cart and checkout
Build and Publish / BuildAndDeployAmd64 (push) Failing after 4s
Build and Publish / BuildAndDeployArm64 (push) Failing after 6s

This commit is contained in:
2026-06-27 19:49:00 +02:00
parent 492f54ff45
commit 528c59bfd3
67 changed files with 3618 additions and 1031 deletions
+101
View File
@@ -233,6 +233,103 @@ func HandleIssueRefund(o *OrderGrain, m *messages.IssueRefund) error {
return nil
}
// HandleRequestExchange records an exchange request.
func HandleRequestExchange(o *OrderGrain, m *messages.RequestExchange) error {
if o.Status != StatusFulfilled && o.Status != StatusCompleted && o.Status != StatusPartiallyFulfilled {
return fmt.Errorf("order: cannot request an exchange in status %q", o.Status)
}
ex := Exchange{
ID: m.GetId(),
ReturnID: m.GetReturnId(),
Reason: m.GetReason(),
RequestedAt: msToString(m.GetAtMs()),
}
// 1. Process return lines
for _, rl := range m.GetReturnLines() {
line := o.findLine(rl.GetReference())
if line == nil {
return fmt.Errorf("order: exchange return references unknown line %q", rl.GetReference())
}
ex.ReturnLines = append(ex.ReturnLines, FulfillmentEntry{
Reference: rl.GetReference(),
Quantity: int(rl.GetQuantity()),
})
}
// 2. Process new lines (replacement items)
for _, nl := range m.GetNewLines() {
line := Line{
Reference: nl.GetReference(),
Sku: nl.GetSku(),
Name: nl.GetName(),
Quantity: int(nl.GetQuantity()),
UnitPrice: nl.GetUnitPrice(),
TaxRate: int(nl.GetTaxRate()),
TotalAmount: nl.GetTotalAmount(),
TotalTax: nl.GetTotalTax(),
}
ex.NewLines = append(ex.NewLines, line)
// Add the replacement line to the order so it can be fulfilled
o.Lines = append(o.Lines, line)
}
// 3. Since there are new lines that are not yet fulfilled, transition order status back to partially fulfilled
o.Status = StatusPartiallyFulfilled
o.Exchanges = append(o.Exchanges, ex)
o.touch(ex.RequestedAt)
return nil
}
// HandleEditOrderDetails updates addresses or shipping price.
func HandleEditOrderDetails(o *OrderGrain, m *messages.EditOrderDetails) error {
if o.Status == StatusCompleted || o.Status == StatusCancelled || o.Status == StatusRefunded {
return fmt.Errorf("order: cannot edit details in status %q", o.Status)
}
if s := m.GetShippingAddress(); len(s) > 0 {
o.ShippingAddress = append([]byte(nil), s...)
}
if b := m.GetBillingAddress(); len(b) > 0 {
o.BillingAddress = append([]byte(nil), b...)
}
if sp := m.GetShippingPrice(); sp > 0 {
found := false
var oldTotal int64
for i := range o.Lines {
if o.Lines[i].Name == "Delivery" {
oldTotal = o.Lines[i].TotalAmount
o.Lines[i].UnitPrice = sp
o.Lines[i].TotalAmount = sp * int64(o.Lines[i].Quantity)
o.Lines[i].TotalTax = ComputeTax(o.Lines[i].TotalAmount, o.Lines[i].TaxRate)
o.TotalAmount = o.TotalAmount - oldTotal + o.Lines[i].TotalAmount
found = true
break
}
}
if !found {
line := Line{
Reference: "delivery",
Sku: "delivery",
Name: "Delivery",
Quantity: 1,
UnitPrice: sp,
TaxRate: 25,
TotalAmount: sp,
TotalTax: ComputeTax(sp, 25),
}
o.Lines = append(o.Lines, line)
o.TotalAmount += sp
}
}
o.touch(msToString(m.GetAtMs()))
return nil
}
// RegisterMutations registers every order mutation handler with the registry so
// the grain pool can apply and replay them.
func RegisterMutations(reg actor.MutationRegistry) {
@@ -245,5 +342,9 @@ func RegisterMutations(reg actor.MutationRegistry) {
actor.NewMutation(HandleCancelOrder),
actor.NewMutation(HandleRequestReturn),
actor.NewMutation(HandleIssueRefund),
actor.NewMutation(HandleRequestExchange),
actor.NewMutation(HandleEditOrderDetails),
)
}
+12
View File
@@ -101,6 +101,16 @@ type Return struct {
RequestedAt string `json:"requestedAt,omitempty"`
}
// Exchange records an exchange request.
type Exchange struct {
ID string `json:"id"`
ReturnID string `json:"returnId"`
Reason string `json:"reason,omitempty"`
ReturnLines []FulfillmentEntry `json:"returnLines,omitempty"`
NewLines []Line `json:"newLines,omitempty"`
RequestedAt string `json:"requestedAt,omitempty"`
}
// Refund records a refund issued against the order.
type Refund struct {
Provider string `json:"provider"`
@@ -137,8 +147,10 @@ type OrderGrain struct {
Payments []*Payment `json:"payments,omitempty"`
Fulfillments []Fulfillment `json:"fulfillments,omitempty"`
Returns []Return `json:"returns,omitempty"`
Exchanges []Exchange `json:"exchanges,omitempty"`
Refunds []Refund `json:"refunds,omitempty"`
CapturedAmount int64 `json:"capturedAmount"`
RefundedAmount int64 `json:"refundedAmount"`
+30
View File
@@ -0,0 +1,30 @@
package order
import "git.k6n.net/mats/go-cart-actor/pkg/cart"
// TaxProvider computes taxes for orders and line items.
// The canonical definition is in pkg/cart; this is a re-export for
// backward compatibility.
type TaxProvider = cart.TaxProvider
// ComputeTax is the shared tax formula: tax = total x rate / (100 + rate).
// Re-exported from pkg/cart for backward compatibility.
func ComputeTax(total int64, taxRate int) int64 {
return cart.ComputeTax(total, taxRate)
}
// NordicTaxProvider implements TaxProvider for the Nordic countries.
// Re-exported from pkg/cart for backward compatibility.
type NordicTaxProvider = cart.NordicTaxProvider
func NewNordicTaxProvider(defaultCountry string) *NordicTaxProvider {
return cart.NewNordicTaxProvider(defaultCountry)
}
// StaticTaxProvider is a stateless TaxProvider with no country awareness.
// Re-exported from pkg/cart for backward compatibility.
type StaticTaxProvider = cart.StaticTaxProvider
func NewStaticTaxProvider() *StaticTaxProvider {
return cart.NewStaticTaxProvider()
}
+34
View File
@@ -0,0 +1,34 @@
package order
import (
"testing"
)
// TestReExports verifies that the order package's type aliases and
// forwarders to pkg/cart compile and work correctly.
func TestReExports(t *testing.T) {
// Type alias: TaxProvider = cart.TaxProvider
var _ TaxProvider = NewNordicTaxProvider("SE")
var _ TaxProvider = NewStaticTaxProvider()
// NordicTaxProvider
p := NewNordicTaxProvider("SE")
if p.Name() != "nordic" {
t.Errorf("Name = %q", p.Name())
}
if got := p.DefaultTaxRate("SE"); got != 25 {
t.Errorf("DefaultTaxRate(SE) = %d, want 25", got)
}
if got := ComputeTax(1250, 25); got != 250 {
t.Errorf("ComputeTax(1250, 25) = %d, want 250", got)
}
// StaticTaxProvider
s := NewStaticTaxProvider()
if s.Name() != "static" {
t.Errorf("Name = %q", s.Name())
}
if got := s.DefaultTaxRate("SE"); got != 0 {
t.Errorf("DefaultTaxRate(SE) = %d, want 0", got)
}
}