From f6c1dffb3531c5ba9b9011b72ddb852b2403ac26 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 25 Jun 2024 13:52:38 +0530 Subject: [PATCH 1/5] fix: incorrect dr/cr on Adv Payment against Journals --- erpnext/accounts/doctype/payment_entry/payment_entry.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index f5d36d387b0..6bad8ffc35a 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -1313,12 +1313,17 @@ class PaymentEntry(AccountsController): return "credit", reference.account if reference.reference_doctype == "Payment Entry": + # reference.account_type and reference.payment_type is only available for Reverse payments if reference.account_type == "Receivable" and reference.payment_type == "Pay": return "credit", self.party_account else: return "debit", self.party_account - return "debit", reference.account + if reference.reference_doctype == "Journal Entry": + if self.party_type == "Customer" and self.payment_type == "Receive": + return "credit", reference.account + else: + return "debit", reference.account def add_advance_gl_for_reference(self, gl_entries, invoice): args_dict = { From 5e84272cf920492a2cdfb2163e8ee3e7f899e6e9 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 25 Jun 2024 16:14:05 +0530 Subject: [PATCH 2/5] test: advance payment against journal entry - customer type --- .../test_payment_reconciliation.py | 116 ++++++++++++++++++ 1 file changed, 116 insertions(+) diff --git a/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py index 53f69a47e75..f272fede589 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py +++ b/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py @@ -109,6 +109,14 @@ class TestPaymentReconciliation(FrappeTestCase): "account_currency": "INR", "account_type": "Payable", }, + # 'Receivable' account for capturing advance received, under 'Liabilities' group + { + "attribute": "advance_receivable_account", + "account_name": "Advance Received", + "parent_account": "Current Liabilities - _PR", + "account_currency": "INR", + "account_type": "Receivable", + }, ] for x in accounts: @@ -1574,6 +1582,114 @@ class TestPaymentReconciliation(FrappeTestCase): ) self.assertEqual(len(pl_entries), 3) + def test_advance_payment_reconciliation_against_journal_for_customer(self): + frappe.db.set_value( + "Company", + self.company, + { + "book_advance_payments_in_separate_party_account": 1, + "default_advance_received_account": self.advance_receivable_account, + "reconcile_on_advance_payment_date": 0, + }, + ) + amount = 200.0 + je = self.create_journal_entry(self.debit_to, self.bank, amount) + je.accounts[0].cost_center = self.main_cc.name + je.accounts[0].party_type = "Customer" + je.accounts[0].party = self.customer + je.accounts[1].cost_center = self.main_cc.name + je = je.save().submit() + + pe = self.create_payment_entry(amount=amount).save().submit() + + pr = self.create_payment_reconciliation() + pr.default_advance_account = self.advance_receivable_account + pr.get_unreconciled_entries() + self.assertEqual(len(pr.invoices), 1) + self.assertEqual(len(pr.payments), 1) + invoices = [invoice.as_dict() for invoice in pr.invoices] + payments = [payment.as_dict() for payment in pr.payments] + pr.allocate_entries(frappe._dict({"invoices": invoices, "payments": payments})) + pr.reconcile() + + # Assert Ledger Entries + gl_entries = frappe.db.get_all( + "GL Entry", + filters={"voucher_no": pe.name, "is_cancelled": 0}, + ) + self.assertEqual(len(gl_entries), 4) + pl_entries = frappe.db.get_all( + "Payment Ledger Entry", + filters={"voucher_no": pe.name, "delinked": 0}, + ) + self.assertEqual(len(pl_entries), 3) + + gl_entries = frappe.db.get_all( + "GL Entry", + filters={"voucher_no": pe.name, "is_cancelled": 0}, + fields=["account", "voucher_no", "against_voucher", "debit", "credit"], + order_by="account, against_voucher, debit", + ) + expected_gle = [ + { + "account": self.advance_receivable_account, + "voucher_no": pe.name, + "against_voucher": pe.name, + "debit": 0.0, + "credit": amount, + }, + { + "account": self.advance_receivable_account, + "voucher_no": pe.name, + "against_voucher": pe.name, + "debit": amount, + "credit": 0.0, + }, + { + "account": self.debit_to, + "voucher_no": pe.name, + "against_voucher": je.name, + "debit": 0.0, + "credit": amount, + }, + { + "account": self.bank, + "voucher_no": pe.name, + "against_voucher": None, + "debit": amount, + "credit": 0.0, + }, + ] + self.assertEqual(gl_entries, expected_gle) + + pl_entries = frappe.db.get_all( + "Payment Ledger Entry", + filters={"voucher_no": pe.name}, + fields=["account", "voucher_no", "against_voucher_no", "amount"], + order_by="account, against_voucher_no, amount", + ) + expected_ple = [ + { + "account": self.advance_receivable_account, + "voucher_no": pe.name, + "against_voucher_no": pe.name, + "amount": -amount, + }, + { + "account": self.advance_receivable_account, + "voucher_no": pe.name, + "against_voucher_no": pe.name, + "amount": amount, + }, + { + "account": self.debit_to, + "voucher_no": pe.name, + "against_voucher_no": je.name, + "amount": -amount, + }, + ] + self.assertEqual(pl_entries, expected_ple) + def make_customer(customer_name, currency=None): if not frappe.db.exists("Customer", customer_name): From 1b384b99429f8953b99a7d6a6c1206ba4a319638 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 25 Jun 2024 16:25:20 +0530 Subject: [PATCH 3/5] test: advance payment entry against journal - supplier type --- .../test_payment_reconciliation.py | 115 ++++++++++++++++++ 1 file changed, 115 insertions(+) diff --git a/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py index f272fede589..a5295585221 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py +++ b/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py @@ -1690,6 +1690,121 @@ class TestPaymentReconciliation(FrappeTestCase): ] self.assertEqual(pl_entries, expected_ple) + def test_advance_payment_reconciliation_against_journal_for_supplier(self): + self.supplier = make_supplier("_Test Supplier") + frappe.db.set_value( + "Company", + self.company, + { + "book_advance_payments_in_separate_party_account": 1, + "default_advance_paid_account": self.advance_payable_account, + "reconcile_on_advance_payment_date": 0, + }, + ) + amount = 200.0 + je = self.create_journal_entry(self.creditors, self.bank, -amount) + je.accounts[0].cost_center = self.main_cc.name + je.accounts[0].party_type = "Supplier" + je.accounts[0].party = self.supplier + je.accounts[1].cost_center = self.main_cc.name + je = je.save().submit() + + pe = self.create_payment_entry(amount=amount) + pe.payment_type = "Pay" + pe.party_type = "Supplier" + pe.paid_from = self.bank + pe.paid_to = self.creditors + pe.party = self.supplier + pe.save().submit() + + pr = self.create_payment_reconciliation(party_is_customer=False) + pr.default_advance_account = self.advance_payable_account + pr.get_unreconciled_entries() + self.assertEqual(len(pr.invoices), 1) + self.assertEqual(len(pr.payments), 1) + invoices = [invoice.as_dict() for invoice in pr.invoices] + payments = [payment.as_dict() for payment in pr.payments] + pr.allocate_entries(frappe._dict({"invoices": invoices, "payments": payments})) + pr.reconcile() + + # Assert Ledger Entries + gl_entries = frappe.db.get_all( + "GL Entry", + filters={"voucher_no": pe.name, "is_cancelled": 0}, + ) + self.assertEqual(len(gl_entries), 4) + pl_entries = frappe.db.get_all( + "Payment Ledger Entry", + filters={"voucher_no": pe.name, "delinked": 0}, + ) + self.assertEqual(len(pl_entries), 3) + + gl_entries = frappe.db.get_all( + "GL Entry", + filters={"voucher_no": pe.name, "is_cancelled": 0}, + fields=["account", "voucher_no", "against_voucher", "debit", "credit"], + order_by="account, against_voucher, debit", + ) + expected_gle = [ + { + "account": self.advance_payable_account, + "voucher_no": pe.name, + "against_voucher": pe.name, + "debit": 0.0, + "credit": amount, + }, + { + "account": self.advance_payable_account, + "voucher_no": pe.name, + "against_voucher": pe.name, + "debit": amount, + "credit": 0.0, + }, + { + "account": self.creditors, + "voucher_no": pe.name, + "against_voucher": je.name, + "debit": amount, + "credit": 0.0, + }, + { + "account": self.bank, + "voucher_no": pe.name, + "against_voucher": None, + "debit": 0.0, + "credit": amount, + }, + ] + self.assertEqual(gl_entries, expected_gle) + + pl_entries = frappe.db.get_all( + "Payment Ledger Entry", + filters={"voucher_no": pe.name}, + fields=["account", "voucher_no", "against_voucher_no", "amount"], + order_by="account, against_voucher_no, amount", + ) + expected_ple = [ + { + "account": self.advance_payable_account, + "voucher_no": pe.name, + "against_voucher_no": pe.name, + "amount": -amount, + }, + { + "account": self.advance_payable_account, + "voucher_no": pe.name, + "against_voucher_no": pe.name, + "amount": amount, + }, + { + "account": self.creditors, + "voucher_no": pe.name, + "against_voucher_no": je.name, + "amount": -amount, + }, + ] + self.assertEqual(pl_entries, expected_ple) + def make_customer(customer_name, currency=None): if not frappe.db.exists("Customer", customer_name): From ad7efd59394aa645f72ff2aa5618a39c4b2277b2 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Fri, 28 Jun 2024 20:38:32 +0530 Subject: [PATCH 4/5] chore: better test name --- .../doctype/unreconcile_payment/test_unreconcile_payment.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/unreconcile_payment/test_unreconcile_payment.py b/erpnext/accounts/doctype/unreconcile_payment/test_unreconcile_payment.py index 882dd1d6dab..43dfbfaef60 100644 --- a/erpnext/accounts/doctype/unreconcile_payment/test_unreconcile_payment.py +++ b/erpnext/accounts/doctype/unreconcile_payment/test_unreconcile_payment.py @@ -107,7 +107,7 @@ class TestUnreconcilePayment(AccountsTestMixin, FrappeTestCase): self.assertEqual(len(pe.references), 1) self.assertEqual(pe.unallocated_amount, 100) - def test_02_unreconcile_one_payment_from_multi_payments(self): + def test_02_unreconcile_one_payment_among_multi_payments(self): """ Scenario: 2 payments, both split against 2 different invoices Unreconcile only one payment from one invoice From 9ec6aef95d99c148731404f2f6b9e323fd2b8b16 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Fri, 28 Jun 2024 20:48:22 +0530 Subject: [PATCH 5/5] refactor: handle purchase invoice as reference --- erpnext/accounts/doctype/payment_entry/payment_entry.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 6bad8ffc35a..ce76f560088 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -1312,6 +1312,9 @@ class PaymentEntry(AccountsController): if reference.reference_doctype == "Sales Invoice": return "credit", reference.account + if reference.reference_doctype == "Purchase Invoice": + return "debit", reference.account + if reference.reference_doctype == "Payment Entry": # reference.account_type and reference.payment_type is only available for Reverse payments if reference.account_type == "Receivable" and reference.payment_type == "Pay":