package repositories import ( "errors" "fmt" "log" "nearle/models" "strconv" "strings" "time" "github.com/jinzhu/copier" "gorm.io/gorm" ) type DeliveriesRepository interface { CreateDeliveries(data []models.Deliveries) error UpdateDelivery(data models.UpdateDeliveryStatus) error GetDeliverySummary(tid, pid, uid, aid, lid int, fdate, tdate string) (models.DeliverySummary, error) GetDeliveryInsight(tid int) ([]models.OrderInsightv1, error) GetLocationDeliverySummary(tenantID int) ([]models.Ordersummarylocation, error) GetReportSummary(tid, pid, uid, aid int, fdate, tdate string) ([]models.ReportSummary, error) GetRiderSummary(aid, pid, tid int, fdate, tdate string) ([]models.Ridersummary, error) GetTenantDeliveries(input models.DeliveryQuery) []models.Deliveryinfo GetPartnerDeliveries(input models.DeliveryQuery) []models.Deliveryinfo GetCustomerDeliveries(input models.DeliveryQuery) []models.Deliveryinfo GetAdminDeliveries(input models.DeliveryQuery) []models.Deliveryinfo GetUserDeliveries(input models.DeliveryQuery) []models.Deliveryinfo GetAppUserDeliveries(input models.DeliveryQuery) []models.Deliveryinfo GetDeliveries(input models.DeliveryQuery) []models.Deliveryinfo GetDeliveryQueues(uid int, fdate, tdate string) ([]models.Deliveryinfo, error) GetTenantLocationDeliveries(input models.DeliveryQuery) []models.Deliveryinfo } type deliveriesRepository struct { db *gorm.DB } func NewDeliveriesRepository(db *gorm.DB) DeliveriesRepository { return &deliveriesRepository{db: db} } const ( core = `SELECT COUNT(*) AS total, SUM(CASE WHEN orderstatus = 'pending' THEN 1 ELSE 0 END) AS pending, SUM(CASE WHEN orderstatus = 'accepted' THEN 1 ELSE 0 END) AS accepted, SUM(CASE WHEN orderstatus = 'arrived' THEN 1 ELSE 0 END) AS arrived, SUM(CASE WHEN orderstatus = 'picked' THEN 1 ELSE 0 END) AS picked, SUM(CASE WHEN orderstatus = 'active' THEN 1 ELSE 0 END) AS active, SUM(CASE WHEN orderstatus = 'delivered' THEN 1 ELSE 0 END) AS delivered, SUM(CASE WHEN orderstatus = 'cancelled' THEN 1 ELSE 0 END) AS cancelled` reports = `SELECT b.tenantid,b.tenantname, COUNT(*) AS totalorders, SUM(CASE WHEN a.orderstatus = 'created' THEN 1 ELSE 0 END) AS orderscreated, SUM(CASE WHEN a.orderstatus = 'pending' THEN 1 ELSE 0 END) AS orderspending, SUM(CASE WHEN a.orderstatus = 'cancelled' THEN 1 ELSE 0 END) AS orderscancelled, SUM(CASE WHEN a.orderstatus = 'delivered' THEN 1 ELSE 0 END) AS orderscompleted, SUM(CASE WHEN c.orderstatus = 'pending' THEN 1 ELSE 0 END) AS deliveriespending, SUM(CASE WHEN c.orderstatus = 'delivered' THEN 1 ELSE 0 END) AS deliveriescompleted, SUM(CASE WHEN c.orderstatus = 'cancelled' THEN 1 ELSE 0 END) AS deliveriescancelled, SUM(CASE WHEN c.paymenttype = 64 THEN c.deliveryamt ELSE 0 END) AS paylater, SUM(CASE WHEN c.paymenttype = 43 THEN c.deliveryamt ELSE 0 END) AS payondelivery, -- Cast before summing: both are TEXT columns. -- Raw, this is "function sum(text) does not exist" and the endpoint 500s on -- every call — verified on production 2026-09-04. Same defect as the rider -- summary above, which is the other place these two columns are added up. ROUND(SUM(COALESCE(NULLIF(c.kms, '')::numeric, 0)), 2) AS kms, ROUND(SUM(COALESCE(NULLIF(c.actualkms, '')::numeric, 0)), 2) AS actualkms, SUM(c.deliveryamt) AS charges FROM tenants b right JOIN orders a ON b.tenantid = a.tenantid right JOIN deliveries c ON a.orderheaderid = c.orderheaderid ` Ridersummary = `SELECT a.userid, a.firstname, a.lastname, CONCAT(a.firstname, ' ', a.lastname) AS fullname,a.status, SUM(CASE WHEN b.orderstatus = 'pending' THEN 1 ELSE 0 END) AS deliveries, SUM(CASE WHEN b.orderstatus = 'assigned' THEN 1 ELSE 0 END) AS assigned, SUM(CASE WHEN b.orderstatus = 'accepted' THEN 1 ELSE 0 END) AS accepted, SUM(CASE WHEN b.orderstatus = 'arrived' THEN 1 ELSE 0 END) AS arrived, SUM(CASE WHEN b.orderstatus = 'picked' THEN 1 ELSE 0 END) AS picked, SUM(CASE WHEN b.orderstatus = 'delivered' THEN 1 ELSE 0 END) AS delivered, -- kms and actualkms are TEXT columns, not numbers. -- -- Summed raw they produce -- "CASE types integer and text cannot be matched", because the ELSE arm is -- the integer 0 — so this endpoint answered 500 on every call. Verified on -- production 2026-09-04. Every other SUM here is over a real numeric -- (deliveryamt) or a literal 1, which is why only these two broke it. -- -- NULLIF handles the empty string, which is what an unridden job carries -- and what ::numeric would choke on; a value that is not a number at all -- would still raise, but nothing writes one. SUM(CASE WHEN b.orderstatus = 'delivered' THEN COALESCE(NULLIF(b.actualkms, '')::numeric, 0) ELSE 0 END) AS actualkms, SUM(CASE WHEN b.orderstatus = 'delivered' THEN COALESCE(NULLIF(b.kms, '')::numeric, 0) ELSE 0 END) AS kms, SUM(CASE WHEN b.paymenttype = 64 THEN b.deliveryamt ELSE 0 END) AS paylater, SUM(CASE WHEN b.paymenttype = 43 THEN b.deliveryamt ELSE 0 END) AS payondelivery, SUM(CASE WHEN b.orderstatus = 'delivered' THEN b.deliveryamt ELSE 0 END) AS deliveryamt FROM app_users a LEFT JOIN deliveries b ON a.userid = b.userid` ) const ( deliveries = `SELECT distinct a.deliveryid,a.applocationid,f.locationname AS applocation,a.orderheaderid,a.configid,a.tenantid,a.partnerid,a.locationid,a.userid,a.categoryid,a.subcategoryid,a.moduleid, a.orderid,a.deliverydate,a.orderstatus,a.assigntime,a.starttime,a.arrivaltime,a.pickuptime,a.deliverytime,a.canceltime, a.customerid,a.pickupcustomer,a.pickupcontactno,a.pickuplocationid,a.pickupaddress,a.pickuplocation,a.pickuplat,a.pickuplon, a.deliverycustomerid,a.deliverycustomer,a.deliverycontactno,a.deliverylocationid,a.deliveryaddress,a.deliverylocation, a.droplat,a.droplon,a.deliverylat,a.deliverylong, a.riderslat,a.riderslon,a.deliveryamt,a.kms,a.actualkms,a.riderkms,a.deliverycharges,a.deliverytype,a.paymenttype,a.smsdelivery, a.expecteddeliverytime,a.profit,a.transitminutes,a.calculationdistancekm, a.notes,a.ordernotes,b.tenantname,b.primarycontact as tenantcontactno,b.tenanttoken,b.suburb as tenantsuburb,b.city as tenantcity, c.firstname AS ridername,c.userfcmtoken,e.locationname,e.suburb AS locationsuburb,e.contactno AS locationcontactno FROM deliveries a INNER JOIN tenants b ON a.tenantid=b.tenantid INNER JOIN app_users c ON a.userid=c.userid INNER JOIN tenantlocations e ON a.locationid=e.locationid INNER JOIN app_location f ON a.applocationid = f.applocationid INNER JOIN app_locationconfig g ON f.applocationid = g.applocationid` ) func (r *deliveriesRepository) CreateDeliveries(data []models.Deliveries) error { var que []models.Deliveryqueues var ord models.Updateorderstatus tx := r.db.Begin() for i := range data { if err := tx.Create(&data[i]).Error; err != nil { tx.Rollback() return err } if err := copier.Copy(&que, &data[i]); err != nil { tx.Rollback() return err } if err := tx.Create(&que).Error; err != nil { tx.Rollback() return err } ord.Orderstatus = data[i].Orderstatus ord.Pending = data[i].Deliverydate res := tx.Table("orders"). Where("orderheaderid=?", data[i].Orderheaderid). Updates(&ord) if res.Error != nil { tx.Rollback() return res.Error } // An UPDATE matching no rows is not an SQL error, so a delivery could // be created against an orderheaderid that does not exist and the whole // call would still report success — leaving a job in the rider's queue // for an order nobody can open. Same shape as the stock-request reject // that reported "4 updated" when only 3 existed. if res.RowsAffected == 0 { tx.Rollback() return fmt.Errorf("order %d not found", data[i].Orderheaderid) } // Link the order back to its delivery. // // Nothing wrote this column. `orders.deliveryid` was NULL on every row // in production, which meant there was no way to ask an order whether // it had been assigned — the console had to cross-reference the whole // deliveries list to find out. Written here, in the same transaction as // the delivery itself, so the two can never disagree. // // `data[i].Deliveryid` is filled in by the Create above: it is the // primary key GORM reads back after the insert. if err := tx.Table("orders"). Where("orderheaderid=?", data[i].Orderheaderid). Update("deliveryid", data[i].Deliveryid).Error; err != nil { tx.Rollback() return err } } if err := tx.Commit().Error; err != nil { return err } return nil } func (r *deliveriesRepository) UpdateDelivery(data models.UpdateDeliveryStatus) error { var ord models.Updateorderstatus var cloc models.Customerlocations if data.Deliveryid == 0 { return errors.New("deliveryid is required") } tx := r.db.Begin() if tx.Error != nil { return tx.Error } if err := tx.Table("deliveries").Where("deliveryid = ?", data.Deliveryid).Updates(&data).Error; err != nil { tx.Rollback() return err } // The parent order is resolved from the delivery row rather than taken from // the request. Every status branch below writes the order with // "WHERE orderheaderid = ?", and a client that omits orderheaderid made that // "WHERE orderheaderid = 0", matching nothing. GORM reports no error for an // update that affects no rows, so the handler still answered 201 Success // while the order silently kept its old status — 635 deliveries are marked // delivered against an order still reading pending because of this. // // deliveryid is the one field every caller must send (it is how the row // above is found), so deriving the link from it makes the sync independent // of how complete the client's payload is. orderHeaderID := data.Orderheaderid if orderHeaderID == 0 { if err := tx.Table("deliveries"). Select("orderheaderid"). Where("deliveryid = ?", data.Deliveryid). Scan(&orderHeaderID).Error; err != nil { tx.Rollback() return err } } if orderHeaderID == 0 { tx.Rollback() return fmt.Errorf("delivery %d has no order attached", data.Deliveryid) } // syncOrder applies the status to the parent order and fails loudly if the // row is not there, instead of reporting success for a write that landed // nowhere. syncOrder := func() error { res := tx.Table("orders").Where("orderheaderid = ?", orderHeaderID).Updates(&ord) if res.Error != nil { return res.Error } if res.RowsAffected == 0 { return fmt.Errorf("order %d not found for delivery %d", orderHeaderID, data.Deliveryid) } return nil } // The lifecycle timestamp mirrored onto the order. Clients frequently send // the status without one, and because Updates() skips zero-valued struct // fields the order's own column was left blank while its status moved on. stamp := func(supplied string) string { if strings.TrimSpace(supplied) != "" { return supplied } return time.Now().Format("2006-01-02 15:04:05") } switch data.Orderstatus { case "pending": ord.Orderstatus = data.Orderstatus ord.Pending = stamp(data.Assigntime) if err := syncOrder(); err != nil { tx.Rollback() return err } case "picked": if data.Pickuplocationid != 0 && data.Deliverytype != "B" { cloc.Latitude = models.FlexibleString(data.Riderslat) cloc.Longitude = models.FlexibleString(data.Riderslon) cloc.Address = data.Address cloc.Suburb = data.Suburb cloc.City = data.City cloc.State = data.State cloc.Postcode = data.Postcode if err := tx.Table("customerlocations").Where("locationid = ?", data.Pickuplocationid).Updates(&cloc).Error; err != nil { tx.Rollback() return err } } case "delivered": ord.Orderstatus = data.Orderstatus ord.Delivered = stamp(data.Deliverytime) if err := syncOrder(); err != nil { tx.Rollback() return err } if data.Deliverylocationid != 0 && data.Deliverytype != "B" { cloc.Latitude = models.FlexibleString(data.Deliverylat) cloc.Longitude = models.FlexibleString(data.Deliverylong) cloc.Address = data.Address cloc.Suburb = data.Suburb cloc.City = data.City cloc.State = data.State cloc.Postcode = data.Postcode if err := tx.Table("customerlocations").Where("locationid = ?", data.Deliverylocationid).Updates(&cloc).Error; err != nil { tx.Rollback() return err } } case "cancelled": ord.Orderstatus = data.Orderstatus ord.Cancelled = stamp(data.Canceltime) if err := syncOrder(); err != nil { tx.Rollback() return err } } if err := tx.Commit().Error; err != nil { return err } return nil } func (r *deliveriesRepository) GetDeliverySummary(tid, pid, uid, aid, lid int, fdate, tdate string) (models.DeliverySummary, error) { var data models.DeliverySummary var q1 string var params []interface{} // Base query q1 = core + " FROM deliveries WHERE configid = 1" // Filters (allow multiple together) if tid != 0 { q1 += " AND tenantid = ?" params = append(params, tid) } if pid != 0 { q1 += " AND partnerid = ?" params = append(params, pid) } if uid != 0 { q1 += " AND userid = ?" params = append(params, uid) } if aid != 0 { q1 += " AND applocationid = ?" params = append(params, aid) } if lid != 0 { q1 += " AND locationid = ?" params = append(params, lid) } // Date filter if fdate != "" && tdate != "" { q1 += " AND deliverydate::date BETWEEN ? AND ?" params = append(params, fdate, tdate) } // Execute query if err := r.db.Raw(q1, params...).Scan(&data).Error; err != nil { return data, err } print(q1) return data, nil } func (r *deliveriesRepository) GetDeliveryInsight(tenantID int) ([]models.OrderInsightv1, error) { var locations []models.OrderInsightv1 var params []interface{} // Base query for fetching active locations q1 := ` SELECT DISTINCT l.locationid, l.locationname FROM tenantlocations l LEFT JOIN deliveries d ON l.locationid = d.locationid WHERE l.status = 'Active' ` // Add tenant filter if provided if tenantID != 0 { q1 += " AND l.tenantid = ?" params = append(params, tenantID) } if err := r.db.Raw(q1, params...).Scan(&locations).Error; err != nil { return nil, err } // Query for monthly deliveries q2 := ` SELECT COALESCE(SUM(CASE WHEN EXTRACT(MONTH FROM d.deliverydate) = 1 THEN 1 ELSE 0 END), 0) AS jan, COALESCE(SUM(CASE WHEN EXTRACT(MONTH FROM d.deliverydate) = 2 THEN 1 ELSE 0 END), 0) AS feb, COALESCE(SUM(CASE WHEN EXTRACT(MONTH FROM d.deliverydate) = 3 THEN 1 ELSE 0 END), 0) AS mar, COALESCE(SUM(CASE WHEN EXTRACT(MONTH FROM d.deliverydate) = 4 THEN 1 ELSE 0 END), 0) AS apr, COALESCE(SUM(CASE WHEN EXTRACT(MONTH FROM d.deliverydate) = 5 THEN 1 ELSE 0 END), 0) AS may, COALESCE(SUM(CASE WHEN EXTRACT(MONTH FROM d.deliverydate) = 6 THEN 1 ELSE 0 END), 0) AS jun, COALESCE(SUM(CASE WHEN EXTRACT(MONTH FROM d.deliverydate) = 7 THEN 1 ELSE 0 END), 0) AS jul, COALESCE(SUM(CASE WHEN EXTRACT(MONTH FROM d.deliverydate) = 8 THEN 1 ELSE 0 END), 0) AS aug, COALESCE(SUM(CASE WHEN EXTRACT(MONTH FROM d.deliverydate) = 9 THEN 1 ELSE 0 END), 0) AS sep, COALESCE(SUM(CASE WHEN EXTRACT(MONTH FROM d.deliverydate) = 10 THEN 1 ELSE 0 END), 0) AS oct, COALESCE(SUM(CASE WHEN EXTRACT(MONTH FROM d.deliverydate) = 11 THEN 1 ELSE 0 END), 0) AS nov, COALESCE(SUM(CASE WHEN EXTRACT(MONTH FROM d.deliverydate) = 12 THEN 1 ELSE 0 END), 0) AS dece FROM deliveries d WHERE d.locationid = ? AND EXTRACT(YEAR FROM d.deliverydate) = EXTRACT(YEAR FROM CURRENT_DATE) ` if tenantID != 0 { q2 += " AND d.tenantid = ?" } // Attach month-wise insights for each location for i := range locations { var orderMonths models.Ordermonths var err error if tenantID != 0 { err = r.db.Raw(q2, locations[i].Locationid, tenantID).Scan(&orderMonths).Error } else { err = r.db.Raw(q2, locations[i].Locationid).Scan(&orderMonths).Error } if err != nil { return nil, err } locations[i].Ordermonths = &orderMonths } return locations, nil } func (r *deliveriesRepository) GetLocationDeliverySummary(tenantID int) ([]models.Ordersummarylocation, error) { var data []models.Ordersummarylocation var params []interface{} q1 := ` SELECT b.locationid, b.locationname, COALESCE(COUNT(a.orderid), 0) AS total, COALESCE(SUM(CASE WHEN a.orderstatus = 'created' THEN 1 ELSE 0 END), 0) AS created, COALESCE(SUM(CASE WHEN a.orderstatus = 'pending' THEN 1 ELSE 0 END), 0) AS pending, COALESCE(SUM(CASE WHEN a.orderstatus = 'processing' THEN 1 ELSE 0 END), 0) AS processing, COALESCE(SUM(CASE WHEN a.orderstatus = 'delivered' THEN 1 ELSE 0 END), 0) AS delivered, COALESCE(SUM(CASE WHEN a.orderstatus = 'cancelled' THEN 1 ELSE 0 END), 0) AS cancelled FROM tenantlocations b LEFT JOIN deliveries a ON a.locationid = b.locationid AND a.configid = 1 ` // ✅ Only filter tenant locations in WHERE if tenantID != 0 { q1 += " WHERE b.tenantid = ?" params = append(params, tenantID) } q1 += " GROUP BY b.locationid, b.locationname ORDER BY b.locationid" if err := r.db.Raw(q1, params...).Scan(&data).Error; err != nil { return nil, err } return data, nil } func (r *deliveriesRepository) GetReportSummary(tid, pid, uid, aid int, fdate, tdate string) ([]models.ReportSummary, error) { var data []models.ReportSummary var q1 string switch { case tid != 0: if fdate != "" && tdate != "" { q1 = reports + ` WHERE a.tenantid=` + strconv.Itoa(tid) + ` AND a.orderdate::date BETWEEN '` + fdate + `' AND '` + tdate + `' GROUP BY b.tenantid, b.tenantname` } else { q1 = reports + ` WHERE a.tenantid=` + strconv.Itoa(tid) + ` GROUP BY b.tenantid, b.tenantname` } case pid != 0: if fdate != "" && tdate != "" { q1 = reports + ` WHERE a.partnerid=` + strconv.Itoa(pid) + ` AND a.orderdate::date BETWEEN '` + fdate + `' AND '` + tdate + `' GROUP BY b.tenantid, b.tenantname` } else { q1 = reports + ` WHERE a.partnerid=` + strconv.Itoa(pid) + ` GROUP BY b.tenantid, b.tenantname` } case uid != 0: if fdate != "" && tdate != "" { q1 = reports + ` WHERE c.userid=` + strconv.Itoa(uid) + ` AND a.orderdate::date BETWEEN '` + fdate + `' AND '` + tdate + `' GROUP BY b.tenantid, b.tenantname` } else { q1 = reports + ` WHERE c.userid=` + strconv.Itoa(uid) + ` GROUP BY b.tenantid, b.tenantname` } case aid != 0: if fdate != "" && tdate != "" { q1 = reports + ` WHERE c.applocationid=` + strconv.Itoa(aid) + ` AND a.orderdate::date BETWEEN '` + fdate + `' AND '` + tdate + `' GROUP BY b.tenantid, b.tenantname` } else { q1 = reports + ` WHERE c.applocationid=` + strconv.Itoa(aid) + ` GROUP BY b.tenantid, b.tenantname` } default: if fdate != "" && tdate != "" { q1 = reports + ` WHERE a.orderdate::date BETWEEN '` + fdate + `' AND '` + tdate + `' GROUP BY b.tenantid, b.tenantname` } else { q1 = reports + ` GROUP BY b.tenantid, b.tenantname` } } if err := r.db.Raw(q1).Scan(&data).Error; err != nil { return nil, err } return data, nil } func (r *deliveriesRepository) GetRiderSummary(aid, pid, tid int, fdate, tdate string) ([]models.Ridersummary, error) { var data []models.Ridersummary var q1 string switch { case aid != 0: if fdate != "" && tdate != "" { q1 = Ridersummary + ` WHERE a.configid=6 AND a.applocationid=` + strconv.Itoa(aid) + ` AND b.deliverydate::date BETWEEN '` + fdate + `' AND '` + tdate + `' GROUP BY a.userid, a.firstname, a.lastname` } else { q1 = Ridersummary + ` WHERE a.configid=6 AND a.applocationid=` + strconv.Itoa(aid) + ` GROUP BY a.userid, a.firstname, a.lastname` } case pid != 0: if fdate != "" && tdate != "" { q1 = Ridersummary + ` WHERE a.configid=6 AND a.partnerid=` + strconv.Itoa(pid) + ` AND b.deliverydate::date BETWEEN '` + fdate + `' AND '` + tdate + `' GROUP BY a.userid, a.firstname, a.lastname` } else { q1 = Ridersummary + ` WHERE a.configid=6 AND a.partnerid=` + strconv.Itoa(pid) + ` GROUP BY a.userid, a.firstname, a.lastname` } case tid != 0: if fdate != "" && tdate != "" { q1 = Ridersummary + ` WHERE a.configid=6 AND b.tenantid=` + strconv.Itoa(tid) + ` AND b.deliverydate::date BETWEEN '` + fdate + `' AND '` + tdate + `' GROUP BY a.userid, a.firstname, a.lastname` } else { q1 = Ridersummary + ` WHERE a.configid=6 AND b.tenantid=` + strconv.Itoa(tid) + ` GROUP BY a.userid, a.firstname, a.lastname` } default: if fdate != "" && tdate != "" { q1 = Ridersummary + ` WHERE a.configid=6 AND b.deliverydate::date BETWEEN '` + fdate + `' AND '` + tdate + `' GROUP BY a.userid, a.firstname, a.lastname` } else { q1 = Ridersummary + ` WHERE a.configid=6 GROUP BY a.userid, a.firstname, a.lastname` } } if err := r.db.Raw(q1).Scan(&data).Error; err != nil { return nil, err } return data, nil } func (r *deliveriesRepository) GetTenantDeliveries(input models.DeliveryQuery) []models.Deliveryinfo { fmt.Println("🔍 Fetching Tenant Deliveries") var data []models.Deliveryinfo var queryBuilder strings.Builder var params []interface{} offset := (input.Pageno - 1) * input.Pagesize // LEFT JOIN on the rider, for the same reason as the branch-scoped read // below: the rider's row supplies a name and a phone number, and a delivery // whose rider is later deactivated should lose the name, not the job. baseQuery := ` SELECT a.*, b.tenantname, c.firstname AS ridername, c.contactno AS ridercontact FROM deliveries a JOIN tenants b ON a.tenantid = b.tenantid LEFT JOIN app_users c ON a.userid = c.userid WHERE a.tenantid = ? ` queryBuilder.WriteString(baseQuery) params = append(params, input.Tenantid) if input.Status != "" { queryBuilder.WriteString(" AND a.orderstatus = ?") params = append(params, input.Status) } if input.Fromdate != "" && input.ToDate != "" { queryBuilder.WriteString(" AND a.deliverydate::date BETWEEN ? AND ?") params = append(params, input.Fromdate, input.ToDate) } if input.Keyword != "" { k := "%" + input.Keyword + "%" queryBuilder.WriteString(` AND ( a.pickupcustomer LIKE ? OR a.deliverycustomer LIKE ? OR a.pickupcontactno LIKE ? OR a.deliverycontactno LIKE ? OR a.orderid LIKE ? ) `) for i := 0; i < 5; i++ { params = append(params, k) } } queryBuilder.WriteString(" ORDER BY a.deliveryid DESC") if input.Pagesize != 0 { queryBuilder.WriteString(" LIMIT ? OFFSET ?") params = append(params, input.Pagesize, offset) } query := queryBuilder.String() fmt.Println("Final query:", query) fmt.Println("Params:", params) r.db.Raw(query, params...).Scan(&data) return data } func (r *deliveriesRepository) GetPartnerDeliveries(input models.DeliveryQuery) []models.Deliveryinfo { fmt.Println("🔍 Fetching Partner Deliveries") var data []models.Deliveryinfo var queryBuilder strings.Builder var params []interface{} offset := (input.Pageno - 1) * input.Pagesize queryBuilder.WriteString(deliveries + ` WHERE a.partnerid = ?`) params = append(params, input.Partnerid) if input.Status != "" { queryBuilder.WriteString(" AND a.orderstatus = ?") params = append(params, input.Status) } if input.Fromdate != "" && input.ToDate != "" { queryBuilder.WriteString(" AND a.deliverydate::date BETWEEN ? AND ?") params = append(params, input.Fromdate, input.ToDate) } queryBuilder.WriteString(" ORDER BY a.deliveryid DESC") if input.Pagesize > 0 { queryBuilder.WriteString(" LIMIT ? OFFSET ?") params = append(params, input.Pagesize, offset) } finalQuery := queryBuilder.String() log.Println("Final Partner Query:", finalQuery, "Params:", params) r.db.Raw(finalQuery, params...).Scan(&data) return data } func (r *deliveriesRepository) GetCustomerDeliveries(input models.DeliveryQuery) []models.Deliveryinfo { fmt.Println("🔍 Fetching Customer Deliveries") var data []models.Deliveryinfo var queryBuilder strings.Builder var params []interface{} offset := (input.Pageno - 1) * input.Pagesize queryBuilder.WriteString(deliveries + ` WHERE a.customerid = ?`) params = append(params, input.Customerid) if input.Partnerid != 0 { queryBuilder.WriteString(" AND a.partnerid = ?") params = append(params, input.Partnerid) } if input.Status != "" { queryBuilder.WriteString(" AND a.orderstatus = ?") params = append(params, input.Status) } if input.Fromdate != "" && input.ToDate != "" { queryBuilder.WriteString(" AND a.deliverydate::date BETWEEN ? AND ?") params = append(params, input.Fromdate, input.ToDate) } if input.Keyword != "" { k := "%" + input.Keyword + "%" queryBuilder.WriteString(` AND ( a.pickupcustomer LIKE ? OR a.deliverycustomer LIKE ? OR a.pickupcontactno LIKE ? OR a.deliverycontactno LIKE ? OR a.orderid LIKE ? )`) for i := 0; i < 5; i++ { params = append(params, k) } } queryBuilder.WriteString(" ORDER BY a.deliveryid DESC") if input.Pagesize != 0 { queryBuilder.WriteString(" LIMIT ? OFFSET ?") params = append(params, input.Pagesize, offset) } query := queryBuilder.String() fmt.Println("Final query:", query) fmt.Println("Params:", params) r.db.Raw(query, params...).Scan(&data) return data } func (r *deliveriesRepository) GetAdminDeliveries(input models.DeliveryQuery) []models.Deliveryinfo { fmt.Println("Admin Deliveries") var data []models.Deliveryinfo var queryBuilder strings.Builder var params []interface{} offset := (input.Pageno - 1) * input.Pagesize queryBuilder.WriteString(` SELECT a.*, b.tenantname, c.firstname as ridername FROM deliveries a JOIN tenants b ON a.tenantid = b.tenantid JOIN app_users c ON c.userid = a.userid WHERE a.applocationid = ? `) params = append(params, input.Applocationid) if input.Status != "" { queryBuilder.WriteString(" AND a.orderstatus = ?") params = append(params, input.Status) } if input.Fromdate != "" && input.ToDate != "" { queryBuilder.WriteString(" AND a.deliverydate::date BETWEEN ? AND ?") params = append(params, input.Fromdate, input.ToDate) } if input.Keyword != "" { k := "%" + input.Keyword + "%" queryBuilder.WriteString(` AND ( a.pickupcustomer LIKE ? OR b.tenantname LIKE ? OR a.deliverycustomer LIKE ? OR a.pickupcontactno LIKE ? OR a.deliverycontactno LIKE ? OR a.orderid LIKE ? ) `) for i := 0; i < 6; i++ { params = append(params, k) } } queryBuilder.WriteString(" ORDER BY a.deliveryid DESC") if input.Pagesize > 0 { queryBuilder.WriteString(" LIMIT ? OFFSET ?") params = append(params, input.Pagesize, offset) } finalQuery := queryBuilder.String() log.Println("AdminDeliveries query:", finalQuery) log.Println("Params:", params) r.db.Raw(finalQuery, params...).Scan(&data) return data } func (r *deliveriesRepository) GetUserDeliveries(input models.DeliveryQuery) []models.Deliveryinfo { var q1 string offset := (input.Pageno - 1) * input.Pagesize var data []models.Deliveryinfo if input.Status != "" { if input.Pagesize != 0 { fmt.Println("Rider Deliveriesv2") q1 = deliveries + ` where a.userid=` + strconv.Itoa(input.UserID) + ` and a.orderstatus='` + input.Status + `' and a.deliverydate::date between '` + input.Fromdate + `' and '` + input.ToDate + `' order by deliveryid desc LIMIT ` + strconv.Itoa(input.Pagesize) + ` OFFSET ` + strconv.Itoa(offset) } else { fmt.Println("Rider Deliveriesv1") q1 = deliveries + ` where a.userid=` + strconv.Itoa(input.UserID) + ` and a.orderstatus='` + input.Status + `' and a.deliverydate::date between '` + input.Fromdate + `' and '` + input.ToDate + `' order by deliveryid asc` } } else { if input.Pagesize != 0 { fmt.Println("Rider Deliveriesv2") q1 = deliveries + ` where a.orderstatus in ('accepted','arrived','picked') and a.userid=` + strconv.Itoa(input.UserID) + ` and a.deliverydate::date between '` + input.Fromdate + `' and '` + input.ToDate + `' order by deliveryid desc LIMIT ` + strconv.Itoa(input.Pagesize) + ` OFFSET ` + strconv.Itoa(offset) } else { fmt.Println("Rider Deliveriesv1") q1 = deliveries + ` where a.orderstatus in ('accepted','arrived','picked') and a.userid=` + strconv.Itoa(input.UserID) + ` and a.deliverydate::date between '` + input.Fromdate + `' and '` + input.ToDate + `' order by deliveryid desc` } } r.db.Raw(q1).Find(&data) return data } func (r *deliveriesRepository) GetAppUserDeliveries(input models.DeliveryQuery) []models.Deliveryinfo { var data []models.Deliveryinfo fmt.Println("Appsuser Deliveries -new") var q1 string offset := (input.Pageno - 1) * input.Pagesize q1 = deliveries + ` WHERE g.status = 'Active' AND g.userid=` + strconv.Itoa(input.Appuserid) if input.Status != "" { q1 += ` AND a.orderstatus='` + input.Status + `'` } if input.Fromdate != "" && input.ToDate != "" { q1 += ` AND a.deliverydate::date BETWEEN '` + input.Fromdate + `' AND '` + input.ToDate + `'` } if input.Keyword != "" { k := "%" + input.Keyword + "%" q1 += ` AND ( a.pickupcustomer LIKE '` + k + `' OR b.tenantname LIKE '` + k + `' OR a.deliverycustomer LIKE '` + k + `' OR a.pickupcontactno LIKE '` + k + `' OR a.deliverycontactno LIKE '` + k + `' OR CAST(a.orderid AS TEXT) LIKE '` + k + `' )` } q1 += ` ORDER BY a.deliveryid DESC` if input.Pagesize != 0 { q1 += ` LIMIT ` + strconv.Itoa(input.Pagesize) + ` OFFSET ` + strconv.Itoa(offset) } log.Println("Final Query:", q1) r.db.Raw(q1).Find(&data) return data } func (r *deliveriesRepository) GetDeliveries(input models.DeliveryQuery) []models.Deliveryinfo { var data []models.Deliveryinfo var queryBuilder strings.Builder var params []interface{} offset := (input.Pageno - 1) * input.Pagesize baseQuery := ` SELECT a.*, b.tenantname, c.firstname as ridername, c.contactno as ridercontact, d.customertoken FROM deliveries a JOIN tenants b ON a.tenantid = b.tenantid Join app_users c ON a.userid = c.userid JOIN customer d ON a.deliverycustomerid = d.customerid WHERE 1=1 ` queryBuilder.WriteString(baseQuery) if input.Fromdate != "" && input.ToDate != "" { queryBuilder.WriteString(" AND a.deliverydate::date BETWEEN ? AND ?") params = append(params, input.Fromdate, input.ToDate) } if input.Status != "" { queryBuilder.WriteString(" AND a.orderstatus = ?") params = append(params, input.Status) } if input.Keyword != "" { k := "%" + input.Keyword + "%" queryBuilder.WriteString(` AND ( a.pickupcustomer LIKE ? OR b.tenantname LIKE ? OR a.deliverycustomer LIKE ? OR a.pickupcontactno LIKE ? OR a.deliverycontactno LIKE ? OR a.orderid LIKE ? ) `) for i := 0; i < 6; i++ { params = append(params, k) } } queryBuilder.WriteString(" ORDER BY a.deliveryid DESC") if input.Pagesize != 0 { queryBuilder.WriteString(" LIMIT ? OFFSET ?") params = append(params, input.Pagesize, offset) } query := queryBuilder.String() log.Println("Final query:", query) log.Println("With params:", params) r.db.Raw(query, params...).Scan(&data) return data } func (r *deliveriesRepository) GetDeliveryQueues(uid int, fdate, tdate string) ([]models.Deliveryinfo, error) { var data []models.Deliveryinfo var q1 string if fdate != "" && tdate != "" { q1 = deliveries + ` WHERE a.orderstatus='pending' AND a.userid=? AND DATE(a.deliverydate) BETWEEN ? AND ? ORDER BY a.deliveryid ASC` if err := r.db.Raw(q1, uid, fdate, tdate).Find(&data).Error; err != nil { return nil, err } } else { // CURRENT_DATE, not CURDATE(). The latter is MySQL and this is // Postgres, so this branch answered // `500 function curdate() does not exist` every time it was taken — // which is every call a rider's app makes without a date range, i.e. // "show me today's jobs". Verified locally 2026-09-03. q1 = deliveries + ` WHERE a.orderstatus='pending' AND a.userid=? AND DATE(a.deliverydate)=CURRENT_DATE ORDER BY a.deliveryid ASC` if err := r.db.Raw(q1, uid).Find(&data).Error; err != nil { return nil, err } } return data, nil } func (r *deliveriesRepository) GetTenantLocationDeliveries(input models.DeliveryQuery) []models.Deliveryinfo { var data []models.Deliveryinfo var params []interface{} var qb strings.Builder offset := (input.Pageno - 1) * input.Pagesize // LEFT JOIN on the rider and the customer; INNER only on the tenant. // // All three were inner joins and two of them were filtering the result to // nothing. `deliverycustomerid` is 0 on every delivery this system has // written — neither the app nor the old console sets it — so joining // `customers` on it matched no row, and the branch-scoped read returned an // empty list for every branch of every tenant. // // Measured on production 2026-09-03: tenant 1135 has 7 deliveries, 6 of // them at location 1166, and ?tenantid=1135&locationid=1166 returned 0. // That is the entire deliveries page for a store user, who is pinned to one // branch and so always sends a locationid. // // The rider join is the same shape and would hide a delivery whose rider // was later deactivated — the accidental-filter pattern already fixed in // GetStaffs, GetUserById, GetTenantByID and the order listings. // // `customertoken` is the only thing the customer join contributes: a push // token, worth having and not worth losing the row over. qb.WriteString(` SELECT a.*, b.tenantname, c.firstname AS ridername, c.contactno AS ridercontact, d.customertoken FROM deliveries a JOIN tenants b ON a.tenantid = b.tenantid LEFT JOIN app_users c ON a.userid = c.userid LEFT JOIN customers d ON a.deliverycustomerid = d.customerid WHERE a.tenantid = ? AND a.locationid = ? `) params = append(params, input.Tenantid, input.Locationid) if input.Fromdate != "" && input.ToDate != "" { qb.WriteString(" AND DATE(a.deliverydate) BETWEEN ? AND ?") params = append(params, input.Fromdate, input.ToDate) } if input.Status != "" { qb.WriteString(" AND a.orderstatus = ?") params = append(params, input.Status) } if input.Keyword != "" { like := "%" + input.Keyword + "%" // A stray `m` sat after the third OR — a typed character that reached // production. Postgres rejects the whole statement, so searching within // a branch's deliveries failed outright rather than returning nothing, // which is why it was invisible behind the empty-list bug above. qb.WriteString(` AND ( a.pickupcustomer LIKE ? OR b.tenantname LIKE ? OR a.deliverycustomer LIKE ? OR a.pickupcontactno LIKE ? OR a.deliverycontactno LIKE ? OR a.orderid LIKE ? ) `) for i := 0; i < 6; i++ { params = append(params, like) } } qb.WriteString(" ORDER BY a.deliveryid DESC") if input.Pagesize != 0 { qb.WriteString(" LIMIT ? OFFSET ?") params = append(params, input.Pagesize, offset) } query := qb.String() log.Println("Tenant+Location Query:", query) log.Println("Params:", params) r.db.Raw(query, params...).Scan(&data) return data }