ConTodo
Data & BI

ConTodo ERP — Data Warehouse, Pipeline ETL/ELT (CDC), Dashboard Ejecutivo y Catálogo de KPIs

Plataforma de Datos & Business Intelligence de ConTodo ERP

Alcance. Este documento define la arquitectura analítica de ConTodo: el Data Warehouse (DW) dimensional bajo esquema estrella (Kimball), el pipeline de ingesta CDC (Change Data Capture) desde PostgreSQL operacional hacia el DW, la capa semántica, el dashboard ejecutivo (C-level) y el catálogo de KPIs financieros y operativos con fórmulas auditables (EBITDA, margen bruto/neto, rotación de inventario, liquidez corriente/ácida, ROE/ROA, DSO/DPO). Todo el diseño respeta el modelo multi-tenant (company_id como partición lógica obligatoria), la terminología SUNAT (PLE, SIRE, detracciones, tipo de cambio SBS) y los principios NIIF/PCGE 2019 que alimentan las métricas. Se incluyen supuestos explícitos, riesgos, alternativas y diagramas mermaid.


1. Principios de diseño y supuestos

#Supuesto / PrincipioJustificación
S1Separación OLTP / OLAP. El ERP transaccional (PostgreSQL RDS) jamás corre consultas analíticas pesadas; estas viven en un DW columnar separado.Proteger latencia del ERP (p95 < 300 ms). Las consultas BI escanean millones de filas; mezclarlas degrada la operación.
S2Multi-tenant físico-lógico. Cada hecho y dimensión lleva company_id. El DW se particiona por company_id + fecha; el RLS del ERP se replica como filtro obligatorio en la capa semántica.Aislamiento de datos entre tenants es contractual. Un dashboard nunca debe filtrar cruzado entre empresas.
S3Esquema estrella (Kimball), no copo de nieve puro. Dimensiones desnormalizadas; jerarquías embebidas.Optimiza lectura analítica y simplifica el modelo semántico. El copo de nieve añade joins que penalizan el dashboard.
S4Moneda dual. Cada hecho monetario guarda monto en moneda origen, TC SBS y monto en moneda funcional (PEN). Métricas consolidadas siempre en PEN.NIC 21 y exigencia SUNAT. Empresas importadoras textiles facturan en USD; el BI consolida en soles.
S5Grano explícito por tabla de hechos. Cada fact_* declara su grano (1 fila = 1 línea de comprobante, 1 movimiento de kardex, 1 snapshot diario).El grano define la verdad. Mezclar granos produce doble conteo en agregaciones — el error #1 en DW.
S6Métricas como código (semantic layer). Las fórmulas de KPI se versionan en una capa semántica (dbt metrics / Cube), no se hardcodean en cada dashboard."Una sola definición de EBITDA" para toda la organización. Evita que Finanzas y Ventas reporten cifras distintas.
S7Latencia por capa. Near-real-time (CDC streaming) para operativo (stock, ventas del día); batch nocturno para estados financieros y snapshots.No todo necesita tiempo real. El cierre contable es batch; el tablero de stock sí debe ser fresco.
S8Idempotencia y reprocesamiento. Todo job ETL es re-ejecutable sin duplicar datos (upsert por clave natural + _loaded_at).Fallos de red/Sidekiq son inevitables; el pipeline debe tolerar reintentos.
S9Slowly Changing Dimensions Tipo 2 para dimensiones con historia (cliente, producto, lista de precios, tipo de cambio).Reportar "margen al momento de la venta" exige conservar el costo/precio histórico, no el actual.

2. Arquitectura de la plataforma de datos

Justificación de capa medallón (bronze/silver/gold). Bronze conserva el dato crudo del CDC (auditoría y reprocesamiento total); Silver aplica limpieza, deduplicación y tipado; Gold materializa el esquema estrella listo para consumo. Esto permite time-travel y rehacer el modelo sin reextraer del OLTP.


3. Pipeline ETL/ELT con CDC desde PostgreSQL

3.1 Por qué CDC y no SELECT periódico

