fix: miler assignment lifecycle, pickup hub resolution, and hyperlocal delivery
Bugs found during live Coimbatore testing against production: - GetMilerAssignments returned every assignment ever made to a miler with no status filter, so weeks-old Rejected assignments still showed up as actionable in the app. Now filters to Assigned/Accepted only. - AcceptMilerAssignment overwrote assignmentstatus unconditionally, letting a stale Rejected assignment be silently reactivated (and pushing its booking back to Pickup_Scheduled). Now 400s unless currently Assigned, reporting the actual status. - RejectMilerAssignment read `reason` from the query string instead of the JSON body, contradicting the API contract and every sibling endpoint. - BookingPickupComplete resolved the origin hub via db.First(&hub) with no Where clause — i.e. the lowest hub ID in the table, unrelated to the booking or miler. Now uses the miler's own MilerProfile.Hubid, falling back to the old behaviour with a warning only when unassigned. - No code path ever set a consignment to Out_for_Delivery, making MilerDeliverConsignment unreachable. BookingPickupComplete now goes straight to Out_for_Delivery when pickup and delivery pincodes share a 3-digit postal-area prefix (same-miler hyperlocal), reusing the hubPincodePrefix convention. Cross-hub still lands at Inwarded_at_Hub. Also allow https://app.doormile.com in CORS, and drop a stray Windows-path log file that was committed by accident. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,37 +0,0 @@
|
|||||||
{"level":"INFO","time":"2026-06-30T15:23:23+05:30","caller":"utils/logger.go:24","msg":"Starting Doormile Backend..."}
|
|
||||||
{"level":"INFO","time":"2026-06-30T15:23:23+05:30","caller":"utils/logger.go:24","msg":"Connecting to database","attempt":1,"host":"31.97.228.132","port":"5433"}
|
|
||||||
{"level":"INFO","time":"2026-06-30T15:23:24+05:30","caller":"utils/logger.go:24","msg":"✅ Database connected successfully"}
|
|
||||||
redis: 2026/06/30 15:23:24 redis.go:478: auto mode fallback: maintnotifications disabled due to handshake error: ERR unknown subcommand 'maint_notifications'. Try CLIENT HELP.
|
|
||||||
{"level":"INFO","time":"2026-06-30T15:23:24+05:30","caller":"utils/logger.go:24","msg":"✅ Redis connected successfully","addr":"31.97.228.132:6379"}
|
|
||||||
{"level":"INFO","time":"2026-06-30T15:23:24+05:30","caller":"utils/logger.go:24","msg":"✅ NATS JetStream connected successfully","url":"nats://66.116.226.161:4223"}
|
|
||||||
{"level":"WARN","time":"2026-06-30T15:23:24+05:30","caller":"utils/logger.go:36","msg":"FCM: FIREBASE_SERVICE_ACCOUNT_PATH not set — push notifications disabled"}
|
|
||||||
{"level":"INFO","time":"2026-06-30T15:23:24+05:30","caller":"utils/logger.go:24","msg":"Starting database auto-migrations for 21 logistics tables..."}
|
|
||||||
{"level":"INFO","time":"2026-06-30T15:23:45+05:30","caller":"utils/logger.go:24","msg":"✅ Database migration completed successfully!"}
|
|
||||||
{"level":"INFO","time":"2026-06-30T15:23:45+05:30","caller":"utils/logger.go:24","msg":"✅ context_embedding vector column ready"}
|
|
||||||
{"level":"INFO","time":"2026-06-30T15:23:45+05:30","caller":"utils/logger.go:24","msg":"✅ ivfflat index on context_embedding ready"}
|
|
||||||
{"level":"INFO","time":"2026-06-30T15:23:45+05:30","caller":"utils/logger.go:24","msg":"WarmPricingCache: warming pricing cache from Postgres..."}
|
|
||||||
{"level":"INFO","time":"2026-06-30T15:23:46+05:30","caller":"utils/logger.go:24","msg":"Pricing cache warmed","zone":"Local","servicetype":"Normal","count":10}
|
|
||||||
{"level":"INFO","time":"2026-06-30T15:23:46+05:30","caller":"utils/logger.go:24","msg":"Pricing cache warmed","zone":"Local","servicetype":"Express","count":9}
|
|
||||||
{"level":"INFO","time":"2026-06-30T15:23:46+05:30","caller":"utils/logger.go:24","msg":"Pricing cache warmed","zone":"Regional","servicetype":"Normal","count":9}
|
|
||||||
{"level":"INFO","time":"2026-06-30T15:23:46+05:30","caller":"utils/logger.go:24","msg":"Pricing cache warmed","zone":"Regional","servicetype":"Express","count":9}
|
|
||||||
{"level":"INFO","time":"2026-06-30T15:23:46+05:30","caller":"utils/logger.go:24","msg":"Pricing cache warmed","zone":"National","servicetype":"Normal","count":9}
|
|
||||||
{"level":"INFO","time":"2026-06-30T15:23:46+05:30","caller":"utils/logger.go:24","msg":"Pricing cache warmed","zone":"National","servicetype":"Express","count":9}
|
|
||||||
{"level":"INFO","time":"2026-06-30T15:23:46+05:30","caller":"utils/logger.go:24","msg":"WarmPricingCache: done","slabs_warmed":6}
|
|
||||||
{"level":"INFO","time":"2026-06-30T15:23:46+05:30","caller":"utils/logger.go:24","msg":"Server starting","port":"8081"}
|
|
||||||
|
|
||||||
┌───────────────────────────────────────────────────┐
|
|
||||||
│ Doormile Logistics Service API v1 │
|
|
||||||
│ Fiber v2.52.10 │
|
|
||||||
│ http://127.0.0.1:8081 │
|
|
||||||
│ (bound on host 0.0.0.0 and port 8081) │
|
|
||||||
│ │
|
|
||||||
│ Handlers ........... 206 Processes ........... 1 │
|
|
||||||
│ Prefork ....... Disabled PID .............. 9448 │
|
|
||||||
└───────────────────────────────────────────────────┘
|
|
||||||
|
|
||||||
{"level":"INFO","time":"2026-06-30T15:23:46+05:30","caller":"utils/logger.go:24","msg":"BookingWorker: subscribed","subject":"api.v1.bookings.create","consumer":"bookings_create"}
|
|
||||||
{"level":"INFO","time":"2026-06-30T15:23:46+05:30","caller":"utils/logger.go:24","msg":"BookingWorker: subscribed","subject":"api.v1.bookings.update","consumer":"bookings_update"}
|
|
||||||
{"level":"INFO","time":"2026-06-30T15:23:46+05:30","caller":"utils/logger.go:24","msg":"BookingWorker: subscribed","subject":"api.v1.bookings.cancel","consumer":"bookings_cancel"}
|
|
||||||
{"level":"INFO","time":"2026-06-30T15:23:46+05:30","caller":"utils/logger.go:24","msg":"BookingWorker: running, waiting for messages..."}
|
|
||||||
{"level":"INFO","time":"2026-06-30T15:26:06+05:30","caller":"utils/logger.go:24","msg":"API request success","method":"GET","path":"/api/v1/health","status":200,"latency":0}
|
|
||||||
exit status 0xffffffff
|
|
||||||
@@ -283,7 +283,9 @@ func GetMilerAssignments(c *fiber.Ctx) error {
|
|||||||
milerUserID := c.Locals("userid").(int)
|
milerUserID := c.Locals("userid").(int)
|
||||||
|
|
||||||
var assignments []models.BookingAssignment
|
var assignments []models.BookingAssignment
|
||||||
if err := db.DB.Where("mileruserid = ?", milerUserID).Order("assignedat DESC").Find(&assignments).Error; err != nil {
|
if err := db.DB.Where("mileruserid = ? AND assignmentstatus IN ?", milerUserID,
|
||||||
|
[]string{constants.AssignmentAssigned, constants.AssignmentAccepted}).
|
||||||
|
Order("assignedat DESC").Find(&assignments).Error; err != nil {
|
||||||
return utils.Internal(c, "failed to fetch assignments")
|
return utils.Internal(c, "failed to fetch assignments")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -326,6 +328,11 @@ func AcceptMilerAssignment(c *fiber.Ctx) error {
|
|||||||
return utils.NotFound(c, "assignment not found")
|
return utils.NotFound(c, "assignment not found")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if assignment.Assignmentstatus != constants.AssignmentAssigned {
|
||||||
|
tx.Rollback()
|
||||||
|
return utils.BadRequest(c, fmt.Sprintf("assignment is not pending acceptance (current status: %s)", assignment.Assignmentstatus))
|
||||||
|
}
|
||||||
|
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
assignment.Assignmentstatus = constants.AssignmentAccepted
|
assignment.Assignmentstatus = constants.AssignmentAccepted
|
||||||
assignment.Acceptedat = &now
|
assignment.Acceptedat = &now
|
||||||
@@ -367,6 +374,16 @@ func RejectMilerAssignment(c *fiber.Ctx) error {
|
|||||||
return utils.BadRequest(c, "invalid assignment ID")
|
return utils.BadRequest(c, "invalid assignment ID")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var req struct {
|
||||||
|
Reason string `json:"reason"`
|
||||||
|
}
|
||||||
|
if err := c.BodyParser(&req); err != nil {
|
||||||
|
return utils.BadRequest(c, "invalid request body")
|
||||||
|
}
|
||||||
|
if req.Reason == "" {
|
||||||
|
req.Reason = "Rejected by rider"
|
||||||
|
}
|
||||||
|
|
||||||
tx := db.DB.Begin()
|
tx := db.DB.Begin()
|
||||||
|
|
||||||
var ba models.BookingAssignment
|
var ba models.BookingAssignment
|
||||||
@@ -376,7 +393,7 @@ func RejectMilerAssignment(c *fiber.Ctx) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ba.Assignmentstatus = constants.AssignmentRejected
|
ba.Assignmentstatus = constants.AssignmentRejected
|
||||||
ba.Remarks = c.Query("reason", "Rejected by rider")
|
ba.Remarks = req.Reason
|
||||||
tx.Save(&ba)
|
tx.Save(&ba)
|
||||||
|
|
||||||
var booking models.PickupBooking
|
var booking models.PickupBooking
|
||||||
@@ -581,10 +598,24 @@ func BookingPickupComplete(c *fiber.Ctx) error {
|
|||||||
trackingNo := generateTrackingNo()
|
trackingNo := generateTrackingNo()
|
||||||
|
|
||||||
var defaultHubID *int
|
var defaultHubID *int
|
||||||
|
if profile.Hubid != nil {
|
||||||
|
defaultHubID = profile.Hubid
|
||||||
|
} else {
|
||||||
|
utils.Warn("BookingPickupComplete: miler has no assigned hub, falling back to first hub row", "miler_user_id", milerUserID, "booking_id", bookingID)
|
||||||
var hub models.Hub
|
var hub models.Hub
|
||||||
if tx.First(&hub).Error == nil {
|
if tx.First(&hub).Error == nil {
|
||||||
defaultHubID = &hub.Hubid
|
defaultHubID = &hub.Hubid
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hyperlocal shortcut: pickup and delivery in the same postal area mean
|
||||||
|
// no hub-to-hub tripsheet leg is needed, so the same miler goes straight
|
||||||
|
// to final-mile delivery instead of parking the consignment at the hub.
|
||||||
|
consignmentStatus := constants.ConsignmentInwardedAtHub
|
||||||
|
if len(booking.Pickuppincode) >= 3 && len(booking.Deliverypincode) >= 3 &&
|
||||||
|
booking.Pickuppincode[:3] == booking.Deliverypincode[:3] {
|
||||||
|
consignmentStatus = constants.ConsignmentOutForDelivery
|
||||||
|
}
|
||||||
|
|
||||||
consignment := models.Consignment{
|
consignment := models.Consignment{
|
||||||
Trackingno: trackingNo,
|
Trackingno: trackingNo,
|
||||||
@@ -602,7 +633,7 @@ func BookingPickupComplete(c *fiber.Ctx) error {
|
|||||||
Volumetricweight: totalChargeable - totalDead,
|
Volumetricweight: totalChargeable - totalDead,
|
||||||
Chargeableweight: totalChargeable,
|
Chargeableweight: totalChargeable,
|
||||||
Paymentmode: "Prepaid",
|
Paymentmode: "Prepaid",
|
||||||
Status: constants.ConsignmentInwardedAtHub,
|
Status: consignmentStatus,
|
||||||
Estimateddeliveryat: nil,
|
Estimateddeliveryat: nil,
|
||||||
Createdby: milerUserID,
|
Createdby: milerUserID,
|
||||||
Originhubid: defaultHubID,
|
Originhubid: defaultHubID,
|
||||||
@@ -632,7 +663,7 @@ func BookingPickupComplete(c *fiber.Ctx) error {
|
|||||||
Consignmentid: consignment.Consignmentid,
|
Consignmentid: consignment.Consignmentid,
|
||||||
Hubid: defaultHubID,
|
Hubid: defaultHubID,
|
||||||
Userid: &milerUserID,
|
Userid: &milerUserID,
|
||||||
Eventstatus: constants.ConsignmentInwardedAtHub,
|
Eventstatus: consignmentStatus,
|
||||||
Remarks: "Package collected by miler and converted to consignment",
|
Remarks: "Package collected by miler and converted to consignment",
|
||||||
}
|
}
|
||||||
tx.Create(&history)
|
tx.Create(&history)
|
||||||
|
|||||||
2
main.go
2
main.go
@@ -50,7 +50,7 @@ func main() {
|
|||||||
// CORS policy
|
// CORS policy
|
||||||
app.Use(cors.New(cors.Config{
|
app.Use(cors.New(cors.Config{
|
||||||
AllowHeaders: "Origin,Content-Type,Accept,Authorization",
|
AllowHeaders: "Origin,Content-Type,Accept,Authorization",
|
||||||
AllowOrigins: "http://localhost:5173,http://localhost:5174,http://localhost:3000,http://localhost:3001,http://localhost:3002,http://localhost:8080,http://localhost:8081,https://doormile.com,https://www.doormile.com,https://admin.doormile.com,https://api.doormile.com,https://crm.doormile.com,https://console.doormile.com",
|
AllowOrigins: "http://localhost:5173,http://localhost:5174,http://localhost:3000,http://localhost:3001,http://localhost:3002,http://localhost:8080,http://localhost:8081,https://doormile.com,https://www.doormile.com,https://admin.doormile.com,https://api.doormile.com,https://crm.doormile.com,https://console.doormile.com,https://app.doormile.com",
|
||||||
AllowCredentials: true,
|
AllowCredentials: true,
|
||||||
AllowMethods: "GET,POST,PUT,DELETE,PATCH,OPTIONS",
|
AllowMethods: "GET,POST,PUT,DELETE,PATCH,OPTIONS",
|
||||||
}))
|
}))
|
||||||
|
|||||||
Reference in New Issue
Block a user