@openmrs/esm-stock-management-app 1.0.1-pre.783 → 1.0.1-pre.785
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/__mocks__/index.ts +1 -0
- package/__mocks__/operation-type.mock.ts +532 -0
- package/dist/155.js +1 -0
- package/dist/155.js.map +1 -0
- package/dist/172.js +1 -1
- package/dist/20.js +1 -1
- package/dist/290.js +1 -1
- package/dist/493.js +2 -0
- package/dist/493.js.map +1 -0
- package/dist/606.js +1 -1
- package/dist/627.js +1 -1
- package/dist/914.js +1 -0
- package/dist/914.js.map +1 -0
- package/dist/main.js +1 -1
- package/dist/main.js.map +1 -1
- package/dist/openmrs-esm-stock-management-app.js +1 -1
- package/dist/openmrs-esm-stock-management-app.js.buildmanifest.json +75 -51
- package/dist/openmrs-esm-stock-management-app.js.map +1 -1
- package/dist/routes.json +1 -1
- package/package.json +1 -1
- package/src/core/utils/utils.ts +29 -0
- package/src/index.ts +4 -0
- package/src/routes.json +9 -0
- package/src/stock-items/add-stock-item/transactions/printout/transactions-stockcard-printout.component.tsx +8 -12
- package/src/stock-items/add-stock-item/transactions/transactions.component.tsx +8 -12
- package/src/stock-items/stock-items.resource.ts +5 -5
- package/src/stock-lookups/stock-lookups.resource.ts +2 -2
- package/src/stock-operations/edit-stock-operation/edit-stock-operation-action-menu.component.tsx +41 -16
- package/src/stock-operations/{add-stock-operation/received-items.component.tsx → received-items.component.tsx} +1 -1
- package/src/stock-operations/stock-operation-reference.component.tsx +64 -0
- package/src/stock-operations/stock-operation-status/stock-operation-status-row.tsx +77 -0
- package/src/stock-operations/stock-operation-status/stock-operation-status.scss +32 -0
- package/src/stock-operations/stock-operation-status/stock-operation-status.tsx +45 -0
- package/src/stock-operations/stock-operation-types-selector/stock-operation-types-selector.component.tsx +30 -29
- package/src/stock-operations/stock-operation.utils.tsx +16 -79
- package/src/stock-operations/stock-operations-dialog/stock-operations-issue-stock-button.component.tsx +27 -39
- package/src/stock-operations/stock-operations-dialog/stock-operations-print-button.component.tsx +51 -59
- package/src/stock-operations/{stock-item-selector/stock-item-selector.resource.tsx → stock-operations-forms/hooks/useFilterableStockItems.ts} +4 -4
- package/src/stock-operations/stock-operations-forms/hooks/useFilteredOperationTypesByRoles.ts +30 -0
- package/src/stock-operations/stock-operations-forms/hooks/useOperationTypePermisions.ts +29 -0
- package/src/stock-operations/stock-operations-forms/hooks/useParties.ts +73 -0
- package/src/stock-operations/{users-selector/users-selector.resource.tsx → stock-operations-forms/hooks/useSearchUser.ts} +9 -7
- package/src/stock-operations/{batch-no-selector/batch-no-selector.resource.tsx → stock-operations-forms/hooks/useStockItemBatchNumbers.ts} +3 -3
- package/src/stock-operations/stock-operations-forms/hooks/useStockOperationLinks.ts +20 -0
- package/src/stock-operations/stock-operations-forms/input-components/batch-no-selector.component.tsx +72 -0
- package/src/stock-operations/stock-operations-forms/input-components/batch-no-selector.test.tsx +90 -0
- package/src/stock-operations/{add-stock-operation/stock-item-search/stock-item-search.scss → stock-operations-forms/input-components/input-components-styles.scss} +2 -2
- package/src/stock-operations/stock-operations-forms/input-components/qty-uim-selector.test.tsx +157 -0
- package/src/stock-operations/stock-operations-forms/input-components/quantity-uom-selector.component.tsx +53 -0
- package/src/stock-operations/stock-operations-forms/input-components/stock-item-search.component.tsx +56 -0
- package/src/stock-operations/stock-operations-forms/input-components/stock-operation-reason-selector.component.tsx +59 -0
- package/src/stock-operations/stock-operations-forms/input-components/stock-operation-reason-selector.test.tsx +216 -0
- package/src/stock-operations/{batch-no-selector → stock-operations-forms/input-components}/unique-batch-no-entry-input.component.tsx +12 -7
- package/src/stock-operations/stock-operations-forms/input-components/user-selector.test.tsx +110 -0
- package/src/stock-operations/stock-operations-forms/input-components/users-selector.component.tsx +111 -0
- package/src/stock-operations/stock-operations-forms/step1.test.tsx +303 -0
- package/src/stock-operations/stock-operations-forms/step2.test.tsx +250 -0
- package/src/stock-operations/stock-operations-forms/step3.test.tsx +223 -0
- package/src/stock-operations/stock-operations-forms/steps/base-operation-details-form-step.tsx +241 -0
- package/src/stock-operations/stock-operations-forms/steps/quantity-uom-cell.component.tsx +33 -0
- package/src/stock-operations/stock-operations-forms/steps/stock-availability-cell.component.tsx +51 -0
- package/src/stock-operations/stock-operations-forms/steps/stock-operation-item-batch-no-cell.component.tsx +40 -0
- package/src/stock-operations/stock-operations-forms/steps/stock-operation-item-cell.component.tsx +38 -0
- package/src/stock-operations/stock-operations-forms/steps/stock-operation-item-expiry-cell.component.tsx +41 -0
- package/src/stock-operations/stock-operations-forms/steps/stock-operation-items-form-step.component.tsx +281 -0
- package/src/stock-operations/stock-operations-forms/steps/stock-operation-items-form-step.scc.scss +64 -0
- package/src/stock-operations/stock-operations-forms/steps/stock-operation-submission-form-step.component.tsx +236 -0
- package/src/stock-operations/stock-operations-forms/stock-issue-form-initializer-with-related-requisition-operation.component.tsx +55 -0
- package/src/stock-operations/stock-operations-forms/stock-item-form/stock-item-form.scss +41 -0
- package/src/stock-operations/stock-operations-forms/stock-item-form/stock-item-form.workspace.tsx +197 -0
- package/src/stock-operations/stock-operations-forms/stock-operation-form-header.component.tsx +166 -0
- package/src/stock-operations/stock-operations-forms/stock-operation-form.component.tsx +200 -0
- package/src/stock-operations/stock-operations-forms/stock-operation-form.scss +111 -0
- package/src/stock-operations/stock-operations-forms/stock-operation-related-link.component.tsx +45 -0
- package/src/stock-operations/stock-operations-forms/stock-operation-stepper/stepper.scss +41 -0
- package/src/stock-operations/stock-operations-forms/stock-operation-stepper/stock-operation-stepper.component.tsx +52 -0
- package/src/stock-operations/stock-operations-forms/stock-operations-form-utils.ts +32 -0
- package/src/stock-operations/stock-operations-table.component.tsx +20 -56
- package/src/stock-operations/stock-operations.resource.ts +16 -13
- package/src/stock-operations/validation-schema.ts +72 -14
- package/dist/766.js +0 -2
- package/dist/766.js.map +0 -1
- package/dist/822.js +0 -1
- package/dist/822.js.map +0 -1
- package/src/stock-operations/add-stock-operation/add-stock-operation.component.tsx +0 -349
- package/src/stock-operations/add-stock-operation/add-stock-operation.resource.tsx +0 -27
- package/src/stock-operations/add-stock-operation/add-stock-operation.scss +0 -60
- package/src/stock-operations/add-stock-operation/add-stock-operation.test.tsx +0 -192
- package/src/stock-operations/add-stock-operation/add-stock-operation.utils.tsx +0 -152
- package/src/stock-operations/add-stock-operation/add-stock-utils.ts +0 -103
- package/src/stock-operations/add-stock-operation/base-operation-details.component.tsx +0 -439
- package/src/stock-operations/add-stock-operation/base-operation-details.scss +0 -30
- package/src/stock-operations/add-stock-operation/stock-item-search/stock-item-search.component.tsx +0 -70
- package/src/stock-operations/add-stock-operation/stock-items-addition-row.component.tsx +0 -357
- package/src/stock-operations/add-stock-operation/stock-items-addition-row.resource.tsx +0 -0
- package/src/stock-operations/add-stock-operation/stock-items-addition-row.scss +0 -12
- package/src/stock-operations/add-stock-operation/stock-items-addition-row.test.tsx +0 -10
- package/src/stock-operations/add-stock-operation/stock-items-addition.component.scss +0 -17
- package/src/stock-operations/add-stock-operation/stock-items-addition.component.tsx +0 -254
- package/src/stock-operations/add-stock-operation/stock-operation-context/useStockOperationContext.tsx +0 -16
- package/src/stock-operations/add-stock-operation/stock-operation-reference.component.tsx +0 -39
- package/src/stock-operations/add-stock-operation/stock-operation-related-link.component.tsx +0 -38
- package/src/stock-operations/add-stock-operation/stock-operation-status.component.tsx +0 -170
- package/src/stock-operations/add-stock-operation/stock-operation-submission.component.tsx +0 -189
- package/src/stock-operations/add-stock-operation/stock-operation-submission.test.tsx +0 -138
- package/src/stock-operations/add-stock-operation/types.ts +0 -55
- package/src/stock-operations/add-stock-operation/validationSchema.ts +0 -54
- package/src/stock-operations/batch-no-selector/batch-no-selector.component.tsx +0 -114
- package/src/stock-operations/batch-no-selector/batch-no-selector.scss +0 -0
- package/src/stock-operations/batch-no-selector/batch-no-selector.test.tsx +0 -101
- package/src/stock-operations/party-selector/party-selector.component.tsx +0 -59
- package/src/stock-operations/qty-uom-selector/qty-uom-selector.component.tsx +0 -65
- package/src/stock-operations/qty-uom-selector/qty-uom-selector.resource.tsx +0 -0
- package/src/stock-operations/qty-uom-selector/qty-uom-selector.scss +0 -0
- package/src/stock-operations/qty-uom-selector/qty-uom-selector.test.tsx +0 -10
- package/src/stock-operations/stock-item-selector/stock-item-selector.component.tsx +0 -69
- package/src/stock-operations/stock-item-selector/stock-item-selector.scss +0 -0
- package/src/stock-operations/stock-item-selector/stock-item-selector.test.tsx +0 -10
- package/src/stock-operations/stock-operation-reason-selector/stock-operation-reason-selector.component.tsx +0 -62
- package/src/stock-operations/users-selector/users-selector.component.tsx +0 -75
- /package/dist/{766.js.LICENSE.txt → 493.js.LICENSE.txt} +0 -0
package/src/stock-operations/stock-operations-dialog/stock-operations-print-button.component.tsx
CHANGED
@@ -1,50 +1,50 @@
|
|
1
|
-
import React from 'react';
|
1
|
+
import React, { useState } from 'react';
|
2
2
|
|
3
3
|
import { Button } from '@carbon/react';
|
4
|
-
import { useTranslation } from 'react-i18next';
|
5
4
|
import { Printer } from '@carbon/react/icons';
|
5
|
+
import { useTranslation } from 'react-i18next';
|
6
|
+
import { StockItemInventory } from '../../core/api/types/stockItem/StockItemInventory';
|
6
7
|
import { StockOperationDTO } from '../../core/api/types/stockOperation/StockOperationDTO';
|
7
8
|
import { StockOperationItemCost } from '../../core/api/types/stockOperation/StockOperationItemCost';
|
8
|
-
import { StockItemInventory } from '../../core/api/types/stockItem/StockItemInventory';
|
9
9
|
|
10
|
-
import {
|
10
|
+
import { InlineLoading } from '@carbon/react';
|
11
|
+
import { showSnackbar } from '@openmrs/esm-framework';
|
12
|
+
import { extractErrorMessagesFromResponse } from '../../constants';
|
11
13
|
import { ResourceRepresentation } from '../../core/api/api';
|
12
|
-
import {
|
14
|
+
import { OperationType } from '../../core/api/types/stockOperation/StockOperationType';
|
15
|
+
import { StockItemInventoryFilter } from '../../stock-items/stock-items.resource';
|
16
|
+
import {
|
17
|
+
getStockItemInventory,
|
18
|
+
getStockOperation,
|
19
|
+
getStockOperationItemsCost,
|
20
|
+
useStockOperationAndItems,
|
21
|
+
} from '../stock-operations.resource';
|
13
22
|
import { PrintGoodsReceivedNoteStockOperation } from '../stock-print-reports/GoodsReceivedNote';
|
14
|
-
import { PrintTransferOutStockOperation } from '../stock-print-reports/StockTransferDocument';
|
15
23
|
import { PrintRequisitionStockOperation } from '../stock-print-reports/RequisitionDocument';
|
16
|
-
import {
|
24
|
+
import { BuildStockOperationData } from '../stock-print-reports/StockOperationReport';
|
25
|
+
import { PrintTransferOutStockOperation } from '../stock-print-reports/StockTransferDocument';
|
17
26
|
|
18
27
|
interface StockOperationCancelButtonProps {
|
19
28
|
operation: StockOperationDTO;
|
20
29
|
}
|
21
30
|
|
22
|
-
const StockOperationPrintButton: React.FC<StockOperationCancelButtonProps> = ({ operation }) => {
|
31
|
+
const StockOperationPrintButton: React.FC<StockOperationCancelButtonProps> = ({ operation: _operation }) => {
|
23
32
|
const { t } = useTranslation();
|
24
|
-
|
33
|
+
const { isLoading, items: operation, error } = useStockOperationAndItems(_operation.uuid);
|
34
|
+
const [loading, setLoading] = useState(false);
|
25
35
|
const onPrintStockOperation = async () => {
|
36
|
+
setLoading(true);
|
26
37
|
try {
|
27
38
|
let parentOperation: StockOperationDTO | null | undefined;
|
28
39
|
let itemsCost: StockOperationItemCost[] | null | undefined = null;
|
29
40
|
let itemInventory: StockItemInventory[] | null | undefined = null;
|
30
41
|
|
31
42
|
if (operation.requisitionStockOperationUuid) {
|
32
|
-
// get stock operation
|
33
|
-
getStockOperation(operation.requisitionStockOperationUuid)
|
34
|
-
|
35
|
-
if ((payload as any).error) {
|
36
|
-
return;
|
37
|
-
}
|
38
|
-
parentOperation = payload;
|
39
|
-
})
|
40
|
-
.catch((error: any) => {
|
41
|
-
if ((error as any).error) {
|
42
|
-
return;
|
43
|
-
}
|
44
|
-
return;
|
45
|
-
});
|
43
|
+
// get related requisition stock operation
|
44
|
+
const response = await getStockOperation(operation.requisitionStockOperationUuid);
|
45
|
+
parentOperation = response.data;
|
46
46
|
if (!parentOperation) {
|
47
|
-
return
|
47
|
+
return;
|
48
48
|
}
|
49
49
|
}
|
50
50
|
|
@@ -59,19 +59,8 @@ const StockOperationPrintButton: React.FC<StockOperationCancelButtonProps> = ({
|
|
59
59
|
if (operation?.uuid) {
|
60
60
|
inventoryFilter.stockOperationUuid = operation.uuid;
|
61
61
|
}
|
62
|
-
getStockOperationItemsCost(inventoryFilter)
|
63
|
-
|
64
|
-
if ((payload as any).error) {
|
65
|
-
return;
|
66
|
-
}
|
67
|
-
itemsCost = payload?.results;
|
68
|
-
})
|
69
|
-
.catch((error: any) => {
|
70
|
-
if ((error as any).error) {
|
71
|
-
return;
|
72
|
-
}
|
73
|
-
return;
|
74
|
-
});
|
62
|
+
const res = await getStockOperationItemsCost(inventoryFilter);
|
63
|
+
itemsCost = res.data?.results;
|
75
64
|
}
|
76
65
|
}
|
77
66
|
const enableBalance = true;
|
@@ -88,35 +77,22 @@ const StockOperationPrintButton: React.FC<StockOperationCancelButtonProps> = ({
|
|
88
77
|
inventoryFilter.groupBy = 'LocationStockItem';
|
89
78
|
inventoryFilter.includeStockItemName = 'true';
|
90
79
|
|
91
|
-
inventoryFilter.date =
|
92
|
-
|
80
|
+
inventoryFilter.date = (parentOperation?.dateCreated ?? operation?.dateCreated) as any;
|
93
81
|
// get stock item inventory
|
94
|
-
getStockItemInventory(inventoryFilter)
|
95
|
-
|
96
|
-
if ((payload as any).error) {
|
97
|
-
return;
|
98
|
-
}
|
99
|
-
itemInventory = payload?.results;
|
100
|
-
})
|
101
|
-
.catch((error: any) => {
|
102
|
-
if ((error as any).error) {
|
103
|
-
return;
|
104
|
-
}
|
105
|
-
return;
|
106
|
-
});
|
82
|
+
const res = await getStockItemInventory(inventoryFilter);
|
83
|
+
itemInventory = res.data?.results;
|
107
84
|
}
|
108
|
-
|
109
85
|
const data = await BuildStockOperationData(
|
110
86
|
operation,
|
111
|
-
operation.stockOperationItems,
|
87
|
+
operation.stockOperationItems ?? _operation?.stockOperationItems ?? [],
|
112
88
|
parentOperation,
|
113
89
|
itemsCost,
|
114
90
|
itemInventory,
|
115
91
|
);
|
116
92
|
if (data) {
|
117
|
-
if (operation?.operationType ===
|
93
|
+
if (operation?.operationType === OperationType.RECEIPT_OPERATION_TYPE) {
|
118
94
|
await PrintGoodsReceivedNoteStockOperation(data);
|
119
|
-
} else if (operation?.operationType ===
|
95
|
+
} else if (operation?.operationType === OperationType.TRANSFER_OUT_OPERATION_TYPE) {
|
120
96
|
await PrintTransferOutStockOperation(data);
|
121
97
|
} else {
|
122
98
|
await PrintRequisitionStockOperation(data);
|
@@ -124,14 +100,30 @@ const StockOperationPrintButton: React.FC<StockOperationCancelButtonProps> = ({
|
|
124
100
|
} else {
|
125
101
|
console.info(data);
|
126
102
|
}
|
127
|
-
} catch (e) {
|
103
|
+
} catch (e: any) {
|
128
104
|
console.info(e);
|
105
|
+
showSnackbar({
|
106
|
+
kind: 'error',
|
107
|
+
title: t('errorPrintingStockOperation', 'Error printing stock operation'),
|
108
|
+
subtitle: extractErrorMessagesFromResponse(e).join(', '),
|
109
|
+
});
|
110
|
+
} finally {
|
111
|
+
setLoading(false);
|
129
112
|
}
|
130
113
|
};
|
131
114
|
|
132
115
|
return (
|
133
|
-
<Button
|
134
|
-
{
|
116
|
+
<Button
|
117
|
+
onClick={onPrintStockOperation}
|
118
|
+
kind="tertiary"
|
119
|
+
disabled={isLoading || loading}
|
120
|
+
renderIcon={(props) => <Printer size={16} {...props} />}
|
121
|
+
>
|
122
|
+
{loading || isLoading ? (
|
123
|
+
<InlineLoading description={t('loading', 'Loading') + '...'} iconDescription={t('loading', 'Loading')} />
|
124
|
+
) : (
|
125
|
+
t('print', 'Print')
|
126
|
+
)}
|
135
127
|
</Button>
|
136
128
|
);
|
137
129
|
};
|
@@ -1,9 +1,9 @@
|
|
1
|
-
import { UserFilterCriteria } from '../../stock-lookups/stock-lookups.resource';
|
2
1
|
import { useEffect, useState } from 'react';
|
3
|
-
import {
|
4
|
-
import {
|
2
|
+
import { StockItemFilter, useStockItems as useStockItemsData } from '../../../stock-items/stock-items.resource';
|
3
|
+
import { UserFilterCriteria } from '../../../stock-lookups/stock-lookups.resource';
|
4
|
+
import { ResourceRepresentation } from '../../../core/api/api';
|
5
5
|
|
6
|
-
export function
|
6
|
+
export function useFilterableStockItems(filter?: StockItemFilter) {
|
7
7
|
const [conceptFilter, setConceptFilter] = useState<UserFilterCriteria>(
|
8
8
|
filter || {
|
9
9
|
v: ResourceRepresentation.Default,
|
@@ -0,0 +1,30 @@
|
|
1
|
+
import { useMemo } from 'react';
|
2
|
+
import { useStockOperationTypes, useUserRoles } from '../../../stock-lookups/stock-lookups.resource';
|
3
|
+
|
4
|
+
const useFilteredOperationTypesByRoles = () => {
|
5
|
+
const {
|
6
|
+
types: { results },
|
7
|
+
isLoading: isStockOperationTypesLoading,
|
8
|
+
error: stockOperationTypesError,
|
9
|
+
} = useStockOperationTypes();
|
10
|
+
|
11
|
+
const { userRoles, isLoading: isUserRolesLoading, error: userRolesError } = useUserRoles();
|
12
|
+
|
13
|
+
const operationTypes = useMemo(() => {
|
14
|
+
const applicablePrivilegeScopes = userRoles?.operationTypes?.map((p) => p.operationTypeUuid) || [];
|
15
|
+
const uniqueApplicablePrivilegeScopes = [...new Set(applicablePrivilegeScopes)];
|
16
|
+
|
17
|
+
return results?.filter((p) => uniqueApplicablePrivilegeScopes.includes(p.uuid)) || [];
|
18
|
+
}, [results, userRoles]);
|
19
|
+
|
20
|
+
const isLoading = isStockOperationTypesLoading || isUserRolesLoading;
|
21
|
+
const error = stockOperationTypesError || userRolesError;
|
22
|
+
|
23
|
+
return {
|
24
|
+
operationTypes,
|
25
|
+
isLoading,
|
26
|
+
error,
|
27
|
+
};
|
28
|
+
};
|
29
|
+
|
30
|
+
export default useFilteredOperationTypesByRoles;
|
@@ -0,0 +1,29 @@
|
|
1
|
+
import {
|
2
|
+
operationFromString,
|
3
|
+
StockOperationType,
|
4
|
+
StockOperationTypeCanCapturePurchasePrice,
|
5
|
+
StockOperationTypeHasPrint,
|
6
|
+
StockOperationTypeIsNegativeQtyAllowed,
|
7
|
+
StockOperationTypeIsQuantityOptional,
|
8
|
+
StockOperationTypeRequiresActualBatchInformation,
|
9
|
+
StockOperationTypeRequiresBatchUuid,
|
10
|
+
StockOperationTypeRequiresDispatchAcknowledgement,
|
11
|
+
StockOperationTypeRequiresStockAdjustmentReason,
|
12
|
+
} from '../../../core/api/types/stockOperation/StockOperationType';
|
13
|
+
|
14
|
+
const useOperationTypePermisions = (stockoperationType: StockOperationType) => {
|
15
|
+
const opType = operationFromString(stockoperationType.operationType);
|
16
|
+
return {
|
17
|
+
isNegativeQuantityAllowed: StockOperationTypeIsNegativeQtyAllowed(opType),
|
18
|
+
requiresBatchUuid: StockOperationTypeRequiresBatchUuid(opType),
|
19
|
+
requiresActualBatchInfo: StockOperationTypeRequiresActualBatchInformation(opType),
|
20
|
+
isQuantityOptional: StockOperationTypeIsQuantityOptional(opType),
|
21
|
+
canCaptureQuantityPrice: StockOperationTypeCanCapturePurchasePrice(opType),
|
22
|
+
requiresStockAdjustmentReason: StockOperationTypeRequiresStockAdjustmentReason(opType),
|
23
|
+
requiresDispatchAcknowledgement: StockOperationTypeRequiresDispatchAcknowledgement(opType),
|
24
|
+
allowExpiredBatchNumbers: stockoperationType?.allowExpiredBatchNumbers ?? false,
|
25
|
+
allowPrinting: StockOperationTypeHasPrint(opType),
|
26
|
+
};
|
27
|
+
};
|
28
|
+
|
29
|
+
export default useOperationTypePermisions;
|
@@ -0,0 +1,73 @@
|
|
1
|
+
import { FetchResponse, openmrsFetch, restBaseUrl } from '@openmrs/esm-framework';
|
2
|
+
import React, { useCallback, useMemo } from 'react';
|
3
|
+
import useSWR from 'swr';
|
4
|
+
import { Party } from '../../../core/api/types/Party';
|
5
|
+
import { LocationTypeLocation, LocationTypeOther } from '../../../core/api/types/stockOperation/LocationType';
|
6
|
+
import { StockOperationType } from '../../../core/api/types/stockOperation/StockOperationType';
|
7
|
+
|
8
|
+
const useParties = (stockOperationType: StockOperationType) => {
|
9
|
+
const apiUrl = `${restBaseUrl}/stockmanagement/party?v=default`;
|
10
|
+
const { data, isLoading, mutate, error } = useSWR<FetchResponse<{ results: Array<Party> }>>(apiUrl, openmrsFetch);
|
11
|
+
const sourceTags = useMemo(() => {
|
12
|
+
return (
|
13
|
+
stockOperationType?.stockOperationTypeLocationScopes
|
14
|
+
?.filter((p) => stockOperationType?.hasSource && p.isSource)
|
15
|
+
.map((p) => p.locationTag) ?? []
|
16
|
+
);
|
17
|
+
}, [stockOperationType]);
|
18
|
+
|
19
|
+
const destinationTags = useMemo(() => {
|
20
|
+
return (
|
21
|
+
stockOperationType?.stockOperationTypeLocationScopes
|
22
|
+
?.filter((p) => stockOperationType?.hasDestination && p.isDestination)
|
23
|
+
.map((p) => p.locationTag) ?? []
|
24
|
+
);
|
25
|
+
}, [stockOperationType]);
|
26
|
+
|
27
|
+
const sourcePartiesFilter = useCallback(
|
28
|
+
(p: Party) => {
|
29
|
+
return (
|
30
|
+
(p.locationUuid &&
|
31
|
+
stockOperationType?.sourceType === LocationTypeLocation &&
|
32
|
+
(sourceTags.length === 0 || (p.tags && sourceTags.some((x) => p.tags.includes(x))))) ||
|
33
|
+
(p.stockSourceUuid && stockOperationType?.sourceType === LocationTypeOther)
|
34
|
+
);
|
35
|
+
},
|
36
|
+
[stockOperationType, sourceTags],
|
37
|
+
);
|
38
|
+
|
39
|
+
const destinationPartiesFilter = useCallback(
|
40
|
+
(p: Party) => {
|
41
|
+
return (
|
42
|
+
(p.locationUuid &&
|
43
|
+
stockOperationType?.destinationType === LocationTypeLocation &&
|
44
|
+
(destinationTags.length === 0 || (p.tags && destinationTags.some((x) => p.tags.includes(x))))) ||
|
45
|
+
(p.stockSourceUuid && stockOperationType?.destinationType === LocationTypeOther)
|
46
|
+
);
|
47
|
+
},
|
48
|
+
[stockOperationType, destinationTags],
|
49
|
+
);
|
50
|
+
|
51
|
+
const sourceParties = useMemo(() => {
|
52
|
+
return data?.data?.results?.filter(sourcePartiesFilter) ?? [];
|
53
|
+
}, [data, sourcePartiesFilter]);
|
54
|
+
|
55
|
+
const destinationParties = useMemo(() => {
|
56
|
+
return data?.data?.results?.filter(destinationPartiesFilter) ?? [];
|
57
|
+
}, [data, destinationPartiesFilter]);
|
58
|
+
|
59
|
+
return {
|
60
|
+
parties: data?.data?.results ?? [],
|
61
|
+
isLoading,
|
62
|
+
mutate,
|
63
|
+
sourceParties,
|
64
|
+
destinationParties,
|
65
|
+
sourcePartiesFilter,
|
66
|
+
destinationPartiesFilter,
|
67
|
+
error,
|
68
|
+
sourceTags,
|
69
|
+
destinationTags,
|
70
|
+
};
|
71
|
+
};
|
72
|
+
|
73
|
+
export default useParties;
|
@@ -1,8 +1,8 @@
|
|
1
|
-
import {
|
2
|
-
import {
|
3
|
-
import { ResourceRepresentation } from '
|
1
|
+
import React, { useEffect, useState } from 'react';
|
2
|
+
import { ConceptFilterCriteria, UserFilterCriteria, useUsers } from '../../../stock-lookups/stock-lookups.resource';
|
3
|
+
import { ResourceRepresentation } from '../../../core/api/api';
|
4
4
|
|
5
|
-
|
5
|
+
const useSearchUser = (filter?: ConceptFilterCriteria) => {
|
6
6
|
const [conceptFilter, setConceptFilter] = useState<UserFilterCriteria>(
|
7
7
|
filter || {
|
8
8
|
v: ResourceRepresentation.Default,
|
@@ -20,12 +20,12 @@ export function useUsersHook(filter?: ConceptFilterCriteria) {
|
|
20
20
|
|
21
21
|
// Drug filter type
|
22
22
|
const [limit, setLimit] = useState(filter?.limit || 10);
|
23
|
-
const [representation, setRepresentation] = useState(filter?.v || ResourceRepresentation.Default);
|
23
|
+
const [representation, setRepresentation] = useState<string>(filter?.v || ResourceRepresentation.Default);
|
24
24
|
|
25
25
|
useEffect(() => {
|
26
26
|
setConceptFilter({
|
27
27
|
startIndex: 0,
|
28
|
-
v: representation,
|
28
|
+
v: representation as ResourceRepresentation,
|
29
29
|
limit: limit,
|
30
30
|
q: searchString,
|
31
31
|
});
|
@@ -38,4 +38,6 @@ export function useUsersHook(filter?: ConceptFilterCriteria) {
|
|
38
38
|
setSearchString,
|
39
39
|
isLoading,
|
40
40
|
};
|
41
|
-
}
|
41
|
+
};
|
42
|
+
|
43
|
+
export default useSearchUser;
|
@@ -1,8 +1,8 @@
|
|
1
|
-
import { StockBatchFilter, useStockBatches } from '../../stock-items/stock-items.resource';
|
2
1
|
import { useEffect, useState } from 'react';
|
3
|
-
import { ResourceRepresentation } from '
|
2
|
+
import { ResourceRepresentation } from '../../../core/api/api';
|
3
|
+
import { StockBatchFilter, useStockBatches } from '../../../stock-items/stock-items.resource';
|
4
4
|
|
5
|
-
export function
|
5
|
+
export function useStockItemBatchNumbers(stockItemUuid: string) {
|
6
6
|
const [conceptFilter, setConceptFilter] = useState<StockBatchFilter>({
|
7
7
|
v: ResourceRepresentation.Default,
|
8
8
|
limit: 10,
|
@@ -0,0 +1,20 @@
|
|
1
|
+
import { FetchResponse, openmrsFetch, restBaseUrl } from '@openmrs/esm-framework';
|
2
|
+
import useSWR from 'swr';
|
3
|
+
import { StockOperationLinkDTO } from '../../../core/api/types/stockOperation/StockOperationLinkDTO';
|
4
|
+
|
5
|
+
const useStockOperationLinks = (stockOperationUuid?: string) => {
|
6
|
+
const apiUrl = `${restBaseUrl}/stockmanagement/stockoperationlink?v=default&q=${stockOperationUuid}`;
|
7
|
+
const { data, error, isLoading, mutate } = useSWR<
|
8
|
+
FetchResponse<{
|
9
|
+
results: Array<StockOperationLinkDTO>;
|
10
|
+
}>
|
11
|
+
>(stockOperationUuid ? apiUrl : null, openmrsFetch);
|
12
|
+
return {
|
13
|
+
error,
|
14
|
+
isLoading,
|
15
|
+
mutate,
|
16
|
+
operationLinks: data?.data?.results ?? [],
|
17
|
+
};
|
18
|
+
};
|
19
|
+
|
20
|
+
export default useStockOperationLinks;
|
package/src/stock-operations/stock-operations-forms/input-components/batch-no-selector.component.tsx
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
import { ComboBox, SelectSkeleton } from '@carbon/react';
|
2
|
+
import React, { useEffect, useMemo } from 'react';
|
3
|
+
import { useTranslation } from 'react-i18next';
|
4
|
+
import { StockBatchDTO } from '../../../core/api/types/stockItem/StockBatchDTO';
|
5
|
+
import { useStockItemBatchInformationHook } from '../../../stock-items/add-stock-item/batch-information/batch-information.resource';
|
6
|
+
import { useStockItemBatchNumbers } from '../hooks/useStockItemBatchNumbers';
|
7
|
+
import { formatForDatePicker } from '../../../constants';
|
8
|
+
|
9
|
+
interface BatchNoSelectorProps {
|
10
|
+
stockItemUuid: string;
|
11
|
+
initialValue?: string;
|
12
|
+
onValueChange?: (value: string) => void;
|
13
|
+
error?: string;
|
14
|
+
}
|
15
|
+
|
16
|
+
const BatchNoSelector: React.FC<BatchNoSelectorProps> = ({ stockItemUuid, error, initialValue, onValueChange }) => {
|
17
|
+
const { isLoading, stockItemBatchNos } = useStockItemBatchNumbers(stockItemUuid);
|
18
|
+
const { t } = useTranslation();
|
19
|
+
|
20
|
+
const { items, setStockItemUuid, isLoading: isLoadingBatchinfo } = useStockItemBatchInformationHook();
|
21
|
+
|
22
|
+
useEffect(() => {
|
23
|
+
setStockItemUuid(stockItemUuid);
|
24
|
+
}, [stockItemUuid, setStockItemUuid]);
|
25
|
+
|
26
|
+
const stockItemBatchesInfo = useMemo(() => {
|
27
|
+
return stockItemBatchNos?.map((item) => {
|
28
|
+
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;
|
36
|
+
});
|
37
|
+
}, [stockItemBatchNos, items]);
|
38
|
+
|
39
|
+
const filteredBatches = useMemo(() => {
|
40
|
+
return stockItemBatchesInfo?.filter((s) => s.quantity !== undefined && s.quantity !== 0);
|
41
|
+
}, [stockItemBatchesInfo]);
|
42
|
+
const initialSelectedItem = useMemo(
|
43
|
+
() => filteredBatches?.find((s) => s.uuid === initialValue),
|
44
|
+
[filteredBatches, initialValue],
|
45
|
+
);
|
46
|
+
|
47
|
+
if (isLoading || isLoadingBatchinfo) return <SelectSkeleton role="progressbar" />;
|
48
|
+
|
49
|
+
return (
|
50
|
+
<ComboBox
|
51
|
+
style={{ flexGrow: '1' }}
|
52
|
+
titleText={t('batchNo', 'Batch')}
|
53
|
+
name={'stockBatchUuid'}
|
54
|
+
id={'stockBatchUuid'}
|
55
|
+
items={filteredBatches || []}
|
56
|
+
onChange={(data: { selectedItem?: StockBatchDTO }) => {
|
57
|
+
onValueChange(data.selectedItem?.uuid);
|
58
|
+
}}
|
59
|
+
selectedItem={initialSelectedItem}
|
60
|
+
itemToString={(s: StockBatchDTO) =>
|
61
|
+
s?.batchNo
|
62
|
+
? `${s?.batchNo} | Qty: ${s?.quantity ?? 'Unknown'} | Expiry: ${formatForDatePicker(s.expiration)}`
|
63
|
+
: ''
|
64
|
+
}
|
65
|
+
placeholder={t('filter', "'Filter") + '...'}
|
66
|
+
invalid={error}
|
67
|
+
invalidText={error}
|
68
|
+
/>
|
69
|
+
);
|
70
|
+
};
|
71
|
+
|
72
|
+
export default BatchNoSelector;
|
package/src/stock-operations/stock-operations-forms/input-components/batch-no-selector.test.tsx
ADDED
@@ -0,0 +1,90 @@
|
|
1
|
+
import { render, screen } from '@testing-library/react';
|
2
|
+
import userEvent from '@testing-library/user-event';
|
3
|
+
import React from 'react';
|
4
|
+
import { StockItemInventory } from '../../../core/api/types/stockItem/StockItemInventory';
|
5
|
+
import { useStockItemBatchInformationHook } from '../../../stock-items/add-stock-item/batch-information/batch-information.resource';
|
6
|
+
import { useStockItemBatchNumbers } from '../hooks/useStockItemBatchNumbers';
|
7
|
+
import BatchNoSelector from './batch-no-selector.component';
|
8
|
+
import { formatForDatePicker } from '../../../constants';
|
9
|
+
|
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 }),
|
14
|
+
}));
|
15
|
+
|
16
|
+
const mockUseStockItemBatchNumbers = useStockItemBatchNumbers as jest.Mock;
|
17
|
+
const mockUseStockItemBatchInformationHook = useStockItemBatchInformationHook as jest.Mock;
|
18
|
+
|
19
|
+
describe('BatchNoSelector', () => {
|
20
|
+
const mockOnValueChange = jest.fn();
|
21
|
+
const mockStockItemUuid = 'test-uuid';
|
22
|
+
const mockExpiration = new Date();
|
23
|
+
beforeEach(() => {
|
24
|
+
jest.clearAllMocks();
|
25
|
+
|
26
|
+
mockUseStockItemBatchNumbers.mockReturnValue({
|
27
|
+
isLoading: false,
|
28
|
+
stockItemBatchNos: [
|
29
|
+
{ uuid: '1', batchNo: 'BATCH-001', quantity: 10, expiration: mockExpiration },
|
30
|
+
{ uuid: '2', batchNo: 'BATCH-002', quantity: 20, expiration: mockExpiration },
|
31
|
+
],
|
32
|
+
});
|
33
|
+
mockUseStockItemBatchInformationHook.mockReturnValue({
|
34
|
+
items: [
|
35
|
+
{ batchNumber: 'BATCH-001', quantity: 10 },
|
36
|
+
{ batchNumber: 'BATCH-002', quantity: 20 },
|
37
|
+
] as StockItemInventory[],
|
38
|
+
setStockItemUuid: jest.fn(),
|
39
|
+
});
|
40
|
+
});
|
41
|
+
|
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() });
|
45
|
+
render(<BatchNoSelector stockItemUuid={mockStockItemUuid} onValueChange={mockOnValueChange} />);
|
46
|
+
expect(screen.getByRole('progressbar')).toBeInTheDocument();
|
47
|
+
});
|
48
|
+
|
49
|
+
it('should render combobox with batch numbers', async () => {
|
50
|
+
render(<BatchNoSelector stockItemUuid={mockStockItemUuid} onValueChange={mockOnValueChange} />);
|
51
|
+
expect(screen.getByRole('combobox')).toBeInTheDocument();
|
52
|
+
expect(screen.getByText('batchNo')).toBeInTheDocument();
|
53
|
+
});
|
54
|
+
|
55
|
+
it('should handle batch selection', async () => {
|
56
|
+
render(<BatchNoSelector stockItemUuid={mockStockItemUuid} onValueChange={mockOnValueChange} />);
|
57
|
+
const combobox = screen.getByRole('combobox');
|
58
|
+
await userEvent.click(combobox);
|
59
|
+
await userEvent.type(combobox, 'BATCH-001');
|
60
|
+
const option = screen.getByText(`BATCH-001 | Qty: 10 | Expiry: ${formatForDatePicker(mockExpiration)}`);
|
61
|
+
await userEvent.click(option);
|
62
|
+
expect(mockOnValueChange).toHaveBeenCalledWith('1');
|
63
|
+
});
|
64
|
+
|
65
|
+
it('should display error message when error prop is provided', () => {
|
66
|
+
const errorMessage = 'This is an error';
|
67
|
+
render(
|
68
|
+
<BatchNoSelector stockItemUuid={mockStockItemUuid} onValueChange={mockOnValueChange} error={errorMessage} />,
|
69
|
+
);
|
70
|
+
expect(screen.getByText(errorMessage)).toBeInTheDocument();
|
71
|
+
});
|
72
|
+
|
73
|
+
it('should filter out batches with zero or undefined quantity', async () => {
|
74
|
+
mockUseStockItemBatchNumbers.mockReturnValue({
|
75
|
+
isLoading: false,
|
76
|
+
stockItemBatchNos: [
|
77
|
+
{ uuid: '1', batchNo: 'BATCH-001', quantity: 10 },
|
78
|
+
{ uuid: '2', batchNo: 'BATCH-002', quantity: 0 },
|
79
|
+
{ uuid: '3', batchNo: 'BATCH-003', quantity: undefined },
|
80
|
+
],
|
81
|
+
});
|
82
|
+
|
83
|
+
render(<BatchNoSelector stockItemUuid={mockStockItemUuid} onValueChange={mockOnValueChange} />);
|
84
|
+
const combobox = screen.getByRole('combobox');
|
85
|
+
await userEvent.click(combobox);
|
86
|
+
|
87
|
+
expect(screen.queryByText('BATCH-002')).not.toBeInTheDocument();
|
88
|
+
expect(screen.queryByText('BATCH-003')).not.toBeInTheDocument();
|
89
|
+
});
|
90
|
+
});
|
@@ -1,6 +1,6 @@
|
|
1
1
|
@use '@carbon/colors';
|
2
2
|
@use '@carbon/type';
|
3
|
-
@use '@carbon/
|
3
|
+
@use '@carbon/layout';
|
4
4
|
@use '@openmrs/esm-styleguide/src/vars' as *;
|
5
5
|
|
6
6
|
// Patient List Table
|
@@ -25,7 +25,7 @@
|
|
25
25
|
.stockItemSearchContainer {
|
26
26
|
position: relative;
|
27
27
|
flex-direction: column;
|
28
|
-
margin-bottom:
|
28
|
+
margin-bottom: layout.$spacing-01;
|
29
29
|
}
|
30
30
|
|
31
31
|
.searchResults {
|