feat(planning): ship holiday-aware planning and assistant upgrades
This commit is contained in:
@@ -0,0 +1,492 @@
|
||||
# Assistant Capability Gap Analysis
|
||||
|
||||
## Zielbild
|
||||
|
||||
Der AI Assistant soll grundsaetzlich alles lesen und ausfuehren koennen, was ein eingeloggter Nutzer gemaess seiner Rolle, Permission-Overrides und Objekt-Sichtbarkeit auch kann. Er darf weder weniger fachlich relevante Informationen sehen als die UI noch mehr Rechte erhalten als der Nutzer selbst.
|
||||
|
||||
## Ist-Zustand
|
||||
|
||||
Der Assistant ist bereits relativ breit aufgestellt:
|
||||
|
||||
- Er haengt an `packages/api/src/router/assistant.ts`.
|
||||
- Er exponiert aktuell 88 Function-Calling-Tools aus `packages/api/src/router/assistant-tools.ts`.
|
||||
- Er deckt viele Kernbereiche bereits ab: Ressourcen, Projekte, Allokationen, Urlaub, Feiertagsabfragen, Staffing, Demand, Dashboard, einfache Insights, Kommentare, Notifications, Tasks, Reporting, Szenario-Simulation und Navigation.
|
||||
|
||||
Trotzdem ist die Paritaet zur eigentlichen App/API noch nicht erreicht. Die groessten Luecken liegen nicht bei "gar nichts vorhanden", sondern bei:
|
||||
|
||||
- fehlenden Admin- und Konfigurationsfaehigkeiten,
|
||||
- fehlenden tiefen Fach-Readmodels,
|
||||
- inkonsistentem Permission-Gating,
|
||||
- fehlender serverseitiger Absicherung fuer schreibende AI-Aktionen,
|
||||
- und einigen objektbezogenen Sichtbarkeitsfehlern.
|
||||
|
||||
## Architektur des Assistants
|
||||
|
||||
### Routing und Tool-Aufruf
|
||||
|
||||
- `assistant.chat` baut den System Prompt, filtert die verfuegbaren Tools und laesst das Modell Tools aufrufen.
|
||||
- Der eigentliche Datenzugriff liegt fast komplett in `executeTool(...)` und den `executors` in `packages/api/src/router/assistant-tools.ts`.
|
||||
|
||||
### Permission-Gating
|
||||
|
||||
Es gibt aktuell vier Permission-/Scope-Ebenen:
|
||||
|
||||
1. Tool-Sichtbarkeit vor dem Modellaufruf in `assistant.ts`
|
||||
- `TOOL_PERMISSION_MAP` blendet bestimmte Schreib-Tools aus.
|
||||
- `COST_TOOLS` blendet kostenrelevante Tools ohne `viewCosts` aus.
|
||||
|
||||
2. Laufzeit-Guards in einzelnen Tool-Executors
|
||||
- Viele Mutationen nutzen `assertPermission(...)`.
|
||||
|
||||
3. Objekt-/Ownership-Checks in einzelnen Tools
|
||||
- Beispiel: `update_task_status` und `execute_task_action` pruefen, ob das Task dem Nutzer gehoert.
|
||||
|
||||
4. Normale DB-/TRPC-Semantik der zugrunde liegenden Queries
|
||||
- Diese ist aber im Assistant nicht automatisch identisch mit den eigentlichen Routern, weil die Assistant-Tools oft eigene DB-Queries verwenden.
|
||||
|
||||
## Assistant Capability Matrix
|
||||
|
||||
### Bereits gut abgedeckt
|
||||
|
||||
- Ressourcen lesen und teilweise verwalten
|
||||
- Projekte lesen und teilweise verwalten
|
||||
- Allokationen lesen sowie erstellen/stornieren/status aendern
|
||||
- Vacation-Grundfaelle: erstellen, genehmigen, ablehnen, stornieren, Balance, Overlap, Pending Approvals
|
||||
- Feiertage aufgeloest nach Region oder Ressource lesen
|
||||
- Staffing/Demand-Grundfaelle
|
||||
- Dashboard-Detailabfragen auf grober Ebene
|
||||
- Basis-Insights
|
||||
- Kommentare lesen/anlegen/resolve
|
||||
- Notifications und Tasks in Grundzuegen
|
||||
- Szenario-Simulation read-only
|
||||
- Navigation in die UI
|
||||
|
||||
### Teilweise abgedeckt
|
||||
|
||||
- Timeline: nur indirekt ueber Navigation und Allokations-Basisabfragen
|
||||
- Estimates: nur Suche, Detail und Anlegen, aber kein voller Lifecycle
|
||||
- Reports: `run_report` ist flexibel, deckt aber nicht die spezialisierten Report-/Analyse-Readmodels ab
|
||||
- Audit/History: nur einfache History-Abfragen, keine volle Audit-API
|
||||
- Notification/Tasking: Kernfaelle vorhanden, aber keine volle Reminder-/Task-/Notification-Paritaet
|
||||
- Country-/Location-Stammdaten: nur lesend und auch dort nur flach
|
||||
- Insights: Summary-Ebene vorhanden, Drilldowns fehlen
|
||||
|
||||
### Vollstaendig fehlend oder fachlich nicht ausreichend
|
||||
|
||||
- Holiday-Calendar-Admin und Editor-Funktionen
|
||||
- Computation Graph fuer vollstaendige Herleitungen
|
||||
- Chargeability Report Readmodel
|
||||
- Webhook-Administration
|
||||
- System Settings / AI / SMTP / Image-Provider Administration
|
||||
- System Role Config Administration
|
||||
- Import/Export-Flows
|
||||
- User Self-Service und Preferences
|
||||
- Country- und Metro-City-Administration
|
||||
- Volle Timeline-Readmodels und Timeline-Mutationen
|
||||
- Voller Estimate-Lifecycle
|
||||
- Dispo-/Import-spezifische Flows
|
||||
|
||||
## Kritische Inkonsistenzen und Risiken
|
||||
|
||||
### P0: Human-in-the-Loop nur im Prompt, nicht serverseitig erzwungen
|
||||
|
||||
Der System Prompt fordert bestaetigte Freigabe vor jeder schreibenden Aktion. Technisch wird das aber nicht serverseitig erzwungen. Wenn das Modell direkt ein Mutation-Tool aufruft, wird es ausgefuehrt.
|
||||
|
||||
Betroffene Stellen:
|
||||
|
||||
- `packages/api/src/router/assistant.ts`
|
||||
- `packages/api/src/router/assistant-tools.ts`
|
||||
|
||||
Konsequenz:
|
||||
|
||||
- Die wichtigste Governance-Regel ist aktuell nur Prompt-Disziplin, keine technische Policy.
|
||||
|
||||
### P0: Notification-Scoping im Assistant ist fachlich/sicherheitsseitig falsch
|
||||
|
||||
Die dedizierte `notificationRouter` scoped strikt auf den aktuellen Nutzer. Die Assistant-Tools tun das in `list_notifications` und `mark_notification_read` nicht.
|
||||
|
||||
Assistant-Verhalten:
|
||||
|
||||
- `list_notifications` listet Notifications ohne `userId`-Filter.
|
||||
- `mark_notification_read` markiert per ID ohne Ownership-Check.
|
||||
|
||||
Konsequenz:
|
||||
|
||||
- Der Assistant kann Informationen sehen oder veraendern, die der Nutzer in der normalen Notification-UI nicht sehen duerfte.
|
||||
|
||||
### P0: `list_users` ist als admin-only beschrieben, aber nicht effektiv admin-only
|
||||
|
||||
Der Tool-Text sagt "Requires admin permission", aber es gibt weder einen Eintrag in `TOOL_PERMISSION_MAP` noch einen `assertPermission(...)` im Executor.
|
||||
|
||||
Konsequenz:
|
||||
|
||||
- Jeder Nutzer mit Assistant-Zugriff kann potenziell die User-Liste lesen, obwohl die normale App dies ueber `userRouter.list` nur Admins gibt.
|
||||
|
||||
### P1: Permission-Beschreibungen und technische Guards sind nicht konsistent
|
||||
|
||||
Beispiele:
|
||||
|
||||
- `create_estimate`
|
||||
- Beschreibung: "Requires manageEstimates permission"
|
||||
- Technik: `TOOL_PERMISSION_MAP` und Executor verlangen `manageProjects`
|
||||
|
||||
- `create_org_unit` / `update_org_unit`
|
||||
- Beschreibung: "Requires admin permission"
|
||||
- Technik: `manageResources`
|
||||
|
||||
- `send_broadcast`
|
||||
- Beschreibung: "Requires manager permission"
|
||||
- Technik: `manageProjects`
|
||||
|
||||
Konsequenz:
|
||||
|
||||
- Der Assistant ist fuer Nutzer und fuer uns selbst schwer vorhersehbar.
|
||||
- Ein sauberer Rechteabgleich "User kann X in UI, also Assistant auch" ist dadurch nicht belastbar.
|
||||
|
||||
### P1: Nicht alle Assistant-Mutationen sind als Mutation-Typ sauber nachverfolgbar
|
||||
|
||||
`MUTATION_TOOLS` dient dem Logging von AI-Mutationen. Nicht jede schreibende Aktion ist dort gleich gut abgebildet.
|
||||
|
||||
Beispiel:
|
||||
|
||||
- `mark_notification_read` aendert Daten, ist aber nicht in `MUTATION_TOOLS`.
|
||||
|
||||
Konsequenz:
|
||||
|
||||
- Luecken im AI-spezifischen Audit-Trail.
|
||||
|
||||
## Was der Assistant heute noch nicht "weiss"
|
||||
|
||||
Die folgende Liste meint: Informationen, die in App/API bereits existieren oder fuer Nutzer sichtbar sind, aber im Assistant heute gar nicht oder nicht in gleichwertiger Tiefe/Struktur verfuegbar sind.
|
||||
|
||||
### Feiertage und Kalender
|
||||
|
||||
- Vollstaendige Holiday-Calendar-Stammdaten:
|
||||
- Kalender-Liste mit Scope, Prioritaet, Aktiv-Status, Entry-Count
|
||||
- einzelne Kalender inklusive aller Entries
|
||||
- Preview der aufgeloesten Feiertage fuer geplante Kalenderaenderungen
|
||||
- Editierkontext des Holiday-Editors:
|
||||
- was global, state-spezifisch oder city-spezifisch konfiguriert ist
|
||||
- welche Kalender sich gegenseitig ueberschreiben oder ergaenzen
|
||||
|
||||
Aktuell im Assistant vorhanden:
|
||||
|
||||
- aufgeloeste Feiertage nach Region oder Ressource
|
||||
|
||||
Fehlend:
|
||||
|
||||
- die eigentlichen Kalenderobjekte und deren Pflegekontext
|
||||
|
||||
### Timeline und Disposition
|
||||
|
||||
- Vollstaendiges Timeline-Readmodel:
|
||||
- `getEntriesView`
|
||||
- Projekt-/Demand-/Assignment-Kontext in derselben Struktur wie die UI
|
||||
- Holiday-Overlays der Timeline
|
||||
- Projektkontext fuer Drag/Shift/Panel-Interaktionen
|
||||
- Timeline-spezifische Vorschau-/Validierungsdaten:
|
||||
- `previewShift`
|
||||
- genaue Konflikte, Kosten-Delta, Auswirkungen vor Commit
|
||||
- Batch- und Inline-Operationen der Timeline:
|
||||
- `updateAllocationInline`
|
||||
- `quickAssign`
|
||||
- `batchQuickAssign`
|
||||
- `batchShiftAllocations`
|
||||
- `applyShift`
|
||||
- Dispo-spezifische Import-/Workbook-Flows
|
||||
|
||||
Konsequenz:
|
||||
|
||||
- Der Assistant kann heute nicht denselben Timeline-Arbeitsmodus wie ein Nutzer in der UI abbilden.
|
||||
|
||||
### Transparenz, Herleitungen und Berechnungsgraphen
|
||||
|
||||
- Vollstaendige Computation-Graph-Daten fuer Resource- und Project-Views:
|
||||
- Herleitungsfaktoren
|
||||
- Formeln
|
||||
- Holiday-/State-/City-Kontext pro Berechnung
|
||||
- Node/Link-Zusammenhaenge
|
||||
- Spezialisierter Chargeability Report:
|
||||
- Monatsreihen
|
||||
- Org-Unit-, Management-Level- und Country-Filter
|
||||
- Gruppenaggregate und Luecken zum Target
|
||||
|
||||
Konsequenz:
|
||||
|
||||
- Der Assistant kann zwar Teilantworten zu Chargeability/Budget geben, aber noch nicht dieselbe Erklaerungstiefe wie die spezialisierten Analyseansichten.
|
||||
|
||||
### Audit, Verlauf und Governance
|
||||
|
||||
- Vollstaendige Audit-API:
|
||||
- paginierte Listen
|
||||
- Detailansicht mit voller `changes`-Struktur
|
||||
- Timeline-View
|
||||
- Activity Summary
|
||||
|
||||
Aktuell im Assistant vorhanden:
|
||||
|
||||
- vereinfachte History-Suche (`query_change_history`)
|
||||
- Entity-History (`get_entity_timeline`)
|
||||
|
||||
Fehlend:
|
||||
|
||||
- die vollstaendige Governance-/Revisionstiefe der Audit-Oberflaeche
|
||||
|
||||
### Admin- und Systemkonfiguration
|
||||
|
||||
- System Settings:
|
||||
- AI-Provider-Konfiguration
|
||||
- SMTP-Konfiguration
|
||||
- Image-Provider-Konfiguration
|
||||
- Score Weights / Sichtbarkeiten
|
||||
- Vacation Defaults
|
||||
- Timeline Undo Settings
|
||||
- Connection Tests
|
||||
- System Role Config:
|
||||
- Rollenlabels
|
||||
- Beschreibungen
|
||||
- Farben
|
||||
- Default-Permissions
|
||||
- Webhooks:
|
||||
- Liste, Detail, Create, Update, Delete, Test
|
||||
|
||||
Konsequenz:
|
||||
|
||||
- Ein Admin kann in der UI deutlich mehr Systemsteuerung als der Assistant.
|
||||
|
||||
### User Self-Service
|
||||
|
||||
- `user.me`
|
||||
- Dashboard-Layout lesen/speichern
|
||||
- Favorite Projects lesen/toggeln
|
||||
- Column Preferences lesen/speichern
|
||||
- MFA / TOTP aktivieren, pruefen, Status lesen
|
||||
- einige Nutzerverwaltungsaktionen aus `userRouter`
|
||||
|
||||
Konsequenz:
|
||||
|
||||
- Der Assistant kennt den Nutzerkontext nur oberflaechlich, aber nicht dessen persoenliche Einstellungen und Self-Service-Moeglichkeiten.
|
||||
|
||||
### Stammdaten fuer Laender und Orte
|
||||
|
||||
- Country-Details inklusive `scheduleRules`
|
||||
- Metro-City-Verwaltung
|
||||
- Country-/City-CRUD
|
||||
|
||||
Aktuell im Assistant vorhanden:
|
||||
|
||||
- `list_countries` mit relativ flachem Output
|
||||
|
||||
Fehlend:
|
||||
|
||||
- volle fachliche Pflege und die tieferen Standortregeln, die fuer Feiertage, SAH und Forecasts relevant sind
|
||||
|
||||
### Estimate-Lifecycle und Fachobjekte unterhalb des Estimates
|
||||
|
||||
- volle Estimate-Listen-/Detail-Paritaet
|
||||
- Versionen, Scope Items, Demand Lines, Locking, Freigaben, weiterfuehrende Mutationen
|
||||
|
||||
Aktuell im Assistant vorhanden:
|
||||
|
||||
- Suche
|
||||
- Baseline-Detail
|
||||
- Anlegen
|
||||
|
||||
Fehlend:
|
||||
|
||||
- der eigentliche Arbeitsprozess auf Estimate-Ebene
|
||||
|
||||
### Notifications, Tasks und Reminder
|
||||
|
||||
Vorhanden:
|
||||
|
||||
- Listen, Task-Detail, Statuswechsel, Reminder anlegen, Task fuer User anlegen, Broadcast senden
|
||||
|
||||
Fehlend:
|
||||
|
||||
- Reminder-Liste
|
||||
- Reminder-Update/Delete
|
||||
- Unread Count
|
||||
- Task Counts
|
||||
- generische Notification-Erstellung mit derselben Tiefe wie `notificationRouter`
|
||||
|
||||
## Capability Gaps nach Router
|
||||
|
||||
### Komplett fehlende Router-Paritaet
|
||||
|
||||
- `holidayCalendar`
|
||||
- `importExport`
|
||||
- `chargeabilityReport`
|
||||
- `computationGraph`
|
||||
- `settings`
|
||||
- `systemRoleConfig`
|
||||
- `webhook`
|
||||
- `dispo`
|
||||
|
||||
### Deutlich unvollstaendige Router-Paritaet
|
||||
|
||||
- `timeline`
|
||||
- `vacation`
|
||||
- `estimate`
|
||||
- `notification`
|
||||
- `user`
|
||||
- `country`
|
||||
- `auditLog`
|
||||
- `insights`
|
||||
- `scenario`
|
||||
- `resource`
|
||||
- `project`
|
||||
- `comment`
|
||||
|
||||
### Nahe an brauchbarer Grundabdeckung, aber nicht vollstaendig
|
||||
|
||||
- `resource`
|
||||
- `project`
|
||||
- `staffing`
|
||||
- `report`
|
||||
- `dashboard`
|
||||
|
||||
## System Prompt: offensichtliche Uebertreibungen / Irrefuehrungen
|
||||
|
||||
Der Prompt suggeriert an mehreren Stellen mehr Paritaet, als technisch heute vorhanden ist.
|
||||
|
||||
### Problematische Aussagen
|
||||
|
||||
- "Urlaub, Feiertage" ist fuer Leseabfragen ok, aber nicht fuer Holiday-Calendar-Administration.
|
||||
- "Notifications anzeigen" stimmt nur eingeschraenkt, weil das Assistant-Tooling aktuell nicht sauber auf den aktuellen Nutzer scoped.
|
||||
- "Dashboard-Details abrufen" stimmt nur fuer einen Teil der Dashboard-/Analysewelt.
|
||||
- "Den User zu relevanten Seiten navigieren" stimmt, ersetzt aber keine echte Daten-/Aktionsparitaet in Timeline, Holiday Editor oder Admin-Bereichen.
|
||||
- "Ressourcenplanung und Projektmanagement" klingt umfassender, als die reale Tool-Abdeckung in spezialisierten Subsystemen ist.
|
||||
|
||||
### Wichtigste Prompt-Luecke
|
||||
|
||||
Die Human-in-the-Loop-Regel wird als harte Pflicht formuliert, ist technisch aber nicht hart erzwungen.
|
||||
|
||||
## Was getan werden muss, damit der Assistant wirklich dieselben Nutzerfaehigkeiten hat
|
||||
|
||||
### P0: Sicherheits- und Governance-Hardening
|
||||
|
||||
1. Serverseitige Confirm-Policy fuer alle schreibenden Assistant-Tools einziehen.
|
||||
- Schreibende Tool-Calls duerfen ohne bestaetigten Confirmation-Token nicht ausgefuehrt werden.
|
||||
- Diese Policy darf nicht nur im Prompt stehen.
|
||||
|
||||
2. Assistant-Tools auf denselben Objekt-Scope wie die eigentlichen Router bringen.
|
||||
- Besonders:
|
||||
- Notifications
|
||||
- Tasks
|
||||
- User-Liste
|
||||
- alle personenbezogenen oder sensitiven Admin-Daten
|
||||
|
||||
3. Permission-Quellen vereinheitlichen.
|
||||
- Ein zentraler Capability-/Policy-Registry sollte festlegen:
|
||||
- welches Tool welche Permission braucht,
|
||||
- ob Objekt-Ownership gilt,
|
||||
- ob `viewCosts` zusaetzlich erforderlich ist,
|
||||
- ob Human Confirmation erforderlich ist.
|
||||
|
||||
### P1: Fachliche Paritaet fuer die wichtigsten Nutzer-Workflows
|
||||
|
||||
1. Holiday-Calendar-Assistant-Strang bauen
|
||||
- `list_holiday_calendars`
|
||||
- `get_holiday_calendar`
|
||||
- `create_holiday_calendar`
|
||||
- `update_holiday_calendar`
|
||||
- `delete_holiday_calendar`
|
||||
- `create_holiday_entry`
|
||||
- `update_holiday_entry`
|
||||
- `delete_holiday_entry`
|
||||
- `preview_resolved_holidays`
|
||||
|
||||
2. Timeline-Assistant-Strang bauen
|
||||
- Read:
|
||||
- `get_timeline_entries_view`
|
||||
- `get_timeline_holiday_overlays`
|
||||
- `get_timeline_project_context`
|
||||
- `preview_project_shift`
|
||||
- Write:
|
||||
- `update_allocation_inline`
|
||||
- `apply_project_shift`
|
||||
- `quick_assign`
|
||||
- `batch_quick_assign`
|
||||
- `batch_shift_allocations`
|
||||
|
||||
3. Analyse-/Transparenz-Paritaet bauen
|
||||
- `get_chargeability_report`
|
||||
- `get_resource_computation_graph`
|
||||
- `get_project_computation_graph`
|
||||
|
||||
### P2: Admin- und Stammdaten-Paritaet
|
||||
|
||||
1. Settings-Admin-Tools
|
||||
- lesen
|
||||
- aktualisieren
|
||||
- Connection Tests
|
||||
|
||||
2. SystemRoleConfig-Tools
|
||||
- listen
|
||||
- update
|
||||
|
||||
3. Country-/City-Tools
|
||||
- Country-Detail
|
||||
- Country-Create/Update
|
||||
- City-Create/Update/Delete
|
||||
|
||||
4. Webhook-Tools
|
||||
- list/get/create/update/delete/test
|
||||
|
||||
### P3: Self-Service- und Workflow-Paritaet
|
||||
|
||||
1. User-Tools
|
||||
- `get_me`
|
||||
- Dashboard-Layout lesen/schreiben
|
||||
- Favoriten lesen/toggeln
|
||||
- Column Preferences lesen/schreiben
|
||||
- MFA-Status / TOTP-Flows
|
||||
|
||||
2. Notification-/Reminder-Paritaet
|
||||
- Reminder listen/update/delete
|
||||
- unreadCount
|
||||
- taskCounts
|
||||
|
||||
3. Estimate-Lifecycle vertiefen
|
||||
- Versionen
|
||||
- Scope Items
|
||||
- Demand Lines
|
||||
- Status-/Locking-/Approval-Flows
|
||||
|
||||
## Empfohlene Umsetzungsreihenfolge
|
||||
|
||||
### Stream A: Safety / Policy
|
||||
|
||||
- serverseitige Confirmation-Gates
|
||||
- Ownership-/Permission-Fixes
|
||||
- Mutation-Audit vervollstaendigen
|
||||
|
||||
### Stream B: Holiday + Timeline Parity
|
||||
|
||||
- Holiday-Calendar-Editor-Tools
|
||||
- Timeline-Readmodels
|
||||
- Timeline-Shift-/Assign-Aktionen
|
||||
|
||||
### Stream C: Explainability / Analytics
|
||||
|
||||
- Chargeability Report
|
||||
- Computation Graph
|
||||
- Audit Summary
|
||||
|
||||
### Stream D: Admin / Ops
|
||||
|
||||
- Settings
|
||||
- System Role Config
|
||||
- Webhooks
|
||||
- Import/Export
|
||||
|
||||
## Kurzfazit
|
||||
|
||||
Der Assistant ist bereits breit genug, um viele operative Fragen und Standardaktionen abzudecken. Er ist aber noch nicht in dem Zustand, dass man sagen kann: "Alles, was der Nutzer kann, kann auch der Assistant."
|
||||
|
||||
Die drei groessten Blocker dafuer sind:
|
||||
|
||||
1. fehlende serverseitige Absicherung fuer schreibende AI-Aktionen,
|
||||
2. unvollstaendige fachliche Paritaet in Holiday/Timeline/Analytics/Admin-Bereichen,
|
||||
3. inkonsistente oder zu schwache Permission- und Ownership-Pruefungen in einzelnen Tools.
|
||||
@@ -0,0 +1,393 @@
|
||||
# Holiday Calendar Implementation Plan
|
||||
|
||||
## Ziel
|
||||
|
||||
Planarchy soll standortabhaengige Feiertage fachlich korrekt berechnen koennen, sodass zwei Personen im selben Land, aber in unterschiedlichen Regionen oder Staedten, unterschiedliche `SAH` und damit unterschiedliche Chargeability erhalten koennen.
|
||||
|
||||
Die Feiertagsaufloesung soll kuenftig diese Prioritaet haben:
|
||||
|
||||
1. `metroCity`
|
||||
2. `federalState` / Region
|
||||
3. `country`
|
||||
|
||||
Manuelle, ressourcenspezifische `PUBLIC_HOLIDAY`-Eintraege bleiben weiterhin moeglich und ueberschreiben bzw. ergaenzen den Kalender.
|
||||
|
||||
## Ist-Zustand
|
||||
|
||||
Aktuell existieren drei getrennte Mechanismen:
|
||||
|
||||
1. Statisch codierte Feiertage in `packages/shared/src/constants/publicHolidays.ts`
|
||||
2. Batch-/Auto-Import von `PUBLIC_HOLIDAY`-Vacations
|
||||
3. Laufzeitberechnung von `SAH` bzw. Chargeability aus Land/Bundesland
|
||||
|
||||
Die zentralen Luecken:
|
||||
|
||||
- Es gibt kein Holiday-Stammdatenmodell in der Datenbank.
|
||||
- Es gibt keinen Editor fuer Feiertagskalender.
|
||||
- `metroCity` wird fuer Feiertage nicht ausgewertet.
|
||||
- Die aktuelle Logik ist faktisch auf Deutschland plus `federalState` zugeschnitten.
|
||||
- Feiertagswissen ist doppelt vorhanden: statische Kalenderlogik plus importierte `Vacation`-Datensaetze.
|
||||
|
||||
## Zielarchitektur
|
||||
|
||||
### 1. Holiday Calendar als Stammdatenmodell
|
||||
|
||||
Neue Stammdatenobjekte:
|
||||
|
||||
- `HolidayCalendar`
|
||||
- `HolidayCalendarEntry`
|
||||
|
||||
`HolidayCalendar` beschreibt den Gueltigkeitsbereich eines Kalenders:
|
||||
|
||||
- `scopeType`: `COUNTRY | STATE | CITY`
|
||||
- `countryId`
|
||||
- optional `stateCode`
|
||||
- optional `metroCityId`
|
||||
- `name`
|
||||
- `isActive`
|
||||
- optional `priority`
|
||||
|
||||
`HolidayCalendarEntry` beschreibt den einzelnen Feiertag:
|
||||
|
||||
- `holidayCalendarId`
|
||||
- `date`
|
||||
- `name`
|
||||
- optional `isRecurringAnnual`
|
||||
- optional `source`
|
||||
|
||||
Fachregel:
|
||||
|
||||
- Pro Scope soll es genau einen aktiven Kalender geben.
|
||||
- Die effektiven Feiertage eines Mitarbeiters ergeben sich aus Merge mit Prioritaet `country < state < city`.
|
||||
- Gleiche Daten auf engerem Scope ueberschreiben denselben Tag vom breiteren Scope.
|
||||
|
||||
### 2. Laufzeit-Resolver statt statischer Sonderlogik
|
||||
|
||||
Neue gemeinsame Backend-Komponente:
|
||||
|
||||
- `resolveResourceHolidayCalendar(...)`
|
||||
|
||||
Aufgaben:
|
||||
|
||||
- liest Kalenderdaten fuer `countryId`, `federalState`, `metroCityId`
|
||||
- ermittelt die effektiven Feiertage fuer einen Zeitraum
|
||||
- merged diese mit expliziten `Vacation`-Eintraegen vom Typ `PUBLIC_HOLIDAY`
|
||||
- liefert:
|
||||
- `publicHolidayStrings`
|
||||
- `absenceDays`
|
||||
- optional Debug-Metadaten zur Herkunft eines Feiertags
|
||||
|
||||
Diese Komponente wird die einzige Quelle fuer Feiertagslogik in:
|
||||
|
||||
- Chargeability Report
|
||||
- Chargeability Alerts
|
||||
- Computation Graph
|
||||
- ggf. weitere SAH-/Allocation-Pfade
|
||||
|
||||
### 3. Import und Editor werden auf Stammdaten umgestellt
|
||||
|
||||
Der heutige Batch-/Auto-Import darf nicht die Primarlogik fuer Feiertage bleiben.
|
||||
|
||||
Zielbild:
|
||||
|
||||
- Stammdatenkalender sind die Quelle der Wahrheit.
|
||||
- Optionaler Import in `Vacation` bleibt nur als Kompatibilitaets- oder Exportfunktion.
|
||||
- Bestehende `PUBLIC_HOLIDAY`-Vacations werden fuer Uebergangszeit weiter beruecksichtigt.
|
||||
|
||||
## Datenmodell
|
||||
|
||||
### Prisma-Erweiterungen
|
||||
|
||||
Neue Modelle:
|
||||
|
||||
```prisma
|
||||
model HolidayCalendar {
|
||||
id String @id @default(cuid())
|
||||
name String
|
||||
scopeType HolidayCalendarScope
|
||||
countryId String
|
||||
stateCode String?
|
||||
metroCityId String?
|
||||
isActive Boolean @default(true)
|
||||
priority Int @default(0)
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
country Country @relation(fields: [countryId], references: [id])
|
||||
metroCity MetroCity? @relation(fields: [metroCityId], references: [id])
|
||||
entries HolidayCalendarEntry[]
|
||||
|
||||
@@index([countryId, scopeType])
|
||||
@@index([metroCityId])
|
||||
}
|
||||
|
||||
model HolidayCalendarEntry {
|
||||
id String @id @default(cuid())
|
||||
holidayCalendarId String
|
||||
date DateTime @db.Date
|
||||
name String
|
||||
isRecurringAnnual Boolean @default(false)
|
||||
source String?
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
holidayCalendar HolidayCalendar @relation(fields: [holidayCalendarId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@unique([holidayCalendarId, date, name])
|
||||
@@index([date])
|
||||
}
|
||||
```
|
||||
|
||||
Neues Enum:
|
||||
|
||||
```prisma
|
||||
enum HolidayCalendarScope {
|
||||
COUNTRY
|
||||
STATE
|
||||
CITY
|
||||
}
|
||||
```
|
||||
|
||||
### Integritaetsregeln
|
||||
|
||||
- `STATE` verlangt `stateCode`.
|
||||
- `CITY` verlangt `metroCityId`.
|
||||
- `CITY` und `STATE` muessen zum selben `countryId` passen.
|
||||
- Ein `CITY`-Kalender darf nur fuer eine `MetroCity` des angegebenen Landes existieren.
|
||||
|
||||
Diese Regeln werden teils im Schema, teils in Router-Validierung erzwungen.
|
||||
|
||||
## API- und Backend-Pakete
|
||||
|
||||
### Paket A: Schema und Datenzugriff
|
||||
|
||||
Dateien:
|
||||
|
||||
- `packages/db/prisma/schema.prisma`
|
||||
- neue Migration
|
||||
- ggf. `packages/shared/src/types/*`
|
||||
- ggf. `packages/shared/src/schemas/*`
|
||||
|
||||
Ergebnis:
|
||||
|
||||
- Holiday-Calendar-Datenmodell ist vorhanden
|
||||
- Zod-/Shared-Typen fuer CRUD sind definiert
|
||||
|
||||
### Paket B: Holiday Calendar Router
|
||||
|
||||
Neue oder erweiterte API:
|
||||
|
||||
- `packages/api/src/router/holiday-calendar.ts`
|
||||
- Router in `packages/api/src/index.ts`
|
||||
|
||||
Operationen:
|
||||
|
||||
- `listCalendars`
|
||||
- `getCalendarById`
|
||||
- `createCalendar`
|
||||
- `updateCalendar`
|
||||
- `deleteCalendar`
|
||||
- `createEntry`
|
||||
- `updateEntry`
|
||||
- `deleteEntry`
|
||||
- optional `previewResolvedHolidays`
|
||||
|
||||
### Paket C: Gemeinsamer Resolver
|
||||
|
||||
Dateien:
|
||||
|
||||
- `packages/api/src/lib/holiday-resolver.ts`
|
||||
- bestehende Hilfen in `packages/api/src/lib/holiday-availability.ts` refactoren oder ersetzen
|
||||
|
||||
Ergebnis:
|
||||
|
||||
- einheitliche Feiertagsaufloesung fuer alle Backend-Pfade
|
||||
- keine neue statische Sonderlogik in Routern
|
||||
|
||||
### Paket D: Integration in Berechnungen
|
||||
|
||||
Betroffene Stellen:
|
||||
|
||||
- `packages/api/src/router/chargeability-report.ts`
|
||||
- `packages/api/src/lib/chargeability-alerts.ts`
|
||||
- `packages/api/src/router/computation-graph.ts`
|
||||
- weitere `calculateSAH`-Aufrufer mit Feiertagsbezug
|
||||
|
||||
Abnahme:
|
||||
|
||||
- dieselbe Ressource liefert je nach `metroCity` / `federalState` unterschiedliche `SAH`
|
||||
- gleiche Eingaben erzeugen in allen Reports denselben Feiertagseffekt
|
||||
|
||||
### Paket E: UI / Admin
|
||||
|
||||
Betroffene Stellen:
|
||||
|
||||
- neue Admin-Seite oder Erweiterung im Country-Admin
|
||||
- Wiederverwendung moeglicher Muster aus:
|
||||
- `apps/web/src/components/admin/CountriesClient.tsx`
|
||||
- `apps/web/src/components/vacations/PublicHolidayBatch.tsx`
|
||||
- vorhandene Modal-/Table-Komponenten
|
||||
|
||||
Ziel:
|
||||
|
||||
- Kalender pro Land / Bundesland / Stadt anlegen und bearbeiten
|
||||
- Eintraege pro Jahr pflegen
|
||||
- Aufloesung fuer eine Beispiel-Ressource optional vorschauen
|
||||
|
||||
### Paket F: Kompatibilitaet / Migration
|
||||
|
||||
Uebergangsstrategie:
|
||||
|
||||
1. Bestehende `PUBLIC_HOLIDAY`-Vacations bleiben gueltig.
|
||||
2. Neuer Resolver nutzt zuerst Stammdatenkalender plus manuelle Overrides.
|
||||
3. Batch-/Auto-Import wird als Legacy-Funktion markiert.
|
||||
4. Spaeter kann entschieden werden, ob Import nur noch Materialisierung fuer Sonderfaelle ist.
|
||||
|
||||
## Fachliche Aufloesungsregeln
|
||||
|
||||
### Prioritaet
|
||||
|
||||
1. Manuelle ressourcenspezifische `PUBLIC_HOLIDAY`-Vacation
|
||||
2. `CITY`-Kalender
|
||||
3. `STATE`-Kalender
|
||||
4. `COUNTRY`-Kalender
|
||||
|
||||
### Merge-Regeln
|
||||
|
||||
- Gleiches Datum mehrfach:
|
||||
- engster Scope gewinnt fuer Anzeige/Quelle
|
||||
- fuer `SAH` zaehlt der Tag genau einmal
|
||||
- Feiertag auf Wochenende:
|
||||
- erscheint im Kalender
|
||||
- reduziert `SAH` nur, wenn der Tag laut Verfuegbarkeit ein Arbeitstag ist
|
||||
- Halbtag-Feiertage:
|
||||
- aktuell nicht erforderlich
|
||||
- nur aufnehmen, wenn fachlich explizit benoetigt
|
||||
|
||||
## Umsetzung in parallelen Workern
|
||||
|
||||
### Worker 1: Schema + Shared Contracts
|
||||
|
||||
Verantwortung:
|
||||
|
||||
- Prisma-Modelle
|
||||
- Migration
|
||||
- Shared Types / Zod Schemas
|
||||
|
||||
Write Scope:
|
||||
|
||||
- `packages/db/prisma/schema.prisma`
|
||||
- `packages/shared/src/types/*`
|
||||
- `packages/shared/src/schemas/*`
|
||||
|
||||
### Worker 2: Backend Router + Validation
|
||||
|
||||
Verantwortung:
|
||||
|
||||
- CRUD-API fuer Holiday Calendars
|
||||
- Validierung von Scope-Regeln
|
||||
- Audit-Logging
|
||||
|
||||
Write Scope:
|
||||
|
||||
- `packages/api/src/router/holiday-calendar.ts`
|
||||
- `packages/api/src/index.ts`
|
||||
- eng verbundene Tests
|
||||
|
||||
### Worker 3: Resolver + Berechnungsintegration
|
||||
|
||||
Verantwortung:
|
||||
|
||||
- gemeinsamer Holiday Resolver
|
||||
- Integration in Report, Alerts, Computation Graph
|
||||
- Entfernung duplizierter Feiertagslogik
|
||||
|
||||
Write Scope:
|
||||
|
||||
- `packages/api/src/lib/holiday-resolver.ts`
|
||||
- `packages/api/src/lib/holiday-availability.ts`
|
||||
- `packages/api/src/router/chargeability-report.ts`
|
||||
- `packages/api/src/lib/chargeability-alerts.ts`
|
||||
- `packages/api/src/router/computation-graph.ts`
|
||||
- eng verbundene Tests
|
||||
|
||||
### Worker 4: Admin UI
|
||||
|
||||
Verantwortung:
|
||||
|
||||
- neue Holiday-Calendar-Admin-Oberflaeche
|
||||
- Calendar-Entry-Editing
|
||||
- optional Preview fuer aufgeloeste Feiertage
|
||||
|
||||
Write Scope:
|
||||
|
||||
- `apps/web/src/components/admin/*`
|
||||
- relevante App-Routen
|
||||
- eng verbundene UI-Tests falls vorhanden
|
||||
|
||||
### Worker 5: Migration / Legacy Behavior / Verify
|
||||
|
||||
Verantwortung:
|
||||
|
||||
- Legacy Import klar einhaengen oder abgrenzen
|
||||
- Verify-/Smoke-Pfade
|
||||
- End-to-End-Pruefung der fachlichen Szenarien
|
||||
|
||||
Write Scope:
|
||||
|
||||
- `packages/api/src/lib/holiday-auto-import.ts`
|
||||
- `packages/api/src/router/vacation.ts`
|
||||
- Verify-Skripte und Tests
|
||||
|
||||
## Teststrategie
|
||||
|
||||
### Unit
|
||||
|
||||
- Resolver merged `country + state + city` korrekt
|
||||
- `CITY` ueberschreibt `STATE`, `STATE` ergaenzt `COUNTRY`
|
||||
- manuelle `PUBLIC_HOLIDAY`-Vacation wird beruecksichtigt
|
||||
- identisches Datum wird nur einmal auf `SAH` angerechnet
|
||||
|
||||
### Integration
|
||||
|
||||
- Chargeability Report: zwei Ressourcen, gleiches Land, unterschiedliche Stadt, unterschiedliche `SAH`
|
||||
- Chargeability Alerts: derselbe Feiertagseffekt wie im Report
|
||||
- Computation Graph: dieselbe Feiertagsanzahl wie Resolver
|
||||
|
||||
### UI
|
||||
|
||||
- Kalender anlegen fuer `COUNTRY`, `STATE`, `CITY`
|
||||
- Eintrag anlegen/aendern/loeschen
|
||||
- Scope-Validierung verhindert ungueltige Kombinationen
|
||||
|
||||
### Datenmigration / Regression
|
||||
|
||||
- bestehende `PUBLIC_HOLIDAY`-Vacations bleiben wirksam
|
||||
- alte Batch-Funktion erzeugt keine Konflikte
|
||||
- Repo-weit:
|
||||
- `pnpm test`
|
||||
- `pnpm typecheck`
|
||||
- relevanter E2E-Smoke fuer Admin-Pfad, falls vorhanden
|
||||
|
||||
## Abnahme-Kriterien
|
||||
|
||||
- Feiertage sind nicht mehr hart an Deutschland/Bundesland im Laufzeitpfad gekoppelt.
|
||||
- `metroCity` kann `SAH` fachlich beeinflussen.
|
||||
- Es gibt eine Admin-faehige Pflege fuer Feiertagskalender.
|
||||
- Report, Alerts und Computation Graph verwenden denselben Resolver.
|
||||
- Bestehende manuelle Feiertagsabwesenheiten bleiben kompatibel.
|
||||
|
||||
## Empfohlene Reihenfolge
|
||||
|
||||
1. Schema + Shared Contracts
|
||||
2. Backend Router
|
||||
3. Resolver + Integration
|
||||
4. UI
|
||||
5. Migration/Legacy und Gesamttests
|
||||
|
||||
## Offene Produktentscheidungen
|
||||
|
||||
- Sollen Feiertage kuenftig nur manuell gepflegt werden oder auch per externem Provider importierbar sein?
|
||||
- Brauchen wir Halbtag-Feiertage?
|
||||
- Reicht `metroCity` als lokaler Scope oder brauchen wir spaeter feinere Geo-Einheiten?
|
||||
- Soll Legacy-Batch-Import langfristig entfernt oder als Materialisierung behalten werden?
|
||||
Reference in New Issue
Block a user