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;