Phase 10: Mandantenfaehigkeit

Ein Server, mehrere Wehren. Ein Konto, mehrere Zugehoerigkeiten. Eine
Kreisebene, die zusieht.

Datenmodell und RLS trugen seit Phase 0 mehrere Wehren. Was fehlte, war
alles darueber: bis hierher war die Personenakte zugleich das Anmeldekonto,
und die gehoert genau einer Wehr. Wer in zweien gefuehrt wurde, brauchte
zwei Passwoerter, und die Anmeldung per E-Mail war mehrdeutig.

Neu sind `Benutzer` (das Konto) und `Mitgliedschaft` (die Zugehoerigkeit).
Das Konto traegt bewusst keine Mandantenkennung und kann keine tragen - es
ist der Angelpunkt, an dem ein Mensch zu mehreren Wehren gehoert. Geschuetzt
wird es deshalb ueber Rechte statt ueber RLS, wie `Berechtigung` seit Phase
0. Was es in einer Wehr darf, entscheidet seine Mitgliedschaft.

Die Sitzung behaelt ihre Form und entsteht nur anders - aus der aktiven
Mitgliedschaft statt aus der Person. Von 414 Fundstellen in 61 Dateien
musste dadurch keine angefasst werden. Die eine Ausnahme ist Absicht:
`personId` ist nullbar geworden, weil eine Aufsichtssitzung keine Akte hat,
und der Typechecker hat daraus genau die fuenf Stellen gemacht, an denen
die Kreisaufsicht nichts verloren hat.

Ihre Rechte werden gefiltert statt aufgezaehlt: alles, was `.lesen` heisst,
minus Kasse, Audit-Log, KI-Protokoll und Personenexport. So bekommt sie
neue Leserechte automatisch und neue Schreibrechte nie.

Die Migration ist datenerhaltend und bricht ab, statt einen halben Zustand
zu hinterlassen.

Nebenbei behoben: `db:test-reset` liess die Nachbarwehr stehen, der
Fehlversuch-Test sperrte das gemeinsame Testkonto, ein Testaufbau war
nicht wiederholbar, und die Wartelogik der Bildschirmfotos konnte
ausloesen, bevor eine Seite ueberhaupt geladen war.

581 Unit-Tests und 266 E2E-Tests gruen.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Florian Wilde
2026-09-05 13:43:24 +02:00
co-authored by Claude Opus 5
parent 2b53556175
commit ff40437a1a
98 changed files with 3810 additions and 201 deletions
+27 -1
View File
@@ -40,7 +40,8 @@ Faktoren, die in die Schätzung eingehen:
| 7 | Benachrichtigungen und Termine | 10–13 PT | 2,5–3,5 h | **3:10 h** |
| 8 | Lokale KI | 9–12 PT | 2,5–3 h | **3:40 h** |
| 9 | Kassenverwaltung | 8–11 PT | 2–3 h | **4:10 h** |
| | **Summe** | **123–164 PT** | **28–37 h** | **28:30 h** |
| 10 | Mandantenfähigkeit | 4–6 PT | 1–1,5 h | **4:20 h** |
| | **Summe** | **127–170 PT** | **29–38,5 h** | **32:50 h** |
123–164 Personentage entsprechen etwa **6 bis 8 Monaten Vollzeitarbeit** einer
einzelnen Person. Zum Vergleich: eine kommerzielle Feuerwehrverwaltung kostet
@@ -432,6 +433,31 @@ der Prüfbericht, der nach dem Festschreiben entsteht, und die
DSGVO-Löschung einer Wehr, die an ihren eigenen festgeschriebenen Buchungen
scheiterte.
## Phase 10 — Mandantenfähigkeit
**Vergleichsaufwand: 4–6 PT** — aus der Kandidatenliste vorgezogen, am
05.09.2026 umgesetzt.
| Baustein | PT |
|---|---:|
| `Benutzer` und `Mitgliedschaft`, datenerhaltende Umstellung | 1,5 |
| Anmeldung: Konto statt Akte, Wehrwechsel im Sitzungstoken | 1,5 |
| Kreisebene mit reinen Leserechten | 1,0 |
| Wehrwähler und Verwaltung der Installation | 1,5 |
Die Schätzung ging von „technisch bereits möglich, Oberfläche fehlt" aus —
und das war zur Hälfte falsch. Datenmodell und RLS trugen tatsächlich mehrere
Wehren, aber die Anmeldung nicht: Passwort und OIDC-Subject hingen an der
Personenakte, und die gehört genau einer Wehr. Das war kein Oberflächenthema,
sondern ein Umbau am Fundament.
**Tatsächlich: 4:20 h.** Getragen hat eine Entscheidung ganz am Anfang: die
Sitzung behält ihre Form und entsteht nur anders. Ohne sie wären 61 Dateien
mit 414 Fundstellen zu ändern gewesen; so waren es acht. Die Zeit ging
stattdessen in die Absicherung — zwei Trigger, vier CHECKs und elf
Datenbanktests — und das ist die richtige Verteilung für eine Phase, deren
einziger Zweck es ist, dass eine Wehr die andere nicht sieht.
---
## Was hier nicht drin ist