up
This commit is contained in:
@@ -0,0 +1,153 @@
|
||||
<?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();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user