hasMany(BundleItem::class); } /** * Productos que componen el kit (relación many-to-many) */ public function inventories() { return $this->belongsToMany(Inventory::class, 'bundle_items') ->withPivot('quantity') ->withTimestamps(); } /** * Precio del kit */ public function price() { return $this->hasOne(BundlePrice::class); } /** * Stock disponible del kit en el almacén principal (único para venta) * = mínimo(stock_almacén_principal_componente / cantidad_requerida) */ public function getAvailableStockAttribute(): int { $mainWarehouseId = Warehouse::where('is_main', true)->value('id'); if (! $mainWarehouseId) { return 0; } return $this->stockInWarehouse($mainWarehouseId); } /** * Stock disponible en un almacén específico */ public function stockInWarehouse(int $warehouseId): int { if ($this->items->isEmpty()) { return 0; } $minStock = PHP_INT_MAX; foreach ($this->items as $item) { $inventory = $item->inventory; $warehouseStock = $inventory->stockInWarehouse($warehouseId); $possibleKits = $warehouseStock > 0 ? floor($warehouseStock / $item->quantity) : 0; $minStock = min($minStock, $possibleKits); } return $minStock === PHP_INT_MAX ? 0 : (int) $minStock; } /** * Costo total del kit (suma de costos de componentes) */ public function getTotalCostAttribute(): float { $total = 0; foreach ($this->items as $item) { $componentCost = $item->inventory->price->cost ?? 0; $total += $componentCost * $item->quantity; } return round($total, 2); } /** * Validar si el kit tiene stock disponible */ public function hasStock(int $quantity = 1, ?int $warehouseId = null): bool { if ($warehouseId) { return $this->stockInWarehouse($warehouseId) >= $quantity; } return $this->available_stock >= $quantity; } }