schema.prisma 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988
  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. createdAt DateTime @default(now())
  400. updatedAt DateTime @default(now()) @updatedAt
  401. quoteId String
  402. quote Quote @relation(fields: [quoteId], references: [id], onDelete: Cascade)
  403. @@index([quoteId])
  404. @@map("quote_parts")
  405. }
  406. model QuoteLabor {
  407. id String @id @default(cuid())
  408. description String
  409. hours Float @default(0)
  410. rate Float @default(0)
  411. total Float @default(0)
  412. createdAt DateTime @default(now())
  413. updatedAt DateTime @default(now()) @updatedAt
  414. quoteId String
  415. quote Quote @relation(fields: [quoteId], references: [id], onDelete: Cascade)
  416. @@index([quoteId])
  417. @@map("quote_labor")
  418. }
  419. model CustomFieldDefinition {
  420. id String @id @default(cuid())
  421. name String
  422. label String
  423. fieldType String @default("text")
  424. options String?
  425. required Boolean @default(false)
  426. entityType String
  427. sortOrder Int @default(0)
  428. isActive Boolean @default(true)
  429. createdAt DateTime @default(now())
  430. updatedAt DateTime @updatedAt
  431. userId String
  432. user User @relation(fields: [userId], references: [id], onDelete: Cascade)
  433. organizationId String?
  434. organization Organization? @relation(fields: [organizationId], references: [id], onDelete: Cascade)
  435. values CustomFieldValue[]
  436. @@unique([organizationId, name, entityType])
  437. @@index([organizationId])
  438. @@map("custom_field_definitions")
  439. }
  440. model CustomFieldValue {
  441. id String @id @default(cuid())
  442. value String
  443. entityId String
  444. entityType String
  445. fieldId String
  446. field CustomFieldDefinition @relation(fields: [fieldId], references: [id], onDelete: Cascade)
  447. @@unique([fieldId, entityId])
  448. @@index([fieldId])
  449. @@map("custom_field_values")
  450. }
  451. model Organization {
  452. id String @id @default(cuid())
  453. name String
  454. portalSlug String? @unique
  455. createdAt DateTime @default(now())
  456. updatedAt DateTime @updatedAt
  457. members OrganizationMember[]
  458. roles Role[]
  459. vehicles Vehicle[]
  460. customers Customer[]
  461. quotes Quote[]
  462. inventoryParts InventoryPart[]
  463. customFieldDefinitions CustomFieldDefinition[]
  464. settings AppSetting[]
  465. subscription Subscription?
  466. invitations TeamInvitation[]
  467. inspectionTemplates InspectionTemplate[]
  468. smsMessages SmsMessage[]
  469. technicians Technician[]
  470. syncDeletions SyncDeletion[]
  471. @@map("organizations")
  472. }
  473. model OrganizationMember {
  474. id String @id @default(cuid())
  475. role String @default("member")
  476. userId String
  477. user User @relation(fields: [userId], references: [id], onDelete: Cascade)
  478. organizationId String
  479. organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
  480. roleId String?
  481. customRole Role? @relation(fields: [roleId], references: [id], onDelete: SetNull)
  482. @@unique([userId, organizationId])
  483. @@map("organization_members")
  484. }
  485. model Role {
  486. id String @id @default(cuid())
  487. name String
  488. isAdmin Boolean @default(false)
  489. createdAt DateTime @default(now())
  490. updatedAt DateTime @updatedAt
  491. organizationId String
  492. organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
  493. permissions Permission[]
  494. members OrganizationMember[]
  495. teamInvitations TeamInvitation[]
  496. @@unique([organizationId, name])
  497. @@map("roles")
  498. }
  499. model Permission {
  500. id String @id @default(cuid())
  501. action String
  502. subject String
  503. roleId String
  504. role Role @relation(fields: [roleId], references: [id], onDelete: Cascade)
  505. @@unique([roleId, action, subject])
  506. @@index([roleId])
  507. @@map("permissions")
  508. }
  509. model SystemSetting {
  510. id String @id @default(cuid())
  511. key String @unique
  512. value String
  513. @@map("system_settings")
  514. }
  515. model TeamInvitation {
  516. id String @id @default(cuid())
  517. email String
  518. role String @default("member")
  519. token String @unique
  520. status String @default("pending") // pending | accepted | cancelled
  521. expiresAt DateTime
  522. createdAt DateTime @default(now())
  523. organizationId String
  524. organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
  525. invitedById String
  526. roleId String?
  527. customRole Role? @relation(fields: [roleId], references: [id], onDelete: SetNull)
  528. @@unique([email, organizationId])
  529. @@index([token])
  530. @@map("team_invitations")
  531. }
  532. // Stripe-ready models (implementation in follow-up)
  533. model SubscriptionPlan {
  534. id String @id @default(cuid())
  535. name String
  536. stripePriceId String? @unique
  537. price Float @default(0)
  538. interval String @default("month") // "month" | "year"
  539. maxMembers Int @default(1)
  540. features String? // JSON feature flags
  541. isActive Boolean @default(true)
  542. createdAt DateTime @default(now())
  543. updatedAt DateTime @updatedAt
  544. subscriptions Subscription[]
  545. @@map("subscription_plans")
  546. }
  547. model Subscription {
  548. id String @id @default(cuid())
  549. status String @default("active") // active | canceled | past_due | trialing
  550. stripeSubscriptionId String? @unique
  551. stripeCustomerId String?
  552. currentPeriodStart DateTime?
  553. currentPeriodEnd DateTime?
  554. cancelAtPeriodEnd Boolean @default(false)
  555. createdAt DateTime @default(now())
  556. updatedAt DateTime @updatedAt
  557. organizationId String @unique
  558. organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
  559. planId String
  560. plan SubscriptionPlan @relation(fields: [planId], references: [id])
  561. @@map("subscriptions")
  562. }
  563. model RecurringInvoice {
  564. id String @id @default(cuid())
  565. title String
  566. description String?
  567. frequency String // "weekly" | "biweekly" | "monthly" | "quarterly" | "yearly"
  568. nextRunDate DateTime
  569. endDate DateTime?
  570. isActive Boolean @default(true)
  571. lastRunAt DateTime?
  572. runCount Int @default(0)
  573. // Template fields (copied to ServiceRecord on generation)
  574. type String @default("maintenance")
  575. cost Float @default(0)
  576. taxRate Float @default(0)
  577. invoiceNotes String?
  578. vehicleId String
  579. vehicle Vehicle @relation(fields: [vehicleId], references: [id], onDelete: Cascade)
  580. createdAt DateTime @default(now())
  581. updatedAt DateTime @updatedAt
  582. templateParts RecurringPart[]
  583. templateLabor RecurringLabor[]
  584. @@index([vehicleId])
  585. @@index([nextRunDate, isActive])
  586. @@map("recurring_invoices")
  587. }
  588. model RecurringPart {
  589. id String @id @default(cuid())
  590. name String
  591. partNumber String?
  592. quantity Int @default(1)
  593. unitPrice Float @default(0)
  594. recurringInvoiceId String
  595. recurringInvoice RecurringInvoice @relation(fields: [recurringInvoiceId], references: [id], onDelete: Cascade)
  596. @@map("recurring_parts")
  597. }
  598. model RecurringLabor {
  599. id String @id @default(cuid())
  600. description String
  601. hours Float @default(0)
  602. rate Float @default(0)
  603. recurringInvoiceId String
  604. recurringInvoice RecurringInvoice @relation(fields: [recurringInvoiceId], references: [id], onDelete: Cascade)
  605. @@map("recurring_labor")
  606. }
  607. model InspectionTemplate {
  608. id String @id @default(cuid())
  609. name String
  610. description String?
  611. isDefault Boolean @default(false)
  612. createdAt DateTime @default(now())
  613. updatedAt DateTime @updatedAt
  614. organizationId String
  615. organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
  616. sections InspectionTemplateSection[]
  617. inspections Inspection[]
  618. @@index([organizationId])
  619. @@map("inspection_templates")
  620. }
  621. model InspectionTemplateSection {
  622. id String @id @default(cuid())
  623. name String
  624. sortOrder Int @default(0)
  625. templateId String
  626. template InspectionTemplate @relation(fields: [templateId], references: [id], onDelete: Cascade)
  627. items InspectionTemplateItem[]
  628. @@index([templateId])
  629. @@map("inspection_template_sections")
  630. }
  631. model InspectionTemplateItem {
  632. id String @id @default(cuid())
  633. name String
  634. sortOrder Int @default(0)
  635. sectionId String
  636. section InspectionTemplateSection @relation(fields: [sectionId], references: [id], onDelete: Cascade)
  637. @@index([sectionId])
  638. @@map("inspection_template_items")
  639. }
  640. model Inspection {
  641. id String @id @default(cuid())
  642. status String @default("in_progress")
  643. mileage Int?
  644. notes String?
  645. publicToken String? @unique
  646. completedAt DateTime?
  647. createdAt DateTime @default(now())
  648. updatedAt DateTime @updatedAt
  649. vehicleId String
  650. vehicle Vehicle @relation(fields: [vehicleId], references: [id], onDelete: Cascade)
  651. templateId String
  652. template InspectionTemplate @relation(fields: [templateId], references: [id])
  653. technicianId String
  654. organizationId String
  655. items InspectionItem[]
  656. quoteRequests InspectionQuoteRequest[]
  657. quotes Quote[]
  658. boardAssignments BoardAssignment[]
  659. @@index([vehicleId])
  660. @@index([organizationId])
  661. @@map("inspections")
  662. }
  663. model InspectionItem {
  664. id String @id @default(cuid())
  665. name String
  666. section String
  667. sortOrder Int @default(0)
  668. condition String @default("not_inspected")
  669. notes String?
  670. imageUrls String[] @default([])
  671. inspectionId String
  672. inspection Inspection @relation(fields: [inspectionId], references: [id], onDelete: Cascade)
  673. @@index([inspectionId])
  674. @@map("inspection_items")
  675. }
  676. model InspectionQuoteRequest {
  677. id String @id @default(cuid())
  678. status String @default("pending")
  679. message String?
  680. selectedItemIds String[]
  681. createdAt DateTime @default(now())
  682. inspectionId String
  683. inspection Inspection @relation(fields: [inspectionId], references: [id], onDelete: Cascade)
  684. organizationId String
  685. @@index([organizationId])
  686. @@index([inspectionId])
  687. @@map("inspection_quote_requests")
  688. }
  689. model Notification {
  690. id String @id @default(cuid())
  691. type String
  692. title String
  693. message String
  694. entityType String
  695. entityId String
  696. entityUrl String
  697. read Boolean @default(false)
  698. organizationId String
  699. createdAt DateTime @default(now())
  700. @@index([organizationId, createdAt(sort: Desc)])
  701. @@index([organizationId, read])
  702. @@map("notifications")
  703. }
  704. model SmsMessage {
  705. id String @id @default(cuid())
  706. direction String // "inbound" | "outbound"
  707. fromNumber String
  708. toNumber String
  709. body String
  710. status String @default("queued") // queued | sent | delivered | failed | received
  711. providerMsgId String?
  712. errorMessage String?
  713. relatedEntityType String?
  714. relatedEntityId String?
  715. createdAt DateTime @default(now())
  716. updatedAt DateTime @updatedAt
  717. organizationId String
  718. organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
  719. customerId String?
  720. customer Customer? @relation(fields: [customerId], references: [id], onDelete: SetNull)
  721. @@index([organizationId, customerId, createdAt(sort: Desc)])
  722. @@index([organizationId, createdAt(sort: Desc)])
  723. @@index([toNumber, organizationId])
  724. @@map("sms_messages")
  725. }
  726. model Technician {
  727. id String @id @default(cuid())
  728. name String
  729. color String @default("#3b82f6")
  730. isActive Boolean @default(true)
  731. sortOrder Int @default(0)
  732. memberId String?
  733. organizationId String
  734. organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
  735. assignments BoardAssignment[]
  736. createdAt DateTime @default(now())
  737. updatedAt DateTime @updatedAt
  738. @@index([organizationId])
  739. @@map("technicians")
  740. }
  741. model BoardAssignment {
  742. id String @id @default(cuid())
  743. date DateTime @db.Date
  744. sortOrder Int @default(0)
  745. notes String?
  746. technicianId String
  747. technician Technician @relation(fields: [technicianId], references: [id], onDelete: Cascade)
  748. serviceRecordId String?
  749. serviceRecord ServiceRecord? @relation(fields: [serviceRecordId], references: [id], onDelete: Cascade)
  750. inspectionId String?
  751. inspection Inspection? @relation(fields: [inspectionId], references: [id], onDelete: Cascade)
  752. organizationId String
  753. createdAt DateTime @default(now())
  754. updatedAt DateTime @updatedAt
  755. @@unique([serviceRecordId, date])
  756. @@unique([inspectionId, date])
  757. @@index([organizationId, date])
  758. @@index([technicianId, date])
  759. @@map("board_assignments")
  760. }
  761. model CustomerSession {
  762. id String @id @default(cuid())
  763. token String @unique
  764. expiresAt DateTime
  765. createdAt DateTime @default(now())
  766. customerId String
  767. customer Customer @relation(fields: [customerId], references: [id], onDelete: Cascade)
  768. organizationId String
  769. @@index([token])
  770. @@index([customerId, organizationId])
  771. @@map("customer_sessions")
  772. }
  773. model CustomerMagicLink {
  774. id String @id @default(cuid())
  775. token String @unique
  776. email String
  777. organizationId String
  778. expiresAt DateTime
  779. usedAt DateTime?
  780. createdAt DateTime @default(now())
  781. @@index([token])
  782. @@map("customer_magic_links")
  783. }
  784. model ServiceRequest {
  785. id String @id @default(cuid())
  786. description String
  787. preferredDate DateTime?
  788. status String @default("pending")
  789. adminNotes String?
  790. createdAt DateTime @default(now())
  791. updatedAt DateTime @updatedAt
  792. customerId String
  793. customer Customer @relation(fields: [customerId], references: [id], onDelete: Cascade)
  794. vehicleId String
  795. vehicle Vehicle @relation(fields: [vehicleId], references: [id], onDelete: Cascade)
  796. organizationId String
  797. @@index([organizationId, status])
  798. @@index([customerId])
  799. @@map("service_requests")
  800. }
  801. model SyncDeletion {
  802. id String @id @default(cuid())
  803. entity String
  804. recordId String
  805. organizationId String
  806. deletedAt DateTime @default(now())
  807. organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
  808. @@index([organizationId, deletedAt])
  809. @@map("sync_deletions")
  810. }