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()