[FIX] web: properly reload record data before doing another action

When a user edit a record, then click on a stat button, the web client was a
little too aggressive in avoiding to reload data.  It was assuming that
even if the server overrides the create method, the web client did not
need to know anything about the record before executing the action from
the stat button.

However, that assumption is incorrect: the display_name property is
needed to correctly display the breadcrumbs.  For example, here is a scenario
that was not correct: open a res.partner in a form view, edit its name, then
click on one of the stat button. You will see that the breadcrumb is not
properly updated.

To fix this issue, we have to reload the record, and to properly update
the title property.  In this commit, we also add a getName method in the
model.
This commit is contained in:
Géry Debongnie
2017-07-14 14:47:13 +02:00
parent 04c3b99cb7
commit 29e4394af9
3 changed files with 80 additions and 19 deletions
@@ -84,11 +84,14 @@ odoo.define('web.BasicModel', function (require) {
var AbstractModel = require('web.AbstractModel');
var concurrency = require('web.concurrency');
var core = require('web.core');
var Context = require('web.Context');
var Domain = require('web.Domain');
var fieldUtils = require('web.field_utils');
var session = require('web.session');
var _t = core._t;
var x2ManyCommands = {
// (0, _, {values})
CREATE: 0,
@@ -420,6 +423,22 @@ var BasicModel = AbstractModel.extend({
this._sortList(list);
return list;
},
/**
* Returns the current display_name for the record.
*
* @param {string} id the localID for a valid record element
* @returns {string}
*/
getName: function (id) {
var record = this.localData[id];
if (record._changes && 'display_name' in record._changes) {
return record._changes.display_name;
}
if ('display_name' in record.data) {
return record.data.display_name;
}
return _t("New");
},
/**
* Returns true if a record is dirty. A record is considered dirty if it has
* some unsaved changes, marked by the _isDirty property on the record or
@@ -87,8 +87,7 @@ var FormController = BasicController.extend({
* @returns {string}
*/
getTitle: function () {
var dataPoint = this.model.get(this.handle, {raw: true});
return dataPoint.data.display_name || _t('New');
return this.model.getName(this.handle);
},
/**
* Called each time the form view is attached into the DOM
@@ -183,13 +182,15 @@ var FormController = BasicController.extend({
* @override
*/
saveRecord: function () {
var result = this._super.apply(this, arguments);
if (_t.database.multi_lang) {
var self = this;
result.then(function (changedFields) {
if (!changedFields.length) {
return changedFields;
}
var self = this;
return this._super.apply(this, arguments).then(function (changedFields) {
// the title could have been changed
self.set('title', self.getTitle());
self._updateEnv();
if (_t.database.multi_lang && changedFields.length) {
// need to make sure changed fields that should be translated
// are displayed with an alert
var fields = self.renderer.state.fields;
var alertFields = [];
for (var k = 0; k < changedFields.length; k++) {
@@ -201,9 +202,9 @@ var FormController = BasicController.extend({
if (alertFields.length) {
self.renderer.displayTranslationAlert(alertFields);
}
});
}
return result;
}
return changedFields;
});
},
//--------------------------------------------------------------------------
@@ -370,7 +371,6 @@ var FormController = BasicController.extend({
// save the record but don't switch to readonly mode
def = this.saveRecord(this.handle, {
stayInEdit: true,
reload: false,
}).then(function () {
// we need to reget the record to make sure we have changes made
// by the basic model, such as the new res_id, if the record is
+48 -6
View File
@@ -920,7 +920,7 @@ QUnit.module('Views', {
QUnit.test('buttons in form view, new record', function (assert) {
// this simulates a situation similar to the settings forms.
assert.expect(6);
assert.expect(7);
var resID;
@@ -960,12 +960,12 @@ QUnit.module('Views', {
});
form.$('.o_form_statusbar button.p').click();
assert.verifySteps(['default_get', 'create', 'execute_action', 'read']);
assert.verifySteps(['default_get', 'create', 'read', 'execute_action', 'read']);
form.destroy();
});
QUnit.test('buttons in form view, new record, with field id in view', function (assert) {
assert.expect(6);
assert.expect(7);
// buttons in form view are one of the rare example of situation when we
// save a record without reloading it immediately, because we only care
// about its id for the next step. But at some point, if the field id
@@ -1010,7 +1010,7 @@ QUnit.module('Views', {
});
form.$('.o_form_statusbar button.p').click();
assert.verifySteps(['default_get', 'create', 'execute_action', 'read']);
assert.verifySteps(['default_get', 'create', 'read', 'execute_action', 'read']);
form.destroy();
});
@@ -1521,7 +1521,7 @@ QUnit.module('Views', {
});
QUnit.test('clicking on stat buttons in edit mode', function (assert) {
assert.expect(8);
assert.expect(9);
var form = createView({
View: FormView,
@@ -1566,7 +1566,49 @@ QUnit.module('Views', {
assert.strictEqual(form.mode, "edit", "form view should be in edit mode");
assert.strictEqual(count, 2, "should have triggered a execute action");
assert.verifySteps(['read', 'write']);
assert.verifySteps(['read', 'write', 'read']);
form.destroy();
});
QUnit.test('clicking on stat buttons save and reload in edit mode', function (assert) {
assert.expect(2);
var form = createView({
View: FormView,
model: 'partner',
data: this.data,
arch:'<form string="Partners">' +
'<sheet>' +
'<div name="button_box">' +
'<button class="oe_stat_button" type="action">' +
'<field name="int_field" widget="statinfo" string="Some number"/>' +
'</button>' +
'</div>' +
'<group>' +
'<field name="name"/>' +
'</group>' +
'</sheet>' +
'</form>',
res_id: 2,
mockRPC: function (route, args) {
if (args.method === 'write') {
// simulate an override of the model...
args.args[1].display_name = "GOLDORAK";
args.args[1].name = "GOLDORAK";
}
return this._super.apply(this, arguments);
},
});
assert.strictEqual(form.getTitle(), 'second record',
"should have correct display_name");
form.$buttons.find('.o_form_button_edit').click();
form.$('input[name="name"]').val('some other name').trigger('input');
form.$('.oe_stat_button').first().click();
assert.strictEqual(form.getTitle(), 'GOLDORAK',
"should have correct display_name");
form.destroy();
});