Order Module Overview
The permanent commercial record — dispatch, delivery, returns and refunds, created from checkout completion by a background job.
Audience: Product owners, QA, frontend and backend developers Scope: The order lifecycle, returns and refunds as separate aggregates, and the money rules
Order Module - Overview
1. What the module is
The order module records a permanent commercial record for every completed checkout, and owns everything that happens afterwards: dispatch, delivery, cancellation, returns and refunds. A customer can see purchase history, poll one order's status, download a receipt PDF, cancel before dispatch, and request a return. An operator can confirm, dispatch (entering device serial numbers), record delivery, record cash for COD, decide and inspect returns, and approve and settle refunds.
Nothing creates an order from the client side. It is created by a background job from the checkout completion — the first a storefront hears of it is that the order appears in the list.
2. The routes — 28
| Surface | Route | Permission |
|---|---|---|
| Customer | GET /api/mobile/orders · GET /:id · GET /:id/status · GET /:id/invoice.pdf · POST /:id/cancel · POST /:id/returns · GET /:id/returns · POST /:id/returns/:returnId/cancel | — |
| Admin | GET /api/orders · GET /:id · POST /:id/{confirm,processing,cancel,notes,cod-collection,shipments} · POST /:id/shipments/:shipmentId/{deliver,fail} | Orders_READ / Orders_UPDATE |
| Admin | POST /api/orders/:id/refunds | Refunds_UPDATE |
| Admin | GET /api/order-returns · GET /:id · POST /:id/{decide,receive,inspect} | Returns_READ / Returns_UPDATE |
| Admin | GET /api/order-refunds · POST /:id/{approve,reject,settle} | Refunds_READ / Refunds_UPDATE |
Returns and Refunds are separate permission modules from Orders, deliberately — a warehouse operator needs Orders_UPDATE every day to dispatch; nobody should acquire the ability to hand money back as a side effect of that.
3. The rules that matter
- The chain: cart → checkout → payment →
order.eligibleoutbox row →CREATE_ORDERjob → order. The outbox row is written inside the checkout completion transaction — a row enqueued after a commit can be lost, and here that means a paid customer with no order. - An order's status has NINE values, and none of them is
returnedorrefunded. Returns and refunds are separate objects hanging off the order, each with its own status; an order can have several of each in flight. A "partially returned" badge is derived from the returns array. - An order item has no status field. Quantities are the truth (
quantity,unavailableQuantity,cancelledQuantity,shippedQuantity,deliveredQuantity,returnedQuantity); the API returns a derivedlabelfor display only. - COD is "committed", not "collected". A COD order is
paidin the data model from placement (that is what lets stock be deducted);codCollectedAtis the field that says money actually changed hands. Cancelling an uncollected COD order refunds nothing — correctly. - Refunds are settled by a human. No gateway refund is integrated. An approved refund waits in
awaiting_settlementuntil a human moves the money and records a settlement reference (required by constraint). - Money is minor units everywhere — per-line discount allocation is computed once at creation with the largest-remainder method and frozen; both refund ceilings (
chk_order_refunded_within_paid,chk_order_item_refunded_within_net) are database invariants. - The invoice is NOT a tax invoice — it is a sales receipt and says so on its face.
Page guide
| Page | Read it for |
|---|---|
| Features and flows | The lifecycle diagram, short orders, returns-as-budget, error-recovery |
| Backend | The chain, the 12 tables, money allocation, COD, serials |
| API | All 28 endpoints, DTOs and error codes |