diff --git a/addons/base/controllers/main.py b/addons/base/controllers/main.py index a1fcc51818c..701bb0f1723 100644 --- a/addons/base/controllers/main.py +++ b/addons/base/controllers/main.py @@ -508,7 +508,7 @@ class DataSet(openerpweb.Controller): @openerpweb.jsonrequest def call(self, req, model, method, args, domain_id=None, context_id=None): - return {'result': self.call_common(req, model, method, args, domain_id, context_id)} + return self.call_common(req, model, method, args, domain_id, context_id) @openerpweb.jsonrequest def call_button(self, req, model, method, args, domain_id=None, context_id=None): @@ -524,20 +524,19 @@ class DataSet(openerpweb.Controller): @openerpweb.jsonrequest def default_get(self, req, model, fields): - m = req.session.model(model) - r = m.default_get(fields, req.session.eval_context(req.context)) - return {'result': r} + Model = req.session.model(model) + return Model.default_get(fields, req.session.eval_context(req.context)) class DataGroup(openerpweb.Controller): _cp_path = "/base/group" @openerpweb.jsonrequest - def read(self, request, model, fields, group_by_fields, domain=None): + def read(self, request, model, fields, group_by_fields, domain=None, sort=None): Model = request.session.model(model) context, domain = eval_context_and_domain(request.session, request.context, domain) return Model.read_group( domain or [], fields, group_by_fields, 0, False, - dict(context, group_by=group_by_fields)) + dict(context, group_by=group_by_fields), sort or False) class View(openerpweb.Controller): _cp_path = "/base/view" @@ -570,10 +569,15 @@ class View(openerpweb.Controller): else: xml = ElementTree.fromstring(arch) fvg['arch'] = Xml2Json.convert_element(xml) - for field in fvg['fields'].values(): - if field.has_key('views') and field['views']: - for view in field["views"].values(): + + for field in fvg['fields'].itervalues(): + if field.get('views'): + for view in field["views"].itervalues(): self.process_view(session, view, None, transform) + if field.get('domain'): + field["domain"] = self.parse_domain(field["domain"], session) + if field.get('context'): + field["context"] = self.parse_context(field["context"], session) @openerpweb.jsonrequest def add_custom(self, request, view_id, arch): @@ -611,27 +615,38 @@ class View(openerpweb.Controller): self.parse_domains_and_contexts(elem, session) return root - def parse_domain(self, elem, attr_name, session): - """ Parses an attribute of the provided name as a domain, transforms it + def parse_domain(self, domain, session): + """ Parses an arbitrary string containing a domain, transforms it to either a literal domain or a :class:`openerpweb.nonliterals.Domain` - :param elem: the node being parsed - :type param: xml.etree.ElementTree.Element - :param str attr_name: the name of the attribute which should be parsed + :param domain: the domain to parse, if the domain is not a string it is assumed to + be a literal domain and is returned as-is :param session: Current OpenERP session :type session: openerpweb.openerpweb.OpenERPSession """ - domain = elem.get(attr_name, '').strip() - if domain: - try: - elem.set( - attr_name, - openerpweb.ast.literal_eval( - domain)) - except ValueError: - # not a literal - elem.set(attr_name, - openerpweb.nonliterals.Domain(session, domain)) + if not isinstance(domain, (str, unicode)): + return domain + try: + return openerpweb.ast.literal_eval(domain) + except ValueError: + # not a literal + return openerpweb.nonliterals.Domain(session, domain) + + def parse_context(self, context, session): + """ Parses an arbitrary string containing a context, transforms it + to either a literal context or a :class:`openerpweb.nonliterals.Context` + + :param context: the context to parse, if the context is not a string it is assumed to + be a literal domain and is returned as-is + :param session: Current OpenERP session + :type session: openerpweb.openerpweb.OpenERPSession + """ + if not isinstance(context, (str, unicode)): + return context + try: + return openerpweb.ast.literal_eval(context) + except ValueError: + return openerpweb.nonliterals.Context(session, context) def parse_domains_and_contexts(self, elem, session): """ Converts domains and contexts from the view into Python objects, @@ -644,18 +659,14 @@ class View(openerpweb.Controller): non-literal objects :type session: openerpweb.openerpweb.OpenERPSession """ - self.parse_domain(elem, 'domain', session) - self.parse_domain(elem, 'filter_domain', session) + for el in ['domain', 'filter_domain']: + domain = elem.get(el, '').strip() + if domain: + elem.set(el, self.parse_domain(domain, session)) for el in ['context', 'default_get']: context_string = elem.get(el, '').strip() if context_string: - try: - elem.set(el, - openerpweb.ast.literal_eval(context_string)) - except ValueError: - elem.set(el, - openerpweb.nonliterals.Context( - session, context_string)) + elem.set(el, self.parse_context(context_string, session)) class FormView(View): _cp_path = "/base/formview" diff --git a/addons/base/static/src/css/base.css b/addons/base/static/src/css/base.css index e91bbd922ec..d220c8b406b 100644 --- a/addons/base/static/src/css/base.css +++ b/addons/base/static/src/css/base.css @@ -577,6 +577,10 @@ background: linear-gradient(top, #ffffff 0%,#d8d8d8 11%,#afafaf 86%,#333333 91%, display: none; } +.openerp .searchview_extended_delete_prop { + text-decoration: none; +} + .openerp .searchview_extended_delete_group span, .openerp .searchview_extended_delete_prop span { font-size: 0.9em; @@ -609,10 +613,14 @@ background: linear-gradient(top, #ffffff 0%,#d8d8d8 11%,#afafaf 86%,#333333 91%, .openerp .oe-listview td.oe-record-delete { text-align: right; } -.openerp .oe-listview th.oe-sortable, -.openerp .oe-listview th.oe-sortable .ui-icon { +.openerp .oe-listview th.oe-sortable { cursor: pointer; } +.openerp .oe-listview th.oe-sortable .ui-icon { + height: 1em; + display: inline; + display: inline-block; +} .openerp .oe-listview .oe-field-cell { cursor: pointer; diff --git a/addons/base/static/src/js/chrome.js b/addons/base/static/src/js/chrome.js index 84f2747166c..c6bf0adada4 100644 --- a/addons/base/static/src/js/chrome.js +++ b/addons/base/static/src/js/chrome.js @@ -365,7 +365,7 @@ openerp.base.Session = openerp.base.BasicController.extend( /** @lends openerp.b params: params, id:null }).then(function () {deferred.resolve.apply(deferred, arguments);}, - function(error) {deferred.reject(error, $.Event());}); + function(error) {deferred.reject(error, $.Event());}); return deferred.fail(function() { deferred.fail(function(error, event) { if (!event.isDefaultPrevented()) { @@ -398,31 +398,35 @@ openerp.base.Session = openerp.base.BasicController.extend( /** @lends openerp.b }, url); var deferred = $.Deferred(); $.ajax(ajax).done(function(response, textStatus, jqXHR) { - self.on_rpc_response(); - if (response.error) { - if (response.error.data.type == "session_invalid") { - self.uid = false; - self.on_session_invalid(function() { - self.rpc(url, payload.params, - function() {deferred.resolve.apply(deferred, arguments);}, - function(error, event) {event.preventDefault(); - deferred.reject.apply(deferred, arguments);}); - }); - } else { - deferred.reject(response.error); - } - } else { - deferred.resolve(response["result"], textStatus, jqXHR); - } - }).fail(function(jqXHR, textStatus, errorThrown) { - self.on_rpc_response(); - var error = { - code: -32098, - message: "XmlHttpRequestError " + errorThrown, - data: {type: "xhr"+textStatus, debug: jqXHR.responseText, objects: [jqXHR, errorThrown] } - }; - deferred.reject(error); + self.on_rpc_response(); + if (!response.error) { + deferred.resolve(response["result"], textStatus, jqXHR); + return; + } + if (response.error.data.type !== "session_invalid") { + deferred.reject(response.error); + return; + } + self.uid = false; + self.on_session_invalid(function() { + self.rpc(url, payload.params, + function() { + deferred.resolve.apply(deferred, arguments); + }, + function(error, event) { + event.preventDefault(); + deferred.reject.apply(deferred, arguments); + }); }); + }).fail(function(jqXHR, textStatus, errorThrown) { + self.on_rpc_response(); + var error = { + code: -32098, + message: "XmlHttpRequestError " + errorThrown, + data: {type: "xhr"+textStatus, debug: jqXHR.responseText, objects: [jqXHR, errorThrown] } + }; + deferred.reject(error); + }); return deferred.promise(); }, on_rpc_request: function() { diff --git a/addons/base/static/src/js/data.js b/addons/base/static/src/js/data.js index a49e6cf901f..c1b64b93307 100644 --- a/addons/base/static/src/js/data.js +++ b/addons/base/static/src/js/data.js @@ -1,6 +1,23 @@ openerp.base.data = function(openerp) { +/** + * Serializes the sort criterion array of a dataset into a form which can be + * consumed by OpenERP's RPC APIs. + * + * @param {Array} criterion array of fields, from first to last criteria, prefixed with '-' for reverse sorting + * @returns {String} SQL-like sorting string (``ORDER BY``) clause + */ +openerp.base.serialize_sort = function (criterion) { + return _.map(criterion, + function (criteria) { + if (criteria[0] === '-') { + return criteria.slice(1) + ' DESC'; + } + return criteria + ' ASC'; + }).join(', '); +}; + openerp.base.DataGroup = openerp.base.Controller.extend( /** @lends openerp.base.DataGroup# */{ /** * Management interface between views and grouped collections of OpenERP @@ -130,7 +147,8 @@ openerp.base.ContainerDataGroup = openerp.base.DataGroup.extend( context: this.context, domain: this.domain, fields: _.uniq(this.group_by.concat(fields)), - group_by_fields: this.group_by + group_by_fields: this.group_by, + sort: openerp.base.serialize_sort(this.sort) }, function () { }).then(function (response) { var data_groups = _(response).map( _.bind(self.transform_group, self)); @@ -174,7 +192,7 @@ openerp.base.ContainerDataGroup = openerp.base.DataGroup.extend( self.session, self.model, group.__domain, child_context, child_context.group_by, self.level + 1), - group); + group, {sort: self.sort}); })); }); } @@ -197,8 +215,8 @@ openerp.base.GrouplessDataGroup = openerp.base.DataGroup.extend( }, list: function (fields, ifGroups, ifRecords) { ifRecords(_.extend( - new openerp.base.DataSetSearch(this.session, this.model), - {domain: this.domain, context: this.context})); + new openerp.base.DataSetSearch(this.session, this.model), + {domain: this.domain, context: this.context, _sort: this.sort})); } }); @@ -378,7 +396,7 @@ openerp.base.DataSetStatic = openerp.base.DataSet.extend({ 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); + return this.read_ids(this.ids.slice(offset, end_pos), fields, callback); }, set_ids: function (ids) { this.ids = ids; @@ -415,7 +433,7 @@ openerp.base.DataSetSearch = openerp.base.DataSet.extend({ // return read_ids(ids.slice(start,start+limit),fields,callback) } } - this.rpc('/base/dataset/search_read', { + return this.rpc('/base/dataset/search_read', { model: this.model, fields: fields, domain: this.domain, @@ -426,7 +444,9 @@ openerp.base.DataSetSearch = openerp.base.DataSet.extend({ }, function (result) { self.ids = result.ids; self.offset = offset; - callback(result.records); + if (callback) { + callback(result.records); + } }); }, /** @@ -444,16 +464,12 @@ openerp.base.DataSetSearch = openerp.base.DataSet.extend({ */ sort: function (field, force_reverse) { if (!field) { - return _.map(this._sort, function (criteria) { - if (criteria[0] === '-') { - return criteria.slice(1) + ' DESC'; - } - return criteria + ' ASC'; - }).join(', '); + return openerp.base.serialize_sort(this._sort); } - var reverse = force_reverse || (this._sort[0] === field); - this._sort = _.without(this._sort, field, '-' + field); + this._sort.splice.apply( + this._sort, [0, this._sort.length].concat( + _.without(this._sort, field, '-' + field))); this._sort.unshift((reverse ? '-' : '') + field); return undefined; diff --git a/addons/base/static/src/js/form.js b/addons/base/static/src/js/form.js index 60511c49c17..523f47e5f33 100644 --- a/addons/base/static/src/js/form.js +++ b/addons/base/static/src/js/form.js @@ -79,6 +79,10 @@ openerp.base.FormView = openerp.base.View.extend( /** @lends openerp.base.FormV 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.$form_header.find('button.oe_get_xml_view').click(function() { + $('' + openerp.base.json_node_to_xml(self.fields_view.arch, true) + '').dialog({ width: '95%', height: 600}); + }); + this.view_manager.sidebar.set_toolbar(data.fields_view.toolbar); this.has_been_loaded.resolve(); }, @@ -235,6 +239,7 @@ openerp.base.FormView = openerp.base.View.extend( /** @lends openerp.base.FormV if (result.value) { for (var f in result.value) { var field = this.fields[f]; + // If field is not defined in the view, just ignore it if (field) { var value = result.value[f]; processed.push(field.name); @@ -245,8 +250,6 @@ openerp.base.FormView = openerp.base.View.extend( /** @lends openerp.base.FormV this.do_onchange(field, processed); } } - } else { - // this is a common case, the normal behavior should be to ignore it } } this.on_form_changed(); @@ -262,16 +265,15 @@ openerp.base.FormView = openerp.base.View.extend( /** @lends openerp.base.FormV }); } if (result.domain) { - // Will be removed ? + // TODO: } this.ready = true; }, on_button_new: function() { var self = this; $.when(this.has_been_loaded).then(function() { - self.dataset.default_get(_.keys(self.fields_view.fields), function(result) { - self.on_record_loaded(result.result); - }); + self.dataset.default_get( + _.keys(self.fields_view.fields), self.on_record_loaded); }); }, /** @@ -826,8 +828,10 @@ openerp.base.form.Field = openerp.base.form.Widget.extend({ * the fields'context with the action's context. */ build_context: function() { - var a_context = this.view.dataset.get_context() || {}; - var f_context = this.field.context || {}; + // I previously belevied contexts should be herrited, but now I doubt it + //var a_context = this.view.dataset.get_context() || {}; + var f_context = this.field.context || null; + // maybe the default_get should only be used when we do a default_get? var v_context1 = this.node.attrs.default_get || {}; var v_context2 = this.node.attrs.context || {}; var v_context = new openerp.base.CompoundContext(v_context1, v_context2); @@ -835,17 +839,19 @@ openerp.base.form.Field = openerp.base.form.Widget.extend({ var fields_values = this._build_view_fields_values(); v_context.set_eval_context(fields_values); } - var ctx = new openerp.base.CompoundContext(a_context, f_context, v_context); + // if there is a context on the node, overrides the model's context + var ctx = f_context || v_context; return ctx; }, build_domain: function() { - var f_domain = this.field.domain || []; + var f_domain = this.field.domain || null; var v_domain = this.node.attrs.domain || []; if (!(v_domain instanceof Array) || true) { //TODO niv: remove || true var fields_values = this._build_view_fields_values(); v_domain = new openerp.base.CompoundDomain(v_domain).set_eval_context(fields_values); } - return new openerp.base.CompoundDomain(f_domain, v_domain); + // if there is a domain on the node, overrides the model's domain + return f_domain || v_domain; } }); @@ -1239,6 +1245,21 @@ openerp.base.form.FieldSelection = openerp.base.form.Field.extend({ }); })(); +openerp.base.form.dialog = function(content, options) { + options = _.extend({ + autoOpen: true, + width: '90%', + height: '90%', + min_width: '800px', + min_height: '600px' + }, options || {}); + options.autoOpen = true; + var dialog = new openerp.base.Dialog(null, options); + dialog.$dialog = $(content).dialog(dialog.options); + console.log(dialog.options) + return dialog.$dialog; +} + openerp.base.form.FieldMany2One = openerp.base.form.Field.extend({ init: function(view, node) { this._super(view, node); @@ -1827,10 +1848,7 @@ openerp.base.form.SelectCreatePopup = openerp.base.BaseWidget.extend({ this.context = context || {}; this.options = _.defaults(options || {}, {"initial_view": "search", "auto_create": true}); this.initial_ids = this.options.initial_ids; - jQuery(this.render()).dialog({title: '', - modal: true, - width: 960, - height: 600}); + openerp.base.form.dialog(this.render()); this.start(); }, start: function() { @@ -2019,10 +2037,10 @@ openerp.base.form.FormOpenPopup = openerp.base.BaseWidget.extend({ } }); -openerp.base.form.FieldReference = openerp.base.form.Field.extend({ +openerp.base.form.FieldReference = openerp.base.form.FieldChar.extend({ init: function(view, node) { this._super(view, node); - this.template = "FieldReference"; + //this.template = "FieldReference"; } }); @@ -2040,6 +2058,10 @@ openerp.base.form.FieldBinary = openerp.base.form.Field.extend({ }, set_value_from_ui: function() { }, + update_dom: function() { + this._super.apply(this, arguments); + this.$element.find('.oe-binary').toggle(!this.readonly); + }, human_filesize : function(size) { var units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']; var i = 0; @@ -2137,6 +2159,13 @@ openerp.base.form.FieldBinaryImage = openerp.base.form.FieldBinary.extend({ this._super.apply(this, arguments); this.$image = this.$element.find('img.oe-binary-image'); }, + set_value: function(value) { + this._super.apply(this, arguments); + this.set_image_maxwidth(); + var url = '/base/binary/image?session_id=' + this.session.session_id + '&model=' + + this.view.dataset.model +'&id=' + (this.view.datarecord.id || '') + '&field=' + this.name + '&t=' + (new Date().getTime()) + this.$image.attr('src', url); + }, set_image_maxwidth: function() { this.$image.css('max-width', this.$element.width()); }, @@ -2152,13 +2181,6 @@ openerp.base.form.FieldBinaryImage = openerp.base.form.FieldBinary.extend({ on_clear: function() { this._super.apply(this, arguments); this.$image.attr('src', '/base/static/src/img/placeholder.png'); - }, - set_value: function(value) { - this._super.apply(this, arguments); - this.set_image_maxwidth(); - var url = '/base/binary/image?session_id=' + this.session.session_id + '&model=' + - this.view.dataset.model +'&id=' + (this.view.datarecord.id || '') + '&field=' + this.name + '&t=' + (new Date().getTime()) - this.$image.attr('src', url); } }); diff --git a/addons/base/static/src/js/list-editable.js b/addons/base/static/src/js/list-editable.js index a460dfcff41..4aa06cc8152 100644 --- a/addons/base/static/src/js/list-editable.js +++ b/addons/base/static/src/js/list-editable.js @@ -247,7 +247,12 @@ openerp.base.list.editable = function (openerp) { this.render_row_as_form(); } }); - openerp.base.list = {form: {}}; + if (!openerp.base.list) { + openerp.base.list = {}; + } + if (!openerp.base.list.form) { + openerp.base.list.form = {}; + } openerp.base.list.form.WidgetFrame = openerp.base.form.WidgetFrame.extend({ template: 'ListView.row.frame' }); diff --git a/addons/base/static/src/js/list.js b/addons/base/static/src/js/list.js index 7922f690784..49cd10c10b6 100644 --- a/addons/base/static/src/js/list.js +++ b/addons/base/static/src/js/list.js @@ -1,5 +1,50 @@ openerp.base.list = function (openerp) { openerp.base.views.add('list', 'openerp.base.ListView'); +openerp.base.list = { + /** + * Formats the rendring of a given value based on its field type + * + * @param {Object} row_data record whose values should be displayed in the cell + * @param {Object} column column descriptor + * @param {"button"|"field"} column.tag base control type + * @param {String} column.type widget type for a field control + * @param {String} [column.string] button label + * @param {String} [column.icon] button icon + * @param {String} [value_if_empty=''] what to display if the field's value is ``false`` + */ + render_cell: function (row_data, column, value_if_empty) { + var attrs = column.modifiers_for(row_data); + if (attrs.invisible) { return ''; } + if (column.tag === 'button') { + return [ + '' + ].join('') + } + + var record = row_data[column.id]; + if (record.value === false) { + return value_if_empty === undefined ? '' : value_if_empty; + } + switch (column.widget || column.type) { + case 'many2one': + // name_get value format + return record.value[1]; + case 'float_time': + return _.sprintf("%02d:%02d", + Math.floor(record.value), + Math.round((record.value % 1) * 60)); + case 'progressbar': + return _.sprintf( + '%.2f%%', + record.value, record.value); + default: + return record.value; + } + } +}; openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListView# */ { defaults: { // records can be selected one by one @@ -147,8 +192,6 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi this.setup_columns(this.fields_view.fields, grouped); - if (!this.fields_view.sorted) { this.fields_view.sorted = {}; } - this.$element.html(QWeb.render("ListView", this)); // Head hook @@ -158,13 +201,20 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi this.$element.find('.oe-list-delete') .attr('disabled', true) .click(this.do_delete_selected); - this.$element.find('thead').delegate('th[data-id]', 'click', function (e) { + this.$element.find('thead').delegate('th.oe-sortable[data-id]', 'click', function (e) { e.stopPropagation(); - self.dataset.sort($(this).data('id')); + var $this = $(this); + self.dataset.sort($this.data('id')); + if ($this.find('span').length) { + $this.find('span').toggleClass( + 'ui-icon-triangle-1-s ui-icon-triangle-1-n'); + } else { + $this.append('') + .siblings('.oe-sortable').find('span').remove(); + } - // TODO: should only reload content (and set the right column to a sorted display state) - self.reload_view(); + self.reload_content(); }); this.$element.find('.oe-list-pager') @@ -263,6 +313,9 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi 'invisible': domain_computer(modifiers.invisible, fields) }; }; + if (modifiers['tree_invisible']) { + column.invisible = '1'; + } } else { column.modifiers_for = noop; } @@ -401,6 +454,7 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi this.session, this.model, results.domain, results.context, results.group_by); + this.groups.datagroup.sort = this.dataset._sort; if (_.isEmpty(results.group_by) && !results.context['group_by_no_leaf']) { results.group_by = null; @@ -634,7 +688,7 @@ openerp.base.ListView.List = Class.extend( /** @lends openerp.base.ListView.List this.$current = this.$_element.clone(true); this.$current.empty().append( QWeb.render('ListView.rows', _.extend({ - render_cell: this.render_cell}, this))); + render_cell: openerp.base.list.render_cell}, this))); }, /** * Gets the ids of all currently selected records, if any @@ -765,49 +819,9 @@ openerp.base.ListView.List = Class.extend( /** @lends openerp.base.ListView.List row: this.rows[record_index], row_parity: (record_index % 2 === 0) ? 'even' : 'odd', row_index: record_index, - render_cell: this.render_cell + render_cell: openerp.base.list.render_cell }); }, - /** - * Formats the rendring of a given value based on its field type - * - * @param {Object} row_data record whose values should be displayed in the cell - * @param {Object} column column descriptor - * @param {"button"|"field"} column.tag base control type - * @param {String} column.type widget type for a field control - * @param {String} [column.string] button label - * @param {String} [column.icon] button icon - */ - render_cell: function (row_data, column) { - var attrs = column.modifiers_for(row_data); - if (attrs.invisible) { return ''; } - if (column.tag === 'button') { - return [ - '' - ].join('') - } - - var record = row_data[column.id]; - if (record.value === false) { return ''; } - switch (column.widget || column.type) { - case 'many2one': - // name_get value format - return record.value[1]; - case 'float_time': - return _.sprintf("%02d:%02d", - Math.floor(record.value), - Math.round((record.value % 1) * 60)); - case 'progressbar': - return _.sprintf( - '%.2f%%', - record.value, record.value); - default: - return record.value; - } - }, /** * Stops displaying the records matching the provided ids. * @@ -959,10 +973,15 @@ openerp.base.ListView.Groups = Class.extend( /** @lends openerp.base.ListView.Gr placeholder.appendChild($row[0]); var $group_column = $('').appendTo($row); + // Don't fill this if group_by_no_leaf but no group_by if (group.grouped_on) { - // Don't fill this if group_by_no_leaf but no group_by - $group_column - .text((group.value instanceof Array ? group.value[1] : group.value)); + var row_data = {}; + row_data[group.grouped_on] = group; + var group_column = _(self.columns).detect(function (column) { + return column.id === group.grouped_on; }); + $group_column.text(openerp.base.list.render_cell( + row_data, group_column, "Undefined" + )); if (group.openable) { // Make openable if not terminal group & group_by_no_leaf $group_column diff --git a/addons/base/static/src/xml/base.xml b/addons/base/static/src/xml/base.xml index 603c75cdb74..8f9d063ffba 100644 --- a/addons/base/static/src/xml/base.xml +++ b/addons/base/static/src/xml/base.xml @@ -283,10 +283,8 @@ - - - - + @@ -336,8 +334,8 @@ -

+

-
+
diff --git a/addons/base_graph/static/src/xml/base_graph.xml b/addons/base_graph/static/src/xml/base_graph.xml index f8dee0a91c6..cb5b1b2b0c4 100644 --- a/addons/base_graph/static/src/xml/base_graph.xml +++ b/addons/base_graph/static/src/xml/base_graph.xml @@ -1,6 +1,6 @@ \ No newline at end of file +