$data['name'], 'sku' => $data['sku'], 'barcode' => $data['barcode'] ?? null, ]); // 2. Agregar componentes al kit foreach ($data['items'] as $item) { BundleItem::create([ 'bundle_id' => $bundle->id, 'inventory_id' => $item['inventory_id'], 'quantity' => $item['quantity'], ]); } // 3. Calcular costos y crear precio $bundle->load('items.inventory.price'); $totalCost = 0; $totalRetailPrice = 0; foreach ($bundle->items as $item) { $totalCost += ($item->inventory->price->cost ?? 0) * $item->quantity; $totalRetailPrice += ($item->inventory->price->retail_price ?? 0) * $item->quantity; } // Permitir override de precio (para promociones) $finalRetailPrice = $data['retail_price'] ?? $totalRetailPrice; $tax = $data['tax'] ?? 16.00; BundlePrice::create([ 'bundle_id' => $bundle->id, 'cost' => $totalCost, 'retail_price' => $finalRetailPrice, 'tax' => $tax, ]); return $bundle->fresh(['items.inventory.price', 'price']); }); } /** * Actualizar un kit existente */ public function updateBundle(Bundle $bundle, array $data): Bundle { return DB::transaction(function () use ($bundle, $data) { // 1. Actualizar datos básicos del bundle $bundle->update([ 'name' => $data['name'] ?? $bundle->name, 'sku' => $data['sku'] ?? $bundle->sku, 'barcode' => $data['barcode'] ?? $bundle->barcode, ]); // 2. Actualizar componentes si se proporcionan if (isset($data['items'])) { // Eliminar componentes actuales $bundle->items()->delete(); // Crear nuevos componentes foreach ($data['items'] as $item) { BundleItem::create([ 'bundle_id' => $bundle->id, 'inventory_id' => $item['inventory_id'], 'quantity' => $item['quantity'], ]); } } // 3. Recalcular o actualizar precio if (isset($data['recalculate_price']) && $data['recalculate_price']) { // Recalcular precio basado en componentes actuales $bundle->load('items.inventory.price'); $totalCost = 0; $totalRetailPrice = 0; foreach ($bundle->items as $item) { $totalCost += ($item->inventory->price->cost ?? 0) * $item->quantity; $totalRetailPrice += ($item->inventory->price->retail_price ?? 0) * $item->quantity; } $finalRetailPrice = $data['retail_price'] ?? $totalRetailPrice; $tax = $data['tax'] ?? 16.00; $bundle->price->update([ 'cost' => $totalCost, 'retail_price' => $finalRetailPrice, 'tax' => $tax, ]); } elseif (isset($data['retail_price'])) { // Solo actualizar precio sin recalcular componentes $bundle->price->update([ 'retail_price' => $data['retail_price'], 'tax' => $data['tax'] ?? 16.00, ]); } return $bundle->fresh(['items.inventory.price', 'price']); }); } /** * Validar disponibilidad de stock del kit */ public function validateBundleAvailability(Bundle $bundle, int $quantity, ?int $warehouseId = null): void { if (!$bundle->hasStock($quantity, $warehouseId)) { $availableStock = $warehouseId ? $bundle->stockInWarehouse($warehouseId) : $bundle->available_stock; throw new \Exception( "Stock insuficiente del kit '{$bundle->name}'. " . "Disponibles: {$availableStock}, Requeridos: {$quantity}" ); } } /** * Obtener precio total de componentes (sin promoción) */ public function getComponentsValue(Bundle $bundle): float { $total = 0; foreach ($bundle->items as $item) { $componentPrice = $item->inventory->price->retail_price ?? 0; $total += $componentPrice * $item->quantity; } return round($total, 2); } /** * Eliminar (soft delete) un bundle */ public function deleteBundle(Bundle $bundle): bool { return $bundle->delete(); } /** * Restaurar un bundle eliminado */ public function restoreBundle(int $bundleId): ?Bundle { $bundle = Bundle::withTrashed()->find($bundleId); if ($bundle && $bundle->trashed()) { $bundle->restore(); return $bundle->fresh(['items.inventory.price', 'price']); } return null; } }