productosStockBajo = $this->getProductosStockBajo(); $this->bodegasEstado = $this->getBodegasEstado(); $this->sugerenciasTransferencia = $this->getSugerenciasTransferencia(); } protected function getProductosStockBajo() { return Producto::with(['bodegas', 'categoria']) ->where('estado', true) ->get() ->filter(function ($producto) { return $producto->getStockEfectivo() <= $producto->stock_minimo; }) ->map(function ($producto) { return [ 'id' => $producto->id, 'nombre' => $producto->nombre, 'categoria' => $producto->categoria->nombre ?? 'Sin categoría', 'stock_actual' => $producto->getStockEfectivo(), 'stock_minimo' => $producto->stock_minimo, 'distribucion' => (function($producto) { // Productos con variantes: agregar stock por bodegas a partir de variantes if ($producto->variants()->exists()) { $bodegaTotals = []; foreach ($producto->variants as $variante) { foreach ($variante->bodegas as $bodega) { $id = $bodega->id; if (! isset($bodegaTotals[$id])) { $bodegaTotals[$id] = ['bodega' => $bodega->nombre, 'stock' => 0]; } $bodegaTotals[$id]['stock'] += (int) ($bodega->pivot->stock ?? 0); } } return array_values($bodegaTotals); } // Productos sin variantes: información directa desde producto_bodega return $producto->bodegas->map(function ($bodega) { return [ 'bodega' => $bodega->nombre, 'stock' => $bodega->pivot->stock, ]; })->toArray(); })($producto), ]; }) ->toArray(); } protected function getBodegasEstado() { return Bodega::with('productos') ->get() ->map(function ($bodega) { $totalProductos = $bodega->productos()->count(); $productosConStock = $bodega->productos()->wherePivot('stock', '>', 0)->count(); $stockTotal = $bodega->productos()->sum('producto_bodega.stock'); return [ 'id' => $bodega->id, 'nombre' => $bodega->nombre, 'total_productos' => $totalProductos, 'productos_con_stock' => $productosConStock, 'stock_total' => $stockTotal, 'utilizacion' => $totalProductos > 0 ? round(($productosConStock / $totalProductos) * 100, 1) : 0, ]; }) ->toArray(); } protected function getSugerenciasTransferencia() { $sugerencias = []; // Buscar productos con stock desbalanceado entre bodegas $productos = Producto::with('bodegas') ->where('estado', true) ->get(); foreach ($productos as $producto) { if ($producto->bodegas->count() > 1) { $bodegas = $producto->bodegas->sortByDesc('pivot.stock'); $mayor = $bodegas->first(); $menor = $bodegas->last(); // Si hay gran diferencia de stock entre bodegas if ($mayor->pivot->stock > 0 && $menor->pivot->stock <= $producto->stock_minimo) { $cantidadSugerida = min( floor($mayor->pivot->stock * 0.3), // Máximo 30% del stock de la bodega con más stock $producto->stock_minimo - $menor->pivot->stock + 5 // Lo necesario para estar sobre el mínimo ); if ($cantidadSugerida > 0) { $sugerencias[] = [ 'producto_id' => $producto->id, 'producto_nombre' => $producto->nombre, 'bodega_origen' => $mayor->nombre, 'bodega_origen_id' => $mayor->id, 'bodega_destino' => $menor->nombre, 'bodega_destino_id' => $menor->id, 'cantidad_sugerida' => $cantidadSugerida, 'razon' => "Rebalanceo: {$menor->nombre} bajo mínimo", 'stock_origen' => $mayor->pivot->stock, 'stock_destino' => $menor->pivot->stock, ]; } } } } return collect($sugerencias)->take(10)->toArray(); } public function ejecutarTransferenciaSugerida($data) { try { $service = new TransferenciaBodegaService(); $transferencia = $service->transferir( $data['producto_id'], $data['bodega_origen_id'], $data['bodega_destino_id'], $data['cantidad_sugerida'], $data['razon'] . ' (Transferencia automática sugerida)' ); Notification::make() ->title('Transferencia ejecutada') ->body("Se transfirieron {$data['cantidad_sugerida']} unidades de {$data['producto_nombre']}") ->success() ->send(); // Recargar datos $this->mount(); } catch (\Exception $e) { Notification::make() ->title('Error en transferencia') ->body($e->getMessage()) ->danger() ->send(); } } }