feat: TDS deduction using journal entry and other fixes (#27451)

* fix: TDS deduction using journal entry

* fix: Multi category application against single supplier

* refactor: TDS payable monthly report

* fix: Server side handling for default tax withholding category

* fix: Supplier filter for Journal Entry

* refactor: TDS computation summary report
diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.json b/erpnext/accounts/doctype/journal_entry/journal_entry.json
index b7bbb74..20678d7 100644
--- a/erpnext/accounts/doctype/journal_entry/journal_entry.json
+++ b/erpnext/accounts/doctype/journal_entry/journal_entry.json
@@ -13,10 +13,12 @@
   "voucher_type",
   "naming_series",
   "finance_book",
+  "tax_withholding_category",
   "column_break1",
   "from_template",
   "company",
   "posting_date",
+  "apply_tds",
   "2_add_edit_gl_entries",
   "accounts",
   "section_break99",
@@ -498,16 +500,32 @@
    "options": "Journal Entry Template",
    "print_hide": 1,
    "report_hide": 1
+  },
+  {
+   "depends_on": "eval:doc.apply_tds",
+   "fieldname": "tax_withholding_category",
+   "fieldtype": "Link",
+   "label": "Tax Withholding Category",
+   "mandatory_depends_on": "eval:doc.apply_tds",
+   "options": "Tax Withholding Category"
+  },
+  {
+   "default": "0",
+   "depends_on": "eval:['Credit Note', 'Debit Note'].includes(doc.voucher_type)",
+   "fieldname": "apply_tds",
+   "fieldtype": "Check",
+   "label": "Apply Tax Withholding Amount "
   }
  ],
  "icon": "fa fa-file-text",
  "idx": 176,
  "is_submittable": 1,
  "links": [],
