From e8a01ddc8870702e62cc23b7efce53ae42fe04dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Theys?= Date: Wed, 9 Jan 2019 11:46:48 +0000 Subject: [PATCH] [FIX] product,sale,website_sale: fix currency conversion of product MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In commit f8dad1bb4b712d8f34035420ef1f6e484c638f5b, we tried to fix the currency conversion of products by always using the currency of the product as the base. This is still correct. However, the conversion method also needs to know the company (to find the correct currency rate), and it was assumed the company was also always set on the product. This is of course not correct when using multi-company. In this commit we fix that issue by falling back if no company is set on the product. The order of the fallback is: product website company, given website company, given pricelist company, current user company. closes #29905 closes #29946 closes #30046 Co-authored-by: Sébastien Theys Co-authored-by: Wolfgang Taferner --- addons/product/models/product_template.py | 30 +++++++++++++++++++ addons/sale/models/product_template.py | 10 ++++++- ...est_sale_product_attribute_value_config.py | 5 ++++ addons/website_sale/controllers/main.py | 7 +++-- addons/website_sale/models/product.py | 8 +++++ 5 files changed, 56 insertions(+), 4 deletions(-) diff --git a/addons/product/models/product_template.py b/addons/product/models/product_template.py index 7c4bb62ddd3..bf0b9786623 100644 --- a/addons/product/models/product_template.py +++ b/addons/product/models/product_template.py @@ -909,6 +909,36 @@ class ProductTemplate(models.Model): return result combination = combination[:-1] + @api.multi + def _get_current_company(self, **kwargs): + """Get the most appropriate company for this product. + + If the company is set on the product, directly return it. Otherwise, + fallback to a contextual company. + + :param kwargs: kwargs forwarded to the fallback method. + + :return: the most appropriate company for this product + :rtype: recordset of one `res.company` + """ + self.ensure_one() + return self.company_id or self._get_current_company_fallback(**kwargs) + + @api.multi + def _get_current_company_fallback(self, **kwargs): + """Fallback to get the most appropriate company for this product. + + This should only be called from `_get_current_company` but is defined + separately to allow override. + + The final fallback will be the current user's company. + + :return: the fallback company for this product + :rtype: recordset of one `res.company` + """ + self.ensure_one() + return self.env.user.company_id + @api.model def get_empty_list_help(self, help): self = self.with_context( diff --git a/addons/sale/models/product_template.py b/addons/sale/models/product_template.py index 8daa24e9f37..b0cd6345784 100644 --- a/addons/sale/models/product_template.py +++ b/addons/sale/models/product_template.py @@ -258,7 +258,7 @@ class ProductTemplate(models.Model): if pricelist and pricelist.currency_id != product_template.currency_id: list_price = product_template.currency_id._convert( - list_price, pricelist.currency_id, product_template.company_id, + list_price, pricelist.currency_id, product_template._get_current_company(pricelist=pricelist), fields.Date.today() ) @@ -293,3 +293,11 @@ class ProductTemplate(models.Model): return False combination = self._get_first_possible_combination(parent_combination) return True if combination else self._is_combination_possible(combination, parent_combination) + + @api.multi + def _get_current_company_fallback(self, **kwargs): + """Override: if a pricelist is given, fallback to the company of the + pricelist if it is set, otherwise use the one from parent method.""" + res = super(ProductTemplate, self)._get_current_company_fallback(**kwargs) + pricelist = kwargs.get('pricelist') + return pricelist and pricelist.company_id or res diff --git a/addons/sale/tests/test_sale_product_attribute_value_config.py b/addons/sale/tests/test_sale_product_attribute_value_config.py index dd36a916778..95b1814f50e 100644 --- a/addons/sale/tests/test_sale_product_attribute_value_config.py +++ b/addons/sale/tests/test_sale_product_attribute_value_config.py @@ -185,6 +185,11 @@ class TestSaleProductAttributeValueConfig(TestSaleProductAttributeValueSetup): do_test(self) def test_02_get_combination_info(self): + # If using multi-company, company_id will be False, and this code should + # still work. + # The case with a company_id will be implicitly tested on website_sale. + self.computer.company_id = False + computer_ssd_256 = self._get_product_template_attribute_value(self.ssd_256) computer_ram_8 = self._get_product_template_attribute_value(self.ram_8) computer_hdd_1 = self._get_product_template_attribute_value(self.hdd_1) diff --git a/addons/website_sale/controllers/main.py b/addons/website_sale/controllers/main.py index aec6d3b17ed..80b50fa61db 100644 --- a/addons/website_sale/controllers/main.py +++ b/addons/website_sale/controllers/main.py @@ -150,9 +150,10 @@ class WebsiteSale(ProductConfiguratorController): return pricelist_context, pricelist def _get_compute_currency(self, pricelist, product=None): - from_currency = (product or request.env.user.company_id).currency_id + company = product and product._get_current_company(pricelist=pricelist, website=request.website) or pricelist.company_id or request.website.company_id + from_currency = (product or company).currency_id to_currency = pricelist.currency_id - return lambda price: from_currency._convert(price, to_currency, (product or request.env.user).company_id, fields.Date.today()) + return lambda price: from_currency._convert(price, to_currency, company, fields.Date.today()) def _get_search_order(self, post): # OrderBy will be parsed in orm and so no direct sql injection @@ -312,7 +313,7 @@ class WebsiteSale(ProductConfiguratorController): pricelist = request.website.get_current_pricelist() def compute_currency(price): - return product.currency_id._convert(price, pricelist.currency_id, product.company_id, fields.Date.today()) + return product.currency_id._convert(price, pricelist.currency_id, product._get_current_company(pricelist=pricelist, website=request.website), fields.Date.today()) if not product_context.get('pricelist'): product_context['pricelist'] = pricelist.id diff --git a/addons/website_sale/models/product.py b/addons/website_sale/models/product.py index db7d9d9cd96..db0ac30f060 100644 --- a/addons/website_sale/models/product.py +++ b/addons/website_sale/models/product.py @@ -286,6 +286,14 @@ class ProductTemplate(models.Model): """ return self._create_product_variant(self._get_first_possible_combination(), log_warning) + @api.multi + def _get_current_company_fallback(self, **kwargs): + """Override: if a website is set on the product or given, fallback to + the company of the website. Otherwise use the one from parent method.""" + res = super(ProductTemplate, self)._get_current_company_fallback(**kwargs) + website = self.website_id or kwargs.get('website') + return website and website.company_id or res + def _default_website_sequence(self): self._cr.execute("SELECT MIN(website_sequence) FROM %s" % self._table) min_sequence = self._cr.fetchone()[0]