Lehrling, and a field that stops being named after its values
Some checks failed
CI / Lint, Typen, Tests, Build (push) Successful in 10m51s
CI / Migrationen auf leerer Datenbank (push) Has been cancelled

worker_type had two values because the field was named after them:
"Angestellte:r / Arbeiter:in". Lehrlinge are the third social-insurance
category in Austria; until now they were filed as one of the other two,
which they are not -- and which skewed every report grouped by this
column by exactly those people.

Adding the value is one line. The label was the work: the field was
called after its two values in six places, and each of them becomes
wrong with a third. They now read "Beschaeftigtengruppe", the name the
import has used all along.

One label deliberately keeps the old wording: app_feld_karte() in the
database. That string is not a caption there but a key -- stored rows in
employee_history and pending_changes carry it, and the map is how
reverting or correcting a history entry finds the field again. Renaming
it without rewriting those rows would make every older entry for this
field unrevertable, and nobody would notice until they tried.

The migration's assertion reads pg_enum rather than comparing against
'Lehrling'::worker_type: migrations run inside a transaction, and
Postgres refuses to use a freshly added enum value in the transaction
that added it. This has not been run against a live database here -- the
CI migration job is the first real execution.

Three hand-kept lists of the same enum (reports, import, the form) now
have a test holding them to one another, each mutation-checked.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-10 17:28:49 +02:00
parent 2838919e42
commit f17d299045
12 changed files with 193 additions and 9 deletions

View File

@@ -0,0 +1,60 @@
import { describe, expect, it } from "vitest";
import { BLAETTER } from "@/lib/import/schema";
import { AUSWAHL_KRITERIEN, parseCriteria } from "@/lib/report-criteria";
import { GROUP_LABELS } from "@/lib/reports";
// Die Beschäftigtengruppe (worker_type) steht an vier Stellen als Liste:
// im Datenbank-Enum, in der Auswahl der Auswertungen, in der Prüfliste des
// Imports und im Auswahlfeld der Oberfläche. Sie werden von Hand gepflegt,
// und sie driften auseinander, ohne dass etwas rot wird — ein fehlender Wert
// heisst dort nicht „Fehler", sondern „gibt es nicht":
//
// * fehlt er in der Auswertung, lässt sich nach der Gruppe nicht filtern,
// * fehlt er im Import, wird die Zeile abgewiesen,
// * fehlt er im Auswahlfeld, ist der Wert nicht einzugeben.
//
// Deshalb steht die erwartete Liste hier einmal, und jede Stelle wird
// dagegen gehalten. Das Enum selbst prüft die Migration (20260910140000).
const GRUPPEN = ["Angestellte:r", "Arbeiter:in", "Lehrling"];
function importSpalte() {
for (const blatt of BLAETTER) {
const treffer = blatt.spalten.find((s) => s.ziel === "worker_type");
if (treffer) return treffer;
}
throw new Error("Keine Importspalte mit ziel worker_type gefunden");
}
describe("Beschäftigtengruppe", () => {
it("bietet in den Auswertungen alle drei zur Auswahl", () => {
const kriterium = AUSWAHL_KRITERIEN.find((k) => k.key === "worker");
expect(kriterium?.optionen.map((o) => o.wert)).toEqual(GRUPPEN);
});
it("lässt jede der drei durch die Prüfung der Adresszeile", () => {
// Was nicht in der Liste steht, verwirft parseCriteria stillschweigend —
// der Filter wirkte dann einfach nicht, ohne Meldung.
for (const gruppe of GRUPPEN) {
expect(parseCriteria((k) => (k === "worker" ? gruppe : undefined)).auswahl).toEqual({ worker: gruppe });
}
});
it("verwirft weiterhin, was nicht dazugehört", () => {
// Die Werte landen in SQL-Vergleichen und im Dateinamen des Downloads.
expect(parseCriteria((k) => (k === "worker" ? "Lehrling'; drop table employees; --" : undefined)).auswahl).toEqual({});
});
it("nimmt alle drei im Import an", () => {
const spalte = importSpalte();
expect(spalte.typ).toMatchObject({ art: "auswahl", werte: GRUPPEN });
});
it("nennt das Feld überall Beschäftigtengruppe", () => {
// Bis September 2026 hiess es „Angestellte:r / Arbeiter:in" — ein Name
// aus seinen beiden Werten, der mit dem dritten falsch wurde.
expect(AUSWAHL_KRITERIEN.find((k) => k.key === "worker")?.label).toBe("Beschäftigtengruppe");
expect(GROUP_LABELS.worker_type).toBe("Beschäftigtengruppe");
expect(importSpalte().name).toBe("Beschäftigtengruppe");
});
});