Merge pull request #37289 from GursheenK/ar-ageing-summary
fix: ageing summary in SOA AR
diff --git a/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py
index 52ae951..6c959ba 100644
--- a/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py
+++ b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py
@@ -48,6 +48,20 @@
def get_report_pdf(doc, consolidated=True):
+ statement_dict = get_statement_dict(doc)
+ if not bool(statement_dict):
+ return False
+ elif consolidated:
+ delimiter = '<div style="page-break-before: always;"></div>' if doc.include_break else ""
+ result = delimiter.join(list(statement_dict.values()))
+ return get_pdf(result, {"orientation": doc.orientation})
+ else:
+ for customer, statement_html in statement_dict.items():
+ statement_dict[customer] = get_pdf(statement_html, {"orientation": doc.orientation})
+ return statement_dict
+
+
+def get_statement_dict(doc, get_statement_dict=False):
statement_dict = {}
ageing = ""
@@ -78,18 +92,11 @@
if not res:
continue
- statement_dict[entry.customer] = get_html(doc, filters, entry, col, res, ageing)
+ statement_dict[entry.customer] = (
+ [res, ageing] if get_statement_dict else get_html(doc, filters, entry, col, res, ageing)
+ )
- if not bool(statement_dict):
- return False
- elif consolidated:
- delimiter = '<div style="page-break-before: always;"></div>' if doc.include_break else ""
- result = delimiter.join(list(statement_dict.values()))
- return get_pdf(result, {"orientation": doc.orientation})
- else:
- for customer, statement_html in statement_dict.items():
- statement_dict[customer] = get_pdf(statement_html, {"orientation": doc.orientation})
- return statement_dict
+ return statement_dict
def set_ageing(doc, entry):
@@ -102,7 +109,8 @@
"range2": 60,
"range3": 90,
"range4": 120,
- "customer": entry.customer,
+ "party_type": "Customer",
+ "party": [entry.customer],
}
)
col1, ageing = get_ageing(ageing_filters)
diff --git a/erpnext/accounts/doctype/process_statement_of_accounts/test_process_statement_of_accounts.py b/erpnext/accounts/doctype/process_statement_of_accounts/test_process_statement_of_accounts.py
index fb0d8d1..a3a74df 100644
--- a/erpnext/accounts/doctype/process_statement_of_accounts/test_process_statement_of_accounts.py
+++ b/erpnext/accounts/doctype/process_statement_of_accounts/test_process_statement_of_accounts.py
@@ -4,39 +4,107 @@
import unittest
import frappe
+from frappe.tests.utils import FrappeTestCase
from frappe.utils import add_days, getdate, today
from erpnext.accounts.doctype.process_statement_of_accounts.process_statement_of_accounts import (
+ get_statement_dict,
send_emails,
)
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
+from erpnext.accounts.test.accounts_mixin import AccountsTestMixin
-class TestProcessStatementOfAccounts(unittest.TestCase):
+class TestProcessStatementOfAccounts(AccountsTestMixin, FrappeTestCase):
def setUp(self):
+ self.create_company()
+ self.create_customer()
+ self.create_customer(customer_name="Other Customer")
+ self.clear_old_entries()
self.si = create_sales_invoice()
- self.process_soa = create_process_soa()
+ create_sales_invoice(customer="Other Customer")
+
+ def test_process_soa_for_gl(self):
+ """Tests the utils for Statement of Accounts(General Ledger)"""
+ process_soa = create_process_soa(
+ name="_Test Process SOA for GL",
+ customers=[{"customer": "_Test Customer"}, {"customer": "Other Customer"}],
+ )
+ statement_dict = get_statement_dict(process_soa, get_statement_dict=True)
+
+ # Checks if the statements are filtered based on the Customer
+ self.assertIn("Other Customer", statement_dict)
+ self.assertIn("_Test Customer", statement_dict)
+
+ # Checks if the correct number of receivable entries exist
+ # 3 rows for opening and closing and 1 row for SI
+ receivable_entries = statement_dict["_Test Customer"][0]
+ self.assertEqual(len(receivable_entries), 4)
+
+ # Checks the amount for the receivable entry
+ self.assertEqual(receivable_entries[1].voucher_no, self.si.name)
+ self.assertEqual(receivable_entries[1].balance, 100)
+
+ def test_process_soa_for_ar(self):
+ """Tests the utils for Statement of Accounts(Accounts Receivable)"""
+ process_soa = create_process_soa(name="_Test Process SOA for AR", report="Accounts Receivable")
+ statement_dict = get_statement_dict(process_soa, get_statement_dict=True)
+
+ # Checks if the statements are filtered based on the Customer
+ self.assertNotIn("Other Customer", statement_dict)
+ self.assertIn("_Test Customer", statement_dict)
+
+ # Checks if the correct number of receivable entries exist
+ receivable_entries = statement_dict["_Test Customer"][0]
+ self.assertEqual(len(receivable_entries), 1)
+
+ # Checks the amount for the receivable entry
+ self.assertEqual(receivable_entries[0].voucher_no, self.si.name)
+ self.assertEqual(receivable_entries[0].total_due, 100)
+
+ # Checks the ageing summary for AR
+ ageing_summary = statement_dict["_Test Customer"][1][0]
+ expected_summary = frappe._dict(
+ range1=100,
+ range2=0,
+ range3=0,
+ range4=0,
+ range5=0,
+ )
+ self.check_ageing_summary(ageing_summary, expected_summary)
def test_auto_email_for_process_soa_ar(self):
- send_emails(self.process_soa.name, from_scheduler=True)
- self.process_soa.load_from_db()
- self.assertEqual(self.process_soa.posting_date, getdate(add_days(today(), 7)))
+ process_soa = create_process_soa(
+ name="_Test Process SOA", enable_auto_email=1, report="Accounts Receivable"
+ )
+ send_emails(process_soa.name, from_scheduler=True)
+ process_soa.load_from_db()
+ self.assertEqual(process_soa.posting_date, getdate(add_days(today(), 7)))
+
+ def check_ageing_summary(self, ageing, expected_ageing):
+ for age_range in expected_ageing:
+ self.assertEqual(expected_ageing[age_range], ageing.get(age_range))
def tearDown(self):
- frappe.delete_doc_if_exists("Process Statement Of Accounts", "Test Process SOA")
+ frappe.db.rollback()
-def create_process_soa():
- frappe.delete_doc_if_exists("Process Statement Of Accounts", "Test Process SOA")
+def create_process_soa(**args):
+ args = frappe._dict(args)
+ frappe.delete_doc_if_exists("Process Statement Of Accounts", args.name)
process_soa = frappe.new_doc("Process Statement Of Accounts")
- soa_dict = {
- "name": "Test Process SOA",
- "company": "_Test Company",
- }
+ soa_dict = frappe._dict(
+ name=args.name,
+ company=args.company or "_Test Company",
+ customers=args.customers or [{"customer": "_Test Customer"}],
+ enable_auto_email=1 if args.enable_auto_email else 0,
+ frequency=args.frequency or "Weekly",
+ report=args.report or "General Ledger",
+ from_date=args.from_date or getdate(today()),
+ to_date=args.to_date or getdate(today()),
+ posting_date=args.posting_date or getdate(today()),
+ include_ageing=1,
+ )
process_soa.update(soa_dict)
- process_soa.set("customers", [{"customer": "_Test Customer"}])
- process_soa.enable_auto_email = 1
- process_soa.frequency = "Weekly"
- process_soa.report = "Accounts Receivable"
process_soa.save()
return process_soa