This commit is contained in:
Ali 2026-03-13 21:04:16 +04:00
parent 5b47d06eec
commit 68c3d7d8a9
1 changed files with 106 additions and 19 deletions

View File

@ -1,6 +1,18 @@
import json import json
import frappe import frappe
from frappe.utils import cint, flt
def _extract_messages(message_log):
"""Extract plain text from frappe.local.message_log entries."""
parts = []
for m in message_log:
if isinstance(m, dict):
parts.append(m.get("message") or m.get("msg") or "")
else:
parts.append(str(m))
return " | ".join(p for p in parts if p)
@frappe.whitelist() @frappe.whitelist()
@ -291,6 +303,8 @@ def _create_and_reconcile_doc(txn, paid_from, paid_to, document_type, multi_curr
return {"success": True, "doc_name": doc_name, "reconciled": False, "error": str(re)} return {"success": True, "doc_name": doc_name, "reconciled": False, "error": str(re)}
except Exception as e: except Exception as e:
frappe.db.rollback()
frappe.local.message_log = []
frappe.log_error( frappe.log_error(
f"_create_and_reconcile_doc failed for {bank_transaction_name}: {e}\n{frappe.get_traceback()}", f"_create_and_reconcile_doc failed for {bank_transaction_name}: {e}\n{frappe.get_traceback()}",
"Kapital Bank Mapping", "Kapital Bank Mapping",
@ -320,11 +334,22 @@ def _create_payment_entry_for_brt(txn, paid_from, paid_to, bank_txn):
if party_type and party: if party_type and party:
pe.party_type = party_type pe.party_type = party_type
pe.party = party pe.party = party
frappe.local.message_log = []
pe.insert(ignore_permissions=True) pe.insert(ignore_permissions=True)
pe.submit() pe.submit()
if frappe.local.message_log:
msgs = _extract_messages(frappe.local.message_log)
frappe.local.message_log = []
frappe.db.rollback()
return {"success": False, "error": msgs}
return {"success": True, "doc_name": pe.name} return {"success": True, "doc_name": pe.name}
except Exception as e: except Exception as e:
frappe.db.rollback()
frappe.local.message_log = []
frappe.log_error( frappe.log_error(
f"_create_payment_entry_for_brt failed: {e}\n{frappe.get_traceback()}", f"_create_payment_entry_for_brt failed: {e}\n{frappe.get_traceback()}",
"Kapital Bank Mapping", "Kapital Bank Mapping",
@ -335,6 +360,9 @@ def _create_payment_entry_for_brt(txn, paid_from, paid_to, bank_txn):
def _create_journal_entry_for_brt(txn, paid_from, paid_to, bank_txn, multi_currency=False): def _create_journal_entry_for_brt(txn, paid_from, paid_to, bank_txn, multi_currency=False):
"""Create and submit a Journal Entry for a BRT transaction.""" """Create and submit a Journal Entry for a BRT transaction."""
try: try:
import erpnext
from erpnext.setup.utils import get_exchange_rate
amount = abs(float(txn.get("amount") or 0)) amount = abs(float(txn.get("amount") or 0))
party_type = (txn.get("party_type") or "").strip() party_type = (txn.get("party_type") or "").strip()
party = (txn.get("party") or "").strip() party = (txn.get("party") or "").strip()
@ -342,9 +370,47 @@ def _create_journal_entry_for_brt(txn, paid_from, paid_to, bank_txn, multi_curre
posting_date = bank_txn.date or txn.get("date") posting_date = bank_txn.date or txn.get("date")
ref_no = bank_txn.reference_number or txn.get("reference_number") or "" ref_no = bank_txn.reference_number or txn.get("reference_number") or ""
# Determine account types to assign party to the right row company_currency = erpnext.get_company_currency(bank_txn.company)
paid_from_type = frappe.db.get_value("Account", paid_from, "account_type") or ""
paid_to_type = frappe.db.get_value("Account", paid_to, "account_type") or "" # Get account currencies and types
from_account = frappe.get_cached_value("Account", paid_from, ["account_type", "account_currency"], as_dict=True) or {}
to_account = frappe.get_cached_value("Account", paid_to, ["account_type", "account_currency"], as_dict=True) or {}
paid_from_type = from_account.get("account_type") or ""
paid_to_type = to_account.get("account_type") or ""
from_currency = from_account.get("account_currency") or company_currency
to_currency = to_account.get("account_currency") or company_currency
# Auto-enable multi_currency if any account is in a foreign currency
is_multi = from_currency != company_currency or to_currency != company_currency
# Frappe rounds debit_in_account_currency to field precision BEFORE multiplying
# by exchange_rate to get debit (company currency).
# Strategy: keep the original amount on the company-currency row, then compute
# an exact exchange rate for the foreign row so that:
# flt(foreign_amount * exact_rate, P) == flt(amount, P)
# This mirrors what Frappe UI does when the user types the AZN amount first.
currency_precision = cint(frappe.db.get_single_value("System Settings", "currency_precision") or 2)
if from_currency == company_currency and to_currency != company_currency:
to_rate = get_exchange_rate(to_currency, company_currency, posting_date)
to_amount = flt(amount / to_rate, currency_precision)
exact_to_rate = (amount / to_amount) if to_amount else to_rate
from_rate = 1
from_amount = amount
to_rate = exact_to_rate
elif to_currency == company_currency and from_currency != company_currency:
from_rate = get_exchange_rate(from_currency, company_currency, posting_date)
from_amount = flt(amount / from_rate, currency_precision)
exact_from_rate = (amount / from_amount) if from_amount else from_rate
to_rate = 1
to_amount = amount
from_rate = exact_from_rate
else:
# Both same currency OR both foreign (rare)
from_rate = 1 if from_currency == company_currency else get_exchange_rate(from_currency, company_currency, posting_date)
to_rate = 1 if to_currency == company_currency else get_exchange_rate(to_currency, company_currency, posting_date)
from_amount = flt(amount / from_rate, currency_precision)
to_amount = flt(amount / to_rate, currency_precision)
je = frappe.new_doc("Journal Entry") je = frappe.new_doc("Journal Entry")
je.voucher_type = "Bank Entry" je.voucher_type = "Bank Entry"
@ -353,28 +419,38 @@ def _create_journal_entry_for_brt(txn, paid_from, paid_to, bank_txn, multi_curre
je.cheque_no = ref_no je.cheque_no = ref_no
je.cheque_date = posting_date je.cheque_date = posting_date
je.user_remark = bank_txn.description or txn.get("purpose") or "" je.user_remark = bank_txn.description or txn.get("purpose") or ""
if multi_currency: if is_multi:
je.multi_currency = 1 je.multi_currency = 1
# Pay: paid_from credit, paid_to debit # Pay: paid_from credit, paid_to debit
# Receive: paid_from debit, paid_to credit # Receive: paid_from debit, paid_to credit
# Only set *_in_account_currency + exchange_rate; Frappe computes debit/credit itself.
if is_pay: if is_pay:
from_credit, from_debit = amount, 0 from_row = {
to_credit, to_debit = 0, amount "account": paid_from,
"exchange_rate": from_rate,
"credit_in_account_currency": from_amount,
"debit_in_account_currency": 0,
}
to_row = {
"account": paid_to,
"exchange_rate": to_rate,
"debit_in_account_currency": to_amount,
"credit_in_account_currency": 0,
}
else: else:
from_credit, from_debit = 0, amount from_row = {
to_credit, to_debit = amount, 0 "account": paid_from,
"exchange_rate": from_rate,
from_row = { "debit_in_account_currency": from_amount,
"account": paid_from, "credit_in_account_currency": 0,
"credit_in_account_currency": from_credit, }
"debit_in_account_currency": from_debit, to_row = {
} "account": paid_to,
to_row = { "exchange_rate": to_rate,
"account": paid_to, "credit_in_account_currency": to_amount,
"credit_in_account_currency": to_credit, "debit_in_account_currency": 0,
"debit_in_account_currency": to_debit, }
}
if party_type and party: if party_type and party:
if paid_from_type in ("Receivable", "Payable"): if paid_from_type in ("Receivable", "Payable"):
@ -386,11 +462,22 @@ def _create_journal_entry_for_brt(txn, paid_from, paid_to, bank_txn, multi_curre
je.append("accounts", from_row) je.append("accounts", from_row)
je.append("accounts", to_row) je.append("accounts", to_row)
frappe.local.message_log = []
je.insert(ignore_permissions=True) je.insert(ignore_permissions=True)
je.submit() je.submit()
if frappe.local.message_log:
msgs = _extract_messages(frappe.local.message_log)
frappe.local.message_log = []
frappe.db.rollback()
return {"success": False, "error": msgs}
return {"success": True, "doc_name": je.name} return {"success": True, "doc_name": je.name}
except Exception as e: except Exception as e:
frappe.db.rollback()
frappe.local.message_log = []
frappe.log_error( frappe.log_error(
f"_create_journal_entry_for_brt failed: {e}\n{frappe.get_traceback()}", f"_create_journal_entry_for_brt failed: {e}\n{frappe.get_traceback()}",
"Kapital Bank Mapping", "Kapital Bank Mapping",