added mapping without party

This commit is contained in:
Ali 2026-03-13 17:20:08 +04:00
parent 7ced7d18a5
commit fc34e5104b
4 changed files with 11987 additions and 12 deletions

View File

@ -1442,7 +1442,7 @@ def _build_import_lookups(settings):
for row in settings.transaction_mappings:
if not row.paid_from or not row.paid_to:
continue
if not row.purpose_keyword and not row.counterparty:
if not row.purpose_keyword and not row.counterparty and not row.counterparty_type:
continue
keyword_lower = ""
@ -1468,8 +1468,10 @@ def _build_import_lookups(settings):
getattr(row, "payment_type", None) or "",
cp_name,
cp_voen,
getattr(row, "counterparty_type", None) or "",
getattr(row, "document_type", None) or "Payment Entry",
getattr(row, "cost_center", None) or "",
bool(getattr(row, "multi_currency", 0)),
))
# Each key maps to a dict with optional "Customer" and "Supplier" entries.
@ -1523,8 +1525,9 @@ def _import_one_transaction(txn_data, settings, purpose_rules, purpose_threshold
both_rules = []
purpose_only_rules = []
counterparty_only_rules = []
fallback_rules = []
for rule in purpose_rules:
keyword, pf, pt, rule_pt, cp_name, cp_voen, doc_type, rule_cc = rule
keyword, pf, pt, rule_pt, cp_name, cp_voen, cp_type, doc_type, rule_cc, rule_mc = rule
has_keyword = bool(keyword)
has_cp = bool(cp_name or cp_voen)
if has_keyword and has_cp:
@ -1533,6 +1536,8 @@ def _import_one_transaction(txn_data, settings, purpose_rules, purpose_threshold
purpose_only_rules.append(rule)
elif has_cp:
counterparty_only_rules.append(rule)
elif cp_type:
fallback_rules.append(rule)
def _counterparty_matches(cp_name, cp_voen):
if cp_voen and contr_voen and cp_voen == contr_voen:
@ -1543,45 +1548,63 @@ def _import_one_transaction(txn_data, settings, purpose_rules, purpose_threshold
def _match_purpose_rules(rules, check_counterparty):
best_score = 0
best_pf = best_pt = best_doc_type = best_cc = None
for keyword, pf, pt, rule_pt, cp_name, cp_voen, doc_type, rule_cc in rules:
best_pf = best_pt = best_doc_type = best_cc = best_mc = None
for keyword, pf, pt, rule_pt, cp_name, cp_voen, cp_type, doc_type, rule_cc, rule_mc in rules:
if rule_pt and rule_pt != payment_type:
continue
if check_counterparty and not _counterparty_matches(cp_name, cp_voen):
continue
if keyword:
if keyword in purpose_lower:
return pf, pt, doc_type, rule_cc
return pf, pt, doc_type, rule_cc, rule_mc
score = SequenceMatcher(None, purpose_lower, keyword).ratio()
if score > best_score and score >= purpose_threshold:
best_score = score
best_pf, best_pt, best_doc_type, best_cc = pf, pt, doc_type, rule_cc
best_pf, best_pt, best_doc_type, best_cc, best_mc = pf, pt, doc_type, rule_cc, rule_mc
else:
return pf, pt, doc_type, rule_cc
return best_pf, best_pt, best_doc_type, best_cc
return pf, pt, doc_type, rule_cc, rule_mc
return best_pf, best_pt, best_doc_type, best_cc, best_mc
doc_type = "Payment Entry"
cost_center = ""
multi_currency = False
# Phase 1: rules with BOTH purpose keyword AND counterparty
paid_from, paid_to, matched_doc_type, matched_cc = _match_purpose_rules(both_rules, check_counterparty=True)
paid_from, paid_to, matched_doc_type, matched_cc, matched_mc = _match_purpose_rules(both_rules, check_counterparty=True)
if matched_doc_type:
doc_type = matched_doc_type
cost_center = matched_cc or ""
multi_currency = matched_mc or False
# Phase 2: rules with only purpose keyword
if not paid_from or not paid_to:
paid_from, paid_to, matched_doc_type, matched_cc = _match_purpose_rules(purpose_only_rules, check_counterparty=False)
paid_from, paid_to, matched_doc_type, matched_cc, matched_mc = _match_purpose_rules(purpose_only_rules, check_counterparty=False)
if matched_doc_type:
doc_type = matched_doc_type
cost_center = matched_cc or ""
multi_currency = matched_mc or False
# Phase 3: rules with only counterparty
if not paid_from or not paid_to:
paid_from, paid_to, matched_doc_type, matched_cc = _match_purpose_rules(counterparty_only_rules, check_counterparty=True)
paid_from, paid_to, matched_doc_type, matched_cc, matched_mc = _match_purpose_rules(counterparty_only_rules, check_counterparty=True)
if matched_doc_type:
doc_type = matched_doc_type
cost_center = matched_cc or ""
multi_currency = matched_mc or False
# Phase 4: fallback rules — counterparty_type only, no specific counterparty
if not paid_from or not paid_to:
inferred_cp_type = "Customer" if payment_type == "Receive" else "Supplier"
for keyword, pf, pt, rule_pt, cp_name, cp_voen, cp_type, doc_type, rule_cc, rule_mc in fallback_rules:
if rule_pt and rule_pt != payment_type:
continue
if cp_type and cp_type != inferred_cp_type:
continue
paid_from, paid_to = pf, pt
doc_type = doc_type
cost_center = rule_cc or ""
multi_currency = rule_mc or False
break
if not paid_from or not paid_to:
return {
@ -1633,6 +1656,8 @@ def _import_one_transaction(txn_data, settings, purpose_rules, purpose_threshold
je.cheque_no = ref_no
je.cheque_date = parsed_date
je.user_remark = purpose
if multi_currency:
je.multi_currency = 1
# Party can only be set on Receivable/Payable accounts
party_accounts = {"Receivable", "Payable"}

View File

@ -14,6 +14,7 @@
"paid_from",
"paid_to",
"cost_center",
"multi_currency",
"notes"
],
"fields": [
@ -77,6 +78,13 @@
"label": "Cost Center",
"options": "Cost Center"
},
{
"default": "0",
"fieldname": "multi_currency",
"fieldtype": "Check",
"in_list_view": 1,
"label": "Multi Currency"
},
{
"fieldname": "notes",
"fieldtype": "Data",
@ -86,7 +94,7 @@
],
"istable": 1,
"links": [],
"modified": "2026-03-10 17:54:47.738883",
"modified": "2026-03-12 16:52:06.237239",
"modified_by": "Administrator",
"module": "Kapital Bank",
"name": "Kapital Bank Transaction Mapping",

View File

@ -203,6 +203,7 @@ def _find_mapping_for_txn(txn, transaction_mappings, purpose_text_cache):
both_rules = []
purpose_only_rules = []
counterparty_only_rules = []
fallback_rules = []
for row in transaction_mappings:
if not row.paid_from or not row.paid_to:
@ -219,6 +220,8 @@ def _find_mapping_for_txn(txn, transaction_mappings, purpose_text_cache):
purpose_only_rules.append(row)
elif has_counterparty:
counterparty_only_rules.append(row)
elif row.counterparty_type:
fallback_rules.append(row)
def _purpose_matches(row):
keyword_text = purpose_text_cache.get(row.purpose_keyword, "")
@ -239,6 +242,12 @@ def _find_mapping_for_txn(txn, transaction_mappings, purpose_text_cache):
if _counterparty_matches(row):
return row
# Phase 4: fallback by counterparty_type
inferred_cp_type = "Customer" if payment_type == "Receive" else "Supplier"
for row in fallback_rules:
if row.counterparty_type == inferred_cp_type:
return row
return None

File diff suppressed because one or more lines are too long