Núcleo impositivo¶
El núcleo impositivo (NucleoImpositivoDto, en el kernel shared) es la pieza más transversal del sistema. Cada importe que viaja por el ticket —el precio de una línea, un descuento de promo, un crédito de envase, el total— no es un BigDecimal suelto: es un núcleo impositivo, es decir, un monto con su descomposición fiscal adentro. Entender esto es entender cómo el sistema mantiene el IVA y los impuestos cuadrados a lo largo de todo el cómputo.
Por qué un objeto y no un número
Si un descuento fuera solo "−\$100", al aplicarlo perderías sobre qué neto y qué IVA impactó. El núcleo impositivo lleva el monto y sus componentes (neto gravado, IVA, impuestos internos…), de modo que sumar, restar o prorratear importes preserva la composición fiscal. Esto es lo que permite emitir un comprobante con el IVA discriminado correcto al final.
Estructura¶
shared/NucleoImpositivoDto.java:
public class NucleoImpositivoDto {
private BigDecimal monto; // Total final (neto + impuestos)
private BigDecimal neto; // Base gravable
private List<NucleoComponenteDto> componentes; // Desglose por tipo
public static final int SCALE = 8; // precisión interna de cálculo
public static final int DISPLAY_SCALE = 2; // escala de moneda (mostrar)
}
Cada componente:
public static class NucleoComponenteDto {
private NucleoComponenteTipo tipo;
private BigDecimal base; // importe base sobre el que se calcula
private BigDecimal alicuota; // % (ej. 21.00, 10.5)
private BigDecimal monto; // base × alícuota / 100
}
Tipos de componente¶
NucleoComponenteTipo modela el sistema impositivo argentino de retail:
| Grupo | Componentes | Qué es |
|---|---|---|
| Neto (base imponible) | NETO_IVA_21, NETO_IVA_10_5, NETO_IVAEXENTO |
La mercadería antes de impuestos, separada por la alícuota de IVA que le corresponde. |
| IVA | IVA_21, IVA_10_5 |
El IVA calculado sobre cada neto. |
| Impuestos internos | IMPINTERNO |
Impuesto interno (tabaco, bebidas, etc.), del campo fImpinterno del artículo. |
| Percepciones | PERCEPCION_IIBB, PERCEPCION_COMIND, PERCEPCION_IVA_21, PERCEPCION_IVA_10_5 |
Percepciones impositivas (Ingresos Brutos, etc.). |
| Retenciones | RETENCION_GANANCIAS, RETENCION_IVA |
Retenciones. |
Qué está implementado y qué está modelado
Los componentes de neto, IVA e impuestos internos se computan y viajan en cada ticket. Los de percepción y retención existen en el enum (el modelo los contempla) pero, según el código actual, CaeService todavía no arma tributos de percepción en la solicitud a AFIP. Documentamos el modelo completo y marcamos lo que aún no se ejercita.
Aritmética que preserva la composición¶
NucleoImpositivoDto ofrece operaciones que operan sobre el monto y propagan a los componentes:
| Operación | Qué hace |
|---|---|
add(otro) |
Suma montos y componentes, normaliza. |
subtract(otro) |
Resta (ej. aplicar un descuento), normaliza. |
multiply(factor) / divide(factor) |
Escala (ej. precio × cantidad). |
recomponer(nuevoMonto) |
Recalcula los componentes para un monto nuevo, manteniendo las proporciones (ej. fijar un precio de promo y que el IVA se reparta solo). |
normalized() |
Recompone monto/neto desde los componentes. |
zero() / isZero() |
Cero impositivo. |
recomponer(): el método clave de las promos¶
Cuando una promo fija un precio nuevo (un combo a \$1.000, un precio mayorista), no se puede simplemente cambiar el monto: hay que recalcular el neto y el IVA para que sigan cuadrando. recomponer(nuevoMonto) hace eso, prorrateando el nuevo monto entre los componentes según su peso original.
recomponer() y la base cero
Si el monto base es cero, recomponer() dividiría por cero (ArithmeticException). El fix TSPM-016 lo evita: un ítem con base cero no recibe descuento (su porción es cero). Si tocás aritmética de núcleo, respetá ese guard.
Normalización: el fix TSTK-025¶
Cuando un NucleoImpositivoDto se deserializa desde JSON, a veces monto/neto llegan en cero aunque los componentes tengan valores. normalized() recompone los totales desde los componentes para no perder precisión en las sumas/restas posteriores (fix TSTK-025). Por eso casi todas las operaciones llaman a normalized() al final.
Escala y redondeo¶
- Cálculo interno:
SCALE = 8decimales — se calcula con holgura para no acumular error. - Moneda:
DISPLAY_SCALE = 2,HALF_UP— se redondea al mostrar/persistir el importe final. - El módulo
promosusa la misma escala (MONEY_SCALE+ 8 para intermedios) justamente para ser consistente con el núcleo.
Regla práctica
Trabajá siempre con NucleoImpositivoDto, no con su monto pelado. Si extraés el BigDecimal y operás aparte, perdés la composición fiscal y vas a descuadrar el IVA del comprobante. La aritmética del dinero vive acá a propósito.
Dónde se usa¶
- En cada línea del ticket (paso 3 del cómputo).
- En cada beneficio de promoción (el descuento es un núcleo, no un número).
- En el crédito de envases (envases y vales).
- En el comprobante fiscal, donde el desglose por alícuota se vuelca al CAE/CAEA y al QR de AFIP (facturación fiscal).