EnfoqueLatenciaCarga sobre OLTPCaptura de deletesVeredicto
Batch SELECT WHERE updated_at >Minutos–horasAlta (full scans)No detecta hard-deletesDescartado para tablas calientes
Triggers + tabla de auditoríaBajaMedia (overhead por escritura)Alternativa si no se puede tocar WAL
CDC vía WAL (Debezium)SegundosMínima (lee el log, no las tablas) (eventos d)Elegido

ConTodo usa replicación lógica de PostgreSQL (publication/slot) consumida por Debezium, que emite eventos c (create), u (update), d (delete) a Kafka. Cada evento porta before, after, op, ts_ms y company_id. El consumidor escribe a Bronze (parquet append-only) y un job dbt incremental construye Silver/Gold.

3.2 Cadencia por dominio

DominioTablas fuenteModoSLA frescura
Ventas / facturaciónsales_invoices, sales_invoice_linesCDC streaming< 5 min
Inventario / Kardexstock_moves, stock_quantsCDC streaming< 5 min
Compraspurchase_orders, billsCDC streaming< 15 min
Tesorería / cobranzaspayments, bank_statementsCDC streaming< 15 min
Contabilidad (mayor)journal_entries, journal_linesBatch incremental1 h
Estados financieros / snapshotsderivadosBatch nocturnoDiario (cierre 02:00)
RRHH / Planillaspayslips, employeesBatch mensualTras cierre de planilla

3.3 Reglas de transformación clave

  • Deduplicación CDC: por (tabla, pk, ts_ms) quedarse con el último evento por ventana.
  • Manejo de deletes: soft-delete lógico en Gold (is_deleted = true) para preservar historia analítica.
  • Conversión de moneda: join con dim_tipo_cambio (SCD2) por fecha del documento → monto PEN.
  • Tests dbt obligatorios: unique y not_null sobre claves, relationships (FK a dimensiones), accepted_values (estados), y test custom de balance (Σ debe = Σ haber por asiento) heredado de la integridad contable.

4. Modelo dimensional (esquema estrella)

4.1 Diagrama del bus dimensional

4.2 Tablas de hechos (facts)

Tabla de hechosGrano (1 fila =)TipoMétricas aditivasMétricas semi/no aditivas
fact_ventas1 línea de comprobante de ventaTransaccionalcantidad, monto_bruto, descuento, igv, monto_neto_pen, costo_ventamargen_unitario (calc)
fact_compras1 línea de orden/factura de compraTransaccionalcantidad, monto_neto_pen, igv, detraccion
fact_inventario_mov1 movimiento de kardexTransaccionalcantidad_in, cantidad_out, costo_unit, valor_mov_pen
fact_inventario_snap1 producto×almacén×díaSnapshot periódicostock_qty, stock_valor_pen (no aditiva en tiempo)
fact_finanzas_gl1 línea de asiento contableTransaccionaldebe_pen, haber_pensaldo (semi-aditiva por cuenta)
fact_cobranzas1 documento por cobrar (snapshot)Acumulativo (accumulating snapshot)dias_vencido, saldo_pendiente

Nota sobre snapshots. fact_inventario_snap evita recalcular stock histórico recorriendo todo el kardex; el dashboard de rotación lee el saldo del día directamente. Es semi-aditiva: se suma entre productos/almacenes pero no entre fechas (se promedia).

4.3 Tablas de dimensiones (dims)

DimensiónAtributos claveSCDJerarquías
dim_tiempofecha, dia, semana, mes, trimestre, año, es_feriado_pe, periodo_fiscalTipo 0 (estática)Año→Trim→Mes→Día
dim_empresacompany_id, razón_social, ruc, régimen_tributario, sectorTipo 1
dim_clientecliente_id, ruc/dni, nombre, tipo_doc, segmento, dpto, provinciaTipo 2Geografía; Segmento
dim_productosku, nombre, línea, categoría, talla, color, unidad, partida_arancelariaTipo 2Categoría→Línea→SKU; (textil: color/talla)
dim_proveedorproveedor_id, ruc, nombre, país_origen, es_importadoTipo 2País→Proveedor
dim_almacenalmacen_id, sucursal_id, tipo, ubicaciónTipo 1Sucursal→Almacén
dim_sucursalsucursal_id, nombre, regiónTipo 1Región→Sucursal
dim_vendedorvendedor_id, nombre, equipo, cuotaTipo 2Equipo→Vendedor
dim_cuentacódigo_pcge, descripción, clase, naturaleza, saldo_normal, ef_destinoTipo 1Clase→Cuenta→Divisionaria
dim_centro_costocc_id, nombre, tipo, responsableTipo 1
dim_monedacódigo_iso, símboloTipo 1
dim_tipo_cambiofecha, moneda, tc_compra, tc_venta, tc_sbsTipo 2

