diff --git a/addons/sale/static/src/js/variant_mixin.js b/addons/sale/static/src/js/variant_mixin.js index a7b20cfea4a..5afcb79a37e 100644 --- a/addons/sale/static/src/js/variant_mixin.js +++ b/addons/sale/static/src/js/variant_mixin.js @@ -206,10 +206,9 @@ var VariantMixin = { * @param {$.Element} $container */ triggerVariantChange: function ($container) { - var self = this; $container.find('ul[data-attribute_exclusions]').trigger('change'); $container.find('input.js_variant_change:checked, select.js_variant_change').each(function () { - self.handleCustomValues($(this)); + VariantMixin.handleCustomValues($(this)); }); }, @@ -312,7 +311,6 @@ var VariantMixin = { * @returns {Promise} the promise that will be resolved with a {integer} productId */ selectOrCreateProduct: function ($container, productId, productTemplateId, useAjax) { - var self = this; productId = parseInt(productId); productTemplateId = parseInt(productTemplateId); var productReady = Promise.resolve(); @@ -322,7 +320,7 @@ var VariantMixin = { var params = { product_template_id: productTemplateId, product_template_attribute_value_ids: - JSON.stringify(self.getSelectedVariantValues($container)), + JSON.stringify(VariantMixin.getSelectedVariantValues($container)), }; var route = '/sale/create_product_variant'; diff --git a/addons/sale_product_configurator/__init__.py b/addons/sale_product_configurator/__init__.py index bbc55806ace..aa4d0fd63a9 100644 --- a/addons/sale_product_configurator/__init__.py +++ b/addons/sale_product_configurator/__init__.py @@ -2,4 +2,3 @@ from . import controllers from . import models -from . import wizard diff --git a/addons/sale_product_configurator/__manifest__.py b/addons/sale_product_configurator/__manifest__.py index 95cb28d45f3..750545113a8 100644 --- a/addons/sale_product_configurator/__manifest__.py +++ b/addons/sale_product_configurator/__manifest__.py @@ -15,13 +15,9 @@ It also enables the "optional products" feature. 'depends': ['sale'], 'data': [ - 'security/ir.model.access.csv', - 'views/product_template_views.xml', 'views/sale_order_views.xml', 'views/templates.xml', - - 'wizard/sale_product_configurator_views.xml', ], 'demo': [ 'data/sale_demo.xml', @@ -29,9 +25,6 @@ It also enables the "optional products" feature. 'assets': { 'web.assets_backend': [ 'sale/static/src/js/variant_mixin.js', - 'sale_product_configurator/static/src/js/product_configurator_renderer.js', - 'sale_product_configurator/static/src/js/product_configurator_controller.js', - 'sale_product_configurator/static/src/js/product_configurator_view.js', 'sale_product_configurator/static/src/js/product_configurator_widget.js', 'sale_product_configurator/static/src/js/product_configurator_modal.js', ], diff --git a/addons/sale_product_configurator/controllers/main.py b/addons/sale_product_configurator/controllers/main.py index edb0c2be7f5..847d4adfbd2 100644 --- a/addons/sale_product_configurator/controllers/main.py +++ b/addons/sale_product_configurator/controllers/main.py @@ -75,7 +75,8 @@ class ProductConfiguratorController(http.Controller): 'variant_values': variant_values, 'pricelist': pricelist, 'handle_stock': handle_stock, - 'already_configured': kw.get("already_configured", False) + 'already_configured': kw.get("already_configured", False), + 'mode': kw.get('mode', 'add') }) def _get_pricelist(self, pricelist_id, pricelist_fallback=False): diff --git a/addons/sale_product_configurator/security/ir.model.access.csv b/addons/sale_product_configurator/security/ir.model.access.csv deleted file mode 100644 index 1c8e002cafd..00000000000 --- a/addons/sale_product_configurator/security/ir.model.access.csv +++ /dev/null @@ -1,2 +0,0 @@ -"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink" -"access_sale_product_configurator","access.sale.product.configurator","model_sale_product_configurator","sales_team.group_sale_salesman",1,1,1,0 diff --git a/addons/sale_product_configurator/static/src/js/product_configurator_controller.js b/addons/sale_product_configurator/static/src/js/product_configurator_controller.js deleted file mode 100644 index 6d7448f0e6b..00000000000 --- a/addons/sale_product_configurator/static/src/js/product_configurator_controller.js +++ /dev/null @@ -1,330 +0,0 @@ -odoo.define('sale_product_configurator.ProductConfiguratorFormController', function (require) { -"use strict"; - -var core = require('web.core'); -var _t = core._t; -var FormController = require('web.FormController'); -var OptionalProductsModal = require('sale_product_configurator.OptionalProductsModal'); - -var ProductConfiguratorFormController = FormController.extend({ - custom_events: _.extend({}, FormController.prototype.custom_events, { - field_changed: '_onFieldChanged', - handle_add: '_handleAdd' - }), - /** - * @override - */ - start: function () { - var self = this; - return this._super.apply(this, arguments).then(function () { - self.$el.addClass('o_product_configurator'); - }); - }, - /** - * We need to first load the template of the selected product and then render the content - * to avoid a flicker when the modal is opened. - * - * @override - */ - willStart: function () { - var def = this._super.apply(this, arguments); - if (this.initialState.data.product_template_id) { - return this._configureProduct( - this.initialState.data.product_template_id.data.id - ).then(function () { - return def; - }); - } - - return def; - }, - /** - * Showing this window is useless for configuratorMode 'options' as this form view - * is used as a bridge between SO lines and optional products. - * - * Placed here because it's the only method that is called after the modal is rendered. - * - * @override - */ - renderButtons: function () { - this._super.apply(this, arguments); - - if (this.renderer.state.context.configuratorMode === 'options') { - this.$el.closest('.modal').addClass('d-none'); - } - - const renderSecondOnly = this.renderer.state.context.configuratorMode !== 'edit'; - this.$el.closest('.o_dialog_container').toggleClass('d-none', renderSecondOnly); - }, - - //-------------------------------------------------------------------------- - // Private - //-------------------------------------------------------------------------- - - - /** - * We need to override the default click behavior for our "Add" button - * because there is a possibility that this product has optional products. - * If so, we need to display an extra modal to choose the options. - * - * @override - */ - _onButtonClicked: function (event) { - if (event.stopPropagation) { - event.stopPropagation(); - } - var attrs = event.data.attrs; - if (attrs.special === 'cancel') { - this._super.apply(this, arguments); - } else { - if (!this.$el - .parents('.modal') - .find('.o_sale_product_configurator_add') - .hasClass('disabled')) { - this._handleAdd(); - } - } - }, - /** - * This is overridden to allow catching the "select" event on our product template select field. - * - * @override - * @private - */ - _onFieldChanged: function (event) { - this._super.apply(this, arguments); - - var self = this; - var productId = event.data.changes.product_template_id.id; - - // check to prevent traceback when emptying the field - if (!productId) { - return; - } - - this._configureProduct(event.data.changes.product_template_id.id) - .then(function () { - self.renderer.renderConfigurator(self.renderer.configuratorHtml); - }); - }, - - /** - * Renders the "variants" part of the wizard - * - * @param {integer} productTemplateId - */ - _configureProduct: function (productTemplateId) { - var self = this; - var initialProduct = this.initialState.data.product_template_id; - var changed = initialProduct && initialProduct.data.id !== productTemplateId; - var data = this.renderer.state.data; - var quantity = initialProduct.context && initialProduct.context.default_quantity ? initialProduct.context.default_quantity : data.quantity; - return this._rpc({ - route: '/sale_product_configurator/configure', - params: { - product_template_id: productTemplateId, - pricelist_id: this.renderer.pricelistId, - add_qty: quantity, - product_template_attribute_value_ids: changed ? [] : this._getAttributeValueIds( - data.product_template_attribute_value_ids - ), - product_no_variant_attribute_value_ids: changed ? [] : this._getAttributeValueIds( - data.product_no_variant_attribute_value_ids - ), - context: this.getSession().user_context, - } - }).then(function (configurator) { - self.renderer.configuratorHtml = configurator; - }); - }, - /** - * When the user adds a product that has optional products, we need to display - * a window to allow the user to choose these extra options. - * - * This will also create the product if it's in "dynamic" mode - * (see product_attribute.create_variant) - * - * If "self.renderer.state.context.configuratorMode" is 'edit', this will only send - * the main product with its changes. - * - * As opposed to the 'add' mode that will add the main product AND all the configured optional products. - * - * A third mode, 'options', is available for products that don't have a configuration but have - * optional products to select. This will bypass the configuration step and open the - * options modal directly. - * - * @private - */ - _handleAdd: function () { - var self = this; - var $modal = this.$el; - var productSelector = [ - 'input[type="hidden"][name="product_id"]', - 'input[type="radio"][name="product_id"]:checked' - ]; - - var productId = parseInt($modal.find(productSelector.join(', ')).first().val(), 10); - var productTemplateId = $modal.find('.product_template_id').val(); - this.renderer.selectOrCreateProduct( - $modal, - productId, - productTemplateId, - false - ).then(function (productId) { - $modal.find(productSelector.join(', ')).val(productId); - - var variantValues = self - .renderer - .getSelectedVariantValues($modal.find('.js_product')); - - var productCustomVariantValues = self - .renderer - .getCustomVariantValues($modal.find('.js_product')); - - var noVariantAttributeValues = self - .renderer - .getNoVariantAttributeValues($modal.find('.js_product')); - - self.rootProduct = { - product_id: productId, - product_template_id: parseInt(productTemplateId), - quantity: parseFloat($modal.find('input[name="add_qty"]').val() || 1), - variant_values: variantValues, - product_custom_attribute_values: productCustomVariantValues, - no_variant_attribute_values: noVariantAttributeValues - }; - - if (self.renderer.state.context.configuratorMode === 'edit') { - // edit mode only takes care of main product - self._onAddRootProductOnly(); - return; - } - - self.optionalProductsModal = new OptionalProductsModal($('body'), { - rootProduct: self.rootProduct, - pricelistId: self.renderer.pricelistId, - okButtonText: _t('Confirm'), - cancelButtonText: _t('Back'), - title: _t('Configure'), - context: self.initialState.context, - previousModalHeight: self.$el.closest('.modal-content').height() - }).open(); - - self.optionalProductsModal.on('options_empty', null, - // no optional products found for this product, only add the root product - self._onAddRootProductOnly.bind(self)); - - self.optionalProductsModal.on('update_quantity', null, - self._onOptionsUpdateQuantity.bind(self)); - - self.optionalProductsModal.on('confirm', null, - self._onModalConfirm.bind(self)); - - self.optionalProductsModal.on('closed', null, - self._onModalClose.bind(self)); - }); - }, - - /** - * Add root product only and forget optional products. - * Used when product has no optional products and in 'edit' mode. - * - * @private - */ - _onAddRootProductOnly: function () { - this._addProducts([this.rootProduct]); - }, - - /** - * Add all selected products - * - * @private - */ - _onModalConfirm: function () { - this._wasConfirmed = true; - this.optionalProductsModal.getAndCreateSelectedProducts().then((products) => { - this._addProducts(products); - }); - }, - - /** - * When the optional products modal is closed (and not confirmed) on 'options' mode, - * this window should also be closed immediately. - * - * @private - */ - _onModalClose: function () { - if (['options', 'add'].includes(this.renderer.state.context.configuratorMode) - && this._wasConfirmed !== true) { - this.do_action({type: 'ir.actions.act_window_close'}); - } - }, - - /** - * Remove "d-none" to allow other modals to display - * - * @override - */ - destroy: function () { - $('.o_dialog_container').removeClass('d-none'); - this._super.apply(this, arguments); - }, - - /** - * Update product configurator form - * when quantity is updated in the optional products window - * - * @private - * @param {integer} quantity - */ - _onOptionsUpdateQuantity: function (quantity) { - this.$el - .find('input[name="add_qty"]') - .val(quantity) - .trigger('change'); - }, - - /** - * This triggers the close action for the window and - * adds the product as the "infos" parameter. - * It will allow the caller (typically the product_configurator widget) of this window - * to handle the added products. - * - * @private - * @param {Array} products the list of added products - * {integer} products.product_id: the id of the product - * {integer} products.quantity: the added quantity for this product - * {Array} products.product_custom_attribute_values: - * see variant_mixin.getCustomVariantValues - * {Array} products.no_variant_attribute_values: - * see variant_mixin.getNoVariantAttributeValues - */ - _addProducts: function (products) { - this.do_action({type: 'ir.actions.act_window_close', infos: { - mainProduct: products[0], - options: products.slice(1) - }}); - }, - /** - * Extracts the ids from the passed attributeValueIds and returns them - * as a plain array. - * - * @param {Array} attributeValueIds - */ - _getAttributeValueIds: function (attributeValueIds) { - if (!attributeValueIds || attributeValueIds.length === 0) { - return false; - } - - var result = []; - _.each(attributeValueIds.data, function (attributeValue) { - result.push(attributeValue.data.id); - }); - - return result; - } -}); - -return ProductConfiguratorFormController; - -}); diff --git a/addons/sale_product_configurator/static/src/js/product_configurator_modal.js b/addons/sale_product_configurator/static/src/js/product_configurator_modal.js index b6dbb6037be..03141503491 100644 --- a/addons/sale_product_configurator/static/src/js/product_configurator_modal.js +++ b/addons/sale_product_configurator/static/src/js/product_configurator_modal.js @@ -1,13 +1,12 @@ -odoo.define('sale_product_configurator.OptionalProductsModal', function (require) { - "use strict"; +/** @odoo-module */ -var ajax = require('web.ajax'); -var Dialog = require('web.Dialog'); -const OwlDialog = require('web.OwlDialog'); -var ServicesMixin = require('web.ServicesMixin'); -var VariantMixin = require('sale.VariantMixin'); +import ajax from 'web.ajax'; +import Dialog from 'web.Dialog'; +import OwlDialog from 'web.OwlDialog'; +import ServicesMixin from 'web.ServicesMixin'; +import VariantMixin from 'sale.VariantMixin'; -var OptionalProductsModal = Dialog.extend(ServicesMixin, VariantMixin, { +export const OptionalProductsModal = Dialog.extend(ServicesMixin, VariantMixin, { events: _.extend({}, Dialog.prototype.events, VariantMixin.events, { 'click a.js_add, a.js_remove': '_onAddOrRemoveOption', 'click button.js_add_cart_json': 'onClickAddCartJSON', @@ -43,7 +42,8 @@ var OptionalProductsModal = Dialog.extend(ServicesMixin, VariantMixin, { buttons: [{ text: params.okButtonText, click: this._onConfirmButtonClick, - classes: 'btn-primary' + // the o_sale_product_configurator_edit class is used for tours. + classes: 'btn-primary o_sale_product_configurator_edit' }, { text: params.cancelButtonText, click: this._onCancelButtonClick @@ -58,6 +58,7 @@ var OptionalProductsModal = Dialog.extend(ServicesMixin, VariantMixin, { this.container = parent; this.pricelistId = params.pricelistId; this.previousModalHeight = params.previousModalHeight; + this.mode = params.mode; this.dialogClass = 'oe_advanced_configurator_modal'; this._productImageField = 'image_128'; @@ -75,6 +76,7 @@ var OptionalProductsModal = Dialog.extend(ServicesMixin, VariantMixin, { var uri = this._getUri("/sale_product_configurator/show_advanced_configurator"); var getModalContent = ajax.jsonRpc(uri, 'call', { + mode: self.mode, product_id: self.rootProduct.product_id, variant_values: self.rootProduct.variant_values, pricelist_id: self.pricelistId || false, @@ -236,12 +238,11 @@ var OptionalProductsModal = Dialog.extend(ServicesMixin, VariantMixin, { $updatedDescription.append($('

', { text: $productDescription.text() })); - $.each(this.rootProduct.product_custom_attribute_values, function () { if (this.custom_value) { const $customInput = $modalContent .find(".main_product [data-is_custom='True']") - .closest(`[data-value_id='${this.custom_product_template_attribute_value_id}']`); + .closest(`[data-value_id='${this.custom_product_template_attribute_value_id.res_id}']`); $customInput.attr('previous_custom_value', this.custom_value); VariantMixin.handleCustomValues($customInput); } @@ -520,7 +521,3 @@ var OptionalProductsModal = Dialog.extend(ServicesMixin, VariantMixin, { return el.dataset.uniqueId; }, }); - -return OptionalProductsModal; - -}); diff --git a/addons/sale_product_configurator/static/src/js/product_configurator_renderer.js b/addons/sale_product_configurator/static/src/js/product_configurator_renderer.js deleted file mode 100644 index 4319e9bcdd4..00000000000 --- a/addons/sale_product_configurator/static/src/js/product_configurator_renderer.js +++ /dev/null @@ -1,136 +0,0 @@ -odoo.define('sale_product_configurator.ProductConfiguratorFormRenderer', function (require) { -"use strict"; - -var FormRenderer = require('web.FormRenderer'); -var VariantMixin = require('sale.VariantMixin'); - -var ProductConfiguratorFormRenderer = FormRenderer.extend(VariantMixin, { - - events: _.extend({}, FormRenderer.prototype.events, VariantMixin.events, { - 'click button.js_add_cart_json': 'onClickAddCartJSON', - }), - - /** - * @override - */ - init: function () { - this._super.apply(this, arguments); - this.pricelistId = this.state.context.default_pricelist_id || 0; - }, - /** - * @override - */ - start: function () { - var self = this; - return this._super.apply(this, arguments).then(function () { - self.$el.append($('

', {class: 'configurator_container'})); - self.renderConfigurator(self.configuratorHtml); - self._checkMode(); - }); - }, - - //-------------------------------------------------------------------------- - // Public - //-------------------------------------------------------------------------- - - /** - * Renders the product configurator within the form - * - * Will also: - * - * - add events handling for variant changes - * - trigger variant change to compute the price and other - * variant specific changes - * - * @param {string} configuratorHtml the evaluated template of - * the product configurator - */ - renderConfigurator: function (configuratorHtml) { - var $configuratorContainer = this.$('.configurator_container'); - $configuratorContainer.empty(); - - var $configuratorHtml = $(configuratorHtml); - $configuratorHtml.appendTo($configuratorContainer); - - this.triggerVariantChange($configuratorContainer); - this._applyCustomValues(); - if (this.state.context.configuratorMode !== 'options') { - this.trigger_up('handle_add'); - } - }, - - //-------------------------------------------------------------------------- - // Private - //-------------------------------------------------------------------------- - - /** - * If the configuratorMode in the given context is 'edit', we need to - * hide the regular 'Add' button to replace it with an 'EDIT' button. - * - * If the configuratorMode is set to 'options', we will directly open the - * options modal. - * - * @private - */ - _checkMode: function () { - if (this.state.context.configuratorMode === 'edit') { - this.$('.o_sale_product_configurator_add').hide(); - this.$('.o_sale_product_configurator_edit').css('display', 'inline-block'); - } else if (this.state.context.configuratorMode === 'options') { - this.trigger_up('handle_add'); - } - }, - - /** - * Toggles the add button depending on the possibility of the current - * combination. - * - * @override - */ - _toggleDisable: function ($parent, isCombinationPossible) { - VariantMixin._toggleDisable.apply(this, arguments); - $parent.parents('.modal').find('.o_sale_product_configurator_add').toggleClass('disabled', !isCombinationPossible); - }, - - /** - * Will fill the custom values input based on the provided initial configuration. - * - * @private - */ - _applyCustomValues: function () { - var self = this; - var customValueIds = this.state.data.product_custom_attribute_value_ids; - if (customValueIds) { - _.each(customValueIds.data, function (customValue) { - if (customValue.data.custom_value) { - var attributeValueId = customValue.data.custom_product_template_attribute_value_id.data.id; - var $input = self._findRelatedAttributeValueInput(attributeValueId); - $input - .closest('li[data-attribute_id]') - .find('.variant_custom_value') - .val(customValue.data.custom_value); - } - }); - } - }, - - /** - * Find the $.Element input/select related to that product.attribute.value - * - * @param {integer} attributeValueId - * - * @private - */ - _findRelatedAttributeValueInput: function (attributeValueId) { - var selectors = [ - 'ul.js_add_cart_variants input[data-value_id="' + attributeValueId + '"]', - 'ul.js_add_cart_variants option[data-value_id="' + attributeValueId + '"]' - ]; - - return this.$(selectors.join(', ')); - } -}); - -return ProductConfiguratorFormRenderer; - -}); diff --git a/addons/sale_product_configurator/static/src/js/product_configurator_view.js b/addons/sale_product_configurator/static/src/js/product_configurator_view.js deleted file mode 100644 index aa16a0bb81d..00000000000 --- a/addons/sale_product_configurator/static/src/js/product_configurator_view.js +++ /dev/null @@ -1,20 +0,0 @@ -odoo.define('sale_product_configurator.ProductConfiguratorFormView', function (require) { -"use strict"; - -var ProductConfiguratorFormController = require('sale_product_configurator.ProductConfiguratorFormController'); -var ProductConfiguratorFormRenderer = require('sale_product_configurator.ProductConfiguratorFormRenderer'); -var FormView = require('web.FormView'); -var viewRegistry = require('web.view_registry'); - -var ProductConfiguratorFormView = FormView.extend({ - config: _.extend({}, FormView.prototype.config, { - Controller: ProductConfiguratorFormController, - Renderer: ProductConfiguratorFormRenderer, - }), -}); - -viewRegistry.add('product_configurator_form', ProductConfiguratorFormView); - -return ProductConfiguratorFormView; - -}); 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 64da75d8a9f..0586722c4fd 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 @@ -1,5 +1,12 @@ -odoo.define('sale_product_configurator.product_configurator', function (require) { -var ProductConfiguratorWidget = require('sale.product_configurator'); +/** @odoo-module */ +import ProductConfiguratorWidget from "sale.product_configurator"; +import { OptionalProductsModal } from "@sale_product_configurator/js/product_configurator_modal"; +import { + selectOrCreateProduct, + getSelectedVariantValues, + getNoVariantAttributeValues, +} from "sale.VariantMixin"; +import { _t } from "web.core"; /** * Extension of the ProductConfiguratorWidget to support product configuration. @@ -16,7 +23,7 @@ ProductConfiguratorWidget.include({ * Override of sale.product_configurator Hook * * @override - */ + */ _isConfigurableProduct: function () { return this.recordData.is_configurable_product || this._super.apply(this, arguments); }, @@ -43,20 +50,20 @@ ProductConfiguratorWidget.include({ }, /** - * This method is overridden to check if the product_template_id - * needs configuration or not: - * - * - The product_template has only one "product.product" and is not dynamic - * -> Set the product_id on the SO line - * -> If the product has optional products, open the configurator in 'options' mode - * - * - The product_template is configurable - * -> Open the product configurator wizard and initialize it with - * the provided product_template_id and its current attribute values - * - * @override - * @private - */ + * This method is overridden to check if the product_template_id + * needs configuration or not: + * + * - The product_template has only one "product.product" and is not dynamic + * -> Set the product_id on the SO line + * -> If the product has optional products, open the configurator in 'options' mode + * + * - The product_template is configurable + * -> Open the product configurator wizard and initialize it with + * the provided product_template_id and its current attribute values + * + * @override + * @private + */ _onTemplateChange: function (productTemplateId, dataPointId) { var self = this; var ctx = {}; @@ -65,19 +72,19 @@ ProductConfiguratorWidget.include({ } return this._rpc({ - model: 'product.template', - method: 'get_single_product_variant', + model: "product.template", + method: "get_single_product_variant", args: [productTemplateId], context: ctx, }).then(function (result) { if (result.product_id && !result.has_optional_products) { - self.trigger_up('field_changed', { + self.trigger_up("field_changed", { dataPointID: dataPointId, changes: { - product_id: {id: result.product_id}, + product_id: { id: result.product_id }, product_custom_attribute_value_ids: { - operation: 'DELETE_ALL' - } + operation: "DELETE_ALL", + }, }, }); } else { @@ -96,16 +103,16 @@ ProductConfiguratorWidget.include({ var self = this; this._super.apply(this, arguments); var parentList = self.getParent(); - var unselectRow = (parentList.unselectRow || function() {}).bind(parentList); // form view on mobile + var unselectRow = (parentList.unselectRow || function () {}).bind(parentList); // form view on mobile if (self.optionalProducts && self.optionalProducts.length !== 0) { - self.trigger_up('add_record', { + self.trigger_up("add_record", { context: self._productsToRecords(self.optionalProducts), - forceEditable: 'bottom', + forceEditable: "bottom", allowWarning: true, onSuccess: function () { // Leave edit mode of one2many list. unselectRow(); - } + }, }); } else if (!self._isConfigurableLine() && self._isConfigurableProduct()) { // Leave edit mode of current line if line was configured @@ -115,11 +122,12 @@ 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 + if (!result.mode || result.mode === "configurator") { + this._openProductConfigurator( + { + configuratorMode: result && result.has_optional_products ? "options" : "add", + pricelist_id: this._getPricelistId(), + product_template_id: productTemplateId, }, dataPointId ); @@ -149,40 +157,108 @@ ProductConfiguratorWidget.include({ * * @private */ - _openProductConfigurator: function (data, dataPointId) { + async _openProductConfigurator(data, dataPointId) { this.optionalProducts = undefined; - var self = this; - this.do_action('sale_product_configurator.sale_product_configurator_action', { - additional_context: data, - on_close: function (result) { - if (result && !result.special) { - self._addProducts(result, dataPointId); - } else { - if (self.restoreProductTemplateId) { - // if configurator opened in edit mode. - self.trigger_up('field_changed', { - dataPointID: dataPointId, - preventProductIdCheck: true, - changes: { - product_template_id: self.restoreProductTemplateId.data - } - }); - } else { - // if configurator opened to create line: - // destroy line if configurator closed during configuration process. - self.trigger_up('field_changed', { - dataPointID: dataPointId, - changes: { - product_template_id: false, - product_id: false, - }, - }); - } - } + const optionalProductsModal = await this._openModal(data); + // no optional products found for this product, only add the root product + optionalProductsModal.on("options_empty", null, () => + this._addProducts({ mainProduct: this.rootProduct }, dataPointId) + ); + + let confirmed = false; + optionalProductsModal.on("confirm", null, async () => { + confirmed = true; + const [ + mainProduct, + ...options + ] = await optionalProductsModal.getAndCreateSelectedProducts(); + this._addProducts({ mainProduct, options }, dataPointId); + }); + optionalProductsModal.on("closed", null, () => { + if (confirmed) { + return; + } + if (this.restoreProductTemplateId) { + // if configurator opened in edit mode. + this.trigger_up("field_changed", { + dataPointID: dataPointId, + preventProductIdCheck: true, + changes: { + product_template_id: this.restoreProductTemplateId.data, + }, + }); + } else { + // if configurator opened to create line: + // destroy line if configurator closed during configuration process. + this.trigger_up("field_changed", { + dataPointID: dataPointId, + changes: { + product_template_id: false, + product_id: false, + }, + }); } }); }, + async _openModal(data) { + const { + product_template_id, + product_template_attribute_value_ids, + product_no_variant_attribute_value_ids, + pricelist_id, + product_custom_attribute_value_ids, + quantity, + configuratorMode, + } = data; + // FIXME replace this with an RPC instead of rendering a template and querying it for data + const $modal = $( + await this._rpc({ + route: "/sale_product_configurator/configure", + params: { + product_template_id, + pricelist_id, + add_qty: quantity, + product_template_attribute_value_ids, + product_no_variant_attribute_value_ids, + context: this.getSession().user_context, + }, + }) + ); + const productSelector = `input[type="hidden"][name="product_id"], input[type="radio"][name="product_id"]:checked`; + const productTemplateId = $modal.find(".product_template_id").val(); + const productId = await selectOrCreateProduct.call( + this, + $modal, + parseInt($modal.find(productSelector).first().val(), 10), + productTemplateId, + false + ); + $modal.find(productSelector).val(productId); + const variantValues = getSelectedVariantValues($modal); + const noVariantAttributeValues = getNoVariantAttributeValues($modal); + this.rootProduct = { + product_id: productId, + product_template_id: parseInt(productTemplateId), + quantity: parseFloat($modal.find('input[name="add_qty"]').val() || 1), + variant_values: variantValues, + product_custom_attribute_values: product_custom_attribute_value_ids, + no_variant_attribute_values: noVariantAttributeValues, + }; + return new OptionalProductsModal(null, { + rootProduct: this.rootProduct, + pricelistId: pricelist_id, + okButtonText: _t("Confirm"), + cancelButtonText: _t("Back"), + title: _t("Configure"), + context: + (this.record && this.recordParams && this.record.getContext(this.recordParams)) || + {}, + previousModalHeight: this.$el.closest(".modal-content").height(), + mode: configuratorMode, + }).open(); + }, + /** * Opens the product configurator in "edit" mode. * (see '_openProductConfigurator' for more info on the "edit" mode). @@ -200,25 +276,30 @@ ProductConfiguratorWidget.include({ } this.restoreProductTemplateId = this.recordData.product_template_id; // If line has been set up through the product_configurator: - this._openProductConfigurator({ - configuratorMode: 'edit', - default_product_template_id: this.recordData.product_template_id.data.id, - default_pricelist_id: this._getPricelistId(), - default_product_template_attribute_value_ids: this._convertFromMany2Many( + this._openProductConfigurator( + { + configuratorMode: "edit", + product_template_id: this.recordData.product_template_id.data.id, + pricelist_id: this._getPricelistId(), + product_template_attribute_value_ids: this._toIdArray( this.recordData.product_template_attribute_value_ids ), - default_product_no_variant_attribute_value_ids: this._convertFromMany2Many( + product_no_variant_attribute_value_ids: this._toIdArray( this.recordData.product_no_variant_attribute_value_ids ), - default_product_custom_attribute_value_ids: this._convertFromOne2Many( - this.recordData.product_custom_attribute_value_ids + product_custom_attribute_value_ids: this.recordData.product_custom_attribute_value_ids.data.map( + (d) => d.data ), - default_quantity: this.recordData.product_uom_qty + quantity: this.recordData.product_uom_qty, }, this.dataPointID ); }, + _toIdArray(recordData) { + return recordData ? [...recordData.res_ids] : null; + }, + /** * This will first modify the SO line to update all the information coming from * the product configurator using the 'field_changed' event. @@ -238,11 +319,11 @@ ProductConfiguratorWidget.include({ * @private */ _addProducts: function (result, dataPointId) { - this.trigger_up('field_changed', { + this.trigger_up("field_changed", { dataPointID: dataPointId, preventProductIdCheck: true, optionalProducts: result.options, - changes: this._getMainProductChanges(result.mainProduct) + changes: this._getMainProductChanges(result.mainProduct), }); }, @@ -264,13 +345,13 @@ ProductConfiguratorWidget.include({ */ _getMainProductChanges: function (mainProduct) { var result = { - product_id: {id: mainProduct.product_id}, - product_template_id: {id: mainProduct.product_template_id}, - product_uom_qty: mainProduct.quantity + product_id: { id: mainProduct.product_id }, + product_template_id: { id: mainProduct.product_template_id }, + product_uom_qty: mainProduct.quantity, }; var customAttributeValues = mainProduct.product_custom_attribute_values; - var customValuesCommands = [{operation: 'DELETE_ALL'}]; + var customValuesCommands = [{ operation: "DELETE_ALL" }]; if (customAttributeValues && customAttributeValues.length !== 0) { _.each(customAttributeValues, function (customValue) { // FIXME awa: This could be optimized by adding a "disableDefaultGet" to avoid @@ -280,36 +361,39 @@ ProductConfiguratorWidget.include({ // those "default_" values and set them on the various fields (text,o2m,m2m,...). // -> This is not considered as worth it right now. customValuesCommands.push({ - operation: 'CREATE', - context: [{ - default_custom_product_template_attribute_value_id: customValue.custom_product_template_attribute_value_id, - default_custom_value: customValue.custom_value - }] + operation: "CREATE", + context: [ + { + default_custom_product_template_attribute_value_id: + customValue.custom_product_template_attribute_value_id, + default_custom_value: customValue.custom_value, + }, + ], }); }); } - result['product_custom_attribute_value_ids'] = { - operation: 'MULTI', - commands: customValuesCommands + result["product_custom_attribute_value_ids"] = { + operation: "MULTI", + commands: customValuesCommands, }; var noVariantAttributeValues = mainProduct.no_variant_attribute_values; - var noVariantCommands = [{operation: 'DELETE_ALL'}]; + var noVariantCommands = [{ operation: "DELETE_ALL" }]; if (noVariantAttributeValues && noVariantAttributeValues.length !== 0) { var resIds = _.map(noVariantAttributeValues, function (noVariantValue) { - return {id: parseInt(noVariantValue.value)}; + return { id: parseInt(noVariantValue.value) }; }); noVariantCommands.push({ - operation: 'ADD_M2M', - ids: resIds + operation: "ADD_M2M", + ids: resIds, }); } - result['product_no_variant_attribute_value_ids'] = { - operation: 'MULTI', - commands: noVariantCommands + result["product_no_variant_attribute_value_ids"] = { + operation: "MULTI", + commands: noVariantCommands, }; return result; @@ -339,41 +423,43 @@ ProductConfiguratorWidget.include({ var record = { default_product_id: product.product_id, default_product_template_id: product.product_template_id, - default_product_uom_qty: product.quantity + default_product_uom_qty: product.quantity, }; if (product.no_variant_attribute_values) { var defaultProductNoVariantAttributeValues = []; _.each(product.no_variant_attribute_values, function (attributeValue) { - defaultProductNoVariantAttributeValues.push( - [4, parseInt(attributeValue.value)] - ); + defaultProductNoVariantAttributeValues.push([ + 4, + parseInt(attributeValue.value), + ]); }); - record['default_product_no_variant_attribute_value_ids'] - = defaultProductNoVariantAttributeValues; + record[ + "default_product_no_variant_attribute_value_ids" + ] = defaultProductNoVariantAttributeValues; } if (product.product_custom_attribute_values) { var defaultCustomAttributeValues = []; _.each(product.product_custom_attribute_values, function (attributeValue) { - defaultCustomAttributeValues.push( - [0, 0, { - custom_product_template_attribute_value_id: attributeValue.custom_product_template_attribute_value_id, - custom_value: attributeValue.custom_value - }] - ); + defaultCustomAttributeValues.push([ + 0, + 0, + { + custom_product_template_attribute_value_id: + attributeValue.custom_product_template_attribute_value_id, + custom_value: attributeValue.custom_value, + }, + ]); }); - record['default_product_custom_attribute_value_ids'] - = defaultCustomAttributeValues; + record["default_product_custom_attribute_value_ids"] = defaultCustomAttributeValues; } records.push(record); }); return records; - } + }, }); -return ProductConfiguratorWidget; - -}); +export { ProductConfiguratorWidget }; diff --git a/addons/sale_product_configurator/static/tests/product_configurator.test.js b/addons/sale_product_configurator/static/tests/product_configurator.test.js index cad2beaa9bf..3db2b281df3 100644 --- a/addons/sale_product_configurator/static/tests/product_configurator.test.js +++ b/addons/sale_product_configurator/static/tests/product_configurator.test.js @@ -1,9 +1,8 @@ -odoo.define('sale.product.configurator.tests', function (require) { -"use strict"; - -const FormView = require('web.FormView'); -const ProductConfiguratorFormView = require('sale_product_configurator.ProductConfiguratorFormView'); -const testUtils = require('web.test_utils'); +/** @odoo-module */ +import FormView from 'web.FormView'; +import testUtils from 'web.test_utils'; +import { patchWithCleanup } from "@web/../tests/helpers/utils"; +import { ProductConfiguratorWidget } from "@sale_product_configurator/js/product_configurator_widget"; const createView = testUtils.createView; const getArch = function (){ @@ -175,8 +174,11 @@ QUnit.module('Product Configurator', { }); QUnit.test('Select a configurable product template and verify that the product configurator is opened', async function (assert) { - assert.expect(2); - + patchWithCleanup(ProductConfiguratorWidget.prototype, { + _openProductConfigurator() { + assert.step("open configurator"); + } + }); const form = await createView({ View: FormView, model: 'sale_order', @@ -189,17 +191,12 @@ QUnit.module('Product Configurator', { } return this._super.apply(this, arguments); }, - intercepts: { - do_action: function (ev) { - if (ev.data.action === 'sale_product_configurator.sale_product_configurator_action') { - assert.ok(true); - } - }, - } }); await testUtils.dom.click(form.$("a:contains('Add a product')")); + assert.verifySteps([]); await testUtils.fields.many2one.searchAndClickItem("product_template_id", {item: 'Customizable Desk'}); + assert.verifySteps(["open configurator"]); form.destroy(); }); @@ -226,39 +223,6 @@ QUnit.module('Product Configurator', { form.destroy(); }); - QUnit.test('Select a product in the list and check for template loading', async function (assert) { - assert.expect(1); - - const product_configurator_form = await createView({ - View: ProductConfiguratorFormView, - model: 'sale_product_configurator', - data: this.data, - arch: - '
' + - '' + - '' + - '' + - '' + - '' + - '' + - '
' + - '
' + - '
', - mockRPC: function (route) { - if (route === '/sale_product_configurator/configure') { - assert.ok(true); - return Promise.resolve('
plop
'); - } - return this._super.apply(this, arguments); - } - }); - await testUtils.dom.click(product_configurator_form.$('.o_input')); - await testUtils.dom.click($("ul.ui-autocomplete li a:contains('Customizable Desk')").mouseenter()); - product_configurator_form.destroy(); - }); - QUnit.test('drag and drop rows containing product_configurator many2one', async function (assert) { assert.expect(4); @@ -305,5 +269,3 @@ QUnit.module('Product Configurator', { form.destroy(); }); }); - -}); diff --git a/addons/sale_product_configurator/views/templates.xml b/addons/sale_product_configurator/views/templates.xml index 4ae24d35d17..f15584272bc 100644 --- a/addons/sale_product_configurator/views/templates.xml +++ b/addons/sale_product_configurator/views/templates.xml @@ -147,9 +147,11 @@ }'/> -

Available Options:

- - + +

Available Options:

+ + + diff --git a/addons/sale_product_configurator/wizard/__init__.py b/addons/sale_product_configurator/wizard/__init__.py deleted file mode 100644 index a0eaada809c..00000000000 --- a/addons/sale_product_configurator/wizard/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -# -*- coding: utf-8 -*- -# Part of Odoo. See LICENSE file for full copyright and licensing details. - -from . import sale_product_configurator diff --git a/addons/sale_product_configurator/wizard/sale_product_configurator.py b/addons/sale_product_configurator/wizard/sale_product_configurator.py deleted file mode 100644 index 4d636383471..00000000000 --- a/addons/sale_product_configurator/wizard/sale_product_configurator.py +++ /dev/null @@ -1,21 +0,0 @@ -# -*- coding: utf-8 -*- -# Part of Odoo. See LICENSE file for full copyright and licensing details. - -from odoo import models, fields - - -class SaleProductConfigurator(models.TransientModel): - _name = 'sale.product.configurator' - _description = 'Sale Product Configurator' - - product_template_id = fields.Many2one( - 'product.template', string="Product", - required=True, domain=[('sale_ok', '=', True), ('attribute_line_ids.value_ids', '!=', False)]) - quantity = fields.Integer('Quantity') - pricelist_id = fields.Many2one('product.pricelist', 'Pricelist', readonly=True) - product_template_attribute_value_ids = fields.Many2many( - 'product.template.attribute.value', 'product_configurator_template_attribute_value_rel', string='Attribute Values', readonly=True) - product_custom_attribute_value_ids = fields.Many2many( - 'product.attribute.custom.value', 'product_configurator_custom_attribute_value_rel', string="Custom Values") - product_no_variant_attribute_value_ids = fields.Many2many( - 'product.template.attribute.value', 'product_configurator_no_variant_attribute_value_rel', string="Extra Values") diff --git a/addons/sale_product_configurator/wizard/sale_product_configurator_views.xml b/addons/sale_product_configurator/wizard/sale_product_configurator_views.xml deleted file mode 100644 index 7c6a4ac275e..00000000000 --- a/addons/sale_product_configurator/wizard/sale_product_configurator_views.xml +++ /dev/null @@ -1,40 +0,0 @@ - - - - sale_product_configurator.product.configurator.view.form - sale.product.configurator - -
- - - - - - - - - - - - - - - - -
-
-
-
-
- - - Configure a product - sale.product.configurator - form - new - - -
diff --git a/addons/sale_product_matrix/static/src/js/product_matrix_configurator.js b/addons/sale_product_matrix/static/src/js/product_matrix_configurator.js index eb43f416e22..e9ff37cb564 100644 --- a/addons/sale_product_matrix/static/src/js/product_matrix_configurator.js +++ b/addons/sale_product_matrix/static/src/js/product_matrix_configurator.js @@ -1,5 +1,5 @@ -odoo.define('sale_product_matrix.product_configurator', function (require) { -var ProductConfiguratorWidget = require('sale_product_configurator.product_configurator'); +/** @odoo-module */ +import { ProductConfiguratorWidget } from "@sale_product_configurator/js/product_configurator_widget"; /** * Extension of the ProductConfiguratorWidget to support product configuration @@ -10,15 +10,14 @@ var ProductConfiguratorWidget = require('sale_product_configurator.product_confi * */ ProductConfiguratorWidget.include({ - /** * @override */ - _openConfigurator: function (result, productTemplateId, dataPointId) { + _openConfigurator: function (result, productTemplateId, dataPointId) { var self = this; var mode = result.mode; this._super.apply(this, arguments).then(function (configuratorOpened) { - if (!configuratorOpened && mode === 'matrix') { + if (!configuratorOpened && mode === "matrix") { self._openGridConfigurator(productTemplateId, dataPointId); return Promise.resolve(true); } @@ -28,9 +27,9 @@ ProductConfiguratorWidget.include({ _openGridConfigurator: function (productTemplateId, dataPointId, edit) { var attribs = edit ? this._getPTAVS() : []; - this.trigger_up('open_matrix', { + this.trigger_up("open_matrix", { product_template_id: productTemplateId, - model: 'sale.order', + model: "sale.order", dataPointId: dataPointId, edit: edit, editedCellAttributes: attribs, @@ -38,42 +37,26 @@ ProductConfiguratorWidget.include({ }, _onEditProductConfiguration: function () { + const { _super } = this; if (!this.recordData.is_configurable_product) { // if line should be edited by another configurator // or simply inline. - this._super.apply(this, arguments); + _super.apply(this, arguments); return; } var self = this; var productTemplateId = this.recordData.product_template_id.data.id; this._rpc({ - model: 'product.template', - method: 'read', - args: [productTemplateId, ['product_add_mode']], + model: "product.template", + method: "read", + args: [productTemplateId, ["product_add_mode"]], }).then(function (result) { - if (result && result[0].product_add_mode === 'matrix') { + if (result && result[0].product_add_mode === "matrix") { self._openGridConfigurator(productTemplateId, self.dataPointID, true); } else { - self.restoreProductTemplateId = self.recordData.product_template_id; // Call super only if product_add_mode different than matrix // to avoid product configurator opening (which is the default case). - self._openProductConfigurator({ - configuratorMode: 'edit', - default_product_template_id: self.recordData.product_template_id.data.id, - default_pricelist_id: self._getPricelistId(), - default_product_template_attribute_value_ids: self._convertFromMany2Many( - self.recordData.product_template_attribute_value_ids - ), - default_product_no_variant_attribute_value_ids: self._convertFromMany2Many( - self.recordData.product_no_variant_attribute_value_ids - ), - default_product_custom_attribute_value_ids: self._convertFromOne2Many( - self.recordData.product_custom_attribute_value_ids - ), - default_quantity: self.recordData.product_uom_qty - }, - self.dataPointID - ); + return _super.apply(self, arguments); } }); }, @@ -81,7 +64,7 @@ ProductConfiguratorWidget.include({ /** * Returns the list of attribute ids (product.template.attribute.value) * from the current SOLine. - */ + */ _getPTAVS: function () { var PTAVSIDS = []; _.each(this.recordData.product_no_variant_attribute_value_ids.res_ids, function (id) { @@ -90,9 +73,8 @@ ProductConfiguratorWidget.include({ _.each(this.recordData.product_template_attribute_value_ids.res_ids, function (id) { PTAVSIDS.push(id); }); - return PTAVSIDS.sort(function (a, b) {return a - b;}); - } - -}); - + return PTAVSIDS.sort(function (a, b) { + return a - b; + }); + }, }); diff --git a/addons/test_sale_product_configurators/static/tests/tours/product_configurator_edition_ui.js b/addons/test_sale_product_configurators/static/tests/tours/product_configurator_edition_ui.js index d4b2435307b..efbe75440c7 100644 --- a/addons/test_sale_product_configurators/static/tests/tours/product_configurator_edition_ui.js +++ b/addons/test_sale_product_configurators/static/tests/tours/product_configurator_edition_ui.js @@ -45,13 +45,13 @@ tour.register('sale_product_configurator_edition_tour', { }, { trigger: '.o_edit_product_configuration', }, { - trigger: '.configurator_container li.js_attribute_value:has(span:contains("Aluminium")) input:checked', + trigger: '.main_product li.js_attribute_value:has(span:contains("Aluminium")) input:checked', run: function (){} // check updated legs }, { trigger: 'span.oe_currency_value:contains("800")', run: function (){} // check updated price }, { - trigger: '.configurator_container span:contains("Steel")', + trigger: '.main_product span:contains("Steel")', run: function () { $('input.product_id').change(function () { if ($('.o_sale_product_configurator_edit').attr('request_count')) { @@ -63,20 +63,20 @@ tour.register('sale_product_configurator_edition_tour', { }); } }, { - trigger: '.configurator_container span:contains("Custom")', + trigger: '.main_product span:contains("Custom")', run: function () { // FIXME awa: since jquery3 update it doesn't "click" // on the element without this run (and 'run: "click"' // doesn't work either) - $('.configurator_container span:contains("Custom")').click(); + $('.main_product span:contains("Custom")').click(); } }, { - trigger: '.configurator_container .variant_custom_value', + trigger: '.main_product .variant_custom_value', run: 'text nice custom value' }, { trigger: 'input[data-value_name="Black"]', }, { - trigger: '.o_sale_product_configurator_edit[request_count="2"]', + trigger: '.product_display_name:contains("Customizable Desk (TEST) (Custom, Black)")', run: function (){} // used to sync with "get_combination_info" completion }, { trigger: '.o_sale_product_configurator_edit', @@ -93,7 +93,7 @@ tour.register('sale_product_configurator_edition_tour', { }, { trigger: '.o_edit_product_configuration', }, { - trigger: '.configurator_container .variant_custom_value', + trigger: '.main_product .variant_custom_value', run: 'text another nice custom value' }, { trigger: '.o_sale_product_configurator_edit', @@ -106,25 +106,25 @@ tour.register('sale_product_configurator_edition_tour', { }, { trigger: '.o_edit_product_configuration', }, { - trigger: '.configurator_container span:contains("Steel")', + trigger: '.main_product span:contains("Steel")', run: function () { $('input.product_id').change(function () { $('.o_sale_product_configurator_edit').attr('request_count', 1); }); } }, { - trigger: '.configurator_container span:contains("Steel")', + trigger: '.main_product span:contains("Steel")', run: function () { // FIXME awa: since jquery3 update it doesn't "click" // on the element without this run (and 'run: "click"' // doesn't work either) - $('.configurator_container span:contains("Steel")').click(); + $('.main_product span:contains("Steel")').click(); } }, { trigger: '.o_sale_product_configurator_edit[request_count="1"]', run: function (){} // used to sync with "get_combination_info" completion }, { - trigger: '.configurator_container button.js_add_cart_json:has(.fa-plus)', + trigger: '.main_product button.js_add_cart_json:has(.fa-plus)', }, { trigger: '.o_sale_product_configurator_edit', }, { diff --git a/addons/test_sale_product_configurators/static/tests/tours/product_configurator_single_custom_attribute_ui.js b/addons/test_sale_product_configurators/static/tests/tours/product_configurator_single_custom_attribute_ui.js index d1dd1aaaa0d..fcb6b9ccab8 100644 --- a/addons/test_sale_product_configurators/static/tests/tours/product_configurator_single_custom_attribute_ui.js +++ b/addons/test_sale_product_configurators/static/tests/tours/product_configurator_single_custom_attribute_ui.js @@ -45,20 +45,20 @@ tour.register('sale_product_configurator_single_custom_attribute_tour', { }, { trigger: '.o_edit_product_configuration', }, { - trigger: '.configurator_container .variant_custom_value', + trigger: '.main_product .variant_custom_value', run: function () { // check custom value initialized - if ($('.configurator_container .variant_custom_value').val() === "great single custom value") { - $('.configurator_container').addClass('tour_success_2'); + if ($('.main_product .variant_custom_value').val() === "great single custom value") { + $('.main_product').addClass('tour_success_2'); } } }, { - trigger: '.configurator_container.tour_success_2', + trigger: '.main_product.tour_success_2', run: function () { //check } }, { - trigger: '.configurator_container', + trigger: '.main_product', run: function () { window.location.href = window.location.origin + '/web'; } diff --git a/addons/website_sale_product_configurator/static/src/js/product_configurator_modal.js b/addons/website_sale_product_configurator/static/src/js/product_configurator_modal.js index 339dde30b23..274b07f83a3 100644 --- a/addons/website_sale_product_configurator/static/src/js/product_configurator_modal.js +++ b/addons/website_sale_product_configurator/static/src/js/product_configurator_modal.js @@ -1,7 +1,7 @@ odoo.define('website_sale_product_configurator.OptionalProductsModal', function (require) { "use strict"; -var OptionalProductsModal = require('sale_product_configurator.OptionalProductsModal'); +const { OptionalProductsModal } = require('@sale_product_configurator/js/product_configurator_modal'); OptionalProductsModal.include({ /** diff --git a/addons/website_sale_product_configurator/static/src/js/website_sale_options.js b/addons/website_sale_product_configurator/static/src/js/website_sale_options.js index 0c43812b4b5..a34bfcf2a3e 100644 --- a/addons/website_sale_product_configurator/static/src/js/website_sale_options.js +++ b/addons/website_sale_product_configurator/static/src/js/website_sale_options.js @@ -4,7 +4,7 @@ odoo.define('website_sale_options.website_sale', function (require) { var ajax = require('web.ajax'); var core = require('web.core'); var publicWidget = require('web.public.widget'); -var OptionalProductsModal = require('sale_product_configurator.OptionalProductsModal'); +var { OptionalProductsModal } = require('@sale_product_configurator/js/product_configurator_modal'); require('website_sale.website_sale'); var _t = core._t;