Fix recargo/descuento en pago parcial

PCPOS — CYRE · pos_prom.cpp · función TICKET::PromocionesMdeP

Validado en producción (runtime + eproCons) v20260728F srcs/pos_prom.cpp — compila OBJ_OK

00Resumen

Cuando el cliente paga una parte del ticket con un medio de pago que tiene recargo o descuento porcentual (ej. VISA/CORDOBESA), el sistema calculaba mal el porcentaje y el total cobrado (DDMP) no coincidía con el total fiscal del ticket (Tresu/Tdeta). Eso generaba el descuadre de las ~45 zetas de agosto/septiembre. El fix corrige el cálculo en 3 puntos encadenados de la misma función y quedó validado corriendo el POS real.

Regla de negocio (modelo B): el monto que tipea el cajero es el BRUTO que va a la tarjeta, con el recargo/descuento adentro. Ese importe no se puede alterar después: el recargo se extrae de ese bruto, no se suma encima.

01El problema — por qué el bruto rompe el cálculo

El recargo/descuento es un porcentaje. Aplicar el % sobre un bruto que ya lo contiene da un valor inflado. Ejemplo con recargo del 10% y un bruto de 100000 que el cajero pone en la tarjeta:

❌ Cálculo previo (bug)

Trata el bruto como si fuera la base:

recargo = bruto × p/100
        = 100000 × 10/100
        = 10000   (inflado)

100000 no es la base: ya trae el recargo adentro.

✔ Cálculo correcto (fix)

Extrae la base contenida en el bruto:

base    = bruto / (1 + p/100)
        = 100000 / 1.10 = 90909.09
recargo = base × p/100
        = 9090.91   (correcto)

Equivale a bruto × p/(100+p).

El segundo golpe: eproCons

Aunque se corrigiera el cálculo, más abajo en la misma función eproCons.getPromoySugerido() volvía a pisar el valor con la fórmula vieja (×/100). Por eso el fix necesita, además, neutralizar ese override para el caso del parcial. Este punto es el que solo se ve corriendo el código (no compilando) — el binario del laboratorio simplificado no tenía eproCons y por eso al principio "daba bien" y en producción no.

02Antes → Después (código)

ANTES

// una sola linea, sin distinguir bruto
_dTmpMontoPromocion =
   (dMontoAPromo * Porcentaje) / 100L;

// ... mas abajo:
if(!eproCons.getPromoySugerido(...)){
   _dTmpMontoPromocion = dPromo; // re-pisa
}

DESPUÉS (5 ajustes)

// guarda del caso (conservadora)
int iParcialBrutoPorcentaje =
  (iPromoIngresoMdeP==1) && !iIngresoLoSugerido
  && (Porcentaje>-100) && (Action==MA_PORCENTAJE)
  && !VALIDAR(Ticket.Percepcion)
  && eproCons.isSinglePromo(IdPromo);

// extraccion de base + topeo
if(iParcialBrutoPorcentaje){
  base = dMontoIngresado/(1.0+Porcentaje/100.0);
  if(base>dMontoDisponibleParaPromo) base=...;
  if(base>dTotalDelTicket) base=...;
  _dTmpMontoPromocion = (base*Porcentaje)/100L;
}

// fiscal alineado (Tresu = DDMP)
// eproCons NO pisa si iParcialBrutoPorcentaje

03Comparación numérica — antes vs después

Bruto de 100000 a la tarjeta, resto en EFECTIVO. Base del ticket = 183000 (caso de laboratorio validado).

Recargo 10%

ConceptoANTES (bug)DESPUÉS (fix)
Bruto a la tarjeta100000.00100000.00
Base cubierta90000.0090909.09
Recargo (RecD)10000.009090.91
Saldo restante93000.0092090.91
DDMP vs Tresu≠ (descuadre)= 201300... OK

Descuento 14%

ConceptoANTES (bug)DESPUÉS (fix)
Bruto a la tarjeta100000.00100000.00
Base cubierta114000.00116279.07
Descuento (RecD)-14000.00-16279.07
Saldo restante69000.0066720.93
DDMP vs Tresu≠ (descuadre)= 166720.93 OK
Caso real que originó el reporte (ticket 439407, Suc44 POS4, VISA 20%, parcial 30000):
ANTES → recargo mdep 6000 pero Tresu usaba 5730.83DDMP 34654.15 ≠ Tresu 34384.98, descuadre -269.17. El cliente fue sobrecobrado.
DESPUÉS → recargo 5000 (=30000×20/120), DDMP = Tresu, descuadre cero.

04Diagrama de flujo (bug vs fix)

01 / Ingreso del pago 02 / PromocionesMdeP (FIX) 03 / Resultado EX / Version anterior (BUG) Entrada bruta Calculo correcto Cierre Bruto 100000 c/recargo modelo B Extrae base bruto/(1+p/100) aj.1-3 Consistencia eproCons+fiscal aj.4-5 DDMP=Tresu descuadre 0 OK recargo/100 inflado 10000 eproCons re-pisa DDMP!=Tresu descuadre parcial % antes Legend User UI Agent logic Policy Tool action Context / trace

05Los 5 ajustes en pos_prom.cpp

06Validación

Corrido con el árbol de producción completo (con eproCons) compilado en el entorno DOSBox del kit, con las bases Btrieve reales:

CasoDIAG FIXDIAG FINALRecD finalReconciliación
Recargo 10%9090.919090.919090.91DDMP=Tresu
Descuento 14%-16279.07-16279.07-16279.07DDMP=Tresu

DIAG FIX == DIAG FINAL confirma que eproCons ya no pisa. Parc=1 confirma que la guarda conservadora dispara en los casos reales. CAE OK.

Convergencia: un análisis independiente (Codex) llegó a la misma solución de 3 partes, incluida la neutralización de eproCons — validación cruzada fuerte. La versión fusionada adopta además sus guardas de robustez. Y comparando con la versión previa (2025-10-14): el código del recargo es idénticono hubo regresión, el bug estaba latente.

07Alcance y pendiente

Tradeoff conservador: casos con percepción, multi-promo o promo no-porcentual quedan en el camino original (el bug reportado no es de esos: consumidor final, % único, sin percepción). Ampliable si aparecen.

PCPOS CYRE · fix recargo/descuento en pago parcial · pos_prom.cpp · generado 16/09/2026