IVAFix recorre los comprobantes de POS guardados como JSON, corrige el IVA 10,5% que quedó calculado al 11% y completa los pagos QR aprobados que nunca se asentaron — siempre sin tocar lo cobrado y respaldando el original antes de escribir.
El mismo ticket se guarda como un documento JSON en la columna jsonticket de una tabla de SQL Server (por defecto trx). IVAFix lo lee, diagnostica y —solo si se lo pedís— escribe la corrección. El escaneo es siempre dry-run.
Detecta tickets donde el IVA reducido se calculó al 11% y redistribuye la diferencia liberada dentro del mismo ticket, con una línea de ajuste, sin cambiar el total cobrado.
Encuentra tickets QR (ROUTERQR / Mercado Pago) aprobados que quedaron sin sección de PAGO y la arma: agrega los movimientos y deja el ledger balanceado.
La alícuota reducida en Argentina es 10,5%. En algún punto, 10.5 se truncó a 11 y el IVA se calculó como base × 0,11. El 21% no está afectado: ya es entero.
| Concepto | Cálculo (neto 1447,96) | IVA |
|---|---|---|
| Correcto (10,5%) | round(1447,96 × 0,105) | 152,04 |
| Como quedó (11%) | round(1447,96 × 0,11) | 159,28 |
| Diferencia liberada | 159,28 − 152,04 | 7,24 |
Como no se puede bajar el total, la diferencia se reinyecta como una línea de ajuste en el mismo ticket — del mismo producto (default) o un ítem exento — y se reconcilian los movimientos. El total queda idéntico; el IVA, bien discriminado.
Después de corregir en memoria, el motor valida dos invariantes: que la suma de componentes dé el total y que la corrección no mueva el ledger. Los PAGO reales traen importes de alta precisión (≈8 decimales) y las VENTA van a 2, así que el ledger original casi nunca es 0 exacto — por eso se valida el cambio, no el cero absoluto (tolerancia 0,005). Si al reducir una VENTA su PAGO no se empareja 1:1 (pago consolidado, anulaciones, promos), el Δ salta y el ticket se marca error: validation y no se escribe.
En tickets QR a veces el cobro se aprueba pero la sección de PAGO no se graba. Quedan VENTA sin PAGO, el ledger abierto, montoPagado y ordenDePago.monto en 0, pero con "estadoPago":"PAGO_APROBADO" y la orden en PAGADA.
movimientoId = id de la VENTA.origenId = idGlobalUUID del medio (no el paymentId de Mercado Pago).montoPagado y ordenDePago.monto = neto a pagar (totalAPagar.monto), nunca el bruto.paymentId, orderid, referencia — vienen de la respuesta del procesador y no están en el ticket, así que no se regeneran; el voucher impreso tampoco. Lo que sí queda correcto es el asiento de movimientos, el ledger en cero y los montos.Una interfaz web local (solo 127.0.0.1) para conectar, elegir el control, escanear en dry-run, inspeccionar cada ticket y aplicar selectivamente. Esta es una reconstrucción fiel de la pantalla real.
| trx.id | Trx | Resultado | Ítems | Total |
|---|---|---|---|---|
| 190 { } ver JSON |
CLOSE | a completar | 8 venta(s) → +8 pago(s) origenId 2825472 |
18.071,72 |
| 772 { } ver JSON |
CLOSE | a completar | 10 venta(s) → +10 pago(s) origenId 2842377 |
46.236,00 |
| 181 { } ver JSON |
ANULADO | a completar | 1 venta(s) → +1 pago(s) origenId 2824788 |
50,00 |
Dos indicadores de problema fijos — Con IVA mal (modo IVA) y Falta pago (modo PAGO) — más Leídos, Completados y Errores. En la grilla, cada fila trae su estado y el botón { } ver JSON, que abre el documento completo con toggle original / resultado.
En modo PAGO, los QR aprobados salen a completar (seleccionables); los no aprobados salen no aprobado (solo informan, no se tocan).
Fat-jar autocontenido (Jackson + mssql-jdbc vía shade). Requiere JDK 17+ y Maven.
# compilar cd ivafix-tool mvn clean package # → target/ivafix.jar # cockpit (recomendado; única vía para Control PAGO) java -jar target/ivafix.jar --ui # CLI del Control IVA — dry-run y luego aplicar java -jar ivafix.jar --server SRV --db MiBase --user sa --pass *** java -jar ivafix.jar --server SRV --db MiBase --user sa --pass *** --apply
Al arrancar con --ui imprime una URL con token (http://127.0.0.1:8742/?t=…). Si refrescás, usá Ctrl+F5. Sin --apply en CLI, nunca escribe.
| Endpoint | Qué hace |
|---|---|
| /api/scan | Recorre y reporta (dry-run). El campo control = iva | pago elige el motor; en pago pre-filtra en SQL los candidatos. |
| /api/apply | Aplica el control activo a los ids seleccionados, en transacción, con respaldo previo. |
| /api/ticket | Devuelve el JSON completo (original + resultado) para el visor. |
| /api/profiles | Lista / guarda / borra perfiles de conexión (archivo local). |
Al aplicar (y solo entonces), cada registro se respalda y se actualiza en transacción; si algo falla, rollback de ese registro.
trx_jsonticket_bkp con trx_id, jsonticket_original y un hash SHA-256.INSERT del original, UPDATE con el JSON resultante, commit.UPDATE t SET t.jsonticket = b.jsonticket_original FROM trx t JOIN trx_jsonticket_bkp b ON b.trx_id = t.id WHERE t.id = <id>;
SELECT jsonticket crudo.