From 61fe7a782002abb5d7cd7394d83a81f6248267e9 Mon Sep 17 00:00:00 2001 From: qdp-odoo Date: Mon, 23 Apr 2018 16:47:00 +0200 Subject: [PATCH 1/6] [FIX] account: allow switching from CoA if empty moves exists This is needed in case we open the Chart of Account step from the configuration bar, as it will silently create the opening journal entry --- addons/account/models/chart_template.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/account/models/chart_template.py b/addons/account/models/chart_template.py index f2e5183eee0..8de2ea0f685 100644 --- a/addons/account/models/chart_template.py +++ b/addons/account/models/chart_template.py @@ -847,7 +847,7 @@ class WizardMultiChartsAccounts(models.TransientModel): @api.multi def existing_accounting(self, company_id): - model_to_check = ['account.move.line', 'account.invoice', 'account.move', 'account.payment', 'account.bank.statement'] + model_to_check = ['account.move.line', 'account.invoice', 'account.payment', 'account.bank.statement'] for model in model_to_check: if len(self.env[model].search([('company_id', '=', company_id.id)])) > 0: return True @@ -882,7 +882,7 @@ class WizardMultiChartsAccounts(models.TransientModel): accounting_props.unlink() # delete account, journal, tax, fiscal position and reconciliation model - models_to_delete = ['account.reconcile.model', 'account.fiscal.position', 'account.tax', 'account.journal'] + models_to_delete = ['account.reconcile.model', 'account.fiscal.position', 'account.tax', 'account.move', 'account.journal'] for model in models_to_delete: res = self.env[model].search([('company_id', '=', self.company_id.id)]) if len(res): From c1bf1c6a5460611c2c1cfdbe736d3b1741c7300b Mon Sep 17 00:00:00 2001 From: Mohammed Shekha Date: Mon, 16 Apr 2018 15:06:10 +0530 Subject: [PATCH 2/6] [FIX] web: preserve new column order after creation When a user creates a new column in a kanban view, leave the action and comes back, the order is not preserved. With this commit, we force a call to resequence to ensure that the order does not change. --- .../src/js/views/kanban/kanban_controller.js | 18 +++++++++++++++--- addons/web/static/tests/helpers/mock_server.js | 3 +++ addons/web/static/tests/views/kanban_tests.js | 12 +++++++++--- 3 files changed, 27 insertions(+), 6 deletions(-) diff --git a/addons/web/static/src/js/views/kanban/kanban_controller.js b/addons/web/static/src/js/views/kanban/kanban_controller.js index 11d17a78fa9..5cc2653d6cf 100644 --- a/addons/web/static/src/js/views/kanban/kanban_controller.js +++ b/addons/web/static/src/js/views/kanban/kanban_controller.js @@ -107,6 +107,16 @@ var KanbanController = BasicController.extend({ var groupedByM2o = groupByField && (groupByField.type === 'many2one'); return groupedByM2o; }, + /** + * @param {string[]} ids + * @private + * @returns {Deferred} + */ + _resequenceColumns: function (ids) { + var state = this.model.get(this.handle, {raw: true}); + var model = state.fields[state.groupedBy[0]].relation; + return this.model.resequence(model, ids, this.handle); + }, /** * This method calls the server to ask for a resequence. Note that this * does not rerender the user interface, because in most case, the @@ -156,6 +166,10 @@ var KanbanController = BasicController.extend({ _onAddColumn: function (event) { var self = this; this.model.createGroup(event.data.value, this.handle).then(function () { + var state = self.model.get(self.handle, {raw: true}); + var ids = _.pluck(state.data, 'res_id').filter(_.isNumber); + return self._resequenceColumns(ids); + }).then(function () { return self.update({}, {reload: false}); }).then(function () { self._updateButtons(); @@ -428,9 +442,7 @@ var KanbanController = BasicController.extend({ */ _onResequenceColumn: function (event) { var self = this; - var state = this.model.get(this.handle, {raw: true}); - var model = state.fields[state.groupedBy[0]].relation; - this.model.resequence(model, event.data.ids, this.handle).then(function () { + this._resequenceColumns(event.data.ids).then(function () { self._updateEnv(); }); }, diff --git a/addons/web/static/tests/helpers/mock_server.js b/addons/web/static/tests/helpers/mock_server.js index b7addb7cb45..5955bae6af7 100644 --- a/addons/web/static/tests/helpers/mock_server.js +++ b/addons/web/static/tests/helpers/mock_server.js @@ -921,6 +921,9 @@ var MockServer = Class.extend({ case '/web/dataset/search_read': return $.when(this._mockSearchReadController(args)); + + case '/web/dataset/resequence': + return $.when(); } if (route.indexOf('/web/image') >= 0 || _.contains(['.png', '.jpg'], route.substr(route.length - 4))) { return $.when(); diff --git a/addons/web/static/tests/views/kanban_tests.js b/addons/web/static/tests/views/kanban_tests.js index 190d94a6c7f..03a6e31ad8e 100644 --- a/addons/web/static/tests/views/kanban_tests.js +++ b/addons/web/static/tests/views/kanban_tests.js @@ -1130,7 +1130,7 @@ QUnit.module('Views', { }); QUnit.test('create a column in grouped on m2o', function (assert) { - assert.expect(13); + assert.expect(14); var nbRPCs = 0; var kanban = createView({ @@ -1149,6 +1149,11 @@ QUnit.module('Views', { if (args.method === 'name_create') { assert.ok(true, "should call name_create"); } + //Create column will call resequence to set column order + if (route === '/web/dataset/resequence') { + assert.ok(true, "should call resequence"); + return $.when(true); + } return this._super(route, args); }, }); @@ -1229,7 +1234,7 @@ QUnit.module('Views', { }); QUnit.test('delete a column in grouped on m2o', function (assert) { - assert.expect(32); + assert.expect(33); testUtils.patch(KanbanRenderer, { _renderGrouped: function () { @@ -2015,7 +2020,7 @@ QUnit.module('Views', { }); QUnit.test('archive new kanban column', function (assert) { - assert.expect(15); + assert.expect(16); this.data.partner.fields.active = {string: 'Active', type: 'char', default: true}; @@ -2044,6 +2049,7 @@ QUnit.module('Views', { kanban.$('.o_column_quick_create input').val('new colum'); kanban.$('.o_column_quick_create button.o_kanban_add').click(); rpcs.push('/web/dataset/call_kw/product/name_create'); + rpcs.push('/web/dataset/resequence'); assert.verifySteps(rpcs); // add a record inside From fcc6e9e3be6c7079c93e4476d0fe340235432aca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9ry=20Debongnie?= Date: Tue, 24 Apr 2018 10:22:24 +0200 Subject: [PATCH 3/6] [FIX] web: correct docstring (was showing incorrect type) This is my mistake, I added the docstring without properly checking the type. --- addons/web/static/src/js/views/kanban/kanban_controller.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/web/static/src/js/views/kanban/kanban_controller.js b/addons/web/static/src/js/views/kanban/kanban_controller.js index 5cc2653d6cf..7d3da850a7e 100644 --- a/addons/web/static/src/js/views/kanban/kanban_controller.js +++ b/addons/web/static/src/js/views/kanban/kanban_controller.js @@ -108,7 +108,7 @@ var KanbanController = BasicController.extend({ return groupedByM2o; }, /** - * @param {string[]} ids + * @param {number[]} ids * @private * @returns {Deferred} */ From 0351b492d55e297699a6881e10500d007bd7e10f Mon Sep 17 00:00:00 2001 From: qdp-odoo Date: Tue, 24 Apr 2018 10:45:57 +0200 Subject: [PATCH 4/6] [FIX] account: fix unreconciliation implying several exchange rate entries Use case fixed: manual reconciliation through the account.move.line list of items with several currencies, that will create a full reconcilation with several exchange rate entries to balance the amounts in all foreign currencies. Unreconciling these lines was wrongly creating an account.move with a lot of lines with debit = credit = amount_currency = 0. --- addons/account/models/account_move.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/addons/account/models/account_move.py b/addons/account/models/account_move.py index 727bdf298ae..11f83b3fd36 100644 --- a/addons/account/models/account_move.py +++ b/addons/account/models/account_move.py @@ -1135,13 +1135,15 @@ class AccountMoveLine(models.Model): #create an empty move that will hold all the exchange rate adjustments exchange_move = False - if aml_to_balance_currency: + if aml_to_balance_currency and any([residual for dummy, residual in aml_to_balance_currency.values()]): exchange_move = self.env['account.move'].create( self.env['account.full.reconcile']._prepare_exchange_diff_move(move_date=maxdate, company=self[0].company_id)) for currency, values in aml_to_balance_currency.items(): aml_to_balance = values[0] total_amount_currency = values[1] + if not total_amount_currency: + continue #eventually create journal entries to book the difference due to foreign currency's exchange rate that fluctuates aml_recs, partial_recs = self.env['account.partial.reconcile'].create_exchange_rate_entry(aml_to_balance, 0.0, total_amount_currency, currency, exchange_move) From b9ce1affbf55bb81dde718af435e4763bd19c80c Mon Sep 17 00:00:00 2001 From: qdp-odoo Date: Tue, 24 Apr 2018 12:10:06 +0200 Subject: [PATCH 5/6] [REV] account: reverts commit 0351b492d55e297699a6881e10500d007bd7e10f. Unexpected runbot failure detected. Pach reverted for further investigation. Sorry for the disturbance --- addons/account/models/account_move.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/addons/account/models/account_move.py b/addons/account/models/account_move.py index 11f83b3fd36..727bdf298ae 100644 --- a/addons/account/models/account_move.py +++ b/addons/account/models/account_move.py @@ -1135,15 +1135,13 @@ class AccountMoveLine(models.Model): #create an empty move that will hold all the exchange rate adjustments exchange_move = False - if aml_to_balance_currency and any([residual for dummy, residual in aml_to_balance_currency.values()]): + if aml_to_balance_currency: exchange_move = self.env['account.move'].create( self.env['account.full.reconcile']._prepare_exchange_diff_move(move_date=maxdate, company=self[0].company_id)) for currency, values in aml_to_balance_currency.items(): aml_to_balance = values[0] total_amount_currency = values[1] - if not total_amount_currency: - continue #eventually create journal entries to book the difference due to foreign currency's exchange rate that fluctuates aml_recs, partial_recs = self.env['account.partial.reconcile'].create_exchange_rate_entry(aml_to_balance, 0.0, total_amount_currency, currency, exchange_move) From 8d17a2ae557aeddd68dd2fe78b6c052bbcc183c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9ry=20Debongnie?= Date: Tue, 24 Apr 2018 12:33:50 +0200 Subject: [PATCH 6/6] [FIX] base: do not read settings record again The settings view is supposed to open a new record on the model res.config.settings, perform the default_get and onchange calls, then display the result. However, once this is done, if the user then clicked on some links (which do save the current record and navigate to another action), then click on the breadcrumbs to come back to the settings view, then the settings view will reload the current record, which has an id, and this also will update the url. This could be a problem. For example, in a multicompany settings, reloading a settings view from an id will reload the settings for a given company, even if the user changed its current company. To fix this issue, we make sure the basic model for the settings view 'forgets' the res_id key, so it will be compelled to actually reread a new settings form record. --- .../base/static/src/js/res_config_settings.js | 19 ++++++ .../static/src/tests/base_setting_test.js | 58 ++++++++++++++++++- 2 files changed, 76 insertions(+), 1 deletion(-) diff --git a/odoo/addons/base/static/src/js/res_config_settings.js b/odoo/addons/base/static/src/js/res_config_settings.js index 8285133c64c..041e6494f64 100644 --- a/odoo/addons/base/static/src/js/res_config_settings.js +++ b/odoo/addons/base/static/src/js/res_config_settings.js @@ -1,6 +1,7 @@ odoo.define('base.settings', function (require) { "use strict"; +var BasicModel = require('web.BasicModel'); var core = require('web.core'); var config = require('web.config'); var FormView = require('web.FormView'); @@ -329,8 +330,25 @@ var BaseSettingController = FormController.extend({ }, }); +var BaseSettingsModel = BasicModel.extend({ + /** + * @override + */ + save: function (recordID) { + var self = this; + return this._super.apply(this, arguments).then(function (result) { + // we remove here the res_id, because the record should still be + // considered new. We want the web client to always perform a + // default_get to fetch the settings anew. + delete self.localData[recordID].res_id; + return result; + }); + }, +}); + var BaseSettingView = FormView.extend({ config: _.extend({}, FormView.prototype.config, { + Model: BaseSettingsModel, Renderer: BaseSettingRenderer, Controller: BaseSettingController, }), @@ -343,6 +361,7 @@ var BaseSettingView = FormView.extend({ view_registry.add('base_settings', BaseSettingView); return { + Model: BaseSettingsModel, Renderer: BaseSettingRenderer, Controller: BaseSettingController, }; diff --git a/odoo/addons/base/static/src/tests/base_setting_test.js b/odoo/addons/base/static/src/tests/base_setting_test.js index 10a00e2713c..3964751a538 100644 --- a/odoo/addons/base/static/src/tests/base_setting_test.js +++ b/odoo/addons/base/static/src/tests/base_setting_test.js @@ -8,6 +8,8 @@ var view_registry = require('web.view_registry'); var createView = testUtils.createView; var BaseSettingsView = view_registry.get('base_settings'); +var createActionManager = testUtils.createActionManager; + BaseSetting.Renderer.include({ _getAppIconUrl: function() { @@ -91,5 +93,59 @@ QUnit.module('base_settings_tests', { assert.strictEqual(form.$('.notFound').hasClass('o_hidden'),false,"record not found message shown"); form.destroy(); }); + + QUnit.test('settings views does not read existing id when coming back in breadcrumbs', function (assert) { + assert.expect(7); + + var actions = [{ + id: 1, + name: 'Settings view', + res_model: 'project', + type: 'ir.actions.act_window', + views: [[1, 'form']], + }, { + id: 4, + name: 'Other action', + res_model: 'project', + type: 'ir.actions.act_window', + views: [[2, 'list']], + }]; + var archs = { + 'project,1,form': '
' + + '
' + + '
' + + '
', + 'project,2,list': '', + 'project,false,search': '', + }; + + var actionManager = createActionManager({ + actions: actions, + archs: archs, + data: this.data, + mockRPC: function (route, args) { + if (args.method) { + assert.step(args.method); + } + return this._super.apply(this, arguments); + }, + }); + + actionManager.doAction(1); + actionManager.$('button[name="4"]').click(); + $('.o_control_panel .breadcrumb li a').click(); + assert.verifySteps([ + 'load_views', // initial setting action + 'default_get', // this is a setting view => create new record + 'create', // when we click on action button => save + 'read', // with save, we have a reload... (not necessary actually) + 'load_views', // for other action in breadcrumb, + // with a searchread (not shown here since it is a route) + 'default_get', // when we come back, we want to restart from scratch + ]); + + actionManager.destroy(); + }); +}); }); -}); \ No newline at end of file