updates on the fix
This commit is contained in:
240
doormile-flow.md
Normal file
240
doormile-flow.md
Normal file
@@ -0,0 +1,240 @@
|
||||
# Doormile — order to delivery
|
||||
|
||||
Every endpoint in the DailyGrubs path, the payload that goes in, and what
|
||||
actually changes when it does. Base URL `https://api.doormile.com/api/v1`.
|
||||
|
||||
Payload shapes are taken from the request structs in this repo, not from
|
||||
documentation — where the two disagree, the code is right. Where a field is
|
||||
optional it says so.
|
||||
|
||||
---
|
||||
|
||||
## Auth
|
||||
|
||||
Two separate logins.
|
||||
|
||||
**Console.** The token carries `tenantid`, which is what scopes a client to
|
||||
their own data. A client passing another tenant's `?tenantid=` gets 403; reading
|
||||
another tenant's resource by id gets 404, so ids aren't probeable.
|
||||
|
||||
```
|
||||
POST /admin/login
|
||||
{ "email": "info@dailygrubs.com", "password": "admin" }
|
||||
|
||||
→ { "token": "eyJ…", "user": { "id": 43, "tenantid": 13 } }
|
||||
```
|
||||
|
||||
**Rider.** Two calls, and `configid` must be **1001** on both — a Doormile login
|
||||
partition with no jupiter equivalent. Omitting it is the most common reason a
|
||||
rider looks like they don't exist.
|
||||
|
||||
```
|
||||
POST /miler/login
|
||||
{ "phone": "9787698259", "configid": 1001 }
|
||||
|
||||
POST /miler/verify-pin
|
||||
{ "phone": "9787698259", "pin": "1234", "configid": 1001, "device_token": "fcm-…" }
|
||||
|
||||
→ { "token": "eyJ…" }
|
||||
```
|
||||
|
||||
`POST /miler/reset-pin` is **admin-only** despite sitting under `/miler`. A phone
|
||||
number is the login identifier, not a secret — left open, reset-then-verify takes
|
||||
over any rider account in two calls. The rider app must not call it.
|
||||
|
||||
---
|
||||
|
||||
## 1. Create a booking
|
||||
|
||||
```
|
||||
POST /admin/expressbooking
|
||||
{
|
||||
"tenantid": 13,
|
||||
"tenantlocationid": 20, // the kitchen. optional — inferred if omitted
|
||||
"customer_phone": "9876500011",
|
||||
"customer_name": "Priya R",
|
||||
"pickupaddress": "DailyGrubs RS Puram Kitchen",
|
||||
"pickuppincode": "641002",
|
||||
"pickuplatitude": 11.004500, "pickuplongitude": 76.961200,
|
||||
"deliveryaddress": "12 Bharathi Rd, Peelamedu",
|
||||
"deliverypincode": "641004",
|
||||
"deliverylatitude": 11.051000, "deliverylongitude": 76.930000,
|
||||
"service_option": "Fast",
|
||||
"finalprice": 65,
|
||||
"parcels": [ { "itemcategory": "Food", "weight": 1.2 } ]
|
||||
}
|
||||
|
||||
→ { "bookingid": 118, "bookingno": "DM-BK-…", "status": "Created" }
|
||||
```
|
||||
|
||||
Writes a `pickupbookings` row plus its parcels and price.
|
||||
|
||||
**On `tenantlocationid`.** This is the client's own site — the kitchen. Omit it
|
||||
and it's resolved from the pickup coordinates: nearest stored site within 150m,
|
||||
falling back to an address match, nil when unsure. It is what per-kitchen
|
||||
reporting groups by.
|
||||
|
||||
`pickuplocationid` is accepted only as a legacy alias and is never stored as
|
||||
given — the column of that name foreign-keys to `appcustomerlocations` (a B2C
|
||||
customer's saved address), so writing a client site id into it fails the insert.
|
||||
|
||||
**CityGate.** The pickup pincode prefix must be an open city: `641` Coimbatore,
|
||||
`600` Chennai, `560` Bengaluru, `500` Hyderabad, `629` Nagercoil. Anything else
|
||||
is refused at creation. jupiter had no such gate.
|
||||
|
||||
## 2. Or create them in bulk
|
||||
|
||||
```
|
||||
POST /admin/expressbooking/bulk
|
||||
{ "bookings": [ { …same shape… }, { … } ] } // max 200
|
||||
|
||||
→ { "results": [
|
||||
{ "index": 0, "success": true, "bookingid": 119, "bookingno": "DM-BK-…" },
|
||||
{ "index": 1, "success": false, "error": "pickup pincode not serviceable" }
|
||||
] }
|
||||
```
|
||||
|
||||
Per-row results, never all-or-nothing — one bad address doesn't lose the other
|
||||
199. Each booking is its own transaction. A client login cannot use bulk to
|
||||
smuggle in another tenant's id; `tenantid` is pinned to the caller's own.
|
||||
|
||||
## 3. A rider is found — automatic
|
||||
|
||||
No call needed. Creation publishes `booking.assignment_requested` to JetStream
|
||||
after the transaction commits. A worker searches riders within 10km via Redis
|
||||
GEO, scores them through the AI layer, and commits the assignment. If nobody is
|
||||
available it retries **5 times, 2 minutes apart**, then publishes
|
||||
`booking.assignment_failed` for the dispatch agent.
|
||||
|
||||
The retry state lives in NATS, not in process memory, so a pod restart no longer
|
||||
loses a booking mid-wait.
|
||||
|
||||
To assign by hand instead:
|
||||
|
||||
```
|
||||
POST /admin/bookings/:id/assign-miler
|
||||
{ "mileruserid": 38 }
|
||||
```
|
||||
|
||||
## 4. Clear a hub queue, and order the stops
|
||||
|
||||
```
|
||||
POST /hub/bookings/batch-assign
|
||||
{ "bookingids": [118, 119, 120], "max_per_rider": 5 }
|
||||
|
||||
→ { "assigned": 3, "skipped": 0, "riderssequenced": 1,
|
||||
"results": [ { "bookingid": 118, "assigned": true,
|
||||
"mileruserid": 38, "distance_km": 1.4 } ] }
|
||||
```
|
||||
|
||||
**This is the only place stops get ordered.** After assigning, each affected
|
||||
rider's whole active set is sent to the Route Optimization API
|
||||
(`POST /api/v1/optimization/doormile/sequence` on `routes.workolik.com`), which
|
||||
returns a road-network sequence via Valhalla — not straight-line distance. The
|
||||
step, per-leg distance, cumulative distance and ETA are written onto
|
||||
`bookingassignments`.
|
||||
|
||||
**Assignment picks _who_; sequencing picks _what order_.** They are separate,
|
||||
and sequencing can only run once a rider is known — which is why bulk *creation*
|
||||
cannot sequence anything, however many bookings you send. jupiter got away with
|
||||
sequencing inside `createdeliveries` because that payload was already one
|
||||
rider's run; Doormile's bulk endpoint is up to 200 bookings across many riders.
|
||||
|
||||
Sequencing is best-effort and runs after the assignments commit: the optimizer
|
||||
is a separate service over the network, and it being down must leave bookings
|
||||
assigned but unordered, never undo the batch.
|
||||
|
||||
## 5. The rider runs the route
|
||||
|
||||
```
|
||||
GET /miler/assignments
|
||||
|
||||
→ [ { "bookingassignmentid": 555, "bookingid": 120,
|
||||
"step": 1, "previouskms": 4.0, "cumulativekms": 4.0,
|
||||
"etaminutes": 14, "cumulativeeta": 14 }, … ]
|
||||
```
|
||||
|
||||
Returned in **step order**. `step: 0` means *not sequenced* — never *first* — and
|
||||
sorts to the end. A rider with one stop is never sequenced, so 0 is common.
|
||||
|
||||
Then, per booking:
|
||||
|
||||
```
|
||||
POST /miler/assignments/:id/accept
|
||||
POST /miler/bookings/:bookingid/reached
|
||||
|
||||
POST /miler/bookings/:bookingid/parcel
|
||||
{ "parcels": [ { "weight": 1.4, "length": 20, "width": 15, "height": 10 } ] }
|
||||
|
||||
POST /miler/bookings/:bookingid/payment
|
||||
{ "amount": 65, "paymentmode": "Cash", "transactionref": "" }
|
||||
|
||||
POST /miler/bookings/:bookingid/pickup-complete
|
||||
```
|
||||
|
||||
`pickup-complete` is the pivot the old system had no concept of. It converts the
|
||||
booking into a **consignment**, recomputes chargeable weight from the dimensions
|
||||
the rider actually measured, carries the kitchen across, and decides routing —
|
||||
matching 3-digit pincode prefixes go straight to `Out_for_Delivery` (hyperlocal),
|
||||
everything else routes via a hub.
|
||||
|
||||
Other rider actions: `vehicle-required` (needs a bigger vehicle), `cancel`
|
||||
(before pickup).
|
||||
|
||||
## 6. Deliver
|
||||
|
||||
```
|
||||
POST /miler/consignments/:id/deliver
|
||||
{ "deliveredtoname": "Priya R",
|
||||
"photourl": "https://…", "receiversignatureurl": "https://…",
|
||||
"lat": 11.051, "lon": 76.93,
|
||||
"otp": "418317" } // only when the tenant requires it
|
||||
```
|
||||
|
||||
Marks the consignment delivered and writes a history row. **Delivery OTP is
|
||||
opt-in per tenant** (`Tenant.Requiredeliveryotp`, default off — off for
|
||||
DailyGrubs). When on it is verified server-side and never serialised outward:
|
||||
returning it would hand the rider the code they are meant to be told.
|
||||
|
||||
Couldn't deliver? `POST /miler/consignments/:id/skip` increments `attemptcount`
|
||||
rather than failing the parcel.
|
||||
|
||||
---
|
||||
|
||||
## Watching it
|
||||
|
||||
| What | Endpoint |
|
||||
|---|---|
|
||||
| One booking end to end | `GET /admin/bookings/:id/track` |
|
||||
| A parcel's GPS trail + history | `GET /admin/consignments/:id/logs` |
|
||||
| Riders today | `GET /admin/milers/summary?from=&to=` |
|
||||
| One rider's logs | `GET /admin/milers/:id/logs` |
|
||||
| Per kitchen | `GET /admin/locations/summary?tenantid=&locationid=` |
|
||||
| Reports | `GET /admin/reports?from=&to=&tenantid=&locationid=&hubid=` |
|
||||
|
||||
Admin miler endpoints key on **`milerprofileid`**, while `assign-miler` takes a
|
||||
**`mileruserid`** in the body — different identity spaces on adjacent endpoints.
|
||||
Worth checking which one you have.
|
||||
|
||||
---
|
||||
|
||||
## State
|
||||
|
||||
| Capability | State |
|
||||
|---|---|
|
||||
| Booking create, bulk, tracking, reports | deployed |
|
||||
| Tenant scoping for client logins | deployed |
|
||||
| Per-kitchen attribution | deployed |
|
||||
| Durable assignment retry (JetStream) | built, **not deployed** |
|
||||
| Stop sequencing (Doormile side) | built, **not deployed** |
|
||||
| `/optimization/doormile/sequence` (routes.workolik.com) | built, **not deployed** |
|
||||
| Multi-stop optimizer service itself | live |
|
||||
|
||||
**Sequencing needs two deploys, not one** — the endpoint in the route-optimizer
|
||||
service (docker-compose on `31.97.228.132`, behind Traefik) and Doormile's
|
||||
client that calls it. Ship one without the other and sequencing fails quietly:
|
||||
bookings stay assigned but unordered, and riders choose their own order.
|
||||
|
||||
Nothing on the client side has moved. The rider app and express console still
|
||||
call `jupiter.nearle.app`. These endpoints exist and are tested; no production
|
||||
traffic uses them yet.
|
||||
Reference in New Issue
Block a user