Merge pull request #36727 from ruthra-kumar/fix_broken_advance_field_in_ar_summary_rpt

fix: broken advance field in Accounts Receivable summary rpt
diff --git a/erpnext/accounts/party.py b/erpnext/accounts/party.py
index 0d67752..8bd7b5a 100644
--- a/erpnext/accounts/party.py
+++ b/erpnext/accounts/party.py
@@ -14,7 +14,7 @@
 from frappe.contacts.doctype.contact.contact import get_contact_details
 from frappe.core.doctype.user_permission.user_permission import get_permitted_documents
 from frappe.model.utils import get_fetch_values
-from frappe.query_builder.functions import Date, Sum
+from frappe.query_builder.functions import Abs, Date, Sum
 from frappe.utils import (
 	add_days,
 	add_months,
@@ -922,35 +922,34 @@
 
 
 def get_partywise_advanced_payment_amount(
-	party_type, posting_date=None, future_payment=0, company=None, party=None, account_type=None
+	party_type, posting_date=None, future_payment=0, company=None, party=None
 ):
-	gle = frappe.qb.DocType("GL Entry")
+	ple = frappe.qb.DocType("Payment Ledger Entry")
 	query = (
-		frappe.qb.from_(gle)
-		.select(gle.party)
+		frappe.qb.from_(ple)
+		.select(ple.party, Abs(Sum(ple.amount).as_("amount")))
 		.where(
-			(gle.party_type.isin(party_type)) & (gle.against_voucher.isnull()) & (gle.is_cancelled == 0)
+			(ple.party_type.isin(party_type))
+			& (ple.amount < 0)
+			& (ple.against_voucher_no == ple.voucher_no)
+			& (ple.delinked == 0)
 		)
-		.groupby(gle.party)
+		.groupby(ple.party)
 	)
-	if account_type == "Receivable":
-		query = query.select(Sum(gle.credit).as_("amount"))
-	else:
-		query = query.select(Sum(gle.debit).as_("amount"))
 
 	if posting_date:
 		if future_payment:
-			query = query.where((gle.posting_date <= posting_date) | (Date(gle.creation) <= posting_date))
+			query = query.where((ple.posting_date <= posting_date) | (Date(ple.creation) <= posting_date))
 		else:
-			query = query.where(gle.posting_date <= posting_date)
+			query = query.where(ple.posting_date <= posting_date)
 
 	if company:
-		query = query.where(gle.company == company)
+		query = query.where(ple.company == company)
 
 	if party:
-		query = query.where(gle.party == party)
+		query = query.where(ple.party == party)
 
-	data = query.run(as_dict=True)
+	data = query.run()
 	if data:
 		return frappe._dict(data)
 
diff --git a/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py b/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py
index da4c9da..3675e80 100644
--- a/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py
+++ b/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py
@@ -50,7 +50,6 @@
 				self.filters.show_future_payments,
 				self.filters.company,
 				party=party,
-				account_type=self.account_type,
 			)
 			or {}
 		)
