Skip to content

Retenciones de Ganancias con Acumulados del Período

Concepto

Las retenciones de ganancias deben considerar los acumulados del período para determinar:

  1. Si se alcanzó el mínimo no imponible
  2. Sobre qué monto se debe retener (excedente)

⚠️ IMPORTANTE: Este documento describe el cálculo general. El método específico depende de la inscripción del proveedor:

  • Inscriptos (insgana='S'): Pueden usar escalas progresivas (ver RG_5423_Codigo_119_Calculo_Retenciones.md)
  • No inscriptos (insgana='N'): SIEMPRE usan porcentaje fijo de 28%, nunca escalas

Reglas de Negocio

RN-007: Validación contra Acumulado Total

La validación del mínimo no imponible NO se hace sobre el neto del comprobante actual, sino sobre el total acumulado en el período:

total_acumulado = acumulado_anterior + neto_comprobante_actual

Si total_acumulado < mínimo_no_imponible: NO se aplica retención

Si total_acumulado >= mínimo_no_imponible: Se aplica retención sobre el excedente

RN-008: Retención sobre el Excedente

La retención SIEMPRE se aplica sobre el excedente del mínimo, NO sobre el neto completo:

excedente = total_acumulado - mínimo_no_imponible
monto_imponible = min(excedente, monto_aplicado_en_este_pago)
retención = monto_imponible * porcentaje

Ejemplos

Ejemplo 1: Sin Acumulado Anterior

Contexto:

  • Acumulado anterior: $0
  • Mínimo no imponible: $1,200
  • Neto del comprobante: $300
  • Monto del pago: $300

Cálculo:

total_acumulado = 0 + 300 = 300
¿300 >= 1200? NO

Resultado: ❌ No se retiene (no alcanza el mínimo)


Ejemplo 2: Con Acumulado que Supera el Mínimo

Contexto:

  • Acumulado anterior: $1,000
  • Mínimo no imponible: $1,200
  • Neto del comprobante: $300
  • Monto del pago: $300
  • Porcentaje: 10%

Cálculo:

total_acumulado = 1000 + 300 = 1300
¿1300 >= 1200? SÍ

excedente = 1300 - 1200 = 100
monto_imponible = min(100, 300) = 100

retención = 100 * 10% = $10

Resultado: ✅ Se retiene $10 (sobre los $100 de excedente, NO sobre los $300 de neto)


Ejemplo 3: Acumulado que Ya Superó el Mínimo

Contexto:

  • Acumulado anterior: $1,500
  • Mínimo no imponible: $1,200
  • Neto del comprobante: $500
  • Monto del pago: $500
  • Porcentaje: 10%

Cálculo:

total_acumulado = 1500 + 500 = 2000
¿2000 >= 1200? SÍ

excedente = 2000 - 1200 = 800
monto_imponible = min(800, 500) = 500

retención = 500 * 10% = $50

Resultado: ✅ Se retiene $50 (como el acumulado ya superó el mínimo, se retiene sobre todo el neto actual)


Ejemplo 4: Pago Parcial del Comprobante

Contexto:

  • Acumulado anterior: $1,000
  • Mínimo no imponible: $1,200
  • Neto del comprobante: $600
  • Monto del pago: $300 (pago parcial)
  • Porcentaje: 10%

Cálculo:

total_acumulado = 1000 + 600 = 1600
¿1600 >= 1200? SÍ

excedente = 1600 - 1200 = 400
monto_imponible = min(400, 300) = 300

retención = 300 * 10% = $30

Resultado: ✅ Se retiene $30 (el excedente es 400, pero solo se pagan 300 en este momento)


Diferencias entre Inscriptos y No Inscriptos

Proveedores INSCRIPTOS (insgana='S')

Pueden usar escalas progresivas según RG AFIP 5423/2023:

Si tiene escalas configuradas:
  → Buscar escala donde: desde <= total_acumulado <= hasta
  → Aplicar: retención = fijo + (porcentaje × (acumulado - excedente))

Si NO tiene escalas:
  → Aplicar porcentaje simple sobre excedente

Ejemplo con escalas RG 5423:

Tabla de escalas (Código 119):

Más de $A $Monto Fijo $%S/exc. de $
426,000568,00056,09023%426,000
568,000852,00088,75027%568,000
Acumulado anterior: $700,000
Neto comprobante: $100,000
Total acumulado: $800,000

