feat: per-site reporting, and actually populate the site on a booking
jupiter's getreportsummary took a locationid — per kitchen, per branch. That was the one report parameter with no Doormile equivalent, and for a food client with 23 kitchens it is the difference between one number and a usable report. GET /admin/reports?locationid= narrows every figure to one site GET /admin/reports now carries a by_location block GET /admin/locations/summary the standalone per-site table The filter alone would have been useless: pickuplocationid was null on every booking in the system, because the console sends a kitchen's address rather than its id. createExpressBooking now resolves the site itself — nearest stored location within 150m, falling back to an address match, nil when nothing matches confidently, since a wrong attribution silently moves orders between kitchens. An explicit pickuplocationid still wins. Bookings that named no site are reported as their own "Unattributed" row rather than dropped, so per-site rows add up to the summary total. Two fixes found while in here: - the payments join in the per-site query fanned out, counting a booking once per payment row; payments are now pre-aggregated per booking - by_rider was empty for every client login, which reads as "your riders did nothing". Riders are tenant-scoped now, so a client sees its own. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
# Doormile Express Console — API reference
|
||||
|
||||
The console surface only (`/admin/*`). 94 routes: 1 login + 93 authenticated.
|
||||
The console surface only (`/admin/*`). 95 routes: 1 login + 94 authenticated.
|
||||
Everything is under `https://api.doormile.com/api/v1`.
|
||||
|
||||
Verified against the build deployed 2026-08-06 12:41 IST.
|
||||
@@ -65,7 +65,7 @@ comment in `routes.go`; the comment is wrong, `AuthMiddleware` is applied.
|
||||
| Method | Path | Notes |
|
||||
|---|---|---|
|
||||
| GET | `/admin/dashboard` | counts + today's numbers |
|
||||
| GET | `/admin/reports` | `?from=YYYY-MM-DD&to=YYYY-MM-DD`, defaults to today (IST) |
|
||||
| GET | `/admin/reports` | `?from=&to=&tenantid=&locationid=&hubid=`, defaults to today (IST) |
|
||||
| GET | `/admin/profile` | current account |
|
||||
| GET | `/admin/me` | alias of the above |
|
||||
| PUT | `/admin/profile/password` | `{ "current_password": "...", "new_password": "..." }` — snake_case |
|
||||
@@ -102,12 +102,36 @@ is a client Doormile delivers for.
|
||||
| PUT | `/admin/tenants/:id` | `requiredeliveryotp` is a pointer — omit it to leave the setting alone |
|
||||
| DELETE | `/admin/tenants/:id` | hard delete |
|
||||
| GET | `/admin/tenants/:id/locations` | the client's sites (kitchens, branches, depots) |
|
||||
| GET | `/admin/locations/summary` | **per-site performance** — `?tenantid=&locationid=&from=&to=` |
|
||||
| POST | `/admin/tenants/:id/locations` | `{ locationname, address, city, state, pincode, latitude, longitude, isprimary, status }` |
|
||||
| PUT | `/admin/tenantlocations/:id` | note: **not** nested under the tenant |
|
||||
|
||||
`requiredeliveryotp` is opt-in per tenant and **off by default**. DailyGrubs runs
|
||||
without delivery OTP by decision.
|
||||
|
||||
### Per-site reporting
|
||||
|
||||
`GET /admin/locations/summary?tenantid=13&from=&to=` returns one row per site:
|
||||
|
||||
```jsonc
|
||||
{ "tenantlocationid": 13, "locationname": "Vidhya kitchen",
|
||||
"address": "…", "pincode": "641015",
|
||||
"bookings": 12, "delivered": 11, "cancelled": 1, "cod_collected": 840 }
|
||||
```
|
||||
|
||||
Sites with no orders in the range still appear, with zeros. A trailing
|
||||
`"Unattributed"` row (`tenantlocationid: null`) carries bookings that never
|
||||
named a site, so the rows always add up to the report's summary total.
|
||||
|
||||
Doormile staff **must** pass `?tenantid=` here — per-site rows across all
|
||||
tenants at once aren't a meaningful report, so it 400s without one.
|
||||
|
||||
The same rows appear as `by_location` inside `GET /admin/reports`.
|
||||
|
||||
**Send `pickuplocationid` on bookings.** Attribution depends on it. The server
|
||||
will try to recognise the site from the pickup coordinates (within 150m) or a
|
||||
matching address, but an explicit id is exact and always wins.
|
||||
|
||||
## Tenant customers (a client's own end customers)
|
||||
|
||||
| Method | Path | Body |
|
||||
|
||||
@@ -90,13 +90,26 @@ Two jupiter bugs that do not carry over, by construction:
|
||||
|
||||
| jupiter | Doormile | Status |
|
||||
|---|---|---|
|
||||
| ✅ `GET /deliveries/getreportsummary/?applocationid=&tenantid=&locationid=&fromdate=&todate=` | `GET /admin/reports?from=&to=&tenantid=&hubid=` | **Done** |
|
||||
| ✅ `GET /deliveries/getreportsummary/?applocationid=&tenantid=&locationid=&fromdate=&todate=` | `GET /admin/reports?from=&to=&tenantid=&locationid=&hubid=` | **Done** |
|
||||
| ~ `getlocationsummary` | `GET /admin/locations/summary?tenantid=&locationid=&from=&to=` | **Done** |
|
||||
| — | `GET /admin/dashboard?tenantid=` | **Done** |
|
||||
|
||||
**One parameter has no equivalent yet: `locationid`.** jupiter could report per
|
||||
client *site* — per kitchen, for a food client. Doormile carries
|
||||
`pickuplocationid` on both the booking and the consignment, so the data is
|
||||
there, but `/admin/reports` does not group or filter by it. See §6.
|
||||
`locationid` is supported: it narrows every figure to one client site, and
|
||||
`/admin/reports` now carries a `by_location` block alongside `by_hub`,
|
||||
`by_tenant` and `by_rider`.
|
||||
|
||||
**Attribution caveat.** Per-site figures depend on `pickuplocationid` being set
|
||||
on the booking. Every booking created before 2026-08-06 has it null, and the
|
||||
console sends a kitchen's *address*, not its id. So `createExpressBooking` now
|
||||
resolves the site itself — nearest stored location within 150m, falling back to
|
||||
an address match — and unattributed bookings are reported as their own
|
||||
`"Unattributed"` row rather than dropped, so the per-site rows still add up to
|
||||
the summary total. Sending `pickuplocationid` explicitly is still better and
|
||||
always wins.
|
||||
|
||||
**`applocationid` (city) is still not a report parameter.** jupiter had it;
|
||||
Doormile filters by `hubid` instead. Only matters once one client runs in more
|
||||
than one city.
|
||||
|
||||
### 2.4 Tenants and their sites
|
||||
|
||||
@@ -207,8 +220,7 @@ rewriting, not repointing. Beyond that:
|
||||
|
||||
| What | Detail |
|
||||
|---|---|
|
||||
| **Per-site reporting** | jupiter's `getreportsummary` took `locationid`. Doormile stores `pickuplocationid` on bookings and consignments but neither groups nor filters by it. For a food client this is "how many orders went out of which kitchen" — likely the first thing DailyGrubs asks for. |
|
||||
| `getlocationsummary` | No per-site rollup endpoint. |
|
||||
| `applocationid` on reports | jupiter could filter a report by city. Doormile filters by `hubid`. Only bites when one client operates in several cities. |
|
||||
| **Route optimisation** | jupiter used external paid services (`routes.workolik.com`) for multi-stop sequencing. Nothing in Doormile replaces true stop-ordering. `HubBatchAssign` decides *who* gets a booking, not *what order* to run stops in. |
|
||||
| Notifications read-state | `PATCH /miler/notifications/:id/read` is a stub; no table exists. |
|
||||
| `riderkms` / `ridercharges` backfill | Populated on new deliveries only. Rows completed before 2026-08-06 read 0 and will not backfill themselves. |
|
||||
|
||||
Reference in New Issue
Block a user