diff --git a/erpnext/accounts/report/accounts_receivable_summary/test_accounts_receivable_summary.py b/erpnext/accounts/report/accounts_receivable_summary/test_accounts_receivable_summary.py
new file mode 100644
index 0000000..3ee35a1
--- /dev/null
+++ b/erpnext/accounts/report/accounts_receivable_summary/test_accounts_receivable_summary.py
@@ -0,0 +1,203 @@
+import unittest
+
+import frappe
+from frappe.tests.utils import FrappeTestCase, change_settings
+from frappe.utils import today
+
+from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry
+from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
+from erpnext.accounts.report.accounts_receivable_summary.accounts_receivable_summary import execute
+from erpnext.accounts.test.accounts_mixin import AccountsTestMixin
+
+
+class TestAccountsReceivable(AccountsTestMixin, FrappeTestCase):
+	def setUp(self):
+		self.maxDiff = None
+		self.create_company()
+		self.create_customer()
+		self.create_item()
+		self.clear_old_entries()
+
+	def tearDown(self):
+		frappe.db.rollback()
+
+	def test_01_receivable_summary_output(self):
+		"""
+		Test for Invoices, Paid, Advance and Outstanding
+		"""
+		filters = {
+			"company": self.company,
+			"customer": self.customer,
+			"posting_date": today(),
+			"range1": 30,
+			"range2": 60,
+			"range3": 90,
+			"range4": 120,
+		}
+
+		si = create_sales_invoice(
+			item=self.item,
+			company=self.company,
+			customer=self.customer,
+			debit_to=self.debit_to,
+			posting_date=today(),
+			parent_cost_center=self.cost_center,
+			cost_center=self.cost_center,
+			rate=200,
+			price_list_rate=200,
+		)
+
+		customer_group, customer_territory = frappe.db.get_all(
+			"Customer",
+			filters={"name": self.customer},
+			fields=["customer_group", "territory"],
+			as_list=True,
+		)[0]
+
+		report = execute(filters)
+		rpt_output = report[1]
+		expected_data = {
+			"party_type": "Customer",
+			"advance": 0,
+			"party": self.customer,
+			"invoiced": 200.0,
+			"paid": 0.0,
+			"credit_note": 0.0,
+			"outstanding": 200.0,
+			"range1": 200.0,
+			"range2": 0.0,
+			"range3": 0.0,
+			"range4": 0.0,
+			"range5": 0.0,
+			"total_due": 200.0,
+			"future_amount": 0.0,
+			"sales_person": [],
+			"currency": si.currency,
+			"territory": customer_territory,
+			"customer_group": customer_group,
+		}
+
+		self.assertEqual(len(rpt_output), 1)
+		self.assertDictEqual(rpt_output[0], expected_data)
+
+		# simulate advance payment
+		pe = get_payment_entry(si.doctype, si.name)
+		pe.paid_amount = 50
+		pe.references[0].allocated_amount = 0  # this essitially removes the reference
+		pe.save().submit()
+
+		# update expected data with advance
+		expected_data.update(
+			{
+				"advance": 50.0,
+				"outstanding": 150.0,
+				"range1": 150.0,
+				"total_due": 150.0,
+			}
+		)
+
+		report = execute(filters)
+		rpt_output = report[1]
+		self.assertEqual(len(rpt_output), 1)
+		self.assertDictEqual(rpt_output[0], expected_data)
+
+		# make partial payment
+		pe = get_payment_entry(si.doctype, si.name)
+		pe.paid_amount = 125
+		pe.references[0].allocated_amount = 125
+		pe.save().submit()
+
+		# update expected data after advance and partial payment
+		expected_data.update(
+			{"advance": 50.0, "paid": 125.0, "outstanding": 25.0, "range1": 25.0, "total_due": 25.0}
+		)
+
+		report = execute(filters)
+		rpt_output = report[1]
+		self.assertEqual(len(rpt_output), 1)
+		self.assertDictEqual(rpt_output[0], expected_data)
+
+	@change_settings("Selling Settings", {"cust_master_name": "Naming Series"})
+	def test_02_various_filters_and_output(self):
+		filters = {
+			"company": self.company,
+			"customer": self.customer,
+			"posting_date": today(),
+			"range1": 30,
+			"range2": 60,
+			"range3": 90,
+			"range4": 120,
+		}
+
+		si = create_sales_invoice(
+			item=self.item,
+			company=self.company,
+			customer=self.customer,
+			debit_to=self.debit_to,
+			posting_date=today(),
+			parent_cost_center=self.cost_center,
+			cost_center=self.cost_center,
+			rate=200,
+			price_list_rate=200,
+		)
+		# make partial payment
+		pe = get_payment_entry(si.doctype, si.name)
+		pe.paid_amount = 150
+		pe.references[0].allocated_amount = 150
+		pe.save().submit()
+
+		customer_group, customer_territory = frappe.db.get_all(
+			"Customer",
+			filters={"name": self.customer},
+			fields=["customer_group", "territory"],
+			as_list=True,
+		)[0]
+
+		report = execute(filters)
+		rpt_output = report[1]
+		expected_data = {
+			"party_type": "Customer",
+			"advance": 0,
+			"party": self.customer,
+			"party_name": self.customer,
+			"invoiced": 200.0,
+			"paid": 150.0,
+			"credit_note": 0.0,
+			"outstanding": 50.0,
+			"range1": 50.0,
+			"range2": 0.0,
+			"range3": 0.0,
+			"range4": 0.0,
+			"range5": 0.0,
+			"total_due": 50.0,
+			"future_amount": 0.0,
+			"sales_person": [],
+			"currency": si.currency,
+			"territory": customer_territory,
+			"customer_group": customer_group,
+		}
+
+		self.assertEqual(len(rpt_output), 1)
+		self.assertDictEqual(rpt_output[0], expected_data)
+
+		# with gl balance filter
+		filters.update({"show_gl_balance": True})
+		expected_data.update({"gl_balance": 50.0, "diff": 0.0})
+		report = execute(filters)
+		rpt_output = report[1]
+		self.assertEqual(len(rpt_output), 1)
+		self.assertDictEqual(rpt_output[0], expected_data)
+
+		# with gl balance and future payments filter
+		filters.update({"show_future_payments": True})
+		expected_data.update({"remaining_balance": 50.0})
+		report = execute(filters)
+		rpt_output = report[1]
+		self.assertEqual(len(rpt_output), 1)
+		self.assertDictEqual(rpt_output[0], expected_data)
+
+		# invoice fully paid
+		pe = get_payment_entry(si.doctype, si.name).save().submit()
+		report = execute(filters)
+		rpt_output = report[1]
+		self.assertEqual(len(rpt_output), 0)
diff --git a/erpnext/accounts/test/accounts_mixin.py b/erpnext/accounts/test/accounts_mixin.py
index 70bbf7e..debfffd 100644
--- a/erpnext/accounts/test/accounts_mixin.py
+++ b/erpnext/accounts/test/accounts_mixin.py
@@ -1,4 +1,5 @@
 import frappe
+from frappe import qb
 
 from erpnext.stock.doctype.item.test_item import create_item
 
@@ -103,3 +104,15 @@
 				)
 				new_acc.save()
 				setattr(self, acc.attribute_name, new_acc.name)
+
+	def clear_old_entries(self):
+		doctype_list = [
+			"GL Entry",
+			"Payment Ledger Entry",
+			"Sales Invoice",
+			"Purchase Invoice",
+			"Payment Entry",
+			"Journal Entry",
+		]
+		for doctype in doctype_list:
+			qb.from_(qb.DocType(doctype)).delete().where(qb.DocType(doctype).company == self.company).run()