Reportado por Mesa de Ayuda (Grupo Dinosaurio) sobre POS 1 de Caroya:
| Concepto | Esperado | Lo que mostraba la caja |
|---|---|---|
| Compra del cliente (efectivo) | $7.709 | $7.709 |
| Medio de pago extra (no ingresado) | — | BANCOR MC DÉBITO $170.081 |
| Saldo del ticket | $7.709 a cobrar | −$162.372 |
El log del día del reclamo no tenía ese pago; el mismo importe aparecía en el log del
día anterior (28-08). Y el ticket que finalmente se anuló mostraba
NroRef: 132103 — el mismo número del ticket del 27-08.
10:47:34 MDEP ADD:BANCOR MC DEBIT MIngr:170081.00 SaldT:0.00 ; pago aplicado, saldo 0 10:47:38 NroRef:132103 Items:56 TOTAL:170081.00 SU PAGO:170081.00 ; ticket impreso 10:47:39 QR-JSON ... nroCmp 126101 ; comprobante CAEA emitido → FINALIZADO ... (1 hora sin un solo registro — corte de energía) 11:55:53 Inicia POS=20251007FF ; reinicio 11:56:00 22000=SAFETY_MARKPAGO=1 22001=SAFETY_MARKNROTICKET=132103 11:56:01 Rec DDMP/TMdeP T:132103 Reg:1 170081.00 ; REVIVE el pago ya cobrado
El ticket 132103 se había completado (impreso + comprobante fiscal CAEA) a las 10:47:39. Segundos después se cortó la energía. Al reiniciar, la rutina de recuperación reinyectó el pago.
09:34:57 SaYaCanc TOT:7709.00 SAL:-162372.00 ; el ticket nuevo ya nace con el fantasma 09:36:32 MDEP DEL:BANCOR MC DEBIT TotDel:170081.00 SaldTi:7709.00 ; la cajera intenta borrarlo 16:27:24 TICKET ANULADO NroRef:132103 ; mismo ticket del día 27
El contador de ticket nunca avanzó de 132103, porque el corte ocurrió antes de que el software lo incrementara.
El cobro tiene una sección crítica protegida contra corte de energía
(pos_vnta.cpp):
SetEndVueltoOK(2, CurrentTicket); // marca "pago pendiente ticket N" ... imprime ticket, emite CAEA ... if (GCmos.LastTicket==CurrentTicket) MCoun->assign(str_LastTicket, ++GCmos.LastTicket); // avanza el contador ResetEndVueltoOK(); // limpia la marca
dE_DDMP con el pago + ETres apuntando al ticket 132103.
Al reiniciar, ReStartEndVueltoOK() → ActualizarDDMPyTMdeP() hace lo que
debe para un ticket no terminado: reinyecta el pago en la tabla de medios de pago del
ticket (dE_TMdeP). El problema es que no distingue "se cortó antes de
finalizar" de "se cortó después de finalizar" — y este ticket ya tenía su comprobante.
El guard original sólo comparaba lTicket == CurrTick, sin mirar si el ticket ya
estaba cerrado.
Un chequeo mínimo en ActualizarDDMPyTMdeP(): si el ticket ya tiene comprobante
emitido (iNroComprob ≠ 0 en dE_TResu), el pago ya fue cobrado en ese
ticket cerrado y no se reinyecta. Reutiliza el camino seguro que el código ya tenía para
HaySafeMarkSaveTMdeP.
int iNoActualizarTMdeP = HaySafeMarkSaveTMdeP(); if(_TicketYaFinalizadoConComprobante(CurrTick)){ // ← NUEVO iNoActualizarTMdeP = 1; // pago ya cobrado en ticket cerrado: NO reinyectar Log(LOGERROR, LOG_AJUSTADDMPTMDEP, GCmos.NroZ, "DDMP: T:%ld ya finalizado (comprob emitido), NO se reinyecta MdeP", CurrTick); }
Helper: abre dE_TResu, ubica el ticket, devuelve 1 si iNroComprob ≠ 0.
iNroComprob ≠ 0 → el guard frena (caso del bug).
Corte durante el pago, antes del comprobante → iNroComprob = 0 → la
recuperación legítima sigue funcionando igual.
Se sembró en un banco de prueba el estado exacto del post-corte
(bT_TResu apuntando al ticket, dE_TResu finalizado con comprobante, y
dE_DDMP con el pago fantasma) y se arrancó el POS con cada ejecutable. Único
cambio: el binario. Se leyó de disco la tabla de medios de pago (dE_TMdeP) después
de cada arranque.
| Escenario | Log de recuperación | dE_TMdeP tras arrancar | Resultado |
|---|---|---|---|
| Semilla (estado post-corte) | — | vacío | — |
| ORIG (sin corrección) | Rec DDMP/TMdeP T:317075 Reg:1 170081.00 | uId=10 fTotal=170081.00 | Arrastra |
| FIXED (con corrección) | DDMP: T:317075 ya finalizado (comprob emitido) | TOTAL = 0 | Limpio |
pos_vnta.cpp (función ActualizarDDMPyTMdeP), compila y linkea limpio; probado en runtime.Rec DDMP/TMdeP… porque el contador corre antes del guard. Conviene mover ese log dentro del bloque de reinyección para no confundir a soporte (mejora cosmética, no funcional).