schema.prisma 25 KB

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