diff --git a/addons/product/models/product_template.py b/addons/product/models/product_template.py index fd05e009dc4..98fb97d49af 100644 --- a/addons/product/models/product_template.py +++ b/addons/product/models/product_template.py @@ -150,12 +150,22 @@ class ProductTemplate(models.Model): item_ids = fields.One2many('product.pricelist.item', 'product_tmpl_id', 'Pricelist Items') can_image_1024_be_zoomed = fields.Boolean("Can Image 1024 be zoomed", compute='_compute_can_image_1024_be_zoomed', store=True) + has_configurable_attributes = fields.Boolean("Is a configurable product", compute='_compute_has_configurable_attributes', store=True) @api.depends('image_1920', 'image_1024') def _compute_can_image_1024_be_zoomed(self): for template in self: template.can_image_1024_be_zoomed = template.image_1920 and tools.is_image_size_above(template.image_1920, template.image_1024) + @api.depends('attribute_line_ids', 'attribute_line_ids.value_ids', 'attribute_line_ids.attribute_id.create_variant') + def _compute_has_configurable_attributes(self): + """A product is considered configurable if: + - It has dynamic attributes + - It has any attribute line with at least 2 attribute values configured + """ + for product in self: + product.has_configurable_attributes = product.has_dynamic_attributes() or any(len(ptal.value_ids) >= 2 for ptal in product.attribute_line_ids) + @api.depends('product_variant_ids') def _compute_product_variant_id(self): for p in self: @@ -984,6 +994,20 @@ class ProductTemplate(models.Model): self.ensure_one() return self.env.company + def get_single_product_variant(self): + """ Method used by the product configurator to check if the product is configurable or not. + + We need to open the product configurator if the product: + - is configurable (see has_configurable_attributes) + - has optional products (method is extended in sale to return optional products info) + """ + self.ensure_one() + if self.product_variant_count == 1 and not self.has_configurable_attributes: + return { + 'product_id': self.product_variant_id.id, + } + return {} + @api.model def get_empty_list_help(self, help): self = self.with_context( diff --git a/addons/sale_product_configurator/models/product.py b/addons/sale_product_configurator/models/product.py index 3250a72add6..d565de3fc2f 100644 --- a/addons/sale_product_configurator/models/product.py +++ b/addons/sale_product_configurator/models/product.py @@ -12,7 +12,6 @@ class ProductTemplate(models.Model): string='Optional Products', help="Optional Products are suggested " "whenever the customer hits *Add to Cart* (cross-sell strategy, " "e.g. for computers: warranty, software, etc.).") - has_configurable_attributes = fields.Boolean("Is a configurable product", compute='_compute_has_configurable_attributes', store=True) @api.depends('attribute_line_ids.value_ids.is_custom', 'attribute_line_ids.attribute_id.create_variant') def _compute_has_configurable_attributes(self): @@ -32,17 +31,12 @@ class ProductTemplate(models.Model): - is configurable (see has_configurable_attributes) - has optional products """ self.ensure_one() - - if self.product_variant_count == 1 and not self.has_configurable_attributes: + res = super(ProductTemplate, self).get_single_product_variant() + if res.get('product_id', False): has_optional_products = False for optional_product in self.product_variant_id.optional_product_ids: if optional_product.has_dynamic_attributes() or optional_product._get_possible_variants(self.product_variant_id.product_template_attribute_value_ids): has_optional_products = True break - - return { - 'product_id': self.product_variant_id.id, - 'has_optional_products': has_optional_products - } - - return None + res.update({'has_optional_products': has_optional_products}) + return res diff --git a/addons/sale_product_configurator/static/src/js/product_configurator_widget.js b/addons/sale_product_configurator/static/src/js/product_configurator_widget.js index 6f41174be73..8ed2fd43f0e 100644 --- a/addons/sale_product_configurator/static/src/js/product_configurator_widget.js +++ b/addons/sale_product_configurator/static/src/js/product_configurator_widget.js @@ -65,7 +65,7 @@ ProductConfiguratorWidget.include({ productTemplateId ] }).then(function (result) { - if (result && !result.has_optional_products) { + if (result.product_id && !result.has_optional_products) { self.trigger_up('field_changed', { dataPointID: dataPointId, changes: { @@ -76,15 +76,8 @@ ProductConfiguratorWidget.include({ }, }); } else { - self._openProductConfigurator({ - configuratorMode: (result && result.has_optional_products ? 'options' : 'add'), - default_pricelist_id: self._getPricelistId(), - default_product_template_id: productTemplateId - }, - dataPointId - ); + return self._openConfigurator(result, productTemplateId, dataPointId); } - return Promise.resolve(true); // always returns true for the moment because no other configurator exists. }); }, @@ -115,6 +108,20 @@ ProductConfiguratorWidget.include({ } }, + _openConfigurator: function (result, productTemplateId, dataPointId) { + if (!result.mode || result.mode === 'configurator') { + this._openProductConfigurator({ + configuratorMode: result && result.has_optional_products ? 'options' : 'add', + default_pricelist_id: this._getPricelistId(), + default_product_template_id: productTemplateId + }, + dataPointId + ); + return Promise.resolve(true); + } + return Promise.resolve(false); + }, + /** * Opens the product configurator to allow configuring the product template * and its various options.