Skip to content

Commit ce6b083

Browse files
bosdbosd
authored andcommitted
[FIX] m2m mapper
1 parent e4401cb commit ce6b083

File tree

2 files changed

+47
-21
lines changed

2 files changed

+47
-21
lines changed

src/odoo_data_flow/lib/mapper.py

Lines changed: 20 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -323,38 +323,39 @@ def m2o_fun(line: LineDict, state: StateDict) -> str:
323323
return m2o_fun
324324

325325

326-
def m2m(prefix: str, *fields: Any, sep: str = ",") -> MapperFunc:
326+
def m2m(prefix: str, *fields: Any, sep: str = ",", default: str = "") -> MapperFunc:
327327
"""Returns a mapper that creates a comma-separated list of Many2many external IDs.
328328
329-
This mapper has two modes:
330-
1. **Multi-column**: If multiple fields are provided, it treats the value of
331-
each field as a single ID.
332-
2. **Single-column**: If one field is provided, it splits the value of that
333-
field by the separator `sep`.
329+
It processes values from specified source columns, splitting them by 'sep'
330+
if they contain the separator, and applies the prefix to each resulting ID.
334331
335332
Args:
336333
prefix: The XML ID prefix to apply to each value.
337-
*fields: One or more source column names.
338-
sep: The separator to use when splitting a single field.
334+
*fields: One or more source column names from which to get values.
335+
sep: The separator to use when splitting values within a single field.
336+
default: The value to return if no IDs are generated.
339337
340338
Returns:
341339
A mapper function that returns a comma-separated string of external IDs.
342340
"""
343341

344342
def m2m_fun(line: LineDict, state: StateDict) -> str:
345-
all_values = []
346-
if len(fields) > 1: # Mode 1: Multiple columns
347-
for field_name in fields:
348-
value = _get_field_value(line, field_name)
349-
if value:
350-
all_values.append(to_m2o(prefix, value))
351-
elif len(fields) == 1: # Mode 2: Single column with separator
352-
field_name = fields[0]
343+
all_ids = []
344+
for field_name in fields:
353345
value = _get_field_value(line, field_name)
354346
if value and isinstance(value, str):
355-
all_values.extend(to_m2o(prefix, v.strip()) for v in value.split(sep))
356-
357-
return ",".join(all_values)
347+
# Always split if the value contains the separator
348+
# This makes behavior consistent regardless of # of fields
349+
current_field_ids = [
350+
to_m2m(prefix, v.strip()) for v in value.split(sep) if v.strip()
351+
]
352+
all_ids.extend(current_field_ids)
353+
354+
# If no IDs are generated and default is provided, use it
355+
if not all_ids and default:
356+
return default
357+
358+
return ",".join(all_ids)
358359

359360
return m2m_fun
360361

tests/test_mapper.py

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -151,16 +151,23 @@ def test_m2o_map_success() -> None:
151151

152152
def test_m2m_multi_column() -> None:
153153
"""Tests the m2m mapper in multi-column mode."""
154+
# With the new m2m logic, this should split both "tags" and "other_tags"
155+
# "tags" ("T1, T2") should become "tag_prefix.T1", "tag_prefix.T2"
156+
# "other_tags" ("T3") should become "tag_prefix.T3"
157+
# Joined by comma: "tag_prefix.T1,tag_prefix.T2,tag_prefix.T3"
154158
mapper_func = mapper.m2m("tag_prefix", "tags", "other_tags")
155159
result = mapper_func(LINE_M2M, {})
156-
assert result == "tag_prefix.T1, T2,tag_prefix.T3"
160+
assert result == "tag_prefix.T1,tag_prefix.T2,tag_prefix.T3"
157161

158162

159163
def test_m2m_multi_column_with_missing_field() -> None:
160164
"""Tests the m2m mapper in multi-column mode with a non-existent field."""
165+
# "tags" ("T1, T2") should become "tag_prefix.T1", "tag_prefix.T2"
166+
# "non_existent_field" will be empty/None
167+
# Joined by comma: "tag_prefix.T1,tag_prefix.T2"
161168
mapper_func = mapper.m2m("tag_prefix", "tags", "non_existent_field")
162169
result = mapper_func(LINE_M2M, {})
163-
assert result == "tag_prefix.T1, T2"
170+
assert result == "tag_prefix.T1,tag_prefix.T2"
164171

165172

166173
def test_m2m_multi_column_with_empty_value() -> None:
@@ -172,10 +179,28 @@ def test_m2m_multi_column_with_empty_value() -> None:
172179

173180
def test_m2m_single_empty_field() -> None:
174181
"""Tests the m2m mapper in single-column mode with an empty field."""
182+
# "empty_tags" is "", so it should return an empty string.
175183
mapper_func = mapper.m2m("tag_prefix", "empty_tags", sep=",")
176184
assert mapper_func(LINE_M2M, {}) == ""
177185

178186

187+
# Add a specific test for m2m single column with a comma-separated value
188+
def test_m2m_single_column_splits_value() -> None:
189+
"""Tests that m2m in single-column mode correctly splits the field value."""
190+
line = {"products": "PROD1, PROD2,PROD3"}
191+
mapper_func = mapper.m2m("prod_prefix", "products", sep=",")
192+
assert (
193+
mapper_func(line, {}) == "prod_prefix.PROD1,prod_prefix.PROD2,prod_prefix.PROD3"
194+
)
195+
196+
197+
def test_m2m_single_column_splits_value_with_custom_sep() -> None:
198+
"""Tests that m2m in single-column mode correctly splits with custom separator."""
199+
line = {"items": "ITEM-A; ITEM-B"}
200+
mapper_func = mapper.m2m("item_prefix", "items", sep=";")
201+
assert mapper_func(line, {}) == "item_prefix.ITEM-A,item_prefix.ITEM-B"
202+
203+
179204
def test_m2m_map_with_concat() -> None:
180205
"""Tests m2m_map wrapping another mapper."""
181206
concat_mapper = mapper.concat(",", "tags", "other_tags")

0 commit comments

Comments
 (0)