From 5b446d45750c45c6a86bb420c689a06945ab4974 Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Mon, 13 Nov 2023 15:05:34 +0530 Subject: [PATCH 1/4] fix: handle partial return against invoices --- erpnext/accounts/doctype/payment_entry/payment_entry.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index fc22f53f4d..2634972800 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -1074,9 +1074,7 @@ class PaymentEntry(AccountsController): reverse_dr_or_cr, standalone_note = 0, 0 if d.reference_doctype in ["Sales Invoice", "Purchase Invoice"]: - is_return, return_against = frappe.db.get_value( - d.reference_doctype, d.reference_name, ["is_return", "return_against"] - ) + is_return = frappe.db.get_value(d.reference_doctype, d.reference_name, "is_return") payable_party_types = get_party_types_from_account_type("Payable") receivable_party_types = get_party_types_from_account_type("Receivable") if is_return and self.party_type in receivable_party_types and (self.payment_type == "Pay"): @@ -1086,7 +1084,7 @@ class PaymentEntry(AccountsController): ): reverse_dr_or_cr = 1 - if is_return and not return_against and not reverse_dr_or_cr: + if is_return and not reverse_dr_or_cr: dr_or_cr = "debit" if dr_or_cr == "credit" else "credit" gle.update( From a59c942cd487e0440625663034760eb89171e2ef Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Mon, 13 Nov 2023 18:09:49 +0530 Subject: [PATCH 2/4] fix: reset dr_or_cr for every reference --- erpnext/accounts/doctype/payment_entry/payment_entry.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 2634972800..448224b3a7 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -1054,9 +1054,9 @@ class PaymentEntry(AccountsController): item=self, ) - dr_or_cr = "credit" if self.payment_type == "Receive" else "debit" - for d in self.get("references"): + # re-defining dr_or_cr for every reference in order to avoid the last value affecting calculation of reverse + dr_or_cr = "credit" if self.payment_type == "Receive" else "debit" cost_center = self.cost_center if d.reference_doctype == "Sales Invoice" and not cost_center: cost_center = frappe.db.get_value(d.reference_doctype, d.reference_name, "cost_center") @@ -1072,7 +1072,7 @@ class PaymentEntry(AccountsController): against_voucher_type = d.reference_doctype against_voucher = d.reference_name - reverse_dr_or_cr, standalone_note = 0, 0 + reverse_dr_or_cr = 0 if d.reference_doctype in ["Sales Invoice", "Purchase Invoice"]: is_return = frappe.db.get_value(d.reference_doctype, d.reference_name, "is_return") payable_party_types = get_party_types_from_account_type("Payable") @@ -1098,6 +1098,7 @@ class PaymentEntry(AccountsController): ) gl_entries.append(gle) + dr_or_cr = "credit" if self.payment_type == "Receive" else "debit" if self.unallocated_amount: exchange_rate = self.get_exchange_rate() base_unallocated_amount = self.unallocated_amount * exchange_rate From 09f9764bbdec90d1b9f85ccc5a368bf85547bea1 Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Mon, 13 Nov 2023 18:11:00 +0530 Subject: [PATCH 3/4] test: payment against partial return invoices --- .../payment_entry/test_payment_entry.py | 54 ++++++++++++++++++- 1 file changed, 52 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py index 603f24a09c..5af37b6a5e 100644 --- a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py @@ -1290,6 +1290,9 @@ class TestPaymentEntry(FrappeTestCase): self.assertEqual(references[2].payment_term, "Tax Receivable") def test_receive_payment_from_payable_party_type(self): + """ + Checks GL entries generated while receiving payments from a Payable Party Type. + """ pe = create_payment_entry( party_type="Supplier", party="_Test Supplier", @@ -1301,8 +1304,55 @@ class TestPaymentEntry(FrappeTestCase): ) self.voucher_no = pe.name self.expected_gle = [ - {"account": "_Test Cash - _TC", "debit": 1000.0, "credit": 0.0}, {"account": "Creditors - _TC", "debit": 0.0, "credit": 1000.0}, + {"account": "_Test Cash - _TC", "debit": 1000.0, "credit": 0.0}, + ] + self.check_gl_entries() + + def test_payment_against_partial_return_invoice(self): + """ + Checks GL entries generated for partial return invoice payments. + """ + si = create_sales_invoice(qty=10, rate=10, customer="_Test Customer") + credit_note = create_sales_invoice( + qty=-4, rate=10, customer="_Test Customer", is_return=1, return_against=si.name + ) + pe = create_payment_entry( + party_type="Customer", + party="_Test Customer", + payment_type="Receive", + paid_from="Debtors - _TC", + paid_to="_Test Cash - _TC", + ) + pe.set( + "references", + [ + { + "reference_doctype": "Sales Invoice", + "reference_name": si.name, + "due_date": si.get("due_date"), + "total_amount": si.grand_total, + "outstanding_amount": si.outstanding_amount, + "allocated_amount": si.outstanding_amount, + }, + { + "reference_doctype": "Sales Invoice", + "reference_name": credit_note.name, + "due_date": credit_note.get("due_date"), + "total_amount": credit_note.grand_total, + "outstanding_amount": credit_note.outstanding_amount, + "allocated_amount": credit_note.outstanding_amount, + }, + ], + ) + pe.save() + pe.submit() + self.voucher_no = pe.name + self.expected_gle = [ + {"account": "Debtors - _TC", "debit": 40.0, "credit": 0.0}, + {"account": "Debtors - _TC", "debit": 0.0, "credit": 940.0}, + {"account": "Debtors - _TC", "debit": 0.0, "credit": 100.0}, + {"account": "_Test Cash - _TC", "debit": 1000.0, "credit": 0.0}, ] self.check_gl_entries() @@ -1316,7 +1366,7 @@ class TestPaymentEntry(FrappeTestCase): gle.credit, ) .where((gle.voucher_no == self.voucher_no) & (gle.is_cancelled == 0)) - .orderby(gle.account) + .orderby(gle.account, gle.debit, gle.credit, order=frappe.qb.desc) ).run(as_dict=True) for row in range(len(self.expected_gle)): for field in ["account", "debit", "credit"]: From 2499675ad121ab370f672e6f9ffdcda3c8c76b8e Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Wed, 15 Nov 2023 13:32:23 +0530 Subject: [PATCH 4/4] fix: test total unallocated amount in payment --- erpnext/accounts/doctype/payment_entry/test_payment_entry.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py index 5af37b6a5e..f4b0c55313 100644 --- a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py @@ -1347,6 +1347,8 @@ class TestPaymentEntry(FrappeTestCase): ) pe.save() pe.submit() + self.assertEqual(pe.total_allocated_amount, 60) + self.assertEqual(pe.unallocated_amount, 940) self.voucher_no = pe.name self.expected_gle = [ {"account": "Debtors - _TC", "debit": 40.0, "credit": 0.0},