fix: panic recovery, rate limiting, transaction error handling, pagination
Hardening pass over the API surface. No route's auth requirements change.
Resilience:
- Add recover middleware. There was none, so an unhandled panic in any
handler propagated out of the process instead of becoming a 500.
- Add a centralized ErrorHandler so errors and recovered panics return the
same {success,message} envelope as the utils helpers, not Fiber's default
plain-text body. 5xx responses are logged with method and path.
Rate limiting:
- Global 300/min per IP as an abuse backstop, exempting health/readiness
probes and websocket upgrades.
- 10/min shared across every credential endpoint (customer/miler/admin/hub
login, verify-pin, reset-pin, email OTP). PINs are 4 digits, so the whole
keyspace was previously walkable in seconds. One shared limiter instance
means rotating between endpoints doesn't reset the budget.
- Add TRUSTED_PROXIES config. Limits key on c.IP(), which behind a TLS
terminator is the proxy, collapsing every client into one bucket. When set,
X-Forwarded-For is honoured only from those proxies so the header can't be
spoofed to dodge the limit. Logs a warning when unset.
Transactions:
- Check the error on all 51 previously-unchecked tx.Save/Create/Delete/
Model(...).Update/Commit calls across 6 controllers. A failed write inside
a transaction was silently ignored and the request still reported success;
an unchecked Commit could fail with the caller told everything worked.
Each site now rolls back and returns a specific message.
Pagination:
- Add utils.ParsePage/Paginated, reusing the pageno/pagesize convention
GetAdminBookings already established. Default 500, hard cap 1000.
- Apply to the previously unbounded consignments, tripsheets, exceptions,
app-users and clients endpoints. Defaults are high so existing consoles
that don't paginate keep working; the cap only stops a growing table from
being loaded wholesale. total is now a real COUNT, not len(data).
- GetClients also loaded the entire auth table to join in memory; it now
fetches only the current page's rows.
Tests (first in the repo):
- Extract the hyperlocal pincode rule out of BookingPickupComplete into
isHyperlocal so it is testable, covering the short/empty pincode fallback.
- Cover calculateVolumetricWeight and the ParsePage clamping rules.
Repo hygiene:
- Tag scratch/*.go with //go:build ignore. Each declared its own main(), so
`go build ./...` failed on redeclaration; it now passes repo-wide.
- Untrack scratch/node_modules (216 files) and ignore node_modules, test
artifacts, and the `doormile` binary `go build .` emits.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -117,9 +117,16 @@ func GetAdminDashboard(c *fiber.Ctx) error {
|
||||
// --------------------
|
||||
|
||||
func GetAppUsers(c *fiber.Ctx) error {
|
||||
page := utils.ParsePage(c)
|
||||
|
||||
var total int64
|
||||
if err := db.DB.Model(&models.AppUser{}).Where("roleid != ?", 5).Count(&total).Error; err != nil {
|
||||
return utils.Internal(c, "failed to count users")
|
||||
}
|
||||
|
||||
var users []models.AppUser
|
||||
// Exclude Milers (Roleid = 5) from the CRM user list
|
||||
if err := db.DB.Where("roleid != ?", 5).Find(&users).Error; err != nil {
|
||||
if err := page.Apply(db.DB.Where("roleid != ?", 5)).Find(&users).Error; err != nil {
|
||||
return utils.Internal(c, "failed to fetch users")
|
||||
}
|
||||
|
||||
@@ -145,7 +152,7 @@ func GetAppUsers(c *fiber.Ctx) error {
|
||||
"status": u.Status,
|
||||
})
|
||||
}
|
||||
return utils.List(c, response, int64(len(response)))
|
||||
return utils.Paginated(c, response, total, page)
|
||||
}
|
||||
|
||||
func CreateAppUser(c *fiber.Ctx) error {
|
||||
@@ -935,7 +942,9 @@ func CreateMiler(c *fiber.Ctx) error {
|
||||
return utils.Internal(c, "failed to create miler profile")
|
||||
}
|
||||
|
||||
tx.Commit()
|
||||
if err := tx.Commit().Error; err != nil {
|
||||
return utils.Internal(c, "failed to create miler")
|
||||
}
|
||||
return utils.Created(c, profile)
|
||||
}
|
||||
|
||||
@@ -995,11 +1004,20 @@ func BlockMiler(c *fiber.Ctx) error {
|
||||
|
||||
profile.Availabilitystatus = constants.MilerBlocked
|
||||
profile.Updatedat = time.Now()
|
||||
tx.Save(&profile)
|
||||
if err := tx.Save(&profile).Error; err != nil {
|
||||
tx.Rollback()
|
||||
return utils.Internal(c, "failed to block miler profile")
|
||||
}
|
||||
|
||||
tx.Model(&models.AppUser{}).Where("userid = ?", profile.Userid).Update("status", "Blocked")
|
||||
if err := tx.Model(&models.AppUser{}).Where("userid = ?", profile.Userid).
|
||||
Update("status", "Blocked").Error; err != nil {
|
||||
tx.Rollback()
|
||||
return utils.Internal(c, "failed to block miler account")
|
||||
}
|
||||
|
||||
tx.Commit()
|
||||
if err := tx.Commit().Error; err != nil {
|
||||
return utils.Internal(c, "failed to block miler")
|
||||
}
|
||||
return utils.Message(c, "miler blocked successfully")
|
||||
}
|
||||
|
||||
@@ -1060,27 +1078,27 @@ func GetAdminBookings(c *fiber.Ctx) error {
|
||||
|
||||
func CreateCRMBooking(c *fiber.Ctx) error {
|
||||
type AdminBookingRequest struct {
|
||||
Appcustomerid int `json:"appcustomerid"`
|
||||
CustomerPhone string `json:"customer_phone"`
|
||||
CustomerName string `json:"customer_name"`
|
||||
Pickupaddress string `json:"pickupaddress"`
|
||||
Pickuppincode string `json:"pickuppincode"`
|
||||
Pickuplatitude float64 `json:"pickuplatitude"`
|
||||
Pickuplongitude float64 `json:"pickuplongitude"`
|
||||
Deliveryaddress string `json:"deliveryaddress"`
|
||||
Deliverypincode string `json:"deliverypincode"`
|
||||
Deliverycity string `json:"deliverycity"`
|
||||
Deliverylatitude float64 `json:"deliverylatitude"`
|
||||
Deliverylongitude float64 `json:"deliverylongitude"`
|
||||
Providercompany string `json:"providercompany"`
|
||||
Providerlocation string `json:"providerlocation"`
|
||||
Notes string `json:"notes"`
|
||||
ServiceOption string `json:"service_option"`
|
||||
Finalprice float64 `json:"finalprice"`
|
||||
Insuranceamount float64 `json:"insuranceamount"`
|
||||
Preferredpickupfrom *time.Time `json:"preferredpickupfrom"`
|
||||
Preferredpickupto *time.Time `json:"preferredpickupto"`
|
||||
Parcels []dto.ParcelRequest `json:"parcels"`
|
||||
Appcustomerid int `json:"appcustomerid"`
|
||||
CustomerPhone string `json:"customer_phone"`
|
||||
CustomerName string `json:"customer_name"`
|
||||
Pickupaddress string `json:"pickupaddress"`
|
||||
Pickuppincode string `json:"pickuppincode"`
|
||||
Pickuplatitude float64 `json:"pickuplatitude"`
|
||||
Pickuplongitude float64 `json:"pickuplongitude"`
|
||||
Deliveryaddress string `json:"deliveryaddress"`
|
||||
Deliverypincode string `json:"deliverypincode"`
|
||||
Deliverycity string `json:"deliverycity"`
|
||||
Deliverylatitude float64 `json:"deliverylatitude"`
|
||||
Deliverylongitude float64 `json:"deliverylongitude"`
|
||||
Providercompany string `json:"providercompany"`
|
||||
Providerlocation string `json:"providerlocation"`
|
||||
Notes string `json:"notes"`
|
||||
ServiceOption string `json:"service_option"`
|
||||
Finalprice float64 `json:"finalprice"`
|
||||
Insuranceamount float64 `json:"insuranceamount"`
|
||||
Preferredpickupfrom *time.Time `json:"preferredpickupfrom"`
|
||||
Preferredpickupto *time.Time `json:"preferredpickupto"`
|
||||
Parcels []dto.ParcelRequest `json:"parcels"`
|
||||
}
|
||||
|
||||
req := new(AdminBookingRequest)
|
||||
@@ -1178,7 +1196,7 @@ func CreateCRMBooking(c *fiber.Ctx) error {
|
||||
if p.Needsinsurance {
|
||||
parcel.Insuranceamount = p.Declaredvalue * 0.01
|
||||
}
|
||||
|
||||
|
||||
// If explicit insurance amount is provided from CRM, apply it to the first parcel
|
||||
if req.Insuranceamount > 0 && totalWeight == math.Max(p.Weight, calculateVolumetricWeight(p.Length, p.Width, p.Height)) {
|
||||
parcel.Insuranceamount = req.Insuranceamount
|
||||
@@ -1195,10 +1213,10 @@ func CreateCRMBooking(c *fiber.Ctx) error {
|
||||
if booking.Deliverylatitude != 0 && booking.Deliverylongitude != 0 {
|
||||
distance = calculateDistance(booking.Pickuplatitude, booking.Pickuplongitude, booking.Deliverylatitude, booking.Deliverylongitude)
|
||||
}
|
||||
|
||||
|
||||
var pricing models.Pricing
|
||||
err := tx.Where("status = ? AND ? BETWEEN effectivefrom AND effectiveto", "Active", time.Now()).Order("priority DESC").First(&pricing).Error
|
||||
|
||||
|
||||
var estimatedPrice float64
|
||||
var pricingID *int
|
||||
if err == nil {
|
||||
@@ -1257,22 +1275,27 @@ func CreateCRMBooking(c *fiber.Ctx) error {
|
||||
Reason: "Oversized package / heavy weight",
|
||||
Status: "Required",
|
||||
}
|
||||
tx.Create(&reqVeh)
|
||||
if err := tx.Create(&reqVeh).Error; err != nil {
|
||||
tx.Rollback()
|
||||
return utils.Internal(c, "failed to save vehicle requirement")
|
||||
}
|
||||
}
|
||||
|
||||
tx.Commit()
|
||||
if err := tx.Commit().Error; err != nil {
|
||||
return utils.Internal(c, "failed to create booking")
|
||||
}
|
||||
|
||||
go assignment.AssignCRMMiler(booking.Bookingid)
|
||||
|
||||
if db.Js != nil {
|
||||
payload := map[string]interface{}{
|
||||
"booking_id": booking.Bookingid,
|
||||
"booking_no": booking.Bookingno,
|
||||
"customer_id": booking.Appcustomerid,
|
||||
"pickup_address": booking.Pickupaddress,
|
||||
"pickup_pincode": booking.Pickuppincode,
|
||||
"status": constants.BookingPendingPickup,
|
||||
"created_at": time.Now().UnixMilli(),
|
||||
"booking_id": booking.Bookingid,
|
||||
"booking_no": booking.Bookingno,
|
||||
"customer_id": booking.Appcustomerid,
|
||||
"pickup_address": booking.Pickupaddress,
|
||||
"pickup_pincode": booking.Pickuppincode,
|
||||
"status": constants.BookingPendingPickup,
|
||||
"created_at": time.Now().UnixMilli(),
|
||||
}
|
||||
if data, err := json.Marshal(payload); err == nil {
|
||||
db.Js.Publish("api.v1.bookings.create", data)
|
||||
@@ -1425,11 +1448,18 @@ func AdminCancelBooking(c *fiber.Ctx) error {
|
||||
// --------------------
|
||||
|
||||
func GetAdminConsignments(c *fiber.Ctx) error {
|
||||
page := utils.ParsePage(c)
|
||||
|
||||
var total int64
|
||||
if err := db.DB.Model(&models.Consignment{}).Count(&total).Error; err != nil {
|
||||
return utils.Internal(c, "failed to count consignments")
|
||||
}
|
||||
|
||||
var list []models.Consignment
|
||||
if err := db.DB.Find(&list).Error; err != nil {
|
||||
if err := page.Apply(db.DB).Find(&list).Error; err != nil {
|
||||
return utils.Internal(c, "failed to fetch consignments")
|
||||
}
|
||||
return utils.List(c, list, int64(len(list)))
|
||||
return utils.Paginated(c, list, total, page)
|
||||
}
|
||||
|
||||
func GetAdminConsignmentDetails(c *fiber.Ctx) error {
|
||||
@@ -1478,7 +1508,10 @@ func AdminUpdateConsignmentStatus(c *fiber.Ctx) error {
|
||||
|
||||
consignment.Status = req.Status
|
||||
consignment.Updatedat = time.Now()
|
||||
tx.Save(&consignment)
|
||||
if err := tx.Save(&consignment).Error; err != nil {
|
||||
tx.Rollback()
|
||||
return utils.Internal(c, "failed to update consignment status")
|
||||
}
|
||||
|
||||
adminUserID := c.Locals("userid").(int)
|
||||
|
||||
@@ -1488,9 +1521,14 @@ func AdminUpdateConsignmentStatus(c *fiber.Ctx) error {
|
||||
Eventstatus: req.Status,
|
||||
Remarks: req.Remarks,
|
||||
}
|
||||
tx.Create(&history)
|
||||
if err := tx.Create(&history).Error; err != nil {
|
||||
tx.Rollback()
|
||||
return utils.Internal(c, "failed to record consignment history")
|
||||
}
|
||||
|
||||
tx.Commit()
|
||||
if err := tx.Commit().Error; err != nil {
|
||||
return utils.Internal(c, "failed to update consignment")
|
||||
}
|
||||
return utils.OK(c, consignment)
|
||||
}
|
||||
|
||||
@@ -1499,11 +1537,18 @@ func AdminUpdateConsignmentStatus(c *fiber.Ctx) error {
|
||||
// --------------------
|
||||
|
||||
func GetTripsheets(c *fiber.Ctx) error {
|
||||
page := utils.ParsePage(c)
|
||||
|
||||
var total int64
|
||||
if err := db.DB.Model(&models.Tripsheet{}).Where("deletedat IS NULL").Count(&total).Error; err != nil {
|
||||
return utils.Internal(c, "failed to count tripsheets")
|
||||
}
|
||||
|
||||
var list []models.Tripsheet
|
||||
if err := db.DB.Where("deletedat IS NULL").Find(&list).Error; err != nil {
|
||||
if err := page.Apply(db.DB.Where("deletedat IS NULL")).Find(&list).Error; err != nil {
|
||||
return utils.Internal(c, "failed to fetch tripsheets")
|
||||
}
|
||||
return utils.List(c, list, int64(len(list)))
|
||||
return utils.Paginated(c, list, total, page)
|
||||
}
|
||||
|
||||
func CreateTripsheet(c *fiber.Ctx) error {
|
||||
@@ -1592,26 +1637,38 @@ func DispatchTripsheet(c *fiber.Ctx) error {
|
||||
tripsheet.Status = constants.TripsheetDispatched
|
||||
tripsheet.Dispatchtime = &now
|
||||
tripsheet.Updatedat = now
|
||||
tx.Save(&tripsheet)
|
||||
if err := tx.Save(&tripsheet).Error; err != nil {
|
||||
tx.Rollback()
|
||||
return utils.Internal(c, "failed to dispatch tripsheet")
|
||||
}
|
||||
|
||||
// Fetch all loaded items
|
||||
var items []models.TripsheetItem
|
||||
tx.Where("tripsheetid = ?", id).Find(&items)
|
||||
if err := tx.Where("tripsheetid = ?", id).Find(&items).Error; err != nil {
|
||||
tx.Rollback()
|
||||
return utils.Internal(c, "failed to load tripsheet items")
|
||||
}
|
||||
|
||||
adminUserID := c.Locals("userid").(int)
|
||||
|
||||
for _, item := range items {
|
||||
// Update item scanning
|
||||
tx.Model(&item).Updates(map[string]interface{}{
|
||||
if err := tx.Model(&item).Updates(map[string]interface{}{
|
||||
"scanstatus": constants.ScanLoaded,
|
||||
"scannedat": &now,
|
||||
})
|
||||
}).Error; err != nil {
|
||||
tx.Rollback()
|
||||
return utils.Internal(c, "failed to update tripsheet item scan status")
|
||||
}
|
||||
|
||||
// Update consignment status to In_Transit
|
||||
tx.Model(&models.Consignment{}).Where("consignmentid = ?", item.Consignmentid).Updates(map[string]interface{}{
|
||||
if err := tx.Model(&models.Consignment{}).Where("consignmentid = ?", item.Consignmentid).Updates(map[string]interface{}{
|
||||
"status": constants.ConsignmentInTransit,
|
||||
"updatedat": now,
|
||||
})
|
||||
}).Error; err != nil {
|
||||
tx.Rollback()
|
||||
return utils.Internal(c, "failed to update consignment status")
|
||||
}
|
||||
|
||||
// Log history
|
||||
history := models.ConsignmentHistory{
|
||||
@@ -1621,10 +1678,15 @@ func DispatchTripsheet(c *fiber.Ctx) error {
|
||||
Eventstatus: constants.ConsignmentInTransit,
|
||||
Remarks: fmt.Sprintf("Consignment dispatched on Tripsheet %s", tripsheet.Tripsheetno),
|
||||
}
|
||||
tx.Create(&history)
|
||||
if err := tx.Create(&history).Error; err != nil {
|
||||
tx.Rollback()
|
||||
return utils.Internal(c, "failed to record consignment history")
|
||||
}
|
||||
}
|
||||
|
||||
tx.Commit()
|
||||
if err := tx.Commit().Error; err != nil {
|
||||
return utils.Internal(c, "failed to dispatch tripsheet")
|
||||
}
|
||||
return utils.OK(c, tripsheet)
|
||||
}
|
||||
|
||||
@@ -1642,26 +1704,38 @@ func ArriveTripsheet(c *fiber.Ctx) error {
|
||||
tripsheet.Status = constants.TripsheetArrived
|
||||
tripsheet.Arrivaltime = &now
|
||||
tripsheet.Updatedat = now
|
||||
tx.Save(&tripsheet)
|
||||
if err := tx.Save(&tripsheet).Error; err != nil {
|
||||
tx.Rollback()
|
||||
return utils.Internal(c, "failed to mark tripsheet arrived")
|
||||
}
|
||||
|
||||
// Fetch items
|
||||
var items []models.TripsheetItem
|
||||
tx.Where("tripsheetid = ?", id).Find(&items)
|
||||
if err := tx.Where("tripsheetid = ?", id).Find(&items).Error; err != nil {
|
||||
tx.Rollback()
|
||||
return utils.Internal(c, "failed to load tripsheet items")
|
||||
}
|
||||
|
||||
adminUserID := c.Locals("userid").(int)
|
||||
|
||||
for _, item := range items {
|
||||
tx.Model(&item).Updates(map[string]interface{}{
|
||||
if err := tx.Model(&item).Updates(map[string]interface{}{
|
||||
"scanstatus": constants.ScanUnloaded,
|
||||
"scannedat": &now,
|
||||
})
|
||||
}).Error; err != nil {
|
||||
tx.Rollback()
|
||||
return utils.Internal(c, "failed to update tripsheet item scan status")
|
||||
}
|
||||
|
||||
// Update consignment status back to Inwarded_at_Hub at destination
|
||||
tx.Model(&models.Consignment{}).Where("consignmentid = ?", item.Consignmentid).Updates(map[string]interface{}{
|
||||
if err := tx.Model(&models.Consignment{}).Where("consignmentid = ?", item.Consignmentid).Updates(map[string]interface{}{
|
||||
"status": constants.ConsignmentInwardedAtHub,
|
||||
"currenthubid": tripsheet.Destinationhubid,
|
||||
"updatedat": now,
|
||||
})
|
||||
}).Error; err != nil {
|
||||
tx.Rollback()
|
||||
return utils.Internal(c, "failed to update consignment status")
|
||||
}
|
||||
|
||||
// Log history
|
||||
history := models.ConsignmentHistory{
|
||||
@@ -1672,10 +1746,15 @@ func ArriveTripsheet(c *fiber.Ctx) error {
|
||||
Eventstatus: constants.ConsignmentInwardedAtHub,
|
||||
Remarks: fmt.Sprintf("Consignment arrived at Hub on Tripsheet %s", tripsheet.Tripsheetno),
|
||||
}
|
||||
tx.Create(&history)
|
||||
if err := tx.Create(&history).Error; err != nil {
|
||||
tx.Rollback()
|
||||
return utils.Internal(c, "failed to record consignment history")
|
||||
}
|
||||
}
|
||||
|
||||
tx.Commit()
|
||||
if err := tx.Commit().Error; err != nil {
|
||||
return utils.Internal(c, "failed to mark tripsheet arrived")
|
||||
}
|
||||
return utils.OK(c, tripsheet)
|
||||
}
|
||||
|
||||
@@ -1796,7 +1875,7 @@ func GetPricingQuoteSimulate(c *fiber.Ctx) error {
|
||||
}
|
||||
|
||||
distance := calculateDistance(req.Pickuplatitude, req.Pickuplongitude, req.Deliverylatitude, req.Deliverylongitude)
|
||||
|
||||
|
||||
var totalWeight float64
|
||||
for _, p := range req.Parcels {
|
||||
volumetric := calculateVolumetricWeight(p.Length, p.Width, p.Height)
|
||||
@@ -1831,11 +1910,18 @@ func GetPricingQuoteSimulate(c *fiber.Ctx) error {
|
||||
// --------------------
|
||||
|
||||
func GetExceptions(c *fiber.Ctx) error {
|
||||
page := utils.ParsePage(c)
|
||||
|
||||
var total int64
|
||||
if err := db.DB.Model(&models.ConsignmentException{}).Where("deletedat IS NULL").Count(&total).Error; err != nil {
|
||||
return utils.Internal(c, "failed to count exceptions")
|
||||
}
|
||||
|
||||
var list []models.ConsignmentException
|
||||
if err := db.DB.Where("deletedat IS NULL").Find(&list).Error; err != nil {
|
||||
if err := page.Apply(db.DB.Where("deletedat IS NULL")).Find(&list).Error; err != nil {
|
||||
return utils.Internal(c, "failed to fetch exceptions")
|
||||
}
|
||||
return utils.List(c, list, int64(len(list)))
|
||||
return utils.Paginated(c, list, total, page)
|
||||
}
|
||||
|
||||
func CreateException(c *fiber.Ctx) error {
|
||||
@@ -1878,8 +1964,12 @@ func CreateException(c *fiber.Ctx) error {
|
||||
}
|
||||
|
||||
if cStatus != "" {
|
||||
tx.Model(&models.Consignment{}).Where("consignmentid = ?", req.Consignmentid).Update("status", cStatus)
|
||||
|
||||
if err := tx.Model(&models.Consignment{}).Where("consignmentid = ?", req.Consignmentid).
|
||||
Update("status", cStatus).Error; err != nil {
|
||||
tx.Rollback()
|
||||
return utils.Internal(c, "failed to update consignment status")
|
||||
}
|
||||
|
||||
// Log history
|
||||
history := models.ConsignmentHistory{
|
||||
Consignmentid: req.Consignmentid,
|
||||
@@ -1889,10 +1979,15 @@ func CreateException(c *fiber.Ctx) error {
|
||||
Eventstatus: cStatus,
|
||||
Remarks: fmt.Sprintf("Exception reported: %s. Description: %s", req.Exceptiontype, req.Description),
|
||||
}
|
||||
tx.Create(&history)
|
||||
if err := tx.Create(&history).Error; err != nil {
|
||||
tx.Rollback()
|
||||
return utils.Internal(c, "failed to record consignment history")
|
||||
}
|
||||
}
|
||||
|
||||
tx.Commit()
|
||||
if err := tx.Commit().Error; err != nil {
|
||||
return utils.Internal(c, "failed to record exception")
|
||||
}
|
||||
return utils.Created(c, exception)
|
||||
}
|
||||
|
||||
@@ -2231,18 +2326,24 @@ func InternalReassign(c *fiber.Ctx) error {
|
||||
now := time.Now()
|
||||
|
||||
if booking.Assignedmileruserid != nil {
|
||||
tx.Model(&models.MilerProfile{}).
|
||||
if err := tx.Model(&models.MilerProfile{}).
|
||||
Where("userid = ?", *booking.Assignedmileruserid).
|
||||
Updates(map[string]interface{}{
|
||||
"availabilitystatus": constants.MilerAvailable,
|
||||
"updatedat": now,
|
||||
})
|
||||
}).Error; err != nil {
|
||||
tx.Rollback()
|
||||
return utils.Internal(c, "failed to free previous miler")
|
||||
}
|
||||
|
||||
tx.Delete(&models.BookingAssignment{},
|
||||
if err := tx.Delete(&models.BookingAssignment{},
|
||||
"bookingid = ? AND assignmentstatus IN ?",
|
||||
booking.Bookingid,
|
||||
[]string{constants.AssignmentAssigned, constants.AssignmentAccepted},
|
||||
)
|
||||
).Error; err != nil {
|
||||
tx.Rollback()
|
||||
return utils.Internal(c, "failed to clear previous assignment")
|
||||
}
|
||||
}
|
||||
|
||||
booking.Status = constants.BookingCreated
|
||||
@@ -2253,7 +2354,9 @@ func InternalReassign(c *fiber.Ctx) error {
|
||||
return utils.Internal(c, "failed to reset booking")
|
||||
}
|
||||
|
||||
tx.Commit()
|
||||
if err := tx.Commit().Error; err != nil {
|
||||
return utils.Internal(c, "failed to reassign booking")
|
||||
}
|
||||
|
||||
if booking.Bookingsource == "CRM_Console" {
|
||||
go assignment.AssignCRMMiler(booking.Bookingid)
|
||||
|
||||
Reference in New Issue
Block a user