- "modified": "2020-10-30 13:56:01.121995",
+ "modified": "2021-09-09 15:31:14.484029",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Journal Entry",
+ "naming_rule": "By \"Naming Series\" field",
  "owner": "Administrator",
  "permissions": [
   {
diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py
index 24368f0..e568a82 100644
--- a/erpnext/accounts/doctype/journal_entry/journal_entry.py
+++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py
@@ -15,6 +15,9 @@
 from erpnext.accounts.doctype.invoice_discounting.invoice_discounting import (
 	get_party_account_based_on_invoice_discounting,
 )
+from erpnext.accounts.doctype.tax_withholding_category.tax_withholding_category import (
+	get_party_tax_withholding_details,
+)
 from erpnext.accounts.party import get_party_account
 from erpnext.accounts.utils import (
 	check_if_stock_and_account_balance_synced,
@@ -57,7 +60,8 @@
 
 		self.validate_against_jv()
 		self.validate_reference_doc()
-		self.set_against_account()
+		if self.docstatus == 0:
+			self.set_against_account()
 		self.create_remarks()
 		self.set_print_format_fields()
 		self.validate_expense_claim()
@@ -66,6 +70,10 @@
 		self.set_account_and_party_balance()
 		self.validate_inter_company_accounts()
 		self.validate_stock_accounts()
+
+		if self.docstatus == 0:
+			self.apply_tax_withholding()
+
 		if not self.title:
 			self.title = self.get_title()
 
@@ -139,6 +147,72 @@
 				frappe.throw(_("Account: {0} can only be updated via Stock Transactions")
 					.format(account), StockAccountInvalidTransaction)
 
+	def apply_tax_withholding(self):
+		from erpnext.accounts.report.general_ledger.general_ledger import get_account_type_map
+
+		if not self.apply_tds or self.voucher_type not in ('Debit Note', 'Credit Note'):
+			return
+
+		parties = [d.party for d in self.get('accounts') if d.party]
+		parties = list(set(parties))
+
+		if len(parties) > 1:
+			frappe.throw(_("Cannot apply TDS against multiple parties in one entry"))
+
+		account_type_map = get_account_type_map(self.company)
+		party_type = 'supplier' if self.voucher_type == 'Credit Note' else 'customer'
+		doctype = 'Purchase Invoice' if self.voucher_type == 'Credit Note' else 'Sales Invoice'
+		debit_or_credit = 'debit_in_account_currency' if self.voucher_type == 'Credit Note' else 'credit_in_account_currency'
+		rev_debit_or_credit = 'credit_in_account_currency' if debit_or_credit == 'debit_in_account_currency' else 'debit_in_account_currency'
+
+		party_account = get_party_account(party_type.title(), parties[0], self.company)
+
+		net_total = sum(d.get(debit_or_credit) for d in self.get('accounts') if account_type_map.get(d.account)
+			not in ('Tax', 'Chargeable'))
+
+		party_amount = sum(d.get(rev_debit_or_credit) for d in self.get('accounts') if d.account == party_account)
+
+		inv = frappe._dict({
+			party_type: parties[0],
+			'doctype': doctype,
+			'company': self.company,
+			'posting_date': self.posting_date,
+			'net_total': net_total
+		})
+
+		tax_withholding_details = get_party_tax_withholding_details(inv, self.tax_withholding_category)
+
+		if not tax_withholding_details:
+			return
+
+		accounts = []
+		for d in self.get('accounts'):
+			if d.get('account') == tax_withholding_details.get("account_head"):
+				d.update({
+					'account': tax_withholding_details.get("account_head"),
+					debit_or_credit: tax_withholding_details.get('tax_amount')
+				})
+
+			accounts.append(d.get('account'))
+
+			if d.get('account') == party_account:
+				d.update({
+					rev_debit_or_credit: party_amount - tax_withholding_details.get('tax_amount')
+				})
+
+		if not accounts or tax_withholding_details.get("account_head") not in accounts:
+			self.append("accounts", {
+				'account': tax_withholding_details.get("account_head"),
+				rev_debit_or_credit: tax_withholding_details.get('tax_amount'),
+				'against_account': parties[0]
+			})
+
+		to_remove = [d for d in self.get('accounts')
+			if not d.get(rev_debit_or_credit) and d.account == tax_withholding_details.get("account_head")]
+
+		for d in to_remove:
+			self.remove(d)
+
 	def update_inter_company_jv(self):
 		if self.voucher_type == "Inter Company Journal Entry" and self.inter_company_journal_entry_reference:
 			frappe.db.set_value("Journal Entry", self.inter_company_journal_entry_reference,\
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
index dd4a005..1c9943f 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
@@ -1146,6 +1146,12 @@
 		if not self.apply_tds:
 			return
 
+		if self.apply_tds and not self.get('tax_withholding_category'):
+			self.tax_withholding_category = frappe.db.get_value('Supplier', self.supplier, 'tax_withholding_category')
+
+		if not self.tax_withholding_category:
+			return
+
 		tax_withholding_details = get_party_tax_withholding_details(self, self.tax_withholding_category)
 
 		if not tax_withholding_details:
diff --git a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py
index fa4ea21..16ef5fc 100644
--- a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py
+++ b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py
@@ -100,6 +100,7 @@
 	for account_detail in tax_withholding.accounts:
 		if company == account_detail.company:
 			return frappe._dict({
+				"tax_withholding_category": tax_withholding_category,
 				"account_head": account_detail.account,
 				"rate": tax_rate_detail.tax_withholding_rate,
 				"from_date": tax_rate_detail.from_date,
@@ -206,18 +207,39 @@
 
 def get_invoice_vouchers(parties, tax_details, company, party_type='Supplier'):
 	dr_or_cr = 'credit' if party_type == 'Supplier' else 'debit'
+	doctype = 'Purchase Invoice' if party_type == 'Supplier' else 'Sales Invoice'
 
 	filters = {
-		dr_or_cr: ['>', 0],
 		'company': company,
-		'party_type': party_type,
-		'party': ['in', parties],
+		frappe.scrub(party_type): ['in', parties],
 		'posting_date': ['between', (tax_details.from_date, tax_details.to_date)],
 		'is_opening': 'No',
-		'is_cancelled': 0
+		'docstatus': 1
 	}
 
-	return frappe.get_all('GL Entry', filters=filters, distinct=1, pluck="voucher_no") or [""]
+	if not tax_details.get('consider_party_ledger_amount') and doctype != "Sales Invoice":
+		filters.update({
+			'apply_tds': 1,
+			'tax_withholding_category': tax_details.get('tax_withholding_category')
+		})
+
+	invoices = frappe.get_all(doctype, filters=filters, pluck="name") or [""]
+
+	journal_entries = frappe.db.sql("""
+		SELECT j.name
+			FROM `tabJournal Entry` j, `tabJournal Entry Account` ja
+		WHERE
+			j.docstatus = 1
+			AND j.is_opening = 'No'
+			AND j.posting_date between %s and %s
+			AND ja.{dr_or_cr} > 0
+			AND ja.party in %s
+	""".format(dr_or_cr=dr_or_cr), (tax_details.from_date, tax_details.to_date, tuple(parties)), as_list=1)
+
+	if journal_entries:
+		journal_entries = journal_entries[0]
+
+	return invoices + journal_entries
 
 def get_advance_vouchers(parties, company=None, from_date=None, to_date=None, party_type='Supplier'):
 	# for advance vouchers, debit and credit is reversed
diff --git a/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py b/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py
index 8a88d79..84b364b 100644
--- a/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py
+++ b/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py
@@ -176,6 +176,29 @@
 		for d in invoices:
 			d.cancel()
 
+	def test_multi_category_single_supplier(self):
+		frappe.db.set_value("Supplier", "Test TDS Supplier5", "tax_withholding_category", "Test Service Category")
+		invoices = []
+
+		pi = create_purchase_invoice(supplier = "Test TDS Supplier5", rate = 500, do_not_save=True)
+		pi.tax_withholding_category = "Test Service Category"
+		pi.save()
+		pi.submit()
+		invoices.append(pi)
+
+		# Second Invoice will apply TDS checked
+		pi1 = create_purchase_invoice(supplier = "Test TDS Supplier5", rate = 2500, do_not_save=True)
+		pi1.tax_withholding_category = "Test Goods Category"
+		pi1.save()
+		pi1.submit()
+		invoices.append(pi1)
+
+		self.assertEqual(pi1.taxes[0].tax_amount, 250)
+
+		#delete invoices to avoid clashing
+		for d in invoices:
+			d.cancel()
+
 def cancel_invoices():
 	purchase_invoices = frappe.get_all("Purchase Invoice", {
 		'supplier': ['in', ['Test TDS Supplier', 'Test TDS Supplier1', 'Test TDS Supplier2']],
@@ -251,7 +274,8 @@
 
 def create_records():
 	# create a new suppliers
-	for name in ['Test TDS Supplier', 'Test TDS Supplier1', 'Test TDS Supplier2', 'Test TDS Supplier3', 'Test TDS Supplier4']:
+	for name in ['Test TDS Supplier', 'Test TDS Supplier1', 'Test TDS Supplier2', 'Test TDS Supplier3',
+		'Test TDS Supplier4', 'Test TDS Supplier5']:
 		if frappe.db.exists('Supplier', name):
 			continue
 
@@ -390,3 +414,39 @@
 				'account': 'TDS - _TC'
 			}]
 		}).insert()
+
+	if not frappe.db.exists("Tax Withholding Category", "Test Service Category"):
+		frappe.get_doc({
+			"doctype": "Tax Withholding Category",
+			"name": "Test Service Category",
+			"category_name": "Test Service Category",
+			"rates": [{
+				'from_date': fiscal_year[1],
+				'to_date': fiscal_year[2],
+				'tax_withholding_rate': 10,
+				'single_threshold': 2000,
+				'cumulative_threshold': 2000
+			}],
+			"accounts": [{
+				'company': '_Test Company',
+				'account': 'TDS - _TC'
+			}]
+		}).insert()
+
+	if not frappe.db.exists("Tax Withholding Category", "Test Goods Category"):
+		frappe.get_doc({
+			"doctype": "Tax Withholding Category",
+			"name": "Test Goods Category",
+			"category_name": "Test Goods Category",
+			"rates": [{
+				'from_date': fiscal_year[1],
+				'to_date': fiscal_year[2],
+				'tax_withholding_rate': 10,
+				'single_threshold': 2000,
+				'cumulative_threshold': 2000
+			}],
+			"accounts": [{
+				'company': '_Test Company',
+				'account': 'TDS - _TC'
+			}]
+		}).insert()
diff --git a/erpnext/accounts/report/tds_computation_summary/tds_computation_summary.json b/erpnext/accounts/report/tds_computation_summary/tds_computation_summary.json
index dfc4b18..91f0798 100644
--- a/erpnext/accounts/report/tds_computation_summary/tds_computation_summary.json
+++ b/erpnext/accounts/report/tds_computation_summary/tds_computation_summary.json
@@ -1,12 +1,15 @@
 {
- "add_total_row": 0,
+ "add_total_row": 1,
+ "columns": [],
  "creation": "2018-08-21 11:25:00.551823",
+ "disable_prepared_report": 0,
  "disabled": 0,
  "docstatus": 0,
  "doctype": "Report",
+ "filters": [],
  "idx": 0,
  "is_standard": "Yes",
- "modified": "2018-09-21 11:25:00.551823",
+ "modified": "2021-09-20 17:43:39.518851",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "TDS Computation Summary",
diff --git a/erpnext/accounts/report/tds_computation_summary/tds_computation_summary.py b/erpnext/accounts/report/tds_computation_summary/tds_computation_summary.py
index c4a8c7a..536df1f 100644
--- a/erpnext/accounts/report/tds_computation_summary/tds_computation_summary.py
+++ b/erpnext/accounts/report/tds_computation_summary/tds_computation_summary.py
@@ -2,11 +2,10 @@
 
 import frappe
 from frappe import _
-from frappe.utils import flt
 
-from erpnext.accounts.doctype.tax_withholding_category.tax_withholding_category import (
-	get_advance_vouchers,
-	get_debit_note_amount,
+from erpnext.accounts.report.tds_payable_monthly.tds_payable_monthly import (
+	get_result,
+	get_tds_docs,
 )
 from erpnext.accounts.utils import get_fiscal_year
 
@@ -17,9 +16,12 @@
 	filters.naming_series = frappe.db.get_single_value('Buying Settings', 'supp_master_name')
 
 	columns = get_columns(filters)
-	res = get_result(filters)
+	tds_docs, tds_accounts, tax_category_map = get_tds_docs(filters)
 
-	return columns, res
+	res = get_result(filters, tds_docs, tds_accounts, tax_category_map)
+	final_result = group_by_supplier_and_category(res)
+
+	return columns, final_result
 
 def validate_filters(filters):
 	''' Validate if dates are properly set and lie in the same fiscal year'''
@@ -33,81 +35,39 @@
 
 	filters["fiscal_year"] = from_year
 
-def get_result(filters):
-	# if no supplier selected, fetch data for all tds applicable supplier
-	# else fetch relevant data for selected supplier
-	pan = "pan" if frappe.db.has_column("Supplier", "pan") else "tax_id"
-	fields = ["name", pan+" as pan", "tax_withholding_category", "supplier_type", "supplier_name"]
+def group_by_supplier_and_category(data):
+	supplier_category_wise_map = {}
 
-	if filters.supplier:
-		filters.supplier = frappe.db.get_list('Supplier',
-			{"name": filters.supplier}, fields)
-	else:
-		filters.supplier = frappe.db.get_list('Supplier',
-			{"tax_withholding_category": ["!=", ""]}, fields)
+	for row in data:
+		supplier_category_wise_map.setdefault((row.get('supplier'), row.get('section_code')), {
+			'pan': row.get('pan'),
+			'supplier': row.get('supplier'),
+			'supplier_name': row.get('supplier_name'),
+			'section_code': row.get('section_code'),
+			'entity_type': row.get('entity_type'),
+			'tds_rate': row.get('tds_rate'),
+			'total_amount_credited': 0.0,
+			'tds_deducted': 0.0
+		})
 
+		supplier_category_wise_map.get((row.get('supplier'), row.get('section_code')))['total_amount_credited'] += \
+			row.get('total_amount_credited', 0.0)
+
+		supplier_category_wise_map.get((row.get('supplier'), row.get('section_code')))['tds_deducted'] += \
+			row.get('tds_deducted', 0.0)
+
+	final_result = get_final_result(supplier_category_wise_map)
+
+	return final_result
+
+
+def get_final_result(supplier_category_wise_map):
 	out = []
-	for supplier in filters.supplier:
-		tds = frappe.get_doc("Tax Withholding Category", supplier.tax_withholding_category)
-		rate = [d.tax_withholding_rate for d in tds.rates if d.fiscal_year == filters.fiscal_year]
-
-		if rate:
-			rate = rate[0]
-
-		try:
-			account = [d.account for d in tds.accounts if d.company == filters.company][0]
-
-		except IndexError:
-			account = []
-		total_invoiced_amount, tds_deducted = get_invoice_and_tds_amount(supplier.name, account,
-			filters.company, filters.from_date, filters.to_date, filters.fiscal_year)
-
-		if total_invoiced_amount or tds_deducted:
-			row = [supplier.pan, supplier.name]
-
-			if filters.naming_series == 'Naming Series':
-				row.append(supplier.supplier_name)
-
-			row.extend([tds.name, supplier.supplier_type, rate, total_invoiced_amount, tds_deducted])
-			out.append(row)
+	for key, value in supplier_category_wise_map.items():
+		out.append(value)
 
 	return out
 
-def get_invoice_and_tds_amount(supplier, account, company, from_date, to_date, fiscal_year):
-	''' calculate total invoice amount and total tds deducted for given supplier  '''
-
-	entries = frappe.db.sql("""
-		select voucher_no, credit
-		from `tabGL Entry`
-		where party in (%s) and credit > 0
-			and company=%s and is_cancelled = 0
-			and posting_date between %s and %s
-	""", (supplier, company, from_date, to_date), as_dict=1)
-
-	supplier_credit_amount = flt(sum(d.credit for d in entries))
-
-	vouchers = [d.voucher_no for d in entries]
-	vouchers += get_advance_vouchers([supplier], company=company,
-		from_date=from_date, to_date=to_date)
-
-	tds_deducted = 0
-	if vouchers:
-		tds_deducted = flt(frappe.db.sql("""
-			select sum(credit)
-			from `tabGL Entry`
-			where account=%s and posting_date between %s and %s
-				and company=%s and credit > 0 and voucher_no in ({0})
-		""".format(', '.join("'%s'" % d for d in vouchers)),
-			(account, from_date, to_date, company))[0][0])
-
-	date_range_filter = [fiscal_year, from_date, to_date]
-
-	debit_note_amount = get_debit_note_amount([supplier], date_range_filter, company=company)
-
-	total_invoiced_amount = supplier_credit_amount + tds_deducted - debit_note_amount
-
-	return total_invoiced_amount, tds_deducted
-
 def get_columns(filters):
 	columns = [
 		{
@@ -149,7 +109,7 @@
 		{
 			"label": _("TDS Rate %"),
 			"fieldname": "tds_rate",
-			"fieldtype": "Float",
+			"fieldtype": "Percent",
 			"width": 90
 		},
 		{
diff --git a/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.js b/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.js
index 72de318..ff2aa30 100644
--- a/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.js
+++ b/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.js
@@ -16,69 +16,6 @@
 			"label": __("Supplier"),
 			"fieldtype": "Link",
 			"options": "Supplier",
-			"get_query": function() {
-				return {
-					"filters": {
-						"tax_withholding_category": ["!=", ""],
-					}
-				}
-			},
-			on_change: function() {
-				frappe.query_report.set_filter_value("purchase_invoice", "");
-				frappe.query_report.refresh();
-			}
-		},
-		{
-			"fieldname":"purchase_invoice",
-			"label": __("Purchase Invoice"),
-			"fieldtype": "Link",
-			"options": "Purchase Invoice",
-			"get_query": function() {
-				return {
-					"filters": {
-						"name": ["in", frappe.query_report.invoices]
-					}
-				}
-			},
-			on_change: function() {
-				let supplier = frappe.query_report.get_filter_value('supplier');
-				if(!supplier) return; // return if no supplier selected
-
-				// filter invoices based on selected supplier
-				let invoices = [];
-				frappe.query_report.invoice_data.map(d => {
-					if(d.supplier==supplier)
-						invoices.push(d.name)
-				});
-				frappe.query_report.invoices = invoices;
-				frappe.query_report.refresh();
-			}
-		},
-		{
-			"fieldname":"purchase_order",
-			"label": __("Purchase Order"),
-			"fieldtype": "Link",
-			"options": "Purchase Order",
-			"get_query": function() {
-				return {
-					"filters": {
-						"name": ["in", frappe.query_report.invoices]
-					}
-				}
-			},
-			on_change: function() {
-				let supplier = frappe.query_report.get_filter_value('supplier');
-				if(!supplier) return; // return if no supplier selected
-
-				// filter invoices based on selected supplier
-				let invoices = [];
-				frappe.query_report.invoice_data.map(d => {
-					if(d.supplier==supplier)
-						invoices.push(d.name)
-				});
-				frappe.query_report.invoices = invoices;
-				frappe.query_report.refresh();
-			}
 		},
 		{
 			"fieldname":"from_date",
@@ -96,23 +33,5 @@
 			"reqd": 1,
 			"width": "60px"
 		}
-	],
-
-	onload: function(report) {
-		// fetch all tds applied invoices
-		frappe.call({
-			"method": "erpnext.accounts.report.tds_payable_monthly.tds_payable_monthly.get_tds_invoices_and_orders",
-			callback: function(r) {
-				let invoices = [];
-
-				r.message.map(d => {
-					invoices.push(d.name);
-				});
-
-				report["invoice_data"] = r.message.invoices;
-				report["invoices"] = invoices;
-
-			}
-		});
-	}
+	]
 }
diff --git a/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.json b/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.json
index 557a62d..4d555bd 100644
--- a/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.json
+++ b/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.json
@@ -1,13 +1,15 @@
 {
  "add_total_row": 1,
+ "columns": [],
  "creation": "2018-08-21 11:32:30.874923",
  "disable_prepared_report": 0,
  "disabled": 0,
  "docstatus": 0,
  "doctype": "Report",
+ "filters": [],
  "idx": 0,
  "is_standard": "Yes",
- "modified": "2019-09-24 13:46:16.473711",
+ "modified": "2021-09-20 12:05:50.387572",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "TDS Payable Monthly",
diff --git a/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.py b/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.py
index 9e1382b..621b697 100644
--- a/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.py
+++ b/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.py
@@ -8,19 +8,12 @@
 
 
 def execute(filters=None):
-	filters["invoices"] = frappe.cache().hget("invoices", frappe.session.user)
 	validate_filters(filters)
-	set_filters(filters)
-
-	# TDS payment entries
-	payment_entries = get_payment_entires(filters)
+	tds_docs, tds_accounts, tax_category_map = get_tds_docs(filters)
 
 	columns = get_columns(filters)
-	if not filters.get("invoices"):
-		return columns, []
 
-	res = get_result(filters, payment_entries)
-
+	res = get_result(filters, tds_docs, tds_accounts, tax_category_map)
 	return columns, res
 
 def validate_filters(filters):
@@ -28,109 +21,59 @@
 	if filters.from_date > filters.to_date:
 		frappe.throw(_("From Date must be before To Date"))
 
-def set_filters(filters):
-	invoices = []
-
-	if not filters.get("invoices"):
-		filters["invoices"] = get_tds_invoices_and_orders()
-
-	if filters.supplier and filters.purchase_invoice:
-		for d in filters["invoices"]:
-			if d.name == filters.purchase_invoice and d.supplier == filters.supplier:
-				invoices.append(d)
-	elif filters.supplier and not filters.purchase_invoice:
-		for d in filters["invoices"]:
-			if d.supplier == filters.supplier:
-				invoices.append(d)
-	elif filters.purchase_invoice and not filters.supplier:
-		for d in filters["invoices"]:
-			if d.name == filters.purchase_invoice:
-				invoices.append(d)
-	elif filters.supplier and filters.purchase_order:
-		for d in filters.get("invoices"):
-			if d.name == filters.purchase_order and d.supplier == filters.supplier:
-				invoices.append(d)
-	elif filters.supplier and not filters.purchase_order:
-		for d in filters.get("invoices"):
-			if d.supplier == filters.supplier:
-				invoices.append(d)
-	elif filters.purchase_order and not filters.supplier:
-		for d in filters.get("invoices"):
-			if d.name == filters.purchase_order:
-				invoices.append(d)
-
-	filters["invoices"] = invoices if invoices else filters["invoices"]
-	filters.naming_series = frappe.db.get_single_value('Buying Settings', 'supp_master_name')
-
-	#print(filters.get('invoices'))
-
-def get_result(filters, payment_entries):
-	supplier_map, tds_docs = get_supplier_map(filters, payment_entries)
-	documents = [d.get('name') for d in filters.get('invoices')] + [d.get('name') for d in payment_entries]
-
-	gle_map = get_gle_map(filters, documents)
+def get_result(filters, tds_docs, tds_accounts, tax_category_map):
+	supplier_map = get_supplier_pan_map()
+	tax_rate_map = get_tax_rate_map(filters)
+	gle_map = get_gle_map(filters, tds_docs)
 
 	out = []
-	for d in gle_map:
+	for name, details in gle_map.items():
 		tds_deducted, total_amount_credited = 0, 0
-		supplier = supplier_map[d]
+		tax_withholding_category = tax_category_map.get(name)
+		rate = tax_rate_map.get(tax_withholding_category)
 
-		tds_doc = tds_docs[supplier.tax_withholding_category]
-		account_list = [i.account for i in tds_doc.accounts if i.company == filters.company]
+		for entry in details:
+			supplier = entry.party or entry.against
+			posting_date = entry.posting_date
+			voucher_type = entry.voucher_type
 
-		if account_list:
-			account = account_list[0]
+			if entry.account in tds_accounts:
+				tds_deducted += (entry.credit - entry.debit)
 
-		for k in gle_map[d]:
-			if k.party == supplier_map[d] and k.credit > 0:
-				total_amount_credited += (k.credit - k.debit)
-			elif account_list and k.account == account and (k.credit - k.debit) > 0:
-				tds_deducted = (k.credit - k.debit)
-				total_amount_credited += (k.credit - k.debit)
-			voucher_type = k.voucher_type
+			total_amount_credited += (entry.credit - entry.debit)
 
-		rate = [i.tax_withholding_rate for i in tds_doc.rates
-			if i.fiscal_year == gle_map[d][0].fiscal_year]
-
-		if rate and len(rate) > 0 and tds_deducted:
-			rate = rate[0]
-
-			row = [supplier.pan, supplier.name]
+		if rate and tds_deducted:
+			row = {
+				'pan' if frappe.db.has_column('Supplier', 'pan') else 'tax_id': supplier_map.get(supplier).pan,
+				'supplier': supplier_map.get(supplier).name
+			}
 
 			if filters.naming_series == 'Naming Series':
-				row.append(supplier.supplier_name)
+				row.update({'supplier_name': supplier_map.get(supplier).supplier_name})
 
-			row.extend([tds_doc.name, supplier.supplier_type, rate, total_amount_credited,
-				tds_deducted, gle_map[d][0].posting_date, voucher_type, d])
+			row.update({
+				'section_code': tax_withholding_category,
+				'entity_type': supplier_map.get(supplier).supplier_type,
+				'tds_rate': rate,
+				'total_amount_credited': total_amount_credited,
+				'tds_deducted': tds_deducted,
+				'transaction_date': posting_date,
+				'transaction_type': voucher_type,
+				'ref_no': name
+			})
+
 			out.append(row)
 
 	return out
 
-def get_supplier_map(filters, payment_entries):
-	# create a supplier_map of the form {"purchase_invoice": {supplier_name, pan, tds_name}}
-	# pre-fetch all distinct applicable tds docs
-	supplier_map, tds_docs = {}, {}
-	pan = "pan" if frappe.db.has_column("Supplier", "pan") else "tax_id"
-	supplier_list = [d.supplier for d in filters["invoices"]]
+def get_supplier_pan_map():
+	supplier_map = frappe._dict()
+	suppliers = frappe.db.get_all('Supplier', fields=['name', 'pan', 'supplier_type', 'supplier_name'])
 
-	supplier_detail = frappe.db.get_all('Supplier',
-		{"name": ["in", supplier_list]},
-		["tax_withholding_category", "name", pan+" as pan", "supplier_type", "supplier_name"])
+	for d in suppliers:
+		supplier_map[d.name] = d
 
-	for d in filters["invoices"]:
-		supplier_map[d.get("name")] = [k for k in supplier_detail
-			if k.name == d.get("supplier")][0]
-
-	for d in payment_entries:
-		supplier_map[d.get("name")] = [k for k in supplier_detail
-			if k.name == d.get("supplier")][0]
-
-	for d in supplier_detail:
-		if d.get("tax_withholding_category") not in tds_docs:
-			tds_docs[d.get("tax_withholding_category")] = \
-				frappe.get_doc("Tax Withholding Category", d.get("tax_withholding_category"))
-
-	return supplier_map, tds_docs
+	return supplier_map
 
 def get_gle_map(filters, documents):
 	# create gle_map of the form
@@ -140,10 +83,9 @@
 	gle = frappe.db.get_all('GL Entry',
 		{
 			"voucher_no": ["in", documents],
-			'is_cancelled': 0,
-			'posting_date': ("between", [filters.get('from_date'), filters.get('to_date')]),
+			"credit": (">", 0)
 		},
-		["fiscal_year", "credit", "debit", "account", "voucher_no", "posting_date", "voucher_type"],
+		["credit", "debit", "account", "voucher_no", "posting_date", "voucher_type", "against", "party"],
 	)
 
 	for d in gle:
@@ -233,39 +175,57 @@
 
 	return columns
 
-def get_payment_entires(filters):
-	filter_dict = {
-		'posting_date': ("between", [filters.get('from_date'), filters.get('to_date')]),
-		'party_type': 'Supplier',
-		'apply_tax_withholding_amount': 1
+def get_tds_docs(filters):
+	tds_documents = []
+	purchase_invoices = []
+	payment_entries = []
+	journal_entries = []
+	tax_category_map = {}
+
+	tds_accounts = frappe.get_all("Tax Withholding Account", {'company': filters.get('company')},
+		pluck="account")
+
+	query_filters = {
+		"credit": ('>', 0),
+		"account": ("in", tds_accounts),
+		"posting_date": ("between", [filters.get("from_date"), filters.get("to_date")]),
+		"is_cancelled": 0
 	}
 
-	if filters.get('purchase_invoice') or filters.get('purchase_order'):
-		parent = frappe.db.get_all('Payment Entry Reference',
-			{'reference_name': ('in', [d.get('name') for d in filters.get('invoices')])}, ['parent'])
+	if filters.get('supplier'):
+		query_filters.update({'against': filters.get('supplier')})
 
-		filter_dict.update({'name': ('in', [d.get('parent') for d in parent])})
+	tds_docs = frappe.get_all("GL Entry", query_filters, ["voucher_no", "voucher_type", "against", "party"])
 
-	payment_entries = frappe.get_all('Payment Entry', fields=['name', 'party_name as supplier'],
-		filters=filter_dict)
+	for d in tds_docs:
+		if d.voucher_type == "Purchase Invoice":
+			purchase_invoices.append(d.voucher_no)
+		elif d.voucher_type == "Payment Entry":
+			payment_entries.append(d.voucher_no)
+		elif d.voucher_type == "Journal Entry":
+			journal_entries.append(d.voucher_no)
 
-	return payment_entries
+		tds_documents.append(d.voucher_no)
 
-@frappe.whitelist()
-def get_tds_invoices_and_orders():
-	# fetch tds applicable supplier and fetch invoices for these suppliers
-	suppliers = [d.name for d in frappe.db.get_list("Supplier",
-		{"tax_withholding_category": ["!=", ""]}, ["name"])]
+	if purchase_invoices:
+		get_tax_category_map(purchase_invoices, 'Purchase Invoice', tax_category_map)
 
-	invoices = frappe.db.get_list("Purchase Invoice",
-		{"supplier": ["in", suppliers]}, ["name", "supplier"])
+	if payment_entries:
+		get_tax_category_map(payment_entries, 'Payment Entry', tax_category_map)
 
-	orders = frappe.db.get_list("Purchase Order",
-		{"supplier": ["in", suppliers]}, ["name", "supplier"])
+	if journal_entries:
+		get_tax_category_map(journal_entries, 'Journal Entry', tax_category_map)
 
-	invoices = invoices + orders
-	invoices = [d for d in invoices if d.supplier]
+	return tds_documents, tds_accounts, tax_category_map
 
-	frappe.cache().hset("invoices", frappe.session.user, invoices)
+def get_tax_category_map(vouchers, doctype, tax_category_map):
+	tax_category_map.update(frappe._dict(frappe.get_all(doctype,
+		filters = {'name': ('in', vouchers)}, fields=['name', 'tax_withholding_category'], as_list=1)))
 
-	return invoices
+def get_tax_rate_map(filters):
+	rate_map = frappe.get_all('Tax Withholding Rate', filters={
+		'from_date': ('<=', filters.get('from_date')),
+		'to_date': ('>=', filters.get('to_date'))
+	}, fields=['parent', 'tax_withholding_rate'], as_list=1)
+
+	return frappe._dict(rate_map)
\ No newline at end of file