link user
Build and Publish / BuildAndDeployAmd64 (push) Failing after 4s
Build and Publish / BuildAndDeployArm64 (push) Failing after 5s

This commit is contained in:
2026-06-30 18:45:58 +02:00
parent 7b657901ac
commit 9495c654fa
5 changed files with 67 additions and 20 deletions
+38 -17
View File
@@ -31,7 +31,7 @@ type Effect interface {
// Apply mutates the cart for a qualifying action and returns the discount it
// took (nil for non-monetary effects such as free shipping) plus whether
// anything took effect worth recording.
Apply(g *cart.CartGrain, rule PromotionRule, a Action) (discount *cart.Price, applied bool)
Apply(g *cart.CartGrain, rule PromotionRule, a Action) (discount *cart.Price, itemIds []uint32, applied bool)
// Progress reports how far the cart is from (further) unlocking this action,
// as an open key/value payload. qualified says whether the owning rule
// currently applies, letting the effect distinguish "remaining to unlock"
@@ -77,8 +77,9 @@ func (s *PromotionService) ApplyResults(g *cart.CartGrain, results []EvaluationR
}
recorded := false
if res.Applicable {
if discount, applied := eff.Apply(g, res.Rule, a); applied {
if discount, itemIds, applied := eff.Apply(g, res.Rule, a); applied {
entry.Discount = discount
entry.ItemIds = itemIds
recorded = true
}
}
@@ -111,9 +112,9 @@ type percentageDiscountEffect struct{}
func (percentageDiscountEffect) Type() ActionType { return ActionPercentageDiscount }
func (percentageDiscountEffect) Apply(g *cart.CartGrain, _ PromotionRule, a Action) (*cart.Price, bool) {
func (percentageDiscountEffect) Apply(g *cart.CartGrain, _ PromotionRule, a Action) (*cart.Price, []uint32, bool) {
d := applyPercentageDiscount(g, percentFromValue(a.Value))
return d, d != nil
return d, nil, d != nil
}
func (percentageDiscountEffect) Progress(s *PromotionService, rule PromotionRule, a Action, ctx *PromotionEvalContext, qualified bool) (map[string]any, bool) {
@@ -129,8 +130,8 @@ type freeShippingEffect struct{}
func (freeShippingEffect) Type() ActionType { return ActionFreeShipping }
func (freeShippingEffect) Apply(_ *cart.CartGrain, _ PromotionRule, _ Action) (*cart.Price, bool) {
return nil, true
func (freeShippingEffect) Apply(_ *cart.CartGrain, _ PromotionRule, _ Action) (*cart.Price, []uint32, bool) {
return nil, nil, true
}
func (freeShippingEffect) Progress(s *PromotionService, rule PromotionRule, a Action, ctx *PromotionEvalContext, qualified bool) (map[string]any, bool) {
@@ -148,9 +149,9 @@ type tieredDiscountEffect struct{}
func (tieredDiscountEffect) Type() ActionType { return ActionTieredDiscount }
func (tieredDiscountEffect) Apply(g *cart.CartGrain, _ PromotionRule, a Action) (*cart.Price, bool) {
func (tieredDiscountEffect) Apply(g *cart.CartGrain, _ PromotionRule, a Action) (*cart.Price, []uint32, bool) {
d := applyTieredDiscount(g, a)
return d, d != nil
return d, nil, d != nil
}
func (tieredDiscountEffect) Progress(s *PromotionService, rule PromotionRule, a Action, ctx *PromotionEvalContext, qualified bool) (map[string]any, bool) {
@@ -387,7 +388,7 @@ type unitItem struct {
price money.Cents
}
func (buyXGetYEffect) Apply(g *cart.CartGrain, rule PromotionRule, a Action) (*cart.Price, bool) {
func (buyXGetYEffect) Apply(g *cart.CartGrain, rule PromotionRule, a Action) (*cart.Price, []uint32, bool) {
config := a.Config
buy := int(firstNum(config, "buy"))
if buy <= 0 {
@@ -416,7 +417,7 @@ func (buyXGetYEffect) Apply(g *cart.CartGrain, rule PromotionRule, a Action) (*c
n := len(units)
numFree := (n / (buy + get)) * get
if numFree <= 0 {
return nil, false
return nil, nil, false
}
// Sort units by price ascending so we discount the cheapest ones
@@ -431,13 +432,15 @@ func (buyXGetYEffect) Apply(g *cart.CartGrain, rule PromotionRule, a Action) (*c
})
totalDiscount := cart.NewPrice()
affectedMap := make(map[uint32]bool)
for i := 0; i < numFree; i++ {
unitDiscount := scalePrice(&units[i].item.Price, discount)
totalDiscount.Add(*unitDiscount)
affectedMap[units[i].item.Id] = true
}
if totalDiscount.IncVat <= 0 {
return nil, false
return nil, nil, false
}
// Never discount below zero.
if totalDiscount.IncVat > g.TotalPrice.IncVat {
@@ -445,7 +448,14 @@ func (buyXGetYEffect) Apply(g *cart.CartGrain, rule PromotionRule, a Action) (*c
}
g.TotalDiscount.Add(*totalDiscount)
g.TotalPrice.Subtract(*totalDiscount)
return totalDiscount, true
var itemIds []uint32
for id := range affectedMap {
itemIds = append(itemIds, id)
}
slices.Sort(itemIds)
return totalDiscount, itemIds, true
}
func (buyXGetYEffect) Progress(s *PromotionService, rule PromotionRule, a Action, ctx *PromotionEvalContext, qualified bool) (map[string]any, bool) {
@@ -460,9 +470,9 @@ type bundleDiscountEffect struct{}
func (bundleDiscountEffect) Type() ActionType { return ActionBundleDiscount }
func (bundleDiscountEffect) Apply(g *cart.CartGrain, _ PromotionRule, a Action) (*cart.Price, bool) {
func (bundleDiscountEffect) Apply(g *cart.CartGrain, _ PromotionRule, a Action) (*cart.Price, []uint32, bool) {
if a.BundleConfig == nil {
return nil, false
return nil, nil, false
}
// 1. Represent all cart items as individual units
@@ -545,10 +555,11 @@ func (bundleDiscountEffect) Apply(g *cart.CartGrain, _ PromotionRule, a Action)
}
if len(formedBundles) == 0 {
return nil, false
return nil, nil, false
}
totalDiscount := cart.NewPrice()
affectedMap := make(map[uint32]bool)
for _, bundle := range formedBundles {
// Calculate the original bundle price
bundlePrice := cart.NewPrice()
@@ -579,11 +590,14 @@ func (bundleDiscountEffect) Apply(g *cart.CartGrain, _ PromotionRule, a Action)
if bundleDiscount != nil && bundleDiscount.IncVat > 0 {
totalDiscount.Add(*bundleDiscount)
for _, item := range bundle {
affectedMap[item.Id] = true
}
}
}
if totalDiscount.IncVat <= 0 {
return nil, false
return nil, nil, false
}
// Never discount below zero.
if totalDiscount.IncVat > g.TotalPrice.IncVat {
@@ -591,7 +605,14 @@ func (bundleDiscountEffect) Apply(g *cart.CartGrain, _ PromotionRule, a Action)
}
g.TotalDiscount.Add(*totalDiscount)
g.TotalPrice.Subtract(*totalDiscount)
return totalDiscount, true
var itemIds []uint32
for id := range affectedMap {
itemIds = append(itemIds, id)
}
slices.Sort(itemIds)
return totalDiscount, itemIds, true
}
func (bundleDiscountEffect) Progress(s *PromotionService, rule PromotionRule, a Action, ctx *PromotionEvalContext, qualified bool) (map[string]any, bool) {
+14
View File
@@ -184,6 +184,13 @@ func TestBuyXGetYEffect(t *testing.T) {
if got := g.TotalPrice.IncVat; got != 2000 {
t.Errorf("total price = %d, want 2000", got)
}
if len(g.AppliedPromotions) != 1 {
t.Fatalf("expected 1 applied promotion, got %d", len(g.AppliedPromotions))
}
itemIds := g.AppliedPromotions[0].ItemIds
if len(itemIds) != 1 || itemIds[0] != 1 {
t.Errorf("expected affected itemIds [1], got %v", itemIds)
}
}
func TestBundleDiscountEffect(t *testing.T) {
@@ -247,4 +254,11 @@ func TestBundleDiscountEffect(t *testing.T) {
if got := g.TotalPrice.IncVat; got != 4000 {
t.Errorf("total price = %d, want 4000", got)
}
if len(g.AppliedPromotions) != 1 {
t.Fatalf("expected 1 applied promotion, got %d", len(g.AppliedPromotions))
}
itemIds := g.AppliedPromotions[0].ItemIds
if len(itemIds) != 2 || itemIds[0] != 1 || itemIds[1] != 2 {
t.Errorf("expected affected itemIds [1, 2], got %v", itemIds)
}
}