import type { ExportColumn } from "./export"; import { ancestorsOf, type OrgMaps } from "./org"; // Der Export für Honestly (https://www.honestly.de/), die Plattform für // Mitarbeiterbefragungen: eine Zeile je Person, die befragt werden soll. // // Zwei Spalten stehen fest, statt aus den Daten zu kommen — so ist es // vorgegeben: die Sprache ist immer Deutsch, die Rolle immer „Respondee". // Wer anders befragt werden soll, wird in Honestly umgestellt, nicht hier. // // Der Organisationsbaum kommt von unten nach oben: „OU" ist die Einheit der // Person, „OU+1" die darüber, und so weiter bis zur obersten. Die Zahl dieser // Spalten richtet sich nach der tiefsten Kette im Export. Wer weiter oben // hängt, hat hinten leere Zellen — und die oberste Einheit steht deshalb // nicht bei allen in derselben Spalte. So ist es bestellt. // Die E-Mail-Spalte trägt die **Firmenadresse** (employees.company_email, // Migration 20260923120000) und niemals die private aus employees.email: die // Datei geht an einen fremden Anbieter, der damit im Namen des Arbeitgebers // einlädt. Eine Privatadresse hat dort nichts verloren. // // Bis zur Migration war die Spalte ein leerer Platzhalter — sie stand // trotzdem in der Datei, damit die Zuordnung in Honestly nicht neu // eingerichtet werden muss. Wer keine Firmenadresse hinterlegt hat, hat sie // weiterhin leer; das ist eine fehlende Angabe und keine Ausrede, die private // zu nehmen. export const HONESTLY_SPRACHE = "de"; export const HONESTLY_ROLLE = "Respondee"; export type HonestlyZeile = { personalnummer: number | string | null; /** Die dienstliche Adresse. Leer, wenn keine hinterlegt ist. */ email: string; vorname: string; nachname: string; standort: string; /** Die Einheit der Person zuerst, die oberste zuletzt. */ kette: string[]; }; /** * Die Namen der Einheiten von der eigenen bis zur obersten. * * ancestorsOf liefert die Wurzel zuerst; hier wird es umgedreht, weil die * Spalten bei der eigenen Einheit anfangen. */ export function orgKette(maps: OrgMaps, unitId: string | null | undefined): string[] { return ancestorsOf(maps, unitId) .map((u) => u.name) .reverse(); } /** Überschrift der n-ten Organisationsspalte: OU, OU+1, OU+2, … */ export function ouUeberschrift(stufe: number): string { return stufe === 0 ? "OU" : `OU+${stufe}`; } /** * Die Spalten in der Reihenfolge der Vorgabe. * * Mindestens eine OU-Spalte, auch wenn niemand im Export einer Einheit * zugeordnet ist: eine Datei, deren Spalten je nach Inhalt verschwinden, * bricht jede Zuordnung, die auf der anderen Seite eingerichtet wurde. */ export function honestlySpalten(tiefe: number): ExportColumn[] { const ou: ExportColumn[] = Array.from({ length: Math.max(1, tiefe) }, (_, stufe) => ({ header: ouUeberschrift(stufe), get: (z) => z.kette[stufe] ?? "", })); return [ { header: "Personalnummer", get: (z) => z.personalnummer }, { header: "Email", get: (z) => z.email }, { header: "Firstname", get: (z) => z.vorname }, { header: "Last Name", get: (z) => z.nachname }, { header: "Language", get: () => HONESTLY_SPRACHE }, { header: "Location", get: (z) => z.standort }, ...ou, { header: "Role", get: () => HONESTLY_ROLLE }, ]; } /** Aus den Exportzeilen die Honestly-Zeilen und die Zahl der OU-Spalten. */ export function baueHonestlyZeilen( personen: { personnel_number: number | string | null; company_email: string | null; first_name: string; last_name: string; location_id: string; org_unit_id: string | null; }[], maps: OrgMaps, standortName: Map ): { zeilen: HonestlyZeile[]; tiefe: number } { const zeilen = personen.map((p) => ({ personalnummer: p.personnel_number, email: p.company_email ?? "", vorname: p.first_name, nachname: p.last_name, standort: standortName.get(p.location_id) ?? "", kette: orgKette(maps, p.org_unit_id), })); return { zeilen, tiefe: Math.max(1, ...zeilen.map((z) => z.kette.length)) }; }