diff --git a/web_widget_one2many_product_picker/README.rst b/web_widget_one2many_product_picker/README.rst index bd6dc7ef0..21ec5ffbe 100644 --- a/web_widget_one2many_product_picker/README.rst +++ b/web_widget_one2many_product_picker/README.rst @@ -14,16 +14,16 @@ Web Widget One2Many Product Picker :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html :alt: License: AGPL-3 .. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fweb-lightgray.png?logo=github - :target: https://github.com/OCA/web/tree/12.0/web_widget_one2many_product_picker + :target: https://github.com/OCA/web/tree/13.0/web_widget_one2many_product_picker :alt: OCA/web .. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png - :target: https://translation.odoo-community.org/projects/web-12-0/web-12-0-web_widget_one2many_product_picker + :target: https://translation.odoo-community.org/projects/web-13-0/web-13-0-web_widget_one2many_product_picker :alt: Translate me on Weblate .. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png - :target: https://runbot.odoo-community.org/runbot/162/12.0 + :target: https://runbot.odoo-community.org/runbot/162/13.0 :alt: Try me on Runbot -|badge1| |badge2| |badge3| |badge4| |badge5| +|badge1| |badge2| |badge3| |badge4| |badge5| Adds the 'one2many_product_picker' friendly mobile widget to create one2many lines linked with product.product records. @@ -196,12 +196,12 @@ accept changes. Parts of the widget: ~~~~~~~~~~~~~~~~~~~~ - .. image:: https://raw.githubusercontent.com/OCA/web/12.0/web_widget_one2many_product_picker/static/img/product_picker_anat.png + .. image:: https://raw.githubusercontent.com/OCA/web/13.0/web_widget_one2many_product_picker/static/img/product_picker_anat.png Preview: ~~~~~~~~ - .. image:: https://raw.githubusercontent.com/OCA/web/12.0/web_widget_one2many_product_picker/static/img/product_picker.gif + .. image:: https://raw.githubusercontent.com/OCA/web/13.0/web_widget_one2many_product_picker/static/img/product_picker.gif Known issues / Roadmap ====================== @@ -215,7 +215,7 @@ Bug Tracker Bugs are tracked on `GitHub Issues `_. In case of trouble, please check there if your issue has already been reported. If you spotted it first, help us smashing it by providing a detailed and welcomed -`feedback `_. +`feedback `_. Do not contact contributors directly about support or help with technical issues. @@ -249,6 +249,6 @@ OCA, or the Odoo Community Association, is a nonprofit organization whose mission is to support the collaborative development of Odoo features and promote its widespread use. -This module is part of the `OCA/web `_ project on GitHub. +This module is part of the `OCA/web `_ project on GitHub. You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/web_widget_one2many_product_picker/__manifest__.py b/web_widget_one2many_product_picker/__manifest__.py index 75eecd4ca..f9b68f363 100644 --- a/web_widget_one2many_product_picker/__manifest__.py +++ b/web_widget_one2many_product_picker/__manifest__.py @@ -4,14 +4,14 @@ { "name": "Web Widget One2Many Product Picker", "summary": "Widget to select products on one2many fields", - "version": "12.0.2.3.0", + "version": "13.0.1.0.0", "category": "Website", - "author": "Tecnativa, " "Odoo Community Association (OCA)", - "website": "https://www.tecnativa.com", + "author": "Tecnativa, Odoo Community Association (OCA)", + "website": "https://github.com/OCA/web", "license": "AGPL-3", - "depends": ["product",], - "data": ["templates/assets.xml",], - "qweb": ["static/src/xml/one2many_product_picker.xml",], + "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/i18n/web_widget_one2many_product_picker.pot b/web_widget_one2many_product_picker/i18n/web_widget_one2many_product_picker.pot index 8e01a53b5..ad2e0a011 100644 --- a/web_widget_one2many_product_picker/i18n/web_widget_one2many_product_picker.pot +++ b/web_widget_one2many_product_picker/i18n/web_widget_one2many_product_picker.pot @@ -107,4 +107,3 @@ msgstr "" #, python-format msgid "[No widget %s]" msgstr "" - diff --git a/web_widget_one2many_product_picker/models/product_product.py b/web_widget_one2many_product_picker/models/product_product.py index f27dc8c7f..2e477a44e 100644 --- a/web_widget_one2many_product_picker/models/product_product.py +++ b/web_widget_one2many_product_picker/models/product_product.py @@ -1,37 +1,21 @@ # Copyright 2020 Tecnativa - Alexandre D. Díaz # License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl). -from odoo import api, fields, models, tools +from odoo import fields, models class ProductProduct(models.Model): _inherit = "product.product" image_variant_medium = fields.Binary( - "Variant Image Medium (Computed)", - compute="_compute_variant_image", + "Variant Image Medium (Related)", + related="image_512", help="This field holds the image used as image for the product variant" "or product image medium, limited to 512x512px.", ) image_variant_big = fields.Binary( - "Variant Image Big (Computed)", - compute="_compute_variant_image", + "Variant Image Big (Related)", + related="image_1024", help="This field holds the image used as image for the product variant" "or product image, limited to 1024x1024px.", ) - - @api.depends("image_variant", "product_tmpl_id.image") - def _compute_variant_image(self): - for record in self: - if record.image_variant: - resized_images = tools.image_get_resized_images( - record.image_variant, - return_big=False, - return_small=False, - 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 - record.image_variant_big = record.product_tmpl_id.image diff --git a/web_widget_one2many_product_picker/static/description/index.html b/web_widget_one2many_product_picker/static/description/index.html index de595921c..a83807324 100644 --- a/web_widget_one2many_product_picker/static/description/index.html +++ b/web_widget_one2many_product_picker/static/description/index.html @@ -367,7 +367,7 @@ ul.auto-toc { !! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! --> -

Beta License: AGPL-3 OCA/web Translate me on Weblate Try me on Runbot

+

Beta License: AGPL-3 OCA/web Translate me on Weblate Try me on Runbot

Adds the ‘one2many_product_picker’ friendly mobile widget to create one2many lines linked with product.product records.

Table of contents

@@ -579,13 +579,13 @@ accept changes.

Preview:

-https://raw.githubusercontent.com/OCA/web/12.0/web_widget_one2many_product_picker/static/img/product_picker.gif +https://raw.githubusercontent.com/OCA/web/13.0/web_widget_one2many_product_picker/static/img/product_picker.gif
@@ -601,7 +601,7 @@ accept changes.

Bugs are tracked on GitHub Issues. In case of trouble, please check there if your issue has already been reported. If you spotted it first, help us smashing it by providing a detailed and welcomed -feedback.

+feedback.

Do not contact contributors directly about support or help with technical issues.

@@ -633,7 +633,7 @@ If you spotted it first, help us smashing it by providing a detailed and welcome

OCA, or the Odoo Community Association, is a nonprofit organization whose mission is to support the collaborative development of Odoo features and promote its widespread use.

-

This module is part of the OCA/web project on GitHub.

+

