Una situación común en el ámbito contable y de recursos humanos, es la necesidad de determinar el salario bruto a partir del salario neto con la finalidad de prever la carga fiscal de una futura contratación. Actualmente, existe paquetería contable que es capaz de realizar tal tarea, sin embargo, en el caso del desarrollo de herramientas de cálculo propias, la información acerca del procedimiento es escaso. Cabe señalar que el siguiente método se basa en cálculo de impuestos de México, aunque podría funcionar para otros paises con sus respectivas adecuaciones.
Planteamiento del problema
Inicialmente, es necesario conocer el proceso mediante el cual se calcula el salario neto a partir del bruto, donde para fines prácticos se asume lo siguiente:
donde , , e son el salario neto, salario bruto, retención de ISR y cuotas IMSS, respectivamente. En ese sentido, el cálculo de puede determinarse como:
Sin embargo, la ecuación anterior no puede calcularse de forma directa, ya que e están en función del y de los intervalos de valores discretos de las Tablas de Retenciones. Por tanto, se buscará el valor de que minimice su diferencia con el salario neto más impuestos. Es así que para realizar dicho cálculo, se plantea el siguiente problema de optimización:
donde la función objetivo es:
traduciendo lo anterior a código de python, se tiene:
def J(salario_bruto, salario_neto, dias_periodo, sbc, factor_isr, limite_inferior_isr, cuota_fija_isr, subsidio, cuota_adicional, factores):
isr = (salario_bruto - limite_inferior_isr)*factor_isr + cuota_fija - subsidio
imss = sbc*dias_periodo*factores + cuota_adicional
return abs(salario_neto + isr + imss - salario_bruto)
En las siguientes secciones se explican los argumentos de .
Cálculo de ISR
El cálculo del monto relacionado con la retención de ISR, se fundamenta en las Tablas de Retenciones para el año fiscal y periodo de interés (mensual, quincenal, etc), donde el valor de debe ubicarse dentro de un límite inferior y un límite superior de las tablas antes mencionadas.
siendo el porcentaje aplicable sobre el excedente del límite inferior, el monto de cuota fija y el subsidio al empleo. Para nuestro caso de análisis, se elige un periodo de nómina mensual. Por tanto, se plantea la siguiente función para obtener los valores involucrados en el cálculo de ISR:
def valores_isr(salario_bruto):
data = dict()
limites_inferiores = [0.01, 644.59, 5470.93, 9614.67, 11176.63, 13381.48, 26988.51, 42537.59, 81211.26, 108281.68, 324845.02]
limites_superiores = [644.58, 5470.92, 9614.66, 11176.62, 13381.47, 26988.50, 42537.58, 81211.25, 108281.67, 324845.01, 999999.00]
cuotas_fijas = [0.00, 12.38, 321.26, 772.10, 1022.01, 1417.12, 4323.58, 7980.73, 19582.83, 28245.36, 101876.90]
factores = [1.92, 6.40, 10.88, 16.00, 17.92, 21.36, 23.52, 30.00, 32.00, 34.00, 35.00]
for i, inferior in enumerate(limites_inferiores):
superior = limites_superiores[i]
if salario_bruto >= inferior and salario_bruto <= superior:
data['limite_inferior_isr'] = limites_inferiores[i]
data['limite_superior_isr'] = limites_superiores[-1]
data['cuota_fija_isr'] = cuotas_fijas[i]
data['factor_isr'] = factores[i]/100
break
else:
pass
return data
donde los valores limites_inferiores
, limites_superiores
, cuotas_fijas
y factores
se obtienen de las Tablas de Retenciones para el año fiscal 2021. Así mismo, se requiere identificar si para el valor de es aplicable algún monto de subsidio al empleo. Para este fin, se propone la siguiente función:
def valores_subsidio(salario_bruto):
data = dict()
limites_inferiores_subsidio = [0.01, 1768.97, 2653.39, 3472.85, 3537.88, 4446.16, 4717.19, 5335.43, 6224.68, 7113.91, 7382.34]
limites_superiores_subsidio = [1768.96, 2653.38, 3472.84, 3537.87, 4446.15, 4717.18, 5335.42, 6224.67, 7113.90, 7382.33, 999999]
montos_subsidio = [407.02, 406.83, 406.62, 392.77, 382.46, 354.23, 324.87, 294.63, 253.54, 217.61, 0]
for i, inferior in enumerate(limites_inferiores_subsidio):
superior = limites_superiores_subsidio[i]
if salario_bruto >= inferior and salario_bruto <= superior:
data['limite_inferior_subsidio'] = limites_inferiores_subsidio[i]
data['limite_superior_subsidio'] = limites_superiores_subsidio[-1]
data['subsidio'] = montos_subsidio[i]
break
else:
pass
return data
Cálculo IMSS
Para el problema planteado, el cálculo de cuotas IMSS depende de los factores aplicables al trabajador, actualizados al año fiscal 2021:
donde es el Salario Base de Cotización, son los días del periodo de nómina, es la sumatoria de los factores IMSS aplicados al trabajador por los conceptos de gastos médicos, prestaciones en dinero, CEAV e invalidez y vida; finalmente, es el monto de la cuota adicional. Para el cálculo del se usa la función:
def valor_sbc(salario_bruto, dias_periodo, uma, salario_minimo):
dias_vacaciones = 6
bono_navidad = 15
bono_vacaciones = 0.25
dias_cotizados = 365
salario_diario = float(salario_bruto/dias_periodo)
if salario_diario <= salario_minimo:
return salario_minimo
else:
factor_integracion = (1/dias_cotizados)*(dias_cotizados + (dias_vacaciones*bono_vacaciones) + bono_navidad)
valor_sbc = salario_diario * factor_integracion
if valor_sbc > 25*uma:
return 25*uma
else:
return valor_sbc
Es importante señalar que el cálculo considera que el valor del no puede ser mayor que 25 veces la Unidad de Medida y Actualización (UMA), tal como se estipula en la ley del IMSS. En lo que respecta al cálculo de la cuota adicional se realiza como sigue:
def valor_cuota_adicional(salario_bruto, dias_periodo, salario_minimo, uma, sbc, factor_cuota_adicional):
if salario_bruto >= 3*uma*dias_periodo:
return ((sbc - 3*uma)*factor_cuota_adicional)*dias_periodo
else:
return 0
así, los factores de las cuotas IMSS son los siguientes:
factor_cuota_adicional = 0.004
factor_gastos_medicos = 0.00375
factor_dinero = 0.0025
factor_invalidez_vida = 0.00625
factor_ceav = 0.01125
factores = factor_gastos_medicos + factor_dinero + factor_invalidez_vida + factor_ceav
Implementación de algoritmo de optimización
Considerándose que el problema de optimización es de una variable, no tiene restricciones y la función objetivo implica un coste computacional bajo, se implementa un método basado en eliminación de regiones, concretamente una versión del Método de División de Intervalos por la Mitad, el cual es un algoritmo que no requiere que sea diferenciable. Más información aquí.
Los valores del salario mínimo y UMA se establecen de acuerdo al año fiscal 2021. Así mismo, se considera un periodo mensual de 30 días. Adicionalmente, se propone una lista de salarios para evaluar los resultados del algoritmo.
uma = 89.62
salario_minimo = 141.70
dias_periodo = 30
salarios_prueba = [
4390,
7000,
8500,
9500,
12500,
16500,
20000,
80000,
]
Con respecto a los parámetros del algoritmo, se establece un número máximo de 100 iteraciones por cálculo, así como una constante que fungirá como segundo criterio de paro al limitar la convergencia de .
iteraciones = 100
eps = 0.001
Realizado lo anterior, se procede a la implementación del algoritmo:
for salario_neto in salarios_prueba:
#Inicialización de vectores
a = []
b = []
L = []
fb = []
fa = []
alp = []
lam = []
mu = []
f_alp = []
f_lam = []
f_mu = []
#Se calculan los valores iniciales
isr_data = valores_isr(salario_neto)
subsidio_data = valores_subsidio(salario_neto)
sbc = valor_sbc(salario_neto, dias_periodo, uma, salario_minimo)
cuota_adicional = valor_cuota_adicional(salario_neto, dias_periodo, salario_minimo, uma, sbc, factor_cuota_adicional)
#Se establecen los valores para la iteración inicial
a.append(isr_data['limite_inferior_isr'])
b.append(isr_data['limite_superior_isr'])
L.append(b[-1] - a[-1])
alp.append((a[-1] + b[-1])/2)
lam.append(0)
mu.append(0)
f_alp.append(0)
f_lam.append(0)
f_mu.append(0)
k=0
f_alp[k] = J(alp[k], salario_neto, dias_periodo, sbc, isr_data['factor_isr'], isr_data['limite_inferior_isr'], isr_data['cuota_fija_isr'], subsidio_data['subsidio'], cuota_adicional, factores)
while k <= iteraciones:
lam[k] = a[k] + L[k]/4
mu[k] = b[k] - L[k]/4
f_alp[k] = J(alp[k], salario_neto, dias_periodo, sbc, isr_data['factor_isr'], isr_data['limite_inferior_isr'], isr_data['cuota_fija_isr'], subsidio_data['subsidio'], cuota_adicional, factores)
f_lam[k] = J(lam[k], salario_neto, dias_periodo, sbc, isr_data['factor_isr'], isr_data['limite_inferior_isr'], isr_data['cuota_fija_isr'], subsidio_data['subsidio'], cuota_adicional, factores)
f_mu[k] = J(mu[k], salario_neto, dias_periodo, sbc, isr_data['factor_isr'], isr_data['limite_inferior_isr'], isr_data['cuota_fija_isr'], subsidio_data['subsidio'], cuota_adicional, factores)
if f_lam[k] < f_alp[k]:
b.append(alp[k])
alp.append(lam[k])
a.append(a[k])
elif f_mu[k] < f_alp[k]:
a.append(alp[k])
alp.append(mu[k])
b.append(b[k])
else:
a.append(lam[k])
b.append(mu[k])
alp.append(alp[k])
lam.append(0)
mu.append(0)
f_alp.append(0)
f_lam.append(0)
f_mu.append(0)
L.append(b[k+1] - a[k+1])
isr_data = valores_isr(alp[k+1])
subsidio_data = valores_subsidio(alp[k+1])
sbc = valor_sbc(alp[k+1], dias_periodo, uma, salario_minimo)
cuota_adicional = valor_cuota_adicional(alp[k+1], dias_periodo, salario_minimo, uma, sbc, factor_cuota_adicional)
#Se evalua criterio de paro eps
if f_alp[k] < eps:
print(f_alp[k], k)
break
k = k + 1
#Se muestra el valor óptimo para el salario neto ingresado
#El salario bruto calculado es alp[-1]
#El número de iteraciones requeridas para la convergencia de J es k
#El valor final de J es f_alp[-1]
print(f"Óptimo: {alp[-1]} k: {k} f_alp: {f_alp[-1]}")
El procedimiento anterior es perfectible. Personalmente implementé esta solución en una plataforma web (Django) para cálculos actuariales y funciona bastante bien. El repositorio con el código puedes encontrarlo aquí.