Skip to main content
All posts
May 23, 2025Β·2 min readΒ·Diogo Hudson

Price on a quote is a snapshot, not a query

Catalog prices change. Quotes you already sent shouldn't. A one-line architectural decision that prevents months of disputes.

Price on a quote is a snapshot, not a query

A common bug in homemade quoting systems: the PDF shows the price at the moment the customer opens it. You adjust the catalog on Tuesday, the customer opens the quote on Thursday, the printed price is wrong, the customer is angry, and nobody can explain what happened.

This bug is insidious because it doesn't look like a bug. The system is working correctly β€” it's showing the current price. The problem is that 'current price' is the wrong answer for a document that was created last week. The quote is a <a href='/pricing' class='text-[var(--color-accent)] underline'>price snapshot</a> of a commercial agreement at a point in time. The catalog is a living document. Confusing the two is a category error, and it's one that costs <a href='/docs/product/finance' class='text-[var(--color-accent)] underline'>real money</a> when a customer holds you to a quote you can't honor at the original price.

Snapshot at line creation Every QuoteItem carries a unit_price that is captured from the Product at line creation time. Catalog updates never retroactively rewrite history. D2 and D11 in the decision log are explicit: snapshot is the contract.

The implementation is straightforward: when a quote line is created, the service reads the product's current unit_price and writes it into QuoteItem.unit_price. After that moment, the two values are independent. The product price can change a hundred times β€” the quote line keeps the price that was current when the line was created. If the commercial person wants to use the updated price, they must explicitly re-price the line.

Why not just re-query? Re-querying means every reload shows a subtly different total. It means your printed PDF and the database disagree. It means two different customers can end up with different prices for the same historical quote. The snapshot removes all of these.

The legal dimension makes this non-negotiable. In most jurisdictions, a quote is an offer. Once accepted, it becomes a contract at the quoted price. If your system silently changes the price between when you sent the quote and when the customer accepted it, you're either eating the difference or arguing with a customer who has a PDF with a different number. Neither outcome is good for business.

Discounts live on the line too discount_pct is per-line and captured at the same time. line_total is a computed property β€” quantity Γ— unit_price Γ— (1 βˆ’ discount_pct / 100). The entire math is reproducible from the row.

Because every input to the line total is stored on the row, the total is deterministic. Two different people looking at the same quote, six months apart, will see the same numbers. An auditor can recompute every line from the stored fields and verify that the total matches. There's no hidden formula, no dynamic lookup, no reference data that might have changed since the quote was created. The row is self-contained and self-proving.

How a quote moves from draft to delivery.

All posts
Short pieces on quoting, inventory, AI, and how small distributors ship a lot of stuff without the fuss.