diff --git a/addons/base/controllers/main.py b/addons/base/controllers/main.py index 3f519f30962..bc6bc141863 100644 --- a/addons/base/controllers/main.py +++ b/addons/base/controllers/main.py @@ -383,11 +383,6 @@ class DataSet(openerpweb.Controller): reads = Model.read(ids, fields or False, context) reads.sort(key=lambda obj: ids.index(obj['id'])) return reads - - @openerpweb.jsonrequest - def read(self, request, model, ids, fields=False): - return self.do_search_read(request, model, ids, fields, - request.session.eval_context(request.context)) @openerpweb.jsonrequest def get(self, request, model, ids, fields=False): diff --git a/addons/base/static/lib/qweb/qweb2.js b/addons/base/static/lib/qweb/qweb2.js index 5041142924d..e617df60183 100644 --- a/addons/base/static/lib/qweb/qweb2.js +++ b/addons/base/static/lib/qweb/qweb2.js @@ -48,7 +48,7 @@ var QWeb2 = { if (o !== null && o !== undefined) { if (o.constructor === Array) { if (o[1] !== null && o[1] !== undefined) { - return ' ' + o[0] + '="' + this.html_escape(o[1]) + '"'; + return ' ' + o[0] + '="' + this.html_escape(o[1], true) + '"'; } } else if (typeof o === 'object') { var r = ''; @@ -670,15 +670,10 @@ QWeb2.Element = (function() { this.top("(function(" + value + ") {"); this.bottom("})(dict);"); this.indent(); - if (this.children.length === 1) { - var lines = this.children[0].node.data.split(/\r?\n/); - for (var i = 0, ilen = lines.length; i < ilen; i++) { - this.top(lines[i]); - } - } else { - this.engine.tools.exception("'js' code block contains " + this.children.length + " nodes instead of 1"); + var lines = this.engine.tools.xml_node_to_string(this.node, true).split(/\r?\n/); + for (var i = 0, ilen = lines.length; i < ilen; i++) { + this.top(lines[i]); } - // Maybe I could handle the children ? this.process_children = false; }, compile_action_debug : function(value) { diff --git a/addons/base/static/src/css/base.css b/addons/base/static/src/css/base.css index 9749dc97110..84b51b2cd54 100644 --- a/addons/base/static/src/css/base.css +++ b/addons/base/static/src/css/base.css @@ -474,9 +474,10 @@ background: linear-gradient(top, #ffffff 0%,#d8d8d8 11%,#afafaf 86%,#333333 91%, background: #AAAAAA; } .openerp .filter_icon { + height: 22px; padding: 1px 2px 0 2px; - margin-left: 0; - margin-right: 0; + margin: 0; + vertical-align: bottom; } .openerp .filter_label { font-weight: bold; @@ -537,14 +538,6 @@ background: linear-gradient(top, #ffffff 0%,#d8d8d8 11%,#afafaf 86%,#333333 91%, .openerp .searchview_group_content { padding-left: 10px; } -.openerp .searchview_group_content .oe-searchview-render-line { - width:0; -} - - -.openerp .oe-searchview-render-line { - width:100%; -} .openerp .searchview_extended_group { padding: 3px; @@ -1055,18 +1048,14 @@ background: linear-gradient(top, #ffffff 0%,#ebe9e9 100%); /* W3C */ .openerp .oe-m2o-drop-down-button img, .openerp .oe-m2o-cm-button img { margin-bottom: -4px; + cursor: pointer; } .openerp .oe-m2o-disabled-cm { color: grey; } -.openerp .oe-dialog-warning { +.openerp .oe-dialog-warning p { + padding-left: 1em; font-size: 1.2em; font-weight: bold; } -.openerp .oe-dialog-warning img { - float: left; - position: relative; - top: -0.5em; - margin:0 7px 7px 0; -} diff --git a/addons/base/static/src/js/chrome.js b/addons/base/static/src/js/chrome.js index 2bea20fe972..b019d7f1867 100644 --- a/addons/base/static/src/js/chrome.js +++ b/addons/base/static/src/js/chrome.js @@ -399,7 +399,6 @@ openerp.base.Session = openerp.base.BasicController.extend( /** @lends openerp.b on_rpc_response: function() { }, on_rpc_error: function(error) { - this.on_log(error.message, error.data.debug); }, /** * The session is validated either by login or by restoration of a previous session @@ -830,8 +829,15 @@ openerp.base.CrashManager = openerp.base.Dialog.extend({ }, on_rpc_error: function(error) { this.error = error; - if (error.code === 200) { - this.dialog_title = "OpenERP Warning"; + if (error.data.fault_code) { + var split = error.data.fault_code.split('\n')[0].split(' -- '); + if (split.length > 1) { + error.type = split.shift(); + error.data.fault_code = error.data.fault_code.substr(error.type.length + 4); + } + } + if (error.code === 200 && error.type) { + this.dialog_title = "OpenERP " + _.capitalize(error.type); this.template = 'DialogWarning'; this.open({ width: 'auto', diff --git a/addons/base/static/src/js/data.js b/addons/base/static/src/js/data.js index 736c31c1f7b..b3ddd7750c8 100644 --- a/addons/base/static/src/js/data.js +++ b/addons/base/static/src/js/data.js @@ -235,7 +235,7 @@ openerp.base.DataSet = openerp.base.Controller.extend( /** @lends openerp.base. this._super(session); this.model = model; this.context = context || {}; - this.index = 0; + this.index = null; this.count = 0; }, start: function() { @@ -310,9 +310,10 @@ openerp.base.DataSet = openerp.base.Controller.extend( /** @lends openerp.base. context: this.context }, callback); }, - unlink: function(ids) { - // to implement in children - this.notification.notify("Unlink", ids); + unlink: function(ids, callback, error_callback) { + var self = this; + return this.call_and_eval("unlink", [ids, this.context], null, 1, + callback, error_callback); }, call: function (method, args, callback, error_callback) { return this.rpc('/base/dataset/call', { @@ -356,15 +357,20 @@ openerp.base.DataSetStatic = openerp.base.DataSet.extend({ this.count = this.ids.length; }, read_slice: function (fields, offset, limit, callback) { + var self = this; + offset = offset || 0; var end_pos = limit && limit !== -1 ? offset + limit : undefined; this.read_ids(this.ids.slice(offset, end_pos), fields, callback); }, set_ids: function (ids) { this.ids = ids; this.count = this.ids.length; + this.index = this.index <= this.count - 1 ? + this.index : (this.count > 0 ? this.count - 1 : 0); }, unlink: function(ids) { this.on_unlink(ids); + return $.Deferred().resolve({result: true}); }, on_unlink: function(ids) { this.set_ids(_.without.apply(null, [this.ids].concat(ids))); @@ -438,9 +444,149 @@ openerp.base.DataSetSearch = openerp.base.DataSet.extend({ this._sort.unshift((reverse ? '-' : '') + field); return undefined; + }, + unlink: function(ids, callback, error_callback) { + var self = this; + return this._super(ids, function(result) { + self.ids = _.without.apply(_, [self.ids].concat(ids)); + self.count = self.ids.length; + self.index = self.index <= self.count - 1 ? + self.index : (self.count > 0 ? self.count -1 : 0); + if (callback) + callback(result); + }, error_callback); } }); +openerp.base.BufferedDataSet = openerp.base.DataSetStatic.extend({ + virtual_id_prefix: "one2many_v_id_", + virtual_id_regex: /one2many_v_id_.*/, + debug_mode: true, + init: function() { + this._super.apply(this, arguments); + this.reset_ids([]); + }, + create: function(data, callback, error_callback) { + var cached = {id:_.uniqueId(this.virtual_id_prefix), values: data}; + this.to_create.push(cached); + this.cache.push(cached); + this.on_change(); + var to_return = $.Deferred().then(callback); + setTimeout(function() {to_return.resolve({result: cached.id});}, 0); + return to_return.promise(); + }, + write: function (id, data, callback) { + var self = this; + var record = _.detect(this.to_create, function(x) {return x.id === id;}); + record = record || _.detect(this.to_write, function(x) {return x.id === id;}); + if (record) { + $.extend(record.values, data); + } else { + record = {id: id, values: data}; + self.to_write.push(record); + } + var cached = _.detect(this.cache, function(x) {return x.id === id;}); + $.extend(cached.values, record.values); + this.on_change(); + var to_return = $.Deferred().then(callback); + setTimeout(function () {to_return.resolve({result: true});}, 0); + return to_return.promise(); + }, + unlink: function(ids, callback, error_callback) { + var self = this; + _.each(ids, function(id) { + if (! _.detect(self.to_create, function(x) { return x.id === id; })) { + self.to_delete.push({id: id}) + } + }); + this.to_create = _.reject(this.to_create, function(x) { return _.include(ids, x.id);}); + this.to_write = _.reject(this.to_write, function(x) { return _.include(ids, x.id);}); + this.cache = _.reject(this.cache, function(x) { return _.include(ids, x.id);}); + this.set_ids(_.without.apply(_, [this.ids].concat(ids))); + this.on_change(); + var to_return = $.Deferred().then(callback); + setTimeout(function () {to_return.resolve({result: true});}, 0); + return to_return.promise(); + }, + reset_ids: function(ids) { + this.set_ids(ids); + this.to_delete = []; + this.to_create = []; + this.to_write = []; + this.cache = []; + }, + on_change: function() {}, + read_ids: function (ids, fields, callback) { + var self = this; + var to_get = []; + _.each(ids, function(id) { + var cached = _.detect(self.cache, function(x) {return x.id === id;}); + var created = _.detect(self.to_create, function(x) {return x.id === id;}); + if (created) { + _.each(fields, function(x) {if (cached.values[x] === undefined) cached.values[x] = false;}); + } else { + if (!cached || !_.all(fields, function(x) {return cached.values[x] !== undefined})) + to_get.push(id); + } + }); + var completion = $.Deferred().then(callback); + var return_records = function() { + var records = _.map(ids, function(id) { + return _.extend({}, _.detect(self.cache, function(c) {return c.id === id;}).values, {"id": id}); + }); + if (self.debug_mode) { + if (_.include(records, undefined)) { + throw "Record not correctly loaded"; + } + } + setTimeout(function () {completion.resolve(records);}, 0); + } + if(to_get.length > 0) { + var rpc_promise = this._super(to_get, fields, function(records) { + _.each(records, function(record, index) { + var id = to_get[index]; + var cached = _.detect(self.cache, function(x) {return x.id === id;}); + if (!cached) { + self.cache.push({id: id, values: record}); + } else { + // I assume cache value is prioritary + _.defaults(cached.values, record); + } + }); + return_records(); + }); + $.when(rpc_promise).fail(function() {completion.reject();}); + } else { + return_records(); + } + return completion.promise(); + }, +}); + +openerp.base.ReadOnlyDataSetSearch = openerp.base.DataSetSearch.extend({ + create: function(data, callback, error_callback) { + this.on_create(data); + var to_return = $.Deferred().then(callback); + setTimeout(function () {to_return.resolve({"result": undefined});}, 0); + return to_return.promise(); + }, + on_create: function(data) {}, + write: function (id, data, callback) { + this.on_write(id); + var to_return = $.Deferred().then(callback); + setTimeout(function () {to_return.resolve({"result": true});}, 0); + return to_return.promise(); + }, + on_write: function(id) {}, + unlink: function(ids, callback, error_callback) { + this.on_unlink(ids); + var to_return = $.Deferred().then(callback); + setTimeout(function () {to_return.resolve({"result": true});}, 0); + return to_return.promise(); + }, + on_unlink: function(ids) {} +}); + openerp.base.CompoundContext = function() { this.__ref = "compound_context"; this.__contexts = []; diff --git a/addons/base/static/src/js/form.js b/addons/base/static/src/js/form.js index d221872598d..495cdc038b1 100644 --- a/addons/base/static/src/js/form.js +++ b/addons/base/static/src/js/form.js @@ -36,6 +36,7 @@ openerp.base.FormView = openerp.base.View.extend( /** @lends openerp.base.FormV this.default_focus_button = null; this.registry = openerp.base.form.widgets; this.has_been_loaded = $.Deferred(); + this.$form_header = null; }, start: function() { //this.log('Starting FormView '+this.model+this.view_id) @@ -59,72 +60,80 @@ openerp.base.FormView = openerp.base.View.extend( /** @lends openerp.base.FormV _.each(this.widgets, function(w) { w.start(); }); - this.$element.find('div.oe_form_pager button[data-pager-action]').click(function() { + this.$form_header = this.$element.find('#' + this.element_id + '_header'); + this.$form_header.find('div.oe_form_pager button[data-pager-action]').click(function() { var action = $(this).data('pager-action'); self.on_pager_action(action); }); - this.$element.find('#' + this.element_id + '_header button.oe_form_button_save').click(this.do_save); - this.$element.find('#' + this.element_id + '_header button.oe_form_button_save_edit').click(this.do_save_edit); - this.$element.find('#' + this.element_id + '_header button.oe_form_button_cancel').click(this.do_cancel); - this.$element.find('#' + this.element_id + '_header button.oe_form_button_new').click(this.on_button_new); + this.$form_header.find('button.oe_form_button_save').click(this.do_save); + this.$form_header.find('button.oe_form_button_save_edit').click(this.do_save_edit); + this.$form_header.find('button.oe_form_button_cancel').click(this.do_cancel); + this.$form_header.find('button.oe_form_button_new').click(this.on_button_new); this.view_manager.sidebar.set_toolbar(data.fields_view.toolbar); this.has_been_loaded.resolve(); }, do_show: function () { var self = this; - self.$element.show(); - if (this.dataset.index === null || (this.dataset.index === 0 && this.dataset.ids.length == 0)) { + if (this.dataset.index === null) { // null index means we should start a new record - // 0 index with empty ids means we called the form with empty dataset (wizards, switched to form from empty list, ...) this.on_button_new(); } else { this.dataset.read_index(_.keys(this.fields_view.fields), this.on_record_loaded); } + self.$element.show(); this.view_manager.sidebar.do_refresh(true); }, do_hide: function () { this.$element.hide(); }, on_record_loaded: function(record) { + if (!record) { + throw("Form: No record received"); + } + if (!record.id) { + this.$form_header.find('.oe_form_on_create').show(); + this.$form_header.find('.oe_form_on_update').hide(); + this.$form_header.find('button.oe_form_button_new').hide(); + } else { + this.$form_header.find('.oe_form_on_create').hide(); + this.$form_header.find('.oe_form_on_update').show(); + this.$form_header.find('button.oe_form_button_new').show(); + } this.touched = false; - if (record) { - this.datarecord = record; - for (var f in this.fields) { + this.datarecord = record; + for (var f in this.fields) { + var field = this.fields[f]; + field.touched = false; + field.set_value(this.datarecord[f] || false); + field.validate(); + } + if (!record.id) { + // New record: Second pass in order to trigger the onchanges + this.touched = true; + this.show_invalid = false; + for (var f in record) { var field = this.fields[f]; - field.touched = false; - field.set_value(this.datarecord[f] || false); - field.validate(); - } - if (!record.id) { - // New record: Second pass in order to trigger the onchanges - this.touched = true; - this.show_invalid = false; - for (var f in record) { - this.on_form_changed(this.fields[f]); + if (field) { + field.touched = true; + this.do_onchange(field); } } - this.on_form_changed(); - this.show_invalid = this.ready = true; - } else { - this.log("No record received"); } + this.on_form_changed(); + this.show_invalid = this.ready = true; this.do_update_pager(record.id == null); this.do_update_sidebar(); if (this.default_focus_field) { this.default_focus_field.focus(); } }, - on_form_changed: function(widget) { - if (widget && widget.node.attrs.on_change) { - this.do_onchange(widget); - } else { - for (var w in this.widgets) { - w = this.widgets[w]; - w.process_attrs(); - w.update_dom(); - } + on_form_changed: function() { + for (var w in this.widgets) { + w = this.widgets[w]; + w.process_attrs(); + w.update_dom(); } }, on_pager_action: function(action) { @@ -157,6 +166,7 @@ openerp.base.FormView = openerp.base.View.extend( /** @lends openerp.base.FormV this.ready = false; var onchange = _.trim(widget.node.attrs.on_change); var call = onchange.match(/^\s?(.*?)\((.*?)\)\s?$/); + console.log("Onchange triggered for field '%s' -> %s", widget.name, onchange); if (call) { var method = call[1], args = []; var argument_replacement = { @@ -195,12 +205,14 @@ openerp.base.FormView = openerp.base.View.extend( /** @lends openerp.base.FormV on_processed_onchange: function(response, processed) { var result = response.result; if (result.value) { + console.log(" |-> Onchange Response :", result.value); for (var f in result.value) { var field = this.fields[f]; if (field) { var value = result.value[f]; processed.push(field.name); if (field.get_value() != value) { + console.log(" |-> Onchange Action : change '%s' value from '%s' to '%s'", field.name, field.get_value(), value); field.set_value(value); if (_.indexOf(processed, field.name) < 0) { this.do_onchange(field, processed); @@ -252,18 +264,23 @@ openerp.base.FormView = openerp.base.View.extend( /** @lends openerp.base.FormV if (!this.ready) { return false; } - var invalid = false; - var values = {}; + var invalid = false, + values = {}, + first_invalid_field = null; for (var f in this.fields) { f = this.fields[f]; if (f.invalid) { invalid = true; f.update_dom(); + if (!first_invalid_field) { + first_invalid_field = f; + } } else if (f.touched) { values[f.name] = f.get_value(); } } if (invalid) { + first_invalid_field.focus(); this.on_invalid(); return false; } else { @@ -360,12 +377,13 @@ openerp.base.FormView = openerp.base.View.extend( /** @lends openerp.base.FormV if (!this.datarecord.id) { this.on_attachments_loaded([]); } else { - this.rpc('/base/dataset/search_read', { + // TODO fme: modify this so it doesn't try to load attachments when there is not sidebar + /*this.rpc('/base/dataset/search_read', { model: 'ir.attachment', fields: ['name', 'url', 'type'], domain: [['res_model', '=', this.dataset.model], ['res_id', '=', this.datarecord.id], ['type', 'in', ['binary', 'url']]], context: this.dataset.context - }, this.on_attachments_loaded); + }, this.on_attachments_loaded);*/ } }, on_attachments_loaded: function(attachments) { @@ -553,7 +571,7 @@ openerp.base.form.WidgetFrame = openerp.base.form.Widget.extend({ var widget_type = node.attrs.widget || type.type || node.tag; var widget = new (this.view.registry.get_object(widget_type)) (this.view, node); if (node.tag == 'field') { - if (node.attrs.default_focus == '1') { + if (!this.view.default_focus_field || node.attrs.default_focus == '1') { this.view.default_focus_field = widget; } if (node.attrs.nolabel != '1') { @@ -718,7 +736,8 @@ openerp.base.form.Field = openerp.base.form.Widget.extend({ this.validate(); if (!this.invalid) { this.set_value_from_ui(); - this.view.on_form_changed(this); + this.view.do_onchange(this); + this.view.on_form_changed(); } else { this.update_dom(); } @@ -857,7 +876,7 @@ openerp.base.form.FieldDatetime = openerp.base.form.Field.extend({ } }, validate: function() { - this.invalid = this.required && this.$element.find('input')[this.jqueryui_object]('getDate') === ''; + this.invalid = this.required && !this.$element.find('input')[this.jqueryui_object]('getDate'); }, focus: function() { this.$element.find('input').focus(); @@ -1074,6 +1093,7 @@ openerp.base.form.FieldMany2One = openerp.base.form.Field.extend({ this.value = null; this.cm_id = _.uniqueId('m2o_cm_'); this.last_search = []; + this.tmp_value = undefined; }, start: function() { this._super(); @@ -1259,11 +1279,32 @@ openerp.base.form.FieldMany2One = openerp.base.form.Field.extend({ }, set_value_from_ui: function() {}, set_value: function(value) { - this._super(value); - this.original_value = value; - this._change_int_ext_value(value); + value = value || null; + var self = this; + var _super = this._super; + this.tmp_value = value; + var real_set_value = function(rval) { + this.tmp_value = undefined; + _super.apply(self, rval); + self.original_value = rval; + self._change_int_ext_value(rval); + }; + if(typeof(value) === "number") { + var dataset = new openerp.base.DataSetStatic(this.session, this.field.relation, []); + dataset.call("name_get", [value], function(data) { + real_set_value(data.result[0]); + }).fail(function() {self.tmp_value = undefined;}); + } else { + setTimeout(function() {real_set_value(value);}, 0); + } }, get_value: function() { + if (this.tmp_value !== undefined) { + if (this.tmp_value instanceof Array) { + return this.tmp_value[0]; + } + return this.tmp_value; + } if (this.value === undefined) throw "theorically unreachable state"; return this.value ? this.value[0] : false; @@ -1276,23 +1317,29 @@ openerp.base.form.FieldMany2One = openerp.base.form.Field.extend({ } }); +/* +# Values: (0, 0, { fields }) create +# (1, ID, { fields }) update +# (2, ID) remove (delete) +# (3, ID) unlink one (target id or target of relation) +# (4, ID) link +# (5) unlink all (only valid for one2many) +*/ + openerp.base.form.FieldOne2Many = openerp.base.form.Field.extend({ init: function(view, node) { this._super(view, node); this.template = "FieldOne2Many"; this.is_started = $.Deferred(); - this.is_setted = $.Deferred(); }, start: function() { this._super.apply(this, arguments); var self = this; - this.dataset = new openerp.base.DataSetStatic(this.session, this.field.relation); - this.dataset.on_unlink.add_last(function(ids) { - self.dataset.set_ids(_.without.apply(_, [self.dataset.ids].concat(ids))); + this.dataset = new openerp.base.BufferedDataSet(this.session, this.field.relation); + this.dataset.on_change.add_last(function() { self.on_ui_change(); - self.reload_current_view(); }); var modes = this.node.attrs.mode; @@ -1328,14 +1375,6 @@ openerp.base.form.FieldOne2Many = openerp.base.form.Field.extend({ self.is_started.resolve(); }); this.viewmanager.start(); - - $.when(this.is_started, this.is_setted).then(function() { - if (modes[0] == "tree") { - var view = self.viewmanager.views[self.viewmanager.active_view].controller; - view.reload_content(); - } - // TODO niv: handle other types of views - }); }, reload_current_view: function() { var self = this; @@ -1343,18 +1382,61 @@ openerp.base.form.FieldOne2Many = openerp.base.form.Field.extend({ if(self.viewmanager.active_view === "list") { view.reload_content(); } else if (self.viewmanager.active_view === "form") { - // TODO niv: but fme did not implemented delete in form view anyway + // TODO niv: implement } }, + set_value_from_ui: function() {}, set_value: function(value) { - if(value != false) { - this.dataset.set_ids(value); - this.is_setted.resolve(); + value = value || []; + var self = this; + if(value.length >= 1 && value[0] instanceof Array) { + var ids = []; + _each(value, function(command) { + if(command[0] == 0) { + var obj = {id: _.uniqueId(self.dataset.virtual_id_prefix), values: command[2]}; + self.dataset.to_create.push(obj); + self.dataset.cache.push(_.clone(obj)); + ids.push(obj.id); + } else if(command[0] == 1) { + var obj = {id: command[1], values: command[2]}; + self.dataset.to_write.push(obj); + self.dataset.cache.push(_.clone(obj)); + ids.push(obj.id); + } else if(command[0] == 2) { + self.dataset.to_delete.push({id: command[1]}); + } else if(command[0] == 4) { + ids.push(command[1]); + } + }); + this._super(ids); + this.dataset.set_ids(ids); + } else { + this._super(value); + this.dataset.reset_ids(value); } + $.when(this.is_started).then(function() { + self.reload_current_view(); + }); }, - get_value: function(value) { - //TODO niv - return []; + get_value: function() { + var self = this; + var val = _.map(this.dataset.ids, function(id) { + var alter_order = _.detect(self.dataset.to_create, function(x) {return x.id === id;}); + if (alter_order) { + return [0, 0, alter_order.values]; + } + alter_order = _.detect(self.dataset.to_write, function(x) {return x.id === id;}); + if (alter_order) { + return [1, alter_order.id, alter_order.values]; + } + return [4, id]; + }); + val = val.concat(_.map(this.dataset.to_delete, function(v, k) {return [2, x.id];})); + return val; + }, + validate: function() { + this.invalid = false; + // TODO niv } }); @@ -1364,14 +1446,15 @@ openerp.base.form.One2ManyListView = openerp.base.ListView.extend({ var pop = new openerp.base.form.SelectCreatePopup(null, self.o2m.view.session); pop.select_element(self.o2m.field.relation,{ initial_view: "form", - alternative_form_view: self.o2m.field.views ? self.o2m.field.views["form"] : undefined + alternative_form_view: self.o2m.field.views ? self.o2m.field.views["form"] : undefined, + auto_create: false }); - pop.on_select_elements.add(function(element_ids) { - var ids = self.o2m.dataset.ids; - _.each(element_ids, function(x) {if (!_.include(ids, x)) ids.push(x);}); - self.o2m.dataset.set_ids(ids); - self.o2m.on_ui_change(); - self.o2m.reload_current_view(); + pop.on_create.add(function(data) { + self.o2m.dataset.create(data, function(r) { + self.o2m.dataset.set_ids(self.o2m.dataset.ids.concat([r.result])); + pop.stop(); + self.o2m.reload_current_view(); + }); }); } }); @@ -1382,7 +1465,6 @@ openerp.base.form.FieldMany2Many = openerp.base.form.Field.extend({ this.template = "FieldMany2Many"; this.list_id = _.uniqueId("many2many"); this.is_started = $.Deferred(); - this.is_setted = $.Deferred(); }, start: function() { this._super.apply(this, arguments); @@ -1392,8 +1474,7 @@ openerp.base.form.FieldMany2Many = openerp.base.form.Field.extend({ this.dataset = new openerp.base.DataSetStatic( this.session, this.field.relation); this.dataset.on_unlink.add_last(function(ids) { - self.list_view.reload_content(); - + //TODO niv: should check this for other cases self.on_ui_change(); }); @@ -1406,15 +1487,18 @@ openerp.base.form.FieldMany2Many = openerp.base.form.Field.extend({ self.is_started.resolve(); }); this.list_view.start(); - $.when(this.is_started, this.is_setted).then(function() { - self.list_view.reload_content(); - }); }, set_value: function(value) { - if (value != false) { - this.dataset.set_ids(value); - this.is_setted.resolve(); + value = value || []; + if (value.length >= 1 && value[0] instanceof Array) { + value = value[0][2]; } + this._super(value); + this.dataset.set_ids(value); + var self = this; + $.when(this.is_started).then(function() { + self.list_view.reload_content(); + }); }, get_value: function() { return [[6,false,this.dataset.ids]]; @@ -1459,12 +1543,13 @@ openerp.base.form.SelectCreatePopup = openerp.base.BaseWidget.extend({ * - initial_view: form or search (default search) * - disable_multiple_selection * - alternative_form_view + * - auto_create (default true) */ select_element: function(model, options, domain, context) { this.model = model; this.domain = domain || []; this.context = context || {}; - this.options = options || {}; + this.options = _.defaults(options || {}, {"initial_view": "search", "auto_create": true}); this.initial_ids = this.options.initial_ids; jQuery(this.render()).dialog({title: '', modal: true, @@ -1473,9 +1558,9 @@ openerp.base.form.SelectCreatePopup = openerp.base.BaseWidget.extend({ }, start: function() { this._super(); - this.dataset = new openerp.base.DataSetSearch(this.session, this.model, + this.dataset = new openerp.base.ReadOnlyDataSetSearch(this.session, this.model, this.context, this.domain); - if ((this.options.initial_view || "search") == "search") { + if (this.options.initial_view == "search") { this.setup_search_view(); } else { // "form" this.new_object(); @@ -1514,7 +1599,7 @@ openerp.base.form.SelectCreatePopup = openerp.base.BaseWidget.extend({ $sbutton.click(function() { self.on_select_elements(self.selected_ids); }); - self.view_list = new openerp.base.form.Many2XPopupListView( null, self.session, + self.view_list = new openerp.base.form.SelectCreateListView( null, self.session, self.element_id + "_view_list", self.dataset, false, {'deletable': false}); self.view_list.popup = self; @@ -1525,6 +1610,15 @@ openerp.base.form.SelectCreatePopup = openerp.base.BaseWidget.extend({ }); this.searchview.start(); }, + on_create: function(data) { + if (!this.options.auto_create) + return; + var self = this; + var wdataset = new openerp.base.DataSetSearch(this.session, this.model, this.context, this.domain); + wdataset.create(data, function(r) { + self.on_select_elements([r.result]); + }); + }, on_select_elements: function(element_ids) { }, on_click_element: function(ids) { @@ -1562,17 +1656,12 @@ openerp.base.form.SelectCreatePopup = openerp.base.BaseWidget.extend({ self.stop(); }); }); - this.view_form.on_created.add_last(function(r, success) { - if (r.result) { - var id = arguments[0].result; - self.on_select_elements([id]); - } - }); + this.dataset.on_create.add(this.on_create); this.view_form.do_show(); } }); -openerp.base.form.Many2XPopupListView = openerp.base.ListView.extend({ +openerp.base.form.SelectCreateListView = openerp.base.ListView.extend({ do_add_record: function () { this.popup.new_object(); }, diff --git a/addons/base/static/src/js/list-editable.js b/addons/base/static/src/js/list-editable.js index f7eb3e27bca..b97a35d0612 100644 --- a/addons/base/static/src/js/list-editable.js +++ b/addons/base/static/src/js/list-editable.js @@ -125,6 +125,15 @@ openerp.base.list.editable = function (openerp) { 'class': $(row).attr('class'), click: function (e) {e.stopPropagation();} }) + .delegate('button.oe-edit-row-save', 'click', function () { + self.save_row(); + }) + .delegate('button.oe-edit-row-cancel', 'click', function () { + self.cancel_edition(); + }) + .delegate('button', 'keyup', function (e) { + e.stopImmediatePropagation(); + }) .keyup(function (e) { switch (e.which) { case KEY_RETURN: @@ -136,12 +145,6 @@ openerp.base.list.editable = function (openerp) { default: return; } - }) - .delegate('button.oe-edit-row-save', 'click', function () { - self.save_row(); - }) - .delegate('button.oe-edit-row-cancel', 'click', function () { - self.cancel_edition(); }); if (row) { $new_row.replaceAll(row); diff --git a/addons/base/static/src/js/list.js b/addons/base/static/src/js/list.js index bd2518dcb66..31c16b98fc1 100644 --- a/addons/base/static/src/js/list.js +++ b/addons/base/static/src/js/list.js @@ -336,25 +336,7 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi } var self = this; return $.when(this.dataset.unlink(ids)).then(function () { - _(self.rows).chain() - .map(function (row, index) { - return { - index: index, - id: row.data.id.value - };}) - .filter(function (record) { - return _.contains(ids, record.id); - }) - .sort(function (a, b) { - // sort in reverse index order, so we delete from the end - // and don't blow up the following indexes (leading to - // removing the wrong records from the visible list) - return b.index - a.index; - }) - .each(function (record) { - self.rows.splice(record.index, 1); - }); - // TODO only refresh modified rows + self.reload_content(); }); }, /** @@ -895,7 +877,7 @@ openerp.base.ListView.Groups = Class.extend( /** @lends openerp.base.ListView.Gr var d = new $.Deferred(); dataset.read_slice( - _.filter(_.pluck(this.columns, 'name'), _.identity), + _.filter(_.pluck(_.select(this.columns, function(x) {return x.tag == "field";}), 'name'), _.identity), 0, false, function (records) { var form_records = _(records).map( diff --git a/addons/base/static/src/js/search.js b/addons/base/static/src/js/search.js index cd69ec4f0e8..6efa6988fe8 100644 --- a/addons/base/static/src/js/search.js +++ b/addons/base/static/src/js/search.js @@ -377,6 +377,7 @@ openerp.base.search.FilterGroup = openerp.base.search.Widget.extend({ init: function (filters, view) { this._super(view); this.filters = filters; + this.length = filters.length; }, start: function () { this._super(); diff --git a/addons/base/static/src/js/views.js b/addons/base/static/src/js/views.js index ee910d57cfd..8b87d9cbdbe 100644 --- a/addons/base/static/src/js/views.js +++ b/addons/base/static/src/js/views.js @@ -113,7 +113,8 @@ openerp.base.ViewManager = openerp.base.Controller.extend({ * @returns {jQuery.Deferred} new view loading promise */ on_mode_switch: function(view_type) { - var view_promise; + var self = this, + view_promise; this.active_view = view_type; var view = this.views[view_type]; if (!view.controller) { @@ -124,6 +125,16 @@ openerp.base.ViewManager = openerp.base.Controller.extend({ if (view.embedded_view) { controller.set_embedded_view(view.embedded_view); } + if (view_type === 'list' && this.flags.search_view === false && this.action && this.action['auto_search']) { + // In case the search view is not instanciated: manually call ListView#search + controller.on_loaded.add({ + callback: function () { + controller.do_search([self.action.domain], [self.action.context], []); + }, + position: 'last', + unique: true + }); + } view_promise = controller.start(); var self = this; $.when(view_promise).then(function() { @@ -132,6 +143,7 @@ openerp.base.ViewManager = openerp.base.Controller.extend({ this.views[view_type].controller = controller; } + if (this.searchview) { if (view.controller.searchable === false) { this.searchview.hide(); @@ -261,16 +273,18 @@ openerp.base.ViewManagerAction = openerp.base.ViewManager.extend({ } }); - // init search view - var searchview_id = this.action.search_view_id && this.action.search_view_id[0]; + if (this.flags.search_view !== false) { + // init search view + var searchview_id = this.action.search_view_id && this.action.search_view_id[0]; - var searchview_loaded = this.setup_search_view( - searchview_id || false, search_defaults); + var searchview_loaded = this.setup_search_view( + searchview_id || false, search_defaults); - // schedule auto_search - if (searchview_loaded != null && this.action['auto_search']) { - $.when(searchview_loaded, inital_view_loaded) - .then(this.searchview.do_search); + // schedule auto_search + if (searchview_loaded != null && this.action['auto_search']) { + $.when(searchview_loaded, inital_view_loaded) + .then(this.searchview.do_search); + } } }, stop: function() { @@ -455,6 +469,41 @@ openerp.base.ProcessView = openerp.base.Controller.extend({ openerp.base.HelpView = openerp.base.Controller.extend({ }); +openerp.base.json_node_to_xml = function(node, single_quote, indent) { + // For debugging purpose, this function will convert a json node back to xml + // Maybe usefull for xml view editor + if (typeof(node.tag) !== 'string' || !node.children instanceof Array || !node.attrs instanceof Object) { + throw("Node a json node"); + } + indent = indent || 0; + var sindent = Array(indent + 1).join('\t'), + r = sindent + '<' + node.tag; + for (var attr in node.attrs) { + var vattr = node.attrs[attr]; + if (typeof(vattr) !== 'string') { + // domains, ... + vattr = JSON.stringify(vattr); + } + vattr = vattr.replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"'); + if (single_quote) { + vattr = vattr.replace(/"/g, "'"); + } + r += ' ' + attr + '="' + vattr + '"'; + } + if (node.children.length) { + r += '>\n'; + var childs = []; + for (var i = 0, ii = node.children.length; i < ii; i++) { + childs.push(openerp.base.json_node_to_xml(node.children[i], single_quote, indent + 1)); + } + r += childs.join('\n'); + r += '\n' + sindent + ''; + return r; + } else { + return r + '/>'; + } +} + }; // vim:et fdc=0 fdl=0 foldnestmax=3 fdm=syntax: diff --git a/addons/base/static/src/xml/base.xml b/addons/base/static/src/xml/base.xml index b03bb044ba4..de95a4294e8 100644 --- a/addons/base/static/src/xml/base.xml +++ b/addons/base/static/src/xml/base.xml @@ -360,8 +360,14 @@

- - + +
@@ -871,10 +877,19 @@
-

- - -

+ + + + + +
+

+ + d.html_error = context.engine.tools.html_escape(d.error.data.fault_code).replace(/\n/g, '
'); +
+ +

+