mirror of
https://github.com/frappe/erpnext.git
synced 2026-05-02 13:08:27 +00:00
fix: ensure tax withholding entries respect date range of category
(cherry picked from commit 9ead8d4e3f)
This commit is contained in:
@@ -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(
|
||||||
|
|||||||
@@ -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)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user