fix: ensure tax withholding entries respect date range of category

(cherry picked from commit 9ead8d4e3f)
This commit is contained in:
ljain112
2026-04-23 13:35:46 +05:30
committed by Mergify
parent c4010b0581
commit 719d982a07
2 changed files with 179 additions and 18 deletions

View File

@@ -5,8 +5,7 @@ import datetime
from unittest.mock import patch from unittest.mock import patch
import frappe import frappe
from frappe.custom.doctype.custom_field.custom_field import create_custom_fields from frappe.utils import add_days, add_months, getdate, today
from frappe.utils import add_days, add_months, today
from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry
from erpnext.accounts.utils import get_fiscal_year from erpnext.accounts.utils import get_fiscal_year
@@ -1922,7 +1921,6 @@ class TestTaxWithholdingCategory(ERPNextTestSuite):
def set_previous_fy_and_tax_category(self): def set_previous_fy_and_tax_category(self):
test_company = "_Test Company" test_company = "_Test Company"
category = "Cumulative Threshold TDS"
def add_company_to_fy(fy, company): def add_company_to_fy(fy, company):
if not [x.company for x in fy.companies if x.company == company]: if not [x.company for x in fy.companies if x.company == company]:
@@ -1948,20 +1946,6 @@ class TestTaxWithholdingCategory(ERPNextTestSuite):
) )
self.prev_fy.save() self.prev_fy.save()
# setup tax withholding category for previous fiscal year
cat = frappe.get_doc("Tax Withholding Category", category)
cat.append(
"rates",
{
"from_date": self.prev_fy.year_start_date,
"to_date": self.prev_fy.year_end_date,
"tax_withholding_rate": 10,
"single_threshold": 0,
"cumulative_threshold": 30000,
},
)
cat.save()
def test_tds_across_fiscal_year(self): def test_tds_across_fiscal_year(self):
""" """
Advance TDS on previous fiscal year should be properly allocated on Invoices in upcoming fiscal year Advance TDS on previous fiscal year should be properly allocated on Invoices in upcoming fiscal year
@@ -1972,6 +1956,14 @@ class TestTaxWithholdingCategory(ERPNextTestSuite):
supplier = "Test TDS Supplier" supplier = "Test TDS Supplier"
# Cumulative threshold 30000 and tax rate 10% # Cumulative threshold 30000 and tax rate 10%
category = "Cumulative Threshold TDS" category = "Cumulative Threshold TDS"
create_tax_withholding_category(
category_name=category,
rate=10,
from_date=self.prev_fy.year_start_date,
to_date=self.prev_fy.year_end_date,
account="TDS - _TC",
cumulative_threshold=30000,
)
frappe.db.set_value( frappe.db.set_value(
"Supplier", "Supplier",
supplier, supplier,
@@ -2043,6 +2035,158 @@ class TestTaxWithholdingCategory(ERPNextTestSuite):
self.assertEqual(pi2.taxes, []) self.assertEqual(pi2.taxes, [])
self.assertEqual(payment.taxes[0].tax_amount, 6000) self.assertEqual(payment.taxes[0].tax_amount, 6000)
def test_threshold_resets_in_new_fiscal_year(self):
"""
Threshold entries from a previous FY must not carry over into the new FY.
"""
self.set_previous_fy_and_tax_category()
invoices = []
supplier = "Test TDS Supplier"
category = "Cumulative Threshold TDS"
create_tax_withholding_category(
category_name=category,
rate=10,
from_date=self.prev_fy.year_start_date,
to_date=self.prev_fy.year_end_date,
account="TDS - _TC",
cumulative_threshold=30000,
)
self.setup_party_with_category("Supplier", supplier, category)
prev_fy_date = add_days(self.prev_fy.year_end_date, -10)
# Previous FY: 3 invoices to cross the 30000 cumulative threshold
for _ in range(3):
pi = create_purchase_invoice(supplier=supplier, posting_date=prev_fy_date, set_posting_time=True)
pi.submit()
invoices.append(pi)
# Third invoice crosses the threshold - 3000 TDS deducted across all three
self.validate_tax_deduction(invoices[-1], 3000)
# Current FY: 10000 invoice - must be Under Withheld, threshold resets
pi_curr = create_purchase_invoice(supplier=supplier)
pi_curr.submit()
invoices.append(pi_curr)
self.validate_tax_deduction(pi_curr, 0)
self.validate_tax_withholding_entries(
"Purchase Invoice",
pi_curr.name,
[
self.get_tax_withholding_entry(
tax_withholding_category=category,
party_type="Supplier",
party=supplier,
taxable_doctype="Purchase Invoice",
taxable_name=pi_curr.name,
tax_rate=10.0,
taxable_amount=10000.0,
withholding_amount=0.0,
status="Under Withheld",
withholding_doctype=None,
withholding_name=None,
under_withheld_reason=None,
)
],
)
self.cleanup_invoices(invoices)
def test_tax_on_excess_threshold_resets_in_new_fiscal_year(self):
"""
For tax-on-excess categories, unused threshold must reset each FY.
"""
self.set_previous_fy_and_tax_category()
invoices = []
supplier = "Test TDS Supplier3"
category = "New TDS Category"
create_tax_withholding_category(
category_name=category,
rate=10,
from_date=self.prev_fy.year_start_date,
to_date=self.prev_fy.year_end_date,
account="TDS - _TC",
cumulative_threshold=30000,
tax_on_excess_amount=1,
round_off_tax_amount=1,
)
self.setup_party_with_category("Supplier", supplier, category)
prev_fy_date = add_days(self.prev_fy.year_end_date, -10)
for _ in range(2):
pi = create_purchase_invoice(supplier=supplier, posting_date=prev_fy_date, set_posting_time=True)
pi.submit()
invoices.append(pi)
pi3 = create_purchase_invoice(
supplier=supplier, rate=20000, posting_date=prev_fy_date, set_posting_time=True
)
pi3.submit()
invoices.append(pi3)
self.validate_tax_deduction(pi3, 1000)
self.validate_tax_withholding_entries(
"Purchase Invoice",
pi3.name,
[
self.get_tax_withholding_entry(
tax_withholding_category=category,
party_type="Supplier",
party=supplier,
taxable_doctype="Purchase Invoice",
taxable_name=pi3.name,
tax_rate=10.0,
taxable_amount=10000.0,
withholding_amount=0.0,
status="Settled",
withholding_doctype="Purchase Invoice",
withholding_name=pi3.name,
under_withheld_reason="Threshold Exemption",
),
self.get_tax_withholding_entry(
tax_withholding_category=category,
party_type="Supplier",
party=supplier,
taxable_doctype="Purchase Invoice",
taxable_name=pi3.name,
tax_rate=10.0,
taxable_amount=10000.0,
withholding_amount=1000.0,
status="Settled",
withholding_doctype="Purchase Invoice",
withholding_name=pi3.name,
under_withheld_reason=None,
),
],
)
# no excess, so no TDS
pi_curr = create_purchase_invoice(supplier=supplier, rate=30000)
pi_curr.submit()
invoices.append(pi_curr)
self.validate_tax_deduction(pi_curr, 0)
self.validate_tax_withholding_entries(
"Purchase Invoice",
pi_curr.name,
[
self.get_tax_withholding_entry(
tax_withholding_category=category,
party_type="Supplier",
party=supplier,
taxable_doctype="Purchase Invoice",
taxable_name=pi_curr.name,
tax_rate=10.0,
taxable_amount=30000.0,
withholding_amount=0.0,
status="Settled",
withholding_doctype="Purchase Invoice",
withholding_name=pi_curr.name,
under_withheld_reason="Threshold Exemption",
),
],
)
self.cleanup_invoices(invoices)
@ERPNextTestSuite.change_settings("Accounts Settings", {"delete_linked_ledger_entries": 1}) @ERPNextTestSuite.change_settings("Accounts Settings", {"delete_linked_ledger_entries": 1})
def test_tds_payment_entry_cancellation(self): def test_tds_payment_entry_cancellation(self):
""" """
@@ -3997,7 +4141,7 @@ def create_tax_withholding_category(
tax_deduction_basis="Net Total", tax_deduction_basis="Net Total",
): ):
if not frappe.db.exists("Tax Withholding Category", category_name): if not frappe.db.exists("Tax Withholding Category", category_name):
frappe.get_doc( doc = frappe.get_doc(
{ {
"doctype": "Tax Withholding Category", "doctype": "Tax Withholding Category",
"name": category_name, "name": category_name,
@@ -4018,6 +4162,22 @@ def create_tax_withholding_category(
"accounts": [{"company": "_Test Company", "account": account}], "accounts": [{"company": "_Test Company", "account": account}],
} }
).insert() ).insert()
else:
doc = frappe.get_doc("Tax Withholding Category", category_name)
if not any(getdate(r.from_date) == getdate(from_date) for r in doc.rates):
doc.append(
"rates",
{
"from_date": from_date,
"to_date": to_date,
"tax_withholding_rate": rate,
"single_threshold": single_threshold,
"cumulative_threshold": cumulative_threshold,
},
)
doc.save()
return doc
def create_lower_deduction_certificate( def create_lower_deduction_certificate(

View File

@@ -640,6 +640,7 @@ class TaxWithholdingController:
.where(entry.tax_withholding_category == category.name) .where(entry.tax_withholding_category == category.name)
.where(entry.company == self.doc.company) .where(entry.company == self.doc.company)
.where(entry.docstatus == 1) .where(entry.docstatus == 1)
.where(entry.taxable_date.between(category.from_date, category.to_date))
.groupby(entry.status) .groupby(entry.status)
) )