feat: item wise tds calculation
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
index 25b128b..2f9ee97 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
@@ -57,6 +57,8 @@
   "column_break_28",
   "total",
   "net_total",
+  "tax_withholding_net_total",
+  "base_tax_withholding_net_total",
   "taxes_section",
   "taxes_and_charges",
   "column_break_58",
@@ -1422,6 +1424,24 @@
    "read_only": 1
   },
   {
+    "default": "0",
+    "fieldname": "tax_withholding_net_total",
+    "fieldtype": "Currency",
+    "label": "Tax Withholding Net Total",
+    "no_copy": 1,
+    "options": "currency",
+    "read_only": 1
+  },
+  {
+    "fieldname": "base_tax_withholding_net_total",
+    "fieldtype": "Currency",
+    "label": "Base Tax Withholding Net Total",
+    "no_copy": 1,
+    "options": "currency",
+    "print_hide": 1,
+    "read_only": 1
+  },
+  {
    "collapsible_depends_on": "tax_withheld_vouchers",
    "fieldname": "tax_withheld_vouchers_section",
    "fieldtype": "Section Break",
@@ -1583,4 +1603,4 @@
  "timeline_field": "supplier",
  "title_field": "title",
  "track_changes": 1
-}
+}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
index 58e29f1..76ea955 100644
--- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
+++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
@@ -1574,35 +1574,6 @@
 
 		self.assertTrue(return_pi.docstatus == 1)
 
-	def test_without_tds(self):
-		make_purchase_invoice_tds()
-
-	def test_total_tds(self):
-		supplier = create_supplier(
-			supplier_name="_Test TDS Advance Supplier",
-			tax_withholding_category="TDS - 194 - Dividends - Individual",
-		)
-		pi = make_purchase_invoice_tds(supplier= "_Test TDS Advance Supplier",total_tds = 1)
-	
-		sum_tds = 0
-		for item in pi.items:
-			sum_tds += item.net_amount
-		
-		self.assertEqual(pi.tax_withholding_net_total, sum_tds)
-		for tax in pi.taxes:
-			self.assertEqual(tax.tax_amount, pi.tax_withholding_net_total * 0.10)
-
-	def test_partial_tds(self):
-		pi = make_purchase_invoice_tds(supplier= "_Test TDS Advance Supplier",partial_tds = 1)
-		
-		sum_tds = 0
-		for item in pi.items:
-			if item.apply_tds:
-				sum_tds += item.net_amount
-		
-		self.assertEqual(pi.tax_withholding_net_total, sum_tds)
-		for tax in pi.taxes:
-			self.assertEqual(tax.tax_amount, pi.tax_withholding_net_total * 0.10)
 
 def check_gl_entries(doc, voucher_no, expected_gle, posting_date):
 	gl_entries = frappe.db.sql(
@@ -1711,86 +1682,6 @@
 			pi.submit()
 	return pi
 
-def make_purchase_invoice_tds(**args):
-	pi = frappe.new_doc("Purchase Invoice")
-	args = frappe._dict(args)
-	pi.posting_date = args.posting_date or today()
-	if args.posting_time:
-		pi.posting_time = args.posting_time
-	if args.update_stock:
-		pi.update_stock = 1
-	if args.is_paid:
-		pi.is_paid = 1
-
-	if args.cash_bank_account:
-		pi.cash_bank_account = args.cash_bank_account
-
-	pi.company = args.company or "_Test Company"
-	pi.supplier = args.supplier or "_Test Supplier"
-	pi.currency = args.currency or "INR"
-	pi.conversion_rate = args.conversion_rate or 1
-	pi.is_return = args.is_return
-	pi.return_against = args.return_against
-	pi.is_subcontracted = args.is_subcontracted or 0
-	pi.supplier_warehouse = args.supplier_warehouse or "_Test Warehouse 1 - _TC"
-	pi.cost_center = args.parent_cost_center
-	
-	if args.total_tds or args.partial_tds:
-		pi.apply_tds = 1
-	
-	pi.extend(
-		"items",
-		[
-			{
-				"item_code": args.item or args.item_code or "_Test Item",
-				"warehouse": args.warehouse or "_Test Warehouse - _TC",
-				"qty": args.qty or 5,
-				"received_qty": args.received_qty or 0,
-				"rejected_qty": args.rejected_qty or 0,
-				"rate": args.rate or 5000,
-				"price_list_rate": args.price_list_rate or 5000,
-				"expense_account": args.expense_account or "_Test Account Cost for Goods Sold - _TC",
-				"discount_account": args.discount_account or None,
-				"discount_amount": args.discount_amount or 0,
-				"conversion_factor": 1.0,
-				"serial_no": args.serial_no,
-				"stock_uom": args.uom or "_Test UOM",
-				"cost_center": args.cost_center or "_Test Cost Center - _TC",
-				"project": args.project,
-				"rejected_warehouse": args.rejected_warehouse or "",
-				"rejected_serial_no": args.rejected_serial_no or "",
-				"asset_location": args.location or "",
-				"allow_zero_valuation_rate": args.get("allow_zero_valuation_rate") or 0,
-				"apply_tds": 1 if (args.total_tds or args.partial_tds) else 0
-			},
-			{
-				"item_code": args.item or args.item_code or "_Test Item",
-				"warehouse": args.warehouse or "_Test Warehouse - _TC",
-				"qty": args.qty or 5,
-				"received_qty": args.received_qty or 0,
-				"rejected_qty": args.rejected_qty or 0,
-				"rate": args.rate or 5000,
-				"price_list_rate": args.price_list_rate or 5000,
-				"expense_account": args.expense_account or "_Test Account Cost for Goods Sold - _TC",
-				"discount_account": args.discount_account or None,
-				"discount_amount": args.discount_amount or 0,
-				"conversion_factor": 1.0,
-				"serial_no": args.serial_no,
-				"stock_uom": args.uom or "_Test UOM",
-				"cost_center": args.cost_center or "_Test Cost Center - _TC",
-				"project": args.project,
-				"rejected_warehouse": args.rejected_warehouse or "",
-				"rejected_serial_no": args.rejected_serial_no or "",
-				"asset_location": args.location or "",
-				"allow_zero_valuation_rate": args.get("allow_zero_valuation_rate") or 0,
-				"apply_tds": 1 if (args.total_tds) else 0
-			},
-		]
-	)
-
-	pi.save()
-	pi.submit()
-	return pi
 
 def make_purchase_invoice_against_cost_center(**args):
 	pi = frappe.new_doc("Purchase Invoice")
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 737338e..06e1c17 100644
--- a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py
+++ b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py
@@ -275,6 +275,11 @@
 
 def get_invoice_vouchers(parties, tax_details, company, party_type="Supplier"):
 	doctype = "Purchase Invoice" if party_type == "Supplier" else "Sales Invoice"
+	field = (
+		"base_tax_withholding_net_total as base_net_total"
+		if party_type == "Supplier"
+		else "base_net_total"
+	)
 	voucher_wise_amount = {}
 	vouchers = []
 
@@ -291,7 +296,7 @@
 			{"apply_tds": 1, "tax_withholding_category": tax_details.get("tax_withholding_category")}
 		)
 
