D1 π Sync mensual de ventas (lote Γ lote)
Hostinger mata HTTP largas a los ~4 minutos β AJAX lote Γ lote orquestado desde el browser.
Usuario Browser (sync_api.php) Server (sync_api_process.php) ChessERP
β β β β
β click "Sync" β β β
ββββββββββββββββββββββββΊβ β β
β β POST action=init β β
β β {mes, anio} β β
β βββββββββββββββββββββββββββββββΊβ β
β β β DELETE sync previo β
β β β INSERT sincronizaciones β
β β β estado='procesando' β
β ββββββββββββββββββββββββββββββββ€ {ok, sync_id} β
β β β β
β β ββββββββ LOOP lote=1..N ββββββ β
β β β β β
β β β POST action=lote β β
β β β {sync_id, lote, mes, anio} β β
β β βββββββββββββββββββββββββββββΊβ β
β β β β login() (1er lote) β
β β β ββββββββββββββββββββββββββββββββΊβ
β β β βββββ sessionId ββββββββββββββββ€
β β β β GET /ventas?nroLote=N β
β β β ββββββββββββββββββββββββββββββββΊβ
β β β βββββ rows[] (200-2000) ββββββββ€
β β β β INSERT ventas (prepared) β
β β ββββββββββββββββββββββββββββββ {ok, filas, finalizado} β
β β β β β
β β β progress bar update β β
β β β si finalizado==false β β β
β β β lote++ y repite β β
β β ββββββββββββββββββββββββββββββ β
β β β β
β β POST action=finalizar β β
β βββββββββββββββββββββββββββββββΊβ β
β β β UPDATE estado='completado' β
β β β fecha_fin=NOW() β
β ββββββββββββββββββββββββββββββββ€ {ok} β
β β
"Mes cargado" β β β
βββββββββββββββββββββββββ€ β β
Tiempo tΓpico: 25-40 segundos para ~20K filas (3-8 lotes).
D2 βοΈ Pipeline 3-tier de clasificaciΓ³n (caja gastos)
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β ENTRADA: 6 archivos Excel del ERP β
β ββββββββββββββββββββββββββββββββββββββββ β
β 1. EgresosCaja.xlsx β
β 2. ConceptosDeGastos.xlsx (catΓ‘logo persistente) β
β 3. EgresosDetalle.xlsx β FUENTE de los gastos β
β 4. GastosTipificados.xlsx β Nivel 1 PRIMARIA β
β 5. MovimientoDetalle.xlsx β Nivel 2 FALLBACK β
β 6. MovimientoResumido.xlsx (opcional, para IVA) β
ββββββββββββββββββββββββββββββββββββββ¬βββββββββββββββββββββββββββββ
β
β SheetJS parsea cliente
β Filtro por operaciΓ³n:
β 'compras / gastos - caja' Γ³
β 'gastos - liquidaciones'
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Por cada fila de EgresosDetalle filtrada: β
β βββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β Extrae nΒΊ ME de col "Observaciones" via regex: β β
β β /ME\s+0*(\d+)-0*(\d+)/i β β
β β β (serie, numero, empresa) β β
β ββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββ β
ββββββββββββββββββββββββββΌβββββββββββββββββββββββββββββββββββββββββ
βΌ
ββββββββββββββββββββββββββ
β ΒΏMatch en gastosMap? β
β key = (s, n, e) β
βββββββββ¬ββββββββββ¬βββββββ
SI NO
β β
βΌ βΌ
ββββββββββββββββββββββββ ββββββββββββββββββββββββ
β NIVEL 1: GT β β ΒΏMatch en movDetMap?β
β rubro_id = GT.rubro β β key = (s, n, e) β
β concepto = GT.conc β ββββββ¬βββββββββββ¬βββββββ
ββββββββββββ¬ββββββββββββ SI NO
β β β
β βΌ βΌ
β ββββββββββββββββββββββββ ββββββββββββββββββββββββββββ
β β NIVEL 2: MD + Conc β β NIVEL 3: SIN CLASIFICAR β
β β MD β concepto_id β β rubro = 'SIN CLASIFICAR' β
β β Conc[id] β rubro β β concepto_libre = obs[:200]β
β ββββββββββββ¬ββββββββββββ ββββββββββββββββ¬βββββββββββββ
β β β
ββββββββββββββββββ΄βββββββββββββββ¬ββββββββββββββββ
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Mapeo de provincia β
β cajero = cajaMap[`${empresa}-${cajaNro}`] β
β provincia = CAJERO_PROV[cajero.toLowerCase().trim()] || 'OTRAS'β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β INSERT caja_gastos (sync_id, mes, anio, provincia, β
β rubro_id, rubro, concepto_id, β
β concepto_libre, tipo_comprobante, β
β serie, numero, empresa, caja_nro, β
β cajero, fecha, monto) β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Bug histΓ³rico fix v2.0.2: el parser leΓa col 6 (Tipo) en vez de col 5 (Comprobante). Ver Β§10 hallazgo CAJ-bug-1.
D3 π¦ Sync bulk multi-mes (caja)
Usuario sube N archivos (cada uno con M meses)
ββββββββββββββββββββββββββββββββββββββββββββββββ
β
βΌ
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β SheetJS parsea TODOS los archivos β
β β cajaRowsGlobal, detalleRowsGlobal, tipificadosRowsGlobalβ
β movDetalleRowsGlobal, movResumidoRowsGlobal, β
β conceptosRowsGlobal β
ββββββββββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββββ
β
βΌ
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β JS escanea fechas β detecta meses ΓΊnicos β
β meses = [{mes:1, anio:2025}, {mes:2, anio:2025}, ...] β
ββββββββββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββ
β Para cada mes en la cola: β
β β
β 1. POST init.php β sync_id β
β β
β 2. Filtra cada Excel por fecha β
β que coincida con (mes, anio) β
β β
β 3. Ejecuta pipeline 3-tier β
β sobre las filas filtradas β
β β
β 4. POST save_raw.php Γ 5-6 tablas β
β (linkeadas a este sync_id) β
β β
β 5. POST save.php Γ N (caja_gastos) β
β β
β 6. POST save.php {finalizar:true} β
β β caja_rebuild_cajeros() β
β β caja_audit_log() β
β β
βββββββββββββββββββββββββββββββββββββββ
β
βΌ
β
Todos los meses
self-contained en BD
Antes de v2.0.1: los archivos compartidos quedaban con el sync_id del primer mes. Eliminar ese mes borraba la raw histΓ³rica de todos los demΓ‘s.
D4 πΈ Cashflow data.php (composiciΓ³n del JSON)
GET /reportes/cashflow/api/data.php?corte=2026-05-20[&sucursal=...]
β
βΌ
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β 1) Validar corte (regex YYYY-MM-DD) o usar MAX(fecha_corte) β
β 2) cashflow_ensure_schema() β
β 3) Listar sucursales disponibles (para selector) β
ββββββββββββββββββ¬ββββββββββββββββββββββββββββββββββββββββββββββββββ
β
ββββββββββββββββββΌβββββββββββββββββββββββββββββββββββββββββββββ
β Bloque βΌ β
β CLIENTES cartera_clientes β
β β
β ββ KPI: COUNT, SUM(saldo_total), SUM(d7/d15/d30/d60) β
β ββ Aging: 8 tramos del antiguedad_dias β
β ββ Top 50 deudores β
β ββ Por vendedor β
β ββ Por provincia β
ββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββββββββββββ
β
ββββββββββββββββββΌβββββββββββββββββββββββββββββββββββββββββββββ
β Bloque βΌ β
β PROVEEDORES cartera_proveedores β
β β
β ββ KPI: a_pagar, vencido, anticipos β
β ββ Aging por dias_atraso β
β ββ Top proveedores β
β ββ Pagos futuros (prΓ³ximos vencimientos) β
β ββ Vencidos (ya vencidos) β
ββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββββββββββββ
β
ββββββββββββββββββΌβββββββββββββββββββββββββββββββββββββββββββββ
β Bloque βΌ β
β CHEQUES cheques_cartera β
β β
β ββ KPI: n_cartera, monto_cartera, monto_vencidos, β
β β monto_endosados, monto_rechazados β
β ββ Lista completa con dias_a_presentar β
β ββ Por banco β
β ββ Por cliente emisor β
ββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββββββββββββ
β
ββββββββββββββββββΌβββββββββββββββββββββββββββββββββββββββββββββ
β Bloque βΌ β
β CRUCE COMERCIAL β
β β
β Para cada cliente con saldo > 0: β
β ββ GROUP BY idCliente en ventas (ΓΊltimos 6 syncs) β
β ββ Calcula: venta_6m, ultima_compra, dias_sin_comprar β
β ββ Calcula: ndcons_n, ndcons_monto β
β ββ Clasifica: critico/fantasma/riesgo/sano β
ββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββββββββββββ
β
ββββββββββββββββββΌβββββββββββββββββββββββββββββββββββββββββββββ
β Bloque βΌ β
β SALDO CAJA HOY β
β β
β SELECT total_saldo_final FROM caja_raw_egresos_caja β
β ORDER BY fecha_caja DESC LIMIT 1 β
ββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββββββββββββ
β
ββββββββββββββββββΌβββββββββββββββββββββββββββββββββββββββββββββ
β Bloque βΌ β
β CALENDARIO SEMANAL (4 semanas) β
β β
β Por cada semana w en [0..3]: β
β ββ cobranzas = heurΓstica sobre aging β
β ββ cheques = SUM(importe) cuyo fecha_pres β semana β
β ββ pagos = SUM(saldo) cuyo fecha_vto β semana β
β ββ neto = cobranzas + cheques - pagos β
ββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββββββββββββ
β
βΌ
{ ok, corte, clientes, proveedores, cheques,
calendario, saldo_caja_hoy, alertas, ... }
D5 π Cross-module bootstrap del schema
Cualquier endpoint del mΓ³dulo VENTA
ββββββββββββββββββββββββββββββββββββ
β
βΌ
ββββββββββββββββββββββββββββββββββββββββββββββββ
β require_once 'includes/db_reportes.php' β
βββββββββββββββββββββββ¬βββββββββββββββββββββββββ
β
βΌ (al primer require, sΓ³lo una vez por proceso)
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β db_reportes.php carga al inicio: β
β require_once '../../articulos/api/_schema.php' β
βββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Cuando llamΓ‘s getApiDB() por primera vez: β
β β
β 1. Crear PDO singleton β
β 2. Llamar articulos_ensure_schema($pdo) β
β ββ CREATE TABLE IF NOT EXISTS articulos_raw β
β ββ CREATE TABLE IF NOT EXISTS articulos_raw_provβ¦ β
β ββ CREATE TABLE IF NOT EXISTS β¦ (5 tablas) β
β ββ ALTER TABLE defensivos en try/catch β
β β
β 3. Return PDO β
βββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Queries de venta hacen JOIN seguro con articulos_raw β
β (la tabla siempre existe, aunque estΓ© vacΓa) β
β β
β Si estΓ‘ vacΓa β IFNULL hace fallback a pesoTotal β
β Si estΓ‘ cargada β usa cat_peso Γ cantidadesTotal β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Beneficio: nunca falla con "Table 'articulos_raw' doesn't exist" aunque el mΓ³dulo ArtΓculos no se haya inicializado.
D6 π― Cross-filter (drill-down) β request lifecycle
Usuario en dashboard.php click en una fila de la tabla "Supervisores"
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
βΌ
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β JS captura el click + lee dataset del row: β
β tipo = 'supervisor' β
β valor = 'LAR | ANDREA SANCHEZ (SUPERVISOR)' β
β id = sync_id actual β
β sucursal = filtro actual β
βββββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββββ
β
βΌ
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β fetch('/reportes/venta/api/filter.php? β
β id=28&tipo=supervisor&valor=...&sucursal=...') β
βββββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββββ
β
βΌ
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β api/filter.php β
β ββββββββββββββββ β
β 1. Validar: β
β Β· id INT > 0 β
β Β· tipo β whitelist β
β Β· valor != '' β
β 2. Verificar sync existe y estado='completado' β
β 3. switch(tipo): β
β case 'supervisor': β
β query principal (KPIs filtrados) β
β query 2: top 15 proveedores β
β case 'deposito': β¦ β
β case 'provincia': β¦ β
β case 'vendedor': β¦ β
β 4. Devolver JSON β
βββββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββββ
β
βΌ
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β JS recibe el JSON y abre modal con: β
β Β· 6 KPI cards filtrados β
β Β· tabla top 15 proveedores β
β Β· breadcrumb "Volver" β cierra modal β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
D7 πΊοΈ Mapa de dependencias completo
βββββββββββββββββββββββββββββ
β u120688891_chess (BD) β
β 33 tablas Β· ~915K filas β
ββββββββββββββ¬βββββββββββββββ
β
ββββββββββββββββ¬βββββββββββββ¬βββ΄ββββββββ¬βββββββββββββ¬ββββββββββββββββ
β β β β β β
βΌ βΌ βΌ βΌ βΌ βΌ
ββββββββββ ββββββββββ ββββββββββ ββββββββββββ ββββββββββ ββββββββββββ
β VENTA β β CAJA β βCASHFLOWβ βARTICULOS β β LISTAS β β /hub/ β
β v2.4.0 β β v2.0.2 β β v1.1.0 β β (no v) β β (no v) β β /reportesβ
βββββ¬βββββ βββββ¬βββββ βββββ¬βββββ ββββββ¬ββββββ ββββββ¬ββββ βββββββ¬βββββ
β β β β β β
β βββββββββββ β β β β
β β β β β β
β β ββββββββββββββββββββ β β β
β β β β β β
βΌ βΌ βΌ βΌ βΌ βΌ
βββββββββββββββββββ ββββββββββββββββββββββ
β JOINS Y LECTURAS CRUZADAS β β SYNC DELEGADO β
β βββββββββββββββββββββββββ β β ββββββββββββββββββ β
β β’ venta β articulos_raw β β listas/sync.php β
β (peso fallback) β β β /catalogo/admin β
β β β /api_sync.php β
β β’ caja/finanzas β ventas β β (fuera de reportes)β
β (cruce rentabilidad) β ββββββββββββββββββββββ
β β
β β’ cashflow β ventas β
β (cruce comercial 6m) β
β β
β β’ cashflow β caja_raw_ β
β egresos_caja (saldo) β
β β
β β’ /hub β ventas + caja + β
β articulos_raw (stats) β
βββββββββββββββββββββββββββββ
APIs externas
βββββββββββββ
β
β HTTPS
βΌ
ββββββββββββββββββββββββββββββββ
β ChessERP β
β /AR1185/web/api/chess/v1 β
β β’ /auth/login β
β β’ /ventas?fechaDesde=β¦ β
ββββββββββββββββββββββββββββββββ
D8 π¨ Theme switching (anti-flash)
Usuario navega a /reportes/venta/dashboard.php
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β <head> β
β β
PRIMERA lΓnea del head (anti-flash): β
β <script> β
β (function(){ β
β var t = localStorage.getItem('za-theme') || 'dark'; β
β document.documentElement.setAttribute('data-theme', t); β
β })(); β
β </script> β
β β
β <link rel="stylesheet" href="style.css"> β
β β Variables CSS leen :root o [data-theme='dark'] β
βββββββββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββββββββ
β
β β
NO HAY FLASH: el atributo se setea
β antes de que el CSS pinte
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β <body> renderiza con tema correcto desde el primer pixel β
β β
β Toggle button: β
β click β cur = dark ? 'light' : 'dark' β
β localStorage.setItem('za-theme', cur) β
β html.dataset.theme = cur β
β (transiciΓ³n CSS de 0.35s) β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Clave za-theme compartida por todos los mΓ³dulos del sitio.
D9 β‘ Lifecycle de una request al dashboard
GET /reportes/venta/dashboard.php?sync=28&sucursal=ZONAS+ARIDAS
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β 1. PHP carga incluyes: β
β require config/database.php β constantes DB + API β
β require includes/db_reportes.php β
β βββΊ require articulos/api/_schema.php β
β require includes/data_api.php β
β require includes/functions.php β
βββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β 2. Validar params: β
β $syncId = (int)$_GET['sync'] β
β $sucursal = trim($_GET['sucursal']) β
β Validar sync existe + estado='completado' β
βββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β 3. loadDashboardDataFromApi($syncId, $sucursal) β
β Devuelve bundle con ~18 keys: β
β kpis, diarias, supervisores, provincias, β
β prov_sup, prov_dep, rankings, empresas, β
β localidades, devoluciones, anulados, evolucion_12m, β
β margen_vend, margen_sup, margen_dep, β
β margen_cli, margen_art, margen_prov, β
β merc_sc, abc, clientes_comp, sin_articulo β
β β
β β
~20 queries SQL ejecutadas secuencialmente β
β Tiempo tΓpico: 800ms - 2s segΓΊn mes/sucursal β
βββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β 4. Renderiza HTML inline: β
β Β· Header sticky + filtros β
β Β· Resumen Ejecutivo β
β Β· Las 14 secciones con datos pre-renderizados β
β Β· Inyecta datos JSON en variables JS: β
β const RENT_VEND_DATA = <?= json_encode(...) ?> β
β const RENT_SUP_DATA = <?= json_encode(...) ?> β
β ... etc β
βββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β 5. Browser ejecuta JS: β
β Β· Crea instancias Tabulator (6 tabs Rentabilidad) β
β Β· Crea instancias Chart.js (ventas diarias, evoluciΓ³n 12m) β
β Β· Setup event listeners (openTab, fullscreen, filter) β
β β
β Drill-downs son requests AJAX adicionales a filter.php β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
D10 π Tabulator render lifecycle (con tabs)
El usuario llega al dashboard con tab "Por Vendedor" activo
β
βΌ
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Cada tab inicializa su Tabulator AL CARGAR la pΓ‘gina: β
β β
β tabRentVend = new Tabulator("#tabulator-rent-vend", { β
β data: RENT_VEND_DATA, β
β columns: [...], β
β rowFormatter: makeRentRowFormatter(...) β
β }); β
β β
β β
Tabulator NO mide bien containers con display:none β
β β Los tabs no activos renderean con dimensiones errΓ³neas β
βββββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββββββββ
β
βΌ
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Usuario hace click en otro tab (ej. "Por Cliente"): β
β β
β openTab(event, 'rent-tab-cli') { β
β 1. Quitar .active a todos los .tab-content β
β 2. Quitar .active a todos los .tab-btn β
β 3. Agregar .active al nuevo tab-content (display:block) β
β 4. setTimeout(()=> { β
β tabRentCli.redraw(true) // β
recalcula dimensiones β
β }, 50); β
β } β
βββββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββββββββ
β
βΌ
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Cada vez que se entra a un tab por primera vez: β
β Β· Tabulator recalcula widths/heights con el container β
β ya visible β
β Β· Renderiza virtual scroll (sΓ³lo filas visibles) β
β Β· bottomCalc se ejecuta con todos los datos β
β Β· rowFormatter aplica .rent-row-top / .rent-row-bot β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
El setTimeout(50ms) es necesario porque display:block no
es inmediato (espera al prΓ³ximo reflow).