Skip to content

Commit fc9382e

Browse files
committed
[ADD] l10n_ar_tax: added test cases
1 parent 3868a31 commit fc9382e

2 files changed

Lines changed: 241 additions & 0 deletions

File tree

l10n_ar_tax/tests/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from . import test_arba
2+
from . import test_map_tax_fiscal_position
23
from . import test_withholding_thresholds
34
from . import test_payment_withholding_multimoneda
45
from . import test_payment_withholding_checks_multimoneda
Lines changed: 240 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,240 @@
1+
"""
2+
Tests para map_tax en posiciones fiscales argentinas (l10n_ar_tax).
3+
Escenarios cubiertos:
4+
1. Pos. Fiscal sólo percepción con domestic FP con sustitución → IVA 21% no se reemplaza por IVA 0%
5+
2. Pos. Fiscal con tax_ids explícitos → map_tax aplica la sustitución correctamente (super)
6+
3. Factura de cliente con Pos. Fiscal con tax_ids explícitos → _get_computed_taxes() aplica IVA 0% reemplazando IVA 21%
7+
4. Factura de cliente con Pos. Fiscal sólo percepción → _get_computed_taxes() conserva IVA 21% sin sustituirlo
8+
5. Pago de factura de cliente con Pos. Fiscal sólo percepción → monto del pago refleja IVA 21% (1210), no IVA 0% (1000)
9+
"""
10+
11+
from odoo import Command
12+
from odoo.addons.l10n_ar.tests.common import TestArCommon
13+
from odoo.tests import tagged
14+
15+
16+
@tagged("-at_install", "post_install")
17+
class TestMapTaxFiscalPosition(TestArCommon):
18+
"""Tests de map_tax para posiciones fiscales de percepción/retención."""
19+
20+
@classmethod
21+
def setUpClass(cls):
22+
super().setUpClass()
23+
24+
# Impuesto de percepción IIBB CABA para usar en l10n_ar_tax_ids
25+
cls.caba_perception_tax = cls.env.ref("account.%i_ri_tax_percepcion_iibb_caba_aplicada" % cls.env.company.id)
26+
27+
# Posición fiscal "percepción-only": sin tax_ids, con l10n_ar_tax_ids
28+
cls.perception_only_fp = cls.env["account.fiscal.position"].create(
29+
{
30+
"name": "Test FP Percepcion Only",
31+
"company_id": cls.company_ri.id,
32+
}
33+
)
34+
cls.env["account.fiscal.position.l10n_ar_tax"].create(
35+
{
36+
"fiscal_position_id": cls.perception_only_fp.id,
37+
"default_tax_id": cls.caba_perception_tax.id,
38+
"tax_type": "perception",
39+
}
40+
)
41+
42+
# Posición fiscal con tax_ids explícitos: IVA 21% → IVA 0%
43+
# En Odoo 19, tax_ids es Many2many a account.tax y el mapeo funciona
44+
# mediante original_tax_ids en el impuesto destino.
45+
cls.tax_0.original_tax_ids = [Command.set(cls.tax_21.ids)]
46+
cls.fp_with_tax_mapping = cls.env["account.fiscal.position"].create(
47+
{
48+
"name": "Test FP Con Mapping IVA",
49+
"company_id": cls.company_ri.id,
50+
"tax_ids": [Command.set(cls.tax_0.ids)],
51+
}
52+
)
53+
54+
def test_map_tax_perception_only_not_affected_by_domestic_fp_substitution(self):
55+
"""
56+
Incluso si el domestic FP tiene una sustitución IVA 21% → IVA 0%,
57+
la FP con sólo percepción debe devolver IVA 21% sin cambios.
58+
"""
59+
# Configurar domestic FP con sustitución IVA 21% → IVA 0%
60+
domestic_fp = self.company_ri.domestic_fiscal_position_id
61+
if not domestic_fp:
62+
self.skipTest("No se encontró domestic fiscal position para la compañía de test.")
63+
64+
# Agregar sustitución al domestic FP via original_tax_ids en IVA 0%
65+
if self.tax_21 not in self.tax_0.original_tax_ids:
66+
self.tax_0.original_tax_ids = [Command.link(self.tax_21.id)]
67+
if self.tax_0 not in domestic_fp.tax_ids:
68+
domestic_fp.tax_ids = [Command.link(self.tax_0.id)]
69+
70+
taxes = self.tax_21
71+
result = self.perception_only_fp.map_tax(taxes)
72+
self.assertEqual(
73+
result,
74+
self.tax_21,
75+
"La FP percepcion-only no debe aplicar la sustitucion IVA 21%→IVA 0% del domestic FP.",
76+
)
77+
78+
def test_map_tax_fp_with_explicit_tax_ids_applies_substitution(self):
79+
"""
80+
Una FP con tax_ids explícitos debe aplicar la sustitución mediante super().map_tax().
81+
"""
82+
taxes = self.tax_21
83+
result = self.fp_with_tax_mapping.map_tax(taxes)
84+
self.assertEqual(
85+
result,
86+
self.tax_0,
87+
"Una FP con tax_ids explícitos debe aplicar la sustitucion de impuestos.",
88+
)
89+
90+
def test_invoice_with_fp_tax_mapping_applies_vat_substitution(self):
91+
"""
92+
Al crear una factura con una FP que tiene tax_ids explícitos (IVA 21% → IVA 0%),
93+
sin tax_ids en la línea, _compute_tax_ids → _get_computed_taxes() → map_tax()
94+
debe sustituir IVA 21% por IVA 0%.
95+
Valida que el flujo de impuestos en facturas de cliente aplica correctamente la sustitución
96+
definida en la FP con tax_ids explícitos."""
97+
invoice = self.env["account.move"].create(
98+
{
99+
"move_type": "out_invoice",
100+
"partner_id": self.res_partner_adhoc.id,
101+
"fiscal_position_id": self.fp_with_tax_mapping.id,
102+
"company_id": self.company_ri.id,
103+
"invoice_date": "2025-01-15",
104+
"date": "2025-01-15",
105+
"invoice_line_ids": [
106+
Command.create(
107+
{
108+
"product_id": self.service_iva_21.id,
109+
"quantity": 1,
110+
"price_unit": 1000.0,
111+
}
112+
)
113+
],
114+
"l10n_latam_document_number": "0001-00000002",
115+
}
116+
)
117+
invoice.action_post()
118+
119+
line_taxes = invoice.invoice_line_ids.tax_ids
120+
self.assertIn(
121+
self.tax_0,
122+
line_taxes,
123+
"IVA 0% debe estar en la línea: la FP con tax_ids debe sustituir IVA 21% por IVA 0%.",
124+
)
125+
self.assertNotIn(
126+
self.tax_21,
127+
line_taxes,
128+
"IVA 21% debe haber sido reemplazado por IVA 0% via la FP con mapping explícito.",
129+
)
130+
131+
def test_invoice_with_perception_only_fp_preserves_vat_taxes(self):
132+
"""
133+
Al crear una factura con una FP con sólo percepción sin tax_ids explícitos en la línea,
134+
Odoo computa tax_ids vía _compute_tax_ids → _get_computed_taxes() → map_tax().
135+
El IVA 21% del producto debe conservarse sin ser reemplazado por IVA 0%.
136+
"""
137+
domestic_fp = self.company_ri.domestic_fiscal_position_id
138+
if not domestic_fp:
139+
self.skipTest("No se encontró domestic fiscal position para la compañía de test.")
140+
141+
# Asegurar que el domestic FP tiene sustitución IVA 21% → IVA 0%
142+
if self.tax_21 not in self.tax_0.original_tax_ids:
143+
self.tax_0.original_tax_ids = [Command.link(self.tax_21.id)]
144+
if self.tax_0 not in domestic_fp.tax_ids:
145+
domestic_fp.tax_ids = [Command.link(self.tax_0.id)]
146+
147+
invoice = self.env["account.move"].create(
148+
{
149+
"move_type": "out_invoice",
150+
"partner_id": self.res_partner_adhoc.id,
151+
"fiscal_position_id": self.perception_only_fp.id,
152+
"company_id": self.company_ri.id,
153+
"invoice_date": "2025-01-15",
154+
"date": "2025-01-15",
155+
"invoice_line_ids": [
156+
Command.create(
157+
{
158+
"product_id": self.service_iva_21.id,
159+
"quantity": 1,
160+
"price_unit": 1000.0,
161+
}
162+
)
163+
],
164+
"l10n_latam_document_number": "0001-00000001",
165+
}
166+
)
167+
invoice.action_post()
168+
169+
line_taxes = invoice.invoice_line_ids.tax_ids
170+
self.assertIn(
171+
self.tax_21,
172+
line_taxes,
173+
"IVA 21% debe estar presente en la línea cuando se usa una FP percepcion-only.",
174+
)
175+
self.assertNotIn(
176+
self.tax_0,
177+
line_taxes,
178+
"IVA 0% no debe aparecer en la línea; la FP percepcion-only no debe sustituir impuestos.",
179+
)
180+
181+
def test_payment_for_invoice_with_perception_only_fp_uses_correct_tax_amount(self):
182+
"""
183+
Al registrar el pago de una factura de cliente con FP percepción-only,
184+
el monto del pago debe reflejar IVA 21% (base 1000 → total 1210), no IVA 0% (1000).
185+
Si map_tax() hubiera aplicado la sustitución del domestic FP, el total de la
186+
factura sería 1000 y el pago por 1210 dejaría un residual, o el pago se
187+
registraría por 1000 y el total sería incorrecto.
188+
"""
189+
domestic_fp = self.company_ri.domestic_fiscal_position_id
190+
if not domestic_fp:
191+
self.skipTest("No se encontró domestic fiscal position para la compañía de test.")
192+
193+
# Asegurar sustitución activa en domestic FP para que el escenario sea realista
194+
if self.tax_21 not in self.tax_0.original_tax_ids:
195+
self.tax_0.original_tax_ids = [Command.link(self.tax_21.id)]
196+
if self.tax_0 not in domestic_fp.tax_ids:
197+
domestic_fp.tax_ids = [Command.link(self.tax_0.id)]
198+
199+
invoice = self.env["account.move"].create(
200+
{
201+
"move_type": "out_invoice",
202+
"partner_id": self.res_partner_adhoc.id,
203+
"fiscal_position_id": self.perception_only_fp.id,
204+
"company_id": self.company_ri.id,
205+
"invoice_date": "2025-01-15",
206+
"date": "2025-01-15",
207+
"invoice_line_ids": [
208+
Command.create(
209+
{
210+
"product_id": self.service_iva_21.id,
211+
"quantity": 1,
212+
"price_unit": 1000.0,
213+
}
214+
)
215+
],
216+
"l10n_latam_document_number": "0001-00000003",
217+
}
218+
)
219+
invoice.action_post()
220+
221+
self.assertAlmostEqual(
222+
invoice.amount_total,
223+
1210.0,
224+
places=2,
225+
msg="El total de la factura debe incluir IVA 21% (1000 + 210 = 1210).",
226+
)
227+
228+
(
229+
self.env["account.payment.register"]
230+
.with_context(active_model="account.move", active_ids=invoice.ids)
231+
.create({"payment_date": "2025-01-15"})
232+
.action_create_payments()
233+
)
234+
235+
self.assertAlmostEqual(
236+
invoice.amount_residual,
237+
0.0,
238+
places=2,
239+
msg="La factura debe quedar completamente saldada con el monto IVA 21% (1210).",
240+
)

0 commit comments

Comments
 (0)