feat(bank-integration): case-insensitive party matching, with per-row override
Party-name lookup at Create & Reconcile previously used a strict (strip-only) key, so a Bank Transaction whose bank_party_name differed in casing from the registry record would never resolve a party. Now: - 'Ignore Case in Party Matching' Check on Bank Integration (default ON) sets the global behaviour: party names are indexed under both their strict and lowercase forms, and the lookup tries both. - 'Case Mode' Select on each Customer / Supplier Mapping row overrides the global for that specific party — blank inherits, "Ignore Case" / "Case Sensitive" forces the mode regardless of the global setting. Honours the established "row > global" priority pattern used elsewhere. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
44c3281cb9
commit
e8e9359cb5
|
|
@ -48,29 +48,64 @@ 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.
|
||||
|
||||
Returns True if the party name for this row should be indexed/looked-up
|
||||
case-insensitively, False otherwise.
|
||||
"""
|
||||
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},
|
||||
built from the mapped rows of customer_mappings / supplier_mappings."""
|
||||
built from the mapped rows of customer_mappings / supplier_mappings.
|
||||
|
||||
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).
|
||||
"""
|
||||
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.bi_customer_name:
|
||||
continue
|
||||
cust_name = frappe.db.get_value("Bank Integration Customer", row.bi_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.bi_supplier_name:
|
||||
continue
|
||||
supp_name = frappe.db.get_value("Bank Integration Supplier", row.bi_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.
|
||||
Prefers Customer for incoming (Receive) and Supplier for outgoing (Pay),
|
||||
falling back to the other."""
|
||||
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"
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@
|
|||
"similarity_threshold_suppliers",
|
||||
"similarity_threshold_purpose",
|
||||
"consider_azeri_chars",
|
||||
"case_insensitive_party_match",
|
||||
"default_party_settings_section",
|
||||
"default_supplier_group",
|
||||
"default_customer_group",
|
||||
|
|
@ -104,6 +105,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",
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
Loading…
Reference in New Issue