import type { CsvForm, ExportColumn } from "./export"; // Der Export für LOLYO, die Mitarbeiter-App: eine Zeile je Person, die dort // einen Zugang bekommen soll. // // Diese Datei tut etwas anderes als die beiden Exporte daneben. Honestly lädt // zu einer Befragung ein, Cornerstone führt Stammdaten nach — hier entstehen // **Konten**, samt Passwort in der Zeile. Wer die Datei hat, hat damit einen // Zugang zu jedem Konto darin, solange niemand sein Passwort gewechselt hat. // Deshalb steht das Passwort nicht in dieser Datei: die Route reicht es herein // (app/api/export/lolyo/route.ts), und es gelangt damit nie in ein Bündel, das // der Browser lädt. Dieselbe Überlegung steht in lib/passwort.ts. // // Kopfzeile und Trennzeichen kommen aus der Vorlage des Anbieters, Zeichen für // Zeichen. Die gemischte Schreibweise — „Titel prefix" deutsch, „Title Suffix" // englisch — steht so darin; sie hier stillschweigend zu vereinheitlichen // hiesse, eine Spalte zu liefern, die LOLYO nicht zuordnet. /** * Semikolon, kein BOM, kein Formelschutz — so liest LOLYO die Datei. * * Die Vorgabe von toCsv ist für Excel gemacht. Das BOM hinge unsichtbar am * Namen der ersten Spalte, und „Benutzer/Code" käme beim Einlesen in keiner * Zuordnung mehr vor. * * Der Formelschutz stellt jedem Wert, der mit `=`, `+`, `-` oder `@` beginnt, * ein Hochkomma voran, damit Excel ihn nicht als Formel rechnet. Hier wäre das * falsch: **jede** österreichische Telefonnummer beginnt mit „+", und in LOLYO * stünde danach `'+4366…` als Nummer — in jedem Konto, und niemandem fiele es * beim Erzeugen auf. Die Datei liest eine Maschine, kein Mensch in Excel. */ export const LOLYO_CSV: CsvForm = { trenner: ";", bom: false, formelschutz: false }; /** Für alle dieselbe. Wer eine andere braucht, stellt sie in LOLYO um, nicht hier. */ export const LOLYO_SPRACHE = "DE"; /** * Die Gruppe, in die alle kommen. * * Fest vorgegeben, nicht aus der Organisationseinheit abgeleitet: LOLYO-Gruppen * sind Verteiler in der App und nicht die Aufbauorganisation. Beides * gleichzusetzen erzeugte 78 Gruppen, die dort niemand bestellt hat. */ export const LOLYO_GRUPPE = "Demo-Gruppe"; /** LOLYO holt die Bestätigung selbst ein; aus Alpenwerk kommt sie nie bestätigt. */ export const LOLYO_EMAIL_BESTAETIGT = "0"; /** * Die Anrede aus dem Geschlecht. * * Alpenwerk kennt genau zwei Werte (gender_type), und die Vorgabe nennt genau * zwei Anreden. Ein dritter Wert bliebe hier leer statt geraten — eine falsche * Anrede steht in jeder Nachricht, die die App verschickt. */ export function anrede(gender: string): string { if (gender === "m") return "Herr"; if (gender === "w") return "Frau"; return ""; } /** * Typografische Zeichen auf ihre ASCII-Entsprechung. * * Im Zielsystem kam der Gedankenstrich „–" als `–` an — das sind seine * UTF-8-Bytes, gelesen als Windows-1252. Gemessen hat die Vorlage des Anbieters * dieselbe Kodierung wie unsere Datei (UTF-8 ohne BOM, nachgeprüft am * „bestätigt" in ihrer Kopfzeile), die Ursache liegt also nicht in dem, was wir * schreiben. Solange sie nicht geklärt ist, geht der Weg über das Zeichen * selbst: ein Bindestrich sagt dasselbe und überlebt jede Fehlinterpretation, * weil er in beiden Kodierungen dasselbe Byte ist. * * Umlaute bleiben. „Bäckerei" zu „Baeckerei" zu machen hiesse, ein Problem zu * verstecken, das es entweder gar nicht gibt — oder das dann auch Namen * betrifft, und dort wäre es keine Kosmetik mehr. */ export function ohneTypografie(text: string): string { return text .replace(/[‐-―−]/g, "-") .replace(/[‘’‚‛′]/g, "'") .replace(/[“”„‟″]/g, '"') .replace(/…/g, "...") .replace(/[   ]/g, " "); } /** Die Person, so wie der Export sie braucht. */ export type LolyoQuelle = { id: string; company_email: string | null; title_prefix: string[]; title_suffix: string[]; first_name: string; last_name: string; job_title: string; phone: string | null; gender: string; }; export type LolyoZeile = Record; /** * Eine Zeile. * * Der Benutzercode ist die Alpenwerk-UUID. Anders als bei Cornerstone ist das * hier richtig: LOLYO kennt die Person noch nicht und vergibt keine eigene * Kennung, die wir treffen müssten — die UUID ist der Schlüssel, über den sich * ein späterer Abgleich wieder auf dieselbe Person bezieht. * * Die E-Mail-Spalte trägt die **Firmenadresse** und niemals die private aus * employees.email: die Datei legt Konten bei einem fremden Anbieter an. */ export function baueLolyoZeile(p: LolyoQuelle, passwort: string): LolyoZeile { // ohneTypografie steht an jeder Zelle, die aus den Daten kommt, und nicht nur // an der Position: aufgefallen ist es dort, weil die Tätigkeiten einen // Gedankenstrich führen — dieselben Zeichen stehen aber auch in Namen und // Titeln, und die fielen erst bei der Person auf, die einen trägt. const aus = (wert: string) => ohneTypografie(wert); return { "Benutzer/Code": p.id, Email: aus(p.company_email ?? ""), // **Ohne** Umschrift: das Passwort muss Zeichen für Zeichen das sein, was // die Person eingibt. Ein ersetztes Zeichen fiele niemandem auf — es gäbe // nur ein Konto, in das niemand hineinkommt. Passwort: passwort, "Titel prefix": aus(p.title_prefix.join(" ")), Vorname: aus(p.first_name), Nachname: aus(p.last_name), "Title Suffix": aus(p.title_suffix.join(" ")), Position: aus(p.job_title), Gruppen: LOLYO_GRUPPE, Telefon: aus(p.phone ?? ""), Anrede: anrede(p.gender), Sprache: LOLYO_SPRACHE, "E-Mail bestätigt": LOLYO_EMAIL_BESTAETIGT, }; } /** Die Spalten in der Reihenfolge der Vorlage. */ export const LOLYO_SPALTEN: readonly string[] = [ "Benutzer/Code", "Email", "Passwort", "Titel prefix", "Vorname", "Nachname", "Title Suffix", "Position", "Gruppen", "Telefon", "Anrede", "Sprache", "E-Mail bestätigt", ]; export function lolyoSpalten(): ExportColumn[] { return LOLYO_SPALTEN.map((header) => ({ header, get: (z) => z[header] ?? "" })); }