ADR-0001 · Fórmula de margen compuesta (no simple)

ADR-0001 · Fórmula de margen compuesta (no simple)

  • Status: ✅ Accepted
  • Fecha: 2026-04-12
  • Decisores: Equipo técnico + gerencia comercial
  • Tags: venta · formula · chesserp

Contexto y problema

ChessERP (el ERP fuente) calcula Vta y Compra con criterios distintos:

  • Vta incluye NDCONs — movimientos financieros sin mercadería:
    • Notas de Débito por cheques rechazados
    • Intereses por mora
    • Recargos financieros
    • Ajustes contables
  • Compra solo cuenta líneas con artículo real.

Si calculamos Margen = Vta − Compra de forma ingenua, los NDCONs (que no tienen costo) inflan el margen aparentemente al 100% — completamente incorrecto desde el punto de vista comercial.

Verificación con un caso real

Cliente SGO | PABLO MOLINA (Abril 2026):

Vta total Compra Margen ingenuo Margen real
Sin filtrar NDCONs $16.059.265 $10.587.444 34,07 % ❌
Filtrando NDCONs $13.417.695 $10.587.444 21,09 %

El margen ingenuo dice 34% pero el real (alineado con ChessERP) es 21%.


Drivers de la decisión

  1. Coherencia con ChessERP — el ERP es la fuente única de verdad comercial.
  2. No distorsionar el margen — los NDCONs financieros no deben aparecer como mercadería rentable.
  3. No perder visibilidad de la Vta total — los NDCONs siguen siendo ingresos al cliente.

Opciones consideradas

Opción A · Filtrar NDCONs con WHERE dsArticulo IS NOT NULL

SELECT SUM(subtotalNeto) AS vta,
       SUM(preciocomprant * cantidadesTotal) AS costo
FROM ventas
WHERE anulado = 'NO' AND dsArticulo IS NOT NULL;

Rechazado: pierde la Vta total. El cliente comercial vio ingresos que después no aparecen en el reporte.

Opción B · Reportar 2 márgenes (con y sin NDCONs)

Rechazado: confunde a la gerencia. ChessERP muestra UNO solo.

Opción C · Fórmula compuesta (★ elegida)

-- Vta total (con NDCONs)
SUM(subtotalNeto) AS vta_total

-- Vta para denominador del margen (sin NDCONs)
SUM(CASE WHEN dsArticulo IS NOT NULL AND dsArticulo != ''
         THEN subtotalNeto ELSE 0 END) AS vta_reales

-- Costo
SUM(preciocomprant * cantidadesTotal) AS costo

-- Margen y contribución
contribucion = vta_reales − costo
%_margen     = contribucion / vta_reales

Aceptado: coincide con ChessERP al peso.


Decisión

Adoptamos la fórmula compuesta como constantes SQL reutilizables en venta/includes/data_api.php (líneas 27-30):

const SUM_VENTAS_REALES = "SUM(CASE WHEN dsArticulo IS NOT NULL AND dsArticulo != ''
                                    THEN subtotalNeto ELSE 0 END)";
const SUM_COSTO_NETO    = "SUM(preciocomprant * cantidadesTotal)";
const EXPR_CONTRIBUCION = "(" . SUM_VENTAS_REALES . " - " . SUM_COSTO_NETO . ")";
const EXPR_MARGEN_PCT   = "CASE WHEN " . SUM_VENTAS_REALES . " > 0
                                THEN " . EXPR_CONTRIBUCION . " / " . SUM_VENTAS_REALES . "
                                ELSE 0 END";

Estas constantes se usan en todas las funciones _apiMargen* y _apiKpis.


Consecuencias

✅ Positivas

  • Coincide con ChessERP al peso — verificado con SAMARELLI, MOLINA, totales mensuales.
  • Tabs de Rentabilidad coherentes — los 6 tabs y los 2 Resúmenes Cruzados muestran el mismo TOTAL.
  • Vta total visible — la gerencia ve todos los ingresos.
  • Margen no inflado — no aparece 100% por culpa de NDCONs.

⚠️ Negativas / consideraciones

  • Aparecen filas (SIN ARTÍCULO) en tablas que agrupan por proveedor/artículo. Son los NDCONs.
  • Requiere COALESCE en GROUP BY para mantener coherencia (ver ADR-0009).
  • Duplicación inicial: las constantes estaban replicadas entre data_api.php y filter.php (corregido en refactor F1-4 del roadmap).

Referencias