Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions l10n_ar_tax/models/account_move.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,3 +65,10 @@ def copy(self, default=None):
recs = super().copy(default=default)
recs._l10n_ar_recompute_fiscal_position_taxes()
return recs

def _prepare_product_base_line_for_taxes_computation(self, product_line):
""" Si es factura de venta argentina mandamos l10n_ar_perception_invoice por contexto. Dicho contexto se usa en l10n_ar_tax/models/account_tax.py por el método _get_tax_details para el cálculo de percepciones de venta teniendo en cuenta el monto mínimo no imponible establecido en el impuesto. """
Copy link

Copilot AI Feb 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

El comentario del docstring es excesivamente largo para una sola línea (más de 200 caracteres). Esto dificulta la legibilidad del código.

Recomendación: Reformatear el docstring para usar múltiples líneas, por ejemplo:

\"\"\"
Si es factura de venta argentina mandamos l10n_ar_perception_invoice por contexto.
Dicho contexto se usa en l10n_ar_tax/models/account_tax.py por el método _get_tax_details 
para el cálculo de percepciones de venta teniendo en cuenta el monto mínimo no imponible 
establecido en el impuesto.
\"\"\"
Suggested change
""" Si es factura de venta argentina mandamos l10n_ar_perception_invoice por contexto. Dicho contexto se usa en l10n_ar_tax/models/account_tax.py por el método _get_tax_details para el cálculo de percepciones de venta teniendo en cuenta el monto mínimo no imponible establecido en el impuesto. """
"""
Si es factura de venta argentina mandamos l10n_ar_perception_invoice por contexto.
Dicho contexto se usa en l10n_ar_tax/models/account_tax.py por el método _get_tax_details
para el cálculo de percepciones de venta teniendo en cuenta el monto mínimo no imponible
establecido en el impuesto.
"""

Copilot uses AI. Check for mistakes.
base_line = super()._prepare_product_base_line_for_taxes_computation(product_line)
if self.move_type in ("out_invoice", "out_refund") and self.company_id.country_id.code == "AR":
base_line["tax_ids"] = base_line["tax_ids"].with_context(l10n_ar_perception_invoice=True)
return base_line
57 changes: 57 additions & 0 deletions l10n_ar_tax/models/account_tax.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,3 +100,60 @@ def _check_tax_used_on_company_tax_fp(self):
raise UserError(
"Error se esta usando en ws de estas cias %s" % ws.mapped("fiscal_position_id.company_id.name")
)


def _get_tax_details(
self,
price_unit,
quantity,
precision_rounding=0.01,
rounding_method="round_per_line",
product=None,
product_uom=None,
special_mode=False,
manual_tax_amounts=None,
filter_tax_function=None,
):
""" Hacer cálculo de percepciones de venta teniendo en cuenta el monto mínimo no imponible establecido en el impuesto."""
Copy link

Copilot AI Feb 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

El comentario del docstring solo menciona "percepciones de venta" pero el método también se aplica a notas de crédito (out_refund) según se puede ver en account_move.py línea 72. Esto podría generar confusión sobre cuándo se aplica la lógica del monto mínimo no imponible.

Recomendación: Actualizar el docstring para mencionar explícitamente que aplica tanto a facturas de venta como notas de crédito de venta argentinas, por ejemplo: "Hacer cálculo de percepciones de venta (facturas y notas de crédito) teniendo en cuenta el monto mínimo no imponible establecido en el impuesto."

Suggested change
""" Hacer cálculo de percepciones de venta teniendo en cuenta el monto mínimo no imponible establecido en el impuesto."""
""" Hacer cálculo de percepciones de venta (facturas y notas de crédito) teniendo en cuenta el monto mínimo no imponible establecido en el impuesto."""

Copilot uses AI. Check for mistakes.
res = super()._get_tax_details(
price_unit,
quantity,
precision_rounding=precision_rounding,
rounding_method=rounding_method,
product=product,
product_uom=product_uom,
special_mode=special_mode,
manual_tax_amounts=manual_tax_amounts,
filter_tax_function=filter_tax_function,
)
if not self._context.get("l10n_ar_perception_invoice"):
return res

if not res.get("taxes_data"):
return res

updated = False
for tax_data in res["taxes_data"]:
tax = tax_data["tax"]
if tax.country_code == "AR" and tax.type_tax_use == "sale" and tax.l10n_ar_non_taxable_amount:
base_amount = tax_data["base_amount"]
net_base = max(0, base_amount - tax.l10n_ar_non_taxable_amount)
if not net_base:
if tax_data["tax_amount"] or tax_data["base_amount"]:
tax_data["tax_amount"] = 0.0
tax_data["base_amount"] = 0.0
updated = True
Comment on lines +142 to +145
Copy link

Copilot AI Feb 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