This module is part of the OCA/web project on GitHub.

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

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 361aa3bbb..5181b1de4 100644 --- a/web_widget_one2many_product_picker/static/src/js/tools.js +++ b/web_widget_one2many_product_picker/static/src/js/tools.js @@ -3,7 +3,7 @@ odoo.define("web_widget_one2many_product_picker.tools", function(require) { "use strict"; - var field_utils = require("web.field_utils"); + const field_utils = require("web.field_utils"); /** * Calculate the price with discount 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 478b0622d..63a54b389 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,3 +1,4 @@ +/* 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.ProductPickerQuickCreateForm", function( @@ -5,18 +6,18 @@ odoo.define("web_widget_one2many_product_picker.ProductPickerQuickCreateForm", f ) { "use strict"; - 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") + const core = require("web.core"); + const Widget = require("web.Widget"); + const widgetRegistry = require("web.widget_registry"); + const ProductPickerQuickCreateFormView = require("web_widget_one2many_product_picker.ProductPickerQuickCreateFormView") .ProductPickerQuickCreateFormView; - var qweb = core.qweb; + const qweb = core.qweb; /** * This widget render a Form. Used by FieldOne2ManyProductPicker */ - var ProductPickerQuickCreateForm = Widget.extend({ + const ProductPickerQuickCreateForm = Widget.extend({ className: "oe_one2many_product_picker_quick_create", xmlDependencies: [ "/web_widget_one2many_product_picker/static/src/xml/one2many_product_picker_quick_create.xml", @@ -50,10 +51,9 @@ odoo.define("web_widget_one2many_product_picker.ProductPickerQuickCreateForm", f * @override */ start: function() { - var self = this; - var def1 = this._super.apply(this, arguments); - var form_arch = this._generateFormArch(); - var fieldsView = { + const def1 = this._super.apply(this, arguments); + const form_arch = this._generateFormArch(); + const fieldsView = { arch: form_arch, fields: this.fields, viewFields: this.fields, @@ -62,11 +62,11 @@ odoo.define("web_widget_one2many_product_picker.ProductPickerQuickCreateForm", f model: this.basicFieldParams.field.relation, }; - var node_context = this.node.attr("context") || "{}"; + const node_context = this.node.attr("context") || "{}"; this.nodeContext = py.eval(node_context, { active_id: this.res_id || false, }); - var refinedContext = _.extend( + const refinedContext = _.extend( {}, this.main_state.getContext(), this.nodeContext @@ -92,13 +92,13 @@ odoo.define("web_widget_one2many_product_picker.ProductPickerQuickCreateForm", f // 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); + const def2 = this.formView.getController(this).then(controller => { + this.controller = controller; + this.$el.empty(); + this.controller.appendTo(this.$el); }); - return $.when(def1, def2); + return Promise.all([def1, def2]); }, on_attach_callback: function() { @@ -112,7 +112,7 @@ odoo.define("web_widget_one2many_product_picker.ProductPickerQuickCreateForm", f * @returns {String} */ _generateFormArch: function() { - var template = + let template = ""; template += this.basicFieldParams.field.views.form.arch; template += ""; @@ -140,21 +140,20 @@ odoo.define("web_widget_one2many_product_picker.ProductPickerQuickCreateForm", f this.id = evt.data.baseRecordID; this.start(); } else { - var self = this; this.getParent() ._generateVirtualState({}, this.editContext) - .then(function(state) { - var data = {}; - data[self.compareKey] = { + .then(state => { + const data = {}; + data[this.compareKey] = { operation: "ADD", id: evt.data.compareValue, }; - self.basicFieldParams.model + this.basicFieldParams.model ._applyChange(state.id, data) - .then(function() { - self.res_id = state.res_id; - self.id = state.id; - self.start(); + .then(() => { + this.res_id = state.res_id; + this.id = state.id; + this.start(); }); }); } 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 8418987fa..6fc6c12f9 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 @@ -10,20 +10,19 @@ odoo.define( * is used by the RecordQuickCreate in One2ManyProductPicker views. */ - var QuickCreateFormView = require("web.QuickCreateFormView"); - var BasicModel = require("web.BasicModel"); - var core = require("web.core"); + const QuickCreateFormView = require("web.QuickCreateFormView"); + const BasicModel = require("web.BasicModel"); + const core = require("web.core"); - var qweb = core.qweb; + const 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]; + const ignore_changes = record.context.ignore_onchanges; + for (const index in ignore_changes) { + const field_name = ignore_changes[index]; delete values[field_name]; } delete record.context.ignore_onchanges; @@ -32,7 +31,7 @@ odoo.define( }, }); - var ProductPickerQuickCreateFormRenderer = QuickCreateFormView.prototype.config.Renderer.extend( + const ProductPickerQuickCreateFormRenderer = QuickCreateFormView.prototype.config.Renderer.extend( { /** * @override @@ -46,7 +45,7 @@ odoo.define( } ); - var ProductPickerQuickCreateFormController = QuickCreateFormView.prototype.config.Controller.extend( + const ProductPickerQuickCreateFormController = QuickCreateFormView.prototype.config.Controller.extend( { events: _.extend({}, QuickCreateFormView.prototype.events, { "click .oe_record_add": "_onClickAdd", @@ -67,14 +66,14 @@ odoo.define( * Create or accept changes */ auto: function() { - var record = this.model.get(this.handle); + const record = this.model.get(this.handle); if ( record.context.has_changes_confirmed || typeof record.context.has_changes_confirmed === "undefined" ) { return; } - var state = this._getRecordState(); + const state = this._getRecordState(); if (state === "new") { this._add(); } else if (state === "dirty") { @@ -87,18 +86,20 @@ odoo.define( * - record: Normal * - new: Is a new record * - dirty: Has changes + * + * @returns {Object} */ _getRecordState: function() { - var record = this.model.get(this.handle); - var state = "record"; + const record = this.model.get(this.handle); + let state = "record"; if (this.model.isNew(record.id)) { state = "new"; } else if (record.isDirty()) { state = "dirty"; } if (state === "new") { - for (var index in this.mainRecordData.data) { - var recordData = this.mainRecordData.data[index]; + for (const index in this.mainRecordData.data) { + const recordData = this.mainRecordData.data[index]; if (recordData.ref === record.ref) { if (record.isDirty()) { state = "dirty"; @@ -160,12 +161,12 @@ odoo.define( /** * @private - * @param {Array[String]} fields_changed + * @param {Array} fields_changed * @returns {Boolean} */ _needReloadCard: function(fields_changed) { - for (var index in fields_changed) { - var field = fields_changed[index]; + for (const index in fields_changed) { + const field = fields_changed[index]; if (field === this.fieldMap[this.compareKey]) { return true; } @@ -182,33 +183,33 @@ odoo.define( * @param {ChangeEvent} ev */ _onFieldChanged: function(ev) { - var fields_changed = Object.keys(ev.data.changes); + const fields_changed = Object.keys(ev.data.changes); if (this._needReloadCard(fields_changed)) { - var field = ev.data.changes[fields_changed[0]]; - var new_value = false; + const field = ev.data.changes[fields_changed[0]]; + let new_value = false; if (typeof field === "object") { new_value = field.id; } else { new_value = field; } - var reload_values = { + const reload_values = { compareValue: new_value, }; - var record = this.model.get(this.handle); - if (!("base_record_id" in record.context)) { - var old_value = record.data[this.compareKey]; + const record = this.model.get(this.handle); + if ("base_record_id" in record.context) { + reload_values.baseRecordID = record.context.base_record_id; + reload_values.baseRecordResID = + record.context.base_record_res_id; + reload_values.baseRecordCompareValue = + record.context.base_record_compare_value; + } else { + let old_value = record.data[this.compareKey]; 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.baseRecordResID = - record.context.base_record_res_id; - reload_values.baseRecordCompareValue = - record.context.base_record_compare_value; } this.trigger_up("reload_view", reload_values); @@ -237,28 +238,30 @@ odoo.define( _add: function() { if (this._disabled) { // Don't do anything if we are already creating a record - return $.Deferred(); + return Promise.resolve(); } this.model.updateRecordContext(this.handle, { has_changes_confirmed: true, }); - var self = this; this._disableQuickCreate(); return this.saveRecord(this.handle, { stayInEdit: true, reload: true, savePoint: true, viewType: "form", - }).then(function() { - var record = self.model.get(self.handle); - self.trigger_up("restore_flip_card", { - success_callback: function() { - self.trigger_up("create_quick_record", { + }).then(() => { + const record = this.model.get(this.handle); + this.model.updateRecordContext(this.handle, {saving: true}); + this.trigger_up("restore_flip_card", { + success_callback: () => { + this.trigger_up("create_quick_record", { id: record.id, + callback: () => { + this.model.unsetDirty(this.handle); + // Self._updateButtons(); + this._enableQuickCreate(); + }, }); - self.model.unsetDirty(self.handle); - // Self._updateButtons(); - self._enableQuickCreate(); }, block: true, }); @@ -267,51 +270,51 @@ odoo.define( _remove: function() { if (this._disabled) { - // Don't do anything if we are already creating a record - return $.Deferred(); + return Promise.resolve(); } this._disableQuickCreate(); this.trigger_up("restore_flip_card", {block: true}); - var record = this.model.get(this.handle); + const record = this.model.get(this.handle); this.trigger_up("list_record_remove", { id: record.id, }); }, _change: function() { - var self = this; + const self = this; if (this._disabled) { // Don't do anything if we are already creating a record - return $.Deferred(); + return Promise.resolve(); } this._disableQuickCreate(); this.model.updateRecordContext(this.handle, { has_changes_confirmed: true, }); - var record = this.model.get(this.handle); + const record = this.model.get(this.handle); this.trigger_up("restore_flip_card", { success_callback: function() { self.trigger_up("update_quick_record", { id: record.id, + callback: function() { + self.model.unsetDirty(self.handle); + // Self._updateButtons(); + self._enableQuickCreate(); + }, }); - self.model.unsetDirty(self.handle); - // Self._updateButtons(); - self._enableQuickCreate(); }, block: true, }); }, _discard: function() { - var self = this; if (this._disabled) { // Don't do anything if we are already creating a record - return $.Deferred(); + return Promise.resolve(); } this._disableQuickCreate(); - var record = this.model.get(this.handle); + const record = this.model.get(this.handle); this.model.discardChanges(this.handle, { rollback: true, }); @@ -324,11 +327,11 @@ odoo.define( this._updateButtons(); this._enableQuickCreate(); } else { - this.update({}, {reload: false}).then(function() { - self.model.unsetDirty(self.handle); - self.trigger_up("restore_flip_card"); - self._updateButtons(); - self._enableQuickCreate(); + this.update({}, {reload: false}).then(() => { + this.model.unsetDirty(this.handle); + this.trigger_up("restore_flip_card"); + this._updateButtons(); + this._enableQuickCreate(); }); } }, @@ -371,7 +374,7 @@ odoo.define( } ); - var ProductPickerQuickCreateFormView = QuickCreateFormView.extend({ + const ProductPickerQuickCreateFormView = QuickCreateFormView.extend({ config: _.extend({}, QuickCreateFormView.prototype.config, { Renderer: ProductPickerQuickCreateFormRenderer, Controller: ProductPickerQuickCreateFormController, 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 4520bd5f0..653e83a8a 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 @@ -5,21 +5,25 @@ odoo.define( function(require) { "use strict"; - var core = require("web.core"); - var Widget = require("web.Widget"); - var ProductPickerQuickModifPriceFormView = require("web_widget_one2many_product_picker.ProductPickerQuickModifPriceFormView") + const core = require("web.core"); + const Widget = require("web.Widget"); + const ProductPickerQuickModifPriceFormView = require("web_widget_one2many_product_picker.ProductPickerQuickModifPriceFormView") .ProductPickerQuickModifPriceFormView; - var qweb = core.qweb; + const qweb = core.qweb; /** * This widget render a Form. Used by FieldOne2ManyProductPicker */ - var ProductPickerQuickModifPriceForm = Widget.extend({ + const 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", ], + events: { + "click .oe_record_change": "_onClickChange", + "click .oe_record_discard": "_onClickDiscard", + }, /** * @override @@ -47,9 +51,8 @@ odoo.define( * @override */ start: function() { - var self = this; - var def1 = this._super.apply(this, arguments); - var fieldsView = { + const def1 = this._super.apply(this, arguments); + const fieldsView = { arch: this._generateFormArch(), fields: this.fields, viewFields: this.fields, @@ -78,19 +81,21 @@ odoo.define( 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); + const def2 = this.formView.getController(this).then(controller => { + this.controller = controller; + this.$(".modal-body").empty(); + this.controller.appendTo(this.$(".modal-body")); + this.$el.on("hidden.bs.modal", this._onModalHidden.bind(this)); }); - return $.when(def1, def2); + return Promise.all([def1, def2]); }, /** * @override */ destroy: function() { + this.$el.off("hidden.bs.modal"); this._super.apply(this, arguments); }, @@ -103,31 +108,33 @@ odoo.define( * @returns {String} */ _generateFormArch: function() { - var wanted_field_states = this._getWantedFieldState(); - var template = + const wanted_field_states = this._getWantedFieldState(); + let template = ""; template += this.basicFieldParams.field.views.form.arch; template += ""; qweb.add_template(template); - var $arch = $( + const $arch = $( qweb.render("One2ManyProductPicker.QuickModifPrice.Form", { field_map: this.fieldMap, record_search: this.searchRecord, }) ); - 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") + const field_names = Object.keys( + this.basicFieldParams.field.views.form.fields + ); + let gen_arch = ""; + for (const index in field_names) { + const field_name = field_names[index]; + const $field = $arch.find("field[name='" + field_name + "']"); + const modifiers = $field.attr("modifiers") ? JSON.parse($field.attr("modifiers")) : {}; - modifiers.invisible = false; + modifiers.invisible = !(field_name in wanted_field_states); modifiers.readonly = wanted_field_states[field_name]; $field.attr("modifiers", JSON.stringify(modifiers)); - $field.attr("invisible", "0"); + $field.attr("invisible", modifiers.invisible ? "1" : "0"); $field.attr( "readonly", wanted_field_states[field_name] ? "1" : "0" @@ -146,11 +153,95 @@ odoo.define( * @returns {Object} */ _getWantedFieldState: function() { - var wantedFieldState = {}; + const wantedFieldState = {}; wantedFieldState[this.fieldMap.discount] = !this.canEditDiscount; wantedFieldState[this.fieldMap.price_unit] = !this.canEditPrice; return wantedFieldState; }, + + /** + * @private + */ + _onModalHidden: function() { + this.destroy(); + }, + + /** + * @private + * @param {MouseEvent} ev + */ + _onClickChange: function(ev) { + ev.stopPropagation(); + const model = this.basicFieldParams.model; + model.updateRecordContext(this.id, { + has_changes_confirmed: true, + }); + const is_virtual = model.isPureVirtual(this.id); + + // If is a 'pure virtual' record, save it in the selected list + if (is_virtual) { + if (model.isDirty(this.id)) { + this._disableQuickCreate(); + this.controller + .saveRecord(this.id, { + stayInEdit: true, + reload: true, + savePoint: true, + viewType: "form", + }) + .then(() => { + this._enableQuickCreate(); + model.unsetDirty(this.id); + this.trigger_up("create_quick_record", { + id: this.id, + }); + }); + } + } else { + // If is a "normal" record, update it + this.trigger_up("update_quick_record", { + id: this.id, + }); + model.unsetDirty(this.id); + } + }, + + /** + * @private + * @param {MouseEvent} ev + */ + _onClickDiscard: function(ev) { + ev.stopPropagation(); + const model = this.basicFieldParams.model; + model.discardChanges(this.id, { + rollback: true, + }); + this.trigger_up("update_quick_record", { + id: this.id, + }); + }, + + /** + * @private + */ + _disableQuickCreate: function() { + // Ensures that the record won't be created twice + this.$el.addClass("o_disabled"); + this.$("input:not(:disabled),button:not(:disabled)") + .addClass("o_temporarily_disabled") + .attr("disabled", "disabled"); + }, + + /** + * @private + */ + _enableQuickCreate: function() { + // Allows to create again + this.$el.removeClass("o_disabled"); + this.$("input.o_temporarily_disabled,button.o_temporarily_disabled") + .removeClass("o_temporarily_disabled") + .attr("disabled", false); + }, }); 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 90ab14c1e..d0040b4e7 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 @@ -10,43 +10,26 @@ odoo.define( * 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"); + const QuickCreateFormView = require("web.QuickCreateFormView"); + const core = require("web.core"); + const tools = require("web_widget_one2many_product_picker.tools"); - var qweb = core.qweb; + const qweb = core.qweb; - var ProductPickerQuickModifPriceFormRenderer = QuickCreateFormView.prototype.config.Renderer.extend( + const ProductPickerQuickModifPriceFormRenderer = QuickCreateFormView.prototype.config.Renderer.extend( { /** * @override */ 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() { - self._appendPrice(); - self._appendButtons(); + return this._super.apply(this, arguments).then(() => { + this._appendPrice(); }); }, - /** - * @private - */ - _appendButtons: function() { - this.$el.find(".oe_one2many_product_picker_form_buttons").remove(); - this.$el.append( - qweb.render( - "One2ManyProductPicker.QuickModifPrice.FormButtons", - { - mode: this.mode, - } - ) - ); - }, - /** * @private */ @@ -59,13 +42,8 @@ odoo.define( } ); - var ProductPickerQuickModifPriceFormController = QuickCreateFormView.prototype.config.Controller.extend( + const ProductPickerQuickModifPriceFormController = QuickCreateFormView.prototype.config.Controller.extend( { - events: _.extend({}, QuickCreateFormView.prototype.events, { - "click .oe_record_change": "_onClickChange", - "click .oe_record_discard": "_onClickDiscard", - }), - /** * @override */ @@ -81,28 +59,29 @@ odoo.define( * @override */ start: function() { - var self = this; - return this._super.apply(this, arguments).then(function() { - self._updatePrice(); + return this._super.apply(this, arguments).then(() => { + const record = this.model.get(this.handle); + this._updatePrice(record.data); }); }, /** * @override */ - _onFieldChanged: function() { + _onFieldChanged: function(ev) { this._super.apply(this, arguments); - this._updatePrice(); + const record = this.model.get(this.handle); + this._updatePrice(_.extend({}, record.data, ev.data.changes)); }, /** * @private + * @param {Object} values */ - _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] + _updatePrice: function(values) { + const price_reduce = tools.priceReduce( + values[this.fieldMap.price_unit], + values[this.fieldMap.discount] ); this.renderer.$el .find(".oe_price") @@ -111,98 +90,14 @@ odoo.define( price_reduce, this.getParent().state.fields[this.fieldMap.price_unit], this.currencyField, - record + values ) ); }, - - /** - * @private - */ - _disableQuickCreate: function() { - // Ensures that the record won't be created twice - this._disabled = true; - this.$el.addClass("o_disabled"); - this.$("input:not(:disabled)") - .addClass("o_temporarily_disabled") - .attr("disabled", "disabled"); - }, - - /** - * @private - */ - _enableQuickCreate: function() { - // Allows to create again - this._disabled = false; - this.$el.removeClass("o_disabled"); - this.$("input.o_temporarily_disabled") - .removeClass("o_temporarily_disabled") - .attr("disabled", false); - }, - - /** - * @private - * @param {MouseEvent} ev - */ - _onClickChange: function(ev) { - var self = this; - ev.stopPropagation(); - this.model.updateRecordContext(this.handle, { - has_changes_confirmed: true, - }); - var is_virtual = this.model.isPureVirtual(this.handle); - - // If is a 'pure virtual' record, save it in the selected list - if (is_virtual) { - if (this.model.isDirty(this.handle)) { - this._disableQuickCreate(); - this.saveRecord(this.handle, { - stayInEdit: true, - reload: true, - savePoint: true, - viewType: "form", - }).then(function() { - self._enableQuickCreate(); - var record = self.model.get(self.handle); - self.model.unsetDirty(self.handle); - self.trigger_up("create_quick_record", { - id: record.id, - }); - self.getParent().destroy(); - }); - } else { - this.getParent().destroy(); - } - } else { - // If is a "normal" record, update it - var record = this.model.get(this.handle); - this.trigger_up("update_quick_record", { - id: record.id, - }); - self.model.unsetDirty(self.handle); - this.getParent().destroy(); - } - }, - - /** - * @private - * @param {MouseEvent} ev - */ - _onClickDiscard: function(ev) { - ev.stopPropagation(); - this.model.discardChanges(this.handle, { - rollback: true, - }); - var record = this.model.get(this.handle); - this.trigger_up("update_quick_record", { - id: record.id, - }); - this.getParent().destroy(); - }, } ); - var ProductPickerQuickModifPriceFormView = QuickCreateFormView.extend({ + const ProductPickerQuickModifPriceFormView = QuickCreateFormView.extend({ config: _.extend({}, QuickCreateFormView.prototype.config, { Renderer: ProductPickerQuickModifPriceFormRenderer, Controller: ProductPickerQuickModifPriceFormController, 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 a0473007d..81f642174 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 @@ -6,19 +6,19 @@ odoo.define("web_widget_one2many_product_picker.One2ManyProductPickerRecord", fu ) { "use strict"; - var core = require("web.core"); - var Widget = require("web.Widget"); - 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"); + const core = require("web.core"); + const Widget = require("web.Widget"); + const Domain = require("web.Domain"); + const widgetRegistry = require("web.widget_registry"); + const tools = require("web_widget_one2many_product_picker.tools"); + const ProductPickerQuickModifPriceForm = require("web_widget_one2many_product_picker.ProductPickerQuickModifPriceForm"); + const config = require("web.config"); - var qweb = core.qweb; - var _t = core._t; + const qweb = core.qweb; + const _t = core._t; /* This represent a record (a card) */ - var One2ManyProductPickerRecord = Widget.extend({ + const One2ManyProductPickerRecord = Widget.extend({ custom_events: { quick_record_updated: "_onQuickRecordUpdated", restore_flip_card: "_onRestoreFlipCard", @@ -80,6 +80,7 @@ odoo.define("web_widget_one2many_product_picker.One2ManyProductPickerRecord", fu * @override */ destroy: function() { + this.$el.remove(); this.$card.off(""); this._super.apply(this, arguments); }, @@ -101,16 +102,19 @@ odoo.define("web_widget_one2many_product_picker.One2ManyProductPickerRecord", fu * new one. * * @param {Object} state + * @returns {Promise} */ recreate: function(state) { if (state) { this._setState(state); } - this.$card.removeClass("blocked"); - // Avoid recreate active record - if (this.$card.hasClass("active")) { - this._processDynamicFields(); - return $.when(); + if (this.$card) { + this.$card.removeClass("blocked"); + // Avoid recreate active record + if (this.$card.hasClass("active")) { + this._processDynamicFields(); + return $.when(); + } } this.on_detach_callback(); @@ -138,10 +142,11 @@ odoo.define("web_widget_one2many_product_picker.One2ManyProductPickerRecord", fu * * @private * @param {String} price_field + * @returns {String} */ _getMonetaryFieldValue: function(price_field) { - var field_name = this.options.fieldMap[price_field]; - var price = this.state.data[field_name]; + const field_name = this.options.fieldMap[price_field]; + const price = this.state.data[field_name]; return tools.monetary( price, this.state.fields[field_name], @@ -184,9 +189,20 @@ odoo.define("web_widget_one2many_product_picker.One2ManyProductPickerRecord", fu if (recordSearch) { this.recordSearch = recordSearch; } - var model = this.options.basicFieldParams.model; + const model = this.options.basicFieldParams.model; this.is_virtual = (this.state && model.isPureVirtual(this.state.id)) || false; + + // Check if has cached qty + if (this.state && this.state.id) { + const record = model.get(this.state.id); + const lazy_qty = (record && record.context.lazy_qty) || 0; + if (lazy_qty) { + model.updateRecordContext(this.state.id, {lazy_qty: 0}); + // Record already has 1 + this._incProductQty(lazy_qty - 1); + } + } }, /** @@ -197,8 +213,8 @@ odoo.define("web_widget_one2many_product_picker.One2ManyProductPickerRecord", fu // 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. - var model = this.options.basicFieldParams.model; - var record = model.get(this.state.id); + const model = this.options.basicFieldParams.model; + const record = model.get(this.state.id); return { record_search: this.recordSearch, user_context: @@ -214,6 +230,8 @@ odoo.define("web_widget_one2many_product_picker.One2ManyProductPickerRecord", fu modified: record && record.context.product_picker_modified, active_model: "", auto_save: this.options.autoSave, + is_saving: record && record.context.saving, + lazy_qty: record && record.context.lazy_qty, }; }, @@ -224,7 +242,7 @@ odoo.define("web_widget_one2many_product_picker.One2ManyProductPickerRecord", fu * @returns {Object} */ _getInternalVirtualRecordContext: function() { - var context = {}; + const context = {}; context["default_" + this.options.basicFieldParams.relation_field] = this.options.basicFieldParams.state.id || null; return context; @@ -238,7 +256,7 @@ odoo.define("web_widget_one2many_product_picker.One2ManyProductPickerRecord", fu * @returns {Object} */ _getInternalVirtualRecordData: function() { - var data = {}; + const data = {}; data[this.options.fieldMap.product] = { operation: "ADD", id: this.recordSearch.id, @@ -253,15 +271,15 @@ odoo.define("web_widget_one2many_product_picker.One2ManyProductPickerRecord", fu * @returns {Object} */ _generateVirtualState: function(data, context) { - var model = this.options.basicFieldParams.model; - var scontext = _.extend( + const model = this.options.basicFieldParams.model; + const scontext = _.extend( {}, 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); + const sdata = _.extend({}, this._getInternalVirtualRecordData(), data); return model.createVirtualRecord(this.options.basicFieldParams.value.id, { data: sdata, context: scontext, @@ -303,33 +321,32 @@ odoo.define("web_widget_one2many_product_picker.One2ManyProductPickerRecord", fu * @private * @param {jQueryElement} $container */ - _processWidgetFields: function($container, widget_list) { - var self = this; - $container.find("field").each(function() { - var $field = $(this); + _processWidgetFields: function($container) { + $container.find("field").each((key, value) => { + const $field = $(value); if ($field.parents("widget").length) { return; } - var field_name = $field.attr("name"); - var field_widget = $field.attr("widget"); + const field_name = $field.attr("name"); + const field_widget = $field.attr("widget"); // 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 (field_widget || this.fields[field_name].type === "many2many") { + let widget = this.subWidgets[field_name]; if (widget) { // A widget already exists for that field, so reset it // with the new state - widget.reset(self.state); + widget.reset(this.state); $field.replaceWith(widget.$el); } else { // The widget doesn't exist yet, so instanciate it - var Widget = self.fieldsInfo[field_name].Widget; + const Widget = this.fieldsInfo[field_name].Widget; if (Widget) { - widget = self._processWidget($field, field_name, Widget); - self.subWidgets[field_name] = widget; - } else if (config.debug) { + widget = this._processWidget($field, field_name, Widget); + this.subWidgets[field_name] = widget; + } else if (config.isDebug()) { // The widget is not implemented $field.replaceWith( $("", { @@ -362,23 +379,26 @@ odoo.define("web_widget_one2many_product_picker.One2ManyProductPickerRecord", fu // field's widgets point of view // 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) { + const attrs = Object.create(null); + _.each(this.fieldsInfo[field_name], (value, key) => { if (_.str.startsWith(key, "t-att-")) { key = key.slice(6); value = $field.attr(key); } attrs[key] = value; }); - var options = _.extend({}, this.options, { + const options = _.extend({}, this.options, { attrs: attrs, data: this.state.data, }); - var widget = new Widget(this, field_name, this.getParent().state, options); - var def = widget.replace($field); - if (def.state() === "pending") { - this.defs.push(def); - } + const widget = new Widget( + this, + field_name, + this.getParent().state, + options + ); + const def = widget.replace($field); + this.defs.push(def); return widget; }, @@ -387,41 +407,45 @@ odoo.define("web_widget_one2many_product_picker.One2ManyProductPickerRecord", fu * * @private * @param {jQueryElement} $container + * @param {String} widget_zone */ _processWidgets: function($container, widget_zone) { - var self = this; - $container.find("widget").each(function() { - var $field = $(this); - var FieldWidget = widgetRegistry.get($field.attr("name")); - var widget = new FieldWidget(self, { - fieldsInfo: self.fieldsInfo, - fields: self.fields, - main_state: self.getParent().state, - state: self.state, - fieldMap: self.options.fieldMap, - searchRecord: self.recordSearch, + $container.find("widget").each((key, value) => { + const $field = $(value); + const FieldWidget = widgetRegistry.get($field.attr("name")); + const widget = new FieldWidget(this, { + fieldsInfo: this.fieldsInfo, + fields: this.fields, + main_state: this.getParent().state, + state: this.state, + fieldMap: this.options.fieldMap, + searchRecord: this.recordSearch, node: $field, - readonly: self.options.readOnlyMode, - basicFieldParams: self.options.basicFieldParams, - data: self.state && self.state.data, + readonly: this.options.readOnlyMode, + basicFieldParams: this.options.basicFieldParams, + data: this.state && this.state.data, }); - self.widgets[widget_zone].push(widget); + this.widgets[widget_zone].push(widget); - var def = widget - ._widgetRenderAndInsert(function() { + const def = widget + ._widgetRenderAndInsert(() => { // Do nothing }) - .then(function() { + .then(() => { widget.$el.addClass("o_widget"); $field.replaceWith(widget.$el); }); - if (def.state() === "pending") { - self.defs.push(def); - } + this.defs.push(def); }); }, + _updateLazyQty: function() { + var model = this.options.basicFieldParams.model; + var record = model.get(this.state.id); + this.$el.find(".lazy_product_qty").text(record.context.lazy_qty); + }, + /** * This is a special handle for display the non-fields. * Similar 't-esc' behaviour. @@ -436,38 +460,37 @@ odoo.define("web_widget_one2many_product_picker.One2ManyProductPickerRecord", fu * text: 20 Items * * @private - * @param {Array[String]} fields + * @param {Array} fields */ _processDynamicFields: function(fields) { if (!this.state) { return; } - var self = this; - var model = this.options.basicFieldParams.model; - var record = model.get(this.state.id); - var state_data = record.data; + const model = this.options.basicFieldParams.model; + const record = model.get(this.state.id); + const state_data = record.data; - var to_find = []; + let to_find = []; if (!_.isEmpty(fields)) { - to_find = _.map(fields, function(field) { + to_find = _.map(fields, field => { return _.str.sprintf("[data-field=%s]", [field]); }); } else { to_find = ["[data-field]"]; } - this.$el.find(to_find.join()).each(function() { - var $elm = $(this); - var format_out = $elm.data("esc") || $elm.data("field"); + this.$el.find(to_find.join()).each((key, value) => { + const $elm = $(value); + const format_out = $elm.data("esc") || $elm.data("field"); $elm.html( - py.eval(format_out, _.extend({}, state_data, self.recordSearch)) + py.eval(format_out, _.extend({}, state_data, this.recordSearch)) ); }); if (this.options.showDiscount) { - var field_map = this.options.fieldMap; + const field_map = this.options.fieldMap; if (state_data) { - var has_discount = state_data[field_map.discount] > 0.0; + const has_discount = state_data[field_map.discount] > 0.0; this.$el .find(".original_price,.discount_price") .toggleClass("d-none", !has_discount); @@ -487,10 +510,10 @@ odoo.define("web_widget_one2many_product_picker.One2ManyProductPickerRecord", fu * @returns {String} */ _calcPriceReduced: function() { - var price_reduce = 0; - var field_map = this.options.fieldMap; - var model = this.options.basicFieldParams.model; - var record = model.get(this.state.id); + let price_reduce = 0; + const field_map = this.options.fieldMap; + const model = this.options.basicFieldParams.model; + const record = model.get(this.state.id); if (record && record.data[field_map.discount]) { price_reduce = tools.priceReduce( record.data[field_map.price_unit], @@ -513,9 +536,10 @@ odoo.define("web_widget_one2many_product_picker.One2ManyProductPickerRecord", fu * @returns {Promise} */ _saveRecord: function() { - var self = this; - var model = this.options.basicFieldParams.model; - var record = model.get(this.state.id); + const model = this.options.basicFieldParams.model; + const record = model.get(this.state.id); + model.updateRecordContext(this.state.id, {saving: true}); + this.recreate(); return model .save(record.id, { stayInEdit: true, @@ -523,17 +547,17 @@ odoo.define("web_widget_one2many_product_picker.One2ManyProductPickerRecord", fu savePoint: true, viewType: "form", }) - .then(function() { - var record = model.get(self.state.id); - self.trigger_up("create_quick_record", { + .then(() => { + const record = model.get(this.state.id); + this.trigger_up("create_quick_record", { id: record.id, - callback: function() { - self.$card + callback: () => { + this.$card .find(".o_catch_attention") .removeClass("o_catch_attention"); }, }); - model.unsetDirty(self.state.id); + model.unsetDirty(this.state.id); }); }, @@ -541,13 +565,12 @@ odoo.define("web_widget_one2many_product_picker.One2ManyProductPickerRecord", fu * @private */ _updateRecord: function() { - var self = this; - var model = this.options.basicFieldParams.model; - var record = model.get(this.state.id); + const model = this.options.basicFieldParams.model; + const record = model.get(this.state.id); this.trigger_up("update_quick_record", { id: record.id, - callback: function() { - self.$card + callback: () => { + this.$card .find(".o_catch_attention") .removeClass("o_catch_attention"); }, @@ -560,20 +583,23 @@ odoo.define("web_widget_one2many_product_picker.One2ManyProductPickerRecord", fu * @returns {Promise} */ _addProduct: function() { - var self = this; - var model = this.options.basicFieldParams.model; + const model = this.options.basicFieldParams.model; 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); + const record = model.get(this.state.id); + // Because we don't hide the 'add' button when the product is added form back form + // we check if the record is in "saving" mode to prevent duplicate it. + if (record.context.saving) { + return Promise.resolve(); + } + const changes = _.pick(record.data, this.options.fieldMap.product_uom_qty); if (changes[this.options.fieldMap.product_uom_qty] === 0) { changes[this.options.fieldMap.product_uom_qty] = 1; } - var model = this.options.basicFieldParams.model; this.$card.addClass("blocked"); - return model.notifyChanges(record.id, changes).then(function() { - self._saveRecord(); + return model.notifyChanges(record.id, changes).then(() => { + this._saveRecord(); }); }, @@ -583,45 +609,54 @@ odoo.define("web_widget_one2many_product_picker.One2ManyProductPickerRecord", fu * @returns {Promise} */ _incProductQty: function(amount) { - var self = this; - var model = this.options.basicFieldParams.model; + const model = this.options.basicFieldParams.model; 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() { - self._processDynamicFields(); - self._lazyUpdateRecord(); - }); + const record = model.get(this.state.id); + if (this.options.autoSave && !this.state.data.id) { + let lazy_qty = record.context.lazy_qty || 1; + lazy_qty += amount; + model.updateRecordContext(this.state.id, {lazy_qty: lazy_qty}); + this._updateLazyQty(); + } else { + // HACK: Modify the raw state value to show correct 'qty' when + // receive the response from Odoo. This happens because the widget + // sends a creation with qty 1 but can still add more qty mean while + // wait for the Odoo response. + const model_record_data = model.localData[this.state.id].data; + model_record_data[this.options.fieldMap.product_uom_qty] += amount; + return model + .notifyChanges(record.id, { + [this.options.fieldMap.product_uom_qty]: + model_record_data[this.options.fieldMap.product_uom_qty], + }) + .then(() => { + this._processDynamicFields(); + this._lazyUpdateRecord(); + }); + } }, /** * @private + * @param {Selector/HTMLElement} target */ - _doInteractAnim: function(target, currentTarget) { - var $target = $(target); - var $currentTarget = $(currentTarget); - var $img = $currentTarget.find(".oe_flip_card_front img"); + _doInteractAnim: function(target) { + const $target = $(target); $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() { - var state_data = this.state && this.state.data; + const state_data = this.state && this.state.data; if (this.options.readOnlyMode || !state_data) { return; } - var modif_price_form = new ProductPickerQuickModifPriceForm(this, { + + const modif_price_form = new ProductPickerQuickModifPriceForm(this, { fieldsInfo: this.fieldsInfo, fields: this.fields, main_state: this.getParent().state, @@ -634,11 +669,12 @@ odoo.define("web_widget_one2many_product_picker.One2ManyProductPickerRecord", fu canEditDiscount: this.options.editDiscount, currencyField: this.options.currencyField, }); - this.$modifPricePopup = $( - qweb.render("One2ManyProductPicker.QuickModifPricePopup") + this.$modifPriceModal = $( + qweb.render("One2ManyProductPicker.QuickModifPrice.Modal") ); - this.$modifPricePopup.appendTo($(".o_main_content")); - modif_price_form.attachTo(this.$modifPricePopup); + this.$modifPriceModal.appendTo($(".oe_one2many_product_picker_view")); + modif_price_form.attachTo(this.$modifPriceModal); + this.$modifPriceModal.modal(); }, // HANDLE EVENTS @@ -649,13 +685,10 @@ odoo.define("web_widget_one2many_product_picker.One2ManyProductPickerRecord", fu */ _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) { return; } - var $target = $(evt.target); + const $target = $(evt.target); if (!this.options.readOnlyMode) { if ( $target.hasClass("add_product") || @@ -664,7 +697,7 @@ odoo.define("web_widget_one2many_product_picker.One2ManyProductPickerRecord", fu if (!this.is_adding_product) { this.is_adding_product = true; this._addProduct(); - this._doInteractAnim(evt.target, evt.currentTarget); + this._doInteractAnim(evt.target); } return; } else if ( @@ -672,13 +705,18 @@ odoo.define("web_widget_one2many_product_picker.One2ManyProductPickerRecord", fu $target.parents(".product_qty").length ) { this._incProductQty(1); - this._doInteractAnim(evt.target, evt.currentTarget); + this._doInteractAnim(evt.target); return; } else if ($target.hasClass("safezone")) { // Do nothing on safe zones return; } } + + if (this.$card.hasClass("blocked")) { + return; + } + if (!this._clickFlipCardDelayed) { this._clickFlipCardDelayed = setTimeout( this._onClickDelayedFlipCard.bind(this, evt), @@ -708,24 +746,23 @@ odoo.define("web_widget_one2many_product_picker.One2ManyProductPickerRecord", fu this.$card.removeClass("active"); this.$front.removeClass("d-none"); } else { - var self = this; this.defs = []; if (!this.widgets.back.length) { this._processWidgetFields(this.$back); this._processWidgets(this.$back, "back"); } this._processDynamicFields(); - $.when(this.defs).then(function() { - var $actived_card = self.$el.parent().find(".active"); + $.when(this.defs).then(() => { + const $actived_card = this.$el.parent().find(".active"); $actived_card.removeClass("active"); $actived_card.find(".oe_flip_card_front").removeClass("d-none"); - self.$card.addClass("active"); - self.$card.on("transitionend", function() { - self.$front.addClass("d-none"); - self.$card.off("transitionend"); + this.$card.addClass("active"); + this.$card.on("transitionend", () => { + this.$front.addClass("d-none"); + this.$card.off("transitionend"); }); - self.trigger_up("record_flip", { - widget_index: self.$el.data("renderer_widget_index"), + this.trigger_up("record_flip", { + widget_index: this.$el.data("renderer_widget_index"), prev_widget_index: $actived_card .parent() .data("renderer_widget_index"), @@ -739,16 +776,16 @@ odoo.define("web_widget_one2many_product_picker.One2ManyProductPickerRecord", fu * @param {MouseEvent} evt */ _onDblClickDelayedFlipCard: function(evt) { - var $target = $(evt.target); + const $target = $(evt.target); if ( $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"); + const $currentTarget = $(evt.currentTarget); + const $img = $currentTarget.find(".oe_flip_card_front img"); + const cur_img_src = $img.attr("src"); if ($currentTarget.hasClass("oe_flip_card_maximized")) { $currentTarget.removeClass("oe_flip_card_maximized"); $currentTarget.on("transitionend", function() { @@ -763,12 +800,12 @@ odoo.define("web_widget_one2many_product_picker.One2ManyProductPickerRecord", fu $currentTarget.off("transitionend"); }); } else { - var $actived_card = this.$el.parent().find(".active"); + const $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"); } - var offset = $currentTarget.offset(); + const offset = $currentTarget.offset(); $currentTarget.css({ position: "fixed", top: offset.top, @@ -788,15 +825,15 @@ odoo.define("web_widget_one2many_product_picker.One2ManyProductPickerRecord", fu /** * @private + * @param {CustomEvent} 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() { - self.$card.css({ + this.$card.on("transitionend", () => { + this.$card.css({ position: "", top: "", left: "", @@ -804,7 +841,7 @@ odoo.define("web_widget_one2many_product_picker.One2ManyProductPickerRecord", fu height: "", zIndex: "", }); - self.$card.off("transitionend"); + this.$card.off("transitionend"); if (evt.data.success_callback) { evt.data.success_callback(); } 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 99bc2cd1c..48b6e2a17 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 @@ -5,15 +5,15 @@ odoo.define( 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"); + const core = require("web.core"); + const BasicRenderer = require("web.BasicRenderer"); + const One2ManyProductPickerRecord = require("web_widget_one2many_product_picker.One2ManyProductPickerRecord"); + const ProductPickerQuickCreateForm = require("web_widget_one2many_product_picker.ProductPickerQuickCreateForm"); - var qweb = core.qweb; + const qweb = core.qweb; /* This is the renderer of the main widget */ - var One2ManyProductPickerRenderer = BasicRenderer.extend({ + const One2ManyProductPickerRenderer = BasicRenderer.extend({ className: "oe_one2many_product_picker_view", events: { @@ -65,7 +65,7 @@ odoo.define( * @param {Object} widget */ removeWidget: function(widget) { - var index = this.widgets.indexOf(widget); + const index = this.widgets.indexOf(widget); widget.destroy(); delete this.widgets[index]; }, @@ -79,6 +79,8 @@ odoo.define( /** * @param {Object} search_data + * @param {Number} count + * @param {Object} search_group */ updateSearchData: function(search_data, count, search_group) { this.search_data = search_data; @@ -101,16 +103,15 @@ odoo.define( * @override */ updateState: function(state, params) { - var self = this; - var force_update = params.force; + const force_update = params.force; delete params.force; - var sparams = _.extend({}, params, {noRender: true}); + const 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); + const old_state = _.clone(this.state.data); + return this._super(state, sparams).then(() => { + this._updateStateRecords(old_state); }); }, @@ -121,8 +122,8 @@ odoo.define( * @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]; + for (let eb = this.widgets.length - 1; eb >= 0; --eb) { + const widget = this.widgets[eb]; if (widget.state.id === state_id) { widget.recreate(new_state); break; @@ -130,46 +131,51 @@ odoo.define( } }, + _isEqualState: function(state_a, state_b) { + if (state_a.id === state_b.id) { + return true; + } + const product_id_a = + state_a.data[this.options.field_map.product].data.id; + const product_id_b = + state_b.data[this.options.field_map.product].data.id; + return product_id_a === product_id_b; + }, + /** + * When destroy states we need check if pure virtual records + * are affected to recreate a new one because this widget can't + * remove pure virtual records. + * * @private - * @param {Array[Object]} states - * @returns {Deferred} + * @param {Array} states + * @returns {Array} */ - _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) { + _processStatesToDestroy: function(states) { + const to_destroy = []; + for (const state of states) { + for (let e = this.widgets.length - 1; e >= 0; --e) { + const widget = this.widgets[e]; + if (widget && this._isEqualState(widget.state, state)) { 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; + const to_add = []; + for (const index_destroy in to_destroy) { + const widget_destroyed = to_destroy[index_destroy]; + let 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]; + for (let eb = this.widgets.length - 1; eb >= 0; --eb) { + const widget = this.widgets[eb]; if ( widget && widget.state && - widget.state.data[this.options.field_map.product].data - .id === widget_product_id + this._isEqualState(widget.state, widget_destroyed.state) ) { found = true; break; @@ -178,37 +184,110 @@ odoo.define( 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; + let state_id = null; + for (let eb = this.state.data.length - 1; eb >= 0; --eb) { + const state = this.state.data[eb]; + if (this._isEqualState(state, widget_destroyed.state)) { + 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] - ); + // "Lines" section doesn't show virtual records + if ( + (state_id && this.search_group.name === "main_lines") || + this.search_group.name !== "main_lines" + ) { + const widget_product_id = + widget_destroyed.state.data[ + this.options.field_map.product + ].data.id; + const search_record = _.find(this.search_data, { + id: widget_product_id, + }); + const new_search_record = _.extend({}, search_record, { + __id: state_id, + }); + const card_id = widget_destroyed.$el.data("cardId"); + to_add.push([[new_search_record], false, true, card_id]); + } } } - _.invoke(to_destroy, "destroy"); - return $.when(defs); + return [to_destroy, to_add]; + }, + + /** + * We need check current states to ensure that doesn't exists duplications, + * update the existing and add the new ones. + * + * @private + * @returns {Array} + */ + _processCurrentStates: function() { + // Records to Update or Create + const to_destroy = []; + const to_add = []; + for (const index in this.state.data) { + const state = this.state.data[index]; + let exists = false; + let search_record_index = -1; + let search_record = false; + for (let e = this.widgets.length - 1; e >= 0; --e) { + const widget = this.widgets[e]; + if (!widget || !widget.state) { + // Already processed widget (deleted) + continue; + } + if (this._isEqualState(widget.state, state)) { + var model = this.getParent().getBasicFieldParams().model; + var record = model.get(widget.state.id); + model.updateRecordContext(state.id, { + lazy_qty: record.context.lazy_qty || 0, + }); + widget.recreate(state); + exists = true; + break; + } else if ( + widget.recordSearch.id === + state.data[this.options.field_map.product].data.id + ) { + // Is a new record (can be other record for the same 'search record' or a replacement for a pure virtual) + search_record_index = widget.$el.index(); + search_record = widget.recordSearch; + var model = this.getParent().getBasicFieldParams().model; + var record = model.get(widget.state.id); + model.updateRecordContext(state.id, { + lazy_qty: record.context.lazy_qty || 0, + }); + } + + // Remove "pure virtual" records that have the same product that the new record + if ( + widget.is_virtual && + this._isEqualState(widget.state, state) + ) { + to_destroy.push(widget); + delete this.widgets[e]; + } + } + + this.state.data = _.compact(this.state.data); + + // Need add a new one? + if (!exists && search_record_index !== -1) { + const new_search_record = _.extend({}, search_record, { + __id: state.id, + }); + to_add.push([ + [new_search_record], + false, + true, + search_record_index, + ]); + } + } + + return [to_destroy, to_add]; }, /** @@ -222,13 +301,13 @@ odoo.define( */ _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) { + const states_to_destroy = []; + for (const index in old_states) { + const old_state = old_states[index]; + let found = false; + for (const e in this.state.data) { + const current_state = this.state.data[e]; + if (this._isEqualState(current_state, old_state)) { found = true; break; } @@ -238,76 +317,53 @@ odoo.define( } } + const def = $.Deferred(); this.state.data = _.compact(this.state.data); - this._removeRecords(states_to_destroy, this.state.data); + const [to_destroy_old, to_add_virtual] = this._processStatesToDestroy( + states_to_destroy + ); - // 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; - } - - // 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]; - } - } - - this.state.data = _.compact(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] - ); - } + // Make widgets to destroy invisible to avoid render 'dance' + for (const widget of to_destroy_old) { + widget.$el.hide(); } - _.invoke(to_destroy, "destroy"); - return $.when(defs); + const oldTasks = []; + for (const params of to_add_virtual) { + oldTasks.push(this.appendSearchRecords.apply(this, params)[0]); + } + Promise.all(oldTasks).then(() => { + const [ + to_destroy_current, + to_add_current, + ] = this._processCurrentStates(); + + // Make widgets to destroy invisible to avoid render 'dance' + for (const widget of to_destroy_current) { + widget.$el.hide(); + } + + const currentTasks = []; + for (const params of to_add_current) { + currentTasks.push( + this.appendSearchRecords.apply(this, params)[0] + ); + } + Promise.all(currentTasks).then(() => { + _.invoke(to_destroy_old, "destroy"); + _.invoke(to_destroy_current, "destroy"); + def.resolve(); + }); + }); + + return def; }, /** * @override */ _renderView: function() { - var self = this; - var oldWidgets = _.compact(this.widgets); + const oldWidgets = _.compact(this.widgets); this.widgets = []; this.$recordsContainer = $("
", { class: "w-100 row", @@ -319,20 +375,20 @@ odoo.define( "#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() { + return new Promise(resolve => { + const defs = this.appendSearchRecords(this.search_data, true); + Promise.all(defs).then(() => { _.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 + this.$el.empty(); + this.$el.append(this.$recordsContainer); + this.$el.append(this.$extraButtonsContainer); + this.showLoadMore( + this.last_search_data_count >= this.options.records_per_page ); - if (self._isInDom) { - _.invoke(self.widgets, "on_attach_callback"); + if (this._isInDom) { + _.invoke(this.widgets, "on_attach_callback"); } - d.resolve(defs[1]); + return resolve(); }); }); }, @@ -343,22 +399,20 @@ odoo.define( */ _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]; + const field_name = this.options.field_map.product; + for (const index_datas in datas) { + const data = datas[index_datas]; - for (var index_state in this.state.data) { - var state_data = this.state.data[index_state]; + for (const index_state in this.state.data) { + const 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) + const sorted_datas = _.chain(datas) .sortBy("_order_value") - .map(function(item) { - return _.omit(item, "_order_value"); - }) + .map(item => _.omit(item, "_order_value")) .value() .reverse(); return sorted_datas; @@ -371,19 +425,19 @@ odoo.define( * Link a current state with the 'search record'. * * @private - * @param {Array[Object]} results - * @returns {Array[Object]} + * @param {Array} results + * @returns {Array} */ _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; + const field_name = this.options.field_map.product; + const records = []; + for (const index in results) { + const record_search = results[index]; + let 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]; + for (const index_data in this.state.data) { + const state_record = this.state.data[index_data]; + const field = state_record.data[field_name]; if ( (typeof field === "object" && field.data.id === record_search.id) || @@ -411,8 +465,8 @@ odoo.define( * @returns {Object} */ _getRecordDataById: function(id) { - for (var index in this.state.data) { - var record = this.state.data[index]; + for (const index in this.state.data) { + const record = this.state.data[index]; if (record.id === id) { return record; } @@ -444,7 +498,7 @@ odoo.define( * Generates the 'Product Card' per record. * * @private - * @param {Array[Object]} search_records + * @param {Array} search_records * @param {Boolean} no_process_records * @param {Number} position */ @@ -453,46 +507,43 @@ odoo.define( no_process_records, position ) { - var self = this; - var processed_records = no_process_records + const 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, + _.each(processed_records, search_record => { + const state_data = this._getRecordDataById(search_record.__id); + const widget_options = this._getRecordOptions(search_record); + widget_options.renderer_widget_index = this.widgets.length; + const ProductPickerRecord = new One2ManyProductPickerRecord( + this, state_data, widget_options ); - self.widgets.push(ProductPickerRecord); + this.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); - } + const defVirtualState = ProductPickerRecord.generateVirtualState(); + this.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( + const def = $.Deferred(); + ProductPickerRecord.appendTo(this.$recordsContainer).then( function(widget, widget_position) { if (typeof widget_position !== "undefined") { - var $elm = this.$el.find( - "> div > div:nth(" + widget_position + ")" + const $elm = this.$el.find( + `[data-card-id="${position}"]` ); - widget.$el.insertAfter($elm); + widget.$el.insertBefore($elm); } - }.bind(self, ProductPickerRecord, position) + def.resolve(); + }.bind(this, ProductPickerRecord, position) ); - if (def.state() === "pending") { - self.defs.push(def); - } + this.defs.push(def); }); }, @@ -506,11 +557,11 @@ odoo.define( /** * Append search records to the view * - * @param {Array[Object]} search_records + * @param {Array} search_records * @param {Boolean} no_attach_widgets * @param {Boolean} no_process_records * @param {Number} position - * @returns {Array[Deferred]} + * @returns {Array} */ appendSearchRecords: function( search_records, @@ -518,25 +569,24 @@ odoo.define( no_process_records, position ) { - var self = this; this.trigger_up("loading_records"); this.defs = []; this.defsVirtualState = []; - var cur_widget_index = this.widgets.length; + const cur_widget_index = this.widgets.length; this._appendSearchRecords(search_records, no_process_records, position); - var defs = this.defs; + const defs = this.defs; delete this.defs; - var defsVirtualState = this.defsVirtualState; + const 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); + Promise.all(defs).then(() => { + if (!no_attach_widgets && this._isInDom) { + const new_widgets = this.widgets.slice(cur_widget_index); _.invoke(new_widgets, "on_attach_callback"); } }), - $.when.apply($, defsVirtualState).then(function() { - self.trigger_up("loading_records", {finished: true}); + Promise.all(defsVirtualState).then(() => { + this.trigger_up("loading_records", {finished: true}); }), ]; }, @@ -556,23 +606,22 @@ odoo.define( * @param {Integer} index */ doWidgetFlip: function(index) { - var widget = this.widgets[index]; - var $actived_card = this.$el.find(".active"); + const widget = this.widgets[index]; + const $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() { + $.when(widget.defs).then(() => { $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"); + widget.$card.addClass("active"); + setTimeout(() => { + widget.$(".oe_flip_card_front").addClass("d-none"); }, 200); }); } @@ -585,11 +634,11 @@ odoo.define( * @param {CustomEvent} evt */ _onRecordFlip: function(evt) { - var prev_widget_index = evt.data.prev_widget_index; + const 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[ + for (const index in this.widgets[prev_widget_index].widgets.back) { + const widget = this.widgets[prev_widget_index].widgets.back[ index ]; if (widget instanceof ProductPickerQuickCreateForm) { 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 34bb8baf0..0eaa7eba6 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 @@ -3,7 +3,7 @@ odoo.define("web_widget_one2many_product_picker.BasicModel", function(require) { "use strict"; - var BasicModel = require("web.BasicModel"); + const BasicModel = require("web.BasicModel"); BasicModel.include({ /** @@ -23,7 +23,7 @@ odoo.define("web_widget_one2many_product_picker.BasicModel", function(require) { * @returns {Boolean} */ isPureVirtual: function(id) { - var data = this.localData[id]; + const data = this.localData[id]; return data._virtual || false; }, @@ -32,7 +32,7 @@ odoo.define("web_widget_one2many_product_picker.BasicModel", function(require) { * @param {Boolean} status */ setPureVirtual: function(id, status) { - var data = this.localData[id]; + const data = this.localData[id]; if (status) { data._virtual = true; } else { @@ -44,9 +44,9 @@ odoo.define("web_widget_one2many_product_picker.BasicModel", function(require) { * @param {Number/String} id */ unsetDirty: function(id) { - var data = this.localData[id]; + const data = this.localData[id]; data._isDirty = false; - this._visitChildren(data, function(r) { + this._visitChildren(data, r => { r._isDirty = false; }); }, @@ -59,12 +59,11 @@ odoo.define("web_widget_one2many_product_picker.BasicModel", function(require) { * @returns {Deferred} */ createVirtualRecord: function(listID, options) { - var self = this; - var list = this.localData[listID]; - var context = _.extend({}, this._getContext(list), options.context); + const list = this.localData[listID]; + const context = _.extend({}, this._getContext(list), options.context); - var position = options ? options.position : "top"; - var params = { + const position = options ? options.position : "top"; + const params = { context: context, fields: list.fields, fieldsInfo: list.fieldsInfo, @@ -75,18 +74,16 @@ 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}); + return new Promise(resolve => { + this._makeDefaultRecord(list.model, params).then(recordID => { + this.setPureVirtual(recordID, true); + this.updateRecordContext(recordID, {ignore_warning: true}); if (options.data) { - self._applyChange(recordID, options.data, params).then( - function() { - d.resolve(self.get(recordID)); - } - ); + this._applyChange(recordID, options.data, params).then(() => { + resolve(this.get(recordID)); + }); } else { - d.resolve(self.get(recordID)); + resolve(this.get(recordID)); } }); }); @@ -100,13 +97,14 @@ odoo.define("web_widget_one2many_product_picker.BasicModel", function(require) { * * @override */ - _performOnChange: function(record, fields, viewType) { + _performOnChange: function(record) { if (record.context && record.context.ignore_warning) { - var this_mp = _.clone(this); - var super_call = this.trigger_up; + const this_mp = _.clone(this); + const super_call = this.trigger_up; this_mp.trigger_up = function(event_name, data) { if (event_name === "warning" && data.type === "dialog") { - return; // Do nothing + // Do nothing + return; } return super_call.apply(this, arguments); }.bind(this); 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 357e492a9..eed9641ee 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 @@ -4,15 +4,15 @@ odoo.define("web_widget_one2many_product_picker.BasicView", function(require) { "use strict"; - var core = require("web.core"); - var pyUtils = require("web.py_utils"); - var BasicView = require("web.BasicView"); + const core = require("web.core"); + const pyUtils = require("web.py_utils"); + const BasicView = require("web.BasicView"); - var _t = core._t; + const _t = core._t; // Add ref to _() -> _t() call - var PY_t = new py.PY_def.fromJSON(function() { - var args = py.PY_parseArgs(arguments, ["str"]); + const PY_t = new py.PY_def.fromJSON(function() { + const args = py.PY_parseArgs(arguments, ["str"]); return py.str.fromJSON(_t(args.str.toJSON())); }); 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 d3ab9f84f..8e7364969 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 @@ -5,17 +5,17 @@ odoo.define("web_widget_one2many_product_picker.FieldOne2ManyProductPicker", fun ) { "use strict"; - 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 tools = require("web_widget_one2many_product_picker.tools"); + const core = require("web.core"); + const field_registry = require("web.field_registry"); + const FieldOne2Many = require("web.relational_fields").FieldOne2Many; + const One2ManyProductPickerRenderer = require("web_widget_one2many_product_picker.One2ManyProductPickerRenderer"); + const tools = require("web_widget_one2many_product_picker.tools"); - var _t = core._t; - var qweb = core.qweb; + const _t = core._t; + const qweb = core.qweb; /* This is the main widget */ - var FieldOne2ManyProductPicker = FieldOne2Many.extend({ + const FieldOne2ManyProductPicker = FieldOne2Many.extend({ className: "oe_field_one2many_product_picker", // Workaround: We need know all records, @@ -24,10 +24,9 @@ odoo.define("web_widget_one2many_product_picker.FieldOne2ManyProductPicker", fun events: _.extend({}, FieldOne2Many.prototype.events, { "click .dropdown-item": "_onClickSearchMode", - "click .oe_search_erase": "_onClickSearchEraser", "click .oe_btn_lines": "_onClickLines", "click .oe_btn_search_group": "_onClickSearchGroup", - "keypress .oe_search_input": "_onKeyPressSearch", + "search .oe_search_input": "_onSearch", "show.bs.dropdown .o_cp_buttons": "_onShowSearchDropdown", }), custom_events: _.extend({}, FieldOne2Many.prototype.custom_events, { @@ -82,7 +81,7 @@ odoo.define("web_widget_one2many_product_picker.FieldOne2ManyProductPicker", fun */ willStart: function() { if (!this.view) { - return $.when(); + return Promise.resolve(); } // Uses to work with searchs, so we can mix properties with the user values. @@ -98,14 +97,17 @@ odoo.define("web_widget_one2many_product_picker.FieldOne2ManyProductPicker", fun }; this._searchContext.activeTest = false; } - return $.when(this._super.apply(this, arguments), this._getSearchRecords()); + return Promise.all([ + this._super.apply(this, arguments), + this._getSearchRecords(), + ]); }, /** * Updates the lines counter badge */ updateBadgeLines: function() { - var records = this.parent_controller.model.get(this.state.id).data[ + const records = this.parent_controller.model.get(this.state.id).data[ this.name ].data; this.$badgeLines.text(records.length); @@ -115,13 +117,13 @@ odoo.define("web_widget_one2many_product_picker.FieldOne2ManyProductPicker", fun 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[ + let prices = []; + const field_map = this.options.field_map; + const records = this.parent_controller.model.get(this.state.id).data[ this.name ].data; if (this.options.show_discount) { - prices = _.map(records, function(line) { + prices = _.map(records, line => { return ( line.data[field_map.product_uom_qty] * tools.priceReduce( @@ -131,15 +133,15 @@ odoo.define("web_widget_one2many_product_picker.FieldOne2ManyProductPicker", fun ); }); } else { - prices = _.map(records, function(line) { + prices = _.map(records, line => { return ( line.data[field_map.product_uom_qty] * line.data[field_map.price_unit] ); }); } - var total = - _.reduce(prices, function(a, b) { + let total = + _.reduce(prices, (a, b) => { return a + b; }) || 0; total = tools.monetary( @@ -185,10 +187,10 @@ odoo.define("web_widget_one2many_product_picker.FieldOne2ManyProductPicker", fun */ _processGroups: function() { this.searchGroups = []; - var hasUserActive = false; - var groups = this.options.groups || []; - for (var groupIndex in groups) { - var group_def = groups[groupIndex]; + let hasUserActive = false; + const groups = this.options.groups || []; + for (const groupIndex in groups) { + const group_def = groups[groupIndex]; if (group_def.active) { group_def.active = !hasUserActive; hasUserActive = true; @@ -213,11 +215,10 @@ odoo.define("web_widget_one2many_product_picker.FieldOne2ManyProductPicker", fun * @override */ _renderControlPanel: function() { - var self = this; - return this._super.apply(this, arguments).then(function() { - self.control_panel.update({ + return this._super.apply(this, arguments).then(() => { + this._controlPanel.updateContents({ cp_content: { - $buttons: self.$buttons, + $buttons: this.$buttons, $pager: false, }, }); @@ -253,21 +254,20 @@ odoo.define("web_widget_one2many_product_picker.FieldOne2ManyProductPicker", fun * @override */ _render: function() { - var self = this; - var def = this._super.apply(this, arguments); + const def = this._super.apply(this, arguments); // Parent implementation can return 'undefined' :( return ( def && - def.then(function() { + def.then(() => { if ( - !self.$el.hasClass("oe_field_one2many_product_picker_maximized") + !this.$el.hasClass("oe_field_one2many_product_picker_maximized") ) { - self.$el.addClass("position-relative d-flex flex-column"); + this.$el.addClass("position-relative d-flex flex-column"); } - self._addMaximizeButton(); - if (self.options.show_subtotal) { - self._addTotalsZone(); + this._addMaximizeButton(); + if (this.options.show_subtotal) { + this._addTotalsZone(); } }) ); @@ -277,15 +277,10 @@ odoo.define("web_widget_one2many_product_picker.FieldOne2ManyProductPicker", fun * @returns {Deferred} */ doRenderSearchRecords: function() { - var self = this; - return $.Deferred(function(d) { - self._getSearchRecords().then(function() { - self.renderer.$el.scrollTop(0); - self.renderer._renderView().then(function(virtualStateDefs) { - virtualStateDefs.then(function() { - d.resolve(); - }); - }); + return new Promise(resolve => { + this._getSearchRecords().then(() => { + this.renderer.$el.scrollTop(0); + this.renderer._renderView().then(() => resolve()); }); }); }, @@ -325,17 +320,16 @@ odoo.define("web_widget_one2many_product_picker.FieldOne2ManyProductPicker", fun * @returns {Deferred} */ _getSearchRecords: function(options, merge) { - var self = this; - var arch = this.view.arch; - var field_name = this.options.field_map.product; - var field_info = this.view.fieldsInfo[arch.tag][field_name]; - var model = this.view.viewFields[field_info.name].relation; + const arch = this.view.arch; + const field_name = this.options.field_map.product; + const field_info = this.view.fieldsInfo[arch.tag][field_name]; + const model = this.view.viewFields[field_info.name].relation; // Launch the rpc request and ensures that we wait for the reply // to continue - var domain = this._getFullSearchDomain(); - var soptions = options || {}; - var context = _.extend( + const domain = this._getFullSearchDomain(); + const soptions = options || {}; + const context = _.extend( { active_search_group_name: this._activeSearchGroup.name, active_search_involved_fields: this._searchContext.involvedFields, @@ -344,37 +338,37 @@ odoo.define("web_widget_one2many_product_picker.FieldOne2ManyProductPicker", fun this.value.getContext() ); - return $.Deferred(function(d) { - var limit = soptions.limit || self.options.records_per_page; - var offset = soptions.offset || 0; - self._rpc({ + return new Promise(resolve => { + const limit = soptions.limit || this.options.records_per_page; + const offset = soptions.offset || 0; + this._rpc({ model: model, method: "search_read", - fields: self.search_read_fields, + fields: this.search_read_fields, domain: domain, limit: limit, offset: offset, - orderBy: self._searchContext.order, + orderBy: this._searchContext.order, kwargs: {context: context}, - }).then(function(results) { + }).then(results => { if (merge) { - self._searchRecords = _.union( - self._searchRecords || [], + this._searchRecords = _.union( + this._searchRecords || [], results ); } else { - self._searchRecords = results; + this._searchRecords = results; } - self._lastSearchRecordsCount = results.length; - self._searchOffset = offset + limit; - if (self.renderer) { - self.renderer.updateSearchData( - self._searchRecords, - self._lastSearchRecordsCount, - self._activeSearchGroup + this._lastSearchRecordsCount = results.length; + this._searchOffset = offset + limit; + if (this.renderer) { + this.renderer.updateSearchData( + this._searchRecords, + this._lastSearchRecordsCount, + this._activeSearchGroup ); } - d.resolve(results); + resolve(results); }); }); }, @@ -384,8 +378,8 @@ odoo.define("web_widget_one2many_product_picker.FieldOne2ManyProductPicker", fun * @param {MouseEvent} evt */ _onClickSearchGroup: function(evt) { - var $btn = $(evt.target); - var groupIndex = Number($btn.data("group")) || 0; + const $btn = $(evt.target); + const groupIndex = Number($btn.data("group")) || 0; this._activeSearchGroup = this.searchGroups[groupIndex]; this._searchContext.domain = this._activeSearchGroup.domain; this._searchContext.order = this._activeSearchGroup.order; @@ -422,17 +416,16 @@ odoo.define("web_widget_one2many_product_picker.FieldOne2ManyProductPicker", fun * @param {MouseEvent} ev */ _onClickSearchMode: function(ev) { - var self = this; ev.preventDefault(); - var $target = $(ev.target); + const $target = $(ev.target); this._searchMode = $target.index(); $target .parent() .children() .removeClass("active"); $target.addClass("active"); - this.doRenderSearchRecords().then(function() { - self.$searchInput.focus(); + this.doRenderSearchRecords().then(() => { + this.$searchInput.focus(); }); }, @@ -470,17 +463,17 @@ odoo.define("web_widget_one2many_product_picker.FieldOne2ManyProductPicker", fun */ _getFullSearchDomain: function() { this._searchContext.involvedFields = []; - var domain = _.clone(this._searchContext.domain) || []; + const domain = _.clone(this._searchContext.domain) || []; if (this._searchContext.text) { - var search_domain = this.options.search; + let search_domain = this.options.search; if (!(search_domain[0] instanceof Array)) { search_domain = search_domain[this._searchMode].domain; } - var involved_fields = []; + const involved_fields = []; // Iterate domain triplets and logic operators - for (var index in search_domain) { - var domain_cloned = _.clone(search_domain[index]); + for (const index in search_domain) { + const domain_cloned = _.clone(search_domain[index]); // Is a triplet if (domain_cloned instanceof Array) { @@ -525,10 +518,11 @@ odoo.define("web_widget_one2many_product_picker.FieldOne2ManyProductPicker", fun 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) { + const field_name = this.options.field_map.product; + const lines = this.parent_controller.model.get(this.state.id).data[ + this.name + ].data; + const ids = _.map(lines, line => { return line.data[field_name].data.id; }); return [["id", "in", ids]]; @@ -562,24 +556,23 @@ odoo.define("web_widget_one2many_product_picker.FieldOne2ManyProductPicker", fun this._searchContext.text = ""; }, - _onKeyPressSearch: function(evt) { + /** + * Odoo stop bubble of the event, but we need listen it. + * + * @override + */ + _onKeydown: function(evt) { if (evt.keyCode === $.ui.keyCode.ENTER) { - var self = this; - this._searchContext.text = evt.target.value; - this.doRenderSearchRecords().then(function() { - self.$searchInput.focus(); - }); + // Do nothing + return; } + return this._super.apply(this, arguments); }, - /** - * @private - */ - _onClickSearchEraser: function() { - var self = this; - this._clearSearchInput(); - this.doRenderSearchRecords().then(function() { - self.$searchInput.focus(); + _onSearch: function(evt) { + this._searchContext.text = evt.target.value; + this.doRenderSearchRecords().then(() => { + this.$searchInput.focus(); }); }, @@ -589,11 +582,11 @@ odoo.define("web_widget_one2many_product_picker.FieldOne2ManyProductPicker", fun */ _onShowSearchDropdown: function(evt) { // Workaround: This "ensures" a correct dropdown position - var offset = $(evt.currentTarget) + const offset = $(evt.currentTarget) .find(".dropdown-toggle") .parent() .height(); - _.defer(function() { + _.defer(() => { $(evt.currentTarget) .find(".dropdown-menu") .css("transform", "translate3d(0px, " + offset + "px, 0px)"); @@ -608,7 +601,6 @@ odoo.define("web_widget_one2many_product_picker.FieldOne2ManyProductPicker", fun */ _onCreateQuickRecord: function(evt) { evt.stopPropagation(); - var self = this; this.parent_controller.model.setPureVirtual(evt.data.id, false); if (this.options.auto_save) { @@ -616,23 +608,22 @@ odoo.define("web_widget_one2many_product_picker.FieldOne2ManyProductPicker", fun this._setValue( {operation: "ADD", id: evt.data.id}, {notifyChange: false} - ).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(); - } - }); + ).then(() => { + this.parent_controller + .saveRecord(undefined, {stayInEdit: true}) + .then(() => { + // Because 'create' generates a new state and we can't know these new id we + // need force update all the current states. + this._setValue( + {operation: "UPDATE", id: evt.data.id}, + {doNotSetDirty: true} + ).then(() => { + if (evt.data.callback) { + evt.data.callback(); + } }); - } else if (evt.data.callback) { + }); + if (evt.data.callback) { evt.data.callback(); } }); @@ -642,7 +633,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(() => { if (evt.data.callback) { evt.data.callback(); } @@ -650,6 +641,53 @@ odoo.define("web_widget_one2many_product_picker.FieldOne2ManyProductPicker", fun } }, + _doUpdateQuickRecord: function(id, data, callback) { + if (this.options.auto_save) { + var self = this; + // Dont trigger state update + this._setValue( + {operation: "UPDATE", id: id, data: data}, + {notifyChange: false} + ).then(function() { + 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 (callback) { + callback(); + } + }); + }); + if (callback) { + callback(); + } + }); + } else { + // This is used to know when need use 'yellow' color + this.parent_controller.model.updateRecordContext(id, { + product_picker_modified: true, + }); + // This will trigger an "state" update + this._setValue({operation: "UPDATE", id: id, data: data}).then( + function() { + if (callback) { + callback(); + } + } + ); + } + }, + /** * Runs the x2many (1,id,values) command. * @@ -658,67 +696,21 @@ odoo.define("web_widget_one2many_product_picker.FieldOne2ManyProductPicker", fun */ _onUpdateQuickRecord: function(evt) { evt.stopPropagation(); - var self = this; - - if (this.options.auto_save) { - // Dont trigger state update - this._setValue( - {operation: "UPDATE", id: evt.data.id, data: evt.data.data}, - {notifyChange: false} - ).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(); - } - }); - }); - } else if (evt.data.callback) { - evt.data.callback(); - } - }); - } else { - // This is used to know when need use 'yellow' color - this.parent_controller.model.updateRecordContext(evt.data.id, { - product_picker_modified: true, - }); - // This will trigger an "state" update - this._setValue({ - operation: "UPDATE", - id: evt.data.id, - data: evt.data.data, - }).then(function() { - if (evt.data.callback) { - evt.data.callback(); - } - }); - } + this._doUpdateQuickRecord(evt.data.id, evt.data.data, evt.data.callback); }, /** * Handle auto_save when remove a record + * + * @param {CustomEvent} evt */ _onListRecordRemove: function(evt) { evt.stopPropagation(); - var self = this; - this._setValue({operation: "DELETE", ids: [evt.data.id]}).then(function() { - if (self.options.auto_save) { - self.parent_controller + this._setValue({operation: "DELETE", ids: [evt.data.id]}).then(() => { + if (this.options.auto_save) { + this.parent_controller .saveRecord(undefined, {stayInEdit: true}) - .then(function() { + .then(() => { if (evt.data.callback) { evt.data.callback(); } @@ -746,14 +738,13 @@ odoo.define("web_widget_one2many_product_picker.FieldOne2ManyProductPicker", fun if (this._isLoading) { return; } - var self = this; this._getSearchRecords( { offset: this._searchOffset, }, true - ).then(function(records) { - self.renderer.appendSearchRecords(records); + ).then(records => { + this.renderer.appendSearchRecords(records); }); }, @@ -784,12 +775,10 @@ odoo.define("web_widget_one2many_product_picker.FieldOne2ManyProductPicker", fun * * @override */ - - _setValue: function(value, options) { - var self = this; - return this._super.apply(this, arguments).then(function() { - self.updateBadgeLines(); - self.updateSubtotalPrice(); + _setValue: function() { + return this._super.apply(this, arguments).then(() => { + this.updateBadgeLines(); + this.updateSubtotalPrice(); }); }, }); 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 d851bfe4d..922ae16e6 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 @@ -67,10 +67,6 @@ height $one2many-product-picker-transition-3d-time; height: $one2many-product-picker-card-min-height; - &.blocked { - filter: blur(2px); - } - &.disabled { filter: grayscale(100%); opacity: 0.5; @@ -235,36 +231,3 @@ } } } - -.oe_product_picker_quick_modif_price { - width: 80%; - max-width: $one2many-product-picker-quick-modif-price-max-width; - margin: auto; - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - border: 1px solid $border-color; - z-index: 55; - - .oe_one2many_product_picker_form_buttons { - text-align: center; - } -} - -.oe_product_picker_catch_attention { - position: relative; - animation: productPickerCatchAttention 200ms normal forwards; -} - -@keyframes productPickerCatchAttention { - 0% { - transform: scale(1); - } - 50% { - transform: scale(1.5); - } - 100% { - 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 13551bfe7..f7b0947d1 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 @@ -39,14 +39,6 @@ aria-label="Search..." aria-describedby="btnGroupAddon2" /> -
- -
@@ -97,16 +89,31 @@
- + +
+ x +
+
+
@@ -187,7 +194,34 @@
- -
+ +