refactor: Healthcare Redesign Changes (#27236)

This commit is contained in:
Rucha Mahabal 2021-08-30 18:26:56 +05:30 committed by GitHub
parent 5fd04101d4
commit 1682402a9f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 825 additions and 651 deletions

View File

@ -12,15 +12,15 @@
"idx": 0, "idx": 0,
"is_public": 1, "is_public": 1,
"is_standard": 1, "is_standard": 1,
"last_synced_on": "2020-07-22 13:22:47.008622", "last_synced_on": "2021-01-30 21:03:30.086891",
"modified": "2020-07-22 13:36:48.114479", "modified": "2021-02-01 13:36:04.469863",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Healthcare", "module": "Healthcare",
"name": "Clinical Procedures", "name": "Clinical Procedures",
"number_of_groups": 0, "number_of_groups": 0,
"owner": "Administrator", "owner": "Administrator",
"timeseries": 0, "timeseries": 0,
"type": "Percentage", "type": "Bar",
"use_report_chart": 0, "use_report_chart": 0,
"y_axis": [] "y_axis": []
} }

View File

@ -12,15 +12,15 @@
"idx": 0, "idx": 0,
"is_public": 1, "is_public": 1,
"is_standard": 1, "is_standard": 1,
"last_synced_on": "2020-07-22 13:22:46.691764", "last_synced_on": "2021-02-01 13:36:38.787783",
"modified": "2020-07-22 13:40:17.215775", "modified": "2021-02-01 13:37:18.718275",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Healthcare", "module": "Healthcare",
"name": "Clinical Procedures Status", "name": "Clinical Procedures Status",
"number_of_groups": 0, "number_of_groups": 0,
"owner": "Administrator", "owner": "Administrator",
"timeseries": 0, "timeseries": 0,
"type": "Pie", "type": "Bar",
"use_report_chart": 0, "use_report_chart": 0,
"y_axis": [] "y_axis": []
} }

View File

@ -5,21 +5,22 @@
"docstatus": 0, "docstatus": 0,
"doctype": "Dashboard Chart", "doctype": "Dashboard Chart",
"document_type": "Patient Encounter Diagnosis", "document_type": "Patient Encounter Diagnosis",
"dynamic_filters_json": "",
"filters_json": "[]", "filters_json": "[]",
"group_by_based_on": "diagnosis", "group_by_based_on": "diagnosis",
"group_by_type": "Count", "group_by_type": "Count",
"idx": 0, "idx": 0,
"is_public": 1, "is_public": 1,
"is_standard": 1, "is_standard": 1,
"last_synced_on": "2020-07-22 13:22:47.895521", "last_synced_on": "2021-01-30 21:03:33.729487",
"modified": "2020-07-22 13:43:32.369481", "modified": "2021-02-01 13:34:57.385335",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Healthcare", "module": "Healthcare",
"name": "Diagnoses", "name": "Diagnoses",
"number_of_groups": 0, "number_of_groups": 0,
"owner": "Administrator", "owner": "Administrator",
"timeseries": 0, "timeseries": 0,
"type": "Percentage", "type": "Bar",
"use_report_chart": 0, "use_report_chart": 0,
"y_axis": [] "y_axis": []
} }

View File

@ -12,15 +12,15 @@
"idx": 0, "idx": 0,
"is_public": 1, "is_public": 1,
"is_standard": 1, "is_standard": 1,
"last_synced_on": "2020-07-22 13:22:47.344055", "last_synced_on": "2021-01-30 21:03:28.272914",
"modified": "2020-07-22 13:37:34.490129", "modified": "2021-02-01 13:36:08.391433",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Healthcare", "module": "Healthcare",
"name": "Lab Tests", "name": "Lab Tests",
"number_of_groups": 0, "number_of_groups": 0,
"owner": "Administrator", "owner": "Administrator",
"timeseries": 0, "timeseries": 0,
"type": "Percentage", "type": "Bar",
"use_report_chart": 0, "use_report_chart": 0,
"y_axis": [] "y_axis": []
} }

View File

@ -12,15 +12,15 @@
"idx": 0, "idx": 0,
"is_public": 1, "is_public": 1,
"is_standard": 1, "is_standard": 1,
"last_synced_on": "2020-07-22 13:22:47.296748", "last_synced_on": "2021-01-30 21:03:32.067473",
"modified": "2020-07-22 13:40:59.655129", "modified": "2021-02-01 13:35:30.953718",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Healthcare", "module": "Healthcare",
"name": "Symptoms", "name": "Symptoms",
"number_of_groups": 0, "number_of_groups": 0,
"owner": "Administrator", "owner": "Administrator",
"timeseries": 0, "timeseries": 0,
"type": "Percentage", "type": "Bar",
"use_report_chart": 0, "use_report_chart": 0,
"y_axis": [] "y_axis": []
} }

View File

@ -34,7 +34,7 @@ class LabTest(Document):
frappe.db.set_value('Lab Prescription', self.prescription, 'lab_test_created', 1) frappe.db.set_value('Lab Prescription', self.prescription, 'lab_test_created', 1)
if frappe.db.get_value('Lab Prescription', self.prescription, 'invoiced'): if frappe.db.get_value('Lab Prescription', self.prescription, 'invoiced'):
self.invoiced = True self.invoiced = True
if not self.lab_test_name and self.template: if self.template:
self.load_test_from_template() self.load_test_from_template()
self.reload() self.reload()
@ -50,7 +50,7 @@ class LabTest(Document):
item.secondary_uom_result = float(item.result_value) * float(item.conversion_factor) item.secondary_uom_result = float(item.result_value) * float(item.conversion_factor)
except: except:
item.secondary_uom_result = '' item.secondary_uom_result = ''
frappe.msgprint(_('Row #{0}: Result for Secondary UOM not calculated'.format(item.idx)), title = _('Warning')) frappe.msgprint(_('Row #{0}: Result for Secondary UOM not calculated').format(item.idx), title = _('Warning'))
def validate_result_values(self): def validate_result_values(self):
if self.normal_test_items: if self.normal_test_items:
@ -229,9 +229,9 @@ def create_sample_doc(template, patient, invoice, company = None):
sample_collection = frappe.get_doc('Sample Collection', sample_exists[0][0]) sample_collection = frappe.get_doc('Sample Collection', sample_exists[0][0])
quantity = int(sample_collection.sample_qty) + int(template.sample_qty) quantity = int(sample_collection.sample_qty) + int(template.sample_qty)
if template.sample_details: if template.sample_details:
sample_details = sample_collection.sample_details + '\n-\n' + _('Test: ') sample_details = sample_collection.sample_details + '\n-\n' + _('Test :')
sample_details += (template.get('lab_test_name') or template.get('template')) + '\n' sample_details += (template.get('lab_test_name') or template.get('template')) + '\n'
sample_details += _('Collection Details: ') + '\n\t' + template.sample_details sample_details += _('Collection Details:') + '\n\t' + template.sample_details
frappe.db.set_value('Sample Collection', sample_collection.name, 'sample_details', sample_details) frappe.db.set_value('Sample Collection', sample_collection.name, 'sample_details', sample_details)
frappe.db.set_value('Sample Collection', sample_collection.name, 'sample_qty', quantity) frappe.db.set_value('Sample Collection', sample_collection.name, 'sample_qty', quantity)

View File

