Healthcare - Patient Appointment - Service unit based scheduling and booking (#13211)

* New Document - Patient Service Unit

* Physician - schedule based on patient service unit

* Consultation - Remove validation on submit

* Consultation - Label changed from Drug Prescription to Medication

* Availability check and book appointment based on service unit, appointment invoice creation optimized

* patch fixes

* Patient Service Unit - field - overlap_appointments

* Patient Appointment - Service Unit based scheduling and booking

* Patient Appointment - issue fixed #13016 Healthcare Patient Appointment Save Button Issue - remove validation on save and enable save on book appointment

* Codacy fixes on PR #13211

* Codacy fixes on PR #13211

* Fee validity test -fixes

* Fee Validity - test - fixes
diff --git a/erpnext/config/healthcare.py b/erpnext/config/healthcare.py
index 34a9b70..4e8bb48 100644
--- a/erpnext/config/healthcare.py
+++ b/erpnext/config/healthcare.py
@@ -86,6 +86,11 @@
 					"type": "doctype",
 					"name": "Medical Code",
 					"label": _("Medical Code"),
+				},
+				{
+					"type": "doctype",
+					"name": "Patient Service Unit",
+					"label": _("Patient Service Unit")
 				}
 			]
 		},
diff --git a/erpnext/healthcare/doctype/consultation/consultation.json b/erpnext/healthcare/doctype/consultation/consultation.json
index 184f484..c1bd37a 100644
--- a/erpnext/healthcare/doctype/consultation/consultation.json
+++ b/erpnext/healthcare/doctype/consultation/consultation.json
@@ -767,7 +767,7 @@
    "in_global_search": 0, 
    "in_list_view": 0, 
    "in_standard_filter": 0, 
-   "label": "Drug Prescription", 
+   "label": "Medication", 
    "length": 0, 
    "no_copy": 0, 
    "permlevel": 0, 
@@ -797,7 +797,7 @@
    "in_global_search": 0, 
    "in_list_view": 0, 
    "in_standard_filter": 0, 
-   "label": "Drug Prescription", 
+   "label": "Medication", 
    "length": 0, 
    "no_copy": 0, 
    "options": "Drug Prescription", 
@@ -945,7 +945,7 @@
  "issingle": 0, 
  "istable": 0, 
  "max_attachments": 0, 
- "modified": "2017-12-28 11:25:35.256848", 
+ "modified": "2018-02-19 11:35:13.826577", 
  "modified_by": "Administrator", 
  "module": "Healthcare", 
  "name": "Consultation", 
diff --git a/erpnext/healthcare/doctype/consultation/consultation.py b/erpnext/healthcare/doctype/consultation/consultation.py
index 7d41afe..451c491 100755
--- a/erpnext/healthcare/doctype/consultation/consultation.py
+++ b/erpnext/healthcare/doctype/consultation/consultation.py
@@ -4,7 +4,6 @@
 
 from __future__ import unicode_literals
 import frappe
-from frappe import _
 from frappe.model.document import Document
 from frappe.utils import getdate
 import json
@@ -19,10 +18,6 @@
 	def after_insert(self):
 		insert_consultation_to_medical_record(self)
 
-	def on_submit(self):
-		if not self.diagnosis or not self.symptoms:
-			frappe.throw(_("Diagnosis and Complaints cannot be left blank"))
-
 	def on_cancel(self):
 		if(self.appointment):
 			frappe.db.set_value("Patient Appointment", self.appointment, "status", "Open")
diff --git a/erpnext/healthcare/doctype/fee_validity/test_fee_validity.py b/erpnext/healthcare/doctype/fee_validity/test_fee_validity.py
index 53f82e0..7ad673d 100644
--- a/erpnext/healthcare/doctype/fee_validity/test_fee_validity.py
+++ b/erpnext/healthcare/doctype/fee_validity/test_fee_validity.py
@@ -5,7 +5,7 @@
 
 import frappe
 import unittest
-from erpnext.healthcare.doctype.patient_appointment.patient_appointment import create_invoice
+from erpnext.healthcare.doctype.patient_appointment.patient_appointment import invoice_appointment
 from frappe.utils.make_random import get_random
 from frappe.utils import nowdate, add_days
 # test_records = frappe.get_test_records('Fee Validity')
@@ -14,6 +14,7 @@
 	def test_fee_validity(self):
 		patient = get_random("Patient")
 		physician = get_random("Physician")
+		department = get_random("Medical Department")
 
 		if not patient:
 			patient = frappe.new_doc("Patient")
@@ -22,33 +23,43 @@
 			patient.save(ignore_permissions=True)
 			patient = patient.name
 
+		if not department:
+			medical_department = frappe.new_doc("Medical Department")
+			medical_department.department = "Test Medical Department"
+			medical_department.save(ignore_permissions=True)
+			department = medical_department.name
+
 		if not physician:
 			physician = frappe.new_doc("Physician")
 			physician.first_name = "Amit Jain"
+			physician.department = department
 			physician.save(ignore_permissions=True)
 			physician = physician.name
 
+
+
 		frappe.db.set_value("Healthcare Settings", None, "max_visit", 2)
 		frappe.db.set_value("Healthcare Settings", None, "valid_days", 7)
 
-		appointment = create_appointment(patient, physician, nowdate())
+		appointment = create_appointment(patient, physician, nowdate(), department)
 		invoice = frappe.db.get_value("Patient Appointment", appointment.name, "sales_invoice")
 		self.assertEqual(invoice, None)
-		create_invoice(frappe.defaults.get_global_default("company"), physician, patient, appointment.name, appointment.appointment_date)
-		appointment = create_appointment(patient, physician, add_days(nowdate(), 4))
+		invoice_appointment(appointment)
+		appointment = create_appointment(patient, physician, add_days(nowdate(), 4), department)
 		invoice = frappe.db.get_value("Patient Appointment", appointment.name, "sales_invoice")
 		self.assertTrue(invoice)
-		appointment = create_appointment(patient, physician, add_days(nowdate(), 5))
+		appointment = create_appointment(patient, physician, add_days(nowdate(), 5), department)
 		invoice = frappe.db.get_value("Patient Appointment", appointment.name, "sales_invoice")
 		self.assertEqual(invoice, None)
