schema.prisma 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658
  1. generator client {
  2. provider = "prisma-client-js"
  3. }
  4. datasource db {
  5. provider = "postgresql"
  6. url = env("DATABASE_URL")
  7. }
  8. model User {
  9. id String @id @default(cuid())
  10. name String
  11. email String @unique
  12. emailVerified Boolean @default(false)
  13. image String?
  14. isSuperAdmin Boolean @default(false)
  15. createdAt DateTime @default(now())
  16. updatedAt DateTime @updatedAt
  17. twoFactorEnabled Boolean @default(false)
  18. termsAcceptedAt DateTime?
  19. sessions Session[]
  20. accounts Account[]
  21. vehicles Vehicle[]
  22. customers Customer[]
  23. settings AppSetting[]
  24. inventoryParts InventoryPart[]
  25. quotes Quote[]
  26. customFieldDefinitions CustomFieldDefinition[]
  27. twoFactor TwoFactor?
  28. organizationMembers OrganizationMember[]
  29. @@map("users")
  30. }
  31. model AppSetting {
  32. id String @id @default(cuid())
  33. key String
  34. value String
  35. userId String
  36. user User @relation(fields: [userId], references: [id], onDelete: Cascade)
  37. organizationId String?
  38. organization Organization? @relation(fields: [organizationId], references: [id], onDelete: Cascade)
  39. @@unique([organizationId, key])
  40. @@index([organizationId])
  41. @@map("app_settings")
  42. }
  43. model Session {
  44. id String @id @default(cuid())
  45. expiresAt DateTime
  46. token String @unique
  47. createdAt DateTime @default(now())
  48. updatedAt DateTime @updatedAt
  49. ipAddress String?
  50. userAgent String?
  51. userId String
  52. user User @relation(fields: [userId], references: [id], onDelete: Cascade)
  53. @@index([userId])
  54. @@map("sessions")
  55. }
  56. model Account {
  57. id String @id @default(cuid())
  58. accountId String
  59. providerId String
  60. userId String
  61. user User @relation(fields: [userId], references: [id], onDelete: Cascade)
  62. accessToken String?
  63. refreshToken String?
  64. idToken String?
  65. accessTokenExpiresAt DateTime?
  66. refreshTokenExpiresAt DateTime?
  67. scope String?
  68. password String?
  69. createdAt DateTime @default(now())
  70. updatedAt DateTime @updatedAt
  71. @@index([userId])
  72. @@map("accounts")
  73. }
  74. model Verification {
  75. id String @id @default(cuid())
  76. identifier String
  77. value String
  78. expiresAt DateTime
  79. createdAt DateTime? @default(now())
  80. updatedAt DateTime? @updatedAt
  81. @@map("verifications")
  82. }
  83. model TwoFactor {
  84. id String @id @default(cuid())
  85. secret String
  86. backupCodes String
  87. userId String @unique
  88. user User @relation(fields: [userId], references: [id], onDelete: Cascade)
  89. @@map("two_factor")
  90. }
  91. model Vehicle {
  92. id String @id @default(cuid())
  93. make String
  94. model String
  95. year Int
  96. vin String?
  97. licensePlate String?
  98. color String?
  99. mileage Int @default(0)
  100. fuelType String? @default("gasoline")
  101. transmission String? @default("automatic")
  102. engineSize String?
  103. purchaseDate DateTime?
  104. purchasePrice Float?
  105. imageUrl String?
  106. isArchived Boolean @default(false)
  107. archiveReason String?
  108. maintenanceDismissed Boolean @default(false)
  109. maintenanceDismissedAt DateTime?
  110. createdAt DateTime @default(now())
  111. updatedAt DateTime @updatedAt
  112. userId String
  113. user User @relation(fields: [userId], references: [id], onDelete: Cascade)
  114. organizationId String?
  115. organization Organization? @relation(fields: [organizationId], references: [id], onDelete: Cascade)
  116. customerId String?
  117. customer Customer? @relation(fields: [customerId], references: [id], onDelete: SetNull)
  118. serviceRecords ServiceRecord[]
  119. notes Note[]
  120. fuelLogs FuelLog[]
  121. reminders Reminder[]
  122. quotes Quote[]
  123. recurringInvoices RecurringInvoice[]
  124. @@index([organizationId])
  125. @@map("vehicles")
  126. }
  127. model ServiceRecord {
  128. id String @id @default(cuid())
  129. title String
  130. description String?
  131. type String @default("maintenance")
  132. status String @default("completed")
  133. cost Float @default(0)
  134. mileage Int?
  135. serviceDate DateTime @default(now())
  136. shopName String?
  137. techName String?
  138. parts String?
  139. laborHours Float?
  140. diagnosticNotes String?
  141. invoiceNotes String?
  142. subtotal Float @default(0)
  143. taxRate Float @default(0)
  144. taxAmount Float @default(0)
  145. totalAmount Float @default(0)
  146. invoiceNumber String?
  147. discountType String?
  148. discountValue Float @default(0)
  149. discountAmount Float @default(0)
  150. manuallyPaid Boolean @default(false)
  151. publicToken String? @unique
  152. createdAt DateTime @default(now())
  153. updatedAt DateTime @updatedAt
  154. vehicleId String
  155. vehicle Vehicle @relation(fields: [vehicleId], references: [id], onDelete: Cascade)
  156. partItems ServicePart[]
  157. laborItems ServiceLabor[]
  158. attachments ServiceAttachment[]
  159. payments Payment[]
  160. @@index([vehicleId])
  161. @@map("service_records")
  162. }
  163. model Note {
  164. id String @id @default(cuid())
  165. title String
  166. content String
  167. isPinned Boolean @default(false)
  168. createdAt DateTime @default(now())
  169. updatedAt DateTime @updatedAt
  170. vehicleId String
  171. vehicle Vehicle @relation(fields: [vehicleId], references: [id], onDelete: Cascade)
  172. @@index([vehicleId])
  173. @@map("notes")
  174. }
  175. model FuelLog {
  176. id String @id @default(cuid())
  177. date DateTime @default(now())
  178. mileage Int
  179. gallons Float
  180. pricePerGallon Float
  181. totalCost Float
  182. isFillUp Boolean @default(true)
  183. station String?
  184. notes String?
  185. createdAt DateTime @default(now())
  186. updatedAt DateTime @updatedAt
  187. vehicleId String
  188. vehicle Vehicle @relation(fields: [vehicleId], references: [id], onDelete: Cascade)
  189. @@index([vehicleId])
  190. @@map("fuel_logs")
  191. }
  192. model Reminder {
  193. id String @id @default(cuid())
  194. title String
  195. description String?
  196. dueDate DateTime?
  197. dueMileage Int?
  198. isCompleted Boolean @default(false)
  199. createdAt DateTime @default(now())
  200. updatedAt DateTime @updatedAt
  201. vehicleId String
  202. vehicle Vehicle @relation(fields: [vehicleId], references: [id], onDelete: Cascade)
  203. @@index([vehicleId])
  204. @@map("reminders")
  205. }
  206. model Customer {
  207. id String @id @default(cuid())
  208. name String
  209. email String?
  210. phone String?
  211. address String?
  212. company String?
  213. notes String?
  214. createdAt DateTime @default(now())
  215. updatedAt DateTime @updatedAt
  216. userId String
  217. user User @relation(fields: [userId], references: [id], onDelete: Cascade)
  218. organizationId String?
  219. organization Organization? @relation(fields: [organizationId], references: [id], onDelete: Cascade)
  220. vehicles Vehicle[]
  221. quotes Quote[]
  222. @@index([organizationId])
  223. @@map("customers")
  224. }
  225. model Payment {
  226. id String @id @default(cuid())
  227. amount Float
  228. date DateTime @default(now())
  229. method String @default("other")
  230. note String?
  231. provider String?
  232. externalId String?
  233. createdAt DateTime @default(now())
  234. updatedAt DateTime @updatedAt
  235. serviceRecordId String
  236. serviceRecord ServiceRecord @relation(fields: [serviceRecordId], references: [id], onDelete: Cascade)
  237. @@index([serviceRecordId])
  238. @@map("payments")
  239. }
  240. model ServiceAttachment {
  241. id String @id @default(cuid())
  242. fileName String
  243. fileUrl String
  244. fileType String
  245. fileSize Int
  246. category String @default("diagnostic")
  247. description String?
  248. includeInInvoice Boolean @default(true)
  249. createdAt DateTime @default(now())
  250. serviceRecordId String
  251. serviceRecord ServiceRecord @relation(fields: [serviceRecordId], references: [id], onDelete: Cascade)
  252. @@index([serviceRecordId])
  253. @@map("service_attachments")
  254. }
  255. model ServicePart {
  256. id String @id @default(cuid())
  257. partNumber String?
  258. name String
  259. quantity Float @default(1)
  260. unitPrice Float @default(0)
  261. total Float @default(0)
  262. serviceRecordId String
  263. serviceRecord ServiceRecord @relation(fields: [serviceRecordId], references: [id], onDelete: Cascade)
  264. @@index([serviceRecordId])
  265. @@map("service_parts")
  266. }
  267. model ServiceLabor {
  268. id String @id @default(cuid())
  269. description String
  270. hours Float @default(0)
  271. rate Float @default(0)
  272. total Float @default(0)
  273. serviceRecordId String
  274. serviceRecord ServiceRecord @relation(fields: [serviceRecordId], references: [id], onDelete: Cascade)
  275. @@index([serviceRecordId])
  276. @@map("service_labor")
  277. }
  278. model InventoryPart {
  279. id String @id @default(cuid())
  280. partNumber String?
  281. name String
  282. description String?
  283. category String?
  284. quantity Int @default(0)
  285. minQuantity Int @default(0)
  286. unitCost Float @default(0)
  287. supplier String?
  288. supplierPhone String?
  289. supplierEmail String?
  290. supplierUrl String?
  291. imageUrl String?
  292. location String?
  293. isArchived Boolean @default(false)
  294. createdAt DateTime @default(now())
  295. updatedAt DateTime @updatedAt
  296. userId String
  297. user User @relation(fields: [userId], references: [id], onDelete: Cascade)
  298. organizationId String?
  299. organization Organization? @relation(fields: [organizationId], references: [id], onDelete: Cascade)
  300. @@index([organizationId])
  301. @@map("inventory_parts")
  302. }
  303. model Quote {
  304. id String @id @default(cuid())
  305. quoteNumber String?
  306. title String
  307. description String?
  308. status String @default("draft")
  309. validUntil DateTime?
  310. subtotal Float @default(0)
  311. taxRate Float @default(0)
  312. taxAmount Float @default(0)
  313. discountType String?
  314. discountValue Float @default(0)
  315. discountAmount Float @default(0)
  316. totalAmount Float @default(0)
  317. notes String?
  318. publicToken String?
  319. convertedToId String?
  320. createdAt DateTime @default(now())
  321. updatedAt DateTime @updatedAt
  322. userId String
  323. user User @relation(fields: [userId], references: [id], onDelete: Cascade)
  324. organizationId String?
  325. organization Organization? @relation(fields: [organizationId], references: [id], onDelete: Cascade)
  326. customerId String?
  327. customer Customer? @relation(fields: [customerId], references: [id], onDelete: SetNull)
  328. vehicleId String?
  329. vehicle Vehicle? @relation(fields: [vehicleId], references: [id], onDelete: SetNull)
  330. partItems QuotePart[]
  331. laborItems QuoteLabor[]
  332. @@index([organizationId])
  333. @@map("quotes")
  334. }
  335. model QuotePart {
  336. id String @id @default(cuid())
  337. partNumber String?
  338. name String
  339. quantity Float @default(1)
  340. unitPrice Float @default(0)
  341. total Float @default(0)
  342. quoteId String
  343. quote Quote @relation(fields: [quoteId], references: [id], onDelete: Cascade)
  344. @@index([quoteId])
  345. @@map("quote_parts")
  346. }
  347. model QuoteLabor {
  348. id String @id @default(cuid())
  349. description String
  350. hours Float @default(0)
  351. rate Float @default(0)
  352. total Float @default(0)
  353. quoteId String
  354. quote Quote @relation(fields: [quoteId], references: [id], onDelete: Cascade)
  355. @@index([quoteId])
  356. @@map("quote_labor")
  357. }
  358. model CustomFieldDefinition {
  359. id String @id @default(cuid())
  360. name String
  361. label String
  362. fieldType String @default("text")
  363. options String?
  364. required Boolean @default(false)
  365. entityType String
  366. sortOrder Int @default(0)
  367. isActive Boolean @default(true)
  368. createdAt DateTime @default(now())
  369. updatedAt DateTime @updatedAt
  370. userId String
  371. user User @relation(fields: [userId], references: [id], onDelete: Cascade)
  372. organizationId String?
  373. organization Organization? @relation(fields: [organizationId], references: [id], onDelete: Cascade)
  374. values CustomFieldValue[]
  375. @@unique([organizationId, name, entityType])
  376. @@index([organizationId])
  377. @@map("custom_field_definitions")
  378. }
  379. model CustomFieldValue {
  380. id String @id @default(cuid())
  381. value String
  382. entityId String
  383. entityType String
  384. fieldId String
  385. field CustomFieldDefinition @relation(fields: [fieldId], references: [id], onDelete: Cascade)
  386. @@unique([fieldId, entityId])
  387. @@index([fieldId])
  388. @@map("custom_field_values")
  389. }
  390. model Organization {
  391. id String @id @default(cuid())
  392. name String
  393. createdAt DateTime @default(now())
  394. updatedAt DateTime @updatedAt
  395. members OrganizationMember[]
  396. roles Role[]
  397. vehicles Vehicle[]
  398. customers Customer[]
  399. quotes Quote[]
  400. inventoryParts InventoryPart[]
  401. customFieldDefinitions CustomFieldDefinition[]
  402. settings AppSetting[]
  403. subscription Subscription?
  404. invitations TeamInvitation[]
  405. @@map("organizations")
  406. }
  407. model OrganizationMember {
  408. id String @id @default(cuid())
  409. role String @default("member")
  410. userId String
  411. user User @relation(fields: [userId], references: [id], onDelete: Cascade)
  412. organizationId String
  413. organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
  414. roleId String?
  415. customRole Role? @relation(fields: [roleId], references: [id], onDelete: SetNull)
  416. @@unique([userId, organizationId])
  417. @@map("organization_members")
  418. }
  419. model Role {
  420. id String @id @default(cuid())
  421. name String
  422. isAdmin Boolean @default(false)
  423. createdAt DateTime @default(now())
  424. updatedAt DateTime @updatedAt
  425. organizationId String
  426. organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
  427. permissions Permission[]
  428. members OrganizationMember[]
  429. teamInvitations TeamInvitation[]
  430. @@unique([organizationId, name])
  431. @@map("roles")
  432. }
  433. model Permission {
  434. id String @id @default(cuid())
  435. action String
  436. subject String
  437. roleId String
  438. role Role @relation(fields: [roleId], references: [id], onDelete: Cascade)
  439. @@unique([roleId, action, subject])
  440. @@index([roleId])
  441. @@map("permissions")
  442. }
  443. model SystemSetting {
  444. id String @id @default(cuid())
  445. key String @unique
  446. value String
  447. @@map("system_settings")
  448. }
  449. model TeamInvitation {
  450. id String @id @default(cuid())
  451. email String
  452. role String @default("member")
  453. token String @unique
  454. status String @default("pending") // pending | accepted | cancelled
  455. expiresAt DateTime
  456. createdAt DateTime @default(now())
  457. organizationId String
  458. organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
  459. invitedById String
  460. roleId String?
  461. customRole Role? @relation(fields: [roleId], references: [id], onDelete: SetNull)
  462. @@unique([email, organizationId])
  463. @@index([token])
  464. @@map("team_invitations")
  465. }
  466. // Stripe-ready models (implementation in follow-up)
  467. model SubscriptionPlan {
  468. id String @id @default(cuid())
  469. name String
  470. stripePriceId String? @unique
  471. price Float @default(0)
  472. interval String @default("month") // "month" | "year"
  473. maxMembers Int @default(1)
  474. features String? // JSON feature flags
  475. isActive Boolean @default(true)
  476. createdAt DateTime @default(now())
  477. updatedAt DateTime @updatedAt
  478. subscriptions Subscription[]
  479. @@map("subscription_plans")
  480. }
  481. model Subscription {
  482. id String @id @default(cuid())
  483. status String @default("active") // active | canceled | past_due | trialing
  484. stripeSubscriptionId String? @unique
  485. stripeCustomerId String?
  486. currentPeriodStart DateTime?
  487. currentPeriodEnd DateTime?
  488. cancelAtPeriodEnd Boolean @default(false)
  489. createdAt DateTime @default(now())
  490. updatedAt DateTime @updatedAt
  491. organizationId String @unique
  492. organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
  493. planId String
  494. plan SubscriptionPlan @relation(fields: [planId], references: [id])
  495. @@map("subscriptions")
  496. }
  497. model RecurringInvoice {
  498. id String @id @default(cuid())
  499. title String
  500. description String?
  501. frequency String // "weekly" | "biweekly" | "monthly" | "quarterly" | "yearly"
  502. nextRunDate DateTime
  503. endDate DateTime?
  504. isActive Boolean @default(true)
  505. lastRunAt DateTime?
  506. runCount Int @default(0)
  507. // Template fields (copied to ServiceRecord on generation)
  508. type String @default("maintenance")
  509. cost Float @default(0)
  510. taxRate Float @default(0)
  511. invoiceNotes String?
  512. vehicleId String
  513. vehicle Vehicle @relation(fields: [vehicleId], references: [id], onDelete: Cascade)
  514. createdAt DateTime @default(now())
  515. updatedAt DateTime @updatedAt
  516. templateParts RecurringPart[]
  517. templateLabor RecurringLabor[]
  518. @@index([vehicleId])
  519. @@index([nextRunDate, isActive])
  520. @@map("recurring_invoices")
  521. }
  522. model RecurringPart {
  523. id String @id @default(cuid())
  524. name String
  525. partNumber String?
  526. quantity Int @default(1)
  527. unitPrice Float @default(0)
  528. recurringInvoiceId String
  529. recurringInvoice RecurringInvoice @relation(fields: [recurringInvoiceId], references: [id], onDelete: Cascade)
  530. @@map("recurring_parts")
  531. }
  532. model RecurringLabor {
  533. id String @id @default(cuid())
  534. description String
  535. hours Float @default(0)
  536. rate Float @default(0)
  537. recurringInvoiceId String
  538. recurringInvoice RecurringInvoice @relation(fields: [recurringInvoiceId], references: [id], onDelete: Cascade)
  539. @@map("recurring_labor")
  540. }