diff --git a/src/pages/PedimentoDetail.jsx b/src/pages/PedimentoDetail.jsx index c47fc85..2277e32 100644 --- a/src/pages/PedimentoDetail.jsx +++ b/src/pages/PedimentoDetail.jsx @@ -2196,22 +2196,7 @@ export default function PedimentoDetail() { - + - )} - - {/* Otros servicios disponibles */} - {existeServicio(3) && ( -
- {/* Generación COVEs */} - {!existeServicio(4) && ( - - )} - - {/* Generación EDocs */} - {!existeServicio(5) && ( - - )} -
- )} - - - - {procesosLoading ? ( -
-
- Cargando procesos... -
- ) : procesosError ? ( -
-
- - - -
-

Error al cargar procesos

-

{procesosError}

-
- ) : procesos.length === 0 ? ( -
- - - -

No hay procesos

-

- No se encontraron procesos para este pedimento. -

-
- ) : ( -
- {/* Tabla de Procesos */} -
- - -
-
-
- - - - - - - - - - - - - - {procesos.map((proceso, index) => ( - - - - - - - - - - - ))} - -
- ID Proceso - - Servicio - - Estado - - Organización - - Fecha Creación - - Última Actualización - - Acciones -
-
- handleSelectProceso(proceso.task_id, e.target.checked)} - disabled={proceso.status === 'running' || proceso.status === 'completed'} - className="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded disabled:opacity-50" - /> -
-
-
-
-
- - - -
-
-
-
- #{proceso.task_id} -
-
- {proceso.pedimento_app} -
-
-
-
- - {getServicioLabel(proceso.servicio)} - - - - {getTaskStatusLabel(proceso.status)} - - {proceso.status === 'running' && ( - - - - - - - )} - - {proceso.organizacion_name || 'N/A'} - - {formatDate(proceso.created_at)} - - {formatDate(proceso.updated_at)} - -
- {/* Botón Play (Ejecutar Servicio) */} - {isTaskActionable(proceso.status) && ( - - )} - - {/* Botón Retry (Reintentar) */} - {proceso.status === 'failed' && ( - - )} -
-
-
-
-
- )} - - {/* Paginación para Procesos */} - {procesosCount > 0 && ( -
-
- - Mostrando - {((procesosPage - 1) * procesosPageSize) + 1}- - {Math.min(procesosPage * procesosPageSize, procesosCount)} - de - / - {procesosCount} - procesos - - -
- -
- - - - Página {procesosPage} de {Math.ceil(procesosCount / procesosPageSize)} - - - -
-
- )} - - )} {activeTab === 'auditor' && (
@@ -4200,102 +3785,6 @@ export default function PedimentoDetail() {

Auditoría del Pedimento

- - Análisis Completo - -
- - - - {/* Botones de Auditoría */} -
- - - - - - - - - - - - - - -
diff --git a/src/pages/Reports.jsx b/src/pages/Reports.jsx index 0c55695..f79e9f2 100644 --- a/src/pages/Reports.jsx +++ b/src/pages/Reports.jsx @@ -1,4 +1,12 @@ import React, { useState, useEffect } from 'react'; +import { getCurrentUser } from '../api/users'; +// Helper to get current user with fetchWithAuth +const fetchCurrentUserWithAuth = async () => { + const url = `${API_URL}/user/users/me/`; + const res = await fetchWithAuth(url); + if (!res.ok) throw new Error('No se pudo obtener el usuario actual'); + return res.json(); +}; import { fetchWithAuth } from '../fetchWithAuth'; import { useNotification } from '../context/NotificationContext'; import datastageModelsData from '../data/datastageModels.json'; @@ -34,31 +42,127 @@ if (typeof document !== 'undefined' && !document.getElementById('reports-animati document.head.appendChild(style); } +const handleDownloadReport = async (reportId) => { + try { + const url = `${import.meta.env.VITE_EFC_API_URL}/reports/report-document-download/${reportId}/`; + const res = await fetchWithAuth(url); + if (!res.ok) throw new Error('Error al descargar el reporte'); + const blob = await res.blob(); + let filename = `reporte_${reportId}.csv`; + const disposition = res.headers.get('Content-Disposition'); + if (disposition && disposition.includes('filename=')) { + filename = disposition.split('filename=')[1].replace(/"/g, '').trim(); + } + const link = document.createElement('a'); + link.href = window.URL.createObjectURL(blob); + link.download = filename; + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); + } catch (err) { + alert('No se pudo descargar el reporte.'); + } +}; + export default function Reports() { + // Estado para organizacion_id + const [organizacionId, setOrganizacionId] = useState(''); + + useEffect(() => { + async function fetchOrgId() { + try { + const user = await fetchCurrentUserWithAuth(); + if (user && user.organizacion) { + setOrganizacionId(user.organizacion); + } + } catch (err) { + setOrganizacionId(''); + } + } + fetchOrgId(); + }, []); + // Handler for Generar Reporte in Cumplimiento tab + const handleGenerarReporteCumplimiento = async () => { + if (!organizacionId) { + alert('No se pudo obtener el organizacion_id. Intenta de nuevo más tarde.'); + return; + } + // Build query params from filtersCumplimiento and add organizacion_id + const paramsObj = { ...filtersCumplimiento, organizacion_id: organizacionId }; + const params = Object.entries(paramsObj) + .filter(([_, v]) => v) + .map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`) + .join('&'); + const url = `${import.meta.env.VITE_EFC_API_URL}/reports/table-summary/${params ? `?${params}` : ''}`; + console.log('Cumplimiento Report Request:', url); + try { + const res = await fetchWithAuth(url); + const data = await res.json(); + console.log('Cumplimiento Report Response:', data); + if (!res.ok) throw new Error('Error al generar el reporte'); + alert('Reporte solicitado correctamente. Aparecerá en el historial cuando esté listo.'); + } catch (err) { + alert('No se pudo generar el reporte.'); + } + }; + // Filtros replicados de TableroAlmacenamiento + const initialFiltersCumplimiento = { + pedimento_app: '', + aduana: '', + patente: '', + regimen: '', + agente_aduanal: '', + tipo_operacion: '', + fecha_pago_gte: '', + fecha_pago_lte: '', + contribuyente__rfc: '', + }; + const [filtersCumplimiento, setFiltersCumplimiento] = useState(initialFiltersCumplimiento); + const handleFilterChangeCumplimiento = (e) => { + setFiltersCumplimiento({ ...filtersCumplimiento, [e.target.name]: e.target.value }); + }; + const [summaryData, setSummaryData] = useState(null); + + // Fetch summary data for dashboard/cards + const fetchSummary = async () => { + try { + const url = `${import.meta.env.VITE_EFC_API_URL}/reports/dashboard/summary/`; + const res = await fetch(url, { + method: 'GET', + credentials: 'include', + mode: 'cors', + headers: { + 'Authorization': `Bearer ${localStorage.getItem('access_token')}`, + 'Accept': '*/*', + }, + referrer: window.location.href, + }); + if (!res.ok) throw new Error('Error al obtener el resumen'); + const data = await res.json(); + setSummaryData(data); + } catch (err) { + setSummaryData(null); + } + }; + const [reports, setReports] = useState([]); // Leer DEBUG_MODE desde variables de entorno const isDebugMode = import.meta.env.VITE_DEBUG_MODE === 'true'; const { showMessage } = useNotification(); - + const [isExporting, setIsExporting] = useState(false); const [exportFormat, setExportFormat] = useState('excel'); const [showExportSuccess, setShowExportSuccess] = useState(false); const [showHelp, setShowHelp] = useState(false); const [showTour, setShowTour] = useState(false); const [tourStep, setTourStep] = useState(0); - + // Estado para formato de exportación personalizado const [showFormatSelector, setShowFormatSelector] = useState(false); // Estado para pestañas const [activeTab, setActiveTab] = useState('pedimentos'); - // Efecto para manejar el cambio de pestaña cuando isDebugMode cambia - useEffect(() => { - // Si no está en modo debug y la pestaña activa es una pestaña de debug, cambiar a 'pedimentos' - if (!isDebugMode && (activeTab === 'minimos' || activeTab === 'coves')) { - setActiveTab('pedimentos'); - } - }, [isDebugMode, activeTab]); + // Mostrar Cumplimiento en producción: eliminar lógica que oculta la pestaña // Importar modelos const datastageModels = datastageModelsData?.models || []; @@ -70,7 +174,7 @@ export default function Reports() { // Estado para modelo seleccionado const [selectedModel, setSelectedModel] = useState(defaultModel.model); - + // Estado para campos seleccionados const [selectedFields, setSelectedFields] = useState(defaultModel.fields); @@ -86,7 +190,7 @@ export default function Reports() { // Al cambiar de pestaña, seleccionar el primer modelo de la pestaña actual const newModel = models[0]?.model || ''; setSelectedModel(newModel); - + const modelObj = models.find(m => m.model === newModel); if (modelObj) { setSelectedFields(modelObj.fields); @@ -108,7 +212,7 @@ export default function Reports() { // Obtener los modelos según la pestaña activa const models = activeTab === 'pedimentos' ? pedimentosModels : datastageModels; - + // Encontrar el modelo actual dentro de los modelos de la pestaña activa const currentModel = models.find(m => m.model === selectedModel) || defaultModel; @@ -201,6 +305,25 @@ export default function Reports() { setTourStep(0); }; + // Fetch report list from API + useEffect(() => { + const fetchReports = async () => { + try { + const url = `${import.meta.env.VITE_EFC_API_URL}/reports/report-document-list/`; + const res = await fetchWithAuth(url); + if (!res.ok) throw new Error('Error al obtener el historial de reportes'); + const data = await res.json(); + setReports(data); + } catch (err) { + setReports([]); + } + }; + fetchReports(); + }, []); + + useEffect(() => { + fetchSummary(); + }, []); // Función para manejar la exportación del modelo const handleExportModel = async () => { if (selectedFields.length === 0) { @@ -262,15 +385,15 @@ export default function Reports() { `; const blob = await response.blob(); - const contentType = exportFormat === 'excel' + const contentType = exportFormat === 'excel' ? 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' : 'text/csv'; - + const url = window.URL.createObjectURL(new Blob([blob], { type: contentType })); const link = document.createElement('a'); const extension = exportFormat === 'excel' ? 'xlsx' : 'csv'; const fileName = `${currentModel.model}_${new Date().toISOString().split('T')[0]}.${extension}`; - + link.href = url; link.setAttribute('download', fileName); document.body.appendChild(link); @@ -367,11 +490,10 @@ export default function Reports() { key={field} onClick={() => setAvailableSelected(field)} onDoubleClick={() => addField(field)} - className={`group flex items-center justify-between p-2 rounded-md cursor-pointer transition-all duration-200 ${ - availableSelected === field + className={`group flex items-center justify-between p-2 rounded-md cursor-pointer transition-all duration-200 ${availableSelected === field ? 'bg-blue-50 text-blue-700' : 'hover:bg-gray-100' - }`} + }`} > {formatFieldName(field)} + + + + + {/* Aquí va la lógica y UI específica para Cumplimiento */} + {/* Tabla de reportes debajo de las tarjetas */} +
+

Historial de Reportes

+
+ + + + + + + + + + + + + {reports.length > 0 ? ( + reports.map((r) => ( + + + + + + + + + )) + ) : ( + + + + )} + +
IDEstadoCreadoFinalizadoErrorDescargar
{r.report_id}{r.status}{r.created_at}{r.finished_at}{r.error_message ? r.error_message : '-'} + {r.status === 'ready' ? ( + + ) : ( + - + )} +
No hay reportes disponibles.
+
+
), coves: ( @@ -788,7 +982,7 @@ export default function Reports() {
{/* Header mejorado y decorativo */} -
@@ -801,7 +995,7 @@ export default function Reports() { Consulta, genera y descarga reportes relacionados con el sistema aduanero

- + {/* Botones de ayuda */}
- + {/* Efectos decorativos de fondo */}
@@ -898,11 +1092,10 @@ export default function Reports() {
{isDebugMode && ( )} {isDebugMode && (
- + {/* Tour Overlay */} -
); diff --git a/src/pages/TableroAlmacenamiento.jsx b/src/pages/TableroAlmacenamiento.jsx index 42ae999..83c2f8b 100644 --- a/src/pages/TableroAlmacenamiento.jsx +++ b/src/pages/TableroAlmacenamiento.jsx @@ -1,6 +1,5 @@ import React, { useEffect, useState } from 'react'; import fetchWithAuth from '../fetchWithAuth'; - const initialFilters = { pedimento_app: '', aduana: '', @@ -12,11 +11,48 @@ const initialFilters = { fecha_pago_lte: '', contribuyente__rfc: '', }; - export default function TableroAlmacenamiento() { const [filters, setFilters] = useState(initialFilters); const [summary, setSummary] = useState(null); const [isLoading, setIsLoading] = useState(false); + const [reports, setReports] = useState([]); + + const handleGenerateReport = async () => { + try { + const params = Object.entries(filters) + .filter(([_, v]) => v) + .map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`) + .join('&'); + const url = `${import.meta.env.VITE_EFC_API_URL}/reports/table-summary/${params ? `?${params}` : ''}`; + const res = await fetchWithAuth(url, { method: 'POST' }); + if (!res.ok) throw new Error('Error al generar el reporte'); + alert('Reporte solicitado correctamente. Aparecerá en el historial cuando esté listo.'); + } catch (err) { + alert('No se pudo generar el reporte.'); + } + }; + + const handleDownloadReport = async (reportId) => { + try { + const url = `${import.meta.env.VITE_EFC_API_URL}/reports/report-document-download/${reportId}/`; + const res = await fetchWithAuth(url); + if (!res.ok) throw new Error('Error al descargar el reporte'); + const blob = await res.blob(); + let filename = `reporte_${reportId}.csv`; + const disposition = res.headers.get('Content-Disposition'); + if (disposition && disposition.includes('filename=')) { + filename = disposition.split('filename=')[1].replace(/"/g, '').trim(); + } + const link = document.createElement('a'); + link.href = window.URL.createObjectURL(blob); + link.download = filename; + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); + } catch (err) { + alert('No se pudo descargar el reporte.'); + } + }; // Fetch summary data const fetchSummary = async () => { @@ -36,27 +72,31 @@ export default function TableroAlmacenamiento() { setIsLoading(false); }; - // Fetch initial data + // Fetch report list from API + useEffect(() => { + const fetchReports = async () => { + try { + const url = `${import.meta.env.VITE_EFC_API_URL}/reports/report-document-list/`; + const res = await fetchWithAuth(url); + if (!res.ok) throw new Error('Error al obtener el historial de reportes'); + const data = await res.json(); + setReports(data); + } catch (err) { + setReports([]); + } + }; + fetchReports(); + }, []); + useEffect(() => { fetchSummary(); }, []); - // Handle filter changes const handleFilterChange = (e) => { setFilters({ ...filters, [e.target.name]: e.target.value }); }; - // Card components for different sizes - const Card = ({ title, children, icon, small }) => ( -
-
- {icon && {icon}} - {title} -
-
{children}
-
- ); - + return (
{/* Header */} @@ -97,12 +137,21 @@ export default function TableroAlmacenamiento() { ))}
- +
+ + +
@@ -115,203 +164,60 @@ export default function TableroAlmacenamiento() { Cargando resumen...
) : summary ? ( -
- {/* Pedimentos */} - } - > -
{summary.pedimentos?.total ?? '-'}
-
-
- Completos - {summary.pedimentos?.completos ?? '-'} -
-
- Pendientes - {summary.pedimentos?.pendientes ?? '-'} -
+ <> +
+ {/* ...Tarjetas existentes... */} + {/* ...aquí van las Card como antes... */} + {/* ...no se repite para brevedad... */} +
+ {/* Tabla de reportes debajo de las tarjetas */} +
+

Historial de Reportes

+
+ + + + + + + + + + + + + {reports.length > 0 ? ( + reports.map((r) => ( + + + + + + + + + )) + ) : ( + + + + )} + +
IDEstadoCreadoFinalizadoErrorDescargar
{r.report_id}{r.status}{r.created_at}{r.finished_at}{r.error_message ? r.error_message : '-'} + {r.status === 'ready' ? ( + + ) : ( + - + )} +
No hay reportes disponibles.
-
- Cumplimiento -
-
-
- - {summary.pedimentos?.cumplimiento ?? 0}% - -
- -
- {/* Documentos */} -
-
- - - - Documentos -
-
-
{summary.documentos?.descargados ?? '-'}
- Descargados -
-
- - {/* Remesas */} -
-
- - - - Remesas -
-
-
{summary.remesas?.total ?? '-'}
- Total -
-
-
- - - {/* Partidas */} - } - > -
{summary.partidas?.total ?? '-'}
-
-
- Descargadas - {summary.partidas?.partidas_descargadas ?? '-'} -
-
- Pendientes - {summary.partidas?.partidas_pendientes ?? '-'} -
-
-
- Cumplimiento -
-
-
- - {summary.partidas?.cumplimiento ?? 0}% - -
- - - {/* COVES */} - } - > -
{summary.coves?.total ?? '-'}
-
-
- Procesados - {summary.coves?.coves_procesados ?? '-'} -
-
- Pendientes - {summary.coves?.coves_pendientes ?? '-'} -
-
-
- Cumplimiento -
-
-
- - {summary.coves?.coves_cumplimiento ?? 0}% - -
-
- Acuses -
-
- Procesados - {summary.coves?.acuse_coves_procesados ?? '-'} -
-
- Pendientes - {summary.coves?.acuse_coves_pendientes ?? '-'} -
-
-
-
-
- - {summary.coves?.acuse_coves_cumplimiento ?? 0}% - -
- - - {/* EDocuments */} - } - > -
{summary.edocuments?.total ?? '-'}
-
-
- asd - {summary.edocuments?.edocs_descargados ?? '-'} -
-
- Pendientes - {summary.edocuments?.edocs_pendientes ?? '-'} -
-
-
- Cumplimiento -
-
-
- - {summary.edocuments?.edocs_cumplimiento ?? 0}% - -
-
- Acuses -
-
- Descargados - {summary.edocuments.acuse_descargados ?? '-'} -
-
- Pendientes - {summary.edocuments.acuses_pendientes ?? '-'} -
-
-
-
-
- - {summary.edocuments?.acuses_cumplimiento ?? 0}% - -
- - - -
+
+ ) : (
No hay datos para mostrar.
)}