-		appointment = create_appointment(patient, physician, add_days(nowdate(), 10))
+		appointment = create_appointment(patient, physician, add_days(nowdate(), 10), department)
 		invoice = frappe.db.get_value("Patient Appointment", appointment.name, "sales_invoice")
 		self.assertEqual(invoice, None)
 
-def create_appointment(patient, physician, appointment_date):
+def create_appointment(patient, physician, appointment_date, department):
 	appointment = frappe.new_doc("Patient Appointment")
 	appointment.patient = patient
 	appointment.physician = physician
+	appointment.department = department
 	appointment.appointment_date = appointment_date
 	appointment.save(ignore_permissions=True)
 	return appointment
diff --git a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js
index 8912157..f34f7cf 100644
--- a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js
+++ b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js
@@ -15,6 +15,20 @@
 				filters: {"disabled": 0}
 			};
 		});
+		frm.set_query("physician", function() {
+			return {
+				filters: {
+					'department': frm.doc.department
+				}
+			};
+		});
+		frm.set_query("service_unit", function(){
+			return {
+				filters: {
+					"is_group": false,
+				}
+			};
+		});
 		if(frm.doc.patient){
 			frm.add_custom_button(__('Medical Record'), function() {
 				frappe.route_options = {"patient": frm.doc.patient};
@@ -83,11 +97,10 @@
 				date: appointment_date
 			},
 			callback: (r) => {
-				// console.log(r);
 				var data = r.message;
-				if(data.available_slots.length > 0) {
+				if(data.slot_details.length > 0){
 					show_availability(data);
-				} else {
+				}else{
 					show_empty_state();
 				}
 			}
@@ -108,48 +121,71 @@
 				primary_action_label: __("Book"),
 				primary_action: function() {
 					// book slot
-					frm.set_value('appointment_time', selected_slot);
-					frm.set_value('duration', data.time_per_appointment);
+					var btn_selected = $wrapper.find('button.btn-selected-slot');
+					frm.set_value('appointment_time', btn_selected.attr('data-name'));
+					frm.set_value('service_unit', btn_selected.attr('data-service-unit') || '');
+					frm.set_value('duration', btn_selected.attr('data-duration'));
 					d.hide();
 					frm.save();
+					frm.enable_save();
 				}
 			});
 			var $wrapper = d.fields_dict.available_slots.$wrapper;
-			var selected_slot = null;
 
 			// disable dialog action initially
 			d.get_primary_btn().attr('disabled', true);
 
-			// make buttons for each slot
-			var slot_html = data.available_slots.map(slot => {
-				return `<button class="btn btn-default"
-					data-name=${slot.from_time}
-					style="margin: 0 10px 10px 0; width: 72px">
-					${slot.from_time.substring(0, slot.from_time.length - 3)}
-				</button>`;
-			}).join("");
+			var slot_details = data.slot_details;
+			var slot_html = "";
+			$.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 => {
+					let disabled = '';
+					let start_str = slot.from_time;
+					let start_time = moment(slot.from_time, 'HH:mm:ss');
+					let to_time = moment(slot.to_time, 'HH:mm:ss');
+					let interval = (to_time - start_time)/60000 | 0;
+					// iterate in all booked appointments, update the start time and duration
+					slot_detail['appointments'].forEach(function(booked) {
+						let booked_moment = moment(booked.appointment_time, 'HH:mm:ss');
+						if(booked_moment.isSame(start_time) || booked_moment.isBetween(start_time, to_time)){
+							if(booked.duration == 0){
+								disabled = 'disabled="disabled"';
+								return false;
+							}
+							start_time = booked_moment;
+							let end_time = booked_moment.add(booked.duration, 'minutes');
+							if(end_time.isSameOrAfter(to_time)){
+								disabled = 'disabled="disabled"';
+								return false;
+							}else{
+								start_str = end_time.format('HH:mm:ss');
+							}
+						}
+					});
+					return `<button class="btn btn-default"
+						data-name=${start_str}
+						data-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)}
+					</button>`;
+				}).join("");
+				slot_html = slot_html + `<br/>`;
+			});
 
 			$wrapper
 				.css('margin-bottom', 0)
 				.addClass('text-center')
 				.html(slot_html);
 
-			// disable buttons for which appointments are booked
-			data.appointments.map(slot => {
-				if(slot.status == "Scheduled" || slot.status == "Open" || slot.status == "Closed"){
-					$wrapper
-						.find(`button[data-name="${slot.appointment_time}"]`)
-						.attr('disabled', true);
-				}
-			});
-
 			// blue button when clicked
 			$wrapper.on('click', 'button', function() {
 				var $btn = $(this);
 				$wrapper.find('button').removeClass('btn-primary');
+				$wrapper.find('button').removeClass('btn-selected-slot');
 				$btn.addClass('btn-primary');
-				selected_slot = $btn.attr('data-name');
-
+				$btn.addClass('btn-selected-slot');
 				// enable dialog action
 				d.get_primary_btn().attr('disabled', null);
 			});
