Browse Source

test TDS calculation (#14919)

* test TDS calculation

* fix failing test cases

* fix codacy
develop
Ranjith Kurungadam 6 years ago
committed by Nabin Hait
parent
commit
dff2ba72d3
  1. 15
      erpnext/hr/doctype/employee_tax_exemption_declaration/test_employee_tax_exemption_declaration.py
  2. 4
      erpnext/hr/doctype/payroll_entry/test_payroll_entry.py
  3. 263
      erpnext/hr/doctype/salary_slip/test_salary_slip.py
  4. 22
      erpnext/hr/doctype/salary_structure/test_salary_structure.py
  5. 8
      erpnext/projects/doctype/timesheet/test_timesheet.py

15
erpnext/hr/doctype/employee_tax_exemption_declaration/test_employee_tax_exemption_declaration.py

@ -3,9 +3,9 @@
# See license.txt
from __future__ import unicode_literals
import frappe
import frappe, erpnext
import unittest
from erpnext.hr.doctype.salary_structure.test_salary_structure import make_employee
from erpnext.hr.doctype.employee.test_employee import make_employee
class TestEmployeeTaxExemptionDeclaration(unittest.TestCase):
def setUp(self):
@ -39,7 +39,7 @@ class TestEmployeeTaxExemptionDeclaration(unittest.TestCase):
declaration = frappe.get_doc({
"doctype": "Employee Tax Exemption Declaration",
"employee": frappe.get_value("Employee", {"user_id":"employee@taxexepmtion.com"}, "name"),
"company": "_Test Company",
"company": erpnext.get_default_company(),
"payroll_period": "_Test Payroll Period",
"declarations": [dict(exemption_sub_category = "_Test Sub Category",
exemption_category = "_Test Category",
@ -55,7 +55,7 @@ class TestEmployeeTaxExemptionDeclaration(unittest.TestCase):
declaration = frappe.get_doc({
"doctype": "Employee Tax Exemption Declaration",
"employee": frappe.get_value("Employee", {"user_id":"employee@taxexepmtion.com"}, "name"),
"company": "_Test Company",
"company": erpnext.get_default_company(),
"payroll_period": "_Test Payroll Period",
"declarations": [dict(exemption_sub_category = "_Test Sub Category",
exemption_category = "_Test Category",
@ -70,7 +70,7 @@ class TestEmployeeTaxExemptionDeclaration(unittest.TestCase):
duplicate_declaration = frappe.get_doc({
"doctype": "Employee Tax Exemption Declaration",
"employee": frappe.get_value("Employee", {"user_id":"employee@taxexepmtion.com"}, "name"),
"company": "_Test Company",
"company": erpnext.get_default_company(),
"payroll_period": "_Test Payroll Period",
"declarations": [dict(exemption_sub_category = "_Test Sub Category",
exemption_category = "_Test Category",
@ -87,10 +87,13 @@ def create_payroll_period():
payroll_period = frappe.get_doc(dict(
doctype = 'Payroll Period',
name = "_Test Payroll Period",
company = "_Test Company",
company = erpnext.get_default_company(),
start_date = date(date.today().year, 1, 1),
end_date = date(date.today().year, 12, 31)
)).insert()
return payroll_period
else:
return frappe.get_doc("Payroll Period", "_Test Payroll Period")
def create_exemption_category():
if not frappe.db.exists("Employee Tax Exemption Category", "_Test Category"):

4
erpnext/hr/doctype/payroll_entry/test_payroll_entry.py

@ -18,8 +18,8 @@ class TestPayrollEntry(unittest.TestCase):
for dt in ["Salary Slip", "Salary Component", "Salary Component Account", "Payroll Entry", "Loan"]:
frappe.db.sql("delete from `tab%s`" % dt)
make_earning_salary_component(["Basic Salary", "Special Allowance", "HRA", "Leave Encashment"])
make_deduction_salary_component(["Professional Tax", "TDS"])
make_earning_salary_component(setup=True)
make_deduction_salary_component(setup=True)
def test_payroll_entry(self): # pylint: disable=no-self-use
company = erpnext.get_default_company()

263
erpnext/hr/doctype/salary_slip/test_salary_slip.py

@ -6,18 +6,19 @@ import unittest
import frappe
import erpnext
import calendar
import random
from erpnext.accounts.utils import get_fiscal_year
from frappe.utils.make_random import get_random
from frappe.utils import getdate, nowdate, add_days, add_months, flt
from frappe.utils import getdate, nowdate, add_days, add_months, flt, get_first_day, get_last_day
from erpnext.hr.doctype.salary_structure.salary_structure import make_salary_slip
from erpnext.hr.doctype.payroll_entry.payroll_entry import get_month_details
from erpnext.hr.doctype.employee.test_employee import make_employee
from erpnext.hr.doctype.employee_tax_exemption_declaration.test_employee_tax_exemption_declaration import create_payroll_period, create_exemption_category
class TestSalarySlip(unittest.TestCase):
def setUp(self):
make_earning_salary_component(["Basic Salary", "Special Allowance", "HRA"])
make_deduction_salary_component(["Professional Tax", "TDS"])
make_earning_salary_component(setup=True)
make_deduction_salary_component(setup=True)
for dt in ["Leave Application", "Leave Allocation", "Salary Slip"]:
frappe.db.sql("delete from `tab%s`" % dt)
@ -164,6 +165,61 @@ class TestSalarySlip(unittest.TestCase):
elif payroll_frequncy == "Daily":
self.assertEqual(ss.end_date, nowdate())
def test_tax_for_payroll_period(self):
data = {}
# test the impact of tax exemption declaration, tax exemption proof submission and deduct check boxes in annual tax calculation
# as per assigned salary structure 40500 in monthly salary so 236000*5/100/12
frappe.db.sql("""delete from `tabPayroll Period`""")
frappe.db.sql("""delete from `tabSalary Component`""")
payroll_period = create_payroll_period()
create_tax_slab(payroll_period)
employee = make_employee("test_tax@salary.slip")
frappe.db.sql("""delete from `tabSalary Slip` where employee=%s""", (employee))
frappe.db.sql("""delete from `tabEmployee Tax Exemption Declaration` where employee=%s""", (employee))
frappe.db.sql("""delete from `tabEmployee Tax Exemption Proof Submission` where employee=%s""", (employee))
from erpnext.hr.doctype.salary_structure.test_salary_structure import make_salary_structure, create_salary_structure_assignment
salary_structure = make_salary_structure("Stucture to test tax", "Monthly", test_tax=True)
create_salary_structure_assignment(employee, salary_structure.name, payroll_period.start_date)
# create salary slip for whole period deducting tax only on last period to find the total tax amount paid
create_salary_slips_for_payroll_period(employee, salary_structure.name, payroll_period)
tax_paid_amount = frappe.db.sql("""select sum(sd.amount) from `tabSalary Detail` sd join `tabSalary Slip` ss where
ss.name=sd.parent and ss.employee=%s and ss.docstatus=1 and sd.salary_component='TDS'""", (employee))
# total taxable income 236000, at 5% tax slab
annual_tax = 11800
self.assertEqual(tax_paid_amount[0][0], annual_tax)
frappe.db.sql("""delete from `tabSalary Slip` where employee=%s""", (employee))
# create exemption declaration so the tax amount varies
create_exemption_declaration(employee, payroll_period.name)
# create for payroll deducting in random months
data["deducted_dates"] = create_salary_slips_for_payroll_period(employee, salary_structure.name, payroll_period, deduct_random=True)
tax_paid_amount = frappe.db.sql("""select sum(sd.amount) from `tabSalary Detail` sd join `tabSalary Slip` ss where
ss.name=sd.parent and ss.employee=%s and ss.docstatus=1 and sd.salary_component='TDS'""", (employee))
# No proof sumitted, total tax paid, should not change
try:
self.assertEqual(tax_paid_amount[0][0], annual_tax)
except AssertionError:
print("\nTax calculation failed on following case\n", data, "\n")
raise
# Submit proof for total 86000
data["proof"] = [create_proof_submission(employee, payroll_period, 50000), 50000]
data["proof1"] = [create_proof_submission(employee, payroll_period, 36000), 36000]
frappe.db.sql("""delete from `tabSalary Slip` where employee=%s""", (employee))
data["deducted_dates"] = create_salary_slips_for_payroll_period(employee, salary_structure.name, payroll_period, deduct_random=True)
tax_paid_amount = frappe.db.sql("""select sum(sd.amount) from `tabSalary Detail` sd join `tabSalary Slip` ss where
ss.name=sd.parent and ss.employee=%s and ss.docstatus=1 and sd.salary_component='TDS'""", (employee))
# total taxable income 150000, at 5% tax slab
try:
self.assertEqual(tax_paid_amount[0][0], 7500)
except AssertionError:
print("\nTax calculation failed on following case\n", data, "\n")
raise
def make_holiday_list(self):
fiscal_year = get_fiscal_year(nowdate(), company=erpnext.get_default_company())
if not frappe.db.get_value("Holiday List", "Salary Slip Test Holiday List"):
@ -212,28 +268,22 @@ def make_employee_salary_slip(user, payroll_frequency, salary_structure=None):
return salary_slip
def make_earning_salary_component(salary_components):
for salary_component in salary_components:
if not frappe.db.exists('Salary Component', salary_component):
sal_comp = frappe.get_doc({
"doctype": "Salary Component",
"salary_component": salary_component,
"type": "Earning"
})
sal_comp.insert()
get_salary_component_account(salary_component)
def make_deduction_salary_component(salary_components):
def make_salary_component(salary_components, test_tax):
for salary_component in salary_components:
if not frappe.db.exists('Salary Component', salary_component):
sal_comp = frappe.get_doc({
"doctype": "Salary Component",
"salary_component": salary_component,
"type": "Deduction"
})
sal_comp.insert()
get_salary_component_account(salary_component)
if not frappe.db.exists('Salary Component', salary_component["salary_component"]):
if test_tax:
if salary_component["type"] == "Earning":
salary_component["is_tax_applicable"] = 1
elif salary_component["salary_component"] == "TDS":
salary_component["variable_based_on_taxable_salary"] = 1
salary_component["amount_based_on_formula"] = 0
salary_component["amount"] = 0
salary_component["formula"] = ""
salary_component["condition"] = ""
salary_component["doctype"] = "Salary Component"
salary_component["salary_component_abbr"] = salary_component["abbr"]
frappe.get_doc(salary_component).insert()
get_salary_component_account(salary_component["salary_component"])
def get_salary_component_account(sal_comp):
company = erpnext.get_default_company()
@ -244,7 +294,6 @@ def get_salary_component_account(sal_comp):
})
sal_comp.save()
def create_account(company):
salary_account = frappe.db.get_value("Account", "Salary - " + frappe.db.get_value('Company', company, 'abbr'))
if not salary_account:
@ -256,64 +305,138 @@ def create_account(company):
}).insert()
return salary_account
def get_earnings_component(setup=False):
if setup:
make_earning_salary_component(["Basic Salary", "Special Allowance", "HRA"])
return [
{
"salary_component": 'Basic Salary',
"abbr":'BS',
"condition": 'base > 10000',
"formula": 'base*.5',
"idx": 1
},
{
def make_earning_salary_component(setup=False, test_tax=False):
data = [
{
"salary_component": 'Basic Salary',
"abbr":'BS',
"condition": 'base > 10000',
"formula": 'base*.5',
"type": "Earning"
},
{
"salary_component": 'HRA',
"abbr":'H',
"amount": 3000,
"type": "Earning"
},
{
"salary_component": 'Special Allowance',
"abbr":'SA',
"condition": 'H < 10000',
"formula": 'BS*.5',
"type": "Earning"
},
{
"salary_component": "Leave Encashment",
"abbr": 'LE',
"is_additional_component": 1,
"type": "Earning"
}
]
if setup or test_tax:
make_salary_component(data, test_tax)
data.append({
"salary_component": 'Basic Salary',
"abbr":'BS',
"condition": 'base < 10000',
"formula": 'base*.2',
"idx": 2
},
{
"salary_component": 'HRA',
"abbr":'H',
"amount": 3000,
"idx": 3
},
{
"salary_component": 'Special Allowance',
"abbr":'SA',
"condition": 'H < 10000',
"formula": 'BS*.5',
"idx": 4
},
]
def get_deductions_component(setup=False):
if setup:
make_deduction_salary_component(["Professional Tax", "TDS"])
"type": "Earning"
})
return data
return [
def make_deduction_salary_component(setup=False, test_tax=False):
data = [
{
"salary_component": 'Professional Tax',
"abbr":'PT',
"condition": 'base > 10000',
"formula": 'base*.1',
"idx": 1
},
{
"salary_component": 'TDS',
"abbr":'T',
"formula": 'base*.1',
"idx": 2
"type": "Deduction"
},
{
"salary_component": 'TDS',
"abbr":'T',
"condition": 'employment_type=="Intern"',
"formula": 'base*.1',
"idx": 3
"type": "Deduction"
}
]
if not test_tax:
data.append({
"salary_component": 'TDS',
"abbr":'T',
"condition": 'employment_type=="Intern"',
"formula": 'base*.1',
"type": "Deduction"
})
if setup or test_tax:
make_salary_component(data, test_tax)
return data
def create_exemption_declaration(employee, payroll_period):
create_exemption_category()
declaration = frappe.get_doc({"doctype": "Employee Tax Exemption Declaration",
"employee": employee,
"payroll_period": payroll_period,
"company": erpnext.get_default_company()})
declaration.append("declarations", {"exemption_sub_category": "_Test Sub Category",
"exemption_category": "_Test Category",
"amount": 100000})
declaration.submit()
def create_proof_submission(employee, payroll_period, amount):
submission_date = add_months(payroll_period.start_date, random.randint(0, 11))
proof_submission = frappe.get_doc({"doctype": "Employee Tax Exemption Proof Submission",
"employee": employee,
"payroll_period": payroll_period.name,
"submission_date": submission_date})
proof_submission.append("tax_exemption_proofs", {"exemption_sub_category": "_Test Sub Category",
"exemption_category": "_Test Category", "type_of_proof": "Test",
"amount": amount})
proof_submission.submit()
return submission_date
def create_tax_slab(payroll_period):
data = [{
"from_amount": 250000,
"to_amount": 500000,
"percent_deduction": 5
},
{
"from_amount": 500000,
"to_amount": 1000000,
"percent_deduction": 20
},
{
"from_amount": 1000000,
"percent_deduction": 30
}]
payroll_period.taxable_salary_slabs = []
for item in data:
payroll_period.append("taxable_salary_slabs", item)
payroll_period.save()
def create_salary_slips_for_payroll_period(employee, salary_structure, payroll_period, deduct_random=False):
deducted_dates = []
i = 0
while i < 12:
slip = frappe.get_doc({"doctype": "Salary Slip", "employee": employee,
"salary_structure": salary_structure, "frequency": "Monthly"})
if i == 0:
posting_date = add_days(payroll_period.start_date, 25)
else:
posting_date = add_months(posting_date, 1)
if i == 11:
slip.deduct_tax_for_unsubmitted_tax_exemption_proof = 1
slip.deduct_tax_for_unclaimed_employee_benefits = 1
if deduct_random and not random.randint(0, 2):
slip.deduct_tax_for_unsubmitted_tax_exemption_proof = 1
deducted_dates.append(posting_date)
slip.posting_date = posting_date
slip.start_date = get_first_day(posting_date)
slip.end_date = get_last_day(posting_date)
doc = make_salary_slip(salary_structure, slip, employee)
doc.submit()
i += 1
return deducted_dates

22
erpnext/hr/doctype/salary_structure/test_salary_structure.py

@ -8,8 +8,8 @@ import erpnext
from frappe.utils.make_random import get_random
from frappe.utils import nowdate, add_days, add_years, getdate, add_months
from erpnext.hr.doctype.salary_structure.salary_structure import make_salary_slip
from erpnext.hr.doctype.salary_slip.test_salary_slip import get_earnings_component,\
get_deductions_component, make_employee_salary_slip
from erpnext.hr.doctype.salary_slip.test_salary_slip import make_earning_salary_component,\
make_deduction_salary_component, make_employee_salary_slip
from erpnext.hr.doctype.employee.test_employee import make_employee
@ -34,7 +34,7 @@ class TestSalaryStructure(unittest.TestCase):
"from_date": nowdate(),
"to_date": add_years(nowdate(), 1),
"weekly_off": "Sunday"
}).insert()
}).insert()
holiday_list.get_weekly_off_dates()
holiday_list.save()
@ -72,14 +72,16 @@ class TestSalaryStructure(unittest.TestCase):
self.assertFalse(("\n" in row.formula) or ("\n" in row.condition))
def make_salary_structure(salary_structure, payroll_frequency, employee=None, dont_submit=False, other_details=None):
def make_salary_structure(salary_structure, payroll_frequency, employee=None, dont_submit=False, other_details=None, test_tax=False):
if test_tax:
frappe.db.sql("""delete from `tabSalary Structure` where name=%s""",(salary_structure))
if not frappe.db.exists('Salary Structure', salary_structure):
details = {
"doctype": "Salary Structure",
"name": salary_structure,
"company": erpnext.get_default_company(),
"earnings": get_earnings_component(),
"deductions": get_deductions_component(),
"earnings": make_earning_salary_component(test_tax=test_tax),
"deductions": make_deduction_salary_component(test_tax=test_tax),
"payroll_frequency": payroll_frequency,
"payment_account": get_random("Account")
}
@ -97,14 +99,16 @@ def make_salary_structure(salary_structure, payroll_frequency, employee=None, do
return salary_structure_doc
def create_salary_structure_assignment(employee, salary_structure):
def create_salary_structure_assignment(employee, salary_structure, from_date=None):
if frappe.db.exists("Salary Structure Assignment", {"employee": employee}):
frappe.db.sql("""delete from `tabSalary Structure Assignment` where employee=%s""",(employee))
salary_structure_assignment = frappe.new_doc("Salary Structure Assignment")
salary_structure_assignment.employee = employee
salary_structure_assignment.base = 50000
salary_structure_assignment.variable = 5000
salary_structure_assignment.from_date = add_months(nowdate(), -1)
salary_structure_assignment.from_date = from_date or add_months(nowdate(), -1)
salary_structure_assignment.salary_structure = salary_structure
salary_structure_assignment.company = erpnext.get_default_company()
salary_structure_assignment.save(ignore_permissions=True)
salary_structure_assignment.submit()
return salary_structure_assignment
return salary_structure_assignment

8
erpnext/projects/doctype/timesheet/test_timesheet.py

@ -19,10 +19,10 @@ class TestTimesheet(unittest.TestCase):
def setUp(self):
for dt in ["Salary Slip", "Salary Structure", "Salary Structure Assignment", "Timesheet"]:
frappe.db.sql("delete from `tab%s`" % dt)
from erpnext.hr.doctype.salary_slip.test_salary_slip import make_earning_salary_component
make_earning_salary_component(["Timesheet Component"])
if not frappe.db.exists("Salary Component", "Timesheet Component"):
frappe.get_doc({"doctype": "Salary Component", "salary_component": "Timesheet Component"}).insert()
def test_timesheet_billing_amount(self):
make_salary_structure_for_timesheet("_T-Employee-00001")

Loading…
Cancel
Save