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
This commit is contained in:
Jamsheer 2018-03-08 13:08:35 +05:30 committed by Nabin Hait
parent 11241044b2
commit 665b48773f
23 changed files with 878 additions and 283 deletions

View File

@ -86,6 +86,11 @@ def get_data():
"type": "doctype", "type": "doctype",
"name": "Medical Code", "name": "Medical Code",
"label": _("Medical Code"), "label": _("Medical Code"),
},
{
"type": "doctype",
"name": "Patient Service Unit",
"label": _("Patient Service Unit")
} }
] ]
}, },

View File

@ -767,7 +767,7 @@
"in_global_search": 0, "in_global_search": 0,
"in_list_view": 0, "in_list_view": 0,
"in_standard_filter": 0, "in_standard_filter": 0,
"label": "Drug Prescription", "label": "Medication",
"length": 0, "length": 0,
"no_copy": 0, "no_copy": 0,
"permlevel": 0, "permlevel": 0,
@ -797,7 +797,7 @@
"in_global_search": 0, "in_global_search": 0,
"in_list_view": 0, "in_list_view": 0,
"in_standard_filter": 0, "in_standard_filter": 0,
"label": "Drug Prescription", "label": "Medication",
"length": 0, "length": 0,
"no_copy": 0, "no_copy": 0,
"options": "Drug Prescription", "options": "Drug Prescription",
@ -945,7 +945,7 @@
"issingle": 0, "issingle": 0,
"istable": 0, "istable": 0,
"max_attachments": 0, "max_attachments": 0,
"modified": "2017-12-28 11:25:35.256848", "modified": "2018-02-19 11:35:13.826577",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Healthcare", "module": "Healthcare",
"name": "Consultation", "name": "Consultation",

View File

@ -4,7 +4,6 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe import frappe
from frappe import _
from frappe.model.document import Document from frappe.model.document import Document
from frappe.utils import getdate from frappe.utils import getdate
import json import json
@ -19,10 +18,6 @@ class Consultation(Document):
def after_insert(self): def after_insert(self):
insert_consultation_to_medical_record(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): def on_cancel(self):
if(self.appointment): if(self.appointment):
frappe.db.set_value("Patient Appointment", self.appointment, "status", "Open") frappe.db.set_value("Patient Appointment", self.appointment, "status", "Open")

View File

