diff --git a/repositories/customerRepository.go b/repositories/customerRepository.go index 9d4fc08..2182c49 100644 --- a/repositories/customerRepository.go +++ b/repositories/customerRepository.go @@ -188,10 +188,21 @@ func (r *customerRepository) GetTenantCustomers(tid, lid, pageno, pagesize int, var args []interface{} searchLike := "%" + keyword + "%" + // DISTINCT ON collapses to one row per customer BEFORE the LIMIT is applied. + // Without it the store-scoped branch below paginated the joined + // customerlocations rows — one per saved address — so `pagesize` bought a + // page of addresses, not of customers. Live example: locationid 1185 returned + // 12 rows that were only 2 people, 11 of them one customer's addresses. A + // store with a page size of 20 therefore listed roughly three customers and + // gave no hint that the rest existed. + // + // The ORDER BY must lead with the DISTINCT ON expression, so customerid sorts + // first; the trailing keys only decide WHICH address represents a customer, + // preferring the one flagged primary. if lid != 0 { - q1 = `SELECT a.customerid,a.firstname,a.lastname,a.contactno,a.email, + q1 = `SELECT DISTINCT ON (a.customerid) a.customerid,a.firstname,a.lastname,a.contactno,a.email, b.locationid as deliverylocationid,b.address,b.suburb,b.city,b.state,b.landmark,b.doorno,b.postcode, - b.latitude,b.longitude,a.applocationid,c.locationid as tenantlocationid,a.status + b.latitude,b.longitude,a.applocationid,c.locationid as tenantlocationid,a.status FROM customers a LEFT JOIN customerlocations b ON a.customerid=b.customerid INNER JOIN tenantcustomers c ON a.customerid=c.customerid @@ -204,13 +215,17 @@ func (r *customerRepository) GetTenantCustomers(tid, lid, pageno, pagesize int, args = append(args, searchLike, searchLike, searchLike) } - q1 += ` ORDER BY a.customerid DESC LIMIT ? OFFSET ?` + q1 += ` ORDER BY a.customerid DESC, b.primaryaddress DESC NULLS LAST, b.locationid ASC + LIMIT ? OFFSET ?` args = append(args, pagesize, offset) } else { - q1 = `SELECT a.customerid,a.firstname,a.lastname,a.contactno,a.email, + // A customer linked to several outlets of the same tenant has one + // tenantcustomers row per outlet, so this branch double-counted them + // against the LIMIT too. + q1 = `SELECT DISTINCT ON (a.customerid) a.customerid,a.firstname,a.lastname,a.contactno,a.email, a.address,a.suburb,a.city,a.state,a.landmark,a.doorno,a.postcode, - a.latitude,a.longitude,a.applocationid,c.locationid as tenantlocationid,a.status + a.latitude,a.longitude,a.applocationid,c.locationid as tenantlocationid,a.status FROM customers a INNER JOIN tenantcustomers c ON a.customerid=c.customerid WHERE c.tenantid = ?` @@ -223,12 +238,10 @@ func (r *customerRepository) GetTenantCustomers(tid, lid, pageno, pagesize int, args = append(args, searchLike, searchLike, searchLike) } - q1 += ` ORDER BY a.customerid DESC LIMIT ? OFFSET ?` + q1 += ` ORDER BY a.customerid DESC, c.locationid ASC LIMIT ? OFFSET ?` args = append(args, pagesize, offset) } - print(q1) - r.db.Raw(q1, args...).Find(&data) return data } diff --git a/repositories/deliveriesRepository.go b/repositories/deliveriesRepository.go index 8f79c5d..4515bc7 100644 --- a/repositories/deliveriesRepository.go +++ b/repositories/deliveriesRepository.go @@ -1,11 +1,13 @@ package repositories import ( + "errors" "fmt" "log" "nearle/models" "strconv" "strings" + "time" "github.com/jinzhu/copier" "gorm.io/gorm" @@ -147,18 +149,75 @@ func (r *deliveriesRepository) UpdateDelivery(data models.UpdateDeliveryStatus) 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 = data.Assigntime - if err := tx.Table("orders").Where("orderheaderid = ?", data.Orderheaderid).Updates(&ord).Error; err != nil { + ord.Pending = stamp(data.Assigntime) + if err := syncOrder(); err != nil { tx.Rollback() return err } @@ -181,8 +240,8 @@ func (r *deliveriesRepository) UpdateDelivery(data models.UpdateDeliveryStatus) case "delivered": ord.Orderstatus = data.Orderstatus - ord.Delivered = data.Deliverytime - if err := tx.Table("orders").Where("orderheaderid = ?", data.Orderheaderid).Updates(&ord).Error; err != nil { + ord.Delivered = stamp(data.Deliverytime) + if err := syncOrder(); err != nil { tx.Rollback() return err } @@ -204,8 +263,8 @@ func (r *deliveriesRepository) UpdateDelivery(data models.UpdateDeliveryStatus) case "cancelled": ord.Orderstatus = data.Orderstatus - ord.Cancelled = data.Canceltime - if err := tx.Table("orders").Where("orderheaderid = ?", data.Orderheaderid).Updates(&ord).Error; err != nil { + ord.Cancelled = stamp(data.Canceltime) + if err := syncOrder(); err != nil { tx.Rollback() return err }