@@ -209,12 +245,9 @@
 };
 
 var btn_invoice_consultation = function(frm){
-	var doc = frm.doc;
 	frappe.call({
-		method:
-		"erpnext.healthcare.doctype.patient_appointment.patient_appointment.create_invoice",
-		args: {company: doc.company, physician:doc.physician, patient: doc.patient,
-			appointment_id: doc.name, appointment_date:doc.appointment_date },
+		doc: frm.doc,
+		method:"create_invoice",
 		callback: function(data){
 			if(!data.exc){
 				if(data.message){
diff --git a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.json b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.json
index 1dcdbb5..022a9d6 100644
--- a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.json
+++ b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.json
@@ -42,6 +42,39 @@
    "reqd": 1, 
    "search_index": 1, 
    "set_only_once": 1, 
+   "translatable": 0, 
+   "unique": 0
+  }, 
+  {
+   "allow_bulk_edit": 0, 
+   "allow_on_submit": 0, 
+   "bold": 0, 
+   "collapsible": 0, 
+   "columns": 0, 
+   "fieldname": "department", 
+   "fieldtype": "Link", 
+   "hidden": 0, 
+   "ignore_user_permissions": 1, 
+   "ignore_xss_filter": 0, 
+   "in_filter": 0, 
+   "in_global_search": 0, 
+   "in_list_view": 1, 
+   "in_standard_filter": 1, 
+   "label": "Department", 
+   "length": 0, 
+   "no_copy": 0, 
+   "options": "Medical Department", 
+   "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": 1, 
+   "set_only_once": 1, 
+   "translatable": 0, 
    "unique": 0
   }, 
   {
@@ -73,6 +106,7 @@
    "reqd": 1, 
    "search_index": 1, 
    "set_only_once": 1, 
+   "translatable": 0, 
    "unique": 0
   }, 
   {
@@ -103,6 +137,71 @@
    "reqd": 1, 
    "search_index": 1, 
    "set_only_once": 1, 
+   "translatable": 0, 
+   "unique": 0
+  }, 
+  {
+   "allow_bulk_edit": 0, 
+   "allow_on_submit": 0, 
+   "bold": 0, 
+   "collapsible": 0, 
+   "columns": 0, 
+   "fieldname": "appointment_time", 
+   "fieldtype": "Time", 
+   "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": "Time", 
+   "length": 0, 
+   "no_copy": 0, 
+   "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, 
+   "depends_on": "eval:!doc.__islocal", 
+   "description": "In Minutes", 
+   "fieldname": "duration", 
+   "fieldtype": "Int", 
+   "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": "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, 
+   "translatable": 0, 
    "unique": 0
   }, 
   {
@@ -134,6 +233,7 @@
    "reqd": 0, 
    "search_index": 0, 
    "set_only_once": 0, 
+   "translatable": 0, 
    "unique": 0
   }, 
   {
@@ -151,8 +251,42 @@
    "in_global_search": 0, 
    "in_list_view": 0, 
    "in_standard_filter": 0, 
+   "label": "", 
    "length": 0, 
    "no_copy": 0, 
+   "options": "", 
+   "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": "service_unit", 
+   "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": "Service Unit", 
+   "length": 0, 
+   "no_copy": 0, 
+   "options": "Patient Service Unit", 
    "permlevel": 0, 
    "precision": "", 
    "print_hide": 0, 
@@ -162,7 +296,8 @@
    "report_hide": 0, 
    "reqd": 0, 
    "search_index": 0, 
-   "set_only_once": 0, 
+   "set_only_once": 1, 
+   "translatable": 0, 
    "unique": 0
   }, 
   {
@@ -195,6 +330,7 @@
    "reqd": 0, 
    "search_index": 0, 
    "set_only_once": 0, 
+   "translatable": 0, 
    "unique": 0
   }, 
   {
@@ -226,6 +362,7 @@
    "reqd": 0, 
    "search_index": 0, 
    "set_only_once": 0, 
+   "translatable": 0, 
    "unique": 0
   }, 
   {
@@ -257,6 +394,7 @@
    "reqd": 0, 
    "search_index": 0, 
    "set_only_once": 0, 
+   "translatable": 0, 
    "unique": 0
   }, 
   {
@@ -271,7 +409,7 @@
    "hidden": 0, 
    "ignore_user_permissions": 0, 
    "ignore_xss_filter": 0, 
-   "in_filter": 0, 
+   "in_filter": 1, 
    "in_global_search": 0, 
    "in_list_view": 1, 
    "in_standard_filter": 0, 
@@ -289,6 +427,7 @@
    "reqd": 0, 
    "search_index": 1, 
    "set_only_once": 0, 
+   "translatable": 0, 
    "unique": 0
   }, 
   {
@@ -319,7 +458,8 @@
    "report_hide": 0, 
    "reqd": 0, 
    "search_index": 0, 
-   "set_only_once": 1, 
+   "set_only_once": 0, 
+   "translatable": 0, 
    "unique": 0
   }, 
   {
@@ -349,68 +489,8 @@
    "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": "appointment_time", 
-   "fieldtype": "Time", 
-   "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": "Time", 
-   "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": 1, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "description": "In Minutes", 
-   "fieldname": "duration", 
-   "fieldtype": "Int", 
-   "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": "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, 
+   "translatable": 0, 
    "unique": 0
   }, 
   {
@@ -441,6 +521,7 @@
    "reqd": 0, 
    "search_index": 1, 
    "set_only_once": 0, 
+   "translatable": 0, 
    "unique": 0
   }, 
   {
@@ -470,37 +551,7 @@
    "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": "department", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 1, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 1, 
-   "in_standard_filter": 1, 
-   "label": "Department", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Medical Department", 
-   "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": 1, 
-   "set_only_once": 1, 
+   "translatable": 0, 
    "unique": 0
   }, 
   {
@@ -532,6 +583,7 @@
    "reqd": 0, 
    "search_index": 1, 
    "set_only_once": 0, 
+   "translatable": 0, 
    "unique": 0
   }, 
   {
@@ -563,6 +615,7 @@
    "reqd": 0, 
    "search_index": 0, 
    "set_only_once": 0, 
+   "translatable": 0, 
    "unique": 0
   }, 
   {
@@ -593,6 +646,7 @@
    "reqd": 0, 
    "search_index": 0, 
    "set_only_once": 0, 
+   "translatable": 0, 
    "unique": 0
   }, 
   {
@@ -623,6 +677,7 @@
    "reqd": 0, 
    "search_index": 0, 
    "set_only_once": 0, 
+   "translatable": 0, 
    "unique": 0
   }, 
   {
@@ -654,6 +709,7 @@
    "reqd": 0, 
    "search_index": 0, 
    "set_only_once": 1, 
+   "translatable": 0, 
    "unique": 0
   }, 
   {
@@ -685,6 +741,7 @@
    "reqd": 0, 
    "search_index": 0, 
    "set_only_once": 0, 
+   "translatable": 0, 
    "unique": 0
   }
  ], 
@@ -698,7 +755,7 @@
  "issingle": 0, 
  "istable": 0, 
  "max_attachments": 0, 
- "modified": "2017-12-28 11:26:20.262978", 
+ "modified": "2018-02-26 12:44:33.756124", 
  "modified_by": "Administrator", 
  "module": "Healthcare", 
  "name": "Patient Appointment", 
@@ -744,6 +801,26 @@
    "share": 1, 
    "submit": 0, 
    "write": 1
+  }, 
+  {
+   "amend": 0, 
+   "apply_user_permissions": 0, 
+   "cancel": 0, 
+   "create": 1, 
+   "delete": 1, 
+   "email": 1, 
+   "export": 1, 
+   "if_owner": 0, 
+   "import": 0, 
+   "permlevel": 0, 
+   "print": 1, 
+   "read": 1, 
+   "report": 1, 
+   "role": "Nursing User", 
+   "set_user_permissions": 0, 
+   "share": 1, 
+   "submit": 0, 
+   "write": 1
   }
  ], 
  "quick_entry": 0, 
diff --git a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py
index 90d4d0e..626774b 100755
--- a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py
+++ b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py
@@ -6,7 +6,7 @@
 import frappe
 from frappe.model.document import Document
 import json
-from frappe.utils import getdate, cint
+from frappe.utils import getdate
 from frappe import _
 import datetime
 from frappe.core.doctype.sms_settings.sms_settings import send_sms
@@ -40,13 +40,8 @@
 				frappe.msgprint(_("{0} has fee validity till {1}").format(appointment.patient, fee_validity.valid_till))
 		confirm_sms(self)
 
-	def save(self, *args, **kwargs):
-		# duration is the only changeable field in the document
-		if not self.is_new():
-			self.db_set('duration', cint(self.duration))
-		else:
-			super(PatientAppointment, self).save(*args, **kwargs)
-
+	def create_invoice(self):
+		return invoice_appointment(self)
 
 def appointment_cancel(appointment_id):
 	appointment = frappe.get_doc("Patient Appointment", appointment_id)
@@ -79,9 +74,9 @@
 	weekday = date.strftime("%A")
 
 	available_slots = []
-	physician_schedule_name = None
+	slot_details = []
 	physician_schedule = None
-	time_per_appointment = None
+
 	employee = None
 
 	physician_obj = frappe.get_doc("Physician", physician)
@@ -112,43 +107,56 @@
 				frappe.throw(_("Dr {0} on Leave on {1}").format(physician, date))
 
 	# get physicians schedule
-	physician_schedule_name = frappe.db.get_value("Physician", physician, "physician_schedule")
-	if physician_schedule_name:
-		physician_schedule = frappe.get_doc("Physician Schedule", physician_schedule_name)
-		time_per_appointment = frappe.db.get_value("Physician", physician, "time_per_appointment")
+	if physician_obj.physician_schedules:
+		for schedule in physician_obj.physician_schedules:
+			if schedule.schedule:
+				physician_schedule = frappe.get_doc("Physician Schedule", schedule.schedule)
+			else:
+				frappe.throw(_("Dr {0} does not have a Physician Schedule. Add it in Physician master".format(physician)))
+
+			if physician_schedule:
+				available_slots = []
+				for t in physician_schedule.time_slots:
+					if weekday == t.day:
+						available_slots.append(t)
+
+				if available_slots:
+					appointments = []
+				 	if schedule.service_unit:
+						slot_name  = schedule.schedule+" - "+schedule.service_unit
+						allow_overlap = frappe.get_value('Patient Service Unit', schedule.service_unit, 'overlap_appointments')
+						if allow_overlap:
+							# fetch all appointments to physician by service unit
+							appointments = frappe.get_all(
+								"Patient Appointment",
+								filters={"physician": physician, "service_unit": schedule.service_unit, "appointment_date": date, "status": ["not in",["Cancelled"]]},
+								fields=["name", "appointment_time", "duration", "status"])
+						else:
+							# fetch all appointments to service unit
+							appointments = frappe.get_all(
+								"Patient Appointment",
+								filters={"service_unit": schedule.service_unit, "appointment_date": date, "status": ["not in",["Cancelled"]]},
+								fields=["name", "appointment_time", "duration", "status"])
+					else:
+						slot_name = schedule.schedule
+						# fetch all appointments to physician without service unit
+						appointments = frappe.get_all(
+							"Patient Appointment",
+							filters={"physician": physician, "service_unit": '', "appointment_date": date, "status": ["not in",["Cancelled"]]},
+							fields=["name", "appointment_time", "duration", "status"])
+
+					slot_details.append({"slot_name":slot_name, "service_unit":schedule.service_unit,
+						"avail_slot":available_slots, 'appointments': appointments})
+
 	else:
 		frappe.throw(_("Dr {0} does not have a Physician Schedule. Add it in Physician master".format(physician)))
 
-	if physician_schedule:
-		for t in physician_schedule.time_slots:
-			if weekday == t.day:
-				available_slots.append(t)
-
-	# `time_per_appointment` should never be None since validation in `Patient` is supposed to prevent
-	# that. However, it isn't impossible so we'll prepare for that.
-	if not time_per_appointment:
-		frappe.throw(_('"Time Per Appointment" hasn"t been set for Dr {0}. Add it in Physician master.').format(physician))
-
-	# if physician not available return
-	if not available_slots:
+	if not available_slots and not slot_details:
 		# TODO: return available slots in nearby dates
 		frappe.throw(_("Physician not available on {0}").format(weekday))
 
-	# if physician on leave return
-
-	# if holiday return
-	# if is_holiday(weekday):
-
-	# get appointments on that day for physician
-	appointments = frappe.get_all(
-		"Patient Appointment",
-		filters={"physician": physician, "appointment_date": date},
-		fields=["name", "appointment_time", "duration", "status"])
-
 	return {
-		"available_slots": available_slots,
-		"appointments": appointments,
-		"time_per_appointment": time_per_appointment
+		"slot_details": slot_details
 	}
 
 
@@ -182,25 +190,25 @@
 
 
 @frappe.whitelist()
-def create_invoice(company, physician, patient, appointment_id, appointment_date):
-	if not appointment_id:
+def invoice_appointment(appointment_doc):
+	if not appointment_doc.name:
 		return False
 	sales_invoice = frappe.new_doc("Sales Invoice")
-	sales_invoice.customer = frappe.get_value("Patient", patient, "customer")
-	sales_invoice.appointment = appointment_id
+	sales_invoice.customer = frappe.get_value("Patient", appointment_doc.patient, "customer")
+	sales_invoice.appointment = appointment_doc.name
 	sales_invoice.due_date = getdate()
 	sales_invoice.is_pos = '0'
-	sales_invoice.debit_to = get_receivable_account(company)
+	sales_invoice.debit_to = get_receivable_account(appointment_doc.company)
 
-	fee_validity = get_fee_validity(physician, patient, appointment_date)
-	create_invoice_items(appointment_id, physician, company, sales_invoice)
+	fee_validity = get_fee_validity(appointment_doc.physician, appointment_doc.patient, appointment_doc.appointment_date)
+	create_invoice_items(appointment_doc.physician, appointment_doc.company, sales_invoice)
 
 	sales_invoice.save(ignore_permissions=True)
-	frappe.db.sql("""update `tabPatient Appointment` set sales_invoice=%s where name=%s""", (sales_invoice.name, appointment_id))
+	frappe.db.sql("""update `tabPatient Appointment` set sales_invoice=%s where name=%s""", (sales_invoice.name, appointment_doc.name))
 	frappe.db.set_value("Fee Validity", fee_validity.name, "ref_invoice", sales_invoice.name)
 	consultation = frappe.db.exists({
 			"doctype": "Consultation",
-			"appointment": appointment_id})
+			"appointment": appointment_doc.name})
 	if consultation:
 		frappe.db.set_value("Consultation", consultation[0][0], "invoice", sales_invoice.name)
 	return sales_invoice.name
