// FPT (Financial Planning Tool) — Datenmodell gemaess FDD/TDD Kapitel 2. // Get a free hosted Postgres database in seconds: `npx create-db` generator client { provider = "prisma-client" output = "../src/generated/prisma" } datasource db { provider = "postgresql" } // Zugriffsschutz der Webapplikation: kein vorkonfiguriertes Passwort -- der // Benutzer legt es beim allerersten Login selbst fest (genau eine Zeile). model AppCredential { id String @id @default(cuid()) passwordHash String createdAt DateTime @default(now()) } enum HouseholdType { SINGLE COUPLE } enum PersonRole { PERSON_A PERSON_B } enum IncomeMode { PER_PERSON HOUSEHOLD } enum OwnerTag { PERSON_A PERSON_B HOUSEHOLD } enum OneTimeEventType { INCOME EXPENSE } enum TransitionDecision { CARRY_OVER SELL } enum PositionType { SECURITY REAL_ESTATE } // Ein Haushalt (1 oder 2 Personen) - Wurzel-Objekt model Household { id String @id @default(cuid()) householdType HouseholdType inflationRateDefault Float createdAt DateTime @default(now()) updatedAt DateTime @updatedAt persons Person[] plans Plan[] } // Einzelperson im Haushalt (Alter, geplantes Pensionsalter) model Person { id String @id @default(cuid()) householdId String household Household @relation(fields: [householdId], references: [id], onDelete: Cascade) role PersonRole age Int retirementAge Int incomeEntries IncomeEntry[] retirementInfos RetirementInfo[] @@unique([householdId, role]) } // Eine vollstaendige Phasenkette; kann Szenario eines anderen Plans sein model Plan { id String @id @default(cuid()) householdId String household Household @relation(fields: [householdId], references: [id], onDelete: Cascade) name String // Szenario-Verzweigung: ein Szenario ist ein eigener Plan mit Verweis auf den Ursprungsplan // und die Phase, ab der die Ketten divergieren (branchFromPhaseId zeigt auf eine Phase // dieses neuen Plans, welche die per Deep-Copy duplizierte letzte gemeinsame Phase ist). parentPlanId String? parentPlan Plan? @relation("PlanScenarios", fields: [parentPlanId], references: [id], onDelete: SetNull) scenarios Plan[] @relation("PlanScenarios") branchFromPhaseId String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt phases Phase[] } // Ein Lebensabschnitt innerhalb eines Plans model Phase { id String @id @default(cuid()) planId String plan Plan @relation(fields: [planId], references: [id], onDelete: Cascade) sequenceNumber Int name String durationYears Int inflationRate Float? incomeMode IncomeMode @default(HOUSEHOLD) incomeEntries IncomeEntry[] expenseEntries ExpenseEntry[] securities Security[] realEstates RealEstate[] oneTimeEvents OneTimeEvent[] retirementInfos RetirementInfo[] // Uebergang IN diese Phase (diese Phase ist Ziel) bzw. AUS dieser Phase (diese Phase ist Quelle) transitionIn PhaseTransition? @relation("TransitionTarget") transitionOut PhaseTransition? @relation("TransitionSource") createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@unique([planId, sequenceNumber]) } // Einkommensposten einer Phase (pro Person oder gemeinsam, je nach Phase.incomeMode) model IncomeEntry { id String @id @default(cuid()) phaseId String phase Phase @relation(fields: [phaseId], references: [id], onDelete: Cascade) personId String? person Person? @relation(fields: [personId], references: [id], onDelete: SetNull) label String? amount Float } // Ausgabenposten einer Phase (immer Haushaltsebene, generischer Gesamtbetrag) model ExpenseEntry { id String @id @default(cuid()) phaseId String phase Phase @relation(fields: [phaseId], references: [id], onDelete: Cascade) label String? amount Float } // Eine Wertschrift innerhalb einer Phase (inkl. jaehrlichem Sparbeitrag) model Security { id String @id @default(cuid()) phaseId String phase Phase @relation(fields: [phaseId], references: [id], onDelete: Cascade) name String startValue Float expectedReturn Float annualContribution Float @default(0) ownerTag OwnerTag @default(HOUSEHOLD) // Steuersatz auf Verkaufsgewinn bei Uebernahme in PhaseTransitionItem (Default 0%, siehe Kap. 9) saleTaxRate Float @default(0) transitionItems PhaseTransitionItem[] } // Eine Immobilie innerhalb einer Phase model RealEstate { id String @id @default(cuid()) phaseId String phase Phase @relation(fields: [phaseId], references: [id], onDelete: Cascade) name String marketValue Float mortgage Float valueGrowth Float amortization Float salePrice Float? // Geschaetzte Grundstueckgewinnsteuer (%), direkt am ausloesenden Ereignis erfasst (Kap. 9) saleTaxRate Float @default(20) transitionItems PhaseTransitionItem[] } // Einmalige Sondereinnahme/-ausgabe model OneTimeEvent { id String @id @default(cuid()) phaseId String phase Phase @relation(fields: [phaseId], references: [id], onDelete: Cascade) type OneTimeEventType amount Float description String? } // Renten-/Kapitalbezugsangaben (nur in Pensionierungsphasen), pro Person model RetirementInfo { id String @id @default(cuid()) phaseId String phase Phase @relation(fields: [phaseId], references: [id], onDelete: Cascade) personId String person Person @relation(fields: [personId], references: [id], onDelete: Cascade) ahvAmount Float @default(0) pkPensionAmount Float @default(0) lumpSumAmount Float @default(0) // Geschaetzte Kapitalbezugssteuer (%), direkt am auslösenden Ereignis erfasst (Kap. 9) lumpSumTaxRate Float @default(8) @@unique([phaseId, personId]) } // Entscheidungen beim Uebergang zweier Phasen (uebernehmen/verkaufen je Position) model PhaseTransition { id String @id @default(cuid()) fromPhaseId String @unique fromPhase Phase @relation("TransitionSource", fields: [fromPhaseId], references: [id], onDelete: Cascade) toPhaseId String @unique toPhase Phase @relation("TransitionTarget", fields: [toPhaseId], references: [id], onDelete: Cascade) items PhaseTransitionItem[] } // Einzelentscheidung fuer eine Position (Wertschrift oder Immobilie) beim Phasenuebergang model PhaseTransitionItem { id String @id @default(cuid()) transitionId String transition PhaseTransition @relation(fields: [transitionId], references: [id], onDelete: Cascade) positionType PositionType securityId String? security Security? @relation(fields: [securityId], references: [id], onDelete: Cascade) realEstateId String? realEstate RealEstate? @relation(fields: [realEstateId], references: [id], onDelete: Cascade) decision TransitionDecision salePrice Float? }