From ac61abb2e40e5ee09223e41c1f6fec154b290738 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 (cherry picked from commit 5b446d45750c45c6a86bb420c689a06945ab4974) --- 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 c0e3ab3ed4..a280852a97 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 22b39ac4b4d46de38e5142ae2bba3b9ca00245df 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 (cherry picked from commit a59c942cd487e0440625663034760eb89171e2ef) --- 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 a280852a97..025277b40c 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 376e09680ca035355a5f5fd7a1bcb7afe075c8cc 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 (cherry picked from commit 09f9764bbdec90d1b9f85ccc5a368bf85547bea1) --- .../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 b6b93b6b11..8dcead67dd 100644 --- a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py @@ -1250,6 +1250,9 @@ class TestPaymentEntry(FrappeTestCase): self.assertEqual(so.advance_paid, so.rounded_total) 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", @@ -1261,8 +1264,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() @@ -1276,7 +1326,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 cb4bb5b4ee126a24492775f627849ed5d5a3ff6f 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 (cherry picked from commit 2499675ad121ab370f672e6f9ffdcda3c8c76b8e) --- 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 8dcead67dd..13c9f21805 100644 --- a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py @@ -1307,6 +1307,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},