schema.prisma 36 KB

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