Kein Formelschutz in einer Datei, die eine Maschine einliest
Some checks failed
CI / Migrationen auf leerer Datenbank (push) Has been cancelled
CI / Lint, Typen, Tests, Build (push) Has been cancelled

Das vorangestellte Hochkomma schuetzt einen Menschen, der eine CSV in Excel
oeffnet: ein Wert mit fuehrendem =, +, - oder @ waere dort sonst eine Formel.
In einer Load-Datei ist es das Gegenteil von Schutz. Jede oesterreichische
Telefonnummer beginnt mit "+", und in LOLYO stuende danach in jedem Konto
'+4366... als Nummer -- ohne dass es beim Erzeugen jemandem auffiele.

toCsv bekommt dafuer formelschutz in der CsvForm, voreingestellt an. Nur LOLYO
schaltet es ab. Die Telefonspalten von Cornerstone sind bisher leer, deshalb
bleibt die Datei dort, wie sie ist.
This commit is contained in:
2026-09-28 13:47:49 +02:00
parent 676cecbf15
commit 38e35e2ff9
3 changed files with 31 additions and 14 deletions

View File

@@ -23,9 +23,10 @@ export function sanitizeForSpreadsheetCell(text: string): string {
return /^[=+\-@]/.test(text) ? `'${text}` : text;
}
function csvCell(value: string | number | boolean | null, trenner: string): string {
function csvCell(value: string | number | boolean | null, trenner: string, formelschutz: boolean): string {
if (value === null || value === undefined) return "";
const text = typeof value === "boolean" ? (value ? "Ja" : "Nein") : sanitizeForSpreadsheetCell(String(value));
const roh = typeof value === "boolean" ? (value ? "Ja" : "Nein") : String(value);
const text = formelschutz ? sanitizeForSpreadsheetCell(roh) : roh;
// Das Trennzeichen gehört in die Prüfung: mit Komma als Trenner muss ein
// Komma im Wert maskiert werden, sonst zerfällt die Zeile in zwei Spalten.
return text.includes(trenner) || /["\n\r]/.test(text) ? `"${text.replace(/"/g, '""')}"` : text;
@@ -39,15 +40,22 @@ function csvCell(value: string | number | boolean | null, trenner: string): stri
* ankommen. Ein fremdes System will oft das Gegenteil — Cornerstone etwa
* erwartet Kommas, und ein BOM machte dort aus der ersten Spalte eine, die in
* keiner Zuordnung vorkommt, weil das unsichtbare Zeichen im Namen steckt.
*
* `formelschutz: false` schaltet das vorangestellte Hochkomma ab (siehe
* sanitizeForSpreadsheetCell). Es gehört in eine Datei, die ein Mensch in Excel
* öffnet, und es verdirbt eine, die eine Maschine einliest: eine österreichische
* Telefonnummer beginnt mit „+", und im Zielsystem stünde danach `'+43…` als
* Nummer. Nur für Dateien setzen, die ausschliesslich ein Fremdsystem liest.
*/
export type CsvForm = { trenner?: "," | ";"; bom?: boolean };
export type CsvForm = { trenner?: "," | ";"; bom?: boolean; formelschutz?: boolean };
export function toCsv<T>(rows: T[], columns: ExportColumn<T>[], form: CsvForm = {}): string {
const trenner = form.trenner ?? ";";
const bom = form.bom ?? true;
const lines = [columns.map((c) => csvCell(c.header, trenner)).join(trenner)];
const schutz = form.formelschutz ?? true;
const lines = [columns.map((c) => csvCell(c.header, trenner, schutz)).join(trenner)];
for (const row of rows) {
lines.push(columns.map((c) => csvCell(c.get(row), trenner)).join(trenner));
lines.push(columns.map((c) => csvCell(c.get(row), trenner, schutz)).join(trenner));
}
return (bom ? "" : "") + lines.join("\r\n");
}

View File

@@ -17,13 +17,19 @@ import type { CsvForm, ExportColumn } from "./export";
// hiesse, eine Spalte zu liefern, die LOLYO nicht zuordnet.
/**
* Semikolon, kein BOM — so sieht die Vorlage aus.
* Semikolon, kein BOM, kein Formelschutz — so liest LOLYO die Datei.
*
* Die Vorgabe von toCsv ist für Excel gemacht und setzt ein BOM voran; das
* hinge unsichtbar am Namen der ersten Spalte, und „Benutzer/Code" käme beim
* Einlesen in keiner Zuordnung mehr vor.
* 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 };
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";

View File

@@ -121,10 +121,13 @@ describe("Die Datei", () => {
expect(csv.split("\r\n")[0]).toBe(VORGABE);
});
it("maskiert eine Telefonnummer, die Excel als Formel läse", () => {
// „+43…" beginnt mit einem Pluszeichen — ohne Schutz macht Excel daraus
// beim Öffnen eine Formel und zeigt einen Fehler statt der Nummer.
it("lässt die Telefonnummer unangetastet", () => {
// Der Formelschutz von toCsv stellt jedem Wert mit führendem „+" ein
// Hochkomma voran, damit Excel ihn nicht rechnet. Diese Datei liest eine
// Maschine: dort stünde danach in **jedem** Konto `'+4366…` als Nummer,
// weil jede österreichische Nummer mit „+" beginnt.
const csv = toCsv([zeile()], lolyoSpalten(), LOLYO_CSV);
expect(csv).toContain("'+4366412345678");
expect(csv).toContain(";+4366412345678;");
expect(csv).not.toContain("'+43");
});
});