Appearance
TMS v2 at a glance
TMS v2 takes BigC customer orders from the store to the customer's door. BigC's order system sends the orders, ops plan them into vehicle routes, drivers deliver them and collect cash, and the store takes the cash back. This page covers an order's day from start to finish, the records involved, which area owns each record, and the build status.
Scope: the backend (core-v2) and the admin (store-v2). The driver app v2 is designed but not started.
How to read it: read this page before writing a spec for any phase. Foundations explains the rules every feature follows, and v1 vs v2 covers what changed from v1 and what it cost. For detail, use the numbered reference pages 01 to 07 in the sidebar (feature list, system overview, schema, states and flows, business rules, master data, integrations).
Every item on this page is marked Built (in code on main-v2-prototype, 9 Nov 2026), Prototype (code exists but does not yet follow the design), or Designed (written down, no code yet).
Who takes part
| who | what they do in TMS | how they reach it |
|---|---|---|
| OMS (BigC's order system) | sends new orders, changes and cancellations; hears back every order status change | Kafka |
| BigC staff app | hears every order status change, for the store team | Kafka |
| Ops admin | plans each store's day, fixes exceptions, follows held orders, open cash and overdue tasks | store-v2 admin in a browser |
| Driver (works for a partner company) | clocks in, loads, delivers stop by stop, collects cash on delivery (COD), reports failed attempts, returns to the store | driver app v2 (not started) |
| Store staff | at the driver's return, confirm the unload with a PIN and decide each failed order: deliver again later or fail it | store PIN at close-out |
| Store cashier | takes the driver's cash for one task and confirms it with their PIN | cashier PIN at the hand-over |
| Partner | the company that supplies drivers and vehicles; sees only its own rows | admin, partner scope |
One order's day
An order goes through seven stages. The order's status changes at each stage. Every change is sent to OMS and the staff app and written to the history.
| stage | what happens | owner | order status after | state |
|---|---|---|---|---|
| 1 Intake | An OMS message becomes a delivery order (DO) for its store within 5 seconds. Each message is applied once. An order for an unknown store is parked so it can be replayed, not lost | intake | UNASSIGNED | Built; parking is Designed (today the message is dead-lettered) |
| 2 Plan | Ops open the plan for one store and one date from the store's template, run Google route optimisation and adjust routes by hand. A plan can have variants, and orders are not claimed yet | planning | UNASSIGNED | Prototype: optimise runs inside the request, no variants |
| 3 Generate | The chosen plan turns each route into one driver task in a single all-or-nothing step. The orders are claimed at this moment | planning | ASSIGNED | Designed; the endpoint is a stub |
| 4 Dispatch | The driver loads the goods and departs (store check-in, staff PIN and photo). Execution writes DISPATCHED; workforce only supplies the driver's duty state, which must be on duty | execution | DISPATCHED | Designed |
| 5 Deliver | The driver opens the first stop, then delivers each order with proof of delivery (POD), collecting COD in the same step. A failed attempt puts the order on HOLD and counts the attempt | execution, cod-payment | OUT_FOR_DELIVERY → DELIVERED or HOLD | Designed |
| 6 Close-out | Back at the store, store staff confirm the unload with a PIN and decide each held order. Redeliver sends it back to the pool for another day; fail ends it | execution | UNASSIGNED or FAILED_DELIVERY | Designed |
| 7 Cash handover | The cashier of the task's store takes the cash for the whole task with their PIN. A shortage needs a reason | cod-payment | (order unchanged) | Designed |
Sending each change to OMS and the staff app is Built: it is written as a durable row in the same step as the change and sent by a worker that retries. 04 States and flows has a sequence diagram for every stage.
The records and how they connect
The order is the centre. Planning and execution hold only references to it, never a copy, so its status and its cash amount live in one place.
| record | in plain words | rule to remember |
|---|---|---|
| delivery_order (DO) | one customer order to deliver | the only place its status and COD amount live |
| delivery_order_item | the order lines as OMS sent them | the DO keeps the totals (weight, volume, amount), so lists never read the lines |
| plan | the planning workspace for one store and one date | can have variants; only one chosen variant generates tasks |
| route | one vehicle's ordered stops inside a plan | becomes at most one task |
| task | one driver's trip from the store and back | replaces v1's load + task pair |
| stop / stop DO | one address in the task / one order at that address | refer to the DO by id, never copy it |
| attempt | one failed delivery try, with its reason | the DO counts attempts in attempt_count |
| duty record | one driver on one date: work windows, clock-in and out, state | two windows of one driver never overlap |
| cash handover | the cash of one task handed to the store | COD money itself stays on the DO |
The full field list is in 03 Schema.
Words every spec uses
Order status. Every module uses these eight names. A redelivered order goes back to UNASSIGNED with its attempt count kept, so there is no separate "rescheduled" status.
| status | meaning | final |
|---|---|---|
| UNASSIGNED | in the pool, on no route yet | no |
| ASSIGNED | on a route that has not left the store | no |
| DISPATCHED | loaded and on a task that has left the store | no |
| OUT_FOR_DELIVERY | the driver has opened the first stop | no |
| HOLD | the driver recorded a failed attempt; the store decides at return | no |
| DELIVERED | delivered with proof; COD collected in the same step | yes |
| FAILED_DELIVERY | will not be delivered on this DO; OMS may send a new one | yes |
| CANCELLED | cancelled by OMS or ops. The design accepts an OMS cancel only before the order is in a task and before any failed attempt, and puts a refused cancel on the ops list (rule waiting on BigC) | yes |
The code today still uses the v1-style names (PENDING, PLANNED, IN_TRANSIT and so on). The mapping is in 04 States and flows.
Task status. A task goes PENDING (generated) → ACCEPTED (driver accepts) → DISPATCHED (left the store) → IN_TRANSIT (first stop opened) → COMPLETED (closed out at return). Close-out completes the task only when every stop is final and every undelivered order has been decided. Ops can cancel a task only before it leaves the store. Stop, plan, duty record and job each have their own statuses, listed in 04 States and flows.
"Hold" means three different things. v1 used one word for all three. v2 gives each its own name, and a spec must say which one it means.
| name | what it is | who sets it |
|---|---|---|
| ops hold (suspend) | ops pause an order so planning skips it; a record on the DO, not a status | ops admin, only while the order is UNASSIGNED or ASSIGNED and its task has not left the store |
| HOLD | the order status after a failed attempt | driver |
| HELD | the driver's duty state: the driver gets no new work | ops admin, or automatically when an on-demand driver does not accept a job in time |
Two more terms: a booking is a work window a driver books (for example 13:00–17:00), which adds that window to the driver's duty record; day close is the end-of-day run that ends every duty record of the day, including drivers who never clocked out.
Who owns what
v2 is split into seven business areas. Each area owns its records and is the only one that writes them. Another area that needs a change calls the owner's command, for example "assign these orders to a route" or "set the COD amount". It never edits the record directly.
The DO is the one record several areas touch, so its rules are fixed: intake creates it, each status move is made by the area that runs that stage (for example planning moves it to ASSIGNED, execution moves it from DISPATCHED onward) through the DO's single transition table, and only cod-payment writes the COD fields.
| area | owns | phase |
|---|---|---|
| platform | login, jobs and schedules, Kafka in and out, history, settings, files, notifications | 1 |
| intake | DO, DO items, parked messages, channels, stores | 2 |
| workforce | drivers, partners, vehicles, bookings, leave, duty record | 3 |
| planning | plan, routes, plan templates, plan history | 4 |
| execution | task, stops, POD, attempts, GPS trail, close-out | 5 |
| cod-payment | COD fields on the DO, cash handover | 5 (collection), 6 (handover) |
| admin-reporting | reports, imports, exports, roles and permissions | 7 |
Where the build stands
The platform base is mostly built. Intake and planning have a working prototype that does not yet follow the designed states and rules. Execution, COD and workforce are designed only. Per-feature status is in 01 Feature list, and the phases are in 02 System overview.
| phase | what works at the end | state |
|---|---|---|
| 1 Platform | safe login, every event, email and job saved and retried, one history, files to S3 | in progress: jobs, outbox, history, settings, files done; auth fixes open |
| 2 Intake + masters | orders become DOs once each, unknown stores parked, cancel and redelivery rules, admin create and import | started: Kafka intake and DO updates out work |
| 3 Workforce | drivers, partners, bookings, duty record, day close | not started |
| 4 Planning | one chosen plan per store and date, optimise as a job, generate tasks | a prototype was built before its phase came up |
| 5 Execution + COD collection | the driver's run end to end | not started |
| 6 COD handover | cashier PIN, one handover per task | not started |
| 7 Admin and reporting | reports on live data, imports, exports, roles | foundations only |
Before you write a spec
A phase spec is ready when it answers these questions. Each one comes from a foundation rule.
| question | why it matters | where to look |
|---|---|---|
| Which status moves does the feature make, and from which starting status? | every write is guarded by the status it expects | 04 States and flows |
| Which area owns each record it changes, and which command does it call? | only the owner writes | this page, "Who owns what" |
| What does it write in one all-or-nothing step? | no half-done states | Foundations |
| What makes a repeat the same request (double tap, resent message)? | repeats must change data once | Foundations |
| Which side effects may never be lost, and which may? | decides durable row or best effort | 07 Integrations and jobs |
| How does each record it opens get closed, and who is told when it does not? | nothing stays open forever | Foundations |
| Which business rules apply, and are any still waiting on BigC? | 17 rules still wait on BigC | 05 Business rules |