2.2 KiB
Domain: Orders, Checkout, Fulfillment, Freight
Lifecycle
cart → checkout (one tx) → order per shop shipment flow
───────────────────────── ─────────────────────────
pending_payment ──pay──► paid ──first shipment──► fulfilling ──all shipped──► shipped
│ │
└──cancel──► cancelled delivered (all shipments) ──► completed
Every arrow is a guarded UPDATE … WHERE status = …; stale actions → 409.
Checkout invariants (modules/order/service.rs)
One transaction per checkout: lock SKUs (FOR UPDATE in id order) → resolve
flash-sale activity pricing → per-shop coupon discount (server-computed,
client sends only coupon ids) → per-shop freight fee → insert order + items +
stock decrements → commit → clear cart. Orders are split per shop; each order
carries total_minor = subtotal − discount + shipping_fee_minor.
Snapshots make history immutable: order items keep unit_price_minor,
product_name, flash_sale_item_id, and the freight
freight_template_id/freight_pricing_method resolved at checkout. Later
template edits never touch past orders.
Freight (modules/freight/)
Templates are shop-scoped (own_shop): by_piece or by_weight (grams on
skus.weight_grams), first-unit + additional-unit fees with part-unit
round-up, always_free, free_threshold_minor, and region rules overriding
default fees by case-insensitive match against address region/city/country.
Resolution per line: product template → shop default → zero fee. Fees merge
per template group within a shop order and convert from the group's SKU
currency to the order currency.
POST /orders/shipping-quote is the read-only quote (normal prices only);
checkout recomputes authoritatively in the order transaction. Client-supplied
fees are never read.
Shipping companies are a dictionary (shipping_companies); shipments record
shipping_company_code validated against it.
Key files
See docs/code_index/order.md.