@ampath/esm-dha-workflow-app 4.0.0-next.340 → 4.0.0-next.344
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/dist/1149.js +1 -1
- package/dist/1171.js +1 -0
- package/dist/1171.js.map +1 -0
- package/dist/1177.js +1 -0
- package/dist/1177.js.map +1 -0
- package/dist/1239.js +1 -0
- package/dist/1239.js.map +1 -0
- package/dist/1551.js +1 -1
- package/dist/1560.js +1 -0
- package/dist/1560.js.map +1 -0
- package/dist/1928.js +1 -0
- package/dist/1928.js.map +1 -0
- package/dist/2093.js +1 -1
- package/dist/2230.js +1 -1
- package/dist/2493.js +1 -1
- package/dist/2914.js +1 -1
- package/dist/3078.js +1 -0
- package/dist/3078.js.map +1 -0
- package/dist/327.js +1 -0
- package/dist/327.js.map +1 -0
- package/dist/4605.js +1 -1
- package/dist/4611.js +1 -1
- package/dist/4674.js +1 -1
- package/dist/4674.js.map +1 -1
- package/dist/4986.js +1 -1
- package/dist/5167.js +1 -1
- package/dist/5317.js +1 -0
- package/dist/5317.js.map +1 -0
- package/dist/5344.js +1 -0
- package/dist/{2936.js.map → 5344.js.map} +1 -1
- package/dist/5765.js +1 -0
- package/dist/5765.js.map +1 -0
- package/dist/5851.js +1 -1
- package/dist/5851.js.map +1 -1
- package/dist/5961.js +1 -1
- package/dist/6264.js +1 -1
- package/dist/6264.js.map +1 -1
- package/dist/6720.js +1 -1
- package/dist/6942.js +1 -1
- package/dist/6942.js.map +1 -1
- package/dist/7521.js +1 -1
- package/dist/7521.js.map +1 -1
- package/dist/7549.js +1 -0
- package/dist/7549.js.map +1 -0
- package/dist/7717.js +1 -1
- package/dist/7857.js +1 -0
- package/dist/7857.js.map +1 -0
- package/dist/850.js +1 -0
- package/dist/850.js.map +1 -0
- package/dist/8733.js +1 -1
- package/dist/9070.js +1 -1
- package/dist/9093.js +1 -1
- package/dist/9093.js.map +1 -1
- package/dist/9338.js +1 -1
- package/dist/9338.js.map +1 -1
- package/dist/9349.js +1 -0
- package/dist/9349.js.map +1 -0
- package/dist/9428.js +1 -1
- package/dist/9428.js.map +1 -1
- package/dist/9789.js +1 -0
- package/dist/9789.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 +362 -239
- 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/package.json +1 -1
- package/src/billing/billing-claims.resource.ts +8 -7
- package/src/billing/dashboard/v2/clearance/cash.component.tsx +9 -9
- package/src/billing/dashboard/v2/clearance/pay-cash.component.tsx +24 -22
- package/src/billing/dashboard/v2/facility-bills/facility-bills.component.tsx +5 -4
- package/src/billing/dashboard/v2/patient-bill-details/attachments/final-bill.component.tsx +2 -2
- package/src/billing/dashboard/v2/patient-bill-details/attachments/generate-attachments.component.tsx +15 -4
- package/src/billing/dashboard/v2/patient-bill-details/attachments/invoice.component.tsx +2 -2
- package/src/billing/dashboard/v2/types/index.ts +6 -0
- package/src/billing/dashboard/v3/claim-visits/claim-intervention-details/claim-intervention-details.component.tsx +6 -1
- package/src/billing/dashboard/v3/claim-visits/claim-visit-details/claim-visit-details.component.tsx +5 -8
- package/src/billing/dashboard/v3/facility-bills/facility-bills.component.tsx +65 -62
- package/src/billing/dashboard/v3/patient-bill-details/attachments/case-summary/case-summary.tsx +30 -16
- package/src/billing/dashboard/v3/patient-bill-details/attachments/generate-attachments.component.tsx +3 -1
- package/src/billing/dashboard/v3/patient-bill-details/bill-details/bill-details.scss +4 -0
- package/src/billing/dashboard/v3/patient-bill-details/bill-details/bill-details.tsx +154 -156
- package/src/billing/dashboard/v3/patient-bill-details/claim-details/patient-claim-details.component.tsx +22 -23
- package/src/billing/dashboard/v3/patient-bill-details/patient-bill-details.tsx +5 -4
- package/src/dashboard/dashboard.component.tsx +3 -2
- package/src/dashboard/overview/overview.component.tsx +3 -1
- package/src/dashboard-meta/emt-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 +324 -0
- package/src/emt/emt-queue.component.tsx +449 -0
- package/src/emt/emt-queue.scss +83 -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 +524 -0
- package/src/emt/handover-modal/handover-modal.scss +88 -0
- package/src/emt/types/emt.types.ts +137 -0
- package/src/index.ts +10 -0
- package/src/registry/registry.component.tsx +49 -1
- package/src/registry/utils/hie-client-adapter.ts +1 -1
- package/src/root.component.tsx +2 -0
- package/src/routes.json +25 -0
- package/src/side-nav-menu/nav-link-config.ts +4 -0
- package/dist/1362.js +0 -1
- package/dist/1362.js.map +0 -1
- package/dist/203.js +0 -1
- package/dist/203.js.map +0 -1
- package/dist/2200.js +0 -1
- package/dist/2200.js.map +0 -1
- package/dist/2936.js +0 -1
- package/dist/2988.js +0 -1
- package/dist/2988.js.map +0 -1
- package/dist/307.js +0 -1
- package/dist/307.js.map +0 -1
- package/dist/4008.js +0 -1
- package/dist/4008.js.map +0 -1
- package/dist/446.js +0 -1
- package/dist/446.js.map +0 -1
- package/dist/5097.js +0 -1
- package/dist/5097.js.map +0 -1
- package/dist/7240.js +0 -1
- package/dist/7240.js.map +0 -1
- package/dist/874.js +0 -1
- package/dist/874.js.map +0 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React, { useEffect, useMemo, useState } from 'react';
|
|
2
2
|
import { type PatientPayment, type PatientFacilityBillDetails, ClaimsVisit } from '../../types';
|
|
3
3
|
import styles from './bill-details.scss';
|
|
4
|
-
import { Button, OverflowMenu, OverflowMenuItem, Table, TableBody, TableCell, TableHead, TableHeader, TableRow, Tag } from '@carbon/react';
|
|
4
|
+
import { Button, OverflowMenu, OverflowMenuItem, RadioButton, RadioButtonGroup, Table, TableBody, TableCell, TableHead, TableHeader, TableRow, Tag } from '@carbon/react';
|
|
5
5
|
import { formatDate, parseDate } from '@openmrs/esm-framework';
|
|
6
6
|
import BillItemPaymentModal from '../modals/bill-item-payment/bill-item-payment.modal';
|
|
7
7
|
import AddClaimLineModal from '../modals/add-claim-line/add-claim-line.modal';
|
|
@@ -17,13 +17,19 @@ interface billDetailsProps {
|
|
|
17
17
|
locationUuid: string;
|
|
18
18
|
claimsVisit: ClaimsVisit;
|
|
19
19
|
}
|
|
20
|
+
type BillingScope = 'OPD' | 'INPATIENT';
|
|
20
21
|
const BillDetails: React.FC<billDetailsProps> = ({ patientBillDetails, patientPayments, amrsVisitDiagnosis, consentToken, locationUuid, claimsVisit }) => {
|
|
21
22
|
const [showPaymentModal, setShowPaymentModal] = useState<boolean>(false);
|
|
22
23
|
const [showAddClaimLineModal, setShowAddClaimLineModal] = useState<boolean>(false);
|
|
23
24
|
const [selectedBillItem, setSelectedBillItem] = useState<PatientFacilityBillDetails | null>(null);
|
|
24
25
|
const setDiagnosisInterventionCode = useMemo(() => getConsultationBillIntervantionCode(), [patientBillDetails]);
|
|
26
|
+
const [billingScope, setBillingScope] = useState<BillingScope>('OPD');
|
|
25
27
|
const invalidateProviderClaimPreview = useInvalidateProviderClaimPreview();
|
|
26
28
|
|
|
29
|
+
const scopedBillDetails = patientBillDetails;
|
|
30
|
+
const scopedPayments = patientPayments;
|
|
31
|
+
const scopedDiagnosis = amrsVisitDiagnosis;
|
|
32
|
+
|
|
27
33
|
if (!patientBillDetails && !patientPayments) {
|
|
28
34
|
return <>No Data</>;
|
|
29
35
|
}
|
|
@@ -61,6 +67,7 @@ const BillDetails: React.FC<billDetailsProps> = ({ patientBillDetails, patientPa
|
|
|
61
67
|
handleCloseAddClaimItemModal();
|
|
62
68
|
invalidateProviderClaimPreview();
|
|
63
69
|
}
|
|
70
|
+
|
|
64
71
|
function interventionAddedToClaimLine(b: PatientFacilityBillDetails) {
|
|
65
72
|
if (b.intervention_code && b.has_claim_line === 0) {
|
|
66
73
|
if (claimsVisit && claimsVisit.invoices) {
|
|
@@ -73,163 +80,154 @@ const BillDetails: React.FC<billDetailsProps> = ({ patientBillDetails, patientPa
|
|
|
73
80
|
}
|
|
74
81
|
return true;
|
|
75
82
|
}
|
|
83
|
+
|
|
84
|
+
const billDetailsLayout = (
|
|
85
|
+
<div className={styles.billDetailsLayout}>
|
|
86
|
+
{scopedBillDetails.length > 0 ? (
|
|
87
|
+
<div className={styles.billRow}>
|
|
88
|
+
<div>
|
|
89
|
+
<h6>Bill Items</h6>
|
|
90
|
+
</div>
|
|
91
|
+
<div>
|
|
92
|
+
<Table aria-label="bill details" size="sm">
|
|
93
|
+
<TableHead>
|
|
94
|
+
<TableRow>
|
|
95
|
+
<TableHeader>No</TableHeader>
|
|
96
|
+
<TableHeader>Bill Item</TableHeader>
|
|
97
|
+
<TableHeader>Intervention Code</TableHeader>
|
|
98
|
+
<TableHeader>Order No</TableHeader>
|
|
99
|
+
<TableHeader>Service Type</TableHeader>
|
|
100
|
+
<TableHeader>Payer</TableHeader>
|
|
101
|
+
<TableHeader>Payment Status</TableHeader>
|
|
102
|
+
<TableHeader>Quantity</TableHeader>
|
|
103
|
+
<TableHeader>Total</TableHeader>
|
|
104
|
+
<TableHeader>Actions</TableHeader>
|
|
105
|
+
</TableRow>
|
|
106
|
+
</TableHead>
|
|
107
|
+
<TableBody>
|
|
108
|
+
{scopedBillDetails.map((b, index) => (
|
|
109
|
+
<TableRow key={b.patient_uuid}>
|
|
110
|
+
<TableCell>{index + 1}</TableCell>
|
|
111
|
+
<TableCell>{b.billable_service}</TableCell>
|
|
112
|
+
<TableCell>{b.intervention_code ?? ''}</TableCell>
|
|
113
|
+
<TableCell>{b.order_no ?? ''}</TableCell>
|
|
114
|
+
<TableCell>{b.service_type ?? ''}</TableCell>
|
|
115
|
+
<TableCell>{b.payment_scheme}</TableCell>
|
|
116
|
+
<TableCell>{b.payment_status}</TableCell>
|
|
117
|
+
<TableCell>{b.item_quantity}</TableCell>
|
|
118
|
+
<TableCell>Ksh {b.item_total_price}</TableCell>
|
|
119
|
+
<TableCell>
|
|
120
|
+
{
|
|
121
|
+
(b.paid_status !== 'PAID' && !b.intervention_code) && <Button size="sm" kind="tertiary" onClick={() => handleBillItemPayment(b)}>Pay</Button>
|
|
122
|
+
}
|
|
123
|
+
{
|
|
124
|
+
b.intervention_code ?
|
|
125
|
+
(
|
|
126
|
+
claimsVisit ?
|
|
127
|
+
(
|
|
128
|
+
claimsVisit.workflow_state === 'DRAFT' ?
|
|
129
|
+
(!interventionAddedToClaimLine(b) && <Button size="sm" kind="tertiary" onClick={() => handleClaimLineAddition(b)}>Add Claim Line</Button>)
|
|
130
|
+
: <Tag>{claimsVisit.workflow_state}</Tag>
|
|
131
|
+
)
|
|
132
|
+
: <></>
|
|
133
|
+
)
|
|
134
|
+
: <></>
|
|
135
|
+
}
|
|
136
|
+
</TableCell>
|
|
137
|
+
</TableRow>
|
|
138
|
+
))}
|
|
139
|
+
</TableBody>
|
|
140
|
+
</Table>
|
|
141
|
+
</div>
|
|
142
|
+
</div>
|
|
143
|
+
) : null}
|
|
144
|
+
|
|
145
|
+
{scopedPayments.length > 0 ? (
|
|
146
|
+
<div className={styles.billRow}>
|
|
147
|
+
<div>
|
|
148
|
+
<h6>Bill Payments</h6>
|
|
149
|
+
</div>
|
|
150
|
+
<div>
|
|
151
|
+
<Table aria-label="bill details" size="sm">
|
|
152
|
+
<TableHead>
|
|
153
|
+
<TableRow>
|
|
154
|
+
<TableHeader>No</TableHeader>
|
|
155
|
+
<TableHeader>Payment Type</TableHeader>
|
|
156
|
+
<TableHeader>Amount</TableHeader>
|
|
157
|
+
<TableHeader>Amount Tendered</TableHeader>
|
|
158
|
+
<TableHeader>Date / Time</TableHeader>
|
|
159
|
+
</TableRow>
|
|
160
|
+
</TableHead>
|
|
161
|
+
<TableBody>
|
|
162
|
+
{scopedPayments.map((p, index) => (
|
|
163
|
+
<TableRow key={p.cashier_bill_payment_uuid}>
|
|
164
|
+
<TableCell>{index + 1}</TableCell>
|
|
165
|
+
<TableCell>{p.payment_mode}</TableCell>
|
|
166
|
+
<TableCell>Ksh {p.amount}</TableCell>
|
|
167
|
+
<TableCell>Ksh {p.amount_tendered}</TableCell>
|
|
168
|
+
<TableCell>{formatDate(parseDate(p.payment_time))}</TableCell>
|
|
169
|
+
</TableRow>
|
|
170
|
+
))}
|
|
171
|
+
</TableBody>
|
|
172
|
+
</Table>
|
|
173
|
+
</div>
|
|
174
|
+
</div>
|
|
175
|
+
) : null}
|
|
176
|
+
|
|
177
|
+
{scopedDiagnosis.length > 0 ? (
|
|
178
|
+
<div className={styles.billRow}>
|
|
179
|
+
<div>
|
|
180
|
+
<h6>Patient Dignosis</h6>
|
|
181
|
+
</div>
|
|
182
|
+
<div>
|
|
183
|
+
<VisitDiagnosisDetails
|
|
184
|
+
amrsVisitDiagnosis={scopedDiagnosis}
|
|
185
|
+
consentToken={consentToken}
|
|
186
|
+
locationUuid={locationUuid}
|
|
187
|
+
interventionCode={setDiagnosisInterventionCode}
|
|
188
|
+
claimsVisit={claimsVisit}
|
|
189
|
+
/>
|
|
190
|
+
</div>
|
|
191
|
+
</div>
|
|
192
|
+
) : null}
|
|
193
|
+
</div>
|
|
194
|
+
);
|
|
195
|
+
|
|
76
196
|
return (
|
|
77
197
|
<>
|
|
78
|
-
<
|
|
79
|
-
{
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
<TableHeader>No</TableHeader>
|
|
90
|
-
<TableHeader>Bill Item</TableHeader>
|
|
91
|
-
<TableHeader>Intervention Code</TableHeader>
|
|
92
|
-
<TableHeader>Order No</TableHeader>
|
|
93
|
-
<TableHeader>Service Type</TableHeader>
|
|
94
|
-
<TableHeader>Payer</TableHeader>
|
|
95
|
-
<TableHeader>Payment Status</TableHeader>
|
|
96
|
-
<TableHeader>Quantity</TableHeader>
|
|
97
|
-
<TableHeader>Total</TableHeader>
|
|
98
|
-
<TableHeader>Actions</TableHeader>
|
|
99
|
-
</TableRow>
|
|
100
|
-
</TableHead>
|
|
101
|
-
<TableBody>
|
|
102
|
-
{patientBillDetails &&
|
|
103
|
-
patientBillDetails.map((b, index) => {
|
|
104
|
-
return (
|
|
105
|
-
<>
|
|
106
|
-
<TableRow key={b.patient_uuid}>
|
|
107
|
-
<TableCell>{index + 1}</TableCell>
|
|
108
|
-
<TableCell>{b.billable_service}</TableCell>
|
|
109
|
-
<TableCell>{b.intervention_code ?? ''}</TableCell>
|
|
110
|
-
<TableCell>{b.order_no ?? ''}</TableCell>
|
|
111
|
-
<TableCell>{b.service_type ?? ''}</TableCell>
|
|
112
|
-
<TableCell>{b.payment_scheme}</TableCell>
|
|
113
|
-
<TableCell>{b.paid_status}</TableCell>
|
|
114
|
-
<TableCell>{b.item_quantity}</TableCell>
|
|
115
|
-
<TableCell>Ksh {b.item_total_price}</TableCell>
|
|
116
|
-
<TableCell>
|
|
117
|
-
{/* <OverflowMenu aria-label="overflow-menu">
|
|
118
|
-
{
|
|
119
|
-
(b.paid_status !== 'PAID' && !b.intervention_code) && <OverflowMenuItem itemText="Pay" onClick={() => handleBillItemPayment(b)} />
|
|
120
|
-
}
|
|
121
|
-
{
|
|
122
|
-
(b.intervention_code && b.has_claim_line === 0) && <OverflowMenuItem itemText="Add Claim Line" onClick={() => handleClaimLineAddition(b)} />
|
|
123
|
-
}
|
|
124
|
-
</OverflowMenu> */}
|
|
125
|
-
{
|
|
126
|
-
(b.paid_status !== 'PAID' && !b.intervention_code) && <Button size="sm" kind="tertiary" onClick={() => handleBillItemPayment(b)}>Pay</Button>
|
|
127
|
-
}
|
|
128
|
-
{
|
|
129
|
-
b.intervention_code ?
|
|
130
|
-
(
|
|
131
|
-
claimsVisit ?
|
|
132
|
-
(
|
|
133
|
-
claimsVisit.workflow_state === 'DRAFT' ?
|
|
134
|
-
(!interventionAddedToClaimLine(b) && <Button size="sm" kind="tertiary" onClick={() => handleClaimLineAddition(b)}>Add Claim Line</Button>)
|
|
135
|
-
: <Tag>{claimsVisit.workflow_state}</Tag>
|
|
136
|
-
)
|
|
137
|
-
: <></>
|
|
138
|
-
)
|
|
139
|
-
: <></>
|
|
140
|
-
}
|
|
141
|
-
</TableCell>
|
|
142
|
-
</TableRow>
|
|
143
|
-
</>
|
|
144
|
-
);
|
|
145
|
-
})}
|
|
146
|
-
</TableBody>
|
|
147
|
-
</Table>
|
|
148
|
-
</div>
|
|
149
|
-
</div>
|
|
150
|
-
</>
|
|
151
|
-
) : (
|
|
152
|
-
<></>
|
|
153
|
-
)}
|
|
154
|
-
{patientPayments.length > 0 ? (
|
|
155
|
-
<>
|
|
156
|
-
<div className={styles.billRow}>
|
|
157
|
-
<div>
|
|
158
|
-
<h6>Bill Payments</h6>
|
|
159
|
-
</div>
|
|
160
|
-
<div>
|
|
161
|
-
<Table aria-label="bill details" size="sm">
|
|
162
|
-
<TableHead>
|
|
163
|
-
<TableRow>
|
|
164
|
-
<TableHeader>No</TableHeader>
|
|
165
|
-
<TableHeader>Payment Type</TableHeader>
|
|
166
|
-
<TableHeader>Amount</TableHeader>
|
|
167
|
-
<TableHeader>Amount Tendered</TableHeader>
|
|
168
|
-
<TableHeader>Date / Time</TableHeader>
|
|
169
|
-
</TableRow>
|
|
170
|
-
</TableHead>
|
|
171
|
-
<TableBody>
|
|
172
|
-
{patientPayments &&
|
|
173
|
-
patientPayments.map((p, index) => {
|
|
174
|
-
return (
|
|
175
|
-
<>
|
|
176
|
-
<TableRow key={p.cashier_bill_payment_uuid}>
|
|
177
|
-
<TableCell>{index + 1}</TableCell>
|
|
178
|
-
<TableCell>{p.payment_mode}</TableCell>
|
|
179
|
-
<TableCell>Ksh {p.amount}</TableCell>
|
|
180
|
-
<TableCell>Ksh {p.amount_tendered}</TableCell>
|
|
181
|
-
<TableCell>
|
|
182
|
-
{formatDate(parseDate(p.payment_time))}</TableCell>
|
|
183
|
-
</TableRow>
|
|
184
|
-
</>
|
|
185
|
-
);
|
|
186
|
-
})}
|
|
187
|
-
</TableBody>
|
|
188
|
-
</Table>
|
|
189
|
-
</div>
|
|
190
|
-
</div>
|
|
191
|
-
</>
|
|
192
|
-
) : (
|
|
193
|
-
<></>
|
|
194
|
-
)}
|
|
195
|
-
{
|
|
196
|
-
amrsVisitDiagnosis.length > 0 ? (<>
|
|
197
|
-
<div className={styles.billRow}>
|
|
198
|
-
<div>
|
|
199
|
-
<h6>Patient Dignosis</h6>
|
|
200
|
-
</div>
|
|
201
|
-
<div>
|
|
202
|
-
<VisitDiagnosisDetails
|
|
203
|
-
amrsVisitDiagnosis={amrsVisitDiagnosis}
|
|
204
|
-
consentToken={consentToken}
|
|
205
|
-
locationUuid={locationUuid}
|
|
206
|
-
interventionCode={setDiagnosisInterventionCode}
|
|
207
|
-
claimsVisit={claimsVisit}
|
|
208
|
-
/>
|
|
209
|
-
</div>
|
|
210
|
-
</div>
|
|
198
|
+
<RadioButtonGroup
|
|
199
|
+
className={styles.radioGroup}
|
|
200
|
+
name="billing-scope"
|
|
201
|
+
valueSelected={billingScope}
|
|
202
|
+
onChange={(value) => setBillingScope(value as BillingScope)}
|
|
203
|
+
invalidText="Invalid selection"
|
|
204
|
+
warnText="Please notice the warning"
|
|
205
|
+
>
|
|
206
|
+
<RadioButton id="outpatient" labelText="OPD" value="OPD" />
|
|
207
|
+
<RadioButton id="inpatient" labelText="INPATIENT" value="INPATIENT" />
|
|
208
|
+
</RadioButtonGroup>
|
|
211
209
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
210
|
+
{billDetailsLayout}
|
|
211
|
+
|
|
212
|
+
{showPaymentModal && selectedBillItem && (
|
|
213
|
+
<BillItemPaymentModal
|
|
214
|
+
open={showPaymentModal}
|
|
215
|
+
billItem={selectedBillItem}
|
|
216
|
+
onClose={handleClosePayModal}
|
|
217
|
+
onPay={handleSuccessfullPayment}
|
|
218
|
+
/>
|
|
219
|
+
)}
|
|
220
|
+
{showAddClaimLineModal && selectedBillItem && (
|
|
221
|
+
<AddClaimLineModal
|
|
222
|
+
open={showAddClaimLineModal}
|
|
223
|
+
billItem={selectedBillItem}
|
|
224
|
+
onClose={handleCloseAddClaimItemModal}
|
|
225
|
+
onSuccess={onSuccess}
|
|
226
|
+
locationUuid={locationUuid}
|
|
227
|
+
consentToken={consentToken}
|
|
228
|
+
/>
|
|
229
|
+
)}
|
|
230
|
+
</>
|
|
231
|
+
);
|
|
234
232
|
};
|
|
235
233
|
export default BillDetails;
|
|
@@ -1,17 +1,18 @@
|
|
|
1
|
-
import React, { useEffect } from
|
|
2
|
-
import { useState } from
|
|
3
|
-
import { type ProviderClaimPreviewDto, type ClaimsVisit, type PatientFacilityBillDetails } from
|
|
4
|
-
import { showSnackbar } from
|
|
5
|
-
import { fetchProviderClaimPreview, useProviderClaimPreview } from
|
|
1
|
+
import React, { useEffect } from 'react';
|
|
2
|
+
import { useState } from 'react';
|
|
3
|
+
import { type ProviderClaimPreviewDto, type ClaimsVisit, type PatientFacilityBillDetails } from '../../types';
|
|
4
|
+
import { showSnackbar } from '@openmrs/esm-framework';
|
|
5
|
+
import { fetchProviderClaimPreview, useProviderClaimPreview } from '../../../../billing-claims.resource';
|
|
6
6
|
import styles from './patient-claim-details.component.scss';
|
|
7
|
-
import ClaimVisitDetails from
|
|
8
|
-
import { InlineLoading } from
|
|
7
|
+
import ClaimVisitDetails from '../../claim-visits/claim-visit-details/claim-visit-details.component';
|
|
8
|
+
import { InlineLoading } from '@carbon/react';
|
|
9
9
|
|
|
10
10
|
interface patientClaimDetailsProps {
|
|
11
11
|
consentToken: string;
|
|
12
12
|
locationUuid: string;
|
|
13
13
|
patientBillDetails: PatientFacilityBillDetails[];
|
|
14
14
|
onBillDetailsChange?: () => void;
|
|
15
|
+
billingDate: string;
|
|
15
16
|
onLoadingClaimVisit?: (claimVisit: ClaimsVisit) => void;
|
|
16
17
|
}
|
|
17
18
|
const PatientClaimDetails: React.FC<patientClaimDetailsProps> = ({
|
|
@@ -19,6 +20,7 @@ const PatientClaimDetails: React.FC<patientClaimDetailsProps> = ({
|
|
|
19
20
|
locationUuid,
|
|
20
21
|
patientBillDetails,
|
|
21
22
|
onBillDetailsChange,
|
|
23
|
+
billingDate,
|
|
22
24
|
onLoadingClaimVisit
|
|
23
25
|
}) => {
|
|
24
26
|
const [patientBill, setPatientBill] = useState<PatientFacilityBillDetails>();
|
|
@@ -37,33 +39,30 @@ const PatientClaimDetails: React.FC<patientClaimDetailsProps> = ({
|
|
|
37
39
|
}, [consentToken, locationUuid]);
|
|
38
40
|
|
|
39
41
|
if (isLoading && !claimVisit) {
|
|
40
|
-
return <InlineLoading description=
|
|
42
|
+
return <InlineLoading description="Loading data...please wait" />;
|
|
41
43
|
}
|
|
42
44
|
|
|
43
45
|
function getPatientBill() {
|
|
44
|
-
const bill = patientBillDetails.find(details => details.consent_token === consentToken);
|
|
46
|
+
const bill = patientBillDetails.find((details) => details.consent_token === consentToken);
|
|
45
47
|
setPatientBill(bill);
|
|
46
48
|
}
|
|
47
49
|
|
|
48
|
-
return
|
|
49
|
-
|
|
50
|
-
isValidating &&
|
|
51
|
-
<
|
|
52
|
-
|
|
53
|
-
<div className={styles.pcLayout}>
|
|
54
|
-
{
|
|
55
|
-
claimVisit && (
|
|
50
|
+
return (
|
|
51
|
+
<>
|
|
52
|
+
{isValidating && <InlineLoading description="Refreshing data..." />}
|
|
53
|
+
<div className={styles.pcLayout}>
|
|
54
|
+
{claimVisit && (
|
|
56
55
|
<ClaimVisitDetails
|
|
57
56
|
patientBillDetails={patientBill}
|
|
58
57
|
claimsVisit={claimVisit}
|
|
59
58
|
locationUuid={locationUuid}
|
|
60
59
|
onBillDetailsChange={onBillDetailsChange}
|
|
61
60
|
claimRefreshing={isValidating}
|
|
61
|
+
billingDate={billingDate}
|
|
62
62
|
/>
|
|
63
|
-
)
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
</>
|
|
63
|
+
)}
|
|
64
|
+
</div>
|
|
65
|
+
</>
|
|
66
|
+
);
|
|
68
67
|
};
|
|
69
|
-
export default PatientClaimDetails;
|
|
68
|
+
export default PatientClaimDetails;
|
|
@@ -139,7 +139,7 @@ const PatientBillDetails: React.FC<patientBillDetailsProps> = ({ patientUuid, lo
|
|
|
139
139
|
return s.paid_status === 'POSTED';
|
|
140
140
|
});
|
|
141
141
|
if (hasPostedBill) {
|
|
142
|
-
return 'PARTIALLY PAID'
|
|
142
|
+
return 'PARTIALLY PAID';
|
|
143
143
|
}
|
|
144
144
|
const hasPendingBill = patientBillDetails.some((s) => {
|
|
145
145
|
return s.paid_status === 'PENDING';
|
|
@@ -208,13 +208,13 @@ const PatientBillDetails: React.FC<patientBillDetailsProps> = ({ patientUuid, lo
|
|
|
208
208
|
return {
|
|
209
209
|
patientUuid: patientUuid,
|
|
210
210
|
visitDate: billingDate,
|
|
211
|
-
locationUuid: locationUuid
|
|
211
|
+
locationUuid: locationUuid,
|
|
212
212
|
};
|
|
213
213
|
}
|
|
214
214
|
function getPatientAmrsMaternityDiagnosisPayload(): AmrsMaternityDiagnosisDto {
|
|
215
215
|
return {
|
|
216
216
|
patientUuid: patientUuid,
|
|
217
|
-
billingDate: billingDate
|
|
217
|
+
billingDate: billingDate,
|
|
218
218
|
};
|
|
219
219
|
}
|
|
220
220
|
function onLoadingClaimVisit(claimVisit: ClaimsVisit) {
|
|
@@ -324,6 +324,7 @@ const PatientBillDetails: React.FC<patientBillDetailsProps> = ({ patientUuid, lo
|
|
|
324
324
|
patientBillDetails={patientBillDetails}
|
|
325
325
|
consentToken={consentToken}
|
|
326
326
|
onBillDetailsChange={getPatientBillDetails}
|
|
327
|
+
billingDate={billingDate}
|
|
327
328
|
onLoadingClaimVisit={onLoadingClaimVisit}
|
|
328
329
|
/>
|
|
329
330
|
</>
|
|
@@ -339,4 +340,4 @@ const PatientBillDetails: React.FC<patientBillDetailsProps> = ({ patientUuid, lo
|
|
|
339
340
|
);
|
|
340
341
|
};
|
|
341
342
|
|
|
342
|
-
export default PatientBillDetails;
|
|
343
|
+
export default PatientBillDetails;
|
|
@@ -3,12 +3,11 @@ import { Analytics } from '@carbon/react/icons';
|
|
|
3
3
|
|
|
4
4
|
import styles from './dashboard.component.scss';
|
|
5
5
|
import Overview from './overview/overview.component';
|
|
6
|
-
import { getServiceQueueByLocationUuid } from '../service-queues/service-queues.resource';
|
|
7
6
|
import { type QueueEntryResult } from '../registry/types';
|
|
8
7
|
import { useSession } from '@openmrs/esm-framework';
|
|
9
|
-
import { QUEUE_SERVICE_UUIDS } from '../shared/constants/concepts';
|
|
10
8
|
import { getDashBoardSummary } from '../resources/dashboard.resource';
|
|
11
9
|
import FacilityAndWorkerSlot from '../shared/ui/facility-worker-slot/facility-worker.component-slot.component';
|
|
10
|
+
import { usePendingReferrals } from '../emt/emt.resource';
|
|
12
11
|
|
|
13
12
|
interface DashboardProps {}
|
|
14
13
|
|
|
@@ -18,6 +17,7 @@ const Dashboard: React.FC<DashboardProps> = () => {
|
|
|
18
17
|
const [dashboardSummary, setDashboardSummary] = useState<any[]>([]);
|
|
19
18
|
const session = useSession();
|
|
20
19
|
const locationUuid = session.sessionLocation.uuid;
|
|
20
|
+
const { count: emtCount } = usePendingReferrals(1, 0);
|
|
21
21
|
useEffect(() => {
|
|
22
22
|
getDashBoardData();
|
|
23
23
|
}, []);
|
|
@@ -45,6 +45,7 @@ const Dashboard: React.FC<DashboardProps> = () => {
|
|
|
45
45
|
triageCount={triageQueueEntries}
|
|
46
46
|
consultationCount={consultationQueueEntries}
|
|
47
47
|
dashboardSummary={dashboardSummary}
|
|
48
|
+
emtCount={emtCount}
|
|
48
49
|
/>
|
|
49
50
|
</div>
|
|
50
51
|
);
|
|
@@ -16,9 +16,10 @@ interface OverviewProps {
|
|
|
16
16
|
triageCount?: QueueEntryResult[];
|
|
17
17
|
consultationCount?: QueueEntryResult[];
|
|
18
18
|
dashboardSummary?: any;
|
|
19
|
+
emtCount?: number;
|
|
19
20
|
}
|
|
20
21
|
|
|
21
|
-
const Overview: React.FC<OverviewProps> = ({ triageCount, consultationCount, dashboardSummary }) => {
|
|
22
|
+
const Overview: React.FC<OverviewProps> = ({ triageCount, consultationCount, dashboardSummary, emtCount }) => {
|
|
22
23
|
const totalPatients: QueueEntryResult[] = [...triageCount, ...consultationCount];
|
|
23
24
|
const patientsInQueue = totalPatients.filter(
|
|
24
25
|
(patient) => patient.status === 'WAITING' || patient.status === 'IN SERVICE',
|
|
@@ -72,6 +73,7 @@ const Overview: React.FC<OverviewProps> = ({ triageCount, consultationCount, das
|
|
|
72
73
|
{ key: 'labs', title: 'Labs', unit: 'Orders', value: dashboardSummary?.labs ?? 0 },
|
|
73
74
|
{ key: 'pharmacy', title: 'Pharmacy', unit: 'Orders', value: dashboardSummary?.pharmacy ?? 0 },
|
|
74
75
|
{ key: 'emergencies', title: 'Emergencies', unit: 'Patients', value: dashboardSummary?.emergencies ?? 0 },
|
|
76
|
+
{ key: 'emt', title: 'EMT Referrals', unit: 'Pending', value: emtCount ?? 0 },
|
|
75
77
|
{
|
|
76
78
|
key: 'waiting',
|
|
77
79
|
title: 'Avg. Waiting Time',
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for the CR-by-crId lookup helper and clientDisplayName utility.
|
|
3
|
+
*/
|
|
4
|
+
jest.mock('../registry/registry.resource', () => ({
|
|
5
|
+
fetchClientRegistryData: jest.fn(),
|
|
6
|
+
}));
|
|
7
|
+
|
|
8
|
+
import { fetchClientRegistryData } from '../registry/registry.resource';
|
|
9
|
+
import { fetchClientByCrId, clientDisplayName } from './cr-lookup.resource';
|
|
10
|
+
|
|
11
|
+
const mockFetchClientRegistryData = jest.mocked(fetchClientRegistryData);
|
|
12
|
+
|
|
13
|
+
beforeEach(() => {
|
|
14
|
+
jest.resetAllMocks();
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
describe('fetchClientByCrId', () => {
|
|
18
|
+
it('searches the client registry by CR id and returns the first match', async () => {
|
|
19
|
+
const client = {
|
|
20
|
+
id: 'CR5617849204955-8',
|
|
21
|
+
first_name: 'Jane',
|
|
22
|
+
middle_name: '',
|
|
23
|
+
last_name: 'Doe',
|
|
24
|
+
gender: 'F',
|
|
25
|
+
date_of_birth: '1990-01-15',
|
|
26
|
+
identification_type: 'id',
|
|
27
|
+
identification_number: 'CR5617849204955-8',
|
|
28
|
+
};
|
|
29
|
+
mockFetchClientRegistryData.mockResolvedValueOnce([client]);
|
|
30
|
+
|
|
31
|
+
const result = await fetchClientByCrId('CR5617849204955-8', 'loc-uuid-1');
|
|
32
|
+
|
|
33
|
+
expect(mockFetchClientRegistryData).toHaveBeenCalledWith({
|
|
34
|
+
identificationNumber: 'CR5617849204955-8',
|
|
35
|
+
identificationType: 'CR id',
|
|
36
|
+
locationUuid: 'loc-uuid-1',
|
|
37
|
+
});
|
|
38
|
+
expect(result?.first_name).toBe('Jane');
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('returns null when the search finds no matching client', async () => {
|
|
42
|
+
mockFetchClientRegistryData.mockResolvedValueOnce([]);
|
|
43
|
+
const result = await fetchClientByCrId('CR-MISSING', 'loc-uuid-1');
|
|
44
|
+
expect(result).toBeNull();
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('returns null when the search fails (graceful degradation)', async () => {
|
|
48
|
+
mockFetchClientRegistryData.mockRejectedValueOnce(new Error('Request failed with 404: Not found'));
|
|
49
|
+
const result = await fetchClientByCrId('CR-MISSING', 'loc-uuid-1');
|
|
50
|
+
expect(result).toBeNull();
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('returns null for an empty cr_id without searching', async () => {
|
|
54
|
+
const result = await fetchClientByCrId('', 'loc-uuid-1');
|
|
55
|
+
expect(result).toBeNull();
|
|
56
|
+
expect(mockFetchClientRegistryData).not.toHaveBeenCalled();
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('returns null for a missing location uuid without searching', async () => {
|
|
60
|
+
const result = await fetchClientByCrId('CR5617849204955-8', '');
|
|
61
|
+
expect(result).toBeNull();
|
|
62
|
+
expect(mockFetchClientRegistryData).not.toHaveBeenCalled();
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
describe('clientDisplayName', () => {
|
|
67
|
+
it('joins first, middle, last names', () => {
|
|
68
|
+
const client = {
|
|
69
|
+
first_name: 'Jane',
|
|
70
|
+
middle_name: 'Akinyi',
|
|
71
|
+
last_name: 'Doe',
|
|
72
|
+
} as any;
|
|
73
|
+
expect(clientDisplayName(client, 'CR123')).toBe('Jane Akinyi Doe');
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it('skips missing name parts', () => {
|
|
77
|
+
const client = {
|
|
78
|
+
first_name: 'Jane',
|
|
79
|
+
middle_name: '',
|
|
80
|
+
last_name: 'Doe',
|
|
81
|
+
} as any;
|
|
82
|
+
expect(clientDisplayName(client, 'CR123')).toBe('Jane Doe');
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it('falls back to cr_id when client is null', () => {
|
|
86
|
+
expect(clientDisplayName(null, 'CR123')).toBe('CR123');
|
|
87
|
+
});
|
|
88
|
+
});
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { fetchClientRegistryData } from '../registry/registry.resource';
|
|
2
|
+
import { HieIdentificationType, type HieClient } from '../registry/types';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Client Registry lookup for an EMT referral.
|
|
6
|
+
*
|
|
7
|
+
* The referral only carries a `cr_id` (the CR record's id). We fetch the full
|
|
8
|
+
* CR record so the queue can show a human-readable patient (name, sex, DOB,
|
|
9
|
+
* national ID, …) instead of a bare identifier.
|
|
10
|
+
*
|
|
11
|
+
* The CR is only queryable through its search endpoint, so we reuse the
|
|
12
|
+
* established `fetchClientRegistryData` search with
|
|
13
|
+
* `identificationType: CR_SEARCH_IDENTIFICATION_TYPE` ('CR id'). The search
|
|
14
|
+
* returns an array; a CR id is unique, so the first entry is the record we want.
|
|
15
|
+
*
|
|
16
|
+
* Lookups are done lazily per-row (on expand / on render of a small page) so a
|
|
17
|
+
* single failed or slow CR fetch never blocks the whole queue.
|
|
18
|
+
*/
|
|
19
|
+
export async function fetchClientByCrId(crId: string, locationUuid: string): Promise<HieClient | null> {
|
|
20
|
+
if (!crId || !locationUuid) return null;
|
|
21
|
+
try {
|
|
22
|
+
const result = await fetchClientRegistryData({
|
|
23
|
+
identificationNumber: crId,
|
|
24
|
+
identificationType: HieIdentificationType.Cr,
|
|
25
|
+
locationUuid,
|
|
26
|
+
});
|
|
27
|
+
return Array.isArray(result) && result.length > 0 ? (result[0] as HieClient) : null;
|
|
28
|
+
} catch {
|
|
29
|
+
// Graceful degradation: a missing CR record must not break the queue row.
|
|
30
|
+
// Callers surface "patient details unavailable" and keep the cr_id visible.
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** Display name from a CR record, falling back gracefully. */
|
|
36
|
+
export function clientDisplayName(client: HieClient | null | undefined, crId: string): string {
|
|
37
|
+
if (!client) return crId;
|
|
38
|
+
return [client.first_name, client.middle_name, client.last_name]
|
|
39
|
+
.filter(Boolean)
|
|
40
|
+
.join(' ')
|
|
41
|
+
.trim() || crId;
|
|
42
|
+
}
|