Paquete: Reportes exportables, Dashboard con gráficos, Auditoría avanzada, fix de filtro PDV
Date: 2026-07-25 Status: ✅ Implementado end-to-end (los 5 puntos) — ver doc/qa/ para el plan de pruebas si se agrega, y la sección "Notas de implementación" al final de este documento para 2 descubrimientos que cambiaron la infra respecto de lo especificado acá (Excel sin maatwebsite/excel y ext-gd real en el Dockerfile).
Este es un paquete único que junta 5 pedidos del cliente + 3 correcciones que salieron en el camino. Se agrupó a pedido explícito del cliente en vez de mandar 5 tareas sueltas.
✅ 1. Fix: filtro de PDV en Transacciones (deshabilitado sin explicación)
El reporte era: "en la lista de transacciones no me deja seleccionar PDV". Investigado: mi archivo de referencia original tenía el filtro de PDV como un input de texto libre, no un combo — Claude Code lo convirtió en un <select> real y lo dejó deshabilitado para transacciones ACCEPTED (correcto, por el fix de asignación de PDV — ver doc/plans/2026-07-25-fix-pdv-asignacion-transacciones.md), pero sin ninguna explicación visual, así que se sentía roto en vez de intencional.
Fix (ver reference/TransaccionesView.vue): el combo ahora muestra un texto chico + tooltip ("No disponible para ACCEPTED — todavía no hay PDV asignado") cuando está deshabilitado, y se habilita normalmente para PAID/CANCELLED con las opciones de PDV del país elegido.
Checklist:
- [x] Aplicar el mismo patrón (deshabilitado + explicación visible) sobre el código real, que ya tiene el combo conectado a datos reales — no pisar esa integración, solo agregar el texto/tooltip explicativo condicionado al estado del filtro.
✅ 2. Dashboard: gráfico animado + más información
Rediseño completo (ver reference/DashboardView.vue), corrido y validado en claro/oscuro/mobile:
- Métrica protagonista: "Monto total pagado hoy" en grande, con variación vs. ayer — en vez de 4 tarjetas del mismo peso visual (research 2026: Mercury/Ramp/Brex lideran con un número).
- 3 métricas secundarias más chicas debajo.
- Gráfico de área animado de verdad (no CSS —
apexcharts+vue3-apexcharts, con animación de entrada real al cargar la página): pagado por día, últimos 7 días. - Ranking de PDVs del día (conecta con la pregunta #11 del funcional: comparativa entre PDVs).
Nueva dependencia — agregar a package.json:
"apexcharts": "^6.6.1",
"vue3-apexcharts": "^1.11.1"Checklist:
- [x]
npm install apexcharts vue3-apexchartsen el proyecto real. - [x] Reemplazar los datos mock (
montoPagadoHoy,chartSeries,rankingPdv) por los agregados reales del backend —DashboardControllerahora calculapaid_today_variation_pct(vs. ayer),chart_series(últimos 7 días, completando con 0 los días sin pagos) yranking_pdv(top 10 del día, respetando el scoping de PDV del usuario). - [x] Los bloques de "Demo de notificaciones" y "Demo — paleta de estados" que quedaron al final del archivo son solo para validar patrones — sacados,
DashboardView.vuequedó reescrito 100% sobre datos reales.
✅ 3. Reportes exportables a Excel y PDF, con membrete de CIS
Decisión de infra confirmada por el cliente: Opción B, barryvdh/laravel-dompdf (su propia sugerencia) — no Browsershot/Chromium.
Descubrimiento durante la implementación — maatwebsite/excel es imposible de instalar en este stack: su última versión (3.1.69) exige illuminate/support ^12.0 (no soporta Laravel 13 en ninguna versión publicada) y además exige phpoffice/phpspreadsheet ^1.30.4, que a su vez exige php <8.5.0. Confirmado con composer require ... --dry-run mostrando el árbol completo de conflicto. Se pivotó a usar phpoffice/phpspreadsheet (v5.9) directamente, sin el wrapper de Laravel — soporta php ^8.2 sin problema. Esto vive en App\Services\ReportExportService (método toXlsx), compartido entre Reportes (punto 4) y Auditoría (punto 5) para no duplicar la lógica de armado de XLSX/PDF.
Segundo descubrimiento — ext-gd es un requisito real de runtime, no solo de resolución de composer: tanto phpspreadsheet como (más importante) dompdf necesitan la extensión PHP gd de verdad para incrustar el logo (imagen rasterizada) en el PDF — se confirmó con una excepción real (The PHP GD extension is required...) al generar un PDF con el logo, no solo con el resultado de composer install. Se agregó ext-gd genuino a backend/Dockerfile.dev (paquetes apt libpng-dev libjpeg62-turbo-dev libfreetype6-dev + docker-php-ext-configure gd + docker-php-ext-install ... gd), se reconstruyó la imagen del backend. Esto es una extensión PHP estándar y liviana — no es Chromium, no contradice la decisión de no complicar la infra.
Membrete: reference/membrete.html se convirtió en resources/views/pdf/membrete.blade.php, usando la fuente embebida DejaVu Sans de dompdf en vez de Google Fonts remotas (dompdf tiene enable_remote: false por defecto, se mantuvo así) y el logo incrustado como data: URI base64 (evita depender de chroot/acceso a filesystem de dompdf).
Checklist:
- [x] Instalar
barryvdh/laravel-dompdf+phpoffice/phpspreadsheet(nomaatwebsite/excel, ver descubrimiento arriba). - [x] Convertir
reference/membrete.htmlen una plantilla Blade reutilizable (resources/views/pdf/membrete.blade.php), parametrizada por título/subtítulo/tabla. - [x] Endpoints:
GET /api/reportes/situacion/export?format=xlsx|pdf(una sola ruta, parámetroformat) yGET /api/admin/auditoria/export(solo PDF, ver punto 5). - [x] Botones de export en
SituacionView.vueyAuditoriaView.vue.
✅ 4. Reportes: filtro de PDV en el Estado de Situación (reporte de actividad por PDV)
Implementado en SituacionView.vue + ReportesController::loadSituacionData(). Valida que el station_id pertenezca al país elegido (422 si no) y esté dentro del alcance del usuario (403 si no). Decisión de comportamiento (no estaba explícita en la especificación original, aplicada por consistencia con el resto del proyecto): al filtrar por un PDV específico, las transacciones ACCEPTED (bolsa compartida sin PDV asignado) y el prefondeo (pool a nivel país, sin concepto de PDV) se excluyen del listado de movimientos — pero summary.balance y summary.prefunding_total siguen siendo el dato real de todo el país, no se ponen en cero, porque no son datos filtrados sino estado país-wide.
Checklist:
- [x] Agregar el filtro de PDV a
SituacionView.vue. - [x] El endpoint de agregados de Reportes acepta
station_idcomo filtro opcional. - [x] El export (punto 3) pasa el filtro activo al generar el archivo.
✅ 5. Auditoría: multi-select de tipo de acción + export PDF con membrete
Implementado. El filtro action (string único) pasó a actions (array) — cambio incompatible hacia atrás asumido a propósito (sin consumidores externos más que el propio frontend/tests del proyecto). El export cachea en EXPORT_LIMIT = 1000 filas (una decisión propia para evitar un PDF de miles de filas sin sentido), avisando en el subtítulo si el total real supera el límite. Cada export (Reportes y Auditoría) audita su propia acción (REPORT_EXPORTED, AUDIT_LOG_EXPORTED) — consistente con que el proyecto audita toda acción significativa, incluyendo Auditoría auditándose a sí misma.
Checklist:
- [x]
AuditoriaView.vue: filtro de acción a multi-select (n-selectconmultiple). - [x] El endpoint de auditoría acepta una lista de acciones (
?actions[]=LOGIN&actions[]=...). - [x] Botón de export a PDF, reusando la plantilla Blade del membrete.
Archivos de referencia adjuntos (reference/)
reference/
├── TransaccionesView.vue → fix del combo de PDV (punto 1)
├── DashboardView.vue → rediseño completo con gráfico animado (punto 2)
├── membrete.html → plantilla del membrete, validada visualmente (punto 3)
├── cislatam-logo-2023-black.png → logo para el membrete
└── package-json-snippet.txt → las 2 dependencias nuevas a agregarResumen de checklist general
- [x] Puntos 1 y 2: aplicados sobre el código real.
- [x] Punto 3: dompdf confirmado por el cliente, instalado y verificado (incluye el logo real incrustado).
- [x] Puntos 3, 4 y 5 implementados end-to-end sobre datos reales.
Notas de implementación (posteriores a la especificación original)
Ver el detalle en cada punto arriba. Resumen de lo que cambió respecto de lo especificado:
- Excel sin
maatwebsite/excel— el paquete es incompatible con Laravel 13 + PHP 8.5 (ver punto 3). Se usaphpoffice/phpspreadsheetdirectamente víaApp\Services\ReportExportService. ext-gdreal enbackend/Dockerfile.dev— tanto dompdf como phpspreadsheet lo necesitan en runtime para incrustar el logo, no solo para resolver dependencias de composer.- Verificación realizada: canario de aislamiento + suite completa de backend (130 tests, 351 assertions) +
npm run build(type-check + build de Vite) + verificación HTTP real vía curl contra el stack local (Dashboard, Reportes con y sin filtro de PDV, export XLSX/PDF, Auditoría con multi-select y su propio export) para cada punto. Sin navegador disponible en esta sesión — la verificación visual queda pendiente de que el usuario la confirme manualmente.

