When an admin creates an invitation with an email address, an email is
now automatically sent containing the registration link and an attached
PDF guide (3 pages: registration steps, feature overview, contact info).
- Add fpdf2 for PDF generation with Unicode font support
- Add PDF guide generator (backend/app/services/pdf_guide.py)
- Extend send_email() to support file attachments
- Fire-and-forget email in create_invitation endpoint
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
PostgreSQL requires globally unique index names (unlike MySQL which scopes
them per table). Prefix generic names: idx_user→idx_al_user/idx_rt_user,
idx_token→idx_rt_token, idx_case/idx_code/idx_haupt→idx_icd_*.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Switch from pymysql to psycopg2-binary, update connection string to
postgresql+psycopg2, replace MySQL-specific JSON imports with generic
SQLAlchemy JSON, convert MySQL prefix index to PostgreSQL left() function,
and remove FetchedValue() (unnecessary with PostgreSQL).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Fall-ID is redundant for DAK staff since KVNR is shown separately.
Export now only includes Datum, KVNR, Fallgruppe, ICD, Gutachten, Status.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Extends DashboardKPIs with total_abgerechnet/pending_abrechnung. Adds
new GET /reports/dashboard/top-gutachter endpoint (admin-only). Frontend
shows Abrechnungsstatus donut + Gutachter-Verteilung progress bars in a
new third row, visible only to admins.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Users can save frequently used filter combinations (year, fallgruppe,
ICD status, search) as named presets. Stored server-side in new
filter_presets table (max 10 per user). Star-icon Popover on CasesPage
to load, save, or delete presets. TanStack Query hooks for CRUD.
New files: FilterPreset model, migration 009, API router, schemas,
useFilterPresets hook.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds "Passwort vergessen?" to login page with email-based password
reset flow. Backend generates secure token (SHA-256 hashed, 1h expiry),
sends reset link via SMTP, and validates on submission. Includes rate
limiting (3 requests/hour/email), audit logging, and account unlock
on successful reset. New ResetPasswordPage with password confirmation.
New DB table: password_reset_tokens (migration 008).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
New GET /cases/export endpoint generates .xlsx with openpyxl using the
same filters as the case list (year, fallgruppe, ICD status, search).
Role-aware columns: admins see patient names, DAK staff does not.
Frontend adds a Download button next to the filter bar with loading
state. Refactors shared query logic into _build_case_query helper.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The previous implementation compared a partial current year against a
full previous year, producing misleading percentages. Now determines the
max KW with data in the selected year and filters the previous year to
the same KW range for a fair comparison.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Dashboard endpoint now returns prev_kpis (previous year's KPIs) alongside
current KPIs. KpiCard component shows percentage change with colored trend
indicators (green up, red down, grey neutral). Also marks completed items
in todo.md (notification center, dark mode toggle were already implemented).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace direct Notification inserts with notification_service functions
that send both in-app and email notifications. Admins receive an email
when a DAK-Mitarbeiter submits a new disclosure request. The requesting
Mitarbeiter receives an email when their request is approved (with
expiration date) or rejected. SMTP was already configured.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add full statistics page replacing placeholder: 4 KPI cards (total, Bestätigung,
Alternative, Uncodiert), stacked bar chart for gutachten types per KW, donut chart
for type distribution, grouped bar chart for therapy changes per KW, and horizontal
bar chart for therapy change reasons. Includes new backend endpoint and service
function combining sheet3/sheet4 data with KPI aggregation. Also adds feature
roadmap todo.md.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Backend: two new endpoints /reports/dashboard/yearly-comparison and
/reports/dashboard/top-icd for multi-year KW case counts and ICD
frequency aggregation.
Frontend: grouped bar chart comparing KW across years (2022+) and
a ranked top 10 ICD list with progress bars on the Dashboard.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add jahr parameter to the coding queue API endpoint and frontend
filter, allowing admins to filter the coding queue by year.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Preview now mirrors the Excel export content: KVNR, Datum, x-markers
for Erstgespräch/Abbruch/Unterlagen/Gutachten/FG, and ICD-10 per case,
grouped by KW blocks.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Wochenübersicht exports now persisted in DB (WeeklyReport) + disk
- POST /reports/wochenuebersicht/generate replaces GET (admin-only)
- POST /reports/wochenuebersicht/upload-icd for ICD upload (all roles)
- GET /reports/list supports report_type_prefix filter
- WochenuebersichtPage: report table + ICD drag-drop upload for all roles
- Route + sidebar open to all authenticated users
- ReportsPage filters out wochenuebersicht report types
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- New Excel export service for weekly DAK summary sheets (c2s / c2s_g_s variants)
- New API endpoint GET /reports/wochenuebersicht (admin-only)
- ICD import auto-detects format (coding template vs. Wochenübersicht KVNR-based)
- New admin frontend page with download form
- Route + sidebar navigation entry
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds report_type support across the full stack:
- Backend: REPORT_TYPES mapping, fallgruppen filter in all 5 sheet
calculations, dynamic Excel columns, report_type DB column with
Alembic migration 007
- Frontend: report type dropdown in generation form, type column in
reports table, dynamic fallgruppen in ReportViewer
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- 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>
"Wieder aufleben" now also works for rejected disclosures, setting
status to approved with a new 24h window. Both buttons (reactivate
and delete) now appear for rejected and expired/revoked disclosures.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
After revoking or expiry, admins now see "Wieder aufleben" (reactivate
with new 24h window) and "Verwerfen" (hard delete) buttons. Rejected
disclosures also show "Verwerfen".
Backend: PUT .../reactivate and DELETE endpoints for admin disclosures.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
DAK employees now see "Erneute Anfrage" (opens dialog with pre-filled
reason) and "Verwerfen" (hard delete) buttons for expired, revoked,
or rejected disclosure requests on their My Disclosures page.
Backend: new DELETE /cases/disclosure-requests/{id} endpoint.
Frontend: new hooks useDeleteDisclosure, useRequestDisclosure.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
PyMySQL returns naive datetimes from MySQL DATETIME columns, but
revoke_disclosure() compared them with timezone-aware datetime.now(timezone.utc),
causing an unhandled TypeError (not caught by except ValueError) and a 500 error.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Previously, generate_full_report() ignored the kw parameter for data
filtering — it was only stored as metadata. This caused all reports to
contain data up to the latest available KW, making historical reports
(e.g., for KW 8) identical to the current one.
Now all 5 sheet calculation functions accept an optional max_kw parameter.
When generating a report for a specific KW, only cases with kw <= max_kw
are included. Dashboard and vorjahr callers are unaffected (max_kw=None).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Dashboard KPI cards are now clickable with role-based links
- New GutachtenStatistikPage placeholder at /gutachten-statistik
- New "Meine Freigaben" page for DAK-Mitarbeiter to view/revoke own disclosures
- Backend: GET /cases/my-disclosure-requests, PUT /cases/disclosure-requests/{id}/revoke
- Admin Disclosures page: full history with status tabs and revoke capability
- Backend: PUT /admin/disclosure-requests/{id}/revoke, default shows all statuses
- Sidebar: "Meine Freigaben" entry for dak_mitarbeiter role
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Remove Nachname/Vorname columns from ICD coding template (DSGVO)
- Restrict /cases/coding-template endpoint to admin-only
- ICD import reads last column for backwards compatibility
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When a KVNR is entered on a case that has a random-suffix or legacy
Nachname-based fall_id, the fall_id is automatically rebuilt using
the new KVNR.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replaces Nachname-based fall_ids with KVNR or random 6-char suffix
for all existing cases in the database.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
CaseResponse.nachname was required (str) but the masking function sets
it to None for dak_mitarbeiter. This caused Pydantic validation errors
(500) making the case list empty for non-admin users.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add VERSICHERUNG_FILTER="DAK" to config and apply it to all case
queries: list, detail, pending-icd, pending-coding, coding queue,
dashboard KPIs, all 5 report sheets, and excel sync export.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add 4 new nullable profile fields to support the upcoming account
management (Kontoverwaltung) feature. Includes Alembic migration
that has been applied to production database.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
German Excel/CRM exports often use semicolons instead of commas.
The parser now uses csv.Sniffer to auto-detect the delimiter,
fixing the issue where semicolon-delimited CSVs produced 0 rows.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The field was editable in the frontend but missing from the backend
CaseUpdate Pydantic schema, causing changes to be silently dropped.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>