[Feature] Add default duration and color to appointment type (#13296)

* Add default duration and color to appointment type

When creating a Patient Appointment, the Duration field will be populated when the Appointment Type is set. This is preserved when checking availability and booking an appointment.
`Appointment Type.color` is used in the calendar display.

* Remove accidental fixed date from git

* Update modified timestamp to ensure migrations.

Set to the timestamp of dd506a0961437540872c01d3487b8e37806c0bb0
diff --git a/erpnext/healthcare/doctype/appointment_type/appointment_type.json b/erpnext/healthcare/doctype/appointment_type/appointment_type.json
index 9d331fa..46ca5d9 100644
--- a/erpnext/healthcare/doctype/appointment_type/appointment_type.json
+++ b/erpnext/healthcare/doctype/appointment_type/appointment_type.json
@@ -71,6 +71,67 @@
    "search_index": 0, 
    "set_only_once": 0, 
    "unique": 0
+  },
+  {
+   "allow_bulk_edit": 0,
+   "allow_on_submit": 0,
+   "bold": 1,
+   "collapsible": 0,
+   "columns": 0,
+   "description": "In Minutes",
+   "fieldname": "default_duration",
+   "fieldtype": "Int",
+   "hidden": 1,
+   "ignore_user_permissions": 0,
+   "ignore_xss_filter": 0,
+   "in_filter": 1,
+   "in_global_search": 0,
+   "in_list_view": 1,
+   "in_standard_filter": 0,
+   "label": "Default Duration",
+   "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,
+   "unique": 0
+  },
+  {
+   "allow_bulk_edit": 0,
+   "allow_on_submit": 0,
+   "bold": 0,
+   "collapsible": 0,
+   "columns": 0,
+   "fieldname": "color",
+   "fieldtype": "Color",
+   "hidden": 1,
+   "ignore_user_permissions": 0,
+   "ignore_xss_filter": 0,
+   "in_filter": 0,
+   "in_global_search": 0,
+   "in_list_view": 1,
+   "in_standard_filter": 0,
+   "label": "Color",
+   "length": 0,
+   "no_copy": 1,
+   "permlevel": 0,
+   "precision": "",
+   "print_hide": 0,
+   "print_hide_if_no_value": 0,
+   "read_only": 0,
+   "remember_last_selected_value": 0,
+   "report_hide": 1,
+   "reqd": 0,
+   "search_index": 0,
+   "set_only_once": 0,
+   "unique": 0
   }
  ], 
  "has_web_view": 0, 
@@ -83,7 +144,7 @@
  "issingle": 0, 
  "istable": 0, 
  "max_attachments": 0, 
- "modified": "2017-10-05 11:07:26.369657", 
+ "modified": "2018-03-13 12:10:00.123456",
  "modified_by": "Administrator", 
  "module": "Healthcare", 
  "name": "Appointment Type", 
diff --git a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js
index f34f7cf..41974a6 100644
--- a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js
+++ b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js
@@ -137,6 +137,7 @@
 
 			var slot_details = data.slot_details;
 			var slot_html = "";