1. Identificar escala: $800,000 está entre $568,000 y $852,000

2. Obtener datos del tramo:
   - Monto fijo: $88,750 (retención acumulada hasta $568,000)
   - Porcentaje: 27%
   - S/exc. de $: $568,000

3. Calcular excedente del tramo:
   Excedente = $800,000 - $568,000 = $232,000

4. Calcular retención:
   Retención = $88,750 + ($232,000 × 27%)
             = $88,750 + $62,640
             = $151,390

✅ El monto fijo $88,750 NO se recalcula, es la suma de retenciones
   de todos los tramos anteriores (desde $0 hasta $568,000)

Proveedores NO INSCRIPTOS (insgana='N')

⚠️ SIEMPRE usan porcentaje fijo de 28%, sin considerar escalas:

Si total_acumulado >= mínimo:
  excedente = total_acumulado - mínimo
  retención = excedente × 28%

Las escalas se IGNORAN aunque estén configuradas

Ejemplo NO inscripto:

Acumulado anterior: $1,500,000
Monto mínimo: $450,000
Neto comprobante: $300,000
Total acumulado: $1,800,000

Excedente = $1,800,000 - $450,000 = $1,350,000
Retención = $1,350,000 × 28% = $378,000

⚠️ No se usan escalas, solo porcentaje fijo

Comparación:

AspectoInscriptoNo Inscripto
Método de cálculoEscalas progresivas o % simpleSiempre % fijo (28%)
Usa escalasSí, si están configuradasNO, nunca
PorcentajeVariable según escalaFijo: 28%
ComplejidadMayor (rangos, fijos, %)Menor (solo excedente × 28%)

Impacto en el Sistema

Base de Datos

Tabla: acuganancias

sql
CREATE TABLE acuganancias (
    id SERIAL PRIMARY KEY,
    codpro INT NOT NULL,          -- Código del proveedor
    mes INT NOT NULL,              -- Mes (1-12)
    ano INT NOT NULL,              -- Año (ej: 2024)
    codgan INT NOT NULL,           -- Código del concepto de ganancia
    monacu DECIMAL(15,2) NOT NULL, -- Monto acumulado (base para validar mínimo)
    monret DECIMAL(15,2),          -- Monto retenido acumulado
    UNIQUE(codpro, mes, ano, codgan)
);

Flujo de Actualización

Cuando se registra una orden de pago con retenciones:

  1. Consultar acumulados del período actual
  2. Calcular retenciones considerando acumulados
  3. Actualizar acumulados sumando:
    • monacu += neto_del_comprobante
    • monret += retención_calculada

Servicios Involucrados

  • AcumuladoGananciaService: Gestiona los acumulados del período
  • RetencionGananciaPagoService: Calcula retenciones considerando acumulados
  • CalculadorRetenciones (Domain): Lógica pura de cálculo

Código de Ejemplo

php
// En el servicio
$acumulados = $this->acumuladoGananciaService->getByProveedor(
    $idProveedor,
    $mesActual,
    $anoActual
);

// En el calculador de dominio
$acumuladoAnterior = $this->acumuladosPorConcepto[$idConcepto] ?? 0.0;
$totalAcumulado = $acumuladoAnterior + $concepto->neto;

if ($totalAcumulado < $configuracion->monto) {
    // No retener: no alcanza el mínimo
}

$excedente = $totalAcumulado - $configuracion->monto;
$montoImponible = min($excedente, $montoAplicado);
$retencion = $montoImponible * ($porcentaje / 100);

Casos Edge

Caso 1: Sin Acumulado Registrado

Si no existe registro de acumulado para el concepto → acumulado = 0

Caso 2: Primer Comprobante del Período

Si es el primer comprobante → acumulado = 0 → validar neto_actual >= mínimo

Caso 3: Cambio de Período

Al cambiar de mes/año, los acumulados se reinician automáticamente (nueva consulta retorna vacío)

Caso 4: Múltiples Conceptos

Cada concepto tiene su propio acumulado independiente

Testing

Ver: Tests/Unit/CtaCte/RetencionGananciaPagoServiceTest.php

Tests específicos para acumulados:

  • testConAcumuladoBajoMinimo - No retiene si acumulado + neto < mínimo
  • testConAcumuladoQueSupera - Retiene sobre excedente
  • testConAcumuladoYaSuperado - Retiene sobre neto completo