# Plan: Budget per Role / Demand ## Anforderungsanalyse Jede Staffing-Demand (Rolle) in einem Projekt soll ein eigenes Budget bekommen. Aktuell gibt es nur ein einziges `budgetCents` auf Projektebene. Ziel: 1. **DemandRequirement** bekommt ein `budgetCents` Feld (wie viel Budget ist dieser Rolle zugewiesen) 2. **StaffingRequirement** (JSONB auf Project) bekommt ein optionales `budgetCents` Feld fuer den Wizard 3. **Project Wizard Step 3** zeigt Budget-Input pro Rolle + verbleibendes unverteiltes Projekt-Budget 4. **Project Detail Page** zeigt pro Demand: zugewiesenes Budget vs. gebuchtes Budget (aus Assignments berechnet) 5. **Fill Demand Modal** zeigt verbleibendes Rollen-Budget beim Zuweisen von Ressourcen ### Architektur-Entscheidung `budgetCents` als **explizite Spalte** auf `DemandRequirement` (nicht in `metadata` JSONB), weil: - Typsicher, indizierbar, aggregierbar via SQL - Konsistent mit dem Muster auf `Project.budgetCents` - Default `0` = kein Budget zugewiesen (abwaertskompatibel) --- ## Betroffene Pakete & Dateien | Paket | Dateien | Art der Aenderung | |-------|---------|-----------------| | `packages/db` | `prisma/schema.prisma` | **edit** — `budgetCents` auf DemandRequirement | | `packages/shared` | `src/types/project.ts` | **edit** — `budgetCents?` auf StaffingRequirement | | `packages/shared` | `src/types/allocation.ts` | **edit** — `budgetCents` auf DemandRequirementRecord | | `packages/shared` | `src/schemas/allocation.schema.ts` | **edit** — `budgetCents` in CreateDemandRequirementSchema | | `packages/api` | `src/router/allocation.ts` | **edit** — budgetCents durchreichen in create/update | | `packages/api` | `src/router/project.ts` | **edit** — bei Demand-Erstellung aus Wizard budgetCents uebernehmen | | `apps/web` | `src/components/projects/ProjectWizard.tsx` | **edit** — Step 3: Budget-Input pro Rolle + Restbudget-Anzeige | | `apps/web` | `src/components/projects/ProjectDemandsTable.tsx` | **edit** — Spalten: Allocated Budget, Booked Budget | | `apps/web` | `src/components/allocations/FillOpenDemandModal.tsx` | **edit** — Rollen-Budget-Anzeige | --- ## Task-Liste ### Task 1: Prisma Schema — `budgetCents` auf DemandRequirement Datei: `packages/db/prisma/schema.prisma` - [ ] `budgetCents Int @default(0)` auf DemandRequirement hinzufuegen - [ ] `pnpm db:push` ausfuehren (generiert Prisma Client) - [ ] Dev-Server neustarten (`.next/` Cache loeschen) ### Task 2: Shared Types aktualisieren Dateien: - `packages/shared/src/types/project.ts` — `budgetCents?: number` auf `StaffingRequirement` - `packages/shared/src/types/allocation.ts` — `budgetCents: number` auf `DemandRequirementRecord` - `packages/shared/src/schemas/allocation.schema.ts` — `budgetCents: z.number().int().min(0).default(0)` in `CreateDemandRequirementBaseSchema` ### Task 3: API — budgetCents durchreichen Datei: `packages/api/src/router/allocation.ts` - [ ] `createDemandRequirement` — `budgetCents` aus Input an Prisma create weitergeben - [ ] `updateDemandRequirement` — `budgetCents` updatebar machen - [ ] `checkResourceAvailability` — optional: gebuchte Kosten vs. Rollen-Budget zurueckgeben Datei: `packages/api/src/router/project.ts` - [ ] Bei Projekt-Erstellung mit StaffingReqs: wenn `staffingReq.budgetCents` vorhanden, an DemandRequirement weitergeben ### Task 4: Project Wizard Step 3 — Budget-Input pro Rolle Datei: `apps/web/src/components/projects/ProjectWizard.tsx` - [ ] Pro StaffingRequirement-Karte: neues Feld "Role Budget (EUR)" (Input, konvertiert zu Cents) - [ ] Oben im Step: Anzeige "Project Budget: X EUR | Allocated: Y EUR | Remaining: Z EUR" - [ ] Farbkodierung: gruen wenn alles verteilt, amber wenn Rest, rot wenn ueberallokiert - [ ] Budget-Wert wird in `state.staffingReqs[i].budgetCents` gespeichert ### Task 5: Project Detail Page — Budget-Spalten pro Demand Datei: `apps/web/src/components/projects/ProjectDemandsTable.tsx` - [ ] Neue Spalte "Allocated Budget" — zeigt `demand.budgetCents` formatiert als EUR - [ ] Neue Spalte "Booked Budget" — berechnet: Summe der `dailyCostCents * Arbeitstage` aller Assignments dieses Demands - Hinweis: Die Demand-Daten vom Server enthalten `assignments[]` — daraus berechnen - [ ] Neue Spalte "Remaining" — Allocated minus Booked - [ ] Farbkodierung: gruen wenn unter Budget, rot wenn ueber Budget ### Task 6: Fill Demand Modal — Rollen-Budget anzeigen Datei: `apps/web/src/components/allocations/FillOpenDemandModal.tsx` - [ ] Im Demand-Summary oben: "Role Budget: X EUR | Booked: Y EUR | Remaining: Z EUR" - [ ] Beim Hinzufuegen einer Ressource zum Plan: geschaetzte Kosten anzeigen (LCR * verfuegbare Stunden) - [ ] Warnung wenn geplante Kosten das Rollen-Budget ueberschreiten --- ## Abhaengigkeiten - **Task 1 → Task 2 → Task 3** (sequentiell: Schema → Types → API) - **Task 4** benoetigt Task 2 (StaffingRequirement-Typ mit budgetCents) - **Task 5** benoetigt Task 1+3 (budgetCents auf DemandRequirement + API liefert es) - **Task 6** benoetigt Task 5 (gleiche Berechnung) - Task 4 und Task 5 koennen **parallel** nach Task 3 --- ## Akzeptanzkriterien - [ ] `pnpm db:push` laeuft ohne Fehler - [ ] `pnpm test:unit` — alle Tests gruen - [ ] `pnpm --filter @planarchy/web exec tsc --noEmit` — keine neuen Errors - [ ] Project Wizard Step 3: Budget-Input pro Rolle sichtbar, Restbudget wird live berechnet - [ ] Project Detail `/projects/[id]`: Demands-Tabelle zeigt Allocated / Booked / Remaining Budget - [ ] Fill Demand Modal: Rollen-Budget und geschaetzte Kosten sichtbar - [ ] Bestehende Projekte/Demands funktionieren weiterhin (budgetCents default 0) --- ## Risiken & offene Fragen 1. **Abwaertskompatibilitaet:** `@default(0)` stellt sicher, dass bestehende Demands kein Budget haben (0 = nicht gesetzt). UI sollte "Not set" anzeigen wenn 0. 2. **Budget-Berechnung Booked:** `dailyCostCents` ist pro Tag. Gebuchte Kosten = `dailyCostCents * Anzahl Arbeitstage im Zeitraum`. Diese Berechnung existiert bereits im Engine-Paket (`computeBudgetStatus`). 3. **StaffingReqs JSONB:** Die `staffingReqs` auf Project sind JSONB. Aeltere Projekte haben kein `budgetCents` darin — der Wizard muss `budgetCents ?? 0` defaulten. 4. **Budget-Ueberschreitung:** Soll weiterhin erlaubt sein (Warnung, kein Block) — konsistent mit dem bestehenden Ansatz bei Projekt-Budget.