-	invoices_details = frappe.get_all(doctype, filters=filters, fields=["name", "base_net_total"])
+	invoices_details = frappe.get_all(doctype, filters=filters, fields=["name", field])
 
 	for d in invoices_details:
 		vouchers.append(d.name)
@@ -431,11 +436,11 @@
 		):
 			# Get net total again as TDS is calculated on net total
 			# Grand is used to just check for threshold breach
-			net_total = 0
-			if vouchers:
-				net_total = frappe.db.get_value("Purchase Invoice", invoice_filters, "sum(net_total)")
-
-			net_total += inv.net_total
+			net_total = (
+				frappe.db.get_value("Purchase Invoice", invoice_filters, "sum(tax_withholding_net_total)")
+				or 0.0
+			)
+			net_total += inv.tax_withholding_net_total
 			supp_credit_amt = net_total - cumulative_threshold
 
 		if ldc and is_valid_certificate(
@@ -559,4 +564,4 @@
 	) and certificate_limit > deducted_amount:
 		valid = True
 
-	return valid
+	return valid
\ No newline at end of file
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 e80fe11..d29af92 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
@@ -186,6 +186,46 @@
 		for d in reversed(invoices):
 			d.cancel()
 
+	def test_tds_calculation_on_net_total_partial_tds(self):
+		frappe.db.set_value(
+			"Supplier", "Test TDS Supplier4", "tax_withholding_category", "Cumulative Threshold TDS"
+		)
+		invoices = []
+
+		pi = create_purchase_invoice(supplier="Test TDS Supplier4", rate=20000, do_not_save=True)
+		pi.extend(
+			"items",
+			[
+				{
+					"doctype": "Purchase Invoice Item",
+					"item_code": frappe.db.get_value("Item", {"item_name": "TDS Item"}, "name"),
+					"qty": 1,
+					"rate": 20000,
+					"cost_center": "Main - _TC",
+					"expense_account": "Stock Received But Not Billed - _TC",
+					"apply_tds": 0,
+				},
+				{
+					"doctype": "Purchase Invoice Item",
+					"item_code": frappe.db.get_value("Item", {"item_name": "TDS Item"}, "name"),
+					"qty": 1,
+					"rate": 35000,
+					"cost_center": "Main - _TC",
+					"expense_account": "Stock Received But Not Billed - _TC",
+					"apply_tds": 1,
+				},
+			],
+		)
+		pi.save()
+		pi.submit()
+		invoices.append(pi)
+
+		self.assertEqual(pi.taxes[0].tax_amount, 5500)
+
+		# cancel invoices to avoid clashing
+		for d in reversed(invoices):
+			d.cancel()
+
 	def test_multi_category_single_supplier(self):
 		frappe.db.set_value(
 			"Supplier", "Test TDS Supplier5", "tax_withholding_category", "Test Service Category"
@@ -559,4 +599,4 @@
 				],
 				"accounts": [{"company": "_Test Company", "account": "TDS - _TC"}],
 			}
