From c1aefca24cf851877d743778c0456f88c043f59a Mon Sep 17 00:00:00 2001 From: Thierry Ducrest Date: Thu, 29 Apr 2021 05:49:16 +0200 Subject: [PATCH 01/26] [13.0][ADD] product_variant_change_attribute_value With standard Odoo there is no way to change the attribute values assigned to a product variant. This module adds a wizard that can be access through the product variant tree view context action menu. The wizard allows to change or remove attribute values on all product variant selected. When an attribute value is removed from all variant of a template, it will also be removed from the configuration of the related product template. --- .../__init__.py | 2 + .../__manifest__.py | 14 ++ .../readme/CONTRIBUTORS.rst | 1 + .../readme/DESCRIPTION.rst | 8 + .../readme/USAGE.rst | 2 + .../tests/__init__.py | 0 ..._product_variant_change_attribute_value.py | 174 ++++++++++++++++++ .../wizards/__init__.py | 2 + .../product_variant_attribute_value_action.py | 40 ++++ .../product_variant_attribute_value_wizard.py | 144 +++++++++++++++ ...product_variant_attribute_value_wizard.xml | 88 +++++++++ 11 files changed, 475 insertions(+) create mode 100644 product_variant_change_attribute_value/__init__.py create mode 100644 product_variant_change_attribute_value/__manifest__.py create mode 100644 product_variant_change_attribute_value/readme/CONTRIBUTORS.rst create mode 100644 product_variant_change_attribute_value/readme/DESCRIPTION.rst create mode 100644 product_variant_change_attribute_value/readme/USAGE.rst create mode 100644 product_variant_change_attribute_value/tests/__init__.py create mode 100644 product_variant_change_attribute_value/tests/test_product_variant_change_attribute_value.py create mode 100644 product_variant_change_attribute_value/wizards/__init__.py create mode 100644 product_variant_change_attribute_value/wizards/product_variant_attribute_value_action.py create mode 100644 product_variant_change_attribute_value/wizards/product_variant_attribute_value_wizard.py create mode 100644 product_variant_change_attribute_value/wizards/product_variant_attribute_value_wizard.xml diff --git a/product_variant_change_attribute_value/__init__.py b/product_variant_change_attribute_value/__init__.py new file mode 100644 index 000000000..14444df3d --- /dev/null +++ b/product_variant_change_attribute_value/__init__.py @@ -0,0 +1,2 @@ +from . import tests +from . import wizards diff --git a/product_variant_change_attribute_value/__manifest__.py b/product_variant_change_attribute_value/__manifest__.py new file mode 100644 index 000000000..764d692f0 --- /dev/null +++ b/product_variant_change_attribute_value/__manifest__.py @@ -0,0 +1,14 @@ +# Copyright 2021 Camptocamp SA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html) + +{ + "name": "Product Variant Change Attribute Value", + "version": "13.0.1.0.0", + "author": "Camptocamp SA, Odoo Community Association (OCA)", + "website": "https://github.com/OCA/product-variant", + "license": "AGPL-3", + "category": "Product Variant", + "depends": ["product"], + "data": ["wizards/product_variant_attribute_value_wizard.xml"], + "installable": True, +} diff --git a/product_variant_change_attribute_value/readme/CONTRIBUTORS.rst b/product_variant_change_attribute_value/readme/CONTRIBUTORS.rst new file mode 100644 index 000000000..0dd376fae --- /dev/null +++ b/product_variant_change_attribute_value/readme/CONTRIBUTORS.rst @@ -0,0 +1 @@ +* Thierry Ducrest diff --git a/product_variant_change_attribute_value/readme/DESCRIPTION.rst b/product_variant_change_attribute_value/readme/DESCRIPTION.rst new file mode 100644 index 000000000..25c318ce2 --- /dev/null +++ b/product_variant_change_attribute_value/readme/DESCRIPTION.rst @@ -0,0 +1,8 @@ +In standard Odoo there is no way to change the attribute values assigned +to a product variant. + +This module adds a wizard to change or remove attribute values on all product +variant selected. + +When an attribute value is removed from all variants of a template, +it will also be removed from the configuration of the template. diff --git a/product_variant_change_attribute_value/readme/USAGE.rst b/product_variant_change_attribute_value/readme/USAGE.rst new file mode 100644 index 000000000..1cbca34d0 --- /dev/null +++ b/product_variant_change_attribute_value/readme/USAGE.rst @@ -0,0 +1,2 @@ +The wizard can be access from the product variant tree view through +the context action menu `Change attribute values assigned` diff --git a/product_variant_change_attribute_value/tests/__init__.py b/product_variant_change_attribute_value/tests/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/product_variant_change_attribute_value/tests/test_product_variant_change_attribute_value.py b/product_variant_change_attribute_value/tests/test_product_variant_change_attribute_value.py new file mode 100644 index 000000000..1aac4e500 --- /dev/null +++ b/product_variant_change_attribute_value/tests/test_product_variant_change_attribute_value.py @@ -0,0 +1,174 @@ +# Copyright 2021 Camptocamp SA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html) + +from odoo.tests import common + + +class TestProductVariantChangeAttributeValue(common.SavepointCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.env = cls.env(context=dict(cls.env.context, tracking_disable=True)) + + cls.legs = cls.env.ref("product.product_attribute_1") + cls.steel = cls.env.ref("product.product_attribute_value_1") + cls.aluminium = cls.env.ref("product.product_attribute_value_2") + + cls.color = cls.env.ref("product.product_attribute_2") + cls.white = cls.env.ref("product.product_attribute_value_3") + cls.black = cls.env.ref("product.product_attribute_value_4") + cls.pink = cls.env["product.attribute.value"].create( + {"name": "Pink", "attribute_id": cls.color.id} + ) + cls.blue = cls.env["product.attribute.value"].create( + {"name": "Blue", "attribute_id": cls.color.id} + ) + + cls.variant_1 = cls.env.ref("product.product_product_4") + cls.variant_2 = cls.env.ref("product.product_product_4b") + cls.variant_3 = cls.env.ref("product.product_product_4c") + cls.variant_4 = cls.env.ref("product.product_product_4d") + cls.variants = [ + cls.variant_1.id, + cls.variant_2.id, + cls.variant_3.id, + cls.variant_4.id, + ] + cls.template = cls.variant_1.product_tmpl_id + assert len(cls.template.product_variant_ids) == 4 + + cls.wizard = cls.env["variant.attribute.value.wizard"] + + def _change_action(self, wiz, value, action, replaced_by_id=False): + "Set an action to do by the wizard on an attribute value." + actions = wiz.attributes_action_ids + action_id = actions.filtered(lambda r: r.product_attribute_value_id == value) + action_id.attribute_action = action + action_id.replaced_by_id = replaced_by_id + + def is_value_on_variant(self, variant, attribute_value): + values = variant.product_template_attribute_value_ids.mapped( + "product_attribute_value_id" + ) + return attribute_value in values + + def _is_attribute_value_on_template(self, product, attribute_value): + """Check if an attribute value is assigned to a variant template.""" + template = product.product_tmpl_id + attribute = attribute_value.attribute_id + attribute_line = template.attribute_line_ids.filtered( + lambda l: l.attribute_id == attribute + ) + if not attribute_line: + return False + ptav = self.env["product.template.attribute.value"].search( + [ + ("attribute_line_id", "=", attribute_line.id), + ("product_attribute_value_id", "=", attribute_value.id), + ], + limit=1, + ) + if not ptav: + return False + # Check that it is also active + return ptav.ptav_active + + def test_remove_attribute_value(self): + """Check removing an attribute value on ALL variants of a template.""" + self.assertTrue(self.is_value_on_variant(self.variant_1, self.steel)) + + wiz = self.wizard.with_context(default_res_ids=self.variants).create({}) + self._change_action(wiz, self.steel, "delete") + wiz.action_change_attributes() + + self.assertFalse(self.is_value_on_variant(self.variant_1, self.steel)) + self.assertFalse( + self._is_attribute_value_on_template(self.variant_1, self.steel) + ) + + def test_change_attribure_value(self): + """Check changing an attribute value on ALL variant of a template.""" + self.assertTrue(self.is_value_on_variant(self.variant_1, self.white)) + + wiz = self.wizard.with_context(default_res_ids=self.variants).create({}) + self._change_action(wiz, self.white, "replace", self.pink) + wiz.action_change_attributes() + + self.assertFalse(self.is_value_on_variant(self.variant_1, self.white)) + self.assertTrue(self.is_value_on_variant(self.variant_1, self.pink)) + # White has been removed from the template + self.assertFalse( + self._is_attribute_value_on_template(self.variant_1, self.white) + ) + + def test_change_attribute_value_2(self): + """Check changing an attribute value on some variant of a template. + + Changing the value white to pink on variant 3 and 4. + """ + self.assertTrue(self.is_value_on_variant(self.variant_3, self.white)) + self.assertFalse(self.is_value_on_variant(self.variant_4, self.white)) + # Variant 1 has the white attribute but is is not picked by the wizard + self.assertTrue(self.is_value_on_variant(self.variant_1, self.white)) + + wiz = self.wizard.with_context( + default_res_ids=[self.variant_3.id, self.variant_4.id] + ).create({}) + self._change_action(wiz, self.white, "replace", self.pink) + wiz.action_change_attributes() + + self.assertFalse(self.is_value_on_variant(self.variant_3, self.white)) + self.assertFalse(self.is_value_on_variant(self.variant_4, self.white)) + self.assertTrue(self.is_value_on_variant(self.variant_3, self.pink)) + self.assertFalse(self.is_value_on_variant(self.variant_4, self.pink)) + # The value should not be remove from the template because of variant 1 + self.assertTrue( + self._is_attribute_value_on_template(self.variant_1, self.white) + ) + self.assertTrue(self.is_value_on_variant(self.variant_1, self.white)) + + def test_active_deactivate_attribute_value_2_step(self): + """ Deactivate a pav and reactivate it in 2 steps. + + Use the wizard to desactivate (not used anymore) the white attribute + And reactivate it by using it on another variant. + + """ + self.assertTrue( + self._is_attribute_value_on_template(self.variant_1, self.white) + ) + self.assertTrue( + self._is_attribute_value_on_template(self.variant_1, self.black) + ) + wiz = self.wizard.with_context(default_res_ids=self.variants).create({}) + self._change_action(wiz, self.white, "replace", self.pink) + wiz.action_change_attributes() + self.assertFalse( + self._is_attribute_value_on_template(self.variant_1, self.white) + ) + self._change_action(wiz, self.black, "replace", self.white) + wiz.action_change_attributes() + self.assertTrue( + self._is_attribute_value_on_template(self.variant_1, self.white) + ) + self.assertFalse( + self._is_attribute_value_on_template(self.variant_1, self.black) + ) + + def test_active_deactivate_attribute_value_1_step(self): + """ Deactivate a pav and reactivate it in 1 steps. + + Same than previous tests but both replacement are done in one + execution of the wizard. + + """ + wiz = self.wizard.with_context(default_res_ids=self.variants).create({}) + self._change_action(wiz, self.white, "replace", self.pink) + self._change_action(wiz, self.black, "replace", self.white) + wiz.action_change_attributes() + self.assertTrue( + self._is_attribute_value_on_template(self.variant_1, self.white) + ) + self.assertFalse( + self._is_attribute_value_on_template(self.variant_1, self.black) + ) diff --git a/product_variant_change_attribute_value/wizards/__init__.py b/product_variant_change_attribute_value/wizards/__init__.py new file mode 100644 index 000000000..5b221e5ce --- /dev/null +++ b/product_variant_change_attribute_value/wizards/__init__.py @@ -0,0 +1,2 @@ +from . import product_variant_attribute_value_action +from . import product_variant_attribute_value_wizard diff --git a/product_variant_change_attribute_value/wizards/product_variant_attribute_value_action.py b/product_variant_change_attribute_value/wizards/product_variant_attribute_value_action.py new file mode 100644 index 000000000..9010effe2 --- /dev/null +++ b/product_variant_change_attribute_value/wizards/product_variant_attribute_value_action.py @@ -0,0 +1,40 @@ +# Copyright 2021 Camptocamp SA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html) + +from odoo import api, fields, models + + +class ProductVariantAttributeValueAction(models.TransientModel): + _name = "variant.attribute.value.action" + _description = "Wizard action to do on variant attribute value" + + def _get_attribute_action_list(self): + return [ + ("delete", "Delete"), + ("replace", "Replace"), + ("do_nothing", "Do Nothing"), + ] + + product_attribute_value_id = fields.Many2one("product.attribute.value",) + attribute_action = fields.Selection( + selection="_get_attribute_action_list", default="do_nothing", required=True, + ) + attribute_id = fields.Many2one( + "product.attribute", + related="product_attribute_value_id.attribute_id", + readonly=True, + ) + selectable_attribute_value_ids = fields.Many2many( + "product.attribute.value", compute="_compute_selectable_attribute_value_ids" + ) + replaced_by_id = fields.Many2one( + "product.attribute.value", + string="Replace with", + domain="[('id', 'in', selectable_attribute_value_ids)]", + ) + + @api.depends("attribute_action") + def _compute_selectable_attribute_value_ids(self): + for rec in self: + attribute_ids = self.attribute_id.value_ids.ids + rec.selectable_attribute_value_ids = [(6, 0, attribute_ids)] diff --git a/product_variant_change_attribute_value/wizards/product_variant_attribute_value_wizard.py b/product_variant_change_attribute_value/wizards/product_variant_attribute_value_wizard.py new file mode 100644 index 000000000..0fea00a99 --- /dev/null +++ b/product_variant_change_attribute_value/wizards/product_variant_attribute_value_wizard.py @@ -0,0 +1,144 @@ +# Copyright 2021 Camptocamp SA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html) + +from odoo import api, fields, models + + +class VariantAttributeValueWizard(models.TransientModel): + _name = "variant.attribute.value.wizard" + _description = "Wizard to change attributes on product variants" + + product_ids = fields.Many2many( + "product.product", default=lambda self: self._default_product_ids() + ) + product_variant_count = fields.Integer(compute="_compute_count") + product_template_count = fields.Integer(compute="_compute_count") + attributes_action_ids = fields.Many2many( + "variant.attribute.value.action", + relation="variant_attribute_wizard_attribute_action_rel", + default=lambda self: self._default_attributes_action_ids(), + ) + + def _default_product_ids(self): + return self.env["product.product"].browse(self._context.get("default_res_ids")) + + def _default_attributes_action_ids(self): + p = self._default_product_ids() + links = p.product_template_attribute_value_ids + attribute_ids = links.product_attribute_value_id + return [ + ( + 0, + 0, + { + "product_attribute_value_id": x.id, + "attribute_id": x.attribute_id, + "attribute_action": "do_nothing", + }, + ) + for x in attribute_ids + ] + + @api.depends("product_ids") + def _compute_count(self): + for record in self: + record.product_variant_count = len(record.product_ids) + record.product_template_count = len( + record.product_ids.mapped("product_tmpl_id") + ) + + def action_change_attributes(self): + for product in self.product_ids: + self.update_variant_value(product) + + def _is_attribute_value_being_used(self, variant_id, attribute_value): + """Check if attribute value is still used by a variant.""" + existing_variants = self.env["product.product"].search( + [ + ("id", "!=", variant_id.id), + ("product_tmpl_id", "=", variant_id.product_tmpl_id.id), + ], + ) + existing_attributes = existing_variants.mapped( + "product_template_attribute_value_ids.product_attribute_value_id" + ) + return attribute_value in existing_attributes + + def update_variant_value(self, product_id): + """Update a variant with all the actions set by the user in the wizard.""" + TplAttrLine = self.env["product.template.attribute.line"] + TplAttrValue = self.env["product.template.attribute.value"] + template = product_id.product_tmpl_id + pav_ids = product_id.product_template_attribute_value_ids.mapped( + "product_attribute_value_id" + ) + for value_action in self.attributes_action_ids: + action = value_action.attribute_action + if action == "do_nothing": + continue + pav = value_action.product_attribute_value_id + if pav not in pav_ids: + continue + pav_replacement = value_action.replaced_by_id + if action == "replace" and not pav_replacement: + continue + elif action == "delete": + pass + elif action == "replace" and pav_replacement: + # Find corresponding attribute line on template or create it + attr = pav_replacement.attribute_id + tpl_attr_line = template.attribute_line_ids.filtered( + lambda l: l.attribute_id == attr + ) + if not tpl_attr_line: + tpl_attr_line = TplAttrLine.create( + { + "product_tmpl_id": template.id, + "attribute_id": attr.id, + "value_ids": [(6, False, [pav_replacement.id])], + } + ) + # Ensure the value exists in this attribute line. + # The context key 'update_product_template_attribute_values' avoids + # to create/unlink variants when values are updated on the template + # attribute line. + tpl_attr_line.with_context( + update_product_template_attribute_values=False + ).write({"value_ids": [(4, pav_replacement.id)]}) + # Get (or create if needed) the 'product.template.attribute.value' + tpl_attr_value = TplAttrValue.search( + [ + ("attribute_line_id", "=", tpl_attr_line.id), + ("product_attribute_value_id", "=", pav_replacement.id), + ] + ) + if not tpl_attr_value: + tpl_attr_value = TplAttrValue.create( + { + "attribute_line_id": tpl_attr_line.id, + "product_attribute_value_id": pav_replacement.id, + } + ) + # Update the values set on the product variant + ptav_ids = product_id.product_template_attribute_value_ids.filtered( + lambda r: r.product_attribute_value_id != pav + ) + if action == "replace": + ptav_ids |= tpl_attr_value + product_id.product_template_attribute_value_ids = ptav_ids + # Remove the changed value from the template attribute line if needed + if not self._is_attribute_value_being_used(product_id, pav): + tpl_attr_line = template.attribute_line_ids.filtered( + lambda l: l.attribute_id == pav.attribute_id + ) + tpl_attr_line.with_context( + update_product_template_attribute_values=False + ).write({"value_ids": [(3, pav.id)]}) + tpl_attr_value = TplAttrValue.search( + [ + ("attribute_line_id", "=", tpl_attr_line.id), + ("product_attribute_value_id", "=", pav.id), + ] + ) + if tpl_attr_value: + tpl_attr_value.unlink() diff --git a/product_variant_change_attribute_value/wizards/product_variant_attribute_value_wizard.xml b/product_variant_change_attribute_value/wizards/product_variant_attribute_value_wizard.xml new file mode 100644 index 000000000..fd532e871 --- /dev/null +++ b/product_variant_change_attribute_value/wizards/product_variant_attribute_value_wizard.xml @@ -0,0 +1,88 @@ + + + + Product Variant Update Attribute + variant.attribute.value.wizard + +
+ +
Updating variant(s) selected out of product template(s).
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+
+
+ +
From 281e3be8d16f7cc584fc11a16e0186f0cb109516 Mon Sep 17 00:00:00 2001 From: Simone Orsi Date: Thu, 5 Aug 2021 16:29:07 +0200 Subject: [PATCH 02/26] product_variant_change_attribute_value: cleanup --- ..._product_variant_change_attribute_value.py | 12 ++++----- .../product_variant_attribute_value_action.py | 25 ++++++++++--------- .../product_variant_attribute_value_wizard.py | 18 ++++++------- ...product_variant_attribute_value_wizard.xml | 4 +-- 4 files changed, 30 insertions(+), 29 deletions(-) diff --git a/product_variant_change_attribute_value/tests/test_product_variant_change_attribute_value.py b/product_variant_change_attribute_value/tests/test_product_variant_change_attribute_value.py index 1aac4e500..021587b7d 100644 --- a/product_variant_change_attribute_value/tests/test_product_variant_change_attribute_value.py +++ b/product_variant_change_attribute_value/tests/test_product_variant_change_attribute_value.py @@ -79,7 +79,7 @@ def test_remove_attribute_value(self): wiz = self.wizard.with_context(default_res_ids=self.variants).create({}) self._change_action(wiz, self.steel, "delete") - wiz.action_change_attributes() + wiz.action_apply() self.assertFalse(self.is_value_on_variant(self.variant_1, self.steel)) self.assertFalse( @@ -92,7 +92,7 @@ def test_change_attribure_value(self): wiz = self.wizard.with_context(default_res_ids=self.variants).create({}) self._change_action(wiz, self.white, "replace", self.pink) - wiz.action_change_attributes() + wiz.action_apply() self.assertFalse(self.is_value_on_variant(self.variant_1, self.white)) self.assertTrue(self.is_value_on_variant(self.variant_1, self.pink)) @@ -115,7 +115,7 @@ def test_change_attribute_value_2(self): default_res_ids=[self.variant_3.id, self.variant_4.id] ).create({}) self._change_action(wiz, self.white, "replace", self.pink) - wiz.action_change_attributes() + wiz.action_apply() self.assertFalse(self.is_value_on_variant(self.variant_3, self.white)) self.assertFalse(self.is_value_on_variant(self.variant_4, self.white)) @@ -142,12 +142,12 @@ def test_active_deactivate_attribute_value_2_step(self): ) wiz = self.wizard.with_context(default_res_ids=self.variants).create({}) self._change_action(wiz, self.white, "replace", self.pink) - wiz.action_change_attributes() + wiz.action_apply() self.assertFalse( self._is_attribute_value_on_template(self.variant_1, self.white) ) self._change_action(wiz, self.black, "replace", self.white) - wiz.action_change_attributes() + wiz.action_apply() self.assertTrue( self._is_attribute_value_on_template(self.variant_1, self.white) ) @@ -165,7 +165,7 @@ def test_active_deactivate_attribute_value_1_step(self): wiz = self.wizard.with_context(default_res_ids=self.variants).create({}) self._change_action(wiz, self.white, "replace", self.pink) self._change_action(wiz, self.black, "replace", self.white) - wiz.action_change_attributes() + wiz.action_apply() self.assertTrue( self._is_attribute_value_on_template(self.variant_1, self.white) ) diff --git a/product_variant_change_attribute_value/wizards/product_variant_attribute_value_action.py b/product_variant_change_attribute_value/wizards/product_variant_attribute_value_action.py index 9010effe2..d35b0c0d0 100644 --- a/product_variant_change_attribute_value/wizards/product_variant_attribute_value_action.py +++ b/product_variant_change_attribute_value/wizards/product_variant_attribute_value_action.py @@ -8,31 +8,32 @@ class ProductVariantAttributeValueAction(models.TransientModel): _name = "variant.attribute.value.action" _description = "Wizard action to do on variant attribute value" - def _get_attribute_action_list(self): - return [ - ("delete", "Delete"), - ("replace", "Replace"), - ("do_nothing", "Do Nothing"), - ] - - product_attribute_value_id = fields.Many2one("product.attribute.value",) + product_attribute_value_id = fields.Many2one(comodel_name="product.attribute.value") attribute_action = fields.Selection( - selection="_get_attribute_action_list", default="do_nothing", required=True, + selection="_selection_action", default="do_nothing", required=True, ) attribute_id = fields.Many2one( - "product.attribute", + comodel_name="product.attribute", related="product_attribute_value_id.attribute_id", readonly=True, ) selectable_attribute_value_ids = fields.Many2many( - "product.attribute.value", compute="_compute_selectable_attribute_value_ids" + comodel_name="product.attribute.value", + compute="_compute_selectable_attribute_value_ids", ) replaced_by_id = fields.Many2one( - "product.attribute.value", + comodel_name="product.attribute.value", string="Replace with", domain="[('id', 'in', selectable_attribute_value_ids)]", ) + def _selection_action(self): + return [ + ("do_nothing", "Do Nothing"), + ("replace", "Replace"), + ("delete", "Delete"), + ] + @api.depends("attribute_action") def _compute_selectable_attribute_value_ids(self): for rec in self: diff --git a/product_variant_change_attribute_value/wizards/product_variant_attribute_value_wizard.py b/product_variant_change_attribute_value/wizards/product_variant_attribute_value_wizard.py index 0fea00a99..f0c15e868 100644 --- a/product_variant_change_attribute_value/wizards/product_variant_attribute_value_wizard.py +++ b/product_variant_change_attribute_value/wizards/product_variant_attribute_value_wizard.py @@ -14,7 +14,7 @@ class VariantAttributeValueWizard(models.TransientModel): product_variant_count = fields.Integer(compute="_compute_count") product_template_count = fields.Integer(compute="_compute_count") attributes_action_ids = fields.Many2many( - "variant.attribute.value.action", + comodel_name="variant.attribute.value.action", relation="variant_attribute_wizard_attribute_action_rel", default=lambda self: self._default_attributes_action_ids(), ) @@ -47,9 +47,9 @@ def _compute_count(self): record.product_ids.mapped("product_tmpl_id") ) - def action_change_attributes(self): + def action_apply(self): for product in self.product_ids: - self.update_variant_value(product) + self._action_apply(product) def _is_attribute_value_being_used(self, variant_id, attribute_value): """Check if attribute value is still used by a variant.""" @@ -64,12 +64,12 @@ def _is_attribute_value_being_used(self, variant_id, attribute_value): ) return attribute_value in existing_attributes - def update_variant_value(self, product_id): + def _action_apply(self, product): """Update a variant with all the actions set by the user in the wizard.""" TplAttrLine = self.env["product.template.attribute.line"] TplAttrValue = self.env["product.template.attribute.value"] - template = product_id.product_tmpl_id - pav_ids = product_id.product_template_attribute_value_ids.mapped( + template = product.product_tmpl_id + pav_ids = product.product_template_attribute_value_ids.mapped( "product_attribute_value_id" ) for value_action in self.attributes_action_ids: @@ -120,14 +120,14 @@ def update_variant_value(self, product_id): } ) # Update the values set on the product variant - ptav_ids = product_id.product_template_attribute_value_ids.filtered( + ptav_ids = product.product_template_attribute_value_ids.filtered( lambda r: r.product_attribute_value_id != pav ) if action == "replace": ptav_ids |= tpl_attr_value - product_id.product_template_attribute_value_ids = ptav_ids + product.product_template_attribute_value_ids = ptav_ids # Remove the changed value from the template attribute line if needed - if not self._is_attribute_value_being_used(product_id, pav): + if not self._is_attribute_value_being_used(product, pav): tpl_attr_line = template.attribute_line_ids.filtered( lambda l: l.attribute_id == pav.attribute_id ) diff --git a/product_variant_change_attribute_value/wizards/product_variant_attribute_value_wizard.xml b/product_variant_change_attribute_value/wizards/product_variant_attribute_value_wizard.xml index fd532e871..de9d28f80 100644 --- a/product_variant_change_attribute_value/wizards/product_variant_attribute_value_wizard.xml +++ b/product_variant_change_attribute_value/wizards/product_variant_attribute_value_wizard.xml @@ -60,8 +60,8 @@