Files
doormile_backend/models/booking.go
Suriyakumarvijayanayagam 549a35a63a fix: admin console reads reachedat on booking rows (JSON tag arrivedat->reachedat)
The Admin Console derives Arrived from Pickup_Scheduled + reachedat != null,
but GetAdminBookings serializes the PickupBooking struct raw and the arrival
field emitted json arrivedat, not reachedat, so the console never saw the
arrival. Align the wire name to reachedat (miler app and /reached already use
it); DB column stays arrivedat.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WRaFH5hMRqmUQvVPQsyjZD
2026-08-26 15:25:42 +05:30

184 lines
12 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"`
// Arrival trace: written by the reached action when the rider is at the
// pickup point. Nullable — a booking that was never reached has no arrival.
// arrivallatitude/longitude are the rider's actual GPS at the moment they
// tapped "arrived", kept as a durable record separate from live telemetry.
// JSON name is reachedat (not arrivedat): the miler app, the /reached
// response and the Admin Console all key off reachedat — the console derives
// its "Arrived" display from Pickup_Scheduled + reachedat != null. The DB
// column stays arrivedat.
Arrivedat *time.Time `json:"reachedat" gorm:"column:arrivedat"`
Arrivallatitude *float64 `json:"arrivallatitude" gorm:"column:arrivallatitude"`
Arrivallongitude *float64 `json:"arrivallongitude" gorm:"column:arrivallongitude"`
Consignmentid *int `json:"consignmentid" gorm:"column:consignmentid"`
// Consignmentstatus is not a column — it is filled in by handlers that want
// the live consignment status alongside the booking (e.g. the admin bookings
// list, so a "Converted_To_Consignment" booking can still show
// Out_for_Delivery / Delivered). omitempty keeps it out of every other
// PickupBooking response that does not populate it.
Consignmentstatus string `json:"consignmentstatus,omitempty" gorm:"-"`
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"`
// Stop sequencing, written by internal/routing from the Route Optimization
// API's road-network ordering. Step is 1..N across a rider's currently
// active assignments — it says what order to run them in, which nothing in
// Doormile decided before: assignment picked *who*, never *in what order*.
//
// Step 0 means "not sequenced yet", not "first". A rider with a single stop
// is never sequenced, and sequencing is best-effort, so 0 is common and must
// not be read as a position.
Step int `json:"step" gorm:"column:step;default:0"`
Previouskms float64 `json:"previouskms" gorm:"column:previouskms;default:0"`
Cumulativekms float64 `json:"cumulativekms" gorm:"column:cumulativekms;default:0"`
Etaminutes int `json:"etaminutes" gorm:"column:etaminutes;default:0"`
Cumulativeeta int `json:"cumulativeeta" gorm:"column:cumulativeeta;default:0"`
Sequencedat *time.Time `json:"sequencedat" gorm:"column:sequencedat"`
}
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"
}