[FIX] web: properly transfer field properties
Before this commit, the field properties (attrs, fieldNames, fields) were not correctly given to sub views in many case expected: * use widget many2many on one2many field * create record when click on save in formDialog * do not send create command to the server * use the fields defined in the field node (instead of tree view fields) * use the fieldNames instead of the record to get field values
This commit is contained in:
committed by
Aaron Bohy
parent
53d8d38f93
commit
454b215389
@@ -302,6 +302,9 @@ var BasicModel = AbstractModel.extend({
|
||||
|
||||
for (var fieldName in element.data) {
|
||||
field = element.fields[fieldName];
|
||||
if (!field) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// get relational datapoint
|
||||
if (field.type === 'many2one') {
|
||||
@@ -842,18 +845,32 @@ var BasicModel = AbstractModel.extend({
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Set and return the fieldattrs on a record element.
|
||||
* Set fieldNames.
|
||||
* Set fields on a record element (current fields as defaults)
|
||||
* Set fieldattrs.
|
||||
* It is mostly useful for the cases where a record element is shared
|
||||
* between various views, such as a one2many with a tree and a form view.
|
||||
*
|
||||
* @param {string} recordID a valid element ID
|
||||
* @param {Object} attrs the new field attrs
|
||||
* @return {Object} the previous field attrs
|
||||
* @param {Object} props
|
||||
* @param {Object} props.fieldNames
|
||||
* @param {Object} props.fields
|
||||
* @param {Object} props.fieldAttrs
|
||||
* @return {Object}
|
||||
* @return {Object} result.fieldNames the previous field names
|
||||
* @return {Object} result.fields the previous description fields
|
||||
* @return {Object} result.fieldattrs the previous field attrs
|
||||
*/
|
||||
setFieldAttrs: function (recordID, attrs) {
|
||||
setFieldProps: function (recordID, props) {
|
||||
var record = this.localData[recordID];
|
||||
var result = record.fieldAttrs;
|
||||
record.fieldAttrs = attrs;
|
||||
var result = {
|
||||
fieldNames: record.fieldNames,
|
||||
fields: record.fields,
|
||||
fieldAttrs: record.fieldAttrs,
|
||||
};
|
||||
record.fieldNames = props.fieldNames;
|
||||
record.fields = _.defaults(props.fields, record.fields);
|
||||
record.fieldAttrs = props.fieldAttrs;
|
||||
return result;
|
||||
},
|
||||
/**
|
||||
@@ -2100,6 +2117,9 @@ var BasicModel = AbstractModel.extend({
|
||||
if (element.type === 'record') {
|
||||
for (var fieldName in element.data) {
|
||||
var field = element.fields[fieldName];
|
||||
if (!field) {
|
||||
continue;
|
||||
}
|
||||
if (_.contains(['one2many', 'many2one', 'many2many'], field.type)) {
|
||||
var relationalElement = this.localData[element.data[fieldName]];
|
||||
|
||||
|
||||
@@ -63,24 +63,28 @@ return AbstractRenderer.extend({
|
||||
* @returns {Object}
|
||||
*/
|
||||
_getFieldValues: function (record) {
|
||||
return _.mapObject(record.data, function (value, name) {
|
||||
var result = {};
|
||||
_.each(record.fieldNames, function (name) {
|
||||
var field = record.fields[name];
|
||||
var value = record.data[name];
|
||||
result[name] = value;
|
||||
if (field.type === 'many2one') {
|
||||
return value.data ? value.data.id || false : false;
|
||||
result[name] = value.data ? value.data.id || false : false;
|
||||
}
|
||||
if (field.type === 'one2many' || field.type === 'many2many') {
|
||||
if (value) {
|
||||
return _.pluck(value.data, 'res_id');
|
||||
result[name] = _.pluck(value.data, 'res_id');
|
||||
} else {
|
||||
return [];
|
||||
result[name] = [];
|
||||
}
|
||||
}
|
||||
if (field.type === 'boolean') {
|
||||
// we want an explicit false value, not null if it is unset
|
||||
return value || false;
|
||||
result[name] = value || false;
|
||||
}
|
||||
return value;
|
||||
});
|
||||
result.id = record.data.id;
|
||||
return result;
|
||||
},
|
||||
/**
|
||||
* Render the view
|
||||
|
||||
@@ -141,7 +141,7 @@ var FormViewDialog = ViewDialog.extend({
|
||||
*/
|
||||
destroy: function () {
|
||||
if (this.recordID && this.fieldAttrs) {
|
||||
this.model.setFieldAttrs(this.recordID, this.fieldAttrs);
|
||||
this.setFieldProps(this.recordID, this.previousFieldProps);
|
||||
}
|
||||
this._super.apply(this, arguments);
|
||||
},
|
||||
@@ -168,8 +168,11 @@ var FormViewDialog = ViewDialog.extend({
|
||||
|
||||
fields_view_def.then(function (fields_view) {
|
||||
if (self.recordID) {
|
||||
self.fieldAttrs =
|
||||
self.model.setFieldAttrs(self.recordID, fields_view.fieldAttrs);
|
||||
self.previousFieldProps = self.model.setFieldProps(self.recordID, {
|
||||
fieldNames: Object.keys(fields_view.fields),
|
||||
fields: fields_view.fields,
|
||||
fieldAttrs: fields_view.fieldAttrs
|
||||
});
|
||||
}
|
||||
var formview = new FormView(fields_view.arch, fields_view.fields, {
|
||||
modelName: self.res_model,
|
||||
|
||||
@@ -20,6 +20,7 @@ QUnit.module('relational_fields', {
|
||||
int_field: {string: "int_field", type: "integer", sortable: true},
|
||||
qux: {string: "Qux", type: "float", digits: [16,1] },
|
||||
p: {string: "one2many field", type: "one2many", relation: 'partner', relation_field: 'trululu'},
|
||||
turtles: {string: "one2many turtle field", type: "one2many", relation: 'turtle'},
|
||||
trululu: {string: "Trululu", type: "many2one", relation: 'partner'},
|
||||
timmy: { string: "pokemon", type: "many2many", relation: 'partner_type'},
|
||||
product_id: {string: "Product", type: "many2one", relation: 'product'},
|
||||
@@ -39,6 +40,7 @@ QUnit.module('relational_fields', {
|
||||
int_field: 10,
|
||||
qux: 0.44,
|
||||
p: [],
|
||||
turtles: [2],
|
||||
timmy: [],
|
||||
trululu: 4,
|
||||
}, {
|
||||
@@ -83,6 +85,36 @@ QUnit.module('relational_fields', {
|
||||
{id: 14, display_name: "silver", color: 5},
|
||||
]
|
||||
},
|
||||
turtle: {
|
||||
fields: {
|
||||
display_name: { string: "Displayed name", type: "char" },
|
||||
turtle_foo: {string: "Foo", type: "char", default: "My little Foo Value"},
|
||||
turtle_bar: {string: "Bar", type: "boolean", default: true},
|
||||
turtle_int: {string: "int", type: "integer", sortable: true, required: true},
|
||||
turtle_qux: {string: "Qux", type: "float", digits: [16,1], required: true},
|
||||
turtle_trululu: {string: "Trululu", type: "many2one", relation: 'partner', required: true, default: 1},
|
||||
product_id: {string: "Product", type: "many2one", relation: 'product', required: true},
|
||||
},
|
||||
records: [{
|
||||
id: 1,
|
||||
display_name: "leonardo",
|
||||
turtle_bar: true,
|
||||
turtle_foo: "yop",
|
||||
}, {
|
||||
id: 2,
|
||||
display_name: "donatello",
|
||||
turtle_bar: true,
|
||||
turtle_foo: "blip",
|
||||
turtle_int: 9,
|
||||
}, {
|
||||
id: 3,
|
||||
display_name: "raphael",
|
||||
turtle_bar: false,
|
||||
turtle_foo: "kawa",
|
||||
turtle_int: 21,
|
||||
turtle_qux: 9.8,
|
||||
}],
|
||||
}
|
||||
};
|
||||
}
|
||||
}, function () {
|
||||
@@ -1573,42 +1605,44 @@ QUnit.module('relational_fields', {
|
||||
form.destroy();
|
||||
});
|
||||
|
||||
QUnit.test('one2many with many2many widget', function (assert) {
|
||||
assert.expect(9);
|
||||
|
||||
this.data.partner.records[0].p = [2];
|
||||
QUnit.test('one2many with many2many widget: create', function (assert) {
|
||||
assert.expect(10);
|
||||
|
||||
var form = createView({
|
||||
View: FormView,
|
||||
model: 'partner',
|
||||
data: this.data,
|
||||
arch:'<form string="Partners">' +
|
||||
'<field name="p" widget="many2many">' +
|
||||
'<field name="turtles" widget="many2many">' +
|
||||
'<tree>' +
|
||||
'<field name="foo"/>' +
|
||||
'<field name="int_field"/>' +
|
||||
'<field name="turtle_foo"/>' +
|
||||
'<field name="turtle_qux"/>' +
|
||||
'<field name="turtle_int"/>' +
|
||||
'<field name="product_id"/>' +
|
||||
'</tree>' +
|
||||
'<form>' +
|
||||
'<group>' +
|
||||
'<field name="foo"/>' +
|
||||
'<field name="bar"/>' +
|
||||
'<field name="int_field"/>' +
|
||||
'<field name="turtle_foo"/>' +
|
||||
'<field name="turtle_bar"/>' +
|
||||
'<field name="turtle_int"/>' +
|
||||
'<field name="product_id"/>' +
|
||||
'</group>' +
|
||||
'</form>' +
|
||||
'</field>' +
|
||||
'</form>',
|
||||
archs: {
|
||||
'partner,false,list': '<tree><field name="foo"/><field name="bar"/><field name="product_id"/></tree>',
|
||||
'partner,false,search': '<search><field name="foo"/><field name="bar"/><field name="product_id"/></search>',
|
||||
'turtle,false,list': '<tree><field name="display_name"/><field name="turtle_foo"/><field name="turtle_bar"/><field name="product_id"/></tree>',
|
||||
'turtle,false,search': '<search><field name="turtle_foo"/><field name="turtle_bar"/><field name="product_id"/></search>',
|
||||
},
|
||||
session: {},
|
||||
res_id: 1,
|
||||
mockRPC: function (route, args) {
|
||||
if (route === '/web/dataset/call_kw/turtle/create') {
|
||||
assert.ok(args.args, "should write on the turtle record");
|
||||
}
|
||||
if (route === '/web/dataset/call_kw/partner/write') {
|
||||
assert.strictEqual(args.args[0][0], 1, "should write on the partner record 1");
|
||||
assert.strictEqual(args.args[1].p[0][0], 6, "should send only a 'replace with' command");
|
||||
assert.strictEqual(args.args[1].turtles[0][0], 6, "should send only a 'replace with' command");
|
||||
}
|
||||
return this._super.apply(this, arguments);
|
||||
},
|
||||
@@ -1630,8 +1664,8 @@ QUnit.module('relational_fields', {
|
||||
"sould have 1 record in the select view");
|
||||
|
||||
$('.modal-footer button:eq(1)').click();
|
||||
$('.modal input.o_form_field[name="foo"]').val('tototo').trigger('input');
|
||||
$('.modal input.o_form_field[name="int_field"]').val(50).trigger('input');
|
||||
$('.modal input.o_form_field[name="turtle_foo"]').val('tototo').trigger('input');
|
||||
$('.modal input.o_form_field[name="turtle_int"]').val(50).trigger('input');
|
||||
var $many2one = $('.modal [name="product_id"] input').click();
|
||||
var $dropdown = $many2one.autocomplete('widget');
|
||||
$dropdown.find('li:first a').mouseenter();
|
||||
@@ -1643,7 +1677,7 @@ QUnit.module('relational_fields', {
|
||||
|
||||
assert.strictEqual(form.$('.o_data_row').length, 3,
|
||||
"sould have 3 records in one2many list");
|
||||
assert.strictEqual(form.$('.o_data_row').text(), "blip9xphoneyop10tototo50xphone",
|
||||
assert.strictEqual(form.$('.o_data_row').text(), "blip0.09yop0.0tototo0.050xphone",
|
||||
"sould display the record values in one2many list");
|
||||
|
||||
$('.o_form_button_save').click();
|
||||
@@ -1651,6 +1685,83 @@ QUnit.module('relational_fields', {
|
||||
form.destroy();
|
||||
});
|
||||
|
||||
QUnit.test('one2many with many2many widget: edition', function (assert) {
|
||||
assert.expect(6);
|
||||
|
||||
var form = createView({
|
||||
View: FormView,
|
||||
model: 'partner',
|
||||
data: this.data,
|
||||
arch:'<form string="Partners">' +
|
||||
'<field name="turtles" widget="many2many">' +
|
||||
'<tree>' +
|
||||
'<field name="turtle_foo"/>' +
|
||||
'<field name="turtle_qux"/>' +
|
||||
'<field name="turtle_int"/>' +
|
||||
'<field name="product_id"/>' +
|
||||
'</tree>' +
|
||||
'<form>' +
|
||||
'<group>' +
|
||||
'<field name="turtle_foo"/>' +
|
||||
'<field name="turtle_bar"/>' +
|
||||
'<field name="turtle_int"/>' +
|
||||
'<field name="product_id"/>' +
|
||||
'</group>' +
|
||||
'</form>' +
|
||||
'</field>' +
|
||||
'</form>',
|
||||
archs: {
|
||||
'turtle,false,list': '<tree><field name="display_name"/><field name="turtle_foo"/><field name="turtle_bar"/><field name="product_id"/></tree>',
|
||||
'turtle,false,search': '<search><field name="turtle_foo"/><field name="turtle_bar"/><field name="product_id"/></search>',
|
||||
},
|
||||
session: {},
|
||||
res_id: 1,
|
||||
mockRPC: function (route, args) {
|
||||
if (route === '/web/dataset/call_kw/turtle/write') {
|
||||
assert.strictEqual(args.args[0].length, 1, "should write on the turtle record");
|
||||
assert.deepEqual(args.args[1], {"product_id":37}, "should write only the product_id on the turtle record");
|
||||
}
|
||||
if (route === '/web/dataset/call_kw/partner/write') {
|
||||
assert.strictEqual(args.args[0][0], 1, "should write on the partner record 1");
|
||||
assert.strictEqual(args.args[1].turtles[0][0], 6, "should send only a 'replace with' command");
|
||||
}
|
||||
return this._super.apply(this, arguments);
|
||||
},
|
||||
});
|
||||
|
||||
form.$('.o_data_row:first').click();
|
||||
$('.modal .o_form_button_cancel').click();
|
||||
form.$buttons.find('.o_form_button_edit').click();
|
||||
|
||||
// edit the first one2many record
|
||||
form.$('.o_data_row:first').click();
|
||||
var $many2one = $('.modal [name="product_id"] input').click();
|
||||
var $dropdown = $many2one.autocomplete('widget');
|
||||
$dropdown.find('li:first a').mouseenter();
|
||||
$dropdown.find('li:first a').click();
|
||||
$('.modal-footer button:first').click();
|
||||
|
||||
$('.o_form_button_save').click(); // don't save anything because the one2many does not change
|
||||
|
||||
// add a one2many record
|
||||
form.$buttons.find('.o_form_button_edit').click();
|
||||
form.$('.o_form_field_x2many_list_row_add a').click();
|
||||
$('.modal .o_data_row:first .o_list_record_selector input').click();
|
||||
$('.modal .o_select_button').click();
|
||||
|
||||
// edit the second one2many record
|
||||
form.$('.o_data_row:eq(1)').click();
|
||||
$many2one = $('.modal [name="product_id"] input').click();
|
||||
$dropdown = $many2one.autocomplete('widget');
|
||||
$dropdown.find('li:first a').mouseenter();
|
||||
$dropdown.find('li:first a').click();
|
||||
$('.modal-footer button:first').click();
|
||||
|
||||
$('.o_form_button_save').click();
|
||||
|
||||
form.destroy();
|
||||
});
|
||||
|
||||
QUnit.test('new record, the context is properly evaluated and sent', function (assert) {
|
||||
assert.expect(2);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user