1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
|
odoo.define('web.PivotView', function (require) {
"use strict";
/**
* The Pivot View is a view that represents data in a 'pivot grid' form. It
* aggregates data on 2 dimensions and displays the result, allows the user to
* 'zoom in' data.
*/
const AbstractView = require('web.AbstractView');
const config = require('web.config');
const core = require('web.core');
const PivotModel = require('web.PivotModel');
const PivotController = require('web.PivotController');
const PivotRenderer = require('web.PivotRenderer');
const RendererWrapper = require('web.RendererWrapper');
const _t = core._t;
const _lt = core._lt;
const searchUtils = require('web.searchUtils');
const GROUPABLE_TYPES = searchUtils.GROUPABLE_TYPES;
const PivotView = AbstractView.extend({
display_name: _lt('Pivot'),
icon: 'fa-table',
config: Object.assign({}, AbstractView.prototype.config, {
Model: PivotModel,
Controller: PivotController,
Renderer: PivotRenderer,
}),
viewType: 'pivot',
searchMenuTypes: ['filter', 'groupBy', 'comparison', 'favorite'],
/**
* @override
* @param {Object} params
* @param {Array} params.additionalMeasures
*/
init: function (viewInfo, params) {
this._super.apply(this, arguments);
const activeMeasures = []; // Store the defined active measures
const colGroupBys = []; // Store the defined group_by used on cols
const rowGroupBys = []; // Store the defined group_by used on rows
const measures = {}; // All the available measures
const groupableFields = {}; // The fields which can be used to group data
const widgets = {}; // Wigdets defined in the arch
const additionalMeasures = params.additionalMeasures || [];
this.fields.__count = { string: _t("Count"), type: "integer" };
//Compute the measures and the groupableFields
Object.keys(this.fields).forEach(name => {
const field = this.fields[name];
if (name !== 'id' && field.store === true) {
if (['integer', 'float', 'monetary'].includes(field.type) || additionalMeasures.includes(name)) {
measures[name] = field;
}
if (GROUPABLE_TYPES.includes(field.type)) {
groupableFields[name] = field;
}
}
});
measures.__count = { string: _t("Count"), type: "integer" };
this.arch.children.forEach(field => {
let name = field.attrs.name;
// Remove invisible fields from the measures if not in additionalMeasures
if (field.attrs.invisible && py.eval(field.attrs.invisible)) {
if (name in groupableFields) {
delete groupableFields[name];
}
if (!additionalMeasures.includes(name)) {
delete measures[name];
return;
}
}
if (field.attrs.interval) {
name += ':' + field.attrs.interval;
}
if (field.attrs.widget) {
widgets[name] = field.attrs.widget;
}
// add active measures to the measure list. This is very rarely
// necessary, but it can be useful if one is working with a
// functional field non stored, but in a model with an overrided
// read_group method. In this case, the pivot view could work, and
// the measure should be allowed. However, be careful if you define
// a measure in your pivot view: non stored functional fields will
// probably not work (their aggregate will always be 0).
if (field.attrs.type === 'measure' && !(name in measures)) {
measures[name] = this.fields[name];
}
if (field.attrs.string && name in measures) {
measures[name].string = field.attrs.string;
}
if (field.attrs.type === 'measure' || 'operator' in field.attrs) {
activeMeasures.push(name);
measures[name] = this.fields[name];
}
if (field.attrs.type === 'col') {
colGroupBys.push(name);
}
if (field.attrs.type === 'row') {
rowGroupBys.push(name);
}
});
if ((!activeMeasures.length) || this.arch.attrs.display_quantity) {
activeMeasures.splice(0, 0, '__count');
}
this.loadParams.measures = activeMeasures;
this.loadParams.colGroupBys = config.device.isMobile ? [] : colGroupBys;
this.loadParams.rowGroupBys = rowGroupBys;
this.loadParams.fields = this.fields;
this.loadParams.default_order = params.default_order || this.arch.attrs.default_order;
this.loadParams.groupableFields = groupableFields;
const disableLinking = !!(this.arch.attrs.disable_linking &&
JSON.stringify(this.arch.attrs.disable_linking));
this.rendererParams.widgets = widgets;
this.rendererParams.disableLinking = disableLinking;
this.controllerParams.disableLinking = disableLinking;
this.controllerParams.title = params.title || this.arch.attrs.string || _t("Untitled");
this.controllerParams.measures = measures;
// retrieve form and list view ids from the action to open those views
// when a data cell of the pivot view is clicked
this.controllerParams.views = [
_findView(params.actionViews, 'list'),
_findView(params.actionViews, 'form'),
];
function _findView(views, viewType) {
const view = views.find(view => {
return view.type === viewType;
});
return [view ? view.viewID : false, viewType];
}
},
/**
*
* @override
*/
getRenderer(parent, state) {
state = Object.assign(state || {}, this.rendererParams);
return new RendererWrapper(parent, this.config.Renderer, state);
},
});
return PivotView;
});
|