test: HRA Exemption in Employee Tax Exemption Declaration
diff --git a/erpnext/payroll/doctype/employee_tax_exemption_declaration/test_employee_tax_exemption_declaration.py b/erpnext/payroll/doctype/employee_tax_exemption_declaration/test_employee_tax_exemption_declaration.py
index 1d90e73..6986bce 100644
--- a/erpnext/payroll/doctype/employee_tax_exemption_declaration/test_employee_tax_exemption_declaration.py
+++ b/erpnext/payroll/doctype/employee_tax_exemption_declaration/test_employee_tax_exemption_declaration.py
@@ -4,13 +4,15 @@
import unittest
import frappe
+from frappe.tests.utils import FrappeTestCase
+from frappe.utils import add_months, getdate
import erpnext
from erpnext.hr.doctype.employee.test_employee import make_employee
from erpnext.hr.utils import DuplicateDeclarationError
-class TestEmployeeTaxExemptionDeclaration(unittest.TestCase):
+class TestEmployeeTaxExemptionDeclaration(FrappeTestCase):
def setUp(self):
make_employee("employee@taxexepmtion.com")
make_employee("employee1@taxexepmtion.com")
@@ -112,6 +114,257 @@
self.assertEqual(declaration.total_exemption_amount, 100000)
+ def test_india_hra_exemption(self):
+ setup_hra_exemption_prerequisites("Monthly")
+ employee = frappe.get_value("Employee", {"user_id": "employee@taxexepmtion.com"}, "name")
+
+ declaration = frappe.get_doc(
+ {
+ "doctype": "Employee Tax Exemption Declaration",
+ "employee": employee,
+ "company": "Test Company",
+ "payroll_period": "_Test Payroll Period 1",
+ "currency": "INR",
+ "monthly_house_rent": 50000,
+ "rented_in_metro_city": 1,
+ "declarations": [
+ dict(
+ exemption_sub_category="_Test Sub Category",
+ exemption_category="_Test Category",
+ amount=80000,
+ ),
+ dict(
+ exemption_sub_category="_Test1 Sub Category",
+ exemption_category="_Test Category",
+ amount=60000,
+ ),
+ ],
+ }
+ ).insert()
+
+ # Monthly HRA received = 3000
+ # should set HRA exemption as per actual annual HRA because that's the minimum
+ self.assertEqual(declaration.monthly_hra_exemption, 3000)
+ self.assertEqual(declaration.annual_hra_exemption, 36000)
+ # 100000 Standard Exemption + 36000 HRA exemption
+ self.assertEqual(declaration.total_exemption_amount, 136000)
+
+ def test_india_hra_exemption_with_daily_payroll_frequency(self):
+ setup_hra_exemption_prerequisites("Daily")
+ employee = frappe.get_value("Employee", {"user_id": "employee@taxexepmtion.com"}, "name")
+
+ declaration = frappe.get_doc(
+ {
+ "doctype": "Employee Tax Exemption Declaration",
+ "employee": employee,
+ "company": "Test Company",
+ "payroll_period": "_Test Payroll Period 1",
+ "currency": "INR",
+ "monthly_house_rent": 170000,
+ "rented_in_metro_city": 1,
+ "declarations": [
+ dict(
+ exemption_sub_category="_Test1 Sub Category",
+ exemption_category="_Test Category",
+ amount=60000,
+ ),
+ ],
+ }
+ ).insert()
+
+ # Daily HRA received = 3000
+ # should set HRA exemption as per (rent - 10% of Basic Salary), that's the minimum
+ self.assertEqual(declaration.monthly_hra_exemption, 17916.67)
+ self.assertEqual(declaration.annual_hra_exemption, 215000)
+ # 50000 Standard Exemption + 215000 HRA exemption
+ self.assertEqual(declaration.total_exemption_amount, 265000)
+
+ def test_india_hra_exemption_with_weekly_payroll_frequency(self):
+ setup_hra_exemption_prerequisites("Weekly")
+ employee = frappe.get_value("Employee", {"user_id": "employee@taxexepmtion.com"}, "name")
+
+ declaration = frappe.get_doc(
+ {
+ "doctype": "Employee Tax Exemption Declaration",
+ "employee": employee,
+ "company": "Test Company",
+ "payroll_period": "_Test Payroll Period 1",
+ "currency": "INR",
+ "monthly_house_rent": 170000,
+ "rented_in_metro_city": 1,
+ "declarations": [
+ dict(
+ exemption_sub_category="_Test1 Sub Category",
+ exemption_category="_Test Category",
+ amount=60000,
+ ),
+ ],
+ }
+ ).insert()
+
+ # Weekly HRA received = 3000
+ # should set HRA exemption as per actual annual HRA because that's the minimum
+ self.assertEqual(declaration.monthly_hra_exemption, 13000)
+ self.assertEqual(declaration.annual_hra_exemption, 156000)
+ # 50000 Standard Exemption + 156000 HRA exemption
+ self.assertEqual(declaration.total_exemption_amount, 206000)
+
+ def test_india_hra_exemption_with_fortnightly_payroll_frequency(self):
+ setup_hra_exemption_prerequisites("Fortnightly")
+ employee = frappe.get_value("Employee", {"user_id": "employee@taxexepmtion.com"}, "name")
+
+ declaration = frappe.get_doc(
+ {
+ "doctype": "Employee Tax Exemption Declaration",
+ "employee": employee,
+ "company": "Test Company",
+ "payroll_period": "_Test Payroll Period 1",
+ "currency": "INR",
+ "monthly_house_rent": 170000,
+ "rented_in_metro_city": 1,
+ "declarations": [
+ dict(
+ exemption_sub_category="_Test1 Sub Category",
+ exemption_category="_Test Category",
+ amount=60000,
+ ),
+ ],
+ }
+ ).insert()
+
+ # Fortnightly HRA received = 3000
+ # should set HRA exemption as per actual annual HRA because that's the minimum
+ self.assertEqual(declaration.monthly_hra_exemption, 6500)
+ self.assertEqual(declaration.annual_hra_exemption, 78000)
+ # 50000 Standard Exemption + 78000 HRA exemption
+ self.assertEqual(declaration.total_exemption_amount, 128000)
+
+ def test_india_hra_exemption_with_bimonthly_payroll_frequency(self):
+ setup_hra_exemption_prerequisites("Bimonthly")
+ employee = frappe.get_value("Employee", {"user_id": "employee@taxexepmtion.com"}, "name")
+
+ declaration = frappe.get_doc(
+ {
+ "doctype": "Employee Tax Exemption Declaration",
+ "employee": employee,
+ "company": "Test Company",
+ "payroll_period": "_Test Payroll Period 1",
+ "currency": "INR",
+ "monthly_house_rent": 50000,
+ "rented_in_metro_city": 1,
+ "declarations": [
+ dict(
+ exemption_sub_category="_Test Sub Category",
+ exemption_category="_Test Category",
+ amount=80000,
+ ),
+ dict(
+ exemption_sub_category="_Test1 Sub Category",
+ exemption_category="_Test Category",
+ amount=60000,
+ ),
+ ],
+ }
+ ).insert()
+
+ # Bimonthly HRA received = 3000
+ # should set HRA exemption as per actual annual HRA because that's the minimum
+ self.assertEqual(declaration.monthly_hra_exemption, 1500)
+ self.assertEqual(declaration.annual_hra_exemption, 18000)
+ # 100000 Standard Exemption + 18000 HRA exemption
+ self.assertEqual(declaration.total_exemption_amount, 118000)
+
+ def test_india_hra_exemption_with_multiple_salary_structure_assignments(self):
+ from erpnext.payroll.doctype.salary_slip.test_salary_slip import create_tax_slab
+ from erpnext.payroll.doctype.salary_structure.test_salary_structure import (
+ create_salary_structure_assignment,
+ make_salary_structure,
+ )
+
+ payroll_period = create_payroll_period(name="_Test Payroll Period 1", company="_Test Company")
+
+ create_tax_slab(
+ payroll_period,
+ allow_tax_exemption=True,
+ currency="INR",
+ effective_date=getdate("2019-04-01"),
+ company="_Test Company",
+ )
+
+ frappe.db.set_value(
+ "Company", "_Test Company", {"basic_component": "Basic Salary", "hra_component": "HRA"}
+ )
+
+ employee = frappe.get_value("Employee", {"user_id": "employee@taxexepmtion.com"}, "name")
+
+ # salary structure with base 50000, HRA 3000
+ make_salary_structure(
+ "Monthly Structure for HRA Exemption 1",
+ "Monthly",
+ employee=employee,
+ company="_Test Company",
+ currency="INR",
+ payroll_period=payroll_period.name,
+ from_date=payroll_period.start_date,
+ )
+
+ # salary structure with base 70000, HRA = base * 0.2 = 14000
+ salary_structure = make_salary_structure(
+ "Monthly Structure for HRA Exemption 2",
+ "Monthly",
+ employee=employee,
+ company="_Test Company",
+ currency="INR",
+ payroll_period=payroll_period.name,
+ from_date=payroll_period.start_date,
+ dont_submit=True,
+ )
+ for component_row in salary_structure.earnings:
+ if component_row.salary_component == "HRA":
+ component_row.amount = 0
+ component_row.amount_based_on_formula = 1
+ component_row.formula = "base * 0.2"
+ break
+
+ salary_structure.submit()
+
+ create_salary_structure_assignment(
+ employee,
+ salary_structure.name,
+ from_date=add_months(payroll_period.start_date, 6),
+ company="_Test Company",
+ currency="INR",
+ payroll_period=payroll_period.name,
+ base=70000,
+ allow_duplicate=True,
+ )
+
+ declaration = frappe.get_doc(
+ {
+ "doctype": "Employee Tax Exemption Declaration",
+ "employee": employee,
+ "company": "Test Company",
+ "payroll_period": "_Test Payroll Period 1",
+ "currency": "INR",
+ "monthly_house_rent": 50000,
+ "rented_in_metro_city": 1,
+ "declarations": [
+ dict(
+ exemption_sub_category="_Test1 Sub Category",
+ exemption_category="_Test Category",
+ amount=60000,
+ ),
+ ],
+ }
+ ).insert()
+
+ # Monthly HRA received = 50000 * 6 months + 70000 * 6 months
+ # should set HRA exemption as per actual annual HRA because that's the minimum
+ self.assertEqual(declaration.monthly_hra_exemption, 8500)
+ self.assertEqual(declaration.annual_hra_exemption, 102000)
+ # 50000 Standard Exemption + 102000 HRA exemption
+ self.assertEqual(declaration.total_exemption_amount, 152000)
+
def create_payroll_period(**args):
args = frappe._dict(args)
@@ -163,3 +416,31 @@
"is_active": 1,
}
).insert()
+
+
+def setup_hra_exemption_prerequisites(frequency):
+ from erpnext.payroll.doctype.salary_slip.test_salary_slip import create_tax_slab
+ from erpnext.payroll.doctype.salary_structure.test_salary_structure import make_salary_structure
+
+ payroll_period = create_payroll_period(name="_Test Payroll Period 1", company="_Test Company")
+
+ create_tax_slab(
+ payroll_period,
+ allow_tax_exemption=True,
+ currency="INR",
+ effective_date=getdate("2019-04-01"),
+ company="_Test Company",
+ )
+
+ make_salary_structure(
+ f"{frequency} Structure for HRA Exemption",
+ frequency,
+ employee=frappe.get_value("Employee", {"user_id": "employee@taxexepmtion.com"}, "name"),
+ company="_Test Company",
+ currency="INR",
+ payroll_period=payroll_period,
+ )
+
+ frappe.db.set_value(
+ "Company", "_Test Company", {"basic_component": "Basic Salary", "hra_component": "HRA"}
+ )
diff --git a/erpnext/payroll/doctype/salary_structure/test_salary_structure.py b/erpnext/payroll/doctype/salary_structure/test_salary_structure.py
index e9b5ed2..5c78e8f 100644
--- a/erpnext/payroll/doctype/salary_structure/test_salary_structure.py
+++ b/erpnext/payroll/doctype/salary_structure/test_salary_structure.py
@@ -230,9 +230,12 @@
company=None,
currency=erpnext.get_default_currency(),
payroll_period=None,
+ base=None,
+ allow_duplicate=False,
):
-
- if frappe.db.exists("Salary Structure Assignment", {"employee": employee}):
+ if not allow_duplicate and frappe.db.exists(
+ "Salary Structure Assignment", {"employee": employee}
+ ):
frappe.db.sql("""delete from `tabSalary Structure Assignment` where employee=%s""", (employee))
if not payroll_period:
@@ -245,7 +248,7 @@
salary_structure_assignment = frappe.new_doc("Salary Structure Assignment")
salary_structure_assignment.employee = employee
- salary_structure_assignment.base = 50000
+ salary_structure_assignment.base = base or 50000
salary_structure_assignment.variable = 5000
if not from_date: