diff options
| author | Martin Trigaux <mat@odoo.com> | 2019-07-22 10:05:49 +0000 |
|---|---|---|
| committer | Martin Trigaux <mat@odoo.com> | 2019-07-22 10:05:49 +0000 |
| commit | aadc4a9f0be4a17ab3f25c48ac26feabf9aaab9a (patch) | |
| tree | 3b9dd52a473cc11d3a211c19ae62e8416ef6ef70 /addons/web | |
| parent | 1ebec12dc0f5b2a431a52b89909a949529e0b6ad (diff) | |
| parent | fe4caecb59dd9a4456bbf40563a842ca7e318307 (diff) | |
[MERGE] Forward port of saas-12.3 to saas-12.4 up to fe4caecb59d
closes odoo/odoo#35059
Signed-off-by: Martin Trigaux (mat) <mat@odoo.com>
Diffstat (limited to 'addons/web')
6 files changed, 129 insertions, 8 deletions
diff --git a/addons/web/static/src/js/views/abstract_controller.js b/addons/web/static/src/js/views/abstract_controller.js index 2a27ad03e92..74e1a239790 100644 --- a/addons/web/static/src/js/views/abstract_controller.js +++ b/addons/web/static/src/js/views/abstract_controller.js @@ -122,8 +122,11 @@ var AbstractController = mvc.Controller.extend(ActionMixin, { * @override */ canBeRemoved: function () { - // AAB: get rid of this option when on_hashchange mechanism is improved - return this.discardChanges(undefined, { readonlyIfRealDiscard: true }); + // AAB: get rid of 'readonlyIfRealDiscard' option when on_hashchange mechanism is improved + return this.discardChanges(undefined, { + noAbandon: true, + readonlyIfRealDiscard: true, + }); }, /** * Discards the changes made on the record associated to the given ID, or @@ -137,9 +140,10 @@ var AbstractController = mvc.Controller.extend(ActionMixin, { * * @param {string} [recordID] * if not given, we consider all the changes made by the controller + * @param {Object} [options] * @returns {Promise} resolved if properly discarded, rejected otherwise */ - discardChanges: function (recordID) { + discardChanges: function (recordID, options) { return Promise.resolve(); }, /** diff --git a/addons/web/static/src/js/views/basic/basic_controller.js b/addons/web/static/src/js/views/basic/basic_controller.js index 610e7e6dc6b..e76aec046e8 100644 --- a/addons/web/static/src/js/views/basic/basic_controller.js +++ b/addons/web/static/src/js/views/basic/basic_controller.js @@ -374,14 +374,16 @@ var BasicController = AbstractController.extend(FieldManagerMixin, { * the new record to be in edit mode too, but the view manager calls * this function as the URL changes...) @todo get rid of this when * the webclient/action_manager's hashchange mechanism is improved. + * @param {boolean} [options.noAbandon=false] * @returns {Promise} */ _discardChanges: function (recordID, options) { var self = this; recordID = recordID || this.handle; + options = options || {}; return this.canBeDiscarded(recordID) .then(function (needDiscard) { - if (options && options.readonlyIfRealDiscard && !needDiscard) { + if (options.noAbandon || (options.readonlyIfRealDiscard && !needDiscard)) { return; } self.model.discardChanges(recordID); diff --git a/addons/web/static/src/js/views/form/form_controller.js b/addons/web/static/src/js/views/form/form_controller.js index 827afbb99ed..660c7dc81e1 100644 --- a/addons/web/static/src/js/views/form/form_controller.js +++ b/addons/web/static/src/js/views/form/form_controller.js @@ -50,13 +50,18 @@ var FormController = BasicController.extend({ this.autofocus(); }, /** - * Force mode back to readonly. Whenever we leave a form view, it is saved, - * and should no longer be in edit mode. + * This hook is called when a form view is restored (by clicking on the + * breadcrumbs). In general, we force mode back to readonly, because + * whenever we leave a form view by stacking another action on the top of + * it, it is saved, and should no longer be in edit mode. However, there is + * a special case for new records for which we still want to be in 'edit' + * as no record has been created (changes have been discarded before + * leaving). * * @override */ willRestore: function () { - this.mode = 'readonly'; + this.mode = this.model.isNew(this.handle) ? 'edit' : 'readonly'; }, //-------------------------------------------------------------------------- diff --git a/addons/web/static/src/js/views/list/list_renderer.js b/addons/web/static/src/js/views/list/list_renderer.js index 46b8d58d05e..df6345d398f 100644 --- a/addons/web/static/src/js/views/list/list_renderer.js +++ b/addons/web/static/src/js/views/list/list_renderer.js @@ -620,7 +620,7 @@ var ListRenderer = BasicRenderer.extend({ var $pager = this._renderGroupPager(group); $lastCell.addClass('o_group_pager').append($pager); } - if (group.isOpen && this.groupbys[groupBy]) { + if (groupLevel === 0 && group.isOpen && this.groupbys[groupBy]) { var $buttons = this._renderGroupButtons(group, this.groupbys[groupBy]); if ($buttons.length) { var $buttonSection = $('<div>', { diff --git a/addons/web/static/tests/chrome/action_manager_tests.js b/addons/web/static/tests/chrome/action_manager_tests.js index c82755f2987..5724da4b2f8 100644 --- a/addons/web/static/tests/chrome/action_manager_tests.js +++ b/addons/web/static/tests/chrome/action_manager_tests.js @@ -3377,6 +3377,80 @@ QUnit.module('ActionManager', { actionManager.destroy(); }); + QUnit.test('execute action from dirty, new record, and come back', async function (assert) { + assert.expect(19); + + this.data.partner.fields.bar.default = 1; + this.archs['partner,false,form'] = '<form>' + + '<field name="foo"/>' + + '<field name="bar" readonly="1"/>' + + '</form>'; + + var actionManager = await createActionManager({ + actions: this.actions, + archs: this.archs, + data: this.data, + mockRPC: function (route, args) { + assert.step(args.method || route); + if (args.method === 'get_formview_action') { + return Promise.resolve({ + res_id: 1, + res_model: 'partner', + type: 'ir.actions.act_window', + views: [[false, 'form']], + }); + } + return this._super.apply(this, arguments); + }, + intercepts: { + do_action: function (ev) { + actionManager.doAction(ev.data.action, {}); + }, + }, + }); + + // execute an action and create a new record + await actionManager.doAction(3); + await testUtils.dom.click(actionManager.$('.o_list_button_add')); + assert.containsOnce(actionManager, '.o_form_view.o_form_editable'); + assert.containsOnce(actionManager, '.o_form_uri:contains(First record)'); + assert.strictEqual(actionManager.$('.o_control_panel .breadcrumb-item').text(), + "PartnersNew"); + + // set form view dirty and open m2o record + await testUtils.fields.editInput(actionManager.$('input[name=foo]'), 'val'); + await testUtils.dom.click(actionManager.$('.o_form_uri:contains(First record)')); + assert.containsOnce($('body'), '.modal'); // confirm discard dialog + + // confirm discard changes + await testUtils.dom.click($('.modal .modal-footer .btn-primary')); + + assert.containsOnce(actionManager, '.o_form_view.o_form_readonly'); + assert.strictEqual(actionManager.$('.o_control_panel .breadcrumb-item').text(), + "PartnersNewFirst record"); + + // go back to New using the breadcrumbs + await testUtils.dom.click(actionManager.$('.o_control_panel .breadcrumb-item:nth(1) a')); + assert.containsOnce(actionManager, '.o_form_view.o_form_editable'); + assert.strictEqual(actionManager.$('.o_control_panel .breadcrumb-item').text(), + "PartnersNew"); + + assert.verifySteps([ + '/web/action/load', // action 3 + 'load_views', // views of action 3 + '/web/dataset/search_read', // list + 'default_get', // form (create) + 'name_get', // m2o in form + 'get_formview_action', // click on m2o + 'load_views', // form view of dynamic action + 'read', // form + 'default_get', // form (create) + 'name_get', // m2o in form + ]); + + actionManager.destroy(); + }); + QUnit.module('Actions in target="new"'); QUnit.test('can execute act_window actions in target="new"', async function (assert) { diff --git a/addons/web/static/tests/views/list_tests.js b/addons/web/static/tests/views/list_tests.js index 9d0bb9fccfd..4e324762372 100644 --- a/addons/web/static/tests/views/list_tests.js +++ b/addons/web/static/tests/views/list_tests.js @@ -2064,6 +2064,42 @@ QUnit.module('Views', { list.destroy(); }); + QUnit.test('groupby node with a button in inner groupbys', async function (assert) { + assert.expect(5); + + var list = await createView({ + View: ListView, + model: 'foo', + data: this.data, + arch: '<tree>' + + '<field name="foo"/>' + + '<groupby name="currency_id">' + + '<button string="Button 1" type="object" name="button_method"/>' + + '</groupby>' + + '</tree>', + groupBy: ['bar', 'currency_id'], + }); + + assert.containsN(list, '.o_group_header', 2, + "there should be 2 group headers"); + assert.containsNone(list, '.o_group_header button', + "there should be no button in the header"); + + await testUtils.dom.click(list.$('.o_group_header:eq(0)')); + + assert.containsN(list, 'tbody:eq(1) .o_group_header', 2, + "there should be 2 inner groups header"); + assert.containsNone(list, 'tbody:eq(1) .o_group_header button', + "there should be no button in the header"); + + await testUtils.dom.click(list.$('tbody:eq(1) .o_group_header:eq(0)')); + + assert.containsNone(list, '.o_group_header button', + "there should be no button in the header"); + + list.destroy(); + }); + QUnit.test('groupby node with a button with modifiers', async function (assert) { assert.expect(11); |