@ -66,7 +66,7 @@ class TestPatientAppointment(unittest.TestCase):
medical_department = create_medical_department() medical_department = create_medical_department()
frappe.db.set_value('Healthcare Settings', None, 'enable_free_follow_ups', 0) frappe.db.set_value('Healthcare Settings', None, 'enable_free_follow_ups', 0)
frappe.db.set_value('Healthcare Settings', None, 'automate_appointment_invoicing', 1) frappe.db.set_value('Healthcare Settings', None, 'automate_appointment_invoicing', 1)
appointment_type = create_appointment_type() appointment_type = create_appointment_type({'medical_department': medical_department})
appointment = create_appointment(patient, practitioner, add_days(nowdate(), 2), appointment = create_appointment(patient, practitioner, add_days(nowdate(), 2),
invoice=1, appointment_type=appointment_type.name, department=medical_department) invoice=1, appointment_type=appointment_type.name, department=medical_department)
@ -91,9 +91,9 @@ class TestPatientAppointment(unittest.TestCase):
'op_consulting_charge': 300 'op_consulting_charge': 300
}] }]
appointment_type = create_appointment_type(args={ appointment_type = create_appointment_type(args={
'name': 'Generic Appointment Type charge', 'name': 'Generic Appointment Type charge',
'items': items 'items': items
}) })
appointment = create_appointment(patient, practitioner, add_days(nowdate(), 2), appointment = create_appointment(patient, practitioner, add_days(nowdate(), 2),
invoice=1, appointment_type=appointment_type.name) invoice=1, appointment_type=appointment_type.name)
@ -408,9 +408,9 @@ def create_appointment_type(args=None):
else: else:
item = create_healthcare_service_items() item = create_healthcare_service_items()
items = [{ items = [{
'medical_department': '_Test Medical Department', 'medical_department': args.get('medical_department') or '_Test Medical Department',
'op_consulting_charge_item': item, 'op_consulting_charge_item': item,
'op_consulting_charge': 200 'op_consulting_charge': 200
}] }]
return frappe.get_doc({ return frappe.get_doc({
'doctype': 'Appointment Type', 'doctype': 'Appointment Type',

View File

@ -18,7 +18,7 @@ class PatientHistorySettings(Document):
def validate_submittable_doctypes(self): def validate_submittable_doctypes(self):
for entry in self.custom_doctypes: for entry in self.custom_doctypes:
if not cint(frappe.db.get_value('DocType', entry.document_type, 'is_submittable')): if not cint(frappe.db.get_value('DocType', entry.document_type, 'is_submittable')):
msg = _('Row #{0}: Document Type {1} is not submittable. ').format( msg = _('Row #{0}: Document Type {1} is not submittable.').format(
entry.idx, frappe.bold(entry.document_type)) entry.idx, frappe.bold(entry.document_type))
msg += _('Patient Medical Record can only be created for submittable document types.') msg += _('Patient Medical Record can only be created for submittable document types.')
frappe.throw(msg) frappe.throw(msg)
@ -116,12 +116,12 @@ def set_subject_field(doc):
fieldname = entry.get('fieldname') fieldname = entry.get('fieldname')
if entry.get('fieldtype') == 'Table' and doc.get(fieldname): if entry.get('fieldtype') == 'Table' and doc.get(fieldname):
formatted_value = get_formatted_value_for_table_field(doc.get(fieldname), meta.get_field(fieldname)) formatted_value = get_formatted_value_for_table_field(doc.get(fieldname), meta.get_field(fieldname))
subject += frappe.bold(_(entry.get('label')) + ': ') + '<br>' + cstr(formatted_value) + '<br>' subject += frappe.bold(_(entry.get('label')) + ':') + '<br>' + cstr(formatted_value) + '<br>'
else: else:
if doc.get(fieldname): if doc.get(fieldname):
formatted_value = format_value(doc.get(fieldname), meta.get_field(fieldname), doc) formatted_value = format_value(doc.get(fieldname), meta.get_field(fieldname), doc)
subject += frappe.bold(_(entry.get('label')) + ': ') + cstr(formatted_value) + '<br>' subject += frappe.bold(_(entry.get('label')) + ':') + cstr(formatted_value) + '<br>'
return subject return subject

View File

@ -38,13 +38,12 @@ class TestPatientHistorySettings(unittest.TestCase):
# tests for medical record creation of standard doctypes in test_patient_medical_record.py # tests for medical record creation of standard doctypes in test_patient_medical_record.py
patient = create_patient() patient = create_patient()
doc = create_doc(patient) doc = create_doc(patient)
# check for medical record # check for medical record
medical_rec = frappe.db.exists("Patient Medical Record", {"status": "Open", "reference_name": doc.name}) medical_rec = frappe.db.exists("Patient Medical Record", {"status": "Open", "reference_name": doc.name})
self.assertTrue(medical_rec) self.assertTrue(medical_rec)
medical_rec = frappe.get_doc("Patient Medical Record", medical_rec) medical_rec = frappe.get_doc("Patient Medical Record", medical_rec)
expected_subject = "Date: {0}Rating: 3Feedback: Test Patient History Settings".format( expected_subject = "Date:{0}Rating:3Feedback:Test Patient History Settings".format(
frappe.utils.format_date(getdate())) frappe.utils.format_date(getdate()))
self.assertEqual(strip_html(medical_rec.subject), expected_subject) self.assertEqual(strip_html(medical_rec.subject), expected_subject)
self.assertEqual(medical_rec.patient, patient) self.assertEqual(medical_rec.patient, patient)

View File

@ -10,7 +10,7 @@
"documentation_url": "https://docs.erpnext.com/docs/user/manual/en/healthcare", "documentation_url": "https://docs.erpnext.com/docs/user/manual/en/healthcare",
"idx": 0, "idx": 0,
"is_complete": 0, "is_complete": 0,
"modified": "2020-07-08 14:06:19.512946", "modified": "2021-01-30 19:22:20.273766",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Healthcare", "module": "Healthcare",
"name": "Healthcare", "name": "Healthcare",

View File

@ -5,14 +5,14 @@
"doctype": "Onboarding Step", "doctype": "Onboarding Step",
"idx": 0, "idx": 0,
"is_complete": 0, "is_complete": 0,
"is_mandatory": 1,
"is_single": 0, "is_single": 0,
"is_skipped": 0, "is_skipped": 0,
"modified": "2020-05-26 23:16:31.965521", "modified": "2021-01-30 12:02:22.849260",
"modified_by": "Administrator", "modified_by": "Administrator",
"name": "Create Healthcare Practitioner", "name": "Create Healthcare Practitioner",
"owner": "Administrator", "owner": "Administrator",
"reference_document": "Healthcare Practitioner", "reference_document": "Healthcare Practitioner",
"show_form_tour": 0,
"show_full_form": 1, "show_full_form": 1,
"title": "Create Healthcare Practitioner", "title": "Create Healthcare Practitioner",
"validate_action": 1 "validate_action": 1

View File

@ -5,14 +5,14 @@
"doctype": "Onboarding Step", "doctype": "Onboarding Step",
"idx": 0, "idx": 0,
"is_complete": 0, "is_complete": 0,
"is_mandatory": 1,
"is_single": 0, "is_single": 0,
"is_skipped": 0, "is_skipped": 0,
"modified": "2020-05-19 12:26:24.023418", "modified": "2021-01-30 00:09:28.786428",
"modified_by": "Administrator", "modified_by": "ruchamahabal2@gmail.com",
"name": "Create Patient", "name": "Create Patient",
"owner": "Administrator", "owner": "Administrator",
"reference_document": "Patient", "reference_document": "Patient",
"show_form_tour": 0,
"show_full_form": 1, "show_full_form": 1,
"title": "Create Patient", "title": "Create Patient",
"validate_action": 1 "validate_action": 1

View File

@ -5,14 +5,14 @@
"doctype": "Onboarding Step", "doctype": "Onboarding Step",
"idx": 0, "idx": 0,
"is_complete": 0, "is_complete": 0,
"is_mandatory": 1,
"is_single": 0, "is_single": 0,
"is_skipped": 0, "is_skipped": 0,
"modified": "2020-05-19 12:27:09.437825", "modified": "2021-01-30 00:09:28.794602",
"modified_by": "Administrator", "modified_by": "ruchamahabal2@gmail.com",
"name": "Create Practitioner Schedule", "name": "Create Practitioner Schedule",
"owner": "Administrator", "owner": "Administrator",
"reference_document": "Practitioner Schedule", "reference_document": "Practitioner Schedule",
"show_form_tour": 0,
"show_full_form": 1, "show_full_form": 1,
"title": "Create Practitioner Schedule", "title": "Create Practitioner Schedule",
"validate_action": 1 "validate_action": 1

View File

@ -5,14 +5,14 @@
"doctype": "Onboarding Step", "doctype": "Onboarding Step",
"idx": 0, "idx": 0,
"is_complete": 0, "is_complete": 0,
"is_mandatory": 0,
"is_single": 0, "is_single": 0,
"is_skipped": 0, "is_skipped": 0,
"modified": "2020-05-26 23:10:24.504030", "modified": "2021-01-30 19:22:08.257160",
"modified_by": "Administrator", "modified_by": "Administrator",
"name": "Explore Clinical Procedure Templates", "name": "Explore Clinical Procedure Templates",
"owner": "Administrator", "owner": "Administrator",
"reference_document": "Clinical Procedure Template", "reference_document": "Clinical Procedure Template",
"show_form_tour": 0,
"show_full_form": 0, "show_full_form": 0,
"title": "Explore Clinical Procedure Templates", "title": "Explore Clinical Procedure Templates",
"validate_action": 1 "validate_action": 1

View File

@ -5,14 +5,14 @@
"doctype": "Onboarding Step", "doctype": "Onboarding Step",
"idx": 0, "idx": 0,
"is_complete": 0, "is_complete": 0,
"is_mandatory": 1,
"is_single": 1, "is_single": 1,
"is_skipped": 0, "is_skipped": 0,
"modified": "2020-05-26 23:10:24.507648", "modified": "2021-01-30 19:22:07.275735",
"modified_by": "Administrator", "modified_by": "Administrator",
"name": "Explore Healthcare Settings", "name": "Explore Healthcare Settings",
"owner": "Administrator", "owner": "Administrator",
"reference_document": "Healthcare Settings", "reference_document": "Healthcare Settings",
"show_form_tour": 0,
"show_full_form": 0, "show_full_form": 0,
"title": "Explore Healthcare Settings", "title": "Explore Healthcare Settings",
"validate_action": 1 "validate_action": 1

View File

@ -6,14 +6,14 @@
"field": "schedule", "field": "schedule",
"idx": 0, "idx": 0,
"is_complete": 0, "is_complete": 0,
"is_mandatory": 1,
"is_single": 0, "is_single": 0,
"is_skipped": 0, "is_skipped": 0,
"modified": "2020-05-26 22:07:07.482530", "modified": "2021-01-30 00:09:28.807129",
"modified_by": "Administrator", "modified_by": "ruchamahabal2@gmail.com",
"name": "Introduction to Healthcare Practitioner", "name": "Introduction to Healthcare Practitioner",
"owner": "Administrator", "owner": "Administrator",
"reference_document": "Healthcare Practitioner", "reference_document": "Healthcare Practitioner",
"show_form_tour": 0,
"show_full_form": 0, "show_full_form": 0,
"title": "Introduction to Healthcare Practitioner", "title": "Introduction to Healthcare Practitioner",
"validate_action": 0 "validate_action": 0

View File

@ -9,6 +9,26 @@
cursor: pointer; cursor: pointer;
} }
.patient-image-container {
margin-top: 17px;
}
.patient-image {
display: inline-block;
width: 100%;
height: 0;
padding: 50% 0px;
background-size: cover;
background-repeat: no-repeat;
background-position: center center;
border-radius: 4px;
}
.patient-name {
font-size: 20px;
margin-top: 25px;
}
.medical_record-label { .medical_record-label {
max-width: 100px; max-width: 100px;
margin-bottom: -4px; margin-bottom: -4px;
@ -19,19 +39,19 @@
} }
.date-indicator { .date-indicator {
background:none; background:none;
font-size:12px; font-size:12px;
vertical-align:middle; vertical-align:middle;
font-weight:bold; font-weight:bold;
color:#6c7680; color:#6c7680;
} }
.date-indicator::after { .date-indicator::after {
margin:0 -4px 0 12px; margin:0 -4px 0 12px;
content:''; content:'';
display:inline-block; display:inline-block;
height:8px; height:8px;
width:8px; width:8px;
border-radius:8px; border-radius:8px;
background: #d1d8dd; background: #d1d8dd;
} }

View File

@ -1,26 +1,18 @@
<div class="col-sm-12"> <div class="row patient-documents">
<div class="col-sm-3"> <div class="col-sm-12">
<p class="patient" style="margin: auto; max-width: 300px; margin-bottom: 20px;"></p> <div class="col-sm-12 show_chart_btns" align="center">
<div class="patient_details" style="z-index=0"></div> </div>
<div id="chart" class="col-sm-12 patient_vital_charts">
</div>
</div> </div>
<div class="col-sm-9 patient_documents"> <div class="header-separator col-sm-12 d-flex border-bottom py-3" style="display:none"></div>
<div class="col-sm-12"> <div class="col-sm-12 d-flex">
<div class="col-sm-12 show_chart_btns" align="center"> <div class="patient-history-filter doctype-filter"></div>
</div> <div class="patient-history-filter date-filter"></div>
<div id="chart" class="col-sm-12 patient_vital_charts"> </div>
</div> <div class="col-sm-12 patient_documents_list">
</div> </div>
<div class="header-separator col-sm-12 d-flex border-bottom py-3" style="display:none"></div> <div class="col-sm-12 text-center py-3">
<div class="row"> <a class="btn btn-sm btn-default btn-get-records" style="display:none">More..</a>
<div class="col-sm-12 d-flex">
<div class="patient-history-filter doctype-filter"></div>
<div class="patient-history-filter date-filter"></div>
</div>
</div>
<div class="col-sm-12 patient_documents_list">
</div>
<div class="col-sm-12 text-center py-3">
<a class="btn btn-sm btn-default btn-get-records" style="display:none">More..</a>
</div>
</div> </div>
</div> </div>

View File

@ -1,403 +1,455 @@
frappe.provide('frappe.patient_history'); frappe.provide('frappe.patient_history');
frappe.pages['patient_history'].on_page_load = function(wrapper) { frappe.pages['patient_history'].on_page_load = function(wrapper) {
let me = this; frappe.ui.make_app_page({
let page = frappe.ui.make_app_page({
parent: wrapper, parent: wrapper,
title: 'Patient History', title: __('Patient History')
single_column: true
}); });
frappe.breadcrumbs.add('Healthcare'); let patient_history = new PatientHistory(wrapper);
let pid = ''; $(wrapper).bind('show', ()=> {
page.main.html(frappe.render_template('patient_history', {})); patient_history.show();
page.main.find('.header-separator').hide();
let patient = frappe.ui.form.make_control({
parent: page.main.find('.patient'),
df: {
fieldtype: 'Link',
options: 'Patient',
fieldname: 'patient',
placeholder: __('Select Patient'),
only_select: true,
change: function() {
let patient_id = patient.get_value();
if (pid != patient_id && patient_id) {
me.start = 0;
me.page.main.find('.patient_documents_list').html('');
setup_filters(patient_id, me);
get_documents(patient_id, me);
show_patient_info(patient_id, me);
show_patient_vital_charts(patient_id, me, 'bp', 'mmHg', 'Blood Pressure');
}
pid = patient_id;
}
},
});
patient.refresh();
if (frappe.route_options) {
patient.set_value(frappe.route_options.patient);
}
this.page.main.on('click', '.btn-show-chart', function() {
let btn_show_id = $(this).attr('data-show-chart-id'), pts = $(this).attr('data-pts');
let title = $(this).attr('data-title');
show_patient_vital_charts(patient.get_value(), me, btn_show_id, pts, title);
});
this.page.main.on('click', '.btn-more', function() {
let doctype = $(this).attr('data-doctype'), docname = $(this).attr('data-docname');
if (me.page.main.find('.'+docname).parent().find('.document-html').attr('data-fetched') == '1') {
me.page.main.find('.'+docname).hide();
me.page.main.find('.'+docname).parent().find('.document-html').show();
} else {
if (doctype && docname) {
let exclude = ['patient', 'patient_name', 'patient_sex', 'encounter_date'];
frappe.call({
method: 'erpnext.healthcare.utils.render_doc_as_html',
args:{
doctype: doctype,
docname: docname,
exclude_fields: exclude
},
freeze: true,
callback: function(r) {
if (r.message) {
me.page.main.find('.' + docname).hide();
me.page.main.find('.' + docname).parent().find('.document-html').html(
`${r.message.html}
<div align='center'>
<a class='btn octicon octicon-chevron-up btn-default btn-xs btn-less'
data-doctype='${doctype}'
data-docname='${docname}'>
</a>
</div>
`);
me.page.main.find('.' + docname).parent().find('.document-html').show();
me.page.main.find('.' + docname).parent().find('.document-html').attr('data-fetched', '1');
}
}
});
}
}
});
this.page.main.on('click', '.btn-less', function() {
let docname = $(this).attr('data-docname');
me.page.main.find('.' + docname).parent().find('.document-id').show();
me.page.main.find('.' + docname).parent().find('.document-html').hide();
});
me.start = 0;
me.page.main.on('click', '.btn-get-records', function() {
get_documents(patient.get_value(), me);
}); });
}; };
let setup_filters = function(patient, me) { class PatientHistory {
$('.doctype-filter').empty(); constructor(wrapper) {
frappe.xcall( this.wrapper = $(wrapper);
'erpnext.healthcare.page.patient_history.patient_history.get_patient_history_doctypes' this.page = wrapper.page;
).then(document_types => { this.sidebar = this.wrapper.find('.layout-side-section');
let doctype_filter = frappe.ui.form.make_control({ this.main_section = this.wrapper.find('.layout-main-section');
parent: $('.doctype-filter'), this.start = 0;
}
show() {
frappe.breadcrumbs.add('Healthcare');
this.sidebar.empty();
let me = this;
let patient = frappe.ui.form.make_control({
parent: me.sidebar,
df: { df: {
fieldtype: 'MultiSelectList', fieldtype: 'Link',
fieldname: 'document_type', options: 'Patient',
placeholder: __('Select Document Type'), fieldname: 'patient',
input_class: 'input-xs', placeholder: __('Select Patient'),
only_select: true,
change: () => { change: () => {
me.start = 0; me.patient_id = '';
me.page.main.find('.patient_documents_list').html(''); if (me.patient_id != patient.get_value() && patient.get_value()) {
get_documents(patient, me, doctype_filter.get_value(), date_range_field.get_value()); me.start = 0;
}, me.patient_id = patient.get_value();
get_data: () => { me.make_patient_profile();
return document_types.map(document_type => { }
return { }
description: document_type,
value: document_type
};
});
},
} }
}); });
doctype_filter.refresh(); patient.refresh();
$('.date-filter').empty(); if (frappe.route_options && !this.patient_id) {
let date_range_field = frappe.ui.form.make_control({ patient.set_value(frappe.route_options.patient);
df: { this.patient_id = frappe.route_options.patient;
fieldtype: 'DateRange', }
fieldname: 'date_range',
placeholder: __('Date Range'), this.sidebar.find('[data-fieldname="patient"]').append('<div class="patient-info"></div>');
input_class: 'input-xs', }
change: () => {
let selected_date_range = date_range_field.get_value(); make_patient_profile() {
if (selected_date_range && selected_date_range.length === 2) { this.page.set_title(__('Patient History'));
this.main_section.empty().append(frappe.render_template('patient_history'));
this.setup_filters();
this.setup_documents();
this.show_patient_info();
this.setup_buttons();
this.show_patient_vital_charts('bp', 'mmHg', 'Blood Pressure');
}
setup_filters() {
$('.doctype-filter').empty();
let me = this;
frappe.xcall(
'erpnext.healthcare.page.patient_history.patient_history.get_patient_history_doctypes'
).then(document_types => {
let doctype_filter = frappe.ui.form.make_control({
parent: $('.doctype-filter'),
df: {
fieldtype: 'MultiSelectList',
fieldname: 'document_type',
placeholder: __('Select Document Type'),
change: () => {
me.start = 0; me.start = 0;
me.page.main.find('.patient_documents_list').html(''); me.page.main.find('.patient_documents_list').html('');
get_documents(patient, me, doctype_filter.get_value(), selected_date_range); this.setup_documents(doctype_filter.get_value(), date_range_field.get_value());
} },
get_data: () => {
return document_types.map(document_type => {
return {
description: document_type,
value: document_type
};
});
},
} }
}, });
parent: $('.date-filter') doctype_filter.refresh();
$('.date-filter').empty();
let date_range_field = frappe.ui.form.make_control({
df: {
fieldtype: 'DateRange',
fieldname: 'date_range',
placeholder: __('Date Range'),
input_class: 'input-xs',
change: () => {
let selected_date_range = date_range_field.get_value();
if (selected_date_range && selected_date_range.length === 2) {
me.start = 0;
me.page.main.find('.patient_documents_list').html('');
this.setup_documents(doctype_filter.get_value(), date_range_field.get_value());
}
}
},
parent: $('.date-filter')
});
date_range_field.refresh();
}); });
date_range_field.refresh(); }
});
};
let get_documents = function(patient, me, document_types="", selected_date_range="") { setup_documents(document_types="", selected_date_range="") {
let filters = { let filters = {
name: patient, name: this.patient_id,
start: me.start, start: this.start,
page_length: 20 page_length: 20
}; };
if (document_types) if (document_types)
filters['document_types'] = document_types; filters['document_types'] = document_types;
if (selected_date_range) if (selected_date_range)
filters['date_range'] = selected_date_range; filters['date_range'] = selected_date_range;
frappe.call({ let me = this;
'method': 'erpnext.healthcare.page.patient_history.patient_history.get_feed', frappe.call({
args: filters, 'method': 'erpnext.healthcare.page.patient_history.patient_history.get_feed',
callback: function(r) { args: filters,
let data = r.message; callback: function(r) {
if (data.length) { let data = r.message;
add_to_records(me, data); if (data.length) {
} else { me.add_to_records(data);
me.page.main.find('.patient_documents_list').append(` } else {
<div class='text-muted' align='center'> me.page.main.find('.patient_documents_list').append(`
<br><br>${__('No more records..')}<br><br> <div class='text-muted' align='center'>
</div>`); <br><br>${__('No more records..')}<br><br>
me.page.main.find('.btn-get-records').hide(); </div>`);
me.page.main.find('.btn-get-records').hide();
}
} }
} });
}); }
};
let add_to_records = function(me, data) { add_to_records(data) {
let details = "<ul class='nav nav-pills nav-stacked'>"; let details = "";
let i; let i;
for (i=0; i<data.length; i++) { for (i=0; i<data.length; i++) {
if (data[i].reference_doctype) { if (data[i].reference_doctype) {
let label = ''; let label = '';
if (data[i].subject) { if (data[i].subject) {
label += "<br/>" + data[i].subject; label += "<br/>" + data[i].subject;
} }
data[i] = add_date_separator(data[i]); data[i] = this.add_date_separator(data[i]);
if (frappe.user_info(data[i].owner).image) { if (frappe.user_info(data[i].owner).image) {
data[i].imgsrc = frappe.utils.get_file_link(frappe.user_info(data[i].owner).image); data[i].imgsrc = frappe.utils.get_file_link(frappe.user_info(data[i].owner).image);
} else { } else {
data[i].imgsrc = false; data[i].imgsrc = false;
} }
let time_line_heading = data[i].practitioner ? `${data[i].practitioner} ` : ``; let time_line_heading = data[i].practitioner ? `${data[i].practitioner} ` : ``;
time_line_heading += data[i].reference_doctype + " - " + time_line_heading += data[i].reference_doctype + " - " +
`<a onclick="frappe.set_route('Form', '${data[i].reference_doctype}', '${data[i].reference_name}');"> `<a onclick="frappe.set_route('Form', '${data[i].reference_doctype}', '${data[i].reference_name}');">
${data[i].reference_name} ${data[i].reference_name}
</a>`; </a>`;
details += `
<li data-toggle='pill' class='patient_doc_menu'
data-doctype='${data[i].reference_doctype}' data-docname='${data[i].reference_name}'>
<div class='col-sm-12 d-flex border-bottom py-3'>`;
if (data[i].imgsrc) {
details += ` details += `
<span class='mr-3'> <div data-toggle='pill' class='patient_doc_menu'
<img class='avtar' src='${data[i].imgsrc}' width='32' height='32'></img> data-doctype='${data[i].reference_doctype}' data-docname='${data[i].reference_name}'>
</span>`; <div class='col-sm-12 d-flex border-bottom py-3'>`;
} else {
details += `<span class='mr-3 avatar avatar-small' style='width:32px; height:32px;'>
<div align='center' class='standard-image' style='background-color: #fafbfc;'>
${data[i].practitioner ? data[i].practitioner.charAt(0) : 'U'}
</div>
</span>`;
}
details += `<div class='d-flex flex-column width-full'> if (data[i].imgsrc) {
<div> details += `<span class='mr-3 avatar avatar-small' style='width:32px; height:32px;'>
`+time_line_heading+` <img class='avatar-frame' src='${data[i].imgsrc}' width='32' height='32'></img>
<span> </span>`;
${data[i].date_sep} } else {
details += `<span class='mr-3 avatar avatar-small' style='width:32px; height:32px;'>
<div align='center' class='avatar-frame' style='background-color: #fafbfc;'>
${data[i].practitioner ? data[i].practitioner.charAt(0) : 'U'}
</div>
</span>`;
}
details += `<div class='d-flex flex-column width-full'>
<div>
`+time_line_heading+`
<span>
${data[i].date_sep}
</span>
</div>
<div class='frappe-card p-5 mt-3'>
<span class='${data[i].reference_name} document-id'>${label}
<br>
<div align='center'>
<a class='btn octicon octicon-chevron-down btn-default btn-xs btn-more'
data-doctype='${data[i].reference_doctype}' data-docname='${data[i].reference_name}'>
</a>
</div>
</span> </span>
</div>
<div class='Box p-3 mt-2'> <span class='document-html' hidden data-fetched='0'>
<span class='${data[i].reference_name} document-id'>${label} </span>
<div align='center'> </div>
<a class='btn octicon octicon-chevron-down btn-default btn-xs btn-more'
data-doctype='${data[i].reference_doctype}' data-docname='${data[i].reference_name}'>
</a>
</div>
</span>
<span class='document-html' hidden data-fetched="0">
</span>
</div> </div>
</div> </div>
</div> </div>`;
</li>`; }
}
this.page.main.find('.patient_documents_list').append(details);
this.start += data.length;
if (data.length === 20) {
this.page.main.find(".btn-get-records").show();
} else {
this.page.main.find(".btn-get-records").hide();
this.page.main.find(".patient_documents_list").append(`
<div class='text-muted' align='center'>
<br><br>${__('No more records..')}<br><br>
</div>`);
} }
} }
details += '</ul>'; add_date_separator(data) {
me.page.main.find('.patient_documents_list').append(details); let date = frappe.datetime.str_to_obj(data.communication_date);
me.start += data.length; let pdate = '';
let diff = frappe.datetime.get_day_diff(frappe.datetime.get_today(),
frappe.datetime.obj_to_str(date));
if (data.length === 20) { if (diff < 1) {
me.page.main.find(".btn-get-records").show(); pdate = __('Today');
} else { } else if (diff < 2) {
me.page.main.find(".btn-get-records").hide(); pdate = __('Yesterday');
me.page.main.find(".patient_documents_list").append(` } else {
<div class='text-muted' align='center'> pdate = __('on {0}', [frappe.datetime.global_date_format(date)]);
<br><br>${__('No more records..')}<br><br>
</div>`);
}
};
let add_date_separator = function(data) {
let date = frappe.datetime.str_to_obj(data.communication_date);
let pdate = '';
let diff = frappe.datetime.get_day_diff(frappe.datetime.get_today(), frappe.datetime.obj_to_str(date));
if (diff < 1) {
pdate = __('Today');
} else if (diff < 2) {
pdate = __('Yesterday');
} else {
pdate = __('on ') + frappe.datetime.global_date_format(date);
}
data.date_sep = pdate;
return data;
};
let show_patient_info = function(patient, me) {
frappe.call({
'method': 'erpnext.healthcare.doctype.patient.patient.get_patient_detail',
args: {
patient: patient
},
callback: function(r) {
let data = r.message;
let details = '';
if (data.image) {
details += `<div><img class='thumbnail' width=75% src='${data.image}'></div>`;
}
details += `<b> ${data.patient_name} </b><br> ${data.sex}`;
if (data.email) details += `<br> ${data.email}`;
if (data.mobile) details += `<br> ${data.mobile}`;
if (data.occupation) details += `<br><br><b> ${__('Occupation')} : </b> ${data.occupation}`;
if (data.blood_group) details += `<br><b> ${__('Blood Group')} : </b> ${data.blood_group}`;
if (data.allergies) details += `<br><br><b> ${__('Allerigies')} : </b> ${data.allergies.replace("\n", ", ")}`;
if (data.medication) details += `<br><b> ${__('Medication')} : </b> ${data.medication.replace("\n", ", ")}`;
if (data.alcohol_current_use) details += `<br><br><b> ${__('Alcohol use')} : </b> ${data.alcohol_current_use}`;
if (data.alcohol_past_use) details += `<br><b> ${__('Alcohol past use')} : </b> ${data.alcohol_past_use}`;
if (data.tobacco_current_use) details += `<br><b> ${__('Tobacco use')} : </b> ${data.tobacco_current_use}`;
if (data.tobacco_past_use) details += `<br><b> ${__('Tobacco past use')} : </b> ${data.tobacco_past_use}`;
if (data.medical_history) details += `<br><br><b> ${__('Medical history')} : </b> ${data.medical_history.replace("\n", ", ")}`;
if (data.surgical_history) details += `<br><b> ${__('Surgical history')} : </b> ${data.surgical_history.replace("\n", ", ")}`;
if (data.surrounding_factors) details += `<br><br><b> ${__('Occupational hazards')} : </b> ${data.surrounding_factors.replace("\n", ", ")}`;
if (data.other_risk_factors) details += `<br><b> ${__('Other risk factors')} : </b> ${data.other_risk_factors.replace("\n", ", ")}`;
if (data.patient_details) details += `<br><br><b> ${__('More info')} : </b> ${data.patient_details.replace("\n", ", ")}`;
if (details) {
details = `<div style='padding-left:10px; font-size:13px;' align='left'>` + details + `</div>`;
}
me.page.main.find('.patient_details').html(details);
} }
}); data.date_sep = pdate;
}; return data;
}
let show_patient_vital_charts = function(patient, me, btn_show_id, pts, title) { show_patient_info() {
frappe.call({ this.get_patient_info().then(() => {
method: 'erpnext.healthcare.utils.get_patient_vitals', $('.patient-info').empty().append(frappe.render_template('patient_history_sidebar', {
args:{ patient_image: this.patient.image,
patient: patient patient_name: this.patient.patient_name,
}, patient_gender: this.patient.sex,
callback: function(r) { patient_mobile: this.patient.mobile
if (r.message) { }));
let show_chart_btns_html = ` this.show_patient_details();
<div style='padding-top:10px;'> });
<a class='btn btn-default btn-xs btn-show-chart' data-show-chart-id='bp' data-pts='mmHg' data-title='Blood Pressure'> }
${__('Blood Pressure')}
</a>
<a class='btn btn-default btn-xs btn-show-chart' data-show-chart-id='pulse_rate' data-pts='per Minutes' data-title='Respiratory/Pulse Rate'>
${__('Respiratory/Pulse Rate')}
</a>
<a class='btn btn-default btn-xs btn-show-chart' data-show-chart-id='temperature' data-pts='°C or °F' data-title='Temperature'>
${__('Temperature')}
</a>
<a class='btn btn-default btn-xs btn-show-chart' data-show-chart-id='bmi' data-pts='' data-title='BMI'>
${__('BMI')}
</a>
</div>`;
me.page.main.find('.show_chart_btns').html(show_chart_btns_html); show_patient_details() {
let me = this;
frappe.call({
'method': 'erpnext.healthcare.doctype.patient.patient.get_patient_detail',
args: {
patient: me.patient_id
},
callback: function(r) {
let data = r.message; let data = r.message;
let labels = [], datasets = []; let details = ``;
let bp_systolic = [], bp_diastolic = [], temperature = [];
let pulse = [], respiratory_rate = [], bmi = [], height = [], weight = [];
for (let i=0; i<data.length; i++) { if (data.occupation) details += `<br><br><b> ${__('Occupation')} : </b> ${data.occupation}`;
labels.push(data[i].signs_date+'||'+data[i].signs_time); if (data.blood_group) details += `<br><b> ${__('Blood Group')} : </b> ${data.blood_group}`;
if (data.allergies) details += `<br><br><b> ${__('Allerigies')} : </b> ${data.allergies.replace("\n", ", ")}`;
if (data.medication) details += `<br><b> ${__('Medication')} : </b> ${data.medication.replace("\n", ", ")}`;
if (data.alcohol_current_use) details += `<br><br><b> ${__('Alcohol use')} : </b> ${data.alcohol_current_use}`;
if (data.alcohol_past_use) details += `<br><b> ${__('Alcohol past use')} : </b> ${data.alcohol_past_use}`;
if (data.tobacco_current_use) details += `<br><b> ${__('Tobacco use')} : </b> ${data.tobacco_current_use}`;
if (data.tobacco_past_use) details += `<br><b> ${__('Tobacco past use')} : </b> ${data.tobacco_past_use}`;
if (data.medical_history) details += `<br><br><b> ${__('Medical history')} : </b> ${data.medical_history.replace("\n", ", ")}`;
if (data.surgical_history) details += `<br><b> ${__('Surgical history')} : </b> ${data.surgical_history.replace("\n", ", ")}`;
if (data.surrounding_factors) details += `<br><br><b> ${__('Occupational hazards')} : </b> ${data.surrounding_factors.replace("\n", ", ")}`;
if (data.other_risk_factors) details += `<br><b> ${__('Other risk factors')} : </b> ${data.other_risk_factors.replace("\n", ", ")}`;
if (data.patient_details) details += `<br><br><b> ${__('More info')} : </b> ${data.patient_details.replace("\n", ", ")}`;
if (btn_show_id === 'bp') { if (details) {
bp_systolic.push(data[i].bp_systolic); details = `<div style='font-size:13px;' align='left'>` + details + `</div>`;
bp_diastolic.push(data[i].bp_diastolic);
}
if (btn_show_id === 'temperature') {
temperature.push(data[i].temperature);
}
if (btn_show_id === 'pulse_rate') {
pulse.push(data[i].pulse);
respiratory_rate.push(data[i].respiratory_rate);
}
if (btn_show_id === 'bmi') {
bmi.push(data[i].bmi);
height.push(data[i].height);
weight.push(data[i].weight);
}
} }
if (btn_show_id === 'temperature') {
datasets.push({name: 'Temperature', values: temperature, chartType: 'line'});
}
if (btn_show_id === 'bmi') {
datasets.push({name: 'BMI', values: bmi, chartType: 'line'});
datasets.push({name: 'Height', values: height, chartType: 'line'});
datasets.push({name: 'Weight', values: weight, chartType: 'line'});
}
if (btn_show_id === 'bp') {
datasets.push({name: 'BP Systolic', values: bp_systolic, chartType: 'line'});
datasets.push({name: 'BP Diastolic', values: bp_diastolic, chartType: 'line'});
}
if (btn_show_id === 'pulse_rate') {
datasets.push({name: 'Heart Rate / Pulse', values: pulse, chartType: 'line'});
datasets.push({name: 'Respiratory Rate', values: respiratory_rate, chartType: 'line'});
}
new frappe.Chart('.patient_vital_charts', {
data: {
labels: labels,
datasets: datasets
},
title: title, me.sidebar.find('.patient-details').html(details);
type: 'axis-mixed',
height: 200,
colors: ['purple', '#ffa3ef', 'light-blue'],
tooltipOptions: {
formatTooltipX: d => (d + '').toUpperCase(),
formatTooltipY: d => d + ' ' + pts,
}
});
me.page.main.find('.header-separator').show();
} else {
me.page.main.find('.patient_vital_charts').html('');
me.page.main.find('.show_chart_btns').html('');
me.page.main.find('.header-separator').hide();
} }
} });
}); }
};
get_patient_info() {
return frappe.xcall('frappe.client.get', {
doctype: 'Patient',
name: this.patient_id,
}).then((patient) => {
if (patient) {
this.patient = patient;
}
});
}
setup_buttons() {
let me = this;
this.page.main.on("click", ".btn-show-chart", function() {
let btn_id = $(this).attr("data-show-chart-id"), scale_unit = $(this).attr("data-pts");
let title = $(this).attr("data-title");
me.show_patient_vital_charts(btn_id, scale_unit, title);
});
this.page.main.on('click', '.btn-more', function() {
let doctype = $(this).attr('data-doctype'), docname = $(this).attr('data-docname');
if (me.page.main.find('.'+docname).parent().find('.document-html').attr('data-fetched') == '1') {
me.page.main.find('.'+docname).hide();
me.page.main.find('.'+docname).parent().find('.document-html').show();
} else {
if (doctype && docname) {
let exclude = ['patient', 'patient_name', 'patient_sex', 'encounter_date', 'naming_series'];
frappe.call({
method: 'erpnext.healthcare.utils.render_doc_as_html',
args: {
doctype: doctype,
docname: docname,
exclude_fields: exclude
},
freeze: true,
callback: function(r) {
if (r.message) {
me.page.main.find('.' + docname).hide();
me.page.main.find('.' + docname).parent().find('.document-html').html(
`${r.message.html}
<br>
<div align='center'>
<a class='btn octicon octicon-chevron-up btn-default btn-xs btn-less'
data-doctype='${doctype}'
data-docname='${docname}'>
</a>
</div>
`);
me.page.main.find('.' + docname).parent().find('.document-html').attr('hidden', false);
me.page.main.find('.' + docname).parent().find('.document-html').attr('data-fetched', '1');
}
}
});
}
}
});
this.page.main.on('click', '.btn-less', function() {
let docname = $(this).attr('data-docname');
me.page.main.find('.' + docname).parent().find('.document-id').show();
me.page.main.find('.' + docname).parent().find('.document-html').hide();
});
me.page.main.on('click', '.btn-get-records', function() {
this.setup_documents();
});
}
show_patient_vital_charts(btn_id, scale_unit, title) {
let me = this;
frappe.call({
method: 'erpnext.healthcare.utils.get_patient_vitals',
args: {
patient: me.patient_id
},
callback: function(r) {
if (r.message) {
let show_chart_btns_html = `
<div style='padding-top:10px;'>
<a class='btn btn-default btn-xs btn-show-chart' data-show-chart-id='bp' data-pts='mmHg' data-title='Blood Pressure'>
${__('Blood Pressure')}
</a>
<a class='btn btn-default btn-xs btn-show-chart' data-show-chart-id='pulse_rate' data-pts='per Minutes' data-title='Respiratory/Pulse Rate'>
${__('Respiratory/Pulse Rate')}
</a>
<a class='btn btn-default btn-xs btn-show-chart' data-show-chart-id='temperature' data-pts='°C or °F' data-title='Temperature'>
${__('Temperature')}
</a>
<a class='btn btn-default btn-xs btn-show-chart' data-show-chart-id='bmi' data-pts='' data-title='BMI'>
${__('BMI')}
</a>
</div>`;
me.page.main.find('.show_chart_btns').html(show_chart_btns_html);
let data = r.message;
let labels = [], datasets = [];
let bp_systolic = [], bp_diastolic = [], temperature = [];
let pulse = [], respiratory_rate = [], bmi = [], height = [], weight = [];
for (let i=0; i<data.length; i++) {
labels.push(data[i].signs_date+' | '+data[i].signs_time);
if (btn_id === 'bp') {
bp_systolic.push(data[i].bp_systolic);
bp_diastolic.push(data[i].bp_diastolic);
}
if (btn_id === 'temperature') {
temperature.push(data[i].temperature);
}
if (btn_id === 'pulse_rate') {
pulse.push(data[i].pulse);
respiratory_rate.push(data[i].respiratory_rate);
}
if (btn_id === 'bmi') {
bmi.push(data[i].bmi);
height.push(data[i].height);
weight.push(data[i].weight);
}
}
if (btn_id === 'temperature') {
datasets.push({name: 'Temperature', values: temperature, chartType: 'line'});
}
if (btn_id === 'bmi') {
datasets.push({name: 'BMI', values: bmi, chartType: 'line'});
datasets.push({name: 'Height', values: height, chartType: 'line'});
datasets.push({name: 'Weight', values: weight, chartType: 'line'});
}
if (btn_id === 'bp') {
datasets.push({name: 'BP Systolic', values: bp_systolic, chartType: 'line'});
datasets.push({name: 'BP Diastolic', values: bp_diastolic, chartType: 'line'});
}
if (btn_id === 'pulse_rate') {
datasets.push({name: 'Heart Rate / Pulse', values: pulse, chartType: 'line'});
datasets.push({name: 'Respiratory Rate', values: respiratory_rate, chartType: 'line'});
}
new frappe.Chart('.patient_vital_charts', {
data: {
labels: labels,
datasets: datasets
},
title: title,
type: 'axis-mixed',
height: 200,
colors: ['purple', '#ffa3ef', 'light-blue'],
tooltipOptions: {
formatTooltipX: d => (d + '').toUpperCase(),
formatTooltipY: d => d + ' ' + scale_unit,
}
});
me.page.main.find('.header-separator').show();
} else {
me.page.main.find('.patient_vital_charts').html('');
me.page.main.find('.show_chart_btns').html('');
me.page.main.find('.header-separator').hide();
}
}
});
}
}

View File

@ -0,0 +1,21 @@
<div class="patient-history-sidebar">
<div class="patient-image-container">
{% if patient_image %}
<div class="patient-image" src={{patient_image}} style="background-image: url(\'{%= patient_image %}\')"></div>
{% endif %}
</div>
<div class="patient-intro">
{% if patient_name %}
<p class="patient-name bold">{{patient_name}}</p>
{% endif %}
{% if patient_gender %}
<p class="patient-gender text-muted">{%=__("Gender: ") %} {{patient_gender}}</p>
{% endif %}
{% if patient_mobile %}
<p class="patient-mobile text-muted">{%=__("Contact: ") %} {{patient_mobile}}</p>
{% endif %}
</div>
<div class="patient-details">
</div>
</div>

View File

@ -29,6 +29,7 @@
.patient-name { .patient-name {
font-size: 20px; font-size: 20px;
margin-top: 25px;
} }
/* heatmap */ /* heatmap */
@ -55,6 +56,7 @@
} }
.heatmap-container .chart-filter { .heatmap-container .chart-filter {
z-index: 1;
position: relative; position: relative;
top: 5px; top: 5px;
margin-right: 10px; margin-right: 10px;
@ -111,10 +113,13 @@ text.title {
} }
.chart-column-container { .chart-column-container {
border-bottom: 1px solid #d1d8dd;
margin: 5px 0; margin: 5px 0;
} }
.progress-graphs .progress-container {
margin-bottom: var(--margin-xl);
}
.line-chart-container .frappe-chart { .line-chart-container .frappe-chart {
margin-top: -20px; margin-top: -20px;
} }
@ -146,6 +151,7 @@ text.title {
} }
.percentage-chart-container .chart-filter { .percentage-chart-container .chart-filter {
z-index: 1;
position: relative; position: relative;
top: 12px; top: 12px;
margin-right: 10px; margin-right: 10px;

View File

@ -1,14 +1,15 @@
<div class="row patient-progress"> <div class="row patient-progress">
<div class="col-md-12"> <div class="col-md-12">
<div class="progress-graphs"> <div class="progress-graphs">
<div class="chart-column-container heatmap-container hidden-xs hidden-sm"> <div class="progress-container chart-column-container heatmap-container hidden-xs hidden-sm frappe-card">
<div class="patient-heatmap"></div> <div class="patient-heatmap"></div>
</div> </div>
<div class="chart-column-container percentage-chart-container">
<div class="progress-container chart-column-container percentage-chart-container frappe-card">
<div class="therapy-session-percentage-chart"></div> <div class="therapy-session-percentage-chart"></div>
</div> </div>
<div class="therapy-progress"> <div class="progress-container therapy-progress frappe-card">
<div class="chart-head"> <div class="chart-head">
<text class="title" text-anchor="start">Therapy Progress</text> <text class="title" text-anchor="start">Therapy Progress</text>
<div class="chart-control pull-right"></div> <div class="chart-control pull-right"></div>
@ -22,7 +23,7 @@
</div> </div>
</div> </div>
<div class="assessment-results"> <div class="progress-container assessment-results frappe-card">
<div class="chart-head"> <div class="chart-head">
<text class="title" text-anchor="start">Assessment Results</text> <text class="title" text-anchor="start">Assessment Results</text>
<div class="chart-control pull-right"></div> <div class="chart-control pull-right"></div>
@ -36,7 +37,7 @@
</div> </div>
</div> </div>
<div class="therapy-assessment-correlation progress-line-chart"> <div class="progress-container therapy-assessment-correlation progress-line-chart frappe-card">
<div class="chart-head"> <div class="chart-head">
<text class="title" text-anchor="start">Therapy Type and Assessment Correlation</text> <text class="title" text-anchor="start">Therapy Type and Assessment Correlation</text>
<div class="chart-control pull-right"></div> <div class="chart-control pull-right"></div>
@ -50,7 +51,7 @@
</div> </div>
</div> </div>
<div class="assessment-parameter-progress progress-line-chart"> <div class="progress-container assessment-parameter-progress progress-line-chart frappe-card">
<div class="chart-head"> <div class="chart-head">
<text class="title" text-anchor="start">Assessment Parameter Wise Progress</text> <text class="title" text-anchor="start">Assessment Parameter Wise Progress</text>
<div class="chart-control pull-right"></div> <div class="chart-control pull-right"></div>

View File

@ -133,8 +133,11 @@ class PatientProgress {
type: 'heatmap', type: 'heatmap',
countLabel: 'Interactions', countLabel: 'Interactions',
data: {}, data: {},
discreteDomains: 0 discreteDomains: 1,
radius: 3,
height: 150
}); });
this.update_heatmap_data(); this.update_heatmap_data();
this.create_heatmap_chart_filters(); this.create_heatmap_chart_filters();
} }
@ -164,33 +167,35 @@ class PatientProgress {
} }
render_percentage_chart(field, title) { render_percentage_chart(field, title) {
frappe.xcall( // REDESIGN-TODO: chart seems to be broken. Enable this once fixed.
'erpnext.healthcare.page.patient_progress.patient_progress.get_therapy_sessions_distribution_data', { this.wrapper.find('.percentage-chart-container').hide();
patient: this.patient_id, // frappe.xcall(
field: field // 'erpnext.healthcare.page.patient_progress.patient_progress.get_therapy_sessions_distribution_data', {
} // patient: this.patient_id,
).then(chart => { // field: field
if (chart.labels.length) { // }
this.percentage_chart = new frappe.Chart('.therapy-session-percentage-chart', { // ).then(chart => {
title: title, // if (chart.labels.length) {
type: 'percentage', // this.percentage_chart = new frappe.Chart('.therapy-session-percentage-chart', {
data: { // title: title,
labels: chart.labels, // type: 'percentage',
datasets: chart.datasets // data: {
}, // labels: chart.labels,
truncateLegends: 1, // datasets: chart.datasets
barOptions: { // },
height: 11, // truncateLegends: 1,
depth: 1 // barOptions: {
}, // height: 11,
height: 160, // depth: 1
maxSlices: 8, // },
colors: ['#5e64ff', '#743ee2', '#ff5858', '#ffa00a', '#feef72', '#28a745', '#98d85b', '#a9a7ac'], // height: 160,
}); // maxSlices: 8,
} else { // colors: ['#5e64ff', '#743ee2', '#ff5858', '#ffa00a', '#feef72', '#28a745', '#98d85b', '#a9a7ac'],
this.wrapper.find('.percentage-chart-container').hide(); // });
} // } else {
}); // this.wrapper.find('.percentage-chart-container').hide();
// }
// });
} }
create_percentage_chart_filters() { create_percentage_chart_filters() {
@ -311,7 +316,7 @@ class PatientProgress {
}, },
axisOptions: { axisOptions: {
xIsSeries: 1 xIsSeries: 1
}, }
}); });
} else { } else {
$(parent).find('.chart-container').show(); $(parent).find('.chart-container').show();
@ -377,7 +382,7 @@ class PatientProgress {
xIsSeries: 1 xIsSeries: 1
}, },
tooltipOptions: { tooltipOptions: {
formatTooltipY: d => d + __(' out of ') + chart.max_score formatTooltipY: d => __('{0} out of {1}', [d, chart.max_score])
} }
}); });
} else { } else {

View File

@ -169,7 +169,7 @@ def get_chart_data(data):
'labels': labels, 'labels': labels,
'datasets': datasets 'datasets': datasets
}, },
'type': 'donut', 'type': 'bar',
'height': 300, 'height': 300,
} }

