fix #29 Corrected User ID
This commit is contained in:
@@ -62,17 +62,17 @@ export default async function staffTimeRoutes(server: FastifyInstance) {
|
|||||||
// 🆕 POST /staff/time/edit (Bearbeiten durch Invalidieren + Neu erstellen)
|
// 🆕 POST /staff/time/edit (Bearbeiten durch Invalidieren + Neu erstellen)
|
||||||
server.post("/staff/time/edit", async (req, reply) => {
|
server.post("/staff/time/edit", async (req, reply) => {
|
||||||
try {
|
try {
|
||||||
const userId = req.user.user_id;
|
// 1. Der "Actor" ist der, der gerade eingeloggt ist (z.B. Manager)
|
||||||
|
const actorId = req.user.user_id;
|
||||||
const tenantId = req.user.tenant_id;
|
const tenantId = req.user.tenant_id;
|
||||||
|
|
||||||
// Wir erwarten das komplette Paket für die Änderung
|
|
||||||
const {
|
const {
|
||||||
originalEventIds, // Array der IDs, die "gelöscht" werden sollen (Start ID, End ID)
|
originalEventIds,
|
||||||
newStart, // ISO String
|
newStart,
|
||||||
newEnd, // ISO String
|
newEnd,
|
||||||
newType, // z.B. 'work', 'vacation'
|
newType,
|
||||||
description,
|
description,
|
||||||
reason // Warum wurde geändert? (Audit)
|
reason
|
||||||
} = req.body as {
|
} = req.body as {
|
||||||
originalEventIds: string[],
|
originalEventIds: string[],
|
||||||
newStart: string,
|
newStart: string,
|
||||||
@@ -86,41 +86,66 @@ export default async function staffTimeRoutes(server: FastifyInstance) {
|
|||||||
return reply.code(400).send({ error: "Keine Events zum Bearbeiten angegeben." });
|
return reply.code(400).send({ error: "Keine Events zum Bearbeiten angegeben." });
|
||||||
}
|
}
|
||||||
|
|
||||||
// 1. Transaction starten (damit alles oder nichts passiert)
|
// -----------------------------------------------------------
|
||||||
|
// SCHRITT A: Den eigentlichen Besitzer (Mitarbeiter) ermitteln
|
||||||
|
// -----------------------------------------------------------
|
||||||
|
// Wir holen uns das erste Event aus der Liste, um zu sehen, wem es gehört.
|
||||||
|
const existingEvents = await server.db
|
||||||
|
.select({
|
||||||
|
user_id: stafftimeevents.user_id,
|
||||||
|
tenant_id: stafftimeevents.tenant_id
|
||||||
|
})
|
||||||
|
.from(stafftimeevents)
|
||||||
|
.where(and(
|
||||||
|
eq(stafftimeevents.id, originalEventIds[0]),
|
||||||
|
eq(stafftimeevents.tenant_id, tenantId) // Sicherheitscheck: Nur im eigenen Tenant
|
||||||
|
))
|
||||||
|
.limit(1);
|
||||||
|
|
||||||
|
if (existingEvents.length === 0) {
|
||||||
|
return reply.code(404).send({ error: "Ursprüngliches Event nicht gefunden oder Zugriff verweigert." });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Das ist der Mitarbeiter, dem die Zeit gehört
|
||||||
|
const targetUserId = existingEvents[0].user_id;
|
||||||
|
|
||||||
|
|
||||||
|
// -----------------------------------------------------------
|
||||||
|
// SCHRITT B: Transaktion durchführen
|
||||||
|
// -----------------------------------------------------------
|
||||||
await server.db.transaction(async (tx) => {
|
await server.db.transaction(async (tx) => {
|
||||||
|
|
||||||
// A. INVALIDIEREN (Die alten Events "löschen")
|
// 1. INVALIDIEREN
|
||||||
// Wir erstellen für jedes alte Event ein 'invalidated' Event
|
// Wir nutzen 'targetUserId' als Besitzer des Events, aber 'actorId' als Auslöser
|
||||||
const invalidations = originalEventIds.map(id => ({
|
const invalidations = originalEventIds.map(id => ({
|
||||||
tenant_id: tenantId,
|
tenant_id: tenantId,
|
||||||
user_id: userId, // Gehört dem Mitarbeiter
|
user_id: targetUserId, // <--- WICHTIG: Gehört dem Mitarbeiter
|
||||||
actortype: "user",
|
actortype: "user",
|
||||||
actoruser_id: userId, // Wer hat geändert?
|
actoruser_id: actorId, // <--- WICHTIG: Geändert durch Manager/Self
|
||||||
eventtime: new Date(),
|
eventtime: new Date(),
|
||||||
eventtype: "invalidated", // <--- NEUER TYP: Muss in loadValidEvents gefiltert werden!
|
eventtype: "invalidated",
|
||||||
source: "WEB",
|
source: "WEB",
|
||||||
related_event_id: id, // Zeigt auf das alte Event
|
related_event_id: id,
|
||||||
metadata: {
|
metadata: {
|
||||||
reason: reason || "Bearbeitung",
|
reason: reason || "Bearbeitung",
|
||||||
replaced_by_edit: true
|
replaced_by_edit: true
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// Batch Insert
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
await tx.insert(stafftimeevents).values(invalidations);
|
await tx.insert(stafftimeevents).values(invalidations);
|
||||||
|
|
||||||
// B. NEU ERSTELLEN (Die korrigierten Events anlegen)
|
// 2. NEU ERSTELLEN
|
||||||
|
|
||||||
// Start Event
|
// Start Event
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
await tx.insert(stafftimeevents).values({
|
await tx.insert(stafftimeevents).values({
|
||||||
tenant_id: tenantId,
|
tenant_id: tenantId,
|
||||||
user_id: userId,
|
user_id: targetUserId, // <--- Gehört dem Mitarbeiter
|
||||||
actortype: "user",
|
actortype: "user",
|
||||||
actoruser_id: userId,
|
actoruser_id: actorId, // <--- Erstellt durch Manager/Self
|
||||||
eventtime: new Date(newStart),
|
eventtime: new Date(newStart),
|
||||||
eventtype: `${newType}_start`, // z.B. work_start
|
eventtype: `${newType}_start`,
|
||||||
source: "WEB",
|
source: "WEB",
|
||||||
payload: { description: description || "" }
|
payload: { description: description || "" }
|
||||||
});
|
});
|
||||||
@@ -130,11 +155,11 @@ export default async function staffTimeRoutes(server: FastifyInstance) {
|
|||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
await tx.insert(stafftimeevents).values({
|
await tx.insert(stafftimeevents).values({
|
||||||
tenant_id: tenantId,
|
tenant_id: tenantId,
|
||||||
user_id: userId,
|
user_id: targetUserId, // <--- Gehört dem Mitarbeiter
|
||||||
actortype: "user",
|
actortype: "user",
|
||||||
actoruser_id: userId,
|
actoruser_id: actorId, // <--- Erstellt durch Manager/Self
|
||||||
eventtime: new Date(newEnd),
|
eventtime: new Date(newEnd),
|
||||||
eventtype: `${newType}_end`, // z.B. work_end
|
eventtype: `${newType}_end`,
|
||||||
source: "WEB"
|
source: "WEB"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user