2018-01-16 11:34:37 +01:00
|
|
|
flectra.define('web.DataExport', function (require) {
|
2018-01-16 06:58:15 +01:00
|
|
|
"use strict";
|
|
|
|
|
|
|
|
var core = require('web.core');
|
|
|
|
var crash_manager = require('web.crash_manager');
|
|
|
|
var data = require('web.data');
|
|
|
|
var Dialog = require('web.Dialog');
|
|
|
|
var framework = require('web.framework');
|
|
|
|
var pyeval = require('web.pyeval');
|
|
|
|
|
|
|
|
var QWeb = core.qweb;
|
|
|
|
var _t = core._t;
|
|
|
|
|
|
|
|
var DataExport = Dialog.extend({
|
|
|
|
template: 'ExportDialog',
|
|
|
|
events: {
|
|
|
|
'click .o_expand_parent': function(e) {
|
|
|
|
this.on_expand_action(this.records[$(e.target).closest('.o_export_tree_item').data('id')]);
|
|
|
|
},
|
|
|
|
'click .o_export_tree_item': function(e) {
|
|
|
|
e.stopPropagation();
|
|
|
|
var $elem = $(e.currentTarget);
|
|
|
|
|
|
|
|
var row_index = $elem.prevAll('.o_export_tree_item').length;
|
|
|
|
var row_index_level = $elem.parents('.o_export_tree_item').length;
|
|
|
|
|
|
|
|
if(e.shiftKey && row_index_level === this.row_index_level) {
|
|
|
|
var minIndex = Math.min(row_index, this.row_index);
|
|
|
|
var maxIndex = Math.max(row_index, this.row_index);
|
|
|
|
|
|
|
|
this.$records.filter(function() { return ($elem.parent()[0] === $(this).parent()[0]); })
|
|
|
|
.slice(minIndex, maxIndex+1)
|
|
|
|
.addClass('o_selected')
|
|
|
|
.filter(':not(:last)')
|
|
|
|
.each(process_children);
|
|
|
|
}
|
|
|
|
|
|
|
|
this.row_index = row_index;
|
|
|
|
this.row_index_level = row_index_level;
|
|
|
|
|
|
|
|
if(e.ctrlKey) {
|
|
|
|
$elem.toggleClass('o_selected').focus();
|
|
|
|
} else if(e.shiftKey) {
|
|
|
|
$elem.addClass('o_selected').focus();
|
|
|
|
} else {
|
|
|
|
this.$(".o_selected").removeClass("o_selected")
|
|
|
|
$elem.addClass("o_selected").focus();
|
|
|
|
}
|
|
|
|
|
|
|
|
function process_children() {
|
|
|
|
var $this = $(this);
|
|
|
|
if($this.hasClass('open')) {
|
|
|
|
$this.children('.o_export_tree_item')
|
|
|
|
.addClass('o_selected')
|
|
|
|
.each(process_children);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
'dblclick .o_export_tree_item': function(e) {
|
|
|
|
var $elem = $(e.currentTarget);
|
|
|
|
$elem.removeClass('o_selected');
|
|
|
|
this.add_field($elem.data('id'), $elem.find('.o_tree_column').first().text());
|
|
|
|
},
|
|
|
|
'keydown .o_export_tree_item': function(e) {
|
|
|
|
e.stopPropagation();
|
|
|
|
var $elem = $(e.currentTarget);
|
|
|
|
var record = this.records[$elem.data('id')];
|
|
|
|
|
|
|
|
switch(e.keyCode || e.which) {
|
|
|
|
case $.ui.keyCode.LEFT:
|
|
|
|
if ($elem.hasClass('open')) {
|
|
|
|
this.on_expand_action(record);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case $.ui.keyCode.RIGHT:
|
|
|
|
if (!$elem.hasClass('open')) {
|
|
|
|
this.on_expand_action(record);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case $.ui.keyCode.UP:
|
|
|
|
var $prev = $elem.prev('.o_export_tree_item');
|
|
|
|
if($prev.length === 1) {
|
|
|
|
while($prev.hasClass('open')) {
|
|
|
|
$prev = $prev.children('.o_export_tree_item').last();
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
$prev = $elem.parent('.o_export_tree_item');
|
|
|
|
if($prev.length === 0) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
$elem.removeClass("o_selected").blur();
|
|
|
|
$prev.addClass("o_selected").focus();
|
|
|
|
break;
|
|
|
|
case $.ui.keyCode.DOWN:
|
|
|
|
var $next;
|
|
|
|
if($elem.hasClass('open')) {
|
|
|
|
$next = $elem.children('.o_export_tree_item').first();
|
|
|
|
} else {
|
|
|
|
$next = $elem.next('.o_export_tree_item');
|
|
|
|
if($next.length === 0) {
|
|
|
|
$next = $elem.parent('.o_export_tree_item').next('.o_export_tree_item');
|
|
|
|
if($next.length === 0) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
$elem.removeClass("o_selected").blur();
|
|
|
|
$next.addClass("o_selected").focus();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
'click .o_add_field': function() {
|
|
|
|
var self = this;
|
|
|
|
this.$('.o_field_tree_structure .o_selected')
|
|
|
|
.removeClass('o_selected')
|
|
|
|
.each(function() {
|
|
|
|
var $this = $(this);
|
|
|
|
self.add_field($this.data('id'), $this.children('.o_tree_column').text());
|
|
|
|
});
|
|
|
|
},
|
|
|
|
'click .o_remove_field': function() {
|
|
|
|
this.$fields_list.find('option:selected').remove();
|
|
|
|
},
|
|
|
|
'click .o_remove_all_field': function() {
|
|
|
|
this.$fields_list.empty();
|
|
|
|
},
|
|
|
|
'click .o_move_up': function() {
|
|
|
|
var $selected_rows = this.$fields_list.find('option:selected');
|
|
|
|
|
|
|
|
var $prev_row = $selected_rows.first().prev();
|
|
|
|
if($prev_row.length){
|
|
|
|
$prev_row.before($selected_rows.detach());
|
|
|
|
}
|
|
|
|
},
|
|
|
|
'click .o_move_down': function () {
|
|
|
|
var $selected_rows = this.$fields_list.find('option:selected');
|
|
|
|
|
|
|
|
var $next_row = $selected_rows.last().next();
|
|
|
|
if($next_row.length){
|
|
|
|
$next_row.after($selected_rows.detach());
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
'click .o_toggle_save_list': function(e) {
|
|
|
|
e.preventDefault();
|
|
|
|
|
|
|
|
var $saveList = this.$(".o_save_list");
|
|
|
|
if($saveList.is(':empty')) {
|
|
|
|
$saveList.append(QWeb.render('Export.SaveList'));
|
|
|
|
} else {
|
|
|
|
if($saveList.is(':hidden')) {
|
|
|
|
$saveList.show();
|
|
|
|
$saveList.find(".o_export_list_input").val("");
|
|
|
|
} else {
|
|
|
|
$saveList.hide();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
'click .o_save_list > button': function(e) {
|
|
|
|
var $saveList = this.$(".o_save_list");
|
|
|
|
|
|
|
|
var value = $saveList.find("input").val();
|
|
|
|
if(!value) {
|
|
|
|
Dialog.alert(this, _t("Please enter save field list name"));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
var fields = this.get_fields();
|
|
|
|
if (fields.length === 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
$saveList.hide();
|
|
|
|
|
|
|
|
var self = this;
|
|
|
|
this.exports.create({
|
|
|
|
name: value,
|
|
|
|
resource: this.record.model,
|
|
|
|
export_fields: _.map(fields, function (field) {
|
|
|
|
return [0, 0, {name: field}];
|
|
|
|
}),
|
|
|
|
}).then(function(export_list_id) {
|
|
|
|
if(!export_list_id) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
var $select = self.$(".o_exported_lists_select");
|
|
|
|
if($select.length === 0 || $select.is(":hidden")) {
|
|
|
|
self.show_exports_list();
|
|
|
|
}
|
|
|
|
$select.append(new Option(value, export_list_id));
|
|
|
|
});
|
|
|
|
},
|
|
|
|
},
|
|
|
|
init: function(parent, record) {
|
|
|
|
var options = {
|
|
|
|
title: _t("Export Data"),
|
|
|
|
buttons: [
|
|
|
|
{text: _t("Export To File"), click: this.export_data, classes: "btn-primary"},
|
|
|
|
{text: _t("Close"), close: true},
|
|
|
|
],
|
|
|
|
};
|
|
|
|
this._super(parent, options);
|
|
|
|
this.records = {};
|
|
|
|
this.record = record;
|
|
|
|
this.exports = new data.DataSetSearch(this, 'ir.exports', this.record.getContext());
|
|
|
|
|
|
|
|
this.row_index = 0;
|
|
|
|
this.row_index_level = 0;
|
|
|
|
},
|
|
|
|
start: function() {
|
|
|
|
var self = this;
|
|
|
|
var waitFor = [this._super.apply(this, arguments)];
|
|
|
|
|
|
|
|
// The default for the ".modal_content" element is "max-height: 100%;"
|
|
|
|
// but we want it to always expand to "height: 100%;" for this modal.
|
|
|
|
// This can be achieved thanks to LESS modification without touching
|
|
|
|
// the ".modal-content" rules... but not with Internet explorer (11).
|
|
|
|
this.$modal.find(".modal-content").css("height", "100%");
|
|
|
|
|
|
|
|
this.$fields_list = this.$('.o_fields_list');
|
|
|
|
this.$import_compat_radios = this.$('.o_import_compat input');
|
|
|
|
|
|
|
|
waitFor.push(this._rpc({route: '/web/export/formats'}).then(do_setup_export_formats));
|
|
|
|
|
|
|
|
var got_fields = new $.Deferred();
|
|
|
|
this.$import_compat_radios.change(function(e) {
|
|
|
|
self.$('.o_field_tree_structure').remove();
|
|
|
|
|
|
|
|
self._rpc({
|
|
|
|
route: '/web/export/get_fields',
|
|
|
|
params: {
|
|
|
|
model: self.record.model,
|
|
|
|
import_compat: !!$(e.target).val(),
|
|
|
|
},
|
|
|
|
})
|
|
|
|
.done(function (records) {
|
|
|
|
var compatible_fields = _.map(records, function (record) {return record.id; });
|
|
|
|
self.$fields_list
|
|
|
|
.find('option')
|
|
|
|
.filter(function () {
|
|
|
|
var option_field = $(this).attr('value');
|
|
|
|
if (compatible_fields.indexOf(option_field) === -1) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.remove();
|
|
|
|
got_fields.resolve();
|
|
|
|
self.on_show_data(records);
|
|
|
|
});
|
|
|
|
}).eq(0).change();
|
|
|
|
waitFor.push(got_fields);
|
|
|
|
|
|
|
|
waitFor.push(this.getParent().getActiveDomain().then(function (domain) {
|
|
|
|
if (domain === undefined) {
|
|
|
|
self.ids_to_export = self.getParent().getSelectedIds();
|
|
|
|
self.domain = self.record.domain;
|
|
|
|
} else {
|
|
|
|
self.ids_to_export = false;
|
|
|
|
self.domain = domain;
|
|
|
|
}
|
|
|
|
self.on_show_domain();
|
|
|
|
}));
|
|
|
|
|
|
|
|
waitFor.push(this.show_exports_list());
|
|
|
|
|
|
|
|
return $.when.apply($, waitFor);
|
|
|
|
|
|
|
|
function do_setup_export_formats(formats) {
|
|
|
|
var $fmts = self.$('.o_export_format');
|
|
|
|
|
|
|
|
_.each(formats, function(format, i) {
|
|
|
|
var $radio = $('<input/>', {type: 'radio', value: format.tag, name: 'o_export_format_name'});
|
|
|
|
var $label = $('<label/>', {html: format.label});
|
|
|
|
|
|
|
|
if (format.error) {
|
|
|
|
$radio.prop('disabled', true);
|
|
|
|
$label.html(_.str.sprintf("%s — %s", format.label, format.error));
|
|
|
|
}
|
|
|
|
|
|
|
|
$fmts.append($("<div/>").append($radio, $label));
|
|
|
|
});
|
|
|
|
|
|
|
|
self.$export_format_inputs = $fmts.find('input');
|
|
|
|
self.$export_format_inputs.first().prop('checked', true);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
show_exports_list: function() {
|
|
|
|
if (this.$('.o_exported_lists_select').is(':hidden')) {
|
|
|
|
this.$('.o_exported_lists').show();
|
|
|
|
return $.when();
|
|
|
|
}
|
|
|
|
|
|
|
|
var self = this;
|
|
|
|
return this._rpc({
|
|
|
|
model: 'ir.exports',
|
|
|
|
method: 'search_read',
|
|
|
|
fields: ['name'],
|
|
|
|
domain: [['resource', '=', this.record.model]]
|
|
|
|
}).then(function (export_list) {
|
|
|
|
if (!export_list.length) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
self.$('.o_exported_lists').append(QWeb.render('Export.SavedList', {'existing_exports': export_list}));
|
|
|
|
self.$('.o_exported_lists_select').on('change', function() {
|
|
|
|
self.$fields_list.empty();
|
|
|
|
var export_id = self.$('.o_exported_lists_select option:selected').val();
|
|
|
|
if(export_id) {
|
|
|
|
self._rpc({
|
|
|
|
route: '/web/export/namelist',
|
|
|
|
params: {
|
|
|
|
model: self.record.model,
|
|
|
|
export_id: parseInt(export_id, 10),
|
|
|
|
},
|
|
|
|
})
|
|
|
|
.then(do_load_export_field);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
self.$('.o_delete_exported_list').click(function() {
|
|
|
|
var select_exp = self.$('.o_exported_lists_select option:selected');
|
|
|
|
var options = {
|
|
|
|
confirm_callback: function () {
|
|
|
|
if (select_exp.val()) {
|
|
|
|
self.exports.unlink([parseInt(select_exp.val(), 10)]);
|
|
|
|
select_exp.remove();
|
|
|
|
self.$fields_list.empty();
|
|
|
|
if (self.$('.o_exported_lists_select option').length <= 1) {
|
|
|
|
self.$('.o_exported_lists').hide();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
Dialog.confirm(this, _t("Do you really want to delete this export template?"), options);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
function do_load_export_field(field_list) {
|
|
|
|
_.each(field_list, function (field) {
|
|
|
|
self.$fields_list.append(new Option(field.label, field.name));
|
|
|
|
});
|
|
|
|
}
|
|
|
|
},
|
|
|
|
on_expand_action: function(record) {
|
|
|
|
if(!record['children']) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
var model = record['params']['model'];
|
|
|
|
var prefix = record['params']['prefix'];
|
|
|
|
var name = record['params']['name'];
|
|
|
|
var exclude_fields = [];
|
|
|
|
if(record['relation_field']) {
|
|
|
|
exclude_fields.push(record['relation_field']);
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!record.loaded) {
|
|
|
|
var self = this;
|
|
|
|
this._rpc({
|
|
|
|
route: '/web/export/get_fields',
|
|
|
|
params: {
|
|
|
|
model: model,
|
|
|
|
prefix: prefix,
|
|
|
|
parent_name: name,
|
|
|
|
import_compat: !!this.$import_compat_radios.filter(':checked').val(),
|
|
|
|
parent_field_type : record['field_type'],
|
|
|
|
exclude: exclude_fields
|
|
|
|
},
|
|
|
|
})
|
|
|
|
.done(function(results) {
|
|
|
|
record.loaded = true;
|
|
|
|
self.on_show_data(results, record.id);
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
this.show_content(record.id);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
on_show_domain: function() {
|
|
|
|
this.$('p').first()
|
|
|
|
.after(QWeb.render('Export.DomainMessage', {record: this}));
|
|
|
|
},
|
|
|
|
on_show_data: function(records, expansion) {
|
|
|
|
var self = this;
|
|
|
|
if(expansion) {
|
|
|
|
this.$('.o_export_tree_item[data-id="' + expansion + '"]')
|
|
|
|
.addClass('open')
|
|
|
|
.find('.o_expand_parent')
|
|
|
|
.toggleClass('fa-plus fa-minus')
|
|
|
|
.next()
|
|
|
|
.after(QWeb.render('Export.TreeItems', {'fields': records, 'debug': this.getSession().debug}));
|
|
|
|
} else {
|
|
|
|
this.$('.o_left_field_panel').empty().append(
|
|
|
|
$("<div/>").addClass('o_field_tree_structure')
|
|
|
|
.append(QWeb.render('Export.TreeItems', {'fields': records, 'debug': this.getSession().debug}))
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
_.extend(this.records, _.object(_.pluck(records, 'id'), records));
|
|
|
|
this.$records = this.$(".o_export_tree_item");
|
|
|
|
this.$records.each(function(i, el) {
|
|
|
|
var $elem = $(el);
|
|
|
|
$elem.find('.o_tree_column').first().toggleClass('o_required', !!self.records[$elem.data('id')].required);
|
|
|
|
});
|
|
|
|
},
|
|
|
|
show_content: function(id) {
|
|
|
|
var $this = this.$('.o_export_tree_item[data-id="' + id + '"]');
|
|
|
|
$this.toggleClass('open');
|
|
|
|
var is_open = $this.hasClass('open');
|
|
|
|
|
|
|
|
$this.children('.o_expand_parent').toggleClass('fa-minus', !!is_open).toggleClass('fa-plus', !is_open);
|
|
|
|
|
|
|
|
var $child_field = $this.find('.o_export_tree_item');
|
|
|
|
var child_len = (id.split("/")).length + 1;
|
|
|
|
for (var i = 0 ; i < $child_field.length ; i++) {
|
|
|
|
var $child = $child_field.eq(i);
|
|
|
|
if(!is_open) {
|
|
|
|
$child.hide();
|
|
|
|
} else if(child_len === $child_field.eq(i).data('id').split("/").length) {
|
|
|
|
if ($child.hasClass('open')) {
|
|
|
|
$child.removeClass('open');
|
|
|
|
$child.children('.o_expand_parent').removeClass('fa-minus').addClass('fa-plus');
|
|
|
|
}
|
|
|
|
$child.show();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
add_field: function(field_id, string) {
|
|
|
|
var $field_list = this.$('.o_fields_list');
|
|
|
|
field_id = this.records[field_id].value || field_id;
|
|
|
|
if($field_list.find("option[value='" + field_id + "']").length === 0) {
|
|
|
|
$field_list.append(new Option(string, field_id));
|
|
|
|
}
|
|
|
|
},
|
|
|
|
get_fields: function() {
|
|
|
|
var $export_fields = this.$(".o_fields_list option").map(function() {
|
|
|
|
return $(this).val();
|
|
|
|
}).get();
|
|
|
|
if($export_fields.length === 0) {
|
|
|
|
Dialog.alert(this, _t("Please select fields to save export list..."));
|
|
|
|
}
|
|
|
|
return $export_fields;
|
|
|
|
},
|
|
|
|
export_data: function() {
|
|
|
|
var self = this;
|
|
|
|
var exported_fields = this.$('.o_fields_list option').map(function () {
|
|
|
|
return {
|
|
|
|
name: (self.records[this.value] || this).value,
|
|
|
|
label: this.textContent || this.innerText // DOM property is textContent, but IE8 only knows innerText
|
|
|
|
};
|
|
|
|
}).get();
|
|
|
|
|
|
|
|
if (_.isEmpty(exported_fields)) {
|
|
|
|
Dialog.alert(this, _t("Please select fields to export..."));
|
|
|
|
return;
|
|
|
|
}
|
2018-04-05 10:25:40 +02:00
|
|
|
exported_fields.unshift({name: 'id', label: _t('External ID')});
|
2018-01-16 06:58:15 +01:00
|
|
|
|
|
|
|
var export_format = this.$export_format_inputs.filter(':checked').val();
|
|
|
|
|
|
|
|
framework.blockUI();
|
|
|
|
this.getSession().get_file({
|
|
|
|
url: '/web/export/' + export_format,
|
|
|
|
data: {data: JSON.stringify({
|
|
|
|
model: this.record.model,
|
|
|
|
fields: exported_fields,
|
|
|
|
ids: this.ids_to_export,
|
|
|
|
domain: this.domain,
|
|
|
|
context: pyeval.eval('contexts', [this.record.getContext()]),
|
|
|
|
import_compat: !!this.$import_compat_radios.filter(':checked').val(),
|
|
|
|
})},
|
|
|
|
complete: framework.unblockUI,
|
|
|
|
error: crash_manager.rpc_error.bind(crash_manager),
|
|
|
|
});
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
|
|
|
return DataExport;
|
|
|
|
|
|
|
|
});
|