Added Backend

This commit is contained in:
2026-01-06 12:07:43 +01:00
parent b013ef8f4b
commit 6f3d4c0bff
165 changed files with 0 additions and 0 deletions

View File

@@ -0,0 +1,105 @@
// src/services/loadValidEvents.ts
import { stafftimeevents } from "../../../db/schema";
import {sql, and, eq, gte, lte, inArray} from "drizzle-orm";
import { FastifyInstance } from "fastify";
export type TimeType = "work_start" | "work_end" | "pause_start" | "pause_end" | "sick_start" | "sick_end" | "vacation_start" | "vacation_end" | "ovetime_compensation_start" | "overtime_compensation_end";
// Die Definition des TimeEvent Typs, der zurückgegeben wird (muss mit dem tatsächlichen Typ übereinstimmen)
export type TimeEvent = {
id: string;
eventtype: string;
eventtime: Date;
actoruser_id: string;
related_event_id: string | null;
// Fügen Sie hier alle weiteren Felder hinzu, die Sie benötigen
};
export async function loadValidEvents(
server: FastifyInstance,
tenantId: number,
userId: string,
from: Date,
to: Date
): Promise<TimeEvent[]> {
// Definieren Sie einen Alias für die stafftimeevents Tabelle in der äußeren Abfrage
const baseEvents = stafftimeevents;
// Die Subquery, um alle IDs zu finden, die ungültig gemacht wurden
// Wir nennen die innere Tabelle 'invalidatingEvents'
const invalidatingEvents = server.db
.select({
invalidatedId: baseEvents.invalidates_event_id
})
.from(baseEvents)
.as('invalidating_events');
// Die Hauptabfrage
const result = await server.db
.select()
.from(baseEvents)
.where(
and(
// 1. Tenant und User filtern
eq(baseEvents.tenant_id, tenantId),
eq(baseEvents.user_id, userId),
// 2. Zeitbereich filtern (Typensicher)
gte(baseEvents.eventtime, from),
lte(baseEvents.eventtime, to),
// 3. WICHTIG: Korrekturen ausschließen (NOT EXISTS)
// Schließe jedes Event aus, dessen ID in der Liste der invalidates_event_id erscheint.
sql`
not exists (
select 1
from ${stafftimeevents} i
where i.invalidates_event_id = ${baseEvents.id}
)
`
)
)
.orderBy(
// Wichtig für die deriveTimeSpans Logik: Event-Zeit muss primär sein
baseEvents.eventtime,
baseEvents.created_at, // Wenn Eventtime identisch ist, das später erstellte zuerst
baseEvents.id
);
// Mapping auf den sauberen TimeEvent Typ
return result.map(e => ({
id: e.id,
eventtype: e.eventtype,
eventtime: e.eventtime,
// Fügen Sie hier alle weiteren benötigten Felder hinzu (z.B. metadata, actoruser_id)
// ...
})) as TimeEvent[];
}
export async function loadRelatedAdminEvents(server, eventIds) {
if (eventIds.length === 0) return [];
// Lädt alle administrativen Events, die sich auf die faktischen Event-IDs beziehen
const adminEvents = await server.db
.select()
.from(stafftimeevents)
.where(
and(
inArray(stafftimeevents.related_event_id, eventIds),
// Wir müssen hier die Entkräftung prüfen, um z.B. einen abgelehnten submitted-Event auszuschließen
sql`
not exists (
select 1
from ${stafftimeevents} i
where i.invalidates_event_id = ${stafftimeevents}.id
)
`,
)
)
// Muss nach Zeit sortiert sein, um den Status korrekt zu bestimmen!
.orderBy(stafftimeevents.eventtime);
return adminEvents;
}