import type { SupabaseClient } from "@supabase/supabase-js"; import { deriveStatusAsOf, EVENT_DATE_OPEN, parseStatuses, todayIso, type OrgLookups, type ReportEmployee, type ReportEvent } from "./reports"; import type { Database, EmploymentType, HistoryEventType } from "./supabase/types"; // Shared by the Berichte page and /api/export/* so they can never drift on // what "the current view" means — same filters, same stichtag/event-window // rules. export type ReportFilters = { division?: string; location?: string; status?: string; employment?: string; }; export type SnapshotFilters = ReportFilters & { asOf?: string }; export type EventFilters = { eventType?: HistoryEventType; division?: string; location?: string; from?: string; to?: string }; export async function loadOrgLookups(supabase: SupabaseClient): Promise<{ lookups: OrgLookups; divisions: { id: string; name: string }[]; locations: { id: string; name: string }[]; }> { const [{ data: divisions }, { data: departments }, { data: teams }, { data: locations }] = await Promise.all([ supabase.from("divisions").select("id, name").order("name"), supabase.from("departments").select("id, name"), supabase.from("teams").select("id, name, department_id"), supabase.from("locations").select("id, name").order("name"), ]); const departmentNameById = new Map((departments ?? []).map((d) => [d.id, d.name])); return { lookups: { divisionName: new Map((divisions ?? []).map((d) => [d.id, d.name])), departmentNameByTeam: new Map((teams ?? []).map((t) => [t.id, departmentNameById.get(t.department_id) ?? "Unbekannt"])), teamName: new Map((teams ?? []).map((t) => [t.id, t.name])), locationName: new Map((locations ?? []).map((l) => [l.id, l.name])), }, divisions: divisions ?? [], locations: locations ?? [], }; } const SNAPSHOT_EMPLOYEE_COLUMNS = "id, first_name, last_name, job_title, division_id, team_id, location_id, employment_type, contract_type, entry_date, exit_date, weekly_hours, source, paygrade, birth_date, gender, karenz_start_date, karenz_return_date"; // Bestand zum Stichtag: reconstructs each employee's status as of `asOf` // (defaults to today) from entry/exit/Karenz dates — see deriveStatusAsOf. // division/team/location still reflect the employee's *current* assignment. export async function loadSnapshotEmployees(supabase: SupabaseClient, filters: SnapshotFilters): Promise { const asOf = filters.asOf || todayIso(); let query = supabase.from("employees").select(SNAPSHOT_EMPLOYEE_COLUMNS); if (filters.division) query = query.eq("division_id", filters.division); if (filters.location) query = query.eq("location_id", filters.location); if (filters.employment) query = query.eq("employment_type", filters.employment as EmploymentType); const { data } = await query; const withDerivedStatus: ReportEmployee[] = (data ?? []).map((e) => ({ id: e.id, first_name: e.first_name, last_name: e.last_name, job_title: e.job_title, division_id: e.division_id, team_id: e.team_id, location_id: e.location_id, status: deriveStatusAsOf(e, asOf), employment_type: e.employment_type, contract_type: e.contract_type, entry_date: e.entry_date, exit_date: e.exit_date, weekly_hours: e.weekly_hours, source: e.source, paygrade: e.paygrade, birth_date: e.birth_date, gender: e.gender, })); const statuses = parseStatuses(filters.status); return withDerivedStatus.filter((e) => statuses.includes(e.status as (typeof statuses)[number])); } // Ereignisse: employee_history has no division_id/team_id of its own, so // this joins in the affected employee's *current* org placement (two plain // queries, merged in JS — the hand-written Database type has no relational // embedding metadata for a single nested-select query). // // from/to: "" (unset) falls back to the current calendar year; the literal // sentinel EVENT_DATE_OPEN means that side of the interval is intentionally // unbounded (e.g. "alle Ereignisse bis heute", no start date). export async function loadEventHistory(supabase: SupabaseClient, filters: EventFilters): Promise { const currentYear = new Date().getFullYear(); const from = filters.from === EVENT_DATE_OPEN ? undefined : filters.from || `${currentYear}-01-01`; const to = filters.to === EVENT_DATE_OPEN ? undefined : filters.to || `${currentYear}-12-31`; let historyQuery = supabase.from("employee_history").select("employee_id, event_date, event_type, description"); if (from) historyQuery = historyQuery.gte("event_date", from); if (to) historyQuery = historyQuery.lte("event_date", to); if (filters.eventType) historyQuery = historyQuery.eq("event_type", filters.eventType); const [{ data: history }, { data: employees }] = await Promise.all([ historyQuery, supabase.from("employees").select("id, first_name, last_name, job_title, division_id, team_id, location_id"), ]); const employeeById = new Map((employees ?? []).map((e) => [e.id, e])); const events: ReportEvent[] = []; for (const h of history ?? []) { const emp = employeeById.get(h.employee_id); if (!emp) continue; if (filters.division && emp.division_id !== filters.division) continue; if (filters.location && emp.location_id !== filters.location) continue; events.push({ employee_id: emp.id, first_name: emp.first_name, last_name: emp.last_name, job_title: emp.job_title, division_id: emp.division_id, team_id: emp.team_id, location_id: emp.location_id, event_date: h.event_date, event_type: h.event_type, description: h.description, }); } return events; }