@openmrs/esm-stock-management-app 3.1.1-pre.987 → 3.1.1-pre.991

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.
@@ -895,9 +895,9 @@
895
895
  "initial": false,
896
896
  "entry": false,
897
897
  "recorded": false,
898
- "size": 1193640,
898
+ "size": 1194726,
899
899
  "sizes": {
900
- "javascript": 1193640
900
+ "javascript": 1194726
901
901
  },
902
902
  "names": [],
903
903
  "idHints": [],
@@ -910,7 +910,7 @@
910
910
  "auxiliaryFiles": [
911
911
  "5125.js.map"
912
912
  ],
913
- "hash": "208cdc75a033cb08",
913
+ "hash": "c7ede212e13afcf6",
914
914
  "childrenByOrder": {}
915
915
  },
916
916
  {
@@ -962,9 +962,9 @@
962
962
  "initial": false,
963
963
  "entry": false,
964
964
  "recorded": false,
965
- "size": 334993,
965
+ "size": 336072,
966
966
  "sizes": {
967
- "javascript": 334993
967
+ "javascript": 336072
968
968
  },
969
969
  "names": [],
970
970
  "idHints": [],
@@ -978,7 +978,7 @@
978
978
  "auxiliaryFiles": [
979
979
  "5333.js.map"
980
980
  ],
981
- "hash": "131480481d40bf8e",
981
+ "hash": "bf2aedda0f392192",
982
982
  "childrenByOrder": {}
983
983
  },
984
984
  {
@@ -1539,10 +1539,10 @@
1539
1539
  "initial": true,
1540
1540
  "entry": true,
1541
1541
  "recorded": false,
1542
- "size": 5542284,
1542
+ "size": 5543370,
1543
1543
  "sizes": {
1544
1544
  "consume-shared": 210,
1545
- "javascript": 5520049,
1545
+ "javascript": 5521135,
1546
1546
  "share-init": 336,
1547
1547
  "runtime": 21689
1548
1548
  },
@@ -1559,7 +1559,7 @@
1559
1559
  "auxiliaryFiles": [
1560
1560
  "main.js.map"
1561
1561
  ],
1562
- "hash": "75bd8aca6005378b",
1562
+ "hash": "48c2a1c503274239",
1563
1563
  "childrenByOrder": {}
1564
1564
  },
