@openmrs/esm-stock-management-app 1.0.1-pre.783 → 1.0.1-pre.788

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 (128) 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/922.js +1 -0
  13. package/dist/922.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 +75 -51
  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/config-schema.ts +6 -0
  22. package/src/core/utils/utils.ts +29 -0
  23. package/src/index.ts +4 -0
  24. package/src/routes.json +9 -0
  25. package/src/stock-items/add-stock-item/transactions/printout/transactions-stockcard-printout.component.tsx +8 -12
  26. package/src/stock-items/add-stock-item/transactions/transactions.component.tsx +8 -12
  27. package/src/stock-items/stock-items.resource.ts +5 -5
  28. package/src/stock-lookups/stock-lookups.resource.ts +2 -2
  29. package/src/stock-operations/add-stock-operation/stock-operations-expanded-row/stock-items-table.scss +34 -0
  30. package/src/stock-operations/add-stock-operation/stock-operations-expanded-row/stock-items-table.tsx +111 -0
  31. package/src/stock-operations/add-stock-operation/stock-operations-expanded-row/stock-operation-expanded-row.component.tsx +87 -0
  32. package/src/stock-operations/add-stock-operation/stock-operations-expanded-row/stock-operation-expanded-row.scss +31 -0
  33. package/src/stock-operations/add-stock-operation/stock-operations-expanded-row/stock-operations-status.tsx +45 -0
  34. package/src/stock-operations/edit-stock-operation/edit-stock-operation-action-menu.component.tsx +41 -16
  35. package/src/stock-operations/stock-operation-reference.component.tsx +64 -0
  36. package/src/stock-operations/stock-operation-status/stock-operation-status-row.tsx +77 -0
  37. package/src/stock-operations/stock-operation-status/stock-operation-status.scss +32 -0
  38. package/src/stock-operations/stock-operation-status/stock-operation-status.tsx +45 -0
  39. package/src/stock-operations/stock-operation-types-selector/stock-operation-types-selector.component.tsx +30 -29
  40. package/src/stock-operations/stock-operation.utils.tsx +16 -79
  41. package/src/stock-operations/stock-operations-dialog/stock-operations-issue-stock-button.component.tsx +27 -39
  42. package/src/stock-operations/stock-operations-dialog/stock-operations-print-button.component.tsx +51 -59
  43. package/src/stock-operations/{stock-item-selector/stock-item-selector.resource.tsx → stock-operations-forms/hooks/useFilterableStockItems.ts} +4 -4
  44. package/src/stock-operations/stock-operations-forms/hooks/useFilteredOperationTypesByRoles.ts +30 -0
  45. package/src/stock-operations/stock-operations-forms/hooks/useOperationTypePermisions.ts +29 -0
  46. package/src/stock-operations/stock-operations-forms/hooks/useParties.ts +73 -0
  47. package/src/stock-operations/{users-selector/users-selector.resource.tsx → stock-operations-forms/hooks/useSearchUser.ts} +9 -7
  48. package/src/stock-operations/{batch-no-selector/batch-no-selector.resource.tsx → stock-operations-forms/hooks/useStockItemBatchNumbers.ts} +3 -3
  49. package/src/stock-operations/stock-operations-forms/hooks/useStockOperationLinks.ts +20 -0
  50. package/src/stock-operations/stock-operations-forms/input-components/batch-no-selector.component.tsx +72 -0
  51. package/src/stock-operations/stock-operations-forms/input-components/batch-no-selector.test.tsx +90 -0
  52. 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
  53. package/src/stock-operations/stock-operations-forms/input-components/qty-uim-selector.test.tsx +157 -0
  54. package/src/stock-operations/stock-operations-forms/input-components/quantity-uom-selector.component.tsx +53 -0
  55. package/src/stock-operations/stock-operations-forms/input-components/stock-item-search.component.tsx +79 -0
  56. package/src/stock-operations/stock-operations-forms/input-components/stock-operation-reason-selector.component.tsx +59 -0
  57. package/src/stock-operations/stock-operations-forms/input-components/stock-operation-reason-selector.test.tsx +216 -0
  58. package/src/stock-operations/{batch-no-selector → stock-operations-forms/input-components}/unique-batch-no-entry-input.component.tsx +12 -7
  59. package/src/stock-operations/stock-operations-forms/input-components/user-selector.test.tsx +110 -0
  60. package/src/stock-operations/stock-operations-forms/input-components/users-selector.component.tsx +111 -0
  61. package/src/stock-operations/stock-operations-forms/step1.test.tsx +303 -0
  62. package/src/stock-operations/stock-operations-forms/step2.test.tsx +254 -0
  63. package/src/stock-operations/stock-operations-forms/step3.test.tsx +223 -0
  64. package/src/stock-operations/stock-operations-forms/steps/base-operation-details-form-step.tsx +241 -0
  65. package/src/stock-operations/stock-operations-forms/steps/quantity-uom-cell.component.tsx +33 -0
  66. package/src/stock-operations/stock-operations-forms/steps/received-items.component.tsx +110 -0
  67. package/src/stock-operations/stock-operations-forms/steps/stock-availability-cell.component.tsx +51 -0
  68. package/src/stock-operations/stock-operations-forms/steps/stock-operation-item-batch-no-cell.component.tsx +40 -0
  69. package/src/stock-operations/stock-operations-forms/steps/stock-operation-item-cell.component.tsx +50 -0
  70. package/src/stock-operations/stock-operations-forms/steps/stock-operation-item-expiry-cell.component.tsx +41 -0
  71. package/src/stock-operations/stock-operations-forms/steps/stock-operation-items-form-step.component.tsx +281 -0
  72. package/src/stock-operations/stock-operations-forms/steps/stock-operation-items-form-step.scc.scss +64 -0
  73. package/src/stock-operations/stock-operations-forms/steps/stock-operation-submission-form-step.component.tsx +243 -0
  74. package/src/stock-operations/stock-operations-forms/stock-issue-form-initializer-with-related-requisition-operation.component.tsx +55 -0
  75. package/src/stock-operations/stock-operations-forms/stock-item-form/stock-item-form.scss +41 -0
  76. package/src/stock-operations/stock-operations-forms/stock-item-form/stock-item-form.workspace.tsx +211 -0
  77. package/src/stock-operations/stock-operations-forms/stock-operation-form-header.component.tsx +166 -0
  78. package/src/stock-operations/stock-operations-forms/stock-operation-form.component.tsx +205 -0
  79. package/src/stock-operations/stock-operations-forms/stock-operation-form.scss +111 -0
  80. package/src/stock-operations/stock-operations-forms/stock-operation-related-link.component.tsx +45 -0
  81. package/src/stock-operations/stock-operations-forms/stock-operation-stepper/stepper.scss +41 -0
  82. package/src/stock-operations/stock-operations-forms/stock-operation-stepper/stock-operation-stepper.component.tsx +52 -0
  83. package/src/stock-operations/stock-operations-forms/stock-operations-form-utils.ts +32 -0
  84. package/src/stock-operations/stock-operations-table.component.tsx +57 -92
  85. package/src/stock-operations/stock-operations.resource.ts +16 -13
  86. package/src/stock-operations/validation-schema.ts +72 -14
  87. package/dist/766.js +0 -2
  88. package/dist/766.js.map +0 -1
  89. package/dist/822.js +0 -1
  90. package/dist/822.js.map +0 -1
  91. package/src/stock-operations/add-stock-operation/add-stock-operation.component.tsx +0 -349
  92. package/src/stock-operations/add-stock-operation/add-stock-operation.resource.tsx +0 -27
  93. package/src/stock-operations/add-stock-operation/add-stock-operation.scss +0 -60
  94. package/src/stock-operations/add-stock-operation/add-stock-operation.test.tsx +0 -192
  95. package/src/stock-operations/add-stock-operation/add-stock-operation.utils.tsx +0 -152
  96. package/src/stock-operations/add-stock-operation/add-stock-utils.ts +0 -103
  97. package/src/stock-operations/add-stock-operation/base-operation-details.component.tsx +0 -439
  98. package/src/stock-operations/add-stock-operation/base-operation-details.scss +0 -30
  99. package/src/stock-operations/add-stock-operation/received-items.component.tsx +0 -93
  100. package/src/stock-operations/add-stock-operation/stock-item-search/stock-item-search.component.tsx +0 -70
  101. package/src/stock-operations/add-stock-operation/stock-items-addition-row.component.tsx +0 -357
  102. package/src/stock-operations/add-stock-operation/stock-items-addition-row.resource.tsx +0 -0
  103. package/src/stock-operations/add-stock-operation/stock-items-addition-row.scss +0 -12
  104. package/src/stock-operations/add-stock-operation/stock-items-addition-row.test.tsx +0 -10
  105. package/src/stock-operations/add-stock-operation/stock-items-addition.component.scss +0 -17
  106. package/src/stock-operations/add-stock-operation/stock-items-addition.component.tsx +0 -254
  107. package/src/stock-operations/add-stock-operation/stock-operation-context/useStockOperationContext.tsx +0 -16
  108. package/src/stock-operations/add-stock-operation/stock-operation-reference.component.tsx +0 -39
  109. package/src/stock-operations/add-stock-operation/stock-operation-related-link.component.tsx +0 -38
  110. package/src/stock-operations/add-stock-operation/stock-operation-status.component.tsx +0 -170
  111. package/src/stock-operations/add-stock-operation/stock-operation-submission.component.tsx +0 -189
  112. package/src/stock-operations/add-stock-operation/stock-operation-submission.test.tsx +0 -138
  113. package/src/stock-operations/add-stock-operation/types.ts +0 -55
  114. package/src/stock-operations/add-stock-operation/validationSchema.ts +0 -54
  115. package/src/stock-operations/batch-no-selector/batch-no-selector.component.tsx +0 -114
  116. package/src/stock-operations/batch-no-selector/batch-no-selector.scss +0 -0
  117. package/src/stock-operations/batch-no-selector/batch-no-selector.test.tsx +0 -101
  118. package/src/stock-operations/party-selector/party-selector.component.tsx +0 -59
  119. package/src/stock-operations/qty-uom-selector/qty-uom-selector.component.tsx +0 -65
  120. package/src/stock-operations/qty-uom-selector/qty-uom-selector.resource.tsx +0 -0
  121. package/src/stock-operations/qty-uom-selector/qty-uom-selector.scss +0 -0
  122. package/src/stock-operations/qty-uom-selector/qty-uom-selector.test.tsx +0 -10
  123. package/src/stock-operations/stock-item-selector/stock-item-selector.component.tsx +0 -69
  124. package/src/stock-operations/stock-item-selector/stock-item-selector.scss +0 -0
  125. package/src/stock-operations/stock-item-selector/stock-item-selector.test.tsx +0 -10
  126. package/src/stock-operations/stock-operation-reason-selector/stock-operation-reason-selector.component.tsx +0 -62
  127. package/src/stock-operations/users-selector/users-selector.component.tsx +0 -75
  128. /package/dist/{766.js.LICENSE.txt → 493.js.LICENSE.txt} +0 -0
