From 67a58e30abfdd626ebbb535a22c2b37d7b4e6790 Mon Sep 17 00:00:00 2001 From: Richard deMeester Date: Tue, 4 Apr 2017 10:55:32 +1000 Subject: [PATCH 1/3] [ENH] - Copy configurable routine added. And change SO line copy so it uses the new copy routine for products. --- product_configurator/models/product.py | 13 +++++++++++++ product_configurator/models/sale.py | 15 +++++++++++++-- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/product_configurator/models/product.py b/product_configurator/models/product.py index 3ceb2915..3e6e4675 100644 --- a/product_configurator/models/product.py +++ b/product_configurator/models/product.py @@ -585,3 +585,16 @@ 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..8f383d13 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 not('product_id') in default and self.product_id.config_ok: + default['product_id'] = self.product_id.copy_configurable().id + return super(SaleOrderLine, self).copy(default=default) From 5c12808ec76e2442c6cac95e96c23f8da137e0d8 Mon Sep 17 00:00:00 2001 From: Richard deMeester Date: Sun, 9 Apr 2017 17:38:41 +1000 Subject: [PATCH 2/3] Fix code for PEP8, and add new tests. --- product_configurator/models/product.py | 18 +++---- product_configurator/models/sale.py | 8 +-- product_configurator/tests/__init__.py | 2 +- product_configurator/tests/test_product.py | 58 ++++++++++++++++++++++ 4 files changed, 72 insertions(+), 14 deletions(-) create mode 100644 product_configurator/tests/test_product.py diff --git a/product_configurator/models/product.py b/product_configurator/models/product.py index 3e6e4675..ff003350 100644 --- a/product_configurator/models/product.py +++ b/product_configurator/models/product.py @@ -451,10 +451,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): @@ -588,13 +586,15 @@ def _compute_name(self): @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 - """ + """ 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) + 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 8f383d13..154c88a7 100644 --- a/product_configurator/models/sale.py +++ b/product_configurator/models/sale.py @@ -23,12 +23,12 @@ class SaleOrderLine(models.Model): @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. + """ 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 not('product_id') in default and self.product_id.config_ok: + 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..ea6e0e37 --- /dev/null +++ b/product_configurator/tests/test_product.py @@ -0,0 +1,58 @@ +# -*- 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(self): + """Test creation 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") + self.product = product + + def test_product_copy(self): + """Test copy of a variant""" + + product2 = self.product.copy_configurable() + self.assertTrue( + set(self.product.attribute_value_ids.ids) == + set(product2.attribute_value_ids.ids), + "Product configuration copy failed") From 2a962bd956f8e013fe29b88b94d1aee1365e9c37 Mon Sep 17 00:00:00 2001 From: Richard deMeester Date: Sun, 9 Apr 2017 17:49:43 +1000 Subject: [PATCH 3/3] Changes from git tests --- product_configurator/models/product.py | 2 +- product_configurator/tests/test_product.py | 13 ++++--------- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/product_configurator/models/product.py b/product_configurator/models/product.py index ff003350..2f65157c 100644 --- a/product_configurator/models/product.py +++ b/product_configurator/models/product.py @@ -596,5 +596,5 @@ def copy_configurable(self): 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) + attribute_values, custom_vals) return new_product diff --git a/product_configurator/tests/test_product.py b/product_configurator/tests/test_product.py index ea6e0e37..2172b439 100644 --- a/product_configurator/tests/test_product.py +++ b/product_configurator/tests/test_product.py @@ -33,8 +33,8 @@ def get_attr_val_ids(self, ext_ids): return value_ids - def test_product_create(self): - """Test creation of a variant""" + def test_product_create_and_copy(self): + """Test creation and copy of a variant""" conf = [ 'gasoline', '228i', 'model_luxury_line', 'silver', 'rims_384', @@ -46,13 +46,8 @@ def test_product_create(self): self.assertTrue( set(attr_val_ids) == set(product.attribute_value_ids.ids), "Product not created with correct attributes") - self.product = product - - def test_product_copy(self): - """Test copy of a variant""" - - product2 = self.product.copy_configurable() + product2 = product.copy_configurable() self.assertTrue( - set(self.product.attribute_value_ids.ids) == + set(product.attribute_value_ids.ids) == set(product2.attribute_value_ids.ids), "Product configuration copy failed")