2011-03-30 14:00:48 +00:00
|
|
|
|
2011-03-30 14:35:56 +00:00
|
|
|
openerp.base.data = function(openerp) {
|
|
|
|
|
2011-04-04 15:06:19 +00:00
|
|
|
openerp.base.DataGroup = openerp.base.Controller.extend( /** @lends openerp.base.DataGroup# */{
|
2011-03-30 23:55:52 +00:00
|
|
|
/**
|
2011-05-10 08:34:20 +00:00
|
|
|
* Management interface between views and grouped collections of OpenERP
|
2011-05-11 12:10:21 +00:00
|
|
|
* records.
|
|
|
|
*
|
|
|
|
* The root DataGroup is instantiated with the relevant information
|
|
|
|
* (a session, a model, a domain, a context and a group_by sequence), the
|
|
|
|
* domain and context may be empty. It is then interacted with via
|
|
|
|
* :js:func:`~openerp.base.DataGroup.list`, which is used to read the
|
2011-05-27 15:10:00 +00:00
|
|
|
* content of the current grouping level.
|
2011-03-31 08:03:35 +00:00
|
|
|
*
|
|
|
|
* @constructs
|
|
|
|
* @extends openerp.base.Controller
|
2011-05-10 08:34:20 +00:00
|
|
|
*
|
2011-03-31 08:03:35 +00:00
|
|
|
* @param {openerp.base.Session} session Current OpenERP session
|
2011-05-10 11:12:04 +00:00
|
|
|
* @param {String} model name of the model managed by this DataGroup
|
|
|
|
* @param {Array} domain search domain for this DataGroup
|
|
|
|
* @param {Object} context context of the DataGroup's searches
|
|
|
|
* @param {Array} group_by sequence of fields by which to group
|
2011-05-24 06:52:05 +00:00
|
|
|
* @param {Number} [level=0] nesting level of the group
|
2011-03-30 23:55:52 +00:00
|
|
|
*/
|
2011-05-24 06:52:05 +00:00
|
|
|
init: function(session, model, domain, context, group_by, level) {
|
2011-05-17 14:03:58 +00:00
|
|
|
if (group_by) {
|
2011-05-24 13:27:29 +00:00
|
|
|
if (group_by.length || context['group_by_no_leaf']) {
|
2011-05-17 14:03:58 +00:00
|
|
|
return new openerp.base.ContainerDataGroup(
|
2011-05-24 06:52:05 +00:00
|
|
|
session, model, domain, context, group_by, level);
|
2011-05-17 14:03:58 +00:00
|
|
|
} else {
|
|
|
|
return new openerp.base.GrouplessDataGroup(
|
2011-05-24 06:52:05 +00:00
|
|
|
session, model, domain, context, level);
|
2011-05-17 14:03:58 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-03-30 14:00:48 +00:00
|
|
|
this._super(session, null);
|
2011-05-10 11:12:04 +00:00
|
|
|
this.model = model;
|
|
|
|
this.context = context;
|
|
|
|
this.domain = domain;
|
2011-05-24 06:52:05 +00:00
|
|
|
|
|
|
|
this.level = level || 0;
|
|
|
|
},
|
|
|
|
cls: 'DataGroup'
|
2011-05-17 14:03:58 +00:00
|
|
|
});
|
|
|
|
openerp.base.ContainerDataGroup = openerp.base.DataGroup.extend(
|
|
|
|
/** @lends openerp.base.ContainerDataGroup# */ {
|
|
|
|
/**
|
|
|
|
*
|
|
|
|
* @constructs
|
|
|
|
* @extends openerp.base.DataGroup
|
|
|
|
*
|
|
|
|
* @param session
|
|
|
|
* @param model
|
|
|
|
* @param domain
|
|
|
|
* @param context
|
|
|
|
* @param group_by
|
2011-05-24 06:52:05 +00:00
|
|
|
* @param level
|
2011-05-17 14:03:58 +00:00
|
|
|
*/
|
2011-05-24 06:52:05 +00:00
|
|
|
init: function (session, model, domain, context, group_by, level) {
|
|
|
|
this._super(session, model, domain, context, null, level);
|
2011-05-10 08:34:20 +00:00
|
|
|
|
|
|
|
this.group_by = group_by;
|
|
|
|
},
|
2011-05-16 08:05:39 +00:00
|
|
|
/**
|
|
|
|
* The format returned by ``read_group`` is absolutely dreadful:
|
|
|
|
*
|
|
|
|
* * A ``__context`` key provides future grouping levels
|
|
|
|
* * A ``__domain`` key provides the domain for the next search
|
|
|
|
* * The current grouping value is provided through the name of the
|
|
|
|
* current grouping name e.g. if currently grouping on ``user_id``, then
|
|
|
|
* the ``user_id`` value for this group will be provided through the
|
|
|
|
* ``user_id`` key.
|
|
|
|
* * Similarly, the number of items in the group (not necessarily direct)
|
|
|
|
* is provided via ``${current_field}_count``
|
|
|
|
* * Other aggregate fields are just dumped there
|
|
|
|
*
|
|
|
|
* This function slightly improves the grouping records by:
|
|
|
|
*
|
|
|
|
* * Adding a ``grouped_on`` property providing the current grouping field
|
|
|
|
* * Adding a ``value`` and a ``length`` properties which replace the
|
|
|
|
* ``$current_field`` and ``${current_field}_count`` ones
|
|
|
|
* * Moving aggregate values into an ``aggregates`` property object
|
|
|
|
*
|
|
|
|
* Context and domain keys remain as-is, they should not be used externally
|
|
|
|
* but in case they're needed...
|
|
|
|
*
|
|
|
|
* @param {Object} group ``read_group`` record
|
|
|
|
*/
|
|
|
|
transform_group: function (group) {
|
|
|
|
var field_name = this.group_by[0];
|
2011-05-24 13:27:29 +00:00
|
|
|
// In cases where group_by_no_leaf and no group_by, the result of
|
|
|
|
// read_group has aggregate fields but no __context or __domain.
|
|
|
|
// Create default (empty) values for those so that things don't break
|
|
|
|
var fixed_group = _.extend(
|
|
|
|
{__context: {group_by: []}, __domain: []},
|
|
|
|
group);
|
2011-05-16 08:05:39 +00:00
|
|
|
|
|
|
|
var aggregates = {};
|
2011-05-24 13:27:29 +00:00
|
|
|
_(fixed_group).each(function (value, key) {
|
2011-05-16 08:05:39 +00:00
|
|
|
if (key.indexOf('__') === 0
|
|
|
|
|| key === field_name
|
|
|
|
|| key === field_name + '_count') {
|
|
|
|
return;
|
|
|
|
}
|
2011-05-24 14:06:48 +00:00
|
|
|
aggregates[key] = value || 0;
|
2011-05-16 08:05:39 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
return {
|
2011-05-24 13:27:29 +00:00
|
|
|
__context: fixed_group.__context,
|
|
|
|
__domain: fixed_group.__domain,
|
2011-05-16 08:05:39 +00:00
|
|
|
|
|
|
|
grouped_on: field_name,
|
2011-05-24 10:26:54 +00:00
|
|
|
// if terminal group (or no group) and group_by_no_leaf => use group.__count
|
2011-05-24 13:27:29 +00:00
|
|
|
length: fixed_group[field_name + '_count'] || fixed_group.__count,
|
|
|
|
value: fixed_group[field_name],
|
2011-05-16 08:05:39 +00:00
|
|
|
|
2011-05-24 10:26:54 +00:00
|
|
|
openable: !(this.context['group_by_no_leaf']
|
2011-05-24 13:27:29 +00:00
|
|
|
&& fixed_group.__context.group_by.length === 0),
|
2011-05-24 09:11:25 +00:00
|
|
|
|
2011-05-16 08:05:39 +00:00
|
|
|
aggregates: aggregates
|
|
|
|
};
|
|
|
|
},
|
2011-05-10 08:34:20 +00:00
|
|
|
fetch: function () {
|
2011-05-11 12:10:21 +00:00
|
|
|
// internal method
|
2011-05-10 08:34:20 +00:00
|
|
|
var d = new $.Deferred();
|
|
|
|
var self = this;
|
|
|
|
|
2011-05-20 14:20:41 +00:00
|
|
|
// disable caching for now, not sure what I should do there
|
|
|
|
if (false && this.groups) {
|
2011-05-10 08:34:20 +00:00
|
|
|
d.resolveWith(this, [this.groups]);
|
|
|
|
} else {
|
|
|
|
this.rpc('/base/group/read', {
|
|
|
|
model: this.model,
|
|
|
|
context: this.context,
|
|
|
|
domain: this.domain,
|
|
|
|
group_by_fields: this.group_by
|
|
|
|
}, function () { }).then(function (response) {
|
2011-05-16 08:05:39 +00:00
|
|
|
var data_groups = _(response.result).map(
|
|
|
|
_.bind(self.transform_group, self));
|
|
|
|
self.groups = data_groups;
|
|
|
|
d.resolveWith(self, [data_groups]);
|
2011-05-10 08:34:20 +00:00
|
|
|
}, function () {
|
|
|
|
d.rejectWith.apply(d, self, [arguments]);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
return d.promise();
|
|
|
|
},
|
|
|
|
/**
|
2011-05-16 08:05:39 +00:00
|
|
|
* The items of a list have the following properties:
|
2011-05-11 12:10:21 +00:00
|
|
|
*
|
|
|
|
* ``length``
|
|
|
|
* the number of records contained in the group (and all of its
|
|
|
|
* sub-groups). This does *not* provide the size of the "next level"
|
|
|
|
* of the group, unless the group is terminal (no more groups within
|
|
|
|
* it).
|
|
|
|
* ``grouped_on``
|
|
|
|
* the name of the field this level was grouped on, this is mostly
|
|
|
|
* used for display purposes, in order to know the name of the current
|
|
|
|
* level of grouping. The ``grouped_on`` should be the same for all
|
|
|
|
* objects of the list.
|
|
|
|
* ``value``
|
|
|
|
* the value which led to this group (this is the value all contained
|
|
|
|
* records have for the current ``grouped_on`` field name).
|
2011-05-16 08:05:39 +00:00
|
|
|
* ``aggregates``
|
|
|
|
* a mapping of other aggregation fields provided by ``read_group``
|
2011-05-17 14:03:58 +00:00
|
|
|
*/
|
|
|
|
list: function (ifGroups, ifRecords) {
|
|
|
|
var self = this;
|
|
|
|
this.fetch().then(function (group_records) {
|
|
|
|
ifGroups(_(group_records).map(function (group) {
|
|
|
|
var child_context = _.extend({}, self.context, group.__context);
|
|
|
|
return _.extend(
|
|
|
|
new openerp.base.DataGroup(
|
|
|
|
self.session, self.model, group.__domain,
|
2011-05-24 06:52:05 +00:00
|
|
|
child_context, child_context.group_by,
|
|
|
|
self.level + 1),
|
2011-05-17 14:03:58 +00:00
|
|
|
group);
|
|
|
|
}));
|
|
|
|
});
|
|
|
|
}
|
|
|
|
});
|
|
|
|
openerp.base.GrouplessDataGroup = openerp.base.DataGroup.extend(
|
|
|
|
/** @lends openerp.base.GrouplessDataGroup# */ {
|
|
|
|
/**
|
|
|
|
*
|
|
|
|
* @constructs
|
|
|
|
* @extends openerp.base.DataGroup
|
2011-05-10 08:34:20 +00:00
|
|
|
*
|
2011-05-17 14:03:58 +00:00
|
|
|
* @param session
|
|
|
|
* @param model
|
|
|
|
* @param domain
|
|
|
|
* @param context
|
2011-05-24 06:52:05 +00:00
|
|
|
* @param level
|
2011-05-10 08:34:20 +00:00
|
|
|
*/
|
2011-05-24 06:52:05 +00:00
|
|
|
init: function (session, model, domain, context, level) {
|
|
|
|
this._super(session, model, domain, context, null, level);
|
2011-05-17 14:03:58 +00:00
|
|
|
},
|
|
|
|
list: function (ifGroups, ifRecords) {
|
2011-06-28 09:35:59 +00:00
|
|
|
ifRecords(new openerp.base.DataSetSearch(this.session, this.model, this.context, this.domain));
|
2011-03-31 08:03:35 +00:00
|
|
|
}
|
2011-03-30 14:00:48 +00:00
|
|
|
});
|
|
|
|
|
2011-05-27 15:10:00 +00:00
|
|
|
openerp.base.StaticDataGroup = openerp.base.GrouplessDataGroup.extend(
|
|
|
|
/** @lends openerp.base.StaticDataGroup# */ {
|
|
|
|
/**
|
|
|
|
* A specialization of groupless data groups, relying on a single static
|
|
|
|
* dataset as its records provider.
|
|
|
|
*
|
|
|
|
* @constructs
|
|
|
|
* @extends openerp.base.GrouplessDataGroup
|
|
|
|
* @param {openep.base.DataSetStatic} dataset a static dataset backing the groups
|
|
|
|
*/
|
|
|
|
init: function (dataset) {
|
|
|
|
this.dataset = dataset;
|
|
|
|
},
|
|
|
|
list: function (ifGroups, ifRecords) {
|
|
|
|
ifRecords(this.dataset);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2011-04-04 15:06:19 +00:00
|
|
|
openerp.base.DataSet = openerp.base.Controller.extend( /** @lends openerp.base.DataSet# */{
|
2011-03-30 15:27:36 +00:00
|
|
|
/**
|
2011-04-04 15:06:19 +00:00
|
|
|
* DateaManagement interface between views and the collection of selected
|
2011-03-30 15:27:36 +00:00
|
|
|
* OpenERP records (represents the view's state?)
|
|
|
|
*
|
|
|
|
* @constructs
|
|
|
|
* @extends openerp.base.Controller
|
|
|
|
*
|
|
|
|
* @param {openerp.base.Session} session current OpenERP session
|
|
|
|
* @param {String} model the OpenERP model this dataset will manage
|
|
|
|
*/
|
2011-06-17 14:19:45 +00:00
|
|
|
init: function(session, model, context) {
|
2011-03-30 14:00:48 +00:00
|
|
|
this._super(session);
|
|
|
|
this.model = model;
|
2011-06-17 14:19:45 +00:00
|
|
|
this.context = context || {};
|
2011-06-23 15:52:18 +00:00
|
|
|
this.index = null;
|
2011-04-04 15:06:19 +00:00
|
|
|
this.count = 0;
|
2011-03-30 14:00:48 +00:00
|
|
|
},
|
|
|
|
start: function() {
|
|
|
|
},
|
2011-04-06 00:51:36 +00:00
|
|
|
previous: function () {
|
|
|
|
this.index -= 1;
|
|
|
|
if (this.index < 0) {
|
2011-04-06 21:10:37 +00:00
|
|
|
this.index = this.count - 1;
|
2011-04-06 00:51:36 +00:00
|
|
|
}
|
|
|
|
return this;
|
|
|
|
},
|
|
|
|
next: function () {
|
|
|
|
this.index += 1;
|
2011-04-06 21:10:37 +00:00
|
|
|
if (this.index >= this.count) {
|
2011-04-06 00:51:36 +00:00
|
|
|
this.index = 0;
|
|
|
|
}
|
|
|
|
return this;
|
2011-04-06 07:09:24 +00:00
|
|
|
},
|
2011-03-30 14:00:48 +00:00
|
|
|
/**
|
2011-04-06 21:10:37 +00:00
|
|
|
* Read records.
|
2011-03-30 14:00:48 +00:00
|
|
|
*/
|
2011-04-06 21:10:37 +00:00
|
|
|
read_ids: function (ids, fields, callback) {
|
2011-04-05 14:34:25 +00:00
|
|
|
var self = this;
|
2011-06-03 14:37:23 +00:00
|
|
|
return this.rpc('/base/dataset/get', {
|
2011-04-04 15:06:19 +00:00
|
|
|
model: this.model,
|
|
|
|
ids: ids,
|
|
|
|
fields: fields
|
2011-04-05 15:14:40 +00:00
|
|
|
}, callback);
|
2011-03-30 14:00:48 +00:00
|
|
|
},
|
2011-04-06 21:10:37 +00:00
|
|
|
/**
|
|
|
|
* Read a slice of the records represented by this DataSet, based on its
|
|
|
|
* domain and context.
|
|
|
|
*
|
|
|
|
* @param {Number} [offset=0] The index from which selected records should be returned
|
|
|
|
* @param {Number} [limit=null] The maximum number of records to return
|
|
|
|
*/
|
|
|
|
read_slice: function (fields, offset, limit, callback) {
|
|
|
|
},
|
|
|
|
/**
|
|
|
|
* Read the indexed record.
|
|
|
|
*/
|
|
|
|
read_index: function (fields, callback) {
|
2011-04-04 16:09:36 +00:00
|
|
|
if (_.isEmpty(this.ids)) {
|
2011-06-03 14:37:23 +00:00
|
|
|
return $.Deferred().reject().promise();
|
2011-04-04 16:09:36 +00:00
|
|
|
} else {
|
|
|
|
fields = fields || false;
|
2011-06-03 14:37:23 +00:00
|
|
|
return this.read_ids([this.ids[this.index]], fields, function(records) {
|
2011-04-05 15:14:40 +00:00
|
|
|
callback(records[0]);
|
|
|
|
});
|
2011-04-04 16:09:36 +00:00
|
|
|
}
|
2011-03-30 14:00:48 +00:00
|
|
|
},
|
2011-06-28 10:30:49 +00:00
|
|
|
default_get: function(fields, callback) {
|
2011-04-07 13:57:32 +00:00
|
|
|
return this.rpc('/base/dataset/default_get', {
|
2011-04-07 13:07:25 +00:00
|
|
|
model: this.model,
|
|
|
|
fields: fields,
|
2011-06-28 10:30:49 +00:00
|
|
|
context: this.context
|
2011-04-07 13:07:25 +00:00
|
|
|
}, callback);
|
2011-04-06 21:10:37 +00:00
|
|
|
},
|
2011-05-05 07:01:46 +00:00
|
|
|
create: function(data, callback, error_callback) {
|
2011-04-11 11:35:16 +00:00
|
|
|
return this.rpc('/base/dataset/create', {
|
|
|
|
model: this.model,
|
|
|
|
data: data,
|
|
|
|
context: this.context
|
2011-05-05 07:01:46 +00:00
|
|
|
}, callback, error_callback);
|
2011-04-06 21:10:37 +00:00
|
|
|
},
|
2011-04-05 16:25:45 +00:00
|
|
|
write: function (id, data, callback) {
|
2011-04-07 13:57:32 +00:00
|
|
|
return this.rpc('/base/dataset/save', {
|
2011-04-05 16:25:45 +00:00
|
|
|
model: this.model,
|
|
|
|
id: id,
|
2011-04-06 09:58:04 +00:00
|
|
|
data: data,
|
|
|
|
context: this.context
|
2011-04-05 16:25:45 +00:00
|
|
|
}, callback);
|
|
|
|
},
|
2011-06-22 14:49:16 +00:00
|
|
|
unlink: function(ids, callback, error_callback) {
|
|
|
|
var self = this;
|
|
|
|
return this.call_and_eval("unlink", [ids, this.context], null, 1,
|
|
|
|
callback, error_callback);
|
2011-04-06 21:10:37 +00:00
|
|
|
},
|
2011-06-10 16:16:29 +00:00
|
|
|
call: function (method, args, callback, error_callback) {
|
2011-04-07 13:57:32 +00:00
|
|
|
return this.rpc('/base/dataset/call', {
|
2011-04-06 13:29:34 +00:00
|
|
|
model: this.model,
|
|
|
|
method: method,
|
2011-06-09 13:20:04 +00:00
|
|
|
args: args || []
|
2011-06-10 16:16:29 +00:00
|
|
|
}, callback, error_callback);
|
2011-04-21 15:56:05 +00:00
|
|
|
},
|
2011-06-17 12:08:34 +00:00
|
|
|
call_and_eval: function (method, args, domain_id, context_id, callback, error_callback) {
|
|
|
|
return this.rpc('/base/dataset/call', {
|
|
|
|
model: this.model,
|
|
|
|
method: method,
|
|
|
|
domain_id: domain_id || null,
|
|
|
|
context_id: context_id || null,
|
|
|
|
args: args || []
|
|
|
|
}, callback, error_callback);
|
|
|
|
},
|
2011-06-14 09:38:44 +00:00
|
|
|
/**
|
|
|
|
* Arguments:
|
2011-06-16 16:37:09 +00:00
|
|
|
* name='', args=[], operator='ilike', context=None, limit=100
|
2011-06-14 09:38:44 +00:00
|
|
|
*/
|
|
|
|
name_search: function (args, callback, error_callback) {
|
2011-06-17 12:08:34 +00:00
|
|
|
return this.call_and_eval('name_search',
|
|
|
|
args, 1, 3,
|
2011-06-14 09:38:44 +00:00
|
|
|
callback, error_callback);
|
2011-04-21 15:56:05 +00:00
|
|
|
},
|
|
|
|
exec_workflow: function (id, signal, callback) {
|
|
|
|
return this.rpc('/base/dataset/exec_workflow', {
|
|
|
|
model: this.model,
|
|
|
|
id: id,
|
|
|
|
signal: signal
|
|
|
|
}, callback);
|
2011-04-08 10:37:36 +00:00
|
|
|
}
|
2011-04-06 00:51:36 +00:00
|
|
|
});
|
2011-03-30 23:55:52 +00:00
|
|
|
|
2011-04-06 21:10:37 +00:00
|
|
|
openerp.base.DataSetStatic = openerp.base.DataSet.extend({
|
2011-05-31 13:01:54 +00:00
|
|
|
init: function(session, model, ids) {
|
2011-04-06 21:10:37 +00:00
|
|
|
this._super(session, model);
|
|
|
|
// all local records
|
2011-05-31 13:01:54 +00:00
|
|
|
this.ids = ids || [];
|
|
|
|
this.count = this.ids.length;
|
2011-06-28 09:58:58 +00:00
|
|
|
if (this.ids.length) {
|
|
|
|
this.index = 0;
|
|
|
|
}
|
2011-04-06 21:10:37 +00:00
|
|
|
},
|
|
|
|
read_slice: function (fields, offset, limit, callback) {
|
2011-06-23 12:21:10 +00:00
|
|
|
var self = this;
|
|
|
|
offset = offset || 0;
|
2011-05-27 15:10:00 +00:00
|
|
|
var end_pos = limit && limit !== -1 ? offset + limit : undefined;
|
|
|
|
this.read_ids(this.ids.slice(offset, end_pos), fields, callback);
|
2011-05-31 13:01:54 +00:00
|
|
|
},
|
|
|
|
set_ids: function (ids) {
|
|
|
|
this.ids = ids;
|
|
|
|
this.count = this.ids.length;
|
2011-06-22 14:49:16 +00:00
|
|
|
this.index = this.index <= this.count - 1 ?
|
|
|
|
this.index : (this.count > 0 ? this.count - 1 : 0);
|
2011-05-31 13:01:54 +00:00
|
|
|
},
|
|
|
|
unlink: function(ids) {
|
|
|
|
this.on_unlink(ids);
|
2011-06-22 14:49:16 +00:00
|
|
|
return $.Deferred().resolve({result: true});
|
2011-05-31 13:01:54 +00:00
|
|
|
},
|
|
|
|
on_unlink: function(ids) {
|
2011-05-31 14:11:19 +00:00
|
|
|
this.set_ids(_.without.apply(null, [this.ids].concat(ids)));
|
2011-04-08 10:37:36 +00:00
|
|
|
}
|
2011-04-06 00:51:36 +00:00
|
|
|
});
|
|
|
|
|
2011-04-06 21:10:37 +00:00
|
|
|
openerp.base.DataSetSearch = openerp.base.DataSet.extend({
|
2011-06-17 14:19:45 +00:00
|
|
|
init: function(session, model, context, domain) {
|
|
|
|
this._super(session, model, context);
|
|
|
|
this.domain = domain || [];
|
2011-05-04 14:05:35 +00:00
|
|
|
this._sort = [];
|
2011-04-06 21:10:37 +00:00
|
|
|
this.offset = 0;
|
|
|
|
// subset records[offset:offset+limit]
|
|
|
|
// is it necessary ?
|
|
|
|
this.ids = [];
|
|
|
|
},
|
|
|
|
read_slice: function (fields, offset, limit, callback) {
|
|
|
|
var self = this;
|
|
|
|
offset = offset || 0;
|
|
|
|
// cached search, not sure it's a good idea
|
|
|
|
if(this.offset <= offset) {
|
|
|
|
var start = offset - this.offset;
|
|
|
|
if(this.ids.length - start >= limit) {
|
|
|
|
// TODO: check if this could work do only read if possible
|
|
|
|
// return read_ids(ids.slice(start,start+limit),fields,callback)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
this.rpc('/base/dataset/search_read', {
|
|
|
|
model: this.model,
|
|
|
|
fields: fields,
|
|
|
|
domain: this.domain,
|
|
|
|
context: this.context,
|
2011-05-04 14:05:35 +00:00
|
|
|
sort: this.sort(),
|
2011-04-06 21:10:37 +00:00
|
|
|
offset: offset,
|
|
|
|
limit: limit
|
|
|
|
}, function (records) {
|
2011-05-20 14:20:41 +00:00
|
|
|
self.ids.splice(0, self.ids.length);
|
2011-04-06 21:10:37 +00:00
|
|
|
self.offset = offset;
|
|
|
|
self.count = records.length; // TODO: get real count
|
|
|
|
for (var i=0; i < records.length; i++ ) {
|
|
|
|
self.ids.push(records[i].id);
|
|
|
|
}
|
|
|
|
callback(records);
|
|
|
|
});
|
2011-05-04 14:05:35 +00:00
|
|
|
},
|
|
|
|
/**
|
|
|
|
* Reads or changes sort criteria on the dataset.
|
|
|
|
*
|
|
|
|
* If not provided with any argument, serializes the sort criteria to
|
|
|
|
* an SQL-like form usable by OpenERP's ORM.
|
|
|
|
*
|
|
|
|
* If given a field, will set that field as first sorting criteria or,
|
|
|
|
* if the field is already the first sorting criteria, will reverse it.
|
|
|
|
*
|
|
|
|
* @param {String} [field] field to sort on, reverses it (toggle from ASC to DESC) if already the main sort criteria
|
|
|
|
* @param {Boolean} [force_reverse=false] forces inserting the field as DESC
|
|
|
|
* @returns {String|undefined}
|
|
|
|
*/
|
|
|
|
sort: function (field, force_reverse) {
|
|
|
|
if (!field) {
|
|
|
|
return _.map(this._sort, function (criteria) {
|
|
|
|
if (criteria[0] === '-') {
|
|
|
|
return criteria.slice(1) + ' DESC';
|
|
|
|
}
|
2011-05-04 14:47:53 +00:00
|
|
|
return criteria + ' ASC';
|
2011-05-04 14:05:35 +00:00
|
|
|
}).join(', ');
|
|
|
|
}
|
|
|
|
|
|
|
|
var reverse = force_reverse || (this._sort[0] === field);
|
|
|
|
this._sort = _.without(this._sort, field, '-' + field);
|
|
|
|
|
|
|
|
this._sort.unshift((reverse ? '-' : '') + field);
|
|
|
|
return undefined;
|
2011-06-22 14:49:16 +00:00
|
|
|
},
|
|
|
|
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);
|
2011-04-08 10:37:36 +00:00
|
|
|
}
|
2011-04-06 00:51:36 +00:00
|
|
|
});
|
|
|
|
|
2011-06-23 16:57:17 +00:00
|
|
|
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) {}
|
|
|
|
});
|
|
|
|
|
2011-06-17 14:19:45 +00:00
|
|
|
openerp.base.CompoundContext = function() {
|
2011-06-17 09:58:35 +00:00
|
|
|
this.__ref = "compound_context";
|
|
|
|
this.__contexts = [];
|
2011-06-28 12:17:47 +00:00
|
|
|
this.__eval_context = null;
|
2011-06-17 14:19:45 +00:00
|
|
|
var self = this;
|
|
|
|
_.each(arguments, function(x) {
|
|
|
|
self.add(x);
|
|
|
|
});
|
2011-06-17 09:58:35 +00:00
|
|
|
};
|
|
|
|
openerp.base.CompoundContext.prototype.add = function(context) {
|
2011-06-28 14:04:18 +00:00
|
|
|
this.__contexts.push(context);
|
2011-06-17 12:08:34 +00:00
|
|
|
return this;
|
2011-06-17 09:58:35 +00:00
|
|
|
};
|
2011-06-28 12:17:47 +00:00
|
|
|
openerp.base.CompoundContext.prototype.set_eval_context = function(eval_context) {
|
|
|
|
this.__eval_context = eval_context;
|
|
|
|
return this;
|
|
|
|
};
|
|
|
|
openerp.base.CompoundContext.prototype.get_eval_context = function() {
|
|
|
|
return this.__eval_context;
|
|
|
|
};
|
2011-06-17 09:58:35 +00:00
|
|
|
|
2011-06-17 14:19:45 +00:00
|
|
|
openerp.base.CompoundDomain = function() {
|
2011-06-17 09:58:35 +00:00
|
|
|
this.__ref = "compound_domain";
|
|
|
|
this.__domains = [];
|
2011-06-28 14:04:18 +00:00
|
|
|
this.__eval_context = null;
|
2011-06-17 14:19:45 +00:00
|
|
|
_.each(arguments, function(x) {
|
|
|
|
self.add(x);
|
|
|
|
});
|
2011-06-17 09:58:35 +00:00
|
|
|
};
|
|
|
|
openerp.base.CompoundDomain.prototype.add = function(domain) {
|
2011-06-28 14:04:18 +00:00
|
|
|
this.__domains.push(domain);
|
|
|
|
return this;
|
|
|
|
};
|
|
|
|
openerp.base.CompoundDomain.prototype.set_eval_context = function(eval_context) {
|
|
|
|
this.__eval_context = eval_context;
|
2011-06-17 12:08:34 +00:00
|
|
|
return this;
|
2011-06-17 09:58:35 +00:00
|
|
|
};
|
2011-06-28 14:04:18 +00:00
|
|
|
openerp.base.CompoundDomain.prototype.get_eval_context = function() {
|
|
|
|
return this.__eval_context;
|
|
|
|
};
|
2011-06-17 09:58:35 +00:00
|
|
|
|
2011-03-30 14:00:48 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
// vim:et fdc=0 fdl=0 foldnestmax=3 fdm=syntax:
|