@@ -247,7 +255,7 @@
 	return fee_validity
 
 
-def create_invoice_items(appointment_id, physician, company, invoice):
+def create_invoice_items(physician, company, invoice):
 	item_line = invoice.append("items")
 	item_line.item_name = "Consulting Charges"
 	item_line.description = "Consulting Charges:  " + physician
diff --git a/erpnext/healthcare/doctype/patient_service_unit/__init__.py b/erpnext/healthcare/doctype/patient_service_unit/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/healthcare/doctype/patient_service_unit/__init__.py
diff --git a/erpnext/healthcare/doctype/patient_service_unit/patient_service_unit.js b/erpnext/healthcare/doctype/patient_service_unit/patient_service_unit.js
new file mode 100644
index 0000000..197b4e5
--- /dev/null
+++ b/erpnext/healthcare/doctype/patient_service_unit/patient_service_unit.js
@@ -0,0 +1,15 @@
+// Copyright (c) 2017, earthians and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('Patient Service Unit', {
+});
+
+// get query select patient service unit
+cur_frm.fields_dict['parent_patient_service_unit'].get_query = function(doc) {
+	return{
+		filters:[
+			['Patient Service Unit', 'is_group', '=', 1],
+			['Patient Service Unit', 'name', '!=', doc.patient_service_unit_name]
+		]
+	};
+};
diff --git a/erpnext/healthcare/doctype/patient_service_unit/patient_service_unit.json b/erpnext/healthcare/doctype/patient_service_unit/patient_service_unit.json
new file mode 100644
index 0000000..0ca1a53
--- /dev/null
+++ b/erpnext/healthcare/doctype/patient_service_unit/patient_service_unit.json
@@ -0,0 +1,358 @@
+{
+ "allow_copy": 0, 
+ "allow_guest_to_view": 0, 
+ "allow_import": 0, 
+ "allow_rename": 1, 
+ "autoname": "field:patient_service_unit_name", 
+ "beta": 1, 
+ "creation": "2016-09-21 13:48:14.731437", 
+ "custom": 0, 
+ "description": "Patinet Service Unit", 
+ "docstatus": 0, 
+ "doctype": "DocType", 
+ "document_type": "Setup", 
+ "editable_grid": 1, 
+ "fields": [
+  {
+   "allow_bulk_edit": 0, 
+   "allow_on_submit": 0, 
+   "bold": 0, 
+   "collapsible": 0, 
+   "columns": 0, 
+   "fieldname": "patient_service_unit_name", 
+   "fieldtype": "Data", 
+   "hidden": 0, 
+   "ignore_user_permissions": 0, 
+   "ignore_xss_filter": 0, 
+   "in_filter": 0, 
+   "in_global_search": 1, 
+   "in_list_view": 1, 
+   "in_standard_filter": 0, 
+   "label": "Service Unit", 
+   "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": 1, 
+   "search_index": 0, 
+   "set_only_once": 0, 
+   "translatable": 0, 
+   "unique": 0
+  }, 
+  {
+   "allow_bulk_edit": 0, 
+   "allow_on_submit": 0, 
+   "bold": 1, 
+   "collapsible": 0, 
+   "columns": 0, 
+   "fieldname": "parent_patient_service_unit", 
+   "fieldtype": "Link", 
+   "hidden": 0, 
+   "ignore_user_permissions": 1, 
+   "ignore_xss_filter": 0, 
+   "in_filter": 0, 
+   "in_global_search": 0, 
+   "in_list_view": 1, 
+   "in_standard_filter": 0, 
+   "label": "Parent Service Unit", 
+   "length": 0, 
+   "no_copy": 0, 
+   "options": "Patient Service Unit", 
+   "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": 1, 
+   "collapsible": 0, 
+   "columns": 0, 
+   "fieldname": "is_group", 
+   "fieldtype": "Check", 
+   "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": "Is Group", 
+   "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": 1, 
+   "collapsible": 0, 
+   "columns": 0, 
+   "fieldname": "overlap_appointments", 
+   "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": "Allow Overlap", 
+   "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": 1, 
+   "ignore_user_permissions": 1, 
+   "ignore_xss_filter": 0, 
+   "in_filter": 0, 
+   "in_global_search": 0, 
+   "in_list_view": 0, 
+   "in_standard_filter": 0, 
+   "label": "Company", 
+   "length": 0, 
+   "no_copy": 1, 
+   "options": "Company", 
+   "permlevel": 0, 
+   "precision": "", 
+   "print_hide": 1, 
+   "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, 
+   "translatable": 0, 
+   "unique": 0
+  }, 
+  {
+   "allow_bulk_edit": 0, 
+   "allow_on_submit": 0, 
+   "bold": 0, 
+   "collapsible": 0, 
+   "columns": 0, 
+   "fieldname": "lft", 
+   "fieldtype": "Int", 
+   "hidden": 1, 
+   "ignore_user_permissions": 0, 
+   "ignore_xss_filter": 0, 
+   "in_filter": 0, 
+   "in_global_search": 0, 
+   "in_list_view": 0, 
+   "in_standard_filter": 0, 
+   "label": "lft", 
+   "length": 0, 
+   "no_copy": 1, 
+   "permlevel": 0, 
+   "precision": "", 
+   "print_hide": 1, 
+   "print_hide_if_no_value": 0, 
+   "read_only": 0, 
+   "remember_last_selected_value": 0, 
+   "report_hide": 0, 
+   "reqd": 0, 
+   "search_index": 1, 
+   "set_only_once": 0, 
+   "translatable": 0, 
+   "unique": 0
+  }, 
+  {
+   "allow_bulk_edit": 0, 
+   "allow_on_submit": 0, 
+   "bold": 0, 
+   "collapsible": 0, 
+   "columns": 0, 
+   "fieldname": "rgt", 
+   "fieldtype": "Int", 
+   "hidden": 1, 
+   "ignore_user_permissions": 0, 
+   "ignore_xss_filter": 0, 
+   "in_filter": 0, 
+   "in_global_search": 0, 
+   "in_list_view": 0, 
+   "in_standard_filter": 0, 
+   "label": "rgt", 
+   "length": 0, 
+   "no_copy": 1, 
+   "permlevel": 0, 
+   "precision": "", 
+   "print_hide": 1, 
+   "print_hide_if_no_value": 0, 
+   "read_only": 0, 
+   "remember_last_selected_value": 0, 
+   "report_hide": 0, 
+   "reqd": 0, 
+   "search_index": 1, 
+   "set_only_once": 0, 
+   "translatable": 0, 
+   "unique": 0
+  }, 
+  {
+   "allow_bulk_edit": 0, 
+   "allow_on_submit": 0, 
+   "bold": 0, 
+   "collapsible": 0, 
+   "columns": 0, 
+   "fieldname": "old_parent", 
+   "fieldtype": "Link", 
+   "hidden": 1, 
+   "ignore_user_permissions": 1, 
+   "ignore_xss_filter": 0, 
+   "in_filter": 0, 
+   "in_global_search": 0, 
+   "in_list_view": 0, 
+   "in_standard_filter": 0, 
+   "label": "Old Parent", 
+   "length": 0, 
+   "no_copy": 1, 
+   "options": "Patient Service Unit", 
+   "permlevel": 0, 
+   "precision": "", 
+   "print_hide": 1, 
+   "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, 
+   "translatable": 0, 
+   "unique": 0
+  }
+ ], 
+ "has_web_view": 0, 
+ "hide_heading": 0, 
+ "hide_toolbar": 0, 
+ "idx": 0, 
+ "image_view": 0, 
+ "in_create": 0, 
+ "is_submittable": 0, 
+ "issingle": 0, 
+ "istable": 0, 
+ "max_attachments": 0, 
+ "modified": "2018-03-07 13:25:51.163029", 
+ "modified_by": "Administrator", 
+ "module": "Healthcare", 
+ "name": "Patient Service Unit", 
+ "name_case": "", 
+ "owner": "Administrator", 
+ "permissions": [
+  {
+   "amend": 0, 
+   "apply_user_permissions": 1, 
+   "cancel": 0, 
+   "create": 0, 
+   "delete": 0, 
+   "email": 1, 
+   "export": 1, 
+   "if_owner": 0, 
+   "import": 0, 
+   "permlevel": 0, 
+   "print": 1, 
+   "read": 1, 
+   "report": 1, 
+   "role": "Nursing User", 
+   "set_user_permissions": 0, 
+   "share": 1, 
+   "submit": 0, 
+   "user_permission_doctypes": "[\"Service Unit\"]", 
+   "write": 0
+  }, 
+  {
+   "amend": 0, 
+   "apply_user_permissions": 0, 
+   "cancel": 0, 
+   "create": 1, 
+   "delete": 1, 
+   "email": 1, 
+   "export": 1, 
+   "if_owner": 0, 
+   "import": 0, 
+   "permlevel": 0, 
+   "print": 1, 
+   "read": 1, 
+   "report": 1, 
+   "role": "Medical Administrator", 
+   "set_user_permissions": 0, 
+   "share": 1, 
+   "submit": 0, 
+   "write": 1
+  }, 
+  {
+   "amend": 0, 
+   "apply_user_permissions": 0, 
+   "cancel": 0, 
+   "create": 1, 
+   "delete": 0, 
+   "email": 1, 
+   "export": 1, 
+   "if_owner": 0, 
+   "import": 0, 
+   "permlevel": 0, 
+   "print": 1, 
+   "read": 1, 
+   "report": 1, 
+   "role": "Physician", 
+   "set_user_permissions": 0, 
+   "share": 1, 
+   "submit": 0, 
+   "write": 1
+  }
+ ], 
+ "quick_entry": 1, 
+ "read_only": 0, 
+ "read_only_onload": 0, 
+ "restrict_to_domain": "Healthcare", 
+ "search_fields": "patient_service_unit_name", 
+ "show_name_in_global_search": 0, 
+ "sort_field": "modified", 
+ "sort_order": "DESC", 
+ "title_field": "patient_service_unit_name", 
+ "track_changes": 1, 
+ "track_seen": 0
+}
\ No newline at end of file
diff --git a/erpnext/healthcare/doctype/patient_service_unit/patient_service_unit.py b/erpnext/healthcare/doctype/patient_service_unit/patient_service_unit.py
new file mode 100644
index 0000000..6c177d8
--- /dev/null
+++ b/erpnext/healthcare/doctype/patient_service_unit/patient_service_unit.py
@@ -0,0 +1,14 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2017, earthians and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+
+from frappe.utils.nestedset import NestedSet
+
+class PatientServiceUnit(NestedSet):
+	nsm_parent_field = 'parent_patient_service_unit'
+
+	def on_update(self):
+		super(PatientServiceUnit, self).on_update()
+		self.validate_one_root()
diff --git a/erpnext/healthcare/doctype/patient_service_unit/patient_service_unit_tree.js b/erpnext/healthcare/doctype/patient_service_unit/patient_service_unit_tree.js
new file mode 100644
index 0000000..0b03f2d
--- /dev/null
+++ b/erpnext/healthcare/doctype/patient_service_unit/patient_service_unit_tree.js
@@ -0,0 +1,3 @@
+frappe.treeview_settings["Patient Service Unit"] = {
+	ignore_fields:["parent_patient_service_unit"]
+};
diff --git a/erpnext/healthcare/doctype/patient_service_unit/test_patient_service_unit.js b/erpnext/healthcare/doctype/patient_service_unit/test_patient_service_unit.js
new file mode 100644
index 0000000..320388a
--- /dev/null
+++ b/erpnext/healthcare/doctype/patient_service_unit/test_patient_service_unit.js
@@ -0,0 +1,23 @@
+/* eslint-disable */
+// rename this file from _test_[name] to test_[name] to activate
+// and remove above this line
+
+QUnit.test("test: Patient Service Unit", function (assert) {
+	let done = assert.async();
+
+	// number of asserts
+	assert.expect(1);
+
+	frappe.run_serially([
+		// insert a new Patient Service Unit
+		() => frappe.tests.make('Patient Service Unit', [
+			// values to be set
+			{key: 'value'}
+		]),
+		() => {
+			assert.equal(cur_frm.doc.key, 'value');
+		},
+		() => done()
+	]);
+
+});
diff --git a/erpnext/healthcare/doctype/patient_service_unit/test_patient_service_unit.py b/erpnext/healthcare/doctype/patient_service_unit/test_patient_service_unit.py
new file mode 100644
index 0000000..ceb49fd
--- /dev/null
+++ b/erpnext/healthcare/doctype/patient_service_unit/test_patient_service_unit.py
@@ -0,0 +1,8 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2017, earthians and Contributors
+# See license.txt
+from __future__ import unicode_literals
+import unittest
+
+class TestPatientServiceUnit(unittest.TestCase):
+	pass
diff --git a/erpnext/healthcare/doctype/physician/physician.js b/erpnext/healthcare/doctype/physician/physician.js
index c224b5d..6ce0199 100755
--- a/erpnext/healthcare/doctype/physician/physician.js
+++ b/erpnext/healthcare/doctype/physician/physician.js
@@ -19,6 +19,13 @@
 		if(!frm.is_new()) {
 			frappe.contacts.render_address_and_contact(frm);
 		}