1565
1565
  {
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":"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-management-app-menu-item","component":"stockManagementAppMenuItem","slot":"app-menu-item-slot","meta":{"name":" Stock Management"}},{"name":"delete-packaging-unit-button","component":"deletePackagingUnitButton"}],"modals":[{"name":"delete-stock-modal","component":"deleteStockModal"},{"name":"delete-stock-user-scope-modal","component":"deleteUserScopeModal"},{"name":"delete-stock-rule-modal","component":"deleteStockRuleModal"},{"name":"delete-packaging-unit-modal","component":"deletePackagingUnitModal"},{"name":"expired-stock-modal","component":"expiredStockModal"},{"name":"import-bulk-stock-items","component":"importBulkStockItemsModal"},{"name":"receiving-stock-modal","component":"receivingStockModal"},{"name":"stock-operations-modal","component":"stockOperationsModal"},{"name":"transactions-print-bincard-preview-modal","component":"transactionBincardPrintPreviewModal"},{"name":"transactions-print-stockcard-preview-modal","component":"transactionStockcardPrintPreviewModal"}],"workspaces":[{"name":"stock-operation-form-workspace","component":"stockOperationFormWorkspace","title":"Stock Operation","type":"form","canMaximize":true,"width":"extra-wide"},{"name":"stock-item-rules-form-workspace","component":"stockItemRulesFormWorkspace","title":"Stock Item Rules","type":"form"},{"name":"stock-item-form-workspace","component":"stockItemFormWorkspace","title":"Stock Items","type":"form","canMaximize":true,"width":"extra-wide"},{"name":"stock-sources-form-workspace","component":"stockSourcesFormWorkspace","title":"Stock Sources","type":"form","canMaximize":true,"width":"extra-wide"},{"name":"stock-location-form-workspace","component":"stockLocationsFormWorkspace","title":"Stock Locations","type":"form","canMaximize":true,"width":"extra-wide"},{"name":"stock-user-role-scopes-form-workspace","component":"stockUserScopesFormWorkspace","title":"User Role Scopes","type":"form","canMaximize":true,"width":"extra-wide"},{"name":"stock-reports-form-workspace","component":"stockReportsFormWorkspace","title":"Reports","type":"form","canMaximize":true,"width":"extra-wide"}],"pages":[{"component":"root","route":"stock-management"}],"version":"3.1.1-pre.987"}
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":"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-management-app-menu-item","component":"stockManagementAppMenuItem","slot":"app-menu-item-slot","meta":{"name":" Stock Management"}},{"name":"delete-packaging-unit-button","component":"deletePackagingUnitButton"}],"modals":[{"name":"delete-stock-modal","component":"deleteStockModal"},{"name":"delete-stock-user-scope-modal","component":"deleteUserScopeModal"},{"name":"delete-stock-rule-modal","component":"deleteStockRuleModal"},{"name":"delete-packaging-unit-modal","component":"deletePackagingUnitModal"},{"name":"expired-stock-modal","component":"expiredStockModal"},{"name":"import-bulk-stock-items","component":"importBulkStockItemsModal"},{"name":"receiving-stock-modal","component":"receivingStockModal"},{"name":"stock-operations-modal","component":"stockOperationsModal"},{"name":"transactions-print-bincard-preview-modal","component":"transactionBincardPrintPreviewModal"},{"name":"transactions-print-stockcard-preview-modal","component":"transactionStockcardPrintPreviewModal"}],"workspaces":[{"name":"stock-operation-form-workspace","component":"stockOperationFormWorkspace","title":"Stock Operation","type":"form","canMaximize":true,"width":"extra-wide"},{"name":"stock-item-rules-form-workspace","component":"stockItemRulesFormWorkspace","title":"Stock Item Rules","type":"form"},{"name":"stock-item-form-workspace","component":"stockItemFormWorkspace","title":"Stock Items","type":"form","canMaximize":true,"width":"extra-wide"},{"name":"stock-sources-form-workspace","component":"stockSourcesFormWorkspace","title":"Stock Sources","type":"form","canMaximize":true,"width":"extra-wide"},{"name":"stock-location-form-workspace","component":"stockLocationsFormWorkspace","title":"Stock Locations","type":"form","canMaximize":true,"width":"extra-wide"},{"name":"stock-user-role-scopes-form-workspace","component":"stockUserScopesFormWorkspace","title":"User Role Scopes","type":"form","canMaximize":true,"width":"extra-wide"},{"name":"stock-reports-form-workspace","component":"stockReportsFormWorkspace","title":"Reports","type":"form","canMaximize":true,"width":"extra-wide"}],"pages":[{"component":"root","route":"stock-management"}],"version":"3.1.1-pre.991"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openmrs/esm-stock-management-app",
3
- "version": "3.1.1-pre.987",
3
+ "version": "3.1.1-pre.991",
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",
@@ -6,3 +6,12 @@ export interface StockBatchDTO {
6
6
  quantity: string;
7
7
  voided: boolean;
8
8
  }
9
+
10
+ export interface StockBatchWithUoM extends StockBatchDTO {
11
+ quantityUoM?: string;
12
+ quantityFactor?: string;
13
+ quantityUoMUuid?: string;
14
+ partyName?: string;
15
+ locationUuid?: string;
16
+ partyUuid?: string;
17
+ }
@@ -6,15 +6,15 @@ import { useDisposalList } from './useDisposalList';
6
6
  import { useStockInventory } from './stock-home-inventory-expiry.resource';
7
7
  import { useStockInventoryItems } from './stock-home-inventory-items.resource';
8
8
  import { type StockOperationFilter } from '../stock-operations/stock-operations.resource';
9
- import useStockList from './useStockList';
9
+ import useStockList, { useOutOfStockList } from './useStockList';
10
10
  import MetricsCard from '../core/components/card/metrics-card-component';
11
11
  import styles from './stock-home.scss';
12
12
 
13
13
  const StockManagementMetrics: React.FC = (filter: StockOperationFilter) => {
14
14
  const { t } = useTranslation();
15
- const { stockList: allStocks, error } = useStockList();
16
15
  const { items: expiryItems } = useStockInventory();
17
16
  const { items: stockItems } = useStockInventoryItems();
17
+ const { totalOutOfStock, error: outOfStockError } = useOutOfStockList();
18
18
 
19
19
  const currentDate = new Date();
20
20
 
@@ -39,8 +39,8 @@ const StockManagementMetrics: React.FC = (filter: StockOperationFilter) => {
39
39
  totalCount: true,
40
40
  });
41
41
 
42
- if (error) {
43
- return <ErrorState headerTitle={t('errorStockMetric', 'Error fetching stock metrics')} error={error} />;
42
+ if (outOfStockError) {
43
+ return <ErrorState headerTitle={t('errorStockMetric', 'Error fetching stock metrics')} error={outOfStockError} />;
44
44
  }
45
45
 
46
46
  const filteredItems =
@@ -64,7 +64,7 @@ const StockManagementMetrics: React.FC = (filter: StockOperationFilter) => {
64
64
  itemsBelowMin: [],
65
65
  itemsAboveMax: [],
66
66
  }}
67
- value={allStocks?.length}
67
+ value={totalOutOfStock}
68
68
  />
69
69
  <MetricsCard
70
70
  disposedCount={{
@@ -1,5 +1,6 @@
1
1
  import useSWR from 'swr';
2
- import { openmrsFetch, restBaseUrl } from '@openmrs/esm-framework';
2
+ import { type FetchResponse, openmrsFetch, restBaseUrl } from '@openmrs/esm-framework';
3
+ import { type OutofStockListResponse } from '../types';
3
4
 
4
5
  interface StockList {
5
6
  uuid: string;
@@ -31,3 +32,18 @@ const useStockList = () => {
31
32
  };
32
33
 
33
34
  export default useStockList;
35
+
36
+ export const useOutOfStockList = () => {
37
+ const url = `${restBaseUrl}/stockmanagement/metrics/outofstockitemstotal`;
38
+ const { data, error, isLoading, mutate } = useSWR<FetchResponse<OutofStockListResponse>>(url, openmrsFetch);
39
+
40
+ const totalOutOfStock = data?.data.results?.reduce((total, item) => total + item.outOfStock, 0) ?? 0;
41
+
42
+ return {
43
+ outOfStockItemsList: data?.data.results ?? [],
44
+ totalOutOfStock,
45
+ isLoading,
46
+ error,
47
+ mutate,
48
+ };
49
+ };
@@ -1,7 +1,7 @@
1
- import React, { useEffect, useMemo } from 'react';
1
+ import React, { useCallback, useEffect, useMemo } from 'react';
2
2
  import { useTranslation } from 'react-i18next';
3
3
  import { ComboBox, SelectSkeleton } from '@carbon/react';
4
- import { type StockBatchDTO } from '../../../core/api/types/stockItem/StockBatchDTO';
4
+ import { type StockBatchWithUoM } from '../../../core/api/types/stockItem/StockBatchDTO';
5
5
  import { formatForDatePicker } from '../../../constants';
6
6
  import { useStockItemBatchInformationHook } from '../../../stock-items/add-stock-item/batch-information/batch-information.resource';
7
7
  import { useStockItemBatchNumbers } from '../hooks/useStockItemBatchNumbers';
@@ -16,7 +16,6 @@ interface BatchNoSelectorProps {
16
16
  const BatchNoSelector: React.FC<BatchNoSelectorProps> = ({ stockItemUuid, error, initialValue, onValueChange }) => {
17
17
  const { isLoading, stockItemBatchNos } = useStockItemBatchNumbers(stockItemUuid);
18
18
  const { t } = useTranslation();
19
-
20
19
  const { items, setStockItemUuid, isLoading: isLoadingBatchinfo } = useStockItemBatchInformationHook();
21
20
 
22
21
  useEffect(() => {
@@ -24,46 +23,93 @@ const BatchNoSelector: React.FC<BatchNoSelectorProps> = ({ stockItemUuid, error,
24
23
  }, [stockItemUuid, setStockItemUuid]);
25
24
 
26
25
  const stockItemBatchesInfo = useMemo(() => {
27
- return stockItemBatchNos?.map((item) => {
26
+ if (!stockItemBatchNos) return [];
27
+
28
+ return stockItemBatchNos.map((item) => {
28
29
  const matchingBatch = items?.find((batch) => batch.batchNumber === item.batchNo);
29
- if (matchingBatch) {
30
- return {
31
- ...item,
32
- quantity: matchingBatch.quantity ?? '',
33
- };
34
- }
35
- return item;
30
+
31
+ return matchingBatch
32
+ ? ({
33
+ ...item,
34
+ ...matchingBatch,
35
+ quantity: String(matchingBatch.quantity),
36
+ } as StockBatchWithUoM)
37
+ : (item as StockBatchWithUoM);
36
38
  });
37
39
  }, [stockItemBatchNos, items]);
38
40
 
39
41
  const filteredBatches = useMemo(() => {
40
- return stockItemBatchesInfo?.filter((s) => s.quantity !== undefined && s.quantity !== 0);
42
+ if (!stockItemBatchesInfo) return [];
43
+
44
+ return stockItemBatchesInfo.filter((batch) => {
45
+ const quantity = typeof batch.quantity === 'string' ? parseFloat(batch.quantity) : batch.quantity;
46
+
47
+ return !isNaN(quantity) && quantity > 0;
48
+ });
41
49
  }, [stockItemBatchesInfo]);
50
+
42
51
  const initialSelectedItem = useMemo(
43
- () => filteredBatches?.find((s) => s.uuid === initialValue),
52
+ () => filteredBatches.find((batch) => batch.uuid === initialValue) ?? null,
44
53
  [filteredBatches, initialValue],
45
54
  );
46
55
 
47
- if (isLoading || isLoadingBatchinfo) return <SelectSkeleton role="progressbar" />;
56
+ const formatQuantityDisplay = useCallback(
57
+ (batch: StockBatchWithUoM): string => {
58
+ if (batch.quantity === undefined) return t('unknown', 'Unknown');
59
+
60
+ const quantity = typeof batch.quantity === 'string' ? parseFloat(batch.quantity) : batch.quantity;
61
+
62
+ if (isNaN(quantity)) return t('unknown', 'Unknown');
63
+
64
+ const baseQuantity = quantity.toString();
65
+
66
+ if (!batch.quantityUoM) return baseQuantity;
67
+
68
+ const withUnit = `${baseQuantity} ${batch.quantityUoM}`;
69
+
70
+ if (!batch.quantityFactor) return withUnit;
71
+
72
+ const factor = parseFloat(batch.quantityFactor);
73
+ return !isNaN(factor) && factor > 1 ? `${withUnit} (${factor} units each)` : withUnit;
74
+ },
75
+ [t],
76
+ );
77
+
78
+ const itemToString = useCallback(
79
+ (batch: StockBatchWithUoM | null): string => {
80
+ if (!batch?.batchNo) return '';
81
+
82
+ const quantityDisplay = formatQuantityDisplay(batch);
83
+ const expiryDate = batch.expiration ? formatForDatePicker(batch.expiration) : t('noExpiry', 'No expiry');
84
+
85
+ return `${batch.batchNo} | Qty: ${quantityDisplay} | Expiry: ${expiryDate}`;
86
+ },
87
+ [formatQuantityDisplay, t],
88
+ );
89
+
90
+ const handleChange = useCallback(
91
+ (data: { selectedItem?: StockBatchWithUoM | null }) => {
92
+ onValueChange?.(data.selectedItem?.uuid ?? '');
93
+ },
94
+ [onValueChange],
95
+ );
96
+
97
+ if (isLoading || isLoadingBatchinfo) {
98
+ return <SelectSkeleton role="progressbar" />;
99
+ }
48
100
 
49
101
  return (
50
102
  <ComboBox
51
- id={'stockBatchUuid'}
103
+ id="stockBatchUuid"
52
104
  invalid={!!error}
53
105
  invalidText={error}
54
- items={filteredBatches || []}
55
- itemToString={(s: StockBatchDTO) =>
56
- s?.batchNo
57
- ? `${s?.batchNo} | Qty: ${s?.quantity ?? 'Unknown'} | Expiry: ${formatForDatePicker(s.expiration)}`
58
- : ''
59
- }
60
- name={'stockBatchUuid'}
61
- onChange={(data: { selectedItem?: StockBatchDTO }) => {
62
- onValueChange(data.selectedItem?.uuid);
63
- }}
64
- placeholder={t('filter', "'Filter") + '...'}
106
+ items={filteredBatches}
107
+ itemToString={itemToString}
108
+ name="stockBatchUuid"
109
+ onChange={handleChange}
110
+ placeholder={`${t('filter', 'Filter')}...`}
65
111
  selectedItem={initialSelectedItem}
66
- style={{ flexGrow: '1' }}
112
+ style={{ flexGrow: 1 }}
67
113
  titleText={t('batchNo', 'Batch no.')}
68
114
  />
69
115
  );
@@ -3,3 +3,13 @@ export interface DashboardConfig {
3
3
  slot: string;
4
4
  title: string;
5
5
  }
6
+
7
+ export interface OutofStockListResponse {
8
+ results: Array<{
9
+ partyUuid: string;
10
+ locationUuid: string;
11
+ partyId: number;
12
+ partyName: string;
13
+ outOfStock: number;
14
+ }>;
15
+ }