schema.prisma 27 KB

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