diff --git a/erpnext/hr/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.js b/erpnext/hr/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.js index fb8c81ac76..130d202c4f 100644 --- a/erpnext/hr/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.js +++ b/erpnext/hr/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.js @@ -61,14 +61,14 @@ frappe.ui.form.on('Employee Tax Exemption Declaration', { } }, monthly_house_rent: function(frm) { - frm.trigger("calculate_hra_component"); + frm.trigger("calculate_hra_exemption"); }, rented_in_metro_city: function(frm) { - frm.trigger("calculate_hra_component"); + frm.trigger("calculate_hra_exemption"); }, - calculate_hra_component: function(frm) { + calculate_hra_exemption: function(frm) { frappe.call({ - method: "calculate_hra_component", + method: "calculate_hra_exemption", doc: frm.doc, callback: function(r) { frm.refresh_fields(); diff --git a/erpnext/hr/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.json b/erpnext/hr/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.json index b984873e4d..473150b825 100644 --- a/erpnext/hr/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.json +++ b/erpnext/hr/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.json @@ -474,7 +474,7 @@ "bold": 0, "collapsible": 0, "columns": 0, - "fieldname": "annual_hra", + "fieldname": "annual_hra_exemption", "fieldtype": "Currency", "hidden": 0, "ignore_user_permissions": 0, @@ -483,7 +483,7 @@ "in_global_search": 0, "in_list_view": 0, "in_standard_filter": 0, - "label": "Annual HRA", + "label": "Annual HRA Exemption", "length": 0, "no_copy": 0, "permlevel": 0, @@ -506,7 +506,7 @@ "bold": 0, "collapsible": 0, "columns": 0, - "fieldname": "monthly_hra", + "fieldname": "monthly_hra_exemption", "fieldtype": "Currency", "hidden": 0, "ignore_user_permissions": 0, @@ -515,7 +515,7 @@ "in_global_search": 0, "in_list_view": 0, "in_standard_filter": 0, - "label": "Monthly HRA", + "label": "Monthly HRA Exemption", "length": 0, "no_copy": 0, "permlevel": 0, @@ -542,7 +542,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2018-05-30 13:35:08.941961", + "modified": "2018-05-30 18:09:50.662362", "modified_by": "Administrator", "module": "HR", "name": "Employee Tax Exemption Declaration", diff --git a/erpnext/hr/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.py b/erpnext/hr/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.py index b2d87bc32d..9bdba4a6d5 100644 --- a/erpnext/hr/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.py +++ b/erpnext/hr/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.py @@ -6,19 +6,18 @@ from __future__ import unicode_literals import frappe from frappe.model.document import Document from frappe import _ -from frappe.utils import getdate, flt -from erpnext.hr.utils import validate_tax_declaration, get_salary_assignment -from erpnext.hr.doctype.salary_structure.salary_structure import make_salary_slip +from frappe.utils import getdate +from erpnext.hr.utils import validate_tax_declaration, calculate_eligible_hra_exemption class EmployeeTaxExemptionDeclaration(Document): def validate(self): validate_tax_declaration(self.declarations) - self.calculate_hra_component() + self.calculate_hra_exemption() self.total_exemption_amount = 0 for item in self.declarations: self.total_exemption_amount += item.amount - if self.annual_hra: - self.total_exemption_amount += self.annual_hra + if self.annual_hra_exemption: + self.total_exemption_amount += self.annual_hra_exemption def before_submit(self): if frappe.db.exists({"doctype": "Employee Tax Exemption Declaration", @@ -28,52 +27,9 @@ class EmployeeTaxExemptionDeclaration(Document): frappe.throw(_("Tax Declaration of {0} for period {1} already submitted.")\ .format(self.employee, self.payroll_period), frappe.DocstatusTransitionError) - def calculate_hra_component(self): - hra_component = frappe.db.get_value("Company", self.company, "hra_component") - if hra_component: - assignment = get_salary_assignment(self.employee, getdate()) - if assignment and frappe.db.exists("Salary Detail", { - "parent": assignment.salary_structure, - "salary_component": hra_component, "parentfield": "earnings"}): - hra_amount = self.get_hra_from_salary_slip(assignment.salary_structure, hra_component) - if hra_amount: - self.salary_structure_hra = hra_amount - if self.monthly_house_rent: - self.annual_hra, self.monthly_hra = 0, 0 - annual_hra = self.calculate_eligible_hra_amount(assignment.salary_structure, assignment.base) - if annual_hra > 0: - self.annual_hra = annual_hra - self.monthly_hra = annual_hra / 12 - - def calculate_eligible_hra_amount(self, salary_structure, base): - # TODO make this configurable - exemptions = [] - frequency = frappe.get_value("Salary Structure", salary_structure, "payroll_frequency") - # case 1: The actual amount allotted by the employer as the HRA. - exemptions.append(self.get_annual_component_pay(frequency, self.salary_structure_hra)) - actual_annual_rent = self.monthly_house_rent * 12 - annual_base = self.get_annual_component_pay(frequency, base) - # case 2: Actual rent paid less 10% of the basic salary. - exemptions.append(flt(actual_annual_rent) - flt(annual_base * 0.1)) - # case 3: 50% of the basic salary, if the employee is staying in a metro city (40% for a non-metro city). - exemptions.append(annual_base * 0.5 if self.rented_in_metro_city else annual_base * 0.4) - # return minimum of 3 cases - return min(exemptions) - - def get_annual_component_pay(self, frequency, amount): - if frequency == "Daily": - return amount * 365 - elif frequency == "Weekly": - return amount * 52 - elif frequency == "Fortnightly": - return amount * 26 - elif frequency == "Monthly": - return amount * 12 - elif frequency == "Bimonthly": - return amount * 6 - - def get_hra_from_salary_slip(self, salary_structure, hra_component): - salary_slip = make_salary_slip(salary_structure, employee=self.employee) - for earning in salary_slip.earnings: - if earning.salary_component == hra_component: - return earning.amount + def calculate_hra_exemption(self): + exemptions = calculate_eligible_hra_exemption(self.company, self.employee, \ + self.monthly_house_rent, self.rented_in_metro_city) + self.salary_structure_hra = exemptions["hra_amount"] + self.annual_hra_exemption = exemptions["annual_exemption"] + self.monthly_hra_exemption = exemptions["monthly_exemption"] diff --git a/erpnext/hr/utils.py b/erpnext/hr/utils.py index 71a763c1aa..cbc373841e 100644 --- a/erpnext/hr/utils.py +++ b/erpnext/hr/utils.py @@ -4,9 +4,10 @@ from __future__ import unicode_literals import frappe from frappe import _ -from frappe.utils import formatdate, format_datetime, getdate, get_datetime, nowdate +from frappe.utils import formatdate, format_datetime, getdate, get_datetime, nowdate, flt from frappe.model.document import Document from frappe.desk.form import assign_to +from erpnext.hr.doctype.salary_structure.salary_structure import make_salary_slip class EmployeeBoardingController(Document): ''' @@ -254,3 +255,56 @@ def get_salary_assignment(employee, date): 'on_date': date, }, as_dict=1) return assignment[0] if assignment else None + +def calculate_eligible_hra_exemption(company, employee, monthly_house_rent, rented_in_metro_city): + hra_component = frappe.db.get_value("Company", company, "hra_component") + annual_exemption, monthly_exemption, hra_amount = 0, 0, 0 + if hra_component: + assignment = get_salary_assignment(employee, getdate()) + if assignment and frappe.db.exists("Salary Detail", { + "parent": assignment.salary_structure, + "salary_component": hra_component, "parentfield": "earnings"}): + hra_amount = get_hra_from_salary_slip(employee, assignment.salary_structure, hra_component) + if hra_amount: + if monthly_house_rent: + annual_exemption = calculate_hra_exemption(assignment.salary_structure, + assignment.base, hra_amount, monthly_house_rent, + rented_in_metro_city) + if annual_exemption > 0: + monthly_exemption = annual_exemption / 12 + else: + annual_exemption = 0 + return {"hra_amount": hra_amount, "annual_exemption": annual_exemption, "monthly_exemption": monthly_exemption} + +def get_hra_from_salary_slip(employee, salary_structure, hra_component): + salary_slip = make_salary_slip(salary_structure, employee=employee) + for earning in salary_slip.earnings: + if earning.salary_component == hra_component: + return earning.amount + +def calculate_hra_exemption(salary_structure, base, monthly_hra, monthly_house_rent, rented_in_metro_city): + # TODO make this configurable + exemptions = [] + frequency = frappe.get_value("Salary Structure", salary_structure, "payroll_frequency") + # case 1: The actual amount allotted by the employer as the HRA. + exemptions.append(get_annual_component_pay(frequency, monthly_hra)) + actual_annual_rent = monthly_house_rent * 12 + annual_base = get_annual_component_pay(frequency, base) + # case 2: Actual rent paid less 10% of the basic salary. + exemptions.append(flt(actual_annual_rent) - flt(annual_base * 0.1)) + # case 3: 50% of the basic salary, if the employee is staying in a metro city (40% for a non-metro city). + exemptions.append(annual_base * 0.5 if rented_in_metro_city else annual_base * 0.4) + # return minimum of 3 cases + return min(exemptions) + +def get_annual_component_pay(frequency, amount): + if frequency == "Daily": + return amount * 365 + elif frequency == "Weekly": + return amount * 52 + elif frequency == "Fortnightly": + return amount * 26 + elif frequency == "Monthly": + return amount * 12 + elif frequency == "Bimonthly": + return amount * 6