Caught by testing the previous commit against production: creating a booking
with a resolved site failed with
pickupbookings_pickuplocationid_fkey
FOREIGN KEY (pickuplocationid) REFERENCES appcustomerlocations(...)
pickuplocationid is the *customer's* saved address, a B2C concept. It never
referred to the client company's own kitchens or branches. The pre-existing
code that validated an incoming pickuplocationid against TenantLocation was
wrong on the same point and would have 500'd for any caller that used it — it
had simply never been called with a value.
Adds tenantlocationid to pickupbookings and consignments (nullable, indexed,
additive via AutoMigrate), carried across at pickup, and points the reporting
filter, the by_location breakdown and the Unattributed bucket at it.
The booking request accepts tenantlocationid, and still accepts
pickuplocationid as an alias so anything written against the earlier docs
starts working instead of failing.
Also gofmt on the two model files touched; booking.go was already failing.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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>
Maps every jupiter endpoint we have replaced to its Doormile equivalent, for
the express console and the miler app only. Marks which jupiter paths were
confirmed from live network logs versus taken from the prior codebase
analysis, and states per row whether the Doormile side has been hit with a
real request or only compiles.
Includes the 11-way decomposition of PUT /deliveries/updatedelivery, the
behaviour changes that break a naive repoint, and the gaps jupiter covered
that Doormile does not yet — per-site reporting being the notable one.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Adds the five rider/tracking endpoints, the ?tenantid= staff filter and the
403-vs-404 refusal rules, and replaces the guesswork coverage note with what
was actually run against production on 2026-08-06.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A client login could list its own bookings but had no way to see what its
riders were actually doing. jupiter's console gave them getridersummary and
the rider/delivery logs; Doormile records all of it and exposed none of it.
New console endpoints, all tenant-scoped:
GET /admin/milers/summary roster with live state + range totals
GET /admin/milers/:id/logs GPS trail from the Redis telemetry index
GET /admin/milers/:id/activity one rider's assignments, duty and breaks
GET /admin/consignments/:id/logs event history + telemetry + proof
GET /admin/bookings/:id/track booking -> assignments -> parcel -> proof
Also closes a rider IDOR: GetMilers scoped the roster to the caller's own
fleet, but reading, editing, blocking, notifying or assigning a vehicle to a
single rider by id did not, so a client login could walk the whole network's
riders by incrementing the id. All five now go through assertMilerAccess.
And the client dashboard no longer reports milers/customers/exceptions as
zero — those have no tenant column, so they are counted through appusers,
bookings and consignments respectively.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Captures the working request bodies for the customer login/booking and the
admin/hub assign-miler endpoints, plus the constraints that are easy to hit
and hard to diagnose:
- CityGate only accepts pickup pincodes in 641/600/560/500
- hub assign-miler 403s unless milerprofiles.hubid matches the calling hub,
and most milers have hubid NULL
- CreateCustomerBooking auto-assigns in the background, so a booking is often
already on a miler before you assign one
- the routing watchdog sweeps unaccepted assignments within ~a minute and may
re-assign under a new bookingassignmentid, so accept IDs must be re-read
- reject takes its reason in the body, not the query string
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>