import React, { useEffect, useState, useRef } from 'react'; import { FileUp } from 'lucide-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 { fetchPedimentoCompleto} from '../api/pedimentoCompleto'; import { fetchWithAuth, postWithAuth, putWithAuth, postFormDataWithAuth } 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 [activeTab, setActiveTab] = useState('pedimento'); 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 [showUploadModal, setShowUploadModal] = useState(false); // Estados para subir documentos const [selectedFiles, setSelectedFiles] = useState([]); const [uploadingDocuments, setUploadingDocuments] = useState(false); const [dashboardSummary, setDashboardSummary] = useState(null); const [showFilters, setShowFilters] = useState(false); const [showFiltersPedimento, setShowFiltersPedimento] = 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: '' }); // Filtros simplificados de pedimento (nuevo diseño) const [pedimentoFilters, setPedimentoFilters] = 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 Pedimento T2025-10-152 const [peddocuments, setPedDocuments] = useState([]); const [peddocsCount, setPedDocsCount] = useState(0); const [pedimentoNext, setPedimentoNext] = useState(null); const [pedimentoPrev, setPedimentoPrev] = useState(null); const [pedimentoLoading, setPedimentoLoading] = useState(false); const [pedimentoError, setPedimentoError] = useState(''); const [pedimentoPage, setPedimentoPage] = useState(1); const [pedimentoPageSize, setPedimentoPageSize] = useState(10); const [downloadingAllPedimento, setDownloadingAllPedimento] = useState(false); // Agrega estos estados para selección de documentos de pedimento const [selectedPedimentoDocuments, setSelectedPedimentoDocuments] = useState([]); const [isSelectAllPedimentoDocs, setIsSelectAllPedimentoDocs] = useState(false); // Efecto para actualizar isSelectAllPedimentoDocs cuando cambia la selección useEffect(() => { if (peddocuments.length > 0) { const allSelected = peddocuments.every(doc => selectedPedimentoDocuments.includes(doc.id)); setIsSelectAllPedimentoDocs(allSelected && selectedPedimentoDocuments.length > 0); } }, [selectedPedimentoDocuments, peddocuments]); // Efecto para limpiar selección cuando cambia de página useEffect(() => { setSelectedPedimentoDocuments([]); setIsSelectAllPedimentoDocs(false); }, [pedimentoPage]); // Funciones para manejo de selección múltiple de documentos de pedimento const handleSelectPedimentoDocument = (documentId) => { const isSelected = selectedPedimentoDocuments.includes(documentId); if (isSelected) { setSelectedPedimentoDocuments(prev => prev.filter(id => id !== documentId)); } else { setSelectedPedimentoDocuments(prev => [...prev, documentId]); } }; const handleSelectAllPedimentoDocuments = () => { if (isSelectAllPedimentoDocs) { setSelectedPedimentoDocuments([]); setIsSelectAllPedimentoDocs(false); } else { const allDocumentIds = peddocuments.map(doc => doc.id); setSelectedPedimentoDocuments(allDocumentIds); setIsSelectAllPedimentoDocs(true); } }; // Función para eliminar documentos seleccionados de pedimento const handleDeleteSelectedPedimentoDocuments = async () => { if (selectedPedimentoDocuments.length === 0) { showMessage('No hay documentos seleccionados para eliminar', 'warning'); return; } try { showMessage(`Eliminando ${selectedPedimentoDocuments.length} documento(s)...`, 'info'); const response = await fetchWithAuth(`${API_URL}/record/documents/bulk-delete/`, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ ids: selectedPedimentoDocuments }) }); 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 || selectedPedimentoDocuments.length} documento(s) eliminado(s) exitosamente`, 'success' ); setSelectedPedimentoDocuments([]); setIsSelectAllPedimentoDocs(false); // Forzar recarga de documentos const currentPage = pedimentoPage; setPedimentoPage(0); setTimeout(() => setPedimentoPage(currentPage), 100); } catch (error) { console.error('Error durante la eliminación masiva:', error); showMessage(`Error durante la eliminación: ${error.message}`, 'error'); } }; // Resetear página cuando cambien los filtros de pedimento useEffect(() => { setPedimentoPage(1); }, [pedimentoFilters]); // 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); // Agregar estado para el modal de documentos const [showDocumentsModal, setShowDocumentsModal] = useState(false); const [selectedVUDocuments, setSelectedVUDocuments] = useState([]); const [selectedVUNumber, setSelectedVUNumber] = useState(''); // Función para manejar la visualización de documentos COVE const handleShowCoveDocuments = (cove) => { if (cove.documentos && cove.documentos.length > 0) { setSelectedVUDocuments(cove.documentos); setSelectedVUNumber(cove.numero_cove); setShowDocumentsModal(true); } }; // Función para manejar la visualización de documentos COVE const handleShowPartidaDocuments = (partida) => { if (partida.documentos && partida.documentos.length > 0) { setSelectedVUDocuments(partida.documentos); setSelectedVUNumber(partida.numero_partida); setShowDocumentsModal(true); } }; // Función para manejar la visualización de Edocuments const handleShowEDocuments = (edocument) => { if (edocument.documentos && edocument.documentos.length > 0) { setSelectedVUDocuments(edocument.documentos); setSelectedVUNumber(edocument.numero_edocument); setShowDocumentsModal(true); } }; // Función para obtener el nombre de pestaña legible const getTabName = (tabKey) => { const tabNames = { 'pedimento': 'Pedimento', 'partidas': 'Partida', 'coves': 'COVE', 'edocs': 'EDoc', 'documentos': 'Documentos', 'auditor': 'Auditor' }; return tabNames[tabKey] || tabKey; }; // 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 handlePreviewVU = 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); } }; // 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); } }; // Funciones para el nuevo diseño de documentos const downloadAllPedimento = async () => { setDownloadingAllPedimento(true); try { const allDocIds = peddocuments.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 { setDownloadingAllPedimento(false); } }; const previewDocumentVU = async (doc) => { // Cerrar el modal primero setShowDocumentsModal(false); setSelectedVUDocuments([]); setSelectedVUNumber(''); await handlePreviewVU(doc); }; const downloadAllVU = async () => { setDownloadingAll(true); try { const allDocIds = selectedVUDocuments.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 downloadDocumentVU = async (doc) => { const fileName = doc.archivo ? doc.archivo.split('/').pop() : `documento_${doc.id}`; await downloadFile(doc.id, fileName, showMessage); }; 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', 6: 'APP-EFC' }; 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'); } }; // Funciones para subir documentos const handleFileSelect = (event) => { const files = Array.from(event.target.files); setSelectedFiles(files); }; const handleUploadDocuments = async () => { if (selectedFiles.length === 0) { showMessage('Por favor selecciona al menos un archivo', 'warning'); return; } setUploadingDocuments(true); try { const formData = new FormData(); // Agregar el ID del pedimento formData.append('pedimento_id', id); // Agregar archivos al FormData selectedFiles.forEach((file) => { formData.append('files', file); }); showMessage(`Subiendo ${selectedFiles.length} archivo(s)...`, 'info'); const result = await postFormDataWithAuth(`${API_URL}/record/documents/bulk-upload/`, formData); showMessage( `${result.uploaded_count || selectedFiles.length} archivo(s) subido(s) exitosamente`, 'success' ); // Limpiar archivos seleccionados y cerrar modal setSelectedFiles([]); setShowUploadModal(false); // Forzar recarga de documentos const currentPage = page; setPage(0); setTimeout(() => setPage(currentPage), 100); } catch (error) { console.error('Error durante la subida:', error); showMessage(`Error durante la subida: ${error.message}`, 'error'); } finally { setUploadingDocuments(false); } }; // 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; // Filtro para identificar modulo de pedimento apiFilters.modulo = 'expedientes-detalle-pedimentos'; // 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 Pedimento T2025-10-152 documents useEffect(() => { if (!id || activeTab !== 'pedimento') return; setPedimentoLoading(true); setPedimentoError(''); // Construir parámetros de filtros para pedimento completo const apiFilters = {}; if (pedimentoFilters.document_type) apiFilters.document_type = pedimentoFilters.document_type; if (pedimentoFilters.name) apiFilters.archivo__icontains = pedimentoFilters.name; if (pedimentoFilters.extension) apiFilters.extension = pedimentoFilters.extension; if (pedimentoFilters.date) apiFilters.created_at__date = pedimentoFilters.date; if (pedimentoFilters.created_at__gte) apiFilters.created_at__gte = pedimentoFilters.created_at__gte; if (pedimentoFilters.created_at__lte) apiFilters.created_at__lte = pedimentoFilters.created_at__lte; if (pedimentoFilters.fuente) apiFilters.fuente = pedimentoFilters.fuente; if (pedimentoFilters.pedimento_numero) apiFilters.pedimento_numero__icontains = pedimentoFilters.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; } fetchPedimentoCompleto(id, pedimentoPage, pedimentoPageSize,apiFilters) .then((data) => { setPedDocuments(data.results); setPedDocsCount(data.count); setPedimentoNext(data.next); setPedimentoPrev(data.previous); setPedimentoLoading(false); }) .catch(err => { console.error('Error fetching Pedimento Completo:', err); if (err.message === 'SESSION_EXPIRED') { showMessage('Tu sesión ha expirado, por favor inicia sesión de nuevo.', 'error'); } else { setPedimentoError(err.message); } setPedimentoLoading(false); }); },[id, activeTab, pedimentoPage, pedimentoPageSize, showMessage, pedimentoFilters, documentTypeFilter, fileNameFilter, extensionFilter, dateFilter, orderBy, orderDir]); // 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 // Descargar todos los AcuseCoves // Ejecutar la acción de procesar AcuseCove para todos los COVEs visibles // Ejecutar la acción de procesar AcuseCove solo para los que no están descargados const handleDownloadAllAcuseCoves = async () => { if (!coves || coves.length === 0) { showMessage('No hay AcuseCoves por descargar', 'info'); return; } const pendientes = coves.filter(cove => !cove.acuse_cove_descargado); if (pendientes.length === 0) { showMessage('No hay AcuseCoves pendientes por descargar', 'info'); return; } let successCount = 0; let errorCount = 0; for (const cove of pendientes) { try { await handleAcuseCoveProcess(cove); successCount++; } catch (error) { errorCount++; } } if (successCount > 0) { showMessage(`${successCount} AcuseCove(s) procesados exitosamente`, 'success'); } if (errorCount > 0) { showMessage(`${errorCount} AcuseCove(s) no se pudieron procesar`, 'error'); } }; // Ejecutar la acción de procesar COVE solo para los que no están descargados const handleDownloadAllCoves = async () => { if (!coves || coves.length === 0) { showMessage('No hay COVEs por descargar', 'info'); return; } const pendientes = coves.filter(cove => !cove.cove_descargado); if (pendientes.length === 0) { showMessage('No hay COVEs pendientes por descargar', 'info'); return; } let successCount = 0; let errorCount = 0; for (const cove of pendientes) { try { await handleCoveProcess(cove); successCount++; } catch (error) { errorCount++; } } if (successCount > 0) { showMessage(`${successCount} COVE(s) procesados exitosamente`, 'success'); } if (errorCount > 0) { showMessage(`${errorCount} COVE(s) no se pudieron procesar`, 'error'); } }; 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); }; // Función para eliminar documentos seleccionados de partida vu const handleDeleteSelectedPartidaVU= async () => { if (selectedPartidas.length === 0) { showMessage('No hay documentos seleccionados para eliminar', 'warning'); return; } try { showMessage(`Eliminando ${selectedPartidas.length} documento(s)...`, 'info'); const response = await fetchWithAuth(`${API_URL}/record/documents/bulk-delete-partidas-vu/`, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ ids: selectedPartidas }) }); if (!response.ok) { const errorData = await response.json().catch(() => null); throw new Error(errorData?.detail || errorData?.message || errorData?.error || `Error ${response.status}: ${response.statusText}`); } const result = await response.json(); showMessage( `${result.deleted_count || selectedPartidas.length} documento(s) eliminado(s) exitosamente`, 'success' ); setSelectedPartidas([]); // Forzar recarga de documentos const currentPage = partidasPage; setPartidasPage(0); setTimeout(() => setPartidasPage(currentPage), 100); } catch (error) { showMessage(`Error durante la eliminación: ${error.message}`, 'error'); } }; //--- Funciones de seleccion de Coves --- // const [selectedCoves, setSelectedCoves] = useState([]); const allCoveIds=coves.map(cove => cove.id); const allCovesSelected=selectedCoves.length === allCoveIds.length && allCoveIds.length > 0; const handleSelectAllCoves = () => { if (allCovesSelected) setSelectedCoves([]); else setSelectedCoves(allCoveIds); }; const handleSelectCove = (id) => { setSelectedCoves(prev => prev.includes(id) ? prev.filter(x => x !== id) : [...prev, id]); }; // Función para eliminar documentos seleccionados de coves vu const handleDeleteSelectedCovesVU= async () => { if (selectedCoves.length === 0) { showMessage('No hay documentos seleccionados para eliminar', 'warning'); return; } try { showMessage(`Eliminando ${selectedCoves.length} documento(s)...`, 'info'); const response = await fetchWithAuth(`${API_URL}/record/documents/bulk-delete-coves-vu/`, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ ids: selectedCoves }) }); if (!response.ok) { const errorData = await response.json().catch(() => null); throw new Error(errorData?.detail || errorData?.message || errorData?.error || `Error ${response.status}: ${response.statusText}`); } const result = await response.json(); showMessage( `${result.deleted_count || selectedCoves.length} documento(s) eliminado(s) exitosamente`, 'success' ); setSelectedCoves([]); // Forzar recarga de documentos // const currentPage = covesPage; setCovesPage(0); setTimeout(() => setCovesPage(1), 100); } catch (error) { showMessage(`Error durante la eliminación: ${error.message}`, 'error'); } }; //--- Funciones seleccion de Pedimentos --- // const [selectedPedimentos, setSelectedPedimentos] = useState([]); const [isSelectAllPedimentos, setIsSelectAllPedimentos] = useState(false); const allPedimentoIds=peddocuments.map(pedimento => pedimento.id); const allPedimentosSelected=selectedPedimentos.length === allPedimentoIds.length && allPedimentoIds.length > 0; const handleSelectAllPedimentos = () => { if (allPedimentosSelected) { setSelectedPedimentos([]); setIsSelectAllPedimentos(false); } else{ setSelectedPedimentos(allPedimentoIds); setIsSelectAllPedimentos(true); } }; const handleSelectPedimento = (id) => { setSelectedPedimentos(prev => prev.includes(id) ? prev.filter(x => x !== id) : [...prev, id]); }; // Función para eliminar documentos seleccionados de pedimento vu const handleDeleteSelectedPedimentosVU= async () => { if (selectedPedimentos.length === 0) { showMessage('No hay documentos seleccionados para eliminar', 'warning'); return; } try { showMessage(`Eliminando ${selectedPedimentos.length} documento(s)...`, 'info'); const response = await fetchWithAuth(`${API_URL}/record/documents/bulk-delete/`, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ ids: selectedPedimentos }) }); 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 || selectedPedimentos.length} documento(s) eliminado(s) exitosamente`, 'success' ); setSelectedPedimentos([]); // Forzar recarga de documentos const currentPage = pedimentoPage; setPedimentoPage(0); setTimeout(() => setPedimentoPage(currentPage), 100); } catch (error) { showMessage(`Error durante la eliminación: ${error.message}`, 'error'); } }; //--- Funciones seleccion de Edocuments --- // const [selectedEdocs, setSelectedEdocs] = useState([]); const allEdocIds=edocs.map(edoc => edoc.id); const allEdocsSelected=selectedEdocs.length === allEdocIds.length && allEdocIds.length > 0; const handleSelectAllEdocs = () => { if (allEdocsSelected) setSelectedEdocs([]); else setSelectedEdocs(allEdocIds); }; const handleSelectEdoc = (id) => { setSelectedEdocs(prev => prev.includes(id) ? prev.filter(x => x !== id) : [...prev, id]); }; // Función para eliminar documentos seleccionados de partida vu const handleDeleteSelectedEdocsVU= async () => { if (selectedEdocs.length === 0) { showMessage('No hay documentos seleccionados para eliminar', 'warning'); return; } try { showMessage(`Eliminando ${selectedEdocs.length} documento(s)...`, 'info'); const response = await fetchWithAuth(`${API_URL}/record/documents/bulk-delete-edocs-vu/`, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ ids: selectedEdocs }) }); if (!response.ok) { const errorData = await response.json().catch(() => null); throw new Error(errorData?.detail || errorData?.error || errorData?.message || `Error ${response.status}: ${response.statusText}`); } const result = await response.json(); showMessage( `${result.deleted_count || selectedEdocs.length} documento(s) eliminado(s) exitosamente`, 'success' ); setSelectedEdocs([]); // Forzar recarga de documentos const currentPage = edocsPage; setEdocsPage(0); setTimeout(() => setEdocsPage(currentPage), 100); } catch (error) { showMessage(`Error durante la eliminación: ${error.message}`, 'error'); } }; // 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); } }; //--- Carga de documentos a partidas ---// // Estados adicionales para subir documentos a partida const [uploadingToPartida, setUploadingToPartida] = useState(null); const [selectedPartidaForUpload, setSelectedPartidaForUpload] = useState(null); const [uploadingToDocumento, setUploadingToDocumento] = useState(null); const [selectedDocumentForUpload, setSelectedDocumentForUpload] = useState(null); const [uploadModalOpen, setUploadModalOpen] = useState(false); const [uploadFiles, setUploadFiles] = useState([]); // Función para manejar la carga de documentos a una partida const handleUploadToPartida = (partida) => { console.log(partida); // setSelectedPartidaForUpload(partida); partida.tab = 'partida'; partida.numero = partida.numero_partida; setSelectedDocumentForUpload(partida); setUploadFiles([]); setUploadModalOpen(true); }; // Función para subir documentos a la partida seleccionada const handleUploadFilesToPartida = async () => { if (!selectedPartidaForUpload || uploadFiles.length === 0) { showMessage('Por favor selecciona al menos un archivo', 'warning'); return; } setUploadingToPartida(selectedPartidaForUpload.id); try{ const formData = new FormData(); // Agregar el ID de la partida formData.append('partida_id', selectedPartidaForUpload.id); // Agregar el ID del pedimento (por si acaso) formData.append('pedimento_id', id); // Agregar archivos al FormData uploadFiles.forEach((file) => { formData.append('files', file); }); showMessage(`Subiendo ${uploadFiles.length} archivo(s) a la partida ${selectedPartidaForUpload.numero_partida}...`, 'info'); // const result = await postFormDataWithAuth(`${API_URL}/record/documents/bulk-upload/`, formData); // showMessage( // `${result.uploaded_count || uploadFiles.length} archivo(s) subido(s) exitosamente a la partida ${selectedPartidaForUpload.numero_partida}`, // 'success' // ); // Recargar las partidas para mostrar los nuevos documentos fetchPedimentoPartidas(partidasPage, partidasPageSize, partidasFilters) .then((data) => { setPartidas(data.results || []); setPartidasCount(data.count || 0); }) .catch(err => { console.error('Error recargando partidas:', err); }); // Limpiar y cerrar setSelectedPartidaForUpload(null); setUploadFiles([]); setUploadModalOpen(false) }catch (error){ showMessage(`Error durante la subida: ${error.message}`, 'error'); } finally { setUploadingToPartida(null); } }; // Función para manejar la selección de archivos const handleUploadFileSelect = (event, fileExtension, seccion) => { if (!event.target.files) return; // Evitar errores si es null try{ const selectedFiles = Array.from(event.target.files).filter(file => file?.name?.toLowerCase().endsWith(fileExtension) ); if (selectedFiles.length < event.target.files.length) { showMessage(`Solo se aceptan archivos ${fileExtension}`, 'warning'); return; } const filesWithSection = selectedFiles.map((file) => { const originalName = file.name; const extension = originalName.split('.').pop(); const nameWithoutExtension = originalName.replace(`.${extension}`, '') const newFileName = `${nameWithoutExtension}.${extension}.${seccion}`; // Crear nuevo objeto File con el nombre modificado const renamedFile = new File( [file], // Los datos del archivo newFileName, // Nuevo nombre { type: file.type, lastModified: file.lastModified } ); return { file: renamedFile, originalName: originalName, seccion: seccion }; }); // setUploadFiles(prevFiles => [ // ...prevFiles, // ...filesWithSection // ]); setUploadFiles(prevFiles => { // Creamos un nuevo array reemplazando archivos existentes con misma extensión y sección const updatedFiles = [...prevFiles]; filesWithSection.forEach(newFileObj => { const index = updatedFiles.findIndex(f => f.seccion === newFileObj.seccion ); if (index !== -1) { // Reemplazar archivo existente updatedFiles[index] = newFileObj; } else { // Agregar archivo nuevo updatedFiles.push(newFileObj); } }); return updatedFiles; }); // Limpiar input para permitir re-subida del mismo archivo }catch(error){ showMessage(`Error durante la subida: ${error.message}`, 'error'); }finally{ // event.target.value = null; } }; // Función para manejar la carga de documentos a un cove const handleUploadToCOVE = (cove) => { cove.tab = 'cove'; cove.numero = cove.numero_cove; // console.log(cove); setSelectedDocumentForUpload(cove); setUploadFiles([]); setUploadModalOpen(true); }; // Función para manejar la carga de documentos a un Edocument const handleUploadToEdoc = (edoc) => { edoc.tab = 'edoc'; edoc.numero = edoc.numero_edocument; // console.log(edoc); setSelectedDocumentForUpload(edoc); setUploadFiles([]); setUploadModalOpen(true); }; // Función para manejar la carga de documentos a un Edocument const handleUploadFilesToDocumento = async (tab) => { console.log(tab); if (!selectedDocumentForUpload || uploadFiles.length === 0) { showMessage('Por favor selecciona al menos un archivo', 'warning'); return; } setUploadingToDocumento(selectedDocumentForUpload.id); try{ const formData = new FormData(); // Agregar el ID del pedimento (por si acaso) formData.append('pedimento_id', selectedDocumentForUpload.pedimento); // Agregar el ID de la partida formData.append('expediente_vu_id', selectedDocumentForUpload.id); // Agregar archivos al FormData // uploadFiles.forEach((file, seccion ) => { // formData.append('files', file); // formData.append('seccion', seccion); // }); uploadFiles.forEach((fileObj, index) => { formData.append('files', fileObj.file); // Mismo nombre para todos los archivos // Si necesitas enviar secciones para cada archivo, puedes hacerlo así: formData.append(`secciones[${index}]`, fileObj.seccion); }); if (tab === 'partida'){ formData.append('tab_seccion', 'partida'); formData.append('numero', selectedDocumentForUpload.numero_partida); showMessage(`Subiendo ${uploadFiles.length} archivo(s) a la partida ${selectedDocumentForUpload.numero_partida}...`, 'info'); }else if(tab === 'cove'){ formData.append('tab_seccion', 'cove'); formData.append('numero', selectedDocumentForUpload.numero_cove); showMessage(`Subiendo ${uploadFiles.length} archivo(s) a la partida ${selectedDocumentForUpload.numero_cove}...`, 'info'); }else if(tab === 'edoc'){ formData.append('tab_seccion', 'edoc'); formData.append('numero', selectedDocumentForUpload.numero_edocument); showMessage(`Subiendo ${uploadFiles.length} archivo(s) a la partida ${selectedDocumentForUpload.numero_edocument}...`, 'info'); }else{ showMessage('Carga de archivos a seccion desconocida', 'error'); return; } const result = await postFormDataWithAuth(`${API_URL}/record/documents/bulk-upload-vu/`, formData); if(!result.ok){ const errorData = await result.json(); throw new Error(errorData.error || 'Error desconocido'); } showMessage( `${result.uploaded_count || uploadFiles.length} archivo(s) subido(s) exitosamente a ${tab}`, 'success' ); // // Recargar las partidas para mostrar los nuevos documentos // fetchPedimentoPartidas(partidasPage, partidasPageSize, partidasFilters) // .then((data) => { // setPartidas(data.results || []); // setPartidasCount(data.count || 0); // }) // .catch(err => { // console.error('Error recargando partidas:', err); // }); }catch (error){ showMessage(`Error durante la subida: ${error.message}`, 'error'); } finally { // Limpiar y cerrar setSelectedDocumentForUpload(null); setUploadFiles([]); setUploadModalOpen(false) setUploadingToDocumento(null); } }; const isPartida = selectedDocumentForUpload?.tab === 'partida'; const isCove = selectedDocumentForUpload?.tab === 'cove'; const isEdoc = selectedDocumentForUpload?.tab === 'edoc'; // Estados para documentos de errores VU const [errorDocuments, setErrorDocuments] = useState([]); const [errorDocsCount, setErrorDocsCount] = useState(0); const [errorDocsLoading, setErrorDocsLoading] = useState(true); const [errorDocsError, setErrorDocsError] = useState(''); const [errorDocsPage, setErrorDocsPage] = useState(1); const [errorDocsPageSize, setErrorDocsPageSize] = useState(10); const [selectedErrorDocuments, setSelectedErrorDocuments] = useState([]); const [isSelectAllErrorDocs, setIsSelectAllErrorDocs] = useState(false); const [downloadingAllErrors, setDownloadingAllErrors] = useState(false); // Filtros para errores VU const [errorFilters, setErrorFilters] = useState({ name: '', document_type: '', extension: '', date: '', created_at__gte: '', created_at__lte: '', fuente: '', pedimento_numero: '', tipo_error: '' }); // Estado para mostrar filtros de errores const [showErrorFilters, setShowErrorFilters] = useState(false); // Efecto para cargar documentos de errores VU useEffect(() => { // Función para obtener documentos de errores VU usando el endpoint específico const fetchErrorDocuments = async (pedimentoId,page = 1, pageSize = 10, filters = {}) => { try { if (!id || !pedimento?.pedimento_app) return; // Construir parámetros de filtros const params = new URLSearchParams({ pedimento: pedimento.pedimento_app, // Usar pedimento_app como requiere el endpoint pedimentoId: pedimentoId, // document_type_id: [14,16,18,20,22,24,26], IDs de tipos de documentos de error VU page: page, page_size: pageSize }); // Solo agregar filtros que tengan valores Object.entries(filters).forEach(([key, value]) => { if (value && value.toString().trim() !== '') { params.append(key, value); } }); // Usar el endpoint específico para documentos de error VU const response = await fetchWithAuth( `${API_URL}/record/documents/vu-documentos-errores/?${params}` ); if (!response.ok) { const errorData = await response.json().catch(() => ({})); throw new Error(errorData.error || `Error ${response.status} al obtener documentos de error VU`); } const data = await response.json(); // El endpoint devuelve un array directo, no un objeto paginado // Aplicamos paginación manualmente en el frontend if (Array.isArray(data)) { const startIndex = (page - 1) * pageSize; const endIndex = startIndex + pageSize; const paginatedResults = data.slice(startIndex, endIndex); return { results: paginatedResults, count: data.length, next: endIndex < data.length ? page + 1 : null, previous: page > 1 ? page - 1 : null }; } // Si por alguna razón devuelve un objeto paginado return data; } catch (error) { console.error('Error fetching error documents:', error); throw error; } }; if (!id || !pedimento || activeTab !== 'errores') return; console.log('Fetching error documents for pedimento:', pedimento.pedimento_app); setErrorDocsLoading(true); setErrorDocsError(''); // Construir parámetros de filtros const apiFilters = {}; if (errorFilters.name) apiFilters.archivo__icontains = errorFilters.name; if (errorFilters.extension) apiFilters.extension = errorFilters.extension; if (errorFilters.date) apiFilters.created_at__date = errorFilters.date; if (errorFilters.created_at__gte) apiFilters.created_at__gte = errorFilters.created_at__gte; if (errorFilters.created_at__lte) apiFilters.created_at__lte = errorFilters.created_at__lte; if (errorFilters.fuente) apiFilters.fuente = errorFilters.fuente; if (errorFilters.tipo_error) { apiFilters.tipo_error = errorFilters.tipo_error; }else{ apiFilters.tipo_error = [14,16,18,20,22,24,26]; } // NOTA: No necesitamos document_type porque el endpoint ya filtra por documentos de error VU // Tampoco necesitamos pedimento_numero porque ya estamos filtrando por pedimento fetchErrorDocuments(id,errorDocsPage, errorDocsPageSize, apiFilters) .then((data) => { console.log('Error documents data:', data); setErrorDocuments(data.results || []); setErrorDocsCount(data.count || 0); setErrorDocsLoading(false); }) .catch(err => { console.error('Error fetching error documents:', err); if (err.message === 'SESSION_EXPIRED') { showMessage('Tu sesión ha expirado, por favor inicia sesión de nuevo.', 'error'); } else { setErrorDocsError(err.message || 'Error al cargar documentos de error VU'); } setErrorDocsLoading(false); }); }, [id, activeTab, errorDocsPage, errorDocsPageSize, errorFilters, showMessage, pedimento]); // Resetear página cuando cambien los filtros de errores useEffect(() => { setErrorDocsPage(1); }, [errorFilters]); // Efecto para actualizar isSelectAllErrorDocs cuando cambia la selección useEffect(() => { if (errorDocuments.length > 0) { const allSelected = errorDocuments.every(doc => selectedErrorDocuments.includes(doc.id)); setIsSelectAllErrorDocs(allSelected && selectedErrorDocuments.length > 0); } }, [selectedErrorDocuments, errorDocuments]); // Efecto para limpiar selección cuando cambia de página useEffect(() => { setSelectedErrorDocuments([]); setIsSelectAllErrorDocs(false); }, [errorDocsPage]); // Funciones para manejo de selección múltiple de documentos de error const handleSelectErrorDocument = (documentId) => { const isSelected = selectedErrorDocuments.includes(documentId); if (isSelected) { setSelectedErrorDocuments(prev => prev.filter(id => id !== documentId)); } else { setSelectedErrorDocuments(prev => [...prev, documentId]); } }; const handleSelectAllErrorDocuments = () => { if (isSelectAllErrorDocs) { setSelectedErrorDocuments([]); setIsSelectAllErrorDocs(false); } else { const allDocumentIds = errorDocuments.map(doc => doc.id); setSelectedErrorDocuments(allDocumentIds); setIsSelectAllErrorDocs(true); } }; // Función para eliminar documentos de error seleccionados const handleDeleteSelectedErrorDocuments = async () => { if (selectedErrorDocuments.length === 0) { showMessage('No hay documentos seleccionados para eliminar', 'warning'); return; } try { showMessage(`Eliminando ${selectedErrorDocuments.length} documento(s) de error...`, 'info'); const response = await fetchWithAuth(`${API_URL}/record/documents/bulk-delete/`, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ ids: selectedErrorDocuments }) }); 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 || selectedErrorDocuments.length} documento(s) de error eliminado(s) exitosamente`, 'success' ); setSelectedErrorDocuments([]); setIsSelectAllErrorDocs(false); // Forzar recarga de documentos const currentPage = errorDocsPage; setErrorDocsPage(0); setTimeout(() => setErrorDocsPage(currentPage), 100); } catch (error) { console.error('Error durante la eliminación masiva de documentos de error:', error); showMessage(`Error durante la eliminación: ${error.message}`, 'error'); } }; // Función para descargar todos los documentos de error const downloadAllErrors = async () => { setDownloadingAllErrors(true); try { const allErrorDocIds = errorDocuments.map(doc => doc.id); await handleBulkDownload(allErrorDocIds); } catch (error) { console.error('Error downloading all error documents:', error); showMessage('Error al descargar todos los documentos de error', 'error'); } finally { setDownloadingAllErrors(false); } }; //----------------------------------------// // Estados de carga if (loading) return (

