Add CSV Import/Export
review27421002
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
* Add CSV Import/Export
|
||||
* Manage translated field
|
||||
* Add favorites menu
|
||||
* Show records names in wizard title
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
"gettext.js": "~0.5.0",
|
||||
"d3": "~3.5.0",
|
||||
"c3": "~0.4.10",
|
||||
"papaparse": "~4.1.2"
|
||||
"fullcalendar": "~3.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -17,6 +17,7 @@ this repository contains the full copyright notices and license terms. -->
|
||||
<script type="text/javascript" src="bower_components/gettext.js/dist/gettext.min.js"></script>
|
||||
<script type="text/javascript" src="bower_components/d3/d3.min.js"></script>
|
||||
<script type="text/javascript" src="bower_components/c3/c3.min.js"></script>
|
||||
<script type="text/javascript" src="bower_components/papaparse/papaparse.min.js"></script>
|
||||
<script type="text/javascript" src="bower_components/fullcalendar/dist/fullcalendar.min.js"></script>
|
||||
<script type="text/javascript" src="bower_components/fullcalendar/dist/locale-all.js"></script>
|
||||
|
||||
|
||||
@@ -225,6 +225,15 @@ button {
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
|
||||
.csv {
|
||||
& .modal-dialog {
|
||||
& .column-fields {
|
||||
height: 35vh;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: @screen-xs-max) {
|
||||
.dict-label {
|
||||
text-align: left;
|
||||
|
||||
+13
-1
@@ -363,7 +363,9 @@
|
||||
['glyphicon-comment', Sao.i18n.gettext('Note'), 'note'],
|
||||
['glyphicon-cog', Sao.i18n.gettext('Action'), 'action'],
|
||||
['glyphicon-share-alt', Sao.i18n.gettext('Relate'), 'relate'],
|
||||
['glyphicon-print', Sao.i18n.gettext('Print'), 'print']
|
||||
['glyphicon-print', Sao.i18n.gettext('Print'), 'print'],
|
||||
['glyphicon-export', Sao.i18n.gettext('Export'), 'export'],
|
||||
['glyphicon-import', Sao.i18n.gettext('Import'), 'import']
|
||||
];
|
||||
},
|
||||
create_toolbar: function() {
|
||||
@@ -805,6 +807,16 @@
|
||||
this.buttons.print.find('ul.dropdown-menu')
|
||||
.dropdown('toggle');
|
||||
}.bind(this));
|
||||
},
|
||||
export: function(){
|
||||
new Sao.Window.Export(this.screen,
|
||||
this.screen.current_view.selected_records().map(function(r) {
|
||||
return r.id;
|
||||
}),
|
||||
this.screen.current_view.get_fields());
|
||||
},
|
||||
import: function(){
|
||||
new Sao.Window.Import(this.screen);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
+977
@@ -732,4 +732,981 @@
|
||||
this.callback(revision);
|
||||
}
|
||||
});
|
||||
|
||||
Sao.Window.CSV = Sao.class_(Object, {
|
||||
init: function(title) {
|
||||
this.encodings = ["866", "ansi_x3.4-1968", "arabic", "ascii",
|
||||
"asmo-708", "big5", "big5-hkscs", "chinese", "cn-big5", "cp1250",
|
||||
"cp1251", "cp1252", "cp1253", "cp1254", "cp1255", "cp1256",
|
||||
"cp1257", "cp1258", "cp819", "cp866", "csbig5", "cseuckr",
|
||||
"cseucpkdfmtjapanese", "csgb2312", "csibm866", "csiso2022jp",
|
||||
"csiso2022kr", "csiso58gb231280", "csiso88596e", "csiso88596i",
|
||||
"csiso88598e", "csiso88598i", "csisolatin1", "csisolatin2",
|
||||
"csisolatin3", "csisolatin4", "csisolatin5", "csisolatin6",
|
||||
"csisolatin9", "csisolatinarabic", "csisolatincyrillic",
|
||||
"csisolatingreek", "csisolatinhebrew", "cskoi8r", "csksc56011987",
|
||||
"csmacintosh", "csshiftjis", "cyrillic", "dos-874", "ecma-114",
|
||||
"ecma-118", "elot_928", "euc-jp", "euc-kr", "gb18030", "gb2312",
|
||||
"gb_2312", "gb_2312-80", "gbk", "greek", "greek8", "hebrew",
|
||||
"hz-gb-2312", "ibm819", "ibm866", "iso-2022-cn", "iso-2022-cn-ext",
|
||||
"iso-2022-jp", "iso-2022-kr", "iso-8859-1", "iso-8859-10",
|
||||
"iso-8859-11", "iso-8859-13", "iso-8859-14", "iso-8859-15",
|
||||
"iso-8859-16", "iso-8859-2", "iso-8859-3", "iso-8859-4",
|
||||
"iso-8859-5", "iso-8859-6", "iso-8859-6-e", "iso-8859-6-i",
|
||||
"iso-8859-7", "iso-8859-8", "iso-8859-8-e", "iso-8859-8-i",
|
||||
"iso-8859-9", "iso-ir-100", "iso-ir-101", "iso-ir-109",
|
||||
"iso-ir-110", "iso-ir-126", "iso-ir-127", "iso-ir-138",
|
||||
"iso-ir-144", "iso-ir-148", "iso-ir-149", "iso-ir-157", "iso-ir-58",
|
||||
"iso8859-1", "iso8859-10", "iso8859-11", "iso8859-13", "iso8859-14",
|
||||
"iso8859-15", "iso8859-2", "iso8859-3", "iso8859-4", "iso8859-5",
|
||||
"iso8859-6", "iso8859-7", "iso8859-8", "iso8859-9", "iso88591",
|
||||
"iso885910", "iso885911", "iso885913", "iso885914", "iso885915",
|
||||
"iso88592", "iso88593", "iso88594", "iso88595", "iso88596",
|
||||
"iso88597", "iso88598", "iso88599", "iso_8859-1", "iso_8859-15",
|
||||
"iso_8859-1:1987", "iso_8859-2", "iso_8859-2:1987", "iso_8859-3",
|
||||
"iso_8859-3:1988", "iso_8859-4", "iso_8859-4:1988", "iso_8859-5",
|
||||
"iso_8859-5:1988", "iso_8859-6", "iso_8859-6:1987", "iso_8859-7",
|
||||
"iso_8859-7:1987", "iso_8859-8", "iso_8859-8:1988", "iso_8859-9",
|
||||
"iso_8859-9:1989", "koi", "koi8", "koi8-r", "koi8-ru", "koi8-u",
|
||||
"koi8_r", "korean", "ks_c_5601-1987", "ks_c_5601-1989", "ksc5601",
|
||||
"ksc_5601", "l1", "l2", "l3", "l4", "l5", "l6", "l9", "latin1",
|
||||
"latin2", "latin3", "latin4", "latin5", "latin6", "logical", "mac",
|
||||
"macintosh", "ms932", "ms_kanji", "shift-jis", "shift_jis", "sjis",
|
||||
"sun_eu_greek", "tis-620", "unicode-1-1-utf-8", "us-ascii",
|
||||
"utf-16", "utf-16be", "utf-16le", "utf-8", "utf8", "visual",
|
||||
"windows-1250", "windows-1251", "windows-1252", "windows-1253",
|
||||
"windows-1254", "windows-1255", "windows-1256", "windows-1257",
|
||||
"windows-1258", "windows-31j", "windows-874", "windows-949",
|
||||
"x-cp1250", "x-cp1251", "x-cp1252", "x-cp1253", "x-cp1254",
|
||||
"x-cp1255", "x-cp1256", "x-cp1257", "x-cp1258", "x-euc-jp", "x-gbk",
|
||||
"x-mac-cyrillic", "x-mac-roman", "x-mac-ukrainian", "x-sjis",
|
||||
"x-user-defined", "x-x-big5"];
|
||||
this.dialog = new Sao.Dialog(title, 'csv', 'lg');
|
||||
this.el = this.dialog.modal;
|
||||
|
||||
this.fields = {};
|
||||
this.fields_model = {};
|
||||
jQuery('<button/>', {
|
||||
'class': 'btn btn-link',
|
||||
'type': 'button'
|
||||
}).append(Sao.i18n.gettext('Cancel')).click(function(){
|
||||
this.response('RESPONSE_CANCEL');
|
||||
}.bind(this)).appendTo(this.dialog.footer);
|
||||
|
||||
jQuery('<button/>', {
|
||||
'class': 'btn btn-primary',
|
||||
'type': 'submit'
|
||||
}).append(Sao.i18n.gettext('OK')).click(function(e){
|
||||
this.response('RESPONSE_OK');
|
||||
e.preventDefault();
|
||||
}.bind(this)).appendTo(this.dialog.footer);
|
||||
|
||||
var row_fields = jQuery('<div/>', {
|
||||
'class': 'row'
|
||||
}).appendTo(this.dialog.body);
|
||||
|
||||
jQuery('<hr/>').appendTo(this.dialog.body);
|
||||
|
||||
var column_fields_all = jQuery('<div/>', {
|
||||
'class': 'col-md-4 column-fields'
|
||||
}).append(jQuery('<label/>', {
|
||||
'text': Sao.i18n.gettext('All Fields')
|
||||
})).appendTo(row_fields);
|
||||
|
||||
this.fields_all = jQuery('<ul/>', {
|
||||
'class': 'list-unstyled'
|
||||
}).css('cursor', 'pointer')
|
||||
.appendTo(column_fields_all);
|
||||
|
||||
var prm = jQuery.Deferred();
|
||||
this.get_fields(this.screen.model_name)
|
||||
.done(function(fields){
|
||||
this.model_populate(fields).then(function() {
|
||||
prm.resolve();
|
||||
});
|
||||
this.view_populate(this.fields_model, this.fields_all);
|
||||
}.bind(this));
|
||||
|
||||
this.column_buttons = jQuery('<div/>', {
|
||||
'class': 'col-md-4'
|
||||
}).append('<label/>').appendTo(row_fields);
|
||||
|
||||
var button_add = jQuery('<button/>', {
|
||||
'class': 'btn btn-default btn-block',
|
||||
'type': 'button'
|
||||
}).append(jQuery('<i/>', {
|
||||
'class': 'glyphicon glyphicon-plus'
|
||||
})).click(function(){
|
||||
this.fields_all.find('.bg-primary').each(function(i, el_field) {
|
||||
this.sig_sel_add(el_field);
|
||||
}.bind(this));
|
||||
}.bind(this)).append(' '+Sao.i18n.gettext('Add'))
|
||||
.appendTo(this.column_buttons);
|
||||
|
||||
jQuery('<button/>', {
|
||||
'class': 'btn btn-default btn-block',
|
||||
'type': 'button'
|
||||
}).append(jQuery('<i/>', {
|
||||
'class': 'glyphicon glyphicon-minus'
|
||||
})).click(function(){
|
||||
// sig_unsel
|
||||
this.fields_selected.children('li.bg-primary').remove();
|
||||
}.bind(this)).append(' '+Sao.i18n.gettext('Remove'))
|
||||
.appendTo(this.column_buttons);
|
||||
|
||||
jQuery('<button/>', {
|
||||
'class': 'btn btn-default btn-block',
|
||||
'type': 'button'
|
||||
}).append(jQuery('<i/>', {
|
||||
'class': 'glyphicon glyphicon-remove'
|
||||
})).click(function(){
|
||||
this.fields_selected.empty();
|
||||
}.bind(this)).append(' '+Sao.i18n.gettext('Clear'))
|
||||
.appendTo(this.column_buttons);
|
||||
|
||||
jQuery('<hr>').appendTo(this.column_buttons);
|
||||
|
||||
var column_fields_selected = jQuery('<div/>', {
|
||||
'class': 'col-md-4 column-fields'
|
||||
}).append(jQuery('<label/>', {
|
||||
'text': Sao.i18n.gettext('Fields Selected')
|
||||
})).appendTo(row_fields);
|
||||
|
||||
// TODO: Make them draggable to re-order
|
||||
this.fields_selected = jQuery('<ul/>', {
|
||||
'class': 'list-unstyled'
|
||||
}).css('cursor', 'pointer').appendTo(column_fields_selected);
|
||||
|
||||
this.chooser_form = jQuery('<div/>', {
|
||||
'class': 'form-inline'
|
||||
}).appendTo(this.dialog.body);
|
||||
|
||||
var row_csv_param = jQuery('<div/>', {
|
||||
'class': 'row'
|
||||
}).appendTo(this.dialog.body);
|
||||
|
||||
var expander_icon = jQuery('<span/>', {
|
||||
'class': 'glyphicon glyphicon-plus',
|
||||
}).css('cursor', 'pointer').html(' ');
|
||||
|
||||
var csv_param_label = jQuery('<label/>', {
|
||||
'text': Sao.i18n.gettext('CSV Parameters')
|
||||
}).css('cursor', 'pointer');
|
||||
|
||||
jQuery('<div/>', {
|
||||
'class': 'col-md-12'
|
||||
}).append(expander_icon).append(csv_param_label)
|
||||
.on('click', function(){
|
||||
expander_icon.toggleClass('glyphicon-plus')
|
||||
.toggleClass('glyphicon-minus');
|
||||
this.expander_csv.collapse('toggle');
|
||||
}.bind(this)).appendTo(row_csv_param);
|
||||
|
||||
this.expander_csv = jQuery('<div/>', {
|
||||
'id': 'expander_csv',
|
||||
'class': 'collapse'
|
||||
}).appendTo(row_csv_param);
|
||||
|
||||
var delimiter_label = jQuery('<label/>', {
|
||||
'text': Sao.i18n.gettext('Delimiter:'),
|
||||
'class': 'col-sm-2 control-label',
|
||||
'for': 'input-delimiter'
|
||||
});
|
||||
|
||||
this.el_csv_delimiter = jQuery('<input/>', {
|
||||
'type': 'text',
|
||||
'class': 'form-control',
|
||||
'id': 'input-delimiter',
|
||||
'size': '1',
|
||||
'maxlength': '1',
|
||||
'value': ','
|
||||
});
|
||||
|
||||
jQuery('<div/>', {
|
||||
'class': 'form-group'
|
||||
}).append(delimiter_label).append(jQuery('<div/>', {
|
||||
'class': 'col-sm-4'
|
||||
}).append(this.el_csv_delimiter)).appendTo(this.expander_csv);
|
||||
|
||||
var quotechar_label = jQuery('<label/>', {
|
||||
'text': Sao.i18n.gettext('Quote Char:'),
|
||||
'class': 'col-sm-2 control-label',
|
||||
'for': 'input-quotechar'
|
||||
});
|
||||
|
||||
this.el_csv_quotechar = jQuery('<input/>', {
|
||||
'type': 'text',
|
||||
'class': 'form-control',
|
||||
'id': 'input-quotechar',
|
||||
'size': '1',
|
||||
'maxlength': '1',
|
||||
'value': '\"',
|
||||
'readonly': '' // Until PapaParse releases custom quote feature
|
||||
});
|
||||
|
||||
jQuery('<div/>', {
|
||||
'class': 'form-group'
|
||||
}).append(quotechar_label).append(jQuery('<div/>', {
|
||||
'class': 'col-sm-4'
|
||||
}).append(this.el_csv_quotechar))
|
||||
.appendTo(this.expander_csv);
|
||||
|
||||
var encoding_label = jQuery('<label/>', {
|
||||
'text': Sao.i18n.gettext('Encoding:'),
|
||||
'class': 'col-sm-2 control-label',
|
||||
'for': 'input-encoding'
|
||||
});
|
||||
|
||||
this.el_csv_encoding = jQuery('<select/>', {
|
||||
'class': 'form-control',
|
||||
'id': 'input-encoding'
|
||||
});
|
||||
|
||||
for(var i=0; i<this.encodings.length; i++) {
|
||||
jQuery('<option/>', {
|
||||
'val': this.encodings[i]
|
||||
}).html(this.encodings[i]).appendTo(this.el_csv_encoding);
|
||||
}
|
||||
|
||||
var enc = 'utf-8';
|
||||
if (navigator.platform == 'Win32' ||
|
||||
navigator.platform == 'Windows') {
|
||||
enc = 'cp1252';
|
||||
}
|
||||
this.el_csv_encoding.children('option[value="' + enc + '"]')
|
||||
.attr('selected', 'selected');
|
||||
|
||||
jQuery('<div/>', {
|
||||
'class': 'form-group'
|
||||
}).append(encoding_label).append(jQuery('<div/>', {
|
||||
'class': 'col-sm-4'
|
||||
}).append(this.el_csv_encoding))
|
||||
.appendTo(this.expander_csv);
|
||||
|
||||
this.el.modal('show');
|
||||
this.el.on('hidden.bs.modal', function() {
|
||||
jQuery(this).remove();
|
||||
});
|
||||
return prm;
|
||||
},
|
||||
get_fields: function(model) {
|
||||
return Sao.rpc({
|
||||
'method': 'model.' + model + '.fields_get'
|
||||
}, this.session);
|
||||
},
|
||||
on_row_expanded: function(node) {
|
||||
var container_view = jQuery('<ul/>').css('list-style', 'none')
|
||||
.insertAfter(node.view);
|
||||
this.children_expand(node).done(function() {
|
||||
this.view_populate(node.children, container_view);
|
||||
}.bind(this));
|
||||
}
|
||||
});
|
||||
|
||||
Sao.Window.Import = Sao.class_(Sao.Window.CSV, {
|
||||
init: function(screen) {
|
||||
this.screen = screen;
|
||||
this.session = Sao.Session.current_session;
|
||||
this.fields_data = {}; // Ask before Removing this.
|
||||
this.fields_invert = {};
|
||||
Sao.Window.Import._super.init.call(this,
|
||||
Sao.i18n.gettext('Import from CSV'));
|
||||
|
||||
jQuery('<button/>', {
|
||||
'class': 'btn btn-default btn-block',
|
||||
'type': 'button'
|
||||
}).append(jQuery('<i/>', {
|
||||
'class': 'glyphicon glyphicon-search'
|
||||
})).click(function(){
|
||||
this.autodetect();
|
||||
}.bind(this)).append(' '+Sao.i18n.gettext('Auto-Detect'))
|
||||
.appendTo(this.column_buttons);
|
||||
|
||||
var chooser_label = jQuery('<label/>', {
|
||||
'text': Sao.i18n.gettext('File to Import'),
|
||||
'class': 'col-sm-6 control-label',
|
||||
'for': 'input-csv-file'
|
||||
});
|
||||
|
||||
this.file_input = jQuery('<input/>', {
|
||||
'type': 'file',
|
||||
'id': 'input-csv-file'
|
||||
});
|
||||
|
||||
jQuery('<div/>', {
|
||||
'class': 'form-group'
|
||||
}).append(chooser_label).append(jQuery('<div/>', {
|
||||
'class': 'col-sm-6'
|
||||
}).append(this.file_input))
|
||||
.appendTo(this.chooser_form);
|
||||
|
||||
jQuery('<hr>').insertAfter(this.chooser_form);
|
||||
|
||||
var skip_label = jQuery('<label/>', {
|
||||
'text': Sao.i18n.gettext('Lines to Skip:'),
|
||||
'class': 'col-sm-2 control-label',
|
||||
'for': 'input-skip'
|
||||
});
|
||||
|
||||
this.el_csv_skip = jQuery('<input/>', {
|
||||
'type': 'number',
|
||||
'class': 'form-control',
|
||||
'id': 'input-skip',
|
||||
'value': '0'
|
||||
});
|
||||
|
||||
jQuery('<div/>', {
|
||||
'class': 'form-group'
|
||||
}).append(skip_label).append(jQuery('<div/>', {
|
||||
'class': 'col-sm-4'
|
||||
}).append(this.el_csv_skip))
|
||||
.appendTo(this.expander_csv);
|
||||
},
|
||||
sig_sel_add: function(el_field) {
|
||||
el_field = jQuery(el_field);
|
||||
var field = el_field.attr('field');
|
||||
var node = jQuery('<li/>', {
|
||||
'field': field,
|
||||
}).html(el_field.attr('name')).click(function(e) {
|
||||
if (e.ctrlKey) {
|
||||
node.toggleClass('bg-primary');
|
||||
} else {
|
||||
jQuery(e.target).addClass('bg-primary')
|
||||
.siblings().removeClass('bg-primary');
|
||||
}
|
||||
}).appendTo(this.fields_selected);
|
||||
},
|
||||
view_populate: function (parent_node, parent_view) {
|
||||
var fields_order = Object.keys(parent_node).sort(function(a,b) {
|
||||
if (parent_node[b].string < parent_node[a].string) {
|
||||
return -1;
|
||||
}
|
||||
else {
|
||||
return 1;
|
||||
}
|
||||
}).reverse();
|
||||
|
||||
fields_order.forEach(function(field) {
|
||||
var name = parent_node[field].string || field;
|
||||
var node = jQuery('<li/>', {
|
||||
'field': parent_node[field].field,
|
||||
'name': parent_node[field].name
|
||||
}).html(name).click(function(e) {
|
||||
if(e.ctrlKey) {
|
||||
node.toggleClass('bg-primary');
|
||||
} else {
|
||||
this.fields_all.find('li').removeClass('bg-primary');
|
||||
node.addClass('bg-primary');
|
||||
}
|
||||
}.bind(this)).appendTo(parent_view);
|
||||
parent_node[field].view = node;
|
||||
|
||||
if (parent_node[field].relation) {
|
||||
node.prepend(' ');
|
||||
var expander_icon = jQuery('<i/>', {
|
||||
'class': 'glyphicon glyphicon-plus'
|
||||
}).click(function(e) {
|
||||
e.stopPropagation();
|
||||
expander_icon.toggleClass('glyphicon-plus')
|
||||
.toggleClass('glyphicon-minus');
|
||||
if(expander_icon.hasClass('glyphicon-minus')) {
|
||||
this.on_row_expanded(parent_node[field]);
|
||||
}
|
||||
else {
|
||||
node.next('ul').remove();
|
||||
}
|
||||
}.bind(this)).prependTo(node);
|
||||
}
|
||||
}.bind(this));
|
||||
},
|
||||
model_populate: function (fields, parent_node, prefix_field,
|
||||
prefix_name) {
|
||||
parent_node = parent_node || this.fields_model;
|
||||
prefix_field = prefix_field || '';
|
||||
prefix_name = prefix_name || '';
|
||||
|
||||
Object.keys(fields).forEach(function(field) {
|
||||
if(!fields[field].readonly) {
|
||||
var name = fields[field].string || field;
|
||||
name = prefix_name + name;
|
||||
// Only One2Many can be nested for import
|
||||
var relation;
|
||||
if (fields[field].type == 'one2many') {
|
||||
relation = fields[field].relation;
|
||||
} else {
|
||||
relation = null;
|
||||
}
|
||||
var node = {
|
||||
name: name,
|
||||
field: prefix_field + field,
|
||||
relation: relation,
|
||||
string: fields[field].string
|
||||
};
|
||||
parent_node[field] = node;
|
||||
this.fields[prefix_field + field] = node;
|
||||
this.fields_invert[name] = prefix_field + field;
|
||||
if (relation) {
|
||||
node.children = {};
|
||||
}
|
||||
}
|
||||
}.bind(this));
|
||||
return jQuery.Deferred();
|
||||
},
|
||||
children_expand: function(node) {
|
||||
var dfd = jQuery.Deferred();
|
||||
if (jQuery.isEmptyObject(node.children)) {
|
||||
this.get_fields(node.relation).done(function(fields) {
|
||||
this.model_populate(fields, node.children,
|
||||
node.field + '/', node.name + '/');
|
||||
dfd.resolve(this);
|
||||
}.bind(this));
|
||||
} else {
|
||||
dfd.resolve(this);
|
||||
}
|
||||
return dfd.promise();
|
||||
},
|
||||
autodetect: function() {
|
||||
var fname = this.file_input.val();
|
||||
if(!fname) {
|
||||
Sao.common.message.run(
|
||||
Sao.i18n.gettext('You must select an import file first'));
|
||||
return;
|
||||
}
|
||||
this.fields_selected.empty();
|
||||
this.el_csv_skip.val(1);
|
||||
Papa.parse(this.file_input[0].files[0], {
|
||||
config: {
|
||||
delimiter: this.el_csv_delimiter.val(),
|
||||
// TODO quoteChar: this.el_csv_quotechar.val(),
|
||||
preview: 1,
|
||||
encoding: this.el_csv_encoding.val()
|
||||
},
|
||||
error: function(err, file, inputElem, reason) {
|
||||
Sao.common.warning(
|
||||
Sao.i18n.gettext('Error occured in loading the file'));
|
||||
},
|
||||
complete: function(results) {
|
||||
results.data[0].forEach(function(word) {
|
||||
if(word in this.fields_invert || word in this.fields) {
|
||||
this.auto_select(word);
|
||||
}
|
||||
else {
|
||||
var fields = this.fields_model;
|
||||
var prefix = '';
|
||||
var parents = word.split('/');
|
||||
this.traverse(fields, prefix, parents, 0);
|
||||
}
|
||||
}.bind(this));
|
||||
}.bind(this)
|
||||
});
|
||||
},
|
||||
auto_select: function(word) {
|
||||
var name,field;
|
||||
if(word in this.fields_invert) {
|
||||
name = word;
|
||||
field = this.fields_invert[word];
|
||||
}
|
||||
else if (word in this.fields) {
|
||||
name = this.fields[word].name;
|
||||
field = [word];
|
||||
}
|
||||
else {
|
||||
Sao.common.warning.run(
|
||||
Sao.i18n.gettext(
|
||||
'Error processing the file at field %1.', word),
|
||||
Sao.i18n.gettext('Error'));
|
||||
return;
|
||||
}
|
||||
var node = jQuery('<li/>', {
|
||||
'field': field
|
||||
}).html(name).click(function(){
|
||||
node.addClass('bg-primary')
|
||||
.siblings().removeClass('bg-primary');
|
||||
}).appendTo(this.fields_selected);
|
||||
},
|
||||
traverse: function(fields, prefix, parents, i) {
|
||||
if(i >= parents.length - 1) {
|
||||
this.auto_select(parents.join('/'));
|
||||
return;
|
||||
}
|
||||
var field, item;
|
||||
var names = Object.keys(fields);
|
||||
for(item = 0; item<names.length; item++) {
|
||||
field = fields[names[item]];
|
||||
if(field.name == (prefix+parents[i]) ||
|
||||
field.field == (prefix+parents[i])) {
|
||||
this.children_expand(field).done(callback);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(item == names.length) {
|
||||
this.auto_select(parents.join('/'));
|
||||
return;
|
||||
}
|
||||
function callback(self) {
|
||||
fields = field.children;
|
||||
prefix += parents[i] + '/';
|
||||
self.traverse(fields, prefix, parents, ++i);
|
||||
}
|
||||
},
|
||||
response: function(response_id) {
|
||||
if(response_id == 'RESPONSE_OK') {
|
||||
var fields = [];
|
||||
this.fields_selected.children('li').each(function(i, field_el) {
|
||||
fields.push(field_el.getAttribute('field'));
|
||||
});
|
||||
this.el.modal('hide');
|
||||
var fname = this.file_input.val();
|
||||
if(fname) {
|
||||
this.import_csv(fname, fields);
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.el.modal('hide');
|
||||
}
|
||||
},
|
||||
import_csv: function(fname, fields) {
|
||||
var skip = this.el_csv_skip.val();
|
||||
var encoding = this.el_csv_encoding.val();
|
||||
|
||||
Papa.parse(this.file_input[0].files[0], {
|
||||
config: {
|
||||
delimiter: this.el_csv_delimiter.val(),
|
||||
// TODO quoteChar: this.el_csv_quotechar.val(),
|
||||
encoding: encoding
|
||||
},
|
||||
error: function(err, file, inputElem, reason) {
|
||||
Sao.common.warning(
|
||||
Sao.i18n.gettext('Error occured in loading the file'));
|
||||
},
|
||||
complete: function(results) {
|
||||
function encode_utf8(s) {
|
||||
return unescape(encodeURIComponent(s));
|
||||
}
|
||||
var data = [];
|
||||
results.data.pop('');
|
||||
results.data.forEach(function(line, i) {
|
||||
if(i < skip) {
|
||||
return;
|
||||
}
|
||||
var arr = [];
|
||||
line.forEach(function(x){
|
||||
arr.push(encode_utf8(x));
|
||||
});
|
||||
data.push(arr);
|
||||
});
|
||||
Sao.rpc({
|
||||
'method': 'model.' + this.screen.model_name +
|
||||
'.import_data',
|
||||
'params': [fields, data, {}]
|
||||
}, this.session).done(function(count) {
|
||||
Sao.common.message.run(
|
||||
Sao.i18n.ngettext('%1 record imported',
|
||||
'%1 records imported', count));
|
||||
});
|
||||
}.bind(this)
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
Sao.Window.Export = Sao.class_(Sao.Window.CSV, {
|
||||
init: function(screen, ids, names) {
|
||||
this.ids = ids;
|
||||
this.screen = screen;
|
||||
this.session = Sao.Session.current_session;
|
||||
Sao.Window.Export._super.init.call(this,
|
||||
Sao.i18n.gettext('Export to CSV')).then(function() {
|
||||
names.forEach(function(name) {
|
||||
this.sel_field(name);
|
||||
}.bind(this));
|
||||
}.bind(this));
|
||||
|
||||
var row_header = jQuery('<div/>', {
|
||||
'class': 'row'
|
||||
}).prependTo(this.dialog.body);
|
||||
|
||||
var predefined_exports_column = jQuery('<div/>', {
|
||||
'class': 'col-md-12'
|
||||
}).append(jQuery('<label/>', {
|
||||
'text': Sao.i18n.gettext('Predefined Exports')
|
||||
})).appendTo(row_header);
|
||||
|
||||
this.predef_exports_list = jQuery('<ul/>', {
|
||||
'class': 'list-unstyled predef-exports'
|
||||
}).css('cursor', 'pointer')
|
||||
.appendTo(predefined_exports_column);
|
||||
|
||||
predefined_exports_column.append('<hr/>');
|
||||
|
||||
this.predef_exports = {};
|
||||
this.fill_predefwin();
|
||||
|
||||
jQuery('<button/>', {
|
||||
'class': 'btn btn-default btn-block',
|
||||
'type': 'button'
|
||||
}).append(jQuery('<i/>', {
|
||||
'class': 'glyphicon glyphicon-floppy-save'
|
||||
})).click(function(){
|
||||
this.addreplace_predef();
|
||||
}.bind(this)).append(' '+Sao.i18n.gettext('Save Export'))
|
||||
.appendTo(this.column_buttons);
|
||||
|
||||
jQuery('<button/>', {
|
||||
'class': 'btn btn-default btn-block',
|
||||
'type': 'button'
|
||||
}).append(jQuery('<i/>', {
|
||||
'class': 'glyphicon glyphicon-floppy-remove'
|
||||
})).click(function(){
|
||||
this.remove_predef();
|
||||
}.bind(this)).append(' '+Sao.i18n.gettext('Delete Export'))
|
||||
.appendTo(this.column_buttons);
|
||||
|
||||
this.el_add_field_names = jQuery('<input/>', {
|
||||
'type': 'checkbox',
|
||||
'checked': 'checked'
|
||||
});
|
||||
|
||||
jQuery('<div/>', {
|
||||
'class': 'form-group'
|
||||
}).append(jQuery('<div/>', {
|
||||
'class': 'col-md-6'
|
||||
}).append(jQuery('<label/>', {
|
||||
'text': ' '+Sao.i18n.gettext('Add Field Names')
|
||||
}).prepend(this.el_add_field_names))).appendTo(this.expander_csv);
|
||||
},
|
||||
view_populate: function(parent_node, parent_view) {
|
||||
var names = Object.keys(parent_node).sort(function(a, b) {
|
||||
if (parent_node[b].string < parent_node[a].string) {
|
||||
return -1;
|
||||
}
|
||||
else {
|
||||
return 1;
|
||||
}
|
||||
}).reverse();
|
||||
|
||||
names.forEach(function(name) {
|
||||
var path = parent_node[name].path;
|
||||
var node = jQuery('<li/>', {
|
||||
'path': path
|
||||
}).html(parent_node[name].string).click(function(e) {
|
||||
if(e.ctrlKey) {
|
||||
node.toggleClass('bg-primary');
|
||||
} else {
|
||||
this.fields_all.find('li')
|
||||
.removeClass('bg-primary');
|
||||
node.addClass('bg-primary');
|
||||
}
|
||||
}.bind(this)).appendTo(parent_view);
|
||||
parent_node[name].view = node;
|
||||
|
||||
if (parent_node[name].children) {
|
||||
node.prepend(' ');
|
||||
var expander_icon = jQuery('<i/>', {
|
||||
'class': 'glyphicon glyphicon-plus'
|
||||
}).click(function(e){
|
||||
e.stopPropagation();
|
||||
expander_icon.toggleClass('glyphicon-plus')
|
||||
.toggleClass('glyphicon-minus');
|
||||
if(expander_icon.hasClass('glyphicon-minus')) {
|
||||
this.on_row_expanded(parent_node[name]);
|
||||
}
|
||||
else {
|
||||
node.next('ul').remove();
|
||||
}
|
||||
}.bind(this)).prependTo(node);
|
||||
}
|
||||
}.bind(this));
|
||||
},
|
||||
model_populate: function(fields, parent_node, prefix_field,
|
||||
prefix_name) {
|
||||
parent_node = parent_node || this.fields_model;
|
||||
prefix_field = prefix_field || '';
|
||||
prefix_name = prefix_name || '';
|
||||
|
||||
var prm = jQuery.Deferred();
|
||||
Object.keys(fields).forEach(function(name) {
|
||||
var field = fields[name];
|
||||
var string = field.string || name;
|
||||
var items = [{ name: name, field: field, string: string }];
|
||||
|
||||
if (field.type == 'selection') {
|
||||
items.push({
|
||||
name: name+'.translated',
|
||||
field: field,
|
||||
string: Sao.i18n.gettext('%1 (string)', string)
|
||||
});
|
||||
}
|
||||
|
||||
items.forEach(function(item) {
|
||||
var path = prefix_field + item.name;
|
||||
var long_string = item.string;
|
||||
|
||||
if (prefix_field) {
|
||||
long_string = prefix_name + item.string;
|
||||
}
|
||||
|
||||
var node = {
|
||||
path: path,
|
||||
string: item.string,
|
||||
long_string: long_string,
|
||||
relation: item.field.relation
|
||||
};
|
||||
parent_node[item.name] = node;
|
||||
this.fields[path] = node;
|
||||
|
||||
// Insert relation only to real field
|
||||
if (item.name.indexOf('.') == -1 && item.field.relation) {
|
||||
node.children = {};
|
||||
}
|
||||
}.bind(this));
|
||||
}.bind(this));
|
||||
prm.resolve();
|
||||
return prm;
|
||||
},
|
||||
children_expand: function(node) {
|
||||
var dfd = jQuery.Deferred();
|
||||
if(jQuery.isEmptyObject(node.children)) {
|
||||
this.get_fields(node.relation).done(function(fields) {
|
||||
this.model_populate(fields, node.children,
|
||||
node.path + '/', node.string + '/');
|
||||
dfd.resolve(this);
|
||||
}.bind(this));
|
||||
} else {
|
||||
dfd.resolve(this);
|
||||
}
|
||||
return dfd.promise();
|
||||
},
|
||||
sig_sel_add: function(el_field) {
|
||||
el_field = jQuery(el_field);
|
||||
var name = el_field.attr('path');
|
||||
this.sel_field(name);
|
||||
},
|
||||
fill_predefwin: function() {
|
||||
Sao.rpc({
|
||||
'method': 'model.ir.export.search',
|
||||
'params': [['resource', '=', this.screen.model_name], {}]
|
||||
}, this.session).done(function(export_ids) {
|
||||
Sao.rpc({
|
||||
'method': 'model.ir.export.read',
|
||||
'params': [export_ids, {}]
|
||||
}, this.session).done(function(exports) {
|
||||
var arr = [];
|
||||
exports.forEach(function(o) {
|
||||
for (var i = 0; i < o.export_fields.length;
|
||||
arr.push(o.export_fields[i++]));
|
||||
});
|
||||
Sao.rpc({
|
||||
'method': 'model.ir.export.line.read',
|
||||
'params': [arr, {}]
|
||||
}, this.session).done(function(lines) {
|
||||
var id2lines = {};
|
||||
lines.forEach(function(line) {
|
||||
id2lines[line.export] = id2lines[line.export] || [];
|
||||
id2lines[line.export].push(line);
|
||||
});
|
||||
exports.forEach(function(export_) {
|
||||
this.predef_exports[export_.id] =
|
||||
id2lines[export_.id].map(function(obj) {
|
||||
if(obj.export == export_.id)
|
||||
return obj.name;
|
||||
});
|
||||
this.add_to_predef(export_.id, export_.name);
|
||||
}.bind(this));
|
||||
this.predef_exports_list.children('li').first().focus();
|
||||
}.bind(this));
|
||||
}.bind(this));
|
||||
}.bind(this));
|
||||
},
|
||||
add_to_predef: function(id, name) {
|
||||
var node = jQuery('<li/>', {
|
||||
'text': name,
|
||||
'export_id': id,
|
||||
'tabindex': 0
|
||||
}).on('keypress', function(e) {
|
||||
var keyCode = (e.keyCode ? e.keyCode : e.which);
|
||||
if(keyCode == 13 || keyCode == 32) {
|
||||
node.click();
|
||||
}
|
||||
}).click(function(event) {
|
||||
node.addClass('bg-primary')
|
||||
.siblings().removeClass('bg-primary');
|
||||
this.sel_predef(jQuery(event.target).attr('export_id'));
|
||||
}.bind(this));
|
||||
this.predef_exports_list.append(node);
|
||||
},
|
||||
addreplace_predef: function() {
|
||||
var fields = [];
|
||||
var selected_fields = this.fields_selected.children('li');
|
||||
for(var i=0; i<selected_fields.length; i++) {
|
||||
fields.push(selected_fields[i].getAttribute('path'));
|
||||
}
|
||||
if(fields.length === 0) {
|
||||
return;
|
||||
}
|
||||
var pref_id, name;
|
||||
var selection = this.predef_exports_list.children('li.bg-primary');
|
||||
if (selection.length === 0) {
|
||||
pref_id = null;
|
||||
Sao.common.ask.run(
|
||||
Sao.i18n.gettext('What is the name of this export?'))
|
||||
.then(function(name) {
|
||||
if (!name) {
|
||||
return;
|
||||
}
|
||||
this.save_predef(name, fields, selection);
|
||||
}.bind(this));
|
||||
}
|
||||
else {
|
||||
pref_id = selection.attr('export_id');
|
||||
name = selection.text();
|
||||
Sao.common.sur.run(
|
||||
Sao.i18n.gettext('Override %1 definition?', name))
|
||||
.done(function() {
|
||||
this.save_predef(name, fields, selection);
|
||||
Sao.rpc({
|
||||
'method': 'model.ir.export.delete',
|
||||
'params': [[pref_id], {}]
|
||||
}, this.session).then(function() {
|
||||
delete this.predef_exports[pref_id];
|
||||
}.bind(this));
|
||||
}.bind(this));
|
||||
}
|
||||
},
|
||||
save_predef: function(name, fields, selection) {
|
||||
Sao.rpc({
|
||||
'method': 'model.ir.export.create',
|
||||
'params': [[{
|
||||
'name': name,
|
||||
'resource': this.screen.model_name,
|
||||
'export_fields': [['create', fields.map(function(x) {
|
||||
return {
|
||||
'name': x
|
||||
};
|
||||
})]]
|
||||
}], {}]
|
||||
}, this.session).then(function(new_id) {
|
||||
this.predef_exports[new_id] = fields;
|
||||
if (selection.length === 0) {
|
||||
this.add_to_predef(new_id, name);
|
||||
}
|
||||
else {
|
||||
this.predef_exports[new_id] = fields;
|
||||
selection.attr('export_id', new_id);
|
||||
}
|
||||
}.bind(this));
|
||||
},
|
||||
remove_predef: function() {
|
||||
var selection = this.predef_exports_list.children('li.bg-primary');
|
||||
if (selection.length === 0) {
|
||||
return;
|
||||
}
|
||||
var export_id = jQuery(selection).attr('export_id');
|
||||
Sao.rpc({
|
||||
'method': 'model.ir.export.delete',
|
||||
'params': [[export_id], {}]
|
||||
}, this.session).then(function() {
|
||||
delete this.predef_exports[export_id];
|
||||
selection.remove();
|
||||
}.bind(this));
|
||||
},
|
||||
sel_predef: function(export_id) {
|
||||
this.fields_selected.empty();
|
||||
this.predef_exports[export_id].forEach(function(name) {
|
||||
if (!(name in this.fields)) {
|
||||
var fields = this.fields_model;
|
||||
var prefix = '';
|
||||
var parents = name.split('/');
|
||||
this.traverse(fields, prefix, parents, 0);
|
||||
}
|
||||
if(!(name in this.fields)) {
|
||||
return;
|
||||
}
|
||||
this.sel_field(name);
|
||||
}.bind(this));
|
||||
},
|
||||
traverse: function(fields, prefix, parents, i) {
|
||||
if(i >= parents.length-1) {
|
||||
this.sel_field(parents.join('/'));
|
||||
return;
|
||||
}
|
||||
var field, item;
|
||||
var names = Object.keys(fields);
|
||||
for(item = 0; item < names.length; item++) {
|
||||
field = fields[names[item]];
|
||||
if(field.path == (prefix+parents[i])) {
|
||||
this.children_expand(field).done(callback);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(item == names.length) {
|
||||
this.sel_field(parents.join('/'));
|
||||
return;
|
||||
}
|
||||
function callback(self){
|
||||
fields = field.children;
|
||||
prefix += parents[i] + '/';
|
||||
self.traverse(fields, prefix, parents, ++i);
|
||||
}
|
||||
},
|
||||
sel_field: function(name) {
|
||||
var long_string = this.fields[name].long_string;
|
||||
var relation = this.fields[name].relation;
|
||||
if (relation) {
|
||||
return;
|
||||
}
|
||||
var node = jQuery('<li/>', {
|
||||
'path': name,
|
||||
}).html(long_string).click(function(e) {
|
||||
if(e.ctrlKey) {
|
||||
node.toggleClass('bg-primary');
|
||||
} else {
|
||||
jQuery(e.target).addClass('bg-primary')
|
||||
.siblings().removeClass('bg-primary');
|
||||
}
|
||||
}).appendTo(this.fields_selected);
|
||||
},
|
||||
response: function(response_id) {
|
||||
if(response_id == 'RESPONSE_OK') {
|
||||
var fields = [];
|
||||
var fields2 = [];
|
||||
this.fields_selected.children('li').each(function(i, field) {
|
||||
fields.push(field.getAttribute('path'));
|
||||
fields2.push(field.innerText);
|
||||
});
|
||||
Sao.rpc({
|
||||
'method': 'model.' + this.screen.model_name +
|
||||
'.export_data',
|
||||
'params': [this.ids, fields, {}]
|
||||
}, this.session).then(function(data) {
|
||||
this.export_csv(fields2, data);
|
||||
}.bind(this));
|
||||
} else {
|
||||
this.el.modal('hide');
|
||||
}
|
||||
},
|
||||
export_csv: function(fields, data) {
|
||||
var encoding = this.el_csv_encoding.val();
|
||||
var unparse_obj = {};
|
||||
unparse_obj.data = data;
|
||||
if (this.el_add_field_names.is(':checked')) {
|
||||
unparse_obj.fields = fields;
|
||||
}
|
||||
var csv = Papa.unparse(unparse_obj, {
|
||||
// TODO quoteChar: this.el_csv_quotechar.val(),
|
||||
delimiter: this.el_csv_delimiter.val()
|
||||
});
|
||||
var blob = new Blob([csv], {type: 'text/csv;charset=' + encoding});
|
||||
var blob_url = window.URL.createObjectURL(blob);
|
||||
if (this.blob_url) {
|
||||
window.URL.revokeObjectURL(this.blob_url);
|
||||
}
|
||||
this.blob_url = blob_url;
|
||||
window.open(blob_url);
|
||||
|
||||
Sao.common.message.run(
|
||||
Sao.i18n.ngettext('%1 record saved', '%1 records saved',
|
||||
data.length));
|
||||
}
|
||||
});
|
||||
|
||||
}());
|
||||
|
||||
Reference in New Issue
Block a user