Merge branch 'develop' of https://github.com/niralisatapara/erpnext into develop
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
index e73d602..25b128b 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
@@ -1583,4 +1583,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 f901257..58e29f1 100644
--- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
+++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
@@ -1574,6 +1574,35 @@
 
 		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(
@@ -1682,6 +1711,86 @@
 			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")
@@ -1734,4 +1843,4 @@
 	return pi
 
 
-test_records = frappe.get_test_records("Purchase Invoice")
+test_records = frappe.get_test_records("Purchase Invoice")
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json
index a8f6f80..5c1cb0d 100644
--- a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json
+++ b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json
@@ -40,6 +40,7 @@
   "discount_amount",
   "base_rate_with_margin",
   "sec_break2",
+  "apply_tds",
   "rate",
   "amount",
   "item_tax_template",
@@ -868,6 +869,12 @@
    "label": "Product Bundle",
    "options": "Product Bundle",
    "read_only": 1
+  },
+  {
+    "default": "1",
+    "fieldname": "apply_tds",
+    "fieldtype": "Check",
+    "label": "Apply TDS"
   }
  ],
  "idx": 1,
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 7eddd81..737338e 100644
--- a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py
+++ b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py
@@ -61,6 +61,9 @@
 
 
 def get_party_tax_withholding_details(inv, tax_withholding_category=None):
+	if inv.doctype == "Payment Entry":
+		inv.tax_withholding_net_total = inv.net_total
+	
 	pan_no = ""
 	parties = []
 	party_type, party = get_party_details(inv)
@@ -242,7 +245,7 @@
 	if party_type == "Supplier":
 		ldc = get_lower_deduction_certificate(tax_details, pan_no)
 		if tax_deducted:
-			net_total = inv.net_total
+			net_total = inv.tax_withholding_net_total
 			if ldc:
 				tax_amount = get_tds_amount_from_ldc(
 					ldc, parties, pan_no, tax_details, posting_date, net_total
@@ -392,7 +395,7 @@
 	tds_amount = 0
 	invoice_filters = {"name": ("in", vouchers), "docstatus": 1, "apply_tds": 1}
 
-	field = "sum(net_total)"
+	field = "sum(tax_withholding_net_total)"
 
 	if cint(tax_details.consider_party_ledger_amount):
 		invoice_filters.pop("apply_tds", None)
@@ -415,12 +418,12 @@
 	)
 
 	supp_credit_amt += supp_jv_credit_amt
-	supp_credit_amt += inv.net_total
+	supp_credit_amt += inv.tax_withholding_net_total
 
 	threshold = tax_details.get("threshold", 0)
 	cumulative_threshold = tax_details.get("cumulative_threshold", 0)
 
-	if (threshold and inv.net_total >= threshold) or (
+	if (threshold and inv.tax_withholding_net_total >= threshold) or (
 		cumulative_threshold and supp_credit_amt >= cumulative_threshold
 	):
 		if (cumulative_threshold and supp_credit_amt >= cumulative_threshold) and cint(
@@ -440,7 +443,7 @@
 			ldc.valid_upto,
 			inv.get("posting_date") or inv.get("transaction_date"),
 			tax_deducted,
-			inv.net_total,
+			inv.tax_withholding_net_total,
 			ldc.certificate_limit,
 		):
 			tds_amount = get_ltds_amount(supp_credit_amt, 0, ldc.certificate_limit, ldc.rate, tax_details)
@@ -523,7 +526,7 @@
 	limit_consumed = frappe.db.get_value(
 		"Purchase Invoice",
 		{"supplier": ("in", parties), "apply_tds": 1, "docstatus": 1},
-		"sum(net_total)",
+		"sum(tax_withholding_net_total)",
 	)
 
 	if is_valid_certificate(
diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py
index b5836c9..a0bc6ba 100644
--- a/erpnext/controllers/taxes_and_totals.py
+++ b/erpnext/controllers/taxes_and_totals.py
@@ -58,12 +58,23 @@
 		self.initialize_taxes()
 		self.determine_exclusive_rate()
 		self.calculate_net_total()
+		self.calculate_tax_withholding_net_total()
 		self.calculate_taxes()
 		self.manipulate_grand_total_for_inclusive_tax()
 		self.calculate_totals()
 		self._cleanup()
 		self.calculate_total_net_weight()
 
+	def calculate_tax_withholding_net_total(self):
+		if hasattr(self.doc, "tax_withholding_net_total"):
+			
+			sum_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
+			
+			self.doc.tax_withholding_net_total = sum_net_amount
+
 	def validate_item_tax_template(self):
 		for item in self.doc.get("items"):
 			if item.item_code and item.get("item_tax_template"):