package main import ( "fmt" "slices" messages "git.tornberg.me/go-cart-actor/pkg/messages" ) // mutation_set_delivery.go // // Registers the SetDelivery mutation. // // Semantics (mirrors legacy switch logic): // - If the payload specifies an explicit list of item IDs (payload.Items): // - Each referenced cart line must exist. // - None of the referenced items may already belong to a delivery. // - Only those items are associated with the new delivery. // - If payload.Items is empty: // - All items currently without any delivery are associated with the new delivery. // - A new delivery line is created with: // - Auto-incremented delivery ID (cart-local) // - Provider from payload // - Fixed price (currently hard-coded: 4900 minor units) – adjust as needed // - Optional PickupPoint copied from payload // - Cart totals are recalculated (WithTotals) // // Error cases: // - Referenced item does not exist // - Referenced item already has a delivery // - No items qualify (resulting association set empty) -> returns error (prevents creating empty delivery) // // Concurrency: // - Uses g.mu to protect lastDeliveryId increment and append to Deliveries slice. // Item scans are read-only and performed outside the lock for simplicity; // if stricter guarantees are needed, widen the lock section. // // Future extension points: // - Variable delivery pricing (based on weight, distance, provider, etc.) // - Validation of provider codes // - Multi-currency delivery pricing func SetDelivery(g *CartGrain, m *messages.SetDelivery) error { if m == nil { return fmt.Errorf("SetDelivery: nil payload") } if m.Provider == "" { return fmt.Errorf("SetDelivery: provider is empty") } withDelivery := g.ItemsWithDelivery() targetItems := make([]uint32, 0) if len(m.Items) == 0 { // Use every item currently without a delivery targetItems = append(targetItems, g.ItemsWithoutDelivery()...) } else { // Validate explicit list for _, id64 := range m.Items { id := uint32(id64) found := false for _, it := range g.Items { if it.Id == id { found = true break } } if !found { return fmt.Errorf("SetDelivery: item id %d not found", id) } if slices.Contains(withDelivery, id) { return fmt.Errorf("SetDelivery: item id %d already has a delivery", id) } targetItems = append(targetItems, id) } } if len(targetItems) == 0 { return fmt.Errorf("SetDelivery: no eligible items to attach") } // Append new delivery g.mu.Lock() g.lastDeliveryId++ newId := g.lastDeliveryId g.Deliveries = append(g.Deliveries, &CartDelivery{ Id: newId, Provider: m.Provider, PickupPoint: m.PickupPoint, Price: *NewPriceFromIncVat(4900, 25.0), Items: targetItems, }) g.mu.Unlock() return nil }