@kenyaemr/esm-billing-app 5.4.2-pre.2265 → 5.4.2-pre.2271
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 +14 -14
- package/dist/115.js +1 -0
- package/dist/115.js.map +1 -0
- package/dist/300.js +1 -1
- package/dist/46.js +8 -0
- package/dist/46.js.map +1 -0
- package/dist/kenyaemr-esm-billing-app.js +1 -1
- package/dist/kenyaemr-esm-billing-app.js.buildmanifest.json +428 -428
- package/dist/kenyaemr-esm-billing-app.js.map +1 -1
- package/dist/main.js +3 -3
- package/dist/main.js.map +1 -1
- package/dist/routes.json +1 -1
- package/package.json +1 -1
- package/src/billable-services/bill-manager/patient-bills.component.tsx +1 -4
- package/src/billing.resource.ts +2 -0
- package/src/index.ts +2 -0
- package/src/invoice/invoice.component.tsx +110 -13
- package/src/invoice/invoice.resource.ts +41 -0
- package/src/invoice/invoice.scss +30 -5
- package/src/modal/bill-action.modal.scss +7 -0
- package/src/modal/bill-action.modal.tsx +122 -0
- package/src/past-patient-bills/patient-bills.component.tsx +1 -2
- package/src/prompt-payment/prompt-payment-modal.component.tsx +5 -2
- package/src/prompt-payment/prompt-payment.resource.tsx +1 -1
- package/src/routes.json +4 -0
- package/src/types/index.ts +2 -0
- package/src/utils.ts +4 -0
- package/translations/en.json +4 -0
- package/dist/632.js +0 -1
- package/dist/632.js.map +0 -1
- package/dist/910.js +0 -8
- package/dist/910.js.map +0 -1
- package/src/invoice/print-bill-receipt/receipt-print-button.component.tsx +0 -57
- /package/dist/{910.js.LICENSE.txt → 46.js.LICENSE.txt} +0 -0
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":"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-group-nav-slot"},{"component":"billDepositDashboardLink","name":"bill-deposit-dashboard-link","slot":"billing-dashboard-group-nav-slot"},{"component":"paymentHistoryLink","name":"payment-history-link","slot":"billing-dashboard-group-nav-slot"},{"component":"paymentPointsLink","name":"payment-points-link","slot":"billing-dashboard-group-nav-slot"},{"component":"paymentModesLink","name":"payment-modes-link","slot":"billing-dashboard-group-nav-slot"},{"component":"billManagerLink","name":"bill-manager-link","slot":"billing-dashboard-group-nav-slot"},{"component":"chargeableItemsLink","name":"chargeable-items-link","slot":"billing-dashboard-group-nav-slot"},{"component":"billableExemptionsLink","name":"billable-exemptions-link","slot":"billing-dashboard-group-nav-slot"},{"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":"add-deposit-workspace","component":"addDepositWorkspace","title":"Add Deposit","type":"other-form"},{"name":"deposit-transaction-workspace","component":"depositTransactionWorkspace","title":"Deposit Transaction","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":"clock-in-modal","component":"clockIn"},{"name":"create-bill-item-modal","component":"createBillItemModal"},{"name":"delete-deposit-modal","component":"deleteDepositModal"},{"name":"reverse-transaction-modal","component":"reverseTransactionModal"},{"name":"print-preview-modal","component":"printPreviewModal"}],"version":"5.4.2-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":"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-group-nav-slot"},{"component":"billDepositDashboardLink","name":"bill-deposit-dashboard-link","slot":"billing-dashboard-group-nav-slot"},{"component":"paymentHistoryLink","name":"payment-history-link","slot":"billing-dashboard-group-nav-slot"},{"component":"paymentPointsLink","name":"payment-points-link","slot":"billing-dashboard-group-nav-slot"},{"component":"paymentModesLink","name":"payment-modes-link","slot":"billing-dashboard-group-nav-slot"},{"component":"billManagerLink","name":"bill-manager-link","slot":"billing-dashboard-group-nav-slot"},{"component":"chargeableItemsLink","name":"chargeable-items-link","slot":"billing-dashboard-group-nav-slot"},{"component":"billableExemptionsLink","name":"billable-exemptions-link","slot":"billing-dashboard-group-nav-slot"},{"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":"add-deposit-workspace","component":"addDepositWorkspace","title":"Add Deposit","type":"other-form"},{"name":"deposit-transaction-workspace","component":"depositTransactionWorkspace","title":"Deposit Transaction","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":"clock-in-modal","component":"clockIn"},{"name":"create-bill-item-modal","component":"createBillItemModal"},{"name":"delete-deposit-modal","component":"deleteDepositModal"},{"name":"reverse-transaction-modal","component":"reverseTransactionModal"},{"name":"print-preview-modal","component":"printPreviewModal"},{"name":"bill-action-modal","component":"billActionModal"}],"version":"5.4.2-pre.2271"}
|
package/package.json
CHANGED
|
@@ -12,8 +12,6 @@ import {
|
|
|
12
12
|
TableExpandRow,
|
|
13
13
|
TableCell,
|
|
14
14
|
TableExpandedRow,
|
|
15
|
-
Tile,
|
|
16
|
-
Button,
|
|
17
15
|
} from '@carbon/react';
|
|
18
16
|
import { convertToCurrency } from '../../helpers';
|
|
19
17
|
import { useTranslation } from 'react-i18next';
|
|
@@ -21,8 +19,7 @@ import { EmptyState } from '@openmrs/esm-patient-common-lib';
|
|
|
21
19
|
import { MappedBill, PaymentStatus } from '../../types';
|
|
22
20
|
import styles from '../../bills-table/bills-table.scss';
|
|
23
21
|
import BillLineItems from './bill-line-items.component';
|
|
24
|
-
import {
|
|
25
|
-
import { ExtensionSlot, launchWorkspace, showModal } from '@openmrs/esm-framework';
|
|
22
|
+
import { ExtensionSlot } from '@openmrs/esm-framework';
|
|
26
23
|
|
|
27
24
|
type PatientBillsProps = {
|
|
28
25
|
bills: Array<MappedBill>;
|
package/src/billing.resource.ts
CHANGED
|
@@ -63,6 +63,7 @@ export const mapBillProperties = (bill: PatientInvoice): MappedBill => {
|
|
|
63
63
|
totalPayments: bill?.totalPayments,
|
|
64
64
|
totalDeposits: bill?.totalDeposits,
|
|
65
65
|
totalExempted: bill?.totalExempted,
|
|
66
|
+
closed: bill?.closed,
|
|
66
67
|
};
|
|
67
68
|
|
|
68
69
|
return mappedBill;
|
|
@@ -142,6 +143,7 @@ export const useBill = (billUuid: string) => {
|
|
|
142
143
|
totalDeposits: bill?.totalDeposits,
|
|
143
144
|
totalExempted: bill?.totalExempted,
|
|
144
145
|
balance: bill?.balance,
|
|
146
|
+
closed: bill?.closed,
|
|
145
147
|
};
|
|
146
148
|
|
|
147
149
|
return mappedBill;
|
package/src/index.ts
CHANGED
|
@@ -33,6 +33,7 @@ import RefundLineItem from './billable-services/bill-manager/bill-actions/refund
|
|
|
33
33
|
import WaiveBillActionButton from './billable-services/bill-manager/bill-actions/waive-bill-action-button.component';
|
|
34
34
|
import { DeleteBillModal } from './billable-services/bill-manager/modals/delete-bill.modal';
|
|
35
35
|
import { RefundBillModal } from './billable-services/bill-manager/modals/refund-bill.modal';
|
|
36
|
+
import BillActionModal from './modal/bill-action.modal';
|
|
36
37
|
import DeleteBillableServiceModal from './billable-services/bill-manager/modals/delete-billable-service.modal';
|
|
37
38
|
import CancelBillWorkspace from './billable-services/bill-manager/workspaces/cancel-bill/cancel-bill.workspace';
|
|
38
39
|
import { EditBillForm } from './billable-services/bill-manager/workspaces/edit-bill/edit-bill-form.workspace';
|
|
@@ -188,6 +189,7 @@ export const deleteBillModal = getSyncLifecycle(DeleteBillModal, options);
|
|
|
188
189
|
export const waiveBillForm = getSyncLifecycle(WaiveBillForm, options);
|
|
189
190
|
export const editBillForm = getSyncLifecycle(EditBillForm, options);
|
|
190
191
|
export const refundBillModal = getSyncLifecycle(RefundBillModal, options);
|
|
192
|
+
export const billActionModal = getSyncLifecycle(BillActionModal, options);
|
|
191
193
|
export const cancelBillWorkspace = getSyncLifecycle(CancelBillWorkspace, options);
|
|
192
194
|
export const waiveBillActionButton = getSyncLifecycle(WaiveBillActionButton, options);
|
|
193
195
|
export const deleteBillActionButton = getSyncLifecycle(DeleteBillActionButton, options);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Button, InlineLoading } from '@carbon/react';
|
|
2
|
-
import { BaggageClaim, Printer, Wallet } from '@carbon/react/icons';
|
|
1
|
+
import { Button, InlineLoading, Popover, PopoverContent } from '@carbon/react';
|
|
2
|
+
import { BaggageClaim, Close, Printer, Wallet, FolderOpen } from '@carbon/react/icons';
|
|
3
3
|
import {
|
|
4
4
|
defaultVisitCustomRepresentation,
|
|
5
5
|
ExtensionSlot,
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
updateVisit,
|
|
14
14
|
useFeatureFlag,
|
|
15
15
|
usePatient,
|
|
16
|
+
UserHasAccess,
|
|
16
17
|
useVisit,
|
|
17
18
|
useVisitContextStore,
|
|
18
19
|
} from '@openmrs/esm-framework';
|
|
@@ -29,7 +30,6 @@ import InvoiceTable from './invoice-table.component';
|
|
|
29
30
|
import { useShaFacilityStatus } from './invoice.resource';
|
|
30
31
|
import styles from './invoice.scss';
|
|
31
32
|
import Payments from './payments/payments.component';
|
|
32
|
-
import ReceiptPrintButton from './print-bill-receipt/receipt-print-button.component';
|
|
33
33
|
import capitalize from 'lodash-es/capitalize';
|
|
34
34
|
import { mutate } from 'swr';
|
|
35
35
|
import startCase from 'lodash-es/startCase';
|
|
@@ -170,16 +170,6 @@ const Invoice: React.FC = () => {
|
|
|
170
170
|
{patient && patientUuid && <ExtensionSlot name="patient-header-slot" state={{ patient, patientUuid }} />}
|
|
171
171
|
<InvoiceSummary bill={bill} />
|
|
172
172
|
<div className={styles.actionArea}>
|
|
173
|
-
<ReceiptPrintButton bill={bill} />
|
|
174
|
-
<Button
|
|
175
|
-
onClick={handlePrint}
|
|
176
|
-
kind="tertiary"
|
|
177
|
-
size="sm"
|
|
178
|
-
renderIcon={Printer}
|
|
179
|
-
iconDescription="Add"
|
|
180
|
-
tooltipPosition="right">
|
|
181
|
-
{t('printInvoice', 'Print invoice')}
|
|
182
|
-
</Button>
|
|
183
173
|
<Button
|
|
184
174
|
onClick={handleBillPayment}
|
|
185
175
|
disabled={bill?.status === 'PAID'}
|
|
@@ -212,10 +202,117 @@ const Invoice: React.FC = () => {
|
|
|
212
202
|
|
|
213
203
|
export function InvoiceSummary({ bill }: { readonly bill: MappedBill }) {
|
|
214
204
|
const { t } = useTranslation();
|
|
205
|
+
const launchBillCloseOrReopenModal = (action: 'close' | 'reopen') => {
|
|
206
|
+
const dispose = showModal('bill-action-modal', {
|
|
207
|
+
closeModal: () => dispose(),
|
|
208
|
+
bill: bill,
|
|
209
|
+
action,
|
|
210
|
+
});
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
const shouldCloseBill = bill.balance === 0 && !bill.closed;
|
|
214
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
215
|
+
|
|
216
|
+
const handlePrint = (documentType: string, documentTitle: string) => {
|
|
217
|
+
const dispose = showModal('print-preview-modal', {
|
|
218
|
+
onClose: () => dispose(),
|
|
219
|
+
title: documentTitle,
|
|
220
|
+
documentUrl: `/openmrs${restBaseUrl}/cashier/print?documentType=${documentType}&billId=${bill?.id}`,
|
|
221
|
+
});
|
|
222
|
+
};
|
|
223
|
+
|
|
215
224
|
return (
|
|
216
225
|
<>
|
|
217
226
|
<div className={styles.invoiceSummary}>
|
|
218
227
|
<span className={styles.invoiceSummaryTitle}>{t('invoiceSummary', 'Invoice Summary')}</span>
|
|
228
|
+
<div className="invoiceSummaryActions">
|
|
229
|
+
<Popover
|
|
230
|
+
isTabTip
|
|
231
|
+
align="bottom-right"
|
|
232
|
+
onKeyDown={() => {}}
|
|
233
|
+
onRequestClose={() => setIsOpen(false)}
|
|
234
|
+
open={isOpen}>
|
|
235
|
+
<button
|
|
236
|
+
className={styles.printButton}
|
|
237
|
+
aria-expanded
|
|
238
|
+
aria-label="Settings"
|
|
239
|
+
onClick={() => setIsOpen(!isOpen)}
|
|
240
|
+
type="button">
|
|
241
|
+
<Printer />
|
|
242
|
+
</button>
|
|
243
|
+
<PopoverContent>
|
|
244
|
+
<div className={styles.popoverContent}>
|
|
245
|
+
<Button
|
|
246
|
+
kind="ghost"
|
|
247
|
+
size="sm"
|
|
248
|
+
onClick={() =>
|
|
249
|
+
handlePrint(
|
|
250
|
+
'invoice',
|
|
251
|
+
`${t('invoice', 'Invoice')} ${bill?.receiptNumber} - ${startCase(bill?.patientName)}`,
|
|
252
|
+
)
|
|
253
|
+
}
|
|
254
|
+
renderIcon={Printer}>
|
|
255
|
+
{t('printInvoice', 'Print Invoice')}
|
|
256
|
+
</Button>
|
|
257
|
+
{bill.balance === 0 && (
|
|
258
|
+
<Button
|
|
259
|
+
kind="ghost"
|
|
260
|
+
size="sm"
|
|
261
|
+
onClick={() => {
|
|
262
|
+
const dispose = showModal('print-preview-modal', {
|
|
263
|
+
onClose: () => dispose(),
|
|
264
|
+
title: `${t('receipt', 'Receipt')} ${bill?.receiptNumber} - ${startCase(bill?.patientName)}`,
|
|
265
|
+
documentUrl: `/openmrs${restBaseUrl}/cashier/receipt?billId=${bill.id}`,
|
|
266
|
+
});
|
|
267
|
+
}}
|
|
268
|
+
renderIcon={Printer}>
|
|
269
|
+
{t('printReceipt', 'Print Receipt')}
|
|
270
|
+
</Button>
|
|
271
|
+
)}
|
|
272
|
+
<Button
|
|
273
|
+
kind="ghost"
|
|
274
|
+
size="sm"
|
|
275
|
+
onClick={() =>
|
|
276
|
+
handlePrint(
|
|
277
|
+
'billstatement',
|
|
278
|
+
`${t('billStatement', 'Bill Statement')} ${bill?.receiptNumber} - ${startCase(
|
|
279
|
+
bill?.patientName,
|
|
280
|
+
)}`,
|
|
281
|
+
)
|
|
282
|
+
}
|
|
283
|
+
renderIcon={Printer}>
|
|
284
|
+
{t('printBillStatement', 'Print Bill Statement')}
|
|
285
|
+
</Button>
|
|
286
|
+
</div>
|
|
287
|
+
</PopoverContent>
|
|
288
|
+
</Popover>
|
|
289
|
+
{shouldCloseBill && (
|
|
290
|
+
<UserHasAccess privilege="Close Cashier Bills">
|
|
291
|
+
<Button
|
|
292
|
+
kind="danger--ghost"
|
|
293
|
+
size="sm"
|
|
294
|
+
renderIcon={Close}
|
|
295
|
+
iconDescription="Add"
|
|
296
|
+
tooltipPosition="right"
|
|
297
|
+
onClick={() => launchBillCloseOrReopenModal('close')}>
|
|
298
|
+
{t('closeBill', 'Close Bill')}
|
|
299
|
+
</Button>
|
|
300
|
+
</UserHasAccess>
|
|
301
|
+
)}
|
|
302
|
+
{bill?.closed && (
|
|
303
|
+
<UserHasAccess privilege="Reopen Cashier Bills">
|
|
304
|
+
<Button
|
|
305
|
+
kind="ghost"
|
|
306
|
+
size="sm"
|
|
307
|
+
renderIcon={FolderOpen}
|
|
308
|
+
iconDescription="Add"
|
|
309
|
+
tooltipPosition="right"
|
|
310
|
+
onClick={() => launchBillCloseOrReopenModal('reopen')}>
|
|
311
|
+
{t('reopen', 'Reopen')}
|
|
312
|
+
</Button>
|
|
313
|
+
</UserHasAccess>
|
|
314
|
+
)}
|
|
315
|
+
</div>
|
|
219
316
|
</div>
|
|
220
317
|
<div className={styles.invoiceSummaryContainer}>
|
|
221
318
|
<div className={styles.invoiceCard}>
|
|
@@ -109,3 +109,44 @@ export const useShaFacilityStatus = () => {
|
|
|
109
109
|
mutate,
|
|
110
110
|
};
|
|
111
111
|
};
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Reopens or closes a bill by making an API call to the billing service.
|
|
115
|
+
*
|
|
116
|
+
* This function allows authorized users to either reopen a closed bill or close an open bill.
|
|
117
|
+
* The action requires a reason to be provided for audit trail purposes.
|
|
118
|
+
*
|
|
119
|
+
* @param {string} billUuid - The unique identifier of the bill to be modified
|
|
120
|
+
* @param {'reopen' | 'close'} action - The action to perform on the bill
|
|
121
|
+
* - 'reopen': Reopens a previously closed bill
|
|
122
|
+
* - 'close': Closes an open bill
|
|
123
|
+
* @param {Object} payload - The payload containing the reason for the action
|
|
124
|
+
* @param {string} payload.reason - A descriptive reason explaining why the bill is being reopened or closed
|
|
125
|
+
*
|
|
126
|
+
* @returns {Promise<FetchResponse>} A promise that resolves to the API response
|
|
127
|
+
*
|
|
128
|
+
* @example
|
|
129
|
+
* // Reopen a closed bill
|
|
130
|
+
* const result = await reOpenOrCloseBill('bill-uuid-123', 'reopen', {
|
|
131
|
+
* reason: 'Patient returned for additional services'
|
|
132
|
+
* });
|
|
133
|
+
*
|
|
134
|
+
* @example
|
|
135
|
+
* // Close an open bill
|
|
136
|
+
* const result = await reOpenOrCloseBill('bill-uuid-456', 'close', {
|
|
137
|
+
* reason: 'Services completed and payment received'
|
|
138
|
+
* });
|
|
139
|
+
*
|
|
140
|
+
* @throws {Error} When the API call fails or returns an error response
|
|
141
|
+
*/
|
|
142
|
+
export function reOpenOrCloseBill(billUuid: string, action: 'reopen' | 'close', payload: { reason: string }) {
|
|
143
|
+
return openmrsFetch(`${restBaseUrl}/kenyaemr-cashier/bill/${billUuid}/${action}`, {
|
|
144
|
+
method: 'POST',
|
|
145
|
+
headers: {
|
|
146
|
+
'Content-Type': 'application/json',
|
|
147
|
+
},
|
|
148
|
+
body: {
|
|
149
|
+
reason: payload.reason,
|
|
150
|
+
},
|
|
151
|
+
});
|
|
152
|
+
}
|
package/src/invoice/invoice.scss
CHANGED
|
@@ -130,12 +130,37 @@
|
|
|
130
130
|
}
|
|
131
131
|
}
|
|
132
132
|
|
|
133
|
-
.
|
|
134
|
-
|
|
135
|
-
|
|
133
|
+
.invoiceSummary {
|
|
134
|
+
display: flex;
|
|
135
|
+
justify-content: space-between;
|
|
136
|
+
align-items: center;
|
|
136
137
|
margin: layout.$spacing-05 layout.$spacing-05 0 layout.$spacing-05;
|
|
137
138
|
background-color: colors.$white;
|
|
138
|
-
display: block;
|
|
139
139
|
border-bottom: 1px solid colors.$gray-20;
|
|
140
|
-
padding: layout.$spacing-04;
|
|
140
|
+
padding: layout.$spacing-01 layout.$spacing-04;
|
|
141
|
+
|
|
142
|
+
.invoiceSummaryActions {
|
|
143
|
+
display: flex;
|
|
144
|
+
gap: layout.$spacing-04;
|
|
145
|
+
}
|
|
146
|
+
.invoiceSummaryTitle {
|
|
147
|
+
@include type.type-style('heading-01');
|
|
148
|
+
color: colors.$gray-70;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
.popoverContent {
|
|
153
|
+
display: flex;
|
|
154
|
+
flex-direction: column;
|
|
155
|
+
gap: layout.$spacing-01;
|
|
156
|
+
|
|
157
|
+
& > button {
|
|
158
|
+
width: 100%;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
.printButton {
|
|
163
|
+
svg {
|
|
164
|
+
fill: colors.$blue-60;
|
|
165
|
+
}
|
|
141
166
|
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { ModalBody, ModalFooter, ModalHeader, Button, TextArea } from '@carbon/react';
|
|
3
|
+
import { type MappedBill } from '../types';
|
|
4
|
+
import { useTranslation } from 'react-i18next';
|
|
5
|
+
import { convertToCurrency } from '../helpers';
|
|
6
|
+
import { reOpenOrCloseBill } from '../invoice/invoice.resource';
|
|
7
|
+
import { showSnackbar } from '@openmrs/esm-framework';
|
|
8
|
+
import { mutate } from 'swr';
|
|
9
|
+
import { Controller, useForm } from 'react-hook-form';
|
|
10
|
+
import { z } from 'zod';
|
|
11
|
+
import { zodResolver } from '@hookform/resolvers/zod';
|
|
12
|
+
import styles from './bill-action.modal.scss';
|
|
13
|
+
|
|
14
|
+
type BillActionModalProps = {
|
|
15
|
+
closeModal: () => void;
|
|
16
|
+
bill: MappedBill;
|
|
17
|
+
action: 'close' | 'reopen';
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const formSchema = z.object({
|
|
21
|
+
reason: z.string().min(1, { message: 'Reason is required' }),
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
type FormData = z.infer<typeof formSchema>;
|
|
25
|
+
|
|
26
|
+
const BillActionModal: React.FC<BillActionModalProps> = (props) => {
|
|
27
|
+
const { closeModal, bill, action } = props;
|
|
28
|
+
const { t } = useTranslation();
|
|
29
|
+
const formMethod = useForm({
|
|
30
|
+
defaultValues: {
|
|
31
|
+
reason: '',
|
|
32
|
+
},
|
|
33
|
+
resolver: zodResolver(formSchema),
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
const modalExplanation =
|
|
37
|
+
action === 'close'
|
|
38
|
+
? t('closeBillExplanation', 'Closing this bill will prevent any new items from being added to this bill')
|
|
39
|
+
: t('reopenBillExplanation', 'Reopening this bill will allow new items to be added to this bill');
|
|
40
|
+
|
|
41
|
+
const handleCloseBill = async (data: FormData) => {
|
|
42
|
+
try {
|
|
43
|
+
const response = await reOpenOrCloseBill(bill?.uuid, action, {
|
|
44
|
+
reason: data.reason,
|
|
45
|
+
});
|
|
46
|
+
if (response?.ok) {
|
|
47
|
+
showSnackbar({
|
|
48
|
+
title: t('billClosedSuccessfully', 'Bill {{action}} successfully', { action: action }),
|
|
49
|
+
subtitle: t('billClosedSuccessfullySubtitle', 'The bill has been {{action}} successfully', {
|
|
50
|
+
action: action,
|
|
51
|
+
}),
|
|
52
|
+
kind: 'success',
|
|
53
|
+
timeoutInMs: 3000,
|
|
54
|
+
isLowContrast: true,
|
|
55
|
+
});
|
|
56
|
+
} else {
|
|
57
|
+
throw new Error('Failed to close bill');
|
|
58
|
+
}
|
|
59
|
+
} catch (error) {
|
|
60
|
+
const errorResponseBody = error?.responseBody?.error || t('errorResponseBodyMessage', 'An error occurred');
|
|
61
|
+
|
|
62
|
+
showSnackbar({
|
|
63
|
+
title: t('billClosedFailed', 'Bill closing failed'),
|
|
64
|
+
subtitle: errorResponseBody,
|
|
65
|
+
kind: 'error',
|
|
66
|
+
timeoutInMs: 3000,
|
|
67
|
+
isLowContrast: true,
|
|
68
|
+
});
|
|
69
|
+
} finally {
|
|
70
|
+
const url = `/ws/rest/v1/cashier/bill/${bill.uuid}`;
|
|
71
|
+
mutate((key) => typeof key === 'string' && key.startsWith(url), undefined, { revalidate: true });
|
|
72
|
+
closeModal();
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
return (
|
|
76
|
+
<form onSubmit={formMethod.handleSubmit(handleCloseBill, (error) => console.error('error', error))}>
|
|
77
|
+
<ModalHeader
|
|
78
|
+
closeModal={closeModal}
|
|
79
|
+
label={t('billActionWithDetails', '{{action}} Bill - {{receiptNumber}} ({{status}}, {{amount}})', {
|
|
80
|
+
action: action === 'close' ? t('close', 'Close') : t('reopen', 'Reopen'),
|
|
81
|
+
receiptNumber: bill?.receiptNumber,
|
|
82
|
+
status: bill?.status,
|
|
83
|
+
amount: bill?.totalAmount ? `${convertToCurrency(bill?.totalAmount)}` : 'N/A',
|
|
84
|
+
})}
|
|
85
|
+
title={t('billAction', '{{action}} Bill', {
|
|
86
|
+
action: action === 'close' ? t('close', 'Close') : t('reopen', 'Reopen'),
|
|
87
|
+
})}
|
|
88
|
+
/>
|
|
89
|
+
<ModalBody>
|
|
90
|
+
<div>
|
|
91
|
+
<p className={styles.modalExplanation}>{modalExplanation}</p>
|
|
92
|
+
</div>
|
|
93
|
+
<Controller
|
|
94
|
+
control={formMethod.control}
|
|
95
|
+
name="reason"
|
|
96
|
+
render={({ field }) => (
|
|
97
|
+
<TextArea
|
|
98
|
+
id="reason"
|
|
99
|
+
labelText={t('reason', 'Reason for {{action}} bill', { action: action })}
|
|
100
|
+
placeholder={t('reason', 'Reason for {{action}} bill', { action: action })}
|
|
101
|
+
rows={4}
|
|
102
|
+
onChange={field.onChange}
|
|
103
|
+
value={field.value}
|
|
104
|
+
invalid={!!formMethod.formState.errors.reason}
|
|
105
|
+
invalidText={formMethod.formState.errors.reason?.message}
|
|
106
|
+
/>
|
|
107
|
+
)}
|
|
108
|
+
/>
|
|
109
|
+
</ModalBody>
|
|
110
|
+
<ModalFooter>
|
|
111
|
+
<Button kind="secondary" onClick={closeModal}>
|
|
112
|
+
{t('cancel', 'Cancel')}
|
|
113
|
+
</Button>
|
|
114
|
+
<Button disabled={!formMethod.formState.isValid} type="submit" kind="danger">
|
|
115
|
+
{action === 'close' ? t('close', 'Close') : t('reopen', 'Reopen')}
|
|
116
|
+
</Button>
|
|
117
|
+
</ModalFooter>
|
|
118
|
+
</form>
|
|
119
|
+
);
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
export default BillActionModal;
|
|
@@ -36,7 +36,6 @@ export const patientBillsHeaders = [
|
|
|
36
36
|
export const PatientBills: React.FC<PatientBillsProps> = ({ bills, onCancel, patientUuid }) => {
|
|
37
37
|
const { t } = useTranslation();
|
|
38
38
|
const { patient, isLoading, error } = usePatient(patientUuid);
|
|
39
|
-
|
|
40
39
|
if (isLoading) {
|
|
41
40
|
return <InlineLoading status="active" description={t('loading', 'Loading...')} />;
|
|
42
41
|
}
|
|
@@ -63,7 +62,7 @@ export const PatientBills: React.FC<PatientBillsProps> = ({ bills, onCancel, pat
|
|
|
63
62
|
style={{ textDecoration: 'none', maxWidth: '50%' }}
|
|
64
63
|
to={billingUrl}
|
|
65
64
|
templateParams={{ patientUuid: bill.patientUuid, uuid: bill.uuid }}>
|
|
66
|
-
{bill.lineItems.map((item) => item
|
|
65
|
+
{bill.lineItems.map((item) => item?.billableService?.split(':')[1]).join(', ')}
|
|
67
66
|
</ConfigurableLink>
|
|
68
67
|
),
|
|
69
68
|
totalAmount: convertToCurrency(bill.totalAmount),
|
|
@@ -28,7 +28,6 @@ const PromptPaymentModal: React.FC<PromptPaymentModalProps> = () => {
|
|
|
28
28
|
const { shouldShowBillingPrompt, isLoading, bills } = useBillingPrompt(patientUuid, 'patient-chart');
|
|
29
29
|
const [showModal, setShowModal] = useState({ loadingModal: true, billingModal: true });
|
|
30
30
|
const { enforceBillPayment } = useConfig<BillingConfig>();
|
|
31
|
-
|
|
32
31
|
const closeButtonText = enforceBillPayment
|
|
33
32
|
? t('navigateBack', 'Navigate back')
|
|
34
33
|
: t('proceedToCare', 'Proceed to care');
|
|
@@ -42,7 +41,9 @@ const PromptPaymentModal: React.FC<PromptPaymentModalProps> = () => {
|
|
|
42
41
|
const lineItems = bills
|
|
43
42
|
.filter((bill) => bill.status !== 'PAID')
|
|
44
43
|
.flatMap((bill) => bill.lineItems)
|
|
45
|
-
.filter(
|
|
44
|
+
.filter(
|
|
45
|
+
(lineItem) => lineItem?.paymentStatus !== 'EXEMPTED' && lineItem?.paymentStatus !== 'PAID' && !lineItem?.voided,
|
|
46
|
+
);
|
|
46
47
|
|
|
47
48
|
if (!shouldShowBillingPrompt) {
|
|
48
49
|
return null;
|
|
@@ -71,6 +72,7 @@ const PromptPaymentModal: React.FC<PromptPaymentModalProps> = () => {
|
|
|
71
72
|
<StructuredListCell head>{t('item', 'Item')}</StructuredListCell>
|
|
72
73
|
<StructuredListCell head>{t('quantity', 'Quantity')}</StructuredListCell>
|
|
73
74
|
<StructuredListCell head>{t('unitPrice', 'Unit price')}</StructuredListCell>
|
|
75
|
+
<StructuredListCell head>{t('status', 'Status')}</StructuredListCell>
|
|
74
76
|
<StructuredListCell head>{t('total', 'Total')}</StructuredListCell>
|
|
75
77
|
</StructuredListRow>
|
|
76
78
|
</StructuredListHead>
|
|
@@ -81,6 +83,7 @@ const PromptPaymentModal: React.FC<PromptPaymentModalProps> = () => {
|
|
|
81
83
|
<StructuredListCell>{extractString(lineItem.billableService || lineItem.item)}</StructuredListCell>
|
|
82
84
|
<StructuredListCell>{lineItem.quantity}</StructuredListCell>
|
|
83
85
|
<StructuredListCell>{convertToCurrency(lineItem.price)}</StructuredListCell>
|
|
86
|
+
<StructuredListCell>{lineItem.paymentStatus}</StructuredListCell>
|
|
84
87
|
<StructuredListCell>{convertToCurrency(lineItem.quantity * lineItem.price)}</StructuredListCell>
|
|
85
88
|
</StructuredListRow>
|
|
86
89
|
);
|
|
@@ -71,7 +71,7 @@ const shouldShowPrompt = (
|
|
|
71
71
|
const hasOnlyOrderBills = (bills: Array<MappedBill>): boolean => {
|
|
72
72
|
const flattenedBills = bills.flatMap((bill) => bill.lineItems);
|
|
73
73
|
// check if all line items are orders, line item with order has order not set to null
|
|
74
|
-
return flattenedBills.every((item) => item
|
|
74
|
+
return flattenedBills.every((item) => item?.order);
|
|
75
75
|
};
|
|
76
76
|
|
|
77
77
|
/**
|
package/src/routes.json
CHANGED
package/src/types/index.ts
CHANGED
|
@@ -26,6 +26,7 @@ export interface MappedBill {
|
|
|
26
26
|
totalDeposits?: number;
|
|
27
27
|
totalExempted?: number;
|
|
28
28
|
balance?: number;
|
|
29
|
+
closed?: boolean;
|
|
29
30
|
}
|
|
30
31
|
|
|
31
32
|
interface LocationLink {
|
|
@@ -145,6 +146,7 @@ export interface PatientInvoice {
|
|
|
145
146
|
totalDeposits?: number;
|
|
146
147
|
totalExempted?: number;
|
|
147
148
|
balance?: number;
|
|
149
|
+
closed?: boolean;
|
|
148
150
|
}
|
|
149
151
|
|
|
150
152
|
export interface PatientDetails {
|
package/src/utils.ts
CHANGED
|
@@ -162,6 +162,10 @@ export function waitForASecond(): Promise<string> {
|
|
|
162
162
|
}
|
|
163
163
|
|
|
164
164
|
export const computeWaivedAmount = (bill: MappedBill) => {
|
|
165
|
+
if (!bill.payments) {
|
|
166
|
+
return 0;
|
|
167
|
+
}
|
|
168
|
+
|
|
165
169
|
return bill.payments
|
|
166
170
|
.filter((payment) => payment.instanceType.name.toLowerCase() === 'waiver')
|
|
167
171
|
.reduce((curr: number, prev) => curr + Number(prev.amountTendered), 0);
|
package/translations/en.json
CHANGED
|
@@ -46,6 +46,7 @@
|
|
|
46
46
|
"billPayment": "Bill payment",
|
|
47
47
|
"billPaymentRequiredMessage": "The current patient has pending bill. Advice patient to settle bill.",
|
|
48
48
|
"billsList": "Bill list",
|
|
49
|
+
"billStatement": "Bill Statement",
|
|
49
50
|
"billTotal": "Bill total",
|
|
50
51
|
"bulkUpload": "Bulk Upload",
|
|
51
52
|
"cancel": "Cancel",
|
|
@@ -77,6 +78,7 @@
|
|
|
77
78
|
"clearSearch": "Clear search input",
|
|
78
79
|
"clockInTime": "Clocked in on {{clockInDate}}",
|
|
79
80
|
"close": "Close",
|
|
81
|
+
"closeBill": "Close Bill",
|
|
80
82
|
"create": "Create",
|
|
81
83
|
"createClaimError": "Create Claim error",
|
|
82
84
|
"created": "Created",
|
|
@@ -230,6 +232,7 @@
|
|
|
230
232
|
"previousPage": "Previous page",
|
|
231
233
|
"price": "Price",
|
|
232
234
|
"prices": "Prices",
|
|
235
|
+
"printBillStatement": "Print Bill Statement",
|
|
233
236
|
"printInvoice": "Print invoice",
|
|
234
237
|
"printReceipt": "Print receipt",
|
|
235
238
|
"proceedToCare": "Proceed to care",
|
|
@@ -248,6 +251,7 @@
|
|
|
248
251
|
"regExp": "Regular expression",
|
|
249
252
|
"rejected": "Rejected",
|
|
250
253
|
"remove": "Remove",
|
|
254
|
+
"reopen": "Reopen",
|
|
251
255
|
"retryRequest": "Retry request",
|
|
252
256
|
"reverse": "Reverse",
|
|
253
257
|
"saveAndClose": "Save & Close",
|