// prisma/schema.prisma generator client { provider = "prisma-client-js" binaryTargets = ["native", "debian-openssl-3.0.x"] } datasource db { provider = "postgresql" url = env("DATABASE_URL") } enum IncomeFrequency { weekly biweekly monthly } enum IncomeType { regular irregular } model User { id String @id @default(uuid()) email String @unique passwordHash String? displayName String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt incomeFrequency IncomeFrequency @default(biweekly) incomeType IncomeType @default(regular) firstIncomeDate DateTime? // Track when user's first income was received for accurate pay period calculation pendingScheduledIncome Boolean @default(false) // Flag set when user dismisses payday overlay, cleared when paycheck is entered timezone String @default("America/New_York") // IANA timezone identifier for accurate date calculations fixedExpensePercentage Int @default(40) // Irregular income: percent of new income to auto-fund fixed expenses totalBudgetCents BigInt? budgetPeriod String @default("monthly") variableCategories VariableCategory[] fixedPlans FixedPlan[] incomes IncomeEvent[] allocations Allocation[] transactions Transaction[] budgetSessions BudgetSession[] } model VariableCategory { id String @id @default(uuid()) userId String user User @relation(fields: [userId], references: [id], onDelete: Cascade) name String percent Int priority Int @default(100) isSavings Boolean @default(false) balanceCents BigInt @default(0) savingsTargetCents BigInt? transactions Transaction[] @relation("TransactionCategory") @@unique([userId, name]) @@index([userId, priority]) } model FixedPlan { id String @id @default(uuid()) userId String user User @relation(fields: [userId], references: [id], onDelete: Cascade) name String cycleStart DateTime dueOn DateTime totalCents BigInt fundedCents BigInt @default(0) currentFundedCents BigInt @default(0) priority Int @default(100) fundingMode String @default("auto-on-deposit") scheduleJson Json? periodDays Int @default(30) frequency String? // "one-time", "weekly", "biweekly", "monthly" autoRollover Boolean @default(true) lastRollover DateTime? lastFundingDate DateTime? lastFundedPayPeriod DateTime? // Track when plan was last funded in a pay period needsFundingThisPeriod Boolean @default(true) // Simple flag to track funding needs // Auto-payment fields autoPayEnabled Boolean @default(false) paymentSchedule Json? // { frequency: "monthly", dayOfMonth: 1, minFundingPercent: 100 } nextPaymentDate DateTime? lastAutoPayment DateTime? maxRetryAttempts Int @default(3) // Overdue tracking fields isOverdue Boolean @default(false) overdueAmount BigInt @default(0) overdueSince DateTime? transactions Transaction[] @relation("TransactionPlan") @@unique([userId, name]) @@index([userId, dueOn]) @@index([userId, priority]) } model IncomeEvent { id String @id @default(uuid()) userId String user User @relation(fields: [userId], references: [id], onDelete: Cascade) postedAt DateTime amountCents BigInt note String? isScheduledIncome Boolean @default(false) // True if this is a regular paycheck (vs bonus/extra income) allocations Allocation[] @@index([userId, postedAt]) } model Allocation { id String @id @default(uuid()) userId String user User @relation(fields: [userId], references: [id], onDelete: Cascade) createdAt DateTime @default(now()) kind String toId String amountCents BigInt incomeId String? income IncomeEvent? @relation(fields: [incomeId], references: [id]) } model Transaction { id String @id @default(uuid()) userId String user User @relation(fields: [userId], references: [id], onDelete: Cascade) occurredAt DateTime kind String categoryId String? category VariableCategory? @relation("TransactionCategory", fields: [categoryId], references: [id], onDelete: SetNull) planId String? plan FixedPlan? @relation("TransactionPlan", fields: [planId], references: [id], onDelete: SetNull) amountCents BigInt note String? receiptUrl String? isAutoPayment Boolean @default(false) isReconciled Boolean @default(false) @@index([userId, occurredAt]) } model BudgetSession { id String @id @default(uuid()) userId String user User @relation(fields: [userId], references: [id], onDelete: Cascade) periodStart DateTime periodEnd DateTime totalBudgetCents BigInt allocatedCents BigInt @default(0) fundedCents BigInt @default(0) availableCents BigInt @default(0) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@unique([userId, periodStart]) @@index([userId, periodStart]) }