@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.
Files changed (121) 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/914.js +1 -0
  13. package/dist/914.js.map +1 -0
  14. package/dist/main.js +1 -1
  15. package/dist/main.js.map +1 -1
  16. package/dist/openmrs-esm-stock-management-app.js +1 -1
  17. package/dist/openmrs-esm-stock-management-app.js.buildmanifest.json +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/core/utils/utils.ts +29 -0
  22. package/src/index.ts +4 -0
  23. package/src/routes.json +9 -0
  24. package/src/stock-items/add-stock-item/transactions/printout/transactions-stockcard-printout.component.tsx +8 -12
  25. package/src/stock-items/add-stock-item/transactions/transactions.component.tsx +8 -12
  26. package/src/stock-items/stock-items.resource.ts +5 -5
  27. package/src/stock-lookups/stock-lookups.resource.ts +2 -2
  28. package/src/stock-operations/edit-stock-operation/edit-stock-operation-action-menu.component.tsx +41 -16
  29. package/src/stock-operations/{add-stock-operation/received-items.component.tsx → received-items.component.tsx} +1 -1
  30. package/src/stock-operations/stock-operation-reference.component.tsx +64 -0
  31. package/src/stock-operations/stock-operation-status/stock-operation-status-row.tsx +77 -0
  32. package/src/stock-operations/stock-operation-status/stock-operation-status.scss +32 -0
  33. package/src/stock-operations/stock-operation-status/stock-operation-status.tsx +45 -0
  34. package/src/stock-operations/stock-operation-types-selector/stock-operation-types-selector.component.tsx +30 -29
  35. package/src/stock-operations/stock-operation.utils.tsx +16 -79
  36. package/src/stock-operations/stock-operations-dialog/stock-operations-issue-stock-button.component.tsx +27 -39
  37. package/src/stock-operations/stock-operations-dialog/stock-operations-print-button.component.tsx +51 -59
  38. package/src/stock-operations/{stock-item-selector/stock-item-selector.resource.tsx → stock-operations-forms/hooks/useFilterableStockItems.ts} +4 -4
  39. package/src/stock-operations/stock-operations-forms/hooks/useFilteredOperationTypesByRoles.ts +30 -0
  40. package/src/stock-operations/stock-operations-forms/hooks/useOperationTypePermisions.ts +29 -0
  41. package/src/stock-operations/stock-operations-forms/hooks/useParties.ts +73 -0
  42. package/src/stock-operations/{users-selector/users-selector.resource.tsx → stock-operations-forms/hooks/useSearchUser.ts} +9 -7
  43. package/src/stock-operations/{batch-no-selector/batch-no-selector.resource.tsx → stock-operations-forms/hooks/useStockItemBatchNumbers.ts} +3 -3
  44. package/src/stock-operations/stock-operations-forms/hooks/useStockOperationLinks.ts +20 -0
  45. package/src/stock-operations/stock-operations-forms/input-components/batch-no-selector.component.tsx +72 -0
  46. package/src/stock-operations/stock-operations-forms/input-components/batch-no-selector.test.tsx +90 -0
  47. package/src/stock-operations/{add-stock-operation/stock-item-search/stock-item-search.scss → stock-operations-forms/input-components/input-components-styles.scss} +2 -2
  48. package/src/stock-operations/stock-operations-forms/input-components/qty-uim-selector.test.tsx +157 -0
  49. package/src/stock-operations/stock-operations-forms/input-components/quantity-uom-selector.component.tsx +53 -0
  50. package/src/stock-operations/stock-operations-forms/input-components/stock-item-search.component.tsx +56 -0
  51. package/src/stock-operations/stock-operations-forms/input-components/stock-operation-reason-selector.component.tsx +59 -0
  52. package/src/stock-operations/stock-operations-forms/input-components/stock-operation-reason-selector.test.tsx +216 -0
  53. package/src/stock-operations/{batch-no-selector → stock-operations-forms/input-components}/unique-batch-no-entry-input.component.tsx +12 -7
  54. package/src/stock-operations/stock-operations-forms/input-components/user-selector.test.tsx +110 -0
  55. package/src/stock-operations/stock-operations-forms/input-components/users-selector.component.tsx +111 -0
  56. package/src/stock-operations/stock-operations-forms/step1.test.tsx +303 -0
  57. package/src/stock-operations/stock-operations-forms/step2.test.tsx +250 -0
  58. package/src/stock-operations/stock-operations-forms/step3.test.tsx +223 -0
  59. package/src/stock-operations/stock-operations-forms/steps/base-operation-details-form-step.tsx +241 -0
  60. package/src/stock-operations/stock-operations-forms/steps/quantity-uom-cell.component.tsx +33 -0
  61. package/src/stock-operations/stock-operations-forms/steps/stock-availability-cell.component.tsx +51 -0
  62. package/src/stock-operations/stock-operations-forms/steps/stock-operation-item-batch-no-cell.component.tsx +40 -0
  63. package/src/stock-operations/stock-operations-forms/steps/stock-operation-item-cell.component.tsx +38 -0
  64. package/src/stock-operations/stock-operations-forms/steps/stock-operation-item-expiry-cell.component.tsx +41 -0
  65. package/src/stock-operations/stock-operations-forms/steps/stock-operation-items-form-step.component.tsx +281 -0
  66. package/src/stock-operations/stock-operations-forms/steps/stock-operation-items-form-step.scc.scss +64 -0
  67. package/src/stock-operations/stock-operations-forms/steps/stock-operation-submission-form-step.component.tsx +236 -0
  68. package/src/stock-operations/stock-operations-forms/stock-issue-form-initializer-with-related-requisition-operation.component.tsx +55 -0
  69. package/src/stock-operations/stock-operations-forms/stock-item-form/stock-item-form.scss +41 -0
  70. package/src/stock-operations/stock-operations-forms/stock-item-form/stock-item-form.workspace.tsx +197 -0
  71. package/src/stock-operations/stock-operations-forms/stock-operation-form-header.component.tsx +166 -0
  72. package/src/stock-operations/stock-operations-forms/stock-operation-form.component.tsx +200 -0
  73. package/src/stock-operations/stock-operations-forms/stock-operation-form.scss +111 -0
  74. package/src/stock-operations/stock-operations-forms/stock-operation-related-link.component.tsx +45 -0
  75. package/src/stock-operations/stock-operations-forms/stock-operation-stepper/stepper.scss +41 -0
  76. package/src/stock-operations/stock-operations-forms/stock-operation-stepper/stock-operation-stepper.component.tsx +52 -0
  77. package/src/stock-operations/stock-operations-forms/stock-operations-form-utils.ts +32 -0
  78. package/src/stock-operations/stock-operations-table.component.tsx +20 -56
  79. package/src/stock-operations/stock-operations.resource.ts +16 -13
  80. package/src/stock-operations/validation-schema.ts +72 -14
  81. package/dist/766.js +0 -2
  82. package/dist/766.js.map +0 -1
  83. package/dist/822.js +0 -1
  84. package/dist/822.js.map +0 -1
  85. package/src/stock-operations/add-stock-operation/add-stock-operation.component.tsx +0 -349
  86. package/src/stock-operations/add-stock-operation/add-stock-operation.resource.tsx +0 -27
  87. package/src/stock-operations/add-stock-operation/add-stock-operation.scss +0 -60
  88. package/src/stock-operations/add-stock-operation/add-stock-operation.test.tsx +0 -192
  89. package/src/stock-operations/add-stock-operation/add-stock-operation.utils.tsx +0 -152
  90. package/src/stock-operations/add-stock-operation/add-stock-utils.ts +0 -103
  91. package/src/stock-operations/add-stock-operation/base-operation-details.component.tsx +0 -439
  92. package/src/stock-operations/add-stock-operation/base-operation-details.scss +0 -30
  93. package/src/stock-operations/add-stock-operation/stock-item-search/stock-item-search.component.tsx +0 -70
  94. package/src/stock-operations/add-stock-operation/stock-items-addition-row.component.tsx +0 -357
  95. package/src/stock-operations/add-stock-operation/stock-items-addition-row.resource.tsx +0 -0
  96. package/src/stock-operations/add-stock-operation/stock-items-addition-row.scss +0 -12
  97. package/src/stock-operations/add-stock-operation/stock-items-addition-row.test.tsx +0 -10
  98. package/src/stock-operations/add-stock-operation/stock-items-addition.component.scss +0 -17
  99. package/src/stock-operations/add-stock-operation/stock-items-addition.component.tsx +0 -254
  100. package/src/stock-operations/add-stock-operation/stock-operation-context/useStockOperationContext.tsx +0 -16
  101. package/src/stock-operations/add-stock-operation/stock-operation-reference.component.tsx +0 -39
  102. package/src/stock-operations/add-stock-operation/stock-operation-related-link.component.tsx +0 -38
  103. package/src/stock-operations/add-stock-operation/stock-operation-status.component.tsx +0 -170
  104. package/src/stock-operations/add-stock-operation/stock-operation-submission.component.tsx +0 -189
  105. package/src/stock-operations/add-stock-operation/stock-operation-submission.test.tsx +0 -138
  106. package/src/stock-operations/add-stock-operation/types.ts +0 -55
  107. package/src/stock-operations/add-stock-operation/validationSchema.ts +0 -54
  108. package/src/stock-operations/batch-no-selector/batch-no-selector.component.tsx +0 -114
  109. package/src/stock-operations/batch-no-selector/batch-no-selector.scss +0 -0
  110. package/src/stock-operations/batch-no-selector/batch-no-selector.test.tsx +0 -101
  111. package/src/stock-operations/party-selector/party-selector.component.tsx +0 -59
  112. package/src/stock-operations/qty-uom-selector/qty-uom-selector.component.tsx +0 -65
  113. package/src/stock-operations/qty-uom-selector/qty-uom-selector.resource.tsx +0 -0
  114. package/src/stock-operations/qty-uom-selector/qty-uom-selector.scss +0 -0
  115. package/src/stock-operations/qty-uom-selector/qty-uom-selector.test.tsx +0 -10
  116. package/src/stock-operations/stock-item-selector/stock-item-selector.component.tsx +0 -69
  117. package/src/stock-operations/stock-item-selector/stock-item-selector.scss +0 -0
  118. package/src/stock-operations/stock-item-selector/stock-item-selector.test.tsx +0 -10
  119. package/src/stock-operations/stock-operation-reason-selector/stock-operation-reason-selector.component.tsx +0 -62
  120. package/src/stock-operations/users-selector/users-selector.component.tsx +0 -75
  121. /package/dist/{766.js.LICENSE.txt → 493.js.LICENSE.txt} +0 -0
