Files
odoo_source/addons/product_matrix/models/product_template.py
T
Sébastien Theys 22a11a6f4d [REF] product, *: save product.template.attribute.value on variants
* account, hr_expense, mrp, sale, sale_product_configurator, stock_account,
  website_sale, website_sale_comparison

Before this commit, the `product.attribute.value` were stored on the variants.
This required filtering of attribute lines to find the appropriate matching
`product.template.attribute.value` that were used in most of the business code,
such as when computing the `price_extra`.

This also prevented to have multiple attribute lines for the same attribute,
which is needed to handle use cases such as grape varieties for wine products.
This will be done in the following commit.

After this commit, the combination of `product.template.attribute.value` will be
directly stored on the product variant.

Other changes
=============

Add `combination_indices` on product, which allows to quickly find a variant
matching a combination (1 simple indexed equality query as opposed to 1 query
with as many joins as there are attribute lines), and to add an easy
SQL constraint to ensure active combination uniqueness.

Add active field on `product.template.attribute.line` and
`product.template.attribute.value`, with the same behavior as variants:

They become archived if they can't be unlinked. This allows to keep the database
consistent, such as archived variants correctly keeping all their values, sales
order lines keeping their custom and no_variant. This is done with the help of
`ondelete=restrict` on the corresponding m2m fields, and the `unlink` methods
falling back to archiving when `unlink` is restricted.

Part of task-1912579

PR: #32946
2019-08-26 13:01:13 +00:00

93 lines
3.6 KiB
Python

# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import itertools
from odoo import models
class ProductTemplate(models.Model):
_inherit = 'product.template'
def _get_template_matrix(self, **kwargs):
self.ensure_one()
company_id = kwargs.get('company_id', None) or self.company_id or self.env.company
currency_id = kwargs.get('currency_id', None) or self.currency_id
display_extra = kwargs.get('display_extra_price', False)
attribute_lines = self.valid_product_template_attribute_line_ids
Attrib = self.env['product.template.attribute.value']
first_line_attributes = attribute_lines[0].product_template_value_ids._only_active()
attribute_ids_by_line = [line.product_template_value_ids._only_active().ids for line in attribute_lines]
header = [{"name": self.display_name}] + [
attr._grid_header_cell(
fro_currency=self.currency_id,
to_currency=currency_id,
company=company_id,
display_extra=display_extra
) for attr in first_line_attributes]
result = [[]]
for pool in attribute_ids_by_line:
result = [x + [y] for y in pool for x in result]
args = [iter(result)] * len(first_line_attributes)
rows = itertools.zip_longest(*args)
matrix = []
for row in rows:
row_attributes = Attrib.browse(row[0][1:])
row_header_cell = row_attributes._grid_header_cell(
fro_currency=self.currency_id,
to_currency=currency_id,
company=company_id,
display_extra=display_extra)
result = [row_header_cell]
for cell in row:
combination = Attrib.browse(cell)
is_possible_combination = self._is_combination_possible(combination)
cell.sort()
result.append({
"ptav_ids": cell,
"qty": 0,
"is_possible_combination": is_possible_combination
})
matrix.append(result)
return {
"header": header,
"matrix": matrix,
}
class ProductTemplateAttributeValue(models.Model):
_inherit = "product.template.attribute.value"
def _grid_header_cell(self, fro_currency, to_currency, company, display_extra=True):
"""Generate a header matrix cell for 1 or multiple attributes.
:param res.currency fro_currency:
:param res.currency to_currency:
:param res.company company:
:param bool display_extra: whether extra prices should be displayed in the cell
True by default, used to avoid showing extra prices on purchases.
:returns: cell with name (and price if any price_extra is defined on self)
:rtype: dict
"""
header_cell = {
'name': ' • '.join([attr.name for attr in self]) if self else " "
} # The " " is to avoid having 'Not available' if the template has only one attribute line.
extra_price = sum(self.mapped('price_extra')) if display_extra else 0
if extra_price:
sign = '+ ' if extra_price > 0 else '- '
header_cell.update({
"price": sign + self.env['ir.qweb.field.monetary'].value_to_html(
extra_price, {
'from_currency': fro_currency,
'display_currency': to_currency,
'company_id': company,
}
)
})
return header_cell