Files
doormile_backend/models/booking.go
Suriya 90fa4fbb74 fix: per-site attribution needs its own column, not pickuplocationid
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>
2026-08-06 13:08:57 +05:30

152 lines
9.6 KiB
Go

package models
import (
"time"
)
type PickupBooking struct {
Bookingid int `json:"bookingid" gorm:"primaryKey;column:bookingid"`
Bookingno string `json:"bookingno" gorm:"column:bookingno;unique;not null"`
// Tenantid identifies which client company this booking is for. Nil for
// direct B2C bookings (Bookingsource "Customer_App") that aren't attributed
// to a tenant yet — see CreateCustomerBooking. Required for CRM bookings
// (Bookingsource "CRM_Console"), since those are always made on behalf of
// a specific tenant. Propagated onto the resulting Consignment at pickup
// time in BookingPickupComplete, instead of inferring it from whichever
// miler happens to complete the pickup.
Tenantid *int `json:"tenantid" gorm:"column:tenantid;index"`
Appcustomerid int `json:"appcustomerid" gorm:"column:appcustomerid"`
// Pickuplocationid is the *customer's* saved address the parcel was collected
// from — it carries a foreign key to appcustomerlocations. It is a B2C
// concept and has nothing to do with the client company's own sites.
Pickuplocationid *int `json:"pickuplocationid" gorm:"column:pickuplocationid"`
// Tenantlocationid is the *client's* site: the kitchen, branch or depot the
// parcel came out of. Separate column because pickuplocationid points at a
// different table entirely; writing a tenantlocations id into it violates
// that foreign key. This is what per-site reporting groups by.
Tenantlocationid *int `json:"tenantlocationid" gorm:"column:tenantlocationid;index"`
Pickupaddress string `json:"pickupaddress" gorm:"column:pickupaddress;not null"`
Pickuppincode string `json:"pickuppincode" gorm:"column:pickuppincode;not null"`
Pickuplatitude float64 `json:"pickuplatitude" gorm:"column:pickuplatitude;not null"`
Pickuplongitude float64 `json:"pickuplongitude" gorm:"column:pickuplongitude;not null"`
Deliveryaddress string `json:"deliveryaddress" gorm:"column:deliveryaddress;not null"`
Deliverypincode string `json:"deliverypincode" gorm:"column:deliverypincode;not null"`
Deliverylatitude float64 `json:"deliverylatitude" gorm:"column:deliverylatitude;not null"`
Deliverylongitude float64 `json:"deliverylongitude" gorm:"column:deliverylongitude;not null"`
Deliverycity string `json:"deliverycity" gorm:"column:deliverycity"`
Nearesthubid *int `json:"nearesthubid" gorm:"column:nearesthubid"`
Bookingsource string `json:"bookingsource" gorm:"column:bookingsource;default:Customer_App"`
Providercompany string `json:"providercompany" gorm:"column:providercompany"`
Providerlocation string `json:"providerlocation" gorm:"column:providerlocation"`
Notes string `json:"notes" gorm:"column:notes"`
Status string `json:"status" gorm:"column:status;default:Created"` // Created, Miler_Assigned, Pickup_Scheduled, Picked_Up, Converted_To_Consignment, Cancelled
Preferredpickupfrom *time.Time `json:"preferredpickupfrom" gorm:"column:preferredpickupfrom"`
Preferredpickupto *time.Time `json:"preferredpickupto" gorm:"column:preferredpickupto"`
Assignedmileruserid *int `json:"assignedmileruserid" gorm:"column:assignedmileruserid"`
Consignmentid *int `json:"consignmentid" gorm:"column:consignmentid"`
Createdat time.Time `json:"createdat" gorm:"column:createdat;default:CURRENT_TIMESTAMP"`
Updatedat time.Time `json:"updatedat" gorm:"column:updatedat;default:CURRENT_TIMESTAMP"`
// Relations
Parcels []BookingParcel `json:"parcels" gorm:"foreignKey:Bookingid"`
ServiceOptions []BookingServiceOption `json:"serviceoptions" gorm:"foreignKey:Bookingid"`
Payments []BookingPayment `json:"payments" gorm:"foreignKey:Bookingid"`
}
func (PickupBooking) TableName() string {
return "pickupbookings"
}
type BookingParcel struct {
Bookingparcelid int `json:"bookingparcelid" gorm:"primaryKey;column:bookingparcelid"`
Bookingid int `json:"bookingid" gorm:"column:bookingid"`
Itemcategory string `json:"itemcategory" gorm:"column:itemcategory"`
Itemdescription string `json:"itemdescription" gorm:"column:itemdescription"`
Declaredvalue float64 `json:"declaredvalue" gorm:"column:declaredvalue"`
Weight float64 `json:"weight" gorm:"column:weight"`
Length float64 `json:"length" gorm:"column:length"`
Width float64 `json:"width" gorm:"column:width"`
Height float64 `json:"height" gorm:"column:height"`
Isfragile bool `json:"isfragile" gorm:"column:isfragile;default:false"`
Needsinsurance bool `json:"needsinsurance" gorm:"column:needsinsurance;default:false"`
Insuranceamount float64 `json:"insuranceamount" gorm:"column:insuranceamount;default:0.00"`
Requireslargevehicle bool `json:"requireslargevehicle" gorm:"column:requireslargevehicle;default:false"`
Suggestedvehicletype string `json:"suggestedvehicletype" gorm:"column:suggestedvehicletype"`
Createdat time.Time `json:"createdat" gorm:"column:createdat;default:CURRENT_TIMESTAMP"`
Updatedat time.Time `json:"updatedat" gorm:"column:updatedat;default:CURRENT_TIMESTAMP"`
}
func (BookingParcel) TableName() string {
return "bookingparcels"
}
type BookingServiceOption struct {
Bookingserviceid int `json:"bookingserviceid" gorm:"primaryKey;column:bookingserviceid"`
Bookingid int `json:"bookingid" gorm:"column:bookingid"`
Servicetype string `json:"servicetype" gorm:"column:servicetype"` // Normal, Fast, Superfast
Estimatedprice float64 `json:"estimatedprice" gorm:"column:estimatedprice"`
Estimateddeliveryat *time.Time `json:"estimateddeliveryat" gorm:"column:estimateddeliveryat"`
Sladueat *time.Time `json:"sladueat" gorm:"column:sladueat"`
Pricingid *int `json:"pricingid" gorm:"column:pricingid"`
Createdat time.Time `json:"createdat" gorm:"column:createdat;default:CURRENT_TIMESTAMP"`
}
func (BookingServiceOption) TableName() string {
return "bookingserviceoptions"
}
type BookingPayment struct {
Bookingpaymentid int `json:"bookingpaymentid" gorm:"primaryKey;column:bookingpaymentid"`
Bookingid int `json:"bookingid" gorm:"column:bookingid"`
Amount float64 `json:"amount" gorm:"column:amount;not null"`
Paymentmode string `json:"paymentmode" gorm:"column:paymentmode"` // Cash, UPI, Card, Wallet
Paymentstatus string `json:"paymentstatus" gorm:"column:paymentstatus;default:Pending"` // Pending, Paid, Failed, Refunded
Collectedbyuserid *int `json:"collectedbyuserid" gorm:"column:collectedbyuserid"`
Transactionref string `json:"transactionref" gorm:"column:transactionref"`
Paidat *time.Time `json:"paidat" gorm:"column:paidat"`
Createdat time.Time `json:"createdat" gorm:"column:createdat;default:CURRENT_TIMESTAMP"`
}
func (BookingPayment) TableName() string {
return "bookingpayments"
}
type BookingAssignment struct {
Bookingassignmentid int `json:"bookingassignmentid" gorm:"primaryKey;column:bookingassignmentid"`
Bookingid int `json:"bookingid" gorm:"column:bookingid"`
Mileruserid int `json:"mileruserid" gorm:"column:mileruserid"`
Assignedbyuserid *int `json:"assignedbyuserid" gorm:"column:assignedbyuserid"`
Assignmentstatus string `json:"assignmentstatus" gorm:"column:assignmentstatus;default:Assigned"` // Assigned, Accepted, Rejected, Reassigned, Completed, Cancelled
Assignedat time.Time `json:"assignedat" gorm:"column:assignedat;default:CURRENT_TIMESTAMP"`
Acceptedat *time.Time `json:"acceptedat" gorm:"column:acceptedat"`
Completedat *time.Time `json:"completedat" gorm:"column:completedat"`
Remarks string `json:"remarks" gorm:"column:remarks"`
AgentDecisionID *uint64 `json:"agent_decision_id,omitempty" gorm:"column:agent_decision_id"`
Riderkms float64 `json:"riderkms" gorm:"column:riderkms;default:0"`
Ridercharges float64 `json:"ridercharges" gorm:"column:ridercharges;default:0"`
Bonuspoints int `json:"bonuspoints" gorm:"column:bonuspoints;default:0"`
}
func (BookingAssignment) TableName() string {
return "bookingassignments"
}
type BookingVehicleRequirement struct {
Requirementid int `json:"requirementid" gorm:"primaryKey;column:requirementid"`
Bookingid int `json:"bookingid" gorm:"column:bookingid"`
Requiredvehicletype string `json:"requiredvehicletype" gorm:"column:requiredvehicletype;not null"`
Reason string `json:"reason" gorm:"column:reason"`
Nearesthubid *int `json:"nearesthubid" gorm:"column:nearesthubid"`
Scheduledpickupfrom *time.Time `json:"scheduledpickupfrom" gorm:"column:scheduledpickupfrom"`
Scheduledpickupto *time.Time `json:"scheduledpickupto" gorm:"column:scheduledpickupto"`
Assignedvehicleid *int `json:"assignedvehicleid" gorm:"column:assignedvehicleid"`
Assigneddriveruserid *int `json:"assigneddriveruserid" gorm:"column:assigneddriveruserid"`
Status string `json:"status" gorm:"column:status;default:Required"` // Required, Scheduled, Assigned, Arrived, Picked_Up, Cancelled
Createdat time.Time `json:"createdat" gorm:"column:createdat;default:CURRENT_TIMESTAMP"`
Updatedat time.Time `json:"updatedat" gorm:"column:updatedat;default:CURRENT_TIMESTAMP"`
}
func (BookingVehicleRequirement) TableName() string {
return "bookingvehiclerequirements"
}