270 lines
12 KiB
PHP
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'),
|
|
]),
|
|
]);
|
|
}
|
|
}
|