Compensatory Leave Request (#14611)

* Compensatory Leave Request - Validate Holidays and Attendance

* Compensatory Leave Request - Cancel

* Half Day in Compensatory Leave Request

* Leave allocation on Compensatory Leave Request - Fix

* Unused import
diff --git a/erpnext/hr/doctype/compensatory_leave_request/compensatory_leave_request.js b/erpnext/hr/doctype/compensatory_leave_request/compensatory_leave_request.js
index bb57562..1baa1e0 100644
--- a/erpnext/hr/doctype/compensatory_leave_request/compensatory_leave_request.js
+++ b/erpnext/hr/doctype/compensatory_leave_request/compensatory_leave_request.js
@@ -10,5 +10,13 @@
 				}
 			};
 		});
+	},
+	half_day: function(frm) {
+		if(frm.doc.half_day == 1){
+			frm.set_df_property('half_day_date', 'reqd', true);
+		}
+		else{
+			frm.set_df_property('half_day_date', 'reqd', false);
+		}
 	}
 });
diff --git a/erpnext/hr/doctype/compensatory_leave_request/compensatory_leave_request.json b/erpnext/hr/doctype/compensatory_leave_request/compensatory_leave_request.json
index 9d2a966..be309cd 100644
--- a/erpnext/hr/doctype/compensatory_leave_request/compensatory_leave_request.json
+++ b/erpnext/hr/doctype/compensatory_leave_request/compensatory_leave_request.json
@@ -43,7 +43,7 @@
    "reqd": 1, 
    "search_index": 0, 
    "set_only_once": 0, 
-   "translatable": 0,
+   "translatable": 0, 
    "unique": 0
   }, 
   {
@@ -77,7 +77,7 @@
    "reqd": 0, 
    "search_index": 0, 
    "set_only_once": 0, 
-   "translatable": 0,
+   "translatable": 0, 
    "unique": 0
   }, 
   {
@@ -87,39 +87,40 @@
    "bold": 0, 
    "collapsible": 0, 
    "columns": 0, 
-   "fetch_from": "employee.department",
-   "fieldname": "department",
-   "fieldtype": "Link",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 0,
-   "in_standard_filter": 0,
-   "label": "Department",
-   "length": 0,
-   "no_copy": 0,
-   "options": "Department",
-   "permlevel": 0,
-   "precision": "",
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 1,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 0,
-   "search_index": 0,
-   "set_only_once": 0,
-   "translatable": 0,
+   "fetch_from": "employee.department", 
+   "fieldname": "department", 
+   "fieldtype": "Link", 
+   "hidden": 0, 
+   "ignore_user_permissions": 0, 
+   "ignore_xss_filter": 0, 
+   "in_filter": 0, 
+   "in_global_search": 0, 
+   "in_list_view": 0, 
+   "in_standard_filter": 0, 
+   "label": "Department", 
+   "length": 0, 
+   "no_copy": 0, 
+   "options": "Department", 
+   "permlevel": 0, 
+   "precision": "", 
+   "print_hide": 0, 
+   "print_hide_if_no_value": 0, 
+   "read_only": 1, 
+   "remember_last_selected_value": 0, 
+   "report_hide": 0, 
+   "reqd": 0, 
+   "search_index": 0, 
+   "set_only_once": 0, 
+   "translatable": 0, 
    "unique": 0
-  },
+  }, 
   {
-   "allow_bulk_edit": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
+   "allow_bulk_edit": 0, 
+   "allow_in_quick_entry": 0, 
+   "allow_on_submit": 0, 
+   "bold": 0, 
+   "collapsible": 0, 
+   "columns": 0, 
    "fieldname": "work_from_date", 
    "fieldtype": "Date", 
    "hidden": 0, 
@@ -142,7 +143,7 @@
    "reqd": 1, 
    "search_index": 0, 
    "set_only_once": 0, 
-   "translatable": 0,
+   "translatable": 0, 
    "unique": 0
   }, 
   {
@@ -174,7 +175,72 @@
    "reqd": 1, 
    "search_index": 0, 
    "set_only_once": 0, 
-   "translatable": 0,
+   "translatable": 0, 
+   "unique": 0
+  }, 
+  {
+   "allow_bulk_edit": 0, 
+   "allow_in_quick_entry": 0, 
+   "allow_on_submit": 0, 
+   "bold": 0, 
+   "collapsible": 0, 
+   "columns": 0, 
+   "fieldname": "half_day", 
+   "fieldtype": "Check", 
+   "hidden": 0, 
+   "ignore_user_permissions": 0, 
+   "ignore_xss_filter": 0, 
+   "in_filter": 0, 
+   "in_global_search": 0, 
+   "in_list_view": 0, 
+   "in_standard_filter": 0, 
+   "label": "Half Day", 
+   "length": 0, 
+   "no_copy": 0, 
+   "permlevel": 0, 
+   "precision": "", 
+   "print_hide": 0, 
+   "print_hide_if_no_value": 0, 
+   "read_only": 0, 
+   "remember_last_selected_value": 0, 
+   "report_hide": 0, 
+   "reqd": 0, 
+   "search_index": 0, 
+   "set_only_once": 0, 
+   "translatable": 0, 
+   "unique": 0
+  }, 
+  {
+   "allow_bulk_edit": 0, 
+   "allow_in_quick_entry": 0, 
+   "allow_on_submit": 0, 
+   "bold": 0, 
+   "collapsible": 0, 
+   "columns": 0, 
+   "depends_on": "half_day", 
+   "fieldname": "half_day_date", 
+   "fieldtype": "Date", 
+   "hidden": 0, 
+   "ignore_user_permissions": 0, 
+   "ignore_xss_filter": 0, 
+   "in_filter": 0, 
+   "in_global_search": 0, 
+   "in_list_view": 0, 
+   "in_standard_filter": 0, 
+   "label": "Half Day Date", 
+   "length": 0, 
+   "no_copy": 0, 
+   "permlevel": 0, 
+   "precision": "", 
+   "print_hide": 0, 
+   "print_hide_if_no_value": 0, 
+   "read_only": 0, 
+   "remember_last_selected_value": 0, 
+   "report_hide": 0, 
+   "reqd": 0, 
+   "search_index": 0, 
+   "set_only_once": 0, 
+   "translatable": 0, 
    "unique": 0
   }, 
   {
@@ -205,7 +271,7 @@
    "reqd": 0, 
    "search_index": 0, 
    "set_only_once": 0, 
-   "translatable": 0,
+   "translatable": 0, 
    "unique": 0
   }, 
   {
@@ -238,7 +304,7 @@
    "reqd": 0, 
    "search_index": 0, 
    "set_only_once": 0, 
-   "translatable": 0,
+   "translatable": 0, 
    "unique": 0
   }, 
   {
@@ -270,7 +336,7 @@
    "reqd": 1, 
    "search_index": 0, 
    "set_only_once": 0, 
-   "translatable": 0,
+   "translatable": 0, 
    "unique": 0
   }, 
   {
@@ -303,7 +369,7 @@
    "reqd": 0, 
    "search_index": 0, 
    "set_only_once": 0, 
-   "translatable": 0,
+   "translatable": 0, 
    "unique": 0
   }, 
   {
@@ -335,7 +401,7 @@
    "reqd": 0, 
    "search_index": 0, 
    "set_only_once": 0, 
-   "translatable": 0,
+   "translatable": 0, 
    "unique": 0
   }
  ], 
