Happy House - Ecommerce Docs
Developer ResourcesOrder

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

SurfaceRoutePermission
CustomerGET /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
AdminGET /api/orders · GET /:id · POST /:id/{confirm,processing,cancel,notes,cod-collection,shipments} · POST /:id/shipments/:shipmentId/{deliver,fail}Orders_READ / Orders_UPDATE
AdminPOST /api/orders/:id/refundsRefunds_UPDATE
AdminGET /api/order-returns · GET /:id · POST /:id/{decide,receive,inspect}Returns_READ / Returns_UPDATE
AdminGET /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

  1. The chain: cart → checkout → payment → order.eligible outbox row → CREATE_ORDER job → 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.
  2. An order's status has NINE values, and none of them is returned or refunded. 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.
  3. An order item has no status field. Quantities are the truth (quantity, unavailableQuantity, cancelledQuantity, shippedQuantity, deliveredQuantity, returnedQuantity); the API returns a derived label for display only.
  4. COD is "committed", not "collected". A COD order is paid in the data model from placement (that is what lets stock be deducted); codCollectedAt is the field that says money actually changed hands. Cancelling an uncollected COD order refunds nothing — correctly.
  5. Refunds are settled by a human. No gateway refund is integrated. An approved refund waits in awaiting_settlement until a human moves the money and records a settlement reference (required by constraint).
  6. 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.
  7. The invoice is NOT a tax invoice — it is a sales receipt and says so on its face.

Page guide

PageRead it for
Features and flowsThe lifecycle diagram, short orders, returns-as-budget, error-recovery
BackendThe chain, the 12 tables, money allocation, COD, serials
APIAll 28 endpoints, DTOs and error codes