variants as a single product
This commit is contained in:
@@ -20,6 +20,7 @@ type ProductService interface {
|
||||
UpdateProductVariant(productid, variantid int) error
|
||||
AddProductVariant(v models.Productvariant) (models.Productvariant, error)
|
||||
RemoveProductVariant(tenantid, variantid int) error
|
||||
VariantChildIDs(tenantid int) (map[int]bool, error)
|
||||
CreateProductStock(stocks []models.Productstock) error
|
||||
CreateProduct(product models.Products) error
|
||||
UpdateProduct(product models.Products) error
|
||||
@@ -156,6 +157,12 @@ func (s *productService) RemoveProductVariant(tenantid, variantid int) error {
|
||||
return s.repo.RemoveProductVariant(tenantid, variantid)
|
||||
}
|
||||
|
||||
// VariantChildIDs lists the products that are a size of another, so a caller
|
||||
// that lists products for a SHOPPER can leave them out.
|
||||
func (s *productService) VariantChildIDs(tenantid int) (map[int]bool, error) {
|
||||
return s.repo.VariantChildIDs(tenantid)
|
||||
}
|
||||
|
||||
func (s *productService) UpdateProductStatus(productIDs []int, status string) error {
|
||||
return s.repo.UpdateProductStatus(productIDs, status)
|
||||
}
|
||||
|
||||
@@ -85,3 +85,35 @@ func InStockOnlyGrouped(groups []models.Tenantproducts) []models.Tenantproducts
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// WithoutVariantChildren drops the products that are a size of another one.
|
||||
//
|
||||
// The companion to InStockOnlyGrouped, and applied only for the customer app.
|
||||
// A shop stocking Aachi Baby Fryums in 100g, 500g and 1kg has three rows; the
|
||||
// shopper should meet one product with three sizes, not three near-identical
|
||||
// listings they have to read the suffix of to tell apart.
|
||||
//
|
||||
// The group's visible row is the PARENT, and it carries the whole set in
|
||||
// `variantoptions` — including its own size — so nothing is unreachable: every
|
||||
// size is one tap away from the one row that remains.
|
||||
//
|
||||
// The console is deliberately unaffected. A merchant prices, restocks and
|
||||
// publishes each size separately, so all three must stay visible there.
|
||||
func WithoutVariantChildren(groups []models.Tenantproducts, isChild map[int]bool) []models.Tenantproducts {
|
||||
if len(isChild) == 0 {
|
||||
return groups
|
||||
}
|
||||
out := make([]models.Tenantproducts, 0, len(groups))
|
||||
for _, group := range groups {
|
||||
kept := make([]models.Products, 0, len(group.Products))
|
||||
for _, product := range group.Products {
|
||||
if isChild[product.Productid] {
|
||||
continue
|
||||
}
|
||||
kept = append(kept, product)
|
||||
}
|
||||
group.Products = kept
|
||||
out = append(out, group)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
@@ -667,3 +667,51 @@ func TestFilteringGroupsDoesNotDisturbTheCallersSlice(t *testing.T) {
|
||||
t.Errorf("the caller's slice was modified: %d products left", len(original[0].Products))
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
One product, three sizes.
|
||||
|
||||
R mart stocks Aachi Baby Fryums as three product rows — 100g, 500g and 1kg —
|
||||
each with its own price and its own shelf. In the app that read as three
|
||||
near-identical listings a shopper had to tell apart by a suffix. Grouped, it is
|
||||
one listing with a size picker.
|
||||
*/
|
||||
|
||||
func TestASizeOfAnotherProductIsNotListedOnItsOwn(t *testing.T) {
|
||||
groups := []models.Tenantproducts{{Products: []models.Products{
|
||||
{Productid: 7112}, // 100g — the parent
|
||||
{Productid: 7113}, // 1kg — a size of it
|
||||
{Productid: 7114}, // 500g — a size of it
|
||||
}}}
|
||||
got := WithoutVariantChildren(groups, map[int]bool{7113: true, 7114: true})
|
||||
if len(got[0].Products) != 1 || got[0].Products[0].Productid != 7112 {
|
||||
t.Fatalf("expected only the parent, got %v", got[0].Products)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAShopWithNoGroupsIsUntouched(t *testing.T) {
|
||||
// The common case by far, and it must not pay for the feature.
|
||||
groups := []models.Tenantproducts{{Products: []models.Products{{Productid: 1}, {Productid: 2}}}}
|
||||
got := WithoutVariantChildren(groups, map[int]bool{})
|
||||
if len(got[0].Products) != 2 {
|
||||
t.Errorf("an ungrouped shop lost products: %v", got[0].Products)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTheShopSurvivesEvenIfEveryProductIsASize(t *testing.T) {
|
||||
// Should not happen — a group always has a parent — but a listing is the
|
||||
// wrong place to discover bad data, and an empty list beats a crash.
|
||||
groups := []models.Tenantproducts{{Products: []models.Products{{Productid: 5}}}}
|
||||
got := WithoutVariantChildren(groups, map[int]bool{5: true})
|
||||
if len(got) != 1 || len(got[0].Products) != 0 {
|
||||
t.Errorf("expected the shop to survive with nothing listed, got %v", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFilteringChildrenDoesNotDisturbTheCallersSlice(t *testing.T) {
|
||||
original := []models.Tenantproducts{{Products: []models.Products{{Productid: 1}, {Productid: 2}}}}
|
||||
_ = WithoutVariantChildren(original, map[int]bool{2: true})
|
||||
if len(original[0].Products) != 2 {
|
||||
t.Errorf("the caller's slice was modified")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user