@ampath/esm-dha-workflow-app 4.0.0-next.35 → 4.0.0-next.355
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +19 -0
- package/dist/1149.js +1 -0
- package/dist/1149.js.map +1 -0
- package/dist/1171.js +1 -0
- package/dist/1171.js.map +1 -0
- package/dist/1197.js +1 -0
- package/dist/1239.js +1 -0
- package/dist/1239.js.map +1 -0
- package/dist/1277.js +1 -0
- package/dist/1277.js.map +1 -0
- package/dist/1363.js +1 -0
- package/dist/1363.js.map +1 -0
- package/dist/1551.js +1 -0
- package/dist/1551.js.map +1 -0
- package/dist/1556.js +1 -0
- package/dist/1556.js.map +1 -0
- package/dist/1560.js +1 -0
- package/dist/1560.js.map +1 -0
- package/dist/1925.js +1 -0
- package/dist/1925.js.map +1 -0
- package/dist/1927.js +1 -0
- package/dist/1927.js.map +1 -0
- package/dist/2054.js +1 -0
- package/dist/2054.js.map +1 -0
- package/dist/2093.js +1 -0
- package/dist/2093.js.map +1 -0
- package/dist/2177.js +2 -0
- package/dist/2177.js.map +1 -0
- package/dist/2230.js +1 -0
- package/dist/2230.js.map +1 -0
- package/dist/2493.js +1 -0
- package/dist/2493.js.map +1 -0
- package/dist/3041.js +1 -0
- package/dist/3041.js.map +1 -0
- package/dist/3099.js +1 -0
- package/dist/3101.js +1 -0
- package/dist/3101.js.map +1 -0
- package/dist/313.js +1 -0
- package/dist/313.js.map +1 -0
- package/dist/3184.js +2 -0
- package/dist/3184.js.map +1 -0
- package/dist/3202.js +1 -0
- package/dist/3202.js.map +1 -0
- package/dist/3222.js +1 -0
- package/dist/3222.js.map +1 -0
- package/dist/327.js +1 -0
- package/dist/327.js.map +1 -0
- package/dist/347.js +1 -0
- package/dist/347.js.map +1 -0
- package/dist/3595.js +1 -0
- package/dist/3595.js.map +1 -0
- package/dist/3928.js +1 -0
- package/dist/3928.js.map +1 -0
- package/dist/3939.js +1 -0
- package/dist/3939.js.map +1 -0
- package/dist/4003.js +1 -0
- package/dist/4003.js.map +1 -0
- package/dist/4055.js +1 -0
- package/dist/4205.js +1 -0
- package/dist/4205.js.map +1 -0
- package/dist/4225.js +1 -0
- package/dist/4225.js.map +1 -0
- package/dist/4231.js +1 -0
- package/dist/4231.js.map +1 -0
- package/dist/4300.js +1 -0
- package/dist/4335.js +1 -0
- package/dist/4353.js +1 -0
- package/dist/4353.js.map +1 -0
- package/dist/4431.js +1 -0
- package/dist/4431.js.map +1 -0
- package/dist/4454.js +1 -0
- package/dist/4454.js.map +1 -0
- package/dist/4457.js +2 -0
- package/dist/4457.js.LICENSE.txt +282 -0
- package/dist/4457.js.map +1 -0
- package/dist/4605.js +1 -0
- package/dist/4605.js.map +1 -0
- package/dist/4611.js +1 -0
- package/dist/4611.js.map +1 -0
- package/dist/4652.js +1 -0
- package/dist/4674.js +1 -0
- package/dist/4674.js.map +1 -0
- package/dist/4755.js +1 -0
- package/dist/4755.js.map +1 -0
- package/dist/4856.js +1 -0
- package/dist/4856.js.map +1 -0
- package/dist/4986.js +1 -0
- package/dist/4986.js.map +1 -0
- package/dist/5119.js +1 -0
- package/dist/5119.js.map +1 -0
- package/dist/5167.js +1 -0
- package/dist/5167.js.map +1 -0
- package/dist/5317.js +1 -0
- package/dist/5317.js.map +1 -0
- package/dist/5344.js +1 -0
- package/dist/5344.js.map +1 -0
- package/dist/5380.js +1 -0
- package/dist/5380.js.map +1 -0
- package/dist/5422.js +1 -0
- package/dist/5422.js.map +1 -0
- package/dist/5576.js +1 -0
- package/dist/5576.js.map +1 -0
- package/dist/5724.js +1 -0
- package/dist/5724.js.map +1 -0
- package/dist/5851.js +1 -0
- package/dist/5851.js.map +1 -0
- package/dist/5961.js +1 -0
- package/dist/5961.js.map +1 -0
- package/dist/6177.js +1 -0
- package/dist/6177.js.map +1 -0
- package/dist/6264.js +1 -0
- package/dist/6264.js.map +1 -0
- package/dist/6540.js +2 -0
- package/dist/6540.js.map +1 -0
- package/dist/6606.js +1 -0
- package/dist/6606.js.map +1 -0
- package/dist/6720.js +1 -0
- package/dist/6720.js.map +1 -0
- package/dist/6824.js +1 -0
- package/dist/6824.js.map +1 -0
- package/dist/6942.js +1 -0
- package/dist/6942.js.map +1 -0
- package/dist/71.js +1 -0
- package/dist/71.js.map +1 -0
- package/dist/7207.js +1 -0
- package/dist/7207.js.map +1 -0
- package/dist/7255.js +1 -0
- package/dist/7255.js.map +1 -0
- package/dist/7420.js +1 -0
- package/dist/7420.js.map +1 -0
- package/dist/7521.js +1 -0
- package/dist/7521.js.map +1 -0
- package/dist/7549.js +1 -0
- package/dist/7549.js.map +1 -0
- package/dist/7557.js +1 -0
- package/dist/7557.js.map +1 -0
- package/dist/7717.js +1 -0
- package/dist/7717.js.map +1 -0
- package/dist/7830.js +2 -0
- package/dist/7830.js.map +1 -0
- package/dist/7857.js +1 -0
- package/dist/7857.js.map +1 -0
- package/dist/7941.js +1 -0
- package/dist/7941.js.map +1 -0
- package/dist/8091.js +1 -0
- package/dist/8091.js.map +1 -0
- package/dist/810.js +1 -0
- package/dist/810.js.map +1 -0
- package/dist/8146.js +1 -0
- package/dist/8146.js.map +1 -0
- package/dist/850.js +1 -0
- package/dist/850.js.map +1 -0
- package/dist/8558.js +1 -0
- package/dist/8558.js.map +1 -0
- package/dist/8684.js +1 -0
- package/dist/8684.js.map +1 -0
- package/dist/8895.js +1 -0
- package/dist/8895.js.map +1 -0
- package/dist/9070.js +1 -0
- package/dist/9070.js.map +1 -0
- package/dist/9091.js +1 -0
- package/dist/9091.js.map +1 -0
- package/dist/9093.js +1 -0
- package/dist/9093.js.map +1 -0
- package/dist/9338.js +1 -0
- package/dist/9338.js.map +1 -0
- package/dist/9349.js +1 -0
- package/dist/9349.js.map +1 -0
- package/dist/9418.js +2 -0
- package/dist/9418.js.LICENSE.txt +1 -0
- package/dist/9418.js.map +1 -0
- package/dist/9428.js +1 -0
- package/dist/9428.js.map +1 -0
- package/dist/9583.js +2 -0
- package/dist/9583.js.map +1 -0
- package/dist/9871.js +1 -0
- package/dist/9871.js.map +1 -0
- package/dist/esm-dha-workflow-app.js +1 -1
- package/dist/esm-dha-workflow-app.js.buildmanifest.json +1830 -221
- package/dist/esm-dha-workflow-app.js.map +1 -1
- package/dist/main.js +1 -1
- package/dist/main.js.map +1 -1
- package/dist/routes.json +1 -1
- package/local-config.json +9 -0
- package/package.json +16 -6
- package/src/admissions/admission-request-list/admission-request-list.scss +0 -0
- package/src/admissions/admission-request-list/admission-request-list.tsx +263 -0
- package/src/admissions/admissions-dashboard.component.scss +15 -0
- package/src/admissions/admissions-dashboard.component.tsx +277 -0
- package/src/admissions/admissions.resource.ts +233 -0
- package/src/admissions/admitted-list/admitted-patients-list.tsx +206 -0
- package/src/admissions/awaiting-discharge-list/awaiting-discharge-list.tsx +197 -0
- package/src/admissions/bed-assigment-request/bed-assignment-request-list.tsx +134 -0
- package/src/admissions/constants/index.ts +33 -0
- package/src/admissions/discharged-list/discharged-list.scss +0 -0
- package/src/admissions/discharged-list/discharged-list.tsx +67 -0
- package/src/admissions/form-entry-workspace/form-entry-workspace.tsx +62 -0
- package/src/admissions/inpatient/admission-history/patient-admission-history.tsx +76 -0
- package/src/admissions/inpatient/admission-requests/patient-admission-requests.scss +19 -0
- package/src/admissions/inpatient/admission-requests/patient-admission-requests.tsx +72 -0
- package/src/admissions/inpatient/inpatient-admissions.component.scss +7 -0
- package/src/admissions/inpatient/inpatient-admissions.component.tsx +46 -0
- package/src/admissions/modal/add-bill-items/add-bill-items.modal.scss +17 -0
- package/src/admissions/modal/add-bill-items/add-bill-items.modal.tsx +233 -0
- package/src/admissions/modal/admit-elsewhere/admit-elsewhere.modal.scss +23 -0
- package/src/admissions/modal/admit-elsewhere/admit-elsewhere.modal.tsx +156 -0
- package/src/admissions/modal/admit-patient/admit-patient.modal.scss +0 -0
- package/src/admissions/modal/admit-patient/admit-patient.modal.tsx +111 -0
- package/src/admissions/modal/bed-swap/bed-swap.modal.scss +0 -0
- package/src/admissions/modal/bed-swap/bed-swap.modal.tsx +102 -0
- package/src/admissions/modal/cancel-admission-request/cancel-admission-request.scss +11 -0
- package/src/admissions/modal/cancel-admission-request/cancel-admission-request.tsx +105 -0
- package/src/admissions/modal/discharge/discharge-patient.modal.scss +13 -0
- package/src/admissions/modal/discharge/discharge-patient.modal.tsx +86 -0
- package/src/admissions/types/index.ts +263 -0
- package/src/appointments/appointments.component.scss +6 -0
- package/src/appointments/appointments.component.tsx +8 -1
- package/src/billing/api/billing.api.ts +139 -0
- package/src/billing/billing-claims.resource.ts +916 -0
- package/src/billing/billing-dashboard-link.component.tsx +31 -0
- package/src/billing/billing-dashboard-reset.ts +27 -0
- package/src/billing/billing-root.tsx +42 -0
- package/src/billing/dashboard/billingDashboard.component.scss +0 -0
- package/src/billing/dashboard/billingDashboard.component.tsx +25 -0
- package/src/billing/dashboard/v2/active-visits/active-visits.component.tsx +257 -0
- package/src/billing/dashboard/v2/active-visits/active-visits.resource.ts +85 -0
- package/src/billing/dashboard/v2/active-visits/active-visits.scss +15 -0
- package/src/billing/dashboard/v2/active-visits.component.tsx +102 -0
- package/src/billing/dashboard/v2/admissions/admission-requests-tab.component.tsx +79 -0
- package/src/billing/dashboard/v2/billing-claims-dashboard.component.scss +209 -0
- package/src/billing/dashboard/v2/billing-claims-dashboard.component.tsx +396 -0
- package/src/billing/dashboard/v2/cash-checklist/bill-document.component.scss +90 -0
- package/src/billing/dashboard/v2/cash-checklist/bill-document.component.tsx +101 -0
- package/src/billing/dashboard/v2/cash-checklist/cash-checklist.component.scss +199 -0
- package/src/billing/dashboard/v2/cash-checklist/cash-checklist.component.tsx +361 -0
- package/src/billing/dashboard/v2/cash-checklist/cash-checklist.resource.ts +239 -0
- package/src/billing/dashboard/v2/cash-checklist/payment-drawer.component.scss +246 -0
- package/src/billing/dashboard/v2/cash-checklist/payment-drawer.component.tsx +182 -0
- package/src/billing/dashboard/v2/claim-statuses.ts +143 -0
- package/src/billing/dashboard/v2/claim-visits/claim-diagnosis-details/claim-diagnosis-details.component.scss +0 -0
- package/src/billing/dashboard/v2/claim-visits/claim-diagnosis-details/claim-diagnosis-details.component.tsx +35 -0
- package/src/billing/dashboard/v2/claim-visits/claim-diagnosis-details/claim-diagnosis-panel.component.scss +67 -0
- package/src/billing/dashboard/v2/claim-visits/claim-diagnosis-details/claim-diagnosis-panel.component.tsx +90 -0
- package/src/billing/dashboard/v2/claim-visits/claim-doctors/claim-doctors.scss +28 -0
- package/src/billing/dashboard/v2/claim-visits/claim-doctors/claim-doctors.tsx +36 -0
- package/src/billing/dashboard/v2/claim-visits/claim-history/claim-history.component.scss +119 -0
- package/src/billing/dashboard/v2/claim-visits/claim-history/claim-history.component.tsx +198 -0
- package/src/billing/dashboard/v2/claim-visits/claim-intervention-details/claim-intervention-details.component.scss +5 -0
- package/src/billing/dashboard/v2/claim-visits/claim-intervention-details/claim-intervention-details.component.tsx +160 -0
- package/src/billing/dashboard/v2/claim-visits/claim-intervention-details/claim-intervention-panel.component.scss +105 -0
- package/src/billing/dashboard/v2/claim-visits/claim-intervention-details/claim-intervention-panel.component.tsx +184 -0
- package/src/billing/dashboard/v2/claim-visits/claim-intervention-details/intervention-attachments.component.scss +161 -0
- package/src/billing/dashboard/v2/claim-visits/claim-intervention-details/intervention-attachments.component.tsx +328 -0
- package/src/billing/dashboard/v2/claim-visits/claim-invoice-details/claim-invoice-details.component.tsx +96 -0
- package/src/billing/dashboard/v2/claim-visits/claim-invoice-details/claim-invoice-document.component.tsx +236 -0
- package/src/billing/dashboard/v2/claim-visits/claim-invoice-details/claim-invoice-panel.component.scss +98 -0
- package/src/billing/dashboard/v2/claim-visits/claim-invoice-details/claim-invoice-panel.component.tsx +292 -0
- package/src/billing/dashboard/v2/claim-visits/claim-invoice-line-details/claim-invoice-line-details.component.scss +133 -0
- package/src/billing/dashboard/v2/claim-visits/claim-invoice-line-details/claim-invoice-line-details.component.tsx +217 -0
- package/src/billing/dashboard/v2/claim-visits/claim-visit-details/claim-details-by-token.component.tsx +150 -0
- package/src/billing/dashboard/v2/claim-visits/claim-visit-details/claim-details-page.component.scss +13 -0
- package/src/billing/dashboard/v2/claim-visits/claim-visit-details/claim-details-page.component.tsx +116 -0
- package/src/billing/dashboard/v2/claim-visits/claim-visit-details/claim-visit-details.component.scss +338 -0
- package/src/billing/dashboard/v2/claim-visits/claim-visit-details/claim-visit-details.component.tsx +616 -0
- package/src/billing/dashboard/v2/claim-visits/claim-visits.component.scss +5 -0
- package/src/billing/dashboard/v2/claim-visits/claim-visits.component.tsx +133 -0
- package/src/billing/dashboard/v2/claim-visits/modal/claim-doctors/add-claim-doctor/add-claim-doctor.modal.scss +5 -0
- package/src/billing/dashboard/v2/claim-visits/modal/claim-doctors/add-claim-doctor/add-claim-doctor.modal.tsx +49 -0
- package/src/billing/dashboard/v2/claim-visits/modal/claim-visit-details/claim-visit-details.modal.scss +17 -0
- package/src/billing/dashboard/v2/claim-visits/modal/claim-visit-details/claim-visit-details.modal.tsx +56 -0
- package/src/billing/dashboard/v2/claim-visits/modal/close-claim/close-claim.modal.scss +0 -0
- package/src/billing/dashboard/v2/claim-visits/modal/close-claim/close-claim.modal.tsx +129 -0
- package/src/billing/dashboard/v2/claim-visits/modal/submit-claim/submit-claim.modal.tsx +204 -0
- package/src/billing/dashboard/v2/claim-visits/shared/_empty-state.scss +28 -0
- package/src/billing/dashboard/v2/claim-visits/shared/record-cards.component.scss +298 -0
- package/src/billing/dashboard/v2/claim-visits/shared/record-cards.component.tsx +252 -0
- package/src/billing/dashboard/v2/claim-visits/shared/record-details.workspace.scss +42 -0
- package/src/billing/dashboard/v2/claim-visits/shared/record-details.workspace.tsx +54 -0
- package/src/billing/dashboard/v2/claim-visits/shared/record-table.component.scss +61 -0
- package/src/billing/dashboard/v2/claim-visits/shared/record-table.component.tsx +150 -0
- package/src/billing/dashboard/v2/claim-visits/switch-intervention/switch-intervention.resource.ts +51 -0
- package/src/billing/dashboard/v2/claim-visits/switch-intervention/switch-intervention.workspace.scss +191 -0
- package/src/billing/dashboard/v2/claim-visits/switch-intervention/switch-intervention.workspace.tsx +648 -0
- package/src/billing/dashboard/v2/claim-workspace/claim-workspace.component.scss +595 -0
- package/src/billing/dashboard/v2/claim-workspace/claim-workspace.component.tsx +810 -0
- package/src/billing/dashboard/v2/claim-workspace/claim-workspace.resource.ts +282 -0
- package/src/billing/dashboard/v2/claims-accounting/bill-utils.ts +134 -0
- package/src/billing/dashboard/v2/claims-accounting/claim-detail.component.tsx +346 -0
- package/src/billing/dashboard/v2/claims-accounting/claims-accounting.component.scss +546 -0
- package/src/billing/dashboard/v2/claims-accounting/claims-accounting.component.tsx +206 -0
- package/src/billing/dashboard/v2/claims-accounting/claims-accounting.resource.ts +337 -0
- package/src/billing/dashboard/v2/claims-accounting/invoice-document.component.scss +90 -0
- package/src/billing/dashboard/v2/claims-accounting/invoice-document.component.tsx +90 -0
- package/src/billing/dashboard/v2/claims-accounting/remittances.component.tsx +169 -0
- package/src/billing/dashboard/v2/claims-accounting/remittances.resource.ts +46 -0
- package/src/billing/dashboard/v2/claims-accounting/status-meta.ts +25 -0
- package/src/billing/dashboard/v2/clearance/cash-patients.scss +18 -0
- package/src/billing/dashboard/v2/clearance/cash.component.tsx +145 -0
- package/src/billing/dashboard/v2/clearance/clearance.component.scss +84 -0
- package/src/billing/dashboard/v2/clearance/clearance.component.tsx +225 -0
- package/src/billing/dashboard/v2/clearance/pay-cash.component.tsx +202 -0
- package/src/billing/dashboard/v2/clearance/pay-cash.scss +28 -0
- package/src/billing/dashboard/v2/clearance/prepaid-services.component.tsx +112 -0
- package/src/billing/dashboard/v2/facility-bills/claim-status.ts +249 -0
- package/src/billing/dashboard/v2/facility-bills/facility-bills.component.scss +140 -0
- package/src/billing/dashboard/v2/facility-bills/facility-bills.component.tsx +688 -0
- package/src/billing/dashboard/v2/facility-bills/preauths-datatables/pending-preauths.component.tsx +132 -0
- package/src/billing/dashboard/v2/facility-bills/preauths-datatables/preauth-preview-table.component.tsx +267 -0
- package/src/billing/dashboard/v2/facility-bills/preauths.component.tsx +130 -0
- package/src/billing/dashboard/v2/inpatient-requests/inpatient-requests.component.scss +22 -0
- package/src/billing/dashboard/v2/inpatient-requests/inpatient-requests.component.tsx +389 -0
- package/src/billing/dashboard/v2/inpatient-requests/modal/start-inpatient-claim/start-inpatient-claim.component.scss +0 -0
- package/src/billing/dashboard/v2/inpatient-requests/modal/start-inpatient-claim/start-inpatient-claim.component.tsx +0 -0
- package/src/billing/dashboard/v2/new-facility-bills/bills-table.component.tsx +78 -0
- package/src/billing/dashboard/v2/new-facility-bills/bills-table.scss +18 -0
- package/src/billing/dashboard/v2/new-facility-bills/new-facility-bills.component.tsx +64 -0
- package/src/billing/dashboard/v2/new-facility-bills/new-facility-bills.scss +146 -0
- package/src/billing/dashboard/v2/patient-bill-details/attachments/add-attachments.component.tsx +223 -0
- package/src/billing/dashboard/v2/patient-bill-details/attachments/attachment.component.tsx +157 -0
- package/src/billing/dashboard/v2/patient-bill-details/attachments/attachments.scss +92 -0
- package/src/billing/dashboard/v2/patient-bill-details/attachments/case-summary/case-summary.scss +295 -0
- package/src/billing/dashboard/v2/patient-bill-details/attachments/case-summary/case-summary.tsx +326 -0
- package/src/billing/dashboard/v2/patient-bill-details/attachments/dialysis/dialysis-chart.scss +238 -0
- package/src/billing/dashboard/v2/patient-bill-details/attachments/dialysis/dialysis-chart.tsx +269 -0
- package/src/billing/dashboard/v2/patient-bill-details/attachments/discharge-summary/discharge-summary.scss +96 -0
- package/src/billing/dashboard/v2/patient-bill-details/attachments/discharge-summary/discharge-summary.tsx +124 -0
- package/src/billing/dashboard/v2/patient-bill-details/attachments/final-bill.component.tsx +152 -0
- package/src/billing/dashboard/v2/patient-bill-details/attachments/final-bill.scss +225 -0
- package/src/billing/dashboard/v2/patient-bill-details/attachments/general-discharge-summary/general-discharge-summary.scss +182 -0
- package/src/billing/dashboard/v2/patient-bill-details/attachments/general-discharge-summary/general-discharge-summary.tsx +173 -0
- package/src/billing/dashboard/v2/patient-bill-details/attachments/generate-attachments.component.tsx +371 -0
- package/src/billing/dashboard/v2/patient-bill-details/attachments/invoice.component.tsx +151 -0
- package/src/billing/dashboard/v2/patient-bill-details/attachments/invoice.scss +225 -0
- package/src/billing/dashboard/v2/patient-bill-details/attachments/type.ts +44 -0
- package/src/billing/dashboard/v2/patient-bill-details/bill-details/bill-details.scss +107 -0
- package/src/billing/dashboard/v2/patient-bill-details/bill-details/bill-details.tsx +508 -0
- package/src/billing/dashboard/v2/patient-bill-details/bill-details-page.component.scss +30 -0
- package/src/billing/dashboard/v2/patient-bill-details/bill-details-page.component.tsx +94 -0
- package/src/billing/dashboard/v2/patient-bill-details/claim-details/claim-details-skeleton.component.scss +89 -0
- package/src/billing/dashboard/v2/patient-bill-details/claim-details/claim-details-skeleton.component.tsx +53 -0
- package/src/billing/dashboard/v2/patient-bill-details/claim-details/patient-claim-details.component.scss +32 -0
- package/src/billing/dashboard/v2/patient-bill-details/claim-details/patient-claim-details.component.tsx +215 -0
- package/src/billing/dashboard/v2/patient-bill-details/modals/add-claim-diagnosis/add-claim-diagnosis.modal.scss +0 -0
- package/src/billing/dashboard/v2/patient-bill-details/modals/add-claim-diagnosis/add-claim-diagnosis.modal.tsx +99 -0
- package/src/billing/dashboard/v2/patient-bill-details/modals/add-claim-line/add-claim-line.modal.tsx +187 -0
- package/src/billing/dashboard/v2/patient-bill-details/patient-bill-details.scss +153 -0
- package/src/billing/dashboard/v2/patient-bill-details/patient-bill-details.tsx +458 -0
- package/src/billing/dashboard/v2/patient-bill-details/payment-mode.ts +124 -0
- package/src/billing/dashboard/v2/patient-bill-details/workspaces/add-claim-line/add-claim-line.workspace.scss +27 -0
- package/src/billing/dashboard/v2/patient-bill-details/workspaces/add-claim-line/add-claim-line.workspace.tsx +223 -0
- package/src/billing/dashboard/v2/patient-bill-details/workspaces/bill-item-payment/bill-item-payment.workspace.scss +27 -0
- package/src/billing/dashboard/v2/patient-bill-details/workspaces/bill-item-payment/bill-item-payment.workspace.tsx +109 -0
- package/src/billing/dashboard/v2/preauth/preauth-attachments.component.tsx +389 -0
- package/src/billing/dashboard/v2/preauth/preauth-list.component.scss +38 -0
- package/src/billing/dashboard/v2/preauth/preauth-list.component.tsx +241 -0
- package/src/billing/dashboard/v2/preauth/preauth-status-tag.component.tsx +75 -0
- package/src/billing/dashboard/v2/preauth/preauth-typed-fields.md +125 -0
- package/src/billing/dashboard/v2/preauth/preauth.resource.ts +1067 -0
- package/src/billing/dashboard/v2/preauth/preauth.workspace.scss +91 -0
- package/src/billing/dashboard/v2/preauth/preauth.workspace.tsx +1802 -0
- package/src/billing/dashboard/v2/preauth/preauthorizations-tab.component.tsx +49 -0
- package/src/billing/dashboard/v2/sha-claims/sha-claims-table.component.tsx +36 -0
- package/src/billing/dashboard/v2/sha-claims/sha-claims.component.tsx +60 -0
- package/src/billing/dashboard/v2/sha-claims/sha-claims.scss +8 -0
- package/src/billing/dashboard/v2/shared/dash-board-card/dash-board-card.component.scss +17 -0
- package/src/billing/dashboard/v2/shared/dash-board-card/dash-board-card.component.tsx +15 -0
- package/src/billing/dashboard/v2/shared/empty-state.component.scss +24 -0
- package/src/billing/dashboard/v2/shared/empty-state.component.tsx +13 -0
- package/src/billing/dashboard/v2/shared/scroll-to-top.component.scss +54 -0
- package/src/billing/dashboard/v2/shared/scroll-to-top.component.tsx +54 -0
- package/src/billing/dashboard/v2/shared/table-toolbar.component.scss +77 -0
- package/src/billing/dashboard/v2/shared/table-toolbar.component.tsx +48 -0
- package/src/billing/dashboard/v2/types/index.ts +514 -0
- package/src/billing/dashboard/v3/active-visits/active-visits.component.tsx +256 -0
- package/src/billing/dashboard/v3/active-visits/active-visits.resource.ts +57 -0
- package/src/billing/dashboard/v3/active-visits/active-visits.scss +15 -0
- package/src/billing/dashboard/v3/cash-checklist/bill-document.component.scss +90 -0
- package/src/billing/dashboard/v3/cash-checklist/bill-document.component.tsx +101 -0
- package/src/billing/dashboard/v3/cash-checklist/cash-checklist.component.scss +199 -0
- package/src/billing/dashboard/v3/cash-checklist/cash-checklist.component.tsx +361 -0
- package/src/billing/dashboard/v3/cash-checklist/cash-checklist.resource.ts +240 -0
- package/src/billing/dashboard/v3/cash-checklist/payment-drawer.component.scss +246 -0
- package/src/billing/dashboard/v3/cash-checklist/payment-drawer.component.tsx +182 -0
- package/src/billing/dashboard/v3/claim-visits/claim-diagnosis-details/claim-diagnosis-details.component.scss +0 -0
- package/src/billing/dashboard/v3/claim-visits/claim-diagnosis-details/claim-diagnosis-details.component.tsx +44 -0
- package/src/billing/dashboard/v3/claim-visits/claim-doctors/claim-doctors.scss +5 -0
- package/src/billing/dashboard/v3/claim-visits/claim-doctors/claim-doctors.tsx +42 -0
- package/src/billing/dashboard/v3/claim-visits/claim-documents/claim-documents.scss +10 -0
- package/src/billing/dashboard/v3/claim-visits/claim-documents/claim-documents.tsx +25 -0
- package/src/billing/dashboard/v3/claim-visits/claim-intervention-details/claim-intervention-details.component.tsx +220 -0
- package/src/billing/dashboard/v3/claim-visits/claim-invoice-details/claim-invoice-details.component.scss +6 -0
- package/src/billing/dashboard/v3/claim-visits/claim-invoice-details/claim-invoice-details.component.tsx +85 -0
- package/src/billing/dashboard/v3/claim-visits/claim-invoice-line-details/claim-invoice-line-details.component.scss +0 -0
- package/src/billing/dashboard/v3/claim-visits/claim-invoice-line-details/claim-invoice-line-details.component.tsx +109 -0
- package/src/billing/dashboard/v3/claim-visits/claim-visit-details/claim-visit-details.component.scss +97 -0
- package/src/billing/dashboard/v3/claim-visits/claim-visit-details/claim-visit-details.component.tsx +343 -0
- package/src/billing/dashboard/v3/claim-visits/claim-visits.component.scss +5 -0
- package/src/billing/dashboard/v3/claim-visits/claim-visits.component.tsx +139 -0
- package/src/billing/dashboard/v3/claim-visits/modal/claim-doctors/add-claim-doctor/add-claim-doctor.modal.scss +5 -0
- package/src/billing/dashboard/v3/claim-visits/modal/claim-doctors/add-claim-doctor/add-claim-doctor.modal.tsx +49 -0
- package/src/billing/dashboard/v3/claim-visits/modal/claim-invoice-lines/claim-invoice-lines.modal.scss +7 -0
- package/src/billing/dashboard/v3/claim-visits/modal/claim-invoice-lines/claim-invoice-lines.modal.tsx +35 -0
- package/src/billing/dashboard/v3/claim-visits/modal/claim-visit-details/claim-visit-details.modal.scss +6 -0
- package/src/billing/dashboard/v3/claim-visits/modal/claim-visit-details/claim-visit-details.modal.tsx +33 -0
- package/src/billing/dashboard/v3/claim-visits/modal/close-claim/close-claim.modal.scss +0 -0
- package/src/billing/dashboard/v3/claim-visits/modal/close-claim/close-claim.modal.tsx +129 -0
- package/src/billing/dashboard/v3/claim-visits/modal/submit-claim/submit-claim.modal.tsx +204 -0
- package/src/billing/dashboard/v3/claim-visits/payer-preview/index.scss +140 -0
- package/src/billing/dashboard/v3/claim-visits/payer-preview/payer-preview-tile.component.tsx +55 -0
- package/src/billing/dashboard/v3/claim-visits/payer-preview/payer-preview-workspace.component.tsx +157 -0
- package/src/billing/dashboard/v3/claim-visits/payer-preview/tables/payer-claim-doctors.component.tsx +54 -0
- package/src/billing/dashboard/v3/claim-visits/payer-preview/tables/payer-claim-lines.component.tsx +48 -0
- package/src/billing/dashboard/v3/claim-visits/payer-preview/tables/payer-claim-notes.component.tsx +47 -0
- package/src/billing/dashboard/v3/claim-visits/payer-preview/tables/payer-diagnoses.component.tsx +44 -0
- package/src/billing/dashboard/v3/claim-visits/payer-preview/tables/payer-interventions.component.tsx +44 -0
- package/src/billing/dashboard/v3/claim-visits/switch-intervention/switch-intervention.resource.ts +51 -0
- package/src/billing/dashboard/v3/claim-visits/switch-intervention/switch-intervention.workspace.scss +191 -0
- package/src/billing/dashboard/v3/claim-visits/switch-intervention/switch-intervention.workspace.tsx +648 -0
- package/src/billing/dashboard/v3/claim-workspace/claim-workspace.component.scss +595 -0
- package/src/billing/dashboard/v3/claim-workspace/claim-workspace.component.tsx +810 -0
- package/src/billing/dashboard/v3/claim-workspace/claim-workspace.resource.ts +282 -0
- package/src/billing/dashboard/v3/claims-accounting/bill-utils.ts +134 -0
- package/src/billing/dashboard/v3/claims-accounting/claim-detail.component.tsx +346 -0
- package/src/billing/dashboard/v3/claims-accounting/claims-accounting.component.scss +546 -0
- package/src/billing/dashboard/v3/claims-accounting/claims-accounting.component.tsx +206 -0
- package/src/billing/dashboard/v3/claims-accounting/claims-accounting.resource.ts +337 -0
- package/src/billing/dashboard/v3/claims-accounting/invoice-document.component.scss +90 -0
- package/src/billing/dashboard/v3/claims-accounting/invoice-document.component.tsx +90 -0
- package/src/billing/dashboard/v3/claims-accounting/remittances.component.tsx +169 -0
- package/src/billing/dashboard/v3/claims-accounting/remittances.resource.ts +46 -0
- package/src/billing/dashboard/v3/claims-accounting/status-meta.ts +25 -0
- package/src/billing/dashboard/v3/clearance/clearance.component.scss +100 -0
- package/src/billing/dashboard/v3/clearance/clearance.component.tsx +0 -0
- package/src/billing/dashboard/v3/clearance/prepaid-services.component.tsx +112 -0
- package/src/billing/dashboard/v3/facility-bills/claim-status.ts +243 -0
- package/src/billing/dashboard/v3/facility-bills/facility-bills.component.scss +72 -0
- package/src/billing/dashboard/v3/facility-bills/facility-bills.component.tsx +181 -0
- package/src/billing/dashboard/v3/facility-bills/preauths-datatables/pending-preauths.component.tsx +132 -0
- package/src/billing/dashboard/v3/facility-bills/preauths-datatables/preauth-preview-table.component.tsx +232 -0
- package/src/billing/dashboard/v3/facility-bills/preauths.component.tsx +127 -0
- package/src/billing/dashboard/v3/patient-bill-details/attachments/add-attachments.component.tsx +223 -0
- package/src/billing/dashboard/v3/patient-bill-details/attachments/attachment.component.tsx +157 -0
- package/src/billing/dashboard/v3/patient-bill-details/attachments/attachments.scss +92 -0
- package/src/billing/dashboard/v3/patient-bill-details/attachments/case-summary/case-summary-helper.ts +58 -0
- package/src/billing/dashboard/v3/patient-bill-details/attachments/case-summary/case-summary.scss +295 -0
- package/src/billing/dashboard/v3/patient-bill-details/attachments/case-summary/case-summary.tsx +287 -0
- package/src/billing/dashboard/v3/patient-bill-details/attachments/dialysis/dialysis-chart.scss +238 -0
- package/src/billing/dashboard/v3/patient-bill-details/attachments/dialysis/dialysis-chart.tsx +317 -0
- package/src/billing/dashboard/v3/patient-bill-details/attachments/dialysis/dialysis-helper.ts +94 -0
- package/src/billing/dashboard/v3/patient-bill-details/attachments/discharge-summary/discharge-summary.scss +96 -0
- package/src/billing/dashboard/v3/patient-bill-details/attachments/discharge-summary/discharge-summary.tsx +126 -0
- package/src/billing/dashboard/v3/patient-bill-details/attachments/final-bill.component.tsx +75 -0
- package/src/billing/dashboard/v3/patient-bill-details/attachments/general-discharge-summary/general-discharge-summary.scss +182 -0
- package/src/billing/dashboard/v3/patient-bill-details/attachments/general-discharge-summary/general-discharge-summary.tsx +253 -0
- package/src/billing/dashboard/v3/patient-bill-details/attachments/generate-attachments.component.tsx +345 -0
- package/src/billing/dashboard/v3/patient-bill-details/attachments/invoice.component.tsx +150 -0
- package/src/billing/dashboard/v3/patient-bill-details/attachments/invoice.scss +225 -0
- package/src/billing/dashboard/v3/patient-bill-details/attachments/type.ts +259 -0
- package/src/billing/dashboard/v3/patient-bill-details/bill-details/bill-details.scss +17 -0
- package/src/billing/dashboard/v3/patient-bill-details/bill-details/bill-details.tsx +260 -0
- package/src/billing/dashboard/v3/patient-bill-details/claim-details/patient-claim-details.component.scss +0 -0
- package/src/billing/dashboard/v3/patient-bill-details/claim-details/patient-claim-details.component.tsx +68 -0
- package/src/billing/dashboard/v3/patient-bill-details/modals/add-claim-diagnosis/add-claim-diagnosis.modal.scss +0 -0
- package/src/billing/dashboard/v3/patient-bill-details/modals/add-claim-diagnosis/add-claim-diagnosis.modal.tsx +99 -0
- package/src/billing/dashboard/v3/patient-bill-details/modals/add-claim-line/add-claim-line.modal.scss +14 -0
- package/src/billing/dashboard/v3/patient-bill-details/modals/add-claim-line/add-claim-line.modal.tsx +112 -0
- package/src/billing/dashboard/v3/patient-bill-details/modals/bill-item-payment/bill-item-payment.modal.scss +6 -0
- package/src/billing/dashboard/v3/patient-bill-details/modals/bill-item-payment/bill-item-payment.modal.tsx +139 -0
- package/src/billing/dashboard/v3/patient-bill-details/patient-bill-details.scss +85 -0
- package/src/billing/dashboard/v3/patient-bill-details/patient-bill-details.tsx +343 -0
- package/src/billing/dashboard/v3/patient-bill-details/visit-diagnosis-details/visit-diagnosis-details.component.scss +0 -0
- package/src/billing/dashboard/v3/patient-bill-details/visit-diagnosis-details/visit-diagnosis-details.component.tsx +92 -0
- package/src/billing/dashboard/v3/preauth/elective/elective-authorize.resource.ts +284 -0
- package/src/billing/dashboard/v3/preauth/elective/elective-preauth-request.workspace.scss +125 -0
- package/src/billing/dashboard/v3/preauth/elective/elective-preauth-request.workspace.tsx +1184 -0
- package/src/billing/dashboard/v3/preauth/elective/elective-preauth.component.scss +15 -0
- package/src/billing/dashboard/v3/preauth/elective/elective-preauth.component.tsx +377 -0
- package/src/billing/dashboard/v3/preauth/elective/elective-preauth.resource.ts +1378 -0
- package/src/billing/dashboard/v3/preauth/elective/elective-requests-list.component.tsx +357 -0
- package/src/billing/dashboard/v3/preauth/preauth-attachments.component.tsx +389 -0
- package/src/billing/dashboard/v3/preauth/preauth-list.component.scss +34 -0
- package/src/billing/dashboard/v3/preauth/preauth-list.component.tsx +300 -0
- package/src/billing/dashboard/v3/preauth/preauth-status-tag.component.tsx +75 -0
- package/src/billing/dashboard/v3/preauth/preauth-typed-fields.md +125 -0
- package/src/billing/dashboard/v3/preauth/preauth.resource.ts +1197 -0
- package/src/billing/dashboard/v3/preauth/preauth.workspace.scss +176 -0
- package/src/billing/dashboard/v3/preauth/preauth.workspace.tsx +2266 -0
- package/src/billing/dashboard/v3/preauth/preauthorizations-tab.component.tsx +55 -0
- package/src/billing/dashboard/v3/shared/empty-state.component.scss +25 -0
- package/src/billing/dashboard/v3/shared/empty-state.component.tsx +13 -0
- package/src/billing/dashboard/v3/shared/table-toolbar.component.scss +64 -0
- package/src/billing/dashboard/v3/shared/table-toolbar.component.tsx +48 -0
- package/src/billing/dashboard/v3/types/index.ts +384 -0
- package/src/billing/extensions/generate-order-bill-button/generate-order-bill-button.extension.tsx +55 -0
- package/src/billing/extensions/generate-order-bill-button/generate-order-bill-button.scss +16 -0
- package/src/billing/extensions/visit-billing/visit-billing.extension.scss +0 -0
- package/src/billing/extensions/visit-billing/visit-billing.extension.tsx +509 -0
- package/src/billing/invoice/bill.resource.ts +47 -0
- package/src/billing/invoice/invoice-header/header-card/header-card.scss +17 -0
- package/src/billing/invoice/invoice-header/header-card/header-card.tsx +21 -0
- package/src/billing/invoice/invoice.scss +113 -0
- package/src/billing/invoice/invoice.tsx +1441 -0
- package/src/billing/invoice/line-items/line-items.scss +0 -0
- package/src/billing/invoice/line-items/line-items.tsx +120 -0
- package/src/billing/invoice/modal/delete-bill-item/delete-bill-item.modal.scss +23 -0
- package/src/billing/invoice/modal/delete-bill-item/delete-bill-item.modal.tsx +114 -0
- package/src/billing/invoice/modal/edit-bill-item/edit-bill-item.modal.scss +0 -0
- package/src/billing/invoice/modal/edit-bill-item/edit-bill-item.modal.tsx +122 -0
- package/src/billing/invoice/payment-details/payment-details.scss +0 -0
- package/src/billing/invoice/payment-details/payment-details.tsx +43 -0
- package/src/billing/invoice/print-invoice/print-receipt.component.tsx +44 -0
- package/src/billing/invoice/print-invoice/print-receipt.scss +14 -0
- package/src/billing/maternity-discharge.resource.ts +21 -0
- package/src/billing/types/index.ts +354 -0
- package/src/billing/utils/index.ts +17 -0
- package/src/billing/widgets/billingTotalsRow.component.scss +0 -0
- package/src/billing/widgets/billingTotalsRow.component.tsx +712 -0
- package/src/billing/workspaces/create-order-bill-form-workspace/create-order-bill-form.resource.ts +130 -0
- package/src/billing/workspaces/create-order-bill-form-workspace/create-order-bill-form.scss +58 -0
- package/src/billing/workspaces/create-order-bill-form-workspace/create-order-bill-form.workspace.tsx +656 -0
- package/src/billing/workspaces/create-order-bill-form-workspace/schema.ts +12 -0
- package/src/bookings/bookings.component.scss +7 -0
- package/src/bookings/bookings.component.tsx +35 -0
- package/src/bookings/daily/daily-bookings.resource.ts +2 -1
- package/src/case-summary/case-summary.extension.scss +13 -0
- package/src/case-summary/case-summary.extension.tsx +40 -0
- package/src/case-summary/case-summary.resource.test.ts +69 -0
- package/src/case-summary/case-summary.resource.ts +52 -0
- package/src/case-summary/components/case-summary-printable.component.scss +174 -0
- package/src/case-summary/components/case-summary-printable.component.tsx +288 -0
- package/src/case-summary/components/case-summary-printable.utils.ts +51 -0
- package/src/case-summary/types/case-summary.types.ts +152 -0
- package/src/claims/claims.component.scss +70 -0
- package/src/claims/claims.component.tsx +743 -0
- package/src/claims/claims.resource.ts +1840 -0
- package/src/claims/index.ts +365 -0
- package/src/claims/interventions.resource.ts +131 -0
- package/src/claims/utils.ts +64 -0
- package/src/config-schema.ts +149 -1
- package/src/context/patient-context.tsx +27 -0
- package/src/createDashboardLink.tsx +9 -0
- package/src/dashboard/charts/chart.component.scss +24 -0
- package/src/dashboard/charts/chart.component.tsx +56 -0
- package/src/dashboard/dashboard.component.scss +48 -3
- package/src/dashboard/dashboard.component.tsx +29 -38
- package/src/dashboard/overview/overview.component.scss +40 -59
- package/src/dashboard/overview/overview.component.tsx +55 -41
- package/src/dashboard-meta/accounting-dashboard.meta.ts +6 -0
- package/src/dashboard-meta/admissions-dashboard.meta.ts +6 -0
- package/src/dashboard-meta/billing-dashboard.meta.ts +6 -0
- package/src/dashboard-meta/bookings-dashboard.meta.ts +6 -0
- package/src/dashboard-meta/case-summary.meta.ts +6 -0
- package/src/dashboard-meta/consultation-dashboard.meta.ts +6 -0
- package/src/dashboard-meta/dha-workflow-dashboard.meta.ts +6 -0
- package/src/dashboard-meta/elective-preauth.meta.ts +6 -0
- package/src/dashboard-meta/emt-dashboard.meta.ts +6 -0
- package/src/dashboard-meta/inpatient-admissions.meta.ts +6 -0
- package/src/dashboard-meta/maternity-dashboard.meta.ts +6 -0
- package/src/dashboard-meta/mch-dashboard.meta.ts +6 -0
- package/src/dashboard-meta/pharmacy-dashboard.meta.ts +6 -0
- package/src/dashboard-meta/queue-dashboard.meta.ts +6 -0
- package/src/dashboard-meta/registers-dashboard.meta.ts +6 -0
- package/src/dashboard-meta/registry-dashboard.meta.ts +6 -0
- package/src/dashboard-meta/service-queue-admin.meta.ts +6 -0
- package/src/dashboard-meta/specialized-clinics-dashboard.meta.ts +6 -0
- package/src/dashboard-meta/triage-dashboard.meta.ts +6 -0
- package/src/emt/cr-lookup.resource.test.ts +88 -0
- package/src/emt/cr-lookup.resource.ts +42 -0
- package/src/emt/emt-queue.component.test.tsx +353 -0
- package/src/emt/emt-queue.component.tsx +463 -0
- package/src/emt/emt-queue.scss +85 -0
- package/src/emt/emt.resource.test.ts +247 -0
- package/src/emt/emt.resource.ts +150 -0
- package/src/emt/handover-modal/handover-modal.component.test.tsx +273 -0
- package/src/emt/handover-modal/handover-modal.component.tsx +541 -0
- package/src/emt/handover-modal/handover-modal.scss +113 -0
- package/src/emt/types/emt.types.ts +137 -0
- package/src/index.ts +204 -6
- package/src/mnch/maternity/maternity-queues.component.scss +7 -0
- package/src/mnch/maternity/maternity-queues.component.tsx +32 -0
- package/src/mnch/maternity/queues/triage/maternity-triage.tsx +15 -0
- package/src/mnch/mch/mch-queues.component.scss +10 -0
- package/src/mnch/mch/mch-queues.component.tsx +36 -0
- package/src/mnch/mch/queues/consultation/mch-consultation.tsx +18 -0
- package/src/mnch/mch/queues/triage/mch-triage.tsx +15 -0
- package/src/mnch/mnch-root.tsx +17 -0
- package/src/mortuary/mortuary.component.tsx +13 -0
- package/src/pharmacy/pharmacy.component.scss +16 -0
- package/src/pharmacy/pharmacy.component.tsx +11 -6
- package/src/registers/registers.component.tsx +8 -0
- package/src/registry/drawer/emr-compare.component.scss +263 -0
- package/src/registry/drawer/emr-compare.component.tsx +302 -0
- package/src/registry/drawer/emr-compare.resource.ts +181 -0
- package/src/registry/drawer/emr-create-preview.component.tsx +123 -0
- package/src/registry/drawer/otp-verification-step.component.tsx +398 -0
- package/src/registry/drawer/pomsf-balance.util.test.ts +177 -0
- package/src/registry/drawer/pomsf-balance.util.ts +79 -0
- package/src/registry/drawer/verification.resource.ts +60 -0
- package/src/registry/drawer/workflow-drawer.component.scss +1242 -0
- package/src/registry/drawer/workflow-drawer.component.tsx +1478 -0
- package/src/registry/eligibility/eligibility-details/eligibility-details.scss +38 -0
- package/src/registry/eligibility/eligibility-details/eligibility-details.tsx +101 -0
- package/src/registry/eligibility/eliigibility-tags/eligibility-tags.scss +16 -0
- package/src/registry/eligibility/eliigibility-tags/eligibility-tags.tsx +120 -0
- package/src/registry/eligibility/modal/eligibility-details.modal.scss +0 -0
- package/src/registry/eligibility/modal/eligibility-details.modal.tsx +45 -0
- package/src/registry/hie.resource.ts +466 -0
- package/src/registry/hie.types.ts +63 -0
- package/src/registry/modal/biometrics-verification-modal/biometrics-verification-modal.scss +277 -0
- package/src/registry/modal/biometrics-verification-modal/biometrics-verification-modal.tsx +218 -0
- package/src/registry/modal/biometrics-verification-modal/type.ts +39 -0
- package/src/registry/modal/client-details-modal/client-details-modal.tsx +65 -29
- package/src/registry/modal/otp-modal/otp-whitlisting-modal.component.tsx +809 -0
- package/src/registry/modal/otp-modal/otp-whitlisting-modal.scss +354 -0
- package/src/registry/modal/otp-modal/otp-whitlisting.scss +0 -0
- package/src/registry/modal/otp-verification-modal/claims-consent.scss +72 -0
- package/src/registry/modal/otp-verification-modal/claims-consent.tsx +135 -0
- package/src/registry/modal/otp-verification-modal/extension/claims-consent.extension.tsx +130 -0
- package/src/registry/modal/otp-verification-modal/otp-verification-modal.scss +30 -0
- package/src/registry/modal/otp-verification-modal/otp-verification-modal.tsx +154 -54
- package/src/registry/modal/send-to-triage/payment-details.tsx +120 -0
- package/src/registry/modal/send-to-triage/payment-method.component.tsx +54 -0
- package/src/registry/modal/send-to-triage/send-to-queue.modal.tsx +981 -0
- package/src/registry/modal/send-to-triage/send-to-triage.modal.scss +156 -3
- package/src/registry/modal/send-to-triage/send-to-triage.modal.tsx +1094 -135
- package/src/registry/modal/start-patient-visit/start-patient-visit.modal.scss +34 -0
- package/src/registry/modal/start-patient-visit/start-patient-visit.modal.tsx +231 -0
- package/src/registry/payment-details/payment-options/payment-options.scss +13 -0
- package/src/registry/payment-details/payment-options/payment-options.tsx +61 -14
- package/src/registry/registration-list/registration-list.component.scss +32 -0
- package/src/registry/registration-list/registration-list.component.tsx +103 -0
- package/src/registry/registry-entry.tsx +11 -0
- package/src/registry/registry.component.scss +402 -78
- package/src/registry/registry.component.tsx +602 -222
- package/src/registry/registry.resource.ts +22 -3
- package/src/registry/types/index.ts +118 -9
- package/src/registry/utils/error-handler.ts +54 -0
- package/src/registry/utils/format-dependant-display-data.ts +1 -1
- package/src/registry/utils/hie-client-adapter.ts +7 -4
- package/src/registry/utils/mask-data.ts +40 -0
- package/src/registry/utils/phone-number-formatter.ts +11 -0
- package/src/resources/dashboard.resource.ts +20 -0
- package/src/resources/identifier-types.ts +2 -0
- package/src/resources/patient-resource.ts +6 -1
- package/src/resources/queue.resource.ts +24 -1
- package/src/resources/superset.resource.ts +21 -0
- package/src/resources/visit.resource.ts +19 -1
- package/src/root.component.tsx +10 -5
- package/src/routes.json +477 -23
- package/src/service-queues/admin/queue/modal/create-queue/create-queue.modal.scss +31 -0
- package/src/service-queues/admin/queue/modal/create-queue/create-queue.modal.tsx +153 -0
- package/src/service-queues/admin/queue/modal/edit-queue/edit-queue.modal.scss +0 -0
- package/src/service-queues/admin/queue/modal/edit-queue/edit-queue.modal.tsx +182 -0
- package/src/service-queues/admin/queue/queue-list/queue-list.scss +33 -0
- package/src/service-queues/admin/queue/queue-list/queue-list.tsx +113 -0
- package/src/service-queues/admin/queue-entry/queue-entry-list.component.scss +6 -0
- package/src/service-queues/admin/queue-entry/queue-entry-list.component.tsx +137 -0
- package/src/service-queues/admin/queue-room/modal/create-queue-room/create-queue-room.modal.scss +31 -0
- package/src/service-queues/admin/queue-room/modal/create-queue-room/create-queue-room.modal.tsx +137 -0
- package/src/service-queues/admin/queue-room/modal/edit-queue-room/edit-queue-room.modal.scss +0 -0
- package/src/service-queues/admin/queue-room/modal/edit-queue-room/edit-queue-room.modal.tsx +146 -0
- package/src/service-queues/admin/queue-room/queue-room-list/queue-room-list.scss +22 -0
- package/src/service-queues/admin/queue-room/queue-room-list/queue-room-list.tsx +120 -0
- package/src/service-queues/admin/service-queue-admin-dashboard.component.scss +6 -0
- package/src/service-queues/admin/service-queue-admin-dashboard.component.tsx +34 -0
- package/src/service-queues/consultation/consultation.component.tsx +9 -0
- package/src/service-queues/extensions/service-queue-patient-banner/service-queue-patient-banner.scss +30 -0
- package/src/service-queues/extensions/service-queue-patient-banner/service-queue-patient-banner.tsx +108 -0
- package/src/service-queues/modals/daily-report-patient-list/daily-report-patient-list.scss +26 -0
- package/src/service-queues/modals/daily-report-patient-list/daily-report-patient-list.tsx +110 -0
- package/src/service-queues/modals/move/move-patient.component.scss +1 -1
- package/src/service-queues/modals/move/move-patient.component.tsx +31 -17
- package/src/service-queues/queue-list/check-in.service.ts +26 -0
- package/src/service-queues/queue-list/queue-list.component.scss +37 -0
- package/src/service-queues/queue-list/queue-list.component.tsx +256 -56
- package/src/service-queues/service-queue/service-queue.component.tsx +151 -8
- package/src/service-queues/service-queue/stats/stat-details/stat-details.component.scss +5 -0
- package/src/service-queues/service-queue/stats/stat-details/stat-details.component.tsx +36 -7
- package/src/service-queues/service-queue.tsx +22 -16
- package/src/service-queues/service-queues.resource.ts +197 -1
- package/src/service-queues/service.resource.ts +1 -1
- package/src/shared/constants/concepts.ts +50 -0
- package/src/shared/constants/patient-category.ts +6 -0
- package/src/shared/constants/patient-type.ts +10 -0
- package/src/shared/constants/visit-types.ts +7 -0
- package/src/shared/services/billing.resource.ts +75 -0
- package/src/shared/services/claims.resource.ts +47 -0
- package/src/shared/services/client-payment-mode.resource.ts +27 -0
- package/src/shared/services/client-payment.resource.ts +17 -0
- package/src/shared/services/consultation-clearance.resource.ts +210 -0
- package/src/shared/services/eligibility.resource.ts +20 -0
- package/src/shared/services/encounters.resource.ts +33 -0
- package/src/shared/services/location.resource.ts +9 -0
- package/src/shared/types/index.ts +156 -0
- package/src/shared/ui/aggregate-stat-card/aggregate-stat-card.scss +42 -0
- package/src/shared/ui/aggregate-stat-card/aggregate-stat-card.tsx +42 -0
- package/src/shared/ui/confirm-modal/confirm.modal.scss +18 -0
- package/src/shared/ui/confirm-modal/confirm.modal.tsx +38 -0
- package/src/shared/ui/facility-worker-slot/facility-worker.component-slot.component.scss +39 -0
- package/src/shared/ui/facility-worker-slot/facility-worker.component-slot.component.tsx +19 -0
- package/src/shared/ui/otp-input/otp-input.component.scss +14 -0
- package/src/shared/ui/otp-input/otp-input.component.tsx +97 -0
- package/src/{service-queues/service-queue/stats → shared/ui}/stat-card/stat-card.component.tsx +6 -3
- package/src/shared/ui/timer/timer.component.scss +5 -0
- package/src/shared/ui/timer/timer.component.tsx +40 -0
- package/src/shared/utils/get-base-url.ts +15 -0
- package/src/shared/utils/get-tag-type.ts +75 -0
- package/src/side-nav-menu/mnch-nav-link-config.ts +16 -0
- package/src/side-nav-menu/mnch-nav-links.tsx +42 -0
- package/src/side-nav-menu/nav-link-config.ts +5 -5
- package/src/side-nav-menu/specialized-clinics-nav-link-config.ts +36 -0
- package/src/side-nav-menu/specialized-clinics-nav-links.tsx +42 -0
- package/src/specialized-clinics/critital-care-unit/critical-care-unit-queues.component.scss +7 -0
- package/src/specialized-clinics/critital-care-unit/critical-care-unit-queues.component.tsx +36 -0
- package/src/specialized-clinics/critital-care-unit/queues/consultation/critical-care-unit-consultation.tsx +15 -0
- package/src/specialized-clinics/critital-care-unit/queues/triage/critical-care-unit-triage.tsx +15 -0
- package/src/specialized-clinics/dental/dental-queues.component.scss +7 -0
- package/src/specialized-clinics/dental/dental-queues.component.tsx +36 -0
- package/src/specialized-clinics/dental/queues/consultation/dental-consultation.tsx +15 -0
- package/src/specialized-clinics/dental/queues/triage/dental-triage.tsx +15 -0
- package/src/specialized-clinics/diagnostic-and-imaging/diagnostic-and-imaging.component.scss +7 -0
- package/src/specialized-clinics/diagnostic-and-imaging/diagnostic-and-imaging.component.tsx +36 -0
- package/src/specialized-clinics/diagnostic-and-imaging/queues/consultation/diagnosis-and-imaging-consultation.tsx +15 -0
- package/src/specialized-clinics/diagnostic-and-imaging/queues/triage/diagnosis-and-imaging-triage.tsx +15 -0
- package/src/specialized-clinics/oncology/oncology-queues.component.scss +7 -0
- package/src/specialized-clinics/oncology/oncology-queues.component.tsx +36 -0
- package/src/specialized-clinics/oncology/queues/consultation/oncology-consultation.tsx +15 -0
- package/src/specialized-clinics/oncology/queues/triage/oncology-triage.tsx +15 -0
- package/src/specialized-clinics/ophthalmology/ophthalmology-queues.component.scss +7 -0
- package/src/specialized-clinics/ophthalmology/ophthalmology-queues.component.tsx +36 -0
- package/src/specialized-clinics/ophthalmology/queues/consultation/ophthalmology-consultation.tsx +15 -0
- package/src/specialized-clinics/ophthalmology/queues/triage/ophthalmology-triage.tsx +15 -0
- package/src/specialized-clinics/psychiatry/psychiatry-queues.component.scss +7 -0
- package/src/specialized-clinics/psychiatry/psychiatry-queues.component.tsx +36 -0
- package/src/specialized-clinics/psychiatry/queues/consultation/psychiatry-consultation.tsx +15 -0
- package/src/specialized-clinics/psychiatry/queues/triage/psychiatry-triage.tsx +15 -0
- package/src/specialized-clinics/renal/queues/consultation/dialysis-consultation.tsx +15 -0
- package/src/specialized-clinics/renal/queues/triage/renal-triage.tsx +15 -0
- package/src/specialized-clinics/renal/renal-queues.component.scss +7 -0
- package/src/specialized-clinics/renal/renal-queues.component.tsx +36 -0
- package/src/specialized-clinics/specialized-clinics-root.tsx +27 -0
- package/src/triage/triage.component.scss +6 -0
- package/src/triage/triage.component.tsx +4 -0
- package/src/types/types.ts +56 -1
- package/webpack.config.js +54 -1
- package/dist/104.js +0 -2
- package/dist/104.js.map +0 -1
- package/dist/15.js +0 -1
- package/dist/15.js.map +0 -1
- package/dist/184.js +0 -2
- package/dist/184.js.map +0 -1
- package/dist/197.js +0 -1
- package/dist/246.js +0 -2
- package/dist/246.js.map +0 -1
- package/dist/255.js +0 -1
- package/dist/255.js.map +0 -1
- package/dist/300.js +0 -1
- package/dist/335.js +0 -1
- package/dist/339.js +0 -1
- package/dist/339.js.map +0 -1
- package/dist/353.js +0 -1
- package/dist/353.js.map +0 -1
- package/dist/420.js +0 -1
- package/dist/420.js.map +0 -1
- package/dist/540.js +0 -2
- package/dist/540.js.map +0 -1
- package/dist/55.js +0 -1
- package/dist/652.js +0 -1
- package/dist/709.js +0 -1
- package/dist/709.js.map +0 -1
- package/dist/710.js +0 -2
- package/dist/710.js.map +0 -1
- package/dist/729.js +0 -1
- package/dist/729.js.map +0 -1
- package/dist/752.js +0 -1
- package/dist/752.js.map +0 -1
- package/dist/833.js +0 -1
- package/dist/833.js.map +0 -1
- package/dist/907.js +0 -1
- package/dist/907.js.map +0 -1
- package/dist/91.js +0 -1
- package/dist/91.js.map +0 -1
- package/dist/93.js +0 -1
- package/dist/93.js.map +0 -1
- package/dist/943.js +0 -1
- package/dist/943.js.map +0 -1
- package/dist/99.js +0 -1
- /package/dist/{104.js.LICENSE.txt → 2177.js.LICENSE.txt} +0 -0
- /package/dist/{184.js.LICENSE.txt → 3184.js.LICENSE.txt} +0 -0
- /package/dist/{540.js.LICENSE.txt → 6540.js.LICENSE.txt} +0 -0
- /package/dist/{710.js.LICENSE.txt → 7830.js.LICENSE.txt} +0 -0
- /package/dist/{246.js.LICENSE.txt → 9583.js.LICENSE.txt} +0 -0
- /package/src/{service-queues/service-queue/stats → shared/ui}/stat-card/stat-card.component.scss +0 -0
|
@@ -0,0 +1,1840 @@
|
|
|
1
|
+
import useSWR, { mutate as globalMutate } from 'swr';
|
|
2
|
+
import dayjs from 'dayjs';
|
|
3
|
+
import {
|
|
4
|
+
ServiceType,
|
|
5
|
+
type BenefitUtilization,
|
|
6
|
+
type InterventionResults,
|
|
7
|
+
type ClaimResult,
|
|
8
|
+
type Intervention,
|
|
9
|
+
VisitType,
|
|
10
|
+
type ClientSubBenefit,
|
|
11
|
+
PomsfBalance,
|
|
12
|
+
PreExistingIntervention,
|
|
13
|
+
PreauthRequest,
|
|
14
|
+
} from './index';
|
|
15
|
+
import { fetchUrl, getHieBaseUrl, getUrl, useHie } from './utils';
|
|
16
|
+
import { openmrsFetch, restBaseUrl, useConfig, useSession, Visit } from '@openmrs/esm-framework';
|
|
17
|
+
import { Order } from '@openmrs/esm-patient-common-lib';
|
|
18
|
+
import { useProviderClaimPreview } from '../billing/billing-claims.resource';
|
|
19
|
+
import { getAuthorizations } from '../registry/hie.resource';
|
|
20
|
+
|
|
21
|
+
export const useClientSubBenefits = (clientRegistryId: string) => {
|
|
22
|
+
const { hieBaseUrl, locationUuid } = useHie();
|
|
23
|
+
const url = clientRegistryId
|
|
24
|
+
? `${hieBaseUrl}/sub-benefits?patient_id=${clientRegistryId}&locationUuid=${locationUuid}`
|
|
25
|
+
: null;
|
|
26
|
+
|
|
27
|
+
const { data, error, isLoading } = useSWR<{ data: Array<ClientSubBenefit> }>(url, openmrsFetch);
|
|
28
|
+
|
|
29
|
+
const results = data?.data;
|
|
30
|
+
|
|
31
|
+
return {
|
|
32
|
+
clientSubBenefits: results,
|
|
33
|
+
error,
|
|
34
|
+
isLoadingClientSubBenefits: isLoading,
|
|
35
|
+
};
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export const useInterventions = (clientRegistryId: string, subBenefitCode: string) => {
|
|
39
|
+
const { hieBaseUrl, locationUuid } = useHie();
|
|
40
|
+
const url =
|
|
41
|
+
clientRegistryId && subBenefitCode
|
|
42
|
+
? `${hieBaseUrl}/interventions?patient_id=${clientRegistryId}&locationUuid=${locationUuid}&sub_benefit_code=${subBenefitCode}`
|
|
43
|
+
: null;
|
|
44
|
+
|
|
45
|
+
const { data, error, isLoading } = useSWR<{ data: Array<Intervention> }>(url, openmrsFetch);
|
|
46
|
+
|
|
47
|
+
const results = data?.data;
|
|
48
|
+
|
|
49
|
+
return {
|
|
50
|
+
interventions: results,
|
|
51
|
+
error,
|
|
52
|
+
isLoadingInterventions: isLoading,
|
|
53
|
+
};
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Fetch patient sub-benefits (required by hie-saf interventions proxy).
|
|
58
|
+
*/
|
|
59
|
+
export async function fetchClientSubBenefits(patientId: string, locationUuid: string): Promise<ClientSubBenefit[]> {
|
|
60
|
+
if (!patientId || !locationUuid) {
|
|
61
|
+
return [];
|
|
62
|
+
}
|
|
63
|
+
const { hieBaseUrl } = await getHieBaseUrl();
|
|
64
|
+
const params = new URLSearchParams({
|
|
65
|
+
patient_id: patientId,
|
|
66
|
+
locationUuid,
|
|
67
|
+
});
|
|
68
|
+
const response = await openmrsFetch(`${hieBaseUrl}/sub-benefits?${params.toString()}`);
|
|
69
|
+
const data = response?.data;
|
|
70
|
+
if (Array.isArray(data)) {
|
|
71
|
+
return data;
|
|
72
|
+
}
|
|
73
|
+
if (Array.isArray(data?.results)) {
|
|
74
|
+
return data.results;
|
|
75
|
+
}
|
|
76
|
+
return [];
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function unwrapInterventions(data: unknown): Intervention[] {
|
|
80
|
+
if (Array.isArray(data)) {
|
|
81
|
+
return data as Intervention[];
|
|
82
|
+
}
|
|
83
|
+
if (data && typeof data === 'object' && Array.isArray((data as { results?: unknown }).results)) {
|
|
84
|
+
return (data as { results: Intervention[] }).results;
|
|
85
|
+
}
|
|
86
|
+
if (data && typeof data === 'object' && 'code' in (data as object)) {
|
|
87
|
+
return [data as Intervention];
|
|
88
|
+
}
|
|
89
|
+
return [];
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** Parent package prefix from an intervention code, e.g. SHA-19-239 → SHA-19 */
|
|
93
|
+
function interventionPackagePrefix(interventionCode: string): string {
|
|
94
|
+
const parts = interventionCode.trim().toUpperCase().split('-');
|
|
95
|
+
if (parts.length >= 2) {
|
|
96
|
+
return `${parts[0]}-${parts[1]}`;
|
|
97
|
+
}
|
|
98
|
+
return interventionCode.trim().toUpperCase();
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Look up a single SHA intervention by code via interventions coverage.
|
|
103
|
+
* hie-saf requires `sub_benefit_code`; we resolve it from the patient's sub-benefits,
|
|
104
|
+
* preferring packages that match the intervention prefix (SHA-19-239 → SHA-19…).
|
|
105
|
+
* Response includes `needsPreauth` / `needsManualPreauthApproval`.
|
|
106
|
+
* @see https://hie-docs.dha.go.ke/docs/claims/process/preauths/normalPreauths
|
|
107
|
+
*/
|
|
108
|
+
export async function fetchShaInterventionByCode(
|
|
109
|
+
patientId: string,
|
|
110
|
+
locationUuid: string,
|
|
111
|
+
interventionCode: string,
|
|
112
|
+
subBenefitCode?: string,
|
|
113
|
+
): Promise<Intervention | null> {
|
|
114
|
+
if (!patientId || !locationUuid || !interventionCode) {
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
const { hieBaseUrl } = await getHieBaseUrl();
|
|
118
|
+
const code = interventionCode.trim().toUpperCase();
|
|
119
|
+
const prefix = interventionPackagePrefix(code);
|
|
120
|
+
|
|
121
|
+
const tryFetch = async (sbCode: string): Promise<Intervention | null> => {
|
|
122
|
+
if (!sbCode) return null;
|
|
123
|
+
const params = new URLSearchParams({
|
|
124
|
+
patient_id: patientId,
|
|
125
|
+
locationUuid,
|
|
126
|
+
sub_benefit_code: sbCode,
|
|
127
|
+
code: interventionCode.trim(),
|
|
128
|
+
});
|
|
129
|
+
const response = await openmrsFetch(`${hieBaseUrl}/interventions?${params.toString()}`);
|
|
130
|
+
const list = unwrapInterventions(response?.data);
|
|
131
|
+
return list.find((i) => (i?.code ?? '').trim().toUpperCase() === code) ?? null;
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
if (subBenefitCode) {
|
|
135
|
+
const hit = await tryFetch(subBenefitCode);
|
|
136
|
+
if (hit) return hit;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const subBenefits = await fetchClientSubBenefits(patientId, locationUuid);
|
|
140
|
+
if (subBenefits.length === 0) {
|
|
141
|
+
return null;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const ranked = [...subBenefits].sort((a, b) => {
|
|
145
|
+
const score = (s: ClientSubBenefit) => {
|
|
146
|
+
const c = (s.code ?? '').toUpperCase();
|
|
147
|
+
const parent = (s.parentBenefitCode ?? '').toUpperCase();
|
|
148
|
+
if (c === prefix || parent === prefix) return 0;
|
|
149
|
+
if (c.startsWith(`${prefix}-`) || c.startsWith(prefix)) return 1;
|
|
150
|
+
return 2;
|
|
151
|
+
};
|
|
152
|
+
return score(a) - score(b);
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
for (const sb of ranked) {
|
|
156
|
+
try {
|
|
157
|
+
const hit = await tryFetch(sb.code);
|
|
158
|
+
if (hit) return hit;
|
|
159
|
+
} catch {
|
|
160
|
+
// try next sub-benefit
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
return null;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
export const useBenefitUtilizations = (
|
|
167
|
+
clientRegistryId: string,
|
|
168
|
+
interventionCode: string,
|
|
169
|
+
isCapitation: boolean,
|
|
170
|
+
isPomsf: boolean,
|
|
171
|
+
) => {
|
|
172
|
+
const { hieBaseUrl, locationUuid } = useHie();
|
|
173
|
+
const url =
|
|
174
|
+
clientRegistryId && interventionCode && !isCapitation && !isPomsf
|
|
175
|
+
? `${hieBaseUrl}/benefits-utilization?patient_id=${clientRegistryId}&locationUuid=${locationUuid}&intervention_code=${interventionCode}`
|
|
176
|
+
: null;
|
|
177
|
+
|
|
178
|
+
const { data, error, isLoading } = useSWR<{ data: Array<BenefitUtilization> }>(url, openmrsFetch);
|
|
179
|
+
|
|
180
|
+
const results = data?.data;
|
|
181
|
+
|
|
182
|
+
if (results && 'error' in results && 'message' in results) {
|
|
183
|
+
return {
|
|
184
|
+
benefitUtilizations: null,
|
|
185
|
+
error,
|
|
186
|
+
isLoadingClientSubBenefits: isLoading,
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
return {
|
|
191
|
+
benefitUtilizations: results,
|
|
192
|
+
error,
|
|
193
|
+
isLoadingBenefitUtilization: isLoading,
|
|
194
|
+
};
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
export const usePomsfBalance = (clientRegistryId: string, isPomsf: boolean) => {
|
|
198
|
+
const { hieBaseUrl, locationUuid } = useHie();
|
|
199
|
+
const url =
|
|
200
|
+
clientRegistryId && isPomsf
|
|
201
|
+
? `${hieBaseUrl}/pomsf-balance?patient_id=${clientRegistryId}&locationUuid=${locationUuid}`
|
|
202
|
+
: null;
|
|
203
|
+
|
|
204
|
+
const { data, error, isLoading } = useSWR<{ data: PomsfBalance }>(url, openmrsFetch);
|
|
205
|
+
|
|
206
|
+
const results = data?.data;
|
|
207
|
+
|
|
208
|
+
if (results && 'error' in results && 'message' in results) {
|
|
209
|
+
return {
|
|
210
|
+
pomsfBalance: null,
|
|
211
|
+
error,
|
|
212
|
+
isLoadingPomsfBalances: isLoading,
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
return {
|
|
217
|
+
pomsfBalance: results,
|
|
218
|
+
error,
|
|
219
|
+
isLoadingPomsfBalances: isLoading,
|
|
220
|
+
};
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
// Promise-based counterpart to usePomsfBalance, for callers that already run
|
|
224
|
+
// their own useEffect (e.g. components inside this same module, which don't
|
|
225
|
+
// need useHie's useConfig({ externalModuleName }) lookup — that path has been
|
|
226
|
+
// observed to throw React error #310 when invoked from certain call sites).
|
|
227
|
+
export async function fetchPomsfBalance(clientRegistryId: string, locationUuid: string): Promise<PomsfBalance | null> {
|
|
228
|
+
if (!clientRegistryId || !locationUuid) {
|
|
229
|
+
return null;
|
|
230
|
+
}
|
|
231
|
+
const { hieBaseUrl } = await getHieBaseUrl();
|
|
232
|
+
const url = `${hieBaseUrl}/pomsf-balance?patient_id=${clientRegistryId}&locationUuid=${locationUuid}`;
|
|
233
|
+
const response = await openmrsFetch<PomsfBalance>(url);
|
|
234
|
+
const results = response?.data;
|
|
235
|
+
|
|
236
|
+
if (results && 'error' in results && 'message' in results) {
|
|
237
|
+
return null;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
return results ?? null;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
export async function createClaimsVisit(
|
|
244
|
+
interventionCode: string,
|
|
245
|
+
crIdentifier: string,
|
|
246
|
+
serviceType: ServiceType,
|
|
247
|
+
locationUuid: string,
|
|
248
|
+
{ auth_guid, otp }: { auth_guid?: string; otp?: string } = {},
|
|
249
|
+
) {
|
|
250
|
+
const { hieBaseUrl } = await getHieBaseUrl();
|
|
251
|
+
const url = `${hieBaseUrl}/claims-visit`;
|
|
252
|
+
|
|
253
|
+
let payload = {
|
|
254
|
+
locationUuid,
|
|
255
|
+
intervention_codes: [interventionCode],
|
|
256
|
+
patient_id: crIdentifier,
|
|
257
|
+
service_type: serviceType, // Type of service Options: CAPITATION, OUTPATIENT, INPATIENT, EMERGENCY
|
|
258
|
+
};
|
|
259
|
+
|
|
260
|
+
if (otp) {
|
|
261
|
+
payload['otp'] = otp;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
if (auth_guid) {
|
|
265
|
+
payload['auth_guid'] = auth_guid;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
const result = await openmrsFetch<ClaimResult>(url, {
|
|
269
|
+
method: 'POST',
|
|
270
|
+
headers: {
|
|
271
|
+
'Content-Type': 'application/json',
|
|
272
|
+
},
|
|
273
|
+
// signal: abortController.signal,
|
|
274
|
+
body: payload,
|
|
275
|
+
}).catch((error) => {
|
|
276
|
+
const message = error?.responseBody?.message ?? '';
|
|
277
|
+
if (typeof message === 'object') {
|
|
278
|
+
throw `${message?.join(',')}`;
|
|
279
|
+
}
|
|
280
|
+
throw message;
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
if (result?.data && 'error' in result.data && 'message' in result.data) {
|
|
284
|
+
const message = result.data.message ?? '';
|
|
285
|
+
throw message;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
return result.data;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
export const usePreExistingIntervention = (patientUuid: string) => {
|
|
292
|
+
const { hieBaseUrl } = useHie();
|
|
293
|
+
const url = patientUuid ? `${hieBaseUrl}/bill-order/patient-claim-bill-order?patient_uuid=${patientUuid}` : null;
|
|
294
|
+
|
|
295
|
+
const { data, error, isLoading } = useSWR<{ data: PreExistingIntervention[] }>(url, openmrsFetch);
|
|
296
|
+
|
|
297
|
+
const results = data?.data;
|
|
298
|
+
|
|
299
|
+
return {
|
|
300
|
+
preExistingInterventions: results,
|
|
301
|
+
error,
|
|
302
|
+
isLoadingPreExistingIntervention: isLoading,
|
|
303
|
+
};
|
|
304
|
+
};
|
|
305
|
+
|
|
306
|
+
export const useElectivePreauthPreview = (consentToken: string) => {
|
|
307
|
+
const { sessionLocation } = useSession();
|
|
308
|
+
const { hieBaseUrl } = useConfig({
|
|
309
|
+
externalModuleName: '@ampath/esm-dha-workflow-app',
|
|
310
|
+
});
|
|
311
|
+
const url = consentToken
|
|
312
|
+
? `${hieBaseUrl}/pre-auth/preview?locationUuid=${sessionLocation?.uuid}&consentToken=${consentToken}`
|
|
313
|
+
: null;
|
|
314
|
+
|
|
315
|
+
const {
|
|
316
|
+
data,
|
|
317
|
+
error,
|
|
318
|
+
isLoading,
|
|
319
|
+
isValidating,
|
|
320
|
+
mutate: mutated,
|
|
321
|
+
} = useSWR<{
|
|
322
|
+
data: {
|
|
323
|
+
results: Array<{
|
|
324
|
+
authorizationDetails: {
|
|
325
|
+
interventions: Array<{
|
|
326
|
+
subBenefitCode: string;
|
|
327
|
+
code: string;
|
|
328
|
+
}>
|
|
329
|
+
},
|
|
330
|
+
interventionCode: string;
|
|
331
|
+
}>
|
|
332
|
+
}
|
|
333
|
+
}>(url, openmrsFetch, {
|
|
334
|
+
errorRetryCount: 2,
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
const results = data?.data?.results ?? [];
|
|
338
|
+
|
|
339
|
+
return {
|
|
340
|
+
preauthRequests: results,
|
|
341
|
+
error,
|
|
342
|
+
isLoading,
|
|
343
|
+
isValidating,
|
|
344
|
+
mutated,
|
|
345
|
+
};
|
|
346
|
+
};
|
|
347
|
+
|
|
348
|
+
export const useExistingElectiveIntervention = (patientUuid: string, order: Order) => {
|
|
349
|
+
const { hieBaseUrl } = useHie();
|
|
350
|
+
// b594be9a-9673-44f3-9741-b05823d4423c
|
|
351
|
+
const url = patientUuid
|
|
352
|
+
? `${hieBaseUrl}/pre-auth/request?patientUuid=${patientUuid}&electivePreauth=true`
|
|
353
|
+
: null;
|
|
354
|
+
|
|
355
|
+
const { data, error, isLoading: isLoadingElPreauth } = useSWR<{
|
|
356
|
+
data: Array<{
|
|
357
|
+
orderNo: string;
|
|
358
|
+
interventionCode: string;
|
|
359
|
+
consentToken: string;
|
|
360
|
+
status: string;
|
|
361
|
+
}>
|
|
362
|
+
}>(url, openmrsFetch);
|
|
363
|
+
|
|
364
|
+
let results = data?.data;
|
|
365
|
+
|
|
366
|
+
if (order && results && results.length) {
|
|
367
|
+
results = results.filter(result => result.orderNo === order?.orderNumber);
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
let electiveIntervention = results?.[0];
|
|
371
|
+
|
|
372
|
+
const { preauthRequests, isLoading } = useElectivePreauthPreview(electiveIntervention?.consentToken);
|
|
373
|
+
|
|
374
|
+
let subBenefitCode = null;
|
|
375
|
+
|
|
376
|
+
if (!isLoading && preauthRequests && preauthRequests.length) {
|
|
377
|
+
subBenefitCode = preauthRequests?.find(p => p.interventionCode === electiveIntervention.interventionCode)?.authorizationDetails?.interventions?.[0]?.subBenefitCode;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
return {
|
|
381
|
+
interventionCode: electiveIntervention?.interventionCode,
|
|
382
|
+
subBenefitCode,
|
|
383
|
+
error,
|
|
384
|
+
isLoadingElectiveIntervention: isLoading || isLoadingElPreauth,
|
|
385
|
+
};
|
|
386
|
+
};
|
|
387
|
+
|
|
388
|
+
export async function updateBillOrderConsentToken(
|
|
389
|
+
id: number,
|
|
390
|
+
consentToken: string,
|
|
391
|
+
) {
|
|
392
|
+
const { hieBaseUrl } = await getHieBaseUrl();
|
|
393
|
+
const url = `${hieBaseUrl}/bill-order/${id}/consent-token`;
|
|
394
|
+
|
|
395
|
+
let payload = {
|
|
396
|
+
consent_token: consentToken,
|
|
397
|
+
};
|
|
398
|
+
|
|
399
|
+
const result = await openmrsFetch<any>(url, {
|
|
400
|
+
method: 'PATCH',
|
|
401
|
+
headers: {
|
|
402
|
+
'Content-Type': 'application/json',
|
|
403
|
+
},
|
|
404
|
+
body: payload,
|
|
405
|
+
}).catch((error) => {
|
|
406
|
+
const message = error?.responseBody?.message ?? '';
|
|
407
|
+
if (typeof message === 'object') {
|
|
408
|
+
throw `${message?.join(',')}`;
|
|
409
|
+
}
|
|
410
|
+
throw message;
|
|
411
|
+
});
|
|
412
|
+
|
|
413
|
+
return result.data;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
export const usePatientVisit = (patientUuid: string) => {
|
|
417
|
+
console.log('patientUuid');
|
|
418
|
+
console.log(patientUuid);
|
|
419
|
+
console.log('patientUuid');
|
|
420
|
+
const sessionLocation = useSession();
|
|
421
|
+
|
|
422
|
+
const url = patientUuid
|
|
423
|
+
? `${restBaseUrl}/visit?patient=${patientUuid}&${sessionLocation?.sessionLocation?.uuid}&includeInactive=false&fromStartDate=${dayjs().startOf('day').toISOString()}&v=full&limit=1`
|
|
424
|
+
: null;
|
|
425
|
+
|
|
426
|
+
const { data, error, isLoading } = useSWR<{
|
|
427
|
+
data: {
|
|
428
|
+
results: Array<Visit>;
|
|
429
|
+
};
|
|
430
|
+
}>(url, openmrsFetch);
|
|
431
|
+
|
|
432
|
+
return {
|
|
433
|
+
data: data?.data?.results,
|
|
434
|
+
error,
|
|
435
|
+
isLoading,
|
|
436
|
+
};
|
|
437
|
+
};
|
|
438
|
+
|
|
439
|
+
export type PreauthAttachmentMeta = {
|
|
440
|
+
document_title: string;
|
|
441
|
+
document_type: string;
|
|
442
|
+
file_field_name: string;
|
|
443
|
+
};
|
|
444
|
+
|
|
445
|
+
/** HIE multipart file field for attachment at zero-based index. */
|
|
446
|
+
export const preauthAttachmentFieldName = (index: number): string => `attachments_${index}_file_blob`;
|
|
447
|
+
|
|
448
|
+
/** Parse index from attachments_N_file_blob; returns null if invalid. */
|
|
449
|
+
export const parsePreauthAttachmentIndex = (fieldName: string): number | null => {
|
|
450
|
+
const match = /^attachments_(\d+)_file_blob$/.exec(fieldName?.trim() ?? '');
|
|
451
|
+
if (!match) return null;
|
|
452
|
+
return Number(match[1]);
|
|
453
|
+
};
|
|
454
|
+
|
|
455
|
+
/**
|
|
456
|
+
* Normalize attachment metadata + files to contiguous indexes 0..n-1.
|
|
457
|
+
* Ensures attachments[i].file_field_name === attachments_i_file_blob and a matching file exists.
|
|
458
|
+
*/
|
|
459
|
+
export const normalizePreauthAttachments = (
|
|
460
|
+
attachments: PreauthAttachmentMeta[] = [],
|
|
461
|
+
files: Record<string, File | Blob> = {},
|
|
462
|
+
): { attachments: PreauthAttachmentMeta[]; files: Record<string, File | Blob> } => {
|
|
463
|
+
const withFiles = attachments
|
|
464
|
+
.map((meta, index) => {
|
|
465
|
+
const declared = meta.file_field_name || preauthAttachmentFieldName(index);
|
|
466
|
+
const file = files[declared] ?? files[preauthAttachmentFieldName(index)];
|
|
467
|
+
if (!file) return null;
|
|
468
|
+
return { meta, file, preferredIndex: parsePreauthAttachmentIndex(declared) ?? index };
|
|
469
|
+
})
|
|
470
|
+
.filter(Boolean) as Array<{ meta: PreauthAttachmentMeta; file: File | Blob; preferredIndex: number }>;
|
|
471
|
+
|
|
472
|
+
// Stable order: by declared index, then original order
|
|
473
|
+
withFiles.sort((a, b) => a.preferredIndex - b.preferredIndex);
|
|
474
|
+
|
|
475
|
+
const normalizedMeta: PreauthAttachmentMeta[] = [];
|
|
476
|
+
const normalizedFiles: Record<string, File | Blob> = {};
|
|
477
|
+
|
|
478
|
+
withFiles.forEach(({ meta, file }, index) => {
|
|
479
|
+
const field = preauthAttachmentFieldName(index);
|
|
480
|
+
normalizedMeta.push({
|
|
481
|
+
document_title: meta.document_title,
|
|
482
|
+
document_type: meta.document_type,
|
|
483
|
+
file_field_name: field,
|
|
484
|
+
});
|
|
485
|
+
normalizedFiles[field] = file;
|
|
486
|
+
});
|
|
487
|
+
|
|
488
|
+
return { attachments: normalizedMeta, files: normalizedFiles };
|
|
489
|
+
};
|
|
490
|
+
|
|
491
|
+
export type PreauthFormPayload = {
|
|
492
|
+
service_start: string;
|
|
493
|
+
service_end: string;
|
|
494
|
+
items: Array<{ unit_price: string; name?: string; quantity?: string }>;
|
|
495
|
+
diagnoses: Array<{ consent_token: string; icd_code: string }>;
|
|
496
|
+
doctors: Array<{
|
|
497
|
+
identification_number: string;
|
|
498
|
+
identification_type: string;
|
|
499
|
+
regulation_body: string;
|
|
500
|
+
intervention_code: string;
|
|
501
|
+
is_primary: boolean;
|
|
502
|
+
}>;
|
|
503
|
+
attachments: PreauthAttachmentMeta[];
|
|
504
|
+
provider_notification_email: string;
|
|
505
|
+
locationUuid: string;
|
|
506
|
+
/** Binary files keyed by file_field_name (e.g. attachments_0_file_blob) */
|
|
507
|
+
files?: Record<string, File | Blob>;
|
|
508
|
+
// Specialty (Postman)
|
|
509
|
+
clinical_indications?: string;
|
|
510
|
+
carcinoma_staging?: string;
|
|
511
|
+
comorbidity?: string;
|
|
512
|
+
metastases?: string[] | string;
|
|
513
|
+
treatment_setting?: string[] | string;
|
|
514
|
+
number_of_sessions_required?: string | number;
|
|
515
|
+
cost_per_session?: string | number;
|
|
516
|
+
is_co_insured?: string | boolean;
|
|
517
|
+
necessity_of_service?: string;
|
|
518
|
+
lens_prescription?: string;
|
|
519
|
+
lens_amount?: string | number;
|
|
520
|
+
eye_examination_amount?: string | number;
|
|
521
|
+
frame_amount?: string | number;
|
|
522
|
+
new_or_replacement?: string;
|
|
523
|
+
frequency_of_sessions?: string;
|
|
524
|
+
start_date?: string;
|
|
525
|
+
progress_report?: string;
|
|
526
|
+
expected_service_start_date?: string;
|
|
527
|
+
is_condition_related_to_employment?: string | boolean;
|
|
528
|
+
is_condition_related_to_auto_or_other_accident?: string | boolean;
|
|
529
|
+
co_insurance_details?: string;
|
|
530
|
+
chief_complaint?: string;
|
|
531
|
+
vital_signs?: string;
|
|
532
|
+
history_of_present_illness?: string;
|
|
533
|
+
physical_examination?: string;
|
|
534
|
+
investigation_report_details?: string;
|
|
535
|
+
type_of_anaesthesia?: string;
|
|
536
|
+
surgery_date?: string;
|
|
537
|
+
};
|
|
538
|
+
|
|
539
|
+
/** Build multipart for POST {hieBaseUrl}/pre-auth/normal.
|
|
540
|
+
* Matches HIE curl POST /api/v1/preauths (snake_case fields + attachments_N_file_blob).
|
|
541
|
+
* Attachment indexes are normalized to 0..n-1 before send.
|
|
542
|
+
*/
|
|
543
|
+
export const generatePreauthFormData = (
|
|
544
|
+
payload: PreauthFormPayload,
|
|
545
|
+
intervention: Pick<
|
|
546
|
+
Intervention,
|
|
547
|
+
| 'code'
|
|
548
|
+
| 'requiresRadiologyPreauth'
|
|
549
|
+
| 'requiresOncologyPreauth'
|
|
550
|
+
| 'requiresOpticalPreauth'
|
|
551
|
+
| 'requiresRenalPreauth'
|
|
552
|
+
| 'requiresSurgicalPreauth'
|
|
553
|
+
>,
|
|
554
|
+
consentToken: string,
|
|
555
|
+
) => {
|
|
556
|
+
const formData = new FormData();
|
|
557
|
+
const { attachments, files } = normalizePreauthAttachments(payload.attachments, payload.files ?? {});
|
|
558
|
+
|
|
559
|
+
formData.append('consent_token', consentToken);
|
|
560
|
+
formData.append('intervention_code', intervention.code);
|
|
561
|
+
formData.append('service_start', payload.service_start);
|
|
562
|
+
formData.append('service_end', payload.service_end);
|
|
563
|
+
formData.append('items', JSON.stringify(payload.items));
|
|
564
|
+
formData.append('diagnoses', JSON.stringify(payload.diagnoses));
|
|
565
|
+
formData.append('doctors', JSON.stringify(payload.doctors));
|
|
566
|
+
formData.append('attachments', JSON.stringify(attachments));
|
|
567
|
+
formData.append('provider_notification_email', payload.provider_notification_email);
|
|
568
|
+
formData.append('locationUuid', payload.locationUuid);
|
|
569
|
+
|
|
570
|
+
// Append in index order: attachments_0_file_blob, attachments_1_file_blob, …
|
|
571
|
+
attachments.forEach((meta) => {
|
|
572
|
+
const file = files[meta.file_field_name];
|
|
573
|
+
if (file) {
|
|
574
|
+
formData.append(meta.file_field_name, file, (file as File).name ?? meta.file_field_name);
|
|
575
|
+
}
|
|
576
|
+
});
|
|
577
|
+
|
|
578
|
+
if (intervention.requiresRadiologyPreauth && payload.clinical_indications) {
|
|
579
|
+
formData.append('clinical_indications', payload.clinical_indications);
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
/** HIE expects JSON lists for metastases / treatment_setting (e.g. `["LUNG"]`). */
|
|
583
|
+
const asJsonListField = (value: string | string[] | undefined | null): string | null => {
|
|
584
|
+
if (value == null) return null;
|
|
585
|
+
if (Array.isArray(value)) return JSON.stringify(value);
|
|
586
|
+
const trimmed = String(value).trim();
|
|
587
|
+
if (!trimmed) return null;
|
|
588
|
+
if (trimmed.startsWith('[')) return trimmed;
|
|
589
|
+
return JSON.stringify([trimmed]);
|
|
590
|
+
};
|
|
591
|
+
|
|
592
|
+
if (intervention.requiresOncologyPreauth) {
|
|
593
|
+
if (payload.carcinoma_staging) formData.append('carcinoma_staging', payload.carcinoma_staging);
|
|
594
|
+
if (payload.comorbidity) formData.append('comorbidity', payload.comorbidity);
|
|
595
|
+
const metastasesJson = asJsonListField(payload.metastases);
|
|
596
|
+
if (metastasesJson) formData.append('metastases', metastasesJson);
|
|
597
|
+
const treatmentSettingJson = asJsonListField(payload.treatment_setting);
|
|
598
|
+
if (treatmentSettingJson) formData.append('treatment_setting', treatmentSettingJson);
|
|
599
|
+
if (payload.number_of_sessions_required != null) {
|
|
600
|
+
formData.append('number_of_sessions_required', String(payload.number_of_sessions_required));
|
|
601
|
+
}
|
|
602
|
+
if (payload.cost_per_session != null) {
|
|
603
|
+
formData.append('cost_per_session', String(payload.cost_per_session));
|
|
604
|
+
}
|
|
605
|
+
if (payload.is_co_insured != null) {
|
|
606
|
+
formData.append('is_co_insured', String(payload.is_co_insured));
|
|
607
|
+
}
|
|
608
|
+
if (payload.start_date) formData.append('start_date', payload.start_date);
|
|
609
|
+
if (payload.progress_report) formData.append('progress_report', payload.progress_report);
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
if (intervention.requiresOpticalPreauth) {
|
|
613
|
+
if (payload.necessity_of_service) formData.append('necessity_of_service', payload.necessity_of_service);
|
|
614
|
+
if (payload.lens_prescription) formData.append('lens_prescription', payload.lens_prescription);
|
|
615
|
+
if (payload.lens_amount != null) formData.append('lens_amount', String(payload.lens_amount));
|
|
616
|
+
if (payload.eye_examination_amount != null) {
|
|
617
|
+
formData.append('eye_examination_amount', String(payload.eye_examination_amount));
|
|
618
|
+
}
|
|
619
|
+
if (payload.frame_amount != null) formData.append('frame_amount', String(payload.frame_amount));
|
|
620
|
+
if (payload.new_or_replacement) formData.append('new_or_replacement', payload.new_or_replacement);
|
|
621
|
+
if (payload.clinical_indications) formData.append('clinical_indications', payload.clinical_indications);
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
if (intervention.requiresRenalPreauth) {
|
|
625
|
+
if (payload.number_of_sessions_required != null) {
|
|
626
|
+
formData.append('number_of_sessions_required', String(payload.number_of_sessions_required));
|
|
627
|
+
}
|
|
628
|
+
if (payload.cost_per_session != null) {
|
|
629
|
+
formData.append('cost_per_session', String(payload.cost_per_session));
|
|
630
|
+
}
|
|
631
|
+
if (payload.frequency_of_sessions) formData.append('frequency_of_sessions', payload.frequency_of_sessions);
|
|
632
|
+
if (payload.clinical_indications) formData.append('clinical_indications', payload.clinical_indications);
|
|
633
|
+
if (payload.start_date) formData.append('start_date', payload.start_date);
|
|
634
|
+
if (payload.is_co_insured != null) formData.append('is_co_insured', String(payload.is_co_insured));
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
// Surgical clinical fields — send snake_case (HIE / gateway expects these keys)
|
|
638
|
+
const hasSurgicalPayload = Boolean(
|
|
639
|
+
payload.chief_complaint?.trim() ||
|
|
640
|
+
payload.vital_signs?.trim() ||
|
|
641
|
+
payload.history_of_present_illness?.trim() ||
|
|
642
|
+
payload.physical_examination?.trim() ||
|
|
643
|
+
payload.investigation_report_details?.trim() ||
|
|
644
|
+
payload.type_of_anaesthesia?.trim() ||
|
|
645
|
+
payload.surgery_date?.trim(),
|
|
646
|
+
);
|
|
647
|
+
if (intervention.requiresSurgicalPreauth || hasSurgicalPayload) {
|
|
648
|
+
const chiefComplaint = (payload.chief_complaint ?? '').trim();
|
|
649
|
+
const vitalSigns = (payload.vital_signs ?? '').trim();
|
|
650
|
+
const historyOfPresentIllness = (payload.history_of_present_illness ?? '').trim();
|
|
651
|
+
const physicalExamination = (payload.physical_examination ?? '').trim();
|
|
652
|
+
const investigationReportDetails = (payload.investigation_report_details ?? '').trim();
|
|
653
|
+
const typeOfAnaesthesia = (payload.type_of_anaesthesia || 'GENERAL').trim();
|
|
654
|
+
const surgeryDate = (payload.surgery_date ?? '').trim();
|
|
655
|
+
|
|
656
|
+
formData.append('chief_complaint', chiefComplaint);
|
|
657
|
+
formData.append('vital_signs', vitalSigns);
|
|
658
|
+
formData.append('history_of_present_illness', historyOfPresentIllness);
|
|
659
|
+
formData.append('physical_examination', physicalExamination);
|
|
660
|
+
formData.append('investigation_report_details', investigationReportDetails);
|
|
661
|
+
formData.append('type_of_anaesthesia', typeOfAnaesthesia);
|
|
662
|
+
if (surgeryDate) {
|
|
663
|
+
formData.append('surgery_date', surgeryDate);
|
|
664
|
+
}
|
|
665
|
+
if (payload.is_condition_related_to_employment != null) {
|
|
666
|
+
formData.append('is_condition_related_to_employment', String(payload.is_condition_related_to_employment));
|
|
667
|
+
}
|
|
668
|
+
if (payload.is_condition_related_to_auto_or_other_accident != null) {
|
|
669
|
+
formData.append(
|
|
670
|
+
'is_condition_related_to_auto_or_other_accident',
|
|
671
|
+
String(payload.is_condition_related_to_auto_or_other_accident),
|
|
672
|
+
);
|
|
673
|
+
}
|
|
674
|
+
if (payload.is_co_insured != null) {
|
|
675
|
+
formData.append('is_co_insured', String(payload.is_co_insured));
|
|
676
|
+
}
|
|
677
|
+
if (payload.co_insurance_details) {
|
|
678
|
+
formData.append('co_insurance_details', payload.co_insurance_details);
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
if (payload.expected_service_start_date) {
|
|
683
|
+
formData.append('expected_service_start_date', payload.expected_service_start_date);
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
return formData;
|
|
687
|
+
};
|
|
688
|
+
|
|
689
|
+
/**
|
|
690
|
+
* Unwrap nested HIE / hie-saf error payloads into a single user-facing string.
|
|
691
|
+
* Example body:
|
|
692
|
+
* `{ "error": "{\"error\":\"Kindly note …\"}", "message": "could not create the preauthorization", "details": [...] }`
|
|
693
|
+
*/
|
|
694
|
+
export function extractHieErrorMessage(error: unknown, fallback = 'Request failed'): string {
|
|
695
|
+
const tryParse = (value: string): unknown => {
|
|
696
|
+
try {
|
|
697
|
+
return JSON.parse(value);
|
|
698
|
+
} catch {
|
|
699
|
+
return undefined;
|
|
700
|
+
}
|
|
701
|
+
};
|
|
702
|
+
|
|
703
|
+
const unwrap = (value: unknown, depth = 0): string => {
|
|
704
|
+
if (depth > 8 || value == null) return '';
|
|
705
|
+
if (typeof value === 'string') {
|
|
706
|
+
const trimmed = value.trim();
|
|
707
|
+
if (!trimmed) return '';
|
|
708
|
+
if (trimmed.startsWith('{') || trimmed.startsWith('[')) {
|
|
709
|
+
const parsed = tryParse(trimmed);
|
|
710
|
+
if (parsed !== undefined) {
|
|
711
|
+
const nested = unwrap(parsed, depth + 1);
|
|
712
|
+
if (nested) return nested;
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
const prefixed = trimmed.match(
|
|
716
|
+
/^could not create the (?:preauthorization|authorization):\s*(.+)$/i,
|
|
717
|
+
);
|
|
718
|
+
if (prefixed?.[1]) {
|
|
719
|
+
const nested = unwrap(prefixed[1], depth + 1);
|
|
720
|
+
if (nested) return nested;
|
|
721
|
+
}
|
|
722
|
+
return trimmed;
|
|
723
|
+
}
|
|
724
|
+
if (Array.isArray(value)) {
|
|
725
|
+
return value
|
|
726
|
+
.map((item) => unwrap(item, depth + 1))
|
|
727
|
+
.filter(Boolean)
|
|
728
|
+
.join('; ');
|
|
729
|
+
}
|
|
730
|
+
if (typeof value === 'object') {
|
|
731
|
+
const o = value as Record<string, unknown>;
|
|
732
|
+
const candidates = [
|
|
733
|
+
unwrap(o.error, depth + 1),
|
|
734
|
+
unwrap(o.details, depth + 1),
|
|
735
|
+
unwrap(o.message, depth + 1),
|
|
736
|
+
unwrap(o.rawMessage, depth + 1),
|
|
737
|
+
unwrap(o.translatedMessage, depth + 1),
|
|
738
|
+
unwrap(o.responseBody, depth + 1),
|
|
739
|
+
unwrap(o.data, depth + 1),
|
|
740
|
+
].filter(Boolean);
|
|
741
|
+
const kindly = candidates.find((c) => /kindly note/i.test(c));
|
|
742
|
+
if (kindly) return kindly;
|
|
743
|
+
// Prefer the most specific (longest) non-generic line
|
|
744
|
+
const generic = /^(could not create the |failed to |request failed)/i;
|
|
745
|
+
const specific = candidates.filter((c) => !generic.test(c));
|
|
746
|
+
const pool = specific.length ? specific : candidates;
|
|
747
|
+
return pool.sort((a, b) => b.length - a.length)[0] ?? '';
|
|
748
|
+
}
|
|
749
|
+
return String(value);
|
|
750
|
+
};
|
|
751
|
+
|
|
752
|
+
if (typeof error === 'string') {
|
|
753
|
+
return unwrap(error) || fallback;
|
|
754
|
+
}
|
|
755
|
+
const e = error as { message?: unknown; responseBody?: unknown; data?: unknown } | null;
|
|
756
|
+
return (
|
|
757
|
+
unwrap(e?.responseBody) ||
|
|
758
|
+
unwrap(e?.data) ||
|
|
759
|
+
unwrap(e) ||
|
|
760
|
+
unwrap(e?.message) ||
|
|
761
|
+
fallback
|
|
762
|
+
);
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
export async function createPreauth(
|
|
766
|
+
payload: PreauthFormPayload,
|
|
767
|
+
intervention: Pick<
|
|
768
|
+
Intervention,
|
|
769
|
+
| 'code'
|
|
770
|
+
| 'requiresRadiologyPreauth'
|
|
771
|
+
| 'requiresOncologyPreauth'
|
|
772
|
+
| 'requiresOpticalPreauth'
|
|
773
|
+
| 'requiresRenalPreauth'
|
|
774
|
+
| 'requiresSurgicalPreauth'
|
|
775
|
+
>,
|
|
776
|
+
consentToken: string,
|
|
777
|
+
) {
|
|
778
|
+
const { hieBaseUrl } = await getHieBaseUrl();
|
|
779
|
+
const url = `${hieBaseUrl}/pre-auth/normal`;
|
|
780
|
+
const formData = generatePreauthFormData(payload, intervention, consentToken);
|
|
781
|
+
|
|
782
|
+
const result = await openmrsFetch(url, {
|
|
783
|
+
method: 'POST',
|
|
784
|
+
body: formData,
|
|
785
|
+
}).catch((error) => {
|
|
786
|
+
throw extractHieErrorMessage(error, 'Failed to create preauth');
|
|
787
|
+
});
|
|
788
|
+
|
|
789
|
+
if (result?.data && typeof result.data === 'object' && ('error' in result.data || 'message' in result.data)) {
|
|
790
|
+
const body = result.data as { error?: unknown; message?: unknown; details?: unknown };
|
|
791
|
+
if (body.error || (typeof body.message === 'string' && /could not create/i.test(body.message))) {
|
|
792
|
+
throw extractHieErrorMessage(body, 'Failed to create preauth');
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
return result?.data;
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
async function fetchPreauthPreview(consentToken: string, locationUuid: string) {
|
|
800
|
+
const { hieBaseUrl } = await getHieBaseUrl();
|
|
801
|
+
const url = `${hieBaseUrl}/pre-auth/preview?consentToken=${encodeURIComponent(consentToken)}&locationUuid=${encodeURIComponent(locationUuid)}`;
|
|
802
|
+
try {
|
|
803
|
+
const result = await openmrsFetch(url);
|
|
804
|
+
return result?.data ?? null;
|
|
805
|
+
} catch (error: any) {
|
|
806
|
+
const status = error?.response?.status ?? error?.status;
|
|
807
|
+
// No preauth raised yet
|
|
808
|
+
if (status === 404) {
|
|
809
|
+
return null;
|
|
810
|
+
}
|
|
811
|
+
const message = error?.responseBody?.message ?? error?.message ?? 'Failed to get preauth preview';
|
|
812
|
+
if (typeof message === 'object') {
|
|
813
|
+
throw `${(message as string[])?.join?.(',') ?? JSON.stringify(message)}`;
|
|
814
|
+
}
|
|
815
|
+
throw message;
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
/**
|
|
820
|
+
* Previews just asked for, and those still in flight, keyed by consent token and location.
|
|
821
|
+
*
|
|
822
|
+
* One dashboard load asks for the same preview from several places at once: the
|
|
823
|
+
* Needs-raise queue (once per bill row, and rows of the same visit share a consent token),
|
|
824
|
+
* the Status table (once per claim), the SWR hook behind a claim's interventions, and the
|
|
825
|
+
* poller following a raise. Each is a round trip to the HIE, and together they were enough
|
|
826
|
+
* to trip its rate limit — which fails whatever asks next rather than merely slowing it.
|
|
827
|
+
*
|
|
828
|
+
* So callers share one request, and its answer for a few seconds after. That window is
|
|
829
|
+
* deliberately short: this is live status someone is waiting on, so the cache is here to
|
|
830
|
+
* collapse a burst, not to hold an answer. Anything that changes a preauth clears it
|
|
831
|
+
* (`invalidatePreauthPreview`), and the pollers ask straight past it (`force`).
|
|
832
|
+
*/
|
|
833
|
+
const PREAUTH_PREVIEW_TTL_MS = 5_000;
|
|
834
|
+
const preauthPreviewCache = new Map<string, { at: number; preview: unknown }>();
|
|
835
|
+
const preauthPreviewInFlight = new Map<string, Promise<unknown>>();
|
|
836
|
+
const preauthPreviewCacheKey = (consentToken: string, locationUuid: string) => `${locationUuid}|${consentToken}`;
|
|
837
|
+
|
|
838
|
+
/** Drop the burst-cached preview for one claim, so the next read goes to the HIE. */
|
|
839
|
+
function forgetPreauthPreview(consentToken: string, locationUuid: string) {
|
|
840
|
+
preauthPreviewCache.delete(preauthPreviewCacheKey(consentToken, locationUuid));
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
export async function getPreauthPreview(
|
|
844
|
+
consentToken: string,
|
|
845
|
+
locationUuid: string,
|
|
846
|
+
{ force = false }: { force?: boolean } = {},
|
|
847
|
+
) {
|
|
848
|
+
const key = preauthPreviewCacheKey(consentToken, locationUuid);
|
|
849
|
+
if (force) {
|
|
850
|
+
preauthPreviewCache.delete(key);
|
|
851
|
+
} else {
|
|
852
|
+
const cached = preauthPreviewCache.get(key);
|
|
853
|
+
if (cached && Date.now() - cached.at < PREAUTH_PREVIEW_TTL_MS) {
|
|
854
|
+
return cached.preview;
|
|
855
|
+
}
|
|
856
|
+
// Two callers asking at the same moment share one round trip rather than racing.
|
|
857
|
+
const inFlight = preauthPreviewInFlight.get(key);
|
|
858
|
+
if (inFlight) {
|
|
859
|
+
return inFlight;
|
|
860
|
+
}
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
// Failures are not cached: the entry is only written once an answer arrives, so a
|
|
864
|
+
// rate-limited or dropped request doesn't stick around as this claim's status.
|
|
865
|
+
const request = fetchPreauthPreview(consentToken, locationUuid)
|
|
866
|
+
.then((preview) => {
|
|
867
|
+
preauthPreviewCache.set(key, { at: Date.now(), preview });
|
|
868
|
+
return preview;
|
|
869
|
+
})
|
|
870
|
+
.finally(() => {
|
|
871
|
+
if (preauthPreviewInFlight.get(key) === request) {
|
|
872
|
+
preauthPreviewInFlight.delete(key);
|
|
873
|
+
}
|
|
874
|
+
});
|
|
875
|
+
preauthPreviewInFlight.set(key, request);
|
|
876
|
+
return request;
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
/** Unwrap HIE preview: bare object, array, or paginated `{ results: [...] }`. */
|
|
880
|
+
export function unwrapPreauthPreviewItem(preview: unknown): Record<string, unknown> | null {
|
|
881
|
+
const items = unwrapPreauthPreviewItems(preview);
|
|
882
|
+
return items[0] ?? null;
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
/** All preauth rows from a preview response (paginated or single). */
|
|
886
|
+
export function unwrapPreauthPreviewItems(preview: unknown): Record<string, unknown>[] {
|
|
887
|
+
if (!preview) {
|
|
888
|
+
return [];
|
|
889
|
+
}
|
|
890
|
+
if (Array.isArray(preview)) {
|
|
891
|
+
return preview.filter((x): x is Record<string, unknown> => !!x && typeof x === 'object');
|
|
892
|
+
}
|
|
893
|
+
if (typeof preview !== 'object') {
|
|
894
|
+
return [];
|
|
895
|
+
}
|
|
896
|
+
const p = preview as Record<string, unknown>;
|
|
897
|
+
if (Array.isArray(p.results)) {
|
|
898
|
+
return p.results.filter((x): x is Record<string, unknown> => !!x && typeof x === 'object');
|
|
899
|
+
}
|
|
900
|
+
return [p];
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
const AWAITING_DOCTOR_STATUSES = new Set(['PENDING_DOCTOR_APPROVAL', 'DOCTOR_REQUEST_SENT', 'DOCTOR_REQUEST_FAILED']);
|
|
904
|
+
|
|
905
|
+
export function isAwaitingDoctorApproval(statusOrItem: string | Record<string, unknown> | null | undefined): boolean {
|
|
906
|
+
if (!statusOrItem) return false;
|
|
907
|
+
if (typeof statusOrItem === 'string') {
|
|
908
|
+
return AWAITING_DOCTOR_STATUSES.has(statusOrItem.toUpperCase());
|
|
909
|
+
}
|
|
910
|
+
const status = String(statusOrItem.status ?? statusOrItem.preauth_status ?? '').toUpperCase();
|
|
911
|
+
if (AWAITING_DOCTOR_STATUSES.has(status)) return true;
|
|
912
|
+
const needs = statusOrItem.needsDoctorApproval === true || statusOrItem.needs_doctor_approval === true;
|
|
913
|
+
const approved = statusOrItem.doctorApproved === true || statusOrItem.doctor_approved === true;
|
|
914
|
+
return needs && !approved && status !== 'FINALISED' && status !== 'FINALIZED';
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
export type PreauthPreviewRow = {
|
|
918
|
+
id: string;
|
|
919
|
+
consentToken: string;
|
|
920
|
+
status: string;
|
|
921
|
+
preauthType: string;
|
|
922
|
+
interventionCode: string;
|
|
923
|
+
interventionName: string;
|
|
924
|
+
memberName: string;
|
|
925
|
+
memberIdentifier: string;
|
|
926
|
+
preauthToken: string;
|
|
927
|
+
doctorName: string;
|
|
928
|
+
practitionerRegistrationNumber: string;
|
|
929
|
+
needsDoctorApproval: boolean;
|
|
930
|
+
doctorApproved: boolean;
|
|
931
|
+
serviceStart: string;
|
|
932
|
+
/** Payer / provider notes (e.g. clarification after automatic checks). */
|
|
933
|
+
notes: string;
|
|
934
|
+
raw: Record<string, unknown>;
|
|
935
|
+
};
|
|
936
|
+
|
|
937
|
+
/** True when payer asked for clarification / response on an already-raised preauth. */
|
|
938
|
+
export function isPreauthNeedsClarification(status: string): boolean {
|
|
939
|
+
const s = (status || '').toUpperCase();
|
|
940
|
+
return s === 'CLARIFICATION_AFTER_AUTOMATIC_CHECKS' || s === 'PENDING_CLARIFICATION' || s.includes('CLARIFICATION');
|
|
941
|
+
}
|
|
942
|
+
|
|
943
|
+
/** Collect note text from preview row `preauthNotes` / item `responseNote`. */
|
|
944
|
+
export function extractPreauthNotesFromItem(item: Record<string, unknown> | null | undefined): string {
|
|
945
|
+
if (!item) return '';
|
|
946
|
+
const notes = item.preauthNotes ?? item.preauth_notes;
|
|
947
|
+
if (Array.isArray(notes)) {
|
|
948
|
+
const texts = notes
|
|
949
|
+
.map((n) => {
|
|
950
|
+
if (!n || typeof n !== 'object') return '';
|
|
951
|
+
return String((n as Record<string, unknown>).note ?? '').trim();
|
|
952
|
+
})
|
|
953
|
+
.filter(Boolean);
|
|
954
|
+
if (texts.length) return texts.join('\n');
|
|
955
|
+
}
|
|
956
|
+
const items = item.preauthItems ?? item.preauth_items;
|
|
957
|
+
if (Array.isArray(items)) {
|
|
958
|
+
const texts = items
|
|
959
|
+
.map((n) => {
|
|
960
|
+
if (!n || typeof n !== 'object') return '';
|
|
961
|
+
const row = n as Record<string, unknown>;
|
|
962
|
+
return String(row.responseNote ?? row.response_note ?? '').trim();
|
|
963
|
+
})
|
|
964
|
+
.filter(Boolean);
|
|
965
|
+
if (texts.length) return texts.join('\n');
|
|
966
|
+
}
|
|
967
|
+
return '';
|
|
968
|
+
}
|
|
969
|
+
|
|
970
|
+
function firstDoctorProfile(item: Record<string, unknown>): Record<string, unknown> | null {
|
|
971
|
+
const doctors = item.preauthDoctors ?? item.preauth_doctors;
|
|
972
|
+
if (!Array.isArray(doctors) || doctors.length === 0) return null;
|
|
973
|
+
const first = doctors[0] as Record<string, unknown>;
|
|
974
|
+
const profile = (first.doctorProfile ?? first.doctor_profile ?? first) as Record<string, unknown>;
|
|
975
|
+
return profile && typeof profile === 'object' ? profile : null;
|
|
976
|
+
}
|
|
977
|
+
|
|
978
|
+
export function normalizePreauthPreviewItem(item: Record<string, unknown>, consentToken: string): PreauthPreviewRow {
|
|
979
|
+
const interventionData = (item.interventionData ?? item.intervention_data ?? {}) as Record<string, unknown>;
|
|
980
|
+
const profile = firstDoctorProfile(item);
|
|
981
|
+
const interventionCode = String(
|
|
982
|
+
item.interventionCode ?? item.intervention_code ?? interventionData.code ?? '',
|
|
983
|
+
).trim();
|
|
984
|
+
const interventionName = String(interventionData.name ?? item.interventionName ?? interventionCode).trim();
|
|
985
|
+
const status = String(item.status ?? item.preauth_status ?? '').toUpperCase();
|
|
986
|
+
const preauthType = String(
|
|
987
|
+
item.preauthType ??
|
|
988
|
+
item.preauth_type ??
|
|
989
|
+
(item.isOncology || item.is_oncology
|
|
990
|
+
? 'ONCOLOGY'
|
|
991
|
+
: item.isSurgical || item.is_surgical
|
|
992
|
+
? 'SURGICAL'
|
|
993
|
+
: item.isRenal || item.is_renal
|
|
994
|
+
? 'RENAL'
|
|
995
|
+
: item.isRadiology || item.is_radiology
|
|
996
|
+
? 'RADIOLOGY'
|
|
997
|
+
: item.isOptical || item.is_optical
|
|
998
|
+
? 'OPTICAL'
|
|
999
|
+
: 'NORMAL'),
|
|
1000
|
+
).toUpperCase();
|
|
1001
|
+
const id = String(item.guid ?? item.id ?? `${consentToken}-${interventionCode}-${status}`);
|
|
1002
|
+
const practitionerRegistrationNumber = String(
|
|
1003
|
+
profile?.practitionerRegistrationNumber ??
|
|
1004
|
+
profile?.practitioner_registration_number ??
|
|
1005
|
+
profile?.nationalIdentifier ??
|
|
1006
|
+
'',
|
|
1007
|
+
).trim();
|
|
1008
|
+
|
|
1009
|
+
return {
|
|
1010
|
+
id,
|
|
1011
|
+
consentToken,
|
|
1012
|
+
status,
|
|
1013
|
+
preauthType,
|
|
1014
|
+
interventionCode,
|
|
1015
|
+
interventionName,
|
|
1016
|
+
memberName: String(item.memberName ?? item.member_name ?? '').trim(),
|
|
1017
|
+
memberIdentifier: String(item.memberIdentifier ?? item.member_identifier ?? '').trim(),
|
|
1018
|
+
preauthToken: String(item.token ?? item.preauth_code ?? item.preauthCode ?? '').trim(),
|
|
1019
|
+
doctorName: String(profile?.name ?? '').trim(),
|
|
1020
|
+
practitionerRegistrationNumber,
|
|
1021
|
+
needsDoctorApproval: item.needsDoctorApproval === true || item.needs_doctor_approval === true,
|
|
1022
|
+
doctorApproved: item.doctorApproved === true || item.doctor_approved === true,
|
|
1023
|
+
serviceStart: String(item.serviceStart ?? item.service_start ?? '').trim(),
|
|
1024
|
+
notes: extractPreauthNotesFromItem(item),
|
|
1025
|
+
raw: item,
|
|
1026
|
+
};
|
|
1027
|
+
}
|
|
1028
|
+
|
|
1029
|
+
export async function resendPreauthDoctorConsent(params: {
|
|
1030
|
+
practitionerRegistrationNumber: string;
|
|
1031
|
+
consentToken: string;
|
|
1032
|
+
interventionCode: string;
|
|
1033
|
+
locationUuid: string;
|
|
1034
|
+
}) {
|
|
1035
|
+
const { hieBaseUrl } = await getHieBaseUrl();
|
|
1036
|
+
const url = `${hieBaseUrl}/pre-auth/doctor-consent`;
|
|
1037
|
+
const body = {
|
|
1038
|
+
practitioner_registration_number: params.practitionerRegistrationNumber,
|
|
1039
|
+
request_type: 'PREAUTH_DOCTOR_APPROVAL_REQUEST' as const,
|
|
1040
|
+
consent_token: params.consentToken,
|
|
1041
|
+
intervention_code: params.interventionCode,
|
|
1042
|
+
locationUuid: params.locationUuid,
|
|
1043
|
+
};
|
|
1044
|
+
try {
|
|
1045
|
+
const result = await openmrsFetch(url, {
|
|
1046
|
+
method: 'POST',
|
|
1047
|
+
headers: { 'Content-Type': 'application/json' },
|
|
1048
|
+
body: JSON.stringify(body),
|
|
1049
|
+
});
|
|
1050
|
+
return result?.data;
|
|
1051
|
+
} catch (error: any) {
|
|
1052
|
+
const message = error?.responseBody?.message ?? error?.message ?? 'Failed to resend doctor consent';
|
|
1053
|
+
if (typeof message === 'object') {
|
|
1054
|
+
throw `${(message as string[])?.join?.(',') ?? JSON.stringify(message)}`;
|
|
1055
|
+
}
|
|
1056
|
+
throw message;
|
|
1057
|
+
}
|
|
1058
|
+
}
|
|
1059
|
+
|
|
1060
|
+
/**
|
|
1061
|
+
* Cancel an existing HIE preauth (POST /api/v1/preauths/cancel via hie-saf).
|
|
1062
|
+
* @see https://hie-docs.dha.go.ke/docs/claims/process/preauths/cancelPreauth
|
|
1063
|
+
*/
|
|
1064
|
+
export async function cancelPreauth(params: {
|
|
1065
|
+
consentToken: string;
|
|
1066
|
+
interventionCode: string;
|
|
1067
|
+
locationUuid: string;
|
|
1068
|
+
}) {
|
|
1069
|
+
const consentToken = String(params.consentToken ?? '').trim();
|
|
1070
|
+
const interventionCode = String(params.interventionCode ?? '').trim();
|
|
1071
|
+
const locationUuid = String(params.locationUuid ?? '').trim();
|
|
1072
|
+
if (!consentToken || !interventionCode || !locationUuid) {
|
|
1073
|
+
throw new Error('Missing consent token, intervention code, or location to cancel preauth');
|
|
1074
|
+
}
|
|
1075
|
+
|
|
1076
|
+
const { hieBaseUrl } = await getHieBaseUrl();
|
|
1077
|
+
const url = `${hieBaseUrl}/pre-auth/request/cancel`;
|
|
1078
|
+
try {
|
|
1079
|
+
const result = await openmrsFetch(url, {
|
|
1080
|
+
method: 'POST',
|
|
1081
|
+
headers: { 'Content-Type': 'application/json' },
|
|
1082
|
+
body: JSON.stringify({
|
|
1083
|
+
consentToken,
|
|
1084
|
+
interventionCode,
|
|
1085
|
+
locationUuid,
|
|
1086
|
+
}),
|
|
1087
|
+
});
|
|
1088
|
+
return result?.data;
|
|
1089
|
+
} catch (error: any) {
|
|
1090
|
+
throw extractHieErrorMessage(error, 'Failed to cancel preauth');
|
|
1091
|
+
}
|
|
1092
|
+
}
|
|
1093
|
+
|
|
1094
|
+
/**
|
|
1095
|
+
* Load all preauth preview rows for a set of claim-visit consent tokens.
|
|
1096
|
+
* Tokens without a preauth (404 / empty) are skipped.
|
|
1097
|
+
*/
|
|
1098
|
+
export async function fetchPreauthPreviewRowsForTokens(
|
|
1099
|
+
consentTokens: string[],
|
|
1100
|
+
locationUuid: string,
|
|
1101
|
+
{ force = false }: { force?: boolean } = {},
|
|
1102
|
+
): Promise<PreauthPreviewRow[]> {
|
|
1103
|
+
const unique = [...new Set(consentTokens.map((t) => t.trim()).filter(Boolean))];
|
|
1104
|
+
const rows: PreauthPreviewRow[] = [];
|
|
1105
|
+
|
|
1106
|
+
const CONCURRENCY = 4;
|
|
1107
|
+
for (let i = 0; i < unique.length; i += CONCURRENCY) {
|
|
1108
|
+
const chunk = unique.slice(i, i + CONCURRENCY);
|
|
1109
|
+
const settled = await Promise.all(
|
|
1110
|
+
chunk.map(async (token) => {
|
|
1111
|
+
try {
|
|
1112
|
+
const preview = await getPreauthPreview(token, locationUuid, { force });
|
|
1113
|
+
return unwrapPreauthPreviewItems(preview).map((item) => normalizePreauthPreviewItem(item, token));
|
|
1114
|
+
} catch {
|
|
1115
|
+
return [] as PreauthPreviewRow[];
|
|
1116
|
+
}
|
|
1117
|
+
}),
|
|
1118
|
+
);
|
|
1119
|
+
for (const batch of settled) {
|
|
1120
|
+
rows.push(...batch);
|
|
1121
|
+
}
|
|
1122
|
+
}
|
|
1123
|
+
|
|
1124
|
+
return rows;
|
|
1125
|
+
}
|
|
1126
|
+
|
|
1127
|
+
const PREAUTH_TERMINAL_FAILURE = new Set([
|
|
1128
|
+
'REJECTED',
|
|
1129
|
+
'CANCELLED',
|
|
1130
|
+
'CANCELED',
|
|
1131
|
+
'FAILED',
|
|
1132
|
+
'DECLINED',
|
|
1133
|
+
// Expired approvals cannot be reused — provider raises a fresh preauth.
|
|
1134
|
+
'EXPIRED',
|
|
1135
|
+
]);
|
|
1136
|
+
|
|
1137
|
+
const PREAUTH_SUBMITTED = new Set(['ACTIVE', 'PENDING_DOCTOR_APPROVAL', 'FINALISED', 'FINALIZED']);
|
|
1138
|
+
|
|
1139
|
+
function interventionCodeFromPreviewItem(item: Record<string, unknown>): string {
|
|
1140
|
+
const interventionData = (item.interventionData ?? item.intervention_data ?? {}) as Record<string, unknown>;
|
|
1141
|
+
return String(item.interventionCode ?? item.intervention_code ?? interventionData.code ?? '').trim();
|
|
1142
|
+
}
|
|
1143
|
+
|
|
1144
|
+
function statusFromPreviewItem(item: Record<string, unknown>): string {
|
|
1145
|
+
const auth = item.authorizationDetails as Record<string, unknown> | undefined;
|
|
1146
|
+
// Prefer the row's own status — overallPreauthFinalised is visit-wide and misleading
|
|
1147
|
+
// when matching a single intervention.
|
|
1148
|
+
const status = String(item.status ?? item.preauth_status ?? '').toUpperCase();
|
|
1149
|
+
const doctorReview = String(item.doctorReviewStatus ?? item.doctor_review_status ?? '').toUpperCase();
|
|
1150
|
+
|
|
1151
|
+
if (doctorReview && PREAUTH_TERMINAL_FAILURE.has(doctorReview) && status !== 'FINALISED' && status !== 'FINALIZED') {
|
|
1152
|
+
return doctorReview;
|
|
1153
|
+
}
|
|
1154
|
+
|
|
1155
|
+
if (!status && (auth?.overallPreauthFinalised === true || auth?.overall_preauth_finalised === true)) {
|
|
1156
|
+
return 'FINALISED';
|
|
1157
|
+
}
|
|
1158
|
+
|
|
1159
|
+
return status;
|
|
1160
|
+
}
|
|
1161
|
+
|
|
1162
|
+
/** Find the preview row for a specific intervention code (paginated `results[]`). */
|
|
1163
|
+
export function findPreauthPreviewForIntervention(
|
|
1164
|
+
preview: unknown,
|
|
1165
|
+
interventionCode: string,
|
|
1166
|
+
): Record<string, unknown> | null {
|
|
1167
|
+
const code = (interventionCode ?? '').trim();
|
|
1168
|
+
if (!code) return null;
|
|
1169
|
+
const items = unwrapPreauthPreviewItems(preview);
|
|
1170
|
+
const direct = items.find((item) => interventionCodeFromPreviewItem(item) === code) ?? null;
|
|
1171
|
+
if (direct) return direct;
|
|
1172
|
+
|
|
1173
|
+
// Some payloads only stamp the code on nested preauthItems[].
|
|
1174
|
+
return (
|
|
1175
|
+
items.find((item) => {
|
|
1176
|
+
const nested = item.preauthItems ?? item.preauth_items;
|
|
1177
|
+
if (!Array.isArray(nested)) return false;
|
|
1178
|
+
return nested.some((n) => {
|
|
1179
|
+
if (!n || typeof n !== 'object') return false;
|
|
1180
|
+
const row = n as Record<string, unknown>;
|
|
1181
|
+
return String(row.interventionCode ?? row.intervention_code ?? '').trim() === code;
|
|
1182
|
+
});
|
|
1183
|
+
}) ?? null
|
|
1184
|
+
);
|
|
1185
|
+
}
|
|
1186
|
+
|
|
1187
|
+
export function extractPreauthStatusForIntervention(preview: unknown, interventionCode: string): string {
|
|
1188
|
+
const item = findPreauthPreviewForIntervention(preview, interventionCode);
|
|
1189
|
+
if (!item) return '';
|
|
1190
|
+
return statusFromPreviewItem(item);
|
|
1191
|
+
}
|
|
1192
|
+
|
|
1193
|
+
export function extractPreauthCodeForIntervention(preview: unknown, interventionCode: string): string | undefined {
|
|
1194
|
+
const item = findPreauthPreviewForIntervention(preview, interventionCode);
|
|
1195
|
+
if (!item) return undefined;
|
|
1196
|
+
return extractPreauthCode(item);
|
|
1197
|
+
}
|
|
1198
|
+
|
|
1199
|
+
/**
|
|
1200
|
+
* True when preview already has a non-failed, non-clarification preauth for this
|
|
1201
|
+
* intervention — Raise should be hidden. Terminal failure and clarification allow
|
|
1202
|
+
* reopening the form to resubmit.
|
|
1203
|
+
*/
|
|
1204
|
+
export function interventionHasBlockingPreauth(preview: unknown, interventionCode: string): boolean {
|
|
1205
|
+
const item = findPreauthPreviewForIntervention(preview, interventionCode);
|
|
1206
|
+
if (!item) return false;
|
|
1207
|
+
const status = statusFromPreviewItem(item);
|
|
1208
|
+
if (!status) return true; // row exists but status blank — treat as already raised
|
|
1209
|
+
return !isPreauthResubmittable(status);
|
|
1210
|
+
}
|
|
1211
|
+
|
|
1212
|
+
/**
|
|
1213
|
+
* True when this intervention's preauth failed or needs clarification —
|
|
1214
|
+
* UI can offer Resubmit and reopen the preauth form.
|
|
1215
|
+
*/
|
|
1216
|
+
export function interventionHasFailedPreauth(preview: unknown, interventionCode: string): boolean {
|
|
1217
|
+
const status = extractPreauthStatusForIntervention(preview, interventionCode);
|
|
1218
|
+
return Boolean(status) && isPreauthResubmittable(status);
|
|
1219
|
+
}
|
|
1220
|
+
|
|
1221
|
+
/** Alias for clarity at call sites that mean "resubmit", not only hard failure. */
|
|
1222
|
+
export function interventionAllowsPreauthResubmit(preview: unknown, interventionCode: string): boolean {
|
|
1223
|
+
return interventionHasFailedPreauth(preview, interventionCode);
|
|
1224
|
+
}
|
|
1225
|
+
|
|
1226
|
+
export type ExistingPreauthMatch = {
|
|
1227
|
+
status: string;
|
|
1228
|
+
preauthCode?: string;
|
|
1229
|
+
consentToken?: string;
|
|
1230
|
+
source: 'hie_preview' | 'local_hold';
|
|
1231
|
+
/** True when Raise/create should be blocked (active / pending / finalised). */
|
|
1232
|
+
blocking: boolean;
|
|
1233
|
+
/** Beneficiary CR from the matched authorization / preview, when known. */
|
|
1234
|
+
beneficiaryCode?: string;
|
|
1235
|
+
};
|
|
1236
|
+
|
|
1237
|
+
/** Normalize CR / beneficiary codes for equality (trim + upper). */
|
|
1238
|
+
function normalizeBeneficiaryCr(value: unknown): string {
|
|
1239
|
+
return String(value ?? '')
|
|
1240
|
+
.trim()
|
|
1241
|
+
.toUpperCase()
|
|
1242
|
+
.replace(/\s+/g, '');
|
|
1243
|
+
}
|
|
1244
|
+
|
|
1245
|
+
/**
|
|
1246
|
+
* Pull beneficiary CR from a preauth preview row (authorizationDetails / beneficiaryDetails).
|
|
1247
|
+
* Prefers CR-style codes (start with CR) over national ID / memberIdentifier.
|
|
1248
|
+
*/
|
|
1249
|
+
export function extractBeneficiaryCrFromPreviewItem(item: Record<string, unknown> | null | undefined): string {
|
|
1250
|
+
if (!item) return '';
|
|
1251
|
+
const auth = (item.authorizationDetails ?? item.authorization_details ?? {}) as Record<string, unknown>;
|
|
1252
|
+
const beneficiary = (item.beneficiaryDetails ?? item.beneficiary_details ?? {}) as Record<string, unknown>;
|
|
1253
|
+
const eligibility = (auth.eligibilityDetails ?? auth.eligibility_details ?? {}) as Record<string, unknown>;
|
|
1254
|
+
const member = (eligibility.member ?? {}) as Record<string, unknown>;
|
|
1255
|
+
|
|
1256
|
+
const candidates = [
|
|
1257
|
+
auth.beneficiaryCode,
|
|
1258
|
+
auth.beneficiary_code,
|
|
1259
|
+
beneficiary.beneficiaryCode,
|
|
1260
|
+
beneficiary.beneficiary_code,
|
|
1261
|
+
member.beneficiaryCode,
|
|
1262
|
+
member.beneficiary_code,
|
|
1263
|
+
];
|
|
1264
|
+
|
|
1265
|
+
for (const c of candidates) {
|
|
1266
|
+
const v = String(c ?? '').trim();
|
|
1267
|
+
if (v) return v;
|
|
1268
|
+
}
|
|
1269
|
+
|
|
1270
|
+
// authCode is often `{CR}-{token}` e.g. CR1900367291321-5-2BEGP65AXH
|
|
1271
|
+
const authCode = String(auth.authCode ?? auth.auth_code ?? '').trim();
|
|
1272
|
+
if (/^CR/i.test(authCode)) {
|
|
1273
|
+
const parts = authCode.split('-');
|
|
1274
|
+
if (parts.length >= 3) {
|
|
1275
|
+
const last = parts[parts.length - 1] ?? '';
|
|
1276
|
+
// Drop trailing authorization token segment when present
|
|
1277
|
+
if (/^[A-Z0-9]{6,}$/i.test(last)) {
|
|
1278
|
+
return parts.slice(0, -1).join('-');
|
|
1279
|
+
}
|
|
1280
|
+
}
|
|
1281
|
+
return authCode;
|
|
1282
|
+
}
|
|
1283
|
+
|
|
1284
|
+
return '';
|
|
1285
|
+
}
|
|
1286
|
+
|
|
1287
|
+
function previewBeneficiaryMatchesCr(
|
|
1288
|
+
preview: unknown,
|
|
1289
|
+
interventionCode: string,
|
|
1290
|
+
beneficiaryCr: string,
|
|
1291
|
+
): boolean {
|
|
1292
|
+
const expected = normalizeBeneficiaryCr(beneficiaryCr);
|
|
1293
|
+
if (!expected) return true; // no CR to enforce
|
|
1294
|
+
const item = findPreauthPreviewForIntervention(preview, interventionCode);
|
|
1295
|
+
if (!item) return false;
|
|
1296
|
+
const found = normalizeBeneficiaryCr(extractBeneficiaryCrFromPreviewItem(item));
|
|
1297
|
+
// If HIE omitted beneficiary fields, do not treat as a match for another patient —
|
|
1298
|
+
// require an explicit CR when we were given one to enforce.
|
|
1299
|
+
if (!found) return false;
|
|
1300
|
+
return found === expected;
|
|
1301
|
+
}
|
|
1302
|
+
|
|
1303
|
+
function authorizationMatchesBeneficiaryCr(auth: { beneficiaryCode?: string }, beneficiaryCr: string): boolean {
|
|
1304
|
+
const expected = normalizeBeneficiaryCr(beneficiaryCr);
|
|
1305
|
+
if (!expected) return true;
|
|
1306
|
+
const found = normalizeBeneficiaryCr(auth?.beneficiaryCode);
|
|
1307
|
+
// Keep tokens missing beneficiaryCode; preview CR check is the hard gate.
|
|
1308
|
+
if (!found) return true;
|
|
1309
|
+
return found === expected;
|
|
1310
|
+
}
|
|
1311
|
+
|
|
1312
|
+
function matchFromPreview(
|
|
1313
|
+
preview: unknown,
|
|
1314
|
+
interventionCode: string,
|
|
1315
|
+
consentToken: string,
|
|
1316
|
+
source: ExistingPreauthMatch['source'] = 'hie_preview',
|
|
1317
|
+
beneficiaryCr?: string | null,
|
|
1318
|
+
): ExistingPreauthMatch | null {
|
|
1319
|
+
const item = findPreauthPreviewForIntervention(preview, interventionCode);
|
|
1320
|
+
if (!item) return null;
|
|
1321
|
+
if (beneficiaryCr && !previewBeneficiaryMatchesCr(preview, interventionCode, beneficiaryCr)) {
|
|
1322
|
+
return null;
|
|
1323
|
+
}
|
|
1324
|
+
const status = extractPreauthStatusForIntervention(preview, interventionCode) || '';
|
|
1325
|
+
const preauthCode = extractPreauthCodeForIntervention(preview, interventionCode);
|
|
1326
|
+
const blocking = interventionHasBlockingPreauth(preview, interventionCode);
|
|
1327
|
+
const beneficiaryCode = extractBeneficiaryCrFromPreviewItem(item) || undefined;
|
|
1328
|
+
return {
|
|
1329
|
+
status: status || 'UNKNOWN',
|
|
1330
|
+
preauthCode,
|
|
1331
|
+
consentToken,
|
|
1332
|
+
source,
|
|
1333
|
+
blocking,
|
|
1334
|
+
beneficiaryCode,
|
|
1335
|
+
};
|
|
1336
|
+
}
|
|
1337
|
+
|
|
1338
|
+
/** TEMP — set false to restore Raise/Submit duplicate preauth blocking. */
|
|
1339
|
+
const DISABLE_DUPLICATE_PREAUTH_CHECK = true;
|
|
1340
|
+
|
|
1341
|
+
/**
|
|
1342
|
+
* Look up an existing HIE (or local hold) preauth for the same beneficiary CR + SHA intervention code.
|
|
1343
|
+
* Used before Raise/Submit to avoid duplicate active preauths.
|
|
1344
|
+
*
|
|
1345
|
+
* Preview / authorization beneficiaryCode must match `beneficiaryCr` when provided —
|
|
1346
|
+
* SHA intervention code alone is not enough (tokens can resolve rows for another member).
|
|
1347
|
+
*/
|
|
1348
|
+
export async function findExistingPreauthForCrAndShaCode(opts: {
|
|
1349
|
+
beneficiaryCr: string;
|
|
1350
|
+
interventionCode: string;
|
|
1351
|
+
locationUuid: string;
|
|
1352
|
+
consentToken?: string | null;
|
|
1353
|
+
patientUuid?: string | null;
|
|
1354
|
+
}): Promise<ExistingPreauthMatch | null> {
|
|
1355
|
+
if (DISABLE_DUPLICATE_PREAUTH_CHECK) {
|
|
1356
|
+
return null;
|
|
1357
|
+
}
|
|
1358
|
+
|
|
1359
|
+
const cr = String(opts.beneficiaryCr ?? '').trim();
|
|
1360
|
+
const code = String(opts.interventionCode ?? '').trim();
|
|
1361
|
+
const loc = String(opts.locationUuid ?? '').trim();
|
|
1362
|
+
if (!code || !loc) return null;
|
|
1363
|
+
|
|
1364
|
+
const preferToken = String(opts.consentToken ?? '').trim();
|
|
1365
|
+
if (preferToken) {
|
|
1366
|
+
try {
|
|
1367
|
+
const preview = await getPreauthPreview(preferToken, loc);
|
|
1368
|
+
const match = matchFromPreview(preview, code, preferToken, 'hie_preview', cr || null);
|
|
1369
|
+
if (match?.blocking) return match;
|
|
1370
|
+
} catch {
|
|
1371
|
+
// continue CR-wide scan
|
|
1372
|
+
}
|
|
1373
|
+
}
|
|
1374
|
+
|
|
1375
|
+
if (cr) {
|
|
1376
|
+
try {
|
|
1377
|
+
const auths = await getAuthorizations(loc, cr, undefined);
|
|
1378
|
+
const tokens = [
|
|
1379
|
+
...new Set(
|
|
1380
|
+
(auths ?? [])
|
|
1381
|
+
.filter((a) => authorizationMatchesBeneficiaryCr(a, cr))
|
|
1382
|
+
.map((a) => String(a?.token ?? '').trim())
|
|
1383
|
+
.filter((t) => t && t !== preferToken),
|
|
1384
|
+
),
|
|
1385
|
+
];
|
|
1386
|
+
// Cap concurrency to avoid hammering HIE
|
|
1387
|
+
const CONCURRENCY = 4;
|
|
1388
|
+
let resubmittable: ExistingPreauthMatch | null = null;
|
|
1389
|
+
for (let i = 0; i < tokens.length; i += CONCURRENCY) {
|
|
1390
|
+
const chunk = tokens.slice(i, i + CONCURRENCY);
|
|
1391
|
+
const results = await Promise.all(
|
|
1392
|
+
chunk.map(async (token) => {
|
|
1393
|
+
try {
|
|
1394
|
+
const preview = await getPreauthPreview(token, loc);
|
|
1395
|
+
return matchFromPreview(preview, code, token, 'hie_preview', cr);
|
|
1396
|
+
} catch {
|
|
1397
|
+
return null;
|
|
1398
|
+
}
|
|
1399
|
+
}),
|
|
1400
|
+
);
|
|
1401
|
+
for (const match of results) {
|
|
1402
|
+
if (!match) continue;
|
|
1403
|
+
if (match.blocking) return match;
|
|
1404
|
+
if (!resubmittable) resubmittable = match;
|
|
1405
|
+
}
|
|
1406
|
+
}
|
|
1407
|
+
if (resubmittable) return resubmittable;
|
|
1408
|
+
} catch {
|
|
1409
|
+
// fall through to local holds
|
|
1410
|
+
}
|
|
1411
|
+
}
|
|
1412
|
+
|
|
1413
|
+
const patientUuid = String(opts.patientUuid ?? '').trim();
|
|
1414
|
+
if (patientUuid) {
|
|
1415
|
+
try {
|
|
1416
|
+
const holds = await listPreAuthRequests({
|
|
1417
|
+
locationUuid: loc,
|
|
1418
|
+
patientUuid,
|
|
1419
|
+
interventionCode: code,
|
|
1420
|
+
});
|
|
1421
|
+
const withToken = holds.filter((h) => String(h.consentToken ?? '').trim());
|
|
1422
|
+
for (const hold of withToken) {
|
|
1423
|
+
const token = String(hold.consentToken).trim();
|
|
1424
|
+
try {
|
|
1425
|
+
const preview = await getPreauthPreview(token, String(hold.locationUuid || loc).trim() || loc);
|
|
1426
|
+
const match = matchFromPreview(preview, code, token, 'local_hold', cr || null);
|
|
1427
|
+
if (match?.blocking) return match;
|
|
1428
|
+
} catch {
|
|
1429
|
+
// No preview — only block from local hold when we have a patientUuid-scoped row.
|
|
1430
|
+
const status = String(hold.status ?? '').trim().toUpperCase();
|
|
1431
|
+
if (status && !isPreauthResubmittable(status)) {
|
|
1432
|
+
return {
|
|
1433
|
+
status,
|
|
1434
|
+
consentToken: token,
|
|
1435
|
+
source: 'local_hold',
|
|
1436
|
+
blocking: true,
|
|
1437
|
+
};
|
|
1438
|
+
}
|
|
1439
|
+
}
|
|
1440
|
+
}
|
|
1441
|
+
// Hold without token but already marked raised/active locally
|
|
1442
|
+
const raisedHold = holds.find((h) => {
|
|
1443
|
+
const status = String(h.status ?? '').trim().toUpperCase();
|
|
1444
|
+
return (
|
|
1445
|
+
status &&
|
|
1446
|
+
!isPreauthResubmittable(status) &&
|
|
1447
|
+
status !== 'DRAFT' &&
|
|
1448
|
+
!String(h.consentToken ?? '').trim()
|
|
1449
|
+
);
|
|
1450
|
+
});
|
|
1451
|
+
if (raisedHold) {
|
|
1452
|
+
return {
|
|
1453
|
+
status: String(raisedHold.status).trim().toUpperCase(),
|
|
1454
|
+
source: 'local_hold',
|
|
1455
|
+
blocking: true,
|
|
1456
|
+
};
|
|
1457
|
+
}
|
|
1458
|
+
} catch {
|
|
1459
|
+
// ignore local hold lookup failures
|
|
1460
|
+
}
|
|
1461
|
+
}
|
|
1462
|
+
|
|
1463
|
+
// Re-check preferred token for non-blocking (resubmittable) match
|
|
1464
|
+
if (preferToken) {
|
|
1465
|
+
try {
|
|
1466
|
+
const preview = await getPreauthPreview(preferToken, loc);
|
|
1467
|
+
return matchFromPreview(preview, code, preferToken, 'hie_preview', cr || null);
|
|
1468
|
+
} catch {
|
|
1469
|
+
return null;
|
|
1470
|
+
}
|
|
1471
|
+
}
|
|
1472
|
+
|
|
1473
|
+
return null;
|
|
1474
|
+
}
|
|
1475
|
+
|
|
1476
|
+
export function extractPreauthStatus(preview: unknown): string {
|
|
1477
|
+
const item = unwrapPreauthPreviewItem(preview);
|
|
1478
|
+
if (!item) {
|
|
1479
|
+
return '';
|
|
1480
|
+
}
|
|
1481
|
+
|
|
1482
|
+
const auth = item.authorizationDetails as Record<string, unknown> | undefined;
|
|
1483
|
+
if (auth?.overallPreauthFinalised === true || auth?.overall_preauth_finalised === true) {
|
|
1484
|
+
return 'FINALISED';
|
|
1485
|
+
}
|
|
1486
|
+
|
|
1487
|
+
return statusFromPreviewItem(item);
|
|
1488
|
+
}
|
|
1489
|
+
|
|
1490
|
+
export function extractPreauthCode(preview: unknown): string | undefined {
|
|
1491
|
+
const item = unwrapPreauthPreviewItem(preview);
|
|
1492
|
+
if (!item) {
|
|
1493
|
+
return undefined;
|
|
1494
|
+
}
|
|
1495
|
+
const auth = item.authorizationDetails as Record<string, unknown> | undefined;
|
|
1496
|
+
const fromAuth = (auth?.authCode as string) || (auth?.auth_code as string) || (auth?.token as string) || undefined;
|
|
1497
|
+
|
|
1498
|
+
return (
|
|
1499
|
+
(item.preauth_code as string) ||
|
|
1500
|
+
(item.preauthCode as string) ||
|
|
1501
|
+
(item.token as string) ||
|
|
1502
|
+
fromAuth ||
|
|
1503
|
+
(item.authCode as string) ||
|
|
1504
|
+
(item.id != null ? String(item.id) : undefined) ||
|
|
1505
|
+
(item.preauth_id != null ? String(item.preauth_id) : undefined)
|
|
1506
|
+
);
|
|
1507
|
+
}
|
|
1508
|
+
|
|
1509
|
+
export function isPreauthFinalised(status: string): boolean {
|
|
1510
|
+
const s = (status || '').toUpperCase();
|
|
1511
|
+
return s === 'FINALISED' || s === 'FINALIZED';
|
|
1512
|
+
}
|
|
1513
|
+
|
|
1514
|
+
export function isPreauthTerminalFailure(status: string): boolean {
|
|
1515
|
+
const s = (status || '').toUpperCase();
|
|
1516
|
+
if (PREAUTH_TERMINAL_FAILURE.has(s)) return true;
|
|
1517
|
+
// HIE sometimes returns prefixed / alternate spellings (e.g. PREAUTH_CANCELLED).
|
|
1518
|
+
if (s.includes('CANCEL')) return true;
|
|
1519
|
+
if (s.includes('EXPIRED')) return true;
|
|
1520
|
+
return false;
|
|
1521
|
+
}
|
|
1522
|
+
|
|
1523
|
+
/** Failure, cancellation, expiry, or clarification — provider may reopen and raise again. */
|
|
1524
|
+
export function isPreauthResubmittable(status: string): boolean {
|
|
1525
|
+
return isPreauthTerminalFailure(status) || isPreauthNeedsClarification(status);
|
|
1526
|
+
}
|
|
1527
|
+
|
|
1528
|
+
/** Statuses that mean the preauth was accepted by HIE — form can close. */
|
|
1529
|
+
export function isPreauthSubmitted(status: string): boolean {
|
|
1530
|
+
return PREAUTH_SUBMITTED.has((status || '').toUpperCase());
|
|
1531
|
+
}
|
|
1532
|
+
|
|
1533
|
+
export type PreauthCheckKind = 'not_raised' | 'finalised' | 'pending' | 'failed' | 'error';
|
|
1534
|
+
|
|
1535
|
+
export type PreauthCheckResult = {
|
|
1536
|
+
status: string;
|
|
1537
|
+
preauthCode?: string;
|
|
1538
|
+
preview: unknown;
|
|
1539
|
+
kind: PreauthCheckKind;
|
|
1540
|
+
notes?: string;
|
|
1541
|
+
error?: string;
|
|
1542
|
+
};
|
|
1543
|
+
|
|
1544
|
+
/**
|
|
1545
|
+
* Read one intervention's preauth status out of a preview already in hand.
|
|
1546
|
+
*
|
|
1547
|
+
* Split out of `checkPreauthStatus` so a list can fetch a visit's preview once and read
|
|
1548
|
+
* every one of that visit's interventions off it. The Needs-raise queue used to call
|
|
1549
|
+
* `checkPreauthStatus` per bill row, and a visit's rows all share its consent token — so a
|
|
1550
|
+
* patient with five interventions cost five identical calls to the same HIE endpoint.
|
|
1551
|
+
*/
|
|
1552
|
+
export function readPreauthCheck(preview: unknown, interventionCode?: string | null): PreauthCheckResult {
|
|
1553
|
+
const code = (interventionCode ?? '').trim();
|
|
1554
|
+
const item = code ? findPreauthPreviewForIntervention(preview, code) : unwrapPreauthPreviewItem(preview);
|
|
1555
|
+
// Paginated empty results / null body → not raised
|
|
1556
|
+
if (!preview || !item) {
|
|
1557
|
+
return { status: '', preview, kind: 'not_raised' };
|
|
1558
|
+
}
|
|
1559
|
+
|
|
1560
|
+
const status = code ? extractPreauthStatusForIntervention(preview, code) : extractPreauthStatus(preview);
|
|
1561
|
+
const preauthCode = code ? extractPreauthCodeForIntervention(preview, code) : extractPreauthCode(preview);
|
|
1562
|
+
const notes = extractPreauthNotesFromItem(item);
|
|
1563
|
+
|
|
1564
|
+
if (!status) {
|
|
1565
|
+
// Row exists for this intervention but status blank — treat as already raised.
|
|
1566
|
+
return { status: '', preview, kind: 'pending', preauthCode, notes };
|
|
1567
|
+
}
|
|
1568
|
+
if (isPreauthFinalised(status)) {
|
|
1569
|
+
return { status: 'FINALISED', preview, kind: 'finalised', preauthCode, notes };
|
|
1570
|
+
}
|
|
1571
|
+
if (isPreauthTerminalFailure(status)) {
|
|
1572
|
+
return { status, preview, kind: 'failed', preauthCode, notes };
|
|
1573
|
+
}
|
|
1574
|
+
// Clarification stays `pending` for status-tag colouring ("Needs clarification"),
|
|
1575
|
+
// but is not a blocking preauth — callers use isPreauthNeedsClarification / resubmit helpers.
|
|
1576
|
+
return { status, preview, kind: 'pending', preauthCode, notes };
|
|
1577
|
+
}
|
|
1578
|
+
|
|
1579
|
+
/** One-shot preauth status check for lists, gates, and non-React callers. */
|
|
1580
|
+
export async function checkPreauthStatus(
|
|
1581
|
+
consentToken: string | null | undefined,
|
|
1582
|
+
locationUuid: string | null | undefined,
|
|
1583
|
+
interventionCode?: string | null,
|
|
1584
|
+
): Promise<PreauthCheckResult> {
|
|
1585
|
+
if (!consentToken?.trim()) {
|
|
1586
|
+
return {
|
|
1587
|
+
status: '',
|
|
1588
|
+
preview: null,
|
|
1589
|
+
kind: 'error',
|
|
1590
|
+
error: 'Missing consent token',
|
|
1591
|
+
};
|
|
1592
|
+
}
|
|
1593
|
+
if (!locationUuid?.trim()) {
|
|
1594
|
+
return {
|
|
1595
|
+
status: '',
|
|
1596
|
+
preview: null,
|
|
1597
|
+
kind: 'error',
|
|
1598
|
+
error: 'Missing location',
|
|
1599
|
+
};
|
|
1600
|
+
}
|
|
1601
|
+
|
|
1602
|
+
try {
|
|
1603
|
+
const preview = await getPreauthPreview(consentToken.trim(), locationUuid.trim());
|
|
1604
|
+
return readPreauthCheck(preview, interventionCode);
|
|
1605
|
+
} catch (e: unknown) {
|
|
1606
|
+
const message = e instanceof Error ? e.message : String(e ?? 'Failed to get preauth preview');
|
|
1607
|
+
return {
|
|
1608
|
+
status: '',
|
|
1609
|
+
preview: null,
|
|
1610
|
+
kind: 'error',
|
|
1611
|
+
error: message,
|
|
1612
|
+
};
|
|
1613
|
+
}
|
|
1614
|
+
}
|
|
1615
|
+
|
|
1616
|
+
export const preauthPreviewSwrKey = (consentToken: string, locationUuid: string) =>
|
|
1617
|
+
['preauth-preview', consentToken, locationUuid] as const;
|
|
1618
|
+
|
|
1619
|
+
/** SWR hook for reusable preauth status elsewhere in the app. */
|
|
1620
|
+
export function usePreauthPreview(consentToken: string | null | undefined, locationUuid: string | null | undefined) {
|
|
1621
|
+
const key =
|
|
1622
|
+
consentToken?.trim() && locationUuid?.trim()
|
|
1623
|
+
? preauthPreviewSwrKey(consentToken.trim(), locationUuid.trim())
|
|
1624
|
+
: null;
|
|
1625
|
+
|
|
1626
|
+
const { data, error, isLoading, isValidating, mutate } = useSWR(
|
|
1627
|
+
key,
|
|
1628
|
+
([, token, loc]) => checkPreauthStatus(token, loc),
|
|
1629
|
+
{
|
|
1630
|
+
keepPreviousData: true,
|
|
1631
|
+
// Every automatic trigger is off, as on `useProviderClaimPreview`. This preview is a
|
|
1632
|
+
// round trip to the HIE, several components read the same claim's copy of it, and
|
|
1633
|
+
// the HIE rate-limits — so it is fetched when something asks: the first read of a
|
|
1634
|
+
// claim, or a mutation that invalidates it (see `invalidatePreauthPreview`). Left on,
|
|
1635
|
+
// `revalidateOnFocus` alone refetched every mounted claim each time the window was
|
|
1636
|
+
// clicked back into, and `revalidateIfStale` refetched on every remount.
|
|
1637
|
+
//
|
|
1638
|
+
// `revalidateOnMount` is deliberately left unset so SWR still fetches when there is
|
|
1639
|
+
// no cached preview yet, and skips it when there is.
|
|
1640
|
+
revalidateOnFocus: false,
|
|
1641
|
+
revalidateOnReconnect: false,
|
|
1642
|
+
revalidateIfStale: false,
|
|
1643
|
+
},
|
|
1644
|
+
);
|
|
1645
|
+
|
|
1646
|
+
return {
|
|
1647
|
+
preview: data?.preview ?? null,
|
|
1648
|
+
status: data?.status ?? '',
|
|
1649
|
+
preauthCode: data?.preauthCode,
|
|
1650
|
+
kind: data?.kind,
|
|
1651
|
+
check: data,
|
|
1652
|
+
error: error ?? (data?.kind === 'error' ? data.error : undefined),
|
|
1653
|
+
isLoading,
|
|
1654
|
+
isValidating,
|
|
1655
|
+
mutate,
|
|
1656
|
+
};
|
|
1657
|
+
}
|
|
1658
|
+
|
|
1659
|
+
export async function invalidatePreauthPreview(consentToken: string, locationUuid: string) {
|
|
1660
|
+
if (!consentToken?.trim() || !locationUuid?.trim()) return;
|
|
1661
|
+
// Both caches in front of the endpoint, in this order: the burst cache first, so the
|
|
1662
|
+
// revalidation SWR is about to run actually reaches the HIE rather than being answered
|
|
1663
|
+
// out of it.
|
|
1664
|
+
forgetPreauthPreview(consentToken.trim(), locationUuid.trim());
|
|
1665
|
+
await globalMutate(preauthPreviewSwrKey(consentToken.trim(), locationUuid.trim()));
|
|
1666
|
+
}
|
|
1667
|
+
|
|
1668
|
+
/** Poll GET /pre-auth/preview until FINALISED or a terminal failure. */
|
|
1669
|
+
export async function pollPreauthUntilFinalised(
|
|
1670
|
+
consentToken: string,
|
|
1671
|
+
locationUuid: string,
|
|
1672
|
+
{
|
|
1673
|
+
intervalMs = 3000,
|
|
1674
|
+
maxAttempts = 40,
|
|
1675
|
+
signal,
|
|
1676
|
+
}: { intervalMs?: number; maxAttempts?: number; signal?: AbortSignal } = {},
|
|
1677
|
+
): Promise<{ status: string; preview: unknown; preauthCode?: string }> {
|
|
1678
|
+
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
1679
|
+
if (signal?.aborted) {
|
|
1680
|
+
throw new Error('Preauth poll cancelled');
|
|
1681
|
+
}
|
|
1682
|
+
// A poll exists to see a change, so it asks past the burst cache every time.
|
|
1683
|
+
const preview = await getPreauthPreview(consentToken, locationUuid, { force: true });
|
|
1684
|
+
const status = extractPreauthStatus(preview);
|
|
1685
|
+
if (isPreauthFinalised(status)) {
|
|
1686
|
+
return { status: 'FINALISED', preview, preauthCode: extractPreauthCode(preview) };
|
|
1687
|
+
}
|
|
1688
|
+
if (status && isPreauthTerminalFailure(status)) {
|
|
1689
|
+
throw new Error(`Preauth ${status}`);
|
|
1690
|
+
}
|
|
1691
|
+
await new Promise((resolve) => setTimeout(resolve, intervalMs));
|
|
1692
|
+
}
|
|
1693
|
+
throw new Error('Timed out waiting for preauth to reach FINALISED');
|
|
1694
|
+
}
|
|
1695
|
+
|
|
1696
|
+
/**
|
|
1697
|
+
* Poll until the matching intervention's preauth is ACTIVE, PENDING_DOCTOR_APPROVAL,
|
|
1698
|
+
* or FINALISED — enough to close the Raise form without waiting on payer finalisation.
|
|
1699
|
+
*/
|
|
1700
|
+
export async function pollPreauthUntilSubmitted(
|
|
1701
|
+
consentToken: string,
|
|
1702
|
+
locationUuid: string,
|
|
1703
|
+
interventionCode: string,
|
|
1704
|
+
{
|
|
1705
|
+
intervalMs = 3000,
|
|
1706
|
+
maxAttempts = 40,
|
|
1707
|
+
signal,
|
|
1708
|
+
}: { intervalMs?: number; maxAttempts?: number; signal?: AbortSignal } = {},
|
|
1709
|
+
): Promise<{ status: string; preview: unknown; preauthCode?: string }> {
|
|
1710
|
+
const code = (interventionCode ?? '').trim();
|
|
1711
|
+
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
1712
|
+
if (signal?.aborted) {
|
|
1713
|
+
throw new Error('Preauth poll cancelled');
|
|
1714
|
+
}
|
|
1715
|
+
const preview = await getPreauthPreview(consentToken, locationUuid, { force: true });
|
|
1716
|
+
const status = code ? extractPreauthStatusForIntervention(preview, code) : extractPreauthStatus(preview);
|
|
1717
|
+
if (status && isPreauthSubmitted(status)) {
|
|
1718
|
+
return {
|
|
1719
|
+
status: isPreauthFinalised(status) ? 'FINALISED' : status,
|
|
1720
|
+
preview,
|
|
1721
|
+
preauthCode: code ? extractPreauthCodeForIntervention(preview, code) : extractPreauthCode(preview),
|
|
1722
|
+
};
|
|
1723
|
+
}
|
|
1724
|
+
if (status && isPreauthTerminalFailure(status)) {
|
|
1725
|
+
throw new Error(`Preauth ${status}`);
|
|
1726
|
+
}
|
|
1727
|
+
await new Promise((resolve) => setTimeout(resolve, intervalMs));
|
|
1728
|
+
}
|
|
1729
|
+
throw new Error('Timed out waiting for preauth submission confirmation');
|
|
1730
|
+
}
|
|
1731
|
+
|
|
1732
|
+
export const getServiceType = (selectedIntervention: Intervention, visitType?: VisitType): ServiceType => {
|
|
1733
|
+
const paymentMechanism = selectedIntervention.paymentMechanism;
|
|
1734
|
+
const accessPoint = selectedIntervention.accessPoint;
|
|
1735
|
+
|
|
1736
|
+
if (paymentMechanism.trim().toUpperCase() === 'CAPITATION') {
|
|
1737
|
+
return 'CAPITATION';
|
|
1738
|
+
}
|
|
1739
|
+
if (['PER DIEM', 'PER_DIEM'].includes(paymentMechanism.trim().toUpperCase())) {
|
|
1740
|
+
return 'INPATIENT';
|
|
1741
|
+
}
|
|
1742
|
+
if (accessPoint.trim().toUpperCase() === 'IP') {
|
|
1743
|
+
return 'INPATIENT';
|
|
1744
|
+
}
|
|
1745
|
+
if (accessPoint.trim().toUpperCase() === 'OP') {
|
|
1746
|
+
return 'OUTPATIENT';
|
|
1747
|
+
}
|
|
1748
|
+
if (accessPoint.trim().toUpperCase() === 'OP AND IP') {
|
|
1749
|
+
return visitType;
|
|
1750
|
+
}
|
|
1751
|
+
return visitType;
|
|
1752
|
+
};
|
|
1753
|
+
|
|
1754
|
+
export const createPreauthRequest = async (preauthRequest: PreauthRequest) => {
|
|
1755
|
+
const hieBaseUrl = await getHieBaseUrl();
|
|
1756
|
+
const postUrl = `${hieBaseUrl}/pre-auth/request`;
|
|
1757
|
+
return openmrsFetch<{}>(postUrl, {
|
|
1758
|
+
method: 'POST',
|
|
1759
|
+
headers: { 'Content-Type': 'application/json' },
|
|
1760
|
+
body: preauthRequest,
|
|
1761
|
+
});
|
|
1762
|
+
};
|
|
1763
|
+
|
|
1764
|
+
export type ListPreAuthRequestsParams = {
|
|
1765
|
+
locationUuid?: string;
|
|
1766
|
+
patientUuid?: string;
|
|
1767
|
+
electivePreauth?: boolean;
|
|
1768
|
+
status?: string;
|
|
1769
|
+
encounterUuid?: string;
|
|
1770
|
+
interventionCode?: string;
|
|
1771
|
+
};
|
|
1772
|
+
|
|
1773
|
+
export const listPreAuthRequests = async (
|
|
1774
|
+
params: ListPreAuthRequestsParams,
|
|
1775
|
+
): Promise<import('./index').PreAuthRequestRecord[]> => {
|
|
1776
|
+
const { hieBaseUrl } = await getHieBaseUrl();
|
|
1777
|
+
const qs = new URLSearchParams();
|
|
1778
|
+
if (params.locationUuid) qs.set('locationUuid', params.locationUuid);
|
|
1779
|
+
if (params.patientUuid) qs.set('patientUuid', params.patientUuid);
|
|
1780
|
+
if (params.status) qs.set('status', params.status);
|
|
1781
|
+
if (params.encounterUuid) qs.set('encounterUuid', params.encounterUuid);
|
|
1782
|
+
if (params.interventionCode) qs.set('interventionCode', params.interventionCode);
|
|
1783
|
+
if (params.electivePreauth !== undefined) {
|
|
1784
|
+
qs.set('electivePreauth', String(params.electivePreauth));
|
|
1785
|
+
}
|
|
1786
|
+
const url = `${hieBaseUrl}/pre-auth/request?${qs.toString()}`;
|
|
1787
|
+
const response = await openmrsFetch(url);
|
|
1788
|
+
const data = response?.data;
|
|
1789
|
+
const rows = Array.isArray(data) ? data : [];
|
|
1790
|
+
return rows.map(normalizePreAuthRequestRecord);
|
|
1791
|
+
};
|
|
1792
|
+
|
|
1793
|
+
/** Normalize hie-saf pre-auth rows (camelCase or snake_case) for the UI. */
|
|
1794
|
+
function normalizePreAuthRequestRecord(raw: unknown): import('./index').PreAuthRequestRecord {
|
|
1795
|
+
const r = (raw ?? {}) as Record<string, unknown>;
|
|
1796
|
+
const str = (a: unknown, b?: unknown) => String(a ?? b ?? '').trim();
|
|
1797
|
+
const bool = (a: unknown, b?: unknown) => {
|
|
1798
|
+
const v = a ?? b;
|
|
1799
|
+
if (typeof v === 'boolean') return v;
|
|
1800
|
+
if (v == null || v === '') return undefined;
|
|
1801
|
+
if (v === true || v === 1 || v === '1' || String(v).toLowerCase() === 'true') return true;
|
|
1802
|
+
if (v === false || v === 0 || v === '0' || String(v).toLowerCase() === 'false') return false;
|
|
1803
|
+
return Boolean(v);
|
|
1804
|
+
};
|
|
1805
|
+
const idRaw = r.id ?? r.preAuthRequestId ?? r.pre_auth_request_id;
|
|
1806
|
+
return {
|
|
1807
|
+
id: Number(idRaw) || 0,
|
|
1808
|
+
patientUuid: str(r.patientUuid, r.patient_uuid),
|
|
1809
|
+
orderNo: str(r.orderNo, r.order_no),
|
|
1810
|
+
subBenefitCode: str(r.subBenefitCode, r.sub_benefit_code),
|
|
1811
|
+
interventionCode: str(r.interventionCode, r.intervention_code),
|
|
1812
|
+
consentToken: str(r.consentToken, r.consent_token) || null,
|
|
1813
|
+
encounterUuid: str(r.encounterUuid, r.encounter_uuid) || null,
|
|
1814
|
+
expectedServiceStartDate: str(r.expectedServiceStartDate, r.expected_service_start_date) || null,
|
|
1815
|
+
serviceType: str(r.serviceType, r.service_type),
|
|
1816
|
+
locationUuid: str(r.locationUuid, r.location_uuid),
|
|
1817
|
+
billableServiceUuid: str(r.billableServiceUuid, r.billable_service_uuid) || null,
|
|
1818
|
+
priceUuid: str(r.priceUuid, r.price_uuid) || null,
|
|
1819
|
+
requiresPreauth: bool(r.requiresPreauth, r.requires_preauth),
|
|
1820
|
+
normalPreauth: bool(r.normalPreauth, r.normal_preauth),
|
|
1821
|
+
electivePreauth: bool(r.electivePreauth, r.elective_preauth),
|
|
1822
|
+
applicableDocumentTypes: str(r.applicableDocumentTypes, r.applicable_document_types) || undefined,
|
|
1823
|
+
requiredPreauthDocumentTypes:
|
|
1824
|
+
str(r.requiredPreauthDocumentTypes, r.required_preauth_document_types) || undefined,
|
|
1825
|
+
status: str(r.status) || undefined,
|
|
1826
|
+
dateCreated: str(r.dateCreated, r.date_created) || undefined,
|
|
1827
|
+
};
|
|
1828
|
+
}
|
|
1829
|
+
|
|
1830
|
+
export const patchPreAuthRequest = async (
|
|
1831
|
+
id: number,
|
|
1832
|
+
body: { status?: string; consentToken?: string; orderNo?: string },
|
|
1833
|
+
) => {
|
|
1834
|
+
const { hieBaseUrl } = await getHieBaseUrl();
|
|
1835
|
+
return openmrsFetch(`${hieBaseUrl}/pre-auth/request/${id}`, {
|
|
1836
|
+
method: 'PATCH',
|
|
1837
|
+
headers: { 'Content-Type': 'application/json' },
|
|
1838
|
+
body,
|
|
1839
|
+
});
|
|
1840
|
+
};
|