55import datetime
66from unittest .mock import patch
77
8- from odoo .addons .account .tests .common import AccountTestInvoicingCommon
9- from odoo .tests import tagged
8+ from odoo .tests import TransactionCase , tagged
109
1110
1211@tagged ("post_install" , "-at_install" )
13- class TestL10nArCurrencyUpdate (AccountTestInvoicingCommon ):
12+ class TestL10nArCurrencyUpdate (TransactionCase ):
1413 @classmethod
15- @AccountTestInvoicingCommon .setup_chart_template ("ar_ri" )
1614 def setUpClass (cls ):
1715 super ().setUpClass ()
1816 cls .ARS = cls .env .ref ("base.ARS" )
1917 cls .USD = cls .env .ref ("base.USD" )
2018 cls .EUR = cls .env .ref ("base.EUR" )
2119
22- # Activamos monedas por las dudas
20+ # Activamos monedas
2321 cls .USD .active = True
2422 cls .EUR .active = True
2523 cls .utils_path = "odoo.addons.l10n_ar_currency_update.models.res_company.ResCompany"
2624
2725 def test_ARS (self ):
28- """When the base currency is ARS"""
29- msg_error = "Should not be any rate for this currency and company to continue with the test"
30- self .assertEqual (self .env .company .currency_id , self .ARS )
31- self .assertEqual (self .ARS .rate , 1.0 , msg_error )
32- self .assertEqual (self .USD .rate , 1.0 , msg_error )
33- self .assertEqual (self .EUR .rate , 1.0 , msg_error )
34-
35- test_date = datetime .date (2024 , 9 , 24 )
26+ """Cuando se hace la actualización de monedas desde ARCA, siempre tenemos que validar que se actualice la moneda local (ARS) aunque su tipo de cambio sea 1.0 .
27+ Esto es requerido por validaciones de contabilidad en método _generate_currency_rates de src/enterprise/currency_rate_live/models/res_config_settings.py.
28+ Si no actualizamos la moneda local, no se crea ningún registro de tipo de cambio para esa fecha y empresa."""
29+ test_date = datetime .date (2026 , 9 , 24 )
30+
31+ # Clean existing rates for test isolation
32+ self .env ["res.currency.rate" ].search (
33+ [
34+ ("name" , "=" , test_date ),
35+ ("company_id" , "=" , self .env .company .id ),
36+ ]
37+ ).unlink ()
38+
39+ # Configure company to use AFIP provider
40+ self .env .company .write (
41+ {
42+ "currency_provider" : "afip" ,
43+ "currency_interval_unit" : "daily" ,
44+ }
45+ )
46+
47+ # Mock data must include base currency for enterprise validation
3648 mocked_res = {
3749 "ARS" : (1.0 , test_date ),
3850 "EUR" : (0.0009435361546070796 , test_date ),
3951 "USD" : (0.0010481301358376655 , test_date ),
4052 }
4153
4254 with patch (f"{ self .utils_path } ._parse_afip_data" , return_value = mocked_res ):
43- self .env .company .update_currency_rates ()
55+ self .env .company .with_context ( l10n_ar_force_create_rate = True ). update_currency_rates ()
4456
45- self .assertEqual (self .ARS .rate , 1.0 )
46- self .assertNotEqual (self .USD .rate , 954.08 )
47- self .assertNotEqual (self .EUR .rate , 1059.8428 )
57+ # Base currency WILL have a rate created with rate=1.0 (expected behavior)
58+ base_rate = self .env ["res.currency.rate" ].search (
59+ [
60+ ("currency_id" , "=" , self .env .company .currency_id .id ),
61+ ("name" , "=" , test_date ),
62+ ("company_id" , "=" , self .env .company .id ),
63+ ]
64+ )
65+ self .assertTrue (base_rate , "Base currency should have a rate created" )
66+ self .assertEqual (base_rate .rate , 1.0 , "Base currency rate should be 1.0" )
67+
68+ # Verify other currencies got rates created
69+ for curr_code in mocked_res .keys ():
70+ if curr_code != self .env .company .currency_id .name :
71+ if curr := self .env ["res.currency" ].search ([("name" , "=" , curr_code )]):
72+ rate = self .env ["res.currency.rate" ].search (
73+ [
74+ ("currency_id" , "=" , curr .id ),
75+ ("name" , "=" , test_date ),
76+ ("company_id" , "=" , self .env .company .id ),
77+ ]
78+ )
79+ self .assertTrue (rate , f"{ curr_code } should have a rate created" )
4880
4981 def test_currency_rate_with_rate_perc (self ):
5082 """Check that the rate percentage is applied only to company 1 when both sync from ARCA"""
@@ -53,8 +85,15 @@ def test_currency_rate_with_rate_perc(self):
5385 rate_perc = 0.03
5486 test_date = datetime .date .today ()
5587
56- # Create a second Argentine company
88+ # Create two fresh Argentine companies to avoid currency change issues
5789 ar_country = self .env .ref ("base.ar" )
90+ company_1 = self .env ["res.company" ].create (
91+ {
92+ "name" : "Test Argentine Company 1" ,
93+ "country_id" : ar_country .id ,
94+ "currency_id" : self .ARS .id ,
95+ }
96+ )
5897 company_2 = self .env ["res.company" ].create (
5998 {
6099 "name" : "Test Argentine Company 2" ,
@@ -63,49 +102,49 @@ def test_currency_rate_with_rate_perc(self):
63102 }
64103 )
65104
66- # Configure the first company (self.env.company) with currency provider AND rate_perc
67- self . env . company .write (
105+ # Configure company_1 with currency provider AND rate_perc
106+ company_1 .write (
68107 {
69108 "currency_provider" : "afip" ,
70109 "rate_perc" : rate_perc ,
71110 "currency_interval_unit" : "daily" ,
72111 }
73112 )
74113
75- # Configure the second company with currency provider but WITHOUT rate_perc
114+ # Configure company_2 with currency provider but WITHOUT rate_perc
76115 company_2 .write (
77116 {
78117 "currency_provider" : "afip" ,
79118 "currency_interval_unit" : "daily" ,
119+ "rate_perc" : 0.0 , # Explicitly no markup
80120 }
81121 )
82122
83123 # Clean up previous rates for both companies
84- existing_rates = self .env ["res.currency.rate" ].search (
124+ self .env ["res.currency.rate" ].search (
85125 [
86126 ("currency_id" , "=" , self .USD .id ),
87127 ("name" , "=" , test_date ),
88- ("company_id" , "in" , [self . env . company .id , company_2 .id ]),
128+ ("company_id" , "in" , [company_1 .id , company_2 .id ]),
89129 ]
90- )
91- existing_rates .unlink ()
130+ ).unlink ()
92131
93132 # Prepare mock data for AFIP
94133 mocked_res = {
95- "ARS" : (1.0 , test_date ),
134+ "ARS" : (1.0 , test_date ), # Base currency for both companies
96135 "USD" : (1.0 / base_arca_rate , test_date ),
97136 }
98137
99138 # Execute test logic with patch - call update_currency_rates on both companies
100139 with patch (f"{ self .utils_path } ._parse_afip_data" , return_value = mocked_res ):
101- (self . env . company | company_2 ).update_currency_rates ()
140+ (company_1 | company_2 ). with_context ( l10n_ar_force_create_rate = True ).update_currency_rates ()
102141
103142 # Validate rate creation for company 1 (WITH percentage rate_perc)
104143 rate_record_company_1 = self .env ["res.currency.rate" ].search (
105144 [
106145 ("currency_id" , "=" , self .USD .id ),
107146 ("name" , "=" , test_date ),
108- ("company_id" , "=" , self . env . company .id ),
147+ ("company_id" , "=" , company_1 .id ),
109148 ]
110149 )
111150
@@ -115,15 +154,16 @@ def test_currency_rate_with_rate_perc(self):
115154 )
116155
117156 # Verify markup was applied correctly for company 1
118- # The inverse rate (ARS per USD unit) should be base_arca_rate * (1 + rate_perc)
119- inverse_rate_company_1 = 1 / rate_record_company_1 .rate
120- expected_inverse_rate_company_1 = base_arca_rate * (1 + rate_perc )
157+ # In Odoo, rate field = 1 / (ARS per USD)
158+ # With markup: ARS_per_USD = base_arca_rate * (1 + rate_perc)
159+ # So rate field = 1 / (base_arca_rate * (1 + rate_perc))
160+ expected_rate_with_markup = 1.0 / (base_arca_rate * (1 + rate_perc ))
121161
122162 self .assertAlmostEqual (
123- inverse_rate_company_1 ,
124- expected_inverse_rate_company_1 ,
125- places = 2 ,
126- msg = "The inverse rate value (ARS per unit) with markup is incorrect for company 1." ,
163+ rate_record_company_1 . rate ,
164+ expected_rate_with_markup ,
165+ places = 6 ,
166+ msg = "The rate with markup is incorrect for company 1." ,
127167 )
128168
129169 # Validate rate creation for company 2 (WITHOUT rate_perc)
@@ -141,13 +181,13 @@ def test_currency_rate_with_rate_perc(self):
141181 )
142182
143183 # Verify NO markup was applied for company 2 (base ARCA rate)
144- inverse_rate_company_2 = 1 / rate_record_company_2 . rate
184+ expected_rate_no_markup = 1.0 / base_arca_rate
145185
146186 self .assertAlmostEqual (
147- inverse_rate_company_2 ,
148- base_arca_rate ,
149- places = 2 ,
150- msg = "The inverse rate value (ARS per unit) should be the base ARCA rate for company 2 (no markup)." ,
187+ rate_record_company_2 . rate ,
188+ expected_rate_no_markup ,
189+ places = 6 ,
190+ msg = "The rate should be the base ARCA rate for company 2 (no markup)." ,
151191 )
152192
153193 # Verify that the rates are different between companies
0 commit comments