diff --git a/base_import_odoo/__openerp__.py b/base_import_odoo/__openerp__.py index baaba59e5..cfc169e3c 100644 --- a/base_import_odoo/__openerp__.py +++ b/base_import_odoo/__openerp__.py @@ -18,6 +18,7 @@ "demo/import_odoo_database_model.xml", ], "data": [ + "views/import_odoo_database_field.xml", "security/ir.model.access.csv", "views/import_odoo_database.xml", "views/menu.xml", diff --git a/base_import_odoo/demo/import_odoo_database_field.xml b/base_import_odoo/demo/import_odoo_database_field.xml index e47c6d070..af9e3d137 100644 --- a/base_import_odoo/demo/import_odoo_database_field.xml +++ b/base_import_odoo/demo/import_odoo_database_field.xml @@ -1,23 +1,59 @@ - + + fixed + + + + + + + fixed + + + + + + + fixed + + + + + + + fixed + + + fixed + + + + + fixed + unique - - + + + + + by_field + + diff --git a/base_import_odoo/demo/import_odoo_database_model.xml b/base_import_odoo/demo/import_odoo_database_model.xml index c9c856766..a10fb3bee 100644 --- a/base_import_odoo/demo/import_odoo_database_model.xml +++ b/base_import_odoo/demo/import_odoo_database_model.xml @@ -13,5 +13,11 @@ [(1, '=', 1)] + + 3 + + + [(1, '=', 1)] + diff --git a/base_import_odoo/models/import_odoo_database.py b/base_import_odoo/models/import_odoo_database.py index 8a15b73f4..8d462bd24 100644 --- a/base_import_odoo/models/import_odoo_database.py +++ b/base_import_odoo/models/import_odoo_database.py @@ -220,25 +220,9 @@ class ImportOdooDatabase(models.Model): dummy_instance(*(context.field_context + (_id,))) ) if not _id: - mapping = self.import_field_mappings.filtered( - lambda x: x.model_id.model == model._name and - ( - not x.fields_id or - x.fields_id.name == context.field_context.field_name and - x.fields_id.model_id.model == - context.field_context.record_model - ) and - x.local_id and - (x.remote_id == record['id'] or not x.remote_id) - )[:1] - if mapping: - if mapping.local_id: - _id = mapping.local_id - context.idmap[(model._name, record['id'])] = _id - else: - _id = self._run_import_create_dummy( - context, model, record, forcecreate=True, - ) + _id = self._run_import_get_record_mapping( + context, model, record, create_dummy=create_dummy, + ) if not _id: xmlid = self.env['ir.model.data'].search([ ('import_database_id', '=', self.id), @@ -252,6 +236,48 @@ class ImportOdooDatabase(models.Model): _id = self._run_import_create_dummy(context, model, record) return _id + @api.multi + def _run_import_get_record_mapping( + self, context, model, record, create_dummy=True, + ): + current_field = self.env['ir.model.fields'].search([ + ('name', '=', context.field_context.field_name), + ('model_id.model', '=', context.field_context.record_model), + ]) + mappings = self.import_field_mappings.filtered( + lambda x: + x.mapping_type == 'fixed' and + x.model_id.model == model._name and + ( + not x.field_ids or current_field in x.field_ids + ) and x.local_id and + (x.remote_id == record['id'] or not x.remote_id) or + x.mapping_type == 'by_field' and + x.model_id.model == model._name + ) + _id = None + for mapping in mappings: + if mapping.mapping_type == 'fixed': + assert mapping.local_id + _id = mapping.local_id + context.idmap[(model._name, record['id'])] = _id + break + elif mapping.mapping_type == 'by_field': + assert mapping.field_ids + if len(record) == 1: + continue + records = model.search([ + (field.name, '=', record[field.name]) + for field in mapping.field_ids + ], limit=1) + if records: + _id = records.id + context.idmap[(model._name, record['id'])] = _id + break + else: + raise exceptions.UserError(_('Unknown mapping')) + return _id + @api.multi def _run_import_create_dummy( self, context, model, record, forcecreate=False, @@ -265,6 +291,12 @@ class ImportOdooDatabase(models.Model): v for (model_name, remote_id), v in context.dummies.iteritems() if model_name == model._name + ] + + [ + mapping.local_id for mapping + in self.import_field_mappings + if mapping.model_id.model == model._name and + mapping.local_id ] ), ], limit=1) @@ -336,12 +368,15 @@ class ImportOdooDatabase(models.Model): new_context = context.with_field_context( model._name, field_name, data['id'] ) + comodel = self.env[model._fields[field_name].comodel_name] data[field_name] = [ self._run_import_get_record( - new_context, - self.env[model._fields[field_name].comodel_name], - {'id': _id}, - create_dummy=model._fields[field_name].required, + new_context, comodel, {'id': _id}, + create_dummy=model._fields[field_name].required or + any( + m.model_id._name == comodel._name + for m in self.import_line_ids + ), ) for _id in ids ] @@ -354,18 +389,16 @@ class ImportOdooDatabase(models.Model): else: data[field_name] = [(6, 0, data[field_name])] for mapping in self.import_field_mappings: - if mapping.model_id.model != model._name or not mapping.fields_id: + if mapping.model_id.model != model._name or\ + mapping.mapping_type != 'unique': continue - if mapping.unique: - value = data.get(mapping.fields_id.name, '') + for field in mapping.field_ids: + value = data.get(field.name, '') counter = 1 while model.with_context(active_test=False).search([ - ( - mapping.fields_id.name, '=', - data.get(mapping.fields_id.name, value) - ), + (field.name, '=', data.get(field.name, value)), ]): - data[mapping.fields_id.name] = '%s (%d)' % (value, counter) + data[field.name] = '%s (%d)' % (value, counter) counter += 1 return data diff --git a/base_import_odoo/models/import_odoo_database_field.py b/base_import_odoo/models/import_odoo_database_field.py index ff823a9b1..95d9b1959 100644 --- a/base_import_odoo/models/import_odoo_database_field.py +++ b/base_import_odoo/models/import_odoo_database_field.py @@ -7,7 +7,9 @@ from openerp import fields, models class ImportOdooDatabaseField(models.Model): _name = 'import.odoo.database.field' _description = 'A field mapping for records in the remote database' + _order = 'database_id, sequence' + sequence = fields.Integer() database_id = fields.Many2one( 'import.odoo.database', string='Database', required=True, ondelete='cascade', @@ -15,14 +17,11 @@ class ImportOdooDatabaseField(models.Model): model_id = fields.Many2one( 'ir.model', string='Model', required=True, ondelete='cascade', ) - fields_id = fields.Many2one( + model = fields.Char(related=['model_id', 'model']) + field_ids = fields.Many2many( 'ir.model.fields', string='Field', help='If set, the mapping is only ' 'effective when setting said field', ondelete='cascade', ) - unique = fields.Boolean( - 'Unique', help='If set on a char field, a number is appended until ' - 'the value is unique. Set this for fields with uniqueness constraints', - ) # TODO: create a reference function field to set this conveniently local_id = fields.Integer( 'Local ID', help='If you leave this empty, a new record will be ' @@ -33,3 +32,11 @@ class ImportOdooDatabaseField(models.Model): 'Remote ID', help='If you leave this empty, every (set) field value ' 'will be mapped to the local ID' ) + mapping_type = fields.Selection( + [ + ('fixed', 'Fixed'), + ('by_field', 'Based on equal fields'), + ('unique', 'Unique'), + ], + string='Type', required=True, default='fixed', + ) diff --git a/base_import_odoo/tests/test_base_import_odoo.py b/base_import_odoo/tests/test_base_import_odoo.py index f3201ecdc..3a9f3a186 100644 --- a/base_import_odoo/tests/test_base_import_odoo.py +++ b/base_import_odoo/tests/test_base_import_odoo.py @@ -31,6 +31,9 @@ class TestBaseImportOdoo(TransactionCase): **(context or self.env.context) ).browse(domain_or_ids).read(fields=fields) + group_count = self.env['res.groups'].search([], count=True) + user_count = self.env['res.users'].search([], count=True) + run = 1 for dummy in range(2): # we run this two times to enter the code path where xmlids exist self.env.ref('base_import_odoo.demodb').write({ @@ -46,11 +49,32 @@ class TestBaseImportOdoo(TransactionCase): self.env.ref(self._get_xmlid('base.user_demo')), self.env.ref('base.user_demo'), ) + # check that the imported scalars are equal + fields = ['name', 'email', 'signature', 'active'] + ( + self.env.ref(self._get_xmlid('base.user_demo')) + + self.env.ref('base.user_demo') + ).read(fields) self.assertEqual( - dict(self.env.ref(self._get_xmlid('base.user_demo'))._cache), - dict(self.env.ref('base.user_demo')._cache), + self._get_cache(self._get_xmlid('base.user_demo'), fields), + self._get_cache('base.user_demo', fields), + ) + # check that links are correctly mapped + self.assertEqual( + self.env.ref(self._get_xmlid('base.user_demo')).partner_id, + self.env.ref(self._get_xmlid('base.partner_demo')) + ) + # no new groups because they should be mapped by name + self.assertEqual( + group_count, self.env['res.groups'].search([], count=True) + ) + # all users save for root should be duplicated for every run + self.assertEqual( + self.env['res.users'].search([], count=True), + user_count + (user_count - 1) * run, ) # TODO: test much more + run += 1 demodb = self.env.ref('base_import_odoo.demodb') demodb.action_import() self.assertTrue(demodb.cronjob_id) @@ -66,3 +90,11 @@ class TestBaseImportOdoo(TransactionCase): remote_obj._name.replace('.', '_'), remote_obj.id, ) + + def _get_cache(self, xmlid, fields): + record = self.env.ref(xmlid) + return { + field_name: record._cache[field_name] + for field_name in record._fields + if field_name in fields + } diff --git a/base_import_odoo/views/import_odoo_database.xml b/base_import_odoo/views/import_odoo_database.xml index d5c67077b..8e293a722 100644 --- a/base_import_odoo/views/import_odoo_database.xml +++ b/base_import_odoo/views/import_odoo_database.xml @@ -35,15 +35,7 @@ - - - - - - - - - + diff --git a/base_import_odoo/views/import_odoo_database_field.xml b/base_import_odoo/views/import_odoo_database_field.xml new file mode 100644 index 000000000..061b63e50 --- /dev/null +++ b/base_import_odoo/views/import_odoo_database_field.xml @@ -0,0 +1,42 @@ + + + + + import.odoo.database.field + + + + + + + + + When a record of this model is imported, it will be replaced with the record you select as local ID. If you select a field and/or a remote ID, this replacement is only effective when setting the specified field and/or when the remote value is the specified record. + + + Select fields which must be equal to treat a pair of remote and local records of this model as being equal. + + + Select fields for which to generate unique values during import. You'll need this for res.users#login for example. + + + + + + + + + + + import.odoo.database.field + + + + + + + + + + +