From 8c124ed4a908bf6f8a33517bf93d9043e4b660fd Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Tue, 23 Jun 2026 11:04:17 +0530 Subject: [PATCH 1/2] test: cover AR/AP journal-entry payments and credit notes The receivable/payable reports lacked coverage for settling invoices via a Journal Entry (rather than a Payment Entry) and for credit notes raised via JE. Add: an invoice partially and fully paid via JE on the receivable side, a standalone JE credit note showing as negative outstanding, and a supplier invoice partially paid via JE on the payable side. --- .../accounts_payable/test_accounts_payable.py | 41 ++++++++++ .../test_accounts_receivable.py | 77 +++++++++++++++++++ 2 files changed, 118 insertions(+) diff --git a/erpnext/accounts/report/accounts_payable/test_accounts_payable.py b/erpnext/accounts/report/accounts_payable/test_accounts_payable.py index cced8318dec..e1eecbdbb33 100644 --- a/erpnext/accounts/report/accounts_payable/test_accounts_payable.py +++ b/erpnext/accounts/report/accounts_payable/test_accounts_payable.py @@ -54,6 +54,47 @@ class TestAccountsPayable(ERPNextTestSuite, AccountsTestMixin): pi = pi.submit() return pi + def test_invoice_partially_paid_via_journal_entry(self): + pi = self.create_purchase_invoice() # outstanding 300 + + je = frappe.new_doc("Journal Entry") + je.company = self.company + je.posting_date = today() + je.append( + "accounts", + { + "account": "Creditors - _TC", + "party_type": "Supplier", + "party": self.supplier, + "debit": 120, + "debit_in_account_currency": 120, + "reference_type": "Purchase Invoice", + "reference_name": pi.name, + "cost_center": "Main - _TC", + }, + ) + je.append( + "accounts", + { + "account": "Cash - _TC", + "credit": 120, + "credit_in_account_currency": 120, + "cost_center": "Main - _TC", + }, + ) + je.save().submit() + + filters = { + "company": self.company, + "party_type": "Supplier", + "party": [self.supplier], + "report_date": today(), + "range": "30, 60, 90, 120", + } + row = next(row for row in execute(filters)[1] if row.voucher_no == pi.name) + self.assertEqual(row.paid, 120) + self.assertEqual(row.outstanding, 180) + def test_payment_terms_template_filters(self): from erpnext.controllers.accounts_controller import get_payment_terms diff --git a/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py index afd9045e5f0..3276d12dddf 100644 --- a/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py @@ -568,6 +568,83 @@ class TestAccountsReceivable(ERPNextTestSuite, AccountsTestMixin): report = execute(filters) self.assertEqual(report[1], []) + def pay_invoice_via_journal_entry(self, si, amount): + je = frappe.new_doc("Journal Entry") + je.company = self.company + je.posting_date = today() + je.append( + "accounts", + { + "account": self.cash, + "debit": amount, + "debit_in_account_currency": amount, + "cost_center": self.cost_center, + }, + ) + je.append( + "accounts", + { + "account": self.debit_to, + "party_type": "Customer", + "party": self.customer, + "credit": amount, + "credit_in_account_currency": amount, + "reference_type": "Sales Invoice", + "reference_name": si.name, + "cost_center": self.cost_center, + }, + ) + return je.save().submit() + + def ar_rows(self): + filters = {"company": self.company, "report_date": today(), "range": "30, 60, 90, 120"} + return execute(filters)[1] + + def test_invoice_partially_paid_via_journal_entry(self): + si = self.create_sales_invoice(no_payment_schedule=True) # outstanding 100 + self.pay_invoice_via_journal_entry(si, 40) + + row = next(row for row in self.ar_rows() if row.voucher_no == si.name) + self.assertEqual(row.paid, 40) + self.assertEqual(row.outstanding, 60) + + def test_invoice_fully_paid_via_journal_entry(self): + si = self.create_sales_invoice(no_payment_schedule=True) # outstanding 100 + self.pay_invoice_via_journal_entry(si, 100) + + # a fully settled invoice drops out of the receivable report + self.assertEqual([row for row in self.ar_rows() if row.voucher_no == si.name], []) + + def test_credit_note_via_journal_entry_shows_negative_outstanding(self): + je = frappe.new_doc("Journal Entry") + je.company = self.company + je.voucher_type = "Credit Note" + je.posting_date = today() + je.append( + "accounts", + { + "account": self.income_account, + "debit": 100, + "debit_in_account_currency": 100, + "cost_center": self.cost_center, + }, + ) + je.append( + "accounts", + { + "account": self.debit_to, + "party_type": "Customer", + "party": self.customer, + "credit": 100, + "credit_in_account_currency": 100, + "cost_center": self.cost_center, + }, + ) + je = je.save().submit() + + row = next(row for row in self.ar_rows() if row.voucher_no == je.name) + self.assertEqual(row.outstanding, -100) + def test_group_by_party(self): si1 = self.create_sales_invoice(do_not_submit=True) si1.posting_date = add_days(today(), -1) From 824415d50edeb83b5cbd80274d5fca126683cbcd Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Tue, 23 Jun 2026 11:32:31 +0530 Subject: [PATCH 2/2] test: cover AR/AP show-remarks, delivery notes and group-by-party filters Add coverage for previously untested checkbox filters: Show Remarks (the invoice remark appears in the row) and Show Linked Delivery Notes on the receivable report, and Show Remarks and Group By Supplier on the payable report. --- .../accounts_payable/test_accounts_payable.py | 37 +++++++++++++++++++ .../test_accounts_receivable.py | 36 ++++++++++++++++++ 2 files changed, 73 insertions(+) diff --git a/erpnext/accounts/report/accounts_payable/test_accounts_payable.py b/erpnext/accounts/report/accounts_payable/test_accounts_payable.py index e1eecbdbb33..84b28a85817 100644 --- a/erpnext/accounts/report/accounts_payable/test_accounts_payable.py +++ b/erpnext/accounts/report/accounts_payable/test_accounts_payable.py @@ -95,6 +95,43 @@ class TestAccountsPayable(ERPNextTestSuite, AccountsTestMixin): self.assertEqual(row.paid, 120) self.assertEqual(row.outstanding, 180) + def test_show_remarks_includes_invoice_remark(self): + pi = self.create_purchase_invoice(do_not_submit=True) + pi.remarks = "AP test remark" + pi.save().submit() + + filters = { + "company": self.company, + "party_type": "Supplier", + "party": [self.supplier], + "report_date": today(), + "range": "30, 60, 90, 120", + "show_remarks": 1, + } + row = next(row for row in execute(filters)[1] if row.voucher_no == pi.name) + self.assertIn("AP test remark", row.remarks or "") + + def test_group_by_supplier_totals(self): + self.create_purchase_invoice() # outstanding 300 + + filters = { + "company": self.company, + "party_type": "Supplier", + "party": [self.supplier], + "report_date": today(), + "range": "30, 60, 90, 120", + "group_by_party": True, + } + report = execute(filters)[1] + + # a per-supplier subtotal row plus a grand total row + party_subtotal = next( + row for row in report if row.get("party") == self.supplier and not row.get("voucher_no") + ) + grand_total = next(row for row in report if row.get("party") == "Total") + self.assertEqual(party_subtotal.get("invoiced"), 300) + self.assertEqual(grand_total.get("outstanding"), 300) + def test_payment_terms_template_filters(self): from erpnext.controllers.accounts_controller import get_payment_terms diff --git a/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py index 3276d12dddf..1de5aedbeca 100644 --- a/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py @@ -645,6 +645,42 @@ class TestAccountsReceivable(ERPNextTestSuite, AccountsTestMixin): row = next(row for row in self.ar_rows() if row.voucher_no == je.name) self.assertEqual(row.outstanding, -100) + def test_show_remarks_includes_invoice_remark(self): + si = self.create_sales_invoice(no_payment_schedule=True, do_not_submit=True) + si.remarks = "AR test remark" + si.save().submit() + + filters = { + "company": self.company, + "report_date": today(), + "range": "30, 60, 90, 120", + "show_remarks": 1, + } + row = next(row for row in execute(filters)[1] if row.voucher_no == si.name) + self.assertIn("AR test remark", row.remarks or "") + + def test_show_delivery_notes_links_delivery_note(self): + from erpnext.stock.doctype.delivery_note.mapper import make_sales_invoice + from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note + from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry + + make_stock_entry(item_code=self.item, qty=5, to_warehouse=self.warehouse, basic_rate=100) + dn = create_delivery_note( + customer=self.customer, item=self.item, warehouse=self.warehouse, cost_center=self.cost_center + ) + si = make_sales_invoice(dn.name) + si.insert() + si.submit() + + filters = { + "company": self.company, + "report_date": today(), + "range": "30, 60, 90, 120", + "show_delivery_notes": 1, + } + row = next(row for row in execute(filters)[1] if row.voucher_no == si.name) + self.assertIn(dn.name, row.delivery_notes or "") + def test_group_by_party(self): si1 = self.create_sales_invoice(do_not_submit=True) si1.posting_date = add_days(today(), -1)