The six work-order tabs persist

Featurekamo-internal
Shipped
August 27, 2026 at 10:13 AM UTC
Author
kamo
Commit
ad1c391

SW6b Task 4. Labour, parts, time, vendors, sign-off and notes were React `useState` on `commerce/service/[id]` and lost everything on reload. They now write to the endpoints SW6b Task 3 landed. The client mirrors the three breaking changes in `ServiceJobApi.java`: * `JobInput` loses `labourTotal`, `materialTotal`, `total`, `signedOff` and `signoffAt`. The totals are recomputed server-side from the job's lines on every line write, and sign-off goes through `PATCH /{uid}/signoff`, which stamps its own clock. Leaving them on the general update was a second door onto five columns the server owns, and the second door is the one nobody checks. * `JobView` gains `lines`, `notes` and `vendors`. **Null means "not loaded", empty means "none"** — `listJobs` leaves all three null so the grid does not run three queries per row, and every tab tells the two apart rather than drawing "no lines" over a job that has twelve. * The proxy exports all five verbs. POST and DELETE were deliberately absent while a job had no children; a line and a vendor engagement are created and removed here, so both are mapped now. **************** asserts the set in both directions and was widened with them. Every mutation answers with the whole detail `JobView` and each handler hands that straight to `adopt`. Nothing merges a write's effect by hand: the three totals are the server's and no client arithmetic reproduces them. `adopt` deliberately does NOT re-seed the sign-off draft — SW6a's page re-seeded every local field from each response, so adding a line silently discarded whatever the engineer had typed into the completion write-up. Only the load and a successful sign-off save re-seed it. The notes tab is ported from the markup salvaged out of the dead work-order dialog, reusing its five existing keys. Two departures, both recorded in the code: the note author is an id and the tab says "You" for the current member and omits the author line for anyone else, because no endpoint resolves a batch of member ids to names and a bare 19-digit number under a heading that says "author" helps nobody; and the salvage's `.reverse()` is dropped, because the server already answers newest-first and reversing that puts the oldest on top. `commerceVendorDirectory.ts` is new and exists because `posApi.getVendors()` cannot be used for the vendor picker: `POSController.getVendors` answers with the `Vendor` entity, whose `uid` is a ~19-digit `unique_rowid()` written as a bare JSON number, so `JSON.parse` rounds it before any component sees it and `posApi.Vendor` duly declares `uid: number`. Engaging a vendor with a rounded id names a row that does not exist. This module reads the response as text and quotes long `"uid"` values before parsing. The narrow fix is a DTO that sends the id as a string, as `ServiceJobApi` does; until that exists this is the honest read. Dictionary landed first: kamo-translation-dictionary 0eaab740.

All changes

Like what you see shipping?

Every one of these updates lands in your workspace automatically. Start free and watch it grow week after week.

Start Free ForeverView Pricing