diff --git a/CHANGELOG.md b/CHANGELOG.md
index b0055b72..37df161d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -12,6 +12,8 @@
* Enable "Hourly" and "Monthly" EDI export scheduling frequency options. Refs UIORGS-415.
* Create Privileged donor information accordion in organization record. Refs UIORGS-397.
* Factor away from unsupported `color()` function. Refs UIORGS-416.
+* Suppress banking information management for unauthorized user. Refs UIORGS-418.
+* Add validation for `Day` field in `Monthly` scheduler for export method. Refs UIORGS-417.
## [5.0.0](https://github.com/folio-org/ui-organizations/tree/v5.0.0) (2023-10-12)
[Full Changelog](https://github.com/folio-org/ui-organizations/compare/v4.0.0...v5.0.0)
diff --git a/src/OrganizationIntegration/OrganizationIntegrationForm/SchedulingForm/SchedulingForm.js b/src/OrganizationIntegration/OrganizationIntegrationForm/SchedulingForm/SchedulingForm.js
index 03de00ca..96dc814c 100644
--- a/src/OrganizationIntegration/OrganizationIntegrationForm/SchedulingForm/SchedulingForm.js
+++ b/src/OrganizationIntegration/OrganizationIntegrationForm/SchedulingForm/SchedulingForm.js
@@ -22,26 +22,16 @@ import {
SCHEDULE_PERIODS,
WEEKDAYS,
} from '../../constants';
-
-const ALLOWED_SCHEDULE_PERIODS = [
- SCHEDULE_PERIODS.none,
- SCHEDULE_PERIODS.hours,
- SCHEDULE_PERIODS.days,
- SCHEDULE_PERIODS.weeks,
- SCHEDULE_PERIODS.months,
-];
-
-const normalizeNumber = value => {
- if (!value && value !== 0) return value;
-
- return Number(value);
-};
-
-const validatePeriod = (value) => {
- return value !== SCHEDULE_PERIODS.none
- ? undefined
- : ;
-};
+import {
+ ALLOWED_SCHEDULE_PERIODS,
+ MAX_DAYS_OF_MONTHLY_SCHEDULE,
+ MIN_REQUIRED_NUMBER,
+} from './constants';
+import {
+ validateRequiredMinAndMaxNumber,
+ normalizeNumber,
+ validatePeriod,
+} from './utils';
export const SchedulingForm = () => {
const { formatMessage } = useIntl();
@@ -135,7 +125,7 @@ export const SchedulingForm = () => {
label={}
name="exportTypeSpecificParameters.vendorEdiOrdersExportConfig.ediSchedule.scheduleParameters.scheduleFrequency"
type="number"
- min={1}
+ min={MIN_REQUIRED_NUMBER}
hasClearIcon={false}
required
validate={validateRequiredPositiveNumber}
@@ -167,7 +157,7 @@ export const SchedulingForm = () => {
label={}
name="exportTypeSpecificParameters.vendorEdiOrdersExportConfig.ediSchedule.scheduleParameters.scheduleFrequency"
type="number"
- min={1}
+ min={MIN_REQUIRED_NUMBER}
hasClearIcon={false}
required
validate={validateRequiredPositiveNumber}
@@ -207,7 +197,7 @@ export const SchedulingForm = () => {
label={}
name="exportTypeSpecificParameters.vendorEdiOrdersExportConfig.ediSchedule.scheduleParameters.scheduleFrequency"
type="number"
- min={1}
+ min={MIN_REQUIRED_NUMBER}
hasClearIcon={false}
required
validate={validateRequiredPositiveNumber}
@@ -259,11 +249,11 @@ export const SchedulingForm = () => {
label={}
name="exportTypeSpecificParameters.vendorEdiOrdersExportConfig.ediSchedule.scheduleParameters.scheduleDay"
type="number"
- min={1}
- max={31}
+ min={MIN_REQUIRED_NUMBER}
+ max={MAX_DAYS_OF_MONTHLY_SCHEDULE}
hasClearIcon={false}
required
- validate={validateRequiredPositiveNumber}
+ validate={validateRequiredMinAndMaxNumber}
parse={normalizeNumber}
/>
diff --git a/src/OrganizationIntegration/OrganizationIntegrationForm/SchedulingForm/constants.js b/src/OrganizationIntegration/OrganizationIntegrationForm/SchedulingForm/constants.js
new file mode 100644
index 00000000..2c64c120
--- /dev/null
+++ b/src/OrganizationIntegration/OrganizationIntegrationForm/SchedulingForm/constants.js
@@ -0,0 +1,12 @@
+import { SCHEDULE_PERIODS } from '../../constants';
+
+export const ALLOWED_SCHEDULE_PERIODS = [
+ SCHEDULE_PERIODS.none,
+ SCHEDULE_PERIODS.hours,
+ SCHEDULE_PERIODS.days,
+ SCHEDULE_PERIODS.weeks,
+ SCHEDULE_PERIODS.months,
+];
+
+export const MIN_REQUIRED_NUMBER = 1;
+export const MAX_DAYS_OF_MONTHLY_SCHEDULE = 31;
diff --git a/src/OrganizationIntegration/OrganizationIntegrationForm/SchedulingForm/utils.js b/src/OrganizationIntegration/OrganizationIntegrationForm/SchedulingForm/utils.js
new file mode 100644
index 00000000..eec77808
--- /dev/null
+++ b/src/OrganizationIntegration/OrganizationIntegrationForm/SchedulingForm/utils.js
@@ -0,0 +1,33 @@
+import { FormattedMessage } from 'react-intl';
+
+import {
+ validateRequiredMinNumber,
+ validateRequiredMaxNumber,
+ validateRequiredNumber,
+} from '@folio/stripes-acq-components';
+
+import { SCHEDULE_PERIODS } from '../../constants';
+import {
+ MAX_DAYS_OF_MONTHLY_SCHEDULE,
+ MIN_REQUIRED_NUMBER,
+} from './constants';
+
+export const normalizeNumber = value => {
+ if (!value && value !== 0) return value;
+
+ return Number(value);
+};
+
+export const validatePeriod = (value) => {
+ return value !== SCHEDULE_PERIODS.none
+ ? undefined
+ : ;
+};
+
+export const validateRequiredMinAndMaxNumber = (value) => {
+ return (
+ validateRequiredNumber(value)
+ || validateRequiredMinNumber({ minNumber: MIN_REQUIRED_NUMBER, value })
+ || validateRequiredMaxNumber({ maxNumber: MAX_DAYS_OF_MONTHLY_SCHEDULE, value })
+ );
+};
diff --git a/src/OrganizationIntegration/OrganizationIntegrationForm/SchedulingForm/utils.test.js b/src/OrganizationIntegration/OrganizationIntegrationForm/SchedulingForm/utils.test.js
new file mode 100644
index 00000000..08d907fe
--- /dev/null
+++ b/src/OrganizationIntegration/OrganizationIntegrationForm/SchedulingForm/utils.test.js
@@ -0,0 +1,49 @@
+import { SCHEDULE_PERIODS } from '../../constants';
+import {
+ validateRequiredMinAndMaxNumber,
+ normalizeNumber,
+ validatePeriod,
+} from './utils';
+
+jest.mock('@folio/stripes-acq-components', () => ({
+ ...jest.requireActual('@folio/stripes-acq-components'),
+ validateRequiredMinNumber: jest.fn(({ minNumber, value }) => (minNumber <= value ? undefined : 'min error')),
+ validateRequiredMaxNumber: jest.fn(({ maxNumber, value }) => (maxNumber >= value ? undefined : 'max error')),
+}));
+
+describe('OrganizationIntegrationForm utils', () => {
+ describe('normalizeNumber', () => {
+ it('should return number', () => {
+ expect(normalizeNumber(0)).toBe(0);
+ });
+
+ it('should return number', () => {
+ expect(normalizeNumber('0')).toBe(0);
+ });
+
+ it('should return undefined', () => {
+ expect(normalizeNumber('')).toBe('');
+ });
+ });
+
+ describe('validatePeriod', () => {
+ it('should return undefined', () => {
+ expect(validatePeriod(SCHEDULE_PERIODS.monthly)).toBe(undefined);
+ });
+
+ it('should return error message', () => {
+ expect(validatePeriod(SCHEDULE_PERIODS.none)).toBeDefined();
+ });
+ });
+
+ describe('validateRequiredMinAndMaxNumber', () => {
+ it('should return undefined', () => {
+ expect(validateRequiredMinAndMaxNumber(0)).toBeTruthy();
+ expect(validateRequiredMinAndMaxNumber('')).toBeTruthy();
+ expect(validateRequiredMinAndMaxNumber(-1)).toBeTruthy();
+ expect(validateRequiredMinAndMaxNumber(1)).toBe(undefined);
+ expect(validateRequiredMinAndMaxNumber(2)).toBe(undefined);
+ expect(validateRequiredMinAndMaxNumber(34)).toBeTruthy();
+ });
+ });
+});
diff --git a/src/OrganizationIntegration/OrganizationIntegrationForm/__snapshots__/OrganizationIntegrationForm.test.js.snap b/src/OrganizationIntegration/OrganizationIntegrationForm/__snapshots__/OrganizationIntegrationForm.test.js.snap
index 8342e0bb..a0fe261f 100644
--- a/src/OrganizationIntegration/OrganizationIntegrationForm/__snapshots__/OrganizationIntegrationForm.test.js.snap
+++ b/src/OrganizationIntegration/OrganizationIntegrationForm/__snapshots__/OrganizationIntegrationForm.test.js.snap
@@ -147,7 +147,7 @@ exports[`OrganizationIntegrationForm should render correct form structure 1`] =
-
+ class="inputGroup"
+ >
+
+
+
-
+ class="inputGroup"
+ >
+
+
+
@@ -898,7 +904,7 @@ exports[`OrganizationIntegrationForm should render correct form structure 1`] =
-
+ class="inputGroup"
+ >
+
+
+
diff --git a/src/OrganizationIntegration/OrganizationIntegrationView/__snapshots__/OrganizationIntegrationView.test.js.snap b/src/OrganizationIntegration/OrganizationIntegrationView/__snapshots__/OrganizationIntegrationView.test.js.snap
index db2488be..e3c10234 100644
--- a/src/OrganizationIntegration/OrganizationIntegrationView/__snapshots__/OrganizationIntegrationView.test.js.snap
+++ b/src/OrganizationIntegration/OrganizationIntegrationView/__snapshots__/OrganizationIntegrationView.test.js.snap
@@ -217,7 +217,7 @@ exports[`OrganizationIntegrationView should render correct view structure 1`] =
-
+ class="inputGroup"
+ >
+
+
+
-
+ class="inputGroup"
+ >
+
+
+
-
+ class="inputGroup"
+ >
+
+
+
chunk(arr, 5).reduce((acc, chunked) => {
}, Promise.resolve());
export const useBankingInformationManager = () => {
+ const stripes = useStripes();
const showCallout = useShowCallout();
const { enabled: isBankingInformationEnabled } = useBankingInformationSettings();
@@ -34,7 +36,15 @@ export const useBankingInformationManager = () => {
bankingInformation,
organization,
}) => {
- if (!(organization.isVendor && isBankingInformationEnabled)) return Promise.resolve();
+ const isOperationRestricted = !(
+ organization.isVendor
+ && isBankingInformationEnabled
+ && stripes.hasPerm('organizations.banking-information.item.post')
+ && stripes.hasPerm('organizations.banking-information.item.put')
+ && stripes.hasPerm('organizations.banking-information.item.delete')
+ );
+
+ if (isOperationRestricted) return Promise.resolve();
const {
created,
@@ -60,6 +70,7 @@ export const useBankingInformationManager = () => {
deleteBankingInformation,
isBankingInformationEnabled,
showCallout,
+ stripes,
updateBankingInformation,
]);
diff --git a/src/common/hooks/useOrganizationBankingInformation/useOrganizationBankingInformation.js b/src/common/hooks/useOrganizationBankingInformation/useOrganizationBankingInformation.js
index 132f90c8..acacbf54 100644
--- a/src/common/hooks/useOrganizationBankingInformation/useOrganizationBankingInformation.js
+++ b/src/common/hooks/useOrganizationBankingInformation/useOrganizationBankingInformation.js
@@ -3,6 +3,7 @@ import { useQuery } from 'react-query';
import {
useNamespace,
useOkapiKy,
+ useStripes,
} from '@folio/stripes/core';
import { LIMIT_MAX } from '@folio/stripes-acq-components';
@@ -11,12 +12,18 @@ import { BANKING_INFORMATION_API } from '../../constants';
const DEFAULT_DATA = [];
export const useOrganizationBankingInformation = (organizationId, options = {}) => {
+ const stripes = useStripes();
const ky = useOkapiKy();
const [namespace] = useNamespace({ key: 'organization-banking-information' });
+ const { enabled = true, ...rest } = options;
const queryOptions = {
- ...options,
- enabled: options.enabled && Boolean(organizationId),
+ ...rest,
+ enabled: Boolean(
+ enabled
+ && Boolean(organizationId)
+ && stripes.hasPerm('organizations.banking-information.collection.get'),
+ ),
};
const {
diff --git a/src/contacts/EditContact/__snapshots__/EditContact.test.js.snap b/src/contacts/EditContact/__snapshots__/EditContact.test.js.snap
index 57812565..f15cac3d 100644
--- a/src/contacts/EditContact/__snapshots__/EditContact.test.js.snap
+++ b/src/contacts/EditContact/__snapshots__/EditContact.test.js.snap
@@ -147,7 +147,7 @@ exports[`EditContact should render correct form structure 1`] = `
-
+ class="inputGroup"
+ >
+
+
+
@@ -7994,7 +7997,7 @@ exports[`EditContact should render correct form structure 1`] = `
-
+ class="inputGroup"
+ >
+
+
+
-
+ class="inputGroup"
+ >
+
+
+
diff --git a/test/jest/__mock__/index.js b/test/jest/__mock__/index.js
index 2b2d24ef..2700c754 100644
--- a/test/jest/__mock__/index.js
+++ b/test/jest/__mock__/index.js
@@ -1 +1,2 @@
import './createRange.mock';
+import './resizeObserver.mock';
diff --git a/test/jest/__mock__/resizeObserver.mock.js b/test/jest/__mock__/resizeObserver.mock.js
new file mode 100644
index 00000000..da66fa4f
--- /dev/null
+++ b/test/jest/__mock__/resizeObserver.mock.js
@@ -0,0 +1,5 @@
+global.ResizeObserver = jest.fn().mockImplementation(() => ({
+ observe: jest.fn(),
+ unobserve: jest.fn(),
+ disconnect: jest.fn(),
+}));