@openmrs/esm-billing-app 1.0.2-pre.98 → 1.0.2-pre.980
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/1537.js +1 -0
- package/dist/1537.js.map +1 -0
- package/dist/1856.js +1 -0
- package/dist/1856.js.map +1 -0
- package/dist/2146.js +1 -1
- package/dist/2524.js +1 -0
- package/dist/2524.js.map +1 -0
- package/dist/2690.js +1 -1
- 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/4300.js +1 -1
- package/dist/4335.js +1 -1
- 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/5442.js +1 -1
- package/dist/5661.js +1 -1
- package/dist/6022.js +1 -1
- package/dist/6468.js +1 -1
- package/dist/6540.js +1 -1
- package/dist/6540.js.map +1 -1
- 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/7255.js +1 -1
- package/dist/7255.js.map +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/8572.js +1 -0
- package/dist/8572.js.map +1 -0
- package/dist/8618.js +1 -1
- package/dist/8708.js +2 -0
- package/dist/{6557.js.LICENSE.txt → 8708.js.LICENSE.txt} +22 -0
- package/dist/8708.js.map +1 -0
- 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 +271 -285
- 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 +18 -15
- package/src/bill-history/bill-history.component.tsx +20 -28
- 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 +226 -0
- package/src/bill-item-actions/edit-bill-item.test.tsx +233 -40
- package/src/billable-services/bill-waiver/bill-selection.component.tsx +5 -5
- package/src/billable-services/bill-waiver/bill-waiver-form.component.tsx +34 -37
- package/src/billable-services/bill-waiver/patient-bills.component.tsx +7 -7
- package/src/billable-services/bill-waiver/utils.ts +13 -3
- package/src/billable-services/{create-edit/add-billable-service.scss → billable-service-form/billable-service-form.scss} +32 -64
- package/src/billable-services/billable-service-form/billable-service-form.test.tsx +1048 -0
- package/src/billable-services/billable-service-form/billable-service-form.workspace.tsx +515 -0
- package/src/billable-services/billable-service.resource.ts +71 -27
- package/src/billable-services/billable-services-home.component.tsx +13 -42
- package/src/billable-services/billable-services-left-panel-link.component.tsx +48 -0
- package/src/billable-services/billable-services-left-panel-menu.component.tsx +46 -0
- package/src/billable-services/billable-services-menu-item/item.component.tsx +5 -4
- package/src/billable-services/billable-services.component.tsx +156 -152
- package/src/billable-services/billable-services.scss +29 -0
- package/src/billable-services/billable-services.test.tsx +6 -49
- package/src/billable-services/cash-point/add-cash-point.modal.tsx +170 -0
- package/src/billable-services/cash-point/cash-point-configuration.component.tsx +19 -193
- package/src/billable-services/cash-point/cash-point-configuration.scss +1 -5
- package/src/billable-services/dashboard/dashboard.component.tsx +0 -2
- package/src/billable-services/payment-modes/delete-payment-mode.modal.tsx +77 -0
- package/src/billable-services/payment-modes/payment-mode-form.modal.tsx +131 -0
- package/src/billable-services/payment-modes/payment-modes-config.component.tsx +139 -0
- package/src/billable-services/{payyment-modes → payment-modes}/payment-modes-config.scss +5 -4
- package/src/billable-services-admin-card-link.component.test.tsx +2 -2
- package/src/billable-services-admin-card-link.component.tsx +1 -1
- package/src/billing-dashboard/billing-dashboard.scss +1 -1
- package/src/billing-form/billing-checkin-form.component.tsx +21 -17
- package/src/billing-form/billing-checkin-form.test.tsx +99 -26
- package/src/billing-form/billing-form.component.tsx +226 -289
- package/src/billing-form/billing-form.scss +143 -0
- package/src/billing-form/visit-attributes/visit-attributes-form.component.tsx +1 -1
- package/src/billing.resource.ts +69 -74
- package/src/bills-table/bills-table.component.tsx +3 -3
- 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 +71 -9
- package/src/invoice/invoice-table.component.tsx +36 -70
- package/src/invoice/invoice-table.scss +8 -5
- package/src/invoice/invoice-table.test.tsx +273 -62
- package/src/invoice/invoice.component.tsx +39 -33
- 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 +55 -67
- 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 -3
- 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 +20 -11
- package/src/invoice/printable-invoice/printable-invoice-header.test.tsx +95 -16
- package/src/invoice/printable-invoice/printable-invoice.component.tsx +21 -35
- package/src/left-panel-link.test.tsx +1 -4
- package/src/metrics-cards/metrics-cards.component.tsx +16 -6
- package/src/metrics-cards/metrics-cards.scss +4 -0
- 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} +18 -19
- package/src/routes.json +44 -20
- package/src/types/index.ts +87 -24
- package/translations/am.json +135 -78
- package/translations/ar.json +136 -79
- package/translations/ar_SY.json +136 -79
- package/translations/bn.json +138 -81
- package/translations/de.json +136 -79
- package/translations/en.json +136 -79
- package/translations/en_US.json +136 -79
- package/translations/es.json +135 -78
- package/translations/es_MX.json +136 -79
- package/translations/fr.json +141 -84
- package/translations/he.json +135 -78
- package/translations/hi.json +136 -79
- package/translations/hi_IN.json +136 -79
- package/translations/id.json +136 -79
- package/translations/it.json +162 -105
- package/translations/ka.json +136 -79
- package/translations/km.json +135 -78
- package/translations/ku.json +136 -79
- package/translations/ky.json +136 -79
- package/translations/lg.json +136 -79
- package/translations/ne.json +136 -79
- package/translations/pl.json +136 -79
- package/translations/pt.json +136 -79
- package/translations/pt_BR.json +136 -79
- package/translations/qu.json +136 -79
- package/translations/ro_RO.json +222 -165
- package/translations/ru_RU.json +136 -79
- package/translations/si.json +136 -79
- package/translations/sw.json +136 -79
- package/translations/sw_KE.json +136 -79
- package/translations/tr.json +136 -79
- package/translations/tr_TR.json +136 -79
- package/translations/uk.json +136 -79
- package/translations/uz.json +136 -79
- package/translations/uz@Latn.json +136 -79
- package/translations/uz_UZ.json +136 -79
- package/translations/vi.json +136 -79
- package/translations/zh.json +136 -79
- package/translations/zh_CN.json +166 -109
- 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/4689.js +0 -2
- package/dist/4689.js.map +0 -1
- package/dist/6557.js +0 -2
- package/dist/6557.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/create-edit/add-billable-service.component.tsx +0 -401
- package/src/billable-services/create-edit/add-billable-service.test.tsx +0 -154
- package/src/billable-services/dashboard/service-metrics.component.tsx +0 -41
- 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/{4689.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, formatDate, parseDate } 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
|
|
@@ -44,6 +45,14 @@ const PrintableInvoiceHeader: React.FC<PrintableInvoiceHeaderProps> = ({ patient
|
|
|
44
45
|
<div className={styles.billDetails}>
|
|
45
46
|
<p className={styles.itemHeading}>{t('billedTo', 'Billed to')}</p>
|
|
46
47
|
<p className={styles.itemLabel}>{patientDetails?.name}</p>
|
|
48
|
+
{patientDetails?.gender && (
|
|
49
|
+
<p className={styles.itemLabel}>{`${t('gender', 'Gender')}: ${patientDetails.gender}`}</p>
|
|
50
|
+
)}
|
|
51
|
+
{patientDetails?.birthDate && (
|
|
52
|
+
<p className={styles.itemLabel}>
|
|
53
|
+
{`${t('birthDate', 'Date of birth')}: ${formatDate(parseDate(patientDetails.birthDate), { time: false })}`}
|
|
54
|
+
</p>
|
|
55
|
+
)}
|
|
47
56
|
<p className={styles.itemLabel}>{patientDetails?.county}</p>
|
|
48
57
|
<p className={styles.itemLabel}>
|
|
49
58
|
{patientDetails?.subCounty}
|
|
@@ -52,8 +61,8 @@ const PrintableInvoiceHeader: React.FC<PrintableInvoiceHeaderProps> = ({ patient
|
|
|
52
61
|
</div>
|
|
53
62
|
|
|
54
63
|
<div className={styles.facilityDetails}>
|
|
55
|
-
<p className={styles.facilityName}>{
|
|
56
|
-
<p className={styles.itemLabel}>
|
|
64
|
+
<p className={styles.facilityName}>{defaultFacility?.display}</p>
|
|
65
|
+
<p className={styles.itemLabel}>{country}</p>
|
|
57
66
|
</div>
|
|
58
67
|
</div>
|
|
59
68
|
</div>
|
|
@@ -1,35 +1,41 @@
|
|
|
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',
|
|
20
18
|
county: 'Nairobi',
|
|
21
19
|
subCounty: 'Westlands',
|
|
22
20
|
city: 'Nairobi',
|
|
23
|
-
|
|
21
|
+
birthDate: '1980-05-15',
|
|
24
22
|
gender: 'Male',
|
|
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
|
|
|
@@ -38,21 +44,94 @@ describe('PrintableInvoiceHeader', () => {
|
|
|
38
44
|
expect(screen.getByText('Westlands, Nairobi')).toBeInTheDocument();
|
|
39
45
|
expect(screen.getByText('MTRH')).toBeInTheDocument();
|
|
40
46
|
expect(screen.getByText('Kenya')).toBeInTheDocument();
|
|
47
|
+
expect(screen.getByText('Gender: Male')).toBeInTheDocument();
|
|
48
|
+
expect(screen.getByText('Date of birth: 15-May-1980')).toBeInTheDocument();
|
|
41
49
|
});
|
|
42
50
|
|
|
43
51
|
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} />);
|
|
52
|
+
render(<PrintableInvoiceHeader {...testProps} defaultFacility={defaultFacility} />);
|
|
47
53
|
const logo = screen.getByAltText('logo');
|
|
48
54
|
expect(logo).toBeInTheDocument();
|
|
49
55
|
});
|
|
50
56
|
|
|
51
|
-
test('should display the default logo when logo is not provided', () => {
|
|
52
|
-
mockUseConfig.mockReturnValue({
|
|
53
|
-
|
|
54
|
-
|
|
57
|
+
test('should display the default OpenMRS SVG logo when logo src is not provided', () => {
|
|
58
|
+
mockUseConfig.mockReturnValue({
|
|
59
|
+
logo: { src: '', alt: '' },
|
|
60
|
+
country: 'Kenya',
|
|
61
|
+
} as unknown as BillingConfig);
|
|
62
|
+
|
|
63
|
+
render(<PrintableInvoiceHeader {...testProps} defaultFacility={defaultFacility} />);
|
|
55
64
|
const logo = screen.getByRole('img');
|
|
56
65
|
expect(logo).toBeInTheDocument();
|
|
66
|
+
expect(logo.tagName).toBe('svg');
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
test('should display logo alt text when src is empty but alt is provided', () => {
|
|
70
|
+
mockUseConfig.mockReturnValue({
|
|
71
|
+
logo: { src: '', alt: 'Test Facility Logo' },
|
|
72
|
+
country: 'Kenya',
|
|
73
|
+
} as unknown as BillingConfig);
|
|
74
|
+
|
|
75
|
+
render(<PrintableInvoiceHeader {...testProps} defaultFacility={defaultFacility} />);
|
|
76
|
+
expect(screen.getByText('Test Facility Logo')).toBeInTheDocument();
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
test('should format birthDate correctly', () => {
|
|
80
|
+
const propsWithDifferentDate = {
|
|
81
|
+
patientDetails: {
|
|
82
|
+
name: 'Jane Doe',
|
|
83
|
+
county: 'Mombasa',
|
|
84
|
+
subCounty: 'Nyali',
|
|
85
|
+
city: 'Mombasa',
|
|
86
|
+
birthDate: '1995-12-25',
|
|
87
|
+
gender: 'Female',
|
|
88
|
+
},
|
|
89
|
+
};
|
|
90
|
+
render(<PrintableInvoiceHeader {...propsWithDifferentDate} defaultFacility={defaultFacility} />);
|
|
91
|
+
expect(screen.getByText('Date of birth: 25-Dec-1995')).toBeInTheDocument();
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
test('should not render birthDate and gender when not provided', () => {
|
|
95
|
+
const propsWithoutBirthDateAndGender = {
|
|
96
|
+
patientDetails: {
|
|
97
|
+
name: 'Jane Doe',
|
|
98
|
+
county: 'Mombasa',
|
|
99
|
+
subCounty: 'Nyali',
|
|
100
|
+
city: 'Mombasa',
|
|
101
|
+
birthDate: '',
|
|
102
|
+
gender: '',
|
|
103
|
+
},
|
|
104
|
+
};
|
|
105
|
+
render(<PrintableInvoiceHeader {...propsWithoutBirthDateAndGender} defaultFacility={defaultFacility} />);
|
|
106
|
+
|
|
107
|
+
expect(screen.queryByText(/Date of birth:/i)).not.toBeInTheDocument();
|
|
108
|
+
expect(screen.queryByText(/Gender:/i)).not.toBeInTheDocument();
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
test('should render city with comma when provided', () => {
|
|
112
|
+
render(<PrintableInvoiceHeader {...testProps} defaultFacility={defaultFacility} />);
|
|
113
|
+
expect(screen.getByText('Westlands, Nairobi')).toBeInTheDocument();
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
test('should render subCounty without comma when city is not provided', () => {
|
|
117
|
+
const propsWithoutCity = {
|
|
118
|
+
patientDetails: {
|
|
119
|
+
name: 'Jane Doe',
|
|
120
|
+
county: 'Mombasa',
|
|
121
|
+
subCounty: 'Nyali',
|
|
122
|
+
city: '',
|
|
123
|
+
birthDate: '1990-01-01',
|
|
124
|
+
gender: 'Female',
|
|
125
|
+
},
|
|
126
|
+
};
|
|
127
|
+
render(<PrintableInvoiceHeader {...propsWithoutCity} defaultFacility={defaultFacility} />);
|
|
128
|
+
expect(screen.getByText('Nyali')).toBeInTheDocument();
|
|
129
|
+
expect(screen.queryByText(/Nyali,/)).not.toBeInTheDocument();
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
test('should handle null defaultFacility gracefully', () => {
|
|
133
|
+
render(<PrintableInvoiceHeader {...testProps} defaultFacility={null} />);
|
|
134
|
+
expect(screen.getByText('Invoice')).toBeInTheDocument();
|
|
135
|
+
expect(screen.getByText('John Doe')).toBeInTheDocument();
|
|
57
136
|
});
|
|
58
137
|
});
|
|
@@ -8,9 +8,8 @@ import {
|
|
|
8
8
|
TableBody,
|
|
9
9
|
TableHeader,
|
|
10
10
|
TableCell,
|
|
11
|
-
DataTableSkeleton,
|
|
12
11
|
} from '@carbon/react';
|
|
13
|
-
import {
|
|
12
|
+
import { 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,16 +47,16 @@ const PrintableInvoice: React.FC<PrintableInvoiceProps> = ({ bill, patient, isLo
|
|
|
47
47
|
}) ?? [];
|
|
48
48
|
|
|
49
49
|
const invoiceTotal = {
|
|
50
|
-
'Total
|
|
51
|
-
'Amount
|
|
52
|
-
'Discount
|
|
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(() => {
|
|
57
57
|
return {
|
|
58
58
|
name: `${patient?.name?.[0]?.given?.join(' ')} ${patient?.name?.[0].family}`,
|
|
59
|
-
|
|
59
|
+
birthDate: patient?.birthDate,
|
|
60
60
|
gender: getGender(patient?.gender, t),
|
|
61
61
|
city: patient?.address?.[0].city,
|
|
62
62
|
county: patient?.address?.[0].district,
|
|
@@ -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,10 +85,10 @@ 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
|
-
<Table {...getTableProps()} aria-label=
|
|
91
|
+
<Table {...getTableProps()} aria-label={t('invoiceLineItems', 'Invoice line items')}>
|
|
106
92
|
<TableHead>
|
|
107
93
|
<TableRow>
|
|
108
94
|
{headers.map((header) => (
|
|
@@ -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
|
});
|
|
@@ -2,6 +2,7 @@ import React, { useMemo } from 'react';
|
|
|
2
2
|
import { InlineLoading } from '@carbon/react';
|
|
3
3
|
import { useTranslation } from 'react-i18next';
|
|
4
4
|
import { ErrorState } from '@openmrs/esm-patient-common-lib';
|
|
5
|
+
import { getCoreTranslation } from '@openmrs/esm-framework';
|
|
5
6
|
import { useBills } from '../billing.resource';
|
|
6
7
|
import { useBillMetrics } from './metrics.resource';
|
|
7
8
|
import Card from './card.component';
|
|
@@ -14,24 +15,33 @@ export default function MetricsCards() {
|
|
|
14
15
|
|
|
15
16
|
const cards = useMemo(
|
|
16
17
|
() => [
|
|
17
|
-
{ title: 'Cumulative
|
|
18
|
-
{ title: 'Pending
|
|
19
|
-
{ title: 'Paid
|
|
18
|
+
{ title: t('cumulativeBills', 'Cumulative bills'), count: cumulativeBills },
|
|
19
|
+
{ title: t('pendingBills', 'Pending bills'), count: pendingBills },
|
|
20
|
+
{ title: t('paidBills', 'Paid bills'), count: paidBills },
|
|
20
21
|
],
|
|
21
|
-
[cumulativeBills, pendingBills, paidBills],
|
|
22
|
+
[cumulativeBills, pendingBills, paidBills, t],
|
|
22
23
|
);
|
|
23
24
|
|
|
24
25
|
if (isLoading) {
|
|
25
26
|
return (
|
|
26
27
|
<section className={styles.container}>
|
|
27
|
-
<InlineLoading
|
|
28
|
+
<InlineLoading
|
|
29
|
+
status="active"
|
|
30
|
+
iconDescription={getCoreTranslation('loading')}
|
|
31
|
+
description={t('loadingBillMetrics', 'Loading bill metrics') + '...'}
|
|
32
|
+
/>
|
|
28
33
|
</section>
|
|
29
34
|
);
|
|
30
35
|
}
|
|
31
36
|
|
|
32
37
|
if (error) {
|
|
33
|
-
return
|
|
38
|
+
return (
|
|
39
|
+
<div className={styles.errorContainer}>
|
|
40
|
+
<ErrorState headerTitle={t('billMetrics', 'Bill metrics')} error={error} />
|
|
41
|
+
</div>
|
|
42
|
+
);
|
|
34
43
|
}
|
|
44
|
+
|
|
35
45
|
return (
|
|
36
46
|
<section className={styles.container}>
|
|
37
47
|
{cards.map((card) => (
|
|
@@ -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
|
-
iconDescription=
|
|
45
|
-
description={t('
|
|
44
|
+
iconDescription={getCoreTranslation('loading')}
|
|
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
|
|