[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:
committed by
xmo-odoo
parent
b3824aa6cb
commit
8fbadb9575
@@ -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;
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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])
|
||||
|
||||
|
||||
Reference in New Issue
Block a user