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" + > + +
+
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(), +}));