5. Catálogo de KPIs con fórmulas

Todas las fórmulas se expresan en moneda funcional (PEN) y se materializan en la capa semántica. Las cuentas referidas usan clases PCGE.

5.1 KPIs financieros

KPIFórmulaComponentes (origen)FrecuenciaBenchmark/objetivo
Margen Bruto(Ventas Netas − Costo de Ventas) / Ventas Netasfact_ventas.monto_neto_pen, fact_ventas.costo_ventaDiariaTextil: 35–50%
Margen NetoUtilidad Neta / Ventas NetasResultado neto (clase 6/7 GL)Mensual≥ 8%
EBITDAUtilidad Operativa + Depreciación + AmortizaciónGL: result. operativo + ctas 681/682Mensual≥ 12% s/ventas
Margen EBITDAEBITDA / Ventas NetasderivadoMensual≥ 12%
ROEUtilidad Neta / Patrimonio Promedioresult. neto; dim_cuenta clase 5Trimestral≥ 15%
ROAUtilidad Neta / Activo Total Promedioclases 1–3Trimestral≥ 8%
Liquidez CorrienteActivo Corriente / Pasivo CorrienteGL ctas corrientesMensual1.5–2.0
Prueba Ácida(Activo Corriente − Existencias) / Pasivo Corrienteexcluye clase 2Mensual≥ 1.0
DSO (Días de Cobro)(Cuentas por Cobrar / Ventas a crédito) × 365fact_cobranzas, fact_ventasMensual≤ 45 días
DPO (Días de Pago)(Cuentas por Pagar / Compras a crédito) × 365fact_compras, GL clase 42Mensual30–60 días
Ciclo de Conversión de EfectivoDSO + DIO − DPOderivadosMensualMinimizar
Capital de TrabajoActivo Corriente − Pasivo CorrienteGLMensual> 0

5.2 KPIs operativos / comerciales / inventario

KPIFórmulaOrigenObjetivo
Rotación de InventarioCosto de Ventas / Inventario Promediofact_ventas.costo_venta, fact_inventario_snap↑ (textil: 4–8x/año)
DIO (Días de Inventario)365 / Rotación de Inventarioderivado≤ 90 días
Ticket PromedioVentas Netas / N.º Comprobantesfact_ventasCrecer MoM
Tasa de Conversión CRMOportunidades Ganadas / Total Oportunidadesmódulo CRM≥ 25%
Cumplimiento de CuotaVentas Vendedor / Cuotafact_ventas, dim_vendedor≥ 100%
Stock-out RateSKU sin stock / Total SKU activosfact_inventario_snap≤ 3%
Margen por Línea/Color/Tallamargen agregado por jerarquíafact_ventas × dim_producto
% Ventas Importado vs Nacionalmix por dim_proveedor.es_importadofact_ventas

5.3 Tabla de cálculo ilustrativo (EBITDA mensual)

ConceptoCuenta PCGEMonto (PEN)
Ventas netas701,200,000
(−) Costo de ventas69(720,000)
Utilidad bruta480,000
(−) Gastos de ventas95(180,000)
(−) Gastos de administración94(140,000)
Utilidad operativa160,000
(+) Depreciación68135,000
(+) Amortización6828,000
EBITDA203,000
Margen EBITDA16.9%

6. Dashboard ejecutivo (C-level)

6.1 Estructura propuesta (React + Recharts sobre capa semántica)

6.2 Especificación de widgets

