@kenyaemr/esm-billing-app 5.4.1-pre.1935 → 5.4.1-pre.1941
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/.turbo/turbo-build.log +70 -70
- package/dist/22.js +1 -0
- package/dist/22.js.map +1 -0
- package/dist/300.js +1 -1
- package/dist/423.js +1 -0
- package/dist/423.js.map +1 -0
- package/dist/917.js +2 -0
- package/dist/917.js.map +1 -0
- package/dist/kenyaemr-esm-billing-app.js +1 -1
- package/dist/kenyaemr-esm-billing-app.js.buildmanifest.json +84 -84
- package/dist/kenyaemr-esm-billing-app.js.map +1 -1
- package/dist/main.js +2 -2
- package/dist/main.js.map +1 -1
- package/dist/routes.json +1 -1
- package/package.json +1 -1
- package/src/benefits-package/benefits-package.resources.tsx +1 -1
- package/src/benefits-package/forms/benefit-pre-auth-form.workspace.tsx +8 -52
- package/src/benefits-package/forms/{package-interventions.component.tsx → interventions-form.component.tsx} +25 -13
- package/src/benefits-package/forms/packages-and-interventions-form.component.tsx +72 -0
- package/src/billable-services/billiable-item/order-actions/components/base-order-button.component.tsx +42 -0
- package/src/billable-services/billiable-item/order-actions/components/lab-order-button.component.tsx +51 -0
- package/src/billable-services/billiable-item/order-actions/components/medication-order-button.component.tsx +84 -0
- package/src/billable-services/billiable-item/order-actions/components/order-action-button.component.tsx +29 -0
- package/src/billable-services/billiable-item/order-actions/hooks/useBillStatus.ts +12 -0
- package/src/billable-services/billiable-item/order-actions/hooks/useLabOrderAction.ts +27 -0
- package/src/billable-services/billiable-item/order-actions/hooks/useMedicationOrderAction.ts +106 -0
- package/src/billable-services/billiable-item/order-actions/hooks/useModalHandler.ts +66 -0
- package/src/billable-services/billiable-item/order-actions/styles/order-action.scss +0 -0
- package/src/billing-form/billing-checkin-form.component.tsx +6 -1
- package/src/billing-form/check-in-form.utils.tsx +2 -0
- package/src/billing-form/visit-attributes/visit-attributes-form.component.tsx +13 -3
- package/src/claims/dashboard/form/claims-form.component.tsx +28 -59
- package/src/claims/dashboard/form/claims-form.resource.ts +18 -2
- package/src/config-schema.ts +6 -0
- package/src/constants.ts +1 -0
- package/src/hooks/useInterventions.ts +19 -21
- package/src/index.ts +2 -2
- package/src/routes.json +3 -3
- package/src/types/index.ts +1 -0
- package/translations/en.json +2 -5
- package/dist/138.js +0 -2
- package/dist/138.js.map +0 -1
- package/dist/202.js +0 -1
- package/dist/202.js.map +0 -1
- package/dist/614.js +0 -1
- package/dist/614.js.map +0 -1
- package/src/billable-services/billiable-item/test-order/test-order-action.component.tsx +0 -134
- package/src/billable-services/billiable-item/test-order/test-order-action.test.tsx +0 -178
- /package/dist/{138.js.LICENSE.txt → 917.js.LICENSE.txt} +0 -0
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { useTranslation } from 'react-i18next';
|
|
2
|
+
import { useStockItemQuantity } from '../../useBillableItem';
|
|
3
|
+
import { useBillableServices } from '../../../billable-service.resource';
|
|
4
|
+
|
|
5
|
+
import { useBillStatus } from './useBillStatus';
|
|
6
|
+
import { createMedicationDispenseProps } from '../../test-order/dispense.resource';
|
|
7
|
+
|
|
8
|
+
interface MedicationRequest {
|
|
9
|
+
request: fhir.MedicationRequest;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function useMedicationOrderAction(medicationRequestBundle?: MedicationRequest) {
|
|
13
|
+
const { t } = useTranslation();
|
|
14
|
+
const request = medicationRequestBundle?.request;
|
|
15
|
+
const orderUuid = request?.id;
|
|
16
|
+
const patientUuid = request?.subject?.reference?.split('/')[1];
|
|
17
|
+
const drugUuid = request?.medicationReference?.reference?.split('/')[1];
|
|
18
|
+
|
|
19
|
+
const { isLoading, hasPendingPayment, itemHasBill } = useBillStatus(orderUuid, patientUuid);
|
|
20
|
+
const { stockItemQuantity, stockItemUuid } = useStockItemQuantity(drugUuid);
|
|
21
|
+
const { billableServices } = useBillableServices();
|
|
22
|
+
|
|
23
|
+
const billableItem =
|
|
24
|
+
billableServices?.filter((service) => {
|
|
25
|
+
const stockItem = service?.stockItem.split(':')[0];
|
|
26
|
+
return stockItem === stockItemUuid;
|
|
27
|
+
}) || [];
|
|
28
|
+
|
|
29
|
+
const dispenseFormProps = medicationRequestBundle ? createMedicationDispenseProps({ medicationRequestBundle }) : null;
|
|
30
|
+
|
|
31
|
+
const shouldShowBillModal = stockItemQuantity > 0 && itemHasBill.length < 1 && billableItem.length > 0;
|
|
32
|
+
const isDisabled = hasPendingPayment || (stockItemQuantity < 1 && !!drugUuid);
|
|
33
|
+
|
|
34
|
+
// Modification is allowed only when:
|
|
35
|
+
// 1. There are no pending payments
|
|
36
|
+
// 2. Stock item is available (quantity > 0)
|
|
37
|
+
// 3. Item hasn't been billed yet (no existing bills)
|
|
38
|
+
// 4. Item exists in billable items list
|
|
39
|
+
const shouldAllowModify =
|
|
40
|
+
!hasPendingPayment && stockItemQuantity > 0 && itemHasBill.length < 1 && billableItem.length > 0;
|
|
41
|
+
|
|
42
|
+
const getButtonText = () => {
|
|
43
|
+
if (hasPendingPayment) {
|
|
44
|
+
return t('unsettledBill', 'Unsettled bill');
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (stockItemQuantity < 1 && drugUuid) {
|
|
48
|
+
return t('outOfStock', 'Out of Stock');
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (shouldShowBillModal) {
|
|
52
|
+
return t('bill', 'Bill');
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return t('dispense', 'Dispense');
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
return {
|
|
59
|
+
isLoading,
|
|
60
|
+
isDisabled,
|
|
61
|
+
buttonText: getButtonText(),
|
|
62
|
+
shouldShowBillModal,
|
|
63
|
+
dispenseFormProps,
|
|
64
|
+
orderUuid,
|
|
65
|
+
patientUuid,
|
|
66
|
+
billableItem,
|
|
67
|
+
itemHasBill,
|
|
68
|
+
medicationRequestBundle,
|
|
69
|
+
hasPendingPayment,
|
|
70
|
+
shouldAllowModify,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
import { useMemo } from 'react';
|
|
75
|
+
import { FetchResponse, openmrsFetch, restBaseUrl } from '@openmrs/esm-framework';
|
|
76
|
+
import { Order } from '@openmrs/esm-patient-common-lib';
|
|
77
|
+
import useSWR from 'swr';
|
|
78
|
+
|
|
79
|
+
const customRepresentation =
|
|
80
|
+
'custom:(uuid,dosingType,orderNumber,accessionNumber,' +
|
|
81
|
+
'patient:ref,action,careSetting:ref,previousOrder:ref,dateActivated,scheduledDate,dateStopped,autoExpireDate,' +
|
|
82
|
+
'orderType:ref,encounter:ref,orderer:(uuid,display,person:(display)),orderReason,orderReasonNonCoded,orderType,urgency,instructions,' +
|
|
83
|
+
'commentToFulfiller,drug:(uuid,display,strength,dosageForm:(display,uuid),concept),dose,doseUnits:ref,' +
|
|
84
|
+
'frequency:ref,asNeeded,asNeededCondition,quantity,quantityUnits:ref,numRefills,dosingInstructions,' +
|
|
85
|
+
'duration,durationUnits:ref,route:ref,brandName,dispenseAsWritten)';
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Hook to get a single order by UUID.
|
|
89
|
+
*
|
|
90
|
+
* @param orderUuid The UUID of the order to fetch.
|
|
91
|
+
*/
|
|
92
|
+
export function useOrderByUuid(orderUuid: string) {
|
|
93
|
+
const ordersUrl = useMemo(
|
|
94
|
+
() => (orderUuid ? `${restBaseUrl}/order/${orderUuid}?v=${customRepresentation}` : null),
|
|
95
|
+
[orderUuid],
|
|
96
|
+
);
|
|
97
|
+
const { data, error, isLoading, isValidating, mutate } = useSWR<FetchResponse<Order>, Error>(ordersUrl, openmrsFetch);
|
|
98
|
+
|
|
99
|
+
return {
|
|
100
|
+
data: data?.data,
|
|
101
|
+
error,
|
|
102
|
+
isLoading,
|
|
103
|
+
isValidating,
|
|
104
|
+
mutate,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { launchWorkspace } from '@openmrs/esm-framework';
|
|
2
|
+
import { Order } from '@openmrs/esm-patient-common-lib';
|
|
3
|
+
import { useCallback } from 'react';
|
|
4
|
+
import { mutate } from 'swr';
|
|
5
|
+
|
|
6
|
+
export function useModalHandler(mutateUrl?: string) {
|
|
7
|
+
const handleModalClose = useCallback(() => {
|
|
8
|
+
if (!mutateUrl) {
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
mutate((key) => typeof key === 'string' && key.startsWith(mutateUrl), undefined, {
|
|
13
|
+
revalidate: true,
|
|
14
|
+
});
|
|
15
|
+
}, [mutateUrl]);
|
|
16
|
+
|
|
17
|
+
return {
|
|
18
|
+
handleModalClose,
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export const launchPrescriptionEditWorkspace = (order: Order, patientUuid: string) => {
|
|
23
|
+
const newItem = {
|
|
24
|
+
uuid: order.uuid,
|
|
25
|
+
display: order.drug?.display,
|
|
26
|
+
previousOrder: order.uuid,
|
|
27
|
+
startDate: new Date(),
|
|
28
|
+
action: 'REVISE',
|
|
29
|
+
drug: order.drug,
|
|
30
|
+
dosage: order.dose,
|
|
31
|
+
unit: {
|
|
32
|
+
value: order.doseUnits?.display,
|
|
33
|
+
valueCoded: order.doseUnits?.uuid,
|
|
34
|
+
},
|
|
35
|
+
frequency: {
|
|
36
|
+
valueCoded: order.frequency?.uuid,
|
|
37
|
+
value: order.frequency?.display,
|
|
38
|
+
},
|
|
39
|
+
route: {
|
|
40
|
+
valueCoded: order.route?.uuid,
|
|
41
|
+
value: order.route?.display,
|
|
42
|
+
},
|
|
43
|
+
commonMedicationName: order.drug?.display,
|
|
44
|
+
isFreeTextDosage: order.dosingType === 'org.openmrs.FreeTextDosingInstructions',
|
|
45
|
+
freeTextDosage: order.dosingType === 'org.openmrs.FreeTextDosingInstructions' ? order.dosingInstructions : '',
|
|
46
|
+
patientInstructions: order.dosingType !== 'org.openmrs.FreeTextDosingInstructions' ? order.dosingInstructions : '',
|
|
47
|
+
asNeeded: order.asNeeded,
|
|
48
|
+
asNeededCondition: order.asNeededCondition,
|
|
49
|
+
duration: order.duration,
|
|
50
|
+
durationUnit: {
|
|
51
|
+
valueCoded: order.durationUnits?.uuid,
|
|
52
|
+
value: order.durationUnits?.display,
|
|
53
|
+
},
|
|
54
|
+
pillsDispensed: order.quantity,
|
|
55
|
+
numRefills: order.numRefills,
|
|
56
|
+
indication: order.orderReasonNonCoded,
|
|
57
|
+
orderer: order.orderer?.uuid,
|
|
58
|
+
careSetting: order.careSetting?.uuid,
|
|
59
|
+
quantityUnits: {
|
|
60
|
+
value: order.quantityUnits?.display,
|
|
61
|
+
valueCoded: order.quantityUnits?.uuid,
|
|
62
|
+
},
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
launchWorkspace('add-drug-order', { patientUuid, order: newItem });
|
|
66
|
+
};
|
|
File without changes
|
|
@@ -6,13 +6,14 @@ import { FormProvider, useForm } from 'react-hook-form';
|
|
|
6
6
|
import { useTranslation } from 'react-i18next';
|
|
7
7
|
import { createPatientBill, useBillableItems, useCashPoint } from '../billing.resource';
|
|
8
8
|
import { BillingConfig } from '../config-schema';
|
|
9
|
-
import { EXEMPTED_PAYMENT_STATUS, PENDING_PAYMENT_STATUS } from '../constants';
|
|
9
|
+
import { EXEMPTED_PAYMENT_STATUS, PENDING_PAYMENT_STATUS, SHA_INSURANCE_SCHEME } from '../constants';
|
|
10
10
|
import { BillingService } from '../types';
|
|
11
11
|
import styles from './billing-checkin-form.scss';
|
|
12
12
|
import { visitAttributesFormSchema, VisitAttributesFormValue } from './check-in-form.utils';
|
|
13
13
|
import { hasPatientBeenExempted } from './helper';
|
|
14
14
|
import SHANumberValidity from './social-health-authority/sha-number-validity.component';
|
|
15
15
|
import VisitAttributesForm from './visit-attributes/visit-attributes-form.component';
|
|
16
|
+
import SHABenefitPackangesAndInterventions from '../benefits-package/forms/packages-and-interventions-form.component';
|
|
16
17
|
|
|
17
18
|
type BillingCheckInFormProps = {
|
|
18
19
|
patientUuid: string;
|
|
@@ -36,11 +37,14 @@ const BillingCheckInForm: React.FC<BillingCheckInFormProps> = ({ patientUuid, se
|
|
|
36
37
|
insuranceScheme: '',
|
|
37
38
|
policyNumber: '',
|
|
38
39
|
exemptionCategory: '',
|
|
40
|
+
interventions: [],
|
|
41
|
+
packages: [],
|
|
39
42
|
},
|
|
40
43
|
resolver: zodResolver(visitAttributesFormSchema),
|
|
41
44
|
});
|
|
42
45
|
const isPatientExemptedValue = formMethods.watch('isPatientExempted');
|
|
43
46
|
const paymentMethod = formMethods.watch('paymentMethods');
|
|
47
|
+
const isInsuranceSchemeSha = formMethods.watch('insuranceScheme') === SHA_INSURANCE_SCHEME;
|
|
44
48
|
|
|
45
49
|
const handleCreateBill = useCallback((createBillPayload) => {
|
|
46
50
|
createPatientBill(createBillPayload).then(
|
|
@@ -125,6 +129,7 @@ const BillingCheckInForm: React.FC<BillingCheckInFormProps> = ({ patientUuid, se
|
|
|
125
129
|
<FormProvider {...formMethods}>
|
|
126
130
|
<VisitAttributesForm setAttributes={setAttributes} />
|
|
127
131
|
{hieFeatureFlags && <SHANumberValidity paymentMethod={attributes} patientUuid={patientUuid} />}
|
|
132
|
+
{hieFeatureFlags && isInsuranceSchemeSha && <SHABenefitPackangesAndInterventions patientUuid={patientUuid} />}
|
|
128
133
|
{paymentMethod && (
|
|
129
134
|
<section className={styles.sectionContainer}>
|
|
130
135
|
<div className={styles.sectionTitle}>{t('billing', 'Billing')}</div>
|
|
@@ -6,6 +6,8 @@ export const visitAttributesFormSchema = z.object({
|
|
|
6
6
|
insuranceScheme: z.string().optional(),
|
|
7
7
|
policyNumber: z.string().optional(),
|
|
8
8
|
exemptionCategory: z.string().optional(),
|
|
9
|
+
packages: z.array(z.string()).nonempty('Require atleast 1 package'),
|
|
10
|
+
interventions: z.array(z.string()).nonempty('Require atleast 1 intervention'),
|
|
9
11
|
});
|
|
10
12
|
|
|
11
13
|
export type VisitAttributesFormValue = z.infer<typeof visitAttributesFormSchema>;
|
|
@@ -16,7 +16,7 @@ const VisitAttributesForm: React.FC<VisitAttributesFormProps> = ({ setAttributes
|
|
|
16
16
|
const { t } = useTranslation();
|
|
17
17
|
const { insuranceSchemes } = useConfig<BillingConfig>();
|
|
18
18
|
const { visitAttributeTypes, patientExemptionCategories, insurancePaymentMethod } = useConfig<BillingConfig>();
|
|
19
|
-
const { setValue, watch, control, getValues } = useFormContext<VisitAttributesFormValue>();
|
|
19
|
+
const { setValue, watch, control, getValues, resetField } = useFormContext<VisitAttributesFormValue>();
|
|
20
20
|
const { paymentModes, isLoading: isLoadingPaymentModes } = usePaymentModes();
|
|
21
21
|
const [isPatientExempted, paymentMethods] = watch(['isPatientExempted', 'paymentMethods']);
|
|
22
22
|
const resetFormFieldsForNonExemptedPatients = useCallback(() => {
|
|
@@ -24,7 +24,9 @@ const VisitAttributesForm: React.FC<VisitAttributesFormProps> = ({ setAttributes
|
|
|
24
24
|
setValue('policyNumber', '');
|
|
25
25
|
setValue('exemptionCategory', '');
|
|
26
26
|
setValue('paymentMethods', '');
|
|
27
|
-
|
|
27
|
+
resetField('packages');
|
|
28
|
+
resetField('interventions');
|
|
29
|
+
}, [setValue, resetField]);
|
|
28
30
|
|
|
29
31
|
useEffect(() => {
|
|
30
32
|
resetFormFieldsForNonExemptedPatients();
|
|
@@ -38,6 +40,10 @@ const VisitAttributesForm: React.FC<VisitAttributesFormProps> = ({ setAttributes
|
|
|
38
40
|
{ uuid: visitAttributeTypes.policyNumber, value: values.policyNumber },
|
|
39
41
|
{ uuid: visitAttributeTypes.insuranceScheme, value: values.insuranceScheme },
|
|
40
42
|
{ uuid: visitAttributeTypes.exemptionCategory, value: values.exemptionCategory },
|
|
43
|
+
{
|
|
44
|
+
uuid: visitAttributeTypes.shaBenefitPackagesAndInterventions,
|
|
45
|
+
value: JSON.stringify({ packages: values.packages, interventions: values.interventions }),
|
|
46
|
+
},
|
|
41
47
|
];
|
|
42
48
|
const visitAttributesPayload = formPayload.filter(
|
|
43
49
|
(item) => item.value !== undefined && item.value !== null && item.value !== '',
|
|
@@ -48,10 +54,12 @@ const VisitAttributesForm: React.FC<VisitAttributesFormProps> = ({ setAttributes
|
|
|
48
54
|
}));
|
|
49
55
|
}, [visitAttributeTypes, getValues]);
|
|
50
56
|
|
|
51
|
-
const [policyNumber, exemptionCategory, insuranceScheme] = watch([
|
|
57
|
+
const [policyNumber, exemptionCategory, insuranceScheme, interventions, packages] = watch([
|
|
52
58
|
'policyNumber',
|
|
53
59
|
'exemptionCategory',
|
|
54
60
|
'insuranceScheme',
|
|
61
|
+
'interventions',
|
|
62
|
+
'packages',
|
|
55
63
|
]);
|
|
56
64
|
|
|
57
65
|
useEffect(() => {
|
|
@@ -64,6 +72,8 @@ const VisitAttributesForm: React.FC<VisitAttributesFormProps> = ({ setAttributes
|
|
|
64
72
|
policyNumber,
|
|
65
73
|
exemptionCategory,
|
|
66
74
|
insuranceScheme,
|
|
75
|
+
interventions,
|
|
76
|
+
packages,
|
|
67
77
|
]);
|
|
68
78
|
|
|
69
79
|
if (isLoadingPaymentModes) {
|
|
@@ -15,21 +15,21 @@ import {
|
|
|
15
15
|
TextInputSkeleton,
|
|
16
16
|
} from '@carbon/react';
|
|
17
17
|
import { zodResolver } from '@hookform/resolvers/zod';
|
|
18
|
-
import { navigate, showSnackbar, useSession } from '@openmrs/esm-framework';
|
|
19
|
-
import React, { useEffect, useState } from 'react';
|
|
18
|
+
import { navigate, showSnackbar, useConfig, useSession } from '@openmrs/esm-framework';
|
|
19
|
+
import React, { useEffect, useMemo, useState } from 'react';
|
|
20
20
|
import { Controller, FormProvider, useForm } from 'react-hook-form';
|
|
21
21
|
import { useTranslation } from 'react-i18next';
|
|
22
22
|
import { useParams } from 'react-router-dom';
|
|
23
23
|
import { z } from 'zod';
|
|
24
|
-
import
|
|
24
|
+
import { BillingConfig } from '../../../config-schema';
|
|
25
25
|
import { formatDate } from '../../../helpers/functions';
|
|
26
26
|
import { useSystemSetting } from '../../../hooks/getMflCode';
|
|
27
|
-
import usePackages from '../../../hooks/usePackages';
|
|
28
27
|
import usePatientDiagnosis from '../../../hooks/usePatientDiagnosis';
|
|
28
|
+
import useProvider from '../../../hooks/useProvider';
|
|
29
29
|
import { LineItem, MappedBill } from '../../../types';
|
|
30
|
-
import { processClaims, useVisit } from './claims-form.resource';
|
|
30
|
+
import { processClaims, SHAPackagesAndInterventionVisitAttribute, useVisit } from './claims-form.resource';
|
|
31
31
|
import styles from './claims-form.scss';
|
|
32
|
-
import
|
|
32
|
+
import SHABenefitPackangesAndInterventions from '../../../benefits-package/forms/packages-and-interventions-form.component';
|
|
33
33
|
|
|
34
34
|
type ClaimsFormProps = {
|
|
35
35
|
bill: MappedBill;
|
|
@@ -44,7 +44,7 @@ const ClaimsFormSchema = z.object({
|
|
|
44
44
|
facility: z.string().nonempty({ message: 'Facility is required' }),
|
|
45
45
|
treatmentStart: z.string().nonempty({ message: 'Treatment start date is required' }),
|
|
46
46
|
treatmentEnd: z.string().nonempty({ message: 'Treatment end date is required' }),
|
|
47
|
-
|
|
47
|
+
packages: z.array(z.string()).nonempty({ message: 'At least one package is required' }),
|
|
48
48
|
interventions: z.array(z.string()).min(1, {
|
|
49
49
|
message: 'At least one intervention is required',
|
|
50
50
|
}),
|
|
@@ -57,11 +57,23 @@ const ClaimsForm: React.FC<ClaimsFormProps> = ({ bill, selectedLineItems }) => {
|
|
|
57
57
|
const { patientUuid, billUuid } = useParams();
|
|
58
58
|
const { visits: recentVisit, error: visitError, isLoading: visitLoading } = useVisit(patientUuid);
|
|
59
59
|
const { diagnoses, error: diagnosisError, isLoading: diagnosisLoading } = usePatientDiagnosis(patientUuid);
|
|
60
|
-
const { error: packagesError, isLoading: packagesLoading, packages } = usePackages();
|
|
61
60
|
const {
|
|
62
61
|
currentProvider: { uuid: providerUuid },
|
|
63
62
|
} = useSession();
|
|
64
63
|
const { providerLoading: providerLoading, provider, error: providerError } = useProvider(providerUuid);
|
|
64
|
+
const { visitAttributeTypes } = useConfig<BillingConfig>();
|
|
65
|
+
const packagesAndinterventions = useMemo(() => {
|
|
66
|
+
if (recentVisit) {
|
|
67
|
+
const values = recentVisit.attributes?.find(
|
|
68
|
+
(attr) => attr.attributeType.uuid === visitAttributeTypes.shaBenefitPackagesAndInterventions,
|
|
69
|
+
)?.value;
|
|
70
|
+
if (values) {
|
|
71
|
+
const payload: SHAPackagesAndInterventionVisitAttribute = JSON.parse(values);
|
|
72
|
+
return payload;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return null;
|
|
76
|
+
}, [recentVisit, visitAttributeTypes]);
|
|
65
77
|
|
|
66
78
|
const encounterUuid = recentVisit?.encounters[0]?.uuid;
|
|
67
79
|
const visitTypeUuid = recentVisit?.visitType.uuid;
|
|
@@ -85,7 +97,7 @@ const ClaimsForm: React.FC<ClaimsFormProps> = ({ bill, selectedLineItems }) => {
|
|
|
85
97
|
facility: `${recentVisit?.location?.display || ''} - ${mflCodeValue || ''}`,
|
|
86
98
|
treatmentStart: recentVisit?.startDatetime ? formatDate(recentVisit.startDatetime) : '',
|
|
87
99
|
treatmentEnd: recentVisit?.stopDatetime ? formatDate(recentVisit.stopDatetime) : '',
|
|
88
|
-
|
|
100
|
+
packages: [],
|
|
89
101
|
interventions: [],
|
|
90
102
|
provider: providerUuid,
|
|
91
103
|
},
|
|
@@ -99,11 +111,6 @@ const ClaimsForm: React.FC<ClaimsFormProps> = ({ bill, selectedLineItems }) => {
|
|
|
99
111
|
reset,
|
|
100
112
|
} = form;
|
|
101
113
|
|
|
102
|
-
const handleInterventionsChange = (e: { selectedItem: string }) => {
|
|
103
|
-
form.setValue('interventions', []);
|
|
104
|
-
setValue('package', e.selectedItem);
|
|
105
|
-
};
|
|
106
|
-
|
|
107
114
|
const onSubmit = async (data: z.infer<typeof ClaimsFormSchema>) => {
|
|
108
115
|
setLoading(true);
|
|
109
116
|
const providedItems = selectedLineItems.reduce((acc, item) => {
|
|
@@ -139,7 +146,7 @@ const ClaimsForm: React.FC<ClaimsFormProps> = ({ bill, selectedLineItems }) => {
|
|
|
139
146
|
use: 'claim',
|
|
140
147
|
insurer: 'SHA',
|
|
141
148
|
billNumber: billUuid,
|
|
142
|
-
packages:
|
|
149
|
+
packages: data.packages,
|
|
143
150
|
interventions: data.interventions,
|
|
144
151
|
};
|
|
145
152
|
try {
|
|
@@ -170,7 +177,6 @@ const ClaimsForm: React.FC<ClaimsFormProps> = ({ bill, selectedLineItems }) => {
|
|
|
170
177
|
setLoading(false);
|
|
171
178
|
}
|
|
172
179
|
};
|
|
173
|
-
const selectedPackage = packages.find((package_) => package_.uuid === form.watch('package'))?.packageCode ?? '';
|
|
174
180
|
|
|
175
181
|
useEffect(() => {
|
|
176
182
|
setValue('diagnoses', diagnoses?.map((d) => d.id) ?? ([] as any));
|
|
@@ -178,9 +184,11 @@ const ClaimsForm: React.FC<ClaimsFormProps> = ({ bill, selectedLineItems }) => {
|
|
|
178
184
|
setValue('facility', `${recentVisit?.location?.display || ''} - ${mflCodeValue || ''}`);
|
|
179
185
|
setValue('treatmentStart', recentVisit?.startDatetime ? formatDate(recentVisit.startDatetime) : '');
|
|
180
186
|
setValue('treatmentEnd', recentVisit?.stopDatetime ? formatDate(recentVisit.stopDatetime) : '');
|
|
181
|
-
|
|
187
|
+
setValue('packages', (packagesAndinterventions?.packages ?? []) as any);
|
|
188
|
+
setValue('interventions', (packagesAndinterventions?.interventions ?? []) as any);
|
|
189
|
+
}, [diagnoses, recentVisit, mflCodeValue, setValue, provider, packagesAndinterventions]);
|
|
182
190
|
|
|
183
|
-
if (visitLoading || diagnosisLoading ||
|
|
191
|
+
if (visitLoading || diagnosisLoading || providerLoading) {
|
|
184
192
|
return (
|
|
185
193
|
<Layer className={styles.loading}>
|
|
186
194
|
{Array.from({ length: 6 }).map((_, index) => (
|
|
@@ -190,7 +198,7 @@ const ClaimsForm: React.FC<ClaimsFormProps> = ({ bill, selectedLineItems }) => {
|
|
|
190
198
|
);
|
|
191
199
|
}
|
|
192
200
|
|
|
193
|
-
if (visitError || diagnosisError ||
|
|
201
|
+
if (visitError || diagnosisError || providerError) {
|
|
194
202
|
return (
|
|
195
203
|
<Layer className={styles.loading}>
|
|
196
204
|
<InlineNotification
|
|
@@ -198,7 +206,6 @@ const ClaimsForm: React.FC<ClaimsFormProps> = ({ bill, selectedLineItems }) => {
|
|
|
198
206
|
subtitle={
|
|
199
207
|
visitError?.message ??
|
|
200
208
|
diagnosisError?.message ??
|
|
201
|
-
packagesError?.message ??
|
|
202
209
|
providerError?.message ??
|
|
203
210
|
'Error occured while loading claims form'
|
|
204
211
|
}
|
|
@@ -284,45 +291,7 @@ const ClaimsForm: React.FC<ClaimsFormProps> = ({ bill, selectedLineItems }) => {
|
|
|
284
291
|
</Layer>
|
|
285
292
|
</Column>
|
|
286
293
|
</Row>
|
|
287
|
-
<
|
|
288
|
-
<Layer className={styles.input}>
|
|
289
|
-
<Controller
|
|
290
|
-
control={form.control}
|
|
291
|
-
name="package"
|
|
292
|
-
render={({ field }) => (
|
|
293
|
-
<>
|
|
294
|
-
{packagesError ? (
|
|
295
|
-
<InlineNotification
|
|
296
|
-
kind="error"
|
|
297
|
-
subtitle={t('errorFetchingPackages', 'Error fetching packeges')}
|
|
298
|
-
lowContrast
|
|
299
|
-
/>
|
|
300
|
-
) : (
|
|
301
|
-
<Dropdown
|
|
302
|
-
ref={field.ref}
|
|
303
|
-
invalid={form.formState.errors[field.name]?.message}
|
|
304
|
-
invalidText={form.formState.errors[field.name]?.message}
|
|
305
|
-
id="package"
|
|
306
|
-
titleText={t('package', 'Package')}
|
|
307
|
-
onChange={handleInterventionsChange}
|
|
308
|
-
initialSelectedItem={field.value}
|
|
309
|
-
label="Choose package"
|
|
310
|
-
items={packages.map((r) => r.uuid)}
|
|
311
|
-
itemToString={(item) => packages.find((r) => r.uuid === item)?.packageName ?? ''}
|
|
312
|
-
/>
|
|
313
|
-
)}
|
|
314
|
-
</>
|
|
315
|
-
)}
|
|
316
|
-
/>
|
|
317
|
-
</Layer>
|
|
318
|
-
</Column>
|
|
319
|
-
|
|
320
|
-
<Column>
|
|
321
|
-
<Layer className={styles.input}>
|
|
322
|
-
<PackageInterventions key={selectedPackage} category={selectedPackage} patientUuid={patientUuid} />
|
|
323
|
-
</Layer>
|
|
324
|
-
</Column>
|
|
325
|
-
|
|
294
|
+
<SHABenefitPackangesAndInterventions patientUuid={patientUuid} />
|
|
326
295
|
<Column>
|
|
327
296
|
<Layer className={styles.input}>
|
|
328
297
|
<Controller
|
|
@@ -13,11 +13,27 @@ interface ProvidersResponse {
|
|
|
13
13
|
results: Provider[];
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
+
interface ExtendedVisit extends Visit {
|
|
17
|
+
attributes: Array<{
|
|
18
|
+
uuid: string;
|
|
19
|
+
display: string;
|
|
20
|
+
attributeType: {
|
|
21
|
+
uuid: string;
|
|
22
|
+
display: string;
|
|
23
|
+
};
|
|
24
|
+
value: string;
|
|
25
|
+
}>;
|
|
26
|
+
}
|
|
27
|
+
export interface SHAPackagesAndInterventionVisitAttribute {
|
|
28
|
+
packages: Array<string>;
|
|
29
|
+
interventions: Array<string>;
|
|
30
|
+
}
|
|
31
|
+
|
|
16
32
|
export function useVisit(patientUuid: string) {
|
|
17
33
|
const customRepresentation =
|
|
18
|
-
'custom:(uuid,patient,encounters:(uuid,diagnoses:(uuid,display,certainty,diagnosis:(coded:(uuid,display))),encounterDatetime,encounterType:(uuid,display),encounterProviders:(uuid,display,provider:(uuid,person:(uuid,display)))),location:(uuid,name,display),visitType:(uuid,name,display),startDatetime,stopDatetime)&limit=1';
|
|
34
|
+
'custom:(uuid,patient,encounters:(uuid,diagnoses:(uuid,display,certainty,diagnosis:(coded:(uuid,display))),encounterDatetime,encounterType:(uuid,display),encounterProviders:(uuid,display,provider:(uuid,person:(uuid,display)))),location:(uuid,name,display),visitType:(uuid,name,display),startDatetime,stopDatetime,attributes)&limit=1';
|
|
19
35
|
|
|
20
|
-
const { data, error, isLoading, isValidating, mutate } = useSWR<{ data: { results: Array<
|
|
36
|
+
const { data, error, isLoading, isValidating, mutate } = useSWR<{ data: { results: Array<ExtendedVisit> } }, Error>(
|
|
21
37
|
`${restBaseUrl}/visit?patient=${patientUuid}&v=${customRepresentation}`,
|
|
22
38
|
openmrsFetch,
|
|
23
39
|
);
|
package/src/config-schema.ts
CHANGED
|
@@ -8,6 +8,7 @@ export interface BillingConfig {
|
|
|
8
8
|
policyNumber: string;
|
|
9
9
|
exemptionCategory: string;
|
|
10
10
|
billPaymentStatus: string;
|
|
11
|
+
shaBenefitPackagesAndInterventions: string;
|
|
11
12
|
};
|
|
12
13
|
insurancePaymentMethod: string;
|
|
13
14
|
inPatientVisitTypeUuid: string;
|
|
@@ -96,6 +97,11 @@ export const configSchema: ConfigSchema = {
|
|
|
96
97
|
_description: 'The bill payment status visit attribute uuid',
|
|
97
98
|
_default: '919b51c9-8e2e-468f-8354-181bf3e55786',
|
|
98
99
|
},
|
|
100
|
+
shaBenefitPackagesAndInterventions: {
|
|
101
|
+
_type: Type.String,
|
|
102
|
+
_description: 'JSON String of SHA Benefit Packages and Interventions for this visit',
|
|
103
|
+
_default: '338725fa-3790-4679-98b9-be623214ee29',
|
|
104
|
+
},
|
|
99
105
|
},
|
|
100
106
|
insurancePaymentMethod: {
|
|
101
107
|
_type: Type.String,
|
package/src/constants.ts
CHANGED
|
@@ -10,20 +10,6 @@ export type InterventionsFilter = {
|
|
|
10
10
|
applicable_gender?: 'MALE' | 'FEMALE';
|
|
11
11
|
};
|
|
12
12
|
|
|
13
|
-
export const toQueryParams = (q: Record<string, any>) => {
|
|
14
|
-
return (
|
|
15
|
-
'?' +
|
|
16
|
-
Object.entries(q)
|
|
17
|
-
.reduce((prev, [key, val]) => {
|
|
18
|
-
if (val !== undefined && val !== null) {
|
|
19
|
-
return [...prev, `${key}=${val}`];
|
|
20
|
-
}
|
|
21
|
-
return prev;
|
|
22
|
-
}, [])
|
|
23
|
-
.join('&')
|
|
24
|
-
);
|
|
25
|
-
};
|
|
26
|
-
|
|
27
13
|
type Data = {
|
|
28
14
|
status: string;
|
|
29
15
|
data: Array<{
|
|
@@ -56,17 +42,18 @@ export const useInterventions = (filters: InterventionsFilter) => {
|
|
|
56
42
|
};
|
|
57
43
|
|
|
58
44
|
const { error: facilityLevelError, isLoading: isLoadingFacilityLevel, level } = useFacilityLevel();
|
|
59
|
-
|
|
60
|
-
const url = `${restBaseUrl}/kenyaemr/sha-interventions${toQueryParams({
|
|
45
|
+
const urlParams = new URLSearchParams({
|
|
61
46
|
...filters,
|
|
62
|
-
synchronize: false,
|
|
63
|
-
})
|
|
47
|
+
synchronize: 'false',
|
|
48
|
+
});
|
|
49
|
+
const url = `${restBaseUrl}/kenyaemr/sha-interventions?${urlParams.toString()}`;
|
|
64
50
|
const { isLoading, error, data } = useSWR<FetchResponse<Data>>(url, openmrsFetch);
|
|
65
51
|
const interventions = useMemo(() => {
|
|
52
|
+
const packageCodes = filters.package_code?.split(',') || [];
|
|
66
53
|
return data?.data?.data
|
|
67
54
|
?.filter((d) => {
|
|
68
55
|
// 1. Filter by package code (only if defined)
|
|
69
|
-
if (
|
|
56
|
+
if (packageCodes.length > 0 && !packageCodes.includes(d.interventionPackage)) {
|
|
70
57
|
return false;
|
|
71
58
|
}
|
|
72
59
|
|
|
@@ -106,12 +93,23 @@ export const useInterventions = (filters: InterventionsFilter) => {
|
|
|
106
93
|
interventionCode,
|
|
107
94
|
subCategoryBenefitsPackage: interventionSubPackage,
|
|
108
95
|
interventionName,
|
|
96
|
+
interventionPackage,
|
|
109
97
|
})) as SHAIntervention[];
|
|
110
98
|
}, [data, filters, level]); // Ensure proper memoization
|
|
111
|
-
|
|
99
|
+
const allInterventions = useMemo(() => {
|
|
100
|
+
return (data?.data?.data ?? []).map(
|
|
101
|
+
({ interventionCode, interventionName, interventionPackage, interventionSubPackage }) => ({
|
|
102
|
+
interventionCode,
|
|
103
|
+
subCategoryBenefitsPackage: interventionSubPackage,
|
|
104
|
+
interventionName,
|
|
105
|
+
interventionPackage,
|
|
106
|
+
}),
|
|
107
|
+
) as SHAIntervention[];
|
|
108
|
+
}, [data]);
|
|
112
109
|
return {
|
|
113
110
|
isLoading: isLoading || isLoadingFacilityLevel,
|
|
114
|
-
interventions,
|
|
111
|
+
interventions: interventions ?? [],
|
|
112
|
+
allInterventions,
|
|
115
113
|
error: error || facilityLevelError,
|
|
116
114
|
};
|
|
117
115
|
};
|
package/src/index.ts
CHANGED
|
@@ -23,7 +23,6 @@ import ImagingOrder from './billable-services/billiable-item/test-order/imaging-
|
|
|
23
23
|
import LabOrder from './billable-services/billiable-item/test-order/lab-order.component';
|
|
24
24
|
import PriceInfoOrder from './billable-services/billiable-item/test-order/price-info-order.componet';
|
|
25
25
|
import ProcedureOrder from './billable-services/billiable-item/test-order/procedure-order.component';
|
|
26
|
-
import TestOrderAction from './billable-services/billiable-item/test-order/test-order-action.component';
|
|
27
26
|
import { BulkImportBillableServices } from './billable-services/bulk-import-billable-service.modal';
|
|
28
27
|
import BillingCheckInForm from './billing-form/billing-checkin-form.component';
|
|
29
28
|
import BillingForm from './billing-form/billing-form.component';
|
|
@@ -42,6 +41,7 @@ import { ClockIn } from './payment-points/payment-point/clock-in.modal';
|
|
|
42
41
|
import { ClockOut } from './payment-points/payment-point/clock-out.modal';
|
|
43
42
|
import RequirePaymentModal from './prompt-payment/prompt-payment-modal.component';
|
|
44
43
|
import rootComponent from './root.component';
|
|
44
|
+
import OrderActionButton from './billable-services/billiable-item/order-actions/components/order-action-button.component';
|
|
45
45
|
|
|
46
46
|
const moduleName = '@kenyaemr/esm-billing-app';
|
|
47
47
|
|
|
@@ -169,7 +169,7 @@ export const priceInfoOrder = getSyncLifecycle(PriceInfoOrder, options);
|
|
|
169
169
|
export const procedureOrder = getSyncLifecycle(ProcedureOrder, options);
|
|
170
170
|
export const imagingOrder = getSyncLifecycle(ImagingOrder, options);
|
|
171
171
|
export const drugOrder = getSyncLifecycle(DrugOrder, options);
|
|
172
|
-
export const
|
|
172
|
+
export const orderActionButton = getSyncLifecycle(OrderActionButton, options);
|
|
173
173
|
|
|
174
174
|
// bill manager modals
|
|
175
175
|
export const deleteBillModal = getSyncLifecycle(DeleteBillModal, options);
|
package/src/routes.json
CHANGED
|
@@ -135,9 +135,9 @@
|
|
|
135
135
|
"slot": "medication-info-slot"
|
|
136
136
|
},
|
|
137
137
|
{
|
|
138
|
-
"name": "
|
|
139
|
-
"component": "
|
|
140
|
-
"slot": "
|
|
138
|
+
"name": "order-action-button",
|
|
139
|
+
"component": "orderActionButton",
|
|
140
|
+
"slot": "prescription-action-button-slot",
|
|
141
141
|
"order": 0
|
|
142
142
|
},
|
|
143
143
|
{
|
package/src/types/index.ts
CHANGED