schema.prisma 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990
  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. lastLogin DateTime?
  20. lastSeen DateTime?
  21. sessions Session[]
  22. accounts Account[]
  23. vehicles Vehicle[]
  24. customers Customer[]
  25. settings AppSetting[]
  26. inventoryParts InventoryPart[]
  27. quotes Quote[]
  28. customFieldDefinitions CustomFieldDefinition[]
  29. twoFactor TwoFactor?
  30. passkeys Passkey[]
  31. organizationMembers OrganizationMember[]
  32. @@map("users")
  33. }
  34. model AppSetting {
  35. id String @id @default(cuid())
  36. key String
  37. value String
  38. userId String
  39. user User @relation(fields: [userId], references: [id], onDelete: Cascade)
  40. organizationId String?
  41. organization Organization? @relation(fields: [organizationId], references: [id], onDelete: Cascade)
  42. @@unique([organizationId, key])
  43. @@index([organizationId])
  44. @@map("app_settings")
  45. }
  46. model Session {
  47. id String @id @default(cuid())
  48. expiresAt DateTime
  49. token String @unique
  50. createdAt DateTime @default(now())
  51. updatedAt DateTime @updatedAt
  52. ipAddress String?
  53. userAgent String?
  54. userId String
  55. user User @relation(fields: [userId], references: [id], onDelete: Cascade)
  56. @@index([userId])
  57. @@map("sessions")
  58. }
  59. model Account {
  60. id String @id @default(cuid())
  61. accountId String
  62. providerId String
  63. userId String
  64. user User @relation(fields: [userId], references: [id], onDelete: Cascade)
  65. accessToken String?
  66. refreshToken String?
  67. idToken String?
  68. accessTokenExpiresAt DateTime?
  69. refreshTokenExpiresAt DateTime?
  70. scope String?
  71. password String?
  72. createdAt DateTime @default(now())
  73. updatedAt DateTime @updatedAt
  74. @@index([userId])
  75. @@map("accounts")
  76. }
  77. model Verification {
  78. id String @id @default(cuid())
  79. identifier String @unique
  80. value String
  81. expiresAt DateTime
  82. createdAt DateTime? @default(now())
  83. updatedAt DateTime? @updatedAt
  84. @@map("verifications")
  85. }
  86. model TwoFactor {
  87. id String @id @default(cuid())
  88. secret String
  89. backupCodes String
  90. userId String @unique
  91. user User @relation(fields: [userId], references: [id], onDelete: Cascade)
  92. @@map("two_factor")
  93. }
  94. model Passkey {
  95. id String @id @default(cuid())
  96. name String?
  97. publicKey String
  98. userId String
  99. user User @relation(fields: [userId], references: [id], onDelete: Cascade)
  100. credentialID String @unique
  101. counter Int
  102. deviceType String
  103. backedUp Boolean
  104. transports String?
  105. aaguid String?
  106. createdAt DateTime @default(now())
  107. @@index([userId])
  108. @@map("passkeys")
  109. }
  110. model Vehicle {
  111. id String @id @default(cuid())
  112. make String
  113. model String
  114. year Int
  115. vin String?
  116. licensePlate String?
  117. color String?
  118. mileage Int @default(0)
  119. fuelType String? @default("gasoline")
  120. transmission String? @default("automatic")
  121. engineSize String?
  122. purchaseDate DateTime?
  123. purchasePrice Float?
  124. imageUrl String?
  125. isArchived Boolean @default(false)
  126. archiveReason String?
  127. maintenanceDismissed Boolean @default(false)
  128. maintenanceDismissedAt DateTime?
  129. createdAt DateTime @default(now())
  130. updatedAt DateTime @updatedAt
  131. userId String
  132. user User @relation(fields: [userId], references: [id], onDelete: Cascade)
  133. organizationId String?
  134. organization Organization? @relation(fields: [organizationId], references: [id], onDelete: Cascade)
  135. customerId String?
  136. customer Customer? @relation(fields: [customerId], references: [id], onDelete: SetNull)
  137. serviceRecords ServiceRecord[]
  138. notes Note[]
  139. fuelLogs FuelLog[]
  140. reminders Reminder[]
  141. quotes Quote[]
  142. recurringInvoices RecurringInvoice[]
  143. inspections Inspection[]
  144. serviceRequests ServiceRequest[]
  145. @@index([organizationId])
  146. @@map("vehicles")
  147. }
  148. model ServiceRecord {
  149. id String @id @default(cuid())
  150. title String
  151. description String?
  152. type String @default("maintenance")
  153. status String @default("completed")
  154. cost Float @default(0)
  155. mileage Int?
  156. serviceDate DateTime @default(now())
  157. shopName String?
  158. techName String?
  159. parts String?
  160. laborHours Float?
  161. diagnosticNotes String?
  162. invoiceNotes String?
  163. subtotal Float @default(0)
  164. taxRate Float @default(0)
  165. taxAmount Float @default(0)
  166. totalAmount Float @default(0)
  167. invoiceNumber String?
  168. discountType String?
  169. discountValue Float @default(0)
  170. discountAmount Float @default(0)
  171. manuallyPaid Boolean @default(false)
  172. publicToken String? @unique
  173. sharedAt DateTime?
  174. viewCount Int @default(0)
  175. lastViewedAt DateTime?
  176. createdAt DateTime @default(now())
  177. updatedAt DateTime @updatedAt
  178. vehicleId String
  179. vehicle Vehicle @relation(fields: [vehicleId], references: [id], onDelete: Cascade)
  180. partItems ServicePart[]
  181. laborItems ServiceLabor[]
  182. attachments ServiceAttachment[]
  183. payments Payment[]
  184. boardAssignments BoardAssignment[]
  185. @@index([vehicleId])
  186. @@map("service_records")
  187. }
  188. model Note {
  189. id String @id @default(cuid())
  190. title String
  191. content String
  192. isPinned Boolean @default(false)
  193. createdAt DateTime @default(now())
  194. updatedAt DateTime @updatedAt
  195. vehicleId String
  196. vehicle Vehicle @relation(fields: [vehicleId], references: [id], onDelete: Cascade)
  197. @@index([vehicleId])
  198. @@map("notes")
  199. }
  200. model FuelLog {
  201. id String @id @default(cuid())
  202. date DateTime @default(now())
  203. mileage Int
  204. gallons Float
  205. pricePerGallon Float
  206. totalCost Float
  207. isFillUp Boolean @default(true)
  208. station String?
  209. notes String?
  210. createdAt DateTime @default(now())
  211. updatedAt DateTime @updatedAt
  212. vehicleId String
  213. vehicle Vehicle @relation(fields: [vehicleId], references: [id], onDelete: Cascade)
  214. @@index([vehicleId])
  215. @@map("fuel_logs")
  216. }
  217. model Reminder {
  218. id String @id @default(cuid())
  219. title String
  220. description String?
  221. dueDate DateTime?
  222. dueMileage Int?
  223. isCompleted Boolean @default(false)
  224. createdAt DateTime @default(now())
  225. updatedAt DateTime @updatedAt
  226. vehicleId String
  227. vehicle Vehicle @relation(fields: [vehicleId], references: [id], onDelete: Cascade)
  228. @@index([vehicleId])
  229. @@map("reminders")
  230. }
  231. model Customer {
  232. id String @id @default(cuid())
  233. name String
  234. email String?
  235. phone String?
  236. address String?
  237. company String?
  238. notes String?
  239. createdAt DateTime @default(now())
  240. updatedAt DateTime @updatedAt
  241. userId String
  242. user User @relation(fields: [userId], references: [id], onDelete: Cascade)
  243. organizationId String?
  244. organization Organization? @relation(fields: [organizationId], references: [id], onDelete: Cascade)
  245. vehicles Vehicle[]
  246. quotes Quote[]
  247. smsMessages SmsMessage[]
  248. customerSessions CustomerSession[]
  249. serviceRequests ServiceRequest[]
  250. @@index([organizationId])
  251. @@map("customers")
  252. }
  253. model Payment {
  254. id String @id @default(cuid())
  255. amount Float
  256. date DateTime @default(now())
  257. method String @default("other")
  258. note String?
  259. provider String?
  260. externalId String?
  261. createdAt DateTime @default(now())
  262. updatedAt DateTime @updatedAt
  263. serviceRecordId String
  264. serviceRecord ServiceRecord @relation(fields: [serviceRecordId], references: [id], onDelete: Cascade)
  265. @@index([serviceRecordId])
  266. @@map("payments")
  267. }
  268. model ServiceAttachment {
  269. id String @id @default(cuid())
  270. fileName String
  271. fileUrl String
  272. fileType String
  273. fileSize Int
  274. category String @default("diagnostic")
  275. description String?
  276. includeInInvoice Boolean @default(true)
  277. createdAt DateTime @default(now())
  278. updatedAt DateTime @default(now()) @updatedAt
  279. serviceRecordId String
  280. serviceRecord ServiceRecord @relation(fields: [serviceRecordId], references: [id], onDelete: Cascade)
  281. @@index([serviceRecordId])
  282. @@map("service_attachments")
  283. }
  284. model ServicePart {
  285. id String @id @default(cuid())
  286. partNumber String?
  287. name String
  288. quantity Float @default(1)
  289. unitPrice Float @default(0)
  290. total Float @default(0)
  291. createdAt DateTime @default(now())
  292. updatedAt DateTime @default(now()) @updatedAt
  293. serviceRecordId String
  294. serviceRecord ServiceRecord @relation(fields: [serviceRecordId], references: [id], onDelete: Cascade)
  295. @@index([serviceRecordId])
  296. @@map("service_parts")
  297. }
  298. model ServiceLabor {
  299. id String @id @default(cuid())
  300. description String
  301. hours Float @default(0)
  302. rate Float @default(0)
  303. total Float @default(0)
  304. createdAt DateTime @default(now())
  305. updatedAt DateTime @default(now()) @updatedAt
  306. serviceRecordId String
  307. serviceRecord ServiceRecord @relation(fields: [serviceRecordId], references: [id], onDelete: Cascade)
  308. @@index([serviceRecordId])
  309. @@map("service_labor")
  310. }
  311. model InventoryPart {
  312. id String @id @default(cuid())
  313. partNumber String?
  314. name String
  315. description String?
  316. category String?
  317. quantity Int @default(0)
  318. minQuantity Int @default(0)
  319. unitCost Float @default(0)
  320. sellPrice Float @default(0)
  321. supplier String?
  322. supplierPhone String?
  323. supplierEmail String?
  324. supplierUrl String?
  325. imageUrl String?
  326. location String?
  327. isArchived Boolean @default(false)
  328. createdAt DateTime @default(now())
  329. updatedAt DateTime @updatedAt
  330. userId String
  331. user User @relation(fields: [userId], references: [id], onDelete: Cascade)
  332. organizationId String?
  333. organization Organization? @relation(fields: [organizationId], references: [id], onDelete: Cascade)
  334. @@index([organizationId])
  335. @@map("inventory_parts")
  336. }
  337. model Quote {
  338. id String @id @default(cuid())
  339. quoteNumber String?
  340. title String
  341. description String?
  342. status String @default("draft")
  343. validUntil DateTime?
  344. subtotal Float @default(0)
  345. taxRate Float @default(0)
  346. taxAmount Float @default(0)
  347. discountType String?
  348. discountValue Float @default(0)
  349. discountAmount Float @default(0)
  350. totalAmount Float @default(0)
  351. notes String?
  352. publicToken String?
  353. sharedAt DateTime?
  354. viewCount Int @default(0)
  355. lastViewedAt DateTime?
  356. customerMessage String?
  357. convertedToId String?
  358. createdAt DateTime @default(now())
  359. updatedAt DateTime @updatedAt
  360. userId String
  361. user User @relation(fields: [userId], references: [id], onDelete: Cascade)
  362. organizationId String?
  363. organization Organization? @relation(fields: [organizationId], references: [id], onDelete: Cascade)
  364. customerId String?
  365. customer Customer? @relation(fields: [customerId], references: [id], onDelete: SetNull)
  366. vehicleId String?
  367. vehicle Vehicle? @relation(fields: [vehicleId], references: [id], onDelete: SetNull)
  368. inspectionId String?
  369. inspection Inspection? @relation(fields: [inspectionId], references: [id], onDelete: SetNull)
  370. partItems QuotePart[]
  371. laborItems QuoteLabor[]
  372. attachments QuoteAttachment[]
  373. @@index([organizationId])
  374. @@map("quotes")
  375. }
  376. model QuoteAttachment {
  377. id String @id @default(cuid())
  378. fileName String
  379. fileUrl String
  380. fileType String
  381. fileSize Int
  382. category String @default("image")
  383. description String?
  384. includeInInvoice Boolean @default(true)
  385. createdAt DateTime @default(now())
  386. updatedAt DateTime @default(now()) @updatedAt
  387. quoteId String
  388. quote Quote @relation(fields: [quoteId], references: [id], onDelete: Cascade)
  389. @@index([quoteId])
  390. @@map("quote_attachments")
  391. }
  392. model QuotePart {
  393. id String @id @default(cuid())
  394. partNumber String?
  395. name String
  396. quantity Float @default(1)
  397. unitPrice Float @default(0)
  398. total Float @default(0)
  399. excluded Boolean @default(false)
  400. createdAt DateTime @default(now())
  401. updatedAt DateTime @default(now()) @updatedAt
  402. quoteId String
  403. quote Quote @relation(fields: [quoteId], references: [id], onDelete: Cascade)
  404. @@index([quoteId])
  405. @@map("quote_parts")
  406. }
  407. model QuoteLabor {
  408. id String @id @default(cuid())
  409. description String
  410. hours Float @default(0)
  411. rate Float @default(0)
  412. total Float @default(0)
  413. excluded Boolean @default(false)
  414. createdAt DateTime @default(now())
  415. updatedAt DateTime @default(now()) @updatedAt
  416. quoteId String
  417. quote Quote @relation(fields: [quoteId], references: [id], onDelete: Cascade)
  418. @@index([quoteId])
  419. @@map("quote_labor")
  420. }
  421. model CustomFieldDefinition {
  422. id String @id @default(cuid())
  423. name String
  424. label String
  425. fieldType String @default("text")
  426. options String?
  427. required Boolean @default(false)
  428. entityType String
  429. sortOrder Int @default(0)
  430. isActive Boolean @default(true)
  431. createdAt DateTime @default(now())
  432. updatedAt DateTime @updatedAt
  433. userId String
  434. user User @relation(fields: [userId], references: [id], onDelete: Cascade)
  435. organizationId String?
  436. organization Organization? @relation(fields: [organizationId], references: [id], onDelete: Cascade)
  437. values CustomFieldValue[]
  438. @@unique([organizationId, name, entityType])
  439. @@index([organizationId])
  440. @@map("custom_field_definitions")
  441. }
  442. model CustomFieldValue {
  443. id String @id @default(cuid())
  444. value String
  445. entityId String
  446. entityType String
  447. fieldId String
  448. field CustomFieldDefinition @relation(fields: [fieldId], references: [id], onDelete: Cascade)
  449. @@unique([fieldId, entityId])
  450. @@index([fieldId])
  451. @@map("custom_field_values")
  452. }
  453. model Organization {
  454. id String @id @default(cuid())
  455. name String
  456. portalSlug String? @unique
  457. createdAt DateTime @default(now())
  458. updatedAt DateTime @updatedAt
  459. members OrganizationMember[]
  460. roles Role[]
  461. vehicles Vehicle[]
  462. customers Customer[]
  463. quotes Quote[]
  464. inventoryParts InventoryPart[]
  465. customFieldDefinitions CustomFieldDefinition[]
  466. settings AppSetting[]
  467. subscription Subscription?
  468. invitations TeamInvitation[]
  469. inspectionTemplates InspectionTemplate[]
  470. smsMessages SmsMessage[]
  471. technicians Technician[]
  472. syncDeletions SyncDeletion[]
  473. @@map("organizations")
  474. }
  475. model OrganizationMember {
  476. id String @id @default(cuid())
  477. role String @default("member")
  478. userId String
  479. user User @relation(fields: [userId], references: [id], onDelete: Cascade)
  480. organizationId String
  481. organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
  482. roleId String?
  483. customRole Role? @relation(fields: [roleId], references: [id], onDelete: SetNull)
  484. @@unique([userId, organizationId])
  485. @@map("organization_members")
  486. }
  487. model Role {
  488. id String @id @default(cuid())
  489. name String
  490. isAdmin Boolean @default(false)
  491. createdAt DateTime @default(now())
  492. updatedAt DateTime @updatedAt
  493. organizationId String
  494. organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
  495. permissions Permission[]
  496. members OrganizationMember[]
  497. teamInvitations TeamInvitation[]
  498. @@unique([organizationId, name])
  499. @@map("roles")
  500. }
  501. model Permission {
  502. id String @id @default(cuid())
  503. action String
  504. subject String
  505. roleId String
  506. role Role @relation(fields: [roleId], references: [id], onDelete: Cascade)
  507. @@unique([roleId, action, subject])
  508. @@index([roleId])
  509. @@map("permissions")
  510. }
  511. model SystemSetting {
  512. id String @id @default(cuid())
  513. key String @unique
  514. value String
  515. @@map("system_settings")
  516. }
  517. model TeamInvitation {
  518. id String @id @default(cuid())
  519. email String
  520. role String @default("member")
  521. token String @unique
  522. status String @default("pending") // pending | accepted | cancelled
  523. expiresAt DateTime
  524. createdAt DateTime @default(now())
  525. organizationId String
  526. organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
  527. invitedById String
  528. roleId String?
  529. customRole Role? @relation(fields: [roleId], references: [id], onDelete: SetNull)
  530. @@unique([email, organizationId])
  531. @@index([token])
  532. @@map("team_invitations")
  533. }
  534. // Stripe-ready models (implementation in follow-up)
  535. model SubscriptionPlan {
  536. id String @id @default(cuid())
  537. name String
  538. stripePriceId String? @unique
  539. price Float @default(0)
  540. interval String @default("month") // "month" | "year"
  541. maxMembers Int @default(1)
  542. features String? // JSON feature flags
  543. isActive Boolean @default(true)
  544. createdAt DateTime @default(now())
  545. updatedAt DateTime @updatedAt
  546. subscriptions Subscription[]
  547. @@map("subscription_plans")
  548. }
  549. model Subscription {
  550. id String @id @default(cuid())
  551. status String @default("active") // active | canceled | past_due | trialing
  552. stripeSubscriptionId String? @unique
  553. stripeCustomerId String?
  554. currentPeriodStart DateTime?
  555. currentPeriodEnd DateTime?
  556. cancelAtPeriodEnd Boolean @default(false)
  557. createdAt DateTime @default(now())
  558. updatedAt DateTime @updatedAt
  559. organizationId String @unique
  560. organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
  561. planId String
  562. plan SubscriptionPlan @relation(fields: [planId], references: [id])
  563. @@map("subscriptions")
  564. }
  565. model RecurringInvoice {
  566. id String @id @default(cuid())
  567. title String
  568. description String?
  569. frequency String // "weekly" | "biweekly" | "monthly" | "quarterly" | "yearly"
  570. nextRunDate DateTime
  571. endDate DateTime?
  572. isActive Boolean @default(true)
  573. lastRunAt DateTime?
  574. runCount Int @default(0)
  575. // Template fields (copied to ServiceRecord on generation)
  576. type String @default("maintenance")
  577. cost Float @default(0)
  578. taxRate Float @default(0)
  579. invoiceNotes String?
  580. vehicleId String
  581. vehicle Vehicle @relation(fields: [vehicleId], references: [id], onDelete: Cascade)
  582. createdAt DateTime @default(now())
  583. updatedAt DateTime @updatedAt
  584. templateParts RecurringPart[]
  585. templateLabor RecurringLabor[]
  586. @@index([vehicleId])
  587. @@index([nextRunDate, isActive])
  588. @@map("recurring_invoices")
  589. }
  590. model RecurringPart {
  591. id String @id @default(cuid())
  592. name String
  593. partNumber String?
  594. quantity Int @default(1)
  595. unitPrice Float @default(0)
  596. recurringInvoiceId String
  597. recurringInvoice RecurringInvoice @relation(fields: [recurringInvoiceId], references: [id], onDelete: Cascade)
  598. @@map("recurring_parts")
  599. }
  600. model RecurringLabor {
  601. id String @id @default(cuid())
  602. description String
  603. hours Float @default(0)
  604. rate Float @default(0)
  605. recurringInvoiceId String
  606. recurringInvoice RecurringInvoice @relation(fields: [recurringInvoiceId], references: [id], onDelete: Cascade)
  607. @@map("recurring_labor")
  608. }
  609. model InspectionTemplate {
  610. id String @id @default(cuid())
  611. name String
  612. description String?
  613. isDefault Boolean @default(false)
  614. createdAt DateTime @default(now())
  615. updatedAt DateTime @updatedAt
  616. organizationId String
  617. organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
  618. sections InspectionTemplateSection[]
  619. inspections Inspection[]
  620. @@index([organizationId])
  621. @@map("inspection_templates")
  622. }
  623. model InspectionTemplateSection {
  624. id String @id @default(cuid())
  625. name String
  626. sortOrder Int @default(0)
  627. templateId String
  628. template InspectionTemplate @relation(fields: [templateId], references: [id], onDelete: Cascade)
  629. items InspectionTemplateItem[]
  630. @@index([templateId])
  631. @@map("inspection_template_sections")
  632. }
  633. model InspectionTemplateItem {
  634. id String @id @default(cuid())
  635. name String
  636. sortOrder Int @default(0)
  637. sectionId String
  638. section InspectionTemplateSection @relation(fields: [sectionId], references: [id], onDelete: Cascade)
  639. @@index([sectionId])
  640. @@map("inspection_template_items")
  641. }
  642. model Inspection {
  643. id String @id @default(cuid())
  644. status String @default("in_progress")
  645. mileage Int?
  646. notes String?
  647. publicToken String? @unique
  648. completedAt DateTime?
  649. createdAt DateTime @default(now())
  650. updatedAt DateTime @updatedAt
  651. vehicleId String
  652. vehicle Vehicle @relation(fields: [vehicleId], references: [id], onDelete: Cascade)
  653. templateId String
  654. template InspectionTemplate @relation(fields: [templateId], references: [id])
  655. technicianId String
  656. organizationId String
  657. items InspectionItem[]
  658. quoteRequests InspectionQuoteRequest[]
  659. quotes Quote[]
  660. boardAssignments BoardAssignment[]
  661. @@index([vehicleId])
  662. @@index([organizationId])
  663. @@map("inspections")
  664. }
  665. model InspectionItem {
  666. id String @id @default(cuid())
  667. name String
  668. section String
  669. sortOrder Int @default(0)
  670. condition String @default("not_inspected")
  671. notes String?
  672. imageUrls String[] @default([])
  673. inspectionId String
  674. inspection Inspection @relation(fields: [inspectionId], references: [id], onDelete: Cascade)
  675. @@index([inspectionId])
  676. @@map("inspection_items")
  677. }
  678. model InspectionQuoteRequest {
  679. id String @id @default(cuid())
  680. status String @default("pending")
  681. message String?
  682. selectedItemIds String[]
  683. createdAt DateTime @default(now())
  684. inspectionId String
  685. inspection Inspection @relation(fields: [inspectionId], references: [id], onDelete: Cascade)
  686. organizationId String
  687. @@index([organizationId])
  688. @@index([inspectionId])
  689. @@map("inspection_quote_requests")
  690. }
  691. model Notification {
  692. id String @id @default(cuid())
  693. type String
  694. title String
  695. message String
  696. entityType String
  697. entityId String
  698. entityUrl String
  699. read Boolean @default(false)
  700. organizationId String
  701. createdAt DateTime @default(now())
  702. @@index([organizationId, createdAt(sort: Desc)])
  703. @@index([organizationId, read])
  704. @@map("notifications")
  705. }
  706. model SmsMessage {
  707. id String @id @default(cuid())
  708. direction String // "inbound" | "outbound"
  709. fromNumber String
  710. toNumber String
  711. body String
  712. status String @default("queued") // queued | sent | delivered | failed | received
  713. providerMsgId String?
  714. errorMessage String?
  715. relatedEntityType String?
  716. relatedEntityId String?
  717. createdAt DateTime @default(now())
  718. updatedAt DateTime @updatedAt
  719. organizationId String
  720. organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
  721. customerId String?
  722. customer Customer? @relation(fields: [customerId], references: [id], onDelete: SetNull)
  723. @@index([organizationId, customerId, createdAt(sort: Desc)])
  724. @@index([organizationId, createdAt(sort: Desc)])
  725. @@index([toNumber, organizationId])
  726. @@map("sms_messages")
  727. }
  728. model Technician {
  729. id String @id @default(cuid())
  730. name String
  731. color String @default("#3b82f6")
  732. isActive Boolean @default(true)
  733. sortOrder Int @default(0)
  734. memberId String?
  735. organizationId String
  736. organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
  737. assignments BoardAssignment[]
  738. createdAt DateTime @default(now())
  739. updatedAt DateTime @updatedAt
  740. @@index([organizationId])
  741. @@map("technicians")
  742. }
  743. model BoardAssignment {
  744. id String @id @default(cuid())
  745. date DateTime @db.Date
  746. sortOrder Int @default(0)
  747. notes String?
  748. technicianId String
  749. technician Technician @relation(fields: [technicianId], references: [id], onDelete: Cascade)
  750. serviceRecordId String?
  751. serviceRecord ServiceRecord? @relation(fields: [serviceRecordId], references: [id], onDelete: Cascade)
  752. inspectionId String?
  753. inspection Inspection? @relation(fields: [inspectionId], references: [id], onDelete: Cascade)
  754. organizationId String
  755. createdAt DateTime @default(now())
  756. updatedAt DateTime @updatedAt
  757. @@unique([serviceRecordId, date])
  758. @@unique([inspectionId, date])
  759. @@index([organizationId, date])
  760. @@index([technicianId, date])
  761. @@map("board_assignments")
  762. }
  763. model CustomerSession {
  764. id String @id @default(cuid())
  765. token String @unique
  766. expiresAt DateTime
  767. createdAt DateTime @default(now())
  768. customerId String
  769. customer Customer @relation(fields: [customerId], references: [id], onDelete: Cascade)
  770. organizationId String
  771. @@index([token])
  772. @@index([customerId, organizationId])
  773. @@map("customer_sessions")
  774. }
  775. model CustomerMagicLink {
  776. id String @id @default(cuid())
  777. token String @unique
  778. email String
  779. organizationId String
  780. expiresAt DateTime
  781. usedAt DateTime?
  782. createdAt DateTime @default(now())
  783. @@index([token])
  784. @@map("customer_magic_links")
  785. }
  786. model ServiceRequest {
  787. id String @id @default(cuid())
  788. description String
  789. preferredDate DateTime?
  790. status String @default("pending")
  791. adminNotes String?
  792. createdAt DateTime @default(now())
  793. updatedAt DateTime @updatedAt
  794. customerId String
  795. customer Customer @relation(fields: [customerId], references: [id], onDelete: Cascade)
  796. vehicleId String
  797. vehicle Vehicle @relation(fields: [vehicleId], references: [id], onDelete: Cascade)
  798. organizationId String
  799. @@index([organizationId, status])
  800. @@index([customerId])
  801. @@map("service_requests")
  802. }
  803. model SyncDeletion {
  804. id String @id @default(cuid())
  805. entity String
  806. recordId String
  807. organizationId String
  808. deletedAt DateTime @default(now())
  809. organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
  810. @@index([organizationId, deletedAt])
  811. @@map("sync_deletions")
  812. }