@ -5,7 +5,7 @@ from __future__ import unicode_literals
import frappe import frappe
import unittest 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.make_random import get_random
from frappe.utils import nowdate, add_days from frappe.utils import nowdate, add_days
# test_records = frappe.get_test_records('Fee Validity') # test_records = frappe.get_test_records('Fee Validity')
@ -14,6 +14,7 @@ class TestFeeValidity(unittest.TestCase):
def test_fee_validity(self): def test_fee_validity(self):
patient = get_random("Patient") patient = get_random("Patient")
physician = get_random("Physician") physician = get_random("Physician")
department = get_random("Medical Department")
if not patient: if not patient:
patient = frappe.new_doc("Patient") patient = frappe.new_doc("Patient")
@ -22,33 +23,43 @@ class TestFeeValidity(unittest.TestCase):
patient.save(ignore_permissions=True) patient.save(ignore_permissions=True)
patient = patient.name 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: if not physician:
physician = frappe.new_doc("Physician") physician = frappe.new_doc("Physician")
physician.first_name = "Amit Jain" physician.first_name = "Amit Jain"
physician.department = department
physician.save(ignore_permissions=True) physician.save(ignore_permissions=True)
physician = physician.name physician = physician.name
frappe.db.set_value("Healthcare Settings", None, "max_visit", 2) frappe.db.set_value("Healthcare Settings", None, "max_visit", 2)
frappe.db.set_value("Healthcare Settings", None, "valid_days", 7) 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") invoice = frappe.db.get_value("Patient Appointment", appointment.name, "sales_invoice")
self.assertEqual(invoice, None) self.assertEqual(invoice, None)
create_invoice(frappe.defaults.get_global_default("company"), physician, patient, appointment.name, appointment.appointment_date) invoice_appointment(appointment)
appointment = create_appointment(patient, physician, add_days(nowdate(), 4)) appointment = create_appointment(patient, physician, add_days(nowdate(), 4), department)
invoice = frappe.db.get_value("Patient Appointment", appointment.name, "sales_invoice") invoice = frappe.db.get_value("Patient Appointment", appointment.name, "sales_invoice")
self.assertTrue(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") invoice = frappe.db.get_value("Patient Appointment", appointment.name, "sales_invoice")
self.assertEqual(invoice, None) 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") invoice = frappe.db.get_value("Patient Appointment", appointment.name, "sales_invoice")
self.assertEqual(invoice, None) 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 = frappe.new_doc("Patient Appointment")
appointment.patient = patient appointment.patient = patient
appointment.physician = physician appointment.physician = physician
appointment.department = department
appointment.appointment_date = appointment_date appointment.appointment_date = appointment_date
appointment.save(ignore_permissions=True) appointment.save(ignore_permissions=True)
return appointment return appointment

View File

@ -15,6 +15,20 @@ frappe.ui.form.on('Patient Appointment', {
filters: {"disabled": 0} 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){ if(frm.doc.patient){
frm.add_custom_button(__('Medical Record'), function() { frm.add_custom_button(__('Medical Record'), function() {
frappe.route_options = {"patient": frm.doc.patient}; frappe.route_options = {"patient": frm.doc.patient};
@ -83,11 +97,10 @@ frappe.ui.form.on('Patient Appointment', {
date: appointment_date date: appointment_date
}, },
callback: (r) => { callback: (r) => {
// console.log(r);
var data = r.message; var data = r.message;
if(data.available_slots.length > 0) { if(data.slot_details.length > 0){
show_availability(data); show_availability(data);
} else { }else{
show_empty_state(); show_empty_state();
} }
} }
@ -108,48 +121,71 @@ frappe.ui.form.on('Patient Appointment', {
primary_action_label: __("Book"), primary_action_label: __("Book"),
primary_action: function() { primary_action: function() {
// book slot // book slot
frm.set_value('appointment_time', selected_slot); var btn_selected = $wrapper.find('button.btn-selected-slot');
frm.set_value('duration', data.time_per_appointment); 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(); d.hide();
frm.save(); frm.save();
frm.enable_save();
} }
}); });
var $wrapper = d.fields_dict.available_slots.$wrapper; var $wrapper = d.fields_dict.available_slots.$wrapper;
var selected_slot = null;
// disable dialog action initially // disable dialog action initially
d.get_primary_btn().attr('disabled', true); d.get_primary_btn().attr('disabled', true);
// make buttons for each slot var slot_details = data.slot_details;
var slot_html = data.available_slots.map(slot => { var slot_html = "";
return `<button class="btn btn-default" $.each(slot_details, function(i, slot_detail){
data-name=${slot.from_time} slot_html = slot_html + `<label>${slot_detail['slot_name']}</label>`;
style="margin: 0 10px 10px 0; width: 72px"> slot_html = slot_html + `<br/>` + slot_detail['avail_slot'].map(slot => {
${slot.from_time.substring(0, slot.from_time.length - 3)} let disabled = '';
</button>`; let start_str = slot.from_time;
}).join(""); 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 $wrapper
.css('margin-bottom', 0) .css('margin-bottom', 0)
.addClass('text-center') .addClass('text-center')
.html(slot_html); .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 // blue button when clicked
$wrapper.on('click', 'button', function() { $wrapper.on('click', 'button', function() {
var $btn = $(this); var $btn = $(this);
$wrapper.find('button').removeClass('btn-primary'); $wrapper.find('button').removeClass('btn-primary');
$wrapper.find('button').removeClass('btn-selected-slot');
$btn.addClass('btn-primary'); $btn.addClass('btn-primary');
selected_slot = $btn.attr('data-name'); $btn.addClass('btn-selected-slot');
// enable dialog action // enable dialog action
d.get_primary_btn().attr('disabled', null); d.get_primary_btn().attr('disabled', null);
}); });
@ -209,12 +245,9 @@ var btn_update_status = function(frm, status){
}; };
var btn_invoice_consultation = function(frm){ var btn_invoice_consultation = function(frm){
var doc = frm.doc;
frappe.call({ frappe.call({
method: doc: frm.doc,
"erpnext.healthcare.doctype.patient_appointment.patient_appointment.create_invoice", method:"create_invoice",
args: {company: doc.company, physician:doc.physician, patient: doc.patient,
appointment_id: doc.name, appointment_date:doc.appointment_date },
callback: function(data){ callback: function(data){
if(!data.exc){ if(!data.exc){
if(data.message){ if(data.message){

View File

@ -42,6 +42,39 @@
"reqd": 1, "reqd": 1,
"search_index": 1, "search_index": 1,
"set_only_once": 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 "unique": 0
}, },
{ {
@ -73,6 +106,7 @@
"reqd": 1, "reqd": 1,
"search_index": 1, "search_index": 1,
"set_only_once": 1, "set_only_once": 1,
"translatable": 0,
"unique": 0 "unique": 0
}, },
{ {
@ -103,6 +137,71 @@
"reqd": 1, "reqd": 1,
"search_index": 1, "search_index": 1,
"set_only_once": 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 "unique": 0
}, },
{ {
@ -134,6 +233,7 @@
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0,
"unique": 0 "unique": 0
}, },
{ {
@ -151,8 +251,42 @@
"in_global_search": 0, "in_global_search": 0,
"in_list_view": 0, "in_list_view": 0,
"in_standard_filter": 0, "in_standard_filter": 0,
"label": "",
"length": 0, "length": 0,
"no_copy": 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, "permlevel": 0,
"precision": "", "precision": "",
"print_hide": 0, "print_hide": 0,
@ -162,7 +296,8 @@
"report_hide": 0, "report_hide": 0,
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 1,
"translatable": 0,
"unique": 0 "unique": 0
}, },
{ {
@ -195,6 +330,7 @@
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0,
"unique": 0 "unique": 0
}, },
{ {
@ -226,6 +362,7 @@
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0,
"unique": 0 "unique": 0
}, },
{ {
@ -257,6 +394,7 @@
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0,
"unique": 0 "unique": 0
}, },
{ {
@ -271,7 +409,7 @@
"hidden": 0, "hidden": 0,
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
"ignore_xss_filter": 0, "ignore_xss_filter": 0,
"in_filter": 0, "in_filter": 1,
"in_global_search": 0, "in_global_search": 0,
"in_list_view": 1, "in_list_view": 1,
"in_standard_filter": 0, "in_standard_filter": 0,
@ -289,6 +427,7 @@
"reqd": 0, "reqd": 0,
"search_index": 1, "search_index": 1,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0,
"unique": 0 "unique": 0
}, },
{ {
@ -319,7 +458,8 @@
"report_hide": 0, "report_hide": 0,
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
"set_only_once": 1, "set_only_once": 0,
"translatable": 0,
"unique": 0 "unique": 0
}, },
{ {
@ -349,68 +489,8 @@
"report_hide": 0, "report_hide": 0,
"reqd": 0, "reqd": 0,
"search_index": 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, "set_only_once": 1,
"unique": 0 "translatable": 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,
"unique": 0 "unique": 0
}, },
{ {
@ -441,6 +521,7 @@
"reqd": 0, "reqd": 0,
"search_index": 1, "search_index": 1,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0,
"unique": 0 "unique": 0
}, },
{ {
@ -470,37 +551,7 @@
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"unique": 0 "translatable": 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,
"unique": 0 "unique": 0
}, },
{ {
@ -532,6 +583,7 @@
"reqd": 0, "reqd": 0,
"search_index": 1, "search_index": 1,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0,
"unique": 0 "unique": 0
}, },
{ {
@ -563,6 +615,7 @@
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0,
"unique": 0 "unique": 0
}, },
{ {
@ -593,6 +646,7 @@
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0,
"unique": 0 "unique": 0
}, },
{ {
@ -623,6 +677,7 @@
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0,
"unique": 0 "unique": 0
}, },
{ {
@ -654,6 +709,7 @@
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
"set_only_once": 1, "set_only_once": 1,
"translatable": 0,
"unique": 0 "unique": 0
}, },
{ {
@ -685,6 +741,7 @@
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0,
"unique": 0 "unique": 0
} }
], ],
@ -698,7 +755,7 @@
"issingle": 0, "issingle": 0,
"istable": 0, "istable": 0,
"max_attachments": 0, "max_attachments": 0,
"modified": "2017-12-28 11:26:20.262978", "modified": "2018-02-26 12:44:33.756124",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Healthcare", "module": "Healthcare",
"name": "Patient Appointment", "name": "Patient Appointment",
@ -744,6 +801,26 @@
"share": 1, "share": 1,
"submit": 0, "submit": 0,
"write": 1 "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, "quick_entry": 0,

View File

@ -6,7 +6,7 @@ from __future__ import unicode_literals
import frappe import frappe
from frappe.model.document import Document from frappe.model.document import Document
import json import json
from frappe.utils import getdate, cint from frappe.utils import getdate
from frappe import _ from frappe import _
import datetime import datetime
from frappe.core.doctype.sms_settings.sms_settings import send_sms from frappe.core.doctype.sms_settings.sms_settings import send_sms
@ -40,13 +40,8 @@ class PatientAppointment(Document):
frappe.msgprint(_("{0} has fee validity till {1}").format(appointment.patient, fee_validity.valid_till)) frappe.msgprint(_("{0} has fee validity till {1}").format(appointment.patient, fee_validity.valid_till))
confirm_sms(self) confirm_sms(self)
def save(self, *args, **kwargs): def create_invoice(self):
# duration is the only changeable field in the document return invoice_appointment(self)
if not self.is_new():
self.db_set('duration', cint(self.duration))
else:
super(PatientAppointment, self).save(*args, **kwargs)
def appointment_cancel(appointment_id): def appointment_cancel(appointment_id):
appointment = frappe.get_doc("Patient Appointment", appointment_id) appointment = frappe.get_doc("Patient Appointment", appointment_id)
@ -79,9 +74,9 @@ def get_availability_data(date, physician):
weekday = date.strftime("%A") weekday = date.strftime("%A")
available_slots = [] available_slots = []
physician_schedule_name = None slot_details = []
physician_schedule = None physician_schedule = None
time_per_appointment = None
employee = None employee = None
physician_obj = frappe.get_doc("Physician", physician) physician_obj = frappe.get_doc("Physician", physician)
@ -112,43 +107,56 @@ def get_availability_data(date, physician):
frappe.throw(_("Dr {0} on Leave on {1}").format(physician, date)) frappe.throw(_("Dr {0} on Leave on {1}").format(physician, date))
# get physicians schedule # get physicians schedule
physician_schedule_name = frappe.db.get_value("Physician", physician, "physician_schedule") if physician_obj.physician_schedules:
if physician_schedule_name: for schedule in physician_obj.physician_schedules:
physician_schedule = frappe.get_doc("Physician Schedule", physician_schedule_name) if schedule.schedule:
time_per_appointment = frappe.db.get_value("Physician", physician, "time_per_appointment") 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: else:
frappe.throw(_("Dr {0} does not have a Physician Schedule. Add it in Physician master".format(physician))) frappe.throw(_("Dr {0} does not have a Physician Schedule. Add it in Physician master".format(physician)))
if physician_schedule: if not available_slots and not slot_details:
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:
# TODO: return available slots in nearby dates # TODO: return available slots in nearby dates
frappe.throw(_("Physician not available on {0}").format(weekday)) 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 { return {
"available_slots": available_slots, "slot_details": slot_details
"appointments": appointments,
"time_per_appointment": time_per_appointment
} }
@ -182,25 +190,25 @@ def confirm_sms(doc):
@frappe.whitelist() @frappe.whitelist()
def create_invoice(company, physician, patient, appointment_id, appointment_date): def invoice_appointment(appointment_doc):
if not appointment_id: if not appointment_doc.name:
return False return False
sales_invoice = frappe.new_doc("Sales Invoice") sales_invoice = frappe.new_doc("Sales Invoice")
sales_invoice.customer = frappe.get_value("Patient", patient, "customer") sales_invoice.customer = frappe.get_value("Patient", appointment_doc.patient, "customer")
sales_invoice.appointment = appointment_id sales_invoice.appointment = appointment_doc.name
sales_invoice.due_date = getdate() sales_invoice.due_date = getdate()
sales_invoice.is_pos = '0' 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) fee_validity = get_fee_validity(appointment_doc.physician, appointment_doc.patient, appointment_doc.appointment_date)
create_invoice_items(appointment_id, physician, company, sales_invoice) create_invoice_items(appointment_doc.physician, appointment_doc.company, sales_invoice)
sales_invoice.save(ignore_permissions=True) 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) frappe.db.set_value("Fee Validity", fee_validity.name, "ref_invoice", sales_invoice.name)
consultation = frappe.db.exists({ consultation = frappe.db.exists({
"doctype": "Consultation", "doctype": "Consultation",
"appointment": appointment_id}) "appointment": appointment_doc.name})
if consultation: if consultation:
frappe.db.set_value("Consultation", consultation[0][0], "invoice", sales_invoice.name) frappe.db.set_value("Consultation", consultation[0][0], "invoice", sales_invoice.name)
return sales_invoice.name return sales_invoice.name
@ -247,7 +255,7 @@ def create_fee_validity(physician, patient, date):
return fee_validity 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 = invoice.append("items")
item_line.item_name = "Consulting Charges" item_line.item_name = "Consulting Charges"
item_line.description = "Consulting Charges: " + physician item_line.description = "Consulting Charges: " + physician

View File

@ -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]
]
};
};

View File

@ -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
}

View File

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

View File

@ -0,0 +1,3 @@
frappe.treeview_settings["Patient Service Unit"] = {
ignore_fields:["parent_patient_service_unit"]
};

View File

@ -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()
]);
});

View File

@ -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

View File

@ -19,6 +19,13 @@ frappe.ui.form.on('Physician', {
if(!frm.is_new()) { if(!frm.is_new()) {
frappe.contacts.render_address_and_contact(frm); frappe.contacts.render_address_and_contact(frm);
} }
frm.set_query("service_unit", "physician_schedules", function(){
return {
filters: {
"is_group": false,
}
};
});
} }
}); });

