This commit is contained in:
@@ -0,0 +1,235 @@
|
||||
// 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?
|
||||
}
|
||||
Reference in New Issue
Block a user