Cargando detalle del pedimento...

); if (error) return (

Error al cargar

{error}

← Volver a expedientes
); return (
{/* Header */}
Volver a la lista

Detalle de Pedimento

Información completa del pedimento y documentos asociados

{docsCount > 0 && (
📄 {docsCount} documentos
)}
{/* Información del Pedimento */} {pedimento && (

Información General

Pedimento
{pedimento.pedimento_app}
Contribuyente
{pedimento.contribuyente}
Fecha de Pago
{pedimento.fecha_pago}
)} {/* Sistema de pestañas */}
{/* Navegación de pestañas */}
{/* Contenido de las pestañas */} {activeTab === 'documentos' && (
{/* Header de la sección */} {/*
{JSON.stringify(documents, null, 2)}
*/}

Documentos Generales

{docsCount} documentos
{documents.length > 0 && ( <> )}
{/* Filtros expandibles */} {showFilters && (
setFilters(prev => ({ ...prev, name: e.target.value }))} placeholder="Nombre del archivo..." className="w-full px-3 py-2 placeholder-gray-400 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm" />
{/*
*/}
{/*
setFilters(prev => ({ ...prev, pedimento_numero: e.target.value }))} placeholder="Número de pedimento..." className="w-full px-3 py-2 placeholder-gray-400 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm" />
*/}
setFilters(prev => ({ ...prev, date: e.target.value }))} className="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm" />
)}
{/* Área de acciones para documentos seleccionados */} {selectedDocuments.length > 0 && (

{selectedDocuments.length} documento{selectedDocuments.length !== 1 ? 's' : ''} seleccionado{selectedDocuments.length !== 1 ? 's' : ''}

Selecciona una acción para continuar

)} {loading ? (
Cargando documentos...
) : error ? (

Error al cargar documentos

{error}

) : documents.length === 0 ? (

No hay documentos

No se encontraron documentos para este pedimento.

) : (
{/* Tabla de documentos */}
{documents.map((doc, index) => ( ))}
Fuente Acciones
handleSelectDocument(doc.id)} className="w-4 h-4 text-blue-600 border-gray-300 rounded focus:ring-blue-500" />
{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)} */} {doc.fuente_nombre} {formatDate(doc.created_at)}
)} {/* Paginación para documentos */} {docsCount > 0 && (
Mostrando {((page - 1) * pageSize) + 1}- {Math.min(page * pageSize, docsCount)} de / {docsCount} documentos
Página {page} de {Math.ceil(docsCount / pageSize)}
)}
)} {activeTab === 'pedimento' && (
{/* Header de la sección */}

Pedimento Completo VU

{peddocsCount} documentos
{peddocuments.length > 0 && ( <> )}
{/* Filtros expandibles */} {showFiltersPedimento && (
setPedimentoFilters(prev => ({ ...prev, name: e.target.value }))} placeholder="Nombre del archivo..." className="w-full px-3 py-2 placeholder-gray-400 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm" />
{/*
setPedimentoFilters(prev => ({ ...prev, pedimento_numero: e.target.value }))} placeholder="Número de pedimento..." className="w-full px-3 py-2 placeholder-gray-400 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm" />
*/}
setPedimentoFilters(prev => ({ ...prev, date: e.target.value }))} className="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm" />
)}
{/* Área de acciones para documentos seleccionados */} {selectedPedimentos.length > 0 && (

{selectedPedimentos.length} documento{selectedPedimentos.length !== 1 ? 's' : ''} seleccionado{selectedPedimentos.length !== 1 ? 's' : ''}

Selecciona una acción para continuar

)} {loading ? (
Cargando documentos...
) : error ? (

Error al cargar documentos

{error}

) : peddocuments.length === 0 ? (

No hay documentos VU

No se encontraron documentos de VU.

) : (
{/* Tabla de documentos */}
{peddocuments.map((doc, index) => ( ))}
Fuente Acciones
handleSelectPedimento(doc.id)} className="w-4 h-4 text-blue-600 border-gray-300 rounded focus:ring-blue-500" />
{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)}
)} {/* Paginación para documentos */} {peddocsCount > 0 && (
Mostrando {((pedimentoPage - 1) * pedimentoPageSize) + 1}- {Math.min(pedimentoPage * pedimentoPageSize, peddocsCount)} de / {peddocsCount} documentos
Página {pedimentoPage} de {Math.ceil(peddocsCount / pedimentoPageSize)}
)}
)} {activeTab === 'partidas' && (
{/*
{JSON.stringify(partidas, null, 2)}
*/} {/* Header de la sección */}

Partidas VU del Pedimento

{partidasCount} partidas
{/* Botones de acción masiva */} {/*
{selectedPartidas.length > 0 && ( )}
*/}
{/* Filtros */}
setPartidasFilters(prev => ({ ...prev, numero_partida: e.target.value }))} placeholder="Número de partida..." className="w-full px-3 py-2 placeholder-gray-400 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm" />
setPartidasFilters(prev => ({ ...prev, numero_partida__gte: e.target.value }))} placeholder="1" className="w-full px-3 py-2 placeholder-gray-400 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm" />
setPartidasFilters(prev => ({ ...prev, numero_partida__lte: e.target.value }))} placeholder={partidasCount?.toString() || "1"} className="w-full px-3 py-2 placeholder-gray-400 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm" />
setPartidasFilters(prev => ({ ...prev, created_at__gte: e.target.value }))} className="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm" />
setPartidasFilters(prev => ({ ...prev, created_at__lte: e.target.value }))} className="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm" />
{/* Área de acciones para documentos seleccionados */} {selectedPartidas.length > 0 && (

{selectedPartidas.length} documento{selectedPartidas.length !== 1 ? 's' : ''} seleccionado{selectedPartidas.length !== 1 ? 's' : ''}

Selecciona una acción para continuar

)} {partidasLoading ? (
Cargando partidas...
) : partidasError ? (

Error al cargar partidas

{partidasError}

) : !partidas || partidas.length === 0 ? (

No hay partidas

No se encontraron partidas para este pedimento.

) : (
{/* Botones de acción masiva */}
{selectedPartidas.length > 0 && ( )}
{/* Botón Descargar partidas y tabla de partidas */}
{(partidas || []).map((partida, index) => ( ))}
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) */} {/* Botón para cargar documentos a la partida */} {/* Botón Documentos VU Partidas */}
)} {/* Paginación para partidas */} {partidasCount > 0 && (
Mostrando {((partidasPage - 1) * partidasPageSize) + 1}- {Math.min(partidasPage * partidasPageSize, partidasCount)} de / {partidasCount} partidas
Página {partidasPage} de {Math.ceil((partidasCount || 0) / (partidasPageSize || 10)) || 1}
)}
)} {activeTab === 'coves' && (
{/*
{JSON.stringify(coves, null, 2)}
*/} {/* Header de la sección */}

COVEs VU del Pedimento

{covesCount} COVEs
{/* Botones para descargar todos los AcuseCoves y COVEs */} {coves.length > 0 && (
)} {/* Filtros */}
setCovesFilters(prev => ({ ...prev, numero_cove: e.target.value }))} placeholder="Número COVE..." className="w-full px-3 py-2 placeholder-gray-400 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm" />
setCovesFilters(prev => ({ ...prev, created_at__gte: e.target.value }))} className="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm" />
{/* Área de acciones para documentos seleccionados */} {selectedCoves.length > 0 && (

{selectedCoves.length} documento{selectedCoves.length !== 1 ? 's' : ''} seleccionado{selectedCoves.length !== 1 ? 's' : ''}

Selecciona una acción para continuar

)} {covesLoading ? (
Cargando COVEs...
) : covesError ? (

Error al cargar COVEs

{covesError}

) : coves.length === 0 ? (

No hay COVEs

No se encontraron COVEs para este pedimento.

) : (
{/* Tabla de COVEs */}
{coves.map((cove, index) => ( ))}
Número COVE Fecha de Creación Estado COVE Estado Acuse Acciones
handleSelectCove(cove.id)} className="w-4 h-4 text-blue-600 border-gray-300 rounded focus:ring-blue-500" />
{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 */} {/* Botón para cargar documentos al cove */} {/* Botón Documentos VU COVE */}
)} {/* Paginación para COVEs */} {covesCount > 0 && (
Mostrando {((covesPage - 1) * covesPageSize) + 1}- {Math.min(covesPage * covesPageSize, covesCount)} de / {covesCount} COVEs
Página {covesPage} de {Math.ceil(covesCount / covesPageSize)}
)}
)} {activeTab === 'edocs' && (
{/*
{JSON.stringify(edocs, null, 2)}
*/} {/* Header de la sección */}

EDocs VU del Pedimento

{edocsCount} EDocs
{/* Filtros */}
setEdocsFilters(prev => ({ ...prev, numero_edocument: e.target.value }))} placeholder="Número EDocs..." className="w-full px-3 py-2 placeholder-gray-400 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm" />
setEdocsFilters(prev => ({ ...prev, clave: e.target.value }))} placeholder="Clave..." className="w-full px-3 py-2 placeholder-gray-400 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm" />
setEdocsFilters(prev => ({ ...prev, descripcion: e.target.value }))} placeholder="Descripción..." className="w-full px-3 py-2 placeholder-gray-400 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm" />
setEdocsFilters(prev => ({ ...prev, created_at__gte: e.target.value }))} className="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm" />
setEdocsFilters(prev => ({ ...prev, created_at__lte: e.target.value }))} className="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm" />
{/* Área de acciones para documentos seleccionados */} {selectedEdocs.length > 0 && (

{selectedEdocs.length} documento{selectedEdocs.length !== 1 ? 's' : ''} seleccionado{selectedEdocs.length !== 1 ? 's' : ''}

Selecciona una acción para continuar

)} {edocsLoading ? (
Cargando EDocs...
) : edocsError ? (

Error al cargar EDocs

{edocsError}

) : edocs.length === 0 ? (

No hay EDocs

No se encontraron documentos electrónicos para este pedimento.

) : (
{/* Tabla de EDocs */}
{edocs.map((edoc, index) => ( ))}
Número EDocs Clave Descripción Fecha de Creación Estado EDocs Estado Acuse Acciones
handleSelectEdoc(edoc.id)} className="w-4 h-4 text-blue-600 border-gray-300 rounded focus:ring-blue-500" />
{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 */} {/* Botón para cargar documentos al cove */} {/* Botón Documentos VU Edocs */}
)} {/* Paginación para EDocs */} {edocsCount > 0 && (
Mostrando {((edocsPage - 1) * edocsPageSize) + 1} a{' '} {Math.min(edocsPage * edocsPageSize, edocsCount)} de{' '} {edocsCount} EDocs
Página {edocsPage} de {Math.ceil(edocsCount / edocsPageSize)}
)}
)} {activeTab === 'auditor' && (
{/* Header de la sección */}

Auditoría del Pedimento

{/* Contenido del Auditor */}
{/* Resumen de Estado */}

Resumen de Estado del Pedimento

{dashboardSummary ? ( <> {/* Indicador de Cumplimiento Principal */}
Cumplimiento Total {dashboardSummary.cumplimiento_total.toFixed(1)}%
{/* Grid de Estadísticas */}
{/* Pedimentos */}
Pedimentos
Completos {dashboardSummary.pedimentos.completos}
Pendientes {dashboardSummary.pedimentos.pendientes}
Cumplimiento {dashboardSummary.pedimentos.cumplimiento.toFixed(1)}%
{/* COVEs */}
COVEs
Procesados {dashboardSummary?.coves?.coves_procesados || 0}
Pendientes {dashboardSummary?.coves?.coves_pendientes || 0}
Cumplimiento {(dashboardSummary?.coves?.coves_cumplimiento || 0).toFixed(1)}%
{/* Acuses de COVEs */}
Acuses de COVEs
Procesados {dashboardSummary?.acuse_coves?.acuse_coves_procesados || 0}
Pendientes {dashboardSummary?.acuse_coves?.acuse_coves_pendientes || 0}
Cumplimiento {(dashboardSummary?.acuse_coves?.acuse_coves_cumplimiento || 0).toFixed(1)}%
{/* E-Documents */}
E-Documents
Descargados {dashboardSummary.edocuments.edocs_descargados}
Pendientes {dashboardSummary.edocuments.edocs_pendientes}
Cumplimiento {dashboardSummary.edocuments.edocs_cumplimiento.toFixed(1)}%
{/* Acuses */}
Acuses
Descargados {dashboardSummary?.acuses?.acuse_descargados || 0}
Pendientes {dashboardSummary?.acuses?.acuses_pendientes || 0}
Cumplimiento {(dashboardSummary?.acuses?.acuses_cumplimiento || 0).toFixed(1)}%
{/* Partidas */}
Partidas
Descargadas {dashboardSummary.partidas.partidas_descargadas}
Pendientes {dashboardSummary.partidas.partidas_pendientes}
Cumplimiento {dashboardSummary.partidas.cumplimiento.toFixed(1)}%
{/* Estadísticas Generales */}
Estadísticas Generales
Total Documentos {dashboardSummary.documentos.descargados}
Total Remesas {dashboardSummary.remesas.total}
Total Partidas {dashboardSummary.partidas.total}
) : (

No hay datos disponibles

Intente recargar la página o contacte a soporte si el problema persiste.

)}
{/* Análisis de Integridad */}

Análisis de Integridad

{dashboardSummary && ( <> {/* Estado General del Pedimento */}
= 100 ? 'bg-green-100' : dashboardSummary.cumplimiento_total >= 75 ? 'bg-yellow-100' : 'bg-red-100' }`}> = 100 ? 'text-green-600' : dashboardSummary.cumplimiento_total >= 75 ? 'text-yellow-600' : 'text-red-600' }`} fill="none" stroke="currentColor" viewBox="0 0 24 24"> = 100 ? "M5 13l4 4L19 7" // check mark : dashboardSummary.cumplimiento_total >= 75 ? "M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" // warning : "M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" // error } />
Estado del Pedimento

{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'}

= 100 ? 'bg-green-100 text-green-800' : dashboardSummary.cumplimiento_total >= 75 ? 'bg-yellow-100 text-yellow-800' : 'bg-red-100 text-red-800' }`}> {dashboardSummary.cumplimiento_total.toFixed(1)}% Completado
{/* Análisis Detallado */}
{/* Documentos Base */}
= 100 ? 'bg-green-50 border-green-200' : 'bg-yellow-50 border-yellow-200' }`}>
= 100 ? 'text-green-600' : 'text-yellow-600' }`} fill="none" stroke="currentColor" viewBox="0 0 24 24">
= 100 ? 'text-green-800' : 'text-yellow-800' }`}>Documentos Base

