@kenyaemr/esm-billing-app 5.4.1-pre.2108 → 5.4.1-pre.2113
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 +81 -81
- package/dist/574.js +1 -1
- package/dist/912.js +1 -1
- package/dist/912.js.map +1 -1
- package/dist/kenyaemr-esm-billing-app.js.buildmanifest.json +9 -9
- 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/claims/dashboard/table/claims-table.component.tsx +14 -35
- package/src/hooks/useInterventions.ts +13 -13
- package/src/hooks/usePaymentSchema.tsx +0 -1
- package/src/invoice/invoice-table.component.tsx +1 -1
- package/src/invoice/payments/payment-form/payment-form.component.tsx +56 -107
- package/src/invoice/payments/payment-form/payment-form.scss +2 -5
- package/src/invoice/payments/payments.component.tsx +2 -6
- package/src/invoice/payments/payments.test.tsx +10 -42
- package/src/invoice/payments/utils.ts +0 -2
- package/src/types/index.ts +1 -24
- package/src/utils.ts +0 -6
- package/translations/en.json +1 -4
- package/src/hooks/payload.json +0 -20824
package/dist/routes.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"$schema":"https://json.openmrs.org/routes.schema.json","backendDependencies":{"kenyaemr":"^19.0.0"},"pages":[{"component":"billableServicesHome","route":"billable-services"},{"component":"requirePaymentModal","routeRegex":"^patient/.+/chart","online":true,"offline":false}],"extensions":[{"component":"billingDashboardLink","name":"billing-dashboard-link","slot":"homepage-dashboard-slot","meta":{"name":"billing","title":"billing","slot":"billing-dashboard-slot"}},{"component":"benefitsPackageDashboardLink","name":"benefits-package-dashboard-link","slot":"patient-chart-dashboard-slot","meta":{"name":"benefits-package","slot":"patient-chart-benefits-dashboard-slot","path":"insurance-benefits","columns":1,"columnSpan":1},"featureFlag":"healthInformationExchange"},{"component":"benefitsPackage","name":"benefits-package","slot":"patient-chart-benefits-dashboard-slot"},{"component":"root","name":"billing-dashboard-root","slot":"billing-dashboard-slot"},{"component":"benefitsEligibilyRequestForm","name":"benefits-eligibility-request-form"},{"component":"benefitsPreAuthForm","name":"benefits-pre-auth-form"},{"name":"billing-patient-summary","component":"billingPatientSummary","slot":"patient-chart-billing-dashboard-slot","order":10,"meta":{"columnSpan":4}},{"name":"billing-summary-dashboard-link","component":"billingSummaryDashboardLink","slot":"patient-chart-dashboard-slot","order":11,"meta":{"columns":1,"columnSpan":1,"slot":"patient-chart-billing-dashboard-slot","path":"Billing","layoutMode":"anchored"}},{"name":"billing-check-in-form","slot":"extra-visit-attribute-slot","component":"billingCheckInForm"},{"name":"require-billing-modal","component":"requirePaymentModal"},{"name":"patient-banner-billing-tags","component":"visitAttributeTags","slot":"patient-banner-tags-slot","order":2},{"name":"initiate-payment-modal","component":"initiatePaymentDialog"},{"name":"delete-billableservice-modal","component":"deleteBillableServiceModal"},{"name":"refund-bill-modal","component":"refundBillModal"},{"name":"delete-bill-modal","component":"deleteBillModal"},{"name":"lab-order-billable-item","component":"labOrder","slot":"top-of-lab-order-form-slot"},{"name":"procedure-order-billable-item","component":"procedureOrder","slot":"top-of-procedure-order-form-slot"},{"name":"imaging-order-billable-item","component":"imagingOrder","slot":"top-of-imaging-order-form-slot"},{"name":"price-info-order","component":"priceInfoOrder"},{"name":"drug-order-billable-item","component":"drugOrder","slot":"medication-info-slot"},{"name":"order-action-button","component":"orderActionButton","slots":["prescription-action-button-slot","imaging-orders-action","procedure-orders-action","tests-ordered-actions-slot"],"order":0},{"component":"billingOverviewLink","name":"billing-overview-link","order":0,"slot":"billing-dashboard-link-slot"},{"component":"paymentHistoryLink","name":"payment-history-link","slot":"billing-dashboard-link-slot"},{"component":"paymentPointsLink","name":"payment-points-link","slot":"billing-dashboard-link-slot"},{"component":"paymentModesLink","name":"payment-modes-link","slot":"billing-dashboard-link-slot"},{"component":"billManagerLink","name":"bill-manager-link","slot":"billing-dashboard-link-slot"},{"component":"chargeableItemsLink","name":"chargeable-items-link","slot":"billing-dashboard-link-slot"},{"component":"billableExemptionsLink","name":"billable-exemptions-link","slot":"billing-dashboard-link-slot"},{"component":"claimsManagementSideNavGroup","name":"claims-management-dashboard-link","slot":"homepage-dashboard-slot","meta":{"name":"claims-management","title":"Claims management Overview","slot":"case-management-slot"},"featureFlag":"healthInformationExchange"},{"component":"claimsManagementOverviewDashboardLink","name":"claims-management-overview-link","order":0,"slot":"claims-management-dashboard-link-slot"},{"component":"preAuthRequestsDashboardLink","name":"preauthrequest-overview-link","slot":"claims-management-dashboard-link-slot"},{"component":"claimsOverview","name":"claims-overview-dashboard-link","slot":"claims-management-overview-slot"},{"component":"waiveBillActionButton","name":"waive-bill-action-button","slot":"bill-actions-slot"},{"component":"deleteBillActionButton","name":"delete-bill-action-button","slot":"bill-actions-slot"},{"component":"refundLineItem","name":"refund-line-item","slot":"bill-actions-overflow-menu-slot"},{"name":"edit-line-item","component":"editLineItem","slot":"bill-actions-overflow-menu-slot"},{"name":"cancel-line-item","component":"cancelLineItem","slot":"bill-actions-overflow-menu-slot"}],"workspaces":[{"name":"create-bill-workspace","component":"createBillWorkspace","title":"Create Bill Workspace","type":"other-form"},{"name":"waive-bill-form","component":"waiveBillForm","title":"Waive Bill Form","type":"other-form"},{"name":"edit-bill-form","component":"editBillForm","title":"Edit Bill Form","type":"other-form"},{"name":"billable-service-form","component":"addServiceForm","title":"Create Charge Item Form","type":"other-form"},{"name":"commodity-form","component":"addCommodityForm","title":"Create Charge Item Form","type":"other-form"},{"name":"billing-form","component":"billingForm","title":"Billing Form","type":"other-form","width":"extra-wide"},{"name":"payment-mode-workspace","component":"paymentModeWorkspace","title":"Payment Mode Workspace","type":"other-form"},{"name":"cancel-bill-workspace","component":"cancelBillWorkspace","title":"Cancel Bill Workspace","type":"other-form"},{"name":"bill-deposit-workspace","component":"billDepositWorkspace","title":"Bill Deposit Workspace","type":"other-form"}],"modals":[{"name":"create-payment-point","component":"createPaymentPoint"},{"name":"clock-out-modal","component":"clockOut"},{"name":"bulk-import-billable-services-modal","component":"bulkImportBillableServicesModal"},{"name":"delete-payment-mode-modal","component":"deletePaymentModeModal"},{"name":"manage-claim-request-modal","component":"manageClaimRequestModal"},{"name":"paid-bill-receipt-print-preview-modal","component":"paidBillReceiptPrintPreviewModal"},{"name":"clock-in-modal","component":"clockIn"},{"name":"create-bill-item-modal","component":"createBillItemModal"}],"version":"5.4.1-pre.
|
|
1
|
+
{"$schema":"https://json.openmrs.org/routes.schema.json","backendDependencies":{"kenyaemr":"^19.0.0"},"pages":[{"component":"billableServicesHome","route":"billable-services"},{"component":"requirePaymentModal","routeRegex":"^patient/.+/chart","online":true,"offline":false}],"extensions":[{"component":"billingDashboardLink","name":"billing-dashboard-link","slot":"homepage-dashboard-slot","meta":{"name":"billing","title":"billing","slot":"billing-dashboard-slot"}},{"component":"benefitsPackageDashboardLink","name":"benefits-package-dashboard-link","slot":"patient-chart-dashboard-slot","meta":{"name":"benefits-package","slot":"patient-chart-benefits-dashboard-slot","path":"insurance-benefits","columns":1,"columnSpan":1},"featureFlag":"healthInformationExchange"},{"component":"benefitsPackage","name":"benefits-package","slot":"patient-chart-benefits-dashboard-slot"},{"component":"root","name":"billing-dashboard-root","slot":"billing-dashboard-slot"},{"component":"benefitsEligibilyRequestForm","name":"benefits-eligibility-request-form"},{"component":"benefitsPreAuthForm","name":"benefits-pre-auth-form"},{"name":"billing-patient-summary","component":"billingPatientSummary","slot":"patient-chart-billing-dashboard-slot","order":10,"meta":{"columnSpan":4}},{"name":"billing-summary-dashboard-link","component":"billingSummaryDashboardLink","slot":"patient-chart-dashboard-slot","order":11,"meta":{"columns":1,"columnSpan":1,"slot":"patient-chart-billing-dashboard-slot","path":"Billing","layoutMode":"anchored"}},{"name":"billing-check-in-form","slot":"extra-visit-attribute-slot","component":"billingCheckInForm"},{"name":"require-billing-modal","component":"requirePaymentModal"},{"name":"patient-banner-billing-tags","component":"visitAttributeTags","slot":"patient-banner-tags-slot","order":2},{"name":"initiate-payment-modal","component":"initiatePaymentDialog"},{"name":"delete-billableservice-modal","component":"deleteBillableServiceModal"},{"name":"refund-bill-modal","component":"refundBillModal"},{"name":"delete-bill-modal","component":"deleteBillModal"},{"name":"lab-order-billable-item","component":"labOrder","slot":"top-of-lab-order-form-slot"},{"name":"procedure-order-billable-item","component":"procedureOrder","slot":"top-of-procedure-order-form-slot"},{"name":"imaging-order-billable-item","component":"imagingOrder","slot":"top-of-imaging-order-form-slot"},{"name":"price-info-order","component":"priceInfoOrder"},{"name":"drug-order-billable-item","component":"drugOrder","slot":"medication-info-slot"},{"name":"order-action-button","component":"orderActionButton","slots":["prescription-action-button-slot","imaging-orders-action","procedure-orders-action","tests-ordered-actions-slot"],"order":0},{"component":"billingOverviewLink","name":"billing-overview-link","order":0,"slot":"billing-dashboard-link-slot"},{"component":"paymentHistoryLink","name":"payment-history-link","slot":"billing-dashboard-link-slot"},{"component":"paymentPointsLink","name":"payment-points-link","slot":"billing-dashboard-link-slot"},{"component":"paymentModesLink","name":"payment-modes-link","slot":"billing-dashboard-link-slot"},{"component":"billManagerLink","name":"bill-manager-link","slot":"billing-dashboard-link-slot"},{"component":"chargeableItemsLink","name":"chargeable-items-link","slot":"billing-dashboard-link-slot"},{"component":"billableExemptionsLink","name":"billable-exemptions-link","slot":"billing-dashboard-link-slot"},{"component":"claimsManagementSideNavGroup","name":"claims-management-dashboard-link","slot":"homepage-dashboard-slot","meta":{"name":"claims-management","title":"Claims management Overview","slot":"case-management-slot"},"featureFlag":"healthInformationExchange"},{"component":"claimsManagementOverviewDashboardLink","name":"claims-management-overview-link","order":0,"slot":"claims-management-dashboard-link-slot"},{"component":"preAuthRequestsDashboardLink","name":"preauthrequest-overview-link","slot":"claims-management-dashboard-link-slot"},{"component":"claimsOverview","name":"claims-overview-dashboard-link","slot":"claims-management-overview-slot"},{"component":"waiveBillActionButton","name":"waive-bill-action-button","slot":"bill-actions-slot"},{"component":"deleteBillActionButton","name":"delete-bill-action-button","slot":"bill-actions-slot"},{"component":"refundLineItem","name":"refund-line-item","slot":"bill-actions-overflow-menu-slot"},{"name":"edit-line-item","component":"editLineItem","slot":"bill-actions-overflow-menu-slot"},{"name":"cancel-line-item","component":"cancelLineItem","slot":"bill-actions-overflow-menu-slot"}],"workspaces":[{"name":"create-bill-workspace","component":"createBillWorkspace","title":"Create Bill Workspace","type":"other-form"},{"name":"waive-bill-form","component":"waiveBillForm","title":"Waive Bill Form","type":"other-form"},{"name":"edit-bill-form","component":"editBillForm","title":"Edit Bill Form","type":"other-form"},{"name":"billable-service-form","component":"addServiceForm","title":"Create Charge Item Form","type":"other-form"},{"name":"commodity-form","component":"addCommodityForm","title":"Create Charge Item Form","type":"other-form"},{"name":"billing-form","component":"billingForm","title":"Billing Form","type":"other-form","width":"extra-wide"},{"name":"payment-mode-workspace","component":"paymentModeWorkspace","title":"Payment Mode Workspace","type":"other-form"},{"name":"cancel-bill-workspace","component":"cancelBillWorkspace","title":"Cancel Bill Workspace","type":"other-form"},{"name":"bill-deposit-workspace","component":"billDepositWorkspace","title":"Bill Deposit Workspace","type":"other-form"}],"modals":[{"name":"create-payment-point","component":"createPaymentPoint"},{"name":"clock-out-modal","component":"clockOut"},{"name":"bulk-import-billable-services-modal","component":"bulkImportBillableServicesModal"},{"name":"delete-payment-mode-modal","component":"deletePaymentModeModal"},{"name":"manage-claim-request-modal","component":"manageClaimRequestModal"},{"name":"paid-bill-receipt-print-preview-modal","component":"paidBillReceiptPrintPreviewModal"},{"name":"clock-in-modal","component":"clockIn"},{"name":"create-bill-item-modal","component":"createBillItemModal"}],"version":"5.4.1-pre.2113"}
|
package/package.json
CHANGED
|
@@ -34,6 +34,7 @@ type ClaimsTableProps = {
|
|
|
34
34
|
|
|
35
35
|
const ClaimsTable: React.FC<ClaimsTableProps> = ({ bill, isSelectable = true, isLoadingBill, onSelectItem }) => {
|
|
36
36
|
const { t } = useTranslation();
|
|
37
|
+
const { lineItems } = bill;
|
|
37
38
|
const layout = useLayoutType();
|
|
38
39
|
const responsiveSize = isDesktop(layout) ? 'sm' : 'lg';
|
|
39
40
|
const [selectedLineItems, setSelectedLineItems] = useState<LineItem[]>([]);
|
|
@@ -42,33 +43,20 @@ const ClaimsTable: React.FC<ClaimsTableProps> = ({ bill, isSelectable = true, is
|
|
|
42
43
|
const [currentPage, setCurrentPage] = useState(1);
|
|
43
44
|
const [pageSize, setPageSize] = useState(10);
|
|
44
45
|
|
|
45
|
-
|
|
46
|
-
const insurancePaidLineItems = useMemo(() => {
|
|
47
|
-
return (bill.lineItems || []).filter((lineItem) => {
|
|
48
|
-
// Check if payment status is PAID
|
|
49
|
-
const isPaid = lineItem.paymentStatus === 'PAID';
|
|
50
|
-
|
|
51
|
-
// Check if there's an Insurance payment for this line item
|
|
52
|
-
const hasInsurancePayment = bill.payments?.some(
|
|
53
|
-
(payment) => payment.billLineItem?.uuid === lineItem.uuid && payment.instanceType?.name === 'Insurance',
|
|
54
|
-
);
|
|
55
|
-
|
|
56
|
-
return isPaid && hasInsurancePayment;
|
|
57
|
-
});
|
|
58
|
-
}, [bill.lineItems, bill.payments]);
|
|
46
|
+
const paidLineItems = useMemo(() => (lineItems || []).filter((item) => item.paymentStatus === 'PAID'), [lineItems]);
|
|
59
47
|
|
|
60
48
|
const filteredLineItems = useMemo(() => {
|
|
61
49
|
if (!debouncedSearchTerm) {
|
|
62
|
-
return
|
|
50
|
+
return paidLineItems;
|
|
63
51
|
}
|
|
64
52
|
|
|
65
53
|
return fuzzy
|
|
66
|
-
.filter(debouncedSearchTerm,
|
|
67
|
-
extract: (lineItem: LineItem) => `${lineItem.item
|
|
54
|
+
.filter(debouncedSearchTerm, paidLineItems, {
|
|
55
|
+
extract: (lineItem: LineItem) => `${lineItem.item}`,
|
|
68
56
|
})
|
|
69
57
|
.sort((r1, r2) => r1.score - r2.score)
|
|
70
58
|
.map((result) => result.original);
|
|
71
|
-
}, [debouncedSearchTerm,
|
|
59
|
+
}, [debouncedSearchTerm, paidLineItems]);
|
|
72
60
|
|
|
73
61
|
const paginatedLineItems = useMemo(() => {
|
|
74
62
|
const startIndex = (currentPage - 1) * pageSize;
|
|
@@ -81,20 +69,11 @@ const ClaimsTable: React.FC<ClaimsTableProps> = ({ bill, isSelectable = true, is
|
|
|
81
69
|
{ header: t('serialNo', 'Serial No'), key: 'serialno' },
|
|
82
70
|
{ header: t('billItem', 'Bill Item'), key: 'inventoryname' },
|
|
83
71
|
{ header: t('status', 'Status'), key: 'status' },
|
|
84
|
-
{ header: t('paymentMethod', 'Payment Method'), key: 'paymentMethod' },
|
|
85
72
|
{ header: t('totalAmount', 'Total amount'), key: 'total' },
|
|
86
73
|
{ header: t('billCreationDate', 'Bill creation date'), key: 'dateofbillcreation' },
|
|
87
74
|
];
|
|
88
75
|
|
|
89
|
-
const processBillItem = (item) =>
|
|
90
|
-
const itemName = item?.item || item?.billableService;
|
|
91
|
-
return itemName?.split(':')[1]?.trim() || itemName;
|
|
92
|
-
};
|
|
93
|
-
|
|
94
|
-
const getPaymentMethod = (lineItemUuid: string) => {
|
|
95
|
-
const payment = bill.payments?.find((p) => p.billLineItem?.uuid === lineItemUuid);
|
|
96
|
-
return payment?.instanceType?.name || 'Unknown';
|
|
97
|
-
};
|
|
76
|
+
const processBillItem = (item) => (item?.item || item?.billableService)?.split(':')[1];
|
|
98
77
|
|
|
99
78
|
const tableRows: Array<typeof DataTableRow> = useMemo(
|
|
100
79
|
() =>
|
|
@@ -105,12 +84,11 @@ const ClaimsTable: React.FC<ClaimsTableProps> = ({ bill, isSelectable = true, is
|
|
|
105
84
|
inventoryname: processBillItem(item),
|
|
106
85
|
serialno: bill.receiptNumber,
|
|
107
86
|
status: item.paymentStatus,
|
|
108
|
-
paymentMethod: getPaymentMethod(item.uuid),
|
|
109
87
|
total: item.price * item.quantity,
|
|
110
88
|
dateofbillcreation: formatDate(new Date(bill.dateCreated), { mode: 'standard' }),
|
|
111
89
|
};
|
|
112
90
|
}) ?? [],
|
|
113
|
-
[bill.dateCreated, bill.receiptNumber,
|
|
91
|
+
[bill.dateCreated, bill.receiptNumber, paginatedLineItems],
|
|
114
92
|
);
|
|
115
93
|
|
|
116
94
|
if (isLoadingBill) {
|
|
@@ -148,10 +126,10 @@ const ClaimsTable: React.FC<ClaimsTableProps> = ({ bill, isSelectable = true, is
|
|
|
148
126
|
className={styles.tableContainer}
|
|
149
127
|
description={
|
|
150
128
|
<span className={styles.tableDescription}>
|
|
151
|
-
<span>{t('
|
|
129
|
+
<span>{t('selectitemstobeclaimed', 'Select items that are to be included in the claims')}</span>
|
|
152
130
|
</span>
|
|
153
131
|
}
|
|
154
|
-
title={t('
|
|
132
|
+
title={t('lineItems', 'Line items')}>
|
|
155
133
|
<div className={styles.toolbarWrapper}>
|
|
156
134
|
<TableToolbar {...getToolbarProps()} className={styles.tableToolbar} size={responsiveSize}>
|
|
157
135
|
<TableToolbarContent className={styles.headerContainer}>
|
|
@@ -165,7 +143,7 @@ const ClaimsTable: React.FC<ClaimsTableProps> = ({ bill, isSelectable = true, is
|
|
|
165
143
|
</TableToolbarContent>
|
|
166
144
|
</TableToolbar>
|
|
167
145
|
</div>
|
|
168
|
-
<Table {...getTableProps()} aria-label="
|
|
146
|
+
<Table {...getTableProps()} aria-label="claim line items" className={styles.table}>
|
|
169
147
|
<TableHead>
|
|
170
148
|
<TableRow>
|
|
171
149
|
{isSelectable ? <TableHeader /> : null}
|
|
@@ -206,13 +184,14 @@ const ClaimsTable: React.FC<ClaimsTableProps> = ({ bill, isSelectable = true, is
|
|
|
206
184
|
<Layer>
|
|
207
185
|
<Tile className={styles.filterEmptyStateTile}>
|
|
208
186
|
<p className={styles.filterEmptyStateContent}>
|
|
209
|
-
{t('
|
|
187
|
+
{t('noMatchingItemsToDisplay', 'No matching items to display')}
|
|
210
188
|
</p>
|
|
189
|
+
<p className={styles.filterEmptyStateHelper}>{t('checkFilters', 'Check the filters above')}</p>
|
|
211
190
|
</Tile>
|
|
212
191
|
</Layer>
|
|
213
192
|
</div>
|
|
214
193
|
)}
|
|
215
|
-
{
|
|
194
|
+
{tableRows.length > pageSize && (
|
|
216
195
|
<Pagination
|
|
217
196
|
forwardText="Next page"
|
|
218
197
|
backwardText="Previous page"
|
|
@@ -97,8 +97,8 @@ export const useInterventions = (filters: InterventionsFilter) => {
|
|
|
97
97
|
const { isLoading, error, data } = useSWR<FetchResponse<{ results: Array<Intervention> }>>(url, openmrsFetch);
|
|
98
98
|
|
|
99
99
|
// Mapper function to transform intervention data
|
|
100
|
-
const mapper = ({
|
|
101
|
-
interventionCode:
|
|
100
|
+
const mapper = ({ code, name, parentBenefitCode }: Intervention): SHAIntervention => ({
|
|
101
|
+
interventionCode: code,
|
|
102
102
|
interventionName: name,
|
|
103
103
|
interventionPackage: parentBenefitCode,
|
|
104
104
|
...({} as any),
|
|
@@ -112,9 +112,9 @@ export const useInterventions = (filters: InterventionsFilter) => {
|
|
|
112
112
|
|
|
113
113
|
const packageCodes = filters.package_code?.split(',').filter(Boolean) || [];
|
|
114
114
|
|
|
115
|
-
|
|
116
|
-
const filteredResults = data.data.results.filter((intervention) => {
|
|
115
|
+
const filteredResults = data.data.results.filter((intervention, i) => {
|
|
117
116
|
// Filter by package code (only if defined)
|
|
117
|
+
|
|
118
118
|
if (packageCodes.length > 0 && !packageCodes.includes(intervention.parentBenefitCode)) {
|
|
119
119
|
return false;
|
|
120
120
|
}
|
|
@@ -128,9 +128,14 @@ export const useInterventions = (filters: InterventionsFilter) => {
|
|
|
128
128
|
return false;
|
|
129
129
|
}
|
|
130
130
|
|
|
131
|
-
// Filter by facility level
|
|
132
|
-
if (level && intervention.levelsApplicable &&
|
|
133
|
-
|
|
131
|
+
// // Filter by facility level
|
|
132
|
+
if (level && intervention.levelsApplicable && Array.isArray(intervention.levelsApplicable)) {
|
|
133
|
+
// Check if level starts with any of the intervention.levelsApplicable values
|
|
134
|
+
const levelMatched = intervention.levelsApplicable.some((appLevel) => level.startsWith(appLevel));
|
|
135
|
+
|
|
136
|
+
if (!levelMatched) {
|
|
137
|
+
return false;
|
|
138
|
+
}
|
|
134
139
|
}
|
|
135
140
|
|
|
136
141
|
return true; // Keep item if it passes all filters
|
|
@@ -139,12 +144,7 @@ export const useInterventions = (filters: InterventionsFilter) => {
|
|
|
139
144
|
// Map to the required format and ensure uniqueness by interventionCode
|
|
140
145
|
const mappedInterventions = filteredResults.map(mapper);
|
|
141
146
|
|
|
142
|
-
|
|
143
|
-
const uniqueInterventions = Array.from(
|
|
144
|
-
new Map(mappedInterventions.map((item) => [item.interventionCode, item])),
|
|
145
|
-
).map(([, value]) => value);
|
|
146
|
-
|
|
147
|
-
return uniqueInterventions;
|
|
147
|
+
return mappedInterventions;
|
|
148
148
|
}, [data, filters, level]);
|
|
149
149
|
|
|
150
150
|
// Map all interventions without filtering
|
|
@@ -23,7 +23,6 @@ export function usePaymentSchema(bill: MappedBill) {
|
|
|
23
23
|
return amountDue >= 0 && value > 0;
|
|
24
24
|
}, 'Amount paid should not be greater than amount due'),
|
|
25
25
|
referenceCode: z.string(),
|
|
26
|
-
lineItemUuid: z.string().uuid().nonempty({ message: 'Line item selection is required' }),
|
|
27
26
|
})
|
|
28
27
|
.refine(
|
|
29
28
|
(data) => {
|
|
@@ -151,7 +151,7 @@ const InvoiceTable: React.FC<InvoiceTableProps> = ({ bill, isSelectable = true,
|
|
|
151
151
|
{...getRowProps({
|
|
152
152
|
row,
|
|
153
153
|
})}>
|
|
154
|
-
{isSelectable && (
|
|
154
|
+
{rows.length > 1 && isSelectable && (
|
|
155
155
|
<TableSelectRow
|
|
156
156
|
aria-label="Select row"
|
|
157
157
|
{...getSelectionProps({ row })}
|
|
@@ -6,33 +6,23 @@ import { Button, Dropdown, NumberInputSkeleton, TextInput, NumberInput } from '@
|
|
|
6
6
|
import { ErrorState } from '@openmrs/esm-patient-common-lib';
|
|
7
7
|
import styles from './payment-form.scss';
|
|
8
8
|
import { usePaymentModes } from '../../../billing.resource';
|
|
9
|
-
import {
|
|
10
|
-
import { extractBillableName } from '../../../utils';
|
|
9
|
+
import { PaymentFormValue, PaymentMethod } from '../../../types';
|
|
11
10
|
|
|
12
11
|
type PaymentFormProps = {
|
|
13
12
|
disablePayment: boolean;
|
|
14
13
|
amountDue: number;
|
|
15
|
-
append: (obj: { method: PaymentMethod; amount: number; referenceCode: string
|
|
14
|
+
append: (obj: { method: PaymentMethod; amount: number; referenceCode: string }) => void;
|
|
16
15
|
fields: FieldArrayWithId<PaymentFormValue, 'payment', 'id'>[];
|
|
17
16
|
remove: UseFieldArrayRemove;
|
|
18
|
-
selectedLineItems: Array<LineItem>;
|
|
19
17
|
};
|
|
20
18
|
|
|
21
|
-
const PaymentForm: React.FC<PaymentFormProps> = ({
|
|
22
|
-
disablePayment,
|
|
23
|
-
amountDue,
|
|
24
|
-
append,
|
|
25
|
-
remove,
|
|
26
|
-
fields,
|
|
27
|
-
selectedLineItems,
|
|
28
|
-
}) => {
|
|
19
|
+
const PaymentForm: React.FC<PaymentFormProps> = ({ disablePayment, amountDue, append, remove, fields }) => {
|
|
29
20
|
const { t } = useTranslation();
|
|
30
21
|
const {
|
|
31
22
|
control,
|
|
32
23
|
formState: { errors },
|
|
33
24
|
setFocus,
|
|
34
25
|
getValues,
|
|
35
|
-
watch,
|
|
36
26
|
} = useFormContext<PaymentFormValue>();
|
|
37
27
|
const { paymentModes, isLoading, error } = usePaymentModes();
|
|
38
28
|
|
|
@@ -41,26 +31,9 @@ const PaymentForm: React.FC<PaymentFormProps> = ({
|
|
|
41
31
|
const attributes = formValues?.payment?.[index]?.method?.attributeTypes ?? [];
|
|
42
32
|
return attributes.some((attribute) => attribute.required) || attributes?.length > 0;
|
|
43
33
|
};
|
|
44
|
-
const paymentValues = watch('payment');
|
|
45
34
|
|
|
46
|
-
const getUsedItemUuids = (currentIndex: number) => {
|
|
47
|
-
return paymentValues
|
|
48
|
-
.map((payment, index) => (index !== currentIndex ? payment?.lineItemUuid : null))
|
|
49
|
-
.filter(Boolean);
|
|
50
|
-
};
|
|
51
|
-
const getAvailableLineItems = (currentIndex: number) => {
|
|
52
|
-
const usedUuids = getUsedItemUuids(currentIndex);
|
|
53
|
-
return selectedLineItems.filter((item) => item.paymentStatus === 'PENDING' && !usedUuids.includes(item.uuid));
|
|
54
|
-
};
|
|
55
35
|
const handleAppendPaymentMode = useCallback(() => {
|
|
56
|
-
|
|
57
|
-
const initialItemUuid = availableItems.length === 1 ? availableItems[0].uuid : '';
|
|
58
|
-
append({
|
|
59
|
-
method: null,
|
|
60
|
-
amount: null,
|
|
61
|
-
referenceCode: '',
|
|
62
|
-
lineItemUuid: initialItemUuid,
|
|
63
|
-
});
|
|
36
|
+
append({ method: null, amount: null, referenceCode: '' });
|
|
64
37
|
setFocus(`payment.${fields.length}.method`);
|
|
65
38
|
}, [append, fields.length, setFocus]);
|
|
66
39
|
|
|
@@ -80,91 +53,67 @@ const PaymentForm: React.FC<PaymentFormProps> = ({
|
|
|
80
53
|
|
|
81
54
|
return (
|
|
82
55
|
<div className={styles.container}>
|
|
83
|
-
{fields.map((field, index) =>
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
onChange
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
items={paymentModes}
|
|
122
|
-
itemToString={(item) => (item ? item.name : '')}
|
|
123
|
-
invalid={!!errors?.payment?.[index]?.method}
|
|
124
|
-
invalidText={errors?.payment?.[index]?.method?.message}
|
|
125
|
-
/>
|
|
126
|
-
)}
|
|
127
|
-
/>
|
|
56
|
+
{fields.map((field, index) => (
|
|
57
|
+
<div key={field.id} className={styles.paymentMethodContainer}>
|
|
58
|
+
<Controller
|
|
59
|
+
control={control}
|
|
60
|
+
name={`payment.${index}.method`}
|
|
61
|
+
render={({ field }) => (
|
|
62
|
+
<Dropdown
|
|
63
|
+
{...field}
|
|
64
|
+
id="paymentMethod"
|
|
65
|
+
onChange={({ selectedItem }) => {
|
|
66
|
+
setFocus(`payment.${index}.amount`);
|
|
67
|
+
field.onChange(selectedItem);
|
|
68
|
+
}}
|
|
69
|
+
titleText={t('paymentMethod', 'Payment method')}
|
|
70
|
+
label={t('selectPaymentMethod', 'Select payment method')}
|
|
71
|
+
items={paymentModes}
|
|
72
|
+
itemToString={(item) => (item ? item.name : '')}
|
|
73
|
+
invalid={!!errors?.payment?.[index]?.method}
|
|
74
|
+
invalidText={errors?.payment?.[index]?.method?.message}
|
|
75
|
+
/>
|
|
76
|
+
)}
|
|
77
|
+
/>
|
|
78
|
+
<Controller
|
|
79
|
+
control={control}
|
|
80
|
+
name={`payment.${index}.amount`}
|
|
81
|
+
render={({ field }) => (
|
|
82
|
+
<NumberInput
|
|
83
|
+
{...field}
|
|
84
|
+
id="paymentAmount"
|
|
85
|
+
onChange={(e) => field.onChange(Number(e.target.value))}
|
|
86
|
+
invalid={!!errors?.payment?.[index]?.amount}
|
|
87
|
+
invalidText={errors?.payment?.[index]?.amount?.message}
|
|
88
|
+
label={t('amount', 'Amount')}
|
|
89
|
+
placeholder={t('enterAmount', 'Enter amount')}
|
|
90
|
+
/>
|
|
91
|
+
)}
|
|
92
|
+
/>
|
|
93
|
+
{shouldShowReferenceCode(index) && (
|
|
128
94
|
<Controller
|
|
95
|
+
name={`payment.${index}.referenceCode`}
|
|
129
96
|
control={control}
|
|
130
|
-
name={`payment.${index}.amount`}
|
|
131
97
|
render={({ field }) => (
|
|
132
|
-
<
|
|
98
|
+
<TextInput
|
|
133
99
|
{...field}
|
|
134
|
-
id="
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
100
|
+
id="paymentReferenceCode"
|
|
101
|
+
labelText={t('referenceNumber', 'Reference number')}
|
|
102
|
+
placeholder={t('enterReferenceNumber', 'Enter ref. number')}
|
|
103
|
+
type="text"
|
|
104
|
+
invalid={!!errors?.payment?.[index]?.referenceCode}
|
|
105
|
+
invalidText={errors?.payment?.[index]?.referenceCode?.message}
|
|
140
106
|
/>
|
|
141
107
|
)}
|
|
142
108
|
/>
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
control={control}
|
|
147
|
-
render={({ field }) => (
|
|
148
|
-
<TextInput
|
|
149
|
-
{...field}
|
|
150
|
-
id="paymentReferenceCode"
|
|
151
|
-
labelText={t('referenceNumber', 'Reference number')}
|
|
152
|
-
placeholder={t('enterReferenceNumber', 'Enter ref. number')}
|
|
153
|
-
type="text"
|
|
154
|
-
invalid={!!errors?.payment?.[index]?.referenceCode}
|
|
155
|
-
invalidText={errors?.payment?.[index]?.referenceCode?.message}
|
|
156
|
-
/>
|
|
157
|
-
)}
|
|
158
|
-
/>
|
|
159
|
-
)}
|
|
160
|
-
<div className={styles.removeButtonContainer}>
|
|
161
|
-
<TrashCan onClick={() => handleRemovePaymentMode(index)} className={styles.removeButton} size={20} />
|
|
162
|
-
</div>
|
|
109
|
+
)}
|
|
110
|
+
<div className={styles.removeButtonContainer}>
|
|
111
|
+
<TrashCan onClick={() => handleRemovePaymentMode(index)} className={styles.removeButton} size={20} />
|
|
163
112
|
</div>
|
|
164
|
-
|
|
165
|
-
|
|
113
|
+
</div>
|
|
114
|
+
))}
|
|
166
115
|
<Button
|
|
167
|
-
disabled={disablePayment
|
|
116
|
+
disabled={disablePayment}
|
|
168
117
|
size="md"
|
|
169
118
|
onClick={handleAppendPaymentMode}
|
|
170
119
|
className={styles.paymentButtons}
|
|
@@ -19,9 +19,9 @@
|
|
|
19
19
|
|
|
20
20
|
.paymentMethodContainer {
|
|
21
21
|
display: grid;
|
|
22
|
-
grid-template-columns: repeat(
|
|
22
|
+
grid-template-columns: repeat(4, minmax(auto, 1fr));
|
|
23
23
|
align-items: flex-start;
|
|
24
|
-
column-gap:
|
|
24
|
+
column-gap: 1rem;
|
|
25
25
|
margin: 0.625rem 0;
|
|
26
26
|
width: 100%;
|
|
27
27
|
}
|
|
@@ -52,6 +52,3 @@
|
|
|
52
52
|
.removeButton {
|
|
53
53
|
color: colors.$red-60;
|
|
54
54
|
}
|
|
55
|
-
.lineItemDropdown {
|
|
56
|
-
min-width: 10rem;
|
|
57
|
-
}
|
|
@@ -67,6 +67,7 @@ const Payments: React.FC<PaymentProps> = ({ bill, selectedLineItems }) => {
|
|
|
67
67
|
globalActiveSheet,
|
|
68
68
|
);
|
|
69
69
|
remove();
|
|
70
|
+
|
|
70
71
|
processBillPayment(paymentPayload, bill.uuid).then(
|
|
71
72
|
(resp) => {
|
|
72
73
|
showSnackbar({
|
|
@@ -141,12 +142,7 @@ const Payments: React.FC<PaymentProps> = ({ bill, selectedLineItems }) => {
|
|
|
141
142
|
className={styles.paymentError}
|
|
142
143
|
/>
|
|
143
144
|
)}
|
|
144
|
-
<PaymentForm
|
|
145
|
-
{...formArrayMethods}
|
|
146
|
-
selectedLineItems={selectedLineItems}
|
|
147
|
-
disablePayment={amountDue <= 0}
|
|
148
|
-
amountDue={amountDue}
|
|
149
|
-
/>
|
|
145
|
+
<PaymentForm {...formArrayMethods} disablePayment={amountDue <= 0} amountDue={amountDue} />
|
|
150
146
|
</div>
|
|
151
147
|
</div>
|
|
152
148
|
<div className={styles.divider} />
|
|
@@ -6,7 +6,6 @@ import { mockBill, mockedActiveSheet, mockLineItems, mockPaymentModes } from '..
|
|
|
6
6
|
import { processBillPayment, usePaymentModes } from '../../billing.resource';
|
|
7
7
|
import { useClockInStatus } from '../../payment-points/use-clock-in-status';
|
|
8
8
|
import Payments from './payments.component';
|
|
9
|
-
|
|
10
9
|
const mockProcessBillPayment = processBillPayment as jest.MockedFunction<typeof processBillPayment>;
|
|
11
10
|
const mockUsePaymentModes = usePaymentModes as jest.MockedFunction<typeof usePaymentModes>;
|
|
12
11
|
const mockShowSnackbar = showSnackbar as jest.MockedFunction<typeof showSnackbar>;
|
|
@@ -32,7 +31,6 @@ describe('Payment', () => {
|
|
|
32
31
|
},
|
|
33
32
|
},
|
|
34
33
|
};
|
|
35
|
-
|
|
36
34
|
mockProcessBillPayment.mockRejectedValueOnce(mockFieldErrorResponse);
|
|
37
35
|
mockUsePaymentModes.mockReturnValue({
|
|
38
36
|
paymentModes: mockPaymentModes,
|
|
@@ -51,19 +49,10 @@ describe('Payment', () => {
|
|
|
51
49
|
});
|
|
52
50
|
|
|
53
51
|
render(<Payments bill={mockBill as any} selectedLineItems={mockLineItems} />);
|
|
54
|
-
|
|
55
52
|
const addPaymentMethod = screen.getByRole('button', { name: /Add payment option/i });
|
|
56
53
|
await user.click(addPaymentMethod);
|
|
57
|
-
|
|
58
|
-
const
|
|
59
|
-
await user.click(lineItemDropdown);
|
|
60
|
-
|
|
61
|
-
const lineItemOptions = await screen.findAllByRole('option');
|
|
62
|
-
await user.click(lineItemOptions[0]);
|
|
63
|
-
|
|
64
|
-
const paymentMethodDropdown = await screen.findByRole('combobox', { name: /Payment method/i });
|
|
65
|
-
await user.click(paymentMethodDropdown);
|
|
66
|
-
const cashOption = await screen.findByRole('option', { name: /Cash/i });
|
|
54
|
+
await user.click(screen.getByRole('combobox', { name: /Payment method/i }));
|
|
55
|
+
const cashOption = screen.getByRole('option', { name: /Cash/i });
|
|
67
56
|
await user.click(cashOption);
|
|
68
57
|
|
|
69
58
|
const amountInput = screen.getByRole('spinbutton', { name: /Amount/i });
|
|
@@ -82,8 +71,6 @@ describe('Payment', () => {
|
|
|
82
71
|
billableService: 'c15d25b9-12bb-441d-9241-cae541dd4575',
|
|
83
72
|
display: 'BillLineItem',
|
|
84
73
|
item: 'c15d25b9-12bb-441d-9241-cae541dd4575',
|
|
85
|
-
itemOrServiceConceptUuid: 'c42525b9-12bb-441d-9241-cae541dd4575',
|
|
86
|
-
serviceTypeUuid: '915d25b9-12bb-441d-9241-cae541dd4575',
|
|
87
74
|
lineItemOrder: 0,
|
|
88
75
|
order: null,
|
|
89
76
|
paymentStatus: 'PAID',
|
|
@@ -100,8 +87,6 @@ describe('Payment', () => {
|
|
|
100
87
|
billableService: '04be5832-5440-44d0-83d2-5c0dfd0ac7de',
|
|
101
88
|
display: 'BillLineItem',
|
|
102
89
|
item: '04be5832-5440-44d0-83d2-5c0dfd0ac7de',
|
|
103
|
-
itemOrServiceConceptUuid: 'c42525b9-12bb-441d-9241-cae541dd4575',
|
|
104
|
-
serviceTypeUuid: '915d25b9-12bb-441d-9241-cae541dd4575',
|
|
105
90
|
lineItemOrder: 1,
|
|
106
91
|
order: null,
|
|
107
92
|
paymentStatus: 'PAID',
|
|
@@ -118,8 +103,6 @@ describe('Payment', () => {
|
|
|
118
103
|
billableService: '3f5d0684-a280-477e-a67b-2a956a1f6dca',
|
|
119
104
|
display: 'BillLineItem',
|
|
120
105
|
item: '3f5d0684-a280-477e-a67b-2a956a1f6dca',
|
|
121
|
-
itemOrServiceConceptUuid: 'c42525b9-12bb-441d-9241-cae541dd4575',
|
|
122
|
-
serviceTypeUuid: '915d25b9-12bb-441d-9241-cae541dd4575',
|
|
123
106
|
lineItemOrder: 2,
|
|
124
107
|
order: null,
|
|
125
108
|
paymentStatus: 'PAID',
|
|
@@ -150,13 +133,7 @@ describe('Payment', () => {
|
|
|
150
133
|
],
|
|
151
134
|
patient: 'b2fcf02b-7ee3-4d16-a48f-576be2b103aa',
|
|
152
135
|
payments: [
|
|
153
|
-
{
|
|
154
|
-
amount: 100,
|
|
155
|
-
amountTendered: 100,
|
|
156
|
-
attributes: [],
|
|
157
|
-
instanceType: '63eff7a4-6f82-43c4-a333-dbcc58fe9f74',
|
|
158
|
-
billLineItem: '314c25fd-2c90-4a7f-9f98-c99cd3f153e8',
|
|
159
|
-
},
|
|
136
|
+
{ amount: 100, amountTendered: 100, attributes: [], instanceType: '63eff7a4-6f82-43c4-a333-dbcc58fe9f74' },
|
|
160
137
|
],
|
|
161
138
|
status: 'PENDING',
|
|
162
139
|
},
|
|
@@ -181,27 +158,18 @@ describe('Payment', () => {
|
|
|
181
158
|
error: null,
|
|
182
159
|
mutate: jest.fn(),
|
|
183
160
|
});
|
|
184
|
-
|
|
185
161
|
render(<Payments bill={mockBill as any} selectedLineItems={mockLineItems} />);
|
|
186
|
-
|
|
187
162
|
const addPaymentMethod = screen.getByRole('button', { name: /Add payment option/i });
|
|
188
163
|
await user.click(addPaymentMethod);
|
|
189
164
|
|
|
190
|
-
|
|
191
|
-
expect(
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
const lineItemOptions = await screen.findAllByRole('option');
|
|
195
|
-
await user.click(lineItemOptions[0]);
|
|
196
|
-
|
|
197
|
-
const paymentMethodField = await screen.findByRole('combobox', { name: /Payment method/i });
|
|
198
|
-
expect(paymentMethodField).toHaveFocus();
|
|
199
|
-
|
|
200
|
-
await user.click(paymentMethodField);
|
|
201
|
-
const cashOption = await screen.findByRole('option', { name: /Cash/i });
|
|
165
|
+
// Check if the payment method field is focused
|
|
166
|
+
expect(screen.getByRole('combobox', { name: /Payment method/i })).toHaveFocus();
|
|
167
|
+
await user.click(screen.getByRole('combobox', { name: /Payment method/i }));
|
|
168
|
+
const cashOption = screen.getByRole('option', { name: /Cash/i });
|
|
202
169
|
await user.click(cashOption);
|
|
203
|
-
|
|
170
|
+
// Check if the amount field is focused
|
|
171
|
+
expect(screen.getByRole('spinbutton', { name: /Amount/i })).toHaveFocus();
|
|
204
172
|
const amountInput = screen.getByRole('spinbutton', { name: /Amount/i });
|
|
205
|
-
|
|
173
|
+
await user.type(amountInput, '100');
|
|
206
174
|
});
|
|
207
175
|
});
|
|
@@ -126,7 +126,6 @@ export const createPaymentPayload = (
|
|
|
126
126
|
value: attribute.value,
|
|
127
127
|
})),
|
|
128
128
|
instanceType: payment.instanceType.uuid,
|
|
129
|
-
billLineItem: payment.billLineItem?.uuid,
|
|
130
129
|
}));
|
|
131
130
|
|
|
132
131
|
// Transform new payments
|
|
@@ -138,7 +137,6 @@ export const createPaymentPayload = (
|
|
|
138
137
|
value: formValue.referenceCode,
|
|
139
138
|
})),
|
|
140
139
|
instanceType: formValue.method?.uuid,
|
|
141
|
-
billLineItem: formValue.lineItemUuid,
|
|
142
140
|
}));
|
|
143
141
|
|
|
144
142
|
// Combine and calculate payments
|