From efa2ac293908b1ab9153b8fbc4e013d5bb0ae013 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Tue, 22 Mar 2011 20:10:43 +0100 Subject: [PATCH] [ADD] ids and set methods on DataSet bzr revid: xmo@openerp.com-20110322191043-d2s7qz2riuv7tjkd --- addons/base/controllers/main.py | 2 +- addons/base/static/openerp/js/base_views.js | 209 ++++++++++++++++---- addons/base/static/openerp/js/tests.js | 146 +++++++++++++- 3 files changed, 316 insertions(+), 41 deletions(-) diff --git a/addons/base/controllers/main.py b/addons/base/controllers/main.py index 9c3aeb55b12..185f2d18bca 100644 --- a/addons/base/controllers/main.py +++ b/addons/base/controllers/main.py @@ -167,7 +167,7 @@ class DataSet(openerpweb.Controller): @openerpweb.jsonrequest def fields(self, req, model): - return {'fields': req.session.model(model).fields_get(False)} + return {'fields': req.session.model(model).fields_get()} @openerpweb.jsonrequest def load(self, req, model, domain=[], fields=['id']): diff --git a/addons/base/static/openerp/js/base_views.js b/addons/base/static/openerp/js/base_views.js index d9db2348066..156b11a9f61 100644 --- a/addons/base/static/openerp/js/base_views.js +++ b/addons/base/static/openerp/js/base_views.js @@ -94,60 +94,195 @@ openerp.base.EmbbededView = openerp.base.Controller.extend({ // to replace Action }); +/** + * Management interface between views and the collection of selected OpenERP + * records (represents the view's state?) + */ openerp.base.DataSet = openerp.base.Controller.extend({ - init: function(session, element_id, model) { - this._super(session, element_id); - this.model = model; - // SHOULD USE THE ONE FROM FIELDS VIEW GET BECAUSE OF SELECTION - // Should merge those 2 - this.model_fields = null; - this.fields = []; + init: function(session, model) { + this._super(session); + this._model = model; - this.domain = []; - this.context = {}; - this.order = ""; - this.count = null; - this.ids = []; - this.values = {}; -/* - group_by - rows record - fields of row1 field fieldname - { type: value: text: text_format: text_completions type_*: a -*/ + this._fields = null; + + this._ids = []; + this._active_ids = null; + this._active_id_index = 0; + + this._sort = []; + this._domain = []; + this._context = {}; }, start: function() { - this.rpc("/base/dataset/fields", {"model":this.model}, this.on_fields); + // TODO: fields_view_get fields selection? + this.rpc("/base/dataset/fields", {"model":this._model}, this.on_fields); }, on_fields: function(result) { - this.model_fields = result.fields; + this._fields = result._fields; this.on_ready(); }, - do_load: function(offset, limit) { - this.rpc("/base/dataset/load", {model: this.model, fields: this.fields }, this.on_loaded); + + /** + * Fetch all the records selected by this DataSet, based on its domain + * and context. + * + * Fires the on_ids event. + * + * @param {Number} [offset=0] The index from which selected records should be returned + * @param {Number} [limit=null] The maximum number of records to return + * @returns itself + */ + ids: function (offset, limit) { + offset = offset || 0; + limit = limit || null; + this.rpc('/base/dataset/load', { + model: this._model, + fields: this._fields, + domain: this._domain, + context: this._context, + sort: this._sort, + offset: offset, + limit: limit + }, _.bind(function (records) { + var data_records = _.map( + records, function (record) { + return new openerp.base.DataRecord( + this.session, this._model, + this._fields, record); + }, this); + + this.on_ids(data_records, { + offset: offset, + limit: limit, + domain: this._domain, + context: this._context, + sort: this._sort + }); + }, this)); + return this; }, - on_loaded: function(data) { - this.ids = data.ids; - this.values = data.values; + /** + * @event + * + * Fires after the DataSet fetched the records matching its internal ids selection + * + * @param {Array} records An array of the DataRecord fetched + * @param event The on_ids event object + * @param {Number} event.offset the offset with which the original DataSet#ids call was performed + * @param {Number} event.limit the limit set on the original DataSet#ids call + * @param {Array} event.domain the domain set on the DataSet before DataSet#ids was called + * @param {Object} event.context the context set on the DataSet before DataSet#ids was called + * @param {Array} event.sort the sorting criteria used to get the ids + */ + on_ids: function (records, event) { + }, - on_reloaded: function(ids) { + + /** + * Fetch all the currently active records for this DataSet (records selected via DataSet#select) + * + * @param {Number} [offset=0] The index from which selected records should be returned + * @param {Number} [limit=-1] The maximum number of records to return + * @returns itself + */ + active_ids: function (offset, limit) { + return this; + }, + /** + * @event + * + * Fires after the DataSet fetched the records matching its internal active ids selection + * + * @param {Array} records An array of the DataRecord fetched + * @param event The on_active_ids event object + * @param {Number} event.offset the offset with which the original DataSet#ids call was performed + * @param {Number} event.limit the limit set on the original DataSet#ids call + * @param {Array} event.sort the sorting criteria used to get the ids + */ + on_active_ids: function (records, event) { + + }, + + /** + * Fetches the current active record for this DataSet + * + * @returns itself + */ + active_id: function () { + return this; + }, + /** + * Fires after the DataSet fetched the record matching the current active record + * + * @param record the record matching the provided id, or null if there is no record for this id + */ + on_active_id: function (record) { + + }, + + /** + * Configures the DataSet + * + * @param options DataSet options + * @param {Array} options.domain the domain to assign to this DataSet for filtering + * @param {Object} options.context the context this DataSet should use during its calls + * @param {Array} options.sort the sorting criteria for this DataSet + * @returns itself + */ + set: function (options) { + if (options.domain) { + this._domain = _.clone(options.domain); + } + if (options.context) { + this._context = _.clone(options.context); + } + if (options.sort) { + this._sort = _.clone(options.sort); + } + return this; + }, + + /** + * Activates the previous id in the active sequence. If there is no previous id, wraps around to the last one + * @returns itself + */ + prev: function () { + return this; + }, + /** + * Activates the next id in the active sequence. If there is no next id, wraps around to the first one + * @returns itself + */ + next: function () { + return this; + }, + + /** + * Sets active_ids and/or active_id by value: + * + * * If only ids is provided + * - Activates all ids part of the current selection + * - Sets active_id to be the first id of the selection + * * If only id is provided, activates this id (for both active_id and active_ids) + * * If both id and ids are provided + * - Activates all ids part of the current selection + * - If id is part of the active ids, activates it, otherwise see first option + * + * @param {Array} [ids] the list of ids to activate + * @param {Object} [id] the id to activate + */ + select: function (ids, id) { + return this; } }); openerp.base.DataRecord = openerp.base.Controller.extend({ - init: function(session, model, fields) { + init: function(session, model, fields, values) { this._super(session, null); this.model = model; - this.id = null; + this.id = values.id || null; this.fields = fields; - this.values = {}; - }, - load: function(id) { - this.id = id; - this.rpc("/base/datarecord/load", {"model": this.model, "id": id, "fields": "todo"}, this.on_loaded); - }, - on_loaded: function(result) { - this.values = result.value; + this.values = values; }, on_change: function() { }, diff --git a/addons/base/static/openerp/js/tests.js b/addons/base/static/openerp/js/tests.js index fe5a2998079..46d08ff8691 100644 --- a/addons/base/static/openerp/js/tests.js +++ b/addons/base/static/openerp/js/tests.js @@ -19,6 +19,18 @@ var module; * @param {Function} test the testing code to run, holding a sequence of assertions (at least one) */ var test; +/** + * @function + * Defines an asynchronous test: equivalent to calling stop() at the start of + * a normal test(). + * + * The test code needs to restart the test runner via start() + * + * @param {String} name the name of the test + * @param {Number} [expected] number of assertions expected to run in this test (useful for asynchronous tests) + * @param {Function} test the testing code to run, holding a sequence of assertions (at least one) + */ +var asyncTest; /** * @function * The most basic boolean assertion (~assertTrue or assert). @@ -42,7 +54,7 @@ var ok; var equal; /** * @function - * Inequality assertion (~assertEqual) + * Inequality assertion (~assertNotEqual) * * Passes if the arguments are different (via !=) * @@ -50,7 +62,135 @@ var equal; * @param {Object} expected the object to check against * @param {String} [message] message output with the assertion result */ -var notEqual, deepEqual, notDeepEqual, - strictEqual, notStrictEqual, raises, start, stop; +var notEqual; +/** + * @function + * Recursive equality assertion. + * + * Works on primitive types using === and traversing through + * Objects and Arrays as well checking their components + * + * @param {Object} actual the object to check for correctness (processing result) + * @param {Object} expected the object to check against + * @param {String} [message] message output with the assertion result + */ +var deepEqual; +var notDeepEqual, strictEqual, notStrictEqual, raises; +/** + * @function + * Starts running the test runner again from the point where it was + * stopped. + * + * Used to resume testing after a callback. + */ +var start; +/** + * @function + * Stops the test runner in order to wait for an asynchronous test to run + * + * @param {Number} [timeout] fails the test after the timeout triggers, only for debugging tests + */ +var stop; + +var Session = function () { + return { + rpc: function (_url, params, on_success) { + setTimeout(on_success); + } + }; +}; $(document).ready(function () { + var openerp; + module("ids_callback", { + setup: function () { + openerp = window.openerp.init(); + } + }); + asyncTest("Baseline event attributes", 6, function () { + var dataset = new openerp.base.DataSet( + new Session()); + dataset.on_ids.add(function (records, event) { + deepEqual(records, [], 'No records returned'); + equal(event.offset, 0, 'No offset set in call'); + equal(event.limit, null, 'No limit set in call'); + deepEqual(event.domain, [], 'No domain on the dataset'); + deepEqual(event.context, {}, 'No context on the dataset'); + deepEqual(event.sort, [], 'The dataset is not sorted'); + start(); + }); + dataset.ids(); + }); + asyncTest("Offset and limit", 2, function () { + var dataset = new openerp.base.DataSet( + new Session()); + dataset.on_ids.add(function (records, event) { + equal(event.offset, 20); + equal(event.limit, 42); + start(); + }); + dataset.ids(20, 42); + }); + asyncTest("Domain and context propagation", 3, function () { + var dataset = new openerp.base.DataSet( + new Session()); + var domain_value = [['foo', '=', 'bar']]; + var context_value= {active_id:3, active_ids:42}; + var sort_value = ['foo']; + dataset.on_ids.add(function (records, event) { + deepEqual(event.domain, domain_value); + deepEqual(event.context, context_value); + deepEqual(event.sort, sort_value); + start(); + }); + dataset.set({ + domain: domain_value, + context: context_value, + sort: sort_value + }); + dataset.ids(); + }); + asyncTest("Data records", function () { + var dataset = new openerp.base.DataSet({ + rpc: function (url, _params, on_success) { + equal('/base/dataset/load', url); + _.delay(on_success, 0, [ + {id: 1, sequence: 3, name: "dummy", age: 42}, + {id: 5, sequence: 7, name: "whee", age: 55} + ]); + } + }); + dataset.on_ids.add(function (records) { + equal(records.length, 2, "I loaded two virtual records"); + var d1 = records[0], + d2 = records[1]; + ok(d1 instanceof openerp.base.DataRecord); + ok(d2 instanceof openerp.base.DataRecord); + start(); + }); + dataset.ids(); + }); + + var dataset; + module("set", { + setup: function () { + var openerp = window.openerp.init(); + dataset = new openerp.base.DataSet(); + } + }); + test('Basic properties setting', function () { + var domain_value = [['foo', '=', 'bar']]; + var result = dataset.set({ + domain: domain_value + }); + ok(dataset === result); + deepEqual(domain_value, dataset._domain); + }); + test("Ensure changes don't stick", function () { + var domain = [['foo', '=', 'bar']]; + dataset.set({ + domain: domain + }); + domain.pop(); + deepEqual([['foo', '=', 'bar']], dataset._domain); + }); });