diff --git a/backend/src/routes/staff/time.ts b/backend/src/routes/staff/time.ts index d1c3a58..e2a3d93 100644 --- a/backend/src/routes/staff/time.ts +++ b/backend/src/routes/staff/time.ts @@ -62,17 +62,17 @@ export default async function staffTimeRoutes(server: FastifyInstance) { // 🆕 POST /staff/time/edit (Bearbeiten durch Invalidieren + Neu erstellen) server.post("/staff/time/edit", async (req, reply) => { 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; - // Wir erwarten das komplette Paket für die Änderung const { - originalEventIds, // Array der IDs, die "gelöscht" werden sollen (Start ID, End ID) - newStart, // ISO String - newEnd, // ISO String - newType, // z.B. 'work', 'vacation' + originalEventIds, + newStart, + newEnd, + newType, description, - reason // Warum wurde geändert? (Audit) + reason } = req.body as { originalEventIds: 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." }); } - // 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) => { - // A. INVALIDIEREN (Die alten Events "löschen") - // Wir erstellen für jedes alte Event ein 'invalidated' Event + // 1. INVALIDIEREN + // Wir nutzen 'targetUserId' als Besitzer des Events, aber 'actorId' als Auslöser const invalidations = originalEventIds.map(id => ({ tenant_id: tenantId, - user_id: userId, // Gehört dem Mitarbeiter + user_id: targetUserId, // <--- WICHTIG: Gehört dem Mitarbeiter actortype: "user", - actoruser_id: userId, // Wer hat geändert? + actoruser_id: actorId, // <--- WICHTIG: Geändert durch Manager/Self eventtime: new Date(), - eventtype: "invalidated", // <--- NEUER TYP: Muss in loadValidEvents gefiltert werden! + eventtype: "invalidated", source: "WEB", - related_event_id: id, // Zeigt auf das alte Event + related_event_id: id, metadata: { reason: reason || "Bearbeitung", replaced_by_edit: true } })); - // Batch Insert // @ts-ignore await tx.insert(stafftimeevents).values(invalidations); - // B. NEU ERSTELLEN (Die korrigierten Events anlegen) + // 2. NEU ERSTELLEN // Start Event // @ts-ignore await tx.insert(stafftimeevents).values({ tenant_id: tenantId, - user_id: userId, + user_id: targetUserId, // <--- Gehört dem Mitarbeiter actortype: "user", - actoruser_id: userId, + actoruser_id: actorId, // <--- Erstellt durch Manager/Self eventtime: new Date(newStart), - eventtype: `${newType}_start`, // z.B. work_start + eventtype: `${newType}_start`, source: "WEB", payload: { description: description || "" } }); @@ -130,11 +155,11 @@ export default async function staffTimeRoutes(server: FastifyInstance) { // @ts-ignore await tx.insert(stafftimeevents).values({ tenant_id: tenantId, - user_id: userId, + user_id: targetUserId, // <--- Gehört dem Mitarbeiter actortype: "user", - actoruser_id: userId, + actoruser_id: actorId, // <--- Erstellt durch Manager/Self eventtime: new Date(newEnd), - eventtype: `${newType}_end`, // z.B. work_end + eventtype: `${newType}_end`, source: "WEB" }); }