Anforderung 1 — Freiwilliger vs unfreiwilliger Austritt. Die Liste der Beendigungsarten zieht aus TerminatePanel.tsx nach lib/beendigung.ts um: der Berichtemanager braucht sie ebenso, und zwei Listen liefen auseinander. Zwei neue Arten (Beendigung in der Probezeit, je Seite). Auf wessen Betreiben beendet wurde, wird aus der Art **abgeleitet** und nicht daneben gespeichert — als zweites freies Feld liesse sich "Entlassung, freiwillig" erfassen. Das Dropdown im Formular schraenkt die Auswahl darunter ein. Drei Gruppen statt zwei: Befristungsablauf geschieht auf niemandes Betreiben, ein Nichtantritt ist kein Austritt. Beide einer Seite zuzuschlagen wuerde jede Fluktuationsquote verfaelschen. Anforderung 2 — Namensfilter in "Anstehend", ab neun Eintraegen. Anforderung 3 — die zwei Unterschriftenfelder im gedruckten Blatt sind weg; "Firmenfahrzeug" steht in beiden Checklisten. has_dienstwagen sagt, ob eines zusteht, nicht ob es uebergeben wurde. Anforderung 4 — "+794 weitere" ist ein Knopf geworden; die Namen waren vorher nur ueber den Export erreichbar. Stammdatenaenderung und Gehaltsanpassung stehen nicht mehr zur Auswahl: die eine entsteht bei jeder geaenderten Telefonnummer, die andere ist ein totes Ereignis, seit das Gehalt in Loga liegt. Neu ist der Untertyp — Beendigungsart beim Austritt, Art der Abwesenheit bei der Langzeitabwesenheit, im Bericht und im Export. Anforderung 10 — zwei Kacheln. "Aktives Dienstverhaeltnis" ist nicht dasselbe wie "Aktive Mitarbeiter:innen": dort steht, wer heute arbeitet, hier, mit wem ein Vertrag laeuft. Sichtbar waren 806 und 10, addieren musste man selbst.
70 lines
3.4 KiB
TypeScript
70 lines
3.4 KiB
TypeScript
import { NextResponse, type NextRequest } from "next/server";
|
|
import { exportFilename, exportResponseHeaders, toCsv, toXlsx, type ExportColumn } from "@/lib/export";
|
|
import {
|
|
EVENT_TYPE_LABELS,
|
|
parseEventDateParam,
|
|
parseEventSubtype,
|
|
parseEventType,
|
|
type OrgLookups,
|
|
type ReportEvent,
|
|
} from "@/lib/reports";
|
|
import { loadEventHistory, loadOrgLookups } from "@/lib/reports-data";
|
|
import { requireHrUser } from "@/lib/auth/require-hr";
|
|
import { withUser } from "@/lib/db";
|
|
|
|
// Full raw event-log dump — one row per employee_history entry in the
|
|
// selected period (default: current year), every event type unless one is
|
|
// picked, org columns resolved from each affected employee's current
|
|
// placement (see loadEventHistory).
|
|
export async function GET(request: NextRequest) {
|
|
const gate = await requireHrUser();
|
|
if ("denied" in gate) return gate.denied;
|
|
|
|
const params = request.nextUrl.searchParams;
|
|
const format = params.get("format") === "xlsx" ? "xlsx" : "csv";
|
|
const eventType = parseEventType(params.get("eventType"));
|
|
|
|
const { lookups, events } = await withUser(gate.userId, async (tx) => {
|
|
const [{ lookups }, events] = await Promise.all([
|
|
loadOrgLookups(tx),
|
|
loadEventHistory(tx, {
|
|
eventType: eventType ?? undefined,
|
|
// Ohne diese Zeile exportierte die Schaltfläche neben der Auswertung
|
|
// etwas anderes, als auf dem Bildschirm steht — die Einschränkung auf
|
|
// die Beendigungsart fiele beim Export still weg.
|
|
eventSubtype: parseEventSubtype(params.get("eventSubtype"), eventType) ?? undefined,
|
|
division: params.get("division") ?? undefined,
|
|
location: params.get("location") ?? undefined,
|
|
from: parseEventDateParam(params.get("from")),
|
|
to: parseEventDateParam(params.get("to")),
|
|
}),
|
|
]);
|
|
return { lookups, events };
|
|
});
|
|
|
|
const columns = eventExportColumns(lookups);
|
|
const filename = exportFilename(`ereignisse-${eventType ?? "alle"}`, format);
|
|
const body = format === "xlsx" ? await toXlsx(events, columns, "Ereignisse") : toCsv(events, columns);
|
|
// TS 5.9's Uint8Array<ArrayBufferLike> vs DOM's BlobPart/ArrayBuffer<> generic
|
|
// mismatch (microsoft/TypeScript#59417) — a real Uint8Array works fine here.
|
|
return new NextResponse(new Blob([body as BlobPart]), { headers: exportResponseHeaders(filename, format) });
|
|
}
|
|
|
|
function eventExportColumns(lookups: OrgLookups): ExportColumn<ReportEvent>[] {
|
|
return [
|
|
{ header: "Datum", get: (e) => e.event_date, kind: "date" },
|
|
{ header: "Ereignistyp", get: (e) => EVENT_TYPE_LABELS[e.event_type] ?? e.event_type },
|
|
// Beendigungsart bzw. Art der Langzeitabwesenheit. Leer bei allen
|
|
// Ereignissen, die keinen Untertyp kennen — eine Versetzung hat keinen.
|
|
{ header: "Untertyp", get: (e) => e.subtype ?? "" },
|
|
{ header: "Vorname", get: (e) => e.first_name },
|
|
{ header: "Nachname", get: (e) => e.last_name },
|
|
{ header: "Position", get: (e) => e.job_title },
|
|
{ header: "Bereich", get: (e) => (e.org_unit_id ? (lookups.divisionName.get(e.org_unit_id) ?? "") : "") },
|
|
{ header: "Abteilung", get: (e) => (e.org_unit_id ? (lookups.departmentName.get(e.org_unit_id) ?? "") : "") },
|
|
{ header: "Team", get: (e) => (e.org_unit_id ? (lookups.teamName.get(e.org_unit_id) ?? "") : "") },
|
|
{ header: "Standort", get: (e) => lookups.locationName.get(e.location_id) ?? "" },
|
|
{ header: "Beschreibung", get: (e) => e.description },
|
|
];
|
|
}
|