From 48515d3c2e62d0a532e0c9a28ededbe6e1c6a1ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9ry=20Debongnie?= Date: Mon, 16 Apr 2018 16:22:47 +0200 Subject: [FIX] web: fix issue with initial action in web client Here is a scenario that could happen before this commit: 1. web client is started 2. it binds a callback on hashchange 3. it performs some async work (read initial action for user) 4. the user (or more likely, a tour) changes the url 5. the hashchange handler is called, and it starts a new action 6. the async work for the web client is over, it then executes the default action and/or clicks on the first menu 7. we are now in the default action state, instead of the desired action state (the one from the new url) With this fix, we simply do nothing when there was an hashchange before the initial action was done. --- addons/web/static/src/js/chrome/web_client.js | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'addons/web/static') diff --git a/addons/web/static/src/js/chrome/web_client.js b/addons/web/static/src/js/chrome/web_client.js index dbdaedfddf2..9b71db30b6f 100644 --- a/addons/web/static/src/js/chrome/web_client.js +++ b/addons/web/static/src/js/chrome/web_client.js @@ -93,6 +93,10 @@ return AbstractWebClient.extend({ bind_hashchange: function() { var self = this; $(window).bind('hashchange', this.on_hashchange); + var didHashChanged = false; + $(window).one('hashchange', function () { + didHashChanged = true; + }); var state = $.bbq.getState(true); if (_.isEmpty(state) || state.action === "login") { @@ -103,6 +107,9 @@ return AbstractWebClient.extend({ args: [[session.uid], ['action_id']], }) .done(function(result) { + if (didHashChanged) { + return; + } var data = result[0]; if(data.action_id) { self.action_manager.do_action(data.action_id[0]); -- cgit v1.2.3 From 4d2e22bc392f27d920542e6ca025273080e5df77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9ry=20Debongnie?= Date: Mon, 16 Apr 2018 16:22:47 +0200 Subject: [FIX] web: fix issue with initial action in web client Here is a scenario that could happen before this commit: 1. web client is started 2. it binds a callback on hashchange 3. it performs some async work (read initial action for user) 4. the user (or more likely, a tour) changes the url 5. the hashchange handler is called, and it starts a new action 6. the async work for the web client is over, it then executes the default action and/or clicks on the first menu 7. we are now in the default action state, instead of the desired action state (the one from the new url) With this fix, we simply do nothing when there was an hashchange before the initial action was done. --- addons/web/static/src/js/chrome/web_client.js | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'addons/web/static') diff --git a/addons/web/static/src/js/chrome/web_client.js b/addons/web/static/src/js/chrome/web_client.js index db440cd742f..5d9f025c865 100644 --- a/addons/web/static/src/js/chrome/web_client.js +++ b/addons/web/static/src/js/chrome/web_client.js @@ -90,6 +90,10 @@ return AbstractWebClient.extend({ bind_hashchange: function() { var self = this; $(window).bind('hashchange', this.on_hashchange); + var didHashChanged = false; + $(window).one('hashchange', function () { + didHashChanged = true; + }); var state = $.bbq.getState(true); if (_.isEmpty(state) || state.action === "login") { @@ -100,6 +104,9 @@ return AbstractWebClient.extend({ args: [[session.uid], ['action_id']], }) .done(function(result) { + if (didHashChanged) { + return; + } var data = result[0]; if(data.action_id) { self.action_manager.doAction(data.action_id[0]); -- cgit v1.2.3 From df02dc37fd2febd3fc899792ee25c80c0149d255 Mon Sep 17 00:00:00 2001 From: "Lucas Perais (lpe)" Date: Fri, 13 Apr 2018 10:49:35 +0200 Subject: [FIX] web: pivot: correctly save and restore groupbys on rows and cols On a pivot view expand a field in column another one in row Then put a domain in the search view Save the whole thing (ir.filter) as favorite Before this commit, the ir.filter was not saved the right way when a domain was present (Let alone its restoration) This was because of - the internal default groupbys of the pivot model was not updated, and what we got in the params was overwritten by what existed before - a slight typo in the code made the row updated with the col values OPW 1835865 OPW 1829925 closes #24215 --- .../web/static/src/js/views/pivot/pivot_model.js | 3 +- addons/web/static/tests/views/pivot_tests.js | 41 ++++++++++++++++++++++ 2 files changed, 43 insertions(+), 1 deletion(-) (limited to 'addons/web/static') diff --git a/addons/web/static/src/js/views/pivot/pivot_model.js b/addons/web/static/src/js/views/pivot/pivot_model.js index 1c76c41ab71..6eddd44ffe7 100644 --- a/addons/web/static/src/js/views/pivot/pivot_model.js +++ b/addons/web/static/src/js/views/pivot/pivot_model.js @@ -250,6 +250,7 @@ var PivotModel = AbstractModel.extend({ this.data.colGroupBys = params.context.pivot_column_groupby || this.data.colGroupBys; this.data.groupedBy = params.context.pivot_row_groupby || this.data.groupedBy; this.data.measures = this._processMeasures(params.context.pivot_measures) || this.data.measures; + this.defaultGroupedBy = this.data.groupedBy.length ? this.data.groupedBy : this.defaultGroupedBy; } if ('domain' in params) { this.data.domain = params.domain; @@ -278,7 +279,7 @@ var PivotModel = AbstractModel.extend({ self._updateTree(old_col_root, self.data.main_col.root); new_groupby_length = self._getHeaderDepth(self.data.main_col.root) - 1; - self.data.main_row.groupbys = old_col_root.groupbys.slice(0, new_groupby_length); + self.data.main_row.groupbys = old_row_root.groupbys.slice(0, new_groupby_length); }); }, /** diff --git a/addons/web/static/tests/views/pivot_tests.js b/addons/web/static/tests/views/pivot_tests.js index 3e77d7aa8b9..198fd0189ae 100644 --- a/addons/web/static/tests/views/pivot_tests.js +++ b/addons/web/static/tests/views/pivot_tests.js @@ -960,4 +960,45 @@ QUnit.module('Views', { pivot.destroy(); }); + + QUnit.test('Row and column groupbys plus a domain', function (assert) { + assert.expect(3); + + var pivot = createView({ + View: PivotView, + model: "partner", + data: this.data, + arch: '' + + '' + + '', + }); + + // Set a column groupby + pivot.$('thead .o_pivot_header_cell_closed').click(); + pivot.$('.o_field_selection li[data-field=customer] a').click(); + + // Set a Row groupby + pivot.$('tbody .o_pivot_header_cell_closed').click(); + pivot.$('.o_pivot_field_menu li[data-field=product_id] a').click(); + + // Set a domain + pivot.update({domain: [['product_id', '=', 41]]}); + + var expectedContext = {pivot_column_groupby: ['customer'], + pivot_measures: ['foo'], + pivot_row_groupby: ['product_id']}; + + // Mock 'save as favorite' + assert.deepEqual(pivot.getContext(), expectedContext, + 'The pivot view should have the right context'); + + var $xpadHeader = pivot.$('tbody .o_pivot_header_cell_closed[data-original-title=Product]'); + assert.equal($xpadHeader.length, 1, + 'There should be only one product line because of the domain'); + + assert.equal($xpadHeader.text(), 'xpad', + 'The product should be the right one'); + + pivot.destroy(); + }); });}); -- cgit v1.2.3 From 9e6c616130622eabc5a43f84eca80c6326ded20b Mon Sep 17 00:00:00 2001 From: Martin Geubelle Date: Wed, 11 Apr 2018 17:19:45 +0200 Subject: [FIX] web: focus select in reference widget In the reference widget, the focus was always on the input (many2one) even though the first select (with the model) was not set. Thanks to @kaerdsar for the original PR. Partially closes #24070 --- .../web/static/src/js/fields/relational_fields.js | 15 +++++++++++++ .../static/tests/fields/relational_fields_tests.js | 26 ++++++++++++++++++++++ 2 files changed, 41 insertions(+) (limited to 'addons/web/static') diff --git a/addons/web/static/src/js/fields/relational_fields.js b/addons/web/static/src/js/fields/relational_fields.js index bc44d6b659a..5c670e11c73 100644 --- a/addons/web/static/src/js/fields/relational_fields.js +++ b/addons/web/static/src/js/fields/relational_fields.js @@ -2280,6 +2280,21 @@ var FieldReference = FieldMany2One.extend({ return this._super.apply(this, arguments); }, + //-------------------------------------------------------------------------- + // Public + //-------------------------------------------------------------------------- + + /** + * @override + * @returns {jQuery} + */ + getFocusableElement: function () { + if (this.mode === 'edit' && !this.field.relation) { + return this.$('select'); + } + return this._super.apply(this, arguments); + }, + //-------------------------------------------------------------------------- // Private //-------------------------------------------------------------------------- diff --git a/addons/web/static/tests/fields/relational_fields_tests.js b/addons/web/static/tests/fields/relational_fields_tests.js index b855f827c97..b28ddfafb0a 100644 --- a/addons/web/static/tests/fields/relational_fields_tests.js +++ b/addons/web/static/tests/fields/relational_fields_tests.js @@ -11298,6 +11298,32 @@ QUnit.module('relational_fields', { form.destroy(); }); + QUnit.test('reference and list navigation', function (assert) { + assert.expect(2); + + var list = createView({ + View: ListView, + model: 'partner', + data: this.data, + arch: '', + }); + + // edit first row + list.$('.o_data_row .o_data_cell').first().click(); + assert.strictEqual(list.$('.o_data_row:eq(0) .o_field_widget[name="reference"] input')[0], document.activeElement, + 'input of first data row should be selected'); + + // press TAB to go to next line + list.$('.o_data_row:eq(0) input').trigger($.Event('keydown', { + which: $.ui.keyCode.TAB, + keyCode: $.ui.keyCode.TAB, + })); + assert.strictEqual(list.$('.o_data_row:eq(1) .o_field_widget[name="reference"] select')[0], document.activeElement, + 'select of second data row should be selected'); + + list.destroy(); + }); + QUnit.test('one2many with extra field from server not in form', function (assert) { assert.expect(6); -- cgit v1.2.3 From cd934f307de19b247bf88b9dc41a8fdea15fd23c Mon Sep 17 00:00:00 2001 From: Martin Geubelle Date: Tue, 17 Apr 2018 11:46:12 +0200 Subject: [FIX] web: handle x2m commands with reference as default Before this rev. it was not possible to define a value for a reference field in x2m commands as default value. Courtesy of @kaerdsar. Closes #24070 --- .../web/static/src/js/views/basic/basic_model.js | 13 ++++++++- .../static/tests/fields/relational_fields_tests.js | 32 ++++++++++++++++++++++ 2 files changed, 44 insertions(+), 1 deletion(-) (limited to 'addons/web/static') diff --git a/addons/web/static/src/js/views/basic/basic_model.js b/addons/web/static/src/js/views/basic/basic_model.js index 4180acae602..8fdcb305ddc 100644 --- a/addons/web/static/src/js/views/basic/basic_model.js +++ b/addons/web/static/src/js/views/basic/basic_model.js @@ -3613,8 +3613,9 @@ var BasicModel = AbstractModel.extend({ if (isFieldInView) { var field = r.fields[fieldName]; var fieldType = field.type; + var rec; if (fieldType === 'many2one') { - var rec = self._makeDataPoint({ + rec = self._makeDataPoint({ context: r.context, modelName: field.relation, data: {id: r._changes[fieldName]}, @@ -3622,6 +3623,16 @@ var BasicModel = AbstractModel.extend({ }); r._changes[fieldName] = rec.id; many2ones[fieldName] = true; + } else if (fieldType === 'reference') { + var reference = r._changes[fieldName].split(','); + rec = self._makeDataPoint({ + context: r.context, + modelName: reference[0], + data: {id: parseInt(reference[1])}, + parentID: r.id, + }); + r._changes[fieldName] = rec.id; + many2ones[fieldName] = true; } else if (_.contains(['one2many', 'many2many'], fieldType)) { var x2mCommands = commands[0][2][fieldName]; defs.push(self._processX2ManyCommands(r, fieldName, x2mCommands)); diff --git a/addons/web/static/tests/fields/relational_fields_tests.js b/addons/web/static/tests/fields/relational_fields_tests.js index b28ddfafb0a..8fb1780489a 100644 --- a/addons/web/static/tests/fields/relational_fields_tests.js +++ b/addons/web/static/tests/fields/relational_fields_tests.js @@ -11248,6 +11248,38 @@ QUnit.module('relational_fields', { form.destroy(); }); + QUnit.test('default_get a reference field in a x2m', function (assert) { + assert.expect(1); + + this.data.partner.fields.turtles.default = [ + [0, false, {turtle_ref: 'product,37'}] + ]; + + var form = createView({ + View: FormView, + model: 'partner', + data: this.data, + arch: '
' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '
', + viewOptions: { + mode: 'edit', + }, + archs: { + 'turtle,false,form': '
', + }, + }); + assert.strictEqual(form.$('.o_field_one2many[name="turtles"] .o_data_row:first').text(), "xphone", + "the default value should be correctly handled"); + form.destroy(); + }); + QUnit.test('widget reference on char field, reset by onchange', function (assert) { assert.expect(4); -- cgit v1.2.3 From 05589db8232a68846cca2660c73914045b1fec5a Mon Sep 17 00:00:00 2001 From: Aaron Bohy Date: Fri, 13 Apr 2018 15:22:47 +0200 Subject: [FIX] web: don't ignore required attrs on image fields Before this rev., falsy image fields could be saved even if they were required. --- addons/web/static/src/js/fields/basic_fields.js | 3 --- addons/web/static/src/less/fields.less | 4 +++ .../web/static/tests/fields/basic_fields_tests.js | 29 +++++++++++++++++++++- 3 files changed, 32 insertions(+), 4 deletions(-) (limited to 'addons/web/static') diff --git a/addons/web/static/src/js/fields/basic_fields.js b/addons/web/static/src/js/fields/basic_fields.js index aff339cb4a2..ac8ad02b24a 100644 --- a/addons/web/static/src/js/fields/basic_fields.js +++ b/addons/web/static/src/js/fields/basic_fields.js @@ -1227,9 +1227,6 @@ var FieldBinaryImage = AbstractFieldBinary.extend({ self.do_warn(_t("Image"), _t("Could not display the selected image.")); }); }, - isSet: function () { - return true; - }, }); var FieldBinaryFile = AbstractFieldBinary.extend({ diff --git a/addons/web/static/src/less/fields.less b/addons/web/static/src/less/fields.less index 26753ca3d8b..4ac5643bf4d 100644 --- a/addons/web/static/src/less/fields.less +++ b/addons/web/static/src/less/fields.less @@ -309,6 +309,10 @@ &:hover .o_form_image_controls { opacity: 0.8; } + + &.o_field_invalid > img { + border: 1px solid @brand-danger; + } } // Input loading/file diff --git a/addons/web/static/tests/fields/basic_fields_tests.js b/addons/web/static/tests/fields/basic_fields_tests.js index 909e778b60a..7f1742b3041 100644 --- a/addons/web/static/tests/fields/basic_fields_tests.js +++ b/addons/web/static/tests/fields/basic_fields_tests.js @@ -1698,7 +1698,6 @@ QUnit.module('basic_fields', { assert.strictEqual(form.$('div[name="document"] > img').attr('width'), "90", "the image should correctly set its attributes"); form.destroy(); - }); QUnit.test('image fields in subviews are loaded correctly', function (assert) { @@ -1750,6 +1749,34 @@ QUnit.module('basic_fields', { form.destroy(); }); + QUnit.test('image fields with required attribute', function (assert) { + assert.expect(2); + + var form = createView({ + View: FormView, + model: 'partner', + data: this.data, + arch: '
' + + '' + + '', + mockRPC: function (route, args) { + if (args.method === 'create') { + throw new Error("Should not do a create RPC with unset required image field"); + } + return this._super.apply(this, arguments); + }, + }); + + form.$buttons.find('.o_form_button_save').click(); + + assert.ok(form.$('.o_form_view').hasClass('o_form_editable'), + "form view should still be editable"); + assert.ok(form.$('.o_field_widget').hasClass('o_field_invalid'), + "image field should be displayed as invalid"); + + form.destroy(); + }); + QUnit.module('JournalDashboardGraph', { beforeEach: function () { _.extend(this.data.partner.fields, { -- cgit v1.2.3