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