schema.prisma 34 KB

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