Skip to content

Commit 7d162b0

Browse files
committed
[18.0][MIG] product_tax_multicompany_default: migration to 18.0
This PR is a continuation of PR OCA#811. This PR is for the migration of the product_tax_multicompany_default module, which is used to propagate taxes across multiple companies. This commit makes changes so that it is not duplicated in the Sales Taxes form view, and removes the functionality when creating a new product in all companies, since Odoo 18 already does this. @moduon MT-11041
1 parent c443832 commit 7d162b0

5 files changed

Lines changed: 158 additions & 67 deletions

File tree

product_tax_multicompany_default/README.rst

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
Product Tax Multi Company Default
33
=================================
44

5-
..
5+
..
66
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
77
!! This file is generated by oca-gen-addon-readme !!
88
!! changes will be overwritten. !!
@@ -94,20 +94,20 @@ Authors
9494
Contributors
9595
------------
9696

97-
- `Tecnativa <https://www.tecnativa.com>`__:
97+
- `Tecnativa <https://www.tecnativa.com>`__:
9898

99-
- Carlos Dauden
100-
- Pedro M. Baeza
101-
- Vicent Cubells
102-
- Ernesto Tejeda
99+
- Carlos Dauden
100+
- Pedro M. Baeza
101+
- Vicent Cubells
102+
- Ernesto Tejeda
103103

104-
- Loo <http://odooerp.cl/>`\_:
104+
- Loo <http://odooerp.cl/>`\_:
105105

106-
- Carlos Lopez
106+
- Carlos Lopez
107107

108-
- `Moduon <https://www.moduon.team>`__:
108+
- `Moduon <https://www.moduon.team>`__:
109109

110-
- Eduardo de Miguel
110+
- Eduardo de Miguel
111111

112112
Maintainers
113113
-----------
@@ -128,7 +128,7 @@ promote its widespread use.
128128

129129
Current `maintainer <https://odoo-community.org/page/maintainer-role>`__:
130130

131-
|maintainer-Shide|
131+
|maintainer-Shide|
132132

133133
This module is part of the `OCA/multi-company <https://github.com/OCA/multi-company/tree/18.0/product_tax_multicompany_default>`_ project on GitHub.
134134

product_tax_multicompany_default/models/product.py

