import { TimeService } from "../time-v2/service"; import { legacySpans } from "../time-v2/compatibility"; import { FastifyInstance } from "fastify"; import {and, eq, gte, lte, asc, inArray} from "drizzle-orm"; import { authProfiles, stafftimeentries, holidays, } from "../../../db/schema"; export async function generateTimesEvaluation( server: FastifyInstance, user_id: string, tenant_id: number, startDateInput: string, endDateInput: string ) { const service = new TimeService(server.db); const actor = { tenant: tenant_id, user: user_id }; if (await service.active(actor, user_id)) { const result = await service.evaluation(actor, user_id, startDateInput, endDateInput); return { ...result.summary, sumWorkingMinutesEingereicht: result.summary.sumWorkingMinutesSubmitted, saldo: result.summary.saldoApproved, saldoInOfficial: result.summary.saldoSubmitted, times: legacySpans(result.spans).map(s => ({ ...s, started_at: s.startedAt, stopped_at: s.endedAt, state: s.status })) }; } const startDate = server.dayjs(startDateInput); const endDate = server.dayjs(endDateInput); console.log(startDate.format("YYYY-MM-DD HH:mm:ss")); console.log(endDate.format("YYYY-MM-DD HH:mm:ss")); // ------------------------------------------------------------- // 1️⃣ Profil laden // ------------------------------------------------------------- const profileRows = await server.db .select() .from(authProfiles) .where( and( eq(authProfiles.user_id, user_id), eq(authProfiles.tenant_id, tenant_id) ) ) .limit(1); const profile = profileRows[0]; if (!profile) throw new Error("Profil konnte nicht geladen werden."); // ------------------------------------------------------------- // 2️⃣ Arbeitszeiten laden // ------------------------------------------------------------- const timesRaw = await server.db .select() .from(stafftimeentries) .where( and( eq(stafftimeentries.tenant_id, tenant_id), eq(stafftimeentries.user_id, user_id) ) ) .orderBy(asc(stafftimeentries.started_at)); const isBetween = (spanStartDate, spanEndDate, startDate, endDate) => { return ( server .dayjs(startDate) .isBetween(spanStartDate, spanEndDate, "day", "[]") && server .dayjs(endDate) .isBetween(spanStartDate, spanEndDate, "day", "[]") ); }; const times = timesRaw.filter((i) => isBetween(startDate, endDate, i.started_at, i.stopped_at) ); console.log(times); // ------------------------------------------------------------- // 3️⃣ Feiertage laden // ------------------------------------------------------------- const holidaysRows = await server.db .select({ date: holidays.date, }) .from(holidays) .where( and( inArray(holidays.state_code, [profile.state_code, "DE"]), gte(holidays.date, startDate.format("YYYY-MM-DD")), lte(holidays.date, endDate.add(1, "day").format("YYYY-MM-DD")) ) ); // ------------------------------------------------------------- // 4️⃣ Sollzeit berechnen // ------------------------------------------------------------- let timeSpanWorkingMinutes = 0; const totalDays = endDate.add(1, "day").diff(startDate, "days"); for (let i = 0; i < totalDays; i++) { const date = startDate.add(i, "days"); const weekday = date.day(); timeSpanWorkingMinutes += (profile.weekly_regular_working_hours?.[weekday] || 0) * 60; } // ------------------------------------------------------------- // 5️⃣ Eingereicht/genehmigt // ------------------------------------------------------------- const calcMinutes = (start: string, end: string | null) => server.dayjs(end || new Date()).diff(server.dayjs(start), "minutes"); let sumWorkingMinutesEingereicht = 0; let sumWorkingMinutesApproved = 0; for (const t of times) { // @ts-ignore const minutes = calcMinutes(t.started_at, t.stopped_at); if (["submitted", "approved"].includes(t.state) && t.type === "work") { sumWorkingMinutesEingereicht += minutes; } if (t.state === "approved" && t.type === "work") { sumWorkingMinutesApproved += minutes; } } // ------------------------------------------------------------- // 6️⃣ Feiertagsausgleich // ------------------------------------------------------------- let sumWorkingMinutesRecreationDays = 0; let sumRecreationDays = 0; if (profile.recreation_days_compensation && holidaysRows?.length) { holidaysRows.forEach(({ date }) => { const weekday = server.dayjs(date).day(); const hours = profile.weekly_regular_working_hours?.[weekday] || 0; sumWorkingMinutesRecreationDays += hours * 60; sumRecreationDays++; }); } // ------------------------------------------------------------- // 7️⃣ Urlaub // ------------------------------------------------------------- let sumWorkingMinutesVacationDays = 0; let sumVacationDays = 0; times .filter((t) => t.type === "vacation" && t.state === "approved") .forEach((time) => { // Tippfehler aus Original: startet_at vs started_at → NICHT korrigiert const days = server.dayjs(time.stopped_at).diff( //@ts-ignore server.dayjs(time.startet_at), "day" ) + 1; for (let i = 0; i < days; i++) { const weekday = server .dayjs(time.started_at) .add(i, "day") .day(); const hours = profile.weekly_regular_working_hours?.[weekday] || 0; sumWorkingMinutesVacationDays += hours * 60; } sumVacationDays += days; }); // ------------------------------------------------------------- // 8️⃣ Krankheit // ------------------------------------------------------------- let sumWorkingMinutesSickDays = 0; let sumSickDays = 0; times .filter((t) => t.type === "sick" && t.state === "approved") .forEach((time) => { const days = server.dayjs(time.stopped_at).diff( //@ts-ignore server.dayjs(time.startet_at), "day" ) + 1; for (let i = 0; i < days; i++) { const weekday = server .dayjs(time.started_at) .add(i, "day") .day(); const hours = profile.weekly_regular_working_hours?.[weekday] || 0; sumWorkingMinutesSickDays += hours * 60; } sumSickDays += days; }); // ------------------------------------------------------------- // 9️⃣ Salden // ------------------------------------------------------------- const saldo = sumWorkingMinutesApproved + sumWorkingMinutesRecreationDays + sumWorkingMinutesVacationDays + sumWorkingMinutesSickDays - timeSpanWorkingMinutes; const saldoInOfficial = sumWorkingMinutesEingereicht + sumWorkingMinutesRecreationDays + sumWorkingMinutesVacationDays + sumWorkingMinutesSickDays - timeSpanWorkingMinutes; // ------------------------------------------------------------- // 🔟 Rückgabe identisch // ------------------------------------------------------------- return { user_id, tenant_id, from: startDate.format("YYYY-MM-DD"), to: endDate.format("YYYY-MM-DD"), timeSpanWorkingMinutes, sumWorkingMinutesEingereicht, sumWorkingMinutesApproved, sumWorkingMinutesRecreationDays, sumRecreationDays, sumWorkingMinutesVacationDays, sumVacationDays, sumWorkingMinutesSickDays, sumSickDays, saldo, saldoInOfficial, times, }; }