@@ -349,7 +415,7 @@
  "issingle": 0, 
  "istable": 0, 
  "max_attachments": 0, 
- "modified": "2018-05-25 12:02:05.585184", 
+ "modified": "2018-06-20 17:32:40.735664", 
  "modified_by": "Administrator", 
  "module": "HR", 
  "name": "Compensatory Leave Request", 
@@ -442,4 +508,4 @@
  "title_field": "employee_name", 
  "track_changes": 1, 
  "track_seen": 0
-}
+}
\ No newline at end of file
diff --git a/erpnext/hr/doctype/compensatory_leave_request/compensatory_leave_request.py b/erpnext/hr/doctype/compensatory_leave_request/compensatory_leave_request.py
index 6a67d47..bc4a1b4 100644
--- a/erpnext/hr/doctype/compensatory_leave_request/compensatory_leave_request.py
+++ b/erpnext/hr/doctype/compensatory_leave_request/compensatory_leave_request.py
@@ -5,33 +5,72 @@
 from __future__ import unicode_literals
 import frappe
 from frappe import _
-from frappe.utils import date_diff, add_days
+from frappe.utils import date_diff, add_days, getdate
 from frappe.model.document import Document
-from erpnext.hr.utils import validate_dates, validate_overlap, get_leave_period
+from erpnext.hr.utils import validate_dates, validate_overlap, get_leave_period, get_holidays_for_employee
 
 class CompensatoryLeaveRequest(Document):
 
 	def validate(self):
 		validate_dates(self, self.work_from_date, self.work_end_date)
