diff --git a/README.md b/README.md index 094634d7..618f7aa7 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ Features in ERPNext can be categorized into several key areas: 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. +3. **Insurance and Financial Management**: This involves managing NHIF claims, tracking changes, setting up company Insurance settings, and handling patient discount requests. 4. **Patient Care and Services**: This covers healthcare package orders, referral management, and tracking of patient care services. 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 10c7bd5f..e23c4f9a 100644 --- a/hms_tz/hms_tz/doctype/lrpmt_returns/lrpmt_returns.json +++ b/hms_tz/hms_tz/doctype/lrpmt_returns/lrpmt_returns.json @@ -215,4 +215,4 @@ "sort_order": "DESC", "states": [], "track_changes": 1 -} \ No newline at end of file +} diff --git a/hms_tz/nhif/doctype/company_nhif_settings/__init__.py b/hms_tz/jubilee/__init__.py similarity index 100% rename from hms_tz/nhif/doctype/company_nhif_settings/__init__.py rename to hms_tz/jubilee/__init__.py diff --git a/hms_tz/jubilee/api/api.py b/hms_tz/jubilee/api/api.py new file mode 100644 index 00000000..911292ff --- /dev/null +++ b/hms_tz/jubilee/api/api.py @@ -0,0 +1,263 @@ +import json +import frappe +import requests +from frappe import _ +from time import sleep +from erpnext import get_default_company +from frappe.utils.background_jobs import enqueue +from hms_tz.jubilee.api.token import get_jubilee_service_token +from hms_tz.jubilee.api.price_package import sync_jubilee_price_package +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, "insurance_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, "insurance_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) + 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 + + +@frappe.whitelist() +def create_jubilee_subscription(patient_id, card_no, insurance_provider): + if not insurance_provider or insurance_provider != "Jubilee": + return + + subscription_list = frappe.get_list( + "Healthcare Insurance Subscription", + filters={"patient": patient_id, "is_active": 1}, + ) + if len(subscription_list) > 0: + frappe.msgprint( + _( + "Existing Patient HIS was found. Create the Healthcare Insurance Subscription manually!" + ) + ) + return + + plan_filters = { + "is_active": 1, + "insurance_company": ["like", "Jubilee%"], + } + company = get_default_company() + if company: + plan_filters["company"] = company + + # Assumed that company is filtered based on user permissions + plan = frappe.db.get_list( + "Healthcare Insurance Coverage Plan", + filters=plan_filters, + fields=["name", "insurance_company", "company"], + ) + + if not plan or len(plan) == 0: + frappe.msgprint( + _("No active Healthcare Insurance Coverage Plan found for Jubilee") + ) + return + + if len(plan) > 1: + frappe.msgprint( + _( + "Multiple active Healthcare Insurance Coverage Plan found for Jubilee,\ +

please create the healthcare Insurance Subscription manually" + ) + ) + return + + sub_doc = frappe.new_doc("Healthcare Insurance Subscription") + sub_doc.patient = patient_id + sub_doc.insurance_company = plan[0].insurance_company + sub_doc.healthcare_insurance_coverage_plan = plan[0].name + sub_doc.coverage_plan_card_number = card_no + sub_doc.save(ignore_permissions=True) + sub_doc.submit() + frappe.msgprint( + _( + f"

AUTO

