Order lifecycle
All sales funnel into POSOrder (backend-main/pos/models.py), distinguished by order_source and payment fields. Status changes go through pos/status.py (backend-main/pos/status.py) (apply_order_status) so archive timestamps and linked customer links stay consistent.
Order sources
order_source | How the row is created | Typical order_id prefix |
|---|---|---|
pos (default) | POS UI / customer-link conversion via POSOrderCreateSerializer (pos/serializers.py (backend-main/pos/serializers.py)) | 5-letter prefix from POS name |
ecommerce | COD or Razorpay fulfillment in ecommerce/checkout_orders.py (backend-main/ecommerce/checkout_orders.py) | ECOMM |
Allocation helpers: allocate_next_order_id / ORDER_ID_PREFIX_ECOMM in pos/models.py (backend-main/pos/models.py) (business day = Asia/Kolkata).
Ecommerce paths
flowchart TD
Cart[Storefront checkout] --> Method{payment_method}
Method -->|cod| COD[create_cod_order]
COD --> OrderCOD["POSOrder<br/>payment_status=not_required<br/>payment_done_later=True"]
Method -->|razorpay| Pend[PendingRazorpayCheckout open]
Pend --> Pay[Verify / webhook capture]
Pay --> OrderRP["POSOrder<br/>payment_status=verified<br/>razorpay_* ids set"]
Pend -.->|unpaid abandon| Open[Remains open — no POSOrder]
- COD: stock allocated/decremented in the same request as order create (Verify concurrency notes in
report.md). - Razorpay: stock is applied when fulfilling the pending checkout, not when the pending row is created.
Order-level statuses
Constants on POSOrder:
| Status | Meaning |
|---|---|
Placed | Default on create |
Processing | Fulfillment started; workflow units bootstrapped |
Needs Action | Derived when units need approval / failed / cancelled (pos/workflows.py (backend-main/pos/workflows.py)) |
Completed | All workflow units completed (or admin set) |
Shipped | Left the shop; delivery orders require tracking_id |
Archived | Soft end-state; preserves archived_from_status / archived_at |
stateDiagram-v2
[*] --> Placed
Placed --> Processing: payment allows + admin/start
Processing --> Needs_Action: workflow needs attention
Needs_Action --> Processing: resolved
Processing --> Completed: units done / admin
Completed --> Shipped: ship
Processing --> Shipped: ship
Completed --> Archived: auto/manual
Shipped --> Archived: auto/manual
Archived --> Completed: restore previous
Archived --> Shipped: restore previous
Moving to Processing is blocked unless payment_status is in PAYMENT_STATUSES_ALLOW_PROCESSING (verified or not_required) — see validation in POSOrderUpdateSerializer (backend-main/pos/serializers.py).
Workflow automation can roll parent status between Processing, Needs Action, and Completed while the order is live; shipped/archived parents are left alone (sync_order_status_from_units (backend-main/pos/workflows.py) pattern).
Payment sub-status
Independent of order status (admin reference):
payment_status | Typical use |
|---|---|
pending | Default; awaiting verification / Razorpay open intent |
verified | Screenshot or Razorpay capture accepted |
rejected | Admin reject (requires reason) |
pay_later | Explicit pay-later path |
not_required | Ecommerce COD |
Related fields: payment_done_later, payment_screenshot, payment_verified_at / payment_verified_by, payment_reject_reason, payment_method, Razorpay ids.
Archive & restore
- Manual / status API: setting status to
Archivedviaapply_order_statusstamps archive fields and, ifcustomer_linkis set, archives that link too (pos/status.py(backend-main/pos/status.py)). - Scheduled:
archive_stale_orders(backend-main/pos/archive.py) archivesCompleted/Shippedorders whosestatus_changed_atis older thanAppSettings.archive_completed_after_days/archive_shipped_after_days(0disables that trigger). Command:pos/management/commands/archive_stale_orders.py(backend-main/pos/management/commands/archive_stale_orders.py); admin trigger:/api/admin/orders/run-archive/. - Restore:
restore_archived_orderreturns the order toarchived_from_status(fallbackCompleted).
Live boards are notified via notify_order_clients (POS/admin order SSE + customer-link status SSE when linked).
Item workflow (fulfillment detail)
Each non–service-charge item can have POSOrderItemWorkflowUnit rows progressing through Stock → Design → Approval → Printing → Ready → Completed (with Failed / Cancelled / Broken branches). See pos/workflows.py (backend-main/pos/workflows.py). Parent order status is a rollup of those units once Processing has started.
Related
- Domain model
- Customer link lifecycle — conversion into
POSOrder