TiFactura · Notificador AFIP — informe de revisión

Qué estaba mal, y cómo quedó.

Ciclo de revisión del 2026-08-27 sobre el build v20260827: tus 4 consultas iniciales (memoria/CPU, puerto, CUIT, "PercIVA=99" de ALMACOR), la auditoría profunda de los IDs de impuestos hasta la respuesta viva de ARCA, y un Judgment Day delta con dos jueces ciegos.

build v20260827 61/61 tests Judgment Day delta: APPROVE ×2 jueces ×2 rondas IDs fiscales: verificados en vivo contra ARCA

01 — Primero lo importante

Lo que estaba MAL

Sin maquillaje. Ordenado de más grave a menor. Cada ítem dice quién lo encontró y cómo quedó resuelto.

graveEl template de config traía IDs fiscales INVENTADOS

El config-template de ejemplo tenía comind: 4, impinterno: 5, iva: 6/7/8 — valores de demo, no del catálogo ARCA. Como estos IDs viajan tal cual al XML fiscal (<ar:Tributo><ar:Id>, verificado en el código del Manager), un copy-paste sin revisar habría mandado códigos de impuesto incorrectos a ARCA. Lo detectaste vos comparando contra tu config de producción legada.

Quedó: el template trae los códigos correctos (2/3/4/99, IVA 3/4/5) con la tabla oficial completa comentada, la fuente de cada valor, y el sello de la verificación en vivo. Ver Sección 03.

riesgo realEl cliente por defecto del template era silenciosamente válido

client: almacor venía pre-cargado: un instalador que editaba el password pero olvidaba el cliente levantaba un servicio emitiendo los tributos equivocados (ej.: un DINO facturando Impuesto Interno en vez de Percepción IVA). Lo confirmaron los dos jueces del Judgment Day.

Quedó: client: CHANGE_ME — el servicio se niega a arrancar hasta poner un cliente válido (doble fail-fast verificado: constructor + inyección condicional).

riesgo realLa cita del manual ARCA estaba sobrevendida

El template y la doc decían que los códigos de tributo 2/3/4 salían "del manual oficial" — pero el PDF no imprime el catálogo de tributos (solo evidencia los de IVA y el 99). Los valores eran correctos, la cita no. Encontrado por ambos jueces, de forma independiente.

Quedó: cada valor con su fuente honesta — y superado con creces: verificación en vivo contra el WS (Sección 03).

riesgo realrun.bat no funcionaba en el layout de instalación

Buscaba el jar solo en target\ (repo de desarrollo) — en una instalación real (jar descargado del Release, al lado del .bat) fallaba con "No jar found". Además: el copy del bootstrap no chequeaba errores y el echo hardcodeaba el puerto 8080.

Quedó: busca el jar primero al lado del script, después en target\; copy verificado; mensajes sin puerto fijo. Lógica cmd.exe revisada línea por línea por los jueces (sin trampas de expansión).

config faltanteMemoria sin tope: ~650 MB

Tu medición era real — pero no era un leak: sin -Xmx, la JVM dimensiona el heap según la RAM total del host. En máquinas grandes el proceso se infla sin necesitarlo.

Quedó: run.bat lanza con -Xms64m -Xmx256m. Medido: ~400 MB de proceso total (vs ~650). Sobra para los 6 clientes.

aclaradoCPU 70-80%: no era el servicio en régimen

Medido acá: en reposo el consumo de CPU es 0% (el tiempo de CPU acumulado quedó congelado entre muestras). Lo que viste fue: el arranque (~30-40 s de JIT, normal), o el cron de dev — que dispara cada 1 segundo (* * * ? * *, heredado de los 6 originales, solo con perfil dev) — o una corrida procesando backlog real.

Quedó: producción usa el cron del YML externo (cada 5 min). El footgun de dev quedó documentado en el troubleshooting de /doc. El cockpit muestra el cron activo y la próxima corrida.

faltantePuerto no configurable + gap del ente sin CUIT

(a) No había forma de cambiar el 8080 si estaba ocupado. (b) El ente viajaba solo anidado (enteFacturador:{id}) — el Manager lo descarta y resuelve por fallback (funciona con 1 comercio; con 2+ daría error 500). Gap heredado de los 6 originales, no introducido por la unificación.

Quedó: server.port en el YML (+ troubleshooting "Port already in use"); nueva propiedad opcional tifactura.cuit que viaja top-level (el Manager resuelve por CUIT, v2). Vacía = comportamiento legacy intacto, goldens sin cambios. Visible en /status.

confusión aclarada"ALMACOR usa PercIVA y el valor era 99"

Dos confusiones en una: (1) ALMACOR no emite Percepción de IVA — su columna fmontopercepcionci es percepción Comercio e Industria (nombre traicionero); (2) el "99" no era un valor raro: es el código ARCA "Otro", que es exactamente cómo viaja la Percepción de IVA (en DINO y CARACOL) — Id 99 + descripción, dentro de <ar:Tributos>, no en el bloque de IVA.

