153 lines
5.6 KiB
PHP
153 lines
5.6 KiB
PHP
<?php
|
|
|
|
namespace App\Filament\Pages;
|
|
|
|
use App\Models\Producto;
|
|
use App\Models\Bodega;
|
|
use Filament\Pages\Page;
|
|
use App\Services\TransferenciaBodegaService;
|
|
use Filament\Notifications\Notification;
|
|
|
|
class GestionStockPage extends Page
|
|
{
|
|
protected static ?string $navigationIcon = 'heroicon-o-chart-bar-square';
|
|
|
|
protected static ?string $navigationLabel = 'Gestión de Stock';
|
|
|
|
protected static ?string $navigationGroup = 'Inventario';
|
|
|
|
protected static string $view = 'filament.pages.gestion-stock';
|
|
|
|
public $productosStockBajo;
|
|
public $bodegasEstado;
|
|
public $sugerenciasTransferencia;
|
|
|
|
public function mount(): void
|
|
{
|
|
// Datos para la vista
|
|
$this->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' => $producto->bodegas->map(function ($bodega) {
|
|
return [
|
|
'bodega' => $bodega->nombre,
|
|
'stock' => $bodega->pivot->stock,
|
|
];
|
|
})->toArray(),
|
|
];
|
|
})
|
|
->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();
|
|
}
|
|
}
|
|
} |