Phase 4 abgeschlossen: Personalakten, Dokumente, Beteiligte, Verknüpfungen

Die Dokumentebene aus docs/13 ist gebaut, additiv: Document, DocumentSection
und DocumentSectionVersion neben Case, CaseSection und CaseEntry. Ein Dokument
hängt an einer Fallakte, an einer Personalakte oder an nichts, höchstens an
einem — gesichert durch eine CHECK-Bedingung. Es gibt kein DELETE; eine
Zurücknahme lässt einen Grabstein stehen, der auch für den Admin zu ist.

Eine neue Dokumentart ist damit ein Datensatz, kein Deployment. Im Seed stehen
vier: vermerk, vorfallsbericht, sonderarbeitsvertrag (mehrseitiger Vertragstext
mit Platzhaltern und Unterschriftenzeilen) und bussgeld.

Zwei Umbenennungen, beide von Hand migriert, damit die vorhandenen Zeilen und
Zählerstände stehen bleiben:
- CaseTemplate -> DocumentTemplate, plus uses[] und layout
- CaseCounter -> NumberSeries, ein Nummernkreis je Präfix für Aktenzeichen
  und Dokumentnummern

Die Regeln für Abschnitte stehen jetzt einmal in lib/sections/render.ts und
gelten für Fallakten und Dokumente gleichermaßen. Die Frage aus docs/13, ob
CaseSection langfristig in die Dokumentebene wandert, ist damit verneint und
begründet.

Rückverweise auf der Personalakte laufen durch visibleCaseWhere, die Zahl
daneben entsteht aus dem gefilterten Ergebnis. Auch die Rollengruppe
verschwindet mit, sonst verriete "Betroffener" ohne Eintrag die Ermittlung.
Die eigene Personalakte ist keine Ausnahme von der Freigabestufe.

Vier neue Rechte: document.create, .edit, .finalize, .retract. Das letzte
steht bei den zurückgehaltenen, es greift in Geschriebenes ein.

