summaryrefslogtreecommitdiff
path: root/addons/web/static/src
diff options
context:
space:
mode:
authorLucas Lefèvre <lul@odoo.com>2019-09-12 13:16:10 +0000
committerYannick Tivisse <yti@odoo.com>2019-09-16 13:57:40 +0000
commitac00548b28c0c384b5b1423f65405e4918101872 (patch)
tree37d3d94cc8dd82e0ff295f893f797541fd138a74 /addons/web/static/src
parent35cbb430ce79ce26d8cd1b7c2f9f112b35d1e6a1 (diff)
[IMP] web: Export list view as XLS file
Purpose ======= Have a quick export feature. The current export wizard is quite complex. We want to be able to quickly export a tree view, with the group by correctly represented, depending on the fields visible in the list view. Specification ============= Add an "EXPORT (XLS)" button in the list view (a little download icon) - export only columns visible in list view - export all the records - while respecting the group by This should be a generic feature but should be applied to all the account.move.lines list views. account.view_move_line_tree_grouped_sales_purchases account.view_move_line_tree_grouped_bank_cash account.view_move_line_tree_grouped_misc Task 2031835 closes odoo/odoo#36943 Signed-off-by: Yannick Tivisse (yti) <yti@odoo.com>
Diffstat (limited to 'addons/web/static/src')
-rw-r--r--addons/web/static/src/js/views/list/list_controller.js94
-rw-r--r--addons/web/static/src/xml/base.xml3
2 files changed, 97 insertions, 0 deletions
diff --git a/addons/web/static/src/js/views/list/list_controller.js b/addons/web/static/src/js/views/list/list_controller.js
index 5ec42156000..dc26a750eb4 100644
--- a/addons/web/static/src/js/views/list/list_controller.js
+++ b/addons/web/static/src/js/views/list/list_controller.js
@@ -8,20 +8,78 @@ odoo.define('web.ListController', function (require) {
*/
var core = require('web.core');
+var session = require('web.session');
+var framework = require('web.framework');
var BasicController = require('web.BasicController');
var DataExport = require('web.DataExport');
var Dialog = require('web.Dialog');
+var fieldUtils = require('web.field_utils');
var Sidebar = require('web.Sidebar');
var _t = core._t;
var qweb = core.qweb;
+/**
+ * Return data required for exporting the current list to a xls file.
+ * Recursive if some groups are nested.
+ * Note: this function is also used when the records are not grouped.
+ * In this scenario, an artificial single group with every record in
+ * the list is built.
+ * @param {Array} groups
+ * @param {Array} displayedFields
+ * @param {Array} allFields
+ * @returns {Array} list of groups with the following structure:
+ * 'data': list of records or list of sub-groups
+ * 'isGrouped': true if data contains sub-groups
+ * 'value': title of the group
+ * 'count': number of records in the group
+ * 'aggregateValues': dict container aggregated values of fields {field: aggregatedValue, ...}
+ * 'hideHeader': self explanatory, used when the records are not grouped at all.
+ */
+var processGroups = (groups, displayedFields, allFields) => {
+ let formatRecord = record => {
+ let data = _.pick(record.data, displayedFields)
+ let formattedData = {}
+
+ Object.entries(data).forEach(([field_name, value]) => {
+ let field = allFields[field_name]
+ let formattedValue = fieldUtils.format[field.type](value, field, {data: record.data, forceString: true})
+ formattedData[field_name] = formattedValue.replace('&nbsp;', '')
+ });
+ return formattedData
+ }
+
+ if (groups.length && groups[0].groupedBy && groups[0].groupedBy.length) {
+ // Recursively process sub-group
+ return groups.map(group => ({
+ hideHeader: !!group.hideHeader,
+ isGrouped: true,
+ value: group.value !== undefined && group.value.toString() || _t("Undefined"),
+ count: group.count,
+ aggregateValues: _.pick(group.aggregateValues, displayedFields),
+ data: processGroups(group.data, displayedFields, allFields),
+ }))
+ }
+ // process records
+ return groups.map(group => ({
+ hideHeader: !!group.hideHeader,
+ isGrouped: false,
+ value: group.value !== undefined && group.value.toString() || _t("Undefined"),
+ count: group.count,
+ aggregateValues: _.pick(group.aggregateValues, displayedFields),
+ data: group.data.map(formatRecord)
+ }))
+}
+
var ListController = BasicController.extend({
/**
* This key contains the name of the buttons template to render on top of
* the list view. It can be overridden to add buttons in specific child views.
*/
buttons_template: 'ListView.buttons',
+ events: _.extend({}, BasicController.prototype.events, {
+ 'click .o_list_download': '_onExportRecords',
+ }),
custom_events: _.extend({}, BasicController.prototype.custom_events, {
activate_next_widget: '_onActivateNextWidget',
add_record: '_onAddRecord',
@@ -50,6 +108,9 @@ var ListController = BasicController.extend({
this.noLeaf = params.noLeaf;
this.selectedRecords = params.selectedRecords || [];
this.multipleRecordsSavingPromise = null;
+ this.activeActions = _.extend({}, this.activeActions, {
+ download: this.renderer.arch.attrs.download ? !!JSON.parse(this.renderer.arch.attrs.download) : true,
+ });
},
//--------------------------------------------------------------------------
@@ -339,6 +400,31 @@ var ListController = BasicController.extend({
});
},
/**
+ * Export the current list data in a xls file.
+ *
+ * @private
+ */
+ _downloadList() {
+ let groups = this.renderer.state.data
+ groups = this.renderer.isGrouped ? groups : [{data: groups, count: groups.length, hideHeader: true}] // Artificial single group
+
+ let allFields = this.renderer.state.fields
+ let columns = this.renderer.columns.map(column => ({
+ field: column.attrs.name,
+ aggregateValue: column.aggregate && column.aggregate.value,
+ string: allFields[column.attrs.name].string,
+ }))
+
+ groups = processGroups(groups, columns.map(c => c.field), allFields)
+
+ return session.get_file({
+ url: '/web/list/export_xls',
+ data: {data: JSON.stringify({columns, groups, title: this._title})},
+ complete: framework.unblockUI,
+ error: (error) => this.call('crash_manager', 'rpc_error', error),
+ });
+ },
+ /**
* @override
* @private
*/
@@ -614,6 +700,14 @@ var ListController = BasicController.extend({
new DataExport(this, record, defaultExportFields).open();
},
/**
+ * Export Records in a xls file
+ *
+ * @private
+ */
+ _onExportRecords() {
+ this._downloadList()
+ },
+ /**
* Opens the related form view.
*
* @private
diff --git a/addons/web/static/src/xml/base.xml b/addons/web/static/src/xml/base.xml
index 300aff10c00..733bd37c211 100644
--- a/addons/web/static/src/xml/base.xml
+++ b/addons/web/static/src/xml/base.xml
@@ -271,6 +271,9 @@
Create
</button>
</t>
+ <t t-if="widget.is_action_enabled('download')">
+ <button type="button" class="btn btn-secondary fa fa-download o_list_download" title="Download xls" aria-label="Download xls"/>
+ </t>
</div>
</t>