WidgetTipo visualKPIDrill-downRefresco
Tarjetas de saludKPI card + sparklineEBITDA, Margen Neto, Liquidez, ROEA estado financieroDiario
Capital de trabajoGauge + tendenciaDSO, DPO, CCEA documentos vencidosDiario
Ventas vs metaBarras + línea metaVentas MTD/YTDA vendedor→cliente→factura< 5 min (CDC)
Mix de margenTreemapMargen por línea/colorA SKUDiario
Rotación inventarioHeatmap por almacénRotación, DIOA SKU lentoDiario
Embudo CRMFunnelConversiónA oportunidadHorario
Alertas IALista priorizadaanomalías, forecastA causa raízDiario

6.3 Seguridad del dashboard

  • RLS en capa semántica: cada query inyecta company_id del JWT del usuario; un usuario nunca ve otra empresa.
  • Permisos por rol: el CFO ve finanzas completas; el jefe de tienda solo su sucursal (dim_sucursal filtrada). Se reutiliza el modelo multirol del módulo Seguridad.
  • Auditoría de consultas: log de quién consultó qué KPI (cumplimiento Big Four).

7. Módulo de IA sobre la plataforma de datos

Caso de usoTécnicaInsumo (DW)Salida
Pronóstico de ventasSeries temporales (Prophet / ARIMA)fact_ventas + dim_tiempo (feriados PE)Forecast 30/90 días por línea
Detección de anomalíasIsolation Forest / z-scorefact_finanzas_gl, fact_ventasAlertas de gasto/venta atípica
Riesgo de cobranzaClasificación (gradient boosting)fact_cobranzas, historial clienteScore de morosidad por cliente
Optimización de stockPunto de reorden + EOQfact_inventario_snap, lead timesSugerencia de reposición
NL2SQL / Chat BILLM + capa semántica (Cube)metadatos + KPIs versionadosRespuesta a "¿cuál fue mi margen en mayo?"

Decisión: el LLM no consulta SQL crudo; opera sobre la capa semántica (definiciones de KPI gobernadas), evitando alucinaciones de métricas y respetando RLS.


8. Riesgos y mitigaciones

#RiesgoImpactoMitigación
R1Slot de replicación lógica crece y llena disco del RDS si el consumidor caeCaída del OLTPMonitoreo de lag del slot; alerta + auto-restart; max_slot_wal_keep_size
R2Doble conteo por mezcla de granosKPIs erróneos en comité directivoGrano documentado por fact + tests dbt de unicidad
R3Definiciones de KPI divergentes entre áreasPérdida de confianza en el BICapa semántica única (metrics-as-code), revisión de Finanzas
R4Fuga de datos entre tenants en dashboardsBrecha contractual/legalRLS obligatorio en capa semántica + tests de aislamiento
R5TC SBS no cargado para una fechaMétricas en PEN incorrectasJob de carga TC con backfill y validación de cobertura diaria
R6Costo de DW columnar (Redshift/Snowflake) elevado para PYMEsMargen del SaaSAlternativa lakehouse DuckDB+S3/Iceberg para tenants pequeños
R7Latencia CDC degradada en picosStock desfasadoParticionado de topics Kafka + autoscaling de consumidores

9. Alternativas arquitectónicas evaluadas

ComponenteOpción elegidaAlternativaPor qué
CDCDebezium + Kafka/MSKTriggers + tabla auditoríaWAL no toca tablas calientes; captura deletes; escala mejor
DWRedshift (grandes) / DuckDB+S3 Iceberg (PYMEs)BigQuery, SnowflakeCosto-eficiencia por segmento; coherencia con AWS del stack
Transformacióndbt (ELT)Spark/EMR (ETL)dbt = SQL versionado, tests, docs; menor complejidad operativa
Capa semánticaCubedbt metrics, LookMLCube expone API REST/SQL/GraphQL con RLS para el dashboard React
Visualización embebidaReact + Recharts (dashboard ejecutivo)Power BI EmbeddedControl total UX y costo; Metabase/Superset para self-service

Recomendación de fasing. Fase 1 (MVP): batch nocturno con dbt + DuckDB/S3, dashboard ejecutivo con 12 KPIs núcleo. Fase 2: CDC streaming para ventas/inventario. Fase 3: módulo IA (forecast, anomalías, NL2SQL). Esto entrega valor financiero temprano sin la complejidad operativa del streaming desde el día uno.