Lines changed: 111 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ class ProductTemplate(models.Model):
1414
string="Has divergent cross-company taxes",
1515
compute="_compute_divergent_company_taxes",
1616
compute_sudo=True,
17-
store=True,
1817
help=(
1918
"Does this product have divergent cross-company taxes? "
2019
"(Only for multi-company products)"
@@ -24,38 +23,125 @@ class ProductTemplate(models.Model):
2423
@api.depends("company_id", "taxes_id", "supplier_taxes_id")
2524
def _compute_divergent_company_taxes(self):
2625
"""Know if this product has divergent taxes across companies."""
27-
all_companies = self.env["res.company"].search(
28-
[
29-
# Useful for tests, to avoid pollution
30-
("id", "not in", self.env.context.get("ignored_company_ids", []))
31-
]
32-
)
3326
for one in self:
3427
one.divergent_company_taxes = False
3528
# Skip single-company products
36-
if one.company_id:
29+
if one.company_id or len(one.env["res.company"].search([]).ids) <= 1:
3730
continue
3831
# A unique constraint in account.tax makes it impossible to have
3932
# duplicated tax names by company
40-
customer_taxes = {
41-
frozenset(tax.name for tax in one.taxes_id if tax.company_id == company)
42-
for company in all_companies
43-
}
44-
if len(customer_taxes) > 1:
45-
one.divergent_company_taxes = True
33+
one.divergent_company_taxes = self._is_divergent_company_taxes(one, "taxes")
34+
if one.divergent_company_taxes:
4635
continue
47-
supplier_taxes = {
48-
frozenset(
49-
tax.name
50-
for tax in one.supplier_taxes_id
51-
if tax.company_id == company
52-
)
53-
for company in all_companies
54-
}
55-
if len(supplier_taxes) > 1:
56-
one.divergent_company_taxes = True
36+
37+
one.divergent_company_taxes = self._is_divergent_company_taxes(
38+
one, "purchase"
39+
)
40+
if one.divergent_company_taxes:
5741
continue
5842

43+
def _is_divergent_company_taxes(self, current_product, tax_type):
44+
"""Returns true or false if there are differences in product taxes.
45+
46+
:param one: product to be checked
47+
:param tax_type: can be 'taxes' or 'purchase'
48+
"""
49+
all_companies = self.env["res.company"].search(
50+
[
51+
# Useful for tests, to avoid pollution
52+
("id", "not in", self.env.context.get("ignored_company_ids", [])),
53+
]
54+
)
55+
current_company = self.env.company
56+
57+
taxes_bd = dict(self._get_product_taxes(tax_type, current_product._origin.id))
58+
for k in list(taxes_bd.keys()):
59+
if k not in all_companies.ids or k == current_company.id:
60+
taxes_bd.pop(k)
61+
if tax_type == "taxes":
62+
current_tax = current_product.taxes_id
63+
field_name_data = "account_sale_tax_id"
64+
elif tax_type == "purchase":
65+
current_tax = current_product.supplier_taxes_id
66+
field_name_data = "account_purchase_tax_id"
67+
propagate_taxes = False
68+
69+
# Tax-free
70+
if not current_tax:
71+
# Since all companies can have an empty tax field,if there is data
72+
# in the database, it means that we can propagate leaving it empty.
73+
if len(taxes_bd) > 0:
74+
propagate_taxes = True
75+
# No tax in other products
76+
elif not taxes_bd:
77+
# If we do not have any taxes in the database and we add a tax, it means
78+
# that we can update the product taxes.
79+
current_tax_ids = current_tax.filtered(
80+
lambda t: t.company_id == current_company
81+
).ids
82+
candidates = set()
83+
all_companies = all_companies.filtered(lambda c: c.id != current_company.id)
84+
for company in all_companies:
85+
candidates.update(
86+
self.taxes_by_company(field_name_data, company, current_tax_ids)
87+
)
88+
if len(candidates) > 0:
89+
propagate_taxes = True
90+
else:
91+
current_names = current_tax.filtered(
92+
lambda t: t.company_id == current_company
93+
).mapped("name")
94+
current_tax_ids = current_tax.filtered(
95+
lambda t: t.company_id == current_company
96+
).ids
97+
for company_id, value in taxes_bd.items():
98+
if current_names == list(value.values()):
99+
continue
100+
# We are looking to see if there are any taxes that can be applied to
101+
# other companies from the taxes that the current product has.
102+
candidates = self.taxes_by_company(
103+
field_name_data,
104+
self.env["res.company"].browse(company_id),
105+
current_tax_ids,
106+
)
107+
if len(candidates) == len(current_tax):
108+
propagate_taxes = True
109+
break
110+
111+
return propagate_taxes
112+
113+
def _get_product_taxes(self, field, product_id):
114+
"""Returns taxes on purchase or sales taxes
115+
116+
We need the product taxes for other companies, and we cannot take
117+
them from the object itself, so we have to consult the database
118+
where the product taxes passed by parameter are located for all
119+
companies.
120+
121+
:param field: can be 'taxes' or 'purchase'
122+
:param product_id: product to which the query should be applied
123+
"""
124+
if not product_id:
125+
return []
126+
127+
table_by_field = {
128+
"taxes": "product_taxes_rel",
129+
"purchase": "product_supplier_taxes_rel",
130+
}
131+
table_name = table_by_field.get(field)
132+
133+
if not table_name:
134+
raise ValueError("field should be 'taxes' or 'purchase'")
135+
136+
sql = f"""
137+
SELECT DISTINCT a_tax.company_id, a_tax.name
138+
FROM {table_name} ptr
139+
LEFT JOIN account_tax a_tax ON tax_id = a_tax.id
140+
WHERE ptr.prod_id = %s
141+
"""
142+
self.env.cr.execute(sql, [product_id])
143+
return self.env.cr.fetchall()
144+
59145
def taxes_by_company(self, field, company, match_tax_ids=None):
60146
taxes_ids = []
61147
if match_tax_ids is None:
@@ -160,6 +246,7 @@ def create(self, vals_list):
160246
new_products = super().create(vals_list)
161247
for product in new_products:
162248
product.set_multicompany_taxes()
249+
new_products.invalidate_recordset(fnames=["taxes_id", "supplier_taxes_id"])
163250
return new_products
164251

165252

product_tax_multicompany_default/static/description/index.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -369,7 +369,7 @@ <h1 class="title">Product Tax Multi Company Default</h1>
369369
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
370370
!! source digest: sha256:9754e5fe5a17246d3dcbb1677fb59090a7e43a4c9f7df455ee968461f3abfbac
371371
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
372-
<p><a class="reference external image-reference" href="https://odoo-community.org/page/development-status"><img alt="Beta" src="https://img.shields.io/badge/maturity-Beta-yellow.png" /></a> <a class="reference external image-reference" href="http://www.gnu.org/licenses/agpl-3.0-standalone.html"><img alt="License: AGPL-3" src="https://img.shields.io/badge/licence-AGPL--3-blue.png" /></a> <a class="reference external image-reference" href="https://github.com/OCA/multi-company/tree/18.0/product_tax_multicompany_default"><img alt="OCA/multi-company" src="https://img.shields.io/badge/github-OCA%2Fmulti--company-lightgray.png?logo=github" /></a> <a class="reference external image-reference" href="https://translation.odoo-community.org/projects/multi-company-18-0/multi-company-17-0-product_tax_multicompany_default"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external image-reference" href="https://runboat.odoo-community.org/builds?repo=OCA/multi-company&amp;target_branch=17.0"><img alt="Try me on Runboat" src="https://img.shields.io/badge/runboat-Try%20me-875A7B.png" /></a></p>
372+
<p><a class="reference external image-reference" href="https://odoo-community.org/page/development-status"><img alt="Beta" src="https://img.shields.io/badge/maturity-Beta-yellow.png" /></a> <a class="reference external image-reference" href="http://www.gnu.org/licenses/agpl-3.0-standalone.html"><img alt="License: AGPL-3" src="https://img.shields.io/badge/licence-AGPL--3-blue.png" /></a> <a class="reference external image-reference" href="https://github.com/OCA/multi-company/tree/18.0/product_tax_multicompany_default"><img alt="OCA/multi-company" src="https://img.shields.io/badge/github-OCA%2Fmulti--company-lightgray.png?logo=github" /></a> <a class="reference external image-reference" href="https://translation.odoo-community.org/projects/multi-company-18-0/multi-company-18-0-product_tax_multicompany_default"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external image-reference" href="https://runboat.odoo-community.org/builds?repo=OCA/multi-company&amp;target_branch=18.0"><img alt="Try me on Runboat" src="https://img.shields.io/badge/runboat-Try%20me-875A7B.png" /></a></p>
373373
<p>This module sets the default company taxes for all the existing
374374
companies when a product is created. It also adds a button in product
375375
view to set all the taxes from other companies matching them by tax

product_tax_multicompany_default/tests/test_product_tax_multicompany.py

Lines changed: 25 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -175,8 +175,8 @@ def test_tax_not_default_set_match(self):
175175
}
176176
)
177177
)
178-
self.assertIn(self.tax_10_cc2, product.taxes_id)
179-
self.assertIn(self.tax_10_sc2, product.supplier_taxes_id)
178+
self.assertIn(self.tax_10_cc1, product.taxes_id)
179+
self.assertIn(self.tax_10_sc1, product.supplier_taxes_id)
180180

