| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656 |
- generator client {
- provider = "prisma-client-js"
- }
- datasource db {
- provider = "postgresql"
- url = env("DATABASE_URL")
- }
- model User {
- id String @id @default(cuid())
- name String
- email String @unique
- emailVerified Boolean @default(false)
- image String?
- isSuperAdmin Boolean @default(false)
- createdAt DateTime @default(now())
- updatedAt DateTime @updatedAt
- twoFactorEnabled Boolean @default(false)
- termsAcceptedAt DateTime?
- sessions Session[]
- accounts Account[]
- vehicles Vehicle[]
- customers Customer[]
- settings AppSetting[]
- inventoryParts InventoryPart[]
- quotes Quote[]
- customFieldDefinitions CustomFieldDefinition[]
- twoFactor TwoFactor?
- organizationMembers OrganizationMember[]
- @@map("users")
- }
- model AppSetting {
- id String @id @default(cuid())
- key String
- value String
- userId String
- user User @relation(fields: [userId], references: [id], onDelete: Cascade)
- organizationId String?
- organization Organization? @relation(fields: [organizationId], references: [id], onDelete: Cascade)
- @@unique([organizationId, key])
- @@index([organizationId])
- @@map("app_settings")
- }
- model Session {
- id String @id @default(cuid())
- expiresAt DateTime
- token String @unique
- createdAt DateTime @default(now())
- updatedAt DateTime @updatedAt
- ipAddress String?
- userAgent String?
- userId String
- user User @relation(fields: [userId], references: [id], onDelete: Cascade)
- @@index([userId])
- @@map("sessions")
- }
- model Account {
- id String @id @default(cuid())
- accountId String
- providerId String
- userId String
- user User @relation(fields: [userId], references: [id], onDelete: Cascade)
- accessToken String?
- refreshToken String?
- idToken String?
- accessTokenExpiresAt DateTime?
- refreshTokenExpiresAt DateTime?
- scope String?
- password String?
- createdAt DateTime @default(now())
- updatedAt DateTime @updatedAt
- @@index([userId])
- @@map("accounts")
- }
- model Verification {
- id String @id @default(cuid())
- identifier String
- value String
- expiresAt DateTime
- createdAt DateTime? @default(now())
- updatedAt DateTime? @updatedAt
- @@map("verifications")
- }
- model TwoFactor {
- id String @id @default(cuid())
- secret String
- backupCodes String
- userId String @unique
- user User @relation(fields: [userId], references: [id], onDelete: Cascade)
- @@map("two_factor")
- }
- model Vehicle {
- id String @id @default(cuid())
- make String
- model String
- year Int
- vin String?
- licensePlate String?
- color String?
- mileage Int @default(0)
- fuelType String? @default("gasoline")
- transmission String? @default("automatic")
- engineSize String?
- purchaseDate DateTime?
- purchasePrice Float?
- imageUrl String?
- isArchived Boolean @default(false)
- archiveReason String?
- createdAt DateTime @default(now())
- updatedAt DateTime @updatedAt
- userId String
- user User @relation(fields: [userId], references: [id], onDelete: Cascade)
- organizationId String?
- organization Organization? @relation(fields: [organizationId], references: [id], onDelete: Cascade)
- customerId String?
- customer Customer? @relation(fields: [customerId], references: [id], onDelete: SetNull)
- serviceRecords ServiceRecord[]
- notes Note[]
- fuelLogs FuelLog[]
- reminders Reminder[]
- quotes Quote[]
- recurringInvoices RecurringInvoice[]
- @@index([organizationId])
- @@map("vehicles")
- }
- model ServiceRecord {
- id String @id @default(cuid())
- title String
- description String?
- type String @default("maintenance")
- status String @default("completed")
- cost Float @default(0)
- mileage Int?
- serviceDate DateTime @default(now())
- shopName String?
- techName String?
- parts String?
- laborHours Float?
- diagnosticNotes String?
- invoiceNotes String?
- subtotal Float @default(0)
- taxRate Float @default(0)
- taxAmount Float @default(0)
- totalAmount Float @default(0)
- invoiceNumber String?
- discountType String?
- discountValue Float @default(0)
- discountAmount Float @default(0)
- manuallyPaid Boolean @default(false)
- publicToken String? @unique
- createdAt DateTime @default(now())
- updatedAt DateTime @updatedAt
- vehicleId String
- vehicle Vehicle @relation(fields: [vehicleId], references: [id], onDelete: Cascade)
- partItems ServicePart[]
- laborItems ServiceLabor[]
- attachments ServiceAttachment[]
- payments Payment[]
- @@index([vehicleId])
- @@map("service_records")
- }
- model Note {
- id String @id @default(cuid())
- title String
- content String
- isPinned Boolean @default(false)
- createdAt DateTime @default(now())
- updatedAt DateTime @updatedAt
- vehicleId String
- vehicle Vehicle @relation(fields: [vehicleId], references: [id], onDelete: Cascade)
- @@index([vehicleId])
- @@map("notes")
- }
- model FuelLog {
- id String @id @default(cuid())
- date DateTime @default(now())
- mileage Int
- gallons Float
- pricePerGallon Float
- totalCost Float
- isFillUp Boolean @default(true)
- station String?
- notes String?
- createdAt DateTime @default(now())
- updatedAt DateTime @updatedAt
- vehicleId String
- vehicle Vehicle @relation(fields: [vehicleId], references: [id], onDelete: Cascade)
- @@index([vehicleId])
- @@map("fuel_logs")
- }
- model Reminder {
- id String @id @default(cuid())
- title String
- description String?
- dueDate DateTime?
- dueMileage Int?
- isCompleted Boolean @default(false)
- createdAt DateTime @default(now())
- updatedAt DateTime @updatedAt
- vehicleId String
- vehicle Vehicle @relation(fields: [vehicleId], references: [id], onDelete: Cascade)
- @@index([vehicleId])
- @@map("reminders")
- }
- model Customer {
- id String @id @default(cuid())
- name String
- email String?
- phone String?
- address String?
- company String?
- notes String?
- createdAt DateTime @default(now())
- updatedAt DateTime @updatedAt
- userId String
- user User @relation(fields: [userId], references: [id], onDelete: Cascade)
- organizationId String?
- organization Organization? @relation(fields: [organizationId], references: [id], onDelete: Cascade)
- vehicles Vehicle[]
- quotes Quote[]
- @@index([organizationId])
- @@map("customers")
- }
- model Payment {
- id String @id @default(cuid())
- amount Float
- date DateTime @default(now())
- method String @default("other")
- note String?
- provider String?
- externalId String?
- createdAt DateTime @default(now())
- updatedAt DateTime @updatedAt
- serviceRecordId String
- serviceRecord ServiceRecord @relation(fields: [serviceRecordId], references: [id], onDelete: Cascade)
- @@index([serviceRecordId])
- @@map("payments")
- }
- model ServiceAttachment {
- id String @id @default(cuid())
- fileName String
- fileUrl String
- fileType String
- fileSize Int
- category String @default("diagnostic")
- description String?
- includeInInvoice Boolean @default(true)
- createdAt DateTime @default(now())
- serviceRecordId String
- serviceRecord ServiceRecord @relation(fields: [serviceRecordId], references: [id], onDelete: Cascade)
- @@index([serviceRecordId])
- @@map("service_attachments")
- }
- model ServicePart {
- id String @id @default(cuid())
- partNumber String?
- name String
- quantity Float @default(1)
- unitPrice Float @default(0)
- total Float @default(0)
- serviceRecordId String
- serviceRecord ServiceRecord @relation(fields: [serviceRecordId], references: [id], onDelete: Cascade)
- @@index([serviceRecordId])
- @@map("service_parts")
- }
- model ServiceLabor {
- id String @id @default(cuid())
- description String
- hours Float @default(0)
- rate Float @default(0)
- total Float @default(0)
- serviceRecordId String
- serviceRecord ServiceRecord @relation(fields: [serviceRecordId], references: [id], onDelete: Cascade)
- @@index([serviceRecordId])
- @@map("service_labor")
- }
- model InventoryPart {
- id String @id @default(cuid())
- partNumber String?
- name String
- description String?
- category String?
- quantity Int @default(0)
- minQuantity Int @default(0)
- unitCost Float @default(0)
- supplier String?
- supplierPhone String?
- supplierEmail String?
- supplierUrl String?
- imageUrl String?
- location String?
- isArchived Boolean @default(false)
- createdAt DateTime @default(now())
- updatedAt DateTime @updatedAt
- userId String
- user User @relation(fields: [userId], references: [id], onDelete: Cascade)
- organizationId String?
- organization Organization? @relation(fields: [organizationId], references: [id], onDelete: Cascade)
- @@index([organizationId])
- @@map("inventory_parts")
- }
- model Quote {
- id String @id @default(cuid())
- quoteNumber String?
- title String
- description String?
- status String @default("draft")
- validUntil DateTime?
- subtotal Float @default(0)
- taxRate Float @default(0)
- taxAmount Float @default(0)
- discountType String?
- discountValue Float @default(0)
- discountAmount Float @default(0)
- totalAmount Float @default(0)
- notes String?
- publicToken String?
- convertedToId String?
- createdAt DateTime @default(now())
- updatedAt DateTime @updatedAt
- userId String
- user User @relation(fields: [userId], references: [id], onDelete: Cascade)
- organizationId String?
- organization Organization? @relation(fields: [organizationId], references: [id], onDelete: Cascade)
- customerId String?
- customer Customer? @relation(fields: [customerId], references: [id], onDelete: SetNull)
- vehicleId String?
- vehicle Vehicle? @relation(fields: [vehicleId], references: [id], onDelete: SetNull)
- partItems QuotePart[]
- laborItems QuoteLabor[]
- @@index([organizationId])
- @@map("quotes")
- }
- model QuotePart {
- id String @id @default(cuid())
- partNumber String?
- name String
- quantity Float @default(1)
- unitPrice Float @default(0)
- total Float @default(0)
- quoteId String
- quote Quote @relation(fields: [quoteId], references: [id], onDelete: Cascade)
- @@index([quoteId])
- @@map("quote_parts")
- }
- model QuoteLabor {
- id String @id @default(cuid())
- description String
- hours Float @default(0)
- rate Float @default(0)
- total Float @default(0)
- quoteId String
- quote Quote @relation(fields: [quoteId], references: [id], onDelete: Cascade)
- @@index([quoteId])
- @@map("quote_labor")
- }
- model CustomFieldDefinition {
- id String @id @default(cuid())
- name String
- label String
- fieldType String @default("text")
- options String?
- required Boolean @default(false)
- entityType String
- sortOrder Int @default(0)
- isActive Boolean @default(true)
- createdAt DateTime @default(now())
- updatedAt DateTime @updatedAt
- userId String
- user User @relation(fields: [userId], references: [id], onDelete: Cascade)
- organizationId String?
- organization Organization? @relation(fields: [organizationId], references: [id], onDelete: Cascade)
- values CustomFieldValue[]
- @@unique([organizationId, name, entityType])
- @@index([organizationId])
- @@map("custom_field_definitions")
- }
- model CustomFieldValue {
- id String @id @default(cuid())
- value String
- entityId String
- entityType String
- fieldId String
- field CustomFieldDefinition @relation(fields: [fieldId], references: [id], onDelete: Cascade)
- @@unique([fieldId, entityId])
- @@index([fieldId])
- @@map("custom_field_values")
- }
- model Organization {
- id String @id @default(cuid())
- name String
- createdAt DateTime @default(now())
- updatedAt DateTime @updatedAt
- members OrganizationMember[]
- roles Role[]
- vehicles Vehicle[]
- customers Customer[]
- quotes Quote[]
- inventoryParts InventoryPart[]
- customFieldDefinitions CustomFieldDefinition[]
- settings AppSetting[]
- subscription Subscription?
- invitations TeamInvitation[]
- @@map("organizations")
- }
- model OrganizationMember {
- id String @id @default(cuid())
- role String @default("member")
- userId String
- user User @relation(fields: [userId], references: [id], onDelete: Cascade)
- organizationId String
- organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
- roleId String?
- customRole Role? @relation(fields: [roleId], references: [id], onDelete: SetNull)
- @@unique([userId, organizationId])
- @@map("organization_members")
- }
- model Role {
- id String @id @default(cuid())
- name String
- isAdmin Boolean @default(false)
- createdAt DateTime @default(now())
- updatedAt DateTime @updatedAt
- organizationId String
- organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
- permissions Permission[]
- members OrganizationMember[]
- teamInvitations TeamInvitation[]
- @@unique([organizationId, name])
- @@map("roles")
- }
- model Permission {
- id String @id @default(cuid())
- action String
- subject String
- roleId String
- role Role @relation(fields: [roleId], references: [id], onDelete: Cascade)
- @@unique([roleId, action, subject])
- @@index([roleId])
- @@map("permissions")
- }
- model SystemSetting {
- id String @id @default(cuid())
- key String @unique
- value String
- @@map("system_settings")
- }
- model TeamInvitation {
- id String @id @default(cuid())
- email String
- role String @default("member")
- token String @unique
- status String @default("pending") // pending | accepted | cancelled
- expiresAt DateTime
- createdAt DateTime @default(now())
- organizationId String
- organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
- invitedById String
- roleId String?
- customRole Role? @relation(fields: [roleId], references: [id], onDelete: SetNull)
- @@unique([email, organizationId])
- @@index([token])
- @@map("team_invitations")
- }
- // Stripe-ready models (implementation in follow-up)
- model SubscriptionPlan {
- id String @id @default(cuid())
- name String
- stripePriceId String? @unique
- price Float @default(0)
- interval String @default("month") // "month" | "year"
- maxMembers Int @default(1)
- features String? // JSON feature flags
- isActive Boolean @default(true)
- createdAt DateTime @default(now())
- updatedAt DateTime @updatedAt
- subscriptions Subscription[]
- @@map("subscription_plans")
- }
- model Subscription {
- id String @id @default(cuid())
- status String @default("active") // active | canceled | past_due | trialing
- stripeSubscriptionId String? @unique
- stripeCustomerId String?
- currentPeriodStart DateTime?
- currentPeriodEnd DateTime?
- cancelAtPeriodEnd Boolean @default(false)
- createdAt DateTime @default(now())
- updatedAt DateTime @updatedAt
- organizationId String @unique
- organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
- planId String
- plan SubscriptionPlan @relation(fields: [planId], references: [id])
- @@map("subscriptions")
- }
- model RecurringInvoice {
- id String @id @default(cuid())
- title String
- description String?
- frequency String // "weekly" | "biweekly" | "monthly" | "quarterly" | "yearly"
- nextRunDate DateTime
- endDate DateTime?
- isActive Boolean @default(true)
- lastRunAt DateTime?
- runCount Int @default(0)
- // Template fields (copied to ServiceRecord on generation)
- type String @default("maintenance")
- cost Float @default(0)
- taxRate Float @default(0)
- invoiceNotes String?
- vehicleId String
- vehicle Vehicle @relation(fields: [vehicleId], references: [id], onDelete: Cascade)
- createdAt DateTime @default(now())
- updatedAt DateTime @updatedAt
- templateParts RecurringPart[]
- templateLabor RecurringLabor[]
- @@index([vehicleId])
- @@index([nextRunDate, isActive])
- @@map("recurring_invoices")
- }
- model RecurringPart {
- id String @id @default(cuid())
- name String
- partNumber String?
- quantity Int @default(1)
- unitPrice Float @default(0)
- recurringInvoiceId String
- recurringInvoice RecurringInvoice @relation(fields: [recurringInvoiceId], references: [id], onDelete: Cascade)
- @@map("recurring_parts")
- }
- model RecurringLabor {
- id String @id @default(cuid())
- description String
- hours Float @default(0)
- rate Float @default(0)
- recurringInvoiceId String
- recurringInvoice RecurringInvoice @relation(fields: [recurringInvoiceId], references: [id], onDelete: Cascade)
- @@map("recurring_labor")
- }
|