diff --git a/package.json b/package.json
index 158442d4..9f34bfc1 100644
--- a/package.json
+++ b/package.json
@@ -19,6 +19,7 @@
"home": "/organizations",
"okapiInterfaces": {
"acquisition-methods": "1.0",
+ "banking-information": "1.0",
"data-export-spring": "1.0",
"configuration": "2.0",
"organizations.organizations": "1.0",
diff --git a/src/Organizations/OrganizationCreate/OrganizationCreate.js b/src/Organizations/OrganizationCreate/OrganizationCreate.js
index 24f5d6de..4fd63c8f 100644
--- a/src/Organizations/OrganizationCreate/OrganizationCreate.js
+++ b/src/Organizations/OrganizationCreate/OrganizationCreate.js
@@ -1,4 +1,4 @@
-import React, { useCallback } from 'react';
+import { useCallback, useMemo } from 'react';
import PropTypes from 'prop-types';
import { withRouter } from 'react-router-dom';
import { useIntl } from 'react-intl';
@@ -46,7 +46,10 @@ export const OrganizationCreate = ({ history, location, mutator }) => {
const showCallout = useShowCallout();
const intl = useIntl();
const createOrganization = useCallback(
- (data) => {
+ ({ bankingInformation, ...data }) => {
+ // TODO: implement create banking info logic
+ console.log('bankingInformation on create', bankingInformation);
+
return mutator.createOrganizationOrg.POST(data)
.then(organization => {
setTimeout(() => cancelForm(organization.id));
@@ -63,9 +66,15 @@ export const OrganizationCreate = ({ history, location, mutator }) => {
[cancelForm, intl, showCallout],
);
+ // TODO: provide info without perms and setting?
+ const initialValues = useMemo(() => ({
+ bankingInformation: [],
+ ...INITIAL_VALUES,
+ }), []);
+
return (
diff --git a/src/Organizations/OrganizationEdit/OrganizationEdit.js b/src/Organizations/OrganizationEdit/OrganizationEdit.js
index 326bef8a..538a334f 100644
--- a/src/Organizations/OrganizationEdit/OrganizationEdit.js
+++ b/src/Organizations/OrganizationEdit/OrganizationEdit.js
@@ -1,6 +1,7 @@
-import React, {
+import {
useCallback,
useEffect,
+ useMemo,
useState,
} from 'react';
import { FormattedMessage, useIntl } from 'react-intl';
@@ -19,6 +20,7 @@ import {
} from '@folio/stripes-acq-components';
import { VIEW_ORG_DETAILS } from '../../common/constants';
+import { useOrganizationBankingInformation } from '../../common/hooks';
import { organizationResourceByUrl } from '../../common/resources';
import {
OrganizationForm,
@@ -29,17 +31,22 @@ export const OrganizationEdit = ({ match, history, location, mutator }) => {
const organizationId = match.params.id;
const [organization, setOrganization] = useState({});
- const [isLoading, setIsLoading] = useState(true);
+ const [isOrganizationLoading, setIsOrganizationLoading] = useState(true);
const showCallout = useShowCallout();
const intl = useIntl();
+ const {
+ bankingInformation: bankingInformationData,
+ isLoading: isBankingInformationLoading,
+ } = useOrganizationBankingInformation(organizationId);
+
useEffect(
() => {
mutator.editOrganizationOrg.GET()
.then(organizationsResponse => {
setOrganization(organizationsResponse);
})
- .finally(() => setIsLoading(false));
+ .finally(() => setIsOrganizationLoading(false));
},
// eslint-disable-next-line react-hooks/exhaustive-deps
[],
@@ -58,7 +65,10 @@ export const OrganizationEdit = ({ match, history, location, mutator }) => {
);
const updateOrganization = useCallback(
- (data) => {
+ ({ bankingInformation, ...data }) => {
+ // TODO: implement create/update banking info logic
+ console.log('bankingInformation on update', bankingInformation);
+
return mutator.editOrganizationOrg.PUT(data)
.then(() => {
setTimeout(cancelForm);
@@ -75,6 +85,13 @@ export const OrganizationEdit = ({ match, history, location, mutator }) => {
[cancelForm, intl, showCallout],
);
+ const initialValues = useMemo(() => ({
+ bankingInformation: bankingInformationData,
+ ...organization,
+ }), [organization, bankingInformationData]);
+
+ const isLoading = isOrganizationLoading || isBankingInformationLoading;
+
if (isLoading) {
return (
@@ -85,7 +102,7 @@ export const OrganizationEdit = ({ match, history, location, mutator }) => {
return (
}
diff --git a/src/Organizations/OrganizationForm/OrganizationBankingInfoForm/BankingInformationField/BankingInformationField.js b/src/Organizations/OrganizationForm/OrganizationBankingInfoForm/BankingInformationField/BankingInformationField.js
new file mode 100644
index 00000000..375ad90a
--- /dev/null
+++ b/src/Organizations/OrganizationForm/OrganizationBankingInfoForm/BankingInformationField/BankingInformationField.js
@@ -0,0 +1,101 @@
+import PropTypes from 'prop-types';
+import { Field } from 'react-final-form';
+import { FormattedMessage } from 'react-intl';
+
+import {
+ Card,
+ Col,
+ Row,
+ TextField,
+} from '@folio/stripes/components';
+import { FieldSelectionFinal } from '@folio/stripes-acq-components';
+
+import { FieldIsPrimary } from '../../../../common/components';
+
+export const BankingInformationField = ({
+ bankingAccountTypeOptions,
+ fields,
+ index,
+ name,
+}) => {
+ const cardHeader = (
+
+ );
+
+ return (
+
+
+
+ }
+ name={`${name}.bankName`}
+ component={TextField}
+ fullWidth
+ validateFields={[]}
+ />
+
+
+ }
+ name={`${name}.bankAccountNumber`}
+ component={TextField}
+ fullWidth
+ validateFields={[]}
+ />
+
+
+ }
+ name={`${name}.transitNumber`}
+ component={TextField}
+ fullWidth
+ validateFields={[]}
+ />
+
+
+ }
+ name={`${name}.addressId`}
+ // TODO: refine address field
+ dataOptions={[]}
+ validateFields={[]}
+ />
+
+
+ }
+ name={`${name}.accountTypeId`}
+ dataOptions={bankingAccountTypeOptions}
+ fullWidth
+ validateFields={[]}
+ />
+
+
+ }
+ name={`${name}.notes`}
+ id={`${name}.notes`}
+ component={TextField}
+ fullWidth
+ validateFields={[]}
+ />
+
+
+
+ );
+};
+
+BankingInformationField.propTypes = {
+ bankingAccountTypeOptions: PropTypes.arrayOf(PropTypes.shape({
+ label: PropTypes.string,
+ value: PropTypes.string,
+ })).isRequired,
+ fields: PropTypes.object.isRequired,
+ index: PropTypes.number.isRequired,
+ name: PropTypes.string.isRequired,
+};
diff --git a/src/Organizations/OrganizationForm/OrganizationBankingInfoForm/BankingInformationField/index.js b/src/Organizations/OrganizationForm/OrganizationBankingInfoForm/BankingInformationField/index.js
new file mode 100644
index 00000000..70a06bd3
--- /dev/null
+++ b/src/Organizations/OrganizationForm/OrganizationBankingInfoForm/BankingInformationField/index.js
@@ -0,0 +1 @@
+export { BankingInformationField } from './BankingInformationField';
diff --git a/src/Organizations/OrganizationForm/OrganizationBankingInfoForm/OrganizationBankingInfoForm.js b/src/Organizations/OrganizationForm/OrganizationBankingInfoForm/OrganizationBankingInfoForm.js
new file mode 100644
index 00000000..5b006783
--- /dev/null
+++ b/src/Organizations/OrganizationForm/OrganizationBankingInfoForm/OrganizationBankingInfoForm.js
@@ -0,0 +1,54 @@
+import { useMemo } from 'react';
+import { FormattedMessage } from 'react-intl';
+import { FieldArray } from 'react-final-form-arrays';
+
+import { Loading } from '@folio/stripes/components';
+import { RepeatableFieldWithValidation } from '@folio/stripes-acq-components';
+
+import { useBankingAccountTypes } from '../../../common/hooks';
+import {
+ createAddNewItem,
+ removeItem,
+} from '../../../common/utils';
+import { validatePrimary } from '../../../common/validation';
+import { BankingInformationField } from './BankingInformationField';
+
+const renderField = (props) => (name, index, fields) => (
+
+);
+
+export const OrganizationBankingInfoForm = () => {
+ const {
+ bankingAccountTypes,
+ isFetching,
+ } = useBankingAccountTypes();
+
+ const bankingAccountTypeOptions = useMemo(() => {
+ return bankingAccountTypes.map(({ id, name }) => ({
+ label: name,
+ value: id,
+ }));
+ }, [bankingAccountTypes]);
+
+ if (isFetching) {
+ return ;
+ }
+
+ return (
+ }
+ component={RepeatableFieldWithValidation}
+ id="bankingInformation"
+ name="bankingInformation"
+ onAdd={createAddNewItem()}
+ onRemove={removeItem}
+ renderField={renderField({ bankingAccountTypeOptions })}
+ validate={validatePrimary}
+ />
+ );
+};
diff --git a/src/Organizations/OrganizationForm/OrganizationBankingInfoForm/index.js b/src/Organizations/OrganizationForm/OrganizationBankingInfoForm/index.js
new file mode 100644
index 00000000..0538aaeb
--- /dev/null
+++ b/src/Organizations/OrganizationForm/OrganizationBankingInfoForm/index.js
@@ -0,0 +1 @@
+export { OrganizationBankingInfoForm } from './OrganizationBankingInfoForm';
diff --git a/src/Organizations/OrganizationForm/OrganizationForm.js b/src/Organizations/OrganizationForm/OrganizationForm.js
index e1d6bd4d..d36aacc6 100644
--- a/src/Organizations/OrganizationForm/OrganizationForm.js
+++ b/src/Organizations/OrganizationForm/OrganizationForm.js
@@ -24,6 +24,7 @@ import {
} from '@folio/stripes-acq-components';
import { ORGANIZATIONS_ROUTE } from '../../common/constants';
+import { OrganizationBankingInfoForm } from './OrganizationBankingInfoForm';
import { OrganizationSummaryForm } from './OrganizationSummaryForm';
import { OrganizationContactInfoFormContainer } from './OrganizationContactInfoForm';
import { OrganizationContactPeopleForm } from './OrganizationContactPeopleForm';
@@ -200,6 +201,13 @@ const OrganizationForm = ({
+
+
+
+
,
+ [ORGANIZATION_SECTIONS.bankingInformationSection]: ,
[ORGANIZATION_SECTIONS.contactInformationSection]: ,
[ORGANIZATION_SECTIONS.contactPeopleSection]: ,
[ORGANIZATION_SECTIONS.interfacesSection]: ,
diff --git a/src/common/constants/api.js b/src/common/constants/api.js
index 5d7d559a..a9055baa 100644
--- a/src/common/constants/api.js
+++ b/src/common/constants/api.js
@@ -1,5 +1,6 @@
export const AGREEMENTS_API = 'erm/sas';
export const BANKING_ACCOUNT_TYPES_API = 'organizations-storage/banking-account-types';
+export const BANKING_INFORMATION_API = 'organizations/banking-information';
export const CATEGORIES_API = 'organizations-storage/categories';
export const CONTACTS_API = 'organizations-storage/contacts';
export const INTERFACES_API = 'organizations-storage/interfaces';
diff --git a/src/common/hooks/index.js b/src/common/hooks/index.js
index 82eb4ca1..9fcb4d7a 100644
--- a/src/common/hooks/index.js
+++ b/src/common/hooks/index.js
@@ -1,7 +1,9 @@
export * from './useAcqMethods';
+export * from './useBankingAccountTypes';
export * from './useBankingInformationSettings';
export * from './useIntegrationConfig';
export * from './useIntegrationConfigMutation';
export * from './useLinkedAgreements';
+export * from './useOrganizationBankingInformation';
export * from './useTranslatedCategories';
export * from './useTypes';
diff --git a/src/common/hooks/useBankingAccountTypes/index.js b/src/common/hooks/useBankingAccountTypes/index.js
new file mode 100644
index 00000000..c1574cdd
--- /dev/null
+++ b/src/common/hooks/useBankingAccountTypes/index.js
@@ -0,0 +1 @@
+export { useBankingAccountTypes } from './useBankingAccountTypes';
diff --git a/src/common/hooks/useBankingAccountTypes/useBankingAccountTypes.js b/src/common/hooks/useBankingAccountTypes/useBankingAccountTypes.js
new file mode 100644
index 00000000..aec118d8
--- /dev/null
+++ b/src/common/hooks/useBankingAccountTypes/useBankingAccountTypes.js
@@ -0,0 +1,40 @@
+import { useQuery } from 'react-query';
+
+import {
+ useNamespace,
+ useOkapiKy,
+} from '@folio/stripes/core';
+import { LIMIT_MAX } from '@folio/stripes-acq-components';
+
+import { BANKING_ACCOUNT_TYPES_API } from '../../constants';
+
+const DEFAULT_DATA = [];
+
+export const useBankingAccountTypes = () => {
+ const ky = useOkapiKy();
+ const [namespace] = useNamespace({ key: 'banking-account-types' });
+
+ const {
+ data,
+ isFetching,
+ isLoading,
+ refetch,
+ } = useQuery(
+ [namespace],
+ () => {
+ const searchParams = {
+ limit: LIMIT_MAX,
+ };
+
+ return ky.get(BANKING_ACCOUNT_TYPES_API, { searchParams }).json();
+ },
+ );
+
+ return ({
+ bankingAccountTypes: data?.bankingAccountTypes || DEFAULT_DATA,
+ totalRecords: data?.totalRecords,
+ isFetching,
+ isLoading,
+ refetch,
+ });
+};
diff --git a/src/common/hooks/useOrganizationBankingInformation/index.js b/src/common/hooks/useOrganizationBankingInformation/index.js
new file mode 100644
index 00000000..b97f1380
--- /dev/null
+++ b/src/common/hooks/useOrganizationBankingInformation/index.js
@@ -0,0 +1 @@
+export { useOrganizationBankingInformation } from './useOrganizationBankingInformation';
diff --git a/src/common/hooks/useOrganizationBankingInformation/useOrganizationBankingInformation.js b/src/common/hooks/useOrganizationBankingInformation/useOrganizationBankingInformation.js
new file mode 100644
index 00000000..245f947e
--- /dev/null
+++ b/src/common/hooks/useOrganizationBankingInformation/useOrganizationBankingInformation.js
@@ -0,0 +1,45 @@
+import { useQuery } from 'react-query';
+
+import {
+ useNamespace,
+ useOkapiKy,
+} from '@folio/stripes/core';
+
+import { BANKING_INFORMATION_API } from '../../constants';
+
+const DEFAULT_DATA = [];
+
+export const useOrganizationBankingInformation = (organizationId, options = {}) => {
+ const ky = useOkapiKy();
+ const [namespace] = useNamespace({ key: 'organization-banking-information' });
+
+ const queryOptions = {
+ ...options,
+ enabled: options.enabled && Boolean(organizationId),
+ };
+
+ const {
+ data,
+ isFetching,
+ isLoading,
+ refetch,
+ } = useQuery(
+ [namespace],
+ () => {
+ const searchParams = {
+ query: `organizationId==${organizationId}`,
+ };
+
+ return ky.get(BANKING_INFORMATION_API, { searchParams }).json();
+ },
+ queryOptions,
+ );
+
+ return ({
+ bankingInformation: data?.bankingInformation || DEFAULT_DATA,
+ totalRecords: data?.totalRecords,
+ isFetching,
+ isLoading,
+ refetch,
+ });
+};
diff --git a/translations/ui-organizations/en.json b/translations/ui-organizations/en.json
index ec12c26e..8f0de46d 100644
--- a/translations/ui-organizations/en.json
+++ b/translations/ui-organizations/en.json
@@ -12,6 +12,13 @@
"comingSoon": "Coming in a future release.",
+ "data.bankingInformation.accountType": "Account type",
+ "data.bankingInformation.address": "Address",
+ "data.bankingInformation.bankAccountNumber": "Bank account number",
+ "data.bankingInformation.bankName": "Bank name",
+ "data.bankingInformation.isPrimary": "Use as primary banking information",
+ "data.bankingInformation.notes": "Notes",
+ "data.bankingInformation.transitNumber": "Transit number",
"data.contactTypes.address": "Address",
"data.contactTypes.addressLine1": "Address 1",
"data.contactTypes.addressLine2": "Address 2",
@@ -84,6 +91,7 @@
"vendor.confirmation.message": "Warning. All vendor information, vendor terms, EDI information, and accounts will be deleted from this record.",
"summary": "Summary",
+ "bankingInformation": "Banking information",
"contactInformation": "Contact information",
"contactPeople": "Contact people",
"agreements": "Agreements",
@@ -120,6 +128,7 @@
"summary.add": "Add alternative names",
"summary.pleaseAddAltNames": "Please add alternative names",
+ "button.bankingInformation.add": "Add banking information",
"button.cancel": "Cancel",
"button.saveAndClose": "Save & close",