Quedó: documentado en /doc con el formato XML exacto. Configurar perciva en ALMACOR no hace nada (su estrategia no lo lee) — si un día lo necesitara, es cambio de estrategia + columnas, no config.

riesgo realEncontrado por el testing E2E de esta versión: el fail-fast dejaba un proceso zombie

Al arrancar con client: CHANGE_ME (o cualquier fallo de arranque), el contexto Spring moría pero el JVM quedaba vivo, con el puerto tomado, respondiendo 404 — el operador reintentaba y se comía "Port already in use". Lo encontró la batería E2E de esta revisión (los dos jueces del Judgment Day lo habían pasado por alto: verificaron que el contexto fallara, no que el proceso muriera).

Quedó: Application.main hace salida dura (System.exit(1)) ante cualquier fallo de arranque. Re-testeado: proceso muerto, puerto libre, cero javas vivos.

cosméticoMenores

Pill de versión vacía en el cockpit antes del primer fetch · comillas de id.iva sin anotar (sacarlas rompe el arranque) · inconsistencias internas del manual (Sección 06 vs 07 sobre run.bat). Todo corregido.

02 — El job

Scheduler: cómo quedó y cómo se cambia

Cron del notificador (notify.to.afip.cron.scheduler)
AmbienteValorDónde viveCómo cambiarlo
Producción0 0/5 * * * ?
(cada 5 min)
YML externo config\application-prod.ymlEditar el YML y reiniciar — sin recompilar
Desarrollo* * * ? * *
(cada 1 segundo ⚠)
application-dev.properties (dentro del jar, solo perfil dev)Editar esa línea; heredado de los 6 originales, solo para debug

03 — Lo que más te preocupaba

IDs de impuestos: verificados EN VIVO contra ARCA

Cuatro niveles de evidencia, todos coincidentes. El último es el definitivo: llamamos de verdad a FEParamGetTiposTributos y FEParamGetTiposIva (login WSAA con tu certificado de homologación, 2026-08-27).

Respuesta viva de ARCA · FEParamGetTiposTributos (homologación)
IdDescripciónVigente desde¿Lo usás?
1Impuestos nacionales2010
2Impuestos provinciales2010✓ ing.bruto (IIBB)
3Impuestos municipales2010✓ comind
4Impuestos Internos2010✓ impinterno
5IIBB (específico)2017alternativa a 2 — cambio deliberado
6Percepción de IVA (específico)2017alternativa a 99 — cambio deliberado
7-9, 13Percepciones varias2017
99Otro (+ Desc "PERCEPCION IVA…")2010✓ perciva
Dato que solo la llamada viva reveló: el código 10 ("Impuesto interno a nivel item") y los 14-19 NO existen en el catálogo de WSFE — el Excel oficial es la unión de varios sistemas. Tu impinterno=4 es el único correcto para este flujo. Y los IVA: 3=0%, 4=10.5%, 5=21% (+ 6=27%, 8=5%, 9=2.5%) — exactos a tu config.

04 — Cómo quedó

El YAML final (esencia)

Un archivo por instalación, fuera del jar. Lo nuevo de este ciclo, marcado:

# config\application-prod.yml — al lado del jar (lo bootstrapea run.bat)
tifactura:
  client: CHANGE_ME        # NUEVO: no arranca hasta poner un cliente válido (fail-fast)
  cuit: ""                # NUEVO: CUIT top-level p/ resolución v2 en el Manager (opcional)

server:
  port: 8080              # NUEVO: cambiable si el puerto está ocupado

spring:
  datasource:             # TipreRetail (dino/almacor/becerra/tiziano) o TipreSucursales (caracol/cyre)
    url: jdbc:sqlserver://localhost:1433;databaseName=TipreRetail
    username: sa
    password: CHANGE_ME

notify.to.afip.cron.scheduler: "0 0/5 * * * ?"   # el job, cada 5 min

id:                       # NUEVO: códigos oficiales + tabla ARCA completa comentada
  ente.facturador: 1      # id interno del Manager (NO es código ARCA)
  tributo:
    ing.bruto: 2          # Impuestos provinciales — VERIFICADO EN VIVO
    comind: 3             # Impuestos municipales — VERIFICADO EN VIVO
    impinterno: 4         # Impuestos Internos — VERIFICADO EN VIVO (no usar 10)
    perciva: 99           # "Otro" + Desc — así viaja la Percepción de IVA
  iva: { "0": 3, "105": 4, "21": 5 }  # comillas obligatorias (anotado)

El template real (config-template\application-prod.yml) trae además la tabla oficial completa, las fuentes de cada valor y el sello de verificación en vivo.

05 — El sello

Cómo se garantizó todo esto