Commit graph

60 commits

Author SHA1 Message Date
3216dd6d53 feat: add email notifications for disclosure request decisions
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>
2026-02-28 12:59:21 +00:00
5da1e523d3 feat: implement Gutachten-Statistik page with KPIs and charts
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>
2026-02-28 12:37:08 +00:00
cb73cf5461 feat: add yearly KW comparison chart and top 10 ICD box to Dashboard
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>
2026-02-28 12:11:51 +00:00
2e41242c9e feat: add year filter to Coding queue
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>
2026-02-27 17:37:04 +00:00
899c125a62 fix: show individual case data in Wochenübersicht preview instead of aggregated summaries
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>
2026-02-27 15:32:30 +00:00
27b3810250 feat: add inline preview for Wochenübersicht reports
- Backend stores per-KW summary in report_data (count, ICD status, FG split)
- WochenuebersichtViewer component shows expandable KW table with totals
- Expand/collapse pattern matching ReportsPage (ChevronRight/Down, useReportData)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 15:26:20 +00:00
efeb619b06 feat: persist Wochenübersicht exports, DAK-Mitarbeiter access + ICD upload
- 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>
2026-02-27 13:51:14 +00:00
6f6a721973 feat: add Wochenübersicht export + ICD import auto-detect
- 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>
2026-02-27 13:28:17 +00:00
48939f01dd feat: add Onko-Intensiv and Galle-Schild report types
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>
2026-02-27 12:47:59 +00:00
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
28e4dc4333 fix: allow reactivating rejected disclosures in admin view
"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>
2026-02-27 08:45:26 +00:00
78ae11fb99 feat: add reactivate and delete buttons for admin disclosure view
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>
2026-02-27 08:25:46 +00:00
547bfa3ea5 feat: add re-request and delete buttons for inactive disclosures
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>
2026-02-27 08:11:08 +00:00
1bedbcf243 fix: resolve TypeError in disclosure revoke due to naive/aware datetime comparison
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>
2026-02-27 07:57:59 +00:00
95e84a6978 fix: filter report data by max KW when generating reports
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>
2026-02-26 23:16:20 +00:00
dbb5afaeb9 feat: add inline report viewer with 5-tab sheet display
- Backend: GET /reports/{id}/data endpoint returns stored report JSON
- Frontend: ReportViewer component renders all 5 Excel sheets as tabs
  (KW gesamt, Fachgebiete, Gutachten, Therapieänderungen, ICD onko)
