From 7d3cda215a85dd81571a5dde3753fd2f954f6ccf Mon Sep 17 00:00:00 2001 From: Christophe Matthieu Date: Wed, 15 Apr 2015 11:35:39 +0200 Subject: [PATCH] [IMP] web: web.form_relational: Change widgets to use AbstractManyField. Remove custom insertion in the DataSet to improve the managment of cache and the odoo commands list. this.get('value') return the list of ids and virtual ids and get_value return a commands list. --- .../src/js/views/form_relational_widgets.js | 468 +++++++++--------- addons/web/static/src/xml/base.xml | 4 +- 2 files changed, 234 insertions(+), 238 deletions(-) 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 @@
- + - +