From bb6e05ca9b1c7d0a38bd516195c62bd9f7839bf2 Mon Sep 17 00:00:00 2001 From: Alisher Musurmonov Date: Thu, 17 Oct 2024 01:14:14 +0500 Subject: [PATCH] UIREC-411: ECS - fix user with no affiliation to the location in POL can not add pieces to the receiving title related to that POL (#598) * UIOR-1324: ECS - fix user with no affiliation to the location in POL can not add pieces to the receiving title related to that POL * test: fix failing tests * refactor: useHoldingsAndLocations options using custom `useReceivingTenantIdsAndLocations` hook * update changelog * test: fix failing tests * UIREC-407: upgrade `holdings-storage` to 8.0 * UIREC-370: Disable pieces dropdown button when "Save and close" button is disabled * fix includes not found issue --- CHANGELOG.md | 3 ++ package.json | 2 +- src/Piece/PieceForm/PieceForm.js | 33 ++++++-------- src/Piece/PieceForm/PieceForm.test.js | 28 +++++++++--- .../PieceFormActionButtons.js | 1 + .../PieceForm/PieceFormContainer.test.js | 16 +++++++ .../TitleBindPiecesCreateItemForm.js | 17 ++++++-- src/TitleReceive/TitleReceiveContainer.js | 16 ++++--- .../TitleReceiveContainer.test.js | 1 + .../LineLocationsView/LineLocationsView.js | 6 +-- src/common/hooks/index.js | 1 + .../useReceivingTenantIdsAndLocations.js | 43 +++++++++++++++++++ 12 files changed, 125 insertions(+), 42 deletions(-) create mode 100644 src/common/hooks/useReceivingTenantIdsAndLocations.js diff --git a/CHANGELOG.md b/CHANGELOG.md index 2c7a1153..b13dff38 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -41,6 +41,9 @@ * Action menu should be closed after 'Save and create another' action. Refs UIREC-399. * Update `consortium-search` interface. Refs UIREC-405. * cleanup permissions for ui-receiving. Refs UIREC-410. +* ECS - fix user with no affiliation to the location in POL can not add pieces to the receiving title related to that POL. Refs UIREC-411. +* Upgrade `holdings-storage` to 8.0. Refs UIREC-407. +* Disable pieces dropdown button when "Save and close" button is disabled. UIREC-370. ## [5.0.5](https://github.com/folio-org/ui-receiving/tree/v5.0.5) (2024-08-05) [Full Changelog](https://github.com/folio-org/ui-receiving/compare/v5.0.4...v5.0.5) diff --git a/package.json b/package.json index 05c59ab0..fafccede 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,7 @@ "circulation": "9.5 10.0 11.0 12.0 13.0 14.0", "configuration": "2.0", "contributor-types": "2.0", - "holdings-storage": "4.2 5.0 6.0 7.0", + "holdings-storage": "4.2 5.0 6.0 7.0 8.0", "identifier-types": "1.2", "instance-formats": "2.0", "instance-types": "2.0", diff --git a/src/Piece/PieceForm/PieceForm.js b/src/Piece/PieceForm/PieceForm.js index d3d78e39..9804b997 100644 --- a/src/Piece/PieceForm/PieceForm.js +++ b/src/Piece/PieceForm/PieceForm.js @@ -1,4 +1,3 @@ -import uniq from 'lodash/uniq'; import PropTypes from 'prop-types'; import { useCallback, @@ -34,7 +33,10 @@ import { useModalToggle, } from '@folio/stripes-acq-components'; -import { useHoldingsAndLocations } from '../../common/hooks'; +import { + useHoldingsAndLocations, + useReceivingTenantIdsAndLocations, +} from '../../common/hooks'; import { getClaimingIntervalFromDate, setLocationValueFormMutator, @@ -100,26 +102,19 @@ const PieceForm = ({ receivingTenantId, } = formValues; - const receivingTenantIds = useMemo(() => { + const receivingTenants = useMemo(() => { if (poLine?.locations?.length) { - return uniq([ - ...poLine.locations.map(({ tenantId }) => tenantId), - receivingTenantId, - ].filter(Boolean)); + return poLine.locations.map(({ tenantId }) => tenantId); } return []; - }, [receivingTenantId, poLine?.locations]); - - const additionalLocations = useMemo(() => { - const locationIds = locationId ? [locationId] : []; - const tenantLocationIdsMap = receivingTenantId ? { [receivingTenantId]: locationIds } : {}; + }, [poLine?.locations]); - return { - additionalLocationIds: locationIds, - additionalTenantLocationIdsMap: tenantLocationIdsMap, - }; - }, [locationId, receivingTenantId]); + const receivingTenantIdsAndLocations = useReceivingTenantIdsAndLocations({ + receivingTenantIds: receivingTenants, + currentReceivingTenantId: receivingTenantId, + currentLocationId: locationId, + }); const { locations, @@ -127,9 +122,7 @@ const PieceForm = ({ isFetching, } = useHoldingsAndLocations({ instanceId, - receivingTenantIds, - tenantId: receivingTenantId, - ...additionalLocations, + ...receivingTenantIdsAndLocations, }); useEffect(() => { diff --git a/src/Piece/PieceForm/PieceForm.test.js b/src/Piece/PieceForm/PieceForm.test.js index a5f7e8b2..f766231c 100644 --- a/src/Piece/PieceForm/PieceForm.test.js +++ b/src/Piece/PieceForm/PieceForm.test.js @@ -11,19 +11,19 @@ import { INVENTORY_RECORDS_TYPE, PIECE_FORMAT, PIECE_STATUS, + useCurrentUserTenants, } from '@folio/stripes-acq-components'; import { renderWithRouter } from '../../../test/jest/helpers'; import { usePieceStatusChangeLog } from '../hooks'; import PieceForm from './PieceForm'; -jest.mock('@folio/stripes-acq-components', () => { - return { - ...jest.requireActual('@folio/stripes-acq-components'), - FieldInventory: jest.fn().mockReturnValue('FieldInventory'), - useCentralOrderingContext: jest.fn(), - }; -}); +jest.mock('@folio/stripes-acq-components', () => ({ + ...jest.requireActual('@folio/stripes-acq-components'), + FieldInventory: jest.fn().mockReturnValue('FieldInventory'), + useCentralOrderingContext: jest.fn(), + useCurrentUserTenants: jest.fn(), +})); jest.mock('../../common/components/LineLocationsView/LineLocationsView', () => jest.fn().mockReturnValue('LineLocationsView')); jest.mock('../hooks', () => ({ ...jest.requireActual('../hooks'), @@ -66,6 +66,16 @@ const userData = { firstName: 'John', }, }; +const tenants = [{ + id: 'tenantId1', + name: 'tenantName1', + isPrimary: true, +}, +{ + id: 'tenantId1', + name: 'tenantName1', + isPrimary: false, +}]; const logs = [ { eventDate: '2023-12-26T14:08:19.402Z', @@ -106,6 +116,10 @@ describe('PieceForm', () => { usePieceStatusChangeLog .mockClear() .mockReturnValue({ data: logs }); + + useCurrentUserTenants + .mockClear() + .mockReturnValue(tenants); }); it('should display the piece form', () => { diff --git a/src/Piece/PieceForm/PieceFormActionButtons/PieceFormActionButtons.js b/src/Piece/PieceForm/PieceFormActionButtons/PieceFormActionButtons.js index e89c1a60..b9bbd5f7 100644 --- a/src/Piece/PieceForm/PieceFormActionButtons/PieceFormActionButtons.js +++ b/src/Piece/PieceForm/PieceFormActionButtons/PieceFormActionButtons.js @@ -79,6 +79,7 @@ export const PieceFormActionButtons = ({ ({ useAcqRestrictions: jest.fn(), useLocationsQuery: jest.fn(), useOrderLine: jest.fn(), + useCurrentUserTenants: jest.fn(), })); jest.mock('../../common/components/LineLocationsView/LineLocationsView', () => jest.fn().mockReturnValue('LineLocationsView')); jest.mock('../../common/hooks', () => ({ @@ -85,6 +87,17 @@ const title = { poLineId: orderLine.id, }; +const tenants = [{ + id: 'tenantId1', + name: 'tenantName1', + isPrimary: true, +}, +{ + id: 'tenantId1', + name: 'tenantName1', + isPrimary: false, +}]; + const restrictions = {}; const defaultProps = { @@ -143,6 +156,9 @@ describe('PieceFormContainer', () => { useUnreceive .mockClear() .mockReturnValue({ unreceive: unreceiveMock }); + useCurrentUserTenants + .mockClear() + .mockReturnValue(tenants); }); it('should display the piece form', () => { diff --git a/src/TitleBindPieces/TitleBindPiecesCreateItemForm/TitleBindPiecesCreateItemForm.js b/src/TitleBindPieces/TitleBindPiecesCreateItemForm/TitleBindPiecesCreateItemForm.js index c3052761..7540574c 100644 --- a/src/TitleBindPieces/TitleBindPiecesCreateItemForm/TitleBindPiecesCreateItemForm.js +++ b/src/TitleBindPieces/TitleBindPiecesCreateItemForm/TitleBindPiecesCreateItemForm.js @@ -18,7 +18,10 @@ import { TextField, } from '@folio/stripes/components'; -import { useHoldingsAndLocations } from '../../common/hooks'; +import { + useHoldingsAndLocations, + useReceivingTenantIdsAndLocations, +} from '../../common/hooks'; import { useReceivingSearchContext } from '../../contexts'; import { PIECE_FORM_FIELD_NAMES } from '../constants'; import { @@ -38,12 +41,18 @@ export const TitleBindPiecesCreateItemForm = ({ const { locationId, tenantId: receivingTenantId } = bindItemValues; - const additionalLocationIds = locationId ? [locationId] : []; - const additionalTenantLocationIdsMap = receivingTenantId ? { [receivingTenantId]: additionalLocationIds } : {}; + const { + additionalLocationIds, + additionalTenantLocationIdsMap, + tenantId, + } = useReceivingTenantIdsAndLocations({ + currentLocationId: locationId, + currentReceivingTenantId: receivingTenantId, + }); const { locations, isFetching } = useHoldingsAndLocations({ instanceId, - tenantId: bindItemValues.tenantId, + tenantId, additionalLocationIds, additionalTenantLocationIdsMap, }); diff --git a/src/TitleReceive/TitleReceiveContainer.js b/src/TitleReceive/TitleReceiveContainer.js index 7eb68d51..4d52bd2e 100644 --- a/src/TitleReceive/TitleReceiveContainer.js +++ b/src/TitleReceive/TitleReceiveContainer.js @@ -1,4 +1,3 @@ -import uniq from 'lodash/uniq'; import { useCallback, useMemo, @@ -19,6 +18,7 @@ import { import { useHoldingsAndLocations, useReceive, + useReceivingTenantIdsAndLocations, useTitleHydratedPieces, } from '../common/hooks'; import { @@ -59,16 +59,18 @@ function TitleReceiveContainer({ history, location, match }) { const { receive } = useReceive(); - const receivingTenantIds = useMemo(() => { + const receivingTenants = useMemo(() => { if (pieces?.length) { - return uniq([ - ...pieces.map(({ receivingTenantId }) => receivingTenantId), - targetTenantId, - ].filter(Boolean)); + return pieces.map(({ receivingTenantId }) => receivingTenantId); } return []; - }, [pieces, targetTenantId]); + }, [pieces]); + + const { receivingTenantIds } = useReceivingTenantIdsAndLocations({ + receivingTenantIds: receivingTenants, + currentReceivingTenantId: targetTenantId, + }); const { locations, isFetching } = useHoldingsAndLocations({ instanceId, diff --git a/src/TitleReceive/TitleReceiveContainer.test.js b/src/TitleReceive/TitleReceiveContainer.test.js index 3cc81f2c..01157e97 100644 --- a/src/TitleReceive/TitleReceiveContainer.test.js +++ b/src/TitleReceive/TitleReceiveContainer.test.js @@ -31,6 +31,7 @@ jest.mock('../common/hooks', () => ({ useHoldingsAndLocations: jest.fn().mockReturnValue({ locations: [] }), useReceive: jest.fn().mockReturnValue({}), useTitleHydratedPieces: jest.fn(), + useReceivingTenantIdsAndLocations: jest.fn().mockReturnValue({}), })); jest.mock('./TitleReceive', () => jest.fn().mockReturnValue('TitleReceive')); diff --git a/src/common/components/LineLocationsView/LineLocationsView.js b/src/common/components/LineLocationsView/LineLocationsView.js index 517141b2..1f1d8c78 100644 --- a/src/common/components/LineLocationsView/LineLocationsView.js +++ b/src/common/components/LineLocationsView/LineLocationsView.js @@ -17,7 +17,7 @@ import { const LineLocationsView = ({ crossTenant = false, instanceId, - poLine, + poLine = {}, locations, }) => { const intl = useIntl(); @@ -27,14 +27,14 @@ const LineLocationsView = ({ const locationsToDisplay = useMemo(() => { const locationsMap = locations.reduce((acc, l) => ({ ...acc, [l.id]: l }), {}); const holdingsMap = holdings.reduce((acc, h) => ({ ...acc, [h.id]: h }), {}); - const lineLocations = poLine.locations.map(({ holdingId, locationId }) => ( + const lineLocations = poLine.locations?.map(({ holdingId, locationId }) => ( holdingId ? holdings.length && holdingsMap[holdingId] && getHoldingLocationName(holdingsMap[holdingId], locationsMap, intl.formatMessage({ id: 'ui-receiving.titles.invalidReference' })) : (locationsMap[locationId]?.name && `${locationsMap[locationId].name} (${locationsMap[locationId].code})`) || '' )); return lineLocations.filter(Boolean).join(', '); - }, [holdings, intl, locations, poLine.locations]); + }, [holdings, intl, locations, poLine?.locations]); return ( <> diff --git a/src/common/hooks/index.js b/src/common/hooks/index.js index b150923f..a9a724d0 100644 --- a/src/common/hooks/index.js +++ b/src/common/hooks/index.js @@ -10,6 +10,7 @@ export * from './usePieces'; export * from './usePiecesExpect'; export * from './usePiecesRequests'; export * from './useReceive'; +export * from './useReceivingTenantIdsAndLocations'; export * from './useTitle'; export * from './useTitleHydratedPieces'; export * from './useUnreceive'; diff --git a/src/common/hooks/useReceivingTenantIdsAndLocations.js b/src/common/hooks/useReceivingTenantIdsAndLocations.js new file mode 100644 index 00000000..27021b6e --- /dev/null +++ b/src/common/hooks/useReceivingTenantIdsAndLocations.js @@ -0,0 +1,43 @@ +import uniq from 'lodash/uniq'; +import { useMemo } from 'react'; + +import { useCurrentUserTenants } from '@folio/stripes-acq-components'; + +export const useReceivingTenantIdsAndLocations = ({ + currentReceivingTenantId, + currentLocationId: locationId, + receivingTenantIds = [], +}) => { + const currentUserTenants = useCurrentUserTenants(); + + const receivingTenants = useMemo(() => { + if (receivingTenantIds.length) { + const currentUserTenantIds = currentUserTenants?.map(({ id: tenantId }) => tenantId); + + // should get unique tenantIds from poLine locations and filter out tenantIds where the current user has assigned + return uniq([ + ...receivingTenantIds, + currentReceivingTenantId, + ].filter((tenantId) => currentUserTenantIds?.includes(tenantId)) + .filter(Boolean)); + } + + return []; + }, [receivingTenantIds, currentUserTenants, currentReceivingTenantId]); + + const additionalLocations = useMemo(() => { + const locationIds = locationId ? [locationId] : []; + const tenantLocationIdsMap = currentReceivingTenantId ? { [currentReceivingTenantId]: locationIds } : {}; + + return { + additionalLocationIds: locationIds, + additionalTenantLocationIdsMap: tenantLocationIdsMap, + }; + }, [locationId, currentReceivingTenantId]); + + return { + receivingTenantIds: receivingTenants, + tenantId: currentReceivingTenantId, + ...additionalLocations, + }; +};