From 6aa7b8bb12ddf1049f901c4e76d0b83460942a46 Mon Sep 17 00:00:00 2001
From: Oleksandr Hladchenko
<85172747+OleksandrHladchenko1@users.noreply.github.com>
Date: Wed, 15 May 2024 13:43:10 +0300
Subject: [PATCH] UIIN-2793: Populate Acquisitions accordion on instance when
central ordering is active (#2482)
* UIIN-2793: Populate Acquisitions accordion on instance when central ordering is active
* Update package.json
* UIIN-2793: Rename file for styles
* UIIN-2793: Display sub accordions when user is in consortium mode AND the user is on non-central tenant
* UIIN-2793: Fix tests
* UIIN-2793: Changes after review
* UIIN-2793: Fix tests
* UIIN-2793: Change naming & mock useStripes
* UIIN-2793: Update CHANGELOG.md
* UIIN-2793: Fix typo in constants.js
---
CHANGELOG.md | 1 +
.../InstanceAcquisition.css | 3 +
.../InstanceAcquisition.js | 106 +++++++++++-------
.../InstanceAcquisition.test.js | 80 ++++++++++++-
.../InstanceAcquisition/TenantAcquisition.js | 68 +++++++++++
.../TenantAcquisition.test.js | 30 +++++
.../InstanceAcquisition/fixtures.js | 1 +
.../useInstanceAcquisition.js | 6 +-
src/constants.js | 9 ++
test/jest/__mock__/stripesCore.mock.js | 2 +-
10 files changed, 256 insertions(+), 50 deletions(-)
create mode 100644 src/Instance/InstanceDetails/InstanceAcquisition/InstanceAcquisition.css
create mode 100644 src/Instance/InstanceDetails/InstanceAcquisition/TenantAcquisition.js
create mode 100644 src/Instance/InstanceDetails/InstanceAcquisition/TenantAcquisition.test.js
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8845c4274..5a9ec2c3a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -20,6 +20,7 @@
* Jest/RTL: Cover ModalContent components with unit tests. Refs UIIN-2669.
* Add callout noting user's active affiliation when it changes after selecting holding or item. Refs UIIN-2831, UIIN-2872.
* Jest/RTL: Cover LocationSelectionWithCheck components with unit tests. Refs UIIN-2670.
+* Populate Acquisitions accordion on instance when central ordering is active. Refs UIIN-2793.
## [11.0.4](https://github.com/folio-org/ui-inventory/tree/v11.0.4) (2024-04-30)
[Full Changelog](https://github.com/folio-org/ui-inventory/compare/v11.0.3...v11.0.4)
diff --git a/src/Instance/InstanceDetails/InstanceAcquisition/InstanceAcquisition.css b/src/Instance/InstanceDetails/InstanceAcquisition/InstanceAcquisition.css
new file mode 100644
index 000000000..b450395ee
--- /dev/null
+++ b/src/Instance/InstanceDetails/InstanceAcquisition/InstanceAcquisition.css
@@ -0,0 +1,3 @@
+.tenantAcquisitionAccordion {
+ padding-left: 15px;
+}
diff --git a/src/Instance/InstanceDetails/InstanceAcquisition/InstanceAcquisition.js b/src/Instance/InstanceDetails/InstanceAcquisition/InstanceAcquisition.js
index ab9da0d99..06ac02f24 100644
--- a/src/Instance/InstanceDetails/InstanceAcquisition/InstanceAcquisition.js
+++ b/src/Instance/InstanceDetails/InstanceAcquisition/InstanceAcquisition.js
@@ -1,66 +1,90 @@
import React from 'react';
import PropTypes from 'prop-types';
import { FormattedMessage } from 'react-intl';
-import { Link } from 'react-router-dom';
import { useStripes } from '@folio/stripes/core';
-import {
- Accordion,
- MultiColumnList,
- NoValue,
-} from '@folio/stripes/components';
+import { Accordion } from '@folio/stripes/components';
-import { getDateWithTime } from '../../../utils';
import { useControlledAccordion } from '../../../common';
+import { isUserInConsortiumMode } from '../../../utils';
import useInstanceAcquisition from './useInstanceAcquisition';
-const visibleColumns = ['poLineNumber', 'orderStatus', 'polReceiptStatus', 'dateOrdered', 'acqUnit', 'orderType'];
-const columnMapping = {
- poLineNumber: ,
- orderStatus: ,
- polReceiptStatus: ,
- dateOrdered: ,
- acqUnit: ,
- orderType: ,
-};
-const formatter = {
- poLineNumber: i => {i.poLineNumber},
- orderStatus: i => (
- <>
- {i.order?.workflowStatus ? : }
- {i.order?.orderCloseReason?.reason && ` - ${i.order.orderCloseReason.reason}`}
- >
- ),
- polReceiptStatus: i => ,
- dateOrdered: i => getDateWithTime(i.order?.dateOrdered),
- acqUnit: i => i.order?.acqUnits?.map(u => u.name)?.join(', ') || ,
- orderType: i => (i.order?.orderType ? : ),
-};
+import TenantAcquisition from './TenantAcquisition';
+
+import css from './InstanceAcquisition.css';
const InstanceAcquisition = ({ accordionId, instanceId }) => {
const stripes = useStripes();
- const { isLoading, instanceAcquisition } = useInstanceAcquisition(instanceId);
- const controlledAccorion = useControlledAccordion(Boolean(instanceAcquisition?.length));
+ const activeTenant = stripes.okapi.tenant;
+ const centralTenant = stripes.user.user?.consortium?.centralTenantId;
+
+ const {
+ isLoading: isLoadingActiveTenantAcquisition,
+ instanceAcquisition: activeTenantAcquisition,
+ } = useInstanceAcquisition(instanceId, activeTenant);
+ const {
+ isLoading: isLoadingCentralTenantAcquisition,
+ instanceAcquisition: centralTenantAcquisition,
+ } = useInstanceAcquisition(instanceId, centralTenant);
+
+ const controlledAcqAccorion = useControlledAccordion(Boolean(activeTenantAcquisition?.length || centralTenantAcquisition?.length));
+ const controlledActiveTenantAcqAccorion = useControlledAccordion(Boolean(activeTenantAcquisition?.length));
+ const controlledCetralTenantAcqAccorion = useControlledAccordion(Boolean(centralTenantAcquisition?.length));
if (!(stripes.hasInterface('order-lines') &&
stripes.hasInterface('orders') &&
stripes.hasInterface('acquisitions-units'))) return null;
+ const renderTenantAcquisitionAccordion = (accId, tenantId, tenantAcquisitions, isLoading, controlledAccorionProps) => {
+ const getTenantAccordionLabel = (tenants, id) => tenants?.find(tenant => tenant.id === id).name;
+
+ return (
+
+
+
+ );
+ };
+
return (
}
- {...controlledAccorion}
+ {...controlledAcqAccorion}
>
-
+ {(isUserInConsortiumMode(stripes) && activeTenant !== centralTenant) ? (
+ <>
+ {renderTenantAcquisitionAccordion(
+ 'active-acquisition-accordion',
+ activeTenant,
+ activeTenantAcquisition,
+ isLoadingActiveTenantAcquisition,
+ controlledActiveTenantAcqAccorion,
+ )}
+ {renderTenantAcquisitionAccordion(
+ 'central-acquisition-accordion',
+ centralTenant,
+ centralTenantAcquisition,
+ isLoadingCentralTenantAcquisition,
+ controlledCetralTenantAcqAccorion,
+ )}
+ >
+ ) : (
+
+ )
+ }
);
};
diff --git a/src/Instance/InstanceDetails/InstanceAcquisition/InstanceAcquisition.test.js b/src/Instance/InstanceDetails/InstanceAcquisition/InstanceAcquisition.test.js
index dd0d570a9..c57343359 100644
--- a/src/Instance/InstanceDetails/InstanceAcquisition/InstanceAcquisition.test.js
+++ b/src/Instance/InstanceDetails/InstanceAcquisition/InstanceAcquisition.test.js
@@ -1,17 +1,25 @@
import React from 'react';
import { BrowserRouter as Router } from 'react-router-dom';
+import { useStripes } from '@folio/stripes/core';
import { screen } from '@folio/jest-config-stripes/testing-library/react';
import '../../../../test/jest/__mock__';
-import renderWithIntl from '../../../../test/jest/helpers/renderWithIntl';
+import {
+ renderWithIntl,
+ translationsProperties,
+} from '../../../../test/jest/helpers';
import { resultData, line } from './fixtures';
import InstanceAcquisition from './InstanceAcquisition';
import useInstanceAcquisition from './useInstanceAcquisition';
+import * as utils from '../../../utils';
+
jest.mock('./useInstanceAcquisition', () => jest.fn());
+const spyOnIsUserInConsortiumMode = jest.spyOn(utils, 'isUserInConsortiumMode');
+
const renderInstanceAcquisition = (props = {}) => (
renderWithIntl(
@@ -20,7 +28,8 @@ const renderInstanceAcquisition = (props = {}) => (
accordionId="accordionId"
{...props}
/>
-
+ ,
+ translationsProperties
)
);
@@ -29,9 +38,70 @@ describe('InstanceAcquisition', () => {
useInstanceAcquisition.mockClear().mockReturnValue({ instanceAcquisition: resultData });
});
- it('should display fetched instance acquisition data', () => {
- renderInstanceAcquisition({ instanceId: 'instanceId' });
+ describe('when user is non-consortial tenant', () => {
+ it('should display acquisition accordion and fetched instance acquisition data', () => {
+ spyOnIsUserInConsortiumMode.mockReturnValue(false);
+ useStripes.mockClear().mockReturnValue({
+ hasInterface: () => true,
+ okapi: { tenant: 'diku' },
+ user: { user: {} },
+ });
+
+ const { container } = renderInstanceAcquisition({ instanceId: 'instanceId' });
+
+ expect(container.querySelector('#accordionId')).toBeInTheDocument();
+ expect(screen.getByText(line.poLineNumber)).toBeInTheDocument();
+ });
+ });
+
+ describe('when user is in central tenant', () => {
+ it('should display acquisition accordion and fetched instance acquisition data', () => {
+ spyOnIsUserInConsortiumMode.mockReturnValue(true);
+ useStripes.mockClear().mockReturnValue({
+ hasInterface: () => true,
+ okapi: { tenant: 'consortium' },
+ user: { user: {
+ consortium: { centralTenantId: 'consortium' },
+ tenants: [{
+ id: 'consortium',
+ name: 'Consortium',
+ }],
+ } },
+ });
+
+ const { container } = renderInstanceAcquisition({ instanceId: 'instanceId' });
+
+ expect(container.querySelector('#accordionId')).toBeInTheDocument();
+ expect(screen.getByText(line.poLineNumber)).toBeInTheDocument();
+ });
+ });
+
+ describe('when user is in member tenant', () => {
+ it('should display central and member tenant subaccordions with fetched instance acquisition data', () => {
+ spyOnIsUserInConsortiumMode.mockReturnValue(true);
+ useStripes.mockClear().mockReturnValue({
+ hasInterface: () => true,
+ okapi: { tenant: 'college' },
+ user: { user: {
+ consortium: { centralTenantId: 'consortium' },
+ tenants: [{
+ id: 'consortium',
+ name: 'Consortium',
+ }, {
+ id: 'college',
+ name: 'College',
+ }],
+ } },
+ });
+
+ const { container } = renderInstanceAcquisition({ instanceId: 'instanceId' });
+
+ expect(container.querySelector('#accordionId')).toBeInTheDocument();
+ expect(container.querySelector('#active-acquisition-accordion')).toBeInTheDocument();
+ expect(screen.getAllByText(line.poLineNumber)[0]).toBeInTheDocument();
- expect(screen.getByText(line.poLineNumber)).toBeInTheDocument();
+ expect(container.querySelector('#central-acquisition-accordion')).toBeInTheDocument();
+ expect(screen.getAllByText(line.poLineNumber)[1]).toBeInTheDocument();
+ });
});
});
diff --git a/src/Instance/InstanceDetails/InstanceAcquisition/TenantAcquisition.js b/src/Instance/InstanceDetails/InstanceAcquisition/TenantAcquisition.js
new file mode 100644
index 000000000..b28de41f1
--- /dev/null
+++ b/src/Instance/InstanceDetails/InstanceAcquisition/TenantAcquisition.js
@@ -0,0 +1,68 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { FormattedMessage } from 'react-intl';
+import { Link } from 'react-router-dom';
+
+import {
+ MultiColumnList,
+ NoValue,
+} from '@folio/stripes/components';
+
+import { getDateWithTime } from '../../../utils';
+import { ACQUISITION_COLUMN_NAMES } from '../../../constants';
+
+const visibleColumns = [
+ ACQUISITION_COLUMN_NAMES.poLineNumber,
+ ACQUISITION_COLUMN_NAMES.orderStatus,
+ ACQUISITION_COLUMN_NAMES.polReceiptStatus,
+ ACQUISITION_COLUMN_NAMES.dateOrdered,
+ ACQUISITION_COLUMN_NAMES.acqUnit,
+ ACQUISITION_COLUMN_NAMES.orderType,
+];
+
+const columnMapping = {
+ [ACQUISITION_COLUMN_NAMES.poLineNumber]: ,
+ [ACQUISITION_COLUMN_NAMES.orderStatus]: ,
+ [ACQUISITION_COLUMN_NAMES.polReceiptStatus]: ,
+ [ACQUISITION_COLUMN_NAMES.dateOrdered]: ,
+ [ACQUISITION_COLUMN_NAMES.acqUnit]: ,
+ [ACQUISITION_COLUMN_NAMES.orderType]: ,
+};
+
+const formatter = {
+ [ACQUISITION_COLUMN_NAMES.poLineNumber]: i => {i.poLineNumber},
+ [ACQUISITION_COLUMN_NAMES.orderStatus]: i => (
+ <>
+ {i.order?.workflowStatus ? : }
+ {i.order?.orderCloseReason?.reason && ` - ${i.order.orderCloseReason.reason}`}
+ >
+ ),
+ [ACQUISITION_COLUMN_NAMES.polReceiptStatus]: i => ,
+ [ACQUISITION_COLUMN_NAMES.dateOrdered]: i => getDateWithTime(i.order?.dateOrdered),
+ [ACQUISITION_COLUMN_NAMES.acqUnit]: i => i.order?.acqUnits?.map(u => u.name)?.join(', ') || ,
+ [ACQUISITION_COLUMN_NAMES.orderType]: i => (i.order?.orderType ? : ),
+};
+
+const TenantAcquisition = ({
+ acquisitions,
+ isLoading,
+ tenantId,
+}) => (
+
+);
+
+TenantAcquisition.propTypes = {
+ acquisitions: PropTypes.arrayOf(PropTypes.object).isRequired,
+ isLoading: PropTypes.bool.isRequired,
+ tenantId: PropTypes.string.isRequired,
+};
+
+export default TenantAcquisition;
diff --git a/src/Instance/InstanceDetails/InstanceAcquisition/TenantAcquisition.test.js b/src/Instance/InstanceDetails/InstanceAcquisition/TenantAcquisition.test.js
new file mode 100644
index 000000000..53005b5bd
--- /dev/null
+++ b/src/Instance/InstanceDetails/InstanceAcquisition/TenantAcquisition.test.js
@@ -0,0 +1,30 @@
+import React from 'react';
+import { BrowserRouter as Router } from 'react-router-dom';
+
+import { screen } from '@folio/jest-config-stripes/testing-library/react';
+
+import '../../../../test/jest/__mock__';
+import { renderWithIntl } from '../../../../test/jest/helpers';
+
+import { resultData, line } from './fixtures';
+import TenantAcquisition from './TenantAcquisition';
+
+const renderTenantAcquisition = () => (
+ renderWithIntl(
+
+
+
+ )
+);
+
+describe('TenantAcquisition', () => {
+ it('should display instance acquisition data', () => {
+ renderTenantAcquisition();
+
+ expect(screen.getByText(line.poLineNumber)).toBeInTheDocument();
+ });
+});
diff --git a/src/Instance/InstanceDetails/InstanceAcquisition/fixtures.js b/src/Instance/InstanceDetails/InstanceAcquisition/fixtures.js
index 626a29e23..88e69d1c0 100644
--- a/src/Instance/InstanceDetails/InstanceAcquisition/fixtures.js
+++ b/src/Instance/InstanceDetails/InstanceAcquisition/fixtures.js
@@ -2,6 +2,7 @@ export const line = {
id: 'lineId',
purchaseOrderId: 'orderId',
poLineNumber: '1000',
+ receiptStatus: 'Ongoing',
};
export const order = {
id: line.purchaseOrderId,
diff --git a/src/Instance/InstanceDetails/InstanceAcquisition/useInstanceAcquisition.js b/src/Instance/InstanceDetails/InstanceAcquisition/useInstanceAcquisition.js
index 691bcecdb..b472ec587 100644
--- a/src/Instance/InstanceDetails/InstanceAcquisition/useInstanceAcquisition.js
+++ b/src/Instance/InstanceDetails/InstanceAcquisition/useInstanceAcquisition.js
@@ -6,13 +6,13 @@ import { useOkapiKy, useNamespace, useStripes } from '@folio/stripes/core';
import { LIMIT_MAX } from '../../../constants';
import { batchRequest } from '../../../utils';
-const useInstanceAcquisition = (id) => {
- const ky = useOkapiKy();
+const useInstanceAcquisition = (id, tenant) => {
+ const ky = useOkapiKy({ tenant });
const [namespace] = useNamespace({ key: 'instance-acquisition' });
const stripes = useStripes();
const { data = [], isLoading } = useQuery(
- [namespace, 'instance-acquisition', id],
+ [namespace, 'instance-acquisition', id, tenant],
async () => {
const { titles = [] } = await ky.get('orders/titles', {
searchParams: {
diff --git a/src/constants.js b/src/constants.js
index 1b4933481..05979f2b6 100644
--- a/src/constants.js
+++ b/src/constants.js
@@ -724,3 +724,12 @@ export const LEADER_RECORD_STATUSES = {
};
export const USER_TOUCHED_STAFF_SUPPRESS_STORAGE_KEY = 'folio_user_touched_staff_suppress';
+
+export const ACQUISITION_COLUMN_NAMES = {
+ poLineNumber: 'poLineNumber',
+ orderStatus: 'orderStatus',
+ polReceiptStatus: 'polReceiptStatus',
+ dateOrdered: 'dateOrdered',
+ acqUnit: 'acqUnit',
+ orderType: 'orderType',
+};
diff --git a/test/jest/__mock__/stripesCore.mock.js b/test/jest/__mock__/stripesCore.mock.js
index 25b8a169f..4f931063c 100644
--- a/test/jest/__mock__/stripesCore.mock.js
+++ b/test/jest/__mock__/stripesCore.mock.js
@@ -83,7 +83,7 @@ const mockStripesCore = {
extend: jest.fn().mockReturnValue(this),
}),
- useStripes: () => STRIPES,
+ useStripes: jest.fn(() => STRIPES),
withStripes: Component => ({ stripes, ...rest }) => {
const fakeStripes = stripes || STRIPES;