@openmrs/esm-stock-management-app 1.0.1-pre.575 → 1.0.1-pre.586

Sign up to get free protection for your applications and to get access to all the features.
@@ -35,6 +35,7 @@ import { useStockOperationPages } from "../stock-operations-table.resource";
35
35
  import { createBaseOperationPayload } from "./add-stock-utils";
36
36
  import { showSnackbar, useSession } from "@openmrs/esm-framework";
37
37
 
38
+ import { Party } from "../../core/api/types/Party";
38
39
  import styles from "../add-stock-operation/base-operation-details.scss";
39
40
 
40
41
  interface BaseOperationDetailsProps {
@@ -54,8 +55,6 @@ const BaseOperationDetails: React.FC<BaseOperationDetailsProps> = ({
54
55
  isEditing,
55
56
  setup: {
56
57
  requiresStockAdjustmentReason: showReason,
57
- shouldLockSource: lockSource,
58
- shouldLockDestination: lockDestination,
59
58
  sourcePartyList,
60
59
  destinationPartyList,
61
60
  },
@@ -114,249 +113,377 @@ const BaseOperationDetails: React.FC<BaseOperationDetailsProps> = ({
114
113
  setIsSaving(false);
115
114
  }
116
115
  };
116
+
117
+ const isCompleteStatus = model?.status === "COMPLETED";
118
+ const sourceTags =
119
+ operation?.stockOperationTypeLocationScopes
120
+ ?.filter((p) => operation?.hasSource && p.isSource)
121
+ .map((p) => p.locationTag) ?? [];
122
+
123
+ const destinationTags =
124
+ operation?.stockOperationTypeLocationScopes
125
+ ?.filter((p) => operation?.hasDestination && p.isDestination)
126
+ .map((p) => p.locationTag) ?? [];
127
+
128
+ const sourcePartyListFilter = (sourcePartyList: Party) => {
129
+ const isValid =
130
+ (sourcePartyList.locationUuid &&
131
+ operation?.sourceType === "Location" &&
132
+ (sourceTags.length === 0 ||
133
+ (sourcePartyList.tags &&
134
+ sourceTags.some((x) => sourcePartyList.tags.includes(x))))) ||
135
+ (sourcePartyList.stockSourceUuid && operation?.sourceType === "Other");
136
+ return isValid;
137
+ };
138
+
139
+ const destinationPartyListFilter = (destinationPartyList: Party) => {
140
+ const isValid =
141
+ (destinationPartyList.locationUuid &&
142
+ operation?.destinationType === "Location" &&
143
+ (destinationTags.length === 0 ||
144
+ (destinationPartyList.tags &&
145
+ destinationTags.some((x) =>
146
+ destinationPartyList.tags.includes(x)
147
+ )))) ||
148
+ (destinationPartyList.stockSourceUuid &&
149
+ operation?.destinationType === "Other");
150
+ return isValid;
151
+ };
117
152
  return (
118
153
  <div style={{ margin: "10px" }}>
119
154
  <form className={`${styles.formContainer} ${styles.verticalForm}`}>
120
- {canEdit && (
121
- <Controller
122
- control={control}
123
- render={({ field: { onChange } }) => (
124
- <DatePicker
125
- datePickerType="single"
126
- maxDate={formatForDatePicker(today())}
127
- locale="en"
128
- dateFormat={DATE_PICKER_CONTROL_FORMAT}
129
- onChange={([newDate]) => {
130
- onChange(newDate);
131
- }}
132
- >
133
- <DatePickerInput
134
- invalid={!!errors.operationDate}
135
- invalidText={errors?.operationDate?.message}
136
- id="operationDate"
137
- name="operationDate"
138
- placeholder={DATE_PICKER_FORMAT}
139
- labelText={t("operationDate", "Operation Date")}
140
- defaultValue={formatForDatePicker(model?.operationDate)}
155
+ {isCompleteStatus ? (
156
+ <>
157
+ {model?.operationDate && (
158
+ <TextInput
159
+ id="operationDateLbl"
160
+ value={formatForDatePicker(model.operationDate)}
161
+ readOnly={true}
162
+ labelText={t("operationDate", "Operation Date")}
163
+ />
164
+ )}
165
+ {model?.operationNumber && (
166
+ <TextInput
167
+ id="operationNoLbl"
168
+ value={model.operationNumber}
169
+ readOnly={true}
170
+ labelText={t("operationNumber", "Operation Number")}
171
+ />
172
+ )}
173
+ {model?.atLocationName && (
174
+ <TextInput
175
+ id="sourceLbl"
176
+ value={model.atLocationName}
177
+ readOnly={true}
178
+ labelText={t("source", "Source")}
179
+ />
180
+ )}
181
+ {model?.destinationName && (
182
+ <TextInput
183
+ id="destinationLbl"
184
+ value={model.destinationName}
185
+ readOnly={true}
186
+ labelText={t("destination", "Destination")}
187
+ />
188
+ )}
189
+ {model?.responsiblePersonGivenName &&
190
+ model?.responsiblePersonFamilyName && (
191
+ <TextInput
192
+ id="responsiblePersonLbl"
193
+ value={`${model.responsiblePersonGivenName} ${model.responsiblePersonFamilyName}`}
194
+ readOnly={true}
195
+ labelText={t("responsiblePerson", "Responsible Person")}
141
196
  />
142
- </DatePicker>
197
+ )}
198
+ {showReason && model?.reasonName && (
199
+ <TextInput
200
+ id="reasonLbl"
201
+ value={model.reasonName}
202
+ readOnly={true}
203
+ labelText={t("reason", "Reason")}
204
+ />
205
+ )}
206
+ {model?.remarks && (
207
+ <TextInput
208
+ id="remarksLbl"
209
+ value={model.remarks}
210
+ readOnly={true}
211
+ labelText={t("remarks", "Remarks")}
212
+ />
143
213
  )}
144
- name="operationDate"
145
- />
146
- )}
147
-
148
- {!canEdit && (
149
- <>
150
- <TextInput
151
- id="operationDateLbl"
152
- value={formatForDatePicker(model?.operationDate)}
153
- readOnly={true}
154
- labelText="Operation Date"
155
- />
156
214
  </>
157
- )}
215
+ ) : (
216
+ <>
217
+ {canEdit && (
218
+ <Controller
219
+ control={control}
220
+ render={({ field: { onChange } }) => (
221
+ <DatePicker
222
+ datePickerType="single"
223
+ maxDate={formatForDatePicker(today())}
224
+ locale="en"
225
+ dateFormat={DATE_PICKER_CONTROL_FORMAT}
226
+ onChange={([newDate]) => {
227
+ onChange(newDate);
228
+ }}
229
+ >
230
+ <DatePickerInput
231
+ invalid={!!errors.operationDate}
232
+ invalidText={errors?.operationDate?.message}
233
+ id="operationDate"
234
+ name="operationDate"
235
+ placeholder={DATE_PICKER_FORMAT}
236
+ labelText={t("operationDate", "Operation Date")}
237
+ defaultValue={formatForDatePicker(model?.operationDate)}
238
+ />
239
+ </DatePicker>
240
+ )}
241
+ name="operationDate"
242
+ />
243
+ )}
158
244
 
159
- {isEditing && model?.operationNumber && (
160
- <TextInput
161
- id="operationNoLbl"
162
- value={model?.operationNumber}
163
- readOnly={true}
164
- labelText={"Operation Number"}
165
- />
166
- )}
245
+ {!canEdit && (
246
+ <>
247
+ <TextInput
248
+ id="operationDateLbl"
249
+ value={formatForDatePicker(model?.operationDate)}
250
+ readOnly={true}
251
+ labelText="Operation Date"
252
+ />
253
+ </>
254
+ )}
167
255
 
168
- {canEdit && !lockSource && operation?.hasSource && (
169
- <PartySelector
170
- controllerName="sourceUuid"
171
- name="sourceUuid"
172
- control={control}
173
- title={
174
- operation?.hasDestination
175
- ? t("from", "From")
176
- : t("location", "Location")
177
- }
178
- placeholder={
179
- operation.hasDestination
180
- ? t("chooseASource", "Choose a source")
181
- : t("chooseALocation", "Choose a location")
182
- }
183
- invalid={!!errors.sourceUuid}
184
- invalidText={errors.sourceUuid && errors?.sourceUuid?.message}
185
- parties={sourcePartyList || []}
186
- />
187
- )}
256
+ {isEditing && model?.operationNumber && (
257
+ <TextInput
258
+ id="operationNoLbl"
259
+ value={model?.operationNumber}
260
+ readOnly={true}
261
+ labelText={"Operation Number"}
262
+ />
263
+ )}
188
264
 
189
- {(!canEdit || isEditing || lockSource) && (
190
- <PartySelector
191
- controllerName="sourceUuid"
192
- name="sourceUuid"
193
- control={control}
194
- title={operation?.hasDestination ? "From" : "Location"}
195
- placeholder={
196
- operation.hasDestination
197
- ? t("chooseASource", "Choose a source")
198
- : t("chooseALocation", "Choose a location")
199
- }
200
- invalid={!!errors.sourceUuid}
201
- invalidText={errors.sourceUuid && errors?.sourceUuid?.message}
202
- parties={sourcePartyList || []}
203
- />
204
- )}
265
+ {canEdit && (operation?.hasSource || model?.atLocationUuid) && (
266
+ <PartySelector
267
+ controllerName="sourceUuid"
268
+ name="sourceUuid"
269
+ control={control}
270
+ partyUuid={model?.atLocationUuid}
271
+ title={
272
+ operation?.hasDestination || model?.destinationUuid
273
+ ? t("from", "From")
274
+ : t("location", "Location")
275
+ }
276
+ placeholder={
277
+ operation.hasDestination || model?.destinationUuid
278
+ ? t("chooseASource", "Choose a source")
279
+ : t("chooseALocation", "Choose a location")
280
+ }
281
+ invalid={!!errors.sourceUuid}
282
+ invalidText={errors.sourceUuid && errors?.sourceUuid?.message}
283
+ parties={sourcePartyList?.filter(sourcePartyListFilter) || []}
284
+ filterFunction={sourcePartyListFilter}
285
+ />
286
+ )}
205
287
 
206
- {canEdit && !lockDestination && operation?.hasDestination && (
207
- <PartySelector
208
- controllerName="destinationUuid"
209
- name="destinationUuid"
210
- control={control}
211
- title={operation?.hasSource ? "To" : "Location"}
212
- placeholder={
213
- operation?.hasSource
214
- ? t("chooseADestination", "Choose a destination")
215
- : "Location"
216
- }
217
- invalid={!!errors.destinationUuid}
218
- invalidText={
219
- errors.destinationUuid && errors?.destinationUuid?.message
220
- }
221
- parties={destinationPartyList || []}
222
- />
223
- )}
288
+ {!canEdit && isEditing && (
289
+ <PartySelector
290
+ controllerName="sourceUuid"
291
+ name="sourceUuid"
292
+ control={control}
293
+ partyUuid={model?.atLocationUuid}
294
+ title={
295
+ operation?.hasDestination || model?.destinationUuid
296
+ ? "From"
297
+ : "Location"
298
+ }
299
+ placeholder={
300
+ operation.hasDestination || model?.destinationUuid
301
+ ? t("chooseASource", "Choose a source")
302
+ : t("chooseALocation", "Choose a location")
303
+ }
304
+ invalid={!!errors.sourceUuid}
305
+ invalidText={errors.sourceUuid && errors?.sourceUuid?.message}
306
+ parties={sourcePartyList?.filter(sourcePartyListFilter) || []}
307
+ filterFunction={sourcePartyListFilter}
308
+ />
309
+ )}
310
+ {canEdit &&
311
+ (operation?.hasDestination || model?.destinationUuid) && (
312
+ <PartySelector
313
+ controllerName="destinationUuid"
314
+ name="destinationUuid"
315
+ control={control}
316
+ partyUuid={model?.destinationUuid}
317
+ title={
318
+ operation?.hasSource || model?.atLocationUuid
319
+ ? t("to", "To")
320
+ : t("location", "Location")
321
+ }
322
+ placeholder={
323
+ operation?.hasSource || model?.atLocationUuid
324
+ ? t("chooseADestination", "Choose a destination")
325
+ : "Location"
326
+ }
327
+ invalid={!!errors.destinationUuid}
328
+ invalidText={
329
+ errors.destinationUuid && errors?.destinationUuid?.message
330
+ }
331
+ parties={
332
+ destinationPartyList?.filter(destinationPartyListFilter) ||
333
+ []
334
+ }
335
+ filterFunction={destinationPartyListFilter}
336
+ />
337
+ )}
224
338
 
225
- {(!canEdit || isEditing || lockDestination) && (
226
- <PartySelector
227
- controllerName="destinationUuid"
228
- name="destinationUuid"
229
- control={control}
230
- title={operation?.hasSource ? "To" : "Location"}
231
- placeholder={
232
- operation?.hasSource
233
- ? t("chooseADestination", "Choose a destination")
234
- : "Location"
235
- }
236
- invalid={!!errors.destinationUuid}
237
- invalidText={
238
- errors.destinationUuid && errors?.destinationUuid?.message
239
- }
240
- parties={destinationPartyList || []}
241
- />
242
- )}
339
+ {!canEdit && isEditing && (
340
+ <PartySelector
341
+ controllerName="destinationUuid"
342
+ name="destinationUuid"
343
+ control={control}
344
+ partyUuid={model?.destinationUuid}
345
+ title={
346
+ operation?.hasSource || model?.atLocationUuid
347
+ ? t("to", "To")
348
+ : t("location", "Location")
349
+ }
350
+ placeholder={
351
+ operation?.hasSource || model?.atLocationUuid
352
+ ? t("chooseADestination", "Choose a destination")
353
+ : "Location"
354
+ }
355
+ invalid={!!errors.destinationUuid}
356
+ invalidText={
357
+ errors.destinationUuid && errors?.destinationUuid?.message
358
+ }
359
+ parties={
360
+ destinationPartyList?.filter(destinationPartyListFilter) || []
361
+ }
362
+ filterFunction={destinationPartyListFilter}
363
+ />
364
+ )}
243
365
 
244
- {canEdit && (
245
- <UsersSelector
246
- controllerName="responsiblePersonUuid"
247
- name="responsiblePersonUuid"
248
- control={control}
249
- title={t("responsiblePerson", "Responsible Person")}
250
- placeholder={t("filter", "Filter ...")}
251
- invalid={!!errors.responsiblePersonUuid}
252
- userUuid={defaultLoggedUserUuid}
253
- invalidText={
254
- errors.responsiblePersonUuid &&
255
- errors?.responsiblePersonUuid?.message
256
- }
257
- onUserChanged={(user) => {
258
- if (user?.uuid === otherUser.uuid) {
259
- setIsOtherUser(true);
260
- } else {
261
- setIsOtherUser(false);
262
- }
263
- }}
264
- />
265
- )}
366
+ {canEdit && (
367
+ <UsersSelector
368
+ controllerName="responsiblePersonUuid"
369
+ name="responsiblePersonUuid"
370
+ control={control}
371
+ userUuid={model?.responsiblePersonUuid}
372
+ title={t("responsiblePerson", "Responsible Person")}
373
+ placeholder={t("filter", "Filter ...")}
374
+ invalid={!!errors.responsiblePersonUuid}
375
+ invalidText={
376
+ errors.responsiblePersonUuid &&
377
+ errors?.responsiblePersonUuid?.message
378
+ }
379
+ onUserChanged={(user) => {
380
+ if (user?.uuid === otherUser.uuid) {
381
+ setIsOtherUser(true);
382
+ } else {
383
+ setIsOtherUser(false);
384
+ }
385
+ }}
386
+ />
387
+ )}
266
388
 
267
- {isOtherUser && (
268
- <ControlledTextInput
269
- id="responsiblePersonOther"
270
- name="responsiblePersonOther"
271
- control={control}
272
- controllerName="responsiblePersonOther"
273
- maxLength={255}
274
- size={"md"}
275
- value={`${model?.responsiblePersonOther ?? ""}`}
276
- labelText={t("responsiblePerson", "Responsible Person")}
277
- placeholder={t("pleaseSpecify", "Please Specify")}
278
- invalid={!!errors.responsiblePersonOther}
279
- invalidText={
280
- errors.responsiblePersonOther &&
281
- errors?.responsiblePersonOther?.message
282
- }
283
- />
284
- )}
389
+ {isOtherUser && (
390
+ <ControlledTextInput
391
+ id="responsiblePersonOther"
392
+ name="responsiblePersonOther"
393
+ control={control}
394
+ controllerName="responsiblePersonOther"
395
+ maxLength={255}
396
+ size={"md"}
397
+ value={`${model?.responsiblePersonOther ?? ""}`}
398
+ labelText={t("responsiblePerson", "Responsible Person")}
399
+ placeholder={t("pleaseSpecify", "Please Specify")}
400
+ invalid={!!errors.responsiblePersonOther}
401
+ invalidText={
402
+ errors.responsiblePersonOther &&
403
+ errors?.responsiblePersonOther?.message
404
+ }
405
+ />
406
+ )}
285
407
 
286
- {!canEdit && (
287
- <UsersSelector
288
- controllerName="responsiblePersonUuid"
289
- name="responsiblePersonUuid"
290
- control={control}
291
- title={t("responsiblePerson", "Responsible Person")}
292
- placeholder={t("filter", "Filter ...")}
293
- invalid={!!errors.responsiblePersonUuid}
294
- invalidText={
295
- errors.responsiblePersonUuid &&
296
- errors?.responsiblePersonUuid?.message
297
- }
298
- onUserChanged={(user) => {
299
- if (user?.uuid === otherUser.uuid) {
300
- setIsOtherUser(true);
301
- } else {
302
- setIsOtherUser(false);
303
- }
304
- }}
305
- />
306
- )}
408
+ {!canEdit && isEditing && (
409
+ <UsersSelector
410
+ controllerName="responsiblePersonUuid"
411
+ name="responsiblePersonUuid"
412
+ control={control}
413
+ userUuid={model?.responsiblePersonUuid}
414
+ title={t("responsiblePerson", "Responsible Person")}
415
+ placeholder={t("filter", "Filter ...")}
416
+ invalid={!!errors.responsiblePersonUuid}
417
+ invalidText={
418
+ errors.responsiblePersonUuid &&
419
+ errors?.responsiblePersonUuid?.message
420
+ }
421
+ onUserChanged={(user) => {
422
+ if (user?.uuid === otherUser.uuid) {
423
+ setIsOtherUser(true);
424
+ } else {
425
+ setIsOtherUser(false);
426
+ }
427
+ }}
428
+ />
429
+ )}
307
430
 
308
- {showReason && canEdit && (
309
- <StockOperationReasonSelector
310
- controllerName="reasonUuid"
311
- name="reasonUuid"
312
- control={control}
313
- placeholder={t("chooseAReason", "Choose a reason")}
314
- title={t("reason", "Reason")}
315
- invalid={!!errors.reasonUuid}
316
- invalidText={errors.reasonUuid && errors?.reasonUuid?.message}
317
- onReasonChange={(reason) => {
318
- setValue("reasonUuid", reason.uuid);
319
- }}
320
- />
321
- )}
431
+ {showReason && canEdit && (
432
+ <StockOperationReasonSelector
433
+ controllerName="reasonUuid"
434
+ name="reasonUuid"
435
+ control={control}
436
+ reasonUuid={model?.reasonUuid}
437
+ placeholder={t("chooseAReason", "Choose a reason")}
438
+ title={t("reason", "Reason")}
439
+ invalid={!!errors.reasonUuid}
440
+ invalidText={errors.reasonUuid && errors?.reasonUuid?.message}
441
+ onReasonChange={(reason) => {
442
+ setValue("reasonUuid", reason.uuid);
443
+ }}
444
+ />
445
+ )}
322
446
 
323
- {showReason && !canEdit && (
324
- <TextInput
325
- id="reasonUuidLbl"
326
- value={model?.reasonName ?? ""}
327
- readOnly={true}
328
- labelText={"Reason:"}
329
- />
330
- )}
447
+ {showReason && !canEdit && (
448
+ <TextInput
449
+ id="reasonUuidLbl"
450
+ value={model?.reasonName ?? ""}
451
+ readOnly={true}
452
+ labelText={"Reason:"}
453
+ />
454
+ )}
331
455
 
332
- <ControlledTextArea
333
- id="remarks"
334
- name="remarks"
335
- control={control}
336
- controllerName="remarks"
337
- maxLength={255}
338
- value={`${model?.remarks ?? ""}`}
339
- labelText={t("remarks", "Remarks")}
340
- invalid={!!errors.remarks}
341
- invalidText={errors.remarks && errors?.remarks?.message}
342
- />
456
+ <ControlledTextArea
457
+ id="remarks"
458
+ name="remarks"
459
+ control={control}
460
+ controllerName="remarks"
461
+ maxLength={255}
462
+ value={`${model?.remarks ?? ""}`}
463
+ labelText={t("remarks", "Remarks")}
464
+ invalid={!!errors.remarks}
465
+ invalidText={errors.remarks && errors?.remarks?.message}
466
+ />
343
467
 
344
- <div style={{ display: "flex", flexDirection: "row-reverse" }}>
345
- <Button
346
- name="save"
347
- type="button"
348
- className="submitButton"
349
- onClick={handleSubmit(handleSave)}
350
- kind="primary"
351
- renderIcon={ArrowRight}
352
- >
353
- {isSaving ? <InlineLoading /> : t("next", "Next")}
354
- </Button>
355
- </div>
468
+ <div style={{ display: "flex", flexDirection: "row-reverse" }}>
469
+ <Button
470
+ name="save"
471
+ type="button"
472
+ className="submitButton"
473
+ onClick={handleSubmit(handleSave)}
474
+ kind="primary"
475
+ renderIcon={ArrowRight}
476
+ >
477
+ {isSaving ? <InlineLoading /> : t("next", "Next")}
478
+ </Button>
479
+ </div>
480
+ </>
481
+ )}
356
482
  </form>
357
483
  </div>
358
484
  );
359
485
  };
486
+
360
487
  function mapIssueStockLocations(stockOperation) {
361
488
  /** Since we are using requisition information to issue stock,
362
489
  please note that the locations will be inverted: the destination listed on the requisition will become the issuing location.