@openmrs/esm-stock-management-app 3.0.1-pre.853 → 3.0.1-pre.855
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/.husky/pre-commit +1 -4
- package/__mocks__/react-i18next.js +9 -8
- package/dist/10.js +1 -1
- package/dist/10.js.map +1 -1
- package/dist/119.js +1 -1
- package/dist/119.js.map +1 -1
- package/dist/14.js +1 -1
- package/dist/14.js.map +1 -1
- package/dist/172.js +1 -1
- package/dist/172.js.map +1 -1
- package/dist/20.js +1 -1
- package/dist/20.js.map +1 -1
- package/dist/290.js +1 -1
- package/dist/290.js.map +1 -1
- package/dist/467.js +1 -1
- package/dist/467.js.map +1 -1
- package/dist/574.js +1 -1
- package/dist/606.js +1 -1
- package/dist/606.js.map +1 -1
- package/dist/627.js +1 -0
- package/dist/627.js.map +1 -0
- package/dist/642.js +1 -1
- package/dist/642.js.map +1 -1
- package/dist/675.js +1 -1
- package/dist/675.js.map +1 -1
- package/dist/727.js +1 -1
- package/dist/727.js.map +1 -1
- package/dist/842.js +1 -1
- package/dist/842.js.map +1 -1
- package/dist/93.js +1 -1
- package/dist/93.js.map +1 -1
- package/dist/main.js +1 -1
- package/dist/main.js.map +1 -1
- package/dist/openmrs-esm-stock-management-app.js.buildmanifest.json +70 -70
- package/dist/routes.json +1 -1
- package/jest.config.js +3 -6
- package/package.json +1 -1
- package/src/core/components/table/table.component.tsx +2 -2
- package/src/index.ts +5 -5
- package/src/stock-items/add-bulk-stock-item/add-stock-items-bulk-import-action-button.component.tsx +3 -3
- package/src/stock-items/add-bulk-stock-item/{stock-items-bulk-import.modal.tsx → stock-items-bulk-import.component.tsx} +19 -20
- package/src/stock-items/add-bulk-stock-item/stock-items-bulk-import.resource.ts +1 -1
- package/src/stock-items/add-bulk-stock-item/stock-items-bulk-import.test.tsx +59 -59
- package/src/stock-items/add-stock-item/add-stock-action-button.component.tsx +6 -6
- package/src/stock-items/add-stock-item/add-stock-item.component.tsx +4 -6
- package/src/stock-items/add-stock-item/add-stock-item.scss +0 -5
- package/src/stock-items/add-stock-item/add-stock-item.test.tsx +43 -28
- package/src/stock-items/add-stock-item/packaging-units/packaging-units-delete-modal.component.tsx +4 -3
- package/src/stock-items/add-stock-item/packaging-units/packaging-units.component.tsx +10 -9
- package/src/stock-items/add-stock-item/packaging-units/packaging-units.scss +4 -4
- package/src/stock-items/add-stock-item/stock-item-details/stock-item-details.component.tsx +19 -27
- package/src/stock-items/add-stock-item/stock-item-references/stock-item-references.scss +4 -4
- package/src/stock-items/add-stock-item/stock-item-rules/add-stock-rules.component.tsx +9 -15
- package/src/stock-items/add-stock-item/stock-item-rules/add-stock-rules.scss +0 -1
- package/src/stock-items/add-stock-item/stock-item-rules/delete-stock-rule-modal.component.tsx +1 -2
- package/src/stock-items/add-stock-item/stock-item-rules/stock-item-rules.component.tsx +16 -14
- package/src/stock-items/add-stock-item/stock-item-rules/stock-item-rules.scss +3 -7
- package/src/stock-items/add-stock-item/transactions/printout/transactions-print-bincard-preview.modal.tsx +6 -14
- package/src/stock-items/add-stock-item/transactions/printout/transactions-print-stockcard-preview.modal.tsx +8 -14
- package/src/stock-items/edit-stock-item/edit-stock-item-action-menu.component.tsx +2 -2
- package/src/stock-items/stock-item.utils.tsx +5 -3
- package/src/stock-items/stock-items-table.component.tsx +45 -47
- package/src/stock-items/stock-items-table.resource.ts +2 -2
- package/src/stock-items/stock-items-table.scss +1 -5
- package/src/stock-items/stock-items-table.test.tsx +65 -106
- package/src/stock-locations/location-admin-form.component.tsx +4 -5
- package/src/stock-locations/stock-locations-table.component.tsx +8 -10
- package/src/stock-lookups/stock-lookups.resource.ts +2 -3
- package/src/stock-operations/stock-operations-dialog/stock-operations-dialog.component.tsx +2 -2
- package/src/stock-operations/stock-operations-forms/input-components/batch-no-selector.component.tsx +11 -11
- package/src/stock-operations/stock-operations-forms/input-components/batch-no-selector.test.tsx +25 -115
- package/src/stock-operations/stock-operations-forms/input-components/qty-uim-selector.test.tsx +65 -107
- package/src/stock-operations/stock-operations-forms/input-components/quantity-uom-selector.component.tsx +9 -9
- package/src/stock-operations/stock-operations-forms/input-components/stock-operation-reason-selector.test.tsx +153 -35
- package/src/stock-operations/stock-operations-forms/input-components/user-selector.test.tsx +29 -82
- package/src/stock-operations/stock-operations-forms/step1.test.tsx +69 -204
- package/src/stock-operations/stock-operations-forms/step2.test.tsx +63 -140
- package/src/stock-operations/stock-operations-forms/step3.test.tsx +60 -79
- package/src/stock-operations/stock-operations-forms/steps/stock-operation-items-form-step.component.tsx +5 -6
- package/src/stock-operations/stock-operations-forms/steps/stock-operation-submission-form-step.component.tsx +11 -12
- package/src/stock-operations/stock-operations-forms/stock-item-form/stock-item-form.scss +0 -1
- package/src/stock-operations/stock-operations-forms/stock-item-form/stock-item-form.workspace.tsx +12 -20
- package/src/stock-operations/stock-operations-forms/stock-operation-form.scss +0 -1
- package/src/stock-operations/stock-operations-forms/stock-operation-stepper/stepper.scss +3 -1
- package/src/stock-operations/stock-operations-forms/stock-operation-stepper/stock-operation-stepper.component.tsx +1 -2
- package/src/stock-reports/generate-report/create-stock-report.scss +2 -3
- package/src/stock-reports/generate-report/create-stock-report.workspace.tsx +25 -32
- package/src/stock-reports/report-list/stock-report-parameters.component.tsx +1 -1
- package/src/stock-reports/report-list/stock-report-status.component.tsx +1 -1
- package/src/stock-reports/report-list/stock-reports.component.tsx +25 -24
- package/src/stock-reports/report-list/stock-reports.scss +2 -10
- package/src/stock-sources/add-stock-sources/add-stock-sources.scss +4 -11
- package/src/stock-sources/add-stock-sources/add-stock-sources.test.tsx +36 -38
- package/src/stock-sources/add-stock-sources/add-stock-sources.workspace.tsx +30 -35
- package/src/stock-sources/delete-stock-modal.component.tsx +1 -2
- package/src/stock-sources/stock-sources-delete/stock-sources-delete.test.tsx +36 -27
- package/src/stock-sources/stock-sources-filter/stock-sources-filter.component.tsx +21 -33
- package/src/stock-sources/stock-sources-items-table.component.tsx +17 -16
- package/src/stock-sources/stock-sources-items-table.resource.ts +6 -8
- package/src/stock-sources/stock-sources-items-table.test.tsx +37 -60
- package/src/stock-sources/stock-sources.scss +2 -6
- package/src/stock-user-role-scopes/add-stock-user-scope/add-stock-user-role-scope.scss +13 -5
- package/src/stock-user-role-scopes/add-stock-user-scope/add-stock-user-role-scope.workspace.tsx +2 -2
- package/src/stock-user-role-scopes/delete-stock-user-scope-modal.component.tsx +1 -2
- package/translations/en.json +6 -5
- package/tsconfig.json +0 -4
- package/dist/33.js +0 -1
- package/dist/33.js.map +0 -1
@@ -1,13 +1,10 @@
|
|
1
1
|
import React from 'react';
|
2
|
+
import { render, screen, waitFor, within } from '@testing-library/react';
|
2
3
|
import userEvent from '@testing-library/user-event';
|
3
|
-
import { render, screen, waitFor } from '@testing-library/react';
|
4
|
-
import { type StockItemDTO } from '../core/api/types/stockItem/StockItem';
|
5
|
-
import { handleMutate } from '../utils';
|
6
|
-
import { launchAddOrEditStockItemWorkspace } from './stock-item.utils';
|
7
|
-
import { useStockItemsPages } from './stock-items-table.resource';
|
8
4
|
import StockItemsTableComponent from './stock-items-table.component';
|
9
|
-
|
10
|
-
|
5
|
+
import { useStockItemsPages } from './stock-items-table.resource';
|
6
|
+
import { handleMutate } from '../utils';
|
7
|
+
import { launchAddOrStockItemWorkspace } from './stock-item.utils';
|
11
8
|
|
12
9
|
jest.mock('./stock-items-table.resource', () => ({
|
13
10
|
useStockItemsPages: jest.fn(),
|
@@ -18,146 +15,109 @@ jest.mock('../utils', () => ({
|
|
18
15
|
}));
|
19
16
|
|
20
17
|
jest.mock('./stock-item.utils', () => ({
|
21
|
-
|
18
|
+
launchAddOrStockItemWorkspace: jest.fn(),
|
19
|
+
}));
|
20
|
+
|
21
|
+
jest.mock('react-i18next', () => ({
|
22
|
+
useTranslation: () => ({
|
23
|
+
t: (key: string) => key,
|
24
|
+
}),
|
22
25
|
}));
|
23
26
|
|
24
27
|
describe('StockItemsTableComponent', () => {
|
28
|
+
const mockUseStockItemsPages = {
|
29
|
+
isLoading: false,
|
30
|
+
items: Array(25)
|
31
|
+
.fill(null)
|
32
|
+
.map((_, index) => ({
|
33
|
+
uuid: `item-${index}`,
|
34
|
+
commonName: `Test Item ${index}`,
|
35
|
+
drugUuid: index % 2 === 0 ? `drug-${index}` : null,
|
36
|
+
conceptName: `Concept ${index}`,
|
37
|
+
dispensingUnitName: `Unit ${index}`,
|
38
|
+
defaultStockOperationsUoMName: `UoM ${index}`,
|
39
|
+
reorderLevel: index * 10,
|
40
|
+
reorderLevelUoMName: 'Units',
|
41
|
+
})),
|
42
|
+
totalCount: 25,
|
43
|
+
currentPageSize: 10,
|
44
|
+
setPageSize: jest.fn(),
|
45
|
+
pageSizes: [10, 20, 30],
|
46
|
+
currentPage: 1,
|
47
|
+
setCurrentPage: jest.fn(),
|
48
|
+
isDrug: '',
|
49
|
+
setDrug: jest.fn(),
|
50
|
+
setSearchString: jest.fn(),
|
51
|
+
};
|
52
|
+
|
25
53
|
beforeEach(() => {
|
26
|
-
|
27
|
-
|
28
|
-
items: Array(25)
|
29
|
-
.fill(null)
|
30
|
-
.map((_, index) => ({
|
31
|
-
uuid: `item-${index}`,
|
32
|
-
commonName: `Test Item ${index}`,
|
33
|
-
drugUuid: index % 2 === 0 ? `drug-${index}` : null,
|
34
|
-
conceptName: `Concept ${index}`,
|
35
|
-
dispensingUnitName: `Unit ${index}`,
|
36
|
-
defaultStockOperationsUoMName: `UoM ${index}`,
|
37
|
-
reorderLevel: index * 10,
|
38
|
-
reorderLevelUoMName: 'Units',
|
39
|
-
})) as StockItemDTO[],
|
40
|
-
pagination: {
|
41
|
-
results: [],
|
42
|
-
totalPages: 3,
|
43
|
-
currentPage: 1,
|
44
|
-
paginated: true,
|
45
|
-
showNextButton: true,
|
46
|
-
showPreviousButton: false,
|
47
|
-
goTo: jest.fn(),
|
48
|
-
goToNext: jest.fn(),
|
49
|
-
goToPrevious: jest.fn(),
|
50
|
-
},
|
51
|
-
error: null,
|
52
|
-
totalCount: 25,
|
53
|
-
currentPageSize: 10,
|
54
|
-
setPageSize: jest.fn(),
|
55
|
-
pageSizes: [10, 20, 30],
|
56
|
-
currentPage: 1,
|
57
|
-
setCurrentPage: jest.fn(),
|
58
|
-
isDrug: '',
|
59
|
-
setDrug: jest.fn(),
|
60
|
-
setSearchString: jest.fn(),
|
61
|
-
});
|
54
|
+
jest.clearAllMocks();
|
55
|
+
(useStockItemsPages as jest.Mock).mockReturnValue(mockUseStockItemsPages);
|
62
56
|
});
|
63
57
|
|
64
58
|
it('renders initial state and UI elements correctly', async () => {
|
59
|
+
render(<StockItemsTableComponent />);
|
60
|
+
expect(screen.getByText('panelDescription')).toBeInTheDocument();
|
61
|
+
expect(screen.getByRole('searchbox')).toBeInTheDocument();
|
62
|
+
|
65
63
|
const user = userEvent.setup();
|
64
|
+
const menuButton = screen.getByTestId('stock-items-menu');
|
65
|
+
await user.click(menuButton);
|
66
66
|
|
67
|
-
|
67
|
+
await screen.findByText('Refresh');
|
68
68
|
|
69
|
-
expect(screen.
|
70
|
-
expect(screen.
|
71
|
-
expect(screen.
|
72
|
-
expect(screen.getByRole('button', { name: /import/i })).toBeInTheDocument();
|
73
|
-
expect(screen.getByRole('button', { name: /settings/i })).toBeInTheDocument();
|
74
|
-
expect(screen.getByRole('searchbox', { name: /filter table/i })).toBeInTheDocument();
|
75
|
-
|
76
|
-
const settingsMenuButton = screen.getByRole('button', { name: /settings/i });
|
77
|
-
await user.click(settingsMenuButton);
|
78
|
-
await screen.findByText(/refresh/i);
|
79
|
-
|
80
|
-
expect(screen.getByRole('button', { name: /generic name/i })).toBeInTheDocument();
|
81
|
-
expect(screen.getByRole('button', { name: /common name/i })).toBeInTheDocument();
|
82
|
-
expect(screen.getByRole('button', { name: /dispensing uom/i })).toBeInTheDocument();
|
83
|
-
expect(screen.getByRole('button', { name: /bulk packaging/i })).toBeInTheDocument();
|
84
|
-
expect(screen.getByRole('button', { name: /reorder level/i })).toBeInTheDocument();
|
69
|
+
expect(screen.getByText('type')).toBeInTheDocument();
|
70
|
+
expect(screen.getByText('genericName')).toBeInTheDocument();
|
71
|
+
expect(screen.getByText('commonName')).toBeInTheDocument();
|
85
72
|
});
|
86
73
|
|
87
74
|
it('displays skeleton loader when isLoading is true', () => {
|
88
|
-
|
89
|
-
...mockUseStockItemsPages,
|
90
|
-
isLoading: true,
|
91
|
-
items: [],
|
92
|
-
pagination: undefined,
|
93
|
-
totalCount: 0,
|
94
|
-
currentPageSize: 0,
|
95
|
-
currentPage: 0,
|
96
|
-
setCurrentPage: function (value: React.SetStateAction<number>): void {
|
97
|
-
throw new Error('Function not implemented.');
|
98
|
-
},
|
99
|
-
setPageSize: function (value: React.SetStateAction<number>): void {
|
100
|
-
throw new Error('Function not implemented.');
|
101
|
-
},
|
102
|
-
pageSizes: [],
|
103
|
-
error: undefined,
|
104
|
-
isDrug: '',
|
105
|
-
setDrug: undefined,
|
106
|
-
setSearchString: function (value: any): void {
|
107
|
-
throw new Error('Function not implemented.');
|
108
|
-
},
|
109
|
-
});
|
110
|
-
|
75
|
+
(useStockItemsPages as jest.Mock).mockReturnValue({ ...mockUseStockItemsPages, isLoading: true });
|
111
76
|
render(<StockItemsTableComponent />);
|
112
|
-
|
113
77
|
expect(screen.getByRole('progressbar')).toBeInTheDocument();
|
114
78
|
});
|
115
79
|
|
116
80
|
it('renders table rows correctly based on items prop', () => {
|
117
81
|
render(<StockItemsTableComponent />);
|
118
|
-
|
119
|
-
expect(screen.
|
120
|
-
expect(screen.
|
121
|
-
expect(screen.getAllByRole('cell', { name: /other/i }).length).toBeGreaterThan(0);
|
82
|
+
expect(screen.getByText('Test Item 0')).toBeInTheDocument();
|
83
|
+
expect(screen.getAllByText('drug').length).toBeGreaterThan(0);
|
84
|
+
expect(screen.getAllByText('other').length).toBeGreaterThan(0);
|
122
85
|
});
|
123
86
|
|
124
87
|
it('updates search input and triggers search function', async () => {
|
125
|
-
const user = userEvent.setup();
|
126
|
-
|
127
88
|
render(<StockItemsTableComponent />);
|
128
|
-
|
89
|
+
const user = userEvent.setup();
|
129
90
|
const searchInput = screen.getByRole('searchbox');
|
130
91
|
await user.type(searchInput, 'test search');
|
131
92
|
await waitFor(
|
132
93
|
() => {
|
133
|
-
expect(mockUseStockItemsPages
|
94
|
+
expect(mockUseStockItemsPages.setSearchString).toHaveBeenCalledWith('test search');
|
134
95
|
},
|
135
96
|
{ timeout: 2000 },
|
136
97
|
);
|
137
98
|
});
|
138
99
|
|
139
100
|
it('updates pagination when page or page size changes', async () => {
|
140
|
-
const user = userEvent.setup();
|
141
101
|
render(<StockItemsTableComponent />);
|
142
|
-
|
143
|
-
const nextPageButton = screen.getByLabelText(
|
102
|
+
const user = userEvent.setup();
|
103
|
+
const nextPageButton = screen.getByLabelText('Next page');
|
144
104
|
await user.click(nextPageButton);
|
145
|
-
expect(mockUseStockItemsPages
|
105
|
+
expect(mockUseStockItemsPages.setCurrentPage).toHaveBeenCalled();
|
146
106
|
|
147
|
-
const pageSizeSelect = screen.getByLabelText(
|
107
|
+
const pageSizeSelect = screen.getByLabelText('Items per page:');
|
148
108
|
await user.selectOptions(pageSizeSelect, '20');
|
149
|
-
expect(mockUseStockItemsPages
|
109
|
+
expect(mockUseStockItemsPages.setPageSize).toHaveBeenCalledWith(20);
|
150
110
|
});
|
151
111
|
|
152
112
|
it('triggers handleRefresh when refresh button is clicked', async () => {
|
153
|
-
const user = userEvent.setup();
|
154
113
|
render(<StockItemsTableComponent />);
|
155
114
|
|
115
|
+
const user = userEvent.setup();
|
156
116
|
const menuButton = screen.getByTestId('stock-items-menu');
|
157
117
|
expect(menuButton).toBeInTheDocument();
|
158
118
|
await user.click(menuButton);
|
159
119
|
|
160
|
-
const refreshButton = await screen.findByText(
|
120
|
+
const refreshButton = await screen.findByText('Refresh');
|
161
121
|
expect(refreshButton).toBeInTheDocument();
|
162
122
|
await user.click(refreshButton);
|
163
123
|
await waitFor(() => {
|
@@ -165,14 +125,13 @@ describe('StockItemsTableComponent', () => {
|
|
165
125
|
});
|
166
126
|
});
|
167
127
|
|
168
|
-
it('triggers
|
169
|
-
const user = userEvent.setup();
|
128
|
+
it('triggers launchAddOrEditDialog when edit button is clicked', async () => {
|
170
129
|
render(<StockItemsTableComponent />);
|
171
|
-
|
172
|
-
const editButtons = screen.getAllByLabelText(
|
130
|
+
const user = userEvent.setup();
|
131
|
+
const editButtons = screen.getAllByLabelText('Edit Stock Item');
|
173
132
|
await user.click(editButtons[0]);
|
174
133
|
|
175
|
-
expect(
|
134
|
+
expect(launchAddOrStockItemWorkspace).toHaveBeenCalledWith(
|
176
135
|
expect.any(Function),
|
177
136
|
expect.objectContaining({ uuid: 'item-0' }),
|
178
137
|
);
|
@@ -5,20 +5,19 @@ import { zodResolver } from '@hookform/resolvers/zod';
|
|
5
5
|
import {
|
6
6
|
Button,
|
7
7
|
ComposedModal,
|
8
|
-
FilterableMultiSelect,
|
9
8
|
FormGroup,
|
10
|
-
InlineNotification,
|
11
9
|
ModalBody,
|
12
10
|
ModalFooter,
|
13
11
|
ModalHeader,
|
14
12
|
Stack,
|
15
13
|
TextInput,
|
14
|
+
InlineNotification,
|
15
|
+
FilterableMultiSelect,
|
16
16
|
} from '@carbon/react';
|
17
|
-
import { getCoreTranslation } from '@openmrs/esm-framework';
|
18
17
|
import { useTranslation } from 'react-i18next';
|
19
18
|
import { type locationData } from '../stock-items/types';
|
20
|
-
import { useLocationTags } from './stock-locations-table.resource';
|
21
19
|
import styles from './stock-locations-table.scss';
|
20
|
+
import { useLocationTags } from './stock-locations-table.resource';
|
22
21
|
|
23
22
|
const LocationAdministrationSchema = z.object({
|
24
23
|
name: z.string().max(255),
|
@@ -147,7 +146,7 @@ const LocationAdministrationForm: React.FC<LocationAdministrationFormProps> = ({
|
|
147
146
|
</ModalBody>
|
148
147
|
<ModalFooter>
|
149
148
|
<Button onClick={() => onModalChange(false)} kind="secondary">
|
150
|
-
{
|
149
|
+
{t('cancel', 'Cancel')}
|
151
150
|
</Button>
|
152
151
|
<Button disabled={!isDirty} type="submit">
|
153
152
|
<span>{t('save', 'Save')}</span>
|
@@ -1,4 +1,5 @@
|
|
1
1
|
import React, { useState } from 'react';
|
2
|
+
import { useStockLocationPages } from './stock-locations-table.resource';
|
2
3
|
import {
|
3
4
|
Button,
|
4
5
|
DataTableSkeleton,
|
@@ -7,16 +8,15 @@ import {
|
|
7
8
|
TableToolbarSearch,
|
8
9
|
Tile,
|
9
10
|
} from '@carbon/react';
|
10
|
-
import
|
11
|
-
import { useTranslation } from 'react-i18next';
|
12
|
-
import { mutate } from 'swr';
|
13
|
-
import { restBaseUrl } from '@openmrs/esm-framework';
|
14
|
-
import { handleMutate } from '../utils';
|
11
|
+
import styles from '../stock-items/stock-items-table.scss';
|
15
12
|
import { ResourceRepresentation } from '../core/api/api';
|
16
|
-
import { useStockLocationPages } from './stock-locations-table.resource';
|
17
13
|
import DataList from '../core/components/table/table.component';
|
14
|
+
import { useTranslation } from 'react-i18next';
|
15
|
+
import { mutate } from 'swr';
|
18
16
|
import NewLocationForm from './add-locations-form.workspace';
|
19
|
-
import
|
17
|
+
import { Add } from '@carbon/react/icons';
|
18
|
+
import { handleMutate } from '../utils';
|
19
|
+
import { restBaseUrl } from '@openmrs/esm-framework';
|
20
20
|
|
21
21
|
interface StockLocationsTableProps {
|
22
22
|
status?: string;
|
@@ -46,9 +46,7 @@ const StockLocationsItems: React.FC<StockLocationsTableProps> = () => {
|
|
46
46
|
<>
|
47
47
|
<TableToolbarSearch persistent onChange={onInputChange} />
|
48
48
|
<TableToolbarMenu>
|
49
|
-
<TableToolbarAction
|
50
|
-
{t('refresh', 'Refresh')}
|
51
|
-
</TableToolbarAction>
|
49
|
+
<TableToolbarAction onClick={handleRefresh}>Refresh</TableToolbarAction>
|
52
50
|
</TableToolbarMenu>
|
53
51
|
{showLocationModal ? (
|
54
52
|
<NewLocationForm onModalChange={setAddLocationModal} showModal={showLocationModal} mutate={mutate} />
|
@@ -190,10 +190,9 @@ export function useConcept(conceptUuid: string) {
|
|
190
190
|
data: Concept;
|
191
191
|
},
|
192
192
|
Error
|
193
|
-
>(
|
194
|
-
|
193
|
+
>(apiUrl, openmrsFetch);
|
195
194
|
return {
|
196
|
-
items: data?.data,
|
195
|
+
items: data?.data || <Concept>{},
|
197
196
|
isLoading,
|
198
197
|
error,
|
199
198
|
};
|
@@ -8,7 +8,7 @@ import {
|
|
8
8
|
type StopOperationActionType,
|
9
9
|
} from '../../core/api/types/stockOperation/StockOperationAction';
|
10
10
|
import { executeStockOperationAction } from '../stock-operations.resource';
|
11
|
-
import {
|
11
|
+
import { restBaseUrl, showSnackbar } from '@openmrs/esm-framework';
|
12
12
|
import { closeOverlay } from '../../core/components/overlay/hook';
|
13
13
|
import { extractErrorMessagesFromResponse } from '../../constants';
|
14
14
|
import { handleMutate } from '../../utils';
|
@@ -135,7 +135,7 @@ const StockOperationDialog: React.FC<StockOperationDialogProps> = ({ title, requ
|
|
135
135
|
</ModalBody>
|
136
136
|
<ModalFooter>
|
137
137
|
<Button kind="secondary" onClick={closeModal}>
|
138
|
-
{
|
138
|
+
{t('cancel', 'Cancel')}
|
139
139
|
</Button>
|
140
140
|
{isApproving ? <InlineLoading /> : <Button type="submit">{t('submit', 'Submit')}</Button>}
|
141
141
|
</ModalFooter>
|
package/src/stock-operations/stock-operations-forms/input-components/batch-no-selector.component.tsx
CHANGED
@@ -1,10 +1,10 @@
|
|
1
|
+
import { ComboBox, SelectSkeleton } from '@carbon/react';
|
1
2
|
import React, { useEffect, useMemo } from 'react';
|
2
3
|
import { useTranslation } from 'react-i18next';
|
3
|
-
import { ComboBox, SelectSkeleton } from '@carbon/react';
|
4
4
|
import { type StockBatchDTO } from '../../../core/api/types/stockItem/StockBatchDTO';
|
5
|
-
import { formatForDatePicker } from '../../../constants';
|
6
5
|
import { useStockItemBatchInformationHook } from '../../../stock-items/add-stock-item/batch-information/batch-information.resource';
|
7
6
|
import { useStockItemBatchNumbers } from '../hooks/useStockItemBatchNumbers';
|
7
|
+
import { formatForDatePicker } from '../../../constants';
|
8
8
|
|
9
9
|
interface BatchNoSelectorProps {
|
10
10
|
stockItemUuid: string;
|
@@ -48,23 +48,23 @@ const BatchNoSelector: React.FC<BatchNoSelectorProps> = ({ stockItemUuid, error,
|
|
48
48
|
|
49
49
|
return (
|
50
50
|
<ComboBox
|
51
|
+
style={{ flexGrow: '1' }}
|
52
|
+
titleText={t('batchNo', 'Batch')}
|
53
|
+
name={'stockBatchUuid'}
|
51
54
|
id={'stockBatchUuid'}
|
52
|
-
invalid={!!error}
|
53
|
-
invalidText={error}
|
54
55
|
items={filteredBatches || []}
|
56
|
+
onChange={(data: { selectedItem?: StockBatchDTO }) => {
|
57
|
+
onValueChange(data.selectedItem?.uuid);
|
58
|
+
}}
|
59
|
+
selectedItem={initialSelectedItem}
|
55
60
|
itemToString={(s: StockBatchDTO) =>
|
56
61
|
s?.batchNo
|
57
62
|
? `${s?.batchNo} | Qty: ${s?.quantity ?? 'Unknown'} | Expiry: ${formatForDatePicker(s.expiration)}`
|
58
63
|
: ''
|
59
64
|
}
|
60
|
-
name={'stockBatchUuid'}
|
61
|
-
onChange={(data: { selectedItem?: StockBatchDTO }) => {
|
62
|
-
onValueChange(data.selectedItem?.uuid);
|
63
|
-
}}
|
64
65
|
placeholder={t('filter', "'Filter") + '...'}
|
65
|
-
|
66
|
-
|
67
|
-
titleText={t('batchNo', 'Batch no.')}
|
66
|
+
invalid={error}
|
67
|
+
invalidText={error}
|
68
68
|
/>
|
69
69
|
);
|
70
70
|
};
|
package/src/stock-operations/stock-operations-forms/input-components/batch-no-selector.test.tsx
CHANGED
@@ -1,127 +1,64 @@
|
|
1
|
-
import React from 'react';
|
2
|
-
import userEvent from '@testing-library/user-event';
|
3
1
|
import { render, screen } from '@testing-library/react';
|
4
|
-
import
|
2
|
+
import userEvent from '@testing-library/user-event';
|
3
|
+
import React from 'react';
|
5
4
|
import { type StockItemInventory } from '../../../core/api/types/stockItem/StockItemInventory';
|
6
|
-
import { formatForDatePicker } from '../../../constants';
|
7
5
|
import { useStockItemBatchInformationHook } from '../../../stock-items/add-stock-item/batch-information/batch-information.resource';
|
8
6
|
import { useStockItemBatchNumbers } from '../hooks/useStockItemBatchNumbers';
|
9
7
|
import BatchNoSelector from './batch-no-selector.component';
|
8
|
+
import { formatForDatePicker } from '../../../constants';
|
10
9
|
|
11
|
-
jest.mock('../hooks/useStockItemBatchNumbers'
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
jest.mock('../../../stock-items/add-stock-item/batch-information/batch-information.resource', () => ({
|
16
|
-
useStockItemBatchInformationHook: jest.fn(),
|
10
|
+
jest.mock('../hooks/useStockItemBatchNumbers');
|
11
|
+
jest.mock('../../../stock-items/add-stock-item/batch-information/batch-information.resource');
|
12
|
+
jest.mock('react-i18next', () => ({
|
13
|
+
useTranslation: () => ({ t: (key: string) => key }),
|
17
14
|
}));
|
18
15
|
|
19
|
-
const mockUseStockItemBatchNumbers = jest.
|
20
|
-
const mockUseStockItemBatchInformationHook = jest.
|
16
|
+
const mockUseStockItemBatchNumbers = useStockItemBatchNumbers as jest.Mock;
|
17
|
+
const mockUseStockItemBatchInformationHook = useStockItemBatchInformationHook as jest.Mock;
|
21
18
|
|
22
19
|
describe('BatchNoSelector', () => {
|
23
20
|
const mockOnValueChange = jest.fn();
|
24
21
|
const mockStockItemUuid = 'test-uuid';
|
25
22
|
const mockExpiration = new Date();
|
26
|
-
|
27
23
|
beforeEach(() => {
|
24
|
+
jest.clearAllMocks();
|
25
|
+
|
28
26
|
mockUseStockItemBatchNumbers.mockReturnValue({
|
29
27
|
isLoading: false,
|
30
28
|
stockItemBatchNos: [
|
31
|
-
{
|
32
|
-
|
33
|
-
batchNo: 'BATCH-001',
|
34
|
-
quantity: '10',
|
35
|
-
expiration: mockExpiration,
|
36
|
-
stockItemUuid: 'item1',
|
37
|
-
voided: false,
|
38
|
-
},
|
39
|
-
{
|
40
|
-
uuid: '2',
|
41
|
-
batchNo: 'BATCH-002',
|
42
|
-
quantity: '20',
|
43
|
-
expiration: mockExpiration,
|
44
|
-
stockItemUuid: 'item2',
|
45
|
-
voided: false,
|
46
|
-
},
|
29
|
+
{ uuid: '1', batchNo: 'BATCH-001', quantity: 10, expiration: mockExpiration },
|
30
|
+
{ uuid: '2', batchNo: 'BATCH-002', quantity: 20, expiration: mockExpiration },
|
47
31
|
],
|
48
|
-
setLimit: jest.fn(),
|
49
|
-
setRepresentation: jest.fn(),
|
50
|
-
setSearchString: jest.fn(),
|
51
32
|
});
|
52
|
-
|
53
33
|
mockUseStockItemBatchInformationHook.mockReturnValue({
|
54
34
|
items: [
|
55
35
|
{ batchNumber: 'BATCH-001', quantity: 10 },
|
56
36
|
{ batchNumber: 'BATCH-002', quantity: 20 },
|
57
37
|
] as StockItemInventory[],
|
58
|
-
totalCount: 2,
|
59
|
-
currentPage: 1,
|
60
|
-
currentPageSize: 10,
|
61
|
-
setCurrentPage: jest.fn(),
|
62
|
-
setStockBatchUuid: jest.fn(),
|
63
38
|
setStockItemUuid: jest.fn(),
|
64
|
-
setPageSize: jest.fn(),
|
65
|
-
pageSizes: [10, 20, 50],
|
66
|
-
isLoading: false,
|
67
|
-
error: null,
|
68
|
-
setLocationUuid: jest.fn(),
|
69
|
-
setPartyUuid: jest.fn(),
|
70
|
-
setSearchString: jest.fn(),
|
71
39
|
});
|
72
40
|
});
|
73
41
|
|
74
|
-
it('should render
|
75
|
-
mockUseStockItemBatchNumbers.mockReturnValue({
|
76
|
-
|
77
|
-
stockItemBatchNos: [] as StockBatchDTO[],
|
78
|
-
setLimit: jest.fn(),
|
79
|
-
setRepresentation: jest.fn(),
|
80
|
-
setSearchString: jest.fn(),
|
81
|
-
});
|
82
|
-
|
83
|
-
mockUseStockItemBatchInformationHook.mockReturnValue({
|
84
|
-
isLoading: true,
|
85
|
-
items: [],
|
86
|
-
setStockItemUuid: jest.fn(),
|
87
|
-
totalCount: 0,
|
88
|
-
currentPage: 1,
|
89
|
-
currentPageSize: 10,
|
90
|
-
setCurrentPage: jest.fn(),
|
91
|
-
setStockBatchUuid: jest.fn(),
|
92
|
-
setPageSize: jest.fn(),
|
93
|
-
pageSizes: [10, 20, 50],
|
94
|
-
error: null,
|
95
|
-
setLocationUuid: jest.fn(),
|
96
|
-
setPartyUuid: jest.fn(),
|
97
|
-
setSearchString: jest.fn(),
|
98
|
-
});
|
99
|
-
|
42
|
+
it('should render loading skeleton when isLoading is true', () => {
|
43
|
+
mockUseStockItemBatchNumbers.mockReturnValue({ isLoading: true, stockItemBatchNos: [] });
|
44
|
+
mockUseStockItemBatchInformationHook.mockReturnValue({ isLoading: true, items: [], setStockItemUuid: jest.fn() });
|
100
45
|
render(<BatchNoSelector stockItemUuid={mockStockItemUuid} onValueChange={mockOnValueChange} />);
|
101
|
-
|
102
46
|
expect(screen.getByRole('progressbar')).toBeInTheDocument();
|
103
47
|
});
|
104
48
|
|
105
|
-
it('should render
|
49
|
+
it('should render combobox with batch numbers', async () => {
|
106
50
|
render(<BatchNoSelector stockItemUuid={mockStockItemUuid} onValueChange={mockOnValueChange} />);
|
107
|
-
|
108
51
|
expect(screen.getByRole('combobox')).toBeInTheDocument();
|
109
|
-
expect(screen.getByText(
|
52
|
+
expect(screen.getByText('batchNo')).toBeInTheDocument();
|
110
53
|
});
|
111
54
|
|
112
55
|
it('should handle batch selection', async () => {
|
113
|
-
const user = userEvent.setup();
|
114
|
-
|
115
56
|
render(<BatchNoSelector stockItemUuid={mockStockItemUuid} onValueChange={mockOnValueChange} />);
|
116
|
-
|
117
57
|
const combobox = screen.getByRole('combobox');
|
118
|
-
await
|
119
|
-
await
|
120
|
-
|
58
|
+
await userEvent.click(combobox);
|
59
|
+
await userEvent.type(combobox, 'BATCH-001');
|
121
60
|
const option = screen.getByText(`BATCH-001 | Qty: 10 | Expiry: ${formatForDatePicker(mockExpiration)}`);
|
122
|
-
|
123
|
-
await user.click(option);
|
124
|
-
|
61
|
+
await userEvent.click(option);
|
125
62
|
expect(mockOnValueChange).toHaveBeenCalledWith('1');
|
126
63
|
});
|
127
64
|
|
@@ -134,45 +71,18 @@ describe('BatchNoSelector', () => {
|
|
134
71
|
});
|
135
72
|
|
136
73
|
it('should filter out batches with zero or undefined quantity', async () => {
|
137
|
-
const user = userEvent.setup();
|
138
|
-
|
139
74
|
mockUseStockItemBatchNumbers.mockReturnValue({
|
140
75
|
isLoading: false,
|
141
76
|
stockItemBatchNos: [
|
142
|
-
{
|
143
|
-
|
144
|
-
|
145
|
-
quantity: '10',
|
146
|
-
expiration: mockExpiration,
|
147
|
-
stockItemUuid: 'item1',
|
148
|
-
voided: false,
|
149
|
-
},
|
150
|
-
{
|
151
|
-
uuid: '2',
|
152
|
-
batchNo: 'BATCH-002',
|
153
|
-
quantity: '0',
|
154
|
-
expiration: mockExpiration,
|
155
|
-
stockItemUuid: 'item2',
|
156
|
-
voided: false,
|
157
|
-
},
|
158
|
-
{
|
159
|
-
uuid: '3',
|
160
|
-
batchNo: 'BATCH-003',
|
161
|
-
quantity: undefined,
|
162
|
-
expiration: mockExpiration,
|
163
|
-
stockItemUuid: 'item3',
|
164
|
-
voided: false,
|
165
|
-
},
|
77
|
+
{ uuid: '1', batchNo: 'BATCH-001', quantity: 10 },
|
78
|
+
{ uuid: '2', batchNo: 'BATCH-002', quantity: 0 },
|
79
|
+
{ uuid: '3', batchNo: 'BATCH-003', quantity: undefined },
|
166
80
|
],
|
167
|
-
setLimit: jest.fn(),
|
168
|
-
setRepresentation: jest.fn(),
|
169
|
-
setSearchString: jest.fn(),
|
170
81
|
});
|
171
82
|
|
172
83
|
render(<BatchNoSelector stockItemUuid={mockStockItemUuid} onValueChange={mockOnValueChange} />);
|
173
|
-
|
174
84
|
const combobox = screen.getByRole('combobox');
|
175
|
-
await
|
85
|
+
await userEvent.click(combobox);
|
176
86
|
|
177
87
|
expect(screen.queryByText('BATCH-002')).not.toBeInTheDocument();
|
178
88
|
expect(screen.queryByText('BATCH-003')).not.toBeInTheDocument();
|