erp-construccion/docs/02-definicion-modulos/MAI-006-reportes-analytics/requerimientos/RF-BI-004-exportacion-distribucion.md

1791 lines
84 KiB
Markdown

# RF-BI-004: Exportacion y Distribucion Automatizada
**Epica:** MAI-006 - Reportes y Business Intelligence
**Modulo:** Exportacion y Distribucion
**Responsable:** Product Owner
**Fecha:** 2025-11-17
**Version:** 1.0
---
## 1. Objetivo
Proporcionar un sistema robusto de exportacion y distribucion automatizada de reportes que permita programar entregas recurrentes, gestionar suscripciones de usuarios, exportar datos masivamente y integrarse con herramientas externas de Business Intelligence como Power BI, Tableau y Google Data Studio.
---
## 2. Casos de Uso
### CU-BI-015: Programacion de Reportes Recurrentes
**Actor:** Director General, CFO, Gerente de Proyecto
**Precondiciones:**
- Usuario con permisos de configuracion de reportes
- Reporte o dashboard configurado
**Flujo Principal:**
1. Usuario accede a modulo de programacion de reportes
2. Usuario selecciona reporte "Dashboard Ejecutivo Semanal"
3. Usuario hace clic en "Programar Entrega Automatica"
4. Sistema muestra formulario de configuracion:
```
┌──────────────────────────────────────────────────────┐
│ Programar Reporte Automatico │
├──────────────────────────────────────────────────────┤
│ │
│ Reporte: [Dashboard Ejecutivo Semanal ▼] │
│ │
│ ┌─ Frecuencia ────────────────────────────────────┐ │
│ │ │ │
│ │ ○ Diaria │ │
│ │ ● Semanal │ │
│ │ Dia: [▼ Lunes ] │ │
│ │ ○ Quincenal │ │
│ │ Dias: [ ] 1 [ ] 15 del mes │ │
│ │ ○ Mensual │ │
│ │ Dia: [▼ Primer dia del mes] │ │
│ │ ○ Trimestral │ │
│ │ ○ Personalizada │ │
│ │ Expresion cron: [________________] │ │
│ └──────────────────────────────────────────────────┘ │
│ │
│ Hora de Generacion: [08:00] AM │
│ Zona Horaria: [America/Mexico_City ▼] │
│ │
│ ┌─ Formato de Exportacion ────────────────────────┐ │
│ │ │ │
│ │ Formato Principal: [▼ PDF ] │ │
│ │ │ │
│ │ ☑ Incluir tambien en Excel (.xlsx) │ │
│ │ ☐ Incluir tambien en PowerPoint (.pptx) │ │
│ │ ☐ Incluir tambien en CSV (datos raw) │ │
│ └──────────────────────────────────────────────────┘ │
│ │
│ ┌─ Destinatarios ──────────────────────────────────┐ │
│ │ │ │
│ │ Para: [director@constructora.com ] │ │
│ │ [+ Agregar] │ │
│ │ │ │
│ │ CC: [cfo@constructora.com ] │ │
│ │ [gerencia@constructora.com ] │ │
│ │ [+ Agregar] │ │
│ │ │ │
│ │ ☑ Solo enviar si hay cambios significativos │ │
│ │ (variacion > 5% en metricas principales) │ │
│ └──────────────────────────────────────────────────┘ │
│ │
│ ┌─ Contenido del Email ────────────────────────────┐ │
│ │ │ │
│ │ Asunto: │ │
│ │ [Dashboard Ejecutivo - Semana {week_number} ] │ │
│ │ │ │
│ │ Mensaje: │ │
│ │ ┌──────────────────────────────────────────────┐ │ │
│ │ │Estimados colegas, │ │ │
│ │ │ │ │ │
│ │ │Adjunto encuentran el Dashboard Ejecutivo │ │ │
│ │ │correspondiente a la semana {week_number}. │ │ │
│ │ │ │ │ │
│ │ │Metricas destacadas: │ │ │
│ │ │- Proyectos Activos: {project_count} │ │ │
│ │ │- Avance Promedio: {avg_progress}% │ │ │
│ │ │- SPI Consolidado: {consolidated_spi} │ │ │
│ │ │ │ │ │
│ │ │Saludos, │ │ │
│ │ │Sistema de Reportes │ │ │
│ │ └──────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ Variables disponibles: {date}, {week_number}, │ │
│ │ {project_count}, {avg_progress}, ... │ │
│ └──────────────────────────────────────────────────┘ │
│ │
│ ┌─ Opciones Avanzadas ─────────────────────────────┐ │
│ │ │ │
│ │ ☑ Aplicar filtros guardados: [Proyectos Activos] │ │
│ │ ☑ Incluir comparativo vs periodo anterior │ │
│ │ ☐ Incluir tendencia (ultimos 6 periodos) │ │
│ │ ☑ Generar solo si todos los datos estan actuales│ │
│ │ ☐ Comprimir archivos grandes (>10MB) en ZIP │ │
│ │ │ │
│ │ Fecha de inicio: [24/11/2025] │ │
│ │ Fecha de fin: ○ Sin fin ● Hasta [31/12/2026] │ │
│ └──────────────────────────────────────────────────┘ │
│ │
│ ☑ Notificarme si falla la generacion del reporte │ │
│ │
│ [Cancelar] [Probar Ahora] [Programar] │
└──────────────────────────────────────────────────────┘
```
5. Usuario configura todos los parametros
6. Usuario hace clic en "Probar Ahora" para validar configuracion
7. Sistema genera reporte de prueba y lo envia al usuario
8. Usuario verifica reporte recibido
9. Usuario hace clic en "Programar"
10. Sistema crea job recurrente en scheduler
11. Sistema muestra confirmacion con proxima ejecucion:
```
✓ Reporte programado exitosamente
Proxima ejecucion: Lunes 24/11/2025 08:00 AM
Destinatarios: 3 personas
Formato: PDF + Excel
[Ver Calendario de Entregas] [Editar] [Desactivar]
```
**Flujo Alternativo - Modificar Programacion:**
1. Usuario accede a lista de reportes programados
2. Usuario ve tabla con programaciones activas:
```
┌────────────────────────────────────────────────────────────────┐
│ Reportes Programados [+ Nuevo Reporte]│
├────────────────────────────────────────────────────────────────┤
│ │
│ Nombre │Frecuencia│Destinos│Estado │Acciones │
│────────────────────────┼──────────┼────────┼───────┼──────────│
│ Dashboard Ejecutivo │Semanal │ 3 │🟢 Activo│[Editar]│
│ Semanal │(Lunes) │ │ │[Pausar] │
│ │ │ │ │[Eliminar]│
│ Ultima ejecucion: 17/11/2025 08:00 - ✓ Exitoso │
│ Proxima ejecucion: 24/11/2025 08:00 │
│────────────────────────┼──────────┼────────┼───────┼──────────│
│ Reporte de Costos │Mensual │ 5 │🟢 Activo│[Editar]│
│ Consolidado │(Dia 1) │ │ │[Pausar] │
│ │ │ │ │[Eliminar]│
│ Ultima ejecucion: 01/11/2025 09:00 - ✓ Exitoso │
│ Proxima ejecucion: 01/12/2025 09:00 │
│────────────────────────┼──────────┼────────┼───────┼──────────│
│ Flujo de Efectivo │Quincenal │ 2 │🟡 Pausado│[Editar]│
│ Proyectado │(1 y 15) │ │ │[Activar]│
│ │ │ │ │[Eliminar]│
│ Ultima ejecucion: 15/10/2025 10:00 - ✓ Exitoso │
│────────────────────────┼──────────┼────────┼───────┼──────────│
│ Analisis de Riesgos IA │Semanal │ 4 │🔴 Error│[Editar]│
│ │(Viernes) │ │ │[Reintentar]│
│ │ │ │ │[Eliminar]│
│ Ultima ejecucion: 15/11/2025 14:00 - ✗ Fallo │
│ Error: Timeout al generar predicciones │
└────────────────────────────────────────────────────────────────┘
```
3. Usuario puede editar, pausar o eliminar programaciones
**Postcondiciones:**
- Job programado creado en sistema de scheduler
- Proximas ejecuciones calculadas correctamente
- Usuario recibe reportes en horarios configurados
---
### CU-BI-016: Suscripciones a Reportes
**Actor:** Gerente de Proyecto, Analista, Stakeholder
**Precondiciones:**
- Reportes disponibles para suscripcion
- Usuario autenticado
**Flujo Principal:**
1. Usuario accede a catalogo de reportes disponibles
2. Sistema muestra galeria de reportes con opcion de suscripcion:
```
┌──────────────────────────────────────────────────────┐
│ Catalogo de Reportes [Mis Suscripciones]│
├──────────────────────────────────────────────────────┤
│ │
│ Filtar por: [▼ Todos] [▼ Categoria] [🔍 Buscar...] │
│ │
│ ┌─────────────────────┐ ┌─────────────────────┐ │
│ │ 📊 Dashboard │ │ 💰 Reporte de │ │
│ │ Ejecutivo │ │ Costos │ │
│ │ │ │ │ │
│ │ Vista consolidada │ │ Analisis detallado │ │
│ │ de todos los │ │ de costos por │ │
│ │ proyectos │ │ proyecto y partida │ │
│ │ │ │ │ │
│ │ 🔔 152 suscriptores │ │ 🔔 98 suscriptores │ │
│ │ │ │ │ │
│ │ [Suscribirse] │ │ ✓ Suscrito │ │
│ │ [Vista Previa] │ │ [Configurar] │ │
│ └─────────────────────┘ └─────────────────────┘ │
│ │
│ ┌─────────────────────┐ ┌─────────────────────┐ │
│ │ 📈 Analisis de │ │ ⏱ Avance de │ │
│ │ Tendencias │ │ Obra │ │
│ │ │ │ │ │
│ │ Tendencias │ │ Seguimiento de │ │
│ │ historicas y │ │ avance fisico y │ │
│ │ proyecciones │ │ financiero │ │
│ │ │ │ │ │
│ │ 🔔 67 suscriptores │ │ 🔔 203 suscriptores │ │
│ │ │ │ │ │
│ │ [Suscribirse] │ │ [Suscribirse] │ │
│ │ [Vista Previa] │ │ [Vista Previa] │ │
│ └─────────────────────┘ └─────────────────────┘ │
└──────────────────────────────────────────────────────┘
```
3. Usuario hace clic en "Suscribirse" en "Analisis de Tendencias"
4. Sistema muestra opciones de suscripcion:
```
┌──────────────────────────────────────────────────────┐
│ Suscripcion: Analisis de Tendencias │
├──────────────────────────────────────────────────────┤
│ │
│ ┌─ Frecuencia de Entrega ──────────────────────────┐ │
│ │ │ │
│ │ ○ Diaria (cada manana a las 7:00 AM) │ │
│ │ ● Semanal (Lunes a las 8:00 AM) │ │
│ │ ○ Mensual (Primer dia del mes a las 9:00 AM) │ │
│ │ ○ Cuando haya cambios significativos │ │
│ └───────────────────────────────────────────────────┘ │
│ │
│ ┌─ Formato Preferido ──────────────────────────────┐ │
│ │ │ │
│ │ ● PDF (documento) │ │
│ │ ○ Excel (datos y graficas) │ │
│ │ ○ Email HTML (sin adjuntos) │ │
│ │ ○ Link para visualizar online │ │
│ └───────────────────────────────────────────────────┘ │
│ │
│ ┌─ Filtros Personalizados ─────────────────────────┐ │
│ │ │ │
│ │ Proyectos: │ │
│ │ ☑ Todos mis proyectos (8) │ │
│ │ ☐ Solo proyectos que administro (3) │ │
│ │ ☐ Proyectos especificos: [▼ Seleccionar...] │ │
│ │ │ │
│ │ Periodo de analisis: │ │
│ │ [▼ Ultimos 6 meses] │ │
│ └───────────────────────────────────────────────────┘ │
│ │
│ ┌─ Metodo de Entrega ──────────────────────────────┐ │
│ │ │ │
│ │ ● Email: juan.perez@constructora.com │ │
│ │ ☐ Tambien notificar por: │ │
│ │ ☐ Slack (#reportes-semanales) │ │
│ │ ☐ Microsoft Teams (Canal Reportes) │ │
│ │ ☐ SMS (solo alertas criticas) │ │
│ └───────────────────────────────────────────────────┘ │
│ │
│ Proxima entrega: Lunes 24/11/2025 08:00 AM │
│ │
│ ☑ Notificarme si hay alertas criticas en el reporte │
│ │
│ [Cancelar] [Suscribirse] │
└──────────────────────────────────────────────────────┘
```
5. Usuario configura preferencias de suscripcion
6. Usuario hace clic en "Suscribirse"
7. Sistema confirma suscripcion y muestra en "Mis Suscripciones"
8. Usuario recibe primer reporte en proxima entrega programada
**Flujo Alternativo - Gestionar Suscripciones:**
1. Usuario accede a "Mis Suscripciones"
2. Sistema muestra lista de suscripciones activas:
```
┌──────────────────────────────────────────────────────┐
│ Mis Suscripciones │
├──────────────────────────────────────────────────────┤
│ │
│ ┌─ Dashboard Ejecutivo ────────────────────────────┐ │
│ │ Frecuencia: Semanal (Lunes 8:00 AM) │ │
│ │ Formato: PDF │ │
│ │ Ultimo recibido: 17/11/2025 ✓ │ │
│ │ Proximo: 24/11/2025 │ │
│ │ │ │
│ │ [Modificar] [Pausar] [Cancelar Suscripcion] │ │
│ └───────────────────────────────────────────────────┘ │
│ │
│ ┌─ Reporte de Costos ──────────────────────────────┐ │
│ │ Frecuencia: Mensual (Dia 1, 9:00 AM) │ │
│ │ Formato: Excel │ │
│ │ Ultimo recibido: 01/11/2025 ✓ │ │
│ │ Proximo: 01/12/2025 │ │
│ │ Filtros: Solo Proyecto "Fracc. Del Valle" │ │
│ │ │ │
│ │ [Modificar] [Pausar] [Cancelar Suscripcion] │ │
│ └───────────────────────────────────────────────────┘ │
│ │
│ ┌─ Analisis de Tendencias ─────────────────────────┐ │
│ │ Frecuencia: Semanal (Lunes 8:00 AM) │ │
│ │ Formato: PDF │ │
│ │ Estado: 🟡 PAUSADO │ │
│ │ Ultimo recibido: 03/11/2025 ✓ │ │
│ │ │ │
│ │ [Modificar] [Reactivar] [Cancelar Suscripcion] │ │
│ └───────────────────────────────────────────────────┘ │
│ │
│ [Explorar Mas Reportes] │
└──────────────────────────────────────────────────────┘
```
3. Usuario puede modificar, pausar o cancelar suscripciones
**Postcondiciones:**
- Suscripcion registrada en sistema
- Usuario recibe reportes segun configuracion
- Historial de entregas registrado
---
### CU-BI-017: Exportacion Masiva de Datos
**Actor:** Analista de Datos, Director de TI, Auditor
**Precondiciones:**
- Usuario con permisos de exportacion masiva
- Datos disponibles en sistema
**Flujo Principal:**
1. Usuario accede a modulo de exportacion masiva
2. Sistema muestra wizard de exportacion:
```
┌──────────────────────────────────────────────────────┐
│ Exportacion Masiva de Datos [1/4] │
├──────────────────────────────────────────────────────┤
│ │
│ Paso 1: Seleccionar Datos │
│ │
│ ┌─ Entidades a Exportar ───────────────────────────┐ │
│ │ │ │
│ │ ☑ Proyectos (12 registros)│ │
│ │ ☑ Presupuestos (348 partidas) │ │
│ │ ☑ Estimaciones (96 estimaciones)│ │
│ │ ☑ Ordenes de Compra (1,245 OCs) │ │
│ │ ☑ Contratos (67 contratos)│ │
│ │ ☐ Nomina (2,340 registros)│ │
│ │ ☐ Asistencias (18,920 registros)│ │
│ │ ☐ Inventario (456 items) │ │
│ │ ☑ Avances de Obra (1,024 mediciones)│ │
│ │ ☐ Documentos (metadata only) (3,567 docs) │ │
│ │ │ │
│ │ [Seleccionar Todos] [Deseleccionar Todos] │ │
│ └───────────────────────────────────────────────────┘ │
│ │
│ Total estimado: ~8.5 MB de datos │
│ │
│ [Cancelar] [Siguiente →] │
└──────────────────────────────────────────────────────┘
```
3. Usuario selecciona entidades a exportar
4. Usuario hace clic en "Siguiente"
5. Sistema muestra paso 2 - Filtros:
```
┌──────────────────────────────────────────────────────┐
│ Exportacion Masiva de Datos [2/4] │
├──────────────────────────────────────────────────────┤
│ │
│ Paso 2: Aplicar Filtros │
│ │
│ ┌─ Rango de Fechas ────────────────────────────────┐ │
│ │ │ │
│ │ Desde: [01/01/2025] Hasta: [17/11/2025] │ │
│ │ │ │
│ │ ○ Todo el historico │ │
│ │ ● Rango personalizado │ │
│ │ ○ Ultimo ano │ │
│ │ ○ Ultimos 6 meses │ │
│ └───────────────────────────────────────────────────┘ │
│ │
│ ┌─ Filtros por Proyecto ───────────────────────────┐ │
│ │ │ │
│ │ ● Todos los proyectos (12) │ │
│ │ ○ Proyectos seleccionados: │ │
│ │ [▼ Seleccionar proyectos...] │ │
│ │ ○ Proyectos por criterio: │ │
│ │ Estado: [▼ Todos] │ │
│ │ Tipo: [▼ Todos] │ │
│ └───────────────────────────────────────────────────┘ │
│ │
│ ┌─ Opciones Avanzadas ─────────────────────────────┐ │
│ │ │ │
│ │ ☑ Incluir registros eliminados (soft-deleted) │ │
│ │ ☑ Incluir datos de auditoria (quien/cuando) │ │
│ │ ☐ Solo registros modificados en rango de fechas │ │
│ │ ☑ Incluir relaciones (foreign keys) │ │
│ └───────────────────────────────────────────────────┘ │
│ │
│ Registros filtrados: ~7,245 │
│ │
│ [← Anterior] [Siguiente →] │
└──────────────────────────────────────────────────────┘
```
6. Usuario configura filtros
7. Usuario hace clic en "Siguiente"
8. Sistema muestra paso 3 - Formato:
```
┌──────────────────────────────────────────────────────┐
│ Exportacion Masiva de Datos [3/4] │
├──────────────────────────────────────────────────────┤
│ │
│ Paso 3: Configurar Formato │
│ │
│ ┌─ Formato de Archivo ─────────────────────────────┐ │
│ │ │ │
│ │ ● Excel (.xlsx) │ │
│ │ ☑ Hoja separada por entidad │ │
│ │ ☑ Incluir formato (colores, bordes) │ │
│ │ ☐ Proteger con contrasena: [__________] │ │
│ │ │ │
│ │ ○ CSV (valores separados por coma) │ │
│ │ Separador: [▼ Coma (,)] │ │
│ │ Encoding: [▼ UTF-8] │ │
│ │ ☐ ZIP multiple files │ │
│ │ │ │
│ │ ○ JSON (formato estructurado) │ │
│ │ ☐ Pretty print (indentado) │ │
│ │ ☑ Incluir metadata │ │
│ │ │ │
│ │ ○ SQL (script de INSERT) │ │
│ │ Database: [▼ PostgreSQL] │ │
│ │ ☑ Incluir CREATE TABLE │ │
│ │ │ │
│ │ ○ Parquet (columnar, para Big Data) │ │
│ │ Compresion: [▼ Snappy] │ │
│ └───────────────────────────────────────────────────┘ │
│ │
│ ┌─ Opciones de Exportacion ────────────────────────┐ │
│ │ │ │
│ │ ☑ Incluir cabeceras de columna │ │
│ │ ☑ Usar nombres descriptivos (vs IDs tecnicos) │ │
│ │ ☐ Anonimizar datos personales (GDPR) │ │
│ │ ☑ Comprimir archivo final (.zip) │ │
│ └───────────────────────────────────────────────────┘ │
│ │
│ Tamano estimado: ~6.2 MB comprimido │
│ │
│ [← Anterior] [Siguiente →] │
└──────────────────────────────────────────────────────┘
```
9. Usuario selecciona formato Excel
10. Usuario hace clic en "Siguiente"
11. Sistema muestra paso 4 - Confirmacion:
```
┌──────────────────────────────────────────────────────┐
│ Exportacion Masiva de Datos [4/4] │
├──────────────────────────────────────────────────────┤
│ │
│ Paso 4: Confirmar y Exportar │
│ │
│ ┌─ Resumen ────────────────────────────────────────┐ │
│ │ │ │
│ │ Entidades: 6 seleccionadas │ │
│ │ • Proyectos (12) │ │
│ │ • Presupuestos (348) │ │
│ │ • Estimaciones (96) │ │
│ │ • Ordenes de Compra (1,245) │ │
│ │ • Contratos (67) │ │
│ │ • Avances de Obra (1,024) │ │
│ │ │ │
│ │ Periodo: 01/01/2025 - 17/11/2025 │ │
│ │ Formato: Excel (.xlsx) comprimido │ │
│ │ Tamano estimado: ~6.2 MB │ │
│ │ Tiempo estimado: 45-60 segundos │ │
│ └───────────────────────────────────────────────────┘ │
│ │
│ ┌─ Metodo de Entrega ──────────────────────────────┐ │
│ │ │ │
│ │ ● Descarga directa │ │
│ │ (archivo estara listo en 1 minuto) │ │
│ │ │ │
│ │ ○ Enviar por email │ │
│ │ Email: [analista@constructora.com] │ │
│ │ │ │
│ │ ○ Guardar en servidor │ │
│ │ Ruta: [/exports/data_export_{date}.xlsx] │ │
│ └───────────────────────────────────────────────────┘ │
│ │
│ Nombre de archivo: │
│ [exportacion_masiva_2025-11-17.xlsx ] │
│ │
│ ☑ Registrar en log de auditoria │
│ ☑ Notificarme cuando la exportacion este lista │
│ │
│ [← Anterior] [Iniciar Exportacion] │
└──────────────────────────────────────────────────────┘
```
12. Usuario hace clic en "Iniciar Exportacion"
13. Sistema muestra progreso:
```
┌──────────────────────────────────────────────────────┐
│ Exportando Datos... │
├──────────────────────────────────────────────────────┤
│ │
│ ████████████████░░░░░░░░░░░░░░░░░░ 65% │
│ │
│ Procesando: Ordenes de Compra (850/1245) │
│ │
│ Tiempo transcurrido: 38s │
│ Tiempo restante: ~22s │
│ │
│ [Cancelar Exportacion] │
└──────────────────────────────────────────────────────┘
```
14. Sistema completa exportacion y muestra descarga:
```
┌──────────────────────────────────────────────────────┐
│ ✓ Exportacion Completada │
├──────────────────────────────────────────────────────┤
│ │
│ Archivo: exportacion_masiva_2025-11-17.xlsx.zip │
│ Tamano: 5.8 MB │
│ Registros exportados: 2,792 │
│ Tiempo total: 58 segundos │
│ │
│ [📥 Descargar Archivo] │
│ │
│ El archivo estara disponible para descarga │
│ durante las proximas 24 horas. │
│ │
│ Contenido del archivo: │
│ • Hoja "Proyectos" - 12 registros │
│ • Hoja "Presupuestos" - 348 registros │
│ • Hoja "Estimaciones" - 96 registros │
│ • Hoja "Ordenes_Compra" - 1,245 registros │
│ • Hoja "Contratos" - 67 registros │
│ • Hoja "Avances_Obra" - 1,024 registros │
│ • Hoja "Metadata" - informacion de exportacion │
│ │
│ [Descargar] [Nueva Exportacion] │
└──────────────────────────────────────────────────────┘
```
**Postcondiciones:**
- Datos exportados en formato solicitado
- Archivo disponible para descarga
- Registro en log de auditoria
---
### CU-BI-018: Integracion con BI Externo (Power BI, Tableau)
**Actor:** Analista de BI, Director de TI
**Precondiciones:**
- Usuario con permisos de configuracion de integraciones
- Credenciales de herramienta externa
**Flujo Principal:**
1. Usuario accede a modulo de integraciones
2. Sistema muestra conectores disponibles:
```
┌──────────────────────────────────────────────────────┐
│ Integraciones con BI Externo │
├──────────────────────────────────────────────────────┤
│ │
│ Conectores Disponibles: │
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ │ │ │ │ │ │
│ │ Power BI │ │ Tableau │ │ Google Data │ │
│ │ │ │ │ │ Studio │ │
│ │ │ │ │ │ │ │
│ │ [Configurar] │ │ [Configurar] │ │ [Configurar] │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ │ │ │ │ │ │
│ │ Looker │ │ Qlik │ │ Metabase │ │
│ │ │ │ │ │ │ │
│ │ │ │ │ │ │ │
│ │ [Configurar] │ │ [Configurar] │ │ [Configurar] │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
│ │
│ ┌─ Integraciones Activas ──────────────────────────┐ │
│ │ │ │
│ │ Power BI - Workspace "Construccion" │ │
│ │ Estado: 🟢 Conectado │ │
│ │ Ultima sincronizacion: 17/11/2025 14:30 │ │
│ │ Datasets: 3 │ │
│ │ [Ver Detalles] [Sincronizar Ahora] [Editar] │ │
│ └───────────────────────────────────────────────────┘ │
│ │
│ [+ Nueva Integracion] │
└──────────────────────────────────────────────────────┘
```
3. Usuario hace clic en "Configurar" en Tableau
4. Sistema muestra wizard de configuracion:
```
┌──────────────────────────────────────────────────────┐
│ Configurar Integracion: Tableau │
├──────────────────────────────────────────────────────┤
│ │
│ ┌─ Metodo de Conexion ─────────────────────────────┐ │
│ │ │ │
│ │ ● API REST de Tableau Server │ │
│ │ ○ Tableau Cloud (Online) │ │
│ │ ○ Archivo Hyper (actualizacion manual) │ │
│ │ ○ Base de datos directa (Live Connection) │ │
│ └───────────────────────────────────────────────────┘ │
│ │
│ ┌─ Credenciales de Tableau Server ─────────────────┐ │
│ │ │ │
│ │ Server URL: │ │
│ │ [https://tableau.constructora.com ] │ │
│ │ │ │
│ │ Autenticacion: │ │
│ │ ● Token de Acceso Personal (PAT) │ │
│ │ ○ Usuario/Contrasena │ │
│ │ │ │
│ │ Token Name: [____________________________] │ │
│ │ Token Secret: [____________________________] │ │
│ │ │ │
│ │ Site: [Default] │ │
│ │ │ │
│ │ [Probar Conexion] │ │
│ └───────────────────────────────────────────────────┘ │
│ │
│ ┌─ Seleccionar Datasets ───────────────────────────┐ │
│ │ │ │
│ │ ☑ Proyectos y KPIs │ │
│ │ Tablas: projects, kpis, milestones │ │
│ │ Frecuencia: Cada 15 minutos │ │
│ │ │ │
│ │ ☑ Costos y Presupuestos │ │
│ │ Tablas: budgets, costs, estimates │ │
│ │ Frecuencia: Cada hora │ │
│ │ │ │
│ │ ☐ Nomina y Recursos Humanos │ │
│ │ Tablas: payroll, attendance, employees │ │
│ │ Frecuencia: Diaria │ │
│ │ │ │
│ │ ☑ Avances de Obra │ │
│ │ Tablas: progress, work_breakdown │ │
│ │ Frecuencia: Cada 30 minutos │ │
│ └───────────────────────────────────────────────────┘ │
│ │
│ ┌─ Opciones de Sincronizacion ─────────────────────┐ │
│ │ │ │
│ │ ● Incremental (solo cambios) │ │
│ │ ○ Full Refresh (todo el dataset) │ │
│ │ │ │
│ │ ☑ Habilitar sincronizacion automatica │ │
│ │ ☑ Notificar si sincronizacion falla │ │
│ │ ☐ Sincronizar solo en horario laboral │ │
│ │ (8:00 AM - 6:00 PM) │ │
│ └───────────────────────────────────────────────────┘ │
│ │
│ [Cancelar] [Guardar y Conectar] │
└──────────────────────────────────────────────────────┘
```
5. Usuario ingresa credenciales de Tableau
6. Usuario hace clic en "Probar Conexion"
7. Sistema valida credenciales exitosamente
8. Usuario selecciona datasets a sincronizar
9. Usuario hace clic en "Guardar y Conectar"
10. Sistema realiza sincronizacion inicial:
```
┌──────────────────────────────────────────────────────┐
│ Sincronizacion Inicial con Tableau │
├──────────────────────────────────────────────────────┤
│ │
│ ████████████████████████████░░░░ 87% │
│ │
│ Sincronizando: Avances de Obra (890/1024 registros) │
│ │
│ Completado: │
│ ✓ Proyectos y KPIs (12 registros) │
│ ✓ Costos y Presupuestos (1,593 registros) │
│ ⏳ Avances de Obra (en progreso...) │
│ │
│ Tiempo transcurrido: 2m 15s │
│ │
│ [Cancelar Sincronizacion] │
└──────────────────────────────────────────────────────┘
```
11. Sistema completa sincronizacion:
```
┌──────────────────────────────────────────────────────┐
│ ✓ Integracion Configurada Exitosamente │
├──────────────────────────────────────────────────────┤
│ │
│ Tableau Server conectado correctamente │
│ │
│ Datasets sincronizados: 3 │
│ Registros totales: 2,629 │
│ Tiempo de sincronizacion: 2m 48s │
│ │
│ Proxima sincronizacion: │
│ • Proyectos y KPIs: 17/11/2025 14:45 (15 min) │
│ • Costos y Presupuestos: 17/11/2025 15:30 (1 hora) │
│ • Avances de Obra: 17/11/2025 15:00 (30 min) │
│ │
│ Enlaces utiles: │
│ • [Abrir Tableau Server] │
│ • [Ver Documentacion de API] │
│ • [Configurar Dashboards en Tableau] │
│ │
│ [Cerrar] [Ir a Integraciones] │
└──────────────────────────────────────────────────────┘
```
**Flujo Alternativo - Monitoreo de Sincronizacion:**
1. Usuario accede a detalles de integracion con Power BI
2. Sistema muestra dashboard de monitoreo:
```
┌──────────────────────────────────────────────────────┐
│ Integracion: Power BI - Workspace "Construccion" │
├──────────────────────────────────────────────────────┤
│ │
│ Estado: 🟢 Activo │
│ Ultima sincronizacion: 17/11/2025 14:30 ✓ │
│ │
│ ┌─ Datasets Sincronizados ─────────────────────────┐ │
│ │ │ │
│ │ Dataset │Registros│Frecuencia│Estado │ │
│ │──────────────────────┼─────────┼──────────┼───────│ │
│ │ Proyectos y KPIs │ 12 │ 15 min │ ✓ │ │
│ │ Costos Presupuestos │ 1,593 │ 1 hora │ ✓ │ │
│ │ Avances de Obra │ 1,024 │ 30 min │ ✓ │ │
│ └───────────────────────────────────────────────────┘ │
│ │
│ ┌─ Historial de Sincronizaciones (ultimas 24h) ───┐ │
│ │ │ │
│ │ Fecha/Hora │Dataset │Resultado │ │
│ │──────────────────┼─────────────────┼──────────────│ │
│ │ 17/11 14:30 │Proyectos y KPIs │✓ Exitoso (8s)│ │
│ │ 17/11 14:15 │Proyectos y KPIs │✓ Exitoso (7s)│ │
│ │ 17/11 14:00 │Avances de Obra │✓ Exitoso (32s)│ │
│ │ 17/11 13:45 │Proyectos y KPIs │✓ Exitoso (9s)│ │
│ │ 17/11 13:30 │Costos Presup. │✓ Exitoso (45s)│ │
│ │ 17/11 13:30 │Avances de Obra │✗ Fallo │ │
│ │ │ │ (Timeout) │ │
│ └───────────────────────────────────────────────────┘ │
│ │
│ ┌─ Estadisticas ───────────────────────────────────┐ │
│ │ │ │
│ │ Sincronizaciones (24h): 96 │ │
│ │ Exitosas: 94 (97.9%) │ │
│ │ Fallidas: 2 (2.1%) │ │
│ │ Tiempo promedio: 18 segundos │ │
│ │ Datos transferidos (24h): 127 MB │ │
│ └───────────────────────────────────────────────────┘ │
│ │
│ [Sincronizar Ahora] [Editar Config] [Desconectar] │
└──────────────────────────────────────────────────────┘
```
**Postcondiciones:**
- Integracion configurada y activa
- Datos sincronizandose automaticamente
- Dashboards de Tableau/Power BI alimentados con datos actualizados
---
## 3. Requerimientos Funcionales
### RF-BI-004.1: Programacion de Reportes Recurrentes
- El sistema DEBE permitir programar reportes con frecuencia: diaria, semanal, quincenal, mensual, trimestral, personalizada (cron)
- El sistema DEBE soportar multiples formatos simultaneos (PDF + Excel + CSV)
- El sistema DEBE permitir configurar destinatarios (To, CC, BCC)
- El sistema DEBE soportar plantillas de email con variables dinamicas
- El sistema DEBE permitir aplicar filtros guardados a reportes programados
- El sistema DEBE permitir definir condiciones de envio (ej: solo si hay cambios >5%)
- El sistema DEBE permitir configurar fecha de inicio y fin de programacion
- El sistema DEBE notificar si falla la generacion de reporte
- El sistema DEBE registrar historial de entregas (fecha, destinatarios, resultado)
### RF-BI-004.2: Gestion de Programaciones
- El sistema DEBE mostrar lista de reportes programados con estado
- El sistema DEBE permitir pausar/reactivar programaciones
- El sistema DEBE permitir editar configuracion de reportes activos
- El sistema DEBE permitir ejecutar reporte programado bajo demanda
- El sistema DEBE mostrar proxima fecha de ejecucion
- El sistema DEBE permitir duplicar programaciones existentes
- El sistema DEBE permitir eliminar programaciones con confirmacion
### RF-BI-004.3: Suscripciones de Usuarios
- El sistema DEBE proveer catalogo de reportes disponibles para suscripcion
- El sistema DEBE permitir a usuarios suscribirse a reportes
- El sistema DEBE permitir configurar frecuencia personalizada por suscripcion
- El sistema DEBE permitir configurar filtros personalizados por usuario
- El sistema DEBE permitir seleccionar formato preferido (PDF, Excel, HTML, link)
- El sistema DEBE permitir configurar metodo de entrega (email, Slack, Teams, SMS)
- El sistema DEBE permitir pausar temporalmente suscripciones
- El sistema DEBE permitir cancelar suscripciones en cualquier momento
- El sistema DEBE mostrar numero de suscriptores por reporte
### RF-BI-004.4: Exportacion Masiva de Datos
- El sistema DEBE ofrecer wizard paso a paso para exportacion masiva
- El sistema DEBE permitir seleccionar multiples entidades simultaneamente
- El sistema DEBE permitir aplicar filtros de fecha, proyecto, estado
- El sistema DEBE soportar formatos: Excel, CSV, JSON, SQL, Parquet
- El sistema DEBE estimar tamano de archivo antes de exportar
- El sistema DEBE mostrar progreso en tiempo real durante exportacion
- El sistema DEBE permitir comprimir archivos grandes (>10MB) automaticamente
- El sistema DEBE mantener archivos exportados disponibles 24-72 horas
- El sistema DEBE registrar exportaciones masivas en log de auditoria
- El sistema DEBE limitar tamano maximo de exportacion (ej: 100MB o 100K registros)
### RF-BI-004.5: Opciones de Formato de Exportacion
- El sistema DEBE generar Excel con hojas separadas por entidad
- El sistema DEBE incluir metadata en exportaciones (fecha, usuario, filtros)
- El sistema DEBE permitir proteger archivos Excel con contrasena
- El sistema DEBE permitir anonimizar datos personales (cumplimiento GDPR)
- El sistema DEBE usar nombres descriptivos de columnas (no solo IDs tecnicos)
- El sistema DEBE incluir cabeceras en CSV/Excel
- El sistema DEBE permitir configurar encoding (UTF-8, Latin1, etc.)
- El sistema DEBE generar SQL compatible con motor especificado (PostgreSQL, MySQL, etc.)
### RF-BI-004.6: Integracion con BI Externo
- El sistema DEBE proveer conectores para: Power BI, Tableau, Google Data Studio, Looker, Qlik, Metabase
- El sistema DEBE soportar autenticacion via API tokens, OAuth2, usuario/contrasena
- El sistema DEBE permitir configurar datasets a sincronizar
- El sistema DEBE permitir configurar frecuencia de sincronizacion por dataset
- El sistema DEBE soportar sincronizacion incremental (solo cambios)
- El sistema DEBE soportar sincronizacion completa (full refresh)
- El sistema DEBE validar credenciales antes de guardar configuracion
- El sistema DEBE realizar sincronizacion inicial al configurar integracion
### RF-BI-004.7: Monitoreo de Integraciones
- El sistema DEBE mostrar estado de integraciones (activo, error, pausado)
- El sistema DEBE mostrar ultima fecha de sincronizacion
- El sistema DEBE mostrar historial de sincronizaciones (ultimas 24-72h)
- El sistema DEBE calcular estadisticas: tasa de exito, tiempo promedio, datos transferidos
- El sistema DEBE notificar si sincronizacion falla
- El sistema DEBE reintentar automaticamente sincronizaciones fallidas (max 3 intentos)
- El sistema DEBE permitir sincronizacion manual bajo demanda
- El sistema DEBE permitir pausar/desconectar integraciones
### RF-BI-004.8: API REST para Datos
- El sistema DEBE exponer API REST para consulta de datos
- El sistema DEBE soportar autenticacion via API key o JWT
- El sistema DEBE implementar rate limiting (ej: 1000 req/hora)
- El sistema DEBE soportar paginacion en respuestas grandes
- El sistema DEBE soportar filtrado, ordenamiento y proyeccion de campos
- El sistema DEBE retornar datos en formato JSON por defecto
- El sistema DEBE proveer documentacion OpenAPI/Swagger
---
## 4. Modelo de Datos
```typescript
// Scheduled Report Job
interface ScheduledReport {
id: string;
reportId: string; // Dashboard or Report template
name: string;
description: string;
schedule: {
frequency: 'daily' | 'weekly' | 'biweekly' | 'monthly' | 'quarterly' | 'custom';
dayOfWeek?: number; // 0-6 for weekly
daysOfMonth?: number[]; // [1, 15] for biweekly
time: string; // "HH:mm"
timezone: string; // "America/Mexico_City"
cronExpression?: string; // for custom
};
validity: {
startDate: Date;
endDate?: Date; // null = indefinite
};
formats: {
primary: 'pdf' | 'xlsx' | 'pptx' | 'csv';
additional: ('pdf' | 'xlsx' | 'pptx' | 'csv')[];
};
recipients: {
to: string[]; // email addresses
cc?: string[];
bcc?: string[];
};
emailTemplate: {
subject: string; // supports variables like {date}, {week_number}
body: string;
variables: Record<string, string>; // available variables
};
conditions: {
onlyIfChanges?: boolean;
changeThreshold?: number; // percentage
onlyIfComplete?: boolean; // all data is up-to-date
};
filters?: Record<string, any>; // saved filter configuration
options: {
includeComparison?: boolean; // vs previous period
includeTrend?: boolean; // last 6 periods
compressIfLarge?: boolean; // ZIP if >10MB
};
status: 'active' | 'paused' | 'ended';
createdBy: string;
createdAt: Date;
updatedAt: Date;
// Execution tracking
nextExecutionDate: Date;
lastExecutionDate?: Date;
lastExecutionStatus?: 'success' | 'failed';
lastExecutionError?: string;
}
// Execution History
interface ReportExecution {
id: string;
scheduledReportId: string;
executionDate: Date;
status: 'pending' | 'processing' | 'success' | 'failed';
generatedFiles: {
format: string;
filePath: string;
fileSize: number;
}[];
recipients: string[];
emailSent: boolean;
emailSentAt?: Date;
error?: string;
processingTime: number; // milliseconds
metadata: {
recordCount: number;
dataAsOf: Date; // latest data timestamp
filtersApplied: Record<string, any>;
};
createdAt: Date;
}
// User Subscription
interface ReportSubscription {
id: string;
userId: string;
reportId: string; // Dashboard or Report template
frequency: 'daily' | 'weekly' | 'monthly' | 'on_change';
deliveryTime?: string; // "HH:mm"
dayOfWeek?: number;
dayOfMonth?: number;
format: 'pdf' | 'xlsx' | 'html' | 'link';
deliveryMethod: {
email: boolean;
slack?: {
enabled: boolean;
channel: string;
};
teams?: {
enabled: boolean;
channel: string;
};
sms?: {
enabled: boolean;
phoneNumber: string;
onlyCriticalAlerts: boolean;
};
};
filters: Record<string, any>; // personalized filters
options: {
onlyIfChanges?: boolean;
notifyOnCriticalAlerts?: boolean;
};
status: 'active' | 'paused';
createdAt: Date;
updatedAt: Date;
lastDeliveryDate?: Date;
deliveryCount: number;
}
// Bulk Export Job
interface BulkExportJob {
id: string;
name: string;
description?: string;
entities: {
name: string; // 'projects', 'budgets', etc.
tableName: string;
recordCount: number;
}[];
filters: {
dateRange?: { from: Date; to: Date };
projectIds?: string[];
includeDeleted?: boolean;
includeAudit?: boolean;
onlyModifiedInRange?: boolean;
};
format: 'xlsx' | 'csv' | 'json' | 'sql' | 'parquet';
formatOptions: {
// Excel options
separateSheets?: boolean;
includeFormatting?: boolean;
password?: string;
// CSV options
delimiter?: ',' | ';' | '\t' | '|';
encoding?: 'utf-8' | 'latin1' | 'utf-16';
zipMultipleFiles?: boolean;
// JSON options
prettyPrint?: boolean;
includeMetadata?: boolean;
// SQL options
targetDatabase?: 'postgresql' | 'mysql' | 'mssql';
includeCreateTable?: boolean;
// Parquet options
compression?: 'snappy' | 'gzip' | 'lzo';
};
exportOptions: {
includeHeaders?: boolean;
useDescriptiveNames?: boolean;
anonymizePersonalData?: boolean;
compressFile?: boolean;
};
deliveryMethod: 'download' | 'email' | 'server';
deliveryPath?: string;
deliveryEmail?: string;
status: 'pending' | 'processing' | 'completed' | 'failed';
progress: number; // 0-100
result?: {
filePath: string;
fileSize: number;
recordCount: number;
processingTime: number; // milliseconds
expiresAt: Date; // download availability
};
error?: string;
createdBy: string;
createdAt: Date;
completedAt?: Date;
}
// External BI Integration
interface BIIntegration {
id: string;
name: string;
provider: 'powerbi' | 'tableau' | 'google_data_studio' | 'looker' | 'qlik' | 'metabase';
connectionType: 'api' | 'database' | 'file';
credentials: {
// API connection
serverUrl?: string;
apiToken?: string;
apiSecret?: string;
oauth2?: {
clientId: string;
clientSecret: string;
refreshToken: string;
};
// Database connection
connectionString?: string;
// File-based
filePath?: string;
};
datasets: {
id: string;
name: string;
description: string;
tables: string[]; // source tables
syncFrequency: number; // minutes
syncType: 'incremental' | 'full';
lastSyncDate?: Date;
lastSyncStatus?: 'success' | 'failed';
recordCount?: number;
}[];
syncOptions: {
autoSync: boolean;
syncOnlyBusinessHours?: boolean;
businessHours?: { start: string; end: string }; // "08:00", "18:00"
notifyOnFailure: boolean;
maxRetries: number;
};
status: 'active' | 'paused' | 'error';
lastSyncDate?: Date;
createdBy: string;
createdAt: Date;
updatedAt: Date;
}
// Sync History
interface SyncHistory {
id: string;
integrationId: string;
datasetId: string;
syncDate: Date;
status: 'success' | 'failed';
recordsAdded: number;
recordsUpdated: number;
recordsDeleted: number;
totalRecords: number;
dataTransferred: number; // bytes
processingTime: number; // milliseconds
error?: string;
createdAt: Date;
}
// API Access Token
interface APIToken {
id: string;
name: string;
description?: string;
token: string; // hashed
userId: string;
permissions: {
resources: string[]; // ['projects', 'budgets', etc.]
actions: ('read' | 'write' | 'delete')[];
};
rateLimit: {
requestsPerHour: number;
requestsPerDay: number;
};
usage: {
lastUsedDate?: Date;
totalRequests: number;
requestsToday: number;
};
status: 'active' | 'revoked';
expiresAt?: Date;
createdAt: Date;
revokedAt?: Date;
}
```
### SQL Schema
```sql
-- Scheduled Reports
CREATE TABLE scheduled_reports (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
report_id UUID NOT NULL REFERENCES dashboards(id),
name VARCHAR(255) NOT NULL,
description TEXT,
schedule JSONB NOT NULL,
validity JSONB NOT NULL,
formats JSONB NOT NULL,
recipients JSONB NOT NULL,
email_template JSONB NOT NULL,
conditions JSONB,
filters JSONB,
options JSONB,
status VARCHAR(20) DEFAULT 'active',
created_by UUID NOT NULL REFERENCES users(id),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
next_execution_date TIMESTAMP,
last_execution_date TIMESTAMP,
last_execution_status VARCHAR(20),
last_execution_error TEXT,
INDEX idx_scheduled_report_status (status),
INDEX idx_scheduled_report_next_exec (next_execution_date)
);
-- Report Executions
CREATE TABLE report_executions (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
scheduled_report_id UUID NOT NULL REFERENCES scheduled_reports(id) ON DELETE CASCADE,
execution_date TIMESTAMP NOT NULL,
status VARCHAR(20) NOT NULL DEFAULT 'pending',
generated_files JSONB,
recipients JSONB,
email_sent BOOLEAN DEFAULT false,
email_sent_at TIMESTAMP,
error TEXT,
processing_time INTEGER, -- milliseconds
metadata JSONB,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
INDEX idx_report_exec_scheduled (scheduled_report_id),
INDEX idx_report_exec_date (execution_date),
INDEX idx_report_exec_status (status)
);
-- Report Subscriptions
CREATE TABLE report_subscriptions (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID NOT NULL REFERENCES users(id),
report_id UUID NOT NULL REFERENCES dashboards(id),
frequency VARCHAR(20) NOT NULL,
delivery_time VARCHAR(5),
day_of_week INTEGER,
day_of_month INTEGER,
format VARCHAR(10) NOT NULL,
delivery_method JSONB NOT NULL,
filters JSONB,
options JSONB,
status VARCHAR(20) DEFAULT 'active',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
last_delivery_date TIMESTAMP,
delivery_count INTEGER DEFAULT 0,
UNIQUE(user_id, report_id),
INDEX idx_subscription_user (user_id),
INDEX idx_subscription_report (report_id),
INDEX idx_subscription_status (status)
);
-- Bulk Export Jobs
CREATE TABLE bulk_export_jobs (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name VARCHAR(255) NOT NULL,
description TEXT,
entities JSONB NOT NULL,
filters JSONB,
format VARCHAR(10) NOT NULL,
format_options JSONB,
export_options JSONB,
delivery_method VARCHAR(20) NOT NULL,
delivery_path VARCHAR(500),
delivery_email VARCHAR(255),
status VARCHAR(20) NOT NULL DEFAULT 'pending',
progress INTEGER DEFAULT 0,
result JSONB,
error TEXT,
created_by UUID NOT NULL REFERENCES users(id),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
completed_at TIMESTAMP,
INDEX idx_bulk_export_status (status),
INDEX idx_bulk_export_creator (created_by),
INDEX idx_bulk_export_created (created_at)
);
-- BI Integrations
CREATE TABLE bi_integrations (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name VARCHAR(255) NOT NULL,
provider VARCHAR(50) NOT NULL,
connection_type VARCHAR(20) NOT NULL,
credentials JSONB NOT NULL,
datasets JSONB NOT NULL,
sync_options JSONB NOT NULL,
status VARCHAR(20) DEFAULT 'active',
last_sync_date TIMESTAMP,
created_by UUID NOT NULL REFERENCES users(id),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
INDEX idx_bi_integration_provider (provider),
INDEX idx_bi_integration_status (status)
);
-- Sync History
CREATE TABLE sync_history (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
integration_id UUID NOT NULL REFERENCES bi_integrations(id) ON DELETE CASCADE,
dataset_id VARCHAR(255) NOT NULL,
sync_date TIMESTAMP NOT NULL,
status VARCHAR(20) NOT NULL,
records_added INTEGER DEFAULT 0,
records_updated INTEGER DEFAULT 0,
records_deleted INTEGER DEFAULT 0,
total_records INTEGER DEFAULT 0,
data_transferred BIGINT, -- bytes
processing_time INTEGER, -- milliseconds
error TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
INDEX idx_sync_history_integration (integration_id),
INDEX idx_sync_history_date (sync_date),
INDEX idx_sync_history_status (status)
);
-- API Tokens
CREATE TABLE api_tokens (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name VARCHAR(255) NOT NULL,
description TEXT,
token_hash VARCHAR(255) NOT NULL UNIQUE,
user_id UUID NOT NULL REFERENCES users(id),
permissions JSONB NOT NULL,
rate_limit JSONB NOT NULL,
usage JSONB DEFAULT '{"totalRequests": 0, "requestsToday": 0}',
status VARCHAR(20) DEFAULT 'active',
expires_at TIMESTAMP,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
revoked_at TIMESTAMP,
INDEX idx_api_token_hash (token_hash),
INDEX idx_api_token_user (user_id),
INDEX idx_api_token_status (status)
);
-- API Request Log (para rate limiting y analytics)
CREATE TABLE api_request_log (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
token_id UUID NOT NULL REFERENCES api_tokens(id),
endpoint VARCHAR(255) NOT NULL,
method VARCHAR(10) NOT NULL,
response_status INTEGER,
response_time INTEGER, -- milliseconds
ip_address INET,
user_agent TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
INDEX idx_api_log_token (token_id),
INDEX idx_api_log_created (created_at)
);
-- Particion por fecha para performance
CREATE TABLE api_request_log_y2025m11 PARTITION OF api_request_log
FOR VALUES FROM ('2025-11-01') TO ('2025-12-01');
```
---
## 5. Criterios de Aceptacion
### Programacion de Reportes
- [ ] Reportes se generan y envian en horario configurado (±2 min)
- [ ] Expresiones cron personalizadas funcionan correctamente
- [ ] Variables en template de email se reemplazan correctamente
- [ ] Condiciones de envio (solo si cambios >5%) se evaluan correctamente
- [ ] Multiples formatos se generan y adjuntan en mismo email
- [ ] Notificaciones de falla se envian al creador del reporte
- [ ] Reportes con fecha de fin se desactivan automaticamente
- [ ] Job programado se puede ejecutar manualmente bajo demanda
### Suscripciones
- [ ] Catalogo muestra todos los reportes disponibles
- [ ] Suscripcion se crea correctamente con preferencias de usuario
- [ ] Filtros personalizados se aplican solo a ese usuario
- [ ] Integracion con Slack/Teams envia notificaciones correctamente
- [ ] Opcion "solo si hay cambios" evita envios innecesarios
- [ ] Pausar suscripcion detiene entregas temporalmente
- [ ] Cancelar suscripcion elimina configuracion y detiene entregas
- [ ] Numero de suscriptores se actualiza en tiempo real
### Exportacion Masiva
- [ ] Wizard de 4 pasos funciona sin errores
- [ ] Estimacion de tamano es precisa (±15%)
- [ ] Barra de progreso refleja avance real
- [ ] Exportacion se completa en <5 minutos para dataset tipico (50K registros)
- [ ] Archivos Excel tienen hojas separadas por entidad
- [ ] Metadata incluye fecha, usuario, filtros aplicados
- [ ] Compresion ZIP reduce tamano >50% en archivos grandes
- [ ] Archivos exportados se eliminan automaticamente despues de 24h
- [ ] Log de auditoria registra exportaciones masivas
### Integracion BI Externo
- [ ] Conectores para Power BI, Tableau, Google Data Studio funcionan
- [ ] Validacion de credenciales detecta errores antes de guardar
- [ ] Sincronizacion incremental solo transfiere cambios
- [ ] Sincronizacion se ejecuta segun frecuencia configurada (±2 min)
- [ ] Historial muestra ultimas 48h de sincronizaciones
- [ ] Estadisticas calculan tasa de exito, tiempo promedio correctamente
- [ ] Notificaciones se envian si sincronizacion falla
- [ ] Reintento automatico funciona (max 3 intentos con exponential backoff)
- [ ] Sincronizacion manual bajo demanda funciona inmediatamente
### API REST
- [ ] Autenticacion via API key funciona correctamente
- [ ] Rate limiting bloquea requests sobre limite (ej: 1000/hora)
- [ ] Paginacion funciona correctamente (parametros page, limit)
- [ ] Filtrado, ordenamiento, proyeccion de campos funcionan
- [ ] Respuestas JSON tienen formato consistente
- [ ] Documentacion Swagger esta actualizada y funcional
- [ ] Errores retornan codigos HTTP apropiados (400, 401, 403, 404, 429, 500)
---
## 6. Notas Tecnicas
### Implementacion de Scheduler para Reportes
```typescript
import cron from 'node-cron';
import { Queue, Worker } from 'bullmq';
class ReportScheduler {
private queue: Queue;
constructor() {
this.queue = new Queue('scheduled-reports', {
connection: { host: 'redis', port: 6379 }
});
this.initializeScheduler();
this.initializeWorker();
}
private async initializeScheduler() {
// Run every minute to check for due reports
cron.schedule('* * * * *', async () => {
await this.checkDueReports();
});
}
private async checkDueReports() {
const now = new Date();
const dueReports = await db.scheduledReports.findMany({
where: {
status: 'active',
next_execution_date: {
lte: now
}
}
});
for (const report of dueReports) {
// Add to job queue
await this.queue.add('generate-report', {
scheduledReportId: report.id,
reportId: report.report_id,
formats: report.formats,
recipients: report.recipients,
filters: report.filters
});
// Calculate next execution
const nextExecution = this.calculateNextExecution(report.schedule);
await db.scheduledReports.update({
where: { id: report.id },
data: { next_execution_date: nextExecution }
});
}
}
private calculateNextExecution(schedule: any): Date {
const now = new Date();
switch (schedule.frequency) {
case 'daily':
const tomorrow = new Date(now);
tomorrow.setDate(tomorrow.getDate() + 1);
tomorrow.setHours(parseInt(schedule.time.split(':')[0]));
tomorrow.setMinutes(parseInt(schedule.time.split(':')[1]));
return tomorrow;
case 'weekly':
const nextWeek = new Date(now);
const daysUntilNext = (schedule.dayOfWeek - now.getDay() + 7) % 7 || 7;
nextWeek.setDate(nextWeek.getDate() + daysUntilNext);
nextWeek.setHours(parseInt(schedule.time.split(':')[0]));
nextWeek.setMinutes(parseInt(schedule.time.split(':')[1]));
return nextWeek;
case 'monthly':
const nextMonth = new Date(now);
nextMonth.setMonth(nextMonth.getMonth() + 1);
nextMonth.setDate(schedule.dayOfMonth);
nextMonth.setHours(parseInt(schedule.time.split(':')[0]));
nextMonth.setMinutes(parseInt(schedule.time.split(':')[1]));
return nextMonth;
case 'custom':
// Parse cron expression
const cronParser = require('cron-parser');
const interval = cronParser.parseExpression(schedule.cronExpression);
return interval.next().toDate();
default:
throw new Error(`Unknown frequency: ${schedule.frequency}`);
}
}
private initializeWorker() {
const worker = new Worker('scheduled-reports', async (job) => {
const { scheduledReportId, reportId, formats, recipients, filters } = job.data;
try {
// Create execution record
const execution = await db.reportExecutions.create({
data: {
scheduled_report_id: scheduledReportId,
execution_date: new Date(),
status: 'processing',
recipients
}
});
// Generate report
const startTime = Date.now();
const files = await this.generateReportFiles(reportId, formats, filters);
const processingTime = Date.now() - startTime;
// Send email
await this.sendReportEmail(recipients, files);
// Update execution record
await db.reportExecutions.update({
where: { id: execution.id },
data: {
status: 'success',
generated_files: files,
email_sent: true,
email_sent_at: new Date(),
processing_time: processingTime
}
});
// Update scheduled report
await db.scheduledReports.update({
where: { id: scheduledReportId },
data: {
last_execution_date: new Date(),
last_execution_status: 'success'
}
});
} catch (error) {
// Update execution record with error
await db.reportExecutions.update({
where: { id: execution.id },
data: {
status: 'failed',
error: error.message
}
});
// Update scheduled report
await db.scheduledReports.update({
where: { id: scheduledReportId },
data: {
last_execution_date: new Date(),
last_execution_status: 'failed',
last_execution_error: error.message
}
});
// Send failure notification
const report = await db.scheduledReports.findUnique({
where: { id: scheduledReportId }
});
await this.sendFailureNotification(report.created_by, error);
}
}, {
connection: { host: 'redis', port: 6379 },
concurrency: 5
});
}
private async generateReportFiles(reportId: string, formats: any, filters: any) {
const files = [];
for (const format of [formats.primary, ...formats.additional]) {
const exporter = this.getExporter(format);
const filePath = await exporter.export(reportId, filters);
files.push({
format,
filePath,
fileSize: await this.getFileSize(filePath)
});
}
return files;
}
private async sendReportEmail(recipients: any, files: any[]) {
const attachments = files.map(f => ({
filename: path.basename(f.filePath),
path: f.filePath
}));
await emailService.send({
to: recipients.to,
cc: recipients.cc,
bcc: recipients.bcc,
subject: 'Dashboard Ejecutivo Semanal',
html: '<p>Adjunto encuentra el reporte solicitado.</p>',
attachments
});
}
}
```
### Implementacion de Sincronizacion con BI Externo
```typescript
import axios from 'axios';
class TableauSyncService {
private baseUrl: string;
private token: string;
constructor(integration: BIIntegration) {
this.baseUrl = integration.credentials.serverUrl;
this.token = integration.credentials.apiToken;
}
async authenticate() {
const response = await axios.post(`${this.baseUrl}/api/3.0/auth/signin`, {
credentials: {
personalAccessTokenName: this.token,
personalAccessTokenSecret: this.tokenSecret,
site: { contentUrl: this.site }
}
});
this.authToken = response.data.credentials.token;
}
async syncDataset(dataset: any) {
const syncStart = Date.now();
try {
// Extract data from source
const data = await this.extractData(dataset.tables);
// Transform to Tableau format
const hyperFile = await this.createHyperFile(data);
// Upload to Tableau Server
await this.uploadHyperFile(dataset.id, hyperFile);
// Refresh extract
await this.refreshExtract(dataset.id);
const syncTime = Date.now() - syncStart;
// Record sync history
await db.syncHistory.create({
data: {
integration_id: this.integrationId,
dataset_id: dataset.id,
sync_date: new Date(),
status: 'success',
total_records: data.length,
processing_time: syncTime
}
});
return { success: true, recordCount: data.length };
} catch (error) {
// Record failure
await db.syncHistory.create({
data: {
integration_id: this.integrationId,
dataset_id: dataset.id,
sync_date: new Date(),
status: 'failed',
error: error.message
}
});
throw error;
}
}
private async extractData(tables: string[]) {
const data = {};
for (const table of tables) {
data[table] = await db[table].findMany();
}
return data;
}
private async createHyperFile(data: any) {
const { TableauHyperApi } = require('tableau-hyper-api');
const hyperFile = '/tmp/data.hyper';
// Create Hyper file and insert data
// ... (implementation details)
return hyperFile;
}
private async uploadHyperFile(datasetId: string, filePath: string) {
const formData = new FormData();
formData.append('file', fs.createReadStream(filePath));
await axios.post(
`${this.baseUrl}/api/3.0/sites/${this.siteId}/datasources/${datasetId}/data`,
formData,
{
headers: {
'X-Tableau-Auth': this.authToken,
...formData.getHeaders()
}
}
);
}
private async refreshExtract(datasetId: string) {
await axios.post(
`${this.baseUrl}/api/3.0/sites/${this.siteId}/datasources/${datasetId}/refresh`,
{},
{
headers: { 'X-Tableau-Auth': this.authToken }
}
);
}
}
```
---
**Fecha:** 2025-11-17
**Preparado por:** Equipo de Producto
**Version:** 1.0
**Estado:** Listo para Revision