From 694c17487d76ddc38764d58311ff2153a15d1ee5 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 27 Feb 2024 15:34:34 +0530 Subject: [PATCH 1/3] fix: incorrect exchange rate if JE has multi parties --- .../payment_reconciliation/payment_reconciliation.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py index b2716c9da47..972ce26f34a 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py @@ -633,7 +633,12 @@ class PaymentReconciliation(Document): journals_map = frappe._dict( frappe.db.get_all( "Journal Entry Account", - filters={"parent": ("in", journals), "account": ("in", [self.receivable_payable_account])}, + filters={ + "parent": ("in", journals), + "account": ("in", [self.receivable_payable_account]), + "party_type": self.party_type, + "party": self.party, + }, fields=[ "parent as `name`", "exchange_rate", From eaac02655b174cca9daa2d16ba85c335e70879c3 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Wed, 28 Feb 2024 17:07:37 +0530 Subject: [PATCH 2/3] fix: don't override reference exchange rate --- erpnext/accounts/doctype/payment_entry/payment_entry.py | 7 ++++++- erpnext/accounts/utils.py | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index c55c8201529..77efe78368b 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -471,7 +471,10 @@ class PaymentEntry(AccountsController): ) def set_missing_ref_details( - self, force: bool = False, update_ref_details_only_for: list | None = None + self, + force: bool = False, + update_ref_details_only_for: list | None = None, + ref_exchange_rate: float | None = None, ) -> None: for d in self.get("references"): if d.allocated_amount: @@ -484,6 +487,8 @@ class PaymentEntry(AccountsController): ref_details = get_reference_details( d.reference_doctype, d.reference_name, self.party_account_currency ) + if ref_exchange_rate: + ref_details.update({"exchange_rate": ref_exchange_rate}) for field, value in ref_details.items(): if d.exchange_gain_loss: diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index 64bc39a77b5..dda4486b84d 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -725,7 +725,7 @@ def update_reference_in_payment_entry( payment_entry.setup_party_account_field() payment_entry.set_missing_values() if not skip_ref_details_update_for_pe: - payment_entry.set_missing_ref_details() + payment_entry.set_missing_ref_details(ref_exchange_rate=d.exchange_rate or None) payment_entry.set_amounts() payment_entry.make_exchange_gain_loss_journal( From ed95d41a51687208e603ffc355fa717018d14185 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Wed, 28 Feb 2024 17:07:57 +0530 Subject: [PATCH 3/3] test: exchange rate fetch on JE with multiple forex parties --- .../test_payment_reconciliation.py | 100 ++++++++++++++++++ 1 file changed, 100 insertions(+) diff --git a/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py index fb75a0f7caf..301e6ef625c 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py +++ b/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py @@ -56,6 +56,7 @@ class TestPaymentReconciliation(FrappeTestCase): self.expense_account = "Cost of Goods Sold - _PR" self.debit_to = "Debtors - _PR" self.creditors = "Creditors - _PR" + self.cash = "Cash - _PR" # create bank account if frappe.db.exists("Account", "HDFC - _PR"): @@ -486,6 +487,91 @@ class TestPaymentReconciliation(FrappeTestCase): self.assertEqual(len(pr.get("invoices")), 0) self.assertEqual(len(pr.get("payments")), 0) + def test_payment_against_foreign_currency_journal(self): + transaction_date = nowdate() + + self.supplier = "_Test Supplier USD" + self.supplier2 = make_supplier("_Test Supplier2 USD", "USD") + amount = 100 + exc_rate1 = 80 + exc_rate2 = 83 + + je = frappe.new_doc("Journal Entry") + je.posting_date = transaction_date + je.company = self.company + je.user_remark = "test" + je.multi_currency = 1 + je.set( + "accounts", + [ + { + "account": self.creditors_usd, + "party_type": "Supplier", + "party": self.supplier, + "exchange_rate": exc_rate1, + "cost_center": self.cost_center, + "credit": amount * exc_rate1, + "credit_in_account_currency": amount, + }, + { + "account": self.creditors_usd, + "party_type": "Supplier", + "party": self.supplier2, + "exchange_rate": exc_rate2, + "cost_center": self.cost_center, + "credit": amount * exc_rate2, + "credit_in_account_currency": amount, + }, + { + "account": self.expense_account, + "cost_center": self.cost_center, + "debit": (amount * exc_rate1) + (amount * exc_rate2), + "debit_in_account_currency": (amount * exc_rate1) + (amount * exc_rate2), + }, + ], + ) + je.save().submit() + + pe = self.create_payment_entry(amount=amount, posting_date=transaction_date) + pe.payment_type = "Pay" + pe.party_type = "Supplier" + pe.party = self.supplier + pe.paid_to = self.creditors_usd + pe.paid_from = self.cash + pe.paid_amount = 8000 + pe.received_amount = 100 + pe.target_exchange_rate = exc_rate1 + pe.paid_to_account_currency = "USD" + pe.save().submit() + + pr = self.create_payment_reconciliation(party_is_customer=False) + pr.receivable_payable_account = self.creditors_usd + pr.minimum_invoice_amount = pr.maximum_invoice_amount = amount + pr.from_invoice_date = pr.to_invoice_date = transaction_date + pr.from_payment_date = pr.to_payment_date = transaction_date + + pr.get_unreconciled_entries() + invoices = [x.as_dict() for x in pr.get("invoices")] + payments = [x.as_dict() for x in pr.get("payments")] + pr.allocate_entries(frappe._dict({"invoices": invoices, "payments": payments})) + + # There should no difference_amount as the Journal and Payment have same exchange rate - 'exc_rate1' + for row in pr.allocation: + self.assertEqual(flt(row.get("difference_amount")), 0.0) + + pr.reconcile() + + # check PR tool output + self.assertEqual(len(pr.get("invoices")), 0) + self.assertEqual(len(pr.get("payments")), 0) + + journals = frappe.db.get_all( + "Journal Entry Account", + filters={"reference_type": je.doctype, "reference_name": je.name, "docstatus": 1}, + fields=["parent"], + ) + self.assertEqual([], journals) + def test_journal_against_invoice(self): transaction_date = nowdate() amount = 100 @@ -1248,3 +1334,17 @@ def make_customer(customer_name, currency=None): return customer.name else: return customer_name + + +def make_supplier(supplier_name, currency=None): + if not frappe.db.exists("Supplier", supplier_name): + supplier = frappe.new_doc("Supplier") + supplier.supplier_name = supplier_name + supplier.type = "Individual" + + if currency: + supplier.default_currency = currency + supplier.save() + return supplier.name + else: + return supplier_name