feat: konfigurierbaren Freigabe-Workflow mit Genehmiger-Rollen einführen
Mandanten können jetzt selbst festlegen, wer bei welcher abgeleiteten Bedingung (Anforderung/Einstufung/Datenklasse) zusätzlich zur Fachebene-Entscheidung zustimmen muss (z. B. Datenschutzbeauftragter, Geschäftsführung) - Genehmiger-Rollen sind orthogonal zu den fünf bestehenden Zugriffsrollen. Alle ausgelösten Rollen müssen zustimmen, eine Ablehnung kippt kaskadierend den gesamten Antrag. Mandanten ohne Freigabe-Regeln (aktuell alle) sind unverändert vom alten Direktpfad betroffen, siehe TestOhneFreigabeRegelnVerhaeltSichWieVorher. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
128
CLAUDE.md
128
CLAUDE.md
@@ -478,6 +478,118 @@ das Register ist eine Genehmigungsliste, kein vollständiges Antragslog
|
||||
|
||||
---
|
||||
|
||||
## Freigabe-Workflow (konfigurierbare Mehrfach-Genehmigung, 2026-08-31)
|
||||
|
||||
**Bewusste Abkehr von einer dokumentierten v1-Ausschlussentscheidung.**
|
||||
Diese CLAUDE.md schloss "konfigurierbare Rollen mit eigener Oberfläche"
|
||||
und "Workflow-Designer" ursprünglich explizit für v1 aus (siehe "Nicht
|
||||
bauen (v1)" unten). Der Nutzer hat das am 2026-08-31 bewusst und nach
|
||||
Rückfrage aufgehoben ("Wir sollten das System schon perfekt bauen! Daher
|
||||
auch gerne komplex") — Auslöser war die Frage, ob ein Datenschutz-
|
||||
beauftragter und/oder die Geschäftsführung zusätzlich in die Entscheidung
|
||||
eingebunden werden müssen. Statt DSB/GF fest zu verdrahten, können
|
||||
Mandanten jetzt selbst definieren, wer bei welcher abgeleiteten
|
||||
Bedingung zusätzlich zustimmen muss.
|
||||
|
||||
**Datenmodell (Migration 0017):** vier neue Tabellen, orthogonal zu den
|
||||
fünf bestehenden Zugriffsrollen (`mitarbeiter`/`verantwortlicher`/
|
||||
`pruefer`/`admin`/`betreiber` bleiben unverändert):
|
||||
|
||||
- `genehmiger_rolle` — eine mandantendefinierte Freigabe-Funktion (z. B.
|
||||
"Datenschutzbeauftragter", "Geschäftsführung"). Kein Zugriffsrecht,
|
||||
nur eine Freigabe-Zuständigkeit.
|
||||
- `nutzer_genehmiger_rolle` — Zuordnung Person↔Genehmiger-Rolle,
|
||||
many-to-many. Eine Person kann null, eine oder mehrere davon haben,
|
||||
unabhängig von ihrer `app_user.role`.
|
||||
- `freigabe_regel` — "wenn Bedingung X zutrifft, ist zusätzlich eine
|
||||
Freigabe durch Genehmiger-Rolle Y nötig". Die Bedingung ist bewusst
|
||||
**kein freier Regeleditor**, sondern auf die drei Vokabulare
|
||||
beschränkt, die die Bewertungslogik ohnehin schon berechnet:
|
||||
Anforderungs-ID (z. B. `dsfa_erforderlich`), Einstufungs-ID (z. B.
|
||||
`hochrisiko`) oder Datenklasse-ID (z. B. `besondere_kategorie`).
|
||||
Kompromiss zwischen "konfigurierbar" (Nutzeranforderung) und
|
||||
"buildbar/auditierbar" (jede mögliche Bedingung ist bereits eine
|
||||
geprüfte, bekannte Ableitung, keine beliebige Freitext-Regel).
|
||||
- `freigabeschritt` — pro Antrag ein Datensatz je ausgelöster
|
||||
Freigabe-Regel, Status `ausstehend`/`genehmigt`/`abgelehnt`.
|
||||
|
||||
`antrag.status` hat einen neuen Zwischenwert `wartet_auf_freigabe`
|
||||
zwischen `eingereicht` und `entschieden` (CHECK-Constraint erweitert).
|
||||
|
||||
**Ablauf:** `handleFallEntscheiden` (`internal/web/fachebene_handlers.go`)
|
||||
prüft nach einer Genehmigung/Genehmigung-mit-Auflagen, ob die Bewertung
|
||||
mindestens eine `freigabe_regel` des Mandanten auslöst
|
||||
(`triggeredGenehmigerRollen`). Falls ja: `antrag.status` wird
|
||||
`wartet_auf_freigabe` statt `entschieden`, pro ausgelöster Regel entsteht
|
||||
ein `freigabeschritt`, **kein** `registereintrag` entsteht noch. Falls
|
||||
keine Regel greift, läuft exakt der alte Pfad (`registriereGenehmigung`)
|
||||
— **verifiziert per dediziertem Test**
|
||||
(`TestOhneFreigabeRegelnVerhaeltSichWieVorher`), da alle heutigen
|
||||
Bestandskunden null Freigabe-Regeln haben und sich für sie nichts ändern
|
||||
darf.
|
||||
|
||||
**Bestätigte Semantik (2026-08-31, Nutzer: "Beides stimmt!"):**
|
||||
1. **Alle** ausgelösten Genehmiger-Rollen müssen zustimmen, nicht nur
|
||||
eine von mehreren — geprüft durch erneutes `ListFreigabeschritteForAntrag`
|
||||
nach jeder Einzelentscheidung.
|
||||
2. **Eine Ablehnung kippt den gesamten Antrag**, geht nicht zurück an
|
||||
die Fachebene zur Neuentscheidung. `KaskadiereAblehnung` setzt dabei
|
||||
automatisch alle anderen noch `ausstehend`en Freigabeschritte
|
||||
desselben Antrags ebenfalls auf `abgelehnt` — sonst würde ein
|
||||
Freigabeschritt in der Liste einer anderen Person ewig unbearbeitet
|
||||
hängen bleiben, obwohl der Antrag längst entschieden ist.
|
||||
|
||||
Eine Ablehnung (ob direkt oder per Kaskade) mutiert **nicht** die
|
||||
ursprüngliche `genehmigt`-Zeile — `entscheidung` bleibt append-only wie
|
||||
bisher. Stattdessen entsteht eine neue `entscheidung`-Zeile mit
|
||||
`Entscheidung: "abgelehnt"`; `GetLatestEntscheidungForAntrag` (bereits
|
||||
bestehender "letzte Zeile gewinnt"-Helfer) zeigt den Override überall
|
||||
dort, wo er ohnehin schon verwendet wird — keine weitere Codeänderung
|
||||
nötig, um die Überschreibung sichtbar zu machen.
|
||||
|
||||
**Frontend** (`internal/web/freigabe_handlers.go` + drei neue Templates):
|
||||
`GET/POST /verwaltung/genehmiger-rollen` (Admin: Rolle anlegen, Mitglieder
|
||||
zuordnen/entfernen — Formular nutzt einen einzelnen `<select>` mit drei
|
||||
`<optgroup>`s für die drei Bedingungstypen, kein abhängiges Dropdown,
|
||||
kein JavaScript), `GET/POST /verwaltung/freigabe-regeln` (Admin: Regel
|
||||
anlegen/löschen), `GET /freigaben` + `POST /freigaben/{id}/entscheiden`
|
||||
(jeder eingeloggte Nicht-Betreiber mit zugeordneter Genehmiger-Rolle:
|
||||
eigene offene Freigaben sehen und entscheiden, mit Pflicht-Kommentarfeld
|
||||
bei Ablehnung). Fall-Detail (`fall_detail.html`) zeigt die
|
||||
Freigabeschritt-Historie mit Ampel-Badge **unabhängig vom aktuellen
|
||||
Antragsstatus** — anfangs fälschlich auf `wartet_auf_freigabe`
|
||||
beschränkt gebaut, beim Live-Verifizieren aufgefallen: sobald der letzte
|
||||
Freigebende zustimmt und der Antrag auf `entschieden` springt, verschwand
|
||||
die komplette Freigabehistorie aus der Ansicht, was der Grundregel
|
||||
widerspricht, dass eine Entscheidung nie ohne vollständige Herleitung
|
||||
gezeigt wird. Der Hinweistext "Genehmigung erst endgültig, wenn alle
|
||||
erteilt sind" erscheint entsprechend nur noch, solange der Antrag
|
||||
tatsächlich noch auf Freigabe wartet.
|
||||
|
||||
**CSS-Bugfix, beim Live-Verifizieren gefunden (sitesweit, nicht nur
|
||||
diese Seite betreffend):** `.beitraege-liste > li:not(:has(> a)) form`
|
||||
hatte `flex: 0 0 auto` (kein Schrumpfen). Bei den bisherigen Listen
|
||||
(Nutzer, Abteilungen) enthielten diese eingebetteten Formulare nur
|
||||
Buttons, die naturgemäß schmal bleiben — die Genehmiger-Rollen-Seite ist
|
||||
die erste mit einem `<select>` darin, dessen Browser-Eigenbreite
|
||||
(bestimmt durch die längste Options-Beschriftung, hier E-Mail-Adressen)
|
||||
den ganzen Zeilen-Container über die Mobile-Viewport-Breite hinaustrieb
|
||||
(horizontales Scrollen der ganzen Seite). Fix: `max-width: 100%` auf
|
||||
derselben Regel ergänzt — generisch, wirkt auf jedes künftige
|
||||
`<select>` in diesem Listen-Pattern, nicht nur hier.
|
||||
|
||||
**Live end-to-end verifiziert** (curl gegen echten lokalen Server +
|
||||
echtes Postgres, nicht nur die Go-Testsuite): Happy Path (eine Regel,
|
||||
eine Rolle, Genehmigung → `wartet_auf_freigabe` → Freigabe erteilt →
|
||||
`entschieden` + 1 Registereintrag) und Kaskaden-Ablehnung (zwei Regeln,
|
||||
zwei Rollen, eine lehnt ab → beide Freigabeschritte `abgelehnt`, Antrag
|
||||
direkt `entschieden` als `abgelehnt`, 0 Registereinträge) — beide exakt
|
||||
wie spezifiziert. Chromium-Headless-Screenshots (Desktop 1440×900 +
|
||||
Mobile 390×844) aller vier neuen/geänderten Seiten bestätigen zusätzlich
|
||||
das responsive Layout nach dem enconf-Card-Pattern.
|
||||
|
||||
---
|
||||
|
||||
## Onboarding
|
||||
|
||||
**Firma (Ebene 1, öffentlich, `POST /register`):** Registrierungsformular
|
||||
@@ -818,12 +930,22 @@ wiederverwendet aus dem alten Produkt), ebenso `bewertung`,
|
||||
(`GET /wiedervorlage`: abgelaufene/bald ablaufende Genehmigungen und
|
||||
Genehmigungen, deren Werkzeug sich seither im Katalog geändert hat
|
||||
oder entfernt wurde. In-App-Liste, kein E-Mail-Versand.)
|
||||
8. ~~Konfigurierbarer Freigabe-Workflow (Mehrfach-Genehmigung)~~ —
|
||||
**erledigt** (siehe "Freigabe-Workflow" weiter oben: Genehmiger-Rollen,
|
||||
Freigabe-Regeln, Freigabeschritte, Migration 0017 — bewusste Abkehr
|
||||
von der ursprünglichen v1-Ausschlussentscheidung, siehe "Nicht bauen
|
||||
(v1)" unten).
|
||||
|
||||
Nicht bauen (v1): automatische Genehmigung ohne Mensch, Erkennung
|
||||
tatsächlicher Werkzeug-Nutzung, Mitarbeiterüberwachung (nichts, was
|
||||
Nutzung einzelner Personen auswertet), konfigurierbare Rollen mit
|
||||
eigener Oberfläche, Konzernstrukturen mit Vererbung, mandantenspezifische
|
||||
Regelwerke, Workflow-Designer, SAML, Schnittstellen zu Fremdsystemen.
|
||||
Nutzung einzelner Personen auswertet), Konzernstrukturen mit Vererbung,
|
||||
mandantenspezifische Regelwerke, SAML, Schnittstellen zu Fremdsystemen.
|
||||
|
||||
~~konfigurierbare Rollen mit eigener Oberfläche~~ / ~~Workflow-Designer~~
|
||||
— **am 2026-08-31 bewusst aufgehoben**, siehe "Freigabe-Workflow"
|
||||
weiter oben: Genehmiger-Rollen und Freigabe-Regeln sind jetzt genau das,
|
||||
nur mit der Bedingung auf die drei bereits von der Bewertungslogik
|
||||
berechneten Vokabulare beschränkt (kein beliebiger Regeleditor).
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user