[IMP] product,sale: harmonize/clarify contextual keys for extra prices

Clarify the uses and impacts of current_attributes_price_extra and
no_variant_attributes_price_extra context keys.
Group the key specification and usage.
Provide a clear API shared by templates and variants.

Part-of: odoo/odoo#113234
This commit is contained in:
Victor Feyens
2023-02-21 21:07:34 +01:00
parent 624b2feef1
commit 35e84e096b
4 changed files with 63 additions and 54 deletions
+27 -8
View File
@@ -620,6 +620,31 @@ class ProductProduct(models.Model):
res |= seller
return res.sorted('price')[:1]
def _get_product_price_context(self, combination):
self.ensure_one()
res = {}
# It is possible that a no_variant attribute is still in a variant if
# the type of the attribute has been changed after creation.
no_variant_attributes_price_extra = [
ptav.price_extra for ptav in combination.filtered(
lambda ptav:
ptav.price_extra
and ptav.product_tmpl_id == self.product_tmpl_id
and ptav not in self.product_template_attribute_value_ids
)
]
if no_variant_attributes_price_extra:
res['no_variant_attributes_price_extra'] = tuple(no_variant_attributes_price_extra)
return res
def _get_attributes_extra_price(self):
self.ensure_one()
return self.price_extra + sum(
self.env.context.get('no_variant_attributes_price_extra', []))
def price_compute(self, price_type, uom=None, currency=None, company=None, date=False):
company = company or self.env.company
date = date or fields.Date.context_today(self)
@@ -637,14 +662,8 @@ class ProductProduct(models.Model):
price_currency = product.currency_id
if price_type == 'standard_price':
price_currency = product.cost_currency_id
if price_type == 'list_price':
price += product.price_extra
# we need to add the price from the attributes that do not generate variants
# (see field product.attribute create_variant)
if self._context.get('no_variant_attributes_price_extra'):
# we have a list of price_extra that comes from the attribute values, we need to sum all that
price += sum(self._context.get('no_variant_attributes_price_extra'))
elif price_type == 'list_price':
price += product._get_attributes_extra_price()
if uom:
price = product.uom_id._compute_price(price, uom)
+23 -6
View File
@@ -562,6 +562,27 @@ class ProductTemplate(models.Model):
},
}
def _get_product_price_context(self, combination):
self.ensure_one()
res = {}
current_attributes_price_extra = [
ptav.price_extra for ptav in combination.filtered(
lambda ptav:
ptav.price_extra
and ptav.product_tmpl_id == self
)
]
if current_attributes_price_extra:
res['current_attributes_price_extra'] = tuple(current_attributes_price_extra)
return res
def _get_attributes_extra_price(self):
self.ensure_one()
return sum(self.env.context.get('current_attributes_price_extra', []))
def price_compute(self, price_type, uom=None, currency=None, company=None, date=False):
company = company or self.env.company
date = date or fields.Date.context_today(self)
@@ -579,12 +600,8 @@ class ProductTemplate(models.Model):
price_currency = template.currency_id
if price_type == 'standard_price':
price_currency = template.cost_currency_id
# yes, there can be attribute values for product template if it's not a variant YET
# (see field product.attribute create_variant)
if price_type == 'list_price' and self._context.get('current_attributes_price_extra'):
# we have a list of price_extra that comes from the attribute values, we need to sum all that
price += sum(self._context.get('current_attributes_price_extra'))
elif price_type == 'list_price':
price += template._get_attributes_extra_price()
if uom:
price = template.uom_id._compute_price(price, uom)
+10 -25
View File
@@ -209,8 +209,6 @@ class ProductTemplate(models.Model):
discount applied (price < list_price), else False
"""
self.ensure_one()
# get the name before the change of context to benefit from prefetch
display_name = self.display_name
quantity = self.env.context.get('quantity', add_qty)
product_template = self
@@ -230,38 +228,25 @@ class ProductTemplate(models.Model):
product = product_template._get_variant_for_combination(combination)
if product:
# We need to add the price_extra for the attributes that are not
# in the variant, typically those of type no_variant, but it is
# possible that a no_variant attribute is still in a variant if
# the type of the attribute has been changed after creation.
no_variant_attributes_price_extra = [
ptav.price_extra for ptav in combination.filtered(
lambda ptav:
ptav.price_extra and
ptav not in product.product_template_attribute_value_ids
)
]
if no_variant_attributes_price_extra:
product = product.with_context(
no_variant_attributes_price_extra=tuple(no_variant_attributes_price_extra)
)
list_price = product.price_compute('list_price')[product.id]
price = pricelist._get_product_price(product, quantity)
display_image = bool(product.image_128)
display_name = product.display_name
price_extra = (product.price_extra or 0.0) + (sum(no_variant_attributes_price_extra) or 0.0)
else:
current_attributes_price_extra = [v.price_extra or 0.0 for v in combination]
product_template = product_template.with_context(current_attributes_price_extra=current_attributes_price_extra)
price_extra = sum(current_attributes_price_extra)
list_price = product_template.price_compute('list_price')[product_template.id]
price = pricelist._get_product_price(product_template, quantity)
display_image = bool(product_template.image_128)
display_name = product_template.display_name
combination_name = combination._get_combination_name()
if combination_name:
display_name = "%s (%s)" % (display_name, combination_name)
product_or_template = product or self
price_context = product_or_template._get_product_price_context(combination)
product_or_template = product_or_template.with_context(**price_context)
list_price = product_or_template.price_compute('list_price')[product_or_template.id]
price_extra = product_or_template._get_attributes_extra_price()
price = pricelist._get_product_price(product_or_template, quantity)
currency = pricelist.currency_id or self.env.company.currency_id
if currency != product_template.currency_id:
list_price = product_template.currency_id._convert(
+3 -15
View File
@@ -489,21 +489,9 @@ class SaleOrderLine(models.Model):
:rtype: dict
"""
self.ensure_one()
res = {}
# It is possible that a no_variant attribute is still in a variant if
# the type of the attribute has been changed after creation.
no_variant_attributes_price_extra = [
ptav.price_extra for ptav in self.product_no_variant_attribute_value_ids.filtered(
lambda ptav:
ptav.price_extra and
ptav not in self.product_id.product_template_attribute_value_ids
)
]
if no_variant_attributes_price_extra:
res['no_variant_attributes_price_extra'] = tuple(no_variant_attributes_price_extra)
return res
return self.product_id._get_product_price_context(
self.product_no_variant_attribute_value_ids,
)
def _get_pricelist_price_before_discount(self):
"""Compute the price used as base for the pricelist price computation.