schema.prisma 18 KB

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