From 0751f15379f17860e2b1113976ad193c611cb97a Mon Sep 17 00:00:00 2001 From: av-dev2 Date: Wed, 20 Mar 2024 18:24:44 +0300 Subject: [PATCH 01/25] chore: add readme content (cherry picked from commit f203741b8ac76354045c450a4595c5a0a8b5d066) --- README.md | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index c79b64a0..094634d7 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,22 @@ -## Hms Tz +## HMS TZ - Healthcare Management System tuned for Tanzania -Hms Tz +HMS Tz is a healthcare management module designed for use within ERPNext. It provides a comprehensive suite of features tailored for healthcare facilities and services. The module includes functionalities for managing laboratory equipment, medication, healthcare providers, financial aspects like insurance claims (specifically NHIF - National Health Insurance Fund), patient care services, and various healthcare data records. HMS Tz is geared towards streamlining operations in healthcare settings, ensuring efficient patient care, and managing financial and administrative tasks effectively. + +Features in ERPNext can be categorized into several key areas: + +1. **Lab and Medication Management**: This includes managing lab machine profiles, messages, and medication change requests. + +2. **Healthcare Provider and Facility Management**: Managing information about healthcare providers, facilities, and practitioner availability. + +3. **Insurance and Financial Management**: This involves managing NHIF claims, tracking changes, setting up company NHIF settings, and handling patient discount requests. + +4. **Patient Care and Services**: This covers healthcare package orders, referral management, and tracking of patient care services. + +5. **Data and Record Management**: Involves managing various healthcare data such as physician qualifications, organ systems, and healthcare referral records. + +6. **NHIF Specific Management**: This includes managing NHIF products, claims, reconciliations, responses, and scheme details. + +This categorization provides a structured overview of the functionalities offered by the HMS Tz module. #### License From ecd51052db9b428f8d9845c082deb6c404e28786 Mon Sep 17 00:00:00 2001 From: av-dev2 Date: Thu, 21 Mar 2024 17:59:34 +0300 Subject: [PATCH 02/25] fix: KeyError on hsic_map[template] when template is not on HSIC (cherry picked from commit ff156b17916c5e3f704403667d372a5d63bd956f) --- hms_tz/nhif/api/patient_encounter.py | 132 +++++++++++++++++---------- 1 file changed, 82 insertions(+), 50 deletions(-) diff --git a/hms_tz/nhif/api/patient_encounter.py b/hms_tz/nhif/api/patient_encounter.py index a06b4f8f..002b65a1 100644 --- a/hms_tz/nhif/api/patient_encounter.py +++ b/hms_tz/nhif/api/patient_encounter.py @@ -400,37 +400,53 @@ def on_submit_validation(doc, method): healthcare_insurance_coverage_plan, ["coverage_plan_name", "is_exclusions"], ) + + validate_item_coverage( + doc, + method, + healthcare_service_templates, + hsic_map, + hicp_name, + is_exclusions, + ) + validate_totals(doc, method) + +def validate_item_coverage( + doc, + method, + healthcare_service_templates, + hsic_map, + hicp_name, + is_exclusions +): for template in healthcare_service_templates: """ If the value of "is_exclusions" is 1, it means the template should not be listed in the "hsic_map". This is because when "is_exclusions" is 1, it indicates that the template which is in "hsic_map" is not covered. - + However, there's an exception to this rule. If the "approval_mandatory_for_claim" for that template is also 1, it means the template is covered but needs extra authorization for approval. - + This apply for NHIF and Non NHIF Insurance """ if is_exclusions: - if template in hsic_map and hsic_map[template].approval_mandatory_for_claim == 0: - for row_item in healthcare_service_templates[template]: - if ( - doc.company - and frappe.get_cached_value( - "Company", - doc.company, - "auto_prescribe_items_on_patient_encounter", - ) - == 1 - ): - row_item.prescribe = 1 - - msg = _( - f"{template}

NOT COVERED