+		frm.set_query("service_unit", "physician_schedules", function(){
+			return {
+				filters: {
+					"is_group": false,
+				}
+			};
+		});
 	}
 });
 
diff --git a/erpnext/healthcare/doctype/physician/physician.json b/erpnext/healthcare/doctype/physician/physician.json
index 1d9794e..e29561e 100644
--- a/erpnext/healthcare/doctype/physician/physician.json
+++ b/erpnext/healthcare/doctype/physician/physician.json
@@ -438,42 +438,11 @@
   {
    "allow_bulk_edit": 0, 
    "allow_on_submit": 0, 
-   "bold": 1, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "physician_schedule", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 1, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Physician Schedule", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Physician Schedule", 
-   "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": "column_break_17", 
-   "fieldtype": "Column Break", 
+   "fieldname": "physician_schedules", 
+   "fieldtype": "Table", 
    "hidden": 0, 
    "ignore_user_permissions": 0, 
    "ignore_xss_filter": 0, 
@@ -481,39 +450,10 @@
    "in_global_search": 0, 
    "in_list_view": 0, 
    "in_standard_filter": 0, 
+   "label": "Physician Schedules", 
    "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, 
-   "description": "In minutes", 
-   "fieldname": "time_per_appointment", 
-   "fieldtype": "Data", 
-   "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": "Time per Appointment", 
-   "length": 0, 
-   "no_copy": 0, 
+   "options": "Physician Service Unit Schedule", 
    "permlevel": 0, 
    "precision": "", 
    "print_hide": 0, 
@@ -811,7 +751,7 @@
  "istable": 0, 
  "max_attachments": 0, 
  "modified": "2018-01-19 15:25:43.166877", 
- "modified_by": "jams@hcf.com", 
+ "modified_by": "Administrator", 
  "module": "Healthcare", 
  "name": "Physician", 
  "name_case": "", 
diff --git a/erpnext/healthcare/doctype/physician/physician.py b/erpnext/healthcare/doctype/physician/physician.py
index 4d035d3..eb03083 100644
--- a/erpnext/healthcare/doctype/physician/physician.py
+++ b/erpnext/healthcare/doctype/physician/physician.py
@@ -20,7 +20,6 @@
 			[cstr(self.get(f)).strip() for f in ["first_name","middle_name","last_name"]]))
 
 	def validate(self):
