schema([ TextInput::make('codigo_escaneado') ->label('Escanear código de barras') ->live() ->afterStateUpdated(function (callable $set, $state) { // Buscar en ProductVariant $variant = ProductVariant::where('barcode', $state)->first(); if ($variant) { $set('producto_id', $variant->producto_id); $set('variante_id', $variant->id); // Validación defensiva para evitar error "name" on null $colorName = $variant->color ? $variant->color->name : 'Sin color'; $sizeName = $variant->size ? $variant->size->name : 'Sin talla'; Notification::make() ->success() ->title('Variante encontrada') ->body("Producto: {$variant->producto->nombre} - Variante: {$colorName}/{$sizeName}") ->send(); return; } // Buscar en Producto $producto = Producto::where('codigo_barras', $state)->first(); if ($producto) { $set('producto_id', $producto->id); $set('variante_id', null); if ($producto->variants()->exists()) { Notification::make() ->warning() ->title('Producto con variantes') ->body('Este producto tiene variantes. Por favor selecciona una variante específica.') ->send(); } else { Notification::make() ->success() ->title('Producto encontrado') ->body("Producto: {$producto->nombre}") ->send(); } } else { Notification::make() ->warning() ->title('Código no encontrado') ->body('No se encontró ningún producto o variante con este código.') ->send(); } }), Select::make('bodega_id') ->label('Bodega de Destino') ->relationship('bodega', 'nombre') ->searchable() ->required() ->default(function () { // Buscar bodega "Principal" como default $bodegaPrincipal = Bodega::where('nombre', 'Principal')->first(); return $bodegaPrincipal ? $bodegaPrincipal->id : null; }) ->helperText('Selecciona la bodega donde se almacenará este producto'), Select::make('producto_id') ->label('Producto') ->relationship('producto', 'nombre') ->searchable() ->required() ->live() ->afterStateUpdated(function (callable $set, $state) { // Limpiar variante cuando cambia el producto $set('variante_id', null); if ($state) { $producto = Producto::find($state); if ($producto && $producto->variants()->exists()) { Notification::make() ->info() ->title('Producto con variantes') ->body('Este producto tiene variantes disponibles. Por favor selecciona una.') ->send(); } } }), Select::make('variante_id') ->label('Variante') ->options(function (callable $get) { $productoId = $get('producto_id'); if (!$productoId) { return []; } $producto = Producto::find($productoId); if (!$producto || !$producto->variants()->exists()) { return []; } return ProductVariant::where('producto_id', $productoId) ->with(['color', 'size']) ->get() ->mapWithKeys(function ($variant) { // Validación defensiva para evitar error "name" on null $colorName = $variant->color ? $variant->color->name : 'Sin color'; $sizeName = $variant->size ? $variant->size->name : 'Sin talla'; return [ $variant->id => "{$colorName} / {$sizeName}", ]; }); }) ->searchable() ->live() ->visible(function (callable $get) { $productoId = $get('producto_id'); if (!$productoId) return false; $producto = Producto::find($productoId); return $producto && $producto->variants()->exists(); }) ->required(function (callable $get) { $productoId = $get('producto_id'); if (!$productoId) return false; $producto = Producto::find($productoId); return $producto && $producto->variants()->exists(); }) ->helperText(function (callable $get) { $productoId = $get('producto_id'); if (!$productoId) return null; $producto = Producto::find($productoId); if (!$producto || !$producto->variants()->exists()) { return null; } return 'Este producto requiere seleccionar una variante específica.'; }), TextInput::make('cantidad') ->required() ->numeric() ->live() ->helperText('Cantidad en unidades individuales') ->afterStateUpdated(function (callable $set, callable $get) { $cantidad = (float) $get('cantidad'); $precio = (float) $get('precio_unitario'); $set('subtotal', $cantidad * $precio); }), TextInput::make('precio_unitario') ->required() ->numeric() ->prefix('$') ->live() ->helperText('Precio por unidad individual') ->afterStateUpdated(function (callable $set, callable $get) { $cantidad = (float) $get('cantidad'); $precio = (float) $get('precio_unitario'); $set('subtotal', $cantidad * $precio); }), TextInput::make('subtotal') ->numeric() ->prefix('$') ->disabled() ->dehydrated(true), // guarda el valor aunque esté deshabilitado TextInput::make('DetalleCompra') ->label('Detalle/Observaciones') ->maxLength(255) ->helperText('Información adicional sobre esta compra (opcional)'), ]); } public function table(Table $table): Table { return $table ->recordTitleAttribute('DetalleCompra') ->columns([ Tables\Columns\TextColumn::make('bodega.nombre') ->label('Bodega') ->badge() ->color('primary'), Tables\Columns\TextColumn::make('producto_nombre') ->label('Producto') ->searchable() ->sortable() ->color(fn($record) => $record->producto_id ? 'primary' : 'warning') ->icon(fn($record) => $record->producto_id ? null : 'heroicon-o-archive-box-x-mark') ->tooltip(fn($record) => $record->producto_id ? null : 'Producto eliminado - información histórica'), Tables\Columns\TextColumn::make('variante_info') ->label('Variante') ->badge() ->color(function ($record) { if ($record->variante_id) return 'success'; if ($record->variante_info_snapshot) return 'warning'; return 'gray'; }) ->formatStateUsing(fn($record) => $record->variante_info ?: '—') ->tooltip(function ($record) { if ($record->variante_id) return null; if ($record->variante_info_snapshot) return 'Variante eliminada - información histórica'; return 'Sin variante'; }), Tables\Columns\TextColumn::make('cantidad') ->label('Cantidad') ->alignEnd(), Tables\Columns\TextColumn::make('precio_unitario') ->label('Precio Unitario') ->money('cop') ->alignEnd(), Tables\Columns\TextColumn::make('subtotal') ->label('Subtotal') ->money('cop') ->alignEnd() ->weight('bold') ->color('primary'), Tables\Columns\TextColumn::make('DetalleCompra') ->label('Detalle') ->limit(30) ->toggleable(isToggledHiddenByDefault: true), ]) ->filters([ // ]) ->headerActions([ Tables\Actions\CreateAction::make(), ]) ->actions([ Tables\Actions\EditAction::make(), Tables\Actions\DeleteAction::make(), ]) ->bulkActions([ Tables\Actions\BulkActionGroup::make([ Tables\Actions\DeleteBulkAction::make(), ]), ]); } }