Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

UIREC-292: fix update item status on unreceive piece #484

Merged
merged 12 commits into from
Jan 30, 2024
24 changes: 19 additions & 5 deletions src/TitleDetails/AddPieceModal/AddPieceModal.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,23 +51,24 @@ import { PieceFields } from './PieceFields';
import { ReceivingStatusChangeLog } from './ReceivingStatusChangeLog';

const AddPieceModal = ({
canDeletePiece,
close,
createInventoryValues,
deletePiece,
canDeletePiece,
form,
initialValues,
getHoldingsItemsAndPieces,
handleSubmit,
hasValidationErrors,
pristine,
initialValues,
instanceId,
locationIds,
locations,
onCheckIn,
onUnreceive,
pieceFormatOptions,
values: formValues,
poLine,
getHoldingsItemsAndPieces,
pristine,
values: formValues,
}) => {
const {
batch,
Expand Down Expand Up @@ -179,6 +180,17 @@ const AddPieceModal = ({
onSave();
}, [change, onSave]);

const onUnreceivePiece = useCallback(async () => {
const currentPiece = {
...formValues,
checked: true,
};

await onUnreceive([currentPiece]);
close();
},
[close, onUnreceive, formValues]);

const onClaimDelay = useCallback(({ claimingDate }) => {
change('claimingInterval', getClaimingIntervalFromDate(claimingDate));
onStatusChange(PIECE_STATUS.claimDelayed);
Expand Down Expand Up @@ -219,6 +231,7 @@ const AddPieceModal = ({
onClaimSend={toggleClaimSendModal}
onDelete={toggleDeleteConfirmation}
onReceive={onReceive}
onUnreceivePiece={onUnreceivePiece}
onSave={onSave}
onStatusChange={onStatusChange}
status={receivingStatus}
Expand Down Expand Up @@ -369,6 +382,7 @@ AddPieceModal.propTypes = {
deletePiece: PropTypes.func.isRequired,
canDeletePiece: PropTypes.bool,
handleSubmit: PropTypes.func.isRequired,
onUnreceive: PropTypes.func.isRequired,
form: PropTypes.object,
values: PropTypes.object.isRequired,
instanceId: PropTypes.string,
Expand Down
32 changes: 32 additions & 0 deletions src/TitleDetails/AddPieceModal/AddPieceModal.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ jest.mock('@folio/stripes-acq-components', () => {
});
jest.mock('../../common/components/LineLocationsView/LineLocationsView',
() => jest.fn().mockReturnValue('LineLocationsView'));
jest.mock('../../common/hooks', () => ({
...jest.requireActual('../../common/hooks'),
useUnreceive: jest.fn().mockReturnValue({ unreceive: jest.fn(() => Promise.resolve()) }),
}));
jest.mock('../hooks', () => ({
...jest.requireActual('../hooks'),
usePieceStatusChangeLog: jest.fn(),
Expand Down Expand Up @@ -262,6 +266,34 @@ describe('AddPieceModal', () => {
expect(defaultProps.onSubmit).toHaveBeenCalled();
});

it('should unreceive piece', async () => {
const onUnreceive = jest.fn();

renderAddPieceModal({
onUnreceive,
hasValidationErrors: false,
initialValues: {
'id': 'cd3fd1e7-c195-4d8e-af75-525e1039d643',
'format': 'Other',
'poLineId': 'a92ae36c-e093-4daf-b234-b4c6dc33a258',
'titleId': '03329fea-1b5d-43ab-b955-20bcd9ba530d',
'holdingId': '60c67dc5-b646-425e-bf08-a8bf2d0681fb',
'isCreateAnother': false,
'isCreateItem': false,
receivingStatus: PIECE_STATUS.received,
receivedDate: new Date().toISOString(),
},
});

await user.click(screen.getByTestId('dropdown-trigger-button'));
const unReceiveButton = await screen.findByTestId('unReceive-piece-button');

expect(unReceiveButton).toBeInTheDocument();
await user.click(unReceiveButton);

expect(onUnreceive).toHaveBeenCalled();
});

describe('Actions', () => {
const initialValues = {
format: PIECE_FORMAT.other,
Expand Down
3 changes: 3 additions & 0 deletions src/TitleDetails/AddPieceModal/AddPieceModalContainer.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ const AddPieceModalContainer = ({
close,
deletePiece,
canDeletePiece,
onUnreceive,
initialValues,
instanceId,
locations,
Expand Down Expand Up @@ -70,6 +71,7 @@ const AddPieceModalContainer = ({
onCheckIn={onQuickReceive}
onSubmit={onSavePiece}
pieceFormatOptions={pieceFormatOptions}
onUnreceive={onUnreceive}
poLine={poLine}
getHoldingsItemsAndPieces={getHoldingsItemsAndPieces}
/>
Expand All @@ -88,6 +90,7 @@ AddPieceModalContainer.propTypes = {
onSubmit: PropTypes.func.isRequired,
poLine: PropTypes.object.isRequired,
getHoldingsItemsAndPieces: PropTypes.func.isRequired,
onUnreceive: PropTypes.func.isRequired,
};

export default AddPieceModalContainer;
4 changes: 4 additions & 0 deletions src/TitleDetails/AddPieceModal/AddPieceModalContainer.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ jest.mock('@folio/stripes-acq-components', () => ({
}));
jest.mock('../../common/components/LineLocationsView/LineLocationsView',
() => jest.fn().mockReturnValue('LineLocationsView'));
jest.mock('../../common/hooks', () => ({
...jest.requireActual('../../common/hooks'),
useUnreceive: jest.fn().mockReturnValue({ unreceive: jest.fn(() => Promise.resolve()) }),
}));
jest.mock('../hooks', () => ({
...jest.requireActual('../hooks'),
usePieceStatusChangeLog: jest.fn(() => ({ data: [] })),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export const ModalActionButtons = ({
onReceive,
onSave,
onStatusChange,
onUnreceivePiece,
status,
}) => {
const actionMenu = getPieceActionMenu({
Expand All @@ -37,6 +38,7 @@ export const ModalActionButtons = ({
onDelete,
onReceive,
onStatusChange,
onUnreceivePiece,
status,
});
const saveButtonLabelId = 'ui-receiving.piece.actions.saveAndClose';
Expand Down Expand Up @@ -95,6 +97,7 @@ ModalActionButtons.propTypes = {
onReceive: PropTypes.func.isRequired,
onSave: PropTypes.func.isRequired,
onStatusChange: PropTypes.func.isRequired,
onUnreceivePiece: PropTypes.func.isRequired,
status: PropTypes.string,
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ export const PIECE_ACTIONS = ({
onStatusChange,
onDelete,
onReceive,
onUnreceivePiece,
}) => ({
delayClaim: (
<Button
Expand Down Expand Up @@ -131,7 +132,7 @@ export const PIECE_ACTIONS = ({
disabled={actionsDisabled[PIECE_ACTION_NAMES.unReceive]}
buttonStyle="dropdownItem"
data-testid="unReceive-piece-button"
onClick={() => onStatusChange(PIECE_STATUS.expected)}
onClick={onUnreceivePiece}
>
<Icon icon="cancel">
<FormattedMessage id="ui-receiving.piece.action.button.unReceive" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,14 +59,14 @@ describe('getPieceActionMenus', () => {
});

describe('unReceive action', () => {
it('should `onStatusChange` be called with `Expected` status value', () => {
const onStatusChange = jest.fn();
const result = getPieceActionMenu({ status: received, onStatusChange });
it('should `onUnreceivePiece` be called with `Expected` status value', () => {
const onUnreceivePiece = jest.fn();
const result = getPieceActionMenu({ status: received, onUnreceivePiece });
const receiveButton = result.find(i => i.props['data-testid'] === 'unReceive-piece-button');

receiveButton.props.onClick();

expect(onStatusChange).toHaveBeenCalledWith(PIECE_STATUS.expected);
expect(onUnreceivePiece).toHaveBeenCalledWith();
});
});

Expand Down
3 changes: 3 additions & 0 deletions src/TitleDetails/TitleDetails.js
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ const TitleDetails = ({
piecesExistance,
poLine,
title,
onUnreceive,
vendorsMap,
getHoldingsItemsAndPieces,
getPieceValues,
Expand Down Expand Up @@ -601,6 +602,7 @@ const TitleDetails = ({
onCheckIn={onQuickReceive}
onSubmit={onSave}
poLine={poLine}
onUnreceive={onUnreceive}
getHoldingsItemsAndPieces={getHoldingsItemsAndPieces}
/>
)}
Expand Down Expand Up @@ -638,6 +640,7 @@ TitleDetails.propTypes = {
vendorsMap: PropTypes.object.isRequired,
getHoldingsItemsAndPieces: PropTypes.func.isRequired,
getPieceValues: PropTypes.func.isRequired,
onUnreceive: PropTypes.func.isRequired,
};

export default withRouter(TitleDetails);
24 changes: 21 additions & 3 deletions src/TitleDetails/TitleDetailsContainer.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,11 @@
PIECE_STATUS,
} from '@folio/stripes-acq-components';

import {
titleResource,
} from '../common/resources';
import { titleResource } from '../common/resources';
import {
usePieceMutator,
useQuickReceive,
useUnreceive,
} from '../common/hooks';
import {
handleCommonErrors,
Expand All @@ -48,6 +47,7 @@

const { mutatePiece } = usePieceMutator();
const { quickReceive } = useQuickReceive();
const { unreceive } = useUnreceive();

const hasPieces = useCallback((lineId, status) => (
mutator.pieces.GET({
Expand Down Expand Up @@ -106,7 +106,7 @@
items: itemsInHolding,
}))
.catch(() => ({}));
}, []);

Check warning on line 109 in src/TitleDetails/TitleDetailsContainer.js

View workflow job for this annotation

GitHub Actions / github-actions-ci

React Hook useCallback has missing dependencies: 'mutator.items' and 'mutator.pieces'. Either include them or remove the dependency array

Check warning on line 109 in src/TitleDetails/TitleDetailsContainer.js

View workflow job for this annotation

GitHub Actions / github-actions-ci

React Hook useCallback has missing dependencies: 'mutator.items' and 'mutator.pieces'. Either include them or remove the dependency array

useEffect(
() => {
Expand Down Expand Up @@ -269,6 +269,23 @@
).finally(() => fetchReceivingResources(poLine.id));
}, [fetchReceivingResources, poLine.id, showCallout, mutatePiece]);

const onUnreceive = useCallback((pieces) => {
return unreceive(pieces)
.then(async () => {
await fetchReceivingResources(poLine.id);
showCallout({
messageId: 'ui-receiving.title.actions.unreceive.success',
type: 'success',
});
})
.catch(() => {
showCallout({
type: 'error',
messageId: 'ui-receiving.title.actions.unreceive.error',
});
});
}, [fetchReceivingResources, poLine.id, showCallout, unreceive]);

if (isLoading || !(locations || vendorsMap)) {
return (
<LoadingPane
Expand All @@ -293,6 +310,7 @@
vendorsMap={vendorsMap}
getHoldingsItemsAndPieces={getHoldingsItemsAndPieces}
getPieceValues={getPieceById(mutator.orderPieces)}
onUnreceive={onUnreceive}
/>
);
};
Expand Down
16 changes: 16 additions & 0 deletions src/TitleDetails/TitleDetailsContainer.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,15 @@ import { QueryClient, QueryClientProvider } from 'react-query';
import {
usePieceMutator,
useQuickReceive,
useUnreceive,
} from '../common/hooks';
import TitleDetails from './TitleDetails';
import TitleDetailsContainer from './TitleDetailsContainer';

jest.mock('../common/hooks', () => ({
usePieceMutator: jest.fn().mockReturnValue({}),
useQuickReceive: jest.fn().mockReturnValue({}),
useUnreceive: jest.fn().mockReturnValue({ unreceive: Promise.resolve() }),
}));
jest.mock('./TitleDetails', () => jest.fn().mockReturnValue('TitleDetails'));

Expand Down Expand Up @@ -127,6 +129,20 @@ describe('TitleDetailsContainer', () => {
expect(quickReceiveMock).toHaveBeenCalled();
});

it('should receive piece when onUnreceive is called', async () => {
const onUnreceive = jest.fn().mockReturnValue(Promise.resolve());

useUnreceive.mockClear().mockReturnValue({ unreceive: onUnreceive });

await act(async () => {
renderTitleDetailsContainer();
});

await TitleDetails.mock.calls[0][0].onUnreceive(pieces[0]);

expect(onUnreceive).toHaveBeenCalled();
});

it('should fetch items and pieces in holding', async () => {
await act(async () => {
renderTitleDetailsContainer();
Expand Down
1 change: 1 addition & 0 deletions src/common/hooks/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ export * from './usePiecesExpect';
export * from './useQuickReceive';
export * from './useReceive';
export * from './useTitle';
export * from './useUnreceive';
24 changes: 24 additions & 0 deletions src/common/hooks/useUnreceive.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { useMutation } from 'react-query';

import { useOkapiKy } from '@folio/stripes/core';

import { RECEIVE_API } from '../constants';
import { unreceivePieces } from '../utils';

export const useUnreceive = (options = {}) => {
const ky = useOkapiKy();

const mutator = {
POST: (pieces) => ky.post(RECEIVE_API, { json: pieces })
.then(response => response.json()),
};

const { mutateAsync } = useMutation({
mutationFn: (pieces) => unreceivePieces(pieces, mutator),
...options,
});

return {
unreceive: mutateAsync,
};
};
39 changes: 39 additions & 0 deletions src/common/hooks/useUnreceive.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import React from 'react';
import { QueryClient, QueryClientProvider } from 'react-query';

import { renderHook } from '@folio/jest-config-stripes/testing-library/react';
import { useOkapiKy } from '@folio/stripes/core';

import { useUnreceive } from './useUnreceive';

const queryClient = new QueryClient();
const pieceValues = {
id: 'pieceId',
holdingId: 'holdingId',
caption: 'v1',
};

const wrapper = ({ children }) => (
<QueryClientProvider client={queryClient}>
{children}
</QueryClientProvider>
);

describe('useUnreceive', () => {
it('should make post request to unreceive pieces', async () => {
const postMock = jest.fn().mockReturnValue(Promise.resolve({ json: () => ({ receivingResults: [] }) }));

useOkapiKy.mockClear().mockReturnValue({
post: postMock,
});

const { result } = renderHook(
() => useUnreceive(),
{ wrapper },
);

await result.current.unreceive([pieceValues]);

expect(postMock).toHaveBeenCalled();
});
});
Loading