From 0fdc914a67bc81d3b9fd64895a3808cb3b46baa7 Mon Sep 17 00:00:00 2001 From: "Lucas Perais (lpe)" Date: Wed, 11 Sep 2019 12:39:56 +0000 Subject: [FIX] web: search panel handles archived parent category Have a model with a parent field Set his parents like Parent 0 | -- Parent 1 | -- Parent 2 Now, put the Parent 1 as inactive Open a view with a search panel, which will fetch the parent field as categories Before this commit, it crashed, because the inner record memory did not contain a reference to the parent of Parent 2 After this commit, it doesn't crash, and the search panel just doesn't display parents categories that are archived, nor any children of those OPW 2070266 closes odoo/odoo#36699 Signed-off-by: Aaron Bohy (aab) --- addons/web/static/src/js/views/kanban/search_panel.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'addons/web/static/src') diff --git a/addons/web/static/src/js/views/kanban/search_panel.js b/addons/web/static/src/js/views/kanban/search_panel.js index d06eacbdb07..b7d2432426f 100644 --- a/addons/web/static/src/js/views/kanban/search_panel.js +++ b/addons/web/static/src/js/views/kanban/search_panel.js @@ -140,8 +140,9 @@ var SearchPanel = Widget.extend({ }); Object.keys(category.values).forEach(function (valueId) { var value = category.values[valueId]; - if (value.parentId) { - category.values[value.parentId].childrenIds.push(value.id); + var parentCategoryId = value.parentId; + if (parentCategoryId && parentCategoryId in category.values) { + category.values[parentCategoryId].childrenIds.push(value.id); } }); category.rootIds = Object.keys(category.values).filter(function (valueId) { -- cgit v1.2.3 From 057d3eba2e07fd035a826f5eeed651149ced469f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexandre=20K=C3=BChn?= Date: Thu, 12 Sep 2019 09:00:39 +0000 Subject: [FIX] web: use search view input with composition mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Forward-port of #36688 (original: #36284) Before this commit, search view was barely usable with Japanese IME. For instance, when pressing ENTER while in composition mode and IME prompts some suggestions, it made a search with the updated input content. The expected behaviour should have been to update the input content, without making the search yet. This commit fixes the issue by turning off some actions of the search view when typing during composition mode. opw-2061590 closes odoo/odoo#36728 Signed-off-by: Alexandre Kühn (aku) --- .../src/js/views/control_panel/search/search_bar.js | 20 ++++++++++++++++++++ .../js/views/control_panel/search/search_facet.js | 20 ++++++++++++++++++++ addons/web/static/src/js/widgets/auto_complete.js | 14 ++++++++++++-- 3 files changed, 52 insertions(+), 2 deletions(-) (limited to 'addons/web/static/src') diff --git a/addons/web/static/src/js/views/control_panel/search/search_bar.js b/addons/web/static/src/js/views/control_panel/search/search_bar.js index 6248489922c..9063b2aca50 100644 --- a/addons/web/static/src/js/views/control_panel/search/search_bar.js +++ b/addons/web/static/src/js/views/control_panel/search/search_bar.js @@ -9,6 +9,8 @@ var Widget = require('web.Widget'); var SearchBar = Widget.extend({ template: 'SearchView.SearchBar', events: _.extend({}, Widget.prototype.events, { + 'compositionend .o_searchview_input': '_onCompositionendInput', + 'compositionstart .o_searchview_input': '_onCompositionstartInput', 'keydown': '_onKeydown', }), /** @@ -34,6 +36,7 @@ var SearchBar = Widget.extend({ this.autoCompleteSources = []; this.searchFacets = []; + this._isInputComposing = false; }, /** * @override @@ -207,11 +210,28 @@ var SearchBar = Widget.extend({ }); } }, + /** + * @rivate + * @param {CompositionEvent} ev + */ + _onCompositionendInput: function () { + this._isInputComposing = false; + }, + /** + * @rivate + * @param {CompositionEvent} ev + */ + _onCompositionstartInput: function () { + this._isInputComposing = true; + }, /** * @private * @param {KeyEvent} e */ _onKeydown: function (e) { + if (this._isInputComposing) { + return; + } switch(e.which) { case $.ui.keyCode.LEFT: this._focusPreceding(); diff --git a/addons/web/static/src/js/views/control_panel/search/search_facet.js b/addons/web/static/src/js/views/control_panel/search/search_facet.js index ca9a7ad6225..3f18597cccf 100644 --- a/addons/web/static/src/js/views/control_panel/search/search_facet.js +++ b/addons/web/static/src/js/views/control_panel/search/search_facet.js @@ -10,6 +10,8 @@ var SearchFacet = Widget.extend({ template: 'SearchView.SearchFacet', events: _.extend({}, Widget.prototype.events, { 'click .o_facet_remove': '_onFacetRemove', + 'compositionend': '_onCompositionend', + 'compositionstart': '_onCompositionstart', 'keydown': '_onKeydown', }), /** @@ -26,6 +28,7 @@ var SearchFacet = Widget.extend({ }); this.separator = this._getSeparator(); this.icon = this._getIcon(); + this._isComposing = false; }, //-------------------------------------------------------------------------- @@ -105,6 +108,20 @@ var SearchFacet = Widget.extend({ // Handlers //-------------------------------------------------------------------------- + /** + * @private + * @param {CompositionEvent} ev + */ + _onCompositionend: function (ev) { + this._isComposing = false; + }, + /** + * @private + * @param {CompositionEvent} ev + */ + _onCompositionstart: function (ev) { + this._isComposing = true; + }, /** * @private */ @@ -116,6 +133,9 @@ var SearchFacet = Widget.extend({ * @param {KeyboardEvent} ev */ _onKeydown: function (ev) { + if (this._isComposing) { + return; + } switch (ev.which) { case $.ui.keyCode.BACKSPACE: this.trigger_up('facet_removed', {group: this.facet}); diff --git a/addons/web/static/src/js/widgets/auto_complete.js b/addons/web/static/src/js/widgets/auto_complete.js index 1bb00be8d38..30c38690d17 100644 --- a/addons/web/static/src/js/widgets/auto_complete.js +++ b/addons/web/static/src/js/widgets/auto_complete.js @@ -29,16 +29,23 @@ return Widget.extend({ this.searching = true; this.search_string = ''; this.current_search = null; + this._isInputComposing = false; }, start: function () { var self = this; + this.$input.on('compositionend', function (ev) { + self._isInputComposing = false; + }); + this.$input.on('compositionstart', function (ev) { + self._isInputComposing = true; + }); this.$input.on('keyup', function (ev) { - if (ev.which === $.ui.keyCode.RIGHT) { + if (ev.which === $.ui.keyCode.RIGHT && !self._isInputComposing) { self.searching = true; ev.preventDefault(); return; } - if (ev.which === $.ui.keyCode.ENTER) { + if (ev.which === $.ui.keyCode.ENTER && !self._isInputComposing) { if (self.search_string.length) { self.select_item(ev); } @@ -68,6 +75,9 @@ return Widget.extend({ } }); this.$input.on('keydown', function (ev) { + if (self._isInputComposing) { + return; + } switch (ev.which) { case $.ui.keyCode.ENTER: -- cgit v1.2.3 From 26c62cbb4401bffb72ce77fee481ca84b77a8143 Mon Sep 17 00:00:00 2001 From: Romeo Fragomeli Date: Wed, 11 Sep 2019 13:06:37 +0000 Subject: [FIX] web: unable to scroll modal's table in Safari (iOS) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Before this commit, users wasn't able to scroll vertically when there was "table-responsive" class on modal's table. In fact the scroll was effective on the body and not in the modal content. We can fix this issue by having flex elements but as this is a fix, we preferred to choose a one line fix solution. Steps to reproduce : * open Odoo in Safari (iOS) * open Sale app * create a new quotation * select a customer * select "Search More ..." * try to scroll the table vertically (BUG) Sources : https://stackoverflow.com/questions/41681251/overflow-x-scroll-inside-a-fixed-div-prevents-vertical-scrolling-on-ios#comment84970666_41693350 https://stackoverflow.com/a/57566116 opw-2042095 closes odoo/odoo#36707 closes odoo/odoo#36730 Signed-off-by: Adrien Dieudonné (adr) Signed-off-by: Adrien Dieudonné (adr) --- addons/web/static/src/scss/modal.scss | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'addons/web/static/src') diff --git a/addons/web/static/src/scss/modal.scss b/addons/web/static/src/scss/modal.scss index 52c3100df31..8976dc1e6e7 100644 --- a/addons/web/static/src/scss/modal.scss +++ b/addons/web/static/src/scss/modal.scss @@ -92,6 +92,11 @@ } } +body.modal-open { + // Allow vertical scrolling in modals on iOS + -webkit-overflow-scrolling: touch; +} + // Temporary fix for modals which are not instantiated thanks to the Dialog // JS classes (deprecated case) (see bootstrap_overridden.scss) + Frontend. // -- cgit v1.2.3 From 416f1794426af55132a19d388a2e5693f59dfde6 Mon Sep 17 00:00:00 2001 From: RomainLibert Date: Mon, 16 Sep 2019 08:24:40 +0000 Subject: [FIX] web: pass the context when executing an action In case you have a button of type action, the controller is doing an rpc on the model using the right method, but in this rpc, the context is not passed. This creates a translation problem (nothing is translated) since the language is not defined in the context. Task Id 2062419 Closes #36189 closes odoo/odoo#36969 Signed-off-by: Yannick Tivisse (yti) --- addons/web/static/src/js/views/abstract_controller.js | 3 +++ 1 file changed, 3 insertions(+) (limited to 'addons/web/static/src') diff --git a/addons/web/static/src/js/views/abstract_controller.js b/addons/web/static/src/js/views/abstract_controller.js index 63d59d153fd..5b5197469db 100644 --- a/addons/web/static/src/js/views/abstract_controller.js +++ b/addons/web/static/src/js/views/abstract_controller.js @@ -19,6 +19,8 @@ var config = require('web.config'); var core = require('web.core'); var mvc = require('web.mvc'); +var session = require('web.session'); + var QWeb = core.qweb; var AbstractController = mvc.Controller.extend(ActionMixin, { @@ -476,6 +478,7 @@ var AbstractController = mvc.Controller.extend(ActionMixin, { this.dp.add(this._rpc({ model: data.model, method: data.method, + context: session.user_context, })).then(function (action) { if (action !== undefined) { self.do_action(action, options); -- cgit v1.2.3 From fa283d3d4eb73eab34d6ae5c8af5623ab07eea49 Mon Sep 17 00:00:00 2001 From: "Lucas Perais (lpe)" Date: Mon, 2 Sep 2019 13:39:38 +0000 Subject: [FIX] web: progress bar feature The progress bar was pretty broken before this commit. > It was not possible to input the value as text > Moving the progress bar to modify value and saving crashed > modifying the max value crashed > The whole feature was ill-defined With this commit, we fix the crashed. It is now possible to write the field behind the progress bar by moving it in RO mode or by typing the input in RW mode *if it is the value we want to write on* It it is the max value that the field targets, we can do it in RO and RW mode, only with the input as text Note that this works *only if* the editable option on the widget is set to true OPW 2061846 closes odoo/odoo#36928 closes odoo/odoo#36970 Signed-off-by: Lucas Perais (lpe) Signed-off-by: Lucas Perais (lpe) --- addons/web/static/src/js/fields/basic_fields.js | 101 ++++++++++++++++-------- 1 file changed, 66 insertions(+), 35 deletions(-) (limited to 'addons/web/static/src') diff --git a/addons/web/static/src/js/fields/basic_fields.js b/addons/web/static/src/js/fields/basic_fields.js index b3bf09ab88d..38276373cc2 100644 --- a/addons/web/static/src/js/fields/basic_fields.js +++ b/addons/web/static/src/js/fields/basic_fields.js @@ -2306,11 +2306,28 @@ var FieldProgressBar = AbstractField.extend({ if (this.recordData[this.nodeOptions.current_value]) { this.value = this.recordData[this.nodeOptions.current_value]; } - this.max_value = this.recordData[this.nodeOptions.max_value] || 100; + + // The few next lines determine if the widget can write on the record or not + this.editable_readonly = !!this.nodeOptions.editable_readonly; + // "hard" readonly this.readonly = this.nodeOptions.readonly || !this.nodeOptions.editable; - this.edit_max_value = this.nodeOptions.edit_max_value || false; + + this.canWrite = !this.readonly && ( + this.mode === 'edit' || + (this.editable_readonly && this.mode === 'readonly') || + (this.viewType === 'kanban') // Keep behavior before commit + ); + + // Boolean to toggle if we edit the numerator (value) or the denominator (max_value) + this.edit_max_value = !!this.nodeOptions.edit_max_value; + this.max_value = this.recordData[this.nodeOptions.max_value] || 100; + this.title = _t(this.attrs.title || this.nodeOptions.title) || ''; - this.edit_on_click = !this.nodeOptions.edit_max_value || false; + + // Ability to edit the field through the bar + // /!\ this feature is disabled + this.enableBarAsInput = false; + this.edit_on_click = this.enableBarAsInput && this.mode === 'readonly' && !this.edit_max_value; this.write_mode = false; }, @@ -2318,19 +2335,19 @@ var FieldProgressBar = AbstractField.extend({ var self = this; this._render_value(); - if (!this.readonly) { + if (this.canWrite) { if (this.edit_on_click) { this.$el.on('click', '.o_progress', function (e) { var $target = $(e.currentTarget); - self.value = Math.floor((e.pageX - $target.offset().left) / $target.outerWidth() * self.max_value); + var numValue = Math.floor((e.pageX - $target.offset().left) / $target.outerWidth() * self.max_value); + self.on_update(numValue); self._render_value(); - self.on_update(self.value); }); } else { this.$el.on('click', function () { if (!self.write_mode) { var $input = $('', {type: 'text', class: 'o_progressbar_value o_input'}); - $input.on('blur', _.bind(self.on_change_input, self)); + $input.on('blur', self.on_change_input.bind(self)); self.$('.o_progressbar_value').replaceWith($input); self.write_mode = true; self._render_value(); @@ -2340,24 +2357,25 @@ var FieldProgressBar = AbstractField.extend({ } return this._super(); }, + /** + * Updates the widget with value + * + * @param {Number} value + */ on_update: function (value) { - if (!isNaN(value)) { - if (this.edit_max_value) { - try { - this.max_value = this._parseValue(value); - this._isValid = true; - } catch (e) { - this._isValid = false; - } - var changes = {}; - changes[this.nodeOptions.max_value] = this.max_value; - this.trigger_up('field_changed', { - dataPointID: this.dataPointID, - changes: changes, - }); - } else { - this._setValue(value); - } + if (this.edit_max_value) { + this.max_value = value; + this._isValid = true; + var changes = {}; + changes[this.nodeOptions.max_value] = this.max_value; + this.trigger_up('field_changed', { + dataPointID: this.dataPointID, + changes: changes, + }); + } else { + // _setValues accepts string and will parse it + var formattedValue = this._formatValue(value); + this._setValue(formattedValue); } }, on_change_input: function (e) { @@ -2365,29 +2383,42 @@ var FieldProgressBar = AbstractField.extend({ if (e.type === 'change' && !$input.is(':focus')) { return; } - if (isNaN($input.val())) { - this.do_warn(_t("Wrong value entered!"), _t("Only Integer Value should be valid.")); - } else { - if (e.type === 'input') { - this._render_value($input.val()); - if (parseFloat($input.val()) === 0) { + + var parsedValue; + try { + // Cover all numbers with parseFloat + parsedValue = field_utils.parse.float($input.val()); + } catch (error) { + this.do_warn(_t("Wrong value entered!"), _t("Only Integer or Float Value should be valid.")); + } + + if (parsedValue !== undefined) { + if (e.type === 'input') { // ensure what has just been typed in the input is a number + // returns NaN if not a number + this._render_value(parsedValue); + if (parsedValue === 0) { $input.select(); } - } else { + } else { // Implicit type === 'blur': we commit the value if (this.edit_max_value) { - this.max_value = $(e.target).val(); - } else { - this.value = $(e.target).val() || 0; + parsedValue = parsedValue || 100; } + var $div = $('
', {class: 'o_progressbar_value'}); this.$('.o_progressbar_value').replaceWith($div); this.write_mode = false; + this.on_update(parsedValue); this._render_value(); - this.on_update(this.edit_max_value ? this.max_value : this.value); } } }, + /** + * Renders the value + * + * @private + * @param {Number} v + */ _render_value: function (v) { var value = this.value; var max_value = this.max_value; -- cgit v1.2.3 From fa9beab3eab77ddbc9d21cd1eb70ea0fe6be5b18 Mon Sep 17 00:00:00 2001 From: oco-odoo Date: Mon, 16 Sep 2019 11:31:31 +0000 Subject: [FIX] account: dashboard: Center the empty dashboard message horizontally when the setup bar is displayed on a small screen Before that, when using a small screen (with width <= 768 px) and showing the accounting dashboard of a company without any chart of accounts installed, the icon and message prompting to install a chart of accounts was displayed in the lower left corner, instead of being horizontally centered. closes odoo/odoo#36974 X-original-commit: 4999f47d7e283c6a5799e68f2b0e1351638e17a3 Signed-off-by: Nicolas Martinelli (nim) Co-authored-by: Nicolas Lempereur --- addons/web/static/src/scss/banner.scss | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'addons/web/static/src') diff --git a/addons/web/static/src/scss/banner.scss b/addons/web/static/src/scss/banner.scss index d7237203897..be011d70698 100644 --- a/addons/web/static/src/scss/banner.scss +++ b/addons/web/static/src/scss/banner.scss @@ -1,8 +1,9 @@ .o_has_banner { .o_view_nocontent { top: 30%; - @media screen and (max-height: map-get($grid-breakpoints, 'md')) { + @include media-breakpoint-down(sm) { position: relative; + margin: auto; } } } -- cgit v1.2.3