This commit is contained in:
2026-09-02 15:37:59 +05:30
parent 68871cb043
commit 09efc5403f
6 changed files with 289 additions and 46 deletions

View File

@@ -54,6 +54,34 @@ func NewOrderRepository(db *gorm.DB) OrderRepository {
// It made a real revenue figure indistinguishable from an order that genuinely
// booked nothing, which is why the historic orders on several tenants all look
// worthless.
// ── Why every join below is a LEFT JOIN ──────────────────────────────────────
//
// These queries list orders. An order that exists is an order that must be
// listable, so a lookup that cannot be resolved may blank a column — it must
// never remove the row.
//
// They were INNER JOINs against customers, tenants, tenantlocations,
// app_location and app_locationconfig, which made each one a silent filter on a
// column the caller never asked to filter by. An order written with
// applocationid 0 or customerid 0 was created successfully, deducted stock, and
// then appeared in no screen that lists orders — not the shopper’s own history,
// not the shop’s. Measured 2026-09-02: two identical orders one field apart,
// both in the database, one of them visible.
//
// Some of these joins contributed no columns at all to the query they sat in,
// so they were pure filters by accident.
//
// The workaround is still in the tree and shows what this cost: the offline
// sales importer builds "header scaffolding" and invents a walk-in customer
// solely so its orders would not vanish (offlineLocationContext,
// resolveOfflineCustomer). Populating those fields stays good practice, but it
// is no longer what keeps an order visible.
//
// Deliberate filtering is unaffected. A query with a WHERE on a joined table —
// GetUserOrders filters e.status and e.userid — still excludes rows the join
// could not resolve, because WHERE runs after the join. GetDistinctLocations
// keeps its INNER JOIN on purpose: it asks which app locations have orders, so
// one that cannot be resolved is genuinely not an answer.
const (
base = `SELECT DISTINCT a.orderheaderid, a.applocationid, h.locationname AS applocation, a.tenantid, a.locationid, a.partnerid, a.configid, a.categoryid, a.subcategoryid, a.moduleid,
a.orderid, a.orderstatus, a.orderdate, a.ordernotes, a.itemcount, a.deliverytime AS deliverydate,
@@ -67,12 +95,12 @@ const (
c.tenantname, c.tenanttoken, c.primarycontact AS tenantcontactno, c.postcode AS tenantpostcode, c.suburb AS tenantsuburb, c.city AS tenantcity,
d.locationname, d.contactno AS locationcontactno, d.postcode AS locationpostcode, d.suburb AS locationsuburb, d.city AS locationcity
FROM orders a
INNER JOIN customers b ON a.customerid = b.customerid
INNER JOIN tenants c ON a.tenantid = c.tenantid
INNER JOIN tenantlocations d ON a.locationid = d.locationid
LEFT JOIN customers b ON a.customerid = b.customerid
LEFT JOIN tenants c ON a.tenantid = c.tenantid
LEFT JOIN tenantlocations d ON a.locationid = d.locationid
INNER JOIN app_location h ON a.applocationid = h.applocationid
INNER JOIN app_locationconfig i ON a.applocationid = i.applocationid`
LEFT JOIN app_location h ON a.applocationid = h.applocationid
LEFT JOIN app_locationconfig i ON a.applocationid = i.applocationid`
orderdetails = `SELECT DISTINCT a.orderheaderid, a.applocationid,
a.tenantid, a.locationid, a.partnerid, a.configid, a.categoryid, a.subcategoryid, a.moduleid,
@@ -87,10 +115,10 @@ const (
c.locationname, c.contactno AS locationcontactno, c.postcode AS locationpostcode, c.suburb AS locationsuburb, c.city AS locationcity,
d.locationname AS applocation
FROM orders a
INNER JOIN tenants b ON a.tenantid = b.tenantid
INNER JOIN tenantlocations c ON a.locationid = c.locationid
INNER JOIN app_location d ON a.applocationid = d.applocationid
INNER JOIN app_locationconfig e ON d.applocationid = e.applocationid`
LEFT JOIN tenants b ON a.tenantid = b.tenantid
LEFT JOIN tenantlocations c ON a.locationid = c.locationid
LEFT JOIN app_location d ON a.applocationid = d.applocationid
LEFT JOIN app_locationconfig e ON d.applocationid = e.applocationid`
)
func (r *orderRepository) GetTenantOrders(input models.DeliveryQuery) ([]models.OrderInfo, error) {
@@ -102,15 +130,33 @@ func (r *orderRepository) GetTenantOrders(input models.DeliveryQuery) ([]models.
baseQuery := base + ` WHERE a.tenantid = ?`
params = append(params, input.Tenantid)
// Status filters only when one was asked for.
//
// The else branch ran unconditionally, so a request naming no status became
// `orderstatus = ''` — which matches nothing. That is precisely the
// console’s default view: All branches, no status chosen. It listed no
// orders at all, however many the shop had taken.
//
// Compared case-insensitively because the ladder is stored capitalised
// ("Pending") while every literal here is lower case. Postgres agrees:
// `SELECT 'Pending' IN ('pending','processing','ready')` is false, so
// "ongoing" matched nothing either.
query = baseQuery
if input.Status == "ongoing" {
query = baseQuery + ` AND a.orderstatus IN ('pending','processing','ready')`
} else {
query = baseQuery + ` AND a.orderstatus = ?`
query += ` AND LOWER(a.orderstatus) IN ('pending','processing','ready')`
} else if input.Status != "" {
query += ` AND LOWER(a.orderstatus) = LOWER(?)`
params = append(params, input.Status)
}
query += ` AND a.deliverytime::date BETWEEN ? AND ?`
params = append(params, input.Fromdate, input.ToDate)
// A date range narrows the list; it is not a precondition for having one.
// Applied unconditionally it cast the empty string to a date and answered
// 500, so the console’s order list failed outright until somebody picked a
// range.
if input.Fromdate != "" && input.ToDate != "" {
query += ` AND a.deliverytime::date BETWEEN ? AND ?`
params = append(params, input.Fromdate, input.ToDate)
}
if input.Keyword != "" {
query += ` AND (
@@ -160,7 +206,7 @@ func (r *orderRepository) GetPartnerOrders(stat, fdate, tdate string, pid, pagen
params = append(params, fdate, tdate)
}
if stat != "" {
query += ` AND a.orderstatus = ?`
query += ` AND LOWER(a.orderstatus) = LOWER(?)`
params = append(params, stat)
}
if keyword != "" {
@@ -180,10 +226,10 @@ func (r *orderRepository) GetPartnerOrders(stat, fdate, tdate string, pid, pagen
countQuery := `
SELECT COUNT(DISTINCT a.orderheaderid)
FROM orders a
INNER JOIN tenants b ON a.tenantid = b.tenantid
INNER JOIN tenantlocations c ON a.locationid = c.locationid
INNER JOIN app_location d ON a.applocationid = d.applocationid
INNER JOIN app_locationconfig e ON d.applocationid = e.applocationid
LEFT JOIN tenants b ON a.tenantid = b.tenantid
LEFT JOIN tenantlocations c ON a.locationid = c.locationid
LEFT JOIN app_location d ON a.applocationid = d.applocationid
LEFT JOIN app_locationconfig e ON d.applocationid = e.applocationid
LEFT JOIN deliveries f ON a.orderheaderid = f.orderheaderid
LEFT JOIN app_users g ON f.userid = g.userid
WHERE a.partnerid = ?`
@@ -274,10 +320,10 @@ func (r *orderRepository) GetCustomerOrders(stat, fdate, tdate string, cid, mid,
countQuery := `
SELECT COUNT(DISTINCT a.orderheaderid)
FROM orders a
INNER JOIN tenants b ON a.tenantid = b.tenantid
INNER JOIN tenantlocations c ON a.locationid = c.locationid
INNER JOIN app_location d ON a.applocationid = d.applocationid
INNER JOIN app_locationconfig e ON d.applocationid = e.applocationid
LEFT JOIN tenants b ON a.tenantid = b.tenantid
LEFT JOIN tenantlocations c ON a.locationid = c.locationid
LEFT JOIN app_location d ON a.applocationid = d.applocationid
LEFT JOIN app_locationconfig e ON d.applocationid = e.applocationid
WHERE a.customerid = ?`
countParams := []interface{}{cid}
@@ -376,10 +422,10 @@ func (r *orderRepository) GetAdminOrders(stat, fdate, tdate string, aid, pageno,
countQuery := `
SELECT COUNT(DISTINCT a.orderheaderid)
FROM orders a
INNER JOIN tenants b ON a.tenantid = b.tenantid
INNER JOIN tenantlocations c ON a.locationid = c.locationid
INNER JOIN app_location d ON a.applocationid = d.applocationid
INNER JOIN app_locationconfig e ON d.applocationid = e.applocationid
LEFT JOIN tenants b ON a.tenantid = b.tenantid
LEFT JOIN tenantlocations c ON a.locationid = c.locationid
LEFT JOIN app_location d ON a.applocationid = d.applocationid
LEFT JOIN app_locationconfig e ON d.applocationid = e.applocationid
LEFT JOIN deliveries f ON a.orderheaderid = f.orderheaderid
LEFT JOIN app_users g ON f.userid = g.userid
WHERE 1=1
@@ -471,10 +517,10 @@ func (r *orderRepository) GetUserOrders(stat, fdate, tdate string, uid, pageno,
countQuery := `
SELECT COUNT(DISTINCT a.orderheaderid)
FROM orders a
INNER JOIN tenants b ON a.tenantid = b.tenantid
INNER JOIN tenantlocations c ON a.locationid = c.locationid
INNER JOIN app_location d ON a.applocationid = d.applocationid
INNER JOIN app_locationconfig e ON d.applocationid = e.applocationid
LEFT JOIN tenants b ON a.tenantid = b.tenantid
LEFT JOIN tenantlocations c ON a.locationid = c.locationid
LEFT JOIN app_location d ON a.applocationid = d.applocationid
LEFT JOIN app_locationconfig e ON d.applocationid = e.applocationid
LEFT JOIN deliveries f ON a.orderheaderid = f.orderheaderid
LEFT JOIN app_users g ON f.userid = g.userid
WHERE e.status = 'Active' AND e.userid = ?
@@ -564,10 +610,10 @@ func (r *orderRepository) GetAllOrders(stat, fdate, tdate string, pageno, pagesi
countQuery := `
SELECT COUNT(DISTINCT a.orderheaderid)
FROM orders a
INNER JOIN tenants b ON a.tenantid = b.tenantid
INNER JOIN tenantlocations c ON a.locationid = c.locationid
INNER JOIN app_location d ON a.applocationid = d.applocationid
INNER JOIN app_locationconfig e ON d.applocationid = e.applocationid
LEFT JOIN tenants b ON a.tenantid = b.tenantid
LEFT JOIN tenantlocations c ON a.locationid = c.locationid
LEFT JOIN app_location d ON a.applocationid = d.applocationid
LEFT JOIN app_locationconfig e ON d.applocationid = e.applocationid
LEFT JOIN deliveries f ON a.orderheaderid = f.orderheaderid
LEFT JOIN app_users g ON f.userid = g.userid
WHERE 1=1
@@ -2314,16 +2360,16 @@ func (r *orderRepository) GetCustomerOrdersv3(customerID, tenantID, moduleID, fr
a.pickupcity, a.deliveryid AS deliverycustomerid, a.deliveryaddress, a.deliverylat,
a.deliverylong, a.deliverytype, a.deliverycustomer, a.deliverycontactno,
a.deliverylocation AS deliverysuburb, a.deliverycity, a.paymenttype, a.smsdelivery,
a.taxamount,
a.orderamount, a.taxamount,
b.tenantname, b.tenanttoken, b.primarycontact AS tenantcontactno,
b.postcode AS tenantpostcode, b.suburb AS tenantsuburb, b.city AS tenantcity, b.registrationno,
c.locationname, c.contactno AS locationcontactno, c.postcode AS locationpostcode,
c.suburb AS locationsuburb, c.city AS locationcity,
d.locationname AS applocation
FROM orders a
INNER JOIN tenants b ON a.tenantid = b.tenantid
INNER JOIN tenantlocations c ON a.locationid = c.locationid
INNER JOIN app_location d ON a.applocationid = d.applocationid
LEFT JOIN tenants b ON a.tenantid = b.tenantid
LEFT JOIN tenantlocations c ON a.locationid = c.locationid
LEFT JOIN app_location d ON a.applocationid = d.applocationid
WHERE 1=1
`
@@ -2402,10 +2448,15 @@ func (r *orderRepository) attachOrderDetails(orders []models.CustomerOrder) ([]m
for i := range orders {
orders[i].OrderDetails = detailMap[orders[i].Orderheaderid]
if len(orders[i].OrderDetails) > 0 {
orders[i].Orderamount = orders[i].OrderDetails[0].Orderamount
orders[i].Totaltaxamount = orders[i].OrderDetails[0].Totaltaxamount
}
// The header already carries the order total, and it is the only place
// that has one. This used to overwrite it with the first LINE’s
// Orderamount and Totaltaxamount — columns that do not exist on the
// orderdetails table, so both scanned as zero and clobbered a correct
// value. Every order in a shopper’s history read ₹0 as a result, however
// much they had actually spent.
//
// Taking the first line’s figure could not have been right even if the
// columns existed: one line is not the order.
}
return orders, nil
@@ -2427,7 +2478,7 @@ func (r *orderRepository) GetTenantLocationOrders(input models.DeliveryQuery) ([
// Status filter
if input.Status == "ongoing" {
query += ` AND a.orderstatus IN ('pending','processing','ready')`
query += ` AND LOWER(a.orderstatus) IN ('pending','processing','ready')`
} else if input.Status != "" {
query += ` AND a.orderstatus = ?`
params = append(params, input.Status)
@@ -2446,8 +2497,12 @@ func (r *orderRepository) GetTenantLocationOrders(input models.DeliveryQuery) ([
}
// Date filter
query += ` AND DATE(a.deliverytime) BETWEEN ? AND ?`
params = append(params, input.Fromdate, input.ToDate)
// Narrows the list rather than gating it — same reasoning as
// GetTenantOrders above, where empty dates cast to a date and answered 500.
if input.Fromdate != "" && input.ToDate != "" {
query += ` AND DATE(a.deliverytime) BETWEEN ? AND ?`
params = append(params, input.Fromdate, input.ToDate)
}
// Keyword filter
if input.Keyword != "" {