summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYannick Tivisse <yti@odoo.com>2019-08-20 12:21:58 +0000
committerYannick Tivisse <yti@odoo.com>2019-08-20 12:23:08 +0000
commit58b3557d6df4628883ca1eb16da52ab82ee0ee6c (patch)
treecad7d0eaa6db5003c9ebb0b6f1730620b2b837b8
parente8544f2f0fdf4750da99fbada6d0f1a173164198 (diff)
[IMP] base_import: Improve import general UX (back2basics)
Purpose ======= Improve error management UX on import. TaskID: 2049992 closes odoo/odoo#35804 Signed-off-by: Yannick Tivisse (yti) <yti@odoo.com>
-rw-r--r--addons/base_import/static/csv/o2m_customers_contacts.csv18
-rw-r--r--addons/base_import/static/src/js/import_action.js52
-rw-r--r--addons/base_import/static/src/scss/base_import.scss8
-rw-r--r--addons/base_import/static/src/xml/base_import.xml55
-rw-r--r--odoo/addons/base/models/ir_fields.py15
-rw-r--r--odoo/addons/base/static/xls/res_partner.xlsbin7168 -> 7168 bytes
-rw-r--r--odoo/addons/base/tests/test_mimetypes.py6
-rw-r--r--odoo/addons/test_impex/tests/test_load.py4
-rw-r--r--odoo/models.py9
9 files changed, 123 insertions, 44 deletions
diff --git a/addons/base_import/static/csv/o2m_customers_contacts.csv b/addons/base_import/static/csv/o2m_customers_contacts.csv
index d29a600f18b..ac91202579d 100644
--- a/addons/base_import/static/csv/o2m_customers_contacts.csv
+++ b/addons/base_import/static/csv/o2m_customers_contacts.csv
@@ -1,9 +1,9 @@
-Name,Is a company,Related company,Address type,Customer,Supplier,Street,ZIP,City,State,Country
-Aurora Shelves,1,,,1,0,25 Pacific Road,95101,San José,California,United States
-Roger Martins,0,Aurora Shelves,Invoice address,1,0,27 Pacific Road,95102,San José,California,United States
-House Sales Direct,1,,,1,0,104 Saint Mary Avenue,94059,Redwood,California,United States
-Yvan Holiday,0,House Sales Direct,Contact,1,0,104 Saint Mary Avenue,94060,Redwood,California,United States
-Jack Unsworth,0,House Sales Direct,Invoice address,1,0,227 Jackson Road,94061,Redwood,California,United States
-Michael Mason,0,,,1,0,16 5th Avenue,94104,San Francisco,California,United States
-International Wood,1,,,1,0,748 White House Boulevard,20004,Washington,D.C.,United States
-Sharon Pecker,0,International Wood,Invoice address,1,0,755 White House Boulevard,20005,Washington,D.C.,United States
+Name,Is a company,Related company,Address type,Street,ZIP,City,State,Country
+Aurora Shelves,1,,,25 Pacific Road,95101,San José,California,United States
+Roger Martins,0,Aurora Shelves,Invoice address,27 Pacific Road,95102,San José,California,United States
+House Sales Direct,1,,,104 Saint Mary Avenue,94059,Redwood,California,United States
+Yvan Holiday,0,House Sales Direct,Contact,104 Saint Mary Avenue,94060,Redwood,California,United States
+Jack Unsworth,0,House Sales Direct,Invoice address,227 Jackson Road,94061,Redwood,California,United States
+Michael Mason,0,,,16 5th Avenue,94104,San Francisco,California,United States
+International Wood,1,,,748 White House Boulevard,20004,Washington,D.C.,United States
+Sharon Pecker,0,International Wood,Invoice address,755 White House Boulevard,20005,Washington,D.C.,United States \ No newline at end of file
diff --git a/addons/base_import/static/src/js/import_action.js b/addons/base_import/static/src/js/import_action.js
index bf41800a41b..a4a4db8d690 100644
--- a/addons/base_import/static/src/js/import_action.js
+++ b/addons/base_import/static/src/js/import_action.js
@@ -102,7 +102,7 @@ var DataImport = AbstractAction.extend({
},
'click .oe_import_report a.oe_import_report_count': function (e) {
e.preventDefault();
- $(e.target).parent().toggleClass('oe_import_report_showmore');
+ $(e.target).parent().parent().toggleClass('oe_import_report_showmore');
},
'click .oe_import_moreinfo_action a': function (e) {
e.preventDefault();
@@ -140,6 +140,7 @@ var DataImport = AbstractAction.extend({
this.session = session;
this._title = _t('Import a File'); // Displayed in the breadcrumbs
this.do_not_change_match = false;
+ this.preview = [[]];
},
/**
* @override
@@ -374,6 +375,8 @@ var DataImport = AbstractAction.extend({
this.$('input.oe_import_advanced_mode').prop('checked', result.advanced_mode);
this.$('.oe_import_grid').html(QWeb.render('ImportView.preview', result));
+ this.preview = result.preview;
+
if (result.headers.length === 1) {
this.$('.oe_import_options').show();
this.onresults(null, null, null, {'messages': [{
@@ -625,6 +628,12 @@ var DataImport = AbstractAction.extend({
this.trigger_up('history_back');
},
onresults: function (event, from, to, results) {
+ var self = this;
+ var fields = this.$('.oe_import_fields input.oe_import_match_field').map(function (index, el) {
+ return $(el).select2('val') || false;
+ }).get();
+ var nameIndex = _.indexOf(fields, 'name');
+
var message = results.messages;
var no_messages = _.isEmpty(message);
if (no_messages) {
@@ -639,19 +648,48 @@ var DataImport = AbstractAction.extend({
// offset more if header
if (this.import_options().headers) { offset += 1; }
+ var messagesSorted = _.sortBy(_(message).groupBy('message'), function (messageGroupped) {
+ var order = 0;
+ if (messageGroupped[0].type === 'warning') {
+ order = fields.length + 1;
+ }
+ return order + _.indexOf(fields, messageGroupped[0].field);
+ });
+
this.$form.addClass('oe_import_error');
this.$('.oe_import_error_report').html(
QWeb.render('ImportView.error', {
- errors: _(message).groupBy('message'),
+ errors: messagesSorted,
at: function (rows) {
var from = rows.from + offset;
var to = rows.to + offset;
+ var rowName = '';
+ if (nameIndex !== -1) {
+ rowName = _.str.sprintf(' (%s)', self.preview[rows.from][nameIndex]);
+ }
if (from === to) {
- return _.str.sprintf(_t("at row %d"), from);
+ return _.str.sprintf(_t("at row %d%s"), from, rowName);
}
return _.str.sprintf(_t("between rows %d and %d"),
from, to);
},
+ at_multi: function (rows) {
+ var from = rows.from + offset;
+ var to = rows.to + offset;
+ var rowName = '';
+ if (nameIndex !== -1) {
+ rowName = _.str.sprintf(' (%s)', self.preview[rows.from][nameIndex]);
+ }
+ if (from === to) {
+ return _.str.sprintf(_t("Row %d%s"), from, rowName);
+ }
+ return _.str.sprintf(_t("Between rows %d and %d"),
+ from, to);
+ },
+ at_multi_header: function (numberLines) {
+ return _.str.sprintf(_t("at %d different rows:"),
+ numberLines);
+ },
more: function (n) {
return _.str.sprintf(_t("(%d more)"), n);
},
@@ -663,8 +701,8 @@ var DataImport = AbstractAction.extend({
}
if (msg instanceof Array) {
return _.str.sprintf(
- '<div class="oe_import_moreinfo oe_import_moreinfo_choices">%s <ul>%s</ul></div>',
- _.str.escapeHTML(_t("Here are the possible values:")),
+ '<div class="oe_import_moreinfo oe_import_moreinfo_choices"><a href="#" class="oe_import_report_count oe_import_see_all"><i class="fa fa-arrow-right"/> %s </a><ul class="oe_import_report_more">%s</ul></div>',
+ _.str.escapeHTML(_t("See possible values")),
_(msg).map(function (msg) {
return '<li>'
+ _.str.escapeHTML(msg)
@@ -674,10 +712,10 @@ var DataImport = AbstractAction.extend({
// Final should be object, action descriptor
return [
'<div class="oe_import_moreinfo oe_import_moreinfo_action">',
- _.str.sprintf('<a href="#" data-action="%s">',
+ _.str.sprintf('<a href="#" data-action="%s" class="oe_import_see_all"><i class="fa fa-arrow-right"/> ',
_.str.escapeHTML(JSON.stringify(msg))),
_.str.escapeHTML(
- _t("Get all possible values")),
+ _t("See possible values")),
'</a>',
'</div>'
].join('');
diff --git a/addons/base_import/static/src/scss/base_import.scss b/addons/base_import/static/src/scss/base_import.scss
index d32cffdfb32..68d9603a813 100644
--- a/addons/base_import/static/src/scss/base_import.scss
+++ b/addons/base_import/static/src/scss/base_import.scss
@@ -75,7 +75,7 @@
&.oe_import_error .oe_import_error_report,
&.oe_import_noheaders .oe_import_noheaders,
.oe_import_report_showmore .oe_import_report_more {
- display: block;
+ display: list-item;
}
/* ------------- ERRORS AND WARNINGS REPORT ------------ */
@@ -95,6 +95,12 @@
@extend .text-danger;
}
}
+ .oe_import_report_count, .oe_import_see_all {
+ color: white;
+ &:hover {
+ color: lightgrey;
+ }
+ }
}
/* ------------- THE CSV TABLE ------------ */
diff --git a/addons/base_import/static/src/xml/base_import.xml b/addons/base_import/static/src/xml/base_import.xml
index c475a30036d..c8896a9da3a 100644
--- a/addons/base_import/static/src/xml/base_import.xml
+++ b/addons/base_import/static/src/xml/base_import.xml
@@ -18,6 +18,7 @@
<t t-esc="option.label"/></label>
<input t-attf-id="#{option.name}_#{_id}"
t-attf-class="oe_import_#{option.name}"
+ style="max-width: 50%;"
t-att-value="option.value"/>
</p>
</div>
@@ -28,6 +29,7 @@
<t t-esc="option.label"/></label>
<input t-attf-id="#{option.name}_#{_id}"
t-attf-class="oe_import_#{option.name}"
+ style="max-width: 50%;"
t-att-value="option.value"/>
</p>
</div>
@@ -140,23 +142,48 @@
<ul t-name="ImportView.error">
<li t-foreach="errors" t-as="error"
t-attf-class="oe_import_report bg-#{error_value[0].type}">
- <t t-call="ImportView.error.each">
- <t t-set="error" t-value="error_value[0]"/>
- </t>
-
- <a href="#" class="oe_import_report_count" t-if="error_value.length gt 1">
- <t t-esc="more(error_value.length - 1)"/>
- </a>
- <ul class="oe_import_report_more" t-if="error_value.length gt 1">
- <li t-foreach="error_value.length - 1" t-as="index">
- <t t-call="ImportView.error.each">
- <t t-set="error" t-value="error_value[index + 1]"/>
+ <t t-if="error_value.length gt 1">
+ <t t-call="ImportView.error.multi.header">
+ <t t-set="error" t-value="error_value[0]"/>
+ <t t-set="error_length" t-value="error_value.length"/>
+ </t>
+ <ul>
+ <t t-foreach="error_value.length" t-as="index">
+ <li t-att-class="index gt 4 ? 'oe_import_report_more':''">
+ <t t-call="ImportView.error.multi.body">
+ <t t-set="error" t-value="error_value[index]"/>
+ <t t-set="index" t-value="index"/>
+ </t>
+ </li>
+ <li t-if="error_value.length gt 5 and index == 4" style="display: block;">
+ <a href="#" class="oe_import_report_count">
+ <t t-esc="more(error_value.length - 5)"/>
+ </a>
+ </li>
</t>
- </li>
- </ul>
+ </ul>
+ <t t-if="error_value[0].moreinfo" t-raw="info(error_value[0].moreinfo)"/>
+
+ </t>
+ <t t-else="">
+ <t t-call="ImportView.error.single">
+ <t t-set="error" t-value="error_value[0]"/>
+ </t>
+ </t>
</li>
</ul>
- <t t-name="ImportView.error.each">
+ <t t-name="ImportView.error.multi.header">
+ <span class="oe_import_report_message">
+ <t t-esc="error.message"/>
+ <t t-esc="at_multi_header(error_length)"/>
+ </span>
+ </t>
+ <t t-name="ImportView.error.multi.body">
+ <span class="oe_import_report_message">
+ <t t-esc="at_multi(error.rows)"/>
+ </span>
+ </t>
+ <t t-name="ImportView.error.single">
<span class="oe_import_report_message">
<t t-esc="error.message"/>
</span>
diff --git a/odoo/addons/base/models/ir_fields.py b/odoo/addons/base/models/ir_fields.py
index 59fabcca860..a1c4c79b0d9 100644
--- a/odoo/addons/base/models/ir_fields.py
+++ b/odoo/addons/base/models/ir_fields.py
@@ -305,10 +305,13 @@ class IrFieldsConverter(models.AbstractModel):
id = None
warnings = []
error_msg = ''
- action = {'type': 'ir.actions.act_window', 'target': 'new',
- 'view_mode': 'tree,form',
- 'views': [(False, 'tree'), (False, 'form')],
- 'help': _(u"See all possible values")}
+ action = {
+ 'name': 'Possible Values',
+ 'type': 'ir.actions.act_window', 'target': 'new',
+ 'view_mode': 'tree,form',
+ 'views': [(False, 'list'), (False, 'form')],
+ 'context': {'create': False},
+ 'help': _(u"See all possible values")}
if subfield is None:
action['res_model'] = field.comodel_name
elif subfield in ('id', '.id'):
@@ -353,8 +356,8 @@ class IrFieldsConverter(models.AbstractModel):
if name_create_enabled_fields.get(field.name):
try:
id, _name = RelatedModel.name_create(name=value)
- except Exception as e:
- error_msg = repr(e)
+ except (Exception, psycopg2.IntegrityError):
+ error_msg = _(u"Cannot create new '%s' records from their name alone. Please create those records manually and try importing again.") % RelatedModel._description
else:
raise self._format_import_error(
Exception,
diff --git a/odoo/addons/base/static/xls/res_partner.xls b/odoo/addons/base/static/xls/res_partner.xls
index dbdec5bf229..9f7321c28b2 100644
--- a/odoo/addons/base/static/xls/res_partner.xls
+++ b/odoo/addons/base/static/xls/res_partner.xls
Binary files differ
diff --git a/odoo/addons/base/tests/test_mimetypes.py b/odoo/addons/base/tests/test_mimetypes.py
index e379c5474f2..b5fa3e1ebf8 100644
--- a/odoo/addons/base/tests/test_mimetypes.py
+++ b/odoo/addons/base/tests/test_mimetypes.py
@@ -16,11 +16,7 @@ AAAAAAAAAAAAA/9oACAEBAAEFAn//xAAUEQEAAAAAAAAAAAAAAAAAAAAA/9oACAEDAQE/AX//xAAUEQE
AA/9oACAECAQE/AX//xAAUEAEAAAAAAAAAAAAAAAAAAAAA/9oACAEBAAY/An//xAAUEAEAAAAAAAAAAAAAAAAAAAAA/9oACAEBA
AE/IX//2gAMAwEAAgADAAAAEB//xAAUEQEAAAAAAAAAAAAAAAAAAAAA/9oACAEDAQE/EH//xAAUEQEAAAAAAAAAAAAAAAAAAAAA
/9oACAECAQE/EH//xAAUEAEAAAAAAAAAAAAAAAAAAAAA/9oACAEBAAE/EH//2Q=="""
-SVG = b"""PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iaXNvLTg4NTktMSI/Pgo8IURPQ1RZUEUgc3ZnIFBVQkxJQyAiL
-S8vVzNDLy9EVEQgU1ZHIDIwMDAxMTAyLy9FTiIKICJodHRwOi8vd3d3LnczLm9yZy9UUi8yMDAwL0NSLVNWRy0yMDAwMTEwMi9E
-VEQvc3ZnLTIwMDAxMTAyLmR0ZCI+Cgo8c3ZnIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiPgogIDxnIHRyYW5zZm9ybT0idHJ
-hbnNsYXRlKDUwLDUwKSI+CiAgICA8cmVjdCB4PSIwIiB5PSIwIiB3aWR0aD0iMTUwIiBoZWlnaHQ9IjUwIiBzdHlsZT0iZmlsbD
-pyZWQ7IiAvPgogIDwvZz4KCjwvc3ZnPgo="""
+SVG = b"""PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iaXNvLTg4NTktMSI/PjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMjAwMDExMDIvL0VOIlxuICJodHRwOi8vd3d3LnczLm9yZy9UUi8yMDAwL0NSLVNWRy0yMDAwMTEwMi9EVEQvc3ZnLTIwMDAxMTAyLmR0ZCI+PHN2ZyB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIj48ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSg1MCw1MCkiPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxNTAiIGhlaWdodD0iNTAiIHN0eWxlPSJmaWxsOnJlZDsiIC8+PC9nPjwvc3ZnPg=="""
class test_guess_mimetype(BaseCase):
diff --git a/odoo/addons/test_impex/tests/test_load.py b/odoo/addons/test_impex/tests/test_load.py
index 3b53ead196f..886b702baab 100644
--- a/odoo/addons/test_impex/tests/test_load.py
+++ b/odoo/addons/test_impex/tests/test_load.py
@@ -18,8 +18,10 @@ def moreaction(**kwargs):
return dict(kwargs,
type='ir.actions.act_window',
target='new',
+ context={'create': False},
+ name='Possible Values',
view_mode='tree,form',
- views=[(False, 'tree'), (False, 'form')],
+ views=[(False, 'list'), (False, 'form')],
help=u"See all possible values")
def values(seq, field='value'):
diff --git a/odoo/models.py b/odoo/models.py
index 51656e0e9ec..8b611768141 100644
--- a/odoo/models.py
+++ b/odoo/models.py
@@ -5916,7 +5916,7 @@ def itemgetter_tuple(items):
def convert_pgerror_not_null(model, fields, info, e):
if e.diag.table_name != model._table:
- return {'message': tools.ustr(e)}
+ return {'message': _(u"Missing required value for the field '%s'") % (e.diag.column_name)}
field_name = e.diag.column_name
field = fields[field_name]
@@ -5964,11 +5964,18 @@ def convert_pgerror_unique(model, fields, info, e):
# no field, unclear which one we should pick and they could be in any order
}
+def convert_pgerror_constraint(model, fields, info, e):
+ sql_constraints = dict([(('%s_%s') % (e.diag.table_name, x[0]), x) for x in model._sql_constraints])
+ if e.diag.constraint_name in sql_constraints.keys():
+ return {'message': sql_constraints[e.diag.constraint_name][2]}
+ return {'message': tools.ustr(e)}
+
PGERROR_TO_OE = defaultdict(
# shape of mapped converters
lambda: (lambda model, fvg, info, pgerror: {'message': tools.ustr(pgerror)}), {
'23502': convert_pgerror_not_null,
'23505': convert_pgerror_unique,
+ '23514': convert_pgerror_constraint,
})