91 Tests im Web-Paket, 15 in der Datenbank, 28 e2e. Größte Route 118 kB
gegen das Budget von 120 kB — die Luft wird knapp.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Harlekin
2026-09-02 14:15:16 +02:00
co-authored by Claude Opus 5
parent f5769ca58a
commit d65cc95eb2
62 changed files with 5312 additions and 225 deletions
+83 -5
View File
@@ -180,7 +180,7 @@ Präfix und ihre Stellenzahl mit, der Nummernkreis läuft je Präfix und Jahr. `
bekommt dafür einen zusammengesetzten Schlüssel aus Präfix und Jahr statt nur dem Jahr.
Bestehende Aktenzeichen bleiben, wie sie sind.
## Offen: Dokumentarten über die Fallakte hinaus
## Dokumentarten über die Fallakte hinaus
**Noch nicht entschieden, noch nichts gebaut.** Geprüft vor Phase 3, weil die Antwort den
Zuschnitt von Phase 3 und 4 bestimmt.
@@ -261,10 +261,75 @@ tragen, eine Personalakte auch, und ein Bußgeld steht für sich.
Damit ist eine neue Dokumentart wieder das, was sie sein soll: **ein Datensatz, kein
Deployment.**
Zu klären, bevor gebaut wird: ob die Abschnitte einer Fallakte langfristig ebenfalls über
diese Ebene laufen sollen. Dafür spräche, dass es dann nur einen Mechanismus gibt; dagegen,
dass `docs/04` und `docs/05` `CaseSection` ausdrücklich benennen und der Umbau bestehende
Akten anfasst.
### Gebaut in Phase 4
Umgesetzt wie empfohlen, additiv. Drei neue Modelle: `Document`, `DocumentSection`,
`DocumentSectionVersion`. `Case`, `CaseSection` und `CaseEntry` blieben unberührt.
Die offene Frage — ob die Abschnitte einer Fallakte langfristig ebenfalls über diese Ebene
laufen sollen — ist **verneint**. Dafür hätte gesprochen, dass es dann nur einen Mechanismus
gibt. Dagegen sprach mehr: `docs/04` und `docs/05` benennen `CaseSection` ausdrücklich, der
Umbau fasst bestehende Akten an, und die Regeln sind ohnehin dieselben. Statt die Tabellen
zusammenzulegen, liegen die Regeln jetzt einmal in `lib/sections/render.ts` und werden von
beiden Diensten aufgerufen. Dieselbe Darstellung, dieselben Zusicherungen, zwei Tabellen.
Zwei Umbenennungen kamen dazu, beide von Hand migriert, damit die vorhandenen Zeilen stehen
bleiben:
| Vorher | Jetzt | Warum |
|---|---|---|
| `CaseTemplate` | `DocumentTemplate` | Eine Vorlage beschreibt jetzt auch Dokumente. Ein Sonderarbeitsvertrag als `CaseTemplate` wäre eine falsche Aussage |
| `CaseCounter` | `NumberSeries` | Aktenzeichen und Dokumentnummern laufen über denselben Zähler, getrennt durch das Präfix aus der Vorlage |
`DocumentTemplate.uses` ist eine Liste, kein Einzelwert: dieselbe Vorlage kann an mehreren
Stellen sinnvoll sein. Der Vermerk hängt an einer Fallakte **und** an einer Personalakte, der
Bußgeldbescheid an einer Person **oder** für sich allein. `layout` bestimmt allein das
Aussehen und wird beim Anlegen in das Dokument kopiert — ein späterer Umbau der Vorlage darf
ein ausgefertigtes Dokument nicht nachträglich anders aussehen lassen.
### Ein Dokument hat höchstens einen Besitzer
`caseId` und `personnelFileId` sind beide optional, eine CHECK-Bedingung lässt nicht zu, dass
beide gesetzt sind. Zwei Besitzer wären eine Aussage über die Sichtbarkeit, die niemand
prüfen könnte: die Prüfung müsste sich für einen der beiden entscheiden, und welcher das
wäre, stünde nirgends.
Die Sichtbarkeit eines Dokuments hat deshalb **zwei** Bedingungen, die beide zutreffen
müssen: der Besitzer muss lesbar sein, und das Dokument selbst muss es sein. Ein Vertrag der
Stufe 1 an einer Personalakte der Stufe 4 bleibt verborgen — sonst verriete die
Dokumentenliste die Existenz der Person.
## Die eigene Personalakte ist keine Ausnahme
Eine Fallakte kennt den ausnahmsweisen Zugang über `CaseParticipant`. Eine Personalakte hat
nichts Vergleichbares, und insbesondere darf **die betroffene Person ihre eigene Akte nicht
allein deshalb lesen**, weil sie es ist.
Der Gedanke lag nahe: die eigene Akte kennt man doch. Er ist trotzdem falsch. Eine Beurteilung
auf Stufe 4 über einen Mitarbeiter der Stufe 1 wäre sonst genau für den einen Menschen
lesbar, vor dem sie geschützt werden soll. Es zählt allein die Freigabestufe.
## Rückverweise zählen nur, was sichtbar ist
Auf einer Personalakte stehen die Fälle, in denen die Person vorkommt. Diese Liste läuft
durch `visibleCaseWhere`, und die **Zahl daneben entsteht aus dem gefilterten Ergebnis**, nie
aus einem eigenen `count`. Eine Personalakte der Stufe 1, auf der „3 Fälle" steht, verriete
sonst die Existenz von Ermittlungen, die der Leser nicht sehen darf.
Auch die Rollengruppe verschwindet mit: stünde dort „Betroffener" ohne Eintrag darunter, wäre
die Ermittlung gegen die Person verraten, ohne dass ein Aktenzeichen sichtbar wird.
Eine Person kann auf zwei Wegen an einem Fall hängen — über ihre Personalakte oder über ihr
Discord-Konto. Beide zählen, sonst fehlen Fälle.
## Der Name eines Beteiligten gehört zur Akte
`listParticipants` zeigt den Namen einer beteiligten Person auch dann, wenn der Leser deren
Personalakte nicht sehen darf. Nur der **Verweis** auf die Personalakte entfällt dann.
Das ist Absicht: wer ein Ermittlungsverfahren lesen darf, muss wissen, gegen wen es läuft.
Der Name ist an dieser Stelle Inhalt der Fallakte, nicht Inhalt der Personalakte. Verborgen
bleibt, was darüber hinaus über die Person aktenkundig ist.
## Prisma 7
@@ -308,3 +373,16 @@ dieselbe Verlustgefahr bei einem Absturz, aber zusätzliche Schreiblast. Kommt i
vier Startvorlagen aber Phase 3 zu. Der Seed legt vorerst nur an, was die Anwendung zum Laufen
braucht: die sechs Freigabestufen, die Änderungsrichtlinie und den Aktenzeichen-Zähler.
Die Vorlagen kommen mit dem Vorlagen-Editor in Phase 3.
## Vier neue Rechte für Dokumente
`document.create`, `document.edit`, `document.finalize` und `document.retract`. Die
Alternative wäre gewesen, Dokumente an `case.edit` und `personnel.edit` zu hängen — dann
hätte aber jeder, der eine Personalakte pflegen darf, auch Bußgeldbescheide ausstellen
können. Das ist nicht dieselbe Befugnis.
`document.retract` steht bei den zurückgehaltenen Rechten, zusammen mit den drei
Eintragsrechten. Gemeinsames Merkmal: sie greifen in bereits Geschriebenes ein.
Im Auslieferungszustand hat keine Rolle diese Rechte. Bis der Leiter sie im Adminbereich
verteilt, legt nur der Administrator Dokumente an.