try new vouchers
All checks were successful
Build and Publish / Metadata (push) Successful in 9s
Build and Publish / BuildAndDeployAmd64 (push) Successful in 1m14s
Build and Publish / BuildAndDeployArm64 (push) Successful in 3m57s

This commit is contained in:
matst80
2025-10-14 22:14:22 +02:00
parent f0b6a733f1
commit a7cbdcd0da
6 changed files with 632 additions and 18 deletions

View File

@@ -8,6 +8,7 @@ import (
"time"
messages "git.tornberg.me/go-cart-actor/pkg/messages"
"git.tornberg.me/go-cart-actor/pkg/voucher"
)
// Legacy padded [16]byte CartId and its helper methods removed.
@@ -86,7 +87,55 @@ type Voucher struct {
Code string `json:"code"`
Rules []*messages.VoucherRule `json:"rules"`
Id uint32 `json:"id"`
Value Price `json:"value"`
Value int64 `json:"value"`
}
func (v *Voucher) AppliesTo(cart *CartGrain) ([]*CartItem, bool) {
// No rules -> applies to entire cart
if len(v.Rules) == 0 {
return cart.Items, true
}
// Build evaluation context once
ctx := voucher.EvalContext{
Items: make([]voucher.Item, 0, len(cart.Items)),
CartTotalInc: 0,
}
if cart.TotalPrice != nil {
ctx.CartTotalInc = cart.TotalPrice.IncVat
}
for _, it := range cart.Items {
category := ""
if it.Meta != nil {
category = it.Meta.Category
}
ctx.Items = append(ctx.Items, voucher.Item{
Sku: it.Sku,
Category: category,
UnitPrice: it.Price.IncVat,
})
}
// All voucher rules must pass (logical AND)
for _, rule := range v.Rules {
expr := rule.GetCondition()
if expr == "" {
// Empty condition treated as pass (acts like a comment / placeholder)
continue
}
rs, err := voucher.ParseRules(expr)
if err != nil {
// Fail closed on parse error
return nil, false
}
if !rs.Applies(ctx) {
return nil, false
}
}
return cart.Items, true
}
func (c *CartGrain) GetId() uint64 {
@@ -208,9 +257,11 @@ func (c *CartGrain) UpdateTotals() {
for _, delivery := range c.Deliveries {
c.TotalPrice.Add(delivery.Price)
}
// for _, voucher := range c.Vouchers {
// c.TotalPrice -= voucher.Value
// c.TotalTax -= voucher.TaxValue
// c.TotalDiscountTax += voucher.TaxValue
// }
for _, voucher := range c.Vouchers {
if _, ok := voucher.AppliesTo(c); ok {
value := NewPriceFromIncVat(voucher.Value, 25)
c.TotalDiscount.Add(*value)
}
}
}

View File

@@ -7,6 +7,32 @@ import (
"git.tornberg.me/go-cart-actor/pkg/messages"
)
func RemoveVoucher(g *CartGrain, m *messages.RemoveVoucher) error {
if m == nil {
return &actor.MutationError{
Message: "RemoveVoucher: nil payload",
Code: 1003,
StatusCode: 400,
}
}
if !slices.ContainsFunc(g.Vouchers, func(v *Voucher) bool {
return v.Id == m.Id
}) {
return &actor.MutationError{
Message: "voucher not applied",
Code: 1004,
StatusCode: 400,
}
}
g.Vouchers = slices.DeleteFunc(g.Vouchers, func(v *Voucher) bool {
return v.Id == m.Id
})
g.UpdateTotals()
return nil
}
func AddVoucher(g *CartGrain, m *messages.AddVoucher) error {
if m == nil {
return &actor.MutationError{
@@ -31,7 +57,7 @@ func AddVoucher(g *CartGrain, m *messages.AddVoucher) error {
Id: g.lastVoucherId,
Code: m.Code,
Rules: m.VoucherRules,
Value: *NewPriceFromIncVat(m.Value, 25.0),
Value: m.Value,
})
g.UpdateTotals()
return nil

View File

@@ -12,6 +12,7 @@ import (
"git.tornberg.me/go-cart-actor/pkg/actor"
messages "git.tornberg.me/go-cart-actor/pkg/messages"
"git.tornberg.me/go-cart-actor/pkg/voucher"
"github.com/gogo/protobuf/proto"
)
@@ -444,9 +445,20 @@ func (s *PoolServer) ProxyHandler(fn func(w http.ResponseWriter, r *http.Request
}
}
type AddVoucherRequest struct {
VoucherCode string `json:"code"`
}
func (s *PoolServer) AddVoucherHandler(w http.ResponseWriter, r *http.Request, cartId CartId) error {
msg := &messages.AddVoucher{}
json.NewDecoder(r.Body).Decode(msg)
data := &AddVoucherRequest{}
json.NewDecoder(r.Body).Decode(data)
v := voucher.Service{}
msg, err := v.GetVoucher(data.VoucherCode)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(err.Error()))
return err
}
reply, err := s.ApplyLocal(cartId, msg)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)