From fd3f74d3ed6125d1565ddd574bfd19f37582d5cc Mon Sep 17 00:00:00 2001 From: Ali <010109ali@gmail.com> Date: Wed, 13 May 2026 16:25:49 +0000 Subject: [PATCH] feat: case-insensitive party matching, with per-row override Mirrors the same logic now in jey_erp.bank_integration. Party-name lookup at Create & Reconcile gains a global 'Ignore Case in Party Matching' Check (default ON) on Kapital Bank Settings, plus a 'Case Mode' Select on each Customer / Supplier Mapping row that overrides the global per row. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../kapital_bank_customer_mapping.json | 8 ++++ .../kapital_bank_settings.json | 8 ++++ .../kapital_bank_supplier_mapping.json | 8 ++++ kapital_bank/mapping.py | 41 ++++++++++++++++--- 4 files changed, 59 insertions(+), 6 deletions(-) diff --git a/kapital_bank/kapital_bank/doctype/kapital_bank_customer_mapping/kapital_bank_customer_mapping.json b/kapital_bank/kapital_bank/doctype/kapital_bank_customer_mapping/kapital_bank_customer_mapping.json index 5826a32..60e01fd 100644 --- a/kapital_bank/kapital_bank/doctype/kapital_bank_customer_mapping/kapital_bank_customer_mapping.json +++ b/kapital_bank/kapital_bank/doctype/kapital_bank_customer_mapping/kapital_bank_customer_mapping.json @@ -10,6 +10,7 @@ "customer_group", "territory", "payment_terms", + "case_mode", "mapping_type" ], "fields": [ @@ -55,6 +56,13 @@ "label": "Payment Terms", "options": "Payment Terms Template" }, + { + "description": "Override the global 'Ignore Case in Party Matching' for this row. Blank = use the global setting.", + "fieldname": "case_mode", + "fieldtype": "Select", + "label": "Case Mode", + "options": "\nIgnore Case\nCase Sensitive" + }, { "default": "Manual", "fieldname": "mapping_type", diff --git a/kapital_bank/kapital_bank/doctype/kapital_bank_settings/kapital_bank_settings.json b/kapital_bank/kapital_bank/doctype/kapital_bank_settings/kapital_bank_settings.json index 944ddce..67d0eb1 100644 --- a/kapital_bank/kapital_bank/doctype/kapital_bank_settings/kapital_bank_settings.json +++ b/kapital_bank/kapital_bank/doctype/kapital_bank_settings/kapital_bank_settings.json @@ -15,6 +15,7 @@ "similarity_threshold_suppliers", "similarity_threshold_purpose", "consider_azeri_chars", + "case_insensitive_party_match", "default_party_settings_section", "default_supplier_group", "default_customer_group", @@ -119,6 +120,13 @@ "fieldtype": "Check", "label": "Consider Azerbaijani Characters" }, + { + "default": "1", + "description": "When resolving a Bank Transaction's counterparty against the mappings, ignore case differences (e.g. 'Acme MMC' matches 'acme mmc'). Each row in Customer/Supplier Mappings can override this via 'Case Mode'.", + "fieldname": "case_insensitive_party_match", + "fieldtype": "Check", + "label": "Ignore Case in Party Matching" + }, { "fieldname": "default_party_settings_section", "fieldtype": "Section Break", diff --git a/kapital_bank/kapital_bank/doctype/kapital_bank_supplier_mapping/kapital_bank_supplier_mapping.json b/kapital_bank/kapital_bank/doctype/kapital_bank_supplier_mapping/kapital_bank_supplier_mapping.json index aaea316..ba0ffe4 100644 --- a/kapital_bank/kapital_bank/doctype/kapital_bank_supplier_mapping/kapital_bank_supplier_mapping.json +++ b/kapital_bank/kapital_bank/doctype/kapital_bank_supplier_mapping/kapital_bank_supplier_mapping.json @@ -9,6 +9,7 @@ "erp_supplier", "supplier_group", "payment_terms", + "case_mode", "mapping_type" ], "fields": [ @@ -47,6 +48,13 @@ "label": "Payment Terms", "options": "Payment Terms Template" }, + { + "description": "Override the global 'Ignore Case in Party Matching' for this row. Blank = use the global setting.", + "fieldname": "case_mode", + "fieldtype": "Select", + "label": "Case Mode", + "options": "\nIgnore Case\nCase Sensitive" + }, { "default": "Manual", "fieldname": "mapping_type", diff --git a/kapital_bank/mapping.py b/kapital_bank/mapping.py index cf9a29f..d5f7a6b 100644 --- a/kapital_bank/mapping.py +++ b/kapital_bank/mapping.py @@ -37,28 +37,57 @@ def _extract_messages(message_log): return " | ".join(p for p in parts if p) +def _effective_case_insensitive(row, global_default): + """Per-row Case Mode overrides the global Ignore Case flag.""" + mode = (getattr(row, "case_mode", None) or "").strip() + if mode == "Ignore Case": + return True + if mode == "Case Sensitive": + return False + return global_default + + def _build_name_to_party(settings): - """Counterparty text -> {"Customer": erp_customer} / {"Supplier": erp_supplier}.""" + """Counterparty text -> {"Customer": erp_customer} / {"Supplier": erp_supplier}. + + A row may be indexed under its strict key only, its lowercased key only, or + both — depending on the row's Case Mode (which overrides the global + `case_insensitive_party_match` setting on Kapital Bank Settings). + """ + global_ci = bool(getattr(settings, "case_insensitive_party_match", 1)) idx = {} + + def _add(name, party_type, erp_party, ci): + key = (name or "").strip() + if not key: + return + idx.setdefault(key, {})[party_type] = erp_party + if ci: + idx.setdefault(key.lower(), {})[party_type] = erp_party + for row in settings.customer_mappings: if not row.erp_customer or not row.kb_customer_name: continue cust_name = frappe.db.get_value("Kapital Bank Customer", row.kb_customer_name, "customer_name") if cust_name: - idx.setdefault(cust_name.strip(), {})["Customer"] = row.erp_customer + _add(cust_name, "Customer", row.erp_customer, _effective_case_insensitive(row, global_ci)) for row in settings.supplier_mappings: if not row.erp_supplier or not row.kb_supplier_name: continue supp_name = frappe.db.get_value("Kapital Bank Supplier", row.kb_supplier_name, "supplier_name") if supp_name: - idx.setdefault(supp_name.strip(), {})["Supplier"] = row.erp_supplier + _add(supp_name, "Supplier", row.erp_supplier, _effective_case_insensitive(row, global_ci)) return idx def _resolve_party_from_name(name, name_to_party, payment_type): - """(party_type, erp_party) for `name`, or (None, None). Prefers Customer for - incoming (Receive) and Supplier for outgoing (Pay), falling back to the other.""" - cands = name_to_party.get((name or "").strip()) + """(party_type, erp_party) for `name`, or (None, None). Tries an exact (strip-only) + lookup first; falls back to lowercased lookup so rows indexed case-insensitively + can be reached from arbitrary Bank Transaction bank_party_name casing.""" + key = (name or "").strip() + if not key: + return None, None + cands = name_to_party.get(key) or name_to_party.get(key.lower()) if not cands: return None, None preferred = "Customer" if payment_type == "Receive" else "Supplier"