@openmrs/esm-billing-app 1.0.2-pre.749 → 1.0.2-pre.758
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/dist/4300.js +1 -1
- package/dist/4724.js +1 -1
- package/dist/4724.js.map +1 -1
- package/dist/4739.js +1 -1
- package/dist/4739.js.map +1 -1
- package/dist/942.js +1 -1
- package/dist/942.js.map +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 +16 -16
- package/dist/routes.json +1 -1
- package/package.json +1 -1
- package/src/bill-item-actions/edit-bill-item.modal.tsx +0 -1
- package/src/bill-item-actions/edit-bill-item.test.tsx +0 -1
- package/src/billable-services/billable-service.resource.ts +13 -6
- package/src/billable-services/billable-services.component.tsx +47 -33
- package/src/billable-services/cash-point/cash-point-configuration.component.tsx +2 -1
- package/src/billable-services/create-edit/add-billable-service.component.tsx +336 -293
- package/src/billable-services/create-edit/add-billable-service.scss +3 -1
- package/src/billable-services/create-edit/add-billable-service.test.tsx +36 -35
- package/src/billable-services/create-edit/edit-billable-service.modal.tsx +6 -5
- package/src/billable-services/payment-modes/payment-modes-config.component.tsx +44 -44
- package/src/billable-services/payment-modes/payment-modes-config.scss +0 -3
- package/src/billing-form/billing-checkin-form.test.tsx +97 -22
- package/src/billing-form/billing-form.component.tsx +7 -4
- package/src/invoice/payments/payments.component.tsx +0 -1
- package/src/modal/require-payment-modal.test.tsx +2 -2
- package/src/types/index.ts +14 -6
- package/translations/en.json +15 -2
|
@@ -6,7 +6,6 @@
|
|
|
6
6
|
.form {
|
|
7
7
|
display: flex;
|
|
8
8
|
flex-direction: column;
|
|
9
|
-
justify-content: space-between;
|
|
10
9
|
height: 100%;
|
|
11
10
|
margin: layout.$spacing-05;
|
|
12
11
|
}
|
|
@@ -132,3 +131,6 @@
|
|
|
132
131
|
font-size: 0.875rem;
|
|
133
132
|
}
|
|
134
133
|
|
|
134
|
+
.serviceNameLabel {
|
|
135
|
+
@include type.type-style('body-compact-02');
|
|
136
|
+
}
|
|
@@ -1,26 +1,29 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import userEvent from '@testing-library/user-event';
|
|
3
3
|
import { render, screen } from '@testing-library/react';
|
|
4
|
-
import { type FetchResponse
|
|
4
|
+
import { navigate, type FetchResponse } from '@openmrs/esm-framework';
|
|
5
5
|
import {
|
|
6
|
+
createBillableService,
|
|
6
7
|
useBillableServices,
|
|
8
|
+
useConceptsSearch,
|
|
7
9
|
usePaymentModes,
|
|
8
10
|
useServiceTypes,
|
|
9
|
-
createBillableService,
|
|
10
11
|
} from '../billable-service.resource';
|
|
11
12
|
import AddBillableService from './add-billable-service.component';
|
|
12
13
|
|
|
13
|
-
const mockUseBillableServices =
|
|
14
|
-
const mockUsePaymentModes =
|
|
15
|
-
const mockUseServiceTypes =
|
|
16
|
-
const
|
|
17
|
-
const
|
|
14
|
+
const mockUseBillableServices = jest.mocked(useBillableServices);
|
|
15
|
+
const mockUsePaymentModes = jest.mocked(usePaymentModes);
|
|
16
|
+
const mockUseServiceTypes = jest.mocked(useServiceTypes);
|
|
17
|
+
const mockCreateBillableService = jest.mocked(createBillableService);
|
|
18
|
+
const mockUseConceptsSearch = jest.mocked(useConceptsSearch);
|
|
18
19
|
|
|
19
20
|
jest.mock('../billable-service.resource', () => ({
|
|
20
21
|
useBillableServices: jest.fn(),
|
|
21
22
|
usePaymentModes: jest.fn(),
|
|
22
23
|
useServiceTypes: jest.fn(),
|
|
23
24
|
createBillableService: jest.fn(),
|
|
25
|
+
updateBillableService: jest.fn(),
|
|
26
|
+
useConceptsSearch: jest.fn(),
|
|
24
27
|
}));
|
|
25
28
|
|
|
26
29
|
const mockPaymentModes = [
|
|
@@ -49,11 +52,7 @@ const mockServiceTypes = [
|
|
|
49
52
|
{ uuid: 'a487a743-62ce-4f93-a66b-c5154ee8987d', display: 'Adherence counselling service' },
|
|
50
53
|
];
|
|
51
54
|
|
|
52
|
-
|
|
53
|
-
beforeEach(() => {
|
|
54
|
-
jest.resetAllMocks();
|
|
55
|
-
});
|
|
56
|
-
|
|
55
|
+
describe('AddBillableService', () => {
|
|
57
56
|
test('should render billable services form and generate correct payload', async () => {
|
|
58
57
|
const user = userEvent.setup();
|
|
59
58
|
const mockOnClose = jest.fn();
|
|
@@ -64,8 +63,9 @@ xdescribe('AddBillableService', () => {
|
|
|
64
63
|
mutate: jest.fn(),
|
|
65
64
|
isValidating: false,
|
|
66
65
|
});
|
|
67
|
-
mockUsePaymentModes.mockReturnValue({ paymentModes: mockPaymentModes, error: null,
|
|
68
|
-
mockUseServiceTypes.mockReturnValue({ serviceTypes: mockServiceTypes, error: false,
|
|
66
|
+
mockUsePaymentModes.mockReturnValue({ paymentModes: mockPaymentModes, error: null, isLoadingPaymentModes: false });
|
|
67
|
+
mockUseServiceTypes.mockReturnValue({ serviceTypes: mockServiceTypes, error: false, isLoadingServiceTypes: false });
|
|
68
|
+
mockUseConceptsSearch.mockReturnValue({ searchResults: [], isSearching: false, error: null });
|
|
69
69
|
|
|
70
70
|
render(<AddBillableService onClose={mockOnClose} />);
|
|
71
71
|
|
|
@@ -84,50 +84,50 @@ xdescribe('AddBillableService', () => {
|
|
|
84
84
|
expect(serviceNameTextInp).toHaveValue('Test Service Name');
|
|
85
85
|
expect(serviceShortNameTextInp).toHaveValue('Test Short Name');
|
|
86
86
|
|
|
87
|
-
const serviceTypeComboBox = screen.getByRole('combobox', { name: /Service
|
|
87
|
+
const serviceTypeComboBox = screen.getByRole('combobox', { name: /Service type/i });
|
|
88
88
|
expect(serviceTypeComboBox).toBeInTheDocument();
|
|
89
89
|
await user.click(serviceTypeComboBox);
|
|
90
90
|
const serviceTypeOptions = screen.getByRole('option', { name: /Lab service/i });
|
|
91
91
|
expect(serviceTypeOptions).toBeInTheDocument();
|
|
92
92
|
await user.click(serviceTypeOptions);
|
|
93
93
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
await user.click(
|
|
98
|
-
|
|
99
|
-
const paymentMethodComboBox = screen.getByRole('combobox', { name: /Payment Mode/i });
|
|
100
|
-
expect(paymentMethodComboBox).toBeInTheDocument();
|
|
101
|
-
await user.click(paymentMethodComboBox);
|
|
94
|
+
// Fill in the default payment option (first one)
|
|
95
|
+
const paymentMethodComboBoxes = screen.getAllByRole('combobox', { name: /Payment mode/i });
|
|
96
|
+
expect(paymentMethodComboBoxes).toHaveLength(1); // Should have one default
|
|
97
|
+
await user.click(paymentMethodComboBoxes[0]);
|
|
102
98
|
const paymentMethodOptions = screen.getByRole('option', { name: /Cash/i });
|
|
103
99
|
expect(paymentMethodOptions).toBeInTheDocument();
|
|
104
100
|
await user.click(paymentMethodOptions);
|
|
105
101
|
|
|
106
|
-
const
|
|
102
|
+
const priceTextInps = screen.getAllByRole('textbox', { name: /Selling Price/i });
|
|
103
|
+
expect(priceTextInps).toHaveLength(1); // Should have one price input for the default payment method
|
|
104
|
+
const priceTextInp = priceTextInps[0];
|
|
107
105
|
expect(priceTextInp).toBeInTheDocument();
|
|
108
106
|
await user.type(priceTextInp, '1000');
|
|
109
107
|
|
|
110
|
-
|
|
111
|
-
const saveBtn = screen.
|
|
108
|
+
mockCreateBillableService.mockReturnValue(Promise.resolve({} as FetchResponse<any>));
|
|
109
|
+
const saveBtn = screen.getAllByRole('button').find((btn) => btn.getAttribute('type') === 'submit');
|
|
112
110
|
expect(saveBtn).toBeInTheDocument();
|
|
111
|
+
|
|
113
112
|
await user.click(saveBtn);
|
|
114
113
|
|
|
115
|
-
expect(
|
|
116
|
-
expect(
|
|
114
|
+
expect(mockCreateBillableService).toHaveBeenCalledTimes(1);
|
|
115
|
+
expect(mockCreateBillableService).toHaveBeenCalledWith({
|
|
117
116
|
name: 'Test Service Name',
|
|
118
117
|
shortName: 'Test Short Name',
|
|
119
|
-
serviceType:
|
|
118
|
+
serviceType: 'c9604249-db0a-4d03-b074-fc6bc2fa13e6',
|
|
120
119
|
servicePrices: [
|
|
121
120
|
{
|
|
122
121
|
paymentMode: '63eff7a4-6f82-43c4-a333-dbcc58fe9f74',
|
|
123
|
-
price:
|
|
122
|
+
price: 1000,
|
|
124
123
|
name: 'Cash',
|
|
125
124
|
},
|
|
126
125
|
],
|
|
127
126
|
serviceStatus: 'ENABLED',
|
|
127
|
+
concept: undefined,
|
|
128
128
|
});
|
|
129
|
-
expect(
|
|
130
|
-
expect(
|
|
129
|
+
expect(navigate).toHaveBeenCalledTimes(1);
|
|
130
|
+
expect(navigate).toHaveBeenCalledWith({ to: '/openmrs/spa/billable-services' });
|
|
131
131
|
});
|
|
132
132
|
|
|
133
133
|
test("should navigate back to billable services dashboard when 'Cancel' button is clicked", async () => {
|
|
@@ -140,12 +140,13 @@ xdescribe('AddBillableService', () => {
|
|
|
140
140
|
mutate: jest.fn(),
|
|
141
141
|
isValidating: false,
|
|
142
142
|
});
|
|
143
|
-
mockUsePaymentModes.mockReturnValue({ paymentModes: mockPaymentModes, error: null,
|
|
144
|
-
mockUseServiceTypes.mockReturnValue({ serviceTypes: mockServiceTypes, error: false,
|
|
143
|
+
mockUsePaymentModes.mockReturnValue({ paymentModes: mockPaymentModes, error: null, isLoadingPaymentModes: false });
|
|
144
|
+
mockUseServiceTypes.mockReturnValue({ serviceTypes: mockServiceTypes, error: false, isLoadingServiceTypes: false });
|
|
145
|
+
mockUseConceptsSearch.mockReturnValue({ searchResults: [], isSearching: false, error: null });
|
|
145
146
|
|
|
146
147
|
render(<AddBillableService onClose={mockOnClose} />);
|
|
147
148
|
|
|
148
|
-
const cancelBtn = screen.
|
|
149
|
+
const cancelBtn = screen.getAllByRole('button').find((btn) => btn.className.includes('cds--btn--secondary'));
|
|
149
150
|
expect(cancelBtn).toBeInTheDocument();
|
|
150
151
|
await user.click(cancelBtn);
|
|
151
152
|
|
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { useTranslation } from 'react-i18next';
|
|
3
3
|
import { Button, ModalBody, ModalFooter, ModalHeader } from '@carbon/react';
|
|
4
|
-
import AddBillableService from './add-billable-service.component';
|
|
5
4
|
import { getCoreTranslation } from '@openmrs/esm-framework';
|
|
5
|
+
import { type BillableService } from '../../types';
|
|
6
|
+
import AddBillableService from './add-billable-service.component';
|
|
6
7
|
|
|
7
8
|
interface EditBillableServiceModalProps {
|
|
8
9
|
closeModal: () => void;
|
|
9
|
-
editingService?: any;
|
|
10
10
|
onServiceUpdated: () => void;
|
|
11
|
+
serviceToEdit?: BillableService;
|
|
11
12
|
}
|
|
12
13
|
|
|
13
14
|
const EditBillableServiceModal: React.FC<EditBillableServiceModalProps> = ({
|
|
14
15
|
closeModal,
|
|
15
|
-
|
|
16
|
+
serviceToEdit,
|
|
16
17
|
onServiceUpdated,
|
|
17
18
|
}) => {
|
|
18
19
|
const { t } = useTranslation();
|
|
@@ -22,10 +23,10 @@ const EditBillableServiceModal: React.FC<EditBillableServiceModalProps> = ({
|
|
|
22
23
|
<ModalHeader closeModal={closeModal} title={t('billableService', 'Billable Service')} />
|
|
23
24
|
<ModalBody>
|
|
24
25
|
<AddBillableService
|
|
25
|
-
|
|
26
|
+
serviceToEdit={serviceToEdit}
|
|
27
|
+
isModal
|
|
26
28
|
onClose={closeModal}
|
|
27
29
|
onServiceUpdated={onServiceUpdated}
|
|
28
|
-
isModal={true}
|
|
29
30
|
/>
|
|
30
31
|
</ModalBody>
|
|
31
32
|
<ModalFooter>
|
|
@@ -41,16 +41,18 @@ const PaymentModesConfig: React.FC = () => {
|
|
|
41
41
|
}, [fetchPaymentModes]);
|
|
42
42
|
|
|
43
43
|
const handleAddPaymentMode = () => {
|
|
44
|
-
showModal('add-payment-mode-modal', {
|
|
44
|
+
const dispose = showModal('add-payment-mode-modal', {
|
|
45
45
|
onPaymentModeAdded: fetchPaymentModes,
|
|
46
|
+
closeModal: () => dispose(),
|
|
46
47
|
});
|
|
47
48
|
};
|
|
48
49
|
|
|
49
50
|
const handleDeletePaymentMode = (paymentMode) => {
|
|
50
|
-
showModal('delete-payment-mode-modal', {
|
|
51
|
+
const dispose = showModal('delete-payment-mode-modal', {
|
|
51
52
|
paymentModeUuid: paymentMode.uuid,
|
|
52
53
|
paymentModeName: paymentMode.name,
|
|
53
54
|
onPaymentModeDeleted: fetchPaymentModes,
|
|
55
|
+
closeModal: () => dispose(),
|
|
54
56
|
});
|
|
55
57
|
};
|
|
56
58
|
|
|
@@ -74,49 +76,47 @@ const PaymentModesConfig: React.FC = () => {
|
|
|
74
76
|
{t('addNewPaymentMode', 'Add New Payment Mode')}
|
|
75
77
|
</Button>
|
|
76
78
|
</CardHeader>
|
|
77
|
-
<
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
<
|
|
81
|
-
<
|
|
82
|
-
<
|
|
83
|
-
|
|
84
|
-
{
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
</TableHeader>
|
|
88
|
-
))}
|
|
89
|
-
</TableRow>
|
|
90
|
-
</TableHead>
|
|
91
|
-
<TableBody>
|
|
92
|
-
{rows.map((row) => (
|
|
93
|
-
<TableRow key={row.id} {...getRowProps({ row })}>
|
|
94
|
-
{row.cells.map((cell) =>
|
|
95
|
-
cell.info.header !== 'actions' ? (
|
|
96
|
-
<TableCell key={cell.id}>{cell.value}</TableCell>
|
|
97
|
-
) : (
|
|
98
|
-
<TableCell key={cell.id}>
|
|
99
|
-
<OverflowMenu>
|
|
100
|
-
<OverflowMenuItem
|
|
101
|
-
className={styles.menuItem}
|
|
102
|
-
itemText={getCoreTranslation('delete')}
|
|
103
|
-
onClick={() => {
|
|
104
|
-
const selected = paymentModes.find((p) => p.uuid === row.id);
|
|
105
|
-
handleDeletePaymentMode(selected);
|
|
106
|
-
}}
|
|
107
|
-
/>
|
|
108
|
-
</OverflowMenu>
|
|
109
|
-
</TableCell>
|
|
110
|
-
),
|
|
111
|
-
)}
|
|
112
|
-
</TableRow>
|
|
79
|
+
<DataTable rows={rowData} headers={headerData} isSortable size="lg">
|
|
80
|
+
{({ rows, headers, getTableProps, getHeaderProps, getRowProps }) => (
|
|
81
|
+
<TableContainer>
|
|
82
|
+
<Table className={styles.table} {...getTableProps()}>
|
|
83
|
+
<TableHead>
|
|
84
|
+
<TableRow>
|
|
85
|
+
{headers.map((header) => (
|
|
86
|
+
<TableHeader key={header.key} {...getHeaderProps({ header })}>
|
|
87
|
+
{header.header}
|
|
88
|
+
</TableHeader>
|
|
113
89
|
))}
|
|
114
|
-
</
|
|
115
|
-
</
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
90
|
+
</TableRow>
|
|
91
|
+
</TableHead>
|
|
92
|
+
<TableBody>
|
|
93
|
+
{rows.map((row) => (
|
|
94
|
+
<TableRow key={row.id} {...getRowProps({ row })}>
|
|
95
|
+
{row.cells.map((cell) =>
|
|
96
|
+
cell.info.header !== 'actions' ? (
|
|
97
|
+
<TableCell key={cell.id}>{cell.value}</TableCell>
|
|
98
|
+
) : (
|
|
99
|
+
<TableCell key={cell.id}>
|
|
100
|
+
<OverflowMenu>
|
|
101
|
+
<OverflowMenuItem
|
|
102
|
+
className={styles.menuItem}
|
|
103
|
+
itemText={getCoreTranslation('delete')}
|
|
104
|
+
onClick={() => {
|
|
105
|
+
const selected = paymentModes.find((p) => p.uuid === row.id);
|
|
106
|
+
handleDeletePaymentMode(selected);
|
|
107
|
+
}}
|
|
108
|
+
/>
|
|
109
|
+
</OverflowMenu>
|
|
110
|
+
</TableCell>
|
|
111
|
+
),
|
|
112
|
+
)}
|
|
113
|
+
</TableRow>
|
|
114
|
+
))}
|
|
115
|
+
</TableBody>
|
|
116
|
+
</Table>
|
|
117
|
+
</TableContainer>
|
|
118
|
+
)}
|
|
119
|
+
</DataTable>
|
|
120
120
|
</div>
|
|
121
121
|
</div>
|
|
122
122
|
);
|
|
@@ -1,9 +1,16 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import userEvent from '@testing-library/user-event';
|
|
3
3
|
import { screen, render } from '@testing-library/react';
|
|
4
|
-
import {
|
|
4
|
+
import { useConfig } from '@openmrs/esm-framework';
|
|
5
|
+
import { type BillingConfig } from '../config-schema';
|
|
6
|
+
import { useBillableItems, useCashPoint, usePaymentMethods } from './billing-form.resource';
|
|
5
7
|
import BillingCheckInForm from './billing-checkin-form.component';
|
|
6
8
|
|
|
9
|
+
const mockUseConfig = jest.mocked(useConfig<BillingConfig>);
|
|
10
|
+
const mockUseCashPoint = jest.mocked(useCashPoint);
|
|
11
|
+
const mockUseBillableItems = jest.mocked(useBillableItems);
|
|
12
|
+
const mockUsePaymentMethods = jest.mocked(usePaymentMethods);
|
|
13
|
+
|
|
7
14
|
const mockCashPoints = [
|
|
8
15
|
{
|
|
9
16
|
uuid: '54065383-b4d4-42d2-af4d-d250a1fd2590',
|
|
@@ -16,6 +23,7 @@ const mockCashPoints = [
|
|
|
16
23
|
const mockBillableItems = [
|
|
17
24
|
{
|
|
18
25
|
uuid: 'b37dddd6-4490-4bf7-b694-43bf19d04059',
|
|
26
|
+
name: 'Consultation',
|
|
19
27
|
conceptUuid: '1926AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
|
|
20
28
|
conceptName: 'Consultation billable item',
|
|
21
29
|
hasExpiration: false,
|
|
@@ -25,9 +33,21 @@ const mockBillableItems = [
|
|
|
25
33
|
categoryName: 'Non Drug',
|
|
26
34
|
commonName: 'Consultation',
|
|
27
35
|
acronym: 'CONSULT',
|
|
36
|
+
servicePrices: [
|
|
37
|
+
{
|
|
38
|
+
uuid: 'price-1',
|
|
39
|
+
name: 'Default',
|
|
40
|
+
price: '100.00',
|
|
41
|
+
paymentMode: {
|
|
42
|
+
uuid: '1c30ee58-82d4-4ea4-a8c1-4bf2f9dfc8cf',
|
|
43
|
+
name: 'Insurance',
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
],
|
|
28
47
|
},
|
|
29
48
|
{
|
|
30
49
|
uuid: 'b47dddd6-4490-4bf7-b694-43bf19d04059',
|
|
50
|
+
name: 'Lab Testing',
|
|
31
51
|
conceptUuid: '1926AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
|
|
32
52
|
conceptName: 'Lab Testing billable item',
|
|
33
53
|
hasExpiration: false,
|
|
@@ -37,23 +57,65 @@ const mockBillableItems = [
|
|
|
37
57
|
categoryName: 'Non Drug',
|
|
38
58
|
commonName: 'Lab Testing',
|
|
39
59
|
acronym: 'CONSULT',
|
|
60
|
+
servicePrices: [
|
|
61
|
+
{
|
|
62
|
+
uuid: 'price-2',
|
|
63
|
+
name: 'Default',
|
|
64
|
+
price: '500.00001',
|
|
65
|
+
paymentMode: {
|
|
66
|
+
uuid: '1c30ee58-82d4-4ea4-a8c1-4bf2f9dfc8cf',
|
|
67
|
+
name: 'Insurance',
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
],
|
|
40
71
|
},
|
|
41
72
|
];
|
|
42
73
|
|
|
43
|
-
const
|
|
44
|
-
|
|
74
|
+
const mockPaymentMethods = [
|
|
75
|
+
{
|
|
76
|
+
uuid: '1c30ee58-82d4-4ea4-a8c1-4bf2f9dfc8cf',
|
|
77
|
+
name: 'Insurance',
|
|
78
|
+
description: 'Insurance payment',
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
uuid: '2c30ee58-82d4-4ea4-a8c1-4bf2f9dfc8cf',
|
|
82
|
+
name: 'Cash',
|
|
83
|
+
description: 'Cash payment',
|
|
84
|
+
},
|
|
85
|
+
];
|
|
45
86
|
|
|
46
87
|
jest.mock('./billing-form.resource', () => ({
|
|
47
88
|
useBillableItems: jest.fn(),
|
|
48
89
|
useCashPoint: jest.fn(),
|
|
49
90
|
createPatientBill: jest.fn(),
|
|
91
|
+
usePaymentMethods: jest.fn(),
|
|
50
92
|
}));
|
|
51
93
|
|
|
52
94
|
const testProps = { patientUuid: 'some-patient-uuid', setExtraVisitInfo: jest.fn() };
|
|
53
95
|
|
|
54
|
-
|
|
96
|
+
describe('BillingCheckInForm', () => {
|
|
55
97
|
beforeEach(() => {
|
|
56
98
|
jest.resetAllMocks();
|
|
99
|
+
mockUseConfig.mockReturnValue({
|
|
100
|
+
patientCatergory: {
|
|
101
|
+
paymentDetails: 'fbc0702d-b4c9-4968-be63-af8ad3ad6239',
|
|
102
|
+
paymentMethods: '8553afa0-bdb9-4d3c-8a98-05fa9350aa85',
|
|
103
|
+
policyNumber: '3a988e33-a6c0-4b76-b924-01abb998944b',
|
|
104
|
+
insuranceScheme: 'aac48226-d143-4274-80e0-264db4e368ee',
|
|
105
|
+
patientCategory: '3b9dfac8-9e4d-11ee-8c90-0242ac120002',
|
|
106
|
+
formPayloadPending: '919b51c9-8e2e-468f-8354-181bf3e55786',
|
|
107
|
+
},
|
|
108
|
+
catergoryConcepts: {
|
|
109
|
+
payingDetails: '44b34972-6630-4e5a-a9f6-a6eb0f109650',
|
|
110
|
+
nonPayingDetails: 'f3fb2d88-cccd-422c-8766-be101ba7bd2e',
|
|
111
|
+
insuranceDetails: 'beac329b-f1dc-4a33-9e7c-d95821a137a6',
|
|
112
|
+
},
|
|
113
|
+
nonPayingPatientCategories: {
|
|
114
|
+
childUnder5: '1528AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
|
|
115
|
+
student: '159465AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
|
|
116
|
+
},
|
|
117
|
+
} as BillingConfig);
|
|
118
|
+
mockUsePaymentMethods.mockReturnValue({ paymentModes: mockPaymentMethods, isLoading: false, error: null });
|
|
57
119
|
});
|
|
58
120
|
|
|
59
121
|
test('should show the loading spinner while retrieving data', () => {
|
|
@@ -76,53 +138,66 @@ xdescribe('BillingCheckInForm', () => {
|
|
|
76
138
|
|
|
77
139
|
test('should render the form correctly and generate the required payload', async () => {
|
|
78
140
|
const user = userEvent.setup();
|
|
79
|
-
mockUseCashPoint.mockReturnValue({ cashPoints:
|
|
141
|
+
mockUseCashPoint.mockReturnValue({ cashPoints: mockCashPoints, isLoading: false, error: null });
|
|
80
142
|
mockUseBillableItems.mockReturnValue({ lineItems: mockBillableItems, isLoading: false, error: null });
|
|
81
143
|
renderBillingCheckinForm();
|
|
82
144
|
|
|
83
145
|
const paymentTypeSelect = screen.getByRole('group', { name: 'Payment Details' });
|
|
84
146
|
expect(paymentTypeSelect).toBeInTheDocument();
|
|
85
147
|
|
|
148
|
+
// Select "Paying" radio button
|
|
86
149
|
const paymentTypeRadio = screen.getByRole('radio', { name: 'Paying' });
|
|
87
150
|
expect(paymentTypeRadio).toBeInTheDocument();
|
|
88
151
|
await user.click(paymentTypeRadio);
|
|
89
152
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
153
|
+
// Wait for payment methods dropdown to appear and select a payment method
|
|
154
|
+
const paymentMethodsDropdown = await screen.findByRole('combobox', { name: /Payment methods/i });
|
|
155
|
+
expect(paymentMethodsDropdown).toBeInTheDocument();
|
|
156
|
+
await user.click(paymentMethodsDropdown);
|
|
93
157
|
|
|
94
|
-
|
|
158
|
+
// Select "Insurance" payment method
|
|
159
|
+
const insuranceOption = await screen.findByText('Insurance');
|
|
160
|
+
await user.click(insuranceOption);
|
|
161
|
+
|
|
162
|
+
// Now select billable service
|
|
163
|
+
const billableSelect = screen.getByRole('combobox', { name: 'Billable service' });
|
|
164
|
+
expect(billableSelect).toBeInTheDocument();
|
|
165
|
+
await user.click(billableSelect);
|
|
166
|
+
|
|
167
|
+
// Click on Lab Testing option
|
|
168
|
+
const labTestingOption = await screen.findByText(/Lab Testing \(Default:500\.00001\)/);
|
|
169
|
+
await user.click(labTestingOption);
|
|
95
170
|
|
|
96
171
|
expect(testProps.setExtraVisitInfo).toHaveBeenCalled();
|
|
97
172
|
expect(testProps.setExtraVisitInfo).toHaveBeenCalledWith({
|
|
98
173
|
createBillPayload: {
|
|
99
174
|
lineItems: [
|
|
100
175
|
{
|
|
101
|
-
|
|
176
|
+
billableService: 'b47dddd6-4490-4bf7-b694-43bf19d04059',
|
|
102
177
|
quantity: 1,
|
|
103
|
-
price: 500.00001,
|
|
178
|
+
price: '500.00001',
|
|
104
179
|
priceName: 'Default',
|
|
105
|
-
priceUuid: '',
|
|
180
|
+
priceUuid: 'price-2',
|
|
106
181
|
lineItemOrder: 0,
|
|
107
182
|
paymentStatus: 'PENDING',
|
|
108
183
|
},
|
|
109
184
|
],
|
|
110
|
-
cashPoint: '',
|
|
185
|
+
cashPoint: '54065383-b4d4-42d2-af4d-d250a1fd2590',
|
|
111
186
|
patient: 'some-patient-uuid',
|
|
112
187
|
status: 'PENDING',
|
|
113
188
|
payments: [],
|
|
114
189
|
},
|
|
115
190
|
handleCreateExtraVisitInfo: expect.anything(),
|
|
116
|
-
attributes: [
|
|
117
|
-
{
|
|
118
|
-
attributeType: '
|
|
191
|
+
attributes: expect.arrayContaining([
|
|
192
|
+
expect.objectContaining({
|
|
193
|
+
attributeType: 'fbc0702d-b4c9-4968-be63-af8ad3ad6239',
|
|
194
|
+
value: '44b34972-6630-4e5a-a9f6-a6eb0f109650',
|
|
195
|
+
}),
|
|
196
|
+
expect.objectContaining({
|
|
197
|
+
attributeType: '8553afa0-bdb9-4d3c-8a98-05fa9350aa85',
|
|
119
198
|
value: '1c30ee58-82d4-4ea4-a8c1-4bf2f9dfc8cf',
|
|
120
|
-
},
|
|
121
|
-
|
|
122
|
-
attributeType: '919b51c9-8e2e-468f-8354-181bf3e55786',
|
|
123
|
-
value: true,
|
|
124
|
-
},
|
|
125
|
-
],
|
|
199
|
+
}),
|
|
200
|
+
]),
|
|
126
201
|
});
|
|
127
202
|
});
|
|
128
203
|
});
|
|
@@ -60,7 +60,8 @@ const BillingForm: React.FC<BillingFormProps> = ({ patientUuid, closeWorkspace }
|
|
|
60
60
|
let selectedPaymentMethod = null;
|
|
61
61
|
|
|
62
62
|
if (availablePaymentMethods.length === 1) {
|
|
63
|
-
|
|
63
|
+
const price = availablePaymentMethods[0].price;
|
|
64
|
+
defaultPrice = typeof price === 'number' ? price : parseFloat(price);
|
|
64
65
|
selectedPaymentMethod = availablePaymentMethods[0];
|
|
65
66
|
}
|
|
66
67
|
|
|
@@ -95,7 +96,7 @@ const BillingForm: React.FC<BillingFormProps> = ({ patientUuid, closeWorkspace }
|
|
|
95
96
|
? {
|
|
96
97
|
...item,
|
|
97
98
|
selectedPaymentMethod: paymentMethod,
|
|
98
|
-
price: parseFloat(paymentMethod.price),
|
|
99
|
+
price: typeof paymentMethod.price === 'number' ? paymentMethod.price : parseFloat(paymentMethod.price),
|
|
99
100
|
priceName: paymentMethod.name,
|
|
100
101
|
priceUuid: paymentMethod.uuid,
|
|
101
102
|
}
|
|
@@ -156,7 +157,6 @@ const BillingForm: React.FC<BillingFormProps> = ({ patientUuid, closeWorkspace }
|
|
|
156
157
|
title: t('saveBill', 'Save Bill'),
|
|
157
158
|
subtitle: t('billProcessingSuccess', 'Bill processing has been successful'),
|
|
158
159
|
kind: 'success',
|
|
159
|
-
timeoutInMs: 3000,
|
|
160
160
|
});
|
|
161
161
|
},
|
|
162
162
|
(error) => {
|
|
@@ -217,7 +217,10 @@ const BillingForm: React.FC<BillingFormProps> = ({ patientUuid, closeWorkspace }
|
|
|
217
217
|
size="md"
|
|
218
218
|
itemToString={(method: ServicePrice) =>
|
|
219
219
|
method
|
|
220
|
-
? `${method.name} - ${convertToCurrency(
|
|
220
|
+
? `${method.name} - ${convertToCurrency(
|
|
221
|
+
typeof method.price === 'number' ? method.price : parseFloat(method.price),
|
|
222
|
+
defaultCurrency,
|
|
223
|
+
)}`
|
|
221
224
|
: ''
|
|
222
225
|
}
|
|
223
226
|
selectedItem={item.selectedPaymentMethod}
|
|
@@ -81,7 +81,6 @@ const Payments: React.FC<PaymentProps> = ({ bill, mutate }) => {
|
|
|
81
81
|
title: t('billPayment', 'Bill payment'),
|
|
82
82
|
subtitle: 'Bill payment processing has been successful',
|
|
83
83
|
kind: 'success',
|
|
84
|
-
timeoutInMs: 3000,
|
|
85
84
|
});
|
|
86
85
|
if (currentVisit) {
|
|
87
86
|
updateBillVisitAttribute(currentVisit);
|
|
@@ -43,8 +43,8 @@ describe('RequirePaymentModal', () => {
|
|
|
43
43
|
{
|
|
44
44
|
status: 'UNPAID',
|
|
45
45
|
lineItems: [
|
|
46
|
-
{ billableService: 'Service 1', quantity: 1, price: 100 },
|
|
47
|
-
{ 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' },
|
|
48
48
|
],
|
|
49
49
|
},
|
|
50
50
|
];
|
package/src/types/index.ts
CHANGED
|
@@ -177,8 +177,17 @@ export type BillableItem = {
|
|
|
177
177
|
};
|
|
178
178
|
|
|
179
179
|
export type ServicePrice = {
|
|
180
|
-
|
|
181
|
-
|
|
180
|
+
itemPriceId?: number;
|
|
181
|
+
name?: string;
|
|
182
|
+
price: string | number;
|
|
183
|
+
paymentMode?: {
|
|
184
|
+
paymentModeId?: number;
|
|
185
|
+
uuid: string;
|
|
186
|
+
name: string;
|
|
187
|
+
description?: string;
|
|
188
|
+
sortOrder?: number;
|
|
189
|
+
};
|
|
190
|
+
billableService?: BillableService;
|
|
182
191
|
uuid: string;
|
|
183
192
|
};
|
|
184
193
|
|
|
@@ -188,10 +197,9 @@ export interface BillableService {
|
|
|
188
197
|
shortName: string;
|
|
189
198
|
serviceStatus: string;
|
|
190
199
|
serviceType?: {
|
|
200
|
+
uuid: string;
|
|
191
201
|
display: string;
|
|
192
202
|
};
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
price: number;
|
|
196
|
-
}>;
|
|
203
|
+
concept?: ServiceConcept;
|
|
204
|
+
servicePrices: Array<ServicePrice>;
|
|
197
205
|
}
|