[FIX] product,sale,website_sale: fix currency conversion of product

In commit f8dad1bb4b, 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 <seb@odoo.com>
Co-authored-by: Wolfgang Taferner <wolfgang.taferner@wt-io-it.at>
This commit is contained in:
Sébastien Theys
2019-01-09 16:19:45 +00:00
co-authored by Wolfgang Taferner
parent d73c4e583a
commit e8a01ddc88
5 changed files with 56 additions and 4 deletions
+30
View File
@@ -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(
+9 -1
View File
@@ -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
@@ -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)
+4 -3
View File
@@ -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
+8
View File
@@ -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]