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
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
|
odoo.define('web.ReportActionManager', function (require) {
"use strict";
/**
* The purpose of this file is to add the support of Odoo actions of type
* 'ir.actions.report' to the ActionManager.
*/
var ActionManager = require('web.ActionManager');
var core = require('web.core');
var framework = require('web.framework');
var session = require('web.session');
var _t = core._t;
var _lt = core._lt;
// Messages that might be shown to the user dependening on the state of wkhtmltopdf
var link = '<br><br><a href="http://wkhtmltopdf.org/" target="_blank">wkhtmltopdf.org</a>';
var WKHTMLTOPDF_MESSAGES = {
broken: _lt('Your installation of Wkhtmltopdf seems to be broken. The report will be shown ' +
'in html.') + link,
install: _lt('Unable to find Wkhtmltopdf on this system. The report will be shown in ' +
'html.') + link,
upgrade: _lt('You should upgrade your version of Wkhtmltopdf to at least 0.12.0 in order to ' +
'get a correct display of headers and footers as well as support for ' +
'table-breaking between pages.') + link,
workers: _lt('You need to start Odoo with at least two workers to print a pdf version of ' +
'the reports.'),
};
ActionManager.include({
//--------------------------------------------------------------------------
// Private
//--------------------------------------------------------------------------
/**
* Downloads a PDF report for the given url. It blocks the UI during the
* report generation and download.
*
* @param {string} url
* @returns {Promise} resolved when the report has been downloaded ;
* rejected if something went wrong during the report generation
*/
_downloadReport: function (url) {
var self = this;
framework.blockUI();
return new Promise(function (resolve, reject) {
var type = 'qweb-' + url.split('/')[2];
var blocked = !session.get_file({
url: '/report/download',
data: {
data: JSON.stringify([url, type]),
context: JSON.stringify(session.user_context),
},
success: resolve,
error: (error) => {
self.call('crash_manager', 'rpc_error', error);
reject();
},
complete: framework.unblockUI,
});
if (blocked) {
// AAB: this check should be done in get_file service directly,
// should not be the concern of the caller (and that way, get_file
// could return a promise)
var message = _t('A popup window with your report was blocked. You ' +
'may need to change your browser settings to allow ' +
'popup windows for this page.');
self.do_warn(_t('Warning'), message, true);
}
});
},
/**
* Launch download action of the report
*
* @private
* @param {Object} action the description of the action to execute
* @param {Object} options @see doAction for details
* @returns {Promise} resolved when the action has been executed
*/
_triggerDownload: function (action, options, type){
var self = this;
var reportUrls = this._makeReportUrls(action);
return this._downloadReport(reportUrls[type]).then(function () {
if (action.close_on_report_download) {
var closeAction = { type: 'ir.actions.act_window_close' };
return self.doAction(closeAction, _.pick(options, 'on_close'));
} else {
return options.on_close();
}
});
},
/**
* Executes actions of type 'ir.actions.report'.
*
* @private
* @param {Object} action the description of the action to execute
* @param {Object} options @see doAction for details
* @returns {Promise} resolved when the action has been executed
*/
_executeReportAction: function (action, options) {
var self = this;
if (action.report_type === 'qweb-html') {
return this._executeReportClientAction(action, options);
} else if (action.report_type === 'qweb-pdf') {
// check the state of wkhtmltopdf before proceeding
return this.call('report', 'checkWkhtmltopdf').then(function (state) {
// display a notification according to wkhtmltopdf's state
if (state in WKHTMLTOPDF_MESSAGES) {
self.do_notify(_t('Report'), WKHTMLTOPDF_MESSAGES[state], true);
}
if (state === 'upgrade' || state === 'ok') {
// trigger the download of the PDF report
return self._triggerDownload(action, options, 'pdf');
} else {
// open the report in the client action if generating the PDF is not possible
return self._executeReportClientAction(action, options);
}
});
} else if (action.report_type === 'qweb-text') {
return self._triggerDownload(action, options, 'text');
} else {
console.error("The ActionManager can't handle reports of type " +
action.report_type, action);
return Promise.reject();
}
},
/**
* Executes the report client action, either because the report_type is
* 'qweb-html', or because the PDF can't be generated by wkhtmltopdf (in
* the case of 'qweb-pdf' reports).
*
* @param {Object} action
* @param {Object} options
* @returns {Promise} resolved when the client action has been executed
*/
_executeReportClientAction: function (action, options) {
var urls = this._makeReportUrls(action);
var clientActionOptions = _.extend({}, options, {
context: action.context,
data: action.data,
display_name: action.display_name,
name: action.name,
report_file: action.report_file,
report_name: action.report_name,
report_url: urls.html,
});
return this.doAction('report.client_action', clientActionOptions);
},
/**
* Overrides to handle the 'ir.actions.report' actions.
*
* @override
* @private
*/
_handleAction: function (action, options) {
if (action.type === 'ir.actions.report') {
return this._executeReportAction(action, options);
}
return this._super.apply(this, arguments);
},
/**
* Generates an object containing the report's urls (as value) for every
* qweb-type we support (as key). It's convenient because we may want to use
* another report's type at some point (for example, when `qweb-pdf` is not
* available).
*
* @param {Object} action
* @returns {Object}
*/
_makeReportUrls: function (action) {
var reportUrls = {
html: '/report/html/' + action.report_name,
pdf: '/report/pdf/' + action.report_name,
text: '/report/text/' + action.report_name,
};
// We may have to build a query string with `action.data`. It's the place
// were report's using a wizard to customize the output traditionally put
// their options.
if (_.isUndefined(action.data) || _.isNull(action.data) ||
(_.isObject(action.data) && _.isEmpty(action.data))) {
if (action.context.active_ids) {
var activeIDsPath = '/' + action.context.active_ids.join(',');
reportUrls = _.mapObject(reportUrls, function (value) {
return value += activeIDsPath;
});
}
reportUrls.html += '?context=' + encodeURIComponent(JSON.stringify(session.user_context));
} else {
var serializedOptionsPath = '?options=' + encodeURIComponent(JSON.stringify(action.data));
serializedOptionsPath += '&context=' + encodeURIComponent(JSON.stringify(action.context));
reportUrls = _.mapObject(reportUrls, function (value) {
return value += serializedOptionsPath;
});
}
return reportUrls;
},
});
});
|