@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,20 +1,25 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
3
|
import userEvent from '@testing-library/user-event';
|
|
3
4
|
import { render, screen, waitFor } from '@testing-library/react';
|
|
4
5
|
import { getDefaultsFromConfigSchema, showModal, useConfig } from '@openmrs/esm-framework';
|
|
5
|
-
import { type MappedBill } from '../types';
|
|
6
|
+
import { BillStatus, RefundStatus, type BillRefund, type MappedBill } from '../types';
|
|
6
7
|
import { configSchema, type BillingConfig } from '../config-schema';
|
|
7
8
|
import InvoiceTable from './invoice-table.component';
|
|
8
9
|
|
|
9
|
-
const mockUseConfig =
|
|
10
|
-
const mockShowModal =
|
|
10
|
+
const mockUseConfig = vi.mocked(useConfig<BillingConfig>);
|
|
11
|
+
const mockShowModal = vi.mocked(showModal);
|
|
11
12
|
|
|
12
|
-
|
|
13
|
-
convertToCurrency:
|
|
13
|
+
vi.mock('../helpers', () => ({
|
|
14
|
+
convertToCurrency: vi.fn((price) => `USD ${price}`),
|
|
15
|
+
}));
|
|
16
|
+
|
|
17
|
+
vi.mock('../discounts/discounts.resource', () => ({
|
|
18
|
+
useBillDiscounts: () => ({ discounts: [], isLoading: false, error: null, mutate: vi.fn() }),
|
|
14
19
|
}));
|
|
15
20
|
|
|
16
21
|
describe('InvoiceTable', () => {
|
|
17
|
-
const mockOnMutate =
|
|
22
|
+
const mockOnMutate = vi.fn();
|
|
18
23
|
|
|
19
24
|
const defaultBill: MappedBill = {
|
|
20
25
|
uuid: 'bill-uuid',
|
|
@@ -25,7 +30,7 @@ describe('InvoiceTable', () => {
|
|
|
25
30
|
{
|
|
26
31
|
uuid: '1',
|
|
27
32
|
item: 'Item 1',
|
|
28
|
-
|
|
33
|
+
status: 'PAID',
|
|
29
34
|
quantity: 1,
|
|
30
35
|
price: 100,
|
|
31
36
|
display: '',
|
|
@@ -40,7 +45,7 @@ describe('InvoiceTable', () => {
|
|
|
40
45
|
{
|
|
41
46
|
uuid: '2',
|
|
42
47
|
item: 'Item 2',
|
|
43
|
-
|
|
48
|
+
status: 'PENDING',
|
|
44
49
|
quantity: 2,
|
|
45
50
|
price: 200,
|
|
46
51
|
display: '',
|
|
@@ -68,6 +73,7 @@ describe('InvoiceTable', () => {
|
|
|
68
73
|
billingService: 'billing-service-uuid',
|
|
69
74
|
payments: [],
|
|
70
75
|
totalAmount: 300,
|
|
76
|
+
netAmount: 300,
|
|
71
77
|
tenderedAmount: 300,
|
|
72
78
|
};
|
|
73
79
|
|
|
@@ -127,7 +133,7 @@ describe('InvoiceTable', () => {
|
|
|
127
133
|
{
|
|
128
134
|
uuid: '1',
|
|
129
135
|
item: 'Service A',
|
|
130
|
-
|
|
136
|
+
status: 'PENDING',
|
|
131
137
|
quantity: 3,
|
|
132
138
|
price: 100,
|
|
133
139
|
display: '',
|
|
@@ -293,7 +299,7 @@ describe('InvoiceTable', () => {
|
|
|
293
299
|
{
|
|
294
300
|
uuid: '1',
|
|
295
301
|
item: 'Free Service',
|
|
296
|
-
|
|
302
|
+
status: 'PAID',
|
|
297
303
|
quantity: 1,
|
|
298
304
|
price: 0,
|
|
299
305
|
display: '',
|
|
@@ -321,7 +327,7 @@ describe('InvoiceTable', () => {
|
|
|
321
327
|
{
|
|
322
328
|
uuid: '1',
|
|
323
329
|
item: 'Service',
|
|
324
|
-
|
|
330
|
+
status: 'PENDING',
|
|
325
331
|
quantity: 0,
|
|
326
332
|
price: 100,
|
|
327
333
|
display: '',
|
|
@@ -350,7 +356,7 @@ describe('InvoiceTable', () => {
|
|
|
350
356
|
uuid: '1',
|
|
351
357
|
item: 'Item Name',
|
|
352
358
|
billableService: 'Billable Service Name',
|
|
353
|
-
|
|
359
|
+
status: 'PAID',
|
|
354
360
|
quantity: 1,
|
|
355
361
|
price: 100,
|
|
356
362
|
display: '',
|
|
@@ -365,7 +371,7 @@ describe('InvoiceTable', () => {
|
|
|
365
371
|
uuid: '2',
|
|
366
372
|
item: 'Item Without Billable',
|
|
367
373
|
billableService: '',
|
|
368
|
-
|
|
374
|
+
status: 'PENDING',
|
|
369
375
|
quantity: 1,
|
|
370
376
|
price: 200,
|
|
371
377
|
display: '',
|
|
@@ -409,4 +415,150 @@ describe('InvoiceTable', () => {
|
|
|
409
415
|
expect(searchInput).toBeInTheDocument();
|
|
410
416
|
expect(searchInput).toBeVisible();
|
|
411
417
|
});
|
|
418
|
+
|
|
419
|
+
describe('viewOnly mode', () => {
|
|
420
|
+
it('hides the search box', () => {
|
|
421
|
+
render(<InvoiceTable bill={defaultBill} viewOnly />);
|
|
422
|
+
expect(screen.queryByPlaceholderText(/search this table/i)).not.toBeInTheDocument();
|
|
423
|
+
});
|
|
424
|
+
|
|
425
|
+
it('hides the actions column header', () => {
|
|
426
|
+
render(<InvoiceTable bill={defaultBill} viewOnly />);
|
|
427
|
+
// 6 data column headers; no 7th actions header
|
|
428
|
+
expect(screen.getAllByRole('columnheader')).toHaveLength(6);
|
|
429
|
+
});
|
|
430
|
+
|
|
431
|
+
it('hides per-row action menus', () => {
|
|
432
|
+
render(<InvoiceTable bill={defaultBill} viewOnly />);
|
|
433
|
+
expect(screen.queryAllByRole('button', { name: /options/i })).toHaveLength(0);
|
|
434
|
+
});
|
|
435
|
+
|
|
436
|
+
it('hides the filter helper text in the empty state', () => {
|
|
437
|
+
const emptyBill: MappedBill = { ...defaultBill, lineItems: [] };
|
|
438
|
+
render(<InvoiceTable bill={emptyBill} viewOnly />);
|
|
439
|
+
expect(screen.getByText(/no matching items to display/i)).toBeInTheDocument();
|
|
440
|
+
expect(screen.queryByText(/check the filters above/i)).not.toBeInTheDocument();
|
|
441
|
+
});
|
|
442
|
+
});
|
|
443
|
+
|
|
444
|
+
describe('line-item refund eligibility', () => {
|
|
445
|
+
const makeRefund = (overrides: Partial<BillRefund> = {}): BillRefund => ({
|
|
446
|
+
uuid: 'r-1',
|
|
447
|
+
billUuid: 'bill-uuid',
|
|
448
|
+
lineItemUuid: null,
|
|
449
|
+
refundAmount: 100,
|
|
450
|
+
reason: 'overcharged',
|
|
451
|
+
initiator: { uuid: 'u1', display: 'cashier' },
|
|
452
|
+
approver: null,
|
|
453
|
+
completer: null,
|
|
454
|
+
dateApproved: null,
|
|
455
|
+
dateCompleted: null,
|
|
456
|
+
dateCreated: '2024-01-01',
|
|
457
|
+
status: RefundStatus.REQUESTED,
|
|
458
|
+
voided: false,
|
|
459
|
+
...overrides,
|
|
460
|
+
});
|
|
461
|
+
|
|
462
|
+
const paidBill: MappedBill = { ...defaultBill, status: BillStatus.PAID, totalAmount: 300, tenderedAmount: 300 };
|
|
463
|
+
|
|
464
|
+
it('shows the refund action for an eligible line item on a PAID bill', async () => {
|
|
465
|
+
const user = userEvent.setup();
|
|
466
|
+
render(<InvoiceTable bill={paidBill} onMutate={mockOnMutate} />);
|
|
467
|
+
await user.click(screen.getByTestId('action-menu-1'));
|
|
468
|
+
expect(screen.getByTestId('request-refund-button-1')).toBeInTheDocument();
|
|
469
|
+
});
|
|
470
|
+
|
|
471
|
+
it('hides the refund action for a line item that already has an active refund', async () => {
|
|
472
|
+
const user = userEvent.setup();
|
|
473
|
+
render(
|
|
474
|
+
<InvoiceTable bill={{ ...paidBill, refunds: [makeRefund({ lineItemUuid: '1' })] }} onMutate={mockOnMutate} />,
|
|
475
|
+
);
|
|
476
|
+
await user.click(screen.getByTestId('action-menu-1'));
|
|
477
|
+
expect(screen.queryByTestId('request-refund-button-1')).not.toBeInTheDocument();
|
|
478
|
+
});
|
|
479
|
+
|
|
480
|
+
it('hides the refund action for all line items when there is an active bill-level refund', async () => {
|
|
481
|
+
const user = userEvent.setup();
|
|
482
|
+
render(
|
|
483
|
+
<InvoiceTable bill={{ ...paidBill, refunds: [makeRefund({ lineItemUuid: null })] }} onMutate={mockOnMutate} />,
|
|
484
|
+
);
|
|
485
|
+
await user.click(screen.getByTestId('action-menu-1'));
|
|
486
|
+
expect(screen.queryByTestId('request-refund-button-1')).not.toBeInTheDocument();
|
|
487
|
+
});
|
|
488
|
+
|
|
489
|
+
it('shows the refund action for a line item when bill is REFUND_REQUESTED due to a different line item', async () => {
|
|
490
|
+
const user = userEvent.setup();
|
|
491
|
+
const bill: MappedBill = {
|
|
492
|
+
...paidBill,
|
|
493
|
+
status: BillStatus.REFUND_REQUESTED,
|
|
494
|
+
refunds: [makeRefund({ uuid: 'r-1', lineItemUuid: '1' })],
|
|
495
|
+
};
|
|
496
|
+
render(<InvoiceTable bill={bill} onMutate={mockOnMutate} />);
|
|
497
|
+
await user.click(screen.getByTestId('action-menu-2'));
|
|
498
|
+
expect(screen.getByTestId('request-refund-button-2')).toBeInTheDocument();
|
|
499
|
+
});
|
|
500
|
+
|
|
501
|
+
it('hides the refund action for the line item that triggered REFUND_REQUESTED status', async () => {
|
|
502
|
+
const user = userEvent.setup();
|
|
503
|
+
const bill: MappedBill = {
|
|
504
|
+
...paidBill,
|
|
505
|
+
status: BillStatus.REFUND_REQUESTED,
|
|
506
|
+
refunds: [makeRefund({ uuid: 'r-1', lineItemUuid: '1' })],
|
|
507
|
+
};
|
|
508
|
+
render(<InvoiceTable bill={bill} onMutate={mockOnMutate} />);
|
|
509
|
+
await user.click(screen.getByTestId('action-menu-1'));
|
|
510
|
+
expect(screen.queryByTestId('request-refund-button-1')).not.toBeInTheDocument();
|
|
511
|
+
});
|
|
512
|
+
|
|
513
|
+
it('hides all line-item refund actions when there is an active bill-level refund on a REFUND_REQUESTED bill', async () => {
|
|
514
|
+
const user = userEvent.setup();
|
|
515
|
+
const bill: MappedBill = {
|
|
516
|
+
...paidBill,
|
|
517
|
+
status: BillStatus.REFUND_REQUESTED,
|
|
518
|
+
refunds: [makeRefund({ lineItemUuid: null })],
|
|
519
|
+
};
|
|
520
|
+
render(<InvoiceTable bill={bill} onMutate={mockOnMutate} />);
|
|
521
|
+
await user.click(screen.getByTestId('action-menu-1'));
|
|
522
|
+
expect(screen.queryByTestId('request-refund-button-1')).not.toBeInTheDocument();
|
|
523
|
+
});
|
|
524
|
+
});
|
|
525
|
+
|
|
526
|
+
describe('per-line-item discount action', () => {
|
|
527
|
+
it('opens the request-discount modal with lineItem scope', async () => {
|
|
528
|
+
const user = userEvent.setup();
|
|
529
|
+
const billPosted: MappedBill = {
|
|
530
|
+
...defaultBill,
|
|
531
|
+
status: 'POSTED',
|
|
532
|
+
lineItems: [
|
|
533
|
+
{
|
|
534
|
+
uuid: 'li1',
|
|
535
|
+
item: 'Consultation',
|
|
536
|
+
status: 'PENDING',
|
|
537
|
+
quantity: 1,
|
|
538
|
+
price: 1000,
|
|
539
|
+
display: '',
|
|
540
|
+
voided: false,
|
|
541
|
+
voidReason: '',
|
|
542
|
+
billableService: '',
|
|
543
|
+
priceName: '',
|
|
544
|
+
priceUuid: '',
|
|
545
|
+
lineItemOrder: 0,
|
|
546
|
+
resourceVersion: '',
|
|
547
|
+
},
|
|
548
|
+
],
|
|
549
|
+
totalAmount: 1000,
|
|
550
|
+
};
|
|
551
|
+
render(<InvoiceTable bill={billPosted} onMutate={mockOnMutate} />);
|
|
552
|
+
|
|
553
|
+
await user.click(screen.getByTestId('action-menu-li1'));
|
|
554
|
+
await user.click(screen.getByText(/request discount/i));
|
|
555
|
+
|
|
556
|
+
expect(mockShowModal).toHaveBeenCalledWith(
|
|
557
|
+
'request-discount-modal',
|
|
558
|
+
expect.objectContaining({
|
|
559
|
+
lineItem: expect.objectContaining({ uuid: 'li1', display: 'Consultation' }),
|
|
560
|
+
}),
|
|
561
|
+
);
|
|
562
|
+
});
|
|
563
|
+
});
|
|
412
564
|
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
|
2
|
-
import { Button, InlineLoading } from '@carbon/react';
|
|
2
|
+
import { Button, InlineLoading, Tooltip } from '@carbon/react';
|
|
3
3
|
import { Add, Printer } from '@carbon/react/icons';
|
|
4
4
|
import { useParams } from 'react-router-dom';
|
|
5
5
|
import { useReactToPrint } from 'react-to-print';
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
ExtensionSlot,
|
|
10
10
|
formatDate,
|
|
11
11
|
launchWorkspace2,
|
|
12
|
+
navigate,
|
|
12
13
|
parseDate,
|
|
13
14
|
showModal,
|
|
14
15
|
showSnackbar,
|
|
@@ -22,7 +23,9 @@ import InvoiceTable from './invoice-table.component';
|
|
|
22
23
|
import Payments from './payments/payments.component';
|
|
23
24
|
import PrintReceipt from './printable-invoice/print-receipt.component';
|
|
24
25
|
import PrintableInvoice from './printable-invoice/printable-invoice.component';
|
|
25
|
-
import { BillStatus } from '../types';
|
|
26
|
+
import { BillDiscountStatus, BillStatus, RefundStatus } from '../types';
|
|
27
|
+
import DiscountsTable from '../discounts/discounts-table.component';
|
|
28
|
+
import RefundsTable from '../refunds/refunds-table.component';
|
|
26
29
|
import styles from './invoice.scss';
|
|
27
30
|
|
|
28
31
|
interface InvoiceDetailsProps {
|
|
@@ -41,6 +44,64 @@ const Invoice: React.FC = () => {
|
|
|
41
44
|
const onBeforeGetContentResolve = useRef<(() => void) | null>(null);
|
|
42
45
|
const { defaultCurrency } = useConfig<BillingConfig>();
|
|
43
46
|
|
|
47
|
+
const discounts = (bill?.discounts ?? []).filter((d) => !d.voided);
|
|
48
|
+
const billLevelDiscountExists = discounts.some((d) => !d.lineItemUuid);
|
|
49
|
+
const lineItemDiscountExists = discounts.some((d) => !!d.lineItemUuid);
|
|
50
|
+
const billStatusEligible = bill?.status === BillStatus.PENDING || bill?.status === BillStatus.POSTED;
|
|
51
|
+
const showRequestDiscountButton = !!bill && billStatusEligible && !billLevelDiscountExists;
|
|
52
|
+
const hasApprovedDiscount = discounts.some((d) => d.status === BillDiscountStatus.APPROVED);
|
|
53
|
+
|
|
54
|
+
const refunds = (bill?.refunds ?? []).filter((r) => !r.voided);
|
|
55
|
+
const billStatusRefundEligible = bill?.status === BillStatus.PAID || bill?.status === BillStatus.PARTIALLY_REFUNDED;
|
|
56
|
+
const activeRefunds = refunds.filter(
|
|
57
|
+
(r) => r.status === RefundStatus.REQUESTED || r.status === RefundStatus.APPROVED,
|
|
58
|
+
);
|
|
59
|
+
const activeBillLevelRefund = activeRefunds.some((r) => !r.lineItemUuid);
|
|
60
|
+
const activeLineRefundUuids = new Set(activeRefunds.flatMap((r) => (r.lineItemUuid ? [r.lineItemUuid] : [])));
|
|
61
|
+
const showRequestRefundButton = !!bill && billStatusRefundEligible && !activeBillLevelRefund;
|
|
62
|
+
|
|
63
|
+
const handleRequestRefund = () => {
|
|
64
|
+
if (!bill) return;
|
|
65
|
+
if (bill.netAmount == null) {
|
|
66
|
+
showSnackbar({
|
|
67
|
+
title: t('refundUnavailable', 'Refund unavailable'),
|
|
68
|
+
subtitle: t('refundUnavailableSubtitle', 'Bill amount could not be determined. Please reload and try again.'),
|
|
69
|
+
kind: 'error',
|
|
70
|
+
});
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
const totalAlreadyRefunded = refunds
|
|
74
|
+
.filter((r) => r.status === RefundStatus.APPROVED || r.status === RefundStatus.COMPLETED)
|
|
75
|
+
.reduce((s, r) => s + r.refundAmount, 0);
|
|
76
|
+
const remainingRefundable = bill.netAmount - totalAlreadyRefunded;
|
|
77
|
+
const dispose = showModal('request-refund-modal', {
|
|
78
|
+
bill: {
|
|
79
|
+
uuid: bill.uuid,
|
|
80
|
+
total: bill.totalAmount ?? 0,
|
|
81
|
+
amountAfterDiscount: bill.netAmount,
|
|
82
|
+
receiptNumber: bill.receiptNumber,
|
|
83
|
+
lineItemCount: bill.lineItems?.length ?? 0,
|
|
84
|
+
},
|
|
85
|
+
remainingRefundable: Math.max(0, remainingRefundable),
|
|
86
|
+
onMutate: () => mutate(),
|
|
87
|
+
closeModal: () => dispose(),
|
|
88
|
+
});
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
const handleRequestDiscount = () => {
|
|
92
|
+
const dispose = showModal('request-discount-modal', {
|
|
93
|
+
bill: {
|
|
94
|
+
uuid: bill!.uuid,
|
|
95
|
+
total: bill!.totalAmount ?? 0,
|
|
96
|
+
amountDue: Math.max(0, (bill!.netAmount ?? bill!.totalAmount ?? 0) - (bill!.tenderedAmount ?? 0)),
|
|
97
|
+
receiptNumber: bill!.receiptNumber,
|
|
98
|
+
lineItemCount: bill!.lineItems?.length ?? 0,
|
|
99
|
+
},
|
|
100
|
+
onMutate: () => mutate(),
|
|
101
|
+
closeModal: () => dispose(),
|
|
102
|
+
});
|
|
103
|
+
};
|
|
104
|
+
|
|
44
105
|
const handleAfterPrint = useCallback(() => {
|
|
45
106
|
onBeforeGetContentResolve.current = null;
|
|
46
107
|
setIsPrinting(false);
|
|
@@ -63,6 +124,14 @@ const Invoice: React.FC = () => {
|
|
|
63
124
|
});
|
|
64
125
|
};
|
|
65
126
|
|
|
127
|
+
const handleDeleteBill = () => {
|
|
128
|
+
const dispose = showModal('delete-bill-confirmation-modal', {
|
|
129
|
+
bill,
|
|
130
|
+
onSuccess: () => navigate({ to: window.getOpenmrsSpaBase() + 'home/billing' }),
|
|
131
|
+
closeModal: () => dispose(),
|
|
132
|
+
});
|
|
133
|
+
};
|
|
134
|
+
|
|
66
135
|
const handlePrint = useReactToPrint({
|
|
67
136
|
contentRef: componentRef,
|
|
68
137
|
documentTitle: `Invoice ${bill?.receiptNumber} - ${patient?.name?.[0]?.given?.join(' ')} ${patient?.name?.[0].family}`,
|
|
@@ -87,17 +156,28 @@ const Invoice: React.FC = () => {
|
|
|
87
156
|
/**
|
|
88
157
|
* t('totalAmount', 'Total amount')
|
|
89
158
|
* t('amountTendered', 'Amount tendered')
|
|
90
|
-
* t('invoiceNumber', 'Invoice
|
|
159
|
+
* t('invoiceNumber', 'Invoice number')
|
|
91
160
|
* t('dateAndTime', 'Date and time')
|
|
92
161
|
* t('invoiceStatus', 'Invoice status')
|
|
93
162
|
*/
|
|
94
|
-
const invoiceDetails = {
|
|
163
|
+
const invoiceDetails: Record<string, string | number | undefined> = {
|
|
164
|
+
[t('dateBillCreated', 'Date bill created')]: bill?.dateCreated
|
|
165
|
+
? formatDate(parseDate(bill.dateCreated), { mode: 'wide' })
|
|
166
|
+
: '--',
|
|
95
167
|
[t('totalAmount', 'Total amount')]: convertToCurrency(bill?.totalAmount, defaultCurrency),
|
|
168
|
+
...(hasApprovedDiscount
|
|
169
|
+
? {
|
|
170
|
+
[t('discount', 'Discount')]:
|
|
171
|
+
`- ${convertToCurrency((bill?.totalAmount ?? 0) - (bill?.netAmount ?? 0), defaultCurrency)}`,
|
|
172
|
+
[t('netAmount', 'Net amount')]: convertToCurrency(bill?.netAmount, defaultCurrency),
|
|
173
|
+
}
|
|
174
|
+
: {}),
|
|
96
175
|
[t('amountTendered', 'Amount tendered')]: convertToCurrency(bill?.tenderedAmount, defaultCurrency),
|
|
176
|
+
[t('amountDue', 'Amount due')]: convertToCurrency(
|
|
177
|
+
bill ? (bill.netAmount ?? 0) - (bill.tenderedAmount ?? 0) : undefined,
|
|
178
|
+
defaultCurrency,
|
|
179
|
+
),
|
|
97
180
|
[t('invoiceNumber', 'Invoice number')]: bill?.receiptNumber,
|
|
98
|
-
[t('dateAndTime', 'Date and time')]: bill?.dateCreated
|
|
99
|
-
? formatDate(parseDate(bill.dateCreated), { mode: 'wide' })
|
|
100
|
-
: '--',
|
|
101
181
|
[t('invoiceStatus', 'Invoice status')]: bill?.status,
|
|
102
182
|
};
|
|
103
183
|
|
|
@@ -129,6 +209,25 @@ const Invoice: React.FC = () => {
|
|
|
129
209
|
<InlineLoading status="active" />
|
|
130
210
|
</span>
|
|
131
211
|
)}
|
|
212
|
+
{showRequestDiscountButton && (
|
|
213
|
+
<Button kind="tertiary" onClick={handleRequestDiscount} disabled={lineItemDiscountExists}>
|
|
214
|
+
{t('requestDiscount', 'Request discount')}
|
|
215
|
+
</Button>
|
|
216
|
+
)}
|
|
217
|
+
{showRequestRefundButton &&
|
|
218
|
+
(activeLineRefundUuids.size > 0 ? (
|
|
219
|
+
<Tooltip
|
|
220
|
+
align="bottom"
|
|
221
|
+
label={t('refundInProgress', 'A refund is already in progress for one or more line items')}>
|
|
222
|
+
<Button kind="tertiary" onClick={handleRequestRefund} disabled>
|
|
223
|
+
{t('requestRefund', 'Request refund')}
|
|
224
|
+
</Button>
|
|
225
|
+
</Tooltip>
|
|
226
|
+
) : (
|
|
227
|
+
<Button kind="tertiary" onClick={handleRequestRefund}>
|
|
228
|
+
{t('requestRefund', 'Request refund')}
|
|
229
|
+
</Button>
|
|
230
|
+
))}
|
|
132
231
|
{bill?.status === BillStatus.PENDING && (
|
|
133
232
|
<>
|
|
134
233
|
<Button
|
|
@@ -143,6 +242,9 @@ const Invoice: React.FC = () => {
|
|
|
143
242
|
}>
|
|
144
243
|
{t('addItemsToBill', 'Add items to bill')}
|
|
145
244
|
</Button>
|
|
245
|
+
<Button kind="danger--ghost" onClick={handleDeleteBill}>
|
|
246
|
+
{t('deleteBill', 'Delete bill')}
|
|
247
|
+
</Button>
|
|
146
248
|
<Button kind="primary" onClick={handleFinalizeBill}>
|
|
147
249
|
{t('finalizeBill', 'Finalize bill')}
|
|
148
250
|
</Button>
|
|
@@ -167,6 +269,8 @@ const Invoice: React.FC = () => {
|
|
|
167
269
|
|
|
168
270
|
<div className={styles.invoiceContent}>
|
|
169
271
|
<InvoiceTable bill={bill} isLoadingBill={isLoadingBill} onMutate={mutate} />
|
|
272
|
+
{bill && <DiscountsTable bill={bill} />}
|
|
273
|
+
{bill && <RefundsTable bill={bill} onMutate={mutate} />}
|
|
170
274
|
<Payments bill={bill} mutate={mutate} />
|
|
171
275
|
</div>
|
|
172
276
|
|