From e194fb9516e1b0a641493a364af07d7b3f760359 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A0n=20Todorovich?= Date: Thu, 30 Jun 2022 15:11:52 -0300 Subject: [PATCH 1/3] [FIX] product_category_tax: onchange method name shouldn't be too generic Unless it's an override, onchange method names can't be too generic, otherwise it has a high risk of unadvertedly overwriting another module's method. --- product_category_tax/models/product_product.py | 2 +- product_category_tax/models/product_template.py | 2 +- product_category_tax/tests/test_product_category_tax.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/product_category_tax/models/product_product.py b/product_category_tax/models/product_product.py index 16c41a503..afc712d47 100644 --- a/product_category_tax/models/product_product.py +++ b/product_category_tax/models/product_product.py @@ -8,7 +8,7 @@ class ProductProduct(models.Model): _inherit = "product.product" @api.onchange("categ_id") - def onchange_categ_id(self): + def _onchange_categ_id_set_taxes(self): if self.categ_id: self.set_tax_from_category() diff --git a/product_category_tax/models/product_template.py b/product_category_tax/models/product_template.py index bd4c3f8e7..23bdb69f2 100644 --- a/product_category_tax/models/product_template.py +++ b/product_category_tax/models/product_template.py @@ -10,7 +10,7 @@ class ProductTemplate(models.Model): taxes_updeatable_from_category = fields.Boolean(default=True) @api.onchange("categ_id") - def onchange_categ_id(self): + def _onchange_categ_id_set_taxes(self): if self.categ_id: self.set_tax_from_category() diff --git a/product_category_tax/tests/test_product_category_tax.py b/product_category_tax/tests/test_product_category_tax.py index bc1d0b393..fcc71721b 100644 --- a/product_category_tax/tests/test_product_category_tax.py +++ b/product_category_tax/tests/test_product_category_tax.py @@ -52,7 +52,7 @@ class ProductCategoryTax(common.SavepointCase): self.product_test = self.product_obj.create( {"name": "TEST 01", "categ_id": test_categ.id, "list_price": 155.0} ) - self.product_test.product_tmpl_id.onchange_categ_id() + self.product_test.product_tmpl_id._onchange_categ_id_set_taxes() self.assertEquals(self.product_test.supplier_taxes_id, self.tax_purchase) def test_02_update_taxes(self): From 9ddd96d938d9ed170cfbc93e3175d582c902a868 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A0n=20Todorovich?= Date: Thu, 30 Jun 2022 15:46:33 -0300 Subject: [PATCH 2/3] [IMP] product_category_tax: set tax performance on large recordsets 1) database searching is much more efficient than py filtering 2) writing on multiple products at once is more efficient --- product_category_tax/models/product_category.py | 13 +++++++++---- product_category_tax/models/product_product.py | 4 +--- product_category_tax/models/product_template.py | 16 +++++++++++++--- 3 files changed, 23 insertions(+), 10 deletions(-) diff --git a/product_category_tax/models/product_category.py b/product_category_tax/models/product_category.py index 23f269912..b1ee87b76 100644 --- a/product_category_tax/models/product_category.py +++ b/product_category_tax/models/product_category.py @@ -32,7 +32,12 @@ class ProductCategory(models.Model): ) def update_product_taxes(self): - for template in self.product_template_ids.filtered( - lambda p: p.taxes_updeatable_from_category - ): - template.set_tax_from_category() + products = self.env["product.template"].search( + [ + ("categ_id", "in", self.ids), + ("taxes_updeatable_from_category", "=", True), + ] + ) + if not products: + return True + return products.set_tax_from_category() diff --git a/product_category_tax/models/product_product.py b/product_category_tax/models/product_product.py index afc712d47..1b7f70a33 100644 --- a/product_category_tax/models/product_product.py +++ b/product_category_tax/models/product_product.py @@ -13,6 +13,4 @@ class ProductProduct(models.Model): self.set_tax_from_category() def set_tax_from_category(self): - self.ensure_one() - self.taxes_id = [(6, 0, self.categ_id.taxes_id.ids)] - self.supplier_taxes_id = [(6, 0, self.categ_id.supplier_taxes_id.ids)] + return self.product_tmpl_id.set_tax_from_category() diff --git a/product_category_tax/models/product_template.py b/product_category_tax/models/product_template.py index 23bdb69f2..2a7c4b836 100644 --- a/product_category_tax/models/product_template.py +++ b/product_category_tax/models/product_template.py @@ -1,6 +1,8 @@ # Copyright 2020 ForgeFlow S.L. (https://www.forgeflow.com) # License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). +from collections import defaultdict + from odoo import api, fields, models @@ -15,6 +17,14 @@ class ProductTemplate(models.Model): self.set_tax_from_category() def set_tax_from_category(self): - self.ensure_one() - self.taxes_id = [(6, 0, self.categ_id.taxes_id.ids)] - self.supplier_taxes_id = [(6, 0, self.categ_id.supplier_taxes_id.ids)] + records_by_categ = defaultdict(lambda: self.browse()) + for rec in self: + records_by_categ[rec.categ_id] += rec + for categ, records in records_by_categ.items(): + records.write( + { + "taxes_id": [(6, 0, categ.taxes_id.ids)], + "supplier_taxes_id": [(6, 0, categ.supplier_taxes_id.ids)], + } + ) + return True From a4ebe10b2c0b9784ca186bf561751d913b545107 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A0n=20Todorovich?= Date: Thu, 30 Jun 2022 16:03:32 -0300 Subject: [PATCH 3/3] [IMP] product_category_tax: copy taxes when creating products programatically --- .../models/product_product.py | 12 +++++ .../models/product_template.py | 12 +++++ .../tests/test_product_category_tax.py | 54 +++++++++++++++++++ 3 files changed, 78 insertions(+) diff --git a/product_category_tax/models/product_product.py b/product_category_tax/models/product_product.py index 1b7f70a33..aca1fea36 100644 --- a/product_category_tax/models/product_product.py +++ b/product_category_tax/models/product_product.py @@ -14,3 +14,15 @@ class ProductProduct(models.Model): def set_tax_from_category(self): return self.product_tmpl_id.set_tax_from_category() + + @api.model_create_multi + def create(self, vals_list): + for vals in vals_list: + if vals.get("categ_id"): + if "taxes_id" not in vals: + categ = self.env["product.category"].browse(vals["categ_id"]) + vals["taxes_id"] = [(6, 0, categ.taxes_id.ids)] + if "supplier_taxes_id" not in vals: + categ = self.env["product.category"].browse(vals["categ_id"]) + vals["supplier_taxes_id"] = [(6, 0, categ.supplier_taxes_id.ids)] + return super().create(vals_list) diff --git a/product_category_tax/models/product_template.py b/product_category_tax/models/product_template.py index 2a7c4b836..083ca1c09 100644 --- a/product_category_tax/models/product_template.py +++ b/product_category_tax/models/product_template.py @@ -28,3 +28,15 @@ class ProductTemplate(models.Model): } ) return True + + @api.model_create_multi + def create(self, vals_list): + for vals in vals_list: + if vals.get("categ_id"): + if "taxes_id" not in vals: + categ = self.env["product.category"].browse(vals["categ_id"]) + vals["taxes_id"] = [(6, 0, categ.taxes_id.ids)] + if "supplier_taxes_id" not in vals: + categ = self.env["product.category"].browse(vals["categ_id"]) + vals["supplier_taxes_id"] = [(6, 0, categ.supplier_taxes_id.ids)] + return super().create(vals_list) diff --git a/product_category_tax/tests/test_product_category_tax.py b/product_category_tax/tests/test_product_category_tax.py index fcc71721b..70ed6b786 100644 --- a/product_category_tax/tests/test_product_category_tax.py +++ b/product_category_tax/tests/test_product_category_tax.py @@ -108,3 +108,57 @@ class ProductCategoryTax(common.SavepointCase): test_categ.update_product_taxes() self.assertEquals(self.product_test3.supplier_taxes_id, self.tax_purchase) self.assertNotEquals(self.product_test4.supplier_taxes_id, self.tax_purchase) + + def test_04_copy_taxes_during_create_product_product(self): + """Default taxes taken from the category during product create""" + category = self.env["product.category"].create( + { + "name": "Super Category", + "taxes_id": [(6, 0, self.tax_sale.ids)], + "supplier_taxes_id": [(6, 0, self.tax_purchase.ids)], + } + ) + # Case 1: Creating product.product with category + product = self.env["product.product"].create( + {"name": "Test Product", "categ_id": category.id} + ) + self.assertEqual(product.taxes_id, category.taxes_id) + self.assertEqual(product.supplier_taxes_id, category.supplier_taxes_id) + # Case 2: Creating product.product with category and values + product = self.env["product.product"].create( + { + "name": "Test Product", + "categ_id": category.id, + "taxes_id": False, + "supplier_taxes_id": [(6, 0, self.tax_purchase2.ids)], + } + ) + self.assertFalse(product.taxes_id, False) + self.assertEqual(product.supplier_taxes_id, self.tax_purchase2) + + def test_04_copy_taxes_during_create_product_template(self): + """Default taxes taken from the category during product create""" + category = self.env["product.category"].create( + { + "name": "Super Category", + "taxes_id": [(6, 0, self.tax_sale.ids)], + "supplier_taxes_id": [(6, 0, self.tax_purchase.ids)], + } + ) + # Case 1: Creating product.product with category + product = self.env["product.template"].create( + {"name": "Test Product", "categ_id": category.id} + ) + self.assertEqual(product.taxes_id, category.taxes_id) + self.assertEqual(product.supplier_taxes_id, category.supplier_taxes_id) + # Case 2: Creating product.product with category and values + product = self.env["product.template"].create( + { + "name": "Test Product", + "categ_id": category.id, + "taxes_id": False, + "supplier_taxes_id": [(6, 0, self.tax_purchase2.ids)], + } + ) + self.assertFalse(product.taxes_id, False) + self.assertEqual(product.supplier_taxes_id, self.tax_purchase2)