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-303 UIREC-304 Implementation of the "Claim send" and "Claim delay" actions #477

Merged
merged 5 commits into from
Jan 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
* View piece status change log. Refs UIREC-305.
* Add CSV export options for new piece statuses. Refs UIREC-306.
* Add the "Acquisition units" protected field to the receiving title form. Refs UIREC-295.
* Delay claim action for piece record. Refs UIREC-303.
* Send claim action for piece record. Refs UIREC-304.

## [4.0.0](https://github.com/folio-org/ui-receiving/tree/v4.0.0) (2023-10-12)
[Full Changelog](https://github.com/folio-org/ui-receiving/compare/v3.0.0...v4.0.0)
Expand Down
62 changes: 61 additions & 1 deletion src/TitleDetails/AddPieceModal/AddPieceModal.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,12 +49,15 @@ import {
LineLocationsView,
} from '../../common/components';
import { HOLDINGS_API } from '../../common/constants';
import { getClaimingIntervalFromDate } from '../../common/utils';
import {
PIECE_MODAL_ACCORDION,
PIECE_MODAL_ACCORDION_LABELS,
} from '../constants';
import { DelayClaimModal } from '../DelayClaimModal';
import { DeletePieceModal } from '../DeletePieceModal';
import { DeleteHoldingsModal } from '../DeleteHoldingsModal';
import { SendClaimModal } from '../SendClaimModal';
import { ModalActionButtons } from './ModalActionButtons';
import { ReceivingStatusChangeLog } from './ReceivingStatusChangeLog';

Expand All @@ -63,7 +66,7 @@ const AddPieceModal = ({
createInventoryValues,
deletePiece,
canDeletePiece,
form: { mutators, change, getState },
form,
initialValues,
handleSubmit,
hasValidationErrors,
Expand All @@ -77,10 +80,18 @@ const AddPieceModal = ({
poLine,
getHoldingsItemsAndPieces,
}) => {
const {
batch,
change,
getState,
mutators,
} = form;
const {
enumeration,
externalNote,
format,
id,
internalNote,
itemId,
isCreateAnother,
metadata,
Expand All @@ -90,8 +101,11 @@ const AddPieceModal = ({
const isLocationRequired = includes(createInventoryValues[format], INVENTORY_RECORDS_TYPE.instanceAndHolding);
const isNotReceived = receivingStatus !== PIECE_STATUS.received;
const labelId = id ? 'ui-receiving.piece.addPieceModal.editTitle' : 'ui-receiving.piece.addPieceModal.title';

const [isDeleteConfirmation, toggleDeleteConfirmation] = useModalToggle();
const [isDeleteHoldingsConfirmation, toggleDeleteHoldingsConfirmation] = useModalToggle();
const [isClaimDelayModalOpen, toggleClaimDelayModal] = useModalToggle();
const [isClaimSendModalOpen, toggleClaimSendModal] = useModalToggle();

const stripes = useStripes();
const ky = useOkapiKy();
Expand Down Expand Up @@ -169,6 +183,19 @@ const AddPieceModal = ({
onSave();
}, [change, onSave]);

const onClaimDelay = useCallback(({ claimingDate }) => {
change('claimingInterval', getClaimingIntervalFromDate(claimingDate));
onStatusChange(PIECE_STATUS.claimDelayed);
}, [change, onStatusChange]);

const onClaimSend = useCallback(({ claimingDate, ...rest }) => {
batch(() => {
change('claimingInterval', getClaimingIntervalFromDate(claimingDate));
Object.entries(rest).forEach(([field, value]) => change(field, value));
});
onStatusChange(PIECE_STATUS.claimSent);
}, [batch, change, onStatusChange]);

const start = (
<Button
data-test-add-piece-cancel
Expand All @@ -185,6 +212,8 @@ const AddPieceModal = ({
isCreateAnother={isCreateAnother}
isEditMode={Boolean(id)}
onCreateAnotherPiece={onCreateAnotherPiece}
onClaimDelay={toggleClaimDelayModal}
onClaimSend={toggleClaimSendModal}
onDelete={toggleDeleteConfirmation}
onReceive={onReceive}
onSave={onSave}
Expand Down Expand Up @@ -334,6 +363,24 @@ const AddPieceModal = ({
/>
</Col>
</Row>
<Row>
<Col xs>
<Field
component={TextArea}
fullWidth
label={<FormattedMessage id="ui-receiving.piece.internalNote" />}
name="internalNote"
/>
</Col>
<Col xs>
<Field
component={TextArea}
fullWidth
label={<FormattedMessage id="ui-receiving.piece.externalNote" />}
name="externalNote"
/>
</Col>
</Row>
<Row>
<Col xs={6}>
<LineLocationsView
Expand Down Expand Up @@ -449,6 +496,19 @@ const AddPieceModal = ({
/>
)
}

<DelayClaimModal
open={isClaimDelayModalOpen}
onCancel={toggleClaimDelayModal}
onSubmit={onClaimDelay}
/>

<SendClaimModal
open={isClaimSendModalOpen}
onCancel={toggleClaimSendModal}
onSubmit={onClaimSend}
initialValues={{ internalNote, externalNote }}
/>
</Modal>
);
};
Expand Down
55 changes: 48 additions & 7 deletions src/TitleDetails/AddPieceModal/AddPieceModal.test.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import moment from 'moment';
import { MemoryRouter } from 'react-router-dom';

import user from '@folio/jest-config-stripes/testing-library/user-event';
Expand Down Expand Up @@ -81,6 +82,9 @@ const kyMock = {
})),
};

const DATE_FORMAT = 'MM/DD/YYYY';
const today = moment();

const renderAddPieceModal = (props = {}) => render(
<AddPieceModal
{...defaultProps}
Expand Down Expand Up @@ -130,7 +134,6 @@ describe('AddPieceModal', () => {
const format = PIECE_FORMAT.electronic;

renderAddPieceModal({
...defaultProps,
createInventoryValues: { [format]: INVENTORY_RECORDS_TYPE.instanceAndHolding },
initialValues: {
format,
Expand All @@ -144,7 +147,6 @@ describe('AddPieceModal', () => {

it('should not be visible when create inventory does not include holding', () => {
renderAddPieceModal({
...defaultProps,
initialValues: {
format: INVENTORY_RECORDS_TYPE.instance,
},
Expand All @@ -159,7 +161,6 @@ describe('AddPieceModal', () => {
const format = PIECE_FORMAT.electronic;

renderAddPieceModal({
...defaultProps,
createInventoryValues: { [format]: INVENTORY_RECORDS_TYPE.instanceAndHolding },
initialValues: {
format,
Expand All @@ -180,7 +181,6 @@ describe('AddPieceModal', () => {
});

renderAddPieceModal({
...defaultProps,
initialValues: {
id: 'pieceId',
format: PIECE_FORMAT.physical,
Expand All @@ -199,7 +199,6 @@ describe('AddPieceModal', () => {
kyMock.get.mockReturnValue(({ json: () => Promise.reject(new Error('404')) }));

renderAddPieceModal({
...defaultProps,
initialValues: {
id: 'pieceId',
format: PIECE_FORMAT.physical,
Expand All @@ -219,7 +218,6 @@ describe('AddPieceModal', () => {
describe('Create another piece', () => {
it('should update footer button when \'Create another\' is active', async () => {
renderAddPieceModal({
...defaultProps,
initialValues: {
isCreateAnother: true,
receivingStatus: PIECE_STATUS.expected,
Expand All @@ -236,7 +234,6 @@ describe('AddPieceModal', () => {
const onChange = jest.fn();

renderAddPieceModal({
...defaultProps,
form: {
...defaultProps.form,
change: onChange,
Expand Down Expand Up @@ -264,4 +261,48 @@ describe('AddPieceModal', () => {

expect(defaultProps.onSubmit).toHaveBeenCalled();
});

describe('Actions', () => {
const initialValues = {
format: PIECE_FORMAT.other,
holdingId: '60c67dc5-b646-425e-bf08-a8bf2d0681fb',
};
const date = today.add(3, 'days');

beforeEach(async () => {
renderAddPieceModal({ initialValues });

await user.click(screen.getByTestId('dropdown-trigger-button'));
});

it('should handle "Delay claim" action', async () => {
await user.click(screen.getByTestId('delay-claim-button'));
await user.type(screen.getByRole('textbox', { name: 'ui-receiving.modal.delayClaim.field.delayTo' }), date.format(DATE_FORMAT));
await user.click(await findButton('stripes-acq-components.FormFooter.save'));

expect(defaultProps.onSubmit).toHaveBeenCalledWith(
expect.objectContaining({
claimingInterval: 3,
receivingStatus: PIECE_STATUS.claimDelayed,
}),
expect.anything(),
expect.anything(),
);
});

it('should handle "Send claim" action', async () => {
await user.click(screen.getByTestId('send-claim-button'));
await user.type(screen.getByRole('textbox', { name: 'ui-receiving.modal.sendClaim.field.claimExpiryDate' }), date.format(DATE_FORMAT));
await user.click(await findButton('stripes-acq-components.FormFooter.save'));

expect(defaultProps.onSubmit).toHaveBeenCalledWith(
expect.objectContaining({
claimingInterval: 3,
receivingStatus: PIECE_STATUS.claimSent,
}),
expect.anything(),
expect.anything(),
);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ export const ModalActionButtons = ({
canDeletePiece,
disabled,
isEditMode,
onClaimDelay,
onClaimSend,
onCreateAnotherPiece,
onDelete,
onReceive,
Expand All @@ -28,6 +30,8 @@ export const ModalActionButtons = ({
canDeletePiece,
disabled,
isEditMode,
onClaimDelay,
onClaimSend,
onCreateAnotherPiece,
onDelete,
onReceive,
Expand Down Expand Up @@ -82,11 +86,13 @@ ModalActionButtons.propTypes = {
canDeletePiece: PropTypes.bool,
disabled: PropTypes.bool,
isEditMode: PropTypes.bool.isRequired,
onCreateAnotherPiece: PropTypes.func,
onDelete: PropTypes.func,
onReceive: PropTypes.func,
onClaimDelay: PropTypes.func.isRequired,
onClaimSend: PropTypes.func.isRequired,
onCreateAnotherPiece: PropTypes.func.isRequired,
onDelete: PropTypes.func.isRequired,
onReceive: PropTypes.func.isRequired,
onSave: PropTypes.func.isRequired,
onStatusChange: PropTypes.func,
onStatusChange: PropTypes.func.isRequired,
status: PropTypes.string,
};

Expand Down
30 changes: 19 additions & 11 deletions src/TitleDetails/AddPieceModal/ModalActionButtons/constants.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { noop } from 'lodash';
import { FormattedMessage } from 'react-intl';

import {
Expand All @@ -18,15 +17,20 @@ export const PIECE_ACTION_NAMES = {
delete: 'delete',
};

export const EXPECTED_PIECES_ACTIONS = [
PIECE_ACTION_NAMES.saveAndCreate,
PIECE_ACTION_NAMES.quickReceive,
PIECE_ACTION_NAMES.sendClaim,
PIECE_ACTION_NAMES.delayClaim,
PIECE_ACTION_NAMES.unReceivable,
PIECE_ACTION_NAMES.delete,
];

export const PIECE_ACTIONS_BY_STATUS = {
[PIECE_STATUS.expected]: [
PIECE_ACTION_NAMES.saveAndCreate,
PIECE_ACTION_NAMES.quickReceive,
PIECE_ACTION_NAMES.sendClaim,
PIECE_ACTION_NAMES.delayClaim,
PIECE_ACTION_NAMES.unReceivable,
PIECE_ACTION_NAMES.delete,
],
[PIECE_STATUS.expected]: EXPECTED_PIECES_ACTIONS,
[PIECE_STATUS.claimDelayed]: EXPECTED_PIECES_ACTIONS,
[PIECE_STATUS.claimSent]: EXPECTED_PIECES_ACTIONS,
[PIECE_STATUS.late]: EXPECTED_PIECES_ACTIONS,
[PIECE_STATUS.received]: [
PIECE_ACTION_NAMES.saveAndCreate,
PIECE_ACTION_NAMES.unReceive,
Expand All @@ -43,6 +47,8 @@ export const PIECE_ACTIONS = ({
canDeletePiece,
disabled,
isEditMode,
onClaimDelay,
onClaimSend,
onCreateAnotherPiece,
onStatusChange,
onDelete,
Expand All @@ -52,7 +58,8 @@ export const PIECE_ACTIONS = ({
<Button
disabled={disabled}
buttonStyle="dropdownItem"
onClick={noop} // TODO UIOR-1160
data-testid="delay-claim-button"
onClick={onClaimDelay}
>
<Icon icon="calendar">
<FormattedMessage id="ui-receiving.piece.action.button.delayClaim" />
Expand Down Expand Up @@ -111,7 +118,8 @@ export const PIECE_ACTIONS = ({
<Button
disabled={disabled}
buttonStyle="dropdownItem"
onClick={noop} // TODO UIOR-1152
data-testid="send-claim-button"
onClick={onClaimSend}
>
<Icon icon="envelope">
<FormattedMessage id="ui-receiving.piece.action.button.sendClaim" />
Expand Down
Loading
Loading