From 0a8fffc37bd411bee3b60fa66b00de8766c02fc6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Todorovich?= Date: Mon, 22 Dec 2025 16:40:06 -0300 Subject: [PATCH] [IMP] connector_importer: use core field converter for dynamic mapper The core field converter is able to do smarter things, like matching selection fields with both their technical value or their label. This makes it easy to support CSV files generated by standard Odoo export, which will use the selection field label. --- .../components/dynamicmapper.py | 6 +++--- connector_importer/tests/test_mapper.py | 21 +++++++++++++++++++ connector_importer/utils/mapper_utils.py | 16 +++++++++----- 3 files changed, 35 insertions(+), 8 deletions(-) diff --git a/connector_importer/components/dynamicmapper.py b/connector_importer/components/dynamicmapper.py index 72816bf3..65905e0c 100644 --- a/connector_importer/components/dynamicmapper.py +++ b/connector_importer/components/dynamicmapper.py @@ -188,9 +188,9 @@ def _is_xmlid_key(self, fname, ftype): def _dynamic_keys_mapping(self, fname, **options): return { - "char": lambda self, rec, fname: rec[fname], - "text": lambda self, rec, fname: rec[fname], - "selection": lambda self, rec, fname: rec[fname], + "char": convert(fname, **options), + "text": convert(fname, **options), + "selection": convert(fname, **options), "integer": convert(fname, "safe_int", **options), "float": convert(fname, "safe_float", **options), "boolean": convert(fname, "bool", **options), diff --git a/connector_importer/tests/test_mapper.py b/connector_importer/tests/test_mapper.py index fb29edb5..e1e3f25c 100644 --- a/connector_importer/tests/test_mapper.py +++ b/connector_importer/tests/test_mapper.py @@ -216,3 +216,24 @@ def test_dynamic_mapper_rename_keys(self): options=dict(source_key_rename={"another_name": "name"}) ) self.assertEqual(mapper.dynamic_fields(rec), expected) + + def test_dynamic_mapper_convert_selection_with_value(self): + """Test that the importer converts a selection field with a tech value.""" + rec = {"type": "invoice"} + expected = {"type": "invoice"} + mapper = self._get_dynamyc_mapper() + self.assertEqual(mapper.dynamic_fields(rec), expected) + + def test_dynamic_mapper_convert_selection_with_label(self): + """Test that the importer converts a selection field with a label value.""" + rec = {"type": "Invoice Address"} + expected = {"type": "invoice"} + mapper = self._get_dynamyc_mapper() + self.assertEqual(mapper.dynamic_fields(rec), expected) + + def test_dynamic_mapper_convert_selection_with_wrong_value(self): + """Test that the importer converts a selection field with a wrong value.""" + rec = {"type": "wrong"} + mapper = self._get_dynamyc_mapper() + with self.assertRaises(TypeError): + mapper.dynamic_fields(rec) diff --git a/connector_importer/utils/mapper_utils.py b/connector_importer/utils/mapper_utils.py index 5ee86c04..a1eb9255 100644 --- a/connector_importer/utils/mapper_utils.py +++ b/connector_importer/utils/mapper_utils.py @@ -95,10 +95,11 @@ def to_safe_int(value): } -def convert(field, conv_type, fallback_field=None, pre_value_handler=None, **kw): - """Convert the source field to a defined ``conv_type`` - (ex. str) before returning it. - You can also use predefined converters like 'date'. +def convert(field, conv_type=None, fallback_field=None, pre_value_handler=None, **kw): + """Convert the source field to the target format. + + Use ``conv_type`` to provide a predefined converter like 'date' or 'safe_float', + or a custom converter function. Use ``fallback_field`` to provide a field of the same type to be used in case the base field has no value. """ @@ -119,7 +120,12 @@ def modifier(self, record, to_attr): # do not use `if not value` otherwise you override all zero values if value is None: return None - return conv_type(value, **kw) + # If a specific converter is provided, use it. + if conv_type: + return conv_type(value, **kw) + # Otherwise, fallback to the core Odoo's ir.fields.converter + conv = self.env["ir.fields.converter"].for_model(self.model) + return conv({field: value}, log=None).get(field) modifier._from_key = field return modifier