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>
Security
- Express console had no tenant scoping at all: LoginAdmin hardcoded tenantid 0
into every JWT and none of the 85 admin handlers filtered by tenant, so any
client given a console login would read every other client's bookings,
customers, pricing and reports. Adds DoormileAuth.Tenantid (nil = Doormile
staff, unrestricted; set = client, scoped), emits it in the token, and scopes
reads, guards writes and pins tenantid on create.
- Miler telemetry (/miler/logs, /miler/status, /miler/consignments/logs) took
userid from the request body, letting any authenticated rider write another
rider's status and GPS trail — data the dispatch layer reasons over. Identity
now comes from the token.
- POST /miler/reset-pin was unauthenticated and overwrote a PIN given only a
phone number, so reset-pin + verify-pin took over any rider account. Now
requires admin/manager/executive auth.
Correctness
- Date ranges compared the container's UTC clock against timestamps the DB
writes as IST wall-clock (DSN sets TimeZone=Asia/Kolkata), so "today so far"
ended 5h30m in the past and silently dropped everything created after noon
IST from every report. Sets TZ in the image and adds utils.DBNow/DBToday,
which stay correct regardless of container timezone.
- CreateMiler never set Configid, so console-created riders got the column
default of 1 while LoginMiler looks up configid 1001 — every such rider was
unable to log in, reported as "no miler account found".
- Delivery wrote no consignment history row, so a tracking timeline never
showed the parcel arriving.
Features
- Delivery OTP is now real (crypto/rand, issued to the receiver, verified and
cleared on delivery) but opt-in per client via Tenant.Requiredeliveryotp,
defaulting off — friction worth it for a courier parcel, not a food order.
- Express bookings accept pickuplocationid, so the console can name a client
site (a DailyGrubs kitchen) instead of retyping its address; validated
against the tenant and carried through to the consignment.
- TenantLocation.Locationname, miler tenantid/hubid, Nagercoil (629) opened.
- PUT /miler/availability accepts both "status" and "availabilitystatus", and
/miler/location no longer drops speed/heading — both were contract
mismatches against the doc the Flutter dev was given.
Co-Authored-By: Claude Opus 5 (1M context) <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>
- models/agentdecision.go: AgentDecision GORM model (context/decision as jsonb, reasoning as text)
- migrations/migrate.go: AutoMigrate AgentDecision then ALTER TABLE to add vector(1536) column and CREATE ivfflat index via raw SQL
- controllers/agentDecisionController.go: CreateAgentDecision, FindSimilarDecisions (cosine distance), UpdateDecisionOutcome
- routes/routes.go: three routes under /api/v1/internal (InternalKeyAuth applied at group level)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- pricingid null: lookupDoormilePrice now returns matched rule ID; wired to BookingServiceOption.Pricingid
- Zone rename: Interstate→Regional, OtherState→National throughout (code + DB migrated)
- Zone from pincodes: CheckPrice now accepts pickup_pincode+delivery_pincode and auto-resolves zone
- Delivery geocoding: pincodeToLatLon() maps 3-digit prefix to city coords when lat/lon are 0
- Device tokens: device_token field added to PinVerify DTOs; saved on both customer and miler login
- Assignment retry: RejectMilerAssignment now re-triggers AssignCustomerMiler/AssignCRMMiler immediately
- Provider empty B2C: defaults to Doormile when no pricing provider row matches
- City gate 422→400: StatusUnprocessableEntity corrected to StatusBadRequest
- Miler GPS 0,0: WS tracking falls back to MilerProfile DB coords when Redis key is expired
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>