@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.
Files changed (30) hide show
  1. package/dist/4300.js +1 -1
  2. package/dist/4724.js +1 -1
  3. package/dist/4724.js.map +1 -1
  4. package/dist/4739.js +1 -1
  5. package/dist/4739.js.map +1 -1
  6. package/dist/942.js +1 -1
  7. package/dist/942.js.map +1 -1
  8. package/dist/main.js +1 -1
  9. package/dist/main.js.map +1 -1
  10. package/dist/openmrs-esm-billing-app.js +1 -1
  11. package/dist/openmrs-esm-billing-app.js.buildmanifest.json +16 -16
  12. package/dist/routes.json +1 -1
  13. package/package.json +1 -1
  14. package/src/bill-item-actions/edit-bill-item.modal.tsx +0 -1
  15. package/src/bill-item-actions/edit-bill-item.test.tsx +0 -1
  16. package/src/billable-services/billable-service.resource.ts +13 -6
  17. package/src/billable-services/billable-services.component.tsx +47 -33
  18. package/src/billable-services/cash-point/cash-point-configuration.component.tsx +2 -1
  19. package/src/billable-services/create-edit/add-billable-service.component.tsx +336 -293
  20. package/src/billable-services/create-edit/add-billable-service.scss +3 -1
  21. package/src/billable-services/create-edit/add-billable-service.test.tsx +36 -35
  22. package/src/billable-services/create-edit/edit-billable-service.modal.tsx +6 -5
  23. package/src/billable-services/payment-modes/payment-modes-config.component.tsx +44 -44
  24. package/src/billable-services/payment-modes/payment-modes-config.scss +0 -3
  25. package/src/billing-form/billing-checkin-form.test.tsx +97 -22
  26. package/src/billing-form/billing-form.component.tsx +7 -4
  27. package/src/invoice/payments/payments.component.tsx +0 -1
  28. package/src/modal/require-payment-modal.test.tsx +2 -2
  29. package/src/types/index.ts +14 -6
  30. 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, navigate } from '@openmrs/esm-framework';
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 = useBillableServices as jest.MockedFunction<typeof useBillableServices>;
14
- const mockUsePaymentModes = usePaymentModes as jest.MockedFunction<typeof usePaymentModes>;
15
- const mockUseServiceTypes = useServiceTypes as jest.MockedFunction<typeof useServiceTypes>;
16
- const mockcreateBillableService = createBillableService as jest.MockedFunction<typeof createBillableService>;
17
- const mockNavigate = navigate as jest.MockedFunction<typeof navigate>;
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
- xdescribe('AddBillableService', () => {
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, isLoading: false });
68
- mockUseServiceTypes.mockReturnValue({ serviceTypes: mockServiceTypes, error: false, isLoading: 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 Type/i });
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
- const addPaymentMethodBtn = screen.getByRole('button', { name: /Add payment option/i });
95
- expect(addPaymentMethodBtn).toBeInTheDocument();
96
-
97
- await user.click(addPaymentMethodBtn);
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 priceTextInp = screen.getByRole('textbox', { name: /Price/i });
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
- mockcreateBillableService.mockReturnValue(Promise.resolve({} as FetchResponse<any>));
111
- const saveBtn = screen.getByRole('button', { name: /Save/i });
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(mockcreateBillableService).toHaveBeenCalledTimes(1);
116
- expect(mockcreateBillableService).toHaveBeenCalledWith({
114
+ expect(mockCreateBillableService).toHaveBeenCalledTimes(1);
115
+ expect(mockCreateBillableService).toHaveBeenCalledWith({
117
116
  name: 'Test Service Name',
118
117
  shortName: 'Test Short Name',
119
- serviceType: undefined,
118
+ serviceType: 'c9604249-db0a-4d03-b074-fc6bc2fa13e6',
120
119
  servicePrices: [
121
120
  {
122
121
  paymentMode: '63eff7a4-6f82-43c4-a333-dbcc58fe9f74',
123
- price: '01000',
122
+ price: 1000,
124
123
  name: 'Cash',
125
124
  },
126
125
  ],
127
126
  serviceStatus: 'ENABLED',
127
+ concept: undefined,
128
128
  });
129
- expect(mockNavigate).toHaveBeenCalledTimes(1);
130
- expect(mockNavigate).toHaveBeenCalledWith({ to: '/openmrs/spa/billable-services' });
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, isLoading: false });
144
- mockUseServiceTypes.mockReturnValue({ serviceTypes: mockServiceTypes, error: false, isLoading: 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.getByRole('button', { name: /Cancel/i });
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
- editingService,
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
- editingService={editingService}
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
- <div className={styles.historyContainer}>
78
- <DataTable rows={rowData} headers={headerData} isSortable size="lg">
79
- {({ rows, headers, getTableProps, getHeaderProps, getRowProps }) => (
80
- <TableContainer>
81
- <Table className={styles.table} {...getTableProps()}>
82
- <TableHead>
83
- <TableRow>
84
- {headers.map((header) => (
85
- <TableHeader key={header.key} {...getHeaderProps({ header })}>
86
- {header.header}
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
- </TableBody>
115
- </Table>
116
- </TableContainer>
117
- )}
118
- </DataTable>
119
- </div>
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
  );
@@ -13,9 +13,6 @@
13
13
  padding: layout.$spacing-05;
14
14
  }
15
15
 
16
- .historyContainer {
17
- margin-top: layout.$spacing-05;
18
- }
19
16
 
20
17
  .table {
21
18
  width: 100%;
@@ -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 { useBillableItems, useCashPoint, createPatientBill, usePaymentMethods } from './billing-form.resource';
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 mockUseCashPoint = useCashPoint as jest.MockedFunction<typeof useCashPoint>;
44
- const mockUseBillableItems = useBillableItems as jest.MockedFunction<typeof useBillableItems>;
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
- xdescribe('BillingCheckInForm', () => {
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: [], isLoading: false, error: null });
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
- const billiableSelect = screen.getByRole('combobox', { name: 'Billable service' });
91
- expect(billiableSelect).toBeInTheDocument();
92
- await user.click(screen.getByRole('combobox', { name: 'Billable service' }));
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
- await user.click(screen.getByText('Lab Testing'));
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
- item: 'b47dddd6-4490-4bf7-b694-43bf19d04059',
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: 'caf2124f-00a9-4620-a250-efd8535afd6d',
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
- defaultPrice = parseFloat(availablePaymentMethods[0].price);
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(parseFloat(method.price), defaultCurrency)}`
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
  ];
@@ -177,8 +177,17 @@ export type BillableItem = {
177
177
  };
178
178
 
179
179
  export type ServicePrice = {
180
- name: string;
181
- price: string;
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
- servicePrices: Array<{
194
- name: string;
195
- price: number;
196
- }>;
203
+ concept?: ServiceConcept;
204
+ servicePrices: Array<ServicePrice>;
197
205
  }