diff --git a/addons/web/__openerp__.py b/addons/web/__openerp__.py index 937442d13c5..f335c52bb10 100644 --- a/addons/web/__openerp__.py +++ b/addons/web/__openerp__.py @@ -77,6 +77,7 @@ This module provides the core of the OpenERP Web Client. "static/test/class.js", "static/test/registry.js", "static/test/form.js", + "static/test/data.js", "static/test/list-utils.js", "static/test/formats.js", "static/test/rpc.js", diff --git a/addons/web/static/src/js/data.js b/addons/web/static/src/js/data.js index cfcd86307ba..a36441add1c 100644 --- a/addons/web/static/src/js/data.js +++ b/addons/web/static/src/js/data.js @@ -112,24 +112,27 @@ instance.web.Query = instance.web.Class.extend({ * @returns {jQuery.Deferred> | null} */ group_by: function (grouping) { - if (grouping === undefined) { - return null; + var ctx = instance.web.pyeval.eval( + 'context', this._model.context(this._context)); + + // undefined passed in explicitly (!) + if (_.isUndefined(grouping)) { + grouping = []; } if (!(grouping instanceof Array)) { grouping = _.toArray(arguments); } - if (_.isEmpty(grouping)) { return null; } + if (_.isEmpty(grouping) && !ctx['group_by_no_leaf']) { + return null; + } var self = this; - - var ctx = instance.web.pyeval.eval( - 'context', this._model.context(this._context)); return this._model.call('read_group', { groupby: grouping, fields: _.uniq(grouping.concat(this._fields || [])), domain: this._model.domain(this._filter), - context: this._model.context(this._context), + context: ctx, offset: this._offset, limit: this._limit, orderby: instance.web.serialize_sort(this._order_by) || false @@ -325,7 +328,7 @@ instance.web.Model = instance.web.Class.extend({ * Fetches the model's domain, combined with the provided domain if any * * @param {Array} [domain] to combine with the model's internal domain - * @returns The model's internal domain, or the AND-ed union of the model's internal domain and the provided domain + * @returns {instance.web.CompoundDomain} The model's internal domain, or the AND-ed union of the model's internal domain and the provided domain */ domain: function (domain) { if (!domain) { return this._domain; } @@ -337,7 +340,7 @@ instance.web.Model = instance.web.Class.extend({ * combined with the provided context if any * * @param {Object} [context] to combine with the model's internal context - * @returns The union of the user's context and the model's internal context, as well as the provided context if any. In that order. + * @returns {instance.web.CompoundContext} The union of the user's context and the model's internal context, as well as the provided context if any. In that order. */ context: function (context) { return new instance.web.CompoundContext( diff --git a/addons/web/static/test/data.js b/addons/web/static/test/data.js new file mode 100644 index 00000000000..6f1e5184063 --- /dev/null +++ b/addons/web/static/test/data.js @@ -0,0 +1,76 @@ +openerp.testing.section('data.model.group_by', { + rpc: 'mock', + dependencies: ['web.data'], +}, function (test) { + var group_result = [{ + bar: 3, bar_count: 5, __context: {}, __domain: [['bar', '=', 3]], + }, { + bar: 5, bar_count: 3, __context: {}, __domain: [['bar', '=', 5]], + }, { + bar: 8, bar_count: 0, __context: {}, __domain: [['bar', '=', 8]], + }]; + test('basic', {asserts: 7}, function (instance, $fix, mock) { + var m = new instance.web.Model('foo'); + mock('foo:read_group', function (args, kwargs) { + deepEqual(kwargs.fields, ['bar'], + "should read grouping field"); + deepEqual(kwargs.groupby, ['bar'], + "should have single grouping field"); + return group_result; + }); + mock('/web/dataset/search_read', function (args) { + deepEqual(args.params.domain, [['bar', '=', 3]], + "should have domain matching that of group_by result"); + return {records: [ + {bar: 3, id: 1}, + {bar: 3, id: 2}, + {bar: 3, id: 4}, + {bar: 3, id: 8}, + {bar: 3, id: 16} + ], length: 5}; + }); + + return m.query().group_by('bar') + .then(function (groups) { + ok(groups, "should have data"); + equal(groups.length, 3, "should have three results"); + var first = groups[0]; + ok(first.attributes.has_children, "should have children"); + return first.query().all(); + }).done(function (first) { + equal(first.length, 5, "should have 5 records") + }); + }); + test('noleaf', {asserts: 5}, function (instance, $fix, mock) { + var m = new instance.web.Model('foo', {group_by_no_leaf: true}); + mock('foo:read_group', function (args, kwargs) { + deepEqual(kwargs.fields, ['bar'], + "should read grouping field"); + deepEqual(kwargs.groupby, ['bar'], + "should have single grouping field"); + + return group_result; + }); + return m.query().group_by('bar') + .then(function (groups) { + ok(groups, "should have data"); + equal(groups.length, 3, "should have three results"); + ok(!groups[0].attributes.has_children, + "should not have children because no_leaf"); + }) + }); + test('nogroup', {rpc: false}, function (instance, $f, mock) { + var m = new instance.web.Model('foo'); + strictEqual(m.query().group_by(), null, "should not group"); + }); + test('empty.noleaf', {asserts: 1}, function (instance, $f, mock) { + var m = new instance.web.Model('foo', {group_by_no_leaf: true}); + mock('foo:read_group', function (args, kwargs) { + return [{__context: [], __domain: []}]; + }); + return m.query().group_by().done(function (groups) { + strictEqual(groups.length, 1, + "should generate a single fake-ish group"); + }); + }); +});