@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.
@@ -85,9 +85,9 @@
85
85
  "initial": false,
86
86
  "entry": false,
87
87
  "recorded": false,
88
- "size": 1841106,
88
+ "size": 1841303,
89
89
  "sizes": {
90
- "javascript": 1840896,
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": "a8491eb316af7797",
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": 5509533,
112
+ "size": 5509730,
113
113
  "sizes": {
114
114
  "consume-shared": 252,
115
- "javascript": 5487583,
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": "33a6a9db8e921f65",
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.632"}
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openmrs/esm-stock-management-app",
3
- "version": "1.0.1-pre.632",
3
+ "version": "1.0.1-pre.641",
4
4
  "license": "MPL-2.0",
5
5
  "description": "Stock management microfrontend for OpenMRS 3.x",
6
6
  "browser": "dist/openmrs-esm-stock-management-app.js",
@@ -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={true}
526
- hideSteppers={true}
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', // Add this
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.type(screen.getByLabelText('Source Type'), 'type2');
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
+ });