Restore the pipeline Quotes tab and wire quote conversion

Featurekamo-internal
Shipped
27 Agosti 2026, 16:59 UTC
Author
kamo
Commit
e2d8e5e

`POST /quotes/{uid}/convert` shipped in SW4 and nothing has ever called it: `quotesApi` had ten methods and `convert` was not among them, so the endpoint was live and unreachable. This puts a Quotes sub-tab back on the SERVICE pipeline and offers Convert on an ACCEPTED quote. The reason this needed a test before it needed code: `subTabLabels` is a `Record<PipelineSubTabId, string>`, so a missing label is a compile error, but `renderSubContent` decided what to draw with four `if (id === "intents") return X; return Y;` chains, and that trailing `return Y` answers every id nobody thought about. Appending an id therefore put a new tab on screen that silently drew the commitments list, in all four verticals, and typechecked. That is the defect `pipelineSubTabs.ts`'s own header narrates having shipped once already. **************** asserts, against the source, that every `SUB_TAB_IDS` entry is named in every vertical. It was red before any branch changed — on `commitments`, in all four verticals, because the fall-through never names it. Deleting one `case` from the finished code leaves `tsc --noEmit` silent and turns the test red, which is the whole point of it existing. The chains are now switches with no `default`. A vertical with no quotes surface still writes `case "quotes":` beside its commitments case, so the next id added makes someone choose four times instead of inheriting an answer. Only SERVICE lists `quotes` in `SUB_TABS_BY_VERTICAL`, so only SERVICE shows the tab — a Quotes tab on RENTAL that rendered Bookings would be the same silent wrongness in a new place. Strip order still comes from `SUB_TAB_IDS` alone: the new map is a set, and `Tab value` stays the `SUB_TAB_IDS` index so omitting a tab cannot renumber the others. `subIndexForVertical` clamps a `?sub=quotes` link that lands on a vertical without the tab, which MUI would otherwise render as an empty selection over fall-back content. `quotesApi.convert` keeps 409 and 404 apart, because they say different things: 409 is "this quote's state is wrong, and the server's reason is the useful half", 404 is "it is gone from this org". Telling someone a record they are looking at was not found when the truth is "not accepted yet" sends them hunting a data problem that does not exist, so only 404 forces a reload. `QuoteConversion.orderUid` is a `string`. `Order extends BaseEntity`, whose id is an `INT8 DEFAULT unique_rowid()`; the server already declares the field as a String, and the Next proxy reparses the body in Node one hop upstream of the browser, so a bare int64 would round before any client code could see it. `total` is typed `string | number` and read through `Number()`: it is a `BigDecimal`, and `HttpWireJacksonConfig` stringifies `Long` only. Convert is offered on ACCEPTED and nothing else; accept and decoder stay on the customer's tokenised page, where the acceptance an order rests on is actually given. Production holds 15 quotes, all DRAFT, so the button is currently visible on none of them.

All changes

Je, unaona nini kuhusu usafiri?

Kila moja ya hizi updates ardhi katika nafasi yako ya kazi moja kwa moja. Kuanza bure na kuangalia kukua wiki baada ya wiki.

Kuwa Huru MileleMtazamo wa bei