schema.prisma 33 KB

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