La condición if not net_base y el bloque anidado if tax_data["tax_amount"] or tax_data["base_amount"] son redundantes. Cuando net_base es 0, siempre se debería actualizar tax_amount y base_amount a 0.0. La condición interna no agrega valor lógico porque:

  1. Si ambos valores ya son 0.0, asignarlos nuevamente a 0.0 es una operación sin efectos secundarios
  2. Si alguno no es 0.0, necesariamente deben ponerse en 0.0

Recomendación: Simplificar eliminando la condición interna y siempre ejecutar la actualización cuando net_base sea 0.

Suggested change
if tax_data["tax_amount"] or tax_data["base_amount"]:
tax_data["tax_amount"] = 0.0
tax_data["base_amount"] = 0.0
updated = True
tax_data["tax_amount"] = 0.0
tax_data["base_amount"] = 0.0
updated = True

Copilot uses AI. Check for mistakes.
else:
if base_amount:
tax_data["tax_amount"] = tax_data["tax_amount"] * (net_base / base_amount)
Copy link

Copilot AI Feb 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

El cálculo proporcional del tax_amount en la línea 148 podría generar problemas de precisión o redondeo. El método recibe un parámetro precision_rounding=0.01 que no se está utilizando al recalcular el monto del impuesto.

Cuando se calcula tax_data["tax_amount"] = tax_data["tax_amount"] * (net_base / base_amount), el resultado de la multiplicación podría tener más decimales de los esperados para una moneda. Esto podría causar diferencias de centavos en los totales.

Recomendación: Aplicar redondeo al resultado usando precision_rounding o la moneda correspondiente, por ejemplo usando float_round de odoo.tools o el método de redondeo de la moneda si está disponible en el contexto.

Copilot uses AI. Check for mistakes.
else:
tax_data["tax_amount"] = 0.0
Comment on lines +147 to +150
Copy link

Copilot AI Feb 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

El recálculo del tax_amount en la línea 148 usa división pero no verifica si base_amount podría ser exactamente 0. Aunque hay una condición if base_amount que previene la división por cero, la estructura del código podría ser más clara.

Sin embargo, la lógica tiene una inconsistencia: cuando base_amount es 0 pero net_base no es 0 (lo cual es matemáticamente imposible dado que net_base = max(0, base_amount - l10n_ar_non_taxable_amount)), se establece tax_amount = 0.0 en línea 150. Esta rama del else nunca debería ejecutarse porque si base_amount = 0, entonces net_base también será 0 (ya procesado en el bloque if not net_base).

Recomendación: Simplificar la lógica eliminando la verificación redundante en línea 147, ya que si llegamos al bloque else (línea 146), sabemos que net_base > 0, lo que implica que base_amount > l10n_ar_non_taxable_amount, por lo tanto base_amount > 0 y no hay riesgo de división por cero.

Suggested change
if base_amount:
tax_data["tax_amount"] = tax_data["tax_amount"] * (net_base / base_amount)
else:
tax_data["tax_amount"] = 0.0
tax_data["tax_amount"] = tax_data["tax_amount"] * (net_base / base_amount)

Copilot uses AI. Check for mistakes.
tax_data["base_amount"] = net_base
updated = True

if updated:
res["total_included"] = res["total_excluded"] + sum(
tax_line["tax_amount"] for tax_line in res["taxes_data"]
)

return res
Comment on lines +105 to +159
Copy link

Copilot AI Feb 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

La nueva funcionalidad de cálculo de percepciones con monto mínimo no imponible carece de tests automatizados. Esto es importante porque:

  1. El módulo ya tiene tests en la carpeta tests/
  2. La lógica introduce cálculos matemáticos no triviales (resta de monto no imponible, recálculo proporcional del tax_amount)
  3. Hay múltiples ramas condicionales que deberían validarse

Recomendación: Agregar tests que verifiquen:

  • Caso a) Base > mínimo no imponible: verificar que el monto a percibir se calcula sobre (base - mínimo_no_imponible)
  • Caso b) Base < mínimo no imponible: verificar que el monto a percibir es 0
  • Caso c) Validar que funciona tanto para facturas como notas de crédito de venta

Copilot uses AI. Check for mistakes.
4 changes: 4 additions & 0 deletions l10n_ar_tax/views/account_tax_view.xml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@
<field name="l10n_ar_code" position="attributes">
<attribute name="invisible">country_code != 'AR'</attribute>
</field>
<!-- Necesitamos que sea visible en percepciones porque Santa Fe requiere el mínimo no imponible -->
<field name="l10n_ar_non_taxable_amount" position="attributes">
<attribute name="invisible">country_code != 'AR' or l10n_ar_type_tax_use not in ['sale', 'supplier'] or amount_type != 'percent'</attribute>
</field>
</field>
</record>

Expand Down
Loading