diff --git a/addons/website_sale/data/data.xml b/addons/website_sale/data/data.xml index ccc5ef78c98..3ad98c695c7 100644 --- a/addons/website_sale/data/data.xml +++ b/addons/website_sale/data/data.xml @@ -110,6 +110,16 @@ model.env.context['product_template_id'] = request.params.get('productTemplateId response = DynamicFilter._get_products('recently_sold_with', model.env.context) + + Alternative Products + + code + +DynamicFilter = model.env['website.snippet.filter'] +model.env.context['product_template_id'] = request.params.get('productTemplateId') +response = DynamicFilter._get_products('alternative_products', model.env.context) + + @@ -143,6 +153,13 @@ response = DynamicFilter._get_products('recently_sold_with', model.env.context) Products Recently Sold With Product True + + + display_name,description_sale,image_512 + + Alternative Products + True + sale.order diff --git a/addons/website_sale/models/product_template.py b/addons/website_sale/models/product_template.py index c6597abe5db..92b0b050ba0 100644 --- a/addons/website_sale/models/product_template.py +++ b/addons/website_sale/models/product_template.py @@ -382,6 +382,10 @@ class ProductTemplate(models.Model): return self.website_ribbon_id return self.product_tag_ids.ribbon_id[:1] or self.product_variant_ids.additional_product_tag_ids.ribbon_id[:1] + @api.model + def _get_alternative_product_filter(self): + return self.env.ref('website_sale.dynamic_filter_cross_selling_alternative_products').id + # --------------------------------------------------------- # Rating Mixin API # --------------------------------------------------------- diff --git a/addons/website_sale/models/website_snippet_filter.py b/addons/website_sale/models/website_snippet_filter.py index 5d1d7f3d448..ce6ec975893 100644 --- a/addons/website_sale/models/website_snippet_filter.py +++ b/addons/website_sale/models/website_snippet_filter.py @@ -157,3 +157,22 @@ class WebsiteSnippetFilter(models.Model): ]) products = self.env['product.product'].with_context(display_default_code=False).search(domain, limit=limit) return products + + def _get_products_alternative_products(self, website, limit, domain, context): + products = self.env['product.product'] + current_id = context.get('product_template_id') + if not current_id: + return products + current_template = self.env['product.template'].browse(int(current_id)) + if current_template.exists(): + excluded_products = website.sale_get_order().order_line.product_id + excluded_products |= current_template.product_variant_ids + included_products = current_template.alternative_product_ids.product_variant_ids + products = included_products - excluded_products + if products: + domain = expression.AND([ + domain, + [('id', 'in', products.ids)], + ]) + products = self.env['product.product'].with_context(display_default_code=False).search(domain, limit=limit) + return products diff --git a/addons/website_sale/static/src/js/website_sale.editor.js b/addons/website_sale/static/src/js/website_sale.editor.js index e9af1eef78b..82970911746 100644 --- a/addons/website_sale/static/src/js/website_sale.editor.js +++ b/addons/website_sale/static/src/js/website_sale.editor.js @@ -766,4 +766,31 @@ options.registry.WebsiteSaleProductAttribute = options.Class.extend({ return this._super(methodName, params); }, }); + +// Disable "Shown on Mobile" option if for dynamic product snippets +options.registry.MobileVisibility.include({ + + //-------------------------------------------------------------------------- + // Private + //-------------------------------------------------------------------------- + + /** + * @override + */ + async _computeVisibility() { + return await this._super(...arguments) + && !this.$target.hasClass('s_dynamic_snippet_products'); + }, +}); + +// Disable save for alternative products snippet +options.registry.SnippetSave.include({ + /** + * @override + */ + async _computeVisibility() { + return await this._super(...arguments) + && !this.$target.hasClass('o_wsale_alternative_products'); + } +}) }); diff --git a/addons/website_sale/static/src/snippets/s_dynamic_snippet_products/000.js b/addons/website_sale/static/src/snippets/s_dynamic_snippet_products/000.js index 7de3b122c73..f0286f6e3ed 100644 --- a/addons/website_sale/static/src/snippets/s_dynamic_snippet_products/000.js +++ b/addons/website_sale/static/src/snippets/s_dynamic_snippet_products/000.js @@ -81,10 +81,19 @@ const DynamicSnippetProducts = DynamicSnippetCarousel.extend({ if (productNames) { const nameDomain = []; for (const productName of productNames.split(',')) { + // Ignore empty names + if (!productName.length) { + continue; + } + // Search on name, internal reference and barcode. if (nameDomain.length) { nameDomain.unshift('|'); } - nameDomain.push(['name', 'ilike', productName]); + nameDomain.push(...[ + '|', '|', ['name', 'ilike', productName], + ['default_code', '=', productName], + ['barcode', '=', productName], + ]); } searchDomain.push(...nameDomain); } diff --git a/addons/website_sale/static/src/snippets/s_dynamic_snippet_products/options.js b/addons/website_sale/static/src/snippets/s_dynamic_snippet_products/options.js index b6f3ab370f5..1d203be31de 100644 --- a/addons/website_sale/static/src/snippets/s_dynamic_snippet_products/options.js +++ b/addons/website_sale/static/src/snippets/s_dynamic_snippet_products/options.js @@ -6,6 +6,10 @@ const s_dynamic_snippet_carousel_options = require('website.s_dynamic_snippet_ca var wUtils = require('website.utils'); +const alternativeSnippetRemovedOptions = [ + 'filter_opt', 'product_category_opt', 'product_tag_opt', 'product_names_opt', +] + const dynamicSnippetProductsOptions = s_dynamic_snippet_carousel_options.extend({ /** @@ -23,11 +27,22 @@ const dynamicSnippetProductsOptions = s_dynamic_snippet_carousel_options.extend( this.contextualFilterDomain.push(['product_cross_selling', '=', false]); } this.productCategories = {}; + this.isAlternativeProductSnippet = this.$target.hasClass('o_wsale_alternative_products'); }, //-------------------------------------------------------------------------- // Private //-------------------------------------------------------------------------- + /** + * @private + * @override + */ + _computeWidgetVisibility(widgetName, params) { + if (this.isAlternativeProductSnippet && alternativeSnippetRemovedOptions.includes(widgetName)) { + return false; + } + return this._super(...arguments); + }, /** * Fetches product categories. * @private diff --git a/addons/website_sale/views/snippets/s_dynamic_snippet_products.xml b/addons/website_sale/views/snippets/s_dynamic_snippet_products.xml index aaaa19cdeff..1f38be1641a 100644 --- a/addons/website_sale/views/snippets/s_dynamic_snippet_products.xml +++ b/addons/website_sale/views/snippets/s_dynamic_snippet_products.xml @@ -32,7 +32,7 @@ data-select-data-attribute=""/> + placeholder="e.g. lamp,bin" title="Comma-separated list of parts of product names, barcodes or internal reference"/> diff --git a/addons/website_sale/views/templates.xml b/addons/website_sale/views/templates.xml index 162730b9635..197cf580455 100644 --- a/addons/website_sale/views/templates.xml +++ b/addons/website_sale/views/templates.xml @@ -724,6 +724,28 @@ + + + + + + + + Your Dynamic Snippet will be displayed here... + This message is displayed because youy did not provide both a filter and a template to use. + + + + + + + + + @@ -761,36 +783,6 @@ - - - - - Alternative Products: - - - - - - - - - - - - - - - - - - - - - diff --git a/addons/website_sale_comparison/static/tests/tours/website_sale_comparison.js b/addons/website_sale_comparison/static/tests/tours/website_sale_comparison.js index 33538c8d26e..97e240b36f9 100644 --- a/addons/website_sale_comparison/static/tests/tours/website_sale_comparison.js +++ b/addons/website_sale_comparison/static/tests/tours/website_sale_comparison.js @@ -2,7 +2,6 @@ odoo.define('website_sale_comparison.tour_comparison', function (require) { 'use strict'; var tour = require('web_tour.tour'); - var rpc = require('web.rpc'); const tourUtils = require('website_sale.tour_utils'); tour.register('product_comparison', { @@ -166,79 +165,5 @@ odoo.define('website_sale_comparison.tour_comparison', function (require) { trigger: '#cart_products:contains("Conference Chair") .js_quantity[value="1"]', run: function () {}, }, - // test advanced configuration and alternative product - { - content: "create product with newly created attribute and its values and set alternative product", - trigger: 'body', - run: function () { - rpc.query({ - model: 'product.attribute', - method: 'create', - args: [{ - 'name': 'color', - 'display_type': 'color', - 'create_variant': 'dynamic', - }], - }).then(function (attributeId) { - rpc.query({ - model: 'product.template', - method: 'create', - args: [{ - 'name': 'Basic Chair', - 'is_published': true, - 'attribute_line_ids': [[0, 0, { - 'attribute_id': attributeId, - 'value_ids': [ - [0, 0, {'name': 'red', 'attribute_id': attributeId, 'sequence': 1}], - [0, 0, {'name': 'blue', 'attribute_id': attributeId, 'sequence': 2}], - ], - }]], - }], - }).then(function (productId) { - rpc.query({ - model: 'product.template', - method: 'create', - args: [{ - 'name': 'Classic Chair', - 'is_published': true, - 'attribute_line_ids': [[0, 0, { - 'attribute_id': attributeId, - 'value_ids': [ - [0, 0, {'name': 'green', 'attribute_id': attributeId, 'sequence': 3}], - [0, 0, {'name': 'yellow', 'attribute_id': attributeId, 'sequence': 4}], - ], - }]], - 'alternative_product_ids': [[6, 0, [productId]]], - }], - }).then(function () { - window.location.href = '/shop?search=Classic Chair'; - }); - }); - }); - }, - }, - { - content: "click on product", - trigger: '.oe_product_cart a:contains("Classic Chair")', - }, - { - content: "click on compare button", - trigger: '.btn.btn-primary:not(.btn-block):contains("Compare")', - }, - { - content: "check product 'Classic Chair' with first variant (green) is on compare page", - trigger: '.o_product_comparison_table:contains("Classic Chair (green)")', - run: function () {}, - }, - { - content: "check alternative product 'Basic Chair' with first variant (red) is on compare page", - trigger: '.o_product_comparison_table:contains("Basic Chair (red)")', - run: function () {}, - }, - { - content: "check there is the correct attribute", - trigger: '.o_ws_category_0:contains("color"):contains("red")', - run: function () {}, - }, ]); }); diff --git a/addons/website_sale_comparison/views/website_sale_comparison_template.xml b/addons/website_sale_comparison/views/website_sale_comparison_template.xml index d76c710bc8e..e725bdca255 100644 --- a/addons/website_sale_comparison/views/website_sale_comparison_template.xml +++ b/addons/website_sale_comparison/views/website_sale_comparison_template.xml @@ -64,24 +64,6 @@ - - - - - - - - - - - Suggested alternatives: Compare - - - Shop Comparator
@@ -761,36 +783,6 @@