diff --git a/product_configurator/models/product.py b/product_configurator/models/product.py index 5ee8bc80..ce7b828b 100644 --- a/product_configurator/models/product.py +++ b/product_configurator/models/product.py @@ -472,10 +472,8 @@ def name_search(self, name='', args=None, operator='ilike', limit=100): def create_variant_ids(self): """ Prevent configurable products from creating variants as these serve only as a template for the product configurator""" - for product in self: - if self.config_ok: - return None - return super(ProductTemplate, self).create_variant_ids() + regular_templates = self.filtered(lambda t: not t.config_ok) + return super(ProductTemplate, regular_templates).create_variant_ids() @api.multi def unlink(self): @@ -606,3 +604,18 @@ def _compute_name(self): product.config_name = product.get_config_name() else: product.config_name = product.name + + @api.multi + def copy_configurable(self): + """ Creates a new product.variant with the same attributes, needed to + ensure custom_values are correctly recreated and not just pointed + to by the original""" + self.ensure_one() + assert self.config_ok + + attribute_values = self.attribute_value_ids.ids + custom_vals = {v.attribute_id.id: v.value or v.attachment_ids.ids + for v in self.value_custom_ids} + new_product = self.product_tmpl_id.create_variant( + attribute_values, custom_vals) + return new_product diff --git a/product_configurator/models/sale.py b/product_configurator/models/sale.py index 46c95cec..154c88a7 100644 --- a/product_configurator/models/sale.py +++ b/product_configurator/models/sale.py @@ -1,7 +1,6 @@ # -*- coding: utf-8 -*- -from odoo import models, fields - +from odoo import models, fields, api # class sale_order_line_attribute(models.Model): # _name = 'sale.order.line.attribute' @@ -21,3 +20,15 @@ class SaleOrderLine(models.Model): ) product_id = fields.Many2one(domain=[('config_ok', '=', False)]) + + @api.multi + def copy(self, default=None): + """ Ensure when a line is copied, it creates to a new configuration, + not just points to the original. Without this, changing one + configuration changes everywhere it appears. + """ + if default is None: + default = {} + if 'product_id' not in default and self.product_id.config_ok: + default['product_id'] = self.product_id.copy_configurable().id + return super(SaleOrderLine, self).copy(default=default) diff --git a/product_configurator/tests/__init__.py b/product_configurator/tests/__init__.py index 8b1b2889..2ad17519 100644 --- a/product_configurator/tests/__init__.py +++ b/product_configurator/tests/__init__.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- -from . import test_configuration_rules +from . import test_configuration_rules, test_product diff --git a/product_configurator/tests/test_product.py b/product_configurator/tests/test_product.py new file mode 100644 index 00000000..2172b439 --- /dev/null +++ b/product_configurator/tests/test_product.py @@ -0,0 +1,53 @@ +# -*- coding: utf-8 -*- + +from odoo.tests.common import TransactionCase + + +class ProductVariant(TransactionCase): + + def setUp(self): + super(ProductVariant, self).setUp() + self.cfg_tmpl = self.env.ref('product_configurator.bmw_2_series') + + attribute_vals = self.cfg_tmpl.attribute_line_ids.mapped('value_ids') + + self.attr_val_ext_ids = { + v: k for k, v in attribute_vals.get_external_id().iteritems() + } + + def get_attr_val_ids(self, ext_ids): + """Return a list of database ids using the external_ids + passed via ext_ids argument""" + + value_ids = [] + + attr_val_prefix = 'product_configurator.product_attribute_value_%s' + + for ext_id in ext_ids: + if ext_id in self.attr_val_ext_ids: + value_ids.append(self.attr_val_ext_ids[ext_id]) + elif attr_val_prefix % ext_id in self.attr_val_ext_ids: + value_ids.append( + self.attr_val_ext_ids[attr_val_prefix % ext_id] + ) + + return value_ids + + def test_product_create_and_copy(self): + """Test creation and copy of a variant""" + + conf = [ + 'gasoline', '228i', 'model_luxury_line', 'silver', 'rims_384', + 'tapistry_black', 'steptronic', 'smoker_package', 'tow_hook' + ] + + attr_val_ids = self.get_attr_val_ids(conf) + product = self.cfg_tmpl.create_variant(attr_val_ids) + self.assertTrue( + set(attr_val_ids) == set(product.attribute_value_ids.ids), + "Product not created with correct attributes") + product2 = product.copy_configurable() + self.assertTrue( + set(product.attribute_value_ids.ids) == + set(product2.attribute_value_ids.ids), + "Product configuration copy failed")