diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index c031be53d65..87fb8c16263 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -526,9 +526,9 @@ class PaymentEntry(AccountsController): def get_valid_reference_doctypes(self): if self.party_type == "Customer": - return ("Sales Order", "Sales Invoice", "Journal Entry", "Dunning") + return ("Sales Order", "Sales Invoice", "Journal Entry", "Dunning", "Payment Entry") elif self.party_type == "Supplier": - return ("Purchase Order", "Purchase Invoice", "Journal Entry") + return ("Purchase Order", "Purchase Invoice", "Journal Entry", "Payment Entry") elif self.party_type == "Shareholder": return ("Journal Entry",) elif self.party_type == "Employee": @@ -1191,6 +1191,7 @@ class PaymentEntry(AccountsController): "Journal Entry", "Sales Order", "Purchase Order", + "Payment Entry", ): self.add_advance_gl_for_reference(gl_entries, ref) @@ -1213,7 +1214,9 @@ class PaymentEntry(AccountsController): if getdate(posting_date) < getdate(self.posting_date): posting_date = self.posting_date - dr_or_cr = "credit" if invoice.reference_doctype in ["Sales Invoice", "Sales Order"] else "debit" + dr_or_cr = ( + "credit" if invoice.reference_doctype in ["Sales Invoice", "Payment Entry"] else "debit" + ) args_dict["account"] = invoice.account args_dict[dr_or_cr] = invoice.allocated_amount args_dict[dr_or_cr + "_in_account_currency"] = invoice.allocated_amount @@ -1660,7 +1663,7 @@ def get_outstanding_reference_documents(args, validate=False): outstanding_invoices = get_outstanding_invoices( args.get("party_type"), args.get("party"), - party_account, + [party_account], common_filter=common_filter, posting_date=posting_and_due_date, min_outstanding=args.get("outstanding_amt_greater_than"), diff --git a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py index 04066666c9e..2b226e1b241 100644 --- a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py @@ -1514,6 +1514,168 @@ class TestPaymentEntry(FrappeTestCase): self.assertEqual(references[1].payment_term, "Basic Amount Receivable") self.assertEqual(references[2].payment_term, "Tax Receivable") + def test_reverse_payment_reconciliation(self): + customer = create_customer(frappe.generate_hash(length=10), "INR") + pe = create_payment_entry( + party_type="Customer", + party=customer, + payment_type="Receive", + paid_from="Debtors - _TC", + paid_to="_Test Cash - _TC", + ) + pe.submit() + + reverse_pe = create_payment_entry( + party_type="Customer", + party=customer, + payment_type="Pay", + paid_from="_Test Cash - _TC", + paid_to="Debtors - _TC", + ) + reverse_pe.submit() + + pr = frappe.get_doc("Payment Reconciliation") + pr.company = "_Test Company" + pr.party_type = "Customer" + pr.party = customer + pr.receivable_payable_account = "Debtors - _TC" + pr.get_unreconciled_entries() + self.assertEqual(len(pr.invoices), 1) + self.assertEqual(len(pr.payments), 1) + + self.assertEqual(reverse_pe.name, pr.invoices[0].invoice_number) + self.assertEqual(pe.name, pr.payments[0].reference_name) + + invoices = [x.as_dict() for x in pr.invoices] + payments = [pr.payments[0].as_dict()] + pr.allocate_entries(frappe._dict({"invoices": invoices, "payments": payments})) + pr.reconcile() + self.assertEqual(len(pr.invoices), 0) + self.assertEqual(len(pr.payments), 0) + + def test_advance_reverse_payment_reconciliation(self): + from erpnext.accounts.doctype.account.test_account import create_account + + company = "_Test Company" + customer = create_customer(frappe.generate_hash(length=10), "INR") + advance_account = create_account( + parent_account="Current Assets - _TC", + account_name="Advances Received", + company=company, + account_type="Receivable", + ) + + frappe.db.set_value( + "Company", + company, + { + "book_advance_payments_in_separate_party_account": 1, + "default_advance_received_account": advance_account, + }, + ) + # Reverse Payment(essentially an Invoice) + reverse_pe = create_payment_entry( + party_type="Customer", + party=customer, + payment_type="Pay", + paid_from="_Test Cash - _TC", + paid_to=advance_account, + ) + reverse_pe.save() # use save() to trigger set_liability_account() + reverse_pe.submit() + + # Advance Payment + pe = create_payment_entry( + party_type="Customer", + party=customer, + payment_type="Receive", + paid_from=advance_account, + paid_to="_Test Cash - _TC", + ) + pe.save() # use save() to trigger set_liability_account() + pe.submit() + + # Partially reconcile advance against invoice + pr = frappe.get_doc("Payment Reconciliation") + pr.company = company + pr.party_type = "Customer" + pr.party = customer + pr.receivable_payable_account = "Debtors - _TC" + pr.default_advance_account = advance_account + pr.get_unreconciled_entries() + + self.assertEqual(len(pr.invoices), 1) + self.assertEqual(len(pr.payments), 1) + + 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})) + pr.allocation[0].allocated_amount = 400 + pr.reconcile() + + # assert General and Payment Ledger entries post partial reconciliation + self.expected_gle = [ + {"account": "Debtors - _TC", "debit": 0.0, "credit": 400.0}, + {"account": advance_account, "debit": 400.0, "credit": 0.0}, + {"account": advance_account, "debit": 0.0, "credit": 1000.0}, + {"account": "_Test Cash - _TC", "debit": 1000.0, "credit": 0.0}, + ] + self.expected_ple = [ + { + "account": advance_account, + "voucher_no": pe.name, + "against_voucher_no": pe.name, + "amount": -1000.0, + }, + { + "account": "Debtors - _TC", + "voucher_no": pe.name, + "against_voucher_no": reverse_pe.name, + "amount": -400.0, + }, + { + "account": advance_account, + "voucher_no": pe.name, + "against_voucher_no": pe.name, + "amount": 400.0, + }, + ] + self.voucher_no = pe.name + self.check_gl_entries() + self.check_pl_entries() + + # Unreconcile + unrecon = ( + frappe.get_doc( + { + "doctype": "Unreconcile Payment", + "company": company, + "voucher_type": pe.doctype, + "voucher_no": pe.name, + "allocations": [{"reference_doctype": reverse_pe.doctype, "reference_name": reverse_pe.name}], + } + ) + .save() + .submit() + ) + + # assert General and Payment Ledger entries post unreconciliation + self.expected_gle = [ + {"account": advance_account, "debit": 0.0, "credit": 1000.0}, + {"account": "_Test Cash - _TC", "debit": 1000.0, "credit": 0.0}, + ] + self.expected_ple = [ + { + "account": advance_account, + "voucher_no": pe.name, + "against_voucher_no": pe.name, + "amount": -1000.0, + }, + ] + self.voucher_no = pe.name + self.check_gl_entries() + self.check_pl_entries() + def create_payment_entry(**args): payment_entry = frappe.new_doc("Payment Entry") diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py index 1bf1acee70d..2c4952a0c66 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py @@ -340,10 +340,15 @@ class PaymentReconciliation(Document): self.build_qb_filter_conditions(get_invoices=True) + accounts = [self.receivable_payable_account] + + if self.default_advance_account: + accounts.append(self.default_advance_account) + non_reconciled_invoices = get_outstanding_invoices( self.party_type, self.party, - self.receivable_payable_account, + accounts, common_filter=self.common_filter_conditions, posting_date=self.ple_posting_date_filter, min_outstanding=self.minimum_invoice_amount if self.minimum_invoice_amount else None, diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index 558eeaa6d35..0e6c041d24d 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -1015,7 +1015,7 @@ def get_outstanding_invoices( if account: root_type, account_type = frappe.get_cached_value( - "Account", account, ["root_type", "account_type"] + "Account", account[0], ["root_type", "account_type"] ) party_account_type = "Receivable" if root_type == "Asset" else "Payable" party_account_type = account_type or party_account_type @@ -1026,7 +1026,7 @@ def get_outstanding_invoices( common_filter = common_filter or [] common_filter.append(ple.account_type == party_account_type) - common_filter.append(ple.account == account) + common_filter.append(ple.account.isin(account)) common_filter.append(ple.party_type == party_type) common_filter.append(ple.party == party)