Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Tax withholding post LDC limit consumed #36611

Merged
merged 2 commits into from
Aug 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -262,14 +262,20 @@ def get_tax_amount(party_type, parties, inv, tax_details, posting_date, pan_no=N
if tax_deducted:
net_total = inv.tax_withholding_net_total
if ldc:
tax_amount = get_tds_amount_from_ldc(ldc, parties, tax_details, posting_date, net_total)
limit_consumed = get_limit_consumed(ldc, parties)
if is_valid_certificate(ldc, posting_date, limit_consumed):
tax_amount = get_lower_deduction_amount(
net_total, limit_consumed, ldc.certificate_limit, ldc.rate, tax_details
)
else:
tax_amount = net_total * tax_details.rate / 100 if net_total > 0 else 0
else:
tax_amount = net_total * tax_details.rate / 100 if net_total > 0 else 0

# once tds is deducted, not need to add vouchers in the invoice
voucher_wise_amount = {}
else:
tax_amount = get_tds_amount(ldc, parties, inv, tax_details, tax_deducted, vouchers)
tax_amount = get_tds_amount(ldc, parties, inv, tax_details, vouchers)

elif party_type == "Customer":
if tax_deducted:
Expand Down Expand Up @@ -416,7 +422,7 @@ def get_deducted_tax(taxable_vouchers, tax_details):
return sum(entries)


def get_tds_amount(ldc, parties, inv, tax_details, tax_deducted, vouchers):
def get_tds_amount(ldc, parties, inv, tax_details, vouchers):
tds_amount = 0
invoice_filters = {"name": ("in", vouchers), "docstatus": 1, "apply_tds": 1}

Expand Down Expand Up @@ -496,15 +502,10 @@ def get_tds_amount(ldc, parties, inv, tax_details, tax_deducted, vouchers):
net_total += inv.tax_withholding_net_total
supp_credit_amt = net_total - cumulative_threshold

if ldc and is_valid_certificate(
ldc.valid_from,
ldc.valid_upto,
inv.get("posting_date") or inv.get("transaction_date"),
tax_deducted,
inv.tax_withholding_net_total,
ldc.certificate_limit,
):
tds_amount = get_ltds_amount(supp_credit_amt, 0, ldc.certificate_limit, ldc.rate, tax_details)
if ldc and is_valid_certificate(ldc, inv.get("posting_date") or inv.get("transaction_date"), 0):
tds_amount = get_lower_deduction_amount(
supp_credit_amt, 0, ldc.certificate_limit, ldc.rate, tax_details
)
else:
tds_amount = supp_credit_amt * tax_details.rate / 100 if supp_credit_amt > 0 else 0

Expand Down Expand Up @@ -582,8 +583,7 @@ def get_invoice_total_without_tcs(inv, tax_details):
return inv.grand_total - tcs_tax_row_amount


def get_tds_amount_from_ldc(ldc, parties, tax_details, posting_date, net_total):
tds_amount = 0
def get_limit_consumed(ldc, parties):
limit_consumed = frappe.db.get_value(
"Purchase Invoice",
{
Expand All @@ -597,37 +597,29 @@ def get_tds_amount_from_ldc(ldc, parties, tax_details, posting_date, net_total):
"sum(tax_withholding_net_total)",
)

if is_valid_certificate(
ldc.valid_from, ldc.valid_upto, posting_date, limit_consumed, net_total, ldc.certificate_limit
):
tds_amount = get_ltds_amount(
net_total, limit_consumed, ldc.certificate_limit, ldc.rate, tax_details
)

return tds_amount
return limit_consumed


def get_ltds_amount(current_amount, deducted_amount, certificate_limit, rate, tax_details):
if certificate_limit - flt(deducted_amount) - flt(current_amount) >= 0:
def get_lower_deduction_amount(
current_amount, limit_consumed, certificate_limit, rate, tax_details
):
if certificate_limit - flt(limit_consumed) - flt(current_amount) >= 0:
return current_amount * rate / 100
else:
ltds_amount = certificate_limit - flt(deducted_amount)
ltds_amount = certificate_limit - flt(limit_consumed)
tds_amount = current_amount - ltds_amount

return ltds_amount * rate / 100 + tds_amount * tax_details.rate / 100


def is_valid_certificate(
valid_from, valid_upto, posting_date, deducted_amount, current_amount, certificate_limit
):
valid = False

available_amount = flt(certificate_limit) - flt(deducted_amount)

if (getdate(valid_from) <= getdate(posting_date) <= getdate(valid_upto)) and available_amount > 0:
valid = True
def is_valid_certificate(ldc, posting_date, limit_consumed):
available_amount = flt(ldc.certificate_limit) - flt(limit_consumed)
if (
getdate(ldc.valid_from) <= getdate(posting_date) <= getdate(ldc.valid_upto)
) and available_amount > 0:
return True

return valid
return False


def normal_round(number):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import unittest

import frappe
from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
from frappe.utils import today

from erpnext.accounts.utils import get_fiscal_year
Expand All @@ -17,6 +18,7 @@ def setUpClass(self):
# create relevant supplier, etc
create_records()
create_tax_withholding_category_records()
make_pan_no_field()

def tearDown(self):
cancel_invoices()
Expand Down Expand Up @@ -451,6 +453,40 @@ def test_tax_withholding_via_payment_entry_for_advances(self):
pe2.cancel()
pe3.cancel()

def test_lower_deduction_certificate_application(self):
frappe.db.set_value(
"Supplier",
"Test LDC Supplier",
{
"tax_withholding_category": "Test Service Category",
"pan": "ABCTY1234D",
},
)

create_lower_deduction_certificate(
supplier="Test LDC Supplier",
certificate_no="1AE0423AAJ",
tax_withholding_category="Test Service Category",
tax_rate=2,
limit=50000,
)

pi1 = create_purchase_invoice(supplier="Test LDC Supplier", rate=35000)
pi1.submit()
self.assertEqual(pi1.taxes[0].tax_amount, 700)

pi2 = create_purchase_invoice(supplier="Test LDC Supplier", rate=35000)
pi2.submit()
self.assertEqual(pi2.taxes[0].tax_amount, 2300)

pi3 = create_purchase_invoice(supplier="Test LDC Supplier", rate=35000)
pi3.submit()
self.assertEqual(pi3.taxes[0].tax_amount, 3500)

pi1.cancel()
pi2.cancel()
pi3.cancel()


def cancel_invoices():
purchase_invoices = frappe.get_all(
Expand Down Expand Up @@ -610,6 +646,7 @@ def create_records():
"Test TDS Supplier6",
"Test TDS Supplier7",
"Test TDS Supplier8",
"Test LDC Supplier",
]:
if frappe.db.exists("Supplier", name):
continue
Expand Down Expand Up @@ -806,3 +843,39 @@ def create_tax_withholding_category(
"accounts": [{"company": "_Test Company", "account": account}],
}
).insert()


def create_lower_deduction_certificate(
supplier, tax_withholding_category, tax_rate, certificate_no, limit
):
fiscal_year = get_fiscal_year(today(), company="_Test Company")
if not frappe.db.exists("Lower Deduction Certificate", certificate_no):
frappe.get_doc(
{
"doctype": "Lower Deduction Certificate",
"company": "_Test Company",
"supplier": supplier,
"certificate_no": certificate_no,
"tax_withholding_category": tax_withholding_category,
"fiscal_year": fiscal_year[0],
"valid_from": fiscal_year[1],
"valid_upto": fiscal_year[2],
"rate": tax_rate,
"certificate_limit": limit,
}
).insert()


def make_pan_no_field():
pan_field = {
"Supplier": [
{
"fieldname": "pan",
"label": "PAN",
"fieldtype": "Data",
"translatable": 0,
}
]
}

create_custom_fields(pan_field, update=1)