Kitchen status
Operator kitchen flow: accepted → preparing → ready → completed.
Kitchen status
The sixth product slice is Kitchen → Status. After accept, the restaurant cooks the pickup order. The operator advances it one step at a time: preparing, then ready, then completed. The guest sees the new status on confirmation. Payment stays cash / unpaid. There is no payment provider.
Checkout still creates the pending order. Accept/reject and the 10-minute timeout are Slice 5: Accept, reject & timeout.
Transitions (ADR-015)
Only these writes exist in this slice:
| From | To | How |
|---|---|---|
accepted | preparing | Dashboard preparing |
preparing | ready | Dashboard ready |
ready | completed | Dashboard complete |
Forward, one step. Skip (accepted → ready, accepted → completed) is 409. Reverse (ready → preparing) is 409. Pending stays Slice 5 only: pending → preparing is 409. Already completed or cancelled → any kitchen write is 409. Double-prepare / double-ready / double-complete is 409.
No cancel after accept. customer_cancelled and kitchen-cancel are out of scope. Existing reject/timeout paths are unchanged.
Columns
No new tables. No new required fields. order_status already includes preparing / ready / completed. eta_minutes stays the accept-time value. cancellation_reason stays null unless Slice 5 cancelled the order. Payment stays unpaid / cash.
Dashboard API
Requires a staff session. Restaurant context is the path, not the storefront hostname (ADR-021). Query/body restaurantId is ignored.
| Method | Path | Body |
|---|---|---|
POST | /dashboard/restaurants/:restaurantId/orders/:orderId/preparing | empty or ignored optional restaurantId |
POST | /dashboard/restaurants/:restaurantId/orders/:orderId/ready | empty or ignored optional restaurantId |
POST | /dashboard/restaurants/:restaurantId/orders/:orderId/complete | empty or ignored optional restaurantId |
GET | /dashboard/restaurants/:restaurantId/orders | — |
GET | /dashboard/restaurants/:restaurantId/orders/:orderId | — |
Each write is one transactional UPDATE … WHERE restaurant_id = :path AND id = :orderId AND order_status = '<expected>'. 0 rows → 409, or 404 when the order does not belong to that restaurant.
GETs return the real status, including kitchen values. List is newest first. Accept/reject and the countdown stay pending-only.
A Luigi orderId under Mario's restaurantId is 404 and is not written.
Storefront
GET /storefront/orders/:orderId returns the same fields. Confirmation shows pending, accepted (with ETA), preparing, ready, completed, or cancelled (rejected vs timeout). The page polls the existing GET until completed or cancelled. A Luigi orderId on Mario's host is 404 / OrderUnavailable.
preparing → ready sends one transactional SMS (“order is ready for pickup”). /preparing and /complete do not send. Skip/illegal 409 and Mario-path 404 do not notify. See Transactional notifications.
Isolation
Seeds still create no orders. Isolation is proven by real checkouts, then accept, then kitchen writes.
- Luigi checkout → Luigi accept
etaMinutes: 30→preparing→ready→complete→completed. Snapshot prices unchanged. ETA 30 remains. Paymentunpaid/cash. - Same Luigi
orderIdunder Mario's dashboard path for/preparing(and ready/complete) → 404, status unchanged. preparingonpendingorcancelled→ 409.readyonaccepted(skip) → 409.- Second
/completeon the same order → 409. - Storefront: Luigi sees
preparing/ready/completed; Mario host → 404 for thatorderId. - Body
restaurantIddoes not change the tenant.
Not in this slice
BitePay/Stripe, card_at_door, capture/refund/void, delivery, address, minimum order, delivery fee, customer accounts, variants/extras/allergens, cancel after accept, customer cancel, e-mail, kitchen pushes except Ready SMS, sound/PWA, production deploy. Closed checkout 409: Opening hours. Dashboard session: Staff authentication. Ready SMS: Transactional notifications.