- ReportsPage: clickable rows with inline expansion to view reports
- Empty KW rows filtered, summary row at bottom, German labels
- Download button still available alongside inline view

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 22:57:35 +00:00
32cee4d30d feat: add KPI links, My Disclosures page, and extend Admin Disclosures
- 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>
2026-02-26 22:19:46 +00:00
56c0c6e662 fix: remove patient names from coding template and restrict to admin
- 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>
2026-02-26 17:20:28 +00:00
c302a91411 feat: auto-update fall_id when KVNR is entered on a case
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>
2026-02-26 17:09:50 +00:00
a436580b03 feat: use KVNR instead of Nachname in fall_id generation
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 17:04:48 +00:00
1612d449c0 fix: make nachname nullable in CaseResponse to support data masking
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>
2026-02-26 16:24:56 +00:00
9825489781 feat: add case masking for dak_mitarbeiter and disclosure endpoints
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 16:07:30 +00:00
00076e2c00 feat: add disclosure service with create, review, and check logic
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 16:04:54 +00:00
3496e4acfe feat: add disclosure schemas and case response masking helper
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 16:03:58 +00:00
bb13ec80a2 feat: add DisclosureRequest model and migration
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 16:02:52 +00:00
32127c30c3 fix: filter all case queries to DAK insurance only
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>
2026-02-26 10:20:33 +00:00
fc609401c3 feat: add MFA disable (self-service + admin reset) endpoints
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 09:40:57 +00:00
661f1ce552 feat: add avatar upload/delete endpoints with static file serving
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 09:39:21 +00:00
1fa5d20d40 feat: add PUT /api/auth/profile endpoint for self-service profile update
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 09:37:17 +00:00
6bc0b3ac5a feat: add ProfileUpdate schema and extend UserResponse with profile fields
- Add first_name, last_name, display_name, avatar_url to UserResponse
- Add ProfileUpdate schema for self-service profile editing
- Add MFADisableRequest schema for password-confirmed MFA disable

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 09:35:54 +00:00
d09fdccc75 feat: add first_name, last_name, display_name, avatar_url to User model
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>
2026-02-26 09:34:33 +00:00
400520aebd fix: auto-detect CSV delimiter (comma, semicolon, tab)
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>
2026-02-24 23:09:16 +00:00
f8a8befb19 fix: add versicherung field to CaseUpdate schema
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>
2026-02-24 13:05:55 +00:00
97731552c5 feat: add editable KVNR field in case detail view with dedicated API endpoint
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 11:07:49 +00:00
7ef1fc9335 feat: add checkbox selection and bulk delete for reports
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 10:40:19 +00:00
9650889d24 feat: add audit logging for login, logout, imports, and report generation
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 10:36:25 +00:00
4a3648a018 feat: serve frontend SPA from FastAPI backend
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 10:10:11 +00:00
9d79ba35bc fix: wrap vorjahr data in "summary" key to match excel_export structure
get_vorjahr_summary returned a flat dict, but excel_export looked for
data under vj.get("summary", {}), resulting in empty Vorjahr columns.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 09:34:19 +00:00
8502c7a7fb fix: use one-decimal percentage format in Sheet 1 (0.0% instead of 0%)
Shows "0,8%" instead of "1%" for small ratios like Ablehnungen/Erstberatungen.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 09:32:32 +00:00
4467e1b1cb fix: upsert report metadata to avoid duplicate key error on re-generation
When generating a report for the same jahr/kw, update the existing
record instead of inserting a duplicate (which caused IntegrityError).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 09:28:40 +00:00
db963a8e12 fix: align excel_export keys with report_service output
report_service uses 'summary'/'weekly'/'keine_rm'/'gutachten' but
excel_export expected 'totals'/'weeks'/'keine_rueckmeldung'/
'gutachten_gesamt'. Also fix nested gesamt dict access in sheet3
and icd_codes path from sheet5.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 09:26:11 +00:00
590125073c fix: support token query param for file download endpoints
Browser-initiated downloads (window.open) cannot set Authorization
headers. Accept ?token= query parameter as fallback on the report
download and coding-template endpoints.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 09:20:14 +00:00
5ee1cff0d6 fix: use SQLAlchemy Integer type in .cast() calls in report_service
.cast(int) uses Python's builtin int, which lacks SQLAlchemy's
_isnull attribute. Replace all occurrences with .cast(Integer).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 09:06:48 +00:00
0e4d19e8bc fix: use savepoints for row-by-row error isolation in Excel import
Prevents a single duplicate fall_id from rolling back the entire
import session. Each row insert now uses db.begin_nested() so
constraint violations are isolated to the offending row.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 09:01:42 +00:00
93884f3c8d fix: add missing cases API, import router, and case schemas
These files were created by a subagent but not included in the commit.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 08:49:55 +00:00
3f8f96097d fix: URL-encode DB password in connection string
Passwords with special characters (@, &, etc.) broke the SQLAlchemy
connection URL parsing.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 08:48:28 +00:00
1748379253 feat: Excel export in Berichtswesen format + historical import
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 08:07:28 +00:00
d6fb04d5a7 feat: coding queue, reports API, Excel sync
- Add coding_service.py with queue retrieval, single + batch coding updates
- Add report schemas (DashboardKPIs, WeeklyDataPoint, ReportMeta)
- Add coding API router with /queue, PUT /{case_id}, POST /batch endpoints
- Add reports API router with /dashboard, /weekly, /generate, /download, /list
- Add excel_sync.py for bidirectional Abrechnung DB<->XLSX sync
- Register coding and reports routers in main.py

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 08:03:17 +00:00
72b6f784fc feat: report service — all 5 sheet calculations + year-over-year
Implements report_service.py with:
- Sheet 1: Auswertung KW gesamt (weekly totals + year summary)
- Sheet 2: Auswertung nach Fachgebieten (per-KW per-Fallgruppe)
- Sheet 3: Auswertung Gutachten (alternative/bestaetigung per group)
- Sheet 4: Auswertung Therapieaenderungen (TA metrics per KW)
- Sheet 5: Auswertung ICD onko (ICD code frequency for onko)
- Dashboard KPIs (total_cases, pending_icd, pending_coding, etc.)
- generate_full_report() for all 5 sheets combined

Implements vorjahr_service.py with:
- Cached year-over-year comparison via yearly_summary table
- get_vorjahr_summary() for Sheet 1 comparison columns
- get_vorjahr_detail() for full previous-year breakdown
- refresh_vorjahr_cache() for cache invalidation

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 08:02:53 +00:00
f4afea7f85 feat: historical Excel import (Abrechnung_DAK.xlsx)
Add service and standalone script to import all cases from the master
Excel workbook into the database. Handles 5 year-sheets (2020-2022,
2023, 2024, 2025, 2026) with dynamic column mapping, fallgruppe
normalization, boolean/date parsing, phone number formatting, and
duplicate detection. Supports dry-run mode and per-sheet import.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 07:58:04 +00:00