The Email column exported employees.email, which is the person's private address. That does not belong in a file sent to an outside survey provider, least of all as the address invitations go to in the employer's name. The column now stays as a placeholder for the work email, which does not exist in the schema yet and will be added later. It is empty until then, but keeps its place so the column mapping set up in Honestly does not have to change once the address arrives. The export no longer reads employees.email at all, so it cannot end up in another column by accident either. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
99 lines
3.8 KiB
TypeScript
99 lines
3.8 KiB
TypeScript
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 ist ein Platzhalter für die **Firmenadresse**, die es
|
|
// im Datenmodell noch nicht gibt. employees.email ist die private Adresse
|
|
// (Migration 20260811140000) und gehört nicht in eine Datei, die an einen
|
|
// fremden Anbieter geht — schon gar nicht als Adresse, an die Einladungen im
|
|
// Namen des Arbeitgebers gehen. Deshalb bleibt die Spalte leer, bis die
|
|
// Firmenadresse erfasst wird; die Spalte selbst bleibt stehen, damit die
|
|
// Zuordnung in Honestly nicht neu eingerichtet werden muss.
|
|
export const HONESTLY_SPRACHE = "de";
|
|
export const HONESTLY_ROLLE = "Respondee";
|
|
|
|
export type HonestlyZeile = {
|
|
personalnummer: number | string | null;
|
|
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<HonestlyZeile>[] {
|
|
const ou: ExportColumn<HonestlyZeile>[] = Array.from({ length: Math.max(1, tiefe) }, (_, stufe) => ({
|
|
header: ouUeberschrift(stufe),
|
|
get: (z) => z.kette[stufe] ?? "",
|
|
}));
|
|
return [
|
|
{ header: "Personalnummer", get: (z) => z.personalnummer },
|
|
// Platzhalter für die Firmenadresse — siehe oben.
|
|
{ header: "Email", get: () => "" },
|
|
{ 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;
|
|
first_name: string;
|
|
last_name: string;
|
|
location_id: string;
|
|
org_unit_id: string | null;
|
|
}[],
|
|
maps: OrgMaps,
|
|
standortName: Map<string, string>
|
|
): { zeilen: HonestlyZeile[]; tiefe: number } {
|
|
const zeilen = personen.map((p) => ({
|
|
personalnummer: p.personnel_number,
|
|
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)) };
|
|
}
|