@openmrs/esm-stock-management-app 1.0.1-pre.632 → 1.0.1-pre.641
Sign up to get free protection for your applications and to get access to all the features.
- package/dist/173.js +1 -1
- package/dist/173.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 +6 -6
- package/dist/routes.json +1 -1
- package/package.json +1 -1
- package/src/core/components/carbon/controlled-number-input/controlled-number-input.component.tsx +1 -0
- package/src/stock-items/stock-items-table.component.tsx +1 -1
- package/src/stock-items/stock-items-table.test.tsx +134 -0
- package/src/stock-operations/add-stock-operation/stock-items-addition-row.component.tsx +5 -4
- package/src/stock-reports/generate-report/create-stock-report.component.tsx +3 -2
- package/src/stock-sources/add-stock-sources/add-stock-sources.test.tsx +4 -4
@@ -85,9 +85,9 @@
|
|
85
85
|
"initial": false,
|
86
86
|
"entry": false,
|
87
87
|
"recorded": false,
|
88
|
-
"size":
|
88
|
+
"size": 1841303,
|
89
89
|
"sizes": {
|
90
|
-
"javascript":
|
90
|
+
"javascript": 1841093,
|
91
91
|
"consume-shared": 210
|
92
92
|
},
|
93
93
|
"names": [],
|
@@ -101,7 +101,7 @@
|
|
101
101
|
"auxiliaryFiles": [
|
102
102
|
"173.js.map"
|
103
103
|
],
|
104
|
-
"hash": "
|
104
|
+
"hash": "be71504bcfe9203c",
|
105
105
|
"childrenByOrder": {}
|
106
106
|
},
|
107
107
|
{
|
@@ -109,10 +109,10 @@
|
|
109
109
|
"initial": true,
|
110
110
|
"entry": true,
|
111
111
|
"recorded": false,
|
112
|
-
"size":
|
112
|
+
"size": 5509730,
|
113
113
|
"sizes": {
|
114
114
|
"consume-shared": 252,
|
115
|
-
"javascript":
|
115
|
+
"javascript": 5487780,
|
116
116
|
"share-init": 252,
|
117
117
|
"runtime": 21446
|
118
118
|
},
|
@@ -129,7 +129,7 @@
|
|
129
129
|
"auxiliaryFiles": [
|
130
130
|
"main.js.map"
|
131
131
|
],
|
132
|
-
"hash": "
|
132
|
+
"hash": "05641415f286ec13",
|
133
133
|
"childrenByOrder": {}
|
134
134
|
},
|
135
135
|
{
|
package/dist/routes.json
CHANGED
@@ -1 +1 @@
|
|
1
|
-
{"$schema":"https://json.openmrs.org/routes.schema.json","backendDependencies":{"fhir2":">=1.2","webservices.rest":"^2.24.0"},"extensions":[{"name":"stock-nav-menu","slot":"stock-sidebar-slot","component":"stockNavMenu","online":true,"offline":true},{"name":"overview-db-link","slot":"stock-page-dashboard-slot","component":"stockOverviewLink","meta":{"name":"overview","slot":"overview-dashboard-slot","title":"overview"},"order":0,"online":true,"offline":true},{"name":"stock-overview-db","slot":"overview-dashboard-slot","component":"stockOverview"},{"name":"operations-db-link","slot":"stock-page-dashboard-slot","component":"stockOperationsLink","meta":{"name":"operations","slot":"operations-dashboard-slot","title":"operations"},"order":2,"online":true,"offline":true},{"name":"stock-operations-db","slot":"operations-dashboard-slot","component":"stockOperations"},{"name":"items-db-link","slot":"stock-page-dashboard-slot","component":"stockItemsLink","meta":{"name":"items","slot":"items-dashboard-slot","title":"items"},"order":1,"online":true,"offline":true},{"name":"stock-items-db","slot":"items-dashboard-slot","component":"stockItems"},{"name":"user-scopes-db-link","slot":"stock-page-dashboard-slot","component":"stockUserScopesLink","meta":{"name":"user-scopes","slot":"user-scopes-dashboard-slot","title":"user-scopes"},"order":3,"online":true,"offline":true},{"name":"stock-user-scopes-db","slot":"user-scopes-dashboard-slot","component":"stockUserScopes"},{"name":"sources-db-link","slot":"stock-page-dashboard-slot","component":"stockSourcesLink","meta":{"name":"sources","slot":"sources-dashboard-slot","title":"Sources"},"order":2,"online":true,"offline":true},{"name":"stock-sources-db","slot":"sources-dashboard-slot","component":"stockSources"},{"name":"locations-db-link","slot":"stock-page-dashboard-slot","component":"stockLocationsLink","meta":{"name":"locations","slot":"locations-dashboard-slot","title":"Locations"},"order":4,"online":true,"offline":true},{"name":"stock-locations-db","slot":"locations-dashboard-slot","component":"stockLocations"},{"name":"reports-db-link","slot":"stock-page-dashboard-slot","component":"stockReportsLink","meta":{"name":"reports","slot":"reports-dashboard-slot","title":"Reports"},"order":5,"online":true,"offline":true},{"name":"stock-reports-db","slot":"reports-dashboard-slot","component":"stockReports"},{"name":"settings-db-link","slot":"stock-page-dashboard-slot","component":"stockSettingsLink","meta":{"name":"settings","slot":"settings-dashboard-slot","title":"Settings"},"order":6,"online":true,"offline":true},{"name":"stock-settings-db","slot":"settings-dashboard-slot","component":"stockSettings"},{"name":"stock-management-admin-card-link","slot":"system-admin-page-card-link-slot","component":"stockManagementAdminCardLink"},{"name":"stock-operation-dialog","component":"stockOperationDialog"},{"name":"import-bulk-stock-items","component":"importBulkStockItemsDialog"},{"name":"delete-stock-modal","component":"deleteStockModal"},{"name":"delete-stock-user-scope-modal","component":"deleteUserScopeModal"},{"name":"stock-management-app-menu-item","component":"stockManagementAppMenuItem","slot":"app-menu-item-slot","meta":{"name":" Stock Management"}},{"name":"delete-stock-rule-modal","component":"deleteStockRuleModal"},{"name":"delete-packaging-unit-modal","component":"deletePackagingUnitModal"},{"name":"delete-packaging-unit-button","component":"deletePackagingUnitButton"}],"pages":[{"component":"root","route":"stock-management"}],"version":"1.0.1-pre.
|
1
|
+
{"$schema":"https://json.openmrs.org/routes.schema.json","backendDependencies":{"fhir2":">=1.2","webservices.rest":"^2.24.0"},"extensions":[{"name":"stock-nav-menu","slot":"stock-sidebar-slot","component":"stockNavMenu","online":true,"offline":true},{"name":"overview-db-link","slot":"stock-page-dashboard-slot","component":"stockOverviewLink","meta":{"name":"overview","slot":"overview-dashboard-slot","title":"overview"},"order":0,"online":true,"offline":true},{"name":"stock-overview-db","slot":"overview-dashboard-slot","component":"stockOverview"},{"name":"operations-db-link","slot":"stock-page-dashboard-slot","component":"stockOperationsLink","meta":{"name":"operations","slot":"operations-dashboard-slot","title":"operations"},"order":2,"online":true,"offline":true},{"name":"stock-operations-db","slot":"operations-dashboard-slot","component":"stockOperations"},{"name":"items-db-link","slot":"stock-page-dashboard-slot","component":"stockItemsLink","meta":{"name":"items","slot":"items-dashboard-slot","title":"items"},"order":1,"online":true,"offline":true},{"name":"stock-items-db","slot":"items-dashboard-slot","component":"stockItems"},{"name":"user-scopes-db-link","slot":"stock-page-dashboard-slot","component":"stockUserScopesLink","meta":{"name":"user-scopes","slot":"user-scopes-dashboard-slot","title":"user-scopes"},"order":3,"online":true,"offline":true},{"name":"stock-user-scopes-db","slot":"user-scopes-dashboard-slot","component":"stockUserScopes"},{"name":"sources-db-link","slot":"stock-page-dashboard-slot","component":"stockSourcesLink","meta":{"name":"sources","slot":"sources-dashboard-slot","title":"Sources"},"order":2,"online":true,"offline":true},{"name":"stock-sources-db","slot":"sources-dashboard-slot","component":"stockSources"},{"name":"locations-db-link","slot":"stock-page-dashboard-slot","component":"stockLocationsLink","meta":{"name":"locations","slot":"locations-dashboard-slot","title":"Locations"},"order":4,"online":true,"offline":true},{"name":"stock-locations-db","slot":"locations-dashboard-slot","component":"stockLocations"},{"name":"reports-db-link","slot":"stock-page-dashboard-slot","component":"stockReportsLink","meta":{"name":"reports","slot":"reports-dashboard-slot","title":"Reports"},"order":5,"online":true,"offline":true},{"name":"stock-reports-db","slot":"reports-dashboard-slot","component":"stockReports"},{"name":"settings-db-link","slot":"stock-page-dashboard-slot","component":"stockSettingsLink","meta":{"name":"settings","slot":"settings-dashboard-slot","title":"Settings"},"order":6,"online":true,"offline":true},{"name":"stock-settings-db","slot":"settings-dashboard-slot","component":"stockSettings"},{"name":"stock-management-admin-card-link","slot":"system-admin-page-card-link-slot","component":"stockManagementAdminCardLink"},{"name":"stock-operation-dialog","component":"stockOperationDialog"},{"name":"import-bulk-stock-items","component":"importBulkStockItemsDialog"},{"name":"delete-stock-modal","component":"deleteStockModal"},{"name":"delete-stock-user-scope-modal","component":"deleteUserScopeModal"},{"name":"stock-management-app-menu-item","component":"stockManagementAppMenuItem","slot":"app-menu-item-slot","meta":{"name":" Stock Management"}},{"name":"delete-stock-rule-modal","component":"deleteStockRuleModal"},{"name":"delete-packaging-unit-modal","component":"deletePackagingUnitModal"},{"name":"delete-packaging-unit-button","component":"deletePackagingUnitButton"}],"pages":[{"component":"root","route":"stock-management"}],"version":"1.0.1-pre.641"}
|
package/package.json
CHANGED
package/src/core/components/carbon/controlled-number-input/controlled-number-input.component.tsx
CHANGED
@@ -19,6 +19,7 @@ const ControlledNumberInput = <T,>(props: ControlledNumberInputProps<T>) => {
|
|
19
19
|
control={props.control}
|
20
20
|
render={({ field: { onChange, value, ref } }) => (
|
21
21
|
<NumberInput
|
22
|
+
disableWheel
|
22
23
|
label={props.label}
|
23
24
|
id={`${props.name}-${props.id}-${props.row?.uuid}`}
|
24
25
|
value={props.row?.factor ?? value}
|
@@ -187,7 +187,7 @@ const StockItemsTableComponent: React.FC<StockItemsTableProps> = () => {
|
|
187
187
|
|
188
188
|
<FilterStockItems filterType={isDrug} changeFilterType={setDrug} />
|
189
189
|
<AddStockItemsBulktImportActionButton />
|
190
|
-
<TableToolbarMenu>
|
190
|
+
<TableToolbarMenu data-testid="stock-items-menu">
|
191
191
|
<TableToolbarAction onClick={handleRefresh}>Refresh</TableToolbarAction>
|
192
192
|
</TableToolbarMenu>
|
193
193
|
<AddStockItemActionButton />
|
@@ -0,0 +1,134 @@
|
|
1
|
+
import React from 'react';
|
2
|
+
import { render, screen, waitFor, within } from '@testing-library/react';
|
3
|
+
import userEvent from '@testing-library/user-event';
|
4
|
+
import StockItemsTableComponent from './stock-items-table.component';
|
5
|
+
import { useStockItemsPages } from './stock-items-table.resource';
|
6
|
+
import { handleMutate } from '../utils';
|
7
|
+
import { launchAddOrEditDialog } from './stock-item.utils';
|
8
|
+
|
9
|
+
jest.mock('./stock-items-table.resource', () => ({
|
10
|
+
useStockItemsPages: jest.fn(),
|
11
|
+
}));
|
12
|
+
|
13
|
+
jest.mock('../utils', () => ({
|
14
|
+
handleMutate: jest.fn(),
|
15
|
+
}));
|
16
|
+
|
17
|
+
jest.mock('./stock-item.utils', () => ({
|
18
|
+
launchAddOrEditDialog: jest.fn(),
|
19
|
+
}));
|
20
|
+
|
21
|
+
jest.mock('react-i18next', () => ({
|
22
|
+
useTranslation: () => ({
|
23
|
+
t: (key: string) => key,
|
24
|
+
}),
|
25
|
+
}));
|
26
|
+
|
27
|
+
describe('StockItemsTableComponent', () => {
|
28
|
+
const mockUseStockItemsPages = {
|
29
|
+
isLoading: false,
|
30
|
+
items: Array(25).fill(null).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
|
+
})),
|
40
|
+
totalCount: 25,
|
41
|
+
currentPageSize: 10,
|
42
|
+
setPageSize: jest.fn(),
|
43
|
+
pageSizes: [10, 20, 30],
|
44
|
+
currentPage: 1,
|
45
|
+
setCurrentPage: jest.fn(),
|
46
|
+
isDrug: '',
|
47
|
+
setDrug: jest.fn(),
|
48
|
+
setSearchString: jest.fn(),
|
49
|
+
};
|
50
|
+
|
51
|
+
beforeEach(() => {
|
52
|
+
jest.clearAllMocks();
|
53
|
+
(useStockItemsPages as jest.Mock).mockReturnValue(mockUseStockItemsPages);
|
54
|
+
});
|
55
|
+
|
56
|
+
it('renders initial state and UI elements correctly', async () => {
|
57
|
+
render(<StockItemsTableComponent />);
|
58
|
+
expect(screen.getByText('panelDescription')).toBeInTheDocument();
|
59
|
+
expect(screen.getByRole('searchbox')).toBeInTheDocument();
|
60
|
+
|
61
|
+
const user = userEvent.setup();
|
62
|
+
const menuButton = screen.getByTestId('stock-items-menu');
|
63
|
+
await user.click(menuButton);
|
64
|
+
|
65
|
+
await screen.findByText('Refresh');
|
66
|
+
|
67
|
+
expect(screen.getByText('type')).toBeInTheDocument();
|
68
|
+
expect(screen.getByText('genericName')).toBeInTheDocument();
|
69
|
+
expect(screen.getByText('commonName')).toBeInTheDocument();
|
70
|
+
});
|
71
|
+
|
72
|
+
it('displays skeleton loader when isLoading is true', () => {
|
73
|
+
(useStockItemsPages as jest.Mock).mockReturnValue({ ...mockUseStockItemsPages, isLoading: true });
|
74
|
+
render(<StockItemsTableComponent />);
|
75
|
+
expect(screen.getByRole('progressbar')).toBeInTheDocument();
|
76
|
+
});
|
77
|
+
|
78
|
+
it('renders table rows correctly based on items prop', () => {
|
79
|
+
render(<StockItemsTableComponent />);
|
80
|
+
expect(screen.getByText('Test Item 0')).toBeInTheDocument();
|
81
|
+
expect(screen.getAllByText('drug').length).toBeGreaterThan(0);
|
82
|
+
expect(screen.getAllByText('other').length).toBeGreaterThan(0);
|
83
|
+
});
|
84
|
+
|
85
|
+
it('updates search input and triggers search function', async () => {
|
86
|
+
render(<StockItemsTableComponent />);
|
87
|
+
const user = userEvent.setup();
|
88
|
+
const searchInput = screen.getByRole('searchbox');
|
89
|
+
await user.type(searchInput, 'test search');
|
90
|
+
await waitFor(() => {
|
91
|
+
expect(mockUseStockItemsPages.setSearchString).toHaveBeenCalledWith('test search');
|
92
|
+
}, { timeout: 2000 });
|
93
|
+
});
|
94
|
+
|
95
|
+
it('updates pagination when page or page size changes', async () => {
|
96
|
+
render(<StockItemsTableComponent />);
|
97
|
+
const user = userEvent.setup();
|
98
|
+
const nextPageButton = screen.getByLabelText('Next page');
|
99
|
+
await user.click(nextPageButton);
|
100
|
+
expect(mockUseStockItemsPages.setCurrentPage).toHaveBeenCalled();
|
101
|
+
|
102
|
+
const pageSizeSelect = screen.getByLabelText('Items per page:');
|
103
|
+
await user.selectOptions(pageSizeSelect, '20');
|
104
|
+
expect(mockUseStockItemsPages.setPageSize).toHaveBeenCalledWith(20);
|
105
|
+
});
|
106
|
+
|
107
|
+
it('triggers handleRefresh when refresh button is clicked', async () => {
|
108
|
+
render(<StockItemsTableComponent />);
|
109
|
+
|
110
|
+
const user = userEvent.setup();
|
111
|
+
const menuButton = screen.getByTestId('stock-items-menu');
|
112
|
+
expect(menuButton).toBeInTheDocument();
|
113
|
+
await user.click(menuButton);
|
114
|
+
|
115
|
+
const refreshButton = await screen.findByText('Refresh');
|
116
|
+
expect(refreshButton).toBeInTheDocument();
|
117
|
+
await user.click(refreshButton);
|
118
|
+
await waitFor(() => {
|
119
|
+
expect(handleMutate).toHaveBeenCalledWith(expect.stringContaining('/stockmanagement/stockitem'));
|
120
|
+
});
|
121
|
+
});
|
122
|
+
|
123
|
+
it('triggers launchAddOrEditDialog when edit button is clicked', async () => {
|
124
|
+
render(<StockItemsTableComponent />);
|
125
|
+
const user = userEvent.setup();
|
126
|
+
const editButtons = screen.getAllByLabelText('Edit Stock Item');
|
127
|
+
await user.click(editButtons[0]);
|
128
|
+
expect(launchAddOrEditDialog).toHaveBeenCalledWith(
|
129
|
+
expect.any(Function),
|
130
|
+
expect.objectContaining({ uuid: 'item-0' }),
|
131
|
+
true
|
132
|
+
);
|
133
|
+
});
|
134
|
+
});
|
@@ -3,7 +3,6 @@ import { isDesktop } from '@openmrs/esm-framework';
|
|
3
3
|
import { Button, DatePicker, DatePickerInput, Link, NumberInput, TableCell, TableRow, TextInput } from '@carbon/react';
|
4
4
|
import { TrashCan } from '@carbon/react/icons';
|
5
5
|
import { StockOperationItemFormData } from '../validation-schema';
|
6
|
-
import StockItemSelector from '../stock-item-selector/stock-item-selector.component';
|
7
6
|
import {
|
8
7
|
Control,
|
9
8
|
FieldArrayWithId,
|
@@ -217,11 +216,12 @@ const StockItemsAdditionRow: React.FC<StockItemsAdditionRowProps> = ({
|
|
217
216
|
<div className={styles.cellContent}>
|
218
217
|
{canEdit && (
|
219
218
|
<NumberInput
|
219
|
+
allowEmpty
|
220
220
|
className="small-placeholder-text"
|
221
|
+
disableWheel
|
222
|
+
hideSteppers
|
221
223
|
size="sm"
|
222
224
|
id={`qty-${row?.uuid}`}
|
223
|
-
hideSteppers={true}
|
224
|
-
allowEmpty={true}
|
225
225
|
onChange={(e: any) => setValue(`stockItems.${index}.quantity`, e?.target?.value)}
|
226
226
|
value={row?.quantity ?? ''}
|
227
227
|
invalidText={errors?.stockItems?.[index]?.quantity?.message}
|
@@ -262,11 +262,12 @@ const StockItemsAdditionRow: React.FC<StockItemsAdditionRowProps> = ({
|
|
262
262
|
<div className={styles.cellContent}>
|
263
263
|
{canEdit && (
|
264
264
|
<NumberInput
|
265
|
+
allowEmpty
|
266
|
+
disableWheel
|
265
267
|
size="sm"
|
266
268
|
invalid={!!errors?.stockItems?.[index]?.purchasePrice}
|
267
269
|
invalidText=""
|
268
270
|
id={`purchaseprice-${row.uuid}`}
|
269
|
-
allowEmpty={true}
|
270
271
|
onChange={(e: any) => setValue(`stockItems.${index}.purchasePrice`, e?.target?.value)}
|
271
272
|
value={row?.purchasePrice ?? ''}
|
272
273
|
title=""
|
@@ -522,8 +522,9 @@ const CreateReport: React.FC<CreateReportProps> = ({ model }) => {
|
|
522
522
|
render={({ field: { onChange, value } }) => (
|
523
523
|
<NumberInput
|
524
524
|
id="limitTop"
|
525
|
-
allowEmpty
|
526
|
-
|
525
|
+
allowEmpty
|
526
|
+
disableWheel
|
527
|
+
hideSteppers
|
527
528
|
value={value}
|
528
529
|
onchange={onChange}
|
529
530
|
label={t('limit', 'Limit')}
|
@@ -41,7 +41,7 @@ describe('StockSourcesAddOrUpdate', () => {
|
|
41
41
|
|
42
42
|
it('renders correctly with model prop', () => {
|
43
43
|
const model: StockSource = {
|
44
|
-
uuid: '123',
|
44
|
+
uuid: '123',
|
45
45
|
name: 'Test Source',
|
46
46
|
acronym: 'TS',
|
47
47
|
sourceType: {
|
@@ -72,7 +72,6 @@ describe('StockSourcesAddOrUpdate', () => {
|
|
72
72
|
retiredBy: undefined,
|
73
73
|
retireReason: '',
|
74
74
|
},
|
75
|
-
// Add these properties from BaseOpenmrsData
|
76
75
|
creator: {
|
77
76
|
uuid: 'creator-uuid',
|
78
77
|
display: 'Creator Name',
|
@@ -115,13 +114,14 @@ describe('StockSourcesAddOrUpdate', () => {
|
|
115
114
|
|
116
115
|
await user.type(screen.getByLabelText('Full Name'), 'New Source');
|
117
116
|
await user.type(screen.getByLabelText('Acronym/Code'), 'NS');
|
118
|
-
await user.
|
117
|
+
await user.selectOptions(screen.getByLabelText('Source Type'), 'type2');
|
119
118
|
await user.click(screen.getByText('Save'));
|
120
119
|
|
121
120
|
expect(createOrUpdateStockSource).toHaveBeenCalledWith(
|
122
121
|
expect.objectContaining({
|
123
122
|
name: 'New Source',
|
124
123
|
acronym: 'NS',
|
124
|
+
sourceType: expect.objectContaining({ uuid: 'type2' }),
|
125
125
|
}),
|
126
126
|
);
|
127
127
|
});
|
@@ -168,4 +168,4 @@ describe('StockSourcesAddOrUpdate', () => {
|
|
168
168
|
|
169
169
|
expect(closeOverlay).toHaveBeenCalled();
|
170
170
|
});
|
171
|
-
});
|
171
|
+
});
|