@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.
- 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/922.js +1 -0
- package/dist/922.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/config-schema.ts +6 -0
- 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/add-stock-operation/stock-operations-expanded-row/stock-items-table.scss +34 -0
- package/src/stock-operations/add-stock-operation/stock-operations-expanded-row/stock-items-table.tsx +111 -0
- package/src/stock-operations/add-stock-operation/stock-operations-expanded-row/stock-operation-expanded-row.component.tsx +87 -0
- package/src/stock-operations/add-stock-operation/stock-operations-expanded-row/stock-operation-expanded-row.scss +31 -0
- package/src/stock-operations/add-stock-operation/stock-operations-expanded-row/stock-operations-status.tsx +45 -0
- package/src/stock-operations/edit-stock-operation/edit-stock-operation-action-menu.component.tsx +41 -16
- 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 +79 -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 +254 -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/received-items.component.tsx +110 -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 +50 -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 +243 -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 +211 -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 +205 -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 +57 -92
- 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/received-items.component.tsx +0 -93
- 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
@@ -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
|
+
}
|
package/src/stock-operations/stock-operations-forms/stock-item-form/stock-item-form.workspace.tsx
ADDED
@@ -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;
|