diff --git a/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.py b/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.py index e55a1433a5..c324228467 100644 --- a/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.py +++ b/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.py @@ -100,7 +100,6 @@ class ClinicalProcedure(Document): allow_start = self.set_actual_qty() if allow_start: self.db_set('status', 'In Progress') - insert_clinical_procedure_to_medical_record(self) return 'success' return 'insufficient stock' @@ -247,21 +246,3 @@ def make_procedure(source_name, target_doc=None): }, target_doc, set_missing_values) return doc - - -def insert_clinical_procedure_to_medical_record(doc): - subject = frappe.bold(_("Clinical Procedure conducted: ")) + cstr(doc.procedure_template) + "
" - if doc.practitioner: - subject += frappe.bold(_('Healthcare Practitioner: ')) + doc.practitioner - if subject and doc.notes: - subject += '
' + doc.notes - - medical_record = frappe.new_doc('Patient Medical Record') - medical_record.patient = doc.patient - medical_record.subject = subject - medical_record.status = 'Open' - medical_record.communication_date = doc.start_date - medical_record.reference_doctype = 'Clinical Procedure' - medical_record.reference_name = doc.name - medical_record.reference_owner = doc.owner - medical_record.save(ignore_permissions=True) diff --git a/erpnext/healthcare/doctype/lab_test/lab_test.json b/erpnext/healthcare/doctype/lab_test/lab_test.json index edf1d911aa..ac61fea3ad 100644 --- a/erpnext/healthcare/doctype/lab_test/lab_test.json +++ b/erpnext/healthcare/doctype/lab_test/lab_test.json @@ -359,6 +359,7 @@ { "fieldname": "normal_test_items", "fieldtype": "Table", + "label": "Normal Test Result", "options": "Normal Test Result", "print_hide": 1 }, @@ -380,6 +381,7 @@ { "fieldname": "sensitivity_test_items", "fieldtype": "Table", + "label": "Sensitivity Test Result", "options": "Sensitivity Test Result", "print_hide": 1, "report_hide": 1 @@ -529,6 +531,7 @@ { "fieldname": "descriptive_test_items", "fieldtype": "Table", + "label": "Descriptive Test Result", "options": "Descriptive Test Result", "print_hide": 1, "report_hide": 1 @@ -549,13 +552,14 @@ { "fieldname": "organism_test_items", "fieldtype": "Table", + "label": "Organism Test Result", "options": "Organism Test Result", "print_hide": 1 } ], "is_submittable": 1, "links": [], - "modified": "2020-07-30 18:18:38.516215", + "modified": "2020-11-30 11:04:17.195848", "modified_by": "Administrator", "module": "Healthcare", "name": "Lab Test", diff --git a/erpnext/healthcare/doctype/lab_test/lab_test.py b/erpnext/healthcare/doctype/lab_test/lab_test.py index 2db7743865..4b57cd073d 100644 --- a/erpnext/healthcare/doctype/lab_test/lab_test.py +++ b/erpnext/healthcare/doctype/lab_test/lab_test.py @@ -17,11 +17,9 @@ class LabTest(Document): self.validate_result_values() self.db_set('submitted_date', getdate()) self.db_set('status', 'Completed') - insert_lab_test_to_medical_record(self) def on_cancel(self): self.db_set('status', 'Cancelled') - delete_lab_test_from_medical_record(self) self.reload() def on_update(self): @@ -330,60 +328,6 @@ def get_employee_by_user_id(user_id): return frappe.get_doc('Employee', emp_id) return None -def insert_lab_test_to_medical_record(doc): - table_row = False - subject = cstr(doc.lab_test_name) - if doc.practitioner: - subject += frappe.bold(_('Healthcare Practitioner: '))+ doc.practitioner + '
' - if doc.normal_test_items: - item = doc.normal_test_items[0] - comment = '' - if item.lab_test_comment: - comment = str(item.lab_test_comment) - table_row = frappe.bold(_('Lab Test Conducted: ')) + item.lab_test_name - - if item.lab_test_event: - table_row += frappe.bold(_('Lab Test Event: ')) + item.lab_test_event - - if item.result_value: - table_row += ' ' + frappe.bold(_('Lab Test Result: ')) + item.result_value - - if item.normal_range: - table_row += ' ' + _('Normal Range: ') + item.normal_range - table_row += ' ' + comment - - elif doc.descriptive_test_items: - item = doc.descriptive_test_items[0] - - if item.lab_test_particulars and item.result_value: - table_row = item.lab_test_particulars + ' ' + item.result_value - - elif doc.sensitivity_test_items: - item = doc.sensitivity_test_items[0] - - if item.antibiotic and item.antibiotic_sensitivity: - table_row = item.antibiotic + ' ' + item.antibiotic_sensitivity - - if table_row: - subject += '
' + table_row - if doc.lab_test_comment: - subject += '
' + cstr(doc.lab_test_comment) - - medical_record = frappe.new_doc('Patient Medical Record') - medical_record.patient = doc.patient - medical_record.subject = subject - medical_record.status = 'Open' - medical_record.communication_date = doc.result_date - medical_record.reference_doctype = 'Lab Test' - medical_record.reference_name = doc.name - medical_record.reference_owner = doc.owner - medical_record.save(ignore_permissions = True) - -def delete_lab_test_from_medical_record(self): - medical_record_id = frappe.db.sql('select name from `tabPatient Medical Record` where reference_name=%s', (self.name)) - - if medical_record_id and medical_record_id[0][0]: - frappe.delete_doc('Patient Medical Record', medical_record_id[0][0]) @frappe.whitelist() def get_lab_test_prescribed(patient): diff --git a/erpnext/healthcare/doctype/patient_appointment/test_patient_appointment.py b/erpnext/healthcare/doctype/patient_appointment/test_patient_appointment.py index 6886f31843..f7ec6f58fc 100644 --- a/erpnext/healthcare/doctype/patient_appointment/test_patient_appointment.py +++ b/erpnext/healthcare/doctype/patient_appointment/test_patient_appointment.py @@ -208,6 +208,7 @@ def create_healthcare_service_items(): item.item_name = 'Consulting Charges' item.item_group = 'Services' item.is_stock_item = 0 + item.stock_uom = 'Nos' item.save() return item.name diff --git a/erpnext/healthcare/doctype/patient_encounter/patient_encounter.json b/erpnext/healthcare/doctype/patient_encounter/patient_encounter.json index 15675f4673..b646ff9ebe 100644 --- a/erpnext/healthcare/doctype/patient_encounter/patient_encounter.json +++ b/erpnext/healthcare/doctype/patient_encounter/patient_encounter.json @@ -210,7 +210,7 @@ { "fieldname": "drug_prescription", "fieldtype": "Table", - "label": "Items", + "label": "Drug Prescription", "options": "Drug Prescription" }, { @@ -328,7 +328,7 @@ ], "is_submittable": 1, "links": [], - "modified": "2020-05-16 21:00:08.644531", + "modified": "2020-11-30 10:39:00.783119", "modified_by": "Administrator", "module": "Healthcare", "name": "Patient Encounter", diff --git a/erpnext/healthcare/doctype/patient_encounter/patient_encounter.py b/erpnext/healthcare/doctype/patient_encounter/patient_encounter.py index 87f42491fc..cc2141790f 100644 --- a/erpnext/healthcare/doctype/patient_encounter/patient_encounter.py +++ b/erpnext/healthcare/doctype/patient_encounter/patient_encounter.py @@ -17,10 +17,6 @@ class PatientEncounter(Document): def on_update(self): if self.appointment: frappe.db.set_value('Patient Appointment', self.appointment, 'status', 'Closed') - update_encounter_medical_record(self) - - def after_insert(self): - insert_encounter_to_medical_record(self) def on_submit(self): if self.therapies: @@ -33,8 +29,6 @@ class PatientEncounter(Document): if self.inpatient_record and self.drug_prescription: delete_ip_medication_order(self) - delete_medical_record(self) - def set_title(self): self.title = _('{0} with {1}').format(self.patient_name or self.patient, self.practitioner_name or self.practitioner)[:100] @@ -102,61 +96,7 @@ def create_therapy_plan(encounter): frappe.msgprint(_('Therapy Plan {0} created successfully.').format(frappe.bold(doc.name)), alert=True) -def insert_encounter_to_medical_record(doc): - subject = set_subject_field(doc) - medical_record = frappe.new_doc('Patient Medical Record') - medical_record.patient = doc.patient - medical_record.subject = subject - medical_record.status = 'Open' - medical_record.communication_date = doc.encounter_date - medical_record.reference_doctype = 'Patient Encounter' - medical_record.reference_name = doc.name - medical_record.reference_owner = doc.owner - medical_record.save(ignore_permissions=True) - - -def update_encounter_medical_record(encounter): - medical_record_id = frappe.db.exists('Patient Medical Record', {'reference_name': encounter.name}) - - if medical_record_id and medical_record_id[0][0]: - subject = set_subject_field(encounter) - frappe.db.set_value('Patient Medical Record', medical_record_id[0][0], 'subject', subject) - else: - insert_encounter_to_medical_record(encounter) - - -def delete_medical_record(encounter): - record = frappe.db.exists('Patient Medical Record', {'reference_name', encounter.name}) - if record: - frappe.delete_doc('Patient Medical Record', record, force=1) - def delete_ip_medication_order(encounter): record = frappe.db.exists('Inpatient Medication Order', {'patient_encounter': encounter.name}) if record: - frappe.delete_doc('Inpatient Medication Order', record, force=1) - - -def set_subject_field(encounter): - subject = frappe.bold(_('Healthcare Practitioner: ')) + encounter.practitioner + '
' - if encounter.symptoms: - subject += frappe.bold(_('Symptoms: ')) + '
' - for entry in encounter.symptoms: - subject += cstr(entry.complaint) + '
' - else: - subject += frappe.bold(_('No Symptoms')) + '
' - - if encounter.diagnosis: - subject += frappe.bold(_('Diagnosis: ')) + '
' - for entry in encounter.diagnosis: - subject += cstr(entry.diagnosis) + '
' - else: - subject += frappe.bold(_('No Diagnosis')) + '
' - - if encounter.drug_prescription: - subject += '
' + _('Drug(s) Prescribed.') - if encounter.lab_test_prescription: - subject += '
' + _('Test(s) Prescribed.') - if encounter.procedure_prescription: - subject += '
' + _('Procedure(s) Prescribed.') - - return subject + frappe.delete_doc('Inpatient Medication Order', record, force=1) \ No newline at end of file diff --git a/erpnext/healthcare/doctype/patient_history_custom_document_type/__init__.py b/erpnext/healthcare/doctype/patient_history_custom_document_type/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/healthcare/doctype/patient_history_custom_document_type/patient_history_custom_document_type.json b/erpnext/healthcare/doctype/patient_history_custom_document_type/patient_history_custom_document_type.json new file mode 100644 index 0000000000..3025c7b06d --- /dev/null +++ b/erpnext/healthcare/doctype/patient_history_custom_document_type/patient_history_custom_document_type.json @@ -0,0 +1,55 @@ +{ + "actions": [], + "creation": "2020-11-25 13:40:23.054469", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "document_type", + "date_fieldname", + "add_edit_fields", + "selected_fields" + ], + "fields": [ + { + "fieldname": "document_type", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Document Type", + "options": "DocType", + "reqd": 1 + }, + { + "fieldname": "selected_fields", + "fieldtype": "Code", + "label": "Selected Fields", + "read_only": 1 + }, + { + "fieldname": "add_edit_fields", + "fieldtype": "Button", + "in_list_view": 1, + "label": "Add / Edit Fields" + }, + { + "fieldname": "date_fieldname", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Date Fieldname", + "reqd": 1 + } + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2020-11-30 13:54:37.474671", + "modified_by": "Administrator", + "module": "Healthcare", + "name": "Patient History Custom Document Type", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/healthcare/doctype/patient_history_custom_document_type/patient_history_custom_document_type.py b/erpnext/healthcare/doctype/patient_history_custom_document_type/patient_history_custom_document_type.py new file mode 100644 index 0000000000..f0a1f929f4 --- /dev/null +++ b/erpnext/healthcare/doctype/patient_history_custom_document_type/patient_history_custom_document_type.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +# import frappe +from frappe.model.document import Document + +class PatientHistoryCustomDocumentType(Document): + pass diff --git a/erpnext/healthcare/doctype/patient_history_settings/__init__.py b/erpnext/healthcare/doctype/patient_history_settings/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/healthcare/doctype/patient_history_settings/patient_history_settings.js b/erpnext/healthcare/doctype/patient_history_settings/patient_history_settings.js new file mode 100644 index 0000000000..453da6a12b --- /dev/null +++ b/erpnext/healthcare/doctype/patient_history_settings/patient_history_settings.js @@ -0,0 +1,133 @@ +// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Patient History Settings', { + refresh: function(frm) { + frm.set_query('document_type', 'custom_doctypes', () => { + return { + filters: { + custom: 1, + is_submittable: 1, + module: 'Healthcare', + } + }; + }); + }, + + field_selector: function(frm, doc, standard=1) { + let document_fields = []; + if (doc.selected_fields) + document_fields = (JSON.parse(doc.selected_fields)).map(f => f.fieldname); + + frm.call({ + method: 'get_doctype_fields', + doc: frm.doc, + args: { + document_type: doc.document_type, + fields: document_fields + }, + freeze: true, + callback: function(r) { + if (r.message) { + let doctype = 'Patient History Custom Document Type'; + if (standard) + doctype = 'Patient History Standard Document Type'; + + frm.events.show_field_selector_dialog(frm, doc, doctype, r.message); + } + } + }); + }, + + show_field_selector_dialog: function(frm, doc, doctype, doc_fields) { + let d = new frappe.ui.Dialog({ + title: __('{0} Fields', [__(doc.document_type)]), + fields: [ + { + label: __('Select Fields'), + fieldtype: 'MultiCheck', + fieldname: 'fields', + options: doc_fields, + columns: 2 + } + ] + }); + + d.$body.prepend(` + ` + ); + + frappe.utils.setup_search(d.$body, '.unit-checkbox', '.label-area'); + + d.set_primary_action(__('Save'), () => { + let values = d.get_values().fields; + + let selected_fields = []; + + frappe.model.with_doctype(doc.document_type, function() { + for (let idx in values) { + let value = values[idx]; + + let field = frappe.get_meta(doc.document_type).fields.filter((df) => df.fieldname == value)[0]; + if (field) { + selected_fields.push({ + label: field.label, + fieldname: field.fieldname, + fieldtype: field.fieldtype + }); + } + } + + d.refresh(); + frappe.model.set_value(doctype, doc.name, 'selected_fields', JSON.stringify(selected_fields)); + }); + + d.hide(); + }); + + d.show(); + }, + + get_date_field_for_dt: function(frm, row) { + frm.call({ + method: 'get_date_field_for_dt', + doc: frm.doc, + args: { + document_type: row.document_type + }, + callback: function(data) { + if (data.message) { + frappe.model.set_value('Patient History Custom Document Type', + row.name, 'date_fieldname', data.message); + } + } + }); + } +}); + +frappe.ui.form.on('Patient History Custom Document Type', { + document_type: function(frm, cdt, cdn) { + let row = locals[cdt][cdn]; + if (row.document_type) { + frm.events.get_date_field_for_dt(frm, row); + } + }, + + add_edit_fields: function(frm, cdt, cdn) { + let row = locals[cdt][cdn]; + if (row.document_type) { + frm.events.field_selector(frm, row, 0); + } + } +}); + +frappe.ui.form.on('Patient History Standard Document Type', { + add_edit_fields: function(frm, cdt, cdn) { + let row = locals[cdt][cdn]; + if (row.document_type) { + frm.events.field_selector(frm, row); + } + } +}); diff --git a/erpnext/healthcare/doctype/patient_history_settings/patient_history_settings.json b/erpnext/healthcare/doctype/patient_history_settings/patient_history_settings.json new file mode 100644 index 0000000000..143e2c91eb --- /dev/null +++ b/erpnext/healthcare/doctype/patient_history_settings/patient_history_settings.json @@ -0,0 +1,55 @@ +{ + "actions": [], + "creation": "2020-11-25 13:41:37.675518", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "standard_doctypes", + "section_break_2", + "custom_doctypes" + ], + "fields": [ + { + "fieldname": "section_break_2", + "fieldtype": "Section Break" + }, + { + "fieldname": "custom_doctypes", + "fieldtype": "Table", + "label": "Custom Document Types", + "options": "Patient History Custom Document Type" + }, + { + "fieldname": "standard_doctypes", + "fieldtype": "Table", + "label": "Standard Document Types", + "options": "Patient History Standard Document Type", + "read_only": 1 + } + ], + "index_web_pages_for_search": 1, + "issingle": 1, + "links": [], + "modified": "2020-11-25 13:43:38.511771", + "modified_by": "Administrator", + "module": "Healthcare", + "name": "Patient History Settings", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/healthcare/doctype/patient_history_settings/patient_history_settings.py b/erpnext/healthcare/doctype/patient_history_settings/patient_history_settings.py new file mode 100644 index 0000000000..2e8c994c3d --- /dev/null +++ b/erpnext/healthcare/doctype/patient_history_settings/patient_history_settings.py @@ -0,0 +1,188 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +import json +from frappe import _ +from frappe.utils import cstr, cint +from frappe.model.document import Document +from erpnext.healthcare.page.patient_history.patient_history import get_patient_history_doctypes + +class PatientHistorySettings(Document): + def validate(self): + self.validate_submittable_doctypes() + self.validate_date_fieldnames() + + def validate_submittable_doctypes(self): + for entry in self.custom_doctypes: + if not cint(frappe.db.get_value('DocType', entry.document_type, 'is_submittable')): + msg = _('Row #{0}: Document Type {1} is not submittable. ').format( + entry.idx, frappe.bold(entry.document_type)) + msg += _('Patient Medical Record can only be created for submittable document types.') + frappe.throw(msg) + + def validate_date_fieldnames(self): + for entry in self.custom_doctypes: + field = frappe.get_meta(entry.document_type).get_field(entry.date_fieldname) + if not field: + frappe.throw(_('Row #{0}: No such Field named {1} found in the Document Type {2}.').format( + entry.idx, frappe.bold(entry.date_fieldname), frappe.bold(entry.document_type))) + + if field.fieldtype not in ['Date', 'Datetime']: + frappe.throw(_('Row #{0}: Field {1} in Document Type {2} is not a Date / Datetime field.').format( + entry.idx, frappe.bold(entry.date_fieldname), frappe.bold(entry.document_type))) + + def get_doctype_fields(self, document_type, fields): + multicheck_fields = [] + doc_fields = frappe.get_meta(document_type).fields + + for field in doc_fields: + if field.fieldtype not in frappe.model.no_value_fields or \ + field.fieldtype in frappe.model.table_fields and not field.hidden: + multicheck_fields.append({ + 'label': field.label, + 'value': field.fieldname, + 'checked': 1 if field.fieldname in fields else 0 + }) + + return multicheck_fields + + def get_date_field_for_dt(self, document_type): + meta = frappe.get_meta(document_type) + date_fields = meta.get('fields', { + 'fieldtype': ['in', ['Date', 'Datetime']] + }) + + if date_fields: + return date_fields[0].get('fieldname') + +def create_medical_record(doc, method=None): + medical_record_required = validate_medical_record_required(doc) + if not medical_record_required: + return + + if frappe.db.exists('Patient Medical Record', { 'reference_name': doc.name }): + return + + subject = set_subject_field(doc) + date_field = get_date_field(doc.doctype) + medical_record = frappe.new_doc('Patient Medical Record') + medical_record.patient = doc.patient + medical_record.subject = subject + medical_record.status = 'Open' + medical_record.communication_date = doc.get(date_field) + medical_record.reference_doctype = doc.doctype + medical_record.reference_name = doc.name + medical_record.reference_owner = doc.owner + medical_record.save(ignore_permissions=True) + + +def update_medical_record(doc, method=None): + medical_record_required = validate_medical_record_required(doc) + if not medical_record_required: + return + + medical_record_id = frappe.db.exists('Patient Medical Record', { 'reference_name': doc.name }) + + if medical_record_id: + subject = set_subject_field(doc) + frappe.db.set_value('Patient Medical Record', medical_record_id[0][0], 'subject', subject) + else: + create_medical_record(doc) + + +def delete_medical_record(doc, method=None): + medical_record_required = validate_medical_record_required(doc) + if not medical_record_required: + return + + record = frappe.db.exists('Patient Medical Record', { 'reference_name': doc.name }) + if record: + frappe.delete_doc('Patient Medical Record', record, force=1) + + +def set_subject_field(doc): + from frappe.utils.formatters import format_value + + meta = frappe.get_meta(doc.doctype) + subject = '' + patient_history_fields = get_patient_history_fields(doc) + + for entry in patient_history_fields: + fieldname = entry.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)) + subject += frappe.bold(_(entry.get('label')) + ': ') + '
' + cstr(formatted_value) + '
' + + else: + if doc.get(fieldname): + formatted_value = format_value(doc.get(fieldname), meta.get_field(fieldname), doc) + subject += frappe.bold(_(entry.get('label')) + ': ') + cstr(formatted_value) + '
' + + return subject + + +def get_date_field(doctype): + dt = get_patient_history_config_dt(doctype) + + return frappe.db.get_value(dt, { 'document_type': doctype }, 'date_fieldname') + + +def get_patient_history_fields(doc): + dt = get_patient_history_config_dt(doc.doctype) + patient_history_fields = frappe.db.get_value(dt, { 'document_type': doc.doctype }, 'selected_fields') + + if patient_history_fields: + return json.loads(patient_history_fields) + + +def get_formatted_value_for_table_field(items, df): + child_meta = frappe.get_meta(df.options) + + table_head = '' + table_row = '' + html = '' + create_head = True + for item in items: + table_row += '' + for cdf in child_meta.fields: + if cdf.in_list_view: + if create_head: + table_head += '' + cdf.label + '' + if item.get(cdf.fieldname): + table_row += '' + str(item.get(cdf.fieldname)) + '' + else: + table_row += '' + create_head = False + table_row += '' + + html += "" + table_head + table_row + "
" + + return html + + +def get_patient_history_config_dt(doctype): + if frappe.db.get_value('DocType', doctype, 'custom'): + return 'Patient History Custom Document Type' + else: + return 'Patient History Standard Document Type' + + +def validate_medical_record_required(doc): + if frappe.flags.in_patch or frappe.flags.in_install or frappe.flags.in_setup_wizard \ + or get_module(doc) != 'Healthcare': + return False + + if doc.doctype not in get_patient_history_doctypes(): + return False + + return True + +def get_module(doc): + module = doc.meta.module + if not module: + module = frappe.db.get_value('DocType', doc.doctype, 'module') + + return module \ No newline at end of file diff --git a/erpnext/healthcare/doctype/patient_history_settings/test_patient_history_settings.py b/erpnext/healthcare/doctype/patient_history_settings/test_patient_history_settings.py new file mode 100644 index 0000000000..c93b788aed --- /dev/null +++ b/erpnext/healthcare/doctype/patient_history_settings/test_patient_history_settings.py @@ -0,0 +1,104 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt +from __future__ import unicode_literals + +import frappe +import unittest +import json +from frappe.utils import getdate +from erpnext.healthcare.doctype.patient_appointment.test_patient_appointment import create_patient + +class TestPatientHistorySettings(unittest.TestCase): + def setUp(self): + dt = create_custom_doctype() + settings = frappe.get_single("Patient History Settings") + settings.append("custom_doctypes", { + "document_type": dt.name, + "date_fieldname": "date", + "selected_fields": json.dumps([{ + "label": "Date", + "fieldname": "date", + "fieldtype": "Date" + }, + { + "label": "Rating", + "fieldname": "rating", + "fieldtype": "Rating" + }, + { + "label": "Feedback", + "fieldname": "feedback", + "fieldtype": "Small Text" + }]) + }) + settings.save() + + def test_custom_doctype_medical_record(self): + # tests for medical record creation of standard doctypes in test_patient_medical_record.py + patient = create_patient() + doc = create_doc(patient) + + # check for medical record + medical_rec = frappe.db.exists("Patient Medical Record", {"status": "Open", "reference_name": doc.name}) + self.assertTrue(medical_rec) + + medical_rec = frappe.get_doc("Patient Medical Record", medical_rec) + expected_subject = "Date: {0}
Rating: 3
Feedback: Test Patient History Settings
".format( + frappe.utils.format_date(getdate())) + self.assertEqual(medical_rec.subject, expected_subject) + self.assertEqual(medical_rec.patient, patient) + self.assertEqual(medical_rec.communication_date, getdate()) + + +def create_custom_doctype(): + if not frappe.db.exists("DocType", "Test Patient Feedback"): + doc = frappe.get_doc({ + "doctype": "DocType", + "module": "Healthcare", + "custom": 1, + "is_submittable": 1, + "fields": [{ + "label": "Date", + "fieldname": "date", + "fieldtype": "Date" + }, + { + "label": "Patient", + "fieldname": "patient", + "fieldtype": "Link", + "options": "Patient" + }, + { + "label": "Rating", + "fieldname": "rating", + "fieldtype": "Rating" + }, + { + "label": "Feedback", + "fieldname": "feedback", + "fieldtype": "Small Text" + }], + "permissions": [{ + "role": "System Manager", + "read": 1 + }], + "name": "Test Patient Feedback", + }) + doc.insert() + return doc + else: + return frappe.get_doc("DocType", "Test Patient Feedback") + + +def create_doc(patient): + doc = frappe.get_doc({ + "doctype": "Test Patient Feedback", + "patient": patient, + "date": getdate(), + "rating": 3, + "feedback": "Test Patient History Settings" + }).insert() + doc.submit() + + return doc \ No newline at end of file diff --git a/erpnext/healthcare/doctype/patient_history_standard_document_type/__init__.py b/erpnext/healthcare/doctype/patient_history_standard_document_type/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/healthcare/doctype/patient_history_standard_document_type/patient_history_standard_document_type.json b/erpnext/healthcare/doctype/patient_history_standard_document_type/patient_history_standard_document_type.json new file mode 100644 index 0000000000..b43099c4ea --- /dev/null +++ b/erpnext/healthcare/doctype/patient_history_standard_document_type/patient_history_standard_document_type.json @@ -0,0 +1,57 @@ +{ + "actions": [], + "creation": "2020-11-25 13:39:36.014814", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "document_type", + "date_fieldname", + "add_edit_fields", + "selected_fields" + ], + "fields": [ + { + "fieldname": "document_type", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Document Type", + "options": "DocType", + "read_only": 1, + "reqd": 1 + }, + { + "fieldname": "selected_fields", + "fieldtype": "Code", + "label": "Selected Fields", + "read_only": 1 + }, + { + "fieldname": "add_edit_fields", + "fieldtype": "Button", + "in_list_view": 1, + "label": "Add / Edit Fields" + }, + { + "fieldname": "date_fieldname", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Date Fieldname", + "read_only": 1, + "reqd": 1 + } + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2020-11-30 13:54:56.773325", + "modified_by": "Administrator", + "module": "Healthcare", + "name": "Patient History Standard Document Type", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/healthcare/doctype/patient_history_standard_document_type/patient_history_standard_document_type.py b/erpnext/healthcare/doctype/patient_history_standard_document_type/patient_history_standard_document_type.py new file mode 100644 index 0000000000..2d94911855 --- /dev/null +++ b/erpnext/healthcare/doctype/patient_history_standard_document_type/patient_history_standard_document_type.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +# import frappe +from frappe.model.document import Document + +class PatientHistoryStandardDocumentType(Document): + pass diff --git a/erpnext/healthcare/doctype/patient_medical_record/test_patient_medical_record.py b/erpnext/healthcare/doctype/patient_medical_record/test_patient_medical_record.py index 419d956425..c1d9872a01 100644 --- a/erpnext/healthcare/doctype/patient_medical_record/test_patient_medical_record.py +++ b/erpnext/healthcare/doctype/patient_medical_record/test_patient_medical_record.py @@ -18,6 +18,7 @@ class TestPatientMedicalRecord(unittest.TestCase): patient, medical_department, practitioner = create_healthcare_docs() appointment = create_appointment(patient, practitioner, nowdate(), invoice=1) encounter = create_encounter(appointment) + # check for encounter medical_rec = frappe.db.exists('Patient Medical Record', {'status': 'Open', 'reference_name': encounter.name}) self.assertTrue(medical_rec) diff --git a/erpnext/healthcare/doctype/therapy_session/therapy_session.py b/erpnext/healthcare/doctype/therapy_session/therapy_session.py index c00054421d..51f267f949 100644 --- a/erpnext/healthcare/doctype/therapy_session/therapy_session.py +++ b/erpnext/healthcare/doctype/therapy_session/therapy_session.py @@ -41,7 +41,6 @@ class TherapySession(Document): def on_submit(self): self.update_sessions_count_in_therapy_plan() - insert_session_medical_record(self) def on_update(self): if self.appointment: @@ -142,23 +141,3 @@ def get_therapy_item(therapy, item): item.reference_dt = 'Therapy Session' item.reference_dn = therapy.name return item - - -def insert_session_medical_record(doc): - subject = frappe.bold(_('Therapy: ')) + cstr(doc.therapy_type) + '
' - if doc.therapy_plan: - subject += frappe.bold(_('Therapy Plan: ')) + cstr(doc.therapy_plan) + '
' - if doc.practitioner: - subject += frappe.bold(_('Healthcare Practitioner: ')) + doc.practitioner - subject += frappe.bold(_('Total Counts Targeted: ')) + cstr(doc.total_counts_targeted) + '
' - subject += frappe.bold(_('Total Counts Completed: ')) + cstr(doc.total_counts_completed) + '
' - - medical_record = frappe.new_doc('Patient Medical Record') - medical_record.patient = doc.patient - medical_record.subject = subject - medical_record.status = 'Open' - medical_record.communication_date = doc.start_date - medical_record.reference_doctype = 'Therapy Session' - medical_record.reference_name = doc.name - medical_record.reference_owner = doc.owner - medical_record.save(ignore_permissions=True) \ No newline at end of file diff --git a/erpnext/healthcare/doctype/vital_signs/vital_signs.py b/erpnext/healthcare/doctype/vital_signs/vital_signs.py index 69d81ff4b0..35c823d739 100644 --- a/erpnext/healthcare/doctype/vital_signs/vital_signs.py +++ b/erpnext/healthcare/doctype/vital_signs/vital_signs.py @@ -12,47 +12,7 @@ class VitalSigns(Document): def validate(self): self.set_title() - def on_submit(self): - insert_vital_signs_to_medical_record(self) - - def on_cancel(self): - delete_vital_signs_from_medical_record(self) - def set_title(self): self.title = _('{0} on {1}').format(self.patient_name or self.patient, frappe.utils.format_date(self.signs_date))[:100] -def insert_vital_signs_to_medical_record(doc): - subject = set_subject_field(doc) - medical_record = frappe.new_doc('Patient Medical Record') - medical_record.patient = doc.patient - medical_record.subject = subject - medical_record.status = 'Open' - medical_record.communication_date = doc.signs_date - medical_record.reference_doctype = 'Vital Signs' - medical_record.reference_name = doc.name - medical_record.reference_owner = doc.owner - medical_record.flags.ignore_mandatory = True - medical_record.save(ignore_permissions=True) - -def delete_vital_signs_from_medical_record(doc): - medical_record = frappe.db.get_value('Patient Medical Record', {'reference_name': doc.name}) - if medical_record: - frappe.delete_doc('Patient Medical Record', medical_record) - -def set_subject_field(doc): - subject = '' - if doc.temperature: - subject += frappe.bold(_('Temperature: ')) + cstr(doc.temperature) + '
' - if doc.pulse: - subject += frappe.bold(_('Pulse: ')) + cstr(doc.pulse) + '
' - if doc.respiratory_rate: - subject += frappe.bold(_('Respiratory Rate: ')) + cstr(doc.respiratory_rate) + '
' - if doc.bp: - subject += frappe.bold(_('BP: ')) + cstr(doc.bp) + '
' - if doc.bmi: - subject += frappe.bold(_('BMI: ')) + cstr(doc.bmi) + '
' - if doc.nutrition_note: - subject += frappe.bold(_('Note: ')) + cstr(doc.nutrition_note) + '
' - - return subject diff --git a/erpnext/healthcare/page/patient_history/patient_history.css b/erpnext/healthcare/page/patient_history/patient_history.css index 865d6abee0..1bb589164e 100644 --- a/erpnext/healthcare/page/patient_history/patient_history.css +++ b/erpnext/healthcare/page/patient_history/patient_history.css @@ -109,6 +109,11 @@ padding-right: 0px; } +.patient-history-filter { + margin-left: 35px; + width: 25%; +} + #page-medical_record .plot-wrapper { padding: 20px 15px; border-bottom: 1px solid #d1d8dd; diff --git a/erpnext/healthcare/page/patient_history/patient_history.html b/erpnext/healthcare/page/patient_history/patient_history.html index 7a9446dffd..be486c62d1 100644 --- a/erpnext/healthcare/page/patient_history/patient_history.html +++ b/erpnext/healthcare/page/patient_history/patient_history.html @@ -1,6 +1,5 @@
-

{%= __("Select Patient") %}

@@ -11,6 +10,13 @@
+ +
+
+
+
+
+
diff --git a/erpnext/healthcare/page/patient_history/patient_history.js b/erpnext/healthcare/page/patient_history/patient_history.js index fe5b7bc488..54343aae44 100644 --- a/erpnext/healthcare/page/patient_history/patient_history.js +++ b/erpnext/healthcare/page/patient_history/patient_history.js @@ -1,141 +1,225 @@ -frappe.provide("frappe.patient_history"); +frappe.provide('frappe.patient_history'); frappe.pages['patient_history'].on_page_load = function(wrapper) { - var me = this; - var page = frappe.ui.make_app_page({ + let me = this; + let page = frappe.ui.make_app_page({ parent: wrapper, title: 'Patient History', single_column: true }); - frappe.breadcrumbs.add("Healthcare"); + frappe.breadcrumbs.add('Healthcare'); let pid = ''; - page.main.html(frappe.render_template("patient_history", {})); - var patient = frappe.ui.form.make_control({ - parent: page.main.find(".patient"), + page.main.html(frappe.render_template('patient_history', {})); + 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", - change: function(){ - if(pid != patient.get_value() && patient.get_value()){ + 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(""); - get_documents(patient.get_value(), me); - show_patient_info(patient.get_value(), me); - show_patient_vital_charts(patient.get_value(), me, "bp", "mmHg", "Blood Pressure"); + 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.get_value(); + pid = patient_id; } }, - only_input: true, }); patient.refresh(); - if (frappe.route_options){ + if (frappe.route_options) { patient.set_value(frappe.route_options.patient); } - this.page.main.on("click", ".btn-show-chart", function() { - var btn_show_id = $(this).attr("data-show-chart-id"), pts = $(this).attr("data-pts"); - var title = $(this).attr("data-title"); + 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() { - var 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"]; + 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", + 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+"\ -
"); - me.page.main.find("."+docname).parent().find('.document-html').show(); - me.page.main.find("."+docname).parent().find('.document-html').attr('data-fetched', "1"); + if (r.message) { + me.page.main.find('.' + docname).hide(); + + me.page.main.find('.' + docname).parent().find('.document-html').html( + `${r.message.html} +
+ + +
+ `); + + me.page.main.find('.' + docname).parent().find('.document-html').show(); + me.page.main.find('.' + docname).parent().find('.document-html').attr('data-fetched', '1'); } - }, - freeze: true + } }); } } }); - this.page.main.on("click", ".btn-less", function() { - var 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(); + 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(){ + me.page.main.on('click', '.btn-get-records', function() { get_documents(patient.get_value(), me); }); }; -var get_documents = function(patient, me){ +let setup_filters = function(patient, me) { + $('.doctype-filter').empty(); + 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'), + input_class: 'input-xs', + change: () => { + me.start = 0; + me.page.main.find('.patient_documents_list').html(''); + get_documents(patient, me, doctype_filter.get_value(), date_range_field.get_value()); + }, + get_data: () => { + return document_types.map(document_type => { + return { + description: document_type, + value: document_type + }; + }); + }, + } + }); + 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(''); + get_documents(patient, me, doctype_filter.get_value(), selected_date_range); + } + } + }, + parent: $('.date-filter') + }); + date_range_field.refresh(); + }); +}; + +let get_documents = function(patient, me, document_types="", selected_date_range="") { + let filters = { + name: patient, + start: me.start, + page_length: 20 + }; + if (document_types) + filters['document_types'] = document_types; + if (selected_date_range) + filters['date_range'] = selected_date_range; + frappe.call({ - "method": "erpnext.healthcare.page.patient_history.patient_history.get_feed", - args: { - name: patient, - start: me.start, - page_length: 20 - }, - callback: function (r) { - var data = r.message; - if(data.length){ + 'method': 'erpnext.healthcare.page.patient_history.patient_history.get_feed', + args: filters, + callback: function(r) { + let data = r.message; + if (data.length) { add_to_records(me, data); - }else{ - me.page.main.find(".patient_documents_list").append("


No more records..

"); - me.page.main.find(".btn-get-records").hide(); + } else { + me.page.main.find('.patient_documents_list').append(` +
+

${__('No more records..')}

+
`); + me.page.main.find('.btn-get-records').hide(); } } }); }; -var add_to_records = function(me, data){ - var details = ""; - me.page.main.find(".patient_documents_list").append(details); + + details += ''; + me.page.main.find('.patient_documents_list').append(details); me.start += data.length; - if(data.length===20){ + + if (data.length === 20) { me.page.main.find(".btn-get-records").show(); - }else{ + } else { me.page.main.find(".btn-get-records").hide(); - me.page.main.find(".patient_documents_list").append("


No more records..

"); + me.page.main.find(".patient_documents_list").append(` +
+

${__('No more records..')}

+
`); } }; -var add_date_separator = function(data) { - var date = frappe.datetime.str_to_obj(data.creation); +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)); - var diff = frappe.datetime.get_day_diff(frappe.datetime.get_today(), frappe.datetime.obj_to_str(date)); - if(diff < 1) { - var pdate = 'Today'; - } else if(diff < 2) { - pdate = 'Yesterday'; + if (diff < 1) { + pdate = __('Today'); + } else if (diff < 2) { + pdate = __('Yesterday'); } else { - pdate = frappe.datetime.global_date_format(date); + pdate = __('on ') + frappe.datetime.global_date_format(date); } data.date_sep = pdate; return data; }; -var show_patient_info = function(patient, me){ +let show_patient_info = function(patient, me) { frappe.call({ - "method": "erpnext.healthcare.doctype.patient.patient.get_patient_detail", + 'method': 'erpnext.healthcare.doctype.patient.patient.get_patient_detail', args: { patient: patient }, - callback: function (r) { - var data = r.message; - var details = ""; - if(data.image){ - details += "
"; + callback: function(r) { + let data = r.message; + let details = ''; + if (data.image) { + details += `
`; } - details += "" + data.patient_name +"
" + data.sex; - if(data.email) details += "
" + data.email; - if(data.mobile) details += "
" + data.mobile; - if(data.occupation) details += "

Occupation : " + data.occupation; - if(data.blood_group) details += "
Blood group : " + data.blood_group; - if(data.allergies) details += "

Allergies : "+ data.allergies.replace("\n", "
"); - if(data.medication) details += "
Medication : "+ data.medication.replace("\n", "
"); - if(data.alcohol_current_use) details += "

Alcohol use : "+ data.alcohol_current_use; - if(data.alcohol_past_use) details += "
Alcohol past use : "+ data.alcohol_past_use; - if(data.tobacco_current_use) details += "
Tobacco use : "+ data.tobacco_current_use; - if(data.tobacco_past_use) details += "
Tobacco past use : "+ data.tobacco_past_use; - if(data.medical_history) details += "

Medical history : "+ data.medical_history.replace("\n", "
"); - if(data.surgical_history) details += "
Surgical history : "+ data.surgical_history.replace("\n", "
"); - if(data.surrounding_factors) details += "

Occupational hazards : "+ data.surrounding_factors.replace("\n", "
"); - if(data.other_risk_factors) details += "
Other risk factors : " + data.other_risk_factors.replace("\n", "
"); - if(data.patient_details) details += "

More info : " + data.patient_details.replace("\n", "
"); - if(details){ - details = "
" + details + "
"; + details += ` ${data.patient_name}
${data.sex}`; + if (data.email) details += `
${data.email}`; + if (data.mobile) details += `
${data.mobile}`; + if (data.occupation) details += `

${__('Occupation')} : ${data.occupation}`; + if (data.blood_group) details += `
${__('Blood Group')} : ${data.blood_group}`; + if (data.allergies) details += `

${__('Allerigies')} : ${data.allergies.replace("\n", ", ")}`; + if (data.medication) details += `
${__('Medication')} : ${data.medication.replace("\n", ", ")}`; + if (data.alcohol_current_use) details += `

${__('Alcohol use')} : ${data.alcohol_current_use}`; + if (data.alcohol_past_use) details += `
${__('Alcohol past use')} : ${data.alcohol_past_use}`; + if (data.tobacco_current_use) details += `
${__('Tobacco use')} : ${data.tobacco_current_use}`; + if (data.tobacco_past_use) details += `
${__('Tobacco past use')} : ${data.tobacco_past_use}`; + if (data.medical_history) details += `

${__('Medical history')} : ${data.medical_history.replace("\n", ", ")}`; + if (data.surgical_history) details += `
${__('Surgical history')} : ${data.surgical_history.replace("\n", ", ")}`; + if (data.surrounding_factors) details += `

${__('Occupational hazards')} : ${data.surrounding_factors.replace("\n", ", ")}`; + if (data.other_risk_factors) details += `
${__('Other risk factors')} : ${data.other_risk_factors.replace("\n", ", ")}`; + if (data.patient_details) details += `

${__('More info')} : ${data.patient_details.replace("\n", ", ")}`; + + if (details) { + details = `
` + details + `
`; } - me.page.main.find(".patient_details").html(details); + me.page.main.find('.patient_details').html(details); } }); }; -var show_patient_vital_charts = function(patient, me, btn_show_id, pts, title) { +let show_patient_vital_charts = function(patient, me, btn_show_id, pts, title) { frappe.call({ - method: "erpnext.healthcare.utils.get_patient_vitals", + method: 'erpnext.healthcare.utils.get_patient_vitals', args:{ patient: patient }, callback: function(r) { - if (r.message){ - var show_chart_btns_html = "
Blood Pressure\ - Respiratory/Pulse Rate\ - Temperature\ - BMI
"; - me.page.main.find(".show_chart_btns").html(show_chart_btns_html); - var data = r.message; + if (r.message) { + let show_chart_btns_html = ` +
+ + ${__('Blood Pressure')} + + + ${__('Respiratory/Pulse Rate')} + + + ${__('Temperature')} + + + ${__('BMI')} + +
`; + + 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(var i=0; i d + ' ' + pts, } }); - }else{ - me.page.main.find(".patient_vital_charts").html(""); - me.page.main.find(".show_chart_btns").html(""); + 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(); } } }); diff --git a/erpnext/healthcare/page/patient_history/patient_history.py b/erpnext/healthcare/page/patient_history/patient_history.py index 772aa4ef5e..4cdfd64a69 100644 --- a/erpnext/healthcare/page/patient_history/patient_history.py +++ b/erpnext/healthcare/page/patient_history/patient_history.py @@ -4,36 +4,70 @@ from __future__ import unicode_literals import frappe +import json from frappe.utils import cint from erpnext.healthcare.utils import render_docs_as_html @frappe.whitelist() -def get_feed(name, start=0, page_length=20): +def get_feed(name, document_types=None, date_range=None, start=0, page_length=20): """get feed""" - result = frappe.db.sql("""select name, owner, creation, - reference_doctype, reference_name, subject - from `tabPatient Medical Record` - where patient=%(patient)s - order by creation desc - limit %(start)s, %(page_length)s""", - { - "patient": name, - "start": cint(start), - "page_length": cint(page_length) - }, as_dict=True) + filters = get_filters(name, document_types, date_range) + + result = frappe.db.get_all('Patient Medical Record', + fields=['name', 'owner', 'communication_date', + 'reference_doctype', 'reference_name', 'subject'], + filters=filters, + order_by='communication_date DESC', + limit=cint(page_length), + start=cint(start) + ) + return result + +def get_filters(name, document_types=None, date_range=None): + filters = {'patient': name} + if document_types: + document_types = json.loads(document_types) + if len(document_types): + filters['reference_doctype'] = ['IN', document_types] + + if date_range: + try: + date_range = json.loads(date_range) + if date_range: + filters['communication_date'] = ['between', [date_range[0], date_range[1]]] + except json.decoder.JSONDecodeError: + pass + + return filters + + @frappe.whitelist() def get_feed_for_dt(doctype, docname): """get feed""" - result = frappe.db.sql("""select name, owner, modified, creation, - reference_doctype, reference_name, subject - from `tabPatient Medical Record` - where reference_name=%(docname)s and reference_doctype=%(doctype)s - order by creation desc""", - { - "docname": docname, - "doctype": doctype - }, as_dict=True) + result = frappe.db.get_all('Patient Medical Record', + fields=['name', 'owner', 'communication_date', + 'reference_doctype', 'reference_name', 'subject'], + filters={ + 'reference_doctype': doctype, + 'reference_name': docname + }, + order_by='communication_date DESC' + ) return result + + +@frappe.whitelist() +def get_patient_history_doctypes(): + document_types = [] + settings = frappe.get_single("Patient History Settings") + + for entry in settings.standard_doctypes: + document_types.append(entry.document_type) + + for entry in settings.custom_doctypes: + document_types.append(entry.document_type) + + return document_types diff --git a/erpnext/healthcare/setup.py b/erpnext/healthcare/setup.py index 06840801d3..bf4df7e4c8 100644 --- a/erpnext/healthcare/setup.py +++ b/erpnext/healthcare/setup.py @@ -16,6 +16,7 @@ def setup_healthcare(): create_healthcare_item_groups() create_sensitivity() add_healthcare_service_unit_tree_root() + setup_patient_history_settings() def create_medical_departments(): departments = [ @@ -213,3 +214,82 @@ def get_company(): if company: return company[0].name return None + +def setup_patient_history_settings(): + import json + + settings = frappe.get_single('Patient History Settings') + configuration = get_patient_history_config() + for dt, config in configuration.items(): + settings.append("standard_doctypes", { + "document_type": dt, + "date_fieldname": config[0], + "selected_fields": json.dumps(config[1]) + }) + settings.save() + +def get_patient_history_config(): + return { + "Patient Encounter": ("encounter_date", [ + {"label": "Healthcare Practitioner", "fieldname": "practitioner", "fieldtype": "Link"}, + {"label": "Symptoms", "fieldname": "symptoms", "fieldtype": "Table Multiselect"}, + {"label": "Diagnosis", "fieldname": "diagnosis", "fieldtype": "Table Multiselect"}, + {"label": "Drug Prescription", "fieldname": "drug_prescription", "fieldtype": "Table"}, + {"label": "Lab Tests", "fieldname": "lab_test_prescription", "fieldtype": "Table"}, + {"label": "Clinical Procedures", "fieldname": "procedure_prescription", "fieldtype": "Table"}, + {"label": "Therapies", "fieldname": "therapies", "fieldtype": "Table"}, + {"label": "Review Details", "fieldname": "encounter_comment", "fieldtype": "Small Text"} + ]), + "Clinical Procedure": ("start_date", [ + {"label": "Procedure Template", "fieldname": "procedure_template", "fieldtype": "Link"}, + {"label": "Healthcare Practitioner", "fieldname": "practitioner", "fieldtype": "Link"}, + {"label": "Notes", "fieldname": "notes", "fieldtype": "Small Text"}, + {"label": "Service Unit", "fieldname": "service_unit", "fieldtype": "Healthcare Service Unit"}, + {"label": "Start Time", "fieldname": "start_time", "fieldtype": "Time"}, + {"label": "Sample", "fieldname": "sample", "fieldtype": "Link"} + ]), + "Lab Test": ("result_date", [ + {"label": "Test Template", "fieldname": "template", "fieldtype": "Link"}, + {"label": "Healthcare Practitioner", "fieldname": "practitioner", "fieldtype": "Link"}, + {"label": "Test Name", "fieldname": "lab_test_name", "fieldtype": "Data"}, + {"label": "Lab Technician Name", "fieldname": "employee_name", "fieldtype": "Data"}, + {"label": "Sample ID", "fieldname": "sample", "fieldtype": "Link"}, + {"label": "Normal Test Result", "fieldname": "normal_test_items", "fieldtype": "Table"}, + {"label": "Descriptive Test Result", "fieldname": "descriptive_test_items", "fieldtype": "Table"}, + {"label": "Organism Test Result", "fieldname": "organism_test_items", "fieldtype": "Table"}, + {"label": "Sensitivity Test Result", "fieldname": "sensitivity_test_items", "fieldtype": "Table"}, + {"label": "Comments", "fieldname": "lab_test_comment", "fieldtype": "Table"} + ]), + "Therapy Session": ("start_date", [ + {"label": "Therapy Type", "fieldname": "therapy_type", "fieldtype": "Link"}, + {"label": "Healthcare Practitioner", "fieldname": "practitioner", "fieldtype": "Link"}, + {"label": "Therapy Plan", "fieldname": "therapy_plan", "fieldtype": "Link"}, + {"label": "Duration", "fieldname": "duration", "fieldtype": "Int"}, + {"label": "Location", "fieldname": "location", "fieldtype": "Link"}, + {"label": "Healthcare Service Unit", "fieldname": "service_unit", "fieldtype": "Link"}, + {"label": "Start Time", "fieldname": "start_time", "fieldtype": "Time"}, + {"label": "Exercises", "fieldname": "exercises", "fieldtype": "Table"}, + {"label": "Total Counts Targeted", "fieldname": "total_counts_targeted", "fieldtype": "Int"}, + {"label": "Total Counts Completed", "fieldname": "total_counts_completed", "fieldtype": "Int"} + ]), + "Vital Signs": ("signs_date", [ + {"label": "Body Temperature", "fieldname": "temperature", "fieldtype": "Data"}, + {"label": "Heart Rate / Pulse", "fieldname": "pulse", "fieldtype": "Data"}, + {"label": "Respiratory rate", "fieldname": "respiratory_rate", "fieldtype": "Data"}, + {"label": "Tongue", "fieldname": "tongue", "fieldtype": "Select"}, + {"label": "Abdomen", "fieldname": "abdomen", "fieldtype": "Select"}, + {"label": "Reflexes", "fieldname": "reflexes", "fieldtype": "Select"}, + {"label": "Blood Pressure", "fieldname": "bp", "fieldtype": "Data"}, + {"label": "Notes", "fieldname": "vital_signs_note", "fieldtype": "Small Text"}, + {"label": "Height (In Meter)", "fieldname": "height", "fieldtype": "Float"}, + {"label": "Weight (In Kilogram)", "fieldname": "weight", "fieldtype": "Float"}, + {"label": "BMI", "fieldname": "bmi", "fieldtype": "Float"} + ]), + "Inpatient Medication Order": ("start_date", [ + {"label": "Healthcare Practitioner", "fieldname": "practitioner", "fieldtype": "Link"}, + {"label": "Start Date", "fieldname": "start_date", "fieldtype": "Date"}, + {"label": "End Date", "fieldname": "end_date", "fieldtype": "Date"}, + {"label": "Medication Orders", "fieldname": "medication_orders", "fieldtype": "Table"}, + {"label": "Total Orders", "fieldname": "total_orders", "fieldtype": "Float"} + ]) + } \ No newline at end of file diff --git a/erpnext/healthcare/utils.py b/erpnext/healthcare/utils.py index 6a499aab7f..40f7f9cabd 100644 --- a/erpnext/healthcare/utils.py +++ b/erpnext/healthcare/utils.py @@ -6,6 +6,7 @@ from __future__ import unicode_literals import math import frappe from frappe import _ +from frappe.utils.formatters import format_value from frappe.utils import time_diff_in_hours, rounded from erpnext.healthcare.doctype.healthcare_settings.healthcare_settings import get_income_account from erpnext.healthcare.doctype.fee_validity.fee_validity import create_fee_validity @@ -648,11 +649,15 @@ def render_doc_as_html(doctype, docname, exclude_fields = []): html += "" \ + table_head + table_row + "
" continue + #on other field types 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: - html += '
{0} : {1}'.format(df.label or df.fieldname, \ - doc.get(df.fieldname)) + if doc.get(df.fieldname): + formatted_value = format_value(doc.get(df.fieldname), meta.get_field(df.fieldname), doc) + html += '
{0} : {1}'.format(df.label or df.fieldname, formatted_value) + if not has_data : has_data = True + if sec_on and col_on and has_data: doc_html += section_html + html + '
' elif sec_on and not col_on and has_data: diff --git a/erpnext/hooks.py b/erpnext/hooks.py index f7ec1c1b6e..14377e1b99 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -221,6 +221,11 @@ standard_queries = { } doc_events = { + "*": { + "on_submit": "erpnext.healthcare.doctype.patient_history_settings.patient_history_settings.create_medical_record", + "on_update_after_submit": "erpnext.healthcare.doctype.patient_history_settings.patient_history_settings.update_medical_record", + "on_cancel": "erpnext.healthcare.doctype.patient_history_settings.patient_history_settings.delete_medical_record" + }, "Stock Entry": { "on_submit": "erpnext.stock.doctype.material_request.material_request.update_completed_and_requested_qty", "on_cancel": "erpnext.stock.doctype.material_request.material_request.update_completed_and_requested_qty" diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 62e9b5cb6a..da52ae9aff 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -746,3 +746,4 @@ erpnext.patches.v13_0.update_returned_qty_in_pr_dn erpnext.patches.v13_0.update_project_template_tasks erpnext.patches.v13_0.set_company_in_leave_ledger_entry erpnext.patches.v13_0.convert_qi_parameter_to_link_field +erpnext.patches.v13_0.setup_patient_history_settings_for_standard_doctypes diff --git a/erpnext/patches/v13_0/setup_patient_history_settings_for_standard_doctypes.py b/erpnext/patches/v13_0/setup_patient_history_settings_for_standard_doctypes.py new file mode 100644 index 0000000000..de08aa26b3 --- /dev/null +++ b/erpnext/patches/v13_0/setup_patient_history_settings_for_standard_doctypes.py @@ -0,0 +1,13 @@ +from __future__ import unicode_literals +import frappe +from erpnext.healthcare.setup import setup_patient_history_settings + +def execute(): + if "Healthcare" not in frappe.get_active_domains(): + return + + frappe.reload_doc("healthcare", "doctype", "Patient History Settings") + frappe.reload_doc("healthcare", "doctype", "Patient History Standard Document Type") + frappe.reload_doc("healthcare", "doctype", "Patient History Custom Document Type") + + setup_patient_history_settings() \ No newline at end of file