all the refactor
Build and Publish / BuildAndDeployAmd64 (push) Failing after 5s
Build and Publish / BuildAndDeployArm64 (push) Failing after 6s

This commit is contained in:
2026-06-28 17:51:52 +02:00
parent 7db0d236c7
commit aa8b2bdedc
84 changed files with 4328 additions and 2344 deletions
+22 -19
View File
@@ -6,6 +6,7 @@ import (
"strings"
"git.k6n.net/mats/go-cart-actor/pkg/cart"
"git.k6n.net/mats/platform/money"
)
// ----------------------------
@@ -161,7 +162,7 @@ func (tieredDiscountEffect) Progress(s *PromotionService, rule PromotionRule, a
currentPct, _ := selectTier(tiers, total) // 0 when below the first tier
// Find the nearest tier whose floor is still above the current total.
nextMin := int64(0)
nextMin := money.Cents(0)
nextPct := 0.0
found := false
for _, t := range tiers {
@@ -177,8 +178,10 @@ func (tieredDiscountEffect) Progress(s *PromotionService, rule PromotionRule, a
return nil, false
}
return map[string]any{
"remaining": nextMin - total,
"threshold": nextMin,
// Untyped JSON-presentation map: emit raw minor-unit int64 so consumers
// (and JSON) see a plain number, not money.Cents.
"remaining": (nextMin - total).Int64(),
"threshold": nextMin.Int64(),
"currentPercent": currentPct,
"nextPercent": nextPct,
}, true
@@ -201,13 +204,13 @@ func (s *PromotionService) cartTotalProgress(rule PromotionRule, ctx *PromotionE
if remaining <= 0 || !s.withinReach(rule, ctx, threshold) {
return nil, false
}
return map[string]any{"remaining": remaining, "threshold": threshold}, true
return map[string]any{"remaining": remaining.Int64(), "threshold": threshold.Int64()}, true
}
// withinReach reports whether the rule would apply if the cart total were
// atTotal — i.e. whether the cart-total threshold is the only thing holding it
// back. Cheap: it re-runs evaluation against a copy of the context.
func (s *PromotionService) withinReach(rule PromotionRule, ctx *PromotionEvalContext, atTotal int64) bool {
func (s *PromotionService) withinReach(rule PromotionRule, ctx *PromotionEvalContext, atTotal money.Cents) bool {
probe := *ctx
probe.CartTotalIncVat = atTotal
return s.EvaluateRule(rule, &probe).Applicable
@@ -216,7 +219,7 @@ func (s *PromotionService) withinReach(rule PromotionRule, ctx *PromotionEvalCon
// cartTotalThreshold finds the lowest cart-total floor a rule requires (a
// cart_total >= / > condition), walking nested groups. Returns false when the
// rule has no such condition.
func cartTotalThreshold(rule PromotionRule) (int64, bool) {
func cartTotalThreshold(rule PromotionRule) (money.Cents, bool) {
var threshold int64
found := false
var walk func(conds Conditions)
@@ -248,7 +251,7 @@ func cartTotalThreshold(rule PromotionRule) (int64, bool) {
}
}
walk(rule.Conditions)
return threshold, found
return money.Cents(threshold), found
}
// ----------------------------
@@ -264,8 +267,8 @@ func cartTotalThreshold(rule PromotionRule) (int64, bool) {
// when a total lands exactly on a shared boundary the higher tier wins, since
// selectTier keeps the last match while scanning in order.
type DiscountTier struct {
MinTotal int64
MaxTotal int64
MinTotal money.Cents
MaxTotal money.Cents
Percent float64
}
@@ -304,7 +307,7 @@ func applyPercentageDiscount(g *cart.CartGrain, pct float64) *cart.Price {
// selectTier returns the discount percent for the band the total falls into.
// Scans in declared order and keeps the last match so shared boundaries favour
// the higher tier.
func selectTier(tiers []DiscountTier, total int64) (float64, bool) {
func selectTier(tiers []DiscountTier, total money.Cents) (float64, bool) {
pct := 0.0
found := false
for _, t := range tiers {
@@ -327,8 +330,8 @@ func scalePrice(p *cart.Price, pct float64) *cart.Price {
return out
}
func scale(v int64, pct float64) int64 {
return int64(math.Round(float64(v) * pct / 100.0))
func scale(v money.Cents, pct float64) money.Cents {
return money.Cents(math.Round(float64(v.Int64()) * pct / 100.0))
}
// parseTiers reads the "tiers" array out of an Action.Config. Because configs
@@ -347,8 +350,8 @@ func parseTiers(config map[string]interface{}) []DiscountTier {
continue
}
tiers = append(tiers, DiscountTier{
MinTotal: int64(firstNum(m, "minTotal", "min_total")),
MaxTotal: int64(firstNum(m, "maxTotal", "max_total")),
MinTotal: money.Cents(int64(firstNum(m, "minTotal", "min_total"))),
MaxTotal: money.Cents(int64(firstNum(m, "maxTotal", "max_total"))),
Percent: firstNum(m, "percent", "discount_percent"),
})
}
@@ -381,7 +384,7 @@ func (buyXGetYEffect) Type() ActionType { return ActionBuyXGetY }
type unitItem struct {
item *cart.CartItem
price int64
price money.Cents
}
func (buyXGetYEffect) Apply(g *cart.CartGrain, rule PromotionRule, a Action) (*cart.Price, bool) {
@@ -561,16 +564,16 @@ func (bundleDiscountEffect) Apply(g *cart.CartGrain, _ PromotionRule, a Action)
var bundleDiscount *cart.Price
switch a.BundleConfig.Pricing.Type {
case "fixed_price":
targetPriceOre := int64(math.Round(a.BundleConfig.Pricing.Value * 100))
targetPriceOre := money.FromFloatMajor(a.BundleConfig.Pricing.Value)
if bundlePrice.IncVat > targetPriceOre {
pct := float64(bundlePrice.IncVat-targetPriceOre) / float64(bundlePrice.IncVat) * 100
pct := float64((bundlePrice.IncVat-targetPriceOre).Int64()) / float64(bundlePrice.IncVat.Int64()) * 100
bundleDiscount = scalePrice(bundlePrice, pct)
}
case "percentage_discount":
bundleDiscount = scalePrice(bundlePrice, a.BundleConfig.Pricing.Value)
case "fixed_discount":
discountOre := int64(math.Round(a.BundleConfig.Pricing.Value * 100))
pct := float64(discountOre) / float64(bundlePrice.IncVat) * 100
discountOre := money.FromFloatMajor(a.BundleConfig.Pricing.Value)
pct := float64(discountOre.Int64()) / float64(bundlePrice.IncVat.Int64()) * 100
bundleDiscount = scalePrice(bundlePrice, pct)
}