updates on the api endpoints on the customer page and more

This commit is contained in:
2026-09-02 16:32:53 +05:30
parent 8e2c484bcb
commit d12629a1e4
31 changed files with 3541 additions and 121 deletions

View File

@@ -0,0 +1,141 @@
package routing
import (
"testing"
"doormile/internal/legs"
)
// Where a rider's leg actually ends.
//
// The bug these tests exist to prevent, found while testing the base-handover
// flow with a bulk sheet of intercity orders: the sequencer read
// pickupbookings.deliverylatitude for every active assignment, with no idea
// whether the parcel was hub-routed. For a Coimbatore → Chennai booking that
// told the route optimizer the rider was riding 430 km to the receiver, when
// the rider's real next stop is a base a few kilometres away.
//
// Wrong on its own — a 430 km "stop" is not a stop anyone makes — and worse in
// company: one such destination in a rider's set drags the ordering of every
// genuine local stop beside it, because the solver is optimising a journey
// nobody is going to make.
// Coimbatore pickup, and the local base a rider hands parcels to.
const (
pickPin = "641025"
pickLat, pickLng = 11.0168, 76.9558
baseLat, baseLng = 11.0272, 76.9905
localPin = "641004"
localLat, localLng = 11.029, 76.993
chennaiPin = "600001"
chennaiLat, chennaiLng = 13.091, 80.285
)
func TestDropForLeg(t *testing.T) {
cases := []struct {
name string
deliveryPin string
dLat, dLng float64
baseLat, baseLng float64
wantLat, wantLng float64
wantOK bool
why string
}{
{
name: "a hyperlocal parcel ends at the receiver",
deliveryPin: localPin, dLat: localLat, dLng: localLng,
baseLat: baseLat, baseLng: baseLng,
wantLat: localLat, wantLng: localLng, wantOK: true,
why: "same postal area — the collecting rider carries it all the way",
},
{
name: "an intercity parcel ends at the base, not in Chennai",
deliveryPin: chennaiPin, dLat: chennaiLat, dLng: chennaiLng,
baseLat: baseLat, baseLng: baseLng,
wantLat: baseLat, wantLng: baseLng, wantOK: true,
why: "this is the whole fix — the rider rides to the base",
},
{
name: "a nearby but different postal area still ends at the base",
deliveryPin: "642001", dLat: 10.658, dLng: 77.008,
baseLat: baseLat, baseLng: baseLng,
wantLat: baseLat, wantLng: baseLng, wantOK: true,
why: "Pollachi is 40km away; the prefix rule decides, not the distance",
},
{
name: "no pincode and a short hop still ends at the receiver",
deliveryPin: "", dLat: 11.05, dLng: 77.01,
baseLat: baseLat, baseLng: baseLng,
wantLat: 11.05, wantLng: 77.01, wantOK: true,
why: "the distance fallback puts it inside 30km",
},
{
name: "no pincode and a long haul ends at the base",
deliveryPin: "", dLat: chennaiLat, dLng: chennaiLng,
baseLat: baseLat, baseLng: baseLng,
wantLat: baseLat, wantLng: baseLng, wantOK: true,
why: "the distance fallback puts it far outside 30km",
},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
lat, lng, ok := dropForLeg(pickPin, tc.deliveryPin,
pickLat, pickLng, tc.dLat, tc.dLng, tc.baseLat, tc.baseLng)
if ok != tc.wantOK {
t.Fatalf("ok = %v, want %v — %s", ok, tc.wantOK, tc.why)
}
if lat != tc.wantLat || lng != tc.wantLng {
t.Errorf("drop = (%v, %v), want (%v, %v) — %s", lat, lng, tc.wantLat, tc.wantLng, tc.why)
}
})
}
}
func TestDropForLegNeverSendsARiderInterstate(t *testing.T) {
// The regression guard, stated as the thing that actually matters rather
// than as a coordinate comparison: whatever the destination, the drop the
// sequencer is given must be somewhere a rider can plausibly ride to.
for _, d := range []struct {
name string
pin string
lat, lng float64
}{
{"Chennai", "600001", 13.091, 80.285},
{"Hyderabad", "500081", 17.44, 78.3489},
{"Bengaluru", "560066", 12.9698, 77.75},
{"Madurai", "625001", 9.9195, 78.119},
} {
t.Run(d.name, func(t *testing.T) {
lat, lng, ok := dropForLeg(pickPin, d.pin, pickLat, pickLng, d.lat, d.lng, baseLat, baseLng)
if !ok {
t.Fatal("a stop with a usable base must still be sequenced")
}
if lat == d.lat && lng == d.lng {
t.Fatalf("%s was passed to the optimizer as the rider's own drop", d.name)
}
if km := legs.HaversineKM(pickLat, pickLng, lat, lng); km > 50 {
t.Errorf("drop is %.0f km from the pickup — no rider is making that leg", km)
}
})
}
}
func TestDropForLegSkipsAHubRoutedStopWithNoBase(t *testing.T) {
// Falling back to the receiver here would be the original bug wearing a
// different hat: better to leave one stop unsequenced than to skew the
// ordering of every other stop the rider is carrying.
_, _, ok := dropForLeg(pickPin, chennaiPin, pickLat, pickLng, chennaiLat, chennaiLng, 0, 0)
if ok {
t.Error("a hub-routed stop with no base coordinates must be left out, not pointed at the receiver")
}
}
func TestDropForLegKeepsAHyperlocalStopWithNoBase(t *testing.T) {
// A hyperlocal parcel never needed a base, so a missing one is irrelevant
// to it and must not cost the rider a stop.
lat, lng, ok := dropForLeg(pickPin, localPin, pickLat, pickLng, localLat, localLng, 0, 0)
if !ok || lat != localLat || lng != localLng {
t.Errorf("hyperlocal stop = (%v, %v, %v), want the receiver and ok", lat, lng, ok)
}
}

