diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3fd58c275..2cee3b27f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,6 +3,8 @@
## 7.1.0 (IN PROGRESS)
* Display the “Record deleted” label in version history only if the UUID no longer exists. Refs UIOR-1355.
+* Add the "Save & keep editing" button to the PO form. Refs UIOR-1325.
+* Add the "Save & keep editing" button to the PO Line form. Refs UIOR-1351.
## [7.0.4](https://github.com/folio-org/ui-orders/tree/v7.0.4) (2024-12-31)
[Full Changelog](https://github.com/folio-org/ui-orders/compare/v7.0.3...v7.0.4)
diff --git a/src/common/constants/constants.js b/src/common/constants/constants.js
index f03f7a096..b0fa0fd47 100644
--- a/src/common/constants/constants.js
+++ b/src/common/constants/constants.js
@@ -71,3 +71,5 @@ export const CENTRAL_ORDERING_DEFAULT_RECEIVING_SEARCH = {
centralDefault: 'Central default',
activeAffiliationDefault: 'Active affiliation default',
};
+
+export const SUBMIT_ACTION_FIELD = '__submitAction__';
diff --git a/src/common/hooks/useOrder/useOrder.js b/src/common/hooks/useOrder/useOrder.js
index a0fba85d0..60bfc6d7d 100644
--- a/src/common/hooks/useOrder/useOrder.js
+++ b/src/common/hooks/useOrder/useOrder.js
@@ -12,7 +12,11 @@ export const useOrder = (orderId) => {
query: `id==${orderId}`,
};
- const { isLoading, data } = useQuery(
+ const {
+ data,
+ isLoading,
+ refetch,
+ } = useQuery(
['ui-orders', 'order', orderId],
async () => {
try {
@@ -29,5 +33,6 @@ export const useOrder = (orderId) => {
return ({
order: data,
isLoading,
+ refetch,
});
};
diff --git a/src/common/hooks/useOrderLine/useOrderLine.js b/src/common/hooks/useOrderLine/useOrderLine.js
index 0edb65dbf..800abe732 100644
--- a/src/common/hooks/useOrderLine/useOrderLine.js
+++ b/src/common/hooks/useOrderLine/useOrderLine.js
@@ -7,20 +7,32 @@ import {
import { LINES_API } from '@folio/stripes-acq-components';
-export const useOrderLine = (lineId) => {
- const ky = useOkapiKy();
- const [namespace] = useNamespace({ key: 'order-versions' });
+export const useOrderLine = (lineId, options = {}) => {
+ const {
+ enabled = true,
+ tenantId,
+ ...queryOptions
+ } = options;
- const { isLoading, data } = useQuery(
- [namespace, lineId],
- async () => ky.get(`${LINES_API}/${lineId}`).json(),
- {
- enabled: Boolean(lineId),
- },
- );
+ const [namespace] = useNamespace({ key: 'purchase-order-line' });
+ const ky = useOkapiKy({ tenant: tenantId });
+
+ const {
+ data,
+ isFetching,
+ isLoading,
+ refetch,
+ } = useQuery({
+ queryKey: [namespace, lineId, tenantId],
+ queryFn: ({ signal }) => ky.get(`${LINES_API}/${lineId}`, { signal }).json(),
+ enabled: enabled && Boolean(lineId),
+ ...queryOptions,
+ });
return ({
orderLine: data,
+ isFetching,
isLoading,
+ refetch,
});
};
diff --git a/src/components/LayerCollection/LayerPO.js b/src/components/LayerCollection/LayerPO.js
index 57585af11..cfbdb3043 100644
--- a/src/components/LayerCollection/LayerPO.js
+++ b/src/components/LayerCollection/LayerPO.js
@@ -1,12 +1,15 @@
-import React, { useState, useEffect, useCallback, useMemo } from 'react';
+import get from 'lodash/get';
import PropTypes from 'prop-types';
-import { get } from 'lodash';
+import {
+ useCallback,
+ useEffect,
+ useMemo,
+ useState,
+} from 'react';
import { FormattedMessage } from 'react-intl';
import { LoadingView } from '@folio/stripes/components';
-import {
- stripesConnect,
-} from '@folio/stripes/core';
+import { stripesConnect } from '@folio/stripes/core';
import {
baseManifest,
ORDER_STATUSES,
@@ -16,9 +19,15 @@ import {
useShowCallout,
} from '@folio/stripes-acq-components';
+import { SUBMIT_ACTION_FIELD } from '../../common/constants';
import {
- createOrEditOrderResource,
-} from '../Utils/orderResource';
+ useHandleOrderUpdateError,
+ useOrder,
+} from '../../common/hooks';
+import { SUBMIT_ACTION } from '../PurchaseOrder/constants';
+import POForm from '../PurchaseOrder/POForm';
+import { UpdateOrderErrorModal } from '../PurchaseOrder/UpdateOrderErrorModal';
+import { createOrEditOrderResource } from '../Utils/orderResource';
import {
ADDRESSES,
ORDER,
@@ -27,9 +36,6 @@ import {
ORDER_TEMPLATES,
USERS,
} from '../Utils/resources';
-import { useHandleOrderUpdateError } from '../../common/hooks/useHandleOrderUpdateError';
-import POForm from '../PurchaseOrder/POForm';
-import { UpdateOrderErrorModal } from '../PurchaseOrder/UpdateOrderErrorModal';
const NEW_ORDER = {
reEncumber: true,
@@ -53,8 +59,17 @@ function LayerPO({
const [isLoading, setIsLoading] = useState(true);
const [updateOrderError, setUpdateOrderError] = useState();
const [isErrorsModalOpened, toggleErrorsModal] = useModalToggle();
- const order = id ? resources?.order?.records[0] : NEW_ORDER;
+
const instanceId = location.state?.instanceId;
+ const instanceTenantId = location.state?.instanceTenantId;
+
+ const {
+ order: fetchedOrder,
+ isLoading: isOrderLoading,
+ refetch,
+ } = useOrder(id);
+
+ const order = id ? fetchedOrder : NEW_ORDER;
useEffect(() => {
memoizedMutator.orderNumber.reset();
@@ -73,33 +88,56 @@ function LayerPO({
setUpdateOrderError(errors);
}, [toggleErrorsModal]);
- const updatePO = useCallback(values => {
+ const updatePO = useCallback((values) => {
setIsLoading(true);
- setSavingValues(values);
- return createOrEditOrderResource(values, memoizedMutator.order)
- .then(savedOrder => {
+ const { [SUBMIT_ACTION_FIELD]: submitAction, ...data } = values;
+
+ setSavingValues(data);
+
+ return createOrEditOrderResource(data, mutator.order)
+ .then((savedOrder) => {
sendCallout({
message: ,
});
- history.push({
- pathname: instanceId ? `/orders/view/${savedOrder.id}/po-line/create` : `/orders/view/${savedOrder.id}`,
- search: location.search,
- state: instanceId ? { instanceId, instanceTenantId: location.state?.instanceTenantId } : {},
- });
+
+ return savedOrder;
+ })
+ .then(async (savedOrder) => {
+ setSavingValues(null);
+
+ switch (submitAction) {
+ case SUBMIT_ACTION.saveAndKeepEditing:
+ await refetch();
+
+ history.push({
+ pathname: `/orders/edit/${savedOrder.id}`,
+ search: location.search,
+ });
+ break;
+ case SUBMIT_ACTION.saveAndClose:
+ default:
+ history.push({
+ pathname: instanceId ? `/orders/view/${savedOrder.id}/po-line/create` : `/orders/view/${savedOrder.id}`,
+ search: location.search,
+ state: instanceId ? { instanceId, instanceTenantId } : {},
+ });
+ break;
+ }
})
.catch(async e => {
- setIsLoading(false);
await handleErrorResponse(e, openOrderErrorModalShow);
- });
+ })
+ .finally(() => setIsLoading(false));
}, [
handleErrorResponse,
history,
instanceId,
location.search,
- location.state?.instanceTenantId,
- memoizedMutator.order,
+ instanceTenantId,
+ mutator.order,
openOrderErrorModalShow,
+ refetch,
sendCallout,
]);
@@ -116,7 +154,14 @@ function LayerPO({
[history, id, location.search, instanceId],
);
- if (isLoading || !order) return ;
+ if (isLoading || isOrderLoading || !order) {
+ return (
+
+ );
+ }
const { poNumber, poNumberPrefix, poNumberSuffix } = order;
const generatedNumber = get(resources, 'orderNumber.records.0.poNumber');
@@ -152,7 +197,10 @@ function LayerPO({
}
LayerPO.manifest = Object.freeze({
- order: ORDER,
+ order: {
+ ...ORDER,
+ fetch: false,
+ },
addresses: ADDRESSES,
users: {
...USERS,
diff --git a/src/components/LayerCollection/LayerPO.test.js b/src/components/LayerCollection/LayerPO.test.js
index 47aecb364..8e9566996 100644
--- a/src/components/LayerCollection/LayerPO.test.js
+++ b/src/components/LayerCollection/LayerPO.test.js
@@ -1,6 +1,10 @@
import { MemoryRouter } from 'react-router';
-import { render, screen, waitFor } from '@folio/jest-config-stripes/testing-library/react';
+import {
+ render,
+ screen,
+ waitFor,
+} from '@folio/jest-config-stripes/testing-library/react';
import { ORDER_TYPES } from '@folio/stripes-acq-components';
import {
@@ -12,23 +16,26 @@ import {
history,
location,
} from 'fixtures/routerMocks';
+import { SUBMIT_ACTION_FIELD } from '../../common/constants';
+import { useOrder } from '../../common/hooks';
+import { SUBMIT_ACTION } from '../PurchaseOrder/constants';
import POForm from '../PurchaseOrder/POForm';
import LayerPO from './LayerPO';
+jest.mock('../../common/hooks', () => ({
+ ...jest.requireActual('../../common/hooks'),
+ useOrder: jest.fn(),
+}));
jest.mock('../PurchaseOrder/POForm', () => jest.fn().mockReturnValue('POForm'));
const defaultProps = {
resourses: {
- order: {
- records: [order],
- },
orderNumber: {
records: [{ poNumber: '10000' }],
},
},
mutator: {
order: {
- GET: jest.fn().mockResolvedValue([order]),
POST: jest.fn().mockResolvedValue([order]),
PUT: jest.fn().mockResolvedValue([order]),
},
@@ -73,9 +80,14 @@ const renderLayerPO = (props = {}) => render(
describe('LayerPO', () => {
beforeEach(() => {
- defaultProps.mutator.order.POST.mockClear();
- history.push.mockClear();
- POForm.mockClear();
+ useOrder.mockReturnValue({
+ order,
+ refetch: jest.fn(),
+ });
+ });
+
+ afterEach(() => {
+ jest.clearAllMocks();
});
it('should render PO form', async () => {
@@ -101,7 +113,22 @@ describe('LayerPO', () => {
orderType: ORDER_TYPES.ongoing,
}));
- expect(history.push).toHaveBeenCalled();
+ expect(history.push).toHaveBeenCalledWith(expect.objectContaining({
+ pathname: expect.stringMatching(/orders\/view/),
+ }));
+ });
+
+ it('should keep edit form opened if saveAndKeepEditing action was selected', async () => {
+ renderLayerPO();
+
+ await waitFor(() => POForm.mock.calls[0][0].onSubmit({
+ orderType: ORDER_TYPES.ongoing,
+ [SUBMIT_ACTION_FIELD]: SUBMIT_ACTION.saveAndKeepEditing,
+ }));
+
+ expect(history.push).toHaveBeenCalledWith(expect.objectContaining({
+ pathname: expect.stringMatching(/orders\/edit/),
+ }));
});
describe('Create from inventory', () => {
@@ -120,6 +147,7 @@ describe('LayerPO', () => {
await waitFor(() => POForm.mock.calls[0][0].onSubmit({
orderType: ORDER_TYPES.ongoing,
+ [SUBMIT_ACTION_FIELD]: SUBMIT_ACTION.saveAndClose,
}));
expect(history.push).toHaveBeenCalledWith(expect.objectContaining({
@@ -130,7 +158,7 @@ describe('LayerPO', () => {
});
it('should throw an error if the order update was failed ', async () => {
- defaultProps.mutator.order.POST.mockRejectedValue({});
+ defaultProps.mutator.order.POST.mockRejectedValueOnce({});
renderLayerPO();
diff --git a/src/components/LayerCollection/LayerPOLine.js b/src/components/LayerCollection/LayerPOLine.js
index ffc79d00d..c2c0d80bb 100644
--- a/src/components/LayerCollection/LayerPOLine.js
+++ b/src/components/LayerCollection/LayerPOLine.js
@@ -25,32 +25,34 @@ import {
LoadingView,
} from '@folio/stripes/components';
import {
- baseManifest,
DICT_CONTRIBUTOR_NAME_TYPES,
DICT_IDENTIFIER_TYPES,
getConfigSetting,
LIMIT_MAX,
materialTypesManifest,
ORDER_FORMATS,
+ ResponseErrorsContainer,
sourceValues,
useCentralOrderingContext,
useIntegrationConfigs,
useLocationsQuery,
useModalToggle,
+ useOrganization,
useShowCallout,
- VENDORS_API,
} from '@folio/stripes-acq-components';
import {
ERROR_CODES,
- WORKFLOW_STATUS,
+ SUBMIT_ACTION_FIELD,
VALIDATION_ERRORS,
+ WORKFLOW_STATUS,
} from '../../common/constants';
import {
useInstance,
useLinesLimit,
useOpenOrderSettings,
useOrder,
+ useOrderLine,
useTitleMutation,
} from '../../common/hooks';
import {
@@ -61,6 +63,7 @@ import {
import DuplicateLinesModal from '../../common/DuplicateLinesModal';
import {
DISCOUNT_TYPE,
+ SUBMIT_ACTION,
} from '../POLine/const';
import {
cloneOrder,
@@ -76,7 +79,6 @@ import {
ORDER_NUMBER,
ORDER_TEMPLATES,
ORDERS,
- VALIDATE_ISBN,
} from '../Utils/resources';
import { POLineForm } from '../POLine';
import LinesLimit from '../PurchaseOrder/LinesLimit';
@@ -96,6 +98,24 @@ const parseErrorMessage = (code) => {
const FIELD_ARRAYS_TO_HYDRATE = ['locations'];
+const handleVendorLoadingError = async (response, sendCallout) => {
+ const { handler } = await ResponseErrorsContainer.create(response);
+
+ sendCallout({
+ message: ,
+ type: 'error',
+ });
+
+ const message = handler.getError().message;
+
+ if (message) {
+ sendCallout({
+ message: ,
+ type: 'error',
+ });
+ }
+};
+
function LayerPOLine({
history,
location: { search, state: locationState },
@@ -105,47 +125,88 @@ function LayerPOLine({
stripes,
}) {
const intl = useIntl();
- const [isLinesLimitExceededModalOpened, setLinesLimitExceededModalOpened] = useState(false);
+ const sendCallout = useShowCallout();
+ const { isCentralOrderingEnabled } = useCentralOrderingContext();
+
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ const memoizedMutator = useMemo(() => mutator, []);
+
const [isDeletePiecesOpened, toggleDeletePieces] = useModalToggle();
const [isNotUniqueOpen, toggleNotUnique] = useModalToggle();
const [isDifferentAccountModalOpened, toggleDifferentAccountModal] = useModalToggle();
+
+ const [isLinesLimitExceededModalOpened, setIsLinesLimitExceededModalOpened] = useState(false);
const [accountNumbers, setAccountNumbers] = useState([]);
const [savingValues, setSavingValues] = useState();
- const sendCallout = useShowCallout();
- const [isLoading, setIsLoading] = useState(false);
- const { isFetching: isOpenOrderSettingsFetching, openOrderSettings } = useOpenOrderSettings();
- const { isOpenOrderEnabled, isDuplicateCheckDisabled } = openOrderSettings;
- const [isValidateDuplicateLines, setValidateDuplicateLines] = useState();
+ const [isProcessing, setIsProcessing] = useState(false);
+ const [isValidateDuplicateLines, setIsValidateDuplicateLines] = useState();
const [duplicateLines, setDuplicateLines] = useState();
- const { isLoading: isOrderLoading, order } = useOrder(id);
- const createInventory = get(resources, ['createInventory', 'records']);
- const createInventorySetting = useMemo(
- () => getCreateInventorySetting(createInventory),
- [createInventory],
- );
+
const [poLines, setPoLines] = useState();
- const poLine = poLines?.find((u) => u.id === lineId);
- const [vendor, setVendor] = useState();
- const { isLoading: isLinesLimitLoading, linesLimit } = useLinesLimit(!(lineId || poLine));
- const [isCreateAnotherChecked, setCreateAnotherChecked] = useState(locationState?.isCreateAnotherChecked);
- const { isFetching: isConfigsFetching, integrationConfigs } = useIntegrationConfigs({ organizationId: vendor?.id });
- const { instance, isLoading: isInstanceLoading } = useInstance(
- locationState?.instanceId,
- {
- tenantId: locationState?.instanceTenantId,
- },
- );
+
+ const locationStateInstanceId = locationState?.instanceId;
+ const isCreateFromInstance = Boolean(locationStateInstanceId);
+
const { mutateTitle } = useTitleMutation();
- const { isCentralOrderingEnabled } = useCentralOrderingContext();
+ /* Queries */
+ const {
+ orderLine: poLine,
+ isLoading: isOrderLineLoading,
+ refetch,
+ } = useOrderLine(lineId);
+
+ const {
+ isFetching: isOpenOrderSettingsFetching,
+ openOrderSettings,
+ } = useOpenOrderSettings();
+
+ const {
+ isLoading: isOrderLoading,
+ order,
+ } = useOrder(id);
+
+ const {
+ organization: vendor,
+ isLoading: isVendorLoading,
+ } = useOrganization(
+ order?.vendor,
+ { onError: ({ response }) => handleVendorLoadingError(response, sendCallout) },
+ );
+
+ const {
+ instance,
+ isLoading: isInstanceLoading,
+ } = useInstance(locationStateInstanceId, { tenantId: locationState?.instanceTenantId });
+
+ const {
+ isLoading: isLinesLimitLoading,
+ linesLimit,
+ } = useLinesLimit(!(lineId || poLine));
+
+ const {
+ isFetching: isConfigsFetching,
+ integrationConfigs,
+ } = useIntegrationConfigs({ organizationId: vendor?.id });
const {
isLoading: isLocationsLoading,
locations,
} = useLocationsQuery({ consortium: isCentralOrderingEnabled });
+ /* */
- // eslint-disable-next-line react-hooks/exhaustive-deps
- const memoizedMutator = useMemo(() => mutator, []);
+ const { isOpenOrderEnabled, isDuplicateCheckDisabled } = openOrderSettings;
+ const { isApprovalRequired } = getConfigSetting(get(resources, 'approvalsSetting.records', {}));
+ const createInventory = resources?.createInventory?.records;
+ const isOrderApproved = isApprovalRequired ? order?.approved : true;
+ const differentAccountsModalLabel = intl.formatMessage({ id: 'ui-orders.differentAccounts.title' });
+ const createInventorySetting = useMemo(() => getCreateInventorySetting(createInventory), [createInventory]);
+
+ const isSaveAndOpenButtonVisible = (
+ isOpenOrderEnabled &&
+ isOrderApproved &&
+ order?.workflowStatus === WORKFLOW_STATUS.pending
+ );
useEffect(() => {
setPoLines();
@@ -159,16 +220,16 @@ function LayerPOLine({
}, [id, memoizedMutator.poLines]);
useEffect(() => {
- setValidateDuplicateLines(!isDuplicateCheckDisabled);
+ setIsValidateDuplicateLines(!isDuplicateCheckDisabled);
}, [isDuplicateCheckDisabled]);
const openLineLimitExceededModal = useCallback(line => {
- setLinesLimitExceededModalOpened(true);
+ setIsLinesLimitExceededModalOpened(true);
setSavingValues(line);
}, []);
const closeLineLimitExceededModal = useCallback(() => {
- setLinesLimitExceededModalOpened(false);
+ setIsLinesLimitExceededModalOpened(false);
setSavingValues();
}, []);
@@ -214,48 +275,43 @@ function LayerPOLine({
[intl, openLineLimitExceededModal, sendCallout, toggleDeletePieces],
);
- const openOrder = useCallback(
- (saveAndOpen, newLine = {}) => {
- if (saveAndOpen) {
- const exportAccountNumbers = getExportAccountNumbers([...order.compositePoLines, newLine]);
+ const openOrder = useCallback((newLine = {}) => {
+ const exportAccountNumbers = getExportAccountNumbers([...order.compositePoLines, newLine]);
- if (!order.manualPo && exportAccountNumbers.length > 1) {
- setAccountNumbers(exportAccountNumbers);
+ if (!order.manualPo && exportAccountNumbers.length > 1) {
+ setAccountNumbers(exportAccountNumbers);
- // eslint-disable-next-line prefer-promise-reject-errors
- return Promise.reject({ validationError: VALIDATION_ERRORS.differentAccount });
- }
+ // eslint-disable-next-line prefer-promise-reject-errors
+ return Promise.reject({ validationError: VALIDATION_ERRORS.differentAccount });
+ }
- return updateOrderResource(order, memoizedMutator.lineOrder, {
- workflowStatus: WORKFLOW_STATUS.open,
- })
- .then(() => {
- sendCallout({
- message: (
-
- ),
- type: 'success',
- });
- })
- .catch(errorResponse => {
- sendCallout({
- message: (
-
- ),
- type: 'error',
- });
- throw errorResponse;
- });
- } else return Promise.resolve();
- },
- [memoizedMutator.lineOrder, order, sendCallout],
- );
+ return updateOrderResource(order, mutator.lineOrder, {
+ workflowStatus: WORKFLOW_STATUS.open,
+ })
+ .then(() => {
+ sendCallout({
+ message: (
+
+ ),
+ type: 'success',
+ });
+ })
+ .catch(errorResponse => {
+ sendCallout({
+ message: (
+
+ ),
+ type: 'error',
+ });
+ throw errorResponse;
+ });
+ }, [mutator.lineOrder, order, sendCallout]);
const formatPOLineBeforeSaving = (line) => {
switch (line.orderFormat) {
@@ -267,41 +323,61 @@ function LayerPOLine({
};
const submitPOLine = useCallback(async (lineValues) => {
- const { saveAndOpen, isAcknowledged, ...line } = lineValues;
- let savedLine;
+ const {
+ [SUBMIT_ACTION_FIELD]: submitAction,
+ isAcknowledged,
+ ...line
+ } = lineValues;
- setIsLoading(true);
+ let savedLine;
+ setIsProcessing(true);
setSavingValues(lineValues);
- try {
- setIsLoading(true);
+ try {
if (isValidateDuplicateLines) {
- setValidateDuplicateLines(false);
+ setIsValidateDuplicateLines(false);
await validateDuplicateLines(line, mutator, resources);
}
const newLine = formatPOLineBeforeSaving(cloneDeep(line));
- savedLine = await memoizedMutator.poLines.POST(newLine);
+ savedLine = await mutator.poLines.POST(newLine);
- await openOrder(saveAndOpen, savedLine);
+ let pathname = isCreateFromInstance
+ ? `/orders/view/${id}/po-line/view/${savedLine.id}`
+ : `/orders/view/${id}`;
- sendCallout({
- message: ,
- type: 'success',
- });
+ switch (submitAction) {
+ case SUBMIT_ACTION.saveAndOpen: {
+ await openOrder(savedLine);
+
+ if (isCreateFromInstance) {
+ pathname = `/inventory/view/${locationStateInstanceId}`;
+ }
- let pathname = isCreateAnotherChecked
- ? `/orders/view/${id}/po-line/create`
- : `/orders/view/${id}/po-line/view/${savedLine.id}`;
+ break;
+ }
+ case SUBMIT_ACTION.saveAndCreateAnother: {
+ pathname = `/orders/view/${id}/po-line/create`;
+ break;
+ }
+ case SUBMIT_ACTION.saveAndKeepEditing: {
+ await refetch();
- if (locationState?.instanceId) {
- pathname = saveAndOpen ? `/inventory/view/${locationState.instanceId}` : `/orders/view/${id}`;
+ pathname = `/orders/view/${id}/po-line/edit/${savedLine.id}`;
+ break;
+ }
+ case SUBMIT_ACTION.saveAndClose:
+ default:
+ break;
}
- const state = isCreateAnotherChecked ? { isCreateAnotherChecked: true } : {};
+ sendCallout({
+ message: ,
+ type: 'success',
+ });
setSavingValues();
@@ -319,7 +395,6 @@ function LayerPOLine({
return history.push({
pathname,
search,
- state,
});
} catch (e) {
if (e?.validationError === VALIDATION_ERRORS.duplicateLines) {
@@ -327,8 +402,9 @@ function LayerPOLine({
return toggleNotUnique();
}
- if (saveAndOpen && savedLine) {
- await memoizedMutator.poLines.DELETE(savedLine);
+
+ if (submitAction === SUBMIT_ACTION.saveAndOpen && savedLine) {
+ await mutator.poLines.DELETE(savedLine);
}
if (e?.validationError === VALIDATION_ERRORS.differentAccount) {
@@ -337,21 +413,22 @@ function LayerPOLine({
return handleErrorResponse(e, line);
} finally {
- setIsLoading(false);
+ setIsProcessing(false);
}
- },
-
- // eslint-disable-next-line react-hooks/exhaustive-deps
- [
- handleErrorResponse,
- history,
- id,
- isCreateAnotherChecked,
+ }, [
isValidateDuplicateLines,
- memoizedMutator.poLines,
- openOrder,
- search,
+ mutator,
+ isCreateFromInstance,
+ id,
sendCallout,
+ history,
+ search,
+ resources,
+ openOrder,
+ locationStateInstanceId,
+ refetch,
+ mutateTitle,
+ handleErrorResponse,
toggleNotUnique,
toggleDifferentAccountModal,
]);
@@ -359,12 +436,12 @@ function LayerPOLine({
const createNewOrder = useCallback(
async () => {
closeLineLimitExceededModal();
- setIsLoading(true);
+ setIsProcessing(true);
try {
const newOrder = await cloneOrder(
order,
- memoizedMutator.lineOrder,
+ mutator.lineOrder,
memoizedMutator.orderNumber,
savingValues && [savingValues],
);
@@ -374,7 +451,7 @@ function LayerPOLine({
search,
});
} catch (e) {
- setIsLoading(false);
+ setIsProcessing(false);
sendCallout({
message: ,
type: 'error',
@@ -384,7 +461,7 @@ function LayerPOLine({
[
closeLineLimitExceededModal,
history,
- memoizedMutator.lineOrder,
+ mutator.lineOrder,
memoizedMutator.orderNumber,
order,
savingValues,
@@ -405,21 +482,21 @@ function LayerPOLine({
}, [history, id, lineId, search, locationState]);
const updatePOLine = useCallback(async (hydratedLine) => {
- const { saveAndOpen, ...data } = hydratedLine;
+ const { [SUBMIT_ACTION_FIELD]: submitAction, ...data } = hydratedLine;
- setIsLoading(true);
+ setIsProcessing(true);
setSavingValues(hydratedLine);
if (isValidateDuplicateLines) {
try {
- setValidateDuplicateLines(false);
+ setIsValidateDuplicateLines(false);
await validateDuplicateLines(hydratedLine, mutator, resources);
} catch (e) {
if (e?.validationError === VALIDATION_ERRORS.duplicateLines) {
setDuplicateLines(e.duplicateLines);
- setIsLoading(false);
+ setIsProcessing(false);
return toggleNotUnique();
}
@@ -430,8 +507,7 @@ function LayerPOLine({
delete line.metadata;
- return memoizedMutator.poLines.PUT(line)
- .then(() => openOrder(saveAndOpen))
+ return mutator.poLines.PUT(line)
.then(() => {
sendCallout({
message: (
@@ -442,10 +518,27 @@ function LayerPOLine({
),
type: 'success',
});
- setTimeout(onCancel);
+ })
+ .then(async () => {
+ switch (submitAction) {
+ case SUBMIT_ACTION.saveAndOpen: {
+ await openOrder();
+ onCancel();
+ break;
+ }
+ case SUBMIT_ACTION.saveAndKeepEditing:
+ await refetch();
+ break;
+ case SUBMIT_ACTION.saveAndClose:
+ default:
+ onCancel();
+ break;
+ }
+
+ setIsProcessing(false);
})
.catch((e) => {
- setIsLoading(false);
+ setIsProcessing(false);
if (e?.validationError === VALIDATION_ERRORS.differentAccount) {
return toggleDifferentAccountModal();
@@ -453,16 +546,16 @@ function LayerPOLine({
return handleErrorResponse(e, line);
});
- },
- // eslint-disable-next-line react-hooks/exhaustive-deps
- [
- handleErrorResponse,
+ }, [
isValidateDuplicateLines,
- memoizedMutator.poLines,
+ mutator,
+ resources,
+ toggleNotUnique,
+ sendCallout,
+ refetch,
onCancel,
openOrder,
- sendCallout,
- toggleNotUnique,
+ handleErrorResponse,
toggleDifferentAccountModal,
]);
@@ -506,7 +599,7 @@ function LayerPOLine({
newObj.eresource.accessProvider = vendor.id;
newObj.physical.materialSupplier = vendor.id;
- if (vendor.discountPercent) {
+ if (vendor?.discountPercent) {
newObj.cost.discountType = DISCOUNT_TYPE.percentage;
newObj.cost.discount = vendor.discountPercent;
}
@@ -515,54 +608,6 @@ function LayerPOLine({
return newObj;
}, [createInventorySetting.eresource, createInventorySetting.physical, order, stripes.currency, vendor]);
- const vendorId = order?.vendor;
-
- useEffect(
- () => {
- if (vendorId) {
- memoizedMutator.orderVendor.GET({ path: `${VENDORS_API}/${vendorId}` })
- .then(
- setVendor,
- errorResponse => {
- setVendor({});
-
- let response;
-
- try {
- response = JSON.parse(errorResponse?.message);
- } catch (parsingException) {
- response = errorResponse;
- }
-
- sendCallout({
- message: ,
- type: 'error',
- });
-
- const message = response?.errors?.[0]?.message;
-
- if (message) {
- sendCallout({
- message: ,
- type: 'error',
- });
- }
- },
- );
- }
- },
- // eslint-disable-next-line react-hooks/exhaustive-deps
- [memoizedMutator.orderVendor, vendorId],
- );
-
- const { isApprovalRequired } = getConfigSetting(
- get(resources, 'approvalsSetting.records', {}),
- );
- const isOrderApproved = isApprovalRequired ? order?.approved : true;
- const isSaveAndOpenButtonVisible =
- isOpenOrderEnabled &&
- isOrderApproved &&
- order?.workflowStatus === WORKFLOW_STATUS.pending;
const isntLoaded = !(
get(resources, 'createInventory.hasLoaded') &&
!isOrderLoading &&
@@ -578,16 +623,23 @@ function LayerPOLine({
!isConfigsFetching &&
!isOpenOrderSettingsFetching &&
!isInstanceLoading &&
- !isLocationsLoading
+ !isLocationsLoading &&
+ !isOrderLineLoading &&
+ !isVendorLoading
);
- if (isLoading || isntLoaded) return ;
+ if (isProcessing || isntLoaded) {
+ return (
+
+ );
+ }
const initialValues = lineId ? poLine : getCreatePOLIneInitialValues;
const onSubmit = lineId ? updatePOLine : submitPOLine;
- const differentAccountsModalLabel = intl.formatMessage({ id: 'ui-orders.differentAccounts.title' });
-
return (
<>
{
toggleNotUnique();
- setValidateDuplicateLines(false);
+ setIsValidateDuplicateLines(false);
onSubmit(savingValues);
}}
onCancel={() => {
toggleNotUnique();
- setValidateDuplicateLines(true);
+ setIsValidateDuplicateLines(true);
}}
/>
)
@@ -665,11 +714,6 @@ LayerPOLine.manifest = Object.freeze({
approvalsSetting: APPROVALS_SETTING,
[DICT_CONTRIBUTOR_NAME_TYPES]: CONTRIBUTOR_NAME_TYPES,
poLines: ORDER_LINES,
- orderVendor: {
- ...baseManifest,
- accumulate: true,
- fetch: false,
- },
createInventory: CREATE_INVENTORY,
orderTemplates: {
...ORDER_TEMPLATES,
@@ -680,7 +724,6 @@ LayerPOLine.manifest = Object.freeze({
accumulate: false,
fetch: true,
},
- validateISBN: VALIDATE_ISBN,
convertToIsbn13: CONVERT_TO_ISBN13,
[DICT_IDENTIFIER_TYPES]: IDENTIFIER_TYPES,
orderNumber: ORDER_NUMBER,
diff --git a/src/components/LayerCollection/LayerPOLine.test.js b/src/components/LayerCollection/LayerPOLine.test.js
index 305d56f01..f82a98858 100644
--- a/src/components/LayerCollection/LayerPOLine.test.js
+++ b/src/components/LayerCollection/LayerPOLine.test.js
@@ -12,6 +12,7 @@ import {
} from '@folio/jest-config-stripes/testing-library/react';
import {
useLocationsQuery,
+ useOrganization,
useShowCallout,
} from '@folio/stripes-acq-components';
@@ -25,9 +26,15 @@ import {
location,
match,
} from 'fixtures/routerMocks';
-import { useOrder } from '../../common/hooks';
+import { SUBMIT_ACTION_FIELD } from '../../common/constants';
+import {
+ useOrder,
+ useOrderLine,
+} from '../../common/hooks';
import ModalDeletePieces from '../ModalDeletePieces';
+import { SUBMIT_ACTION } from '../POLine/const';
import POLineForm from '../POLine/POLineForm';
+import { updateOrderResource } from '../Utils/orderResource';
import LayerPOLine from './LayerPOLine';
jest.mock('@folio/stripes-acq-components', () => ({
@@ -35,12 +42,14 @@ jest.mock('@folio/stripes-acq-components', () => ({
useCentralOrderingContext: jest.fn(() => ({ isCentralOrderingEnabled: false })),
useIntegrationConfigs: jest.fn().mockReturnValue({ integrationConfigs: [], isLoading: false }),
useLocationsQuery: jest.fn(),
+ useOrganization: jest.fn(),
useShowCallout: jest.fn(),
}));
jest.mock('../../common/hooks', () => ({
useOpenOrderSettings: jest.fn().mockReturnValue({ isFetching: false, openOrderSettings: {} }),
useLinesLimit: jest.fn().mockReturnValue({ isLoading: false, linesLimit: 1 }),
useOrder: jest.fn(),
+ useOrderLine: jest.fn(),
useInstance: jest.fn().mockReturnValue({ isLoading: false, instance: {} }),
useTitleMutation: jest.fn().mockReturnValue({ mutateTitle: jest.fn().mockReturnValue(() => Promise.resolve()) }),
}));
@@ -50,13 +59,16 @@ jest.mock('../../common/utils', () => ({
...jest.requireActual('../../common/utils'),
validateDuplicateLines: jest.fn().mockReturnValue(Promise.resolve()),
}));
+jest.mock('../Utils/orderResource', () => ({
+ ...jest.requireActual('../Utils/orderResource'),
+ updateOrderResource: jest.fn(() => Promise.resolve()),
+}));
const queryClient = new QueryClient();
const defaultProps = {
mutator: {
lineOrder: {
- GET: jest.fn().mockResolvedValue(order),
POST: jest.fn().mockResolvedValue(order),
PUT: jest.fn().mockResolvedValue(order),
},
@@ -71,9 +83,6 @@ const defaultProps = {
PUT: jest.fn().mockResolvedValue(orderLine),
POST: jest.fn().mockResolvedValue(orderLine),
},
- orderVendor: {
- GET: jest.fn().mockResolvedValue(vendor),
- },
createInventory: {
GET: jest.fn().mockResolvedValue(),
},
@@ -83,9 +92,6 @@ const defaultProps = {
materialTypes: {
GET: jest.fn().mockResolvedValue(),
},
- validateISBN: {
- GET: jest.fn().mockResolvedValue(),
- },
identifierTypes: {
GET: jest.fn().mockResolvedValue(),
},
@@ -97,10 +103,6 @@ const defaultProps = {
createInventory: {
hasLoaded: true,
},
- lineOrder: {
- hasLoaded: true,
- records: [order],
- },
approvalsSetting: {
hasLoaded: true,
},
@@ -128,7 +130,6 @@ const defaultProps = {
history,
};
-// eslint-disable-next-line react/prop-types
const wrapper = ({ children }) => (
@@ -146,22 +147,26 @@ const renderLayerPOLine = (props = {}) => render(
);
const mockShowCallout = jest.fn();
+const refetchOrderLine = jest.fn();
describe('LayerPOLine', () => {
beforeEach(() => {
- POLineForm.mockClear();
- history.push.mockClear();
- defaultProps.mutator.poLines.PUT.mockClear();
- defaultProps.mutator.poLines.POST.mockClear();
- useOrder
- .mockClear()
- .mockReturnValue({ isLoading: false, order });
- useLocationsQuery
- .mockClear()
- .mockReturnValue({ locations: [location] });
- useShowCallout
- .mockClear()
- .mockReturnValue(mockShowCallout);
+ useOrder.mockReturnValue({
+ isLoading: false,
+ order: { ...order, compositePoLines: [] },
+ });
+ useOrderLine.mockReturnValue({
+ isLoading: false,
+ orderLine,
+ refetch: refetchOrderLine,
+ });
+ useOrganization.mockReturnValue({ organization: vendor });
+ useLocationsQuery.mockReturnValue({ locations: [location] });
+ useShowCallout.mockReturnValue(mockShowCallout);
+ });
+
+ afterEach(() => {
+ jest.clearAllMocks();
});
it('should render POLineForm', async () => {
@@ -180,7 +185,7 @@ describe('LayerPOLine', () => {
},
} });
- await waitFor(() => POLineForm.mock.calls[0][0].onSubmit({ saveAndOpen: false, isAcknowledged: true }));
+ await waitFor(() => POLineForm.mock.calls[0][0].onSubmit({ isAcknowledged: true }));
expect(defaultProps.mutator.poLines.POST).toHaveBeenCalled();
});
@@ -190,12 +195,89 @@ describe('LayerPOLine', () => {
await waitFor(() => POLineForm.mock.calls[0][0].onSubmit({
...orderLine,
- saveAndOpen: true,
+ [SUBMIT_ACTION_FIELD]: SUBMIT_ACTION.saveAndOpen,
}));
expect(defaultProps.mutator.poLines.PUT).toHaveBeenCalled();
});
+ describe('Alternative submit actions', () => {
+ const submitWithType = (submitAction) => () => {
+ return POLineForm.mock.calls[0][0].onSubmit({
+ ...orderLine,
+ [SUBMIT_ACTION_FIELD]: submitAction,
+ });
+ };
+
+ describe('Add PO Line', () => {
+ it('should create POLine and keep the edit form opened', async () => {
+ renderLayerPOLine({
+ match: {
+ ...match,
+ params: { id: order.id },
+ },
+ });
+
+ await waitFor(submitWithType(SUBMIT_ACTION.saveAndKeepEditing));
+
+ expect(defaultProps.mutator.poLines.POST).toHaveBeenCalled();
+ expect(history.push).toHaveBeenCalledWith(expect.objectContaining({
+ pathname: expect.stringMatching(/orders\/view\/.*\/po-line\/edit\/.*/),
+ }));
+ });
+
+ it('should create POLine and open the form for another PO Line', async () => {
+ renderLayerPOLine({
+ match: {
+ ...match,
+ params: { id: order.id },
+ },
+ });
+
+ await waitFor(submitWithType(SUBMIT_ACTION.saveAndCreateAnother));
+
+ expect(defaultProps.mutator.poLines.POST).toHaveBeenCalled();
+ expect(history.push).toHaveBeenCalledWith(expect.objectContaining({
+ pathname: expect.stringMatching(/orders\/view\/.*\/po-line\/create/),
+ }));
+ });
+
+ it('should create POLine and open the order', async () => {
+ renderLayerPOLine({
+ match: {
+ ...match,
+ params: { id: order.id },
+ },
+ });
+
+ await waitFor(submitWithType(SUBMIT_ACTION.saveAndOpen));
+
+ expect(defaultProps.mutator.poLines.POST).toHaveBeenCalled();
+ expect(updateOrderResource).toHaveBeenCalled();
+ });
+ });
+
+ describe('Update PO Line', () => {
+ it('should update POLine and keep the edit form opened', async () => {
+ renderLayerPOLine();
+
+ await waitFor(submitWithType(SUBMIT_ACTION.saveAndKeepEditing));
+
+ expect(defaultProps.mutator.poLines.PUT).toHaveBeenCalled();
+ expect(refetchOrderLine).toHaveBeenCalled();
+ });
+
+ it('should update POLine and open the order', async () => {
+ renderLayerPOLine();
+
+ await waitFor(submitWithType(SUBMIT_ACTION.saveAndOpen));
+
+ expect(defaultProps.mutator.poLines.PUT).toHaveBeenCalled();
+ expect(updateOrderResource).toHaveBeenCalled();
+ });
+ });
+ });
+
it('should call onCancel if cancelling', async () => {
renderLayerPOLine();
@@ -298,7 +380,7 @@ describe('LayerPOLine', () => {
renderLayerPOLine();
- await waitFor(() => POLineForm.mock.calls[0][0].onSubmit({ saveAndOpen: true }));
+ await waitFor(() => POLineForm.mock.calls[0][0].onSubmit({ [SUBMIT_ACTION_FIELD]: SUBMIT_ACTION.saveAndOpen }));
const modal = await screen.findByText(/ui-orders.differentAccounts.title/i);
@@ -310,6 +392,15 @@ describe('LayerPOLine', () => {
['someError', 'error message'],
['genericError', 'Invalid token'],
])('should handle \'%s\' error', async (code, message) => {
+ useOrganization.mockImplementationOnce(async (_id, { onError }) => {
+ await onError({
+ clone: jest.fn().mockReturnThis(),
+ json: jest.fn().mockResolvedValue({ errors: [{ message: '' }] }),
+ });
+
+ return { organization: null };
+ });
+
// eslint-disable-next-line prefer-promise-reject-errors
defaultProps.mutator.poLines.PUT.mockImplementation(() => Promise.reject({
errors: [{
diff --git a/src/components/POLine/POLineForm.css b/src/components/POLine/POLineForm.css
deleted file mode 100644
index 4aba2d2aa..000000000
--- a/src/components/POLine/POLineForm.css
+++ /dev/null
@@ -1,3 +0,0 @@
-.createAnotherCheckbox {
- margin-right: 10px;
-};
diff --git a/src/components/POLine/POLineForm.js b/src/components/POLine/POLineForm.js
index 80a160b79..4920fd2c8 100644
--- a/src/components/POLine/POLineForm.js
+++ b/src/components/POLine/POLineForm.js
@@ -30,7 +30,6 @@ import {
AccordionSet,
AccordionStatus,
Button,
- Checkbox,
checkScope,
Col,
collapseAllSections,
@@ -44,6 +43,7 @@ import {
Pane,
PaneFooter,
PaneMenu,
+ Paneset,
Row,
Selection,
} from '@folio/stripes/components';
@@ -57,7 +57,10 @@ import {
ViewMetaData,
} from '@folio/stripes/smart-components';
-import { ENTITY_TYPE_PO_LINE } from '../../common/constants';
+import {
+ ENTITY_TYPE_PO_LINE,
+ SUBMIT_ACTION_FIELD,
+} from '../../common/constants';
import {
useErrorAccordionStatus,
useFundDistributionValidation,
@@ -89,6 +92,7 @@ import {
INITIAL_SECTIONS,
MAP_FIELD_ACCORDION,
POL_TEMPLATE_FIELDS_MAP,
+ SUBMIT_ACTION,
} from './const';
import getMaterialTypesForSelect from '../Utils/getMaterialTypesForSelect';
import getIdentifierTypesForSelect from '../Utils/getIdentifierTypesForSelect';
@@ -97,10 +101,8 @@ import getOrderTemplatesForSelect from '../Utils/getOrderTemplatesForSelect';
import { ifDisabledToChangePaymentInfo } from '../PurchaseOrder/util';
import getOrderTemplateValue from '../Utils/getOrderTemplateValue';
import calculateEstimatedPrice from './calculateEstimatedPrice';
-import { createPOLDataFromInstance } from './Item/util';
import { useManageDonorOrganizationIds } from './hooks';
-
-import styles from './POLineForm.css';
+import { createPOLDataFromInstance } from './Item/util';
const GAME_CHANGER_FIELDS = ['isPackage', 'orderFormat', 'checkinItems', 'packagePoLineId', 'instanceId'];
const GAME_CHANGER_TIMEOUT = 50;
@@ -123,8 +125,6 @@ function POLineForm({
enableSaveBtn,
linesLimit,
locations,
- isCreateAnotherChecked = false,
- toggleCreateAnother,
integrationConfigs = [],
instance,
isCreateFromInstance = false,
@@ -138,7 +138,6 @@ function POLineForm({
const identifierTypes = getIdentifierTypesForSelect(parentResources);
const lineId = get(initialValues, 'id');
- const saveBtnLabelId = isCreateAnotherChecked ? 'ui-orders.buttons.line.save' : 'stripes-components.saveAndClose';
const initialDonorOrganizationIds = get(initialValues, 'donorOrganizationIds', []);
const fundDistribution = get(formValues, 'fundDistribution', []);
const lineLocations = get(formValues, 'locations', []);
@@ -299,67 +298,99 @@ function POLineForm({
)
);
- const submitAndOpen = useCallback(() => {
- change('saveAndOpen', true);
+ const onSaveAndClose = useCallback(() => {
+ change(SUBMIT_ACTION_FIELD, SUBMIT_ACTION.saveAndClose);
+ handleSubmit();
+ }, [change, handleSubmit]);
+
+ const onSaveAndCreateAnother = useCallback(() => {
+ change(SUBMIT_ACTION_FIELD, SUBMIT_ACTION.saveAndCreateAnother);
+ handleSubmit();
+ }, [change, handleSubmit]);
+
+ const onSaveAndOpen = useCallback(() => {
+ change(SUBMIT_ACTION_FIELD, SUBMIT_ACTION.saveAndOpen);
handleSubmit();
}, [change, handleSubmit]);
- const submit = useCallback(() => {
- change('saveAndOpen', false);
+ const onSaveAndKeepEditing = useCallback(() => {
+ change(SUBMIT_ACTION_FIELD, SUBMIT_ACTION.saveAndKeepEditing);
handleSubmit();
}, [change, handleSubmit]);
const getPaneFooter = () => {
+ const isSubmitBtnDisabled = !enableSaveBtn && (pristine || submitting);
+
const start = (
-
- {([btnLabel]) => (
+
+
- )}
-
+
+
);
- const buttonSaveStyle = isSaveAndOpenButtonVisible ? 'default mega' : 'primary mega';
-
const end = (
- <>
+
+
+
+
+
{!isCreateFromInstance && !lineId && (linesLimit > 1) && (
- }
- checked={isCreateAnotherChecked}
- onChange={e => toggleCreateAnother(e.target.checked)}
- className={styles.createAnotherCheckbox}
- inline
- />
+
+
+
)}
-
- {isSaveAndOpenButtonVisible && (
+
+
+
+
+ {isSaveAndOpenButtonVisible && (
+
+
+
)}
- >
+
);
return (
@@ -491,223 +522,225 @@ function POLineForm({
isWithinScope={checkScope}
scope={document.body}
>
-
-
- {({ status }) => (
-
+ )}
+
+
+
);
}
@@ -737,8 +770,6 @@ POLineForm.propTypes = {
libraryId: PropTypes.string.isRequired,
tenantId: PropTypes.string,
})).isRequired,
- isCreateAnotherChecked: PropTypes.bool,
- toggleCreateAnother: PropTypes.func.isRequired,
integrationConfigs: PropTypes.arrayOf(PropTypes.object),
instance: PropTypes.object,
isCreateFromInstance: PropTypes.bool,
diff --git a/src/components/POLine/POLineForm.test.js b/src/components/POLine/POLineForm.test.js
index e9a14cb7c..cd7ee455b 100644
--- a/src/components/POLine/POLineForm.test.js
+++ b/src/components/POLine/POLineForm.test.js
@@ -123,8 +123,6 @@ const defaultProps = {
}],
},
},
- toggleCreateAnother: jest.fn(),
- isCreateAnotherChecked: false,
linesLimit: 3,
values: {
orderFormat: 'P/E Mix',
@@ -221,7 +219,7 @@ describe('POLineForm', () => {
renderPOLineForm();
await waitFor(() => {
- expect(screen.getByText('ui-orders.buttons.line.createAnother')).toBeInTheDocument();
+ expect(screen.getByText('ui-orders.buttons.line.saveAndCreateAnother')).toBeInTheDocument();
});
});
@@ -229,7 +227,7 @@ describe('POLineForm', () => {
renderPOLineForm({ isCreateFromInstance: true });
await waitFor(() => {
- expect(screen.queryByText('ui-orders.buttons.line.createAnother')).not.toBeInTheDocument();
+ expect(screen.queryByText('ui-orders.buttons.line.saveAndCreateAnother')).not.toBeInTheDocument();
});
});
});
diff --git a/src/components/POLine/const.js b/src/components/POLine/const.js
index ec05028e6..abb43d59f 100644
--- a/src/components/POLine/const.js
+++ b/src/components/POLine/const.js
@@ -87,3 +87,10 @@ export const ACCOUNT_STATUS = {
INACTIVE: 'Inactive',
PENDING: 'Pending',
};
+
+export const SUBMIT_ACTION = {
+ saveAndClose: 'saveAndClose',
+ saveAndCreateAnother: 'saveAndCreateAnother',
+ saveAndKeepEditing: 'saveAndKeepEditing',
+ saveAndOpen: 'saveAndOpen',
+};
diff --git a/src/components/PurchaseOrder/PO.js b/src/components/PurchaseOrder/PO.js
index 39fa39820..99a2d450a 100644
--- a/src/components/PurchaseOrder/PO.js
+++ b/src/components/PurchaseOrder/PO.js
@@ -1,9 +1,18 @@
/* eslint-disable max-lines */
-import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
-import { FormattedMessage, useIntl } from 'react-intl';
import PropTypes from 'prop-types';
+import get from 'lodash/get';
+import {
+ useCallback,
+ useEffect,
+ useMemo,
+ useRef,
+ useState,
+} from 'react';
+import {
+ FormattedMessage,
+ useIntl,
+} from 'react-intl';
import ReactRouterPropTypes from 'react-router-prop-types';
-import { get } from 'lodash';
import {
IfPermission,
@@ -28,22 +37,23 @@ import {
AccordionSet,
AccordionStatus,
Button,
- Col,
checkScope,
+ Col,
collapseAllSections,
ConfirmationModal,
Dropdown,
DropdownMenu,
+ ErrorModal,
ExpandAllButton,
expandAllSections,
HasCommand,
Icon,
Loading,
LoadingPane,
+ MenuSection,
Pane,
PaneMenu,
Row,
- ErrorModal,
} from '@folio/stripes/components';
import {
ColumnManagerMenu,
@@ -99,15 +109,15 @@ import {
ORDERS,
} from '../Utils/resources';
import CloseOrderModal from './CloseOrder';
-import OpenOrderConfirmationModal from './OpenOrderConfirmationModal';
-import LineListing from './LineListing';
-import LinesLimit from './LinesLimit';
-import POInvoicesContainer from './POInvoices';
import { LINE_LISTING_COLUMN_MAPPING } from './constants';
import { getPOActionMenu } from './getPOActionMenu';
import { useOrderMutation } from './hooks';
+import LineListing from './LineListing';
+import LinesLimit from './LinesLimit';
import { OngoingOrderInfoView } from './OngoingOrderInfo';
+import OpenOrderConfirmationModal from './OpenOrderConfirmationModal';
import { PODetailsView } from './PODetails';
+import POInvoicesContainer from './POInvoices';
import { SummaryView } from './Summary';
import { UnopenOrderConfirmationModal } from './UnopenOrderConfirmationModal';
import { UpdateOrderErrorModal } from './UpdateOrderErrorModal';
@@ -600,19 +610,22 @@ const PO = ({
buttonProps={{ buttonStyle: 'primary' }}
>
-
-
-
+
+
+
+
+
+
(prev ? undefined : template?.hiddenFields || {}));
}, [template]);
+ const onSaveAndKeepEditing = useCallback(() => {
+ change(SUBMIT_ACTION_FIELD, SUBMIT_ACTION.saveAndKeepEditing);
+ handleSubmit();
+ }, [change, handleSubmit]);
+
+ const onSaveAndClose = useCallback(() => {
+ change(SUBMIT_ACTION_FIELD, SUBMIT_ACTION.saveAndClose);
+ handleSubmit();
+ }, [change, handleSubmit]);
+
const firstMenu = useMemo(() => {
return (
@@ -161,34 +181,58 @@ const POForm = ({
), [hiddenFields, template, toggleForceVisibility]);
const getPaneFooter = useCallback((id, label) => {
+ const isSubmitBtnDisabled = pristine || submitting;
+
const start = (
-
- {(btnLabel) => (
-
- )}
-
+
+
+
+ {(btnLabel) => (
+
+ )}
+
+
+
);
const end = (
-
- {btnLabel => (
-
+
+ {!instanceId && (
+
+
+
)}
-
+
+
+
+ {btnLabel => (
+
+ )}
+
+
+
);
return (
@@ -197,7 +241,14 @@ const POForm = ({
renderEnd={end}
/>
);
- }, [handleSubmit, onCancel, pristine, submitting]);
+ }, [
+ instanceId,
+ onCancel,
+ onSaveAndClose,
+ onSaveAndKeepEditing,
+ pristine,
+ submitting,
+ ]);
const onChangeTemplate = useCallback((value) => {
const templateValue = getOrderTemplateValue(parentResources, value);
@@ -246,9 +297,9 @@ const POForm = ({
?
: ;
- const buttonLabelId = instanceId ? 'ui-orders.paneMenu.addPOLine' : 'ui-orders.paneMenu.saveOrder';
+ const buttonLabelId = instanceId ? 'ui-orders.paneMenu.addPOLine' : 'stripes-components.saveAndClose';
const paneFooter = initialValues.id
- ? getPaneFooter('clickable-update-purchase-order', 'ui-orders.paneMenu.saveOrder')
+ ? getPaneFooter('clickable-update-purchase-order', 'stripes-components.saveAndClose')
: getPaneFooter('clickable-create-new-purchase-order', buttonLabelId);
const prefixesSetting = get(parentResources, 'prefixesSetting.records', [])
@@ -296,108 +347,106 @@ const POForm = ({
}
return (
-
-
-
-
-
- {({ status }) => (
-
-
+
+
+
+
+
+
+
+
+ )}
+
+
+
+
);
};
diff --git a/src/components/PurchaseOrder/constants.js b/src/components/PurchaseOrder/constants.js
index 2de65e830..7664e4e6c 100644
--- a/src/components/PurchaseOrder/constants.js
+++ b/src/components/PurchaseOrder/constants.js
@@ -32,3 +32,8 @@ export const MAP_FIELD_ACCORDION = {
orderType: ACCORDION_ID.purchaseOrder,
notes: ACCORDION_ID.purchaseOrder,
};
+
+export const SUBMIT_ACTION = {
+ saveAndClose: 'saveAndClose',
+ saveAndKeepEditing: 'saveAndKeepEditing',
+};
diff --git a/translations/ui-orders/en.json b/translations/ui-orders/en.json
index 3bc900440..833e45301 100644
--- a/translations/ui-orders/en.json
+++ b/translations/ui-orders/en.json
@@ -23,9 +23,8 @@
"button.printLine": "Print order line",
"buttons.line.cancel": "Cancel",
"buttons.line.close": "Close",
- "buttons.line.save": "Save",
"buttons.line.saveAndOpen": "Save & open order",
- "buttons.line.createAnother": "Create another",
+ "buttons.line.saveAndCreateAnother": "Save & create another",
"buttons.line.submit": "Submit",
"buttons.line.changeInstance": "Change instance connection",
"canceled": "Canceled",
@@ -496,7 +495,6 @@
"paneMenu.addPOLine": "Add POL",
"paneMenu.createPurchaseOrder": "Create purchase order",
"paneMenu.editOrder": "Edit order",
- "paneMenu.saveOrder": "Save & close",
"paneBlock.approveBtn": "Approve",
"payment_status.awaitingPayment": "Awaiting payment",
"payment_status.cancelled": "Cancelled",