Healthcare Insurance Subscription: {sub_doc.name} is created for {plan[0].name}" + ) + ) + + +@frappe.whitelist() +def get_authorization_number( + company, + card_no, + appointment_no, + insurance_subscription, + insurance_provider="Jubilee", +): + if insurance_provider != "Jubilee": + return + + setting_name, service_url = frappe.get_cached_value( + "Company Insurance Setting", + {"company": company, "insurance_provider": insurance_provider, "enable": 1}, + ["name", "service_url"], + ) + if not setting_name: + frappe.throw( + f"Company Insurance Setting not found for company: {company} and API Provider: {insurance_provider}, please Create or Enable one." + ) + + if not card_no: + frappe.msgprint( + _( + f"Please set Card No in Healthcare Insurance Subscription {insurance_subscription}" + ) + ) + return + + token = get_jubilee_service_token(company, insurance_provider) + + headers = {"Content-Type": "application/json", "Authorization": "Bearer " + token} + url = str(service_url) + "/jubileeapi/CheckVerification?MemberNo=" + str(card_no) + + 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="AuthorizeCard", + request_url=url, + request_header=headers, + response_data=json.loads(r.text), + status_code=r.status_code, + ref_doctype="Patient Appointment", + ref_docname=appointment_no, + company=company, + ) + card = json.loads(r.text) + if card.get("Status") != "OK": + frappe.throw(title=card.get("Status"), msg=card["Description"]) + frappe.msgprint(_(card["Description"]), alert=True) + return card + else: + add_jubilee_log( + request_type="AuthorizeCard", + request_url=url, + request_header=headers, + response_data=json.loads(r.text), + status_code=r.status_code, + ref_doctype="Patient Appointment", + ref_docname=appointment_no, + company=company, + ) + frappe.throw(json.loads(r.text)) + + +@frappe.whitelist() +def enqueue_get_jubilee_price_packages(company): + enqueue( + method=get_jubilee_price_packages, + job_name="get_jubilee_price_packages", + queue="default", + timeout=10000000, + is_async=True, + company=company, + ) + + +@frappe.whitelist() +def get_jubilee_price_packages(company, insurance_provider="Jubilee"): + 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, "insurance_provider": insurance_provider}, + "service_url", + ) + headers = {"Authorization": "Bearer " + token} + url = str(service_url) + "/jubileeapi/GetPriceList" + r = requests.get(url, headers=headers, timeout=300) + if r.status_code != 200: + add_jubilee_log( + request_type="GetPricePackage", + request_url=url, + request_header=headers, + response_data=r.text, + status_code=r.status_code, + ref_doctype="Jubilee Price Package", + company=company, + ) + frappe.throw(json.loads(r.text)) + else: + if json.loads(r.text): + log_name = add_jubilee_log( + request_type="GetPricePackage", + request_url=url, + request_header=headers, + response_data=r.text, + status_code=r.status_code, + ref_doctype="Jubilee Price Package", + company=company, + ) + + packages = json.loads(r.text)["Description"] + sync_jubilee_price_package(packages, company, log_name) diff --git a/hms_tz/jubilee/api/price_package.py b/hms_tz/jubilee/api/price_package.py new file mode 100644 index 00000000..5d4ab4f4 --- /dev/null +++ b/hms_tz/jubilee/api/price_package.py @@ -0,0 +1,561 @@ +import json +import frappe +from time import sleep +from frappe.query_builder import DocType +from frappe.utils.background_jobs import enqueue +from frappe.query_builder.terms import ValueWrapper +from frappe.utils import now_datetime, nowdate, flt + + +@frappe.whitelist() +def process_jubilee_records(company): + enqueue( + method=process_jubilee_price_list, + job_name="process_jubilee_price_list", + queue="default", + timeout=10000000, + is_async=True, + company=company, + ) + + enqueue( + method=process_jubilee_coverage, + job_name="process_jubilee_coverage", + queue="default", + timeout=10000000, + is_async=True, + company=company, + ) + + +def sync_jubilee_price_package( + packages, company, log_name, insurance_provider="Jubilee" +): + if len(packages) == 0: + return + + jpp = DocType("Jubilee Price Package") + frappe.qb.from_(jpp).delete().where(jpp.company == company).run() + + sleep(60) + create_price_package(packages, company, log_name) + + sleep(60) + set_package_diff(company) + + +def create_price_package(packages, company, log_name): + fields = [ + "name", + "company", + "itemcode", + "itemname", + "cleanname", + "itemprice", + "providerid", + "log_name", + "time_stamp", + ] + + data = [] + time_stamp = now_datetime() + for row in packages: + jpp_name = frappe.generate_hash(length=10) + + data.append( + ( + jpp_name, + company, + row.get("ItemCode"), + row.get("ItemName"), + row.get("CleanName"), + row.get("ItemPrice"), + row.get("ProviderID"), + log_name, + time_stamp, + ) + ) + frappe.db.bulk_insert( + "Jubilee Price Package", fields=fields, values=data, chunk_size=1000 + ) + + +def process_jubilee_coverage(company, coverage_plan=None): + hsic = DocType("Healthcare Service Insurance Coverage") + + coverage_plan_list = None + if coverage_plan: + coverage_plan_list.append({"name": coverage_plan}) + else: + coverage_plan_list = frappe.get_all( + "Healthcare Insurance Coverage Plan", + fields={"name"}, + filters={ + "insurance_company": ["like", "%Jubilee%"], + "company": company, + "is_active": 1, + }, + page_length=1, + ) + + if len(coverage_plan_list) == 0: + frappe.throw("No active coverage plan found for Jubilee") + + for coverage_plan in coverage_plan_list: + item_list = get_jubile_items(company) + if len(item_list) == 0: + frappe.throw("No items found for Jubilee") + + data = [] + for item in item_list: + hsic_name = frappe.generate_hash(length=10) + data.append( + ( + hsic_name, + now_datetime(), + now_datetime(), + frappe.session.user, + frappe.session.user, + company, + coverage_plan.get("name"), + 100, + nowdate(), + "2099-12-31", + item.dt, + item.service_template, + 1, + 1, + ) + ) + + if len(data) > 0 and coverage_plan.get("name"): + frappe.qb.from_(hsic).delete().where( + (hsic.healthcare_insurance_coverage_plan == coverage_plan.get("name")) + & (hsic.company == company) + & (hsic.is_auto_generated == 1) + ).run() + + sleep(60) + + fields = [ + "name", + "creation", + "modified", + "modified_by", + "owner", + "company", + "healthcare_insurance_coverage_plan", + "coverage", + "start_date", + "end_date", + "healthcare_service", + "healthcare_service_template", + "is_auto_generated", + "is_active", + ] + frappe.db.bulk_insert( + "Healthcare Service Insurance Coverage", + fields=fields, + values=data, + chunk_size=1000, + ) + + sleep(60) + + +def process_jubilee_price_list(company, item=None): + itp = DocType("Item Price") + + company_info = frappe.get_cached_value( + "Company", company, ["abbr", "default_currency"], as_dict=True + ) + price_list_name = f"Jubilee {company_info.abbr}" + if not frappe.db.exists("Price List", price_list_name): + price_list_doc = frappe.new_doc("Price List") + price_list_doc.price_list_name = price_list_name + price_list_doc.currency = company_info.default_currency + price_list_doc.buying = 0 + price_list_doc.selling = 1 + price_list_doc.save(ignore_permissions=True) + + item_list = get_items_for_price_list(company, item) + + for item in item_list: + item_price_list = ( + frappe.qb.from_(itp) + .select("name", "item_code", "price_list_rate") + .where( + (itp.selling == 1) + & (itp.price_list == price_list_name) + & (itp.item_code == item.get("item_code")) + & (itp.currency == company_info.default_currency) + ) + ).run(as_dict=True) + + if len(item_price_list) > 0: + for price in item_price_list: + if flt(price.price_list_rate) != flt(item.itemprice): + # delete Item Price if no item.itemprice or it is 0 + if not flt(item.itemprice) or flt(item.itemprice) == 0: + frappe.qb.from_(itp).delete().where( + itp.name == price.name + ).run() + else: + # update Item Price with the new price + frappe.qb.update(itp).set( + itp.price_list_rate, flt(item.itemprice) + ).where(itp.name == price.name).run() + else: + item_price_doc = frappe.new_doc("Item Price") + item_price_doc.update( + { + "item_code": item.item_code, + "price_list": price_list_name, + "currency": company_info.default_currency, + "price_list_rate": flt(item.itemprice), + "buying": 0, + "selling": 1, + } + ) + item_price_doc.insert(ignore_permissions=True) + item_price_doc.save(ignore_permissions=True) + + frappe.db.commit() + + +def set_package_diff(company): + logs = frappe.get_all( + "Jubilee Response Log", + filters={ + "request_type": "GetPricePackage", + "response_data": ["not in", ["", None]], + "company": company, + }, + fields=["name", "response_data"], + order_by="creation desc", + page_length=2, + ) + if len(logs) < 2: + return + + current_rec = json.loads(logs[0]["response_data"]) + previous_rec = json.loads(logs[1]["response_data"]) + current_price_packages = current_rec.get("Description") + previousـprice_packages = previous_rec.get("Description") + + diff_price_packages_from_current = [ + i for i in current_price_packages if i not in previousـprice_packages + ] + diff_price_packages_from_previous = [ + i for i in previousـprice_packages if i not in current_price_packages + ] + + changed_price_packages = [] + new_price_packages = [] + + for e in diff_price_packages_from_current: + exist_rec = next( + ( + item + for item in diff_price_packages_from_previous + if item.get("ItemCode") == e.get("ItemCode") + ), + None, + ) + if exist_rec: + changed_price_packages.append(exist_rec) + else: + new_price_packages.append(e) + + deleted_price_packages = [] + + for z in diff_price_packages_from_previous: + exist_rec = next( + ( + item + for item in diff_price_packages_from_current + if item.get("ItemCode") == z.get("ItemCode") + ), + None, + ) + if not exist_rec: + deleted_price_packages.append(z) + + if ( + len(changed_price_packages) > 0 + or len(new_price_packages) > 0 + or len(deleted_price_packages) > 0 + ): + doc = frappe.new_doc("Jubilee Update") + + add_price_packages_records(doc, changed_price_packages, "Changed") + add_price_packages_records(doc, new_price_packages, "New") + add_price_packages_records(doc, deleted_price_packages, "Deleted") + + if (doc.get("price_package") and len(doc.price_package)) or (): + doc.company = company + doc.current_log = logs[0].name + doc.previous_log = logs[1].name + doc.save(ignore_permissions=True) + + +def add_price_packages_records(doc, rec, type): + if not len(rec) > 0: + return + + for e in rec: + price_row = doc.append("price_package", {}) + price_row.itemcode = e.get("ItemCode") + price_row.type = type + price_row.olditemcode = e.get("OldItemCode") + price_row.itemname = e.get("ItemName") + price_row.strength = e.get("Strength") + price_row.dosage = e.get("Dosage") + price_row.unitprice = e.get("UnitPrice") + price_row.record = json.dumps(e) + + +def get_jubile_items(company): + lab_items = get_lab_templates(company) + radiology_items = get_radiology_templates(company) + procedure_items = get_procedure_templates(company) + medication_items = get_medication_templates(company) + therapy_items = get_therapy_templates(company) + hsut_items = get_healthcare_service_unit_types(company) + + item_list = ( + lab_items + + radiology_items + + procedure_items + + medication_items + + therapy_items + + hsut_items + ) + + return item_list + + +def get_lab_templates(company): + it = DocType("Item") + icd = DocType("Item Customer Detail") + jpp = DocType("Jubilee Price Package") + lab = DocType("Lab Test Template") + + lab_query = ( + frappe.qb.from_(icd) + .inner_join(it) + .on(it.name == icd.parent) + .inner_join(lab) + .on(icd.parent == lab.item) + .inner_join(jpp) + .on(icd.ref_code == jpp.itemcode) + .select( + ValueWrapper("Lab Test Template").as_("dt"), + lab.name.as_("service_template"), + icd.ref_code, + icd.parent.as_("item_code"), + ) + .where( + (icd.customer_name == "Jubilee") + & (it.disabled == 0) + & (jpp.company == company) + ) + .groupby(lab.name, icd.ref_code, icd.parent) + ) + lab_data = lab_query.run(as_dict=True) + + return lab_data + + +def get_radiology_templates(company): + jpp = DocType("Jubilee Price Package") + it = DocType("Item") + icd = DocType("Item Customer Detail") + radiology = DocType("Radiology Examination Template") + + radiology_query = ( + frappe.qb.from_(icd) + .inner_join(it) + .on(it.name == icd.parent) + .inner_join(radiology) + .on(icd.parent == radiology.item) + .inner_join(jpp) + .on(icd.ref_code == jpp.itemcode) + .select( + ValueWrapper("Radiology Examination Template").as_("dt"), + radiology.name.as_("service_template"), + icd.ref_code, + icd.parent.as_("item_code"), + ) + .where( + (icd.customer_name == "The Jubilee Insurance (T) Ltd") + & (it.disabled == 0) + & (jpp.company == company) + ) + .groupby(radiology.name, icd.ref_code, icd.parent) + ) + + radiology_data = radiology_query.run(as_dict=True) + + return radiology_data + + +def get_procedure_templates(company): + it = DocType("Item") + icd = DocType("Item Customer Detail") + jpp = DocType("Jubilee Price Package") + procedure = DocType("Clinical Procedure Template") + + procedure_query = ( + frappe.qb.from_(icd) + .inner_join(it) + .on(it.name == icd.parent) + .inner_join(procedure) + .on(icd.parent == procedure.item) + .inner_join(jpp) + .on(icd.ref_code == jpp.itemcode) + .select( + ValueWrapper("Clinical Procedure Template").as_("dt"), + procedure.name.as_("service_template"), + icd.ref_code, + icd.parent.as_("item_code"), + ) + .where( + (icd.customer_name == "The Jubilee Insurance (T) Ltd") + & (it.disabled == 0) + & (jpp.company == company) + ) + .groupby(procedure.name, icd.ref_code, icd.parent) + ) + + procedure_data = procedure_query.run(as_dict=True) + return procedure_data + + +def get_medication_templates(company): + it = DocType("Item") + icd = DocType("Item Customer Detail") + jpp = DocType("Jubilee Price Package") + medication = DocType("Medication") + + medication_query = ( + frappe.qb.from_(icd) + .inner_join(it) + .on(it.name == icd.parent) + .inner_join(medication) + .on(icd.parent == medication.item) + .inner_join(jpp) + .on(icd.ref_code == jpp.itemcode) + .select( + ValueWrapper("Medication").as_("dt"), + medication.name.as_("service_template"), + icd.ref_code, + icd.parent.as_("item_code"), + ) + .where( + (icd.customer_name == "The Jubilee Insurance (T) Ltd") + & (it.disabled == 0) + & (jpp.company == company) + ) + .groupby(medication.name, icd.ref_code, icd.parent) + ) + + medication_data = medication_query.run(as_dict=True) + return medication_data + + +def get_therapy_templates(company): + it = DocType("Item") + icd = DocType("Item Customer Detail") + jpp = DocType("Jubilee Price Package") + therapy = DocType("Therapy Type") + + therapy_query = ( + frappe.qb.from_(icd) + .inner_join(it) + .on(it.name == icd.parent) + .inner_join(therapy) + .on(icd.parent == therapy.item) + .inner_join(jpp) + .on(icd.ref_code == jpp.itemcode) + .select( + ValueWrapper("Therapy Type").as_("dt"), + therapy.name.as_("service_template"), + icd.ref_code, + icd.parent.as_("item_code"), + ) + .where( + (icd.customer_name == "The Jubilee Insurance (T) Ltd") + & (it.disabled == 0) + & (jpp.company == company) + ) + .groupby(therapy.name, icd.ref_code, icd.parent) + ) + + therapy_data = therapy_query.run(as_dict=True) + return therapy_data + + +def get_healthcare_service_unit_types(company): + it = DocType("Item") + icd = DocType("Item Customer Detail") + jpp = DocType("Jubilee Price Package") + hsut = DocType("Healthcare Service Unit Type") + + hsut_query = ( + frappe.qb.from_(icd) + .inner_join(it) + .on(it.name == icd.parent) + .inner_join(hsut) + .on(icd.parent == hsut.item) + .inner_join(jpp) + .on(icd.ref_code == jpp.itemcode) + .select( + ValueWrapper("Healthcare Service Unit Type").as_("dt"), + hsut.name.as_("service_template"), + icd.ref_code, + icd.parent.as_("item_code"), + ) + .where( + (icd.customer_name == "The Jubilee Insurance (T) Ltd") + & (it.disabled == 0) + & (jpp.company == company) + ) + .groupby(hsut.name, icd.ref_code, icd.parent) + ) + + hsut_data = hsut_query.run(as_dict=True) + return hsut_data + + +def get_items_for_price_list(company, item=None): + it = DocType("Item") + icd = DocType("Item Customer Detail") + jpp = DocType("Jubilee Price Package") + + item_query = ( + frappe.qb.from_(icd) + .inner_join(it) + .on(it.name == icd.parent) + .inner_join(jpp) + .on(icd.ref_code == jpp.itemcode) + .select( + icd.ref_code, + icd.parent.as_("item_code"), + jpp.itemcode, + jpp.itemname, + jpp.cleanname, + jpp.itemprice, + ) + .where( + (it.disabled == 0) + & (jpp.company == company) + & (icd.customer_name == "The Jubilee Insurance (T) Ltd") + ) + .groupby(icd.ref_code, icd.parent) + ) + if item: + item_query = item_query.where(icd.parent == item) + + item_data = item_query.run(as_dict=True) + return item_data diff --git a/hms_tz/jubilee/api/token.py b/hms_tz/jubilee/api/token.py new file mode 100644 index 00000000..a9ad5b20 --- /dev/null +++ b/hms_tz/jubilee/api/token.py @@ -0,0 +1,139 @@ +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, + company=doc.company, + ) + + 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, + company=doc.company, + ) + 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, insurance_provider): + setting_name = frappe.get_cached_value( + "Company Insurance Setting", + {"company": company, "insurance_provider": insurance_provider, "enable": 1}, + "name", + ) + if not setting_name: + frappe.throw( + f"Company Insurance Setting not found for company: {company} and Insurance Provider: {insurance_provider}, please Create or Enable 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, insurance_provider): + setting_name = frappe.get_cached_value( + "Company Insurance Setting", + {"company": company, "insurance_provider": insurance_provider, "enable": 1}, + "name", + ) + if not setting_name: + frappe.throw( + f"Company Insurance Setting not found for company: {company} and Insurance Provider: {insurance_provider}, please Create or Enable 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/nhif/doctype/nhif_folio_counter/__init__.py b/hms_tz/jubilee/doctype/__init__.py similarity index 100% rename from hms_tz/nhif/doctype/nhif_folio_counter/__init__.py rename to hms_tz/jubilee/doctype/__init__.py diff --git a/hms_tz/jubilee/doctype/jubilee_patient_claim/__init__.py b/hms_tz/jubilee/doctype/jubilee_patient_claim/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/hms_tz/jubilee/doctype/jubilee_patient_claim/jubilee_patient_claim.js b/hms_tz/jubilee/doctype/jubilee_patient_claim/jubilee_patient_claim.js new file mode 100644 index 00000000..a0f9ae5d --- /dev/null +++ b/hms_tz/jubilee/doctype/jubilee_patient_claim/jubilee_patient_claim.js @@ -0,0 +1,102 @@ +// Copyright (c) 2024, Aakvatech and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Jubilee Patient Claim', { + setup: function (frm) { + frm.set_query("patient_appointment", function () { + return { + "filters": { + "jubilee_patient_claim": ["in", ["", "None"]], + "insurance_company": ["like", "Jubilee%"], + "insurance_subscription": ["not in", ["", "None"]] + } + }; + }); + }, + refresh: function (frm) { + $("[data-action='delete_all_rows']").hide(); + + if (frm.doc.docstatus === 0 && frm.doc.authorization_no) { + frm.add_custom_button(__("Re-concile Repeated Items"), () => { + frappe.call({ + method: "reconcile_repeated_items", + args: { + self: frm.doc + }, + freeze: true, + freeze_message: __(''), + }).then(r => { + if (r.message) { + frm.refresh(); + } + }); + }); + + frm.add_custom_button(__("Merge Claims"), function () { + frm.dirty() + frm.call('get_appointments', { + self: frm.doc, + freeze: true, + freeze_message: __(''), + }).then(r => { + frm.save(); + frm.refresh(); + }); + }); + } + + // frm.add_custom_button(__("Send Claim"), () => { + // frm.call('send_jubilee_claim', { + // self: frm.doc, + // freeze: true, + // freeze_message: __(''), + // }).then(r => { + // // frm.refresh(); + // }); + // }); + }, + + onload: function (frm) { + $("[data-action='delete_all_rows']").hide(); + + if (frm.doc.patient && frm.doc.patient_appointment) { + frappe.db.get_list('LRPMT Returns', { + fields: ['name'], + filters: { + 'patient': frm.doc.patient, + 'appointment': frm.doc.patient_appointment, + 'docstatus': 1 + } + }).then(data => { + if (data.length > 0) { + let msg_lrpmt = `` + data.forEach(element => { + msg_lrpmt += `${__(element.name)} ,` + }); + + frappe.msgprint({ + title: __('Notification'), + indicator: 'orange', + message: __(` +

This Patient: ${__(frm.doc.patient)} of appointment No: ${__(frm.doc.patient_appointment)} + having some item(s) cancelled or some quantity of item(s) returned to stock, by ${__(msg_lrpmt)}, + inorder for items and their quantities to be reflected on this claim

+

+ Tick allow changes, then Untick allow changes and Save again

+ ` + ) + }); + } + }); + }; + }, + + is_ready_for_auto_submission: (frm) => { + if (frm.doc.is_ready_for_auto_submission == 1) { + frm.set_value("reviewed_by", frappe.user.full_name()); + } else { + frm.set_value("reviewed_by", ""); + } + } + +}); diff --git a/hms_tz/jubilee/doctype/jubilee_patient_claim/jubilee_patient_claim.json b/hms_tz/jubilee/doctype/jubilee_patient_claim/jubilee_patient_claim.json new file mode 100644 index 00000000..ae1348cd --- /dev/null +++ b/hms_tz/jubilee/doctype/jubilee_patient_claim/jubilee_patient_claim.json @@ -0,0 +1,533 @@ +{ + "actions": [], + "allow_copy": 1, + "allow_import": 1, + "autoname": "naming_series:", + "creation": "2024-07-26 13:43:31.553011", + "default_view": "List", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "naming_series", + "patient_appointment", + "patient", + "patient_name", + "first_name", + "last_name", + "allow_changes", + "column_break_vgxlm", + "coverage_plan_name", + "cardno", + "authorization_no", + "inpatient_record", + "gender", + "date_of_birth", + "total_amount", + "column_break_10", + "telephone_no", + "company", + "posting_date", + "patient_signature", + "section_break_auto_send", + "is_ready_for_auto_submission", + "column_break_review_by", + "reviewed_by", + "section_break_8", + "folio_id", + "facility_code", + "claim_year", + "claim_month", + "folio_no", + "serial_no", + "item_crt_by", + "column_break_tqszx", + "practitioner_name", + "practitioner_no", + "date_discharge", + "discharge_time", + "date_admitted", + "admitted_time", + "patient_type_code", + "column_break_18", + "patient_file_no", + "attendance_date", + "attendance_time", + "delayreason", + "patient_file_section", + "patient_file", + "claim_file_section", + "claim_file", + "section_break_40", + "notes_sb", + "clinical_notes", + "section_break_28", + "jubilee_patient_claim_disease", + "section_break_30", + "jubilee_patient_claim_item", + "original_section_break", + "original_jubilee_patient_claim_item", + "hms_tz_claim_appointment_list", + "amended_from" + ], + "fields": [ + { + "default": "NPC-.#########", + "fieldname": "naming_series", + "fieldtype": "Select", + "hidden": 1, + "label": "Naming Series", + "options": "NPC-.#########", + "print_hide": 1, + "report_hide": 1, + "reqd": 1 + }, + { + "fieldname": "amended_from", + "fieldtype": "Link", + "label": "Amended From", + "no_copy": 1, + "options": "Jubilee Patient Claim", + "print_hide": 1, + "read_only": 1, + "search_index": 1 + }, + { + "fieldname": "patient_appointment", + "fieldtype": "Link", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Patient Appointment", + "options": "Patient Appointment", + "reqd": 1 + }, + { + "fetch_from": "patient_appointment.patient", + "fieldname": "patient", + "fieldtype": "Link", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Patient", + "options": "Patient", + "read_only": 1 + }, + { + "fetch_from": "patient.patient_name", + "fieldname": "patient_name", + "fieldtype": "Data", + "in_standard_filter": 1, + "label": "Patient Name", + "read_only": 1 + }, + { + "fetch_from": "patient.first_name", + "fieldname": "first_name", + "fieldtype": "Data", + "label": "First Name", + "read_only": 1 + }, + { + "fetch_from": "patient.last_name", + "fieldname": "last_name", + "fieldtype": "Data", + "label": "Last Name", + "read_only": 1 + }, + { + "fetch_from": "patient.mobile", + "fieldname": "telephone_no", + "fieldtype": "Data", + "in_standard_filter": 1, + "label": "Telephone No", + "read_only": 1 + }, + { + "fetch_from": "patient.dob", + "fieldname": "date_of_birth", + "fieldtype": "Date", + "label": "Date Of Birth", + "read_only": 1 + }, + { + "fetch_from": "patient.sex", + "fieldname": "gender", + "fieldtype": "Data", + "label": "Gender", + "read_only": 1 + }, + { + "default": "0", + "fieldname": "allow_changes", + "fieldtype": "Check", + "label": "Allow Changes" + }, + { + "fieldname": "column_break_10", + "fieldtype": "Column Break" + }, + { + "fetch_from": "patient_appointment.company", + "fieldname": "company", + "fieldtype": "Link", + "label": "Company", + "options": "Company", + "read_only": 1 + }, + { + "fieldname": "posting_date", + "fieldtype": "Date", + "label": "Posting Date", + "read_only": 1 + }, + { + "fieldname": "inpatient_record", + "fieldtype": "Link", + "label": "Inpatient Record", + "options": "Inpatient Record", + "read_only": 1 + }, + { + "fetch_from": "patient_appointment.coverage_plan_card_number", + "fieldname": "cardno", + "fieldtype": "Data", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "CardNo", + "no_copy": 1, + "reqd": 1 + }, + { + "fetch_from": "patient_appointment.authorization_number", + "fieldname": "authorization_no", + "fieldtype": "Data", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Authorization No", + "no_copy": 1, + "permlevel": 1, + "reqd": 1 + }, + { + "fetch_from": "patient_appointment.coverage_plan_name", + "fieldname": "coverage_plan_name", + "fieldtype": "Data", + "label": "Coverage Plan Name", + "no_copy": 1 + }, + { + "fieldname": "total_amount", + "fieldtype": "Float", + "label": "Total Amount", + "precision": "0", + "read_only": 1 + }, + { + "depends_on": "eval: doc.jubilee_patient_claim_item", + "fieldname": "patient_signature", + "fieldtype": "Signature", + "label": "Patient Signature" + }, + { + "fieldname": "section_break_auto_send", + "fieldtype": "Section Break" + }, + { + "bold": 1, + "default": "0", + "depends_on": "eval: doc.patient_signature", + "description": "If ticked, this claim will be submitted during night time", + "fieldname": "is_ready_for_auto_submission", + "fieldtype": "Check", + "label": "Is ready for auto submission" + }, + { + "fieldname": "column_break_review_by", + "fieldtype": "Column Break" + }, + { + "fieldname": "reviewed_by", + "fieldtype": "Data", + "label": "Reviewed By", + "read_only": 1 + }, + { + "collapsible": 1, + "fieldname": "section_break_8", + "fieldtype": "Section Break", + "label": "Folio Info" + }, + { + "fieldname": "folio_id", + "fieldtype": "Data", + "label": "Folio ID", + "read_only": 1 + }, + { + "fieldname": "facility_code", + "fieldtype": "Data", + "label": "Facility Code", + "read_only": 1 + }, + { + "fieldname": "claim_year", + "fieldtype": "Int", + "label": "Claim Year", + "read_only": 1 + }, + { + "fieldname": "claim_month", + "fieldtype": "Int", + "label": "Claim Month", + "read_only": 1 + }, + { + "fieldname": "folio_no", + "fieldtype": "Int", + "label": "Folio No", + "read_only": 1 + }, + { + "fieldname": "serial_no", + "fieldtype": "Data", + "label": "Serial No", + "read_only": 1 + }, + { + "fieldname": "item_crt_by", + "fieldtype": "Data", + "label": "Created By", + "read_only": 1 + }, + { + "fieldname": "column_break_tqszx", + "fieldtype": "Column Break" + }, + { + "fieldname": "practitioner_name", + "fieldtype": "Data", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Practitioner Name", + "read_only": 1 + }, + { + "fieldname": "practitioner_no", + "fieldtype": "Data", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Practitioner No", + "read_only": 1 + }, + { + "fieldname": "date_discharge", + "fieldtype": "Date", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Date Discharge", + "read_only": 1 + }, + { + "fieldname": "discharge_time", + "fieldtype": "Time", + "label": "Discharge Time", + "read_only": 1 + }, + { + "fieldname": "date_admitted", + "fieldtype": "Date", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Date Admitted", + "read_only": 1 + }, + { + "fieldname": "admitted_time", + "fieldtype": "Time", + "label": "Admitted Time", + "read_only": 1 + }, + { + "fieldname": "patient_type_code", + "fieldtype": "Data", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Patient Type Code", + "read_only": 1 + }, + { + "fieldname": "column_break_18", + "fieldtype": "Column Break" + }, + { + "fieldname": "patient_file_no", + "fieldtype": "Data", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Patient File No", + "read_only": 1 + }, + { + "fetch_from": "patient_appointment.appointment_date", + "fieldname": "attendance_date", + "fieldtype": "Date", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Attendance Date", + "read_only": 1 + }, + { + "fetch_from": "patient_appointment.appointment_time", + "fieldname": "attendance_time", + "fieldtype": "Time", + "label": "Attendance Time", + "read_only": 1 + }, + { + "fieldname": "delayreason", + "fieldtype": "Small Text", + "label": "DelayReason" + }, + { + "collapsible": 1, + "fieldname": "patient_file_section", + "fieldtype": "Section Break", + "label": "Patient File", + "print_hide": 1, + "report_hide": 1 + }, + { + "fieldname": "patient_file", + "fieldtype": "Markdown Editor", + "label": "Patient File", + "no_copy": 1, + "print_hide": 1, + "read_only": 1, + "report_hide": 1 + }, + { + "collapsible": 1, + "fieldname": "claim_file_section", + "fieldtype": "Section Break", + "label": "Claim File" + }, + { + "fieldname": "claim_file", + "fieldtype": "Markdown Editor", + "label": "Claim File", + "read_only": 1 + }, + { + "fieldname": "section_break_40", + "fieldtype": "Section Break" + }, + { + "collapsible": 1, + "fieldname": "notes_sb", + "fieldtype": "Section Break", + "label": "Clinical Notes" + }, + { + "fieldname": "clinical_notes", + "fieldtype": "Long Text", + "label": "Clinical Notes", + "read_only": 1 + }, + { + "fieldname": "section_break_28", + "fieldtype": "Section Break" + }, + { + "fieldname": "jubilee_patient_claim_disease", + "fieldtype": "Table", + "label": "Jubilee Patient Claim Disease", + "options": "Jubilee Patient Claim Disease" + }, + { + "fieldname": "section_break_30", + "fieldtype": "Section Break" + }, + { + "fieldname": "jubilee_patient_claim_item", + "fieldtype": "Table", + "label": "Jubilee Patient Claim Item", + "options": "Jubilee Patient Claim Item" + }, + { + "collapsible": 1, + "fieldname": "original_section_break", + "fieldtype": "Section Break", + "label": "Original Jubillee Patient Claim Item" + }, + { + "allow_on_submit": 1, + "fieldname": "original_jubilee_patient_claim_item", + "fieldtype": "Table", + "options": "Original Jubilee Patient Claim Item", + "read_only": 1 + }, + { + "fieldname": "hms_tz_claim_appointment_list", + "fieldtype": "Data", + "hidden": 1, + "label": "Claim Appointment List" + }, + { + "fieldname": "amended_from", + "fieldtype": "Link", + "label": "Amended From", + "no_copy": 1, + "options": "Jubilee Patient Claim", + "print_hide": 1, + "read_only": 1, + "search_index": 1 + }, + { + "fieldname": "column_break_vgxlm", + "fieldtype": "Column Break" + } + ], + "index_web_pages_for_search": 1, + "is_submittable": 1, + "links": [], + "modified": "2024-07-26 14:06:47.705131", + "modified_by": "Administrator", + "module": "Jubilee", + "name": "Jubilee Patient Claim", + "naming_rule": "By \"Naming Series\" field", + "owner": "Administrator", + "permissions": [ + { + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "submit": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Healthcare Administrator", + "share": 1, + "submit": 1, + "write": 1 + } + ], + "show_name_in_global_search": 1, + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "title_field": "patient_name", + "track_changes": 1 +} \ No newline at end of file diff --git a/hms_tz/jubilee/doctype/jubilee_patient_claim/jubilee_patient_claim.py b/hms_tz/jubilee/doctype/jubilee_patient_claim/jubilee_patient_claim.py new file mode 100644 index 00000000..a1518496 --- /dev/null +++ b/hms_tz/jubilee/doctype/jubilee_patient_claim/jubilee_patient_claim.py @@ -0,0 +1,1431 @@ +# Copyright (c) 2024, Aakvatech and contributors +# For license information, please see license.txt + +import os +import json +import uuid +import frappe +import requests +import calendar +from frappe import _ +from PyPDF2 import PdfFileWriter +from frappe.utils.pdf import get_pdf +from frappe.query_builder import DocType +from frappe.model.document import Document +from hms_tz.nhif.api.patient_encounter import finalized_encounter +from hms_tz.jubilee.api.token import get_jubilee_claimsservice_token +from hms_tz.jubilee.doctype.jubilee_response_log.jubilee_response_log import ( + add_jubilee_log, +) +from hms_tz.nhif.api.healthcare_utils import ( + get_item_rate, + to_base64, + get_approval_number_from_LRPMT, +) +from frappe.utils import ( + cint, + unique, + nowdate, + nowtime, + getdate, + get_time, + date_diff, + now_datetime, + get_datetime, + get_fullname, + get_url_to_form, + time_diff_in_seconds, +) + +pa = DocType("Patient Appointment") +pe = DocType("Patient Encounter") +ct = DocType("Codification Table") + + +class JubileePatientClaim(Document): + def before_insert(self): + if frappe.db.exists( + { + "doctype": "Jubilee Patient Claim", + "patient": self.patient, + "patient_appointment": self.patient_appointment, + "cardno": self.cardno, + "docstatus": 0, + } + ): + frappe.throw( + f"Jubilee Patient Claim is already exist for patient: #{self.patient} with appointment: #{self.patient_appointment}" + ) + + self.validate_appointment_info() + self.validate_multiple_appointments_per_authorization_no("before_insert") + + def after_insert(self): + folio_counter = frappe.db.get_all( + "Insurance Folio Counter", + filters={ + "company": self.company, + "claim_year": self.claim_year, + "claim_month": self.claim_month, + "insurance_provider": "Jubilee" + }, + fields=["name", "folio_no"], + page_length=1, + ) + + folio_no = 1 + if len(folio_counter) == 0: + new_folio_doc = frappe.get_doc( + { + "doctype": "Insurance Folio Counter", + "company": self.company, + "claim_year": self.claim_year, + "claim_month": self.claim_month, + "posting_date": now_datetime(), + "insurance_provider": "Jubilee", + "folio_no": folio_no, + } + ).insert(ignore_permissions=True) + new_folio_doc.reload() + else: + folio_no = cint(folio_counter[0].folio_no) + 1 + frappe.set_value("Insurance Folio Counter", folio_counter[0].name, { + "folio_no": folio_no, + "posting_date": now_datetime() + } + ) + frappe.set_value(self.doctype, self.name, "folio_no", folio_no) + + items = [] + for row in self.jubilee_patient_claim_item: + new_row = row.as_dict() + for fieldname in [ + "name", + "owner", + "creation", + "modified", + "modified_by", + "docstatus", + ]: + new_row[fieldname] = None + items.append(new_row) + + if len(items) > 0: + frappe.set_value( + self.doctype, self.name, "original_jubilee_patient_claim_item", items + ) + + self.reload() + + def validate(self): + if self.docstatus != 0: + return + + self.validate_appointment_info() + self.patient_encounters = self.get_patient_encounters() + if not self.patient_encounters: + frappe.throw(_("There are no submitted encounters for this application")) + + if not self.allow_changes: + finalized_encounter(self.patient_encounters[-1]) + + self.final_patient_encounter = self.get_final_patient_encounter() + self.set_claim_values() + + if not self.get("final_patient_encounter"): + self.final_patient_encounter = self.get_final_patient_encounter() + + self.calculate_totals() + + if not self.is_new(): + self.update_original_patient_claim() + + frappe.qb.update(pa).set(pa.jubilee_patient_claim, self.name).where( + pa.name == self.patient_appointment + ).run() + + def before_submit(self): + start_datetime = now_datetime() + frappe.msgprint("Submit process started: " + str(now_datetime())) + + # validation + self.validate_claim_items_and_claim_diseases() + self.validate_submit_date() + self.validate_item_status() + self.validate_multiple_appointments_per_authorization_no() + + if not self.get("patient_encounters"): + self.patient_encounters = self.get_patient_encounters() + + if not self.patient_signature: + get_missing_patient_signature(self) + + # self.claim_file_mem = get_claim_pdf_file(self) + frappe.msgprint("Sending Jubilee Claim: " + str(now_datetime())) + + self.send_jubilee_claim() + frappe.msgprint("Got response from Jubilee: " + str(now_datetime())) + end_datetime = now_datetime() + time_in_seconds = time_diff_in_seconds(str(end_datetime), str(start_datetime)) + frappe.msgprint( + "Total time to complete the process in seconds = " + str(time_in_seconds) + ) + + def on_submit(self): + pass + + def on_trash(self): + frappe.qb.update(pa).set("jubilee_patient_claim", "").where( + pa.jubilee_patient_claim == self.name + ).run() + + def validate_appointment_info(self): + appointment_details = frappe.db.get_value( + "Patient Appointment", + self.patient_appointment, + ["authorization_number", "coverage_plan_card_number"], + as_dict=1, + ) + + if self.authorization_no != appointment_details.authorization_number: + url = get_url_to_form("Patient Appointment", self.patient_appointment) + frappe.throw( + _( + f"Authorization Number: {self.authorization_no} of this Claim is not same to \ + Authorization Number: {appointment_details.authorization_number} on Patient Appointment: {self.patient_appointment}

\ + Please rectify before creating this Claim" + ) + ) + if self.cardno != appointment_details.coverage_plan_card_number: + url = get_url_to_form("Patient Appointment", self.patient_appointment) + frappe.throw( + _( + f"Card Number: {self.cardno} of this Claim is not same to \ + Card Number: {appointment_details.coverage_plan_card_number} on Patient Appointment: {self.patient_appointment}

\ + Please rectify before creating this Claim" + ) + ) + + def validate_multiple_appointments_per_authorization_no(self, caller=None): + """Validate if patient gets multiple appointments with same authorization number""" + + # Check if there are multiple claims with same authorization number + claim_details = frappe.db.get_all( + "Jubilee Patient Claim", + filters={ + "patient": self.patient, + "authorization_no": self.authorization_no, + "cardno": self.cardno, + "docstatus": 0, + }, + fields=["name", "patient", "patient_name", "hms_tz_claim_appointment_list"], + ) + claim_name_list = "" + merged_appointments = [] + for claim in claim_details: + url = get_url_to_form("Jubilee Patient Claim", claim["name"]) + claim_name_list += f"{claim['name']} , " + if claim["hms_tz_claim_appointment_list"]: + merged_appointments += json.loads( + claim["hms_tz_claim_appointment_list"] + ) + + if len(claim_details) > 1 and not caller: + frappe.throw( + f"

\ + This Authorization Number: {self.authorization_no} \ + has used multiple times in Jubilee Patient Claim: {claim_name_list}. \ + Please merge these {len(claim_details)} claims to Proceed

" + ) + + # Check if there are multiple patient appointments with same authorization number + appointment_documents = frappe.db.get_all( + "Patient Appointment", + filters={ + "patient": self.patient, + "authorization_number": self.authorization_no, + "coverage_plan_card_number": self.cardno, + "status": ["!=", "Cancelled"], + }, + pluck="name", + ) + + if len(appointment_documents) > 1: + self.validate_hold_card_status( + appointment_documents, claim_details, merged_appointments, caller + ) + elif caller: + frappe.msgprint("Release Patient Card", 20, alert=True) + + def validate_hold_card_status( + self, appointment_documents, claim_details, merged_appointments, caller=None + ): + msg = f"

Patient: {self.patient}-{self.patient_name} has multiple appointments:
" + + # check if there is any merging done before + reqd_throw_count = 0 + for appointment in appointment_documents: + url = get_url_to_form("Patient Appointment", appointment) + msg += f"{appointment} , " + + if merged_appointments: + for app in unique(merged_appointments): + if appointment == app: + reqd_throw_count += 1 + + if caller: + unique_claims_appointments = 0 + if len(unique(merged_appointments)) < len(claim_details): + unique_claims_appointments = len(claim_details) + else: + unique_claims_appointments = len(unique(merged_appointments)) + + if (len(appointment_documents) - 1) == unique_claims_appointments: + frappe.msgprint("Release Patient Card", 20, alert=True) + frappe.msgprint("Release Patient Card") + else: + msg += f"
with same authorization no: {self.authorization_no}

\ + Please Hold patient card until claims for all {len(appointment_documents)} appointments to be created.

" + frappe.msgprint("Please Hold Card", 20, alert=True) + frappe.msgprint(str(msg)) + + return + + msg += f"
with same authorization no: {self.authorization_no}

Please consider merging of claims\ + if Claims for all {len(appointment_documents)} appointments have already been created

" + + if reqd_throw_count < len(appointment_documents): + frappe.throw(msg) + + def validate_claim_items_and_claim_diseases(self): + try: + if len(self.jubilee_patient_claim_disease) == 0: + frappe.throw( + _( + "

Please add at least one disease code, before submitting this claim

" + ) + ) + if len(self.jubilee_patient_claim_item) == 0: + frappe.throw( + _( + "

Please add at least one item, before submitting this claim

" + ) + ) + + if self.total_amount != sum( + [item.amount_claimed for item in self.jubilee_patient_claim_item] + ): + frappe.throw( + _( + "

Total amount does not match with the total of the items

" + ) + ) + except Exception as e: + self.add_comment( + comment_type="Comment", + text=str(e), + ) + frappe.db.commit() + frappe.throw("") + + def validate_item_status(self): + for row in self.jubilee_patient_claim_item: + if row.status == "Draft": + frappe.throw( + f"Item: {frappe.bold(row.item_name)}, doctype: {frappe.bold(row.ref_doctype)}. \ + RowNo: {frappe.bold(row.idx)} is in Draft,\ + please contact relevant department for clarification" + ) + + def validate_submit_date(self): + submit_claim_month, submit_claim_year = frappe.get_cached_value( + "Company Insurance Setting", + {"company": self.company, "insurance_provider": "Jubilee"}, + ["submit_claim_month", "submit_claim_year"], + ) + + if not (submit_claim_month or submit_claim_year): + frappe.throw( + frappe.bold( + "Submit Claim Month or Submit Claim Year not found,\ + 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( + f"Claim Month: {frappe.bold(calendar.month_name[self.claim_month])} or Claim Year: {frappe.bold(self.claim_year)} \ + of this document is not same to Submit Claim Month: {frappe.bold(calendar.month_name[submit_claim_month])} \ + or Submit Claim Year: {frappe.bold(submit_claim_year)} on Company Insurance Setting" + ) + + @frappe.whitelist() + def get_appointments(self): + appointment_list = frappe.db.get_all( + "Jubilee Patient Claim", + filters={ + "patient": self.patient, + "authorization_no": self.authorization_no, + "cardno": self.cardno, + }, + fields=["patient_appointment", "hms_tz_claim_appointment_list"], + ) + if len(appointment_list) == 1: + frappe.throw( + _( + f"

\ + This Authorization no: {frappe.bold(self.authorization_no)} \ + as used only once on
Jubilee Patient Claim: {frappe.bold(self.name)}
\ +

" + ) + ) + + app_list = [] + for app_name in appointment_list: + if app_name["hms_tz_claim_appointment_list"]: + app_numbers = json.loads(app_name["hms_tz_claim_appointment_list"]) + app_list += app_numbers + + for d in app_numbers: + frappe.qb.update(pa).set(pa.jubilee_patient_claim, self.name).where( + pa.name == d + ).run() + else: + app_list.append(app_name["patient_appointment"]) + frappe.qb.update(pa).set(pa.jubilee_patient_claim, self.name).where( + pa.name == app_name["patient_appointment"] + ).run() + + app_list = list(set(app_list)) + self.allow_changes = 0 + self.hms_tz_claim_appointment_list = json.dumps(app_list) + + self.save(ignore_permissions=True) + + def get_patient_encounters(self): + if not self.hms_tz_claim_appointment_list: + patient_appointment = self.patient_appointment + else: + patient_appointment = ["in", json.loads(self.hms_tz_claim_appointment_list)] + + patient_encounters = frappe.db.get_all( + "Patient Encounter", + filters={ + "appointment": patient_appointment, + "docstatus": 1, + }, + fields={"name", "encounter_date"}, + order_by="`creation` ASC", + ) + return patient_encounters + + def get_final_patient_encounter(self): + appointment = None + if self.hms_tz_claim_appointment_list: + appointment = ["in", json.loads(self.hms_tz_claim_appointment_list)] + else: + appointment = self.patient_appointment + + patient_encounter_list = frappe.db.get_all( + "Patient Encounter", + filters={ + "appointment": appointment, + "docstatus": 1, + "duplicated": 0, + "encounter_type": "Final", + }, + fields=["name", "practitioner", "inpatient_record"], + order_by="`modified` desc", + ) + if len(patient_encounter_list) == 0: + frappe.throw(_("There no Final Patient Encounter for this Appointment")) + + return patient_encounter_list + + def set_claim_values(self): + if not self.folio_id: + self.folio_id = str(uuid.uuid1()) + + self.facility_code = frappe.get_cached_value( + "Company Insurance Setting", + {"enable": 1, "insurance_provider": "Jubilee", "company": self.company}, + "facility_code", + ) + + self.posting_date = nowdate() + self.serial_no = cint(self.name[-9:]) + self.item_crt_by = get_fullname(frappe.session.user) + practitioners = [d.practitioner for d in self.final_patient_encounter] + practitioner_details = frappe.db.get_all( + "Healthcare Practitioner", + {"name": ["in", practitioners]}, + ["practitioner_name", "tz_mct_code"], + ) + if not practitioner_details[0].practitioner_name: + frappe.throw( + _(f"There is no Practitioner Name for Practitioner: {practitioners[0]}") + ) + + if not practitioner_details[0].tz_mct_code: + frappe.throw( + _( + f"There is no TZ MCT Code for Practitioner {practitioner_details[0].practitioner_name}" + ) + ) + + self.practitioner_name = practitioner_details[0].practitioner_name + self.practitioner_no = ",".join([d.tz_mct_code for d in practitioner_details]) + inpatient_record = [ + h.inpatient_record + for h in self.final_patient_encounter + if h.inpatient_record + ] or None + self.inpatient_record = inpatient_record[0] if inpatient_record else None + # Reset values for every validate + self.patient_type_code = "OUT" + self.date_admitted = None + self.admitted_time = None + self.date_discharge = None + self.discharge_time = None + if self.inpatient_record: + ( + discharge_date, + scheduled_date, + admitted_datetime, + time_created, + ) = frappe.db.get_value( + "Inpatient Record", + self.inpatient_record, + ["discharge_date", "scheduled_date", "admitted_datetime", "creation"], + ) + + if getdate(scheduled_date) < getdate(admitted_datetime): + self.date_admitted = scheduled_date + self.admitted_time = get_time(get_datetime(time_created)) + else: + self.date_admitted = getdate(admitted_datetime) + self.admitted_time = get_time(get_datetime(admitted_datetime)) + + # If the patient is same day discharged then consider it as Outpatient + if self.date_admitted == getdate(discharge_date): + self.patient_type_code = "OUT" + self.date_admitted = None + else: + self.patient_type_code = "IN" + self.date_discharge = discharge_date + + # the time claim is created will treated as discharge time + # because there is no field of discharge time on Inpatient Record + self.discharge_time = nowtime() + + self.attendance_date, self.attendance_time = frappe.db.get_value( + "Patient Appointment", + self.patient_appointment, + ["appointment_date", "appointment_time"], + ) + if self.date_discharge: + self.claim_year = int(self.date_discharge.strftime("%Y")) + self.claim_month = int(self.date_discharge.strftime("%m")) + else: + self.claim_year = int(self.attendance_date.strftime("%Y")) + self.claim_month = int(self.attendance_date.strftime("%m")) + + self.patient_file_no = self.patient + + if not self.allow_changes: + self.set_patient_claim_disease() + self.set_patient_claim_item() + + def calculate_totals(self): + self.total_amount = 0 + for item in self.jubilee_patient_claim_item: + item.amount_claimed = item.unit_price * item.item_quantity + item.folio_item_id = item.folio_item_id or str(uuid.uuid1()) + item.date_created = item.date_created or nowdate() + item.folio_id = item.folio_id or self.folio_id + + self.total_amount += item.amount_claimed + + for item in self.jubilee_patient_claim_disease: + item.folio_id = item.folio_id or self.folio_id + item.folio_disease_id = item.folio_disease_id or str(uuid.uuid1()) + item.date_created = item.date_created or nowdate() + + def set_clinical_notes(self, encounter_doc): + if not self.clinical_notes: + patient_name = f"Patient: {self.patient_name}," + date_of_birth = f"Date of Birth: {self.date_of_birth}," + gender = f"Gender: {self.gender}," + years = ( + f"Age: {(date_diff(nowdate(), self.date_of_birth))//365} years," + ) + self.clinical_notes = ( + " ".join([patient_name, gender, date_of_birth, years]) + "
" + ) + + if not encounter_doc.examination_detail: + frappe.msgprint( + _( + f"Encounter {encounter_doc.name} does not have Examination Details defined. Check the encounter." + ), + alert=True, + ) + + department = frappe.get_cached_value( + "Healthcare Practitioner", encounter_doc.practitioner, "department" + ) + self.clinical_notes += f"
PractitionerName: {encounter_doc.practitioner_name}, Speciality: {department}, DateofService: {encounter_doc.encounter_date} {encounter_doc.encounter_time}
" + self.clinical_notes += encounter_doc.examination_detail or "" + + if len(encounter_doc.get("drug_prescription")) > 0: + self.clinical_notes += "
Medication(s):
" + for row in encounter_doc.get("drug_prescription"): + med_info = "" + if row.dosage: + med_info += f", Dosage: {row.dosage}" + if row.period: + med_info += f", Period: {row.period}" + if row.dosage_form: + med_info += f", Dosage Form: {row.dosage_form}" + + self.clinical_notes += f"Drug: {row.drug_code} {med_info}" + self.clinical_notes += "
" + self.clinical_notes = self.clinical_notes.replace('"', " ") + + def set_patient_claim_disease(self): + self.jubilee_patient_claim_disease = [] + preliminary_diagnosis_list = ( + frappe.qb.from_(ct) + .inner_join(pe) + .on(ct.parent == pe.name) + .select( + ct.name, + ct.parent, + ct.code, + ct.medical_code, + ct.description, + ct.modified_by, + ct.modified, + ct.creation, + pe.practitioner, + ) + .where( + (ct.parenttype == "Patient Encounter") + & (ct.parentfield == "patient_encounter_preliminary_diagnosis") + & ( + ct.parent.isin( + [encounter.name for encounter in self.patient_encounters] + ) + ) + ) + ).run(as_dict=True) + + for row in preliminary_diagnosis_list: + new_row = self.append("jubilee_patient_claim_disease", {}) + new_row.diagnosis_type = "Provisional Diagnosis" + new_row.status = "Provisional" + new_row.patient_encounter = row.encounter + new_row.codification_table = row.name + new_row.medical_code = row.medical_code + # Convert the ICD code of CDC to Jubilee + if row.code and len(row.code) > 3 and "." not in row.code: + new_row.disease_code = row.code[:3] + "." + (row.code[3:4] or "0") + elif row.code and len(row.code) <= 5 and "." in row.code: + new_row.disease_code = row.code + else: + new_row.disease_code = row.code[:3] + new_row.description = row.description[0:139] + new_row.item_crt_by = row.practitioner + new_row.date_created = row.modified.strftime("%Y-%m-%d") + + final_diagnosis_list = ( + frappe.qb.from_(ct) + .inner_join(pe) + .on(ct.parent == pe.name) + .select( + ct.name, + ct.parent, + ct.code, + ct.medical_code, + ct.description, + ct.modified_by, + ct.modified, + pe.practitioner, + ) + .where( + (ct.parenttype == "Patient Encounter") + & (ct.parentfield == "patient_encounter_final_diagnosis") + & ( + ct.parent.isin( + [encounter.name for encounter in self.patient_encounters] + ) + ) + ) + ).run(as_dict=True) + + for row in final_diagnosis_list: + new_row = self.append("jubilee_patient_claim_disease", {}) + new_row.diagnosis_type = "Final Diagnosis" + new_row.status = "Final" + new_row.patient_encounter = row.parent + new_row.codification_table = row.name + new_row.medical_code = row.medical_code + # Convert the ICD code of CDC to Jubilee + if row.code and len(row.code) > 3 and "." not in row.code: + new_row.disease_code = row.code[:3] + "." + (row.code[3:4] or "0") + elif row.code and len(row.code) <= 5 and "." in row.code: + new_row.disease_code = row.code + else: + new_row.disease_code = row.code[:3] + new_row.description = row.description[0:139] + new_row.item_crt_by = row.practitioner + new_row.date_created = row.modified.strftime("%Y-%m-%d") + + def set_patient_claim_item(self, called_method=None): + if called_method == "enqueue": + self.reload() + self.final_patient_encounter = self.get_final_patient_encounter() + self.patient_encounters = self.get_patient_encounters() + + self.jubilee_patient_claim_item = [] + self.clinical_notes = "" + if not self.inpatient_record: + for encounter in self.patient_encounters: + encounter_doc = frappe.get_doc("Patient Encounter", encounter.name) + + self.set_clinical_notes(encounter_doc) + self.set_service_items(encounter_doc) + + else: + ip_doc = frappe.get_doc("Inpatient Record", self.inpatient_record) + + occupancies = self.get_occupancies(ip_doc) + + for occupancy in occupancies: + if not occupancy.is_confirmed: + continue + + checkin_date = occupancy.check_in.strftime("%Y-%m-%d") + + self.set_ipd_consultancies(ip_doc, occupancy, checkin_date) + + for encounter in self.patient_encounters: + if str(encounter.encounter_date) != checkin_date: + continue + + encounter_doc = frappe.get_doc("Patient Encounter", encounter.name) + + # allow clinical notes to be added to the claim even if the service is not chargeable and encounters will be ignored + self.set_clinical_notes(encounter_doc) + + if not occupancy.is_service_chargeable: + continue + + self.set_service_items(encounter_doc) + + self.set_opd_consultancy() + + def set_service_items(self, encounter_doc): + for child in get_child_map(): + for row in encounter_doc.get(child.get("table")): + if row.prescribe or row.is_cancelled: + continue + + item_code = frappe.db.get_value( + child.get("doctype"), row.get(child.get("item")), "item" + ) + + 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("jubilee_patient_claim_item", {}) + new_row.item_name = row.get(child.get("item_name")) + new_row.item_code = get_item_refcode(item_code) + new_row.item_quantity = delivered_quantity or 1 + new_row.unit_price = row.get("amount") + new_row.amount_claimed = new_row.unit_price * new_row.item_quantity + new_row.approval_ref_no = get_approval_number_from_LRPMT( + child["ref_doctype"], row.get(child["ref_docname"]) + ) + + new_row.status = get_LRPMT_status(encounter_doc.name, row, child) + new_row.patient_encounter = encounter_doc.name + new_row.ref_doctype = row.doctype + new_row.ref_docname = row.name + new_row.folio_item_id = str(uuid.uuid1()) + new_row.folio_id = self.folio_id + new_row.date_created = row.modified.strftime("%Y-%m-%d") + new_row.item_crt_by = encounter_doc.practitioner + + def set_opd_consultancy(self): + patient_appointment_list = [] + if not self.hms_tz_claim_appointment_list: + patient_appointment_list.append(self.patient_appointment) + else: + patient_appointment_list = json.loads(self.hms_tz_claim_appointment_list) + + sorted_patient_claim_item = sorted( + self.jubilee_patient_claim_item, + key=lambda k: ( + k.get("ref_doctype"), + k.get("item_code"), + k.get("date_created"), + ), + ) + idx = len(patient_appointment_list) + 1 + for row in sorted_patient_claim_item: + row.idx = idx + idx += 1 + self.jubilee_patient_claim_item = sorted_patient_claim_item + + appointment_idx = 1 + for appointment_no in patient_appointment_list: + patient_appointment_doc = frappe.get_doc( + "Patient Appointment", appointment_no + ) + + # SHM Rock: 202 + if patient_appointment_doc.has_no_consultation_charges == 1: + continue + + if not self.inpatient_record and not patient_appointment_doc.follow_up: + item_code = patient_appointment_doc.billing_item + new_row = self.append("jubilee_patient_claim_item", {}) + new_row.item_name = patient_appointment_doc.billing_item + new_row.item_code = get_item_refcode(item_code) + new_row.item_quantity = 1 + new_row.unit_price = patient_appointment_doc.paid_amount + new_row.amount_claimed = patient_appointment_doc.paid_amount + new_row.approval_ref_no = "" + new_row.ref_doctype = patient_appointment_doc.doctype + new_row.ref_docname = patient_appointment_doc.name + new_row.folio_item_id = str(uuid.uuid1()) + new_row.folio_id = self.folio_id + new_row.date_created = patient_appointment_doc.modified.strftime( + "%Y-%m-%d" + ) + new_row.item_crt_by = get_fullname(patient_appointment_doc.modified_by) + new_row.idx = appointment_idx + appointment_idx += 1 + + def set_occupancies(self, ip_doc): + beds = [] + dates = [] + admission_encounter_doc = frappe.get_doc( + "Patient Encounter", ip_doc.admission_encounter + ) + for occupancy in ip_doc.inpatient_occupancies: + if not occupancy.is_confirmed: + continue + + service_unit_type = frappe.get_cached_value( + "Healthcare Service Unit", + occupancy.service_unit, + "service_unit_type", + ) + + ( + is_service_chargeable, + is_consultancy_chargeable, + item_code, + ) = frappe.get_cached_value( + "Healthcare Service Unit Type", + service_unit_type, + ["is_service_chargeable", "is_consultancy_chargeable", "item"], + ) + + # update occupancy object + occupancy.update( + { + "service_unit_type": service_unit_type, + "is_service_chargeable": is_service_chargeable, + "is_consultancy_chargeable": is_consultancy_chargeable, + } + ) + + checkin_date = occupancy.check_in.strftime("%Y-%m-%d") + # Add only in occupancy once a day. + if checkin_date not in dates: + dates.append(checkin_date) + beds.append(occupancy) + + item_rate = get_item_rate( + item_code, + self.company, + admission_encounter_doc.insurance_subscription, + admission_encounter_doc.insurance_company, + ) + new_row = self.append("jubilee_patient_claim_item", {}) + new_row.item_name = occupancy.service_unit + new_row.item_code = get_item_refcode(item_code) + new_row.item_quantity = 1 + new_row.unit_price = item_rate + new_row.amount_claimed = new_row.unit_price * new_row.item_quantity + new_row.approval_ref_no = "" + new_row.patient_encounter = admission_encounter_doc.name + new_row.ref_doctype = occupancy.doctype + new_row.ref_docname = occupancy.name + new_row.folio_item_id = str(uuid.uuid1()) + new_row.folio_id = self.folio_id + new_row.date_created = occupancy.modified.strftime("%Y-%m-%d") + new_row.item_crt_by = get_fullname(occupancy.modified_by) + + return beds + + def set_ipd_consultancies(self, ip_doc, occupancy, checkin_date): + if occupancy.is_consultancy_chargeable: + for row_item in ip_doc.inpatient_consultancy: + if ( + row_item.is_confirmed + and str(row_item.date) == checkin_date + and row_item.rate + ): + item_code = row_item.consultation_item + new_row = self.append("jubilee_patient_claim_item", {}) + new_row.item_name = row_item.consultation_item + new_row.item_code = get_item_refcode(item_code) + new_row.item_quantity = 1 + new_row.unit_price = row_item.rate + new_row.amount_claimed = row_item.rate + new_row.approval_ref_no = "" + new_row.patient_encounter = ( + row_item.encounter or ip_doc.admission_encounter + ) + new_row.ref_doctype = row_item.doctype + new_row.ref_docname = row_item.name + new_row.folio_item_id = str(uuid.uuid1()) + new_row.folio_id = self.folio_id + new_row.date_created = row_item.modified.strftime("%Y-%m-%d") + new_row.item_crt_by = get_fullname(row_item.modified_by) + + @frappe.whitelist() + def send_jubilee_claim(self): + json_data, json_data_wo_files = self.get_folio_json_data() + token = get_jubilee_claimsservice_token(self.company, "Jubilee") + claimsserver_url = frappe.get_cached_value( + "Company Insurance Setting", + {"company": self.company, "insurance_provider": "Jubilee"}, + "claimsserver_url", + ) + headers = { + "Authorization": "Bearer " + token, + "Content-Type": "application/json", + } + url = str(claimsserver_url) + "/jubileeapi/SendClaim" + r = None + try: + r = requests.post(url, headers=headers, data=json_data, timeout=300) + + if r.status_code != 200: + frappe.throw( + f"Jubilee Server responded with HTTP status code: {r.status_code}

{str(r.text) if r.text else str(r)}" + ) + else: + data = json.loads(r.text) + if data.get("status") == "ERROR": + frappe.throw(str(data.get("description"))) + + else: + frappe.msgprint(str(data.get("description"))) + if data: + add_jubilee_log( + request_type="SubmitClaim", + request_url=url, + request_header=headers, + request_body=json_data_wo_files, + response_data=data, + status_code=r.status_code, + ref_doctype=self.doctype, + ref_docname=self.name, + company=self.company, + ) + + frappe.msgprint( + _("The claim has been sent successfully"), alert=True + ) + + except Exception as e: + add_jubilee_log( + request_type="SubmitClaim", + request_url=url, + request_header=headers, + request_body=json_data, + response_data=(r.text if str(r) else "NO RESPONSE r. Timeout???"), + status_code=( + r.status_code if str(r) else "NO STATUS CODE r. Timeout???" + ), + ref_doctype=self.doctype, + ref_docname=self.name, + company=self.company, + ) + self.add_comment( + comment_type="Comment", + text=r.text if str(r) else "NO RESPONSE", + ) + frappe.db.commit() + + frappe.throw( + "This folio was NOT submitted due to the error above!. \ + Please retry after resolving the problem. " + + str(now_datetime()) + ) + + def get_folio_json_data(self): + folio_data = frappe._dict() + folio_data.entities = [] + entities = frappe._dict() + entities.FolioID = self.folio_id + entities.ClaimYear = self.claim_year + entities.ClaimYear = self.claim_year + entities.ClaimMonth = self.claim_month + entities.FolioNo = self.folio_no + entities.SerialNo = self.serial_no + # entities.FacilityCode = self.facility_code + entities.CardNo = self.cardno.strip() + entities.BillNo = self.name + entities.FirstName = self.first_name + entities.LastName = self.last_name + entities.Gender = self.gender + entities.DateOfBirth = str(self.date_of_birth) + entities.Age = f"{(date_diff(nowdate(), self.date_of_birth)) // 365}" + entities.TelephoneNo = self.telephone_no + entities.PatientFileNo = self.patient_file_no + entities.AuthorizationNo = self.authorization_no + entities.AttendanceDate = str(self.attendance_date) + entities.PatientTypeCode = self.patient_type_code + if self.patient_type_code == "IN": + entities.DateAdmitted = ( + str(self.date_admitted) + " " + str(self.admitted_time) + ) + entities.DateDischarged = ( + str(self.date_discharge) + " " + str(self.discharge_time) + ) + entities.PractitionerNo = self.practitioner_no + # entities.PractitionerName = self.practitioner_name + entities.ProviderID = ( + frappe.get_cached_value( + "Company Insurance Setting", + {"company": self.company, "insurance_provider": "Jubilee"}, + "providerid", + ) + or None + ) + entities.ClinicalNotes = self.clinical_notes + entities.AmountClaimed = sum( + [item.amount_claimed for item in self.jubilee_patient_claim_item] + ) + entities.DelayReason = self.delayreason + entities.LateSubmissionReason = self.delayreason + entities.LateAuthorizationReason = None + entities.EmergencyAuthorizationReason = get_emergency_reason( + self.patient_appointment + ) + entities.CreatedBy = self.item_crt_by + entities.DateCreated = str(self.posting_date) + entities.LastModifiedBy = get_fullname(frappe.session.user) + entities.LastModified = str(now_datetime()) + entities.PatientFile = generate_pdf(self) + entities.ClaimFile = get_claim_pdf_file(self) + + entities.FolioDiseases = [] + for disease in self.jubilee_patient_claim_disease: + FolioDisease = frappe._dict() + FolioDisease.DiseaseCode = disease.disease_code + FolioDisease.Remarks = None + FolioDisease.Status = disease.status + FolioDisease.CreatedBy = disease.item_crt_by + FolioDisease.DateCreated = str(disease.date_created) + FolioDisease.LastModifiedBy = disease.item_crt_by + FolioDisease.LastModified = str(disease.date_created) + entities.FolioDiseases.append(FolioDisease) + + entities.FolioItems = [] + for item in self.jubilee_patient_claim_item: + FolioItem = frappe._dict() + FolioItem.ItemCode = item.item_code + FolioItem.OtherDetails = None + FolioItem.ItemQuantity = item.item_quantity + FolioItem.UnitPrice = item.unit_price + FolioItem.AmountClaimed = item.amount_claimed + FolioItem.ApprovalRefNo = item.approval_ref_no or None + FolioItem.CreatedBy = item.item_crt_by + FolioItem.DateCreated = str(item.date_created) + FolioItem.LastModifiedBy = item.item_crt_by + FolioItem.LastModified = str(item.date_created) + entities.FolioItems.append(FolioItem) + + folio_data.entities.append(entities) + jsonStr = json.dumps(folio_data) + + # Strip off the patient file + folio_data.entities[0].PatientFile = "Stripped off" + folio_data.entities[0].ClaimFile = "Stripped off" + jsonStr_wo_files = json.dumps(folio_data) + return jsonStr, jsonStr_wo_files + + def update_original_patient_claim(self): + """Update original patient claim incase merging if done for this claim""" + + ref_docnames = [] + for item in self.original_jubilee_patient_claim_item: + if item.ref_docname: + d = item.ref_docname.split(",") + ref_docnames.extend(d) + + for row in self.jubilee_patient_claim_item: + if row.ref_docname not in ref_docnames: + new_row = row.as_dict() + for fieldname in [ + "name", + "owner", + "creation", + "modified", + "modified_by", + "docstatus", + ]: + new_row[fieldname] = None + + self.append("original_jubilee_patient_claim_item", new_row) + + @frappe.whitelist() + def reconcile_repeated_items(self): + def reconcile_items(claim_items): + unique_items = [] + repeated_items = [] + unique_refcodes = [] + + for row in claim_items: + if row.item_code not in unique_refcodes: + unique_refcodes.append(row.item_code) + unique_items.append(row) + else: + repeated_items.append(row) + + if len(repeated_items) > 0: + items = [] + for item in unique_items: + ref_docnames = [] + ref_encounters = [] + + for d in repeated_items: + if item.item_code == d.item_code: + item.item_quantity += d.item_quantity + item.amount_claimed += d.amount_claimed + + if d.approval_ref_no: + approval_ref_no = None + if item.approval_ref_no: + approval_ref_no = ( + str(item.approval_ref_no) + + "," + + str(d.approval_ref_no) + ) + else: + approval_ref_no = d.approval_ref_no + + item.approval_ref_no = approval_ref_no + + if d.patient_encounter: + ref_encounters.append(d.patient_encounter) + if d.ref_docname: + ref_docnames.append(d.ref_docname) + + if item.status != "Submitted" and d.status == "Submitted": + item.status = "Submitted" + + if item.patient_encounter: + ref_encounters.append(item.patient_encounter) + if item.ref_docname: + ref_docnames.append(item.ref_docname) + + if len(ref_encounters) > 0: + item.patient_encounter = ",".join(set(ref_encounters)) + + if len(ref_docnames) > 0: + item.ref_docname = ",".join(set(ref_docnames)) + + items.append(item) + + for record in repeated_items: + frappe.delete_doc( + record.doctype, + record.name, + force=True, + ignore_permissions=True, + ignore_on_trash=True, + delete_permanently=True, + ) + return items + + else: + return unique_items + + # claim_doc = frappe.get_doc("Jubilee Patient Claim", self.name) + self.allow_changes = 1 + self.jubilee_patient_claim_item = reconcile_items( + self.jubilee_patient_claim_item + ) + self.original_jubilee_patient_claim_item = reconcile_items( + self.original_jubilee_patient_claim_item + ) + + self.save(ignore_permissions=True) + self.reload() + return True + + +def get_child_map(): + childs_map = [ + { + "table": "lab_test_prescription", + "doctype": "Lab Test Template", + "item": "lab_test_code", + "item_name": "lab_test_name", + "comment": "lab_test_comment", + "ref_doctype": "Lab Test", + "ref_docname": "lab_test", + }, + { + "table": "radiology_procedure_prescription", + "doctype": "Radiology Examination Template", + "item": "radiology_examination_template", + "item_name": "radiology_procedure_name", + "comment": "radiology_test_comment", + "ref_doctype": "Radiology Examination", + "ref_docname": "radiology_examination", + }, + { + "table": "procedure_prescription", + "doctype": "Clinical Procedure Template", + "item": "procedure", + "item_name": "procedure_name", + "comment": "comments", + "ref_doctype": "Clinical Procedure", + "ref_docname": "clinical_procedure", + }, + { + "table": "drug_prescription", + "doctype": "Medication", + "item": "drug_code", + "item_name": "drug_name", + "comment": "comment", + "ref_doctype": "Delivery Note Item", + "ref_docname": "dn_detail", + }, + { + "table": "therapies", + "doctype": "Therapy Type", + "item": "therapy_type", + "item_name": "therapy_type", + "comment": "comment", + "ref_doctype": "", + "ref_docname": "", + }, + ] + return childs_map + + +def get_item_refcode(item_code): + code_list = frappe.db.get_all( + "Item Customer Detail", + filters={"parent": item_code, "customer_name": ["like", "%Jubilee%"]}, + fields=["ref_code"], + ) + if len(code_list) == 0: + frappe.throw(_(f"Item: {item_code} has not Jubilee Code Reference")) + # return None + + ref_code = code_list[0].ref_code + if not ref_code: + frappe.throw(_(f"Item: {item_code} has not Jubilee Code Reference")) + # return None + + return ref_code + + +def get_LRPMT_status(encounter_no, row, child): + status = None + if child["doctype"] == "Therapy Type" or row.get(child["ref_docname"]): + status = "Submitted" + + elif child["doctype"] == "Lab Test Template" and not row.get(child["ref_docname"]): + lab_workflow_state = frappe.get_value( + "Lab Test", + { + "ref_docname": encounter_no, + "ref_doctype": "Patient Encounter", + "hms_tz_ref_childname": row.name, + }, + "workflow_state", + ) + if lab_workflow_state and lab_workflow_state != "Lab Test Requested": + status = "Submitted" + else: + status = "Draft" + else: + status = "Draft" + + return status + + +def get_missing_patient_signature(doc): + if doc.patient: + signature = frappe.get_cached_value("Patient", doc.patient, "patient_signature") + if not signature: + frappe.throw(_("Patient signature is required")) + + doc.patient_signature = signature + + +def get_emergency_reason(appointment): + remarks = frappe.db.get_value( + "Patient Appointment", + {"name": appointment, "appointment_type": ["like", "Emergency%"]}, + "remarks", + ) + + return remarks or None + + +def generate_pdf(doc): + file_list = frappe.db.get_all( + "File", + filters={ + "attached_to_doctype": "Jubilee Patient Claim", + "file_name": str(doc.name + ".pdf"), + }, + ) + if file_list: + patientfile = frappe.get_cached_doc("File", file_list[0].name) + if patientfile: + pdf = patientfile.get_content() + return to_base64(pdf) + + data_list = [] + for i in doc.patient_encounters: + data_list.append(i.name) + + doctype = dict({"Patient Encounter": data_list}) + print_format = "" + default_print_format = frappe.db.get_value( + "Property Setter", + dict(property="default_print_format", doc_type="Patient Encounter"), + "value", + ) + if default_print_format: + print_format = default_print_format + else: + print_format = "Patient File" + + pdf = download_multi_pdf( + doctype, doc.name, print_format=print_format, no_letterhead=1 + ) + if pdf: + ret = frappe.get_doc( + { + "doctype": "File", + "attached_to_doctype": "Jubilee Patient Claim", + "attached_to_name": doc.name, + "folder": "Home/Attachments", + "file_name": doc.name + ".pdf", + "file_url": "/private/files/" + doc.name + ".pdf", + "content": pdf, + "is_private": 1, + } + ) + ret.save(ignore_permissions=1) + # ret.db_update() + return to_base64(pdf) + + +def download_multi_pdf(doctype, name, print_format=None, no_letterhead=0): + output = PdfFileWriter() + if isinstance(doctype, dict): + for doctype_name in doctype: + for doc_name in doctype[doctype_name]: + try: + output = frappe.get_print( + doctype_name, + doc_name, + print_format, + as_pdf=True, + output=output, + no_letterhead=no_letterhead, + ) + except Exception: + frappe.log_error(frappe.get_traceback()) + + return read_multi_pdf(output) + + +def read_multi_pdf(output): + fname = os.path.join("/tmp", f"frappe-pdf-{frappe.generate_hash()}.pdf") + output.write(open(fname, "wb")) + + with open(fname, "rb") as fileobj: + filedata = fileobj.read() + + return filedata + + +def get_claim_pdf_file(doc): + file_list = frappe.db.get_all( + "File", + filters={ + "attached_to_doctype": "Jubilee Patient Claim", + "file_name": str(doc.name + "-claim.pdf"), + }, + ) + if file_list: + for file in file_list: + frappe.delete_doc("File", file.name, ignore_permissions=True, force=True) + + default_print_format = frappe.db.get_value( + "Property Setter", + dict(property="default_print_format", doc_type=doc.doctype), + "value", + ) + if default_print_format: + print_format = default_print_format + else: + print_format = "Jubilee Form 2A & B" + + html = frappe.get_print( + doc.doctype, doc.name, print_format, doc=None, no_letterhead=1 + ) + + filename = f"""{doc.name.replace(" ", "-").replace("/", "-")}-claim""" + pdf = get_pdf(html) + if pdf: + ret = frappe.get_doc( + { + "doctype": "File", + "attached_to_doctype": doc.doctype, + "attached_to_name": doc.name, + "folder": "Home/Attachments", + "file_name": filename + ".pdf", + "file_url": "/private/files/" + filename + ".pdf", + "content": pdf, + "is_private": 1, + } + ) + ret.insert(ignore_permissions=True) + ret.db_update() + if not ret.name: + frappe.throw("ret name not exist") + base64_data = to_base64(pdf) + return base64_data + else: + frappe.throw(_("Failed to generate pdf")) diff --git a/hms_tz/jubilee/doctype/jubilee_patient_claim/test_jubilee_patient_claim.py b/hms_tz/jubilee/doctype/jubilee_patient_claim/test_jubilee_patient_claim.py new file mode 100644 index 00000000..71af459d --- /dev/null +++ b/hms_tz/jubilee/doctype/jubilee_patient_claim/test_jubilee_patient_claim.py @@ -0,0 +1,9 @@ +# Copyright (c) 2024, Aakvatech and Contributors +# See license.txt + +# import frappe +from frappe.tests.utils import FrappeTestCase + + +class TestJubileePatientClaim(FrappeTestCase): + pass diff --git a/hms_tz/jubilee/doctype/jubilee_patient_claim_disease/__init__.py b/hms_tz/jubilee/doctype/jubilee_patient_claim_disease/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/hms_tz/jubilee/doctype/jubilee_patient_claim_disease/jubilee_patient_claim_disease.json b/hms_tz/jubilee/doctype/jubilee_patient_claim_disease/jubilee_patient_claim_disease.json new file mode 100644 index 00000000..399c9e7c --- /dev/null +++ b/hms_tz/jubilee/doctype/jubilee_patient_claim_disease/jubilee_patient_claim_disease.json @@ -0,0 +1,128 @@ +{ + "actions": [], + "creation": "2024-07-26 13:18:04.846672", + "default_view": "List", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "diagnosis_type", + "status", + "medical_code", + "disease_code", + "description", + "column_break_7", + "patient_encounter", + "codification_table", + "folio_id", + "folio_disease_id", + "created_by", + "item_crt_by", + "date_created" + ], + "fields": [ + { + "fieldname": "diagnosis_type", + "fieldtype": "Data", + "label": "Diagnosis Type", + "permlevel": 1 + }, + { + "fieldname": "status", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Status", + "options": "Provisional\nFinal" + }, + { + "fieldname": "medical_code", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Medical Code", + "options": "Medical Code", + "permlevel": 1 + }, + { + "fetch_from": "medical_code.code", + "fetch_if_empty": 1, + "fieldname": "disease_code", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Disease Code", + "permlevel": 1 + }, + { + "fetch_from": "medical_code.description", + "fieldname": "description", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Description", + "read_only": 1 + }, + { + "fieldname": "column_break_7", + "fieldtype": "Column Break" + }, + { + "fieldname": "patient_encounter", + "fieldtype": "Link", + "label": "Patient Encounter", + "options": "Patient Encounter", + "read_only": 1 + }, + { + "fieldname": "codification_table", + "fieldtype": "Link", + "label": "Codification Table", + "options": "Codification Table", + "read_only": 1 + }, + { + "fieldname": "folio_id", + "fieldtype": "Data", + "label": "Folio ID", + "read_only": 1 + }, + { + "fieldname": "folio_disease_id", + "fieldtype": "Data", + "label": "Folio Disease ID", + "read_only": 1 + }, + { + "fieldname": "created_by", + "fieldtype": "Data", + "hidden": 1, + "label": "Old Created By", + "read_only": 1 + }, + { + "fieldname": "item_crt_by", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Created By", + "read_only": 1 + }, + { + "fieldname": "date_created", + "fieldtype": "Date", + "in_list_view": 1, + "label": "Date Created", + "permlevel": 1 + } + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2024-07-26 13:18:04.846672", + "modified_by": "Administrator", + "module": "Jubilee", + "name": "Jubilee Patient Claim Disease", + "owner": "Administrator", + "permissions": [], + "quick_entry": 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_patient_claim_disease/jubilee_patient_claim_disease.py b/hms_tz/jubilee/doctype/jubilee_patient_claim_disease/jubilee_patient_claim_disease.py new file mode 100644 index 00000000..98381955 --- /dev/null +++ b/hms_tz/jubilee/doctype/jubilee_patient_claim_disease/jubilee_patient_claim_disease.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 JubileePatientClaimDisease(Document): + pass diff --git a/hms_tz/jubilee/doctype/jubilee_patient_claim_item/__init__.py b/hms_tz/jubilee/doctype/jubilee_patient_claim_item/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/hms_tz/jubilee/doctype/jubilee_patient_claim_item/jubilee_patient_claim_item.json b/hms_tz/jubilee/doctype/jubilee_patient_claim_item/jubilee_patient_claim_item.json new file mode 100644 index 00000000..5bfceb2a --- /dev/null +++ b/hms_tz/jubilee/doctype/jubilee_patient_claim_item/jubilee_patient_claim_item.json @@ -0,0 +1,152 @@ +{ + "actions": [], + "creation": "2024-07-26 13:16:36.223382", + "default_view": "List", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "ref_doctype", + "item_name", + "item_code", + "item_quantity", + "unit_price", + "amount_claimed", + "created_by", + "item_crt_by", + "column_break_8", + "status", + "patient_encounter", + "ref_docname", + "approval_ref_no", + "folio_item_id", + "folio_id", + "date_created" + ], + "fields": [ + { + "fieldname": "ref_doctype", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Ref DocType", + "options": "DocType", + "permlevel": 1, + "reqd": 1 + }, + { + "fieldname": "item_name", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Item Name", + "read_only": 1 + }, + { + "columns": 1, + "fieldname": "item_code", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Item Code", + "read_only": 1 + }, + { + "columns": 1, + "fieldname": "item_quantity", + "fieldtype": "Int", + "in_list_view": 1, + "label": "Item Quantity" + }, + { + "columns": 1, + "fieldname": "unit_price", + "fieldtype": "Float", + "in_list_view": 1, + "label": "Unit Price", + "permlevel": 1 + }, + { + "columns": 1, + "fieldname": "amount_claimed", + "fieldtype": "Float", + "in_list_view": 1, + "label": "Amount Claimed", + "read_only": 1 + }, + { + "fieldname": "created_by", + "fieldtype": "Data", + "hidden": 1, + "label": "Old Created By", + "read_only": 1 + }, + { + "default": "None", + "fieldname": "item_crt_by", + "fieldtype": "Data", + "label": "Created By" + }, + { + "fieldname": "column_break_8", + "fieldtype": "Column Break", + "read_only": 1 + }, + { + "bold": 1, + "fieldname": "status", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Status" + }, + { + "fieldname": "patient_encounter", + "fieldtype": "Small Text", + "in_list_view": 1, + "label": "Patient Encounter", + "read_only": 1 + }, + { + "fieldname": "ref_docname", + "fieldtype": "Small Text", + "label": "Ref DocName", + "read_only": 1 + }, + { + "fieldname": "approval_ref_no", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Approval Ref No", + "permlevel": 1 + }, + { + "fieldname": "folio_item_id", + "fieldtype": "Data", + "label": "Folio Item ID", + "read_only": 1 + }, + { + "fieldname": "folio_id", + "fieldtype": "Data", + "label": "Folio ID", + "read_only": 1 + }, + { + "fieldname": "date_created", + "fieldtype": "Date", + "label": "Date Created", + "read_only": 1 + } + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2024-07-26 13:16:36.223382", + "modified_by": "Administrator", + "module": "Jubilee", + "name": "Jubilee Patient Claim Item", + "owner": "Administrator", + "permissions": [], + "quick_entry": 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_patient_claim_item/jubilee_patient_claim_item.py b/hms_tz/jubilee/doctype/jubilee_patient_claim_item/jubilee_patient_claim_item.py new file mode 100644 index 00000000..07abd252 --- /dev/null +++ b/hms_tz/jubilee/doctype/jubilee_patient_claim_item/jubilee_patient_claim_item.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 JubileePatientClaimItem(Document): + pass diff --git a/hms_tz/jubilee/doctype/jubilee_price_package/__init__.py b/hms_tz/jubilee/doctype/jubilee_price_package/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/hms_tz/jubilee/doctype/jubilee_price_package/jubilee_price_package.js b/hms_tz/jubilee/doctype/jubilee_price_package/jubilee_price_package.js new file mode 100644 index 00000000..e66aa74b --- /dev/null +++ b/hms_tz/jubilee/doctype/jubilee_price_package/jubilee_price_package.js @@ -0,0 +1,8 @@ +// Copyright (c) 2024, Aakvatech and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Jubilee Price Package', { + // refresh: function(frm) { + + // } +}); diff --git a/hms_tz/jubilee/doctype/jubilee_price_package/jubilee_price_package.json b/hms_tz/jubilee/doctype/jubilee_price_package/jubilee_price_package.json new file mode 100644 index 00000000..8b6a8751 --- /dev/null +++ b/hms_tz/jubilee/doctype/jubilee_price_package/jubilee_price_package.json @@ -0,0 +1,135 @@ +{ + "actions": [], + "autoname": "JPP-.YY.-.MM.-.DD.-.#####", + "creation": "2024-06-14 16:02:14.924451", + "default_view": "List", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "providerid", + "log_name", + "column_break_4", + "company", + "time_stamp", + "section_break_3", + "itemcode", + "itemprice", + "strength", + "dosage", + "column_break_12", + "itemname", + "cleanname" + ], + "fields": [ + { + "fieldname": "log_name", + "fieldtype": "Data", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Log Name", + "read_only": 1 + }, + { + "fieldname": "time_stamp", + "fieldtype": "Datetime", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Time Stamp", + "read_only": 1 + }, + { + "fieldname": "column_break_4", + "fieldtype": "Column Break" + }, + { + "fieldname": "company", + "fieldtype": "Data", + "in_standard_filter": 1, + "label": "Company", + "read_only": 1 + }, + { + "fieldname": "section_break_3", + "fieldtype": "Section Break" + }, + { + "fieldname": "itemcode", + "fieldtype": "Data", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "ItemCode", + "read_only": 1 + }, + { + "fieldname": "itemname", + "fieldtype": "Small Text", + "in_standard_filter": 1, + "label": "ItemName", + "read_only": 1 + }, + { + "fieldname": "strength", + "fieldtype": "Data", + "label": "Strength", + "read_only": 1 + }, + { + "fieldname": "dosage", + "fieldtype": "Data", + "label": "Dosage", + "read_only": 1 + }, + { + "fieldname": "column_break_12", + "fieldtype": "Column Break" + }, + { + "fieldname": "providerid", + "fieldtype": "Data", + "label": "ProviderID", + "read_only": 1 + }, + { + "fieldname": "itemprice", + "fieldtype": "Float", + "in_list_view": 1, + "label": "ItemPrice", + "read_only": 1 + }, + { + "fieldname": "cleanname", + "fieldtype": "Small Text", + "in_standard_filter": 1, + "label": "CleanName", + "read_only": 1 + } + ], + "index_web_pages_for_search": 1, + "links": [], + "modified": "2024-06-18 18:11:06.535715", + "modified_by": "Administrator", + "module": "Jubilee", + "name": "Jubilee Price Package", + "naming_rule": "Expression (old style)", + "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": [], + "title_field": "itemname", + "track_changes": 1 +} \ No newline at end of file diff --git a/hms_tz/jubilee/doctype/jubilee_price_package/jubilee_price_package.py b/hms_tz/jubilee/doctype/jubilee_price_package/jubilee_price_package.py new file mode 100644 index 00000000..3477fb26 --- /dev/null +++ b/hms_tz/jubilee/doctype/jubilee_price_package/jubilee_price_package.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 JubileePricePackage(Document): + pass diff --git a/hms_tz/jubilee/doctype/jubilee_price_package/test_jubilee_price_package.py b/hms_tz/jubilee/doctype/jubilee_price_package/test_jubilee_price_package.py new file mode 100644 index 00000000..c33e5b68 --- /dev/null +++ b/hms_tz/jubilee/doctype/jubilee_price_package/test_jubilee_price_package.py @@ -0,0 +1,9 @@ +# Copyright (c) 2024, Aakvatech and Contributors +# See license.txt + +# import frappe +from frappe.tests.utils import FrappeTestCase + + +class TestJubileePricePackage(FrappeTestCase): + pass 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..e2ee7cb2 --- /dev/null +++ b/hms_tz/jubilee/doctype/jubilee_response_log/jubilee_response_log.json @@ -0,0 +1,154 @@ +{ + "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", + "company", + "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 + }, + { + "fieldname": "company", + "fieldtype": "Data", + "hidden": 1, + "label": "Company", + "read_only": 1 + } + ], + "index_web_pages_for_search": 1, + "links": [], + "modified": "2024-06-18 09:28:58.023706", + "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..2322af6e --- /dev/null +++ b/hms_tz/jubilee/doctype/jubilee_response_log/jubilee_response_log.py @@ -0,0 +1,36 @@ +# 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, + company=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.company = company + 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 diff --git a/hms_tz/jubilee/doctype/jubilee_update/__init__.py b/hms_tz/jubilee/doctype/jubilee_update/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/hms_tz/jubilee/doctype/jubilee_update/jubilee_update.js b/hms_tz/jubilee/doctype/jubilee_update/jubilee_update.js new file mode 100644 index 00000000..42ca9555 --- /dev/null +++ b/hms_tz/jubilee/doctype/jubilee_update/jubilee_update.js @@ -0,0 +1,8 @@ +// Copyright (c) 2024, Aakvatech and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Jubilee Update', { + // refresh: function(frm) { + + // } +}); diff --git a/hms_tz/jubilee/doctype/jubilee_update/jubilee_update.json b/hms_tz/jubilee/doctype/jubilee_update/jubilee_update.json new file mode 100644 index 00000000..cc96ddd9 --- /dev/null +++ b/hms_tz/jubilee/doctype/jubilee_update/jubilee_update.json @@ -0,0 +1,92 @@ +{ + "actions": [], + "autoname": "JU-.YY.-.#########", + "creation": "2024-06-18 09:47:39.160869", + "default_view": "List", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "company", + "datetime", + "column_break_lln7e", + "current_log", + "previous_log", + "jubilee_price_packages_section", + "price_package" + ], + "fields": [ + { + "fieldname": "company", + "fieldtype": "Data", + "label": "company", + "read_only": 1 + }, + { + "default": "Now", + "fieldname": "datetime", + "fieldtype": "Datetime", + "label": "DateTime", + "read_only": 1 + }, + { + "fieldname": "column_break_lln7e", + "fieldtype": "Column Break" + }, + { + "fieldname": "current_log", + "fieldtype": "Link", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Current Log", + "options": "Jubilee Response Log", + "read_only": 1, + "reqd": 1 + }, + { + "fieldname": "previous_log", + "fieldtype": "Link", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Previous Log", + "options": "Jubilee Response Log", + "read_only": 1, + "reqd": 1 + }, + { + "fieldname": "jubilee_price_packages_section", + "fieldtype": "Section Break", + "label": "Jubilee Price Packages" + }, + { + "fieldname": "price_package", + "fieldtype": "Table", + "label": "Price Packages", + "options": "Jubilee Update Item", + "read_only": 1 + } + ], + "index_web_pages_for_search": 1, + "links": [], + "modified": "2024-06-18 09:47:39.160869", + "modified_by": "Administrator", + "module": "Jubilee", + "name": "Jubilee Update", + "naming_rule": "Expression (old style)", + "owner": "Administrator", + "permissions": [ + { + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 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_update/jubilee_update.py b/hms_tz/jubilee/doctype/jubilee_update/jubilee_update.py new file mode 100644 index 00000000..57f89c88 --- /dev/null +++ b/hms_tz/jubilee/doctype/jubilee_update/jubilee_update.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 JubileeUpdate(Document): + pass diff --git a/hms_tz/jubilee/doctype/jubilee_update/test_jubilee_update.py b/hms_tz/jubilee/doctype/jubilee_update/test_jubilee_update.py new file mode 100644 index 00000000..08fa8b15 --- /dev/null +++ b/hms_tz/jubilee/doctype/jubilee_update/test_jubilee_update.py @@ -0,0 +1,9 @@ +# Copyright (c) 2024, Aakvatech and Contributors +# See license.txt + +# import frappe +from frappe.tests.utils import FrappeTestCase + + +class TestJubileeUpdate(FrappeTestCase): + pass diff --git a/hms_tz/jubilee/doctype/jubilee_update_item/__init__.py b/hms_tz/jubilee/doctype/jubilee_update_item/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/hms_tz/jubilee/doctype/jubilee_update_item/jubilee_update_item.json b/hms_tz/jubilee/doctype/jubilee_update_item/jubilee_update_item.json new file mode 100644 index 00000000..cb535334 --- /dev/null +++ b/hms_tz/jubilee/doctype/jubilee_update_item/jubilee_update_item.json @@ -0,0 +1,91 @@ +{ + "actions": [], + "creation": "2024-06-18 09:41:52.551063", + "default_view": "List", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "type", + "section_break_3", + "itemcode", + "itemprice", + "strength", + "dosage", + "column_break_12", + "itemname", + "cleanname", + "record" + ], + "fields": [ + { + "fieldname": "type", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Change Type" + }, + { + "fieldname": "section_break_3", + "fieldtype": "Section Break" + }, + { + "fieldname": "itemcode", + "fieldtype": "Data", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "ItemCode" + }, + { + "fieldname": "itemname", + "fieldtype": "Small Text", + "in_standard_filter": 1, + "label": "ItemName" + }, + { + "fieldname": "strength", + "fieldtype": "Data", + "label": "Strength" + }, + { + "fieldname": "dosage", + "fieldtype": "Data", + "label": "Dosage" + }, + { + "fieldname": "column_break_12", + "fieldtype": "Column Break" + }, + { + "fieldname": "record", + "fieldtype": "Small Text", + "label": "Record" + }, + { + "fieldname": "itemprice", + "fieldtype": "Float", + "in_list_view": 1, + "label": "ItemPrice", + "read_only": 1 + }, + { + "fieldname": "cleanname", + "fieldtype": "Small Text", + "in_list_view": 1, + "label": "CleanName", + "read_only": 1 + } + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2024-06-18 18:56:42.770935", + "modified_by": "Administrator", + "module": "Jubilee", + "name": "Jubilee Update Item", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "title_field": "itemname" +} \ No newline at end of file diff --git a/hms_tz/jubilee/doctype/jubilee_update_item/jubilee_update_item.py b/hms_tz/jubilee/doctype/jubilee_update_item/jubilee_update_item.py new file mode 100644 index 00000000..76b75f40 --- /dev/null +++ b/hms_tz/jubilee/doctype/jubilee_update_item/jubilee_update_item.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 JubileeUpdateItem(Document): + pass diff --git a/hms_tz/jubilee/doctype/original_jubilee_patient_claim_item/__init__.py b/hms_tz/jubilee/doctype/original_jubilee_patient_claim_item/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/hms_tz/jubilee/doctype/original_jubilee_patient_claim_item/original_jubilee_patient_claim_item.json b/hms_tz/jubilee/doctype/original_jubilee_patient_claim_item/original_jubilee_patient_claim_item.json new file mode 100644 index 00000000..82a51c5a --- /dev/null +++ b/hms_tz/jubilee/doctype/original_jubilee_patient_claim_item/original_jubilee_patient_claim_item.json @@ -0,0 +1,174 @@ +{ + "actions": [], + "creation": "2024-07-26 13:05:14.116544", + "default_view": "List", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "ref_doctype", + "item_name", + "item_code", + "item_quantity", + "unit_price", + "amount_claimed", + "created_by", + "item_crt_by", + "column_break_8", + "status", + "patient_encounter", + "ref_docname", + "approval_ref_no", + "folio_item_id", + "folio_id", + "date_created", + "section_break_15", + "reference_doctype", + "reference_docname" + ], + "fields": [ + { + "fieldname": "ref_doctype", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Ref DocType", + "permlevel": 1, + "reqd": 1 + }, + { + "fieldname": "item_name", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Item Name", + "read_only": 1 + }, + { + "columns": 1, + "fieldname": "item_code", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Item Code", + "read_only": 1 + }, + { + "columns": 1, + "fieldname": "item_quantity", + "fieldtype": "Int", + "in_list_view": 1, + "label": "Item Quantity" + }, + { + "columns": 1, + "fieldname": "unit_price", + "fieldtype": "Float", + "in_list_view": 1, + "label": "Unit Price", + "permlevel": 1 + }, + { + "columns": 1, + "fieldname": "amount_claimed", + "fieldtype": "Float", + "in_list_view": 1, + "label": "Amount Claimed", + "read_only": 1 + }, + { + "fieldname": "created_by", + "fieldtype": "Data", + "hidden": 1, + "label": "Old Created By", + "read_only": 1 + }, + { + "default": "None", + "fieldname": "item_crt_by", + "fieldtype": "Data", + "label": "Created By" + }, + { + "fieldname": "column_break_8", + "fieldtype": "Column Break", + "read_only": 1 + }, + { + "bold": 1, + "fieldname": "status", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Status" + }, + { + "fieldname": "patient_encounter", + "fieldtype": "Small Text", + "in_list_view": 1, + "label": "Patient Encounter", + "read_only": 1 + }, + { + "fieldname": "ref_docname", + "fieldtype": "Small Text", + "label": "Ref DocName", + "read_only": 1 + }, + { + "fieldname": "approval_ref_no", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Approval Ref No", + "permlevel": 1 + }, + { + "fieldname": "folio_item_id", + "fieldtype": "Data", + "label": "Folio Item ID", + "read_only": 1 + }, + { + "fieldname": "folio_id", + "fieldtype": "Data", + "label": "Folio ID", + "read_only": 1 + }, + { + "fieldname": "date_created", + "fieldtype": "Date", + "label": "Date Created", + "read_only": 1 + }, + { + "fieldname": "section_break_15", + "fieldtype": "Section Break" + }, + { + "allow_on_submit": 1, + "fieldname": "reference_doctype", + "fieldtype": "Link", + "label": "Reference DocType", + "options": "DocType", + "permlevel": 2 + }, + { + "allow_on_submit": 1, + "fieldname": "reference_docname", + "fieldtype": "Dynamic Link", + "label": "Reference DocName", + "options": "reference_doctype", + "permlevel": 2 + } + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2024-07-26 13:13:11.956151", + "modified_by": "Administrator", + "module": "Jubilee", + "name": "Original Jubilee Patient Claim Item", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "track_changes": 1 +} \ No newline at end of file diff --git a/hms_tz/jubilee/doctype/original_jubilee_patient_claim_item/original_jubilee_patient_claim_item.py b/hms_tz/jubilee/doctype/original_jubilee_patient_claim_item/original_jubilee_patient_claim_item.py new file mode 100644 index 00000000..0457e44a --- /dev/null +++ b/hms_tz/jubilee/doctype/original_jubilee_patient_claim_item/original_jubilee_patient_claim_item.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 OriginalJubileePatientClaimItem(Document): + pass diff --git a/hms_tz/jubilee/print_format/__init__.py b/hms_tz/jubilee/print_format/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/hms_tz/jubilee/print_format/jubilee_form_2a_&_b/__init__.py b/hms_tz/jubilee/print_format/jubilee_form_2a_&_b/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/hms_tz/jubilee/print_format/jubilee_form_2a_&_b/jubilee_form_2a_&_b.json b/hms_tz/jubilee/print_format/jubilee_form_2a_&_b/jubilee_form_2a_&_b.json new file mode 100644 index 00000000..1393d5df --- /dev/null +++ b/hms_tz/jubilee/print_format/jubilee_form_2a_&_b/jubilee_form_2a_&_b.json @@ -0,0 +1,34 @@ +{ + "absolute_value": 0, + "align_labels_right": 0, + "creation": "2024-07-28 15:18:31.358673", + "css": "hr {\r\n border: 0;\r\n clear:both;\r\n display:block;\r\n width: 96%; \r\n background-color:#cdcdca;\r\n height: 1px;\r\n}", + "custom_format": 1, + "default_print_language": "en", + "disabled": 0, + "doc_type": "Jubilee Patient Claim", + "docstatus": 0, + "doctype": "Print Format", + "font": "Default", + "font_size": 0, + "format_data": "[{\"fieldname\": \"print_heading_template\", \"fieldtype\": \"Custom HTML\", \"options\": \"\\n\\n \\n \\n \\n
\\n \\n \\n
\\n CONFIDENTIAL
\\n THE NHIF - HEALTH PROVIDER IN/OUT PATIENT CLAIM FORM\\n
\\n
\\n Form NHIF 2A & B
\\n Regulation 18(1)
\\n Serial No:
{{ doc.folio_no }}\\n
\"}, {\"fieldtype\": \"Section Break\", \"label\": \"A: PARTICULARS\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldname\": \"company\", \"print_hide\": 0, \"align\": \"left\", \"label\": \"Healthcare Facility\"}, {\"fieldname\": \"patient_name\", \"print_hide\": 0, \"align\": \"left\", \"label\": \"Name\"}, {\"fieldname\": \"date_of_birth\", \"print_hide\": 0, \"label\": \"Date Of Birth\"}, {\"fieldname\": \"gender\", \"print_hide\": 0, \"align\": \"left\", \"label\": \"Sex\"}, {\"fieldname\": \"_custom_html\", \"print_hide\": 0, \"label\": \"Custom HTML\", \"fieldtype\": \"HTML\", \"options\": \"
\\n
\\n \\n\\t
\\n\\t
\\n\\t
\\n
\"}, {\"fieldname\": \"_custom_html\", \"print_hide\": 0, \"label\": \"Custom HTML\", \"fieldtype\": \"HTML\", \"options\": \"
\\n
\\n \\n\\t
\\n\\t
\\n\\t\\t{{frappe.db.get_value(\\\"Patient\\\", doc.patient, \\\"area\\\")}}\\n\\t
\\n
\\n\"}, {\"fieldname\": \"_custom_html\", \"print_hide\": 0, \"label\": \"Custom HTML\", \"fieldtype\": \"HTML\", \"options\": \"
\\n
\\n \\n\\t
\\n\\t
\\n\\t
\\n
\\n\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldname\": \"_custom_html\", \"print_hide\": 0, \"label\": \"Custom HTML\", \"fieldtype\": \"HTML\", \"options\": \"
\\n
\\n \\n\\t
\\n\\t
\\n\\t\\tChusi Street, P.O.BOX 581 Ilala.\\n\\t
\\n
\"}, {\"fieldname\": \"cardno\", \"print_hide\": 0, \"label\": \"CardNo\"}, {\"fieldname\": \"attendance_date\", \"print_hide\": 0, \"align\": \"left\", \"label\": \"Date of Attendance\"}, {\"fieldname\": \"patient_type_code\", \"print_hide\": 0, \"align\": \"left\", \"label\": \"Patient Type Code\"}, {\"fieldname\": \"_custom_html\", \"print_hide\": 0, \"label\": \"Custom HTML\", \"fieldtype\": \"HTML\", \"options\": \"
\\n
\\n \\n\\t
\\n\\t
\\n\\t\\t{{frappe.db.get_value(\\\"Patient Appointment\\\", doc.patient_appointment, \\\"department\\\")}}\\n\\t
\\n
\"}, {\"fieldname\": \"patient_file_no\", \"print_hide\": 0, \"label\": \"Patient File No\"}, {\"fieldname\": \"authorization_no\", \"print_hide\": 0, \"label\": \"Authorization No\"}, {\"fieldtype\": \"Section Break\", \"label\": \"Diagnosis Code\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldname\": \"_custom_html\", \"print_hide\": 0, \"label\": \"Custom HTML\", \"fieldtype\": \"HTML\", \"options\": \"
\\n Provisional Diagnosis
\\n \\n \\n \\n \\n \\n \\n \\n {% for item in doc.nhif_patient_claim_disease %}\\n {% if item.status == \\\"Provisional\\\" %}\\n \\n \\n \\n \\n {% endif %}\\n {% endfor %}\\n
Disease CodeDescription
{{item.disease_code }}{{item.description }}
\\n
\\n\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldname\": \"_custom_html\", \"print_hide\": 0, \"label\": \"Custom HTML\", \"fieldtype\": \"HTML\", \"options\": \"
\\n Final Diagnosis
\\n \\n \\n \\n \\n \\n \\n \\n {% for item in doc.nhif_patient_claim_disease %}\\n {% if item.status == \\\"Final\\\" %}\\n \\n \\n \\n \\n {% endif %}\\n {% endfor %}\\n
Disease CodeDescription
{{item.disease_code }}{{item.description }}
\\n
\"}, {\"fieldtype\": \"Section Break\", \"label\": \"B: Details / Cost of service's\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldname\": \"_custom_html\", \"print_hide\": 0, \"label\": \"Custom HTML\", \"fieldtype\": \"HTML\", \"options\": \"\\n
\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n {% set total = [0] %}\\n {% for item in doc.nhif_patient_claim_item %}\\n {% set total = total.append(total.pop() + item.amount_claimed) %}\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n {# {%= frappe.utils.fmt_money(d.price_list_rate, currency = \\u2018USD\\u2019, precision = 2) %} #}\\n {% endfor %}\\n \\n \\n \\n \\n
SrServiceItem NameItem CodeQtyUnit PriceAmount
{{ loop.index }}{{ item.ref_doctype }}{{item.item_name }} {% if item.approval_ref_no %} {{ item.approval_ref_no }} {% endif %}{{item.item_code }}{{item.item_quantity }}{{ frappe.utils.fmt_money(item.unit_price, currency=\\\"TZS\\\", precision=2) }}{{ frappe.utils.fmt_money(item.amount_claimed, currency='TZS', precision = 2) }}
Total{{ frappe.utils.fmt_money(total[0], currency='TZS', precision = 2) }}
\\n
\"}, {\"fieldtype\": \"Section Break\", \"label\": \"C: Clinician Information\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldname\": \"_custom_html\", \"print_hide\": 0, \"label\": \"Custom HTML\", \"fieldtype\": \"HTML\", \"options\": \"
\\n
\\n \\n\\t
\\n\\t
\\n\\t\\t{{ frappe.db.get_value(\\\"Patient Appointment\\\", doc.patient_appointment, \\\"practitioner\\\") }}\\n\\t
\\n
\"}, {\"fieldname\": \"_custom_html\", \"print_hide\": 0, \"label\": \"Custom HTML\", \"fieldtype\": \"HTML\", \"options\": \"
\\n
\\n \\n\\t
\\n\\t
\\n\\t\\t{{ frappe.db.get_value(\\n \\\"Healthcare Practitioner\\\", \\n frappe.db.get_value(\\n \\\"Patient Appointment\\\", \\n doc.patient_appointment, \\n \\\"practitioner\\\"), \\n \\\"tz_mct_code\\\")\\n\\t\\t}}\\n\\t
\\n
\"}, {\"fieldname\": \"_custom_html\", \"print_hide\": 0, \"label\": \"Custom HTML\", \"fieldtype\": \"HTML\", \"options\": \"
\\n
\\n \\n\\t
\\n\\t
\\n\\t\\t{{ frappe.db.get_value(\\n \\\"NHIF Physician Qualification\\\",\\n frappe.db.get_value(\\n \\\"Healthcare Practitioner\\\", \\n frappe.db.get_value(\\n \\\"Patient Appointment\\\", \\n doc.patient_appointment, \\n \\\"practitioner\\\"), \\n \\\"nhif_physician_qualification\\\"),\\n \\\"qualification\\\")\\n\\t\\t}}\\n\\t
\\n
\"}, {\"fieldname\": \"_custom_html\", \"print_hide\": 0, \"label\": \"Custom HTML\", \"fieldtype\": \"HTML\", \"options\": \"
\\n
\\n \\n\\t
\\n\\t
\\n\\t\\t{{ frappe.db.get_value(\\n \\\"Healthcare Practitioner\\\", \\n frappe.db.get_value(\\n \\\"Patient Appointment\\\", \\n doc.patient_appointment, \\n \\\"practitioner\\\"), \\n \\\"mobile_phone\\\")\\n\\t\\t}}\\n\\t
\\n
\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldname\": \"_custom_html\", \"print_hide\": 0, \"label\": \"Custom HTML\", \"fieldtype\": \"HTML\", \"options\": \"{% set signature = frappe.db.get_value( \\\"Healthcare Practitioner\\\", frappe.db.get_value(\\\"Patient Appointment\\\",doc.patient_appointment, \\\"practitioner\\\"), \\\"doctors_signature\\\") %}\\r\\n{% if not signature %}\\r\\n {% set signature = frappe.db.get_value( \\\"Healthcare Practitioner\\\", \\\"Direct Cash\\\", \\\"doctors_signature\\\") %}\\r\\n{% endif %}\\r\\n
\\r\\n
\\r\\n \\r\\n
\\r\\n
\\r\\n\\t \\r\\n\\t
\\r\\n
\"}, {\"fieldtype\": \"Section Break\", \"label\": \"D: Patient Certification:\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldname\": \"_custom_html\", \"print_hide\": 0, \"label\": \"Custom HTML\", \"fieldtype\": \"HTML\", \"options\": \"I certify that I received the above named services.\"}, {\"fieldname\": \"patient_name\", \"print_hide\": 0, \"align\": \"left\", \"label\": \"Name\"}, {\"fieldname\": \"telephone_no\", \"print_hide\": 0, \"align\": \"left\", \"label\": \"Mobile No\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldname\": \"_custom_html\", \"print_hide\": 0, \"label\": \"Custom HTML\", \"fieldtype\": \"HTML\", \"options\": \"
\\r\\n
\\r\\n \\r\\n
\\r\\n
\\r\\n\\t \\r\\n\\t
\\r\\n
\"}, {\"fieldtype\": \"Section Break\", \"label\": \"E: Description of In/Out-patient Management / any other information\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldname\": \"attendance_date\", \"print_hide\": 0, \"align\": \"left\", \"label\": \"Date of Attendance\"}, {\"fieldname\": \"clinical_notes\", \"print_hide\": 0, \"label\": \"Clinical Notes\"}, {\"fieldtype\": \"Section Break\", \"label\": \"F. Claimant Certification:\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldname\": \"_custom_html\", \"print_hide\": 0, \"label\": \"Custom HTML\", \"fieldtype\": \"HTML\", \"options\": \"I certify that I provided the above named services\"}, {\"fieldname\": \"_custom_html\", \"print_hide\": 0, \"label\": \"Custom HTML\", \"fieldtype\": \"HTML\", \"options\": \"
\\n
\\n \\n\\t
\\n\\t
\\n\\t\\t{{ frappe.db.get_value(\\\"Healthcare Practitioner\\\",\\n\\t\\t {\\\"user_id\\\": doc.modified_by}, \\\"name\\\") }}\\n\\t
\\n
\"}, {\"fieldname\": \"_custom_html\", \"print_hide\": 0, \"label\": \"Custom HTML\", \"fieldtype\": \"HTML\", \"options\": \"\\n\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldname\": \"_custom_html\", \"print_hide\": 0, \"label\": \"Custom HTML\", \"fieldtype\": \"HTML\", \"options\": \"{% set signature = frappe.db.get_value(\\\"Healthcare Practitioner\\\", {\\\"user_id\\\": doc.modified_by}, \\\"doctors_signature\\\") %}\\n{% if not signature %}\\n {% set signature = frappe.db.get_value( \\\"Healthcare Practitioner\\\", \\\"Direct Cash\\\", \\\"doctors_signature\\\") %}\\n{% endif %}\\n
\\n
\\n \\n
\\n
\\n\\t \\n\\t
\\n
\\n\"}, {\"fieldtype\": \"Section Break\", \"label\": \"NOTE\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldname\": \"_custom_html\", \"print_hide\": 0, \"label\": \"Custom HTML\", \"fieldtype\": \"HTML\", \"options\": \"

\\n Patient should sign the form after completion of service
\\n Before referring patient to other facility, referring facility should be satisfied for the missing item and its alternative within\\nthe facility.
\\nAny falsified information may subject you to prosecution in accordance with NHIF Act Cap 395 & No. 8 of 1999.\\n

\"}]", + "html": "\n\n \n \n \n \n
\n \n \n
\n CONFIDENTIAL
\n THE JUBILEE - HEALTH PROVIDER IN/OUT PATIENT CLAIM FORM\n
\n
\n Appendix X
\n Form JUBILEE 2A & B
\n Regulation 18(1)
\n AuthorizationNo {{ doc.authorization_no }}
\n Serial No:
{{ doc.folio_no }}\n
\n
\n\n \n \n \n \n \n \n \n \n
\n A: PARTICULARS\n
\n
\n
\n
\n \n
\n
\n {{ doc.company }} \n
\n
\n
\n
\n \n
\n
\n {{doc.patient_name }}\n
\n
\n
\n
\n \n
\n
\n {{ doc.date_of_birth }}\n
\n
\n
\n
\n \n
\n
\n {{ doc.gender }} \n
\n
\n
\n
\n \n
\n
\n \n
\n
\n
\n
\n \n
\n
\n {### {{frappe.db.get_value(\"Patient\", doc.patient, \"area\")}} ####}\n
\n
\n
\n
\n \n
\n
\n \n
\n
\n
\n
\n \n
\n
\n
\n \n
\n
\n \n {% set add_details = frappe.db.get_value(\"Company\", doc.company, [\"p_o_box\", \"street\"]) %}\n {{ add_details[0] }} {{add_details[1]}}\n
\n
\n
\n
\n \n
\n
\n {{ doc.cardno }}\n
\n
\n
\n
\n \n
\n
\n {{doc.attendance_date}}\n
\n
\n
\n
\n \n
\n
\n {{ doc.patient_type_code }}\n
\n
\n
\n
\n \n
\n
\n {{ frappe.db.get_value(\"Healthcare Practitioner\", {\"practitioner_name\": doc.practitioner_name}, \"department\") }}\n
\n
\n
\n
\n \n
\n
\n {{ doc.patient }}\n
\n
\n
\n
\n \n
\n
\n {{ doc.authorization_no }}\n
\n
\n
\n
\n
\n\n

\n Diagnosis Code\n

\n\n \n \n \n \n \n
\n Provisional Diagnosis\n \n \n \n \n \n \n \n {% for item in doc.jubilee_patient_claim_disease %}\n {% if item.status == \"Provisional\" %}\n \n \n \n \n {% endif %}\n {% endfor %}\n
Disease CodeDescription
{{item.disease_code }}{{item.description }}
\n
\n Final Diagnosis\n \n \n \n \n \n \n \n {% for item in doc.jubilee_patient_claim_disease %}\n {% if item.status == \"Final\" %}\n \n \n \n \n {% endif %}\n {% endfor %}\n
Disease CodeDescription
{{item.disease_code }}{{item.description }}
\n
\n
\n\n

\n B: Details / Cost of services\n

\n\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n {% set total = [0] %}\n {% for item in doc.jubilee_patient_claim_item %}\n {% set total = total.append(total.pop() + item.amount_claimed) %}\n \n \n \n \n \n \n \n \n \n \n {% endfor %}\n \n \n \n \n \n \n
SrRef DocTypeItem NameItem CodeItem QuantityUnit PriceAmount
{{ loop.index }}{{ item.ref_doctype | replace(\" Prescription\",\":\") }}\n {% if item.ref_doctype == \"Drug Prescription\" %}\n {% set drug = frappe.db.get_all(\"Drug Prescription\", \n {\"parent\": item.patient_encounter, \"name\": item.ref_docname, \"drug_code\":item.item_name, \"drug_name\": item.item_name},\n [\"dosage\", \"period\", \"dosage_form\"]) %}\n \n {{item.item_name}},\n {% if drug %}\n {% if drug[0][\"dosage\"] %} Dosage: {{ drug[0][\"dosage\"] }}, {% endif %}\n {% if drug[0][\"period\"] %} Period: {{ drug[0][\"period\"] }}, {% endif %}\n {% if drug[0][\"dosage_form\"] %} Form: {{ drug[0][\"dosage_form\"] }} {% endif %}\n {% endif %}\n\n {% else %}\n {{item.item_name}}\n \n {% endif %}\n {% if item.approval_ref_no %} {{ item.approval_ref_no }} {% endif %}\n {{item.item_code }}{{item.item_quantity }}{{ frappe.utils.fmt_money(item.unit_price, currency=\"TZS\", precision=2) }}{{ frappe.utils.fmt_money(item.amount_claimed, currency='TZS', precision = 2) }}
Total {{ frappe.utils.fmt_money(total[0], currency=\"TZS\", precision = 2) }}
\n
\n\n

\n C: Clinician Information\n

\n\n \n \n \n \n
\n
\n
\n \n \t
\n \t
\n \t\t{{ doc.practitioner_name }}\n \t
\n
\n \n
\n
\n \n \t
\n \t
\n \t\t{{ frappe.db.get_value(\n \"Healthcare Practitioner\", \n {\"practitioner_name\": doc.practitioner_name},\n \"tz_mct_code\")\n \t\t}}\n \t
\n
\n \n
\n
\n \n \t
\n \t
\n \t\t{{ frappe.db.get_value(\n \"NHIF Physician Qualification\",\n frappe.db.get_value(\n \"Healthcare Practitioner\", \n {\"practitioner_name\": doc.practitioner_name}, \n \"nhif_physician_qualification\"),\n \"qualification\")\n \t\t}}\n \t
\n
\n \n
\n
\n \n \t
\n \t
\n \t\t{{ frappe.db.get_value(\n \"Healthcare Practitioner\", \n {\"practitioner_name\": doc.practitioner_name}, \n \"mobile_phone\")\n \t\t}}\n \t
\n
\n
\n {% set signature = frappe.db.get_value( \n \"Healthcare Practitioner\", \n {\"practitioner_name\": doc.practitioner_name}, \n \"doctors_signature\") \n %}\n {% if not signature %}\n {% set signature = frappe.db.get_value( \"Healthcare Practitioner\", \"Direct Cash\", \"doctors_signature\") %}\n {% endif %}\n
\n
\n
\n \n
\n
\n \t \n \t
\n \t
\n
\n
\n
\n\n

\n D: Patient Certification:\n

\n\n \n \n \n \n
\n I certify that I received the above named services.\n \n
\n
\n \n \t
\n \t
\n \t\t{{ doc.patient_name\t}}\n \t
\n
\n
\n
\n \n
\n
\n \t {{doc.telephone_no }}\n \t
\n
\n
\n
\n
\n \n \t
\n \t
\n \t\t\n \t
\n
\n
\n
\n\n

\n E: Description of In/Out-patient Management / any other information\n

\n

\n Date of Attendance: {{doc.attendance_date}}\n

\n\n

\n Clinical Notes\n

\n

\n {{ doc.clinical_notes }}\n

\n
\n\n

\n F: Claimant Certification:\n

\n\n \n \n \n \n
\n I certify that I provided the above named services\n \n
\n
\n \n \t
\n \t
\n \t {% set practitioner = frappe.db.get_value( \"Healthcare Practitioner\", {\"user_id\": doc.modified_by}, \"name\") %}\n \t {% if not practitioner %}\n \t {% set practitioner = frappe.db.get_value( \"Healthcare Practitioner\", {\"user_id\": frappe.session.user}, \"name\") %}\n \t {% endif %}\n \t {{ practitioner }}\n \t
\n
\n \n {% if doc.company == \"Shree Hindu Mandal Hospital - Dar es Salaam\" %}\n \n {% elif doc.company == \"Shree Hindu Mandal Super Specialized Polyclinic - Kunduchi\" %}\n \n {% endif %}\n
\n
\n
\n
\n \n
\n
\n {% set signature = frappe.db.get_value(\"Healthcare Practitioner\", {\"user_id\": doc.modified_by}, \"doctors_signature\") %}\n {% if not signature %}\n {% set signature = frappe.db.get_value( \"Healthcare Practitioner\", {\"user_id\": frappe.session.user}, \"doctors_signature\") %}\n {% endif %}\n {% if not signature %}\n {% set signature = frappe.db.get_value( \"Healthcare Practitioner\", \"Direct Cash\", \"doctors_signature\") %}\n {% endif %}\n \n \t \n \t
\n
\n
\n
\n\n

\n NOTE:\n

\n

\n Patient should sign the form after completion of service
\n Before referring patient to other facility, referring facility should be satisfied for the missing item and its alternative within\nthe facility.
\nAny falsified information may subject you to prosecution in accordance with Jubilee Act No. 8 of 1999.\n

\n\n\n", + "idx": 0, + "line_breaks": 0, + "margin_bottom": 0.0, + "margin_left": 0.0, + "margin_right": 0.0, + "margin_top": 0.0, + "modified": "2024-07-28 15:23:32.864131", + "modified_by": "Administrator", + "module": "Jubilee", + "name": "Jubilee Form 2A & B", + "owner": "Administrator", + "page_number": "Hide", + "print_format_builder": 1, + "print_format_builder_beta": 0, + "print_format_type": "Jinja", + "raw_printing": 0, + "show_section_headings": 0, + "standard": "Yes" +} \ No newline at end of file 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 diff --git a/hms_tz/nhif/api/healthcare_utils.py b/hms_tz/nhif/api/healthcare_utils.py index b731365b..ec59cb88 100644 --- a/hms_tz/nhif/api/healthcare_utils.py +++ b/hms_tz/nhif/api/healthcare_utils.py @@ -1370,14 +1370,14 @@ def create_invoiced_items_if_not_created(): def auto_submit_nhif_patient_claim(setting_dict=None): """Routine to submit patient claims and will be triggered: 1. Every 00:01 am at night by cron job - 2. By a button called 'Auto Submit Patient Claim' which is on Company NHIF settings + 2. By a button called 'Auto Submit Patient Claim' which is on Company Insurance setting """ company_setting_detail = [] if not setting_dict: company_setting_detail = frappe.get_all( - "Company NHIF Settings", - filters={"enable": 1, "enable_auto_submit_of_claims": 1}, + "Company Insurance Setting", + filters={"insurance_provider": "NHIF", "enable": 1, "enable_auto_submit_of_claims": 1}, fields=["company", "submit_claim_year", "submit_claim_month"], ) else: @@ -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, + "Company Insurance Setting", + {"company": company, "insurance_provider": "NHIF"}, [ "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,11 +1523,11 @@ 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}" ) - token = get_nhifservice_token(company) + token = get_nhifservice_token(company, "NHIF") headers = {"Content-Type": "application/json", "Authorization": "Bearer " + token} diff --git a/hms_tz/nhif/api/insurance_company.js b/hms_tz/nhif/api/insurance_company.js index fb05c3bf..619be49b 100644 --- a/hms_tz/nhif/api/insurance_company.js +++ b/hms_tz/nhif/api/insurance_company.js @@ -1,15 +1,29 @@ frappe.ui.form.on('Healthcare Insurance Company', { onload: function (frm) { - add_get_price_btn(frm) + // For NHIF + add_nhif_get_price_btn(frm); + + // For Jubilee + add_jubilee_get_price_btn(frm); + }, refresh: function (frm) { - add_get_price_btn(frm) + // For NHIF + add_nhif_get_price_btn(frm); + + // For Jubilee + add_jubilee_get_price_btn(frm); }, }); -var add_get_price_btn = function (frm) { +var add_nhif_get_price_btn = function (frm) { if (!frm.doc.insurance_company_name.includes("NHIF")) { return } frm.add_custom_button(__('Get NHIF Price Package'), function () { + frappe.show_alert({ + message: __("Fetching NHIF Price packages."), + indicator: 'green' + }, 10); + frappe.call({ method: 'hms_tz.nhif.api.insurance_company.enqueue_get_nhif_price_package', args: { company: frm.doc.company }, @@ -20,8 +34,12 @@ var add_get_price_btn = function (frm) { } }); }); - if (!frm.doc.insurance_company_name.includes("NHIF")) { return } frm.add_custom_button(__('Only Process NHIF Records'), function () { + frappe.show_alert({ + message: __("Processing NHIF Price packages."), + indicator: 'green' + }, 10); + frappe.call({ method: 'hms_tz.nhif.api.insurance_company.process_nhif_records', args: { company: frm.doc.company }, @@ -33,4 +51,42 @@ var add_get_price_btn = function (frm) { }); }); +} + +var add_jubilee_get_price_btn = function (frm) { + if (!frm.doc.insurance_company_name.includes("Jubilee")) { return } + + frm.add_custom_button(__('Get Jubilee Price Package'), function () { + frappe.show_alert({ + message: __("Fetching Jubilee Price packages."), + indicator: 'green' + }, 10); + + frappe.call({ + method: 'hms_tz.jubilee.api.api.enqueue_get_jubilee_price_packages', + args: { company: frm.doc.company }, + callback: function (data) { + // if (data.message) { + // console.log(data.message) + // } + } + }); + }); + frm.add_custom_button(__('Only Process Jubilee Records'), function () { + frappe.show_alert({ + message: __("Processing Jubilee Price packages."), + indicator: 'green' + }, 10); + + frappe.call({ + method: 'hms_tz.jubilee.api.price_package.process_jubilee_records', + args: { company: frm.doc.company }, + callback: function (data) { + // if (data.message) { + // console.log(data.message) + // } + } + }); + }); + } \ No newline at end of file diff --git a/hms_tz/nhif/api/insurance_company.py b/hms_tz/nhif/api/insurance_company.py index b8912d1f..a30f69a4 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( @@ -36,9 +37,11 @@ def enqueue_get_nhif_price_package(company): def get_nhif_price_package(kwargs): company = kwargs user = frappe.session.user - token = get_claimsservice_token(company) + token = get_claimsservice_token(company, "NHIF") claimsserver_url, facility_code = frappe.get_cached_value( - "Company NHIF Settings", company, ["claimsserver_url", "facility_code"] + "Company Insurance Setting", + {"company": company, "insurance_provider": "NHIF"}, + ["claimsserver_url", "facility_code"], ) headers = {"Authorization": "Bearer " + token} url = ( @@ -53,13 +56,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 +81,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 +195,11 @@ 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": company, "insurance_provider": "NHIF"}, + "facility_code", + ) currency = frappe.get_cached_value("Company", company, "default_currency") schemeid_list = frappe.db.sql( """ @@ -257,13 +266,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 +285,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 +370,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 +496,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/insurance_subscription.py b/hms_tz/nhif/api/insurance_subscription.py index 7ef5a360..da067367 100644 --- a/hms_tz/nhif/api/insurance_subscription.py +++ b/hms_tz/nhif/api/insurance_subscription.py @@ -5,6 +5,7 @@ from __future__ import unicode_literals import frappe from frappe import _ +from time import sleep # from frappe import _ from hms_tz.nhif.api.patient import get_patient_info @@ -41,6 +42,8 @@ def set_insurance_card_detail_in_patient(doc): card_count += 1 str_coverage_plan_card_number += card.coverage_plan_card_number + ", " + # wait for 30 seconds to avoid an Error of 'Document is already modified' + sleep(30) frappe.db.set_value( "Patient", doc.patient, diff --git a/hms_tz/nhif/api/patient.js b/hms_tz/nhif/api/patient.js index 490dff70..311d5519 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') }); - $(`