@openmrs/esm-billing-app 1.1.2-pre.9 → 1.2.0
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/cache/31f1dfc7f71601df-meta.json +1 -0
- package/.turbo/cache/31f1dfc7f71601df.tar.zst +0 -0
- package/.turbo/turbo-build.log +13 -42
- package/__mocks__/bills.mock.ts +3 -2
- package/dist/1480.js +1 -0
- package/dist/1480.js.map +1 -0
- package/dist/1564.js +1 -0
- package/dist/1564.js.map +1 -0
- package/dist/1578.js +1 -0
- package/dist/1578.js.map +1 -0
- package/dist/1646.js +1 -0
- package/dist/1646.js.map +1 -0
- package/dist/1869.js +1 -0
- package/dist/1869.js.map +1 -0
- package/dist/1877.js +1 -0
- package/dist/1877.js.map +1 -0
- package/dist/1899.js +1 -0
- package/dist/1899.js.map +1 -0
- package/dist/196.js +2 -0
- package/dist/196.js.map +1 -0
- package/dist/2250.js +43 -0
- package/dist/2250.js.map +1 -0
- package/dist/2269.js +1 -0
- package/dist/2269.js.map +1 -0
- package/dist/2317.js +1 -0
- package/dist/2317.js.map +1 -0
- package/dist/2416.js +1 -0
- package/dist/2416.js.map +1 -0
- package/dist/2489.js +1 -0
- package/dist/2489.js.map +1 -0
- package/dist/282.js +1 -0
- package/dist/282.js.map +1 -0
- package/dist/2881.js +1 -0
- package/dist/2881.js.map +1 -0
- package/dist/2997.js +1 -0
- package/dist/2997.js.map +1 -0
- package/dist/3378.js +1 -0
- package/dist/3378.js.map +1 -0
- package/dist/3379.js +1 -0
- package/dist/3379.js.map +1 -0
- package/dist/3784.js +1 -0
- package/dist/3784.js.map +1 -0
- package/dist/3963.js +1 -0
- package/dist/3963.js.map +1 -0
- package/dist/4106.js +1 -0
- package/dist/4106.js.map +1 -0
- package/dist/4111.js +1 -0
- package/dist/4111.js.map +1 -0
- package/dist/434.js +1 -0
- package/dist/434.js.map +1 -0
- package/dist/4348.js +1 -0
- package/dist/4348.js.map +1 -0
- package/dist/4383.js +1 -0
- package/dist/4383.js.map +1 -0
- package/dist/4658.js +1 -0
- package/dist/4658.js.map +1 -0
- package/dist/4870.js +1 -0
- package/dist/4870.js.map +1 -0
- package/dist/4928.js +1 -0
- package/dist/4928.js.map +1 -0
- package/dist/5098.js +1 -0
- package/dist/5098.js.map +1 -0
- package/dist/5117.js +1 -0
- package/dist/5117.js.map +1 -0
- package/dist/5132.js +1 -0
- package/dist/5132.js.map +1 -0
- package/dist/5145.js +1 -0
- package/dist/5145.js.map +1 -0
- package/dist/5390.js +1 -0
- package/dist/5390.js.map +1 -0
- package/dist/5503.js +1 -0
- package/dist/5503.js.map +1 -0
- package/dist/556.js +1 -0
- package/dist/556.js.map +1 -0
- package/dist/5644.js +1 -0
- package/dist/5644.js.map +1 -0
- package/dist/5898.js +1 -0
- package/dist/5898.js.map +1 -0
- package/dist/5940.js +1 -0
- package/dist/5940.js.map +1 -0
- package/dist/6047.js +1 -0
- package/dist/6047.js.map +1 -0
- package/dist/6237.js +1 -0
- package/dist/6237.js.map +1 -0
- package/dist/6362.js +1 -0
- package/dist/6362.js.map +1 -0
- package/dist/6371.js +1 -0
- package/dist/6371.js.map +1 -0
- package/dist/6377.js +1 -0
- package/dist/6377.js.map +1 -0
- package/dist/6444.js +1 -0
- package/dist/6444.js.map +1 -0
- package/dist/6508.js +1 -0
- package/dist/6508.js.map +1 -0
- package/dist/6594.js +1 -0
- package/dist/6594.js.map +1 -0
- package/dist/6724.js +1 -0
- package/dist/6724.js.map +1 -0
- package/dist/6904.js +1 -0
- package/dist/6904.js.map +1 -0
- package/dist/7045.js +1 -0
- package/dist/7045.js.map +1 -0
- package/dist/7175.js +1 -0
- package/dist/7175.js.map +1 -0
- package/dist/7182.js +1 -0
- package/dist/7182.js.map +1 -0
- package/dist/7247.js +1 -0
- package/dist/7247.js.map +1 -0
- package/dist/7742.js +1 -0
- package/dist/7742.js.map +1 -0
- package/dist/7912.js +1 -0
- package/dist/7912.js.map +1 -0
- package/dist/8358.js +1 -0
- package/dist/8358.js.map +1 -0
- package/dist/8359.js +1 -0
- package/dist/8359.js.map +1 -0
- package/dist/8695.js +1 -0
- package/dist/8695.js.map +1 -0
- package/dist/903.js +1 -0
- package/dist/903.js.map +1 -0
- package/dist/9072.js +1 -0
- package/dist/9072.js.map +1 -0
- package/dist/9414.js +1 -0
- package/dist/9414.js.map +1 -0
- package/dist/9655.js +11 -0
- package/dist/9655.js.map +1 -0
- package/dist/9806.js +1 -0
- package/dist/9806.js.map +1 -0
- package/dist/990.js +1 -0
- package/dist/990.js.map +1 -0
- package/dist/main.js +17 -2
- package/dist/main.js.map +1 -1
- package/dist/openmrs-esm-billing-app.js +6 -1
- package/dist/openmrs-esm-billing-app.js.buildmanifest.json +643 -436
- package/dist/openmrs-esm-billing-app.js.map +1 -1
- package/dist/routes.json +1 -1
- package/e2e/commands/billing-operations.ts +21 -0
- package/e2e/commands/types.ts +9 -1
- package/e2e/pages/discounts-page.ts +75 -0
- package/e2e/pages/index.ts +1 -0
- package/e2e/pages/invoice-page.ts +7 -7
- package/e2e/specs/bill-discounts.spec.ts +255 -0
- package/e2e/specs/billing-dashboard.spec.ts +3 -3
- package/e2e/specs/billing-patient-chart.spec.ts +2 -2
- package/package.json +13 -22
- package/rspack.config.js +1 -0
- package/src/bill-history/bill-action-menu.component.tsx +20 -2
- package/src/bill-history/bill-history.test.tsx +23 -22
- package/src/bill-item-actions/edit-bill-item.modal.tsx +1 -1
- package/src/bill-item-actions/edit-bill-item.test.tsx +29 -27
- package/src/billable-services/billable-service-form/billable-service-form.test.tsx +74 -73
- package/src/billable-services/billable-services-home.component.tsx +4 -2
- package/src/billable-services/billable-services.test.tsx +8 -7
- package/src/billable-services/dashboard/dashboard.test.tsx +3 -2
- package/src/billable-services-admin-card-link.test.tsx +2 -1
- package/src/billing-dashboard/billing-dashboard.test.tsx +19 -3
- package/src/billing-form/billing-checkin-form.component.tsx +7 -3
- package/src/billing-form/billing-checkin-form.test.tsx +22 -21
- package/src/billing-form/billing-form.resource.test.ts +7 -6
- package/src/billing-form/billing-form.test.tsx +77 -40
- package/src/billing-form/billing-form.workspace.tsx +25 -6
- package/src/billing-form/visit-attributes/visit-attributes-form.component.tsx +6 -2
- package/src/billing.resource.test.ts +43 -41
- package/src/billing.resource.ts +65 -16
- package/src/bills-table/bills-table.component.tsx +15 -4
- package/src/bills-table/bills-table.test.tsx +72 -71
- package/src/config-schema.ts +0 -7
- package/src/discounts/admin/discount-requests-left-panel-link.component.tsx +43 -0
- package/src/discounts/admin/discount-requests.component.tsx +316 -0
- package/src/discounts/admin/discount-requests.scss +133 -0
- package/src/discounts/admin/discount-requests.test.tsx +104 -0
- package/src/discounts/admin/review-bill-discounts/bill-line-items-table/bill-line-items-table.component.tsx +42 -0
- package/src/discounts/admin/review-bill-discounts/bill-line-items-table/bill-line-items-table.scss +76 -0
- package/src/discounts/admin/review-bill-discounts/bill-payments-table/bill-payments-table.component.tsx +50 -0
- package/src/discounts/admin/review-bill-discounts/bill-payments-table/bill-payments-table.scss +63 -0
- package/src/discounts/admin/review-bill-discounts/bill-receipt-rail/bill-receipt-rail.component.tsx +73 -0
- package/src/discounts/admin/review-bill-discounts/bill-receipt-rail/bill-receipt-rail.scss +54 -0
- package/src/discounts/admin/review-bill-discounts/bill-totals-summary/bill-totals-summary.component.tsx +95 -0
- package/src/discounts/admin/review-bill-discounts/bill-totals-summary/bill-totals-summary.scss +128 -0
- package/src/discounts/admin/review-bill-discounts/discount-card/discount-card.component.tsx +158 -0
- package/src/discounts/admin/review-bill-discounts/discount-card/discount-card.scss +164 -0
- package/src/discounts/admin/review-bill-discounts/discount-review-stack/discount-review-stack.component.tsx +86 -0
- package/src/discounts/admin/review-bill-discounts/discount-review-stack/discount-review-stack.scss +40 -0
- package/src/discounts/admin/review-bill-discounts/review-bill-discounts.modal.scss +14 -0
- package/src/discounts/admin/review-bill-discounts/review-bill-discounts.modal.test.tsx +153 -0
- package/src/discounts/admin/review-bill-discounts/review-bill-discounts.modal.tsx +167 -0
- package/src/discounts/admin/review-bill-discounts/review-bill-discounts.utils.ts +42 -0
- package/src/discounts/discounts-table.component.tsx +109 -0
- package/src/discounts/discounts-table.scss +37 -0
- package/src/discounts/discounts-table.test.tsx +67 -0
- package/src/discounts/discounts.resource.ts +71 -0
- package/src/discounts/request-discount.modal.scss +88 -0
- package/src/discounts/request-discount.modal.test.tsx +161 -0
- package/src/discounts/request-discount.modal.tsx +253 -0
- package/src/index.ts +52 -21
- package/src/invoice/invoice-table.component.tsx +116 -18
- package/src/invoice/invoice-table.test.tsx +165 -13
- package/src/invoice/invoice.component.tsx +111 -7
- package/src/invoice/invoice.test.tsx +366 -66
- package/src/invoice/line-item-action-menu.component.tsx +31 -1
- package/src/invoice/payments/payment-form/payment-form.test.tsx +20 -19
- package/src/invoice/payments/payment-history/payment-history.test.tsx +13 -10
- package/src/invoice/payments/payments.component.tsx +20 -6
- package/src/invoice/payments/payments.test.tsx +88 -23
- package/src/invoice/printable-invoice/print-receipt.test.tsx +10 -28
- package/src/invoice/printable-invoice/printable-footer.test.tsx +5 -4
- package/src/invoice/printable-invoice/printable-invoice-header.component.tsx +3 -3
- package/src/invoice/printable-invoice/printable-invoice-header.test.tsx +26 -11
- package/src/invoice/printable-invoice/printable-invoice.component.tsx +38 -15
- package/src/left-panel-link.test.tsx +3 -3
- package/src/metrics-cards/metrics-cards.test.tsx +11 -10
- package/src/modal/delete-bill-confirmation.modal.test.tsx +134 -0
- package/src/modal/delete-bill-confirmation.modal.tsx +98 -0
- package/src/modal/delete-line-item-confirmation.modal.test.tsx +11 -9
- package/src/modal/finalize-bill-confirmation.modal.test.tsx +10 -8
- package/src/modal/require-payment-modal.test.tsx +12 -11
- package/src/payment-status-tag/payment-status-tag.component.tsx +50 -0
- package/src/payment-status-tag/payment-status-tag.scss +6 -0
- package/src/payment-status-tag/payment-status-tag.test.tsx +113 -0
- package/src/refunds/admin/refund-requests-left-panel-link.component.tsx +43 -0
- package/src/refunds/admin/refund-requests.component.tsx +324 -0
- package/src/refunds/admin/refund-requests.scss +133 -0
- package/src/refunds/admin/refund-requests.test.tsx +99 -0
- package/src/refunds/admin/review-bill-refunds/bill-line-items-table/bill-line-items-table.component.tsx +42 -0
- package/src/refunds/admin/review-bill-refunds/bill-line-items-table/bill-line-items-table.scss +76 -0
- package/src/refunds/admin/review-bill-refunds/bill-payments-table/bill-payments-table.component.tsx +50 -0
- package/src/refunds/admin/review-bill-refunds/bill-payments-table/bill-payments-table.scss +63 -0
- package/src/refunds/admin/review-bill-refunds/bill-receipt-rail/bill-receipt-rail.component.tsx +84 -0
- package/src/refunds/admin/review-bill-refunds/bill-receipt-rail/bill-receipt-rail.scss +54 -0
- package/src/refunds/admin/review-bill-refunds/bill-totals-summary/bill-totals-summary.component.tsx +83 -0
- package/src/refunds/admin/review-bill-refunds/bill-totals-summary/bill-totals-summary.scss +65 -0
- package/src/refunds/admin/review-bill-refunds/refund-card/refund-card.component.tsx +170 -0
- package/src/refunds/admin/review-bill-refunds/refund-card/refund-card.scss +155 -0
- package/src/refunds/admin/review-bill-refunds/refund-review-stack/refund-review-stack.component.tsx +86 -0
- package/src/refunds/admin/review-bill-refunds/refund-review-stack/refund-review-stack.scss +40 -0
- package/src/refunds/admin/review-bill-refunds/review-bill-refunds.modal.scss +14 -0
- package/src/refunds/admin/review-bill-refunds/review-bill-refunds.modal.test.tsx +313 -0
- package/src/refunds/admin/review-bill-refunds/review-bill-refunds.modal.tsx +188 -0
- package/src/refunds/admin/review-bill-refunds/review-bill-refunds.utils.ts +66 -0
- package/src/refunds/refunds-table.component.tsx +137 -0
- package/src/refunds/refunds-table.scss +37 -0
- package/src/refunds/refunds-table.test.tsx +105 -0
- package/src/refunds/refunds.resource.test.ts +44 -0
- package/src/refunds/refunds.resource.ts +42 -0
- package/src/refunds/refunds.types.test.ts +15 -0
- package/src/refunds/request-refund.modal.scss +84 -0
- package/src/refunds/request-refund.modal.test.tsx +204 -0
- package/src/refunds/request-refund.modal.tsx +218 -0
- package/src/routes.json +36 -2
- package/src/types/index.ts +116 -1
- package/src/visit-bills/visit-bills-panel.component.tsx +151 -0
- package/src/visit-bills/visit-bills-panel.scss +31 -0
- package/src/visit-bills/visit-bills-panel.test.tsx +113 -0
- package/tools/empty-module.ts +1 -0
- package/tools/setup-tests.ts +9 -9
- package/translations/am.json +154 -16
- package/translations/ar.json +154 -16
- package/translations/ar_SY.json +154 -16
- package/translations/bn.json +154 -16
- package/translations/cs.json +154 -16
- package/translations/de.json +154 -16
- package/translations/en.json +154 -16
- package/translations/en_US.json +154 -16
- package/translations/es.json +154 -16
- package/translations/es_MX.json +154 -16
- package/translations/fr.json +154 -16
- package/translations/he.json +154 -16
- package/translations/hi.json +154 -16
- package/translations/hi_IN.json +154 -16
- package/translations/id.json +154 -16
- package/translations/it.json +154 -16
- package/translations/ka.json +154 -16
- package/translations/km.json +154 -16
- package/translations/ku.json +154 -16
- package/translations/ky.json +154 -16
- package/translations/lg.json +154 -16
- package/translations/ne.json +154 -16
- package/translations/pl.json +154 -16
- package/translations/pt.json +154 -16
- package/translations/pt_BR.json +154 -16
- package/translations/qu.json +154 -16
- package/translations/ro_RO.json +154 -16
- package/translations/ru_RU.json +154 -16
- package/translations/si.json +154 -16
- package/translations/sq.json +154 -16
- package/translations/sw.json +154 -16
- package/translations/sw_KE.json +154 -16
- package/translations/tr.json +154 -16
- package/translations/tr_TR.json +154 -16
- package/translations/uk.json +154 -16
- package/translations/uz.json +154 -16
- package/translations/uz@Latn.json +154 -16
- package/translations/uz_UZ.json +154 -16
- package/translations/vi.json +154 -16
- package/translations/zh.json +154 -16
- package/translations/zh_CN.json +179 -41
- package/translations/zh_TW.json +154 -16
- package/tsconfig.json +3 -3
- package/vitest.config.js +28 -0
- package/.turbo/cache/4e30f71f570fc412-meta.json +0 -1
- package/.turbo/cache/4e30f71f570fc412.tar.zst +0 -0
- package/dist/1119.js +0 -1
- package/dist/1197.js +0 -1
- package/dist/1435.js +0 -1
- package/dist/1435.js.map +0 -1
- package/dist/1807.js +0 -1
- package/dist/1807.js.map +0 -1
- package/dist/2146.js +0 -1
- package/dist/2177.js +0 -2
- package/dist/2177.js.LICENSE.txt +0 -9
- package/dist/2177.js.map +0 -1
- package/dist/2690.js +0 -1
- package/dist/2704.js +0 -1
- package/dist/2704.js.map +0 -1
- package/dist/3002.js +0 -1
- package/dist/3002.js.map +0 -1
- package/dist/3041.js +0 -1
- package/dist/3041.js.map +0 -1
- package/dist/3099.js +0 -1
- package/dist/3184.js +0 -2
- package/dist/3184.js.LICENSE.txt +0 -14
- package/dist/3184.js.map +0 -1
- package/dist/3584.js +0 -1
- package/dist/4055.js +0 -1
- package/dist/4132.js +0 -1
- package/dist/4225.js +0 -1
- package/dist/4225.js.map +0 -1
- package/dist/4300.js +0 -1
- package/dist/4335.js +0 -1
- package/dist/439.js +0 -1
- package/dist/4618.js +0 -1
- package/dist/4652.js +0 -1
- package/dist/4944.js +0 -1
- package/dist/5173.js +0 -1
- package/dist/5241.js +0 -1
- package/dist/5422.js +0 -1
- package/dist/5422.js.map +0 -1
- package/dist/5442.js +0 -1
- package/dist/5661.js +0 -1
- package/dist/6022.js +0 -1
- package/dist/6404.js +0 -1
- package/dist/6404.js.map +0 -1
- package/dist/6468.js +0 -1
- package/dist/6540.js +0 -2
- package/dist/6540.js.LICENSE.txt +0 -9
- package/dist/6540.js.map +0 -1
- package/dist/6589.js +0 -1
- package/dist/6606.js +0 -1
- package/dist/6606.js.map +0 -1
- package/dist/6679.js +0 -1
- package/dist/6792.js +0 -1
- package/dist/6792.js.map +0 -1
- package/dist/6840.js +0 -1
- package/dist/6859.js +0 -1
- package/dist/7097.js +0 -1
- package/dist/7159.js +0 -1
- package/dist/723.js +0 -1
- package/dist/7255.js +0 -1
- package/dist/7255.js.map +0 -1
- package/dist/7617.js +0 -1
- package/dist/795.js +0 -1
- package/dist/8163.js +0 -1
- package/dist/8341.js +0 -2
- package/dist/8341.js.LICENSE.txt +0 -52
- package/dist/8341.js.map +0 -1
- package/dist/8349.js +0 -1
- package/dist/8371.js +0 -1
- package/dist/8421.js +0 -1
- package/dist/8421.js.map +0 -1
- package/dist/8618.js +0 -1
- package/dist/890.js +0 -1
- package/dist/9214.js +0 -1
- package/dist/9538.js +0 -1
- package/dist/9569.js +0 -1
- package/dist/961.js +0 -2
- package/dist/961.js.LICENSE.txt +0 -19
- package/dist/961.js.map +0 -1
- package/dist/986.js +0 -1
- package/dist/9879.js +0 -1
- package/dist/9895.js +0 -1
- package/dist/9900.js +0 -1
- package/dist/9913.js +0 -1
- package/dist/main.js.LICENSE.txt +0 -62
- package/src/billable-services/bill-waiver/bill-selection.component.tsx +0 -76
- package/src/billable-services/bill-waiver/bill-waiver-form.component.tsx +0 -107
- package/src/billable-services/bill-waiver/bill-waiver-form.scss +0 -34
- package/src/billable-services/bill-waiver/bill-waiver.component.tsx +0 -34
- package/src/billable-services/bill-waiver/bill-waiver.scss +0 -10
- package/src/billable-services/bill-waiver/patient-bills.component.tsx +0 -134
- package/webpack.config.js +0 -1
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
3
|
+
import { mockBill, mockPatient } from '@mocks/bills.mock';
|
|
4
|
+
import { waitForLoadingToFinish } from '@tools/test-helpers';
|
|
2
5
|
import userEvent from '@testing-library/user-event';
|
|
3
6
|
import { render, screen, waitFor } from '@testing-library/react';
|
|
4
7
|
import { useReactToPrint } from 'react-to-print';
|
|
@@ -10,44 +13,49 @@ import {
|
|
|
10
13
|
usePatient,
|
|
11
14
|
} from '@openmrs/esm-framework';
|
|
12
15
|
import { configSchema, type BillingConfig } from '../config-schema';
|
|
13
|
-
import {
|
|
14
|
-
|
|
16
|
+
import {
|
|
17
|
+
type BillDiscount,
|
|
18
|
+
BillDiscountStatus,
|
|
19
|
+
BillDiscountType,
|
|
20
|
+
BillStatus,
|
|
21
|
+
RefundStatus,
|
|
22
|
+
type MappedBill,
|
|
23
|
+
} from '../types';
|
|
15
24
|
import { useBill } from '../billing.resource';
|
|
16
25
|
import { usePaymentModes } from './payments/payment.resource';
|
|
17
|
-
import { waitForLoadingToFinish } from 'tools/test-helpers';
|
|
18
26
|
import Invoice from './invoice.component';
|
|
19
27
|
|
|
20
|
-
const mockUseConfig =
|
|
21
|
-
const mockUseBill =
|
|
22
|
-
const mockUsePatient =
|
|
23
|
-
const mockUsePaymentModes =
|
|
24
|
-
const mockUseReactToPrint =
|
|
25
|
-
const mockShowModal =
|
|
28
|
+
const mockUseConfig = vi.mocked(useConfig<BillingConfig>);
|
|
29
|
+
const mockUseBill = vi.mocked(useBill);
|
|
30
|
+
const mockUsePatient = vi.mocked(usePatient);
|
|
31
|
+
const mockUsePaymentModes = vi.mocked(usePaymentModes);
|
|
32
|
+
const mockUseReactToPrint = vi.mocked(useReactToPrint);
|
|
33
|
+
const mockShowModal = vi.mocked(showModal);
|
|
26
34
|
|
|
27
|
-
|
|
28
|
-
convertToCurrency:
|
|
35
|
+
vi.mock('../helpers/functions', () => ({
|
|
36
|
+
convertToCurrency: vi.fn((amount) => `USD ${amount}`),
|
|
29
37
|
}));
|
|
30
38
|
|
|
31
39
|
window.i18next = {
|
|
32
40
|
language: 'en',
|
|
33
41
|
} as any;
|
|
34
42
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
);
|
|
43
|
+
vi.mock('./printable-invoice/print-receipt.component', () => ({
|
|
44
|
+
default: vi.fn(() => <div data-testid="mock-print-receipt">Print Receipt Mock</div>),
|
|
45
|
+
}));
|
|
38
46
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
);
|
|
47
|
+
vi.mock('./printable-invoice/printable-invoice.component', () => ({
|
|
48
|
+
default: vi.fn(() => <div data-testid="mock-printable-invoice">Printable Invoice Mock</div>),
|
|
49
|
+
}));
|
|
42
50
|
|
|
43
|
-
|
|
44
|
-
usePaymentModes:
|
|
45
|
-
updateBillVisitAttribute:
|
|
51
|
+
vi.mock('./payments/payment.resource', () => ({
|
|
52
|
+
usePaymentModes: vi.fn(),
|
|
53
|
+
updateBillVisitAttribute: vi.fn(),
|
|
46
54
|
}));
|
|
47
55
|
|
|
48
|
-
|
|
49
|
-
useBill:
|
|
50
|
-
useDefaultFacility:
|
|
56
|
+
vi.mock('../billing.resource', () => ({
|
|
57
|
+
useBill: vi.fn(),
|
|
58
|
+
useDefaultFacility: vi.fn().mockReturnValue({
|
|
51
59
|
data: {
|
|
52
60
|
uuid: '54065383-b4d4-42d2-af4d-d250a1fd2590',
|
|
53
61
|
display: 'MTRH',
|
|
@@ -55,23 +63,32 @@ jest.mock('../billing.resource', () => ({
|
|
|
55
63
|
}),
|
|
56
64
|
}));
|
|
57
65
|
|
|
58
|
-
|
|
59
|
-
|
|
66
|
+
vi.mock('../discounts/discounts-table.component', () => ({
|
|
67
|
+
default: vi.fn(() => null),
|
|
68
|
+
}));
|
|
69
|
+
|
|
70
|
+
vi.mock('../refunds/refunds-table.component', () => ({
|
|
71
|
+
default: vi.fn(() => null),
|
|
72
|
+
}));
|
|
73
|
+
|
|
74
|
+
vi.mock('react-router-dom', () => ({
|
|
75
|
+
useParams: vi.fn().mockReturnValue({
|
|
60
76
|
patientUuid: 'patientUuid',
|
|
61
77
|
billUuid: 'billUuid',
|
|
62
78
|
}),
|
|
63
79
|
}));
|
|
64
80
|
|
|
65
|
-
|
|
66
|
-
useReactToPrint:
|
|
81
|
+
vi.mock('react-to-print', () => ({
|
|
82
|
+
useReactToPrint: vi.fn(),
|
|
67
83
|
}));
|
|
68
84
|
|
|
69
85
|
describe('Invoice', () => {
|
|
70
|
-
const defaultBillData = {
|
|
86
|
+
const defaultBillData: MappedBill = {
|
|
71
87
|
...mockBill,
|
|
72
88
|
uuid: 'test-uuid',
|
|
73
89
|
status: BillStatus.PENDING,
|
|
74
90
|
totalAmount: 1000,
|
|
91
|
+
netAmount: 1000,
|
|
75
92
|
tenderedAmount: 0,
|
|
76
93
|
receiptNumber: 'RCPT-001',
|
|
77
94
|
dateCreated: '2024-01-01',
|
|
@@ -81,7 +98,7 @@ describe('Invoice', () => {
|
|
|
81
98
|
item: 'Test Service',
|
|
82
99
|
quantity: 1,
|
|
83
100
|
price: 1000,
|
|
84
|
-
|
|
101
|
+
status: 'PENDING',
|
|
85
102
|
billableService: 'Test Service',
|
|
86
103
|
display: '',
|
|
87
104
|
voided: false,
|
|
@@ -100,7 +117,7 @@ describe('Invoice', () => {
|
|
|
100
117
|
isLoading: false,
|
|
101
118
|
error: null,
|
|
102
119
|
isValidating: false,
|
|
103
|
-
mutate:
|
|
120
|
+
mutate: vi.fn(),
|
|
104
121
|
});
|
|
105
122
|
|
|
106
123
|
mockUsePatient.mockReturnValue({
|
|
@@ -117,22 +134,38 @@ describe('Invoice', () => {
|
|
|
117
134
|
],
|
|
118
135
|
isLoading: false,
|
|
119
136
|
error: null,
|
|
120
|
-
mutate:
|
|
137
|
+
mutate: vi.fn(),
|
|
121
138
|
});
|
|
122
139
|
|
|
123
140
|
mockUseConfig.mockReturnValue({ ...getDefaultsFromConfigSchema(configSchema), defaultCurrency: 'USD' });
|
|
124
141
|
|
|
125
|
-
const printHandler =
|
|
142
|
+
const printHandler = vi.fn();
|
|
126
143
|
mockUseReactToPrint.mockReturnValue(printHandler);
|
|
127
144
|
});
|
|
128
145
|
|
|
146
|
+
const makeDiscount = (overrides: Partial<BillDiscount> = {}): BillDiscount => ({
|
|
147
|
+
uuid: 'discount-1',
|
|
148
|
+
billUuid: 'test-uuid',
|
|
149
|
+
lineItemUuid: null,
|
|
150
|
+
discountType: BillDiscountType.PERCENTAGE,
|
|
151
|
+
discountValue: 10,
|
|
152
|
+
discountAmount: 100,
|
|
153
|
+
justification: 'goodwill',
|
|
154
|
+
initiator: { uuid: 'u1', display: 'cashier' },
|
|
155
|
+
approver: null,
|
|
156
|
+
dateCreated: '2024-01-01',
|
|
157
|
+
status: BillDiscountStatus.APPROVED,
|
|
158
|
+
voided: false,
|
|
159
|
+
...overrides,
|
|
160
|
+
});
|
|
161
|
+
|
|
129
162
|
it('should render loading state when bill is loading', () => {
|
|
130
163
|
mockUseBill.mockReturnValue({
|
|
131
164
|
bill: null,
|
|
132
165
|
isLoading: true,
|
|
133
166
|
error: null,
|
|
134
167
|
isValidating: false,
|
|
135
|
-
mutate:
|
|
168
|
+
mutate: vi.fn(),
|
|
136
169
|
});
|
|
137
170
|
|
|
138
171
|
render(<Invoice />);
|
|
@@ -157,7 +190,7 @@ describe('Invoice', () => {
|
|
|
157
190
|
isLoading: false,
|
|
158
191
|
error: new Error('Failed to load bill'),
|
|
159
192
|
isValidating: false,
|
|
160
|
-
mutate:
|
|
193
|
+
mutate: vi.fn(),
|
|
161
194
|
});
|
|
162
195
|
|
|
163
196
|
render(<Invoice />);
|
|
@@ -171,24 +204,12 @@ describe('Invoice', () => {
|
|
|
171
204
|
expect(screen.getAllByText(/total amount/i).length).toBeGreaterThan(0);
|
|
172
205
|
expect(screen.getAllByText(/amount tendered/i).length).toBeGreaterThan(0);
|
|
173
206
|
expect(screen.getByRole('heading', { name: /invoice number/i })).toBeInTheDocument();
|
|
174
|
-
expect(screen.getByText(/date
|
|
207
|
+
expect(screen.getByText(/date bill created/i)).toBeInTheDocument();
|
|
175
208
|
expect(screen.getByText(/invoice status/i)).toBeInTheDocument();
|
|
176
209
|
expect(screen.getAllByText('RCPT-001').length).toBeGreaterThan(0);
|
|
177
210
|
expect(screen.getAllByText('PENDING').length).toBeGreaterThan(0);
|
|
178
|
-
});
|
|
179
|
-
|
|
180
|
-
it('should render invoice table with line items', async () => {
|
|
181
|
-
render(<Invoice />);
|
|
182
|
-
await waitForLoadingToFinish();
|
|
183
|
-
|
|
184
211
|
expect(screen.getByText(/line items/i)).toBeInTheDocument();
|
|
185
212
|
expect(screen.getByText('Test Service')).toBeInTheDocument();
|
|
186
|
-
});
|
|
187
|
-
|
|
188
|
-
it('should render payments section', async () => {
|
|
189
|
-
render(<Invoice />);
|
|
190
|
-
await waitForLoadingToFinish();
|
|
191
|
-
|
|
192
213
|
expect(screen.getByText(/payments/i)).toBeInTheDocument();
|
|
193
214
|
});
|
|
194
215
|
|
|
@@ -202,7 +223,7 @@ describe('Invoice', () => {
|
|
|
202
223
|
isLoading: false,
|
|
203
224
|
error: null,
|
|
204
225
|
isValidating: false,
|
|
205
|
-
mutate:
|
|
226
|
+
mutate: vi.fn(),
|
|
206
227
|
});
|
|
207
228
|
|
|
208
229
|
render(<Invoice />);
|
|
@@ -221,7 +242,7 @@ describe('Invoice', () => {
|
|
|
221
242
|
isLoading: false,
|
|
222
243
|
error: null,
|
|
223
244
|
isValidating: false,
|
|
224
|
-
mutate:
|
|
245
|
+
mutate: vi.fn(),
|
|
225
246
|
});
|
|
226
247
|
|
|
227
248
|
render(<Invoice />);
|
|
@@ -238,7 +259,7 @@ describe('Invoice', () => {
|
|
|
238
259
|
});
|
|
239
260
|
|
|
240
261
|
it('should handle print button click', async () => {
|
|
241
|
-
const handlePrintMock =
|
|
262
|
+
const handlePrintMock = vi.fn();
|
|
242
263
|
const user = userEvent.setup();
|
|
243
264
|
mockUseReactToPrint.mockReturnValue(handlePrintMock);
|
|
244
265
|
|
|
@@ -269,7 +290,7 @@ describe('Invoice', () => {
|
|
|
269
290
|
});
|
|
270
291
|
|
|
271
292
|
it('should search and filter line items in the table', async () => {
|
|
272
|
-
const billWithMultipleItems = {
|
|
293
|
+
const billWithMultipleItems: MappedBill = {
|
|
273
294
|
...defaultBillData,
|
|
274
295
|
lineItems: [
|
|
275
296
|
{
|
|
@@ -277,7 +298,7 @@ describe('Invoice', () => {
|
|
|
277
298
|
item: 'Lab Test',
|
|
278
299
|
quantity: 1,
|
|
279
300
|
price: 500,
|
|
280
|
-
|
|
301
|
+
status: 'PENDING',
|
|
281
302
|
billableService: 'Lab Test',
|
|
282
303
|
display: '',
|
|
283
304
|
voided: false,
|
|
@@ -292,7 +313,7 @@ describe('Invoice', () => {
|
|
|
292
313
|
item: 'X-Ray',
|
|
293
314
|
quantity: 1,
|
|
294
315
|
price: 500,
|
|
295
|
-
|
|
316
|
+
status: 'PENDING',
|
|
296
317
|
billableService: 'X-Ray',
|
|
297
318
|
display: '',
|
|
298
319
|
voided: false,
|
|
@@ -310,7 +331,7 @@ describe('Invoice', () => {
|
|
|
310
331
|
isLoading: false,
|
|
311
332
|
error: null,
|
|
312
333
|
isValidating: false,
|
|
313
|
-
mutate:
|
|
334
|
+
mutate: vi.fn(),
|
|
314
335
|
});
|
|
315
336
|
|
|
316
337
|
const user = userEvent.setup();
|
|
@@ -330,7 +351,7 @@ describe('Invoice', () => {
|
|
|
330
351
|
});
|
|
331
352
|
|
|
332
353
|
it('should handle bill data updates via mutate', async () => {
|
|
333
|
-
const mockMutate =
|
|
354
|
+
const mockMutate = vi.fn();
|
|
334
355
|
mockUseBill.mockReturnValue({
|
|
335
356
|
bill: defaultBillData,
|
|
336
357
|
isLoading: false,
|
|
@@ -378,7 +399,7 @@ describe('Invoice', () => {
|
|
|
378
399
|
isLoading: true,
|
|
379
400
|
error: null,
|
|
380
401
|
isValidating: false,
|
|
381
|
-
mutate:
|
|
402
|
+
mutate: vi.fn(),
|
|
382
403
|
});
|
|
383
404
|
|
|
384
405
|
render(<Invoice />);
|
|
@@ -392,7 +413,7 @@ describe('Invoice', () => {
|
|
|
392
413
|
isLoading: false,
|
|
393
414
|
error: null,
|
|
394
415
|
isValidating: false,
|
|
395
|
-
mutate:
|
|
416
|
+
mutate: vi.fn(),
|
|
396
417
|
});
|
|
397
418
|
|
|
398
419
|
mockUsePatient.mockReturnValue({
|
|
@@ -444,7 +465,7 @@ describe('Invoice', () => {
|
|
|
444
465
|
});
|
|
445
466
|
|
|
446
467
|
it('should pass mutate function to Payments component', async () => {
|
|
447
|
-
const mockMutate =
|
|
468
|
+
const mockMutate = vi.fn();
|
|
448
469
|
mockUseBill.mockReturnValue({
|
|
449
470
|
bill: defaultBillData,
|
|
450
471
|
isLoading: false,
|
|
@@ -471,7 +492,7 @@ describe('Invoice', () => {
|
|
|
471
492
|
isLoading: false,
|
|
472
493
|
error: null,
|
|
473
494
|
isValidating: false,
|
|
474
|
-
mutate:
|
|
495
|
+
mutate: vi.fn(),
|
|
475
496
|
});
|
|
476
497
|
|
|
477
498
|
render(<Invoice />);
|
|
@@ -499,7 +520,7 @@ describe('Invoice', () => {
|
|
|
499
520
|
isLoading: false,
|
|
500
521
|
error: null,
|
|
501
522
|
isValidating: false,
|
|
502
|
-
mutate:
|
|
523
|
+
mutate: vi.fn(),
|
|
503
524
|
});
|
|
504
525
|
|
|
505
526
|
render(<Invoice />);
|
|
@@ -525,7 +546,7 @@ describe('Invoice', () => {
|
|
|
525
546
|
isLoading: false,
|
|
526
547
|
error: null,
|
|
527
548
|
isValidating: false,
|
|
528
|
-
mutate:
|
|
549
|
+
mutate: vi.fn(),
|
|
529
550
|
});
|
|
530
551
|
|
|
531
552
|
render(<Invoice />);
|
|
@@ -547,7 +568,7 @@ describe('Invoice', () => {
|
|
|
547
568
|
isLoading: false,
|
|
548
569
|
error: null,
|
|
549
570
|
isValidating: false,
|
|
550
|
-
mutate:
|
|
571
|
+
mutate: vi.fn(),
|
|
551
572
|
});
|
|
552
573
|
|
|
553
574
|
render(<Invoice />);
|
|
@@ -562,7 +583,7 @@ describe('Invoice', () => {
|
|
|
562
583
|
isLoading: false,
|
|
563
584
|
error: null,
|
|
564
585
|
isValidating: false,
|
|
565
|
-
mutate:
|
|
586
|
+
mutate: vi.fn(),
|
|
566
587
|
});
|
|
567
588
|
|
|
568
589
|
render(<Invoice />);
|
|
@@ -572,7 +593,7 @@ describe('Invoice', () => {
|
|
|
572
593
|
});
|
|
573
594
|
|
|
574
595
|
it('should open finalize confirmation modal when "Finalize bill" button is clicked', async () => {
|
|
575
|
-
const mockMutate =
|
|
596
|
+
const mockMutate = vi.fn();
|
|
576
597
|
const user = userEvent.setup();
|
|
577
598
|
|
|
578
599
|
mockUseBill.mockReturnValue({
|
|
@@ -595,9 +616,288 @@ describe('Invoice', () => {
|
|
|
595
616
|
});
|
|
596
617
|
});
|
|
597
618
|
|
|
619
|
+
describe('discount entry points', () => {
|
|
620
|
+
it('renders the "Request discount" button when bill is eligible', async () => {
|
|
621
|
+
render(<Invoice />);
|
|
622
|
+
expect(await screen.findByRole('button', { name: /request discount/i })).toBeInTheDocument();
|
|
623
|
+
});
|
|
624
|
+
|
|
625
|
+
it('opens the request-discount modal on click', async () => {
|
|
626
|
+
const user = userEvent.setup();
|
|
627
|
+
render(<Invoice />);
|
|
628
|
+
await user.click(await screen.findByRole('button', { name: /request discount/i }));
|
|
629
|
+
expect(mockShowModal).toHaveBeenCalledWith('request-discount-modal', expect.any(Object));
|
|
630
|
+
});
|
|
631
|
+
|
|
632
|
+
it('shows Total amount / Discount / Net amount trio when an approved discount exists', async () => {
|
|
633
|
+
mockUseBill.mockReturnValue({
|
|
634
|
+
bill: {
|
|
635
|
+
...defaultBillData,
|
|
636
|
+
totalAmount: 5000,
|
|
637
|
+
netAmount: 4500,
|
|
638
|
+
tenderedAmount: 500,
|
|
639
|
+
discounts: [makeDiscount({ status: BillDiscountStatus.APPROVED, discountAmount: 500 })],
|
|
640
|
+
},
|
|
641
|
+
isLoading: false,
|
|
642
|
+
error: null,
|
|
643
|
+
isValidating: false,
|
|
644
|
+
mutate: vi.fn(),
|
|
645
|
+
});
|
|
646
|
+
render(<Invoice />);
|
|
647
|
+
expect(await screen.findByRole('heading', { name: /^net amount$/i })).toBeInTheDocument();
|
|
648
|
+
expect(screen.getByRole('heading', { name: /^total amount$/i })).toBeInTheDocument();
|
|
649
|
+
expect(screen.getByRole('heading', { name: /^discount$/i })).toBeInTheDocument();
|
|
650
|
+
expect(screen.getAllByText(/^- USD 500$/).length).toBeGreaterThan(0);
|
|
651
|
+
// Amount due must be netAmount - tendered (4000), not totalAmount - tendered (4500).
|
|
652
|
+
expect(screen.getAllByText('USD 4000').length).toBeGreaterThan(0);
|
|
653
|
+
});
|
|
654
|
+
|
|
655
|
+
it('hides the Discount / Net amount trio when no approved discount exists, even if totalAmount differs from netAmount', async () => {
|
|
656
|
+
mockUseBill.mockReturnValue({
|
|
657
|
+
bill: {
|
|
658
|
+
...defaultBillData,
|
|
659
|
+
totalAmount: 5000,
|
|
660
|
+
netAmount: 4500,
|
|
661
|
+
discounts: [makeDiscount({ status: BillDiscountStatus.PENDING })],
|
|
662
|
+
},
|
|
663
|
+
isLoading: false,
|
|
664
|
+
error: null,
|
|
665
|
+
isValidating: false,
|
|
666
|
+
mutate: vi.fn(),
|
|
667
|
+
});
|
|
668
|
+
render(<Invoice />);
|
|
669
|
+
await screen.findByRole('heading', { name: /^total amount$/i });
|
|
670
|
+
expect(screen.queryByRole('heading', { name: /^net amount$/i })).not.toBeInTheDocument();
|
|
671
|
+
expect(screen.queryByRole('heading', { name: /^discount$/i })).not.toBeInTheDocument();
|
|
672
|
+
});
|
|
673
|
+
|
|
674
|
+
it('passes an onMutate to the modal that revalidates the bill SWR cache', async () => {
|
|
675
|
+
const mockMutate = vi.fn();
|
|
676
|
+
mockUseBill.mockReturnValue({
|
|
677
|
+
bill: defaultBillData,
|
|
678
|
+
isLoading: false,
|
|
679
|
+
error: null,
|
|
680
|
+
isValidating: false,
|
|
681
|
+
mutate: mockMutate,
|
|
682
|
+
});
|
|
683
|
+
const user = userEvent.setup();
|
|
684
|
+
render(<Invoice />);
|
|
685
|
+
await user.click(await screen.findByRole('button', { name: /request discount/i }));
|
|
686
|
+
const [, props] = mockShowModal.mock.calls.at(-1)!;
|
|
687
|
+
(props as { onMutate: () => void }).onMutate();
|
|
688
|
+
expect(mockMutate).toHaveBeenCalled();
|
|
689
|
+
});
|
|
690
|
+
});
|
|
691
|
+
|
|
692
|
+
describe('discount eligibility — bill status', () => {
|
|
693
|
+
it.each([
|
|
694
|
+
[BillStatus.PAID, 1000],
|
|
695
|
+
[BillStatus.ADJUSTED, 0],
|
|
696
|
+
])('hides the bill-level "Request discount" button for %s bills', (status, tendered) => {
|
|
697
|
+
mockUseBill.mockReturnValue({
|
|
698
|
+
bill: { ...defaultBillData, status, tenderedAmount: tendered },
|
|
699
|
+
isLoading: false,
|
|
700
|
+
error: null,
|
|
701
|
+
isValidating: false,
|
|
702
|
+
mutate: vi.fn(),
|
|
703
|
+
});
|
|
704
|
+
render(<Invoice />);
|
|
705
|
+
expect(screen.queryByRole('button', { name: /request discount/i })).not.toBeInTheDocument();
|
|
706
|
+
});
|
|
707
|
+
|
|
708
|
+
it.each([
|
|
709
|
+
[BillStatus.PAID, 1000],
|
|
710
|
+
[BillStatus.ADJUSTED, 0],
|
|
711
|
+
])('hides the per-line-item "Request discount" action for %s bills', async (status, tendered) => {
|
|
712
|
+
mockUseBill.mockReturnValue({
|
|
713
|
+
bill: { ...defaultBillData, status, tenderedAmount: tendered },
|
|
714
|
+
isLoading: false,
|
|
715
|
+
error: null,
|
|
716
|
+
isValidating: false,
|
|
717
|
+
mutate: vi.fn(),
|
|
718
|
+
});
|
|
719
|
+
const user = userEvent.setup();
|
|
720
|
+
render(<Invoice />);
|
|
721
|
+
await user.click(screen.getByTestId('action-menu-item-1'));
|
|
722
|
+
expect(screen.queryByTestId('request-discount-button-item-1')).not.toBeInTheDocument();
|
|
723
|
+
});
|
|
724
|
+
});
|
|
725
|
+
|
|
726
|
+
describe('discount eligibility — line already discounted', () => {
|
|
727
|
+
it.each([BillDiscountStatus.PENDING, BillDiscountStatus.APPROVED, BillDiscountStatus.REJECTED])(
|
|
728
|
+
'hides the per-line-item "Request discount" action when an existing %s discount targets the line',
|
|
729
|
+
async (discountStatus) => {
|
|
730
|
+
mockUseBill.mockReturnValue({
|
|
731
|
+
bill: { ...defaultBillData, discounts: [makeDiscount({ lineItemUuid: 'item-1', status: discountStatus })] },
|
|
732
|
+
isLoading: false,
|
|
733
|
+
error: null,
|
|
734
|
+
isValidating: false,
|
|
735
|
+
mutate: vi.fn(),
|
|
736
|
+
});
|
|
737
|
+
const user = userEvent.setup();
|
|
738
|
+
render(<Invoice />);
|
|
739
|
+
await user.click(screen.getByTestId('action-menu-item-1'));
|
|
740
|
+
expect(screen.queryByTestId('request-discount-button-item-1')).not.toBeInTheDocument();
|
|
741
|
+
},
|
|
742
|
+
);
|
|
743
|
+
});
|
|
744
|
+
|
|
745
|
+
describe('discount eligibility — cross-blocking between bill-level and line-level', () => {
|
|
746
|
+
it.each([BillDiscountStatus.PENDING, BillDiscountStatus.APPROVED, BillDiscountStatus.REJECTED])(
|
|
747
|
+
'disables the bill-level "Request discount" button when a %s line-item discount exists',
|
|
748
|
+
(discountStatus) => {
|
|
749
|
+
mockUseBill.mockReturnValue({
|
|
750
|
+
bill: { ...defaultBillData, discounts: [makeDiscount({ lineItemUuid: 'item-1', status: discountStatus })] },
|
|
751
|
+
isLoading: false,
|
|
752
|
+
error: null,
|
|
753
|
+
isValidating: false,
|
|
754
|
+
mutate: vi.fn(),
|
|
755
|
+
});
|
|
756
|
+
render(<Invoice />);
|
|
757
|
+
expect(screen.getByRole('button', { name: /request discount/i })).toBeDisabled();
|
|
758
|
+
},
|
|
759
|
+
);
|
|
760
|
+
|
|
761
|
+
it.each([BillDiscountStatus.PENDING, BillDiscountStatus.APPROVED, BillDiscountStatus.REJECTED])(
|
|
762
|
+
'hides the per-line-item "Request discount" action when a %s bill-level discount exists',
|
|
763
|
+
async (discountStatus) => {
|
|
764
|
+
mockUseBill.mockReturnValue({
|
|
765
|
+
bill: { ...defaultBillData, discounts: [makeDiscount({ lineItemUuid: null, status: discountStatus })] },
|
|
766
|
+
isLoading: false,
|
|
767
|
+
error: null,
|
|
768
|
+
isValidating: false,
|
|
769
|
+
mutate: vi.fn(),
|
|
770
|
+
});
|
|
771
|
+
const user = userEvent.setup();
|
|
772
|
+
render(<Invoice />);
|
|
773
|
+
await user.click(screen.getByTestId('action-menu-item-1'));
|
|
774
|
+
expect(screen.queryByTestId('request-discount-button-item-1')).not.toBeInTheDocument();
|
|
775
|
+
},
|
|
776
|
+
);
|
|
777
|
+
});
|
|
778
|
+
|
|
779
|
+
describe('refund entry points', () => {
|
|
780
|
+
const paidBill: MappedBill = {
|
|
781
|
+
...defaultBillData,
|
|
782
|
+
status: BillStatus.PAID,
|
|
783
|
+
totalAmount: 1000,
|
|
784
|
+
netAmount: 1000,
|
|
785
|
+
tenderedAmount: 1000,
|
|
786
|
+
};
|
|
787
|
+
|
|
788
|
+
beforeEach(() => {
|
|
789
|
+
mockUseBill.mockReturnValue({
|
|
790
|
+
bill: paidBill,
|
|
791
|
+
isLoading: false,
|
|
792
|
+
error: null,
|
|
793
|
+
isValidating: false,
|
|
794
|
+
mutate: vi.fn(),
|
|
795
|
+
});
|
|
796
|
+
});
|
|
797
|
+
|
|
798
|
+
it('shows "Request refund" button for PAID bills with no active refund', async () => {
|
|
799
|
+
render(<Invoice />);
|
|
800
|
+
expect(await screen.findByRole('button', { name: /request refund/i })).toBeInTheDocument();
|
|
801
|
+
});
|
|
802
|
+
|
|
803
|
+
it('hides "Request refund" button when bill is in REFUND_REQUESTED status', async () => {
|
|
804
|
+
mockUseBill.mockReturnValue({
|
|
805
|
+
bill: { ...paidBill, status: BillStatus.REFUND_REQUESTED },
|
|
806
|
+
isLoading: false,
|
|
807
|
+
error: null,
|
|
808
|
+
isValidating: false,
|
|
809
|
+
mutate: vi.fn(),
|
|
810
|
+
});
|
|
811
|
+
render(<Invoice />);
|
|
812
|
+
await waitForLoadingToFinish();
|
|
813
|
+
expect(screen.queryByRole('button', { name: /request refund/i })).not.toBeInTheDocument();
|
|
814
|
+
});
|
|
815
|
+
|
|
816
|
+
const billWithActiveLineRefund: MappedBill = {
|
|
817
|
+
...paidBill,
|
|
818
|
+
refunds: [
|
|
819
|
+
{
|
|
820
|
+
uuid: 'r-active',
|
|
821
|
+
billUuid: 'test-uuid',
|
|
822
|
+
lineItemUuid: 'item-1',
|
|
823
|
+
refundAmount: 200,
|
|
824
|
+
reason: 'duplicate',
|
|
825
|
+
initiator: { uuid: 'u1', display: 'cashier' },
|
|
826
|
+
approver: null,
|
|
827
|
+
completer: null,
|
|
828
|
+
dateApproved: null,
|
|
829
|
+
dateCompleted: null,
|
|
830
|
+
dateCreated: '2026-06-01T00:00:00.000+0000',
|
|
831
|
+
status: RefundStatus.REQUESTED,
|
|
832
|
+
voided: false,
|
|
833
|
+
},
|
|
834
|
+
],
|
|
835
|
+
};
|
|
836
|
+
|
|
837
|
+
it('disables "Request refund" button when an active line-item refund is in progress', async () => {
|
|
838
|
+
mockUseBill.mockReturnValue({
|
|
839
|
+
bill: billWithActiveLineRefund,
|
|
840
|
+
isLoading: false,
|
|
841
|
+
error: null,
|
|
842
|
+
isValidating: false,
|
|
843
|
+
mutate: vi.fn(),
|
|
844
|
+
});
|
|
845
|
+
render(<Invoice />);
|
|
846
|
+
await waitForLoadingToFinish();
|
|
847
|
+
expect(
|
|
848
|
+
screen.getByRole('button', { name: /a refund is already in progress for one or more line items/i }),
|
|
849
|
+
).toBeDisabled();
|
|
850
|
+
});
|
|
851
|
+
|
|
852
|
+
it('shows a tooltip explaining why "Request refund" is disabled when a line-item refund is in progress', async () => {
|
|
853
|
+
mockUseBill.mockReturnValue({
|
|
854
|
+
bill: billWithActiveLineRefund,
|
|
855
|
+
isLoading: false,
|
|
856
|
+
error: null,
|
|
857
|
+
isValidating: false,
|
|
858
|
+
mutate: vi.fn(),
|
|
859
|
+
});
|
|
860
|
+
render(<Invoice />);
|
|
861
|
+
await waitForLoadingToFinish();
|
|
862
|
+
expect(screen.getByText(/a refund is already in progress for one or more line items/i)).toBeInTheDocument();
|
|
863
|
+
});
|
|
864
|
+
|
|
865
|
+
it('passes remainingRefundable that deducts COMPLETED refunds when opening the request-refund modal', async () => {
|
|
866
|
+
const completedRefund = {
|
|
867
|
+
uuid: 'r-done',
|
|
868
|
+
billUuid: 'test-uuid',
|
|
869
|
+
lineItemUuid: null,
|
|
870
|
+
refundAmount: 300,
|
|
871
|
+
reason: 'overcharged',
|
|
872
|
+
initiator: { uuid: 'u1', display: 'cashier' },
|
|
873
|
+
approver: { uuid: 'u2', display: 'admin' },
|
|
874
|
+
completer: { uuid: 'u3', display: 'cashier2' },
|
|
875
|
+
dateApproved: '2026-05-21T00:00:00.000+0000',
|
|
876
|
+
dateCompleted: '2026-05-21T00:00:00.000+0000',
|
|
877
|
+
dateCreated: '2026-05-21T00:00:00.000+0000',
|
|
878
|
+
status: RefundStatus.COMPLETED,
|
|
879
|
+
voided: false,
|
|
880
|
+
};
|
|
881
|
+
mockUseBill.mockReturnValue({
|
|
882
|
+
bill: { ...paidBill, refunds: [completedRefund] },
|
|
883
|
+
isLoading: false,
|
|
884
|
+
error: null,
|
|
885
|
+
isValidating: false,
|
|
886
|
+
mutate: vi.fn(),
|
|
887
|
+
});
|
|
888
|
+
const user = userEvent.setup();
|
|
889
|
+
render(<Invoice />);
|
|
890
|
+
await user.click(await screen.findByRole('button', { name: /request refund/i }));
|
|
891
|
+
expect(mockShowModal).toHaveBeenCalledWith(
|
|
892
|
+
'request-refund-modal',
|
|
893
|
+
expect.objectContaining({ remainingRefundable: 700 }),
|
|
894
|
+
);
|
|
895
|
+
});
|
|
896
|
+
});
|
|
897
|
+
|
|
598
898
|
it('should launch workspace with billUuid when "Add items to bill" is clicked', async () => {
|
|
599
|
-
const mockMutate =
|
|
600
|
-
const mockLaunchWorkspace2 =
|
|
899
|
+
const mockMutate = vi.fn();
|
|
900
|
+
const mockLaunchWorkspace2 = vi.mocked(launchWorkspace2);
|
|
601
901
|
|
|
602
902
|
mockUseBill.mockReturnValue({
|
|
603
903
|
bill: defaultBillData,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import React, { useCallback } from 'react';
|
|
2
|
+
import { useTranslation } from 'react-i18next';
|
|
2
3
|
import { Layer, OverflowMenu, OverflowMenuItem } from '@carbon/react';
|
|
3
4
|
import { getCoreTranslation, isDesktop, showModal, useLayoutType } from '@openmrs/esm-framework';
|
|
4
5
|
import { BillStatus, type LineItem, type MappedBill } from '../types';
|
|
@@ -8,9 +9,22 @@ type LineItemActionMenuProps = {
|
|
|
8
9
|
bill: MappedBill;
|
|
9
10
|
item: LineItem;
|
|
10
11
|
onMutate?: () => void;
|
|
12
|
+
showDiscountRequest?: boolean;
|
|
13
|
+
onDiscountRequest?: () => void;
|
|
14
|
+
showRefundRequest?: boolean;
|
|
15
|
+
onRefundRequest?: () => void;
|
|
11
16
|
};
|
|
12
17
|
|
|
13
|
-
const LineItemActionMenu: React.FC<LineItemActionMenuProps> = ({
|
|
18
|
+
const LineItemActionMenu: React.FC<LineItemActionMenuProps> = ({
|
|
19
|
+
bill,
|
|
20
|
+
item,
|
|
21
|
+
onMutate,
|
|
22
|
+
showDiscountRequest,
|
|
23
|
+
onDiscountRequest,
|
|
24
|
+
showRefundRequest,
|
|
25
|
+
onRefundRequest,
|
|
26
|
+
}) => {
|
|
27
|
+
const { t } = useTranslation();
|
|
14
28
|
const layout = useLayoutType();
|
|
15
29
|
|
|
16
30
|
const handleEditLineItem = useCallback(() => {
|
|
@@ -54,6 +68,22 @@ const LineItemActionMenu: React.FC<LineItemActionMenuProps> = ({ bill, item, onM
|
|
|
54
68
|
itemText={getCoreTranslation('delete')}
|
|
55
69
|
onClick={handleDeleteLineItem}
|
|
56
70
|
/>
|
|
71
|
+
{showDiscountRequest && (
|
|
72
|
+
<OverflowMenuItem
|
|
73
|
+
className={styles.menuitem}
|
|
74
|
+
data-testid={`request-discount-button-${item.uuid}`}
|
|
75
|
+
itemText={t('requestDiscount', 'Request discount')}
|
|
76
|
+
onClick={onDiscountRequest}
|
|
77
|
+
/>
|
|
78
|
+
)}
|
|
79
|
+
{showRefundRequest && (
|
|
80
|
+
<OverflowMenuItem
|
|
81
|
+
className={styles.menuitem}
|
|
82
|
+
data-testid={`request-refund-button-${item.uuid}`}
|
|
83
|
+
itemText={t('requestRefund', 'Request refund')}
|
|
84
|
+
onClick={onRefundRequest}
|
|
85
|
+
/>
|
|
86
|
+
)}
|
|
57
87
|
</OverflowMenu>
|
|
58
88
|
</Layer>
|
|
59
89
|
);
|