@openmrs/esm-billing-app 1.0.2-pre.82 → 1.0.2-pre.824
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/.eslintrc +16 -2
- package/README.md +54 -9
- package/__mocks__/bills.mock.ts +12 -0
- package/__mocks__/react-i18next.js +6 -5
- package/dist/1119.js +1 -1
- package/dist/1146.js +1 -2
- package/dist/1146.js.map +1 -1
- package/dist/1197.js +1 -1
- package/dist/1856.js +1 -0
- package/dist/1856.js.map +1 -0
- package/dist/2146.js +1 -1
- package/dist/2177.js +2 -0
- package/dist/2177.js.LICENSE.txt +9 -0
- package/dist/2177.js.map +1 -0
- package/dist/2524.js +1 -0
- package/dist/2524.js.map +1 -0
- package/dist/2690.js +1 -1
- package/dist/3041.js +1 -0
- package/dist/3041.js.map +1 -0
- package/dist/3099.js +1 -1
- package/dist/3584.js +1 -1
- package/dist/3717.js +2 -0
- package/dist/3717.js.map +1 -0
- package/dist/4055.js +1 -1
- package/dist/4132.js +1 -1
- package/dist/4225.js +1 -0
- package/dist/4225.js.map +1 -0
- package/dist/4300.js +1 -1
- package/dist/4335.js +1 -1
- package/dist/4344.js +1 -0
- package/dist/4344.js.map +1 -0
- package/dist/4618.js +1 -1
- package/dist/4652.js +1 -1
- package/dist/4724.js +1 -0
- package/dist/4724.js.map +1 -0
- package/dist/4739.js +1 -1
- package/dist/4739.js.map +1 -1
- package/dist/4944.js +1 -1
- package/dist/5173.js +1 -1
- package/dist/5241.js +1 -1
- package/dist/5422.js +1 -0
- package/dist/5422.js.map +1 -0
- package/dist/5442.js +1 -1
- package/dist/5661.js +1 -1
- package/dist/6022.js +1 -1
- package/dist/6295.js +2 -0
- package/dist/{6525.js.LICENSE.txt → 6295.js.LICENSE.txt} +16 -4
- package/dist/6295.js.map +1 -0
- package/dist/6468.js +1 -1
- package/dist/6540.js +1 -1
- package/dist/6540.js.map +1 -1
- package/dist/6606.js +1 -0
- package/dist/6606.js.map +1 -0
- package/dist/6679.js +1 -1
- package/dist/6840.js +1 -1
- package/dist/6859.js +1 -1
- package/dist/7097.js +1 -1
- package/dist/7159.js +1 -1
- package/dist/723.js +1 -1
- package/dist/7617.js +1 -1
- package/dist/795.js +1 -1
- package/dist/8163.js +1 -1
- package/dist/8349.js +1 -1
- package/dist/8618.js +1 -1
- package/dist/890.js +1 -1
- package/dist/9214.js +1 -1
- package/dist/9538.js +1 -1
- package/dist/9569.js +1 -1
- package/dist/961.js +1 -1
- package/dist/961.js.map +1 -1
- package/dist/986.js +1 -1
- package/dist/9879.js +1 -1
- package/dist/9895.js +1 -1
- package/dist/9900.js +1 -1
- package/dist/9913.js +1 -1
- package/dist/main.js +1 -1
- package/dist/main.js.map +1 -1
- package/dist/openmrs-esm-billing-app.js +1 -1
- package/dist/openmrs-esm-billing-app.js.buildmanifest.json +388 -282
- package/dist/openmrs-esm-billing-app.js.map +1 -1
- package/dist/routes.json +1 -1
- package/e2e/README.md +19 -18
- package/e2e/core/test.ts +1 -1
- package/e2e/fixtures/api.ts +1 -1
- package/e2e/specs/sample-test.spec.ts +0 -1
- package/e2e/support/github/Dockerfile +1 -1
- package/package.json +13 -10
- package/src/bill-history/bill-history.component.tsx +17 -25
- package/src/bill-history/bill-history.scss +4 -94
- package/src/bill-history/bill-history.test.tsx +37 -78
- package/src/bill-item-actions/bill-item-actions.scss +21 -5
- package/src/bill-item-actions/edit-bill-item.modal.tsx +225 -0
- package/src/bill-item-actions/edit-bill-item.test.tsx +211 -37
- package/src/billable-services/bill-waiver/bill-selection.component.tsx +4 -4
- package/src/billable-services/bill-waiver/bill-waiver-form.component.tsx +26 -30
- package/src/billable-services/bill-waiver/patient-bills.component.tsx +3 -3
- package/src/billable-services/bill-waiver/utils.ts +13 -3
- package/src/billable-services/billable-service.resource.ts +28 -12
- package/src/billable-services/billable-services-home.component.tsx +1 -1
- package/src/billable-services/billable-services.component.tsx +142 -145
- package/src/billable-services/billable-services.scss +3 -0
- package/src/billable-services/billable-services.test.tsx +2 -45
- package/src/billable-services/cash-point/add-cash-point.modal.tsx +168 -0
- package/src/billable-services/cash-point/cash-point-configuration.component.tsx +18 -192
- package/src/billable-services/cash-point/cash-point-configuration.scss +1 -5
- package/src/billable-services/create-edit/add-billable-service.component.tsx +358 -300
- package/src/billable-services/create-edit/add-billable-service.scss +6 -65
- package/src/billable-services/create-edit/add-billable-service.test.tsx +166 -80
- package/src/billable-services/create-edit/edit-billable-service.modal.tsx +51 -0
- package/src/billable-services/payment-modes/add-payment-mode.modal.tsx +121 -0
- package/src/billable-services/payment-modes/delete-payment-mode.modal.tsx +72 -0
- package/src/billable-services/payment-modes/payment-modes-config.component.tsx +125 -0
- package/src/billable-services/{payyment-modes → payment-modes}/payment-modes-config.scss +5 -4
- package/src/billing-form/billing-checkin-form.component.tsx +20 -16
- package/src/billing-form/billing-checkin-form.test.tsx +97 -24
- package/src/billing-form/billing-form.component.tsx +222 -292
- package/src/billing-form/billing-form.scss +143 -0
- package/src/billing.resource.ts +69 -74
- package/src/bills-table/bills-table.test.tsx +98 -54
- package/src/config-schema.ts +52 -24
- package/src/dashboard.meta.ts +4 -2
- package/src/helpers/functions.ts +5 -4
- package/src/index.ts +17 -6
- package/src/invoice/invoice-table.component.tsx +35 -69
- package/src/invoice/invoice-table.scss +8 -5
- package/src/invoice/invoice-table.test.tsx +273 -62
- package/src/invoice/invoice.component.tsx +38 -29
- package/src/invoice/invoice.scss +11 -4
- package/src/invoice/invoice.test.tsx +324 -120
- package/src/invoice/payments/invoice-breakdown/invoice-breakdown.scss +9 -9
- package/src/invoice/payments/payment-form/payment-form.component.tsx +43 -34
- package/src/invoice/payments/payment-form/payment-form.scss +5 -6
- package/src/invoice/payments/payment-form/payment-form.test.tsx +216 -66
- package/src/invoice/payments/payment-history/payment-history.component.tsx +6 -4
- package/src/invoice/payments/payment-history/payment-history.test.tsx +9 -14
- package/src/invoice/payments/payments.component.tsx +53 -65
- package/src/invoice/payments/payments.scss +4 -3
- package/src/invoice/payments/payments.test.tsx +282 -0
- package/src/invoice/payments/utils.ts +15 -27
- package/src/invoice/printable-invoice/print-receipt.component.tsx +3 -2
- package/src/invoice/printable-invoice/print-receipt.test.tsx +14 -25
- package/src/invoice/printable-invoice/printable-footer.component.tsx +2 -2
- package/src/invoice/printable-invoice/printable-footer.test.tsx +4 -13
- package/src/invoice/printable-invoice/printable-invoice-header.component.tsx +12 -11
- package/src/invoice/printable-invoice/printable-invoice-header.test.tsx +16 -14
- package/src/invoice/printable-invoice/printable-invoice.component.tsx +19 -33
- package/src/left-panel-link.test.tsx +1 -4
- package/src/metrics-cards/metrics-cards.test.tsx +18 -5
- package/src/modal/require-payment-modal.test.tsx +27 -22
- package/src/modal/{require-payment-modal.component.tsx → require-payment.modal.tsx} +17 -18
- package/src/routes.json +25 -7
- package/src/types/index.ts +80 -18
- package/translations/am.json +69 -21
- package/translations/ar.json +69 -21
- package/translations/ar_SY.json +69 -21
- package/translations/bn.json +74 -26
- package/translations/de.json +69 -21
- package/translations/en.json +74 -24
- package/translations/en_US.json +69 -21
- package/translations/es.json +69 -21
- package/translations/es_MX.json +69 -21
- package/translations/fr.json +82 -34
- package/translations/he.json +69 -21
- package/translations/hi.json +69 -21
- package/translations/hi_IN.json +69 -21
- package/translations/id.json +69 -21
- package/translations/it.json +104 -56
- package/translations/ka.json +69 -21
- package/translations/km.json +69 -21
- package/translations/ku.json +69 -21
- package/translations/ky.json +69 -21
- package/translations/lg.json +69 -21
- package/translations/ne.json +69 -21
- package/translations/pl.json +69 -21
- package/translations/pt.json +69 -21
- package/translations/pt_BR.json +69 -21
- package/translations/qu.json +69 -21
- package/translations/ro_RO.json +213 -165
- package/translations/ru_RU.json +69 -21
- package/translations/si.json +69 -21
- package/translations/sw.json +69 -21
- package/translations/sw_KE.json +69 -21
- package/translations/tr.json +69 -21
- package/translations/tr_TR.json +69 -21
- package/translations/uk.json +69 -21
- package/translations/uz.json +69 -21
- package/translations/uz@Latn.json +69 -21
- package/translations/uz_UZ.json +69 -21
- package/translations/vi.json +69 -21
- package/translations/zh.json +69 -21
- package/translations/zh_CN.json +124 -76
- package/dist/1146.js.LICENSE.txt +0 -21
- package/dist/2352.js +0 -1
- package/dist/2352.js.map +0 -1
- package/dist/246.js +0 -1
- package/dist/246.js.map +0 -1
- package/dist/6525.js +0 -2
- package/dist/6525.js.map +0 -1
- package/dist/8556.js +0 -2
- package/dist/8556.js.map +0 -1
- package/dist/8638.js +0 -1
- package/dist/8638.js.map +0 -1
- package/dist/9968.js +0 -1
- package/dist/9968.js.map +0 -1
- package/src/bill-item-actions/edit-bill-item.component.tsx +0 -221
- package/src/billable-services/payyment-modes/payment-modes-config.component.tsx +0 -280
- package/src/invoice/payments/payments.component.test.tsx +0 -121
- /package/dist/{8556.js.LICENSE.txt → 3717.js.LICENSE.txt} +0 -0
|
@@ -3,28 +3,19 @@ import { screen, render } from '@testing-library/react';
|
|
|
3
3
|
import { useDefaultFacility } from '../../billing.resource';
|
|
4
4
|
import PrintableFooter from './printable-footer.component';
|
|
5
5
|
|
|
6
|
-
const mockUseDefaultFacility =
|
|
6
|
+
const mockUseDefaultFacility = jest.mocked<typeof useDefaultFacility>(useDefaultFacility);
|
|
7
7
|
|
|
8
8
|
jest.mock('../../billing.resource', () => ({
|
|
9
9
|
useDefaultFacility: jest.fn(),
|
|
10
10
|
}));
|
|
11
11
|
|
|
12
12
|
describe('PrintableFooter', () => {
|
|
13
|
-
beforeEach(() => {
|
|
14
|
-
jest.clearAllMocks();
|
|
15
|
-
});
|
|
16
|
-
|
|
17
13
|
test('should render PrintableFooter component', () => {
|
|
18
|
-
mockUseDefaultFacility.mockReturnValue({
|
|
14
|
+
mockUseDefaultFacility.mockReturnValue({
|
|
15
|
+
data: { display: 'MTRH', uuid: 'mtrh-uuid', links: [] },
|
|
16
|
+
});
|
|
19
17
|
render(<PrintableFooter />);
|
|
20
18
|
const footer = screen.getByText('MTRH');
|
|
21
19
|
expect(footer).toBeInTheDocument();
|
|
22
20
|
});
|
|
23
|
-
|
|
24
|
-
test('should show placeholder text when facility isLoading', () => {
|
|
25
|
-
mockUseDefaultFacility.mockReturnValue({ data: { display: 'MTRH', uuid: 'mtrh-uuid' }, isLoading: true });
|
|
26
|
-
render(<PrintableFooter />);
|
|
27
|
-
const footer = screen.getByText('--');
|
|
28
|
-
expect(footer).toBeInTheDocument();
|
|
29
|
-
});
|
|
30
21
|
});
|
|
@@ -1,27 +1,28 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { type PatientDetails } from '../../types';
|
|
3
|
-
import { useConfig } from '@openmrs/esm-framework';
|
|
3
|
+
import { type SessionLocation, useConfig, interpolateUrl } from '@openmrs/esm-framework';
|
|
4
4
|
import { useTranslation } from 'react-i18next';
|
|
5
|
-
import {
|
|
5
|
+
import type { BillingConfig } from '../../config-schema';
|
|
6
6
|
import styles from './printable-invoice-header.scss';
|
|
7
|
+
import isEmpty from 'lodash/isEmpty';
|
|
7
8
|
|
|
8
9
|
interface PrintableInvoiceHeaderProps {
|
|
9
10
|
patientDetails: PatientDetails;
|
|
11
|
+
defaultFacility: SessionLocation | null;
|
|
10
12
|
}
|
|
11
13
|
|
|
12
|
-
const PrintableInvoiceHeader: React.FC<PrintableInvoiceHeaderProps> = ({ patientDetails }) => {
|
|
14
|
+
const PrintableInvoiceHeader: React.FC<PrintableInvoiceHeaderProps> = ({ patientDetails, defaultFacility }) => {
|
|
13
15
|
const { t } = useTranslation();
|
|
14
|
-
const { logo } = useConfig();
|
|
15
|
-
const { data } = useDefaultFacility();
|
|
16
|
+
const { logo, country } = useConfig<BillingConfig>();
|
|
16
17
|
|
|
17
18
|
return (
|
|
18
19
|
<div className={styles.container}>
|
|
19
20
|
<div className={styles.printableHeader}>
|
|
20
21
|
<p className={styles.heading}>{t('invoice', 'Invoice')}</p>
|
|
21
|
-
{logo?.src ? (
|
|
22
|
-
<img className={styles.img} src={logo.src} alt={logo.alt} />
|
|
23
|
-
) : logo?.
|
|
24
|
-
logo.
|
|
22
|
+
{logo?.src && !isEmpty(logo.src) ? (
|
|
23
|
+
<img className={styles.img} src={interpolateUrl(logo.src)} alt={logo.alt} />
|
|
24
|
+
) : logo?.alt && !isEmpty(logo.alt) ? (
|
|
25
|
+
logo.alt
|
|
25
26
|
) : (
|
|
26
27
|
// OpenMRS Logo
|
|
27
28
|
<svg
|
|
@@ -52,8 +53,8 @@ const PrintableInvoiceHeader: React.FC<PrintableInvoiceHeaderProps> = ({ patient
|
|
|
52
53
|
</div>
|
|
53
54
|
|
|
54
55
|
<div className={styles.facilityDetails}>
|
|
55
|
-
<p className={styles.facilityName}>{
|
|
56
|
-
<p className={styles.itemLabel}>
|
|
56
|
+
<p className={styles.facilityName}>{defaultFacility?.display}</p>
|
|
57
|
+
<p className={styles.itemLabel}>{country}</p>
|
|
57
58
|
</div>
|
|
58
59
|
</div>
|
|
59
60
|
</div>
|
|
@@ -1,19 +1,17 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { screen, render } from '@testing-library/react';
|
|
3
3
|
import { useConfig } from '@openmrs/esm-framework';
|
|
4
|
+
import { type BillingConfig } from '../../config-schema';
|
|
4
5
|
import { useDefaultFacility } from '../../billing.resource';
|
|
5
6
|
import PrintableInvoiceHeader from './printable-invoice-header.component';
|
|
6
7
|
|
|
7
|
-
const mockUseDefaultFacility =
|
|
8
|
-
const mockUseConfig =
|
|
8
|
+
const mockUseDefaultFacility = jest.mocked(useDefaultFacility);
|
|
9
|
+
const mockUseConfig = jest.mocked(useConfig<BillingConfig>);
|
|
9
10
|
|
|
10
11
|
jest.mock('../../billing.resource', () => ({
|
|
11
12
|
useDefaultFacility: jest.fn(),
|
|
12
13
|
}));
|
|
13
14
|
|
|
14
|
-
jest.mock('@openmrs/esm-framework', () => ({
|
|
15
|
-
useConfig: jest.fn(),
|
|
16
|
-
}));
|
|
17
15
|
const testProps = {
|
|
18
16
|
patientDetails: {
|
|
19
17
|
name: 'John Doe',
|
|
@@ -25,11 +23,19 @@ const testProps = {
|
|
|
25
23
|
},
|
|
26
24
|
};
|
|
27
25
|
|
|
26
|
+
const defaultFacility = { display: 'MTRH', uuid: 'mtrh-uuid', links: [] };
|
|
27
|
+
|
|
28
28
|
describe('PrintableInvoiceHeader', () => {
|
|
29
|
+
beforeEach(() => {
|
|
30
|
+
mockUseConfig.mockReturnValue({
|
|
31
|
+
logo: { src: 'logo.png', alt: 'logo' },
|
|
32
|
+
country: 'Kenya',
|
|
33
|
+
} as unknown as BillingConfig);
|
|
34
|
+
mockUseDefaultFacility.mockReturnValue({ data: { display: 'MTRH', uuid: 'mtrh-uuid', links: [] } });
|
|
35
|
+
});
|
|
36
|
+
|
|
29
37
|
test('should render PrintableInvoiceHeader component', () => {
|
|
30
|
-
|
|
31
|
-
mockUseDefaultFacility.mockReturnValue({ data: { display: 'MTRH', uuid: 'mtrh-uuid' }, isLoading: false });
|
|
32
|
-
render(<PrintableInvoiceHeader {...testProps} />);
|
|
38
|
+
render(<PrintableInvoiceHeader {...testProps} defaultFacility={defaultFacility} />);
|
|
33
39
|
const header = screen.getByText('Invoice');
|
|
34
40
|
expect(header).toBeInTheDocument();
|
|
35
41
|
|
|
@@ -41,17 +47,13 @@ describe('PrintableInvoiceHeader', () => {
|
|
|
41
47
|
});
|
|
42
48
|
|
|
43
49
|
test('should display the logo when logo is provided', () => {
|
|
44
|
-
|
|
45
|
-
mockUseDefaultFacility.mockReturnValue({ data: { display: 'MTRH', uuid: 'mtrh-uuid' }, isLoading: false });
|
|
46
|
-
render(<PrintableInvoiceHeader {...testProps} />);
|
|
50
|
+
render(<PrintableInvoiceHeader {...testProps} defaultFacility={defaultFacility} />);
|
|
47
51
|
const logo = screen.getByAltText('logo');
|
|
48
52
|
expect(logo).toBeInTheDocument();
|
|
49
53
|
});
|
|
50
54
|
|
|
51
55
|
test('should display the default logo when logo is not provided', () => {
|
|
52
|
-
|
|
53
|
-
mockUseDefaultFacility.mockReturnValue({ data: { display: 'MTRH', uuid: 'mtrh-uuid' }, isLoading: false });
|
|
54
|
-
render(<PrintableInvoiceHeader {...testProps} />);
|
|
56
|
+
render(<PrintableInvoiceHeader {...testProps} defaultFacility={defaultFacility} />);
|
|
55
57
|
const logo = screen.getByRole('img');
|
|
56
58
|
expect(logo).toBeInTheDocument();
|
|
57
59
|
});
|
|
@@ -8,9 +8,8 @@ import {
|
|
|
8
8
|
TableBody,
|
|
9
9
|
TableHeader,
|
|
10
10
|
TableCell,
|
|
11
|
-
DataTableSkeleton,
|
|
12
11
|
} from '@carbon/react';
|
|
13
|
-
import { age, isDesktop, useLayoutType } from '@openmrs/esm-framework';
|
|
12
|
+
import { age, isDesktop, type SessionLocation, useLayoutType } from '@openmrs/esm-framework';
|
|
14
13
|
import { getGender } from '../../helpers';
|
|
15
14
|
import { type MappedBill } from '../../types';
|
|
16
15
|
import { useTranslation } from 'react-i18next';
|
|
@@ -21,25 +20,26 @@ import styles from './printable-invoice.scss';
|
|
|
21
20
|
type PrintableInvoiceProps = {
|
|
22
21
|
bill: MappedBill;
|
|
23
22
|
patient: fhir.Patient;
|
|
24
|
-
|
|
23
|
+
componentRef: React.RefObject<HTMLDivElement>;
|
|
24
|
+
defaultFacility: SessionLocation;
|
|
25
25
|
};
|
|
26
26
|
|
|
27
|
-
const PrintableInvoice: React.FC<PrintableInvoiceProps> = ({ bill, patient,
|
|
27
|
+
const PrintableInvoice: React.FC<PrintableInvoiceProps> = ({ bill, patient, componentRef, defaultFacility }) => {
|
|
28
28
|
const { t } = useTranslation();
|
|
29
29
|
const layout = useLayoutType();
|
|
30
30
|
const responsiveSize = isDesktop(layout) ? 'sm' : 'lg';
|
|
31
31
|
const headerData = [
|
|
32
|
-
{ header: 'Inventory item', key: 'billItem' },
|
|
33
|
-
{ header: 'Quantity', key: 'quantity' },
|
|
34
|
-
{ header: 'Unit price', key: 'price' },
|
|
35
|
-
{ header: 'Total', key: 'total' },
|
|
32
|
+
{ header: t('inventoryItem', 'Inventory item'), key: 'billItem' },
|
|
33
|
+
{ header: t('quantity', 'Quantity'), key: 'quantity' },
|
|
34
|
+
{ header: t('unitPrice', 'Unit price'), key: 'price' },
|
|
35
|
+
{ header: t('total', 'Total'), key: 'total' },
|
|
36
36
|
];
|
|
37
37
|
|
|
38
38
|
const rowData =
|
|
39
39
|
bill?.lineItems?.map((item) => {
|
|
40
40
|
return {
|
|
41
41
|
id: `${item.uuid}`,
|
|
42
|
-
billItem: item.item,
|
|
42
|
+
billItem: item.billableService ?? item.item,
|
|
43
43
|
quantity: item.quantity,
|
|
44
44
|
price: item.price,
|
|
45
45
|
total: item.price * item.quantity,
|
|
@@ -47,10 +47,10 @@ const PrintableInvoice: React.FC<PrintableInvoiceProps> = ({ bill, patient, isLo
|
|
|
47
47
|
}) ?? [];
|
|
48
48
|
|
|
49
49
|
const invoiceTotal = {
|
|
50
|
-
'Total Amount': bill?.totalAmount,
|
|
51
|
-
'Amount Tendered': bill?.tenderedAmount,
|
|
52
|
-
'Discount Amount': 0,
|
|
53
|
-
'Amount due': bill?.totalAmount - bill?.tenderedAmount,
|
|
50
|
+
[t('totalAmount', 'Total Amount')]: bill?.totalAmount,
|
|
51
|
+
[t('amountTendered', 'Amount Tendered')]: bill?.tenderedAmount,
|
|
52
|
+
[t('discountAmount', 'Discount Amount')]: 0,
|
|
53
|
+
[t('amountDue', 'Amount due')]: bill?.totalAmount - bill?.tenderedAmount,
|
|
54
54
|
};
|
|
55
55
|
|
|
56
56
|
const patientDetails = useMemo(() => {
|
|
@@ -65,28 +65,14 @@ const PrintableInvoice: React.FC<PrintableInvoiceProps> = ({ bill, patient, isLo
|
|
|
65
65
|
}, [patient, t]);
|
|
66
66
|
|
|
67
67
|
const invoiceDetails = {
|
|
68
|
-
'Invoice #': bill?.receiptNumber,
|
|
69
|
-
'Invoice date': bill
|
|
70
|
-
Status: bill?.status,
|
|
68
|
+
[t('invoiceNumber', 'Invoice #')]: bill?.receiptNumber,
|
|
69
|
+
[t('invoiceDate', 'Invoice date')]: bill?.dateCreated,
|
|
70
|
+
[t('status', 'Status')]: bill?.status,
|
|
71
71
|
};
|
|
72
72
|
|
|
73
|
-
if (isLoading) {
|
|
74
|
-
return (
|
|
75
|
-
<div className={styles.loaderContainer}>
|
|
76
|
-
<DataTableSkeleton
|
|
77
|
-
columnCount={headerData?.length ?? 0}
|
|
78
|
-
showHeader={false}
|
|
79
|
-
showToolbar={false}
|
|
80
|
-
size={responsiveSize}
|
|
81
|
-
zebra
|
|
82
|
-
/>
|
|
83
|
-
</div>
|
|
84
|
-
);
|
|
85
|
-
}
|
|
86
|
-
|
|
87
73
|
return (
|
|
88
|
-
<div className={styles.container}>
|
|
89
|
-
<PrintableInvoiceHeader patientDetails={patientDetails} />
|
|
74
|
+
<div className={styles.container} ref={componentRef}>
|
|
75
|
+
<PrintableInvoiceHeader patientDetails={patientDetails} defaultFacility={defaultFacility} />
|
|
90
76
|
<div className={styles.printableInvoiceContainer}>
|
|
91
77
|
<div className={styles.detailsContainer}>
|
|
92
78
|
{Object.entries(invoiceDetails).map(([key, val]) => (
|
|
@@ -99,7 +85,7 @@ const PrintableInvoice: React.FC<PrintableInvoiceProps> = ({ bill, patient, isLo
|
|
|
99
85
|
|
|
100
86
|
<div className={styles.itemsContainer}>
|
|
101
87
|
<div className={styles.tableContainer}>
|
|
102
|
-
<DataTable
|
|
88
|
+
<DataTable rows={rowData} headers={headerData} size={responsiveSize} useZebraStyles={false}>
|
|
103
89
|
{({ rows, headers, getRowProps, getTableProps }) => (
|
|
104
90
|
<TableContainer>
|
|
105
91
|
<Table {...getTableProps()} aria-label="Invoice line items">
|
|
@@ -23,7 +23,6 @@ describe('LinkExtension Component', () => {
|
|
|
23
23
|
});
|
|
24
24
|
|
|
25
25
|
describe('createLeftPanelLink Function', () => {
|
|
26
|
-
const user = userEvent.setup();
|
|
27
26
|
test('returns a component that renders LinkExtension', () => {
|
|
28
27
|
const config = { name: 'billing', title: 'Billing' };
|
|
29
28
|
const TestComponent = createLeftPanelLink(config);
|
|
@@ -31,8 +30,6 @@ describe('createLeftPanelLink Function', () => {
|
|
|
31
30
|
render(<TestComponent />);
|
|
32
31
|
expect(screen.getByText('Billing')).toBeInTheDocument();
|
|
33
32
|
const testLink = screen.getByRole('link', { name: 'Billing' });
|
|
34
|
-
|
|
35
|
-
expect(window.location.pathname).toBe('/billing/6eb8d678-514d-46ad-9554-51e48d96d567');
|
|
36
|
-
// expect(testLink).toHaveClass('active-left-nav-link');
|
|
33
|
+
expect(testLink).toHaveAttribute('href', '/openmrs/spa/home/billing');
|
|
37
34
|
});
|
|
38
35
|
});
|
|
@@ -3,10 +3,11 @@ import { render, screen } from '@testing-library/react';
|
|
|
3
3
|
import { useConfig } from '@openmrs/esm-framework';
|
|
4
4
|
import { billsSummary } from '../../__mocks__/bills.mock';
|
|
5
5
|
import { useBills } from '../billing.resource';
|
|
6
|
+
import { type MappedBill } from '../types';
|
|
6
7
|
import MetricsCards from './metrics-cards.component';
|
|
7
8
|
|
|
8
|
-
const mockUseBills =
|
|
9
|
-
const mockUseConfig =
|
|
9
|
+
const mockUseBills = jest.mocked(useBills);
|
|
10
|
+
const mockUseConfig = jest.mocked(useConfig);
|
|
10
11
|
|
|
11
12
|
jest.mock('../billing.resource', () => ({
|
|
12
13
|
useBills: jest.fn(),
|
|
@@ -14,13 +15,19 @@ jest.mock('../billing.resource', () => ({
|
|
|
14
15
|
|
|
15
16
|
describe('MetricsCards', () => {
|
|
16
17
|
test('renders loading state', () => {
|
|
17
|
-
mockUseBills.mockReturnValue({ isLoading: true, bills: [], error: null });
|
|
18
|
+
mockUseBills.mockReturnValue({ isLoading: true, bills: [], error: null, isValidating: false, mutate: jest.fn() });
|
|
18
19
|
renderMetricsCards();
|
|
19
20
|
expect(screen.getByText(/Loading bill metrics.../i)).toBeInTheDocument();
|
|
20
21
|
});
|
|
21
22
|
|
|
22
23
|
test('renders error state', () => {
|
|
23
|
-
mockUseBills.mockReturnValue({
|
|
24
|
+
mockUseBills.mockReturnValue({
|
|
25
|
+
isLoading: false,
|
|
26
|
+
bills: [],
|
|
27
|
+
error: new Error('Internal server error'),
|
|
28
|
+
isValidating: false,
|
|
29
|
+
mutate: jest.fn(),
|
|
30
|
+
});
|
|
24
31
|
renderMetricsCards();
|
|
25
32
|
expect(
|
|
26
33
|
screen.getByText(
|
|
@@ -30,7 +37,13 @@ describe('MetricsCards', () => {
|
|
|
30
37
|
});
|
|
31
38
|
|
|
32
39
|
test('renders metrics cards', () => {
|
|
33
|
-
mockUseBills.mockReturnValue({
|
|
40
|
+
mockUseBills.mockReturnValue({
|
|
41
|
+
isLoading: false,
|
|
42
|
+
bills: billsSummary as unknown as MappedBill[],
|
|
43
|
+
error: null,
|
|
44
|
+
isValidating: false,
|
|
45
|
+
mutate: jest.fn(),
|
|
46
|
+
});
|
|
34
47
|
mockUseConfig.mockImplementation(() => ({ defaultCurrency: 'USD' }));
|
|
35
48
|
renderMetricsCards();
|
|
36
49
|
expect(screen.getByRole('heading', { name: /cumulative bills/i })).toBeInTheDocument();
|
|
@@ -1,16 +1,14 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import
|
|
3
|
-
import '@testing-library/
|
|
2
|
+
import userEvent from '@testing-library/user-event';
|
|
3
|
+
import { render, screen } from '@testing-library/react';
|
|
4
|
+
import { getDefaultsFromConfigSchema, useConfig } from '@openmrs/esm-framework';
|
|
4
5
|
import { useBills } from '../billing.resource';
|
|
5
|
-
import
|
|
6
|
+
import { type MappedBill } from '../types';
|
|
7
|
+
import { configSchema, type BillingConfig } from '../config-schema';
|
|
8
|
+
import RequirePaymentModal from './require-payment.modal';
|
|
6
9
|
|
|
7
|
-
jest.
|
|
8
|
-
|
|
9
|
-
}));
|
|
10
|
-
|
|
11
|
-
jest.mock('@openmrs/esm-framework', () => ({
|
|
12
|
-
useConfig: () => ({ defaultCurrency: 'USD' }),
|
|
13
|
-
}));
|
|
10
|
+
const mockUseConfig = jest.mocked(useConfig<BillingConfig>);
|
|
11
|
+
const mockUseBills = jest.mocked<typeof useBills>(useBills);
|
|
14
12
|
|
|
15
13
|
jest.mock('../billing.resource', () => ({
|
|
16
14
|
useBills: jest.fn(),
|
|
@@ -25,19 +23,19 @@ describe('RequirePaymentModal', () => {
|
|
|
25
23
|
const patientUuid = '12345';
|
|
26
24
|
|
|
27
25
|
beforeEach(() => {
|
|
28
|
-
|
|
26
|
+
mockUseConfig.mockReturnValue({ ...getDefaultsFromConfigSchema(configSchema), defaultCurrency: 'USD' });
|
|
29
27
|
});
|
|
30
28
|
|
|
31
29
|
it('renders correctly', () => {
|
|
32
|
-
|
|
30
|
+
mockUseBills.mockReturnValue({ bills: [], isLoading: false, error: null, isValidating: false, mutate: jest.fn() });
|
|
33
31
|
render(<RequirePaymentModal closeModal={closeModal} patientUuid={patientUuid} />);
|
|
34
|
-
expect(screen.getByText('
|
|
32
|
+
expect(screen.getByText('Patient Billing Alert')).toBeInTheDocument();
|
|
35
33
|
});
|
|
36
34
|
|
|
37
35
|
it('displays loading state', () => {
|
|
38
|
-
|
|
36
|
+
mockUseBills.mockReturnValue({ bills: [], isLoading: true, error: null, isValidating: false, mutate: jest.fn() });
|
|
39
37
|
render(<RequirePaymentModal closeModal={closeModal} patientUuid={patientUuid} />);
|
|
40
|
-
expect(screen.getByText('
|
|
38
|
+
expect(screen.getByText('Loading bill items...')).toBeInTheDocument();
|
|
41
39
|
});
|
|
42
40
|
|
|
43
41
|
it('displays line items', () => {
|
|
@@ -45,22 +43,29 @@ describe('RequirePaymentModal', () => {
|
|
|
45
43
|
{
|
|
46
44
|
status: 'UNPAID',
|
|
47
45
|
lineItems: [
|
|
48
|
-
{ billableService: 'Service 1', quantity: 1, price: 100 },
|
|
49
|
-
{ item: 'Item 1', quantity: 2, price: 50 },
|
|
46
|
+
{ billableService: 'Service 1', quantity: 1, price: 100, uuid: 'billable-service-1' },
|
|
47
|
+
{ item: 'Item 1', quantity: 2, price: 50, uuid: 'billable-item-1' },
|
|
50
48
|
],
|
|
51
49
|
},
|
|
52
50
|
];
|
|
53
|
-
|
|
51
|
+
mockUseBills.mockReturnValue({
|
|
52
|
+
bills: bills as unknown as MappedBill[],
|
|
53
|
+
isLoading: false,
|
|
54
|
+
error: null,
|
|
55
|
+
isValidating: false,
|
|
56
|
+
mutate: jest.fn(),
|
|
57
|
+
});
|
|
54
58
|
render(<RequirePaymentModal closeModal={closeModal} patientUuid={patientUuid} />);
|
|
55
59
|
expect(screen.getByText('Service 1')).toBeInTheDocument();
|
|
56
60
|
expect(screen.getByText('Item 1')).toBeInTheDocument();
|
|
57
61
|
});
|
|
58
62
|
|
|
59
|
-
it('handles closeModal', () => {
|
|
60
|
-
|
|
63
|
+
it('handles closeModal', async () => {
|
|
64
|
+
const user = userEvent.setup();
|
|
65
|
+
mockUseBills.mockReturnValue({ bills: [], isLoading: false, error: null, isValidating: false, mutate: jest.fn() });
|
|
61
66
|
render(<RequirePaymentModal closeModal={closeModal} patientUuid={patientUuid} />);
|
|
62
|
-
|
|
63
|
-
|
|
67
|
+
await user.click(screen.getByText('Cancel'));
|
|
68
|
+
await user.click(screen.getByText('OK'));
|
|
64
69
|
expect(closeModal).toHaveBeenCalledTimes(2);
|
|
65
70
|
});
|
|
66
71
|
});
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
StructuredListRow,
|
|
13
13
|
StructuredListWrapper,
|
|
14
14
|
} from '@carbon/react';
|
|
15
|
-
import { useConfig } from '@openmrs/esm-framework';
|
|
15
|
+
import { getCoreTranslation, useConfig } from '@openmrs/esm-framework';
|
|
16
16
|
import { useBills } from '../billing.resource';
|
|
17
17
|
import { convertToCurrency } from '../helpers';
|
|
18
18
|
import styles from './require-payment.scss';
|
|
@@ -29,22 +29,23 @@ const RequirePaymentModal: React.FC<RequirePaymentModalProps> = ({ closeModal, p
|
|
|
29
29
|
const lineItems = bills.filter((bill) => bill?.status !== 'PAID').flatMap((bill) => bill?.lineItems);
|
|
30
30
|
|
|
31
31
|
return (
|
|
32
|
-
|
|
32
|
+
<>
|
|
33
33
|
<ModalHeader closeModal={closeModal} title={t('patientBillingAlert', 'Patient Billing Alert')} />
|
|
34
34
|
<ModalBody>
|
|
35
35
|
<p className={styles.bodyShort02}>
|
|
36
36
|
{t(
|
|
37
37
|
'billPaymentRequiredMessage',
|
|
38
|
-
'The current patient has pending bill.
|
|
38
|
+
'The current patient has a pending bill. Advise the patient to settle the bill before receiving services',
|
|
39
39
|
)}
|
|
40
40
|
</p>
|
|
41
41
|
{isLoading && (
|
|
42
42
|
<InlineLoading
|
|
43
43
|
status="active"
|
|
44
44
|
iconDescription="Loading"
|
|
45
|
-
description={t('
|
|
45
|
+
description={t('loadingBillItems', 'Loading bill items') + '...'}
|
|
46
46
|
/>
|
|
47
47
|
)}
|
|
48
|
+
|
|
48
49
|
<StructuredListWrapper isCondensed>
|
|
49
50
|
<StructuredListHead>
|
|
50
51
|
<StructuredListRow head>
|
|
@@ -55,30 +56,28 @@ const RequirePaymentModal: React.FC<RequirePaymentModalProps> = ({ closeModal, p
|
|
|
55
56
|
</StructuredListRow>
|
|
56
57
|
</StructuredListHead>
|
|
57
58
|
<StructuredListBody>
|
|
58
|
-
{lineItems.map((lineItem) =>
|
|
59
|
-
|
|
60
|
-
<
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
);
|
|
69
|
-
})}
|
|
59
|
+
{lineItems.map((lineItem) => (
|
|
60
|
+
<StructuredListRow key={lineItem.uuid}>
|
|
61
|
+
<StructuredListCell>{lineItem.billableService || lineItem.item}</StructuredListCell>
|
|
62
|
+
<StructuredListCell>{lineItem.quantity}</StructuredListCell>
|
|
63
|
+
<StructuredListCell>{convertToCurrency(lineItem.price, defaultCurrency)}</StructuredListCell>
|
|
64
|
+
<StructuredListCell>
|
|
65
|
+
{convertToCurrency(lineItem.quantity * lineItem.price, defaultCurrency)}
|
|
66
|
+
</StructuredListCell>
|
|
67
|
+
</StructuredListRow>
|
|
68
|
+
))}
|
|
70
69
|
</StructuredListBody>
|
|
71
70
|
</StructuredListWrapper>
|
|
72
71
|
</ModalBody>
|
|
73
72
|
<ModalFooter>
|
|
74
73
|
<Button kind="secondary" onClick={closeModal}>
|
|
75
|
-
{
|
|
74
|
+
{getCoreTranslation('cancel')}
|
|
76
75
|
</Button>
|
|
77
76
|
<Button kind="primary" onClick={closeModal}>
|
|
78
77
|
{t('ok', 'OK')}
|
|
79
78
|
</Button>
|
|
80
79
|
</ModalFooter>
|
|
81
|
-
|
|
80
|
+
</>
|
|
82
81
|
);
|
|
83
82
|
};
|
|
84
83
|
|
package/src/routes.json
CHANGED
|
@@ -78,15 +78,33 @@
|
|
|
78
78
|
"name": "billing-home-tiles-ext",
|
|
79
79
|
"slot": "billing-home-tiles-slot",
|
|
80
80
|
"component": "serviceMetrics"
|
|
81
|
-
},
|
|
82
|
-
{
|
|
83
|
-
"name": "edit-bill-line-item-dialog",
|
|
84
|
-
"component": "editBillLineItemDialog",
|
|
85
|
-
"online": true,
|
|
86
|
-
"offline": true
|
|
87
81
|
}
|
|
88
82
|
],
|
|
89
83
|
"modals": [
|
|
84
|
+
{
|
|
85
|
+
"name": "add-cash-point-modal",
|
|
86
|
+
"component": "addCashPointModal"
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
"name": "add-payment-mode-modal",
|
|
90
|
+
"component": "addPaymentModeModal"
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
"name": "delete-payment-mode-modal",
|
|
94
|
+
"component": "deletePaymentModeModal"
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
"name": "edit-bill-item-modal",
|
|
98
|
+
"component": "editBillLineItemModal"
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
"name": "edit-bill-line-item-modal",
|
|
102
|
+
"component": "editBillLineItemModal"
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
"name": "edit-billable-service-modal",
|
|
106
|
+
"component": "editBillableServiceModal"
|
|
107
|
+
},
|
|
90
108
|
{
|
|
91
109
|
"name": "require-billing-modal",
|
|
92
110
|
"component": "requirePaymentModal"
|
|
@@ -107,4 +125,4 @@
|
|
|
107
125
|
"description": "This feature introduces navigation links on the patient chart and home page to allow accessing the billing module features"
|
|
108
126
|
}
|
|
109
127
|
]
|
|
110
|
-
}
|
|
128
|
+
}
|