schema.prisma 31 KB

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