@kenyaemr/esm-billing-app 5.4.2-pre.2259 → 5.4.2-pre.2265
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +16 -16
- package/dist/300.js +1 -1
- package/dist/632.js +1 -0
- package/dist/632.js.map +1 -0
- package/dist/{46.js → 910.js} +3 -3
- package/dist/910.js.map +1 -0
- package/dist/kenyaemr-esm-billing-app.js +1 -1
- package/dist/kenyaemr-esm-billing-app.js.buildmanifest.json +712 -712
- package/dist/kenyaemr-esm-billing-app.js.map +1 -1
- package/dist/main.js +3 -3
- package/dist/main.js.map +1 -1
- package/dist/routes.json +1 -1
- package/package.json +1 -1
- package/src/{invoice/print-bill-receipt/print-bill-receipt.resource.ts → hooks/usePrintPreview.tsx} +2 -4
- package/src/index.ts +7 -2
- package/src/invoice/invoice.component.tsx +13 -34
- package/src/invoice/print-bill-receipt/receipt-print-button.component.tsx +5 -3
- package/src/payment-modes/payment-mode-dashboard.compont.tsx +2 -2
- package/src/payment-modes/payment-mode.workspace.test.tsx +57 -1
- package/src/payment-modes/payment-mode.workspace.tsx +9 -3
- package/src/payment-modes/usePaymentModeFormSchema.tsx +1 -1
- package/src/print-preview/print-preview.modal.tsx +45 -0
- package/src/routes.json +4 -4
- package/translations/en.json +1 -3
- package/dist/46.js.map +0 -1
- package/dist/964.js +0 -1
- package/dist/964.js.map +0 -1
- package/src/invoice/print-bill-receipt/receipt-print-preview.modal.tsx +0 -42
- package/src/invoice/printable-invoice/print-invoice.test.tsx +0 -36
- package/src/invoice/printable-invoice/print-receipt.component.tsx +0 -28
- package/src/invoice/printable-invoice/print-receipt.scss +0 -10
- package/src/invoice/printable-invoice/printable-footer.component.tsx +0 -33
- package/src/invoice/printable-invoice/printable-footer.scss +0 -24
- package/src/invoice/printable-invoice/printable-footer.test.tsx +0 -19
- package/src/invoice/printable-invoice/printable-invoice-header.component.tsx +0 -63
- package/src/invoice/printable-invoice/printable-invoice-header.scss +0 -61
- package/src/invoice/printable-invoice/printable-invoice-header.test.tsx +0 -56
- package/src/invoice/printable-invoice/printable-invoice.component.tsx +0 -134
- package/src/invoice/printable-invoice/printable-invoice.scss +0 -60
- /package/dist/{46.js.LICENSE.txt → 910.js.LICENSE.txt} +0 -0
- /package/src/{invoice/print-bill-receipt/print-bill-receipt.scss → print-preview/print-preview.scss} +0 -0
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import { Button, InlineLoading, ModalBody, ModalFooter, ModalHeader } from '@carbon/react';
|
|
2
|
-
import { ErrorState } from '@openmrs/esm-framework';
|
|
3
|
-
import React from 'react';
|
|
4
|
-
import { useTranslation } from 'react-i18next';
|
|
5
|
-
import { MappedBill } from '../../types';
|
|
6
|
-
import { usePaidBillReceiptFileObjectUrl } from './print-bill-receipt.resource';
|
|
7
|
-
import styles from './print-bill-receipt.scss';
|
|
8
|
-
|
|
9
|
-
type ReceiptPrintPreviewModalProps = {
|
|
10
|
-
onClose: () => void;
|
|
11
|
-
bill: MappedBill;
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
const ReceiptPrintPreviewModal: React.FC<ReceiptPrintPreviewModalProps> = ({ onClose, bill }) => {
|
|
15
|
-
const { t } = useTranslation();
|
|
16
|
-
const { mutate, isLoading, url, error } = usePaidBillReceiptFileObjectUrl(bill.id);
|
|
17
|
-
return (
|
|
18
|
-
<>
|
|
19
|
-
<ModalHeader closeModal={onClose} className={styles.title}>
|
|
20
|
-
{t('printReceipt', 'Print Receipt')}
|
|
21
|
-
</ModalHeader>
|
|
22
|
-
<ModalBody>
|
|
23
|
-
{isLoading && (
|
|
24
|
-
<InlineLoading
|
|
25
|
-
status="active"
|
|
26
|
-
iconDescription="Loading"
|
|
27
|
-
description={t('loadingReceipt', 'Loading Receipt')}
|
|
28
|
-
/>
|
|
29
|
-
)}
|
|
30
|
-
{error && <ErrorState error={error} headerTitle={t('previewError', 'Preview Error')} />}
|
|
31
|
-
{url && !isLoading && <iframe src={String(url)} title="Receipt Preview" className={styles.previewFrame} />}
|
|
32
|
-
</ModalBody>
|
|
33
|
-
<ModalFooter>
|
|
34
|
-
<Button kind="secondary" onClick={onClose} type="button" className={styles.btn}>
|
|
35
|
-
{t('close', 'Close')}
|
|
36
|
-
</Button>
|
|
37
|
-
</ModalFooter>
|
|
38
|
-
</>
|
|
39
|
-
);
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
export default ReceiptPrintPreviewModal;
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { screen, render } from '@testing-library/react';
|
|
3
|
-
import PrintableInvoice from './printable-invoice.component';
|
|
4
|
-
import { mockBills } from '../../../../../__mocks__/bills.mock';
|
|
5
|
-
import { mockPatient } from '../../../../../__mocks__/patient.mock';
|
|
6
|
-
import { extractString } from '../../helpers';
|
|
7
|
-
|
|
8
|
-
describe('PrintableInvoice', () => {
|
|
9
|
-
test('should render PrintableInvoice', () => {
|
|
10
|
-
render(
|
|
11
|
-
<PrintableInvoice
|
|
12
|
-
bill={{ ...mockBills, dateCreated: '2021-01-18T09:42:40+00:00', tenderedAmount: 650 }}
|
|
13
|
-
patient={mockPatient}
|
|
14
|
-
isPrinting={false}
|
|
15
|
-
facilityInfo={{ display: 'facility test' }}
|
|
16
|
-
/>,
|
|
17
|
-
);
|
|
18
|
-
expect(screen.getByText('Invoice')).toBeInTheDocument();
|
|
19
|
-
expect(screen.getByText('Billed to')).toBeInTheDocument();
|
|
20
|
-
const lineItems = mockBills.lineItems;
|
|
21
|
-
lineItems.forEach((item, index) => {
|
|
22
|
-
expect(screen.getByText(`${index + 1} - ${extractString(item.billableService)}`)).toBeInTheDocument();
|
|
23
|
-
});
|
|
24
|
-
expect(screen.getAllByText('facility test')).toHaveLength(2);
|
|
25
|
-
expect(screen.getByText('Total')).toBeInTheDocument();
|
|
26
|
-
expect(
|
|
27
|
-
screen.getByText(
|
|
28
|
-
/The invoice has been electronically generated and is a valid document. It was created by {{userName}} on {{date}} at {{time}}/,
|
|
29
|
-
),
|
|
30
|
-
).toBeInTheDocument();
|
|
31
|
-
expect(screen.getByText('Invoice #')).toBeInTheDocument();
|
|
32
|
-
expect(screen.getByText('Invoice date')).toBeInTheDocument();
|
|
33
|
-
expect(screen.getByText('Status')).toBeInTheDocument();
|
|
34
|
-
expect(screen.getByText(/Pending/i)).toBeInTheDocument();
|
|
35
|
-
});
|
|
36
|
-
});
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import { ConfigurableLink } from '@openmrs/esm-framework';
|
|
2
|
-
import React from 'react';
|
|
3
|
-
import { useTranslation } from 'react-i18next';
|
|
4
|
-
import styles from './print-receipt.scss';
|
|
5
|
-
import { Button } from '@carbon/react';
|
|
6
|
-
import { Printer } from '@carbon/react/icons';
|
|
7
|
-
|
|
8
|
-
interface PrintReceiptProps {
|
|
9
|
-
billId: number;
|
|
10
|
-
}
|
|
11
|
-
const PrintReceipt: React.FC<PrintReceiptProps> = ({ billId }) => {
|
|
12
|
-
const { t } = useTranslation();
|
|
13
|
-
return (
|
|
14
|
-
<Button
|
|
15
|
-
kind="secondary"
|
|
16
|
-
className={styles.button}
|
|
17
|
-
size="md"
|
|
18
|
-
renderIcon={(props) => <Printer size={24} {...props} />}>
|
|
19
|
-
<ConfigurableLink
|
|
20
|
-
className={styles.configurableLink}
|
|
21
|
-
to={`\${openmrsBase}/ws/rest/v1/cashier/receipt?billId=${billId}`}>
|
|
22
|
-
{t('printReceipt', 'Print receipt')}
|
|
23
|
-
</ConfigurableLink>{' '}
|
|
24
|
-
</Button>
|
|
25
|
-
);
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
export default PrintReceipt;
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import styles from './printable-footer.scss';
|
|
3
|
-
import { useTranslation } from 'react-i18next';
|
|
4
|
-
import dayjs from 'dayjs';
|
|
5
|
-
import { useSession } from '@openmrs/esm-framework';
|
|
6
|
-
|
|
7
|
-
type PrintableFooterProps = {
|
|
8
|
-
facilityInfo: Record<string, any>;
|
|
9
|
-
};
|
|
10
|
-
|
|
11
|
-
const PrintableFooter: React.FC<PrintableFooterProps> = ({ facilityInfo }) => {
|
|
12
|
-
const { t } = useTranslation();
|
|
13
|
-
const session = useSession();
|
|
14
|
-
|
|
15
|
-
return (
|
|
16
|
-
<div className={styles.container}>
|
|
17
|
-
<p className={styles.itemFooter}>{facilityInfo?.display}</p>
|
|
18
|
-
<p className={styles.footDescription}>
|
|
19
|
-
{t(
|
|
20
|
-
'generatedMessage',
|
|
21
|
-
'The invoice has been electronically generated and is a valid document. It was created by {{userName}} on {{date}} at {{time}}',
|
|
22
|
-
{
|
|
23
|
-
userName: `${session?.user?.display}`,
|
|
24
|
-
date: dayjs().format('DD-MM-YYYY'),
|
|
25
|
-
time: dayjs().format('hh:mm A'),
|
|
26
|
-
},
|
|
27
|
-
)}
|
|
28
|
-
</p>
|
|
29
|
-
</div>
|
|
30
|
-
);
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
export default PrintableFooter;
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
@use '@carbon/colors';
|
|
2
|
-
@use '@carbon/layout';
|
|
3
|
-
@use '@carbon/type';
|
|
4
|
-
|
|
5
|
-
.container {
|
|
6
|
-
display: flex;
|
|
7
|
-
flex-direction: column;
|
|
8
|
-
width: 100%;
|
|
9
|
-
bottom: 0;
|
|
10
|
-
padding: layout.$spacing-01;
|
|
11
|
-
justify-content: center;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
.itemFooter {
|
|
15
|
-
padding: layout.$spacing-01 layout.$spacing-05;
|
|
16
|
-
@include type.type-style('body-compact-02');
|
|
17
|
-
color: colors.$cool-gray-90;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
.footDescription {
|
|
21
|
-
@include type.type-style('legal-01');
|
|
22
|
-
padding: layout.$spacing-01 layout.$spacing-05;
|
|
23
|
-
color: colors.$cool-gray-70;
|
|
24
|
-
}
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { screen, render } from '@testing-library/react';
|
|
3
|
-
import PrintableFooter from './printable-footer.component';
|
|
4
|
-
|
|
5
|
-
jest.mock('../../billing.resource', () => ({
|
|
6
|
-
useDefaultFacility: jest.fn(),
|
|
7
|
-
}));
|
|
8
|
-
|
|
9
|
-
describe('PrintableFooter', () => {
|
|
10
|
-
beforeEach(() => {
|
|
11
|
-
jest.clearAllMocks();
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
test('should render PrintableFooter component', () => {
|
|
15
|
-
render(<PrintableFooter facilityInfo={{ display: 'MTRH', uuid: 'mtrh-uuid' }} />);
|
|
16
|
-
const footer = screen.getByText('MTRH');
|
|
17
|
-
expect(footer).toBeInTheDocument();
|
|
18
|
-
});
|
|
19
|
-
});
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { PatientDetails } from '../../types';
|
|
3
|
-
import styles from './printable-invoice-header.scss';
|
|
4
|
-
import { useConfig } from '@openmrs/esm-framework';
|
|
5
|
-
import { useTranslation } from 'react-i18next';
|
|
6
|
-
import startCase from 'lodash-es/startCase';
|
|
7
|
-
|
|
8
|
-
interface PrintableInvoiceHeaderProps {
|
|
9
|
-
patientDetails: PatientDetails;
|
|
10
|
-
facilityInfo: Record<string, any>;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
const PrintableInvoiceHeader: React.FC<PrintableInvoiceHeaderProps> = ({ patientDetails, facilityInfo }) => {
|
|
14
|
-
const { t } = useTranslation();
|
|
15
|
-
const { logo } = useConfig({ externalModuleName: '@kenyaemr/esm-login-app' });
|
|
16
|
-
|
|
17
|
-
return (
|
|
18
|
-
<div className={styles.container}>
|
|
19
|
-
<div className={styles.printableHeader}>
|
|
20
|
-
<p className={styles.heading}>{t('invoice', 'Invoice')}</p>
|
|
21
|
-
{logo?.src ? (
|
|
22
|
-
<img className={styles.img} height={60} width={250} src={logo.src} alt={logo.alt} />
|
|
23
|
-
) : logo?.name ? (
|
|
24
|
-
logo.name
|
|
25
|
-
) : (
|
|
26
|
-
// OpenMRS Logo
|
|
27
|
-
<svg
|
|
28
|
-
className={styles.img}
|
|
29
|
-
role="img"
|
|
30
|
-
width={110}
|
|
31
|
-
height={40}
|
|
32
|
-
viewBox="0 0 380 119"
|
|
33
|
-
xmlns="http://www.w3.org/2000/svg">
|
|
34
|
-
<path
|
|
35
|
-
fillRule="evenodd"
|
|
36
|
-
clipRule="evenodd"
|
|
37
|
-
d="M40.29 40.328a27.755 27.755 0 0 1 19.688-8.154c7.669 0 14.613 3.102 19.647 8.116l.02-18.54A42.835 42.835 0 0 0 59.978 17c-7.089 0-13.813 1.93-19.709 4.968l.021 18.36ZM79.645 79.671a27.744 27.744 0 0 1-19.684 8.154c-7.67 0-14.614-3.101-19.651-8.116l-.02 18.54A42.857 42.857 0 0 0 59.96 103a42.833 42.833 0 0 0 19.672-4.751l.013-18.578ZM40.328 79.696c-5.038-5.037-8.154-11.995-8.154-19.685 0-7.669 3.102-14.612 8.116-19.65l-18.54-.02A42.85 42.85 0 0 0 17 60.012a42.819 42.819 0 0 0 4.752 19.672l18.576.013ZM79.634 40.289a27.753 27.753 0 0 1 8.154 19.688 27.744 27.744 0 0 1-8.117 19.646l18.542.02a42.842 42.842 0 0 0 4.749-19.666c0-7.09-1.714-13.779-4.751-19.675l-18.577-.013ZM156.184 60.002c0-8.748-6.118-15.776-15.025-15.776-8.909 0-15.025 7.028-15.025 15.776 0 8.749 6.116 15.78 15.025 15.78 8.907 0 15.025-7.031 15.025-15.78Zm-34.881 0c0-11.482 8.318-19.958 19.856-19.958 11.536 0 19.855 8.477 19.855 19.959 0 11.484-8.319 19.964-19.855 19.964-11.538 0-19.856-8.48-19.856-19.965ZM179.514 75.54c5.507 0 9.05-4.14 9.05-9.482 0-5.341-3.543-9.483-9.05-9.483-5.505 0-9.046 4.142-9.046 9.483 0 5.342 3.541 9.482 9.046 9.482ZM166.22 53.306h4.248v3.704h.11c2.344-2.725 5.449-4.36 9.154-4.36 8.014 0 13.408 5.67 13.408 13.408 0 7.63-5.613 13.406-12.752 13.406-4.58 0-8.231-2.29-9.81-5.178h-.11V90.87h-4.248V53.306ZM217.773 63.768c-.163-4.305-3-7.193-7.686-7.193-4.685 0-7.79 2.888-8.335 7.193h16.021Zm3.653 10.412c-3.001 3.868-6.596 5.284-11.339 5.284-8.01 0-12.914-5.993-12.914-13.406 0-7.901 5.559-13.407 13.08-13.407 7.196 0 12.096 4.906 12.096 13.354v1.362h-20.597c.325 4.413 3.704 8.173 8.335 8.173 3.65 0 6.105-1.307 8.12-3.868l3.219 2.508ZM227.854 59.356c0-2.346-.216-4.36-.216-6.05h4.031c0 1.363.11 2.777.11 4.195h.11c1.144-2.505 4.306-4.85 8.5-4.85 6.705 0 9.699 4.252 9.699 10.41v15.748h-4.248v-15.31c0-4.253-1.856-6.924-5.833-6.924-5.503 0-7.903 3.979-7.903 9.811V78.81h-4.25V59.356ZM259.211 41.008h6.708L278.8 70.791h.107l12.982-29.782h6.549v37.99h-4.506V47.124h-.106L280.192 79h-2.738l-13.629-31.875h-.107V79h-4.507V41.01ZM312.392 57.752h4.023c4.992 0 11.487 0 11.487-6.282 0-5.47-4.776-6.276-9.177-6.276h-6.333v12.558Zm-4.506-16.744h9.711c7.352 0 15.132 1.072 15.132 10.462 0 5.527-3.594 9.125-9.495 10.037L334.018 79h-5.525l-10.304-17.063h-5.797V79h-4.506V41.01ZM358.123 47.712c-1.506-2.413-4.187-3.486-6.926-3.486-3.973 0-8.1 1.88-8.1 6.385 0 3.49 1.931 5.047 7.994 6.98 5.903 1.878 11.377 3.809 11.377 11.267 0 7.567-6.495 11.11-13.36 11.11-4.402 0-9.125-1.45-11.7-5.262l3.862-3.165c1.61 2.794 4.83 4.24 8.105 4.24 3.862 0 8.263-2.253 8.263-6.601 0-4.669-3.165-5.474-9.928-7.728-5.366-1.771-9.442-4.134-9.442-10.463 0-7.298 6.277-10.945 12.929-10.945 4.241 0 7.836 1.178 10.625 4.45l-3.699 3.218Z"
|
|
38
|
-
/>
|
|
39
|
-
</svg>
|
|
40
|
-
)}
|
|
41
|
-
</div>
|
|
42
|
-
|
|
43
|
-
<div className={styles.printableBody}>
|
|
44
|
-
<div className={styles.billDetails}>
|
|
45
|
-
<p className={styles.itemHeading}>{t('billedTo', 'Billed to')}</p>
|
|
46
|
-
<p className={styles.itemLabel}>{startCase(patientDetails?.name)}</p>
|
|
47
|
-
<p className={styles.itemLabel}>{patientDetails?.county}</p>
|
|
48
|
-
<p className={styles.itemLabel}>
|
|
49
|
-
{patientDetails?.subCounty}
|
|
50
|
-
{patientDetails?.city ? `, ${patientDetails?.city}` : null}
|
|
51
|
-
</p>
|
|
52
|
-
</div>
|
|
53
|
-
|
|
54
|
-
<div className={styles.facilityDetails}>
|
|
55
|
-
<p className={styles.facilityName}>{facilityInfo?.display}</p>
|
|
56
|
-
<p className={styles.itemLabel}>Kenya</p>
|
|
57
|
-
</div>
|
|
58
|
-
</div>
|
|
59
|
-
</div>
|
|
60
|
-
);
|
|
61
|
-
};
|
|
62
|
-
|
|
63
|
-
export default PrintableInvoiceHeader;
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
@use '@carbon/colors';
|
|
2
|
-
@use '@carbon/layout';
|
|
3
|
-
@use '@carbon/type';
|
|
4
|
-
|
|
5
|
-
.container {
|
|
6
|
-
padding: 0 1rem 2rem;
|
|
7
|
-
border-bottom: 1px solid #ebedf2;
|
|
8
|
-
margin-bottom: 2rem;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
.printableBody {
|
|
12
|
-
display: flex;
|
|
13
|
-
flex-direction: row;
|
|
14
|
-
justify-content: space-between;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
.printableHeader {
|
|
18
|
-
display: flex;
|
|
19
|
-
flex-direction: row;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
.img {
|
|
23
|
-
display: flex;
|
|
24
|
-
margin-left: auto;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
.billDetails {
|
|
28
|
-
display: flex;
|
|
29
|
-
width: 50%;
|
|
30
|
-
flex-direction: column;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
.facilityDetails {
|
|
34
|
-
display: flex;
|
|
35
|
-
flex-flow: column wrap;
|
|
36
|
-
text-align: right;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
.heading {
|
|
40
|
-
font-size: 40px;
|
|
41
|
-
text-transform: uppercase;
|
|
42
|
-
margin-bottom: layout.$spacing-05;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
.facilityName {
|
|
46
|
-
@include type.type-style('heading-compact-02');
|
|
47
|
-
font-weight: bold;
|
|
48
|
-
color: colors.$green-70;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
.itemHeading {
|
|
52
|
-
@include type.type-style('body-compact-02');
|
|
53
|
-
margin-bottom: 0.25rem;
|
|
54
|
-
font-weight: bold;
|
|
55
|
-
color: colors.$cool-gray-90;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
.itemLabel {
|
|
59
|
-
@include type.type-style('body-compact-02');
|
|
60
|
-
color: colors.$cool-gray-90;
|
|
61
|
-
}
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { screen, render } from '@testing-library/react';
|
|
3
|
-
import PrintableInvoiceHeader from './printable-invoice-header.component';
|
|
4
|
-
import { useConfig } from '@openmrs/esm-framework';
|
|
5
|
-
|
|
6
|
-
const mockUseConfig = useConfig as jest.MockedFunction<typeof useConfig>;
|
|
7
|
-
|
|
8
|
-
jest.mock('../../billing.resource', () => ({
|
|
9
|
-
useDefaultFacility: jest.fn(),
|
|
10
|
-
}));
|
|
11
|
-
|
|
12
|
-
jest.mock('@openmrs/esm-framework', () => ({
|
|
13
|
-
useConfig: jest.fn(),
|
|
14
|
-
}));
|
|
15
|
-
const testProps = {
|
|
16
|
-
patientDetails: {
|
|
17
|
-
name: 'John Doe',
|
|
18
|
-
county: 'Nairobi',
|
|
19
|
-
subCounty: 'Westlands',
|
|
20
|
-
city: 'Nairobi',
|
|
21
|
-
age: '45',
|
|
22
|
-
gender: 'Male',
|
|
23
|
-
},
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
describe('PrintableInvoiceHeader', () => {
|
|
27
|
-
test('should render PrintableInvoiceHeader component', () => {
|
|
28
|
-
mockUseConfig.mockReturnValue({ logo: { src: 'logo.png', alt: 'logo' } });
|
|
29
|
-
|
|
30
|
-
render(<PrintableInvoiceHeader facilityInfo={{ display: 'MTRH', uuid: 'mtrh-uuid' }} {...testProps} />);
|
|
31
|
-
const header = screen.getByText('Invoice');
|
|
32
|
-
expect(header).toBeInTheDocument();
|
|
33
|
-
|
|
34
|
-
expect(screen.getByText('John Doe')).toBeInTheDocument();
|
|
35
|
-
expect(screen.getByText('Nairobi')).toBeInTheDocument();
|
|
36
|
-
expect(screen.getByText('Westlands, Nairobi')).toBeInTheDocument();
|
|
37
|
-
expect(screen.getByText('MTRH')).toBeInTheDocument();
|
|
38
|
-
expect(screen.getByText('Kenya')).toBeInTheDocument();
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
test('should display the logo when logo is provided', () => {
|
|
42
|
-
mockUseConfig.mockReturnValue({ logo: { src: 'logo.png', alt: 'logo' } });
|
|
43
|
-
|
|
44
|
-
render(<PrintableInvoiceHeader facilityInfo={{ display: 'MTRH', uuid: 'mtrh-uuid' }} {...testProps} />);
|
|
45
|
-
const logo = screen.getByAltText('logo');
|
|
46
|
-
expect(logo).toBeInTheDocument();
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
test('should display the default logo when logo is not provided', () => {
|
|
50
|
-
mockUseConfig.mockReturnValue({ logo: {} });
|
|
51
|
-
|
|
52
|
-
render(<PrintableInvoiceHeader facilityInfo={{ display: 'MTRH', uuid: 'mtrh-uuid' }} {...testProps} />);
|
|
53
|
-
const logo = screen.getByRole('img');
|
|
54
|
-
expect(logo).toBeInTheDocument();
|
|
55
|
-
});
|
|
56
|
-
});
|
|
@@ -1,134 +0,0 @@
|
|
|
1
|
-
import React, { useMemo } from 'react';
|
|
2
|
-
import {
|
|
3
|
-
DataTable,
|
|
4
|
-
Table,
|
|
5
|
-
TableContainer,
|
|
6
|
-
TableHead,
|
|
7
|
-
TableRow,
|
|
8
|
-
TableBody,
|
|
9
|
-
TableHeader,
|
|
10
|
-
TableCell,
|
|
11
|
-
} from '@carbon/react';
|
|
12
|
-
import { age, formatDatetime, parseDate } from '@openmrs/esm-framework';
|
|
13
|
-
import { convertToCurrency, extractString, getGender } from '../../helpers';
|
|
14
|
-
import { MappedBill } from '../../types';
|
|
15
|
-
import { useTranslation } from 'react-i18next';
|
|
16
|
-
import PrintableFooter from './printable-footer.component';
|
|
17
|
-
import PrintableInvoiceHeader from './printable-invoice-header.component';
|
|
18
|
-
import styles from './printable-invoice.scss';
|
|
19
|
-
|
|
20
|
-
type PrintableInvoiceProps = {
|
|
21
|
-
bill: MappedBill;
|
|
22
|
-
patient: fhir.Patient;
|
|
23
|
-
isPrinting: boolean;
|
|
24
|
-
facilityInfo: Record<string, any>;
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
const PrintableInvoice = React.forwardRef<HTMLDivElement, PrintableInvoiceProps>(
|
|
28
|
-
({ bill, patient, facilityInfo }, ref) => {
|
|
29
|
-
const { t } = useTranslation();
|
|
30
|
-
const headerData = [
|
|
31
|
-
{ header: 'Inventory item', key: 'billItem' },
|
|
32
|
-
{ header: 'Quantity', key: 'quantity' },
|
|
33
|
-
{ header: 'Unit price', key: 'price' },
|
|
34
|
-
{ header: 'Total', key: 'total' },
|
|
35
|
-
];
|
|
36
|
-
|
|
37
|
-
const rowData =
|
|
38
|
-
bill?.lineItems?.map((item, index) => {
|
|
39
|
-
return {
|
|
40
|
-
id: `${item.uuid}`,
|
|
41
|
-
billItem: `${index + 1} - ${
|
|
42
|
-
item.item === '' ? extractString(item?.billableService) : extractString(item.item)
|
|
43
|
-
}`,
|
|
44
|
-
quantity: item.quantity,
|
|
45
|
-
price: item.price,
|
|
46
|
-
total: item.price * item.quantity,
|
|
47
|
-
};
|
|
48
|
-
}) ?? [];
|
|
49
|
-
|
|
50
|
-
const invoiceTotal = {
|
|
51
|
-
'Total Amount': convertToCurrency(bill?.totalAmount),
|
|
52
|
-
'Amount Tendered': convertToCurrency(bill?.tenderedAmount),
|
|
53
|
-
'Discount Amount': convertToCurrency(0),
|
|
54
|
-
'Amount due': convertToCurrency(bill?.totalAmount - bill?.tenderedAmount),
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
const patientDetails = useMemo(() => {
|
|
58
|
-
return {
|
|
59
|
-
name: `${patient?.name?.[0]?.given?.join(' ')} ${patient?.name?.[0].family}`,
|
|
60
|
-
age: age(patient?.birthDate),
|
|
61
|
-
gender: getGender(patient?.gender, t),
|
|
62
|
-
city: patient?.address?.[0].city,
|
|
63
|
-
county: patient?.address?.[0].district,
|
|
64
|
-
subCounty: patient?.address?.[0].state,
|
|
65
|
-
};
|
|
66
|
-
}, [patient, t]);
|
|
67
|
-
|
|
68
|
-
const invoiceDetails = {
|
|
69
|
-
'Invoice #': bill.receiptNumber,
|
|
70
|
-
'Invoice date': formatDatetime(parseDate(bill.dateCreated), { mode: 'standard', noToday: true }),
|
|
71
|
-
Status: bill.status,
|
|
72
|
-
};
|
|
73
|
-
|
|
74
|
-
return (
|
|
75
|
-
<div ref={ref} className={styles.container}>
|
|
76
|
-
<PrintableInvoiceHeader patientDetails={patientDetails} facilityInfo={facilityInfo} />
|
|
77
|
-
<div className={styles.printableInvoiceContainer}>
|
|
78
|
-
<div className={styles.detailsContainer}>
|
|
79
|
-
{Object.entries(invoiceDetails).map(([key, val]) => (
|
|
80
|
-
<div key={key} className={styles.item}>
|
|
81
|
-
<p className={styles.itemHeading}>{key}</p>
|
|
82
|
-
<span className={styles.itemValue}>{val}</span>
|
|
83
|
-
</div>
|
|
84
|
-
))}
|
|
85
|
-
</div>
|
|
86
|
-
|
|
87
|
-
<div className={styles.itemsContainer}>
|
|
88
|
-
<div className={styles.tableContainer}>
|
|
89
|
-
<DataTable size="sm" isSortable rows={rowData} headers={headerData} useZebraStyles>
|
|
90
|
-
{({ rows, headers, getRowProps, getTableProps }) => (
|
|
91
|
-
<TableContainer>
|
|
92
|
-
<Table {...getTableProps()} aria-label="Invoice line items">
|
|
93
|
-
<TableHead>
|
|
94
|
-
<TableRow>
|
|
95
|
-
{headers.map((header) => (
|
|
96
|
-
<TableHeader key={header.key}>{header.header}</TableHeader>
|
|
97
|
-
))}
|
|
98
|
-
</TableRow>
|
|
99
|
-
</TableHead>
|
|
100
|
-
<TableBody>
|
|
101
|
-
{rows.map((row) => (
|
|
102
|
-
<TableRow
|
|
103
|
-
key={row.id}
|
|
104
|
-
{...getRowProps({
|
|
105
|
-
row,
|
|
106
|
-
})}>
|
|
107
|
-
{row.cells.map((cell) => (
|
|
108
|
-
<TableCell key={cell.id}>{cell.value}</TableCell>
|
|
109
|
-
))}
|
|
110
|
-
</TableRow>
|
|
111
|
-
))}
|
|
112
|
-
</TableBody>
|
|
113
|
-
</Table>
|
|
114
|
-
</TableContainer>
|
|
115
|
-
)}
|
|
116
|
-
</DataTable>
|
|
117
|
-
</div>
|
|
118
|
-
|
|
119
|
-
<div className={styles.totalContainer}>
|
|
120
|
-
{Object.entries(invoiceTotal).map(([key, val]) => (
|
|
121
|
-
<p key={key} className={styles.itemTotal}>
|
|
122
|
-
<span className={styles.itemHeading}>{key}</span>: <span className={styles.itemLabel}>{val}</span>
|
|
123
|
-
</p>
|
|
124
|
-
))}
|
|
125
|
-
</div>
|
|
126
|
-
</div>
|
|
127
|
-
</div>
|
|
128
|
-
<PrintableFooter facilityInfo={facilityInfo} />
|
|
129
|
-
</div>
|
|
130
|
-
);
|
|
131
|
-
},
|
|
132
|
-
);
|
|
133
|
-
|
|
134
|
-
export default PrintableInvoice;
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
@use '@carbon/colors';
|
|
2
|
-
@use '@carbon/layout';
|
|
3
|
-
@use '@carbon/type';
|
|
4
|
-
|
|
5
|
-
.printableInvoiceContainer {
|
|
6
|
-
display: flex;
|
|
7
|
-
flex-direction: row;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
.container {
|
|
11
|
-
margin-top: layout.$spacing-05;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
.itemsContainer {
|
|
15
|
-
display: flex;
|
|
16
|
-
flex-direction: column;
|
|
17
|
-
width: 80%;
|
|
18
|
-
padding: layout.$spacing-05;
|
|
19
|
-
margin: layout.$spacing-05;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
.detailsContainer {
|
|
23
|
-
margin: layout.$spacing-05 0;
|
|
24
|
-
width: 20%;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
.tableContainer {
|
|
28
|
-
margin-bottom: layout.$spacing-05;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
.totalContainer {
|
|
32
|
-
display: flex;
|
|
33
|
-
flex-direction: column;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
.item {
|
|
37
|
-
margin: layout.$spacing-04;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
.itemHeading {
|
|
41
|
-
@include type.type-style('label-02');
|
|
42
|
-
font-style: bold;
|
|
43
|
-
color: colors.$cool-gray-90;
|
|
44
|
-
margin-bottom: layout.$spacing-01;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
.itemValue {
|
|
48
|
-
@include type.type-style('legal-01');
|
|
49
|
-
color: colors.$cool-gray-90;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
.itemTotal {
|
|
53
|
-
border-bottom: solid 0.00125rem colors.$cool-gray-10;
|
|
54
|
-
padding: layout.$spacing-02;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
.itemLabel {
|
|
58
|
-
float: right;
|
|
59
|
-
@include type.type-style('label-01');
|
|
60
|
-
}
|
|
File without changes
|
/package/src/{invoice/print-bill-receipt/print-bill-receipt.scss → print-preview/print-preview.scss}
RENAMED
|
File without changes
|