.soporte/20260922. El traffic.log (release 20260921) fue clave para diagnosticar el tema 1.| # | Tema | Módulo | Diagnóstico | Estado |
|---|---|---|---|---|
| 1 | Beneficio para factura A/B no aplica (SIN_BENEFICIOS · FACTURA_NO_COINCIDE) | BeneficiosCenter | El gateway no envía el tipo de factura (no existe en el momento del QR) | Corregido + E2E · v1.4.0 |
| 2 | Fechas del emulador con +3h en autorizadorintencionfecha/trxfecha | GatewayQR | El emulador emitía UTC (Z); al persistir se re-parseaba como local | Corregido + E2E |
| 3 | trx.total sin el descuento aplicado | RouterQR | El descuento YA está persistido en txrx.rx (JSON in/out) | Observación |
La transacción se resuelve como SIN_BENEFICIOS y, al abrir el detalle, muestra MOTIVO: FACTURA_NO_COINCIDE.
Causa raíz. En el momento del pago por QR la factura todavía no existe (se define después, en el paso fiscal/POS). Por eso GatewayQR envía el bloque invoice en null de forma fija:
// GatewayQR — BeneficiosCenterClient.java:278-284 // Buyer invoice data is not available on the gateway yet (known limitation) invoice.put("type", JSONObject.NULL); invoice.put("taxIdKind", JSONObject.NULL); invoice.put("taxId", JSONObject.NULL); invoice.put("extraCustomerTypeId", JSONObject.NULL);
Y BeneficiosCenter descarta el beneficio cuando la factura viene nula y el beneficio está restringido por tipo de factura:
// BeneficiosCenter — BenefitResolver.java:123-126 if (!beneficio.tiposFactura().isEmpty() && (request.invoiceType() == null || !beneficio.tiposFactura().contains(request.invoiceType()))) { return MotivoDescarte.FACTURA_NO_COINCIDE; // null SIEMPRE descarta }
Prueba en el traffic. Dos transacciones consecutivas, misma sucursal y POS, el mismo invoice nulo — la única diferencia es la restricción de factura del beneficio:
| requestId | invoice.type | Resultado |
|---|---|---|
| 1206791 | null | FACTURA_NO_COINCIDE — benefits: [] |
| 1206792 | null | ✓ aplicó benefit 1 (DINI_NEGOCIOS, 10%) |
La 1206792 solo pudo pasar el filtro con type=null porque se le quitó la restricción de factura al beneficio. Conclusión: cualquier beneficio restringido por tipo de factura nunca aplicará por el canal QR. Lo mismo vale para extraCustomerTypeId (códigos extra).
Solución — filtro de factura neutralizado en el resolver. Se quitó el descarte FACTURA_NO_COINCIDE ([BenefitResolver.java:123](backend/src/main/java/com/tipre/beneficioscenter/resolucion/BenefitResolver.java)). Un beneficio con restricción de factura ya no se descarta nunca. El campo tiposFactura y su tabla quedan dormidos (no se borran): para reactivar el check el día que el gateway pueda enviar la factura, se restaura el bloque. Sin migración de esquema. Release v1.4.0.
Verificación E2E OK — beneficio con restricción factura=[B,C] sembrada, pegándole directo al /resolver:
| invoice.type | Antes | Ahora |
|---|---|---|
| "A" | FACTURA_NO_COINCIDE | ✓ aplica |
| "B" | ✓ aplica | ✓ aplica |
| null | FACTURA_NO_COINCIDE | ✓ aplica |
Y el money-path completo por GatewayQR (factura null) con el beneficio restringido: importe_final 9000, importe_recdesc -1000, pm 999. 44 tests del resolver en verde.
Los campos autorizadorintencionfecha y trxfecha de transacciones originadas por el emulador se guardaban 3 horas adelantadas (ej. 15:26:52 por un evento de las 12:26). Con el autorizador real (Tecso) las fechas salían bien.
Causa raíz. El emulador seteaba la fecha con getDatenow().toString(). getDatenow() devuelve un Instant, y Instant.toString() siempre serializa en UTC con Z (ej. 14:49Z por un evento de las 11:49 en Córdoba). Al persistir, getDatenowFromString() hace substring(0,23) — corta la Z — y re-parsea esos dígitos UTC como si fueran hora local → +3h. Tecso nunca falló porque envía wall-clock local.
Fix. El emulador ahora emite wall-clock local (mismo formato que Tecso). No se tocó getDatenow() (lo usa el camino real).
MyCurrentDateTime.java — nuevo método:
public String getDatenowString() { return new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS") .format(Calendar.getInstance().getTime()); // local, sin 'Z' }
TrxResource.java (líneas ~1263 y ~1391): getDatenow().toString() → getDatenowString() en los dos bloques del emulador.
Verificación E2E OK — BeneficiosCenter (:8080) + GatewayQR con el war corregido (:8082, emulador ON), pago QR real por 10.000:
| Campo persistido (trx 10145) | Valor | Antes del fix |
|---|---|---|
trxfecha | 16:10:51 | (siempre OK) |
autorizadorintencionfecha | 16:10:52 ✓ | 19:10 (+3) |
autorizadortrxfecha | 16:10:52 ✓ | 19:10 (+3) |
Emulador emitió "intention_datetime":"2026-09-22T16:10:52.120" (local, sin Z). Hora real del evento: 16:10. Money-path OK de paso: importe_final 9000, importe_recdesc -1000, pm 999, APROBADA.
trx.total sin descuento (RouterQR) Observación documentadaEl dato del descuento NO se pierde. RouterQR ya persiste el payload JSON in/out de cada llamada a autorizador en la tabla txrx:
txrx.txrequest JSON (varchar 100000)
txrx.rxresponse JSON — contiene importe_final e importe_recdesc del body de la DINI
claveSe guarda vía txrxRepository.saveAndFlush() (TxRxService.java:46), disparado por AutorizadorService por cada trx.
El único punto "incómodo" es que trx.total muestra el bruto (se llena desde importe, no desde importe_final). Para conciliar y unir las trx, el neto/descuento se obtiene del JSON ya guardado:
SELECT trxid, JSON_VALUE(rx,'$.importe_final') AS importe_final, JSON_VALUE(rx,'$.importe_recdesc') AS importe_recdesc FROM txrx WHERE tipoaut = 'DINI' AND trxid = @trxid;
Conclusión. Es un tema de ergonomía de reporte, no de datos faltantes. Se deja como observación: el dato está disponible en txrx.rx. Si a futuro se quiere el descuento como columna directa en trx (sin parsear JSON), sería agregar importe_final/importe_recdesc a la entidad — no se hizo en esta ronda.
github.com/tipre/GatewayQR. Archivos: helper/MyCurrentDateTime.java, web/rest/TrxResource.java, pom.xml.github.com/tipre/BeneficiosCenter. Archivos: resolucion/BenefitResolver.java + tests. Revisado con code-review antes de commitear.Captura de pantalla 2026-09-22 115447.png, BeneficiosCenter_SinBeneficios_Motivo_FacturaNoCoincide.zip (RouterQR + GatewayQR + BeneficiosCenter logs, con traffic.log).