View File

@ -8,8 +8,7 @@ import frappe
import json import json
from frappe import _ from frappe import _
from frappe.utils.formatters import format_value from frappe.utils.formatters import format_value
from frappe.utils import time_diff_in_hours, rounded from frappe.utils import time_diff_in_hours, rounded, cstr
from six import string_types
from erpnext.healthcare.doctype.healthcare_settings.healthcare_settings import get_income_account from erpnext.healthcare.doctype.healthcare_settings.healthcare_settings import get_income_account
from erpnext.healthcare.doctype.fee_validity.fee_validity import create_fee_validity from erpnext.healthcare.doctype.fee_validity.fee_validity import create_fee_validity
from erpnext.healthcare.doctype.lab_test.lab_test import create_multiple from erpnext.healthcare.doctype.lab_test.lab_test import create_multiple
@ -181,9 +180,9 @@ def get_clinical_procedures_to_invoice(patient, company):
service_item = frappe.db.get_single_value('Healthcare Settings', 'clinical_procedure_consumable_item') service_item = frappe.db.get_single_value('Healthcare Settings', 'clinical_procedure_consumable_item')
if not service_item: if not service_item:
msg = _('Please Configure Clinical Procedure Consumable Item in ') frappe.throw(_('Please configure Clinical Procedure Consumable Item in {0}').format(
msg += '''<b><a href='/app/Form/Healthcare Settings'>Healthcare Settings</a></b>''' frappe.utils.get_link_to_form('Healthcare Settings', 'Healthcare Settings')),
frappe.throw(msg, title=_('Missing Configuration')) title=_('Missing Configuration'))
clinical_procedures_to_invoice.append({ clinical_procedures_to_invoice.append({
'reference_type': 'Clinical Procedure', 'reference_type': 'Clinical Procedure',
@ -312,7 +311,7 @@ def get_therapy_sessions_to_invoice(patient, company):
@frappe.whitelist() @frappe.whitelist()
def get_service_item_and_practitioner_charge(doc): def get_service_item_and_practitioner_charge(doc):
if isinstance(doc, string_types): if isinstance(doc, str):
doc = json.loads(doc) doc = json.loads(doc)
doc = frappe.get_doc(doc) doc = frappe.get_doc(doc)
@ -607,101 +606,147 @@ def render_docs_as_html(docs):
@frappe.whitelist() @frappe.whitelist()
def render_doc_as_html(doctype, docname, exclude_fields = []): def render_doc_as_html(doctype, docname, exclude_fields = []):
#render document as html, three column layout will break """
Render document as HTML
"""
doc = frappe.get_doc(doctype, docname) doc = frappe.get_doc(doctype, docname)
meta = frappe.get_meta(doctype) meta = frappe.get_meta(doctype)
doc_html = "<div class='col-md-12 col-sm-12'>" doc_html = section_html = section_label = html = ""
section_html = '' sec_on = has_data = False
section_label = ''
html = ''
sec_on = False
col_on = 0 col_on = 0
has_data = False
for df in meta.fields: for df in meta.fields:
#on section break append append previous section and html to doc html # on section break append previous section and html to doc html
if df.fieldtype == "Section Break": if df.fieldtype == "Section Break":
if has_data and col_on and sec_on: if has_data and col_on and sec_on:
doc_html += section_html + html + "</div>" doc_html += section_html + html + "</div>"
elif has_data and not col_on and sec_on: elif has_data and not col_on and sec_on:
doc_html += "<div class='col-md-12 col-sm-12'\ doc_html += """
><div class='col-md-12 col-sm-12'>" \ <br>
+ section_html + html +"</div></div>" <div class='row'>
<div class='col-md-12 col-sm-12'>
<b>{0}</b>
</div>
</div>
<div class='row'>
<div class='col-md-12 col-sm-12'>
{1} {2}
</div>
</div>
""".format(section_label, section_html, html)
# close divs for columns
while col_on: while col_on:
doc_html += "</div>" doc_html += "</div>"
col_on -= 1 col_on -= 1
sec_on = True sec_on = True
has_data= False has_data = False
col_on = 0 col_on = 0
section_html = '' section_html = html = ""
html = ''
if df.label: if df.label:
section_label = df.label section_label = df.label
continue continue
#on column break append html to section html or doc html
# on column break append html to section html or doc html
if df.fieldtype == "Column Break": if df.fieldtype == "Column Break":
if sec_on and has_data: if sec_on and not col_on and has_data:
section_html += "<div class='col-md-12 col-sm-12'\ section_html += """
><div class='col-md-6 col\ <br>
-sm-6'><b>" + section_label + "</b>" + html + "</div><div \ <div class='row'>
class='col-md-6 col-sm-6'>" <div class='col-md-12 col-sm-12'>
elif has_data: <b>{0}</b>
doc_html += "<div class='col-md-12 col-sm-12'><div class='col-m\ </div>
d-6 col-sm-6'>" + html + "</div><div class='col-md-6 col-sm-6'>" </div>
elif sec_on and not col_on: <div class='row'>
section_html += "<div class='col-md-6 col-sm-6'>" <div class='col-md-4 col-sm-4'>
html = '' {1}
</div>
""".format(section_label, html)
elif col_on == 1 and has_data:
section_html += "<div class='col-md-4 col-sm-4'>" + html + "</div>"
elif col_on > 1 and has_data:
doc_html += "<div class='col-md-4 col-sm-4'>" + html + "</div>"
else:
doc_html += """
<div class='row'>
<div class='col-md-12 col-sm-12'>
{0}
</div>
</div>
""".format(html)
html = ""
col_on += 1 col_on += 1
if df.label: if df.label:
html += '<br>' + df.label html += "<br>" + df.label
continue continue
#on table iterate in items and create table based on in_list_view, append to section html or doc html
if df.fieldtype == 'Table': # on table iterate through items and create table
# based on the in_list_view property
# append to section html or doc html
if df.fieldtype == "Table":
items = doc.get(df.fieldname) items = doc.get(df.fieldname)
if not items: continue if not items:
continue
child_meta = frappe.get_meta(df.options) child_meta = frappe.get_meta(df.options)
if not has_data : has_data = True
table_head = '' if not has_data:
table_row = '' has_data = True
table_head = table_row = ""
create_head = True create_head = True
for item in items: for item in items:
table_row += '<tr>' table_row += "<tr>"
for cdf in child_meta.fields: for cdf in child_meta.fields:
if cdf.in_list_view: if cdf.in_list_view:
if create_head: if create_head:
table_head += '<th>' + cdf.label + '</th>' table_head += "<th class='text-muted'>" + cdf.label + "</th>"
if item.get(cdf.fieldname): if item.get(cdf.fieldname):
table_row += '<td>' + str(item.get(cdf.fieldname)) \ table_row += "<td>" + cstr(item.get(cdf.fieldname)) + "</td>"
+ '</td>'
else: else:
table_row += '<td></td>' table_row += "<td></td>"
create_head = False create_head = False
table_row += '</tr>' table_row += "</tr>"
if sec_on: if sec_on:
section_html += "<table class='table table-condensed \ section_html += """
bordered'>" + table_head + table_row + '</table>' <table class='table table-condensed bordered'>
{0} {1}
</table>
""".format(table_head, table_row)
else: else:
html += "<table class='table table-condensed table-bordered'>" \ html += """
+ table_head + table_row + "</table>" <table class='table table-condensed table-bordered'>
{0} {1}
</table>
""".format(table_head, table_row)
continue continue
#on other field types add label and value to html # on any other field type add label and value to html
if not df.hidden and not df.print_hide and doc.get(df.fieldname) and df.fieldname not in exclude_fields: if not df.hidden and not df.print_hide and doc.get(df.fieldname) and df.fieldname not in exclude_fields:
if doc.get(df.fieldname): formatted_value = format_value(doc.get(df.fieldname), meta.get_field(df.fieldname), doc)
formatted_value = format_value(doc.get(df.fieldname), meta.get_field(df.fieldname), doc) html += "<br>{0} : {1}".format(df.label or df.fieldname, formatted_value)
html += '<br>{0} : {1}'.format(df.label or df.fieldname, formatted_value)
if not has_data : has_data = True if not has_data : has_data = True
if sec_on and col_on and has_data: if sec_on and col_on and has_data:
doc_html += section_html + html + '</div></div>' doc_html += section_html + html + "</div></div>"
elif sec_on and not col_on and has_data: elif sec_on and not col_on and has_data:
doc_html += "<div class='col-md-12 col-sm-12'\ doc_html += """
><div class='col-md-12 col-sm-12'>" \ <div class='col-md-12 col-sm-12'>
+ section_html + html +'</div></div>' <div class='col-md-12 col-sm-12'>
if doc_html: {0} {1}
doc_html = "<div class='small'><div class='col-md-12 text-right'><a class='btn btn-default btn-xs' href='/app/Form/%s/%s'></a></div>" %(doctype, docname) + doc_html + '</div>' </div>
</div>
""".format(section_html, html)
return {'html': doc_html} return {"html": doc_html}
def update_address_links(address, method): def update_address_links(address, method):

View File

@ -7,7 +7,7 @@
} }
], ],
"charts_label": "", "charts_label": "",
"content": "[{\"type\": \"onboarding\", \"data\": {\"onboarding_name\":\"Healthcare\", \"col\": 12}}, {\"type\": \"chart\", \"data\": {\"chart_name\": \"Patient Appointments\", \"col\": 12}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Your Shortcuts\", \"level\": 4, \"col\": 12}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Patient Appointment\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Patient\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Healthcare Service Unit\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Healthcare Practitioner\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Patient History\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Dashboard\", \"col\": 4}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Reports & Masters\", \"level\": 4, \"col\": 12}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Masters\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Consultation Setup\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Consultation\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Settings\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Laboratory Setup\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Laboratory\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Rehabilitation and Physiotherapy\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Records and History\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Reports\", \"col\": 4}}]", "content": "[{\"type\": \"onboarding\", \"data\": {\"onboarding_name\":\"Healthcare\", \"col\": 12}}, {\"type\": \"chart\", \"data\": {\"chart_name\": \"Patient Appointments\", \"col\": 12}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Your Shortcuts\", \"level\": 4, \"col\": 12}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Patient Appointment\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Patient\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Healthcare Service Unit\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Healthcare Practitioner\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Patient History\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Dashboard\", \"col\": 4}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Reports & Masters\", \"level\": 4, \"col\": 12}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Masters\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Consultation Setup\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Consultation\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Facility Management\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Settings\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Inpatient\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Laboratory Setup\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Laboratory\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Rehabilitation and Physiotherapy\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Records and History\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Reports\", \"col\": 4}}]",
"creation": "2020-03-02 17:23:17.919682", "creation": "2020-03-02 17:23:17.919682",
"developer_mode_only": 0, "developer_mode_only": 0,
"disable_user_customization": 0, "disable_user_customization": 0,
@ -75,55 +75,10 @@
"onboard": 0, "onboard": 0,
"type": "Link" "type": "Link"
}, },
{
"dependencies": "",
"hidden": 0,
"is_query_report": 0,
"label": "Healthcare Service Unit Type",
"link_count": 0,
"link_to": "Healthcare Service Unit Type",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
},
{
"dependencies": "",
"hidden": 0,
"is_query_report": 0,
"label": "Healthcare Service Unit",
"link_count": 0,
"link_to": "Healthcare Service Unit",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
},
{
"dependencies": "",
"hidden": 0,
"is_query_report": 0,
"label": "Medical Code Standard",
"link_count": 0,
"link_to": "Medical Code Standard",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
},
{
"dependencies": "",
"hidden": 0,
"is_query_report": 0,
"label": "Medical Code",
"link_count": 0,
"link_to": "Medical Code",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
},
{ {
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Consultation Setup", "label": "Consultation Setup",
"link_count": 0,
"onboard": 0, "onboard": 0,
"type": "Card Break" "type": "Card Break"
}, },
@ -132,7 +87,6 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Appointment Type", "label": "Appointment Type",
"link_count": 0,
"link_to": "Appointment Type", "link_to": "Appointment Type",
"link_type": "DocType", "link_type": "DocType",
"onboard": 0, "onboard": 0,
@ -143,7 +97,6 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Clinical Procedure Template", "label": "Clinical Procedure Template",
"link_count": 0,
"link_to": "Clinical Procedure Template", "link_to": "Clinical Procedure Template",
"link_type": "DocType", "link_type": "DocType",
"onboard": 0, "onboard": 0,
@ -154,7 +107,6 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Prescription Dosage", "label": "Prescription Dosage",
"link_count": 0,
"link_to": "Prescription Dosage", "link_to": "Prescription Dosage",
"link_type": "DocType", "link_type": "DocType",
"onboard": 0, "onboard": 0,
@ -165,7 +117,6 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Prescription Duration", "label": "Prescription Duration",
"link_count": 0,
"link_to": "Prescription Duration", "link_to": "Prescription Duration",
"link_type": "DocType", "link_type": "DocType",
"onboard": 0, "onboard": 0,
@ -176,70 +127,16 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Antibiotic", "label": "Antibiotic",
"link_count": 0,
"link_to": "Antibiotic", "link_to": "Antibiotic",
"link_type": "DocType", "link_type": "DocType",
"onboard": 0, "onboard": 0,
"type": "Link" "type": "Link"
}, },
{
"hidden": 0,
"is_query_report": 0,
"label": "Consultation",
"link_count": 0,
"onboard": 0,
"type": "Card Break"
},
{
"dependencies": "",
"hidden": 0,
"is_query_report": 0,
"label": "Patient Appointment",
"link_count": 0,
"link_to": "Patient Appointment",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
},
{
"dependencies": "",
"hidden": 0,
"is_query_report": 0,
"label": "Clinical Procedure",
"link_count": 0,
"link_to": "Clinical Procedure",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
},
{
"dependencies": "",
"hidden": 0,
"is_query_report": 0,
"label": "Patient Encounter",
"link_count": 0,
"link_to": "Patient Encounter",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
},
{
"dependencies": "",
"hidden": 0,
"is_query_report": 0,
"label": "Vital Signs",
"link_count": 0,
"link_to": "Vital Signs",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
},
{ {
"dependencies": "", "dependencies": "",
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Complaint", "label": "Complaint",
"link_count": 0,
"link_to": "Complaint", "link_to": "Complaint",
"link_type": "DocType", "link_type": "DocType",
"onboard": 0, "onboard": 0,
@ -250,23 +147,124 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Diagnosis", "label": "Diagnosis",
"link_count": 0,
"link_to": "Diagnosis", "link_to": "Diagnosis",
"link_type": "DocType", "link_type": "DocType",
"onboard": 0, "onboard": 0,
"type": "Link" "type": "Link"
}, },
{
"hidden": 0,
"is_query_report": 0,
"label": "Consultation",
"onboard": 0,
"type": "Card Break"
},
{
"dependencies": "",
"hidden": 0,
"is_query_report": 0,
"label": "Patient Appointment",
"link_to": "Patient Appointment",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
},
{
"dependencies": "",
"hidden": 0,
"is_query_report": 0,
"label": "Clinical Procedure",
"link_to": "Clinical Procedure",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
},
{
"dependencies": "",
"hidden": 0,
"is_query_report": 0,
"label": "Patient Encounter",
"link_to": "Patient Encounter",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
},
{
"dependencies": "",
"hidden": 0,
"is_query_report": 0,
"label": "Vital Signs",
"link_to": "Vital Signs",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
},
{ {
"dependencies": "", "dependencies": "",
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Fee Validity", "label": "Fee Validity",
"link_count": 0,
"link_to": "Fee Validity", "link_to": "Fee Validity",
"link_type": "DocType", "link_type": "DocType",
"onboard": 0, "onboard": 0,
"type": "Link" "type": "Link"
}, },
{
"hidden": 0,
"is_query_report": 0,
"label": "Facility Management",
"link_type": "DocType",
"onboard": 0,
"type": "Card Break"
},
{
"dependencies": "",
"hidden": 0,
"is_query_report": 0,
"label": "Healthcare Service Unit Type",
"link_to": "Healthcare Service Unit Type",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
},
{
"dependencies": "",
"hidden": 0,
"is_query_report": 0,
"label": "Healthcare Service Unit",
"link_to": "Healthcare Service Unit",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
},
{
"hidden": 0,
"is_query_report": 0,
"label": "Medical Coding",
"link_type": "DocType",
"onboard": 0,
"type": "Card Break"
},
{
"dependencies": "",
"hidden": 0,
"is_query_report": 0,
"label": "Medical Code Standard",
"link_to": "Medical Code Standard",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
},
{
"dependencies": "",
"hidden": 0,
"is_query_report": 0,
"label": "Medical Code",
"link_to": "Medical Code",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
},
{ {
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
@ -338,6 +336,16 @@
"onboard": 0, "onboard": 0,
"type": "Link" "type": "Link"
}, },
{
"dependencies": "",
"hidden": 0,
"is_query_report": 0,
"label": "Dosage Form",
"link_to": "Dosage Form",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
},
{ {
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
@ -369,12 +377,36 @@
"type": "Link" "type": "Link"
}, },
{ {
"dependencies": "",
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Dosage Form", "label": "Inpatient",
"link_count": 0, "link_type": "DocType",
"link_to": "Dosage Form", "onboard": 0,
"type": "Card Break"
},
{
"hidden": 0,
"is_query_report": 0,
"label": "Inpatient Medication Order",
"link_to": "Inpatient Medication Order",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
},
{
"hidden": 0,
"is_query_report": 0,
"label": "Inpatient Record",
"link_to": "Inpatient Record",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
},
{
"hidden": 0,
"is_query_report": 0,
"label": "Inpatient Medication Entry",
"link_to": "Inpatient Medication Entry",
"link_type": "DocType", "link_type": "DocType",
"onboard": 0, "onboard": 0,
"type": "Link" "type": "Link"
@ -536,7 +568,7 @@
"type": "Link" "type": "Link"
} }
], ],
"modified": "2021-08-05 12:15:59.434612", "modified": "2021-08-30 17:37:45.316999",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Healthcare", "module": "Healthcare",
"name": "Healthcare", "name": "Healthcare",