From 574d7f0d4ca18ae291ce0c0beeb51934bef0d41e Mon Sep 17 00:00:00 2001 From: Ravi Singh Date: Wed, 25 Sep 2019 09:25:06 +0000 Subject: [PATCH] [IMP] core: import-compatible export of m2m fields When performing an import-compatible export, m2m values would be exported as a record per cell unless the `id` subfield was the first to be exported. Which is not the case when using the export UI (as it always adds the field itself before any subfield). This would make the m2m not actually export-compatible in most cases. * export m2m "display name" in an import compatible format as well * re-prioritise exporting xids when there are multiple m2m exports in import-compatible mode * never fall back on the o2m / non-import-compatible m2m path for m2ms in import-compatible mode Note: if multiple m2m fields are specified only one of them gets filled. Task 2065428 closes odoo/odoo#37407 Signed-off-by: Xavier Morel (xmo) Co-authored-by: Ravi Singh Co-authored-by: Mohammed Shekha Co-authored-by: Xavier Morel --- odoo/addons/test_impex/tests/test_export.py | 18 +++++++++-- odoo/models.py | 34 ++++++++++++++++----- 2 files changed, 41 insertions(+), 11 deletions(-) diff --git a/odoo/addons/test_impex/tests/test_export.py b/odoo/addons/test_impex/tests/test_export.py index 3d76ec925e6..01d322e0d15 100644 --- a/odoo/addons/test_impex/tests/test_export.py +++ b/odoo/addons/test_impex/tests/test_export.py @@ -606,18 +606,20 @@ class test_m2m(CreatorCase): def test_single_subfield(self): self.assertEqual( self.export([(0, False, {'value': 42})], - fields=['value', 'value/value']), + fields=['value', 'value/value'], + context={'import_compat': False}), [[u'export.many2many.other:42', 42]]) def test_integrate_one_in_parent(self): self.assertEqual( self.export([(0, False, {'value': 42})], - fields=['const', 'value/value']), + fields=['const', 'value/value'], + context={'import_compat': False}), [[4, 42]]) def test_multiple_records(self): self.assertEqual( - self.export(self.commands, fields=['const', 'value/value']), + self.export(self.commands, fields=['const', 'value/value'], context={'import_compat': False}), [ [4, 4], [u'', 42], @@ -629,6 +631,12 @@ class test_m2m(CreatorCase): def test_multiple_records_name(self): self.assertEqual( self.export(self.commands, fields=['const', 'value']), + [ + [4, 'export.many2many.other:4,export.many2many.other:42,export.many2many.other:36,export.many2many.other:4,export.many2many.other:13'], + ]) + + self.assertEqual( + self.export(self.commands, fields=['const', 'value'], context={'import_compat': False}), [ [4, u'export.many2many.other:4'], ['', u'export.many2many.other:42'], @@ -664,6 +672,10 @@ class test_m2m(CreatorCase): r.with_context(import_compat=True)._export_rows([['value', 'id']]), [['__t__.record000,__t__.record001,__t__.record010,__t__.record011,__t__.record100']] ) + self.assertEqual( + r.with_context(import_compat=True)._export_rows([['value'], ['value', 'id']]), + [['', '__t__.record000,__t__.record001,__t__.record010,__t__.record011,__t__.record100']] + ) self.assertEqual( r.with_context(import_compat=False)._export_rows([['id'], ['value', 'id'], ['value', 'value']]), diff --git a/odoo/models.py b/odoo/models.py index f1c6b890935..33c966bb1be 100644 --- a/odoo/models.py +++ b/odoo/models.py @@ -22,6 +22,7 @@ """ import collections +import contextlib import datetime import dateutil import fnmatch @@ -838,18 +839,35 @@ class BaseModel(MetaModel('DummyModel', (object,), {'_register': False})): current[i] = field.convert_to_export(value, record) else: primary_done.append(name) - - # in import_compat mode, m2m should always be exported as - # a comma-separated list of xids in a single cell - if import_compatible and field.type == 'many2many' and len(path) > 1 and path[1] == 'id': - xml_ids = [xid for _, xid in value.__ensure_xml_id()] - current[i] = ','.join(xml_ids) or False - continue - # recursively export the fields that follow name; use # 'display_name' where no subfield is exported fields2 = [(p[1:] or ['display_name'] if p and p[0] == name else []) for p in fields] + + # in import_compat mode, m2m should always be exported as + # a comma-separated list of xids or names in a single cell + if import_compatible and field.type == 'many2many': + index = None + # find out which subfield the user wants & its + # location as we might not get it as the first + # column we encounter + for name in ['id', 'name', 'display_name']: + with contextlib.suppress(ValueError): + index = fields2.index([name]) + break + if index is None: + # not found anything, assume we just want the + # name_get in the first column + name = None + index = i + + if name == 'id': + xml_ids = [xid for _, xid in value.__ensure_xml_id()] + current[index] = ','.join(xml_ids) or False + else: + current[index] = field.convert_to_export(value, record) or False + continue + lines2 = value._export_rows(fields2, _is_toplevel_call=False) if lines2: # merge first line with record's main line