@openmrs/esm-stock-management-app 1.0.1-pre.777 → 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.
Files changed (120) hide show
  1. package/__mocks__/index.ts +1 -0
  2. package/__mocks__/operation-type.mock.ts +532 -0
  3. package/dist/155.js +1 -0
  4. package/dist/155.js.map +1 -0
  5. package/dist/172.js +1 -1
  6. package/dist/20.js +1 -1
  7. package/dist/290.js +1 -1
  8. package/dist/493.js +2 -0
  9. package/dist/493.js.map +1 -0
  10. package/dist/606.js +1 -1
  11. package/dist/627.js +1 -1
  12. package/dist/{400.js → 914.js} +1 -1
  13. package/dist/914.js.map +1 -0
  14. package/dist/main.js +1 -1
  15. package/dist/main.js.map +1 -1
  16. package/dist/openmrs-esm-stock-management-app.js +1 -1
  17. package/dist/openmrs-esm-stock-management-app.js.buildmanifest.json +81 -57
  18. package/dist/openmrs-esm-stock-management-app.js.map +1 -1
  19. package/dist/routes.json +1 -1
  20. package/package.json +1 -1
  21. package/src/core/utils/utils.ts +29 -0
  22. package/src/index.ts +4 -0
  23. package/src/routes.json +9 -0
  24. package/src/stock-items/add-stock-item/transactions/printout/transactions-stockcard-printout.component.tsx +8 -12
  25. package/src/stock-items/add-stock-item/transactions/transactions.component.tsx +8 -12
  26. package/src/stock-items/stock-items.resource.ts +5 -5
  27. package/src/stock-lookups/stock-lookups.resource.ts +2 -2
  28. package/src/stock-operations/edit-stock-operation/edit-stock-operation-action-menu.component.tsx +41 -16
  29. package/src/stock-operations/{add-stock-operation/received-items.component.tsx → received-items.component.tsx} +1 -1
  30. package/src/stock-operations/stock-operation-reference.component.tsx +64 -0
  31. package/src/stock-operations/stock-operation-status/stock-operation-status-row.tsx +77 -0
  32. package/src/stock-operations/stock-operation-status/stock-operation-status.scss +32 -0
  33. package/src/stock-operations/stock-operation-status/stock-operation-status.tsx +45 -0
  34. package/src/stock-operations/stock-operation-types-selector/stock-operation-types-selector.component.tsx +30 -29
  35. package/src/stock-operations/stock-operation.utils.tsx +16 -79
  36. package/src/stock-operations/stock-operations-dialog/stock-operations-issue-stock-button.component.tsx +27 -39
  37. package/src/stock-operations/stock-operations-dialog/stock-operations-print-button.component.tsx +51 -59
  38. package/src/stock-operations/{stock-item-selector/stock-item-selector.resource.tsx → stock-operations-forms/hooks/useFilterableStockItems.ts} +4 -4
  39. package/src/stock-operations/stock-operations-forms/hooks/useFilteredOperationTypesByRoles.ts +30 -0
  40. package/src/stock-operations/stock-operations-forms/hooks/useOperationTypePermisions.ts +29 -0
  41. package/src/stock-operations/stock-operations-forms/hooks/useParties.ts +73 -0
  42. package/src/stock-operations/{users-selector/users-selector.resource.tsx → stock-operations-forms/hooks/useSearchUser.ts} +9 -7
  43. package/src/stock-operations/{batch-no-selector/batch-no-selector.resource.tsx → stock-operations-forms/hooks/useStockItemBatchNumbers.ts} +3 -3
  44. package/src/stock-operations/stock-operations-forms/hooks/useStockOperationLinks.ts +20 -0
  45. package/src/stock-operations/stock-operations-forms/input-components/batch-no-selector.component.tsx +72 -0
  46. package/src/stock-operations/stock-operations-forms/input-components/batch-no-selector.test.tsx +90 -0
  47. 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
  48. package/src/stock-operations/stock-operations-forms/input-components/qty-uim-selector.test.tsx +157 -0
  49. package/src/stock-operations/stock-operations-forms/input-components/quantity-uom-selector.component.tsx +53 -0
  50. package/src/stock-operations/stock-operations-forms/input-components/stock-item-search.component.tsx +56 -0
  51. package/src/stock-operations/stock-operations-forms/input-components/stock-operation-reason-selector.component.tsx +59 -0
  52. package/src/stock-operations/stock-operations-forms/input-components/stock-operation-reason-selector.test.tsx +216 -0
  53. package/src/stock-operations/stock-operations-forms/input-components/unique-batch-no-entry-input.component.tsx +59 -0
  54. package/src/stock-operations/stock-operations-forms/input-components/user-selector.test.tsx +110 -0
  55. package/src/stock-operations/stock-operations-forms/input-components/users-selector.component.tsx +111 -0
  56. package/src/stock-operations/stock-operations-forms/step1.test.tsx +303 -0
  57. package/src/stock-operations/stock-operations-forms/step2.test.tsx +250 -0
  58. package/src/stock-operations/stock-operations-forms/step3.test.tsx +223 -0
  59. package/src/stock-operations/stock-operations-forms/steps/base-operation-details-form-step.tsx +241 -0
  60. package/src/stock-operations/stock-operations-forms/steps/quantity-uom-cell.component.tsx +33 -0
  61. package/src/stock-operations/stock-operations-forms/steps/stock-availability-cell.component.tsx +51 -0
  62. package/src/stock-operations/stock-operations-forms/steps/stock-operation-item-batch-no-cell.component.tsx +40 -0
  63. package/src/stock-operations/stock-operations-forms/steps/stock-operation-item-cell.component.tsx +38 -0
  64. package/src/stock-operations/stock-operations-forms/steps/stock-operation-item-expiry-cell.component.tsx +41 -0
  65. package/src/stock-operations/stock-operations-forms/steps/stock-operation-items-form-step.component.tsx +281 -0
  66. package/src/stock-operations/stock-operations-forms/steps/stock-operation-items-form-step.scc.scss +64 -0
  67. package/src/stock-operations/stock-operations-forms/steps/stock-operation-submission-form-step.component.tsx +236 -0
  68. package/src/stock-operations/stock-operations-forms/stock-issue-form-initializer-with-related-requisition-operation.component.tsx +55 -0
  69. package/src/stock-operations/stock-operations-forms/stock-item-form/stock-item-form.scss +41 -0
  70. package/src/stock-operations/stock-operations-forms/stock-item-form/stock-item-form.workspace.tsx +197 -0
  71. package/src/stock-operations/stock-operations-forms/stock-operation-form-header.component.tsx +166 -0
  72. package/src/stock-operations/stock-operations-forms/stock-operation-form.component.tsx +200 -0
  73. package/src/stock-operations/stock-operations-forms/stock-operation-form.scss +111 -0
  74. package/src/stock-operations/stock-operations-forms/stock-operation-related-link.component.tsx +45 -0
  75. package/src/stock-operations/stock-operations-forms/stock-operation-stepper/stepper.scss +41 -0
  76. package/src/stock-operations/stock-operations-forms/stock-operation-stepper/stock-operation-stepper.component.tsx +52 -0
  77. package/src/stock-operations/stock-operations-forms/stock-operations-form-utils.ts +32 -0
  78. package/src/stock-operations/stock-operations-table.component.tsx +20 -56
  79. package/src/stock-operations/stock-operations.resource.ts +16 -13
  80. package/src/stock-operations/validation-schema.ts +72 -14
  81. package/dist/400.js.map +0 -1
  82. package/dist/766.js +0 -2
  83. package/dist/766.js.map +0 -1
  84. package/src/stock-operations/add-stock-operation/add-stock-operation.component.tsx +0 -349
  85. package/src/stock-operations/add-stock-operation/add-stock-operation.resource.tsx +0 -27
  86. package/src/stock-operations/add-stock-operation/add-stock-operation.scss +0 -60
  87. package/src/stock-operations/add-stock-operation/add-stock-operation.test.tsx +0 -192
  88. package/src/stock-operations/add-stock-operation/add-stock-operation.utils.tsx +0 -152
  89. package/src/stock-operations/add-stock-operation/add-stock-utils.ts +0 -103
  90. package/src/stock-operations/add-stock-operation/base-operation-details.component.tsx +0 -439
  91. package/src/stock-operations/add-stock-operation/base-operation-details.scss +0 -30
  92. package/src/stock-operations/add-stock-operation/stock-item-search/stock-item-search.component.tsx +0 -70
  93. package/src/stock-operations/add-stock-operation/stock-items-addition-row.component.tsx +0 -360
  94. package/src/stock-operations/add-stock-operation/stock-items-addition-row.resource.tsx +0 -0
  95. package/src/stock-operations/add-stock-operation/stock-items-addition-row.scss +0 -12
  96. package/src/stock-operations/add-stock-operation/stock-items-addition-row.test.tsx +0 -10
  97. package/src/stock-operations/add-stock-operation/stock-items-addition.component.scss +0 -17
  98. package/src/stock-operations/add-stock-operation/stock-items-addition.component.tsx +0 -254
  99. package/src/stock-operations/add-stock-operation/stock-operation-context/useStockOperationContext.tsx +0 -16
  100. package/src/stock-operations/add-stock-operation/stock-operation-reference.component.tsx +0 -39
  101. package/src/stock-operations/add-stock-operation/stock-operation-related-link.component.tsx +0 -38
  102. package/src/stock-operations/add-stock-operation/stock-operation-status.component.tsx +0 -170
  103. package/src/stock-operations/add-stock-operation/stock-operation-submission.component.tsx +0 -189
  104. package/src/stock-operations/add-stock-operation/stock-operation-submission.test.tsx +0 -138
  105. package/src/stock-operations/add-stock-operation/types.ts +0 -55
  106. package/src/stock-operations/add-stock-operation/validationSchema.ts +0 -54
  107. package/src/stock-operations/batch-no-selector/batch-no-selector.component.tsx +0 -114
  108. package/src/stock-operations/batch-no-selector/batch-no-selector.scss +0 -0
  109. package/src/stock-operations/batch-no-selector/batch-no-selector.test.tsx +0 -101
  110. package/src/stock-operations/party-selector/party-selector.component.tsx +0 -59
  111. package/src/stock-operations/qty-uom-selector/qty-uom-selector.component.tsx +0 -65
  112. package/src/stock-operations/qty-uom-selector/qty-uom-selector.resource.tsx +0 -0
  113. package/src/stock-operations/qty-uom-selector/qty-uom-selector.scss +0 -0
  114. package/src/stock-operations/qty-uom-selector/qty-uom-selector.test.tsx +0 -10
  115. package/src/stock-operations/stock-item-selector/stock-item-selector.component.tsx +0 -69
  116. package/src/stock-operations/stock-item-selector/stock-item-selector.scss +0 -0
  117. package/src/stock-operations/stock-item-selector/stock-item-selector.test.tsx +0 -10
  118. package/src/stock-operations/stock-operation-reason-selector/stock-operation-reason-selector.component.tsx +0 -62
  119. package/src/stock-operations/users-selector/users-selector.component.tsx +0 -75
  120. /package/dist/{766.js.LICENSE.txt → 493.js.LICENSE.txt} +0 -0
