Skip to main content

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_sourceHow the row is createdTypical 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
ecommerceCOD 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:

StatusMeaning
PlacedDefault on create
ProcessingFulfillment started; workflow units bootstrapped
Needs ActionDerived when units need approval / failed / cancelled (pos/workflows.py (backend-main/pos/workflows.py))
CompletedAll workflow units completed (or admin set)
ShippedLeft the shop; delivery orders require tracking_id
ArchivedSoft 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_statusTypical use
pendingDefault; awaiting verification / Razorpay open intent
verifiedScreenshot or Razorpay capture accepted
rejectedAdmin reject (requires reason)
pay_laterExplicit pay-later path
not_requiredEcommerce 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 Archived via apply_order_status stamps archive fields and, if customer_link is set, archives that link too (pos/status.py (backend-main/pos/status.py)).
  • Scheduled: archive_stale_orders (backend-main/pos/archive.py) archives Completed / Shipped orders whose status_changed_at is older than AppSettings.archive_completed_after_days / archive_shipped_after_days (0 disables 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_order returns the order to archived_from_status (fallback Completed).

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.