364 lines
18 KiB
PHP
364 lines
18 KiB
PHP
<?php
|
|
|
|
namespace App\Filament\Resources;
|
|
|
|
use App\Filament\Resources\TransferenciaBodegaResource\Pages;
|
|
use App\Models\TransferenciaBodega;
|
|
use App\Models\Producto;
|
|
use App\Models\ProductVariant;
|
|
use App\Models\Bodega;
|
|
use App\Services\TransferenciaBodegaService;
|
|
use Illuminate\Support\Facades\Auth;
|
|
use Filament\Forms;
|
|
use Filament\Forms\Form;
|
|
use Filament\Resources\Resource;
|
|
use Filament\Tables;
|
|
use Filament\Tables\Table;
|
|
use Filament\Notifications\Notification;
|
|
use Filament\Tables\Filters\SelectFilter;
|
|
use Illuminate\Database\Eloquent\Builder;
|
|
|
|
class TransferenciaBodegaResource extends Resource
|
|
{
|
|
protected static ?string $model = TransferenciaBodega::class;
|
|
|
|
protected static ?string $navigationIcon = 'heroicon-o-arrows-right-left';
|
|
|
|
protected static ?string $navigationGroup = 'Inventario';
|
|
|
|
protected static ?string $navigationLabel = 'Transferencias de Stock';
|
|
|
|
protected static ?string $modelLabel = 'Transferencia';
|
|
|
|
protected static ?string $pluralModelLabel = 'Transferencias';
|
|
|
|
public static function form(Form $form): Form
|
|
{
|
|
return $form
|
|
->schema([
|
|
Forms\Components\Section::make('Información del Producto')
|
|
->schema([
|
|
Forms\Components\Select::make('producto_id')
|
|
->label('Producto')
|
|
->relationship('producto', 'nombre')
|
|
->searchable()
|
|
->preload()
|
|
->required()
|
|
->reactive()
|
|
->afterStateUpdated(function ($set, $get, $state) {
|
|
if ($state) {
|
|
$producto = Producto::find($state);
|
|
if ($producto && $producto->variants()->exists()) {
|
|
$set('tiene_variantes', true);
|
|
$set('variante_id', null); // Limpiar selección anterior
|
|
$set('stock_disponible_info', null);
|
|
// Mostrar información sobre las variantes
|
|
$cantidadVariantes = $producto->variants()->count();
|
|
$set('info_variantes', "Este producto tiene {$cantidadVariantes} variante(s). Debe seleccionar una variante específica para la transferencia.");
|
|
} else {
|
|
$set('tiene_variantes', false);
|
|
$set('variante_id', null);
|
|
$set('info_variantes', null);
|
|
// Mostrar stock disponible del producto
|
|
$stockTotal = $producto->getStockEfectivo();
|
|
$set('stock_disponible_info', "Stock total del producto: {$stockTotal} unidades");
|
|
}
|
|
} else {
|
|
$set('tiene_variantes', false);
|
|
$set('variante_id', null);
|
|
$set('info_variantes', null);
|
|
$set('stock_disponible_info', null);
|
|
}
|
|
}),
|
|
|
|
Forms\Components\Placeholder::make('info_variantes')
|
|
->label('Información del Producto')
|
|
->content(fn ($get) => $get('info_variantes') ?? '')
|
|
->visible(fn ($get) => $get('tiene_variantes')),
|
|
|
|
Forms\Components\Placeholder::make('stock_disponible_info')
|
|
->label('Stock Disponible')
|
|
->content(fn ($get) => $get('stock_disponible_info') ?? '')
|
|
->visible(fn ($get) => !$get('tiene_variantes') && $get('stock_disponible_info')),
|
|
|
|
Forms\Components\Select::make('variante_id')
|
|
->label('Seleccionar Variante')
|
|
->options(function ($get) {
|
|
$productoId = $get('producto_id');
|
|
if ($productoId) {
|
|
return ProductVariant::where('producto_id', $productoId)
|
|
->with(['color', 'size'])
|
|
->get()
|
|
->mapWithKeys(function ($variante) {
|
|
$colorName = $variante->color->name ?? '';
|
|
$sizeName = $variante->size->name ?? '';
|
|
$stockEfectivo = $variante->getStockEfectivo();
|
|
$displayName = "{$variante->sku} - {$colorName} {$sizeName} (Stock: {$stockEfectivo})";
|
|
return [$variante->id => $displayName];
|
|
});
|
|
}
|
|
return [];
|
|
})
|
|
->searchable()
|
|
->required(fn ($get) => $get('tiene_variantes'))
|
|
->visible(fn ($get) => $get('tiene_variantes'))
|
|
->reactive()
|
|
->afterStateUpdated(function ($set, $get, $state) {
|
|
if ($state) {
|
|
$variante = ProductVariant::find($state);
|
|
if ($variante) {
|
|
$stockTotal = $variante->getStockEfectivo();
|
|
$set('stock_variante_info', "Stock total de esta variante: {$stockTotal} unidades");
|
|
}
|
|
} else {
|
|
$set('stock_variante_info', null);
|
|
}
|
|
})
|
|
->helperText('Seleccione la variante específica que desea transferir'),
|
|
|
|
Forms\Components\Placeholder::make('stock_variante_info')
|
|
->label('Stock de la Variante')
|
|
->content(fn ($get) => $get('stock_variante_info') ?? '')
|
|
->visible(fn ($get) => $get('tiene_variantes') && $get('variante_id') && $get('stock_variante_info')),
|
|
])
|
|
->columns(2),
|
|
|
|
Forms\Components\Section::make('Detalles de la Transferencia')
|
|
->schema([
|
|
Forms\Components\Select::make('bodega_origen_id')
|
|
->label('Bodega Origen')
|
|
->options(function ($get) {
|
|
$productoId = $get('producto_id');
|
|
$varianteId = $get('variante_id');
|
|
|
|
if ($varianteId) {
|
|
// Si hay variante seleccionada, mostrar solo bodegas con stock de esa variante
|
|
$variante = ProductVariant::find($varianteId);
|
|
if ($variante) {
|
|
return $variante->bodegas()
|
|
->where('variante_bodega.stock', '>', 0)
|
|
->get()
|
|
->mapWithKeys(function ($bodega) {
|
|
return [$bodega->id => "{$bodega->nombre} (Stock: {$bodega->pivot->stock})"];
|
|
});
|
|
}
|
|
} elseif ($productoId) {
|
|
// Si hay producto sin variantes, mostrar bodegas con stock del producto
|
|
$producto = Producto::find($productoId);
|
|
if ($producto && !$producto->variants()->exists()) {
|
|
return $producto->bodegas()
|
|
->where('producto_bodega.stock', '>', 0)
|
|
->get()
|
|
->mapWithKeys(function ($bodega) {
|
|
return [$bodega->id => "{$bodega->nombre} (Stock: {$bodega->pivot->stock})"];
|
|
});
|
|
}
|
|
}
|
|
|
|
// Fallback: mostrar todas las bodegas
|
|
return Bodega::pluck('nombre', 'id');
|
|
})
|
|
->searchable()
|
|
->required()
|
|
->reactive()
|
|
->afterStateUpdated(function ($set, $get, $state) {
|
|
$set('stock_origen_disponible', null);
|
|
if ($state) {
|
|
$productoId = $get('producto_id');
|
|
$varianteId = $get('variante_id');
|
|
|
|
if ($varianteId) {
|
|
$variante = ProductVariant::find($varianteId);
|
|
$stock = $variante->bodegas()
|
|
->where('bodega_id', $state)
|
|
->first()?->pivot?->stock ?? 0;
|
|
} elseif ($productoId) {
|
|
$producto = Producto::find($productoId);
|
|
$stock = $producto->bodegas()
|
|
->where('bodega_id', $state)
|
|
->first()?->pivot?->stock ?? 0;
|
|
} else {
|
|
$stock = 0;
|
|
}
|
|
|
|
$bodegaNombre = Bodega::find($state)?->nombre ?? '';
|
|
$set('stock_origen_disponible', "Stock disponible en {$bodegaNombre}: {$stock} unidades");
|
|
$set('max_cantidad', $stock);
|
|
}
|
|
})
|
|
->helperText('Solo se muestran bodegas con stock disponible'),
|
|
|
|
Forms\Components\Placeholder::make('stock_origen_disponible')
|
|
->label('Stock en Bodega Origen')
|
|
->content(fn ($get) => $get('stock_origen_disponible') ?? '')
|
|
->visible(fn ($get) => $get('stock_origen_disponible')),
|
|
|
|
Forms\Components\Select::make('bodega_destino_id')
|
|
->label('Bodega Destino')
|
|
->relationship('bodegaDestino', 'nombre')
|
|
->searchable()
|
|
->required()
|
|
->different('bodega_origen_id'),
|
|
|
|
Forms\Components\TextInput::make('cantidad')
|
|
->label('Cantidad a Transferir')
|
|
->numeric()
|
|
->required()
|
|
->minValue(1)
|
|
->maxValue(fn ($get) => $get('max_cantidad') ?? 999999)
|
|
->helperText(fn ($get) =>
|
|
$get('max_cantidad') ? "Máximo disponible: {$get('max_cantidad')} unidades" : ''
|
|
),
|
|
|
|
Forms\Components\Textarea::make('motivo')
|
|
->label('Motivo de la Transferencia')
|
|
->placeholder('Ej: Reposición de sucursal, reorganización de inventario...')
|
|
->rows(3)
|
|
->columnSpanFull(),
|
|
])
|
|
->columns(2),
|
|
|
|
Forms\Components\Hidden::make('usuario_id')
|
|
->default(Auth::id()),
|
|
|
|
Forms\Components\Hidden::make('fecha_transferencia')
|
|
->default(now()),
|
|
|
|
Forms\Components\Hidden::make('tiene_variantes')
|
|
->default(false),
|
|
|
|
Forms\Components\Hidden::make('max_cantidad')
|
|
->default(0),
|
|
|
|
Forms\Components\Hidden::make('info_variantes'),
|
|
Forms\Components\Hidden::make('stock_disponible_info'),
|
|
Forms\Components\Hidden::make('stock_variante_info'),
|
|
Forms\Components\Hidden::make('stock_origen_disponible'),
|
|
]);
|
|
}
|
|
|
|
public static function table(Table $table): Table
|
|
{
|
|
return $table
|
|
->columns([
|
|
Tables\Columns\TextColumn::make('producto.nombre')
|
|
->label('Producto')
|
|
->searchable()
|
|
->sortable()
|
|
->wrap(),
|
|
|
|
Tables\Columns\TextColumn::make('variante_info')
|
|
->label('Variante')
|
|
->getStateUsing(function ($record) {
|
|
if ($record->variante_id) {
|
|
$variante = ProductVariant::find($record->variante_id);
|
|
if ($variante) {
|
|
$colorName = $variante->color->name ?? '';
|
|
$sizeName = $variante->size->name ?? '';
|
|
return "{$variante->sku} - {$colorName} {$sizeName}";
|
|
}
|
|
}
|
|
return 'Producto general';
|
|
})
|
|
->badge()
|
|
->color(fn ($state) => $state === 'Producto general' ? 'info' : 'warning')
|
|
->searchable(query: function (Builder $query, string $search): Builder {
|
|
return $query->whereHas('producto.variants', function ($q) use ($search) {
|
|
$q->where('sku', 'like', "%{$search}%");
|
|
});
|
|
}),
|
|
|
|
Tables\Columns\TextColumn::make('bodegaOrigen.nombre')
|
|
->label('Bodega Origen')
|
|
->badge()
|
|
->color('danger')
|
|
->sortable(),
|
|
|
|
Tables\Columns\TextColumn::make('bodegaDestino.nombre')
|
|
->label('Bodega Destino')
|
|
->badge()
|
|
->color('success')
|
|
->sortable(),
|
|
|
|
Tables\Columns\TextColumn::make('cantidad')
|
|
->label('Cantidad')
|
|
->numeric()
|
|
->sortable()
|
|
->badge()
|
|
->color('primary'),
|
|
|
|
Tables\Columns\TextColumn::make('motivo')
|
|
->label('Motivo')
|
|
->limit(30)
|
|
->tooltip(function ($record) {
|
|
return $record->motivo;
|
|
})
|
|
->wrap(),
|
|
|
|
Tables\Columns\TextColumn::make('usuario.name')
|
|
->label('Usuario')
|
|
->sortable()
|
|
->toggleable(isToggledHiddenByDefault: true),
|
|
|
|
Tables\Columns\TextColumn::make('fecha_transferencia')
|
|
->label('Fecha')
|
|
->dateTime('d/m/Y H:i')
|
|
->sortable()
|
|
->badge()
|
|
->color('gray'),
|
|
|
|
Tables\Columns\TextColumn::make('created_at')
|
|
->label('Creado')
|
|
->dateTime('d/m/Y H:i')
|
|
->sortable()
|
|
->toggleable(isToggledHiddenByDefault: true),
|
|
])
|
|
->filters([
|
|
SelectFilter::make('bodega_origen_id')
|
|
->label('Bodega Origen')
|
|
->relationship('bodegaOrigen', 'nombre'),
|
|
|
|
SelectFilter::make('bodega_destino_id')
|
|
->label('Bodega Destino')
|
|
->relationship('bodegaDestino', 'nombre'),
|
|
|
|
SelectFilter::make('producto_id')
|
|
->label('Producto')
|
|
->relationship('producto', 'nombre')
|
|
->searchable(),
|
|
|
|
Tables\Filters\Filter::make('con_variantes')
|
|
->label('Solo Variantes')
|
|
->query(fn (Builder $query): Builder => $query->whereNotNull('variante_id')),
|
|
|
|
Tables\Filters\Filter::make('sin_variantes')
|
|
->label('Solo Productos Generales')
|
|
->query(fn (Builder $query): Builder => $query->whereNull('variante_id')),
|
|
])
|
|
->actions([
|
|
Tables\Actions\EditAction::make(),
|
|
])
|
|
->bulkActions([
|
|
Tables\Actions\BulkActionGroup::make([
|
|
Tables\Actions\DeleteBulkAction::make(),
|
|
]),
|
|
]);
|
|
}
|
|
|
|
public static function getRelations(): array
|
|
{
|
|
return [
|
|
//
|
|
];
|
|
}
|
|
|
|
public static function getPages(): array
|
|
{
|
|
return [
|
|
'index' => Pages\ListTransferenciaBodegas::route('/'),
|
|
'create' => Pages\CreateTransferenciaBodega::route('/create'),
|
|
'edit' => Pages\EditTransferenciaBodega::route('/{record}/edit'),
|
|
];
|
|
}
|
|
}
|