@@ -0,0 +1,243 @@
1
+ import { Button, Column, InlineLoading, RadioButton, RadioButtonGroup, Stack } from '@carbon/react';
2
+ import { Departure, ListChecked, Save, SendFilled } from '@carbon/react/icons';
3
+ import { restBaseUrl, showSnackbar } from '@openmrs/esm-framework';
4
+ import React, { useCallback, useMemo, useState } from 'react';
5
+ import { useFormContext } from 'react-hook-form';
6
+ import { useTranslation } from 'react-i18next';
7
+ import { extractErrorMessagesFromResponse } from '../../../constants';
8
+ import { StockOperationDTO } from '../../../core/api/types/stockOperation/StockOperationDTO';
9
+ import { StockOperationItemDTO } from '../../../core/api/types/stockOperation/StockOperationItemDTO';
10
+ import { OperationType, StockOperationType } from '../../../core/api/types/stockOperation/StockOperationType';
11
+ import { otherUser } from '../../../core/utils/utils';
12
+ import { handleMutate } from '../../../utils';
13
+ import { showActionDialogButton } from '../../stock-operation.utils';
14
+ import { createStockOperation, deleteStockOperationItem, updateStockOperation } from '../../stock-operations.resource';
15
+ import { StockOperationItemDtoSchema } from '../../validation-schema';
16
+ import useOperationTypePermisions from '../hooks/useOperationTypePermisions';
17
+ import styles from '../stock-operation-form.scss';
18
+
19
+ type StockOperationSubmissionFormStepProps = {
20
+ onPrevious?: () => void;
21
+ stockOperation?: StockOperationDTO;
22
+ stockOperationType: StockOperationType;
23
+ onNext?: () => void;
24
+ };
25
+ const StockOperationSubmissionFormStep: React.FC<StockOperationSubmissionFormStepProps> = ({
26
+ onPrevious,
27
+ stockOperationType,
28
+ stockOperation,
29
+ onNext,
30
+ }) => {
31
+ const { t } = useTranslation();
32
+ const operationTypePermision = useOperationTypePermisions(stockOperationType);
33
+ const editable = useMemo(() => !stockOperation || stockOperation.status === 'NEW', [stockOperation]);
34
+ const form = useFormContext<StockOperationItemDtoSchema>();
35
+ const [approvalRequired, setApprovalRequired] = useState<boolean | null>(stockOperation?.approvalRequired);
36
+ const isStockIssueOperation = useMemo(
37
+ () => OperationType.STOCK_ISSUE_OPERATION_TYPE === stockOperationType.operationType,
38
+ [stockOperationType],
39
+ );
40
+ const handleRadioButtonChange = (selectedItem: boolean) => {
41
+ setApprovalRequired(selectedItem);
42
+ };
43
+
44
+ const handleSave = useCallback(async () => {
45
+ let result: StockOperationDTO; // To store the result for returning
46
+ await form.handleSubmit(async (formData) => {
47
+ try {
48
+ // Get deleted items (items in stock operation bt not i form data)
49
+ const itemsToDelete =
50
+ stockOperation?.stockOperationItems?.reduce<Array<StockOperationItemDTO>>((prev, curr) => {
51
+ const itemDoNotExistInFormData =
52
+ formData.stockOperationItems.findIndex((item) => item.uuid === curr.uuid) === -1;
53
+ if (itemDoNotExistInFormData) {
54
+ return [...prev, curr];
55
+ }
56
+ return prev;
57
+ }, []) ?? [];
58
+ // Delete them from backend asynchronosely
59
+ const deleted = await Promise.allSettled(itemsToDelete.map((item) => deleteStockOperationItem(item.uuid)));
60
+ // Give delete status on completion
61
+ deleted.forEach((del, index) => {
62
+ showSnackbar({
63
+ kind: del.status === 'rejected' ? 'error' : 'success',
64
+ title:
65
+ del.status === 'rejected'
66
+ ? t('stockoperationItemDeleteError', 'Error deleting stock operation item {{item}}', {
67
+ item: itemsToDelete[index].commonName,
68
+ })
69
+ : t('success', 'Success'),
70
+ subtitle:
71
+ del.status === 'rejected'
72
+ ? del.reason?.message
73
+ : t('stockoperationItemDeletSuccess', 'Stock operation item {{item}} deleted succesfully', {
74
+ item: itemsToDelete[index].commonName,
75
+ }),
76
+ });
77
+ });
78
+ // construct update payload
79
+ const payload = {
80
+ ...formData,
81
+ // Remove other uuid if responsible person is set to other
82
+ responsiblePersonUuid:
83
+ formData.responsiblePersonUuid === otherUser.uuid ? undefined : formData.responsiblePersonUuid,
84
+ approvalRequired: approvalRequired ? true : false,
85
+ stockOperationItems: [
86
+ ...formData.stockOperationItems.map((item) => ({
87
+ ...item,
88
+ uuid:
89
+ item.uuid.startsWith('new-item-') || (!stockOperation && isStockIssueOperation) ? undefined : item.uuid, // Remove uuid for newly inserted items and stock issue items derived from requisition to avoid foreign key constraint lookup error
90
+ })),
91
+ ],
92
+ };
93
+ const resp = await (stockOperation
94
+ ? updateStockOperation(stockOperation, payload as any)
95
+ : createStockOperation(payload as any));
96
+ result = resp.data; // Store the response data
97
+ handleMutate(`${restBaseUrl}/stockmanagement/stockoperation`);
98
+ showSnackbar({
99
+ isLowContrast: true,
100
+ title: stockOperation
101
+ ? t('editStockOperation', 'Edit stock operation')
102
+ : t('addStockOperation', 'Add stock operation'),
103
+ kind: 'success',
104
+ subtitle: stockOperation
105
+ ? t('stockOperationEdited', 'Stock operation edited successfully')
106
+ : t('stockOperationAdded', 'Stock operation added successfully'),
107
+ });
108
+ } catch (error) {
109
+ const errorMessages = extractErrorMessagesFromResponse(error);
110
+ showSnackbar({
111
+ subtitle: errorMessages.join(', '),
112
+ title: t('errorSavingForm', 'Error on saving form'),
113
+ kind: 'error',
114
+ isLowContrast: true,
115
+ });
116
+ throw error;
117
+ }
118
+ })(); // Call handleSubmit to trigger validation and submission
119
+ return result; // Return the result after handleSubmit completes
120
+ }, [form, stockOperation, t, approvalRequired, isStockIssueOperation]);
121
+
122
+ const handleComplete = useCallback(() => {
123
+ handleSave().then((operation) => {
124
+ showActionDialogButton('Complete', false, { ...operation, status: 'COMPLETED' });
125
+ });
126
+ }, [handleSave]);
127
+ const handleSubmitForReview = useCallback(() => {
128
+ handleSave().then((operation) => {
129
+ showActionDialogButton('Submit', false, { ...operation, status: 'SUBMITTED' });
130
+ });
131
+ }, [handleSave]);
132
+ const handleDispatch = useCallback(() => {
133
+ handleSave().then((operation) => {
134
+ showActionDialogButton('Dispatch', false, { ...operation, status: 'DISPATCHED' });
135
+ });
136
+ }, [handleSave]);
137
+
138
+ return (
139
+ <Stack gap={4} className={styles.grid}>
140
+ <div className={styles.heading}>
141
+ <h4>
142
+ {operationTypePermision?.requiresDispatchAcknowledgement
143
+ ? t('submitAndDispatch', 'Submit/Dispatch')
144
+ : t('submitAndComplete', 'Submit/Complete')}
145
+ </h4>
146
+ <div className={styles.btnSet}>
147
+ {typeof onPrevious === 'function' && (
148
+ <Button kind="secondary" onClick={onPrevious}>
149
+ Previous
150
+ </Button>
151
+ )}
152
+ {typeof onNext === 'function' && (
153
+ <Button kind="primary" onClick={onNext}>
154
+ Next
155
+ </Button>
156
+ )}
157
+ </div>
158
+ </div>
159
+
160
+ <Column>
161
+ <RadioButtonGroup
162
+ name="rbgApprovelRequired"
163
+ legendText={t('doesThisTransactionRequireApproval', 'Does the transaction require approval ?')}
164
+ onChange={handleRadioButtonChange}
165
+ readOnly={!editable}
166
+ valueSelected={approvalRequired === true}
167
+ >
168
+ <RadioButton value={true} id="rbgApprovelRequired-true" labelText={t('yes', 'Yes')} />
169
+ <RadioButton value={false} id="rbgApprovelRequired-false" labelText={t('no', 'No')} />
170
+ </RadioButtonGroup>
171
+ </Column>
172
+ {editable && (
173
+ <Column>
174
+ {approvalRequired != null && (
175
+ <>
176
+ {!operationTypePermision.requiresDispatchAcknowledgement && !approvalRequired && (
177
+ <Button
178
+ name="complete"
179
+ type="button"
180
+ style={{ margin: '4px' }}
181
+ className="submitButton"
182
+ kind="primary"
183
+ onClick={handleComplete}
184
+ renderIcon={ListChecked}
185
+ >
186
+ {t('complete', 'Complete')}
187
+ </Button>
188
+ )}
189
+ {operationTypePermision.requiresDispatchAcknowledgement && !approvalRequired && (
190
+ <Button
191
+ name="dispatch"
192
+ type="button"
193
+ style={{ margin: '4px' }}
194
+ className="submitButton"
195
+ kind="primary"
196
+ onClick={handleDispatch}
197
+ renderIcon={Departure}
198
+ >
199
+ {form.formState.isSubmitting ? (
200
+ <InlineLoading description={t('dispatching', 'Dispatching')} />
201
+ ) : (
202
+ t('dispatch', 'Dispatch')
203
+ )}
204
+ </Button>
205
+ )}
206
+ {approvalRequired && (
207
+ <Button
208
+ name="submit"
209
+ type="button"
210
+ style={{ margin: '4px' }}
211
+ className="submitButton"
212
+ kind="primary"
213
+ onClick={handleSubmitForReview}
214
+ renderIcon={SendFilled}
215
+ >
216
+ {form.formState.isSubmitting ? (
217
+ <InlineLoading description={t('submittingForReview', 'Submitting for review')} />
218
+ ) : (
219
+ t('submitForReview', 'Submit For Review')
220
+ )}
221
+ </Button>
222
+ )}
223
+ </>
224
+ )}
225
+ <Button
226
+ name="save"
227
+ type="button"
228
+ className="submitButton"
229
+ style={{ margin: '4px' }}
230
+ disabled={form.formState.isSubmitting}
231
+ kind="secondary"
232
+ onClick={handleSave}
233
+ renderIcon={Save}
234
+ >
235
+ {form.formState.isSubmitting ? <InlineLoading /> : t('save', 'Save')}
236
+ </Button>
237
+ </Column>
238
+ )}
239
+ </Stack>
240
+ );
241
+ };
242
+
243
+ export default StockOperationSubmissionFormStep;
@@ -0,0 +1,55 @@
1
+ import { showSnackbar } from '@openmrs/esm-framework';
2
+ import React, { useEffect, useMemo } from 'react';
3
+ import { useFormContext } from 'react-hook-form';
4
+ import { useTranslation } from 'react-i18next';
5
+ import { OperationType, StockOperationType } from '../../core/api/types/stockOperation/StockOperationType';
6
+ import { useStockOperation } from '../stock-operations.resource';
7
+ import { getStockOperationItemFormSchema, StockOperationItemDtoSchema } from '../validation-schema';
8
+
9
+ type StockIssueFormInitializerWithRelatedRequisitionOperationProps = {
10
+ stockRequisitionUuid: string;
11
+ stockOperationType: StockOperationType;
12
+ };
13
+
14
+ const StockIssueFormInitializerWithRelatedRequisitionOperation: React.FC<
15
+ StockIssueFormInitializerWithRelatedRequisitionOperationProps
16
+ > = ({ stockRequisitionUuid, stockOperationType }) => {
17
+ const form = useFormContext<StockOperationItemDtoSchema>();
18
+ const { t } = useTranslation();
19
+ const { error, items: stockOperation, isLoading } = useStockOperation(stockRequisitionUuid);
20
+ const { setValue } = form;
21
+ const stockOperationItemFormSchema = useMemo(() => {
22
+ return getStockOperationItemFormSchema(OperationType.STOCK_ISSUE_OPERATION_TYPE);
23
+ }, []);
24
+ const items = form.watch('stockOperationItems');
25
+ // initialize form values with requisition values for Stock issue operation type
26
+ useEffect(() => {
27
+ if (stockOperation) {
28
+ // Initialize form with the values
29
+ setValue('sourceUuid', stockOperation.sourceUuid);
30
+ setValue('destinationUuid', stockOperation.destinationUuid);
31
+ setValue('requisitionStockOperationUuid', stockRequisitionUuid);
32
+ setValue('operationTypeUuid', stockOperationType.uuid);
33
+ }
34
+ }, [stockOperation, stockOperationItemFormSchema, setValue, items, stockOperationType, stockRequisitionUuid]);
35
+
36
+ // Handle errors encountered
37
+ useEffect(() => {
38
+ if (!stockRequisitionUuid)
39
+ showSnackbar({
40
+ kind: 'error',
41
+ title: t('stockIssueError', 'StockIssue error'),
42
+ subtitle: t('relatedStockRequisitionRequired', 'Related stock requisition Required'),
43
+ });
44
+ if (error) {
45
+ showSnackbar({
46
+ kind: 'error',
47
+ title: t('stockIssueError', 'StockIssue error'),
48
+ subtitle: error?.message,
49
+ });
50
+ }
51
+ }, [stockRequisitionUuid, error, t]);
52
+ return <React.Fragment />;
53
+ };
54
+
55
+ export default StockIssueFormInitializerWithRelatedRequisitionOperation;
@@ -0,0 +1,41 @@
1
+ @use '@carbon/type';
2
+ @use '@carbon/layout';
3
+ @use '@carbon/colors';
4
+
5
+ .form {
6
+ display: flex;
7
+ flex-direction: column;
8
+ justify-content: space-between;
9
+ width: 100%;
10
+ height: 100%;
11
+ }
12
+
13
+ .grid {
14
+ margin: layout.$spacing-05 layout.$spacing-05;
15
+ padding: layout.$spacing-05 0 0 0;
16
+ }
17
+
18
+ .button {
19
+ display: flex;
20
+ align-content: flex-start;
21
+ align-items: baseline;
22
+ min-width: 50%;
23
+ }
24
+
25
+ .buttonSet {
26
+ display: flex;
27
+ justify-content: space-between;
28
+ width: 100%;
29
+ }
30
+
31
+ .datePickerInput span,
32
+ .datePickerInput div,
33
+ .datePickerInput input,
34
+ .datePickerInput {
35
+ min-width: 100%;
36
+ }
37
+
38
+ .title {
39
+ @include type.type-style('heading-02');
40
+ padding: layout.$spacing-03;
41
+ }
@@ -0,0 +1,211 @@
1
+ import {
2
+ Button,
3
+ ButtonSet,
4
+ Column,
5
+ DatePicker,
6
+ DatePickerInput,
7
+ Form,
8
+ NumberInput,
9
+ Stack,
10
+ TextInput,
11
+ } from '@carbon/react';
12
+ import { zodResolver } from '@hookform/resolvers/zod';
13
+ import { DefaultWorkspaceProps, useConfig } from '@openmrs/esm-framework';
14
+ import React, { useMemo } from 'react';
15
+ import { Controller, useForm } from 'react-hook-form';
16
+ import { useTranslation } from 'react-i18next';
17
+ import { z } from 'zod';
18
+ import { DATE_PICKER_CONTROL_FORMAT, DATE_PICKER_FORMAT, formatForDatePicker, today } from '../../../constants';
19
+ import { operationFromString, StockOperationType } from '../../../core/api/types/stockOperation/StockOperationType';
20
+ import { useStockItem } from '../../../stock-items/stock-items.resource';
21
+ import { BaseStockOperationItemFormData, getStockOperationItemFormSchema } from '../../validation-schema';
22
+ import useOperationTypePermisions from '../hooks/useOperationTypePermisions';
23
+ import BatchNoSelector from '../input-components/batch-no-selector.component';
24
+ import QtyUomSelector from '../input-components/quantity-uom-selector.component';
25
+ import styles from './stock-item-form.scss';
26
+ import UniqueBatchNoEntryInput from '../input-components/unique-batch-no-entry-input.component';
27
+ import { ConfigObject } from '../../../config-schema';
28
+
29
+ export interface StockItemFormProps {
30
+ stockOperationType: StockOperationType;
31
+ stockOperationItem: BaseStockOperationItemFormData;
32
+ onSave?: (data: BaseStockOperationItemFormData) => void;
33
+ }
34
+
35
+ interface Props extends DefaultWorkspaceProps, StockItemFormProps {}
36
+ const StockItemForm: React.FC<Props> = ({ closeWorkspace, stockOperationType, stockOperationItem, onSave }) => {
37
+ const operationType = useMemo(() => {
38
+ return operationFromString(stockOperationType.operationType);
39
+ }, [stockOperationType]);
40
+ const formschema = useMemo(() => {
41
+ return getStockOperationItemFormSchema(operationType);
42
+ }, [operationType]);
43
+ const operationTypePermision = useOperationTypePermisions(stockOperationType);
44
+ const { useItemCommonNameAsDisplay } = useConfig<ConfigObject>();
45
+
46
+ const fields = formschema.keyof().options;
47
+ const form = useForm<z.infer<typeof formschema>>({
48
+ resolver: zodResolver(formschema),
49
+ defaultValues: stockOperationItem,
50
+ mode: 'all',
51
+ });
52
+ const { t } = useTranslation();
53
+ const { item } = useStockItem(form.getValues('stockItemUuid'));
54
+ const commonName = useMemo(() => {
55
+ if (!useItemCommonNameAsDisplay) return;
56
+ const drugName = item?.drugName ? `(Drug name: ${item.drugName})` : undefined;
57
+ return `${item?.commonName || t('noCommonNameAvailable', 'No common name available') + (drugName ?? '')}`;
58
+ }, [item, useItemCommonNameAsDisplay, t]);
59
+
60
+ const drugName = useMemo(() => {
61
+ if (useItemCommonNameAsDisplay) return;
62
+ const commonName = item?.commonName ? `(Common name: ${item.commonName})` : undefined;
63
+ return `${item?.drugName || t('noDrugNameAvailable', 'No drug name available') + (commonName ?? '')}`;
64
+ }, [item, useItemCommonNameAsDisplay, t]);
65
+
66
+ const onSubmit = (data: z.infer<typeof formschema>) => {
67
+ onSave?.(data);
68
+ closeWorkspace();
69
+ // Implementation of adding or updating itsms in items table
70
+ };
71
+ return (
72
+ <Form onSubmit={form.handleSubmit(onSubmit)} className={styles.form}>
73
+ <Stack gap={4} className={styles.grid}>
74
+ <p className={styles.title}>{useItemCommonNameAsDisplay ? commonName : drugName}</p>
75
+
76
+ {(operationTypePermision.requiresActualBatchInfo || operationTypePermision.requiresBatchUuid) &&
77
+ fields.includes('batchNo' as any) && (
78
+ <Column>
79
+ <Controller
80
+ control={form.control}
81
+ defaultValue={stockOperationItem?.batchNo}
82
+ name={'batchNo' as any}
83
+ render={({ field, fieldState: { error } }) => (
84
+ <UniqueBatchNoEntryInput
85
+ defaultValue={field.value}
86
+ onValueChange={field.onChange}
87
+ stockItemUuid={stockOperationItem.stockItemUuid}
88
+ error={error?.message}
89
+ stockOperationItemUuid={stockOperationItem.uuid}
90
+ />
91
+ )}
92
+ />
93
+ </Column>
94
+ )}
95
+
96
+ {operationTypePermision.requiresBatchUuid && !operationTypePermision.requiresActualBatchInfo && (
97
+ <Column>
98
+ <Controller
99
+ control={form.control}
100
+ name={'stockBatchUuid' as any}
101
+ render={({ field, fieldState: { error } }) => (
102
+ <BatchNoSelector
103
+ initialValue={stockOperationItem?.stockBatchUuid}
104
+ onValueChange={field.onChange}
105
+ stockItemUuid={stockOperationItem.stockItemUuid}
106
+ error={error?.message}
107
+ />
108
+ )}
109
+ />
110
+ </Column>
111
+ )}
112
+ {(operationTypePermision.requiresActualBatchInfo || operationTypePermision.requiresBatchUuid) &&
113
+ fields.includes('expiration' as any) && (
114
+ <Column>
115
+ <Controller
116
+ control={form.control}
117
+ name={'expiration' as any}
118
+ render={({ field, fieldState: { error } }) => (
119
+ <DatePicker
120
+ id={`expiration`}
121
+ datePickerType="single"
122
+ minDate={formatForDatePicker(today())}
123
+ locale="en"
124
+ className={styles.datePickerInput}
125
+ dateFormat={DATE_PICKER_CONTROL_FORMAT}
126
+ {...field}
127
+ onChange={([newDate]) => {
128
+ field.onChange(newDate);
129
+ }}
130
+ >
131
+ <DatePickerInput
132
+ autoComplete="off"
133
+ id={`expiration-input`}
134
+ name="operationDate"
135
+ placeholder={DATE_PICKER_FORMAT}
136
+ labelText={t('expiriation', 'Expiration Date')}
137
+ invalid={error?.message}
138
+ />
139
+ </DatePicker>
140
+ )}
141
+ />
142
+ </Column>
143
+ )}
144
+
145
+ <Column>
146
+ <Controller
147
+ control={form.control}
148
+ name="quantity"
149
+ render={({ field, fieldState: { error } }) => (
150
+ <NumberInput
151
+ allowEmpty
152
+ className="small-placeholder-text"
153
+ disableWheel
154
+ hideSteppers
155
+ id={`qty`}
156
+ {...field}
157
+ label={t('qty', 'Qty')}
158
+ invalidText={error?.message}
159
+ invalid={error?.message}
160
+ />
161
+ )}
162
+ />
163
+ </Column>
164
+ <Column>
165
+ <Controller
166
+ control={form.control}
167
+ name={'stockItemPackagingUOMUuid'}
168
+ render={({ field, fieldState: { error } }) => (
169
+ <QtyUomSelector
170
+ stockItemUuid={stockOperationItem?.stockItemUuid}
171
+ intiallvalue={field.value}
172
+ error={error?.message}
173
+ onValueChange={field.onChange}
174
+ />
175
+ )}
176
+ />
177
+ </Column>
178
+
179
+ {operationTypePermision?.canCaptureQuantityPrice && fields.includes('purchasePrice' as any) && (
180
+ <Column>
181
+ <Controller
182
+ control={form.control}
183
+ name={'purchasePrice' as any}
184
+ render={({ field, fieldState: { error } }) => (
185
+ <TextInput
186
+ {...field}
187
+ labelText={t('purchasePrice', 'Purchase Price')}
188
+ invalid={error?.message}
189
+ invalidText={error?.message}
190
+ id={`purchaseprice`}
191
+ placeholder={t('purchasePrice', 'Purchase Price')}
192
+ />
193
+ )}
194
+ />
195
+ </Column>
196
+ )}
197
+ </Stack>
198
+
199
+ <ButtonSet className={styles.buttonSet}>
200
+ <Button className={styles.button} kind="secondary" onClick={closeWorkspace}>
201
+ {t('discard', 'Discard')}
202
+ </Button>
203
+ <Button className={styles.button} kind="primary" type="submit" disabled={form.formState.isSubmitting}>
204
+ {t('save', 'Save')}
205
+ </Button>
206
+ </ButtonSet>
207
+ </Form>
208
+ );
209
+ };
210
+
211
+ export default StockItemForm;