From 5ba6d93810ba447f94ca2bb0d6804865a32b5cbf Mon Sep 17 00:00:00 2001 From: Martin Geubelle Date: Fri, 17 Nov 2017 14:59:10 +0100 Subject: [PATCH 01/23] [FIX] web: Kanban: prevent drag&drop for readonly If the kanban view is grouped by a readonly field, it should not be possible to drag&drop the record from one column to another as this will trigger a write on this readonly field. It should however be possible to resequence records so the option `containment` is used in the records. Closes #19390 --- .../src/js/views/kanban/kanban_column.js | 5 +- .../src/js/views/kanban/kanban_record.js | 1 - .../src/js/views/kanban/kanban_renderer.js | 7 +- addons/web/static/tests/views/kanban_tests.js | 73 +++++++++++++++++++ 4 files changed, 82 insertions(+), 4 deletions(-) diff --git a/addons/web/static/src/js/views/kanban/kanban_column.js b/addons/web/static/src/js/views/kanban/kanban_column.js index 269e37e7082..82fc5d168ff 100644 --- a/addons/web/static/src/js/views/kanban/kanban_column.js +++ b/addons/web/static/src/js/views/kanban/kanban_column.js @@ -50,7 +50,7 @@ var KanbanColumn = Widget.extend({ this.grouped_by_m2o = options.grouped_by_m2o; this.editable = options.editable; this.deletable = options.deletable; - this.draggable = recordOptions.draggable; + this.draggable = options.draggable; this.records_editable = options.records_editable; this.records_deletable = options.records_deletable; this.relation = options.relation; @@ -87,12 +87,13 @@ var KanbanColumn = Widget.extend({ } this.$header.tooltip(); - if (config.device.size_class > config.device.SIZES.XS && this.draggable !== false) { + if (config.device.size_class > config.device.SIZES.XS) { // deactivate sortable in mobile mode. It does not work anyway, // and it breaks horizontal scrolling in kanban views. Someday, we // should find a way to use the touch events to make sortable work. this.$el.sortable({ connectWith: '.o_kanban_group', + containment: this.draggable ? '.o_kanban_view' : 'parent', revert: 0, delay: 0, items: '> .o_kanban_record:not(.o_updating)', diff --git a/addons/web/static/src/js/views/kanban/kanban_record.js b/addons/web/static/src/js/views/kanban/kanban_record.js index e5846e55cf4..7eb8b126508 100644 --- a/addons/web/static/src/js/views/kanban/kanban_record.js +++ b/addons/web/static/src/js/views/kanban/kanban_record.js @@ -33,7 +33,6 @@ var KanbanRecord = Widget.extend({ this.options = options; this.editable = options.editable; this.deletable = options.deletable; - this.draggable = options.draggable; this.read_only_mode = options.read_only_mode; this.qweb = options.qweb; this.subWidgets = {}; diff --git a/addons/web/static/src/js/views/kanban/kanban_renderer.js b/addons/web/static/src/js/views/kanban/kanban_renderer.js index cd9572b0fc5..b8c2e3c0bcd 100644 --- a/addons/web/static/src/js/views/kanban/kanban_renderer.js +++ b/addons/web/static/src/js/views/kanban/kanban_renderer.js @@ -316,7 +316,7 @@ var KanbanRenderer = BasicRenderer.extend({ var groupByFieldInfo = state.fieldsInfo.kanban[state.groupedBy[0]]; // Deactivate the drag'n'drop if the groupedBy field: // - is a date or datetime since we group by month or - // - is readonly + // - is readonly (on the field attrs or in the view) var draggable = true; if (groupByFieldAttrs) { if (groupByFieldAttrs.type === "date" || groupByFieldAttrs.type === "datetime") { @@ -325,6 +325,11 @@ var KanbanRenderer = BasicRenderer.extend({ draggable = !(groupByFieldAttrs.readonly); } } + if (groupByFieldInfo) { + if (draggable && groupByFieldInfo.readonly !== undefined) { + draggable = !(groupByFieldInfo.readonly); + } + } this.groupedByM2O = groupByFieldAttrs && (groupByFieldAttrs.type === 'many2one'); var grouped_by_field = this.groupedByM2O && groupByFieldAttrs.relation; var groupByTooltip = groupByFieldInfo && groupByFieldInfo.options.group_by_tooltip; diff --git a/addons/web/static/tests/views/kanban_tests.js b/addons/web/static/tests/views/kanban_tests.js index 64decd5f657..ca6a2ce50d5 100644 --- a/addons/web/static/tests/views/kanban_tests.js +++ b/addons/web/static/tests/views/kanban_tests.js @@ -581,6 +581,79 @@ QUnit.module('Views', { kanban.destroy(); }); + QUnit.test('prevent drag and drop of record if grouped by readonly', function (assert) { + assert.expect(12); + + this.data.partner.fields.foo.readonly = true; + var kanban = createView({ + View: KanbanView, + model: 'partner', + data: this.data, + arch: '' + + '' + + '
' + + '' + + '' + + '
' + + '
' + + '
', + mockRPC: function (route, args) { + if (route === '/web/dataset/resequence') { + return $.when(); + } + if (args.model === 'partner' && args.method === 'write') { + throw new Error('should not be draggable'); + } + return this._super(route, args); + }, + }); + // simulate an update coming from the searchview, with another groupby given + kanban.update({groupBy: ['state']}); + assert.strictEqual(kanban.$('.o_kanban_group:nth-child(1) .o_kanban_record').length, 1, + "column should contain 1 record(s)"); + assert.strictEqual(kanban.$('.o_kanban_group:nth-child(2) .o_kanban_record').length, 1, + "column should contain 1 record(s)"); + // drag&drop a record in another column + var $record = kanban.$('.o_kanban_group:nth-child(1) .o_kanban_record:first'); + var $group = kanban.$('.o_kanban_group:nth-child(2)'); + testUtils.dragAndDrop($record, $group); + // should not be draggable + assert.strictEqual(kanban.$('.o_kanban_group:nth-child(1) .o_kanban_record').length, 1, + "column should now contain 1 record(s)"); + assert.strictEqual(kanban.$('.o_kanban_group:nth-child(2) .o_kanban_record').length, 1, + "column should contain 1 record(s)"); + + // simulate an update coming from the searchview, with another groupby given + kanban.update({groupBy: ['foo']}); + assert.strictEqual(kanban.$('.o_kanban_group:nth-child(1) .o_kanban_record').length, 1, + "column should contain 1 record(s)"); + assert.strictEqual(kanban.$('.o_kanban_group:nth-child(2) .o_kanban_record').length, 2, + "column should contain 2 record(s)"); + // drag&drop a record in another column + $record = kanban.$('.o_kanban_group:nth-child(1) .o_kanban_record:first'); + $group = kanban.$('.o_kanban_group:nth-child(2)'); + testUtils.dragAndDrop($record, $group); + // should not be draggable + assert.strictEqual(kanban.$('.o_kanban_group:nth-child(1) .o_kanban_record').length, 1, + "column should now contain 1 record(s)"); + assert.strictEqual(kanban.$('.o_kanban_group:nth-child(2) .o_kanban_record').length, 2, + "column should contain 2 record(s)"); + + // drag&drop a record in the same column + var $record1 = kanban.$('.o_kanban_group:nth-child(2) .o_kanban_record:eq(0)'); + var $record2 = kanban.$('.o_kanban_group:nth-child(2) .o_kanban_record:eq(1)'); + assert.strictEqual($record1.text(), "blipDEF", "first record should be DEF"); + assert.strictEqual($record2.text(), "blipGHI", "second record should be GHI"); + testUtils.dragAndDrop($record2, $record1, {position: 'top'}); + // should still be able to resequence + assert.strictEqual(kanban.$('.o_kanban_group:nth-child(2) .o_kanban_record:eq(0)').text(), "blipGHI", + "records should have been resequenced"); + assert.strictEqual(kanban.$('.o_kanban_group:nth-child(2) .o_kanban_record:eq(1)').text(), "blipDEF", + "records should have been resequenced"); + + kanban.destroy(); + }); + QUnit.test('kanban view with default_group_by', function (assert) { assert.expect(7); this.data.partner.records.product_id = 1; From d7d9603f1bc9d7200a7954880dc9c82fd6369be7 Mon Sep 17 00:00:00 2001 From: Simon Lejeune Date: Wed, 15 Nov 2017 15:34:10 +0100 Subject: [PATCH 02/23] [FIX] resource: mixin: copy When copying a record implementing the resource mixin, the linked resource should be copied too and not shared across the records. Fixes #20956 opw-781508 --- addons/resource/models/resource_mixin.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/addons/resource/models/resource_mixin.py b/addons/resource/models/resource_mixin.py index bd9fcc2e640..7aad40534e4 100644 --- a/addons/resource/models/resource_mixin.py +++ b/addons/resource/models/resource_mixin.py @@ -31,6 +31,16 @@ class ResourceMixin(models.AbstractModel): values['resource_id'] = resource.id return super(ResourceMixin, self).create(values) + @api.multi + def copy_data(self, default=None): + if default is None: + default = {} + resource = self.resource_id.copy() + default['resource_id'] = resource.id + default['company_id'] = resource.company_id.id + default['resource_calendar_id'] = resource.calendar_id.id + return super(ResourceMixin, self).copy_data(default) + def get_work_days_count(self, from_datetime, to_datetime, calendar=None): """ Return the number of work days for the resource, taking into account leaves. An optional calendar can be given in case multiple calendars can From 5d1640edff14b465a1f83d9c09393738c48af882 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9ry=20Debongnie?= Date: Fri, 17 Nov 2017 13:16:59 +0100 Subject: [PATCH 03/23] [FIX] web: add support for limit attr in arch in sub tree view Before this commit, the 'limit' attribtue worked properly for list views (but was not documented), and was ignored for inline tree views. There was no good reason for that limitation, so we fix this issue. --- .../web/static/src/js/views/abstract_view.js | 23 ++++++-- .../tests/fields/relational_fields_tests.js | 59 +++++++++++++++++++ addons/web/static/tests/views/list_tests.js | 24 ++++++++ doc/reference/views.rst | 2 + 4 files changed, 104 insertions(+), 4 deletions(-) diff --git a/addons/web/static/src/js/views/abstract_view.js b/addons/web/static/src/js/views/abstract_view.js index 240ce7707a9..4c2b304d1e4 100644 --- a/addons/web/static/src/js/views/abstract_view.js +++ b/addons/web/static/src/js/views/abstract_view.js @@ -253,9 +253,6 @@ var AbstractView = Class.extend({ return; } - attrs.limit = attrs.mode === "tree" ? 80 : - (attrs.widget === 'many2many_tags' ? 1000 : 40); - if (attrs.Widget.prototype.useSubview && !attrs.__no_fetch && !attrs.views[attrs.mode]) { var context = {}; var regex = /'([a-z]*_view_ref)' *: *'(.*?)'/g; @@ -271,11 +268,29 @@ var AbstractView = Class.extend({ for (var viewName in views) { attrs.views[viewName] = views[viewName]; } + self._setSubViewLimit(attrs); })); + } else { + self._setSubViewLimit(attrs); } }); return $.when.apply($, defs); - } + }, + /** + * We set here the limit for the number of records fetched (in one page). + * This method is only called for subviews, not for main views. + * + * @private + * @param {Object} attrs + */ + _setSubViewLimit: function (attrs) { + var view = attrs.views && attrs.views[attrs.mode]; + var limit = view && view.arch.attrs.limit && parseInt(view.arch.attrs.limit); + if (!limit && attrs.widget === 'many2many_tags') { + limit = 1000; + } + attrs.limit = limit || 40; + }, }); return AbstractView; diff --git a/addons/web/static/tests/fields/relational_fields_tests.js b/addons/web/static/tests/fields/relational_fields_tests.js index 4c78c2ec550..b0b966a01c1 100644 --- a/addons/web/static/tests/fields/relational_fields_tests.js +++ b/addons/web/static/tests/fields/relational_fields_tests.js @@ -2122,6 +2122,65 @@ QUnit.module('relational_fields', { form.destroy(); }); + QUnit.test('use the limit attribute in arch (in field o2m inline tree view)', function (assert) { + assert.expect(2); + + this.data.partner.records[0].turtles = [1,2,3]; + var form = createView({ + View: FormView, + model: 'partner', + data: this.data, + arch:'
' + + '' + + '' + + '' + + '' + + '' + + '
', + res_id: 1, + mockRPC: function (route, args) { + if (args.model === 'turtle') { + assert.deepEqual(args.args[0], [1,2], + 'should only load first 2 records'); + } + return this._super.apply(this, arguments); + }, + }); + + assert.strictEqual(form.$('.o_data_row').length, 2, + 'should display 2 data rows'); + form.destroy(); + }); + + QUnit.test('use the limit attribute in arch (in field o2m non inline tree view)', function (assert) { + assert.expect(2); + + this.data.partner.records[0].turtles = [1,2,3]; + var form = createView({ + View: FormView, + model: 'partner', + data: this.data, + arch:'
' + + '' + + '', + archs: { + 'turtle,false,list': '', + }, + res_id: 1, + mockRPC: function (route, args) { + if (args.model === 'turtle') { + assert.deepEqual(args.args[0], [1,2], + 'should only load first 2 records'); + } + return this._super.apply(this, arguments); + }, + }); + + assert.strictEqual(form.$('.o_data_row').length, 2, + 'should display 2 data rows'); + form.destroy(); + }); + QUnit.test('embedded one2many with widget', function (assert) { assert.expect(1); diff --git a/addons/web/static/tests/views/list_tests.js b/addons/web/static/tests/views/list_tests.js index 6156a218744..6ebf52ad8d0 100644 --- a/addons/web/static/tests/views/list_tests.js +++ b/addons/web/static/tests/views/list_tests.js @@ -3157,6 +3157,30 @@ QUnit.module('Views', { "The first element of the row name should be a span"); list.destroy(); }); + + QUnit.test('use the limit attribute in arch', function (assert) { + assert.expect(3); + + var list = createView({ + View: ListView, + model: 'foo', + data: this.data, + arch: '', + mockRPC: function (route, args) { + assert.strictEqual(args.limit, 2, + 'should use the correct limit value'); + return this._super.apply(this, arguments); + }, + }); + + assert.strictEqual(list.pager.$el.text().trim(), '1-2 / 4', + "pager should be correct"); + + assert.strictEqual(list.$('.o_data_row').length, 2, + 'should display 2 data rows'); + list.destroy(); + }); + }); }); diff --git a/doc/reference/views.rst b/doc/reference/views.rst index e2d83e767b0..ba79106c676 100644 --- a/doc/reference/views.rst +++ b/doc/reference/views.rst @@ -171,6 +171,8 @@ root can have the following attributes: ``create``, ``edit``, ``delete`` allows *dis*\ abling the corresponding action in the view by setting the corresponding attribute to ``false`` +``limit`` + the default size of a page. It should be a positive integer ``on_write`` only makes sense on an ``editable`` list. Should be the name of a method on the list's model. The method will be called with the ``id`` of a record From 6cd17d575c7d970286ec3dfedcfacf01be5f7592 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9ry=20Debongnie?= Date: Fri, 17 Nov 2017 13:17:16 +0100 Subject: [PATCH 04/23] [FIX] web: properly refetch x2manys in some cases Consider the following situation: a form view with a one2many which contains sub x2many fields (for example a m2m_tags). Suppose that the one2many has more than 40 records. Before this commit, trying to delete one of these records caused a crash. The reason is that when we delete a record, we need to reload the missing records in the one2many field. We actually do that with a _readUngroupedList. However, the _readUngroupedList method did not process x2many fields, so the information stored in the datapoints was 'raw', and the rest of the web client expected it to be a datapoint. To reproduce the issue in the testing environment, i had to use onchanges and a readonly attrs, to reach a situation where a crash could occur. This shows that, while the issue is critical, it was not totally trivial to be impacted by it. --- .../static/src/js/views/basic/basic_model.js | 4 +- .../tests/fields/relational_fields_tests.js | 46 +++++++++++++++++++ 2 files changed, 49 insertions(+), 1 deletion(-) 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 fcd9785b9a5..ed18be034af 100644 --- a/addons/web/static/src/js/views/basic/basic_model.js +++ b/addons/web/static/src/js/views/basic/basic_model.js @@ -1573,7 +1573,9 @@ var BasicModel = AbstractModel.extend({ return $.when.apply($, defs).then(function () { // ensure to fetch up to 'limit' records (may be useful if records of // the current page have been removed) - return self._readUngroupedList(list); + return self._readUngroupedList(list).then(function () { + return self._fetchX2ManysBatched(list); + }); }); }, /** diff --git a/addons/web/static/tests/fields/relational_fields_tests.js b/addons/web/static/tests/fields/relational_fields_tests.js index b0b966a01c1..eed533fb6fe 100644 --- a/addons/web/static/tests/fields/relational_fields_tests.js +++ b/addons/web/static/tests/fields/relational_fields_tests.js @@ -5084,6 +5084,52 @@ QUnit.module('relational_fields', { form.destroy(); }); + QUnit.test('many2manytag in one2many, onchange, some modifiers, and more than one page', function (assert) { + assert.expect(9); + + this.data.partner.records[0].turtles = [1,2,3]; + + this.data.partner.onchanges.turtles = function () {}; + + var form = createView({ + View: FormView, + model: 'partner', + data: this.data, + arch: '
' + + '' + + '' + + '' + + '' + + '' + + '' + + '
', + res_id: 1, + viewOptions: {mode: 'edit'}, + mockRPC: function (route, args) { + assert.step(args.method); + return this._super.apply(this, arguments); + }, + }); + assert.strictEqual(form.$('.o_data_row').length, 2, + 'there should be only 2 rows displayed'); + form.$('.o_list_record_delete').click(); + form.$('.o_list_record_delete').click(); + + assert.strictEqual(form.$('.o_data_row').length, 1, + 'there should be just one remaining row'); + + assert.verifySteps([ + "read", // initial read on partner + "read", // initial read on turtle + "read", // batched read on partner (field partner_ids) + "read", // after first delete, read on turtle (to fetch 3rd record) + "onchange", // after first delete, onchange on field turtles + "onchange" // onchange after second delete + ]); + + form.destroy(); + }); + QUnit.test('load view for x2many in one2many', function (assert) { assert.expect(2); From a96c07a7655d090f6a0986c60a097aaae2728611 Mon Sep 17 00:00:00 2001 From: Martin Geubelle Date: Mon, 20 Nov 2017 11:48:17 +0100 Subject: [PATCH 05/23] [FIX] web: make broken test in firefox pass The attribute `autocomplete` is set with the jQuery function `attr` but the test was using the function `prop`. This seems to work in Chrome but apparently not in Firefox. --- addons/web/static/tests/views/form_tests.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/web/static/tests/views/form_tests.js b/addons/web/static/tests/views/form_tests.js index 5fdcb2b1ae3..bf905f47878 100644 --- a/addons/web/static/tests/views/form_tests.js +++ b/addons/web/static/tests/views/form_tests.js @@ -5593,9 +5593,9 @@ QUnit.module('Views', { form.$buttons.find('.o_form_button_edit').click(); assert.strictEqual(form.$('input[name="foo"]').val(), '***', "password should be displayed with stars"); - assert.strictEqual(form.$('input[name="display_name"]').prop('autocomplete'), 'coucou', + assert.strictEqual(form.$('input[name="display_name"]').attr('autocomplete'), 'coucou', "attribute autocomplete should be set"); - assert.strictEqual(form.$('input[name="foo"]').prop('autocomplete'), 'new-password', + assert.strictEqual(form.$('input[name="foo"]').attr('autocomplete'), 'new-password', "attribute autocomplete should be set to 'new-password' on password input"); form.destroy(); }); From d5e51717d518a9efd6246704a96e14fce963470e Mon Sep 17 00:00:00 2001 From: "Lucas Perais (lpe)" Date: Mon, 20 Nov 2017 14:16:36 +0100 Subject: [PATCH 06/23] [FIX] point_of_sale: some events changed to jquery Before this commit, it was not possible to type on a physical keyboard the amount of a payment in the relevant pos screen This was due to the refactoring of BarcodeEvents (commit b131e9b0ef458e8cc00b0a2eb6c1b4d8dfe889a0) which converted vanilla JS events to Jquery ones, which the pos was not ready for. After this commit, it is possible to type the amount of a payment in the payment screen, just as it worked in 10.0 so far. OPW 781242 closes #21094 --- addons/point_of_sale/static/src/js/screens.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/addons/point_of_sale/static/src/js/screens.js b/addons/point_of_sale/static/src/js/screens.js index 0f13a4e7f6f..be7ebc43243 100644 --- a/addons/point_of_sale/static/src/js/screens.js +++ b/addons/point_of_sale/static/src/js/screens.js @@ -1834,11 +1834,18 @@ var PaymentScreenWidget = ScreenWidget.extend({ this.reset_input(); this.render_paymentlines(); this.order_changes(); + // that one comes from BarcodeEvents + $('body').keypress(this.keyboard_handler); + // that one comes from the pos, but we prefer to cover all the basis + $('body').keydown(this.keyboard_keydown_handler); + // legacy vanilla JS listeners window.document.body.addEventListener('keypress',this.keyboard_handler); window.document.body.addEventListener('keydown',this.keyboard_keydown_handler); this._super(); }, hide: function(){ + $('body').off('keypress', this.keyboard_handler); + $('body').off('keydown', this.keyboard_keydown_handler); window.document.body.removeEventListener('keypress',this.keyboard_handler); window.document.body.removeEventListener('keydown',this.keyboard_keydown_handler); this._super(); From fd7b767637cd9563e9869adf537150867925f120 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9ry=20Debongnie?= Date: Mon, 20 Nov 2017 15:17:23 +0100 Subject: [PATCH 07/23] [FIX] web: empty field char should be set to false With the new views, empty char fields were sent to the server as '' (empty string) instead of false. This is not the correct behaviour, and actually cause some problems. For example, if you have a char field with a unicity constraint, then you cannot set it to '', since there is only one record that can take that value. --- .../static/src/js/views/basic/basic_model.js | 2 ++ .../static/tests/fields/basic_fields_tests.js | 32 +++++++++++++++++++ 2 files changed, 34 insertions(+) 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 ed18be034af..e8f4a3993fa 100644 --- a/addons/web/static/src/js/views/basic/basic_model.js +++ b/addons/web/static/src/js/views/basic/basic_model.js @@ -2499,6 +2499,8 @@ var BasicModel = AbstractModel.extend({ changes[fieldName] = value ? this.localData[value].model + ',' + this.localData[value].res_id : false; + } else if (type === 'char' && changes[fieldName] === '') { + changes[fieldName] = false; } else if (changes[fieldName] === null) { changes[fieldName] = false; } diff --git a/addons/web/static/tests/fields/basic_fields_tests.js b/addons/web/static/tests/fields/basic_fields_tests.js index c6feea74b77..30e5d477882 100644 --- a/addons/web/static/tests/fields/basic_fields_tests.js +++ b/addons/web/static/tests/fields/basic_fields_tests.js @@ -718,6 +718,38 @@ QUnit.module('basic_fields', { form.destroy(); }); + QUnit.test('setting a char field to empty string is saved as a false value', function (assert) { + assert.expect(1); + + var form = createView({ + View: FormView, + model: 'partner', + data: this.data, + arch: '
' + + '' + + '' + + '' + + '' + + '' + + '
', + res_id: 1, + viewOptions: {mode: 'edit'}, + mockRPC: function (route, args) { + if (args.method === 'write') { + assert.strictEqual(args.args[1].foo, false, + 'the foo value should be false'); + } + return this._super.apply(this, arguments); + } + }); + + form.$('input[type="text"].o_field_widget').val('').trigger('input'); + + // save + form.$buttons.find('.o_form_button_save').click(); + form.destroy(); + }); + QUnit.test('char field with size attribute', function (assert) { assert.expect(1); From 4de48320cac5450db8c881b9f410d812c4f7748c Mon Sep 17 00:00:00 2001 From: Martin Geubelle Date: Mon, 20 Nov 2017 15:04:12 +0100 Subject: [PATCH 08/23] [FIX] board: avoid crash for non-existing action After uninstalling an module, some actions specified in the dashboard may not exist anymore (the dashboard view isn't correctly cleaned up). This was causing a traceback because the dashboard was trying to load views corresponding to an non-existing action. Closes #19995 --- addons/board/static/src/js/dashboard.js | 4 +++ addons/board/static/tests/dashboard_tests.js | 38 ++++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/addons/board/static/src/js/dashboard.js b/addons/board/static/src/js/dashboard.js index 72a2a0383f6..989511b5327 100644 --- a/addons/board/static/src/js/dashboard.js +++ b/addons/board/static/src/js/dashboard.js @@ -222,6 +222,10 @@ FormRenderer.include({ params: {action_id: params.actionID} }) .then(function (action) { + if (!action) { + // the action does not exist anymore + return $.when(); + } var view = _.find(action.views, function (descr) { return descr[1] === params.viewType; }); diff --git a/addons/board/static/tests/dashboard_tests.js b/addons/board/static/tests/dashboard_tests.js index 27d685ff46e..83e65f7a9e6 100644 --- a/addons/board/static/tests/dashboard_tests.js +++ b/addons/board/static/tests/dashboard_tests.js @@ -380,4 +380,42 @@ QUnit.test('twice the same action in a dashboard', function (assert) { form.destroy(); }); +QUnit.test('non-existing action in a dashboard', function (assert) { + assert.expect(1); + + var form = createView({ + View: FormView, + model: 'board', + data: this.data, + arch: '
' + + '' + + '' + + '' + + '' + + '' + + '
', + intercepts: { + load_views: function () { + throw new Error('load_views should not be called'); + } + }, + mockRPC: function (route) { + if (route === '/board/static/src/img/layout_1-1-1.png') { + return $.when(); + } + if (route === '/web/action/load') { + // server answer if the action doesn't exist anymore + return $.when(false); + } + return this._super.apply(this, arguments); + }, + }); + + assert.strictEqual(form.$('.oe_action:contains(ABC)').length, 1, + "there should be a box for the non-existing action"); + + form.destroy(); +}); + + }); From 5c9f4fd67d37b088dd9453d06351310210497b6e Mon Sep 17 00:00:00 2001 From: "Lucas Perais (lpe)" Date: Mon, 13 Nov 2017 11:14:07 +0100 Subject: [PATCH 09/23] [FIX] web: fieldDependency should be in fields too Commit f45edfbe6d03b15977392dda9561cd7a3e16ce34 fixed a bug on image fields but was a partial fix. The problem is that while adding field dependencies to fieldsInfo, it did not add them in the fields of the view, making the view crash. After this commit, views and subviews contain fieldDependency as both info and actual fields of the view OPW 780021, 781620, 779649 [FIX] web: tests on many2many subviews closes #20891 --- .../static/src/js/services/data_manager.js | 6 ++- .../static/tests/fields/basic_fields_tests.js | 49 +++++++++++++++++++ 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/addons/web/static/src/js/services/data_manager.js b/addons/web/static/src/js/services/data_manager.js index 67e39d10bc8..b787fab55f9 100644 --- a/addons/web/static/src/js/services/data_manager.js +++ b/addons/web/static/src/js/services/data_manager.js @@ -406,8 +406,12 @@ return core.Class.extend({ if (fieldsInfo[node.attrs.name].fieldDependencies) { var deps = fieldsInfo[node.attrs.name].fieldDependencies; for (var dependency_name in deps) { + var dependency_dict = {name: dependency_name, type: deps[dependency_name].type}; if (!(dependency_name in fieldsInfo)) { - fieldsInfo[dependency_name] = {'name': dependency_name, 'type': deps[dependency_name].type, 'options': deps[dependency_name].options || {}}; + fieldsInfo[dependency_name] = _.extend({}, dependency_dict, {options: deps[dependency_name].options || {}}); + } + if (!(dependency_name in fields)) { + fields[dependency_name] = dependency_dict; } } } diff --git a/addons/web/static/tests/fields/basic_fields_tests.js b/addons/web/static/tests/fields/basic_fields_tests.js index 30e5d477882..ef555868e94 100644 --- a/addons/web/static/tests/fields/basic_fields_tests.js +++ b/addons/web/static/tests/fields/basic_fields_tests.js @@ -1496,6 +1496,55 @@ QUnit.module('basic_fields', { }); + QUnit.test('image fields in subviews are loaded correctly', function (assert) { + assert.expect(5); + + this.data.partner.records[0].__last_update = '2017-02-08 10:00:00'; + this.data.partner.records[0].document = 'myimage'; + this.data.partner_type.fields.image = {name: 'image', type: 'binary'}; + this.data.partner_type.records[0].image = 'product_image'; + this.data.partner.records[0].timmy = [12]; + + var form = createView({ + View: FormView, + model: 'partner', + data: this.data, + arch: '
' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '', + res_id: 1, + mockRPC: function (route, args) { + if (route === 'data:image/png;base64,myimage') { + assert.step("The view's image should have been fetched"); + return $.when('wow'); + } + return this._super.apply(this, arguments); + }, + }); + assert.verifySteps(["The view's image should have been fetched"]); + + assert.strictEqual(form.$('tr.o_data_row').length, 1, + 'There should be one record in the many2many'); + + // Actual flow: click on an element of the m2m to get its form view + form.$('tbody td:contains(gold)').click(); + assert.strictEqual($('.modal-dialog').length, 1, + 'The modal should have opened'); + assert.strictEqual($('.modal-dialog').find('.o_field_image > img')[0].src, + 'data:image/png;base64,product_image', + 'The image of the many2many in its form view should be present'); + + form.destroy(); + }); + QUnit.module('JournalDashboardGraph'); QUnit.test('graph dashboard widget is rendered correctly', function (assert) { From 98f72efcbe228709d1858d43f4aa5bb43912032c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9ry=20Debongnie?= Date: Tue, 21 Nov 2017 14:36:00 +0100 Subject: [PATCH 10/23] [FIX] web_tour: add some logging messages With this commit, we add a few extra logs to help debugging issues with phantomjs. I suspect that the recent problems with phantomjs are due to the localstorage not being cleared properly, but the logs are not sufficient to determine the exact cause. --- addons/web_tour/static/src/js/tour_manager.js | 2 ++ odoo/tests/phantomtest.js | 1 + 2 files changed, 3 insertions(+) diff --git a/addons/web_tour/static/src/js/tour_manager.js b/addons/web_tour/static/src/js/tour_manager.js index 8d6d35c102f..9b5d4a19718 100644 --- a/addons/web_tour/static/src/js/tour_manager.js +++ b/addons/web_tour/static/src/js/tour_manager.js @@ -204,6 +204,7 @@ return core.Class.extend(mixins.EventDispatcherMixin, ServicesMixin, { this.running_step_delay = parseInt(local_storage.getItem(get_running_delay_key()), 10) || 10; this.edition = (_.last(session.server_version_info) === 'e') ? 'enterprise' : 'community'; this._log = []; + console.log('Tour Manager is ready. running_tour=' + this.running_tour); }, /** * Registers a tour described by the following arguments (in order) @@ -271,6 +272,7 @@ return core.Class.extend(mixins.EventDispatcherMixin, ServicesMixin, { }).bind(this)); }, run: function (tour_name, step_delay) { + console.log(_.str.sprintf("Preparing tour %s", tour_name)); if (this.running_tour) { this._deactivate_tip(this.active_tooltips[this.running_tour]); this._consume_tour(this.running_tour, _.str.sprintf("Killing tour %s", this.running_tour)); diff --git a/odoo/tests/phantomtest.js b/odoo/tests/phantomtest.js index a372821d58e..b77cc3ffe9d 100644 --- a/odoo/tests/phantomtest.js +++ b/odoo/tests/phantomtest.js @@ -61,6 +61,7 @@ function PhantomTest() { console.log(''+message+''); }; this.page.onLoadFinished = function(status) { + console.log('PhantomJS: onLoadFinished. Status=', status); if (status === "success") { for (var k in self.inject) { var found = false; From 83c1ef83cdb5c5df561a3822154508ecd9533d23 Mon Sep 17 00:00:00 2001 From: Nicolas Martinelli Date: Wed, 22 Nov 2017 13:38:35 +0100 Subject: [PATCH 11/23] [FIX] report: boxed layout In the footer of the boxed layout, the font-size used might lead to a wrapping of the phone and VAT numbers. opw-782639 --- addons/report/static/src/less/layout_boxed.less | 1 + 1 file changed, 1 insertion(+) diff --git a/addons/report/static/src/less/layout_boxed.less b/addons/report/static/src/less/layout_boxed.less index a81e09f347b..21fcf66efc5 100644 --- a/addons/report/static/src/less/layout_boxed.less +++ b/addons/report/static/src/less/layout_boxed.less @@ -15,6 +15,7 @@ } } .o_boxed_footer { + white-space: nowrap; border-top: 3px solid @gray; ul { margin: 4px 0; From 728f624efd1a3a65c596c4b8c37f3f6d15b5db51 Mon Sep 17 00:00:00 2001 From: Yannick Tivisse Date: Wed, 22 Nov 2017 15:57:32 +0100 Subject: [PATCH 12/23] [FIX] l10n_be_hr_payroll: Lower the rule sequence Just to be able to insert other rules between before 100 --- addons/l10n_be_hr_payroll/data/l10n_be_hr_payroll_data.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/l10n_be_hr_payroll/data/l10n_be_hr_payroll_data.xml b/addons/l10n_be_hr_payroll/data/l10n_be_hr_payroll_data.xml index b7e0c3b6ecb..2b2c60f6f8a 100644 --- a/addons/l10n_be_hr_payroll/data/l10n_be_hr_payroll_data.xml +++ b/addons/l10n_be_hr_payroll/data/l10n_be_hr_payroll_data.xml @@ -401,7 +401,7 @@ elif salary <= 2461.27: ATN Company Car ATN.CAR - 99 + 70 python result = bool(contract.transport_mode == 'company_car' and contract.car_atn) From 534081c92ca9fb179af42974ed188bf2ce971b03 Mon Sep 17 00:00:00 2001 From: Martin Trigaux Date: Thu, 23 Nov 2017 10:05:50 +0100 Subject: [PATCH 13/23] [FIX] hr: unprout views Courtesy of @tde-banana-odoo --- addons/hr/i18n/hr.pot | 5 ----- addons/hr/views/hr_config_settings_views.xml | 2 +- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/addons/hr/i18n/hr.pot b/addons/hr/i18n/hr.pot index 6438af674df..f3e09c8a6bd 100644 --- a/addons/hr/i18n/hr.pot +++ b/addons/hr/i18n/hr.pot @@ -610,11 +610,6 @@ msgstr "" msgid "Position" msgstr "" -#. module: hr -#: model:ir.ui.view,arch_db:hr.hr_config_settings_view_form -msgid "Prout prout prout" -msgstr "" - #. module: hr #: model:ir.ui.view,arch_db:hr.view_employee_form msgid "Public Information" diff --git a/addons/hr/views/hr_config_settings_views.xml b/addons/hr/views/hr_config_settings_views.xml index e3071728307..d4244203861 100644 --- a/addons/hr/views/hr_config_settings_views.xml +++ b/addons/hr/views/hr_config_settings_views.xml @@ -12,7 +12,7 @@

Work Organization

-
+