-		self.validate_schedule_and_time()
 		validate_party_accounts(self)
 
 		if self.user_id:
@@ -37,15 +36,6 @@
 				frappe.permissions.remove_user_permission(
 					"Physician", self.name, existing_user_id)
 
-	def validate_schedule_and_time(self):
-		if (self.physician_schedule or self.time_per_appointment) and \
-				not (self.physician_schedule and self.time_per_appointment):
-			frappe.msgprint(
-				_('Both "Physician Schedule" and Time Per Appointment" must be set for Dr {0}').format(
-					self.first_name),
-				title='Error', raise_exception=1, indicator='red'
-			)
-
 	def on_update(self):
 		if self.user_id:
 			frappe.permissions.add_user_permission("Physician", self.name, self.user_id)
diff --git a/erpnext/healthcare/doctype/physician/test_physician.py b/erpnext/healthcare/doctype/physician/test_physician.py
index 4bd497a..2fbf574 100644
--- a/erpnext/healthcare/doctype/physician/test_physician.py
+++ b/erpnext/healthcare/doctype/physician/test_physician.py
@@ -12,23 +12,6 @@
 	def tearDown(self):
 		frappe.delete_doc_if_exists('Physician', '_Testdoctor2', force=1)
 
-	def test_schedule_and_time(self):
-		physician = frappe.new_doc('Physician')
-		physician.first_name = '_Testdoctor2'
-		physician.physician_schedule = '_Testdoctor2 Schedule'
-
-		self.assertRaises(frappe.ValidationError, physician.insert)
-
-		physician.physician_schedule = ''
-		physician.time_per_appointment = 15
-
-		self.assertRaises(frappe.ValidationError, physician.insert)
-
-		physician.physician_schedule = '_Testdoctor2 Schedule'
-		physician.time_per_appointment = 15
-
-		physician.insert()
-
 	def test_new_physician_without_schedule(self):
 		physician = frappe.new_doc('Physician')
 		physician.first_name = '_Testdoctor2'