View File

@@ -17,6 +17,7 @@ import (
"doormile/constants"
"doormile/db"
"doormile/internal/legs"
"doormile/models"
"doormile/utils"
)
@@ -183,13 +184,31 @@ func loadActiveStops(milerUserID int) ([]stop, error) {
Pickuplongitude float64
Deliverylatitude float64
Deliverylongitude float64
Pickuppincode string
Deliverypincode string
// The base this rider would hand a hub-routed parcel to: the one the
// booking was routed to if anything set it, otherwise the rider's own.
// Same order of preference as controllers.resolveHandoverHub.
Baselatitude float64
Baselongitude float64
}
// A rider's leg does NOT always end at the address on the booking. For an
// intercity parcel the rider carries it to a base and hands it over there;
// the receiver is somebody else's problem, on another vehicle, days later.
// The base coordinates are joined in here so the sequencer can use them as
// the real end of the leg — see the substitution below.
if err := db.DB.Table("bookingassignments AS ba").
Select(`ba.bookingassignmentid, ba.bookingid, b.bookingno,
b.pickuplatitude, b.pickuplongitude,
b.deliverylatitude, b.deliverylongitude`).
b.deliverylatitude, b.deliverylongitude,
b.pickuppincode, b.deliverypincode,
COALESCE(bh.latitude, rh.latitude, 0) AS baselatitude,
COALESCE(bh.longitude, rh.longitude, 0) AS baselongitude`).
Joins("JOIN pickupbookings AS b ON b.bookingid = ba.bookingid").
Joins("LEFT JOIN hubs AS bh ON bh.hubid = b.nearesthubid AND bh.deletedat IS NULL").
Joins("LEFT JOIN milerprofiles AS mp ON mp.userid = ba.mileruserid").
Joins("LEFT JOIN hubs AS rh ON rh.hubid = mp.hubid AND rh.deletedat IS NULL").
Where("ba.mileruserid = ? AND ba.assignmentstatus IN ?",
milerUserID,
[]string{constants.AssignmentAssigned, constants.AssignmentAccepted}).
@@ -209,19 +228,60 @@ func loadActiveStops(milerUserID int) ([]stop, error) {
"assignment_id", r.Bookingassignmentid, "booking_id", r.Bookingid)
continue
}
dropLat, dropLng, ok := dropForLeg(
r.Pickuppincode, r.Deliverypincode,
r.Pickuplatitude, r.Pickuplongitude,
r.Deliverylatitude, r.Deliverylongitude,
r.Baselatitude, r.Baselongitude)
if !ok {
// Routed to a base, but no base has usable coordinates. Ordering it
// against the far-away receiver would distort every other stop, so it
// is left out of the route rather than allowed to skew it.
utils.Warn("routing: hub-routed stop has no base coordinates, leaving it unsequenced",
"assignment_id", r.Bookingassignmentid, "booking_id", r.Bookingid)
continue
}
stops = append(stops, stop{
AssignmentID: r.Bookingassignmentid,
BookingID: r.Bookingid,
BookingNo: r.Bookingno,
PickupLat: r.Pickuplatitude,
PickupLng: r.Pickuplongitude,
DeliveryLat: r.Deliverylatitude,
DeliveryLng: r.Deliverylongitude,
DeliveryLat: dropLat,
DeliveryLng: dropLng,
})
}
return stops, nil
}
// dropForLeg is where THIS rider's leg ends — which is not always the address
// on the booking.
//
// A hyperlocal parcel ends at the receiver. A hub-routed one ends at a base: the
// rider hands it over there and the receiver is somebody else's leg, on another
// vehicle, possibly days later. Sequencing a hub-routed parcel against the
// receiver's coordinates asks the optimizer to plan a ride to another state —
// wrong on its own terms, since a 430km "stop" is not a stop a rider makes, and
// worse in company: one intercity destination in the set drags the ordering of
// every genuine local stop beside it, because the solver is optimising a journey
// nobody is going to make.
//
// The final destination is not lost. It is simply not this leg.
//
// ok is false when the parcel is hub-routed and no base has usable coordinates.
// The caller drops the stop rather than falling back to the receiver, because a
// stop in the wrong country is more damaging to the route than a missing one.
func dropForLeg(pickupPincode, deliveryPincode string, pLat, pLng, dLat, dLng, baseLat, baseLng float64) (lat, lng float64, ok bool) {
if legs.IsHyperlocal(pickupPincode, deliveryPincode, pLat, pLng, dLat, dLng) {
return dLat, dLng, true
}
if baseLat != 0 || baseLng != 0 {
return baseLat, baseLng, true
}
return 0, 0, false
}
// optimize calls the Route Optimization API and maps its answer back onto our
// assignment ids.
func optimize(stops []stop) ([]Result, error) {