Files
backend_jupiter/domain/delivery.go
2026-05-25 11:45:56 +05:30

1296 lines
35 KiB
Go
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package domain
import (
"context"
"encoding/json"
"errors"
"nearle/db"
"nearle/models"
"nearle/utils"
"strconv"
"strings"
"time"
"github.com/jinzhu/copier"
"github.com/redis/go-redis/v9"
)
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.acceptedtime,
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.quantity,a.collectionamt,a.collectedamt,a.collectionstatus,
a.notes,a.ordernotes,a.step,a.eta,a.previouskms,a.cumulativekms,a.ridertime,b.tenantname,b.primarycontact as tenantcontactno,b.tenanttoken,b.suburb as tenantsuburb,b.city as tenantcity,
b.address AS tenantaddress, CONCAT(c.firstname, ' ', c.lastname) AS ridername,c.userfcmtoken,c.contactno as ridercontact,e.locationname,e.suburb AS locationsuburb,e.contactno AS locationcontactno,e.address AS locationaddress,
h.slab, h.pricingdate, h.baseprice, h.minkm, h.priceperkm, h.maxkm, h.orders, h.othercharges, h.surgecharges,
a.expecteddeliverytime, a.profit, a.transitminutes, a.calculationdistancekm
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
LEFT JOIN tenantpricing h ON a.tenantid = h.tenantid`
)
func PublishLog(input []models.Deliverylogs) error {
logInfo("PublishLog", "records", len(input))
tx := db.DB.Begin()
for i := range input {
t1 := tx.Create(&input[i])
if t1.Error != nil {
logError("PublishLog failed", "error", tx.Error)
tx.Rollback()
return t1.Error
}
}
err := tx.Commit().Error
if err != nil {
return err
}
return nil
}
func CreateDeliveries(data []models.Deliveries) error {
logInfo("CreateDeliveries", "records", len(data))
var que []models.Deliveryqueues
var ord models.Updateorderstatus
tx := db.DB.Begin()
for i := range data {
t1 := tx.Create(&data[i])
if t1.Error != nil {
logError("CreateDeliveries failed", "error", tx.Error)
tx.Rollback()
return t1.Error
}
if err := copier.Copy(&que, &data[i]); err != nil {
return err
}
t2 := tx.Create(&que)
if t2.Error != nil {
logError("CreateDeliveries queue failed", "error", tx.Error)
tx.Rollback()
return t2.Error
}
ord.Orderstatus = data[i].Orderstatus
ord.Pending = data[i].Deliverydate
t3 := tx.Table("orders").Where("orderheaderid=?", data[i].Orderheaderid).Updates(&ord)
if t3.Error != nil {
logError("CreateDeliveries order update failed", "error", tx.Error)
tx.Rollback()
return t3.Error
}
}
err := tx.Commit().Error
if err != nil {
return err
}
return nil
}
func CreateDeliveriesV2(data []models.Deliveries) ([]int64, error) {
ctx := db.Ctx
var deliveryIDs []int64
logInfo("🔥 CreateDeliveriesV2 DOMAIN CALLED")
for _, d := range data {
// 🔒 Rider guard key
riderKey := "rider:" + strconv.Itoa(d.Userid) + ":active_delivery"
// ⏱ delivery TTL (example: 2 hours)
ttl := 2 * time.Hour
// 🔒 PREVENT DOUBLE ASSIGN (CREATE ONLY IF NOT EXISTS)
ok, err := db.Rdb.SetNX(ctx, riderKey, "LOCK", ttl).Result()
if err != nil {
return nil, err
}
if !ok {
return nil, errors.New("rider " + strconv.Itoa(d.Userid) + " already has active delivery")
}
// 🔥 Generate delivery ID
deliveryID, err := db.Rdb.Incr(ctx, "counter:deliveryid").Result()
if err != nil {
db.Rdb.Del(ctx, riderKey)
return nil, err
}
deliveryIDs = append(deliveryIDs, deliveryID)
// 🔑 Redis keys
deliveryKey := "delivery:active:" + strconv.FormatInt(deliveryID, 10)
cityKey := "city:" + strconv.Itoa(d.Tenantid) + ":active_deliveries"
pipe := db.Rdb.TxPipeline()
// 🔥 ACTIVE DELIVERY HASH (AUTO-CREATES)
pipe.HSet(ctx, deliveryKey, map[string]interface{}{
"delivery_id": deliveryID,
"orderheaderid": d.Orderheaderid,
"orderid": d.Orderid,
"tenantid": d.Tenantid, // city
"partnerid": d.Partnerid,
"rider_id": d.Userid,
"status": "ASSIGNED",
"pickupaddress": d.Pickupaddress,
"deliveryaddress": d.Deliveryaddress,
"pickuplat": d.Pickuplat,
"pickuplon": d.Pickuplon,
"deliverylat": d.Deliverylat,
"deliverylong": d.Deliverylong,
"eta": d.Eta,
"expecteddeliverytime": d.Expecteddeliverytime,
"profit": d.Profit,
"transitminutes": d.Transitminutes,
"calculationdistancekm": d.Calculationdistancekm,
"created_at": time.Now().Unix(),
"last_updated": time.Now().Unix(),
})
// 🔥 CITY → ACTIVE DELIVERIES INDEX (AUTO-CREATES)
pipe.SAdd(ctx, cityKey, deliveryID)
// 🔥 SET RIDER VALUE (REPLACE LOCK)
pipe.Set(ctx, riderKey, deliveryID, ttl)
// 🔥 TTL ONLY ON DELIVERY & RIDER (NEVER CITY)
pipe.Expire(ctx, deliveryKey, ttl)
cmds, err := pipe.Exec(ctx)
if err != nil {
db.Rdb.Del(ctx, riderKey)
return nil, err
}
// 🔍 Per-command safety check
for _, cmd := range cmds {
if cmd.Err() != nil {
db.Rdb.Del(ctx, riderKey)
return nil, cmd.Err()
}
}
logInfo("✅ Delivery created", "deliveryID", deliveryID)
}
logInfo("✅ CreateDeliveriesV2 COMPLETED")
return deliveryIDs, nil
}
func CreateDelivery(data models.Deliveries) error {
logInfo("CreateDelivery", "orderheaderid", data.Orderheaderid)
var que models.Deliveryqueues
var ord models.Updateorderstatus
tx := db.DB.Begin()
t1 := tx.Table("deliveries").Create(&data)
if t1.Error != nil {
tx.Rollback()
return t1.Error
}
if err := copier.Copy(&que, &data); err != nil {
return err
}
t2 := tx.Table("deliveryqueues").Create(&que)
if t2.Error != nil {
logError("CreateDelivery queue failed", "error", tx.Error)
tx.Rollback()
return t2.Error
}
ord.Orderstatus = data.Orderstatus
ord.Pending = data.Assigntime
t3 := tx.Table("orders").Where("orderheaderid=?", data.Orderheaderid).Updates(&ord)
if t3.Error != nil {
logError("CreateDelivery order update failed", "error", tx.Error)
tx.Rollback()
return t3.Error
}
err := tx.Commit().Error
if err != nil {
return err
}
return nil
}
func UpdateDelivery(data models.UpdateDeliveryStatus) error {
logInfo("UpdateDelivery", "deliveryid", data.Deliveryid, "status", data.Orderstatus)
var ord models.Updateorderstatus
var cloc models.Customerlocations
tx := db.DB.Begin()
t1 := tx.Table("deliveries").Where("deliveryid=?", data.Deliveryid).Updates(&data)
if t1.Error != nil {
logError("UpdateDelivery failed", "error", tx.Error)
tx.Rollback()
}
if data.Orderstatus == "pending" {
ord.Orderstatus = data.Orderstatus
ord.Pending = data.Assigntime
t2 := tx.Table("orders").Where("orderheaderid=?", data.Orderheaderid).Updates(&ord)
if t2.Error != nil {
logError("UpdateDelivery pending update failed", "error", tx.Error)
tx.Rollback()
}
}
if data.Orderstatus == "picked" {
if data.Pickuplocationid != 0 && data.Deliverytype != "B" {
cloc.Latitude = data.Riderslat
cloc.Longitude = data.Riderslon
cloc.Address = data.Address
cloc.Suburb = data.Suburb
cloc.City = data.City
cloc.State = data.State
cloc.Postcode = data.Postcode
t5 := tx.Table("customerlocations").Where("locationid=?", data.Pickuplocationid).Updates(&cloc)
if t5.Error != nil {
logError("UpdateDelivery customer location update failed", "error", tx.Error)
tx.Rollback()
}
}
}
if data.Orderstatus == "delivered" {
ord.Orderstatus = data.Orderstatus
ord.Delivered = data.Deliverytime
t2 := tx.Table("orders").Where("orderheaderid=?", data.Orderheaderid).Updates(&ord)
if t2.Error != nil {
logError("UpdateDelivery delivered update failed", "error", tx.Error)
tx.Rollback()
}
if data.Deliverylocationid != 0 && data.Deliverytype != "B" {
cloc.Latitude = data.Deliverylat
cloc.Longitude = data.Deliverylong
cloc.Address = data.Address
cloc.Suburb = data.Suburb
cloc.City = data.City
cloc.State = data.State
cloc.Postcode = data.Postcode
t5 := tx.Table("customerlocations").Where("locationid=?", data.Deliverylocationid).Updates(&cloc)
if t5.Error != nil {
logError("UpdateDelivery delivery location update failed", "error", tx.Error)
tx.Rollback()
}
}
}
if data.Orderstatus == "cancelled" {
ord.Orderstatus = data.Orderstatus
ord.Cancelled = data.Canceltime
t2 := tx.Table("orders").Where("orderheaderid=?", data.Orderheaderid).Updates(&ord)
if t2.Error != nil {
logError("UpdateDelivery cancelled update failed", "error", tx.Error)
tx.Rollback()
}
}
err := tx.Commit().Error
if err != nil {
return err
}
return err
}
func GetDeliveryQueues(uid int, fdate, tdate string) []models.Deliveryinfo {
logInfo("GetDeliveryQueues", "userid", uid)
var data []models.Deliveryinfo
var q1 string
if fdate != "" && tdate != "" {
q1 = deliveries + " where a.orderstatus='pending' and a.userid= ? and a.deliverydate::date between ? and ? order by a.deliveryid asc"
db.DB.Raw(q1, uid, fdate, tdate).Find(&data)
} else {
q1 = deliveries + " where a.orderstatus='pending' and a.userid= ? and a.deliverydate::date = CURRENT_DATE order by a.deliveryid asc"
db.DB.Raw(q1, uid).Find(&data)
}
logInfo("GetDeliveryQueues Query executed", "userid", uid)
return data
}
func GetDeliveryQueuesV1(uid int, fdate, tdate string) []models.Deliveryinfo {
logInfo("GetDeliveryQueuesV1", "userid", uid)
var data []models.Deliveryinfo
var q1 string
statusFilter := `a.orderstatus IN ('pending', 'accepted', 'arrived')`
if fdate != "" && tdate != "" {
q1 = deliveries + " WHERE " + statusFilter + " AND a.userid = ? AND a.deliverydate::date BETWEEN ? AND ? ORDER BY a.deliveryid ASC"
db.DB.Raw(q1, uid, fdate, tdate).Find(&data)
} else {
q1 = deliveries + " WHERE " + statusFilter + " AND a.userid = ? AND a.deliverydate::date = CURRENT_DATE ORDER BY a.deliveryid ASC"
db.DB.Raw(q1, uid).Find(&data)
}
return data
}
func GetDeliveryQueuesPicked(uid int, fdate, tdate string) []models.Deliveryinfo {
logInfo("GetDeliveryQueuesPicked", "userid", uid)
var data []models.Deliveryinfo
var q1 string
if fdate != "" && tdate != "" {
q1 = deliveries + " where a.orderstatus='picked' and a.userid=? and a.deliverydate::date between ? and ? order by a.deliveryid asc"
db.DB.Raw(q1, uid, fdate, tdate).Find(&data)
} else {
q1 = deliveries + " where a.orderstatus='picked' and a.userid=? and a.deliverydate::date = CURRENT_DATE order by a.deliveryid asc"
db.DB.Raw(q1, uid).Find(&data)
}
return data
}
func GetDeliverylogs(did int) []models.Deliverylogs {
logInfo("GetDeliverylogs", "deliveryid", did)
var data []models.Deliverylogs
q1 := "select * from deliverylogs where deliveryid=? order by logid asc"
db.DB.Raw(q1, did).Find(&data)
return data
}
func GetTenantDeliveries(input models.DeliveryQuery) []models.Deliveryinfo {
logInfo("🔍 Fetching Tenant Deliveries", "tenantid", input.Tenantid)
var data []models.Deliveryinfo
var queryBuilder strings.Builder
var params []interface{}
offset := (input.Pageno - 1) * input.Pagesize
baseQuery := `
SELECT a.*, b.tenantname,
CONCAT(c.firstname, ' ', c.lastname) AS ridername,
c.contactno AS ridercontact, c.userfcmtoken,
d.locationname, d.contactno AS locationcontactno,
d.suburb AS locationsuburb, d.address AS locationaddress,
e.slab, e.pricingdate, e.baseprice, e.minkm,
e.priceperkm, e.maxkm, e.orders,
e.othercharges, e.surgecharges
FROM deliveries a
JOIN tenants b ON a.tenantid = b.tenantid
JOIN app_users c ON a.userid = c.userid
JOIN tenantlocations d ON a.locationid = d.locationid
JOIN tenantpricing e ON a.tenantid = e.tenantid
WHERE b.moduleid = 6
AND a.tenantid = ?
`
queryBuilder.WriteString(baseQuery)
params = append(params, input.Tenantid)
// ✅ USERID FILTER (FIX)
if input.UserID > 0 {
queryBuilder.WriteString(" AND a.userid = ?")
params = append(params, input.UserID)
}
// ✅ STATUS FILTER (ignore "all")
if input.Status != "" && strings.ToLower(input.Status) != "all" {
queryBuilder.WriteString(" AND LOWER(a.orderstatus) = LOWER(?)")
params = append(params, input.Status)
}
// ✅ DATE FILTER
if input.Fromdate != "" && input.ToDate != "" {
queryBuilder.WriteString(" AND a.deliverydate::date BETWEEN ? AND ?")
params = append(params, input.Fromdate, input.ToDate)
}
// ✅ KEYWORD FILTER
if input.Keyword != "" {
k := "%" + strings.ToLower(input.Keyword) + "%"
queryBuilder.WriteString(`
AND (
LOWER(a.pickupcustomer) LIKE ? OR
LOWER(a.deliverycustomer) LIKE ? OR
LOWER(a.pickupcontactno) LIKE ? OR
LOWER(a.deliverycontactno) LIKE ? OR
LOWER(CAST(a.orderid AS CHAR)) LIKE ? OR
LOWER(c.firstname) LIKE ? OR
LOWER(c.lastname) LIKE ? OR
LOWER(c.contactno) LIKE ?
)
`)
for i := 0; i < 8; 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()
// print("Final Query:", query)
db.DB.Raw(query, params...).Scan(&data)
return data
}
func GetPartnerDeliveries(input models.DeliveryQuery) []models.Deliveryinfo {
logInfo("Partner Deliveries", "partnerid", input.Partnerid)
var data []models.Deliveryinfo
var qb strings.Builder
var params []interface{}
offset := (input.Pageno - 1) * input.Pagesize
// 🔹 Base query
qb.WriteString(deliveries)
qb.WriteString(` WHERE b.moduleid = 6 AND a.partnerid = ?`)
params = append(params, input.Partnerid)
// 🔹 STATUS FILTER (ignore "all")
if input.Status != "" && strings.ToLower(input.Status) != "all" {
qb.WriteString(` AND LOWER(a.orderstatus) = LOWER(?)`)
params = append(params, input.Status)
}
// 🔹 DATE FILTER
if input.Fromdate != "" && input.ToDate != "" {
qb.WriteString(` AND a.deliverydate::date BETWEEN ? AND ?`)
params = append(params, input.Fromdate, input.ToDate)
}
// 🔹 ORDER
qb.WriteString(` ORDER BY a.deliveryid DESC`)
// 🔹 PAGINATION
if input.Pagesize > 0 {
qb.WriteString(` LIMIT ? OFFSET ?`)
params = append(params, input.Pagesize, offset)
}
finalQuery := qb.String()
logInfo("Final Query", "query", finalQuery)
logInfo("Params", "params", params)
db.DB.Raw(finalQuery, params...).Scan(&data)
return data
}
func GetCustomerDeliveries(input models.DeliveryQuery) []models.Deliveryinfo {
logInfo("Customer Deliveries", "partnerid", input.Partnerid)
var q1 string
offset := (input.Pageno - 1) * input.Pagesize
var data []models.Deliveryinfo
// ✅ Apply status filter ONLY if status != "" AND status != "all"
if input.Status != "" && strings.ToLower(input.Status) != "all" {
if input.Fromdate != "" && input.ToDate != "" {
if input.Pagesize != 0 {
q1 = deliveries + " where b.moduleid=6 and a.partnerid=? and a.orderstatus=? and a.deliverydate::date between ? and ? order by deliveryid desc LIMIT ? OFFSET ?"
db.DB.Raw(q1, input.Partnerid, input.Status, input.Fromdate, input.ToDate, input.Pagesize, offset).Find(&data)
} else {
q1 = deliveries + " where b.moduleid=6 and a.partnerid=? and a.orderstatus=? and a.deliverydate::date between ? and ? order by deliveryid desc"
db.DB.Raw(q1, input.Partnerid, input.Status, input.Fromdate, input.ToDate).Find(&data)
}
} else {
if input.Pagesize != 0 {
q1 = deliveries + " where b.moduleid=6 and a.partnerid=? and a.orderstatus=? order by deliveryid desc LIMIT ? OFFSET ?"
db.DB.Raw(q1, input.Partnerid, input.Status, input.Pagesize, offset).Find(&data)
} else {
q1 = deliveries + " where b.moduleid=6 and a.partnerid=? and a.orderstatus=? order by deliveryid desc"
db.DB.Raw(q1, input.Partnerid, input.Status).Find(&data)
}
}
} else {
// ✅ status = "" OR status = "all" → NO STATUS FILTER
if input.Pagesize != 0 {
q1 = deliveries + " where b.moduleid=6 and a.partnerid=? and a.deliverydate::date between ? and ? order by deliveryid desc LIMIT ? OFFSET ?"
db.DB.Raw(q1, input.Partnerid, input.Fromdate, input.ToDate, input.Pagesize, offset).Find(&data)
} else {
q1 = deliveries + " where b.moduleid=6 and a.partnerid=? and a.deliverydate::date between ? and ? order by deliveryid desc"
db.DB.Raw(q1, input.Partnerid, input.Fromdate, input.ToDate).Find(&data)
}
}
return data
}
func GetAdminDeliveries(input models.DeliveryQuery) []models.Deliveryinfo {
logInfo("Admin Deliveries", "applocationid", input.Applocationid)
var data []models.Deliveryinfo
var qb strings.Builder
var params []interface{}
offset := (input.Pageno - 1) * input.Pagesize
qb.WriteString(`
SELECT a.*,
b.tenantname,b.primarycontact as tenantcontactno,b.tenanttoken,b.suburb as tenantsuburb,b.city as tenantcity, b.address AS tenantaddress,
CONCAT(c.firstname, ' ', c.lastname) AS ridername,c.userfcmtoken,c.contactno as ridercontact,
d.locationname,d.suburb AS locationsuburb,d.contactno AS locationcontactno,d.address AS locationaddress,
e.locationname AS applocation, f.slab, f.pricingdate, f.baseprice, f.minkm, f.priceperkm, f.maxkm, f.orders, f.othercharges, f.surgecharges
FROM deliveries a
JOIN tenants b ON a.tenantid = b.tenantid
JOIN app_users c ON a.userid = c.userid
JOIN tenantlocations d ON a.locationid = d.locationid
JOIN app_location e ON a.applocationid = e.applocationid
JOIN tenantpricing f ON a.tenantid = f.tenantid
WHERE b.moduleid = 6
AND a.applocationid = ?
`)
params = append(params, input.Applocationid)
// ✅ TENANT FILTER
if input.Tenantid != 0 {
qb.WriteString(" AND a.tenantid = ?")
params = append(params, input.Tenantid)
}
// ✅ LOCATION FILTER
if input.Locationid != 0 {
qb.WriteString(" AND a.locationid = ?")
params = append(params, input.Locationid)
}
// ✅ USER / RIDER FILTER
if input.UserID != 0 {
qb.WriteString(" AND a.userid = ?")
params = append(params, input.UserID)
}
// ✅ STATUS FILTER
if input.Status != "" && strings.ToLower(input.Status) != "all" {
qb.WriteString(" AND LOWER(a.orderstatus) = LOWER(?)")
params = append(params, input.Status)
}
// ✅ DATE RANGE FILTER
if input.Fromdate != "" && input.ToDate != "" {
qb.WriteString(`
AND a.deliverydate >= (CONCAT(?::text, ' 00:00:00'))::timestamp
AND a.deliverydate <= (CONCAT(?::text, ' 23:59:59'))::timestamp
`)
params = append(params, input.Fromdate, input.ToDate)
}
qb.WriteString(" ORDER BY a.deliveryid DESC")
// ✅ PAGINATION
if input.Pagesize > 0 {
qb.WriteString(" LIMIT ? OFFSET ?")
params = append(params, input.Pagesize, offset)
}
finalQuery := qb.String()
db.DB.Raw(finalQuery, params...).Scan(&data)
return data
}
func GetUserDeliveries(input models.DeliveryQuery) []models.Deliveryinfo {
var q1 string
logInfo("User Deliveries", "userid", input.UserID)
offset := (input.Pageno - 1) * input.Pagesize
var data []models.Deliveryinfo
var params []interface{}
q1 = deliveries + " where b.moduleid=6 and a.userid=?"
params = append(params, input.UserID)
// CASE 1: status = all → show all statuses
if input.Status == "all" {
q1 += " and a.deliverydate::date between ? and ? order by deliveryid desc"
params = append(params, input.Fromdate, input.ToDate)
// CASE 2: Specific status (picked, active, delivered…)
} else if input.Status != "" {
q1 += " and a.orderstatus=? and a.deliverydate::date between ? and ? order by deliveryid desc"
params = append(params, input.Status, input.Fromdate, input.ToDate)
// CASE 3: No status passed → default statuses
} else {
q1 += " and a.orderstatus in ('picked','active','skipped') and a.deliverydate::date between ? and ? order by deliveryid desc"
params = append(params, input.Fromdate, input.ToDate)
}
if input.Pagesize != 0 {
q1 += " LIMIT ? OFFSET ?"
params = append(params, input.Pagesize, offset)
}
db.DB.Raw(q1, params...).Find(&data)
return data
}
func GetUserDeliveriesv1(input models.DeliveryQuery) []models.Deliveryinfo {
var q1 string
logInfo("User Deliveriesv1", "userid", input.UserID)
offset := (input.Pageno - 1) * input.Pagesize
var data []models.Deliveryinfo
if input.Status != "" {
if input.Pagesize != 0 {
logInfo("Rider Deliveriesv2")
q1 = deliveries + " where a.userid=? and a.orderstatus=? and a.deliverydate::date between ? and ? order by deliveryid desc LIMIT ? OFFSET ?"
db.DB.Raw(q1, input.UserID, input.Status, input.Fromdate, input.ToDate, input.Pagesize, offset).Find(&data)
} else {
logInfo("Rider Deliveriesv1")
q1 = deliveries + " where a.userid=? and a.orderstatus=? and a.deliverydate::date between ? and ? order by deliveryid asc"
db.DB.Raw(q1, input.UserID, input.Status, input.Fromdate, input.ToDate).Find(&data)
}
} else {
if input.Pagesize != 0 {
logInfo("Rider Deliveriesv2")
q1 = deliveries + " where a.orderstatus in ('picked') and a.userid=? and a.deliverydate::date between ? and ? order by deliveryid desc LIMIT ? OFFSET ?"
db.DB.Raw(q1, input.UserID, input.Fromdate, input.ToDate, input.Pagesize, offset).Find(&data)
} else {
logInfo("Rider Deliveriesv1")
q1 = deliveries + " where a.orderstatus in ('picked') and a.userid=? and a.deliverydate::date between ? and ? order by deliveryid desc"
db.DB.Raw(q1, input.UserID, input.Fromdate, input.ToDate).Find(&data)
}
}
return data
}
func GetAppUserDeliveries(input models.DeliveryQuery) []models.Deliveryinfo {
var data []models.Deliveryinfo
logInfo("App User Deliveries", "appuserid", input.UserID)
var (
q1 strings.Builder
params []interface{}
)
offset := (input.Pageno - 1) * input.Pagesize
// 🔹 Base query
q1.WriteString(deliveries)
q1.WriteString(" WHERE b.moduleid = 6 AND g.status = 'Active' ")
// 🔹 Apply userid filter ONLY if userid > 0
if input.UserID > 0 {
q1.WriteString(" AND g.userid = ? ")
params = append(params, input.UserID)
}
// 🔹 Status filter (IGNORE if status = all)
if input.Status != "" && strings.ToLower(input.Status) != "all" {
q1.WriteString(" AND a.orderstatus = ? ")
params = append(params, input.Status)
}
// 🔹 Date filter
if input.Fromdate != "" && input.ToDate != "" {
q1.WriteString(" AND a.deliverydate::date BETWEEN ? AND ? ")
params = append(params, input.Fromdate, input.ToDate)
}
// 🔹 Keyword filter (case-insensitive)
if input.Keyword != "" {
k := "%" + strings.ToLower(input.Keyword) + "%"
q1.WriteString(`
AND (
LOWER(a.pickupcustomer) LIKE ? OR
LOWER(b.tenantname) LIKE ? OR
LOWER(a.deliverycustomer) LIKE ? OR
LOWER(a.pickupcontactno) LIKE ? OR
LOWER(a.deliverycontactno) LIKE ? OR
LOWER(CAST(a.orderid AS CHAR)) LIKE ? OR
LOWER(CAST(a.deliveryid AS CHAR)) LIKE ? OR
LOWER(c.firstname) LIKE ? OR
LOWER(c.contactno) LIKE ?
)
`)
for i := 0; i < 9; i++ {
params = append(params, k)
}
}
q1.WriteString(" ORDER BY a.deliveryid DESC ")
if input.Pagesize > 0 {
q1.WriteString(" LIMIT ? OFFSET ? ")
params = append(params, input.Pagesize, offset)
}
db.DB.Raw(q1.String(), params...).Scan(&data)
return data
}
func GetDeliveries(input models.DeliveryQuery) []models.Deliveryinfo {
var data []models.Deliveryinfo
var queryBuilder strings.Builder
var params []interface{}
logInfo("Get All Deliveries -new")
offset := (input.Pageno - 1) * input.Pagesize
baseQuery := `
SELECT a.*, b.tenantname, c.userid,
CONCAT(c.firstname, ' ', c.lastname) AS ridername,
c.contactno AS ridercontact,
d.slab, d.pricingdate, d.baseprice, d.minkm,
d.priceperkm, d.maxkm, d.orders,
d.othercharges, d.surgecharges
FROM deliveries a
JOIN tenants b ON a.tenantid = b.tenantid
JOIN app_users c ON a.userid = c.userid
JOIN tenantpricing d ON a.tenantid = d.tenantid
WHERE 1=1
`
queryBuilder.WriteString(baseQuery)
// ✅ DATE FILTER
if input.Fromdate != "" && input.ToDate != "" {
queryBuilder.WriteString(" AND b.moduleid=6 AND a.deliverydate::date BETWEEN ? AND ?")
params = append(params, input.Fromdate, input.ToDate)
}
// ✅ STATUS FILTER (ignore "all")
if input.Status != "" && strings.ToLower(input.Status) != "all" {
queryBuilder.WriteString(" AND b.moduleid=6 AND LOWER(a.orderstatus) = LOWER(?)")
params = append(params, input.Status)
}
// ✅ KEYWORD FILTER
if input.Keyword != "" {
k := "%" + strings.ToLower(input.Keyword) + "%"
queryBuilder.WriteString(`
AND (
LOWER(a.pickupcustomer) LIKE ? OR
LOWER(b.tenantname) LIKE ? OR
LOWER(a.deliverycustomer) LIKE ? OR
LOWER(a.pickupcontactno) LIKE ? OR
LOWER(a.deliverycontactno) LIKE ? OR
LOWER(CAST(a.orderid AS CHAR)) 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()
db.DB.Raw(query, params...).Scan(&data)
return data
}
func GetTenantLocationDeliveries(input models.DeliveryQuery) []models.Deliveryinfo {
logInfo("Tenant Location Deliveries", "tenantid", input.Tenantid, "locationid", input.Locationid)
var data []models.Deliveryinfo
var queryBuilder strings.Builder
var params []interface{}
offset := (input.Pageno - 1) * input.Pagesize
// 🔹 Base query
queryBuilder.WriteString(deliveries)
queryBuilder.WriteString(` WHERE b.moduleid = 6 AND a.tenantid = ? AND a.locationid = ?`)
params = append(params, input.Tenantid, input.Locationid)
// 🔹 USER FILTER (apply only if userid > 0)
if input.UserID > 0 {
queryBuilder.WriteString(` AND a.userid = ?`)
params = append(params, input.UserID)
}
// 🔹 STATUS FILTER (ignore "all", case-insensitive)
if input.Status != "" && strings.ToLower(input.Status) != "all" {
queryBuilder.WriteString(` AND LOWER(a.orderstatus) = LOWER(?)`)
params = append(params, input.Status)
}
// 🔹 DATE FILTER
if input.Fromdate != "" && input.ToDate != "" {
queryBuilder.WriteString(` AND a.deliverydate::date BETWEEN ? AND ?`)
params = append(params, input.Fromdate, input.ToDate)
}
// 🔹 KEYWORD FILTER (case-insensitive)
if input.Keyword != "" {
k := "%" + strings.ToLower(input.Keyword) + "%"
queryBuilder.WriteString(`
AND (
LOWER(a.pickupcustomer) LIKE ? OR
LOWER(b.tenantname) LIKE ? OR
LOWER(a.deliverycustomer) LIKE ? OR
LOWER(a.pickupcontactno) LIKE ? OR
LOWER(a.deliverycontactno) LIKE ? OR
LOWER(CAST(a.orderid AS CHAR)) LIKE ?
)
`)
// ✔ exactly 6 placeholders
for i := 0; i < 6; i++ {
params = append(params, k)
}
}
// 🔹 ORDER + PAGINATION
queryBuilder.WriteString(` ORDER BY a.deliveryid DESC`)
if input.Pagesize > 0 {
queryBuilder.WriteString(` LIMIT ? OFFSET ?`)
params = append(params, input.Pagesize, offset)
}
finalQuery := queryBuilder.String()
logInfo("Final Query", "query", finalQuery)
logInfo("Params", "params", params)
db.DB.Raw(finalQuery, params...).Scan(&data)
return data
}
func GetLocationDeliveries(input models.DeliveryQuery) []models.Deliveryinfo {
logInfo("Location Deliveries", "locationid", input.Locationid)
var q1 string
var data []models.Deliveryinfo
offset := (input.Pageno - 1) * input.Pagesize
if input.Status != "" {
if input.Fromdate != "" && input.ToDate != "" {
if input.Pagesize != 0 {
q1 = deliveries + " WHERE b.moduleid=6 and a.locationid=? AND a.orderstatus=? AND a.deliverydate::date BETWEEN ? AND ? ORDER BY a.deliveryid DESC LIMIT ? OFFSET ?"
db.DB.Raw(q1, input.Locationid, input.Status, input.Fromdate, input.ToDate, input.Pagesize, offset).Find(&data)
} else {
q1 = deliveries + " WHERE b.moduleid=6 and a.locationid=? AND a.orderstatus=? AND a.deliverydate::date BETWEEN ? AND ? ORDER BY a.deliveryid DESC"
db.DB.Raw(q1, input.Locationid, input.Status, input.Fromdate, input.ToDate).Find(&data)
}
} else {
if input.Pagesize != 0 {
q1 = deliveries + " WHERE b.moduleid=6 and a.locationid=? AND a.orderstatus=? ORDER BY a.deliveryid DESC LIMIT ? OFFSET ?"
db.DB.Raw(q1, input.Locationid, input.Status, input.Pagesize, offset).Find(&data)
} else {
q1 = deliveries + " WHERE b.moduleid=6 and a.locationid=? AND a.orderstatus=? ORDER BY a.deliveryid DESC"
db.DB.Raw(q1, input.Locationid, input.Status).Find(&data)
}
}
} else {
if input.Pagesize != 0 {
q1 = deliveries + " WHERE b.moduleid=6 and a.locationid=? AND a.deliverydate::date BETWEEN ? AND ? ORDER BY a.deliveryid DESC LIMIT ? OFFSET ?"
db.DB.Raw(q1, input.Locationid, input.Fromdate, input.ToDate, input.Pagesize, offset).Find(&data)
} else {
q1 = deliveries + " WHERE b.moduleid=6 and a.locationid=? AND a.deliverydate::date BETWEEN ? AND ? ORDER BY a.deliveryid DESC"
db.DB.Raw(q1, input.Locationid, input.Fromdate, input.ToDate).Find(&data)
}
}
return data
}
// func PublishLogv1(input []models.Deliverylogs) error {
// key := "Deliverylogs"
// for _, item := range input {
// // Convert each item to JSON
// jsonData, err := json.Marshal(item)
// if err != nil {
// return fmt.Errorf("failed to marshal delivery log: %v", err)
// }
// // Push each record into Redis list
// if err := db.Rdb.RPush(db.Ctx, key, jsonData).Err(); err != nil {
// return fmt.Errorf("failed to push to Redis: %v", err)
// }
// }
// return nil
// }
// func PublishLogv1(input []models.Deliverylogs) error {
// for _, item := range input {
// // key per delivery
// key := fmt.Sprintf("Deliverylogs:%d", item.Deliveryid)
// // Convert to JSON
// jsonData, err := json.Marshal(item)
// if err != nil {
// return fmt.Errorf("marshal error: %v", err)
// }
// // Write to delivery-specific list
// if err := db.Rdb.RPush(db.Ctx, key, jsonData).Err(); err != nil {
// return fmt.Errorf("redis push error: %v", err)
// }
// }
// return nil
// }
func PublishLogv1(input []models.Deliverylogs) error {
logInfo("PublishLogv1", "records", len(input))
pipe := db.Rdb.TxPipeline()
for _, item := range input {
logInfo("DELIVERY LOG RECEIVED",
"deliveryid", item.Deliveryid,
"userid", item.Userid,
"logdate", item.Logdate,
)
logTime, err := time.Parse("2006-01-02 15:04:05", item.Logdate)
if err != nil {
logError("❌ TIME PARSE FAILED", "error", err)
continue
}
ts := logTime.Unix()
logKey := "Deliverylogs:" + strconv.Itoa(item.Deliveryid)
userIndexKey := "user:deliverylogs:" + strconv.Itoa(item.Userid)
jsonData, _ := json.Marshal(item)
pipe.RPush(db.Ctx, logKey, jsonData)
pipe.ZAdd(db.Ctx, userIndexKey, redis.Z{
Score: float64(ts),
Member: item.Deliveryid,
})
logInfo("✅ ZADD PREPARED", "key", userIndexKey, "deliveryid", item.Deliveryid)
}
_, err := pipe.Exec(db.Ctx)
if err != nil {
logError("❌ PIPELINE EXEC FAILED", "error", err)
}
return err
}
func GetDeliverylogsv1(did int) ([]models.Deliverylogs, error) {
logInfo("GetDeliverylogsv1", "deliveryid", did)
var result []models.Deliverylogs
key := "Deliverylogs"
// Get all elements from the Redis list
values, err := db.Rdb.LRange(db.Ctx, key, 0, -1).Result()
if err != nil {
return nil, errors.New("failed to read from Redis: " + err.Error())
}
for _, v := range values {
var log models.Deliverylogs
if err := json.Unmarshal([]byte(v), &log); err != nil {
continue // skip invalid JSON entries
}
// filter only the deliveryid we need
if log.Deliveryid == did {
result = append(result, log)
}
}
return result, nil
}
func GetDeliveryLogsv1(deliveryID string) ([]models.Deliverylogs, error) {
logInfo("GetDeliveryLogsv1", "deliveryid", deliveryID)
ctx := context.Background()
var results []models.Deliverylogs
key := "Deliverylogs:" + deliveryID
// Fetch logs ONLY for this delivery
listItems, err := db.Rdb.LRange(ctx, key, 0, -1).Result()
if err != nil {
return nil, errors.New("redis lrange error: " + err.Error())
}
for _, item := range listItems {
var log models.Deliverylogs
if err := json.Unmarshal([]byte(item), &log); err != nil {
continue
}
// Fetch username using redis cached function
if log.Userid != 0 {
user, err := GetUserCached(log.Userid)
if err == nil {
log.Firstname = user.Firstname
log.Lastname = user.Lastname
}
}
results = append(results, log)
}
return results, nil
}
// func GetDeliveryCached(deliveryID int) (models.Delivery, error) {
// ctx := context.Background()
// var data models.Delivery
// key := fmt.Sprintf("delivery:info:%d", deliveryID)
// // Check in Redis
// cached, err := db.Rdb.Get(ctx, key).Result()
// if err == nil {
// json.Unmarshal([]byte(cached), &data)
// return data, nil
// }
// // Fetch from DB if not cached
// q := `
// SELECT deliveryid, pickupcustomer, deliverycustomer,
// pickupcontactno, deliverycontactno
// FROM deliveries
// WHERE deliveryid=? LIMIT 1
// `
// if err := db.DB.Raw(q, deliveryID).Scan(&data).Error; err != nil {
// return data, err
// }
// // Store back to Redis for 6 hours
// b, _ := json.Marshal(data)
// db.Rdb.Set(ctx, key, b, 6*time.Hour)
// return data, nil
// }
func GetUserDeliveryLogs(userid int, fromdate, todate string) ([]models.Deliverylogs, error) {
logInfo("GetUserDeliveryLogs", "userid", userid)
var fromTS int64 = 0
var toTS int64 = time.Now().Unix()
if fromdate != "" {
t, err := time.Parse("2006-01-02", fromdate)
if err != nil {
return nil, errors.New("invalid fromdate")
}
fromTS = t.Unix()
}
if todate != "" {
t, err := time.Parse("2006-01-02", todate)
if err != nil {
return nil, errors.New("invalid todate")
}
toTS = t.Add(23*time.Hour + 59*time.Minute + 59*time.Second).Unix()
}
// 1⃣ Get delivery IDs for user (O(logN))
deliveryIDs, err := db.Rdb.ZRangeByScore(
db.Ctx,
"user:deliverylogs:"+strconv.Itoa(userid),
&redis.ZRangeBy{
Min: strconv.FormatInt(fromTS, 10),
Max: strconv.FormatInt(toTS, 10),
},
).Result()
if err != nil || len(deliveryIDs) == 0 {
return []models.Deliverylogs{}, nil
}
// 2⃣ Batch fetch logs
var result []models.Deliverylogs
for _, id := range deliveryIDs {
key := "Deliverylogs:" + id
values, err := db.Rdb.LRange(db.Ctx, key, 0, -1).Result()
if err != nil {
continue
}
for _, v := range values {
var log models.Deliverylogs
if err := json.Unmarshal([]byte(v), &log); err == nil {
result = append(result, log)
}
}
}
return result, nil
}
func logInfo(msg string, props ...interface{}) {
utils.Logger.Infow(msg, props...)
}
func logError(msg string, props ...interface{}) {
utils.Logger.Errorw(msg, props...)
}