[Enhancement] Tax Withholding Category (#15064)

* add single, cumulative threshold, remove checkboxes

* remove tds child table from supplier & add tds link field

* add description field in tax withholding category

* add tax withholding data for indian setup, some fixes

* add a checkbox for tax withholding in purchase invoice

* remove supplier's child table for tds

* enable tds field if supplier has tds set inits master

* move rates data to child table - adding fiscal year support

* change bootstrap data according to child table config of tds

* show category name in list view

* loyalty program fixes

* moved tax calculation to tax_withholding.py
- calculation for tds amount for cumulative threshold from gl entry

* add fiscal year dependency in company test

* minor loyalty program fix

* minor tier calculation fix

* minor handling duplicate exception

* toggle apply_tds according to supplier, code rectify

* minor fixes for loyalty program

* test case for single and cumulative threshold

* codacy fix
diff --git a/erpnext/accounts/doctype/loyalty_program/loyalty_program.py b/erpnext/accounts/doctype/loyalty_program/loyalty_program.py
index 96eda7f..d840304 100644
--- a/erpnext/accounts/doctype/loyalty_program/loyalty_program.py
+++ b/erpnext/accounts/doctype/loyalty_program/loyalty_program.py
@@ -13,28 +13,47 @@
 	pass
 
 
-def get_loyalty_details(customer, loyalty_program, expiry_date=None, company=None):
+def get_loyalty_details(customer, loyalty_program, expiry_date=None, company=None, include_expired_entry=False):
 	if not expiry_date:
 		expiry_date = today()
 
-	args_list = [customer, loyalty_program, expiry_date, expiry_date]
 	condition = ''
 	if company:
-		condition = " and company=%s "
-		args_list.append(company)
+		condition = " and company='%s' " % frappe.db.escape(company)
+	if not include_expired_entry:
+		condition += " and expiry_date>='%s' " % expiry_date
+
 	loyalty_point_details = frappe.db.sql('''select sum(loyalty_points) as loyalty_points,
 		sum(purchase_amount) as total_spent from `tabLoyalty Point Entry`
-		where customer=%s and loyalty_program=%s
-		and expiry_date>=%s and posting_date <= %s
+		where customer=%s and loyalty_program=%s and posting_date <= %s
 		{condition}
-		group by customer'''.format(condition=condition), tuple(args_list), as_dict=1)
+		group by customer'''.format(condition=condition),
+		(customer, loyalty_program, expiry_date), as_dict=1)
+
 	if loyalty_point_details:
 		return loyalty_point_details[0]
 	else:
 		return {"loyalty_points": 0, "total_spent": 0}
 
 @frappe.whitelist()
-def get_loyalty_program_details(customer, loyalty_program=None, expiry_date=None, company=None, silent=False):
+def get_loyalty_program_details_with_points(customer, loyalty_program=None, expiry_date=None, company=None, silent=False, include_expired_entry=False, current_transaction_amount=0):
+	lp_details = get_loyalty_program_details(customer, loyalty_program, company=company, silent=silent)
+	loyalty_program = frappe.get_doc("Loyalty Program", loyalty_program)
+	lp_details.update(get_loyalty_details(customer, loyalty_program.name, expiry_date, company, include_expired_entry))
+
+	tier_spent_level = sorted([d.as_dict() for d in loyalty_program.collection_rules],
+		key=lambda rule:rule.min_spent, reverse=True)
+	for i, d in enumerate(tier_spent_level):
+		if i==0 or (lp_details.total_spent+current_transaction_amount) <= d.min_spent:
+			lp_details.tier_name = d.tier_name
+			lp_details.collection_factor = d.collection_factor
+		else:
+			break
+
+	return lp_details
+
+@frappe.whitelist()
+def get_loyalty_program_details(customer, loyalty_program=None, expiry_date=None, company=None, silent=False, include_expired_entry=False):
 	lp_details = frappe._dict()
 
 	if not loyalty_program:
@@ -51,17 +70,6 @@
 	loyalty_program = frappe.get_doc("Loyalty Program", loyalty_program)
 	lp_details.update({"loyalty_program": loyalty_program.name})
 	lp_details.update(loyalty_program.as_dict())
-
-	lp_details.update(get_loyalty_details(customer, loyalty_program.name, expiry_date, company))
-
-	tier_spent_level = sorted([d.as_dict() for d in loyalty_program.collection_rules],
-		key=lambda rule:rule.min_spent, reverse=True)
-	for i, d in enumerate(tier_spent_level):
-		if i == 0 or lp_details.total_spent < d.min_spent:
-			lp_details.tier_name = d.tier_name
-			lp_details.collection_factor = d.collection_factor
-		else:
-			break
 	return lp_details
 
 @frappe.whitelist()
@@ -95,7 +103,7 @@
 		frappe.throw(_("The Loyalty Program isn't valid for the selected company"))
 
 	if loyalty_program and points_to_redeem:
-		loyalty_program_details = get_loyalty_program_details(ref_doc.customer, loyalty_program,
+		loyalty_program_details = get_loyalty_program_details_with_points(ref_doc.customer, loyalty_program,
 			posting_date, ref_doc.company)
 
 		if points_to_redeem > loyalty_program_details.loyalty_points:
diff --git a/erpnext/accounts/doctype/loyalty_program/test_loyalty_program.py b/erpnext/accounts/doctype/loyalty_program/test_loyalty_program.py
index 87a0cb8..99553b4 100644
--- a/erpnext/accounts/doctype/loyalty_program/test_loyalty_program.py
+++ b/erpnext/accounts/doctype/loyalty_program/test_loyalty_program.py
@@ -6,7 +6,7 @@
 import frappe
 import unittest
 from frappe.utils import today, cint, flt, getdate
-from erpnext.accounts.doctype.loyalty_program.loyalty_program import get_loyalty_program_details
+from erpnext.accounts.doctype.loyalty_program.loyalty_program import get_loyalty_program_details_with_points
 
 class TestLoyaltyProgram(unittest.TestCase):
 	@classmethod
@@ -15,6 +15,7 @@
 		create_records()
 
 	def test_loyalty_points_earned_single_tier(self):
+		frappe.db.set_value("Customer", "Test Loyalty Customer", "loyalty_program", "Test Single Loyalty")
 		# create a new sales invoice
 		si_original = create_sales_invoice_record()
 		si_original.insert()
@@ -50,6 +51,7 @@
 			frappe.delete_doc('Sales Invoice', d.name)
 
 	def test_loyalty_points_earned_multiple_tier(self):
+		frappe.db.set_value("Customer", "Test Loyalty Customer", "loyalty_program", "Test Multiple Loyalty")
 		# assign multiple tier program to the customer
 		customer = frappe.get_doc('Customer', {"customer_name": "Test Loyalty Customer"})
 		customer.loyalty_program = frappe.get_doc('Loyalty Program', {'loyalty_program_name': 'Test Multiple Loyalty'}).name
@@ -92,6 +94,7 @@
 
 	def test_cancel_sales_invoice(self):
 		''' cancelling the sales invoice should cancel the earned points'''
+		frappe.db.set_value("Customer", "Test Loyalty Customer", "loyalty_program", "Test Single Loyalty")
 		# create a new sales invoice
 		si = create_sales_invoice_record()
 		si.insert()
@@ -106,6 +109,7 @@
 		self.assertEqual(True, (lpe is None))
 
 	def test_sales_invoice_return(self):
+		frappe.db.set_value("Customer", "Test Loyalty Customer", "loyalty_program", "Test Single Loyalty")
 		# create a new sales invoice
 		si_original = create_sales_invoice_record(2)
 		si_original.conversion_rate = flt(1)
@@ -149,8 +153,8 @@
 		""", self.name)
 		return abs(flt(returned_amount[0][0])) if returned_amount else 0
 
-	lp_details = get_loyalty_program_details(self.customer, company=self.company,
-	loyalty_program=self.loyalty_program, expiry_date=self.posting_date)
+	lp_details = get_loyalty_program_details_with_points(self.customer, company=self.company,
+		loyalty_program=self.loyalty_program, expiry_date=self.posting_date, include_expired_entry=True)
 	if lp_details and getdate(lp_details.from_date) <= getdate(self.posting_date) and \
 		(not lp_details.to_date or getdate(lp_details.to_date) >= getdate(self.posting_date)):
 		returned_amount = get_returned_amount()
@@ -167,6 +171,7 @@
 		"company": '_Test Company',
 		"due_date": today(),
 		"posting_date": today(),
+		"currency": "INR",
 		"taxes_and_charges": "",
 		"debit_to": "Debtors - _TC",
 		"taxes": [],
@@ -174,6 +179,7 @@
 			'doctype': 'Sales Invoice Item',
 			'item_code': frappe.get_doc('Item', {'item_name': 'Loyal Item'}).name,
 			'qty': qty,
+			"rate": 10000,
 			'income_account': 'Sales - _TC',
 			'cost_center': 'Main - _TC',
 			'expense_account': 'Cost of Goods Sold - _TC'
diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py
index c981d08..456acba 100644
--- a/erpnext/accounts/doctype/payment_entry/payment_entry.py
+++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py
@@ -7,7 +7,7 @@
 from frappe import _, scrub, ValidationError
 from frappe.utils import flt, comma_or, nowdate, getdate
 from erpnext.accounts.utils import get_outstanding_invoices, get_account_currency, get_balance_on
-from erpnext.accounts.party import get_party_account, get_patry_tax_withholding_details
+from erpnext.accounts.party import get_party_account
 from erpnext.accounts.doctype.journal_entry.journal_entry import get_default_bank_cash_account
 from erpnext.setup.utils import get_exchange_rate
 from erpnext.accounts.general_ledger import make_gl_entries
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js
index 9a9fe20..14f7891 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js
@@ -242,6 +242,9 @@
 				price_list: this.frm.doc.buying_price_list
 			}, function() {
 				me.apply_pricing_rule();
+
+				me.frm.doc.apply_tds = me.frm.supplier_tds ? 1 : 0;
+				me.frm.set_df_property("apply_tds", "read_only", me.frm.supplier_tds ? 0 : 1);
 			})
 	},
 
@@ -493,6 +496,10 @@
 	},
 
 	onload: function(frm) {
+		if(frm.doc.__onload && !frm.doc.__onload.supplier_tds) {
+			me.frm.set_df_property("apply_tds", "read_only", 1);
+		}
+
 		$.each(["warehouse", "rejected_warehouse"], function(i, field) {
 			frm.set_query(field, "items", function() {
 				return {
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
index 6eb629c..60ccf1c 100755
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
@@ -288,6 +288,39 @@
    "bold": 0, 
    "collapsible": 0, 
    "columns": 0, 
+   "default": "",
+   "fieldname": "apply_tds",
+   "fieldtype": "Check",
+   "hidden": 0,
+   "ignore_user_permissions": 0,
+   "ignore_xss_filter": 0,
+   "in_filter": 0,
+   "in_global_search": 0,
+   "in_list_view": 0,
+   "in_standard_filter": 0,
+   "label": "Apply Tax Withholding Amount",
+   "length": 0,
+   "no_copy": 0,
+   "permlevel": 0,
+   "precision": "",
+   "print_hide": 1,
+   "print_hide_if_no_value": 0,
+   "read_only": 0,
+   "remember_last_selected_value": 0,
+   "report_hide": 0,
+   "reqd": 0,
+   "search_index": 0,
+   "set_only_once": 0,
+   "translatable": 0,
+   "unique": 0
+  },
+  {
+   "allow_bulk_edit": 0,
+   "allow_in_quick_entry": 0,
+   "allow_on_submit": 0,
+   "bold": 0,
+   "collapsible": 0,
+   "columns": 0,
    "fieldname": "column_break1", 
    "fieldtype": "Column Break", 
    "hidden": 0, 
@@ -4606,5 +4639,6 @@
  "timeline_field": "supplier", 
  "title_field": "title", 
  "track_changes": 1, 
- "track_seen": 0
+ "track_seen": 0,
+ "track_views": 0
 }
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
index c469a02..f343165 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
@@ -8,7 +8,7 @@
 import frappe.defaults
 
 from erpnext.controllers.buying_controller import BuyingController
-from erpnext.accounts.party import get_party_account, get_due_date, get_patry_tax_withholding_details
+from erpnext.accounts.party import get_party_account, get_due_date
 from erpnext.accounts.utils import get_account_currency, get_fiscal_year
 from erpnext.stock.doctype.purchase_receipt.purchase_receipt import update_billed_amount_based_on_po
 from erpnext.stock import get_warehouse_account_map
@@ -21,6 +21,7 @@
 from six import iteritems
 from erpnext.accounts.doctype.sales_invoice.sales_invoice import validate_inter_company_party, update_linked_invoice,\
 	unlink_inter_company_invoice
+from erpnext.accounts.doctype.tax_withholding_category.tax_withholding_category import get_party_tax_withholding_details
 
 form_grid_templates = {
 	"items": "templates/form_grid/item_grid.html"
@@ -42,6 +43,10 @@
 			'overflow_type': 'billing'
 		}]
 
+	def onload(self):
+		supplier_tds = frappe.db.get_value("Supplier", self.supplier, "tax_withholding_category")
+		self.set_onload("supplier_tds", supplier_tds)
+
 	def before_save(self):
 		if not self.on_hold:
 			self.release_date = ''
@@ -54,7 +59,10 @@
 			self.is_opening = 'No'
 
 		self.validate_posting_time()
+
+		# apply tax withholding only if checked and applicable
 		self.set_tax_withholding()
+
 		super(PurchaseInvoice, self).validate()
 
 		if not self.is_return:
@@ -768,14 +776,18 @@
 		self.db_set('release_date', None)
 
 	def set_tax_withholding(self):
-		tax_withholding_details = get_patry_tax_withholding_details(self)
-		for tax_details in tax_withholding_details:
-			if flt(self.get("rounded_total") or self.grand_total) >= flt(tax_details['threshold']):
-				if self.taxes:
-					if tax_details['tax']['description'] not in [tax.description for tax in self.taxes]:
-						self.append('taxes', tax_details['tax'])
-				else:
-					self.append('taxes', tax_details['tax'])
+		if not self.apply_tds:
+			return
+
+		tax_withholding_details = get_party_tax_withholding_details(self)
+		accounts = []
+		for d in self.taxes:
+			if d.account_head == tax_withholding_details.get("account_head"):
+				d.update(tax_withholding_details)
+			accounts.append(d.account_head)
+
+		if not accounts or tax_withholding_details.get("account_head") not in accounts:
+			self.append("taxes", tax_withholding_details)
 
 @frappe.whitelist()
 def make_debit_note(source_name, target_doc=None):
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
index 2c4125f..f559fc0 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
@@ -22,7 +22,7 @@
 from erpnext.setup.doctype.company.company import update_company_current_month_sales
 from erpnext.accounts.general_ledger import get_round_off_account_and_cost_center
 from erpnext.accounts.doctype.loyalty_program.loyalty_program import \
-	get_loyalty_program_details, get_loyalty_details, validate_loyalty_points
+	get_loyalty_program_details_with_points, get_loyalty_details, validate_loyalty_points
 
 from six import iteritems
 
@@ -973,12 +973,14 @@
 
 	# collection of the loyalty points, create the ledger entry for that.
 	def make_loyalty_point_entry(self):
-		lp_details = get_loyalty_program_details(self.customer, company=self.company,
-			loyalty_program=self.loyalty_program, expiry_date=self.posting_date)
+		returned_amount = self.get_returned_amount()
+		current_amount = flt(self.grand_total) - cint(self.loyalty_amount)
+		eligible_amount = current_amount - returned_amount
+		lp_details = get_loyalty_program_details_with_points(self.customer, company=self.company,
+			current_transaction_amount=current_amount, loyalty_program=self.loyalty_program,
+			expiry_date=self.posting_date, include_expired_entry=True)
 		if lp_details and getdate(lp_details.from_date) <= getdate(self.posting_date) and \
 			(not lp_details.to_date or getdate(lp_details.to_date) >= getdate(self.posting_date)):
-			returned_amount = self.get_returned_amount()
-			eligible_amount = flt(self.grand_total) - cint(self.loyalty_amount) - returned_amount
 			points_earned = cint(eligible_amount/lp_details.collection_factor)
 			doc = frappe.get_doc({
 				"doctype": "Loyalty Point Entry",
@@ -994,7 +996,7 @@
 			})
 			doc.flags.ignore_permissions = 1
 			doc.save()
-			frappe.db.set_value("Customer", self.customer, "loyalty_program_tier", lp_details.tier_name)
+			self.set_loyalty_program_tier()
 
 	# valdite the redemption and then delete the loyalty points earned on cancel of the invoice
 	def delete_loyalty_point_entry(self):
@@ -1009,9 +1011,12 @@
 		else:
 			frappe.db.sql('''delete from `tabLoyalty Point Entry` where sales_invoice=%s''', (self.name))
 			# Set loyalty program
-			lp_details = get_loyalty_program_details(self.customer, company=self.company,
-				loyalty_program=self.loyalty_program, expiry_date=self.posting_date)
-			frappe.db.set_value("Customer", self.customer, "loyalty_program_tier", lp_details.tier_name)
+			self.set_loyalty_program_tier()
+
+	def set_loyalty_program_tier(self):
+		lp_details = get_loyalty_program_details_with_points(self.customer, company=self.company,
+				loyalty_program=self.loyalty_program, include_expired_entry=True)
+		frappe.db.set_value("Customer", self.customer, "loyalty_program_tier", lp_details.tier_name)
 
 	def get_returned_amount(self):
 		returned_amount = frappe.db.sql("""
diff --git a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.json b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.json
index 8edaf01..f9160e2 100644
--- a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.json
+++ b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.json
@@ -15,51 +15,21 @@
  "fields": [
   {
    "allow_bulk_edit": 0, 
+   "allow_in_quick_entry": 0,
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
    "columns": 0, 
-   "fieldname": "percent_of_tax_withheld", 
-   "fieldtype": "Float", 
+   "fieldname": "category_name",
+   "fieldtype": "Data",
    "hidden": 0, 
    "ignore_user_permissions": 0, 
    "ignore_xss_filter": 0, 
    "in_filter": 0, 
    "in_global_search": 0, 
-   "in_list_view": 1, 
+   "in_list_view": 1,
    "in_standard_filter": 0, 
-   "label": "Percent of Tax Withheld", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 1, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "threshold", 
-   "fieldtype": "Currency", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Threshold", 
+   "label": "Category Name",
    "length": 0, 
    "no_copy": 0, 
    "permlevel": 0, 
@@ -77,100 +47,74 @@
   }, 
   {
    "allow_bulk_edit": 0, 
+   "allow_in_quick_entry": 0,
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
    "columns": 0, 
-   "fieldname": "column_break_3", 
-   "fieldtype": "Column Break", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
+   "fieldname": "section_break_8",
+   "fieldtype": "Section Break",
+   "hidden": 0,
+   "ignore_user_permissions": 0,
+   "ignore_xss_filter": 0,
+   "in_filter": 0,
+   "in_global_search": 0,
+   "in_list_view": 0,
+   "in_standard_filter": 0,
+   "label": "Tax Withholding Rates",
+   "length": 0,
+   "no_copy": 0,
+   "permlevel": 0,
+   "precision": "",
+   "print_hide": 0,
+   "print_hide_if_no_value": 0,
+   "read_only": 0,
+   "remember_last_selected_value": 0,
+   "report_hide": 0,
+   "reqd": 0,
+   "search_index": 0,
+   "set_only_once": 0,
+   "translatable": 0,
    "unique": 0
-  }, 
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "book_on_invoice", 
-   "fieldtype": "Check", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Book on Invoice", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
+   "allow_bulk_edit": 0,
+   "allow_in_quick_entry": 0,
+   "allow_on_submit": 0,
+   "bold": 0,
+   "collapsible": 0,
+   "columns": 0,
+   "fieldname": "rates",
+   "fieldtype": "Table",
+   "hidden": 0,
+   "ignore_user_permissions": 0,
+   "ignore_xss_filter": 0,
+   "in_filter": 0,
+   "in_global_search": 0,
+   "in_list_view": 0,
+   "in_standard_filter": 0,
+   "label": "Rates",
+   "length": 0,
+   "no_copy": 0,
+   "options": "Tax Withholding Rate",
+   "permlevel": 0,
+   "precision": "",
+   "print_hide": 0,
+   "print_hide_if_no_value": 0,
+   "read_only": 0,
+   "remember_last_selected_value": 0,
+   "report_hide": 0,
+   "reqd": 1,
+   "search_index": 0,
+   "set_only_once": 0,
+   "translatable": 0,
    "unique": 0
-  }, 
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "withhold_cumulative_tax_amount", 
-   "fieldtype": "Check", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Withhold Cumulative Tax Amount On First Invoice After Threshold", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
+   "allow_bulk_edit": 0,
+   "allow_in_quick_entry": 0,
+   "allow_on_submit": 0,
+   "bold": 0,
    "collapsible": 0, 
    "columns": 0, 
    "fieldname": "section_break_7", 
@@ -182,6 +126,7 @@
    "in_global_search": 0, 
    "in_list_view": 0, 
    "in_standard_filter": 0, 
+   "label": "Account Details",
    "length": 0, 
    "no_copy": 0, 
    "permlevel": 0, 
@@ -199,6 +144,7 @@
   }, 
   {
    "allow_bulk_edit": 0, 
+   "allow_in_quick_entry": 0,
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
@@ -240,7 +186,7 @@
  "issingle": 0, 
  "istable": 0, 
  "max_attachments": 0, 
- "modified": "2018-05-16 13:57:52.489773", 
+ "modified": "2018-07-17 22:53:26.193179",
  "modified_by": "Administrator", 
  "module": "Accounts", 
  "name": "Tax Withholding Category", 
@@ -305,12 +251,13 @@
    "write": 1
   }
  ], 
- "quick_entry": 1, 
+ "quick_entry": 0,
  "read_only": 0, 
  "read_only_onload": 0, 
  "show_name_in_global_search": 0, 
  "sort_field": "modified", 
  "sort_order": "DESC", 
  "track_changes": 1, 
- "track_seen": 0
+ "track_seen": 0,
+ "track_views": 0
 }
\ No newline at end of file
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 90c7d50..0dc4ecf 100644
--- a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py
+++ b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py
@@ -4,7 +4,115 @@
 
 from __future__ import unicode_literals
 import frappe
+from frappe import _
 from frappe.model.document import Document
+from frappe.utils import flt
+from erpnext.accounts.utils import get_fiscal_year
 
 class TaxWithholdingCategory(Document):
-	pass
\ No newline at end of file
+	pass
+
+def get_party_tax_withholding_details(ref_doc):
+	tax_withholding_category = frappe.db.get_value('Supplier', ref_doc.supplier, 'tax_withholding_category')
+	if not tax_withholding_category:
+		return
+
+	fy = get_fiscal_year(ref_doc.posting_date, company=ref_doc.company)
+	tax_details = get_tax_withholding_details(tax_withholding_category, fy[0], ref_doc.company)
+	tds_amount = get_tds_amount(ref_doc, tax_details, fy)
+	tax_row = get_tax_row(tax_details, tds_amount)
+	return tax_row
+
+def get_tax_withholding_details(tax_withholding_category, fiscal_year, company):
+	tax_withholding = frappe.get_doc("Tax Withholding Category", tax_withholding_category)
+
+	tax_rate_detail = get_tax_withholding_rates(tax_withholding, fiscal_year)
+
+	for account_detail in tax_withholding.accounts:
+		if company == account_detail.company:
+			return frappe._dict({
+				"account_head": account_detail.account,
+				"rate": tax_rate_detail.tax_withholding_rate,
+				"threshold": tax_rate_detail.single_threshold,
+				"cumulative_threshold": tax_rate_detail.cumulative_threshold,
+				"description": tax_withholding.category_name
+			})
+
+def get_tax_withholding_rates(tax_withholding, fiscal_year):
+	# returns the row that matches with the fiscal year from posting date
+	for rate in tax_withholding.rates:
+		if rate.fiscal_year == fiscal_year:
+			return rate
+
+	frappe.throw(_("No Tax Withholding data found for the current Fiscal Year."))
+
+def get_tax_row(tax_details, tds_amount):
+	return {
+		"category": "Total",
+		"add_deduct_tax": "Deduct",
+		"charge_type": "Actual",
+		"account_head": tax_details.account_head,
+		"description": tax_details.description,
+		"tax_amount": tds_amount
+	}
+
+def get_tds_amount(ref_doc, tax_details, fiscal_year_details):
+	fiscal_year, year_start_date, year_end_date = fiscal_year_details
+	tds_amount = 0
+
+	def _get_tds():
+		tds_amount = 0
+		if not tax_details.threshold or ref_doc.net_total >= tax_details.threshold:
+			tds_amount = ref_doc.net_total * tax_details.rate / 100
+		return tds_amount
+
+	if tax_details.cumulative_threshold:
+		entries = frappe.db.sql("""
+			select voucher_no, credit
+			from `tabGL Entry`
+			where party=%s and fiscal_year=%s and credit > 0
+		""", (ref_doc.supplier, fiscal_year), 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(ref_doc.supplier, fiscal_year)
+
+		tds_deducted = 0
+		if vouchers:
+			tds_deducted = flt(frappe.db.sql("""
+				select sum(credit)
+				from `tabGL Entry`
+				where account=%s and fiscal_year=%s and credit > 0
+					and voucher_no in ({0})
+			""".format(', '.join(["'%s'" % d for d in vouchers])),
+				(tax_details.account_head, fiscal_year))[0][0])
+
+		debit_note_amount = get_debit_note_amount(ref_doc.supplier, year_start_date, year_end_date)
+
+		total_invoiced_amount = supplier_credit_amount + tds_deducted \
+			+ flt(ref_doc.net_total) - debit_note_amount
+		if total_invoiced_amount >= tax_details.cumulative_threshold:
+			total_applicable_tds = total_invoiced_amount * tax_details.rate / 100
+			tds_amount = min(total_applicable_tds - tds_deducted, ref_doc.net_total)
+		else:
+			tds_amount = _get_tds()
+	else:
+		tds_amount = _get_tds()
+
+	return tds_amount
+
+def get_advance_vouchers(supplier, fiscal_year):
+	return frappe.db.sql_list("""
+		select distinct voucher_no
+		from `tabGL Entry`
+		where party=%s and fiscal_year=%s and debit > 0
+	""", (supplier, fiscal_year))
+
+def get_debit_note_amount(supplier, year_start_date, year_end_date):
+	return flt(frappe.db.sql("""
+		select abs(sum(net_total))
+		from `tabPurchase Invoice`
+		where supplier=%s and is_return=1 and docstatus=1
+			and posting_date between %s and %s
+	""", (supplier, year_start_date, year_end_date)))
\ 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 7d99011..20e1746 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
@@ -5,6 +5,107 @@
 
 import frappe
 import unittest
+from frappe.utils import today
+
+test_dependencies = ["Supplier Group"]
 
 class TestTaxWithholdingCategory(unittest.TestCase):
-	pass
+	@classmethod
+	def setUpClass(self):
+		# create relevant supplier, etc
+		create_records()
+
+	def test_single_threshold_tds(self):
+		frappe.db.set_value("Supplier", "Test TDS Supplier", "tax_withholding_category", "TDS - 194D - Individual")
+		pi = create_purchase_invoice()
+		pi.submit()
+
+		self.assertEqual(pi.taxes_and_charges_deducted, 800)
+		self.assertEqual(pi.grand_total, 15200)
+
+		# check gl entry for the purchase invoice
+		gl_entries = frappe.db.get_all('GL Entry', filters={'voucher_no': pi.name}, fields=["*"])
+		self.assertEqual(len(gl_entries), 3)
+		for d in gl_entries:
+			if d.account == pi.credit_to:
+				self.assertEqual(d.credit, 15200)
+			elif d.account == pi.items[0].get("expense_account"):
+				self.assertEqual(d.debit, 16000)
+			elif d.account == pi.taxes[0].get("account_head"):
+				self.assertEqual(d.credit, 800)
+			else:
+				raise ValueError("Account head does not match.")
+
+		# delete purchase invoice to avoid it interefering in other tests
+		pi.cancel()
+		frappe.delete_doc('Purchase Invoice', pi.name)
+
+	def test_cumulative_threshold_tds(self):
+		frappe.db.set_value("Supplier", "Test TDS Supplier", "tax_withholding_category", "TDS - 194C - Individual")
+		invoices = []
+
+		# create invoices for lower than single threshold tax rate
+		for _ in xrange(6):
+			pi = create_purchase_invoice()
+			pi.submit()
+			invoices.append(pi)
+
+		# create another invoice whose total when added to previously created invoice,
+		# surpasses cumulative threshhold
+		pi = create_purchase_invoice()
+		pi.submit()
+
+		# assert equal tax deduction on total invoice amount uptil now
+		self.assertEqual(pi.taxes_and_charges_deducted, 1120)
+		self.assertEqual(pi.grand_total, 14880)
+		invoices.append(pi)
+
+		# delete invoices to avoid clashing
+		for d in invoices:
+			d.cancel()
+			frappe.delete_doc("Purchase Invoice", d.name)
+
+def create_purchase_invoice(qty=1):
+	# return sales invoice doc object
+	item = frappe.get_doc('Item', {'item_name': 'TDS Item'})
+	pi = frappe.get_doc({
+		"doctype": "Purchase Invoice",
+		"posting_date": today(),
+		"apply_tds": 1,
+		"supplier": frappe.get_doc('Supplier', {"supplier_name": "Test TDS Supplier"}).name,
+		"company": '_Test Company',
+		"taxes_and_charges": "",
+		"currency": "INR",
+		"credit_to": "Creditors - _TC",
+		"taxes": [],
+		"items": [{
+			'doctype': 'Purchase Invoice Item',
+			'item_code': item.name,
+			'qty': qty,
+			'rate': 16000,
+			'cost_center': 'Main - _TC',
+			'expense_account': 'Stock Received But Not Billed - _TC'
+		}]
+	})
+
+	pi.save()
+	return pi
+
+def create_records():
+	# create a new supplier
+	frappe.get_doc({
+		"supplier_group": "_Test Supplier Group",
+		"supplier_name": "Test TDS Supplier",
+		"doctype": "Supplier",
+		"tax_withholding_category": "TDS - 194D - Individual"
+	}).insert()
+
+	# create an item
+	frappe.get_doc({
+		"doctype": "Item",
+		"item_code": "TDS Item",
+		"item_name": "TDS Item",
+		"item_group": "All Item Groups",
+		"company": "_Test Company",
+		"is_stock_item": 0,
+	}).insert()
\ No newline at end of file
diff --git a/erpnext/buying/doctype/party_tax_withholding_config/__init__.py b/erpnext/accounts/doctype/tax_withholding_rate/__init__.py
similarity index 100%
rename from erpnext/buying/doctype/party_tax_withholding_config/__init__.py
rename to erpnext/accounts/doctype/tax_withholding_rate/__init__.py
diff --git a/erpnext/buying/doctype/party_tax_withholding_config/party_tax_withholding_config.json b/erpnext/accounts/doctype/tax_withholding_rate/tax_withholding_rate.json
similarity index 70%
rename from erpnext/buying/doctype/party_tax_withholding_config/party_tax_withholding_config.json
rename to erpnext/accounts/doctype/tax_withholding_rate/tax_withholding_rate.json
index d2583e5..1e8194a 100644
--- a/erpnext/buying/doctype/party_tax_withholding_config/party_tax_withholding_config.json
+++ b/erpnext/accounts/doctype/tax_withholding_rate/tax_withholding_rate.json
@@ -4,7 +4,7 @@
  "allow_import": 0, 
  "allow_rename": 0, 
  "beta": 0, 
- "creation": "2018-05-11 13:32:33.825307", 
+ "creation": "2018-07-17 16:53:13.716665", 
  "custom": 0, 
  "docstatus": 0, 
  "doctype": "DocType", 
@@ -18,8 +18,8 @@
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "tax_withholding_category", 
+   "columns": 2, 
+   "fieldname": "fiscal_year", 
    "fieldtype": "Link", 
    "hidden": 0, 
    "ignore_user_permissions": 0, 
@@ -28,10 +28,10 @@
    "in_global_search": 0, 
    "in_list_view": 1, 
    "in_standard_filter": 0, 
-   "label": "Tax Withholding Category", 
+   "label": "Fiscal Year", 
    "length": 0, 
    "no_copy": 0, 
-   "options": "Tax Withholding Category", 
+   "options": "Fiscal Year", 
    "permlevel": 0, 
    "precision": "", 
    "print_hide": 0, 
@@ -39,7 +39,7 @@
    "read_only": 0, 
    "remember_last_selected_value": 0, 
    "report_hide": 0, 
-   "reqd": 0, 
+   "reqd": 1, 
    "search_index": 0, 
    "set_only_once": 0, 
    "translatable": 0, 
@@ -51,41 +51,8 @@
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "valid_till", 
-   "fieldtype": "Date", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 1, 
-   "in_standard_filter": 0, 
-   "label": "Valid Till", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fetch_from": "tax_withholding_category.percent_of_tax_withheld", 
-   "fieldname": "applicable_percent", 
+   "columns": 2, 
+   "fieldname": "tax_withholding_rate", 
    "fieldtype": "Float", 
    "hidden": 0, 
    "ignore_user_permissions": 0, 
@@ -94,7 +61,38 @@
    "in_global_search": 0, 
    "in_list_view": 1, 
    "in_standard_filter": 0, 
-   "label": "Applicable Percent", 
+   "label": "Tax Withholding Rate", 
+   "length": 0, 
+   "no_copy": 0, 
+   "permlevel": 0, 
+   "precision": "", 
+   "print_hide": 0, 
+   "print_hide_if_no_value": 0, 
+   "read_only": 0, 
+   "remember_last_selected_value": 0, 
+   "report_hide": 0, 
+   "reqd": 1, 
+   "search_index": 0, 
+   "set_only_once": 0, 
+   "translatable": 0, 
+   "unique": 0
+  }, 
+  {
+   "allow_bulk_edit": 0, 
+   "allow_in_quick_entry": 0, 
+   "allow_on_submit": 0, 
+   "bold": 0, 
+   "collapsible": 0, 
+   "columns": 0, 
+   "fieldname": "column_break_3", 
+   "fieldtype": "Column Break", 
+   "hidden": 0, 
+   "ignore_user_permissions": 0, 
+   "ignore_xss_filter": 0, 
+   "in_filter": 0, 
+   "in_global_search": 0, 
+   "in_list_view": 0, 
+   "in_standard_filter": 0, 
    "length": 0, 
    "no_copy": 0, 
    "permlevel": 0, 
@@ -116,17 +114,49 @@
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "certificate_received", 
-   "fieldtype": "Check", 
+   "columns": 3, 
+   "fieldname": "single_threshold", 
+   "fieldtype": "Currency", 
    "hidden": 0, 
    "ignore_user_permissions": 0, 
    "ignore_xss_filter": 0, 
    "in_filter": 0, 
    "in_global_search": 0, 
-   "in_list_view": 0, 
+   "in_list_view": 1, 
    "in_standard_filter": 0, 
-   "label": "Certificate Received", 
+   "label": "Single Transaction Threshold", 
+   "length": 0, 
+   "no_copy": 0, 
+   "permlevel": 0, 
+   "precision": "", 
+   "print_hide": 0, 
+   "print_hide_if_no_value": 0, 
+   "read_only": 0, 
+   "remember_last_selected_value": 0, 
+   "report_hide": 0, 
+   "reqd": 0, 
+   "search_index": 0, 
+   "set_only_once": 0, 
+   "translatable": 0, 
+   "unique": 0
+  }, 
+  {
+   "allow_bulk_edit": 0, 
+   "allow_in_quick_entry": 0, 
+   "allow_on_submit": 0, 
+   "bold": 0, 
+   "collapsible": 0, 
+   "columns": 3, 
+   "fieldname": "cumulative_threshold", 
+   "fieldtype": "Currency", 
+   "hidden": 0, 
+   "ignore_user_permissions": 0, 
+   "ignore_xss_filter": 0, 
+   "in_filter": 0, 
+   "in_global_search": 0, 
+   "in_list_view": 1, 
+   "in_standard_filter": 0, 
+   "label": "Cumulative Transaction Threshold", 
    "length": 0, 
    "no_copy": 0, 
    "permlevel": 0, 
@@ -153,10 +183,10 @@
  "issingle": 0, 
  "istable": 1, 
  "max_attachments": 0, 
- "modified": "2018-06-18 22:38:38.638721", 
+ "modified": "2018-07-17 17:13:09.819580", 
  "modified_by": "Administrator", 
- "module": "Buying", 
- "name": "Party Tax Withholding Config", 
+ "module": "Accounts", 
+ "name": "Tax Withholding Rate", 
  "name_case": "", 
  "owner": "Administrator", 
  "permissions": [], 
diff --git a/erpnext/buying/doctype/party_tax_withholding_config/party_tax_withholding_config.py b/erpnext/accounts/doctype/tax_withholding_rate/tax_withholding_rate.py
similarity index 84%
rename from erpnext/buying/doctype/party_tax_withholding_config/party_tax_withholding_config.py
rename to erpnext/accounts/doctype/tax_withholding_rate/tax_withholding_rate.py
index bec7e83..6e32abe 100644
--- a/erpnext/buying/doctype/party_tax_withholding_config/party_tax_withholding_config.py
+++ b/erpnext/accounts/doctype/tax_withholding_rate/tax_withholding_rate.py
@@ -6,5 +6,5 @@
 import frappe
 from frappe.model.document import Document
 
-class PartyTaxWithholdingConfig(Document):
+class TaxWithholdingRate(Document):
 	pass
diff --git a/erpnext/accounts/party.py b/erpnext/accounts/party.py
index d7fe123..cc90c74 100644
--- a/erpnext/accounts/party.py
+++ b/erpnext/accounts/party.py
@@ -65,6 +65,10 @@
 			"allocated_percentage": d.allocated_percentage or None
 		} for d in party.get("sales_team")]
 
+	# supplier tax withholding category
+	if party_type == "Supplier" and party:
+		out["supplier_tds"] = frappe.get_value(party_type, party.name, "tax_withholding_category")
+
 	return out
 
 def set_address_details(out, party, party_type, doctype=None, company=None):
@@ -507,57 +511,3 @@
 		return out[0][0]
 	else:
 		return ''
-
-def get_patry_tax_withholding_details(ref_doc):
-	supplier = frappe.get_doc("Supplier", ref_doc.supplier)
-	tax_withholding_details = []
-	for tax in supplier.tax_withholding_config:
-		tax_mapper = get_tax_mapper()
-
-		set_tax_withholding_details(tax_mapper, ref_doc, tax_withholding_category=tax.tax_withholding_category)
-
-		if tax.valid_till and date_diff(tax.valid_till, ref_doc.posting_date) > 0:
-			tax_mapper.update({
-				"rate": tax.applicable_percent
-			})
-
-		prepare_tax_withholding_details(tax_mapper, tax_withholding_details)
-
-	return tax_withholding_details
-
-def prepare_tax_withholding_details(tax_mapper, tax_withholding_details):
-	if tax_mapper.get('account_head'):
-		
-		tax_withholding_details.append({
-			"threshold": tax_mapper['threshold'],
-			"tax": tax_mapper
-		})
-
-		del tax_mapper['threshold']
-
-def set_tax_withholding_details(tax_mapper, ref_doc, tax_withholding_category=None, use_default=0):
-	if tax_withholding_category:
-		tax_withholding = frappe.get_doc("Tax Withholding Category", tax_withholding_category)
-
-	if tax_withholding.book_on_invoice and ref_doc.doctype=='Purchase Invoice' \
-		or ref_doc.doctype in ('Payment Entry', 'Journal Entry'):
-
-		for account_detail in tax_withholding.accounts:
-			if ref_doc.company == account_detail.company:
-				tax_mapper.update({
-					"account_head": account_detail.account,
-					"rate": tax_withholding.percent_of_tax_withheld,
-					"threshold": tax_withholding.threshold,
-					"description": tax_withholding.name
-				})
-
-def get_tax_mapper():
-	return {
-		"category": "Total",
-		"add_deduct_tax": "Deduct",
-		"charge_type": "On Net Total",
-		"rate": 0,
-		"description": '',
-		"account_head": '',
-		"threshold": 0.0
-	}
diff --git a/erpnext/buying/doctype/supplier/supplier.json b/erpnext/buying/doctype/supplier/supplier.json
index 1d342cd..45901dc 100644
--- a/erpnext/buying/doctype/supplier/supplier.json
+++ b/erpnext/buying/doctype/supplier/supplier.json
@@ -186,6 +186,39 @@
    "bold": 0, 
    "collapsible": 0, 
    "columns": 0, 
+   "fieldname": "tax_withholding_category",
+   "fieldtype": "Link",
+   "hidden": 0,
+   "ignore_user_permissions": 0,
+   "ignore_xss_filter": 0,
+   "in_filter": 0,
+   "in_global_search": 0,
+   "in_list_view": 0,
+   "in_standard_filter": 0,
+   "label": "Tax Withholding Category",
+   "length": 0,
+   "no_copy": 0,
+   "options": "Tax Withholding Category",
+   "permlevel": 0,
+   "precision": "",
+   "print_hide": 0,
+   "print_hide_if_no_value": 0,
+   "read_only": 0,
+   "remember_last_selected_value": 0,
+   "report_hide": 0,
+   "reqd": 0,
+   "search_index": 0,
+   "set_only_once": 0,
+   "translatable": 0,
+   "unique": 0
+  },
+  {
+   "allow_bulk_edit": 0,
+   "allow_in_quick_entry": 0,
+   "allow_on_submit": 0,
+   "bold": 0,
+   "collapsible": 0,
+   "columns": 0,
    "default": "0", 
    "fieldname": "is_internal_supplier", 
    "fieldtype": "Check", 
@@ -1159,39 +1192,6 @@
    "allow_in_quick_entry": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "tax_withholding_config", 
-   "fieldtype": "Table", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Tax Withholding Account", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Party Tax Withholding Config", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
    "collapsible": 1, 
    "collapsible_depends_on": "supplier_details", 
    "columns": 0, 
@@ -1363,7 +1363,7 @@
  "issingle": 0, 
  "istable": 0, 
  "max_attachments": 0, 
- "modified": "2018-06-26 13:44:53.124637", 
+ "modified": "2018-07-17 12:14:59.417939",
  "modified_by": "Administrator", 
  "module": "Buying", 
  "name": "Supplier", 
@@ -1512,5 +1512,6 @@
  "sort_order": "ASC", 
  "title_field": "supplier_name", 
  "track_changes": 1, 
- "track_seen": 0
+ "track_seen": 0,
+ "track_views": 0
 }
\ No newline at end of file
diff --git a/erpnext/public/js/utils/party.js b/erpnext/public/js/utils/party.js
index 378a803..3a5a062 100644
--- a/erpnext/public/js/utils/party.js
+++ b/erpnext/public/js/utils/party.js
@@ -42,6 +42,7 @@
 		args: args,
 		callback: function(r) {
 			if(r.message) {
+				frm.supplier_tds = r.message.supplier_tds;
 				frm.updating_party_details = true;
 				frappe.run_serially([
 					() => frm.set_value(r.message),
diff --git a/erpnext/regional/india/setup.py b/erpnext/regional/india/setup.py
index 7bb09e9..b16affa 100644
--- a/erpnext/regional/india/setup.py
+++ b/erpnext/regional/india/setup.py
@@ -7,6 +7,8 @@
 from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
 from frappe.permissions import add_permission
 from erpnext.regional.india import states
+from erpnext.accounts.utils import get_fiscal_year
+from frappe.utils import today
 
 def setup(company=None, patch=True):
 	make_custom_fields()
@@ -257,8 +259,10 @@
 			doc.insert()
 		except frappe.NameError:
 			pass
+		except frappe.DuplicateEntryError:
+			pass
 
-	# create tds fixtures
+	# create records for Tax Withholding Category
 	set_tax_withholding_category(company)
 
 def set_salary_components(docs):
@@ -279,19 +283,24 @@
 	if company and tds_account:
 		accounts = [dict(company=company, account=tds_account)]
 
-	tds = frappe.get_doc({
-		'doctype': 'Tax Withholding Category', 'name': 'TDS',
-		'percent_of_tax_withheld': 10,'threshold': 150000, 'book_on_invoice': 1,
-		'withhold_cumulative_tax_amount': 0, 'accounts': accounts
-	})
+	fiscal_year = get_fiscal_year(today(), company=accounts[0].get('company'))[0]
+	docs = get_tds_details(accounts, fiscal_year)
 
-	try:
-		tds.flags.ignore_permissions = True
-		tds.insert()
-	except frappe.DuplicateEntryError:
-		tds = frappe.get_doc("Tax Withholding Category", tds.get("name"))
-		tds.append("accounts", accounts[0])
-		tds.save()
+	for d in docs:
+		try:
+			doc = frappe.get_doc(d)
+			doc.flags.ignore_permissions = True
+			doc.insert()
+		except frappe.DuplicateEntryError:
+			doc = frappe.get_doc("Tax Withholding Category", d.get("name"))
+			doc.append("accounts", accounts[0])
+
+			# if fiscal year don't match with any of the already entered data, append rate row
+			fy_exist = [k for k in doc.get('rates') if k.get('fiscal_year')==fiscal_year]
+			if not fy_exist:
+				doc.append("rates", d.get('rates')[0])
+
+			doc.save()
 
 def set_tds_account(docs, company):
 	abbr = frappe.get_value("Company", company, "abbr")
@@ -301,3 +310,148 @@
 			"parent_account": "Duties and Taxes - {0}".format(abbr), "company": company
 		}
 	])
+
+def get_tds_details(accounts, fiscal_year):
+	# bootstrap default tax withholding sections
+	return [
+		dict(name="TDS - 194C - Company",
+			category_name="Payment to Contractors (Single / Aggregate)",
+			doctype="Tax Withholding Category", accounts=accounts,
+			rates=[{"fiscal_year": fiscal_year, "tax_withholding_rate": 2,
+			"single_threshold": 30000, "cumulative_threshold": 100000}]),
+		dict(name="TDS - 194C - Individual",
+			category_name="Payment to Contractors (Single / Aggregate)",
+			doctype="Tax Withholding Category", accounts=accounts,
+			rates=[{"fiscal_year": fiscal_year, "tax_withholding_rate": 1,
+			"single_threshold": 30000, "cumulative_threshold": 100000}]),
+		dict(name="TDS - 194C - No PAN / Invalid PAN",
+			category_name="Payment to Contractors (Single / Aggregate)",
+			doctype="Tax Withholding Category", accounts=accounts,
+			rates=[{"fiscal_year": fiscal_year, "tax_withholding_rate": 20,
+			"single_threshold": 30000, "cumulative_threshold": 100000}]),
+		dict(name="TDS - 194D - Company",
+			category_name="Insurance Commission",
+			doctype="Tax Withholding Category", accounts=accounts,
+			rates=[{"fiscal_year": fiscal_year, "tax_withholding_rate": 5,
+			"single_threshold": 15000, "cumulative_threshold": 0}]),
+		dict(name="TDS - 194D - Company Assessee",
+			category_name="Insurance Commission",
+			doctype="Tax Withholding Category", accounts=accounts,
+			rates=[{"fiscal_year": fiscal_year, "tax_withholding_rate": 10,
+			"single_threshold": 15000, "cumulative_threshold": 0}]),
+		dict(name="TDS - 194D - Individual",
+			category_name="Insurance Commission",
+			doctype="Tax Withholding Category", accounts=accounts,
+			rates=[{"fiscal_year": fiscal_year, "tax_withholding_rate": 5,
+			"single_threshold": 15000, "cumulative_threshold": 0}]),
+		dict(name="TDS - 194D - No PAN / Invalid PAN",
+			category_name="Insurance Commission",
+			doctype="Tax Withholding Category", accounts=accounts,
+			rates=[{"fiscal_year": fiscal_year, "tax_withholding_rate": 20,
+			"single_threshold": 15000, "cumulative_threshold": 0}]),
+		dict(name="TDS - 194DA - Company",
+			category_name="Non-exempt payments made under a life insurance policy",
+			doctype="Tax Withholding Category", accounts=accounts,
+			rates=[{"fiscal_year": fiscal_year, "tax_withholding_rate": 1,
+			"single_threshold": 100000, "cumulative_threshold": 0}]),
+		dict(name="TDS - 194DA - Individual",
+			category_name="Non-exempt payments made under a life insurance policy",
+			doctype="Tax Withholding Category", accounts=accounts,
+			rates=[{"fiscal_year": fiscal_year, "tax_withholding_rate": 1,
+			"single_threshold": 100000, "cumulative_threshold": 0}]),
+		dict(name="TDS - 194DA - No PAN / Invalid PAN",
+			category_name="Non-exempt payments made under a life insurance policy",
+			doctype="Tax Withholding Category", accounts=accounts,
+			rates=[{"fiscal_year": fiscal_year, "tax_withholding_rate": 20,
+			"single_threshold": 100000, "cumulative_threshold": 0}]),
+		dict(name="TDS - 194H - Company",
+			category_name="Commission / Brokerage",
+			doctype="Tax Withholding Category", accounts=accounts,
+			rates=[{"fiscal_year": fiscal_year, "tax_withholding_rate": 5,
+			"single_threshold": 15000, "cumulative_threshold": 0}]),
+		dict(name="TDS - 194H - Individual",
+			category_name="Commission / Brokerage",
+			doctype="Tax Withholding Category", accounts=accounts,
+			rates=[{"fiscal_year": fiscal_year, "tax_withholding_rate": 5,
+			"single_threshold": 15000, "cumulative_threshold": 0}]),
+		dict(name="TDS - 194H - No PAN / Invalid PAN",
+			category_name="Commission / Brokerage",
+			doctype="Tax Withholding Category", accounts=accounts,
+			rates=[{"fiscal_year": fiscal_year, "tax_withholding_rate": 20,
+			"single_threshold": 15000, "cumulative_threshold": 0}]),
+		dict(name="TDS - 194I - Rent - Company",
+			category_name="Rent",
+			doctype="Tax Withholding Category", accounts=accounts,
+			rates=[{"fiscal_year": fiscal_year, "tax_withholding_rate": 10,
+			"single_threshold": 180000, "cumulative_threshold": 0}]),
+		dict(name="TDS - 194I - Rent - Individual",
+			category_name="Rent",
+			doctype="Tax Withholding Category", accounts=accounts,
+			rates=[{"fiscal_year": fiscal_year, "tax_withholding_rate": 10,
+			"single_threshold": 180000, "cumulative_threshold": 0}]),
+		dict(name="TDS - 194I - Rent - No PAN / Invalid PAN",
+			category_name="Rent",
+			doctype="Tax Withholding Category", accounts=accounts,
+			rates=[{"fiscal_year": fiscal_year, "tax_withholding_rate": 20,
+			"single_threshold": 180000, "cumulative_threshold": 0}]),
+		dict(name="TDS - 194I - Rent/Machinery - Company",
+			category_name="Rent-Plant / Machinery",
+			doctype="Tax Withholding Category", accounts=accounts,
+			rates=[{"fiscal_year": fiscal_year, "tax_withholding_rate": 2,
+			"single_threshold": 180000, "cumulative_threshold": 0}]),
+		dict(name="TDS - 194I - Rent/Machinery - Individual",
+			category_name="Rent-Plant / Machinery",
+			doctype="Tax Withholding Category", accounts=accounts,
+			rates=[{"fiscal_year": fiscal_year, "tax_withholding_rate": 2,
+			"single_threshold": 180000, "cumulative_threshold": 0}]),
+		dict(name="TDS - 194I - Rent/Machinery - No PAN / Invalid PAN",
+			category_name="Rent-Plant / Machinery",
+			doctype="Tax Withholding Category", accounts=accounts,
+			rates=[{"fiscal_year": fiscal_year, "tax_withholding_rate": 20,
+			"single_threshold": 180000, "cumulative_threshold": 0}]),
+		dict(name="TDS - 194J - Professional Fees - Company",
+			category_name="Professional Fees",
+			doctype="Tax Withholding Category", accounts=accounts,
+			rates=[{"fiscal_year": fiscal_year, "tax_withholding_rate": 10,
+			"single_threshold": 30000, "cumulative_threshold": 0}]),
+		dict(name="TDS - 194J - Professional Fees - Individual",
+			category_name="Professional Fees",
+			doctype="Tax Withholding Category", accounts=accounts,
+			rates=[{"fiscal_year": fiscal_year, "tax_withholding_rate": 10,
+			"single_threshold": 30000, "cumulative_threshold": 0}]),
+		dict(name="TDS - 194J - Professional Fees - No PAN / Invalid PAN",
+			category_name="Professional Fees",
+			doctype="Tax Withholding Category", accounts=accounts,
+			rates=[{"fiscal_year": fiscal_year, "tax_withholding_rate": 20,
+			"single_threshold": 30000, "cumulative_threshold": 0}]),
+		dict(name="TDS - 194J - Director Fees - Company",
+			category_name="Director Fees",
+			doctype="Tax Withholding Category", accounts=accounts,
+			rates=[{"fiscal_year": fiscal_year, "tax_withholding_rate": 10,
+			"single_threshold": 0, "cumulative_threshold": 0}]),
+		dict(name="TDS - 194J - Director Fees - Individual",
+			category_name="Director Fees",
+			doctype="Tax Withholding Category", accounts=accounts,
+			rates=[{"fiscal_year": fiscal_year, "tax_withholding_rate": 10,
+			"single_threshold": 0, "cumulative_threshold": 0}]),
+		dict(name="TDS - 194J - Director Fees - No PAN / Invalid PAN",
+			category_name="Director Fees",
+			doctype="Tax Withholding Category", accounts=accounts,
+			rates=[{"fiscal_year": fiscal_year, "tax_withholding_rate": 20,
+			"single_threshold": 0, "cumulative_threshold": 0}]),
+		dict(name="TDS - 194 - Dividends - Company",
+			category_name="Dividends",
+			doctype="Tax Withholding Category", accounts=accounts,
+			rates=[{"fiscal_year": fiscal_year, "tax_withholding_rate": 10,
+			"single_threshold": 2500, "cumulative_threshold": 0}]),
+		dict(name="TDS - 194 - Dividends - Individual",
+			category_name="Dividends",
+			doctype="Tax Withholding Category", accounts=accounts,
+			rates=[{"fiscal_year": fiscal_year, "tax_withholding_rate": 10,
+			"single_threshold": 2500, "cumulative_threshold": 0}]),
+		dict(name="TDS - 194 - Dividends - No PAN / Invalid PAN",
+			category_name="Dividends",
+			doctype="Tax Withholding Category", accounts=accounts,
+			rates=[{"fiscal_year": fiscal_year, "tax_withholding_rate": 20,
+			"single_threshold": 2500, "cumulative_threshold": 0}])
+	]
diff --git a/erpnext/setup/doctype/company/test_company.py b/erpnext/setup/doctype/company/test_company.py
index 4ad8af8..c3260ab 100644
--- a/erpnext/setup/doctype/company/test_company.py
+++ b/erpnext/setup/doctype/company/test_company.py
@@ -8,6 +8,7 @@
 from erpnext.accounts.doctype.account.chart_of_accounts.chart_of_accounts import get_charts_for_country
 
 test_ignore = ["Account", "Cost Center", "Payment Terms Template", "Salary Component"]
+test_dependencies = ["Fiscal Year"]
 test_records = frappe.get_test_records('Company')
 
 class TestCompany(unittest.TestCase):
diff --git a/erpnext/templates/pages/order.py b/erpnext/templates/pages/order.py
index 70bd702..34985d9 100644
--- a/erpnext/templates/pages/order.py
+++ b/erpnext/templates/pages/order.py
@@ -36,8 +36,8 @@
 	# check for the loyalty program of the customer
 	customer_loyalty_program = frappe.db.get_value("Customer", context.doc.customer, "loyalty_program")	
 	if customer_loyalty_program:
-		from erpnext.accounts.doctype.loyalty_program.loyalty_program import get_loyalty_program_details
-		loyalty_program_details = get_loyalty_program_details(context.doc.customer, customer_loyalty_program)
+		from erpnext.accounts.doctype.loyalty_program.loyalty_program import get_loyalty_program_details_with_points
+		loyalty_program_details = get_loyalty_program_details_with_points(context.doc.customer, customer_loyalty_program)
 		context.available_loyalty_points = int(loyalty_program_details.get("loyalty_points"))
 
 def get_attachments(dt, dn):