feat(dsgvo): Löschkonzept/Aufbewahrungsfristen (Auto-Purge)
Security Audit / Python Dependency Audit (push) Has been cancelled
Security Audit / Node.js Dependency Audit (push) Has been cancelled

Neuer retention_service.py: Lohn-/zeitrelevante Daten (time_entries,
hours_payouts) werden nach konfigurierbarer Frist gelöscht
(company.settings.retention_lohn_years, Default 10 Jahre). Technische
Tabellen mit fester Frist: audit_logs (3 Jahre), abgelaufene
sessions/password_resets (sofort).

Täglicher Scheduler-Job (03:00 Uhr, Redis-Tageslock analog Reminder-Jobs)
plus manuelle Trigger: POST /companies/me/run-retention-purge
(COMPANY_ADMIN/HR, nur eigene Firma) und POST /admin/run-retention-purge
(SUPER_ADMIN, global inkl. technischer Tabellen).

Letzter offener Punkt aus dem DSGVO-Löschkonzept (Art. 15/17 bereits erledigt).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Gis16MnuwkYcivLrSxK1pD
This commit is contained in:
2026-08-27 09:04:27 +02:00
co-authored by Claude Sonnet 5
parent 71e3e82a4e
commit c60c0d6c90
6 changed files with 271 additions and 1 deletions
+15
View File
@@ -52,6 +52,21 @@ async def run_reminders_now(
return {"sent": result}
@router.post("/me/run-retention-purge")
async def run_retention_purge_now(
current_user: User = require_role(*_admin_roles),
db: AsyncSession = Depends(get_db),
):
"""Löscht Zeiterfassung/Auszahlungen der eigenen Firma, die älter als die
konfigurierte Aufbewahrungsfrist (company.settings.retention_lohn_years,
Default 10 Jahre) sind. Technische Tabellen (audit_logs/sessions) laufen
nur im globalen Scheduler-Job, nicht firmenscoped."""
from app.services.retention_service import run_retention_purge
result = await run_retention_purge(db, company_id=current_user.company_id)
await db.commit()
return {"deleted": result}
@router.patch("/me", response_model=CompanyOut)
async def update_my_company(
data: CompanyUpdate,
+15
View File
@@ -217,3 +217,18 @@ async def update_reseller(
last_name=reseller.last_name, is_active=reseller.is_active,
created_at=reseller.created_at, company_count=counts.get(reseller.id, 0),
)
@router.post("/run-retention-purge")
async def run_global_retention_purge(
current_user: User = require_role(*_sa),
db: AsyncSession = Depends(get_db),
):
"""Globaler DSGVO-Purge über alle Firmen: Zeiterfassung/Auszahlungen je Firma
(Frist konfigurierbar) plus firmenübergreifende technische Tabellen
(audit_logs > 3 Jahre, abgelaufene sessions/password_resets). Gleiche Logik
wie der tägliche Scheduler-Job."""
from app.services.retention_service import run_retention_purge
result = await run_retention_purge(db, company_id=None)
await db.commit()
return {"deleted": result}