{`${dashboardSummary.pedimentos.completos} de ${dashboardSummary.pedimentos.total} documentos procesados`}

{/* Validación de COVEs */}
= 100 && dashboardSummary.acuse_coves.acuse_coves_cumplimiento >= 100) ? 'bg-green-50 border-green-200' : 'bg-yellow-50 border-yellow-200' }`}>
= 100 && dashboardSummary.acuse_coves.acuse_coves_cumplimiento >= 100) ? 'text-green-600' : 'text-yellow-600' }`} fill="none" stroke="currentColor" viewBox="0 0 24 24">
= 100 && dashboardSummary.acuse_coves.acuse_coves_cumplimiento >= 100) ? 'text-green-800' : 'text-yellow-800' }`}>Validación de COVEs

{`COVEs: ${dashboardSummary.coves.coves_procesados} de ${dashboardSummary.coves.total} procesados`}

{`Acuses: ${dashboardSummary.acuse_coves.acuse_coves_procesados} de ${dashboardSummary.acuse_coves.total} procesados`}

{/* E-Documents y Acuses */}
= 100 && dashboardSummary.acuses.acuses_cumplimiento >= 100) ? 'bg-green-50 border-green-200' : 'bg-yellow-50 border-yellow-200' }`}>
= 100 && dashboardSummary.acuses.acuses_cumplimiento >= 100) ? 'text-green-600' : 'text-yellow-600' }`} fill="none" stroke="currentColor" viewBox="0 0 24 24">
= 100 && dashboardSummary.acuses.acuses_cumplimiento >= 100) ? 'text-green-800' : 'text-yellow-800' }`}>E-Documents y Acuses

