quotes.prisma 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. model Quote {
  2. id String @id @default(cuid())
  3. quoteNumber String?
  4. title String
  5. description String?
  6. status String @default("draft")
  7. validUntil DateTime?
  8. subtotal Float @default(0)
  9. taxRate Float @default(0)
  10. taxAmount Float @default(0)
  11. taxInclusive Boolean @default(false)
  12. discountType String?
  13. discountValue Float @default(0)
  14. discountAmount Float @default(0)
  15. totalAmount Float @default(0)
  16. notes String?
  17. publicToken String?
  18. sharedAt DateTime?
  19. viewCount Int @default(0)
  20. lastViewedAt DateTime?
  21. customerMessage String?
  22. convertedToId String?
  23. createdAt DateTime @default(now())
  24. updatedAt DateTime @updatedAt
  25. /// Most recent time this quote reached the customer, by email or share
  26. /// link. Exists so an unlock is spent when the quote is issued again; the
  27. /// status alone cannot show that, because re-sending an accepted quote does
  28. /// not change it.
  29. sentAt DateTime?
  30. /// See ServiceRecord.editUnlockedAt.
  31. editUnlockedAt DateTime?
  32. editUnlockedById String?
  33. userId String
  34. user User @relation(fields: [userId], references: [id], onDelete: Cascade)
  35. organizationId String?
  36. organization Organization? @relation(fields: [organizationId], references: [id], onDelete: Cascade)
  37. customerId String?
  38. customer Customer? @relation(fields: [customerId], references: [id], onDelete: SetNull)
  39. /// The stored set this quote is about, when it came out of the tire hotel.
  40. /// SetNull rather than Cascade: a quote already sent is a document the
  41. /// customer holds, and deleting the set must not erase it.
  42. tireSetId String?
  43. tireSet TireSet? @relation(fields: [tireSetId], references: [id], onDelete: SetNull)
  44. vehicleId String?
  45. vehicle Vehicle? @relation(fields: [vehicleId], references: [id], onDelete: SetNull)
  46. inspectionId String?
  47. inspection Inspection? @relation(fields: [inspectionId], references: [id], onDelete: SetNull)
  48. partItems QuotePart[]
  49. laborItems QuoteLabor[]
  50. attachments QuoteAttachment[]
  51. @@index([organizationId])
  52. @@map("quotes")
  53. }
  54. model QuoteAttachment {
  55. id String @id @default(cuid())
  56. fileName String
  57. fileUrl String
  58. fileType String
  59. fileSize Int
  60. category String @default("image")
  61. description String?
  62. includeInInvoice Boolean @default(true)
  63. createdAt DateTime @default(now())
  64. quoteId String
  65. quote Quote @relation(fields: [quoteId], references: [id], onDelete: Cascade)
  66. @@index([quoteId])
  67. @@map("quote_attachments")
  68. }
  69. model QuotePart {
  70. id String @id @default(cuid())
  71. partNumber String?
  72. name String
  73. quantity Float @default(1)
  74. /// Unit of measure snapshotted from InventoryPart at pick time; null for
  75. /// free-text lines and legacy rows.
  76. unit String?
  77. /// Internal purchase cost — never shown to customers (quote PDF/share
  78. /// render unitPrice only)
  79. unitCost Float @default(0)
  80. markupPercent Float @default(0)
  81. unitPrice Float @default(0)
  82. total Float @default(0)
  83. excluded Boolean @default(false)
  84. /// Optional link to the stocked part this line was picked from. Carried into
  85. /// ServicePart on conversion so the job deducts the right inventory item.
  86. inventoryPartId String?
  87. quoteId String
  88. quote Quote @relation(fields: [quoteId], references: [id], onDelete: Cascade)
  89. @@index([quoteId])
  90. @@map("quote_parts")
  91. }
  92. model QuoteLabor {
  93. id String @id @default(cuid())
  94. description String
  95. hours Float @default(0)
  96. rate Float @default(0)
  97. total Float @default(0)
  98. pricingType String @default("hourly")
  99. excluded Boolean @default(false)
  100. quoteId String
  101. quote Quote @relation(fields: [quoteId], references: [id], onDelete: Cascade)
  102. @@index([quoteId])
  103. @@map("quote_labor")
  104. }