A quote is not a cart with a PDF
A retail cart is a suggestion that expires when the session does. A wholesale quote is a commitment: lines, prices, terms, and a deadline someone will hold you to. Shopify's B2B draft-order and quote flows make that distinction easier to ship in 2026 — and easier to blur if you keep modeling the quote as a cart that happens to print.
Carts mutate; quotes version
A cart is allowed to be wrong for a while. Prices refresh, variants go out of stock, the buyer removes a line and adds another. The platform can re-price at checkout because nothing has been promised yet.
A quote is different the moment it is sent. The buyer may route it through purchasing, attach a PO number later, and come back days after the catalog moved. If your system re-reads live variant prices on accept, you have not fulfilled a quote — you have opened a new cart that happens to share SKUs.
So the first modeling rule is boring: when a quote is issued, freeze the commercial facts that matter — line identity, quantity, unit price, currency, payment terms, and expiry — on the quote record itself. Catalog changes after that date belong to a revision or a refusal, not a silent refresh.
Name the states before you build the button
Most quote bugs are missing states dressed up as UI. Draft, sent, accepted, expired, declined, and converted-to-order are not labels for a status chip. They are gates on what mutations are legal.
A draft can still be edited by the rep. A sent quote should refuse casual line edits, or else every edit must mint a new revision with its own id and invalidate the previous share link. An expired quote must not convert. An accepted quote that already produced an order must not produce a second one when someone double-clicks Accept.
We treat this the same way we treat webhook ingestion on Al's Flowers: assume the network and the human will retry. Conversion from quote to order needs an idempotency key tied to the quote revision, not to the click. The second accept returns the first order.
Keep Shopify as the order authority
On Provale Cup and Al's Flowers we keep checkout and the canonical order on Shopify, and put custom work on either side of that seam. Wholesale quoting is one of the cases where the commerce logic itself is differentiated — account pricing, net terms, approval — but the seam still holds.
Use the platform's company, catalog, draft-order, and B2B checkout primitives for identity, price lists, and the eventual paid or invoiced order. Put custom code where native quoting stops: multi-step approvals, ERP price confirmation, freight that is not a shipping rate table, or a revision history buyers can audit.
What we avoid is a parallel order database that becomes truth after accept. The quote may live in your app while it is being negotiated. The moment it converts, Shopify (or whichever commerce platform owns money) should own the order id that fulfillment, print, and accounting hang off — the same instinct as treating an Al's Flowers webhook as the start of ops, not as a rumor you rewrite into a second ledger.
Reservation is not the same as sellable
A sent quote that silently holds stock for thirty days will starve the retail channel. A quote that holds nothing will accept against empty shelves. Neither failure is fixed by listening harder to inventory_levels/update.
Decide, per account or per quote size, whether lines reserve inventory, soft-allocate against a pool, or only check availability at accept. Encode that decision on the quote, and make expiry release whatever you reserved. Available is still not sellable — a unit committed to an open quote is spoken for even if your storefront cache has not noticed.
If two channels can sell the same SKU, reconciliation has to know about quote reservations the way it knows about checkout commits. Otherwise wholesale "wins" by accident whenever a buyer accepts late.
What we ask before we schedule a quote flow
Who is allowed to issue a price that is not on the price list? How long is a sent quote valid? What happens when a line's catalog price drops or rises before accept? Who approves above a threshold — and is that approval inside Shopify company roles or somewhere else?
Those answers decide whether native draft orders and quote requests are enough, or whether you need revisioned quote documents in your own store with a narrow convert step into the platform. They also decide whether ops after the order — tickets, print, ERP — can stay exactly as thin as they are for a normal checkout.
If the honest workflow is email plus a spreadsheet today, the smallest useful version is often a draft-order handoff with expiry and a locked PDF, not a CPQ suite. Build the state machine you can explain on a whiteboard. The PDF is an export of that state, not the system of record.
Questions
- Why can't a wholesale quote just be a cart?
- A cart may re-price and reshuffle until checkout. A sent quote is a commercial commitment with frozen lines, prices, terms, and an expiry. Re-reading live catalog prices on accept quietly replaces the agreement the buyer approved.
- Where should the order live after a quote is accepted?
- On the commerce platform that already owns money and fulfillment hooks — for our Shopify work, that means converting into a real order (often from a draft order) rather than inventing a second order ledger in the app.
- Should a sent quote reserve inventory?
- Only if you decide it should. Soft checks at accept, timed reservations, and no hold at all are all valid — but expiry must release holds, and multi-channel stock sync must know about them or retail will oversell against open quotes.