Files
pos_heidiver/app/Filament/Resources/ProductVariantResource/RelationManagers/BodegasRelationManager.php
T
2026-01-06 15:35:59 -05:00

270 lines
12 KiB
PHP

<?php
namespace App\Filament\Resources\ProductVariantResource\RelationManagers;
use App\Models\Bodega;
use App\Models\ProductVariant;
use App\Services\TransferenciaBodegaService;
use Filament\Forms;
use Filament\Forms\Form;
use Filament\Resources\RelationManagers\RelationManager;
use Filament\Tables;
use Filament\Tables\Table;
use Filament\Notifications\Notification;
class BodegasRelationManager extends RelationManager
{
protected static string $relationship = 'bodegas';
protected static ?string $title = 'Stock por Bodega';
protected static ?string $modelLabel = 'Bodega';
protected static ?string $pluralModelLabel = 'Bodegas';
public function form(Form $form): Form
{
return $form
->schema([
Forms\Components\Select::make('bodega_id')
->label('Bodega')
->options(\App\Models\Bodega::pluck('nombre', 'id'))
->required()
->searchable()
->preload(),
Forms\Components\TextInput::make('stock')
->label('Stock en esta Bodega')
->numeric()
->required()
->default(0)
->minValue(0)
->helperText('Cantidad de esta variante en la bodega seleccionada'),
]);
}
public function table(Table $table): Table
{
return $table
->recordTitleAttribute('nombre')
->columns([
Tables\Columns\TextColumn::make('nombre')
->label('Bodega')
->searchable()
->sortable(),
Tables\Columns\TextColumn::make('pivot.stock')
->label('Stock')
->alignCenter()
->sortable()
->formatStateUsing(function ($state) {
$estado = $state > 0 ? '✅' : '⚠️';
return "{$estado} {$state} unidades";
})
->tooltip(function ($state) {
return "Stock en unidades: {$state}";
}),
Tables\Columns\TextColumn::make('pivot.updated_at')
->label('Última Actualización')
->dateTime('d/m/Y H:i')
->sortable()
->toggleable(isToggledHiddenByDefault: true),
])
->filters([
Tables\Filters\Filter::make('con_stock')
->label('Solo con Stock')
->query(fn($query) => $query->where('variante_bodega.stock', '>', 0)),
])
->headerActions([
Tables\Actions\CreateAction::make()
->label('Asignar a Bodega')
->modalHeading('Asignar Variante a Bodega')
->action(function (array $data): void {
$variante = $this->getOwnerRecord();
$bodegaId = $data['bodega_id'];
$stock = $data['stock'];
// Verificar si ya existe la relación
if ($variante->bodegas()->where('bodega_id', $bodegaId)->exists()) {
Notification::make()
->warning()
->title('Variante ya asignada')
->body('Esta variante ya está asignada a esa bodega. Use la opción de editar.')
->send();
return;
}
// Crear la relación
$variante->bodegas()->attach($bodegaId, ['stock' => $stock]);
Notification::make()
->success()
->title('Variante asignada')
->body('La variante ha sido asignada a la bodega correctamente.')
->send();
}),
Tables\Actions\Action::make('transferir_stock')
->label('Transferir entre Bodegas')
->icon('heroicon-o-arrows-right-left')
->color('info')
->form([
Forms\Components\Placeholder::make('info')
->label('Información de la Variante')
->content(function () {
$variante = $this->getOwnerRecord();
return "Variante: {$variante->sku} | Stock Total: {$variante->getStockEfectivo()} unidades";
}),
Forms\Components\Select::make('bodega_origen_id')
->label('Bodega Origen')
->options(function () {
$variante = $this->getOwnerRecord();
return $variante->bodegas()
->where('variante_bodega.stock', '>', 0)
->pluck('nombre', 'bodegas.id');
})
->required()
->searchable()
->reactive()
->afterStateUpdated(function ($set, $get, $state) {
if ($state) {
$variante = $this->getOwnerRecord();
$stock = $variante->bodegas()
->where('bodegas.id', $state)
->first()?->pivot?->stock ?? 0;
$set('stock_disponible', $stock);
}
}),
Forms\Components\Placeholder::make('stock_disponible')
->label('Stock Disponible en Origen')
->content(fn($get) => ($get('stock_disponible') ?? 0) . ' unidades'),
Forms\Components\Select::make('bodega_destino_id')
->label('Bodega Destino')
->options(Bodega::pluck('nombre', 'id'))
->required()
->searchable()
->different('bodega_origen_id'),
Forms\Components\TextInput::make('cantidad')
->label('Cantidad a Transferir')
->numeric()
->required()
->minValue(1)
->maxValue(function ($get) {
return $get('stock_disponible') ?? 0;
}),
Forms\Components\TextInput::make('motivo')
->label('Motivo de la Transferencia')
->placeholder('Ej: Reposición, reorganización de inventario...')
->maxLength(255),
])
->action(function (array $data): void {
$variante = $this->getOwnerRecord();
$service = new TransferenciaBodegaService();
try {
$transferencia = $service->transferirVariante(
$variante->id,
$data['bodega_origen_id'],
$data['bodega_destino_id'],
$data['cantidad'],
$data['motivo'] ?? null
);
Notification::make()
->success()
->title('Transferencia Exitosa')
->body("Se transfirieron {$data['cantidad']} unidades entre bodegas.")
->send();
} catch (\Exception $e) {
Notification::make()
->danger()
->title('Error en Transferencia')
->body($e->getMessage())
->send();
}
}),
])
->actions([
Tables\Actions\EditAction::make()
->label('Editar Stock')
->form([
Forms\Components\TextInput::make('stock')
->label('Stock en esta Bodega')
->numeric()
->required()
->minValue(0)
->helperText('Cantidad de esta variante en la bodega'),
])
->fillForm(function ($record): array {
return [
'stock' => $record->pivot?->stock ?? 0,
];
})
->using(function (array $data, $record): void {
// Actualizamos el pivot directamente
$record->pivot->update(['stock' => (int)$data['stock']]);
// Notificación
Notification::make()
->success()
->title('Stock Actualizado')
->body("El stock se actualizó a {$data['stock']} unidades.")
->send();
}),
Tables\Actions\Action::make('vaciar_stock')
->label('Vaciar Stock')
->icon('heroicon-o-minus-circle')
->color('warning')
->requiresConfirmation()
->modalHeading('Vaciar Stock de Bodega')
->modalDescription('¿Estás seguro de que quieres poner el stock en 0 para esta bodega? La bodega seguirá asignada a la variante.')
->modalSubmitActionLabel('Sí, vaciar stock')
->action(function ($record): void {
$record->pivot->update(['stock' => 0]);
Notification::make()
->success()
->title('Stock Vaciado')
->body('El stock de la bodega ha sido puesto en 0.')
->send();
}),
])
->bulkActions([
Tables\Actions\BulkActionGroup::make([
Tables\Actions\BulkAction::make('vaciar_stock_multiple')
->label('Vaciar Stock de Seleccionadas')
->icon('heroicon-o-minus-circle')
->color('warning')
->requiresConfirmation()
->modalHeading('Vaciar Stock de Bodegas Seleccionadas')
->modalDescription('¿Estás seguro de que quieres poner el stock en 0 para todas las bodegas seleccionadas?')
->action(function ($records): void {
foreach ($records as $record) {
$record->pivot->update(['stock' => 0]);
}
Notification::make()
->success()
->title('Stock Vaciado')
->body('El stock de las bodegas seleccionadas ha sido puesto en 0.')
->send();
}),
Tables\Actions\DeleteBulkAction::make()
->label('Quitar de Bodegas Seleccionadas')
->requiresConfirmation()
->modalHeading('Quitar Variante de Bodegas')
->modalDescription('¿Estás seguro de que quieres quitar completamente esta variante de las bodegas seleccionadas? Se perderá toda la información de stock.')
->modalSubmitActionLabel('Sí, quitar completamente'),
]),
]);
}
}