generator client { provider = "prisma-client-js" } datasource db { provider = "postgresql" url = env("DATABASE_URL") shadowDatabaseUrl = env("DATABASE_URL_SHADOW") } model User { id String @id @default(uuid()) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt status String @default("active") email String @unique name String? avatarUrl String? defaultCurrency String? timeZone String? emailVerified Boolean @default(false) otpEmailEnabled Boolean @default(false) otpTotpEnabled Boolean @default(false) otpTotpSecret String? passwordHash String? otpWhatsappEnabled Boolean @default(false) phone String? @unique lastLoginAt DateTime? role String @default("user") suspendedAt DateTime? suspendedReason String? apiKeys ApiKey[] authAccounts AuthAccount[] categories Category[] goals Goal[] payments Payment[] Recurrence Recurrence[] sessions Session[] subscriptions Subscription? transactions Transaction[] wallets Wallet[] webhooks Webhook[] } model AuthAccount { id String @id @default(uuid()) userId String provider String issuer String subject String lastLogin DateTime @default(now()) user User @relation(fields: [userId], references: [id]) @@unique([issuer, subject]) @@index([userId]) } model Session { id String @id @default(uuid()) userId String createdAt DateTime @default(now()) expiresAt DateTime ip String? userAgent String? revokedAt DateTime? user User @relation(fields: [userId], references: [id]) @@index([userId]) } model Wallet { id String @id @default(uuid()) userId String createdAt DateTime @default(now()) updatedAt DateTime @updatedAt kind String name String currency String? unit String? deletedAt DateTime? initialAmount Decimal? @db.Decimal(18, 2) pricePerUnit Decimal? @db.Decimal(18, 2) reservedBalance Decimal @default(0) @db.Decimal(18, 2) goalAllocations GoalAllocation[] transactions Transaction[] user User @relation(fields: [userId], references: [id]) @@index([userId]) } model Category { id String @id @default(uuid()) userId String name String createdAt DateTime @default(now()) updatedAt DateTime @updatedAt user User @relation(fields: [userId], references: [id]) @@unique([userId, name]) @@index([userId]) } model Transaction { id String @id @default(uuid()) userId String walletId String createdAt DateTime @default(now()) date DateTime amount Decimal @db.Decimal(18, 2) direction String category String? memo String? recurrenceId String? user User @relation(fields: [userId], references: [id]) wallet Wallet @relation(fields: [walletId], references: [id]) @@index([userId, walletId, date]) } model Recurrence { id String @id @default(uuid()) userId String rule String nextRunAt DateTime lastRunAt DateTime? idempotency String @unique user User @relation(fields: [userId], references: [id]) @@index([userId]) } model CurrencyRate { id String @id @default(uuid()) base String quote String at DateTime rate Decimal @db.Decimal(18, 6) @@unique([base, quote, at]) @@index([base, quote]) } model Plan { id String @id @default(uuid()) name String slug String @unique description String? price Decimal @db.Decimal(10, 2) currency String @default("IDR") durationType String durationDays Int? trialDays Int @default(7) features Json badge String? badgeColor String? highlightColor String? sortOrder Int @default(0) isActive Boolean @default(true) isVisible Boolean @default(true) isFeatured Boolean @default(false) maxWallets Int? maxGoals Int? maxTeamMembers Int? apiEnabled Boolean @default(false) apiRateLimit Int? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt subscriptions Subscription[] @@index([slug]) @@index([isActive]) @@index([isVisible]) @@index([sortOrder]) } model Subscription { id String @id @default(uuid()) userId String @unique planId String status String startDate DateTime endDate DateTime isTrialUsed Boolean @default(false) trialEndDate DateTime? cancelledAt DateTime? cancellationReason String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt payments Payment[] plan Plan @relation(fields: [planId], references: [id]) user User @relation(fields: [userId], references: [id]) @@index([userId]) @@index([status]) @@index([endDate]) } model Payment { id String @id @default(uuid()) userId String subscriptionId String? invoiceNumber String @unique amount Decimal @db.Decimal(10, 2) currency String @default("IDR") method String tripayReference String? @unique tripayFee Decimal? @db.Decimal(10, 2) totalAmount Decimal @db.Decimal(10, 2) paymentChannel String? paymentUrl String? qrUrl String? status String proofImageUrl String? transferDate DateTime? verifiedBy String? verifiedAt DateTime? rejectionReason String? couponId String? discountAmount Decimal? @db.Decimal(10, 2) notes String? expiresAt DateTime? paidAt DateTime? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt coupon Coupon? @relation(fields: [couponId], references: [id]) subscription Subscription? @relation(fields: [subscriptionId], references: [id]) user User @relation(fields: [userId], references: [id]) @@index([userId]) @@index([status]) @@index([invoiceNumber]) @@index([tripayReference]) } model PaymentMethod { id String @id @default(uuid()) type String provider String accountName String accountNumber String displayName String logoUrl String? instructions String? isActive Boolean @default(true) sortOrder Int @default(0) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@index([isActive]) @@index([sortOrder]) } model Coupon { id String @id @default(uuid()) code String @unique name String description String? discountType String discountValue Decimal @db.Decimal(10, 2) maxDiscount Decimal? @db.Decimal(10, 2) validFrom DateTime validUntil DateTime maxUses Int? usedCount Int @default(0) minPurchase Decimal? @db.Decimal(10, 2) applicablePlans String[] isActive Boolean @default(true) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt payments Payment[] @@index([code]) @@index([isActive]) } model AppConfig { id String @id @default(uuid()) key String @unique value String category String label String description String? type String isSecret Boolean @default(false) updatedAt DateTime @updatedAt updatedBy String? @@index([category]) } model ApiKey { id String @id @default(uuid()) userId String name String keyHash String @unique prefix String scopes String[] lastUsedAt DateTime? expiresAt DateTime? revokedAt DateTime? createdAt DateTime @default(now()) user User @relation(fields: [userId], references: [id]) usage ApiKeyUsage[] @@index([userId]) @@index([keyHash]) } model ApiKeyUsage { id String @id @default(uuid()) apiKeyId String endpoint String method String statusCode Int responseTime Int timestamp DateTime @default(now()) apiKey ApiKey @relation(fields: [apiKeyId], references: [id], onDelete: Cascade) @@index([apiKeyId, timestamp]) } model Webhook { id String @id @default(uuid()) userId String url String events String[] secret String isActive Boolean @default(true) lastTriggeredAt DateTime? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt user User @relation(fields: [userId], references: [id]) deliveries WebhookDelivery[] @@index([userId]) } model WebhookDelivery { id String @id @default(uuid()) webhookId String event String payload Json status String statusCode Int? response String? attempts Int @default(0) nextRetryAt DateTime? deliveredAt DateTime? createdAt DateTime @default(now()) webhook Webhook @relation(fields: [webhookId], references: [id], onDelete: Cascade) @@index([webhookId]) @@index([status]) } model Goal { id String @id @default(uuid()) userId String teamId String? name String description String? targetAmount Decimal @db.Decimal(18, 2) currentAmount Decimal @default(0) @db.Decimal(18, 2) currency String @default("IDR") targetDate DateTime? imageUrl String? category String? status String @default("active") completedAt DateTime? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt user User @relation(fields: [userId], references: [id], onDelete: Cascade) allocations GoalAllocation[] milestones GoalMilestone[] @@index([userId]) @@index([status]) @@index([teamId]) } model GoalAllocation { id String @id @default(uuid()) goalId String walletId String amount Decimal @db.Decimal(18, 2) currency String exchangeRate Decimal? @db.Decimal(18, 6) amountInGoalCurrency Decimal @db.Decimal(18, 2) notes String? createdAt DateTime @default(now()) createdBy String goal Goal @relation(fields: [goalId], references: [id], onDelete: Cascade) wallet Wallet @relation(fields: [walletId], references: [id], onDelete: Cascade) @@index([goalId]) @@index([walletId]) @@index([createdAt]) } model GoalMilestone { id String @id @default(uuid()) goalId String percentage Int targetAmount Decimal @db.Decimal(18, 2) achievedAt DateTime? notifiedAt DateTime? goal Goal @relation(fields: [goalId], references: [id], onDelete: Cascade) @@unique([goalId, percentage]) @@index([goalId]) @@index([achievedAt]) }