dak.c2s/backend/app/schemas/case.py
CCS Admin 5f957ee8ed fix: mask contact fields for non-admin users, require disclosure for visibility
- Add strasse, plz, ort, email, telefonnummer, mobiltelefon, ansprechpartner
  to SENSITIVE_FIELDS in backend (nullified without disclosure)
- Add visibleTo: 'admin' to all Kontakt fields in frontend fieldConfig
- Consolidate _utcnow_naive() usage across all disclosure service functions
  for consistent naive datetime handling with MySQL

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 11:13:32 +00:00

146 lines
4.4 KiB
Python

"""Pydantic schemas for case list, detail, update, ICD, and coding endpoints."""
from datetime import date, datetime
from typing import Optional
from pydantic import BaseModel
class CaseResponse(BaseModel):
"""Full representation of a case returned by list and detail endpoints."""
id: int
fall_id: Optional[str] = None
crm_ticket_id: Optional[str] = None
jahr: int
kw: int
datum: date
anrede: Optional[str] = None
vorname: Optional[str] = None
nachname: Optional[str] = None
geburtsdatum: Optional[date] = None
kvnr: Optional[str] = None
versicherung: str
icd: Optional[str] = None
fallgruppe: str
strasse: Optional[str] = None
plz: Optional[str] = None
ort: Optional[str] = None
email: Optional[str] = None
ansprechpartner: Optional[str] = None
telefonnummer: Optional[str] = None
mobiltelefon: Optional[str] = None
unterlagen: bool
unterlagen_verschickt: Optional[date] = None
erhalten: Optional[bool] = None
unterlagen_erhalten: Optional[date] = None
unterlagen_an_gutachter: Optional[date] = None
gutachten: bool
gutachter: Optional[str] = None
gutachten_erstellt: Optional[date] = None
gutachten_versendet: Optional[date] = None
schweigepflicht: bool
ablehnung: bool
abbruch: bool
abbruch_datum: Optional[date] = None
gutachten_typ: Optional[str] = None
therapieaenderung: Optional[str] = None
ta_diagnosekorrektur: bool
ta_unterversorgung: bool
ta_uebertherapie: bool
kurzbeschreibung: Optional[str] = None
fragestellung: Optional[str] = None
kommentar: Optional[str] = None
sonstiges: Optional[str] = None
abgerechnet: bool
abrechnung_datum: Optional[date] = None
import_source: Optional[str] = None
imported_at: datetime
updated_at: datetime
disclosure_granted: bool = False
disclosure_expires_at: Optional[datetime] = None
model_config = {"from_attributes": True}
class CaseListResponse(BaseModel):
"""Paginated case list response."""
items: list[CaseResponse]
total: int
page: int
per_page: int
class CaseUpdate(BaseModel):
"""Partial update for case fields (admin only).
All fields are optional; only provided fields are applied.
"""
anrede: Optional[str] = None
vorname: Optional[str] = None
nachname: Optional[str] = None
geburtsdatum: Optional[date] = None
kvnr: Optional[str] = None
versicherung: Optional[str] = None
strasse: Optional[str] = None
plz: Optional[str] = None
ort: Optional[str] = None
email: Optional[str] = None
ansprechpartner: Optional[str] = None
telefonnummer: Optional[str] = None
mobiltelefon: Optional[str] = None
unterlagen: Optional[bool] = None
unterlagen_verschickt: Optional[date] = None
erhalten: Optional[bool] = None
unterlagen_erhalten: Optional[date] = None
unterlagen_an_gutachter: Optional[date] = None
gutachten: Optional[bool] = None
gutachter: Optional[str] = None
gutachten_erstellt: Optional[date] = None
gutachten_versendet: Optional[date] = None
schweigepflicht: Optional[bool] = None
ablehnung: Optional[bool] = None
abbruch: Optional[bool] = None
abbruch_datum: Optional[date] = None
kurzbeschreibung: Optional[str] = None
fragestellung: Optional[str] = None
kommentar: Optional[str] = None
sonstiges: Optional[str] = None
abgerechnet: Optional[bool] = None
abrechnung_datum: Optional[date] = None
class ICDUpdate(BaseModel):
"""Payload for setting ICD codes on a case."""
icd: str
class CodingUpdate(BaseModel):
"""Payload for setting coding/gutachten classification on a case."""
gutachten_typ: str # "Bestätigung" or "Alternative"
therapieaenderung: str # "Ja" or "Nein"
ta_diagnosekorrektur: bool = False
ta_unterversorgung: bool = False
ta_uebertherapie: bool = False
SENSITIVE_FIELDS = (
"nachname", "vorname", "geburtsdatum", "anrede",
"strasse", "plz", "ort", "email", "telefonnummer", "mobiltelefon", "ansprechpartner",
)
def mask_case_for_mitarbeiter(case_dict: dict, disclosure_granted: bool = False) -> dict:
"""Remove sensitive personal data fields for dak_mitarbeiter users.
If disclosure_granted is True, the fields remain visible.
"""
if not disclosure_granted:
for field in SENSITIVE_FIELDS:
case_dict[field] = None
case_dict["disclosure_granted"] = disclosure_granted
return case_dict