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>
This commit is contained in:
@@ -356,7 +356,7 @@ func GetAdminReports(c *fiber.Ctx) error {
|
|||||||
|
|
||||||
bookingScope := func() *gorm.DB {
|
bookingScope := func() *gorm.DB {
|
||||||
q := scopeToTenant(db.DB.Model(&models.PickupBooking{}), "tenantid", ownTenant)
|
q := scopeToTenant(db.DB.Model(&models.PickupBooking{}), "tenantid", ownTenant)
|
||||||
return scopeToLocation(q, "pickuplocationid", locationID)
|
return scopeToLocation(q, "tenantlocationid", locationID)
|
||||||
}
|
}
|
||||||
|
|
||||||
var totalBookings int64
|
var totalBookings int64
|
||||||
@@ -374,7 +374,7 @@ func GetAdminReports(c *fiber.Ctx) error {
|
|||||||
|
|
||||||
consignmentQuery := scopeToLocation(
|
consignmentQuery := scopeToLocation(
|
||||||
scopeToTenant(db.DB.Model(&models.Consignment{}), "tenantid", ownTenant),
|
scopeToTenant(db.DB.Model(&models.Consignment{}), "tenantid", ownTenant),
|
||||||
"pickuplocationid", locationID).
|
"tenantlocationid", locationID).
|
||||||
Where("createdat BETWEEN ? AND ?", from, to)
|
Where("createdat BETWEEN ? AND ?", from, to)
|
||||||
if hubID != "" {
|
if hubID != "" {
|
||||||
consignmentQuery = consignmentQuery.Where("currenthubid = ?", hubID)
|
consignmentQuery = consignmentQuery.Where("currenthubid = ?", hubID)
|
||||||
@@ -639,11 +639,11 @@ func GetLocationSummary(c *fiber.Ctx) error {
|
|||||||
// each of the client's own locations raised in the range, how many reached a
|
// each of the client's own locations raised in the range, how many reached a
|
||||||
// consignment, and what COD came back.
|
// consignment, and what COD came back.
|
||||||
//
|
//
|
||||||
// Bookings with no pickuplocationid are reported as a single "Unattributed"
|
// Bookings with no tenantlocationid are reported as a single "Unattributed"
|
||||||
// row rather than dropped, so the per-site figures still add up to the summary
|
// row rather than dropped, so the per-site figures still add up to the summary
|
||||||
// total. That row is not noise — it is every booking created without naming
|
// total. That row is not noise — it is every booking created without naming
|
||||||
// its site, and it should shrink to zero as the console starts sending
|
// its site, and it should shrink to zero as the console starts sending
|
||||||
// pickuplocationid.
|
// tenantlocationid.
|
||||||
func locationBreakdown(tenantID, locationID int, from, to time.Time) []fiber.Map {
|
func locationBreakdown(tenantID, locationID int, from, to time.Time) []fiber.Map {
|
||||||
// Only meaningful within one client. Across the whole network the rows
|
// Only meaningful within one client. Across the whole network the rows
|
||||||
// would be a mix of every tenant's sites, which is a screen nobody asked
|
// would be a mix of every tenant's sites, which is a screen nobody asked
|
||||||
@@ -680,7 +680,7 @@ func locationBreakdown(tenantID, locationID int, from, to time.Time) []fiber.Map
|
|||||||
COALESCE(SUM(p.paid), 0) AS cod_collected
|
COALESCE(SUM(p.paid), 0) AS cod_collected
|
||||||
FROM tenantlocations tl
|
FROM tenantlocations tl
|
||||||
LEFT JOIN pickupbookings b
|
LEFT JOIN pickupbookings b
|
||||||
ON b.pickuplocationid = tl.tenantlocationid
|
ON b.tenantlocationid = tl.tenantlocationid
|
||||||
AND b.createdat BETWEEN ? AND ?
|
AND b.createdat BETWEEN ? AND ?
|
||||||
LEFT JOIN (
|
LEFT JOIN (
|
||||||
SELECT bookingid, SUM(amount) AS paid
|
SELECT bookingid, SUM(amount) AS paid
|
||||||
@@ -723,7 +723,7 @@ func locationBreakdown(tenantID, locationID int, from, to time.Time) []fiber.Map
|
|||||||
if locationID == 0 {
|
if locationID == 0 {
|
||||||
var unattributed int64
|
var unattributed int64
|
||||||
db.DB.Model(&models.PickupBooking{}).
|
db.DB.Model(&models.PickupBooking{}).
|
||||||
Where("tenantid = ? AND pickuplocationid IS NULL AND createdat BETWEEN ? AND ?",
|
Where("tenantid = ? AND tenantlocationid IS NULL AND createdat BETWEEN ? AND ?",
|
||||||
tenantID, from, to).Count(&unattributed)
|
tenantID, from, to).Count(&unattributed)
|
||||||
if unattributed > 0 {
|
if unattributed > 0 {
|
||||||
out = append(out, fiber.Map{
|
out = append(out, fiber.Map{
|
||||||
@@ -1977,10 +1977,16 @@ type AdminBookingRequest struct {
|
|||||||
Appcustomerid int `json:"appcustomerid"`
|
Appcustomerid int `json:"appcustomerid"`
|
||||||
CustomerPhone string `json:"customer_phone"`
|
CustomerPhone string `json:"customer_phone"`
|
||||||
CustomerName string `json:"customer_name"`
|
CustomerName string `json:"customer_name"`
|
||||||
// Pickuplocationid names the client site the parcel is collected from — a
|
// Tenantlocationid names the client site the parcel is collected from — a
|
||||||
// DailyGrubs kitchen, for instance. Optional, but supplying it lets the
|
// DailyGrubs kitchen, for instance. Optional, but supplying it lets the
|
||||||
// address/pincode/coordinates be filled from the stored location instead of
|
// address/pincode/coordinates be filled from the stored site instead of
|
||||||
// retyped, and is the only thing that makes per-site reporting possible.
|
// retyped, and is what per-site reporting groups by. When it is omitted the
|
||||||
|
// site is inferred from the pickup coordinates or address.
|
||||||
|
Tenantlocationid *int `json:"tenantlocationid"`
|
||||||
|
// Pickuplocationid is accepted only as an alias for the field above, for
|
||||||
|
// callers written against the earlier docs. It is never stored as-is: the
|
||||||
|
// column of that name foreign-keys to appcustomerlocations, not to a
|
||||||
|
// client's sites.
|
||||||
Pickuplocationid *int `json:"pickuplocationid"`
|
Pickuplocationid *int `json:"pickuplocationid"`
|
||||||
Pickupaddress string `json:"pickupaddress"`
|
Pickupaddress string `json:"pickupaddress"`
|
||||||
Pickuppincode string `json:"pickuppincode"`
|
Pickuppincode string `json:"pickuppincode"`
|
||||||
@@ -2027,18 +2033,30 @@ func createExpressBooking(req AdminBookingRequest) (*models.PickupBooking, error
|
|||||||
return nil, &expressBookingValidationError{"tenantid does not match a known tenant"}
|
return nil, &expressBookingValidationError{"tenantid does not match a known tenant"}
|
||||||
}
|
}
|
||||||
|
|
||||||
// A named pickup location fills in whatever the caller left blank, so the
|
// A named pickup site fills in whatever the caller left blank, so the console
|
||||||
// console can send a kitchen id instead of restating its address every time.
|
// can send a kitchen id instead of restating its address every time. It must
|
||||||
// It must belong to the booking's tenant — otherwise one client could book
|
// belong to the booking's tenant — otherwise one client could book against
|
||||||
// against another client's site.
|
// another client's site.
|
||||||
if req.Pickuplocationid != nil {
|
//
|
||||||
|
// `pickuplocationid` is accepted as an alias here purely for callers written
|
||||||
|
// against the earlier documentation. It is the wrong column: it foreign-keys
|
||||||
|
// to appcustomerlocations, so a tenantlocations id in it fails the insert.
|
||||||
|
// Both names resolve to Tenantlocationid.
|
||||||
|
siteID := req.Tenantlocationid
|
||||||
|
if siteID == nil {
|
||||||
|
siteID = req.Pickuplocationid
|
||||||
|
}
|
||||||
|
req.Pickuplocationid = nil
|
||||||
|
|
||||||
|
if siteID != nil {
|
||||||
var loc models.TenantLocation
|
var loc models.TenantLocation
|
||||||
if err := db.DB.Where("tenantlocationid = ?", *req.Pickuplocationid).First(&loc).Error; err != nil {
|
if err := db.DB.Where("tenantlocationid = ?", *siteID).First(&loc).Error; err != nil {
|
||||||
return nil, &expressBookingValidationError{"pickuplocationid does not match a known location"}
|
return nil, &expressBookingValidationError{"tenantlocationid does not match a known location"}
|
||||||
}
|
}
|
||||||
if loc.Tenantid != req.Tenantid {
|
if loc.Tenantid != req.Tenantid {
|
||||||
return nil, &expressBookingValidationError{"pickuplocationid does not belong to this tenant"}
|
return nil, &expressBookingValidationError{"tenantlocationid does not belong to this tenant"}
|
||||||
}
|
}
|
||||||
|
req.Tenantlocationid = siteID
|
||||||
if req.Pickupaddress == "" {
|
if req.Pickupaddress == "" {
|
||||||
req.Pickupaddress = loc.Address
|
req.Pickupaddress = loc.Address
|
||||||
}
|
}
|
||||||
@@ -2060,10 +2078,8 @@ func createExpressBooking(req AdminBookingRequest) (*models.PickupBooking, error
|
|||||||
// pickup actually is. Without this the field stays null — as it did on every
|
// pickup actually is. Without this the field stays null — as it did on every
|
||||||
// booking in the system — and per-site reporting has nothing to group by,
|
// booking in the system — and per-site reporting has nothing to group by,
|
||||||
// because the console sends a kitchen's address rather than its id.
|
// because the console sends a kitchen's address rather than its id.
|
||||||
if req.Pickuplocationid == nil {
|
if req.Tenantlocationid == nil {
|
||||||
if matched := matchTenantLocation(req.Tenantid, req.Pickupaddress, req.Pickuplatitude, req.Pickuplongitude); matched != nil {
|
req.Tenantlocationid = matchTenantLocation(req.Tenantid, req.Pickupaddress, req.Pickuplatitude, req.Pickuplongitude)
|
||||||
req.Pickuplocationid = matched
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
tx := db.DB.Begin()
|
tx := db.DB.Begin()
|
||||||
@@ -2099,6 +2115,7 @@ func createExpressBooking(req AdminBookingRequest) (*models.PickupBooking, error
|
|||||||
Tenantid: &tenantID,
|
Tenantid: &tenantID,
|
||||||
Appcustomerid: customerID,
|
Appcustomerid: customerID,
|
||||||
Pickuplocationid: req.Pickuplocationid,
|
Pickuplocationid: req.Pickuplocationid,
|
||||||
|
Tenantlocationid: req.Tenantlocationid,
|
||||||
Pickupaddress: req.Pickupaddress,
|
Pickupaddress: req.Pickupaddress,
|
||||||
Pickuppincode: req.Pickuppincode,
|
Pickuppincode: req.Pickuppincode,
|
||||||
Pickuplatitude: req.Pickuplatitude,
|
Pickuplatitude: req.Pickuplatitude,
|
||||||
|
|||||||
@@ -804,6 +804,7 @@ func BookingPickupComplete(c *fiber.Ctx) error {
|
|||||||
Trackingno: trackingNo,
|
Trackingno: trackingNo,
|
||||||
Tenantid: consignmentTenantID,
|
Tenantid: consignmentTenantID,
|
||||||
Pickuplocationid: booking.Pickuplocationid,
|
Pickuplocationid: booking.Pickuplocationid,
|
||||||
|
Tenantlocationid: booking.Tenantlocationid,
|
||||||
Pickuplatitude: booking.Pickuplatitude,
|
Pickuplatitude: booking.Pickuplatitude,
|
||||||
Pickuplongitude: booking.Pickuplongitude,
|
Pickuplongitude: booking.Pickuplongitude,
|
||||||
Deliverylatitude: booking.Deliverylatitude,
|
Deliverylatitude: booking.Deliverylatitude,
|
||||||
|
|||||||
@@ -128,7 +128,7 @@ tenants at once aren't a meaningful report, so it 400s without one.
|
|||||||
|
|
||||||
The same rows appear as `by_location` inside `GET /admin/reports`.
|
The same rows appear as `by_location` inside `GET /admin/reports`.
|
||||||
|
|
||||||
**Send `pickuplocationid` on bookings.** Attribution depends on it. The server
|
**Send `tenantlocationid` on bookings.** Attribution depends on it. The server
|
||||||
will try to recognise the site from the pickup coordinates (within 150m) or a
|
will try to recognise the site from the pickup coordinates (within 150m) or a
|
||||||
matching address, but an explicit id is exact and always wins.
|
matching address, but an explicit id is exact and always wins.
|
||||||
|
|
||||||
@@ -266,8 +266,8 @@ delivered, riderkms, ridercharges, dutyminutes }`.
|
|||||||
POST /admin/expressbooking
|
POST /admin/expressbooking
|
||||||
{
|
{
|
||||||
"tenantid": 13, // required; forced to your own tenant on client logins
|
"tenantid": 13, // required; forced to your own tenant on client logins
|
||||||
"pickuplocationid": 15, // a stored kitchen/branch — fills address, pincode and
|
"tenantlocationid": 13, // a stored kitchen/branch — fills address, pincode and
|
||||||
// coords for you, and is what makes per-site reporting work
|
// coords for you, and is what per-site reporting groups by
|
||||||
"customer_phone": "9876543210", // creates a Guest customer if unknown
|
"customer_phone": "9876543210", // creates a Guest customer if unknown
|
||||||
"customer_name": "Ramesh",
|
"customer_name": "Ramesh",
|
||||||
"deliveryaddress": "12 Cross Cut Road, Gandhipuram",
|
"deliveryaddress": "12 Cross Cut Road, Gandhipuram",
|
||||||
@@ -286,8 +286,12 @@ POST /admin/expressbooking
|
|||||||
|
|
||||||
Rules worth knowing:
|
Rules worth knowing:
|
||||||
- `parcels` must be non-empty and `tenantid` must exist.
|
- `parcels` must be non-empty and `tenantid` must exist.
|
||||||
- Pickup address + pincode are required **unless** `pickuplocationid` supplies them.
|
- Pickup address + pincode are required **unless** `tenantlocationid` supplies them.
|
||||||
- A `pickuplocationid` belonging to another tenant is rejected.
|
- A `tenantlocationid` belonging to another tenant is rejected.
|
||||||
|
- `pickuplocationid` is accepted as an alias for `tenantlocationid`, for anything
|
||||||
|
written against the earlier version of this doc. Prefer the new name: the
|
||||||
|
database column called `pickuplocationid` means something else entirely (the
|
||||||
|
B2C customer's saved address) and is not what per-site reporting uses.
|
||||||
- **CityGate**: the pickup pincode prefix must be an open city — `641`
|
- **CityGate**: the pickup pincode prefix must be an open city — `641`
|
||||||
Coimbatore, `600` Chennai, `560` Bengaluru, `500` Hyderabad, `629` Nagercoil.
|
Coimbatore, `600` Chennai, `560` Bengaluru, `500` Hyderabad, `629` Nagercoil.
|
||||||
Any other prefix is refused at the middleware, before the handler runs.
|
Any other prefix is refused at the middleware, before the handler runs.
|
||||||
|
|||||||
@@ -98,13 +98,17 @@ Two jupiter bugs that do not carry over, by construction:
|
|||||||
`/admin/reports` now carries a `by_location` block alongside `by_hub`,
|
`/admin/reports` now carries a `by_location` block alongside `by_hub`,
|
||||||
`by_tenant` and `by_rider`.
|
`by_tenant` and `by_rider`.
|
||||||
|
|
||||||
**Attribution caveat.** Per-site figures depend on `pickuplocationid` being set
|
**Attribution caveat.** Per-site figures group by `tenantlocationid` on the
|
||||||
on the booking. Every booking created before 2026-08-06 has it null, and the
|
booking — a column added 2026-08-06. The pre-existing `pickuplocationid` column
|
||||||
console sends a kitchen's *address*, not its id. So `createExpressBooking` now
|
is *not* it: that one foreign-keys to `appcustomerlocations`, the B2C customer's
|
||||||
resolves the site itself — nearest stored location within 150m, falling back to
|
saved address, so writing a client-site id into it fails the insert. Every
|
||||||
an address match — and unattributed bookings are reported as their own
|
booking created before 2026-08-06 has no site at all.
|
||||||
`"Unattributed"` row rather than dropped, so the per-site rows still add up to
|
|
||||||
the summary total. Sending `pickuplocationid` explicitly is still better and
|
Since the console sends a kitchen's *address* rather than its id,
|
||||||
|
`createExpressBooking` resolves the site itself — nearest stored location within
|
||||||
|
150m, falling back to an address match. Bookings with no site are reported as
|
||||||
|
their own `"Unattributed"` row rather than dropped, so per-site rows still add
|
||||||
|
up to the summary total. Sending `tenantlocationid` explicitly is exact and
|
||||||
always wins.
|
always wins.
|
||||||
|
|
||||||
**`applocationid` (city) is still not a report parameter.** jupiter had it;
|
**`applocationid` (city) is still not a report parameter.** jupiter had it;
|
||||||
@@ -121,7 +125,7 @@ than one city.
|
|||||||
| ~ tenant create/edit | `POST /admin/tenants`, `PUT /admin/tenants/:id` | **Done** |
|
| ~ tenant create/edit | `POST /admin/tenants`, `PUT /admin/tenants/:id` | **Done** |
|
||||||
| ~ location create/edit | `POST /admin/tenants/:id/locations`, `PUT /admin/tenantlocations/:id` | **Done** |
|
| ~ location create/edit | `POST /admin/tenants/:id/locations`, `PUT /admin/tenantlocations/:id` | **Done** |
|
||||||
| ~ `getbranches` | `GET /admin/hubs` — *jupiter "branches" ≈ Doormile hubs; verify this is the same concept before relying on it* | **Built** |
|
| ~ `getbranches` | `GET /admin/hubs` — *jupiter "branches" ≈ Doormile hubs; verify this is the same concept before relying on it* | **Built** |
|
||||||
| ~ `getlocationsummary` | **Gap** — no per-site rollup, same missing piece as `locationid` above | **Gap** |
|
| ~ `getlocationsummary` | `GET /admin/locations/summary` — see §2.3 | **Done** |
|
||||||
|
|
||||||
Doormile adds `locationname` on a tenant location. jupiter identified a site by
|
Doormile adds `locationname` on a tenant location. jupiter identified a site by
|
||||||
its address alone, which does not distinguish two branches on one street.
|
its address alone, which does not distinguish two branches on one street.
|
||||||
|
|||||||
@@ -32,13 +32,17 @@ func (Pricing) TableName() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Consignment struct {
|
type Consignment struct {
|
||||||
Consignmentid int `json:"consignmentid" gorm:"primaryKey;column:consignmentid"`
|
Consignmentid int `json:"consignmentid" gorm:"primaryKey;column:consignmentid"`
|
||||||
Trackingno string `json:"trackingno" gorm:"column:trackingno;unique;not null"`
|
Trackingno string `json:"trackingno" gorm:"column:trackingno;unique;not null"`
|
||||||
Orderheaderid *int `json:"orderheaderid" gorm:"column:orderheaderid"`
|
Orderheaderid *int `json:"orderheaderid" gorm:"column:orderheaderid"`
|
||||||
Tenantid int `json:"tenantid" gorm:"column:tenantid"`
|
Tenantid int `json:"tenantid" gorm:"column:tenantid"`
|
||||||
Senderid *int `json:"senderid" gorm:"column:senderid"`
|
Senderid *int `json:"senderid" gorm:"column:senderid"`
|
||||||
Receiverid *int `json:"receiverid" gorm:"column:receiverid"`
|
Receiverid *int `json:"receiverid" gorm:"column:receiverid"`
|
||||||
|
// See PickupBooking: pickuplocationid is the customer's saved address,
|
||||||
|
// tenantlocationid is the client's own site. Carried over at pickup so a
|
||||||
|
// parcel stays traceable to the kitchen or branch it left.
|
||||||
Pickuplocationid *int `json:"pickuplocationid" gorm:"column:pickuplocationid"`
|
Pickuplocationid *int `json:"pickuplocationid" gorm:"column:pickuplocationid"`
|
||||||
|
Tenantlocationid *int `json:"tenantlocationid" gorm:"column:tenantlocationid;index"`
|
||||||
Deliverylocationid *int `json:"deliverylocationid" gorm:"column:deliverylocationid"`
|
Deliverylocationid *int `json:"deliverylocationid" gorm:"column:deliverylocationid"`
|
||||||
Originhubid *int `json:"originhubid" gorm:"column:originhubid"`
|
Originhubid *int `json:"originhubid" gorm:"column:originhubid"`
|
||||||
Currenthubid *int `json:"currenthubid" gorm:"column:currenthubid"`
|
Currenthubid *int `json:"currenthubid" gorm:"column:currenthubid"`
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type PickupBooking struct {
|
type PickupBooking struct {
|
||||||
Bookingid int `json:"bookingid" gorm:"primaryKey;column:bookingid"`
|
Bookingid int `json:"bookingid" gorm:"primaryKey;column:bookingid"`
|
||||||
Bookingno string `json:"bookingno" gorm:"column:bookingno;unique;not null"`
|
Bookingno string `json:"bookingno" gorm:"column:bookingno;unique;not null"`
|
||||||
// Tenantid identifies which client company this booking is for. Nil for
|
// Tenantid identifies which client company this booking is for. Nil for
|
||||||
// direct B2C bookings (Bookingsource "Customer_App") that aren't attributed
|
// direct B2C bookings (Bookingsource "Customer_App") that aren't attributed
|
||||||
// to a tenant yet — see CreateCustomerBooking. Required for CRM bookings
|
// to a tenant yet — see CreateCustomerBooking. Required for CRM bookings
|
||||||
@@ -14,30 +14,38 @@ type PickupBooking struct {
|
|||||||
// a specific tenant. Propagated onto the resulting Consignment at pickup
|
// a specific tenant. Propagated onto the resulting Consignment at pickup
|
||||||
// time in BookingPickupComplete, instead of inferring it from whichever
|
// time in BookingPickupComplete, instead of inferring it from whichever
|
||||||
// miler happens to complete the pickup.
|
// miler happens to complete the pickup.
|
||||||
Tenantid *int `json:"tenantid" gorm:"column:tenantid;index"`
|
Tenantid *int `json:"tenantid" gorm:"column:tenantid;index"`
|
||||||
Appcustomerid int `json:"appcustomerid" gorm:"column:appcustomerid"`
|
Appcustomerid int `json:"appcustomerid" gorm:"column:appcustomerid"`
|
||||||
Pickuplocationid *int `json:"pickuplocationid" gorm:"column:pickuplocationid"`
|
// Pickuplocationid is the *customer's* saved address the parcel was collected
|
||||||
Pickupaddress string `json:"pickupaddress" gorm:"column:pickupaddress;not null"`
|
// from — it carries a foreign key to appcustomerlocations. It is a B2C
|
||||||
Pickuppincode string `json:"pickuppincode" gorm:"column:pickuppincode;not null"`
|
// concept and has nothing to do with the client company's own sites.
|
||||||
Pickuplatitude float64 `json:"pickuplatitude" gorm:"column:pickuplatitude;not null"`
|
Pickuplocationid *int `json:"pickuplocationid" gorm:"column:pickuplocationid"`
|
||||||
Pickuplongitude float64 `json:"pickuplongitude" gorm:"column:pickuplongitude;not null"`
|
// Tenantlocationid is the *client's* site: the kitchen, branch or depot the
|
||||||
Deliveryaddress string `json:"deliveryaddress" gorm:"column:deliveryaddress;not null"`
|
// parcel came out of. Separate column because pickuplocationid points at a
|
||||||
Deliverypincode string `json:"deliverypincode" gorm:"column:deliverypincode;not null"`
|
// different table entirely; writing a tenantlocations id into it violates
|
||||||
Deliverylatitude float64 `json:"deliverylatitude" gorm:"column:deliverylatitude;not null"`
|
// that foreign key. This is what per-site reporting groups by.
|
||||||
Deliverylongitude float64 `json:"deliverylongitude" gorm:"column:deliverylongitude;not null"`
|
Tenantlocationid *int `json:"tenantlocationid" gorm:"column:tenantlocationid;index"`
|
||||||
Deliverycity string `json:"deliverycity" gorm:"column:deliverycity"`
|
Pickupaddress string `json:"pickupaddress" gorm:"column:pickupaddress;not null"`
|
||||||
Nearesthubid *int `json:"nearesthubid" gorm:"column:nearesthubid"`
|
Pickuppincode string `json:"pickuppincode" gorm:"column:pickuppincode;not null"`
|
||||||
Bookingsource string `json:"bookingsource" gorm:"column:bookingsource;default:Customer_App"`
|
Pickuplatitude float64 `json:"pickuplatitude" gorm:"column:pickuplatitude;not null"`
|
||||||
Providercompany string `json:"providercompany" gorm:"column:providercompany"`
|
Pickuplongitude float64 `json:"pickuplongitude" gorm:"column:pickuplongitude;not null"`
|
||||||
Providerlocation string `json:"providerlocation" gorm:"column:providerlocation"`
|
Deliveryaddress string `json:"deliveryaddress" gorm:"column:deliveryaddress;not null"`
|
||||||
Notes string `json:"notes" gorm:"column:notes"`
|
Deliverypincode string `json:"deliverypincode" gorm:"column:deliverypincode;not null"`
|
||||||
Status string `json:"status" gorm:"column:status;default:Created"` // Created, Miler_Assigned, Pickup_Scheduled, Picked_Up, Converted_To_Consignment, Cancelled
|
Deliverylatitude float64 `json:"deliverylatitude" gorm:"column:deliverylatitude;not null"`
|
||||||
Preferredpickupfrom *time.Time `json:"preferredpickupfrom" gorm:"column:preferredpickupfrom"`
|
Deliverylongitude float64 `json:"deliverylongitude" gorm:"column:deliverylongitude;not null"`
|
||||||
Preferredpickupto *time.Time `json:"preferredpickupto" gorm:"column:preferredpickupto"`
|
Deliverycity string `json:"deliverycity" gorm:"column:deliverycity"`
|
||||||
Assignedmileruserid *int `json:"assignedmileruserid" gorm:"column:assignedmileruserid"`
|
Nearesthubid *int `json:"nearesthubid" gorm:"column:nearesthubid"`
|
||||||
Consignmentid *int `json:"consignmentid" gorm:"column:consignmentid"`
|
Bookingsource string `json:"bookingsource" gorm:"column:bookingsource;default:Customer_App"`
|
||||||
Createdat time.Time `json:"createdat" gorm:"column:createdat;default:CURRENT_TIMESTAMP"`
|
Providercompany string `json:"providercompany" gorm:"column:providercompany"`
|
||||||
Updatedat time.Time `json:"updatedat" gorm:"column:updatedat;default:CURRENT_TIMESTAMP"`
|
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
|
// Relations
|
||||||
Parcels []BookingParcel `json:"parcels" gorm:"foreignKey:Bookingid"`
|
Parcels []BookingParcel `json:"parcels" gorm:"foreignKey:Bookingid"`
|
||||||
@@ -91,7 +99,7 @@ type BookingPayment struct {
|
|||||||
Bookingpaymentid int `json:"bookingpaymentid" gorm:"primaryKey;column:bookingpaymentid"`
|
Bookingpaymentid int `json:"bookingpaymentid" gorm:"primaryKey;column:bookingpaymentid"`
|
||||||
Bookingid int `json:"bookingid" gorm:"column:bookingid"`
|
Bookingid int `json:"bookingid" gorm:"column:bookingid"`
|
||||||
Amount float64 `json:"amount" gorm:"column:amount;not null"`
|
Amount float64 `json:"amount" gorm:"column:amount;not null"`
|
||||||
Paymentmode string `json:"paymentmode" gorm:"column:paymentmode"` // Cash, UPI, Card, Wallet
|
Paymentmode string `json:"paymentmode" gorm:"column:paymentmode"` // Cash, UPI, Card, Wallet
|
||||||
Paymentstatus string `json:"paymentstatus" gorm:"column:paymentstatus;default:Pending"` // Pending, Paid, Failed, Refunded
|
Paymentstatus string `json:"paymentstatus" gorm:"column:paymentstatus;default:Pending"` // Pending, Paid, Failed, Refunded
|
||||||
Collectedbyuserid *int `json:"collectedbyuserid" gorm:"column:collectedbyuserid"`
|
Collectedbyuserid *int `json:"collectedbyuserid" gorm:"column:collectedbyuserid"`
|
||||||
Transactionref string `json:"transactionref" gorm:"column:transactionref"`
|
Transactionref string `json:"transactionref" gorm:"column:transactionref"`
|
||||||
@@ -129,8 +137,8 @@ type BookingVehicleRequirement struct {
|
|||||||
Requiredvehicletype string `json:"requiredvehicletype" gorm:"column:requiredvehicletype;not null"`
|
Requiredvehicletype string `json:"requiredvehicletype" gorm:"column:requiredvehicletype;not null"`
|
||||||
Reason string `json:"reason" gorm:"column:reason"`
|
Reason string `json:"reason" gorm:"column:reason"`
|
||||||
Nearesthubid *int `json:"nearesthubid" gorm:"column:nearesthubid"`
|
Nearesthubid *int `json:"nearesthubid" gorm:"column:nearesthubid"`
|
||||||
Scheduledpickupfrom *time.Time `json:"scheduledpickupfrom" gorm:"column:scheduledpickupfrom"`
|
Scheduledpickupfrom *time.Time `json:"scheduledpickupfrom" gorm:"column:scheduledpickupfrom"`
|
||||||
Scheduledpickupto *time.Time `json:"scheduledpickupto" gorm:"column:scheduledpickupto"`
|
Scheduledpickupto *time.Time `json:"scheduledpickupto" gorm:"column:scheduledpickupto"`
|
||||||
Assignedvehicleid *int `json:"assignedvehicleid" gorm:"column:assignedvehicleid"`
|
Assignedvehicleid *int `json:"assignedvehicleid" gorm:"column:assignedvehicleid"`
|
||||||
Assigneddriveruserid *int `json:"assigneddriveruserid" gorm:"column:assigneddriveruserid"`
|
Assigneddriveruserid *int `json:"assigneddriveruserid" gorm:"column:assigneddriveruserid"`
|
||||||
Status string `json:"status" gorm:"column:status;default:Required"` // Required, Scheduled, Assigned, Arrived, Picked_Up, Cancelled
|
Status string `json:"status" gorm:"column:status;default:Required"` // Required, Scheduled, Assigned, Arrived, Picked_Up, Cancelled
|
||||||
|
|||||||
Reference in New Issue
Block a user