diff --git a/repositories/productRepository.go b/repositories/productRepository.go index 923a9bd..d7a514a 100644 --- a/repositories/productRepository.go +++ b/repositories/productRepository.go @@ -508,7 +508,27 @@ func (r *productRepository) GetLocationProducts(tenantID, locationID, subcategor } offset := (pageno - 1) * pagesize - params := []interface{}{tenantID, locationID} + // locationID 0 means EVERY branch this tenant runs. + // + // There was no such read, and the console papered over it by listing the + // FIRST branch under "All branches" — so a merchant with five outlets saw + // RS Puram's four products and not NSN's five, with nothing on screen + // saying so. Picking one branch showed MORE than picking all of them, which + // is the one shape a filter must never have. + // + // Aggregated per product rather than one row per branch: this is the admin + // catalogue answering "what do we sell", and the same product repeated five + // times is a different question nobody asked. Stock sums across branches — + // each productstocks row belongs to exactly one branch, so the join cannot + // double-count it — while price, publication and status collapse to the + // best answer any branch has, which is what a tenant-wide view of a + // per-branch column means. + allBranches := locationID <= 0 + + params := []interface{}{tenantID} + if !allBranches { + params = append(params, locationID) + } // b.price is the per-store selling price. It has to be selected explicitly: // a.* only covers products (whose price column is retailprice, the master @@ -531,8 +551,20 @@ func (r *productRepository) GetLocationProducts(tenantID, locationID, subcategor // productlocations. Without it the column came back null for every row — // including ones that had just been published — and the store catalogue, // which filters on it, showed nothing. - query := `SELECT a.*, b.productlocationid, b.status, b.publishedat, - COALESCE(NULLIF(b.price, 0), a.retailprice, 0) AS price, + // Per-branch columns are aggregated when every branch is asked for, and + // selected plainly when one is. Postgres needs one or the other — a column + // that is neither grouped nor aggregated is a hard error, not a warning — + // so the two shapes are written out rather than patched together. + perBranch := `b.productlocationid, b.status, b.publishedat, + COALESCE(NULLIF(b.price, 0), a.retailprice, 0) AS price,` + if allBranches { + perBranch = `MIN(b.productlocationid) AS productlocationid, + MAX(b.status) AS status, + MAX(b.publishedat) AS publishedat, + COALESCE(NULLIF(MAX(b.price), 0), a.retailprice, 0) AS price,` + } + + query := `SELECT a.*, ` + perBranch + ` COALESCE(SUM(CASE WHEN UPPER(c.stocktype) = 'IN' THEN c.quantity ELSE 0 END), 0) AS total_in, COALESCE(SUM(CASE WHEN UPPER(c.stocktype) = 'OUT' THEN c.quantity ELSE 0 END), 0) AS total_out, COALESCE(SUM(CASE WHEN UPPER(c.stocktype) = 'IN' THEN c.quantity ELSE 0 END) - @@ -541,8 +573,11 @@ func (r *productRepository) GetLocationProducts(tenantID, locationID, subcategor SUM(CASE WHEN UPPER(c.stocktype) = 'OUT' THEN c.quantity ELSE 0 END), 0) AS quantity FROM products a INNER JOIN productlocations b ON a.productid = b.productid AND a.tenantid = b.tenantid - LEFT JOIN productstocks c ON a.productid = c.productid AND b.locationid = c.locationid AND a.tenantid = c.tenantid - WHERE a.approve=1 AND a.tenantid = ? AND b.locationid = ?` + LEFT JOIN productstocks c ON a.productid = c.productid AND b.locationid = c.locationid AND a.tenantid = c.tenantid + WHERE a.approve=1 AND a.tenantid = ?` + if !allBranches { + query += ` AND b.locationid = ?` + } if subcategoryID != 0 { query += " AND a.subcategoryid = ?" @@ -554,10 +589,18 @@ func (r *productRepository) GetLocationProducts(tenantID, locationID, subcategor params = append(params, "%"+strings.ToLower(keyword)+"%") } - query += ` GROUP BY a.productid, a.productname, a.productimage, a.categoryid, a.subcategoryid, + // Grouped by the product alone when every branch is asked for, so one + // product is one row however many outlets stock it. Including b.locationid + // there would return the same product five times over — which is what a + // merchant with five branches would have seen instead of a catalogue. + groupBy := ` GROUP BY a.productid, a.productname, a.productimage, a.categoryid, a.subcategoryid, a.productunit, a.productcost, a.taxpercent, a.taxamount, a.retailprice, - b.tenantid, b.locationid, b.productlocationid, b.status, b.price, b.publishedat - ORDER BY a.productid DESC LIMIT ? OFFSET ?` + b.tenantid, b.locationid, b.productlocationid, b.status, b.price, b.publishedat` + if allBranches { + groupBy = ` GROUP BY a.productid, a.productname, a.productimage, a.categoryid, a.subcategoryid, + a.productunit, a.productcost, a.taxpercent, a.taxamount, a.retailprice` + } + query += groupBy + ` ORDER BY a.productid DESC LIMIT ? OFFSET ?` params = append(params, pagesize, offset)