import React, { useEffect, useState, useRef } from 'react'; // Animación fade-in/slide-up para bloques const fadeInSlideUp = `@keyframes fadein-slideup { 0% { opacity: 0; transform: translateY(40px); } 100% { opacity: 1; transform: translateY(0); } }`; if (typeof document !== 'undefined' && !document.getElementById('fadein-slideup-pedimento')) { const style = document.createElement('style'); style.id = 'fadein-slideup-pedimento'; style.innerHTML = fadeInSlideUp; document.head.appendChild(style); } import hljs from 'highlight.js/lib/core'; import xml from 'highlight.js/lib/languages/xml'; import 'highlight.js/styles/github.css'; hljs.registerLanguage('xml', xml); import { fetchPedimentoDocuments } from '../api/pedimentoDocuments'; import { fetchWithAuth, postWithAuth, putWithAuth } from '../fetchWithAuth'; import { fetchTasks } from '../api/procesos.ts'; import { fetchPedimentoCoves, downloadCove, downloadAcuseCove } from '../api/coves'; import { fetchPedimentoEdocuments, downloadEdocument, downloadAcuseEdocument } from '../api/edocuments'; import { getTaskStatusLabel, getTaskStatusColor, isTaskActionable, isTaskFinal } from '../api/taskStatus'; import { useParams, Link } from 'react-router-dom'; import { useNotification } from '../context/NotificationContext'; import { downloadFile, downloadBulkZip } from '../utils/downloadUtils'; const API_URL = import.meta.env.VITE_EFC_API_URL; const MICROSERVICE_URL = import.meta.env.VITE_EFC_MICROSERVICE_URL; const MICROSERVICE_URL_2 = import.meta.env.VITE_EFC_MICROSERVICE_URL_2; // Funciones auxiliares para estados y servicios (copiadas de procesos.js) const getEstadoLabel = (estado) => { const estados = { 1: 'Pendiente', 2: 'En Proceso', 3: 'Completado', 4: 'Error', 5: 'Cancelado' }; return estados[estado] || `Estado ${estado}`; }; const getEstadoColor = (estado) => { const colores = { 1: 'bg-yellow-100 text-yellow-800', 2: 'bg-blue-100 text-blue-800', 3: 'bg-green-100 text-green-800', 4: 'bg-red-100 text-red-800', 5: 'bg-gray-100 text-gray-800' }; return colores[estado] || 'bg-gray-100 text-gray-800'; }; const getServicioLabel = (servicio) => { const servicios = { 1: 'Estado de Pedimento', 3: 'Pedimento Completo', 4: 'Partidas', 5: 'Remesa', 6: 'Acuse ', 7: 'EDocuments', 8: 'Acuse de Cove', 9: 'Cove' }; return servicios[servicio] || `Servicio ${servicio}`; }; const getServicioColor = (servicio) => { const colores = { 1: 'bg-purple-100 text-purple-800', 2: 'bg-indigo-100 text-indigo-800', 3: 'bg-blue-100 text-blue-800', 4: 'bg-cyan-100 text-cyan-800', 5: 'bg-teal-100 text-teal-800', 6: 'bg-green-100 text-green-800', 7: 'bg-yellow-100 text-yellow-800', 8: 'bg-orange-100 text-orange-800', 9: 'bg-pink-100 text-pink-800' }; return colores[servicio] || 'bg-gray-100 text-gray-800'; }; // Función para formatear XML (pretty print) function formatXml(xml) { const PADDING = ' '; const reg = /(>)(<)(\/*)/g; let formatted = ''; let pad = 0; xml = xml.replace(reg, '$1\r\n$2$3'); xml.split(/\r?\n/).forEach((node) => { let indent = 0; if (node.match(/.+<\/\w[^>]*>$/)) { indent = 0; } else if (node.match(/^<\/\w/)) { if (pad !== 0) pad -= 1; } else if (node.match(/^<\w[^>]*[^\/]>/)) { indent = 1; } formatted += PADDING.repeat(pad) + node + '\r\n'; pad += indent; }); return formatted.trim(); } export default function PedimentoDetail() { // Estados principales const [activeTab, setActiveTab] = useState('documentos'); const [pedimento, setPedimento] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(''); const { id } = useParams(); const { showMessage } = useNotification(); // Estados para documentos const [documents, setDocuments] = useState([]); const [docsCount, setDocsCount] = useState(0); const [docsLoading, setDocsLoading] = useState(true); const [docsError, setDocsError] = useState(''); const [docsNext, setDocsNext] = useState(null); const [docsPrev, setDocsPrev] = useState(null); const [page, setPage] = useState(1); const [pageSize, setPageSize] = useState(10); const [selected, setSelected] = useState([]); const [downloading, setDownloading] = useState(false); const [downloadingAll, setDownloadingAll] = useState(false); // Estados para selección múltiple con checkboxes const [selectedDocuments, setSelectedDocuments] = useState([]); const [isSelectAllDocs, setIsSelectAllDocs] = useState(false); const [showDeleteModal, setShowDeleteModal] = useState(false); const [dashboardSummary, setDashboardSummary] = useState(null); const [showFilters, setShowFilters] = useState(false); // Filtros simplificados de documentos (nuevo diseño) const [filters, setFilters] = useState({ name: '', document_type: '', extension: '', date: '', created_at__gte: '', created_at__lte: '', fuente: '', pedimento_numero: '' }); // Estados para filtros (legacy - para compatibilidad) const [documentTypeFilter, setDocumentTypeFilter] = useState(''); const [fileNameFilter, setFileNameFilter] = useState(''); const [extensionFilter, setExtensionFilter] = useState(''); const [dateFilter, setDateFilter] = useState(''); const [orderBy, setOrderBy] = useState(''); const [orderDir, setOrderDir] = useState('asc'); // Estados para COVEs const [coves, setCoves] = useState([]); const [covesCount, setCovesCount] = useState(0); const [covesLoading, setCovesLoading] = useState(false); const [covesError, setCovesError] = useState(''); const [covesPage, setCovesPage] = useState(1); const [covesPageSize, setCovesPageSize] = useState(10); const [covesFilters, setCovesFilters] = useState({ numero_cove: '', cove_descargado: '', acuse_cove_descargado: '', created_at__gte: '', created_at__lte: '' }); // Estados para EDocs const [edocs, setEdocs] = useState([]); const [edocsCount, setEdocsCount] = useState(0); const [edocsLoading, setEdocsLoading] = useState(false); const [edocsError, setEdocsError] = useState(''); const [edocsPage, setEdocsPage] = useState(1); const [edocsPageSize, setEdocsPageSize] = useState(10); const [edocsFilters, setEdocsFilters] = useState({ numero_edocument: '', clave: '', descripcion: '', edocument_descargado: '', acuse_descargado: '', created_at__gte: '', created_at__lte: '' }); // Estados para Partidas const [partidas, setPartidas] = useState([]); const [partidasCount, setPartidasCount] = useState(0); const [partidasLoading, setPartidasLoading] = useState(false); const [partidasError, setPartidasError] = useState(''); const [partidasPage, setPartidasPage] = useState(1); const [partidasPageSize, setPartidasPageSize] = useState(10); const [partidasFilters, setPartidasFilters] = useState({ numero_partida: '', descargado: '', numero_partida__gte: '', numero_partida__lte: '', created_at__gte: '', created_at__lte: '' }); const [selectedPartidas, setSelectedPartidas] = useState([]); const [downloadingPartidas, setDownloadingPartidas] = useState(false); const [downloadingAllPartidas, setDownloadingAllPartidas] = useState(false); // Estados para credenciales VUCEM const [credenciales, setCredenciales] = useState([]); const [processingPartida, setProcessingPartida] = useState(null); const [processingCove, setProcessingCove] = useState(null); const [processingAcuseCove, setProcessingAcuseCove] = useState(null); const [processingEdoc, setProcessingEdoc] = useState(null); const [processingAcuseEdoc, setProcessingAcuseEdoc] = useState(null); // Función para obtener el resumen del dashboard const fetchDashboardSummary = async () => { try { if (!pedimento) return; const url = `${import.meta.env.VITE_EFC_API_URL}/reports/dashboard/summary/?organizacion_id=${pedimento.organizacion}&pedimento_app=${pedimento.pedimento_app}`; const res = await fetchWithAuth(url); if (!res.ok) { throw new Error('Error al obtener el resumen del dashboard'); } const data = await res.json(); setDashboardSummary(data); } catch (err) { console.error('Error obteniendo el resumen del dashboard:', err); showMessage('Error al obtener el resumen del dashboard: ' + err.message, 'error'); } }; // Función para obtener credenciales VUCEM const fetchCredenciales = async (contribuyente) => { try { const response = await fetchWithAuth(`${API_URL}/vucem/vucem/?importador=${contribuyente}`); if (response.ok) { const data = await response.json(); // La API devuelve un array directamente, no un objeto con results const credenciales = Array.isArray(data) ? data : (data.results || []); return credenciales; } return []; } catch (error) { console.error('Error fetching credenciales:', error); return []; } }; // Función para procesar partida const handlePartidaProcess = async (partida) => { setProcessingPartida(partida.id); try { // Obtener credenciales para el contribuyente del pedimento const credencialesList = await fetchCredenciales(pedimento.contribuyente); if (credencialesList.length === 0) { showMessage('No se encontraron credenciales VUCEM para este contribuyente', 'error'); return; } // Usar la primera credencial activa disponible const credencial = credencialesList.find(c => c.is_active) || credencialesList[0]; const requestBody = { partida: { id: partida.id, numero: partida.numero_partida }, pedimento: { id: pedimento.id, pedimento: pedimento.pedimento, pedimento_app: pedimento.pedimento_app, aduana: pedimento.aduana, patente: pedimento.patente, organizacion: pedimento.organizacion, regimen: pedimento.regimen || "test", clave_pedimento: pedimento.clave_pedimento || "test", numero_operacion: pedimento.numero_operacion || "" }, credencial: { id: credencial.id, user: credencial.usuario, password: credencial.password, efirma: credencial.efirma, // Convertir URLs completas a rutas relativas key: credencial.key ? credencial.key.split('/').slice(-2).join('/') : '', cer: credencial.cer ? credencial.cer.split('/').slice(-2).join('/') : '', is_active: credencial.is_active, organizacion: credencial.organizacion } }; // Verificar si MICROSERVICE_URL_2 está definido if (!MICROSERVICE_URL_2) { throw new Error('La variable de entorno VITE_EFC_MICROSERVICE_URL_2 no está configurada'); } const response = await fetchWithAuth(`${MICROSERVICE_URL_2}/services/partida/`, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(requestBody) }); if (response.ok) { const result = await response.json(); showMessage('Partida procesada correctamente', 'success'); } else { const errorData = await response.json().catch(() => ({})); throw new Error(errorData.message || 'Error al procesar la partida'); } } catch (error) { console.error('Error procesando partida:', error); showMessage(`Error al procesar la partida: ${error.message}`, 'error'); } finally { setProcessingPartida(null); } }; // Función para obtener partidas const fetchPedimentoPartidas = async (page = 1, pageSize = 10, filters = {}) => { console.log('fetchPedimentoPartidas called with:', { page, pageSize, filters }); console.log('pedimento object:', pedimento); console.log('pedimento.id:', pedimento?.id); if (!pedimento?.id) { throw new Error('No hay ID de pedimento disponible'); } const params = new URLSearchParams({ pedimento: pedimento.id, page: page.toString(), page_size: pageSize.toString() }); // Solo agregar filtros que tengan valores Object.entries(filters).forEach(([key, value]) => { if (value && value.toString().trim() !== '') { params.append(key, value); } }); const finalUrl = `${API_URL}/customs/partidas/?${params}`; console.log('Final URL:', finalUrl); const response = await fetchWithAuth(finalUrl); if (!response.ok) { throw new Error('Error al obtener las partidas'); } const result = await response.json(); console.log('API Response:', result); return result; }; // Estados para Procesos const [procesos, setProcesos] = useState([]); const [procesosCount, setProcesosCount] = useState(0); const [procesosLoading, setProcesosLoading] = useState(false); const [procesosError, setProcesosError] = useState(''); const [procesosPage, setProcesosPage] = useState(1); const [procesosPageSize, setProcesosPageSize] = useState(10); const [procesosFilters, setProcesosFilters] = useState({}); const [sortField, setSortField] = useState(''); const [sortOrder, setSortOrder] = useState('asc'); const [selectedProcesos, setSelectedProcesos] = useState([]); const [isSelectAll, setIsSelectAll] = useState(false); // Estados para las acciones de procesos const [executingId, setExecutingId] = useState(null); const [changingStateId, setChangingStateId] = useState(null); const [creatingService, setCreatingService] = useState(null); // Ref para rastrear valores previos de filtros const prevFiltersRef = useRef({ sortField: '', sortOrder: 'asc' }); // Estados para modal de preview const [previewOpen, setPreviewOpen] = useState(false); const [previewUrl, setPreviewUrl] = useState(''); const [previewType, setPreviewType] = useState(''); const [previewLoading, setPreviewLoading] = useState(false); const [previewError, setPreviewError] = useState(''); const [previewXml, setPreviewXml] = useState(''); const [previewXmlHtml, setPreviewXmlHtml] = useState(''); const [previewDoc, setPreviewDoc] = useState(null); const [previewContent, setPreviewContent] = useState(''); const [imageZoom, setImageZoom] = useState(1); // Refs const focusKeeperRef = useRef(null); // Opciones para tipos de documento const documentTypeOptions = [ { value: '', label: 'Todos' }, { value: 1, label: 'Pedimento Partida' }, { value: 2, label: 'Pedimento Completo' }, { value: 3, label: 'Remesa' }, { value: 4, label: 'Acuse' }, { value: 5, label: 'EDocument' }, { value: 7, label: 'Acuse Cove' }, { value: 8, label: 'Cove' }, { value: 9, label: 'Documento de digitalización' }, ]; // Helper para obtener el nombre legible del tipo de documento const getDocumentTypeName = (type) => { const found = documentTypeOptions.find(opt => String(opt.value) === String(type)); return found ? found.label : 'Documento'; }; // Función para cambiar de pestaña const handleTabChange = (tab) => { setActiveTab(tab); if (tab === 'auditor') { fetchDashboardSummary(); } }; // Handler SPA para paginación const handlePageChange = (newPage, e) => { if (e && typeof e.preventDefault === 'function') e.preventDefault(); if (e && typeof e.stopPropagation === 'function') e.stopPropagation(); if (newPage < 1 || newPage > Math.max(1, Math.ceil(docsCount / pageSize)) || newPage === page) return; setPage(newPage); // Quitar el foco del botón activo para evitar salto de scroll if (typeof document !== 'undefined' && document.activeElement instanceof HTMLElement) { document.activeElement.blur(); } }; // Handler para cambio de ordenamiento const handleSort = (field) => { if (orderBy === field) { setOrderDir(orderDir === 'asc' ? 'desc' : 'asc'); } else { setOrderBy(field); setOrderDir('asc'); } }; // Funciones de selección const allDocIds = documents.map(doc => doc.id); const allSelected = selected.length === allDocIds.length && allDocIds.length > 0; const handleSelect = (id) => { setSelected(prev => prev.includes(id) ? prev.filter(x => x !== id) : [...prev, id]); }; const handleSelectAllDocs = () => { if (allSelected) setSelected([]); else setSelected(allDocIds); }; const handleBulkDownload = async (ids) => { setDownloading(true); await downloadBulkZip(ids, showMessage, pedimento?.pedimento); setDownloading(false); }; // Vista previa de documento const handlePreview = async (doc) => { setPreviewLoading(true); setPreviewError(''); setPreviewUrl(''); setPreviewType(''); setPreviewXml(''); setPreviewDoc(doc); setImageZoom(1); setPreviewContent(''); setPreviewOpen(true); try { const res = await fetchWithAuth(`${API_URL}/record/documents/descargar/${doc.id}/`); if (!res.ok) { setPreviewError('Error al obtener el archivo'); setPreviewLoading(false); return; } // Detectar tipo de archivo let type = ''; if (doc.extension) { if (doc.extension.toLowerCase() === 'pdf') type = 'pdf'; else if (["jpg","jpeg","png","gif","bmp","webp"].includes(doc.extension.toLowerCase())) type = 'img'; else if (doc.extension.toLowerCase() === 'xml') type = 'xml'; else if (["txt","log","csv"].includes(doc.extension.toLowerCase())) type = 'txt'; else type = 'other'; } setPreviewType(type); if (type === 'xml') { const text = await res.text(); const prettyText = formatXml(text); setPreviewXml(prettyText); // Formatear y resaltar XML try { const highlighted = hljs.highlight(prettyText, { language: 'xml' }).value; setPreviewXmlHtml(highlighted); } catch (e) { setPreviewXmlHtml(prettyText); } setPreviewLoading(false); } else if (type === 'txt') { const text = await res.text(); setPreviewContent(text); setPreviewLoading(false); } else { const blob = await res.blob(); const url = window.URL.createObjectURL(blob); setPreviewUrl(url); setPreviewLoading(false); } } catch (err) { console.error('Error in preview:', err); if (err.message === 'SESSION_EXPIRED') { setPreviewError('Tu sesión ha expirado, por favor inicia sesión de nuevo.'); } else { setPreviewError('Error al obtener el archivo'); } setPreviewLoading(false); } }; // Cerrar modal y limpiar blob const handleClosePreview = () => { setPreviewOpen(false); if (previewUrl) window.URL.revokeObjectURL(previewUrl); setPreviewUrl(''); setPreviewType(''); setPreviewError(''); setPreviewXml(''); setPreviewXmlHtml(''); setPreviewDoc(null); setPreviewContent(''); setImageZoom(1); }; // Funciones para el nuevo diseño de documentos const downloadAll = async () => { setDownloadingAll(true); try { const allDocIds = documents.map(doc => doc.id); await handleBulkDownload(allDocIds); } catch (error) { console.error('Error downloading all documents:', error); showMessage('Error al descargar todos los documentos', 'error'); } finally { setDownloadingAll(false); } }; const previewDocument = async (doc) => { await handlePreview(doc); }; const downloadDocument = async (doc) => { const fileName = doc.archivo ? doc.archivo.split('/').pop() : `documento_${doc.id}`; await downloadFile(doc.id, fileName, showMessage); }; const formatFileSize = (bytes) => { if (!bytes) return 'N/A'; const kb = bytes / 1024; if (kb < 1024) { return `${kb.toFixed(1)} KB`; } const mb = kb / 1024; return `${mb.toFixed(1)} MB`; }; const getFileExtension = (filename) => { if (!filename) return ''; const parts = filename.split('.'); return parts.length > 1 ? parts[parts.length - 1] : ''; }; const getFuenteName = (fuente) => { const fuentes = { 1: 'Manual', 2: 'VU', 3: 'Importación', 4: 'Sistema' }; return fuentes[fuente] || 'Desconocida'; }; // Funciones para manejo de selección múltiple de documentos const handleSelectDocument = (documentId) => { const isSelected = selectedDocuments.includes(documentId); if (isSelected) { setSelectedDocuments(prev => prev.filter(id => id !== documentId)); } else { setSelectedDocuments(prev => [...prev, documentId]); } }; const handleSelectAllDocuments = () => { if (isSelectAllDocs) { setSelectedDocuments([]); setIsSelectAllDocs(false); } else { const allDocumentIds = documents.map(doc => doc.id); setSelectedDocuments(allDocumentIds); setIsSelectAllDocs(true); } }; // Función para eliminar documentos seleccionados const handleDeleteSelectedDocuments = async () => { if (selectedDocuments.length === 0) { showMessage('No hay documentos seleccionados para eliminar', 'warning'); return; } // Mostrar modal de confirmación setShowDeleteModal(true); }; // Función para confirmar la eliminación const confirmDeleteDocuments = async () => { setShowDeleteModal(false); try { showMessage(`Eliminando ${selectedDocuments.length} documento(s)...`, 'info'); // Enviar todos los IDs seleccionados a un endpoint específico const response = await fetchWithAuth(`${API_URL}/record/documents/bulk-delete/`, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ ids: selectedDocuments }) }); if (!response.ok) { const errorData = await response.json().catch(() => null); throw new Error(errorData?.detail || errorData?.message || `Error ${response.status}: ${response.statusText}`); } const result = await response.json(); showMessage( `${result.deleted_count || selectedDocuments.length} documento(s) eliminado(s) exitosamente`, 'success' ); setSelectedDocuments([]); setIsSelectAllDocs(false); // Forzar recarga de documentos cambiando la página temporalmente const currentPage = page; setPage(0); // Cambio temporal setTimeout(() => setPage(currentPage), 100); // Restaurar después de un breve delay } catch (error) { console.error('Error durante la eliminación masiva:', error); showMessage(`Error durante la eliminación: ${error.message}`, 'error'); } }; // Efecto para actualizar isSelectAllDocs cuando cambia la selección useEffect(() => { if (documents.length > 0) { const allSelected = documents.every(doc => selectedDocuments.includes(doc.id)); setIsSelectAllDocs(allSelected && selectedDocuments.length > 0); } }, [selectedDocuments, documents]); // Efecto para limpiar selección cuando cambia de página useEffect(() => { setSelectedDocuments([]); setIsSelectAllDocs(false); }, [page]); // Efecto para cargar datos del pedimento useEffect(() => { const fetchPedimento = async () => { try { const response = await fetchWithAuth(`${API_URL}/customs/pedimentos/${id}/`); if (response.ok) { const data = await response.json(); setPedimento(data); } } catch (err) { setError('Error al cargar el pedimento'); if (err.message === 'SESSION_EXPIRED') { showMessage('Tu sesión ha expirado, por favor inicia sesión de nuevo.', 'error'); } } finally { setLoading(false); } }; if (id) { fetchPedimento(); } }, [id, showMessage]); // Fetch paginated documents useEffect(() => { if (!id) return; console.log('Starting to fetch documents with:', { id, page, pageSize, filters, orderBy, orderDir }); setDocsLoading(true); setDocsError(''); // Construir parámetros de filtros usando el nuevo sistema const apiFilters = {}; if (filters.document_type) apiFilters.document_type = filters.document_type; if (filters.name) apiFilters.archivo__icontains = filters.name; if (filters.extension) apiFilters.extension = filters.extension; if (filters.date) apiFilters.created_at__date = filters.date; if (filters.created_at__gte) apiFilters.created_at__gte = filters.created_at__gte; if (filters.created_at__lte) apiFilters.created_at__lte = filters.created_at__lte; if (filters.fuente) apiFilters.fuente = filters.fuente; if (filters.pedimento_numero) apiFilters.pedimento_numero__icontains = filters.pedimento_numero; // Mantener compatibilidad con filtros legacy si están en uso if (documentTypeFilter) apiFilters.document_type = documentTypeFilter; if (fileNameFilter) apiFilters.archivo__icontains = fileNameFilter; if (extensionFilter) apiFilters.extension = extensionFilter; if (dateFilter) apiFilters.created_at__date = dateFilter; if (orderBy) { const orderField = orderBy === 'archivo' ? 'archivo' : orderBy === 'document_type' ? 'document_type' : orderBy === 'created_at' ? 'created_at' : orderBy === 'size' ? 'size' : orderBy; apiFilters.ordering = orderDir === 'desc' ? `-${orderField}` : orderField; } console.log('Calling fetchPedimentoDocuments with filters:', apiFilters); fetchPedimentoDocuments(id, page, pageSize, apiFilters) .then((data) => { console.log('Received documents data:', data); setDocuments(data.results); setDocsCount(data.count); setDocsNext(data.next); setDocsPrev(data.previous); setDocsLoading(false); }) .catch(err => { console.error('Error fetching documents:', err); if (err.message === 'SESSION_EXPIRED') { showMessage('Tu sesión ha expirado, por favor inicia sesión de nuevo.', 'error'); } else { setDocsError(err.message); } setDocsLoading(false); }); }, [id, page, pageSize, filters, documentTypeFilter, fileNameFilter, extensionFilter, dateFilter, orderBy, orderDir, showMessage]); // Resetear página cuando cambien los filtros useEffect(() => { setPage(1); }, [filters, documentTypeFilter, fileNameFilter, extensionFilter, dateFilter]); // Debug logs useEffect(() => { console.log('Documents data:', { documents, docsCount, page, pageSize, docsLoading, docsError }); }, [documents, docsCount, page, pageSize, docsLoading, docsError]); // Fetch COVEs cuando sea necesario useEffect(() => { if (!id || activeTab !== 'coves') return; setCovesLoading(true); setCovesError(''); fetchPedimentoCoves(id, covesPage, covesPageSize, covesFilters) .then((data) => { setCoves(data.results); setCovesCount(data.count); setCovesLoading(false); }) .catch(err => { console.error('Error fetching COVEs:', err); if (err.message === 'SESSION_EXPIRED') { showMessage('Tu sesión ha expirado, por favor inicia sesión de nuevo.', 'error'); } else { setCovesError(err.message); } setCovesLoading(false); }); }, [id, activeTab, covesPage, covesPageSize, covesFilters, showMessage]); // Resetear página de COVEs cuando cambien los filtros useEffect(() => { setCovesPage(1); }, [covesFilters]); // Fetch EDocs cuando sea necesario useEffect(() => { if (!id || activeTab !== 'edocs') return; setEdocsLoading(true); setEdocsError(''); fetchPedimentoEdocuments(id, edocsPage, edocsPageSize, edocsFilters) .then((data) => { setEdocs(data.results); setEdocsCount(data.count); setEdocsLoading(false); }) .catch(err => { console.error('Error fetching EDocs:', err); if (err.message === 'SESSION_EXPIRED') { showMessage('Tu sesión ha expirado, por favor inicia sesión de nuevo.', 'error'); } else { setEdocsError(err.message); } setEdocsLoading(false); }); }, [id, activeTab, edocsPage, edocsPageSize, edocsFilters, showMessage]); // Resetear página de EDocs cuando cambien los filtros useEffect(() => { setEdocsPage(1); }, [edocsFilters]); // Funciones para acciones de Procesos // Map legacy estado values to new status values const mapEstadoToStatus = (estado) => { const mapping = { 1: 'pending', // Pendiente 2: 'processing', // En Proceso 3: 'completed', // Completado 4: 'failed', // Error 5: 'cancelled' // Cancelado }; return mapping[estado] || 'pending'; }; const updateProcesoEstado = (procId, nuevoEstado) => { setProcesos(procesos => procesos.map(proc => proc.id === procId ? { ...proc, status: mapEstadoToStatus(nuevoEstado) } : proc ) ); }; // Verificar si existe un servicio específico const existeServicio = (tipoServicio) => { return procesos.some(proceso => proceso.servicio === tipoServicio); }; // Función para crear un nuevo servicio const handleCrearServicio = async (tipoServicio, nombreServicio) => { setCreatingService(tipoServicio); try { const body = { pedimento: id, servicio: tipoServicio, estado: 1, // Pendiente tipo_procesamiento: 1, organizacion: pedimentoData?.organizacion || 1 // Usar la organización del pedimento o default }; const res = await postWithAuth(`${API_URL}/tasks/tasks/`, body); if (!res.ok) { throw new Error(`Error al crear el servicio: ${nombreServicio}`); } const nuevoServicio = await res.json(); // Agregar el nuevo servicio a la lista local setProcesos(prev => [...prev, nuevoServicio]); setProcesosCount(prev => prev + 1); showMessage(`Servicio "${nombreServicio}" creado correctamente`, 'success'); // Refrescar la lista después de un breve delay setTimeout(() => { // Re-fetch los procesos para asegurar consistencia const filters = { ...procesosFilters, pedimento: id }; fetchTasks(procesosPage, procesosPageSize, filters) .then((data) => { setProcesos(data.results); setProcesosCount(data.count); }) .catch(err => { console.error('Error refreshing procesos:', err); }); }, 1000); } catch (err) { console.error('Error creando servicio:', err); showMessage(`Error al crear el servicio: ${err.message}`, 'error'); } finally { setCreatingService(null); } }; const handlePasarAEspera = async (proc) => { setChangingStateId(proc.id); // Cambiar estado visual a "Procesando" inmediatamente updateProcesoEstado(proc.id, 'processing'); try { const body = { status: 'pending', // Cambiar a Pendiente tipo_procesamiento: 2, pedimento: typeof proc.pedimento === 'object' && proc.pedimento !== null ? proc.pedimento.id : proc.pedimento, servicio: proc.servicio, organizacion: proc.organizacion_id || proc.organizacion || proc.organizacionId }; const res = await putWithAuth(`${API_URL}/tasks/tasks/${proc.task_id}/`, body); if (!res.ok) { // Si falla, cambiar a estado Error updateProcesoEstado(proc.id, 'failed'); // Failed throw new Error('Error al cambiar el estado del proceso'); } // Cambiar estado visual a "En Espera" si fue exitoso updateProcesoEstado(proc.id, 'pending'); showMessage('Estado cambiado a "En Espera" correctamente', 'success'); } catch (err) { console.error('Error cambiando estado:', err); updateProcesoEstado(proc.id, 'failed'); showMessage('Error al cambiar el estado del proceso: ' + err.message, 'error'); } finally { setChangingStateId(null); } }; const handleEjecutarServicio = async (proc) => { setExecutingId(proc.id); // Cambiar estado visual a "Procesando" inmediatamente updateProcesoEstado(proc.id, 'processing'); let endpoint = ''; // Determinar endpoint según el tipo de servicio switch (proc.servicio) { case 3: endpoint = '/services/pedimento_completo'; break; case 4: // Partidas endpoint = '/services/partidas'; break; case 5: // Remesas endpoint = '/services/remesas'; break; case 6: // Acuse endpoint = '/services/acuse'; break; case 7: endpoint = '/services/edocument'; break; case 8: // Coves endpoint = '/services/coves'; break; case 9: // Acuse Cove endpoint = '/services/acuseCove'; break; default: // Revertir estado si el servicio no es soportado updateProcesoEstado(proc.id, proc.status); // Revertir al estado original showMessage('Este servicio no es compatible para ejecución directa.', 'error'); setExecutingId(null); return; } try { const body = { pedimento: typeof proc.pedimento === 'object' && proc.pedimento !== null ? proc.pedimento.id : proc.pedimento, organizacion: proc.organizacion_id || proc.organizacion || proc.organizacionId, }; const res = await postWithAuth(`${MICROSERVICE_URL}${endpoint}`, body); if (!res.ok) { // Si falla, cambiar estado a Error updateProcesoEstado(proc.id, 'failed'); throw new Error('Error al ejecutar el servicio'); } // Si es exitoso, cambiar estado a Finalizado updateProcesoEstado(proc.id, 'completed'); showMessage('El servicio se ha ejecutado correctamente', 'success'); } catch (err) { // Cambiar estado a Error en caso de excepción updateProcesoEstado(proc.id, 'failed'); if (err.message === 'SESSION_EXPIRED') { showMessage('Tu sesión ha expirado. Por favor, inicia sesión nuevamente.', 'error'); } else { showMessage('Error al ejecutar servicio: ' + err.message, 'error'); } } finally { setExecutingId(null); } }; // Función para comparar Remesas vs COVEs en DB const handleCompararRemesasCoves = async () => { try { showMessage('Iniciando comparación de Remesas vs COVEs...', 'info'); const body = { pedimento: id, organizacion: pedimento?.organizacion || 1 }; // Llamada al endpoint de comparación (ajustar según la API real) const res = await postWithAuth(`${MICROSERVICE_URL}/services/comparar_remesas_coves`, body); if (!res.ok) { throw new Error('Error al realizar la comparación'); } const resultado = await res.json(); // Mostrar resultado de la comparación if (resultado.coincidencias) { showMessage(`Comparación completada: ${resultado.mensaje || 'Remesas y COVEs coinciden correctamente'}`, 'success'); } else { showMessage(`Discrepancias encontradas: ${resultado.mensaje || 'Se encontraron diferencias entre Remesas y COVEs'}`, 'warning'); } // Opcional: mostrar detalles en consola para debugging console.log('Resultado comparación Remesas vs COVEs:', resultado); } catch (err) { console.error('Error comparando Remesas vs COVEs:', err); if (err.message === 'SESSION_EXPIRED') { showMessage('Tu sesión ha expirado. Por favor, inicia sesión nuevamente.', 'error'); } else { showMessage('Error al comparar Remesas vs COVEs: ' + err.message, 'error'); } } }; // Función para auditar Pedimento Completo const handleAuditarPedimentoCompleto = async () => { try { showMessage('Iniciando auditoría del Pedimento Completo...', 'info'); const body = { pedimento: id, organizacion: pedimento?.organizacion || 1 }; const res = await postWithAuth(`${MICROSERVICE_URL}/services/auditar_pedimento_completo`, body); if (!res.ok) { throw new Error('Error al auditar el pedimento completo'); } const resultado = await res.json(); if (resultado.auditoria_exitosa) { showMessage(`Auditoría completada: ${resultado.mensaje || 'Pedimento completo auditado correctamente'}`, 'success'); } else { showMessage(`Problemas encontrados: ${resultado.mensaje || 'Se encontraron inconsistencias en el pedimento'}`, 'warning'); } console.log('Resultado auditoría Pedimento Completo:', resultado); } catch (err) { console.error('Error auditando Pedimento Completo:', err); if (err.message === 'SESSION_EXPIRED') { showMessage('Tu sesión ha expirado. Por favor, inicia sesión nuevamente.', 'error'); } else { showMessage('Error al auditar Pedimento Completo: ' + err.message, 'error'); } } }; // Función para auditar EDocs const handleAuditarEDocs = async () => { try { showMessage('Iniciando auditoría de EDocs...', 'info'); const body = { pedimento: id, organizacion: pedimento?.organizacion || 1 }; const res = await postWithAuth(`${MICROSERVICE_URL}/services/auditar_edocs`, body); if (!res.ok) { throw new Error('Error al auditar EDocs'); } const resultado = await res.json(); if (resultado.auditoria_exitosa) { showMessage(`Auditoría EDocs completada: ${resultado.mensaje || 'EDocs auditados correctamente'}`, 'success'); } else { showMessage(`Problemas en EDocs: ${resultado.mensaje || 'Se encontraron inconsistencias en EDocs'}`, 'warning'); } console.log('Resultado auditoría EDocs:', resultado); } catch (err) { console.error('Error auditando EDocs:', err); if (err.message === 'SESSION_EXPIRED') { showMessage('Tu sesión ha expirado. Por favor, inicia sesión nuevamente.', 'error'); } else { showMessage('Error al auditar EDocs: ' + err.message, 'error'); } } }; // Función para auditar Partidas const handleAuditarPartidas = async () => { try { showMessage('Iniciando auditoría de Partidas...', 'info'); const body = { pedimento: id, organizacion: pedimento?.organizacion || 1 }; const res = await postWithAuth(`${MICROSERVICE_URL}/services/auditar_partidas`, body); if (!res.ok) { throw new Error('Error al auditar Partidas'); } const resultado = await res.json(); if (resultado.auditoria_exitosa) { showMessage(`Auditoría Partidas completada: ${resultado.mensaje || 'Partidas auditadas correctamente'}`, 'success'); } else { showMessage(`Problemas en Partidas: ${resultado.mensaje || 'Se encontraron inconsistencias en Partidas'}`, 'warning'); } console.log('Resultado auditoría Partidas:', resultado); } catch (err) { console.error('Error auditando Partidas:', err); if (err.message === 'SESSION_EXPIRED') { showMessage('Tu sesión ha expirado. Por favor, inicia sesión nuevamente.', 'error'); } else { showMessage('Error al auditar Partidas: ' + err.message, 'error'); } } }; // Función para auditar Acuses const handleAuditarAcuses = async () => { try { showMessage('Iniciando auditoría de Acuses...', 'info'); const body = { pedimento: id, organizacion: pedimento?.organizacion || 1 }; const res = await postWithAuth(`${MICROSERVICE_URL}/services/auditar_acuses`, body); if (!res.ok) { throw new Error('Error al auditar Acuses'); } const resultado = await res.json(); if (resultado.auditoria_exitosa) { showMessage(`Auditoría Acuses completada: ${resultado.mensaje || 'Acuses auditados correctamente'}`, 'success'); } else { showMessage(`Problemas en Acuses: ${resultado.mensaje || 'Se encontraron inconsistencias en Acuses'}`, 'warning'); } console.log('Resultado auditoría Acuses:', resultado); } catch (err) { console.error('Error auditando Acuses:', err); if (err.message === 'SESSION_EXPIRED') { showMessage('Tu sesión ha expirado. Por favor, inicia sesión nuevamente.', 'error'); } else { showMessage('Error al auditar Acuses: ' + err.message, 'error'); } } }; // Función para auditar COVEs const handleAuditarCoves = async () => { try { showMessage('Iniciando auditoría de COVEs...', 'info'); const body = { pedimento: id, organizacion: pedimento?.organizacion || 1 }; const res = await postWithAuth(`${MICROSERVICE_URL}/services/auditar_coves`, body); if (!res.ok) { throw new Error('Error al auditar COVEs'); } const resultado = await res.json(); if (resultado.auditoria_exitosa) { showMessage(`Auditoría COVEs completada: ${resultado.mensaje || 'COVEs auditados correctamente'}`, 'success'); } else { showMessage(`Problemas en COVEs: ${resultado.mensaje || 'Se encontraron inconsistencias en COVEs'}`, 'warning'); } console.log('Resultado auditoría COVEs:', resultado); } catch (err) { console.error('Error auditando COVEs:', err); if (err.message === 'SESSION_EXPIRED') { showMessage('Tu sesión ha expirado. Por favor, inicia sesión nuevamente.', 'error'); } else { showMessage('Error al auditar COVEs: ' + err.message, 'error'); } } }; // Función para auditar Acuse de COVEs const handleAuditarAcuseCoves = async () => { try { showMessage('Iniciando auditoría de Acuse de COVEs...', 'info'); const body = { pedimento: id, organizacion: pedimento?.organizacion || 1 }; const res = await postWithAuth(`${MICROSERVICE_URL}/services/auditar_acuse_coves`, body); if (!res.ok) { throw new Error('Error al auditar Acuse de COVEs'); } const resultado = await res.json(); if (resultado.auditoria_exitosa) { showMessage(`Auditoría Acuse COVEs completada: ${resultado.mensaje || 'Acuse de COVEs auditado correctamente'}`, 'success'); } else { showMessage(`Problemas en Acuse COVEs: ${resultado.mensaje || 'Se encontraron inconsistencias en Acuse de COVEs'}`, 'warning'); } console.log('Resultado auditoría Acuse COVEs:', resultado); } catch (err) { console.error('Error auditando Acuse COVEs:', err); if (err.message === 'SESSION_EXPIRED') { showMessage('Tu sesión ha expirado. Por favor, inicia sesión nuevamente.', 'error'); } else { showMessage('Error al auditar Acuse COVEs: ' + err.message, 'error'); } } }; // Funciones de manejo de procesos // Removed duplicate definitions // Handle select all process checkboxes const handleSelectAllProcesos = (e) => { if (e.target.checked) { const availableProcesos = procesos .filter(proc => proc.status !== 'running' && proc.status !== 'completed') .map(proc => proc.task_id); setSelectedProcesos(availableProcesos); setIsSelectAll(true); } else { setSelectedProcesos([]); setIsSelectAll(false); } }; // Handle select individual process checkbox const handleSelectProceso = (id, checked) => { if (checked) { setSelectedProcesos(prev => [...prev, id]); } else { setSelectedProcesos(prev => prev.filter(i => i !== id)); } // Update isSelectAll const availableProcesos = procesos .filter(proc => proc.status !== 'running' && proc.status !== 'completed') .map(proc => proc.task_id); setIsSelectAll(availableProcesos.length === selectedProcesos.length + (checked ? 1 : -1)); }; const handlePasarPaginaAEspera = async () => { const failedProcesses = procesos.filter(proc => proc.status === 'failed'); if (failedProcesses.length === 0) return; for (const proceso of failedProcesses) { await handlePasarAEspera(proceso); } }; const handlePasarSeleccionadosAEspera = async () => { const failedSelectedProcesses = procesos.filter( proc => selectedProcesos.includes(proc.task_id) && proc.status === 'failed' ); if (failedSelectedProcesses.length === 0) return; for (const proceso of failedSelectedProcesses) { await handlePasarAEspera(proceso); } }; // Función para verificar servicios creados const handleVerificarServiciosCreados = async () => { try { showMessage('Iniciando verificación de servicios creados...', 'info'); const body = { pedimento: id, organizacion: pedimento?.organizacion || 1 }; const res = await postWithAuth(`${MICROSERVICE_URL}/services/verificar_servicios_creados`, body); if (!res.ok) { throw new Error('Error al verificar servicios creados'); } const resultado = await res.json(); if (resultado.verificacion_exitosa) { showMessage(`Verificación completada: ${resultado.mensaje || 'Todos los servicios han sido verificados correctamente'}`, 'success'); } else { showMessage(`Problemas encontrados: ${resultado.mensaje || 'Se encontraron servicios faltantes o con problemas'}`, 'warning'); } // Mostrar resumen de servicios si está disponible if (resultado.resumen) { console.log('Resumen de servicios:', resultado.resumen); // Opcional: mostrar el resumen en una notificación adicional if (resultado.resumen.total_servicios) { showMessage(`Servicios encontrados: ${resultado.resumen.servicios_activos}/${resultado.resumen.total_servicios}`, 'info'); } } console.log('Resultado verificación servicios creados:', resultado); } catch (err) { console.error('Error verificando servicios creados:', err); if (err.message === 'SESSION_EXPIRED') { showMessage('Tu sesión ha expirado. Por favor, inicia sesión nuevamente.', 'error'); } else { showMessage('Error al verificar servicios creados: ' + err.message, 'error'); } } }; // Fetch Partidas cuando sea necesario useEffect(() => { console.log('Partidas useEffect triggered:', { id, activeTab, pedimento: pedimento?.id, partidasPage, partidasPageSize, partidasFilters }); if (!id || activeTab !== 'partidas' || !pedimento) { console.log('Skipping partidas fetch due to missing conditions:', { hasId: !!id, isPartidasTab: activeTab === 'partidas', hasPedimento: !!pedimento }); return; } console.log('Starting partidas fetch...'); setPartidasLoading(true); setPartidasError(''); fetchPedimentoPartidas(partidasPage, partidasPageSize, partidasFilters) .then((data) => { console.log('Partidas fetch success:', data); setPartidas(data.results || []); setPartidasCount(data.count || 0); setPartidasLoading(false); }) .catch(err => { console.error('Error fetching Partidas:', err); if (err.message === 'SESSION_EXPIRED') { showMessage('Tu sesión ha expirado, por favor inicia sesión de nuevo.', 'error'); } else { setPartidasError(err.message); } setPartidasLoading(false); }); }, [id, activeTab, partidasPage, partidasPageSize, partidasFilters, showMessage, pedimento]); // Resetear página de Partidas cuando cambien los filtros useEffect(() => { setPartidasPage(1); }, [partidasFilters]); // Fetch Procesos cuando sea necesario useEffect(() => { if (!id || activeTab !== 'procesos') return; // Detectar si algún filtro cambió const currentFilters = { sortField, sortOrder }; const filtersChanged = Object.keys(currentFilters).some( key => currentFilters[key] !== prevFiltersRef.current[key] ); // Si los filtros cambiaron y no estamos en la página 1, resetear página if (filtersChanged && procesosPage !== 1) { setProcesosPage(1); // Actualizar ref con valores actuales prevFiltersRef.current = { ...currentFilters }; return; // Salir temprano, el efecto se ejecutará de nuevo con page = 1 } // Actualizar ref con valores actuales prevFiltersRef.current = { ...currentFilters }; setProcesosLoading(true); setProcesosError(''); // Construir filtros const filters = { ...procesosFilters, pedimento: id // Filtrar por el pedimento actual }; if (sortField) { const fieldMapping = { 'id': 'task_id', 'estado': 'status', 'pedimento': 'pedimento_app' }; const mappedField = fieldMapping[sortField] || sortField; filters['ordering'] = (sortOrder === 'desc' ? '-' : '') + mappedField; } fetchTasks(procesosPage, procesosPageSize, filters) .then((data) => { setProcesos(data.results); setProcesosCount(data.count); setProcesosLoading(false); }) .catch(err => { console.error('Error fetching Procesos:', err); if (err.message === 'SESSION_EXPIRED') { showMessage('Tu sesión ha expirado, por favor inicia sesión de nuevo.', 'error'); } else { setProcesosError(err.message); } setProcesosLoading(false); }); }, [id, activeTab, procesosPage, procesosPageSize, sortField, sortOrder, procesosFilters, showMessage]); // Resetear página de Procesos cuando cambie el pedimento useEffect(() => { setProcesosPage(1); }, [id]); // Funciones para COVEs const handleCoveDownload = async (cove) => { try { await downloadCove(cove.id); showMessage(`COVE ${cove.numero_cove} descargado exitosamente`, 'success'); } catch (error) { showMessage(`Error al descargar COVE: ${error.message}`, 'error'); } }; const handleAcuseCoveDownload = async (cove) => { try { await downloadAcuseCove(cove.id); showMessage(`Acuse de COVE ${cove.numero_cove} descargado exitosamente`, 'success'); } catch (error) { showMessage(`Error al descargar acuse de COVE: ${error.message}`, 'error'); } }; // Funciones para EDocs const handleEdocDownload = async (edoc) => { try { await downloadEdocument(edoc.id); showMessage(`EDocs ${edoc.numero_edocument} descargado exitosamente`, 'success'); } catch (error) { showMessage(`Error al descargar EDocs: ${error.message}`, 'error'); } }; const handleAcuseEdocDownload = async (edoc) => { try { await downloadAcuseEdocument(edoc.id); showMessage(`Acuse de EDocs ${edoc.numero_edocument} descargado exitosamente`, 'success'); } catch (error) { showMessage(`Error al descargar acuse de EDocs: ${error.message}`, 'error'); } }; // Funciones para procesar peticiones const handleCoveRequest = async (cove) => { console.log('Request cove:', cove); showMessage(`Procesando petición para COVE #${cove.numero_cove}...`, 'info'); // Aquí implementarías la lógica de petición específica para COVEs }; // Función para procesar COVE const handleCoveProcess = async (cove) => { setProcessingCove(cove.id); try { // Obtener credenciales para el contribuyente del pedimento const credencialesList = await fetchCredenciales(pedimento.contribuyente); if (credencialesList.length === 0) { showMessage('No se encontraron credenciales VUCEM para este contribuyente', 'error'); return; } // Usar la primera credencial activa disponible const credencial = credencialesList.find(c => c.is_active) || credencialesList[0]; const requestBody = { cove: { id: cove.id, cove: cove.numero_cove }, pedimento: { id: pedimento.id, pedimento: pedimento.pedimento, pedimento_app: pedimento.pedimento_app, aduana: pedimento.aduana, patente: pedimento.patente, organizacion: pedimento.organizacion, regimen: pedimento.regimen || "test", clave_pedimento: pedimento.clave_pedimento || "test", numero_operacion: pedimento.numero_operacion || "test" }, credencial: { id: credencial.id, user: credencial.usuario, password: credencial.password, efirma: credencial.efirma, key: credencial.key, cer: credencial.cer, is_active: credencial.is_active, organizacion: credencial.organizacion } }; // Verificar si MICROSERVICE_URL_2 está definido if (!MICROSERVICE_URL_2) { throw new Error('La variable de entorno VITE_EFC_MICROSERVICE_URL_2 no está configurada'); } const response = await fetchWithAuth(`${MICROSERVICE_URL_2}/services/cove/`, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(requestBody) }); if (response.ok) { const result = await response.json(); showMessage('COVE procesado correctamente', 'success'); } else { const errorData = await response.json().catch(() => ({})); throw new Error(errorData.message || 'Error al procesar el COVE'); } } catch (error) { console.error('Error procesando COVE:', error); showMessage(`Error al procesar el COVE: ${error.message}`, 'error'); } finally { setProcessingCove(null); } }; // Función para procesar Acuse de COVE const handleAcuseCoveProcess = async (cove) => { setProcessingAcuseCove(cove.id); try { // Obtener credenciales para el contribuyente del pedimento const credencialesList = await fetchCredenciales(pedimento.contribuyente); if (credencialesList.length === 0) { showMessage('No se encontraron credenciales VUCEM para este contribuyente', 'error'); return; } // Usar la primera credencial activa disponible const credencial = credencialesList.find(c => c.is_active) || credencialesList[0]; const requestBody = { cove: { id: cove.id, cove: cove.numero_cove }, pedimento: { id: pedimento.id, pedimento: pedimento.pedimento, pedimento_app: pedimento.pedimento_app, aduana: pedimento.aduana, patente: pedimento.patente, organizacion: pedimento.organizacion, regimen: pedimento.regimen || "test", clave_pedimento: pedimento.clave_pedimento || "test", numero_operacion: pedimento.numero_operacion || "test" }, credencial: { id: credencial.id, user: credencial.usuario, password: credencial.password, efirma: credencial.efirma, key: credencial.key, cer: credencial.cer, is_active: credencial.is_active, organizacion: credencial.organizacion } }; // Verificar si MICROSERVICE_URL_2 está definido if (!MICROSERVICE_URL_2) { throw new Error('La variable de entorno VITE_EFC_MICROSERVICE_URL_2 no está configurada'); } const response = await fetchWithAuth(`${MICROSERVICE_URL_2}/services/acuse/cove/`, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(requestBody) }); if (response.ok) { const result = await response.json(); showMessage('Acuse de COVE procesado correctamente', 'success'); } else { const errorData = await response.json().catch(() => ({})); throw new Error(errorData.message || 'Error al procesar el acuse de COVE'); } } catch (error) { console.error('Error procesando acuse de COVE:', error); showMessage(`Error al procesar el acuse de COVE: ${error.message}`, 'error'); } finally { setProcessingAcuseCove(null); } }; // Función para procesar EDoc const handleEdocProcess = async (edoc) => { setProcessingEdoc(edoc.id); try { // Obtener credenciales para el contribuyente del pedimento const credencialesList = await fetchCredenciales(pedimento.contribuyente); if (credencialesList.length === 0) { showMessage('No se encontraron credenciales VUCEM para este contribuyente', 'error'); return; } // Usar la primera credencial activa disponible const credencial = credencialesList.find(c => c.is_active) || credencialesList[0]; const requestBody = { edoc: { id: edoc.id, numero_edocument: edoc.numero_edocument }, idEDocument: edoc.numero_edocument, pedimento: { id: pedimento.id, pedimento: pedimento.pedimento, pedimento_app: pedimento.pedimento_app, aduana: pedimento.aduana, patente: pedimento.patente, organizacion: pedimento.organizacion, regimen: pedimento.regimen || "test", clave_pedimento: pedimento.clave_pedimento || "test", numero_operacion: pedimento.numero_operacion || "test" }, credencial: { id: credencial.id, user: credencial.usuario, password: credencial.password, efirma: credencial.efirma, key: credencial.key, cer: credencial.cer, is_active: credencial.is_active, organizacion: credencial.organizacion } }; // Verificar si MICROSERVICE_URL_2 está definido if (!MICROSERVICE_URL_2) { throw new Error('La variable de entorno VITE_EFC_MICROSERVICE_URL_2 no está configurada'); } const response = await fetchWithAuth(`${MICROSERVICE_URL_2}/services/download/edoc/`, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(requestBody) }); if (response.ok) { const result = await response.json(); showMessage('EDoc procesado correctamente', 'success'); } else { const errorData = await response.json().catch(() => ({})); throw new Error(errorData.message || 'Error al procesar el EDoc'); } } catch (error) { console.error('Error procesando EDoc:', error); showMessage(`Error al procesar el EDoc: ${error.message}`, 'error'); } finally { setProcessingEdoc(null); } }; // Función para procesar Acuse de EDoc const handleAcuseEdocProcess = async (edoc) => { setProcessingAcuseEdoc(edoc.id); try { // Obtener credenciales para el contribuyente del pedimento const credencialesList = await fetchCredenciales(pedimento.contribuyente); if (credencialesList.length === 0) { showMessage('No se encontraron credenciales VUCEM para este contribuyente', 'error'); return; } // Usar la primera credencial activa disponible const credencial = credencialesList.find(c => c.is_active) || credencialesList[0]; const requestBody = { edoc: { id: edoc.id, numero_edocument: edoc.numero_edocument }, idEDocument: edoc.numero_edocument, pedimento: { id: pedimento.id, pedimento: pedimento.pedimento, pedimento_app: pedimento.pedimento_app, aduana: pedimento.aduana, patente: pedimento.patente, organizacion: pedimento.organizacion, regimen: pedimento.regimen || "test", clave_pedimento: pedimento.clave_pedimento || "test", numero_operacion: pedimento.numero_operacion || "test" }, credencial: { id: credencial.id, user: credencial.usuario, password: credencial.password, efirma: credencial.efirma, key: credencial.key, cer: credencial.cer, is_active: credencial.is_active, organizacion: credencial.organizacion } }; // Verificar si MICROSERVICE_URL_2 está definido if (!MICROSERVICE_URL_2) { throw new Error('La variable de entorno VITE_EFC_MICROSERVICE_URL_2 no está configurada'); } const response = await fetchWithAuth(`${MICROSERVICE_URL_2}/services/acuse/`, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(requestBody) }); if (response.ok) { const result = await response.json(); showMessage('Acuse de EDoc procesado correctamente', 'success'); } else { const errorData = await response.json().catch(() => ({})); throw new Error(errorData.message || 'Error al procesar el acuse de EDoc'); } } catch (error) { console.error('Error procesando acuse de EDoc:', error); showMessage(`Error al procesar el acuse de EDoc: ${error.message}`, 'error'); } finally { setProcessingAcuseEdoc(null); } }; const handleEdocRequest = async (edoc) => { console.log('Request edoc:', edoc); showMessage(`Procesando petición para EDocs #${edoc.numero_edocument}...`, 'info'); // Aquí implementarías la lógica de petición específica para EDocs }; const formatDate = (dateString) => { if (!dateString) return 'N/A'; return new Date(dateString).toLocaleDateString('es-ES', { year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit' }); }; // Funciones para Partidas const handlePartidaPreview = async (partida) => { // Pasar un objeto que simule un documento con el ID del pedimento const partidaDoc = { ...partida, id: pedimento.id }; await handlePreview(partidaDoc); }; const handlePartidaDownload = async (partida) => { const fileName = partida.archivo ? partida.archivo.split('/').pop() : `partida_${partida.numero_partida}_${partida.id}`; await downloadFile(pedimento.id, fileName, showMessage); }; const handlePartidaRequest = async (partida) => { await handlePartidaProcess(partida); }; // Funciones de selección para partidas const allPartidaIds = partidas.map(partida => partida.id); const allPartidasSelected = selectedPartidas.length === allPartidaIds.length && allPartidaIds.length > 0; const handleSelectPartida = (id) => { setSelectedPartidas(prev => prev.includes(id) ? prev.filter(x => x !== id) : [...prev, id]); }; const handleSelectAllPartidas = () => { if (allPartidasSelected) setSelectedPartidas([]); else setSelectedPartidas(allPartidaIds); }; const handleBulkDownloadPartidas = async (ids) => { setDownloadingPartidas(true); await downloadBulkZip(ids, showMessage, `${pedimento?.pedimento}_partidas`); setDownloadingPartidas(false); }; const downloadAllPartidas = async () => { setDownloadingAllPartidas(true); try { const allPartidaIds = partidas.map(partida => partida.id); await handleBulkDownloadPartidas(allPartidaIds); } catch (error) { console.error('Error downloading all partidas:', error); showMessage('Error al descargar todas las partidas', 'error'); } finally { setDownloadingAllPartidas(false); } }; // Estados de carga if (loading) return (
Cargando detalle del pedimento...
{error}
← Volver a expedientesInformación completa del pedimento y documentos asociados
{docsCount > 0 && (Selecciona una acción para continuar
{error}
No se encontraron documentos para este pedimento.
| Fuente | Acciones | ||||||
|---|---|---|---|---|---|---|---|
| handleSelectDocument(doc.id)} className="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded" /> |
{doc.archivo ? doc.archivo.split('/').pop() : 'Sin nombre'}
Pedimento: {doc.pedimento_numero || 'N/A'}
|
{getDocumentTypeName(doc.document_type)} | {doc.extension ? doc.extension.toUpperCase() : 'N/A'} | {formatFileSize(doc.size)} | {getFuenteName(doc.fuente)} | {formatDate(doc.created_at)} |
|
{partidasError}
No se encontraron partidas para este pedimento.
| ID | Número de Partida | Estado de Descarga | Fecha de Creación | Última Actualización | Acciones | |
|---|---|---|---|---|---|---|
| handleSelectPartida(partida.id)} /> |
#{partida.id}
|
{partida.numero_partida}
|
{partida.descargado ? 'Descargado' : 'Pendiente'} | {partida.created_at ? formatDate(partida.created_at) : 'N/A'} | {partida.updated_at ? formatDate(partida.updated_at) : 'N/A'} |
{/* Botón Petición (solo activo si está pendiente) */}
|
{covesError}
No se encontraron COVEs para este pedimento.
| Número COVE | Fecha de Creación | Estado COVE | Estado Acuse | Acciones |
|---|---|---|---|---|
|
{cove.numero_cove}
|
{formatDate(cove.created_at)} | {cove.cove_descargado ? 'Descargado' : 'Pendiente'} | {cove.acuse_cove_descargado ? 'Descargado' : 'Pendiente'} |
{/* Botón COVE */}
{/* Botón Acuse de COVE */}
|
{edocsError}
No se encontraron documentos electrónicos para este pedimento.
| Número EDocs | Clave | Descripción | Fecha de Creación | Estado EDocs | Estado Acuse | Acciones |
|---|---|---|---|---|---|---|
|
{edoc.numero_edocument}
|
{edoc.clave || 'N/A'} | {edoc.descripcion || 'Sin descripción'} | {formatDate(edoc.created_at)} | {edoc.edocument_descargado ? 'Descargado' : 'Pendiente'} | {edoc.acuse_descargado ? 'Descargado' : 'Pendiente'} |
{/* Botón EDoc */}
{/* Botón Acuse de EDoc */}
|
{procesosError}
No se encontraron procesos para este pedimento.
| ID Proceso | Servicio | Estado | Organización | Fecha Creación | Última Actualización | Acciones | |
|---|---|---|---|---|---|---|---|
|
handleSelectProceso(proceso.task_id, e.target.checked)}
disabled={proceso.status === 'running' || proceso.status === 'completed'}
className="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded disabled:opacity-50"
/>
|
#{proceso.task_id}
{proceso.pedimento_app}
|
{getServicioLabel(proceso.servicio)} | {getTaskStatusLabel(proceso.status)} {proceso.status === 'running' && ( )} | {proceso.organizacion_name || 'N/A'} | {formatDate(proceso.created_at)} | {formatDate(proceso.updated_at)} |
{/* Botón Play (Ejecutar Servicio) */}
{isTaskActionable(proceso.status) && (
)}
{/* Botón Retry (Reintentar) */}
{proceso.status === 'failed' && (
)}
|
Intente recargar la página o contacte a soporte si el problema persiste.
{dashboardSummary.cumplimiento_total >= 100 ? 'Todos los documentos y validaciones están completos' : dashboardSummary.cumplimiento_total >= 75 ? 'El pedimento requiere atención en algunos aspectos' : 'Se requiere atención urgente en varios aspectos'}
{`${dashboardSummary.pedimentos.completos} de ${dashboardSummary.pedimentos.total} documentos procesados`}
{`COVEs: ${dashboardSummary.coves.coves_procesados} de ${dashboardSummary.coves.total} procesados`}
{`Acuses: ${dashboardSummary.acuse_coves.acuse_coves_procesados} de ${dashboardSummary.acuse_coves.total} procesados`}
{`E-Docs: ${dashboardSummary.edocuments.edocs_descargados} de ${dashboardSummary.edocuments.total} descargados`}
{`Acuses: ${dashboardSummary.acuses.acuse_descargados} de ${dashboardSummary.acuses.total} descargados`}
{`${dashboardSummary.partidas.partidas_descargadas} de ${dashboardSummary.partidas.total} partidas procesadas`}
Trabajando en ello
Estamos preparando el historial de actividades
Esta funcionalidad estará disponible próximamente
Preparando vista previa
{previewError}
{previewContent || 'Contenido no disponible'}
Este tipo de archivo no se puede mostrar en el navegador
Descargar archivoNo se pudo cargar el documento
Esta acción no se puede deshacer
¿Estás seguro de que deseas eliminar{' '} {selectedDocuments.length} documento{selectedDocuments.length !== 1 ? 's' : ''} ?
Advertencia importante
Los documentos eliminados no podrán ser recuperados. Asegúrate de que realmente deseas proceder con esta acción.