[ADD] base_import: debug option to create M2O/M2M records

Adds a checkbox to import columns (in debug mode) allowing a user to
create records M2O and M2M records not found (via name_search).

Task ID: 1850633

* uses a context key to avoid altering basically all the import callstack
* attempted to lift the creation in the `_str_to_*` functions and create
  m2m via commands, but that doesn't really work out
This commit is contained in:
Mohammed Shekha
2018-08-16 11:10:35 +02:00
committed by xmo-odoo
parent b3824aa6cb
commit 8fbadb9575
8 changed files with 78 additions and 8 deletions
+2 -1
View File
@@ -888,7 +888,8 @@ class Import(models.TransientModel):
_logger.info('importing %d rows...', len(data))
model = self.env[self.res_model].with_context(import_file=True)
name_create_enabled_fields = options.pop('name_create_enabled_fields', {})
model = self.env[self.res_model].with_context(import_file=True, name_create_enabled_fields=name_create_enabled_fields)
import_result = model.load(import_fields, data)
_logger.info('done')
@@ -280,7 +280,8 @@ var DataImport = AbstractAction.extend(ControlPanelMixin, {
var options = {
headers: this.$('input.oe_import_has_header').prop('checked'),
advanced: this.$('input.oe_import_advanced_mode').prop('checked'),
keep_matches: this.do_not_change_match
keep_matches: this.do_not_change_match,
name_create_enabled_fields: {},
};
_(this.opts).each(function (opt) {
options[opt.name] =
@@ -301,6 +302,12 @@ var DataImport = AbstractAction.extend(ControlPanelMixin, {
}).get();
}
this.do_not_change_match = false;
this.$('input.o_import_create_option').each(function () {
var field = this.getAttribute('field');
if (field) {
options.name_create_enabled_fields[field] = this.checked;
}
});
return options;
},
@@ -402,6 +409,24 @@ var DataImport = AbstractAction.extend(ControlPanelMixin, {
};
$fields.each(function (k,v) {
var filtered_data = self.generate_fields_completion(result, k);
var $thing = $();
var bind = function (d) {};
if (session.debug) {
$thing = $(QWeb.render('ImportView.create_record_option')).insertAfter(v).hide();
bind = function (data) {
switch (data.type) {
case 'many2one': case 'many2many':
$thing.find('input').attr('field', data.id);
$thing.show();
break;
default:
$thing.find('input').attr('field', '').prop('checked', false);
$thing.hide();
}
}
}
$(v).select2({
allowClear: true,
minimumInputLength: 0,
@@ -413,11 +438,15 @@ var DataImport = AbstractAction.extend(ControlPanelMixin, {
return;
}
callback(item_finder(default_value));
var data = item_finder(default_value);
bind(data);
callback(data);
},
placeholder: _t('Don\'t import'),
width: 'resolve',
dropdownCssClass: 'oe_import_selector'
}).on('change', function (e) {
bind(item_finder(e.currentTarget.value));
});
});
},
@@ -448,7 +477,8 @@ var DataImport = AbstractAction.extend(ControlPanelMixin, {
collection.push({
id: id,
text: label,
required: field.required
required: field.required,
type: field.type
});
}
@@ -521,6 +551,7 @@ var DataImport = AbstractAction.extend(ControlPanelMixin, {
var columns = this.$('.oe_import_grid-header .oe_import_grid-cell .o_import_header_name').map(function () {
return $(this).text().trim().toLowerCase() || false;
}).get();
var tracking_disable = 'tracking_disable' in kwargs ? kwargs.tracking_disable : !this.$('#oe_import_tracking').prop('checked')
var defer_parent_store = 'defer_parent_store' in kwargs ? kwargs.defer_parent_store : !!this.$('#oe_import_deferparentstore').prop('checked')
delete kwargs.tracking_disable;
+1
View File
@@ -106,6 +106,7 @@
}
td {
padding: 4px;
vertical-align: top;
}
}
}
@@ -100,6 +100,15 @@
<button type="button" class="btn btn-secondary o_import_cancel">Cancel</button>
</t>
<t t-name="ImportView.create_record_option">
<div class="mt4">
<label title="Creates new records if they can't be found (instead of failing to import). Note that the value in the column will be used as the new record's 'name', and assumes this is sufficient to create the record.">
<input type="checkbox"/>
Create if doesn't exist
</label>
</div>
</t>
<t t-name="ImportView.preview">
<thead>
<tr t-if="headers" class="oe_import_grid-header">
+14 -2
View File
@@ -303,6 +303,7 @@ class IrFieldsConverter(models.AbstractModel):
id = None
warnings = []
error_msg = ''
action = {'type': 'ir.actions.act_window', 'target': 'new',
'view_mode': 'tree,form', 'view_type': 'form',
'views': [(False, 'tree'), (False, 'form')],
@@ -346,6 +347,13 @@ class IrFieldsConverter(models.AbstractModel):
_(u"Found multiple matches for field '%%(field)s' (%d matches)")
% (len(ids))))
id, _name = ids[0]
else:
name_create_enabled_fields = self.env.context.get('name_create_enabled_fields') or {}
if name_create_enabled_fields.get(field.name):
try:
id, _name = RelatedModel.name_create(name=value)
except Exception as e:
error_msg = repr(e)
else:
raise self._format_import_error(
Exception,
@@ -354,10 +362,14 @@ class IrFieldsConverter(models.AbstractModel):
)
if id is None:
if error_msg:
message = _("No matching record found for %(field_type)s '%(value)s' in field '%%(field)s' and the following error was encountered when we attempted to create one: %(error_message)s")
else:
message = _("No matching record found for %(field_type)s '%(value)s' in field '%%(field)s'")
raise self._format_import_error(
ValueError,
_(u"No matching record found for %(field_type)s '%(value)s' in field '%%(field)s'"),
{'field_type': field_type, 'value': value},
message,
{'field_type': field_type, 'value': value, 'error_message': error_msg},
{'moreinfo': action})
return id, field_type, warnings
+1 -1
View File
@@ -40,6 +40,7 @@ MODELS = [
for name, field in MODELS:
class NewModel(models.Model):
_name = 'export.%s' % name
_rec_name = 'value'
const = fields.Integer(default=4)
value = field
@@ -55,7 +56,6 @@ for name, field in MODELS:
else:
return []
class One2ManyChild(models.Model):
_name = 'export.one2many.child'
# FIXME: orm.py:1161, fix to name_get on m2o field
+14
View File
@@ -675,6 +675,20 @@ class test_m2o(ImporterCase):
u"name, external id or database id")])
self.assertIs(result['ids'], False)
def test_name_create_enabled_m2o(self):
result = self.import_(['value'], [[101]])
self.assertEqual(result['messages'], [message(
u"No matching record found for name '101' "
u"in field 'Value'", moreinfo=moreaction(
res_model='export.integer'))])
self.assertIs(result['ids'], False)
context = {
'name_create_enabled_fields': {'value': True},
}
result = self.import_(['value'], [[101]], context=context)
self.assertFalse(result['messages'])
self.assertEqual(len(result['ids']), 1)
class test_m2m(ImporterCase):
model_name = 'export.many2many'
+3 -1
View File
@@ -2659,7 +2659,9 @@ class Many2many(_RelationalMulti):
elif act[0] in (5, 6):
if recs and recs[-1] == record:
flush()
links.difference_update(pair for pair in links if pair[0] == record.id)
# must reify the RHS as Python can update the subject on
# the fly leading to "RuntimeError: Set changed size during iteration"
links.difference_update([pair for pair in links if pair[0] == record.id])
if act[0] == 6:
links.update((record.id, id2) for id2 in act[2])