schema.prisma 34 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195
  1. generator client {
  2. provider = "prisma-client"
  3. output = "../src/generated/prisma"
  4. }
  5. datasource db {
  6. provider = "postgresql"
  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. findings VehicleFinding[]
  149. @@index([organizationId])
  150. @@map("vehicles")
  151. }
  152. model ServiceRecord {
  153. id String @id @default(cuid())
  154. title String
  155. description String?
  156. type String @default("maintenance")
  157. status String @default("completed")
  158. cost Float @default(0)
  159. mileage Int?
  160. serviceDate DateTime @default(now())
  161. startDateTime DateTime?
  162. endDateTime DateTime?
  163. shopName String?
  164. techName String?
  165. parts String?
  166. laborHours Float?
  167. diagnosticNotes String?
  168. invoiceNotes String?
  169. subtotal Float @default(0)
  170. taxRate Float @default(0)
  171. taxAmount Float @default(0)
  172. totalAmount Float @default(0)
  173. invoiceNumber String?
  174. invoiceDate DateTime?
  175. invoiceDueDate DateTime?
  176. discountType String?
  177. discountValue Float @default(0)
  178. discountAmount Float @default(0)
  179. manuallyPaid Boolean @default(false)
  180. publicToken String? @unique
  181. sharedAt DateTime?
  182. viewCount Int @default(0)
  183. lastViewedAt DateTime?
  184. createdAt DateTime @default(now())
  185. updatedAt DateTime @updatedAt
  186. vehicleId String
  187. vehicle Vehicle @relation(fields: [vehicleId], references: [id], onDelete: Cascade)
  188. technicianId String?
  189. technician Technician? @relation(fields: [technicianId], references: [id], onDelete: SetNull)
  190. sortOrder Int @default(0)
  191. partItems ServicePart[]
  192. laborItems ServiceLabor[]
  193. attachments ServiceAttachment[]
  194. payments Payment[]
  195. statusReports StatusReport[]
  196. discoveredFindings VehicleFinding[] @relation("FindingDiscoveredIn")
  197. resolvedFindings VehicleFinding[] @relation("FindingResolvedIn")
  198. @@index([vehicleId])
  199. @@index([technicianId])
  200. @@map("service_records")
  201. }
  202. model Note {
  203. id String @id @default(cuid())
  204. title String
  205. content String
  206. isPinned Boolean @default(false)
  207. createdAt DateTime @default(now())
  208. updatedAt DateTime @updatedAt
  209. vehicleId String
  210. vehicle Vehicle @relation(fields: [vehicleId], references: [id], onDelete: Cascade)
  211. @@index([vehicleId])
  212. @@map("notes")
  213. }
  214. model FuelLog {
  215. id String @id @default(cuid())
  216. date DateTime @default(now())
  217. mileage Int
  218. gallons Float
  219. pricePerGallon Float
  220. totalCost Float
  221. isFillUp Boolean @default(true)
  222. station String?
  223. notes String?
  224. createdAt DateTime @default(now())
  225. updatedAt DateTime @updatedAt
  226. vehicleId String
  227. vehicle Vehicle @relation(fields: [vehicleId], references: [id], onDelete: Cascade)
  228. @@index([vehicleId])
  229. @@map("fuel_logs")
  230. }
  231. model Reminder {
  232. id String @id @default(cuid())
  233. title String
  234. description String?
  235. dueDate DateTime?
  236. dueMileage Int?
  237. isCompleted Boolean @default(false)
  238. createdAt DateTime @default(now())
  239. updatedAt DateTime @updatedAt
  240. vehicleId String
  241. vehicle Vehicle @relation(fields: [vehicleId], references: [id], onDelete: Cascade)
  242. @@index([vehicleId])
  243. @@map("reminders")
  244. }
  245. model VehicleFinding {
  246. id String @id @default(cuid())
  247. description String
  248. severity String @default("needs_work")
  249. status String @default("open")
  250. notes String?
  251. imageUrls String[] @default([])
  252. createdAt DateTime @default(now())
  253. updatedAt DateTime @updatedAt
  254. vehicleId String
  255. vehicle Vehicle @relation(fields: [vehicleId], references: [id], onDelete: Cascade)
  256. serviceRecordId String?
  257. serviceRecord ServiceRecord? @relation("FindingDiscoveredIn", fields: [serviceRecordId], references: [id], onDelete: SetNull)
  258. resolvedServiceRecordId String?
  259. resolvedServiceRecord ServiceRecord? @relation("FindingResolvedIn", fields: [resolvedServiceRecordId], references: [id], onDelete: SetNull)
  260. @@index([vehicleId])
  261. @@index([serviceRecordId])
  262. @@map("vehicle_findings")
  263. }
  264. model Customer {
  265. id String @id @default(cuid())
  266. name String
  267. email String?
  268. phone String?
  269. address String?
  270. company String?
  271. notes String?
  272. telegramChatId String?
  273. createdAt DateTime @default(now())
  274. updatedAt DateTime @updatedAt
  275. userId String
  276. user User @relation(fields: [userId], references: [id], onDelete: Cascade)
  277. organizationId String?
  278. organization Organization? @relation(fields: [organizationId], references: [id], onDelete: Cascade)
  279. vehicles Vehicle[]
  280. quotes Quote[]
  281. smsMessages SmsMessage[]
  282. telegramMessages TelegramMessage[]
  283. customerSessions CustomerSession[]
  284. serviceRequests ServiceRequest[]
  285. @@index([organizationId])
  286. @@map("customers")
  287. }
  288. model Payment {
  289. id String @id @default(cuid())
  290. amount Float
  291. date DateTime @default(now())
  292. method String @default("other")
  293. note String?
  294. provider String?
  295. externalId String?
  296. createdAt DateTime @default(now())
  297. updatedAt DateTime @updatedAt
  298. serviceRecordId String
  299. serviceRecord ServiceRecord @relation(fields: [serviceRecordId], references: [id], onDelete: Cascade)
  300. @@index([serviceRecordId])
  301. @@map("payments")
  302. }
  303. model ServiceAttachment {
  304. id String @id @default(cuid())
  305. fileName String
  306. fileUrl String
  307. fileType String
  308. fileSize Int
  309. category String @default("diagnostic")
  310. description String?
  311. includeInInvoice Boolean @default(true)
  312. createdAt DateTime @default(now())
  313. serviceRecordId String
  314. serviceRecord ServiceRecord @relation(fields: [serviceRecordId], references: [id], onDelete: Cascade)
  315. @@index([serviceRecordId])
  316. @@map("service_attachments")
  317. }
  318. model ServicePart {
  319. id String @id @default(cuid())
  320. partNumber String?
  321. name String
  322. quantity Float @default(1)
  323. unitPrice Float @default(0)
  324. total Float @default(0)
  325. unitCost Float @default(0)
  326. inventoryPartId String?
  327. serviceRecordId String
  328. serviceRecord ServiceRecord @relation(fields: [serviceRecordId], references: [id], onDelete: Cascade)
  329. @@index([serviceRecordId])
  330. @@map("service_parts")
  331. }
  332. model ServiceLabor {
  333. id String @id @default(cuid())
  334. description String
  335. hours Float @default(0)
  336. rate Float @default(0)
  337. total Float @default(0)
  338. pricingType String @default("hourly")
  339. serviceRecordId String
  340. serviceRecord ServiceRecord @relation(fields: [serviceRecordId], references: [id], onDelete: Cascade)
  341. @@index([serviceRecordId])
  342. @@map("service_labor")
  343. }
  344. model LaborPreset {
  345. id String @id @default(cuid())
  346. name String
  347. description String?
  348. isArchived Boolean @default(false)
  349. createdAt DateTime @default(now())
  350. updatedAt DateTime @updatedAt
  351. userId String
  352. organizationId String
  353. organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
  354. items LaborPresetItem[]
  355. @@index([organizationId])
  356. @@map("labor_presets")
  357. }
  358. model LaborPresetItem {
  359. id String @id @default(cuid())
  360. description String
  361. hours Float @default(0)
  362. rate Float @default(0)
  363. pricingType String @default("hourly")
  364. sortOrder Int @default(0)
  365. presetId String
  366. preset LaborPreset @relation(fields: [presetId], references: [id], onDelete: Cascade)
  367. @@index([presetId])
  368. @@map("labor_preset_items")
  369. }
  370. model InventoryPart {
  371. id String @id @default(cuid())
  372. partNumber String?
  373. barcode String?
  374. name String
  375. description String?
  376. category String?
  377. quantity Int @default(0)
  378. minQuantity Int @default(0)
  379. unitCost Float @default(0)
  380. sellPrice Float @default(0)
  381. supplier String?
  382. supplierPhone String?
  383. supplierEmail String?
  384. supplierUrl String?
  385. imageUrl String?
  386. location String?
  387. isArchived Boolean @default(false)
  388. createdAt DateTime @default(now())
  389. updatedAt DateTime @updatedAt
  390. userId String
  391. user User @relation(fields: [userId], references: [id], onDelete: Cascade)
  392. organizationId String?
  393. organization Organization? @relation(fields: [organizationId], references: [id], onDelete: Cascade)
  394. gallery StoredImage[] @relation("InventoryPartImages")
  395. @@index([organizationId])
  396. @@map("inventory_parts")
  397. }
  398. model StoredImage {
  399. id String @id @default(cuid())
  400. url String
  401. fileName String?
  402. description String?
  403. sortOrder Int @default(0)
  404. createdAt DateTime @default(now())
  405. inventoryPartId String?
  406. inventoryPart InventoryPart? @relation("InventoryPartImages", fields: [inventoryPartId], references: [id], onDelete: Cascade)
  407. @@index([inventoryPartId])
  408. @@map("stored_images")
  409. }
  410. model Quote {
  411. id String @id @default(cuid())
  412. quoteNumber String?
  413. title String
  414. description String?
  415. status String @default("draft")
  416. validUntil DateTime?
  417. subtotal Float @default(0)
  418. taxRate Float @default(0)
  419. taxAmount Float @default(0)
  420. discountType String?
  421. discountValue Float @default(0)
  422. discountAmount Float @default(0)
  423. totalAmount Float @default(0)
  424. notes String?
  425. publicToken String?
  426. sharedAt DateTime?
  427. viewCount Int @default(0)
  428. lastViewedAt DateTime?
  429. customerMessage String?
  430. convertedToId String?
  431. createdAt DateTime @default(now())
  432. updatedAt DateTime @updatedAt
  433. userId String
  434. user User @relation(fields: [userId], references: [id], onDelete: Cascade)
  435. organizationId String?
  436. organization Organization? @relation(fields: [organizationId], references: [id], onDelete: Cascade)
  437. customerId String?
  438. customer Customer? @relation(fields: [customerId], references: [id], onDelete: SetNull)
  439. vehicleId String?
  440. vehicle Vehicle? @relation(fields: [vehicleId], references: [id], onDelete: SetNull)
  441. inspectionId String?
  442. inspection Inspection? @relation(fields: [inspectionId], references: [id], onDelete: SetNull)
  443. partItems QuotePart[]
  444. laborItems QuoteLabor[]
  445. attachments QuoteAttachment[]
  446. @@index([organizationId])
  447. @@map("quotes")
  448. }
  449. model QuoteAttachment {
  450. id String @id @default(cuid())
  451. fileName String
  452. fileUrl String
  453. fileType String
  454. fileSize Int
  455. category String @default("image")
  456. description String?
  457. includeInInvoice Boolean @default(true)
  458. createdAt DateTime @default(now())
  459. quoteId String
  460. quote Quote @relation(fields: [quoteId], references: [id], onDelete: Cascade)
  461. @@index([quoteId])
  462. @@map("quote_attachments")
  463. }
  464. model QuotePart {
  465. id String @id @default(cuid())
  466. partNumber String?
  467. name String
  468. quantity Float @default(1)
  469. unitPrice Float @default(0)
  470. total Float @default(0)
  471. excluded Boolean @default(false)
  472. quoteId String
  473. quote Quote @relation(fields: [quoteId], references: [id], onDelete: Cascade)
  474. @@index([quoteId])
  475. @@map("quote_parts")
  476. }
  477. model QuoteLabor {
  478. id String @id @default(cuid())
  479. description String
  480. hours Float @default(0)
  481. rate Float @default(0)
  482. total Float @default(0)
  483. pricingType String @default("hourly")
  484. excluded Boolean @default(false)
  485. quoteId String
  486. quote Quote @relation(fields: [quoteId], references: [id], onDelete: Cascade)
  487. @@index([quoteId])
  488. @@map("quote_labor")
  489. }
  490. model CustomFieldDefinition {
  491. id String @id @default(cuid())
  492. name String
  493. label String
  494. fieldType String @default("text")
  495. options String?
  496. required Boolean @default(false)
  497. entityType String
  498. sortOrder Int @default(0)
  499. isActive Boolean @default(true)
  500. createdAt DateTime @default(now())
  501. updatedAt DateTime @updatedAt
  502. userId String
  503. user User @relation(fields: [userId], references: [id], onDelete: Cascade)
  504. organizationId String?
  505. organization Organization? @relation(fields: [organizationId], references: [id], onDelete: Cascade)
  506. values CustomFieldValue[]
  507. @@unique([organizationId, name, entityType])
  508. @@index([organizationId])
  509. @@map("custom_field_definitions")
  510. }
  511. model CustomFieldValue {
  512. id String @id @default(cuid())
  513. value String
  514. entityId String
  515. entityType String
  516. fieldId String
  517. field CustomFieldDefinition @relation(fields: [fieldId], references: [id], onDelete: Cascade)
  518. @@unique([fieldId, entityId])
  519. @@index([fieldId])
  520. @@map("custom_field_values")
  521. }
  522. model Organization {
  523. id String @id @default(cuid())
  524. name String
  525. portalSlug String? @unique
  526. createdAt DateTime @default(now())
  527. updatedAt DateTime @updatedAt
  528. members OrganizationMember[]
  529. roles Role[]
  530. vehicles Vehicle[]
  531. customers Customer[]
  532. quotes Quote[]
  533. inventoryParts InventoryPart[]
  534. laborPresets LaborPreset[]
  535. customFieldDefinitions CustomFieldDefinition[]
  536. settings AppSetting[]
  537. subscription Subscription?
  538. invitations TeamInvitation[]
  539. inspectionTemplates InspectionTemplate[]
  540. smsMessages SmsMessage[]
  541. telegramMessages TelegramMessage[]
  542. technicians Technician[]
  543. auditLogs AuditLog[]
  544. statusReports StatusReport[]
  545. reportSchedules ReportSchedule[]
  546. @@map("organizations")
  547. }
  548. model OrganizationMember {
  549. id String @id @default(cuid())
  550. role String @default("member")
  551. userId String
  552. user User @relation(fields: [userId], references: [id], onDelete: Cascade)
  553. organizationId String
  554. organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
  555. roleId String?
  556. customRole Role? @relation(fields: [roleId], references: [id], onDelete: SetNull)
  557. @@unique([userId, organizationId])
  558. @@map("organization_members")
  559. }
  560. model Role {
  561. id String @id @default(cuid())
  562. name String
  563. isAdmin Boolean @default(false)
  564. createdAt DateTime @default(now())
  565. updatedAt DateTime @updatedAt
  566. organizationId String
  567. organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
  568. permissions Permission[]
  569. members OrganizationMember[]
  570. teamInvitations TeamInvitation[]
  571. @@unique([organizationId, name])
  572. @@map("roles")
  573. }
  574. model Permission {
  575. id String @id @default(cuid())
  576. action String
  577. subject String
  578. roleId String
  579. role Role @relation(fields: [roleId], references: [id], onDelete: Cascade)
  580. @@unique([roleId, action, subject])
  581. @@index([roleId])
  582. @@map("permissions")
  583. }
  584. model SystemSetting {
  585. id String @id @default(cuid())
  586. key String @unique
  587. value String
  588. @@map("system_settings")
  589. }
  590. model TeamInvitation {
  591. id String @id @default(cuid())
  592. email String
  593. role String @default("member")
  594. token String @unique
  595. status String @default("pending") // pending | accepted | cancelled
  596. expiresAt DateTime
  597. createdAt DateTime @default(now())
  598. organizationId String
  599. organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
  600. invitedById String
  601. roleId String?
  602. customRole Role? @relation(fields: [roleId], references: [id], onDelete: SetNull)
  603. @@unique([email, organizationId])
  604. @@index([token])
  605. @@map("team_invitations")
  606. }
  607. // Stripe-ready models (implementation in follow-up)
  608. model SubscriptionPlan {
  609. id String @id @default(cuid())
  610. name String
  611. stripePriceId String? @unique
  612. price Float @default(0)
  613. interval String @default("month") // "month" | "year"
  614. maxMembers Int @default(1)
  615. features String? // JSON feature flags
  616. isActive Boolean @default(true)
  617. createdAt DateTime @default(now())
  618. updatedAt DateTime @updatedAt
  619. subscriptions Subscription[]
  620. @@map("subscription_plans")
  621. }
  622. model Subscription {
  623. id String @id @default(cuid())
  624. status String @default("active") // active | canceled | past_due | trialing
  625. stripeSubscriptionId String? @unique
  626. stripeCustomerId String?
  627. currentPeriodStart DateTime?
  628. currentPeriodEnd DateTime?
  629. cancelAtPeriodEnd Boolean @default(false)
  630. createdAt DateTime @default(now())
  631. updatedAt DateTime @updatedAt
  632. organizationId String @unique
  633. organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
  634. planId String
  635. plan SubscriptionPlan @relation(fields: [planId], references: [id])
  636. @@map("subscriptions")
  637. }
  638. model RecurringInvoice {
  639. id String @id @default(cuid())
  640. title String
  641. description String?
  642. frequency String // "weekly" | "biweekly" | "monthly" | "quarterly" | "yearly"
  643. nextRunDate DateTime
  644. endDate DateTime?
  645. isActive Boolean @default(true)
  646. lastRunAt DateTime?
  647. runCount Int @default(0)
  648. // Template fields (copied to ServiceRecord on generation)
  649. type String @default("maintenance")
  650. cost Float @default(0)
  651. taxRate Float @default(0)
  652. invoiceNotes String?
  653. vehicleId String
  654. vehicle Vehicle @relation(fields: [vehicleId], references: [id], onDelete: Cascade)
  655. createdAt DateTime @default(now())
  656. updatedAt DateTime @updatedAt
  657. templateParts RecurringPart[]
  658. templateLabor RecurringLabor[]
  659. @@index([vehicleId])
  660. @@index([nextRunDate, isActive])
  661. @@map("recurring_invoices")
  662. }
  663. model RecurringPart {
  664. id String @id @default(cuid())
  665. name String
  666. partNumber String?
  667. quantity Int @default(1)
  668. unitPrice Float @default(0)
  669. recurringInvoiceId String
  670. recurringInvoice RecurringInvoice @relation(fields: [recurringInvoiceId], references: [id], onDelete: Cascade)
  671. @@map("recurring_parts")
  672. }
  673. model RecurringLabor {
  674. id String @id @default(cuid())
  675. description String
  676. hours Float @default(0)
  677. rate Float @default(0)
  678. pricingType String @default("hourly")
  679. recurringInvoiceId String
  680. recurringInvoice RecurringInvoice @relation(fields: [recurringInvoiceId], references: [id], onDelete: Cascade)
  681. @@map("recurring_labor")
  682. }
  683. model InspectionTemplate {
  684. id String @id @default(cuid())
  685. name String
  686. description String?
  687. isDefault Boolean @default(false)
  688. createdAt DateTime @default(now())
  689. updatedAt DateTime @updatedAt
  690. organizationId String
  691. organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
  692. sections InspectionTemplateSection[]
  693. inspections Inspection[]
  694. @@index([organizationId])
  695. @@map("inspection_templates")
  696. }
  697. model InspectionTemplateSection {
  698. id String @id @default(cuid())
  699. name String
  700. sortOrder Int @default(0)
  701. templateId String
  702. template InspectionTemplate @relation(fields: [templateId], references: [id], onDelete: Cascade)
  703. items InspectionTemplateItem[]
  704. @@index([templateId])
  705. @@map("inspection_template_sections")
  706. }
  707. model InspectionTemplateItem {
  708. id String @id @default(cuid())
  709. name String
  710. sortOrder Int @default(0)
  711. sectionId String
  712. section InspectionTemplateSection @relation(fields: [sectionId], references: [id], onDelete: Cascade)
  713. @@index([sectionId])
  714. @@map("inspection_template_items")
  715. }
  716. model Inspection {
  717. id String @id @default(cuid())
  718. status String @default("in_progress")
  719. mileage Int?
  720. notes String?
  721. startDateTime DateTime?
  722. endDateTime DateTime?
  723. publicToken String? @unique
  724. completedAt DateTime?
  725. createdAt DateTime @default(now())
  726. updatedAt DateTime @updatedAt
  727. vehicleId String
  728. vehicle Vehicle @relation(fields: [vehicleId], references: [id], onDelete: Cascade)
  729. templateId String
  730. template InspectionTemplate @relation(fields: [templateId], references: [id])
  731. technicianId String?
  732. technician Technician? @relation(fields: [technicianId], references: [id], onDelete: SetNull)
  733. sortOrder Int @default(0)
  734. organizationId String
  735. items InspectionItem[]
  736. quoteRequests InspectionQuoteRequest[]
  737. quotes Quote[]
  738. @@index([vehicleId])
  739. @@index([organizationId])
  740. @@index([technicianId])
  741. @@map("inspections")
  742. }
  743. model InspectionItem {
  744. id String @id @default(cuid())
  745. name String
  746. section String
  747. sortOrder Int @default(0)
  748. condition String @default("not_inspected")
  749. notes String?
  750. imageUrls String[] @default([])
  751. inspectionId String
  752. inspection Inspection @relation(fields: [inspectionId], references: [id], onDelete: Cascade)
  753. @@index([inspectionId])
  754. @@map("inspection_items")
  755. }
  756. model InspectionQuoteRequest {
  757. id String @id @default(cuid())
  758. status String @default("pending")
  759. message String?
  760. selectedItemIds String[]
  761. createdAt DateTime @default(now())
  762. inspectionId String
  763. inspection Inspection @relation(fields: [inspectionId], references: [id], onDelete: Cascade)
  764. organizationId String
  765. @@index([organizationId])
  766. @@index([inspectionId])
  767. @@map("inspection_quote_requests")
  768. }
  769. model Notification {
  770. id String @id @default(cuid())
  771. type String
  772. title String
  773. message String
  774. entityType String
  775. entityId String
  776. entityUrl String
  777. read Boolean @default(false)
  778. organizationId String
  779. createdAt DateTime @default(now())
  780. @@index([organizationId, createdAt(sort: Desc)])
  781. @@index([organizationId, read])
  782. @@map("notifications")
  783. }
  784. model SmsMessage {
  785. id String @id @default(cuid())
  786. direction String // "inbound" | "outbound"
  787. fromNumber String
  788. toNumber String
  789. body String
  790. status String @default("queued") // queued | sent | delivered | failed | received
  791. providerMsgId String?
  792. errorMessage String?
  793. relatedEntityType String?
  794. relatedEntityId String?
  795. createdAt DateTime @default(now())
  796. updatedAt DateTime @updatedAt
  797. organizationId String
  798. organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
  799. customerId String?
  800. customer Customer? @relation(fields: [customerId], references: [id], onDelete: SetNull)
  801. @@index([organizationId, customerId, createdAt(sort: Desc)])
  802. @@index([organizationId, createdAt(sort: Desc)])
  803. @@index([toNumber, organizationId])
  804. @@map("sms_messages")
  805. }
  806. model TelegramMessage {
  807. id String @id @default(cuid())
  808. direction String // "inbound" | "outbound"
  809. chatId String // Telegram chat ID
  810. body String
  811. status String @default("queued") // queued | sent | delivered | failed | received
  812. telegramMessageId String? // Telegram's message ID for tracking
  813. errorMessage String?
  814. relatedEntityType String?
  815. relatedEntityId String?
  816. createdAt DateTime @default(now())
  817. updatedAt DateTime @updatedAt
  818. organizationId String
  819. organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
  820. customerId String?
  821. customer Customer? @relation(fields: [customerId], references: [id], onDelete: SetNull)
  822. @@index([organizationId, customerId, createdAt(sort: Desc)])
  823. @@index([organizationId, chatId, createdAt(sort: Desc)])
  824. @@map("telegram_messages")
  825. }
  826. model Technician {
  827. id String @id @default(cuid())
  828. name String
  829. color String @default("#3b82f6")
  830. isActive Boolean @default(true)
  831. sortOrder Int @default(0)
  832. dailyCapacity Int @default(480)
  833. userId String?
  834. user User? @relation(fields: [userId], references: [id], onDelete: SetNull)
  835. organizationId String
  836. organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
  837. serviceRecords ServiceRecord[]
  838. inspections Inspection[]
  839. statusReports StatusReport[]
  840. createdAt DateTime @default(now())
  841. updatedAt DateTime @updatedAt
  842. @@index([organizationId])
  843. @@map("technicians")
  844. }
  845. model AuditLog {
  846. id String @id @default(cuid())
  847. timestamp DateTime @default(now())
  848. action String
  849. entity String?
  850. entityId String?
  851. message String?
  852. metadata Json?
  853. ip String?
  854. userAgent String?
  855. userId String?
  856. user User? @relation(fields: [userId], references: [id], onDelete: SetNull)
  857. organizationId String?
  858. organization Organization? @relation(fields: [organizationId], references: [id], onDelete: SetNull)
  859. @@index([organizationId, timestamp(sort: Desc)])
  860. @@index([userId, timestamp(sort: Desc)])
  861. @@index([action, timestamp(sort: Desc)])
  862. @@map("audit_logs")
  863. }
  864. model CustomerSession {
  865. id String @id @default(cuid())
  866. token String @unique
  867. expiresAt DateTime
  868. createdAt DateTime @default(now())
  869. customerId String
  870. customer Customer @relation(fields: [customerId], references: [id], onDelete: Cascade)
  871. organizationId String
  872. @@index([token])
  873. @@index([customerId, organizationId])
  874. @@map("customer_sessions")
  875. }
  876. model CustomerMagicLink {
  877. id String @id @default(cuid())
  878. token String @unique
  879. email String
  880. organizationId String
  881. expiresAt DateTime
  882. usedAt DateTime?
  883. createdAt DateTime @default(now())
  884. @@index([token])
  885. @@map("customer_magic_links")
  886. }
  887. model ServiceRequest {
  888. id String @id @default(cuid())
  889. description String
  890. preferredDate DateTime?
  891. status String @default("pending")
  892. adminNotes String?
  893. createdAt DateTime @default(now())
  894. updatedAt DateTime @updatedAt
  895. customerId String
  896. customer Customer @relation(fields: [customerId], references: [id], onDelete: Cascade)
  897. vehicleId String
  898. vehicle Vehicle @relation(fields: [vehicleId], references: [id], onDelete: Cascade)
  899. organizationId String
  900. @@index([organizationId, status])
  901. @@index([customerId])
  902. @@map("service_requests")
  903. }
  904. model AiGeneratedMessage {
  905. id String @id @default(cuid())
  906. vehicleId String
  907. type String // "summary" | "common_issues" | etc.
  908. content String
  909. createdAt DateTime @default(now())
  910. updatedAt DateTime @updatedAt
  911. vehicle Vehicle @relation(fields: [vehicleId], references: [id], onDelete: Cascade)
  912. @@unique([vehicleId, type])
  913. @@map("ai_generated_messages")
  914. }
  915. model AiChat {
  916. id String @id @default(cuid())
  917. title String @default("New chat")
  918. createdAt DateTime @default(now())
  919. updatedAt DateTime @updatedAt
  920. userId String
  921. organizationId String
  922. messages AiChatMessage[]
  923. @@index([organizationId, userId, updatedAt(sort: Desc)])
  924. @@map("ai_chats")
  925. }
  926. model AiChatMessage {
  927. id String @id @default(cuid())
  928. role String // "user" | "assistant"
  929. content String
  930. createdAt DateTime @default(now())
  931. chatId String
  932. chat AiChat @relation(fields: [chatId], references: [id], onDelete: Cascade)
  933. @@index([chatId, createdAt])
  934. @@map("ai_chat_messages")
  935. }
  936. model StatusReport {
  937. id String @id @default(cuid())
  938. title String?
  939. message String? @db.Text
  940. videoUrl String?
  941. videoFileName String?
  942. publicToken String @unique
  943. status String @default("draft") // draft | published | sent | viewed
  944. sentVia String? // email, sms, telegram (comma-separated)
  945. sentAt DateTime?
  946. viewedAt DateTime?
  947. customerFeedback String? @db.Text
  948. feedbackAt DateTime?
  949. expiresAt DateTime?
  950. createdAt DateTime @default(now())
  951. updatedAt DateTime @updatedAt
  952. serviceRecordId String
  953. serviceRecord ServiceRecord @relation(fields: [serviceRecordId], references: [id], onDelete: Cascade)
  954. organizationId String
  955. organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
  956. technicianId String?
  957. technician Technician? @relation(fields: [technicianId], references: [id], onDelete: SetNull)
  958. @@index([serviceRecordId])
  959. @@index([organizationId])
  960. @@index([publicToken])
  961. @@index([expiresAt])
  962. @@map("status_reports")
  963. }
  964. model ReportSchedule {
  965. id String @id @default(cuid())
  966. name String @default("Scheduled Report")
  967. frequency String // "daily" | "weekly" | "monthly" etc.
  968. dateRange String @default("last30d") // "last1d" | "last7d" | "last30d" | "ytd" | "allTime" etc.
  969. sections String // JSON array of section keys
  970. recipients String // JSON array of user IDs
  971. nextRunDate DateTime
  972. endDate DateTime?
  973. isActive Boolean @default(true)
  974. lastRunAt DateTime?
  975. runCount Int @default(0)
  976. createdAt DateTime @default(now())
  977. updatedAt DateTime @updatedAt
  978. organizationId String
  979. organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
  980. createdById String
  981. @@index([organizationId])
  982. @@index([nextRunDate, isActive])
  983. @@map("report_schedules")
  984. }