@openmrs/esm-stock-management-app 1.0.1-pre.783 → 1.0.1-pre.785
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/__mocks__/index.ts +1 -0
- package/__mocks__/operation-type.mock.ts +532 -0
- package/dist/155.js +1 -0
- package/dist/155.js.map +1 -0
- package/dist/172.js +1 -1
- package/dist/20.js +1 -1
- package/dist/290.js +1 -1
- package/dist/493.js +2 -0
- package/dist/493.js.map +1 -0
- package/dist/606.js +1 -1
- package/dist/627.js +1 -1
- package/dist/914.js +1 -0
- package/dist/914.js.map +1 -0
- package/dist/main.js +1 -1
- package/dist/main.js.map +1 -1
- package/dist/openmrs-esm-stock-management-app.js +1 -1
- package/dist/openmrs-esm-stock-management-app.js.buildmanifest.json +75 -51
- package/dist/openmrs-esm-stock-management-app.js.map +1 -1
- package/dist/routes.json +1 -1
- package/package.json +1 -1
- package/src/core/utils/utils.ts +29 -0
- package/src/index.ts +4 -0
- package/src/routes.json +9 -0
- package/src/stock-items/add-stock-item/transactions/printout/transactions-stockcard-printout.component.tsx +8 -12
- package/src/stock-items/add-stock-item/transactions/transactions.component.tsx +8 -12
- package/src/stock-items/stock-items.resource.ts +5 -5
- package/src/stock-lookups/stock-lookups.resource.ts +2 -2
- package/src/stock-operations/edit-stock-operation/edit-stock-operation-action-menu.component.tsx +41 -16
- package/src/stock-operations/{add-stock-operation/received-items.component.tsx → received-items.component.tsx} +1 -1
- package/src/stock-operations/stock-operation-reference.component.tsx +64 -0
- package/src/stock-operations/stock-operation-status/stock-operation-status-row.tsx +77 -0
- package/src/stock-operations/stock-operation-status/stock-operation-status.scss +32 -0
- package/src/stock-operations/stock-operation-status/stock-operation-status.tsx +45 -0
- package/src/stock-operations/stock-operation-types-selector/stock-operation-types-selector.component.tsx +30 -29
- package/src/stock-operations/stock-operation.utils.tsx +16 -79
- package/src/stock-operations/stock-operations-dialog/stock-operations-issue-stock-button.component.tsx +27 -39
- package/src/stock-operations/stock-operations-dialog/stock-operations-print-button.component.tsx +51 -59
- package/src/stock-operations/{stock-item-selector/stock-item-selector.resource.tsx → stock-operations-forms/hooks/useFilterableStockItems.ts} +4 -4
- package/src/stock-operations/stock-operations-forms/hooks/useFilteredOperationTypesByRoles.ts +30 -0
- package/src/stock-operations/stock-operations-forms/hooks/useOperationTypePermisions.ts +29 -0
- package/src/stock-operations/stock-operations-forms/hooks/useParties.ts +73 -0
- package/src/stock-operations/{users-selector/users-selector.resource.tsx → stock-operations-forms/hooks/useSearchUser.ts} +9 -7
- package/src/stock-operations/{batch-no-selector/batch-no-selector.resource.tsx → stock-operations-forms/hooks/useStockItemBatchNumbers.ts} +3 -3
- package/src/stock-operations/stock-operations-forms/hooks/useStockOperationLinks.ts +20 -0
- package/src/stock-operations/stock-operations-forms/input-components/batch-no-selector.component.tsx +72 -0
- package/src/stock-operations/stock-operations-forms/input-components/batch-no-selector.test.tsx +90 -0
- package/src/stock-operations/{add-stock-operation/stock-item-search/stock-item-search.scss → stock-operations-forms/input-components/input-components-styles.scss} +2 -2
- package/src/stock-operations/stock-operations-forms/input-components/qty-uim-selector.test.tsx +157 -0
- package/src/stock-operations/stock-operations-forms/input-components/quantity-uom-selector.component.tsx +53 -0
- package/src/stock-operations/stock-operations-forms/input-components/stock-item-search.component.tsx +56 -0
- package/src/stock-operations/stock-operations-forms/input-components/stock-operation-reason-selector.component.tsx +59 -0
- package/src/stock-operations/stock-operations-forms/input-components/stock-operation-reason-selector.test.tsx +216 -0
- package/src/stock-operations/{batch-no-selector → stock-operations-forms/input-components}/unique-batch-no-entry-input.component.tsx +12 -7
- package/src/stock-operations/stock-operations-forms/input-components/user-selector.test.tsx +110 -0
- package/src/stock-operations/stock-operations-forms/input-components/users-selector.component.tsx +111 -0
- package/src/stock-operations/stock-operations-forms/step1.test.tsx +303 -0
- package/src/stock-operations/stock-operations-forms/step2.test.tsx +250 -0
- package/src/stock-operations/stock-operations-forms/step3.test.tsx +223 -0
- package/src/stock-operations/stock-operations-forms/steps/base-operation-details-form-step.tsx +241 -0
- package/src/stock-operations/stock-operations-forms/steps/quantity-uom-cell.component.tsx +33 -0
- package/src/stock-operations/stock-operations-forms/steps/stock-availability-cell.component.tsx +51 -0
- package/src/stock-operations/stock-operations-forms/steps/stock-operation-item-batch-no-cell.component.tsx +40 -0
- package/src/stock-operations/stock-operations-forms/steps/stock-operation-item-cell.component.tsx +38 -0
- package/src/stock-operations/stock-operations-forms/steps/stock-operation-item-expiry-cell.component.tsx +41 -0
- package/src/stock-operations/stock-operations-forms/steps/stock-operation-items-form-step.component.tsx +281 -0
- package/src/stock-operations/stock-operations-forms/steps/stock-operation-items-form-step.scc.scss +64 -0
- package/src/stock-operations/stock-operations-forms/steps/stock-operation-submission-form-step.component.tsx +236 -0
- package/src/stock-operations/stock-operations-forms/stock-issue-form-initializer-with-related-requisition-operation.component.tsx +55 -0
- package/src/stock-operations/stock-operations-forms/stock-item-form/stock-item-form.scss +41 -0
- package/src/stock-operations/stock-operations-forms/stock-item-form/stock-item-form.workspace.tsx +197 -0
- package/src/stock-operations/stock-operations-forms/stock-operation-form-header.component.tsx +166 -0
- package/src/stock-operations/stock-operations-forms/stock-operation-form.component.tsx +200 -0
- package/src/stock-operations/stock-operations-forms/stock-operation-form.scss +111 -0
- package/src/stock-operations/stock-operations-forms/stock-operation-related-link.component.tsx +45 -0
- package/src/stock-operations/stock-operations-forms/stock-operation-stepper/stepper.scss +41 -0
- package/src/stock-operations/stock-operations-forms/stock-operation-stepper/stock-operation-stepper.component.tsx +52 -0
- package/src/stock-operations/stock-operations-forms/stock-operations-form-utils.ts +32 -0
- package/src/stock-operations/stock-operations-table.component.tsx +20 -56
- package/src/stock-operations/stock-operations.resource.ts +16 -13
- package/src/stock-operations/validation-schema.ts +72 -14
- package/dist/766.js +0 -2
- package/dist/766.js.map +0 -1
- package/dist/822.js +0 -1
- package/dist/822.js.map +0 -1
- package/src/stock-operations/add-stock-operation/add-stock-operation.component.tsx +0 -349
- package/src/stock-operations/add-stock-operation/add-stock-operation.resource.tsx +0 -27
- package/src/stock-operations/add-stock-operation/add-stock-operation.scss +0 -60
- package/src/stock-operations/add-stock-operation/add-stock-operation.test.tsx +0 -192
- package/src/stock-operations/add-stock-operation/add-stock-operation.utils.tsx +0 -152
- package/src/stock-operations/add-stock-operation/add-stock-utils.ts +0 -103
- package/src/stock-operations/add-stock-operation/base-operation-details.component.tsx +0 -439
- package/src/stock-operations/add-stock-operation/base-operation-details.scss +0 -30
- package/src/stock-operations/add-stock-operation/stock-item-search/stock-item-search.component.tsx +0 -70
- package/src/stock-operations/add-stock-operation/stock-items-addition-row.component.tsx +0 -357
- package/src/stock-operations/add-stock-operation/stock-items-addition-row.resource.tsx +0 -0
- package/src/stock-operations/add-stock-operation/stock-items-addition-row.scss +0 -12
- package/src/stock-operations/add-stock-operation/stock-items-addition-row.test.tsx +0 -10
- package/src/stock-operations/add-stock-operation/stock-items-addition.component.scss +0 -17
- package/src/stock-operations/add-stock-operation/stock-items-addition.component.tsx +0 -254
- package/src/stock-operations/add-stock-operation/stock-operation-context/useStockOperationContext.tsx +0 -16
- package/src/stock-operations/add-stock-operation/stock-operation-reference.component.tsx +0 -39
- package/src/stock-operations/add-stock-operation/stock-operation-related-link.component.tsx +0 -38
- package/src/stock-operations/add-stock-operation/stock-operation-status.component.tsx +0 -170
- package/src/stock-operations/add-stock-operation/stock-operation-submission.component.tsx +0 -189
- package/src/stock-operations/add-stock-operation/stock-operation-submission.test.tsx +0 -138
- package/src/stock-operations/add-stock-operation/types.ts +0 -55
- package/src/stock-operations/add-stock-operation/validationSchema.ts +0 -54
- package/src/stock-operations/batch-no-selector/batch-no-selector.component.tsx +0 -114
- package/src/stock-operations/batch-no-selector/batch-no-selector.scss +0 -0
- package/src/stock-operations/batch-no-selector/batch-no-selector.test.tsx +0 -101
- package/src/stock-operations/party-selector/party-selector.component.tsx +0 -59
- package/src/stock-operations/qty-uom-selector/qty-uom-selector.component.tsx +0 -65
- package/src/stock-operations/qty-uom-selector/qty-uom-selector.resource.tsx +0 -0
- package/src/stock-operations/qty-uom-selector/qty-uom-selector.scss +0 -0
- package/src/stock-operations/qty-uom-selector/qty-uom-selector.test.tsx +0 -10
- package/src/stock-operations/stock-item-selector/stock-item-selector.component.tsx +0 -69
- package/src/stock-operations/stock-item-selector/stock-item-selector.scss +0 -0
- package/src/stock-operations/stock-item-selector/stock-item-selector.test.tsx +0 -10
- package/src/stock-operations/stock-operation-reason-selector/stock-operation-reason-selector.component.tsx +0 -62
- package/src/stock-operations/users-selector/users-selector.component.tsx +0 -75
- /package/dist/{766.js.LICENSE.txt → 493.js.LICENSE.txt} +0 -0
package/src/stock-operations/stock-operations-forms/stock-item-form/stock-item-form.workspace.tsx
ADDED
@@ -0,0 +1,197 @@
|
|
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 } 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
|
+
|
28
|
+
export interface StockItemFormProps {
|
29
|
+
stockOperationType: StockOperationType;
|
30
|
+
stockOperationItem: BaseStockOperationItemFormData;
|
31
|
+
onSave?: (data: BaseStockOperationItemFormData) => void;
|
32
|
+
}
|
33
|
+
|
34
|
+
interface Props extends DefaultWorkspaceProps, StockItemFormProps {}
|
35
|
+
const StockItemForm: React.FC<Props> = ({ closeWorkspace, stockOperationType, stockOperationItem, onSave }) => {
|
36
|
+
const operationType = useMemo(() => {
|
37
|
+
return operationFromString(stockOperationType.operationType);
|
38
|
+
}, [stockOperationType]);
|
39
|
+
const formschema = useMemo(() => {
|
40
|
+
return getStockOperationItemFormSchema(operationType);
|
41
|
+
}, [operationType]);
|
42
|
+
const operationTypePermision = useOperationTypePermisions(stockOperationType);
|
43
|
+
|
44
|
+
const fields = formschema.keyof().options;
|
45
|
+
const form = useForm<z.infer<typeof formschema>>({
|
46
|
+
resolver: zodResolver(formschema),
|
47
|
+
defaultValues: stockOperationItem,
|
48
|
+
mode: 'all',
|
49
|
+
});
|
50
|
+
const { t } = useTranslation();
|
51
|
+
const { item } = useStockItem(form.getValues('stockItemUuid'));
|
52
|
+
const onSubmit = (data: z.infer<typeof formschema>) => {
|
53
|
+
onSave?.(data);
|
54
|
+
closeWorkspace();
|
55
|
+
// Implementation of adding or updating itsms in items table
|
56
|
+
};
|
57
|
+
return (
|
58
|
+
<Form onSubmit={form.handleSubmit(onSubmit)} className={styles.form}>
|
59
|
+
<Stack gap={4} className={styles.grid}>
|
60
|
+
{item?.commonName && <p className={styles.title}>{item?.commonName}</p>}
|
61
|
+
|
62
|
+
{(operationTypePermision.requiresActualBatchInfo || operationTypePermision.requiresBatchUuid) &&
|
63
|
+
fields.includes('batchNo' as any) && (
|
64
|
+
<Column>
|
65
|
+
<Controller
|
66
|
+
control={form.control}
|
67
|
+
defaultValue={stockOperationItem?.batchNo}
|
68
|
+
name={'batchNo' as any}
|
69
|
+
render={({ field, fieldState: { error } }) => (
|
70
|
+
<UniqueBatchNoEntryInput
|
71
|
+
defaultValue={field.value}
|
72
|
+
onValueChange={field.onChange}
|
73
|
+
stockItemUuid={stockOperationItem.stockItemUuid}
|
74
|
+
error={error?.message}
|
75
|
+
stockOperationItemUuid={stockOperationItem.uuid}
|
76
|
+
/>
|
77
|
+
)}
|
78
|
+
/>
|
79
|
+
</Column>
|
80
|
+
)}
|
81
|
+
|
82
|
+
{operationTypePermision.requiresBatchUuid && !operationTypePermision.requiresActualBatchInfo && (
|
83
|
+
<Column>
|
84
|
+
<Controller
|
85
|
+
control={form.control}
|
86
|
+
name={'stockBatchUuid' as any}
|
87
|
+
render={({ field, fieldState: { error } }) => (
|
88
|
+
<BatchNoSelector
|
89
|
+
initialValue={stockOperationItem?.stockBatchUuid}
|
90
|
+
onValueChange={field.onChange}
|
91
|
+
stockItemUuid={stockOperationItem.stockItemUuid}
|
92
|
+
error={error?.message}
|
93
|
+
/>
|
94
|
+
)}
|
95
|
+
/>
|
96
|
+
</Column>
|
97
|
+
)}
|
98
|
+
{(operationTypePermision.requiresActualBatchInfo || operationTypePermision.requiresBatchUuid) &&
|
99
|
+
fields.includes('expiration' as any) && (
|
100
|
+
<Column>
|
101
|
+
<Controller
|
102
|
+
control={form.control}
|
103
|
+
name={'expiration' as any}
|
104
|
+
render={({ field, fieldState: { error } }) => (
|
105
|
+
<DatePicker
|
106
|
+
id={`expiration`}
|
107
|
+
datePickerType="single"
|
108
|
+
minDate={formatForDatePicker(today())}
|
109
|
+
locale="en"
|
110
|
+
className={styles.datePickerInput}
|
111
|
+
dateFormat={DATE_PICKER_CONTROL_FORMAT}
|
112
|
+
{...field}
|
113
|
+
onChange={([newDate]) => {
|
114
|
+
field.onChange(newDate);
|
115
|
+
}}
|
116
|
+
>
|
117
|
+
<DatePickerInput
|
118
|
+
autoComplete="off"
|
119
|
+
id={`expiration-input`}
|
120
|
+
name="operationDate"
|
121
|
+
placeholder={DATE_PICKER_FORMAT}
|
122
|
+
labelText={t('expiriation', 'Expiration Date')}
|
123
|
+
invalid={error?.message}
|
124
|
+
/>
|
125
|
+
</DatePicker>
|
126
|
+
)}
|
127
|
+
/>
|
128
|
+
</Column>
|
129
|
+
)}
|
130
|
+
|
131
|
+
<Column>
|
132
|
+
<Controller
|
133
|
+
control={form.control}
|
134
|
+
name="quantity"
|
135
|
+
render={({ field, fieldState: { error } }) => (
|
136
|
+
<NumberInput
|
137
|
+
allowEmpty
|
138
|
+
className="small-placeholder-text"
|
139
|
+
disableWheel
|
140
|
+
hideSteppers
|
141
|
+
id={`qty`}
|
142
|
+
{...field}
|
143
|
+
label={t('qty', 'Qty')}
|
144
|
+
invalidText={error?.message}
|
145
|
+
invalid={error?.message}
|
146
|
+
/>
|
147
|
+
)}
|
148
|
+
/>
|
149
|
+
</Column>
|
150
|
+
<Column>
|
151
|
+
<Controller
|
152
|
+
control={form.control}
|
153
|
+
name={'stockItemPackagingUOMUuid'}
|
154
|
+
render={({ field, fieldState: { error } }) => (
|
155
|
+
<QtyUomSelector
|
156
|
+
stockItemUuid={stockOperationItem?.stockItemUuid}
|
157
|
+
intiallvalue={field.value}
|
158
|
+
error={error?.message}
|
159
|
+
onValueChange={field.onChange}
|
160
|
+
/>
|
161
|
+
)}
|
162
|
+
/>
|
163
|
+
</Column>
|
164
|
+
|
165
|
+
{operationTypePermision?.canCaptureQuantityPrice && fields.includes('purchasePrice' as any) && (
|
166
|
+
<Column>
|
167
|
+
<Controller
|
168
|
+
control={form.control}
|
169
|
+
name={'purchasePrice' as any}
|
170
|
+
render={({ field, fieldState: { error } }) => (
|
171
|
+
<TextInput
|
172
|
+
{...field}
|
173
|
+
labelText={t('purchasePrice', 'Purchase Price')}
|
174
|
+
invalid={error?.message}
|
175
|
+
invalidText={error?.message}
|
176
|
+
id={`purchaseprice`}
|
177
|
+
placeholder={t('purchasePrice', 'Purchase Price')}
|
178
|
+
/>
|
179
|
+
)}
|
180
|
+
/>
|
181
|
+
</Column>
|
182
|
+
)}
|
183
|
+
</Stack>
|
184
|
+
|
185
|
+
<ButtonSet className={styles.buttonSet}>
|
186
|
+
<Button className={styles.button} kind="secondary" onClick={closeWorkspace}>
|
187
|
+
{t('discard', 'Discard')}
|
188
|
+
</Button>
|
189
|
+
<Button className={styles.button} kind="primary" type="submit" disabled={form.formState.isSubmitting}>
|
190
|
+
{t('save', 'Save')}
|
191
|
+
</Button>
|
192
|
+
</ButtonSet>
|
193
|
+
</Form>
|
194
|
+
);
|
195
|
+
};
|
196
|
+
|
197
|
+
export default StockItemForm;
|
@@ -0,0 +1,166 @@
|
|
1
|
+
import React, { useMemo } from 'react';
|
2
|
+
import { useTranslation } from 'react-i18next';
|
3
|
+
import { StockOperationDTO } from '../../core/api/types/stockOperation/StockOperationDTO';
|
4
|
+
import {
|
5
|
+
operationFromString,
|
6
|
+
OperationType,
|
7
|
+
StockOperationType,
|
8
|
+
StockOperationTypeHasPrint,
|
9
|
+
} from '../../core/api/types/stockOperation/StockOperationType';
|
10
|
+
import StockOperationApprovalButton from '../stock-operations-dialog/stock-operations-approve-button.component';
|
11
|
+
import StockOperationApproveDispatchButton from '../stock-operations-dialog/stock-operations-approve-dispatch-button.component';
|
12
|
+
import StockOperationCancelButton from '../stock-operations-dialog/stock-operations-cancel-button.component';
|
13
|
+
import StockOperationCompleteDispatchButton from '../stock-operations-dialog/stock-operations-completed-dispatch-button.component';
|
14
|
+
import StockOperationIssueStockButton from '../stock-operations-dialog/stock-operations-issue-stock-button.component';
|
15
|
+
import StockOperationPrintButton from '../stock-operations-dialog/stock-operations-print-button.component';
|
16
|
+
import StockOperationRejectButton from '../stock-operations-dialog/stock-operations-reject-button.component';
|
17
|
+
import StockOperationReturnButton from '../stock-operations-dialog/stock-operations-return-button.component';
|
18
|
+
import { operationStatusColor } from '../stock-operations.resource';
|
19
|
+
import useOperationTypePermisions from './hooks/useOperationTypePermisions';
|
20
|
+
import useStockOperationLinks from './hooks/useStockOperationLinks';
|
21
|
+
import styles from './stock-operation-form.scss';
|
22
|
+
import StockOperationRelatedLink from './stock-operation-related-link.component';
|
23
|
+
import StockOperationStatusRow from '../stock-operation-status/stock-operation-status-row';
|
24
|
+
|
25
|
+
type Props = {
|
26
|
+
stockOperation: StockOperationDTO;
|
27
|
+
stockOperationType: StockOperationType;
|
28
|
+
};
|
29
|
+
|
30
|
+
const StockOperationFormHeader: React.FC<Props> = ({ stockOperationType, stockOperation }) => {
|
31
|
+
const operationTypePermision = useOperationTypePermisions(stockOperationType);
|
32
|
+
const operationType = useMemo(() => {
|
33
|
+
return operationFromString(stockOperationType.operationType);
|
34
|
+
}, [stockOperationType]);
|
35
|
+
const requisitionOperationUuid = useMemo(() => {
|
36
|
+
if (
|
37
|
+
stockOperationType?.operationType === OperationType.REQUISITION_OPERATION_TYPE ||
|
38
|
+
stockOperation?.operationType === OperationType.REQUISITION_OPERATION_TYPE ||
|
39
|
+
stockOperationType?.operationType === OperationType.STOCK_ISSUE_OPERATION_TYPE ||
|
40
|
+
stockOperation?.operationType === OperationType.STOCK_ISSUE_OPERATION_TYPE
|
41
|
+
) {
|
42
|
+
return stockOperation.uuid;
|
43
|
+
}
|
44
|
+
return null;
|
45
|
+
}, [stockOperationType, stockOperation]);
|
46
|
+
const { error, isLoading, operationLinks } = useStockOperationLinks(requisitionOperationUuid);
|
47
|
+
const { t } = useTranslation();
|
48
|
+
if (isLoading || error) return null;
|
49
|
+
|
50
|
+
return (
|
51
|
+
<div>
|
52
|
+
<div className={styles.statusBody}>
|
53
|
+
<div className={styles.operationlinkscontainer}>
|
54
|
+
<div className={styles.statusLabel}>
|
55
|
+
<span className={styles.textHeading}>{t('status', 'Status ')}:</span>
|
56
|
+
<span
|
57
|
+
style={{
|
58
|
+
marginLeft: '2px',
|
59
|
+
color: `${operationStatusColor(stockOperation?.status)}`,
|
60
|
+
}}
|
61
|
+
>
|
62
|
+
{stockOperation?.status}
|
63
|
+
</span>
|
64
|
+
</div>
|
65
|
+
<StockOperationStatusRow stockOperation={stockOperation} />
|
66
|
+
</div>
|
67
|
+
|
68
|
+
{((!stockOperation.permission?.canEdit &&
|
69
|
+
(stockOperation.permission?.canApprove || stockOperation.permission?.canReceiveItems)) ||
|
70
|
+
stockOperation.permission?.canEdit ||
|
71
|
+
StockOperationTypeHasPrint(operationType) ||
|
72
|
+
(stockOperation?.permission?.isRequisitionAndCanIssueStock ?? false) ||
|
73
|
+
stockOperation.permission?.isRequisitionAndCanIssueStock) && (
|
74
|
+
<div className={styles.actionBtns}>
|
75
|
+
<>
|
76
|
+
{!stockOperation.permission?.canEdit && stockOperation.permission?.canApprove && (
|
77
|
+
<>
|
78
|
+
{!operationTypePermision.requiresDispatchAcknowledgement && (
|
79
|
+
<StockOperationApprovalButton operation={stockOperation} />
|
80
|
+
)}
|
81
|
+
|
82
|
+
{operationTypePermision.requiresDispatchAcknowledgement && (
|
83
|
+
<StockOperationApproveDispatchButton operation={stockOperation} />
|
84
|
+
)}
|
85
|
+
|
86
|
+
<StockOperationRejectButton operation={stockOperation} />
|
87
|
+
<StockOperationReturnButton operation={stockOperation} />
|
88
|
+
<StockOperationCancelButton operation={stockOperation} />
|
89
|
+
</>
|
90
|
+
)}
|
91
|
+
|
92
|
+
{!stockOperation.permission?.canEdit && stockOperation.permission?.canReceiveItems && (
|
93
|
+
<>
|
94
|
+
<StockOperationCompleteDispatchButton operation={stockOperation} reason={false} />
|
95
|
+
<StockOperationReturnButton operation={stockOperation} />
|
96
|
+
</>
|
97
|
+
)}
|
98
|
+
|
99
|
+
{stockOperation.permission?.canEdit && <StockOperationCancelButton operation={stockOperation} />}
|
100
|
+
{stockOperation.permission?.isRequisitionAndCanIssueStock && (
|
101
|
+
<StockOperationIssueStockButton operation={stockOperation} />
|
102
|
+
)}
|
103
|
+
{(stockOperation.permission?.isRequisitionAndCanIssueStock ||
|
104
|
+
stockOperation.operationType === OperationType.STOCK_ISSUE_OPERATION_TYPE ||
|
105
|
+
stockOperation.operationType === OperationType.REQUISITION_OPERATION_TYPE ||
|
106
|
+
stockOperation.operationType === OperationType.RECEIPT_OPERATION_TYPE ||
|
107
|
+
stockOperation.operationType === OperationType.TRANSFER_OUT_OPERATION_TYPE) && (
|
108
|
+
<StockOperationPrintButton operation={stockOperation} />
|
109
|
+
)}
|
110
|
+
</>
|
111
|
+
</div>
|
112
|
+
)}
|
113
|
+
</div>
|
114
|
+
{operationLinks && operationLinks.length > 0 && (
|
115
|
+
<div className={styles.operationlinkscontainer}>
|
116
|
+
<h6 className={styles.relatedTransactionHeader}>Related Transactions:</h6>
|
117
|
+
{operationLinks.map(
|
118
|
+
(item) =>
|
119
|
+
(stockOperation.uuid === item?.parentUuid || stockOperationType?.uuid === item?.parentUuid) && (
|
120
|
+
<React.Fragment key={item.uuid}>
|
121
|
+
<span>{item?.childOperationTypeName}</span>
|
122
|
+
<span className={item?.childVoided ? 'voided' : ''}>
|
123
|
+
{' '}
|
124
|
+
{item?.childVoided && item?.childOperationNumber}
|
125
|
+
{!item?.childVoided && (
|
126
|
+
<span className={styles.relatedLink}>
|
127
|
+
<StockOperationRelatedLink
|
128
|
+
stockOperationUuid={item?.childUuid}
|
129
|
+
operationNumber={item?.childOperationNumber}
|
130
|
+
/>
|
131
|
+
</span>
|
132
|
+
)}
|
133
|
+
</span>{' '}
|
134
|
+
<span>[{item?.childStatus}]</span>
|
135
|
+
</React.Fragment>
|
136
|
+
),
|
137
|
+
)}
|
138
|
+
<span> </span>
|
139
|
+
{operationLinks.map(
|
140
|
+
(item) =>
|
141
|
+
(stockOperation.uuid === item?.childUuid || stockOperationType.uuid === item?.childUuid) && (
|
142
|
+
<React.Fragment key={item.uuid}>
|
143
|
+
<span>{item?.parentOperationTypeName}</span>
|
144
|
+
<span className={item?.parentVoided ? 'voided' : ''}>
|
145
|
+
{' '}
|
146
|
+
{item?.parentVoided && item?.parentOperationNumber}
|
147
|
+
{!item?.parentVoided && (
|
148
|
+
<span className={styles.relatedLink}>
|
149
|
+
<StockOperationRelatedLink
|
150
|
+
stockOperationUuid={item?.parentUuid}
|
151
|
+
operationNumber={item?.parentOperationNumber}
|
152
|
+
/>
|
153
|
+
</span>
|
154
|
+
)}
|
155
|
+
</span>{' '}
|
156
|
+
<span>[{item?.parentStatus}]</span>
|
157
|
+
</React.Fragment>
|
158
|
+
),
|
159
|
+
)}
|
160
|
+
</div>
|
161
|
+
)}
|
162
|
+
</div>
|
163
|
+
);
|
164
|
+
};
|
165
|
+
|
166
|
+
export default StockOperationFormHeader;
|
@@ -0,0 +1,200 @@
|
|
1
|
+
import { CircleDash } from '@carbon/react/icons';
|
2
|
+
import { zodResolver } from '@hookform/resolvers/zod';
|
3
|
+
import { parseDate, showSnackbar, useConfig, useSession } from '@openmrs/esm-framework';
|
4
|
+
import React, { useEffect, useMemo, useState } from 'react';
|
5
|
+
import { FieldError, FormProvider, useForm } from 'react-hook-form';
|
6
|
+
import { useTranslation } from 'react-i18next';
|
7
|
+
import { ConfigObject } from '../../config-schema';
|
8
|
+
import { today } from '../../constants';
|
9
|
+
import { StockOperationDTO } from '../../core/api/types/stockOperation/StockOperationDTO';
|
10
|
+
import {
|
11
|
+
operationFromString,
|
12
|
+
OperationType,
|
13
|
+
StockOperationType,
|
14
|
+
StockOperationTypeIsStockIssue,
|
15
|
+
} from '../../core/api/types/stockOperation/StockOperationType';
|
16
|
+
import { TabItem } from '../../core/components/tabs/types';
|
17
|
+
import { otherUser, pick } from '../../core/utils/utils';
|
18
|
+
import ReceivedItems from '../received-items.component';
|
19
|
+
import {
|
20
|
+
getStockOperationFormSchema,
|
21
|
+
getStockOperationItemFormSchema,
|
22
|
+
StockOperationItemDtoSchema,
|
23
|
+
} from '../validation-schema';
|
24
|
+
import useOperationTypePermisions from './hooks/useOperationTypePermisions';
|
25
|
+
import BaseOperationDetailsFormStep from './steps/base-operation-details-form-step';
|
26
|
+
import StockOperationItemsFormStep from './steps/stock-operation-items-form-step.component';
|
27
|
+
import StockOperationSubmissionFormStep from './steps/stock-operation-submission-form-step.component';
|
28
|
+
import StockOperationFormHeader from './stock-operation-form-header.component';
|
29
|
+
import StockOperationStepper from './stock-operation-stepper/stock-operation-stepper.component';
|
30
|
+
import StockIssueFormInitializerWithRelatedRequisitionOperation from './stock-issue-form-initializer-with-related-requisition-operation.component';
|
31
|
+
|
32
|
+
/**
|
33
|
+
* Props interface for the StockOperationForm component
|
34
|
+
* @interface StockOperationFormProps
|
35
|
+
* @property {StockOperationType} [stockOperationType] - The stock operation type being created or edited.
|
36
|
+
* @property {StockOperationDTO} [stockOperation] - The stock operation data transfer object.
|
37
|
+
* @property {string} [stockRequisitionUuid] - Requisition operation uuid used in stock issue stockOperation type
|
38
|
+
* When undefined or null, the form will be in creation mode.
|
39
|
+
*/
|
40
|
+
type StockOperationFormProps = {
|
41
|
+
stockOperation?: StockOperationDTO;
|
42
|
+
stockOperationType: StockOperationType;
|
43
|
+
stockRequisitionUuid?: string;
|
44
|
+
};
|
45
|
+
|
46
|
+
const StockOperationForm: React.FC<StockOperationFormProps> = ({
|
47
|
+
stockOperation,
|
48
|
+
stockOperationType,
|
49
|
+
stockRequisitionUuid,
|
50
|
+
}) => {
|
51
|
+
const { t } = useTranslation();
|
52
|
+
const operationType = useMemo(() => {
|
53
|
+
return operationFromString(stockOperationType.operationType);
|
54
|
+
}, [stockOperationType]);
|
55
|
+
const operationTypePermision = useOperationTypePermisions(stockOperationType);
|
56
|
+
const stockOperationItemFormSchema = useMemo(() => {
|
57
|
+
return getStockOperationItemFormSchema(operationType);
|
58
|
+
}, [operationType]);
|
59
|
+
const formschema = useMemo(() => {
|
60
|
+
return getStockOperationFormSchema(operationType);
|
61
|
+
}, [operationType]);
|
62
|
+
const steps: TabItem[] = useMemo(() => {
|
63
|
+
return [
|
64
|
+
{
|
65
|
+
name: stockOperation ? `${stockOperationType?.name} Details` : `${stockOperationType?.name} Details`,
|
66
|
+
component: (
|
67
|
+
<BaseOperationDetailsFormStep
|
68
|
+
stockOperation={stockOperation}
|
69
|
+
stockOperationType={stockOperationType}
|
70
|
+
onNext={() => setSelectedIndex(1)}
|
71
|
+
/>
|
72
|
+
),
|
73
|
+
disabled: true,
|
74
|
+
},
|
75
|
+
{
|
76
|
+
name: t('stockItems', 'Stock Items'),
|
77
|
+
component: (
|
78
|
+
<StockOperationItemsFormStep
|
79
|
+
stockOperation={stockOperation}
|
80
|
+
stockOperationType={stockOperationType}
|
81
|
+
onNext={() => setSelectedIndex(2)}
|
82
|
+
onPrevious={() => setSelectedIndex(0)}
|
83
|
+
/>
|
84
|
+
),
|
85
|
+
disabled: true,
|
86
|
+
},
|
87
|
+
{
|
88
|
+
name: operationTypePermision?.requiresDispatchAcknowledgement ? 'Submit/Dispatch' : 'Submit/Complete',
|
89
|
+
component: (
|
90
|
+
<StockOperationSubmissionFormStep
|
91
|
+
stockOperation={stockOperation}
|
92
|
+
stockOperationType={stockOperationType}
|
93
|
+
onPrevious={() => setSelectedIndex(1)}
|
94
|
+
/>
|
95
|
+
),
|
96
|
+
disabled: true,
|
97
|
+
},
|
98
|
+
].concat(
|
99
|
+
StockOperationTypeIsStockIssue(stockOperation?.operationType as OperationType) ||
|
100
|
+
stockOperation?.permission?.canDisplayReceivedItems
|
101
|
+
? stockOperation.status === 'DISPATCHED' || stockOperation.status === 'COMPLETED'
|
102
|
+
? [
|
103
|
+
{
|
104
|
+
name: t('receivedItems', 'Received Items'),
|
105
|
+
component: <ReceivedItems model={stockOperation} />,
|
106
|
+
disabled: true,
|
107
|
+
},
|
108
|
+
]
|
109
|
+
: []
|
110
|
+
: [],
|
111
|
+
) as TabItem[];
|
112
|
+
}, [stockOperation, stockOperationType, t, operationTypePermision]);
|
113
|
+
const {
|
114
|
+
user: { uuid: defaultLoggedUserUuid },
|
115
|
+
} = useSession();
|
116
|
+
const { autoPopulateResponsiblePerson } = useConfig<ConfigObject>();
|
117
|
+
const [selectedIndex, setSelectedIndex] = useState(0);
|
118
|
+
const form = useForm<StockOperationItemDtoSchema>({
|
119
|
+
// defaultValues: operationType === OperationType.STOCK_ISSUE_OPERATION_TYPE ? issueStockOperation : model,
|
120
|
+
defaultValues: {
|
121
|
+
responsiblePersonUuid:
|
122
|
+
stockOperation?.responsiblePersonUuid ?? // if person uuid exist, make it default
|
123
|
+
(stockOperation?.responsiblePersonOther ? otherUser.uuid : undefined) ?? // if other resp person exist, default other user uuid
|
124
|
+
(autoPopulateResponsiblePerson ? defaultLoggedUserUuid : undefined), //Else default login user if configured
|
125
|
+
operationDate: stockOperation?.operationDate ? parseDate(stockOperation!.operationDate as any) : today(),
|
126
|
+
remarks: stockOperation?.remarks ?? '',
|
127
|
+
|
128
|
+
operationTypeUuid: stockOperation?.operationTypeUuid ?? stockOperationType?.uuid,
|
129
|
+
reasonUuid: stockOperation?.reasonUuid ?? '',
|
130
|
+
responsiblePersonOther: stockOperation?.responsiblePersonOther ?? '',
|
131
|
+
stockOperationItems:
|
132
|
+
stockOperation?.stockOperationItems?.map((item) =>
|
133
|
+
pick(
|
134
|
+
{ ...item, expiration: item.expiration ? parseDate(item.expiration as any) : undefined },
|
135
|
+
stockOperationItemFormSchema.keyof().options,
|
136
|
+
),
|
137
|
+
) ?? [],
|
138
|
+
sourceUuid: stockOperation?.sourceUuid ?? '',
|
139
|
+
destinationUuid: stockOperation?.destinationUuid ?? '',
|
140
|
+
},
|
141
|
+
mode: 'all',
|
142
|
+
resolver: zodResolver(formschema),
|
143
|
+
});
|
144
|
+
|
145
|
+
useEffect(() => {
|
146
|
+
// Show error snackbar
|
147
|
+
Object.entries(form.formState.errors ?? {}).forEach(([key, val]) => {
|
148
|
+
if (['stockOperationItems', 'operationTypeUuid'].includes(key)) {
|
149
|
+
showSnackbar({ kind: 'error', title: key, subtitle: (val[key] as FieldError)?.message });
|
150
|
+
}
|
151
|
+
});
|
152
|
+
// Navigate to step where the error is
|
153
|
+
const fieldSteps = [
|
154
|
+
[
|
155
|
+
'responsiblePersonUuid',
|
156
|
+
'operationDate',
|
157
|
+
'remarks',
|
158
|
+
'sourceUuid',
|
159
|
+
'destinationUuid',
|
160
|
+
'reasonUuid',
|
161
|
+
'responsiblePersonOther',
|
162
|
+
],
|
163
|
+
['stockOperationItems'],
|
164
|
+
];
|
165
|
+
for (let step = 0; step < fieldSteps.length; step++) {
|
166
|
+
const hasError = fieldSteps[step].some((field) => field in form.formState.errors);
|
167
|
+
if (hasError) {
|
168
|
+
setSelectedIndex(step);
|
169
|
+
break;
|
170
|
+
}
|
171
|
+
}
|
172
|
+
}, [form.formState.errors]);
|
173
|
+
|
174
|
+
return (
|
175
|
+
<FormProvider {...form}>
|
176
|
+
{stockOperation && (
|
177
|
+
<StockOperationFormHeader stockOperationType={stockOperationType} stockOperation={stockOperation} />
|
178
|
+
)}
|
179
|
+
{stockOperationType.operationType === OperationType.STOCK_ISSUE_OPERATION_TYPE && (
|
180
|
+
<StockIssueFormInitializerWithRelatedRequisitionOperation
|
181
|
+
stockRequisitionUuid={stockRequisitionUuid as string}
|
182
|
+
stockOperationType={stockOperationType}
|
183
|
+
/>
|
184
|
+
)}
|
185
|
+
<StockOperationStepper
|
186
|
+
steps={steps.map((tab, index) => ({
|
187
|
+
title: tab.name,
|
188
|
+
component: tab.component,
|
189
|
+
disabled: tab.disabled,
|
190
|
+
// subTitle: `Subtitle for ${tab.name}`,
|
191
|
+
icon: <CircleDash />,
|
192
|
+
}))}
|
193
|
+
selectedIndex={selectedIndex}
|
194
|
+
onChange={setSelectedIndex}
|
195
|
+
/>
|
196
|
+
</FormProvider>
|
197
|
+
);
|
198
|
+
};
|
199
|
+
|
200
|
+
export default StockOperationForm;
|
@@ -0,0 +1,111 @@
|
|
1
|
+
@use '@carbon/type';
|
2
|
+
@use '@carbon/layout';
|
3
|
+
@use '@carbon/colors';
|
4
|
+
@import '~@openmrs/esm-styleguide/src/vars';
|
5
|
+
|
6
|
+
.form {
|
7
|
+
display: flex;
|
8
|
+
flex-direction: column;
|
9
|
+
justify-content: space-between;
|
10
|
+
width: 100%;
|
11
|
+
height: 100%;
|
12
|
+
}
|
13
|
+
|
14
|
+
.grid {
|
15
|
+
margin: layout.$spacing-05 layout.$spacing-05;
|
16
|
+
padding: layout.$spacing-05 0 0 0;
|
17
|
+
}
|
18
|
+
|
19
|
+
.button {
|
20
|
+
display: flex;
|
21
|
+
align-content: flex-start;
|
22
|
+
align-items: baseline;
|
23
|
+
min-width: 50%;
|
24
|
+
}
|
25
|
+
|
26
|
+
.buttonSet {
|
27
|
+
display: flex;
|
28
|
+
justify-content: space-between;
|
29
|
+
width: 100%;
|
30
|
+
}
|
31
|
+
|
32
|
+
.datePickerInput span,
|
33
|
+
.datePickerInput div,
|
34
|
+
.datePickerInput input,
|
35
|
+
.datePickerInput {
|
36
|
+
min-width: 100%;
|
37
|
+
}
|
38
|
+
|
39
|
+
.title {
|
40
|
+
@include type.type-style('heading-02');
|
41
|
+
padding: layout.$spacing-03;
|
42
|
+
}
|
43
|
+
|
44
|
+
.heading {
|
45
|
+
text-align: left;
|
46
|
+
text-transform: capitalize;
|
47
|
+
display: flex;
|
48
|
+
flex-direction: row;
|
49
|
+
justify-content: space-between;
|
50
|
+
align-items: center;
|
51
|
+
margin: layout.$spacing-03 0 layout.$spacing-03 0;
|
52
|
+
|
53
|
+
h4 {
|
54
|
+
@include type.type-style('heading-compact-02');
|
55
|
+
color: colors.$gray-70;
|
56
|
+
|
57
|
+
&:after {
|
58
|
+
content: '';
|
59
|
+
display: block;
|
60
|
+
width: 2rem;
|
61
|
+
padding-top: 3px;
|
62
|
+
border-bottom: 0.375rem solid;
|
63
|
+
@include brand-03(border-bottom-color);
|
64
|
+
}
|
65
|
+
}
|
66
|
+
}
|
67
|
+
|
68
|
+
.btnSet {
|
69
|
+
display: flex;
|
70
|
+
flex-direction: row;
|
71
|
+
gap: layout.$spacing-03;
|
72
|
+
align-items: center;
|
73
|
+
}
|
74
|
+
|
75
|
+
.relatedLink {
|
76
|
+
margin-left: layout.$spacing-02;
|
77
|
+
color: colors.$blue-60;
|
78
|
+
}
|
79
|
+
|
80
|
+
.textHeading {
|
81
|
+
font-weight: bold;
|
82
|
+
}
|
83
|
+
|
84
|
+
.statusLabel {
|
85
|
+
display: flex;
|
86
|
+
flex-direction: row;
|
87
|
+
margin-bottom: layout.$spacing-05;
|
88
|
+
}
|
89
|
+
|
90
|
+
.statusBody {
|
91
|
+
display: flex;
|
92
|
+
justify-content: space-between;
|
93
|
+
margin: layout.$spacing-02;
|
94
|
+
}
|
95
|
+
|
96
|
+
.actionBtns {
|
97
|
+
margin: layout.$spacing-03;
|
98
|
+
display: flex;
|
99
|
+
flex-direction: row;
|
100
|
+
gap: layout.$spacing-03;
|
101
|
+
flex-wrap: wrap;
|
102
|
+
height: fit-content;
|
103
|
+
}
|
104
|
+
|
105
|
+
.operationlinkscontainer {
|
106
|
+
margin: layout.$spacing-05;
|
107
|
+
}
|
108
|
+
|
109
|
+
.relatedTransactionHeader {
|
110
|
+
color: colors.$green-50;
|
111
|
+
}
|