The Admin Console derives Arrived from Pickup_Scheduled + reachedat != null,
but GetAdminBookings serializes the PickupBooking struct raw and the arrival
field emitted json arrivedat, not reachedat, so the console never saw the
arrival. Align the wire name to reachedat (miler app and /reached already use
it); DB column stays arrivedat.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WRaFH5hMRqmUQvVPQsyjZD
- GET /miler/bookings now returns reachedat + arrivallatitude/arrivallongitude
on every row, so the app reconstructs "Arrived" (Pickup_Scheduled + reachedat)
after a restart with no new status.
- reached records arrival as a FACT (timestamp + GPS) and no longer flips the
booking to Arrived_At_Pickup — the status stays Pickup_Scheduled, matching the
rider app's derive-from-reachedat model and dropping the console mapping need.
- New PATCH /miler/bookings/:id/addresses: partial pickup/delivery address,
pincode, coords, city correction before pickup-complete (INVALID_STATE after).
- pickupbookings gains nullable arrivedat/arrivallatitude/arrivallongitude
(AutoMigrate, additive).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WRaFH5hMRqmUQvVPQsyjZD
Miler app P0 + contract gaps found in the live audit:
- GET /miler/bookings now returns consignmentid + consignmentstatus on every
row (nullable), so the app can call deliver/skip/start-delivery straight from
the list. /miler/assignments is the active-only queue, so this is the
authoritative fix for stops that have moved onto the delivery leg.
- GET /miler/bookings now returns sequencedat per row: non-null means the
console/optimizer fixed this stop's order and the app follows step exactly;
null means no route assigned and the app may fall back to nearest-first.
- Route sequencing (internal/routing) now runs automatically after every
assignment — customer auto-assign, express auto-assign, manual assign, and
accept — via SequenceMilerStopsAsync (fire-and-forget, no-op below two active
stops). Previously only hub batch-assign sequenced, so most riders saw step=0.
- GET /admin/bookings now surfaces the live consignmentstatus alongside the
frozen booking status, so a Converted_To_Consignment booking can still show
Out_for_Delivery / Delivered instead of a generic "Active".
Two-step hyperlocal flow (Arrived_At_Pickup, Collected_By_Miler, start-delivery)
stays gated behind MILER_COLLECTED_STATE_ENABLED (default off) until the app
ships; consignmentid/status, GET /miler/consignments/:id, stable error codes and
Idempotency-Key handling are unconditional and safe on the current app.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WRaFH5hMRqmUQvVPQsyjZD
Assignment decided who carried a booking but never what order to run
several of them in -- the one capability jupiter had that Doormile did
not. It turns out we already own the solver: routes.workolik.com is a
live in-house Route Optimization API backed by Valhalla road-network
routing, not the paid third party I had assumed. This is a client for
it, not a solver.
internal/routing posts a rider's active stops and writes back step,
previouskms, cumulativekms and ETA onto BookingAssignment. HubBatchAssign
calls it after committing a batch, which is exactly the case it exists
for: a rider used to walk away with several bookings and no order to run
them in. GetMilerAssignments now returns sequenced stops in step order,
falling back to newest-first for anything unsequenced.
Contract discovered by probing the live service -- the OpenAPI schema
types the body as a bare object array, so the field names are not
documented anywhere. They are pickuplat/pickuplong/deliverylat/
deliverylong, NOT pickuplatitude/deliverylatitude. Sending the wrong
names does not fail: it returns HTTP 200 with every coordinate defaulted
to "0.0", no reordering and all distances zero. That trap is recorded in
a comment so the next person does not lose an afternoon to it.
Numeric fields come back inconsistently typed -- previouskms as a number,
actualkms and eta as strings, some decimal -- so they are decoded loosely
and coerced, with tests pinning the coercion. Steps for deliveryids we
did not send are discarded rather than written, so an echoed or stale id
cannot reorder another rider's work.
Sequencing is best-effort throughout and runs after assignments commit.
The optimizer is a separate service over the network; it being down must
leave bookings assigned but unordered, never undo the batch. Step 0 means
"not sequenced", not "first".
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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>
Adds MilerDutyLog, MilerBreakLog, MilerSupportTicket models and earnings
fields on BookingAssignment, plus a new milerAppController.go wiring 10
endpoints under /api/v1/miler for the rider app: duty start/end/status,
break start/end, own-bookings listing, delivery confirmation (writes
DeliveryProof, completes the assignment, publishes booking.outcome via
NATS, and pushes an FCM delivery notification), earnings summaries
(daily/weekly/monthly), synthetic notifications, and support tickets.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>