{`E-Docs: ${dashboardSummary.edocuments.edocs_descargados} de ${dashboardSummary.edocuments.total} descargados`}

{`Acuses: ${dashboardSummary.acuses.acuse_descargados} de ${dashboardSummary.acuses.total} descargados`}

{/* Partidas */}
= 100 ? 'bg-green-50 border-green-200' : 'bg-yellow-50 border-yellow-200' }`}>
= 100 ? 'text-green-600' : 'text-yellow-600' }`} fill="none" stroke="currentColor" viewBox="0 0 24 24">
= 100 ? 'text-green-800' : 'text-yellow-800' }`}>Validación de Partidas

{`${dashboardSummary.partidas.partidas_descargadas} de ${dashboardSummary.partidas.total} partidas procesadas`}

{/* Recomendaciones */} {(dashboardSummary.cumplimiento_total < 100) && (
Recomendaciones
    {dashboardSummary.coves.coves_cumplimiento < 100 && (
  • • Procesar los COVEs pendientes ({dashboardSummary.coves.coves_pendientes} restantes)
  • )} {dashboardSummary.acuse_coves.acuse_coves_cumplimiento < 100 && (
  • • Obtener los acuses de COVEs faltantes ({dashboardSummary.acuse_coves.acuse_coves_pendientes} restantes)
  • )} {dashboardSummary.edocuments.edocs_cumplimiento < 100 && (
  • • Completar la descarga de E-Documents ({dashboardSummary.edocuments.edocs_pendientes} pendientes)
  • )} {dashboardSummary.acuses.acuses_cumplimiento < 100 && (
  • • Obtener los acuses de E-Documents ({dashboardSummary.acuses.acuses_pendientes} pendientes)
  • )} {dashboardSummary.partidas.cumplimiento < 100 && (
  • • Procesar las partidas pendientes ({dashboardSummary.partidas.partidas_pendientes} restantes)
  • )}
)} )}
{/* Timeline de Actividades */}

Timeline de Actividades Recientes

Trabajando en ello

Estamos preparando el historial de actividades

Esta funcionalidad estará disponible próximamente

)} {/* NUEVO CONTENIDO PARA TAB DE ERRORES VU */} {activeTab === 'errores' && (
{/* Header de la sección */}

Documentos de Error VU

{errorDocsCount} documentos
{errorDocuments.length > 0 && ( <> )}
{/* Filtros expandibles para errores */} {showErrorFilters && (
setErrorFilters(prev => ({ ...prev, name: e.target.value }))} placeholder="Nombre del archivo..." className="w-full px-3 py-2 placeholder-gray-400 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-red-500 focus:border-red-500 sm:text-sm" />
{/*
setErrorFilters(prev => ({ ...prev, date: e.target.value }))} className="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-red-500 focus:border-red-500 sm:text-sm" />
*/}
)}
{/* Área de acciones para documentos de error seleccionados */} {selectedErrorDocuments.length > 0 && (

{selectedErrorDocuments.length} documento{selectedErrorDocuments.length !== 1 ? 's' : ''} de error seleccionado{selectedErrorDocuments.length !== 1 ? 's' : ''}

Selecciona una acción para continuar

)} {errorDocsLoading ? (
Cargando documentos de error...
) : errorDocsError ? (

Error al cargar documentos de error

{errorDocsError}

) : errorDocuments.length === 0 ? (

No hay documentos de error

No se encontraron documentos de error VU para este pedimento.

) : (
{/* Tabla de documentos de error */}
{errorDocuments.map((doc, index) => ( ))}
Documento Tipo Extensión Tamaño Fuente Fecha de Creación Acciones
handleSelectErrorDocument(doc.id)} className="w-4 h-4 text-red-600 border-gray-300 rounded focus:ring-red-500" />
{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)} {doc.fuente_nombre} {formatDate(doc.created_at)}
)} {/* Paginación para documentos de error */} {errorDocsCount > 0 && (
Mostrando {((errorDocsPage - 1) * errorDocsPageSize) + 1}- {Math.min(errorDocsPage * errorDocsPageSize, errorDocsCount)} de / {errorDocsCount} documentos
Página {errorDocsPage} de {Math.ceil(errorDocsCount / errorDocsPageSize)}
)}
)}
{/* Modal de vista previa mejorado */} {previewOpen && (
{/* Header del modal con gradiente e información del archivo */}

Vista previa de documento

{previewDoc && (
{previewDoc.archivo_original || 'Sin nombre'} {previewDoc.size && ( {formatFileSize(previewDoc.size)} )} {previewDoc.extension && ( {previewDoc.extension} )}
)}
{/* Controles del header */}
{/* Botón de descarga siempre visible */} {previewDoc && ( )} {/* Controles de zoom para imágenes */} {previewType === 'img' && (
{Math.round(imageZoom * 100)}%
)} {/* Botón cerrar */}
{/* Contenido del modal */}
{previewLoading ? (
Cargando documento...

Preparando vista previa

) : previewError ? (

Error al cargar documento

{previewError}

) : previewType === 'pdf' ? (