# Datenaufbewahrung und Anonymisierungskonzept

## Aktueller Stand

Dieses Dokument füllt eine Lücke, die beim Abgleich von `PROCUREX-PLANUNGSENTSCHEIDUNGEN.md`
gegen den tatsächlichen Repo-Zustand identifiziert wurde (2026-08-17): Confluence Seite 10 nennt
unter `[ANNAHME]` ein „Lösch-/Anonymisierungskonzept für `Supplier`-Kontaktdaten und
`ApprovalRequest`-Begründungen nach fiktiver Aufbewahrungsfrist von 24 Monaten“, ohne dass dieses
Konzept bisher irgendwo ausformuliert war. PROCUREX ist ein fiktives Lernprojekt ohne echte
Personendaten (siehe README.md); dieses Dokument ist entsprechend eine **konzeptionelle**
Übung, keine Umsetzung einer realen Compliance-Anforderung.

**Umsetzungsstatus: implementiert (seit 2026-08-29).** `SupplierRetentionJob`
(`procurex-supplier/.../retention/`) und `ApprovalRetentionJob`
(`procurex-approval/.../retention/`) setzen die unten beschriebene Fristlogik als
`@Scheduled`-Jobs um, analog zu den bestehenden Outbox-Relays. Keine Flyway-Migration nötig, da
kein neues Spaltenschema entsteht — nur bestehende Felder werden überschrieben. Beide Jobs sind
per `procurex.retention.enabled` abschaltbar (Standard: an) und in Tests deaktiviert, damit
Integrationstests nicht mit dem periodischen Lauf um dieselbe Testcontainers-Datenbank
konkurrieren.

## Betroffene Daten

| Entität | Feld(er) | Schema.Tabelle | Fristbeginn |
|---|---|---|---|
| `Supplier` | `contact_email` | `supplier.supplier` | `updated_at` (letzte Statusänderung) |
| `ApprovalRequest` | `reason` | `approval.approval_request` | `decided_at` |

Beide Felder enthalten die einzigen personenbezogen wirkenden Freitext-/Kontaktdaten im
Datenmodell (siehe `procurex-supplier/.../domain/Supplier.java` und
`procurex-approval/.../domain/ApprovalRequest.java`). Alle übrigen Entitäten (Katalogartikel,
Bestellungen, Wareneingänge, Rechnungen) enthalten ausschließlich Geschäftsdaten ohne
Personenbezug.

## Konzept: Anonymisierung statt Löschung

Vollständiges Löschen der Datensätze würde die revisionssichere Nachvollziehbarkeit von
Bestellungen und Genehmigungsentscheidungen zerstören (siehe `ApprovalRequest`-Klassenkommentar:
„Revisionssicherer Beleg einer Genehmigungsentscheidung“, PRX-5/PRX-8). Deshalb: Anonymisierung
statt Löschung.

* **`Supplier.contactEmail`**: nach Ablauf der Frist wird der Wert durch einen Platzhalter
  (`anonymized@procurex.invalid`) ersetzt. `name`, `taxId` und `status` bleiben erhalten, da sie
  für die Nachvollziehbarkeit historischer Bestellungen (welcher Lieferant war zum Zeitpunkt X
  aktiv) benötigt werden und keinen direkten Personenbezug haben (Firmendaten, keine
  Kontaktperson).
* **`ApprovalRequest.reason`**: nach Ablauf der Frist wird der Freitext durch einen Platzhalter
  (`[anonymisiert nach Aufbewahrungsfrist]`) ersetzt. `orderId`, `decision` und `decidedAt`
  bleiben erhalten — die revisionssichere Kernaussage („wer hat wann wie entschieden“) bleibt
  damit auch nach Anonymisierung nachvollziehbar, nur der potenziell personenbezogene Freitext
  entfällt.

## Fristlogik (konzeptionell)

```
IF Supplier.updatedAt < now() - 24 Monate
   AND Supplier.contactEmail != 'anonymized@procurex.invalid'
THEN Supplier.contactEmail = 'anonymized@procurex.invalid'

IF ApprovalRequest.decidedAt < now() - 24 Monate
   AND ApprovalRequest.reason != '[anonymisiert nach Aufbewahrungsfrist]'
THEN ApprovalRequest.reason = '[anonymisiert nach Aufbewahrungsfrist]'
```

Tatsächliche Umsetzung: ein `@Scheduled`-Job je Modul (analog zu den bestehenden Outbox-Relays,
siehe ADR-0003 und `AbstractOutboxRelay`), der periodisch (Standard: alle 24h,
`procurex.retention.check-interval-ms`) prüft und in Batches von 200 Datensätzen anonymisiert, um
Lock-Contention mit dem laufenden Betrieb zu vermeiden. Bewusst **kein** hartes `DELETE`, damit
referentielle Integrität zu `PurchaseOrder` bzw. Fremdschlüsseln erhalten bleibt. Die 24-Monats-
Frist wird als 730 Tage angenähert (`procurex.retention.supplier-contact-email-days` /
`procurex.retention.approval-reason-days`, je Standard 730) — eine bewusste, dokumentierte
Vereinfachung für einfache `Instant`-Arithmetik ohne Kalendermonate.

## Bewusste Grenze

* Keine Betroffenenrechte-Workflows (Auskunft, Widerspruch) beschrieben, da es sich um fiktive
  Übungsdaten ohne echte betroffene Personen handelt (siehe Lernprojekt-Hinweis auf
  Confluence-Seiten 01/02/07).
* Die 24-Monats-Frist ist eine `[ANNAHME]` aus der ursprünglichen Planung, keine aus einer echten
  Rechtsgrundlage abgeleitete Vorgabe; die Umsetzung nähert sie als feste 730-Tage-Dauer an statt
  echter Kalendermonate.
* Kein Anonymisierungskonzept für Kafka-Event-Payloads oder den Audit-Log
  (`audit.audit_log_entry`) — Events und Audit-Einträge enthalten `orderId`/`decision`, aber
  keine Kontaktdaten, daher aktuell kein zusätzlicher Bedarf identifiziert.
* Keine Testcontainers-Integrationstests für die Jobs gegen eine echte PostgreSQL-Instanz — nur
  Unit-Tests mit gemocktem Repository (`SupplierRetentionJobTest`, `ApprovalRetentionJobTest`)
  plus Domain-Tests für die Anonymisierungs-Methoden selbst.

## Weitere Informationen

Siehe Confluence Seite 10 „APIs, Events, Daten und Security“ (CONFIRMED, Abschnitt „Daten“) sowie
`docs/runbooks/backup-restore.md` für die verwandte Frage der Datensicherung (nicht Löschung).
