Merge branch 'develop' of https://github.com/frappe/erpnext into razorpay-subscription

This commit is contained in:
Shivam Mishra 2020-04-14 12:00:38 +05:30
commit 8e91553d4b
124 changed files with 7095 additions and 13882 deletions

View File

@ -164,7 +164,7 @@ def build_forest(data):
error_messages = []
for i in data:
account_name, _, account_number, is_group, account_type, root_type = i
account_name, dummy, account_number, is_group, account_type, root_type = i
if not account_name:
error_messages.append("Row {0}: Please enter Account Name".format(line_no))

View File

@ -794,7 +794,7 @@ frappe.ui.form.on('Sales Invoice', {
},
refresh: function(frm) {
if (frappe.boot.active_domains.includes("Healthcare")){
if (frappe.boot.active_domains.includes("Healthcare")) {
frm.set_df_property("patient", "hidden", 0);
frm.set_df_property("patient_name", "hidden", 0);
frm.set_df_property("ref_practitioner", "hidden", 0);
@ -807,7 +807,7 @@ frappe.ui.form.on('Sales Invoice', {
},"Get items from");
}
}
else{
else {
frm.set_df_property("patient", "hidden", 1);
frm.set_df_property("patient_name", "hidden", 1);
frm.set_df_property("ref_practitioner", "hidden", 1);

View File

@ -162,8 +162,9 @@ def get_default_price_list(party):
def set_price_list(party_details, party, party_type, given_price_list, pos=None):
# price list
price_list = get_permitted_documents('Price List')
if price_list:
# if there is only one permitted document based on user permissions, set it
if price_list and len(price_list) == 1:
price_list = price_list[0]
elif pos and party_type == 'Customer':
customer_price_list = frappe.get_value('Customer', party.name, 'default_price_list')
@ -635,4 +636,4 @@ def get_default_contact(doctype, name):
except:
return None
else:
return None
return None

View File

@ -2,131 +2,68 @@ from __future__ import unicode_literals
from frappe import _
def get_data():
return [
{
"label": _("Consultation"),
"icon": "icon-star",
"items": [
{
"type": "doctype",
"name": "Patient Appointment",
"label": _("Patient Appointment"),
},
{
"type": "doctype",
"name": "Patient Encounter",
"label": _("Patient Encounter"),
},
{
"type": "doctype",
"name": "Vital Signs",
"label": _("Vital Signs"),
"description": _("Record Patient Vitals"),
},
{
"type": "page",
"name": "patient_history",
"label": _("Patient History"),
},
{
"type": "page",
"name": "appointment-analytic",
"label": _("Appointment Analytics"),
},
{
"type": "doctype",
"name": "Clinical Procedure",
"label": _("Clinical Procedure"),
},
{
"type": "doctype",
"name": "Inpatient Record",
"label": _("Inpatient Record"),
}
]
},
{
"label": _("Laboratory"),
"icon": "icon-list",
"items": [
{
"type": "doctype",
"name": "Lab Test",
"label": _("Lab Test"),
},
{
"type": "doctype",
"name": "Sample Collection",
"label": _("Sample Collection"),
},
{
"type": "report",
"name": "Lab Test Report",
"is_query_report": True,
"label": _("Lab Test Report"),
}
]
},
{
"label": _("Masters"),
"icon": "icon-list",
"items": [
{
"type": "doctype",
"name": "Patient",
"label": _("Patient"),
"onboard": 1,
"onboard": 1
},
{
"type": "doctype",
"name": "Healthcare Practitioner",
"label": _("Healthcare Practitioner"),
"onboard": 1,
"onboard": 1
},
{
"type": "doctype",
"name": "Practitioner Schedule",
"label": _("Practitioner Schedule"),
},
{
"type": "doctype",
"name": "Medical Code Standard",
"label": _("Medical Code Standard"),
},
{
"type": "doctype",
"name": "Medical Code",
"label": _("Medical Code"),
"onboard": 1,
},
{
"type": "doctype",
"name": "Healthcare Service Unit",
"label": _("Healthcare Service Unit")
}
]
},
{
"label": _("Settings"),
"icon": "icon-cog",
"items": [
{
"type": "doctype",
"name": "Healthcare Settings",
"label": _("Healthcare Settings"),
"onboard": 1,
"onboard": 1
},
{
"type": "doctype",
"name": "Medical Department",
"label": _("Medical Department"),
},
{
"type": "doctype",
"name": "Healthcare Service Unit Type",
"label": _("Healthcare Service Unit Type")
},
{
"type": "doctype",
"name": "Healthcare Service Unit",
"label": _("Healthcare Service Unit")
},
{
"type": "doctype",
"name": "Medical Code Standard",
"label": _("Medical Code Standard")
},
{
"type": "doctype",
"name": "Medical Code",
"label": _("Medical Code")
}
]
},
{
"label": _("Consultation Setup"),
"items": [
{
"type": "doctype",
"name": "Appointment Type",
"label": _("Appointment Type"),
},
{
"type": "doctype",
"name": "Clinical Procedure Template",
"label": _("Clinical Procedure Template")
},
{
"type": "doctype",
"name": "Prescription Dosage",
@ -137,6 +74,36 @@ def get_data():
"name": "Prescription Duration",
"label": _("Prescription Duration")
},
{
"type": "doctype",
"name": "Antibiotic",
"label": _("Antibiotic")
}
]
},
{
"label": _("Consultation"),
"items": [
{
"type": "doctype",
"name": "Patient Appointment",
"label": _("Patient Appointment")
},
{
"type": "doctype",
"name": "Clinical Procedure",
"label": _("Clinical Procedure")
},
{
"type": "doctype",
"name": "Patient Encounter",
"label": _("Patient Encounter")
},
{
"type": "doctype",
"name": "Vital Signs",
"label": _("Vital Signs")
},
{
"type": "doctype",
"name": "Complaint",
@ -147,40 +114,104 @@ def get_data():
"name": "Diagnosis",
"label": _("Diagnosis")
},
{
"type": "doctype",
"name": "Fee Validity",
"label": _("Fee Validity")
}
]
},
{
"label": _("Settings"),
"items": [
{
"type": "doctype",
"name": "Healthcare Settings",
"label": _("Healthcare Settings"),
"onboard": 1
}
]
},
{
"label": _("Laboratory Setup"),
"items": [
{
"type": "doctype",
"name": "Lab Test Template",
"label": _("Lab Test Template")
},
{
"type": "doctype",
"name": "Lab Test Sample",
"label": _("Lab Test Sample"),
"label": _("Lab Test Sample")
},
{
"type": "doctype",
"name": "Lab Test UOM",
"label": _("Lab Test UOM")
},
{
"type": "doctype",
"name": "Antibiotic",
"label": _("Antibiotic")
},
{
"type": "doctype",
"name": "Sensitivity",
"label": _("Sensitivity")
}
]
},
{
"label": _("Laboratory"),
"items": [
{
"type": "doctype",
"name": "Lab Test",
"label": _("Lab Test")
},
{
"type": "doctype",
"name": "Lab Test Template",
"label": _("Lab Test Template")
"name": "Sample Collection",
"label": _("Sample Collection")
},
{
"type": "doctype",
"name": "Clinical Procedure Template",
"label": _("Clinical Procedure Template"),
"name": "Dosage Form",
"label": _("Dosage Form")
}
]
},
{
"label": _("Records and History"),
"items": [
{
"type": "page",
"name": "patient_history",
"label": _("Patient History"),
},
{
"type": "doctype",
"name": "Healthcare Service Unit Type",
"label": _("Healthcare Service Unit Type")
"name": "Patient Medical Record",
"label": _("Patient Medical Record")
},
{
"type": "doctype",
"name": "Inpatient Record",
"label": _("Inpatient Record")
}
]
},
{
"label": _("Reports"),
"items": [
{
"type": "report",
"is_query_report": True,
"name": "Patient Appointment Analytics",
"doctype": "Patient Appointment"
},
{
"type": "report",
"is_query_report": True,
"name": "Lab Test Report",
"doctype": "Lab Test",
"label": _("Lab Test Report")
}
]
}

View File

@ -1,28 +1,49 @@
{
"cards": [
{
"hidden": 0,
"label": "Settings",
"links": "[\n {\n \"label\": \"Healthcare Settings\",\n \"name\": \"Healthcare Settings\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Medical Department\",\n \"name\": \"Medical Department\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Appointment Type\",\n \"name\": \"Appointment Type\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Prescription Dosage\",\n \"name\": \"Prescription Dosage\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Prescription Duration\",\n \"name\": \"Prescription Duration\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Complaint\",\n \"name\": \"Complaint\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Diagnosis\",\n \"name\": \"Diagnosis\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Lab Test Sample\",\n \"name\": \"Lab Test Sample\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Lab Test UOM\",\n \"name\": \"Lab Test UOM\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Antibiotic\",\n \"name\": \"Antibiotic\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Sensitivity\",\n \"name\": \"Sensitivity\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Lab Test Template\",\n \"name\": \"Lab Test Template\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Clinical Procedure Template\",\n \"name\": \"Clinical Procedure Template\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Healthcare Service Unit Type\",\n \"name\": \"Healthcare Service Unit Type\",\n \"type\": \"doctype\"\n }\n]"
"icon": "",
"links": "[\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Patient\",\n\t\t\"label\": \"Patient\",\n\t\t\"onboard\": 1\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Healthcare Practitioner\",\n\t\t\"label\":\"Healthcare Practitioner\",\n\t\t\"onboard\": 1\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Practitioner Schedule\",\n\t\t\"label\": \"Practitioner Schedule\",\n\t\t\"onboard\": 1\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Medical Department\",\n\t\t\"label\": \"Medical Department\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Healthcare Service Unit Type\",\n\t\t\"label\": \"Healthcare Service Unit Type\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Healthcare Service Unit\",\n\t\t\"label\": \"Healthcare Service Unit\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Medical Code Standard\",\n\t\t\"label\": \"Medical Code Standard\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Medical Code\",\n\t\t\"label\": \"Medical Code\"\n\t}\n]",
"title": "Masters"
},
{
"hidden": 0,
"label": "Laboratory",
"links": "[\n {\n \"label\": \"Lab Test\",\n \"name\": \"Lab Test\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Sample Collection\",\n \"name\": \"Sample Collection\",\n \"type\": \"doctype\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Lab Test Report\",\n \"name\": \"Lab Test Report\",\n \"type\": \"report\"\n }\n]"
"icon": "",
"links": "[\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Appointment Type\",\n\t\t\"label\": \"Appointment Type\"\n },\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Clinical Procedure Template\",\n\t\t\"label\": \"Clinical Procedure Template\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Prescription Dosage\",\n\t\t\"label\": \"Prescription Dosage\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Prescription Duration\",\n\t\t\"label\": \"Prescription Duration\"\n\t},\n\t{\n\t \"type\": \"doctype\",\n\t\t\"name\": \"Antibiotic\",\n\t\t\"label\": \"Antibiotic\"\n\t}\n]",
"title": "Consultation Setup"
},
{
"hidden": 0,
"label": "Masters",
"links": "[\n {\n \"label\": \"Patient\",\n \"name\": \"Patient\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Healthcare Practitioner\",\n \"name\": \"Healthcare Practitioner\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Practitioner Schedule\",\n \"name\": \"Practitioner Schedule\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Medical Code Standard\",\n \"name\": \"Medical Code Standard\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Medical Code\",\n \"name\": \"Medical Code\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Healthcare Service Unit\",\n \"name\": \"Healthcare Service Unit\",\n \"type\": \"doctype\"\n }\n]"
"icon": "icon-star",
"links": "[\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Patient Appointment\",\n\t\t\"label\": \"Patient Appointment\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Clinical Procedure\",\n\t\t\"label\": \"Clinical Procedure\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Patient Encounter\",\n\t\t\"label\": \"Patient Encounter\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Vital Signs\",\n\t\t\"label\": \"Vital Signs\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Complaint\",\n\t\t\"label\": \"Complaint\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Diagnosis\",\n\t\t\"label\": \"Diagnosis\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Fee Validity\",\n\t\t\"label\": \"Fee Validity\"\n\t}\n]",
"title": "Consultation"
},
{
"hidden": 0,
"label": "Consultation",
"links": "[\n {\n \"label\": \"Patient Appointment\",\n \"name\": \"Patient Appointment\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Patient Encounter\",\n \"name\": \"Patient Encounter\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Record Patient Vitals\",\n \"label\": \"Vital Signs\",\n \"name\": \"Vital Signs\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Patient History\",\n \"name\": \"patient_history\",\n \"type\": \"page\"\n },\n {\n \"label\": \"Appointment Analytics\",\n \"name\": \"appointment-analytic\",\n \"type\": \"page\"\n },\n {\n \"label\": \"Clinical Procedure\",\n \"name\": \"Clinical Procedure\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Inpatient Record\",\n \"name\": \"Inpatient Record\",\n \"type\": \"doctype\"\n }\n]"
"links": "[\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Healthcare Settings\",\n\t\t\"label\": \"Healthcare Settings\",\n\t\t\"onboard\": 1\n\t}\n]",
"title": "Settings"
},
{
"links": "[\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Lab Test Template\",\n\t\t\"label\": \"Lab Test Template\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Lab Test Sample\",\n\t\t\"label\": \"Lab Test Sample\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Lab Test UOM\",\n\t\t\"label\": \"Lab Test UOM\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Sensitivity\",\n\t\t\"label\": \"Sensitivity\"\n\t}\n]",
"title": "Laboratory Setup"
},
{
"links": "[\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Lab Test\",\n\t\t\"label\": \"Lab Test\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Sample Collection\",\n\t\t\"label\": \"Sample Collection\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Dosage Form\",\n\t\t\"label\": \"Dosage Form\"\n\t}\n]",
"title": "Laboratory"
},
{
"links": "[\n\t{\n\t\t\"type\": \"page\",\n\t\t\"name\": \"patient_history\",\n\t\t\"label\": \"Patient History\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Patient Medical Record\",\n\t\t\"label\": \"Patient Medical Record\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Inpatient Record\",\n\t\t\"label\": \"Inpatient Record\"\n\t}\n]",
"title": "Records and History"
},
{
"links": "[\n\t{\n\t\t\"type\": \"report\",\n\t\t\"is_query_report\": true,\n\t\t\"name\": \"Patient Appointment Analytics\",\n\t\t\"doctype\": \"Patient Appointment\"\n\t},\n\t{\n\t\t\"type\": \"report\",\n\t\t\"is_query_report\": true,\n\t\t\"name\": \"Lab Test Report\",\n\t\t\"doctype\": \"Lab Test\",\n\t\t\"label\": \"Lab Test Report\"\n\t}\n]",
"title": "Reports"
}
],
"category": "Domains",
"charts": [],
"charts": [
{
"chart_name": "Patient Appointments",
"label": "Patient Appointments"
}
],
"charts_label": "",
"creation": "2020-03-02 17:23:17.919682",
"developer_mode_only": 0,
"disable_user_customization": 0,
@ -32,7 +53,7 @@
"idx": 0,
"is_standard": 1,
"label": "Healthcare",
"modified": "2020-04-01 11:28:50.996366",
"modified": "2020-03-26 16:10:44.629795",
"modified_by": "Administrator",
"module": "Healthcare",
"name": "Healthcare",
@ -40,5 +61,37 @@
"pin_to_bottom": 0,
"pin_to_top": 0,
"restrict_to_domain": "Healthcare",
"shortcuts": []
"shortcuts": [
{
"format": "{} Open",
"is_query_report": 0,
"link_to": "Patient Appointment",
"stats_filter": "{\n \"status\": \"Open\"\n}",
"type": "DocType"
},
{
"format": "{} Active",
"is_query_report": 0,
"link_to": "Patient",
"stats_filter": "{\n \"status\": \"Active\"\n}",
"type": "DocType"
},
{
"format": "{} Vacant",
"is_query_report": 0,
"link_to": "Healthcare Service Unit",
"stats_filter": "{\n \"occupancy_status\": \"Vacant\",\n \"is_group\": 0\n}",
"type": "DocType"
},
{
"is_query_report": 0,
"link_to": "Healthcare Practitioner",
"type": "DocType"
},
{
"is_query_report": 0,
"link_to": "patient_history",
"type": "Page"
}
]
}

View File

@ -1,213 +1,94 @@
{
"allow_copy": 0,
"allow_guest_to_view": 0,
"allow_import": 1,
"allow_rename": 1,
"autoname": "field:appointment_type",
"beta": 1,
"creation": "2016-07-22 11:52:34.953019",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "Setup",
"editable_grid": 0,
"actions": [],
"allow_import": 1,
"allow_rename": 1,
"autoname": "field:appointment_type",
"beta": 1,
"creation": "2016-07-22 11:52:34.953019",
"doctype": "DocType",
"document_type": "Setup",
"engine": "InnoDB",
"field_order": [
"appointment_type",
"ip",
"default_duration",
"color"
],
"fields": [
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 1,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "appointment_type",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 1,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Type",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 1,
"allow_in_quick_entry": 1,
"fieldname": "appointment_type",
"fieldtype": "Data",
"ignore_xss_filter": 1,
"in_list_view": 1,
"label": "Type",
"reqd": 1,
"translatable": 1,
"unique": 1
},
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 1,
"collapsible": 0,
"columns": 0,
"fieldname": "ip",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Is Inpatient",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 1,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 1,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
"bold": 1,
"default": "0",
"fieldname": "ip",
"fieldtype": "Check",
"label": "Is Inpatient",
"print_hide": 1,
"report_hide": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 1,
"allow_on_submit": 0,
"bold": 1,
"collapsible": 0,
"columns": 0,
"description": "In Minutes",
"fieldname": "default_duration",
"fieldtype": "Int",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 1,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Default Duration",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
"allow_in_quick_entry": 1,
"bold": 1,
"fieldname": "default_duration",
"fieldtype": "Int",
"in_filter": 1,
"in_list_view": 1,
"label": "Default Duration (In Minutes)"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 1,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "color",
"fieldtype": "Color",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Color",
"length": 0,
"no_copy": 1,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 1,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
"allow_in_quick_entry": 1,
"fieldname": "color",
"fieldtype": "Color",
"in_list_view": 1,
"label": "Color",
"no_copy": 1,
"report_hide": 1
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2018-08-08 12:57:54.544216",
"modified_by": "Administrator",
"module": "Healthcare",
"name": "Appointment Type",
"name_case": "",
"owner": "Administrator",
],
"links": [],
"modified": "2020-02-03 21:06:05.833050",
"modified_by": "Administrator",
"module": "Healthcare",
"name": "Appointment Type",
"owner": "Administrator",
"permissions": [
{
"amend": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Healthcare Administrator",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "Healthcare Administrator",
"share": 1,
"write": 1
},
},
{
"amend": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Physician",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "Physician",
"share": 1,
"write": 1
}
],
"quick_entry": 1,
"read_only": 0,
"read_only_onload": 0,
"restrict_to_domain": "Healthcare",
"search_fields": "appointment_type",
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"title_field": "",
"track_changes": 0,
"track_seen": 0,
"track_views": 0
],
"quick_entry": 1,
"restrict_to_domain": "Healthcare",
"search_fields": "appointment_type",
"sort_field": "modified",
"sort_order": "DESC"
}

View File

@ -0,0 +1,13 @@
from __future__ import unicode_literals
from frappe import _
def get_data():
return {
'fieldname': 'appointment_type',
'transactions': [
{
'label': _('Patient Appointments'),
'items': ['Patient Appointment']
},
]
}

View File

@ -4,244 +4,285 @@
frappe.ui.form.on('Clinical Procedure', {
setup: function(frm) {
frm.set_query('batch_no', 'items', function(doc, cdt, cdn) {
var item = locals[cdt][cdn];
if(!item.item_code) {
frappe.throw(__("Please enter Item Code to get Batch Number"));
let item = locals[cdt][cdn];
if (!item.item_code) {
frappe.throw(__('Please enter Item Code to get Batch Number'));
} else {
let filters = {'item_code': item.item_code};
if (frm.doc.status == 'In Progress') {
var filters = {
'item_code': item.item_code,
'posting_date': frm.doc.start_date || frappe.datetime.nowdate()
};
if(frm.doc.warehouse) filters["warehouse"] = frm.doc.warehouse;
} else {
filters = {
'item_code': item.item_code
};
filters['posting_date'] = frm.doc.start_date || frappe.datetime.nowdate();
if (frm.doc.warehouse) filters['warehouse'] = frm.doc.warehouse;
}
return {
query : "erpnext.controllers.queries.get_batch_no",
query : 'erpnext.controllers.queries.get_batch_no',
filters: filters
};
}
});
},
refresh: function(frm) {
frm.set_query("patient", function () {
frm.set_query('patient', function () {
return {
filters: {"disabled": 0}
filters: {'status': ['!=', 'Disabled']}
};
});
frm.set_query("appointment", function () {
frm.set_query('appointment', function () {
return {
filters: {
"procedure_template": ["not in", null],
"status": ['in', 'Open, Scheduled']
'procedure_template': ['not in', null],
'status': ['in', 'Open, Scheduled']
}
};
});
frm.set_query("service_unit", function(){
frm.set_query('service_unit', function() {
return {
filters: {
"is_group": false,
"allow_appointments": true
'is_group': false,
'allow_appointments': true
}
};
});
frm.set_query("practitioner", function() {
frm.set_query('practitioner', function() {
return {
filters: {
'department': frm.doc.medical_department
}
};
});
if(frm.doc.consume_stock){
if (frm.doc.consume_stock) {
frm.set_indicator_formatter('item_code',
function(doc) { return (doc.qty<=doc.actual_qty) ? "green" : "orange" ; });
function(doc) { return (doc.qty<=doc.actual_qty) ? 'green' : 'orange' ; });
}
if (!frm.doc.__islocal && frm.doc.status == 'In Progress'){
if (frm.doc.docstatus == 1) {
if (frm.doc.status == 'In Progress') {
let btn_label = '';
let msg = '';
if (frm.doc.consume_stock) {
btn_label = __('Complete and Consume');
msg = __('Complete {0} and Consume Stock?', [frm.doc.name]);
} else {
btn_label = 'Complete';
msg = __('Complete {0}?', [frm.doc.name]);
}
if(frm.doc.consume_stock){
var btn_label = 'Complete and Consume';
var msg = 'Complete '+frm.doc.name+' and Consume Stock?';
}else{
btn_label = 'Complete';
msg = 'Complete '+frm.doc.name+'?';
}
frm.add_custom_button(__(btn_label), function () {
frappe.confirm(
msg,
function() {
frappe.call({
method: 'complete_procedure',
doc: frm.doc,
callback: function(r) {
if (r.message) {
frappe.show_alert({
message: __('Stock Entry {0} created',
['<a class="bold" href="#Form/Stock Entry/'+ r.message + '">' + r.message + '</a>']),
indicator: 'green'
});
frm.reload_doc();
}
}
});
}
);
}).addClass("btn-primary");
frm.add_custom_button(__(btn_label), function () {
frappe.confirm(
__(msg),
function(){
frappe.call({
doc: frm.doc,
method: "complete",
callback: function(r) {
if(!r.exc){
} else if (frm.doc.status == 'Pending') {
frm.add_custom_button(__('Start'), function() {
frappe.call({
doc: frm.doc,
method: 'start_procedure',
callback: function(r) {
if (!r.exc) {
if (r.message == 'insufficient stock') {
let msg = __('Stock quantity to start the Procedure is not available in the Warehouse {0}. Do you want to record a Stock Entry?',
[frm.doc.warehouse.bold()]);
frappe.confirm(
msg,
function() {
frappe.call({
doc: frm.doc,
method: 'make_material_receipt',
callback: function(r) {
if (!r.exc) {
cur_frm.reload_doc();
let doclist = frappe.model.sync(r.message);
frappe.set_route('Form', doclist[0].doctype, doclist[0].name);
}
}
});
}
);
} else {
cur_frm.reload_doc();
}
}
});
}
);
});
}else if (frm.doc.status == 'Draft') {
frm.add_custom_button(__("Start"), function () {
frappe.call({
doc: frm.doc,
method: "start",
callback: function(r) {
if(!r.exc){
if(frm.doc.status == 'Draft'){
frappe.confirm(
__("Stock quantity to start procedure is not available in the warehouse. Do you want to record a Stock Transfer"),
function(){
frappe.call({
doc: frm.doc,
method: "make_material_transfer",
callback: function(r) {
if(!r.exc){
cur_frm.reload_doc();
var doclist = frappe.model.sync(r.message);
frappe.set_route("Form", doclist[0].doctype, doclist[0].name);
}
}
});
}
);
}else{
cur_frm.reload_doc();
}
}
}
});
});
}
if (frm.doc.__islocal){
frm.set_df_property("consumables", "hidden", 1);
}else{
frm.set_df_property("consumables", "hidden", 0);
});
}).addClass("btn-primary");
}
}
},
onload: function(frm){
if(frm.doc.status == 'Completed'){
frm.set_df_property("items", "read_only", 1);
}
if(frm.is_new()) {
frm.add_fetch("procedure_template", "medical_department", "medical_department");
frm.set_value("start_time", null);
onload: function(frm) {
if (frm.is_new()) {
frm.add_fetch('procedure_template', 'medical_department', 'medical_department');
frm.set_value('start_time', null);
}
},
patient: function(frm) {
if(frm.doc.patient){
if (frm.doc.patient) {
frappe.call({
"method": "erpnext.healthcare.doctype.patient.patient.get_patient_detail",
'method': 'erpnext.healthcare.doctype.patient.patient.get_patient_detail',
args: {
patient: frm.doc.patient
},
callback: function (data) {
let age = "";
if(data.message.dob){
let age = '';
if (data.message.dob) {
age = calculate_age(data.message.dob);
}else if (data.message.age){
} else if (data.message.age) {
age = data.message.age;
if(data.message.age_as_on){
age = age+" as on "+data.message.age_as_on;
if (data.message.age_as_on) {
age = __('{0} as on {1}', [age, data.message.age_as_on]);
}
}
frm.set_value("patient_age", age);
frm.set_value("patient_sex", data.message.sex);
frm.set_value('patient_age', age);
frm.set_value('patient_sex', data.message.sex);
}
});
}else{
frm.set_value("patient_age", "");
frm.set_value("patient_sex", "");
} else {
frm.set_value('patient_age', '');
frm.set_value('patient_sex', '');
}
},
appointment: function(frm) {
if(frm.doc.appointment){
if (frm.doc.appointment) {
frappe.call({
"method": "frappe.client.get",
'method': 'frappe.client.get',
args: {
doctype: "Patient Appointment",
doctype: 'Patient Appointment',
name: frm.doc.appointment
},
callback: function (data) {
frm.set_value("patient", data.message.patient);
frm.set_value("procedure_template", data.message.procedure_template);
frm.set_value("medical_department", data.message.department);
frm.set_value("start_date", data.message.appointment_date);
frm.set_value("start_time", data.message.appointment_time);
frm.set_value("notes", data.message.notes);
frm.set_value("service_unit", data.message.service_unit);
callback: function(data) {
frm.set_value('patient', data.message.patient);
frm.set_value('procedure_template', data.message.procedure_template);
frm.set_value('medical_department', data.message.department);
frm.set_value('start_date', data.message.appointment_date);
frm.set_value('start_time', data.message.appointment_time);
frm.set_value('notes', data.message.notes);
frm.set_value('service_unit', data.message.service_unit);
}
});
}
},
procedure_template: function(frm) {
if(frm.doc.procedure_template){
if (frm.doc.procedure_template) {
frappe.call({
"method": "frappe.client.get",
'method': 'frappe.client.get',
args: {
doctype: "Clinical Procedure Template",
doctype: 'Clinical Procedure Template',
name: frm.doc.procedure_template
},
callback: function (data) {
frm.set_value("medical_department", data.message.medical_department);
frm.set_value("consume_stock", data.message.consume_stock);
if(!frm.doc.warehouse){
frappe.call({
method: "frappe.client.get_value",
args: {
doctype: "Stock Settings",
fieldname: "default_warehouse"
},
callback: function (data) {
frm.set_value("warehouse", data.message.default_warehouse);
}
});
}
frm.set_value('medical_department', data.message.medical_department);
frm.set_value('consume_stock', data.message.consume_stock);
frm.events.set_warehouse(frm);
frm.events.set_procedure_consumables(frm);
}
});
}else{
frm.set_value("consume_stock", 0);
}
},
service_unit: function(frm) {
if(frm.doc.service_unit){
if (frm.doc.service_unit) {
frappe.call({
method: "frappe.client.get_value",
method: 'frappe.client.get_value',
args:{
fieldname: "warehouse",
doctype: "Healthcare Service Unit",
fieldname: 'warehouse',
doctype: 'Healthcare Service Unit',
filters:{name: frm.doc.service_unit},
},
callback: function(data) {
if(data.message){
frm.set_value("warehouse", data.message.warehouse);
if (data.message) {
frm.set_value('warehouse', data.message.warehouse);
}
}
});
}
},
practitioner: function(frm) {
if(frm.doc.practitioner){
if (frm.doc.practitioner) {
frappe.call({
"method": "frappe.client.get",
'method': 'frappe.client.get',
args: {
doctype: "Healthcare Practitioner",
doctype: 'Healthcare Practitioner',
name: frm.doc.practitioner
},
callback: function (data) {
frappe.model.set_value(frm.doctype,frm.docname, "medical_department",data.message.department);
frappe.model.set_value(frm.doctype,frm.docname, 'medical_department',data.message.department);
}
});
}
},
set_warehouse: function(frm) {
if (!frm.doc.warehouse) {
frappe.call({
method: 'frappe.client.get_value',
args: {
doctype: 'Stock Settings',
fieldname: 'default_warehouse'
},
callback: function (data) {
frm.set_value('warehouse', data.message.default_warehouse);
}
});
}
},
set_procedure_consumables: function(frm) {
frappe.call({
method: 'erpnext.healthcare.doctype.clinical_procedure.clinical_procedure.get_procedure_consumables',
args: {
procedure_template: frm.doc.procedure_template
},
callback: function(data) {
if (data.message) {
frm.doc.items = [];
$.each(data.message, function(i, v) {
let item = frm.add_child('items');
item.item_code = v.item_code;
item.item_name = v.item_name;
item.uom = v.uom;
item.stock_uom = v.stock_uom;
item.qty = flt(v.qty);
item.transfer_qty = v.transfer_qty;
item.conversion_factor = v.conversion_factor;
item.invoice_separately_as_consumables = v.invoice_separately_as_consumables;
item.batch_no = v.batch_no;
});
refresh_field('items');
}
}
});
}
});
cur_frm.set_query("procedure_template", function(doc) {
cur_frm.set_query('procedure_template', function(doc) {
return {
filters: {
'medical_department': doc.medical_department
@ -249,57 +290,51 @@ cur_frm.set_query("procedure_template", function(doc) {
};
});
cur_frm.set_query("appointment", function() {
return {
filters: {
status:['in',["Open"]]
}
};
});
frappe.ui.form.on('Clinical Procedure Item', {
qty: function(frm, cdt, cdn){
var d = locals[cdt][cdn];
frappe.model.set_value(cdt, cdn, "transfer_qty", d.qty*d.conversion_factor);
qty: function(frm, cdt, cdn) {
let d = locals[cdt][cdn];
frappe.model.set_value(cdt, cdn, 'transfer_qty', d.qty*d.conversion_factor);
},
uom: function(doc, cdt, cdn){
var d = locals[cdt][cdn];
if(d.uom && d.item_code){
uom: function(doc, cdt, cdn) {
let d = locals[cdt][cdn];
if (d.uom && d.item_code) {
return frappe.call({
method: "erpnext.stock.doctype.stock_entry.stock_entry.get_uom_details",
method: 'erpnext.stock.doctype.stock_entry.stock_entry.get_uom_details',
args: {
item_code: d.item_code,
uom: d.uom,
qty: d.qty
},
callback: function(r) {
if(r.message) {
if (r.message) {
frappe.model.set_value(cdt, cdn, r.message);
}
}
});
}
},
item_code: function(frm, cdt, cdn) {
var d = locals[cdt][cdn];
let d = locals[cdt][cdn];
let args = null;
if(d.item_code) {
if (d.item_code) {
args = {
'doctype' : "Clinical Procedure",
'doctype' : 'Clinical Procedure',
'item_code' : d.item_code,
'company' : frm.doc.company,
'warehouse': frm.doc.warehouse
};
return frappe.call({
method: "erpnext.stock.get_item_details.get_item_details",
method: 'erpnext.healthcare.doctype.clinical_procedure_template.clinical_procedure_template.get_item_details',
args: {args: args},
callback: function(r) {
if(r.message) {
frappe.model.set_value(cdt, cdn, "item_name", r.message.item_name);
frappe.model.set_value(cdt, cdn, "stock_uom", r.message.stock_uom);
frappe.model.set_value(cdt, cdn, "conversion_factor", r.message.conversion_factor);
frappe.model.set_value(cdt, cdn, "actual_qty", r.message.actual_qty);
refresh_field("items");
if (r.message) {
let d = locals[cdt][cdn];
$.each(r.message, function(k, v) {
d[k] = v;
});
refresh_field('items');
}
}
});
@ -307,16 +342,16 @@ frappe.ui.form.on('Clinical Procedure Item', {
}
});
var calculate_age = function(birth) {
var ageMS = Date.parse(Date()) - Date.parse(birth);
var age = new Date();
let calculate_age = function(birth) {
let ageMS = Date.parse(Date()) - Date.parse(birth);
let age = new Date();
age.setTime(ageMS);
var years = age.getFullYear() - 1970;
return years + " Year(s) " + age.getMonth() + " Month(s) " + age.getDate() + " Day(s)";
let years = age.getFullYear() - 1970;
return years + ' Year(s) ' + age.getMonth() + ' Month(s) ' + age.getDate() + ' Day(s)';
};
// List Stock items
cur_frm.set_query("item_code", "items", function() {
cur_frm.set_query('item_code', 'items', function() {
return {
filters: {
is_stock_item:1

View File

@ -6,88 +6,101 @@ from __future__ import unicode_literals
import frappe
from frappe import _
from frappe.model.document import Document
from frappe.utils import cint, flt, nowdate, nowtime, cstr
from frappe.utils import flt, nowdate, nowtime, cstr
from erpnext.healthcare.doctype.healthcare_settings.healthcare_settings import get_account
from erpnext.healthcare.doctype.lab_test.lab_test import create_sample_doc
from erpnext.stock.stock_ledger import get_previous_sle
from erpnext.stock.get_item_details import get_item_details
from frappe.model.mapper import get_mapped_doc
class ClinicalProcedure(Document):
def validate(self):
if self.consume_stock and not self.status == 'Draft':
if not self.warehouse:
frappe.throw(_("Set warehouse for Procedure {0} ").format(self.name))
self.set_status()
if self.consume_stock:
self.set_actual_qty()
if self.items:
self.invoice_separately_as_consumables = False
for item in self.items:
if item.invoice_separately_as_consumables == 1:
if item.invoice_separately_as_consumables:
self.invoice_separately_as_consumables = True
def before_insert(self):
if self.consume_stock:
set_stock_items(self, self.procedure_template, "Clinical Procedure Template")
self.set_actual_qty();
self.set_actual_qty()
def after_insert(self):
if self.prescription:
frappe.db.set_value("Procedure Prescription", self.prescription, "procedure_created", 1)
frappe.db.set_value('Procedure Prescription', self.prescription, 'procedure_created', 1)
if self.appointment:
frappe.db.set_value("Patient Appointment", self.appointment, "status", "Closed")
template = frappe.get_doc("Clinical Procedure Template", self.procedure_template)
frappe.db.set_value('Patient Appointment', self.appointment, 'status', 'Closed')
template = frappe.get_doc('Clinical Procedure Template', self.procedure_template)
if template.sample:
patient = frappe.get_doc("Patient", self.patient)
patient = frappe.get_doc('Patient', self.patient)
sample_collection = create_sample_doc(template, patient, None)
frappe.db.set_value("Clinical Procedure", self.name, "sample", sample_collection.name)
frappe.db.set_value('Clinical Procedure', self.name, 'sample', sample_collection.name)
self.reload()
def complete(self):
def set_status(self):
if self.docstatus == 0:
self.status = 'Draft'
elif self.docstatus == 1:
if self.status not in ['In Progress', 'Completed']:
self.status = 'Pending'
elif self.docstatus == 2:
self.status = 'Cancelled'
def complete_procedure(self):
if self.consume_stock and self.items:
create_stock_entry(self)
frappe.db.set_value("Clinical Procedure", self.name, "status", 'Completed')
stock_entry = make_stock_entry(self)
if self.items:
consumable_total_amount = 0
consumption_details = False
for item in self.items:
if item.invoice_separately_as_consumables:
price_list, price_list_currency = frappe.db.get_values("Price List", {"selling": 1}, ['name', 'currency'])[0]
args = {
'doctype': "Sales Invoice",
'item_code': item.item_code,
'company': self.company,
'warehouse': self.warehouse,
'customer': frappe.db.get_value("Patient", self.patient, "customer"),
'selling_price_list': price_list,
'price_list_currency': price_list_currency,
'plc_conversion_rate': 1.0,
'conversion_rate': 1.0
}
item_details = get_item_details(args)
item_price = item_details.price_list_rate * item.transfer_qty
item_consumption_details = item_details.item_name+"\t"+str(item.qty)+" "+item.uom+"\t"+str(item_price)
consumable_total_amount += item_price
if not consumption_details:
consumption_details = "Clinical Procedure ("+self.name+"):\n\t"+item_consumption_details
else:
consumption_details += "\n\t"+item_consumption_details
if consumable_total_amount > 0:
frappe.db.set_value("Clinical Procedure", self.name, "consumable_total_amount", consumable_total_amount)
frappe.db.set_value("Clinical Procedure", self.name, "consumption_details", consumption_details)
customer = frappe.db.get_value('Patient', self.patient, 'customer')
if customer:
for item in self.items:
if item.invoice_separately_as_consumables:
price_list, price_list_currency = frappe.db.get_values('Price List', {'selling': 1}, ['name', 'currency'])[0]
args = {
'doctype': 'Sales Invoice',
'item_code': item.item_code,
'company': self.company,
'warehouse': self.warehouse,
'customer': customer,
'selling_price_list': price_list,
'price_list_currency': price_list_currency,
'plc_conversion_rate': 1.0,
'conversion_rate': 1.0
}
item_details = get_item_details(args)
item_price = item_details.price_list_rate * item.qty
item_consumption_details = item_details.item_name + ' ' + str(item.qty) + ' ' + item.uom + ' ' + str(item_price)
consumable_total_amount += item_price
if not consumption_details:
consumption_details = _('Clinical Procedure ({0}):').format(self.name)
consumption_details += '\n\t' + item_consumption_details
if consumable_total_amount > 0:
frappe.db.set_value('Clinical Procedure', self.name, 'consumable_total_amount', consumable_total_amount)
frappe.db.set_value('Clinical Procedure', self.name, 'consumption_details', consumption_details)
else:
frappe.throw(_('Please set Customer in Patient {0}').format(frappe.bold(self.patient)), title=_('Customer Not Found'))
def start(self):
frappe.db.set_value('Clinical Procedure', self.name, 'status', 'Completed')
if self.consume_stock and self.items:
return stock_entry
def start_procedure(self):
allow_start = self.set_actual_qty()
if allow_start:
self.status = 'In Progress'
self.db_set('status', 'In Progress')
insert_clinical_procedure_to_medical_record(self)
else:
self.status = 'Draft'
self.save()
return 'success'
return 'insufficient stock'
def set_actual_qty(self):
allow_negative_stock = cint(frappe.db.get_value("Stock Settings", None, "allow_negative_stock"))
allow_negative_stock = frappe.db.get_single_value('Stock Settings', 'allow_negative_stock')
allow_start = True
for d in self.get('items'):
@ -95,15 +108,16 @@ class ClinicalProcedure(Document):
# validate qty
if not allow_negative_stock and d.actual_qty < d.qty:
allow_start = False
break
return allow_start
def make_material_transfer(self):
stock_entry = frappe.new_doc("Stock Entry")
def make_material_receipt(self, submit=False):
stock_entry = frappe.new_doc('Stock Entry')
stock_entry.purpose = "Material Transfer"
stock_entry.stock_entry_type = 'Material Receipt'
stock_entry.to_warehouse = self.warehouse
expense_account = get_account(None, "expense_account", "Healthcare Settings", self.company)
expense_account = get_account(None, 'expense_account', 'Healthcare Settings', self.company)
for item in self.items:
if item.qty > item.actual_qty:
se_child = stock_entry.append('items')
@ -111,7 +125,7 @@ class ClinicalProcedure(Document):
se_child.item_name = item.item_name
se_child.uom = item.uom
se_child.stock_uom = item.stock_uom
se_child.qty = flt(item.qty-item.actual_qty)
se_child.qty = flt(item.qty - item.actual_qty)
se_child.t_warehouse = self.warehouse
# in stock uom
se_child.transfer_qty = flt(item.transfer_qty)
@ -119,104 +133,130 @@ class ClinicalProcedure(Document):
cost_center = frappe.get_cached_value('Company', self.company, 'cost_center')
se_child.cost_center = cost_center
se_child.expense_account = expense_account
if submit:
stock_entry.submit()
return stock_entry
return stock_entry.as_dict()
@frappe.whitelist()
def get_stock_qty(item_code, warehouse):
return get_previous_sle({
"item_code": item_code,
"warehouse": warehouse,
"posting_date": nowdate(),
"posting_time": nowtime()
}).get("qty_after_transaction") or 0
'item_code': item_code,
'warehouse': warehouse,
'posting_date': nowdate(),
'posting_time': nowtime()
}).get('qty_after_transaction') or 0
@frappe.whitelist()
def get_procedure_consumables(procedure_template):
return get_items('Clinical Procedure Item', procedure_template, 'Clinical Procedure Template')
@frappe.whitelist()
def set_stock_items(doc, stock_detail_parent, parenttype):
item_dict = get_item_dict("Clinical Procedure Item", stock_detail_parent, parenttype)
items = get_items('Clinical Procedure Item', stock_detail_parent, parenttype)
for d in item_dict:
for item in items:
se_child = doc.append('items')
se_child.item_code = d["item_code"]
se_child.item_name = d["item_name"]
se_child.uom = d["uom"]
se_child.stock_uom = d["stock_uom"]
se_child.qty = flt(d["qty"])
se_child.item_code = item.item_code
se_child.item_name = item.item_name
se_child.uom = item.uom
se_child.stock_uom = item.stock_uom
se_child.qty = flt(item.qty)
# in stock uom
se_child.transfer_qty = flt(d["transfer_qty"])
se_child.conversion_factor = flt(d["conversion_factor"])
if d["batch_no"]:
se_child.batch_no = d["batch_no"]
if parenttype == "Clinical Procedure Template":
se_child.invoice_separately_as_consumables = d["invoice_separately_as_consumables"]
se_child.transfer_qty = flt(item.transfer_qty)
se_child.conversion_factor = flt(item.conversion_factor)
if item.batch_no:
se_child.batch_no = item.batch_no
if parenttype == 'Clinical Procedure Template':
se_child.invoice_separately_as_consumables = item.invoice_separately_as_consumables
return doc
def get_item_dict(table, parent, parenttype):
query = """select * from `tab{table}` where parent = '{parent}' and parenttype = '{parenttype}' """
return frappe.db.sql(query.format(table=table, parent=parent, parenttype=parenttype), as_dict=True)
def get_items(table, parent, parenttype):
items = frappe.db.get_all(table, filters={
'parent': parent,
'parenttype': parenttype
}, fields=['*'])
def create_stock_entry(doc):
stock_entry = frappe.new_doc("Stock Entry")
stock_entry = set_stock_items(stock_entry, doc.name, "Clinical Procedure")
stock_entry.purpose = "Material Issue"
return items
@frappe.whitelist()
def make_stock_entry(doc):
stock_entry = frappe.new_doc('Stock Entry')
stock_entry = set_stock_items(stock_entry, doc.name, 'Clinical Procedure')
stock_entry.stock_entry_type = 'Material Issue'
stock_entry.from_warehouse = doc.warehouse
stock_entry.company = doc.company
expense_account = get_account(None, "expense_account", "Healthcare Settings", doc.company)
expense_account = get_account(None, 'expense_account', 'Healthcare Settings', doc.company)
for item_line in stock_entry.items:
cost_center = frappe.get_cached_value('Company', doc.company, 'cost_center')
#item_line.s_warehouse = warehouse #deaful source warehouse set, stock entry to copy to lines
item_line.cost_center = cost_center
#if not expense_account:
# expense_account = frappe.db.get_value("Item", item_line.item_code, "expense_account")
item_line.expense_account = expense_account
stock_entry.insert(ignore_permissions = True)
stock_entry.save(ignore_permissions=True)
stock_entry.submit()
return stock_entry.name
@frappe.whitelist()
def create_procedure(appointment):
appointment = frappe.get_doc("Patient Appointment",appointment)
procedure = frappe.new_doc("Clinical Procedure")
procedure.appointment = appointment.name
procedure.patient = appointment.patient
procedure.patient_age = appointment.patient_age
procedure.patient_sex = appointment.patient_sex
procedure.procedure_template = appointment.procedure_template
procedure.prescription = appointment.procedure_prescription
procedure.practitioner = appointment.practitioner
procedure.invoiced = appointment.invoiced
procedure.medical_department = appointment.department
procedure.start_date = appointment.appointment_date
procedure.start_time = appointment.appointment_time
procedure.notes = appointment.notes
procedure.service_unit = appointment.service_unit
procedure.company = appointment.company
consume_stock = frappe.db.get_value("Clinical Procedure Template", appointment.procedure_template, "consume_stock")
if consume_stock == 1:
procedure.consume_stock = True
warehouse = False
if appointment.service_unit:
warehouse = frappe.db.get_value("Healthcare Service Unit", appointment.service_unit, "warehouse")
if not warehouse:
warehouse = frappe.db.get_value("Stock Settings", None, "default_warehouse")
if warehouse:
procedure.warehouse = warehouse
return procedure.as_dict()
def make_procedure(source_name, target_doc=None):
def set_missing_values(source, target):
consume_stock = frappe.db.get_value('Clinical Procedure Template', source.procedure_template, 'consume_stock')
if consume_stock:
target.consume_stock = 1
warehouse = None
if source.service_unit:
warehouse = frappe.db.get_value('Healthcare Service Unit', source.service_unit, 'warehouse')
if not warehouse:
warehouse = frappe.db.get_value('Stock Settings', None, 'default_warehouse')
if warehouse:
target.warehouse = warehouse
set_stock_items(target, source.procedure_template, 'Clinical Procedure Template')
doc = get_mapped_doc('Patient Appointment', source_name, {
'Patient Appointment': {
'doctype': 'Clinical Procedure',
'field_map': [
['appointment', 'name'],
['patient', 'patient'],
['patient_age', 'patient_age'],
['patient_sex', 'patient_sex'],
['procedure_template', 'procedure_template'],
['prescription', 'procedure_prescription'],
['practitioner', 'practitioner'],
['medical_department', 'department'],
['start_date', 'appointment_date'],
['start_time', 'appointment_time'],
['notes', 'notes'],
['service_unit', 'service_unit'],
['company', 'company'],
['invoiced', 'invoiced']
]
}
}, target_doc, set_missing_values)
return doc
def insert_clinical_procedure_to_medical_record(doc):
subject = cstr(doc.procedure_template)
if doc.practitioner:
subject += " "+doc.practitioner
subject += ' ' + doc.practitioner
if subject and doc.notes:
subject += "<br/>"+doc.notes
subject += '<br/>' + doc.notes
medical_record = frappe.new_doc("Patient Medical Record")
medical_record = frappe.new_doc('Patient Medical Record')
medical_record.patient = doc.patient
medical_record.subject = subject
medical_record.status = "Open"
medical_record.status = 'Open'
medical_record.communication_date = doc.start_date
medical_record.reference_doctype = "Clinical Procedure"
medical_record.reference_doctype = 'Clinical Procedure'
medical_record.reference_name = doc.name
medical_record.reference_owner = doc.owner
medical_record.save(ignore_permissions=True)

View File

@ -0,0 +1,11 @@
frappe.listview_settings['Clinical Procedure'] = {
get_indicator: function(doc) {
var colors = {
'Completed': 'green',
'In Progress': 'orange',
'Pending': 'orange',
'Cancelled': 'grey'
};
return [__(doc.status), colors[doc.status], 'status,=,' + doc.status];
}
};

View File

@ -4,6 +4,60 @@
from __future__ import unicode_literals
import unittest
import frappe
from erpnext.healthcare.doctype.patient_appointment.test_patient_appointment import create_healthcare_docs, create_clinical_procedure_template
class TestClinicalProcedure(unittest.TestCase):
pass
def test_procedure_template_item(self):
patient, medical_department, practitioner = create_healthcare_docs()
procedure_template = create_clinical_procedure_template()
self.assertTrue(frappe.db.exists('Item', procedure_template.item))
procedure_template.disabled = 1
procedure_template.save()
self.assertEquals(frappe.db.get_value('Item', procedure_template.item, 'disabled'), 1)
def test_consumables(self):
patient, medical_department, practitioner = create_healthcare_docs()
procedure_template = create_clinical_procedure_template()
procedure_template.allow_stock_consumption = 1
consumable = create_consumable()
procedure_template.append('items', {
'item_code': consumable.item_code,
'qty': 1,
'uom': consumable.stock_uom,
'stock_uom': consumable.stock_uom
})
procedure_template.save()
procedure = create_procedure(procedure_template, patient, practitioner)
result = procedure.start_procedure()
if result == 'insufficient stock':
procedure.make_material_receipt(submit=True)
result = procedure.start_procedure()
self.assertEqual(procedure.status, 'In Progress')
result = procedure.complete_procedure()
# check consumption
self.assertTrue(frappe.db.exists('Stock Entry', result))
def create_consumable():
if frappe.db.exists('Item', 'Syringe'):
return frappe.get_doc('Item', 'Syringe')
consumable = frappe.new_doc('Item')
consumable.item_code = 'Syringe'
consumable.item_group = '_Test Item Group'
consumable.stock_uom = 'Nos'
consumable.valuation_rate = 5.00
consumable.save()
return consumable
def create_procedure(procedure_template, patient, practitioner):
procedure = frappe.new_doc('Clinical Procedure')
procedure.procedure_template = procedure_template.name
procedure.patient = patient
procedure.practitioner = practitioner
procedure.consume_stock = procedure_template.allow_stock_consumption
procedure.items = procedure_template.items
procedure.warehouse = frappe.db.get_single_value('Stock Settings', 'default_warehouse')
procedure.submit()
return procedure

View File

@ -1,431 +1,123 @@
{
"allow_copy": 0,
"allow_events_in_timeline": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"beta": 1,
"creation": "2017-10-05 16:15:10.876952",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "",
"editable_grid": 1,
"engine": "InnoDB",
"actions": [],
"beta": 1,
"creation": "2017-10-05 16:15:10.876952",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"item_code",
"item_name",
"qty",
"barcode",
"uom",
"invoice_separately_as_consumables",
"column_break_5",
"batch_no",
"conversion_factor",
"stock_uom",
"transfer_qty",
"actual_qty"
],
"fields": [
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 1,
"collapsible": 0,
"columns": 3,
"fieldname": "item_code",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 1,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 1,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Item",
"length": 0,
"no_copy": 0,
"options": "Item",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 1,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
"bold": 1,
"columns": 3,
"fieldname": "item_code",
"fieldtype": "Link",
"ignore_user_permissions": 1,
"in_global_search": 1,
"in_list_view": 1,
"label": "Item",
"options": "Item",
"reqd": 1,
"search_index": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "barcode",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Barcode",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
"fieldname": "barcode",
"fieldtype": "Data",
"label": "Barcode"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "item_name",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Item Name",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
"fieldname": "item_name",
"fieldtype": "Data",
"in_list_view": 1,
"label": "Item Name",
"read_only": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "qty",
"fieldtype": "Float",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Quantity",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
"fieldname": "qty",
"fieldtype": "Float",
"in_list_view": 1,
"label": "Quantity",
"reqd": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "uom",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "UOM",
"length": 0,
"no_copy": 0,
"options": "UOM",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
"fieldname": "uom",
"fieldtype": "Link",
"in_list_view": 1,
"label": "UOM",
"options": "UOM",
"reqd": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "invoice_separately_as_consumables",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Invoice Separately as Consumables",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
"default": "0",
"fieldname": "invoice_separately_as_consumables",
"fieldtype": "Check",
"in_list_view": 1,
"label": "Invoice Separately as Consumables"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_5",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
"fieldname": "column_break_5",
"fieldtype": "Column Break"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "batch_no",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Batch",
"length": 0,
"no_copy": 0,
"options": "Batch",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
"fieldname": "batch_no",
"fieldtype": "Link",
"label": "Batch",
"options": "Batch"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "conversion_factor",
"fieldtype": "Float",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Conversion Factor",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
"fieldname": "conversion_factor",
"fieldtype": "Float",
"label": "Conversion Factor",
"read_only": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "stock_uom",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Stock UOM",
"length": 0,
"no_copy": 0,
"options": "UOM",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
"fieldname": "stock_uom",
"fieldtype": "Link",
"label": "Stock UOM",
"options": "UOM",
"read_only": 1,
"reqd": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "transfer_qty",
"fieldtype": "Float",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Transfer Qty",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
"fieldname": "transfer_qty",
"fieldtype": "Float",
"label": "Transfer Qty",
"read_only": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "actual_qty",
"fieldtype": "Float",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Actual Qty (at source/target)",
"length": 0,
"no_copy": 1,
"permlevel": 0,
"precision": "",
"print_hide": 1,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 1,
"set_only_once": 0,
"translatable": 0,
"unique": 0
"fieldname": "actual_qty",
"fieldtype": "Float",
"label": "Actual Qty (at source/target)",
"no_copy": 1,
"print_hide": 1,
"read_only": 1,
"search_index": 1
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 1,
"max_attachments": 0,
"modified": "2018-11-04 03:33:16.833884",
"modified_by": "Administrator",
"module": "Healthcare",
"name": "Clinical Procedure Item",
"name_case": "",
"owner": "Administrator",
"permissions": [],
"quick_entry": 1,
"read_only": 0,
"read_only_onload": 0,
"restrict_to_domain": "Healthcare",
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1,
"track_seen": 0,
"track_views": 0
],
"istable": 1,
"links": [],
"modified": "2020-03-01 15:34:54.226722",
"modified_by": "Administrator",
"module": "Healthcare",
"name": "Clinical Procedure Item",
"owner": "Administrator",
"permissions": [],
"quick_entry": 1,
"restrict_to_domain": "Healthcare",
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1
}

View File

@ -3,170 +3,141 @@
frappe.ui.form.on('Clinical Procedure Template', {
template: function(frm) {
if(!frm.doc.item_code)
frm.set_value("item_code", frm.doc.template);
if(!frm.doc.description)
frm.set_value("description", frm.doc.template);
if (!frm.doc.item_code)
frm.set_value('item_code', frm.doc.template);
if (!frm.doc.description)
frm.set_value('description', frm.doc.template);
mark_change_in_item(frm);
},
rate: function(frm) {
mark_change_in_item(frm);
},
is_billable: function (frm) {
mark_change_in_item(frm);
},
item_group: function(frm) {
mark_change_in_item(frm);
},
description: function(frm) {
mark_change_in_item(frm);
},
medical_department: function(frm) {
mark_change_in_item(frm);
},
refresh: function(frm) {
frm.fields_dict["items"].grid.set_column_disp("barcode", false);
frm.fields_dict["items"].grid.set_column_disp("batch_no", false);
cur_frm.set_df_property("item_code", "read_only", frm.doc.__islocal ? 0 : 1);
if(!frm.doc.__islocal) {
frm.fields_dict['items'].grid.set_column_disp('barcode', false);
frm.fields_dict['items'].grid.set_column_disp('batch_no', false);
if (!frm.doc.__islocal) {
cur_frm.add_custom_button(__('Change Item Code'), function() {
change_template_code(frm.doc);
} );
if(frm.doc.disabled == 1){
cur_frm.add_custom_button(__('Enable Template'), function() {
enable_template(frm.doc);
} );
}
else{
cur_frm.add_custom_button(__('Disable Template'), function() {
disable_template(frm.doc);
} );
}
});
}
}
});
var mark_change_in_item = function(frm) {
if(!frm.doc.__islocal){
let mark_change_in_item = function(frm) {
if (!frm.doc.__islocal) {
frm.doc.change_in_item = 1;
}
};
var disable_template = function(doc){
frappe.call({
method: "erpnext.healthcare.doctype.clinical_procedure_template.clinical_procedure_template.disable_enable_template",
args: {status: 1, name: doc.name, item_code: doc.item_code, is_billable: doc.is_billable},
callback: function(){
cur_frm.reload_doc();
}
});
};
var enable_template = function(doc){
frappe.call({
method: "erpnext.healthcare.doctype.clinical_procedure_template.clinical_procedure_template.disable_enable_template",
args: {status: 0, name: doc.name, item_code: doc.item_code, is_billable: doc.is_billable},
callback: function(){
cur_frm.reload_doc();
}
});
};
var change_template_code = function(doc){
var d = new frappe.ui.Dialog({
title:__("Change Template Code"),
let change_template_code = function(doc) {
let d = new frappe.ui.Dialog({
title:__('Change Item Code'),
fields:[
{
"fieldtype": "Data",
"label": "Template Code",
"fieldname": "Item Code",
reqd:1
},
{
"fieldtype": "Button",
"label": __("Change Code"),
click: function() {
var values = d.get_values();
if(!values)
return;
change_item_code_from_template(values["Item Code"], doc);
d.hide();
}
'fieldtype': 'Data',
'label': 'Item Code',
'fieldname': 'item_code',
reqd: 1
}
]
],
primary_action: function() {
let values = d.get_values();
if (values) {
frappe.call({
'method': 'erpnext.healthcare.doctype.clinical_procedure_template.clinical_procedure_template.change_item_code_from_template',
'args': {item_code: values.item_code, doc: doc},
callback: function () {
cur_frm.reload_doc();
frappe.show_alert({
message: 'Item Code renamed successfully',
indicator: 'green'
});
}
});
}
d.hide();
},
primary_action_label: __('Change Item Code')
});
d.show();
d.set_values({
'Item Code': doc.item_code
});
};
var change_item_code_from_template = function(item_code, doc){
frappe.call({
"method": "erpnext.healthcare.doctype.clinical_procedure_template.clinical_procedure_template.change_item_code_from_template",
"args": {item_code: item_code, doc: doc},
callback: function () {
cur_frm.reload_doc();
frappe.show_alert({
message: "Item Code renamed successfully",
indicator: "green"
});
}
d.set_values({
'item_code': doc.item_code
});
};
frappe.ui.form.on('Clinical Procedure Item', {
qty: function(frm, cdt, cdn){
var d = locals[cdt][cdn];
frappe.model.set_value(cdt, cdn, "transfer_qty", d.qty*d.conversion_factor);
qty: function(frm, cdt, cdn) {
let d = locals[cdt][cdn];
frappe.model.set_value(cdt, cdn, 'transfer_qty', d.qty * d.conversion_factor);
},
uom: function(doc, cdt, cdn){
var d = locals[cdt][cdn];
if(d.uom && d.item_code){
let d = locals[cdt][cdn];
if (d.uom && d.item_code) {
return frappe.call({
method: "erpnext.stock.doctype.stock_entry.stock_entry.get_uom_details",
method: 'erpnext.stock.doctype.stock_entry.stock_entry.get_uom_details',
args: {
item_code: d.item_code,
uom: d.uom,
qty: d.qty
},
callback: function(r) {
if(r.message) {
if (r.message) {
frappe.model.set_value(cdt, cdn, r.message);
}
}
});
}
},
item_code: function(frm, cdt, cdn) {
var d = locals[cdt][cdn];
if(d.item_code) {
let d = locals[cdt][cdn];
if (d.item_code) {
let args = {
'item_code' : d.item_code,
'transfer_qty' : d.transfer_qty,
'company' : frm.doc.company,
'quantity' : d.qty
'quantity' : d.qty
};
return frappe.call({
doc: frm.doc,
method: "get_item_details",
args: args,
method: 'erpnext.healthcare.doctype.clinical_procedure_template.clinical_procedure_template.get_item_details',
args: {args: args},
callback: function(r) {
if(r.message) {
var d = locals[cdt][cdn];
$.each(r.message, function(k, v){
if (r.message) {
let d = locals[cdt][cdn];
$.each(r.message, function(k, v) {
d[k] = v;
});
refresh_field("items");
refresh_field('items');
}
}
});
}
}
});
// List Stock items
cur_frm.set_query("item_code", "items", function() {
cur_frm.set_query('item_code', 'items', function() {
return {
filters: {
is_stock_item:1

View File

@ -7,126 +7,114 @@ import frappe, json
from frappe import _
from frappe.model.document import Document
from frappe.model.rename_doc import rename_doc
from frappe.utils import nowdate
class ClinicalProcedureTemplate(Document):
def on_update(self):
#Item and Price List update --> if (change_in_item)
if(self.change_in_item and self.is_billable == 1 and self.item):
updating_item(self)
if(self.rate != 0.0):
updating_rate(self)
elif(self.is_billable == 0 and self.item):
frappe.db.set_value("Item",self.item,"disabled",1)
frappe.db.set_value(self.doctype,self.name,"change_in_item",0)
self.reload()
def validate(self):
self.enable_disable_item()
def after_insert(self):
create_item_from_template(self)
#Call before delete the template
def on_trash(self):
if(self.item):
try:
frappe.delete_doc("Item",self.item)
except Exception:
frappe.throw(_("""Not permitted. Please disable the Procedure Template"""))
def on_update(self):
if self.change_in_item:
self.update_item_and_item_price()
def get_item_details(self, args=None):
item = frappe.db.sql("""select stock_uom, item_name
from `tabItem`
where name = %s
and disabled=0
and (end_of_life is null or end_of_life='0000-00-00' or end_of_life > %s)""",
(args.get('item_code'), nowdate()), as_dict = 1)
if not item:
frappe.throw(_("Item {0} is not active or end of life has been reached").format(args.get('item_code')))
def enable_disable_item(self):
if self.is_billable:
if self.disabled:
frappe.db.set_value('Item', self.item, 'disabled', 1)
else:
frappe.db.set_value('Item', self.item, 'disabled', 0)
item = item[0]
def update_item_and_item_price(self):
if self.is_billable and self.item:
item_doc = frappe.get_doc('Item', {'item_code': self.item})
item_doc.item_name = self.template
item_doc.item_group = self.item_group
item_doc.description = self.description
item_doc.disabled = 0
item_doc.save(ignore_permissions=True)
ret = {
'uom' : item.stock_uom,
'stock_uom' : item.stock_uom,
'item_name' : item.item_name,
'quantity' : 0,
'transfer_qty' : 0,
'conversion_factor' : 1
}
return ret
if self.rate:
item_price = frappe.get_doc('Item Price', {'item_code': self.item})
item_price.item_name = self.template
item_price.price_list_rate = self.rate
item_price.save()
def updating_item(self):
frappe.db.sql("""update `tabItem` set item_name=%s, item_group=%s, disabled=0,
description=%s, modified=NOW() where item_code=%s""",
(self.template, self.item_group , self.description, self.item))
def updating_rate(self):
frappe.db.sql("""update `tabItem Price` set item_name=%s, price_list_rate=%s, modified=NOW() where
item_code=%s""",(self.template, self.rate, self.item))
elif not self.is_billable and self.item:
frappe.db.set_value('Item', self.item, 'disabled', 1)
self.db_set('change_in_item', 0)
@frappe.whitelist()
def get_item_details(args=None):
if not isinstance(args, dict):
args = json.loads(args)
item = frappe.db.get_all('Item',
filters={
'disabled': 0,
'name': args.get('item_code')
},
fields=['stock_uom', 'item_name']
)
if not item:
frappe.throw(_('Item {0} is not active').format(args.get('item_code')))
item = item[0]
ret = {
'uom': item.stock_uom,
'stock_uom': item.stock_uom,
'item_name': item.item_name,
'qty': 1,
'transfer_qty': 0,
'conversion_factor': 1
}
return ret
def create_item_from_template(doc):
disabled = 1
if(doc.is_billable == 1):
disabled = doc.disabled
if doc.is_billable and not doc.disabled:
disabled = 0
#insert item
item = frappe.get_doc({
"doctype": "Item",
"item_code": doc.template,
"item_name":doc.template,
"item_group": doc.item_group,
"description":doc.description,
"is_sales_item": 1,
"is_service_item": 1,
"is_purchase_item": 0,
"is_stock_item": 0,
"show_in_website": 0,
"is_pro_applicable": 0,
"disabled": disabled,
"stock_uom": "Unit"
}).insert(ignore_permissions=True)
item = frappe.get_doc({
'doctype': 'Item',
'item_code': doc.template,
'item_name':doc.template,
'item_group': doc.item_group,
'description':doc.description,
'is_sales_item': 1,
'is_service_item': 1,
'is_purchase_item': 0,
'is_stock_item': 0,
'show_in_website': 0,
'is_pro_applicable': 0,
'disabled': disabled,
'stock_uom': 'Unit'
}).insert(ignore_permissions=True, ignore_mandatory=True)
#insert item price
#get item price list to insert item price
if(doc.rate != 0.0):
price_list_name = frappe.db.get_value("Price List", {"selling": 1})
if(doc.rate):
make_item_price(item.name, price_list_name, doc.rate)
else:
make_item_price(item.name, price_list_name, 0.0)
#Set item to the template
frappe.db.set_value("Clinical Procedure Template", doc.name, "item", item.name)
make_item_price(item.name, doc.rate)
doc.db_set('item', item.name)
doc.reload() #refresh the doc after insert.
def make_item_price(item, price_list_name, item_price):
def make_item_price(item, item_price):
price_list_name = frappe.db.get_value('Price List', {'selling': 1})
frappe.get_doc({
"doctype": "Item Price",
"price_list": price_list_name,
"item_code": item,
"price_list_rate": item_price
}).insert(ignore_permissions=True)
'doctype': 'Item Price',
'price_list': price_list_name,
'item_code': item,
'price_list_rate': item_price
}).insert(ignore_permissions=True, ignore_mandatory=True)
@frappe.whitelist()
def change_item_code_from_template(item_code, doc):
args = json.loads(doc)
doc = frappe._dict(args)
doc = frappe._dict(json.loads(doc))
if(frappe.db.exists({
"doctype": "Item",
"item_code": item_code})):
frappe.throw(_("Code {0} already exist").format(item_code))
if frappe.db.exists('Item', {'item_code': item_code}):
frappe.throw(_('Item with Item Code {0} already exists').format(item_code))
else:
rename_doc("Item", doc.item_code, item_code, ignore_permissions=True)
frappe.db.set_value("Clinical Procedure Template", doc.name, "item_code", item_code)
rename_doc('Item', doc.item_code, item_code, ignore_permissions=True)
frappe.db.set_value('Clinical Procedure Template', doc.name, 'item_code', item_code)
return
@frappe.whitelist()
def disable_enable_template(status, name, item_code):
frappe.db.set_value("Clinical Procedure Template", name, "disabled", status)
if (frappe.db.exists({ #set Item's disabled field to status
"doctype": "Item",
"item_code": item_code})):
frappe.db.set_value("Item", item_code, "disabled", status)
return

View File

@ -0,0 +1,13 @@
from __future__ import unicode_literals
from frappe import _
def get_data():
return {
'fieldname': 'procedure_template',
'transactions': [
{
'label': _('Consultations'),
'items': ['Clinical Procedure']
}
]
}

View File

@ -1,140 +1,56 @@
{
"allow_copy": 1,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"beta": 1,
"creation": "2017-06-22 13:09:23.159579",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "",
"editable_grid": 1,
"engine": "InnoDB",
"actions": [],
"allow_copy": 1,
"beta": 1,
"creation": "2017-06-22 13:09:23.159579",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"medical_code",
"code",
"description"
],
"fields": [
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "medical_code",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 1,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Medical Code",
"length": 0,
"no_copy": 0,
"options": "Medical Code",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
"fieldname": "medical_code",
"fieldtype": "Link",
"ignore_user_permissions": 1,
"in_list_view": 1,
"label": "Medical Code",
"options": "Medical Code",
"reqd": 1
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_from": "medical_code.code",
"fieldname": "code",
"fieldtype": "Read Only",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 1,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Code",
"length": 0,
"no_copy": 0,
"options": "",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
"fieldname": "code",
"fieldtype": "Data",
"ignore_xss_filter": 1,
"in_list_view": 1,
"label": "Code",
"read_only": 1
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_from": "medical_code.description",
"fieldname": "description",
"fieldtype": "Read Only",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 1,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Description",
"length": 0,
"no_copy": 0,
"options": "",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
"fieldname": "description",
"fieldtype": "Small Text",
"ignore_xss_filter": 1,
"in_list_view": 1,
"label": "Description",
"read_only": 1
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 1,
"max_attachments": 0,
"modified": "2018-05-16 22:43:27.047479",
"modified_by": "Administrator",
"module": "Healthcare",
"name": "Codification Table",
"name_case": "",
"owner": "Administrator",
"permissions": [],
"quick_entry": 1,
"read_only": 0,
"read_only_onload": 0,
"restrict_to_domain": "Healthcare",
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1,
"track_seen": 0
],
"istable": 1,
"links": [],
"modified": "2020-02-26 13:17:49.016293",
"modified_by": "Administrator",
"module": "Healthcare",
"name": "Codification Table",
"owner": "Administrator",
"permissions": [],
"quick_entry": 1,
"restrict_to_domain": "Healthcare",
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1
}

View File

@ -1,381 +1,116 @@
{
"allow_copy": 1,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"beta": 1,
"creation": "2016-09-16 16:41:45.533374",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "Document",
"editable_grid": 1,
"engine": "InnoDB",
"actions": [],
"allow_copy": 1,
"beta": 1,
"creation": "2016-09-16 16:41:45.533374",
"doctype": "DocType",
"document_type": "Document",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"drug_code",
"drug_name",
"dosage",
"period",
"dosage_form",
"column_break_7",
"comment",
"usage_interval",
"interval",
"interval_uom",
"update_schedule"
],
"fields": [
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "drug_code",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 1,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Drug Code",
"length": 0,
"no_copy": 0,
"options": "Item",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
"fieldname": "drug_code",
"fieldtype": "Link",
"ignore_user_permissions": 1,
"in_list_view": 1,
"label": "Drug",
"options": "Item",
"reqd": 1
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "drug_name",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Description/Strength",
"length": 0,
"no_copy": 0,
"options": "",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
"fetch_from": "drug_code.item_name",
"fieldname": "drug_name",
"fieldtype": "Data",
"in_list_view": 1,
"label": "Drug Name / Description"
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "dosage",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 1,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Dosage",
"length": 0,
"no_copy": 0,
"options": "Prescription Dosage",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
"fieldname": "dosage",
"fieldtype": "Link",
"ignore_user_permissions": 1,
"in_list_view": 1,
"label": "Dosage",
"options": "Prescription Dosage"
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "period",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 1,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Period",
"length": 0,
"no_copy": 0,
"options": "Prescription Duration",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
"fieldname": "period",
"fieldtype": "Link",
"ignore_user_permissions": 1,
"in_list_view": 1,
"label": "Period",
"options": "Prescription Duration"
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "dosage_form",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 1,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Dosage Form",
"length": 0,
"no_copy": 0,
"options": "Dosage Form",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
"fieldname": "dosage_form",
"fieldtype": "Link",
"ignore_user_permissions": 1,
"label": "Dosage Form",
"options": "Dosage Form"
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_7",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
"fieldname": "column_break_7",
"fieldtype": "Column Break"
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "comment",
"fieldtype": "Small Text",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 1,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Comment",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
"fieldname": "comment",
"fieldtype": "Small Text",
"ignore_xss_filter": 1,
"in_list_view": 1,
"label": "Comment"
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "0",
"fieldname": "use_interval",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Dosage by time interval",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
"depends_on": "use_interval",
"fieldname": "interval",
"fieldtype": "Int",
"in_list_view": 1,
"label": "Interval"
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "use_interval",
"fieldname": "interval",
"fieldtype": "Int",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Interval",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
"default": "1",
"fieldname": "update_schedule",
"fieldtype": "Check",
"hidden": 1,
"label": "Update Schedule",
"print_hide": 1,
"report_hide": 1
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "use_interval",
"fieldname": "in_every",
"fieldtype": "Select",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Interval UOM",
"length": 0,
"no_copy": 0,
"options": "\nHour\nDay",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
"depends_on": "use_interval",
"fieldname": "interval_uom",
"fieldtype": "Select",
"in_list_view": 1,
"label": "Interval UOM",
"options": "\nHour\nDay"
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "1",
"fieldname": "update_schedule",
"fieldtype": "Check",
"hidden": 1,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Update Schedule",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 1,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 1,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
"default": "0",
"fieldname": "usage_interval",
"fieldtype": "Check",
"label": "Dosage by Time Interval"
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 1,
"max_attachments": 0,
"modified": "2017-10-04 17:09:54.998517",
"modified_by": "Administrator",
"module": "Healthcare",
"name": "Drug Prescription",
"name_case": "",
"owner": "Administrator",
"permissions": [],
"quick_entry": 0,
"read_only": 0,
"read_only_onload": 0,
"restrict_to_domain": "Healthcare",
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 0,
"track_seen": 0
],
"istable": 1,
"links": [],
"modified": "2020-02-26 17:02:42.741338",
"modified_by": "Administrator",
"module": "Healthcare",
"name": "Drug Prescription",
"owner": "Administrator",
"permissions": [],
"restrict_to_domain": "Healthcare",
"sort_field": "modified",
"sort_order": "DESC"
}

View File

@ -12,21 +12,21 @@ class DrugPrescription(Document):
dosage = None
period = None
if(self.dosage):
dosage = frappe.get_doc("Prescription Dosage",self.dosage)
if self.dosage:
dosage = frappe.get_doc('Prescription Dosage', self.dosage)
for item in dosage.dosage_strength:
quantity += item.strength
if(self.period and self.interval):
period = frappe.get_doc("Prescription Duration",self.period)
if(self.interval < period.get_days()):
quantity = quantity*(period.get_days()/self.interval)
if self.period and self.interval:
period = frappe.get_doc('Prescription Duration', self.period)
if self.interval < period.get_days():
quantity = quantity * (period.get_days()/self.interval)
elif(self.interval and self.in_every and self.period):
period = frappe.get_doc("Prescription Duration",self.period)
interval_in = self.in_every
if(interval_in == 'Day' and (self.interval < period.get_days())):
elif self.interval and self.interval_uom and self.period:
period = frappe.get_doc('Prescription Duration', self.period)
interval_in = self.interval_uom
if interval_in == 'Day' and self.interval < period.get_days():
quantity = period.get_days()/self.interval
elif(interval_in == 'Hour' and (self.interval < period.get_hours())):
elif interval_in == 'Hour' and self.interval < period.get_hours():
quantity = period.get_hours()/self.interval
if quantity > 0:
return quantity

View File

@ -1,259 +1,134 @@
{
"allow_copy": 1,
"allow_guest_to_view": 0,
"allow_import": 1,
"allow_rename": 0,
"beta": 1,
"creation": "2017-01-05 10:56:29.564806",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "Setup",
"editable_grid": 1,
"engine": "InnoDB",
"actions": [],
"allow_copy": 1,
"allow_import": 1,
"beta": 1,
"creation": "2017-01-05 10:56:29.564806",
"doctype": "DocType",
"document_type": "Setup",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"practitioner",
"patient",
"column_break_3",
"status",
"section_break_5",
"section_break_3",
"max_visits",
"visited",
"ref_appointments",
"column_break_6",
"start_date",
"valid_till"
],
"fields": [
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "practitioner",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Healthcare Practitioner",
"length": 0,
"no_copy": 0,
"options": "Healthcare Practitioner",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 1,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
"fieldname": "practitioner",
"fieldtype": "Link",
"in_list_view": 1,
"label": "Healthcare Practitioner",
"options": "Healthcare Practitioner",
"read_only": 1,
"reqd": 1,
"search_index": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "patient",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Patient",
"length": 0,
"no_copy": 0,
"options": "Patient",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 1,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
"fieldname": "patient",
"fieldtype": "Link",
"in_list_view": 1,
"label": "Patient",
"options": "Patient",
"read_only": 1,
"reqd": 1,
"search_index": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "max_visit",
"fieldtype": "Int",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Max number of visit",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
"fieldname": "visited",
"fieldtype": "Int",
"label": "Visited yet",
"read_only": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "visited",
"fieldtype": "Int",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Visited yet",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
"fieldname": "valid_till",
"fieldtype": "Date",
"label": "Valid till",
"read_only": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "valid_till",
"fieldtype": "Date",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Valid till",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
"fieldname": "section_break_3",
"fieldtype": "Section Break",
"label": "Validity",
"read_only": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "ref_invoice",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Reference Inv",
"length": 0,
"no_copy": 0,
"options": "Sales Invoice",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
"fieldname": "column_break_6",
"fieldtype": "Column Break"
},
{
"fieldname": "max_visits",
"fieldtype": "Int",
"label": "Max number of visit",
"read_only": 1
},
{
"fieldname": "column_break_3",
"fieldtype": "Column Break"
},
{
"fieldname": "status",
"fieldtype": "Select",
"in_list_view": 1,
"in_standard_filter": 1,
"label": "Status",
"options": "Completed\nPending",
"read_only": 1
},
{
"fetch_from": "ref_appointment.appointment_date",
"fieldname": "start_date",
"fieldtype": "Date",
"label": "Start Date",
"read_only": 1
},
{
"fieldname": "ref_appointments",
"fieldtype": "Table MultiSelect",
"label": "Reference Appointments",
"options": "Fee Validity Reference",
"read_only": 1
},
{
"collapsible": 1,
"fieldname": "section_break_5",
"fieldtype": "Section Break"
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2018-07-16 12:43:45.635230",
"modified_by": "Administrator",
"module": "Healthcare",
"name": "Fee Validity",
"name_case": "",
"owner": "Administrator",
],
"in_create": 1,
"links": [],
"modified": "2020-03-17 20:25:06.487418",
"modified_by": "Administrator",
"module": "Healthcare",
"name": "Fee Validity",
"owner": "Administrator",
"permissions": [
{
"amend": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Healthcare Administrator",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "Healthcare Administrator",
"share": 1,
"write": 1
}
],
"quick_entry": 1,
"read_only": 0,
"read_only_onload": 0,
"restrict_to_domain": "Healthcare",
"search_fields": "practitioner, patient",
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"title_field": "practitioner",
"track_changes": 0,
"track_seen": 0
],
"quick_entry": 1,
"restrict_to_domain": "Healthcare",
"search_fields": "practitioner, patient",
"sort_field": "modified",
"sort_order": "DESC",
"title_field": "practitioner"
}

View File

@ -9,28 +9,55 @@ from frappe.utils import getdate
import datetime
class FeeValidity(Document):
pass
def validate(self):
self.update_status()
self.set_start_date()
def update_fee_validity(fee_validity, date, ref_invoice=None):
max_visit = frappe.db.get_value("Healthcare Settings", None, "max_visit")
valid_days = frappe.db.get_value("Healthcare Settings", None, "valid_days")
if not valid_days:
valid_days = 1
if not max_visit:
max_visit = 1
date = getdate(date)
valid_till = date + datetime.timedelta(days=int(valid_days))
fee_validity.max_visit = max_visit
def update_status(self):
if self.visited >= self.max_visits:
self.status = 'Completed'
else:
self.status = 'Pending'
def set_start_date(self):
self.start_date = getdate()
for appointment in self.ref_appointments:
appointment_date = frappe.db.get_value('Patient Appointment', appointment.appointment, 'appointment_date')
if getdate(appointment_date) < self.start_date:
self.start_date = getdate(appointment_date)
def create_fee_validity(appointment):
if not check_is_new_patient(appointment):
return
fee_validity = frappe.new_doc('Fee Validity')
fee_validity.practitioner = appointment.practitioner
fee_validity.patient = appointment.patient
fee_validity.max_visits = frappe.db.get_single_value('Healthcare Settings', 'max_visits') or 1
valid_days = frappe.db.get_single_value('Healthcare Settings', 'valid_days') or 1
fee_validity.visited = 1
fee_validity.valid_till = valid_till
fee_validity.ref_invoice = ref_invoice
fee_validity.valid_till = getdate(appointment.appointment_date) + datetime.timedelta(days=int(valid_days))
fee_validity.append('ref_appointments', {
'appointment': appointment.name
})
fee_validity.save(ignore_permissions=True)
return fee_validity
def check_is_new_patient(appointment):
validity_exists = frappe.db.exists('Fee Validity', {
'practitioner': appointment.practitioner,
'patient': appointment.patient
})
if validity_exists:
return False
def create_fee_validity(practitioner, patient, date, ref_invoice=None):
fee_validity = frappe.new_doc("Fee Validity")
fee_validity.practitioner = practitioner
fee_validity.patient = patient
fee_validity = update_fee_validity(fee_validity, date, ref_invoice)
return fee_validity
appointment_exists = frappe.db.get_all('Patient Appointment', {
'name': ('!=', appointment.name),
'status': ('!=', 'Cancelled'),
'patient': appointment.patient,
'practitioner': appointment.practitioner
})
if len(appointment_exists) and appointment_exists[0]:
return False
return True

View File

@ -5,100 +5,44 @@ from __future__ import unicode_literals
import frappe
import unittest
from frappe.utils.make_random import get_random
from frappe.utils import nowdate, add_days, getdate
from frappe.utils import nowdate, add_days
from erpnext.healthcare.doctype.patient_appointment.test_patient_appointment import create_healthcare_docs, create_appointment, create_healthcare_service_items
test_dependencies = ["Company"]
class TestFeeValidity(unittest.TestCase):
def test_fee_validity(self):
def setUp(self):
frappe.db.sql("""delete from `tabPatient Appointment`""")
frappe.db.sql("""delete from `tabFee Validity`""")
patient = get_random("Patient")
practitioner = get_random("Healthcare Practitioner")
department = get_random("Medical Department")
frappe.db.sql("""delete from `tabPatient`""")
if not patient:
patient = frappe.new_doc("Patient")
patient.patient_name = "_Test Patient"
patient.sex = "Male"
patient.save(ignore_permissions=True)
patient = patient.name
def test_fee_validity(self):
item = create_healthcare_service_items()
healthcare_settings = frappe.get_single("Healthcare Settings")
healthcare_settings.enable_free_follow_ups = 1
healthcare_settings.max_visits = 2
healthcare_settings.valid_days = 7
healthcare_settings.automate_appointment_invoicing = 1
healthcare_settings.op_consulting_charge_item = item
healthcare_settings.save(ignore_permissions=True)
patient, medical_department, practitioner = create_healthcare_docs()
if not department:
medical_department = frappe.new_doc("Medical Department")
medical_department.department = "_Test Medical Department"
medical_department.save(ignore_permissions=True)
department = medical_department.name
if not practitioner:
practitioner = frappe.new_doc("Healthcare Practitioner")
practitioner.first_name = "_Test Healthcare Practitioner"
practitioner.department = department
practitioner.save(ignore_permissions=True)
practitioner = practitioner.name
frappe.db.set_value("Healthcare Settings", None, "max_visit", 2)
frappe.db.set_value("Healthcare Settings", None, "valid_days", 7)
appointment = create_appointment(patient, practitioner, nowdate(), department)
# appointment should not be invoiced. Check Fee Validity created for new patient
appointment = create_appointment(patient, practitioner, nowdate())
invoiced = frappe.db.get_value("Patient Appointment", appointment.name, "invoiced")
self.assertEqual(invoiced, 0)
invoice_appointment(appointment)
appointment = create_appointment(patient, practitioner, add_days(nowdate(), 4), department)
invoiced = frappe.db.get_value("Patient Appointment", appointment.name, "invoiced")
self.assertTrue(invoiced)
appointment = create_appointment(patient, practitioner, add_days(nowdate(), 5), department)
# appointment should not be invoiced as it is within fee validity
appointment = create_appointment(patient, practitioner, add_days(nowdate(), 4))
invoiced = frappe.db.get_value("Patient Appointment", appointment.name, "invoiced")
self.assertEqual(invoiced, 0)
appointment = create_appointment(patient, practitioner, add_days(nowdate(), 10), department)
# appointment should be invoiced as it is within fee validity but the max_visits are exceeded
appointment = create_appointment(patient, practitioner, add_days(nowdate(), 5), invoice=1)
invoiced = frappe.db.get_value("Patient Appointment", appointment.name, "invoiced")
self.assertEqual(invoiced, 0)
self.assertEqual(invoiced, 1)
def create_appointment(patient, practitioner, appointment_date, department):
appointment = frappe.new_doc("Patient Appointment")
appointment.patient = patient
appointment.practitioner = practitioner
appointment.department = department
appointment.appointment_date = appointment_date
appointment.company = "_Test Company"
appointment.duration = 15
appointment.save(ignore_permissions=True)
return appointment
def invoice_appointment(appointment_doc):
if not appointment_doc.name:
return False
sales_invoice = frappe.new_doc("Sales Invoice")
sales_invoice.customer = frappe.get_value("Patient", appointment_doc.patient, "customer")
sales_invoice.due_date = getdate()
sales_invoice.is_pos = 0
sales_invoice.company = appointment_doc.company
sales_invoice.debit_to = "_Test Receivable - _TC"
create_invoice_items(appointment_doc, sales_invoice)
sales_invoice.save(ignore_permissions=True)
sales_invoice.submit()
def create_invoice_items(appointment, invoice):
item_line = invoice.append("items")
item_line.item_name = "Consulting Charges"
item_line.description = "Consulting Charges: " + appointment.practitioner
item_line.uom = "Nos"
item_line.conversion_factor = 1
item_line.income_account = "_Test Account Cost for Goods Sold - _TC"
item_line.cost_center = "_Test Cost Center - _TC"
item_line.rate = 250
item_line.amount = 250
item_line.qty = 1
item_line.reference_dt = "Patient Appointment"
item_line.reference_dn = appointment.name
return invoice
# appointment should be invoiced as it is not within fee validity and the max_visits are exceeded
appointment = create_appointment(patient, practitioner, add_days(nowdate(), 10), invoice=1)
invoiced = frappe.db.get_value("Patient Appointment", appointment.name, "invoiced")
self.assertEqual(invoiced, 1)

View File

@ -0,0 +1,32 @@
{
"actions": [],
"creation": "2020-03-13 16:08:42.859996",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"appointment"
],
"fields": [
{
"fieldname": "appointment",
"fieldtype": "Link",
"in_list_view": 1,
"label": "Patient Appointment",
"options": "Patient Appointment",
"reqd": 1
}
],
"istable": 1,
"links": [],
"modified": "2020-03-15 00:27:02.076470",
"modified_by": "Administrator",
"module": "Healthcare",
"name": "Fee Validity Reference",
"owner": "Administrator",
"permissions": [],
"quick_entry": 1,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1
}

View File

@ -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 FeeValidityReference(Document):
pass

View File

@ -4,7 +4,7 @@
frappe.ui.form.on('Healthcare Practitioner', {
setup: function(frm) {
frm.set_query('account', 'accounts', function(doc, cdt, cdn) {
var d = locals[cdt][cdn];
let d = locals[cdt][cdn];
return {
filters: {
'root_type': 'Income',
@ -16,23 +16,28 @@ frappe.ui.form.on('Healthcare Practitioner', {
},
refresh: function(frm) {
frappe.dynamic_link = {doc: frm.doc, fieldname: 'name', doctype: 'Healthcare Practitioner'};
if(!frm.is_new()) {
if (!frm.is_new()) {
frappe.contacts.render_address_and_contact(frm);
} else {
frappe.contacts.clear_address_and_contact(frm);
}
frm.set_query("service_unit", "practitioner_schedules", function(){
frm.set_query('service_unit', 'practitioner_schedules', function(){
return {
filters: {
"is_group": false,
"allow_appointments": true
'is_group': false,
'allow_appointments': true
}
};
});
set_query_service_item(frm, 'inpatient_visit_charge_item');
set_query_service_item(frm, 'op_consulting_charge_item');
}
});
var set_query_service_item = function(frm, service_item_field) {
let set_query_service_item = function(frm, service_item_field) {
frm.set_query(service_item_field, function() {
return {
filters: {
@ -43,62 +48,62 @@ var set_query_service_item = function(frm, service_item_field) {
});
};
frappe.ui.form.on("Healthcare Practitioner", "user_id",function(frm) {
if(frm.doc.user_id){
frappe.ui.form.on('Healthcare Practitioner', 'user_id',function(frm) {
if (frm.doc.user_id) {
frappe.call({
"method": "frappe.client.get",
'method': 'frappe.client.get',
args: {
doctype: "User",
doctype: 'User',
name: frm.doc.user_id
},
callback: function (data) {
frappe.model.get_value('Employee', {'user_id': frm.doc.user_id}, 'name',
function(data) {
if(data){
if(!frm.doc.employee || frm.doc.employee != data.name)
frappe.model.set_value(frm.doctype,frm.docname, "employee", data.name);
}else{
frappe.model.set_value(frm.doctype,frm.docname, "employee", "");
if (data) {
if (!frm.doc.employee || frm.doc.employee != data.name)
frappe.model.set_value(frm.doctype, frm.docname, 'employee', data.name);
} else {
frappe.model.set_value(frm.doctype, frm.docname, 'employee', '');
}
}
);
if(!frm.doc.first_name || frm.doc.first_name != data.message.first_name)
frappe.model.set_value(frm.doctype,frm.docname, "first_name", data.message.first_name);
if(!frm.doc.middle_name || frm.doc.middle_name != data.message.middle_name)
frappe.model.set_value(frm.doctype,frm.docname, "middle_name", data.message.middle_name);
if(!frm.doc.last_name || frm.doc.last_name != data.message.last_name)
frappe.model.set_value(frm.doctype,frm.docname, "last_name", data.message.last_name);
if(!frm.doc.mobile_phone || frm.doc.mobile_phone != data.message.mobile_no)
frappe.model.set_value(frm.doctype,frm.docname, "mobile_phone", data.message.mobile_no);
if (!frm.doc.first_name || frm.doc.first_name != data.message.first_name)
frappe.model.set_value(frm.doctype,frm.docname, 'first_name', data.message.first_name);
if (!frm.doc.middle_name || frm.doc.middle_name != data.message.middle_name)
frappe.model.set_value(frm.doctype,frm.docname, 'middle_name', data.message.middle_name);
if (!frm.doc.last_name || frm.doc.last_name != data.message.last_name)
frappe.model.set_value(frm.doctype,frm.docname, 'last_name', data.message.last_name);
if (!frm.doc.mobile_phone || frm.doc.mobile_phone != data.message.mobile_no)
frappe.model.set_value(frm.doctype,frm.docname, 'mobile_phone', data.message.mobile_no);
}
});
}
});
frappe.ui.form.on("Healthcare Practitioner", "employee", function(frm) {
if(frm.doc.employee){
frappe.ui.form.on('Healthcare Practitioner', 'employee', function(frm) {
if (frm.doc.employee){
frappe.call({
"method": "frappe.client.get",
'method': 'frappe.client.get',
args: {
doctype: "Employee",
doctype: 'Employee',
name: frm.doc.employee
},
callback: function (data) {
if(!frm.doc.user_id || frm.doc.user_id != data.message.user_id)
frm.set_value("user_id", data.message.user_id);
if(!frm.doc.designation || frm.doc.designation != data.message.designation)
frappe.model.set_value(frm.doctype,frm.docname, "designation", data.message.designation);
if(!frm.doc.first_name || !frm.doc.user_id){
frappe.model.set_value(frm.doctype,frm.docname, "first_name", data.message.employee_name);
frappe.model.set_value(frm.doctype,frm.docname, "middle_name", "");
frappe.model.set_value(frm.doctype,frm.docname, "last_name", "");
if (!frm.doc.user_id || frm.doc.user_id != data.message.user_id)
frm.set_value('user_id', data.message.user_id);
if (!frm.doc.designation || frm.doc.designation != data.message.designation)
frappe.model.set_value(frm.doctype,frm.docname, 'designation', data.message.designation);
if (!frm.doc.first_name || !frm.doc.user_id){
frappe.model.set_value(frm.doctype,frm.docname, 'first_name', data.message.first_name);
frappe.model.set_value(frm.doctype,frm.docname, 'middle_name', '');
frappe.model.set_value(frm.doctype,frm.docname, 'last_name', data.message.last_name);
}
if(!frm.doc.mobile_phone || !frm.doc.user_id)
frappe.model.set_value(frm.doctype,frm.docname, "mobile_phone", data.message.cell_number);
if(!frm.doc.address || frm.doc.address != data.message.current_address)
frappe.model.set_value(frm.doctype,frm.docname, "address", data.message.current_address);
if (!frm.doc.mobile_phone || !frm.doc.user_id)
frappe.model.set_value(frm.doctype,frm.docname, 'mobile_phone', data.message.cell_number);
if (!frm.doc.address || frm.doc.address != data.message.current_address)
frappe.model.set_value(frm.doctype,frm.docname, 'address', data.message.current_address);
}
});
}

View File

@ -5,10 +5,10 @@
from __future__ import unicode_literals
import frappe
from frappe.model.document import Document
from frappe import throw, _
from frappe.utils import cstr
from frappe import _
from erpnext.accounts.party import validate_party_accounts
from frappe.contacts.address_and_contact import load_address_and_contact, delete_contact_and_address
from frappe.model.naming import append_number_if_name_exists
from frappe.desk.reportview import build_match_conditions, get_filters_cond
class HealthcarePractitioner(Document):
@ -16,63 +16,66 @@ class HealthcarePractitioner(Document):
load_address_and_contact(self)
def autoname(self):
# practitioner first_name and last_name
self.name = " ".join(filter(None,
[cstr(self.get(f)).strip() for f in ["first_name","middle_name","last_name"]]))
# concat first and last name
self.name = self.practitioner_name
if frappe.db.exists('Healthcare Practitioner', self.name):
self.name = append_number_if_name_exists('Contact', self.name)
def validate(self):
self.set_full_name()
validate_party_accounts(self)
if self.inpatient_visit_charge_item:
validate_service_item(self.inpatient_visit_charge_item, "Configure a service Item for Inpatient Visit Charge Item")
validate_service_item(self.inpatient_visit_charge_item, 'Configure a service Item for Inpatient Consulting Charge Item')
if self.op_consulting_charge_item:
validate_service_item(self.op_consulting_charge_item, "Configure a service Item for Out Patient Consulting Charge Item")
validate_service_item(self.op_consulting_charge_item, 'Configure a service Item for Out Patient Consulting Charge Item')
if self.user_id:
self.validate_for_enabled_user_id()
self.validate_duplicate_user_id()
existing_user_id = frappe.db.get_value("Healthcare Practitioner", self.name, "user_id")
if self.user_id != existing_user_id:
frappe.permissions.remove_user_permission(
"Healthcare Practitioner", self.name, existing_user_id)
self.validate_user_id()
else:
existing_user_id = frappe.db.get_value("Healthcare Practitioner", self.name, "user_id")
existing_user_id = frappe.db.get_value('Healthcare Practitioner', self.name, 'user_id')
if existing_user_id:
frappe.permissions.remove_user_permission(
"Healthcare Practitioner", self.name, existing_user_id)
'Healthcare Practitioner', self.name, existing_user_id)
def on_update(self):
if self.user_id:
frappe.permissions.add_user_permission("Healthcare Practitioner", self.name, self.user_id)
frappe.permissions.add_user_permission('Healthcare Practitioner', self.name, self.user_id)
def set_full_name(self):
if self.last_name:
self.practitioner_name = ' '.join(filter(None, [self.first_name, self.last_name]))
else:
self.practitioner_name = self.first_name
def validate_for_enabled_user_id(self):
enabled = frappe.db.get_value("User", self.user_id, "enabled")
if enabled is None:
frappe.throw(_("User {0} does not exist").format(self.user_id))
if enabled == 0:
frappe.throw(_("User {0} is disabled").format(self.user_id))
def validate_user_id(self):
if not frappe.db.exists('User', self.user_id):
frappe.throw(_('User {0} does not exist').format(self.user_id))
elif not frappe.db.exists('User', self.user_id, 'enabled'):
frappe.throw(_('User {0} is disabled').format(self.user_id))
def validate_duplicate_user_id(self):
practitioner = frappe.db.sql_list("""select name from `tabHealthcare Practitioner` where
user_id=%s and name!=%s""", (self.user_id, self.name))
# check duplicate
practitioner = frappe.db.exists('Healthcare Practitioner', {
'user_id': self.user_id,
'name': ('!=', self.name)
})
if practitioner:
throw(_("User {0} is already assigned to Healthcare Practitioner {1}").format(
self.user_id, practitioner[0]), frappe.DuplicateEntryError)
frappe.throw(_('User {0} is already assigned to Healthcare Practitioner {1}').format(
self.user_id, practitioner))
def on_trash(self):
delete_contact_and_address('Healthcare Practitioner', self.name)
def validate_service_item(item, msg):
if frappe.db.get_value("Item", item, "is_stock_item") == 1:
if frappe.db.get_value('Item', item, 'is_stock_item'):
frappe.throw(_(msg))
def get_practitioner_list(doctype, txt, searchfield, start, page_len, filters=None):
fields = ["name", "first_name", "mobile_phone"]
fields = ['name', 'practitioner_name', 'mobile_phone']
filters = {
'name': ("like", "%%%s%%" % txt)
'name': ('like', '%%%s%%' % txt)
}
return frappe.get_all("Healthcare Practitioner", fields = fields,
filters = filters, start=start, page_length=page_len, order_by="name, first_name", as_list=1)
return frappe.get_all('Healthcare Practitioner', fields = fields,
filters = filters, start=start, page_length=page_len, order_by='name, practitioner_name', as_list=1)

View File

@ -9,7 +9,11 @@ def get_data():
'transactions': [
{
'label': _('Appointments and Patient Encounters'),
'items': ['Patient Appointment', 'Patient Encounter']
'items': ['Patient Appointment', 'Patient Encounter', 'Fee Validity']
},
{
'label': _('Consultation'),
'items': ['Clinical Procedure', 'Lab Test']
}
]
}

View File

@ -3,7 +3,7 @@
frappe.ui.form.on('Healthcare Service Unit', {
onload: function(frm) {
frm.list_route = "Tree/Healthcare Service Unit";
frm.list_route = 'Tree/Healthcare Service Unit';
// get query select healthcare service unit
frm.fields_dict['parent_healthcare_service_unit'].get_query = function(doc) {
@ -16,32 +16,32 @@ frappe.ui.form.on('Healthcare Service Unit', {
};
},
refresh: function(frm) {
frm.trigger("set_root_readonly");
frm.set_df_property("service_unit_type", "reqd", 1);
frm.add_custom_button(__("Healthcare Service Unit Tree"), function() {
frappe.set_route("Tree", "Healthcare Service Unit");
frm.trigger('set_root_readonly');
frm.set_df_property('service_unit_type', 'reqd', 1);
frm.add_custom_button(__('Healthcare Service Unit Tree'), function() {
frappe.set_route('Tree', 'Healthcare Service Unit');
});
},
set_root_readonly: function(frm) {
// read-only for root healthcare service unit
frm.set_intro("");
if(!frm.doc.parent_healthcare_service_unit) {
frm.set_intro('');
if (!frm.doc.parent_healthcare_service_unit) {
frm.set_read_only();
frm.set_intro(__("This is a root healthcare service unit and cannot be edited."), true);
frm.set_intro(__('This is a root healthcare service unit and cannot be edited.'), true);
}
},
allow_appointments: function(frm) {
if(!frm.doc.allow_appointments){
frm.set_value("overlap_appointments", false);
if (!frm.doc.allow_appointments) {
frm.set_value('overlap_appointments', false);
}
},
is_group: function(frm) {
if(frm.doc.is_group == 1){
frm.set_value("allow_appointments", false);
frm.set_df_property("service_unit_type", "reqd", 0);
if (frm.doc.is_group == 1) {
frm.set_value('allow_appointments', false);
frm.set_df_property('service_unit_type', 'reqd', 0);
}
else{
frm.set_df_property("service_unit_type", "reqd", 1);
else {
frm.set_df_property('service_unit_type', 'reqd', 1);
}
}
});

View File

@ -9,6 +9,7 @@
"doctype": "DocType",
"document_type": "Setup",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"healthcare_service_unit_name",
"parent_healthcare_service_unit",
@ -18,6 +19,7 @@
"overlap_appointments",
"inpatient_occupancy",
"occupancy_status",
"column_break_9",
"warehouse",
"company",
"lft",
@ -99,9 +101,13 @@
"fieldtype": "Select",
"label": "Occupancy Status",
"no_copy": 1,
"options": "Vacant\nOccupied",
"options": "\nVacant\nOccupied",
"read_only": 1
},
{
"fieldname": "column_break_9",
"fieldtype": "Column Break"
},
{
"bold": 1,
"depends_on": "eval:doc.is_group != 1",
@ -153,13 +159,11 @@
"report_hide": 1
}
],
"is_tree": 1,
"links": [],
"modified": "2020-03-18 18:02:23.713439",
"modified": "2020-03-26 16:13:08.675952",
"modified_by": "Administrator",
"module": "Healthcare",
"name": "Healthcare Service Unit",
"nsm_parent_field": "parent_healthcare_service_unit",
"owner": "Administrator",
"permissions": [
{

View File

@ -23,9 +23,9 @@ class HealthcareServiceUnit(NestedSet):
self.validate_one_root()
def validate(self):
if self.is_group == 1:
if self.is_group:
self.allow_appointments = 0
self.overlap_appointments = 0
self.inpatient_occupancy = 0
elif self.allow_appointments != 1:
elif not self.allow_appointments:
self.overlap_appointments = 0

View File

@ -2,118 +2,85 @@
// For license information, please see license.txt
frappe.ui.form.on('Healthcare Service Unit Type', {
refresh: function(frm) {
frm.set_df_property('item_code', 'read_only', frm.doc.__islocal ? 0 : 1);
if (!frm.doc.__islocal && frm.doc.is_billable) {
frm.add_custom_button(__('Change Item Code'), function() {
change_item_code(cur_frm, frm.doc);
});
}
},
service_unit_type: function(frm) {
set_item_details(frm);
if(!frm.doc.__islocal){
if (!frm.doc.__islocal) {
frm.doc.change_in_item = 1;
}
},
is_billable: function(frm) {
set_item_details(frm);
},
refresh: function(frm) {
frm.set_df_property("item_code", "read_only", frm.doc.__islocal ? 0 : 1);
if(!frm.doc.__islocal) {
frm.add_custom_button(__('Change Item Code'), function() {
change_item_code(cur_frm,frm.doc);
} );
if(frm.doc.disabled == 1){
frm.add_custom_button(__('Enable'), function() {
enable(cur_frm);
} );
}
else{
frm.add_custom_button(__('Disable'), function() {
disable(cur_frm);
} );
}
}
},
rate: function(frm) {
if(!frm.doc.__islocal){
if (!frm.doc.__islocal) {
frm.doc.change_in_item = 1;
}
},
item_group: function(frm) {
if(!frm.doc.__islocal){
if (!frm.doc.__islocal) {
frm.doc.change_in_item = 1;
}
},
description: function(frm) {
if(!frm.doc.__islocal){
if (!frm.doc.__islocal) {
frm.doc.change_in_item = 1;
}
}
});
var disable = function(frm){
var doc = frm.doc;
frappe.call({
method: "erpnext.healthcare.doctype.healthcare_service_unit_type.healthcare_service_unit_type.disable_enable",
args: {status: 1, doc_name: doc.name, item: doc.item, is_billable: doc.is_billable},
callback: function(){
cur_frm.reload_doc();
}
});
let set_item_details = function(frm) {
if (frm.doc.service_unit_type && frm.doc.is_billable) {
if (!frm.doc.item_code)
frm.set_value('item_code', frm.doc.service_unit_type);
if (!frm.doc.description)
frm.set_value('description', frm.doc.service_unit_type);
if (!frm.doc.item_group)
frm.set_value('item_group', 'Services');
}
};
var enable = function(frm){
var doc = frm.doc;
frappe.call({
method: "erpnext.healthcare.doctype.healthcare_service_unit_type.healthcare_service_unit_type.disable_enable",
args: {status: 0, doc_name: doc.name, item: doc.item, is_billable: doc.is_billable},
callback: function(){
cur_frm.reload_doc();
}
});
};
var change_item_code = function(frm, doc){
var d = new frappe.ui.Dialog({
title:__("Change Item Code"),
fields:[
let change_item_code = function(frm, doc) {
let d = new frappe.ui.Dialog({
title: __('Change Item Code'),
fields: [
{
"fieldtype": "Data",
"label": "Item Code",
"fieldname": "Item Code",
reqd:1
},
{
"fieldtype": "Button",
"label": __("Change Code"),
click: function() {
var values = d.get_values();
if(!values)
return;
change_item_code_from_unit_type(values["Item Code"], doc);
d.hide();
}
'fieldtype': 'Data',
'label': 'Item Code',
'fieldname': 'item_code',
'default': doc.item_code,
reqd: 1,
}
]
],
primary_action: function() {
let values = d.get_values();
if (values) {
frappe.call({
"method": "erpnext.healthcare.doctype.healthcare_service_unit_type.healthcare_service_unit_type.change_item_code",
"args": {item: doc.item, item_code: values['item_code'], doc_name: doc.name},
callback: function () {
frm.reload_doc();
}
});
}
d.hide();
},
primary_action_label: __("Change Template Code")
});
d.show();
d.set_values({
'Item Code': frm.doc.item_code
});
var change_item_code_from_unit_type = function(item_code, doc){
frappe.call({
"method": "erpnext.healthcare.doctype.healthcare_service_unit_type.healthcare_service_unit_type.change_item_code",
"args": {item: doc.item, item_code: item_code, doc_name: doc.name},
callback: function () {
frm.reload_doc();
}
});
};
};
var set_item_details = function(frm) {
if(frm.doc.service_unit_type && frm.doc.is_billable == 1){
if(!frm.doc.item_code)
frm.set_value("item_code", frm.doc.service_unit_type);
if(!frm.doc.description)
frm.set_value("description", frm.doc.service_unit_type);
if(!frm.doc.item_group)
frm.set_value("item_group", 'Services');
}
};

View File

@ -1,588 +1,164 @@
{
"allow_copy": 0,
"allow_guest_to_view": 0,
"allow_import": 1,
"allow_rename": 1,
"autoname": "field:service_unit_type",
"beta": 0,
"creation": "2018-07-11 16:47:51.414675",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "",
"editable_grid": 1,
"engine": "InnoDB",
"actions": [],
"allow_import": 1,
"allow_rename": 1,
"autoname": "field:service_unit_type",
"creation": "2018-07-11 16:47:51.414675",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"disabled",
"service_unit_type",
"allow_appointments",
"overlap_appointments",
"inpatient_occupancy",
"is_billable",
"item_details",
"item",
"item_code",
"item_group",
"uom",
"no_of_hours",
"column_break_11",
"rate",
"description",
"change_in_item"
],
"fields": [
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "service_unit_type",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Service Unit Type",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"fieldname": "service_unit_type",
"fieldtype": "Data",
"in_list_view": 1,
"label": "Service Unit Type",
"reqd": 1,
"unique": 1
},
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 1,
"collapsible": 0,
"columns": 0,
"default": "0",
"depends_on": "eval:doc.inpatient_occupancy != 1",
"fieldname": "allow_appointments",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Allow Appointments",
"length": 0,
"no_copy": 1,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
"bold": 1,
"default": "0",
"depends_on": "eval:doc.inpatient_occupancy != 1",
"fieldname": "allow_appointments",
"fieldtype": "Check",
"label": "Allow Appointments",
"no_copy": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 1,
"collapsible": 0,
"columns": 0,
"default": "0",
"depends_on": "eval:doc.allow_appointments == 1 && doc.inpatient_occupany != 1",
"fieldname": "overlap_appointments",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Allow Overlap",
"length": 0,
"no_copy": 1,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
"bold": 1,
"default": "0",
"depends_on": "eval:doc.allow_appointments == 1 && doc.inpatient_occupany != 1",
"fieldname": "overlap_appointments",
"fieldtype": "Check",
"label": "Allow Overlap",
"no_copy": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 1,
"collapsible": 0,
"columns": 0,
"default": "0",
"depends_on": "eval:doc.allow_appointments != 1",
"fieldname": "inpatient_occupancy",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Inpatient Occupancy",
"length": 0,
"no_copy": 1,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
"bold": 1,
"default": "0",
"depends_on": "eval:doc.allow_appointments != 1",
"fieldname": "inpatient_occupancy",
"fieldtype": "Check",
"label": "Inpatient Occupancy",
"no_copy": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 1,
"collapsible": 0,
"columns": 0,
"depends_on": "eval:doc.inpatient_occupancy == 1 && doc.allow_appointments != 1",
"fieldname": "is_billable",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Is Billable",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
"bold": 1,
"default": "0",
"depends_on": "eval:doc.inpatient_occupancy == 1 && doc.allow_appointments != 1",
"fieldname": "is_billable",
"fieldtype": "Check",
"label": "Is Billable"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "is_billable",
"fieldname": "item_details",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Item Details",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
"depends_on": "is_billable",
"fieldname": "item_details",
"fieldtype": "Section Break",
"label": "Item Details"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "item",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Item",
"length": 0,
"no_copy": 0,
"options": "Item",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
"fieldname": "item",
"fieldtype": "Link",
"label": "Item",
"options": "Item",
"read_only": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "item_code",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Item Code",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
"fieldname": "item_code",
"fieldtype": "Data",
"label": "Item Code",
"mandatory_depends_on": "eval: doc.is_billable == 1"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "item_group",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Item Group",
"length": 0,
"no_copy": 0,
"options": "Item Group",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
"fieldname": "item_group",
"fieldtype": "Link",
"label": "Item Group",
"mandatory_depends_on": "eval: doc.is_billable == 1",
"options": "Item Group"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "uom",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "UOM",
"length": 0,
"no_copy": 0,
"options": "UOM",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
"fieldname": "uom",
"fieldtype": "Link",
"label": "UOM",
"mandatory_depends_on": "eval: doc.is_billable == 1",
"options": "UOM"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "no_of_hours",
"fieldtype": "Int",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "UOM Conversion in Hours",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
"fieldname": "no_of_hours",
"fieldtype": "Int",
"label": "UOM Conversion in Hours",
"mandatory_depends_on": "eval: doc.is_billable == 1"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_11",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
"fieldname": "column_break_11",
"fieldtype": "Column Break"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "rate",
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Rate / UOM",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
"fieldname": "rate",
"fieldtype": "Currency",
"label": "Rate / UOM"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "0",
"fieldname": "disabled",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Disabled",
"length": 0,
"no_copy": 1,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
"default": "0",
"fieldname": "disabled",
"fieldtype": "Check",
"label": "Disabled",
"no_copy": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "description",
"fieldtype": "Small Text",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Description",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
"fieldname": "description",
"fieldtype": "Small Text",
"label": "Description"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "change_in_item",
"fieldtype": "Check",
"hidden": 1,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Change in Item",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
"default": "0",
"fieldname": "change_in_item",
"fieldtype": "Check",
"hidden": 1,
"label": "Change in Item"
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2018-08-08 13:00:23.751635",
"modified_by": "Administrator",
"module": "Healthcare",
"name": "Healthcare Service Unit Type",
"name_case": "",
"owner": "Administrator",
],
"links": [],
"modified": "2020-01-30 16:06:00.624496",
"modified_by": "Administrator",
"module": "Healthcare",
"name": "Healthcare Service Unit Type",
"owner": "Administrator",
"permissions": [
{
"amend": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Healthcare Administrator",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "Healthcare Administrator",
"share": 1,
"write": 1
}
],
"quick_entry": 0,
"read_only": 0,
"read_only_onload": 0,
"restrict_to_domain": "Healthcare",
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"title_field": "service_unit_type",
"track_changes": 0,
"track_seen": 0,
"track_views": 0
],
"restrict_to_domain": "Healthcare",
"sort_field": "modified",
"sort_order": "DESC",
"title_field": "service_unit_type"
}

View File

@ -10,109 +10,107 @@ from frappe.model.rename_doc import rename_doc
class HealthcareServiceUnitType(Document):
def validate(self):
if self.is_billable == 1:
if not self.uom or not self.item_group or not self.description or not self.no_of_hours > 0:
frappe.throw(_("Configure Item Fields like UOM, Item Group, Description and No of Hours."))
if self.is_billable:
if self.disabled:
frappe.db.set_value('Item', self.item, 'disabled', 1)
else:
frappe.db.set_value('Item', self.item, 'disabled', 0)
def after_insert(self):
if self.inpatient_occupancy and self.is_billable:
create_item(self)
def on_trash(self):
if(self.item):
if self.item:
try:
frappe.delete_doc("Item",self.item)
frappe.delete_doc('Item', self.item)
except Exception:
frappe.throw(_("""Not permitted. Please disable the Service Unit Type"""))
frappe.throw(_('Not permitted. Please disable the Service Unit Type'))
def on_update(self):
if(self.change_in_item and self.is_billable == 1 and self.item):
updating_item(self)
item_price = item_price_exist(self)
if not item_price:
if(self.rate != 0.0):
price_list_name = frappe.db.get_value("Price List", {"selling": 1})
if(self.rate):
make_item_price(self.item_code, price_list_name, self.rate)
else:
make_item_price(self.item_code, price_list_name, 0.0)
else:
frappe.db.set_value("Item Price", item_price, "price_list_rate", self.rate)
if self.change_in_item and self.is_billable and self.item:
update_item(self)
frappe.db.set_value(self.doctype,self.name,"change_in_item",0)
elif(self.is_billable == 0 and self.item):
frappe.db.set_value("Item",self.item,"disabled",1)
item_price = item_price_exists(self)
if not item_price:
price_list_name = frappe.db.get_value('Price List', {'selling': 1})
if self.rate:
make_item_price(self.item_code, price_list_name, self.rate)
else:
make_item_price(self.item_code, price_list_name, 0.0)
else:
frappe.db.set_value('Item Price', item_price, 'price_list_rate', self.rate)
frappe.db.set_value(self.doctype, self.name, 'change_in_item',0)
elif not self.is_billable and self.item:
frappe.db.set_value('Item', self.item, 'disabled', 1)
self.reload()
def item_price_exist(doc):
item_price = frappe.db.exists({
"doctype": "Item Price",
"item_code": doc.item_code})
if(item_price):
return item_price[0][0]
else:
return False
def updating_item(doc):
frappe.db.sql("""update `tabItem` set item_name=%s, item_group=%s, disabled=0, standard_rate=%s,
description=%s, modified=NOW() where item_code=%s""",
(doc.service_unit_type, doc.item_group , doc.rate, doc.description, doc.item))
def item_price_exists(doc):
item_price = frappe.db.exists({'doctype': 'Item Price', 'item_code': doc.item_code})
if len(item_price):
return item_price[0][0]
return False
def create_item(doc):
#insert item
# insert item
item = frappe.get_doc({
"doctype": "Item",
"item_code": doc.item_code,
"item_name":doc.service_unit_type,
"item_group": doc.item_group,
"description":doc.description,
"is_sales_item": 1,
"is_service_item": 1,
"is_purchase_item": 0,
"is_stock_item": 0,
"show_in_website": 0,
"is_pro_applicable": 0,
"disabled": 0,
"stock_uom": doc.uom
}).insert(ignore_permissions=True)
'doctype': 'Item',
'item_code': doc.item_code,
'item_name': doc.service_unit_type,
'item_group': doc.item_group,
'description': doc.description or doc.item_code,
'is_sales_item': 1,
'is_service_item': 1,
'is_purchase_item': 0,
'is_stock_item': 0,
'show_in_website': 0,
'is_pro_applicable': 0,
'disabled': 0,
'stock_uom': doc.uom
}).insert(ignore_permissions=True, ignore_mandatory=True)
#insert item price
#get item price list to insert item price
if(doc.rate != 0.0):
price_list_name = frappe.db.get_value("Price List", {"selling": 1})
if(doc.rate):
make_item_price(item.name, price_list_name, doc.rate)
item.standard_rate = doc.rate
else:
make_item_price(item.name, price_list_name, 0.0)
item.standard_rate = 0.0
item.save(ignore_permissions = True)
#Set item to the Doc
frappe.db.set_value("Healthcare Service Unit Type", doc.name, "item", item.name)
# insert item price
# get item price list to insert item price
price_list_name = frappe.db.get_value('Price List', {'selling': 1})
if doc.rate:
make_item_price(item.name, price_list_name, doc.rate)
item.standard_rate = doc.rate
else:
make_item_price(item.name, price_list_name, 0.0)
item.standard_rate = 0.0
doc.reload() #refresh the doc after insert.
item.save(ignore_permissions=True)
# Set item in the doc
doc.db_set('item', item.name)
def make_item_price(item, price_list_name, item_price):
frappe.get_doc({
"doctype": "Item Price",
"price_list": price_list_name,
"item_code": item,
"price_list_rate": item_price
}).insert(ignore_permissions=True)
'doctype': 'Item Price',
'price_list': price_list_name,
'item_code': item,
'price_list_rate': item_price
}).insert(ignore_permissions=True, ignore_mandatory=True)
def update_item(doc):
item = frappe.get_doc("Item", doc.item)
if item:
item.update({
"item_name": doc.service_unit_type,
"item_group": doc.item_group,
"disabled": 0,
"standard_rate": doc.rate,
"description": doc.description
})
item.db_update()
@frappe.whitelist()
def change_item_code(item, item_code, doc_name):
item_exist = frappe.db.exists({
"doctype": "Item",
"item_code": item_code})
if(item_exist):
frappe.throw(_("Code {0} already exist").format(item_code))
if frappe.db.exists({'doctype': 'Item', 'item_code': item_code}):
frappe.throw(_('Item with Item Code {0} already exists').format(item_code))
else:
rename_doc("Item", item, item_code, ignore_permissions=True)
frappe.db.set_value("Healthcare Service Unit Type", doc_name, "item_code", item_code)
@frappe.whitelist()
def disable_enable(status, doc_name, item=None, is_billable=None):
frappe.db.set_value("Healthcare Service Unit Type", doc_name, "disabled", status)
if(is_billable == 1):
frappe.db.set_value("Item", item, "disabled", status)
rename_doc('Item', item, item_code, ignore_permissions=True)
frappe.db.set_value('Healthcare Service Unit Type', doc_name, 'item_code', item_code)

View File

@ -0,0 +1,13 @@
from __future__ import unicode_literals
from frappe import _
def get_data():
return {
'fieldname': 'service_unit_type',
'transactions': [
{
'label': _('Healthcare Service Units'),
'items': ['Healthcare Service Unit']
},
]
}

View File

@ -3,6 +3,31 @@
# See license.txt
from __future__ import unicode_literals
import unittest
import frappe
class TestHealthcareServiceUnitType(unittest.TestCase):
pass
def test_item_creation(self):
unit_type = get_unit_type()
self.assertTrue(frappe.db.exists('Item', unit_type.item))
# check item disabled
unit_type.disabled = 1
unit_type.save()
self.assertEqual(frappe.db.get_value('Item', unit_type.item, 'disabled'), 1)
def get_unit_type():
if frappe.db.exists('Healthcare Service Unit Type', 'Inpatient Rooms'):
return frappe.get_doc('Healthcare Service Unit Type', 'Inpatient Rooms')
unit_type = frappe.new_doc('Healthcare Service Unit Type')
unit_type.service_unit_type = 'Inpatient Rooms'
unit_type.inpatient_occupancy = 1
unit_type.is_billable = 1
unit_type.item_code = 'Inpatient Rooms'
unit_type.item_group = 'Services'
unit_type.uom = 'Hour'
unit_type.no_of_hours = 1
unit_type.rate = 4000
unit_type.save()
return unit_type

View File

@ -7,40 +7,40 @@
"engine": "InnoDB",
"field_order": [
"sb_op_settings",
"patient_master_name",
"manage_customer",
"patient_name_by",
"link_customer_to_patient",
"default_medical_code_standard",
"column_break_9",
"collect_registration_fee",
"registration_fee",
"manage_appointment_invoice_automatically",
"max_visit",
"automate_appointment_invoicing",
"enable_free_follow_ups",
"max_visits",
"valid_days",
"healthcare_service_items",
"inpatient_visit_charge_item",
"op_consulting_charge_item",
"column_break_13",
"clinical_procedure_consumable_item",
"out_patient_sms_alerts",
"reg_sms",
"reg_msg",
"app_con",
"app_con_msg",
"no_con",
"column_break_16",
"app_rem",
"app_rem_msg",
"rem_before",
"sb_in_ac",
"income_account",
"sb_r_ac",
"receivable_account",
"out_patient_sms_alerts",
"send_registration_msg",
"registration_msg",
"send_appointment_confirmation",
"appointment_confirmation_msg",
"avoid_confirmation",
"column_break_16",
"send_appointment_reminder",
"appointment_reminder_msg",
"remind_before",
"sb_lab_settings",
"create_test_on_si_submit",
"require_sample_collection",
"require_test_result_approval",
"create_lab_test_on_si_submit",
"create_sample_collection_for_lab_test",
"column_break_34",
"employee_name_and_designation_in_print",
"lab_test_approval_required",
"custom_signature_in_print",
"laboratory_sms_alerts",
"sms_printed",
@ -53,19 +53,6 @@
"fieldtype": "Section Break",
"label": "Out Patient Settings"
},
{
"fieldname": "patient_master_name",
"fieldtype": "Select",
"label": "Patient Name By",
"options": "Patient Name\nNaming Series"
},
{
"default": "1",
"description": "If checked, a customer will be created, mapped to Patient.\nPatient Invoices will be created against this Customer. You can also select existing Customer while creating Patient.",
"fieldname": "manage_customer",
"fieldtype": "Check",
"label": "Manage Customer"
},
{
"fieldname": "default_medical_code_standard",
"fieldtype": "Link",
@ -78,6 +65,7 @@
},
{
"default": "0",
"description": "Checking this will create new Patients with a Disabled status by default and will only be enabled after invoicing the Registration Fee.",
"fieldname": "collect_registration_fee",
"fieldtype": "Check",
"label": "Collect Fee for Patient Registration"
@ -91,27 +79,19 @@
"options": "Currency"
},
{
"default": "0",
"description": "Manage Appointment Invoice submit and cancel automatically for Patient Encounter",
"fieldname": "manage_appointment_invoice_automatically",
"fieldtype": "Check",
"label": "Invoice Appointments Automatically"
},
{
"fieldname": "max_visit",
"fieldtype": "Int",
"label": "Patient Encounters in valid days"
},
{
"depends_on": "eval:doc.enable_free_follow_ups == 1",
"description": "Time period (Valid number of days) for free consultations",
"fieldname": "valid_days",
"fieldtype": "Int",
"label": "Valid number of days"
"label": "Valid Number of Days",
"mandatory_depends_on": "eval:doc.enable_free_follow_ups == 1"
},
{
"collapsible": 1,
"description": "You can configure default Items for billing consultation charges, procedure consumption items and inpatient visits",
"fieldname": "healthcare_service_items",
"fieldtype": "Section Break",
"label": "Healthcare Service Items"
"label": "Default Healthcare Service Items"
},
{
"fieldname": "inpatient_visit_charge_item",
@ -141,87 +121,25 @@
"fieldtype": "Section Break",
"label": "Out Patient SMS Alerts"
},
{
"default": "0",
"fieldname": "reg_sms",
"fieldtype": "Check",
"label": "Patient Registration"
},
{
"default": "Hello {{doc.patient}}, Thank you for registering with {{doc.company}}. Your ID is {{doc.id}} . Please note this ID for future reference. \nThank You, Get well soon!",
"depends_on": "reg_sms",
"fieldname": "reg_msg",
"fieldtype": "Small Text",
"ignore_xss_filter": 1,
"label": "Registration Message"
},
{
"default": "0",
"fieldname": "app_con",
"fieldtype": "Check",
"label": "Appointment Confirmation"
},
{
"default": "Hello {{doc.patient}}, You have scheduled an appointment with {{doc.practitioner}} by {{doc.start_dt}} at {{doc.company}}.\nThank you, Good day!",
"depends_on": "app_con",
"fieldname": "app_con_msg",
"fieldtype": "Small Text",
"ignore_xss_filter": 1,
"label": "Confirmation Message"
},
{
"default": "0",
"depends_on": "app_con",
"description": "Do not confirm if appointment is created for the same day",
"fieldname": "no_con",
"fieldtype": "Check",
"label": "Avoid Confirmation"
},
{
"fieldname": "column_break_16",
"fieldtype": "Column Break"
},
{
"default": "0",
"fieldname": "app_rem",
"fieldtype": "Check",
"label": "Appointment Reminder"
},
{
"default": "Hello {{doc.patient}}, You have an appointment with {{doc.practitioner}} by {{doc.appointment_time}} at {{doc.company}}.\nThank you, Good day!\n",
"depends_on": "app_rem",
"fieldname": "app_rem_msg",
"fieldtype": "Small Text",
"ignore_xss_filter": 1,
"label": "Reminder Message"
},
{
"depends_on": "app_rem",
"fieldname": "rem_before",
"fieldtype": "Time",
"label": "Remind Before"
},
{
"collapsible": 1,
"description": "Default income accounts to be used if not set in Healthcare Practitioner to book Appointment charges.",
"fieldname": "sb_in_ac",
"fieldtype": "Section Break",
"label": "Income Account"
"label": "Default Accounts"
},
{
"description": "Default income accounts to be used if not set in Healthcare Practitioner to book Appointment charges.",
"fieldname": "income_account",
"fieldtype": "Table",
"label": "Income Account",
"options": "Party Account"
},
{
"collapsible": 1,
"description": "Default receivable accounts to be used if not set in Patient to book Appointment charges.",
"fieldname": "sb_r_ac",
"fieldtype": "Section Break",
"label": "Receivable Account"
},
{
"description": "Default receivable accounts to be used to book Appointment charges.",
"fieldname": "receivable_account",
"fieldtype": "Table",
"label": "Receivable Account",
@ -233,31 +151,13 @@
"fieldtype": "Section Break",
"label": "Laboratory Settings"
},
{
"default": "0",
"fieldname": "create_test_on_si_submit",
"fieldtype": "Check",
"label": "Create Lab Test(s) on Sales Invoice Submit"
},
{
"default": "0",
"description": "Create documents for sample collection",
"fieldname": "require_sample_collection",
"fieldtype": "Check",
"label": "Manage Sample Collection"
},
{
"default": "0",
"fieldname": "require_test_result_approval",
"fieldtype": "Check",
"label": "Require Lab Test Approval"
},
{
"fieldname": "column_break_34",
"fieldtype": "Column Break"
},
{
"default": "1",
"description": "Check this if you want the Name and Designation of the Employee associated with the User who submits the document to be printed in the Lab Test Report.",
"fieldname": "employee_name_and_designation_in_print",
"fieldtype": "Check",
"label": "Employee name and designation in print"
@ -279,7 +179,7 @@
"fieldname": "sms_printed",
"fieldtype": "Small Text",
"ignore_xss_filter": 1,
"label": "Result Printed"
"label": "Result Printed Message"
},
{
"fieldname": "column_break_28",
@ -290,12 +190,123 @@
"fieldname": "sms_emailed",
"fieldtype": "Small Text",
"ignore_xss_filter": 1,
"label": "Result Emailed"
"label": "Result Emailed Message"
},
{
"default": "0",
"description": "Checking this will restrict printing and emailing of Lab Test documents unless they have the status as Approved.",
"fieldname": "lab_test_approval_required",
"fieldtype": "Check",
"label": "Do not print or email Lab Tests without Approval"
},
{
"default": "1",
"description": "If checked, a customer will be created, mapped to Patient.\nPatient Invoices will be created against this Customer. You can also select existing Customer while creating Patient.",
"fieldname": "link_customer_to_patient",
"fieldtype": "Check",
"label": "Link Customer to Patient"
},
{
"default": "0",
"description": "Checking this will create Lab Test(s) specified in the Sales Invoice on submission.",
"fieldname": "create_lab_test_on_si_submit",
"fieldtype": "Check",
"label": "Create Lab Test(s) on Sales Invoice Submission"
},
{
"default": "0",
"description": "Checking this will create a Sample Collection document every time you create a Lab Test",
"fieldname": "create_sample_collection_for_lab_test",
"fieldtype": "Check",
"label": "Create Sample Collection document for Lab Test"
},
{
"fieldname": "patient_name_by",
"fieldtype": "Select",
"label": "Patient Name By",
"options": "Patient Name\nNaming Series"
},
{
"default": "0",
"description": "Manage Appointment Invoice submit and cancel automatically for Patient Encounter",
"fieldname": "automate_appointment_invoicing",
"fieldtype": "Check",
"label": "Automate Appointment Invoicing"
},
{
"default": "0",
"fieldname": "send_registration_msg",
"fieldtype": "Check",
"label": "Patient Registration"
},
{
"default": "Hello {{doc.patient}}, Thank you for registering with {{doc.company}}. Your ID is {{doc.id}} . Please note this ID for future reference. \nThank You, Get well soon!",
"depends_on": "send_registration_msg",
"fieldname": "registration_msg",
"fieldtype": "Small Text",
"ignore_xss_filter": 1,
"label": "Registration Message"
},
{
"default": "0",
"fieldname": "send_appointment_confirmation",
"fieldtype": "Check",
"label": "Appointment Confirmation"
},
{
"default": "Hello {{doc.patient}}, You have scheduled an appointment with {{doc.practitioner}} by {{doc.start_dt}} at {{doc.company}}.\nThank you, Good day!",
"depends_on": "send_appointment_confirmation",
"fieldname": "appointment_confirmation_msg",
"fieldtype": "Small Text",
"ignore_xss_filter": 1,
"label": "Confirmation Message"
},
{
"default": "0",
"depends_on": "send_appointment_confirmation",
"description": "Do not confirm if appointment is created for the same day",
"fieldname": "avoid_confirmation",
"fieldtype": "Check",
"label": "Avoid Confirmation"
},
{
"default": "0",
"fieldname": "send_appointment_reminder",
"fieldtype": "Check",
"label": "Appointment Reminder"
},
{
"default": "Hello {{doc.patient}}, You have an appointment with {{doc.practitioner}} by {{doc.appointment_time}} at {{doc.company}}.\nThank you, Good day!\n",
"depends_on": "send_appointment_reminder",
"fieldname": "appointment_reminder_msg",
"fieldtype": "Small Text",
"ignore_xss_filter": 1,
"label": "Reminder Message"
},
{
"depends_on": "send_appointment_reminder",
"fieldname": "remind_before",
"fieldtype": "Time",
"label": "Remind Before"
},
{
"depends_on": "eval:doc.enable_free_follow_ups == 1",
"description": "The number of free follow ups (Patient Encounters in valid days) allowed",
"fieldname": "max_visits",
"fieldtype": "Int",
"label": "Number of Patient Encounters in Valid Days",
"mandatory_depends_on": "eval:doc.enable_free_follow_ups == 1"
},
{
"default": "0",
"fieldname": "enable_free_follow_ups",
"fieldtype": "Check",
"label": "Enable Free Follow-ups"
}
],
"issingle": 1,
"links": [],
"modified": "2020-01-23 13:31:43.699711",
"modified": "2020-03-26 11:25:21.842092",
"modified_by": "Administrator",
"module": "Healthcare",
"name": "Healthcare Settings",

View File

@ -11,69 +11,80 @@ import json
class HealthcareSettings(Document):
def validate(self):
for key in ["collect_registration_fee","manage_customer","patient_master_name",
"require_test_result_approval","require_sample_collection", "default_medical_code_standard"]:
for key in ['collect_registration_fee', 'link_customer_to_patient', 'patient_name_by',
'lab_test_approval_required', 'create_sample_collection_for_lab_test', 'default_medical_code_standard']:
frappe.db.set_default(key, self.get(key, ""))
if(self.collect_registration_fee):
if self.registration_fee <= 0 :
frappe.throw(_("Registration fee can not be Zero"))
if self.collect_registration_fee:
if self.registration_fee <= 0:
frappe.throw(_('Registration Fee cannot be negative or zero'))
if self.inpatient_visit_charge_item:
validate_service_item(self.inpatient_visit_charge_item, "Configure a service Item for Inpatient Visit Charge Item")
validate_service_item(self.inpatient_visit_charge_item)
if self.op_consulting_charge_item:
validate_service_item(self.op_consulting_charge_item, "Configure a service Item for Out Patient Consulting Charge Item")
validate_service_item(self.op_consulting_charge_item)
if self.clinical_procedure_consumable_item:
validate_service_item(self.clinical_procedure_consumable_item, "Configure a service Item for Clinical Procedure Consumable Item")
validate_service_item(self.clinical_procedure_consumable_item)
def validate_service_item(item):
if frappe.db.get_value('Item', item, 'is_stock_item'):
frappe.throw(_('Configure a service Item for {0}').format(item))
@frappe.whitelist()
def get_sms_text(doc):
sms_text = {}
doc = frappe.get_doc("Lab Test",doc)
#doc = json.loads(doc)
context = {"doc": doc, "alert": doc, "comments": None}
emailed = frappe.db.get_value("Healthcare Settings", None, "sms_emailed")
sms_text['emailed'] = frappe.render_template(emailed, context)
printed = frappe.db.get_value("Healthcare Settings", None, "sms_printed")
sms_text['printed'] = frappe.render_template(printed, context)
return sms_text
sms_text = {}
doc = frappe.get_doc('Lab Test', doc)
context = {'doc': doc, 'alert': doc, 'comments': None}
emailed = frappe.db.get_value('Healthcare Settings', None, 'sms_emailed')
sms_text['emailed'] = frappe.render_template(emailed, context)
printed = frappe.db.get_value('Healthcare Settings', None, 'sms_printed')
sms_text['printed'] = frappe.render_template(printed, context)
return sms_text
def send_registration_sms(doc):
if (frappe.db.get_value("Healthcare Settings", None, "reg_sms")=='1'):
if doc.mobile:
context = {"doc": doc, "alert": doc, "comments": None}
if doc.get("_comments"):
context["comments"] = json.loads(doc.get("_comments"))
messages = frappe.db.get_value("Healthcare Settings", None, "reg_msg")
messages = frappe.render_template(messages, context)
number = [doc.mobile]
send_sms(number,messages)
else:
frappe.msgprint(doc.name + " Has no mobile number to send registration SMS", alert=True)
if frappe.db.get_single_value('Healthcare Settings', 'send_registration_msg'):
if doc.mobile:
context = {'doc': doc, 'alert': doc, 'comments': None}
if doc.get('_comments'):
context['comments'] = json.loads(doc.get('_comments'))
messages = frappe.db.get_single_value('Healthcare Settings', 'registration_msg')
messages = frappe.render_template(messages, context)
number = [doc.mobile]
send_sms(number,messages)
else:
frappe.msgprint(doc.name + ' has no mobile number to send registration SMS', alert=True)
def get_receivable_account(company):
receivable_account = get_account(None, "receivable_account", "Healthcare Settings", company)
if receivable_account:
return receivable_account
return frappe.get_cached_value('Company', company, "default_receivable_account")
receivable_account = get_account(None, 'receivable_account', 'Healthcare Settings', company)
if receivable_account:
return receivable_account
return frappe.get_cached_value('Company', company, 'default_receivable_account')
def get_income_account(practitioner, company):
if(practitioner):
income_account = get_account("Healthcare Practitioner", None, practitioner, company)
if income_account:
return income_account
income_account = get_account(None, "income_account", "Healthcare Settings", company)
if income_account:
return income_account
return frappe.get_cached_value('Company', company, "default_income_account")
# check income account in Healthcare Practitioner
if practitioner:
income_account = get_account('Healthcare Practitioner', None, practitioner, company)
if income_account:
return income_account
# else check income account in Healthcare Settings
income_account = get_account(None, 'income_account', 'Healthcare Settings', company)
if income_account:
return income_account
# else return default income account of company
return frappe.get_cached_value('Company', company, 'default_income_account')
def get_account(parent_type, parent_field, parent, company):
if(parent_type):
return frappe.db.get_value("Party Account",
{"parenttype": parent_type, "parent": parent, "company": company}, "account")
if(parent_field):
return frappe.db.get_value("Party Account",
{"parentfield": parent_field, "parent": parent, "company": company}, "account")
if parent_type:
return frappe.db.get_value('Party Account',
{'parenttype': parent_type, 'parent': parent, 'company': company}, 'account')
def validate_service_item(item, msg):
if frappe.db.get_value("Item", item, "is_stock_item") == 1:
frappe.throw(_(msg))
if parent_field:
return frappe.db.get_value('Party Account',
{'parentfield': parent_field, 'parent': parent, 'company': company}, 'account')

View File

@ -8,11 +8,12 @@ import unittest
from frappe.utils import now_datetime, today
from frappe.utils.make_random import get_random
from erpnext.healthcare.doctype.inpatient_record.inpatient_record import admit_patient, discharge_patient, schedule_discharge
from erpnext.healthcare.doctype.patient_appointment.test_patient_appointment import create_patient
class TestInpatientRecord(unittest.TestCase):
def test_admit_and_discharge(self):
frappe.db.sql("""delete from `tabInpatient Record`""")
patient = get_patient()
patient = create_patient()
# Schedule Admission
ip_record = create_inpatient(patient)
ip_record.save(ignore_permissions = True)
@ -41,7 +42,7 @@ class TestInpatientRecord(unittest.TestCase):
def test_validate_overlap_admission(self):
frappe.db.sql("""delete from `tabInpatient Record`""")
patient = get_patient()
patient = create_patient()
ip_record = create_inpatient(patient)
ip_record.save(ignore_permissions = True)
@ -75,17 +76,6 @@ def create_inpatient(patient):
inpatient_record.scheduled_date = today()
return inpatient_record
def get_patient():
patient = get_random("Patient")
if not patient:
patient = frappe.new_doc("Patient")
patient.patient_name = "Test Patient"
patient.sex = "Male"
patient.save(ignore_permissions=True)
return patient.name
return patient
def get_healthcare_service_unit():
service_unit = get_random("Healthcare Service Unit", filters={"inpatient_occupancy": 1})
if not service_unit:

View File

@ -1,238 +1,78 @@
{
"allow_copy": 1,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"beta": 1,
"creation": "2016-09-16 16:53:06.882970",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "Document",
"editable_grid": 1,
"actions": [],
"allow_copy": 1,
"beta": 1,
"creation": "2016-09-16 16:53:06.882970",
"doctype": "DocType",
"document_type": "Document",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"lab_test_code",
"lab_test_name",
"invoiced",
"column_break_4",
"lab_test_comment",
"lab_test_created"
],
"fields": [
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "lab_test_code",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 1,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Test Code",
"length": 0,
"no_copy": 0,
"options": "Lab Test Template",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
"fieldname": "lab_test_code",
"fieldtype": "Link",
"ignore_user_permissions": 1,
"in_list_view": 1,
"label": "Lab Test",
"options": "Lab Test Template",
"reqd": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_from": "lab_test_code.lab_test_name",
"fieldname": "lab_test_name",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Test",
"length": 0,
"no_copy": 0,
"options": "",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
"fetch_from": "lab_test_code.lab_test_name",
"fieldname": "lab_test_name",
"fieldtype": "Data",
"in_list_view": 1,
"label": "Lab Test Name"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "0",
"fieldname": "invoiced",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Invoiced",
"length": 0,
"no_copy": 1,
"options": "",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 1,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
"default": "0",
"fieldname": "invoiced",
"fieldtype": "Check",
"label": "Invoiced",
"no_copy": 1,
"read_only": 1,
"search_index": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_4",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
"fieldname": "column_break_4",
"fieldtype": "Column Break"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "lab_test_comment",
"fieldtype": "Small Text",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 1,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Comments",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
"fieldname": "lab_test_comment",
"fieldtype": "Small Text",
"ignore_xss_filter": 1,
"in_list_view": 1,
"label": "Comments"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "lab_test_created",
"fieldtype": "Check",
"hidden": 1,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Test Created",
"length": 0,
"no_copy": 1,
"permlevel": 0,
"precision": "",
"print_hide": 1,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 1,
"reqd": 0,
"search_index": 1,
"set_only_once": 0,
"translatable": 0,
"unique": 0
"default": "0",
"fieldname": "lab_test_created",
"fieldtype": "Check",
"hidden": 1,
"label": "Test Created",
"no_copy": 1,
"print_hide": 1,
"report_hide": 1,
"search_index": 1
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 1,
"max_attachments": 0,
"modified": "2018-09-04 09:02:18.592637",
"modified_by": "Administrator",
"module": "Healthcare",
"name": "Lab Prescription",
"name_case": "",
"owner": "Administrator",
"permissions": [],
"quick_entry": 0,
"read_only": 0,
"read_only_onload": 0,
"restrict_to_domain": "Healthcare",
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 0,
"track_seen": 0,
"track_views": 0
],
"istable": 1,
"links": [],
"modified": "2020-02-26 17:03:00.255560",
"modified_by": "Administrator",
"module": "Healthcare",
"name": "Lab Prescription",
"owner": "Administrator",
"permissions": [],
"restrict_to_domain": "Healthcare",
"sort_field": "modified",
"sort_order": "DESC"
}

View File

@ -29,7 +29,7 @@ frappe.ui.form.on('Lab Test', {
get_lab_test_prescribed(frm);
});
}
if(frm.doc.docstatus==1 && frm.doc.status!='Approved' && frm.doc.status!='Rejected' && frappe.defaults.get_default("require_test_result_approval") && frappe.user.has_role("LabTest Approver")){
if(frm.doc.docstatus==1 && frm.doc.status!='Approved' && frm.doc.status!='Rejected' && frappe.defaults.get_default("lab_test_approval_required") && frappe.user.has_role("LabTest Approver")){
frm.add_custom_button(__('Approve'), function() {
status_update(1,frm);
});

File diff suppressed because it is too large Load Diff

View File

@ -191,20 +191,24 @@ def create_specials(template, lab_test):
special.template = template.name
def create_sample_doc(template, patient, invoice):
if(template.sample):
sample_exist = frappe.db.exists({
if template.sample:
sample_exists = frappe.db.exists({
"doctype": "Sample Collection",
"patient": patient.name,
"docstatus": 0,
"sample": template.sample})
if sample_exist :
#Update Sample Collection by adding quantity
sample_collection = frappe.get_doc("Sample Collection",sample_exist[0][0])
quantity = int(sample_collection.sample_quantity)+int(template.sample_quantity)
if(template.sample_collection_details):
sample_collection_details = sample_collection.sample_collection_details+"\n==============\n"+"Test :"+template.lab_test_name+"\n"+"Collection Detials:\n\t"+template.sample_collection_details
frappe.db.set_value("Sample Collection", sample_collection.name, "sample_collection_details",sample_collection_details)
frappe.db.set_value("Sample Collection", sample_collection.name, "sample_quantity",quantity)
"sample": template.sample
})
if sample_exists:
# update Sample Collection by adding quantity
sample_collection = frappe.get_doc("Sample Collection", sample_exists[0][0])
quantity = int(sample_collection.sample_qty) + int(template.sample_qty)
if template.sample_details:
sample_details = sample_collection.sample_details + "\n==============\n" + _("Test: ")
sample_details += (template.get("lab_test_name") or template.get("template")) + "\n"
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_qty", quantity)
else:
#create Sample Collection for template, copy vals from Invoice
@ -216,15 +220,15 @@ def create_sample_doc(template, patient, invoice):
sample_collection.patient_sex = patient.sex
sample_collection.sample = template.sample
sample_collection.sample_uom = template.sample_uom
sample_collection.sample_quantity = template.sample_quantity
if(template.sample_collection_details):
sample_collection.sample_collection_details = "Test :"+template.lab_test_name+"\n"+"Collection Detials:\n\t"+template.sample_collection_details
sample_collection.sample_qty = template.sample_qty
if(template.sample_details):
sample_collection.sample_details = "Test :" + (template.get("lab_test_name") or template.get("template")) +"\n"+"Collection Detials:\n\t"+template.sample_details
sample_collection.save(ignore_permissions=True)
return sample_collection
def create_sample_collection(lab_test, template, patient, invoice):
if(frappe.db.get_value("Healthcare Settings", None, "require_sample_collection") == "1"):
if(frappe.db.get_value("Healthcare Settings", None, "create_sample_collection_for_lab_test") == "1"):
sample_collection = create_sample_doc(template, patient, invoice)
if(sample_collection):
lab_test.sample = sample_collection.name
@ -290,10 +294,14 @@ def insert_lab_test_to_medical_record(doc):
comment = ""
if item.lab_test_comment:
comment = str(item.lab_test_comment)
event = ""
table_row = item.lab_test_name
if item.lab_test_event:
event = item.lab_test_event
table_row = item.lab_test_name +" "+ event +" "+ item.result_value
table_row += " " + item.lab_test_event
if item.result_value:
table_row += " " + item.result_value
if item.normal_range:
table_row += " normal_range("+item.normal_range+")"
table_row += " "+comment

View File

@ -1,145 +1,68 @@
{
"allow_copy": 1,
"allow_guest_to_view": 0,
"allow_import": 1,
"allow_rename": 1,
"autoname": "field:sample",
"beta": 1,
"creation": "2016-04-04 17:35:44.823951",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "Setup",
"editable_grid": 0,
"actions": [],
"allow_copy": 1,
"allow_import": 1,
"allow_rename": 1,
"autoname": "field:sample",
"beta": 1,
"creation": "2016-04-04 17:35:44.823951",
"doctype": "DocType",
"document_type": "Setup",
"engine": "InnoDB",
"field_order": [
"sample",
"sample_uom"
],
"fields": [
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "sample",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Sample",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
"fieldname": "sample",
"fieldtype": "Data",
"in_list_view": 1,
"label": "Sample",
"reqd": 1,
"unique": 1
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 1,
"collapsible": 0,
"columns": 0,
"fieldname": "sample_uom",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "UOM",
"length": 0,
"no_copy": 0,
"options": "",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
"bold": 1,
"fieldname": "sample_uom",
"fieldtype": "Link",
"in_list_view": 1,
"label": "UOM",
"options": "Lab Test UOM"
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2017-08-31 13:46:22.508908",
"modified_by": "Administrator",
"module": "Healthcare",
"name": "Lab Test Sample",
"name_case": "",
"owner": "Administrator",
],
"links": [],
"modified": "2020-01-29 23:02:02.249839",
"modified_by": "Administrator",
"module": "Healthcare",
"name": "Lab Test Sample",
"owner": "Administrator",
"permissions": [
{
"amend": 0,
"apply_user_permissions": 0,
"cancel": 0,
"create": 1,
"delete": 0,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Healthcare Administrator",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"create": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "Healthcare Administrator",
"share": 1,
"write": 1
},
},
{
"amend": 0,
"apply_user_permissions": 0,
"cancel": 0,
"create": 0,
"delete": 0,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Laboratory User",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 0
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "Laboratory User",
"share": 1
}
],
"quick_entry": 1,
"read_only": 0,
"read_only_onload": 0,
"restrict_to_domain": "Healthcare",
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"title_field": "sample",
"track_changes": 1,
"track_seen": 0
],
"quick_entry": 1,
"restrict_to_domain": "Healthcare",
"sort_field": "modified",
"sort_order": "DESC",
"title_field": "sample",
"track_changes": 1
}

View File

@ -35,8 +35,8 @@
"sb_sample_collection",
"sample",
"sample_uom",
"sample_quantity",
"sample_collection_details",
"sample_qty",
"sample_details",
"change_in_item"
],
"fields": [
@ -105,7 +105,7 @@
"description": "If unchecked, the item wont be appear in Sales Invoice, but can be used in group test creation. ",
"fieldname": "is_billable",
"fieldtype": "Check",
"label": "Is billable",
"label": "Is Billable",
"search_index": 1
},
{
@ -114,7 +114,8 @@
"fieldname": "lab_test_rate",
"fieldtype": "Currency",
"in_list_view": 1,
"label": "Standard Selling Rate"
"label": "Rate",
"mandatory_depends_on": "eval:doc.is_billable == 1"
},
{
"depends_on": "eval:doc.lab_test_template_type == 'Single'",
@ -209,18 +210,6 @@
"label": "UOM",
"read_only": 1
},
{
"default": "0",
"fieldname": "sample_quantity",
"fieldtype": "Float",
"label": "Quantity"
},
{
"fieldname": "sample_collection_details",
"fieldtype": "Text",
"ignore_xss_filter": 1,
"label": "Collection Details"
},
{
"default": "0",
"fieldname": "change_in_item",
@ -236,11 +225,23 @@
"fieldname": "disabled",
"fieldtype": "Check",
"label": "Disabled"
},
{
"default": "0",
"fieldname": "sample_qty",
"fieldtype": "Float",
"label": "Quantity"
},
{
"fieldname": "sample_details",
"fieldtype": "Text",
"ignore_xss_filter": 1,
"label": "Collection Details"
}
],
"links": [],
"modified": "2020-01-21 21:02:16.108347",
"modified_by": "ruchamahabal2@gmail.com",
"modified": "2020-03-25 16:53:01.740103",
"modified_by": "Administrator",
"module": "Healthcare",
"name": "Lab Test Template",
"owner": "Administrator",

View File

@ -99,13 +99,10 @@ def create_item_from_template(doc):
# get item price list to insert item price
if doc.lab_test_rate != 0.0:
price_list_name = frappe.db.get_value("Price List", {"selling": 1})
if(doc.lab_test_rate):
if doc.lab_test_rate:
make_item_price(item.name, price_list_name, doc.lab_test_rate)
item.standard_rate = doc.lab_test_rate
else:
make_item_price(item.name, price_list_name, 0.0)
item.standard_rate = 0.0
item.save(ignore_permissions = True)
# Set item in the template
frappe.db.set_value("Lab Test Template", doc.name, "item", item.name)

View File

@ -3,128 +3,77 @@
frappe.ui.form.on('Patient', {
refresh: function (frm) {
frm.set_query("patient", "patient_relation", function () {
frm.set_query('patient', 'patient_relation', function () {
return {
filters: [
["Patient", "name", "!=", frm.doc.name]
['Patient', 'name', '!=', frm.doc.name]
]
};
});
if (frappe.defaults.get_default("patient_master_name") != "Naming Series") {
frm.toggle_display("naming_series", false);
if (frappe.defaults.get_default('patient_name_by') != 'Naming Series') {
frm.toggle_display('naming_series', false);
} else {
erpnext.toggle_naming_series();
}
if (frappe.defaults.get_default("collect_registration_fee") && frm.doc.disabled == 1) {
if (frappe.defaults.get_default('collect_registration_fee') && frm.doc.status == 'Disabled') {
frm.add_custom_button(__('Invoice Patient Registration'), function () {
btn_invoice_registration(frm);
invoice_registration(frm);
});
}
if (frm.doc.patient_name && frappe.user.has_role("Physician")) {
frm.add_custom_button(__('Patient History'), function () {
frappe.route_options = { "patient": frm.doc.name };
frappe.set_route("patient_history");
},"View");
if (frm.doc.patient_name && frappe.user.has_role('Physician')) {
frm.add_custom_button(__('Patient History'), function() {
frappe.route_options = {'patient': frm.doc.name};
frappe.set_route('patient_history');
},'View');
}
if (!frm.doc.__islocal && (frappe.user.has_role("Nursing User") || frappe.user.has_role("Physician"))) {
if (!frm.doc.__islocal && (frappe.user.has_role('Nursing User') || frappe.user.has_role('Physician'))) {
frm.add_custom_button(__('Vital Signs'), function () {
btn_create_vital_signs(frm);
}, "Create");
create_vital_signs(frm);
}, 'Create');
frm.add_custom_button(__('Medical Record'), function () {
create_medical_record(frm);
}, "Create");
}, 'Create');
frm.add_custom_button(__('Patient Encounter'), function () {
btn_create_encounter(frm);
}, "Create");
create_encounter(frm);
}, 'Create');
}
},
onload: function (frm) {
if(!frm.doc.dob){
$(frm.fields_dict['age_html'].wrapper).html("");
$(frm.fields_dict['age_html'].wrapper).html('');
}
if(frm.doc.dob){
$(frm.fields_dict['age_html'].wrapper).html("AGE : " + get_age(frm.doc.dob));
$(frm.fields_dict['age_html'].wrapper).html('AGE : ' + get_age(frm.doc.dob));
}
}
});
frappe.ui.form.on("Patient", "dob", function(frm) {
if(frm.doc.dob) {
var today = new Date();
var birthDate = new Date(frm.doc.dob);
if(today < birthDate){
frappe.msgprint(__("Please select a valid Date"));
frappe.model.set_value(frm.doctype,frm.docname, "dob", "");
frappe.ui.form.on('Patient', 'dob', function(frm) {
if (frm.doc.dob) {
let today = new Date();
let birthDate = new Date(frm.doc.dob);
if (today < birthDate){
frappe.msgprint(__('Please select a valid Date'));
frappe.model.set_value(frm.doctype,frm.docname, 'dob', '');
}
else{
var age_str = get_age(frm.doc.dob);
$(frm.fields_dict['age_html'].wrapper).html("AGE : " + age_str);
else {
let age_str = get_age(frm.doc.dob);
$(frm.fields_dict['age_html'].wrapper).html('AGE : ' + age_str);
}
}
else {
$(frm.fields_dict['age_html'].wrapper).html("");
$(frm.fields_dict['age_html'].wrapper).html('');
}
});
var create_medical_record = function (frm) {
frappe.route_options = {
"patient": frm.doc.name,
"status": "Open",
"reference_doctype": "Patient Medical Record",
"reference_owner": frm.doc.owner
};
frappe.new_doc("Patient Medical Record");
};
var get_age = function (birth) {
var ageMS = Date.parse(Date()) - Date.parse(birth);
var age = new Date();
age.setTime(ageMS);
var years = age.getFullYear() - 1970;
return years + " Year(s) " + age.getMonth() + " Month(s) " + age.getDate() + " Day(s)";
};
var btn_create_vital_signs = function (frm) {
if (!frm.doc.name) {
frappe.throw(__("Please save the patient first"));
}
frappe.route_options = {
"patient": frm.doc.name,
};
frappe.new_doc("Vital Signs");
};
var btn_create_encounter = function (frm) {
if (!frm.doc.name) {
frappe.throw(__("Please save the patient first"));
}
frappe.route_options = {
"patient": frm.doc.name,
};
frappe.new_doc("Patient Encounter");
};
var btn_invoice_registration = function (frm) {
frappe.call({
doc: frm.doc,
method: "invoice_patient_registration",
callback: function(data){
if(!data.exc){
if(data.message.invoice){
/* frappe.show_alert(__('Sales Invoice {0} created',
['<a href="#Form/Sales Invoice/'+data.message.invoice+'">' + data.message.invoice+ '</a>'])); */
frappe.set_route("Form", "Sales Invoice", data.message.invoice);
}
cur_frm.reload_doc();
}
}
});
};
frappe.ui.form.on('Patient Relation', {
patient_relation_add: function(frm){
frm.fields_dict['patient_relation'].grid.get_field('patient').get_query = function(doc){
var patient_list = [];
let patient_list = [];
if(!doc.__islocal) patient_list.push(doc.name);
$.each(doc.patient_relation, function(idx, val){
if (val.patient) patient_list.push(val.patient);
@ -133,3 +82,56 @@ frappe.ui.form.on('Patient Relation', {
};
}
});
let create_medical_record = function (frm) {
frappe.route_options = {
'patient': frm.doc.name,
'status': 'Open',
'reference_doctype': 'Patient Medical Record',
'reference_owner': frm.doc.owner
};
frappe.new_doc('Patient Medical Record');
};
let get_age = function (birth) {
let ageMS = Date.parse(Date()) - Date.parse(birth);
let age = new Date();
age.setTime(ageMS);
let years = age.getFullYear() - 1970;
return years + ' Year(s) ' + age.getMonth() + ' Month(s) ' + age.getDate() + ' Day(s)';
};
let create_vital_signs = function (frm) {
if (!frm.doc.name) {
frappe.throw(__('Please save the patient first'));
}
frappe.route_options = {
'patient': frm.doc.name,
};
frappe.new_doc('Vital Signs');
};
let create_encounter = function (frm) {
if (!frm.doc.name) {
frappe.throw(__('Please save the patient first'));
}
frappe.route_options = {
'patient': frm.doc.name,
};
frappe.new_doc('Patient Encounter');
};
let invoice_registration = function (frm) {
frappe.call({
doc: frm.doc,
method: 'invoice_patient_registration',
callback: function(data) {
if (!data.exc) {
if (data.message.invoice) {
frappe.set_route('Form', 'Sales Invoice', data.message.invoice);
}
cur_frm.reload_doc();
}
}
});
};

View File

@ -1,4 +1,5 @@
{
"actions": [],
"allow_copy": 1,
"allow_import": 1,
"allow_rename": 1,
@ -11,23 +12,29 @@
"engine": "InnoDB",
"field_order": [
"basic_info",
"inpatient_status",
"inpatient_record",
"naming_series",
"first_name",
"middle_name",
"last_name",
"patient_name",
"sex",
"blood_group",
"dob",
"age_html",
"status",
"image",
"column_break_14",
"status",
"inpatient_status",
"inpatient_record",
"customer",
"report_preference",
"mobile",
"email",
"phone",
"disabled",
"report_preference",
"personal_and_social_history",
"occupation",
"column_break_25",
"marital_status",
"sb_relation",
"patient_relation",
"allergy_medical_and_surgical_history",
@ -36,10 +43,6 @@
"column_break_20",
"medical_history",
"surgical_history",
"personal_and_social_history",
"occupation",
"column_break_25",
"marital_status",
"risk_factors",
"tobacco_past_use",
"tobacco_current_use",
@ -78,28 +81,28 @@
{
"fieldname": "naming_series",
"fieldtype": "Select",
"label": "Patient ID",
"label": "Series",
"options": "HLC-PAT-.YYYY.-",
"print_hide": 1,
"report_hide": 1
"report_hide": 1,
"set_only_once": 1
},
{
"bold": 1,
"fieldname": "patient_name",
"fieldtype": "Data",
"in_filter": 1,
"in_global_search": 1,
"in_list_view": 1,
"in_standard_filter": 1,
"label": "Full Name",
"no_copy": 1,
"oldfieldtype": "Data",
"reqd": 1,
"read_only": 1,
"search_index": 1
},
{
"fieldname": "sex",
"fieldtype": "Select",
"fieldtype": "Link",
"label": "Gender",
"options": "\nMale\nFemale\nOther",
"options": "Gender",
"reqd": 1
},
{
@ -125,15 +128,15 @@
"report_hide": 1
},
{
"default": "Active",
"fieldname": "status",
"fieldtype": "Select",
"hidden": 1,
"in_filter": 1,
"in_list_view": 1,
"label": "Status",
"no_copy": 1,
"options": "Active\nDormant\nOpen",
"options": "Active\nDisabled",
"print_hide": 1,
"report_hide": 1
"read_only": 1
},
{
"fieldname": "image",
@ -149,12 +152,12 @@
"fieldtype": "Column Break"
},
{
"description": "If \"Link Customer to Patient\" is checked in Healthcare Settings and an existing Customer is not selected then, a Customer will be created for this Patient for recording transactions in Accounts module.",
"fieldname": "customer",
"fieldtype": "Link",
"ignore_user_permissions": 1,
"label": "Customer",
"options": "Customer",
"set_only_once": 1
"options": "Customer"
},
{
"fieldname": "report_preference",
@ -183,19 +186,8 @@
"fieldname": "phone",
"fieldtype": "Data",
"in_filter": 1,
"in_list_view": 1,
"label": "Phone"
},
{
"default": "0",
"fieldname": "disabled",
"fieldtype": "Check",
"hidden": 1,
"label": "Disabled",
"no_copy": 1,
"print_hide": 1,
"report_hide": 1
},
{
"collapsible": 1,
"fieldname": "sb_relation",
@ -276,25 +268,25 @@
"fieldname": "tobacco_past_use",
"fieldtype": "Data",
"ignore_xss_filter": 1,
"label": "Tobacco Past Use"
"label": "Tobacco Consumption Habbits (Past)"
},
{
"fieldname": "tobacco_current_use",
"fieldtype": "Data",
"ignore_xss_filter": 1,
"label": "Tobacco Current Use"
"label": "Tobacco Consumption Habbits (Present)"
},
{
"fieldname": "alcohol_past_use",
"fieldtype": "Data",
"ignore_xss_filter": 1,
"label": "Alcohol Past Use"
"label": "Alcohol Consumption Habbits (Past)"
},
{
"fieldname": "alcohol_current_use",
"fieldtype": "Data",
"ignore_user_permissions": 1,
"label": "Alcohol Current Use"
"label": "Alcohol Consumption Habbits (Present)"
},
{
"fieldname": "column_break_32",
@ -342,12 +334,30 @@
"label": "Default Currency",
"options": "Currency",
"print_hide": 1
},
{
"fieldname": "last_name",
"fieldtype": "Data",
"label": "Last Name"
},
{
"fieldname": "first_name",
"fieldtype": "Data",
"label": "First Name",
"oldfieldtype": "Data",
"reqd": 1
},
{
"fieldname": "middle_name",
"fieldtype": "Data",
"label": "Middle Name (optional)"
}
],
"icon": "fa fa-user",
"image_field": "image",
"links": [],
"max_attachments": 50,
"modified": "2019-09-25 23:30:49.905893",
"modified": "2020-04-06 12:55:30.807744",
"modified_by": "Administrator",
"module": "Healthcare",
"name": "Patient",

View File

@ -6,46 +6,57 @@ from __future__ import unicode_literals
import frappe
from frappe import _
from frappe.model.document import Document
from frappe.utils import cint, cstr, getdate, flt
from frappe.utils import cint, cstr, getdate
import dateutil
from frappe.model.naming import set_name_by_naming_series
from erpnext.healthcare.doctype.healthcare_settings.healthcare_settings import get_receivable_account,get_income_account,send_registration_sms
from frappe.utils.nestedset import get_root_of
from erpnext.healthcare.doctype.healthcare_settings.healthcare_settings import get_receivable_account, get_income_account, send_registration_sms
class Patient(Document):
def after_insert(self):
if(frappe.db.get_value("Healthcare Settings", None, "manage_customer") == '1' and not self.customer):
create_customer(self)
if(frappe.db.get_value("Healthcare Settings", None, "collect_registration_fee") == '1'):
frappe.db.set_value("Patient", self.name, "disabled", 1)
else:
send_registration_sms(self)
self.reload()
def on_update(self):
def validate(self):
self.set_full_name()
self.add_as_website_user()
def after_insert(self):
self.add_as_website_user()
self.reload()
if frappe.db.get_single_value('Healthcare Settings', 'link_customer_to_patient') and not self.customer:
create_customer(self)
if frappe.db.get_single_value('Healthcare Settings', 'collect_registration_fee'):
frappe.db.set_value('Patient', self.name, 'status', 'Disabled')
else:
send_registration_sms(self)
def set_full_name(self):
if self.last_name:
self.patient_name = ' '.join(filter(None, [self.first_name, self.last_name]))
else:
self.patient_name = self.first_name
def add_as_website_user(self):
if(self.email):
if not frappe.db.exists ("User", self.email):
if self.email:
if not frappe.db.exists ('User', self.email):
user = frappe.get_doc({
"doctype": "User",
"first_name": self.patient_name,
"email": self.email,
"user_type": "Website User"
'doctype': 'User',
'first_name': self.first_name,
'last_name': self.last_name,
'email': self.email,
'user_type': 'Website User'
})
user.flags.ignore_permissions = True
user.add_roles("Patient")
user.add_roles('Patient')
def autoname(self):
patient_master_name = frappe.defaults.get_global_default('patient_master_name')
if patient_master_name == 'Patient Name':
patient_name_by = frappe.db.get_single_value('Healthcare Settings', 'patient_name_by')
if patient_name_by == 'Patient Name':
self.name = self.get_patient_name()
else:
set_name_by_naming_series(self)
def get_patient_name(self):
self.set_full_name()
name = self.patient_name
if frappe.db.get_value("Patient", name):
if frappe.db.get_value('Patient', name):
count = frappe.db.sql("""select ifnull(MAX(CAST(SUBSTRING_INDEX(name, ' ', -1) AS UNSIGNED)), 0) from tabPatient
where name like %s""", "%{0} - %".format(name), as_list=1)[0][0]
count = cint(count) + 1
@ -54,56 +65,62 @@ class Patient(Document):
return name
def get_age(self):
age_str = ""
age_str = ''
if self.dob:
born = getdate(self.dob)
age = dateutil.relativedelta.relativedelta(getdate(), born)
age_str = str(age.years) + " year(s) " + str(age.months) + " month(s) " + str(age.days) + " day(s)"
dob = getdate(self.dob)
age = dateutil.relativedelta.relativedelta(getdate(), dob)
age_str = str(age.years) + ' year(s) ' + str(age.months) + ' month(s) ' + str(age.days) + ' day(s)'
return age_str
def invoice_patient_registration(self):
frappe.db.set_value("Patient", self.name, "disabled", 0)
send_registration_sms(self)
if(flt(frappe.get_value("Healthcare Settings", None, "registration_fee"))>0):
if frappe.db.get_single_value('Healthcare Settings', 'registration_fee'):
company = frappe.defaults.get_user_default('company')
if not company:
company = frappe.db.get_value("Global Defaults", None, "default_company")
company = frappe.db.get_single_value('Global Defaults', 'default_company')
sales_invoice = make_invoice(self.name, company)
sales_invoice.save(ignore_permissions=True)
frappe.db.set_value('Patient', self.name, 'status', 'Active')
send_registration_sms(self)
return {'invoice': sales_invoice.name}
def create_customer(doc):
customer_group = frappe.get_value("Selling Settings", None, "customer_group")
territory = frappe.get_value("Selling Settings", None, "territory")
customer_group = frappe.db.get_single_value('Selling Settings', 'customer_group')
territory = frappe.db.get_single_value('Selling Settings', 'territory')
if not (customer_group and territory):
customer_group = "Commercial"
territory = "Rest Of The World"
frappe.msgprint(_("Please set default customer group and territory in Selling Settings"), alert=True)
customer = frappe.get_doc({"doctype": "Customer",
"customer_name": doc.patient_name,
"customer_group": customer_group,
"territory" : territory,
"customer_type": "Individual"
}).insert(ignore_permissions=True)
frappe.db.set_value("Patient", doc.name, "customer", customer.name)
frappe.msgprint(_("Customer {0} is created.").format(customer.name), alert=True)
customer_group = get_root_of('Customer Group')
territory = get_root_of('Territory')
frappe.msgprint(_('Please set default customer group and territory in Selling Settings'), alert=True)
customer = frappe.get_doc({
'doctype': 'Customer',
'customer_name': doc.patient_name,
'customer_group': customer_group,
'territory' : territory,
'customer_type': 'Individual'
}).insert(ignore_permissions=True, ignore_mandatory=True)
frappe.db.set_value('Patient', doc.name, 'customer', customer.name)
frappe.msgprint(_('Customer {0} is created.').format(customer.name), alert=True)
def make_invoice(patient, company):
sales_invoice = frappe.new_doc("Sales Invoice")
sales_invoice.customer = frappe.get_value("Patient", patient, "customer")
uom = frappe.db.exists('UOM', 'Nos') or frappe.db.get_single_value('Stock Settings', 'stock_uom')
sales_invoice = frappe.new_doc('Sales Invoice')
sales_invoice.customer = frappe.db.get_value('Patient', patient, 'customer')
sales_invoice.due_date = getdate()
sales_invoice.company = company
sales_invoice.is_pos = '0'
sales_invoice.is_pos = 0
sales_invoice.debit_to = get_receivable_account(company)
item_line = sales_invoice.append("items")
item_line.item_name = "Registeration Fee"
item_line.description = "Registeration Fee"
item_line = sales_invoice.append('items')
item_line.item_name = 'Registeration Fee'
item_line.description = 'Registeration Fee'
item_line.qty = 1
item_line.uom = "Nos"
item_line.uom = uom
item_line.conversion_factor = 1
item_line.income_account = get_income_account(None, company)
item_line.rate = frappe.get_value("Healthcare Settings", None, "registration_fee")
item_line.rate = frappe.db.get_single_value('Healthcare Settings', 'registration_fee')
item_line.amount = item_line.rate
sales_invoice.set_missing_values()
return sales_invoice
@ -112,7 +129,7 @@ def make_invoice(patient, company):
def get_patient_detail(patient):
patient_dict = frappe.db.sql("""select * from tabPatient where name=%s""", (patient), as_dict=1)
if not patient_dict:
frappe.throw(_("Patient not found"))
frappe.throw(_('Patient not found'))
vital_sign = frappe.db.sql("""select * from `tabVital Signs` where patient=%s
order by signs_date desc limit 1""", (patient), as_dict=1)

View File

@ -4,8 +4,31 @@
from __future__ import unicode_literals
import unittest
# test_records = frappe.get_test_records('Patient')
import frappe
from erpnext.healthcare.doctype.patient_appointment.test_patient_appointment import create_patient
class TestPatient(unittest.TestCase):
pass
def test_customer_created(self):
frappe.db.sql("""delete from `tabPatient`""")
frappe.db.set_value('Healthcare Settings', None, 'link_customer_to_patient', 1)
patient = create_patient()
self.assertTrue(frappe.db.get_value('Patient', patient, 'customer'))
def test_patient_registration(self):
frappe.db.sql("""delete from `tabPatient`""")
settings = frappe.get_single('Healthcare Settings')
settings.collect_registration_fee = 1
settings.registration_fee = 500
settings.save()
patient = create_patient()
patient = frappe.get_doc('Patient', patient)
self.assertEqual(patient.status, 'Disabled')
# check sales invoice and patient status
result = patient.invoice_patient_registration()
self.assertTrue(frappe.db.exists('Sales Invoice', result.get('invoice')))
self.assertTrue(patient.status, 'Active')
settings.collect_registration_fee = 0
settings.save()

View File

@ -1,6 +1,6 @@
// Copyright (c) 2016, ESS LLP and contributors
// For license information, please see license.txt
frappe.provide("erpnext.queries");
frappe.provide('erpnext.queries');
frappe.ui.form.on('Patient Appointment', {
setup: function(frm) {
frm.custom_make_buttons = {
@ -8,151 +8,188 @@ frappe.ui.form.on('Patient Appointment', {
'Patient Encounter': 'Patient Encounter'
};
},
onload: function(frm) {
if (frm.is_new()) {
frm.set_value('appointment_time', null);
frm.disable_save();
}
},
refresh: function(frm) {
frm.set_query("patient", function () {
frm.set_query('patient', function () {
return {
filters: {"disabled": 0}
filters: {'status': 'Active'}
};
});
frm.set_query("practitioner", function() {
frm.set_query('practitioner', function() {
return {
filters: {
'department': frm.doc.department
}
};
});
frm.set_query("service_unit", function(){
frm.set_query('service_unit', function(){
return {
filters: {
"is_group": false,
"allow_appointments": true
'is_group': 0,
'allow_appointments': 1
}
};
});
if(frm.doc.patient){
if (frm.is_new()) {
frm.page.set_primary_action(__('Check Availability'), function() {
if (!frm.doc.patient) {
frappe.msgprint({
title: __('Not Allowed'),
message: __('Please select Patient first'),
indicator: 'red'
});
} else {
frappe.call({
method: 'erpnext.healthcare.doctype.patient_appointment.patient_appointment.check_payment_fields_reqd',
args: {'patient': frm.doc.patient},
callback: function(data) {
if (data.message == true) {
if (frm.doc.mode_of_payment && frm.doc.paid_amount) {
check_and_set_availability(frm);
}
if (!frm.doc.mode_of_payment) {
frappe.msgprint({
title: __('Not Allowed'),
message: __('Please select a Mode of Payment first'),
indicator: 'red'
});
}
if (!frm.doc.paid_amount) {
frappe.msgprint({
title: __('Not Allowed'),
message: __('Please set the Paid Amount first'),
indicator: 'red'
});
}
} else {
check_and_set_availability(frm);
}
}
});
}
});
} else {
frm.page.set_primary_action(__('Save'), () => frm.save());
}
if (frm.doc.patient) {
frm.add_custom_button(__('Patient History'), function() {
frappe.route_options = {"patient": frm.doc.patient};
frappe.set_route("patient_history");
},__("View"));
frappe.route_options = {'patient': frm.doc.patient};
frappe.set_route('patient_history');
}, __('View'));
}
if(frm.doc.status == "Open"){
if (frm.doc.status == 'Open' || (frm.doc.status == 'Scheduled' && !frm.doc.__islocal)) {
frm.add_custom_button(__('Cancel'), function() {
btn_update_status(frm, "Cancelled");
update_status(frm, 'Cancelled');
});
frm.add_custom_button(__('Reschedule'), function() {
check_and_set_availability(frm);
});
if(frm.doc.procedure_template){
frm.add_custom_button(__("Procedure"),function(){
btn_create_procedure(frm);
},"Create");
}
else{
frm.add_custom_button(__("Patient Encounter"),function(){
btn_create_encounter(frm);
},"Create");
if (frm.doc.procedure_template) {
frm.add_custom_button(__('Clinical Procedure'), function(){
frappe.model.open_mapped_doc({
method: 'erpnext.healthcare.doctype.clinical_procedure.clinical_procedure.make_procedure',
frm: frm,
});
}, __('Create'));
} else {
frm.add_custom_button(__('Patient Encounter'), function() {
frappe.model.open_mapped_doc({
method: 'erpnext.healthcare.doctype.patient_appointment.patient_appointment.make_encounter',
frm: frm,
});
}, __('Create'));
}
frm.add_custom_button(__('Vital Signs'), function() {
btn_create_vital_signs(frm);
},"Create");
create_vital_signs(frm);
}, __('Create'));
}
if(frm.doc.status == "Scheduled" && !frm.doc.__islocal){
frm.add_custom_button(__('Cancel'), function() {
btn_update_status(frm, "Cancelled");
});
frm.add_custom_button(__('Reschedule'), function() {
check_and_set_availability(frm);
});
if(frm.doc.procedure_template){
frm.add_custom_button(__("Procedure"),function(){
btn_create_procedure(frm);
},"Create");
}
else{
frm.add_custom_button(__("Patient Encounter"),function(){
btn_create_encounter(frm);
},"Create");
}
},
frm.add_custom_button(__('Vital Signs'), function() {
btn_create_vital_signs(frm);
},"Create");
patient: function(frm) {
if (frm.doc.patient) {
frm.trigger('toggle_payment_fields');
}
if(frm.doc.status == "Pending"){
frm.add_custom_button(__('Set Open'), function() {
btn_update_status(frm, "Open");
});
frm.add_custom_button(__('Cancel'), function() {
btn_update_status(frm, "Cancelled");
});
}
frm.set_df_property("get_procedure_from_encounter", "read_only", frm.doc.__islocal ? 0 : 1);
frappe.db.get_value('Healthcare Settings', {name: 'Healthcare Settings'}, 'manage_appointment_invoice_automatically', (r) => {
if(r.manage_appointment_invoice_automatically == 1){
frm.set_df_property("mode_of_payment", "hidden", 0);
frm.set_df_property("paid_amount", "hidden", 0);
frm.set_df_property("mode_of_payment", "reqd", 1);
frm.set_df_property("paid_amount", "reqd", 1);
}
else{
frm.set_df_property("mode_of_payment", "hidden", 1);
frm.set_df_property("paid_amount", "hidden", 1);
frm.set_df_property("mode_of_payment", "reqd", 0);
frm.set_df_property("paid_amount", "reqd", 0);
},
get_procedure_from_encounter: function(frm) {
get_prescribed_procedure(frm);
},
toggle_payment_fields: function(frm) {
frappe.call({
method: 'erpnext.healthcare.doctype.patient_appointment.patient_appointment.check_payment_fields_reqd',
args: {'patient': frm.doc.patient},
callback: function(data) {
if (data.message.fee_validity) {
// if fee validity exists and automated appointment invoicing is enabled,
// show payment fields as non-mandatory
frm.toggle_display('mode_of_payment', 0);
frm.toggle_display('paid_amount', 0);
frm.toggle_reqd('mode_of_payment', 0);
frm.toggle_reqd('paid_amount', 0);
} else {
// if automated appointment invoicing is disabled, hide fields
frm.toggle_display('mode_of_payment', data.message ? 1 : 0);
frm.toggle_display('paid_amount', data.message ? 1 : 0);
frm.toggle_reqd('mode_of_payment', data.message ? 1 : 0);
frm.toggle_reqd('paid_amount', data.message ? 1 :0);
}
}
});
},
check_availability: function(frm) {
check_and_set_availability(frm);
},
onload:function(frm){
if(frm.is_new()) {
frm.set_value("appointment_time", null);
frm.disable_save();
}
},
get_procedure_from_encounter: function(frm) {
get_procedure_prescribed(frm);
}
});
var check_and_set_availability = function(frm) {
var selected_slot = null;
var service_unit = null;
var duration = null;
let check_and_set_availability = function(frm) {
let selected_slot = null;
let service_unit = null;
let duration = null;
show_availability();
function show_empty_state(practitioner, appointment_date) {
frappe.msgprint({
title: __('Not Available'),
message: __("Healthcare Practitioner {0} not available on {1}", [practitioner.bold(), appointment_date.bold()]),
message: __('Healthcare Practitioner {0} not available on {1}', [practitioner.bold(), appointment_date.bold()]),
indicator: 'red'
});
}
function show_availability() {
let selected_practitioner = '';
var d = new frappe.ui.Dialog({
title: __("Available slots"),
let d = new frappe.ui.Dialog({
title: __('Available slots'),
fields: [
{ fieldtype: 'Link', options: 'Medical Department', reqd:1, fieldname: 'department', label: 'Medical Department'},
{ fieldtype: 'Link', options: 'Medical Department', reqd: 1, fieldname: 'department', label: 'Medical Department'},
{ fieldtype: 'Column Break'},
{ fieldtype: 'Link', options: 'Healthcare Practitioner', reqd:1, fieldname: 'practitioner', label: 'Healthcare Practitioner'},
{ fieldtype: 'Link', options: 'Healthcare Practitioner', reqd: 1, fieldname: 'practitioner', label: 'Healthcare Practitioner'},
{ fieldtype: 'Column Break'},
{ fieldtype: 'Date', reqd:1, fieldname: 'appointment_date', label: 'Date'},
{ fieldtype: 'Date', reqd: 1, fieldname: 'appointment_date', label: 'Date'},
{ fieldtype: 'Section Break'},
{ fieldtype: 'HTML', fieldname: 'available_slots'}
],
primary_action_label: __("Book"),
primary_action_label: __('Book'),
primary_action: function() {
frm.set_value('appointment_time', selected_slot);
frm.set_value('service_unit', service_unit || '');
frm.set_value('duration', duration);
frm.set_value('practitioner', d.get_value('practitioner'));
frm.set_value('department', d.get_value('department'));
frm.set_value('appointment_date', d.get_value('appointment_date'));
if (service_unit) {
frm.set_value('service_unit', service_unit);
}
d.hide();
frm.enable_save();
frm.save();
@ -167,16 +204,16 @@ var check_and_set_availability = function(frm) {
'appointment_date': frm.doc.appointment_date
});
d.fields_dict["department"].df.onchange = () => {
d.fields_dict['department'].df.onchange = () => {
d.set_values({
'practitioner': ''
});
var department = d.get_value('department');
if(department){
let department = d.get_value('department');
if (department) {
d.fields_dict.practitioner.get_query = function() {
return {
filters: {
"department": department
'department': department
}
};
};
@ -188,13 +225,13 @@ var check_and_set_availability = function(frm) {
// Field Change Handler
var fd = d.fields_dict;
let fd = d.fields_dict;
d.fields_dict["appointment_date"].df.onchange = () => {
d.fields_dict['appointment_date'].df.onchange = () => {
show_slots(d, fd);
};
d.fields_dict["practitioner"].df.onchange = () => {
if(d.get_value('practitioner') && d.get_value('practitioner') != selected_practitioner){
d.fields_dict['practitioner'].df.onchange = () => {
if (d.get_value('practitioner') && d.get_value('practitioner') != selected_practitioner) {
selected_practitioner = d.get_value('practitioner');
show_slots(d, fd);
}
@ -203,8 +240,8 @@ var check_and_set_availability = function(frm) {
}
function show_slots(d, fd) {
if (d.get_value('appointment_date') && d.get_value('practitioner')){
fd.available_slots.html("");
if (d.get_value('appointment_date') && d.get_value('practitioner')) {
fd.available_slots.html('');
frappe.call({
method: 'erpnext.healthcare.doctype.patient_appointment.patient_appointment.get_availability_data',
args: {
@ -212,13 +249,13 @@ var check_and_set_availability = function(frm) {
date: d.get_value('appointment_date')
},
callback: (r) => {
var data = r.message;
if(data.slot_details.length > 0) {
var $wrapper = d.fields_dict.available_slots.$wrapper;
let data = r.message;
if (data.slot_details.length > 0) {
let $wrapper = d.fields_dict.available_slots.$wrapper;
// make buttons for each slot
var slot_details = data.slot_details;
var slot_html = "";
let slot_details = data.slot_details;
let slot_html = '';
for (let i = 0; i < slot_details.length; i++) {
slot_html = slot_html + `<label>${slot_details[i].slot_name}</label>`;
slot_html = slot_html + `<br/>` + slot_details[i].avail_slot.map(slot => {
@ -232,14 +269,14 @@ var check_and_set_availability = function(frm) {
let booked_moment = moment(booked.appointment_time, 'HH:mm:ss');
let end_time = booked_moment.clone().add(booked.duration, 'minutes');
// Deal with 0 duration appointments
if(booked_moment.isSame(slot_start_time) || booked_moment.isBetween(slot_start_time, slot_to_time)){
if (booked_moment.isSame(slot_start_time) || booked_moment.isBetween(slot_start_time, slot_to_time)) {
if(booked.duration == 0){
disabled = 'disabled="disabled"';
return false;
}
}
// Check for overlaps considering appointment duration
if(slot_start_time.isBefore(end_time) && slot_to_time.isAfter(booked_moment)){
if (slot_start_time.isBefore(end_time) && slot_to_time.isAfter(booked_moment)) {
// There is an overlap
disabled = 'disabled="disabled"';
return false;
@ -263,7 +300,7 @@ var check_and_set_availability = function(frm) {
// blue button when clicked
$wrapper.on('click', 'button', function() {
var $btn = $(this);
let $btn = $(this);
$wrapper.find('button').removeClass('btn-primary');
$btn.addClass('btn-primary');
selected_slot = $btn.attr('data-name');
@ -273,48 +310,57 @@ var check_and_set_availability = function(frm) {
d.get_primary_btn().attr('disabled', null);
});
}else {
// fd.available_slots.html("Please select a valid date.".bold())
} else {
// fd.available_slots.html('Please select a valid date.'.bold())
show_empty_state(d.get_value('practitioner'), d.get_value('appointment_date'));
}
},
freeze: true,
freeze_message: __("Fetching records......")
freeze_message: __('Fetching records......')
});
}else{
fd.available_slots.html("Appointment date and Healthcare Practitioner are Mandatory".bold());
} else {
fd.available_slots.html(__('Appointment date and Healthcare Practitioner are Mandatory').bold());
}
}
};
var get_procedure_prescribed = function(frm){
if(frm.doc.patient){
let get_prescribed_procedure = function(frm) {
if (frm.doc.patient) {
frappe.call({
method:"erpnext.healthcare.doctype.patient_appointment.patient_appointment.get_procedure_prescribed",
method: 'erpnext.healthcare.doctype.patient_appointment.patient_appointment.get_procedure_prescribed',
args: {patient: frm.doc.patient},
callback: function(r){
show_procedure_templates(frm, r.message);
callback: function(r) {
if (r.message && r.message.length) {
show_procedure_templates(frm, r.message);
} else {
frappe.msgprint({
title: __('Not Found'),
message: __('No Prescribed Procedures found for the selected Patient')
});
}
}
});
}
else{
frappe.msgprint(__("Please select Patient to get prescribed procedure"));
} else {
frappe.msgprint({
title: __('Not Allowed'),
message: __('Please select a Patient first')
});
}
};
var show_procedure_templates = function(frm, result){
var d = new frappe.ui.Dialog({
title: __("Prescribed Procedures"),
let show_procedure_templates = function(frm, result){
let d = new frappe.ui.Dialog({
title: __('Prescribed Procedures'),
fields: [
{
fieldtype: "HTML", fieldname: "procedure_template"
fieldtype: 'HTML', fieldname: 'procedure_template'
}
]
});
var html_field = d.fields_dict.procedure_template.$wrapper;
let html_field = d.fields_dict.procedure_template.$wrapper;
html_field.empty();
$.each(result, function(x, y){
var row = $(repl('<div class="col-xs-12" style="padding-top:12px; text-align:center;" >\
$.each(result, function(x, y) {
let row = $(repl('<div class="col-xs-12" style="padding-top:12px; text-align:center;" >\
<div class="col-xs-5"> %(encounter)s <br> %(consulting_practitioner)s <br> %(encounter_date)s </div>\
<div class="col-xs-5"> %(procedure_template)s <br>%(practitioner)s <br> %(date)s</div>\
<div class="col-xs-2">\
@ -326,76 +372,47 @@ var show_procedure_templates = function(frm, result){
encounter:y[2], consulting_practitioner:y[3], encounter_date:y[4],
practitioner:y[5]? y[5]:'', date: y[6]? y[6]:'', department: y[7]? y[7]:''})).appendTo(html_field);
row.find("a").click(function() {
frm.doc.procedure_template = $(this).attr("data-procedure-template");
frm.doc.procedure_prescription = $(this).attr("data-name");
frm.doc.practitioner = $(this).attr("data-practitioner");
frm.doc.appointment_date = $(this).attr("data-date");
frm.doc.department = $(this).attr("data-department");
refresh_field("procedure_template");
refresh_field("procedure_prescription");
refresh_field("appointment_date");
refresh_field("practitioner");
refresh_field("department");
frm.doc.procedure_template = $(this).attr('data-procedure-template');
frm.doc.procedure_prescription = $(this).attr('data-name');
frm.doc.practitioner = $(this).attr('data-practitioner');
frm.doc.appointment_date = $(this).attr('data-date');
frm.doc.department = $(this).attr('data-department');
refresh_field('procedure_template');
refresh_field('procedure_prescription');
refresh_field('appointment_date');
refresh_field('practitioner');
refresh_field('department');
d.hide();
return false;
});
});
if(!result){
var msg = "There are no procedure prescribed for "+frm.doc.patient;
if (!result) {
let msg = __('There are no procedure prescribed for ') + frm.doc.patient;
$(repl('<div class="col-xs-12" style="padding-top:20px;" >%(msg)s</div></div>', {msg: msg})).appendTo(html_field);
}
d.show();
};
var btn_create_procedure = function(frm){
var doc = frm.doc;
frappe.call({
method:"erpnext.healthcare.doctype.clinical_procedure.clinical_procedure.create_procedure",
args: {appointment: doc.name},
callback: function(data){
if(!data.exc){
var doclist = frappe.model.sync(data.message);
frappe.set_route("Form", doclist[0].doctype, doclist[0].name);
}
}
});
};
var btn_create_encounter = function(frm){
var doc = frm.doc;
frappe.call({
method:"erpnext.healthcare.doctype.patient_appointment.patient_appointment.create_encounter",
args: {appointment: doc.name},
callback: function(data){
if(!data.exc){
var doclist = frappe.model.sync(data.message);
frappe.set_route("Form", doclist[0].doctype, doclist[0].name);
}
}
});
};
var btn_create_vital_signs = function (frm) {
if(!frm.doc.patient){
frappe.throw(__("Please select patient"));
let create_vital_signs = function(frm) {
if (!frm.doc.patient) {
frappe.throw(__('Please select patient'));
}
frappe.route_options = {
"patient": frm.doc.patient,
"appointment": frm.doc.name,
'patient': frm.doc.patient,
'appointment': frm.doc.name,
};
frappe.new_doc("Vital Signs");
frappe.new_doc('Vital Signs');
};
var btn_update_status = function(frm, status){
var doc = frm.doc;
let update_status = function(frm, status){
let doc = frm.doc;
frappe.confirm(__('Are you sure you want to cancel this appointment?'),
function() {
frappe.call({
method:
"erpnext.healthcare.doctype.patient_appointment.patient_appointment.update_status",
method: 'erpnext.healthcare.doctype.patient_appointment.patient_appointment.update_status',
args: {appointment_id: doc.name, status:status},
callback: function(data){
if(!data.exc){
callback: function(data) {
if (!data.exc) {
frm.reload_doc();
}
}
@ -404,60 +421,60 @@ var btn_update_status = function(frm, status){
);
};
frappe.ui.form.on("Patient Appointment", "practitioner", function(frm) {
if(frm.doc.practitioner){
frappe.ui.form.on('Patient Appointment', 'practitioner', function(frm) {
if (frm.doc.practitioner) {
frappe.call({
"method": "frappe.client.get",
method: 'frappe.client.get',
args: {
doctype: "Healthcare Practitioner",
doctype: 'Healthcare Practitioner',
name: frm.doc.practitioner
},
callback: function (data) {
frappe.model.set_value(frm.doctype,frm.docname, "department",data.message.department);
frappe.model.set_value(frm.doctype,frm.docname, "paid_amount",data.message.op_consulting_charge);
frappe.model.set_value(frm.doctype, frm.docname, 'department', data.message.department);
frappe.model.set_value(frm.doctype, frm.docname, 'paid_amount', data.message.op_consulting_charge);
}
});
}
});
frappe.ui.form.on("Patient Appointment", "patient", function(frm) {
if(frm.doc.patient){
frappe.ui.form.on('Patient Appointment', 'patient', function(frm) {
if (frm.doc.patient) {
frappe.call({
"method": "frappe.client.get",
method: 'frappe.client.get',
args: {
doctype: "Patient",
doctype: 'Patient',
name: frm.doc.patient
},
callback: function (data) {
var age = null;
if(data.message.dob){
let age = null;
if (data.message.dob) {
age = calculate_age(data.message.dob);
}
frappe.model.set_value(frm.doctype,frm.docname, "patient_age", age);
frappe.model.set_value(frm.doctype,frm.docname, 'patient_age', age);
}
});
}
});
frappe.ui.form.on("Patient Appointment", "appointment_type", function(frm) {
if(frm.doc.appointment_type) {
frappe.ui.form.on('Patient Appointment', 'appointment_type', function(frm) {
if (frm.doc.appointment_type) {
frappe.call({
"method": "frappe.client.get",
method: 'frappe.client.get',
args: {
doctype: "Appointment Type",
doctype: 'Appointment Type',
name: frm.doc.appointment_type
},
callback: function (data) {
frappe.model.set_value(frm.doctype,frm.docname, "duration",data.message.default_duration);
callback: function(data) {
frappe.model.set_value(frm.doctype,frm.docname, 'duration',data.message.default_duration);
}
});
}
});
var calculate_age = function(birth) {
var ageMS = Date.parse(Date()) - Date.parse(birth);
var age = new Date();
let calculate_age = function(birth) {
let ageMS = Date.parse(Date()) - Date.parse(birth);
let age = new Date();
age.setTime(ageMS);
var years = age.getFullYear() - 1970;
return years + " Year(s) " + age.getMonth() + " Month(s) " + age.getDate() + " Day(s)";
let years = age.getFullYear() - 1970;
return years + ' Year(s) ' + age.getMonth() + ' Month(s) ' + age.getDate() + ' Day(s)';
};

View File

@ -6,26 +6,42 @@ from __future__ import unicode_literals
import frappe
from frappe.model.document import Document
import json
from frappe.utils import getdate, add_days, get_time
from frappe.utils import getdate, get_time
from frappe.model.mapper import get_mapped_doc
from frappe import _
import datetime
from frappe.core.doctype.sms_settings.sms_settings import send_sms
from erpnext.hr.doctype.employee.employee import is_holiday
from erpnext.healthcare.doctype.healthcare_settings.healthcare_settings import get_receivable_account,get_income_account
from erpnext.healthcare.utils import validity_exists, service_item_and_practitioner_charge
from erpnext.healthcare.doctype.healthcare_settings.healthcare_settings import get_receivable_account, get_income_account
from erpnext.healthcare.utils import check_fee_validity, get_service_item_and_practitioner_charge, manage_fee_validity
class PatientAppointment(Document):
def on_update(self):
today = datetime.date.today()
def validate(self):
self.validate_overlaps()
self.set_appointment_datetime()
self.validate_customer_created()
self.set_status()
def after_insert(self):
self.update_prescription_details()
invoice_appointment(self)
self.update_fee_validity()
send_confirmation_msg(self)
def set_status(self):
today = getdate()
appointment_date = getdate(self.appointment_date)
# If appointment created for today set as open
if today == appointment_date:
frappe.db.set_value("Patient Appointment", self.name, "status", "Open")
self.reload()
# If appointment is created for today set status as Open else Scheduled
if appointment_date == today:
self.status = 'Open'
elif appointment_date > today:
self.status = 'Scheduled'
def validate_overlaps(self):
end_time = datetime.datetime.combine(getdate(self.appointment_date), get_time(self.appointment_time)) \
+ datetime.timedelta(minutes=float(self.duration))
def validate(self):
end_time = datetime.datetime.combine(getdate(self.appointment_date), get_time(self.appointment_time)) + datetime.timedelta(minutes=float(self.duration))
overlaps = frappe.db.sql("""
select
name, practitioner, patient, appointment_time, duration
@ -41,135 +57,153 @@ class PatientAppointment(Document):
self.appointment_time, end_time.time(), self.appointment_time, end_time.time(), self.appointment_time))
if overlaps:
frappe.throw(_("""Appointment overlaps with {0}.<br> {1} has appointment scheduled
with {2} at {3} having {4} minute(s) duration.""").format(overlaps[0][0], overlaps[0][1], overlaps[0][2], overlaps[0][3], overlaps[0][4]))
overlapping_details = _('Appointment overlaps with ')
overlapping_details += "<b><a href='#Form/Patient Appointment/{0}'>{0}</a></b><br>".format(overlaps[0][0])
overlapping_details += _('{0} has appointment scheduled with {1} at {2} having {3} minute(s) duration.').format(
overlaps[0][1], overlaps[0][2], overlaps[0][3], overlaps[0][4])
frappe.throw(overlapping_details, title=_('Appointments Overlapping'))
def after_insert(self):
def set_appointment_datetime(self):
self.appointment_datetime = "%s %s" % (self.appointment_date, self.appointment_time or "00:00:00")
def validate_customer_created(self):
if frappe.db.get_single_value('Healthcare Settings', 'automate_appointment_invoicing'):
if not frappe.db.get_value('Patient', self.patient, 'customer'):
msg = _("Please set a Customer linked to the Patient")
msg += " <b><a href='#Form/Patient/{0}'>{0}</a></b>".format(self.patient)
frappe.throw(msg, title=_('Customer Not Found'))
def update_prescription_details(self):
if self.procedure_prescription:
frappe.db.set_value("Procedure Prescription", self.procedure_prescription, "appointment_booked", True)
frappe.db.set_value('Procedure Prescription', self.procedure_prescription, 'appointment_booked', 1)
if self.procedure_template:
comments = frappe.db.get_value("Procedure Prescription", self.procedure_prescription, "comments")
comments = frappe.db.get_value('Procedure Prescription', self.procedure_prescription, 'comments')
if comments:
frappe.db.set_value("Patient Appointment", self.name, "notes", comments)
# Check fee validity exists
appointment = self
validity_exist = validity_exists(appointment.practitioner, appointment.patient)
if validity_exist:
fee_validity = frappe.get_doc("Fee Validity", validity_exist[0][0])
frappe.db.set_value('Patient Appointment', self.name, 'notes', comments)
# Check if the validity is valid
appointment_date = getdate(appointment.appointment_date)
if (fee_validity.valid_till >= appointment_date) and (fee_validity.visited < fee_validity.max_visit):
visited = fee_validity.visited + 1
frappe.db.set_value("Fee Validity", fee_validity.name, "visited", visited)
if fee_validity.ref_invoice:
frappe.db.set_value("Patient Appointment", appointment.name, "invoiced", True)
frappe.msgprint(_("{0} has fee validity till {1}").format(appointment.patient, fee_validity.valid_till))
confirm_sms(self)
def update_fee_validity(self):
fee_validity = manage_fee_validity(self)
if fee_validity:
frappe.msgprint(_('{0} has fee validity till {1}').format(self.patient, fee_validity.valid_till))
if frappe.db.get_value("Healthcare Settings", None, "manage_appointment_invoice_automatically") == '1' and \
frappe.db.get_value("Patient Appointment", self.name, "invoiced") != 1:
invoice_appointment(self)
@frappe.whitelist()
def invoice_appointment(appointment_doc):
if not appointment_doc.name:
return False
sales_invoice = frappe.new_doc("Sales Invoice")
sales_invoice.customer = frappe.get_value("Patient", appointment_doc.patient, "customer")
sales_invoice.appointment = appointment_doc.name
sales_invoice.due_date = getdate()
sales_invoice.is_pos = True
sales_invoice.company = appointment_doc.company
sales_invoice.debit_to = get_receivable_account(appointment_doc.company)
item_line = sales_invoice.append("items")
service_item, practitioner_charge = service_item_and_practitioner_charge(appointment_doc)
item_line.item_code = service_item
item_line.description = "Consulting Charges: " + appointment_doc.practitioner
item_line.income_account = get_income_account(appointment_doc.practitioner, appointment_doc.company)
item_line.rate = practitioner_charge
item_line.amount = practitioner_charge
item_line.qty = 1
item_line.reference_dt = "Patient Appointment"
item_line.reference_dn = appointment_doc.name
payments_line = sales_invoice.append("payments")
payments_line.mode_of_payment = appointment_doc.mode_of_payment
payments_line.amount = appointment_doc.paid_amount
sales_invoice.set_missing_values(for_validate = True)
sales_invoice.save(ignore_permissions=True)
sales_invoice.submit()
frappe.msgprint(_("Sales Invoice {0} created as paid").format(sales_invoice.name), alert=True)
def appointment_cancel(appointment_id):
appointment = frappe.get_doc("Patient Appointment", appointment_id)
# If invoiced --> fee_validity update with -1 visit
if appointment.invoiced:
sales_invoice = exists_sales_invoice(appointment)
if sales_invoice and cancel_sales_invoice(sales_invoice):
frappe.msgprint(
_("Appointment {0} and Sales Invoice {1} cancelled").format(appointment.name, sales_invoice.name)
)
else:
validity = validity_exists(appointment.practitioner, appointment.patient)
if validity:
fee_validity = frappe.get_doc("Fee Validity", validity[0][0])
if appointment_valid_in_fee_validity(appointment, fee_validity.valid_till, True, fee_validity.ref_invoice):
visited = fee_validity.visited - 1
frappe.db.set_value("Fee Validity", fee_validity.name, "visited", visited)
frappe.msgprint(
_("Appointment cancelled, Please review and cancel the invoice {0}").format(fee_validity.ref_invoice)
)
else:
frappe.msgprint(_("Appointment cancelled"))
else:
frappe.msgprint(_("Appointment cancelled"))
else:
frappe.msgprint(_("Appointment cancelled"))
def appointment_valid_in_fee_validity(appointment, valid_end_date, invoiced, ref_invoice):
valid_days = frappe.db.get_value("Healthcare Settings", None, "valid_days")
max_visit = frappe.db.get_value("Healthcare Settings", None, "max_visit")
valid_start_date = add_days(getdate(valid_end_date), -int(valid_days))
# Appointments which has same fee validity range with the appointment
appointments = frappe.get_list("Patient Appointment",{'patient': appointment.patient, 'invoiced': invoiced,
'appointment_date':("<=", getdate(valid_end_date)), 'appointment_date':(">=", getdate(valid_start_date)),
'practitioner': appointment.practitioner}, order_by="appointment_date desc", limit=int(max_visit))
if appointments and len(appointments) > 0:
appointment_obj = appointments[len(appointments)-1]
sales_invoice = exists_sales_invoice(appointment_obj)
if sales_invoice.name == ref_invoice:
return True
def check_payment_fields_reqd(patient):
automate_invoicing = frappe.db.get_single_value('Healthcare Settings', 'automate_appointment_invoicing')
free_follow_ups = frappe.db.get_single_value('Healthcare Settings', 'enable_free_follow_ups')
if automate_invoicing:
if free_follow_ups:
fee_validity = frappe.db.exists('Fee Validity', {'patient': patient, 'status': 'Pending'})
if fee_validity:
return {'fee_validity': fee_validity}
if check_is_new_patient(patient):
return False
return True
return False
def invoice_appointment(appointment_doc):
automate_invoicing = frappe.db.get_single_value('Healthcare Settings', 'automate_appointment_invoicing')
appointment_invoiced = frappe.db.get_value('Patient Appointment', appointment_doc.name, 'invoiced')
enable_free_follow_ups = frappe.db.get_single_value('Healthcare Settings', 'enable_free_follow_ups')
if enable_free_follow_ups:
fee_validity = check_fee_validity(appointment_doc)
if fee_validity and fee_validity.status == 'Completed':
fee_validity = None
elif not fee_validity:
if frappe.db.exists('Fee Validity Reference', {'appointment': appointment_doc.name}):
return
if check_is_new_patient(appointment_doc.patient, appointment_doc.name):
return
else:
fee_validity = None
if automate_invoicing and not appointment_invoiced and not fee_validity:
sales_invoice = frappe.new_doc('Sales Invoice')
sales_invoice.customer = frappe.get_value('Patient', appointment_doc.patient, 'customer')
sales_invoice.appointment = appointment_doc.name
sales_invoice.due_date = getdate()
sales_invoice.is_pos = 1
sales_invoice.company = appointment_doc.company
sales_invoice.debit_to = get_receivable_account(appointment_doc.company)
item = sales_invoice.append('items', {})
item = get_appointment_item(appointment_doc, item)
payment = sales_invoice.append('payments', {})
payment.mode_of_payment = appointment_doc.mode_of_payment
payment.amount = appointment_doc.paid_amount
sales_invoice.set_missing_values(for_validate=True)
sales_invoice.flags.ignore_mandatory = True
sales_invoice.save(ignore_permissions=True)
sales_invoice.submit()
frappe.msgprint(_('Sales Invoice {0} created as paid'.format(sales_invoice.name)), alert=True)
frappe.db.set_value('Patient Appointment', appointment_doc.name, 'invoiced', 1)
frappe.db.set_value('Patient Appointment', appointment_doc.name, 'ref_sales_invoice', sales_invoice.name)
def check_is_new_patient(patient, name=None):
filters = {'patient': patient, 'status': ('!=','Cancelled')}
if name:
filters['name'] = ('!=', name)
has_previous_appointment = frappe.db.exists('Patient Appointment', filters)
if has_previous_appointment:
return False
return True
def get_appointment_item(appointment_doc, item):
service_item, practitioner_charge = get_service_item_and_practitioner_charge(appointment_doc)
item.item_code = service_item
item.description = _('Consulting Charges: {0}').format(appointment_doc.practitioner)
item.income_account = get_income_account(appointment_doc.practitioner, appointment_doc.company)
item.cost_center = frappe.get_cached_value('Company', appointment_doc.company, 'cost_center')
item.rate = practitioner_charge
item.amount = practitioner_charge
item.qty = 1
item.reference_dt = 'Patient Appointment'
item.reference_dn = appointment_doc.name
return item
def cancel_appointment(appointment_id):
appointment = frappe.get_doc('Patient Appointment', appointment_id)
if appointment.invoiced:
sales_invoice = check_sales_invoice_exists(appointment)
if sales_invoice and cancel_sales_invoice(sales_invoice):
msg = _('Appointment {0} and Sales Invoice {1} cancelled').format(appointment.name, sales_invoice.name)
else:
msg = _('Appointment Cancelled. Please review and cancel the invoice {0}').format(sales_invoice.name)
else:
fee_validity = manage_fee_validity(appointment)
msg = _('Appointment Cancelled.')
if fee_validity:
msg += _('Fee Validity {0} updated.').format(fee_validity.name)
frappe.msgprint(msg)
def cancel_sales_invoice(sales_invoice):
if frappe.db.get_value("Healthcare Settings", None, "manage_appointment_invoice_automatically") == '1':
if frappe.db.get_single_value('Healthcare Settings', 'automate_appointment_invoicing'):
if len(sales_invoice.items) == 1:
sales_invoice.cancel()
return True
return False
def exists_sales_invoice_item(appointment):
return frappe.db.exists(
"Sales Invoice Item",
{
"reference_dt": "Patient Appointment",
"reference_dn": appointment.name
}
)
def exists_sales_invoice(appointment):
sales_item_exist = exists_sales_invoice_item(appointment)
if sales_item_exist:
sales_invoice = frappe.get_doc("Sales Invoice", frappe.db.get_value("Sales Invoice Item", sales_item_exist, "parent"))
def check_sales_invoice_exists(appointment):
sales_invoice = frappe.db.get_value('Sales Invoice Item', {
'reference_dt': 'Patient Appointment',
'reference_dn': appointment.name
}, 'parent')
if sales_invoice:
sales_invoice = frappe.get_doc('Sales Invoice', sales_invoice)
return sales_invoice
return False
@frappe.whitelist()
def get_availability_data(date, practitioner):
"""
@ -180,169 +214,163 @@ def get_availability_data(date, practitioner):
"""
date = getdate(date)
weekday = date.strftime("%A")
weekday = date.strftime('%A')
available_slots = []
slot_details = []
practitioner_schedule = None
practitioner_doc = frappe.get_doc('Healthcare Practitioner', practitioner)
check_employee_wise_availability(date, practitioner_doc)
if practitioner_doc.practitioner_schedules:
slot_details = get_available_slots(practitioner_doc, date)
else:
frappe.throw(_('{0} does not have a Healthcare Practitioner Schedule. Add it in Healthcare Practitioner master').format(
practitioner), title=_('Practitioner Schedule Not Found'))
if not slot_details:
# TODO: return available slots in nearby dates
frappe.throw(_('Healthcare Practitioner not available on {0}').format(weekday), title=_('Not Available'))
return {'slot_details': slot_details}
def check_employee_wise_availability(date, practitioner_doc):
employee = None
practitioner_obj = frappe.get_doc("Healthcare Practitioner", practitioner)
# Get practitioner employee relation
if practitioner_obj.employee:
employee = practitioner_obj.employee
elif practitioner_obj.user_id:
if frappe.db.exists({
"doctype": "Employee",
"user_id": practitioner_obj.user_id
}):
employee = frappe.get_doc("Employee", {"user_id": practitioner_obj.user_id}).name
if practitioner_doc.employee:
employee = practitioner_doc.employee
elif practitioner_doc.user_id:
employee = frappe.db.get_value('Employee', {'user_id': practitioner_doc.user_id}, 'name')
if employee:
# Check if it is Holiday
# check holiday
if is_holiday(employee, date):
frappe.throw(_("{0} is a company holiday").format(date))
frappe.throw(_('{0} is a holiday'.format(date)), title=_('Not Available'))
# Check if He/She on Leave
# check leave status
leave_record = frappe.db.sql("""select half_day from `tabLeave Application`
where employee = %s and %s between from_date and to_date
and docstatus = 1""", (employee, date), as_dict=True)
if leave_record:
if leave_record[0].half_day:
frappe.throw(_("{0} on Half day Leave on {1}").format(practitioner, date))
frappe.throw(_('{0} is on a Half day Leave on {1}').format(practitioner_doc.name, date), title=_('Not Available'))
else:
frappe.throw(_("{0} on Leave on {1}").format(practitioner, date))
frappe.throw(_('{0} is on Leave on {1}').format(practitioner_doc.name, date), title=_('Not Available'))
# get practitioners schedule
if practitioner_obj.practitioner_schedules:
for schedule in practitioner_obj.practitioner_schedules:
if schedule.schedule:
practitioner_schedule = frappe.get_doc("Practitioner Schedule", schedule.schedule)
else:
frappe.throw(_("{0} does not have a Healthcare Practitioner Schedule. Add it in Healthcare Practitioner master").format(practitioner))
if practitioner_schedule:
available_slots = []
for t in practitioner_schedule.time_slots:
if weekday == t.day:
available_slots.append(t)
def get_available_slots(practitioner_doc, date):
available_slots = []
slot_details = []
weekday = date.strftime('%A')
practitioner = practitioner_doc.name
if available_slots:
appointments = []
for schedule_entry in practitioner_doc.practitioner_schedules:
if schedule_entry.schedule:
practitioner_schedule = frappe.get_doc('Practitioner Schedule', schedule_entry.schedule)
else:
frappe.throw(_('{0} does not have a Healthcare Practitioner Schedule. Add it in Healthcare Practitioner').format(
frappe.bold(practitioner)), title=_('Practitioner Schedule Not Found'))
if schedule.service_unit:
slot_name = schedule.schedule+" - "+schedule.service_unit
allow_overlap = frappe.get_value('Healthcare Service Unit', schedule.service_unit, 'overlap_appointments')
if allow_overlap:
# fetch all appointments to practitioner by service unit
appointments = frappe.get_all(
"Patient Appointment",
filters={"practitioner": practitioner, "service_unit": schedule.service_unit, "appointment_date": date, "status": ["not in",["Cancelled"]]},
fields=["name", "appointment_time", "duration", "status"])
else:
# fetch all appointments to service unit
appointments = frappe.get_all(
"Patient Appointment",
filters={"service_unit": schedule.service_unit, "appointment_date": date, "status": ["not in",["Cancelled"]]},
fields=["name", "appointment_time", "duration", "status"])
else:
slot_name = schedule.schedule
# fetch all appointments to practitioner without service unit
appointments = frappe.get_all(
"Patient Appointment",
filters={"practitioner": practitioner, "service_unit": '', "appointment_date": date, "status": ["not in",["Cancelled"]]},
fields=["name", "appointment_time", "duration", "status"])
if practitioner_schedule:
available_slots = []
for time_slot in practitioner_schedule.time_slots:
if weekday == time_slot.day:
available_slots.append(time_slot)
slot_details.append({"slot_name":slot_name, "service_unit":schedule.service_unit,
"avail_slot":available_slots, 'appointments': appointments})
if available_slots:
appointments = []
# fetch all appointments to practitioner by service unit
filters = {
'practitioner': practitioner,
'service_unit': schedule_entry.service_unit,
'appointment_date': date,
'status': ['not in',['Cancelled']]
}
else:
frappe.throw(_("{0} does not have a Healthcare Practitioner Schedule. Add it in Healthcare Practitioner master").format(practitioner))
if schedule_entry.service_unit:
slot_name = schedule_entry.schedule + ' - ' + schedule_entry.service_unit
allow_overlap = frappe.get_value('Healthcare Service Unit', schedule_entry.service_unit, 'overlap_appointments')
if not allow_overlap:
# fetch all appointments to service unit
filters.pop('practitioner')
else:
slot_name = schedule_entry.schedule
# fetch all appointments to practitioner without service unit
filters['practitioner'] = practitioner
filters.pop('service_unit')
if not available_slots and not slot_details:
# TODO: return available slots in nearby dates
frappe.throw(_("Healthcare Practitioner not available on {0}").format(weekday))
appointments = frappe.get_all(
'Patient Appointment',
filters=filters,
fields=['name', 'appointment_time', 'duration', 'status'])
return {
"slot_details": slot_details
}
slot_details.append({'slot_name':slot_name, 'service_unit':schedule_entry.service_unit,
'avail_slot':available_slots, 'appointments': appointments})
return slot_details
@frappe.whitelist()
def update_status(appointment_id, status):
frappe.db.set_value("Patient Appointment", appointment_id, "status", status)
frappe.db.set_value('Patient Appointment', appointment_id, 'status', status)
appointment_booked = True
if status == "Cancelled":
if status == 'Cancelled':
appointment_booked = False
appointment_cancel(appointment_id)
cancel_appointment(appointment_id)
procedure_prescription = frappe.db.get_value("Patient Appointment", appointment_id, "procedure_prescription")
procedure_prescription = frappe.db.get_value('Patient Appointment', appointment_id, 'procedure_prescription')
if procedure_prescription:
frappe.db.set_value("Procedure Prescription", procedure_prescription, "appointment_booked", appointment_booked)
frappe.db.set_value('Procedure Prescription', procedure_prescription, 'appointment_booked', appointment_booked)
@frappe.whitelist()
def set_open_appointments():
today = getdate()
frappe.db.sql(
"update `tabPatient Appointment` set status='Open' where status = 'Scheduled'"
" and appointment_date = %s", today)
@frappe.whitelist()
def set_pending_appointments():
today = getdate()
frappe.db.sql(
"update `tabPatient Appointment` set status='Pending' where status in "
"('Scheduled','Open') and appointment_date < %s", today)
def confirm_sms(doc):
if frappe.db.get_value("Healthcare Settings", None, "app_con") == '1':
message = frappe.db.get_value("Healthcare Settings", None, "app_con_msg")
def send_confirmation_msg(doc):
if frappe.db.get_single_value('Healthcare Settings', 'send_appointment_confirmation'):
message = frappe.db.get_single_value('Healthcare Settings', 'appointment_confirmation_msg')
send_message(doc, message)
@frappe.whitelist()
def create_encounter(appointment):
appointment = frappe.get_doc("Patient Appointment", appointment)
encounter = frappe.new_doc("Patient Encounter")
encounter.appointment = appointment.name
encounter.patient = appointment.patient
encounter.practitioner = appointment.practitioner
encounter.visit_department = appointment.department
encounter.patient_sex = appointment.patient_sex
encounter.encounter_date = appointment.appointment_date
if appointment.invoiced:
encounter.invoiced = True
return encounter.as_dict()
def make_encounter(source_name, target_doc=None):
doc = get_mapped_doc('Patient Appointment', source_name, {
'Patient Appointment': {
'doctype': 'Patient Encounter',
'field_map': [
['appointment', 'name'],
['patient', 'patient'],
['practitioner', 'practitioner'],
['medical_department', 'department'],
['patient_sex', 'patient_sex'],
['encounter_date', 'appointment_date'],
['invoiced', 'invoiced']
]
}
}, target_doc)
return doc
def remind_appointment():
if frappe.db.get_value("Healthcare Settings", None, "app_rem") == '1':
rem_before = datetime.datetime.strptime(frappe.get_value("Healthcare Settings", None, "rem_before"), "%H:%M:%S")
rem_dt = datetime.datetime.now() + datetime.timedelta(
hours=rem_before.hour, minutes=rem_before.minute, seconds=rem_before.second)
def send_appointment_reminder():
if frappe.db.get_single_value('Healthcare Settings', 'send_appointment_reminder'):
remind_before = datetime.datetime.strptime(frappe.db.get_single_value('Healthcare Settings', 'remind_before'), '%H:%M:%S')
reminder_dt = datetime.datetime.now() + datetime.timedelta(
hours=remind_before.hour, minutes=remind_before.minute, seconds=remind_before.second)
appointment_list = frappe.db.sql(
"select name from `tabPatient Appointment` where start_dt between %s and %s and reminded = 0 ",
(datetime.datetime.now(), rem_dt)
)
appointment_list = frappe.db.get_all('Patient Appointment', {
'appointment_datetime': ['between', (datetime.datetime.now(), reminder_dt)],
'reminded': 0,
'status': ['!=', 'Cancelled']
})
for i in range(0, len(appointment_list)):
doc = frappe.get_doc("Patient Appointment", appointment_list[i][0])
message = frappe.db.get_value("Healthcare Settings", None, "app_rem_msg")
for appointment in appointment_list:
doc = frappe.get_doc('Patient Appointment', appointment.name)
message = frappe.db.get_single_value('Healthcare Settings', 'appointment_reminder_msg')
send_message(doc, message)
frappe.db.set_value("Patient Appointment", doc.name, "reminded",1)
frappe.db.set_value('Patient Appointment', doc.name, 'reminded', 1)
def send_message(doc, message):
patient = frappe.get_doc("Patient", doc.patient)
patient = frappe.get_doc('Patient', doc.patient)
if patient.mobile:
context = {"doc": doc, "alert": doc, "comments": None}
if doc.get("_comments"):
context["comments"] = json.loads(doc.get("_comments"))
context = {'doc': doc, 'alert': doc, 'comments': None}
if doc.get('_comments'):
context['comments'] = json.loads(doc.get('_comments'))
# jinja to string convertion happens here
message = frappe.render_template(message, context)
@ -359,7 +387,7 @@ def get_events(start, end, filters=None):
:param filters: Filters (JSON).
"""
from frappe.desk.calendar import get_event_conditions
conditions = get_event_conditions("Patient Appointment", filters)
conditions = get_event_conditions('Patient Appointment', filters)
data = frappe.db.sql("""
select
@ -381,10 +409,21 @@ def get_events(start, end, filters=None):
return data
@frappe.whitelist()
def get_procedure_prescribed(patient):
return frappe.db.sql("""select pp.name, pp.procedure, pp.parent, ct.practitioner,
ct.encounter_date, pp.practitioner, pp.date, pp.department
from `tabPatient Encounter` ct, `tabProcedure Prescription` pp
where ct.patient=%(patient)s and pp.parent=ct.name and pp.appointment_booked=0
order by ct.creation desc""", {"patient": patient})
order by ct.creation desc""", {'patient': patient})
def update_appointment_status():
# update the status of appointments daily
appointments = frappe.get_all('Patient Appointment', {
'status': ('not in', ['Closed', 'Cancelled'])
}, as_dict=1)
for appointment in appointments:
frappe.get_doc('Patient Appointment', appointment.name).set_status()

View File

@ -3,4 +3,14 @@
*/
frappe.listview_settings['Patient Appointment'] = {
filters: [["status", "=", "Open"]],
get_indicator: function(doc) {
var colors = {
"Open": "orange",
"Scheduled": "yellow",
"Closed": "green",
"Cancelled": "red",
"Expired": "grey"
};
return [__(doc.status), colors[doc.status], "status,=," + doc.status];
}
};

View File

@ -3,8 +3,144 @@
# See license.txt
from __future__ import unicode_literals
import unittest
# test_records = frappe.get_test_records('Patient Appointment')
import frappe
from erpnext.healthcare.doctype.patient_appointment.patient_appointment import update_status
from frappe.utils import nowdate, add_days
from frappe.utils.make_random import get_random
class TestPatientAppointment(unittest.TestCase):
pass
def setUp(self):
frappe.db.sql("""delete from `tabPatient Appointment`""")
frappe.db.sql("""delete from `tabFee Validity""")
def test_status(self):
patient, medical_department, practitioner = create_healthcare_docs()
frappe.db.set_value('Healthcare Settings', None, 'automate_appointment_invoicing', 0)
appointment = create_appointment(patient, practitioner, nowdate())
self.assertEquals(appointment.status, 'Open')
appointment = create_appointment(patient, practitioner, add_days(nowdate(), 2))
self.assertEquals(appointment.status, 'Scheduled')
create_encounter(appointment)
self.assertEquals(frappe.db.get_value('Patient Appointment', appointment.name, 'status'), 'Closed')
def test_invoicing(self):
patient, medical_department, practitioner = create_healthcare_docs()
frappe.db.set_value('Healthcare Settings', None, 'enable_free_follow_ups', 0)
frappe.db.set_value('Healthcare Settings', None, 'automate_appointment_invoicing', 0)
appointment = create_appointment(patient, practitioner, nowdate())
self.assertEqual(frappe.db.get_value('Patient Appointment', appointment.name, 'invoiced'), 0)
frappe.db.set_value('Healthcare Settings', None, 'automate_appointment_invoicing', 1)
appointment = create_appointment(patient, practitioner, add_days(nowdate(), 2), invoice=1)
self.assertEqual(frappe.db.get_value('Patient Appointment', appointment.name, 'invoiced'), 1)
self.assertTrue(frappe.db.get_value('Patient Appointment', appointment.name, 'ref_sales_invoice'))
def test_appointment_cancel(self):
patient, medical_department, practitioner = create_healthcare_docs()
frappe.db.set_value('Healthcare Settings', None, 'enable_free_follow_ups', 1)
appointment = create_appointment(patient, practitioner, nowdate())
fee_validity = frappe.db.get_value('Fee Validity Reference', {'appointment': appointment.name}, 'parent')
# fee validity created
self.assertTrue(fee_validity)
visited = frappe.db.get_value('Fee Validity', fee_validity, 'visited')
update_status(appointment.name, 'Cancelled')
# check fee validity updated
self.assertEqual(frappe.db.get_value('Fee Validity', fee_validity, 'visited'), visited - 1)
frappe.db.set_value('Healthcare Settings', None, 'enable_free_follow_ups', 0)
frappe.db.set_value('Healthcare Settings', None, 'automate_appointment_invoicing', 1)
appointment = create_appointment(patient, practitioner, nowdate(), invoice=1)
update_status(appointment.name, 'Cancelled')
# check invoice cancelled
sales_invoice = frappe.db.get_value('Patient Appointment', appointment.name, 'ref_sales_invoice')
self.assertEqual(frappe.db.get_value('Sales Invoice', sales_invoice, 'status'), 'Cancelled')
def create_healthcare_docs():
patient = create_patient()
practitioner = frappe.db.exists('Healthcare Practitioner', '_Test Healthcare Practitioner')
medical_department = frappe.db.exists('Medical Department', '_Test Medical Department')
if not medical_department:
medical_department = frappe.new_doc('Medical Department')
medical_department.department = '_Test Medical Department'
medical_department.save(ignore_permissions=True)
medical_department = medical_department.name
if not practitioner:
practitioner = frappe.new_doc('Healthcare Practitioner')
practitioner.first_name = '_Test Healthcare Practitioner'
practitioner.gender = 'Female'
practitioner.department = medical_department
practitioner.op_consulting_charge = 500
practitioner.inpatient_visit_charge = 500
practitioner.save(ignore_permissions=True)
practitioner = practitioner.name
return patient, medical_department, practitioner
def create_patient():
patient = frappe.db.exists('Patient', '_Test Patient')
if not patient:
patient = frappe.new_doc('Patient')
patient.first_name = '_Test Patient'
patient.sex = 'Female'
patient.save(ignore_permissions=True)
patient = patient.name
return patient
def create_encounter(appointment=None):
encounter = frappe.new_doc('Patient Encounter')
if appointment:
encounter.appointment = appointment.name
encounter.patient = appointment.patient
encounter.practitioner = appointment.practitioner
encounter.encounter_date = appointment.appointment_date
encounter.encounter_time = appointment.appointment_time
encounter.save()
encounter.submit()
return encounter
def create_appointment(patient, practitioner, appointment_date, invoice=0, procedure_template=0):
item = create_healthcare_service_items()
frappe.db.set_value('Healthcare Settings', None, 'inpatient_visit_charge_item', item)
frappe.db.set_value('Healthcare Settings', None, 'op_consulting_charge_item', item)
appointment = frappe.new_doc('Patient Appointment')
appointment.patient = patient
appointment.practitioner = practitioner
appointment.department = '_Test Medical Department'
appointment.appointment_date = appointment_date
appointment.company = '_Test Company'
appointment.duration = 15
if invoice:
appointment.mode_of_payment = 'Cash'
appointment.paid_amount = 500
if procedure_template:
appointment.procedure_template = create_clinical_procedure_template().get('name')
appointment.save(ignore_permissions=True)
return appointment
def create_healthcare_service_items():
if frappe.db.exists('Item', 'HLC-SI-001'):
return 'HLC-SI-001'
item = frappe.new_doc('Item')
item.item_code = 'HLC-SI-001'
item.item_name = 'Consulting Charges'
item.item_group = 'Services'
item.is_stock_item = 0
item.save()
return item.name
def create_clinical_procedure_template():
if frappe.db.exists('Clinical Procedure Template', 'Knee Surgery and Rehab'):
return frappe.get_doc('Clinical Procedure Template', 'Knee Surgery and Rehab')
template = frappe.new_doc('Clinical Procedure Template')
template.template = 'Knee Surgery and Rehab'
template.item_code = 'Knee Surgery and Rehab'
template.item_group = 'Services'
template.is_billable = 1
template.description = 'Knee Surgery and Rehab'
template.rate = 50000
template.save()
return template

View File

@ -19,297 +19,237 @@ frappe.ui.form.on('Patient Encounter', {
refresh: function(frm) {
refresh_field('drug_prescription');
refresh_field('lab_test_prescription');
if (!frm.doc.__islocal){
frappe.call({
method: 'frappe.client.get_value',
args: {
doctype: 'Patient',
fieldname: 'inpatient_status',
filters: {name: frm.doc.patient}
},
callback: function(data) {
if(data.message && data.message.inpatient_status == "Admission Scheduled" || data.message.inpatient_status == "Admitted"){
frm.add_custom_button(__('Schedule Discharge'), function() {
schedule_discharge(frm);
});
}
else if(data.message.inpatient_status != "Discharge Scheduled"){
frm.add_custom_button(__('Schedule Admission'), function() {
schedule_inpatient(frm);
});
}
}
});
}
frm.add_custom_button(__('Patient History'), function() {
if (frm.doc.patient) {
frappe.route_options = {"patient": frm.doc.patient};
frappe.set_route("patient_history");
} else {
frappe.msgprint(__("Please select Patient"));
if (!frm.doc.__islocal) {
if (frm.doc.inpatient_status == 'Admission Scheduled' || frm.doc.inpatient_status == 'Admitted') {
frm.add_custom_button(__('Schedule Discharge'), function() {
schedule_discharge(frm);
});
} else if (frm.doc.inpatient_status != 'Discharge Scheduled') {
frm.add_custom_button(__('Schedule Admission'), function() {
schedule_inpatient(frm);
});
}
},"View");
frm.add_custom_button(__('Vital Signs'), function() {
btn_create_vital_signs(frm);
},"Create");
frm.add_custom_button(__('Medical Record'), function() {
create_medical_record(frm);
},"Create");
frm.add_custom_button(__("Procedure"),function(){
btn_create_procedure(frm);
},"Create");
frm.add_custom_button(__('Patient History'), function() {
if (frm.doc.patient) {
frappe.route_options = {'patient': frm.doc.patient};
frappe.set_route('patient_history');
} else {
frappe.msgprint(__('Please select Patient'));
}
},'View');
frm.set_query("patient", function () {
frm.add_custom_button(__('Vital Signs'), function() {
create_vital_signs(frm);
},'Create');
frm.add_custom_button(__('Medical Record'), function() {
create_medical_record(frm);
},'Create');
frm.add_custom_button(__('Clinical Procedure'), function() {
create_procedure(frm);
},'Create');
}
frm.set_query('patient', function() {
return {
filters: {"disabled": 0}
filters: {'status': 'Active'}
};
});
frm.set_query("drug_code", "drug_prescription", function() {
frm.set_query('drug_code', 'drug_prescription', function() {
return {
filters: {
is_stock_item:'1'
is_stock_item: 1
}
};
});
frm.set_query("lab_test_code", "lab_test_prescription", function() {
frm.set_query('lab_test_code', 'lab_test_prescription', function() {
return {
filters: {
is_billable:'1'
is_billable: 1
}
};
});
frm.set_query("medical_code", "codification_table", function() {
return {
filters: {
medical_code_standard: frappe.defaults.get_default("default_medical_code_standard")
}
};
});
frm.set_query("appointment", function() {
frm.set_query('appointment', function() {
return {
filters: {
// Scheduled filter for demo ...
status:['in',["Open","Scheduled"]]
status:['in',['Open','Scheduled']]
}
};
});
frm.set_df_property("appointment", "read_only", frm.doc.__islocal ? 0:1);
frm.set_df_property("patient", "read_only", frm.doc.__islocal ? 0:1);
frm.set_df_property("patient_age", "read_only", frm.doc.__islocal ? 0:1);
frm.set_df_property("patient_sex", "read_only", frm.doc.__islocal ? 0:1);
frm.set_df_property("type", "read_only", frm.doc.__islocal ? 0:1);
frm.set_df_property("practitioner", "read_only", frm.doc.__islocal ? 0:1);
frm.set_df_property("visit_department", "read_only", frm.doc.__islocal ? 0:1);
frm.set_df_property("encounter_date", "read_only", frm.doc.__islocal ? 0:1);
frm.set_df_property("encounter_time", "read_only", frm.doc.__islocal ? 0:1);
}
});
var schedule_inpatient = function(frm) {
frappe.call({
method: "erpnext.healthcare.doctype.inpatient_record.inpatient_record.schedule_inpatient",
args: {patient: frm.doc.patient, encounter_id: frm.doc.name, practitioner: frm.doc.practitioner},
callback: function(data) {
if(!data.exc){
frm.reload_doc();
}
},
freeze: true,
freeze_message: "Process Inpatient Scheduling"
});
};
frm.set_df_property('patient', 'read_only', frm.doc.appointment ? 1 : 0);
},
var schedule_discharge = function(frm) {
frappe.call({
method: "erpnext.healthcare.doctype.inpatient_record.inpatient_record.schedule_discharge",
args: {patient: frm.doc.patient, encounter_id: frm.doc.name, practitioner: frm.doc.practitioner},
callback: function(data) {
if(!data.exc){
frm.reload_doc();
}
},
freeze: true,
freeze_message: "Process Discharge"
});
};
appointment: function(frm) {
frm.events.set_appointment_fields(frm);
},
var create_medical_record = function (frm) {
if(!frm.doc.patient){
frappe.throw(__("Please select patient"));
}
frappe.route_options = {
"patient": frm.doc.patient,
"status": "Open",
"reference_doctype": "Patient Medical Record",
"reference_owner": frm.doc.owner
};
frappe.new_doc("Patient Medical Record");
};
patient: function(frm) {
frm.events.set_patient_info(frm);
},
var btn_create_vital_signs = function (frm) {
if(!frm.doc.patient){
frappe.throw(__("Please select patient"));
}
frappe.route_options = {
"patient": frm.doc.patient,
"appointment": frm.doc.appointment
};
frappe.new_doc("Vital Signs");
};
var btn_create_procedure = function (frm) {
if(!frm.doc.patient){
frappe.throw(__("Please select patient"));
}
frappe.route_options = {
"patient": frm.doc.patient,
"medical_department": frm.doc.visit_department
};
frappe.new_doc("Clinical Procedure");
};
frappe.ui.form.on("Patient Encounter", "appointment", function(frm){
if(frm.doc.appointment){
frappe.call({
"method": "frappe.client.get",
args: {
doctype: "Patient Appointment",
name: frm.doc.appointment
},
callback: function (data) {
frappe.model.set_value(frm.doctype,frm.docname, "patient", data.message.patient);
frappe.model.set_value(frm.doctype,frm.docname, "type", data.message.appointment_type);
frappe.model.set_value(frm.doctype,frm.docname, "practitioner", data.message.practitioner);
frappe.model.set_value(frm.doctype,frm.docname, "invoiced", data.message.invoiced);
}
});
}
else{
frappe.model.set_value(frm.doctype,frm.docname, "patient", "");
frappe.model.set_value(frm.doctype,frm.docname, "type", "");
frappe.model.set_value(frm.doctype,frm.docname, "practitioner", "");
frappe.model.set_value(frm.doctype,frm.docname, "invoiced", 0);
}
});
frappe.ui.form.on("Patient Encounter", "practitioner", function(frm) {
if(frm.doc.practitioner){
frappe.call({
"method": "frappe.client.get",
args: {
doctype: "Healthcare Practitioner",
name: frm.doc.practitioner
},
callback: function (data) {
frappe.model.set_value(frm.doctype,frm.docname, "visit_department",data.message.department);
}
});
}
});
frappe.ui.form.on("Patient Encounter", "symptoms_select", function(frm) {
if(frm.doc.symptoms_select){
var symptoms = null;
if(frm.doc.symptoms)
symptoms = frm.doc.symptoms + "\n" +frm.doc.symptoms_select;
else
symptoms = frm.doc.symptoms_select;
frappe.model.set_value(frm.doctype,frm.docname, "symptoms", symptoms);
frappe.model.set_value(frm.doctype,frm.docname, "symptoms_select", null);
}
});
frappe.ui.form.on("Patient Encounter", "diagnosis_select", function(frm) {
if(frm.doc.diagnosis_select){
var diagnosis = null;
if(frm.doc.diagnosis)
diagnosis = frm.doc.diagnosis + "\n" +frm.doc.diagnosis_select;
else
diagnosis = frm.doc.diagnosis_select;
frappe.model.set_value(frm.doctype,frm.docname, "diagnosis", diagnosis);
frappe.model.set_value(frm.doctype,frm.docname, "diagnosis_select", null);
}
});
frappe.ui.form.on("Patient Encounter", "patient", function(frm) {
if(frm.doc.patient){
frappe.call({
"method": "erpnext.healthcare.doctype.patient.patient.get_patient_detail",
args: {
patient: frm.doc.patient
},
callback: function (data) {
var age = "";
if(data.message.dob){
age = calculate_age(data.message.dob);
}
frappe.model.set_value(frm.doctype,frm.docname, "patient_age", age);
frappe.model.set_value(frm.doctype,frm.docname, "patient_sex", data.message.sex);
}
});
}
});
frappe.ui.form.on("Drug Prescription", {
drug_code: function(frm, cdt, cdn) {
var child = locals[cdt][cdn];
if(child.drug_code){
set_appointment_fields: function(frm) {
if (frm.doc.appointment) {
frappe.call({
"method": "frappe.client.get",
method: 'frappe.client.get',
args: {
doctype: "Item",
name: child.drug_code,
doctype: 'Patient Appointment',
name: frm.doc.appointment
},
callback: function (data) {
frappe.model.set_value(cdt, cdn, 'drug_name',data.message.item_name);
callback: function(data) {
let values = {
'patient':data.message.patient,
'type': data.message.appointment_type,
'practitioner': data.message.practitioner,
'invoiced': data.message.invoiced
};
frm.set_value(values);
}
});
}
else {
let values = {
'patient': '',
'patient_name': '',
'type': '',
'practitioner': '',
'invoiced': 0,
'patient_sex': '',
'patient_age': '',
'inpatient_record': '',
'inpatient_status': ''
};
frm.set_value(values);
}
},
set_patient_info: function(frm) {
if (frm.doc.patient) {
frappe.call({
method: 'erpnext.healthcare.doctype.patient.patient.get_patient_detail',
args: {
patient: frm.doc.patient
},
callback: function(data) {
let age = '';
if (data.message.dob) {
age = calculate_age(data.message.dob);
}
frappe.model.set_value(frm.doctype, frm.docname, 'patient_age', age);
frappe.model.set_value(frm.doctype, frm.docname, 'patient_sex', data.message.sex);
if (data.message.inpatient_record) {
frappe.model.set_value(frm.doctype, frm.docname, 'inpatient_record', data.message.inpatient_record);
frappe.model.set_value(frm.doctype, frm.docname, 'inpatient_status', data.message.inpatient_status);
}
}
});
} else {
frappe.model.set_value(frm.doctype, frm.docname, 'patient_sex', '');
frappe.model.set_value(frm.doctype, frm.docname, 'patient_age', '');
frappe.model.set_value(frm.doctype, frm.docname, 'inpatient_record', '');
frappe.model.set_value(frm.doctype, frm.docname, 'inpatient_status', '');
}
}
});
let schedule_inpatient = function(frm) {
frappe.call({
method: 'erpnext.healthcare.doctype.inpatient_record.inpatient_record.schedule_inpatient',
args: {patient: frm.doc.patient, encounter_id: frm.doc.name, practitioner: frm.doc.practitioner},
callback: function(data) {
if (!data.exc) {
frm.reload_doc();
}
},
freeze: true,
freeze_message: __('Process Inpatient Scheduling')
});
};
let schedule_discharge = function(frm) {
frappe.call({
method: 'erpnext.healthcare.doctype.inpatient_record.inpatient_record.schedule_discharge',
args: {patient: frm.doc.patient, encounter_id: frm.doc.name, practitioner: frm.doc.practitioner},
callback: function(data) {
if (!data.exc) {
frm.reload_doc();
}
},
freeze: true,
freeze_message: 'Process Discharge'
});
};
let create_medical_record = function (frm) {
if (!frm.doc.patient) {
frappe.throw(__('Please select patient'));
}
frappe.route_options = {
'patient': frm.doc.patient,
'status': 'Open',
'reference_doctype': 'Patient Medical Record',
'reference_owner': frm.doc.owner
};
frappe.new_doc('Patient Medical Record');
};
let create_vital_signs = function (frm) {
if (!frm.doc.patient) {
frappe.throw(__('Please select patient'));
}
frappe.route_options = {
'patient': frm.doc.patient,
'appointment': frm.doc.appointment,
'encounter': frm.doc.name
};
frappe.new_doc('Vital Signs');
};
let create_procedure = function (frm) {
if (!frm.doc.patient) {
frappe.throw(__('Please select patient'));
}
frappe.route_options = {
'patient': frm.doc.patient,
'medical_department': frm.doc.medical_department
};
frappe.new_doc('Clinical Procedure');
};
frappe.ui.form.on('Drug Prescription', {
dosage: function(frm, cdt, cdn){
frappe.model.set_value(cdt, cdn, 'update_schedule', 1);
var child = locals[cdt][cdn];
if(child.dosage){
frappe.model.set_value(cdt, cdn, 'in_every', 'Day');
let child = locals[cdt][cdn];
if (child.dosage) {
frappe.model.set_value(cdt, cdn, 'interval_uom', 'Day');
frappe.model.set_value(cdt, cdn, 'interval', 1);
}
},
period: function(frm, cdt, cdn){
period: function(frm, cdt, cdn) {
frappe.model.set_value(cdt, cdn, 'update_schedule', 1);
},
in_every: function(frm, cdt, cdn){
interval_uom: function(frm, cdt, cdn) {
frappe.model.set_value(cdt, cdn, 'update_schedule', 1);
var child = locals[cdt][cdn];
if(child.in_every == "Hour"){
let child = locals[cdt][cdn];
if (child.interval_uom == 'Hour') {
frappe.model.set_value(cdt, cdn, 'dosage', null);
}
}
});
frappe.ui.form.on("Procedure Prescription", {
procedure: function(frm, cdt, cdn) {
var child = locals[cdt][cdn];
if(child.procedure){
frappe.call({
"method": "frappe.client.get_value",
args: {
doctype: "Clinical Procedure Template",
fieldname: "medical_department",
filters: {name: child.procedure}
},
callback: function (data) {
frappe.model.set_value(cdt, cdn, 'department',data.message.medical_department);
}
});
}
}
});
var calculate_age = function(birth) {
var ageMS = Date.parse(Date()) - Date.parse(birth);
var age = new Date();
let calculate_age = function(birth) {
let ageMS = Date.parse(Date()) - Date.parse(birth);
let age = new Date();
age.setTime(ageMS);
var years = age.getFullYear() - 1970;
return years + " Year(s) " + age.getMonth() + " Month(s) " + age.getDate() + " Day(s)";
let years = age.getFullYear() - 1970;
return years + ' Year(s) ' + age.getMonth() + ' Month(s) ' + age.getDate() + ' Day(s)';
};

View File

@ -6,59 +6,63 @@ from __future__ import unicode_literals
import frappe
from frappe.model.document import Document
from frappe.utils import cstr
from frappe import _
class PatientEncounter(Document):
def on_update(self):
if(self.appointment):
frappe.db.set_value("Patient Appointment", self.appointment, "status", "Closed")
update_encounter_to_medical_record(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_cancel(self):
if(self.appointment):
frappe.db.set_value("Patient Appointment", self.appointment, "status", "Open")
if self.appointment:
frappe.db.set_value('Patient Appointment', self.appointment, 'status', 'Open')
delete_medical_record(self)
def insert_encounter_to_medical_record(doc):
subject = set_subject_field(doc)
medical_record = frappe.new_doc("Patient Medical Record")
medical_record = frappe.new_doc('Patient Medical Record')
medical_record.patient = doc.patient
medical_record.subject = subject
medical_record.status = "Open"
medical_record.status = 'Open'
medical_record.communication_date = doc.encounter_date
medical_record.reference_doctype = "Patient Encounter"
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_to_medical_record(encounter):
medical_record_id = frappe.db.sql("select name from `tabPatient Medical Record` where reference_name=%s", (encounter.name))
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)
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):
frappe.db.sql("""delete from `tabPatient Medical Record` where reference_name = %s""", (encounter.name))
frappe.db.delete_doc_if_exists('Patient Medical Record', 'reference_name', encounter.name)
def set_subject_field(encounter):
subject = encounter.practitioner+"<br/>"
if(encounter.symptoms):
subject += "Symptoms: "+ cstr(encounter.symptoms)+".<br/>"
subject = encounter.practitioner + '\n'
if encounter.symptoms:
subject += _('Symptoms: ') + cstr(encounter.symptoms) + '\n'
else:
subject += "No Symptoms <br/>"
if(encounter.diagnosis):
subject += "Diagnosis: "+ cstr(encounter.diagnosis)+".<br/>"
subject += _('No Symptoms') + '\n'
if encounter.diagnosis:
subject += _('Diagnosis: ') + cstr(encounter.diagnosis) + '\n'
else:
subject += "No Diagnosis <br/>"
if(encounter.drug_prescription):
subject +="\nDrug(s) Prescribed. "
if(encounter.lab_test_prescription):
subject += "\nTest(s) Prescribed."
if(encounter.procedure_prescription):
subject += "\nProcedure(s) Prescribed."
subject += _('No Diagnosis') + '\n'
if encounter.drug_prescription:
subject += '\n' + _('Drug(s) Prescribed.')
if encounter.lab_test_prescription:
subject += '\n' + _('Test(s) Prescribed.')
if encounter.procedure_prescription:
subject += '\n' + _('Procedure(s) Prescribed.')
return subject

View File

@ -2,5 +2,5 @@
(c) ESS 2015-16
*/
frappe.listview_settings['Patient Encounter'] = {
filters:[["docstatus","!=","1"]]
filters:[["docstatus","!=","2"]]
};

View File

@ -0,0 +1,33 @@
{
"actions": [],
"beta": 1,
"creation": "2020-02-26 16:48:16.835105",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"diagnosis"
],
"fields": [
{
"fieldname": "diagnosis",
"fieldtype": "Link",
"in_list_view": 1,
"label": "Diagnosis",
"options": "Diagnosis",
"reqd": 1
}
],
"istable": 1,
"links": [],
"modified": "2020-02-26 16:58:16.480583",
"modified_by": "Administrator",
"module": "Healthcare",
"name": "Patient Encounter Diagnosis",
"owner": "Administrator",
"permissions": [],
"quick_entry": 1,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1
}

View File

@ -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 PatientEncounterDiagnosis(Document):
pass

View File

@ -0,0 +1,33 @@
{
"actions": [],
"beta": 1,
"creation": "2020-02-26 16:47:00.525657",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"complaint"
],
"fields": [
{
"fieldname": "complaint",
"fieldtype": "Link",
"in_list_view": 1,
"label": "Complaint",
"options": "Complaint",
"reqd": 1
}
],
"istable": 1,
"links": [],
"modified": "2020-02-26 16:57:37.997481",
"modified_by": "Administrator",
"module": "Healthcare",
"name": "Patient Encounter Symptom",
"owner": "Administrator",
"permissions": [],
"quick_entry": 1,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1
}

View File

@ -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 PatientEncounterSymptom(Document):
pass

View File

@ -1,457 +1,155 @@
{
"allow_copy": 1,
"allow_guest_to_view": 0,
"allow_import": 1,
"allow_rename": 0,
"autoname": "naming_series:",
"beta": 1,
"creation": "2016-06-09 11:30:44.972056",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "Setup",
"editable_grid": 0,
"actions": [],
"allow_copy": 1,
"allow_import": 1,
"autoname": "naming_series:",
"beta": 1,
"creation": "2016-06-09 11:30:44.972056",
"doctype": "DocType",
"document_type": "Setup",
"engine": "InnoDB",
"field_order": [
"naming_series",
"patient",
"status",
"column_break_2",
"attach",
"section_break_4",
"subject",
"section_break_8",
"communication_date",
"reference_doctype",
"reference_name",
"column_break_9",
"reference_owner",
"user"
],
"fields": [
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "",
"fieldname": "naming_series",
"fieldtype": "Select",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Series",
"length": 0,
"no_copy": 0,
"options": "HLC-PMR-.YYYY.-",
"permlevel": 0,
"precision": "",
"print_hide": 1,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 1,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
"fieldname": "naming_series",
"fieldtype": "Select",
"label": "Series",
"options": "HLC-PMR-.YYYY.-",
"print_hide": 1,
"report_hide": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "patient",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 1,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Patient",
"length": 0,
"no_copy": 0,
"options": "Patient",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 1,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
"fieldname": "patient",
"fieldtype": "Link",
"ignore_user_permissions": 1,
"in_list_view": 1,
"label": "Patient",
"options": "Patient",
"search_index": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_2",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
"fieldname": "column_break_2",
"fieldtype": "Column Break"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "attach",
"fieldtype": "Attach",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
"fieldname": "attach",
"fieldtype": "Attach",
"label": "Attach Medical Record"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "section_break_4",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
"fieldname": "section_break_4",
"fieldtype": "Section Break"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "subject",
"fieldtype": "Small Text",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 1,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Subject",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
"fieldname": "subject",
"fieldtype": "Small Text",
"ignore_xss_filter": 1,
"label": "Subject"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "status",
"fieldtype": "Select",
"hidden": 1,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Status",
"length": 0,
"no_copy": 0,
"options": "Open\nClose",
"permlevel": 0,
"precision": "",
"print_hide": 1,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
"fieldname": "status",
"fieldtype": "Select",
"label": "Status",
"options": "Open\nClose",
"read_only": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "Today",
"fieldname": "communication_date",
"fieldtype": "Date",
"hidden": 1,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Datetime",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
"default": "Today",
"fieldname": "communication_date",
"fieldtype": "Date",
"in_list_view": 1,
"label": "Datetime",
"read_only": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "reference_doctype",
"fieldtype": "Link",
"hidden": 1,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Reference DocType",
"length": 0,
"no_copy": 0,
"options": "DocType",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 1,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
"fieldname": "reference_doctype",
"fieldtype": "Link",
"in_list_view": 1,
"label": "Reference DocType",
"options": "DocType",
"read_only": 1,
"search_index": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "reference_name",
"fieldtype": "Dynamic Link",
"hidden": 1,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Reference Name",
"length": 0,
"no_copy": 0,
"options": "reference_doctype",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 1,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
"fieldname": "reference_name",
"fieldtype": "Dynamic Link",
"in_list_view": 1,
"label": "Reference Name",
"options": "reference_doctype",
"read_only": 1,
"search_index": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_from": "reference_name.owner",
"fieldname": "reference_owner",
"fieldtype": "Data",
"hidden": 1,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Reference Owner",
"length": 0,
"no_copy": 1,
"options": "",
"permlevel": 0,
"precision": "",
"print_hide": 1,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 1,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
"fetch_from": "reference_name.owner",
"fieldname": "reference_owner",
"fieldtype": "Data",
"label": "Reference Owner",
"no_copy": 1,
"print_hide": 1,
"read_only": 1,
"report_hide": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "__user",
"fieldname": "user",
"fieldtype": "Link",
"hidden": 1,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "User",
"length": 0,
"no_copy": 0,
"options": "User",
"permlevel": 0,
"precision": "",
"print_hide": 1,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 1,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
"default": "__user",
"fieldname": "user",
"fieldtype": "Link",
"label": "User",
"options": "User",
"print_hide": 1,
"read_only": 1,
"report_hide": 1
},
{
"fieldname": "column_break_9",
"fieldtype": "Column Break"
},
{
"fieldname": "section_break_8",
"fieldtype": "Section Break"
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 1,
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2018-08-21 14:44:37.927284",
"modified_by": "Administrator",
"module": "Healthcare",
"name": "Patient Medical Record",
"name_case": "",
"owner": "Administrator",
],
"in_create": 1,
"links": [],
"modified": "2020-03-23 19:26:59.308383",
"modified_by": "Administrator",
"module": "Healthcare",
"name": "Patient Medical Record",
"owner": "Administrator",
"permissions": [
{
"amend": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Physician",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "Physician",
"share": 1,
"write": 1
}
],
"quick_entry": 0,
"read_only": 0,
"read_only_onload": 0,
"restrict_to_domain": "Healthcare",
"search_fields": "patient, subject, communication_date, reference_doctype, reference_name",
"show_name_in_global_search": 1,
"sort_field": "modified",
"sort_order": "DESC",
"title_field": "patient",
"track_changes": 1,
"track_seen": 1,
"track_views": 0
}
],
"restrict_to_domain": "Healthcare",
"search_fields": "patient, subject, communication_date, reference_doctype, reference_name",
"show_name_in_global_search": 1,
"sort_field": "modified",
"sort_order": "DESC",
"title_field": "patient",
"track_changes": 1,
"track_seen": 1
}

View File

@ -3,8 +3,86 @@
# See license.txt
from __future__ import unicode_literals
import unittest
# test_records = frappe.get_test_records('Patient Medical Record')
import frappe
from frappe.utils import nowdate
from erpnext.healthcare.doctype.patient_appointment.test_patient_appointment import create_encounter, create_healthcare_docs, create_appointment
class TestPatientMedicalRecord(unittest.TestCase):
pass
def setUp(self):
frappe.db.set_value('Healthcare Settings', None, 'enable_free_follow_ups', 0)
frappe.db.set_value('Healthcare Settings', None, 'automate_appointment_invoicing', 1)
def test_medical_record(self):
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)
vital_signs = create_vital_signs(appointment)
# check for vital signs
medical_rec = frappe.db.exists('Patient Medical Record', {'status': 'Open', 'reference_name': vital_signs.name})
self.assertTrue(medical_rec)
appointment = create_appointment(patient, practitioner, nowdate(), invoice=1, procedure_template=1)
procedure = create_procedure(appointment)
procedure.start_procedure()
procedure.complete_procedure()
# check for clinical procedure
medical_rec = frappe.db.exists('Patient Medical Record', {'status': 'Open', 'reference_name': procedure.name})
self.assertTrue(medical_rec)
template = create_lab_test_template(medical_department)
lab_test = create_lab_test(template, patient)
# check for lab test
medical_rec = frappe.db.exists('Patient Medical Record', {'status': 'Open', 'reference_name': lab_test.name})
self.assertTrue(medical_rec)
def create_procedure(appointment):
if appointment:
procedure = frappe.new_doc('Clinical Procedure')
procedure.procedure_template = appointment.procedure_template
procedure.appointment = appointment.name
procedure.patient = appointment.patient
procedure.practitioner = appointment.practitioner
procedure.medical_department = appointment.department
procedure.start_dt = appointment.appointment_date
procedure.start_time = appointment.appointment_time
procedure.save()
procedure.submit()
return procedure
def create_vital_signs(appointment):
vital_signs = frappe.new_doc('Vital Signs')
vital_signs.patient = appointment.patient
vital_signs.signs_date = appointment.appointment_date
vital_signs.signs_time = appointment.appointment_time
vital_signs.temperature = 38.5
vital_signs.save()
vital_signs.submit()
return vital_signs
def create_lab_test_template(medical_department):
if frappe.db.exists('Lab Test Template', 'Blood Test'):
return 'Blood Test'
template = frappe.new_doc('Lab Test Template')
template.lab_test_name = 'Blood Test'
template.lab_test_code = 'Blood Test'
template.lab_test_group = 'Services'
template.department = medical_department
template.is_billable = 1
template.lab_test_rate = 2000
template.save()
return template.name
def create_lab_test(template, patient):
lab_test = frappe.new_doc('Lab Test')
lab_test.patient = patient
lab_test.patient_sex = frappe.db.get_value('Patient', patient, 'sex')
lab_test.template = template
lab_test.save()
lab_test.submit()
return lab_test

View File

@ -1,134 +1,52 @@
{
"allow_copy": 1,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"beta": 1,
"creation": "2017-04-26 15:40:11.561855",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "",
"editable_grid": 1,
"engine": "InnoDB",
"actions": [],
"allow_copy": 1,
"beta": 1,
"creation": "2017-04-26 15:40:11.561855",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"patient",
"relation",
"description"
],
"fields": [
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "relation",
"fieldtype": "Select",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Relation",
"length": 0,
"no_copy": 0,
"options": "\nFather\nMother\nSpouse\nSiblings\nFamily",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 1,
"set_only_once": 0,
"unique": 0
},
"fieldname": "relation",
"fieldtype": "Select",
"in_list_view": 1,
"label": "Relation",
"options": "\nFather\nMother\nSpouse\nSiblings\nFamily\nOther",
"search_index": 1
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "patient",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 1,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Patient",
"length": 0,
"no_copy": 0,
"options": "Patient",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
"fieldname": "patient",
"fieldtype": "Link",
"ignore_user_permissions": 1,
"in_list_view": 1,
"label": "Patient",
"options": "Patient",
"reqd": 1
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "description",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 1,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Description",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
"fieldname": "description",
"fieldtype": "Small Text",
"ignore_xss_filter": 1,
"label": "Description"
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 1,
"max_attachments": 0,
"modified": "2017-10-04 16:12:45.485333",
"modified_by": "Administrator",
"module": "Healthcare",
"name": "Patient Relation",
"name_case": "",
"owner": "Administrator",
"permissions": [],
"quick_entry": 1,
"read_only": 0,
"read_only_onload": 0,
"restrict_to_domain": "Healthcare",
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1,
"track_seen": 0
],
"istable": 1,
"links": [],
"modified": "2020-01-29 12:45:40.081899",
"modified_by": "Administrator",
"module": "Healthcare",
"name": "Patient Relation",
"owner": "Administrator",
"permissions": [],
"quick_entry": 1,
"restrict_to_domain": "Healthcare",
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1
}

View File

@ -5,9 +5,9 @@ frappe.ui.form.on('Practitioner Schedule', {
refresh: function(frm) {
cur_frm.fields_dict["time_slots"].grid.wrapper.find('.grid-add-row').hide();
cur_frm.fields_dict["time_slots"].grid.add_custom_button(__('Add Time Slots'), () => {
var d = new frappe.ui.Dialog({
let d = new frappe.ui.Dialog({
fields: [
{fieldname: 'days', label: __('Select Days'), fieldtype:'MultiSelect',
{fieldname: 'days', label: __('Select Days'), fieldtype: 'MultiSelect',
options:[
{value:'Sunday', label:__('Sunday')},
{value:'Monday', label:__('Monday')},
@ -17,40 +17,41 @@ frappe.ui.form.on('Practitioner Schedule', {
{value:'Friday', label:__('Friday')},
{value:'Saturday', label:__('Saturday')},
], reqd: 1},
{fieldname: 'from_time', label:__('From'), fieldtype:'Time',
{fieldname: 'from_time', label: __('From'), fieldtype: 'Time',
'default': '09:00:00', reqd: 1},
{fieldname: 'to_time', label:__('To'), fieldtype:'Time',
{fieldname: 'to_time', label: __('To'), fieldtype: 'Time',
'default': '12:00:00', reqd: 1},
{fieldname: 'duration', label:__('Appointment Duration (mins)'),
{fieldname: 'duration', label: __('Appointment Duration (mins)'),
fieldtype:'Int', 'default': 15, reqd: 1},
],
primary_action_label: __('Add Timeslots'),
primary_action: () => {
var values = d.get_values();
if(values) {
let values = d.get_values();
if (values) {
let slot_added = false;
values.days.split(',').forEach(function(day){
day = $.trim(day);
if(['Sunday', 'Monday', 'Tuesday', 'Wednesday',
if (['Sunday', 'Monday', 'Tuesday', 'Wednesday',
'Thursday', 'Friday', 'Saturday'].includes(day)){
add_slots(day);
}
});
function check_overlap_or_add_slot(week_day, cur_time, end_time, add_slots_to_child){
let overlap = false;
while(cur_time < end_time) {
while (cur_time < end_time) {
let add_to_child = true;
let to_time = cur_time.clone().add(values.duration, 'minutes');
if(to_time <= end_time) {
if(frm.doc.time_slots){
if (to_time <= end_time) {
if (frm.doc.time_slots){
frm.doc.time_slots.forEach(function(slot) {
if(slot.day == week_day){
if (slot.day == week_day){
let slot_from_moment = moment(slot.from_time, 'HH:mm:ss');
let slot_to_moment = moment(slot.to_time, 'HH:mm:ss');
if(cur_time.isSame(slot_from_moment) || cur_time.isBetween(slot_from_moment, slot_to_moment) ||
to_time.isSame(slot_to_moment) || to_time.isBetween(slot_from_moment, slot_to_moment)){
if (cur_time.isSame(slot_from_moment) || cur_time.isBetween(slot_from_moment, slot_to_moment) ||
to_time.isSame(slot_to_moment) || to_time.isBetween(slot_from_moment, slot_to_moment)) {
overlap = true;
if(add_slots_to_child){
if (add_slots_to_child) {
frappe.show_alert({
message:__('Time slot skiped, the slot {0} to {1} overlap exisiting slot {2} to {3}',
[cur_time.format('HH:mm:ss'), to_time.format('HH:mm:ss'), slot.from_time, slot.to_time]),
@ -63,7 +64,7 @@ frappe.ui.form.on('Practitioner Schedule', {
});
}
// add a new timeslot
if(add_to_child && add_slots_to_child){
if (add_to_child && add_slots_to_child) {
frm.add_child('time_slots', {
from_time: cur_time.format('HH:mm:ss'),
to_time: to_time.format('HH:mm:ss'),
@ -76,10 +77,11 @@ frappe.ui.form.on('Practitioner Schedule', {
}
return overlap;
}
function add_slots(week_day){
function add_slots(week_day) {
let cur_time = moment(values.from_time, 'HH:mm:ss');
let end_time = moment(values.to_time, 'HH:mm:ss');
if(check_overlap_or_add_slot(week_day, cur_time, end_time, false)){
if (check_overlap_or_add_slot(week_day, cur_time, end_time, false)) {
frappe.confirm(__('Schedules for {0} overlaps, do you want to proceed after skiping overlaped slots ?', [week_day]),
function() {
// if Yes
@ -88,21 +90,22 @@ frappe.ui.form.on('Practitioner Schedule', {
function() {
// if No
frappe.show_alert({
message:__('Slots for {0} are not added to the schedule', [week_day]),
indicator:'red'
message: __('Slots for {0} are not added to the schedule', [week_day]),
indicator: 'red'
});
}
);
}
else{
} else {
check_overlap_or_add_slot(week_day, cur_time, end_time, true);
}
}
frm.refresh_field('time_slots');
if(slot_added){
if (slot_added) {
frappe.show_alert({
message:__('Time slots added'),
indicator:'green'
message: __('Time slots added'),
indicator: 'green'
});
}
}

View File

@ -1,160 +1,71 @@
{
"allow_copy": 0,
"allow_guest_to_view": 0,
"allow_import": 1,
"allow_rename": 1,
"autoname": "field:schedule_name",
"beta": 1,
"creation": "2017-05-03 17:28:03.926787",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "",
"editable_grid": 1,
"engine": "InnoDB",
"actions": [],
"allow_import": 1,
"allow_rename": 1,
"autoname": "field:schedule_name",
"beta": 1,
"creation": "2017-05-03 17:28:03.926787",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"disabled",
"schedule_details_section",
"schedule_name",
"time_slots"
],
"fields": [
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "schedule_name",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 1,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Schedule Name",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
"fieldname": "schedule_name",
"fieldtype": "Data",
"ignore_xss_filter": 1,
"in_list_view": 1,
"label": "Schedule Name",
"reqd": 1,
"unique": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "time_slots",
"fieldtype": "Table",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Time Slots",
"length": 0,
"no_copy": 0,
"options": "Healthcare Schedule Time Slot",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
"fieldname": "time_slots",
"fieldtype": "Table",
"label": "Time Slots",
"options": "Healthcare Schedule Time Slot"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "disabled",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Disabled",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 1,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 1,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
"default": "0",
"fieldname": "disabled",
"fieldtype": "Check",
"label": "Disabled",
"print_hide": 1
},
{
"fieldname": "schedule_details_section",
"fieldtype": "Section Break",
"label": "Schedule Details"
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2018-06-29 14:55:34.795995",
"modified_by": "Administrator",
"module": "Healthcare",
"name": "Practitioner Schedule",
"name_case": "",
"owner": "rmehta@gmail.com",
],
"links": [],
"modified": "2020-01-31 12:21:45.975488",
"modified_by": "Administrator",
"module": "Healthcare",
"name": "Practitioner Schedule",
"owner": "rmehta@gmail.com",
"permissions": [
{
"amend": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Healthcare Administrator",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "Healthcare Administrator",
"share": 1,
"write": 1
}
],
"quick_entry": 0,
"read_only": 0,
"read_only_onload": 0,
"restrict_to_domain": "Healthcare",
"show_name_in_global_search": 1,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1,
"track_seen": 0
],
"restrict_to_domain": "Healthcare",
"show_name_in_global_search": 1,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1
}

View File

@ -1,338 +1,99 @@
{
"allow_copy": 1,
"allow_events_in_timeline": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"beta": 1,
"creation": "2017-11-17 15:52:48.324157",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "",
"editable_grid": 1,
"engine": "InnoDB",
"actions": [],
"allow_copy": 1,
"beta": 1,
"creation": "2017-11-17 15:52:48.324157",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"procedure",
"procedure_name",
"department",
"practitioner",
"date",
"comments",
"appointment_booked",
"procedure_created",
"invoiced"
],
"fields": [
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "procedure",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Procedure",
"length": 0,
"no_copy": 0,
"options": "Clinical Procedure Template",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
"fieldname": "procedure",
"fieldtype": "Link",
"in_list_view": 1,
"label": "Clinical Procedure",
"options": "Clinical Procedure Template",
"reqd": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_from": "procedure.template",
"fieldname": "procedure_name",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Procedure Name",
"length": 0,
"no_copy": 0,
"options": "",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
"fetch_from": "procedure.template",
"fieldname": "procedure_name",
"fieldtype": "Data",
"in_list_view": 1,
"label": "Procedure Name"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "department",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Department",
"length": 0,
"no_copy": 0,
"options": "Medical Department",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
"fetch_from": "procedure.medical_department",
"fieldname": "department",
"fieldtype": "Link",
"label": "Department",
"options": "Medical Department"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "practitioner",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Referral",
"length": 0,
"no_copy": 0,
"options": "Healthcare Practitioner",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
"fieldname": "practitioner",
"fieldtype": "Link",
"in_list_view": 1,
"label": "Referring Practitioner",
"options": "Healthcare Practitioner"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "date",
"fieldtype": "Date",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Date",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
"fieldname": "date",
"fieldtype": "Date",
"in_list_view": 1,
"label": "Date"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "comments",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Comments",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
"fieldname": "comments",
"fieldtype": "Data",
"label": "Comments"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "appointment_booked",
"fieldtype": "Check",
"hidden": 1,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Appointment Booked",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 1,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
"default": "0",
"fieldname": "appointment_booked",
"fieldtype": "Check",
"hidden": 1,
"label": "Appointment Booked",
"search_index": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "procedure_created",
"fieldtype": "Check",
"hidden": 1,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Procedure Created",
"length": 0,
"no_copy": 1,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 1,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
"default": "0",
"fieldname": "procedure_created",
"fieldtype": "Check",
"hidden": 1,
"label": "Procedure Created",
"no_copy": 1,
"search_index": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "0",
"fieldname": "invoiced",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Invoiced",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 1,
"set_only_once": 0,
"translatable": 0,
"unique": 0
"default": "0",
"fieldname": "invoiced",
"fieldtype": "Check",
"label": "Invoiced",
"read_only": 1,
"search_index": 1
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 1,
"max_attachments": 0,
"modified": "2018-11-04 03:33:35.939816",
"modified_by": "Administrator",
"module": "Healthcare",
"name": "Procedure Prescription",
"name_case": "",
"owner": "Administrator",
"permissions": [],
"quick_entry": 0,
"read_only": 0,
"read_only_onload": 0,
"restrict_to_domain": "Healthcare",
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1,
"track_seen": 0,
"track_views": 0
],
"istable": 1,
"links": [],
"modified": "2020-02-26 15:42:33.988081",
"modified_by": "Administrator",
"module": "Healthcare",
"name": "Procedure Prescription",
"owner": "Administrator",
"permissions": [],
"restrict_to_domain": "Healthcare",
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1
}

View File

@ -3,7 +3,7 @@
frappe.ui.form.on('Sample Collection', {
refresh: function(frm) {
if(frappe.defaults.get_default("require_sample_collection")){
if(frappe.defaults.get_default("create_sample_collection_for_lab_test")){
frm.add_custom_button(__("View Lab Tests"), function() {
frappe.route_options = {"sample": frm.doc.name};
frappe.set_route("List", "Lab Test");

View File

@ -1,687 +1,199 @@
{
"allow_copy": 1,
"allow_guest_to_view": 0,
"allow_import": 1,
"allow_rename": 0,
"autoname": "naming_series:",
"beta": 1,
"creation": "2016-04-05 15:58:18.076977",
"custom": 0,
"default_print_format": "",
"docstatus": 0,
"doctype": "DocType",
"document_type": "Document",
"editable_grid": 0,
"actions": [],
"allow_copy": 1,
"allow_import": 1,
"autoname": "naming_series:",
"beta": 1,
"creation": "2016-04-05 15:58:18.076977",
"doctype": "DocType",
"document_type": "Document",
"engine": "InnoDB",
"field_order": [
"inpatient_record",
"naming_series",
"invoiced",
"patient",
"column_break_4",
"patient_age",
"patient_sex",
"company",
"section_break_6",
"sample",
"sample_uom",
"sample_qty",
"column_break_10",
"collected_by",
"collected_time",
"num_print",
"amended_from",
"section_break_15",
"sample_details"
],
"fields": [
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_from": "patient.inpatient_record",
"fieldname": "inpatient_record",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Inpatient Record",
"length": 0,
"no_copy": 0,
"options": "Inpatient Record",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
"fetch_from": "patient.inpatient_record",
"fieldname": "inpatient_record",
"fieldtype": "Link",
"label": "Inpatient Record",
"options": "Inpatient Record",
"read_only": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 1,
"collapsible": 0,
"columns": 0,
"default": "",
"fieldname": "naming_series",
"fieldtype": "Select",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Series",
"length": 0,
"no_copy": 1,
"options": "HLC-SC-.YYYY.-",
"permlevel": 0,
"precision": "",
"print_hide": 1,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
"bold": 1,
"fieldname": "naming_series",
"fieldtype": "Select",
"label": "Series",
"no_copy": 1,
"options": "HLC-SC-.YYYY.-",
"print_hide": 1,
"reqd": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "invoiced",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Invoiced",
"length": 0,
"no_copy": 1,
"options": "",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 1,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
"default": "0",
"fieldname": "invoiced",
"fieldtype": "Check",
"label": "Invoiced",
"no_copy": 1,
"read_only": 1,
"search_index": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_from": "inpatient_record.patient",
"fieldname": "patient",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 1,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 1,
"label": "Patient",
"length": 0,
"no_copy": 0,
"options": "Patient",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 1,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
"fetch_from": "inpatient_record.patient",
"fieldname": "patient",
"fieldtype": "Link",
"ignore_user_permissions": 1,
"in_standard_filter": 1,
"label": "Patient",
"options": "Patient",
"search_index": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_4",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
"fieldname": "column_break_4",
"fieldtype": "Column Break"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "patient_age",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Age",
"length": 0,
"no_copy": 0,
"options": "",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
"fieldname": "patient_age",
"fieldtype": "Data",
"label": "Age"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_from": "patient.sex",
"fieldname": "patient_sex",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Gender",
"length": 0,
"no_copy": 0,
"options": "",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
"fetch_from": "patient.sex",
"fieldname": "patient_sex",
"fieldtype": "Data",
"label": "Gender"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "company",
"fieldtype": "Link",
"hidden": 1,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Company",
"length": 0,
"no_copy": 0,
"options": "Company",
"permlevel": 0,
"precision": "",
"print_hide": 1,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 1,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
"fieldname": "company",
"fieldtype": "Link",
"hidden": 1,
"label": "Company",
"options": "Company",
"print_hide": 1,
"report_hide": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "section_break_6",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
"fieldname": "section_break_6",
"fieldtype": "Section Break"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "sample",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 1,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 1,
"label": "Sample",
"length": 0,
"no_copy": 0,
"options": "Lab Test Sample",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 1,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
"fieldname": "sample",
"fieldtype": "Link",
"ignore_user_permissions": 1,
"in_list_view": 1,
"in_standard_filter": 1,
"label": "Sample",
"options": "Lab Test Sample",
"reqd": 1,
"search_index": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_from": "sample.sample_uom",
"fieldname": "sample_uom",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "UOM",
"length": 0,
"no_copy": 0,
"options": "",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
"fetch_from": "sample.sample_uom",
"fieldname": "sample_uom",
"fieldtype": "Data",
"in_list_view": 1,
"label": "UOM"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "0",
"fieldname": "sample_quantity",
"fieldtype": "Float",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Quantity",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
"fieldname": "column_break_10",
"fieldtype": "Column Break"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_10",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
"fieldname": "collected_by",
"fieldtype": "Link",
"ignore_user_permissions": 1,
"label": "Collected By",
"options": "User"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "collected_by",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 1,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Collected By",
"length": 0,
"no_copy": 0,
"options": "User",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
"fieldname": "collected_time",
"fieldtype": "Datetime",
"label": "Collected Time"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "collected_time",
"fieldtype": "Datetime",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Collected Time",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
"allow_on_submit": 1,
"default": "1",
"fieldname": "num_print",
"fieldtype": "Int",
"label": "No. of print",
"print_hide": 1,
"report_hide": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 1,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "1",
"fieldname": "num_print",
"fieldtype": "Int",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "No. of print",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 1,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 1,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
"fieldname": "amended_from",
"fieldtype": "Link",
"label": "Amended From",
"no_copy": 1,
"options": "Sample Collection",
"print_hide": 1,
"read_only": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "amended_from",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Amended From",
"length": 0,
"no_copy": 1,
"options": "Sample Collection",
"permlevel": 0,
"print_hide": 1,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
"fieldname": "section_break_15",
"fieldtype": "Section Break"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "section_break_15",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
"default": "0",
"fieldname": "sample_qty",
"fieldtype": "Float",
"in_list_view": 1,
"label": "Quantity"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "sample_collection_details",
"fieldtype": "Long Text",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 1,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Collection Details",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
"fieldname": "sample_details",
"fieldtype": "Long Text",
"ignore_xss_filter": 1,
"label": "Collection Details"
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"is_submittable": 1,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2018-08-21 14:44:39.128989",
"modified_by": "Administrator",
"module": "Healthcare",
"name": "Sample Collection",
"name_case": "",
"owner": "Administrator",
],
"is_submittable": 1,
"links": [],
"modified": "2020-03-25 16:55:52.376834",
"modified_by": "Administrator",
"module": "Healthcare",
"name": "Sample Collection",
"owner": "Administrator",
"permissions": [
{
"amend": 1,
"cancel": 1,
"create": 1,
"delete": 0,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Laboratory User",
"set_user_permissions": 0,
"share": 1,
"submit": 1,
"amend": 1,
"cancel": 1,
"create": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "Laboratory User",
"share": 1,
"submit": 1,
"write": 1
}
],
"quick_entry": 0,
"read_only": 0,
"read_only_onload": 0,
"restrict_to_domain": "Healthcare",
"search_fields": "patient, sample",
"show_name_in_global_search": 1,
"sort_field": "modified",
"sort_order": "DESC",
"title_field": "patient",
"track_changes": 1,
"track_seen": 1,
"track_views": 0
}
],
"restrict_to_domain": "Healthcare",
"search_fields": "patient, sample",
"show_name_in_global_search": 1,
"sort_field": "modified",
"sort_order": "DESC",
"title_field": "patient",
"track_changes": 1,
"track_seen": 1
}

View File

@ -1,49 +1,52 @@
// Copyright (c) 2016, ESS LLP and contributors
// For license information, please see license.txt
frappe.ui.form.on("Vital Signs", "height", function(frm) {
if(frm.doc.height && frm.doc.weight){
calculate_bmi(frm);
frappe.ui.form.on('Vital Signs', {
height: function(frm) {
if (frm.doc.height && frm.doc.weight) {
calculate_bmi(frm);
}
},
weight: function(frm) {
if (frm.doc.height && frm.doc.weight) {
calculate_bmi(frm);
}
},
bp_systolic: function(frm) {
if (frm.doc.bp_systolic && frm.doc.bp_diastolic) {
set_bp(frm);
}
},
bp_diastolic: function(frm) {
if (frm.doc.bp_systolic && frm.doc.bp_diastolic) {
set_bp(frm);
}
}
});
frappe.ui.form.on("Vital Signs", "weight", function(frm) {
if(frm.doc.height && frm.doc.weight){
calculate_bmi(frm);
}
});
var calculate_bmi = function(frm){
let calculate_bmi = function(frm){
// Reference https://en.wikipedia.org/wiki/Body_mass_index
// bmi = weight (in Kg) / height * height (in Meter)
var bmi = (frm.doc.weight/(frm.doc.height*frm.doc.height)).toFixed(2);
var bmi_note = null;
if(bmi<18.5){
bmi_note = "Underweight";
}else if(bmi>=18.5 && bmi<25){
bmi_note = "Normal";
}else if(bmi>=25 && bmi<30){
bmi_note = "Overweight";
}else if(bmi>=30){
bmi_note = "Obese";
let bmi = (frm.doc.weight / (frm.doc.height * frm.doc.height)).toFixed(2);
let bmi_note = null;
if (bmi<18.5) {
bmi_note = 'Underweight';
} else if (bmi>=18.5 && bmi<25) {
bmi_note = 'Normal';
} else if (bmi>=25 && bmi<30) {
bmi_note = 'Overweight';
} else if (bmi>=30) {
bmi_note = 'Obese';
}
frappe.model.set_value(frm.doctype,frm.docname, "bmi", bmi);
frappe.model.set_value(frm.doctype,frm.docname, "nutrition_note", bmi_note);
frappe.model.set_value(frm.doctype,frm.docname, 'bmi', bmi);
frappe.model.set_value(frm.doctype,frm.docname, 'nutrition_note', bmi_note);
};
frappe.ui.form.on("Vital Signs", "bp_systolic", function(frm) {
if(frm.doc.bp_systolic && frm.doc.bp_diastolic){
set_bp(frm);
}
});
frappe.ui.form.on("Vital Signs", "bp_diastolic", function(frm) {
if(frm.doc.bp_systolic && frm.doc.bp_diastolic){
set_bp(frm);
}
});
var set_bp = function(frm){
var bp = frm.doc.bp_systolic+"/"+frm.doc.bp_diastolic+" mmHg";
frappe.model.set_value(frm.doctype,frm.docname, "bp", bp);
let set_bp = function(frm){
let bp = frm.doc.bp_systolic+ '/' + frm.doc.bp_diastolic + ' mmHg';
frappe.model.set_value(frm.doctype,frm.docname, 'bp', bp);
};

File diff suppressed because it is too large Load Diff

View File

@ -6,6 +6,7 @@ from __future__ import unicode_literals
import frappe
from frappe.model.document import Document
from frappe.utils import cstr
from frappe import _
class VitalSigns(Document):
def on_submit(self):
@ -16,34 +17,35 @@ class VitalSigns(Document):
def insert_vital_signs_to_medical_record(doc):
subject = set_subject_field(doc)
medical_record = frappe.new_doc("Patient Medical Record")
medical_record = frappe.new_doc('Patient Medical Record')
medical_record.patient = doc.patient
medical_record.subject = subject
medical_record.status = "Open"
medical_record.status = 'Open'
medical_record.communication_date = doc.signs_date
medical_record.reference_doctype = "Vital Signs"
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_id = frappe.db.sql("select name from `tabPatient Medical Record` where reference_name=%s",(doc.name))
if medical_record_id and medical_record_id[0][0]:
frappe.delete_doc("Patient Medical Record", medical_record_id[0][0])
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 = ""
subject = ''
if(doc.temperature):
subject += "Temperature: \n"+ cstr(doc.temperature)+". "
subject += _('Temperature: ') + '\n'+ cstr(doc.temperature) + '. '
if(doc.pulse):
subject += "Pulse: \n"+ cstr(doc.pulse)+". "
subject += _('Pulse: ') + '\n' + cstr(doc.pulse) + '. '
if(doc.respiratory_rate):
subject += "Respiratory Rate: \n"+ cstr(doc.respiratory_rate)+". "
subject += _('Respiratory Rate: ') + '\n' + cstr(doc.respiratory_rate) + '. '
if(doc.bp):
subject += "BP: \n"+ cstr(doc.bp)+". "
subject += _('BP: ') + '\n' + cstr(doc.bp) + '. '
if(doc.bmi):
subject += "BMI: \n"+ cstr(doc.bmi)+". "
subject += _('BMI: ') + '\n' + cstr(doc.bmi) + '. '
if(doc.nutrition_note):
subject += "Note: \n"+ cstr(doc.nutrition_note)+". "
subject += _('Note: ') + '\n' + cstr(doc.nutrition_note) + '. '
return subject

View File

@ -1,205 +0,0 @@
frappe.pages['appointment-analytic'].on_page_load = function(wrapper) {
frappe.ui.make_app_page({
parent: wrapper,
title: 'Appointment Analytics',
single_column: true
});
new erpnext.AppointmentAnalytics(wrapper);
frappe.breadcrumbs.add("Medical");
};
erpnext.AppointmentAnalytics = frappe.views.TreeGridReport.extend({
init: function(wrapper) {
this._super({
title: __("Appointment Analytics"),
parent: $(wrapper).find('.layout-main'),
page: wrapper.page,
doctypes: ["Patient Appointment", "Healthcare Practitioner", "Medical Department", "Appointment Type", "Patient"],
tree_grid: { show: true }
});
this.tree_grids = {
"Medical Department": {
label: __("Department"),
show: true,
item_key: "practitioner",
parent_field: "department",
formatter: function(item) {
return item.name;
}
},
"Healthcare Practitioner": {
label: __("Healthcare Practitioner"),
show: true,
item_key: "practitioner",
formatter: function(item) {
return item.name;
}
},
};
},
setup_columns: function() {
this.tree_grid = this.tree_grids[this.tree_type];
var std_columns = [
{id: "name", name: this.tree_grid.label, field: "name", width: 300},
{id: "total", name: "Total", field: "total", plot: false,
formatter: this.currency_formatter}
];
this.make_date_range_columns();
this.columns = std_columns.concat(this.columns);
},
filters: [
{fieldtype:"Select", label: __("Tree Type"), fieldname: "tree_type",
options:["Healthcare Practitioner", "Medical Department"], filter: function(val, item, opts, me) {
return me.apply_zero_filter(val, item, opts, me);}},
{fieldtype:"Select", label: __("Status"), fieldname: "status",
options:[
{label: __("Select Status"), value: "Select Status..."},
{label: __("Open"), value: "Open"},
{label: __("Closed"), value: "Closed"},
{label: __("Pending"), value: "Pending"},
{label: __("Scheduled"), value: "Scheduled"},
{label: __("Cancelled"), value: "Cancelled"}]},
{fieldtype:"Select", label: __("Type"), link:"Appointment Type", fieldname: "type",
default_value: __("Select Type...")},
{fieldtype:"Select", label: __("Healthcare Practitioner"), link:"Healthcare Practitioner", fieldname: "practitioner",
default_value: __("Select Healthcare Practitioner..."), filter: function(val, item, opts) {
return val == opts.default_value || item.name == val || item._show;
}, link_formatter: {filter_input: "practitioner"}},
{fieldtype:"Select", label: __("Department"), link:"Medical Department", fieldname: "department",
default_value: __("Select Department..."), filter: function(val, item, opts) {
return val == opts.default_value || item.department == val || item._show;
}, link_formatter: {filter_input: "department"}},
{fieldtype:"Date", label: __("From Date"), fieldname: "from_date"},
{fieldtype:"Date", label: __("To Date"), fieldname: "to_date"},
{fieldtype:"Select", label: __("Range"), fieldname: "range",
options:[{label: __("Daily"), value: "Daily"}, {label: __("Weekly"), value: "Weekly"},
{label: __("Monthly"), value: "Monthly"}, {label: __("Quarterly"), value: "Quarterly"},
{label: __("Yearly"), value: "Yearly"}]}
],
setup_filters: function() {
this._super();
this.trigger_refresh_on_change(["tree_type", "practitioner", "department", "status", "type"]);
// this.show_zero_check()
},
init_filter_values: function() {
this._super();
this.filter_inputs.range.val('Quarterly');
},
prepare_data: function() {
var me = this;
if (!this.tl) {
this.tl = frappe.report_dump.data["Patient Appointment"];
}
if(!this.data || me.item_type != me.tree_type) {
var items = null;
if(me.tree_type=='Healthcare Practitioner') {
items = frappe.report_dump.data["Healthcare Practitioner"];
} if(me.tree_type=='Medical Department') {
items = this.prepare_tree("Healthcare Practitioner", "Medical Department");
}
me.item_type = me.tree_type;
me.parent_map = {};
me.item_by_name = {};
me.data = [];
$.each(items, function(i, v) {
var d = copy_dict(v);
me.data.push(d);
me.item_by_name[d.name] = d;
if(d[me.tree_grid.parent_field]) {
me.parent_map[d.name] = d[me.tree_grid.parent_field];
}
me.reset_item_values(d);
});
this.set_indent();
} else {
// otherwise, only reset values
$.each(this.data, function(i, d) {
me.reset_item_values(d);
});
}
this.prepare_balances();
if(me.tree_grid.show) {
this.set_totals(false);
this.update_groups();
} else {
this.set_totals(true);
}
},
prepare_balances: function() {
var me = this;
var from_date = frappe.datetime.str_to_obj(this.from_date);
var status = this.status;
var type = this.type;
var to_date = frappe.datetime.str_to_obj(this.to_date);
$.each(this.tl, function(i, tl) {
if (me.is_default('company') ? true : tl.company === me.company) {
var date = frappe.datetime.str_to_obj(tl.appointment_date);
if (date >= from_date && date <= to_date) {
var item = me.item_by_name[tl[me.tree_grid.item_key]] ||
me.item_by_name['Not Set'];
var d = tl.appointment_date.split(" ")[0];
if(status == "Select Status..." && type=="Select Type...")
{
item[me.column_map[d].field] += 1;
}else if (status !== "Select Status..." && type == "Select Type..."){
if(status === tl.status){item[me.column_map[d].field] += 1;}
}else if (status == "Select Status..." && type !== "Select Type..."){
if(type === tl.appointment_type){item[me.column_map[d].field] += 1;}
}else {
if(type === tl.appointment_type && status === tl.status){item[me.column_map[d].field] += 1;}
}
}
}
});
},
update_groups: function() {
var me = this;
$.each(this.data, function(i, item) {
var parent = me.parent_map[item.name];
while(parent) {
var parent_group = me.item_by_name[parent];
$.each(me.columns, function(c, col) {
if (col.formatter == me.currency_formatter) {
parent_group[col.field] =
flt(parent_group[col.field])
+ flt(item[col.field]);
}
});
parent = me.parent_map[parent];
}
});
},
set_totals: function(sort) {
var me = this;
$.each(this.data, function(i, d) {
d.total = 0.0;
$.each(me.columns, function(i, col) {
if(col.formatter==me.currency_formatter && !col.hidden && col.field!="total")
d.total += d[col.field];
});
});
if(sort)this.data = this.data.sort(function(a, b) { return b.total - a.total; });
if(!this.checked) {
this.data[0].checked = true;
}
}
});

View File

@ -1,24 +0,0 @@
{
"content": null,
"creation": "2016-08-18 12:29:52.497819",
"docstatus": 0,
"doctype": "Page",
"idx": 0,
"modified": "2018-08-06 11:40:53.082863",
"modified_by": "Administrator",
"module": "Healthcare",
"name": "appointment-analytic",
"owner": "Administrator",
"page_name": "Appointment Analytics",
"restrict_to_domain": "Healthcare",
"roles": [
{
"role": "Physician"
}
],
"script": null,
"standard": "Yes",
"style": null,
"system_page": 0,
"title": "Appointment Analytics"
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,128 @@
// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
/* eslint-disable */
frappe.query_reports['Patient Appointment Analytics'] = {
"filters": [
{
fieldname: 'tree_type',
label: __('Tree Type'),
fieldtype: 'Select',
options: ['Healthcare Practitioner', 'Medical Department'],
default: 'Healthcare Practitioner',
reqd: 1
},
{
fieldname: 'status',
label: __('Appointment Status'),
fieldtype: 'Select',
options:[
{label: __('Scheduled'), value: 'Scheduled'},
{label: __('Open'), value: 'Open'},
{label: __('Closed'), value: 'Closed'},
{label: __('Expired'), value: 'Expired'},
{label: __('Cancelled'), value: 'Cancelled'}
]
},
{
fieldname: 'appointment_type',
label: __('Appointment Type'),
fieldtype: 'Link',
options: 'Appointment Type'
},
{
fieldname: 'practitioner',
label: __('Healthcare Practitioner'),
fieldtype: 'Link',
options: 'Healthcare Practitioner'
},
{
fieldname: 'department',
label: __('Medical Department'),
fieldtype: 'Link',
options: 'Medical Department'
},
{
fieldname: 'from_date',
label: __('From Date'),
fieldtype: 'Date',
default: frappe.defaults.get_user_default('year_start_date'),
reqd: 1
},
{
fieldname: 'to_date',
label: __('To Date'),
fieldtype: 'Date',
default: frappe.defaults.get_user_default('year_end_date'),
reqd: 1
},
{
fieldname: 'range',
label: __('Range'),
fieldtype: 'Select',
options:[
{label: __('Weekly'), value: 'Weekly'},
{label: __('Monthly'), value: 'Monthly'},
{label: __('Quarterly'), value: 'Quarterly'},
{label: __('Yearly'), value: 'Yearly'}
],
default: 'Monthly',
reqd: 1
}
],
after_datatable_render: function(datatable_obj) {
$(datatable_obj.wrapper).find(".dt-row-0").find('input[type=checkbox]').click();
},
get_datatable_options(options) {
return Object.assign(options, {
checkboxColumn: true,
events: {
onCheckRow: function(data) {
row_name = data[2].content;
length = data.length;
row_values = data.slice(3,length-1).map(function (column) {
return column.content;
})
entry = {
'name': row_name,
'values': row_values
}
let raw_data = frappe.query_report.chart.data;
let new_datasets = raw_data.datasets;
let found = false;
for (let i=0; i < new_datasets.length;i++) {
if (new_datasets[i].name == row_name) {
found = true;
new_datasets.splice(i,1);
break;
}
}
if (!found) {
new_datasets.push(entry);
}
let new_data = {
labels: raw_data.labels,
datasets: new_datasets
}
setTimeout(() => {
frappe.query_report.chart.update(new_data)
}, 500)
setTimeout(() => {
frappe.query_report.chart.draw(true);
}, 1000)
frappe.query_report.raw_chart_data = new_data;
},
}
})
},
};

View File

@ -0,0 +1,36 @@
{
"add_total_row": 1,
"creation": "2020-03-02 15:13:16.273493",
"disable_prepared_report": 0,
"disabled": 0,
"docstatus": 0,
"doctype": "Report",
"idx": 0,
"is_standard": "Yes",
"modified": "2020-03-02 15:13:16.273493",
"modified_by": "Administrator",
"module": "Healthcare",
"name": "Patient Appointment Analytics",
"owner": "Administrator",
"prepared_report": 0,
"ref_doctype": "Patient Appointment",
"report_name": "Patient Appointment Analytics",
"report_type": "Script Report",
"roles": [
{
"role": "Healthcare Administrator"
},
{
"role": "LabTest Approver"
},
{
"role": "Physician"
},
{
"role": "Nursing User"
},
{
"role": "Laboratory User"
}
]
}

View File

@ -0,0 +1,194 @@
# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
import frappe
from frappe.utils import getdate, flt, add_to_date, add_days
from frappe import _ , scrub
from six import iteritems
from erpnext.accounts.utils import get_fiscal_year
def execute(filters=None):
return Analytics(filters).run()
class Analytics(object):
def __init__(self, filters=None):
"""Patient Appointment Analytics Report."""
self.filters = frappe._dict(filters or {})
self.months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
self.get_period_date_ranges()
def run(self):
self.get_columns()
self.get_data()
self.get_chart_data()
return self.columns, self.data, None, self.chart
def get_period_date_ranges(self):
from dateutil.relativedelta import relativedelta, MO
from_date, to_date = getdate(self.filters.from_date), getdate(self.filters.to_date)
increment = {
'Monthly': 1,
'Quarterly': 3,
'Half-Yearly': 6,
'Yearly': 12
}.get(self.filters.range, 1)
if self.filters.range in ['Monthly', 'Quarterly']:
from_date = from_date.replace(day=1)
elif self.filters.range == 'Yearly':
from_date = get_fiscal_year(from_date)[1]
else:
from_date = from_date + relativedelta(from_date, weekday=MO(-1))
self.periodic_daterange = []
for dummy in range(1, 53):
if self.filters.range == 'Weekly':
period_end_date = add_days(from_date, 6)
else:
period_end_date = add_to_date(from_date, months=increment, days=-1)
if period_end_date > to_date:
period_end_date = to_date
self.periodic_daterange.append(period_end_date)
from_date = add_days(period_end_date, 1)
if period_end_date == to_date:
break
def get_columns(self):
self.columns = []
if self.filters.tree_type == 'Healthcare Practitioner':
self.columns.append({
'label': _('Healthcare Practitioner'),
'options': 'Healthcare Practitioner',
'fieldname': 'practitioner',
'fieldtype': 'Link',
'width': 200
})
elif self.filters.tree_type == 'Medical Department':
self.columns.append({
'label': _('Medical Department'),
'fieldname': 'department',
'fieldtype': 'Link',
'options': 'Medical Department',
'width': 150
})
for end_date in self.periodic_daterange:
period = self.get_period(end_date)
self.columns.append({
'label': _(period),
'fieldname': scrub(period),
'fieldtype': 'Int',
'width': 120
})
self.columns.append({
'label': _('Total'),
'fieldname': 'total',
'fieldtype': 'Int',
'width': 120
})
def get_data(self):
if self.filters.tree_type == 'Healthcare Practitioner':
self.get_appointments_based_on_healthcare_practitioner()
self.get_rows()
elif self.filters.tree_type == 'Medical Department':
self.get_appointments_based_on_medical_department()
self.get_rows()
def get_period(self, appointment_date):
if self.filters.range == 'Weekly':
period = 'Week ' + str(appointment_date.isocalendar()[1])
elif self.filters.range == 'Monthly':
period = str(self.months[appointment_date.month - 1])
elif self.filters.range == 'Quarterly':
period = 'Quarter ' + str(((appointment_date.month - 1) // 3) + 1)
else:
year = get_fiscal_year(appointment_date, company=self.filters.company)
period = str(year[0])
if getdate(self.filters.from_date).year != getdate(self.filters.to_date).year:
period += ' ' + str(appointment_date.year)
return period
def get_appointments_based_on_healthcare_practitioner(self):
filters = self.get_common_filters()
self.entries = frappe.db.get_all('Patient Appointment',
fields=['appointment_date', 'name', 'patient', 'practitioner'],
filters=filters
)
def get_appointments_based_on_medical_department(self):
filters = self.get_common_filters()
if not filters.get('department'):
filters['department'] = ('!=', '')
self.entries = frappe.db.get_all('Patient Appointment',
fields=['appointment_date', 'name', 'patient', 'practitioner', 'department'],
filters=filters
)
def get_common_filters(self):
filters = {}
filters['appointment_date'] = ('between', [self.filters.from_date, self.filters.to_date])
for entry in ['appointment_type', 'practitioner', 'department', 'status']:
if self.filters.get(entry):
filters[entry] = self.filters.get(entry)
return filters
def get_rows(self):
self.data = []
self.get_periodic_data()
for entity, period_data in iteritems(self.appointment_periodic_data):
if self.filters.tree_type == 'Healthcare Practitioner':
row = {'practitioner': entity}
elif self.filters.tree_type == 'Medical Department':
row = {'department': entity}
total = 0
for end_date in self.periodic_daterange:
period = self.get_period(end_date)
amount = flt(period_data.get(period, 0.0))
row[scrub(period)] = amount
total += amount
row['total'] = total
self.data.append(row)
def get_periodic_data(self):
self.appointment_periodic_data = frappe._dict()
for d in self.entries:
period = self.get_period(d.get('appointment_date'))
if self.filters.tree_type == 'Healthcare Practitioner':
self.appointment_periodic_data.setdefault(d.practitioner, frappe._dict()).setdefault(period, 0.0)
self.appointment_periodic_data[d.practitioner][period] += 1
elif self.filters.tree_type == 'Medical Department':
self.appointment_periodic_data.setdefault(d.department, frappe._dict()).setdefault(period, 0.0)
self.appointment_periodic_data[d.department][period] += 1
def get_chart_data(self):
length = len(self.columns)
labels = [d.get("label") for d in self.columns[1:length - 1]]
self.chart = {
"data": {
'labels': labels,
'datasets': []
},
"type": "line"
}

View File

@ -4,417 +4,471 @@
from __future__ import unicode_literals
import frappe
import datetime
from frappe import _
import math
from frappe.utils import time_diff_in_hours, rounded, getdate, add_days
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, update_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
@frappe.whitelist()
def get_healthcare_services_to_invoice(patient):
patient = frappe.get_doc("Patient", patient)
patient = frappe.get_doc('Patient', patient)
if patient:
if patient.customer:
item_to_invoice = []
patient_appointments = frappe.get_list("Patient Appointment",{'patient': patient.name, 'invoiced': False},
order_by="appointment_date")
if patient_appointments:
fee_validity_details = []
valid_days = frappe.db.get_value("Healthcare Settings", None, "valid_days")
max_visit = frappe.db.get_value("Healthcare Settings", None, "max_visit")
for patient_appointment in patient_appointments:
patient_appointment_obj = frappe.get_doc("Patient Appointment", patient_appointment['name'])
validate_customer_created(patient)
items_to_invoice = []
patient_appointments = frappe.get_list(
'Patient Appointment',
fields='*',
filters={'patient': patient.name, 'invoiced': 0},
order_by='appointment_date'
)
if patient_appointments:
items_to_invoice = get_fee_validity(patient_appointments)
if patient_appointment_obj.procedure_template:
if frappe.db.get_value("Clinical Procedure Template", patient_appointment_obj.procedure_template, "is_billable") == 1:
item_to_invoice.append({'reference_type': 'Patient Appointment', 'reference_name': patient_appointment_obj.name, 'service': patient_appointment_obj.procedure_template})
else:
practitioner_exist_in_list = False
skip_invoice = False
if fee_validity_details:
for validity in fee_validity_details:
if validity['practitioner'] == patient_appointment_obj.practitioner:
practitioner_exist_in_list = True
if validity['valid_till'] >= patient_appointment_obj.appointment_date:
validity['visits'] = validity['visits']+1
if int(max_visit) > validity['visits']:
skip_invoice = True
if not skip_invoice:
validity['visits'] = 1
validity['valid_till'] = patient_appointment_obj.appointment_date + datetime.timedelta(days=int(valid_days))
if not practitioner_exist_in_list:
valid_till = patient_appointment_obj.appointment_date + datetime.timedelta(days=int(valid_days))
visits = 0
validity_exist = validity_exists(patient_appointment_obj.practitioner, patient_appointment_obj.patient)
if validity_exist:
fee_validity = frappe.get_doc("Fee Validity", validity_exist[0][0])
valid_till = fee_validity.valid_till
visits = fee_validity.visited
fee_validity_details.append({'practitioner': patient_appointment_obj.practitioner,
'valid_till': valid_till, 'visits': visits})
encounters = get_encounters_to_invoice(patient)
lab_tests = get_lab_tests_to_invoice(patient)
clinical_procedures = get_clinical_procedures_to_invoice(patient)
inpatient_services = get_inpatient_services_to_invoice(patient)
if not skip_invoice:
practitioner_charge = 0
income_account = None
service_item = None
if patient_appointment_obj.practitioner:
service_item, practitioner_charge = service_item_and_practitioner_charge(patient_appointment_obj)
income_account = get_income_account(patient_appointment_obj.practitioner, patient_appointment_obj.company)
item_to_invoice.append({'reference_type': 'Patient Appointment', 'reference_name': patient_appointment_obj.name,
'service': service_item, 'rate': practitioner_charge,
'income_account': income_account})
items_to_invoice += encounters + lab_tests + clinical_procedures + inpatient_services
return items_to_invoice
encounters = frappe.get_list("Patient Encounter", {'patient': patient.name, 'invoiced': False, 'docstatus': 1})
if encounters:
for encounter in encounters:
encounter_obj = frappe.get_doc("Patient Encounter", encounter['name'])
if not encounter_obj.appointment:
practitioner_charge = 0
income_account = None
service_item = None
if encounter_obj.practitioner:
service_item, practitioner_charge = service_item_and_practitioner_charge(encounter_obj)
income_account = get_income_account(encounter_obj.practitioner, encounter_obj.company)
def validate_customer_created(patient):
if not frappe.db.get_value('Patient', patient.name, 'customer'):
msg = _("Please set a Customer linked to the Patient")
msg += " <b><a href='#Form/Patient/{0}'>{0}</a></b>".format(patient.name)
frappe.throw(msg, title=_('Customer Not Found'))
item_to_invoice.append({'reference_type': 'Patient Encounter', 'reference_name': encounter_obj.name,
'service': service_item, 'rate': practitioner_charge,
'income_account': income_account})
def get_fee_validity(patient_appointments):
if not frappe.db.get_single_value('Healthcare Settings', 'enable_free_follow_ups'):
return
lab_tests = frappe.get_list("Lab Test", {'patient': patient.name, 'invoiced': False, 'docstatus': 1})
if lab_tests:
for lab_test in lab_tests:
lab_test_obj = frappe.get_doc("Lab Test", lab_test['name'])
if frappe.db.get_value("Lab Test Template", lab_test_obj.template, "is_billable") == 1:
item_to_invoice.append({'reference_type': 'Lab Test', 'reference_name': lab_test_obj.name,
'service': frappe.db.get_value("Lab Test Template", lab_test_obj.template, "item")})
lab_rxs = frappe.db.sql("""select lp.name from `tabPatient Encounter` et, `tabLab Prescription` lp
where et.patient=%s and lp.parent=et.name and lp.lab_test_created=0 and lp.invoiced=0""", (patient.name))
if lab_rxs:
for lab_rx in lab_rxs:
rx_obj = frappe.get_doc("Lab Prescription", lab_rx[0])
if rx_obj.lab_test_code and (frappe.db.get_value("Lab Test Template", rx_obj.lab_test_code, "is_billable") == 1):
item_to_invoice.append({'reference_type': 'Lab Prescription', 'reference_name': rx_obj.name,
'service': frappe.db.get_value("Lab Test Template", rx_obj.lab_test_code, "item")})
procedures = frappe.get_list("Clinical Procedure", {'patient': patient.name, 'invoiced': False})
if procedures:
for procedure in procedures:
procedure_obj = frappe.get_doc("Clinical Procedure", procedure['name'])
if not procedure_obj.appointment:
if procedure_obj.procedure_template and (frappe.db.get_value("Clinical Procedure Template", procedure_obj.procedure_template, "is_billable") == 1):
item_to_invoice.append({'reference_type': 'Clinical Procedure', 'reference_name': procedure_obj.name,
'service': frappe.db.get_value("Clinical Procedure Template", procedure_obj.procedure_template, "item")})
procedure_rxs = frappe.db.sql("""select pp.name from `tabPatient Encounter` et,
`tabProcedure Prescription` pp where et.patient=%s and pp.parent=et.name and
pp.procedure_created=0 and pp.invoiced=0 and pp.appointment_booked=0""", (patient.name))
if procedure_rxs:
for procedure_rx in procedure_rxs:
rx_obj = frappe.get_doc("Procedure Prescription", procedure_rx[0])
if frappe.db.get_value("Clinical Procedure Template", rx_obj.procedure, "is_billable") == 1:
item_to_invoice.append({'reference_type': 'Procedure Prescription', 'reference_name': rx_obj.name,
'service': frappe.db.get_value("Clinical Procedure Template", rx_obj.procedure, "item")})
procedures = frappe.get_list("Clinical Procedure",
{'patient': patient.name, 'invoice_separately_as_consumables': True, 'consumption_invoiced': False,
'consume_stock': True, 'status': 'Completed'})
if procedures:
service_item = get_healthcare_service_item('clinical_procedure_consumable_item')
if not service_item:
msg = _(("Please Configure {0} in ").format("Clinical Procedure Consumable Item") \
+ """<b><a href="#Form/Healthcare Settings">Healthcare Settings</a></b>""")
frappe.throw(msg)
for procedure in procedures:
procedure_obj = frappe.get_doc("Clinical Procedure", procedure['name'])
item_to_invoice.append({'reference_type': 'Clinical Procedure', 'reference_name': procedure_obj.name,
'service': service_item, 'rate': procedure_obj.consumable_total_amount, 'description': procedure_obj.consumption_details})
inpatient_services = frappe.db.sql("""select io.name, io.parent from `tabInpatient Record` ip,
`tabInpatient Occupancy` io where ip.patient=%s and io.parent=ip.name and
io.left=1 and io.invoiced=0""", (patient.name))
if inpatient_services:
for inpatient_service in inpatient_services:
inpatient_occupancy = frappe.get_doc("Inpatient Occupancy", inpatient_service[0])
service_unit_type = frappe.get_doc("Healthcare Service Unit Type", frappe.db.get_value("Healthcare Service Unit", inpatient_occupancy.service_unit, "service_unit_type"))
if service_unit_type and service_unit_type.is_billable == 1:
hours_occupied = time_diff_in_hours(inpatient_occupancy.check_out, inpatient_occupancy.check_in)
qty = 0.5
if hours_occupied > 0:
actual_qty = hours_occupied / service_unit_type.no_of_hours
floor = math.floor(actual_qty)
decimal_part = actual_qty - floor
if decimal_part > 0.5:
qty = rounded(floor + 1, 1)
elif decimal_part < 0.5 and decimal_part > 0:
qty = rounded(floor + 0.5, 1)
if qty <= 0:
qty = 0.5
item_to_invoice.append({'reference_type': 'Inpatient Occupancy', 'reference_name': inpatient_occupancy.name,
'service': service_unit_type.item, 'qty': qty})
return item_to_invoice
items_to_invoice = []
for appointment in patient_appointments:
if appointment.procedure_template:
if frappe.db.get_value('Clinical Procedure Template', appointment.procedure_template, 'is_billable'):
items_to_invoice.append({
'reference_type': 'Patient Appointment',
'reference_name': appointment.name,
'service': appointment.procedure_template
})
else:
frappe.throw(_("The Patient {0} do not have customer refrence to invoice").format(patient.name))
fee_validity = frappe.db.exists('Fee Validity Reference', {'appointment': appointment.name})
if not fee_validity:
practitioner_charge = 0
income_account = None
service_item = None
if appointment.practitioner:
service_item, practitioner_charge = get_service_item_and_practitioner_charge(appointment)
income_account = get_income_account(appointment.practitioner, appointment.company)
items_to_invoice.append({
'reference_type': 'Patient Appointment',
'reference_name': appointment.name,
'service': service_item,
'rate': practitioner_charge,
'income_account': income_account
})
def service_item_and_practitioner_charge(doc):
is_ip = doc_is_ip(doc)
if is_ip:
service_item = get_practitioner_service_item(doc.practitioner, "inpatient_visit_charge_item")
return items_to_invoice
def get_encounters_to_invoice(patient):
encounters_to_invoice = []
encounters = frappe.get_list(
'Patient Encounter',
fields=['*'],
filters={'patient': patient.name, 'invoiced': False, 'docstatus': 1}
)
if encounters:
for encounter in encounters:
if not encounter.appointment:
practitioner_charge = 0
income_account = None
service_item = None
if encounter.practitioner:
service_item, practitioner_charge = get_service_item_and_practitioner_charge(encounter)
income_account = get_income_account(encounter.practitioner, encounter.company)
encounters_to_invoice.append({
'reference_type': 'Patient Encounter',
'reference_name': encounter.name,
'service': service_item,
'rate': practitioner_charge,
'income_account': income_account
})
return encounters_to_invoice
def get_lab_tests_to_invoice(patient):
lab_tests_to_invoice = []
lab_tests = frappe.get_list(
'Lab Test',
fields=['name', 'template'],
filters={'patient': patient.name, 'invoiced': False, 'docstatus': 1}
)
for lab_test in lab_tests:
item, is_billable = frappe.get_cached_value('Lab Test Template', lab_test.lab_test_code, ['item', 'is_billable'])
if is_billable:
lab_tests_to_invoice.append({
'reference_type': 'Lab Test',
'reference_name': lab_test.name,
'service': item
})
lab_prescriptions = frappe.db.sql(
'''
SELECT
lp.name, lp.lab_test_code
FROM
`tabPatient Encounter` et, `tabLab Prescription` lp
WHERE
et.patient=%s
and lp.parent=et.name
and lp.lab_test_created=0
and lp.invoiced=0
''', (patient.name), as_dict=1)
for prescription in lab_prescriptions:
item, is_billable = frappe.get_cached_value('Lab Test Template', prescription.lab_test_code, ['item', 'is_billable'])
if prescription.lab_test_code and is_billable:
lab_tests_to_invoice.append({
'reference_type': 'Lab Prescription',
'reference_name': prescription.name,
'service': item
})
return lab_tests_to_invoice
def get_clinical_procedures_to_invoice(patient):
clinical_procedures_to_invoice = []
procedures = frappe.get_list(
'Clinical Procedure',
fields='*',
filters={'patient': patient.name, 'invoiced': False}
)
for procedure in procedures:
if not procedure.appointment:
item, is_billable = frappe.get_cached_value('Clinical Procedure Template', procedure.procedure_template, ['item', 'is_billable'])
if procedure.procedure_template and is_billable:
clinical_procedures_to_invoice.append({
'reference_type': 'Clinical Procedure',
'reference_name': procedure.name,
'service': item
})
# consumables
if procedure.invoice_separately_as_consumables and procedure.consume_stock \
and procedure.status == 'Completed' and not procedure.consumption_invoiced:
service_item = get_healthcare_service_item('clinical_procedure_consumable_item')
if not service_item:
msg = _('Please Configure Clinical Procedure Consumable Item in ')
msg += '''<b><a href='#Form/Healthcare Settings'>Healthcare Settings</a></b>'''
frappe.throw(msg, title=_('Missing Configuration'))
clinical_procedures_to_invoice.append({
'reference_type': 'Clinical Procedure',
'reference_name': procedure.name,
'service': service_item,
'rate': procedure.consumable_total_amount,
'description': procedure.consumption_details
})
procedure_prescriptions = frappe.db.sql(
'''
SELECT
pp.name, pp.procedure
FROM
`tabPatient Encounter` et, `tabProcedure Prescription` pp
WHERE
et.patient=%s
and pp.parent=et.name
and pp.procedure_created=0
and pp.invoiced=0
and pp.appointment_booked=0
''', (patient.name), as_dict=1)
for prescription in procedure_prescriptions:
item, is_billable = frappe.get_cached_value('Clinical Procedure Template', prescription.procedure, ['item', 'is_billable'])
if is_billable:
clinical_procedures_to_invoice.append({
'reference_type': 'Procedure Prescription',
'reference_name': prescription.name,
'service': item
})
return clinical_procedures_to_invoice
def get_inpatient_services_to_invoice(patient):
services_to_invoice = []
inpatient_services = frappe.db.sql(
'''
SELECT
io.*
FROM
`tabInpatient Record` ip, `tabInpatient Occupancy` io
WHERE
ip.patient=%s
and io.parent=ip.name
and io.left=1
and io.invoiced=0
''', (patient.name), as_dict=1)
for inpatient_occupancy in inpatient_services:
service_unit_type = frappe.db.get_value('Healthcare Service Unit', inpatient_occupancy.service_unit, 'service_unit_type')
service_unit_type = frappe.get_cached_doc('Healthcare Service Unit Type', service_unit_type)
if service_unit_type and service_unit_type.is_billable:
hours_occupied = time_diff_in_hours(inpatient_occupancy.check_out, inpatient_occupancy.check_in)
qty = 0.5
if hours_occupied > 0:
actual_qty = hours_occupied / service_unit_type.no_of_hours
floor = math.floor(actual_qty)
decimal_part = actual_qty - floor
if decimal_part > 0.5:
qty = rounded(floor + 1, 1)
elif decimal_part < 0.5 and decimal_part > 0:
qty = rounded(floor + 0.5, 1)
if qty <= 0:
qty = 0.5
services_to_invoice.append({
'reference_type': 'Inpatient Occupancy',
'reference_name': inpatient_occupancy.name,
'service': service_unit_type.item, 'qty': qty
})
return services_to_invoice
def get_service_item_and_practitioner_charge(doc):
is_inpatient = doc.inpatient_record
if is_inpatient:
service_item = get_practitioner_service_item(doc.practitioner, 'inpatient_visit_charge_item')
if not service_item:
service_item = get_healthcare_service_item("inpatient_visit_charge_item")
service_item = get_healthcare_service_item('inpatient_visit_charge_item')
else:
service_item = get_practitioner_service_item(doc.practitioner, "op_consulting_charge_item")
service_item = get_practitioner_service_item(doc.practitioner, 'op_consulting_charge_item')
if not service_item:
service_item = get_healthcare_service_item("op_consulting_charge_item")
service_item = get_healthcare_service_item('op_consulting_charge_item')
if not service_item:
throw_config_service_item(is_ip)
throw_config_service_item(is_inpatient)
practitioner_charge = get_practitioner_charge(doc.practitioner, is_ip)
practitioner_charge = get_practitioner_charge(doc.practitioner, is_inpatient)
if not practitioner_charge:
throw_config_practitioner_charge(is_ip, doc.practitioner)
throw_config_practitioner_charge(is_inpatient, doc.practitioner)
return service_item, practitioner_charge
def throw_config_service_item(is_ip):
service_item_lable = "Out Patient Consulting Charge Item"
if is_ip:
service_item_lable = "Inpatient Visit Charge Item"
msg = _(("Please Configure {0} in ").format(service_item_lable) \
+ """<b><a href="#Form/Healthcare Settings">Healthcare Settings</a></b>""")
frappe.throw(msg)
def throw_config_service_item(is_inpatient):
service_item_label = _('Out Patient Consulting Charge Item')
if is_inpatient:
service_item_label = _('Inpatient Visit Charge Item')
def throw_config_practitioner_charge(is_ip, practitioner):
charge_name = "OP Consulting Charge"
if is_ip:
charge_name = "Inpatient Visit Charge"
msg = _(('Please Configure {0} in ').format(service_item_label) \
+ '''<b><a href='#Form/Healthcare Settings'>Healthcare Settings</a></b>''')
frappe.throw(msg, title=_('Missing Configuration'))
def throw_config_practitioner_charge(is_inpatient, practitioner):
charge_name = _('OP Consulting Charge')
if is_inpatient:
charge_name = _('Inpatient Visit Charge')
msg = _(('Please Configure {0} for Healthcare Practitioner').format(charge_name) \
+ ''' <b><a href='#Form/Healthcare Practitioner/{0}'>{0}</a></b>'''.format(practitioner))
frappe.throw(msg, title=_('Missing Configuration'))
msg = _(("Please Configure {0} for Healthcare Practitioner").format(charge_name) \
+ """ <b><a href="#Form/Healthcare Practitioner/{0}">{0}</a></b>""".format(practitioner))
frappe.throw(msg)
def get_practitioner_service_item(practitioner, service_item_field):
return frappe.db.get_value("Healthcare Practitioner", practitioner, service_item_field)
return frappe.db.get_value('Healthcare Practitioner', practitioner, service_item_field)
def get_healthcare_service_item(service_item_field):
return frappe.db.get_value("Healthcare Settings", None, service_item_field)
return frappe.db.get_single_value('Healthcare Settings', service_item_field)
def doc_is_ip(doc):
is_ip = False
if doc.inpatient_record:
is_ip = True
return is_ip
def get_practitioner_charge(practitioner, is_ip):
if is_ip:
practitioner_charge = frappe.db.get_value("Healthcare Practitioner", practitioner, "inpatient_visit_charge")
def get_practitioner_charge(practitioner, is_inpatient):
if is_inpatient:
practitioner_charge = frappe.db.get_value('Healthcare Practitioner', practitioner, 'inpatient_visit_charge')
else:
practitioner_charge = frappe.db.get_value("Healthcare Practitioner", practitioner, "op_consulting_charge")
practitioner_charge = frappe.db.get_value('Healthcare Practitioner', practitioner, 'op_consulting_charge')
if practitioner_charge:
return practitioner_charge
return False
def manage_invoice_submit_cancel(doc, method):
if doc.items:
for item in doc.items:
if item.get("reference_dt") and item.get("reference_dn"):
if frappe.get_meta(item.reference_dt).has_field("invoiced"):
if item.get('reference_dt') and item.get('reference_dn'):
if frappe.get_meta(item.reference_dt).has_field('invoiced'):
set_invoiced(item, method, doc.name)
if method=="on_submit" and frappe.db.get_value("Healthcare Settings", None, "create_test_on_si_submit") == '1':
create_multiple("Sales Invoice", doc.name)
if method=='on_submit' and frappe.db.get_single_value('Healthcare Settings', 'create_lab_test_on_si_submit'):
create_multiple('Sales Invoice', doc.name)
def set_invoiced(item, method, ref_invoice=None):
invoiced = False
if(method=="on_submit"):
if method=='on_submit':
validate_invoiced_on_submit(item)
invoiced = True
if item.reference_dt == 'Clinical Procedure':
if get_healthcare_service_item('clinical_procedure_consumable_item') == item.item_code:
frappe.db.set_value(item.reference_dt, item.reference_dn, "consumption_invoiced", invoiced)
frappe.db.set_value(item.reference_dt, item.reference_dn, 'consumption_invoiced', invoiced)
else:
frappe.db.set_value(item.reference_dt, item.reference_dn, "invoiced", invoiced)
frappe.db.set_value(item.reference_dt, item.reference_dn, 'invoiced', invoiced)
else:
frappe.db.set_value(item.reference_dt, item.reference_dn, "invoiced", invoiced)
frappe.db.set_value(item.reference_dt, item.reference_dn, 'invoiced', invoiced)
if item.reference_dt == 'Patient Appointment':
if frappe.db.get_value('Patient Appointment', item.reference_dn, 'procedure_template'):
dt_from_appointment = "Clinical Procedure"
dt_from_appointment = 'Clinical Procedure'
else:
manage_fee_validity(item.reference_dn, method, ref_invoice)
dt_from_appointment = "Patient Encounter"
manage_doc_for_appoitnment(dt_from_appointment, item.reference_dn, invoiced)
dt_from_appointment = 'Patient Encounter'
manage_doc_for_appointment(dt_from_appointment, item.reference_dn, invoiced)
elif item.reference_dt == 'Lab Prescription':
manage_prescriptions(invoiced, item.reference_dt, item.reference_dn, "Lab Test", "lab_test_created")
manage_prescriptions(invoiced, item.reference_dt, item.reference_dn, 'Lab Test', 'lab_test_created')
elif item.reference_dt == 'Procedure Prescription':
manage_prescriptions(invoiced, item.reference_dt, item.reference_dn, "Clinical Procedure", "procedure_created")
manage_prescriptions(invoiced, item.reference_dt, item.reference_dn, 'Clinical Procedure', 'procedure_created')
def validate_invoiced_on_submit(item):
if item.reference_dt == 'Clinical Procedure' and get_healthcare_service_item('clinical_procedure_consumable_item') == item.item_code:
is_invoiced = frappe.db.get_value(item.reference_dt, item.reference_dn, "consumption_invoiced")
is_invoiced = frappe.db.get_value(item.reference_dt, item.reference_dn, 'consumption_invoiced')
else:
is_invoiced = frappe.db.get_value(item.reference_dt, item.reference_dn, "invoiced")
if is_invoiced == 1:
frappe.throw(_("The item referenced by {0} - {1} is already invoiced"\
is_invoiced = frappe.db.get_value(item.reference_dt, item.reference_dn, 'invoiced')
if is_invoiced:
frappe.throw(_('The item referenced by {0} - {1} is already invoiced'\
).format(item.reference_dt, item.reference_dn))
def manage_prescriptions(invoiced, ref_dt, ref_dn, dt, created_check_field):
created = frappe.db.get_value(ref_dt, ref_dn, created_check_field)
if created == 1:
if created:
# Fetch the doc created for the prescription
doc_created = frappe.db.get_value(dt, {'prescription': ref_dn})
frappe.db.set_value(dt, doc_created, 'invoiced', invoiced)
def validity_exists(practitioner, patient):
return frappe.db.exists({
"doctype": "Fee Validity",
"practitioner": practitioner,
"patient": patient})
def manage_fee_validity(appointment_name, method, ref_invoice=None):
appointment_doc = frappe.get_doc("Patient Appointment", appointment_name)
validity_exist = validity_exists(appointment_doc.practitioner, appointment_doc.patient)
do_not_update = False
visited = 0
if validity_exist:
fee_validity = frappe.get_doc("Fee Validity", validity_exist[0][0])
# Check if the validity is valid
if (fee_validity.valid_till >= appointment_doc.appointment_date):
if (method == "on_cancel" and appointment_doc.status != "Closed"):
if ref_invoice == fee_validity.ref_invoice:
visited = fee_validity.visited - 1
if visited < 0:
visited = 0
frappe.db.set_value("Fee Validity", fee_validity.name, "visited", visited)
do_not_update = True
elif (method == "on_submit" and fee_validity.visited < fee_validity.max_visit):
visited = fee_validity.visited + 1
frappe.db.set_value("Fee Validity", fee_validity.name, "visited", visited)
do_not_update = True
else:
do_not_update = False
def check_fee_validity(appointment):
if not frappe.db.get_single_value('Healthcare Settings', 'enable_free_follow_ups'):
return
if not do_not_update:
fee_validity = update_fee_validity(fee_validity, appointment_doc.appointment_date, ref_invoice)
visited = fee_validity.visited
validity = frappe.db.exists('Fee Validity', {
'practitioner': appointment.practitioner,
'patient': appointment.patient,
'valid_till': ('>=', appointment.appointment_date)
})
if not validity:
return
validity = frappe.get_doc('Fee Validity', validity)
return validity
def manage_fee_validity(appointment):
fee_validity = check_fee_validity(appointment)
if fee_validity:
if appointment.status == 'Cancelled' and fee_validity.visited > 0:
fee_validity.visited -= 1
frappe.db.delete('Fee Validity Reference', {'appointment': appointment.name})
elif fee_validity.status == 'Completed':
return
else:
fee_validity.visited += 1
fee_validity.append('ref_appointments', {
'appointment': appointment.name
})
fee_validity.save(ignore_permissions=True)
else:
fee_validity = create_fee_validity(appointment_doc.practitioner, appointment_doc.patient, appointment_doc.appointment_date, ref_invoice)
visited = fee_validity.visited
fee_validity = create_fee_validity(appointment)
return fee_validity
# Mark All Patient Appointment invoiced = True in the validity range do not cross the max visit
if (method == "on_cancel"):
invoiced = True
else:
invoiced = False
patient_appointments = appointments_valid_in_fee_validity(appointment_doc, invoiced)
if patient_appointments and fee_validity:
visit = visited
for appointment in patient_appointments:
if (method == "on_cancel" and appointment.status != "Closed"):
if ref_invoice == fee_validity.ref_invoice:
visited = visited - 1
if visited < 0:
visited = 0
frappe.db.set_value("Fee Validity", fee_validity.name, "visited", visited)
frappe.db.set_value("Patient Appointment", appointment.name, "invoiced", False)
manage_doc_for_appoitnment("Patient Encounter", appointment.name, False)
elif method == "on_submit" and int(fee_validity.max_visit) > visit:
if ref_invoice == fee_validity.ref_invoice:
visited = visited + 1
frappe.db.set_value("Fee Validity", fee_validity.name, "visited", visited)
frappe.db.set_value("Patient Appointment", appointment.name, "invoiced", True)
manage_doc_for_appoitnment("Patient Encounter", appointment.name, True)
if ref_invoice == fee_validity.ref_invoice:
visit = visit + 1
if method == "on_cancel":
ref_invoice_in_fee_validity = frappe.db.get_value("Fee Validity", fee_validity.name, 'ref_invoice')
if ref_invoice_in_fee_validity == ref_invoice:
frappe.delete_doc("Fee Validity", fee_validity.name)
def appointments_valid_in_fee_validity(appointment, invoiced):
valid_days = frappe.db.get_value("Healthcare Settings", None, "valid_days")
max_visit = frappe.db.get_value("Healthcare Settings", None, "max_visit")
if int(max_visit) < 1:
max_visit = 1
valid_days_date = add_days(getdate(appointment.appointment_date), int(valid_days))
return frappe.get_list("Patient Appointment",{'patient': appointment.patient, 'invoiced': invoiced,
'appointment_date':("<=", valid_days_date), 'appointment_date':(">=", getdate(appointment.appointment_date)),
'practitioner': appointment.practitioner}, order_by="appointment_date", limit=int(max_visit)-1)
def manage_doc_for_appoitnment(dt_from_appointment, appointment, invoiced):
dn_from_appointment = frappe.db.exists(
def manage_doc_for_appointment(dt_from_appointment, appointment, invoiced):
dn_from_appointment = frappe.db.get_value(
dt_from_appointment,
{
"appointment": appointment
}
filters={'appointment': appointment}
)
if dn_from_appointment:
frappe.db.set_value(dt_from_appointment, dn_from_appointment, "invoiced", invoiced)
frappe.db.set_value(dt_from_appointment, dn_from_appointment, 'invoiced', invoiced)
@frappe.whitelist()
def get_drugs_to_invoice(encounter):
encounter = frappe.get_doc("Patient Encounter", encounter)
encounter = frappe.get_doc('Patient Encounter', encounter)
if encounter:
patient = frappe.get_doc("Patient", encounter.patient)
if patient and patient.customer:
item_to_invoice = []
patient = frappe.get_doc('Patient', encounter.patient)
if patient:
if patient.customer:
items_to_invoice = []
for drug_line in encounter.drug_prescription:
if drug_line.drug_code:
qty = 1
if frappe.db.get_value("Item", drug_line.drug_code, "stock_uom") == "Nos":
if frappe.db.get_value('Item', drug_line.drug_code, 'stock_uom') == 'Nos':
qty = drug_line.get_quantity()
description = False
if drug_line.dosage:
description = drug_line.dosage
if description and drug_line.period:
description += " for "+drug_line.period
if not description:
description = ""
item_to_invoice.append({'drug_code': drug_line.drug_code, 'quantity': qty,
'description': description})
return item_to_invoice
description = ''
if drug_line.dosage and drug_line.period:
description = _('{0} for {1}').format(drug_line.dosage, drug_line.period)
items_to_invoice.append({
'drug_code': drug_line.drug_code,
'quantity': qty,
'description': description
})
return items_to_invoice
else:
validate_customer_created(patient)
@frappe.whitelist()
def get_children(doctype, parent, company, is_root=False):
parent_fieldname = 'parent_' + doctype.lower().replace(' ', '_')
parent_fieldname = "parent_" + doctype.lower().replace(" ", "_")
fields = [
'name as value',
'is_group as expandable',
'lft',
'rgt'
"name as value",
"is_group as expandable",
"lft",
"rgt"
]
# fields = [ 'name', 'is_group', 'lft', 'rgt' ]
filters = [['ifnull(`{0}`,"")'.format(parent_fieldname), '=', '' if is_root else parent]]
# fields = [ "name", "is_group", "lft", "rgt" ]
filters = [["ifnull(`{0}`,'')".format(parent_fieldname), "=", "" if is_root else parent]]
if is_root:
fields += ['service_unit_type'] if doctype == 'Healthcare Service Unit' else []
filters.append(['company', '=', company])
fields += ["service_unit_type"] if doctype == "Healthcare Service Unit" else []
filters.append(["company", "=", company])
else:
fields += ['service_unit_type', 'allow_appointments', 'inpatient_occupancy', 'occupancy_status'] if doctype == 'Healthcare Service Unit' else []
fields += [parent_fieldname + ' as parent']
fields += ["service_unit_type", "allow_appointments", "inpatient_occupancy", "occupancy_status"] if doctype == "Healthcare Service Unit" else []
fields += [parent_fieldname + " as parent"]
hc_service_units = frappe.get_list(doctype, fields=fields, filters=filters)
if doctype == 'Healthcare Service Unit':
if doctype == "Healthcare Service Unit":
for each in hc_service_units:
occupancy_msg = ""
if each['expandable'] == 1:
if each["expandable"] == 1:
occupied = False
vacant = False
child_list = frappe.db.sql("""
select name, occupancy_status from `tabHealthcare Service Unit`
where inpatient_occupancy = 1 and
lft > %s and rgt < %s""",
(each['lft'], each['rgt']))
child_list = frappe.db.sql(
'''
SELECT
name, occupancy_status
FROM
`tabHealthcare Service Unit`
WHERE
inpatient_occupancy = 1
and lft > %s and rgt < %s
''', (each['lft'], each['rgt']))
for child in child_list:
if not occupied:
occupied = 0
@ -425,39 +479,44 @@ def get_children(doctype, parent, company, is_root=False):
if child[1] == "Vacant":
vacant += 1
if vacant and occupied:
occupancy_total = vacant+occupied
occupancy_total = vacant + occupied
occupancy_msg = str(occupied) + " Occupied out of " + str(occupancy_total)
each["occupied_out_of_vacant"] = occupancy_msg
return hc_service_units
@frappe.whitelist()
def get_patient_vitals(patient, from_date=None, to_date=None):
if not patient: return
vitals = frappe.db.sql("""select * from `tabVital Signs` where \
docstatus=1 and patient=%s order by signs_date, signs_time""", \
(patient), as_dict=1)
if vitals and vitals[0]:
vitals = frappe.db.get_all('Vital Signs', {
'docstatus': 1,
'patient': patient
}, order_by='signs_date, signs_time')
if len(vitals):
return vitals
else:
return False
return False
@frappe.whitelist()
def render_docs_as_html(docs):
# docs key value pair {doctype: docname}
docs_html = "<div class='col-md-12 col-sm-12 text-muted'>"
for doc in docs:
docs_html += render_doc_as_html(doc['doctype'], doc['docname'])['html'] + "<br/>"
docs_html += render_doc_as_html(doc['doctype'], doc['docname'])['html'] + '<br/>'
return {'html': docs_html}
@frappe.whitelist()
def render_doc_as_html(doctype, docname, exclude_fields = []):
#render document as html, three column layout will break
doc = frappe.get_doc(doctype, docname)
meta = frappe.get_meta(doctype)
doc_html = "<div class='col-md-12 col-sm-12'>"
section_html = ""
section_label = ""
html = ""
section_html = ''
section_label = ''
html = ''
sec_on = False
col_on = 0
has_data = False
@ -476,8 +535,8 @@ def render_doc_as_html(doctype, docname, exclude_fields = []):
sec_on = True
has_data= False
col_on = 0
section_html = ""
html = ""
section_html = ''
html = ''
if df.label:
section_label = df.label
continue
@ -493,19 +552,19 @@ def render_doc_as_html(doctype, docname, exclude_fields = []):
d-6 col-sm-6'>" + html + "</div><div class='col-md-6 col-sm-6'>"
elif sec_on and not col_on:
section_html += "<div class='col-md-6 col-sm-6'>"
html = ""
html = ''
col_on += 1
if df.label:
html += '<br>' + df.label
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":
if df.fieldtype == 'Table':
items = doc.get(df.fieldname)
if not items: continue
child_meta = frappe.get_meta(df.options)
if not has_data : has_data = True
table_head = ""
table_row = ""
table_head = ''
table_row = ''
create_head = True
for item in items:
table_row += '<tr>'
@ -521,24 +580,24 @@ def render_doc_as_html(doctype, docname, exclude_fields = []):
create_head = False
table_row += '</tr>'
if sec_on:
section_html += '<table class="table table-condensed \
bordered">' + table_head + table_row + '</table>'
section_html += "<table class='table table-condensed \
bordered'>" + table_head + table_row + '</table>'
else:
html += '<table class="table table-condensed table-bordered">' \
+ table_head + table_row + '</table>'
html += "<table class='table table-condensed table-bordered'>" \
+ table_head + table_row + "</table>"
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 += "<br>{0} : {1}".format(df.label or df.fieldname, \
html += '<br>{0} : {1}'.format(df.label or df.fieldname, \
doc.get(df.fieldname))
if not has_data : has_data = True
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:
doc_html += "<div class='col-md-12 col-sm-12'\
><div class='col-md-12 col-sm-12'>" \
+ section_html + html +"</div></div>"
+ section_html + html +'</div></div>'
if doc_html:
doc_html = "<div class='small'><div class='col-md-12 text-right'><a class='btn btn-default btn-xs' href='#Form/%s/%s'></a></div>" %(doctype, docname) + doc_html + "</div>"
doc_html = "<div class='small'><div class='col-md-12 text-right'><a class='btn btn-default btn-xs' href='#Form/%s/%s'></a></div>" %(doctype, docname) + doc_html + '</div>'
return {'html': doc_html}

View File

@ -0,0 +1,3 @@
frappe.ready(function() {
// bind events here
});

View File

@ -0,0 +1,397 @@
{
"accept_payment": 0,
"allow_comments": 0,
"allow_delete": 0,
"allow_edit": 1,
"allow_incomplete": 0,
"allow_multiple": 0,
"allow_print": 0,
"amount": 0.0,
"amount_based_on_field": 0,
"button_label": "Register",
"creation": "2020-03-03 01:01:16.250607",
"currency": "INR",
"doc_type": "Patient",
"docstatus": 0,
"doctype": "Web Form",
"idx": 0,
"introduction_text": "",
"is_standard": 1,
"login_required": 0,
"max_attachment_size": 0,
"modified": "2020-03-26 17:25:15.361918",
"modified_by": "Administrator",
"module": "Healthcare",
"name": "patient-registration",
"owner": "Administrator",
"payment_button_label": "Buy Now",
"published": 1,
"route": "patient-registration",
"route_to_success_link": 0,
"show_attachments": 0,
"show_in_grid": 0,
"show_sidebar": 1,
"sidebar_items": [],
"success_message": "Registration Successfully. Thank You!",
"success_url": "/patient-registration",
"title": "Patient Registration",
"web_form_fields": [
{
"allow_read_on_all_link_options": 0,
"fieldname": "basic_info",
"fieldtype": "Section Break",
"hidden": 0,
"label": "Patient Demographics",
"max_length": 0,
"max_value": 0,
"options": "fa fa-user",
"read_only": 0,
"reqd": 0,
"show_in_filter": 0
},
{
"allow_read_on_all_link_options": 0,
"fieldname": "first_name",
"fieldtype": "Data",
"hidden": 0,
"label": "First Name",
"max_length": 0,
"max_value": 0,
"read_only": 0,
"reqd": 1,
"show_in_filter": 0
},
{
"allow_read_on_all_link_options": 0,
"fieldname": "middle_name",
"fieldtype": "Data",
"hidden": 0,
"label": "Middle Name (optional)",
"max_length": 0,
"max_value": 0,
"read_only": 0,
"reqd": 0,
"show_in_filter": 0
},
{
"allow_read_on_all_link_options": 0,
"fieldname": "last_name",
"fieldtype": "Data",
"hidden": 0,
"label": "Last Name",
"max_length": 0,
"max_value": 0,
"read_only": 0,
"reqd": 1,
"show_in_filter": 0
},
{
"allow_read_on_all_link_options": 0,
"fieldname": "sex",
"fieldtype": "Link",
"hidden": 0,
"label": "Gender",
"max_length": 0,
"max_value": 0,
"options": "Gender",
"read_only": 0,
"reqd": 1,
"show_in_filter": 0
},
{
"allow_read_on_all_link_options": 0,
"fieldname": "blood_group",
"fieldtype": "Select",
"hidden": 0,
"label": "Blood Group",
"max_length": 0,
"max_value": 0,
"options": "\nA Positive\nA Negative\nAB Positive\nAB Negative\nB Positive\nB Negative\nO Positive\nO Negative",
"read_only": 0,
"reqd": 0,
"show_in_filter": 0
},
{
"allow_read_on_all_link_options": 0,
"fieldname": "",
"fieldtype": "Column Break",
"hidden": 0,
"max_length": 0,
"max_value": 0,
"read_only": 0,
"reqd": 0,
"show_in_filter": 0
},
{
"allow_read_on_all_link_options": 0,
"fieldname": "dob",
"fieldtype": "Date",
"hidden": 0,
"label": "Date of birth",
"max_length": 0,
"max_value": 0,
"read_only": 0,
"reqd": 0,
"show_in_filter": 0
},
{
"allow_read_on_all_link_options": 0,
"fieldname": "mobile",
"fieldtype": "Data",
"hidden": 0,
"label": "Mobile",
"max_length": 0,
"max_value": 0,
"read_only": 0,
"reqd": 0,
"show_in_filter": 0
},
{
"allow_read_on_all_link_options": 0,
"fieldname": "email",
"fieldtype": "Data",
"hidden": 0,
"label": "Email",
"max_length": 0,
"max_value": 0,
"options": "Email",
"read_only": 0,
"reqd": 0,
"show_in_filter": 0
},
{
"allow_read_on_all_link_options": 0,
"fieldname": "phone",
"fieldtype": "Data",
"hidden": 0,
"label": "Phone",
"max_length": 0,
"max_value": 0,
"read_only": 0,
"reqd": 0,
"show_in_filter": 0
},
{
"allow_read_on_all_link_options": 0,
"fieldname": "",
"fieldtype": "Section Break",
"hidden": 0,
"label": "Personal Details",
"max_length": 0,
"max_value": 0,
"read_only": 0,
"reqd": 0,
"show_in_filter": 0
},
{
"allow_read_on_all_link_options": 0,
"fieldname": "occupation",
"fieldtype": "Data",
"hidden": 0,
"label": "Occupation",
"max_length": 0,
"max_value": 0,
"read_only": 0,
"reqd": 0,
"show_in_filter": 0
},
{
"allow_read_on_all_link_options": 0,
"fieldname": "",
"fieldtype": "Column Break",
"hidden": 0,
"max_length": 0,
"max_value": 0,
"read_only": 0,
"reqd": 0,
"show_in_filter": 0
},
{
"allow_read_on_all_link_options": 0,
"fieldname": "marital_status",
"fieldtype": "Select",
"hidden": 0,
"label": "Marital Status",
"max_length": 0,
"max_value": 0,
"options": "\nSingle\nMarried\nDivorced\nWidow",
"read_only": 0,
"reqd": 0,
"show_in_filter": 0
},
{
"allow_read_on_all_link_options": 0,
"fieldname": "allergy_medical_and_surgical_history",
"fieldtype": "Section Break",
"hidden": 0,
"label": "Allergies, Medical and Surgical History",
"max_length": 0,
"max_value": 0,
"read_only": 0,
"reqd": 0,
"show_in_filter": 0
},
{
"allow_read_on_all_link_options": 0,
"fieldname": "allergies",
"fieldtype": "Small Text",
"hidden": 0,
"label": "Allergies",
"max_length": 0,
"max_value": 0,
"read_only": 0,
"reqd": 0,
"show_in_filter": 0
},
{
"allow_read_on_all_link_options": 0,
"fieldname": "medication",
"fieldtype": "Small Text",
"hidden": 0,
"label": "Medication",
"max_length": 0,
"max_value": 0,
"read_only": 0,
"reqd": 0,
"show_in_filter": 0
},
{
"allow_read_on_all_link_options": 0,
"fieldname": "column_break_20",
"fieldtype": "Column Break",
"hidden": 0,
"max_length": 0,
"max_value": 0,
"read_only": 0,
"reqd": 0,
"show_in_filter": 0
},
{
"allow_read_on_all_link_options": 0,
"fieldname": "medical_history",
"fieldtype": "Small Text",
"hidden": 0,
"label": "Medical History",
"max_length": 0,
"max_value": 0,
"read_only": 0,
"reqd": 0,
"show_in_filter": 0
},
{
"allow_read_on_all_link_options": 0,
"fieldname": "surgical_history",
"fieldtype": "Small Text",
"hidden": 0,
"label": "Surgical History",
"max_length": 0,
"max_value": 0,
"read_only": 0,
"reqd": 0,
"show_in_filter": 0
},
{
"allow_read_on_all_link_options": 0,
"fieldname": "risk_factors",
"fieldtype": "Section Break",
"hidden": 0,
"label": "Risk Factors",
"max_length": 0,
"max_value": 0,
"read_only": 0,
"reqd": 0,
"show_in_filter": 0
},
{
"allow_read_on_all_link_options": 0,
"default": "0",
"fieldname": "tobacco_past_use",
"fieldtype": "Check",
"hidden": 0,
"label": "Check if you have a history of Tobacco Consumption",
"max_length": 0,
"max_value": 0,
"options": "",
"read_only": 0,
"reqd": 0,
"show_in_filter": 0
},
{
"allow_read_on_all_link_options": 0,
"default": "0",
"fieldname": "tobacco_current_use",
"fieldtype": "Check",
"hidden": 0,
"label": "Check if you consume Tobacco",
"max_length": 0,
"max_value": 0,
"options": "",
"read_only": 0,
"reqd": 0,
"show_in_filter": 0
},
{
"allow_read_on_all_link_options": 0,
"default": "0",
"fieldname": "alcohol_past_use",
"fieldtype": "Check",
"hidden": 0,
"label": "Check if you have a history of Alcohol Consumption",
"max_length": 0,
"max_value": 0,
"options": "",
"read_only": 0,
"reqd": 0,
"show_in_filter": 0
},
{
"allow_read_on_all_link_options": 0,
"default": "0",
"fieldname": "alcohol_current_use",
"fieldtype": "Check",
"hidden": 0,
"label": "Check if you consume Alcohol",
"max_length": 0,
"max_value": 0,
"options": "",
"read_only": 0,
"reqd": 0,
"show_in_filter": 0
},
{
"allow_read_on_all_link_options": 0,
"fieldname": "column_break_32",
"fieldtype": "Column Break",
"hidden": 0,
"max_length": 0,
"max_value": 0,
"read_only": 0,
"reqd": 0,
"show_in_filter": 0
},
{
"allow_read_on_all_link_options": 0,
"fieldname": "surrounding_factors",
"fieldtype": "Small Text",
"hidden": 0,
"label": "Occupational Hazards and Environmental Factors",
"max_length": 0,
"max_value": 0,
"read_only": 0,
"reqd": 0,
"show_in_filter": 0
},
{
"allow_read_on_all_link_options": 0,
"fieldname": "other_risk_factors",
"fieldtype": "Small Text",
"hidden": 0,
"label": "Other Risk Factors",
"max_length": 0,
"max_value": 0,
"read_only": 0,
"reqd": 0,
"show_in_filter": 0
}
]
}

View File

@ -0,0 +1,5 @@
from __future__ import unicode_literals
def get_context(context):
# do your magic here
pass

View File

@ -269,7 +269,8 @@ auto_cancel_exempted_doctypes= [
scheduler_events = {
"all": [
"erpnext.projects.doctype.project.project.project_status_update_reminder"
"erpnext.projects.doctype.project.project.project_status_update_reminder",
"erpnext.healthcare_healthcare.doctype.patient_appointment.patient_appointment.send_appointment_reminder"
],
"hourly": [
'erpnext.hr.doctype.daily_work_summary_group.daily_work_summary_group.trigger_emails',
@ -304,21 +305,22 @@ scheduler_events = {
"erpnext.support.doctype.service_level_agreement.service_level_agreement.check_agreement_status",
"erpnext.crm.doctype.email_campaign.email_campaign.send_email_to_leads_or_contacts",
"erpnext.crm.doctype.email_campaign.email_campaign.set_email_campaign_status",
"erpnext.selling.doctype.quotation.quotation.set_expired_status"
"erpnext.selling.doctype.quotation.quotation.set_expired_status",
"erpnext.healthcare.doctype.patient_appointment.patient_appointment.update_appointment_status"
],
"daily_long": [
"erpnext.setup.doctype.email_digest.email_digest.send",
"erpnext.manufacturing.doctype.bom_update_tool.bom_update_tool.update_latest_price_in_all_boms",
"erpnext.hr.doctype.leave_ledger_entry.leave_ledger_entry.process_expired_allocation",
"erpnext.hr.utils.generate_leave_encashment",
"erpnext.loan_management.doctype.loan_security_shortfall.loan_security_shortfall.check_for_ltv_shortfall",
"erpnext.loan_management.doctype.loan_interest_accrual.loan_interest_accrual.make_accrual_interest_entry_for_term_loans"
"erpnext.loan_management.doctype.loan_security_shortfall.loan_security_shortfall.create_process_loan_security_shortfall",
"erpnext.loan_management.doctype.loan_interest_accrual.loan_interest_accrual.process_loan_interest_accrual_for_term_loans"
],
"monthly_long": [
"erpnext.accounts.deferred_revenue.convert_deferred_revenue_to_income",
"erpnext.accounts.deferred_revenue.convert_deferred_expense_to_expense",
"erpnext.hr.utils.allocate_earned_leaves",
"erpnext.loan_management.doctype.loan_interest_accrual.loan_interest_accrual.process_loan_interest_accrual"
"erpnext.loan_management.doctype.loan_interest_accrual.loan_interest_accrual.process_loan_interest_accrual_for_demand_loans"
]
}

View File

@ -55,8 +55,8 @@ frappe.ui.form.on('Employee',{
};
});
},
prefered_contact_email:function(frm){
frm.events.update_contact(frm)
prefered_contact_email:function(frm){
frm.events.update_contact(frm)
},
personal_email:function(frm){
frm.events.update_contact(frm)

View File

@ -580,19 +580,22 @@ def get_leaves_for_period(employee, leave_type, from_date, to_date):
return leave_days
def skip_expiry_leaves(leave_entry, date):
''' Checks whether the expired leaves coincide with the to_date of leave balance check '''
''' Checks whether the expired leaves coincide with the to_date of leave balance check.
This allows backdated leave entry creation for non carry forwarded allocation '''
end_date = frappe.db.get_value("Leave Allocation", {'name': leave_entry.transaction_name}, ['to_date'])
return True if end_date == date and not leave_entry.is_carry_forward else False
def get_leave_entries(employee, leave_type, from_date, to_date):
''' Returns leave entries between from_date and to_date '''
''' Returns leave entries between from_date and to_date. '''
return frappe.db.sql("""
SELECT
employee, leave_type, from_date, to_date, leaves, transaction_name, transaction_type,
is_carry_forward, is_expired
FROM `tabLeave Ledger Entry`
WHERE employee=%(employee)s AND leave_type=%(leave_type)s
AND docstatus=1 AND leaves<0
AND docstatus=1
AND (leaves<0
OR is_expired=1)
AND (from_date between %(from_date)s AND %(to_date)s
OR to_date between %(from_date)s AND %(to_date)s
OR (from_date < %(from_date)s AND to_date > %(to_date)s))

View File

@ -8,7 +8,6 @@ from frappe import _
from frappe.model.document import Document
from frappe.utils import getdate, nowdate, flt
from erpnext.hr.utils import set_employee_name
from erpnext.hr.doctype.leave_application.leave_application import get_leave_balance_on
from erpnext.hr.doctype.salary_structure_assignment.salary_structure_assignment import get_assigned_salary_structure
from erpnext.hr.doctype.leave_ledger_entry.leave_ledger_entry import create_leave_ledger_entry
from erpnext.hr.doctype.leave_allocation.leave_allocation import get_unused_leaves

View File

@ -141,6 +141,7 @@ def expire_allocation(allocation, expiry_date=None):
leaves = get_remaining_leaves(allocation)
expiry_date = expiry_date if expiry_date else allocation.to_date
# allows expired leaves entry to be created/reverted
if leaves:
args = dict(
leaves=flt(leaves) * -1,
@ -160,6 +161,8 @@ def expire_carried_forward_allocation(allocation):
from erpnext.hr.doctype.leave_application.leave_application import get_leaves_for_period
leaves_taken = get_leaves_for_period(allocation.employee, allocation.leave_type, allocation.from_date, allocation.to_date)
leaves = flt(allocation.leaves) + flt(leaves_taken)
# allow expired leaves entry to be created
if leaves > 0:
args = frappe._dict(
transaction_name=allocation.name,

View File

@ -13,7 +13,7 @@ from erpnext.hr.doctype.salary_slip.test_salary_slip import get_salary_component
make_earning_salary_component, make_deduction_salary_component
from erpnext.hr.doctype.salary_structure.test_salary_structure import make_salary_structure
from erpnext.loan_management.doctype.loan.test_loan import create_loan, make_loan_disbursement_entry
from erpnext.loan_management.doctype.loan_interest_accrual.loan_interest_accrual import make_accrual_interest_entry_for_term_loans
from erpnext.loan_management.doctype.process_loan_interest_accrual.process_loan_interest_accrual import process_loan_interest_accrual_for_term_loans
class TestPayrollEntry(unittest.TestCase):
def setUp(self):
@ -81,7 +81,7 @@ class TestPayrollEntry(unittest.TestCase):
make_loan_disbursement_entry(loan.name, loan.loan_amount, disbursement_date=add_months(nowdate(), -1))
make_accrual_interest_entry_for_term_loans(posting_date=nowdate())
process_loan_interest_accrual_for_term_loans(posting_date=nowdate())
dates = get_start_end_dates('Monthly', nowdate())

View File

@ -372,8 +372,7 @@
"fieldtype": "Table",
"label": "Employee Loan",
"options": "Salary Slip Loan",
"print_hide": 1,
"read_only": 1
"print_hide": 1
},
{
"fieldname": "section_break_43",
@ -464,7 +463,7 @@
"idx": 9,
"is_submittable": 1,
"links": [],
"modified": "2019-12-31 17:13:45.146271",
"modified": "2020-04-09 20:02:53.159827",
"modified_by": "Administrator",
"module": "HR",
"name": "Salary Slip",

View File

@ -755,30 +755,43 @@ class SalarySlip(TransactionBase):
d.amount = self.get_amount_based_on_payment_days(d, joining_date, relieving_date)[0]
def set_loan_repayment(self):
self.set('loans', [])
self.total_loan_repayment = 0
self.total_interest_amount = 0
self.total_principal_amount = 0
for loan in self.get_loan_details():
if not self.get('loans'):
for loan in self.get_loan_details():
amounts = calculate_amounts(loan.name, self.posting_date, "Regular Payment")
amounts = calculate_amounts(loan.name, self.posting_date, "Regular Payment")
total_payment = amounts['interest_amount'] + amounts['payable_principal_amount']
if amounts['interest_amount'] or amounts['payable_principal_amount']:
self.append('loans', {
'loan': loan.name,
'total_payment': amounts['interest_amount'] + amounts['payable_principal_amount'],
'interest_amount': amounts['interest_amount'],
'principal_amount': amounts['payable_principal_amount'],
'loan_account': loan.loan_account,
'interest_income_account': loan.interest_income_account
})
if total_payment:
self.append('loans', {
'loan': loan.name,
'total_payment': total_payment,
'interest_amount': amounts['interest_amount'],
'principal_amount': amounts['payable_principal_amount'],
'loan_account': loan.loan_account,
'interest_income_account': loan.interest_income_account
})
for payment in self.get('loans'):
amounts = calculate_amounts(payment.loan, self.posting_date, "Regular Payment")
self.total_loan_repayment += total_payment
self.total_interest_amount += amounts['interest_amount']
self.total_principal_amount += amounts['payable_principal_amount']
if payment.interest_amount > amounts['interest_amount']:
frappe.throw(_("""Row {0}: Paid Interest amount {1} is greater than pending interest amount {2}
against loan {3}""").format(payment.idx, frappe.bold(payment.interest_amount),
frappe.bold(amounts['interest_amount']), frappe.bold(payment.loan)))
if payment.principal_amount > amounts['payable_principal_amount']:
frappe.throw(_("""Row {0}: Paid Principal amount {1} is greater than pending principal amount {2}
against loan {3}""").format(payment.idx, frappe.bold(payment.principal_amount),
frappe.bold(amounts['payable_principal_amount']), frappe.bold(payment.loan)))
payment.total_payment = payment.interest_amount + payment.principal_amount
self.total_interest_amount += payment.interest_amount
self.total_principal_amount += payment.principal_amount
self.total_loan_repayment = self.total_interest_amount + self.total_principal_amount
def get_loan_details(self):

View File

@ -146,7 +146,7 @@ class TestSalarySlip(unittest.TestCase):
def test_loan_repayment_salary_slip(self):
from erpnext.loan_management.doctype.loan.test_loan import create_loan_type, create_loan, make_loan_disbursement_entry, create_loan_accounts
from erpnext.loan_management.doctype.loan_interest_accrual.loan_interest_accrual import make_accrual_interest_entry_for_term_loans
from erpnext.loan_management.doctype.process_loan_interest_accrual.process_loan_interest_accrual import process_loan_interest_accrual_for_term_loans
applicant = make_employee("test_loanemployee@salary.com", company="_Test Company")
@ -166,7 +166,7 @@ class TestSalarySlip(unittest.TestCase):
make_loan_disbursement_entry(loan.name, loan.loan_amount, disbursement_date=add_months(nowdate(), -1))
make_accrual_interest_entry_for_term_loans(posting_date=nowdate())
process_loan_interest_accrual_for_term_loans(posting_date=nowdate())
ss = make_employee_salary_slip("test_loanemployee@salary.com", "Monthly")
ss.submit()

Some files were not shown because too many files have changed in this diff Show More