View File

@ -435,45 +435,14 @@
"set_only_once": 0, "set_only_once": 0,
"unique": 0 "unique": 0
}, },
{
"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_bulk_edit": 0,
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fieldname": "column_break_17", "fieldname": "physician_schedules",
"fieldtype": "Column Break", "fieldtype": "Table",
"hidden": 0, "hidden": 0,
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
"ignore_xss_filter": 0, "ignore_xss_filter": 0,
@ -481,39 +450,10 @@
"in_global_search": 0, "in_global_search": 0,
"in_list_view": 0, "in_list_view": 0,
"in_standard_filter": 0, "in_standard_filter": 0,
"label": "Physician Schedules",
"length": 0, "length": 0,
"no_copy": 0, "no_copy": 0,
"permlevel": 0, "options": "Physician Service Unit Schedule",
"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,
"permlevel": 0, "permlevel": 0,
"precision": "", "precision": "",
"print_hide": 0, "print_hide": 0,
@ -811,7 +751,7 @@
"istable": 0, "istable": 0,
"max_attachments": 0, "max_attachments": 0,
"modified": "2018-01-19 15:25:43.166877", "modified": "2018-01-19 15:25:43.166877",
"modified_by": "jams@hcf.com", "modified_by": "Administrator",
"module": "Healthcare", "module": "Healthcare",
"name": "Physician", "name": "Physician",
"name_case": "", "name_case": "",

View File

@ -20,7 +20,6 @@ class Physician(Document):
[cstr(self.get(f)).strip() for f in ["first_name","middle_name","last_name"]])) [cstr(self.get(f)).strip() for f in ["first_name","middle_name","last_name"]]))
def validate(self): def validate(self):
self.validate_schedule_and_time()
validate_party_accounts(self) validate_party_accounts(self)
if self.user_id: if self.user_id:
@ -37,15 +36,6 @@ class Physician(Document):
frappe.permissions.remove_user_permission( frappe.permissions.remove_user_permission(
"Physician", self.name, existing_user_id) "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): def on_update(self):
if self.user_id: if self.user_id:
frappe.permissions.add_user_permission("Physician", self.name, self.user_id) frappe.permissions.add_user_permission("Physician", self.name, self.user_id)

View File

@ -12,23 +12,6 @@ class TestPhysician(unittest.TestCase):
def tearDown(self): def tearDown(self):
frappe.delete_doc_if_exists('Physician', '_Testdoctor2', force=1) 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): def test_new_physician_without_schedule(self):
physician = frappe.new_doc('Physician') physician = frappe.new_doc('Physician')
physician.first_name = '_Testdoctor2' physician.first_name = '_Testdoctor2'

View File

@ -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
}

View File

@ -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

View File

@ -504,6 +504,7 @@ execute:frappe.delete_doc('DocType', 'Production Planning Tool', ignore_missing=
erpnext.patches.v10_0.migrate_daily_work_summary_settings_to_daily_work_summary_group 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.v10_0.add_default_cash_flow_mappers
erpnext.patches.v11_0.make_quality_inspection_template 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.update_status_for_multiple_source_in_po
erpnext.patches.v10_0.set_auto_created_serial_no_in_stock_entry erpnext.patches.v10_0.set_auto_created_serial_no_in_stock_entry
erpnext.patches.v10_0.update_territory_and_customer_group erpnext.patches.v10_0.update_territory_and_customer_group

View File

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