+			var duration = frm.doc.duration | 0;
 			$.each(slot_details, function(i, slot_detail){
 				slot_html = slot_html + `<label>${slot_detail['slot_name']}</label>`;
 				slot_html = slot_html + `<br/>` + slot_detail['avail_slot'].map(slot => {
@@ -155,7 +156,7 @@
 							}
 							start_time = booked_moment;
 							let end_time = booked_moment.add(booked.duration, 'minutes');
-							if(end_time.isSameOrAfter(to_time)){
+							if(end_time.isSameOrAfter(to_time) || end_time.add(duration).isAfter(to_time)){
 								disabled = 'disabled="disabled"';
 								return false;
 							}else{
@@ -163,9 +164,10 @@
 							}
 						}
 					});
+
 					return `<button class="btn btn-default"
 						data-name=${start_str}
-						data-duration=${interval}
+						data-duration=${duration || interval}
 						data-service-unit="${slot_detail['service_unit'] || ''}"
 						style="margin: 0 10px 10px 0; width: 72px;" ${disabled}>
 						${start_str.substring(0, start_str.length - 3)}
@@ -293,6 +295,21 @@
 	}
 });
 
+frappe.ui.form.on("Patient Appointment", "appointment_type", function(frm) {
+	if(frm.doc.appointment_type) {
+		frappe.call({
+			"method": "frappe.client.get",
+			args: {
+				doctype: "Appointment Type",
+				name: frm.doc.appointment_type
+			},
+			callback: function (data) {
+				frappe.model.set_value(frm.doctype,frm.docname, "duration",data.message.default_duration);
+			}
+		});
+	}
+});
+
 var calculate_age = function(birth) {
 	var ageMS = Date.parse(Date()) - Date.parse(birth);
 	var age = new Date();
diff --git a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.json b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.json
index 022a9d6..1cdf393 100644
--- a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.json
+++ b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.json
@@ -176,17 +176,16 @@
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
-   "columns": 0, 
-   "depends_on": "eval:!doc.__islocal", 
+   "columns": 0,
    "description": "In Minutes", 
    "fieldname": "duration", 
    "fieldtype": "Int", 
    "hidden": 0, 
    "ignore_user_permissions": 0, 
    "ignore_xss_filter": 0, 
-   "in_filter": 0, 
+   "in_filter": 1,
    "in_global_search": 0, 
-   "in_list_view": 0, 
+   "in_list_view": 1,
    "in_standard_filter": 0, 
    "label": "Duration", 
    "length": 0, 
@@ -755,7 +754,7 @@
  "issingle": 0, 
  "istable": 0, 
  "max_attachments": 0, 
- "modified": "2018-02-26 12:44:33.756124", 
+ "modified": "2018-03-13 12:10:00.123456",
  "modified_by": "Administrator", 
  "module": "Healthcare", 
  "name": "Patient Appointment", 
diff --git a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py
index 926dc28..861f7c4 100755
--- a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py
+++ b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py
@@ -327,11 +327,12 @@
 	"""
 	from frappe.desk.calendar import get_event_conditions
 	conditions = get_event_conditions("Patient Appointment", filters)
-	data = frappe.db.sql("""select name, patient, physician, status,
-		duration, timestamp(appointment_date, appointment_time) as
-		'start' from `tabPatient Appointment` where
-		(appointment_date between %(start)s and %(end)s)
-		and docstatus < 2 {conditions}""".format(conditions=conditions),
+	data = frappe.db.sql("""select `tabPatient Appointment`.name, patient, physician, status,
+		duration, timestamp(appointment_date, appointment_time) as 'start', type.color as 'color'
+    	from `tabPatient Appointment`
+    	left join `tabAppointment Type` as type on `tabPatient Appointment`.appointment_type=type.name
+    	where (appointment_date between %(start)s and %(end)s )
+		and `tabPatient Appointment`.docstatus < 2 {conditions}""".format(conditions=conditions),
 		{"start": start, "end": end}, as_dict=True, update={"allDay": 0})
 	for item in data:
 		item.end = item.start + datetime.timedelta(minutes = item.duration)
diff --git a/erpnext/healthcare/doctype/patient_appointment/patient_appointment_calendar.js b/erpnext/healthcare/doctype/patient_appointment/patient_appointment_calendar.js
index bfb53b8..2905949 100644
--- a/erpnext/healthcare/doctype/patient_appointment/patient_appointment_calendar.js
+++ b/erpnext/healthcare/doctype/patient_appointment/patient_appointment_calendar.js
@@ -5,7 +5,8 @@
 		"end": "end",
 		"id": "name",
 		"title": "patient",
-		"allDay": "allDay"
+		"allDay": "allDay",
+		"eventColor": "color"
 	},
 	gantt: true,
 	get_events_method: "erpnext.healthcare.doctype.patient_appointment.patient_appointment.get_events",