From f2f1e81a3f60999bf3dddf308ba8889defb99474 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Wed, 10 Jul 2019 20:28:52 +0530 Subject: [PATCH 1/6] feat: added chart to cash-flow report --- .../accounts/report/cash_flow/cash_flow.py | 21 +++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/report/cash_flow/cash_flow.py b/erpnext/accounts/report/cash_flow/cash_flow.py index 75d99e75de..626d18514e 100644 --- a/erpnext/accounts/report/cash_flow/cash_flow.py +++ b/erpnext/accounts/report/cash_flow/cash_flow.py @@ -69,7 +69,9 @@ def execute(filters=None): add_total_row_account(data, data, _("Net Change in Cash"), period_list, company_currency) columns = get_columns(filters.periodicity, period_list, filters.accumulated_values, filters.company) - return columns, data + chart = get_chart_data(columns, data) + + return columns, data, None, chart def get_cash_flow_accounts(): operation_accounts = { @@ -171,4 +173,19 @@ def add_total_row_account(out, data, label, period_list, currency, consolidated total_row["total"] += row["total"] out.append(total_row) - out.append({}) \ No newline at end of file + out.append({}) + +def get_chart_data(columns, data): + labels = [d.get("label") for d in columns[2:]] + datasets = [{'name':account.get('account').replace("'", ""), 'values': [account.get('total')]} for account in data if account.get('parent_account') == None and account.get('currency')] + datasets = datasets[:-1] + + chart = { + "data": { + 'labels': labels, + 'datasets': datasets + }, + "type": "bar" + } + + return chart \ No newline at end of file From f8c92f30bf1a1db415dd76004d75db7d404088eb Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Thu, 11 Jul 2019 18:27:19 +0530 Subject: [PATCH 2/6] feat(purchase-order): Add tax category field in purchase order --- .../doctype/purchase_order/purchase_order.json | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.json b/erpnext/buying/doctype/purchase_order/purchase_order.json index dead1f030c..5dce349782 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.json +++ b/erpnext/buying/doctype/purchase_order/purchase_order.json @@ -66,10 +66,11 @@ "net_total", "total_net_weight", "taxes_section", - "taxes_and_charges", + "tax_category", "column_break_50", "shipping_rule", "section_break_52", + "taxes_and_charges", "taxes", "sec_tax_breakup", "other_charges_calculation", @@ -569,7 +570,7 @@ { "fieldname": "taxes_and_charges", "fieldtype": "Link", - "label": "Taxes and Charges", + "label": "Purchase Taxes and Charges Template", "oldfieldname": "purchase_other_charges", "oldfieldtype": "Link", "options": "Purchase Taxes and Charges Template", @@ -1032,12 +1033,18 @@ "fieldname": "update_auto_repeat_reference", "fieldtype": "Button", "label": "Update Auto Repeat Reference" + }, + { + "fieldname": "tax_category", + "fieldtype": "Link", + "label": "Tax Category", + "options": "Tax Category" } ], "icon": "fa fa-file-text", "idx": 105, "is_submittable": 1, - "modified": "2019-06-24 21:22:05.483429", + "modified": "2019-07-11 18:25:49.509343", "modified_by": "Administrator", "module": "Buying", "name": "Purchase Order", From 53e7df078ec5fe5265c5ae28ae01a59e2151157c Mon Sep 17 00:00:00 2001 From: Anurag Mishra <32095923+Anurag810@users.noreply.github.com> Date: Fri, 12 Jul 2019 12:05:39 +0530 Subject: [PATCH 3/6] fix: set project link in item tables (#18233) --- .../doctype/material_request/material_request.py | 2 +- .../stock_entry_detail/stock_entry_detail.json | 12 ++++++++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/erpnext/stock/doctype/material_request/material_request.py b/erpnext/stock/doctype/material_request/material_request.py index 2b079e7995..f2fe44879d 100644 --- a/erpnext/stock/doctype/material_request/material_request.py +++ b/erpnext/stock/doctype/material_request/material_request.py @@ -450,7 +450,7 @@ def make_stock_entry(source_name, target_doc=None): "field_map": { "name": "material_request_item", "parent": "material_request", - "uom": "stock_uom", + "uom": "stock_uom" }, "postprocess": update_item, "condition": lambda doc: doc.ordered_qty < doc.stock_qty diff --git a/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json b/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json index 5933700457..912a4651b3 100644 --- a/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json +++ b/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json @@ -61,7 +61,8 @@ "ste_detail", "column_break_51", "transferred_qty", - "reference_purchase_receipt" + "reference_purchase_receipt", + "project" ], "fields": [ { @@ -472,11 +473,18 @@ "label": "Reference Purchase Receipt", "options": "Purchase Receipt", "read_only": 1 + }, + { + "fieldname": "project", + "fieldtype": "Link", + "label": "Project", + "options": "Project", + "read_only": 1 } ], "idx": 1, "istable": 1, - "modified": "2019-06-14 11:58:41.958144", + "modified": "2019-07-12 11:34:53.190749", "modified_by": "Administrator", "module": "Stock", "name": "Stock Entry Detail", From 7d1d9aa46bbcb9e50774cb4f90adac12f6536683 Mon Sep 17 00:00:00 2001 From: Himanshu Date: Fri, 12 Jul 2019 06:49:35 +0000 Subject: [PATCH 4/6] feat(Issue): Reset SLA (#18244) * feat: reset sla * fix: db.get_single_value * feat: timeline entry for reset sla * fix: remove communication from support config * fix: reset sla on split_issue --- erpnext/config/support.py | 8 +--- erpnext/support/doctype/issue/issue.js | 42 +++++++++++++++++++ erpnext/support/doctype/issue/issue.json | 23 ++++++++-- erpnext/support/doctype/issue/issue.py | 21 +++++++++- erpnext/support/doctype/issue/test_issue.py | 3 +- .../test_service_level_agreement.py | 1 + .../support_settings/support_settings.json | 9 +++- 7 files changed, 93 insertions(+), 14 deletions(-) diff --git a/erpnext/config/support.py b/erpnext/config/support.py index 36b4214196..151c4f743e 100644 --- a/erpnext/config/support.py +++ b/erpnext/config/support.py @@ -21,13 +21,7 @@ def get_data(): "type": "doctype", "name": "Issue Priority", "description": _("Issue Priority."), - }, - { - "type": "doctype", - "name": "Communication", - "description": _("Communication log."), - "onboard": 1, - }, + } ] }, { diff --git a/erpnext/support/doctype/issue/issue.js b/erpnext/support/doctype/issue/issue.js index 2d9650c5a4..aec9db9d4a 100644 --- a/erpnext/support/doctype/issue/issue.js +++ b/erpnext/support/doctype/issue/issue.js @@ -2,6 +2,12 @@ frappe.ui.form.on("Issue", { onload: function(frm) { frm.email_field = "raised_by"; + frappe.db.get_value("Support Settings", {name: "Support Settings"}, "allow_resetting_service_level_agreement", (r) => { + if (!r.allow_resetting_service_level_agreement) { + frm.set_df_property("reset_service_level_agreement", "hidden", 1) ; + } + }); + if (frm.doc.service_level_agreement) { frappe.call({ method: "erpnext.support.doctype.service_level_agreement.service_level_agreement.get_service_level_agreement_filters", @@ -73,6 +79,42 @@ frappe.ui.form.on("Issue", { } }, + reset_service_level_agreement: function(frm) { + let reset_sla = new frappe.ui.Dialog({ + title: __("Reset Service Level Agreement"), + fields: [ + { + fieldtype: "Data", + fieldname: "reason", + label: __("Reason"), + reqd: 1 + } + ], + primary_action_label: __("Reset"), + primary_action: (values) => { + reset_sla.disable_primary_action(); + reset_sla.hide(); + reset_sla.clear(); + + frappe.show_alert({ + indicator: 'green', + message: __('Resetting Service Level Agreement.') + }); + + frm.call("reset_service_level_agreement", { + reason: values.reason, + user: frappe.session.user_email + }, () => { + reset_sla.enable_primary_action(); + frm.refresh(); + frappe.msgprint(__("Service Level Agreement Reset.")); + }); + } + }); + + reset_sla.show(); + }, + timeline_refresh: function(frm) { // create button for "Help Article" if(frappe.model.can_create('Help Article')) { diff --git a/erpnext/support/doctype/issue/issue.json b/erpnext/support/doctype/issue/issue.json index 72153dcdea..41fe380e75 100644 --- a/erpnext/support/doctype/issue/issue.json +++ b/erpnext/support/doctype/issue/issue.json @@ -22,10 +22,12 @@ "service_level_agreement", "response_by", "response_by_variance", + "reset_service_level_agreement", "cb", "agreement_fulfilled", "resolution_by", "resolution_by_variance", + "service_level_agreement_creation", "response", "mins_to_first_response", "first_responded_on", @@ -68,9 +70,9 @@ "fieldname": "subject", "fieldtype": "Data", "in_global_search": 1, + "in_standard_filter": 1, "label": "Subject", - "reqd": 1, - "in_standard_filter": 1 + "reqd": 1 }, { "fieldname": "customer", @@ -336,11 +338,24 @@ "fieldtype": "Float", "label": "Resolution By Variance", "read_only": 1 + }, + { + "fieldname": "service_level_agreement_creation", + "fieldtype": "Datetime", + "hidden": 1, + "label": "Service Level Agreement Creation", + "read_only": 1 + }, + { + "depends_on": "eval: doc.service_level_agreement", + "fieldname": "reset_service_level_agreement", + "fieldtype": "Button", + "label": "Reset Service Level Agreement" } ], "icon": "fa fa-ticket", "idx": 7, - "modified": "2019-06-30 13:19:38.215525", + "modified": "2019-07-11 23:57:22.015881", "modified_by": "Administrator", "module": "Support", "name": "Issue", @@ -365,4 +380,4 @@ "timeline_field": "customer", "title_field": "subject", "track_seen": 1 -} +} \ No newline at end of file diff --git a/erpnext/support/doctype/issue/issue.py b/erpnext/support/doctype/issue/issue.py index 226676fec3..ce9fb12145 100644 --- a/erpnext/support/doctype/issue/issue.py +++ b/erpnext/support/doctype/issue/issue.py @@ -121,6 +121,7 @@ class Issue(Document): # Reset SLA if replicated_issue.service_level_agreement: + replicated_issue.service_level_agreement_creation = now_datetime() replicated_issue.service_level_agreement = None replicated_issue.agreement_fulfilled = "Ongoing" replicated_issue.response_by = None @@ -173,8 +174,9 @@ class Issue(Document): if not self.creation: self.creation = now_datetime() + self.service_level_agreement_creation = now_datetime() - start_date_time = get_datetime(self.creation) + start_date_time = get_datetime(self.service_level_agreement_creation) self.response_by = get_expected_time_for(parameter='response', service_level=priority, start_date_time=start_date_time) self.resolution_by = get_expected_time_for(parameter='resolution', service_level=priority, start_date_time=start_date_time) @@ -193,6 +195,23 @@ class Issue(Document): self.set_response_and_resolution_time(priority=self.priority, service_level_agreement=self.service_level_agreement) frappe.msgprint(_("Service Level Agreement has been changed to {0}.").format(self.service_level_agreement)) + def reset_service_level_agreement(self, reason, user): + if not frappe.db.get_single_value("Support Settings", "allow_resetting_service_level_agreement"): + frappe.throw(_("Allow Resetting Service Level Agreement from Support Settings.")) + + frappe.get_doc({ + "doctype": "Comment", + "comment_type": "Info", + "reference_doctype": self.doctype, + "reference_name": self.name, + "comment_email": user, + "content": " resetted Service Level Agreement - {0}".format(_(reason)), + }).insert(ignore_permissions=True) + + self.service_level_agreement_creation = now_datetime() + self.set_response_and_resolution_time(priority=self.priority, service_level_agreement=self.service_level_agreement) + self.save() + def get_expected_time_for(parameter, service_level, start_date_time): current_date_time = start_date_time expected_time = current_date_time diff --git a/erpnext/support/doctype/issue/test_issue.py b/erpnext/support/doctype/issue/test_issue.py index eb1736e6c9..7a5e3e300d 100644 --- a/erpnext/support/doctype/issue/test_issue.py +++ b/erpnext/support/doctype/issue/test_issue.py @@ -80,7 +80,8 @@ def make_issue(creation=None, customer=None, index=0): "customer": customer, "raised_by": "test@example.com", "description": "Service Level Agreement Issue", - "creation": creation + "creation": creation, + "service_level_agreement_creation": creation }).insert(ignore_permissions=True) return issue diff --git a/erpnext/support/doctype/service_level_agreement/test_service_level_agreement.py b/erpnext/support/doctype/service_level_agreement/test_service_level_agreement.py index 68b82d10ec..4a741ea4e1 100644 --- a/erpnext/support/doctype/service_level_agreement/test_service_level_agreement.py +++ b/erpnext/support/doctype/service_level_agreement/test_service_level_agreement.py @@ -76,6 +76,7 @@ def create_service_level_agreement(default_service_level_agreement, service_leve service_level_agreement = frappe.get_doc({ "doctype": "Service Level Agreement", + "enable": 1, "default_service_level_agreement": default_service_level_agreement, "service_level": service_level, "holiday_list": holiday_list, diff --git a/erpnext/support/doctype/support_settings/support_settings.json b/erpnext/support/doctype/support_settings/support_settings.json index 2dced15d4e..be9e064591 100644 --- a/erpnext/support/doctype/support_settings/support_settings.json +++ b/erpnext/support/doctype/support_settings/support_settings.json @@ -6,6 +6,7 @@ "field_order": [ "sb_00", "track_service_level_agreement", + "allow_resetting_service_level_agreement", "issues_sb", "close_issue_after_days", "portal_sb", @@ -118,10 +119,16 @@ "fieldname": "track_service_level_agreement", "fieldtype": "Check", "label": "Track Service Level Agreement" + }, + { + "default": "0", + "fieldname": "allow_resetting_service_level_agreement", + "fieldtype": "Check", + "label": "Allow Resetting Service Level Agreement" } ], "issingle": 1, - "modified": "2019-07-09 17:11:38.216732", + "modified": "2019-07-10 22:52:39.663873", "modified_by": "Administrator", "module": "Support", "name": "Support Settings", From 91417646b105e453d69480e062d2146c685487d2 Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Fri, 12 Jul 2019 12:24:05 +0530 Subject: [PATCH 5/6] Fix: Add fieldtype as currency --- erpnext/accounts/report/cash_flow/cash_flow.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/report/cash_flow/cash_flow.py b/erpnext/accounts/report/cash_flow/cash_flow.py index 626d18514e..cd3d8dc125 100644 --- a/erpnext/accounts/report/cash_flow/cash_flow.py +++ b/erpnext/accounts/report/cash_flow/cash_flow.py @@ -187,5 +187,7 @@ def get_chart_data(columns, data): }, "type": "bar" } + + chart["fieldtype"] = "Currency" - return chart \ No newline at end of file + return chart From 5073ac4dcb7bfd46fab64ce1ad261adcbe96ecc2 Mon Sep 17 00:00:00 2001 From: Jamsheer Date: Fri, 12 Jul 2019 12:28:34 +0530 Subject: [PATCH 6/6] fix: Patient History (Patient Medical Record) (#17972) * Page - Patient History * Healthcare Utils - get patient vitals * Healthcare Utils - render doc as html for patient history page * Page - Patient History - prcatitioner image in time line * Page - Patient History in menu * fix - page medical history * fix: page patient_history broken img, minor fixes * fix: patient_history page, add pagination, show doc if fetched * fix: Patient Medical Record to Patient History * fix: patient history page - set route options * fix: update to code standards * fix: remove Eidt button form patient history * fix: css update to code standards * fix: Remove page medical_record --- erpnext/config/healthcare.py | 4 +- erpnext/healthcare/doctype/patient/patient.js | 4 +- .../patient_appointment.js | 4 +- .../patient_encounter/patient_encounter.js | 4 +- .../page/medical_record/__init__.py | 1 - .../page/medical_record/medical_record.js | 182 ----------- .../page/medical_record/medical_record.py | 24 -- .../medical_record/medical_record_row.html | 21 -- .../page/medical_record/patient_select.html | 5 - .../page/patient_history/__init__.py | 0 .../patient_history.css} | 64 +++- .../page/patient_history/patient_history.html | 20 ++ .../page/patient_history/patient_history.js | 300 ++++++++++++++++++ .../patient_history.json} | 15 +- .../page/patient_history/patient_history.py | 39 +++ erpnext/healthcare/utils.py | 113 +++++++ erpnext/patches.txt | 1 + .../remove_patient_medical_record_page.py | 7 + 18 files changed, 556 insertions(+), 252 deletions(-) delete mode 100644 erpnext/healthcare/page/medical_record/__init__.py delete mode 100644 erpnext/healthcare/page/medical_record/medical_record.js delete mode 100644 erpnext/healthcare/page/medical_record/medical_record.py delete mode 100644 erpnext/healthcare/page/medical_record/medical_record_row.html delete mode 100644 erpnext/healthcare/page/medical_record/patient_select.html create mode 100644 erpnext/healthcare/page/patient_history/__init__.py rename erpnext/healthcare/page/{medical_record/medical_record.css => patient_history/patient_history.css} (62%) create mode 100644 erpnext/healthcare/page/patient_history/patient_history.html create mode 100644 erpnext/healthcare/page/patient_history/patient_history.js rename erpnext/healthcare/page/{medical_record/medical_record.json => patient_history/patient_history.json} (57%) create mode 100644 erpnext/healthcare/page/patient_history/patient_history.py create mode 100644 erpnext/patches/v12_0/remove_patient_medical_record_page.py diff --git a/erpnext/config/healthcare.py b/erpnext/config/healthcare.py index 1311111618..756d22e416 100644 --- a/erpnext/config/healthcare.py +++ b/erpnext/config/healthcare.py @@ -26,8 +26,8 @@ def get_data(): }, { "type": "page", - "name": "medical_record", - "label": _("Patient Medical Record"), + "name": "patient_history", + "label": _("Patient History"), }, { "type": "page", diff --git a/erpnext/healthcare/doctype/patient/patient.js b/erpnext/healthcare/doctype/patient/patient.js index 169281430c..1a34fe8076 100644 --- a/erpnext/healthcare/doctype/patient/patient.js +++ b/erpnext/healthcare/doctype/patient/patient.js @@ -21,9 +21,9 @@ frappe.ui.form.on('Patient', { }); } if (frm.doc.patient_name && frappe.user.has_role("Physician")) { - frm.add_custom_button(__('Medical Record'), function () { + frm.add_custom_button(__('Patient History'), function () { frappe.route_options = { "patient": frm.doc.name }; - frappe.set_route("medical_record"); + frappe.set_route("patient_history"); },"View"); } if (!frm.doc.__islocal && (frappe.user.has_role("Nursing User") || frappe.user.has_role("Physician"))) { diff --git a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js index b3cbd1f753..858145eef3 100644 --- a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js +++ b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js @@ -30,9 +30,9 @@ frappe.ui.form.on('Patient Appointment', { }; }); if(frm.doc.patient){ - frm.add_custom_button(__('Medical Record'), function() { + frm.add_custom_button(__('Patient History'), function() { frappe.route_options = {"patient": frm.doc.patient}; - frappe.set_route("medical_record"); + frappe.set_route("patient_history"); },__("View")); } if(frm.doc.status == "Open"){ diff --git a/erpnext/healthcare/doctype/patient_encounter/patient_encounter.js b/erpnext/healthcare/doctype/patient_encounter/patient_encounter.js index 7ea45688fd..088bc8161b 100644 --- a/erpnext/healthcare/doctype/patient_encounter/patient_encounter.js +++ b/erpnext/healthcare/doctype/patient_encounter/patient_encounter.js @@ -41,10 +41,10 @@ frappe.ui.form.on('Patient Encounter', { } }); } - frm.add_custom_button(__('Medical Record'), function() { + frm.add_custom_button(__('Patient History'), function() { if (frm.doc.patient) { frappe.route_options = {"patient": frm.doc.patient}; - frappe.set_route("medical_record"); + frappe.set_route("patient_history"); } else { frappe.msgprint(__("Please select Patient")); } diff --git a/erpnext/healthcare/page/medical_record/__init__.py b/erpnext/healthcare/page/medical_record/__init__.py deleted file mode 100644 index baffc48825..0000000000 --- a/erpnext/healthcare/page/medical_record/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from __future__ import unicode_literals diff --git a/erpnext/healthcare/page/medical_record/medical_record.js b/erpnext/healthcare/page/medical_record/medical_record.js deleted file mode 100644 index df19d8f4dc..0000000000 --- a/erpnext/healthcare/page/medical_record/medical_record.js +++ /dev/null @@ -1,182 +0,0 @@ -frappe.provide("frappe.medical_record"); -frappe.pages['medical_record'].on_page_load = function(wrapper) { - var me = this; - var page = frappe.ui.make_app_page({ - parent: wrapper, - title: 'Medical Record', - }); - - frappe.breadcrumbs.add("Medical"); - - page.main.html(frappe.render_template("patient_select", {})); - var patient = frappe.ui.form.make_control({ - parent: page.main.find(".patient"), - df: { - fieldtype: "Link", - options: "Patient", - fieldname: "patient", - change: function(){ - page.main.find(".frappe-list").html(""); - draw_page(patient.get_value(), me); - } - }, - only_input: true, - }); - patient.refresh(); - - - this.page.main.on("click", ".medical_record-message", function() { - var doctype = $(this).attr("data-doctype"), - docname = $(this).attr("data-docname"); - - if (doctype && docname) { - frappe.route_options = { - scroll_to: { "doctype": doctype, "name": docname } - }; - frappe.set_route(["Form", doctype, docname]); - } - }); - - this.page.sidebar.on("click", ".edit-details", function() { - patient = patient.get_value(); - if (patient) { - frappe.set_route(["Form", "Patient", patient]); - } - }); - -}; - -frappe.pages['medical_record'].refresh = function() { - var me = this; - - if(frappe.route_options) { - if(frappe.route_options.patient){ - me.page.main.find(".frappe-list").html(""); - var patient = frappe.route_options.patient; - draw_page(patient,me); - me.page.main.find("[data-fieldname='patient']").val(patient); - frappe.route_options = null; - } - } -}; -var show_patient_info = function(patient, me){ - frappe.call({ - "method": "erpnext.healthcare.doctype.patient.patient.get_patient_detail", - args: { - patient: patient - }, - callback: function (r) { - var data = r.message; - var details = ""; - if(data.email) details += "
Email : " + data.email; - if(data.mobile) details += "
Mobile : " + data.mobile; - if(data.occupation) details += "
Occupation : " + data.occupation; - if(data.blood_group) details += "
Blood group : " + data.blood_group; - if(data.allergies) details += "

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

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

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

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

More info : " + data.patient_details; - - if(details){ - details = "

Patient Details" + details + "
"; - } - - var vitals = ""; - if(data.temperature) vitals += "
Temperature : " + data.temperature; - if(data.pulse) vitals += "
Pulse : " + data.pulse; - if(data.respiratory_rate) vitals += "
Respiratory Rate : " + data.respiratory_rate; - if(data.bp) vitals += "
BP : " + data.bp; - if(data.bmi) vitals += "
BMI : " + data.bmi; - if(data.height) vitals += "
Height : " + data.height; - if(data.weight) vitals += "
Weight : " + data.weight; - if(data.signs_date) vitals += "
Date : " + data.signs_date; - - if(vitals){ - vitals = "

Vital Signs" + vitals + "
"; - details = vitals + details; - } - if(details) details += "

Edit Details
"; - - me.page.sidebar.addClass("col-sm-3"); - me.page.sidebar.html(details); - me.page.wrapper.find(".layout-main-section-wrapper").addClass("col-sm-9"); - } - }); -}; -var draw_page = function(patient, me){ - frappe.model.with_doctype("Patient Medical Record", function() { - me.page.list = new frappe.ui.BaseList({ - hide_refresh: true, - page: me.page, - method: 'erpnext.healthcare.page.medical_record.medical_record.get_feed', - args: {name: patient}, - parent: $("
").appendTo(me.page.main), - render_view: function(values) { - var me = this; - var wrapper = me.page.main.find(".result-list").get(0); - values.map(function (value) { - var row = $('
').data("data", value).appendTo($(wrapper)).get(0); - new frappe.medical_record.Feed(row, value); - }); - }, - show_filters: true, - doctype: "Patient Medical Record", - }); - show_patient_info(patient, me); - me.page.list.run(); - }); -}; - -frappe.medical_record.last_feed_date = false; -frappe.medical_record.Feed = Class.extend({ - init: function(row, data) { - this.scrub_data(data); - this.add_date_separator(row, data); - if(!data.add_class) - data.add_class = "label-default"; - - data.link = ""; - if (data.reference_doctype && data.reference_name) { - data.link = frappe.format(data.reference_name, {fieldtype: "Link", options: data.reference_doctype}, - {label: __(data.reference_doctype)}); - } - - $(row) - .append(frappe.render_template("medical_record_row", data)) - .find("a").addClass("grey"); - }, - scrub_data: function(data) { - data.by = frappe.user.full_name(data.owner); - data.imgsrc = frappe.utils.get_file_link(frappe.user_info(data.owner).image); - - data.icon = "icon-flag"; - }, - add_date_separator: function(row, data) { - var date = frappe.datetime.str_to_obj(data.creation); - var last = frappe.medical_record.last_feed_date; - - if((last && frappe.datetime.obj_to_str(last) != frappe.datetime.obj_to_str(date)) || (!last)) { - var diff = frappe.datetime.get_day_diff(frappe.datetime.get_today(), frappe.datetime.obj_to_str(date)); - if(diff < 1) { - var pdate = 'Today'; - } else if(diff < 2) { - pdate = 'Yesterday'; - } else { - pdate = frappe.datetime.global_date_format(date); - } - data.date_sep = pdate; - data.date_class = pdate=='Today' ? "date-indicator blue" : "date-indicator"; - } else { - data.date_sep = null; - data.date_class = ""; - } - frappe.medical_record.last_feed_date = date; - } -}); diff --git a/erpnext/healthcare/page/medical_record/medical_record.py b/erpnext/healthcare/page/medical_record/medical_record.py deleted file mode 100644 index 22c5852b05..0000000000 --- a/erpnext/healthcare/page/medical_record/medical_record.py +++ /dev/null @@ -1,24 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (c) 2015, ESS LLP and contributors -# For license information, please see license.txt - -from __future__ import unicode_literals -import frappe -from frappe.utils import cint - -@frappe.whitelist() -def get_feed(start, page_length, name): - """get feed""" - result = frappe.db.sql("""select name, owner, modified, creation, - reference_doctype, reference_name, subject - from `tabPatient Medical Record` - where patient=%(patient)s - order by creation desc - limit %(start)s, %(page_length)s""", - { - "start": cint(start), - "page_length": cint(page_length), - "patient": name - }, as_dict=True) - - return result diff --git a/erpnext/healthcare/page/medical_record/medical_record_row.html b/erpnext/healthcare/page/medical_record/medical_record_row.html deleted file mode 100644 index 9a670c9aa7..0000000000 --- a/erpnext/healthcare/page/medical_record/medical_record_row.html +++ /dev/null @@ -1,21 +0,0 @@ -
-
- {%= date_sep || "" %} -
-
- -
- -
- - {% if (reference_doctype && reference_name) { %} - {%= __("{0}: {1}", [link, "" + subject + ""]) %} - {% } else { %} - {%= subject %} - {% } %} - -
-
diff --git a/erpnext/healthcare/page/medical_record/patient_select.html b/erpnext/healthcare/page/medical_record/patient_select.html deleted file mode 100644 index 321baf738d..0000000000 --- a/erpnext/healthcare/page/medical_record/patient_select.html +++ /dev/null @@ -1,5 +0,0 @@ -
- -

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

-

-
diff --git a/erpnext/healthcare/page/patient_history/__init__.py b/erpnext/healthcare/page/patient_history/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/healthcare/page/medical_record/medical_record.css b/erpnext/healthcare/page/patient_history/patient_history.css similarity index 62% rename from erpnext/healthcare/page/medical_record/medical_record.css rename to erpnext/healthcare/page/patient_history/patient_history.css index 977625bbd1..865d6abee0 100644 --- a/erpnext/healthcare/page/medical_record/medical_record.css +++ b/erpnext/healthcare/page/patient_history/patient_history.css @@ -14,6 +14,10 @@ margin-bottom: -4px; } +.medical_record-row > * { + z-index: -999; +} + .date-indicator { background:none; font-size:12px; @@ -35,6 +39,61 @@ color: #5e64ff; } +.div-bg-color { + background: #fafbfc; +} + +.bg-color-white { + background: #FFFFFF; +} + +.d-flex { + display: flex; +} + +.width-full { + width: 100%; +} + +.p-3 { + padding: 16px; +} + +.mt-2 { + margin-top: 8px; +} + +.mr-3 { + margin-right: 16px; +} + +.Box { + background-color: #fff; + border: 1px solid #d1d5da; + border-radius: 3px; +} + +.flex-column { + flex-direction: column; +} + +.avatar { + display: inline-block; + overflow: hidden; + line-height: 1; + vertical-align: middle; + border-radius: 3px; +} + +.py-3 { + padding-top: 16px; + padding-bottom: 16px; +} + +.border-bottom { + border-bottom: 1px #e1e4e8 solid; +} + .date-indicator.blue::after { background: #5e64ff; } @@ -65,8 +124,3 @@ #page-medical_record .list-filters { display: none ; } - -#page-medical_record .octicon-heart { - color: #ff5858; - margin: 0px 5px; -} diff --git a/erpnext/healthcare/page/patient_history/patient_history.html b/erpnext/healthcare/page/patient_history/patient_history.html new file mode 100644 index 0000000000..7a9446dffd --- /dev/null +++ b/erpnext/healthcare/page/patient_history/patient_history.html @@ -0,0 +1,20 @@ +
+
+

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

+

+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+
\ No newline at end of file diff --git a/erpnext/healthcare/page/patient_history/patient_history.js b/erpnext/healthcare/page/patient_history/patient_history.js new file mode 100644 index 0000000000..87fe7edd29 --- /dev/null +++ b/erpnext/healthcare/page/patient_history/patient_history.js @@ -0,0 +1,300 @@ +frappe.provide("frappe.patient_history"); +frappe.pages['patient_history'].on_page_load = function(wrapper) { + var me = this; + var page = frappe.ui.make_app_page({ + parent: wrapper, + title: 'Patient History', + single_column: true + }); + + frappe.breadcrumbs.add("Healthcare"); + let pid = ''; + page.main.html(frappe.render_template("patient_history", {})); + var patient = frappe.ui.form.make_control({ + parent: page.main.find(".patient"), + df: { + fieldtype: "Link", + options: "Patient", + fieldname: "patient", + change: function(){ + if(pid != patient.get_value() && patient.get_value()){ + me.start = 0; + me.page.main.find(".patient_documents_list").html(""); + get_documents(patient.get_value(), me); + show_patient_info(patient.get_value(), me); + show_patient_vital_charts(patient.get_value(), me, "bp", "mmHg", "Blood Pressure"); + } + pid = patient.get_value(); + } + }, + only_input: true, + }); + patient.refresh(); + + if (frappe.route_options){ + patient.set_value(frappe.route_options.patient); + } + + this.page.main.on("click", ".btn-show-chart", function() { + var btn_show_id = $(this).attr("data-show-chart-id"), pts = $(this).attr("data-pts"); + var title = $(this).attr("data-title"); + show_patient_vital_charts(patient.get_value(), me, btn_show_id, pts, title); + }); + + this.page.main.on("click", ".btn-more", function() { + var doctype = $(this).attr("data-doctype"), docname = $(this).attr("data-docname"); + if(me.page.main.find("."+docname).parent().find('.document-html').attr('data-fetched') == "1"){ + me.page.main.find("."+docname).hide(); + me.page.main.find("."+docname).parent().find('.document-html').show(); + }else{ + if(doctype && docname){ + let exclude = ["patient", "patient_name", 'patient_sex', "encounter_date"]; + frappe.call({ + method: "erpnext.healthcare.utils.render_doc_as_html", + args:{ + doctype: doctype, + docname: docname, + exclude_fields: exclude + }, + callback: function(r) { + if (r.message){ + me.page.main.find("."+docname).hide(); + me.page.main.find("."+docname).parent().find('.document-html').html(r.message.html+"\ +
"); + me.page.main.find("."+docname).parent().find('.document-html').show(); + me.page.main.find("."+docname).parent().find('.document-html').attr('data-fetched', "1"); + } + }, + freeze: true + }); + } + } + }); + + this.page.main.on("click", ".btn-less", function() { + var docname = $(this).attr("data-docname"); + me.page.main.find("."+docname).parent().find('.document-id').show(); + me.page.main.find("."+docname).parent().find('.document-html').hide(); + }); + me.start = 0; + me.page.main.on("click", ".btn-get-records", function(){ + get_documents(patient.get_value(), me); + }); +}; + +var get_documents = function(patient, me){ + frappe.call({ + "method": "erpnext.healthcare.page.patient_history.patient_history.get_feed", + args: { + name: patient, + start: me.start, + page_length: 20 + }, + callback: function (r) { + var data = r.message; + if(data.length){ + add_to_records(me, data); + }else{ + me.page.main.find(".patient_documents_list").append("


No more records..

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


No more records..

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

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

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

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

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

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

More info : " + data.patient_details.replace("\n", "
"); + + if(details){ + details = "
" + details + "
"; + } + me.page.main.find(".patient_details").html(details); + } + }); +}; + +var show_patient_vital_charts = function(patient, me, btn_show_id, pts, title) { + frappe.call({ + method: "erpnext.healthcare.utils.get_patient_vitals", + args:{ + patient: patient + }, + callback: function(r) { + if (r.message){ + var show_chart_btns_html = ""; + me.page.main.find(".show_chart_btns").html(show_chart_btns_html); + var data = r.message; + let labels = [], datasets = []; + let bp_systolic = [], bp_diastolic = [], temperature = []; + let pulse = [], respiratory_rate = [], bmi = [], height = [], weight = []; + for(var i=0; i (d + '').toUpperCase(), + formatTooltipY: d => d + ' ' + pts, + } + }); + }else{ + me.page.main.find(".patient_vital_charts").html(""); + me.page.main.find(".show_chart_btns").html(""); + } + } + }); +}; diff --git a/erpnext/healthcare/page/medical_record/medical_record.json b/erpnext/healthcare/page/patient_history/patient_history.json similarity index 57% rename from erpnext/healthcare/page/medical_record/medical_record.json rename to erpnext/healthcare/page/patient_history/patient_history.json index ca30c3be29..b3892a41c6 100644 --- a/erpnext/healthcare/page/medical_record/medical_record.json +++ b/erpnext/healthcare/page/patient_history/patient_history.json @@ -1,18 +1,21 @@ { "content": null, - "creation": "2016-06-09 11:33:14.025787", + "creation": "2018-08-08 17:09:13.816199", "docstatus": 0, "doctype": "Page", - "icon": "icon-play", + "icon": "", "idx": 0, - "modified": "2018-08-06 11:40:39.705660", + "modified": "2018-08-08 17:09:55.969424", "modified_by": "Administrator", "module": "Healthcare", - "name": "medical_record", + "name": "patient_history", "owner": "Administrator", - "page_name": "medical_record", + "page_name": "patient_history", "restrict_to_domain": "Healthcare", "roles": [ + { + "role": "Healthcare Administrator" + }, { "role": "Physician" } @@ -21,5 +24,5 @@ "standard": "Yes", "style": null, "system_page": 0, - "title": "Medical Record" + "title": "Patient History" } \ No newline at end of file diff --git a/erpnext/healthcare/page/patient_history/patient_history.py b/erpnext/healthcare/page/patient_history/patient_history.py new file mode 100644 index 0000000000..772aa4ef5e --- /dev/null +++ b/erpnext/healthcare/page/patient_history/patient_history.py @@ -0,0 +1,39 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2018, ESS LLP and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +from frappe.utils import cint +from erpnext.healthcare.utils import render_docs_as_html + +@frappe.whitelist() +def get_feed(name, start=0, page_length=20): + """get feed""" + result = frappe.db.sql("""select name, owner, creation, + reference_doctype, reference_name, subject + from `tabPatient Medical Record` + where patient=%(patient)s + order by creation desc + limit %(start)s, %(page_length)s""", + { + "patient": name, + "start": cint(start), + "page_length": cint(page_length) + }, as_dict=True) + return result + +@frappe.whitelist() +def get_feed_for_dt(doctype, docname): + """get feed""" + result = frappe.db.sql("""select name, owner, modified, creation, + reference_doctype, reference_name, subject + from `tabPatient Medical Record` + where reference_name=%(docname)s and reference_doctype=%(doctype)s + order by creation desc""", + { + "docname": docname, + "doctype": doctype + }, as_dict=True) + + return result diff --git a/erpnext/healthcare/utils.py b/erpnext/healthcare/utils.py index 6a226d9c6b..97bb98f677 100644 --- a/erpnext/healthcare/utils.py +++ b/erpnext/healthcare/utils.py @@ -429,3 +429,116 @@ def get_children(doctype, parent, company, is_root=False): 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]: + return vitals + else: + return False + +@frappe.whitelist() +def render_docs_as_html(docs): + # docs key value pair {doctype: docname} + docs_html = "
" + for doc in docs: + docs_html += render_doc_as_html(doc['doctype'], doc['docname'])['html'] + "
" + 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 = "
" + section_html = "" + section_label = "" + html = "" + sec_on = False + col_on = 0 + has_data = False + for df in meta.fields: + #on section break append append previous section and html to doc html + if df.fieldtype == "Section Break": + if has_data and col_on and sec_on: + doc_html += section_html + html + "
" + elif has_data and not col_on and sec_on: + doc_html += "
" \ + + section_html + html +"
" + while col_on: + doc_html += "
" + col_on -= 1 + sec_on = True + has_data= False + col_on = 0 + section_html = "" + html = "" + if df.label: + section_label = df.label + continue + #on column break append html to section html or doc html + if df.fieldtype == "Column Break": + if sec_on and has_data: + section_html += "
" + section_label + "" + html + "
" + elif has_data: + doc_html += "
" + html + "
" + elif sec_on and not col_on: + section_html += "
" + html = "" + col_on += 1 + if df.label: + html += '
' + 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": + 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 = "" + create_head = True + for item in items: + table_row += '' + for cdf in child_meta.fields: + if cdf.in_list_view: + if create_head: + table_head += '' + cdf.label + '' + if item.get(cdf.fieldname): + table_row += '' + str(item.get(cdf.fieldname)) \ + + '' + else: + table_row += '' + create_head = False + table_row += '' + if sec_on: + section_html += '' + table_head + table_row + '
' + else: + html += '' \ + + table_head + table_row + '
' + continue + #on other field types add label and value to html + if not df.hidden and not df.print_hide and doc.get(df.fieldname) and df.fieldname not in exclude_fields: + html += "
{0} : {1}".format(df.label or df.fieldname, \ + doc.get(df.fieldname)) + if not has_data : has_data = True + if sec_on and col_on and has_data: + doc_html += section_html + html + "
" + elif sec_on and not col_on and has_data: + doc_html += "
" \ + + section_html + html +"
" + if doc_html: + doc_html = "
" %(doctype, docname) + doc_html + "
" + + return {'html': doc_html} diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 571c2dc75b..a29378547e 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -605,6 +605,7 @@ erpnext.patches.v11_1.delete_scheduling_tool erpnext.patches.v12_0.make_custom_fields_for_bank_remittance #14-06-2019 execute:frappe.delete_doc_if_exists("Page", "support-analytics") erpnext.patches.v12_0.make_item_manufacturer +erpnext.patches.v12_0.remove_patient_medical_record_page erpnext.patches.v11_1.move_customer_lead_to_dynamic_column erpnext.patches.v11_1.set_default_action_for_quality_inspection erpnext.patches.v11_1.delete_bom_browser diff --git a/erpnext/patches/v12_0/remove_patient_medical_record_page.py b/erpnext/patches/v12_0/remove_patient_medical_record_page.py new file mode 100644 index 0000000000..904bfe4bf1 --- /dev/null +++ b/erpnext/patches/v12_0/remove_patient_medical_record_page.py @@ -0,0 +1,7 @@ +# Copyright (c) 2019 + +from __future__ import unicode_literals +import frappe + +def execute(): + frappe.delete_doc("Page", "medical_record")