From 8fbadb9575c25e20f08637c837b386fde213203c Mon Sep 17 00:00:00 2001 From: Mohammed Shekha Date: Thu, 16 Aug 2018 14:40:35 +0530 Subject: [PATCH] [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 --- addons/base_import/models/base_import.py | 3 +- .../static/src/js/import_action.js | 37 +++++++++++++++++-- .../static/src/scss/base_import.scss | 1 + .../static/src/xml/base_import.xml | 9 +++++ odoo/addons/base/models/ir_fields.py | 16 +++++++- odoo/addons/test_impex/models.py | 2 +- odoo/addons/test_impex/tests/test_load.py | 14 +++++++ odoo/fields.py | 4 +- 8 files changed, 78 insertions(+), 8 deletions(-) diff --git a/addons/base_import/models/base_import.py b/addons/base_import/models/base_import.py index f1384bfb5a2..1d04c98b9ce 100644 --- a/addons/base_import/models/base_import.py +++ b/addons/base_import/models/base_import.py @@ -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') diff --git a/addons/base_import/static/src/js/import_action.js b/addons/base_import/static/src/js/import_action.js index e2eff44c174..8349603fc9f 100644 --- a/addons/base_import/static/src/js/import_action.js +++ b/addons/base_import/static/src/js/import_action.js @@ -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; diff --git a/addons/base_import/static/src/scss/base_import.scss b/addons/base_import/static/src/scss/base_import.scss index f3686e7b448..d32cffdfb32 100644 --- a/addons/base_import/static/src/scss/base_import.scss +++ b/addons/base_import/static/src/scss/base_import.scss @@ -106,6 +106,7 @@ } td { padding: 4px; + vertical-align: top; } } } diff --git a/addons/base_import/static/src/xml/base_import.xml b/addons/base_import/static/src/xml/base_import.xml index fb29ac0c9ea..3b8d8136da9 100644 --- a/addons/base_import/static/src/xml/base_import.xml +++ b/addons/base_import/static/src/xml/base_import.xml @@ -100,6 +100,15 @@ + +
+ +
+
+ diff --git a/odoo/addons/base/models/ir_fields.py b/odoo/addons/base/models/ir_fields.py index 9a862f02eee..233c611478b 100644 --- a/odoo/addons/base/models/ir_fields.py +++ b/odoo/addons/base/models/ir_fields.py @@ -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 diff --git a/odoo/addons/test_impex/models.py b/odoo/addons/test_impex/models.py index ddecc63b265..5a098936ccc 100644 --- a/odoo/addons/test_impex/models.py +++ b/odoo/addons/test_impex/models.py @@ -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 diff --git a/odoo/addons/test_impex/tests/test_load.py b/odoo/addons/test_impex/tests/test_load.py index 817a56a1c9f..ff303eea407 100644 --- a/odoo/addons/test_impex/tests/test_load.py +++ b/odoo/addons/test_impex/tests/test_load.py @@ -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' diff --git a/odoo/fields.py b/odoo/fields.py index ec3a16b0a71..213c5696063 100644 --- a/odoo/fields.py +++ b/odoo/fields.py @@ -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])