better results
Build and Publish / BuildAndDeployArm64 (push) Failing after 6s
Build and Publish / BuildAndDeployAmd64 (push) Failing after 5s

This commit is contained in:
2026-07-08 00:55:05 +02:00
parent 781c1bd171
commit e20793a6b3
4 changed files with 545 additions and 91 deletions
+24 -53
View File
@@ -202,59 +202,17 @@ func main() {
actor.NewMutationProcessor(func(ctx context.Context, g *cart.CartGrain) error {
_, span := tracer.Start(ctx, "Totals and promotions")
defer span.End()
// Clear bypass flags first
for _, v := range g.Vouchers {
if v != nil {
v.BypassedByPromotions = false
}
}
g.UpdateTotals()
promotionCtx := promotions.NewContextFromCart(g, promotions.WithNow(time.Now()))
results, _ := promotionService.EvaluateAll(promotionStore.Snapshot(), promotionCtx)
// Check if any qualified promotion rule has a coupon_code condition matching our vouchers
hasBypassed := false
for _, res := range results {
if res.Applicable {
promotions.WalkConditions(res.Rule.Conditions, func(c promotions.Condition) bool {
if bc, ok := c.(promotions.BaseCondition); ok && bc.Type == promotions.CondCouponCode {
var codes []string
if s, ok := bc.Value.AsString(); ok {
codes = append(codes, strings.ToLower(s))
} else if arr, ok := bc.Value.AsStringSlice(); ok {
for _, s := range arr {
codes = append(codes, strings.ToLower(s))
}
}
for _, code := range codes {
for _, v := range g.Vouchers {
if v != nil && strings.ToLower(v.Code) == code {
if !v.BypassedByPromotions {
v.BypassedByPromotions = true
hasBypassed = true
}
}
}
}
}
return true
})
}
}
if hasBypassed {
// Re-evaluate with bypassed vouchers
g.UpdateTotals()
promotionCtx = promotions.NewContextFromCart(g, promotions.WithNow(time.Now()))
results, _ = promotionService.EvaluateAll(promotionStore.Snapshot(), promotionCtx)
}
// ApplyResults applies qualifying actions in priority order and records
// every effect — both applied discounts and pending "spend X more for ..."
// nudges with their progress — in g.AppliedPromotions.
promotionService.ApplyResults(g, results, promotionCtx)
// Canonical promotion pipeline — same call site as the
// /promotions/evaluate-with-cart preview handler in
// promotions_evaluate.go. Going through the shared
// PromotionService.EvaluateAndApply means the bypass
// loop, the re-eval trigger, and ApplyResults stay in
// lockstep with the preview so a what-if always matches
// post-add behavior (including the coupon+promotion-
// overlap edge case). If you find yourself adding
// promotion logic here, add it to EvaluateAndApply
// instead — the preview must follow.
promotionService.EvaluateAndApply(promotionStore.Snapshot(), g, promotions.WithNow(time.Now()))
g.Version++
return nil
}),
@@ -490,6 +448,19 @@ func main() {
// without creating or mutating a real cart.
mux.HandleFunc("POST /promotions/evaluate", newPromotionEvaluateHandler(promotionStore, promotionService))
// Cart-aware promotion evaluation: reads the cartid cookie the storefront
// already maintains, fetches the actual cart grain (cross-pod via
// GetAnywhere), merges with the request's items (the items the user is
// considering adding), and runs the engine on the merged line list. The
// cart is the source of truth (vouchers, customer segment, customer
// lifetime value, total) so a serialised copy from the browser can race
// the live cart — letting the backend read the cookie + fetch the grain
// means exactly one source of truth and no race. Missing cookie or fetch
// failure gracefully degrades to a stateless evaluation of the request
// items alone. See newPromotionEvaluateWithCartHandler in
// promotions_evaluate.go for the full contract.
mux.HandleFunc("POST /promotions/evaluate-with-cart", newPromotionEvaluateWithCartHandler(promotionStore, promotionService, syncedServer))
// only for local
mux.HandleFunc("GET /add/remote/{host}", func(w http.ResponseWriter, r *http.Request) {
pool.AddRemote(r.PathValue("host"))