label('Marcar como Recibida') ->icon('heroicon-o-check-circle') ->color('success') ->visible(fn() => $this->record->estado === 'Pendiente') ->requiresConfirmation() ->modalHeading('Marcar compra como recibida') ->modalDescription('Esto actualizará el stock en las bodegas según los productos de esta compra.') ->action(function () { $this->record->update(['estado' => 'Recibida']); Notification::make() ->success() ->title('Compra recibida') ->body('El stock ha sido actualizado en las bodegas.') ->send(); $this->redirect(static::getResource()::getUrl('edit', ['record' => $this->record])); }), Actions\Action::make('descargar_plantilla') ->label('Descargar Plantilla Excel') ->icon('heroicon-o-document-arrow-down') ->color('info') ->action(function () { return Excel::download( new PlantillaCompraExport(), 'plantilla_compras.xlsx' ); }), Actions\Action::make('importar_excel') ->label('Importar desde Excel') ->icon('heroicon-o-arrow-down-tray') ->color('success') ->form([ FileUpload::make('archivo') ->label('Archivo Excel') ->acceptedFileTypes([ 'application/vnd.ms-excel', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', 'text/csv', ]) ->required() ->helperText('Formato: Código de Barras, Producto, Cantidad, Precio Unitario, Bodega (opcional), Observaciones (opcional)') ->disk('local') ->directory('temp-imports'), ]) ->action(function (array $data) { try { $filePath = Storage::disk('local')->path($data['archivo']); $import = new CompraDetallesImport(); Excel::import($import, $filePath); $previewData = $import->getPreviewData(); $stats = $import->getStats(); // Si hay errores, mostrarlos if ($stats['invalid'] > 0) { $errorMessages = collect($previewData) ->filter(fn($item) => !$item['valid']) ->map(fn($item) => "Fila {$item['row_number']}: " . implode(', ', $item['errors'])) ->take(10) ->implode("\n"); Notification::make() ->warning() ->title("Se encontraron {$stats['invalid']} filas con errores") ->body($errorMessages) ->persistent() ->send(); } // Obtener detalles válidos para agregar $detallesValidos = $import->getDetallesForSave(); if (count($detallesValidos) > 0) { // Agregar los detalles a la compra foreach ($detallesValidos as $detalle) { $this->record->detalles()->create($detalle); } // Recalcular el total $nuevoTotal = $this->record->detalles()->sum('subtotal'); $this->record->update(['total' => $nuevoTotal]); $mensaje = "{$stats['valid']} productos agregados. Total: $" . number_format($stats['total_amount'], 2); if ($stats['productos_creados'] > 0) { $mensaje .= "\n✨ {$stats['productos_creados']} productos nuevos creados automáticamente"; } Notification::make() ->success() ->title('Importación exitosa') ->body($mensaje) ->send(); // Si la compra está en estado Pendiente, preguntar si desea recibirla if ($this->record->estado === 'Pendiente') { Notification::make() ->info() ->title('Actualizar stock') ->body('⚠️ La compra está en estado "Pendiente". Cambia el estado a "Recibida" para actualizar el stock en las bodegas.') ->persistent() ->send(); } // Refrescar la página $this->redirect(static::getResource()::getUrl('edit', ['record' => $this->record])); } else { Notification::make() ->warning() ->title('No hay datos válidos para importar') ->send(); } // Limpiar archivo temporal Storage::disk('local')->delete($data['archivo']); } catch (\Exception $e) { Notification::make() ->danger() ->title('Error en la importación') ->body($e->getMessage()) ->send(); } }), Actions\DeleteAction::make(), ]; } protected function mutateFormDataBeforeSave(array $data): array { // Calcular el total basado en los detalles if (isset($data['detalles']) && is_array($data['detalles'])) { $total = collect($data['detalles'])->sum('subtotal'); $data['total'] = $total; // Agregar snapshots para cada detalle foreach ($data['detalles'] as &$detalle) { if (isset($detalle['producto_id'])) { $producto = Producto::find($detalle['producto_id']); $detalle['producto_nombre_snapshot'] = $producto?->nombre; } if (isset($detalle['variante_id'])) { $variante = ProductVariant::find($detalle['variante_id']); if ($variante) { $colorName = $variante->color?->name ?? 'Sin color'; $sizeName = $variante->size?->name ?? 'Sin talla'; $detalle['variante_info_snapshot'] = "$colorName / $sizeName"; } } } } else { $data['total'] = 0; } return $data; } }