Shift Management (#13667)

* [fix] #13634

* review_changes

* rename function

* rename function
diff --git a/erpnext/config/hr.py b/erpnext/config/hr.py
index 9a82981..fb00529 100644
--- a/erpnext/config/hr.py
+++ b/erpnext/config/hr.py
@@ -263,10 +263,6 @@
 				{
 					"type": "doctype",
 					"name": "Shift Assignment",
-				},
-				{
-					"type": "doctype",
-					"name": "Shift Assignment Tool",
 				}
 			]
 		},
diff --git a/erpnext/hr/doctype/shift_assignment/shift_assignment.json b/erpnext/hr/doctype/shift_assignment/shift_assignment.json
index 87d69e4..897cfed 100644
--- a/erpnext/hr/doctype/shift_assignment/shift_assignment.json
+++ b/erpnext/hr/doctype/shift_assignment/shift_assignment.json
@@ -1,7 +1,7 @@
 {
  "allow_copy": 0, 
  "allow_guest_to_view": 0, 
- "allow_import": 0, 
+ "allow_import": 1, 
  "allow_rename": 0, 
  "autoname": "SH.#####", 
  "beta": 0, 
@@ -26,7 +26,7 @@
    "ignore_xss_filter": 0, 
    "in_filter": 0, 
    "in_global_search": 0, 
-   "in_list_view": 1, 
+   "in_list_view": 0, 
    "in_standard_filter": 0, 
    "label": "Employee", 
    "length": 0, 
@@ -183,7 +183,7 @@
    "ignore_xss_filter": 0, 
    "in_filter": 0, 
    "in_global_search": 0, 
-   "in_list_view": 1, 
+   "in_list_view": 0, 
    "in_standard_filter": 0, 
    "label": "Company", 
    "length": 0, 
@@ -215,7 +215,7 @@
    "ignore_xss_filter": 0, 
    "in_filter": 0, 
    "in_global_search": 0, 
-   "in_list_view": 0, 
+   "in_list_view": 1, 
    "in_standard_filter": 0, 
    "label": "Date", 
    "length": 0, 
@@ -239,6 +239,38 @@
    "bold": 0, 
    "collapsible": 0, 
    "columns": 0, 
+   "fieldname": "shift_request", 
+   "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": "Shift Request", 
+   "length": 0, 
+   "no_copy": 0, 
+   "options": "Shift Request", 
+   "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, 
    "fieldname": "amended_from", 
    "fieldtype": "Link", 
    "hidden": 0, 
@@ -275,7 +307,7 @@
  "issingle": 0, 
  "istable": 0, 
  "max_attachments": 0, 
- "modified": "2018-04-14 15:42:12.617715", 
+ "modified": "2018-04-17 14:50:09.125737", 
  "modified_by": "Administrator", 
  "module": "HR", 
  "name": "Shift Assignment", 
diff --git a/erpnext/hr/doctype/shift_assignment/shift_assignment.py b/erpnext/hr/doctype/shift_assignment/shift_assignment.py
index 272dd2b..fbbfe31 100644
--- a/erpnext/hr/doctype/shift_assignment/shift_assignment.py
+++ b/erpnext/hr/doctype/shift_assignment/shift_assignment.py
@@ -4,7 +4,77 @@
 
 from __future__ import unicode_literals
 import frappe
+from frappe import _
 from frappe.model.document import Document
+from frappe.utils import cint, cstr, date_diff, flt, formatdate, getdate
+
+class OverlapError(frappe.ValidationError): pass
 
 class ShiftAssignment(Document):
-	pass
+	def validate(self):
+		self.validate_overlapping_dates();
+
+	def validate_overlapping_dates(self):
+			if not self.name:
+				self.name = "New Shift Assignment"
+
+			d = frappe.db.sql("""
+				select
+					name, shift_type, date
+				from `tabShift Assignment`
+				where employee = %(employee)s and docstatus < 2
+				and date = %(date)s
+				and name != %(name)s""", {
+					"employee": self.employee,
+					"shift_type": self.shift_type,
+					"date": self.date,
+					"name": self.name
+				}, as_dict = 1)
+
+			for date_overlap in d:
+				if date_overlap['name']:
+					self.throw_overlap_error(date_overlap)
+
+	def throw_overlap_error(self, d):
+		msg = _("Employee {0} has already applied for {1} on {2} : ").format(self.employee,
+			d['shift_type'], formatdate(d['date'])) \
+			+ """ <b><a href="#Form/Shift Request/{0}">{0}</a></b>""".format(d["name"])
+		frappe.throw(msg, OverlapError)
+
+@frappe.whitelist()
+def get_events(start, end, filters=None):
+	events = []
+
+	employee = frappe.db.get_value("Employee", {"user_id": frappe.session.user}, ["name", "company"],
+		as_dict=True)
+	if employee:
+		employee, company = employee.name, employee.company
+	else:
+		employee=''
+		company=frappe.db.get_value("Global Defaults", None, "default_company")
+
+	from frappe.desk.reportview import get_filters_cond
+	conditions = get_filters_cond("Shift Assignment", filters, [])
+	add_assignments(events, start, end, conditions=conditions)
+	return events
+
+def add_assignments(events, start, end, conditions=None):
+	query = """select name, date, employee_name, 
+		employee, docstatus
+		from `tabShift Assignment` where
+		date <= %(date)s
+		and docstatus < 2"""
+	if conditions:
+		query += conditions
+
+	for d in frappe.db.sql(query, {"date":start, "date":end}, as_dict=True):
+		e = {
+			"name": d.name,
+			"doctype": "Shift Assignment",
+			"date": d.date,
+			"title": cstr(d.employee_name) + \
+				cstr(d.shift_type),
+			"docstatus": d.docstatus
+		}
+		if e not in events:
+			events.append(e)
\ No newline at end of file
diff --git a/erpnext/hr/doctype/shift_assignment/shift_assignment_calendar.js b/erpnext/hr/doctype/shift_assignment/shift_assignment_calendar.js
new file mode 100644
index 0000000..c2c9bc0
--- /dev/null
+++ b/erpnext/hr/doctype/shift_assignment/shift_assignment_calendar.js
@@ -0,0 +1,19 @@
+// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.views.calendar["Shift Assignment"] = {
+	field_map: {
+		"start": "date",
+		"end": "date",
+		"id": "name",
+		"docstatus": 1
+	},
+	options: {
+		header: {
+			left: 'prev,next today',
+			center: 'title',
+			right: 'month'
+		}
+	},
+	get_events_method: "erpnext.hr.doctype.shift_assignment.shift_assignment.get_events"
+}
\ No newline at end of file
diff --git a/erpnext/hr/doctype/shift_request/shift_request.json b/erpnext/hr/doctype/shift_request/shift_request.json
index 1210d15..04a3edf 100644
--- a/erpnext/hr/doctype/shift_request/shift_request.json
+++ b/erpnext/hr/doctype/shift_request/shift_request.json
@@ -1,7 +1,7 @@
 {
  "allow_copy": 0, 
  "allow_guest_to_view": 0, 
- "allow_import": 0, 
+ "allow_import": 1, 
  "allow_rename": 0, 
  "autoname": "SREQ.#####", 
  "beta": 0, 
@@ -19,6 +19,38 @@
    "bold": 0, 
    "collapsible": 0, 
    "columns": 0, 
+   "fieldname": "shift_type", 
+   "fieldtype": "Link", 
+   "hidden": 0, 
+   "ignore_user_permissions": 0, 
+   "ignore_xss_filter": 0, 
+   "in_filter": 0, 
+   "in_global_search": 0, 
+   "in_list_view": 1, 
+   "in_standard_filter": 0, 
+   "label": "Shift Type", 
+   "length": 0, 
+   "no_copy": 0, 
+   "options": "Shift Type", 
+   "permlevel": 0, 
+   "precision": "", 
+   "print_hide": 0, 
+   "print_hide_if_no_value": 0, 
+   "read_only": 0, 
+   "remember_last_selected_value": 0, 
+   "report_hide": 0, 
+   "reqd": 1, 
+   "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, 
    "fieldname": "employee", 
    "fieldtype": "Link", 
    "hidden": 0, 
@@ -83,6 +115,36 @@
    "bold": 0, 
    "collapsible": 0, 
    "columns": 0, 
+   "fieldname": "column_break_4", 
+   "fieldtype": "Column Break", 
+   "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, 
+   "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_on_submit": 0, 
+   "bold": 0, 
+   "collapsible": 0, 
+   "columns": 0, 
    "fieldname": "company", 
    "fieldtype": "Link", 
    "hidden": 0, 
@@ -115,38 +177,6 @@
    "bold": 0, 
    "collapsible": 0, 
    "columns": 0, 
-   "fieldname": "shift_type", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 1, 
-   "in_standard_filter": 0, 
-   "label": "Shift Type", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Shift Type", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 1, 
-   "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, 
    "fieldname": "from_date", 
    "fieldtype": "Date", 
    "hidden": 0, 
@@ -245,7 +275,7 @@
  "issingle": 0, 
  "istable": 0, 
  "max_attachments": 0, 
- "modified": "2018-04-14 15:40:39.590051", 
+ "modified": "2018-04-16 11:01:25.902995", 
  "modified_by": "Administrator", 
  "module": "HR", 
  "name": "Shift Request", 
diff --git a/erpnext/hr/doctype/shift_request/shift_request.py b/erpnext/hr/doctype/shift_request/shift_request.py
index e6755ae..7057d20 100644
--- a/erpnext/hr/doctype/shift_request/shift_request.py
+++ b/erpnext/hr/doctype/shift_request/shift_request.py
@@ -4,7 +4,84 @@
 
 from __future__ import unicode_literals
 import frappe
+from frappe import _
 from frappe.model.document import Document
+from frappe.utils import formatdate, getdate
+
+class OverlapError(frappe.ValidationError): pass
 
 class ShiftRequest(Document):
-	pass
+	def validate(self):
+		self.validate_dates();
+		self.validate_shift_request_overlap_dates();
+
+	def on_submit(self):
+		date_list = self.get_working_days(self.from_date, self.to_date)
+		for date in date_list:
+			assignment_doc = frappe.new_doc("Shift Assignment")
+			assignment_doc.company = self.company
+			assignment_doc.shift_type = self.shift_type
+			assignment_doc.employee = self.employee
+			assignment_doc.date = date
+			assignment_doc.shift_request = self.name
+			assignment_doc.insert()
+			assignment_doc.submit()
+
+	def validate_dates(self):
+		if self.from_date and self.to_date and (getdate(self.to_date) < getdate(self.from_date)):
+			frappe.throw(_("To date cannot be before from date"))
+
+	def validate_shift_request_overlap_dates(self):
+			if not self.name:
+				self.name = "New Shift Request"
+
+			d = frappe.db.sql("""
+				select
+					name, shift_type, from_date, to_date
+				from `tabShift Request`
+				where employee = %(employee)s and docstatus < 2
+				and ((%(from_date)s >= from_date
+					and %(from_date)s <= to_date) or
+					( %(to_date)s >= from_date
+					and %(to_date)s <= to_date ))
+				and name != %(name)s""", {
+					"employee": self.employee,
+					"shift_type": self.shift_type,
+					"from_date": self.from_date,
+					"to_date": self.to_date,
+					"name": self.name
+				}, as_dict=1)
+
+			for date_overlap in d:
+				if date_overlap ['name']:
+					self.throw_overlap_error(date_overlap)
+
+	def throw_overlap_error(self, d):
+		msg = _("Employee {0} has already applied for {1} between {2} and {3} : ").format(self.employee,
+			d['shift_type'], formatdate(d['from_date']), formatdate(d['to_date'])) \
+			+ """ <b><a href="#Form/Shift Request/{0}">{0}</a></b>""".format(d["name"])
+		frappe.throw(msg, OverlapError)
+
+	def get_working_days(self, start_date, end_date):
+		start_date, end_date = getdate(start_date), getdate(end_date)
+
+		from datetime import timedelta
+
+		date_list = []
+		employee_holiday_list = []
+
+		employee_holidays = frappe.db.sql("""select holiday_date from `tabHoliday`
+								where parent in (select holiday_list from `tabEmployee`
+								where name = %s)""",self.employee,as_dict=1)
+
+		for d in employee_holidays:
+			employee_holiday_list.append(d.holiday_date)
+
+		reference_date = start_date
+		
+		while reference_date <= end_date:
+			if reference_date not in employee_holiday_list:
+				date_list.append(reference_date)
+			reference_date += timedelta(days=1)
+
+		return date_list
\ No newline at end of file
diff --git a/erpnext/hr/doctype/shift_type/shift_type.py b/erpnext/hr/doctype/shift_type/shift_type.py
index 6721439..88ae243 100644
--- a/erpnext/hr/doctype/shift_type/shift_type.py
+++ b/erpnext/hr/doctype/shift_type/shift_type.py
@@ -7,4 +7,4 @@
 from frappe.model.document import Document
 
 class ShiftType(Document):
-	pass
+	pass
\ No newline at end of file