5.1 🎯 Overview del módulo
El módulo más analítico. No tiene sync con API externa: trabaja sobre snapshots manuales de cartera (CSV/XLSX) y los combina con datos de venta y caja ya cargados para proyectar el flujo de los próximos 30/60/90 días.
ventas y caja_raw_egresos_cajaPreguntas que responde
- ¿Cuánto cobro y pago en los próximos 7/15/30/60 días?
- ¿Mi saldo proyectado a 30 días alcanza para cubrir pagos?
- ¿Qué clientes son fantasmas (deben mucho y no compran hace meses)?
- ¿Qué vendedor tiene la peor performance de cobranza?
- ¿Qué proveedor concentra el mayor riesgo de pago?
- ¿Cuántos cheques tengo en cartera, en qué banco, de qué clientes?
- ¿Hay cheques vencidos sin presentar al cobro? (riesgo de prescripción)
5.2 📥 Datos de entrada (3 tipos)
| Tipo | Archivo del ERP | Granularidad | Tabla |
|---|---|---|---|
cliente |
Cartera de clientes (CSV/XLSX) | Una fila por cliente (aging por tramos) | cashflow_cartera_clientes |
proveedor |
Cartera de proveedores (CSV/XLSX) | Una fila por comprobante (FC/NC/ND/AD/ME) | cashflow_cartera_proveedores |
cheque |
Cheques Recibidos (CSV/XLSX) | Una fila por cheque | cashflow_cheques_cartera |
Detección de encoding del CSV
El ERP exporta CSVs en cp1252 con BOM raro. El JS detecta y aplica fallback:
let txt = new TextDecoder('utf-8', {fatal: false}).decode(buf);
// Heurística: si tiene caracteres mal codificados, probar Windows-1252
if (txt.includes('Â') || txt.includes('Ã')) {
txt = new TextDecoder('windows-1252').decode(buf);
}
5.3 🗄️ Schema
Relación entre las 4 tablas
┌──────────────────────────────────────┐
│ cashflow_sincronizaciones │
│ PK id · UK (tipo, fecha_corte) │
│ tipo ∈ {cliente, proveedor, cheque} │
└─────────────────┬────────────────────┘
│ 1
│
┌─────────────────┼─────────────────────┐
│ N │ N │ N
▼ ▼ ▼
┌──────────────────┐ ┌─────────────────────┐ ┌──────────────────┐
│ cartera_clientes │ │ cartera_proveedores │ │ cheques_cartera │
│ cliente_id │ │ comprobante │ │ numero_cheque │
│ saldo + 5 tramos│ │ fecha_vto · total │ │ banco · estado │
│ vendedor · prov │ │ vencido · atraso │ │ presentación │
└────────┬─────────┘ └──────────┬──────────┘ └────────┬─────────┘
│ │ │
│ JOIN id ▼ │ │ JOIN id ▼
▼ │ ▼
┌──────────────────┐ │ ┌──────────────────┐
│ ventas │ │ │ ventas │
│ (cruce 6 meses) │ │ │ (concentración) │
└──────────────────┘ │ └──────────────────┘
│
│
▼ extractos (futuro)
┌──────────────────┐ ┌──────────────────────────┐
│ extractos_banc. │ │ caja_raw_egresos_caja │ ← lee
│ (saldo bancario) │ │ (saldo caja físico) │ ← Saldo Caja KPI
└──────────────────┘ └──────────────────────────┘
5.4 📊 Dashboard index.php (51 KB)
10 secciones
┌─────────────────────────────────────────────────────────────┐ │ HEADER · selector de corte (dropdown con todos los snapshots)│ ├─────────────────────────────────────────────────────────────┤ │ ⚡ 5 KPI CARDS principales │ │ Saldo Caja Hoy · Cobranzas 30d · Pagos 30d │ │ Cheques Cartera · Saldo Proyectado 30d (con semáforo) │ ├─────────────────────────────────────────────────────────────┤ │ 🚨 ALERTAS AUTOMÁTICAS │ │ · 🔴 Vencido a pagar > $150M │ │ · 🟡 Saldo proyectado < $10M │ ├─────────────────────────────────────────────────────────────┤ │ 📊 AGINGS (2 columnas) │ │ Cartera Clientes (8 tramos: 0/1-7/8-15/16-30/31-60/ │ │ 61-90/91-180/+180 días) │ │ Cartera Proveedores (días de atraso) │ ├─────────────────────────────────────────────────────────────┤ │ 📅 CALENDARIO SEMANAL (próximas 4 semanas) │ │ Cols: Cobranzas · Cheques · Pagos · Neto │ │ Saldo acumulado proyectado · semáforo crítico │ ├─────────────────────────────────────────────────────────────┤ │ 👥 TOP DEUDORES CLIENTES (Tabulator) │ │ Con cruce comercial: venta 6m, días sin comprar, NDCONs │ │ Categorización automática (semáforo de riesgo) │ ├─────────────────────────────────────────────────────────────┤ │ 💸 PAGOS PRÓXIMOS / VENCIDOS (Tabulator) │ │ Vencidos (rojo) + futuros (verde) ordenables │ ├─────────────────────────────────────────────────────────────┤ │ 🧑💼 PERFORMANCE COBRANZA POR VENDEDOR │ │ Cartera total · vencido · % vencido (barra + semáforo) │ ├─────────────────────────────────────────────────────────────┤ │ 🧾 CHEQUES EN CARTERA (Tabulator) │ │ Nº cheque · banco · cliente emisor · presentación · días │ │ Semáforo: rojo=vencido, ámbar≤7d, azul≤30d, verde>30d │ ├─────────────────────────────────────────────────────────────┤ │ 🏦 CONCENTRACIÓN BANCO · 👤 CONCENTRACIÓN CLIENTE EMISOR │ │ (2 columnas) · alerta si cliente >50% es riesgo crítico │ └─────────────────────────────────────────────────────────────┘
Estructura del JSON de api/data.php
{
"ok": true,
"corte": "2026-05-20",
"cortes_dispo": ["2026-05-20", "2026-04-30", "2026-03-31"],
"saldo_caja_hoy": 58000000,
"clientes": {
"kpi": { total, d7, d15, d30, d60, vencido, clientes },
"aging": { "0_al_dia", "1_7", "8_15", "16_30", "31_60", "61_90", "91_180", "mas_180" },
"top_deudores": [{cliente_nombre, vendedor, saldo, antiguedad,
categoria, accion, venta_6m, dias_sin_comprar, …}],
"por_vendedor": [...],
"por_provincia": [...]
},
"proveedores": {
"kpi": { a_pagar, vencido, anticipos, ... },
"aging": { ... },
"top": [...],
"pagos_futuros":[...],
"vencidos": [...]
},
"cheques": {
"kpi": { n_cartera, monto_cartera, n_vencidos, monto_vencidos,
monto_endosados, monto_rechazados },
"lista": [{numero_cheque, banco, fecha_presentacion, importe,
cliente_nombre, dias_a_presentar, ...}],
"por_banco": [{banco, n, monto}],
"por_cliente": [{cliente_nombre, cuit_cliente, n, monto}]
},
"calendario": [{semana, desde, hasta, cobranzas, cheques, pagos, neto}, ...]
}
5.5 🚨 Clasificación automática de riesgo (clientes)
// Pseudocódigo PHP en api/data.php
if ($antig > 180 && $saldo > 5_000_000) $cat = 'critico_incobrable';
else if ($ndcons_monto > 0) $cat = 'critico_incumplimiento';
else if ($dias_sin_comprar > 90 && $saldo > 1M) $cat = 'fantasma';
else if ($saldo > $venta_6m / 6 * 3) $cat = 'riesgo_creciente';
else if ($antig <= 30) $cat = 'sano';
else $cat = 'normal';
Tabla de categorías y acciones sugeridas
| Categoría | Trigger | Acción sugerida | Color |
|---|---|---|---|
critico_incobrable |
+180 d antig + $5M+ | Provisionar como incobrable / Acción legal | rojo |
critico_incumplimiento |
Tiene NDCONs (cheque rechazado) | Cortar crédito / Solo contado | rojo |
fantasma |
+90 d sin comprar + $1M+ | Llamar / Verificar relación comercial | ámbar |
riesgo_creciente |
Saldo > 3 meses de venta promedio | Limitar nuevos pedidos / Negociar plan | ámbar |
sano |
Antig ≤ 30 d | Continuar normal | verde |
Casos reales detectados con datos cargados
| Cliente | Categoría | Por qué |
|---|---|---|
| LOS LEONES SRL | crítico_incumplimiento | $2.6M en NDCONs (cheque rechazado) |
| VICTOR NICOLAS VEGA | crítico_incobrable | 460 d antigüedad + saldo $4.3M |
| BARRIONUEVO FERNANDO | riesgo_creciente | Saldo 3× venta promedio mensual |
5.6 📅 Calendario semanal de flujo
Lógica de estimación de cobranza por semana
// JavaScript en index.php (frontend)
// Aproximación a partir de los tramos del aging
if (w == 0) cobr = cli.d7; // todo lo que vence en 7d → semana 1
if (w == 1) cobr = cli.d15 / 2; // mitad de 8-15d → semana 2
if (w == 2) cobr = cli.d15 / 2 + cli.d30 / 4; // resto + cuarto de 30d
if (w == 3) cobr = cli.d30 / 4 + cli.d30 / 4; // dos cuartos
Cálculo del neto semanal
neto_semana = cobranzas_estim + cheques_a_presentar − pagos_exactos
// Saldo proyectado al final de cada semana:
saldo_w1 = saldo_caja_hoy + neto_w1
saldo_w2 = saldo_w1 + neto_w2
saldo_w3 = saldo_w2 + neto_w3
saldo_w4 = saldo_w3 + neto_w4
fecha_vto real en cada comprobante.
Sólo las cobranzas se estiman (porque el CSV de clientes viene agrupado por tramos,
no fecha por fecha).
5.7 🔄 Cruce comercial (el "superpoder")
Para cada cliente con deuda, se extrae su historial de los últimos 6 meses desde
la tabla ventas:
SELECT v.idCliente,
COUNT(DISTINCT v.sync_id) AS meses_activo_6m,
SUM(v.subtotalNeto) AS venta_6m,
MAX(v.fechaComprobate) AS ultima_compra,
DATEDIFF(?, MAX(v.fechaComprobate)) AS dias_sin_comprar,
SUM(CASE WHEN v.dsArticulo IS NULL THEN v.subtotalNeto ELSE 0 END) AS ndcons_monto,
SUM(CASE WHEN v.dsArticulo IS NULL THEN 1 ELSE 0 END) AS ndcons_n
FROM ventas v
WHERE v.sync_id IN (últimos 6 meses)
AND v.anulado = 'NO' AND v.idCliente IS NOT NULL
GROUP BY v.idCliente;
Esto enriquece a cada deudor con:
- Cuántos meses compró de los últimos 6.
- Cuánto vendió en total.
- Última fecha de compra y días desde entonces.
- NDCONs (cheques rechazados, intereses) → flag de incumplimiento.
KPIs de cheques
SELECT
SUM(CASE WHEN estado='EN CARTERA' THEN 1 ELSE 0 END) AS n_cartera,
SUM(CASE WHEN estado='EN CARTERA' THEN importe ELSE 0 END) AS monto_cartera,
SUM(CASE WHEN estado='EN CARTERA' AND fecha_presentacion < :corte
THEN importe ELSE 0 END) AS monto_vencidos,
SUM(CASE WHEN estado='PAGO A TERCEROS' THEN importe ELSE 0 END) AS monto_endosados,
SUM(CASE WHEN estado='RECHAZADO' THEN importe ELSE 0 END) AS monto_rechazados
FROM cashflow_cheques_cartera
WHERE fecha_corte = :corte;
monto_vencidos mide riesgo operativo: cheques EN CARTERA
cuya fecha de presentación ya pasó. Cada uno es plata estancada (o peor, riesgo de
prescripción a 30 días post-vencimiento).
5.8 ⚠️ Hallazgos específicos del módulo
| ID | Hallazgo | Prioridad |
|---|---|---|
CSH-01 |
reset_db.php y delete-sync.php sin auth — borrar todo es trivial. |
Crítico |
CSH-02 |
Heurística de cobranza semanal (cuartos arbitrarios) — no usa fechas exactas de FCs. | Medio |
CSH-03 |
Cheques endosados (PAGO A TERCEROS) no se cruzan con cancelación del FC del proveedor. |
Medio |
CSH-04 |
cashflow_extractos_bancarios creada pero sin sync ni UI — schema sin uso. |
Medio |
CSH-05 |
Categorías de riesgo hardcodeadas en PHP con umbrales mágicos ($5M, 180d, $1M). |
Medio |
CSH-06 |
Match cliente↔venta por idCliente rígido — si el ERP cambió el nombre o duplicó el cliente, falla. |
Bajo |
CSH-07 |
Cliente duplicado en el ERP aparece como 2 entidades en el dashboard. | Bajo |
CSH-08 |
Sin alerta de cheques próximos a prescribir (30 d post-vencimiento). | Medio |
5.9 🚀 Propuestas específicas del módulo
P-CSH-1 · Reglas de riesgo en tabla DB
CREATE TABLE cashflow_reglas_riesgo (
id INT PK AI,
categoria VARCHAR(40),
prioridad TINYINT, -- orden de evaluación
condicion VARCHAR(255), -- DSL simple: "antig>180 AND saldo>5000000"
accion VARCHAR(255),
color VARCHAR(7), -- #ef4444
activo TINYINT(1) DEFAULT 1
);
-- Engine de evaluación en PHP que parsea el DSL
-- UI admin para editar umbrales sin tocar código
P-CSH-2 · Sync de extractos bancarios
La tabla cashflow_extractos_bancarios ya existe (1.744 filas).
Crear sync.php?tipo=extracto para alimentarla y agregar al dashboard:
- Conciliación:
egr_transferenciasde caja vsdebitodel extracto. - Detección de movimientos no contabilizados.
- Saldo bancario en KPI principal (hoy solo está saldo de caja).
P-CSH-3 · Cruce de cheques endosados
Cuando un cheque cambia a PAGO A TERCEROS:
- Match con un FC de proveedor por monto + fecha.
- Descontar de "pagos a proveedor" (hoy se ignora el endoso).
- Mostrar trazabilidad en el detalle del cheque y del FC.
P-CSH-4 · Forecast de cobranza con regresión
En vez de "cuartos arbitrarios", usar histórico real de cobranza:
- De los cortes pasados, calcular qué % de cada tramo se cobró en cada semana siguiente.
- Ajustar por estacionalidad (mes calendario) y vendedor.
- Devolver intervalo de confianza (±15%).
P-CSH-5 · Alerta de cheques próximos a prescribir
-- Cheques EN CARTERA presentados hace > 0 días y < 30 días
SELECT COUNT(*), SUM(importe)
FROM cashflow_cheques_cartera
WHERE estado = 'EN CARTERA'
AND fecha_corte = (SELECT MAX(fecha_corte) FROM cashflow_sincronizaciones)
AND fecha_presentacion < CURRENT_DATE
AND fecha_presentacion >= DATE_SUB(CURRENT_DATE, INTERVAL 30 DAY);
-- → KPI rojo en dashboard: "X cheques en zona de prescripción"
P-CSH-6 · Snapshot diff entre cortes
Cuando hay 2+ cortes, mostrar evolución MoM por cliente:
- Clientes que subieron de saldo entre cortes (riesgo creciente).
- Clientes que cancelaron deuda (action: agradecer + ofrecer límite mayor).
- Clientes nuevos vs salidos de la cartera.
P-CSH-7 · Email diario automático del estado
Cron 06:00 → enviar email con:
- Saldo proyectado a 30 días (con semáforo).
- Top 5 deudores nuevos / con saldo creciente.
- Cheques que se presentan hoy.
- Pagos del día (vencidos + nuevos vencimientos).