summaryrefslogtreecommitdiff
path: root/addons/web/static/src/js/views/basic/basic_model.js
diff options
context:
space:
mode:
authorRobot Odoo <xmo+roboto@odoo.com>2019-04-02 15:46:39 +0200
committerGitHub <noreply@github.com>2019-04-02 15:46:39 +0200
commit9aea7ae1627e7af10fb03677142ccfc5333f654e (patch)
tree1233647e18260747f3fd315f79f4cae8b06bc0f6 /addons/web/static/src/js/views/basic/basic_model.js
parenta82dbe4d680149b9bf15b7a122c6f8bd1b3582fa (diff)
parent8ea5a40417c2da2e82db66ad36fbf1e8e78bbe11 (diff)
[MERGE] web,*: list view improvements
This branch brings several improvements related to the list view: - grouped list views are now editable - 'expand' attribute can be used to open by default the first level groups - if there are a lot of groups, a pager is displayed (instead of having all groups in the same page) - when grouped on a many2one field, custom buttons (on the many2one comodel) can be inserted in each group's header - we can edit several records in a row in editable list views - editable lists do not flicker anymore when switching a row to edit mode - we can navigate through the cells in list views with the arrows (in readonly) Co-authored-by: Aaron Bohy aab@odoo.com Co-authored-by: David Monjoie dmo@odoo.com Co-authored-by: Martin Geubelle mge@odoo.com Co-authored-by: Ravi Gadhia rga@odoo.com Task 1915702 closes odoo/odoo#31756 Signed-off-by: VincentSchippefilt <VincentSchippefilt@users.noreply.github.com>
Diffstat (limited to 'addons/web/static/src/js/views/basic/basic_model.js')
-rw-r--r--addons/web/static/src/js/views/basic/basic_model.js100
1 files changed, 80 insertions, 20 deletions
diff --git a/addons/web/static/src/js/views/basic/basic_model.js b/addons/web/static/src/js/views/basic/basic_model.js
index 79fac0f2247..804e6d6d1c9 100644
--- a/addons/web/static/src/js/views/basic/basic_model.js
+++ b/addons/web/static/src/js/views/basic/basic_model.js
@@ -90,6 +90,7 @@ var core = require('web.core');
var Domain = require('web.Domain');
var session = require('web.session');
var utils = require('web.utils');
+var viewUtils = require('web.viewUtils');
var _t = core._t;
@@ -178,7 +179,7 @@ var BasicModel = AbstractModel.extend({
addDefaultRecord: function (listID, options) {
var self = this;
var list = this.localData[listID];
- var context = this._getContext(list);
+ var context = _.extend({}, this._getDefaultContext(list), this._getContext(list));
var position = (options && options.position) || 'top';
var params = {
@@ -637,6 +638,9 @@ var BasicModel = AbstractModel.extend({
getDomain: element.getDomain,
getFieldNames: element.getFieldNames,
groupedBy: element.groupedBy,
+ groupsCount: element.groupsCount,
+ groupsLimit: element.groupsLimit,
+ groupsOffset: element.groupsOffset,
id: element.id,
isDirty: element.isDirty,
isOpen: element.isOpen,
@@ -3333,6 +3337,27 @@ var BasicModel = AbstractModel.extend({
return toFetch;
},
/**
+ * Given a dataPoint of type list (that may be a group), returns an object
+ * with 'default_' keys to be used to create new records in that group.
+ *
+ * @private
+ * @param {Object} dataPoint
+ * @returns {Object}
+ */
+ _getDefaultContext: function (dataPoint) {
+ var defaultContext = {};
+ while (dataPoint.parentID) {
+ var parent = this.localData[dataPoint.parentID];
+ var groupByField = parent.groupedBy[0].split(':')[0];
+ var value = viewUtils.getGroupValue(dataPoint, groupByField);
+ if (value) {
+ defaultContext['default_' + groupByField] = value;
+ }
+ dataPoint = parent;
+ }
+ return defaultContext;
+ },
+ /**
* Some records are associated to a/some domain(s). This method allows to
* retrieve them, evaluated.
*
@@ -3709,6 +3734,9 @@ var BasicModel = AbstractModel.extend({
fields: fields,
fieldsInfo: params.fieldsInfo,
groupedBy: params.groupedBy || [],
+ groupsCount: 0,
+ groupsLimit: type === 'list' && params.groupsLimit || null,
+ groupsOffset: 0,
id: _.uniqueId(params.modelName + '_'),
isOpen: params.isOpen,
limit: type === 'record' ? 1 : params.limit,
@@ -4219,23 +4247,33 @@ var BasicModel = AbstractModel.extend({
*/
_readGroup: function (list, options) {
var self = this;
+ options = options || {};
var groupByField = list.groupedBy[0];
var rawGroupBy = groupByField.split(':')[0];
var fields = _.uniq(list.getFieldNames().concat(rawGroupBy));
- var orderedBy = _.filter(list.orderedBy, function(order){
+ var orderedBy = _.filter(list.orderedBy, function (order) {
return order.name === rawGroupBy || list.fields[order.name].group_operator !== undefined;
});
+ var openGroupsLimit = list.groupsLimit || self.OPEN_GROUP_LIMIT;
+ var expand = list.openGroupByDefault && options.fetchRecordsWithGroups;
return this._rpc({
model: list.model,
- method: 'read_group',
+ method: 'web_read_group',
fields: fields,
domain: list.domain,
context: list.context,
groupBy: list.groupedBy,
+ limit: list.groupsLimit,
+ offset: list.groupsOffset,
orderBy: orderedBy,
lazy: true,
+ expand: expand,
+ expand_limit: expand ? list.limit : null,
+ expand_orderby: expand ? list.orderedBy : null,
})
- .then(function (groups) {
+ .then(function (result) {
+ var groups = result.groups;
+ list.groupsCount = result.length;
var previousGroups = _.map(list.data, function (groupID) {
return self.localData[groupID];
});
@@ -4285,7 +4323,7 @@ var BasicModel = AbstractModel.extend({
delete self.localData[newGroup.id];
// restore the internal state of the group
var updatedProps = _.pick(oldGroup, 'isOpen', 'offset', 'id');
- if (options && options.onlyGroups || oldGroup.isOpen && newGroup.groupedBy.length) {
+ if (options.onlyGroups || oldGroup.isOpen && newGroup.groupedBy.length) {
// If the group is opened and contains subgroups,
// also keep its data to keep internal state of
// sub-groups
@@ -4298,20 +4336,28 @@ var BasicModel = AbstractModel.extend({
// form view) are reloaded
newGroup.limit = oldGroup.limit + oldGroup.loadMoreOffset;
self.localData[newGroup.id] = newGroup;
- } else if (!newGroup.openGroupByDefault || openGroupCount >= self.OPEN_GROUP_LIMIT) {
+ } else if (!newGroup.openGroupByDefault || openGroupCount >= openGroupsLimit) {
newGroup.isOpen = false;
+ } else if ('__fold' in group) {
+ newGroup.isOpen = !group.__fold;
} else {
- newGroup.isOpen = '__fold' in group ? !group.__fold : true;
+ // open the group iff it is a first level group
+ newGroup.isOpen = !self.localData[newGroup.parentID].parentID;
}
list.data.push(newGroup.id);
list.count += newGroup.count;
if (newGroup.isOpen && newGroup.count > 0) {
openGroupCount++;
+ if (group.__data) {
+ // bypass the search_read when the group's records have been obtained
+ // by the call to 'web_read_group' (see @_searchReadUngroupedList)
+ newGroup.__data = group.__data;
+ }
options = _.defaults({enableRelationalFetch: false}, options);
defs.push(self._load(newGroup, options));
}
});
- if (options && options.keepEmptyGroups) {
+ if (options.keepEmptyGroups) {
// Find the groups that were available in a previous
// readGroup but are not there anymore.
// Note that these groups are put after existing groups so
@@ -4327,7 +4373,7 @@ var BasicModel = AbstractModel.extend({
}
return Promise.all(defs).then(function (groups) {
- if (!options || !options.onlyGroups) {
+ if (!options.onlyGroups) {
// generate the res_ids of the main list, being the concatenation
// of the fetched res_ids in each group
list.res_ids = _.flatten(_.map(groups, function (group) {
@@ -4460,6 +4506,12 @@ var BasicModel = AbstractModel.extend({
if (options.offset !== undefined) {
this._setOffset(element.id, options.offset);
}
+ if (options.groupsLimit !== undefined) {
+ element.groupsLimit = options.groupsLimit;
+ }
+ if (options.groupsOffset !== undefined) {
+ element.groupsOffset = options.groupsOffset;
+ }
if (options.loadMoreOffset !== undefined) {
element.loadMoreOffset = options.loadMoreOffset;
} else {
@@ -4510,17 +4562,25 @@ var BasicModel = AbstractModel.extend({
_searchReadUngroupedList: function (list) {
var self = this;
var fieldNames = list.getFieldNames();
- return this._rpc({
- route: '/web/dataset/search_read',
- model: list.model,
- fields: fieldNames,
- context: list.getContext(),
- domain: list.domain || [],
- limit: list.limit,
- offset: list.loadMoreOffset + list.offset,
- orderBy: list.orderedBy,
- })
- .then(function (result) {
+ var prom;
+ if (list.__data) {
+ // the data have already been fetched (alonside the groups by the
+ // call to 'web_read_group'), so we can bypass the search_read
+ prom = Promise.resolve(list.__data);
+ } else {
+ prom = this._rpc({
+ route: '/web/dataset/search_read',
+ model: list.model,
+ fields: fieldNames,
+ context: list.getContext(),
+ domain: list.domain || [],
+ limit: list.limit,
+ offset: list.loadMoreOffset + list.offset,
+ orderBy: list.orderedBy,
+ });
+ }
+ return prom.then(function (result) {
+ delete list.__data;
list.count = result.length;
var ids = _.pluck(result.records, 'id');
var data = _.map(result.records, function (record) {