parent item mutations
This commit is contained in:
@@ -2,9 +2,11 @@ package cart
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"git.k6n.net/mats/go-cart-actor/pkg/actor"
|
||||
cart_messages "git.k6n.net/mats/go-cart-actor/proto/cart"
|
||||
"git.k6n.net/mats/platform/inventory"
|
||||
)
|
||||
|
||||
@@ -71,6 +73,73 @@ func (c *CartMutationContext) ReleaseItem(ctx context.Context, cartId CartId, sk
|
||||
return c.reservationService.Release(ctx, inventory.SKU(sku), l, inventory.CartID(cartId.String()))
|
||||
}
|
||||
|
||||
// CascadeChildQuantities scales every direct child's quantity
|
||||
// proportionally when a parent's quantity is bumped (ChangeQuantity or
|
||||
// the AddItem-merge path).
|
||||
//
|
||||
// - Ratio: newChildQty = oldChildQty × newParentQty / oldParentQty.
|
||||
// - Floor: scale-down via integer division can yield 0 (e.g. parent
|
||||
// 2 → 1, child qty 1 → (1×1)/2 = 0). We clamp to 1 so a scale-down
|
||||
// never silently deletes an accessory. The mismatch is acceptable —
|
||||
// shipping one extra child is better than dropping one.
|
||||
// - Reservations: each child's reservation is released then re-acquired
|
||||
// at the new quantity if UseReservations is true for that child AND a
|
||||
// prior reservation existed. A failing reservation is logged, not
|
||||
// raised, so a single bad child doesn't abort the whole cascade
|
||||
// (matches the existing removal pattern in mutation_items.go).
|
||||
// - Recursion: descends into grandchildren using the child's own
|
||||
// before/after quantities as the next ratio's input, so deeply
|
||||
// nested accessory trees (parent → child → grandchild) stay in
|
||||
// ratio with the top-level change.
|
||||
//
|
||||
// Caller is responsible for locking any grain mutex and for re-running
|
||||
// UpdateTotals after the cascade has settled.
|
||||
func (c *CartMutationContext) CascadeChildQuantities(
|
||||
ctx context.Context,
|
||||
g *CartGrain,
|
||||
parentLineId uint32,
|
||||
oldParentQty uint16,
|
||||
newParentQty uint16,
|
||||
) {
|
||||
if oldParentQty == 0 || oldParentQty == newParentQty {
|
||||
return
|
||||
}
|
||||
|
||||
for _, it := range g.Items {
|
||||
if it == nil {
|
||||
continue
|
||||
}
|
||||
if it.ParentId == nil || *it.ParentId != parentLineId {
|
||||
continue
|
||||
}
|
||||
oldChildQty := it.Quantity
|
||||
ratio := uint32(newParentQty) * uint32(oldChildQty) / uint32(oldParentQty)
|
||||
newChildQty := uint16(ratio)
|
||||
if newChildQty == 0 {
|
||||
newChildQty = 1
|
||||
}
|
||||
if newChildQty == oldChildQty {
|
||||
continue
|
||||
}
|
||||
|
||||
if g.Type == cart_messages.CartType_REGULAR && c.UseReservations(it) && it.ReservationEndTime != nil {
|
||||
if err := c.ReleaseItem(ctx, g.Id, it.Sku, it.StoreId); err != nil {
|
||||
log.Printf("CascadeChildQuantities: failed to release %s: %v", it.Sku, err)
|
||||
}
|
||||
endTime, err := c.ReserveItem(ctx, g.Id, it.Sku, it.StoreId, newChildQty)
|
||||
if err != nil {
|
||||
log.Printf("CascadeChildQuantities: failed to reserve %s at qty %d: %v", it.Sku, newChildQty, err)
|
||||
} else if endTime != nil {
|
||||
it.ReservationEndTime = endTime
|
||||
}
|
||||
}
|
||||
|
||||
it.Quantity = newChildQty
|
||||
// Descend so grandchildren track this child's ratio.
|
||||
c.CascadeChildQuantities(ctx, g, it.Id, oldChildQty, newChildQty)
|
||||
}
|
||||
}
|
||||
|
||||
func NewCartMultationRegistry(context *CartMutationContext) actor.MutationRegistry {
|
||||
|
||||
reg := actor.NewMutationRegistry()
|
||||
|
||||
Reference in New Issue
Block a user