From 220f5ae53f66400320b697c778039b0585da8fc4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Warnon?= Date: Wed, 6 Mar 2019 07:24:28 +0000 Subject: [PATCH] [IMP] sale_product_configurator: improve p. configurator Task #1891970 Original p. configurator commit d3530eb Purpose ======= - The p. configurator now comes with a widget that is "o2m" like in the SO lines view. The widget is only used on the added "product_template_id" field on the SO line. This widget controls the opening of the configuration window and removes the need of a "Configure a product" button. The "SectionAndNoteListRenderer" is now cleaned from p. configurator specific code. The widget is also responsible for handling the configuration provided by the p. configurator form and applying it on the SO line with a 'field_changed' event that updates all the necessary fields. - Added support for 'MULTI' and 'DELETE_ALL' operations on X2Many fields in basic_model.js - 'MULTI' allows to batch multiple operations at once - 'DELETE_ALL' behaves like 'DELETE' with all the current data of the field Spec ======= - remove "configure a product" from sale order lines When the product configurator is active, replace the product_product_id by a product_template_id in the sale order line. When we select a template without variants, it sets the variant automatically, but when we select a product template having variants, it opens the configurator dialog. - add a widget to modify the product configuration in the sale order line (next to product template field) - UX improvements: - Invert image and configuration in the main screen - If the product doesn't have an image, hide it instead of showing the placeholder (only for first screen?) - new independent option in sales settings to activate product configurator. (same in e-commerce) - demo data: Change demo data to set Customizable Desk & Conference Chair as make to order - If only one attribute value that is Custom, don't display radio, selection or even color box --- .../src/js/section_and_note_fields_backend.js | 118 ----- .../src/js/event_configurator_test_ui.js | 3 +- .../src/js/event_configurator_widget.js | 115 +---- .../src/js/event_configurator_widget_mixin.js | 124 +++++ addons/event_sale/views/assets.xml | 1 + .../__init__.py | 1 + .../__manifest__.py | 19 + .../src/js/event_configurator_test_ui.js | 27 ++ .../src/js/product_configurator_widget.js | 43 ++ .../views/assets.xml | 9 + addons/product/models/res_config_settings.py | 2 +- addons/sale/models/product_product.py | 2 +- addons/sale/models/product_template.py | 3 + addons/sale/models/sale.py | 15 +- .../src/js/product_configurator_mixin.js | 77 ++-- addons/sale/static/src/js/tour.js | 11 +- .../static/src/scss/product_configurator.scss | 13 +- .../sale/views/res_config_settings_views.xml | 2 +- addons/sale/views/sale_views.xml | 3 - addons/sale/views/variant_templates.xml | 23 +- .../controllers/main.py | 9 +- .../models/__init__.py | 4 + .../models/product.py | 37 +- .../models/sale_order_line.py | 12 + .../src/js/product_configurator_controller.js | 183 ++++++-- .../src/js/product_configurator_modal.js | 69 ++- .../src/js/product_configurator_renderer.js | 63 +++ .../src/js/product_configurator_view.js | 2 +- .../src/js/product_configurator_widget.js | 434 ++++++++++++++++++ .../static/src/js/tour.js | 18 + .../src/scss/product_configurator_widget.scss | 9 + .../static/tests/product_configurator.test.js | 406 +++++++++------- .../product_configurator_advanced_test_ui.js | 17 +- .../product_configurator_edition_test_ui.js | 160 +++++++ ..._configurator_optional_products_test_ui.js | 51 ++ .../product_configurator_pricelist_test_ui.js | 21 +- ...gurator_single_custom_attribute_test_ui.js | 79 ++++ .../tests/product_configurator_test_ui.js | 31 +- .../test_sale_product_configurator_ui.py | 20 +- .../views/assets.xml | 1 + .../views/sale_views.xml | 41 ++ .../views/templates.xml | 92 ++-- .../wizard/__init__.py | 4 + .../wizard/sale_product_configurator.py | 9 +- .../sale_product_configurator_views.xml | 22 +- .../static/src/js/views/basic/basic_model.js | 53 ++- .../static/tests/views/basic_model_tests.js | 161 +++++++ .../static/src/js/website_sale.js | 5 +- .../views/res_config_settings_views.xml | 6 +- addons/website_sale/views/templates.xml | 6 +- .../src/js/product_configurator_modal.js | 8 +- .../test_main_flows/static/src/js/tour.js | 75 ++- 52 files changed, 2087 insertions(+), 632 deletions(-) create mode 100644 addons/event_sale/static/src/js/event_configurator_widget_mixin.js create mode 100644 addons/event_sale_product_configurator/__init__.py create mode 100644 addons/event_sale_product_configurator/__manifest__.py create mode 100644 addons/event_sale_product_configurator/static/src/js/event_configurator_test_ui.js create mode 100644 addons/event_sale_product_configurator/static/src/js/product_configurator_widget.js create mode 100644 addons/event_sale_product_configurator/views/assets.xml create mode 100644 addons/sale_product_configurator/models/__init__.py create mode 100644 addons/sale_product_configurator/models/sale_order_line.py create mode 100644 addons/sale_product_configurator/static/src/js/product_configurator_widget.js create mode 100644 addons/sale_product_configurator/static/src/js/tour.js create mode 100644 addons/sale_product_configurator/static/src/scss/product_configurator_widget.scss create mode 100644 addons/sale_product_configurator/static/tests/product_configurator_edition_test_ui.js create mode 100644 addons/sale_product_configurator/static/tests/product_configurator_optional_products_test_ui.js create mode 100644 addons/sale_product_configurator/static/tests/product_configurator_single_custom_attribute_test_ui.js create mode 100644 addons/sale_product_configurator/wizard/__init__.py diff --git a/addons/account/static/src/js/section_and_note_fields_backend.js b/addons/account/static/src/js/section_and_note_fields_backend.js index 26328cc045b..1f232ac0db8 100644 --- a/addons/account/static/src/js/section_and_note_fields_backend.js +++ b/addons/account/static/src/js/section_and_note_fields_backend.js @@ -6,9 +6,6 @@ odoo.define('account.section_and_note_backend', function (require) { // [UPDATED] now also allows configuring products on sale order. "use strict"; -var pyUtils = require('web.py_utils'); -var core = require('web.core'); -var _t = core._t; var FieldChar = require('web.basic_fields').FieldChar; var FieldOne2Many = require('web.relational_fields').FieldOne2Many; var fieldRegistry = require('web.field_registry'); @@ -72,121 +69,6 @@ var SectionAndNoteListRenderer = ListRenderer.extend({ return this._super.apply(this, arguments).then(function () { self.$('> table').addClass('o_section_and_note_list_view'); }); - }, - /** - * Add support for product configurator - * - * @override - * @private - */ - _onAddRecord: function (ev) { - // we don't want the browser to navigate to a the # url - ev.preventDefault(); - - // we don't want the click to cause other effects, such as unselecting - // the row that we are creating, because it counts as a click on a tr - ev.stopPropagation(); - - // but we do want to unselect current row - var self = this; - this.unselectRow().then(function () { - var context = ev.currentTarget.dataset.context; - - var pricelistId = self._getPricelistId(); - if (context && pyUtils.py_eval(context).open_product_configurator){ - self._rpc({ - model: 'ir.model.data', - method: 'xmlid_to_res_id', - kwargs: {xmlid: 'sale.sale_product_configurator_view_form'}, - }).then(function (res_id) { - self.do_action({ - name: _t('Configure a product'), - type: 'ir.actions.act_window', - res_model: 'sale.product.configurator', - views: [[res_id, 'form']], - target: 'new', - context: { - 'default_pricelist_id': pricelistId - } - }, { - on_close: function (products) { - if (products && products !== 'special'){ - self.trigger_up('add_record', { - context: self._productsToRecords(products), - forceEditable: "bottom" , - allowWarning: true, - onSuccess: function (){ - self.unselectRow(); - } - }); - } - } - }); - }); - } else { - self.trigger_up('add_record', {context: context && [context]}); // TODO write a test, the deferred was not considered - } - }); - }, - - /** - * Will try to get the pricelist_id value from the parent sale_order form - * - * @private - * @returns {integer} pricelist_id's id - */ - _getPricelistId: function () { - var saleOrderForm = this.getParent() && this.getParent().getParent(); - var stateData = saleOrderForm && saleOrderForm.state && saleOrderForm.state.data; - var pricelist_id = stateData.pricelist_id && stateData.pricelist_id.data && stateData.pricelist_id.data.id; - - return pricelist_id; - }, - - /** - * Will map the products to appropriate record objects that are - * ready for the default_get - * - * @private - * @param {Array} products The products to transform into records - */ - _productsToRecords: function (products) { - var records = []; - _.each(products, function (product){ - var record = { - default_product_id: product.product_id, - default_product_uom_qty: product.quantity - }; - - if (product.no_variant_attribute_values) { - var default_product_no_variant_attribute_values = []; - _.each(product.no_variant_attribute_values, function (attribute_value) { - default_product_no_variant_attribute_values.push( - [4, parseInt(attribute_value.value)] - ); - }); - record['default_product_no_variant_attribute_value_ids'] - = default_product_no_variant_attribute_values; - } - - if (product.product_custom_attribute_values) { - var default_custom_attribute_values = []; - _.each(product.product_custom_attribute_values, function (attribute_value) { - default_custom_attribute_values.push( - [0, 0, { - attribute_value_id: attribute_value.attribute_value_id, - custom_value: attribute_value.custom_value - }] - ); - }); - record['default_product_custom_attribute_value_ids'] - = default_custom_attribute_values; - } - - records.push(record); - }); - - return records; } }); diff --git a/addons/event_sale/static/src/js/event_configurator_test_ui.js b/addons/event_sale/static/src/js/event_configurator_test_ui.js index 9be91f9c170..2a748e7b865 100644 --- a/addons/event_sale/static/src/js/event_configurator_test_ui.js +++ b/addons/event_sale/static/src/js/event_configurator_test_ui.js @@ -27,7 +27,8 @@ tour.register('event_configurator_tour', { var keyDownEvent = jQuery.Event("keydown"); keyDownEvent.which = 42; $input.trigger(keyDownEvent); - } + }, + id: 'product_selection_step' }, { trigger: 'ul.ui-autocomplete a:contains("EVENT")', run: 'click' diff --git a/addons/event_sale/static/src/js/event_configurator_widget.js b/addons/event_sale/static/src/js/event_configurator_widget.js index f03a8366daa..5324a1fc16b 100644 --- a/addons/event_sale/static/src/js/event_configurator_widget.js +++ b/addons/event_sale/static/src/js/event_configurator_widget.js @@ -2,8 +2,7 @@ odoo.define('event.configurator', function (require) { var relationalFields = require('web.relational_fields'); var FieldsRegistry = require('web.field_registry'); -var core = require('web.core'); -var _t = core._t; +var EventConfiguratorWidgetMixin = require('event_sale.EventConfiguratorWidgetMixin'); /** * The event configurator widget is a simple FieldMany2One that adds the capability @@ -15,38 +14,21 @@ var _t = core._t; * * !!! It should only be used on a product_id field !!! */ -var EventConfiguratorWidget = relationalFields.FieldMany2One.extend({ - events: _.extend({}, relationalFields.FieldMany2One.prototype.events, { - 'click .o_event_sale_js_event_configurator_edit': '_onEditEventConfiguration' - }), +var EventConfiguratorWidget = relationalFields.FieldMany2One.extend(EventConfiguratorWidgetMixin, { + events: _.extend({}, + relationalFields.FieldMany2One.prototype.events, + EventConfiguratorWidgetMixin.events + ), /** - * This method will check if the current product set on the SO line is a configurable event. - * -> If so, we add a 'Edit Configuration' button next to the dropdown. - * + * @see _addEventConfigurationEditButton for more info * @override */ start: function () { - var prom = this._super.apply(this, arguments); - - var $inputDropdown = this.$('.o_input_dropdown'); - - if (this.recordData.event_ok && - $inputDropdown.length !== 0 && - this.$('.o_event_sale_js_event_configurator_edit').length === 0) { - var $editConfigurationButton = $('