diff --git a/erpnext/healthcare/doctype/physician_service_unit_schedule/__init__.py b/erpnext/healthcare/doctype/physician_service_unit_schedule/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/healthcare/doctype/physician_service_unit_schedule/__init__.py
diff --git a/erpnext/healthcare/doctype/physician_service_unit_schedule/physician_service_unit_schedule.json b/erpnext/healthcare/doctype/physician_service_unit_schedule/physician_service_unit_schedule.json
new file mode 100644
index 0000000..69fe7b3
--- /dev/null
+++ b/erpnext/healthcare/doctype/physician_service_unit_schedule/physician_service_unit_schedule.json
@@ -0,0 +1,103 @@
+{
+ "allow_copy": 0, 
+ "allow_guest_to_view": 0, 
+ "allow_import": 0, 
+ "allow_rename": 0, 
+ "beta": 1, 
+ "creation": "2017-11-16 12:19:17.163786", 
+ "custom": 0, 
+ "docstatus": 0, 
+ "doctype": "DocType", 
+ "document_type": "", 
+ "editable_grid": 1, 
+ "engine": "InnoDB", 
+ "fields": [
+  {
+   "allow_bulk_edit": 0, 
+   "allow_on_submit": 0, 
+   "bold": 0, 
+   "collapsible": 0, 
+   "columns": 0, 
+   "fieldname": "schedule", 
+   "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": "Schedule", 
+   "length": 0, 
+   "no_copy": 0, 
+   "options": "Physician Schedule", 
+   "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, 
+   "unique": 0
+  }, 
+  {
+   "allow_bulk_edit": 0, 
+   "allow_on_submit": 0, 
+   "bold": 0, 
+   "collapsible": 0, 
+   "columns": 0, 
+   "fieldname": "service_unit", 
+   "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": "Service Unit", 
+   "length": 0, 
+   "no_copy": 0, 
+   "options": "Patient Service Unit", 
+   "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
+  }
+ ], 
+ "has_web_view": 0, 
+ "hide_heading": 0, 
+ "hide_toolbar": 0, 
+ "idx": 0, 
+ "image_view": 0, 
+ "in_create": 0, 
+ "is_submittable": 0, 
+ "issingle": 0, 
+ "istable": 1, 
+ "max_attachments": 0, 
+ "modified": "2017-12-27 10:57:42.301295", 
+ "modified_by": "Administrator", 
+ "module": "Healthcare", 
+ "name": "Physician Service Unit Schedule", 
+ "name_case": "", 
+ "owner": "Administrator", 
+ "permissions": [], 
+ "quick_entry": 1, 
+ "read_only": 0, 
+ "read_only_onload": 0, 
+ "show_name_in_global_search": 0, 
+ "sort_field": "modified", 
+ "sort_order": "DESC", 
+ "track_changes": 1, 
+ "track_seen": 0
+}
\ No newline at end of file
diff --git a/erpnext/healthcare/doctype/physician_service_unit_schedule/physician_service_unit_schedule.py b/erpnext/healthcare/doctype/physician_service_unit_schedule/physician_service_unit_schedule.py
new file mode 100644
index 0000000..7aaec4d
--- /dev/null
+++ b/erpnext/healthcare/doctype/physician_service_unit_schedule/physician_service_unit_schedule.py
@@ -0,0 +1,9 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+from frappe.model.document import Document
+
+class PhysicianServiceUnitSchedule(Document):
+	pass
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index c2c565d..e049a81 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -504,6 +504,7 @@
 erpnext.patches.v10_0.migrate_daily_work_summary_settings_to_daily_work_summary_group
 erpnext.patches.v10_0.add_default_cash_flow_mappers
 erpnext.patches.v11_0.make_quality_inspection_template
+erpnext.patches.v10_0.remove_and_copy_fields_in_physician
 erpnext.patches.v10_0.update_status_for_multiple_source_in_po
 erpnext.patches.v10_0.set_auto_created_serial_no_in_stock_entry
 erpnext.patches.v10_0.update_territory_and_customer_group
diff --git a/erpnext/patches/v10_0/remove_and_copy_fields_in_physician.py b/erpnext/patches/v10_0/remove_and_copy_fields_in_physician.py
new file mode 100644
index 0000000..bf28644
--- /dev/null
+++ b/erpnext/patches/v10_0/remove_and_copy_fields_in_physician.py
@@ -0,0 +1,12 @@
+import frappe
+
+def execute():
+    if frappe.db.exists("DocType", "Physician"):
+        frappe.reload_doc("healthcare", "doctype", "physician")
+        frappe.reload_doc("healthcare", "doctype", "physician_service_unit_schedule")
+        if frappe.db.has_column('Physician', 'physician_schedule'):
+            for doc in frappe.get_all('Physician'):
+                _doc = frappe.get_doc('Physician', doc.name)
+                if _doc.physician_schedule:
+                    _doc.append('physician_schedules', {'schedule': _doc.physician_schedule})
+                    _doc.save()