From 97794b7ded1ec3a8e2b52190609332312170c15a Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Fri, 3 Jul 2026 11:41:21 +0530 Subject: [PATCH 1/3] test: add coverage for Process Payment Reconciliation --- .../test_process_payment_reconciliation.py | 61 +++++++++++++++++-- 1 file changed, 57 insertions(+), 4 deletions(-) diff --git a/erpnext/accounts/doctype/process_payment_reconciliation/test_process_payment_reconciliation.py b/erpnext/accounts/doctype/process_payment_reconciliation/test_process_payment_reconciliation.py index eff49ecadc5..659c8ab86c5 100644 --- a/erpnext/accounts/doctype/process_payment_reconciliation/test_process_payment_reconciliation.py +++ b/erpnext/accounts/doctype/process_payment_reconciliation/test_process_payment_reconciliation.py @@ -1,11 +1,64 @@ -# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and Contributors +# Copyright (c) 2026, Frappe Technologies Pvt. Ltd. and Contributors # See license.txt -# import frappe - +import frappe +from erpnext.accounts.doctype.process_payment_reconciliation.process_payment_reconciliation import ( + get_pr_instance, +) from erpnext.tests.utils import ERPNextTestSuite +COMPANY = "_Test Company" + class TestProcessPaymentReconciliation(ERPNextTestSuite): - pass + """Process Payment Reconciliation validates its accounts against the company, + moves to Queued on submit, and hands its filters to a Payment Reconciliation run.""" + + def setUp(self): + frappe.set_user("Administrator") + + def make_ppr(self, **args): + args = frappe._dict(args) + doc = frappe.new_doc("Process Payment Reconciliation") + doc.company = COMPANY + doc.party_type = "Customer" + doc.party = "_Test Customer" + doc.receivable_payable_account = args.get("receivable_payable_account", "Debtors - _TC") + doc.bank_cash_account = args.get("bank_cash_account") + doc.from_invoice_date = args.get("from_invoice_date") + doc.to_invoice_date = args.get("to_invoice_date") + return doc + + def test_receivable_account_must_belong_to_company(self): + other = frappe.get_all( + "Account", + {"company": "_Test Company 1", "account_type": "Receivable", "is_group": 0}, + pluck="name", + )[0] + doc = self.make_ppr(receivable_payable_account=other) + self.assertRaises(frappe.ValidationError, doc.insert) + + def test_bank_cash_account_must_belong_to_company(self): + other = frappe.get_all("Account", {"company": "_Test Company 1", "is_group": 0}, pluck="name")[0] + doc = self.make_ppr(bank_cash_account=other) + self.assertRaises(frappe.ValidationError, doc.insert) + + def test_submit_sets_status_to_queued(self): + doc = self.make_ppr() + doc.insert() + doc.submit() + self.assertEqual(doc.status, "Queued") + + def test_get_pr_instance_copies_filters_and_caps_limits(self): + doc = self.make_ppr(from_invoice_date="2026-01-01", to_invoice_date="2026-06-30") + doc.insert() + + pr = get_pr_instance(doc.name) + self.assertEqual(pr.company, COMPANY) + self.assertEqual(pr.party, "_Test Customer") + self.assertEqual(pr.receivable_payable_account, "Debtors - _TC") + self.assertEqual(str(pr.from_invoice_date), "2026-01-01") + # the tool run is capped so a single process can't fetch unbounded rows + self.assertEqual(pr.invoice_limit, 1000) + self.assertEqual(pr.payment_limit, 1000) From 974571aba7e84225ccb3a2ce3c457d6fcc09f231 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Fri, 3 Jul 2026 12:08:11 +0530 Subject: [PATCH 2/3] test: guard account lookups and cover dropped pr_instance filters --- .../test_process_payment_reconciliation.py | 28 +++++++++++++------ 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/erpnext/accounts/doctype/process_payment_reconciliation/test_process_payment_reconciliation.py b/erpnext/accounts/doctype/process_payment_reconciliation/test_process_payment_reconciliation.py index 659c8ab86c5..2950677ae75 100644 --- a/erpnext/accounts/doctype/process_payment_reconciliation/test_process_payment_reconciliation.py +++ b/erpnext/accounts/doctype/process_payment_reconciliation/test_process_payment_reconciliation.py @@ -30,18 +30,18 @@ class TestProcessPaymentReconciliation(ERPNextTestSuite): doc.to_invoice_date = args.get("to_invoice_date") return doc + def other_company_account(self, **extra): + filters = {"company": "_Test Company 1", "is_group": 0, **extra} + account = frappe.db.get_value("Account", filters, "name") + self.assertTrue(account, "need a matching account in _Test Company 1") + return account + def test_receivable_account_must_belong_to_company(self): - other = frappe.get_all( - "Account", - {"company": "_Test Company 1", "account_type": "Receivable", "is_group": 0}, - pluck="name", - )[0] - doc = self.make_ppr(receivable_payable_account=other) + doc = self.make_ppr(receivable_payable_account=self.other_company_account(account_type="Receivable")) self.assertRaises(frappe.ValidationError, doc.insert) def test_bank_cash_account_must_belong_to_company(self): - other = frappe.get_all("Account", {"company": "_Test Company 1", "is_group": 0}, pluck="name")[0] - doc = self.make_ppr(bank_cash_account=other) + doc = self.make_ppr(bank_cash_account=self.other_company_account()) self.assertRaises(frappe.ValidationError, doc.insert) def test_submit_sets_status_to_queued(self): @@ -62,3 +62,15 @@ class TestProcessPaymentReconciliation(ERPNextTestSuite): # the tool run is capped so a single process can't fetch unbounded rows self.assertEqual(pr.invoice_limit, 1000) self.assertEqual(pr.payment_limit, 1000) + + def test_get_pr_instance_drops_bank_cash_and_cost_center_filters(self): + # SUSPECTED BUG: get_pr_instance's field list omits bank_cash_account and + # cost_center, so those filters are silently lost when the tool run is built. + # Locking the current (wrong) behaviour. + doc = self.make_ppr(bank_cash_account="Cash - _TC") + doc.cost_center = "_Test Cost Center - _TC" + doc.insert() + + pr = get_pr_instance(doc.name) + self.assertFalse(pr.get("bank_cash_account")) + self.assertFalse(pr.get("cost_center")) From c9960b4d51804b7b3d7590812e304bcd0bed9385 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Fri, 3 Jul 2026 14:16:46 +0530 Subject: [PATCH 3/3] fix: carry bank/cash account and cost center into Payment Reconciliation --- .../process_payment_reconciliation.py | 2 ++ .../test_process_payment_reconciliation.py | 9 +++------ 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/erpnext/accounts/doctype/process_payment_reconciliation/process_payment_reconciliation.py b/erpnext/accounts/doctype/process_payment_reconciliation/process_payment_reconciliation.py index 21ac42a5d3a..9c843f21486 100644 --- a/erpnext/accounts/doctype/process_payment_reconciliation/process_payment_reconciliation.py +++ b/erpnext/accounts/doctype/process_payment_reconciliation/process_payment_reconciliation.py @@ -106,6 +106,8 @@ def get_pr_instance(doc: str): "party", "receivable_payable_account", "default_advance_account", + "bank_cash_account", + "cost_center", "from_invoice_date", "to_invoice_date", "from_payment_date", diff --git a/erpnext/accounts/doctype/process_payment_reconciliation/test_process_payment_reconciliation.py b/erpnext/accounts/doctype/process_payment_reconciliation/test_process_payment_reconciliation.py index 2950677ae75..ccdaca2da1c 100644 --- a/erpnext/accounts/doctype/process_payment_reconciliation/test_process_payment_reconciliation.py +++ b/erpnext/accounts/doctype/process_payment_reconciliation/test_process_payment_reconciliation.py @@ -63,14 +63,11 @@ class TestProcessPaymentReconciliation(ERPNextTestSuite): self.assertEqual(pr.invoice_limit, 1000) self.assertEqual(pr.payment_limit, 1000) - def test_get_pr_instance_drops_bank_cash_and_cost_center_filters(self): - # SUSPECTED BUG: get_pr_instance's field list omits bank_cash_account and - # cost_center, so those filters are silently lost when the tool run is built. - # Locking the current (wrong) behaviour. + def test_get_pr_instance_copies_bank_cash_and_cost_center(self): doc = self.make_ppr(bank_cash_account="Cash - _TC") doc.cost_center = "_Test Cost Center - _TC" doc.insert() pr = get_pr_instance(doc.name) - self.assertFalse(pr.get("bank_cash_account")) - self.assertFalse(pr.get("cost_center")) + self.assertEqual(pr.bank_cash_account, "Cash - _TC") + self.assertEqual(pr.cost_center, "_Test Cost Center - _TC")