M8: Loesch-/Anonymisierungsprozess fuer Teilnehmer und Mandanten

Zwei getrennte Flows:

- Teilnehmer anonymisieren statt loeschen (ledger_anonymize_participant):
  Name/E-Mail/PayPal-Name werden durch einen Platzhalter ersetzt, das
  Mitglied deaktiviert und vom Login-Konto getrennt. Buchungshistorie
  bleibt fuer die Kassenfuehrung erhalten, konsistent mit dem
  Storno-statt-Delete-Prinzip. Default-Mandant spiegelt die
  Anonymisierung in kl_Mitarbeiter (Email dort NOT NULL UNIQUE, bekommt
  Platzhalter statt NULL). Inhaber kann nicht anonymisiert werden.
- Mandant vollstaendig loeschen (mandant-loeschen.php, nur Inhaber):
  erfordert exakte Eingabe des Kundenkuerzels, loescht die tenants-Zeile;
  alle tenant-scoped Tabellen kaskadieren per Fremdschluessel. users
  bleiben bestehen (koennen zu mehreren Mandanten gehoeren). Der
  migrierte Default-Mandant ist ausgenommen, da seine kl_Mitarbeiter-
  Historie sonst verwaisen wuerde.

Live getestet: Mitglied mit Buchungshistorie anonymisiert (Historie
blieb erhalten), Mandantenloeschung mit falscher/richtiger Bestaetigung
geprueft, vollstaendiger Cascade-Delete ueber alle tenant-scoped Tabellen
verifiziert, globale users-Zeile bleibt korrekt erhalten.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-15 20:17:57 +02:00
co-authored by Claude Sonnet 5
parent f3045dbab9
commit f60c0bb85e
7 changed files with 218 additions and 3 deletions
+47 -2
View File
@@ -149,17 +149,62 @@ scripts/http-smoke.php
geprüft, verifiziert dass keine Passwörter enthalten sind. Testdaten
anschließend entfernt.
## Lösch-/Anonymisierungsprozess
Umgesetzte Dateien:
```text
app/ledger.php (ledger_anonymize_participant)
mitarbeiterverwalten.php
mandant-loeschen.php
konto.php
scripts/http-smoke.php
```
Zwei getrennte Flows, je nachdem was gelöscht werden soll:
- **Teilnehmer anonymisieren** statt löschen: Name, E-Mail und PayPal-Name
werden durch einen nicht-identifizierenden Platzhalter ersetzt, das
Mitglied wird deaktiviert und vom Login-Konto getrennt
(`ledger_anonymize_participant()`). Die Ledger-Historie (Buchungen)
bleibt für die Kassenführung erhalten konsistent mit dem
„keine harten Deletes"-Prinzip für Buchungen. Für den Default-Mandanten
wird die verknüpfte `kl_Mitarbeiter`-Zeile mitanonymisiert (`Email` ist
dort NOT NULL UNIQUE, bekommt also einen Platzhalter statt NULL). Der
Inhaber kann nicht anonymisiert werden. Button „Anonymisieren" mit
JS-Bestätigungsdialog in `mitarbeiterverwalten.php`.
- **Mandant vollständig löschen**: neue Seite `mandant-loeschen.php`
(nur Inhaber). Erfordert die exakte Eingabe des Kundenkürzels zur
Bestätigung. Löscht die `tenants`-Zeile; alle tenant-scoped Tabellen
(`participants`, `ledger_entries`, `notices`, `tenant_memberships`,
`audit_log`, `outbound_emails`, `payment_import_batches`/`_rows`,
`tenant_settings`) sind per Fremdschlüssel `ON DELETE CASCADE`
verknüpft und verschwinden automatisch mit. `users` bleiben bestehen,
da ein Login-Konto zu mehreren Mandanten gehören kann. Der migrierte
Default-Mandant ist von dieser Selbstbedienungs-Löschung ausgenommen
(seine `kl_Mitarbeiter`-Historie hat keinen Fremdschlüssel zu `tenants`
und würde verwaisen); dafür verweist die Seite an den Betreiber.
- Beide Aktionen werden auditiert (`participant.anonymized`; die
Mandantenlöschung selbst nicht, da mit ihr auch das Audit-Log dieses
Mandanten verschwindet das ist beabsichtigt, echte Löschung soll
keine Spuren hinterlassen).
Live getestet: Mitglied mit Buchungshistorie angelegt, anonymisiert
Name/E-Mail weg, Jahresstriche unverändert erhalten. Mandantenlöschung mit
falscher Bestätigung abgewiesen, mit korrekter Bestätigung vollständig
durchgeführt; anschließend geprüft, dass wirklich alle tenant-scoped
Tabellen leer sind und die globale `users`-Zeile erhalten bleibt.
## Prüfstatus
- Golden Master: grün mit 104 Assertions.
- M4 Ledger-Migration: grün mit 73 Assertions.
- M3 Settings-Flow: grün mit 13 Assertions.
- HTTP-Smoke: grün mit 27 geprüften Seiten.
- HTTP-Smoke: grün mit 28 geprüften Seiten.
- M8 Mandanten-Isolation: grün mit 10 Assertions.
- M8 Rollenmatrix: grün mit 55 Assertions.
## Noch offen in M8
- Content-Security-Policy (braucht Template-Bereinigung der Inline-Styles).
- Lösch-/Anonymisierungsprozess für Teilnehmer und Kunden.
- Backup-/Restore-Prozess und Monitoring/Fehlerlogging dokumentieren.