Merge branch 'develop' into service-units-in-appointment
diff --git a/erpnext/controllers/queries.py b/erpnext/controllers/queries.py
index e3aac9a..81f0ad3 100644
--- a/erpnext/controllers/queries.py
+++ b/erpnext/controllers/queries.py
@@ -657,6 +657,34 @@
@frappe.whitelist()
@frappe.validate_and_sanitize_search_inputs
+def get_healthcare_service_units(doctype, txt, searchfield, start, page_len, filters):
+ query = """
+ select name
+ from `tabHealthcare Service Unit`
+ where
+ is_group = 0
+ and company = {company}
+ and name like {txt}""".format(
+ company = frappe.db.escape(filters.get('company')), txt = frappe.db.escape('%{0}%'.format(txt)))
+
+ if filters and filters.get('inpatient_record'):
+ from erpnext.healthcare.doctype.inpatient_medication_entry.inpatient_medication_entry import get_current_healthcare_service_unit
+ service_unit = get_current_healthcare_service_unit(filters.get('inpatient_record'))
+
+ # if the patient is admitted, then appointments should be allowed against the admission service unit,
+ # inspite of it being an Inpatient Occupancy service unit
+ if service_unit:
+ query += " and (allow_appointments = 1 or name = {service_unit})".format(service_unit = frappe.db.escape(service_unit))
+ else:
+ query += " and allow_appointments = 1"
+ else:
+ query += " and allow_appointments = 1"
+
+ return frappe.db.sql(query, filters)
+
+
+@frappe.whitelist()
+@frappe.validate_and_sanitize_search_inputs
def get_tax_template(doctype, txt, searchfield, start, page_len, filters):
item_doc = frappe.get_cached_doc('Item', filters.get('item_code'))
diff --git a/erpnext/healthcare/doctype/inpatient_medication_entry/inpatient_medication_entry.py b/erpnext/healthcare/doctype/inpatient_medication_entry/inpatient_medication_entry.py
index bba5213..e731908 100644
--- a/erpnext/healthcare/doctype/inpatient_medication_entry/inpatient_medication_entry.py
+++ b/erpnext/healthcare/doctype/inpatient_medication_entry/inpatient_medication_entry.py
@@ -264,7 +264,7 @@
def get_current_healthcare_service_unit(inpatient_record):
ip_record = frappe.get_doc('Inpatient Record', inpatient_record)
- if ip_record.inpatient_occupancies:
+ if ip_record.status in ['Admitted', 'Discharge Scheduled'] and ip_record.inpatient_occupancies:
return ip_record.inpatient_occupancies[-1].service_unit
return
diff --git a/erpnext/healthcare/doctype/inpatient_record/test_inpatient_record.py b/erpnext/healthcare/doctype/inpatient_record/test_inpatient_record.py
index 10990d4..8a918b0 100644
--- a/erpnext/healthcare/doctype/inpatient_record/test_inpatient_record.py
+++ b/erpnext/healthcare/doctype/inpatient_record/test_inpatient_record.py
@@ -142,11 +142,15 @@
return inpatient_record
-def get_healthcare_service_unit():
- service_unit = get_random("Healthcare Service Unit", filters={"inpatient_occupancy": 1})
+def get_healthcare_service_unit(unit_name=None):
+ if not unit_name:
+ service_unit = get_random("Healthcare Service Unit", filters={"inpatient_occupancy": 1})
+ else:
+ service_unit = frappe.db.exists("Healthcare Service Unit", {"healthcare_service_unit_name": unit_name})
+
if not service_unit:
service_unit = frappe.new_doc("Healthcare Service Unit")
- service_unit.healthcare_service_unit_name = "Test Service Unit Ip Occupancy"
+ service_unit.healthcare_service_unit_name = unit_name or "Test Service Unit Ip Occupancy"
service_unit.company = "_Test Company"
service_unit.service_unit_type = get_service_unit_type()
service_unit.inpatient_occupancy = 1
diff --git a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js
index 79e1775..3d5073b 100644
--- a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js
+++ b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js
@@ -31,12 +31,12 @@
};
});
- frm.set_query('service_unit', function(){
+ frm.set_query('service_unit', function() {
return {
+ query: 'erpnext.controllers.queries.get_healthcare_service_units',
filters: {
- 'is_group': false,
- 'allow_appointments': true,
- 'company': frm.doc.company
+ company: frm.doc.company,
+ inpatient_record: frm.doc.inpatient_record
}
};
});
diff --git a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py
index dc820cb..b05c673 100755
--- a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py
+++ b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py
@@ -18,6 +18,7 @@
class PatientAppointment(Document):
def validate(self):
self.validate_overlaps()
+ self.validate_service_unit()
self.set_appointment_datetime()
self.validate_customer_created()
self.set_status()
@@ -68,6 +69,19 @@
overlaps[0][1], overlaps[0][2], overlaps[0][3], overlaps[0][4])
frappe.throw(overlapping_details, title=_('Appointments Overlapping'))
+ def validate_service_unit(self):
+ if self.inpatient_record and self.service_unit:
+ from erpnext.healthcare.doctype.inpatient_medication_entry.inpatient_medication_entry import get_current_healthcare_service_unit
+
+ is_inpatient_occupancy_unit = frappe.db.get_value('Healthcare Service Unit', self.service_unit,
+ 'inpatient_occupancy')
+ service_unit = get_current_healthcare_service_unit(self.inpatient_record)
+ if is_inpatient_occupancy_unit and service_unit != self.service_unit:
+ msg = _('Patient {0} is not admitted in the service unit {1}').format(frappe.bold(self.patient), frappe.bold(self.service_unit)) + '<br>'
+ msg += _('Appointment for service units with Inpatient Occupancy can only be created against the unit where patient has been admitted.')
+ frappe.throw(msg, title=_('Invalid Healthcare Service Unit'))
+
+
def set_appointment_datetime(self):
self.appointment_datetime = "%s %s" % (self.appointment_date, self.appointment_time or "00:00:00")
diff --git a/erpnext/healthcare/doctype/patient_appointment/test_patient_appointment.py b/erpnext/healthcare/doctype/patient_appointment/test_patient_appointment.py
index b681ed1..6886f31 100644
--- a/erpnext/healthcare/doctype/patient_appointment/test_patient_appointment.py
+++ b/erpnext/healthcare/doctype/patient_appointment/test_patient_appointment.py
@@ -5,7 +5,7 @@
import unittest
import frappe
from erpnext.healthcare.doctype.patient_appointment.patient_appointment import update_status, make_encounter
-from frappe.utils import nowdate, add_days
+from frappe.utils import nowdate, add_days, now_datetime
from frappe.utils.make_random import get_random
from erpnext.accounts.doctype.pos_profile.test_pos_profile import make_pos_profile
@@ -78,6 +78,59 @@
sales_invoice_name = frappe.db.get_value('Sales Invoice Item', {'reference_dn': appointment.name}, 'parent')
self.assertEqual(frappe.db.get_value('Sales Invoice', sales_invoice_name, 'status'), 'Cancelled')
+ def test_appointment_booking_for_admission_service_unit(self):
+ from erpnext.healthcare.doctype.inpatient_record.inpatient_record import admit_patient, discharge_patient, schedule_discharge
+ from erpnext.healthcare.doctype.inpatient_record.test_inpatient_record import \
+ create_inpatient, get_healthcare_service_unit, mark_invoiced_inpatient_occupancy
+
+ frappe.db.sql("""delete from `tabInpatient Record`""")
+ patient, medical_department, practitioner = create_healthcare_docs()
+ patient = create_patient()
+ # Schedule Admission
+ ip_record = create_inpatient(patient)
+ ip_record.expected_length_of_stay = 0
+ ip_record.save(ignore_permissions = True)
+
+ # Admit
+ service_unit = get_healthcare_service_unit('Test Service Unit Ip Occupancy')
+ admit_patient(ip_record, service_unit, now_datetime())
+
+ appointment = create_appointment(patient, practitioner, nowdate(), service_unit=service_unit)
+ self.assertEqual(appointment.service_unit, service_unit)
+
+ # Discharge
+ schedule_discharge(frappe.as_json({'patient': patient}))
+ ip_record1 = frappe.get_doc("Inpatient Record", ip_record.name)
+ mark_invoiced_inpatient_occupancy(ip_record1)
+ discharge_patient(ip_record1)
+
+ def test_invalid_healthcare_service_unit_validation(self):
+ from erpnext.healthcare.doctype.inpatient_record.inpatient_record import admit_patient, discharge_patient, schedule_discharge
+ from erpnext.healthcare.doctype.inpatient_record.test_inpatient_record import \
+ create_inpatient, get_healthcare_service_unit, mark_invoiced_inpatient_occupancy
+
+ frappe.db.sql("""delete from `tabInpatient Record`""")
+ patient, medical_department, practitioner = create_healthcare_docs()
+ patient = create_patient()
+ # Schedule Admission
+ ip_record = create_inpatient(patient)
+ ip_record.expected_length_of_stay = 0
+ ip_record.save(ignore_permissions = True)
+
+ # Admit
+ service_unit = get_healthcare_service_unit('Test Service Unit Ip Occupancy')
+ admit_patient(ip_record, service_unit, now_datetime())
+
+ appointment_service_unit = get_healthcare_service_unit('Test Service Unit Ip Occupancy for Appointment')
+ appointment = create_appointment(patient, practitioner, nowdate(), service_unit=appointment_service_unit, save=0)
+ self.assertRaises(frappe.exceptions.ValidationError, appointment.save)
+
+ # Discharge
+ schedule_discharge(frappe.as_json({'patient': patient}))
+ ip_record1 = frappe.get_doc("Inpatient Record", ip_record.name)
+ mark_invoiced_inpatient_occupancy(ip_record1)
+ discharge_patient(ip_record1)
+
def create_healthcare_docs():
patient = create_patient()
@@ -125,7 +178,7 @@
encounter.submit()
return encounter
-def create_appointment(patient, practitioner, appointment_date, invoice=0, procedure_template=0):
+def create_appointment(patient, practitioner, appointment_date, invoice=0, procedure_template=0, service_unit=None, save=1):
item = create_healthcare_service_items()
frappe.db.set_value('Healthcare Settings', None, 'inpatient_visit_charge_item', item)
frappe.db.set_value('Healthcare Settings', None, 'op_consulting_charge_item', item)
@@ -136,12 +189,15 @@
appointment.appointment_date = appointment_date
appointment.company = '_Test Company'
appointment.duration = 15
+ if service_unit:
+ appointment.service_unit = service_unit
if invoice:
appointment.mode_of_payment = 'Cash'
appointment.paid_amount = 500
if procedure_template:
appointment.procedure_template = create_clinical_procedure_template().get('name')
- appointment.save(ignore_permissions=True)
+ if save:
+ appointment.save(ignore_permissions=True)
return appointment
def create_healthcare_service_items():