+		if self.half_day:
+			if not self.half_day_date:
+				frappe.throw(_("Half Day Date is mandatory"))
+			if not getdate(self.work_from_date)<=getdate(self.half_day_date)<=getdate(self.work_end_date):
+				frappe.throw(_("Half Day Date should be in between Work From Date and Work End Date"))
 		validate_overlap(self, self.work_from_date, self.work_end_date)
+		self.validate_holidays()
+		self.validate_attendance()
+		if not self.leave_type:
+			frappe.throw(_("Leave Type is madatory"))
+
+	def validate_attendance(self):
+		query = """select attendance_date, status
+			from `tabAttendance` where
+			attendance_date between %(work_from_date)s and %(work_end_date)s
+			and docstatus=1 and status = 'Present' and employee=%(employee)s"""
+
+		attendance = frappe.db.sql(query, {
+			"work_from_date": self.work_from_date,
+			"work_end_date": self.work_end_date,
+			"employee": self.employee
+		}, as_dict=True)
+		if len(attendance) < date_diff(self.work_end_date, self.work_from_date) + 1:
+			frappe.throw(_("You are not present all day(s) between compensatory leave request days"))
+
+	def validate_holidays(self):
+		holidays = get_holidays_for_employee(self.employee, self.work_from_date, self.work_end_date)
+		if len(holidays) < date_diff(self.work_end_date, self.work_from_date) + 1:
+			frappe.throw(_("Compensatory leave request days not in valid holidays"))
 
 	def on_submit(self):
-		if not self.leave_type:
-			frappe.throw(_("Please select a leave type to submit the request"))
-		else:
-			company = frappe.db.get_value("Employee", self.employee, "company")
-			date_difference = date_diff(self.work_end_date, self.work_from_date) + 1
-			leave_period = get_leave_period(self.work_from_date, self.work_end_date, company)
-			if leave_period:
-				leave_allocation = self.exists_allocation_for_period(leave_period)
-				if leave_allocation:
-					leave_allocation.new_leaves_allocated += date_difference
-					leave_allocation.submit()
-				else:
-					leave_allocation = self.create_leave_allocation(leave_period, date_difference)
-				self.db_set("leave_allocation", leave_allocation.name)
+		company = frappe.db.get_value("Employee", self.employee, "company")
+		date_difference = date_diff(self.work_end_date, self.work_from_date) + 1
+		if self.half_day:
+			date_difference -= 0.5
+		leave_period = get_leave_period(self.work_from_date, self.work_end_date, company)
+		if leave_period:
+			leave_allocation = self.exists_allocation_for_period(leave_period)
+			if leave_allocation:
+				leave_allocation.new_leaves_allocated += date_difference
+				leave_allocation.submit()
 			else:
-				frappe.throw(_("There is no leave period in between {0} and {1}").format(self.work_from_date, self.work_end_date))
+				leave_allocation = self.create_leave_allocation(leave_period, date_difference)
+			self.db_set("leave_allocation", leave_allocation.name)
+		else:
+			frappe.throw(_("There is no leave period in between {0} and {1}").format(self.work_from_date, self.work_end_date))
+
+	def on_cancel(self):
+		if self.leave_allocation:
+			date_difference = date_diff(self.work_end_date, self.work_from_date) + 1
+			if self.half_day:
+				date_difference -= 0.5
+			leave_allocation = frappe.get_doc("Leave Allocation", self.leave_allocation)
+			if leave_allocation:
+				leave_allocation.new_leaves_allocated -= date_difference
+				if leave_allocation.total_leaves_allocated - date_difference <= 0:
+					leave_allocation.total_leaves_allocated = 0
+				leave_allocation.submit()
 
 	def exists_allocation_for_period(self, leave_period):
 		leave_allocation = frappe.db.sql("""
diff --git a/erpnext/hr/doctype/leave_allocation/leave_allocation.py b/erpnext/hr/doctype/leave_allocation/leave_allocation.py
index 7cffa4c..dc270db 100755
--- a/erpnext/hr/doctype/leave_allocation/leave_allocation.py
+++ b/erpnext/hr/doctype/leave_allocation/leave_allocation.py
@@ -92,7 +92,7 @@
 
 		self.total_leaves_allocated = flt(self.carry_forwarded_leaves) + flt(self.new_leaves_allocated)
 
-		if not self.total_leaves_allocated and not frappe.db.get_value("Leave Type", self.leave_type, "is_earned_leave"):
+		if not self.total_leaves_allocated and not frappe.db.get_value("Leave Type", self.leave_type, "is_earned_leave") and not frappe.db.get_value("Leave Type", self.leave_type, "is_compensatory"):
 			frappe.throw(_("Total leaves allocated is mandatory for Leave Type {0}".format(self.leave_type)))
 
 	def validate_total_leaves_allocated(self):