@@ -1,152 +0,0 @@
1
- /* eslint-disable prefer-const */
2
- import { StockOperationDTO } from '../../core/api/types/stockOperation/StockOperationDTO';
3
- import { initialStockOperationValue } from '../../core/utils/utils';
4
- import { MAIN_STORE_LOCATION_TAG, today } from '../../constants';
5
- import {
6
- operationFromString,
7
- StockOperationType,
8
- StockOperationTypeCanCapturePurchasePrice,
9
- StockOperationTypeHasPrint,
10
- StockOperationTypeIsNegativeQtyAllowed,
11
- StockOperationTypeIsQuantityOptional,
12
- StockOperationTypeRequiresActualBatchInformation,
13
- StockOperationTypeRequiresBatchUuid,
14
- StockOperationTypeRequiresDispatchAcknowledgement,
15
- StockOperationTypeRequiresStockAdjustmentReason,
16
- } from '../../core/api/types/stockOperation/StockOperationType';
17
- import { StockOperationItemDTO } from '../../core/api/types/stockOperation/StockOperationItemDTO';
18
- import { InitializeResult } from './types';
19
- import { LocationTypeLocation, LocationTypeOther } from '../../core/api/types/stockOperation/LocationType';
20
- import { getParties, getStockOperationTypes } from '../../stock-lookups/stock-lookups.resource';
21
- import { Party } from '../../core/api/types/Party';
22
-
23
- export async function initializeNewStockOperation(
24
- currentStockOperationType: StockOperationType,
25
- stockOperation?: StockOperationDTO,
26
- stockOperationTypes?: StockOperationType[],
27
- ): Promise<InitializeResult> {
28
- let model: StockOperationDTO;
29
- const isNew = !!stockOperation;
30
- const newItemsToCopy: StockOperationItemDTO[] = [];
31
- const showQuantityRequested = false;
32
-
33
- let operationTypes = stockOperationTypes;
34
- const canIssueStock = stockOperation?.permission?.isRequisitionAndCanIssueStock ?? false;
35
- const sourceTags =
36
- currentStockOperationType?.stockOperationTypeLocationScopes
37
- ?.filter((p) => currentStockOperationType?.hasSource && p.isSource)
38
- .map((p) => p.locationTag) ?? [];
39
- const shouldLockSource = sourceTags.length === 1 && sourceTags[0] === MAIN_STORE_LOCATION_TAG;
40
-
41
- const destinationTags =
42
- currentStockOperationType?.stockOperationTypeLocationScopes
43
- ?.filter((p) => currentStockOperationType?.hasDestination && p.isDestination)
44
- .map((p) => p.locationTag) ?? [];
45
- const shouldLockDestination = destinationTags.length === 1 && destinationTags[0] === MAIN_STORE_LOCATION_TAG;
46
- let location: string | null | undefined = null;
47
- let sourcePartyList: Party[] | null | undefined;
48
- let destinationPartyList: Party[] | null | undefined;
49
-
50
- const partyList = await getParties();
51
- if (!partyList.ok) throw Error('Error loading parties');
52
- sourcePartyList = partyList?.data?.results?.filter(
53
- (p) =>
54
- (p.locationUuid &&
55
- currentStockOperationType?.sourceType === LocationTypeLocation &&
56
- (sourceTags.length === 0 || (p.tags && sourceTags.some((x) => p.tags.includes(x))))) ||
57
- (p.stockSourceUuid && currentStockOperationType?.sourceType === LocationTypeOther),
58
- );
59
- destinationPartyList = partyList?.data?.results?.filter(
60
- (p) =>
61
- (p.locationUuid &&
62
- currentStockOperationType?.destinationType === LocationTypeLocation &&
63
- (destinationTags.length === 0 || (p.tags && destinationTags.some((x) => p.tags.includes(x))))) ||
64
- (p.stockSourceUuid && currentStockOperationType?.destinationType === LocationTypeOther),
65
- );
66
-
67
- if (isNew) {
68
- model = structuredClone(initialStockOperationValue());
69
- model = Object.assign(model, {
70
- operationDate: today(),
71
- operationTypeName: currentStockOperationType?.name,
72
- operationTypeUuid: currentStockOperationType?.uuid,
73
- operationType: currentStockOperationType?.operationType,
74
- });
75
- if (currentStockOperationType?.hasSource) {
76
- if (isNew && shouldLockSource && sourcePartyList?.length > 0) {
77
- const party = sourcePartyList[0];
78
- model.sourceUuid = party.uuid;
79
- model.sourceName = party.name;
80
- location = party?.locationUuid;
81
- }
82
- }
83
-
84
- if (currentStockOperationType?.hasDestination) {
85
- if (isNew && shouldLockDestination && destinationPartyList?.length > 0) {
86
- const party = destinationPartyList[0];
87
- model.destinationUuid = party.uuid;
88
- model.destinationName = party.name;
89
- }
90
- }
91
- } else {
92
- model = stockOperation!;
93
- const response = await getStockOperationTypes();
94
- if (response.ok) {
95
- operationTypes = response.data.results;
96
- } else {
97
- throw Error('Error loading operation types');
98
- }
99
- }
100
-
101
- const opType = operationFromString(currentStockOperationType.operationType);
102
- return {
103
- batchBalance: {},
104
- batchNos: {},
105
- itemUoM: {},
106
- requisition: '',
107
- showQuantityRequested: false,
108
- dto: model,
109
- stockItems: newItemsToCopy,
110
- isNegativeQuantityAllowed: StockOperationTypeIsNegativeQtyAllowed(opType),
111
- requiresBatchUuid: StockOperationTypeRequiresBatchUuid(opType),
112
- requiresActualBatchInfo: StockOperationTypeRequiresActualBatchInformation(opType),
113
- isQuantityOptional: StockOperationTypeIsQuantityOptional(opType),
114
- canCaptureQuantityPrice: StockOperationTypeCanCapturePurchasePrice(opType),
115
- requiresStockAdjustmentReason: StockOperationTypeRequiresStockAdjustmentReason(opType),
116
- requiresDispatchAcknowledgement: StockOperationTypeRequiresDispatchAcknowledgement(opType),
117
- allowExpiredBatchNumbers: currentStockOperationType?.allowExpiredBatchNumbers ?? false,
118
- canEditModel: stockOperation?.permission?.canEdit ?? false,
119
- canViewModel: stockOperation?.permission?.canView ?? false,
120
- canApproveModel: stockOperation?.permission?.canApprove ?? false,
121
- canIssueStock,
122
- canReceiveItems: stockOperation?.permission?.canReceiveItems ?? false,
123
- canDisplayReceivedItems: stockOperation?.permission?.canDisplayReceivedItems ?? false,
124
- canUpdateItemsBatchInformation: stockOperation?.permission?.canUpdateBatchInformation ?? false,
125
- canPrint: canIssueStock || StockOperationTypeHasPrint(opType),
126
- sourceTags,
127
- destinationTags,
128
- shouldLockDestination,
129
- shouldLockSource,
130
- sourcePartyListFilter: (p) => {
131
- return (
132
- (p.locationUuid &&
133
- currentStockOperationType?.sourceType === LocationTypeLocation &&
134
- (sourceTags.length === 0 || (p.tags && sourceTags.some((x) => p.tags.includes(x))))) ||
135
- (p.stockSourceUuid && currentStockOperationType?.sourceType === LocationTypeOther)
136
- );
137
- },
138
- destinationPartyListFilter: (p) => {
139
- return (
140
- (p.locationUuid &&
141
- currentStockOperationType?.destinationType === LocationTypeLocation &&
142
- (destinationTags.length === 0 || (p.tags && destinationTags.some((x) => p.tags.includes(x))))) ||
143
- (p.stockSourceUuid && currentStockOperationType?.destinationType === LocationTypeOther)
144
- );
145
- },
146
- location,
147
- sourcePartyList,
148
- destinationPartyList,
149
- stockOperationTypes: operationTypes,
150
- hasQtyRequested: showQuantityRequested,
151
- };
152
- }
@@ -1,103 +0,0 @@
1
- import { StockOperationDTO } from '../../core/api/types/stockOperation/StockOperationDTO';
2
- import { OperationType } from '../../core/api/types/stockOperation/StockOperationType';
3
-
4
- const OPERATION_TYPES_FOR_DESTINATION_NAME_DELETION = [
5
- OperationType.ADJUSTMENT_OPERATION_TYPE,
6
- OperationType.RECEIPT_OPERATION_TYPE,
7
- OperationType.STOCK_ISSUE_OPERATION_TYPE,
8
- OperationType.STOCK_TAKE_OPERATION_TYPE,
9
- OperationType.RETURN_OPERATION_TYPE,
10
- OperationType.DISPOSED_OPERATION_TYPE,
11
- OperationType.OPENING_STOCK_OPERATION_TYPE,
12
- OperationType.TRANSFER_OUT_OPERATION_TYPE,
13
- ];
14
-
15
- const OPERATION_TYPES_FOR_DESTINATION_UUID_DELETION = [
16
- OperationType.ADJUSTMENT_OPERATION_TYPE,
17
- OperationType.DISPOSED_OPERATION_TYPE,
18
- OperationType.STOCK_TAKE_OPERATION_TYPE,
19
- OperationType.OPENING_STOCK_OPERATION_TYPE,
20
- ];
21
-
22
- export function getRequisitionStockOperations(items: Array<StockOperationDTO> = []) {
23
- // Extract stock issued requisition UUIDs
24
- const stockIssuedRequisitionUuids =
25
- items
26
- ?.filter((item) => item.operationType === OperationType.STOCK_ISSUE_OPERATION_TYPE)
27
- .map((item) => item.requisitionStockOperationUuid) ?? [];
28
-
29
- // Filter requisition stock operations
30
- const requisitionStockOperations =
31
- items?.filter(
32
- (item) =>
33
- item.operationType === OperationType.REQUISITION_OPERATION_TYPE &&
34
- !stockIssuedRequisitionUuids.includes(item.uuid),
35
- ) ?? [];
36
-
37
- return requisitionStockOperations;
38
- }
39
-
40
- function deleteProperties(req, properties) {
41
- properties.forEach((prop) => {
42
- delete req[prop];
43
- });
44
- }
45
-
46
- function shouldDeleteDestinationName(operationType) {
47
- return OPERATION_TYPES_FOR_DESTINATION_NAME_DELETION.includes(operationType);
48
- }
49
-
50
- function shouldDeleteDestinationUuid(operationType) {
51
- return OPERATION_TYPES_FOR_DESTINATION_UUID_DELETION.includes(operationType);
52
- }
53
-
54
- export function createBaseOperationPayload(model, item, operationType) {
55
- const req = Object.assign(model, item);
56
- const propertiesToDelete = [
57
- 'submitted',
58
- 'cancelledByFamilyName',
59
- 'atLocationName',
60
- 'completedByGivenName',
61
- 'cancelledBy',
62
- 'submittedByFamilyName',
63
- 'operationOrder',
64
- 'dispatchedByGivenName',
65
- 'submittedByGivenName',
66
- 'returnedByGivenName',
67
- 'operationNumber',
68
- 'responsiblePersonFamilyName',
69
- 'returnReason',
70
- 'atLocationUuid',
71
- 'cancelReason',
72
- 'rejectedByGivenName',
73
- 'reasonName',
74
- 'submittedBy',
75
- 'creator',
76
- 'completedByFamilyName',
77
- 'operationTypeName',
78
- 'rejectedByFamilyName',
79
- 'responsiblePerson',
80
- 'creatorFamilyName',
81
- 'returnedByFamilyName',
82
- 'cancelledByGivenName',
83
- 'operationType',
84
- 'responsiblePersonGivenName',
85
- 'sourceName',
86
- 'rejectionReason',
87
- 'completedBy',
88
- 'creatorGivenName',
89
- 'dispatchedByFamilyName',
90
- 'uuid',
91
- ];
92
-
93
- deleteProperties(req, propertiesToDelete);
94
-
95
- if (shouldDeleteDestinationName(operationType)) {
96
- delete req.destinationName;
97
- }
98
-
99
- if (shouldDeleteDestinationUuid(operationType)) {
100
- delete req.destinationUuid;
101
- }
102
- return req;
103
- }
@@ -1,439 +0,0 @@
1
- import React, { useState, useEffect } from 'react';
2
- import { useTranslation } from 'react-i18next';
3
- import { StockOperationDTO } from '../../core/api/types/stockOperation/StockOperationDTO';
4
- import { SaveStockOperation } from '../../stock-items/types';
5
- import { operationFromString, StockOperationType } from '../../core/api/types/stockOperation/StockOperationType';
6
- import { DATE_PICKER_CONTROL_FORMAT, DATE_PICKER_FORMAT, formatForDatePicker, today } from '../../constants';
7
- import { Button, DatePicker, DatePickerInput, InlineLoading, TextInput } from '@carbon/react';
8
- import { Controller, useForm } from 'react-hook-form';
9
- import { zodResolver } from '@hookform/resolvers/zod';
10
- import { operationSchema, StockOperationFormData } from '../validation-schema';
11
- import { ArrowRight } from '@carbon/react/icons';
12
- import PartySelector from '../party-selector/party-selector.component';
13
- import UsersSelector from '../users-selector/users-selector.component';
14
- import { otherUser } from '../../core/utils/utils';
15
- import ControlledTextInput from '../../core/components/carbon/controlled-text-input/controlled-text-input.component';
16
- import StockOperationReasonSelector from '../stock-operation-reason-selector/stock-operation-reason-selector.component';
17
- import ControlledTextArea from '../../core/components/carbon/controlled-text-area/controlled-text-area.component';
18
- import { InitializeResult } from './types';
19
- import { ResourceRepresentation } from '../../core/api/api';
20
- import { useStockOperationPages } from '../stock-operations-table.resource';
21
- import { createBaseOperationPayload } from './add-stock-utils';
22
- import { showSnackbar, useSession } from '@openmrs/esm-framework';
23
-
24
- import { Party } from '../../core/api/types/Party';
25
- import styles from '../add-stock-operation/base-operation-details.scss';
26
-
27
- interface BaseOperationDetailsProps {
28
- isEditing?: boolean;
29
- canEdit?: boolean;
30
- model?: StockOperationDTO;
31
- onSave?: SaveStockOperation;
32
- operation: StockOperationType;
33
- setup: InitializeResult;
34
- }
35
-
36
- const BaseOperationDetails: React.FC<BaseOperationDetailsProps> = ({
37
- model,
38
- onSave,
39
- operation,
40
- canEdit,
41
- isEditing,
42
- setup: { requiresStockAdjustmentReason: showReason, sourcePartyList, destinationPartyList },
43
- }) => {
44
- const { t } = useTranslation();
45
- const { isLoading } = useStockOperationPages({
46
- v: ResourceRepresentation.Full,
47
- totalCount: true,
48
- });
49
- const operationType = operationFromString(operation?.operationType);
50
- const issueStockOperation = mapIssueStockLocations(model);
51
- const { user } = useSession();
52
- const defaultLoggedUserUuid = user.uuid;
53
-
54
- const {
55
- handleSubmit,
56
- control,
57
- formState: { errors },
58
- setValue,
59
- } = useForm<StockOperationFormData>({
60
- defaultValues: operationType === 'stockissue' ? issueStockOperation : model,
61
- mode: 'all',
62
- resolver: zodResolver(operationSchema(operationType)),
63
- });
64
-
65
- const [isOtherUser, setIsOtherUser] = useState<boolean | null>();
66
- const [isSaving, setIsSaving] = useState(false);
67
- useEffect(() => {
68
- if (defaultLoggedUserUuid) {
69
- setValue('responsiblePersonUuid', defaultLoggedUserUuid);
70
- }
71
- }, [defaultLoggedUserUuid, setValue]);
72
-
73
- if (isLoading) {
74
- return (
75
- <InlineLoading status="active" iconDescription="Loading" description={t('loadingData', 'Loading data...')} />
76
- );
77
- }
78
-
79
- const handleSave = async (item: StockOperationDTO) => {
80
- try {
81
- setIsSaving(true);
82
- const payload = createBaseOperationPayload(model, item, operationType);
83
- await onSave(payload);
84
- } catch (e) {
85
- showSnackbar({
86
- title: t('errorSavingBaseOperation', 'Error saving base operation'),
87
- isLowContrast: true,
88
- kind: 'error',
89
- });
90
- } finally {
91
- setIsSaving(false);
92
- }
93
- };
94
- const sourceTags =
95
- operation?.stockOperationTypeLocationScopes
96
- ?.filter((p) => operation?.hasSource && p.isSource)
97
- .map((p) => p.locationTag) ?? [];
98
-
99
- const destinationTags =
100
- operation?.stockOperationTypeLocationScopes
101
- ?.filter((p) => operation?.hasDestination && p.isDestination)
102
- .map((p) => p.locationTag) ?? [];
103
-
104
- const sourcePartyListFilter = (sourcePartyList: Party) => {
105
- const isValid =
106
- (sourcePartyList.locationUuid &&
107
- operation?.sourceType === 'Location' &&
108
- (sourceTags.length === 0 ||
109
- (sourcePartyList.tags && sourceTags.some((x) => sourcePartyList.tags.includes(x))))) ||
110
- (sourcePartyList.stockSourceUuid && operation?.sourceType === 'Other');
111
- return isValid;
112
- };
113
-
114
- const destinationPartyListFilter = (destinationPartyList: Party) => {
115
- const isValid =
116
- (destinationPartyList.locationUuid &&
117
- operation?.destinationType === 'Location' &&
118
- (destinationTags.length === 0 ||
119
- (destinationPartyList.tags && destinationTags.some((x) => destinationPartyList.tags.includes(x))))) ||
120
- (destinationPartyList.stockSourceUuid && operation?.destinationType === 'Other');
121
- return isValid;
122
- };
123
- return (
124
- <div style={{ margin: '10px' }}>
125
- <form className={`${styles.formContainer} ${styles.verticalForm}`}>
126
- {!canEdit || operationType === 'stockissue' ? (
127
- <>
128
- {model?.operationDate && (
129
- <TextInput
130
- id="operationDateLbl"
131
- value={formatForDatePicker(model.operationDate)}
132
- readOnly={true}
133
- labelText={t('operationDate', 'Operation Date')}
134
- />
135
- )}
136
- {model?.operationNumber && (
137
- <TextInput
138
- id="operationNoLbl"
139
- value={model?.operationNumber}
140
- readOnly={true}
141
- labelText={t('operationNumber', 'Operation Number')}
142
- />
143
- )}
144
- {model?.atLocationName && (
145
- <TextInput
146
- id="sourceLbl"
147
- value={operationType === 'stockissue' ? issueStockOperation.sourceName : model?.sourceName ?? ''}
148
- readOnly={true}
149
- labelText={t('source', 'Source')}
150
- />
151
- )}
152
- {model?.destinationName && (
153
- <TextInput
154
- id="destinationLbl"
155
- value={
156
- operationType === 'stockissue' ? issueStockOperation.destinationName : model?.destinationName ?? ''
157
- }
158
- readOnly={true}
159
- labelText={t('destination', 'Destination')}
160
- />
161
- )}
162
- {model?.responsiblePersonGivenName && model?.responsiblePersonFamilyName && (
163
- <TextInput
164
- id="responsiblePersonLbl"
165
- value={`${model.responsiblePersonGivenName} ${model.responsiblePersonFamilyName}`}
166
- readOnly={true}
167
- labelText={t('responsiblePerson', 'Responsible Person')}
168
- />
169
- )}
170
- {showReason && model?.reasonName && (
171
- <TextInput id="reasonLbl" value={model.reasonName} readOnly={true} labelText={t('reason', 'Reason')} />
172
- )}
173
- {model?.remarks && (
174
- <TextInput id="remarksLbl" value={model.remarks} readOnly={true} labelText={t('remarks', 'Remarks')} />
175
- )}
176
- {operationType === 'stockissue' && (
177
- <div style={{ display: 'flex', flexDirection: 'row-reverse' }}>
178
- <Button
179
- name="save"
180
- type="button"
181
- className="submitButton"
182
- onClick={handleSubmit(handleSave)}
183
- kind="primary"
184
- renderIcon={ArrowRight}
185
- >
186
- {isSaving ? <InlineLoading /> : t('next', 'Next')}
187
- </Button>
188
- </div>
189
- )}
190
- </>
191
- ) : (
192
- <>
193
- {canEdit && (
194
- <Controller
195
- control={control}
196
- render={({ field: { onChange } }) => (
197
- <DatePicker
198
- datePickerType="single"
199
- maxDate={formatForDatePicker(today())}
200
- locale="en"
201
- dateFormat={DATE_PICKER_CONTROL_FORMAT}
202
- onChange={([newDate]) => {
203
- onChange(newDate);
204
- }}
205
- >
206
- <DatePickerInput
207
- invalid={!!errors.operationDate}
208
- invalidText={errors?.operationDate?.message}
209
- id="operationDate"
210
- name="operationDate"
211
- placeholder={DATE_PICKER_FORMAT}
212
- labelText={t('operationDate', 'Operation Date')}
213
- defaultValue={formatForDatePicker(model?.operationDate)}
214
- />
215
- </DatePicker>
216
- )}
217
- name="operationDate"
218
- />
219
- )}
220
-
221
- {!canEdit && (
222
- <>
223
- <TextInput
224
- id="operationDateLbl"
225
- value={formatForDatePicker(model?.operationDate)}
226
- readOnly={true}
227
- labelText="Operation Date"
228
- />
229
- </>
230
- )}
231
-
232
- {isEditing && model?.operationNumber && (
233
- <TextInput
234
- id="operationNoLbl"
235
- value={model?.operationNumber}
236
- readOnly={true}
237
- labelText={'Operation Number'}
238
- />
239
- )}
240
-
241
- {canEdit && (operation?.hasSource || model?.atLocationUuid) && (
242
- <PartySelector
243
- controllerName="sourceUuid"
244
- name="sourceUuid"
245
- control={control}
246
- partyUuid={model?.atLocationUuid}
247
- title={
248
- operation?.hasDestination || model?.destinationUuid ? t('from', 'From') : t('location', 'Location')
249
- }
250
- placeholder={
251
- operation.hasDestination || model?.destinationUuid
252
- ? t('chooseASource', 'Choose a source')
253
- : t('chooseALocation', 'Choose a location')
254
- }
255
- invalid={!!errors.sourceUuid}
256
- invalidText={errors.sourceUuid && errors?.sourceUuid?.message}
257
- parties={sourcePartyList?.filter(sourcePartyListFilter) || []}
258
- filterFunction={sourcePartyListFilter}
259
- />
260
- )}
261
-
262
- {!canEdit && isEditing && (
263
- <PartySelector
264
- controllerName="sourceUuid"
265
- name="sourceUuid"
266
- control={control}
267
- partyUuid={model?.atLocationUuid}
268
- title={operation?.hasDestination || model?.destinationUuid ? 'From' : 'Location'}
269
- placeholder={
270
- operation.hasDestination || model?.destinationUuid
271
- ? t('chooseASource', 'Choose a source')
272
- : t('chooseALocation', 'Choose a location')
273
- }
274
- invalid={!!errors.sourceUuid}
275
- invalidText={errors.sourceUuid && errors?.sourceUuid?.message}
276
- parties={sourcePartyList?.filter(sourcePartyListFilter) || []}
277
- filterFunction={sourcePartyListFilter}
278
- />
279
- )}
280
- {canEdit && (operation?.hasDestination || model?.destinationUuid) && (
281
- <PartySelector
282
- controllerName="destinationUuid"
283
- name="destinationUuid"
284
- control={control}
285
- partyUuid={model?.destinationUuid}
286
- title={operation?.hasSource || model?.atLocationUuid ? t('to', 'To') : t('location', 'Location')}
287
- placeholder={
288
- operation?.hasSource || model?.atLocationUuid
289
- ? t('chooseADestination', 'Choose a destination')
290
- : 'Location'
291
- }
292
- invalid={!!errors.destinationUuid}
293
- invalidText={errors.destinationUuid && errors?.destinationUuid?.message}
294
- parties={destinationPartyList?.filter(destinationPartyListFilter) || []}
295
- filterFunction={destinationPartyListFilter}
296
- />
297
- )}
298
-
299
- {!canEdit && isEditing && (
300
- <PartySelector
301
- controllerName="destinationUuid"
302
- name="destinationUuid"
303
- control={control}
304
- partyUuid={model?.destinationUuid}
305
- title={operation?.hasSource || model?.atLocationUuid ? t('to', 'To') : t('location', 'Location')}
306
- placeholder={
307
- operation?.hasSource || model?.atLocationUuid
308
- ? t('chooseADestination', 'Choose a destination')
309
- : 'Location'
310
- }
311
- invalid={!!errors.destinationUuid}
312
- invalidText={errors.destinationUuid && errors?.destinationUuid?.message}
313
- parties={destinationPartyList?.filter(destinationPartyListFilter) || []}
314
- filterFunction={destinationPartyListFilter}
315
- />
316
- )}
317
-
318
- {canEdit && (
319
- <UsersSelector
320
- controllerName="responsiblePersonUuid"
321
- name="responsiblePersonUuid"
322
- control={control}
323
- userUuid={model?.responsiblePersonUuid}
324
- title={t('responsiblePerson', 'Responsible Person')}
325
- placeholder={t('filter', 'Filter ...')}
326
- invalid={!!errors.responsiblePersonUuid}
327
- invalidText={errors.responsiblePersonUuid && errors?.responsiblePersonUuid?.message}
328
- onUserChanged={(user) => {
329
- if (user?.uuid === otherUser.uuid) {
330
- setIsOtherUser(true);
331
- } else {
332
- setIsOtherUser(false);
333
- }
334
- }}
335
- />
336
- )}
337
-
338
- {isOtherUser && (
339
- <ControlledTextInput
340
- id="responsiblePersonOther"
341
- name="responsiblePersonOther"
342
- control={control}
343
- controllerName="responsiblePersonOther"
344
- maxLength={255}
345
- size={'md'}
346
- value={`${model?.responsiblePersonOther ?? ''}`}
347
- labelText={t('responsiblePerson', 'Responsible Person')}
348
- placeholder={t('pleaseSpecify', 'Please Specify')}
349
- invalid={!!errors.responsiblePersonOther}
350
- invalidText={errors.responsiblePersonOther && errors?.responsiblePersonOther?.message}
351
- />
352
- )}
353
-
354
- {!canEdit && isEditing && (
355
- <UsersSelector
356
- controllerName="responsiblePersonUuid"
357
- name="responsiblePersonUuid"
358
- control={control}
359
- userUuid={model?.responsiblePersonUuid}
360
- title={t('responsiblePerson', 'Responsible Person')}
361
- placeholder={t('filter', 'Filter ...')}
362
- invalid={!!errors.responsiblePersonUuid}
363
- invalidText={errors.responsiblePersonUuid && errors?.responsiblePersonUuid?.message}
364
- onUserChanged={(user) => {
365
- if (user?.uuid === otherUser.uuid) {
366
- setIsOtherUser(true);
367
- } else {
368
- setIsOtherUser(false);
369
- }
370
- }}
371
- />
372
- )}
373
-
374
- {showReason && canEdit && (
375
- <StockOperationReasonSelector
376
- controllerName="reasonUuid"
377
- name="reasonUuid"
378
- control={control}
379
- reasonUuid={model?.reasonUuid}
380
- placeholder={t('chooseAReason', 'Choose a reason')}
381
- title={t('reason', 'Reason')}
382
- invalid={!!errors.reasonUuid}
383
- invalidText={errors.reasonUuid && errors?.reasonUuid?.message}
384
- onReasonChange={(reason) => {
385
- setValue('reasonUuid', reason.uuid);
386
- }}
387
- />
388
- )}
389
-
390
- {showReason && !canEdit && (
391
- <TextInput id="reasonUuidLbl" value={model?.reasonName ?? ''} readOnly={true} labelText={'Reason:'} />
392
- )}
393
-
394
- <ControlledTextArea
395
- id="remarks"
396
- name="remarks"
397
- control={control}
398
- controllerName="remarks"
399
- maxLength={255}
400
- value={`${model?.remarks ?? ''}`}
401
- labelText={t('remarks', 'Remarks')}
402
- invalid={!!errors.remarks}
403
- invalidText={errors.remarks && errors?.remarks?.message}
404
- />
405
-
406
- <div style={{ display: 'flex', flexDirection: 'row-reverse' }}>
407
- <Button
408
- name="save"
409
- type="button"
410
- className="submitButton"
411
- onClick={handleSubmit(handleSave)}
412
- kind="primary"
413
- renderIcon={ArrowRight}
414
- >
415
- {isSaving ? <InlineLoading /> : t('next', 'Next')}
416
- </Button>
417
- </div>
418
- </>
419
- )}
420
- </form>
421
- </div>
422
- );
423
- };
424
-
425
- function mapIssueStockLocations(stockOperation) {
426
- /** Since we are using requisition information to issue stock,
427
- please note that the locations will be inverted: the destination listed on the requisition will become the issuing location.
428
- */
429
- const { sourceUuid, sourceName, destinationUuid, destinationName } = stockOperation;
430
- return {
431
- ...stockOperation,
432
- sourceUuid: destinationUuid,
433
- sourceName: destinationName,
434
- destinationUuid: sourceUuid,
435
- destinationName: sourceName,
436
- };
437
- }
438
-
439
- export default BaseOperationDetails;