Merge pull request #16016 from rohitwaghchaure/fixed_tax_withholding_issue
[Fix] Tax Withholding, TDS is applying on the tax
This commit is contained in:
commit
136c4c4820
@ -830,6 +830,10 @@ class PurchaseInvoice(BuyingController):
|
|||||||
return
|
return
|
||||||
|
|
||||||
tax_withholding_details = get_party_tax_withholding_details(self)
|
tax_withholding_details = get_party_tax_withholding_details(self)
|
||||||
|
|
||||||
|
if not tax_withholding_details:
|
||||||
|
return
|
||||||
|
|
||||||
accounts = []
|
accounts = []
|
||||||
for d in self.taxes:
|
for d in self.taxes:
|
||||||
if d.account_head == tax_withholding_details.get("account_head"):
|
if d.account_head == tax_withholding_details.get("account_head"):
|
||||||
@ -839,6 +843,12 @@ class PurchaseInvoice(BuyingController):
|
|||||||
if not accounts or tax_withholding_details.get("account_head") not in accounts:
|
if not accounts or tax_withholding_details.get("account_head") not in accounts:
|
||||||
self.append("taxes", tax_withholding_details)
|
self.append("taxes", tax_withholding_details)
|
||||||
|
|
||||||
|
to_remove = [d for d in self.taxes
|
||||||
|
if not d.tax_amount and d.account_head == tax_withholding_details.get("account_head")]
|
||||||
|
|
||||||
|
for d in to_remove:
|
||||||
|
self.remove(d)
|
||||||
|
|
||||||
# calculate totals again after applying TDS
|
# calculate totals again after applying TDS
|
||||||
self.calculate_taxes_and_totals()
|
self.calculate_taxes_and_totals()
|
||||||
|
|
||||||
|
@ -24,6 +24,7 @@ def get_party_tax_withholding_details(ref_doc):
|
|||||||
.format(tax_withholding_category, ref_doc.company))
|
.format(tax_withholding_category, ref_doc.company))
|
||||||
tds_amount = get_tds_amount(ref_doc, tax_details, fy)
|
tds_amount = get_tds_amount(ref_doc, tax_details, fy)
|
||||||
tax_row = get_tax_row(tax_details, tds_amount)
|
tax_row = get_tax_row(tax_details, tds_amount)
|
||||||
|
|
||||||
return tax_row
|
return tax_row
|
||||||
|
|
||||||
def get_tax_withholding_details(tax_withholding_category, fiscal_year, company):
|
def get_tax_withholding_details(tax_withholding_category, fiscal_year, company):
|
||||||
@ -62,46 +63,64 @@ def get_tax_row(tax_details, tds_amount):
|
|||||||
def get_tds_amount(ref_doc, tax_details, fiscal_year_details):
|
def get_tds_amount(ref_doc, tax_details, fiscal_year_details):
|
||||||
fiscal_year, year_start_date, year_end_date = fiscal_year_details
|
fiscal_year, year_start_date, year_end_date = fiscal_year_details
|
||||||
tds_amount = 0
|
tds_amount = 0
|
||||||
|
tds_deducted = 0
|
||||||
|
|
||||||
def _get_tds():
|
def _get_tds(amount):
|
||||||
tds_amount = 0
|
if amount <= 0:
|
||||||
if not tax_details.threshold or ref_doc.net_total >= tax_details.threshold:
|
return 0
|
||||||
tds_amount = ref_doc.net_total * tax_details.rate / 100
|
|
||||||
return tds_amount
|
|
||||||
|
|
||||||
if tax_details.cumulative_threshold:
|
return amount * tax_details.rate / 100
|
||||||
entries = frappe.db.sql("""
|
|
||||||
|
entries = frappe.db.sql("""
|
||||||
select voucher_no, credit
|
select voucher_no, credit
|
||||||
from `tabGL Entry`
|
from `tabGL Entry`
|
||||||
where party=%s and fiscal_year=%s and credit > 0
|
where party=%s and fiscal_year=%s and credit > 0
|
||||||
""", (ref_doc.supplier, fiscal_year), as_dict=1)
|
""", (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]
|
||||||
|
advance_vouchers = get_advance_vouchers(ref_doc.supplier, fiscal_year)
|
||||||
|
|
||||||
vouchers = [d.voucher_no for d in entries]
|
tds_vouchers = vouchers + advance_vouchers
|
||||||
vouchers += get_advance_vouchers(ref_doc.supplier, fiscal_year)
|
|
||||||
|
|
||||||
tds_deducted = 0
|
if tds_vouchers:
|
||||||
if vouchers:
|
tds_deducted = frappe.db.sql("""
|
||||||
tds_deducted = flt(frappe.db.sql("""
|
SELECT sum(credit) FROM `tabGL Entry`
|
||||||
select sum(credit)
|
WHERE
|
||||||
from `tabGL Entry`
|
account=%s and fiscal_year=%s and credit > 0
|
||||||
where account=%s and fiscal_year=%s and credit > 0
|
and voucher_no in ({0})""". format(','.join(['%s'] * len(tds_vouchers))),
|
||||||
and voucher_no in ({0})
|
((tax_details.account_head, fiscal_year) + tuple(tds_vouchers)))
|
||||||
""".format(', '.join(["'%s'" % d for d in vouchers])),
|
|
||||||
(tax_details.account_head, fiscal_year))[0][0])
|
tds_deducted = tds_deducted[0][0] if tds_deducted and tds_deducted[0][0] else 0
|
||||||
|
|
||||||
|
if tds_deducted:
|
||||||
|
tds_amount = _get_tds(ref_doc.net_total)
|
||||||
|
else:
|
||||||
|
supplier_credit_amount = frappe.get_all('Purchase Invoice Item',
|
||||||
|
fields = ['sum(net_amount)'],
|
||||||
|
filters = {'parent': ('in', vouchers), 'docstatus': 1}, as_list=1)
|
||||||
|
|
||||||
|
supplier_credit_amount = (supplier_credit_amount[0][0]
|
||||||
|
if supplier_credit_amount and supplier_credit_amount[0][0] else 0)
|
||||||
|
|
||||||
|
jv_supplier_credit_amt = frappe.get_all('Journal Entry Account',
|
||||||
|
fields = ['sum(credit_in_account_currency)'],
|
||||||
|
filters = {
|
||||||
|
'parent': ('in', vouchers), 'docstatus': 1,
|
||||||
|
'party': ref_doc.supplier,
|
||||||
|
'reference_type': ('not in', ['Purchase Invoice'])
|
||||||
|
}, as_list=1)
|
||||||
|
|
||||||
|
supplier_credit_amount += (jv_supplier_credit_amt[0][0]
|
||||||
|
if jv_supplier_credit_amt and jv_supplier_credit_amt[0][0] else 0)
|
||||||
|
|
||||||
|
supplier_credit_amount += ref_doc.net_total
|
||||||
|
|
||||||
debit_note_amount = get_debit_note_amount(ref_doc.supplier, year_start_date, year_end_date)
|
debit_note_amount = get_debit_note_amount(ref_doc.supplier, year_start_date, year_end_date)
|
||||||
|
supplier_credit_amount -= debit_note_amount
|
||||||
|
|
||||||
total_invoiced_amount = supplier_credit_amount + tds_deducted \
|
if ((tax_details.get('threshold', 0) and supplier_credit_amount >= tax_details.threshold)
|
||||||
+ flt(ref_doc.net_total) - debit_note_amount
|
or (tax_details.get('cumulative_threshold', 0) and supplier_credit_amount >= tax_details.cumulative_threshold)):
|
||||||
if total_invoiced_amount >= tax_details.cumulative_threshold:
|
tds_amount = _get_tds(supplier_credit_amount)
|
||||||
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
|
return tds_amount
|
||||||
|
|
||||||
@ -114,7 +133,7 @@ def get_advance_vouchers(supplier, fiscal_year=None, company=None, from_date=Non
|
|||||||
select distinct voucher_no
|
select distinct voucher_no
|
||||||
from `tabGL Entry`
|
from `tabGL Entry`
|
||||||
where party=%s and %s and debit > 0
|
where party=%s and %s and debit > 0
|
||||||
""", (supplier, condition))
|
""", (supplier, condition)) or []
|
||||||
|
|
||||||
def get_debit_note_amount(supplier, year_start_date, year_end_date, company=None):
|
def get_debit_note_amount(supplier, year_start_date, year_end_date, company=None):
|
||||||
condition = ""
|
condition = ""
|
||||||
|
@ -6,6 +6,7 @@ from __future__ import unicode_literals
|
|||||||
import frappe
|
import frappe
|
||||||
import unittest
|
import unittest
|
||||||
from frappe.utils import today
|
from frappe.utils import today
|
||||||
|
from erpnext.accounts.utils import get_fiscal_year
|
||||||
|
|
||||||
test_dependencies = ["Supplier Group"]
|
test_dependencies = ["Supplier Group"]
|
||||||
|
|
||||||
@ -14,65 +15,105 @@ class TestTaxWithholdingCategory(unittest.TestCase):
|
|||||||
def setUpClass(self):
|
def setUpClass(self):
|
||||||
# create relevant supplier, etc
|
# create relevant supplier, etc
|
||||||
create_records()
|
create_records()
|
||||||
|
create_tax_with_holding_category()
|
||||||
|
|
||||||
def test_single_threshold_tds(self):
|
def test_cumulative_threshold_tds(self):
|
||||||
frappe.db.set_value("Supplier", "Test TDS Supplier", "tax_withholding_category", "TDS - 194D - Individual")
|
frappe.db.set_value("Supplier", "Test TDS Supplier", "tax_withholding_category", "Cumulative Threshold TDS")
|
||||||
pi = create_purchase_invoice()
|
invoices = []
|
||||||
|
|
||||||
|
# create invoices for lower than single threshold tax rate
|
||||||
|
for _ in xrange(2):
|
||||||
|
pi = create_purchase_invoice(supplier = "Test TDS Supplier")
|
||||||
|
pi.submit()
|
||||||
|
invoices.append(pi)
|
||||||
|
|
||||||
|
# create another invoice whose total when added to previously created invoice,
|
||||||
|
# surpasses cumulative threshhold
|
||||||
|
pi = create_purchase_invoice(supplier = "Test TDS Supplier")
|
||||||
pi.submit()
|
pi.submit()
|
||||||
|
|
||||||
self.assertEqual(pi.taxes_and_charges_deducted, 800)
|
# assert equal tax deduction on total invoice amount uptil now
|
||||||
self.assertEqual(pi.grand_total, 15200)
|
self.assertEqual(pi.taxes_and_charges_deducted, 3000)
|
||||||
|
self.assertEqual(pi.grand_total, 7000)
|
||||||
|
invoices.append(pi)
|
||||||
|
|
||||||
|
# TDS is already deducted, so from onward system will deduct the TDS on every invoice
|
||||||
|
pi = create_purchase_invoice(supplier = "Test TDS Supplier", rate=5000)
|
||||||
|
pi.submit()
|
||||||
|
|
||||||
|
# assert equal tax deduction on total invoice amount uptil now
|
||||||
|
self.assertEqual(pi.taxes_and_charges_deducted, 500)
|
||||||
|
invoices.append(pi)
|
||||||
|
|
||||||
|
#delete invoices to avoid clashing
|
||||||
|
for d in invoices:
|
||||||
|
d.cancel()
|
||||||
|
frappe.delete_doc("Purchase Invoice", d.name)
|
||||||
|
|
||||||
|
def test_single_threshold_tds(self):
|
||||||
|
invoices = []
|
||||||
|
frappe.db.set_value("Supplier", "Test TDS Supplier1", "tax_withholding_category", "Single Threshold TDS")
|
||||||
|
pi = create_purchase_invoice(supplier = "Test TDS Supplier1", rate = 20000)
|
||||||
|
pi.submit()
|
||||||
|
invoices.append(pi)
|
||||||
|
|
||||||
|
self.assertEqual(pi.taxes_and_charges_deducted, 2000)
|
||||||
|
self.assertEqual(pi.grand_total, 18000)
|
||||||
|
|
||||||
# check gl entry for the purchase invoice
|
# check gl entry for the purchase invoice
|
||||||
gl_entries = frappe.db.get_all('GL Entry', filters={'voucher_no': pi.name}, fields=["*"])
|
gl_entries = frappe.db.get_all('GL Entry', filters={'voucher_no': pi.name}, fields=["*"])
|
||||||
self.assertEqual(len(gl_entries), 3)
|
self.assertEqual(len(gl_entries), 3)
|
||||||
for d in gl_entries:
|
for d in gl_entries:
|
||||||
if d.account == pi.credit_to:
|
if d.account == pi.credit_to:
|
||||||
self.assertEqual(d.credit, 15200)
|
self.assertEqual(d.credit, 18000)
|
||||||
elif d.account == pi.items[0].get("expense_account"):
|
elif d.account == pi.items[0].get("expense_account"):
|
||||||
self.assertEqual(d.debit, 16000)
|
self.assertEqual(d.debit, 20000)
|
||||||
elif d.account == pi.taxes[0].get("account_head"):
|
elif d.account == pi.taxes[0].get("account_head"):
|
||||||
self.assertEqual(d.credit, 800)
|
self.assertEqual(d.credit, 2000)
|
||||||
else:
|
else:
|
||||||
raise ValueError("Account head does not match.")
|
raise ValueError("Account head does not match.")
|
||||||
|
|
||||||
# delete purchase invoice to avoid it interefering in other tests
|
pi = create_purchase_invoice(supplier = "Test TDS Supplier1")
|
||||||
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()
|
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)
|
invoices.append(pi)
|
||||||
|
|
||||||
|
# TDS amount is 1000 because in previous invoices it's already deducted
|
||||||
|
self.assertEqual(pi.taxes_and_charges_deducted, 1000)
|
||||||
|
|
||||||
# delete invoices to avoid clashing
|
# delete invoices to avoid clashing
|
||||||
for d in invoices:
|
for d in invoices:
|
||||||
d.cancel()
|
d.cancel()
|
||||||
frappe.delete_doc("Purchase Invoice", d.name)
|
frappe.delete_doc("Purchase Invoice", d.name)
|
||||||
|
|
||||||
def create_purchase_invoice(qty=1):
|
def test_single_threshold_tds_with_previous_vouchers(self):
|
||||||
|
invoices = []
|
||||||
|
frappe.db.set_value("Supplier", "Test TDS Supplier2", "tax_withholding_category", "Single Threshold TDS")
|
||||||
|
pi = create_purchase_invoice(supplier="Test TDS Supplier2")
|
||||||
|
pi.submit()
|
||||||
|
invoices.append(pi)
|
||||||
|
|
||||||
|
pi = create_purchase_invoice(supplier="Test TDS Supplier2")
|
||||||
|
pi.submit()
|
||||||
|
invoices.append(pi)
|
||||||
|
|
||||||
|
self.assertEqual(pi.taxes_and_charges_deducted, 2000)
|
||||||
|
self.assertEqual(pi.grand_total, 8000)
|
||||||
|
|
||||||
|
# delete invoices to avoid clashing
|
||||||
|
for d in invoices:
|
||||||
|
d.cancel()
|
||||||
|
frappe.delete_doc("Purchase Invoice", d.name)
|
||||||
|
|
||||||
|
def create_purchase_invoice(**args):
|
||||||
# return sales invoice doc object
|
# return sales invoice doc object
|
||||||
item = frappe.get_doc('Item', {'item_name': 'TDS Item'})
|
item = frappe.get_doc('Item', {'item_name': 'TDS Item'})
|
||||||
|
|
||||||
|
args = frappe._dict(args)
|
||||||
pi = frappe.get_doc({
|
pi = frappe.get_doc({
|
||||||
"doctype": "Purchase Invoice",
|
"doctype": "Purchase Invoice",
|
||||||
"posting_date": today(),
|
"posting_date": today(),
|
||||||
"apply_tds": 1,
|
"apply_tds": 1,
|
||||||
"supplier": frappe.get_doc('Supplier', {"supplier_name": "Test TDS Supplier"}).name,
|
"supplier": args.supplier,
|
||||||
"company": '_Test Company',
|
"company": '_Test Company',
|
||||||
"taxes_and_charges": "",
|
"taxes_and_charges": "",
|
||||||
"currency": "INR",
|
"currency": "INR",
|
||||||
@ -81,8 +122,8 @@ def create_purchase_invoice(qty=1):
|
|||||||
"items": [{
|
"items": [{
|
||||||
'doctype': 'Purchase Invoice Item',
|
'doctype': 'Purchase Invoice Item',
|
||||||
'item_code': item.name,
|
'item_code': item.name,
|
||||||
'qty': qty,
|
'qty': args.qty or 1,
|
||||||
'rate': 16000,
|
'rate': args.rate or 10000,
|
||||||
'cost_center': 'Main - _TC',
|
'cost_center': 'Main - _TC',
|
||||||
'expense_account': 'Stock Received But Not Billed - _TC'
|
'expense_account': 'Stock Received But Not Billed - _TC'
|
||||||
}]
|
}]
|
||||||
@ -92,20 +133,73 @@ def create_purchase_invoice(qty=1):
|
|||||||
return pi
|
return pi
|
||||||
|
|
||||||
def create_records():
|
def create_records():
|
||||||
# create a new supplier
|
# create a new suppliers
|
||||||
frappe.get_doc({
|
for name in ['Test TDS Supplier', 'Test TDS Supplier1', 'Test TDS Supplier2']:
|
||||||
"supplier_group": "_Test Supplier Group",
|
if frappe.db.exists('Supplier', name):
|
||||||
"supplier_name": "Test TDS Supplier",
|
continue
|
||||||
"doctype": "Supplier",
|
|
||||||
"tax_withholding_category": "TDS - 194D - Individual"
|
frappe.get_doc({
|
||||||
}).insert()
|
"supplier_group": "_Test Supplier Group",
|
||||||
|
"supplier_name": name,
|
||||||
|
"doctype": "Supplier",
|
||||||
|
}).insert()
|
||||||
|
|
||||||
# create an item
|
# create an item
|
||||||
frappe.get_doc({
|
if not frappe.db.exists('Item', "TDS Item"):
|
||||||
"doctype": "Item",
|
frappe.get_doc({
|
||||||
"item_code": "TDS Item",
|
"doctype": "Item",
|
||||||
"item_name": "TDS Item",
|
"item_code": "TDS Item",
|
||||||
"item_group": "All Item Groups",
|
"item_name": "TDS Item",
|
||||||
"company": "_Test Company",
|
"item_group": "All Item Groups",
|
||||||
"is_stock_item": 0,
|
"is_stock_item": 0,
|
||||||
}).insert()
|
}).insert()
|
||||||
|
|
||||||
|
# create an account
|
||||||
|
if not frappe.db.exists("Account", "TDS - _TC"):
|
||||||
|
frappe.get_doc({
|
||||||
|
'doctype': 'Account',
|
||||||
|
'company': '_Test Company',
|
||||||
|
'account_name': 'TDS',
|
||||||
|
'parent_account': 'Tax Assets - _TC',
|
||||||
|
'report_type': 'Balance Sheet',
|
||||||
|
'root_type': 'Asset'
|
||||||
|
}).insert()
|
||||||
|
|
||||||
|
def create_tax_with_holding_category():
|
||||||
|
fiscal_year = get_fiscal_year(today(), company="_Test Company")[0]
|
||||||
|
|
||||||
|
# Cummulative thresold
|
||||||
|
if not frappe.db.exists("Tax Withholding Category", "Cumulative Threshold TDS"):
|
||||||
|
frappe.get_doc({
|
||||||
|
"doctype": "Tax Withholding Category",
|
||||||
|
"name": "Cumulative Threshold TDS",
|
||||||
|
"category_name": "10% TDS",
|
||||||
|
"rates": [{
|
||||||
|
'fiscal_year': fiscal_year,
|
||||||
|
'tax_withholding_rate': 10,
|
||||||
|
'single_threshold': 0,
|
||||||
|
'cumulative_threshold': 30000.00
|
||||||
|
}],
|
||||||
|
"accounts": [{
|
||||||
|
'company': '_Test Company',
|
||||||
|
'account': 'TDS - _TC'
|
||||||
|
}]
|
||||||
|
}).insert()
|
||||||
|
|
||||||
|
# Single thresold
|
||||||
|
if not frappe.db.exists("Tax Withholding Category", "Single Threshold TDS"):
|
||||||
|
frappe.get_doc({
|
||||||
|
"doctype": "Tax Withholding Category",
|
||||||
|
"name": "Single Threshold TDS",
|
||||||
|
"category_name": "10% TDS",
|
||||||
|
"rates": [{
|
||||||
|
'fiscal_year': fiscal_year,
|
||||||
|
'tax_withholding_rate': 10,
|
||||||
|
'single_threshold': 20000.00,
|
||||||
|
'cumulative_threshold': 0
|
||||||
|
}],
|
||||||
|
"accounts": [{
|
||||||
|
'company': '_Test Company',
|
||||||
|
'account': 'TDS - _TC'
|
||||||
|
}]
|
||||||
|
}).insert()
|
Loading…
x
Reference in New Issue
Block a user