-- GoBD-konformes Audit-Log: jede MA-Änderung wird mit Hash-Chain -- gespeichert, manipulationssicher, nur INSERT erlaubt. -- ==================================================================== -- Format: jede Zeile enthält content_hash = SHA256 über (prev_hash + -- entity + entity_id + actor + timestamp + diff_json). Wenn ein -- Eintrag nachträglich geändert wird, bricht die Kette und das ist -- erkennbar. -- -- entity: "employee" | "position" | "org_unit" | … -- entity_id: UUID des betroffenen Records -- action: "create" | "update" | "delete" | "activate" | "deactivate" -- actor: E-Mail des Admin-Users (oder "system" für Bulk/Cron) -- diff: JSON mit { field: { before, after } } — nur geänderte -- Felder -- -- Aufbewahrung: 10 Jahre laut GoBD; aktuell keine harte Retention, -- App-Doku verweist auf manuelle Archivierung via CSV-Export. create table if not exists public.change_log ( id uuid primary key default gen_random_uuid(), occurred_at timestamptz not null default now(), entity text not null, entity_id uuid not null, action text not null, actor text not null, diff jsonb not null default '{}'::jsonb, prev_hash text, -- hash des vorherigen eintrags (kette) content_hash text not null -- sha256 dieses eintrags ); create index if not exists change_log_entity_idx on public.change_log (entity, entity_id, occurred_at desc); create index if not exists change_log_occurred_at_idx on public.change_log (occurred_at desc); -- Schreibschutz auf Updates/Deletes — service-role-only, aber -- explizit als Trigger gegen Versehen. create or replace function public.protect_change_log() returns trigger language plpgsql as $$ begin raise exception 'change_log ist immutable — keine Updates oder Deletes erlaubt'; end; $$; drop trigger if exists trg_protect_change_log_update on public.change_log; create trigger trg_protect_change_log_update before update on public.change_log for each row execute function public.protect_change_log(); drop trigger if exists trg_protect_change_log_delete on public.change_log; create trigger trg_protect_change_log_delete before delete on public.change_log for each row execute function public.protect_change_log(); alter table public.change_log enable row level security; -- Service-Role-only — anon hat gar keinen Zugriff.