A number you can replay
Drafts change, published versions don't, and a quote freezes every price and rate it used. So any number can be traced back to what produced it, and a number sent to someone never changes.
- Project
- STP: Vehicle Cost Platform, part 5 of 10
- Published
- October 5, 2026
- Reading time
- 3 min read
When a margin looks wrong in a review, the first question is "wrong compared to what?". Answering it needs the exact structure, prices and rates the number came from, and most cost tools only keep the latest of each. STP is built so that every number can be traced back to what produced it, and so that a number sent to someone never changes afterwards.
Three kinds of BOM state
- A working BOM is a draft. It's autosaved as one document, and every save increments its revision.
- A published BOM is a frozen copy of a draft at one revision, under a version label (
v1,v2). Its structure never changes, and its status can only go fromRELEASEDtoOBSOLETE. The draft stays editable, to prepare the next version. - A quote is a computation of a published BOM, frozen along with everything it used.
A save that carries an old revision is refused with a 409. That's optimistic concurrency control: nobody waits for a lock to save, but nobody overwrites a version they haven't seen.
Publishing copies the whole structure into relational rows, so it used to be the slowest operation in STP: 8.6 seconds for 20,000 parts, until the inserts were batched and it came down to 2.2.
Why a published BOM holds no prices
Prices move constantly: the controller fetches supplier updates every 5 seconds, and exchange rates change every few seconds to minutes. Structure changes when engineering decides. If a published BOM carried prices, every price update would mean either a new version or a version that quietly stopped matching its own label. So a published BOM holds structure only, and prices are resolved from the current offers each time it's computed.
A quote is a photograph
Creating a quote copies, for every part active in the chosen configuration, its supplier, offer revision, price, currency, quantity and validity, along with every exchange rate and its revision, all in one transaction. A quote is valid until the earliest expiry among its active offers, and an option that isn't selected doesn't shorten it.
After that, nothing touches it. A new price or a new rate applies to new computations and new quotes, never to an existing one. A quote can't even be created while one active part has no valid price, because an incomplete quote would look final while being wrong.
Every line explains itself
Here is one entry of a quote's snapshot, the screw from the reference fixture, with its price in its supplier's currency, followed by the rates the quote used:
{
"component_id": "…",
"supplier_code": "SUPPLIER-01",
"revision": 1,
"price": "2",
"currency": "EUR",
"weight": "0.1",
"carbon": "0.03",
"quantity": "6",
"validated_until": "…"
}
"exchange_rates": { "EUR/USD": { "rate": "1.08", "revision": 8 } }
A live computation returns the same evidence for each part, plus the converted amount and, when a volume is set, the price tier reached. The UI shows the rates under every result ("EUR/USD: 1.08, revision 8").
Scenarios follow the same principle. A "what if" never modifies the BOM it starts from, and a scenario worth keeping becomes a new draft while the original version stays as it was.
One editor at a time
Only one person edits a given BOM at a time. The edit lock lives in PostgreSQL, lasts two minutes and is renewed every 30 seconds, so it survives a backend restart and every backend instance sees the same holder. Merging concurrent edits to a graph is a project of its own, and a lock with a visible holder and an admin takeover covered how BOMs are actually edited, so we kept the limit and listed it as one.