Plain-English summary
Deliveries is the queue for orders that go out to customers. On the counter app it is not a separate page — it is the POS order screen in delivery mode: the same session POS filtered to delivery orders, with its own status tabs (Open → Preparing → Ready → Enroute → Delivered), a delivery-sector filter, and a one-tap driver assignment modal. Riders use a separate Flutter mobile app to see their assigned orders, navigate, collect cash, and mark orders delivered; rider cash flows back to the register through a handover/acknowledge step. Delivery-specific data (sectors with per-sector charges, delivery addresses, driver assignment) attaches to the order at creation.
When you'd use this
- Dispatching drivers — assign orders, track Enroute/Delivered status
- Working the delivery queue during service — which orders are ready to go out
- Filtering the queue by delivery sector to batch nearby orders onto one rider
- Reconciling rider cash at end of shift (Delivery Cash tab on the session)
How to access
- Sidebar: "Deliveries" — with an open session it navigates to
/sessions/{sessionId}/pos?mode=delivery; with no session it goes to/counter?intent=deliveryso you open a register first. [code: foodops.counter.app/src/components/layout/Sidebar.tsx:321-339 (mode=delivery at :325, no-session intent at :335) @main] - Mobile bottom nav: same
?mode=deliverynavigation. [code: src/components/layout/MobileBottomNav.tsx:80 @main] - Note: there is no separate
/deliveriespage in the counter app — the real surface is POS delivery mode. [code: NavigationContext.tsx:37 (active-menu mapping only, no Route) @main]
Walkthrough
Working the delivery queue (cashier/dispatcher, counter app)
Reconstructed from POSPage source, re-audited at main HEAD
df086e4(2026-06-14).
- Open the sidebar Deliveries item (requires an open register session). The POS loads with header Deliveries and delivery-specific status tabs: Open / Preparing / Ready / Enroute / Delivered. [code: foodops.counter.app/src/pages/POSPage.tsx:4367(header label),4425-4434(delivery tab list) @main]
- (Optional) Filter by delivery sector — sectors are fetched per session and each carries a name + delivery charge (
DisplayName => $"{Name} ({FormattedCharge})", e.g. "Hulhumalé (MVR 30.00)"). Selecting a sector reloads the order list scoped to it. [code: POSPage.tsx:651-665(per-session fetch),668-671(reload on sector change) @main; hubits-api-sales/Hubits.Sales.Api/Dto/Counter/Delivery/DeliverySectorDto.cs:10 @main] - Create a delivery order with the Delivery button (shown only when
orderButtonConfig.enableDeliveryis true for the outlet) — this routes into the standard order-create flow with order type id = 3 (delivery); customer, delivery sector and delivery address attach via the order summary panel. [code: POSPage.tsx:4377,4398,4766(button gated on enableDelivery),1469/1765(orderTypeId: isDeliveryMode ? 3) @main; src/components/orders/OrderSummaryPanel.tsx:21-22(deliverySectorId?/deliveryAddressId?) @main] - Assign a driver from the order card — the driver assignment modal lists drivers and assigns on a single tap ("Mirrors the Restaurant.App
_DriversAssignment.cshtmlUX: tap a card, it assigns immediately and closes"). [code: src/components/orders/DriverAssignmentModal.tsx:14-15(doc comment),41(getDrivers(orderId)),52(currentDriverId guard) @main] - Mark the order Enroute (Out for Delivery, status id 6) — from the order card or automatically when the rider picks it up via the rider app. [code: src/components/orders/OrderCard.tsx:742]
- Delivered (status id 7) is the terminal state. The rider flow owns this transition — item-served events do NOT auto-advance delivery orders the way they advance dine-in (Served) and takeout (Picked). [code: POSPage.tsx:774-777 — "Delivery doesn't auto-advance on item-served; leave the operation status untouched so the rider flow stays in control" @main]
Rider workflow (Flutter driver app — DORMANT/LEGACY)
Warning: Launch posture: the standalone Flutter rider app is a dormant/legacy surface (manual build, no CI;
foodops.delivery.apiretired 2025-11-27). It still functions because it points at the live sales API, but it is not a primary launch surface. For the July-10 launch, delivery is operated from the counter delivery queue (above) + the sales API delivery service; rider assignment and cash reconciliation run there. See Rider App.
The driver app (foodops.delivery.mobile, Flutter) talks directly to the sales API (sales-api.hubits.io) and its /orderHub SignalR hub. [code: foodops.delivery.mobile/lib/Helper/Api/api_constant.dart:4(baseUrl=https://sales-api.hubits.io/api/),8(signalRHubUrl=.../orderHub),18(identity-api) @master]
- Rider logs in (identity API) and picks the organisation. [screens:
lib/Screens/DeliveryScreen/LoginScreen,OrganizationScreen] - Rider sees delivery orders (
delivery-management/orders) and order details; assigned orders surface on the Assigned Orders screen. [code: api_constant.dart:28,39] - Rider marks the order en route (
POST api/delivery/orders/mark-enroute) and later delivered (mark-delivered). [code: foodops.delivery.mobile/lib/Helper/Api/api_constant.dart:55-56 @master; hubits-api-sales/Hubits.Sales.Api/Controllers/DeliveryOrdersController.cs:291(mark-enroute),349(mark-delivered) @main] - For cash orders the rider records collection (
POST delivery-management/orders/{id}/cash/collect), hands the cash over to the register (POST delivery-management/cash/handover), and can check cash on hand (GET delivery-management/cash/on-hand). [code: hubits-api-sales/Hubits.Sales.Api/Controllers/DeliveryManagementController.cs:182(collect),208(handover),234(on-hand) @main] - The cashier acknowledges the handed-over cash in the session's Delivery Cash tab, which adds it to the session's Expected balance — see Cash Handling.
The driver app also contains a Waiter mode (dine-in order taking screens) alongside the delivery screens. [dirs: lib/Screens/WaiterScreen/{AddOrderScreen,DineInScreen,...}]
Key concepts
- Delivery mode, not a delivery page — the queue is POSPage with
?mode=delivery; the tab whitelist, status mapping, and sector filter all key offisDeliveryMode. [code: POSPage.tsx:176(isDeliveryMode = modeParam === 'delivery'),1397-1399(tab whitelist),651-665(sector fetch) @main] - Status lifecycle (delivery) — New(1)/Accepted(2) → Preparing(3) → Ready(4) → OutForDelivery(6, shown as "Enroute") → Delivered(7). (Server enum is authoritative: New=1…Served=5, OutForDelivery=6, Delivered=7, Picked=8.) The rendered delivery-mode tabs are Open / Preparing / Ready / Enroute / Delivered (the last tab has internal
key='Served'butlabel='Delivered', mapping to the server Delivered filter). The "Delivered" tab badge sums delivered+served+picked terminal buckets so badge count equals list count. [code: POSPage.tsx:705-714(client ids),4425-4434(rendered delivery tabs) @main; haafai.domain/haafai.domain/Haafai.Models/Constants/ModelConstants.cs:RestaurantOrderOperationStatus:51-60 @staging] - Delivery sectors — named zones with a per-sector delivery charge and currency, maintained in restaurant settings (
api/settings/restaurant/delivery-sectors) and selectable on the order; also usable as a queue filter. [code: hubits-api-sales/Hubits.Sales.Api/Controllers/RestaurantSettingsController.cs:749-863; DeliverySectorDto.cs] - Driver assignment — drivers are fetched per order (
driverApiService.getDrivers(orderId)) and assigned one-tap; the current driver is highlighted. [code: DriverAssignmentModal.tsx:41(getDrivers),52(current driver guard) @main; src/services/driver/driverApiService.ts:46(getDrivers signature) @main] - Rider cash loop — collect (rider) → handover (rider) → acknowledge (cashier, session Delivery Cash tab) → Expected balance. Unacknowledged rider cash shows up as an unexplained over at session close.
- Two backends, one live — the rider app's original backend repo (
foodops.delivery.api, thefoodops.app.apiproject) is dormant (master HEAD51a1f8cdated 2025-11-27); the same controller surface (Delivery, DeliveryManagement, CashOnHand, DeliveryOrders, DeliverySectors, OrderHub) now lives inhubits-api-sales, and the shipped app points atsales-api.hubits.io. [code: foodops.delivery.mobile/lib/Helper/Api/api_constant.dart:4 @master; hubits-api-sales/Hubits.Sales.Api/Controllers/{Delivery,DeliveryManagement,CashOnHand,DeliveryOrders,DeliverySectors}Controller.cs + Hubs/OrderHub.cs mapped at Program.cs:337 @main]
Common questions
Q: Where do I see all delivery orders? A: Sidebar → Deliveries. With an open session you land in POS delivery mode; the Open tab shows everything not yet out the door, Enroute shows orders with riders, Delivered is the terminal tab.
Q: How do I assign a rider to an order? A: Open the delivery order's card and choose the driver assignment action — the modal lists drivers; tapping one assigns immediately and closes. [code: DriverAssignmentModal.tsx]
Q: Can a delivery order exist without a sector?
A: Yes — the sector is optional on the order (deliverySectorId?), and the queue's sector filter defaults to "All". [code: OrderSummaryPanel.tsx:21; POSPage.tsx:314]
Q: Who marks the order Delivered — the cashier or the rider?
A: Either can, but the flow is designed for the rider: the rider app calls mark-delivered, and the POS deliberately leaves delivery orders alone on item-served events "so the rider flow stays in control". The cashier can also mark Delivered from the order card (the "Delivered" button on out-for-delivery cards). [code: POSPage.tsx:774-777; OrderCard.tsx:handleMarkAsDelivered:778-797 @main]
Q: What does the "Enroute" tab mean exactly?
A: Orders in operation status OutForDelivery (id 6) that are not yet paid/closed. The tab key maps to the server's OutForDelivery filter. [code: POSPage.tsx:681-682(Enroute→OutForDelivery),2154 — restaurantOperationStatusId === 6 ... && !isOrderPaid(o) @main]
Q: How does the delivery charge get onto the order? A: From the selected delivery sector — each sector carries a configured charge; selecting a sector on the order passes the charge to the order totals. [code: OrderSummaryPanel.tsx:51; DeliverySectorDto.cs]
Q: Where are delivery sectors configured?
A: Restaurant settings → outlet Services/delivery settings (admin app), backed by api/settings/restaurant/delivery-sectors CRUD. [code: RestaurantSettingsController.cs:749-863; foodops-admin-app DeliverySectorFormDialog.tsx]
Q: What happens with cash the rider collected? A: The rider records collection and hands cash over via the rider app; the cashier acknowledges the handover in Session Details → Delivery Cash, which counts it into the session's Expected cash. See Cash Handling.
Q: Do delivery orders appear on the Kitchen Display?
A: Yes — kitchen routing is order-type-agnostic; KOTs fire on confirmation like any other order. See Kitchen Display System.
Q: Is there an integration with third-party delivery platforms (UberEats-style)? A: Not found in current code as of 2026-06-14. The delivery stack is in-house riders only — sectors, own driver assignment, own rider app. (The user story US-INT-004 describes third-party fleet integration as an aspiration; no implementing code was found.) [code: VERIFIED-ABSENT — grep of foodops.counter.app@main src/ for ubereats|deliveroo|talabat|doordash|grubhub|aggregator returned no delivery-integration code]
Q: Customers ordering delivery online — same queue? A: Yes. Online orders placed with delivery mode flow into the counter via SignalR and appear in the same delivery queue. See Online Ordering Platform.
Q: Can I take payment before the rider leaves?
A: Yes — delivery orders support the normal payment flow at the counter; cash-on-delivery instead flows through the rider cash loop (delivery/orders/payment endpoint serves the rider-side payment record). [code: DeliveryOrdersController.cs:154]
Edge cases and known issues
- No open session → no queue — the Deliveries sidebar item needs a session; without one you are routed to
/counter?intent=deliveryto open a register first. [code: Sidebar.tsx:323-338 —if (currentSession) navigate(/sessions/{id}/pos?mode=delivery); else navigate('/counter?intent=delivery')@main] - Item-served does not advance delivery orders — unlike dine-in/takeout, a delivery order stays in its current status when the kitchen marks all items served; only the enroute/delivered transitions move it. Symptom: "kitchen finished but order still shows Preparing/Ready" is expected until dispatch. [code: POSPage.tsx:774-777 — the
orderTypeLower === 'delivery'branch deliberately leaves operationStatus untouched while takeout advances to Picked(8) @main] - Legacy rider backend —
foodops.delivery.api(projectfoodops.app.api) is dormant; do not verify rider behaviour against it. The live rider endpoints are inhubits-api-salesunderapi/delivery,api/delivery/orders,api/delivery-management. The repo is retained insource_reposonly as the historical reference for the Flutter app's API contract. - Sector filter only in delivery mode — leaving delivery mode clears any selected sector filter; sector state does not leak into takeout/counter views. [code: POSPage.tsx:1386-1390 —
if (!isDeliveryMode && selectedDeliverySectorId) setSelectedDeliverySectorId('')@main] - Old monolith Delivery views retired — the legacy
haafai.app.foodopsHaafai.Restaurant.App/Views/Delivery/**monolith surface is retired at the July-10 launch; the live delivery surface is the counter delivery mode +hubits-api-sales.
Related
Takeouts — sibling queue for pickup orders (
?mode=takeout)Order Types — dine-in vs takeout vs delivery enablement per outlet
Cash Handling — the rider-cash acknowledge flow on the session
Online Ordering Platform — the customer-facing source of many delivery orders
Outlets — enabling the Delivery service per outlet
2026-06-14 — codebase TRUTH AUDIT against current prod HEADs
foodops.counter.app@df086e4(main),hubits-api-sales@f79eb7b(main),foodops.delivery.mobile@3d9ff2c(master),haafai.domain@a2bd90e(staging). All customer-facing claims confirmed TRUE; line-number citations re-pinned to current HEAD and@branchtags added. Specifics verified in code: status enum New(1)…OutForDelivery(6)/Delivered(7)/Picked(8) againsthaafai.domain ModelConstants.cs:RestaurantOrderOperationStatus; rendered delivery tabs Open/Preparing/Ready/Enroute/Delivered (last tab internal key=Served, label=Delivered) at POSPage.tsx:4425-4434; no-auto-advance-on-item-served for delivery at POSPage.tsx:774-777; Sidebar/MobileBottomNav?mode=delivery+?intent=deliverynav; no/deliveriesRoute (only vestigial NavigationContext mapping); DeliveryManagementController cash collect(182)/handover(208)/on-hand(234) — FIXED transposed handover/on-hand line cites; DeliveryOrdersController mark-enroute(291)/mark-delivered(349)/payment(154); RestaurantSettings delivery-sectors CRUD(749+); Flutter api_constant baseUrl=sales-api.hubits.io + WaiterScreen/DeliveryScreen dirs; foodops.delivery.api dormant (master HEAD 51a1f8c dated 2025-11-27 ✓). Third-party platform integration re-verified ABSENT via grep. ONE soft claim flagged UNVERIFIED: delivery KOTs appear on KDS (no order-type gating found that excludes delivery, but no affirmative delivery-specific KOT path — deferred to kitchen-display.md).2026-06-29 — codebase: NEW-STACK launch re-verification (July-10). Re-pinned to current prod HEADs:
foodops.counter.app@32669a9f3a,hubits-api-sales@19190b9a04,haafai.domain@e1389444ca(staging). CONFIRMED the live delivery surface is the counter delivery queue (POS?mode=delivery) +hubits-api-salesServices/Counter/Delivery/**(DeliveryController/DeliveryOrdersController/DeliveryManagementController/DeliverySectorsController/DeliveryService all present on main). Removed dormantfoodops.delivery.apifrom source_repos (retired 2025-11-27; superseded by sales-api). LAUNCH POSTURE FLAGGED: the standalone Flutter rider app (foodops.delivery.mobile, master HEAD1ff0a759f3, re-point commit 2026-06-15, no CI) is dormant/legacy, not a primary launch surface — added a warning to the rider workflow section. Retired-monolith Delivery views edge case updated. Customer-facing delivery-queue claims (status tabs, sector filter, driver assignment, rider cash loop, no-auto-advance-on-item-served) unchanged from the 2026-06-14 audit.
Additional verification events are appended here by the weekly KB sync and by manual re-verification passes. See _meta/failure-log.md for the global failure index.