From 015a50cbb3b874d81f5641c1f2f573192b14400a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexandre=20D=2E=20D=C3=ADaz?= Date: Wed, 3 Feb 2021 11:44:05 +0100 Subject: [PATCH] [IMP] web_widget_one2many_product_picker: Auto apply changes without confirmation --- .../quick_create_form_view.js | 112 ++++++++++++------ .../js/views/One2ManyProductPicker/record.js | 76 ++++++++++-- .../views/One2ManyProductPicker/renderer.js | 76 ++++++++++-- .../static/src/scss/_variables.scss | 3 +- 4 files changed, 206 insertions(+), 61 deletions(-) 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 a3c3b827d..23c3b2501 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 @@ -64,11 +64,24 @@ odoo.define("web_widget_one2many_product_picker.ProductPickerQuickCreateFormView }, /** - * Updates buttons depending on record status - * - * @private + * Automatically create or accept changes */ - _updateButtons: function () { + auto: function () { + var state = this._getRecordState(); + if (state === "new") { + this._add(); + } else if (state === "dirty") { + this._change(); + } + }, + + /** + * Know the real state of the record + * - record: Normal + * - new: Is a new record + * - dirty: Has changes + */ + _getRecordState: function () { var record = this.model.get(this.handle); var state = "record"; if (this.model.isNew(record.id)) { @@ -89,12 +102,22 @@ odoo.define("web_widget_one2many_product_picker.ProductPickerQuickCreateFormView } } } + + return state; + }, + + /** + * Updates buttons depending on record status + * + * @private + */ + _updateButtons: function () { this.$el.find( ".oe_one2many_product_picker_form_buttons").remove(); this.$el.find(".o_form_view").append( qweb.render( "One2ManyProductPicker.QuickCreate.FormButtons", { - state: state, + state: this._getRecordState(), }) ); }, @@ -103,7 +126,9 @@ odoo.define("web_widget_one2many_product_picker.ProductPickerQuickCreateFormView * @private */ _disableQuickCreate: function () { - + if (!this.$el) { + return; + } // Ensures that the record won't be created twice this._disabled = true; this.$el.addClass("o_disabled"); @@ -203,6 +228,10 @@ odoo.define("web_widget_one2many_product_picker.ProductPickerQuickCreateFormView * @returns {Deferred} */ _add: function () { + this.model.updateRecordContext(this.handle, { + has_changes_confirmed: true, + }); + if (this._disabled) { // Don't do anything if we are already creating a record @@ -223,38 +252,18 @@ odoo.define("web_widget_one2many_product_picker.ProductPickerQuickCreateFormView }); self.model.unsetDirty(self.handle); self._updateButtons(); + self.trigger_up("restore_flip_card"); }); }, - /** - * @private - * @param {MouseEvent} ev - */ - _onClickAdd: function (ev) { - ev.stopPropagation(); - this.model.updateRecordContext(this.handle, { - has_changes_confirmed: true, - }); - this._add(); - }, - - /** - * @private - * @param {MouseEvent} ev - */ - _onClickRemove: function (ev) { - ev.stopPropagation(); + _remove: function () { + this.trigger_up("restore_flip_card"); this.trigger_up("list_record_remove", { id: this.renderer.state.id, }); }, - /** - * @private - * @param {MouseEvent} ev - */ - _onClickChange: function (ev) { - ev.stopPropagation(); + _change: function () { this.model.updateRecordContext(this.handle, { has_changes_confirmed: true, }); @@ -267,13 +276,8 @@ odoo.define("web_widget_one2many_product_picker.ProductPickerQuickCreateFormView this._updateButtons(); }, - /** - * @private - * @param {MouseEvent} ev - */ - _onClickDiscard: function (ev) { + _discard: function () { var self = this; - ev.stopPropagation(); var record = this.model.get(this.handle); this.model.discardChanges(this.handle, { rollback: true, @@ -293,6 +297,42 @@ odoo.define("web_widget_one2many_product_picker.ProductPickerQuickCreateFormView }); } }, + + /** + * @private + * @param {MouseEvent} ev + */ + _onClickAdd: function (ev) { + ev.stopPropagation(); + this._add(); + }, + + /** + * @private + * @param {MouseEvent} ev + */ + _onClickRemove: function (ev) { + ev.stopPropagation(); + this._remove(); + }, + + /** + * @private + * @param {MouseEvent} ev + */ + _onClickChange: function (ev) { + ev.stopPropagation(); + this._change(); + }, + + /** + * @private + * @param {MouseEvent} ev + */ + _onClickDiscard: function (ev) { + ev.stopPropagation(); + this._discard(); + }, } ); 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 34d581562..263cb0c48 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 @@ -38,7 +38,10 @@ odoo.define("web_widget_one2many_product_picker.One2ManyProductPickerRecord", fu this.subWidgets = {}; this._clickFlipCardCount = 0; this._setState(state, options.searchRecord); - this.widgets = []; + this.widgets = { + front: [], + back: [], + }; }, /** @@ -71,6 +74,14 @@ odoo.define("web_widget_one2many_product_picker.One2ManyProductPickerRecord", fu _.invoke(this.subWidgets, "on_detach_callback"); }, + /** + * @override + */ + destroy: function () { + this.$card.off("") + this._super.apply(this, arguments); + }, + /** * @override */ @@ -94,6 +105,12 @@ odoo.define("web_widget_one2many_product_picker.One2ManyProductPickerRecord", fu if (state) { this._setState(state); } + // Avoid recreate active record + if (this.$card.hasClass("active")) { + this._processDynamicFields(); + return $.when(); + } + this.on_detach_callback(); return this._render(); }, @@ -238,10 +255,20 @@ odoo.define("web_widget_one2many_product_picker.One2ManyProductPickerRecord", fu }); }, + _detachAllWidgets: function () { + _.invoke(this.widgets.front, "on_detach_callback"); + _.invoke(this.widgets.back, "on_detach_callback"); + this.widgets = { + front: [], + back: [], + }; + }, + /** * @override */ _render: function () { + this._detachAllWidgets(); this.defs = []; this._replaceElement( qweb.render( @@ -249,11 +276,12 @@ odoo.define("web_widget_one2many_product_picker.One2ManyProductPickerRecord", fu this._getQWebContext() ) ); + this.$el.data("renderer_widget_index", this.options.renderer_widget_index); this.$card = this.$(".oe_flip_card"); this.$front = this.$(".oe_flip_card_front"); this.$back = this.$(".oe_flip_card_back"); this._processWidgetFields(this.$front); - this._processWidgets(this.$front); + this._processWidgets(this.$front, 'front'); this._processDynamicFields(); return $.when.apply(this, this.defs); }, @@ -265,7 +293,7 @@ odoo.define("web_widget_one2many_product_picker.One2ManyProductPickerRecord", fu * @private * @param {jQueryElement} $container */ - _processWidgetFields: function ($container) { + _processWidgetFields: function ($container, widget_list) { var self = this; $container.find("field").each(function () { var $field = $(this); @@ -357,7 +385,7 @@ odoo.define("web_widget_one2many_product_picker.One2ManyProductPickerRecord", fu * @private * @param {jQueryElement} $container */ - _processWidgets: function ($container) { + _processWidgets: function ($container, widget_zone) { var self = this; $container.find("widget").each(function () { var $field = $(this); @@ -375,7 +403,7 @@ odoo.define("web_widget_one2many_product_picker.One2ManyProductPickerRecord", fu data: self.state && self.state.data, }); - self.widgets.push(widget); + self.widgets[widget_zone].push(widget); var def = widget ._widgetRenderAndInsert(function () { @@ -534,21 +562,28 @@ odoo.define("web_widget_one2many_product_picker.One2ManyProductPickerRecord", fu } if (this.$card.hasClass("active")) { this.$card.removeClass("active"); - this.$card.find('.oe_flip_card_front').removeClass("d-none"); + this.$front.removeClass("d-none"); } else { var self = this; this.defs = []; - this._processWidgetFields(this.$back); - this._processWidgets(this.$back); + 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"); $actived_card.removeClass("active"); $actived_card.find('.oe_flip_card_front').removeClass("d-none"); self.$card.addClass("active"); - setTimeout(function () { - self.$('.oe_flip_card_front').addClass("d-none"); - }, 200); + self.$card.on('transitionend', function () { + self.$front.addClass("d-none"); + self.$card.off('transitionend'); + }); + self.trigger_up("record_flip", { + widget_index: self.$el.data("renderer_widget_index"), + prev_widget_index: $actived_card.parent().data("renderer_widget_index"), + }); }); } }, @@ -610,8 +645,23 @@ odoo.define("web_widget_one2many_product_picker.One2ManyProductPickerRecord", fu * @private */ _onRestoreFlipCard: function () { - this.$(".oe_flip_card").removeClass("active"); - this.$('.oe_flip_card_front').removeClass("d-none"); + 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({ + position: "", + top: "", + left: "", + width: "", + height: "", + zIndex: "", + }); + self.$card.off('transitionend'); + }); + } }, /** 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 2c71f771e..b5a181115 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 @@ -9,6 +9,8 @@ odoo.define("web_widget_one2many_product_picker.One2ManyProductPickerRenderer", var BasicRenderer = require("web.BasicRenderer"); var One2ManyProductPickerRecord = require( "web_widget_one2many_product_picker.One2ManyProductPickerRecord"); + var ProductPickerQuickCreateForm = require( + "web_widget_one2many_product_picker.ProductPickerQuickCreateForm"); var qweb = core.qweb; @@ -19,6 +21,9 @@ odoo.define("web_widget_one2many_product_picker.One2ManyProductPickerRenderer", events: { 'click #productPickerLoadMore': '_onClickLoadMore', }, + custom_events: { + 'record_flip': '_onRecordFlip', + }, DELAY_GET_RECORDS: 150, MIN_PERC_GET_RECORDS: 0.9, @@ -62,8 +67,9 @@ odoo.define("web_widget_one2many_product_picker.One2ManyProductPickerRenderer", * @param {Object} widget */ removeWidget: function (widget) { - this.widgets.splice(this.widgets.indexOf(widget), 1); + var index = this.widgets.indexOf(widget); widget.destroy(); + delete this.widgets[index]; }, /** @@ -128,7 +134,6 @@ odoo.define("web_widget_one2many_product_picker.One2ManyProductPickerRenderer", } } } - this.widgets = _.compact(this.widgets); if (this.search_group.name === "main_lines") { _.invoke(to_destroy, "destroy"); @@ -145,6 +150,7 @@ odoo.define("web_widget_one2many_product_picker.One2ManyProductPickerRenderer", for (var eb = this.widgets.length-1; eb>=0; --eb) { var widget = this.widgets[eb]; if ( + widget && widget.state.data[this.options.field_map.product].data.id === widget_product_id ) { found = true; @@ -230,7 +236,8 @@ odoo.define("web_widget_one2many_product_picker.One2ManyProductPickerRenderer", // 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 + widget.state.data[this.options.field_map.product].data.id === state.data[this.options.field_map.product].data.id && + widget.state.data[this.options.compa].data.id === state.data[this.options.field_map.product].data.id ) { to_destroy.push(widget); delete this.widgets[e]; @@ -243,7 +250,7 @@ odoo.define("web_widget_one2many_product_picker.One2ManyProductPickerRenderer", defs.push(this.appendSearchRecords([new_search_record], false, true, search_record_index)[0]); } } - this.widgets = _.compact(this.widgets); + _.invoke(to_destroy, "destroy"); return $.when(defs); }, @@ -360,10 +367,12 @@ odoo.define("web_widget_one2many_product_picker.One2ManyProductPickerRenderer", no_process_records?search_records:this._processSearchRecords(search_records); _.each(processed_records, function (search_record) { var state_data = self._getRecordDataById(search_record.__id); + var widget_options = self._getRecordOptions(search_record); + widget_options.renderer_widget_index = self.widgets.length; var ProductPickerRecord = new One2ManyProductPickerRecord( self, state_data, - self._getRecordOptions(search_record) + widget_options ); self.widgets.push(ProductPickerRecord); @@ -380,12 +389,12 @@ odoo.define("web_widget_one2many_product_picker.One2ManyProductPickerRenderer", // the search data. Using search data instead of waiting for // simulated state gives a low FCP time. var def = ProductPickerRecord.appendTo(self.$recordsContainer) - .then(function () { - if (typeof position !== "undefined") { - var $elm = self.$el.find("> div > div:nth("+position+")"); - ProductPickerRecord.$el.insertAfter($elm); + .then(function (widget, widget_position) { + if (typeof widget_position !== "undefined") { + var $elm = this.$el.find("> div > div:nth("+widget_position+")"); + widget.$el.insertAfter($elm); } - }); + }.bind(self, ProductPickerRecord, position)); if (def.state() === "pending") { self.defs.push(def); } @@ -441,6 +450,53 @@ odoo.define("web_widget_one2many_product_picker.One2ManyProductPickerRenderer", this._loadMoreWorking = true; }, + /** + * Do card flip + * + * @param {Integer} index + */ + doWidgetFlip: function (index) { + var widget = this.widgets[index]; + var $actived_card = this.$el.find(".active"); + if (widget.$card.hasClass("active")) { + widget.$card.removeClass("active"); + widget.$card.find('.oe_flip_card_front').removeClass("d-none"); + } else { + var self = widget; + widget.defs = []; + widget._processWidgetFields(widget.$back); + widget._processWidgets(widget.$back); + widget._processDynamicFields(); + $.when(widget.defs).then(function () { + $actived_card.removeClass("active"); + $actived_card.find('.oe_flip_card_front').removeClass("d-none"); + self.$card.addClass("active"); + setTimeout(function () { + self.$('.oe_flip_card_front').addClass("d-none"); + }, 200); + }); + } + }, + + /** + * Handle card flip. + * Used to create/update the record + * + * @param {CustomEvent} evt + */ + _onRecordFlip: function (evt) { + var prev_widget_index = evt.data.prev_widget_index; + if (typeof prev_widget_index !== "undefined") { + // Only check 'back' widgets so there is where the form was created + for (var index in this.widgets[prev_widget_index].widgets.back) { + var widget = this.widgets[prev_widget_index].widgets.back[index]; + if (widget instanceof ProductPickerQuickCreateForm) { + widget.controller.auto(); + } + } + } + } + }); return One2ManyProductPickerRenderer; diff --git a/web_widget_one2many_product_picker/static/src/scss/_variables.scss b/web_widget_one2many_product_picker/static/src/scss/_variables.scss index 4a65150a3..6d2d3e00a 100644 --- a/web_widget_one2many_product_picker/static/src/scss/_variables.scss +++ b/web_widget_one2many_product_picker/static/src/scss/_variables.scss @@ -1,7 +1,6 @@ -$one2many-product-picker-max-height: 500px; $one2many-product-picker-card-min-height: 150px; $one2many-product-picker-card-max-height: 150px; -$one2many-product-picker-transition-3d-time: 0.3s; +$one2many-product-picker-transition-3d-time: 0.15s; $one2many-product-picker-card-form-padding: 0em; $one2many-product-picker-quick-modif-price-max-width: 400px; $one2many-product-picker-zoom-scale: 1.4;