- Shipped
- August 27, 2026 at 8:57 AM UTC
- Author
- Kamo
- Commit
- 93aa00e
Labour and parts become ServiceJobLine rows on the job rather than ServiceLineItem rows, because OrderLineItem.order is optional = false: a ServiceLineItem cannot exist without an Order, and a technician records labour long before anything is billed. Making that parent nullable would let a PurchaseLineItem exist outside an order too, so the mapping test pins the invariant instead. ServiceJobNote is authored and append-only - no updatedAt, createdAt updatable = false and stamped in @PrePersist. ServiceJobVendor is the many-side the Vendors tab has always displayed; role stays free text because no vendor-type vocabulary exists to freeze into the schema. Every repository reaches the org through an EXPLICIT join of the job. The implicit l.job.organization.id renders as an INNER join and silently drops rows whose association is null; ServiceJobLineMappingTest asserts the shape rather than trusting it.