-- Konfigurierbarer Mehrfach-Freigabe-Workflow (2026-08-31): manche -- Anträge brauchen zusätzlich zur normalen Fachebene-Entscheidung eine -- Freigabe durch bestimmte Personen (z. B. Datenschutzbeauftragte bei -- dsfa_erforderlich, Geschäftsführung bei hochrisiko). Bewusst additiv -- zur bestehenden app_user.role (Zugriffskontrolle) — eine -- Genehmiger-Rolle ist eine reine Freigabe-Funktion, keine Berechtigung, -- und eine Person kann mehrere davon gleichzeitig innehaben. -- Stammdaten wie abteilung, pro Mandant frei benennbar. CREATE TABLE genehmiger_rolle ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), account_id UUID NOT NULL REFERENCES account (id), name TEXT NOT NULL, created_at TIMESTAMPTZ NOT NULL DEFAULT now(), UNIQUE (account_id, name) ); -- Welche Personen eine Genehmiger-Rolle innehaben (n:m). CREATE TABLE nutzer_genehmiger_rolle ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), app_user_id UUID NOT NULL REFERENCES app_user (id), genehmiger_rolle_id UUID NOT NULL REFERENCES genehmiger_rolle (id), created_at TIMESTAMPTZ NOT NULL DEFAULT now(), UNIQUE (app_user_id, genehmiger_rolle_id) ); -- "Wenn Bedingung X zutrifft, ist zusätzlich eine Freigabe durch -- Genehmiger-Rolle Y nötig." Bedingung ist bewusst eine der bereits vom -- Regelwerk abgeleiteten Größen (Anforderungs-ID, Einstufungs-ID, -- Datenklasse-ID) — kein freier Regel-Editor, keine beliebige Logik. CREATE TABLE freigabe_regel ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), account_id UUID NOT NULL REFERENCES account (id), bedingung_typ TEXT NOT NULL CHECK (bedingung_typ IN ('anforderung', 'einstufung', 'datenklasse')), bedingung_wert TEXT NOT NULL, genehmiger_rolle_id UUID NOT NULL REFERENCES genehmiger_rolle (id), created_at TIMESTAMPTZ NOT NULL DEFAULT now() ); -- Pro Antrag ein Eintrag je durch eine Regel ausgelöster nötiger -- Freigabe. Nicht append-only (wie werkzeug) — ein Freigabeschritt ist -- eine einzelne Aufgabe, die von ausstehend in einen Endzustand -- übergeht, keine Historie mehrerer Entscheidungen zum selben Schritt. CREATE TABLE freigabeschritt ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), antrag_id UUID NOT NULL REFERENCES antrag (id), genehmiger_rolle_id UUID NOT NULL REFERENCES genehmiger_rolle (id), status TEXT NOT NULL DEFAULT 'ausstehend' CHECK (status IN ('ausstehend', 'genehmigt', 'abgelehnt')), entschieden_von UUID REFERENCES app_user (id), entschieden_am TIMESTAMPTZ, kommentar TEXT NOT NULL DEFAULT '', created_at TIMESTAMPTZ NOT NULL DEFAULT now() ); -- Ein Antrag, der auf zusätzliche Freigaben wartet, ist weder "nur -- eingereicht" noch schon "entschieden" — eigener Zwischenzustand. ALTER TABLE antrag DROP CONSTRAINT antrag_status_check; ALTER TABLE antrag ADD CONSTRAINT antrag_status_check CHECK (status IN ('entwurf', 'eingereicht', 'wartet_auf_freigabe', 'entschieden'));