-- PROJ: trash-staged-deletion -- Doku-only (siehe README.md in diesem Verzeichnis) — die tatsächliche -- Ausführung passiert idempotent über internal/storage/trash.go -- initTrashSchema(), aufgerufen aus (*Store).initSchema(). -- -- Papierkorb + gestaffeltes Löschkonzept (GoBD): -- * documents.deleted_at/deleted_by = Soft-Delete (Papierkorb). Die WORM-Datei -- bleibt physisch unangetastet (chmod 0440) bis ein Löschantrag den Status -- 'executed' erreicht. -- * document_delete_requests = Vier-/Zwei-Augen-Workflow für finales Löschen: -- User A stellt Antrag ('pending'), ein anderer domain_admin (User B) -- bestätigt ('confirmed'->'executed'). Retention (retain_until) wird bei -- Antrag UND Bestätigung geprüft; ein blockierter Versuch wird als -- 'blocked_retention' protokolliert (Nachvollziehbarkeit). -- * Finales Löschen: physische Datei via os.Remove entfernt, DB-Row als -- Tombstone behalten (storage_path/ocr_text geleert, content_hash bleibt). -- * Das Vier-Augen-Prinzip (confirmed_by != requested_by) wird im Go-Store -- erzwungen, NICHT per CHECK-Constraint (confirmed_by wird erst später -- gesetzt). ALTER TABLE documents ADD COLUMN IF NOT EXISTS deleted_at TIMESTAMPTZ; ALTER TABLE documents ADD COLUMN IF NOT EXISTS deleted_by BIGINT; CREATE TABLE IF NOT EXISTS document_delete_requests ( id BIGSERIAL PRIMARY KEY, document_id BIGINT NOT NULL REFERENCES documents(id) ON DELETE CASCADE, tenant_id BIGINT NOT NULL, requested_by BIGINT NOT NULL, requested_at TIMESTAMPTZ NOT NULL DEFAULT now(), confirmed_by BIGINT, confirmed_at TIMESTAMPTZ, status TEXT NOT NULL DEFAULT 'pending' CHECK (status IN ('pending','confirmed','executed','cancelled','blocked_retention')), UNIQUE(document_id, status) ); CREATE INDEX IF NOT EXISTS idx_ddr_tenant_status ON document_delete_requests(tenant_id, status);