summaryrefslogtreecommitdiff
path: root/addons/website_sale
diff options
context:
space:
mode:
authorSébastien Theys <seb@odoo.com>2019-08-26 08:38:56 +0000
committerSébastien Theys <seb@odoo.com>2019-08-26 13:01:13 +0000
commit22a11a6f4d0b08a56712fafd0f87a2e7bb5b00ec (patch)
treebb88f34b8bc298c31aa3af5c83f1f393a7f108bc /addons/website_sale
parent191213bca2582ed60c92769a8aa8a17cca0432e6 (diff)
[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
Diffstat (limited to 'addons/website_sale')
-rw-r--r--addons/website_sale/data/demo.xml42
-rw-r--r--addons/website_sale/models/product.py12
-rw-r--r--addons/website_sale/models/sale_order.py11
-rw-r--r--addons/website_sale/tests/test_customize.py6
-rw-r--r--addons/website_sale/tests/test_website_sale_image.py18
5 files changed, 46 insertions, 43 deletions
diff --git a/addons/website_sale/data/demo.xml b/addons/website_sale/data/demo.xml
index dd27704fa7a..3e38aadb212 100644
--- a/addons/website_sale/data/demo.xml
+++ b/addons/website_sale/data/demo.xml
@@ -635,33 +635,45 @@
<field name="public_categ_ids" eval="[(6, 0, [ref('website_sale.services')])]"/>
</record>
- <record id="product_template_attribute_line_1" model="product.template.attribute.line">
+ <record id="product_1_attribute_3_product_template_attribute_line" model="product.template.attribute.line">
<field name="product_tmpl_id" ref="website_sale.product_product_1_product_template"/>
<field name="attribute_id" ref="product.product_attribute_3"/>
<field name="value_ids" eval="[(6,0,[ref('product.product_attribute_value_5'), ref('product.product_attribute_value_6')])]"/>
</record>
- <record id="product_product_1" model="product.product">
- <field name="default_code">SERV_125889</field>
- <field name="product_tmpl_id" ref="website_sale.product_product_1_product_template"/>
- <field name="attribute_value_ids" eval="[(6, 0, [ref('product.product_attribute_value_5')])]"/>
- </record>
- <record id="product_product_1b" model="product.product">
- <field name="default_code">SERV_125890</field>
- <field name="product_tmpl_id" ref="website_sale.product_product_1_product_template"/>
- <field name="attribute_value_ids" eval="[(6, 0, [ref('product.product_attribute_value_6')])]"/>
- </record>
-
<!-- Handle automatically created product.template.attribute.value -->
<function model="ir.model.data" name="_update_xmlids">
<value model="base" eval="[{
- 'xml_id': 'website_sale.product_template_attribute_value_1',
- 'record': obj().env.ref('website_sale.product_template_attribute_line_1').product_template_value_ids[1],
+ 'xml_id': 'website_sale.product_1_attribute_3_value_1',
+ 'record': obj().env.ref('website_sale.product_1_attribute_3_product_template_attribute_line').product_template_value_ids[0],
+ 'noupdate': True,
+ }, {
+ 'xml_id': 'website_sale.product_1_attribute_3_value_2',
+ 'record': obj().env.ref('website_sale.product_1_attribute_3_product_template_attribute_line').product_template_value_ids[1],
'noupdate': True,
}]"/>
</function>
- <record id="product_template_attribute_value_1" model="product.template.attribute.value">
+ <function model="ir.model.data" name="_update_xmlids">
+ <value model="base" eval="[{
+ 'xml_id': 'website_sale.product_product_1',
+ 'record': obj().env.ref('website_sale.product_product_1_product_template')._get_variant_for_combination(obj().env.ref('website_sale.product_1_attribute_3_value_1')),
+ 'noupdate': True,
+ }, {
+ 'xml_id': 'website_sale.product_product_1b',
+ 'record': obj().env.ref('website_sale.product_product_1_product_template')._get_variant_for_combination(obj().env.ref('website_sale.product_1_attribute_3_value_2')),
+ 'noupdate': True,
+ },]"/>
+ </function>
+
+ <record id="product_product_1" model="product.product">
+ <field name="default_code">SERV_125889</field>
+ </record>
+ <record id="product_product_1b" model="product.product">
+ <field name="default_code">SERV_125890</field>
+ </record>
+
+ <record id="website_sale.product_1_attribute_3_value_2" model="product.template.attribute.value">
<field name="price_extra">18.00</field>
</record>
</odoo>
diff --git a/addons/website_sale/models/product.py b/addons/website_sale/models/product.py
index d3f80b639d4..1426237f595 100644
--- a/addons/website_sale/models/product.py
+++ b/addons/website_sale/models/product.py
@@ -202,7 +202,7 @@ class ProductTemplate(models.Model):
:rtype: bool
"""
self.ensure_one()
- return any(a.create_variant == 'no_variant' for a in self.valid_product_attribute_ids)
+ return any(a.create_variant == 'no_variant' for a in self.valid_product_template_attribute_line_ids.attribute_id)
def _has_is_custom_values(self):
self.ensure_one()
@@ -212,7 +212,7 @@ class ProductTemplate(models.Model):
:return: True if at least one is_custom attribute value, False otherwise
:rtype: bool
"""
- return any(v.is_custom for v in self.valid_product_attribute_value_ids)
+ return any(v.is_custom for v in self.valid_product_template_attribute_line_ids.product_template_value_ids._only_active())
def _get_possible_variants_sorted(self, parent_combination=None):
"""Return the sorted recordset of variants that are possible.
@@ -243,9 +243,9 @@ class ProductTemplate(models.Model):
- second level sort: same as "product.attribute.value"._order
"""
keys = []
- for attribute in variant.attribute_value_ids.sorted(_sort_key_attribute_value):
+ for attribute in variant.product_template_attribute_value_ids.sorted(_sort_key_attribute_value):
# if you change this order, keep it in sync with _order from `product.attribute.value`
- keys.append(attribute.sequence)
+ keys.append(attribute.product_attribute_value_id.sequence)
keys.append(attribute.id)
return keys
@@ -402,10 +402,10 @@ class Product(models.Model):
website_url = fields.Char('Website URL', compute='_compute_product_website_url', help='The full URL to access the document through the website.')
- @api.depends('product_tmpl_id.website_url', 'attribute_value_ids')
+ @api.depends('product_tmpl_id.website_url', 'product_template_attribute_value_ids')
def _compute_product_website_url(self):
for product in self:
- attributes = ','.join(str(x) for x in product.attribute_value_ids.ids)
+ attributes = ','.join(str(x) for x in product.product_template_attribute_value_ids.ids)
product.website_url = "%s#attr=%s" % (product.product_tmpl_id.website_url, attributes)
def website_publish_button(self):
diff --git a/addons/website_sale/models/sale_order.py b/addons/website_sale/models/sale_order.py
index fac0ae04b6e..49accd853c1 100644
--- a/addons/website_sale/models/sale_order.py
+++ b/addons/website_sale/models/sale_order.py
@@ -196,8 +196,6 @@ class SaleOrder(models.Model):
for ptav in combination.filtered(lambda ptav: ptav.attribute_id.create_variant == 'no_variant' and ptav not in received_no_variant_values):
no_variant_attribute_values.append({
'value': ptav.id,
- 'attribute_name': ptav.attribute_id.name,
- 'attribute_value_name': ptav.name,
})
# save no_variant attributes values
@@ -208,19 +206,18 @@ class SaleOrder(models.Model):
# add is_custom attribute values that were not received
custom_values = kwargs.get('product_custom_attribute_values') or []
- received_custom_values = product.env['product.attribute.value'].browse([int(ptav['attribute_value_id']) for ptav in custom_values])
+ received_custom_values = product.env['product.template.attribute.value'].browse([int(ptav['custom_product_template_attribute_value_id']) for ptav in custom_values])
- for ptav in combination.filtered(lambda ptav: ptav.is_custom and ptav.product_attribute_value_id not in received_custom_values):
+ for ptav in combination.filtered(lambda ptav: ptav.is_custom and ptav not in received_custom_values):
custom_values.append({
- 'attribute_value_id': ptav.product_attribute_value_id.id,
- 'attribute_value_name': ptav.name,
+ 'custom_product_template_attribute_value_id': ptav.id,
'custom_value': '',
})
# save is_custom attributes values
if custom_values:
values['product_custom_attribute_value_ids'] = [(0, 0, {
- 'attribute_value_id': custom_value['attribute_value_id'],
+ 'custom_product_template_attribute_value_id': custom_value['custom_product_template_attribute_value_id'],
'custom_value': custom_value['custom_value']
}) for custom_value in custom_values]
diff --git a/addons/website_sale/tests/test_customize.py b/addons/website_sale/tests/test_customize.py
index d120d7f6651..c306639eec4 100644
--- a/addons/website_sale/tests/test_customize.py
+++ b/addons/website_sale/tests/test_customize.py
@@ -39,8 +39,6 @@ class TestUi(odoo.tests.HttpCase):
else:
ptav.price_extra = 50.4
- product_template.create_variant_ids()
-
def test_01_admin_shop_customize_tour(self):
self.start_tour("/", 'shop_customize', login="admin")
@@ -165,8 +163,6 @@ class TestUi(odoo.tests.HttpCase):
product_template_attribute_values[1].price_extra = 20
product_template_attribute_values[2].price_extra = 30
- product_template.create_variant_ids()
-
# archive first combination (first variant)
product_template.product_variant_ids[0].active = False
# delete second combination (which is now first variant since cache has been cleared)
@@ -209,8 +205,6 @@ class TestUi(odoo.tests.HttpCase):
# set a price on the value
ptal.product_template_value_ids.price_extra = 10
- product_template.create_variant_ids()
-
self.start_tour("/", 'tour_shop_no_variant_attribute', login="demo")
def test_06_admin_list_view_b2c(self):
diff --git a/addons/website_sale/tests/test_website_sale_image.py b/addons/website_sale/tests/test_website_sale_image.py
index ad6fcb15300..bda48819247 100644
--- a/addons/website_sale/tests/test_website_sale_image.py
+++ b/addons/website_sale/tests/test_website_sale_image.py
@@ -93,11 +93,13 @@ class TestWebsiteSaleImage(odoo.tests.HttpCase):
})
# set the color attribute and values on the template
- self.env['product.template.attribute.line'].create([{
+ line = self.env['product.template.attribute.line'].create([{
'attribute_id': product_attribute.id,
'product_tmpl_id': template.id,
'value_ids': [(6, 0, attr_values.ids)]
}])
+ value_red = line.product_template_value_ids[0]
+ value_green = line.product_template_value_ids[1]
# set a different price on the variants to differentiate them
product_template_attribute_values = self.env['product.template.attribute.value'].search([('product_tmpl_id', '=', template.id)])
@@ -108,22 +110,20 @@ class TestWebsiteSaleImage(odoo.tests.HttpCase):
else:
val.price_extra = 20
- # Create RED variant, and set image to blue (will be set on the template
+ # Get RED variant, and set image to blue (will be set on the template
# because the template image is empty and there is only one variant)
- product_red = self.env['product.product'].create({
- 'product_tmpl_id': template.id,
+ product_red = template._get_variant_for_combination(value_red)
+ product_red.write({
'image_1920': blue_image,
- 'attribute_value_ids': [(6, 0, attr_values.filtered(lambda l: l.name == name_red).ids)],
'product_variant_image_ids': [(0, 0, {'name': 'image 2', 'image_1920': image_bmp})],
})
self.assertEqual(template.image_1920, blue_image)
- # create the green variant
- product_green = self.env['product.product'].create({
+ # Get the green variant
+ product_green = template._get_variant_for_combination(value_green)
+ product_green.write({
'image_1920': green_image,
- 'product_tmpl_id': template.id,
- 'attribute_value_ids': [(6, 0, attr_values.filtered(lambda l: l.name == name_green).ids)],
'product_variant_image_ids': [(0, 0, {'name': 'image 3', 'image_1920': image_png})],
})