$raw * @param Collection $activeAddons Keyed by addon id. * @param array $existingQtyByAddonId Already-owned qty per addon id, for capping total against max. * @return list * * @throws ValidationException */ public static function resolve(array $raw, Collection $activeAddons, array $existingQtyByAddonId = []): array { $resolved = []; foreach ($raw as $entry) { $addonId = $entry['addon_id'] ?? null; if (!$addonId) { continue; } $addon = $activeAddons->get($addonId); if (!$addon) { throw ValidationException::withMessages([ 'addons' => ['Addon không tồn tại hoặc không áp dụng cho sản phẩm này.'], ]); } $qty = (int) ($entry['qty'] ?? 1); if ($qty < $addon->min) { throw ValidationException::withMessages([ 'addons' => ["Số lượng addon {$addon->name} phải lớn hơn hoặc bằng {$addon->min}."], ]); } $totalQty = ($existingQtyByAddonId[$addonId] ?? 0) + $qty; if ($addon->max > 0 && $totalQty > $addon->max) { throw ValidationException::withMessages([ 'addons' => ["Số lượng addon {$addon->name} không được vượt quá {$addon->max}."], ]); } if ($addon->step > 0 && $qty % $addon->step !== 0) { throw ValidationException::withMessages([ 'addons' => ["Số lượng addon {$addon->name} phải là bội số của {$addon->step}."], ]); } $resolved[] = [ 'addon_id' => $addon->id, 'code' => strtolower((string) $addon->code), 'name' => $addon->name, 'qty' => $qty, 'price' => $addon->price, 'subtotal' => $addon->price * $qty, ]; } return $resolved; } }