SparesHub — Order status: APIs that change (or set) `orders.status` ================================================================================ All paths are relative to the API base (e.g. `/api`). English reference only. Canonical enum (order in DB): pending, driver_accept, store_accept, packing, out_for_delivery, rejected, completed. Legend ------ - "No status change" means `orders.status` is not updated (other columns may change). 1) Store — accept or reject order (store staff) ----------------------------------------------- Method: POST Path: /store/orders/{orderId}/status Auth: store user + permission `orders.change_status` Body: { "status": "store_accept" | "rejected" } Transitions: pending -> rejected (when status = rejected; only from pending) pending -> store_accept (when status = store_accept AND assigned_driver_id IS NULL) pending -> (422) (when status = store_accept but a driver is already assigned: store must wait for driver accept, then apply store_accept from driver_accept — see below) driver_accept -> store_accept (when status = store_accept; driver has already accepted) Other current statuses: 422, no update (with appropriate error keys / hints). 1b) Store — advance fulfillment one step (packing / dispatch) ----------------------------------------------------------- Method: POST Path: /store/orders/{orderId}/advance-status Auth: store user + permission `orders.change_status` Body: none (order id in URL only) Transitions (exactly one step per request): store_accept -> packing packing -> out_for_delivery 422 with `status_advance_not_allowed` for other current statuses with a short `hint` field. 2) Driver — respond to assigned order (accept / reject assignment) ------------------------------------------------------------------ Method: POST Path: /driver/orders/{order}/respond Auth: driver user; order must be assigned to this driver Body: { "decision": "accept" | "reject" } A) decision = accept - If current status is already `driver_accept`: no status change (200). - If current status is `pending` or `store_accept`: -> `driver_accept` - Else: 422, no status change B) decision = reject - Allowed if current status is `pending`, `store_accept`, or `driver_accept`. - Effect: assigned_driver_id cleared, redispatched_at set. - If status was `driver_accept`, status becomes `pending` (redispatch). Otherwise orders.status is NOT modified. 3) Driver — mark delivery complete ------------------------------------ Method: POST Path: /driver/orders/{order}/complete Auth: driver user; order assigned to this driver Rule: If status is already `completed`: no status change (200). If status is `rejected`: 422, no change. Else current status must be one of: packing | out_for_delivery Transitions: packing -> completed out_for_delivery -> completed 4) Super admin — set order status directly (admin tool) ------------------------------------------------------- Method: PATCH Path: /super_admin/orders/{order}/status Auth: super_admin + permission `sa.orders.update_status` Body: { "status": "" } Allowed values (as in code): pending | driver_accept | store_accept | packing | out_for_delivery | rejected | completed Transition: ANY current status -> requested status (no step validation; overwrites). 5) Admin prefix — same handler as super_admin PATCH --------------------------------------------------- Method: PATCH Path: /admin/orders/{order}/status Auth: super_admin + permission `sa.orders.update_status` Same behavior as (4). 6) Customer — create order (initial status) --------------------------------------------- Method: POST Paths: /user/checkout /user/orders Auth: customer Typical result: New order starts as `pending`. Exception: If every store on the order has `auto_accept_orders` = true, status may be set to `store_accept` immediately after creation. 7) Customer — reorder (new order, initial status) ------------------------------------------------- Method: POST Path: /user/orders/{order}/reorder Auth: customer; must own the source order Transition: N/A for the old order. A NEW order row is created with status `pending`. Notes ----- - Normal flow reaches `packing` and `out_for_delivery` via POST /store/orders/{id}/advance-status (1b), after the order is in `store_accept`. - Driver reject (2B) does not set `rejected` (that is store-only from `pending`); it unassigns the driver and may return the order to `pending` when rejecting from `driver_accept`.