181181
@users("user_12")
182182
def test_set_multicompany_taxes(self):
@@ -192,41 +192,49 @@ def test_set_multicompany_taxes(self):
192192
# Create product with empty taxes
193193
# use sudo because the account.group_account_manager group
194194
# does not have permission to create products.
195-
pf_u3_c1 = Form(self.env["product.product"].sudo().with_company(self.company_1))
196-
pf_u3_c1.name = "Testing Empty Taxes"
197-
pf_u3_c1.taxes_id.clear()
198-
pf_u3_c1.supplier_taxes_id.clear()
199-
product = pf_u3_c1.save()
195+
product = (
196+
self.env["product.product"]
197+
.sudo()
198+
.with_company(self.company_1)
199+
.create({"name": "X"})
200+
)
201+
product.product_tmpl_id.write(
202+
{
203+
"taxes_id": [(5, 0, 0)],
204+
"supplier_taxes_id": [(5, 0, 0)],
205+
}
206+
)
200207
self.assertFalse(
201208
product.taxes_id,
202209
"Taxes not empty when initializing product",
203210
)
204-
pf_u3_c1 = Form(product.with_company(self.company_1))
211+
ctx = {"default_taxes_id": [], "default_supplier_taxes_id": []}
212+
pf_u3_c1 = Form(
213+
self.env["product.template"]
214+
.sudo()
215+
.with_company(self.company_1)
216+
.with_context(**ctx)
217+
)
205218
# Fill taxes
206219
pf_u3_c1.name = "Testing Filling Taxes"
207220
pf_u3_c1.taxes_id.add(self.tax_30_cc1)
208221
pf_u3_c1.supplier_taxes_id.add(self.tax_30_sc1)
209222
product = pf_u3_c1.save()
210-
self.assertEqual(
211-
product.taxes_id,
212-
self.tax_30_cc1,
213-
"Taxes has been propagated before calling set_multicompany_taxes",
214-
)
215223
product.with_company(self.company_1).set_multicompany_taxes()
216224
company_1_taxes_fill = product.taxes_id.filtered(
217225
lambda t: t.company_id == self.company_1
218226
)
219227
company_2_taxes_fill = product.taxes_id.filtered(
220228
lambda t: t.company_id == self.company_2
221229
)
222-
self.assertEqual(
223-
company_1_taxes_fill,
230+
self.assertIn(
224231
self.tax_30_cc1,
232+
company_1_taxes_fill,
225233
"Incorrect taxes when setting it for the first time in Company 1",
226234
)
227-
self.assertEqual(
228-
company_2_taxes_fill,
235+
self.assertIn(
229236
self.tax_30_cc2,
237+
company_2_taxes_fill,
230238
"Incorrect taxes when setting it for the first time in Company 2",
231239
)
232240
# Change taxes

product_tax_multicompany_default/views/product_template_view.xml

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -11,20 +11,16 @@
1111
<field name="taxes_id" position="attributes">
1212
<attribute name="class" separator=" " add="oe_inline" />
1313
</field>
14-
<field name="taxes_id" position="replace">
15-
<label for="taxes_id" />
16-
<div name="taxes_id">
17-
<t>$0</t>
18-
<button
19-
name="set_multicompany_taxes"
20-
icon="fa-clone"
21-
string="Propagate Taxes"
22-
type="object"
23-
class="btn btn-link oe_inline"
24-
invisible="not divergent_company_taxes"
25-
groups="account.group_account_user"
26-
/>
27-
</div>
14+
<field name="taxes_id" position="after">
15+
<button
16+
name="set_multicompany_taxes"
17+
type="object"
18+
icon="fa-clone"
19+
string="Propagate Taxes"
20+
class="btn btn-link oe_inline"
21+
invisible="not divergent_company_taxes"
22+
groups="account.group_account_user"
23+
/>
2824
</field>
2925
</field>
3026
</record>

0 commit comments

Comments
 (0)