From eb1a53d92a5ef6131a845d22a1137bc4f1fb6604 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexandre=20D=2E=20D=C3=ADaz?= Date: Tue, 16 Mar 2021 20:09:18 +0100 Subject: [PATCH] [IMP] web_widget_one2many_product_picker: black, isort, prettier --- .../addons/web_widget_one2many_product_picker | 1 + .../setup.py | 6 + .../__manifest__.py | 31 +- .../models/product_product.py | 17 +- .../static/src/js/tools.js | 22 +- .../quick_create_form.js | 63 +- .../quick_create_form_view.js | 182 ++- .../quick_modif_price_form.js | 280 ++--- .../quick_modif_price_form_view.js | 133 ++- .../js/views/One2ManyProductPicker/record.js | 305 ++--- .../views/One2ManyProductPicker/renderer.js | 1060 +++++++++-------- .../static/src/js/views/basic_model.js | 56 +- .../static/src/js/views/basic_view.js | 23 +- .../widgets/field_one2many_product_picker.js | 290 +++-- .../static/src/scss/main_variables.scss | 6 +- .../src/scss/one2many_product_picker.scss | 28 +- .../src/xml/one2many_product_picker.xml | 142 ++- .../one2many_product_picker_quick_create.xml | 14 +- ...2many_product_picker_quick_modif_price.xml | 16 +- .../static/tests/widget_tests.js | 324 +++-- .../templates/assets.xml | 80 +- 21 files changed, 1698 insertions(+), 1381 deletions(-) create mode 120000 setup/web_widget_one2many_product_picker/odoo/addons/web_widget_one2many_product_picker create mode 100644 setup/web_widget_one2many_product_picker/setup.py diff --git a/setup/web_widget_one2many_product_picker/odoo/addons/web_widget_one2many_product_picker b/setup/web_widget_one2many_product_picker/odoo/addons/web_widget_one2many_product_picker new file mode 120000 index 000000000..fb1be7c81 --- /dev/null +++ b/setup/web_widget_one2many_product_picker/odoo/addons/web_widget_one2many_product_picker @@ -0,0 +1 @@ +../../../../web_widget_one2many_product_picker \ No newline at end of file diff --git a/setup/web_widget_one2many_product_picker/setup.py b/setup/web_widget_one2many_product_picker/setup.py new file mode 100644 index 000000000..28c57bb64 --- /dev/null +++ b/setup/web_widget_one2many_product_picker/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +) diff --git a/web_widget_one2many_product_picker/__manifest__.py b/web_widget_one2many_product_picker/__manifest__.py index 5842edae2..75eecd4ca 100644 --- a/web_widget_one2many_product_picker/__manifest__.py +++ b/web_widget_one2many_product_picker/__manifest__.py @@ -2,23 +2,16 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). { - 'name': 'Web Widget One2Many Product Picker', - 'summary': 'Widget to select products on one2many fields', - 'version': '12.0.2.3.0', - 'category': 'Website', - 'author': "Tecnativa, " - "Odoo Community Association (OCA)", - 'website': 'https://www.tecnativa.com', - 'license': 'AGPL-3', - 'depends': [ - 'product', - ], - 'data': [ - 'templates/assets.xml', - ], - 'qweb': [ - 'static/src/xml/one2many_product_picker.xml', - ], - 'installable': True, - 'auto_install': False, + "name": "Web Widget One2Many Product Picker", + "summary": "Widget to select products on one2many fields", + "version": "12.0.2.3.0", + "category": "Website", + "author": "Tecnativa, " "Odoo Community Association (OCA)", + "website": "https://www.tecnativa.com", + "license": "AGPL-3", + "depends": ["product",], + "data": ["templates/assets.xml",], + "qweb": ["static/src/xml/one2many_product_picker.xml",], + "installable": True, + "auto_install": False, } diff --git a/web_widget_one2many_product_picker/models/product_product.py b/web_widget_one2many_product_picker/models/product_product.py index 6beab967a..f27dc8c7f 100644 --- a/web_widget_one2many_product_picker/models/product_product.py +++ b/web_widget_one2many_product_picker/models/product_product.py @@ -4,23 +4,23 @@ from odoo import api, fields, models, tools class ProductProduct(models.Model): - _inherit = 'product.product' + _inherit = "product.product" image_variant_medium = fields.Binary( "Variant Image Medium (Computed)", - compute='_compute_variant_image', + compute="_compute_variant_image", help="This field holds the image used as image for the product variant" - "or product image medium, limited to 512x512px.", + "or product image medium, limited to 512x512px.", ) image_variant_big = fields.Binary( "Variant Image Big (Computed)", - compute='_compute_variant_image', + compute="_compute_variant_image", help="This field holds the image used as image for the product variant" - "or product image, limited to 1024x1024px.", + "or product image, limited to 1024x1024px.", ) - @api.depends('image_variant', 'product_tmpl_id.image') + @api.depends("image_variant", "product_tmpl_id.image") def _compute_variant_image(self): for record in self: if record.image_variant: @@ -28,8 +28,9 @@ class ProductProduct(models.Model): record.image_variant, return_big=False, return_small=False, - avoid_resize_medium=True) - record.image_variant_medium = resized_images['image_medium'] + avoid_resize_medium=True, + ) + record.image_variant_medium = resized_images["image_medium"] record.image_variant_big = record.image_variant else: record.image_variant_medium = record.product_tmpl_id.image_medium diff --git a/web_widget_one2many_product_picker/static/src/js/tools.js b/web_widget_one2many_product_picker/static/src/js/tools.js index 34a9c018a..361aa3bbb 100644 --- a/web_widget_one2many_product_picker/static/src/js/tools.js +++ b/web_widget_one2many_product_picker/static/src/js/tools.js @@ -1,8 +1,6 @@ // Copyright 2020 Tecnativa - Alexandre Díaz // License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -odoo.define("web_widget_one2many_product_picker.tools", function ( - require -) { +odoo.define("web_widget_one2many_product_picker.tools", function(require) { "use strict"; var field_utils = require("web.field_utils"); @@ -14,7 +12,7 @@ odoo.define("web_widget_one2many_product_picker.tools", function ( * @param {Number} discount * @returns {Number} */ - function priceReduce (price, discount) { + function priceReduce(price, discount) { return price * (1.0 - discount / 100.0); } @@ -28,20 +26,16 @@ odoo.define("web_widget_one2many_product_picker.tools", function ( * @param {Object} data * @returns {String} */ - function monetary (value, field_info, currency_field, data) { - return field_utils.format.monetary( - value, - field_info, - { - data: data, - currency_field: currency_field, - field_digits: true, - }); + function monetary(value, field_info, currency_field, data) { + return field_utils.format.monetary(value, field_info, { + data: data, + currency_field: currency_field, + field_digits: true, + }); } return { monetary: monetary, priceReduce: priceReduce, }; - }); diff --git a/web_widget_one2many_product_picker/static/src/js/views/One2ManyProductPicker/quick_create_form.js b/web_widget_one2many_product_picker/static/src/js/views/One2ManyProductPicker/quick_create_form.js index 8cc47226e..478b0622d 100644 --- a/web_widget_one2many_product_picker/static/src/js/views/One2ManyProductPicker/quick_create_form.js +++ b/web_widget_one2many_product_picker/static/src/js/views/One2ManyProductPicker/quick_create_form.js @@ -1,6 +1,6 @@ // Copyright 2020 Tecnativa - Alexandre Díaz // License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -odoo.define("web_widget_one2many_product_picker.ProductPickerQuickCreateForm", function ( +odoo.define("web_widget_one2many_product_picker.ProductPickerQuickCreateForm", function( require ) { "use strict"; @@ -8,9 +8,8 @@ odoo.define("web_widget_one2many_product_picker.ProductPickerQuickCreateForm", f var core = require("web.core"); var Widget = require("web.Widget"); var widgetRegistry = require("web.widget_registry"); - var ProductPickerQuickCreateFormView = require( - "web_widget_one2many_product_picker.ProductPickerQuickCreateFormView" - ).ProductPickerQuickCreateFormView; + var ProductPickerQuickCreateFormView = require("web_widget_one2many_product_picker.ProductPickerQuickCreateFormView") + .ProductPickerQuickCreateFormView; var qweb = core.qweb; @@ -30,7 +29,7 @@ odoo.define("web_widget_one2many_product_picker.ProductPickerQuickCreateForm", f /** * @override */ - init: function (parent, options) { + init: function(parent, options) { this._super.apply(this, arguments); this.state = options.state; this.main_state = options.main_state; @@ -50,7 +49,7 @@ odoo.define("web_widget_one2many_product_picker.ProductPickerQuickCreateForm", f /** * @override */ - start: function () { + start: function() { var self = this; var def1 = this._super.apply(this, arguments); var form_arch = this._generateFormArch(); @@ -70,7 +69,8 @@ odoo.define("web_widget_one2many_product_picker.ProductPickerQuickCreateForm", f var refinedContext = _.extend( {}, this.main_state.getContext(), - this.nodeContext); + this.nodeContext + ); _.extend(refinedContext, this.editContext); this.formView = new ProductPickerQuickCreateFormView(fieldsView, { context: refinedContext, @@ -89,10 +89,10 @@ odoo.define("web_widget_one2many_product_picker.ProductPickerQuickCreateForm", f model: this.basicFieldParams.model, mainRecordData: this.getParent().getParent().state, }); - // if (this.id) { + // If (this.id) { // this.basicFieldParams.model.save(this.id, {savePoint: true}); // } - var def2 = this.formView.getController(this).then(function (controller) { + var def2 = this.formView.getController(this).then(function(controller) { self.controller = controller; self.$el.empty(); self.controller.appendTo(self.$el); @@ -101,7 +101,7 @@ odoo.define("web_widget_one2many_product_picker.ProductPickerQuickCreateForm", f return $.when(def1, def2); }, - on_attach_callback: function () { + on_attach_callback: function() { if (this.controller) { this.controller.autofocus(); } @@ -111,8 +111,9 @@ odoo.define("web_widget_one2many_product_picker.ProductPickerQuickCreateForm", f * @private * @returns {String} */ - _generateFormArch: function () { - var template = ""; + _generateFormArch: function() { + var template = + ""; template += this.basicFieldParams.field.views.form.arch; template += ""; qweb.add_template(template); @@ -126,12 +127,12 @@ odoo.define("web_widget_one2many_product_picker.ProductPickerQuickCreateForm", f * @private * @param {CustomEvent} evt */ - _onReloadView: function (evt) { + _onReloadView: function(evt) { this.editContext = { - 'ignore_onchanges': [this.compareKey], - 'base_record_id': evt.data.baseRecordID || null, - 'base_record_res_id': evt.data.baseRecordResID || null, - 'base_record_compare_value': evt.data.baseRecordCompareValue || null, + ignore_onchanges: [this.compareKey], + base_record_id: evt.data.baseRecordID || null, + base_record_res_id: evt.data.baseRecordResID || null, + base_record_compare_value: evt.data.baseRecordCompareValue || null, }; if (evt.data.baseRecordCompareValue === evt.data.compareValue) { @@ -140,20 +141,30 @@ odoo.define("web_widget_one2many_product_picker.ProductPickerQuickCreateForm", f this.start(); } else { var self = this; - this.getParent()._generateVirtualState({}, this.editContext).then(function (state) { - var data = {}; - data[self.compareKey] = {operation: 'ADD', id: evt.data.compareValue}; - self.basicFieldParams.model._applyChange(state.id, data).then(function () { - self.res_id = state.res_id; - self.id = state.id; - self.start(); + this.getParent() + ._generateVirtualState({}, this.editContext) + .then(function(state) { + var data = {}; + data[self.compareKey] = { + operation: "ADD", + id: evt.data.compareValue, + }; + self.basicFieldParams.model + ._applyChange(state.id, data) + .then(function() { + self.res_id = state.res_id; + self.id = state.id; + self.start(); + }); }); - }); } }, }); - widgetRegistry.add("product_picker_quick_create_form", ProductPickerQuickCreateForm); + widgetRegistry.add( + "product_picker_quick_create_form", + ProductPickerQuickCreateForm + ); return ProductPickerQuickCreateForm; }); diff --git a/web_widget_one2many_product_picker/static/src/js/views/One2ManyProductPicker/quick_create_form_view.js b/web_widget_one2many_product_picker/static/src/js/views/One2ManyProductPicker/quick_create_form_view.js index f2bf19443..8418987fa 100644 --- a/web_widget_one2many_product_picker/static/src/js/views/One2ManyProductPicker/quick_create_form_view.js +++ b/web_widget_one2many_product_picker/static/src/js/views/One2ManyProductPicker/quick_create_form_view.js @@ -1,54 +1,52 @@ // Copyright 2020 Tecnativa - Alexandre Díaz // License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -odoo.define("web_widget_one2many_product_picker.ProductPickerQuickCreateFormView", function ( - require -) { - "use strict"; +odoo.define( + "web_widget_one2many_product_picker.ProductPickerQuickCreateFormView", + function(require) { + "use strict"; - /** - * This file defines the QuickCreateFormView, an extension of the FormView that - * is used by the RecordQuickCreate in One2ManyProductPicker views. - */ + /** + * This file defines the QuickCreateFormView, an extension of the FormView that + * is used by the RecordQuickCreate in One2ManyProductPicker views. + */ - var QuickCreateFormView = require("web.QuickCreateFormView"); - var BasicModel = require("web.BasicModel"); - var core = require("web.core"); + var QuickCreateFormView = require("web.QuickCreateFormView"); + var BasicModel = require("web.BasicModel"); + var core = require("web.core"); - var qweb = core.qweb; + var qweb = core.qweb; - BasicModel.include({ - _applyOnChange: function (values, record, viewType) { - var vt = viewType || record.viewType; - // Ignore changes by record context 'ignore_onchanges' fields - if ('ignore_onchanges' in record.context) { - var ignore_changes = record.context.ignore_onchanges; - for (var index in ignore_changes) { - var field_name = ignore_changes[index]; - delete values[field_name]; + BasicModel.include({ + _applyOnChange: function(values, record, viewType) { + var vt = viewType || record.viewType; + // Ignore changes by record context 'ignore_onchanges' fields + if ("ignore_onchanges" in record.context) { + var ignore_changes = record.context.ignore_onchanges; + for (var index in ignore_changes) { + var field_name = ignore_changes[index]; + delete values[field_name]; + } + delete record.context.ignore_onchanges; } - delete record.context.ignore_onchanges; - } - return this._super(values, record, viewType); - }, - }); + return this._super(values, record, viewType); + }, + }); - var ProductPickerQuickCreateFormRenderer = - QuickCreateFormView.prototype.config.Renderer.extend( + var ProductPickerQuickCreateFormRenderer = QuickCreateFormView.prototype.config.Renderer.extend( { - /** * @override */ - start: function () { + start: function() { this.$el.addClass( - "oe_one2many_product_picker_form_view o_xxs_form_view"); + "oe_one2many_product_picker_form_view o_xxs_form_view" + ); return this._super.apply(this, arguments); }, } ); - var ProductPickerQuickCreateFormController = - QuickCreateFormView.prototype.config.Controller.extend( + var ProductPickerQuickCreateFormController = QuickCreateFormView.prototype.config.Controller.extend( { events: _.extend({}, QuickCreateFormView.prototype.events, { "click .oe_record_add": "_onClickAdd", @@ -57,7 +55,7 @@ odoo.define("web_widget_one2many_product_picker.ProductPickerQuickCreateFormView "click .oe_record_discard": "_onClickDiscard", }), - init: function (parent, model, renderer, params) { + init: function(parent, model, renderer, params) { this.compareKey = params.compareKey; this.fieldMap = params.fieldMap; this.context = params.context; @@ -68,9 +66,12 @@ odoo.define("web_widget_one2many_product_picker.ProductPickerQuickCreateFormView /** * Create or accept changes */ - auto: function () { + auto: function() { var record = this.model.get(this.handle); - if (record.context.has_changes_confirmed || typeof record.context.has_changes_confirmed === "undefined") { + if ( + record.context.has_changes_confirmed || + typeof record.context.has_changes_confirmed === "undefined" + ) { return; } var state = this._getRecordState(); @@ -87,7 +88,7 @@ odoo.define("web_widget_one2many_product_picker.ProductPickerQuickCreateFormView * - new: Is a new record * - dirty: Has changes */ - _getRecordState: function () { + _getRecordState: function() { var record = this.model.get(this.handle); var state = "record"; if (this.model.isNew(record.id)) { @@ -117,15 +118,13 @@ odoo.define("web_widget_one2many_product_picker.ProductPickerQuickCreateFormView * * @private */ - _updateButtons: function () { - this.$el.find( - ".oe_one2many_product_picker_form_buttons").remove(); + _updateButtons: function() { + this.$el.find(".oe_one2many_product_picker_form_buttons").remove(); this.$el.find(".o_form_view").append( - qweb.render( - "One2ManyProductPicker.QuickCreate.FormButtons", { - state: this._getRecordState(), - }) - ); + qweb.render("One2ManyProductPicker.QuickCreate.FormButtons", { + state: this._getRecordState(), + }) + ); if (this._disabled) { this._disableQuickCreate(); @@ -135,7 +134,7 @@ odoo.define("web_widget_one2many_product_picker.ProductPickerQuickCreateFormView /** * @private */ - _disableQuickCreate: function () { + _disableQuickCreate: function() { if (!this.$el) { return; } @@ -150,8 +149,7 @@ odoo.define("web_widget_one2many_product_picker.ProductPickerQuickCreateFormView /** * @private */ - _enableQuickCreate: function () { - + _enableQuickCreate: function() { // Allows to create again this._disabled = false; this.$el.removeClass("o_disabled"); @@ -165,7 +163,7 @@ odoo.define("web_widget_one2many_product_picker.ProductPickerQuickCreateFormView * @param {Array[String]} fields_changed * @returns {Boolean} */ - _needReloadCard: function (fields_changed) { + _needReloadCard: function(fields_changed) { for (var index in fields_changed) { var field = fields_changed[index]; if (field === this.fieldMap[this.compareKey]) { @@ -183,7 +181,7 @@ odoo.define("web_widget_one2many_product_picker.ProductPickerQuickCreateFormView * @private * @param {ChangeEvent} ev */ - _onFieldChanged: function (ev) { + _onFieldChanged: function(ev) { var fields_changed = Object.keys(ev.data.changes); if (this._needReloadCard(fields_changed)) { var field = ev.data.changes[fields_changed[0]]; @@ -197,17 +195,16 @@ odoo.define("web_widget_one2many_product_picker.ProductPickerQuickCreateFormView compareValue: new_value, }; var record = this.model.get(this.handle); - if (!('base_record_id' in record.context)) { + if (!("base_record_id" in record.context)) { var old_value = record.data[this.compareKey]; - if (typeof old_value === 'object') { + if (typeof old_value === "object") { old_value = old_value.data.id; } reload_values.baseRecordID = record.id; reload_values.baseRecordResID = record.ref; reload_values.baseRecordCompareValue = old_value; } else { - reload_values.baseRecordID = - record.context.base_record_id; + reload_values.baseRecordID = record.context.base_record_id; reload_values.baseRecordResID = record.context.base_record_res_id; reload_values.baseRecordCompareValue = @@ -237,9 +234,8 @@ odoo.define("web_widget_one2many_product_picker.ProductPickerQuickCreateFormView /** * @returns {Deferred} */ - _add: function () { + _add: function() { if (this._disabled) { - // Don't do anything if we are already creating a record return $.Deferred(); } @@ -253,15 +249,15 @@ odoo.define("web_widget_one2many_product_picker.ProductPickerQuickCreateFormView reload: true, savePoint: true, viewType: "form", - }).then(function () { + }).then(function() { var record = self.model.get(self.handle); self.trigger_up("restore_flip_card", { - success_callback: function () { + success_callback: function() { self.trigger_up("create_quick_record", { id: record.id, }); self.model.unsetDirty(self.handle); - //self._updateButtons(); + // Self._updateButtons(); self._enableQuickCreate(); }, block: true, @@ -269,9 +265,8 @@ odoo.define("web_widget_one2many_product_picker.ProductPickerQuickCreateFormView }); }, - _remove: function () { + _remove: function() { if (this._disabled) { - // Don't do anything if we are already creating a record return $.Deferred(); } @@ -284,10 +279,9 @@ odoo.define("web_widget_one2many_product_picker.ProductPickerQuickCreateFormView }); }, - _change: function () { + _change: function() { var self = this; if (this._disabled) { - // Don't do anything if we are already creating a record return $.Deferred(); } @@ -298,22 +292,21 @@ odoo.define("web_widget_one2many_product_picker.ProductPickerQuickCreateFormView var record = this.model.get(this.handle); this.trigger_up("restore_flip_card", { - success_callback: function () { + success_callback: function() { self.trigger_up("update_quick_record", { id: record.id, }); self.model.unsetDirty(self.handle); - //self._updateButtons(); + // Self._updateButtons(); self._enableQuickCreate(); }, block: true, }); }, - _discard: function () { + _discard: function() { var self = this; if (this._disabled) { - // Don't do anything if we are already creating a record return $.Deferred(); } @@ -331,7 +324,7 @@ odoo.define("web_widget_one2many_product_picker.ProductPickerQuickCreateFormView this._updateButtons(); this._enableQuickCreate(); } else { - this.update({}, {reload: false}).then(function () { + this.update({}, {reload: false}).then(function() { self.model.unsetDirty(self.handle); self.trigger_up("restore_flip_card"); self._updateButtons(); @@ -344,7 +337,7 @@ odoo.define("web_widget_one2many_product_picker.ProductPickerQuickCreateFormView * @private * @param {MouseEvent} ev */ - _onClickAdd: function (ev) { + _onClickAdd: function(ev) { ev.stopPropagation(); this._add(); }, @@ -353,7 +346,7 @@ odoo.define("web_widget_one2many_product_picker.ProductPickerQuickCreateFormView * @private * @param {MouseEvent} ev */ - _onClickRemove: function (ev) { + _onClickRemove: function(ev) { ev.stopPropagation(); this._remove(); }, @@ -362,7 +355,7 @@ odoo.define("web_widget_one2many_product_picker.ProductPickerQuickCreateFormView * @private * @param {MouseEvent} ev */ - _onClickChange: function (ev) { + _onClickChange: function(ev) { ev.stopPropagation(); this._change(); }, @@ -371,34 +364,35 @@ odoo.define("web_widget_one2many_product_picker.ProductPickerQuickCreateFormView * @private * @param {MouseEvent} ev */ - _onClickDiscard: function (ev) { + _onClickDiscard: function(ev) { ev.stopPropagation(); this._discard(); }, } ); - var ProductPickerQuickCreateFormView = QuickCreateFormView.extend({ - config: _.extend({}, QuickCreateFormView.prototype.config, { - Renderer: ProductPickerQuickCreateFormRenderer, - Controller: ProductPickerQuickCreateFormController, - }), + var ProductPickerQuickCreateFormView = QuickCreateFormView.extend({ + config: _.extend({}, QuickCreateFormView.prototype.config, { + Renderer: ProductPickerQuickCreateFormRenderer, + Controller: ProductPickerQuickCreateFormController, + }), - /** - * @override - */ - init: function (viewInfo, params) { - this._super.apply(this, arguments); - this.controllerParams.compareKey = params.compareKey; - this.controllerParams.fieldMap = params.fieldMap; - this.controllerParams.context = params.context; - this.controllerParams.mainRecordData = params.mainRecordData; - }, - }); + /** + * @override + */ + init: function(viewInfo, params) { + this._super.apply(this, arguments); + this.controllerParams.compareKey = params.compareKey; + this.controllerParams.fieldMap = params.fieldMap; + this.controllerParams.context = params.context; + this.controllerParams.mainRecordData = params.mainRecordData; + }, + }); - return { - ProductPickerQuickCreateFormRenderer: ProductPickerQuickCreateFormRenderer, - ProductPickerQuickCreateFormController: ProductPickerQuickCreateFormController, - ProductPickerQuickCreateFormView: ProductPickerQuickCreateFormView, - }; -}); + return { + ProductPickerQuickCreateFormRenderer: ProductPickerQuickCreateFormRenderer, + ProductPickerQuickCreateFormController: ProductPickerQuickCreateFormController, + ProductPickerQuickCreateFormView: ProductPickerQuickCreateFormView, + }; + } +); diff --git a/web_widget_one2many_product_picker/static/src/js/views/One2ManyProductPicker/quick_modif_price_form.js b/web_widget_one2many_product_picker/static/src/js/views/One2ManyProductPicker/quick_modif_price_form.js index e014707d5..4520bd5f0 100644 --- a/web_widget_one2many_product_picker/static/src/js/views/One2ManyProductPicker/quick_modif_price_form.js +++ b/web_widget_one2many_product_picker/static/src/js/views/One2ManyProductPicker/quick_modif_price_form.js @@ -1,152 +1,158 @@ // Copyright 2020 Tecnativa - Alexandre Díaz // License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -odoo.define("web_widget_one2many_product_picker.ProductPickerQuickModifPriceForm", function ( - require -) { - "use strict"; +odoo.define( + "web_widget_one2many_product_picker.ProductPickerQuickModifPriceForm", + function(require) { + "use strict"; - var core = require("web.core"); - var Widget = require("web.Widget"); - var ProductPickerQuickModifPriceFormView = require( - "web_widget_one2many_product_picker.ProductPickerQuickModifPriceFormView" - ).ProductPickerQuickModifPriceFormView; + var core = require("web.core"); + var Widget = require("web.Widget"); + var ProductPickerQuickModifPriceFormView = require("web_widget_one2many_product_picker.ProductPickerQuickModifPriceFormView") + .ProductPickerQuickModifPriceFormView; - var qweb = core.qweb; - - /** - * This widget render a Form. Used by FieldOne2ManyProductPicker - */ - var ProductPickerQuickModifPriceForm = Widget.extend({ - className: "oe_one2many_product_picker_quick_modif_price", - xmlDependencies: [ - "/web_widget_one2many_product_picker/static/src/xml/one2many_product_picker_quick_modif_price.xml", - ], + var qweb = core.qweb; /** - * @override + * This widget render a Form. Used by FieldOne2ManyProductPicker */ - init: function (parent, options) { - this._super.apply(this, arguments); - this.state = options.state; - this.main_state = options.main_state; - this.node = options.node; - this.fields = options.fields; - this.fieldMap = options.fieldMap; - this.searchRecord = options.searchRecord; - this.fieldsInfo = options.fieldsInfo; - this.readonly = options.readonly; - this.basicFieldParams = options.basicFieldParams; - this.canEditPrice = options.canEditPrice; - this.canEditDiscount = options.canEditDiscount; - this.currencyField = options.currencyField; - this.res_id = this.state && this.state.res_id; - this.id = this.state && this.state.id; - this.editContext = {}; - }, + var ProductPickerQuickModifPriceForm = Widget.extend({ + className: "oe_one2many_product_picker_quick_modif_price", + xmlDependencies: [ + "/web_widget_one2many_product_picker/static/src/xml/one2many_product_picker_quick_modif_price.xml", + ], - /** - * @override - */ - start: function () { - var self = this; - var def1 = this._super.apply(this, arguments); - var fieldsView = { - arch: this._generateFormArch(), - fields: this.fields, - viewFields: this.fields, - base_model: this.basicFieldParams.field.relation, - type: "form", - model: this.basicFieldParams.field.relation, - }; - this.formView = new ProductPickerQuickModifPriceFormView(fieldsView, { - context: this.main_state.getContext(), - fieldMap: this.fieldMap, - modelName: this.basicFieldParams.field.relation, - userContext: this.getSession().user_context, - ids: this.res_id ? [this.res_id] : [], - currentId: this.res_id || undefined, - mode: this.res_id && this.readonly ? "readonly" : "edit", - recordID: this.id, - index: 0, - parentID: this.basicFieldParams.parentID, - default_buttons: true, - withControlPanel: false, - model: this.basicFieldParams.model, - parentRecordData: this.basicFieldParams.recordData, - currencyField: this.currencyField, - disable_autofocus: true, - }); - if (this.id) { - this.basicFieldParams.model.save(this.id, {savePoint: true}); - } - var def2 = this.formView.getController(this).then(function (controller) { - self.controller = controller; - self.$el.empty(); - self.controller.appendTo(self.$el); - }); + /** + * @override + */ + init: function(parent, options) { + this._super.apply(this, arguments); + this.state = options.state; + this.main_state = options.main_state; + this.node = options.node; + this.fields = options.fields; + this.fieldMap = options.fieldMap; + this.searchRecord = options.searchRecord; + this.fieldsInfo = options.fieldsInfo; + this.readonly = options.readonly; + this.basicFieldParams = options.basicFieldParams; + this.canEditPrice = options.canEditPrice; + this.canEditDiscount = options.canEditDiscount; + this.currencyField = options.currencyField; + this.res_id = this.state && this.state.res_id; + this.id = this.state && this.state.id; + this.editContext = {}; + }, - return $.when(def1, def2); - }, + /** + * @override + */ + start: function() { + var self = this; + var def1 = this._super.apply(this, arguments); + var fieldsView = { + arch: this._generateFormArch(), + fields: this.fields, + viewFields: this.fields, + base_model: this.basicFieldParams.field.relation, + type: "form", + model: this.basicFieldParams.field.relation, + }; + this.formView = new ProductPickerQuickModifPriceFormView(fieldsView, { + context: this.main_state.getContext(), + fieldMap: this.fieldMap, + modelName: this.basicFieldParams.field.relation, + userContext: this.getSession().user_context, + ids: this.res_id ? [this.res_id] : [], + currentId: this.res_id || undefined, + mode: this.res_id && this.readonly ? "readonly" : "edit", + recordID: this.id, + index: 0, + parentID: this.basicFieldParams.parentID, + default_buttons: true, + withControlPanel: false, + model: this.basicFieldParams.model, + parentRecordData: this.basicFieldParams.recordData, + currencyField: this.currencyField, + disable_autofocus: true, + }); + if (this.id) { + this.basicFieldParams.model.save(this.id, {savePoint: true}); + } + var def2 = this.formView.getController(this).then(function(controller) { + self.controller = controller; + self.$el.empty(); + self.controller.appendTo(self.$el); + }); - /** - * @override - */ - destroy: function () { - this._super.apply(this, arguments); - }, + return $.when(def1, def2); + }, - on_attach_callback: function () { - // Do nothing - }, + /** + * @override + */ + destroy: function() { + this._super.apply(this, arguments); + }, - /** - * @private - * @returns {String} - */ - _generateFormArch: function () { - var wanted_field_states = this._getWantedFieldState(); - var template = - ""; - template += this.basicFieldParams.field.views.form.arch; - template += ""; - qweb.add_template(template); - var $arch = $(qweb.render("One2ManyProductPicker.QuickModifPrice.Form", { - field_map: this.fieldMap, - record_search: this.searchRecord, - })); + on_attach_callback: function() { + // Do nothing + }, - var field_names = Object.keys(wanted_field_states); - var gen_arch = "
"; - for (var index in field_names) { - var field_name = field_names[index]; - var $field = $arch.find("field[name='"+field_name+"']"); - var modifiers = - $field.attr("modifiers") ? JSON.parse($field.attr("modifiers")) : {}; - modifiers.invisible = false; - modifiers.readonly = wanted_field_states[field_name]; - $field.attr("modifiers", JSON.stringify(modifiers)); - $field.attr("invisible", "0"); - $field.attr("readonly", wanted_field_states[field_name]?"1":"0"); - gen_arch += $field[0].outerHTML; - } - gen_arch += "
"; - return gen_arch; - }, + /** + * @private + * @returns {String} + */ + _generateFormArch: function() { + var wanted_field_states = this._getWantedFieldState(); + var template = + ""; + template += this.basicFieldParams.field.views.form.arch; + template += ""; + qweb.add_template(template); + var $arch = $( + qweb.render("One2ManyProductPicker.QuickModifPrice.Form", { + field_map: this.fieldMap, + record_search: this.searchRecord, + }) + ); - /** - * This method returns the wanted fields to be displayed in the view. - * {field_name: readonly_state} - * - * @private - * @returns {Object} - */ - _getWantedFieldState: function () { - var wantedFieldState = {}; - wantedFieldState[this.fieldMap.discount] = !this.canEditDiscount; - wantedFieldState[this.fieldMap.price_unit] = !this.canEditPrice; - return wantedFieldState; - }, - }); + var field_names = Object.keys(wanted_field_states); + var gen_arch = "
"; + for (var index in field_names) { + var field_name = field_names[index]; + var $field = $arch.find("field[name='" + field_name + "']"); + var modifiers = $field.attr("modifiers") + ? JSON.parse($field.attr("modifiers")) + : {}; + modifiers.invisible = false; + modifiers.readonly = wanted_field_states[field_name]; + $field.attr("modifiers", JSON.stringify(modifiers)); + $field.attr("invisible", "0"); + $field.attr( + "readonly", + wanted_field_states[field_name] ? "1" : "0" + ); + gen_arch += $field[0].outerHTML; + } + gen_arch += "
"; + return gen_arch; + }, - return ProductPickerQuickModifPriceForm; -}); + /** + * This method returns the wanted fields to be displayed in the view. + * {field_name: readonly_state} + * + * @private + * @returns {Object} + */ + _getWantedFieldState: function() { + var wantedFieldState = {}; + wantedFieldState[this.fieldMap.discount] = !this.canEditDiscount; + wantedFieldState[this.fieldMap.price_unit] = !this.canEditPrice; + return wantedFieldState; + }, + }); + + return ProductPickerQuickModifPriceForm; + } +); diff --git a/web_widget_one2many_product_picker/static/src/js/views/One2ManyProductPicker/quick_modif_price_form_view.js b/web_widget_one2many_product_picker/static/src/js/views/One2ManyProductPicker/quick_modif_price_form_view.js index bb128b730..90ab14c1e 100644 --- a/web_widget_one2many_product_picker/static/src/js/views/One2ManyProductPicker/quick_modif_price_form_view.js +++ b/web_widget_one2many_product_picker/static/src/js/views/One2ManyProductPicker/quick_modif_price_form_view.js @@ -1,32 +1,32 @@ // Copyright 2020 Tecnativa - Alexandre Díaz // License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -odoo.define("web_widget_one2many_product_picker.ProductPickerQuickModifPriceFormView", function ( - require -) { - "use strict"; +odoo.define( + "web_widget_one2many_product_picker.ProductPickerQuickModifPriceFormView", + function(require) { + "use strict"; - /** - * This file defines the QuickCreateFormView, an extension of the FormView that - * is used by the RecordQuickCreate in One2ManyProductPicker views. - */ + /** + * This file defines the QuickCreateFormView, an extension of the FormView that + * is used by the RecordQuickCreate in One2ManyProductPicker views. + */ - var QuickCreateFormView = require("web.QuickCreateFormView"); - var core = require("web.core"); - var tools = require("web_widget_one2many_product_picker.tools"); + var QuickCreateFormView = require("web.QuickCreateFormView"); + var core = require("web.core"); + var tools = require("web_widget_one2many_product_picker.tools"); - var qweb = core.qweb; + var qweb = core.qweb; - var ProductPickerQuickModifPriceFormRenderer = - QuickCreateFormView.prototype.config.Renderer.extend( + var ProductPickerQuickModifPriceFormRenderer = QuickCreateFormView.prototype.config.Renderer.extend( { /** * @override */ - start: function () { + start: function() { var self = this; this.$el.addClass( - "oe_one2many_product_picker_form_view o_xxs_form_view"); - return this._super.apply(this, arguments).then(function () { + "oe_one2many_product_picker_form_view o_xxs_form_view" + ); + return this._super.apply(this, arguments).then(function() { self._appendPrice(); self._appendButtons(); }); @@ -35,21 +35,22 @@ odoo.define("web_widget_one2many_product_picker.ProductPickerQuickModifPriceForm /** * @private */ - _appendButtons: function () { - this.$el.find( - ".oe_one2many_product_picker_form_buttons").remove(); + _appendButtons: function() { + this.$el.find(".oe_one2many_product_picker_form_buttons").remove(); this.$el.append( qweb.render( - "One2ManyProductPicker.QuickModifPrice.FormButtons", { + "One2ManyProductPicker.QuickModifPrice.FormButtons", + { mode: this.mode, - }) - ); + } + ) + ); }, /** * @private */ - _appendPrice: function () { + _appendPrice: function() { this.$el.find(".oe_price").remove(); this.$el.append( qweb.render("One2ManyProductPicker.QuickModifPrice.Price") @@ -58,8 +59,7 @@ odoo.define("web_widget_one2many_product_picker.ProductPickerQuickModifPriceForm } ); - var ProductPickerQuickModifPriceFormController = - QuickCreateFormView.prototype.config.Controller.extend( + var ProductPickerQuickModifPriceFormController = QuickCreateFormView.prototype.config.Controller.extend( { events: _.extend({}, QuickCreateFormView.prototype.events, { "click .oe_record_change": "_onClickChange", @@ -69,7 +69,7 @@ odoo.define("web_widget_one2many_product_picker.ProductPickerQuickModifPriceForm /** * @override */ - init: function (parent, model, renderer, params) { + init: function(parent, model, renderer, params) { this.fieldMap = params.fieldMap; this.context = params.context; this._super.apply(this, arguments); @@ -80,9 +80,9 @@ odoo.define("web_widget_one2many_product_picker.ProductPickerQuickModifPriceForm /** * @override */ - start: function () { + start: function() { var self = this; - return this._super.apply(this, arguments).then(function () { + return this._super.apply(this, arguments).then(function() { self._updatePrice(); }); }, @@ -90,7 +90,7 @@ odoo.define("web_widget_one2many_product_picker.ProductPickerQuickModifPriceForm /** * @override */ - _onFieldChanged: function () { + _onFieldChanged: function() { this._super.apply(this, arguments); this._updatePrice(); }, @@ -98,26 +98,28 @@ odoo.define("web_widget_one2many_product_picker.ProductPickerQuickModifPriceForm /** * @private */ - _updatePrice: function () { + _updatePrice: function() { var record = this.model.get(this.handle); var price_reduce = tools.priceReduce( record.data[this.fieldMap.price_unit], - record.data[this.fieldMap.discount]); - this.renderer.$el.find(".oe_price").html( - tools.monetary( - price_reduce, - this.getParent().state.fields[this.fieldMap.price_unit], - this.currencyField, - record - ) + record.data[this.fieldMap.discount] ); + this.renderer.$el + .find(".oe_price") + .html( + tools.monetary( + price_reduce, + this.getParent().state.fields[this.fieldMap.price_unit], + this.currencyField, + record + ) + ); }, /** * @private */ - _disableQuickCreate: function () { - + _disableQuickCreate: function() { // Ensures that the record won't be created twice this._disabled = true; this.$el.addClass("o_disabled"); @@ -129,8 +131,7 @@ odoo.define("web_widget_one2many_product_picker.ProductPickerQuickModifPriceForm /** * @private */ - _enableQuickCreate: function () { - + _enableQuickCreate: function() { // Allows to create again this._disabled = false; this.$el.removeClass("o_disabled"); @@ -143,7 +144,7 @@ odoo.define("web_widget_one2many_product_picker.ProductPickerQuickModifPriceForm * @private * @param {MouseEvent} ev */ - _onClickChange: function (ev) { + _onClickChange: function(ev) { var self = this; ev.stopPropagation(); this.model.updateRecordContext(this.handle, { @@ -160,7 +161,7 @@ odoo.define("web_widget_one2many_product_picker.ProductPickerQuickModifPriceForm reload: true, savePoint: true, viewType: "form", - }).then(function () { + }).then(function() { self._enableQuickCreate(); var record = self.model.get(self.handle); self.model.unsetDirty(self.handle); @@ -173,7 +174,6 @@ odoo.define("web_widget_one2many_product_picker.ProductPickerQuickModifPriceForm this.getParent().destroy(); } } else { - // If is a "normal" record, update it var record = this.model.get(this.handle); this.trigger_up("update_quick_record", { @@ -188,7 +188,7 @@ odoo.define("web_widget_one2many_product_picker.ProductPickerQuickModifPriceForm * @private * @param {MouseEvent} ev */ - _onClickDiscard: function (ev) { + _onClickDiscard: function(ev) { ev.stopPropagation(); this.model.discardChanges(this.handle, { rollback: true, @@ -202,24 +202,25 @@ odoo.define("web_widget_one2many_product_picker.ProductPickerQuickModifPriceForm } ); - var ProductPickerQuickModifPriceFormView = QuickCreateFormView.extend({ - config: _.extend({}, QuickCreateFormView.prototype.config, { - Renderer: ProductPickerQuickModifPriceFormRenderer, - Controller: ProductPickerQuickModifPriceFormController, - }), + var ProductPickerQuickModifPriceFormView = QuickCreateFormView.extend({ + config: _.extend({}, QuickCreateFormView.prototype.config, { + Renderer: ProductPickerQuickModifPriceFormRenderer, + Controller: ProductPickerQuickModifPriceFormController, + }), - init: function (viewInfo, params) { - this._super.apply(this, arguments); - this.controllerParams.fieldMap = params.fieldMap; - this.controllerParams.context = params.context; - this.controllerParams.parentRecordData = params.parentRecordData; - this.controllerParams.currencyField = params.currencyField; - }, - }); + init: function(viewInfo, params) { + this._super.apply(this, arguments); + this.controllerParams.fieldMap = params.fieldMap; + this.controllerParams.context = params.context; + this.controllerParams.parentRecordData = params.parentRecordData; + this.controllerParams.currencyField = params.currencyField; + }, + }); - return { - ProductPickerQuickModifPriceFormRenderer: ProductPickerQuickModifPriceFormRenderer, - ProductPickerQuickModifPriceFormController: ProductPickerQuickModifPriceFormController, - ProductPickerQuickModifPriceFormView: ProductPickerQuickModifPriceFormView, - }; -}); + return { + ProductPickerQuickModifPriceFormRenderer: ProductPickerQuickModifPriceFormRenderer, + ProductPickerQuickModifPriceFormController: ProductPickerQuickModifPriceFormController, + ProductPickerQuickModifPriceFormView: ProductPickerQuickModifPriceFormView, + }; + } +); diff --git a/web_widget_one2many_product_picker/static/src/js/views/One2ManyProductPicker/record.js b/web_widget_one2many_product_picker/static/src/js/views/One2ManyProductPicker/record.js index f65ecc85b..a0473007d 100644 --- a/web_widget_one2many_product_picker/static/src/js/views/One2ManyProductPicker/record.js +++ b/web_widget_one2many_product_picker/static/src/js/views/One2ManyProductPicker/record.js @@ -1,7 +1,7 @@ /* global py */ // Copyright 2020 Tecnativa - Alexandre Díaz // License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -odoo.define("web_widget_one2many_product_picker.One2ManyProductPickerRecord", function ( +odoo.define("web_widget_one2many_product_picker.One2ManyProductPickerRecord", function( require ) { "use strict"; @@ -11,9 +11,8 @@ odoo.define("web_widget_one2many_product_picker.One2ManyProductPickerRecord", fu var Domain = require("web.Domain"); var widgetRegistry = require("web.widget_registry"); var tools = require("web_widget_one2many_product_picker.tools"); - var ProductPickerQuickModifPriceForm = require( - "web_widget_one2many_product_picker.ProductPickerQuickModifPriceForm"); - var FieldManagerMixin = require('web.FieldManagerMixin'); + var ProductPickerQuickModifPriceForm = require("web_widget_one2many_product_picker.ProductPickerQuickModifPriceForm"); + var FieldManagerMixin = require("web.FieldManagerMixin"); var qweb = core.qweb; var _t = core._t; @@ -33,7 +32,7 @@ odoo.define("web_widget_one2many_product_picker.One2ManyProductPickerRecord", fu /** * @override */ - init: function (parent, state, options) { + init: function(parent, state, options) { this._super(parent); this.options = options; this.subWidgets = {}; @@ -52,44 +51,43 @@ odoo.define("web_widget_one2many_product_picker.One2ManyProductPickerRecord", fu * * @returns {Object} */ - generateVirtualState: function () { + generateVirtualState: function() { return this._generateVirtualState().then(this.recreate.bind(this)); }, /** * @override */ - start: function () { + start: function() { return $.when(this._super.apply(this, arguments), this._render()); }, /** * @override */ - on_attach_callback: function () { + on_attach_callback: function() { _.invoke(this.subWidgets, "on_attach_callback"); }, /** * @override */ - on_detach_callback: function () { + on_detach_callback: function() { _.invoke(this.subWidgets, "on_detach_callback"); }, /** * @override */ - destroy: function () { - this.$card.off("") + destroy: function() { + this.$card.off(""); this._super.apply(this, arguments); }, /** * @override */ - update: function (record) { - + update: function(record) { // Detach the widgets because the record will empty its $el, which // will remove all event handlers on its descendants, and we want // to keep those handlers alive as we will re-use these widgets @@ -104,7 +102,7 @@ odoo.define("web_widget_one2many_product_picker.One2ManyProductPickerRecord", fu * * @param {Object} state */ - recreate: function (state) { + recreate: function(state) { if (state) { this._setState(state); } @@ -127,11 +125,12 @@ odoo.define("web_widget_one2many_product_picker.One2ManyProductPickerRecord", fu * @param {String} field_name * @returns {String} */ - _getImageUrl: function (product_id, field_name) { + _getImageUrl: function(product_id, field_name) { return _.str.sprintf( "/web/image/product.product/%d/%s", product_id, - field_name); + field_name + ); }, /** @@ -140,14 +139,15 @@ odoo.define("web_widget_one2many_product_picker.One2ManyProductPickerRecord", fu * @private * @param {String} price_field */ - _getMonetaryFieldValue: function (price_field) { + _getMonetaryFieldValue: function(price_field) { var field_name = this.options.fieldMap[price_field]; var price = this.state.data[field_name]; return tools.monetary( price, this.state.fields[field_name], this.options.currencyField, - this.state.data); + this.state.data + ); }, /** @@ -155,7 +155,7 @@ odoo.define("web_widget_one2many_product_picker.One2ManyProductPickerRecord", fu * @param {String} d a stringified domain * @returns {Boolean} the domain evaluted with the current values */ - _computeDomain: function (d) { + _computeDomain: function(d) { return new Domain(d).compute( (this.state || this.getParent().state).evalContext ); @@ -168,8 +168,7 @@ odoo.define("web_widget_one2many_product_picker.One2ManyProductPickerRecord", fu * @param {Object} viewState * @param {Object} recordSearch */ - _setState: function (viewState, recordSearch) { - + _setState: function(viewState, recordSearch) { // No parent = product_pricker widget destroyed // So this is a 'zombie' record. Destroy it! if (!this.getParent()) { @@ -186,15 +185,15 @@ odoo.define("web_widget_one2many_product_picker.One2ManyProductPickerRecord", fu this.recordSearch = recordSearch; } var model = this.options.basicFieldParams.model; - this.is_virtual = this.state && model.isPureVirtual(this.state.id) || false; + this.is_virtual = + (this.state && model.isPureVirtual(this.state.id)) || false; }, /** * @private * @returns {Object} */ - _getQWebContext: function () { - + _getQWebContext: function() { // Using directly the 'model record' instead of the state because // the state it's a parsed version of this record that doesn't // contains the '_virtual' attribute. @@ -202,7 +201,8 @@ odoo.define("web_widget_one2many_product_picker.One2ManyProductPickerRecord", fu var record = model.get(this.state.id); return { record_search: this.recordSearch, - user_context: this.getSession() && this.getSession().user_context || {}, + user_context: + (this.getSession() && this.getSession().user_context) || {}, image: this._getImageUrl.bind(this), compute_domain: this._computeDomain.bind(this), state: this.state, @@ -212,7 +212,7 @@ odoo.define("web_widget_one2many_product_picker.One2ManyProductPickerRecord", fu show_discount: this.options.showDiscount, is_virtual: this.is_virtual, modified: record && record.context.product_picker_modified, - active_model: '', + active_model: "", auto_save: this.options.autoSave, }; }, @@ -223,7 +223,7 @@ odoo.define("web_widget_one2many_product_picker.One2ManyProductPickerRecord", fu * @private * @returns {Object} */ - _getInternalVirtualRecordContext: function () { + _getInternalVirtualRecordContext: function() { var context = {}; context["default_" + this.options.basicFieldParams.relation_field] = this.options.basicFieldParams.state.id || null; @@ -237,10 +237,10 @@ odoo.define("web_widget_one2many_product_picker.One2ManyProductPickerRecord", fu * @private * @returns {Object} */ - _getInternalVirtualRecordData: function () { + _getInternalVirtualRecordData: function() { var data = {}; data[this.options.fieldMap.product] = { - operation: 'ADD', + operation: "ADD", id: this.recordSearch.id, }; return data; @@ -252,21 +252,23 @@ odoo.define("web_widget_one2many_product_picker.One2ManyProductPickerRecord", fu * @param {Object} context * @returns {Object} */ - _generateVirtualState: function (data, context) { + _generateVirtualState: function(data, context) { var model = this.options.basicFieldParams.model; var scontext = _.extend( - {}, this._getInternalVirtualRecordContext(), context); + {}, + this._getInternalVirtualRecordContext(), + context + ); // Force qty to 1.0 to launch correct onchanges scontext[`default_${this.options.fieldMap.product_uom_qty}`] = 1.0; var sdata = _.extend({}, this._getInternalVirtualRecordData(), data); - return model.createVirtualRecord( - this.options.basicFieldParams.value.id, { - data: sdata, - context: scontext, - }); + return model.createVirtualRecord(this.options.basicFieldParams.value.id, { + data: sdata, + context: scontext, + }); }, - _detachAllWidgets: function () { + _detachAllWidgets: function() { _.invoke(this.widgets.front, "on_detach_callback"); _.invoke(this.widgets.back, "on_detach_callback"); this.widgets = { @@ -278,21 +280,18 @@ odoo.define("web_widget_one2many_product_picker.One2ManyProductPickerRecord", fu /** * @override */ - _render: function () { + _render: function() { this._detachAllWidgets(); this.defs = []; this._replaceElement( - qweb.render( - "One2ManyProductPicker.FlipCard", - this._getQWebContext() - ) + qweb.render("One2ManyProductPicker.FlipCard", this._getQWebContext()) ); this.$el.data("renderer_widget_index", this.options.renderer_widget_index); this.$card = this.$(".oe_flip_card"); this.$front = this.$(".oe_flip_card_front"); this.$back = this.$(".oe_flip_card_back"); this._processWidgetFields(this.$front); - this._processWidgets(this.$front, 'front'); + this._processWidgets(this.$front, "front"); this._processDynamicFields(); return $.when.apply(this, this.defs); }, @@ -304,9 +303,9 @@ odoo.define("web_widget_one2many_product_picker.One2ManyProductPickerRecord", fu * @private * @param {jQueryElement} $container */ - _processWidgetFields: function ($container, widget_list) { + _processWidgetFields: function($container, widget_list) { var self = this; - $container.find("field").each(function () { + $container.find("field").each(function() { var $field = $(this); if ($field.parents("widget").length) { return; @@ -314,27 +313,24 @@ odoo.define("web_widget_one2many_product_picker.One2ManyProductPickerRecord", fu var field_name = $field.attr("name"); var field_widget = $field.attr("widget"); - // a widget is specified for that field or a field is a many2many ; + // A widget is specified for that field or a field is a many2many ; // in this latest case, we want to display the widget many2manytags // even if it is not specified in the view. if (field_widget || self.fields[field_name].type === "many2many") { var widget = self.subWidgets[field_name]; if (widget) { - - // a widget already exists for that field, so reset it + // A widget already exists for that field, so reset it // with the new state widget.reset(self.state); $field.replaceWith(widget.$el); } else { - - // the widget doesn't exist yet, so instanciate it + // The widget doesn't exist yet, so instanciate it var Widget = self.fieldsInfo[field_name].Widget; if (Widget) { widget = self._processWidget($field, field_name, Widget); self.subWidgets[field_name] = widget; } else if (config.debug) { - - // the widget is not implemented + // The widget is not implemented $field.replaceWith( $("", { text: _.str.sprintf( @@ -358,9 +354,8 @@ odoo.define("web_widget_one2many_product_picker.One2ManyProductPickerRecord", fu * @param {Class} Widget * @returns {Widget} the widget instance */ - _processWidget: function ($field, field_name, Widget) { - - // some field's attrs might be record dependent (they start with + _processWidget: function($field, field_name, Widget) { + // Some field's attrs might be record dependent (they start with // 't-att-') and should thus be evaluated, which is done by qweb // we here replace those attrs in the dict of attrs of the state // by their evaluted value, to make it transparent from the @@ -368,7 +363,7 @@ odoo.define("web_widget_one2many_product_picker.One2ManyProductPickerRecord", fu // that dict being shared between records, we don't modify it // in place var attrs = Object.create(null); - _.each(this.fieldsInfo[field_name], function (value, key) { + _.each(this.fieldsInfo[field_name], function(value, key) { if (_.str.startsWith(key, "t-att-")) { key = key.slice(6); value = $field.attr(key); @@ -379,10 +374,7 @@ odoo.define("web_widget_one2many_product_picker.One2ManyProductPickerRecord", fu attrs: attrs, data: this.state.data, }); - var widget = new Widget( - this, field_name, - this.getParent().state, - options); + var widget = new Widget(this, field_name, this.getParent().state, options); var def = widget.replace($field); if (def.state() === "pending") { this.defs.push(def); @@ -396,9 +388,9 @@ odoo.define("web_widget_one2many_product_picker.One2ManyProductPickerRecord", fu * @private * @param {jQueryElement} $container */ - _processWidgets: function ($container, widget_zone) { + _processWidgets: function($container, widget_zone) { var self = this; - $container.find("widget").each(function () { + $container.find("widget").each(function() { var $field = $(this); var FieldWidget = widgetRegistry.get($field.attr("name")); var widget = new FieldWidget(self, { @@ -417,9 +409,10 @@ odoo.define("web_widget_one2many_product_picker.One2ManyProductPickerRecord", fu self.widgets[widget_zone].push(widget); var def = widget - ._widgetRenderAndInsert(function () { + ._widgetRenderAndInsert(function() { // Do nothing - }).then(function () { + }) + .then(function() { widget.$el.addClass("o_widget"); $field.replaceWith(widget.$el); }); @@ -445,7 +438,7 @@ odoo.define("web_widget_one2many_product_picker.One2ManyProductPickerRecord", fu * @private * @param {Array[String]} fields */ - _processDynamicFields: function (fields) { + _processDynamicFields: function(fields) { if (!this.state) { return; } @@ -456,14 +449,14 @@ odoo.define("web_widget_one2many_product_picker.One2ManyProductPickerRecord", fu var to_find = []; if (!_.isEmpty(fields)) { - to_find = _.map(fields, function (field) { + to_find = _.map(fields, function(field) { return _.str.sprintf("[data-field=%s]", [field]); }); } else { to_find = ["[data-field]"]; } - this.$el.find(to_find.join()).each(function () { + this.$el.find(to_find.join()).each(function() { var $elm = $(this); var format_out = $elm.data("esc") || $elm.data("field"); $elm.html( @@ -475,13 +468,15 @@ odoo.define("web_widget_one2many_product_picker.One2ManyProductPickerRecord", fu var field_map = this.options.fieldMap; if (state_data) { var has_discount = state_data[field_map.discount] > 0.0; - this.$el.find(".original_price,.discount_price") + this.$el + .find(".original_price,.discount_price") .toggleClass("d-none", !has_discount); if (has_discount) { this.$el.find(".price_unit").html(this._calcPriceReduced()); } else { - this.$el.find(".price_unit").html( - this._getMonetaryFieldValue("price_unit")); + this.$el + .find(".price_unit") + .html(this._getMonetaryFieldValue("price_unit")); } } } @@ -491,7 +486,7 @@ odoo.define("web_widget_one2many_product_picker.One2ManyProductPickerRecord", fu * @private * @returns {String} */ - _calcPriceReduced: function () { + _calcPriceReduced: function() { var price_reduce = 0; var field_map = this.options.fieldMap; var model = this.options.basicFieldParams.model; @@ -499,13 +494,17 @@ odoo.define("web_widget_one2many_product_picker.One2ManyProductPickerRecord", fu if (record && record.data[field_map.discount]) { price_reduce = tools.priceReduce( record.data[field_map.price_unit], - record.data[field_map.discount]); + record.data[field_map.discount] + ); } - return price_reduce && tools.monetary( - price_reduce, - this.state.fields[field_map.price_unit], - this.options.currencyField, - this.state.data + return ( + price_reduce && + tools.monetary( + price_reduce, + this.state.fields[field_map.price_unit], + this.options.currencyField, + this.state.data + ) ); }, @@ -513,39 +512,45 @@ odoo.define("web_widget_one2many_product_picker.One2ManyProductPickerRecord", fu * @private * @returns {Promise} */ - _saveRecord: function () { + _saveRecord: function() { var self = this; var model = this.options.basicFieldParams.model; var record = model.get(this.state.id); - return model.save(record.id, { - stayInEdit: true, - reload: true, - savePoint: true, - viewType: "form", - }).then(function () { - var record = model.get(self.state.id); - self.trigger_up("create_quick_record", { - id: record.id, - callback: function () { - self.$card.find('.o_catch_attention').removeClass('o_catch_attention'); - } + return model + .save(record.id, { + stayInEdit: true, + reload: true, + savePoint: true, + viewType: "form", + }) + .then(function() { + var record = model.get(self.state.id); + self.trigger_up("create_quick_record", { + id: record.id, + callback: function() { + self.$card + .find(".o_catch_attention") + .removeClass("o_catch_attention"); + }, + }); + model.unsetDirty(self.state.id); }); - model.unsetDirty(self.state.id); - }); }, /** * @private */ - _updateRecord: function () { + _updateRecord: function() { var self = this; var model = this.options.basicFieldParams.model; var record = model.get(this.state.id); this.trigger_up("update_quick_record", { id: record.id, - callback: function () { - self.$card.find('.o_catch_attention').removeClass('o_catch_attention'); - } + callback: function() { + self.$card + .find(".o_catch_attention") + .removeClass("o_catch_attention"); + }, }); model.unsetDirty(this.state.id); }, @@ -554,10 +559,12 @@ odoo.define("web_widget_one2many_product_picker.One2ManyProductPickerRecord", fu * @private * @returns {Promise} */ - _addProduct: function () { + _addProduct: function() { var self = this; var model = this.options.basicFieldParams.model; - model.updateRecordContext(this.state.id, {ignore_warning: this.options.ignoreWarning}); + model.updateRecordContext(this.state.id, { + ignore_warning: this.options.ignoreWarning, + }); var record = model.get(this.state.id); var changes = _.pick(record.data, this.options.fieldMap.product_uom_qty); if (changes[this.options.fieldMap.product_uom_qty] === 0) { @@ -565,10 +572,7 @@ odoo.define("web_widget_one2many_product_picker.One2ManyProductPickerRecord", fu } var model = this.options.basicFieldParams.model; this.$card.addClass("blocked"); - return model.notifyChanges( - record.id, - changes - ).then(function () { + return model.notifyChanges(record.id, changes).then(function() { self._saveRecord(); }); }, @@ -578,18 +582,17 @@ odoo.define("web_widget_one2many_product_picker.One2ManyProductPickerRecord", fu * @param {Number} amount * @returns {Promise} */ - _incProductQty: function (amount) { + _incProductQty: function(amount) { var self = this; var model = this.options.basicFieldParams.model; - model.updateRecordContext(this.state.id, {ignore_warning: this.options.ignoreWarning}); + model.updateRecordContext(this.state.id, { + ignore_warning: this.options.ignoreWarning, + }); var record = model.get(this.state.id); var changes = _.pick(record.data, this.options.fieldMap.product_uom_qty); changes[this.options.fieldMap.product_uom_qty] += amount; - return model.notifyChanges( - record.id, - changes - ).then(function () { + return model.notifyChanges(record.id, changes).then(function() { self._processDynamicFields(); self._lazyUpdateRecord(); }); @@ -598,22 +601,22 @@ odoo.define("web_widget_one2many_product_picker.One2ManyProductPickerRecord", fu /** * @private */ - _doInteractAnim: function (target, currentTarget) { + _doInteractAnim: function(target, currentTarget) { var $target = $(target); var $currentTarget = $(currentTarget); var $img = $currentTarget.find(".oe_flip_card_front img"); - $target.addClass('o_catch_attention'); - $img.addClass('oe_product_picker_catch_attention'); - $img.on('animationend', function () { - $img.removeClass('oe_product_picker_catch_attention'); - $img.off('animationend'); + $target.addClass("o_catch_attention"); + $img.addClass("oe_product_picker_catch_attention"); + $img.on("animationend", function() { + $img.removeClass("oe_product_picker_catch_attention"); + $img.off("animationend"); }); }, /** * @private */ - _openPriceModifier: function () { + _openPriceModifier: function() { var state_data = this.state && this.state.data; if (this.options.readOnlyMode || !state_data) { return; @@ -632,7 +635,8 @@ odoo.define("web_widget_one2many_product_picker.One2ManyProductPickerRecord", fu currencyField: this.options.currencyField, }); this.$modifPricePopup = $( - qweb.render("One2ManyProductPicker.QuickModifPricePopup")); + qweb.render("One2ManyProductPicker.QuickModifPricePopup") + ); this.$modifPricePopup.appendTo($(".o_main_content")); modif_price_form.attachTo(this.$modifPricePopup); }, @@ -643,16 +647,19 @@ odoo.define("web_widget_one2many_product_picker.One2ManyProductPickerRecord", fu * @private * @param {ClickEvent} evt */ - _onClickFlipCard: function (evt) { + _onClickFlipCard: function(evt) { // Avoid clicks on form elements - if (['INPUT', 'BUTTON', 'A'].indexOf(evt.target.tagName) !== -1 || this.$card.hasClass('blocked')) { + if ( + ["INPUT", "BUTTON", "A"].indexOf(evt.target.tagName) !== -1 || + this.$card.hasClass("blocked") + ) { return; } var $target = $(evt.target); if (!this.options.readOnlyMode) { if ( - $target.hasClass('add_product') || - $target.parents('.add_product').length + $target.hasClass("add_product") || + $target.parents(".add_product").length ) { if (!this.is_adding_product) { this.is_adding_product = true; @@ -661,13 +668,13 @@ odoo.define("web_widget_one2many_product_picker.One2ManyProductPickerRecord", fu } return; } else if ( - $target.hasClass('product_qty') || - $target.parents('.product_qty').length + $target.hasClass("product_qty") || + $target.parents(".product_qty").length ) { this._incProductQty(1); this._doInteractAnim(evt.target, evt.currentTarget); return; - } else if ($target.hasClass('safezone')) { + } else if ($target.hasClass("safezone")) { // Do nothing on safe zones return; } @@ -675,7 +682,8 @@ odoo.define("web_widget_one2many_product_picker.One2ManyProductPickerRecord", fu if (!this._clickFlipCardDelayed) { this._clickFlipCardDelayed = setTimeout( this._onClickDelayedFlipCard.bind(this, evt), - this._click_card_delayed_time); + this._click_card_delayed_time + ); } ++this._clickFlipCardCount; if (this._clickFlipCardCount >= 2) { @@ -689,7 +697,7 @@ odoo.define("web_widget_one2many_product_picker.One2ManyProductPickerRecord", fu /** * @private */ - _onClickDelayedFlipCard: function () { + _onClickDelayedFlipCard: function() { this._clickFlipCardDelayed = false; this._clickFlipCardCount = 0; @@ -704,21 +712,23 @@ odoo.define("web_widget_one2many_product_picker.One2ManyProductPickerRecord", fu this.defs = []; if (!this.widgets.back.length) { this._processWidgetFields(this.$back); - this._processWidgets(this.$back, 'back'); + this._processWidgets(this.$back, "back"); } this._processDynamicFields(); - $.when(this.defs).then(function () { + $.when(this.defs).then(function() { var $actived_card = self.$el.parent().find(".active"); $actived_card.removeClass("active"); - $actived_card.find('.oe_flip_card_front').removeClass("d-none"); + $actived_card.find(".oe_flip_card_front").removeClass("d-none"); self.$card.addClass("active"); - self.$card.on('transitionend', function () { + self.$card.on("transitionend", function() { self.$front.addClass("d-none"); - self.$card.off('transitionend'); + self.$card.off("transitionend"); }); self.trigger_up("record_flip", { widget_index: self.$el.data("renderer_widget_index"), - prev_widget_index: $actived_card.parent().data("renderer_widget_index"), + prev_widget_index: $actived_card + .parent() + .data("renderer_widget_index"), }); }); } @@ -728,20 +738,20 @@ odoo.define("web_widget_one2many_product_picker.One2ManyProductPickerRecord", fu * @private * @param {MouseEvent} evt */ - _onDblClickDelayedFlipCard: function (evt) { + _onDblClickDelayedFlipCard: function(evt) { var $target = $(evt.target); if ( - $target.hasClass('badge_price') || - $target.parents('.badge_price').length + $target.hasClass("badge_price") || + $target.parents(".badge_price").length ) { this._openPriceModifier(); } else { var $currentTarget = $(evt.currentTarget); var $img = $currentTarget.find(".oe_flip_card_front img"); var cur_img_src = $img.attr("src"); - if ($currentTarget.hasClass('oe_flip_card_maximized')) { - $currentTarget.removeClass('oe_flip_card_maximized'); - $currentTarget.on('transitionend', function () { + if ($currentTarget.hasClass("oe_flip_card_maximized")) { + $currentTarget.removeClass("oe_flip_card_maximized"); + $currentTarget.on("transitionend", function() { $currentTarget.css({ position: "", top: "", @@ -750,14 +760,13 @@ odoo.define("web_widget_one2many_product_picker.One2ManyProductPickerRecord", fu height: "", zIndex: "", }); - $currentTarget.off('transitionend'); + $currentTarget.off("transitionend"); }); } else { var $actived_card = this.$el.parent().find(".active"); if ($actived_card[0] !== $currentTarget[0]) { $actived_card.removeClass("active"); - $actived_card.find('.oe_flip_card_front') - .removeClass("d-none"); + $actived_card.find(".oe_flip_card_front").removeClass("d-none"); } var offset = $currentTarget.offset(); $currentTarget.css({ @@ -768,8 +777,8 @@ odoo.define("web_widget_one2many_product_picker.One2ManyProductPickerRecord", fu height: $currentTarget.height(), zIndex: 50, }); - _.defer(function () { - $currentTarget.addClass('oe_flip_card_maximized'); + _.defer(function() { + $currentTarget.addClass("oe_flip_card_maximized"); }); } $img.attr("src", $img.data("srcAlt")); @@ -780,13 +789,13 @@ odoo.define("web_widget_one2many_product_picker.One2ManyProductPickerRecord", fu /** * @private */ - _onRestoreFlipCard: function (evt) { + _onRestoreFlipCard: function(evt) { var self = this; this.$card.removeClass("active"); this.$front.removeClass("d-none"); - if (this.$card.hasClass('oe_flip_card_maximized')) { - this.$card.removeClass('oe_flip_card_maximized'); - this.$card.on('transitionend', function () { + if (this.$card.hasClass("oe_flip_card_maximized")) { + this.$card.removeClass("oe_flip_card_maximized"); + this.$card.on("transitionend", function() { self.$card.css({ position: "", top: "", @@ -795,7 +804,7 @@ odoo.define("web_widget_one2many_product_picker.One2ManyProductPickerRecord", fu height: "", zIndex: "", }); - self.$card.off('transitionend'); + self.$card.off("transitionend"); if (evt.data.success_callback) { evt.data.success_callback(); } @@ -824,7 +833,7 @@ odoo.define("web_widget_one2many_product_picker.One2ManyProductPickerRecord", fu * @private * @param {CustomEvent} evt */ - _onQuickRecordUpdated: function (evt) { + _onQuickRecordUpdated: function(evt) { this._processDynamicFields(Object.keys(evt.data.changes)); this.trigger_up("update_subtotal"); }, diff --git a/web_widget_one2many_product_picker/static/src/js/views/One2ManyProductPicker/renderer.js b/web_widget_one2many_product_picker/static/src/js/views/One2ManyProductPicker/renderer.js index 71750434d..99bc2cd1c 100644 --- a/web_widget_one2many_product_picker/static/src/js/views/One2ManyProductPicker/renderer.js +++ b/web_widget_one2many_product_picker/static/src/js/views/One2ManyProductPicker/renderer.js @@ -1,565 +1,605 @@ // Copyright 2020 Tecnativa - Alexandre Díaz // License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -odoo.define("web_widget_one2many_product_picker.One2ManyProductPickerRenderer", function ( - require -) { - "use strict"; +odoo.define( + "web_widget_one2many_product_picker.One2ManyProductPickerRenderer", + function(require) { + "use strict"; - var core = require("web.core"); - var BasicRenderer = require("web.BasicRenderer"); - var One2ManyProductPickerRecord = require( - "web_widget_one2many_product_picker.One2ManyProductPickerRecord"); - var ProductPickerQuickCreateForm = require( - "web_widget_one2many_product_picker.ProductPickerQuickCreateForm"); + var core = require("web.core"); + var BasicRenderer = require("web.BasicRenderer"); + var One2ManyProductPickerRecord = require("web_widget_one2many_product_picker.One2ManyProductPickerRecord"); + var ProductPickerQuickCreateForm = require("web_widget_one2many_product_picker.ProductPickerQuickCreateForm"); - var qweb = core.qweb; + var qweb = core.qweb; - /* This is the renderer of the main widget */ - var One2ManyProductPickerRenderer = BasicRenderer.extend({ - className: 'oe_one2many_product_picker_view', + /* This is the renderer of the main widget */ + var One2ManyProductPickerRenderer = BasicRenderer.extend({ + className: "oe_one2many_product_picker_view", - events: { - 'click #productPickerLoadMore': '_onClickLoadMore', - }, - custom_events: { - 'record_flip': '_onRecordFlip', - }, + events: { + "click #productPickerLoadMore": "_onClickLoadMore", + }, + custom_events: { + record_flip: "_onRecordFlip", + }, - DELAY_GET_RECORDS: 150, - MIN_PERC_GET_RECORDS: 0.9, + DELAY_GET_RECORDS: 150, + MIN_PERC_GET_RECORDS: 0.9, - /** - * @override - */ - init: function (parent, state, params) { - this._super.apply(this, arguments); - this.widgets = []; - this.recordOptions = _.extend({}, params.record_options, { - viewType: 'One2ManyProductPicker', - }); + /** + * @override + */ + init: function(parent, state, params) { + this._super.apply(this, arguments); + this.widgets = []; + this.recordOptions = _.extend({}, params.record_options, { + viewType: "One2ManyProductPicker", + }); - // Workaround: Odoo initilize this class so we need do this to - // 'receive' more arguments. - this.options = parent.options; - this.mode = parent.mode; - this.search_data = parent._searchRecords; - this.search_group = parent._activeSearchGroup; - this.last_search_data_count = parent._lastSearchRecordsCount; - }, + // Workaround: Odoo initilize this class so we need do this to + // 'receive' more arguments. + this.options = parent.options; + this.mode = parent.mode; + this.search_data = parent._searchRecords; + this.search_group = parent._activeSearchGroup; + this.last_search_data_count = parent._lastSearchRecordsCount; + }, - /** - * Propagate the event to the view widgets - */ - on_attach_callback: function () { - this._isInDom = true; - _.invoke(this.widgets, 'on_attach_callback'); - }, + /** + * Propagate the event to the view widgets + */ + on_attach_callback: function() { + this._isInDom = true; + _.invoke(this.widgets, "on_attach_callback"); + }, - /** - * Propagate the event to the view widgets - */ - on_detach_callback: function () { - this._isInDom = false; - _.invoke(this.widgets, 'on_detach_callback'); - }, + /** + * Propagate the event to the view widgets + */ + on_detach_callback: function() { + this._isInDom = false; + _.invoke(this.widgets, "on_detach_callback"); + }, - /** - * @param {Object} widget - */ - removeWidget: function (widget) { - var index = this.widgets.indexOf(widget); - widget.destroy(); - delete this.widgets[index]; - }, + /** + * @param {Object} widget + */ + removeWidget: function(widget) { + var index = this.widgets.indexOf(widget); + widget.destroy(); + delete this.widgets[index]; + }, - /** - * @override - */ - start: function () { - return this._super.apply(this, arguments); - }, + /** + * @override + */ + start: function() { + return this._super.apply(this, arguments); + }, - /** - * @param {Object} search_data - */ - updateSearchData: function (search_data, count, search_group) { - this.search_data = search_data; - this.last_search_data_count = count; - this.search_group = search_group; - this._loadMoreWorking = false; - this.$btnLoadMore.attr("disabled", false); - }, + /** + * @param {Object} search_data + */ + updateSearchData: function(search_data, count, search_group) { + this.search_data = search_data; + this.last_search_data_count = count; + this.search_group = search_group; + this._loadMoreWorking = false; + this.$btnLoadMore.attr("disabled", false); + }, - /** - * @param {Boolean} block - */ - blockLoadMore: function (block) { - this.$btnLoadMore.attr("disabled", block); - }, + /** + * @param {Boolean} block + */ + blockLoadMore: function(block) { + this.$btnLoadMore.attr("disabled", block); + }, - /** - * Avoid re-render 'pure virtual' states - * - * @override - */ - updateState: function (state, params) { - var self = this; - var force_update = params.force; - delete params.force; - var sparams = _.extend({}, params, {noRender: true}); - if (!force_update && _.isEqual(this.state.data, state.data)) { - return this._super(state, sparams); - } - var old_state = _.clone(this.state.data); - return this._super(state, sparams).then(function () { - self._updateStateRecords(old_state); - }); - }, - - /** - * Recreate the given widget by the state id - * - * @param {String} state_id - * @param {Object} new_state - */ - updateRecord: function (state_id, new_state) { - for (var eb = this.widgets.length-1; eb>=0; --eb) { - var widget = this.widgets[eb]; - if (widget.state.id === state_id) { - widget.recreate(new_state); - break; + /** + * Avoid re-render 'pure virtual' states + * + * @override + */ + updateState: function(state, params) { + var self = this; + var force_update = params.force; + delete params.force; + var sparams = _.extend({}, params, {noRender: true}); + if (!force_update && _.isEqual(this.state.data, state.data)) { + return this._super(state, sparams); } - } - }, + var old_state = _.clone(this.state.data); + return this._super(state, sparams).then(function() { + self._updateStateRecords(old_state); + }); + }, - /** - * @private - * @param {Array[Object]} states - * @returns {Deferred} - */ - _removeRecords: function (states, new_states) { - var defs = []; - var to_destroy = []; - for (var index_state in states) { - var state = states[index_state]; - for (var e = this.widgets.length-1; e>=0; --e) { - var widget = this.widgets[e]; - if (widget && widget.state.id === state.id) { - to_destroy.push(widget); - delete this.widgets[e]; - } - } - } - - if (this.search_group.name === "main_lines") { - _.invoke(to_destroy, "destroy"); - return $.when(); - } - - // If doesn't exists other records with the same product, we need - // create a 'pure virtual' record again. - for (var index_destroy in to_destroy) { - var widget_destroyed = to_destroy[index_destroy]; - var widget_product_id = widget_destroyed.state - .data[this.options.field_map.product].data.id; - var found = false; - // If already exists a widget for the product don't try create a new one - for (var eb = this.widgets.length-1; eb>=0; --eb) { + /** + * Recreate the given widget by the state id + * + * @param {String} state_id + * @param {Object} new_state + */ + updateRecord: function(state_id, new_state) { + for (var eb = this.widgets.length - 1; eb >= 0; --eb) { var widget = this.widgets[eb]; - if ( - widget && - widget.state && - widget.state.data[this.options.field_map.product].data.id === widget_product_id - ) { - found = true; + if (widget.state.id === state_id) { + widget.recreate(new_state); break; } } + }, - if (!found) { - // Get the new state ID if exists to link it with the new record - var new_state_id = undefined; - for (var eb = new_states.length-1; eb>=0; --eb) { - var state = new_states[eb]; + /** + * @private + * @param {Array[Object]} states + * @returns {Deferred} + */ + _removeRecords: function(states, new_states) { + var defs = []; + var to_destroy = []; + for (var index_state in states) { + var state = states[index_state]; + for (var e = this.widgets.length - 1; e >= 0; --e) { + var widget = this.widgets[e]; + if (widget && widget.state.id === state.id) { + to_destroy.push(widget); + delete this.widgets[e]; + } + } + } + + if (this.search_group.name === "main_lines") { + _.invoke(to_destroy, "destroy"); + return $.when(); + } + + // If doesn't exists other records with the same product, we need + // create a 'pure virtual' record again. + for (var index_destroy in to_destroy) { + var widget_destroyed = to_destroy[index_destroy]; + var widget_product_id = + widget_destroyed.state.data[this.options.field_map.product].data + .id; + var found = false; + // If already exists a widget for the product don't try create a new one + for (var eb = this.widgets.length - 1; eb >= 0; --eb) { + var widget = this.widgets[eb]; if ( - state.data[this.options.field_map.product].data.id === widget_product_id + widget && + widget.state && + widget.state.data[this.options.field_map.product].data + .id === widget_product_id ) { - new_state_id = state.id; + found = true; break; } } - var search_record = _.find(this.search_data, {id: widget_product_id}); - var new_search_record = _.extend({}, search_record, {__id: new_state_id}); - var search_record_index = widget_destroyed.$el.index(); - defs.push( - this.appendSearchRecords( - [new_search_record], - false, - true, - search_record_index - )[0] - ); - } - } - _.invoke(to_destroy, "destroy"); - return $.when(defs); - }, - - /** - * When the state change this method tries to update current records, delete - * or update them. - * Thanks to this we don't need re-render 'pure virtual' records. - * - * @private - * @param {Object} old_states - * @returns {Deferred} - */ - _updateStateRecords: function (old_states) { - - // States to remove - var states_to_destroy = []; - for (var index in old_states) { - var old_state = old_states[index]; - var found = false; - for (var e in this.state.data) { - var current_state = this.state.data[e]; - if (current_state.id === old_state.id) { - found = true; - break; + if (!found) { + // Get the new state ID if exists to link it with the new record + var new_state_id = undefined; + for (var eb = new_states.length - 1; eb >= 0; --eb) { + var state = new_states[eb]; + if ( + state.data[this.options.field_map.product].data.id === + widget_product_id + ) { + new_state_id = state.id; + break; + } + } + var search_record = _.find(this.search_data, { + id: widget_product_id, + }); + var new_search_record = _.extend({}, search_record, { + __id: new_state_id, + }); + var search_record_index = widget_destroyed.$el.index(); + defs.push( + this.appendSearchRecords( + [new_search_record], + false, + true, + search_record_index + )[0] + ); } } - if (!found) { - states_to_destroy.push(old_state); - } - } - this.state.data = _.compact(this.state.data); - this._removeRecords(states_to_destroy, this.state.data); + _.invoke(to_destroy, "destroy"); + return $.when(defs); + }, - // Records to Update or Create - var defs = []; - var to_destroy = []; - for (var index in this.state.data) { - var state = this.state.data[index]; - var exists = false; - var search_record_index = -1; - var search_record = false; - for (var e = this.widgets.length-1; e>=0; --e) { - var widget = this.widgets[e]; - if (!widget || !widget.state) { - - // Already processed widget (deleted) - continue; + /** + * When the state change this method tries to update current records, delete + * or update them. + * Thanks to this we don't need re-render 'pure virtual' records. + * + * @private + * @param {Object} old_states + * @returns {Deferred} + */ + _updateStateRecords: function(old_states) { + // States to remove + var states_to_destroy = []; + for (var index in old_states) { + var old_state = old_states[index]; + var found = false; + for (var e in this.state.data) { + var current_state = this.state.data[e]; + if (current_state.id === old_state.id) { + found = true; + break; + } } - if (widget.state.id === state.id) { - widget.recreate(state); - exists = true; - break; - } else if ( - widget.recordSearch.id === state.data[this.options.field_map.product].data.id - ) { - - // Is a new record - search_record_index = widget.$el.index(); - search_record = widget.recordSearch; - } - - // Remove "pure virtual" records that have the same product that the new record - if ( - widget.is_virtual && - widget.state.data[this.options.field_map.product].data.id === state.data[this.options.field_map.product].data.id - ) { - to_destroy.push(widget); - delete this.widgets[e]; + if (!found) { + states_to_destroy.push(old_state); } } this.state.data = _.compact(this.state.data); + this._removeRecords(states_to_destroy, this.state.data); - // Need add a new one? - if (!exists && search_record_index !== -1) { - var new_search_record = _.extend({}, search_record, {__id: state.id}); - defs.push(this.appendSearchRecords([new_search_record], false, true, search_record_index)[0]); - } - } + // Records to Update or Create + var defs = []; + var to_destroy = []; + for (var index in this.state.data) { + var state = this.state.data[index]; + var exists = false; + var search_record_index = -1; + var search_record = false; + for (var e = this.widgets.length - 1; e >= 0; --e) { + var widget = this.widgets[e]; + if (!widget || !widget.state) { + // Already processed widget (deleted) + continue; + } + if (widget.state.id === state.id) { + widget.recreate(state); + exists = true; + break; + } else if ( + widget.recordSearch.id === + state.data[this.options.field_map.product].data.id + ) { + // Is a new record + search_record_index = widget.$el.index(); + search_record = widget.recordSearch; + } - _.invoke(to_destroy, "destroy"); - return $.when(defs); - }, - - /** - * @override - */ - _renderView: function () { - var self = this; - var oldWidgets = _.compact(this.widgets); - this.widgets = []; - this.$recordsContainer = $("
", { - class: "w-100 row", - }); - this.$extraButtonsContainer = $(qweb.render("One2ManyProductPicker.ExtraButtons")); - this.$btnLoadMore = this.$extraButtonsContainer.find("#productPickerLoadMore"); - this.search_data = this._sort_search_data(this.search_data); - return $.Deferred(function (d) { - var defs = self.appendSearchRecords(self.search_data, true); - defs[0].then(function () { - _.invoke(oldWidgets, "destroy"); - self.$el.empty(); - self.$el.append(self.$recordsContainer); - self.$el.append(self.$extraButtonsContainer); - self.showLoadMore(self.last_search_data_count >= self.options.records_per_page); - if (self._isInDom) { - _.invoke(self.widgets, "on_attach_callback"); - } - d.resolve(defs[1]); - }); - }); - }, - - /** - * @param {Array} datas - * @returns {Array} - */ - _sort_search_data: function (datas) { - if (this.search_group.name === "main_lines") { - var field_name = this.options.field_map.product; - for (var index_datas in datas) { - var data = datas[index_datas]; - - for (var index_state in this.state.data) { - var state_data = this.state.data[index_state]; - if (state_data.data[field_name].res_id === data.id) { - data._order_value = state_data.res_id; + // Remove "pure virtual" records that have the same product that the new record + if ( + widget.is_virtual && + widget.state.data[this.options.field_map.product].data + .id === + state.data[this.options.field_map.product].data.id + ) { + to_destroy.push(widget); + delete this.widgets[e]; } } - } - var sorted_datas = _.chain(datas).sortBy('_order_value').map(function(item) { - return _.omit(item, '_order_value'); - }).value().reverse(); - return sorted_datas; - } - return datas; - }, - /** - * Compare search results with current lines. - * Link a current state with the 'search record'. - * - * @private - * @param {Array[Object]} results - * @returns {Array[Object]} - */ - _processSearchRecords: function (results) { - var field_name = this.options.field_map.product; - var records = []; - for (var index in results) { - var record_search = results[index]; - var state_data_found = false; + this.state.data = _.compact(this.state.data); - for (var index_data in this.state.data) { - var state_record = this.state.data[index_data]; - var field = state_record.data[field_name]; - if ( - (typeof field === "object" && field.data.id === record_search.id) || - field === record_search.id - ) { - records.push( - _.extend({}, record_search, { - __id: state_record.id, - }) + // Need add a new one? + if (!exists && search_record_index !== -1) { + var new_search_record = _.extend({}, search_record, { + __id: state.id, + }); + defs.push( + this.appendSearchRecords( + [new_search_record], + false, + true, + search_record_index + )[0] ); - state_data_found = true; - } - } - if (!state_data_found) { - records.push(record_search); - } - } - - return records; - }, - - /** - * @private - * @param {Int} id - * @returns {Object} - */ - _getRecordDataById: function (id) { - for (var index in this.state.data) { - var record = this.state.data[index]; - if (record.id === id) { - return record; - } - } - return false; - }, - - /** - * @private - * @param {Object} search_record - * @returns {Object} - */ - _getRecordOptions: function (search_record) { - return _.extend({}, this.recordOptions, { - fieldMap: this.options.field_map, - searchRecord: search_record, - basicFieldParams: this.getParent().getBasicFieldParams(), - currencyField: this.options.currency_field, - readOnlyMode: this.mode === "readonly", - showDiscount: this.options.show_discount, - editDiscount: this.options.edit_discount, - editPrice: this.options.edit_price, - autoSave: this.options.auto_save, - ignoreWarning: this.options.ignore_warning, - }); - }, - - /** - * Generates the 'Product Card' per record. - * - * @private - * @param {Array[Object]} search_records - * @param {Boolean} no_process_records - * @param {Number} position - */ - _appendSearchRecords: function (search_records, no_process_records, position) { - var self = this; - var processed_records = - no_process_records?search_records:this._processSearchRecords(search_records); - _.each(processed_records, function (search_record) { - var state_data = self._getRecordDataById(search_record.__id); - var widget_options = self._getRecordOptions(search_record); - widget_options.renderer_widget_index = self.widgets.length; - var ProductPickerRecord = new One2ManyProductPickerRecord( - self, - state_data, - widget_options - ); - self.widgets.push(ProductPickerRecord); - - // Simulate new lines to dispatch get_default & onchange's to get the - // relevant data to print. This case increase the TTI time. - if (!state_data) { - var defVirtualState = ProductPickerRecord.generateVirtualState(); - if (defVirtualState.state() === "pending") { - self.defsVirtualState.push(defVirtualState); } } - // At this point the widget will use the existing state (line) or - // the search data. Using search data instead of waiting for - // simulated state gives a low FCP time. - var def = ProductPickerRecord.appendTo(self.$recordsContainer) - .then(function (widget, widget_position) { - if (typeof widget_position !== "undefined") { - var $elm = this.$el.find("> div > div:nth("+widget_position+")"); - widget.$el.insertAfter($elm); - } - }.bind(self, ProductPickerRecord, position)); - if (def.state() === "pending") { - self.defs.push(def); - } - }); - }, + _.invoke(to_destroy, "destroy"); + return $.when(defs); + }, - /** - * @param {Boolean} status - */ - showLoadMore: function (status) { - this.$btnLoadMore.toggleClass("d-none", !status); - }, - - /** - * Append search records to the view - * - * @param {Array[Object]} search_records - * @param {Boolean} no_attach_widgets - * @param {Boolean} no_process_records - * @param {Number} position - * @returns {Array[Deferred]} - */ - appendSearchRecords: function (search_records, no_attach_widgets, no_process_records, position) { - var self = this; - this.trigger_up("loading_records"); - this.defs = []; - this.defsVirtualState = []; - var cur_widget_index = this.widgets.length; - this._appendSearchRecords(search_records, no_process_records, position); - var defs = this.defs; - delete this.defs; - var defsVirtualState = this.defsVirtualState; - delete this.defsVirtualState; - return [ - $.when.apply($, defs).then(function () { - if (!no_attach_widgets && self._isInDom) { - var new_widgets = self.widgets.slice(cur_widget_index); - _.invoke(new_widgets, "on_attach_callback"); - } - }), - $.when.apply($, defsVirtualState).then(function () { - self.trigger_up("loading_records", {finished:true}); - }), - ]; - }, - - /** - * @private - */ - _onClickLoadMore: function () { - this.$btnLoadMore.attr("disabled", true); - this.trigger_up("load_more"); - this._loadMoreWorking = true; - }, - - /** - * Do card flip - * - * @param {Integer} index - */ - doWidgetFlip: function (index) { - var widget = this.widgets[index]; - var $actived_card = this.$el.find(".active"); - if (widget.$card.hasClass("active")) { - widget.$card.removeClass("active"); - widget.$card.find('.oe_flip_card_front').removeClass("d-none"); - } else { - var self = widget; - widget.defs = []; - widget._processWidgetFields(widget.$back); - widget._processWidgets(widget.$back); - widget._processDynamicFields(); - $.when(widget.defs).then(function () { - $actived_card.removeClass("active"); - $actived_card.find('.oe_flip_card_front').removeClass("d-none"); - self.$card.addClass("active"); - setTimeout(function () { - self.$('.oe_flip_card_front').addClass("d-none"); - }, 200); + /** + * @override + */ + _renderView: function() { + var self = this; + var oldWidgets = _.compact(this.widgets); + this.widgets = []; + this.$recordsContainer = $("
", { + class: "w-100 row", }); - } - }, + this.$extraButtonsContainer = $( + qweb.render("One2ManyProductPicker.ExtraButtons") + ); + this.$btnLoadMore = this.$extraButtonsContainer.find( + "#productPickerLoadMore" + ); + this.search_data = this._sort_search_data(this.search_data); + return $.Deferred(function(d) { + var defs = self.appendSearchRecords(self.search_data, true); + defs[0].then(function() { + _.invoke(oldWidgets, "destroy"); + self.$el.empty(); + self.$el.append(self.$recordsContainer); + self.$el.append(self.$extraButtonsContainer); + self.showLoadMore( + self.last_search_data_count >= self.options.records_per_page + ); + if (self._isInDom) { + _.invoke(self.widgets, "on_attach_callback"); + } + d.resolve(defs[1]); + }); + }); + }, - /** - * Handle card flip. - * Used to create/update the record - * - * @param {CustomEvent} evt - */ - _onRecordFlip: function (evt) { - var prev_widget_index = evt.data.prev_widget_index; - if (typeof prev_widget_index !== "undefined") { - // Only check 'back' widgets so there is where the form was created - for (var index in this.widgets[prev_widget_index].widgets.back) { - var widget = this.widgets[prev_widget_index].widgets.back[index]; - if (widget instanceof ProductPickerQuickCreateForm) { - widget.controller.auto(); + /** + * @param {Array} datas + * @returns {Array} + */ + _sort_search_data: function(datas) { + if (this.search_group.name === "main_lines") { + var field_name = this.options.field_map.product; + for (var index_datas in datas) { + var data = datas[index_datas]; + + for (var index_state in this.state.data) { + var state_data = this.state.data[index_state]; + if (state_data.data[field_name].res_id === data.id) { + data._order_value = state_data.res_id; + } + } + } + var sorted_datas = _.chain(datas) + .sortBy("_order_value") + .map(function(item) { + return _.omit(item, "_order_value"); + }) + .value() + .reverse(); + return sorted_datas; + } + return datas; + }, + + /** + * Compare search results with current lines. + * Link a current state with the 'search record'. + * + * @private + * @param {Array[Object]} results + * @returns {Array[Object]} + */ + _processSearchRecords: function(results) { + var field_name = this.options.field_map.product; + var records = []; + for (var index in results) { + var record_search = results[index]; + var state_data_found = false; + + for (var index_data in this.state.data) { + var state_record = this.state.data[index_data]; + var field = state_record.data[field_name]; + if ( + (typeof field === "object" && + field.data.id === record_search.id) || + field === record_search.id + ) { + records.push( + _.extend({}, record_search, { + __id: state_record.id, + }) + ); + state_data_found = true; + } + } + if (!state_data_found) { + records.push(record_search); } } - } - } - }); + return records; + }, - return One2ManyProductPickerRenderer; -}); + /** + * @private + * @param {Int} id + * @returns {Object} + */ + _getRecordDataById: function(id) { + for (var index in this.state.data) { + var record = this.state.data[index]; + if (record.id === id) { + return record; + } + } + return false; + }, + + /** + * @private + * @param {Object} search_record + * @returns {Object} + */ + _getRecordOptions: function(search_record) { + return _.extend({}, this.recordOptions, { + fieldMap: this.options.field_map, + searchRecord: search_record, + basicFieldParams: this.getParent().getBasicFieldParams(), + currencyField: this.options.currency_field, + readOnlyMode: this.mode === "readonly", + showDiscount: this.options.show_discount, + editDiscount: this.options.edit_discount, + editPrice: this.options.edit_price, + autoSave: this.options.auto_save, + ignoreWarning: this.options.ignore_warning, + }); + }, + + /** + * Generates the 'Product Card' per record. + * + * @private + * @param {Array[Object]} search_records + * @param {Boolean} no_process_records + * @param {Number} position + */ + _appendSearchRecords: function( + search_records, + no_process_records, + position + ) { + var self = this; + var processed_records = no_process_records + ? search_records + : this._processSearchRecords(search_records); + _.each(processed_records, function(search_record) { + var state_data = self._getRecordDataById(search_record.__id); + var widget_options = self._getRecordOptions(search_record); + widget_options.renderer_widget_index = self.widgets.length; + var ProductPickerRecord = new One2ManyProductPickerRecord( + self, + state_data, + widget_options + ); + self.widgets.push(ProductPickerRecord); + + // Simulate new lines to dispatch get_default & onchange's to get the + // relevant data to print. This case increase the TTI time. + if (!state_data) { + var defVirtualState = ProductPickerRecord.generateVirtualState(); + if (defVirtualState.state() === "pending") { + self.defsVirtualState.push(defVirtualState); + } + } + + // At this point the widget will use the existing state (line) or + // the search data. Using search data instead of waiting for + // simulated state gives a low FCP time. + var def = ProductPickerRecord.appendTo(self.$recordsContainer).then( + function(widget, widget_position) { + if (typeof widget_position !== "undefined") { + var $elm = this.$el.find( + "> div > div:nth(" + widget_position + ")" + ); + widget.$el.insertAfter($elm); + } + }.bind(self, ProductPickerRecord, position) + ); + if (def.state() === "pending") { + self.defs.push(def); + } + }); + }, + + /** + * @param {Boolean} status + */ + showLoadMore: function(status) { + this.$btnLoadMore.toggleClass("d-none", !status); + }, + + /** + * Append search records to the view + * + * @param {Array[Object]} search_records + * @param {Boolean} no_attach_widgets + * @param {Boolean} no_process_records + * @param {Number} position + * @returns {Array[Deferred]} + */ + appendSearchRecords: function( + search_records, + no_attach_widgets, + no_process_records, + position + ) { + var self = this; + this.trigger_up("loading_records"); + this.defs = []; + this.defsVirtualState = []; + var cur_widget_index = this.widgets.length; + this._appendSearchRecords(search_records, no_process_records, position); + var defs = this.defs; + delete this.defs; + var defsVirtualState = this.defsVirtualState; + delete this.defsVirtualState; + return [ + $.when.apply($, defs).then(function() { + if (!no_attach_widgets && self._isInDom) { + var new_widgets = self.widgets.slice(cur_widget_index); + _.invoke(new_widgets, "on_attach_callback"); + } + }), + $.when.apply($, defsVirtualState).then(function() { + self.trigger_up("loading_records", {finished: true}); + }), + ]; + }, + + /** + * @private + */ + _onClickLoadMore: function() { + this.$btnLoadMore.attr("disabled", true); + this.trigger_up("load_more"); + this._loadMoreWorking = true; + }, + + /** + * Do card flip + * + * @param {Integer} index + */ + doWidgetFlip: function(index) { + var widget = this.widgets[index]; + var $actived_card = this.$el.find(".active"); + if (widget.$card.hasClass("active")) { + widget.$card.removeClass("active"); + widget.$card.find(".oe_flip_card_front").removeClass("d-none"); + } else { + var self = widget; + widget.defs = []; + widget._processWidgetFields(widget.$back); + widget._processWidgets(widget.$back); + widget._processDynamicFields(); + $.when(widget.defs).then(function() { + $actived_card.removeClass("active"); + $actived_card.find(".oe_flip_card_front").removeClass("d-none"); + self.$card.addClass("active"); + setTimeout(function() { + self.$(".oe_flip_card_front").addClass("d-none"); + }, 200); + }); + } + }, + + /** + * Handle card flip. + * Used to create/update the record + * + * @param {CustomEvent} evt + */ + _onRecordFlip: function(evt) { + var prev_widget_index = evt.data.prev_widget_index; + if (typeof prev_widget_index !== "undefined") { + // Only check 'back' widgets so there is where the form was created + for (var index in this.widgets[prev_widget_index].widgets.back) { + var widget = this.widgets[prev_widget_index].widgets.back[ + index + ]; + if (widget instanceof ProductPickerQuickCreateForm) { + widget.controller.auto(); + } + } + } + }, + }); + + return One2ManyProductPickerRenderer; + } +); diff --git a/web_widget_one2many_product_picker/static/src/js/views/basic_model.js b/web_widget_one2many_product_picker/static/src/js/views/basic_model.js index 9d8fd3f52..34bb8baf0 100644 --- a/web_widget_one2many_product_picker/static/src/js/views/basic_model.js +++ b/web_widget_one2many_product_picker/static/src/js/views/basic_model.js @@ -1,28 +1,28 @@ // Copyright 2020 Tecnativa - Alexandre Díaz // License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -odoo.define("web_widget_one2many_product_picker.BasicModel", function (require) { +odoo.define("web_widget_one2many_product_picker.BasicModel", function(require) { "use strict"; var BasicModel = require("web.BasicModel"); BasicModel.include({ - /** * @param {Number/String} handle * @param {Object} context */ - updateRecordContext: function (handle, context) { + updateRecordContext: function(handle, context) { this.localData[handle].context = _.extend( {}, this.localData[handle].context, - context); + context + ); }, /** * @param {Number/String} id * @returns {Boolean} */ - isPureVirtual: function (id) { + isPureVirtual: function(id) { var data = this.localData[id]; return data._virtual || false; }, @@ -31,7 +31,7 @@ odoo.define("web_widget_one2many_product_picker.BasicModel", function (require) * @param {Number/String} id * @param {Boolean} status */ - setPureVirtual: function (id, status) { + setPureVirtual: function(id, status) { var data = this.localData[id]; if (status) { data._virtual = true; @@ -43,10 +43,10 @@ odoo.define("web_widget_one2many_product_picker.BasicModel", function (require) /** * @param {Number/String} id */ - unsetDirty: function (id) { + unsetDirty: function(id) { var data = this.localData[id]; data._isDirty = false; - this._visitChildren(data, function (r) { + this._visitChildren(data, function(r) { r._isDirty = false; }); }, @@ -58,12 +58,12 @@ odoo.define("web_widget_one2many_product_picker.BasicModel", function (require) * @param {Object} options * @returns {Deferred} */ - createVirtualRecord: function (listID, options) { + createVirtualRecord: function(listID, options) { var self = this; var list = this.localData[listID]; var context = _.extend({}, this._getContext(list), options.context); - var position = options?options.position:'top'; + var position = options ? options.position : "top"; var params = { context: context, fields: list.fields, @@ -75,23 +75,20 @@ odoo.define("web_widget_one2many_product_picker.BasicModel", function (require) doNotSetDirty: true, }; - return $.Deferred(function (d) { - self._makeDefaultRecord(list.model, params) - .then(function (recordID) { - self.setPureVirtual(recordID, true); - self.updateRecordContext(recordID, {ignore_warning: true}); - if (options.data) { - self._applyChange( - recordID, - options.data, - params - ).then(function () { + return $.Deferred(function(d) { + self._makeDefaultRecord(list.model, params).then(function(recordID) { + self.setPureVirtual(recordID, true); + self.updateRecordContext(recordID, {ignore_warning: true}); + if (options.data) { + self._applyChange(recordID, options.data, params).then( + function() { d.resolve(self.get(recordID)); - }); - } else { - d.resolve(self.get(recordID)); - } - }); + } + ); + } else { + d.resolve(self.get(recordID)); + } + }); }); }, @@ -103,12 +100,12 @@ odoo.define("web_widget_one2many_product_picker.BasicModel", function (require) * * @override */ - _performOnChange: function (record, fields, viewType) { + _performOnChange: function(record, fields, viewType) { if (record.context && record.context.ignore_warning) { var this_mp = _.clone(this); var super_call = this.trigger_up; - this_mp.trigger_up = function (event_name, data) { - if (event_name === 'warning' && data.type === "dialog") { + this_mp.trigger_up = function(event_name, data) { + if (event_name === "warning" && data.type === "dialog") { return; // Do nothing } return super_call.apply(this, arguments); @@ -118,5 +115,4 @@ odoo.define("web_widget_one2many_product_picker.BasicModel", function (require) return this._super.apply(this, arguments); }, }); - }); diff --git a/web_widget_one2many_product_picker/static/src/js/views/basic_view.js b/web_widget_one2many_product_picker/static/src/js/views/basic_view.js index f10d35071..357e492a9 100644 --- a/web_widget_one2many_product_picker/static/src/js/views/basic_view.js +++ b/web_widget_one2many_product_picker/static/src/js/views/basic_view.js @@ -1,7 +1,7 @@ /* global py */ // Copyright 2020 Tecnativa - Alexandre Díaz // License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -odoo.define("web_widget_one2many_product_picker.BasicView", function (require) { +odoo.define("web_widget_one2many_product_picker.BasicView", function(require) { "use strict"; var core = require("web.core"); @@ -11,18 +11,16 @@ odoo.define("web_widget_one2many_product_picker.BasicView", function (require) { var _t = core._t; // Add ref to _() -> _t() call - var PY_t = new py.PY_def.fromJSON(function () { - var args = py.PY_parseArgs(arguments, ['str']); + var PY_t = new py.PY_def.fromJSON(function() { + var args = py.PY_parseArgs(arguments, ["str"]); return py.str.fromJSON(_t(args.str.toJSON())); }); BasicView.include({ - /** * @override */ - _processField: function (viewType, field, attrs) { - + _processField: function(viewType, field, attrs) { /** * We need process 'options' attribute to handle translations and * special replacements @@ -31,15 +29,16 @@ odoo.define("web_widget_one2many_product_picker.BasicView", function (require) { attrs.widget === "one2many_product_picker" && !_.isObject(attrs.options) ) { - attrs.options = attrs.options ? pyUtils.py_eval(attrs.options, { - _: PY_t, + attrs.options = attrs.options + ? pyUtils.py_eval(attrs.options, { + _: PY_t, - // Hack: This allow use $number_search out of an string - number_search: '$number_search', - }) : {}; + // Hack: This allow use $number_search out of an string + number_search: "$number_search", + }) + : {}; } return this._super(viewType, field, attrs); }, }); - }); diff --git a/web_widget_one2many_product_picker/static/src/js/widgets/field_one2many_product_picker.js b/web_widget_one2many_product_picker/static/src/js/widgets/field_one2many_product_picker.js index d642a7aea..d3ab9f84f 100644 --- a/web_widget_one2many_product_picker/static/src/js/widgets/field_one2many_product_picker.js +++ b/web_widget_one2many_product_picker/static/src/js/widgets/field_one2many_product_picker.js @@ -1,6 +1,6 @@ // Copyright 2020 Tecnativa - Alexandre Díaz // License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -odoo.define("web_widget_one2many_product_picker.FieldOne2ManyProductPicker", function ( +odoo.define("web_widget_one2many_product_picker.FieldOne2ManyProductPicker", function( require ) { "use strict"; @@ -8,8 +8,7 @@ odoo.define("web_widget_one2many_product_picker.FieldOne2ManyProductPicker", fun var core = require("web.core"); var field_registry = require("web.field_registry"); var FieldOne2Many = require("web.relational_fields").FieldOne2Many; - var One2ManyProductPickerRenderer = require( - "web_widget_one2many_product_picker.One2ManyProductPickerRenderer"); + var One2ManyProductPickerRenderer = require("web_widget_one2many_product_picker.One2ManyProductPickerRenderer"); var tools = require("web_widget_one2many_product_picker.tools"); var _t = core._t; @@ -43,24 +42,27 @@ odoo.define("web_widget_one2many_product_picker.FieldOne2ManyProductPicker", fun _auto_search_delay: 450, // Model product.product fields - search_read_fields: [ - "id", - "display_name", - ], + search_read_fields: ["id", "display_name"], /** * @override */ - init: function (parent, name, record) { + init: function(parent, name, record) { this._super.apply(this, arguments); // This is the parent state this.state = record; // Use jquery 'extend' to have a 'deep' merge. - this.options = $.extend(true, this._getDefaultOptions(), this.attrs.options); + this.options = $.extend( + true, + this._getDefaultOptions(), + this.attrs.options + ); if (!this.options.search) { - this.options.search = [[this.options.field_map.name, 'ilike', '$search']]; + this.options.search = [ + [this.options.field_map.name, "ilike", "$search"], + ]; } this._searchMode = 0; this._searchCategoryNames = []; @@ -78,7 +80,7 @@ odoo.define("web_widget_one2many_product_picker.FieldOne2ManyProductPicker", fun /** * @override */ - willStart: function () { + willStart: function() { if (!this.view) { return $.when(); } @@ -92,7 +94,7 @@ odoo.define("web_widget_one2many_product_picker.FieldOne2ManyProductPicker", fun }; if (this.mode === "readonly") { this._activeSearchGroup = { - 'name': 'main_lines', + name: "main_lines", }; this._searchContext.activeTest = false; } @@ -102,28 +104,34 @@ odoo.define("web_widget_one2many_product_picker.FieldOne2ManyProductPicker", fun /** * Updates the lines counter badge */ - updateBadgeLines: function () { - var records = this.parent_controller.model.get(this.state.id).data[this.name].data; + updateBadgeLines: function() { + var records = this.parent_controller.model.get(this.state.id).data[ + this.name + ].data; this.$badgeLines.text(records.length); }, - updateSubtotalPrice: function () { + updateSubtotalPrice: function() { if (!this.options.show_subtotal) { return; } var prices = []; var field_map = this.options.field_map; - var records = this.parent_controller.model.get(this.state.id).data[this.name].data; + var records = this.parent_controller.model.get(this.state.id).data[ + this.name + ].data; if (this.options.show_discount) { - prices = _.map(records, function (line) { - return line.data[field_map.product_uom_qty] * + prices = _.map(records, function(line) { + return ( + line.data[field_map.product_uom_qty] * tools.priceReduce( line.data[field_map.price_unit], line.data[field_map.discount] - ); + ) + ); }); } else { - prices = _.map(records, function (line) { + prices = _.map(records, function(line) { return ( line.data[field_map.product_uom_qty] * line.data[field_map.price_unit] @@ -131,7 +139,7 @@ odoo.define("web_widget_one2many_product_picker.FieldOne2ManyProductPicker", fun }); } var total = - _.reduce(prices, function (a, b) { + _.reduce(prices, function(a, b) { return a + b; }) || 0; total = tools.monetary( @@ -149,7 +157,7 @@ odoo.define("web_widget_one2many_product_picker.FieldOne2ManyProductPicker", fun * * @returns {Object} */ - getBasicFieldParams: function () { + getBasicFieldParams: function() { return { domain: this.record.getDomain(this.recordParams), field: this.field, @@ -166,7 +174,7 @@ odoo.define("web_widget_one2many_product_picker.FieldOne2ManyProductPicker", fun /** * @override */ - _getRenderer: function () { + _getRenderer: function() { return One2ManyProductPickerRenderer; }, @@ -175,7 +183,7 @@ odoo.define("web_widget_one2many_product_picker.FieldOne2ManyProductPicker", fun * * @private */ - _processGroups: function () { + _processGroups: function() { this.searchGroups = []; var hasUserActive = false; var groups = this.options.groups || []; @@ -189,7 +197,7 @@ odoo.define("web_widget_one2many_product_picker.FieldOne2ManyProductPicker", fun } this.searchGroups.splice(0, 0, { - name: 'all', + name: "all", string: _t("All"), domain: [], order: false, @@ -204,9 +212,9 @@ odoo.define("web_widget_one2many_product_picker.FieldOne2ManyProductPicker", fun * * @override */ - _renderControlPanel: function () { + _renderControlPanel: function() { var self = this; - return this._super.apply(this, arguments).then(function () { + return this._super.apply(this, arguments).then(function() { self.control_panel.update({ cp_content: { $buttons: self.$buttons, @@ -219,7 +227,7 @@ odoo.define("web_widget_one2many_product_picker.FieldOne2ManyProductPicker", fun /** * @override */ - _renderButtons: function () { + _renderButtons: function() { if (this.isReadonly) { return this._super.apply(this, arguments); } @@ -227,8 +235,8 @@ odoo.define("web_widget_one2many_product_picker.FieldOne2ManyProductPicker", fun qweb.render("One2ManyProductPicker.ControlPanelButtons", { search_category_names: this._searchCategoryNames, search_mode: this._searchMode, - } - )); + }) + ); this.$searchInput = this.$buttons.find(".oe_search_input"); this.$groups = $( qweb.render("One2ManyProductPicker.ControlPanelGroupButtons", { @@ -244,15 +252,17 @@ odoo.define("web_widget_one2many_product_picker.FieldOne2ManyProductPicker", fun /** * @override */ - _render: function () { + _render: function() { var self = this; var def = this._super.apply(this, arguments); // Parent implementation can return 'undefined' :( return ( def && - def.then(function () { - if (!self.$el.hasClass("oe_field_one2many_product_picker_maximized")) { + def.then(function() { + if ( + !self.$el.hasClass("oe_field_one2many_product_picker_maximized") + ) { self.$el.addClass("position-relative d-flex flex-column"); } self._addMaximizeButton(); @@ -266,13 +276,13 @@ odoo.define("web_widget_one2many_product_picker.FieldOne2ManyProductPicker", fun /** * @returns {Deferred} */ - doRenderSearchRecords: function () { + doRenderSearchRecords: function() { var self = this; - return $.Deferred(function (d) { - self._getSearchRecords().then(function () { + return $.Deferred(function(d) { + self._getSearchRecords().then(function() { self.renderer.$el.scrollTop(0); - self.renderer._renderView().then(function (virtualStateDefs) { - virtualStateDefs.then(function () { + self.renderer._renderView().then(function(virtualStateDefs) { + virtualStateDefs.then(function() { d.resolve(); }); }); @@ -285,7 +295,7 @@ odoo.define("web_widget_one2many_product_picker.FieldOne2ManyProductPicker", fun * * @private */ - _addMaximizeButton: function () { + _addMaximizeButton: function() { this.$("#product_picker_maximize").remove(); this.$btnMaximize = $(qweb.render("One2ManyProductPicker.ButtonMaximize")); this.$btnMaximize @@ -298,7 +308,7 @@ odoo.define("web_widget_one2many_product_picker.FieldOne2ManyProductPicker", fun * * @private */ - _addTotalsZone: function () { + _addTotalsZone: function() { this.$("#product_picker_total").remove(); this.$totalZone = $(qweb.render("One2ManyProductPicker.Total")); this.$totalZone.appendTo(this.$el); @@ -314,7 +324,7 @@ odoo.define("web_widget_one2many_product_picker.FieldOne2ManyProductPicker", fun * @param {Boolean} merge * @returns {Deferred} */ - _getSearchRecords: function (options, merge) { + _getSearchRecords: function(options, merge) { var self = this; var arch = this.view.arch; var field_name = this.options.field_map.product; @@ -325,13 +335,16 @@ odoo.define("web_widget_one2many_product_picker.FieldOne2ManyProductPicker", fun // to continue var domain = this._getFullSearchDomain(); var soptions = options || {}; - var context = _.extend({ - 'active_search_group_name': this._activeSearchGroup.name, - 'active_search_involved_fields': this._searchContext.involvedFields, - 'active_test': this._searchContext.activeTest, - }, this.value.getContext()); + var context = _.extend( + { + active_search_group_name: this._activeSearchGroup.name, + active_search_involved_fields: this._searchContext.involvedFields, + active_test: this._searchContext.activeTest, + }, + this.value.getContext() + ); - return $.Deferred(function (d) { + return $.Deferred(function(d) { var limit = soptions.limit || self.options.records_per_page; var offset = soptions.offset || 0; self._rpc({ @@ -343,7 +356,7 @@ odoo.define("web_widget_one2many_product_picker.FieldOne2ManyProductPicker", fun offset: offset, orderBy: self._searchContext.order, kwargs: {context: context}, - }).then(function (results) { + }).then(function(results) { if (merge) { self._searchRecords = _.union( self._searchRecords || [], @@ -370,7 +383,7 @@ odoo.define("web_widget_one2many_product_picker.FieldOne2ManyProductPicker", fun * @private * @param {MouseEvent} evt */ - _onClickSearchGroup: function (evt) { + _onClickSearchGroup: function(evt) { var $btn = $(evt.target); var groupIndex = Number($btn.data("group")) || 0; this._activeSearchGroup = this.searchGroups[groupIndex]; @@ -379,26 +392,28 @@ odoo.define("web_widget_one2many_product_picker.FieldOne2ManyProductPicker", fun this._searchContext.activeTest = true; this.doRenderSearchRecords(); this.$btnLines.removeClass("active"); - $btn.parent().find(".active").removeClass("active"); + $btn.parent() + .find(".active") + .removeClass("active"); $btn.addClass("active"); }, /** * @private */ - _onClickMaximize: function () { + _onClickMaximize: function() { this.$el.toggleClass( "position-relative h-100 bg-white oe_field_one2many_product_picker_maximized" ); if (this.$buttons) { - this.$buttons.find('.dropdown-toggle').popover('update'); + this.$buttons.find(".dropdown-toggle").popover("update"); } }, /** * @private */ - _onClickLines: function () { + _onClickLines: function() { this.showLines(); }, @@ -406,14 +421,17 @@ odoo.define("web_widget_one2many_product_picker.FieldOne2ManyProductPicker", fun * @private * @param {MouseEvent} ev */ - _onClickSearchMode: function (ev) { + _onClickSearchMode: function(ev) { var self = this; ev.preventDefault(); var $target = $(ev.target); this._searchMode = $target.index(); - $target.parent().children().removeClass('active'); - $target.addClass('active'); - this.doRenderSearchRecords().then(function () { + $target + .parent() + .children() + .removeClass("active"); + $target.addClass("active"); + this.doRenderSearchRecords().then(function() { self.$searchInput.focus(); }); }, @@ -422,7 +440,7 @@ odoo.define("web_widget_one2many_product_picker.FieldOne2ManyProductPicker", fun * @private * @returns {Object} */ - _getDefaultOptions: function () { + _getDefaultOptions: function() { return { currency_field: "currency_id", records_per_page: 16, @@ -450,7 +468,7 @@ odoo.define("web_widget_one2many_product_picker.FieldOne2ManyProductPicker", fun * @private * @returns {Array} */ - _getFullSearchDomain: function () { + _getFullSearchDomain: function() { this._searchContext.involvedFields = []; var domain = _.clone(this._searchContext.domain) || []; if (this._searchContext.text) { @@ -466,12 +484,11 @@ odoo.define("web_widget_one2many_product_picker.FieldOne2ManyProductPicker", fun // Is a triplet if (domain_cloned instanceof Array) { - // Replace right leaf with the current value of the search input if (domain_cloned[2] === "$number_search") { domain_cloned[2] = Number(this._searchContext.text); involved_fields.push({ - type: 'number', + type: "number", field: domain_cloned[0], oper: domain_cloned[1], }); @@ -479,10 +496,12 @@ odoo.define("web_widget_one2many_product_picker.FieldOne2ManyProductPicker", fun typeof domain_cloned[2] === "string" && domain_cloned[2].includes("$search") ) { - domain_cloned[2] = domain_cloned[2] - .replace(/\$search/, this._searchContext.text); + domain_cloned[2] = domain_cloned[2].replace( + /\$search/, + this._searchContext.text + ); involved_fields.push({ - type: 'text', + type: "text", field: domain_cloned[0], oper: domain_cloned[1], }); @@ -502,14 +521,14 @@ odoo.define("web_widget_one2many_product_picker.FieldOne2ManyProductPicker", fun * @private * @returns {Array} */ - _getLinesDomain: function () { + _getLinesDomain: function() { if (!this.view) { return []; } var field_name = this.options.field_map.product; - var lines = this.parent_controller.model.get(this.state.id) - .data[this.name].data; - var ids = _.map(lines, function (line) { + var lines = this.parent_controller.model.get(this.state.id).data[this.name] + .data; + var ids = _.map(lines, function(line) { return line.data[field_name].data.id; }); return [["id", "in", ids]]; @@ -519,15 +538,18 @@ odoo.define("web_widget_one2many_product_picker.FieldOne2ManyProductPicker", fun * The lines are special data, so we need display it in a other way * that the search results. Use directy in-memory values. */ - showLines: function () { + showLines: function() { this._clearSearchInput(); - this.$btnLines.parent().find(".active").removeClass("active"); + this.$btnLines + .parent() + .find(".active") + .removeClass("active"); this.$btnLines.addClass("active"); this._activeSearchGroup = { - 'name': 'main_lines', + name: "main_lines", }; this._searchContext.domain = this._getLinesDomain(); - this._searchContext.order = [{'name': 'sequence'}, {'name': 'id'}]; + this._searchContext.order = [{name: "sequence"}, {name: "id"}]; this._searchContext.activeTest = false; this.doRenderSearchRecords(); }, @@ -535,16 +557,16 @@ odoo.define("web_widget_one2many_product_picker.FieldOne2ManyProductPicker", fun /** * @private */ - _clearSearchInput: function () { + _clearSearchInput: function() { this.$searchInput.val(""); this._searchContext.text = ""; }, - _onKeyPressSearch: function (evt) { + _onKeyPressSearch: function(evt) { if (evt.keyCode === $.ui.keyCode.ENTER) { var self = this; this._searchContext.text = evt.target.value; - this.doRenderSearchRecords().then(function () { + this.doRenderSearchRecords().then(function() { self.$searchInput.focus(); }); } @@ -553,10 +575,10 @@ odoo.define("web_widget_one2many_product_picker.FieldOne2ManyProductPicker", fun /** * @private */ - _onClickSearchEraser: function () { + _onClickSearchEraser: function() { var self = this; this._clearSearchInput(); - this.doRenderSearchRecords().then(function () { + this.doRenderSearchRecords().then(function() { self.$searchInput.focus(); }); }, @@ -565,12 +587,15 @@ odoo.define("web_widget_one2many_product_picker.FieldOne2ManyProductPicker", fun * @private * @param {DropdownEvent} evt */ - _onShowSearchDropdown: function (evt) { - + _onShowSearchDropdown: function(evt) { // Workaround: This "ensures" a correct dropdown position - var offset = $(evt.currentTarget).find(".dropdown-toggle").parent().height(); - _.defer(function () { - $(evt.currentTarget).find(".dropdown-menu") + var offset = $(evt.currentTarget) + .find(".dropdown-toggle") + .parent() + .height(); + _.defer(function() { + $(evt.currentTarget) + .find(".dropdown-menu") .css("transform", "translate3d(0px, " + offset + "px, 0px)"); }); }, @@ -581,7 +606,7 @@ odoo.define("web_widget_one2many_product_picker.FieldOne2ManyProductPicker", fun * @private * @param {CustomEvent} evt */ - _onCreateQuickRecord: function (evt) { + _onCreateQuickRecord: function(evt) { evt.stopPropagation(); var self = this; this.parent_controller.model.setPureVirtual(evt.data.id, false); @@ -591,20 +616,22 @@ odoo.define("web_widget_one2many_product_picker.FieldOne2ManyProductPicker", fun this._setValue( {operation: "ADD", id: evt.data.id}, {notifyChange: false} - ).then(function () { + ).then(function() { if (self.options.auto_save) { - self.parent_controller.saveRecord(undefined, {stayInEdit: true}).then(function (rrr) { - // Because 'create' generates a new state and we can't know these new id we - // need force update the all the current states. - self._setValue( - {operation: "UPDATE", id: evt.data.id}, - {doNotSetDirty: true} - ).then(function () { - if (evt.data.callback) { - evt.data.callback(); - } + self.parent_controller + .saveRecord(undefined, {stayInEdit: true}) + .then(function(rrr) { + // Because 'create' generates a new state and we can't know these new id we + // need force update the all the current states. + self._setValue( + {operation: "UPDATE", id: evt.data.id}, + {doNotSetDirty: true} + ).then(function() { + if (evt.data.callback) { + evt.data.callback(); + } + }); }); - }); } else if (evt.data.callback) { evt.data.callback(); } @@ -615,7 +642,7 @@ odoo.define("web_widget_one2many_product_picker.FieldOne2ManyProductPicker", fun product_picker_modified: true, }); // This will trigger an "state" update - this._setValue({operation: "ADD", id: evt.data.id}).then(function () { + this._setValue({operation: "ADD", id: evt.data.id}).then(function() { if (evt.data.callback) { evt.data.callback(); } @@ -629,7 +656,7 @@ odoo.define("web_widget_one2many_product_picker.FieldOne2ManyProductPicker", fun * @private * @param {CustomEevent} evt */ - _onUpdateQuickRecord: function (evt) { + _onUpdateQuickRecord: function(evt) { evt.stopPropagation(); var self = this; @@ -638,19 +665,27 @@ odoo.define("web_widget_one2many_product_picker.FieldOne2ManyProductPicker", fun this._setValue( {operation: "UPDATE", id: evt.data.id, data: evt.data.data}, {notifyChange: false} - ).then(function () { + ).then(function() { if (self.options.auto_save) { - self.parent_controller.saveRecord(undefined, {stayInEdit: true}).then(function () { - // Workaround to get updated values - self.parent_controller.model.reload(self.value.id).then(function (result) { - var new_data = self.parent_controller.model.get(result); - self.value.data = new_data.data; - self.renderer.updateState(self.value, {force: true}); - if (evt.data.callback) { - evt.data.callback(); - } + self.parent_controller + .saveRecord(undefined, {stayInEdit: true}) + .then(function() { + // Workaround to get updated values + self.parent_controller.model + .reload(self.value.id) + .then(function(result) { + var new_data = self.parent_controller.model.get( + result + ); + self.value.data = new_data.data; + self.renderer.updateState(self.value, { + force: true, + }); + if (evt.data.callback) { + evt.data.callback(); + } + }); }); - }); } else if (evt.data.callback) { evt.data.callback(); } @@ -661,9 +696,11 @@ odoo.define("web_widget_one2many_product_picker.FieldOne2ManyProductPicker", fun product_picker_modified: true, }); // This will trigger an "state" update - this._setValue( - {operation: "UPDATE", id: evt.data.id, data: evt.data.data}, - ).then(function () { + this._setValue({ + operation: "UPDATE", + id: evt.data.id, + data: evt.data.data, + }).then(function() { if (evt.data.callback) { evt.data.callback(); } @@ -674,16 +711,18 @@ odoo.define("web_widget_one2many_product_picker.FieldOne2ManyProductPicker", fun /** * Handle auto_save when remove a record */ - _onListRecordRemove: function (evt) { + _onListRecordRemove: function(evt) { evt.stopPropagation(); var self = this; - this._setValue({operation: "DELETE", ids: [evt.data.id]}).then(function () { + this._setValue({operation: "DELETE", ids: [evt.data.id]}).then(function() { if (self.options.auto_save) { - self.parent_controller.saveRecord(undefined, {stayInEdit: true}).then(function () { - if (evt.data.callback) { - evt.data.callback(); - } - }); + self.parent_controller + .saveRecord(undefined, {stayInEdit: true}) + .then(function() { + if (evt.data.callback) { + evt.data.callback(); + } + }); } else if (evt.data.callback) { evt.data.callback(); } @@ -693,7 +732,7 @@ odoo.define("web_widget_one2many_product_picker.FieldOne2ManyProductPicker", fun /** * @private */ - _onUpdateSubtotal: function () { + _onUpdateSubtotal: function() { this.updateSubtotalPrice(); }, @@ -703,7 +742,7 @@ odoo.define("web_widget_one2many_product_picker.FieldOne2ManyProductPicker", fun * * @private */ - _onLoadMore: function () { + _onLoadMore: function() { if (this._isLoading) { return; } @@ -713,7 +752,7 @@ odoo.define("web_widget_one2many_product_picker.FieldOne2ManyProductPicker", fun offset: this._searchOffset, }, true - ).then(function (records) { + ).then(function(records) { self.renderer.appendSearchRecords(records); }); }, @@ -722,7 +761,7 @@ odoo.define("web_widget_one2many_product_picker.FieldOne2ManyProductPicker", fun * @private * @param {CustomEvent} evt */ - _onLoadingRecords: function (evt) { + _onLoadingRecords: function(evt) { this._isLoading = !evt.data.finished; this._blockControlPanel(this._isLoading); if (this.renderer) { @@ -734,22 +773,21 @@ odoo.define("web_widget_one2many_product_picker.FieldOne2ManyProductPicker", fun * @private * @param {Boolean} block */ - _blockControlPanel: function (block) { + _blockControlPanel: function(block) { if (this.$buttons) { this.$buttons.find("input,button").attr("disabled", block); } }, - /** * Refresh lines count on every change. * * @override */ - _setValue: function (value, options) { + _setValue: function(value, options) { var self = this; - return this._super.apply(this, arguments).then(function () { + return this._super.apply(this, arguments).then(function() { self.updateBadgeLines(); self.updateSubtotalPrice(); }); diff --git a/web_widget_one2many_product_picker/static/src/scss/main_variables.scss b/web_widget_one2many_product_picker/static/src/scss/main_variables.scss index 159cf8450..96ab3f083 100644 --- a/web_widget_one2many_product_picker/static/src/scss/main_variables.scss +++ b/web_widget_one2many_product_picker/static/src/scss/main_variables.scss @@ -1,6 +1,6 @@ $one2many-product-picker-grid-breakpoints: map-merge( $grid-breakpoints, - ( - xxl: 1440px, - ) + ( + xxl: 1440px, + ) ); diff --git a/web_widget_one2many_product_picker/static/src/scss/one2many_product_picker.scss b/web_widget_one2many_product_picker/static/src/scss/one2many_product_picker.scss index ef0479347..d851bfe4d 100644 --- a/web_widget_one2many_product_picker/static/src/scss/one2many_product_picker.scss +++ b/web_widget_one2many_product_picker/static/src/scss/one2many_product_picker.scss @@ -45,7 +45,10 @@ } .oe_one2many_product_picker_view { - @include make-grid-columns($columns: 24, $breakpoints: $one2many-product-picker-grid-breakpoints); + @include make-grid-columns( + $columns: 24, + $breakpoints: $one2many-product-picker-grid-breakpoints + ); overflow: auto; @@ -58,7 +61,10 @@ user-select: none; background-color: transparent; perspective: 1000px; - transition: top $one2many-product-picker-transition-3d-time, left $one2many-product-picker-transition-3d-time, width $one2many-product-picker-transition-3d-time, height $one2many-product-picker-transition-3d-time; + transition: top $one2many-product-picker-transition-3d-time, + left $one2many-product-picker-transition-3d-time, + width $one2many-product-picker-transition-3d-time, + height $one2many-product-picker-transition-3d-time; height: $one2many-product-picker-card-min-height; &.blocked { @@ -97,11 +103,13 @@ } } - .o_field_widget, .oe_one2many_product_picker_form_buttons .btn { + .o_field_widget, + .oe_one2many_product_picker_form_buttons .btn { transform: scale($one2many-product-picker-zoom-scale); margin-bottom: 1.3em !important; } - .o_field_widget, .w-100 { + .o_field_widget, + .w-100 { width: 100% / $one2many-product-picker-zoom-scale !important; } } @@ -133,7 +141,9 @@ position: relative; width: 100%; height: $one2many-product-picker-card-min-height; - transition: transform $one2many-product-picker-transition-3d-time, height $one2many-product-picker-transition-3d-time/2 ease-in-out $one2many-product-picker-transition-3d-time/2; + transition: transform $one2many-product-picker-transition-3d-time, + height $one2many-product-picker-transition-3d-time/2 ease-in-out + $one2many-product-picker-transition-3d-time/2; transform-style: preserve-3d; .position-absolute { @@ -217,7 +227,9 @@ font-size: 0.95rem; z-index: 0; } - .add_product, .product_qty, .price_unit { + .add_product, + .product_qty, + .price_unit { cursor: pointer; } } @@ -247,12 +259,12 @@ @keyframes productPickerCatchAttention { 0% { - transform: scale(1.0); + transform: scale(1); } 50% { transform: scale(1.5); } 100% { - transform: scale(1.0); + transform: scale(1); } } diff --git a/web_widget_one2many_product_picker/static/src/xml/one2many_product_picker.xml b/web_widget_one2many_product_picker/static/src/xml/one2many_product_picker.xml index 50133fa6a..13551bfe7 100644 --- a/web_widget_one2many_product_picker/static/src/xml/one2many_product_picker.xml +++ b/web_widget_one2many_product_picker/static/src/xml/one2many_product_picker.xml @@ -1,58 +1,83 @@ diff --git a/web_widget_one2many_product_picker/static/src/xml/one2many_product_picker_quick_create.xml b/web_widget_one2many_product_picker/static/src/xml/one2many_product_picker_quick_create.xml index be1b9d2b1..43466160a 100644 --- a/web_widget_one2many_product_picker/static/src/xml/one2many_product_picker_quick_create.xml +++ b/web_widget_one2many_product_picker/static/src/xml/one2many_product_picker_quick_create.xml @@ -1,18 +1,22 @@ diff --git a/web_widget_one2many_product_picker/static/src/xml/one2many_product_picker_quick_modif_price.xml b/web_widget_one2many_product_picker/static/src/xml/one2many_product_picker_quick_modif_price.xml index 48f1ff09a..26a50f1d3 100644 --- a/web_widget_one2many_product_picker/static/src/xml/one2many_product_picker_quick_modif_price.xml +++ b/web_widget_one2many_product_picker/static/src/xml/one2many_product_picker_quick_modif_price.xml @@ -1,14 +1,18 @@ diff --git a/web_widget_one2many_product_picker/static/tests/widget_tests.js b/web_widget_one2many_product_picker/static/tests/widget_tests.js index 9c52b193b..64049fdfb 100644 --- a/web_widget_one2many_product_picker/static/tests/widget_tests.js +++ b/web_widget_one2many_product_picker/static/tests/widget_tests.js @@ -1,136 +1,228 @@ /* global QUnit */ // Copyright 2020 Tecnativa - Alexandre Díaz // License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -odoo.define('web_widget_one2many_product_picker.widget_tests', function (require) { +odoo.define("web_widget_one2many_product_picker.widget_tests", function(require) { "use strict"; - var FormView = require('web.FormView'); - var testUtils = require('web.test_utils'); + var FormView = require("web.FormView"); + var testUtils = require("web.test_utils"); var createView = testUtils.createView; - var getArch = function () { - return '
' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - ''; + var getArch = function() { + return ( + "
" + + '' + + "" + + "" + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + "" + + "" + + "" + ); }; - QUnit.module('Web Widget One2Many Product Picker', { - beforeEach: function () { - this.data = { - foo: { - fields: { - currency_id: {string: "Currency", type: "many2one", relation: "currency"}, - line_ids: {string: "Lines Test", type: "one2many", relation: "line", relation_field: "foo_id"}, - display_name: {string: "Display Name", type: "char"}, + QUnit.module( + "Web Widget One2Many Product Picker", + { + beforeEach: function() { + this.data = { + foo: { + fields: { + currency_id: { + string: "Currency", + type: "many2one", + relation: "currency", + }, + line_ids: { + string: "Lines Test", + type: "one2many", + relation: "line", + relation_field: "foo_id", + }, + display_name: {string: "Display Name", type: "char"}, + }, + records: [ + { + id: 1, + line_ids: [1, 2], + currency_id: 1, + display_name: "FT01", + }, + ], }, - records: [ - {id: 1, line_ids: [1, 2], currency_id: 1, display_name: "FT01"}, - ], - }, - line: { - fields: { - name: {string: "Product Name", type: "string"}, - product_id: {string: "Product", type: "many2one", relation: "product"}, - product_uom: {string: "UoM", type: "many2one", relation: "uom"}, - product_uom_qty: {string: "Qty", type: "integer"}, - price_unit: {string: "Product Price", type: "float"}, - price_reduce: {string: "Product Price Reduce", type: "float"}, - foo_id: {string: "Parent", type: "many2one", relation: "foo"}, + line: { + fields: { + name: {string: "Product Name", type: "string"}, + product_id: { + string: "Product", + type: "many2one", + relation: "product", + }, + product_uom: { + string: "UoM", + type: "many2one", + relation: "uom", + }, + product_uom_qty: {string: "Qty", type: "integer"}, + price_unit: {string: "Product Price", type: "float"}, + price_reduce: { + string: "Product Price Reduce", + type: "float", + }, + foo_id: { + string: "Parent", + type: "many2one", + relation: "foo", + }, + }, + records: [ + { + id: 1, + name: "Large Cabinet", + product_id: 1, + product_uom: 1, + product_uom_qty: 3, + price_unit: 9.99, + price_reduce: 9.0, + foo_id: 1, + }, + { + id: 2, + name: "Cabinet with Doors", + product_id: 2, + product_uom: 1, + product_uom_qty: 8, + price_unit: 42.99, + price_reduce: 40.0, + foo_id: 1, + }, + ], }, - records: [ - {id: 1, name: "Large Cabinet", product_id: 1, product_uom: 1, product_uom_qty: 3, price_unit: 9.99, price_reduce: 9.00, foo_id: 1}, - {id: 2, name: "Cabinet with Doors", product_id: 2, product_uom: 1, product_uom_qty: 8, price_unit: 42.99, price_reduce: 40.00, foo_id: 1}, - ], - }, - product: { - fields: { - name: {string : "Product name", type: "char"}, - display_name: {string : "Display Name", type: "char"}, - list_price: {string: "Price", type: "float"}, - image_medium: {string: "Image Medium", type: "binary"}, - uom_category_id: {string: "Category", type: "many2one", relation: "uom_category"}, + product: { + fields: { + name: {string: "Product name", type: "char"}, + display_name: {string: "Display Name", type: "char"}, + list_price: {string: "Price", type: "float"}, + image_medium: {string: "Image Medium", type: "binary"}, + uom_category_id: { + string: "Category", + type: "many2one", + relation: "uom_category", + }, + }, + records: [ + { + id: 1, + name: "Large Cabinet", + display_name: "Large Cabinet", + list_price: 9.99, + image_medium: "", + uom_category_id: 1, + }, + { + id: 2, + name: "Cabinet with Doors", + display_name: "Cabinet with Doors", + list_price: 42.0, + image_medium: "", + uom_category_id: 1, + }, + ], }, - records: [ - {id: 1, name: "Large Cabinet", display_name: "Large Cabinet", list_price: 9.99, image_medium: "", uom_category_id: 1}, - {id: 2, name: "Cabinet with Doors", display_name: "Cabinet with Doors", list_price: 42.0, image_medium: "", uom_category_id: 1}, - ], - }, - uom_category: { - fields: { - display_name: {string : "Display Name", type: "char"}, + uom_category: { + fields: { + display_name: {string: "Display Name", type: "char"}, + }, + records: [{id: 1, display_name: "Unit(s)"}], }, - records: [ - {id: 1, display_name: "Unit(s)"}, - ], - }, - uom: { - fields: { - name: {string: "Name", type: "char"}, + uom: { + fields: { + name: {string: "Name", type: "char"}, + }, + records: [{id: 1, name: "Unit(s)"}], }, - records: [ - {id: 1, name: "Unit(s)"}, - ], - }, - currency: { - fields: { - name: {string: "Name", type: "char"}, - symbol: {string: "Symbol", type: "char"}, + currency: { + fields: { + name: {string: "Name", type: "char"}, + symbol: {string: "Symbol", type: "char"}, + }, + records: [{id: 1, name: "Eur", symbol: "€"}], }, - records: [ - {id: 1, name: "Eur", symbol: "€"}, - ], - }, - }; + }; + }, }, - }, function () { - QUnit.test('Load widget', function (assert) { - assert.expect(4); + function() { + QUnit.test("Load widget", function(assert) { + assert.expect(4); - var form = createView({ - View: FormView, - model: 'foo', - data: this.data, - arch: getArch(), - res_id: 1, - viewOptions: { - ids: [1], - index: 0, - }, - mockRPC: function (route, args) { - if (route === '/web/dataset/call_kw/foo/read') { - assert.deepEqual(args.args[1], ['currency_id', 'line_ids', 'display_name'], - 'should only read "currency_id", "line_ids" and "display_name"'); - return $.when(this.data.foo.records); - } else if (route === '/web/dataset/call_kw/line/read') { - assert.deepEqual(args.args[1], ['name', 'product_id', 'price_reduce', 'price_unit', 'foo_id', 'product_uom_qty', 'product_uom'], - 'should only read "name", "product_id", "price_reduce", "price_unit", "foo_id", "product_uom_qty" and "product_uom"'); - return $.when(this.data.line.records); - } else if (route === '/web/dataset/call_kw/product/search_read') { - assert.deepEqual(args.kwargs.fields, ['id', 'uom_id', 'display_name', 'uom_category_id', 'image_medium', 'list_price'], - 'should only read "id", "uom_id", "display_name", "uom_category_id", "image_medium" and "list_price"'); - return $.when(this.data.product.records); - } - return this._super.apply(this, arguments); - }, + var form = createView({ + View: FormView, + model: "foo", + data: this.data, + arch: getArch(), + res_id: 1, + viewOptions: { + ids: [1], + index: 0, + }, + mockRPC: function(route, args) { + if (route === "/web/dataset/call_kw/foo/read") { + assert.deepEqual( + args.args[1], + ["currency_id", "line_ids", "display_name"], + 'should only read "currency_id", "line_ids" and "display_name"' + ); + return $.when(this.data.foo.records); + } else if (route === "/web/dataset/call_kw/line/read") { + assert.deepEqual( + args.args[1], + [ + "name", + "product_id", + "price_reduce", + "price_unit", + "foo_id", + "product_uom_qty", + "product_uom", + ], + 'should only read "name", "product_id", "price_reduce", "price_unit", "foo_id", "product_uom_qty" and "product_uom"' + ); + return $.when(this.data.line.records); + } else if ( + route === "/web/dataset/call_kw/product/search_read" + ) { + assert.deepEqual( + args.kwargs.fields, + [ + "id", + "uom_id", + "display_name", + "uom_category_id", + "image_medium", + "list_price", + ], + 'should only read "id", "uom_id", "display_name", "uom_category_id", "image_medium" and "list_price"' + ); + return $.when(this.data.product.records); + } + return this._super.apply(this, arguments); + }, + }); + + assert.ok( + form.$(".oe_field_one2many_product_picker").is(":visible"), + "should have a visible one2many product picker" + ); + + form.destroy(); }); - - assert.ok(form.$('.oe_field_one2many_product_picker').is(':visible'), - "should have a visible one2many product picker"); - - form.destroy(); - }); - }); - + } + ); }); diff --git a/web_widget_one2many_product_picker/templates/assets.xml b/web_widget_one2many_product_picker/templates/assets.xml index 3c476236a..cbe883e47 100644 --- a/web_widget_one2many_product_picker/templates/assets.xml +++ b/web_widget_one2many_product_picker/templates/assets.xml @@ -1,38 +1,78 @@ - + - -