diff --git a/addons/web/static/src/js/views/form_relational_widgets.js b/addons/web/static/src/js/views/form_relational_widgets.js index 4876440e4f6..17f6cd842c9 100644 --- a/addons/web/static/src/js/views/form_relational_widgets.js +++ b/addons/web/static/src/js/views/form_relational_widgets.js @@ -16,7 +16,7 @@ var ViewManager = require('web.ViewManager'); var _t = core._t; var QWeb = core.qweb; -var commands = common.commands; +var COMMANDS = common.commands; var list_widget_registry = core.list_widget_registry; var M2ODialog = Dialog.extend({ @@ -498,103 +498,222 @@ var AddAnItemList = ListView.List.extend({ /** * A Abstract field for one2many and many2many field - * init: add dataset - * set_value: compute comands list into ids + * For all fields on2many or many2many: + * - this.get('value') contains a list of ids and virtual ids + * - get_value() return an odoo write command list */ var AbstractManyField = common.AbstractField.extend({ init: function(field_manager, node) { var self = this; this._super(field_manager, node); - this.dataset = new One2ManyDataSet(this, this.field.relation); + this.dataset = new One2ManyDataSet(this, this.field.relation, this.build_context()); this.dataset.o2m = this; this.dataset.parent_view = this.view; this.dataset.child_name = this.name; + this.set('value', []); + this.starting_ids = []; + this.view.on("load_record", this, function () { + self.starting_ids = self.get('value'); + }); this.dataset.on('dataset_changed', this, function() { - self.dataset_changed(); + // the editable lists change the dataset without call AbstractManyField methods + if (!this.internal_dataset_changed) { + this.trigger("change:commands"); + } + }); + this.on("change:commands", this, function () { + this.set({'value': this.dataset.ids.slice()}); }); }, - dataset_changed: function() { - this.trigger('changed_value'); - }, - /** - * Set value use command to convert tuple of commands in id list - */ - set_value: function(value_) { - this.dataset.reset_ids([]); - var ids = this.get_ids_from_command(value_); - this._super(ids); + + set_value: function(ids) { + ids = (ids || []).slice(); + if (_.find(ids, function(id) { return typeof(id) === "string"; } )) { + throw new Error("set_value of '"+this.name+"' must receive an list of ids without virtual ids.", ids); + } + if (_.find(ids, function(id) { return typeof(id) !== "number"; } )) { + return this.send_commands(ids); + } this.dataset.reset_ids(ids); - if (this.dataset.index === null && this.dataset.ids.length > 0) { - this.dataset.index = 0; - } - this.dataset_changed(); + this.set({'value': ids}); }, - // return id for this commands instead of the object ('ALL' to activate all conversion) - get_ids_from_command_list: ['LINK_TO', 'REPLACE_WITH'], - get_ids_from_command: function (value_) { + internal_set_value: function(ids) { + if (_.isEqual(ids, this.get("value"))) { + return; + } + var tmp = this.no_rerender; + this.no_rerender = true; + this.data_replace(ids.slice()); + this.no_rerender = tmp; + }, + + /* + *@value: data {object} contains all value to send to the db + * options {object} options sent to the dataset (like the default values) + *@return deferred resolve with the created virtual id + */ + data_create: function (data, options) { + return this.send_commands([COMMANDS.create(data)], options); + }, + + /* + *@value: id {int or string} id or virtual id of the record to update + * data {object} contains all value to send to the db + * options {object} options sent to the dataset + *@return deferred + */ + data_update: function (id, data, options) { + return this.send_commands([COMMANDS.update(id, data)], options); + }, + + /* + *@value: id {int or string} id or virtual id of the record to add + * options {object} options sent to the dataset + *@return deferred + */ + data_link: function (id, options) { + return this.send_commands([COMMANDS.link_to(id)], options); + }, + + /* + *@value: ids {array} list of ids or virtual ids of the record to add + * options {object} options sent to the dataset + *@return deferred + */ + data_link_multi: function (ids, options) { + return this.send_commands(_.map(ids, function (id) { return COMMANDS.link_to(id); }), options); + }, + + /* + *@value: id {int or string} id or virtual id of the record to unlink or delete (function of field type) + *@return deferred + */ + data_delete: function (id) { + return this.send_commands([COMMANDS.delete(id)]); + }, + + /* + *@value: ids {array} list of ids or virtual ids of the record who replace the previous list + * options {object} options sent to the dataset + *@return deferred + */ + data_replace: function (ids, options) { + return this.send_commands([COMMANDS.replace_with(ids)], options); + }, + + /* + *@value: ids {array} list of ids or virtual ids of the record to read + * fields {array} list of the field to read + * options {object} options sent to the dataset + *@return deferred resolve with the records + */ + data_read: function (ids, fields, options) { + return this.dataset.read_ids(ids, fields, options); + }, + + /** + *Compute the write command list into the dataset + *@value: command_list {array} command list + * options {object} options for the datasets (eg: the default values) + *@return : deferred + */ + send_commands: function (command_list, options) { var self = this; - var ids; - var command_list = []; - if (this.get_ids_from_command_list && this.get_ids_from_command_list !== 'ALL') { - for (var i=0; i= 1 && value_[0] instanceof Array) { - ids = []; - _.each(value_, function(command) { - var obj = {values: command[2]}; - if (command_list.length && command.indexOf(command_list) === -1) { - ids.push(obj); - } - switch (command[0]) { - case commands.CREATE: - obj.id = _.uniqueId(self.dataset.virtual_id_prefix); - obj.defaults = {}; - self.dataset.to_create.push(obj); - self.dataset.cache.push(_.extend(_.clone(obj), {values: _.clone(command[2])})); - ids.push(obj.id); - return; - case commands.UPDATE: - obj.id = command[1]; - self.dataset.to_write.push(obj); - self.dataset.cache.push(_.extend(_.clone(obj), {values: _.clone(command[2])})); - ids.push(obj.id); - return; - case commands.DELETE: - self.dataset.to_delete.push({id: command[1]}); - return; - case commands.LINK_TO: - ids.push(command[1]); - return; - case commands.DELETE_ALL: - self.dataset.delete_all = true; - return; - case commands.REPLACE_WITH: - ids = command[2]; - return; - } - }); - return ids; - } else if (value_.length >= 1 && typeof(value_[0]) === "object") { - ids = []; - this.dataset.delete_all = true; - _.each(value_, function(command) { - var obj = {values: command}; - obj.id = _.uniqueId(self.dataset.virtual_id_prefix); - obj.defaults = {}; - self.dataset.to_create.push(obj); - self.dataset.cache.push(_.clone(obj)); - ids.push(obj.id); - }); - return ids; - } else { - return value_; - } - } + _.each(starting_ids, function(id) { + if (is_one2many) { + command_list.push(COMMANDS.delete(id)); + } else if (is_one2many && !self.dataset.delete_all) { + command_list.push(COMMANDS.unlink(id)); + } + }); + + return command_list; + }, + + is_false: function() { + return _(this.get('value')).isEmpty(); + }, }); @@ -606,10 +725,13 @@ var FieldOne2Many = AbstractManyField.extend({ this.is_loaded = $.Deferred(); this.initial_is_loaded = this.is_loaded; - this.form_last_update = $.Deferred(); - this.init_form_last_update = this.form_last_update; this.is_started = false; this.set_value([]); + this.on('change:commands', this, function () { + if (this.is_started && !this.no_rerender) { + this.reload_current_view(); + } + }); }, start: function() { this._super.apply(this, arguments); @@ -665,13 +787,6 @@ var FieldOne2Many = AbstractManyField.extend({ reorderable: false, }); } - } else if (view.view_type === "form") { - if (self.get("effective_readonly")) { - view.view_type = 'form'; - } - _.extend(view.options, { - not_interactible_on_create: true, - }); } else if (view.view_type === "kanban") { _.extend(view.options, { confirm_on_delete: false, @@ -691,9 +806,6 @@ var FieldOne2Many = AbstractManyField.extend({ this.viewmanager = new One2ManyViewManager(this, this.dataset, views, {}); this.viewmanager.o2m = self; - var once = $.Deferred().done(function() { - self.init_form_last_update.resolve(); - }); var def = $.Deferred().done(function() { self.initial_is_loaded.resolve(); }); @@ -712,14 +824,6 @@ var FieldOne2Many = AbstractManyField.extend({ return true; }); } - } else if (view_type === "form") { - if (self.get("effective_readonly")) { - $(".oe_form_buttons", controller.$el).children().remove(); - } - controller.on("load_record", self, function(){ - once.resolve(); - }); - controller.on('pager_action_executed',self,self.save_any_view); } else if (view_type == "graph") { self.reload_current_view(); } @@ -745,15 +849,6 @@ var FieldOne2Many = AbstractManyField.extend({ var view = self.get_active_view(); if (view.type === "list") { return view.controller.reload_content(); - } else if (view.type === "form") { - if (self.dataset.index === null && self.dataset.ids.length >= 1) { - self.dataset.index = 0; - } - var act = function() { - return view.controller.do_show(); - }; - self.form_last_update = self.form_last_update.then(act, act); - return self.form_last_update; } else if (view.controller.do_search) { return view.controller.do_search(self.build_domain(), self.dataset.get_context(), []); } @@ -766,35 +861,12 @@ var FieldOne2Many = AbstractManyField.extend({ */ return (this.viewmanager && this.viewmanager.active_view); }, - get_ids_from_command_list: 'ALL', set_value: function(value_) { this._super(value_); if (this.is_started && !this.no_rerender) { - return this.reload_current_view(); - } else { - return $.when(); + this.reload_current_view(); } }, - get_value: function() { - var self = this; - if (!this.dataset) - return []; - var val = this.dataset.delete_all ? [commands.delete_all()] : []; - val = val.concat(_.map(this.dataset.ids, function(id) { - var alter_order = _.detect(self.dataset.to_create, function(x) {return x.id === id;}); - if (alter_order) { - return commands.create(alter_order.values); - } - alter_order = _.detect(self.dataset.to_write, function(x) {return x.id === id;}); - if (alter_order) { - return commands.update(alter_order.id, alter_order.values); - } - return commands.link_to(id); - })); - return val.concat(_.map( - this.dataset.to_delete, function(x) { - return commands['delete'](x.id);})); - }, commit_value: function() { return this.save_any_view(); }, @@ -846,11 +918,6 @@ var One2ManyListView = ListView.extend({ })); this.on('edit:after', this, this.proxy('_after_edit')); this.on('save:before cancel:before', this, this.proxy('_before_unedit')); - - this.records - .bind('add', this.proxy("changed_records")) - .bind('remove', this.proxy("changed_records")); - this.on('save:after', this, this.proxy("changed_records")); }, start: function () { var ret = this._super(); @@ -859,9 +926,6 @@ var One2ManyListView = ListView.extend({ .on('mousedown.handleButtons', 'table button, div a.oe_m2o_cm_button', this.proxy('_button_down')); return ret; }, - changed_records: function () { - this.o2m.dataset_changed(); - }, is_valid: function () { var self = this; if (!this.fields_view || !this.editable()){ @@ -906,13 +970,10 @@ var One2ManyListView = ListView.extend({ initial_view: "form", alternative_form_view: self.o2m.field.views ? self.o2m.field.views.form : undefined, create_function: function(data, options) { - return self.o2m.dataset.create(data, options).done(function(r) { - self.o2m.dataset.set_ids(self.o2m.dataset.ids.concat([r])); - self.o2m.dataset.trigger("dataset_changed", r); - }); + return self.o2m.data_create(data, options); }, - read_function: function() { - return self.o2m.dataset.read_ids.apply(self.o2m.dataset, arguments); + read_function: function(ids, fields, options) { + return self.o2m.data_read(ids, fields, options); }, parent_view: self.o2m.view, child_name: self.o2m.name, @@ -932,15 +993,15 @@ var One2ManyListView = ListView.extend({ pop.show_element(self.o2m.field.relation, id, self.o2m.build_context(), { title: _t("Open: ") + self.o2m.string, write_function: function(id, data) { - return self.o2m.dataset.write(id, data, {}).done(function() { + return self.o2m.data_update(id, data, {}).done(function() { self.o2m.reload_current_view(); }); }, alternative_form_view: self.o2m.field.views ? self.o2m.field.views.form : undefined, parent_view: self.o2m.view, child_name: self.o2m.name, - read_function: function() { - return self.o2m.dataset.read_ids.apply(self.o2m.dataset, arguments); + read_function: function(ids, fields, options) { + return self.o2m.data_read(ids, fields, options); }, form_view_options: {'not_interactible_on_create':true}, readonly: !this.is_action_enabled('edit') || self.o2m.get("effective_readonly") @@ -1103,21 +1164,18 @@ var One2ManyViewManager = ViewManager.extend({ pop.show_element(self.o2m.field.relation, id, self.o2m.build_context(), { title: _t("Open: ") + self.o2m.string, create_function: function(data, options) { - return self.o2m.dataset.create(data, options).done(function(r) { - self.o2m.dataset.set_ids(self.o2m.dataset.ids.concat([r])); - self.o2m.dataset.trigger("dataset_changed", r); - }); + return self.o2m.data_create(data, options); }, write_function: function(id, data, options) { - return self.o2m.dataset.write(id, data, {}).done(function() { + return self.o2m.data_update(id, data, {}).done(function() { self.o2m.reload_current_view(); }); }, alternative_form_view: self.o2m.field.views ? self.o2m.field.views.form : undefined, parent_view: self.o2m.view, child_name: self.o2m.name, - read_function: function() { - return self.o2m.dataset.read_ids.apply(self.o2m.dataset, arguments); + read_function: function(ids, fields, options) { + return self.o2m.data_read(ids, fields, options); }, form_view_options: {'not_interactible_on_create':true}, readonly: self.o2m.get("effective_readonly") @@ -1230,13 +1288,6 @@ var FieldMany2ManyTags = AbstractManyField.extend(common.CompletionFieldMixin, c } }); }, - is_false: function() { - return _(this.get("value")).isEmpty(); - }, - get_value: function() { - var tmp = [commands.replace_with(this.get("value"))]; - return tmp; - }, get_search_blacklist: function() { return this.get("value"); }, @@ -1244,9 +1295,7 @@ var FieldMany2ManyTags = AbstractManyField.extend(common.CompletionFieldMixin, c return _.map(data, function(el) {return {name: el[1], id:el[0]};}); }, get_render_data: function(ids){ - var self = this; - var dataset = new data.DataSetStatic(this, this.field.relation, self.build_context()); - return dataset.name_get(ids); + return this.dataset.name_get(ids); }, render_tag: function(data) { var self = this; @@ -1260,7 +1309,6 @@ var FieldMany2ManyTags = AbstractManyField.extend(common.CompletionFieldMixin, c }, render_value: function() { var self = this; - var dataset = new data.DataSetStatic(this, this.field.relation, self.build_context()); var values = self.get("value"); var handle_names = function(data) { if (self.isDestroyed()) @@ -1291,7 +1339,7 @@ var FieldMany2ManyTags = AbstractManyField.extend(common.CompletionFieldMixin, c width: width, minHeight: height }); - }, + }, _search_create_popup: function() { this.ignore_blur = true; return common.CompletionFieldMixin._search_create_popup.apply(this, arguments); @@ -1350,22 +1398,13 @@ var FieldMany2Many = AbstractManyField.extend(common.ReinitializeFieldMixin, { this.list_view.destroy(); this.list_view = undefined; }, - get_value: function() { - return [commands.replace_with(this.get('value'))]; - }, - is_false: function () { - return _(this.get("value")).isEmpty(); - }, render_value: function() { var self = this; - this.dataset.set_ids(this.get("value")); + //this.dataset.set_ids(this.get('value') || []); this.render_value_dm.add(this.is_loaded).then(function() { return self.list_view.reload_content(); }); - }, - dataset_changed: function() { - this.internal_set_value(this.dataset.ids); - }, + } }); var Many2ManyDataSet = data.DataSetStatic.extend({ @@ -1399,17 +1438,7 @@ var Many2ManyListView = ListView.extend(/** @lends instance.web.form.Many2ManyLi ); var self = this; pop.on("elements_selected", self, function(element_ids) { - var reload = false; - _(element_ids).each(function (id) { - if(! _.detect(self.dataset.ids, function(x) {return x == id;})) { - self.dataset.set_ids(self.dataset.ids.concat([id])); - self.m2m_field.dataset_changed(); - reload = true; - } - }); - if (reload) { - self.reload_content(); - } + return self.o2m.data_link_multi(element_ids); }); }, do_activate_record: function(index, id) { @@ -1420,7 +1449,10 @@ var Many2ManyListView = ListView.extend(/** @lends instance.web.form.Many2ManyLi alternative_form_view: this.m2m_field.field.views ? this.m2m_field.field.views.form : undefined, readonly: this.getParent().get("effective_readonly") }); - pop.on('write_completed', self, self.reload_content); + pop.on('write_completed', self, function () { + self.dataset.evict_record(id); + self.reload_content(); + }); }, do_button_action: function(name, id, callback) { var self = this; @@ -1467,9 +1499,6 @@ var FieldMany2ManyKanban = AbstractManyField.extend(common.CompletionFieldMixin, }); }); }, - get_value: function() { - return [commands.replace_with(this.get('value'))]; - }, load_view: function() { var self = this; var Many2ManyKanbanView = core.view_registry.get('many2many_kanban'); @@ -1503,9 +1532,6 @@ var FieldMany2ManyKanban = AbstractManyField.extend(common.CompletionFieldMixin, return self.kanban_view.do_search(self.build_domain(), self.dataset.get_context(), []); }); }, - dataset_changed: function() { - this.set({'value': this.dataset.ids}); - }, open_popup: function(type, unused) { if (type !== "form") return; @@ -1522,13 +1548,7 @@ var FieldMany2ManyKanban = AbstractManyField.extend(common.CompletionFieldMixin, this.build_context() ); pop.on("elements_selected", self, function(element_ids) { - _.each(element_ids, function(one_id) { - if(! _.detect(self.dataset.ids, function(x) {return x == one_id;})) { - self.dataset.set_ids([].concat(self.dataset.ids, [one_id])); - self.dataset_changed(); - self.render_value(); - } - }); + return self.data_link_multi(element_ids); }); } else { var id = self.dataset.ids[self.dataset.index]; @@ -1536,7 +1556,7 @@ var FieldMany2ManyKanban = AbstractManyField.extend(common.CompletionFieldMixin, pop.show_element(self.field.relation, id, self.build_context(), { title: _t("Open: ") + self.string, write_function: function(id, data, options) { - return self.dataset.write(id, data, {}).done(function() { + return self.data_update(id, data, {}).done(function() { self.render_value(); }); }, @@ -1577,9 +1597,6 @@ var FieldMany2ManyBinaryMultiFiles = AbstractManyField.extend(common.Reinitializ initialize_content: function() { this.$el.on('change', 'input.oe_form_binary_file', this.on_file_change ); }, - get_value: function() { - return [commands.replace_with(this.get("value"))]; - }, get_file_url: function (attachment) { return this.session.url('/web/binary/saveas', {model: 'ir.attachment', field: 'datas', filename_field: 'datas_fname', id: attachment.id}); }, @@ -1709,7 +1726,6 @@ var FieldMany2ManyCheckBoxes = AbstractManyField.extend(common.ReinitializeField className: "oe_form_many2many_checkboxes", init: function() { this._super.apply(this, arguments); - this.set("value", {}); this.set("records", []); this.field_manager.on("view_content_has_changed", this, function() { var domain = new data.CompoundDomain(this.build_domain()).eval(); @@ -1735,38 +1751,18 @@ var FieldMany2ManyCheckBoxes = AbstractManyField.extend(common.ReinitializeField }); }, render_value: function() { - this.$().html(QWeb.render("FieldMany2ManyCheckBoxes", {widget: this, selected: this.get("value")})); + this.$().html(QWeb.render("FieldMany2ManyCheckBoxes", {widget: this})); var inputs = this.$("input"); inputs.change(_.bind(this.from_dom, this)); if (this.get("effective_readonly")) inputs.attr("disabled", "true"); }, from_dom: function() { - var new_value = {}; - this.$("input").each(function() { - var elem = $(this); - new_value[elem.data("record-id")] = elem.is(":checked") ? true : undefined; - }); - if (! _.isEqual(new_value, this.get("value"))) + var new_value = this.$("input:checked").map(function() { return +$(this).data("record-id"); }).get(); + if (! _.isEqual(new_value, this.get("value"))) { this.internal_set_value(new_value); - }, - set_value: function(value_) { - var value_ = this.get_ids_from_command(value_); - var formatted = {}; - _.each(value_, function(el) { - formatted[JSON.stringify(el)] = true; - }); - this.set({'value': formatted}); - }, - get_value: function() { - var value = _.filter(_.keys(this.get("value")), function(el) { - return this.get("value")[el]; - }, this); - value = _.map(value, function(el) { - return JSON.parse(el); - }); - return [commands.replace_with(value)]; - }, + } + } }); @@ -1778,7 +1774,7 @@ core.form_widget_registry .add('one2many', FieldOne2Many) .add('one2many_list', FieldOne2Many) .add('many2many_binary', FieldMany2ManyBinaryMultiFiles) - .add('many2many_checkboxes', FieldMany2ManyCheckBoxes) + .add('many2many_checkboxes', FieldMany2ManyCheckBoxes); return { FieldMany2ManyTags: FieldMany2ManyTags, diff --git a/addons/web/static/src/xml/base.xml b/addons/web/static/src/xml/base.xml index 285c9066c66..9d9b54e4d74 100644 --- a/addons/web/static/src/xml/base.xml +++ b/addons/web/static/src/xml/base.xml @@ -1932,10 +1932,10 @@
- + - +