@@ -1,189 +0,0 @@
1
- import React, { useState } from 'react';
2
- import { useTranslation } from 'react-i18next';
3
- import { StockOperationDTO } from '../../core/api/types/stockOperation/StockOperationDTO';
4
- import { SaveStockOperation, SaveStockOperationAction } from '../../stock-items/types';
5
- import { StockOperationType } from '../../core/api/types/stockOperation/StockOperationType';
6
- import { InitializeResult } from './types';
7
- import { Button, InlineLoading, RadioButton, RadioButtonGroup, TextInput } from '@carbon/react';
8
- import { Departure, ListChecked, Save, SendFilled, Undo } from '@carbon/react/icons';
9
-
10
- interface StockOperationSubmissionProps {
11
- isEditing?: boolean;
12
- model?: StockOperationDTO;
13
- onSave?: SaveStockOperation;
14
- operation: StockOperationType;
15
- setup: InitializeResult;
16
- canEdit?: boolean;
17
- locked?: boolean;
18
- requiresDispatchAcknowledgement?: boolean;
19
- actions: {
20
- onGoBack: () => void;
21
- onSave?: SaveStockOperation;
22
- onComplete: SaveStockOperationAction;
23
- onSubmit: SaveStockOperationAction;
24
- onDispatch: SaveStockOperationAction;
25
- };
26
- }
27
-
28
- const StockOperationSubmission: React.FC<StockOperationSubmissionProps> = ({
29
- canEdit,
30
- locked,
31
- model,
32
- requiresDispatchAcknowledgement,
33
- actions,
34
- isEditing,
35
- }) => {
36
- const { t } = useTranslation();
37
- const [isSaving, setIsSaving] = useState(false);
38
- const [approvalRequired, setApprovalRequired] = useState<boolean | null>(model?.approvalRequired);
39
-
40
- const handleRadioButtonChange = (selectedItem: boolean) => {
41
- setApprovalRequired(selectedItem);
42
- };
43
-
44
- return (
45
- <div style={{ margin: '10px' }}>
46
- {canEdit && !locked && (
47
- <div style={{ margin: '10px' }}>
48
- <RadioButtonGroup
49
- name="rbgApprovelRequired"
50
- legendText={t('doesThisTransactionRequireApproval', 'Does the transaction require approval ?')}
51
- onChange={handleRadioButtonChange}
52
- >
53
- <RadioButton value={true} id="rbgApprovelRequired-true" labelText={t('yes', 'Yes')} />
54
- <RadioButton value={false} id="rbgApprovelRequired-false" labelText={t('no', 'No')} />
55
- </RadioButtonGroup>
56
- </div>
57
- )}
58
- {!canEdit && (
59
- <>
60
- <TextInput
61
- style={{ margin: '5px' }}
62
- id="rbgApproveRequiredLbl"
63
- value={approvalRequired ? t('yes', 'Yes') : t('no', 'No')}
64
- readOnly={true}
65
- labelText={t('doesThisTransactionRequireApproval', 'Does the transaction require approval ?')}
66
- />
67
- </>
68
- )}
69
-
70
- {canEdit && !locked && (
71
- <div className="stkpg-form-buttons" style={{ margin: '10px' }}>
72
- {approvalRequired != null && (
73
- <>
74
- {!requiresDispatchAcknowledgement && !approvalRequired && (
75
- <Button
76
- name="complete"
77
- type="button"
78
- style={{ margin: '4px' }}
79
- className="submitButton"
80
- kind="primary"
81
- onClick={async () => {
82
- delete model?.dateCreated;
83
- setIsSaving(true);
84
- if (!isEditing) {
85
- delete model.status;
86
- await actions.onSave(model);
87
- setIsSaving(false);
88
- }
89
- model.status = 'COMPLETED';
90
- actions.onComplete(model);
91
- setIsSaving(false);
92
- }}
93
- renderIcon={ListChecked}
94
- >
95
- {t('complete', 'Complete')}
96
- </Button>
97
- )}
98
- {requiresDispatchAcknowledgement && !approvalRequired && (
99
- <Button
100
- name="dispatch"
101
- type="button"
102
- style={{ margin: '4px' }}
103
- className="submitButton"
104
- kind="primary"
105
- onClick={async () => {
106
- delete model?.dateCreated;
107
- delete model?.status;
108
- setIsSaving(true);
109
- await actions.onSave(model).then(() => {
110
- model.status = 'DISPATCHED';
111
- actions.onDispatch(model);
112
- setIsSaving(false);
113
- });
114
- }}
115
- renderIcon={Departure}
116
- >
117
- {isSaving ? (
118
- <InlineLoading description={t('dispatching', 'Dispatching')} />
119
- ) : (
120
- t('dispatch', 'Dispatch')
121
- )}
122
- </Button>
123
- )}
124
- {approvalRequired && (
125
- <Button
126
- name="submit"
127
- type="button"
128
- style={{ margin: '4px' }}
129
- className="submitButton"
130
- kind="primary"
131
- onClick={async () => {
132
- delete model?.dateCreated;
133
- delete model?.status;
134
- setIsSaving(true);
135
- await actions.onSave(model).then(() => {
136
- model.status = 'SUBMITTED';
137
- actions.onSubmit(model);
138
- setIsSaving(false);
139
- });
140
- }}
141
- renderIcon={SendFilled}
142
- >
143
- {isSaving ? (
144
- <InlineLoading description={t('submittingForReview', 'Submitting for review')} />
145
- ) : (
146
- t('submitForReview', 'Submit For Review')
147
- )}
148
- </Button>
149
- )}
150
- </>
151
- )}
152
- <Button
153
- name="save"
154
- type="button"
155
- className="submitButton"
156
- style={{ margin: '4px' }}
157
- disabled={isSaving}
158
- onClick={async () => {
159
- delete model?.dateCreated;
160
- delete model?.status;
161
- setIsSaving(true);
162
- model.approvalRequired = approvalRequired ? true : false;
163
- await actions.onSave(model);
164
- setIsSaving(false);
165
- }}
166
- kind="secondary"
167
- renderIcon={Save}
168
- >
169
- {isSaving ? <InlineLoading /> : t('save', 'Save')}
170
- </Button>
171
- {!isSaving && (
172
- <Button
173
- type="button"
174
- style={{ margin: '4px' }}
175
- className="cancelButton"
176
- kind="tertiary"
177
- onClick={actions.onGoBack}
178
- renderIcon={Undo}
179
- >
180
- {t('goBack', 'Go Back')}
181
- </Button>
182
- )}
183
- </div>
184
- )}
185
- </div>
186
- );
187
- };
188
-
189
- export default StockOperationSubmission;
@@ -1,138 +0,0 @@
1
- import React from 'react';
2
- import { render, waitFor, screen } from '@testing-library/react';
3
- import StockOperationSubmission from './stock-operation-submission.component';
4
- import { StockOperationDTO } from '../../core/api/types/stockOperation/StockOperationDTO';
5
- import { StockOperationType } from '../../core/api/types/stockOperation/StockOperationType';
6
- import { InitializeResult } from './types';
7
- import userEvent from '@testing-library/user-event';
8
-
9
- const mockOnGoBack = jest.fn();
10
- const mockOnSave = jest.fn();
11
- const mockOnComplete = jest.fn();
12
- const mockOnSubmit = jest.fn();
13
- const mockOnDispatch = jest.fn();
14
- const defaultProps = {
15
- canEdit: true,
16
- locked: false,
17
- model: { approvalRequired: null } as StockOperationDTO,
18
- operation: {
19
- name: 'Stock Issue',
20
- description: 'Issuing stock',
21
- operationType: 'stockissue',
22
- hasSource: true,
23
- sourceType: {},
24
- hasDestination: true,
25
- destinationType: {},
26
- hasRecipient: false,
27
- recipientRequired: false,
28
- availableWhenReserved: false,
29
- allowExpiredBatchNumbers: false,
30
- stockOperationTypeLocationScopes: [],
31
- } as StockOperationType,
32
- setup: {
33
- isNegativeQuantityAllowed: false,
34
- requiresBatchUuid: false,
35
- requiresActualBatchInfo: false,
36
- isQuantityOptional: false,
37
- } as InitializeResult,
38
- actions: {
39
- onGoBack: mockOnGoBack,
40
- onSave: mockOnSave,
41
- onComplete: mockOnComplete,
42
- onSubmit: mockOnSubmit,
43
- onDispatch: mockOnDispatch,
44
- },
45
- };
46
-
47
- describe('StockOperationSubmission', () => {
48
- let user;
49
-
50
- beforeEach(() => {
51
- jest.clearAllMocks();
52
- user = userEvent.setup();
53
- });
54
-
55
- it('renders without crashing', () => {
56
- render(<StockOperationSubmission {...defaultProps} />);
57
- expect(screen.getByText(/does the transaction require approval/i)).toBeInTheDocument();
58
- });
59
-
60
- it('allows approval required to be selected', async () => {
61
- render(<StockOperationSubmission {...defaultProps} />);
62
- await user.click(screen.getByLabelText(/yes/i));
63
- const yesRadioButton = screen.getByLabelText(/yes/i) as HTMLInputElement;
64
- expect(yesRadioButton.checked).toBe(true);
65
- });
66
-
67
- it('allows approval not required to be selected', async () => {
68
- render(<StockOperationSubmission {...defaultProps} />);
69
- await user.click(screen.getByLabelText(/no/i));
70
- const noRadioButton = screen.getByLabelText(/no/i) as HTMLInputElement;
71
- expect(noRadioButton.checked).toBe(true);
72
- });
73
-
74
- it('calls onGoBack when Go Back button is clicked', async () => {
75
- render(<StockOperationSubmission {...defaultProps} />);
76
- await user.click(screen.getByText(/go back/i));
77
- expect(mockOnGoBack).toHaveBeenCalled();
78
- });
79
-
80
- it('shows loading indicator when saving', async () => {
81
- render(<StockOperationSubmission {...defaultProps} />);
82
- await user.click(screen.getByText(/save/i));
83
- await waitFor(() => {
84
- expect(mockOnSave).toHaveBeenCalled();
85
- });
86
- });
87
-
88
- it('calls onSave when save button is clicked', async () => {
89
- render(<StockOperationSubmission {...defaultProps} />);
90
- await user.click(screen.getByText(/save/i));
91
- await waitFor(() => {
92
- expect(mockOnSave).toHaveBeenCalled();
93
- });
94
- });
95
-
96
- it('disables the save button while saving', async () => {
97
- const mockOnSave = jest.fn<Promise<void>, [StockOperationDTO]>(
98
- (model) => new Promise((resolve) => setTimeout(resolve, 500)),
99
- );
100
-
101
- const testProps = {
102
- ...defaultProps,
103
- actions: {
104
- ...defaultProps.actions,
105
- onSave: mockOnSave,
106
- },
107
- };
108
-
109
- render(<StockOperationSubmission {...testProps} />);
110
- const saveButton = screen.getByRole('button', { name: 'Save' });
111
- await user.click(saveButton);
112
-
113
- expect(saveButton).toBeDisabled();
114
-
115
- await waitFor(() => {
116
- expect(saveButton).not.toBeDisabled();
117
- });
118
- });
119
-
120
- it('calls onComplete when Complete button is clicked', async () => {
121
- render(<StockOperationSubmission {...defaultProps} />);
122
- await user.click(screen.getByText(/complete/i));
123
- await waitFor(() => {
124
- expect(mockOnComplete).toHaveBeenCalled();
125
- });
126
- });
127
-
128
- it('does not render buttons when locked', () => {
129
- const lockedProps = {
130
- ...defaultProps,
131
- locked: true,
132
- };
133
-
134
- render(<StockOperationSubmission {...lockedProps} />);
135
- expect(screen.queryByRole('button', { name: /save/i })).not.toBeInTheDocument();
136
- expect(screen.queryByRole('button', { name: /complete/i })).not.toBeInTheDocument();
137
- });
138
- });
@@ -1,55 +0,0 @@
1
- import { StockOperationDTO } from '../../core/api/types/stockOperation/StockOperationDTO';
2
- import { StockOperationItemDTO } from '../../core/api/types/stockOperation/StockOperationItemDTO';
3
- import { Party } from '../../core/api/types/Party';
4
- import { StockOperationType } from '../../core/api/types/stockOperation/StockOperationType';
5
- import { SaveStockOperation } from '../../stock-items/types';
6
- import { StockBatchDTO } from '../../core/api/types/stockItem/StockBatchDTO';
7
- import { StockItemPackagingUOMDTO } from '../../core/api/types/stockItem/StockItemPackagingUOM';
8
- import { StockItemInventory } from '../../core/api/types/stockItem/StockItemInventory';
9
-
10
- export interface InitializeResult {
11
- dto?: StockOperationDTO;
12
- requisition?: string;
13
- stockItems?: StockOperationItemDTO[];
14
- showQuantityRequested?: boolean;
15
- isNegativeQuantityAllowed: boolean;
16
- requiresBatchUuid: boolean;
17
- requiresActualBatchInfo: boolean;
18
- isQuantityOptional: boolean;
19
- canCaptureQuantityPrice: boolean;
20
- requiresStockAdjustmentReason: boolean;
21
- requiresDispatchAcknowledgement: boolean;
22
- allowExpiredBatchNumbers: boolean;
23
- canEditModel: boolean;
24
- canViewModel: boolean;
25
- canApproveModel: boolean;
26
- canIssueStock: boolean;
27
- canReceiveItems: boolean;
28
- canDisplayReceivedItems: boolean;
29
- canUpdateItemsBatchInformation: boolean;
30
- canPrint: boolean;
31
- sourceTags: string[];
32
- destinationTags: string[];
33
- shouldLockSource: boolean;
34
- shouldLockDestination: boolean;
35
- sourcePartyListFilter?: (p: Party) => boolean;
36
- destinationPartyListFilter?: (p: Party) => boolean;
37
- location?: string;
38
- sourcePartyList?: Party[];
39
- destinationPartyList?: Party[];
40
- stockOperationTypes: StockOperationType[];
41
- hasQtyRequested: boolean;
42
- batchNos?: { [key: string]: StockBatchDTO[] };
43
- itemUoM?: { [key: string]: StockItemPackagingUOMDTO[] };
44
- batchBalance?: { [key: string]: StockItemInventory };
45
- }
46
-
47
- export interface AddStockOperationProps {
48
- isEditing?: boolean;
49
- model?: StockOperationDTO;
50
- onSave?: SaveStockOperation;
51
- operation: StockOperationType;
52
- operations?: StockOperationType[];
53
- canPrint?: boolean;
54
- canEdit?: boolean;
55
- }
@@ -1,54 +0,0 @@
1
- import { z } from 'zod';
2
-
3
- export const baseStockOperationSchema = {
4
- stockItemUuid: z.string().min(1, { message: 'Required' }),
5
- stockItemName: z.string().min(1).nullish(),
6
- stockItemPackagingUOMUuid: z.string().min(1, { message: 'Required' }),
7
- stockItemPackagingUOMName: z.string().min(1).nullish(),
8
- batchNo: z.string().min(1, { message: 'Required' }),
9
- stockBatchUuid: z.string().optional(),
10
- expiration: z.coerce.date({ required_error: 'Required' }),
11
- quantity: z.coerce.number().min(1, { message: 'Required' }),
12
- purchasePrice: z.coerce.number().nullish(),
13
- hasExpiration: z.boolean().nullish(),
14
- };
15
-
16
- export const stockItemTableSchema = z.object(baseStockOperationSchema);
17
-
18
- export const stockItemTableSchemaWithNoExpiration = stockItemTableSchema.omit({
19
- expiration: true,
20
- });
21
-
22
- export const stockOperationItemsSchema = z.object({
23
- stockItems: z.array(stockItemTableSchema),
24
- });
25
-
26
- export type StockOperationItemsFormData = z.infer<typeof stockOperationItemsSchema>;
27
-
28
- export type StockOperationItemFormData = z.infer<typeof stockOperationItemsSchema>['stockItems'][number];
29
-
30
- export function useValidationSchema(operationType?: string) {
31
- if (operationType === 'requisition') {
32
- const customSchema = stockItemTableSchema.omit({
33
- batchNo: true,
34
- expiration: true,
35
- });
36
- return z.object({
37
- stockItems: z.array(customSchema),
38
- });
39
- }
40
- if (operationType === 'adjustment') {
41
- const customSchema = stockItemTableSchema.extend({
42
- quantity: z.coerce
43
- .number()
44
- .refine((value) => value !== 0, {
45
- message: 'Quantity cannot be zero.',
46
- })
47
- .or(z.literal(0, { invalid_type_error: 'Invalid quantity format' })),
48
- });
49
- return z.object({
50
- stockItems: z.array(customSchema),
51
- });
52
- }
53
- return stockOperationItemsSchema;
54
- }
@@ -1,114 +0,0 @@
1
- import React, { ReactNode, useEffect, useMemo, useState } from 'react';
2
- import { Control, Controller, FieldValues } from 'react-hook-form';
3
- import { ComboBox, InlineLoading } from '@carbon/react';
4
- import { useStockItemBatchNos } from './batch-no-selector.resource';
5
- import { StockBatchDTO } from '../../core/api/types/stockItem/StockBatchDTO';
6
- import { useStockItemBatchInformationHook } from '../../stock-items/add-stock-item/batch-information/batch-information.resource';
7
- import { useTranslation } from 'react-i18next';
8
- import { formatForDatePicker } from '../../constants';
9
-
10
- interface BatchNoSelectorProps<T> {
11
- placeholder?: string;
12
- stockItemUuid: string;
13
- batchUuid?: string;
14
- onBatchNoChanged?: (item: StockBatchDTO) => void;
15
- title?: string;
16
- invalid?: boolean;
17
- invalidText?: ReactNode;
18
- selectedItem?: string;
19
-
20
- // Control
21
- controllerName: string;
22
- name: string;
23
- control: Control<FieldValues, T>;
24
- }
25
-
26
- const BatchNoSelector = <T,>(props: BatchNoSelectorProps<T>) => {
27
- const { isLoading, stockItemBatchNos } = useStockItemBatchNos(props.stockItemUuid);
28
- const [validationMessage, setValidationMessage] = useState<string | null>(null);
29
- const [selectedItem, setSelectedItem] = useState<StockBatchDTO | null>(null);
30
- const { t } = useTranslation();
31
-
32
- const initialSelectedItem = useMemo(
33
- () => stockItemBatchNos?.find((stockItem) => stockItem.uuid === props.batchUuid) ?? '',
34
- [stockItemBatchNos, props.batchUuid],
35
- );
36
-
37
- const { items, setStockItemUuid } = useStockItemBatchInformationHook();
38
-
39
- useEffect(() => {
40
- setStockItemUuid(props.stockItemUuid);
41
- }, [props.stockItemUuid, setStockItemUuid]);
42
-
43
- const stockItemBatchesInfo = stockItemBatchNos?.map((item) => {
44
- const matchingBatch = items?.find((batch) => batch.batchNumber === item.batchNo);
45
- if (matchingBatch) {
46
- return {
47
- ...item,
48
- quantity: matchingBatch.quantity ?? '',
49
- };
50
- }
51
- return item;
52
- });
53
-
54
- const filteredBatches = stockItemBatchesInfo?.filter((s) => s.quantity !== undefined && s.quantity !== 0);
55
-
56
- useEffect(() => {
57
- if (
58
- !isLoading &&
59
- stockItemBatchNos &&
60
- props.selectedItem &&
61
- (stockItemBatchNos.length === 0 || filteredBatches.length === 0)
62
- ) {
63
- setValidationMessage('No stock batch numbers defined. Do a initial/receipt stock operation first.');
64
- } else {
65
- setValidationMessage(null);
66
- }
67
- }, [isLoading, stockItemBatchNos, props.selectedItem, filteredBatches]);
68
-
69
- if (isLoading) return <InlineLoading status="active" data-testid="loading" />;
70
-
71
- return (
72
- <div style={{ display: 'flex', flexDirection: 'column' }}>
73
- <Controller
74
- name={props.controllerName}
75
- control={props.control}
76
- render={({ field: { onChange, ref } }) => (
77
- <ComboBox
78
- style={{ flexGrow: '1' }}
79
- titleText={props.title}
80
- name={props.name}
81
- control={props.control}
82
- controllerName={props.controllerName}
83
- id={props.name}
84
- size={'sm'}
85
- items={filteredBatches || []}
86
- onChange={(data: { selectedItem?: StockBatchDTO }) => {
87
- setSelectedItem(data.selectedItem || null);
88
- props.onBatchNoChanged?.(data.selectedItem);
89
- onChange(data.selectedItem?.uuid);
90
- }}
91
- initialSelectedItem={initialSelectedItem}
92
- itemToString={(s: StockBatchDTO) =>
93
- s?.batchNo
94
- ? `${s?.batchNo} | Qty: ${s?.quantity ?? 'Unknown'} | Expiry: ${formatForDatePicker(s.expiration)}`
95
- : ''
96
- }
97
- placeholder={props.placeholder}
98
- invalid={props.invalid}
99
- invalidText={props.invalidText}
100
- ref={ref}
101
- />
102
- )}
103
- />
104
- {isLoading && <InlineLoading status="active" />}
105
- {validationMessage && (
106
- <div data-testid="validation-message" style={{ color: 'red', marginTop: '8px' }}>
107
- {t(validationMessage)}
108
- </div>
109
- )}
110
- </div>
111
- );
112
- };
113
-
114
- export default BatchNoSelector;
@@ -1,101 +0,0 @@
1
- import React from 'react';
2
- import { render, screen } from '@testing-library/react';
3
- import { useForm, FormProvider } from 'react-hook-form';
4
- import { useStockItemBatchNos } from './batch-no-selector.resource';
5
- import { useStockItemBatchInformationHook } from '../../stock-items/add-stock-item/batch-information/batch-information.resource';
6
- import BatchNoSelector from './batch-no-selector.component';
7
- import { StockBatchDTO } from '../../core/api/types/stockItem/StockBatchDTO';
8
- import userEvent from '@testing-library/user-event';
9
- import { waitFor } from '@testing-library/react';
10
-
11
- // Mock hooks
12
- jest.mock('./batch-no-selector.resource');
13
- jest.mock('../../stock-items/add-stock-item/batch-information/batch-information.resource');
14
- jest.mock('react-i18next', () => ({
15
- useTranslation: () => ({ t: (key: string) => key }),
16
- }));
17
-
18
- const mockUseStockItemBatchNos = useStockItemBatchNos as jest.Mock;
19
- const mockUseStockItemBatchInformationHook = useStockItemBatchInformationHook as jest.Mock;
20
-
21
- describe('BatchNoSelector Component', () => {
22
- const stockItemUuid = 'test-uuid';
23
- const batchUuid = 'batch-uuid';
24
- const mockStockItemBatchNos: StockBatchDTO[] = [
25
- { uuid: '1', batchNo: 'batch1', quantity: '10', expiration: new Date(), stockItemUuid: '', voided: false },
26
- { uuid: '2', batchNo: 'batch2', quantity: '20', expiration: new Date(), stockItemUuid: '', voided: false },
27
- ];
28
- const mockBatchInformation = [
29
- { batchNumber: 'batch1', quantity: '10' },
30
- { batchNumber: 'batch2', quantity: '20' },
31
- ];
32
-
33
- beforeEach(() => {
34
- mockUseStockItemBatchNos.mockReturnValue({
35
- isLoading: false,
36
- stockItemBatchNos: mockStockItemBatchNos,
37
- });
38
- mockUseStockItemBatchInformationHook.mockReturnValue({
39
- items: mockBatchInformation,
40
- setStockItemUuid: jest.fn(),
41
- });
42
- });
43
-
44
- const renderComponent = () => {
45
- const Wrapper = () => {
46
- const methods = useForm();
47
- return (
48
- <FormProvider {...methods}>
49
- <BatchNoSelector
50
- stockItemUuid={stockItemUuid}
51
- batchUuid={batchUuid}
52
- controllerName="batchNo"
53
- name="batchNo"
54
- control={methods.control}
55
- />
56
- </FormProvider>
57
- );
58
- };
59
- render(<Wrapper />);
60
- };
61
-
62
- test('should render without crashing', () => {
63
- renderComponent();
64
- expect(screen.getByRole('combobox')).toBeInTheDocument();
65
- });
66
-
67
- test('should display loading state when isLoading is true', () => {
68
- mockUseStockItemBatchNos.mockReturnValueOnce({
69
- isLoading: true,
70
- stockItemBatchNos: [],
71
- });
72
- renderComponent();
73
- expect(screen.getByTestId('loading')).toBeInTheDocument();
74
- });
75
-
76
- test('should call onBatchNoChanged when a batch is selected', async () => {
77
- const onBatchNoChanged = jest.fn();
78
- const Wrapper = () => {
79
- const methods = useForm();
80
- return (
81
- <FormProvider {...methods}>
82
- <BatchNoSelector
83
- stockItemUuid={stockItemUuid}
84
- batchUuid={batchUuid}
85
- controllerName="batchNo"
86
- name="batchNo"
87
- control={methods.control}
88
- onBatchNoChanged={onBatchNoChanged}
89
- />
90
- </FormProvider>
91
- );
92
- };
93
- render(<Wrapper />);
94
-
95
- const combobox = screen.getByRole('combobox');
96
- await userEvent.click(combobox);
97
- await userEvent.click(screen.getByText((content) => content.includes('batch1')));
98
-
99
- expect(onBatchNoChanged).toHaveBeenCalledWith(mockStockItemBatchNos[0]);
100
- });
101
- });