[FIX] fields, web: improve form onchange to x2many
In a form view, when a field onchange lead to a change on a x2many, there was two different behavior: - if the x2many had an embedded view (eg. a tree view inside a form view) the onchange would notify that it expected the x2many field in this embedded view to be changed and handled the changes correctly. - if the x2many had a default view, the onchange ORM would not be aware the x2many could be modified and would not sent the changes back causing blank or not updated x2m lines and error on save. --- Two solutions were birthed to solve the second point: => PR #10557 = solving everything With this PR the onchange in the ORM is aware of every fields in the current view (even field in a x2m in a x2m in a x2m in a form view) and if any of these are change the javascript gets back the value of the fields present in the view. This PR has currently not been merged by fear of changing too much and anyway could only be done in master. => PR #12249 = if no field for x2many, send its form view fields With this change, if the ORM onchange is not aware of the fields in the x2many widget to returns, all the field in the x2m default form view are returned. This was merged inbbdf960but introduced a number of other issue: - in most situation the x2many is represented by a list view, which may have fields missing of the form view, so the original is still present. - the view used may differ from the default form view in other way (depending on value in context or other possibilities). - the form view could have fields not present in the form view which could end up in `write` on fields which should not be written to. --- This commit revertsbbdf960and adapts a small part of #10557 so the x2many with default view works as an embedded x2many. For more than one level (eg. a x2many in a x2many) this would still not work but it is only solvable by a PR such as #10557 which could only be targetted for master. With this commit: - the list of fields sent to ORM onchange is computed at the first onchange - the fields from a x2many field default view is sent for onchange - the initial onchange on record creation is delayed to when x2many are loaded closes #12249, closes #15336, closes #15890 fixes #11236, fixes #12249, fixes #15129, #15419 opw-705965 opw-716095 opw-715619 opw-710440
This commit is contained in:
@@ -55,7 +55,6 @@ var FormView = View.extend(common.FieldManagerMixin, {
|
||||
this.fields = {};
|
||||
this.fields_order = [];
|
||||
this.datarecord = {};
|
||||
this._onchange_specs = {};
|
||||
this.onchanges_mutex = new utils.Mutex();
|
||||
this.default_focus_field = null;
|
||||
this.default_focus_button = null;
|
||||
@@ -75,7 +74,6 @@ var FormView = View.extend(common.FieldManagerMixin, {
|
||||
this.rendering_engine = new FormRenderingEngine(this);
|
||||
this.set({actual_mode: this.options.initial_mode});
|
||||
this.has_been_loaded.done(function() {
|
||||
self._build_onchange_specs();
|
||||
self.on("change:actual_mode", self, self.toggle_buttons);
|
||||
self.on("change:actual_mode", self, self.toggle_sidebar);
|
||||
});
|
||||
@@ -318,8 +316,11 @@ var FormView = View.extend(common.FieldManagerMixin, {
|
||||
this.update_pager(); // the mode must be actualized before updating the pager
|
||||
return $.when.apply(null, set_values).then(function() {
|
||||
if (!record.id) {
|
||||
// trigger onchanges
|
||||
self.do_onchange(null);
|
||||
// trigger onchange for new record after x2many with non-embedded views are loaded
|
||||
var fields_loaded = _.pluck(self.fields, 'is_loaded');
|
||||
$.when.apply(null, fields_loaded).done(function() {
|
||||
self.do_onchange(null);
|
||||
});
|
||||
}
|
||||
self.on_form_changed();
|
||||
self.rendering_engine.init_fields().then(function() {
|
||||
@@ -333,7 +334,7 @@ var FormView = View.extend(common.FieldManagerMixin, {
|
||||
} else {
|
||||
self.do_push_state({});
|
||||
}
|
||||
self.$el.removeClass('oe_form_dirty');
|
||||
self.$el.removeClass('oe_form_dirty');
|
||||
});
|
||||
});
|
||||
},
|
||||
@@ -381,7 +382,24 @@ var FormView = View.extend(common.FieldManagerMixin, {
|
||||
_.each(this.fields, function(field, name) {
|
||||
self._onchange_fields.push(name);
|
||||
self._onchange_specs[name] = find(name, field.node);
|
||||
_.each(field.field.views, function(view) {
|
||||
|
||||
// we get the list of first-level fields of x2many firstly by
|
||||
// getting them from the field embedded views, then if no embedded
|
||||
// view is present for a loaded view, we get them from the default
|
||||
// view that has been loaded
|
||||
|
||||
// gather embedded view objects
|
||||
var views = _.clone(field.field.views);
|
||||
// also gather default view objects
|
||||
if (field.viewmanager) {
|
||||
_.each(field.viewmanager.views, function(view, view_type) {
|
||||
// add default view if it was not embedded and it is loaded
|
||||
if (views[view_type] === undefined && view.controller) {
|
||||
views[view_type] = view.controller.fields_view;
|
||||
}
|
||||
});
|
||||
}
|
||||
_.each(views, function(view) {
|
||||
_.each(view.fields, function(_, subname) {
|
||||
self._onchange_specs[name + '.' + subname] = find(subname, view.arch);
|
||||
});
|
||||
@@ -410,6 +428,9 @@ var FormView = View.extend(common.FieldManagerMixin, {
|
||||
|
||||
do_onchange: function(widget) {
|
||||
var self = this;
|
||||
if (self._onchange_specs === undefined) {
|
||||
self._build_onchange_specs();
|
||||
}
|
||||
var onchange_specs = self._onchange_specs;
|
||||
try {
|
||||
var def = $.when({});
|
||||
|
||||
@@ -307,12 +307,7 @@ class TestOnChange(common.TransactionCase):
|
||||
|
||||
# When one2many domain contains non-computed field, things are ok
|
||||
self.assertEqual(result['value']['important_messages'],
|
||||
[(5,)] + [(1, msg.id, {
|
||||
'name': msg.name,
|
||||
'body': msg.body,
|
||||
'author': (msg.author.id, msg.author.display_name),
|
||||
'size': msg.size
|
||||
}) for msg in discussion.important_messages])
|
||||
[(5,)] + [(4, msg.id) for msg in discussion.important_messages])
|
||||
|
||||
# But here with commit 5676d81, we get value of: [(2, email.id)]
|
||||
self.assertEqual(
|
||||
|
||||
+1
-1
@@ -2133,7 +2133,7 @@ class One2many(_RelationalMulti):
|
||||
_description_relation_field = property(attrgetter('inverse_name'))
|
||||
|
||||
def convert_to_onchange(self, value, record, fnames=()):
|
||||
fnames = set(fnames or value.fields_view_get()['fields'])
|
||||
fnames = set(fnames or ())
|
||||
fnames.discard(self.inverse_name)
|
||||
return super(One2many, self).convert_to_onchange(value, record, fnames)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user