app sbucategories

This commit is contained in:
2026-09-08 11:49:34 +05:30
parent e87f09c4f2
commit 7d9e628fd0
2 changed files with 26 additions and 7 deletions

View File

@@ -21,11 +21,21 @@ type Productcount struct {
Outofstock int `json:"outofstock"` Outofstock int `json:"outofstock"`
} }
// ProductCategoryUpdate is one row of a bulk re-filing: which product, and // ProductCategoryUpdate is one row of a bulk re-filing: which product, which
// which category it should sit in. // category it sits in, and — the field that actually decides what a shopper
// sees — which subcategory.
//
// Both are here because they do different jobs. GetProducts filters on
// categoryid (the customer app asks for 2), while GetProductsBySubcategory
// GROUPS on subcategoryid, so the aisle heading in the app comes from the
// second and the first only decides whether the product is returned at all.
// A caller re-filing products into aisles sends categoryid 2 unchanged and the
// subcategory it worked out.
type ProductCategoryUpdate struct { type ProductCategoryUpdate struct {
Productid int `json:"productid"` Productid int `json:"productid"`
Categoryid int `json:"categoryid"` Categoryid int `json:"categoryid"`
// Optional: 0 leaves whatever the product already has.
Subcategoryid int `json:"subcategoryid"`
} }
type ProductCategory struct { type ProductCategory struct {

View File

@@ -1429,7 +1429,7 @@ func (r *productRepository) RecategoriseProducts(tenantID int, updates []models.
} }
placeholders := make([]string, 0, len(updates)) placeholders := make([]string, 0, len(updates))
args := make([]any, 0, len(updates)*2) args := make([]any, 0, len(updates)*3)
for _, u := range updates { for _, u := range updates {
if u.Productid == 0 || u.Categoryid == 0 { if u.Productid == 0 || u.Categoryid == 0 {
// categoryid 0 is the value the customer app rejects outright. // categoryid 0 is the value the customer app rejects outright.
@@ -1437,18 +1437,27 @@ func (r *productRepository) RecategoriseProducts(tenantID int, updates []models.
// product from every shopper. // product from every shopper.
continue continue
} }
placeholders = append(placeholders, "(?::bigint, ?::bigint)") placeholders = append(placeholders, "(?::bigint, ?::bigint, ?::bigint)")
args = append(args, u.Productid, u.Categoryid) args = append(args, u.Productid, u.Categoryid, u.Subcategoryid)
} }
if len(placeholders) == 0 { if len(placeholders) == 0 {
return 0, nil return 0, nil
} }
// subcategoryid is written only when the caller sent one.
//
// It is the field the customer app groups by, so a 0 arriving from a caller
// that does not know about aisles would silently move a product back into
// the app's "Uncategorized" bucket. Zero therefore means "leave it alone",
// the same rule UpdateProductCategory already follows.
args = append(args, tenantID) args = append(args, tenantID)
tx := r.db.Exec(` tx := r.db.Exec(`
UPDATE products p UPDATE products p
SET categoryid = v.categoryid SET categoryid = v.categoryid,
FROM (VALUES `+strings.Join(placeholders, ",")+`) AS v(productid, categoryid) subcategoryid = CASE WHEN v.subcategoryid > 0
THEN v.subcategoryid
ELSE p.subcategoryid END
FROM (VALUES `+strings.Join(placeholders, ",")+`) AS v(productid, categoryid, subcategoryid)
WHERE p.productid = v.productid WHERE p.productid = v.productid
AND p.tenantid = ? AND p.tenantid = ?
`, args...) `, args...)