Skip to content

Commit b3510d8

Browse files
authored
Merge pull request #197 from OpenSPP/fix/991-import-match-condition-field
fix(spp_import_match): conditional rows act as pure gates, hide UI columns (#991)
2 parents 7c8f3b8 + d44adb3 commit b3510d8

7 files changed

Lines changed: 120 additions & 11 deletions

File tree

spp_import_match/README.rst

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -393,6 +393,26 @@ Test 12: Security — Non-Admin Access
393393
Changelog
394394
=========
395395

396+
19.0.2.0.2
397+
~~~~~~~~~~
398+
399+
- chore(views): hide the conditional-gate columns (``Is Conditional``,
400+
``Condition Field``, ``Condition Value``) from the match-rule fields
401+
list — the schema and matching-engine wiring stay in place, but no
402+
current import flow uses the gate, so the columns are kept out of the
403+
UI until a real use case lands.
404+
405+
19.0.2.0.1
406+
~~~~~~~~~~
407+
408+
- fix(matching): add a ``condition_field_id`` Many2one column to
409+
``spp.import.match.fields`` and rewrite the matching loop so
410+
conditional rows act as pure gates — never added to the DB search
411+
domain. Renames the IMPORTED VALUE column heading to **Condition
412+
Value**. Fixes the case where a CSV-only metadata column (e.g.
413+
``data_source``) was being injected into the search domain and causing
414+
zero matches.
415+
396416
19.0.2.0.0
397417
~~~~~~~~~~
398418

spp_import_match/__manifest__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
"name": "OpenSPP Import Match",
66
"summary": "OpenSPP Import Match enhances data import processes by intelligently matching incoming records against existing data, preventing duplication and ensuring registry integrity. It provides configurable matching logic and supports seamless updates to existing records during bulk data onboarding.",
77
"category": "OpenSPP/Integration",
8-
"version": "19.0.2.0.0",
8+
"version": "19.0.2.0.2",
99
"sequence": 1,
1010
"author": "OpenSPP.org",
1111
"website": "https://github.com/OpenSPP/OpenSPP2",

spp_import_match/models/import_match.py

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,9 +68,21 @@ def _match_find(self, model, converted_row, imported_row):
6868
domain = list()
6969
for field in combination.field_ids:
7070
if field.is_conditional:
71-
if imported_row[field.name] != field.imported_value:
71+
# Conditional rows are pure *gates* — they decide whether
72+
# the rule applies to this CSV row. The CSV column that
73+
# carries the gate value is `condition_field_id` when
74+
# set; otherwise we fall back to `field_id` for
75+
# backwards compatibility with rules created before
76+
# OP#991. Crucially, a conditional row is **not** added
77+
# to the DB search domain — the gate column may be a
78+
# CSV-only metadata field (e.g. `data_source`) that
79+
# doesn't exist on the registrant model.
80+
gate_field_name = field.condition_field_id.name if field.condition_field_id else field.field_id.name
81+
if imported_row.get(gate_field_name) != field.imported_value:
7282
combination_valid = False
7383
break
84+
continue
85+
7486
if field.field_id.name in converted_row:
7587
row_value = converted_row[field.field_id.name]
7688
# Skip matching on empty values to avoid false matches
@@ -141,7 +153,29 @@ class SPPImportMatchFields(models.Model):
141153
match_id = fields.Many2one("spp.import.match", string="Match", ondelete="cascade")
142154
model_id = fields.Many2one(related="match_id.model_id")
143155
is_conditional = fields.Boolean()
144-
imported_value = fields.Char(help="This will be used as a condition to disregard this field if matched")
156+
condition_field_id = fields.Many2one(
157+
"ir.model.fields",
158+
string="Condition Field",
159+
ondelete="cascade",
160+
domain="[('model_id', '=', model_id)]",
161+
help=(
162+
"When `Is Conditional` is set, the rule only fires for CSV rows "
163+
"whose value in this field equals `Condition Value`. The "
164+
"condition field is used purely as a gate — it is **not** added "
165+
"to the database search domain, so it can safely be a CSV-only "
166+
"metadata column (e.g. `data_source`) that doesn't have data on "
167+
"the registrant. Leave empty to fall back to the legacy "
168+
"behaviour where `Field` is used as both the gate and the "
169+
"search predicate."
170+
),
171+
)
172+
imported_value = fields.Char(
173+
string="Condition Value",
174+
help=(
175+
"Expected value of the condition field. The rule only applies "
176+
"when the imported row's `Condition Field` matches this value."
177+
),
178+
)
145179

146180
def _compute_name(self):
147181
for rec in self:

spp_import_match/readme/HISTORY.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
### 19.0.2.0.2
2+
3+
- chore(views): hide the conditional-gate columns (`Is Conditional`, `Condition Field`, `Condition Value`) from the match-rule fields list — the schema and matching-engine wiring stay in place, but no current import flow uses the gate, so the columns are kept out of the UI until a real use case lands.
4+
5+
### 19.0.2.0.1
6+
7+
- fix(matching): add a `condition_field_id` Many2one column to `spp.import.match.fields` and rewrite the matching loop so conditional rows act as pure gates — never added to the DB search domain. Renames the IMPORTED VALUE column heading to **Condition Value**. Fixes the case where a CSV-only metadata column (e.g. `data_source`) was being injected into the search domain and causing zero matches.
8+
19
### 19.0.2.0.0
210

311
- Initial migration to OpenSPP2

spp_import_match/static/description/index.html

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -804,6 +804,28 @@ <h2><a class="toc-backref" href="#toc-entry-15">Changelog</a></h2>
804804
</div>
805805
</div>
806806
<div class="section" id="section-1">
807+
<h1>19.0.2.0.2</h1>
808+
<ul class="simple">
809+
<li>chore(views): hide the conditional-gate columns (<tt class="docutils literal">Is Conditional</tt>,
810+
<tt class="docutils literal">Condition Field</tt>, <tt class="docutils literal">Condition Value</tt>) from the match-rule fields
811+
list — the schema and matching-engine wiring stay in place, but no
812+
current import flow uses the gate, so the columns are kept out of the
813+
UI until a real use case lands.</li>
814+
</ul>
815+
</div>
816+
<div class="section" id="section-2">
817+
<h1>19.0.2.0.1</h1>
818+
<ul class="simple">
819+
<li>fix(matching): add a <tt class="docutils literal">condition_field_id</tt> Many2one column to
820+
<tt class="docutils literal">spp.import.match.fields</tt> and rewrite the matching loop so
821+
conditional rows act as pure gates — never added to the DB search
822+
domain. Renames the IMPORTED VALUE column heading to <strong>Condition
823+
Value</strong>. Fixes the case where a CSV-only metadata column (e.g.
824+
<tt class="docutils literal">data_source</tt>) was being injected into the search domain and causing
825+
zero matches.</li>
826+
</ul>
827+
</div>
828+
<div class="section" id="section-3">
807829
<h1>19.0.2.0.0</h1>
808830
<ul class="simple">
809831
<li>Initial migration to OpenSPP2</li>

spp_import_match/tests/test_import_match_model.py

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -120,21 +120,31 @@ def test_match_find_conditional_skip(self):
120120
self.assertFalse(result.id)
121121

122122
def test_match_find_conditional_match(self):
123-
"""Test _match_find uses rule when conditional value matches."""
124-
partner = self.env["res.partner"].create({"name": "ConditionalMatchTest"})
123+
"""Test _match_find applies the rule when the conditional gate passes.
124+
125+
Under OP#991 semantics, an `is_conditional=True` row is a pure gate —
126+
it decides whether the rule applies to this CSV row but is never
127+
added to the DB search domain (the gate column may be a CSV-only
128+
metadata field that doesn't exist on the registrant model). The
129+
combination must include at least one non-conditional row to
130+
provide the actual search predicate. Here `name` is the gate and
131+
`email` is the search predicate.
132+
"""
133+
partner = self.env["res.partner"].create({"name": "ConditionalMatchTest", "email": "conditional@example.com"})
125134
match = self._create_match_rule(
126135
[
127136
{
128137
"field_id": self.name_field.id,
129138
"is_conditional": True,
130139
"imported_value": "ConditionalMatchTest",
131-
}
140+
},
141+
{"field_id": self.email_field.id},
132142
]
133143
)
134144
result = match._match_find(
135145
self.env["res.partner"],
136-
{"name": "ConditionalMatchTest"},
137-
{"name": "ConditionalMatchTest", "id": None},
146+
{"email": "conditional@example.com"},
147+
{"name": "ConditionalMatchTest", "email": "conditional@example.com", "id": None},
138148
)
139149
self.assertEqual(result, partner)
140150

spp_import_match/views/import_match_view.xml

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,11 +52,26 @@
5252
/>
5353
<field name="match_id" column_invisible="1" />
5454
<field name="model_id" column_invisible="1" />
55-
<field name="is_conditional" />
56-
<!-- Odoo 19: Converted attrs to individual attributes -->
55+
<!-- Conditional gate columns hidden for now —
56+
the schema (is_conditional / condition_field_id /
57+
imported_value) and the matching-engine wiring
58+
stay in place, but no current import flow needs
59+
the gate, so the columns are kept out of the UI
60+
until a use case lands. See OP#991. -->
61+
<field
62+
name="is_conditional"
63+
column_invisible="1"
64+
/>
65+
<field
66+
name="condition_field_id"
67+
string="Condition Field"
68+
column_invisible="1"
69+
options="{'no_create': True}"
70+
/>
5771
<field
5872
name="imported_value"
59-
readonly="is_conditional == False"
73+
string="Condition Value"
74+
column_invisible="1"
6075
/>
6176
</list>
6277
</field>

0 commit comments

Comments
 (0)