-		).insert()
+		).insert()
\ No newline at end of file
diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py
index a0bc6ba..16bc01d 100644
--- a/erpnext/controllers/taxes_and_totals.py
+++ b/erpnext/controllers/taxes_and_totals.py
@@ -67,13 +67,15 @@
 
 	def calculate_tax_withholding_net_total(self):
 		if hasattr(self.doc, "tax_withholding_net_total"):
-			
 			sum_net_amount = 0
+			sum_base_net_amount = 0
 			for item in self.doc.get("items"):
 				if hasattr(item, "apply_tds") and item.apply_tds:
 					sum_net_amount += item.net_amount
-			
+					sum_base_net_amount += item.base_net_amount
+
 			self.doc.tax_withholding_net_total = sum_net_amount
+			self.doc.base_tax_withholding_net_total = sum_base_net_amount
 
 	def validate_item_tax_template(self):
 		for item in self.doc.get("items"):
@@ -1076,4 +1078,4 @@
 	def set_amounts_in_company_currency(self):
 		for d in self.doc.get(self.tax_field):
 			d.amount = flt(d.amount, d.precision("amount"))
-			d.base_amount = flt(d.amount * flt(d.exchange_rate), d.precision("base_amount"))
+			d.base_amount = flt(d.amount * flt(d.exchange_rate), d.precision("base_amount"))
\ No newline at end of file
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index 6a8c21f..2624181 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -317,3 +317,4 @@
 erpnext.patches.v14_0.migrate_remarks_from_gl_to_payment_ledger
 erpnext.patches.v13_0.update_schedule_type_in_loans
 erpnext.patches.v14_0.create_accounting_dimensions_for_asset_capitalization
+erpnext.patches.v14_0.update_tds_fields
diff --git a/erpnext/patches/v14_0/update_tds_fields.py b/erpnext/patches/v14_0/update_tds_fields.py
new file mode 100644
index 0000000..ffada07
--- /dev/null
+++ b/erpnext/patches/v14_0/update_tds_fields.py
@@ -0,0 +1,25 @@
+import frappe
+
+from erpnext.accounts.utils import get_fiscal_year
+
+
+def execute():
+	# Only do for current fiscal year, no need to repost for all years
+	for company in frappe.get_all("Company"):
+		fiscal_year_details = get_fiscal_year(company=company.name, as_dict=True)
+
+		purchase_invoice = frappe.qb.DocType("Purchase Invoice")
+
+		frappe.qb.update(purchase_invoice).set(
+			purchase_invoice.tax_withholding_net_total, purchase_invoice.net_total
+		).set(
+			purchase_invoice.base_tax_withholding_net_total, purchase_invoice.base_net_total
+		).where(
+			purchase_invoice.company == company.name
+		).where(
+			purchase_invoice.apply_tds == 1
+		).where(
+			purchase_invoice.posting_date >= fiscal_year_details.year_start_date
+		).where(
+			purchase_invoice.docstatus == 1
+		).run()
\ No newline at end of file
diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js
index dd957c7..c2e34a6 100644
--- a/erpnext/public/js/controllers/transaction.js
+++ b/erpnext/public/js/controllers/transaction.js
@@ -1200,7 +1200,7 @@
 			"base_rounding_adjustment"], company_currency);
 
 		this.frm.set_currency_labels(["total", "net_total", "total_taxes_and_charges", "discount_amount",
-			"grand_total", "taxes_and_charges_added", "taxes_and_charges_deducted",
+			"grand_total", "taxes_and_charges_added", "taxes_and_charges_deducted","tax_withholding_net_total",
 			"rounded_total", "in_words", "paid_amount", "write_off_amount", "operating_cost",
 			"scrap_material_cost", "rounding_adjustment", "raw_material_cost",
 			"total_cost"], this.frm.doc.currency);
@@ -1217,7 +1217,7 @@
 		}
 
 		// toggle fields
-		this.frm.toggle_display(["conversion_rate", "base_total", "base_net_total",
+		this.frm.toggle_display(["conversion_rate", "base_total", "base_net_total", "base_tax_withholding_net_total",
 			"base_total_taxes_and_charges", "base_taxes_and_charges_added", "base_taxes_and_charges_deducted",
 			"base_grand_total", "base_rounded_total", "base_in_words", "base_discount_amount",
 			"base_paid_amount", "base_write_off_amount", "base_operating_cost", "base_raw_material_cost",