fix #29 Corrected User ID
All checks were successful
Build and Push Docker Images / build-backend (push) Successful in 33s
Build and Push Docker Images / build-frontend (push) Successful in 16s

This commit is contained in:
2026-01-08 12:26:13 +01:00
parent 2cb0d9b607
commit 76b363fdaf

View File

@@ -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"
});
}