cart and checkout
This commit is contained in:
+34
-2
@@ -42,6 +42,7 @@ type PromotionEvalContext struct {
|
||||
CustomerLifetimeValue float64
|
||||
OrderCount int
|
||||
Now time.Time
|
||||
CouponCodes []string
|
||||
}
|
||||
|
||||
// ContextOption allows customization of fields when building a PromotionEvalContext.
|
||||
@@ -67,6 +68,11 @@ func WithNow(t time.Time) ContextOption {
|
||||
return func(c *PromotionEvalContext) { c.Now = t }
|
||||
}
|
||||
|
||||
// WithCouponCodes sets active coupon codes.
|
||||
func WithCouponCodes(codes []string) ContextOption {
|
||||
return func(c *PromotionEvalContext) { c.CouponCodes = codes }
|
||||
}
|
||||
|
||||
// NewContextFromCart builds a PromotionEvalContext from a CartGrain and optional metadata.
|
||||
func NewContextFromCart(g *cart.CartGrain, opts ...ContextOption) *PromotionEvalContext {
|
||||
ctx := &PromotionEvalContext{
|
||||
@@ -74,6 +80,7 @@ func NewContextFromCart(g *cart.CartGrain, opts ...ContextOption) *PromotionEval
|
||||
CartTotalIncVat: 0,
|
||||
TotalItemQuantity: 0,
|
||||
Now: time.Now(),
|
||||
CouponCodes: make([]string, 0),
|
||||
}
|
||||
if g.TotalPrice != nil {
|
||||
ctx.CartTotalIncVat = g.TotalPrice.IncVat
|
||||
@@ -91,6 +98,9 @@ func NewContextFromCart(g *cart.CartGrain, opts ...ContextOption) *PromotionEval
|
||||
})
|
||||
ctx.TotalItemQuantity += uint32(it.Quantity)
|
||||
}
|
||||
for _, v := range g.Vouchers {
|
||||
ctx.CouponCodes = append(ctx.CouponCodes, v.Code)
|
||||
}
|
||||
for _, o := range opts {
|
||||
o(ctx)
|
||||
}
|
||||
@@ -106,18 +116,29 @@ type PromotionService struct {
|
||||
// even one that has already applied (e.g. a tiered discount at 5% pointing at
|
||||
// the next 9% tier). Register a new Effect to add a new action type.
|
||||
effects map[ActionType]Effect
|
||||
// DefaultTaxProvider provides the VAT rate used when constructing synthetic
|
||||
// carts for stateless evaluation and preview. When nil, falls back to 25 %.
|
||||
DefaultTaxProvider cart.TaxProvider
|
||||
}
|
||||
|
||||
// NewPromotionService constructs a PromotionService with an optional tracer and
|
||||
// the built-in effect handlers.
|
||||
func NewPromotionService(t Tracer) *PromotionService {
|
||||
// NewPromotionService constructs a PromotionService with an optional tracer.
|
||||
// When tp is non-nil, it is used as DefaultTaxProvider for synthetic cart
|
||||
// construction (stateless evaluation, preview); otherwise the service falls
|
||||
// back to 25 % VAT.
|
||||
func NewPromotionService(t Tracer, tp ...cart.TaxProvider) *PromotionService {
|
||||
if t == nil {
|
||||
t = NoopTracer{}
|
||||
}
|
||||
return &PromotionService{
|
||||
s := &PromotionService{
|
||||
tracer: t,
|
||||
effects: defaultEffects(),
|
||||
}
|
||||
if len(tp) > 0 && tp[0] != nil {
|
||||
s.DefaultTaxProvider = tp[0]
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// RegisterEffect adds (or overrides) the handler for an action type, so new
|
||||
@@ -360,11 +381,22 @@ func evaluateBaseCondition(b BaseCondition, ctx *PromotionEvalContext) bool {
|
||||
return evalDayOfWeek(ctx.Now, b)
|
||||
case CondTimeOfDay:
|
||||
return evalTimeOfDay(ctx.Now, b)
|
||||
case CondCouponCode:
|
||||
return evalAnyCouponMatch(ctx.CouponCodes, b)
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func evalAnyCouponMatch(codes []string, b BaseCondition) bool {
|
||||
for _, code := range codes {
|
||||
if evalStringCompare(code, b) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func evalAnyItemMatch(pred func(PromotionItem) bool, b BaseCondition, ctx *PromotionEvalContext) bool {
|
||||
if slices.ContainsFunc(ctx.Items, pred) {
|
||||
return true
|
||||
|
||||
Reference in New Issue
Block a user