in Healthcare Insurance Coverage Plan {str(hicp_name)} plan.
Patient should pay cash for this service" - ) - msgThrow( - msg, + if ( + template in hsic_map + and hsic_map[template].approval_mandatory_for_claim == 0 + ): + mark_item_not_covered( + doc.company, + hicp_name, + template, + healthcare_service_templates, method, ) + elif ( + template in hsic_map + and hsic_map[template].approval_mandatory_for_claim == 1 + ): + set_is_restricted(template, healthcare_service_templates, hsic_map) + else: """ If the value of "is_exclusions" is 0, it means the template must be listed in the "hsic_map" for it to be covered. @@ -441,40 +457,56 @@ def on_submit_validation(doc, method): This apply for NHIF and Non NHIF Insurance. """ if template not in hsic_map: - for row_item in healthcare_service_templates[template]: - if ( - doc.company - and frappe.get_cached_value( - "Company", - doc.company, - "auto_prescribe_items_on_patient_encounter", - ) - == 1 - ): - row_item.prescribe = 1 - - msg = _( - f"{template}

NOT COVERED

in Healthcare Insurance Coverage Plan {str(hicp_name)} plan.
Patient should pay cash for this service" - ) - msgThrow( - msg, + mark_item_not_covered( + doc.company, + hicp_name, + template, + healthcare_service_templates, method, ) - coverage_info = hsic_map[template] - for row in healthcare_service_templates[template]: - row.is_restricted = coverage_info.approval_mandatory_for_claim - if row.is_restricted: - frappe.msgprint( - _("{0} with template {1} requires additional authorization").format( - _(row.doctype), template - ), - alert=True, - ) - if coverage_info.maximum_number_of_claims == 0: - continue + elif ( + template in hsic_map + and hsic_map[template].approval_mandatory_for_claim == 1 + ): + set_is_restricted(template, healthcare_service_templates, hsic_map) - validate_totals(doc, method) + +def mark_item_not_covered( + company, hicp_name, template, healthcare_service_templates, method +): + for row_item in healthcare_service_templates[template]: + if ( + company + and frappe.get_cached_value( + "Company", + company, + "auto_prescribe_items_on_patient_encounter", + ) + == 1 + ): + row_item.prescribe = 1 + + msg = _( + f"{template}

NOT COVERED

in Healthcare Insurance Coverage Plan {str(hicp_name)} plan.
Patient should pay cash for this service" + ) + msgThrow( + msg, + method, + ) + + +def set_is_restricted(template, healthcare_service_templates, hsic_map): + coverage_info = hsic_map[template] + for row in healthcare_service_templates[template]: + row.is_restricted = coverage_info.approval_mandatory_for_claim + if row.is_restricted: + frappe.msgprint( + _("{0} with template {1} requires additional authorization").format( + _(row.doctype), template + ), + alert=True, + ) def checkـforـduplicate(doc, method): From 5551af57c2b39e5cfe5b21ff0db59acd60458aa1 Mon Sep 17 00:00:00 2001 From: av-dev2 Date: Thu, 4 Apr 2024 00:59:47 +0300 Subject: [PATCH 03/25] feat: Cancel or return therapy sessions via LRPMT Returns doctype (cherry picked from commit db8d8dd182ba5a8f9e2fe81063263f8910f25209) # Conflicts: # hms_tz/hms_tz/doctype/lrpmt_returns/lrpmt_returns.json --- .../doctype/item_return/item_return.json | 4 +- .../doctype/lrpmt_returns/lrpmt_returns.js | 278 +++++- .../doctype/lrpmt_returns/lrpmt_returns.json | 22 +- .../doctype/lrpmt_returns/lrpmt_returns.py | 833 ++++++++++++------ .../medication_return/medication_return.json | 18 +- .../hms_tz/doctype/therapy_return/__init__.py | 0 .../therapy_return/therapy_return.json | 110 +++ .../doctype/therapy_return/therapy_return.py | 8 + .../therapy_session/therapy_session.js | 6 - hms_tz/hooks.py | 9 +- hms_tz/nhif/api/therapy_plan.js | 24 +- hms_tz/nhif/api/therapy_plan.py | 32 +- hms_tz/nhif/api/therapy_session.js | 27 + hms_tz/nhif/api/therapy_session.py | 18 + hms_tz/patches.txt | 2 + ...led_custom_field_on_therapy_plan_detail.py | 26 + .../status_options_for_therapy_plan.py | 14 + 17 files changed, 1096 insertions(+), 335 deletions(-) create mode 100644 hms_tz/hms_tz/doctype/therapy_return/__init__.py create mode 100644 hms_tz/hms_tz/doctype/therapy_return/therapy_return.json create mode 100644 hms_tz/hms_tz/doctype/therapy_return/therapy_return.py create mode 100644 hms_tz/patches/custom_fields/sessions_cancelled_custom_field_on_therapy_plan_detail.py create mode 100644 hms_tz/patches/property_setter/status_options_for_therapy_plan.py diff --git a/hms_tz/hms_tz/doctype/item_return/item_return.json b/hms_tz/hms_tz/doctype/item_return/item_return.json index 2f7f815d..2f2cc9c7 100644 --- a/hms_tz/hms_tz/doctype/item_return/item_return.json +++ b/hms_tz/hms_tz/doctype/item_return/item_return.json @@ -28,7 +28,6 @@ "default": "1", "fieldname": "quantity", "fieldtype": "Int", - "in_list_view": 1, "label": "Quantity", "non_negative": 1, "read_only": 1 @@ -76,7 +75,7 @@ "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2022-01-10 11:51:17.676628", + "modified": "2024-04-02 22:51:39.983491", "modified_by": "Administrator", "module": "Hms Tz", "name": "Item Return", @@ -84,5 +83,6 @@ "permissions": [], "sort_field": "modified", "sort_order": "DESC", + "states": [], "track_changes": 1 } \ No newline at end of file diff --git a/hms_tz/hms_tz/doctype/lrpmt_returns/lrpmt_returns.js b/hms_tz/hms_tz/doctype/lrpmt_returns/lrpmt_returns.js index 92dcae91..276a87f6 100644 --- a/hms_tz/hms_tz/doctype/lrpmt_returns/lrpmt_returns.js +++ b/hms_tz/hms_tz/doctype/lrpmt_returns/lrpmt_returns.js @@ -2,16 +2,21 @@ // For license information, please see license.txt frappe.ui.form.on('LRPMT Returns', { - setup: function(frm) { - var requested_by = frappe.user.full_name() - frm.set_value("requested_by", requested_by) + setup: function (frm) { + var requested_by = frappe.user.full_name(); + frm.set_value("requested_by", requested_by); }, - after_save: function(frm) { - frm.reload_doc() + after_save: function (frm) { + frm.reload_doc(); }, - refresh: function(frm) { + refresh: function (frm) { + frm.get_field("lrpt_items").grid.cannot_add_rows = true; + frm.get_field("therapy_items").grid.cannot_add_rows = true; + frm.get_field("drug_items").grid.cannot_add_rows = true; + frm.get_field("sales_items").grid.cannot_add_rows = true; + frm.set_query('appointment', () => { return { filters: { @@ -19,22 +24,31 @@ frappe.ui.form.on('LRPMT Returns', { } } }); - + if (frm.doc.patient && frm.doc.appointment) { - frm.add_custom_button(__("Get LRPT Items"), function(){ - frm.trigger("get_lrpt_items") + frm.add_custom_button(__("Get LRP Items"), function () { + frm.trigger("get_lrp_items"); + }) + + frm.add_custom_button(__("Get Therapy Items"), function () { + frm.trigger("get_therapy_items"); }) - frm.add_custom_button(__("Get Drug Items"), function(){ - frm.trigger("get_drug_items") + frm.add_custom_button(__("Get Drug Items"), function () { + frm.trigger("get_drug_items"); }) } }, - - onload: function(frm){ - frm.validate_quantity_to_return = function(frm, row) { + + onload: function (frm) { + frm.get_field("lrpt_items").grid.cannot_add_rows = true; + frm.get_field("therapy_items").grid.cannot_add_rows = true; + frm.get_field("drug_items").grid.cannot_add_rows = true; + frm.get_field("sales_items").grid.cannot_add_rows = true; + + frm.validate_quantity_to_return = function (frm, row) { frm.doc.drug_items.forEach(data => { - if (data.quantity_to_return > data.quantity_prescribed) { + if (data.quantity_to_return > (data.quantity_prescribed - data.qty_returned)) { row.quantity_to_return = 0; frappe.msgprint({ title: __('Message'), @@ -48,15 +62,31 @@ frappe.ui.form.on('LRPMT Returns', { } }) } + frm.validate_no_of_sessions_to_cancel = function (frm, row) { + frm.doc.therapy_items.forEach(data => { + if (data.sessions_to_cancel > (data.sessions_prescribed - data.sessions_cancelled)) { + row.sessions_to_cancel = 0; + frappe.msgprint({ + title: __('Message'), + indicator: 'yellow', + message: __( + '

\ + No of Sessions to Cancel can not be greater than No of Sessions Prescribed

' + ) + }); + frm.refresh_field("therapy_items") + } + }) + } }, - before_submit: function(frm) { + before_submit: function (frm) { frm.set_value("approved_by", frappe.user.full_name()) }, - get_lrpt_items: function (frm) { + get_lrp_items: function (frm) { let d = new frappe.ui.Dialog({ - title: "Select LRPT Items", + title: "Select LRP Items", fields: [ { fieldname: "result_area", @@ -71,17 +101,19 @@ frappe.ui.form.on('LRPMT Returns', { var columns = (["item_name", "encounter_no", "reference_docname", "reference_doctype", "status"]) frappe.call({ - method: "hms_tz.hms_tz.doctype.lrpmt_returns.lrpmt_returns.get_lrpt_item_list", + method: "hms_tz.hms_tz.doctype.lrpmt_returns.lrpmt_returns.get_lrp_item_list", args: { patient: frm.doc.patient, appointment: frm.doc.appointment, company: frm.doc.company }, + freeze: true, + freeze_message: __(''), callback: (data) => { if (data.message.length > 0) { - $results.append(make_lrpt_list_row(columns, true)); + $results.append(make_lrp_list_row(columns, true)); for (let i = 0; i < data.message.length; i++) { - $results.append(make_lrpt_list_row(columns, true, data.message[i])); + $results.append(make_lrp_list_row(columns, true, data.message[i])); } } else { $results.append($placeholder); @@ -95,19 +127,77 @@ frappe.ui.form.on('LRPMT Returns', { $placeholder = $(`
-

No LRPT Items found

+

No LRP Items found

`); $results.on('click', '.list-item--head :checkbox', (e) => { $results.find('.list-item-container .list-row-check') .prop("checked", ($(e.target).is(':checked'))); }); - set_lrpt_primary_action(frm, d, $results, true); + set_lrp_primary_action(frm, d, $results, true); d.$wrapper.find('.modal-content').css('width', '900px'); d.show(); }, + get_therapy_items: (frm) => { + let d = new frappe.ui.Dialog({ + title: "Select Therapy Items", + fields: [ + { + fieldname: "html_space", + fieldtype: "HTML" + } + ] + }); + var $wrapper; + var $results; + var $placeholder; + if (frm.doc.patient, frm.doc.appointment, frm.doc.company) { + let columns = (["therapy_type", "sessions", "encounter_no", "therapy_plan", "therapy_session", 'status']); + + frappe.call({ + method: "hms_tz.hms_tz.doctype.lrpmt_returns.lrpmt_returns.get_therapies", + args: { + patient: frm.doc.patient, + appointment: frm.doc.appointment, + company: frm.doc.company + }, + freeze: true, + freeze_message: __(''), + callback: (r) => { + if (r.message.length > 0) { + var data = r.message; + $results.append(make_therapy_list_row(columns, true)); + for (let i = 0; i < data.length; i++) { + $results.append(make_therapy_list_row(columns, true, data[i])); + } + } + else { + $results.append($placeholder) + } + } + }) + }; + $wrapper = d.fields_dict.html_space.$wrapper.append(`
`); + + + $results = $wrapper.find('.results'); + $placeholder = $(`
+ + +

No Therapy Items found

+
+
`); + $results.on('click', '.list-item--head :checkbox', (e) => { + $results.find('.list-item-container .list-row-check') + .prop("checked", ($(e.target).is(':checked'))); + }); + set_therapy_primary_action(frm, d, $results, true); + d.$wrapper.find('.modal-content').css('width', '950px'); + d.show(); + }, - get_drug_items: function (frm){ + get_drug_items: function (frm) { let d = new frappe.ui.Dialog({ title: "Select Drug Items", fields: [ @@ -121,7 +211,7 @@ frappe.ui.form.on('LRPMT Returns', { var $results; var $placeholder; if (frm.doc.patient, frm.doc.appointment, frm.doc.company) { - var columns = (["item_name", "quantity", "encounter_no", "delivery_note", 'status']) + var columns = (["item_name", "qty_prescribed", "qty_returned", "encounter_no", "delivery_note", 'status']) frappe.call({ method: "hms_tz.hms_tz.doctype.lrpmt_returns.lrpmt_returns.get_drug_item_list", args: { @@ -129,11 +219,13 @@ frappe.ui.form.on('LRPMT Returns', { appointment: frm.doc.appointment, company: frm.doc.company }, + freeze: true, + freeze_message: __(''), callback: (r) => { if (r.message.length > 0) { var data = r.message; $results.append(make_drug_list_row(columns, true)); - for (let i = 0; i < data.length; i++){ + for (let i = 0; i < data.length; i++) { $results.append(make_drug_list_row(columns, true, data[i])); } } @@ -162,7 +254,7 @@ frappe.ui.form.on('LRPMT Returns', { }, }); -var make_lrpt_list_row = function (columns, item, result = {}) { +var make_lrp_list_row = function (columns, item, result = {}) { var me = this; // Make a head row by default (if result not passed) let head = Object.keys(result).length === 0; @@ -184,11 +276,11 @@ var make_lrpt_list_row = function (columns, item, result = {}) { ${contents} `); - $row = list_row_lrpt_items(head, $row, result, item); + $row = list_row_lrp_items(head, $row, result, item); return $row; }; -var list_row_lrpt_items = function (head, $row, result, item) { +var list_row_lrp_items = function (head, $row, result, item) { if (item) { head ? $row.addClass('list-item--head') : $row = $(`
0) { - add_to_lrpt_line(frm, checked_items, item); + add_to_lrp_line(frm, checked_items, item); d.hide(); } }); }; -var get_checked_lrpt_items = function ($results) { +var get_checked_lrp_items = function ($results) { return $results.find('.list-item-container').map(function () { let checked_items = {}; if ($(this).find(".list-row-check:checkbox:checked").length > 0) { @@ -240,10 +332,10 @@ var get_checked_lrpt_items = function ($results) { }).get(); }; -var add_to_lrpt_line = function (frm, checked_items, item) { +var add_to_lrp_line = function (frm, checked_items, item) { if (item) { frappe.call({ - method: "hms_tz.hms_tz.doctype.lrpmt_returns.lrpmt_returns.set_checked_lrpt_items", + method: "hms_tz.hms_tz.doctype.lrpmt_returns.lrpmt_returns.set_checked_lrp_items", args: { doc: frm.doc, checked_items: checked_items @@ -267,7 +359,8 @@ var make_drug_list_row = function (columns, item, result = {}) { let head = Object.keys(result).length === 0; let contents = ``; columns.forEach(function (column) { - contents += `
+ let contentWidth = column === "qty_prescribed" || column === "qty_returned" || column === "status" ? 100 : 160; // Set width based on column name + contents += `
${head ? `${__(frappe.model.unscrub(column))}` : (column !== "name" ? `${__(result[column])}` @@ -293,7 +386,8 @@ var list_row_drug_items = function (head, $row, result, item) { : $row = $(`
0) { checked_items["child_name"] = $(this).attr("data-child_name"); checked_items["item_name"] = $(this).attr("data-item_name"); - checked_items["quantity_prescribed"] = $(this).attr("data-quantity"); + checked_items["qty_prescribed"] = $(this).attr("data-qty_prescribed"); + checked_items["qty_returned"] = $(this).attr("data-qty_returned"); checked_items["encounter_no"] = $(this).attr("data-encounter_no"); checked_items["delivery_note"] = $(this).attr("data-delivery_note"); checked_items["dn_detail"] = $(this).attr("data-dn_detail"); @@ -351,9 +446,112 @@ var add_to_drug_line = function (frm, checked_items, item) { } }; +var make_therapy_list_row = function (columns, item, result = {}) { + var me = this; + // Make a head row by default (if result not passed) + let head = Object.keys(result).length === 0; + let contents = ``; + columns.forEach(function (column) { + let contentWidth = column === "status" || column === "sessions" ? 65 : 150; // Set width based on column name + contents += `
+ ${head ? `${__(frappe.model.unscrub(column))}` + : (column !== "name" ? `${__(result[column])}` + : `${__(result[column])}`) + } +
`; + }); + + let $row = $(`
+
+ +
+ ${contents} +
`); + + $row = list_row_therapy_items(head, $row, result, item); + return $row; +}; + +var list_row_therapy_items = function (head, $row, result, item) { + if (item) { + head ? $row.addClass('list-item--head') + : $row = $(`
`).append($row); + } + return $row; +}; + +var set_therapy_primary_action = function (frm, d, $results, item) { + var me = this; + d.set_primary_action(__('Add'), function () { + let checked_items = get_checked_therapy_items($results); + if (checked_items.length > 0) { + add_to_therapy_line(frm, checked_items, item); + d.hide(); + } + }); +}; + +var get_checked_therapy_items = function ($results) { + return $results.find('.list-item-container').map(function () { + let checked_items = {}; + if ($(this).find(".list-row-check:checkbox:checked").length > 0) { + checked_items["status"] = $(this).attr("data-status"); + checked_items["therapy_type"] = $(this).attr("data-therapy_type"); + checked_items["encounter_no"] = $(this).attr("data-encounter_no"); + checked_items["therapy_plan"] = $(this).attr("data-therapy_plan"); + checked_items["sessions_prescribed"] = $(this).attr("data-sessions"); + checked_items["therapy_session"] = $(this).attr("data-therapy_session"); + checked_items["sessions_cancelled"] = $(this).attr("data-sessions_cancelled"); + checked_items["plan_child_table_id"] = $(this).attr("data-plan_child_table_id"); + checked_items["encounter_child_table_id"] = $(this).attr("data-encounter_child_table_id"); + return checked_items; + } + }).get(); +}; + +var add_to_therapy_line = function (frm, checked_items, item) { + if (item) { + frappe.call({ + method: "hms_tz.hms_tz.doctype.lrpmt_returns.lrpmt_returns.set_checked_therapy_items", + args: { + doc: frm.doc, + checked_items: checked_items + }, + callback: function (r) { + frm.trigger("validate"); + frm.refresh_fields(); + if (frm.is_new()) { + frappe.set_route("Form", "LRPMT Returns", r.message); + } else { + frm.reload_doc(); + } + } + }); + } +}; + frappe.ui.form.on("Medication Return", { - quantity_to_return: function(frm, cdt, cdn) { + quantity_to_return: function (frm, cdt, cdn) { let row = locals[cdt][cdn]; frm.validate_quantity_to_return(frm, row) } -}) \ No newline at end of file +}) + + +frappe.ui.form.on("Therapy Return", { + sessions_to_cancel: function (frm, cdt, cdn) { + let row = locals[cdt][cdn]; + frm.validate_no_of_sessions_to_cancel(frm, row) + } +}) diff --git a/hms_tz/hms_tz/doctype/lrpmt_returns/lrpmt_returns.json b/hms_tz/hms_tz/doctype/lrpmt_returns/lrpmt_returns.json index 0dfae2d2..3d18442e 100644 --- a/hms_tz/hms_tz/doctype/lrpmt_returns/lrpmt_returns.json +++ b/hms_tz/hms_tz/doctype/lrpmt_returns/lrpmt_returns.json @@ -17,6 +17,8 @@ "admitted_datetime", "sec_break_1", "lrpt_items", + "therapy_sec", + "therapy_items", "sec_break_2", "drug_items", "sec_break_3", @@ -71,7 +73,7 @@ "fetch_from": "inpatient_record.status", "fieldname": "status", "fieldtype": "Data", - "label": "Status", + "label": "Inpatient Status", "read_only": 1 }, { @@ -87,7 +89,7 @@ "depends_on": "eval: doc.appointment", "fieldname": "sec_break_1", "fieldtype": "Section Break", - "label": "LRPT Items" + "label": "LRP Items" }, { "fieldname": "naming_series", @@ -163,12 +165,28 @@ "in_list_view": 1, "label": "Patient Appointment", "options": "Patient Appointment" + }, + { + "fieldname": "therapy_sec", + "fieldtype": "Section Break", + "label": "Therapy Items" + }, + { + "depends_on": "eval: doc.appointment", + "fieldname": "therapy_items", + "fieldtype": "Table", + "label": "items", + "options": "Therapy Return" } ], "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], +<<<<<<< HEAD "modified": "2022-04-29 18:22:45.789471", +======= + "modified": "2024-04-02 22:41:54.900854", +>>>>>>> db8d8dd1 (feat: Cancel or return therapy sessions via LRPMT Returns doctype) "modified_by": "Administrator", "module": "Hms Tz", "name": "LRPMT Returns", diff --git a/hms_tz/hms_tz/doctype/lrpmt_returns/lrpmt_returns.py b/hms_tz/hms_tz/doctype/lrpmt_returns/lrpmt_returns.py index 7577fb9f..8e730f4a 100644 --- a/hms_tz/hms_tz/doctype/lrpmt_returns/lrpmt_returns.py +++ b/hms_tz/hms_tz/doctype/lrpmt_returns/lrpmt_returns.py @@ -1,23 +1,34 @@ # Copyright (c) 2021, Aakvatech and contributors # For license information, please see license.txt -import frappe import json +import frappe +import itertools from frappe import bold, _ from frappe.model.document import Document from frappe.model.workflow import apply_workflow -from frappe.utils import nowdate, nowtime, flt, unique, get_fullname, get_url_to_form +from frappe.utils import ( + nowdate, + nowtime, + flt, + cint, + unique, + get_fullname, + get_url_to_form, +) from hms_tz.nhif.api.healthcare_utils import validate_nhif_patient_claim_status class LRPMTReturns(Document): def validate(self): set_missing_values(self) + combine_therapy_info(self.therapy_items) def before_insert(self): validate_nhif_patient_claim_status( "LRPMT Return", self.company, self.appointment ) + self.validate_duplicates() def before_submit(self): if not self.approved_by: @@ -27,31 +38,95 @@ def before_submit(self): "LRPMT Return", self.company, self.appointment ) - validate_reason(self) - validate_drug_row(self) - cancel_lrpt_doc(self) - return_or_cancel_drug_item(self) + self.validate_reason() + self.validate_drug_row() + self.cancel_lrp_doc() + self.cancel_therapy_doc() + self.return_or_cancel_drugs() def on_submit(self): - get_sales_return(self) - - -def cancel_lrpt_doc(self): - prescription_lrpmt = { - "Lab Test": "Lab Prescription", - "Radiology Examination": "Radiology Procedure Prescription", - "Clinical Procedure": "Procedure Prescription", - } - for item in self.lrpt_items: - if item.reference_doctype == "Therapy Plan": - cancel_tharapy_plan_doc(self.patient, item.reference_docname) - frappe.db.set_value( - "Therapy Plan Detail", item.child_name, "is_cancelled", 1 + self.get_sales_return() + + def validate_reason(self): + def check_reason(item_table): + msg = "" + for entry in item_table: + if not entry.reason: + msg += f"Reason Field is Empty for Item name: {bold(entry.get('item_name') or entry.get('therapy_type'))},\ + Row: #{bold(entry.idx)}, please fill it to proceed
" + + if msg: + frappe.throw( + title="Notification", + msg=msg, + exc="Frappe.ValidationError", + ) + + if len(self.lrpt_items) > 0: + check_reason(self.lrpt_items) + + if len(self.therapy_items) > 0: + check_reason(self.therapy_items) + + def validate_drug_row(self): + if len(self.drug_items) > 0: + msg = "" + for row in self.drug_items: + msg_print = "" + if row.quantity_to_return == 0: + msg_print += f"Quantity to return can not be Zero for drug name: {bold(row.drug_name)},\ + Row: #{bold(row.idx)}:
" + + if not row.reason: + msg_print += "Reason for Return Field can not be Empty for drug name: {bold(row.drug_name)},\ + Row: #{bold(row.idx)}:
" + + if not row.drug_condition: + msg_print += "Drug Condition Field can not Empty for drug name: {bold(row.drug_name)},\ + Row: #{bold(row.idx)}:
" + + if not msg_print: + continue + + msg += msg_print + "
" + + if msg: + frappe.throw( + title="Notification", msg=msg, exc="Frappe.ValidationError" + ) + + def validate_duplicates(self): + dt = frappe.qb.DocType("LRPMT Returns") + lrpmts = ( + frappe.qb.from_(dt) + .select(dt.name.as_("lrpmt_docname")) + .where( + (dt.docstatus == 0) + & (dt.name != self.name) + & (dt.appointment == self.appointment) + ) + ).run(as_dict=True) + if len(lrpmts) > 0: + frappe.throw( + title="Duplicate Error", + exc=frappe.ValidationError, + msg="draft LRPMT Returns for this appointment: {bold(self.appointment)} already exists,\ + Please visit {lrpmts[0].lrpmt_docname)} to continue", ) - else: + + def cancel_lrp_doc(self): + if len(self.lrpt_items) == 0: + return + + prescription_lrp = { + "Lab Test": "Lab Prescription", + "Radiology Examination": "Radiology Procedure Prescription", + "Clinical Procedure": "Procedure Prescription", + } + for item in self.lrpt_items: if not item.reference_docname: frappe.db.set_value( - prescription_lrpmt[item.reference_doctype], + prescription_lrp[item.reference_doctype], item.child_name, "is_cancelled", 1, @@ -73,7 +148,7 @@ def cancel_lrpt_doc(self): or doc.workflow_state == "Submitted but Not Serviced" ): frappe.db.set_value( - prescription_lrpmt[item.reference_doctype], + prescription_lrp[item.reference_doctype], item.child_name, "is_cancelled", 1, @@ -81,86 +156,276 @@ def cancel_lrpt_doc(self): except Exception: frappe.log_error(frappe.get_traceback(), str(self.doctype)) frappe.throw( - "There was an error while cancelling the Item: {0} of ReferenceDoctype: {1}, ReferenceName: {2},
\ - Check error log for review".format( - frappe.bold(item.item_name), - frappe.bold(item.reference_doctype), - frappe.bold(item.reference_docname), - ) + f"There was an error while cancelling the Item: {bold(item.item_name)} of ReferenceDoctype: {bold(item.reference_doctype)},\ + ReferenceName: {bold(item.reference_docname)},
Check error log for review" ) + def cancel_therapy_doc(self): + if len(self.therapy_items) == 0: + return -def cancel_tharapy_plan_doc(patient, therapy_plan_id): - therapy_sessions = frappe.get_all( - "Therapy Session", - filters={"patient": patient, "therapy_plan": therapy_plan_id}, - fields=["name"], - pluck="name", - ) + therapy_details = combine_therapy_info(self.therapy_items) - if therapy_sessions: - for session in therapy_sessions: - therapy_session_doc = frappe.get_doc("Therapy Session", session) + for item in therapy_details: + total_sessions_prescribed = ( + frappe.get_cached_value( + "Therapy Plan Detail", + item.get("encounter_child_table_id"), + "no_of_sessions", + ) + or 0 + ) + is_cancelled = 0 + total_sessions_cancelled = item.get("sessions_to_cancel") + item.get( + "sessions_cancelled" + ) + delivered_quantity = total_sessions_prescribed - total_sessions_cancelled + if total_sessions_prescribed <= total_sessions_cancelled: + is_cancelled = 1 - if therapy_session_doc.docstatus == 1: - therapy_session_doc.cancel() + if not item.get("therapy_plan"): + frappe.db.set_value( + "Therapy Plan Detail", + item.get("encounter_child_table_id"), + { + "is_cancelled": is_cancelled, + "delivered_quantity": delivered_quantity, + "sessions_cancelled": total_sessions_cancelled, + }, + ) - therapy_plan_doc = frappe.get_doc("Therapy Plan", therapy_plan_id) + elif item.get("therapy_plan") and len(item.get("therapy_session_ids")) == 0: + update_therapy_plan( + self, + item, + is_cancelled, + delivered_quantity, + total_sessions_cancelled, + ) - for entry in therapy_plan_doc.therapy_plan_details: - if entry.no_of_sessions: - entry.no_of_sessions = 0 - if entry.sessions_completed: - entry.sessions_completed = 0 - therapy_plan_doc.total_sessions = 0 - therapy_plan_doc.total_sessions_completed = 0 - therapy_plan_doc.status = "Not Serviced" - therapy_plan_doc.save(ignore_permissions=True) - therapy_plan_doc.reload() - return therapy_plan_doc.name + elif item.get("therapy_plan") and len(item.get("therapy_session_ids")) > 0: + update_therapy_session( + self, + item, + is_cancelled, + delivered_quantity, + total_sessions_cancelled, + ) + def return_or_cancel_drugs(self): + if len(self.drug_items) == 0: + return -def return_or_cancel_drug_item(self): - if len(self.drug_items) == 0: - return + update_drug_prescription_for_uncreated_delivery_note(self) - update_drug_prescription_for_uncreated_delivery_note(self) + unique_draft_delivery_notes = get_unique_delivery_notes(self, "Draft") + if unique_draft_delivery_notes: + for draft_delivery_note in unique_draft_delivery_notes: + update_drug_description_for_draft_delivery_note( + self, draft_delivery_note + ) - unique_draft_delivery_notes = get_unique_delivery_notes(self, "Draft") - if unique_draft_delivery_notes: - for draft_delivery_note in unique_draft_delivery_notes: - update_drug_description_for_draft_delivery_note(self, draft_delivery_note) + unique_submitted_delivery_notes = get_unique_delivery_notes(self, "Submitted") + if unique_submitted_delivery_notes: + for dn in unique_submitted_delivery_notes: + try: + source_doc = frappe.get_doc("Delivery Note", dn) + target_doc = return_drug_quantity_to_stock(self, source_doc) - unique_submitted_delivery_notes = get_unique_delivery_notes(self, "Submitted") - if unique_submitted_delivery_notes: - for dn in unique_submitted_delivery_notes: - try: - source_doc = frappe.get_doc("Delivery Note", dn) - target_doc = return_drug_quantity_to_stock(self, source_doc) + if target_doc.get("name"): + transition_workflow_states(source_doc, target_doc) + + except Exception: + frappe.log_error( + frappe.get_traceback(), + str(f"Error in creating return delivery note for {dn}"), + ) + frappe.throw( + f"Error in creating return delivery note against delivery note: {bold(dn)}\ +
Check error log for review" + ) + + return self.name + + def get_sales_return(self): + conditions = { + "patient": self.patient, + "company": self.company, + "reference_name": self.name, + "is_return": 1, + "docstatus": 1, + } + + returned_delivery_note_nos = frappe.get_all( + "Delivery Note", filters=conditions, fields=["name"], pluck="name" + ) - if target_doc.get("name"): - transition_workflow_states(source_doc, target_doc) + if returned_delivery_note_nos: + for dn in returned_delivery_note_nos: + sales_doc = frappe.get_doc("Delivery Note", dn) + + for item in sales_doc.items: + for dd_n in self.drug_items: + if item.item_code == dd_n.drug_name: + self.append( + "sales_items", + { + "drug_name": item.item_code, + "quantity_prescribed": dd_n.quantity_prescribed, + "quantity_returned": item.qty - dd_n.qty_returned, + "quantity_serviced": flt( + dd_n.quantity_prescribed + item.qty - dd_n.qty_returned + ), + "delivery_note_no": dn, + "dn_detail": item.name, + "warehouse": item.warehouse, + "reference_doctype": item.reference_doctype, + "reference_name": item.reference_name, + }, + ) + self.save(ignore_permissions=True) + self.reload() + + return self.name + +def combine_therapy_info(therapy_items): + therapy_detail_map = [] + + for key, group in itertools.groupby( + therapy_items, + lambda x: { + "therapy_type": x.therapy_type, + "encounter_no": x.encounter_no, + "therapy_plan": x.therapy_plan, + "plan_child_table_id": x.plan_child_table_id, + "encounter_child_table_id": x.encounter_child_table_id, + }, + ): + sessions_prescribed = 0 + sessions_to_cancel = 0 + sessions_cancelled = 0 + therapy_session_ids = [] + for item in list(group): + sessions_prescribed += cint(item.sessions_prescribed) + sessions_to_cancel += cint(item.sessions_to_cancel) + sessions_cancelled += cint(item.sessions_cancelled) + if item.therapy_session: + therapy_session_ids.append(item.therapy_session) + + key.update( + { + "sessions_prescribed": sessions_prescribed, + "sessions_to_cancel": sessions_to_cancel, + "sessions_cancelled": sessions_cancelled, + "therapy_session_ids": therapy_session_ids, + } + ) + therapy_detail_map.append(key) + + return therapy_detail_map + + +def update_therapy_plan( + self, + row, + is_cancelled, + delivered_quantity, + total_sessions_cancelled, + session_docstatus=0, +): + plan_doc = frappe.get_cached_doc("Therapy Plan", row.get("therapy_plan")) + try: + for d in plan_doc.therapy_plan_details: + if d.name == row.get("plan_child_table_id") and d.therapy_type == row.get( + "therapy_type" + ): + d.sessions_cancelled += row.get("sessions_to_cancel") + d.no_of_sessions -= row.get("sessions_to_cancel") + if session_docstatus == 1: + d.sessions_completed -= 1 + + plan_doc.save(ignore_permissions=True) + plan_doc.add_comment( + text=f"LRPMT Returns: {bold(self.name)} is submitted" + ) + + frappe.db.set_value( + "Therapy Plan Detail", + row.get("encounter_child_table_id"), + { + "is_cancelled": is_cancelled, + "delivered_quantity": delivered_quantity, + "sessions_cancelled": total_sessions_cancelled, + }, + ) + except Exception: + frappe.log_error( + frappe.get_traceback(), + str(f"{self.doctype}/{plan_doc.doctype}"), + ) + frappe.throw( + f"There was an error while cancelling the Therapy Plan: {bold(plan_doc.name)}\ +
Check error log for review" + ) + + +def update_therapy_session( + self, + item, + is_cancelled, + delivered_quantity, + total_sessions_cancelled, +): + for session_id in item.get("therapy_session_ids"): + session_doc = frappe.get_doc("Therapy Session", session_id) + if session_doc.docstatus < 2: + try: + apply_workflow(session_doc, "Not Serviced") + session_doc.save(ignore_permissions=True) + session_doc.add_comment( + text=f"LRPMT Return: {bold(self.name)} is submitted" + ) + session_doc.reload() + + if session_doc.workflow_state in [ + "Not Serviced", + "Submitted but Not Serviced", + ]: + update_therapy_plan( + self, + item, + is_cancelled, + delivered_quantity, + total_sessions_cancelled, + session_doc.docstatus, + ) except Exception: frappe.log_error( frappe.get_traceback(), - str("Error in creating return delivery note for {0}".format(dn)), + str(f"{self.doctype}/{session_doc.doctype}"), ) frappe.throw( - "Error in creating return delivery note against delivery note: {0}".format( - frappe.bold(dn) - ) + f"There was an error while cancelling the Therapy Session: {bold(session_doc.name)}\ +
Check error log for review" ) - return self.name - def update_drug_prescription_for_uncreated_delivery_note(self): for item in self.drug_items: if item.child_name and not ( item.dn_detail and item.delivery_note_no and item.status ): - frappe.db.set_value("Drug Prescription", item.child_name, "is_cancelled", 1) + frappe.db.set_value( + "Drug Prescription", + item.child_name, + { + "is_cancelled": 1, + "quantity_returned": item.quantity_to_return + item.qty_returned, + "delivered_quantity": item.quantity_prescribed + - (item.quantity_to_return + item.qty_returned), + }, + ) def update_drug_description_for_draft_delivery_note(self, delivey_note): @@ -178,24 +443,21 @@ def update_drug_description_for_draft_delivery_note(self, delivey_note): item.child_name, { "is_cancelled": 1, - "quantity_returned": item.quantity_to_return, + "quantity_returned": item.quantity_to_return + + item.qty_returned, + "delivered_quantity": item.quantity_prescribed + - (item.quantity_to_return + item.qty_returned), }, ) except Exception: frappe.log_error( frappe.get_traceback(), - str( - "Apply workflow error for Delivery Note {0}".format( - frappe.bold(delivey_note) - ) - ), + str(f"Apply workflow error for Delivery Note {bold(delivey_note)}"), ) frappe.throw( str( - "Apply workflow error, for delivery note: {0}, check error log for more details".format( - frappe.bold(delivey_note) - ) + f"Apply workflow error, for delivery note: {bold(delivey_note)}, check error log for more details" ) ) @@ -203,17 +465,17 @@ def update_drug_description_for_draft_delivery_note(self, delivey_note): def update_drug_prescription_for_submitted_delivery_note(item): - if (item.quantity_prescribed - item.quantity_to_return) == 0: + item_cancelled = 0 + if item.quantity_prescribed <= (item.quantity_to_return + item.qty_returned): item_cancelled = 1 - else: - item_cancelled = 0 frappe.db.set_value( "Drug Prescription", item.child_name, { - "quantity_returned": item.quantity_to_return, - "delivered_quantity": item.quantity_prescribed - item.quantity_to_return, + "quantity_returned": item.quantity_to_return + item.qty_returned, + "delivered_quantity": item.quantity_prescribed + - (item.quantity_to_return + item.qty_returned), "is_cancelled": item_cancelled, }, ) @@ -298,119 +560,29 @@ def transition_workflow_states(source_doc, target_doc): frappe.log_error( frappe.get_traceback(), str( - "Apply workflow error for Delivery Note {0}".format( - frappe.bold(source_doc.name) - ) + f"Apply workflow error for Delivery Note {bold(source_doc.name)}" ), ) except Exception: frappe.log_error( frappe.get_traceback(), - str( - "Apply workflow error for Delivery Note {0}".format( - frappe.bold(target_doc.name) - ) - ), + str(f"Apply workflow error for Delivery Note {bold(target_doc.name)}"), ) frappe.throw( str( - "Apply workflow error, for delivery note: {0}, check error log for more details".format( - frappe.bold(target_doc.name) - ) + f"Apply workflow error, for delivery note: {bold(target_doc.name)}, check error log for more details" ) ) -def get_sales_return(self): - conditions = { - "patient": self.patient, - "company": self.company, - "reference_name": self.name, - "is_return": 1, - } - - returned_delivery_note_nos = frappe.get_all( - "Delivery Note", filters=conditions, fields=["name"], pluck="name" - ) - - if returned_delivery_note_nos: - doc = frappe.get_doc("LRPMT Returns", self.name) - - for dn in returned_delivery_note_nos: - sales_doc = frappe.get_doc("Delivery Note", dn) - - for item in sales_doc.items: - for dd_n in doc.drug_items: - if item.item_code == dd_n.drug_name: - doc.append( - "sales_items", - { - "drug_name": item.item_code, - "quantity_prescribed": dd_n.quantity_prescribed, - "quantity_returned": item.qty, - "quantity_serviced": flt( - dd_n.quantity_prescribed + item.qty - ), - "delivery_note_no": dn, - "dn_detail": item.name, - "warehouse": item.warehouse, - "reference_doctype": item.reference_doctype, - "reference_name": item.reference_name, - }, - ) - doc.save(ignore_permissions=True) - doc.reload() - - return self.name - - -def validate_reason(self): - if self.lrpt_items: - for entry in self.lrpt_items: - if not entry.reason: - msg = "Reason Field is Empty for Item name: {0}, Row: #{1}, please fill it to proceed" - frappe.throw( - title="Notification", - msg=msg.format(bold(entry.item_name), bold(entry.idx)), - exc="Frappe.ValidationError", - ) - - -def validate_drug_row(self): - if self.drug_items: - msg = "" - for row in self.drug_items: - msg_print = "" - if row.quantity_to_return == 0: - msg_print += "Quantity to return can not be Zero for drug name: {0},\ - Row: #{1}:
".format( - bold(row.drug_name), bold(row.idx) - ) - if not row.reason: - msg_print += "Reason for Return Field can not be Empty for drug name: {0},\ - Row: #{1}:
".format( - bold(row.drug_name), bold(row.idx) - ) - if not row.drug_condition: - msg_print += "Drug Condition Field can not Empty for drug name: {0},\ - Row: #{1}:
".format( - bold(row.drug_name), bold(row.idx) - ) - - msg = msg_print - - if msg: - frappe.throw(title="Notification", msg=msg, exc="Frappe.ValidationError") - - def get_unique_delivery_notes(self, status): return unique([d.delivery_note_no for d in self.drug_items if d.status == status]) @frappe.whitelist() -def get_lrpt_item_list(patient, appointment, company): +def get_lrp_item_list(patient, appointment, company): item_list = [] - child_list = get_lrpt_map() + child_list = get_lrp_map() encounter_list = get_patient_encounters(patient, appointment, company) @@ -498,21 +670,10 @@ def get_lrpt_item_list(patient, appointment, company): } ) - # if item.therapy_type: - # therapy_plan = frappe.get_value("Patient Encounter", item.parent, "therapy_plan") - # item_list.append({ - # "child_name": item.name, - # "item_name": item.therapy_type, - # "quantity": 1, - # "encounter_no": item.parent, - # "reference_doctype": "Therapy Plan", - # "reference_docname": therapy_plan - # }) - return item_list -def get_lrpt_map(): +def get_lrp_map(): child_map = [ { "doctype": "Lab Prescription", @@ -538,14 +699,6 @@ def get_lrpt_map(): "parent", ], }, - # { - # "doctype": "Therapy Plan Detail", - # "fields": [ - # "name", - # "therapy_type", - # "parent" - # ] - # } ] return child_map @@ -647,7 +800,7 @@ def get_patient_encounters(patient, appointment, company): @frappe.whitelist() -def set_checked_lrpt_items(doc, checked_items): +def set_checked_lrp_items(doc, checked_items): doc = frappe.get_doc(json.loads(doc)) checked_items = json.loads(checked_items) @@ -671,6 +824,8 @@ def get_drug_item_list(patient, appointment, company): delivery_note_items = [] item_list, name_list = get_drugs(patient, appointment, company) + if len(item_list) == 0: + return [] if name_list: delivery_note_items += frappe.get_all( @@ -717,25 +872,26 @@ def get_drug_item_list(patient, appointment, company): ) avoid_duplicate_list = [] - if item_list: - for item in item_list: - for delivery_note in delivery_note_items: - if delivery_note.docstatus == 0: - status = "Draft" - else: - status = "Submitted" - - if ( - item.dn_detail - and item.drug_prescription_created == 1 - and item.dn_detail == delivery_note.name - and delivery_note.docstatus == 1 - ): + for item in item_list: + for delivery_note in delivery_note_items: + if delivery_note.docstatus == 0: + status = "Draft" + else: + status = "Submitted" + + if ( + item.dn_detail + and item.drug_prescription_created == 1 + and item.dn_detail == delivery_note.name + and delivery_note.docstatus == 1 + ): + if item.name not in avoid_duplicate_list: drug_list.append( { "child_name": item.name, "item_name": item.drug_code, - "quantity": item.quantity - item.quantity_returned, + "qty_prescribed": item.quantity, # - item.quantity_returned, + "qty_returned": item.quantity_returned or "", "encounter_no": item.parent, "delivery_note": delivery_note.parent, "dn_detail": item.dn_detail, @@ -744,19 +900,21 @@ def get_drug_item_list(patient, appointment, company): ) avoid_duplicate_list.append(item.name) - if ( - delivery_note.reference_name - and item.name == delivery_note.reference_name - and item.drug_prescription_created == 1 - and delivery_note.docstatus == 0 - and not item.dn_detail - and not delivery_note.si_detail - ): + if ( + delivery_note.reference_name + and item.name == delivery_note.reference_name + and item.drug_prescription_created == 1 + and delivery_note.docstatus == 0 + and not item.dn_detail + and not delivery_note.si_detail + ): + if item.name not in avoid_duplicate_list: drug_list.append( { "child_name": item.name, "item_name": item.drug_code, - "quantity": item.quantity - item.quantity_returned, + "qty_prescribed": item.quantity, # - item.quantity_returned, + "qty_returned": item.quantity_returned or "", "encounter_no": item.parent, "delivery_note": delivery_note.parent, "dn_detail": item.dn_detail or "", @@ -765,39 +923,41 @@ def get_drug_item_list(patient, appointment, company): ) avoid_duplicate_list.append(item.name) - if ( - not item.dn_detail - and not delivery_note.reference_name - and item.drug_prescription_created == 1 - and delivery_note.si_detail - and delivery_note.docstatus == 0 - ): - if item.name not in avoid_duplicate_list: - drug_list.append( - { - "child_name": item.name, - "item_name": item.drug_code, - "quantity": item.quantity - item.quantity_returned, - "encounter_no": item.parent, - "delivery_note": delivery_note.parent, - "dn_detail": item.dn_detail or "", - "status": status, - } - ) - avoid_duplicate_list.append(item.name) + if ( + not item.dn_detail + and not delivery_note.reference_name + and item.drug_prescription_created == 1 + and delivery_note.si_detail + and delivery_note.docstatus == 0 + ): + if item.name not in avoid_duplicate_list: + drug_list.append( + { + "child_name": item.name, + "item_name": item.drug_code, + "qty_prescribed": item.quantity, # - item.quantity_returned, + "qty_returned": item.quantity_returned or "", + "encounter_no": item.parent, + "delivery_note": delivery_note.parent, + "dn_detail": item.dn_detail or "", + "status": status, + } + ) + avoid_duplicate_list.append(item.name) - if item.name not in avoid_duplicate_list: - drug_list.append( - { - "child_name": item.name, - "item_name": item.drug_code, - "quantity": item.quantity - item.quantity_returned, - "encounter_no": item.parent, - "delivery_note": "", - "dn_detail": "", - "status": "", - } - ) + if item.name not in avoid_duplicate_list: + drug_list.append( + { + "child_name": item.name, + "item_name": item.drug_code, + "qty_prescribed": item.quantity, # - item.quantity_returned, + "qty_returned": item.quantity_returned or "", + "encounter_no": item.parent, + "delivery_note": "", + "dn_detail": "", + "status": "", + } + ) return drug_list @@ -849,7 +1009,8 @@ def set_checked_drug_items(doc, checked_items): for checked_item in checked_items: item_row = doc.append("drug_items", {}) item_row.drug_name = checked_item["item_name"] - item_row.quantity_prescribed = checked_item["quantity_prescribed"] + item_row.quantity_prescribed = cint(checked_item["qty_prescribed"]) + item_row.qty_returned = cint(checked_item["qty_returned"]) item_row.encounter_no = checked_item["encounter_no"] item_row.delivery_note_no = checked_item["delivery_note"] or "" item_row.status = checked_item["status"] or "" @@ -858,3 +1019,149 @@ def set_checked_drug_items(doc, checked_items): doc.save() return doc.name + + +@frappe.whitelist() +def get_therapies(patient, appointment, company): + therapies = [] + plan_child_table_ids = [] + + encounter_list = get_patient_encounters(patient, appointment, company) + if len(encounter_list) == 0: + return [] + + therapy_items = frappe.get_all( + "Therapy Plan Detail", + filters={ + "parent": ["in", encounter_list], + "is_not_available_inhouse": 0, + "is_cancelled": 0, + }, + fields=[ + "name", + "therapy_type", + "parent", + "no_of_sessions", + "sessions_cancelled", + ], + ) + + for item in therapy_items: + if item.therapy_type: + therapy_plan = frappe.get_value( + "Therapy Plan", + {"ref_doctype": "Patient Encounter", "ref_docname": item.parent}, + "name", + ) + + plan_child_table_id = frappe.db.get_value( + "Therapy Plan Detail", + { + "parent": therapy_plan, + "parenttype": "Therapy Plan", + "hms_tz_ref_childname": item.name, + "therapy_type": item.therapy_type, + "parentfield": "therapy_plan_details", + }, + "name", + ) + + if not plan_child_table_id: + # find plan_child_table_id when hms_tz_ref_childname does not have value, (for old therapies) + child_table_ids = frappe.db.get_all( + "Therapy Plan Detail", + { + "parent": therapy_plan, + "parenttype": "Therapy Plan", + "therapy_type": item.therapy_type, + "parentfield": "therapy_plan_details", + }, + "name", + ) + if len(child_table_ids) > 0: + for row in child_table_ids: + if row.name not in child_table_ids: + plan_child_table_id = row.name + break + + if plan_child_table_id: + plan_child_table_ids.append(plan_child_table_id) + + record = { + "status": "", + "therapy_session": "", + "encounter_no": item.parent, + "sessions": item.no_of_sessions, + "therapy_type": item.therapy_type, + "therapy_plan": therapy_plan or "", + "encounter_child_table_id": item.name, + "plan_child_table_id": plan_child_table_id, + "sessions_cancelled": item.sessions_cancelled, + } + + sessions_info = frappe.get_all( + "Therapy Session", + { + "patient": patient, + "docstatus": ["!=", 2], + "appointment": appointment, + "therapy_plan": therapy_plan, + "therapy_type": item.therapy_type, + "workflow_state": [ + "not in", + ["Not Serviced", "Submitted but Not Serviced"], + ], + }, + ["name", "docstatus"], + ) + + if len(sessions_info) == 0: + therapies.append(record) + + elif item.no_of_sessions > len(sessions_info): + record.update( + { + "sessions": item.no_of_sessions - len(sessions_info), + } + ) + therapies.append(record) + + for session in sessions_info: + session_dict = record.copy() + session_dict.update( + { + "sessions": 1, + "sessions_cancelled": 0, + "therapy_session": session.name, + "status": ( + "Draft" if session.docstatus == 0 else "Submitted" + ), + } + ) + + therapies.append(session_dict) + + return therapies + + +@frappe.whitelist() +def set_checked_therapy_items(doc, checked_items): + doc = frappe.get_doc(json.loads(doc)) + checked_items = json.loads(checked_items) + + doc.therapy_items = [] + + for checked_item in checked_items: + item_row = doc.append("therapy_items", {}) + item_row.status = checked_item["status"] or "" + item_row.therapy_type = checked_item["therapy_type"] + item_row.encounter_no = checked_item["encounter_no"] + item_row.therapy_plan = checked_item["therapy_plan"] or "" + item_row.sessions_cancelled = cint(checked_item["sessions_cancelled"]) + item_row.therapy_session = checked_item["therapy_session"] or "" + item_row.sessions_prescribed = cint(checked_item["sessions_prescribed"]) + item_row.plan_child_table_id = checked_item["plan_child_table_id"] + item_row.encounter_child_table_id = checked_item["encounter_child_table_id"] + + doc.save() + return doc.name diff --git a/hms_tz/hms_tz/doctype/medication_return/medication_return.json b/hms_tz/hms_tz/doctype/medication_return/medication_return.json index f6858e0b..a8fa8f92 100644 --- a/hms_tz/hms_tz/doctype/medication_return/medication_return.json +++ b/hms_tz/hms_tz/doctype/medication_return/medication_return.json @@ -8,6 +8,7 @@ "field_order": [ "drug_name", "quantity_prescribed", + "qty_returned", "quantity_to_return", "reason", "drug_condition", @@ -28,6 +29,7 @@ "reqd": 1 }, { + "columns": 1, "fieldname": "reason", "fieldtype": "Link", "in_list_view": 1, @@ -36,6 +38,7 @@ "options": "Healthcare Return Reason" }, { + "columns": 1, "fieldname": "drug_condition", "fieldtype": "Link", "in_list_view": 1, @@ -65,11 +68,12 @@ "read_only": 1 }, { + "columns": 2, "default": "1", "fieldname": "quantity_prescribed", "fieldtype": "Float", "in_list_view": 1, - "label": "Quantity Prescribed", + "label": "Qty Prescribed", "non_negative": 1, "read_only": 1 }, @@ -90,12 +94,21 @@ "fieldtype": "Data", "label": "Delivery Note Status", "read_only": 1 + }, + { + "columns": 2, + "fieldname": "qty_returned", + "fieldtype": "Float", + "in_list_view": 1, + "label": "Qty Returned", + "non_negative": 1, + "read_only": 1 } ], "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2022-04-05 11:00:30.856037", + "modified": "2024-04-04 00:35:44.137138", "modified_by": "Administrator", "module": "Hms Tz", "name": "Medication Return", @@ -103,5 +116,6 @@ "permissions": [], "sort_field": "modified", "sort_order": "DESC", + "states": [], "track_changes": 1 } \ No newline at end of file diff --git a/hms_tz/hms_tz/doctype/therapy_return/__init__.py b/hms_tz/hms_tz/doctype/therapy_return/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/hms_tz/hms_tz/doctype/therapy_return/therapy_return.json b/hms_tz/hms_tz/doctype/therapy_return/therapy_return.json new file mode 100644 index 00000000..6405ba74 --- /dev/null +++ b/hms_tz/hms_tz/doctype/therapy_return/therapy_return.json @@ -0,0 +1,110 @@ +{ + "actions": [], + "allow_rename": 1, + "creation": "2024-03-27 15:10:16.749568", + "default_view": "List", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "therapy_type", + "sessions_prescribed", + "sessions_cancelled", + "sessions_to_cancel", + "reason", + "encounter_no", + "therapy_plan", + "therapy_session", + "encounter_child_table_id", + "plan_child_table_id" + ], + "fields": [ + { + "bold": 1, + "fieldname": "therapy_type", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Therapy Type", + "read_only": 1, + "reqd": 1, + "search_index": 1 + }, + { + "fieldname": "reason", + "fieldtype": "Link", + "in_list_view": 1, + "in_preview": 1, + "label": "Reason", + "options": "Healthcare Return Reason", + "search_index": 1 + }, + { + "fieldname": "encounter_no", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Patient Encounter", + "options": "Patient Encounter", + "read_only": 1, + "search_index": 1 + }, + { + "fieldname": "therapy_plan", + "fieldtype": "Data", + "label": "Therapy Plan", + "read_only": 1 + }, + { + "fieldname": "therapy_session", + "fieldtype": "Data", + "label": "Therapy Session", + "read_only": 1, + "search_index": 1 + }, + { + "fieldname": "sessions_prescribed", + "fieldtype": "Int", + "in_list_view": 1, + "label": "Sessions Prescribed", + "read_only": 1 + }, + { + "fieldname": "sessions_to_cancel", + "fieldtype": "Int", + "in_list_view": 1, + "label": "Sessions to Cancel" + }, + { + "fieldname": "encounter_child_table_id", + "fieldtype": "Data", + "label": "Encounter Child Table ID", + "read_only": 1 + }, + { + "fieldname": "plan_child_table_id", + "fieldtype": "Data", + "label": "Therapy Plan Child Table ID", + "read_only": 1 + }, + { + "fieldname": "sessions_cancelled", + "fieldtype": "Int", + "in_list_view": 1, + "label": "Sessions Cancelled", + "non_negative": 1, + "read_only": 1 + } + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2024-04-03 19:36:23.703640", + "modified_by": "Administrator", + "module": "Hms Tz", + "name": "Therapy Return", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "track_changes": 1 +} \ No newline at end of file diff --git a/hms_tz/hms_tz/doctype/therapy_return/therapy_return.py b/hms_tz/hms_tz/doctype/therapy_return/therapy_return.py new file mode 100644 index 00000000..41dde5b8 --- /dev/null +++ b/hms_tz/hms_tz/doctype/therapy_return/therapy_return.py @@ -0,0 +1,8 @@ +# Copyright (c) 2024, Aakvatech and contributors +# For license information, please see license.txt + +# import frappe +from frappe.model.document import Document + +class TherapyReturn(Document): + pass diff --git a/hms_tz/hms_tz/doctype/therapy_session/therapy_session.js b/hms_tz/hms_tz/doctype/therapy_session/therapy_session.js index bbda42e4..89262a3a 100644 --- a/hms_tz/hms_tz/doctype/therapy_session/therapy_session.js +++ b/hms_tz/hms_tz/doctype/therapy_session/therapy_session.js @@ -34,12 +34,6 @@ frappe.ui.form.on('Therapy Session', { if (frm.doc.source) { set_source_referring_practitioner(frm) } - if (frm.doc.patient) { - frm.add_custom_button(__('Patient History'), function () { - frappe.route_options = { 'patient': frm.doc.patient }; - frappe.set_route('tz-patient-history'); - }); - } }, refresh: function (frm) { diff --git a/hms_tz/hooks.py b/hms_tz/hooks.py index 0c77a5a8..8044fd99 100644 --- a/hms_tz/hooks.py +++ b/hms_tz/hooks.py @@ -209,11 +209,14 @@ "validate": "hms_tz.nhif.api.sales_order.validate", "before_submit": "hms_tz.nhif.api.sales_order.before_submit", }, - # "Therapy Plan": { - # "validate": "hms_tz.nhif.api.therapy_plan.validate", - # }, + "Therapy Plan": { + "before_insert": "hms_tz.nhif.api.therapy_plan.before_insert", + "validate": "hms_tz.nhif.api.therapy_plan.validate", + }, "Therapy Session": { + "before_insert": "hms_tz.nhif.api.therapy_session.before_insert", "after_insert": "hms_tz.nhif.api.therapy_session.after_insert", + "before_submit": "hms_tz.nhif.api.therapy_session.before_submit", }, } diff --git a/hms_tz/nhif/api/therapy_plan.js b/hms_tz/nhif/api/therapy_plan.js index 8f8af87e..ff152994 100644 --- a/hms_tz/nhif/api/therapy_plan.js +++ b/hms_tz/nhif/api/therapy_plan.js @@ -1,14 +1,14 @@ frappe.ui.form.on('Therapy Plan', { - setup: function(frm) { + setup: function (frm) { frm.get_field('therapy_plan_details').grid.editable_fields = [ - {fieldname: 'therapy_type', columns: 4}, - {fieldname: 'no_of_sessions', columns: 2}, + { fieldname: 'therapy_type', columns: 4 }, + { fieldname: 'no_of_sessions', columns: 2 }, { fieldname: 'sessions_completed', columns: 2 }, - { "fieldname": 'is_restricted', columns: 2} + { "fieldname": 'is_restricted', columns: 2 } ]; }, - - refresh: function(frm) { + + refresh: function (frm) { if (!frm.doc.__islocal && (frm.doc.status == 'Not Serviced')) { frm.clear_custom_buttons(); // frm.remove_custom_button("Create") @@ -20,4 +20,16 @@ frappe.ui.form.on('Therapy Plan', { }); } }, + onload: function (frm) { + if (!frm.doc.__islocal && (frm.doc.status == 'Not Serviced')) { + frm.clear_custom_buttons(); + // frm.remove_custom_button("Create") + } + if (frm.doc.patient) { + frm.add_custom_button(__('Patient History'), () => { + frappe.route_options = { 'patient': frm.doc.patient }; + frappe.set_route('tz-patient-history'); + }); + } + } }); \ No newline at end of file diff --git a/hms_tz/nhif/api/therapy_plan.py b/hms_tz/nhif/api/therapy_plan.py index f63040b3..886986d1 100644 --- a/hms_tz/nhif/api/therapy_plan.py +++ b/hms_tz/nhif/api/therapy_plan.py @@ -1,12 +1,18 @@ import frappe -def before_insert(doc): +def before_insert(doc, method): total_sessions = 0 + total_sessions_cancelled = 0 for entry in doc.therapy_plan_details: if entry.no_of_sessions: total_sessions += entry.no_of_sessions + + if entry.sessions_cancelled: + total_sessions_cancelled += entry.sessions_cancelled + doc.total_sessions = total_sessions + doc.total_sessions_cancelled = total_sessions_cancelled def validate(doc, method): @@ -15,26 +21,30 @@ def validate(doc, method): def set_status(doc): - if not doc.total_sessions_completed and not doc.total_sessions: + if doc.total_sessions == 0 and doc.total_sessions_cancelled > 0: doc.status = "Not Serviced" - if doc.total_sessions and not doc.total_sessions_completed: + elif doc.total_sessions and not doc.total_sessions_completed: doc.status = "Not Started" - else: - if doc.total_sessions_completed < doc.total_sessions: - doc.status = "In Progress" + elif doc.total_sessions_completed < doc.total_sessions: + doc.status = "In Progress" - elif doc.total_sessions != 0 and ( - doc.total_sessions_completed == doc.total_sessions - ): - doc.status = "Completed" + elif doc.total_sessions != 0 and ( + doc.total_sessions_completed == doc.total_sessions + ): + doc.status = "Completed" def set_totals(doc): total_sessions_completed = 0 + total_sessions_cancelled = 0 for entry in doc.therapy_plan_details: if entry.sessions_completed: total_sessions_completed += entry.sessions_completed - doc.db_set("total_sessions_completed", total_sessions_completed) + if entry.sessions_cancelled: + total_sessions_cancelled += entry.sessions_cancelled + + doc.total_sessions_completed = total_sessions_completed + doc.total_sessions_cancelled = total_sessions_cancelled diff --git a/hms_tz/nhif/api/therapy_session.js b/hms_tz/nhif/api/therapy_session.js index 5bb390dd..07ebba5d 100644 --- a/hms_tz/nhif/api/therapy_session.js +++ b/hms_tz/nhif/api/therapy_session.js @@ -1,5 +1,32 @@ frappe.ui.form.on('Therapy Session', { + refresh: function (frm) { + $('[data-label="Not%20Serviced"]').parent().hide(); + if (!frm.doc.__islocal && (frm.doc.status == 'Not Serviced')) { + frm.clear_custom_buttons(); + // frm.remove_custom_button("Create") + } + if (frm.doc.patient) { + frm.add_custom_button(__('Patient History'), () => { + frappe.route_options = { 'patient': frm.doc.patient }; + frappe.set_route('tz-patient-history'); + }); + } + }, + onload: function (frm) { + $('[data-label="Not%20Serviced"]').parent().hide(); + if (!frm.doc.__islocal && (frm.doc.status == 'Not Serviced')) { + frm.clear_custom_buttons(); + // frm.remove_custom_button("Create") + } + if (frm.doc.patient) { + frm.add_custom_button(__('Patient History'), () => { + frappe.route_options = { 'patient': frm.doc.patient }; + frappe.set_route('tz-patient-history'); + }); + } + }, + approval_number: (frm) => { frm.fields_dict.approval_number.$input.focusout(() => { if (frm.doc.approval_number != "" && frm.doc.approval_number != undefined) { diff --git a/hms_tz/nhif/api/therapy_session.py b/hms_tz/nhif/api/therapy_session.py index 88cab088..44390342 100644 --- a/hms_tz/nhif/api/therapy_session.py +++ b/hms_tz/nhif/api/therapy_session.py @@ -1,6 +1,10 @@ import frappe +def before_insert(doc, method): + validate_not_serviced(doc) + + def after_insert(doc, method): if doc.therapy_plan: plan = frappe.get_doc("Therapy Plan", doc.therapy_plan) @@ -20,3 +24,17 @@ def after_insert(doc, method): break doc.save(ignore_permissions=True) + + +def before_submit(doc, method): + validate_not_serviced(doc) + + +def validate_not_serviced(doc): + if doc.therapy_plan: + status = frappe.db.get_value("Therapy Plan", doc.therapy_plan, "status") + if status == "Not Serviced": + frappe.throw( + f"This Therapy Plan: {frappe.bold(doc.therapy_plan)} is Not Serviced,\ + Please select another Therapy Plan. or cancel this therapy session" + ) diff --git a/hms_tz/patches.txt b/hms_tz/patches.txt index aa1e6231..c36cfa85 100644 --- a/hms_tz/patches.txt +++ b/hms_tz/patches.txt @@ -57,3 +57,5 @@ hms_tz.patches.property_setter.set_only_once_appointment_property hms_tz.patches.custom_fields.add_admission_service_unit_type_custom_field hms_tz.patches.custom_fields.auto_sales_order_creation_from_encounter hms_tz.patches.custom_fields.nhif_restricted_therapy_for_service_approval_number +hms_tz.patches.custom_fields.sessions_cancelled_custom_field_on_therapy_plan_detail +hms_tz.patches.property_setter.status_options_for_therapy_plan diff --git a/hms_tz/patches/custom_fields/sessions_cancelled_custom_field_on_therapy_plan_detail.py b/hms_tz/patches/custom_fields/sessions_cancelled_custom_field_on_therapy_plan_detail.py new file mode 100644 index 00000000..7055b3dd --- /dev/null +++ b/hms_tz/patches/custom_fields/sessions_cancelled_custom_field_on_therapy_plan_detail.py @@ -0,0 +1,26 @@ +import frappe +from frappe.custom.doctype.custom_field.custom_field import create_custom_fields + +def execute(): + fields = { + "Therapy Plan Detail": [ + { + "fieldname": "sessions_cancelled", + "fieldtype": "Int", + "label": "Sessions Cancelled", + "insert_after": "no_of_sessions", + "read_only": 1, + } + ], + "Therapy Plan": [ + { + "fieldname": "total_sessions_cancelled", + "fieldtype": "Int", + "label": "Total Sessions Cancelled", + "insert_after": "total_sessions", + "read_only": 1, + } + ], + } + + create_custom_fields(fields, update=True) diff --git a/hms_tz/patches/property_setter/status_options_for_therapy_plan.py b/hms_tz/patches/property_setter/status_options_for_therapy_plan.py new file mode 100644 index 00000000..cf9ea2fc --- /dev/null +++ b/hms_tz/patches/property_setter/status_options_for_therapy_plan.py @@ -0,0 +1,14 @@ +import frappe +from frappe.custom.doctype.property_setter.property_setter import make_property_setter + +def execute(): + make_property_setter( + "Therapy Plan", + "status", + "options", + "Not Started\nIn Progress\nCompleted\nCancelled\nNot Serviced", + "Text", + for_doctype=False, + validate_fields_for_doctype=False, + ) + frappe.db.commit() From a8a1001de6b5cbde21b22b80a20a79ec69a411dc Mon Sep 17 00:00:00 2001 From: av-dev2 Date: Tue, 9 Apr 2024 12:20:21 +0300 Subject: [PATCH 04/25] fix: conflict of 'modified' on lrpmt_returns.json file --- hms_tz/hms_tz/doctype/lrpmt_returns/lrpmt_returns.json | 4 ---- 1 file changed, 4 deletions(-) diff --git a/hms_tz/hms_tz/doctype/lrpmt_returns/lrpmt_returns.json b/hms_tz/hms_tz/doctype/lrpmt_returns/lrpmt_returns.json index 3d18442e..291e5afc 100644 --- a/hms_tz/hms_tz/doctype/lrpmt_returns/lrpmt_returns.json +++ b/hms_tz/hms_tz/doctype/lrpmt_returns/lrpmt_returns.json @@ -182,11 +182,7 @@ "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], -<<<<<<< HEAD - "modified": "2022-04-29 18:22:45.789471", -======= "modified": "2024-04-02 22:41:54.900854", ->>>>>>> db8d8dd1 (feat: Cancel or return therapy sessions via LRPMT Returns doctype) "modified_by": "Administrator", "module": "Hms Tz", "name": "LRPMT Returns", From 8c445b5fa69bc474b5f0ca9d776f736fc2d3e1ce Mon Sep 17 00:00:00 2001 From: av-dev2 Date: Fri, 12 Apr 2024 15:45:39 +0300 Subject: [PATCH 05/25] feat: therapy automation and controls like other LRPM documents (cherry picked from commit 143ef1fba8f7e664752da8ab98ddcdfd9415e395) # Conflicts: # hms_tz/patches/custom_fields/sessions_cancelled_custom_field_on_therapy_plan_detail.py --- .../patient_encounter/patient_encounter.py | 49 ----- hms_tz/nhif/api/healthcare_utils.py | 167 ++++++++++++++++++ hms_tz/nhif/api/patient_encounter.py | 39 ++-- hms_tz/nhif/api/sales_invoice.py | 15 ++ hms_tz/nhif/api/sales_order.py | 2 + ...led_custom_field_on_therapy_plan_detail.py | 34 ++++ 6 files changed, 239 insertions(+), 67 deletions(-) create mode 100644 hms_tz/patches/custom_fields/sessions_cancelled_custom_field_on_therapy_plan_detail.py diff --git a/hms_tz/hms_tz/doctype/patient_encounter/patient_encounter.py b/hms_tz/hms_tz/doctype/patient_encounter/patient_encounter.py index ce9e49ab..f0b6a6a0 100644 --- a/hms_tz/hms_tz/doctype/patient_encounter/patient_encounter.py +++ b/hms_tz/hms_tz/doctype/patient_encounter/patient_encounter.py @@ -34,7 +34,6 @@ def after_insert(self): def on_submit(self): update_encounter_medical_record(self) - create_therapy_plan(self) create_healthcare_service_order(self) # make_insurance_claim(self) @@ -52,54 +51,6 @@ def set_title(self): )[:100] -def create_therapy_plan(encounter): - if len(encounter.therapies): - therapies = [] - for row in encounter.therapies: - if ( - row.is_cancelled - or row.hms_tz_is_limit_exceeded - or row.is_not_available_inhouse - ): - continue - - therapies.append(row) - if len(therapies) == 0: - return - - doc = frappe.new_doc("Therapy Plan") - doc.patient = encounter.patient - doc.company = encounter.company - doc.start_date = encounter.encounter_date - doc.hms_tz_appointment = encounter.appointment - doc.hms_tz_patient_age = encounter.patient_age - doc.hms_tz_patient_sex = encounter.patient_sex - doc.hms_tz_insurance_coverage_plan = encounter.insurance_coverage_plan - doc.insurance_company = encounter.insurance_company - doc.ref_doctype = "Patient Encounter" - doc.ref_docname = encounter.name - for entry in therapies: - doc.append( - "therapy_plan_details", - { - "therapy_type": entry.therapy_type, - "no_of_sessions": entry.no_of_sessions, - "prescribe": entry.prescribe or 0, - "is_restricted": entry.is_restricted or 0, - "hms_tz_ref_childname": entry.name - }, - ) - doc.save(ignore_permissions=True) - if doc.get("name"): - encounter.db_set("therapy_plan", doc.name) - frappe.msgprint( - _( - f"Therapy Plan {frappe.bold(doc.name)} created successfully." - ), - alert=True - ) - - def insert_encounter_to_medical_record(doc): subject = set_subject_field(doc) medical_record = frappe.new_doc("Patient Medical Record") diff --git a/hms_tz/nhif/api/healthcare_utils.py b/hms_tz/nhif/api/healthcare_utils.py index 6b0379f3..b731365b 100644 --- a/hms_tz/nhif/api/healthcare_utils.py +++ b/hms_tz/nhif/api/healthcare_utils.py @@ -134,6 +134,11 @@ def get_healthcare_service_order_to_invoice( ) qty = 1 + if value.get("doctype") == "Therapy Plan Detail": + qty = (row.get("no_of_sessions") or 0) - ( + row.get("sessions_cancelled") or 0 + ) + if value.get("doctype") == "Drug Prescription": qty = (row.get("quantity") or 0) - ( row.get("quantity_returned") or 0 @@ -804,6 +809,158 @@ def create_individual_procedure_prescription(source_doc, child): child.db_update() +def create_therapy_plan(enc_doc=None, invoice_therapy_dict=[]): + therapies = [] + encounter_ids = [] + patient_encounter_docs = [] + + if not enc_doc and len(invoice_therapy_dict) == 0: + return + + if len(invoice_therapy_dict) > 0: + # These invoice_therapy_dict comes from sales invoice items + for row in invoice_therapy_dict: + therapy_child = frappe.get_cached_doc( + "Therapy Plan Detail", row.reference_dn + ) + if therapy_child.is_cancelled == 1: + frappe.throw( + f"Item: {frappe.bold(therapy_child.therapy_type)} RowNo#: {frappe.bold(row.idx)} is already cancelled,\ + Please confirm cancellation of this item on Patient Encounter and remove this item from sales invoice" + ) + + if therapy_child.parent not in encounter_ids: + encounter_ids.append(therapy_child.parent) + enc_doc = frappe.get_doc("Patient Encounter", therapy_child.parent) + patient_encounter_docs.append(enc_doc) + + if ( + therapy_child.therapy_plan_created + or therapy_child.is_not_available_inhouse + or therapy_child.hms_tz_is_limit_exceeded + ): + continue + + therapy_child.invoiced = 1 + therapy_child.sales_invoice_number = row.parent + therapy_child.save(ignore_permissions=True) + therapies.append(therapy_child) + + row.hms_tz_is_lrp_item_created = 1 + row.db_update() + + elif len(enc_doc.therapies) > 0: + if enc_doc.docstatus != 1: + frappe.msgprint( + _( + "Cannot process Patient Encounter that is not submitted! Please submit" + " and try again." + ), + alert=True, + ) + return + if not enc_doc.appointment: + frappe.msgprint( + _( + "Patient Encounter does not have patient appointment number! Request" + " for support with this message." + ), + alert=True, + ) + return + + if not enc_doc.insurance_subscription and not enc_doc.inpatient_record: + return + + patient_encounter_docs.append(enc_doc) + for row in enc_doc.therapies: + if ( + row.is_cancelled + or row.hms_tz_is_limit_exceeded + or row.is_not_available_inhouse + or row.therapy_plan_created + ): + continue + + # ignore uncovered therapies for insurance patients + if enc_doc.insurance_subscription and row.prescribe == 1: + continue + + # ignore therapies that won't be paid by cash + if not enc_doc.insurance_subscription and row.prescribe == 0: + continue + + is_disabled = frappe.get_cached_value( + "Therapy Type", row.therapy_type, "disabled" + ) + if is_disabled == 1: + frappe.throw( + _( + f"Therapy Type: {row.therapy_type} selected at Row#: {row.idx} is disabled. Please select an enabled item." + ) + ) + + therapies.append(row) + + if len(therapies) == 0: + return + + create_plan(patient_encounter_docs, therapies) + + +def create_plan(patient_encounter_docs, therapies): + for encounter_doc in patient_encounter_docs: + item_counts = 0 + doc = frappe.new_doc("Therapy Plan") + doc.patient = encounter_doc.patient + doc.company = encounter_doc.company + doc.start_date = encounter_doc.encounter_date + doc.hms_tz_appointment = encounter_doc.appointment + doc.hms_tz_patient_age = encounter_doc.patient_age + doc.hms_tz_patient_sex = encounter_doc.patient_sex + doc.hms_tz_insurance_coverage_plan = encounter_doc.insurance_coverage_plan + doc.insurance_company = encounter_doc.insurance_company + doc.ref_doctype = encounter_doc.doctype + doc.ref_docname = encounter_doc.name + for entry in therapies: + if entry.parent == encounter_doc.name: + item_counts += 1 + doc.append( + "therapy_plan_details", + { + "therapy_type": entry.therapy_type, + "prescribe": entry.prescribe or 0, + "is_restricted": entry.is_restricted or 0, + "hms_tz_ref_childname": entry.name, + "no_of_sessions": entry.no_of_sessions + - entry.sessions_cancelled, + }, + ) + if item_counts == 0: + continue + + doc.save(ignore_permissions=True) + if doc.get("name"): + # April 09, 2024 + # stopping updating therapy plan on encounter, + # since single encounter may have multiple therapy plans, + # this may happen when insurance patient pay cash for some therapies and insurance for other therapies + + # encounter_doc.db_set("therapy_plan", doc.name) + for entry in therapies: + if entry.parent == encounter_doc.name: + entry.therapy_plan_created = 1 + entry.delivered_quantity = ( + entry.no_of_sessions - entry.sessions_cancelled + ) + entry.db_update() + + frappe.msgprint( + _(f"Therapy Plan {frappe.bold(doc.name)} created successfully."), + alert=True, + ) + + def msgThrow(msg, method="throw", alert=True): if method == "validate": frappe.msgprint(msg, alert=alert) @@ -1060,6 +1217,8 @@ def create_invoiced_items_if_not_created(): for invoice in si_invoices: si_doc = frappe.get_doc("Sales Invoice", invoice.name) + therapy_items = [] + for item in si_doc.items: if item.reference_dt in [ "Lab Prescription", @@ -1196,6 +1355,14 @@ def create_invoiced_items_if_not_created(): traceback = frappe.get_traceback() frappe.log_error(traceback) + elif item.reference_dt == "Therapy Plan Detail": + if item.hms_tz_is_lrp_item_created == 1: + continue + + therapy_items.append(item) + + create_therapy_plan(invoice_therapy_dict=therapy_items) + frappe.db.commit() diff --git a/hms_tz/nhif/api/patient_encounter.py b/hms_tz/nhif/api/patient_encounter.py index 002b65a1..ca688d0e 100644 --- a/hms_tz/nhif/api/patient_encounter.py +++ b/hms_tz/nhif/api/patient_encounter.py @@ -23,6 +23,7 @@ create_individual_lab_test, create_individual_radiology_examination, create_individual_procedure_prescription, + create_therapy_plan, msgThrow, msgPrint, validate_nhif_patient_claim_status, @@ -400,7 +401,7 @@ def on_submit_validation(doc, method): healthcare_insurance_coverage_plan, ["coverage_plan_name", "is_exclusions"], ) - + validate_item_coverage( doc, method, @@ -411,13 +412,9 @@ def on_submit_validation(doc, method): ) validate_totals(doc, method) + def validate_item_coverage( - doc, - method, - healthcare_service_templates, - hsic_map, - hicp_name, - is_exclusions + doc, method, healthcare_service_templates, hsic_map, hicp_name, is_exclusions ): for template in healthcare_service_templates: """ @@ -735,7 +732,7 @@ def on_submit(doc, method): if ( doc.healthcare_package_order - and not doc.insurance_subscription + # and not doc.insurance_subscription and not doc.inpatient_record ): create_items_from_healthcare_package_orders(doc, method) @@ -765,6 +762,7 @@ def create_healthcare_docs(patient_encounter_doc, method="event"): create_healthcare_docs_per_encounter( frappe.get_doc("Patient Encounter", encounter) ) + create_therapy_plan(enc_doc=frappe.get_doc("Patient Encounter", encounter)) if method == "from_button": frappe.msgprint( _( @@ -1661,16 +1659,17 @@ def inpatient_billing(patient_encounter_doc, method): "item_field": "procedure", "doctype": "Clinical Procedure Template", }, - { - "table_field": "drug_prescription", - "item_field": "drug_code", - "doctype": "Medication", - }, - { - "table_field": "therapies", - "item_field": "therapy_type", - "doctype": "Therapy Type", - }, + # unused part + # { + # "table_field": "drug_prescription", + # "item_field": "drug_code", + # "doctype": "Medication", + # }, + # { + # "table_field": "therapies", + # "item_field": "therapy_type", + # "doctype": "Therapy Type", + # }, ] for child_table_field in child_tables_list: if patient_encounter_doc.get(child_table_field.get("table_field")): @@ -1706,6 +1705,8 @@ def inpatient_billing(patient_encounter_doc, method): create_individual_procedure_prescription( patient_encounter_doc, child ) + + create_therapy_plan(enc_doc=patient_encounter_doc) create_delivery_note(patient_encounter_doc, method) @@ -2546,6 +2547,8 @@ def create_items_from_healthcare_package_orders(doc, method): create_individual_radiology_examination(doc, child) elif child.doctype == "Procedure Prescription": create_individual_procedure_prescription(doc, child) + + create_therapy_plan(enc_doc=doc) create_delivery_note(doc, method) diff --git a/hms_tz/nhif/api/sales_invoice.py b/hms_tz/nhif/api/sales_invoice.py index 532abecf..8b7e0438 100644 --- a/hms_tz/nhif/api/sales_invoice.py +++ b/hms_tz/nhif/api/sales_invoice.py @@ -10,6 +10,7 @@ create_individual_lab_test, create_individual_radiology_examination, create_individual_procedure_prescription, + create_therapy_plan ) from hms_tz.nhif.api.sales_order import validate_stock_item @@ -100,6 +101,8 @@ def create_healthcare_docs(doc, method): ) ) return + + therapy_items = [] if doc.get("items"): for item in doc.items: if item.reference_dt and item.reference_dt in [ @@ -108,6 +111,12 @@ def create_healthcare_docs(doc, method): "Procedure Prescription", ]: child = frappe.get_doc(item.reference_dt, item.reference_dn) + if child.is_cancelled == 1: + frappe.throw( + f"Item: {frappe.bold(item.item_code)} RowNo#: {frappe.bold(item.idx)} is already cancelled,\ + Please confirm cancellation of this item on Patient Encounter and remove this item from sales invoice" + ) + patient_encounter_doc = frappe.get_doc( "Patient Encounter", child.parent ) @@ -127,6 +136,10 @@ def create_healthcare_docs(doc, method): item.hms_tz_is_lrp_item_created = 1 item.db_update() + + elif item.reference_dt and item.reference_dt == "Therapy Plan Detail": + therapy_items.append(item) + elif item.reference_dt and item.reference_dt in [ "Inpatient Occupancy", "Inpatient Consultancy", @@ -144,6 +157,8 @@ def create_healthcare_docs(doc, method): }, ) + create_therapy_plan(invoice_therapy_dict=therapy_items) + if method == "From Front End": frappe.db.commit() diff --git a/hms_tz/nhif/api/sales_order.py b/hms_tz/nhif/api/sales_order.py index 246953cf..6c1a6c30 100644 --- a/hms_tz/nhif/api/sales_order.py +++ b/hms_tz/nhif/api/sales_order.py @@ -132,6 +132,8 @@ def get_items_from_encounter(doc, warehouse): drug_items.append(new_row) else: + if row.doctype == "Therapy Plan Detail": + new_row.update({"qty": row.no_of_sessions - row.sessions_cancelled,}) lrpt_items.append(new_row) return drug_items, lrpt_items diff --git a/hms_tz/patches/custom_fields/sessions_cancelled_custom_field_on_therapy_plan_detail.py b/hms_tz/patches/custom_fields/sessions_cancelled_custom_field_on_therapy_plan_detail.py new file mode 100644 index 00000000..341e0a7f --- /dev/null +++ b/hms_tz/patches/custom_fields/sessions_cancelled_custom_field_on_therapy_plan_detail.py @@ -0,0 +1,34 @@ +import frappe +from frappe.custom.doctype.custom_field.custom_field import create_custom_fields + + +def execute(): + fields = { + "Therapy Plan Detail": [ + { + "fieldname": "sessions_cancelled", + "fieldtype": "Int", + "label": "Sessions Cancelled", + "insert_after": "no_of_sessions", + "read_only": 1, + }, + { + "fieldname": "therapy_plan_created", + "fieldtype": "Check", + "label": "Therapy Plan Created", + "insert_after": "section_break_20", + "read_only": 1, + }, + ], + "Therapy Plan": [ + { + "fieldname": "total_sessions_cancelled", + "fieldtype": "Int", + "label": "Total Sessions Cancelled", + "insert_after": "total_sessions", + "read_only": 1, + } + ], + } + + create_custom_fields(fields, update=True) From d962a10c7bff5f621528ccc1b3598e3e44bdc84c Mon Sep 17 00:00:00 2001 From: av-dev2 Date: Fri, 12 Apr 2024 17:52:49 +0300 Subject: [PATCH 06/25] feat: include 'sessions_cancelled' on all the revenue reports like itemized, ipd and itemwise hospital revenue (cherry picked from commit 2eb9315bdf90136d28f0b6404e61917cfd74559a) --- .../ipd_billing_report/ipd_billing_report.py | 10 +++---- .../itemized_bill_report.py | 12 ++++---- .../nhif_patient_claim/nhif_patient_claim.py | 30 +++++++++++++++---- .../itemwise_hospital_revenue.py | 18 +++++------ 4 files changed, 44 insertions(+), 26 deletions(-) diff --git a/hms_tz/hms_tz/report/ipd_billing_report/ipd_billing_report.py b/hms_tz/hms_tz/report/ipd_billing_report/ipd_billing_report.py index 2f20675b..79e1c796 100644 --- a/hms_tz/hms_tz/report/ipd_billing_report/ipd_billing_report.py +++ b/hms_tz/hms_tz/report/ipd_billing_report/ipd_billing_report.py @@ -285,7 +285,7 @@ def get_transaction_data(args): union all select 0 as lab_amount, 0 as radiology_amount, 0 as procedure_amount, 0 as drug_amount, - sum(lrpmt.amount) as therapy_amount, date(pe.encounter_date) as date, "" as service_unit, + sum(lrpmt.amount * (lrpmt.no_of_sessions - lrpmt.sessions_cancelled)) as therapy_amount, date(pe.encounter_date) as date, "" as service_unit, 0 as bed_charges, 0 as cons_charges from `tabTherapy Plan Detail` lrpmt inner join `tabPatient Encounter` pe on lrpmt.parent = pe.name @@ -793,9 +793,9 @@ def get_cash_lrpmt_transaction(filters): DATE(lrpmt.creation) AS date, item_template.item_group AS category, lrpmt.therapy_type AS description, - 1 AS quantity, + (lrpmt.no_of_sessions - lrpmt.sessions_cancelled) AS quantity, lrpmt.amount AS rate, - lrpmt.amount AS total_amount, + ((lrpmt.no_of_sessions - lrpmt.sessions_cancelled) * lrpmt.amount) AS total_amount, pa.patient AS patient, pa.patient_name AS patient_name, pa.appointment_type AS appointment_type, @@ -969,9 +969,9 @@ def get_insurance_lrpmt_transaction(filters): DATE(lrpmt.creation) AS date, item_template.item_group AS category, lrpmt.therapy_type AS description, - 1 AS quantity, + (lrpmt.no_of_sessions - lrpmt.sessions_cancelled) AS quantity, lrpmt.amount AS rate, - lrpmt.amount AS total_amount, + ((lrpmt.no_of_sessions - lrpmt.sessions_cancelled) * lrpmt.amount) AS total_amount, pa.patient AS patient, pa.patient_name AS patient_name, pa.appointment_type AS appointment_type, diff --git a/hms_tz/hms_tz/report/itemized_bill_report/itemized_bill_report.py b/hms_tz/hms_tz/report/itemized_bill_report/itemized_bill_report.py index 31fdc2ae..0f634b1c 100644 --- a/hms_tz/hms_tz/report/itemized_bill_report/itemized_bill_report.py +++ b/hms_tz/hms_tz/report/itemized_bill_report/itemized_bill_report.py @@ -406,9 +406,9 @@ def get_exceeded_therapy_items(encounters): fn.Date(therapy.creation).as_("date"), temp.item_group.as_("category"), therapy.therapy_type.as_("description"), - ValueWrapper(1).as_("quantity"), + (therapy.no_of_sessions - therapy.sessions_cancelled).as_("quantity"), therapy.amount.as_("rate"), - therapy.amount.as_("amount"), + ((therapy.no_of_sessions - therapy.sessions_cancelled) * therapy.amount).as_("amount"), ) .where( (therapy.prescribe == 0) @@ -745,9 +745,9 @@ def get_cash_lrpmt_transaction(filters): DATE(lrpmt.creation) AS date, item_template.item_group AS category, lrpmt.therapy_type AS description, - 1 AS quantity, + (lrpmt.no_of_sessions - lrpmt.sessions_cancelled) AS quantity, AS quantity, lrpmt.amount AS rate, - lrpmt.amount AS amount, + ((lrpmt.no_of_sessions - lrpmt.sessions_cancelled) * lrpmt.amount) AS amount, pa.patient AS patient, pa.patient_name AS patient_name, pa.appointment_type AS appointment_type, @@ -920,9 +920,9 @@ def get_insurance_lrpmt_transaction(filters): DATE(lrpmt.creation) AS date, item_template.item_group AS category, lrpmt.therapy_type AS description, - 1 AS quantity, + (lrpmt.no_of_sessions - lrpmt.sessions_cancelled) AS quantity, lrpmt.amount AS rate, - lrpmt.amount AS amount, + ((lrpmt.no_of_sessions - lrpmt.sessions_cancelled) * lrpmt.amount) AS amount, pa.patient AS patient, pa.patient_name AS patient_name, pa.appointment_type AS appointment_type, diff --git a/hms_tz/nhif/doctype/nhif_patient_claim/nhif_patient_claim.py b/hms_tz/nhif/doctype/nhif_patient_claim/nhif_patient_claim.py index 224bd2da..7e715d56 100644 --- a/hms_tz/nhif/doctype/nhif_patient_claim/nhif_patient_claim.py +++ b/hms_tz/nhif/doctype/nhif_patient_claim/nhif_patient_claim.py @@ -436,13 +436,22 @@ def set_patient_claim_item(self, inpatient_record=None, called_method=None): for row in encounter_doc.get(child.get("table")): if row.prescribe or row.is_cancelled: continue + item_code = frappe.get_value( child.get("doctype"), row.get(child.get("item")), "item" ) - delivered_quantity = (row.get("quantity") or 0) - ( - row.get("quantity_returned") or 0 - ) + delivered_quantity = 0 + if row.get("doctype") == "Drug Prescription": + delivered_quantity = (row.get("quantity") or 0) - ( + row.get("quantity_returned") or 0 + ) + elif row.get("doctype") == "Therapy Plan Detail": + delivered_quantity = (row.get("no_of_sessions") or 0) - ( + row.get("sessions_cancelled") or 0 + ) + else: + delivered_quantity = 1 new_row = self.append("nhif_patient_claim_item", {}) new_row.item_name = row.get(child.get("item_name")) @@ -575,15 +584,24 @@ def set_patient_claim_item(self, inpatient_record=None, called_method=None): for row in encounter_doc.get(child.get("table")): if row.prescribe or row.is_cancelled: continue + item_code = frappe.get_value( child.get("doctype"), row.get(child.get("item")), "item", ) - delivered_quantity = (row.get("quantity") or 0) - ( - row.get("quantity_returned") or 0 - ) + delivered_quantity = 0 + if row.get("doctype") == "Drug Prescription": + delivered_quantity = (row.get("quantity") or 0) - ( + row.get("quantity_returned") or 0 + ) + elif row.get("doctype") == "Therapy Plan Detail": + delivered_quantity = (row.get("no_of_sessions") or 0) - ( + row.get("sessions_cancelled") or 0 + ) + else: + delivered_quantity = 1 new_row = self.append("nhif_patient_claim_item", {}) new_row.item_name = row.get(child.get("item")) diff --git a/hms_tz/nhif/report/itemwise_hospital_revenue/itemwise_hospital_revenue.py b/hms_tz/nhif/report/itemwise_hospital_revenue/itemwise_hospital_revenue.py index de72d366..445329ea 100644 --- a/hms_tz/nhif/report/itemwise_hospital_revenue/itemwise_hospital_revenue.py +++ b/hms_tz/nhif/report/itemwise_hospital_revenue/itemwise_hospital_revenue.py @@ -1372,9 +1372,9 @@ def get_insurance_therapy_data(filters, appoints): tt.item_group.as_("service_type"), tpd.therapy_type.as_("service_name"), tp.hms_tz_insurance_coverage_plan.as_("payment_method"), - ValueWrapper(1).as_("qty"), + (tpd.no_of_sessions - tpd.sessions_cancelled).as_("qty"), tpd.amount.as_("rate"), - tpd.amount.as_("amount"), + ((tpd.no_of_sessions - tpd.sessions_cancelled) * tpd.amount).as_("amount"), Case() .when(tp.status == "Completed", "Submitted") .else_("Draft") @@ -1460,7 +1460,7 @@ def get_cash_therapy_data(filters, appoints): tt.item_group.as_("service_type"), tpd.therapy_type.as_("service_name"), ValueWrapper("Cash").as_("payment_method"), - ValueWrapper(1).as_("qty"), + (tpd.no_of_sessions - tpd.sessions_cancelled).as_("qty"), tpd.amount.as_("rate"), (sii.amount - sii.net_amount).as_("discount_amount"), sii.net_amount.as_("amount"), @@ -1526,9 +1526,9 @@ def get_cash_therapy_data(filters, appoints): tt.item_group.as_("service_type"), tpd.therapy_type.as_("service_name"), ValueWrapper("Cash").as_("payment_method"), - ValueWrapper(1).as_("qty"), + (tpd.no_of_sessions - tpd.sessions_cancelled).as_("qty"), tpd.amount.as_("rate"), - tpd.amount.as_("amount"), + ((tpd.no_of_sessions - tpd.sessions_cancelled) * tpd.amount).as_("amount"), Case() .when(tp.status == "Completed", "Submitted") .else_("Draft") @@ -1910,7 +1910,7 @@ def get_cancelled_lab_items(filters): .inner_join(pe) .on(ireturn.encounter_no == pe.name) .select( - fn.Date(lreturn.modified).as_("date"), + Date(lreturn.modified).as_("date"), lreturn.patient.as_("patient"), lreturn.patient_name.as_("patient_name"), Case() @@ -1980,7 +1980,7 @@ def get_cancelled_radiology_items(filters): .inner_join(pe) .on(ireturn.encounter_no == pe.name) .select( - fn.Date(lreturn.modified).as_("date"), + Date(lreturn.modified).as_("date"), lreturn.patient.as_("patient"), lreturn.patient_name.as_("patient_name"), Case() @@ -2050,7 +2050,7 @@ def get_cancelled_procedure_items(filters): .inner_join(pe) .on(ireturn.encounter_no == pe.name) .select( - fn.Date(lreturn.modified).as_("date"), + Date(lreturn.modified).as_("date"), lreturn.patient.as_("patient"), lreturn.patient_name.as_("patient_name"), Case() @@ -2119,7 +2119,7 @@ def get_cancelled_drug_items(filters): .inner_join(pe) .on(mreturn.encounter_no == pe.name) .select( - fn.Date(lreturn.modified).as_("date"), + Date(lreturn.modified).as_("date"), lreturn.patient.as_("patient"), lreturn.patient_name.as_("patient_name"), Case() From 66f394f7c2c196dcf75dfdc169fcde515059895e Mon Sep 17 00:00:00 2001 From: av-dev2 Date: Wed, 27 Mar 2024 12:27:43 +0300 Subject: [PATCH 07/25] chore: add list view filters for patient, patient_name, appointment, company and inpatient_record (cherry picked from commit 7cbbae1e27b025ea13946b85b9ffa51d43446668) # Conflicts: # hms_tz/hms_tz/doctype/lrpmt_returns/lrpmt_returns.json --- .../doctype/lrpmt_returns/lrpmt_returns.json | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/hms_tz/hms_tz/doctype/lrpmt_returns/lrpmt_returns.json b/hms_tz/hms_tz/doctype/lrpmt_returns/lrpmt_returns.json index 291e5afc..521693d4 100644 --- a/hms_tz/hms_tz/doctype/lrpmt_returns/lrpmt_returns.json +++ b/hms_tz/hms_tz/doctype/lrpmt_returns/lrpmt_returns.json @@ -2,7 +2,7 @@ "actions": [], "allow_rename": 1, "autoname": "naming_series:", - "creation": "2021-12-11 10:43:22.393583", + "creation": "2024-03-27 12:05:48.759179", "doctype": "DocType", "editable_grid": 1, "engine": "InnoDB", @@ -35,6 +35,7 @@ "fieldname": "patient", "fieldtype": "Link", "in_list_view": 1, + "in_standard_filter": 1, "label": "Patient", "options": "Patient", "reqd": 1 @@ -43,6 +44,8 @@ "fetch_from": "patient.patient_name", "fieldname": "patient_name", "fieldtype": "Data", + "in_preview": 1, + "in_standard_filter": 1, "label": "Patient Name", "read_only": 1, "read_only_depends_on": "eval: doc.patient_name != \"\"" @@ -56,6 +59,8 @@ "fetch_from": "appointment.company", "fieldname": "company", "fieldtype": "Link", + "in_preview": 1, + "in_standard_filter": 1, "label": "Company", "options": "Company" }, @@ -64,6 +69,8 @@ "fieldname": "inpatient_record", "fieldtype": "Link", "in_list_view": 1, + "in_preview": 1, + "in_standard_filter": 1, "label": "Inpatient Record", "options": "Inpatient Record", "read_only": 1 @@ -163,6 +170,8 @@ "fieldname": "appointment", "fieldtype": "Link", "in_list_view": 1, + "in_preview": 1, + "in_standard_filter": 1, "label": "Patient Appointment", "options": "Patient Appointment" }, @@ -182,10 +191,15 @@ "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], +<<<<<<< HEAD "modified": "2024-04-02 22:41:54.900854", +======= + "modified": "2024-03-27 12:09:57.779364", +>>>>>>> 7cbbae1e (chore: add list view filters for patient, patient_name, appointment, company and inpatient_record) "modified_by": "Administrator", "module": "Hms Tz", "name": "LRPMT Returns", + "naming_rule": "By \"Naming Series\" field", "owner": "Administrator", "permissions": [ { @@ -203,5 +217,6 @@ ], "sort_field": "modified", "sort_order": "DESC", + "states": [], "track_changes": 1 } \ No newline at end of file From 07dca98bddd1b86b0248788b05f4027dd1d070a0 Mon Sep 17 00:00:00 2001 From: elius mgani <78465509+av-dev2@users.noreply.github.com> Date: Wed, 17 Apr 2024 13:26:46 +0300 Subject: [PATCH 08/25] fix: conflicts lrpmt_returns.json --- hms_tz/hms_tz/doctype/lrpmt_returns/lrpmt_returns.json | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/hms_tz/hms_tz/doctype/lrpmt_returns/lrpmt_returns.json b/hms_tz/hms_tz/doctype/lrpmt_returns/lrpmt_returns.json index 521693d4..e23c4f9a 100644 --- a/hms_tz/hms_tz/doctype/lrpmt_returns/lrpmt_returns.json +++ b/hms_tz/hms_tz/doctype/lrpmt_returns/lrpmt_returns.json @@ -191,11 +191,7 @@ "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], -<<<<<<< HEAD "modified": "2024-04-02 22:41:54.900854", -======= - "modified": "2024-03-27 12:09:57.779364", ->>>>>>> 7cbbae1e (chore: add list view filters for patient, patient_name, appointment, company and inpatient_record) "modified_by": "Administrator", "module": "Hms Tz", "name": "LRPMT Returns", @@ -219,4 +215,4 @@ "sort_order": "DESC", "states": [], "track_changes": 1 -} \ No newline at end of file +} From 95826b38e0b6f6c965026333603213e8591b61a2 Mon Sep 17 00:00:00 2001 From: av-dev2 Date: Wed, 17 Apr 2024 17:21:00 +0300 Subject: [PATCH 09/25] chore: allow rename on 'Company NHIF Settings' --- .../doctype/company_nhif_settings/company_nhif_settings.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/hms_tz/nhif/doctype/company_nhif_settings/company_nhif_settings.json b/hms_tz/nhif/doctype/company_nhif_settings/company_nhif_settings.json index 1568e266..1f37d02f 100644 --- a/hms_tz/nhif/doctype/company_nhif_settings/company_nhif_settings.json +++ b/hms_tz/nhif/doctype/company_nhif_settings/company_nhif_settings.json @@ -1,5 +1,6 @@ { "actions": [], + "allow_rename": 1, "autoname": "field:company", "creation": "2020-11-10 16:52:35.894771", "doctype": "DocType", @@ -195,10 +196,11 @@ ], "index_web_pages_for_search": 1, "links": [], - "modified": "2023-09-28 23:11:07.972344", + "modified": "2024-04-17 17:20:12.085065", "modified_by": "Administrator", "module": "NHIF", "name": "Company NHIF Settings", + "naming_rule": "By fieldname", "owner": "Administrator", "permissions": [ { @@ -216,5 +218,6 @@ ], "sort_field": "modified", "sort_order": "DESC", + "states": [], "track_changes": 1 } \ No newline at end of file From 956ed25384d7d358194ab911a44c506f0d507f9e Mon Sep 17 00:00:00 2001 From: av-dev2 Date: Wed, 17 Apr 2024 18:12:11 +0300 Subject: [PATCH 10/25] feat: rename 'Company NHIF Settings' to 'Company Insurance Setting' to support multiple insurance settings --- hms_tz/nhif/api/healthcare_utils.py | 8 +- hms_tz/nhif/api/patient.py | 6 +- hms_tz/nhif/api/patient_appointment.py | 6 +- hms_tz/nhif/api/token.py | 24 ++-- .../__init__.py | 0 .../company_insurance_setting.js} | 6 +- .../company_insurance_setting.json} | 133 ++++++++++-------- .../company_insurance_setting.py} | 2 +- .../test_company_insurance_setting.py} | 2 +- .../healthcare_referral.py | 22 ++- 10 files changed, 116 insertions(+), 93 deletions(-) rename hms_tz/nhif/doctype/{company_nhif_settings => company_insurance_setting}/__init__.py (100%) rename hms_tz/nhif/doctype/{company_nhif_settings/company_nhif_settings.js => company_insurance_setting/company_insurance_setting.js} (75%) rename hms_tz/nhif/doctype/{company_nhif_settings/company_nhif_settings.json => company_insurance_setting/company_insurance_setting.json} (69%) rename hms_tz/nhif/doctype/{company_nhif_settings/company_nhif_settings.py => company_insurance_setting/company_insurance_setting.py} (85%) rename hms_tz/nhif/doctype/{company_nhif_settings/test_company_nhif_settings.py => company_insurance_setting/test_company_insurance_setting.py} (76%) diff --git a/hms_tz/nhif/api/healthcare_utils.py b/hms_tz/nhif/api/healthcare_utils.py index b731365b..91d10c89 100644 --- a/hms_tz/nhif/api/healthcare_utils.py +++ b/hms_tz/nhif/api/healthcare_utils.py @@ -1501,15 +1501,15 @@ def get_card_no(appointment=None, encounter=None): ( enable_nhif_api, - nhifservice_url, + service_url, validate_service_approval_no, ) = frappe.get_cached_value( "Company NHIF Settings", company, [ "enable", - "nhifservice_url", - "validate_service_approval_number_on_lrpm_documents", + "service_url", + "validate_service_approval_number_on_lrpmt_documents", ], ) if not enable_nhif_api: @@ -1523,7 +1523,7 @@ def get_card_no(appointment=None, encounter=None): item_code = get_item_ref_code(template_doctype, template_name) url = ( - str(nhifservice_url) + str(service_url) + f"/nhifservice/breeze/verification/GetReferenceNoStatus?CardNo={cardno}&ReferenceNo={approval_number}&ItemCode={item_code}" ) diff --git a/hms_tz/nhif/api/patient.py b/hms_tz/nhif/api/patient.py index cfb30766..fb6f4f1e 100644 --- a/hms_tz/nhif/api/patient.py +++ b/hms_tz/nhif/api/patient.py @@ -69,12 +69,12 @@ def get_patient_info(card_no=None): frappe.throw(_("No companies found to connect to NHIF")) token = get_nhifservice_token(company) - nhifservice_url = frappe.get_cached_value( - "Company NHIF Settings", company, "nhifservice_url" + service_url = frappe.get_cached_value( + "Company NHIF Settings", company, "service_url" ) headers = {"Authorization": "Bearer " + token} url = ( - str(nhifservice_url) + str(service_url) + "/nhifservice/breeze/verification/GetCardDetails?CardNo=" + str(card_no) ) diff --git a/hms_tz/nhif/api/patient_appointment.py b/hms_tz/nhif/api/patient_appointment.py index 2c9b5b3c..a9450b90 100644 --- a/hms_tz/nhif/api/patient_appointment.py +++ b/hms_tz/nhif/api/patient_appointment.py @@ -357,8 +357,8 @@ def get_authorization_num( referral_no="", remarks="", ): - enable_nhif_api, nhifservice_url = frappe.get_cached_value( - "Company NHIF Settings", company, ["enable", "nhifservice_url"] + enable_nhif_api, service_url = frappe.get_cached_value( + "Company NHIF Settings", company, ["enable", "service_url"] ) if not enable_nhif_api: frappe.msgprint( @@ -389,7 +389,7 @@ def get_authorization_num( headers = {"Content-Type": "application/json", "Authorization": "Bearer " + token} url = ( - str(nhifservice_url) + str(service_url) + "/nhifservice/breeze/verification/AuthorizeCard?" + card_no + visit_type_id diff --git a/hms_tz/nhif/api/token.py b/hms_tz/nhif/api/token.py index 89b75d2d..a36e1a99 100644 --- a/hms_tz/nhif/api/token.py +++ b/hms_tz/nhif/api/token.py @@ -61,20 +61,20 @@ def make_token_request(doc, url, headers, payload, fields): def get_nhifservice_token(company): setting_doc = frappe.get_cached_doc("Company NHIF Settings", company) if ( - setting_doc.nhifservice_expiry - and setting_doc.nhifservice_expiry > now_datetime() + setting_doc.service_token_expiry + and setting_doc.service_token_expiry > now_datetime() ): - return setting_doc.nhifservice_token + return setting_doc.service_token username = setting_doc.username password = get_decrypted_password("Company NHIF Settings", company, "password") payload = "grant_type=password&username={0}&password={1}".format(username, password) headers = {"Content-Type": "application/x-www-form-urlencoded"} - url = str(setting_doc.nhifservice_url) + "/nhifservice/Token" + url = str(setting_doc.service_url) + "/nhifservice/Token" nhifservice_fields = { - "token": "nhifservice_token", - "expiry": "nhifservice_expiry", + "token": "service_token", + "expiry": "service_token_expiry", } return make_token_request(setting_doc, url, headers, payload, nhifservice_fields) @@ -108,21 +108,21 @@ def get_formservice_token(company): frappe.throw(_("Company {0} not enabled for NHIF Integration".format(company))) if ( - company_nhif_doc.nhifform_expiry - and company_nhif_doc.nhifform_expiry > now_datetime() + company_nhif_doc.form_token_expiry + and company_nhif_doc.form_token_expiry > now_datetime() ): - return company_nhif_doc.nhifform_token + return company_nhif_doc.form_token username = company_nhif_doc.username password = get_decrypted_password("Company NHIF Settings", company, "password") payload = "grant_type=password&username={0}&password={1}".format(username, password) headers = {"Content-Type": "application/x-www-form-urlencoded"} - url = cstr(company_nhif_doc.nhifform_url) + "/formposting/Token" + url = cstr(company_nhif_doc.form_url) + "/formposting/Token" nhifform_fields = { - "token": "nhifform_token", - "expiry": "nhifform_expiry", + "token": "form_token", + "expiry": "form_token_expiry", } return make_token_request(company_nhif_doc, url, headers, payload, nhifform_fields) diff --git a/hms_tz/nhif/doctype/company_nhif_settings/__init__.py b/hms_tz/nhif/doctype/company_insurance_setting/__init__.py similarity index 100% rename from hms_tz/nhif/doctype/company_nhif_settings/__init__.py rename to hms_tz/nhif/doctype/company_insurance_setting/__init__.py diff --git a/hms_tz/nhif/doctype/company_nhif_settings/company_nhif_settings.js b/hms_tz/nhif/doctype/company_insurance_setting/company_insurance_setting.js similarity index 75% rename from hms_tz/nhif/doctype/company_nhif_settings/company_nhif_settings.js rename to hms_tz/nhif/doctype/company_insurance_setting/company_insurance_setting.js index cd0de0f3..e428efa3 100644 --- a/hms_tz/nhif/doctype/company_nhif_settings/company_nhif_settings.js +++ b/hms_tz/nhif/doctype/company_insurance_setting/company_insurance_setting.js @@ -1,7 +1,7 @@ // Copyright (c) 2020, Aakvatech and contributors // For license information, please see license.txt -frappe.ui.form.on('Company NHIF Settings', { +frappe.ui.form.on('Company Insurance Setting', { // refresh: function(frm) { // } @@ -10,7 +10,8 @@ frappe.ui.form.on('Company NHIF Settings', { frappe.msgprint("Please set submit claim year or submit claim month}"); return } - frappe.call("hms_tz.nhif.api.healthcare_utils.auto_submit_nhif_patient_claim", { + if (frm.doc.api_provider == "NHIF") { + frappe.call("hms_tz.nhif.api.healthcare_utils.auto_submit_nhif_patient_claim", { setting_dict: { "company": frm.doc.name, "submit_claim_year": frm.doc.submit_claim_year, @@ -19,5 +20,6 @@ frappe.ui.form.on('Company NHIF Settings', { }).then(r => { // do nothing }) + } } }); diff --git a/hms_tz/nhif/doctype/company_nhif_settings/company_nhif_settings.json b/hms_tz/nhif/doctype/company_insurance_setting/company_insurance_setting.json similarity index 69% rename from hms_tz/nhif/doctype/company_nhif_settings/company_nhif_settings.json rename to hms_tz/nhif/doctype/company_insurance_setting/company_insurance_setting.json index 1f37d02f..26370770 100644 --- a/hms_tz/nhif/doctype/company_nhif_settings/company_nhif_settings.json +++ b/hms_tz/nhif/doctype/company_insurance_setting/company_insurance_setting.json @@ -8,27 +8,28 @@ "engine": "InnoDB", "field_order": [ "company", + "facility_code", "username", "password", "column_break_4", - "facility_code", + "api_provider", "enable", - "validate_service_approval_number_on_lrpm_documents", + "validate_service_approval_number_on_lrpmt_documents", "enable_auto_submit_of_claims", "auto_submit_patient_claim", "section_break_5", - "nhifservice_url", - "nhifservice_token", - "nhifservice_expiry", + "service_url", + "service_token", + "service_token_expiry", "column_break_9", "claimsserver_url", "claimsserver_token", "claimsserver_expiry", "section_break_11", - "nhifform_url", - "nhifform_expiry", + "form_url", + "form_token_expiry", "column_break_18", - "nhifform_token", + "form_token", "section_break_15", "update_patient_history", "section_break_16", @@ -51,42 +52,28 @@ "fieldtype": "Data", "in_list_view": 1, "label": "User Name", - "reqd": 1 + "reqd": 1, + "search_index": 1 }, { "fieldname": "password", "fieldtype": "Password", "label": "Password", - "reqd": 1 + "reqd": 1, + "search_index": 1 }, { "default": "0", "fieldname": "enable", "fieldtype": "Check", "in_list_view": 1, - "label": "Enable NHIF API" + "label": "Enable API", + "search_index": 1 }, { "fieldname": "section_break_5", "fieldtype": "Section Break" }, - { - "fieldname": "nhifservice_url", - "fieldtype": "Data", - "label": "NHIF Service URL", - "reqd": 1 - }, - { - "fieldname": "nhifservice_token", - "fieldtype": "Small Text", - "label": "NHIF Service Token", - "read_only": 1 - }, - { - "fieldname": "nhifservice_expiry", - "fieldtype": "Datetime", - "label": "NHIF Service Token Expiry" - }, { "fieldname": "column_break_9", "fieldtype": "Column Break" @@ -95,7 +82,8 @@ "fieldname": "claimsserver_url", "fieldtype": "Data", "label": "Claims Server URL", - "reqd": 1 + "reqd": 1, + "search_index": 1 }, { "fieldname": "claimsserver_token", @@ -106,7 +94,8 @@ { "fieldname": "claimsserver_expiry", "fieldtype": "Datetime", - "label": "Claims Server Token Expiry" + "label": "Claims Server Token Expiry", + "search_index": 1 }, { "fieldname": "column_break_4", @@ -116,7 +105,8 @@ "fieldname": "facility_code", "fieldtype": "Data", "label": "Facility Code", - "reqd": 1 + "reqd": 1, + "search_index": 1 }, { "fieldname": "section_break_15", @@ -126,7 +116,8 @@ "default": "1", "fieldname": "update_patient_history", "fieldtype": "Check", - "label": "Update Patient history from Chronic records" + "label": "Update Patient history from Chronic records", + "search_index": 1 }, { "fieldname": "section_break_16", @@ -141,29 +132,15 @@ "bold": 1, "fieldname": "submit_claim_month", "fieldtype": "Int", - "label": "Submit Claim Month" + "label": "Submit Claim Month", + "search_index": 1 }, { "bold": 1, "fieldname": "submit_claim_year", "fieldtype": "Int", - "label": "Submit Claim Year" - }, - { - "fieldname": "nhifform_url", - "fieldtype": "Data", - "label": "NHIF Form URL" - }, - { - "fieldname": "nhifform_token", - "fieldtype": "Small Text", - "label": "NHIF Form Token", - "read_only": 1 - }, - { - "fieldname": "nhifform_expiry", - "fieldtype": "Datetime", - "label": "NHIF Form Token Expiry" + "label": "Submit Claim Year", + "search_index": 1 }, { "fieldname": "section_break_11", @@ -178,7 +155,8 @@ "depends_on": "eval: doc.enable", "fieldname": "enable_auto_submit_of_claims", "fieldtype": "Check", - "label": "Enable Auto Submit of Claims" + "label": "Enable Auto Submit of Claims", + "search_index": 1 }, { "depends_on": "eval: doc.enable && doc.enable_auto_submit_of_claims", @@ -189,17 +167,62 @@ { "default": "1", "depends_on": "eval: doc.enable", - "fieldname": "validate_service_approval_number_on_lrpm_documents", + "fieldname": "validate_service_approval_number_on_lrpmt_documents", "fieldtype": "Check", - "label": "Validate Service Approval number on LRPM Documents" + "label": "Validate Service Approval number on LRPMT Documents", + "search_index": 1 + }, + { + "fieldname": "service_url", + "fieldtype": "Data", + "label": "Service URL", + "reqd": 1, + "search_index": 1 + }, + { + "fieldname": "service_token", + "fieldtype": "Small Text", + "label": "Service Token", + "read_only": 1 + }, + { + "fieldname": "service_token_expiry", + "fieldtype": "Datetime", + "label": "Service Token Expiry", + "search_index": 1 + }, + { + "fieldname": "form_url", + "fieldtype": "Data", + "label": "Form URL", + "search_index": 1 + }, + { + "fieldname": "form_token_expiry", + "fieldtype": "Datetime", + "label": "Form Token Expiry", + "search_index": 1 + }, + { + "fieldname": "form_token", + "fieldtype": "Small Text", + "label": "Form Token", + "read_only": 1 + }, + { + "fieldname": "api_provider", + "fieldtype": "Select", + "label": "API Provider", + "options": "\nNHIF\nJubilee\nStrategis\nAssemble", + "reqd": 1 } ], "index_web_pages_for_search": 1, "links": [], - "modified": "2024-04-17 17:20:12.085065", + "modified": "2024-04-17 18:08:51.344756", "modified_by": "Administrator", "module": "NHIF", - "name": "Company NHIF Settings", + "name": "Company Insurance Setting", "naming_rule": "By fieldname", "owner": "Administrator", "permissions": [ diff --git a/hms_tz/nhif/doctype/company_nhif_settings/company_nhif_settings.py b/hms_tz/nhif/doctype/company_insurance_setting/company_insurance_setting.py similarity index 85% rename from hms_tz/nhif/doctype/company_nhif_settings/company_nhif_settings.py rename to hms_tz/nhif/doctype/company_insurance_setting/company_insurance_setting.py index cf76213a..00cd4771 100644 --- a/hms_tz/nhif/doctype/company_nhif_settings/company_nhif_settings.py +++ b/hms_tz/nhif/doctype/company_insurance_setting/company_insurance_setting.py @@ -8,5 +8,5 @@ from frappe.model.document import Document -class CompanyNHIFSettings(Document): +class CompanyInsuranceSetting(Document): pass diff --git a/hms_tz/nhif/doctype/company_nhif_settings/test_company_nhif_settings.py b/hms_tz/nhif/doctype/company_insurance_setting/test_company_insurance_setting.py similarity index 76% rename from hms_tz/nhif/doctype/company_nhif_settings/test_company_nhif_settings.py rename to hms_tz/nhif/doctype/company_insurance_setting/test_company_insurance_setting.py index 97bfbe67..46299d4b 100644 --- a/hms_tz/nhif/doctype/company_nhif_settings/test_company_nhif_settings.py +++ b/hms_tz/nhif/doctype/company_insurance_setting/test_company_insurance_setting.py @@ -7,5 +7,5 @@ import unittest -class TestCompanyNHIFSettings(unittest.TestCase): +class TestCompanyInsuranceSetting(unittest.TestCase): pass diff --git a/hms_tz/nhif/doctype/healthcare_referral/healthcare_referral.py b/hms_tz/nhif/doctype/healthcare_referral/healthcare_referral.py index 074f14d0..54f39376 100644 --- a/hms_tz/nhif/doctype/healthcare_referral/healthcare_referral.py +++ b/hms_tz/nhif/doctype/healthcare_referral/healthcare_referral.py @@ -20,22 +20,20 @@ def validate(self): def get_referral_no(name): doc = frappe.get_doc("Healthcare Referral", name) validate_required_fields(doc) - - nhifservice_url = frappe.get_cached_value( - "Company NHIF Settings", - doc.source_facility, - "nhifservice_url" - ) + + service_url = frappe.get_cached_value( + "Company NHIF Settings", doc.source_facility, "service_url" + ) token = get_nhifservice_token(doc.source_facility) - url = str(nhifservice_url) + "/nhifservice/breeze/verification/AddReferral" - + url = str(service_url) + "/nhifservice/breeze/verification/AddReferral" + qualificationid = frappe.get_cached_value( - "NHIF Physician Qualification", - doc.nhif_physician_qualification, - "physicianqualificationid" - ) + "NHIF Physician Qualification", + doc.nhif_physician_qualification, + "physicianqualificationid", + ) payload = json.dumps( { From be6ccf0edec9a8d4cdfcf60e639a25f8191fc80b Mon Sep 17 00:00:00 2001 From: av-dev2 Date: Wed, 17 Apr 2024 23:24:17 +0300 Subject: [PATCH 11/25] chore: change 'Company NHIF Settings' to 'Company Insurance Setting' on deffirent files --- hms_tz/nhif/api/healthcare_utils.py | 4 +- hms_tz/nhif/api/insurance_company.py | 37 +- hms_tz/nhif/api/patient.py | 6 +- hms_tz/nhif/api/patient_appointment.py | 2 +- hms_tz/nhif/api/token.py | 12 +- .../healthcare_referral.json | 522 +++++++++--------- .../healthcare_referral.py | 2 +- .../nhif_claim_reconciliation.json | 294 +++++----- .../nhif_claim_reconciliation.py | 192 ++++--- .../nhif_custom_excluded_services.json | 230 ++++---- .../nhif_patient_claim/nhif_patient_claim.py | 18 +- .../claims_reconciliation_report.py | 4 +- .../nhif_tracking_claim_change_report.js | 4 +- 13 files changed, 672 insertions(+), 655 deletions(-) diff --git a/hms_tz/nhif/api/healthcare_utils.py b/hms_tz/nhif/api/healthcare_utils.py index 91d10c89..cfd6a104 100644 --- a/hms_tz/nhif/api/healthcare_utils.py +++ b/hms_tz/nhif/api/healthcare_utils.py @@ -1376,7 +1376,7 @@ def auto_submit_nhif_patient_claim(setting_dict=None): if not setting_dict: company_setting_detail = frappe.get_all( - "Company NHIF Settings", + "Company Insurance Setting", filters={"enable": 1, "enable_auto_submit_of_claims": 1}, fields=["company", "submit_claim_year", "submit_claim_month"], ) @@ -1504,7 +1504,7 @@ def get_card_no(appointment=None, encounter=None): service_url, validate_service_approval_no, ) = frappe.get_cached_value( - "Company NHIF Settings", + "Company Insurance Setting", company, [ "enable", diff --git a/hms_tz/nhif/api/insurance_company.py b/hms_tz/nhif/api/insurance_company.py index b8912d1f..dcb9d272 100644 --- a/hms_tz/nhif/api/insurance_company.py +++ b/hms_tz/nhif/api/insurance_company.py @@ -21,6 +21,7 @@ from time import sleep + @frappe.whitelist() def enqueue_get_nhif_price_package(company): enqueue( @@ -38,7 +39,7 @@ def get_nhif_price_package(kwargs): user = frappe.session.user token = get_claimsservice_token(company) claimsserver_url, facility_code = frappe.get_cached_value( - "Company NHIF Settings", company, ["claimsserver_url", "facility_code"] + "Company Insurance Setting", company, ["claimsserver_url", "facility_code"] ) headers = {"Authorization": "Bearer " + token} url = ( @@ -53,13 +54,15 @@ def get_nhif_price_package(kwargs): request_url=url, request_header=headers, response_data=r.text, - status_code=r.status_code + status_code=r.status_code, ) frappe.throw(json.loads(r.text)) else: if json.loads(r.text): frappe.db.sql( - """DELETE FROM `tabNHIF Price Package` WHERE company = '{0}' """.format(company) + """DELETE FROM `tabNHIF Price Package` WHERE company = '{0}' """.format( + company + ) ) frappe.db.sql( """DELETE FROM `tabNHIF Excluded Services` WHERE company = '{0}' """.format( @@ -76,7 +79,7 @@ def get_nhif_price_package(kwargs): request_url=url, request_header=headers, response_data=r.text, - status_code=r.status_code + status_code=r.status_code, ) time_stamp = now() data = json.loads(r.text) @@ -190,7 +193,9 @@ def process_nhif_records(company): def process_prices_list(kwargs): company = kwargs - facility_code = frappe.get_cached_value("Company NHIF Settings", company, "facility_code") + facility_code = frappe.get_cached_value( + "Company Insurance Setting", company, "facility_code" + ) currency = frappe.get_cached_value("Company", company, "default_currency") schemeid_list = frappe.db.sql( """ @@ -257,13 +262,11 @@ def process_prices_list(kwargs): ) if len(item_price_list) > 0: for price in item_price_list: - #2023-05-16 + # 2023-05-16 # remove condition for isactive # if package.isactive and int(package.isactive) == 1: - - if float(price.price_list_rate) != float( - package.unitprice - ): + + if float(price.price_list_rate) != float(package.unitprice): # delete Item Price if no package.unitprice or it is 0 if ( not float(package.unitprice) @@ -278,7 +281,7 @@ def process_prices_list(kwargs): float(package.unitprice), ) # else: - # frappe.delete_doc("Item Price", price.name) + # frappe.delete_doc("Item Price", price.name) # elif package.isactive and int(package.isactive) == 1: else: @@ -363,7 +366,9 @@ def get_insurance_coverage_items(company): WHERE icd.customer_name = 'NHIF' AND npp.company = {company} GROUP BY dt, m.name, icd.ref_code , icd.parent, npp.schemeid - """.format(company=frappe.db.escape(company)), + """.format( + company=frappe.db.escape(company) + ), as_dict=1, ) return items_list @@ -487,12 +492,12 @@ def process_insurance_coverages(kwargs): if insert_data: if plan.name: frappe.db.sql( - "DELETE FROM `tabHealthcare Service Insurance Coverage` WHERE is_auto_generated = 1 AND healthcare_insurance_coverage_plan = '{0}'".format( - plan.name - ) + "DELETE FROM `tabHealthcare Service Insurance Coverage` WHERE is_auto_generated = 1 AND healthcare_insurance_coverage_plan = '{0}'".format( + plan.name ) + ) frappe.db.commit() - + # wait for 60 seconds before creating HSIC records again sleep(60) diff --git a/hms_tz/nhif/api/patient.py b/hms_tz/nhif/api/patient.py index fb6f4f1e..97907a24 100644 --- a/hms_tz/nhif/api/patient.py +++ b/hms_tz/nhif/api/patient.py @@ -63,14 +63,14 @@ def get_patient_info(card_no=None): company = frappe.defaults.get_user_default("Company") if not company: company = frappe.get_list( - "Company NHIF Settings", fields=["company"], filters={"enable": 1} + "Company Insurance Setting", fields=["company"], filters={"enable": 1} )[0].company if not company: frappe.throw(_("No companies found to connect to NHIF")) token = get_nhifservice_token(company) service_url = frappe.get_cached_value( - "Company NHIF Settings", company, "service_url" + "Company Insurance Setting", company, "service_url" ) headers = {"Authorization": "Bearer " + token} url = ( @@ -120,7 +120,7 @@ def update_patient_history(doc): # Remarked till multi company setting is required and feasible from Patient doctype 2021-03-20 19:57:14 # company = get_default_company() # update_history = frappe.get_cached_value( - # "Company NHIF Settings", company, "update_patient_history") + # "Company Insurance Setting", company, "update_patient_history") # if not update_history: # return diff --git a/hms_tz/nhif/api/patient_appointment.py b/hms_tz/nhif/api/patient_appointment.py index a9450b90..202e1c1c 100644 --- a/hms_tz/nhif/api/patient_appointment.py +++ b/hms_tz/nhif/api/patient_appointment.py @@ -358,7 +358,7 @@ def get_authorization_num( remarks="", ): enable_nhif_api, service_url = frappe.get_cached_value( - "Company NHIF Settings", company, ["enable", "service_url"] + "Company Insurance Setting", company, ["enable", "service_url"] ) if not enable_nhif_api: frappe.msgprint( diff --git a/hms_tz/nhif/api/token.py b/hms_tz/nhif/api/token.py index a36e1a99..127f9d8b 100644 --- a/hms_tz/nhif/api/token.py +++ b/hms_tz/nhif/api/token.py @@ -59,7 +59,7 @@ def make_token_request(doc, url, headers, payload, fields): def get_nhifservice_token(company): - setting_doc = frappe.get_cached_doc("Company NHIF Settings", company) + setting_doc = frappe.get_cached_doc("Company Insurance Setting", company) if ( setting_doc.service_token_expiry and setting_doc.service_token_expiry > now_datetime() @@ -67,7 +67,7 @@ def get_nhifservice_token(company): return setting_doc.service_token username = setting_doc.username - password = get_decrypted_password("Company NHIF Settings", company, "password") + password = get_decrypted_password("Company Insurance Setting", company, "password") payload = "grant_type=password&username={0}&password={1}".format(username, password) headers = {"Content-Type": "application/x-www-form-urlencoded"} url = str(setting_doc.service_url) + "/nhifservice/Token" @@ -81,7 +81,7 @@ def get_nhifservice_token(company): def get_claimsservice_token(company): - setting_doc = frappe.get_cached_doc("Company NHIF Settings", company) + setting_doc = frappe.get_cached_doc("Company Insurance Setting", company) if ( setting_doc.claimsserver_expiry and setting_doc.claimsserver_expiry > now_datetime() @@ -89,7 +89,7 @@ def get_claimsservice_token(company): return setting_doc.claimsserver_token username = setting_doc.username - password = get_decrypted_password("Company NHIF Settings", company, "password") + password = get_decrypted_password("Company Insurance Setting", company, "password") payload = "grant_type=password&username={0}&password={1}".format(username, password) headers = {"Content-Type": "application/x-www-form-urlencoded"} url = str(setting_doc.claimsserver_url) + "/claimsserver/Token" @@ -103,7 +103,7 @@ def get_claimsservice_token(company): def get_formservice_token(company): - company_nhif_doc = frappe.get_cached_doc("Company NHIF Settings", company) + company_nhif_doc = frappe.get_cached_doc("Company Insurance Setting", company) if not company_nhif_doc.enable: frappe.throw(_("Company {0} not enabled for NHIF Integration".format(company))) @@ -114,7 +114,7 @@ def get_formservice_token(company): return company_nhif_doc.form_token username = company_nhif_doc.username - password = get_decrypted_password("Company NHIF Settings", company, "password") + password = get_decrypted_password("Company Insurance Setting", company, "password") payload = "grant_type=password&username={0}&password={1}".format(username, password) headers = {"Content-Type": "application/x-www-form-urlencoded"} diff --git a/hms_tz/nhif/doctype/healthcare_referral/healthcare_referral.json b/hms_tz/nhif/doctype/healthcare_referral/healthcare_referral.json index 27b8c61f..3c85ec5d 100644 --- a/hms_tz/nhif/doctype/healthcare_referral/healthcare_referral.json +++ b/hms_tz/nhif/doctype/healthcare_referral/healthcare_referral.json @@ -1,263 +1,263 @@ { - "actions": [], - "allow_rename": 1, - "autoname": "naming_series:", - "creation": "2022-06-29 17:17:49.625315", - "doctype": "DocType", - "editable_grid": 1, - "engine": "InnoDB", - "field_order": [ - "appointment", - "patient_name", - "gender", - "status_column_break", - "referral_status", - "card_no", - "authorization_number", - "posting_date", - "facility_section_break", - "source_facility", - "source_facility_code", - "facility_column_break", - "referrer_facility", - "referrer_facility_code", - "practitioner_section_break", - "practitioner", - "mobile_no", - "qualification_column_break", - "nhif_physician_qualification", - "reason_section", - "reason_for_referral", - "reason_column_break", - "referring_diagnosis", - "referral_section_break", - "referral_no", - "referralid", - "referringdate_column_break", - "referringdate", - "naming_series", - "amended_from" - ], - "fields": [ - { - "fieldname": "appointment", - "fieldtype": "Link", - "in_standard_filter": 1, - "label": "Patient Appointment", - "options": "Patient Appointment", - "reqd": 1 - }, - { - "fetch_from": "appointment.patient_name", - "fieldname": "patient_name", - "fieldtype": "Data", - "in_list_view": 1, - "in_standard_filter": 1, - "label": "Patient Name", - "read_only": 1 - }, - { - "fetch_from": "appointment.patient_sex", - "fieldname": "gender", - "fieldtype": "Select", - "label": "Gender", - "options": "\nFemale\nMale", - "read_only": 1 - }, - { - "fetch_from": "appointment.coverage_plan_card_number", - "fieldname": "card_no", - "fieldtype": "Data", - "in_list_view": 1, - "in_standard_filter": 1, - "label": "CardNo", - "read_only": 1 - }, - { - "fetch_from": "appointment.authorization_number", - "fieldname": "authorization_number", - "fieldtype": "Data", - "in_list_view": 1, - "in_standard_filter": 1, - "label": "AuthorizationNo", - "read_only": 1 - }, - { - "fetch_from": "source_facility.facility_code", - "fieldname": "source_facility_code", - "fieldtype": "Data", - "label": "Source Facility Code", - "read_only": 1 - }, - { - "fieldname": "practitioner", - "fieldtype": "Link", - "in_standard_filter": 1, - "label": "Healthcare Practitioner", - "options": "Healthcare Practitioner", - "reqd": 1 - }, - { - "fieldname": "mobile_no", - "fieldtype": "Data", - "label": "Practitioner MobileNo" - }, - { - "fieldname": "posting_date", - "fieldtype": "Datetime", - "in_standard_filter": 1, - "label": "Posting Date", - "read_only": 1 - }, - { - "fieldname": "reason_for_referral", - "fieldtype": "Small Text", - "label": "Reason For Referral", - "reqd": 1 - }, - { - "fieldname": "reason_column_break", - "fieldtype": "Column Break" - }, - { - "fieldname": "referring_diagnosis", - "fieldtype": "Small Text", - "label": "Referring Diagnosis", - "reqd": 1 - }, - { - "fieldname": "referral_section_break", - "fieldtype": "Section Break", - "label": "Referral Info" - }, - { - "bold": 1, - "fieldname": "referral_no", - "fieldtype": "Data", - "in_list_view": 1, - "label": "ReferralNo", - "read_only": 1 - }, - { - "fieldname": "referralid", - "fieldtype": "Data", - "label": "ReferralID", - "read_only": 1 - }, - { - "fieldname": "referringdate", - "fieldtype": "Date", - "label": "ReferringDate", - "read_only": 1 - }, - { - "fieldname": "naming_series", - "fieldtype": "Select", - "label": "Series", - "options": "HLC-.R-.YYYY.-", - "read_only": 1 - }, - { - "fieldname": "status_column_break", - "fieldtype": "Column Break" - }, - { - "fieldname": "referral_status", - "fieldtype": "Select", - "in_list_view": 1, - "label": "Referal Status", - "options": "Pending\nSuccess", - "read_only": 1 - }, - { - "fieldname": "facility_section_break", - "fieldtype": "Section Break", - "label": "Health Facility info" - }, - { - "fetch_from": "appointment.company", - "fieldname": "source_facility", - "fieldtype": "Link", - "in_standard_filter": 1, - "label": "Source Facility", - "options": "Company NHIF Settings", - "read_only": 1 - }, - { - "fieldname": "facility_column_break", - "fieldtype": "Column Break" - }, - { - "fieldname": "practitioner_section_break", - "fieldtype": "Section Break", - "label": "Practitioner Info" - }, - { - "fieldname": "qualification_column_break", - "fieldtype": "Column Break" - }, - { - "fieldname": "reason_section", - "fieldtype": "Section Break", - "label": "Referring Reason &Diagnosis" - }, - { - "fieldname": "referringdate_column_break", - "fieldtype": "Column Break" - }, - { - "fieldname": "referrer_facility", - "fieldtype": "Link", - "label": "Referrer Facility", - "options": "Healthcare External Referrer", - "reqd": 1 - }, - { - "fetch_from": "referrer_facility.facility_code", - "fieldname": "referrer_facility_code", - "fieldtype": "Data", - "label": "Referrer Facility Code", - "read_only": 1 - }, - { - "fieldname": "amended_from", - "fieldtype": "Link", - "label": "Amended From", - "no_copy": 1, - "options": "Healthcare Referral", - "print_hide": 1, - "read_only": 1 - }, - { - "fetch_from": "practitioner.nhif_physician_qualification", - "fieldname": "nhif_physician_qualification", - "fieldtype": "Data", - "label": "NHIF Physician Qualifiaction", - "read_only": 1 - } - ], - "index_web_pages_for_search": 1, - "is_submittable": 1, - "links": [], - "modified": "2022-06-30 12:18:38.471974", - "modified_by": "Administrator", - "module": "NHIF", - "name": "Healthcare Referral", - "owner": "Administrator", - "permissions": [ - { - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "System Manager", - "share": 1, - "write": 1 - } - ], - "sort_field": "modified", - "sort_order": "DESC" + "actions": [], + "allow_rename": 1, + "autoname": "naming_series:", + "creation": "2022-06-29 17:17:49.625315", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "appointment", + "patient_name", + "gender", + "status_column_break", + "referral_status", + "card_no", + "authorization_number", + "posting_date", + "facility_section_break", + "source_facility", + "source_facility_code", + "facility_column_break", + "referrer_facility", + "referrer_facility_code", + "practitioner_section_break", + "practitioner", + "mobile_no", + "qualification_column_break", + "nhif_physician_qualification", + "reason_section", + "reason_for_referral", + "reason_column_break", + "referring_diagnosis", + "referral_section_break", + "referral_no", + "referralid", + "referringdate_column_break", + "referringdate", + "naming_series", + "amended_from" + ], + "fields": [ + { + "fieldname": "appointment", + "fieldtype": "Link", + "in_standard_filter": 1, + "label": "Patient Appointment", + "options": "Patient Appointment", + "reqd": 1 + }, + { + "fetch_from": "appointment.patient_name", + "fieldname": "patient_name", + "fieldtype": "Data", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Patient Name", + "read_only": 1 + }, + { + "fetch_from": "appointment.patient_sex", + "fieldname": "gender", + "fieldtype": "Select", + "label": "Gender", + "options": "\nFemale\nMale", + "read_only": 1 + }, + { + "fetch_from": "appointment.coverage_plan_card_number", + "fieldname": "card_no", + "fieldtype": "Data", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "CardNo", + "read_only": 1 + }, + { + "fetch_from": "appointment.authorization_number", + "fieldname": "authorization_number", + "fieldtype": "Data", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "AuthorizationNo", + "read_only": 1 + }, + { + "fetch_from": "source_facility.facility_code", + "fieldname": "source_facility_code", + "fieldtype": "Data", + "label": "Source Facility Code", + "read_only": 1 + }, + { + "fieldname": "practitioner", + "fieldtype": "Link", + "in_standard_filter": 1, + "label": "Healthcare Practitioner", + "options": "Healthcare Practitioner", + "reqd": 1 + }, + { + "fieldname": "mobile_no", + "fieldtype": "Data", + "label": "Practitioner MobileNo" + }, + { + "fieldname": "posting_date", + "fieldtype": "Datetime", + "in_standard_filter": 1, + "label": "Posting Date", + "read_only": 1 + }, + { + "fieldname": "reason_for_referral", + "fieldtype": "Small Text", + "label": "Reason For Referral", + "reqd": 1 + }, + { + "fieldname": "reason_column_break", + "fieldtype": "Column Break" + }, + { + "fieldname": "referring_diagnosis", + "fieldtype": "Small Text", + "label": "Referring Diagnosis", + "reqd": 1 + }, + { + "fieldname": "referral_section_break", + "fieldtype": "Section Break", + "label": "Referral Info" + }, + { + "bold": 1, + "fieldname": "referral_no", + "fieldtype": "Data", + "in_list_view": 1, + "label": "ReferralNo", + "read_only": 1 + }, + { + "fieldname": "referralid", + "fieldtype": "Data", + "label": "ReferralID", + "read_only": 1 + }, + { + "fieldname": "referringdate", + "fieldtype": "Date", + "label": "ReferringDate", + "read_only": 1 + }, + { + "fieldname": "naming_series", + "fieldtype": "Select", + "label": "Series", + "options": "HLC-.R-.YYYY.-", + "read_only": 1 + }, + { + "fieldname": "status_column_break", + "fieldtype": "Column Break" + }, + { + "fieldname": "referral_status", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Referal Status", + "options": "Pending\nSuccess", + "read_only": 1 + }, + { + "fieldname": "facility_section_break", + "fieldtype": "Section Break", + "label": "Health Facility info" + }, + { + "fetch_from": "appointment.company", + "fieldname": "source_facility", + "fieldtype": "Link", + "in_standard_filter": 1, + "label": "Source Facility", + "options": "Company Insurance Setting", + "read_only": 1 + }, + { + "fieldname": "facility_column_break", + "fieldtype": "Column Break" + }, + { + "fieldname": "practitioner_section_break", + "fieldtype": "Section Break", + "label": "Practitioner Info" + }, + { + "fieldname": "qualification_column_break", + "fieldtype": "Column Break" + }, + { + "fieldname": "reason_section", + "fieldtype": "Section Break", + "label": "Referring Reason &Diagnosis" + }, + { + "fieldname": "referringdate_column_break", + "fieldtype": "Column Break" + }, + { + "fieldname": "referrer_facility", + "fieldtype": "Link", + "label": "Referrer Facility", + "options": "Healthcare External Referrer", + "reqd": 1 + }, + { + "fetch_from": "referrer_facility.facility_code", + "fieldname": "referrer_facility_code", + "fieldtype": "Data", + "label": "Referrer Facility Code", + "read_only": 1 + }, + { + "fieldname": "amended_from", + "fieldtype": "Link", + "label": "Amended From", + "no_copy": 1, + "options": "Healthcare Referral", + "print_hide": 1, + "read_only": 1 + }, + { + "fetch_from": "practitioner.nhif_physician_qualification", + "fieldname": "nhif_physician_qualification", + "fieldtype": "Data", + "label": "NHIF Physician Qualifiaction", + "read_only": 1 + } + ], + "index_web_pages_for_search": 1, + "is_submittable": 1, + "links": [], + "modified": "2022-06-30 12:18:38.471974", + "modified_by": "Administrator", + "module": "NHIF", + "name": "Healthcare Referral", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC" } \ No newline at end of file diff --git a/hms_tz/nhif/doctype/healthcare_referral/healthcare_referral.py b/hms_tz/nhif/doctype/healthcare_referral/healthcare_referral.py index 54f39376..d4b3529a 100644 --- a/hms_tz/nhif/doctype/healthcare_referral/healthcare_referral.py +++ b/hms_tz/nhif/doctype/healthcare_referral/healthcare_referral.py @@ -22,7 +22,7 @@ def get_referral_no(name): validate_required_fields(doc) service_url = frappe.get_cached_value( - "Company NHIF Settings", doc.source_facility, "service_url" + "Company Insurance Setting", doc.source_facility, "service_url" ) token = get_nhifservice_token(doc.source_facility) diff --git a/hms_tz/nhif/doctype/nhif_claim_reconciliation/nhif_claim_reconciliation.json b/hms_tz/nhif/doctype/nhif_claim_reconciliation/nhif_claim_reconciliation.json index 3a186420..8696e0f1 100644 --- a/hms_tz/nhif/doctype/nhif_claim_reconciliation/nhif_claim_reconciliation.json +++ b/hms_tz/nhif/doctype/nhif_claim_reconciliation/nhif_claim_reconciliation.json @@ -1,149 +1,149 @@ { - "actions": [], - "allow_rename": 1, - "autoname": "NCR-.#######", - "creation": "2022-08-22 13:22:02.826140", - "doctype": "DocType", - "editable_grid": 1, - "engine": "InnoDB", - "field_order": [ - "company", - "claim_year", - "claim_month", - "status_column_break", - "status", - "posting_date", - "get_detail", - "section_break_ts", - "number_of_submitted_claims", - "column_break_ac", - "total_amount_claimed", - "section_break_cards", - "claim_details", - "amended_from" - ], - "fields": [ - { - "fieldname": "company", - "fieldtype": "Link", - "in_global_search": 1, - "in_list_view": 1, - "in_standard_filter": 1, - "label": "Company", - "options": "Company NHIF Settings", - "reqd": 1, - "search_index": 1 - }, - { - "fieldname": "claim_year", - "fieldtype": "Data", - "in_global_search": 1, - "in_list_view": 1, - "in_standard_filter": 1, - "label": "Claim Year", - "reqd": 1, - "search_index": 1 - }, - { - "fieldname": "claim_month", - "fieldtype": "Int", - "in_list_view": 1, - "in_standard_filter": 1, - "label": "Claim Month", - "reqd": 1, - "search_index": 1 - }, - { - "fieldname": "section_break_ts", - "fieldtype": "Section Break" - }, - { - "fieldname": "column_break_ac", - "fieldtype": "Column Break" - }, - { - "bold": 1, - "fieldname": "total_amount_claimed", - "fieldtype": "Currency", - "label": "Total Amount Claimed", - "read_only": 1 - }, - { - "fieldname": "section_break_cards", - "fieldtype": "Section Break", - "label": "Claimed Cards" - }, - { - "fieldname": "claim_details", - "fieldtype": "Table", - "options": "NHIF Claim Reconciliation Detail", - "read_only": 1 - }, - { - "fieldname": "amended_from", - "fieldtype": "Link", - "label": "Amended From", - "no_copy": 1, - "options": "NHIF Claim Reconciliation", - "print_hide": 1, - "read_only": 1 - }, - { - "fieldname": "status_column_break", - "fieldtype": "Column Break" - }, - { - "fieldname": "status", - "fieldtype": "Select", - "label": "Status", - "options": "Pending\nSuccessful", - "read_only": 1 - }, - { - "fieldname": "posting_date", - "fieldtype": "Date", - "label": "Posting Date", - "read_only": 1 - }, - { - "bold": 1, - "fieldname": "number_of_submitted_claims", - "fieldtype": "Int", - "label": "Number of Submitted Claims", - "read_only": 1 - }, - { - "depends_on": "eval: doc.docstatus==0", - "fieldname": "get_detail", - "fieldtype": "Button", - "label": "Get Detail" - } - ], - "index_web_pages_for_search": 1, - "is_submittable": 1, - "links": [], - "modified": "2022-08-27 13:04:56.427583", - "modified_by": "Administrator", - "module": "NHIF", - "name": "NHIF Claim Reconciliation", - "owner": "Administrator", - "permissions": [ - { - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "System Manager", - "share": 1, - "write": 1 - } - ], - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1, - "track_seen": 1, - "track_views": 1 + "actions": [], + "allow_rename": 1, + "autoname": "NCR-.#######", + "creation": "2022-08-22 13:22:02.826140", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "company", + "claim_year", + "claim_month", + "status_column_break", + "status", + "posting_date", + "get_detail", + "section_break_ts", + "number_of_submitted_claims", + "column_break_ac", + "total_amount_claimed", + "section_break_cards", + "claim_details", + "amended_from" + ], + "fields": [ + { + "fieldname": "company", + "fieldtype": "Link", + "in_global_search": 1, + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Company", + "options": "Company Insurance Setting", + "reqd": 1, + "search_index": 1 + }, + { + "fieldname": "claim_year", + "fieldtype": "Data", + "in_global_search": 1, + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Claim Year", + "reqd": 1, + "search_index": 1 + }, + { + "fieldname": "claim_month", + "fieldtype": "Int", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Claim Month", + "reqd": 1, + "search_index": 1 + }, + { + "fieldname": "section_break_ts", + "fieldtype": "Section Break" + }, + { + "fieldname": "column_break_ac", + "fieldtype": "Column Break" + }, + { + "bold": 1, + "fieldname": "total_amount_claimed", + "fieldtype": "Currency", + "label": "Total Amount Claimed", + "read_only": 1 + }, + { + "fieldname": "section_break_cards", + "fieldtype": "Section Break", + "label": "Claimed Cards" + }, + { + "fieldname": "claim_details", + "fieldtype": "Table", + "options": "NHIF Claim Reconciliation Detail", + "read_only": 1 + }, + { + "fieldname": "amended_from", + "fieldtype": "Link", + "label": "Amended From", + "no_copy": 1, + "options": "NHIF Claim Reconciliation", + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "status_column_break", + "fieldtype": "Column Break" + }, + { + "fieldname": "status", + "fieldtype": "Select", + "label": "Status", + "options": "Pending\nSuccessful", + "read_only": 1 + }, + { + "fieldname": "posting_date", + "fieldtype": "Date", + "label": "Posting Date", + "read_only": 1 + }, + { + "bold": 1, + "fieldname": "number_of_submitted_claims", + "fieldtype": "Int", + "label": "Number of Submitted Claims", + "read_only": 1 + }, + { + "depends_on": "eval: doc.docstatus==0", + "fieldname": "get_detail", + "fieldtype": "Button", + "label": "Get Detail" + } + ], + "index_web_pages_for_search": 1, + "is_submittable": 1, + "links": [], + "modified": "2022-08-27 13:04:56.427583", + "modified_by": "Administrator", + "module": "NHIF", + "name": "NHIF Claim Reconciliation", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1, + "track_seen": 1, + "track_views": 1 } \ No newline at end of file diff --git a/hms_tz/nhif/doctype/nhif_claim_reconciliation/nhif_claim_reconciliation.py b/hms_tz/nhif/doctype/nhif_claim_reconciliation/nhif_claim_reconciliation.py index 47d554dc..802d67a2 100644 --- a/hms_tz/nhif/doctype/nhif_claim_reconciliation/nhif_claim_reconciliation.py +++ b/hms_tz/nhif/doctype/nhif_claim_reconciliation/nhif_claim_reconciliation.py @@ -9,110 +9,120 @@ from hms_tz.nhif.api.token import get_claimsservice_token from hms_tz.nhif.doctype.nhif_response_log.nhif_response_log import add_log + class NHIFClaimReconciliation(Document): - def validate(self): - if not self.posting_date: - self.posting_date = nowdate() - - def validate_reqd_fields(self): - for fieldname in ["company", "claim_year", "claim_month"]: - if not self.get(fieldname): - frappe.throw(frappe.bold("{0} is required".format(fieldname))) - - def before_submit(self): - if self.status == "Pending": - frappe.throw("Cannot submit a pending reconciliation") + def validate(self): + if not self.posting_date: + self.posting_date = nowdate() + + def validate_reqd_fields(self): + for fieldname in ["company", "claim_year", "claim_month"]: + if not self.get(fieldname): + frappe.throw(frappe.bold("{0} is required".format(fieldname))) + + def before_submit(self): + if self.status == "Pending": + frappe.throw("Cannot submit a pending reconciliation") @frappe.whitelist() def get_submitted_claims(self): - doc = frappe.get_doc(frappe.parse_json(self)) - doc.validate_reqd_fields() + doc = frappe.get_doc(frappe.parse_json(self)) + doc.validate_reqd_fields() - token = get_claimsservice_token(doc.get("company")) - headers = { - "Content-Type": "application/json", - "Authorization": "Bearer " + token - } + token = get_claimsservice_token(doc.get("company")) + headers = {"Content-Type": "application/json", "Authorization": "Bearer " + token} - facility_code, base_url = frappe.get_cached_value("Company NHIF Settings", doc.company, ["facility_code", "claimsserver_url"]) - params = "FacilityCode={0}&ClaimYear={1}&ClaimMonth={2}".format(facility_code, doc.claim_year, doc.claim_month) + facility_code, base_url = frappe.get_cached_value( + "Company Insurance Setting", doc.company, ["facility_code", "claimsserver_url"] + ) + params = "FacilityCode={0}&ClaimYear={1}&ClaimMonth={2}".format( + facility_code, doc.claim_year, doc.claim_month + ) - url = "{0}/claimsserver/api/v1/claims/getSubmittedClaims?{1}".format(base_url, params) + url = "{0}/claimsserver/api/v1/claims/getSubmittedClaims?{1}".format( + base_url, params + ) - data = make_request(url, headers, params) + data = make_request(url, headers, params) - if len(data) > 0: - return update_reconciliation_detail(doc, data) - - else: - frappe.msgprint("No record found for facility: {0}, claim year: {1} and claim month: {2}".format( - frappe.bold(doc.company), frappe.bold(doc.claim_year), frappe.bold(doc.claim_month) - )) - return False + if len(data) > 0: + return update_reconciliation_detail(doc, data) + + else: + frappe.msgprint( + "No record found for facility: {0}, claim year: {1} and claim month: {2}".format( + frappe.bold(doc.company), + frappe.bold(doc.claim_year), + frappe.bold(doc.claim_month), + ) + ) + return False def make_request(url, headers, payload): - try: - response = requests.get(url, headers=headers) - if response.status_code == 200: - data = json.loads(response.text) - add_log( - request_type="GetSubmittedClaims", - request_url=url, - request_header=headers, - request_body=payload, - response_data="total_records: {0}".format(len(data)), - status_code=response.status_code, - ) - return data - - else: - add_log( - request_type="GetSubmittedClaims", - request_url=url, - request_header=headers, - request_body=payload, - response_data=response.text, - status_code=response.status_code, - ) - data = json.loads(response.text) - frappe.throw("Message: {0}".format(frappe.bold(data["Message"]))) - - except Exception as e: - raise e - frappe.throw(str(e)) + try: + response = requests.get(url, headers=headers) + if response.status_code == 200: + data = json.loads(response.text) + add_log( + request_type="GetSubmittedClaims", + request_url=url, + request_header=headers, + request_body=payload, + response_data="total_records: {0}".format(len(data)), + status_code=response.status_code, + ) + return data + + else: + add_log( + request_type="GetSubmittedClaims", + request_url=url, + request_header=headers, + request_body=payload, + response_data=response.text, + status_code=response.status_code, + ) + data = json.loads(response.text) + frappe.throw("Message: {0}".format(frappe.bold(data["Message"]))) + + except Exception as e: + raise e + frappe.throw(str(e)) def update_reconciliation_detail(self, records): - total_amount = 0 - self.status = "Successful" - self.number_of_submitted_claims = len(records) - - self.claim_details = [] - for record in records: - total_amount += flt(record["AmountClaimed"]) - self.append("claim_details", { - "foliono": record["FolioNo"], - "billno": record["BillNo"], - "datesubmitted": record["DateSubmitted"], - "cardno": record["CardNo"], - "authorizationno": record["AuthorizationNo"], - "amountclaimed": flt(record["AmountClaimed"]), - "submissionid": record["SubmissionID"], - "submissionno": record["SubmissionNo"], - "remarks": record["Remarks"], - }) - - self.total_amount_claimed = total_amount - - self.save(ignore_permissions=True) - frappe.db.commit() - - self.reload() - - if len(self.claim_details) > 0: - self.submit() - - return True - + total_amount = 0 + self.status = "Successful" + self.number_of_submitted_claims = len(records) + + self.claim_details = [] + for record in records: + total_amount += flt(record["AmountClaimed"]) + self.append( + "claim_details", + { + "foliono": record["FolioNo"], + "billno": record["BillNo"], + "datesubmitted": record["DateSubmitted"], + "cardno": record["CardNo"], + "authorizationno": record["AuthorizationNo"], + "amountclaimed": flt(record["AmountClaimed"]), + "submissionid": record["SubmissionID"], + "submissionno": record["SubmissionNo"], + "remarks": record["Remarks"], + }, + ) + + self.total_amount_claimed = total_amount + + self.save(ignore_permissions=True) + frappe.db.commit() + + self.reload() + + if len(self.claim_details) > 0: + self.submit() + + return True diff --git a/hms_tz/nhif/doctype/nhif_custom_excluded_services/nhif_custom_excluded_services.json b/hms_tz/nhif/doctype/nhif_custom_excluded_services/nhif_custom_excluded_services.json index 25dec005..659cb03f 100644 --- a/hms_tz/nhif/doctype/nhif_custom_excluded_services/nhif_custom_excluded_services.json +++ b/hms_tz/nhif/doctype/nhif_custom_excluded_services/nhif_custom_excluded_services.json @@ -1,117 +1,117 @@ { - "actions": [], - "allow_rename": 1, - "autoname": "NCXS-.YY.-.MM.-.DD.-.#####", - "creation": "2022-04-08 16:04:53.235211", - "doctype": "DocType", - "editable_grid": 1, - "engine": "InnoDB", - "field_order": [ - "company", - "time_stamp", - "column_break_1", - "facilitycode", - "title", - "section_break_1", - "item", - "column_break_2", - "itemcode", - "section_break_2", - "excludedforproducts" - ], - "fields": [ - { - "fieldname": "company", - "fieldtype": "Link", - "in_standard_filter": 1, - "label": "Company", - "options": "Company NHIF Settings", - "reqd": 1 - }, - { - "fieldname": "time_stamp", - "fieldtype": "Datetime", - "in_standard_filter": 1, - "label": "Time Stamp", - "read_only": 1 - }, - { - "fieldname": "column_break_1", - "fieldtype": "Column Break" - }, - { - "fetch_from": "company.facility_code", - "fieldname": "facilitycode", - "fieldtype": "Data", - "label": "FacilityCode", - "read_only": 1 - }, - { - "fieldname": "section_break_1", - "fieldtype": "Section Break" - }, - { - "fieldname": "item", - "fieldtype": "Link", - "label": "Item", - "options": "Item", - "reqd": 1 - }, - { - "fieldname": "column_break_2", - "fieldtype": "Column Break" - }, - { - "fieldname": "itemcode", - "fieldtype": "Data", - "in_list_view": 1, - "in_standard_filter": 1, - "label": "ItemCode" - }, - { - "fieldname": "section_break_2", - "fieldtype": "Section Break" - }, - { - "fieldname": "excludedforproducts", - "fieldtype": "Small Text", - "in_list_view": 1, - "label": "ExcludedForProducts", - "reqd": 1 - }, - { - "fieldname": "title", - "fieldtype": "Data", - "hidden": 1, - "in_list_view": 1, - "in_standard_filter": 1, - "label": "Title", - "read_only": 1 - } - ], - "index_web_pages_for_search": 1, - "links": [], - "modified": "2022-04-09 21:14:50.842195", - "modified_by": "Administrator", - "module": "NHIF", - "name": "NHIF Custom Excluded Services", - "owner": "Administrator", - "permissions": [ - { - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "System Manager", - "share": 1, - "write": 1 - } - ], - "sort_field": "modified", - "sort_order": "DESC", - "title_field": "title", - "track_changes": 1 + "actions": [], + "allow_rename": 1, + "autoname": "NCXS-.YY.-.MM.-.DD.-.#####", + "creation": "2022-04-08 16:04:53.235211", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "company", + "time_stamp", + "column_break_1", + "facilitycode", + "title", + "section_break_1", + "item", + "column_break_2", + "itemcode", + "section_break_2", + "excludedforproducts" + ], + "fields": [ + { + "fieldname": "company", + "fieldtype": "Link", + "in_standard_filter": 1, + "label": "Company", + "options": "Company Insurance Setting", + "reqd": 1 + }, + { + "fieldname": "time_stamp", + "fieldtype": "Datetime", + "in_standard_filter": 1, + "label": "Time Stamp", + "read_only": 1 + }, + { + "fieldname": "column_break_1", + "fieldtype": "Column Break" + }, + { + "fetch_from": "company.facility_code", + "fieldname": "facilitycode", + "fieldtype": "Data", + "label": "FacilityCode", + "read_only": 1 + }, + { + "fieldname": "section_break_1", + "fieldtype": "Section Break" + }, + { + "fieldname": "item", + "fieldtype": "Link", + "label": "Item", + "options": "Item", + "reqd": 1 + }, + { + "fieldname": "column_break_2", + "fieldtype": "Column Break" + }, + { + "fieldname": "itemcode", + "fieldtype": "Data", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "ItemCode" + }, + { + "fieldname": "section_break_2", + "fieldtype": "Section Break" + }, + { + "fieldname": "excludedforproducts", + "fieldtype": "Small Text", + "in_list_view": 1, + "label": "ExcludedForProducts", + "reqd": 1 + }, + { + "fieldname": "title", + "fieldtype": "Data", + "hidden": 1, + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Title", + "read_only": 1 + } + ], + "index_web_pages_for_search": 1, + "links": [], + "modified": "2022-04-09 21:14:50.842195", + "modified_by": "Administrator", + "module": "NHIF", + "name": "NHIF Custom Excluded Services", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "title_field": "title", + "track_changes": 1 } \ No newline at end of file diff --git a/hms_tz/nhif/doctype/nhif_patient_claim/nhif_patient_claim.py b/hms_tz/nhif/doctype/nhif_patient_claim/nhif_patient_claim.py index 7e715d56..d993b94d 100644 --- a/hms_tz/nhif/doctype/nhif_patient_claim/nhif_patient_claim.py +++ b/hms_tz/nhif/doctype/nhif_patient_claim/nhif_patient_claim.py @@ -194,7 +194,7 @@ def set_claim_values(self): if not self.folio_id: self.folio_id = str(uuid.uuid1()) self.facility_code = frappe.get_cached_value( - "Company NHIF Settings", self.company, "facility_code" + "Company Insurance Setting", self.company, "facility_code" ) self.posting_date = nowdate() self.serial_no = int(self.name[-9:]) @@ -584,7 +584,7 @@ def set_patient_claim_item(self, inpatient_record=None, called_method=None): for row in encounter_doc.get(child.get("table")): if row.prescribe or row.is_cancelled: continue - + item_code = frappe.get_value( child.get("doctype"), row.get(child.get("item")), @@ -597,9 +597,9 @@ def set_patient_claim_item(self, inpatient_record=None, called_method=None): row.get("quantity_returned") or 0 ) elif row.get("doctype") == "Therapy Plan Detail": - delivered_quantity = (row.get("no_of_sessions") or 0) - ( - row.get("sessions_cancelled") or 0 - ) + delivered_quantity = ( + row.get("no_of_sessions") or 0 + ) - (row.get("sessions_cancelled") or 0) else: delivered_quantity = 1 @@ -782,7 +782,7 @@ def send_nhif_claim(self): json_data, json_data_wo_files = self.get_folio_json_data() token = get_claimsservice_token(self.company) claimsserver_url = frappe.get_value( - "Company NHIF Settings", self.company, "claimsserver_url" + "Company Insurance Setting", self.company, "claimsserver_url" ) headers = { "Authorization": "Bearer " + token, @@ -1023,7 +1023,7 @@ def validate_submit_date(self): import calendar submit_claim_month, submit_claim_year = frappe.get_value( - "Company NHIF Settings", + "Company Insurance Setting", self.company, ["submit_claim_month", "submit_claim_year"], ) @@ -1032,14 +1032,14 @@ def validate_submit_date(self): frappe.throw( frappe.bold( "Submit Claim Month or Submit Claim Year not found,\ - please inform IT department to set it on Company NHIF Settings" + please inform IT department to set it on Company Insurance Setting" ) ) if self.claim_month != submit_claim_month or self.claim_year != submit_claim_year: frappe.throw( "Claim Month: {0} or Claim Year: {1} of this document is not same to Submit Claim Month: {2}\ - or Submit Claim Year: {3} on Company NHIF Settings".format( + or Submit Claim Year: {3} on Company Insurance Setting".format( frappe.bold(calendar.month_name[self.claim_month]), frappe.bold(self.claim_year), frappe.bold(calendar.month_name[submit_claim_month]), diff --git a/hms_tz/nhif/report/claims_reconciliation_report/claims_reconciliation_report.py b/hms_tz/nhif/report/claims_reconciliation_report/claims_reconciliation_report.py index 549ba325..c05e7085 100644 --- a/hms_tz/nhif/report/claims_reconciliation_report/claims_reconciliation_report.py +++ b/hms_tz/nhif/report/claims_reconciliation_report/claims_reconciliation_report.py @@ -140,7 +140,9 @@ def get_data(filters): def get_nhif_data(filters): token = get_claimsservice_token(filters.company) claimsserver_url, facility_code = frappe.get_value( - "Company NHIF Settings", filters.company, ["claimsserver_url", "facility_code"] + "Company Insurance Setting", + filters.company, + ["claimsserver_url", "facility_code"], ) headers = {"Authorization": "Bearer " + token, "Content-Type": "application/json"} url = str( diff --git a/hms_tz/nhif/report/nhif_tracking_claim_change_report/nhif_tracking_claim_change_report.js b/hms_tz/nhif/report/nhif_tracking_claim_change_report/nhif_tracking_claim_change_report.js index 6b13c234..d2f24f82 100644 --- a/hms_tz/nhif/report/nhif_tracking_claim_change_report/nhif_tracking_claim_change_report.js +++ b/hms_tz/nhif/report/nhif_tracking_claim_change_report/nhif_tracking_claim_change_report.js @@ -8,7 +8,7 @@ frappe.query_reports["NHIF Tracking Claim Change Report"] = { "fieldname": "company", "label": __("Company"), "fieldtype": "Link", - "options": "Company NHIF Settings", + "options": "Company Insurance Setting", "reqd": 1, }, { @@ -40,7 +40,7 @@ frappe.query_reports["NHIF Tracking Claim Change Report"] = { "label": __("Claim Submitted By"), "fieldtype": "Link", "options": "User", - + }, ], "formatter": (value, row, column, data, default_formatter) => { From c3d9e3050421af3ea24f756acf43e9d920049176 Mon Sep 17 00:00:00 2001 From: av-dev2 Date: Wed, 17 Apr 2024 23:43:09 +0300 Subject: [PATCH 12/25] chore: add providerid and will be used for jubilee token request --- .../company_insurance_setting.json | 28 +++++++++++++++---- 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/hms_tz/nhif/doctype/company_insurance_setting/company_insurance_setting.json b/hms_tz/nhif/doctype/company_insurance_setting/company_insurance_setting.json index 26370770..e85197ee 100644 --- a/hms_tz/nhif/doctype/company_insurance_setting/company_insurance_setting.json +++ b/hms_tz/nhif/doctype/company_insurance_setting/company_insurance_setting.json @@ -1,18 +1,20 @@ { "actions": [], "allow_rename": 1, - "autoname": "field:company", + "autoname": "format:{api_provider}-{abbr}", "creation": "2020-11-10 16:52:35.894771", "doctype": "DocType", "editable_grid": 1, "engine": "InnoDB", "field_order": [ "company", - "facility_code", + "abbr", "username", "password", "column_break_4", + "facility_code", "api_provider", + "providerid", "enable", "validate_service_approval_number_on_lrpmt_documents", "enable_auto_submit_of_claims", @@ -44,8 +46,7 @@ "in_list_view": 1, "label": "Company", "options": "Company", - "reqd": 1, - "unique": 1 + "reqd": 1 }, { "fieldname": "username", @@ -215,15 +216,30 @@ "label": "API Provider", "options": "\nNHIF\nJubilee\nStrategis\nAssemble", "reqd": 1 + }, + { + "fetch_from": "company.abbr", + "fetch_if_empty": 1, + "fieldname": "abbr", + "fieldtype": "Data", + "label": "Abbr", + "read_only": 1 + }, + { + "depends_on": "eval: doc.api_provider == \"Jubilee\"", + "fieldname": "providerid", + "fieldtype": "Data", + "label": "ProviderID", + "mandatory_depends_on": "eval: doc.api_provider == \"Jubilee\"" } ], "index_web_pages_for_search": 1, "links": [], - "modified": "2024-04-17 18:08:51.344756", + "modified": "2024-04-17 19:24:32.362279", "modified_by": "Administrator", "module": "NHIF", "name": "Company Insurance Setting", - "naming_rule": "By fieldname", + "naming_rule": "Expression", "owner": "Administrator", "permissions": [ { From 755cb23785b604baf779a414efc442fdf9d19ed4 Mon Sep 17 00:00:00 2001 From: av-dev2 Date: Wed, 17 Apr 2024 23:43:28 +0300 Subject: [PATCH 13/25] feat: jubilee module --- hms_tz/modules.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hms_tz/modules.txt b/hms_tz/modules.txt index a3f631da..b341f7d7 100644 --- a/hms_tz/modules.txt +++ b/hms_tz/modules.txt @@ -1,2 +1,3 @@ Hms Tz -NHIF \ No newline at end of file +NHIF +Jubilee \ No newline at end of file From 4355e4de3feb46b58cef8e96a5e5ec3977492907 Mon Sep 17 00:00:00 2001 From: av-dev2 Date: Wed, 17 Apr 2024 23:45:04 +0300 Subject: [PATCH 14/25] feat: jubilee response log for storing all request and response log sent to and received from Jubilee Server --- .../doctype/jubilee_response_log/__init__.py | 0 .../jubilee_response_log.js | 8 + .../jubilee_response_log.json | 146 ++++++++++++++++++ .../jubilee_response_log.py | 33 ++++ .../test_jubilee_response_log.py | 9 ++ 5 files changed, 196 insertions(+) create mode 100644 hms_tz/jubilee/doctype/jubilee_response_log/__init__.py create mode 100644 hms_tz/jubilee/doctype/jubilee_response_log/jubilee_response_log.js create mode 100644 hms_tz/jubilee/doctype/jubilee_response_log/jubilee_response_log.json create mode 100644 hms_tz/jubilee/doctype/jubilee_response_log/jubilee_response_log.py create mode 100644 hms_tz/jubilee/doctype/jubilee_response_log/test_jubilee_response_log.py diff --git a/hms_tz/jubilee/doctype/jubilee_response_log/__init__.py b/hms_tz/jubilee/doctype/jubilee_response_log/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/hms_tz/jubilee/doctype/jubilee_response_log/jubilee_response_log.js b/hms_tz/jubilee/doctype/jubilee_response_log/jubilee_response_log.js new file mode 100644 index 00000000..503d20ae --- /dev/null +++ b/hms_tz/jubilee/doctype/jubilee_response_log/jubilee_response_log.js @@ -0,0 +1,8 @@ +// Copyright (c) 2024, Aakvatech and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Jubilee Response Log', { + // refresh: function(frm) { + + // } +}); diff --git a/hms_tz/jubilee/doctype/jubilee_response_log/jubilee_response_log.json b/hms_tz/jubilee/doctype/jubilee_response_log/jubilee_response_log.json new file mode 100644 index 00000000..b0fcba3b --- /dev/null +++ b/hms_tz/jubilee/doctype/jubilee_response_log/jubilee_response_log.json @@ -0,0 +1,146 @@ +{ + "actions": [], + "autoname": "naming_series:", + "creation": "2024-04-17 19:47:17.483482", + "default_view": "List", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "timestamp", + "request_type", + "request_url", + "request_header", + "column_break_6", + "user_id", + "status_code", + "ref_doctype", + "ref_docname", + "request_section", + "request_body", + "section_break_8", + "response_data", + "naming_series" + ], + "fields": [ + { + "default": "Now", + "fieldname": "timestamp", + "fieldtype": "Datetime", + "in_list_view": 1, + "label": "Timestamp", + "read_only": 1 + }, + { + "fieldname": "request_type", + "fieldtype": "Data", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Request Type", + "read_only": 1 + }, + { + "fieldname": "request_url", + "fieldtype": "Data", + "label": "Request URL", + "length": 1000, + "read_only": 1 + }, + { + "fieldname": "status_code", + "fieldtype": "Data", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Response Status Code", + "read_only": 1, + "search_index": 1 + }, + { + "fieldname": "column_break_6", + "fieldtype": "Column Break" + }, + { + "fieldname": "user_id", + "fieldtype": "Data", + "label": "User ID", + "read_only": 1 + }, + { + "fieldname": "request_header", + "fieldtype": "Small Text", + "label": "Request Header", + "read_only": 1 + }, + { + "collapsible": 1, + "fieldname": "request_section", + "fieldtype": "Section Break", + "label": "Request Body" + }, + { + "fieldname": "request_body", + "fieldtype": "Long Text", + "label": "Request Body", + "read_only": 1 + }, + { + "collapsible": 1, + "fieldname": "section_break_8", + "fieldtype": "Section Break", + "label": "Response Data" + }, + { + "fieldname": "response_data", + "fieldtype": "Long Text", + "label": "Response Data" + }, + { + "fieldname": "naming_series", + "fieldtype": "Select", + "label": "Naming Series", + "options": "JUB-RES-.YY.-.########", + "read_only": 1 + }, + { + "fieldname": "ref_doctype", + "fieldtype": "Data", + "label": "Ref Doctype", + "read_only": 1 + }, + { + "fieldname": "ref_docname", + "fieldtype": "Data", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Ref Docname", + "read_only": 1, + "search_index": 1 + } + ], + "index_web_pages_for_search": 1, + "links": [], + "modified": "2024-04-17 19:55:34.807520", + "modified_by": "Administrator", + "module": "Jubilee", + "name": "Jubilee Response Log", + "naming_rule": "By \"Naming Series\" field", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "track_changes": 1 +} \ No newline at end of file diff --git a/hms_tz/jubilee/doctype/jubilee_response_log/jubilee_response_log.py b/hms_tz/jubilee/doctype/jubilee_response_log/jubilee_response_log.py new file mode 100644 index 00000000..cf8f83ac --- /dev/null +++ b/hms_tz/jubilee/doctype/jubilee_response_log/jubilee_response_log.py @@ -0,0 +1,33 @@ +# Copyright (c) 2024, Aakvatech and contributors +# For license information, please see license.txt + +import frappe +from frappe.model.document import Document + +class JubileeResponseLog(Document): + pass + + +def add_jubilee_log( + request_type, + request_url, + request_header=None, + request_body=None, + response_data=None, + status_code=None, + ref_doctype=None, + ref_docname=None, +): + doc = frappe.new_doc("Jubilee Response Log") + doc.request_type = str(request_type) + doc.request_url = str(request_url) + doc.request_header = str(request_header) or "" + doc.request_body = str(request_body) or "" + doc.response_data = str(response_data) or "" + doc.user_id = frappe.session.user + doc.status_code = status_code or "" + doc.ref_doctype = ref_doctype or "" + doc.ref_docname = ref_docname or "" + doc.save(ignore_permissions=True) + frappe.db.commit() + return doc.name diff --git a/hms_tz/jubilee/doctype/jubilee_response_log/test_jubilee_response_log.py b/hms_tz/jubilee/doctype/jubilee_response_log/test_jubilee_response_log.py new file mode 100644 index 00000000..c8910d49 --- /dev/null +++ b/hms_tz/jubilee/doctype/jubilee_response_log/test_jubilee_response_log.py @@ -0,0 +1,9 @@ +# Copyright (c) 2024, Aakvatech and Contributors +# See license.txt + +# import frappe +from frappe.tests.utils import FrappeTestCase + + +class TestJubileeResponseLog(FrappeTestCase): + pass From d623bd569736f8359186003911bb0235bf8111e4 Mon Sep 17 00:00:00 2001 From: av-dev2 Date: Wed, 17 Apr 2024 23:45:42 +0300 Subject: [PATCH 15/25] feat: request token from jubilee server --- hms_tz/jubilee/__init__.py | 0 hms_tz/jubilee/api/token.py | 137 +++++++++++++++++++++++++++++ hms_tz/jubilee/doctype/__init__.py | 0 3 files changed, 137 insertions(+) create mode 100644 hms_tz/jubilee/__init__.py create mode 100644 hms_tz/jubilee/api/token.py create mode 100644 hms_tz/jubilee/doctype/__init__.py diff --git a/hms_tz/jubilee/__init__.py b/hms_tz/jubilee/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/hms_tz/jubilee/api/token.py b/hms_tz/jubilee/api/token.py new file mode 100644 index 00000000..cd2f77a4 --- /dev/null +++ b/hms_tz/jubilee/api/token.py @@ -0,0 +1,137 @@ +import json +import frappe +import requests +from time import sleep, localtime +from frappe.utils import now_datetime, add_to_date, now +from frappe.utils.password import get_decrypted_password +from hms_tz.jubilee.doctype.jubilee_response_log.jubilee_response_log import ( + add_jubilee_log, +) + + +def make_jubilee_token_request(doc, url, headers, payload, fields): + for i in range(3): + try: + r = requests.request("POST", url, headers=headers, data=payload, timeout=5) + r.raise_for_status() + frappe.logger().debug({"webhook_success": r.text}) + data = json.loads(r.text) + if data: + add_jubilee_log( + request_type="Token", + request_url=url, + request_header=headers, + request_body=payload, + response_data=data, + status_code=r.status_code, + ) + + if ( + data["Description"] + and data["Description"].get("token_type") == "Bearer" + ): + token = data["Description"].get("access_token") + expired = data["Description"].get("expires_in") + expiry_date = localtime(expired) + doc.update({fields["token"]: token, fields["expiry"]: expiry_date}) + + doc.db_update() + frappe.db.commit() + return token + else: + add_jubilee_log( + request_type="Token", + request_url=url, + request_header=headers, + request_body=payload, + response_data=data, + status_code=r.status_code, + ) + frappe.throw(data) + + except Exception as e: + frappe.logger().debug({"webhook_error": e, "try": i + 1}) + sleep(3 * i + 1) + if i != 2: + continue + else: + raise e + + +@frappe.whitelist() +def get_jubilee_service_token(company, api_provider): + setting_name = frappe.get_cached_value( + "Company Insurance Setting", + {"company": company, "api_provider": api_provider}, + "name", + ) + if not setting_name: + frappe.throw( + f"Company Insurance Setting not found for company: {company} and API Provider: {api_provider}, please create one." + ) + + setting_doc = frappe.get_cached_doc("Company Insurance Setting", setting_name) + if ( + setting_doc.service_token_expiry + and setting_doc.service_token_expiry > now_datetime() + ): + return setting_doc.service_token + + payload = { + "username": setting_doc.username, + "password": get_decrypted_password( + setting_doc.doctype, setting_doc.name, "password" + ), + "providerid": setting_doc.providerid, + } + headers = {} + url = str(setting_doc.service_url) + "/jubileeapi/Token" + + jubileeservice_fields = { + "token": "service_token", + "expiry": "service_token_expiry", + } + + return make_jubilee_token_request( + setting_doc, url, headers, payload, jubileeservice_fields + ) + + +@frappe.whitelist() +def get_jubilee_claimsservice_token(company, api_provider): + setting_name = frappe.get_cached_value( + "Company Insurance Setting", + {"company": company, "api_provider": api_provider}, + "name", + ) + if not setting_name: + frappe.throw( + f"Company Insurance Setting not found for company: {company} and API Provider: {api_provider}, please create one." + ) + + setting_doc = frappe.get_cached_doc("Company Insurance Setting", setting_name) + + if ( + setting_doc.claimsserver_expiry + and setting_doc.claimsserver_expiry > now_datetime() + ): + return setting_doc.claimsserver_token + + payload = { + "username": setting_doc.username, + "password": get_decrypted_password( + setting_doc.doctype, setting_doc.name, "password" + ), + "providerid": setting_doc.providerid, + } + headers = {} + url = str(setting_doc.claimsserver_url) + "/jubileeapi/Token" + + claimserver_fields = { + "token": "claimsserver_token", + "expiry": "claimsserver_expiry", + } + + return make_jubilee_token_request( + setting_doc, url, headers, payload, claimserver_fields + ) diff --git a/hms_tz/jubilee/doctype/__init__.py b/hms_tz/jubilee/doctype/__init__.py new file mode 100644 index 00000000..e69de29b From 446df5c3d225903246da58c5cf03afbe75bf18b7 Mon Sep 17 00:00:00 2001 From: av-dev2 Date: Mon, 22 Apr 2024 16:39:15 +0300 Subject: [PATCH 16/25] feat: fetch member card details from jubilee server --- hms_tz/jubilee/api/api.py | 79 +++++ hms_tz/nhif/api/patient.js | 272 +++++++++++++----- .../custom_fields/hms_tz_custom_fields.py | 2 +- 3 files changed, 276 insertions(+), 77 deletions(-) create mode 100644 hms_tz/jubilee/api/api.py diff --git a/hms_tz/jubilee/api/api.py b/hms_tz/jubilee/api/api.py new file mode 100644 index 00000000..cb750544 --- /dev/null +++ b/hms_tz/jubilee/api/api.py @@ -0,0 +1,79 @@ +import json +import frappe +import requests +from frappe import _ +from time import sleep +from erpnext import get_default_company +from hms_tz.jubilee.api.token import get_jubilee_service_token +from hms_tz.jubilee.doctype.jubilee_response_log.jubilee_response_log import add_jubilee_log + + +@frappe.whitelist() +def get_member_card_detials(card_no, insurance_provider): + if not card_no or insurance_provider != "Jubilee": + return + + company = get_default_company() + if not company: + company = frappe.defaults.get_user_default("Company") + + if not company: + company = frappe.get_list( + "Company Insurance Setting", fields=["company"], filters={"enable": 1, "api_provider": insurance_provider} + )[0].company + if not company: + frappe.throw(_("No companies found to connect to Jubilee")) + + token = get_jubilee_service_token(company, insurance_provider) + + service_url = frappe.get_cached_value( + "Company Insurance Setting", + {"company": company, "api_provider": insurance_provider}, + "service_url", + ) + headers = {"Authorization": "Bearer " + token} + url = ( + str(service_url) + f"/jubileeapi/Getcarddetails?MemberNo={str(card_no)}" + ) + for i in range(3): + try: + r = requests.get(url, headers=headers, timeout=5) + r.raise_for_status() + frappe.logger().debug({"webhook_success": r.text}) + + if json.loads(r.text): + add_jubilee_log( + request_type="GetCardDetails", + request_url=url, + request_header=headers, + response_data=json.loads(r.text), + status_code=r.status_code, + ref_doctype="Patient", + ) + card = json.loads(r.text) + frappe.msgprint(_(card["Status"]), alert=True) + # add_scheme(card.get("SchemeID"), card.get("SchemeName")) + # add_product(card.get("ProductCode"), card.get("ProductName")) + return card + else: + add_jubilee_log( + request_type="GetCardDetails", + request_url=url, + request_header=headers, + response_data=str(r), + status_code=r.status_code, + ref_doctype="Patient", + ) + frappe.msgprint(json.loads(r.text)) + frappe.msgprint( + _( + "Getting information from NHIF failed. Try again after sometime, or continue manually." + ) + ) + except Exception as e: + frappe.logger().debug({"webhook_error": e, "try": i + 1}) + sleep(3 * i + 1) + if i != 2: + continue + else: + raise e diff --git a/hms_tz/nhif/api/patient.js b/hms_tz/nhif/api/patient.js index 490dff70..20da49ec 100644 --- a/hms_tz/nhif/api/patient.js +++ b/hms_tz/nhif/api/patient.js @@ -18,11 +18,18 @@ frappe.ui.form.on('Patient', { }); }, card_no: function (frm) { - frm.fields_dict.card_no.$input.focusout(function() { - frm.trigger('get_patient_info'); + frm.fields_dict.card_no.$input.focusout(function () { + if (frm.doc.insurance_provider) { + frm.trigger('get_patient_info'); + } frm.set_df_property('card_no', 'read_only', 1); }); }, + insurance_provider: function (frm) { + if (frm.doc.card_no && frm.doc.insurance_provider) { + frm.trigger('get_patient_info'); + } + }, mobile: function (frm) { frappe.call({ method: 'hms_tz.nhif.api.patient.validate_mobile_number', @@ -53,26 +60,86 @@ frappe.ui.form.on('Patient', { } }); if (exists) return; - frappe.call({ - method: 'hms_tz.nhif.api.patient.get_patient_info', - args: { - 'card_no': frm.doc.card_no, - }, - freeze: true, - freeze_message: __("Please Wait..."), - callback: function (data) { - if (data.message) { - const card = data.message; - if (!frm.is_new()) { - const d = new frappe.ui.Dialog({ - title: "Patient's information", - primary_action_label: 'Submit', - primary_action(values) { - update_patient_info(frm, card); - d.hide(); - } + + if (frm.doc.card_no && frm.doc.insurance_provider) { + if (frm.doc.insurance_provider == 'NHIF') { + get_nhif_patient_info(frm); + } else if (frm.doc.insurance_provider == "Jubilee") { + get_jubilee_patient_info(frm); + } + } + }, + update_cash_limit: function (frm) { + if (frappe.user.has_role('Healthcare Administrator')) { + frm.add_custom_button(__('Update Cash Limit'), function () { + let d = new frappe.ui.Dialog({ + title: 'Change Cash Limit', + fields: [ + { + fieldname: 'current_cash_limit', + fieldtype: 'Currency', + label: __('Current Cash Limit'), + default: frm.doc.cash_limit, + reqd: true + }, + { + fieldname: 'column_break_1', + fieldtype: 'Column Break' + }, + { + fieldname: 'new_cash_limit', + fieldtype: 'Currency', + label: 'New Cash Limit', + reqd: true + } + ], + }); + d.set_primary_action(__('Submit'), function () { + if (d.get_value('new_cash_limit') == 0) { + frappe.msgprint({ + title: 'Notification', + indicator: 'red', + message: __('New cash limit cannot be zero') }); - $(`