Fix 9 backend bugs: pricing, zones, geocoding, device tokens, assignment retry

- 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>
This commit is contained in:
2026-06-26 11:47:41 +05:30
parent c91c887726
commit 9d409a0d85
9 changed files with 116 additions and 40 deletions

View File

@@ -46,17 +46,44 @@ func pincodeToState(pincode string) string {
}
// resolveZone determines the DoormilePricing zone from pickup and delivery pincodes.
// - Local — same 3-digit prefix (same city/sorting district)
// - Interstate — different city, same state
// - OtherState — different state
// - Local — same 3-digit prefix (same city/sorting district)
// - Regional — different city, same state
// - National — different state
func resolveZone(pickupPincode, deliveryPincode string) string {
if len(pickupPincode) >= 3 && len(deliveryPincode) >= 3 && pickupPincode[:3] == deliveryPincode[:3] {
return "Local"
}
if pincodeToState(pickupPincode) == pincodeToState(deliveryPincode) {
return "Interstate"
return "Regional"
}
return "OtherState"
return "National"
}
// pincodeToLatLon returns approximate coordinates for a pincode using its
// 3-digit postal division. Returns ok=false for unrecognised pincodes.
func pincodeToLatLon(pincode string) (lat, lon float64, ok bool) {
if len(pincode) < 3 {
return
}
p, err := strconv.Atoi(pincode[:3])
if err != nil {
return
}
switch {
case p >= 500 && p <= 535:
return 17.385044, 78.486671, true // Hyderabad / Telangana
case p >= 560 && p <= 591:
return 12.971599, 77.594566, true // Bengaluru / Karnataka
case p >= 600 && p <= 643:
return 13.082680, 80.270718, true // Chennai / Tamil Nadu
case p >= 670 && p <= 695:
return 10.850516, 76.271080, true // Kerala
case p >= 380 && p <= 396:
return 23.022505, 72.571365, true // Ahmedabad / Gujarat
case p >= 400 && p <= 444:
return 19.075984, 72.877656, true // Mumbai / Maharashtra
}
return
}
// normalizePricingCategory returns a DoormilePricing-compatible category string.
@@ -81,8 +108,8 @@ func mapServiceTypeToPricing(serviceType string) string {
// lookupDoormilePrice fetches pricing for the given zone/serviceType/weight/category
// from Redis first, falling back to Postgres on a cache miss.
// Returns the midpoint of the matched price band and true, or 0 and false if no rule matches.
func lookupDoormilePrice(zone, pricingServiceType string, weight float64, category string) (float64, bool) {
// Returns (midpoint price, pricingID, true) on match, or (0, nil, false) when no rule matches.
func lookupDoormilePrice(zone, pricingServiceType string, weight float64, category string) (float64, *int, bool) {
var allRules []models.DoormilePricing
if db.Rdb != nil {
@@ -102,7 +129,7 @@ func lookupDoormilePrice(zone, pricingServiceType string, weight float64, catego
var err error
allRules, err = loadFromPostgres(zone, pricingServiceType)
if err != nil || len(allRules) == 0 {
return 0, false
return 0, nil, false
}
}
@@ -111,9 +138,10 @@ func lookupDoormilePrice(zone, pricingServiceType string, weight float64, catego
matched = applyFilters(allRules, weight, "General")
}
if len(matched) == 0 {
return 0, false
return 0, nil, false
}
rule := matched[0]
return (rule.Minprice + rule.Maxprice) / 2, true
id := rule.Doormile_pricing_id
return (rule.Minprice + rule.Maxprice) / 2, &id, true
}