Employee benefit - Late employee benefit application (#14465)
* HR Utils - get salary slip total benefit given for a payroll period
* Late employee benefit application
* Additional Salary - code refactor - get_amount
* new line in salary detail json
* Employee benefit late application - validation refactor
* Codacy fix
diff --git a/erpnext/hr/doctype/additional_salary/additional_salary.py b/erpnext/hr/doctype/additional_salary/additional_salary.py
index 7482c8b..dfa22d7 100644
--- a/erpnext/hr/doctype/additional_salary/additional_salary.py
+++ b/erpnext/hr/doctype/additional_salary/additional_salary.py
@@ -25,11 +25,6 @@
frappe.throw(_("To date can not greater than employee's relieving date"))
def get_amount(self, sal_start_date, sal_end_date):
- # If additional salary dates in between the salary slip dates
- # then return complete additional salary amount
- if getdate(sal_start_date) <= getdate(self.from_date) <= getdate(sal_end_date)\
- and getdate(sal_end_date) >= getdate(self.to_date) >= getdate(sal_start_date):
- return self.amount
start_date = getdate(sal_start_date)
end_date = getdate(sal_end_date)
total_days = date_diff(getdate(self.to_date), getdate(self.from_date)) + 1
@@ -38,12 +33,9 @@
start_date = getdate(self.from_date)
if getdate(sal_end_date) > getdate(self.to_date):
end_date = getdate(self.to_date)
- no_of_days = date_diff(getdate(end_date), getdate(start_date))
+ no_of_days = date_diff(getdate(end_date), getdate(start_date)) + 1
return amount_per_day * no_of_days
-
-
-
@frappe.whitelist()
def get_additional_salary_component(employee, start_date, end_date):
additional_components = frappe.db.sql("""
diff --git a/erpnext/hr/doctype/employee_benefit_application/employee_benefit_application.js b/erpnext/hr/doctype/employee_benefit_application/employee_benefit_application.js
index f96f262..b63c76f 100644
--- a/erpnext/hr/doctype/employee_benefit_application/employee_benefit_application.js
+++ b/erpnext/hr/doctype/employee_benefit_application/employee_benefit_application.js
@@ -11,25 +11,53 @@
});
},
employee: function(frm) {
- if(frm.doc.employee && frm.doc.date){
- frappe.call({
- method: "erpnext.hr.doctype.employee_benefit_application.employee_benefit_application.get_max_benefits",
- args:{
- employee: frm.doc.employee,
- on_date: frm.doc.date
- },
- callback: function (data) {
- if(!data.exc){
- if(data.message){
- frm.set_value("max_benefits", data.message);
- }
- }
- }
- });
+ var method, args;
+ if(frm.doc.employee && frm.doc.date && frm.doc.payroll_period){
+ method = "erpnext.hr.doctype.employee_benefit_application.employee_benefit_application.get_max_benefits_remaining";
+ args = {
+ employee: frm.doc.employee,
+ on_date: frm.doc.date,
+ payroll_period: frm.doc.payroll_period
+ };
+ get_max_benefits(frm, method, args);
+ }
+ else if(frm.doc.employee && frm.doc.date){
+ method = "erpnext.hr.doctype.employee_benefit_application.employee_benefit_application.get_max_benefits";
+ args = {
+ employee: frm.doc.employee,
+ on_date: frm.doc.date
+ };
+ get_max_benefits(frm, method, args);
+ }
+ },
+ payroll_period: function(frm) {
+ var method, args;
+ if(frm.doc.employee && frm.doc.date && frm.doc.payroll_period){
+ method = "erpnext.hr.doctype.employee_benefit_application.employee_benefit_application.get_max_benefits_remaining";
+ args = {
+ employee: frm.doc.employee,
+ on_date: frm.doc.date,
+ payroll_period: frm.doc.payroll_period
+ };
+ get_max_benefits(frm, method, args);
}
}
});
+var get_max_benefits=function(frm, method, args) {
+ frappe.call({
+ method: method,
+ args: args,
+ callback: function (data) {
+ if(!data.exc){
+ if(data.message){
+ frm.set_value("max_benefits", data.message);
+ }
+ }
+ }
+ });
+};
+
frappe.ui.form.on("Employee Benefit Application Detail",{
amount: function(frm) {
calculate_all(frm.doc);
diff --git a/erpnext/hr/doctype/employee_benefit_application/employee_benefit_application.py b/erpnext/hr/doctype/employee_benefit_application/employee_benefit_application.py
index 2d33ce8..ed54fb8 100644
--- a/erpnext/hr/doctype/employee_benefit_application/employee_benefit_application.py
+++ b/erpnext/hr/doctype/employee_benefit_application/employee_benefit_application.py
@@ -5,10 +5,11 @@
from __future__ import unicode_literals
import frappe
from frappe import _
-from frappe.utils import date_diff, getdate
+from frappe.utils import date_diff, getdate, rounded
from frappe.model.document import Document
from erpnext.hr.doctype.payroll_period.payroll_period import get_payroll_period_days
from erpnext.hr.doctype.salary_structure_assignment.salary_structure_assignment import get_assigned_salary_structure
+from erpnext.hr.utils import get_sal_slip_total_benefit_given
class EmployeeBenefitApplication(Document):
def validate(self):
@@ -41,9 +42,10 @@
pro_rata_amount += max_benefit_amount
else:
non_pro_rata_amount += max_benefit_amount
+
if pro_rata_amount == 0 and non_pro_rata_amount == 0:
frappe.throw(_("Please add the remainig benefits {0} to any of the existing component").format(self.remainig_benefits))
- elif non_pro_rata_amount > 0 and non_pro_rata_amount < self.remainig_benefits:
+ elif non_pro_rata_amount > 0 and non_pro_rata_amount < rounded(self.remainig_benefits):
frappe.throw(_("You can claim only an amount of {0}, the rest amount {1} should be in the application \
as pro-rata component").format(non_pro_rata_amount, self.remainig_benefits - non_pro_rata_amount))
elif non_pro_rata_amount == 0:
@@ -65,7 +67,9 @@
for employee_benefit in self.employee_benefits:
if employee_benefit.earning_component == earning_component_name:
benefit_amount += employee_benefit.amount
- if benefit_amount > max_benefit_amount:
+ prev_sal_slip_flexi_amount = get_sal_slip_total_benefit_given(self.employee, frappe.get_doc("Payroll Period", self.payroll_period), earning_component_name)
+ benefit_amount += prev_sal_slip_flexi_amount
+ if rounded(benefit_amount, 2) > max_benefit_amount:
frappe.throw(_("Maximum benefit amount of component {0} exceeds {1}").format(earning_component_name, max_benefit_amount))
def validate_duplicate_on_payroll_period(self):
@@ -87,10 +91,17 @@
max_benefits = frappe.db.get_value("Salary Structure", sal_struct, "max_benefits")
if max_benefits > 0:
return max_benefits
- else:
- frappe.throw(_("Employee {0} has no max benefits in salary structure {1}").format(employee, sal_struct[0][0]))
- else:
- frappe.throw(_("Employee {0} has no salary structure assigned").format(employee))
+ return False
+
+@frappe.whitelist()
+def get_max_benefits_remaining(employee, on_date, payroll_period):
+ max_benefits = get_max_benefits(employee, on_date)
+ if max_benefits and max_benefits > 0:
+ payroll_period_obj = frappe.get_doc("Payroll Period", payroll_period)
+ # Get all salary slip flexi amount in the payroll period
+ prev_sal_slip_flexi_total = get_sal_slip_total_benefit_given(employee, payroll_period_obj)
+ return max_benefits - prev_sal_slip_flexi_total
+ return max_benefits
def get_benefit_component_amount(employee, start_date, end_date, struct_row, sal_struct):
# Considering there is only one application for an year
diff --git a/erpnext/hr/doctype/employee_benefit_claim/employee_benefit_claim.py b/erpnext/hr/doctype/employee_benefit_claim/employee_benefit_claim.py
index 1aed7ce..5029599 100644
--- a/erpnext/hr/doctype/employee_benefit_claim/employee_benefit_claim.py
+++ b/erpnext/hr/doctype/employee_benefit_claim/employee_benefit_claim.py
@@ -13,6 +13,8 @@
class EmployeeBenefitClaim(Document):
def validate(self):
max_benefits = get_max_benefits(self.employee, self.claim_date)
+ if not max_benefits or max_benefits <= 0:
+ frappe.throw(_("Employee {0} has no maximum benefit amount").format(self.employee))
payroll_period = get_payroll_period(self.claim_date, self.claim_date, frappe.db.get_value("Employee", self.employee, "company"))
self.validate_max_benefit_for_component(payroll_period)
self.validate_max_benefit_for_sal_struct(max_benefits)
diff --git a/erpnext/hr/doctype/salary_detail/salary_detail.json b/erpnext/hr/doctype/salary_detail/salary_detail.json
index a8f389b..4212f4a 100644
--- a/erpnext/hr/doctype/salary_detail/salary_detail.json
+++ b/erpnext/hr/doctype/salary_detail/salary_detail.json
@@ -606,4 +606,4 @@
"sort_order": "DESC",
"track_changes": 0,
"track_seen": 0
-}
\ No newline at end of file
+}
diff --git a/erpnext/hr/doctype/salary_slip/salary_slip.py b/erpnext/hr/doctype/salary_slip/salary_slip.py
index dc2446b..dbd73b5 100644
--- a/erpnext/hr/doctype/salary_slip/salary_slip.py
+++ b/erpnext/hr/doctype/salary_slip/salary_slip.py
@@ -107,7 +107,8 @@
'depends_on_lwp' : struct_row.depends_on_lwp,
'salary_component' : struct_row.salary_component,
'abbr' : struct_row.abbr,
- 'do_not_include_in_total' : struct_row.do_not_include_in_total
+ 'do_not_include_in_total' : struct_row.do_not_include_in_total,
+ 'is_flexible_benefit': struct_row.is_flexible_benefit
})
else:
component_row.amount = amount
diff --git a/erpnext/hr/utils.py b/erpnext/hr/utils.py
index 11dbdf2..f473317 100644
--- a/erpnext/hr/utils.py
+++ b/erpnext/hr/utils.py
@@ -350,3 +350,31 @@
return amount * 12
elif frequency == "Bimonthly":
return amount * 6
+
+def get_sal_slip_total_benefit_given(employee, payroll_period, component=False):
+ total_given_benefit_amount = 0
+ query = """
+ select sum(sd.amount) as 'total_amount'
+ from `tabSalary Slip` ss, `tabSalary Detail` sd
+ where ss.employee=%(employee)s
+ and ss.docstatus = 1 and ss.name = sd.parent
+ and sd.is_flexible_benefit = 1 and sd.parentfield = "earnings"
+ and sd.parenttype = "Salary Slip"
+ and (ss.start_date between %(start_date)s and %(end_date)s
+ or ss.end_date between %(start_date)s and %(end_date)s
+ or (ss.start_date < %(start_date)s and ss.end_date > %(end_date)s))
+ """
+
+ if component:
+ query += "and sd.salary_component = %(component)s"
+
+ sum_of_given_benefit = frappe.db.sql(query, {
+ 'employee': employee,
+ 'start_date': payroll_period.start_date,
+ 'end_date': payroll_period.end_date,
+ 'component': component
+ }, as_dict=True)
+
+ if sum_of_given_benefit and sum_of_given_benefit[0].total_amount > 0:
+ total_given_benefit_amount = sum_of_given_benefit[0].total_amount
+ return total_given_benefit_amount