Skip to content

Commit

Permalink
UINV-470: Invoices - Show in version history record view, which field…
Browse files Browse the repository at this point in the history
…s have been edited
  • Loading branch information
alisher-epam committed Nov 14, 2024
1 parent 5cb7181 commit 9feae21
Show file tree
Hide file tree
Showing 12 changed files with 664 additions and 119 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

* Open version history pane on click changelog icon. Refs UINV-468.
* Display all invoice versions in change log in fourth pane. Refs UINV-469.
* Invoices - Show in version history record view, which fields have been edited. Refs UINV-470.

## [6.1.0](https://github.com/folio-org/ui-invoice/tree/v6.1.0) (2024-10-31)
[Full Changelog](https://github.com/folio-org/ui-invoice/compare/v6.0.4...v6.1.0)
Expand Down
1 change: 1 addition & 0 deletions src/common/hooks/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export * from './useOrderLine';
export * from './useOrderLines';
export * from './useOrders';
export * from './usePayableFiscalYears';
export * from './useSelectedInvoiceVersion';
export * from './useVendors';
export * from './useVoucherById';
export * from './useVoucherByInvoiceId';
Expand Down
1 change: 1 addition & 0 deletions src/common/hooks/useSelectedInvoiceVersion/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { useSelectedInvoiceVersion } from './useSelectedInvoiceVersion';
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
import {
filter,
flow,
get,
keyBy,
uniq,
} from 'lodash/fp';
import { useMemo } from 'react';
import { useIntl } from 'react-intl';
import { useQuery } from 'react-query';

import {
useNamespace,
useOkapiKy,
} from '@folio/stripes/core';
import { getFullName } from '@folio/stripes/util';
import {
ACQUISITIONS_UNITS_API,
CONFIG_ADDRESSES,
CONFIG_API,
LIMIT_MAX,
MODULE_TENANT,
fetchExportDataByIds,
fetchOrganizationsByIds,
getAddresses,
useUsersBatch,
} from '@folio/stripes-acq-components';

import { useInvoice } from '../useInvoice';

const getUniqItems = (arr) => (
flow(
uniq,
filter(Boolean),
)(arr)
);

export const getTenantAddresses = (ky) => async () => {
const searchParams = {
limit: LIMIT_MAX,
query: `(module=${MODULE_TENANT} and configName=${CONFIG_ADDRESSES})`,
};

return ky.get(CONFIG_API, { searchParams }).json();
};

export const getVersionMetadata = (version, entity) => ({
...get(entity, 'metadata', {}),
updatedByUserId: version?.userId,
updatedDate: version?.actionDate,
});

export const useSelectedInvoiceVersion = ({ versionId, versions, snapshotPath }, options = {}) => {
const intl = useIntl();
const ky = useOkapiKy();
const [namespace] = useNamespace({ key: 'selected-invoice-version' });

const deletedRecordLabel = intl.formatMessage({ id: 'stripes-acq-components.versionHistory.deletedRecord' });
const currentVersion = useMemo(() => (
versions?.find(({ id }) => id === versionId)
), [versionId, versions]);
const versionSnapshot = useMemo(() => (
get(snapshotPath, currentVersion)
), [snapshotPath, currentVersion]);

const {
invoice,
isLoading: isInvoiceLoading,
} = useInvoice(currentVersion?.id);

const metadata = useMemo(() => getVersionMetadata(currentVersion, invoice), [currentVersion, invoice]);
const assignedToId = versionSnapshot?.assignedTo;
const createdByUserId = metadata?.createdByUserId;
const vendorId = versionSnapshot?.vendor;
const billToId = versionSnapshot?.billTo;

const versionUserIds = useMemo(() => getUniqItems([assignedToId, createdByUserId]), [assignedToId, createdByUserId]);
const {
users,
isLoading: isUsersLoading,
} = useUsersBatch(versionUserIds);
const versionUsersMap = keyBy('id', users);

const {
isLoading: isVersionDataLoading,
data: selectedVersion = {},
} = useQuery(
[namespace, versionId, versionSnapshot?.id],
async () => {
const organizationIds = [vendorId];
const acqUnitsIds = versionSnapshot?.acqUnitIds || [];

const [
organizationsMap,
acqUnitsMap,
addressesMap,
] = await Promise.all([
fetchOrganizationsByIds(ky)(organizationIds).then(keyBy('id')),
fetchExportDataByIds({ ky, ids: acqUnitsIds, api: ACQUISITIONS_UNITS_API, records: 'acquisitionsUnits' })(acqUnitsIds).then(keyBy('id')),
getTenantAddresses(ky)()
.then(({ configs }) => getAddresses(configs))
.then(keyBy('id')),
]);

const assignedTo = versionUsersMap[assignedToId]
? getFullName(versionUsersMap[assignedToId])
: deletedRecordLabel;

const createdByUser = versionUsersMap[createdByUserId]
? getFullName(versionUsersMap[createdByUserId])
: deletedRecordLabel;

return {
...versionSnapshot,
acqUnits: acqUnitsIds.map(acqUnitsId => acqUnitsMap[acqUnitsId]?.name || deletedRecordLabel).join(', '),
assignedTo: assignedToId && assignedTo,
vendor: organizationsMap[vendorId]?.name || deletedRecordLabel,
createdByUser: createdByUserId && createdByUser,
billTo: billToId && (addressesMap[billToId]?.address || deletedRecordLabel),
metadata,
};
},
{
enabled: Boolean(
versionId
&& invoice?.id
&& !isInvoiceLoading
&& !isUsersLoading,
),
...options,
},
);

const isLoading = (
isInvoiceLoading
|| isUsersLoading
|| isVersionDataLoading
);

return {
selectedVersion,
isLoading,
};
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import {
QueryClient,
QueryClientProvider,
} from 'react-query';

import { renderHook, waitFor } from '@folio/jest-config-stripes/testing-library/react';
import { useOkapiKy } from '@folio/stripes/core';
import { getFullName } from '@folio/stripes/util';
import {
ACQUISITIONS_UNITS_API,
CONFIG_API,
ORDERS_API,
useUsersBatch,
VENDORS_API,
} from '@folio/stripes-acq-components';
import {
acqUnit,
address,
orderAuditEvent,
vendor,
} from '@folio/stripes-acq-components/test/jest/fixtures';
import { useSelectedInvoiceVersion } from './useSelectedInvoiceVersion';

jest.mock('@folio/stripes-acq-components/lib/hooks/useUsersBatch', () => ({
useUsersBatch: jest.fn(() => ({ users: [], isLoading: false })),
}));

const order = {
...orderAuditEvent.orderSnapshot,
};

const user = {
id: '58edf8c3-89e4-559c-9aed-aae637a3f40b',
personal: { firstName: 'Galt', lastName: 'John' },
};

const kyMock = {
get: jest.fn((url) => ({
json: async () => {
if (url.startsWith(ACQUISITIONS_UNITS_API)) {
return { acquisitionsUnits: [acqUnit] };
}
if (url.startsWith(CONFIG_API)) {
return { configs: [address] };
}
if (url.startsWith(VENDORS_API)) {
return { organizations: [vendor] };
}
if (url.startsWith(ORDERS_API)) {
return { ...order };
}

return {};
},
})),
};

const queryClient = new QueryClient();

// eslint-disable-next-line react/prop-types
const wrapper = ({ children }) => (
<QueryClientProvider client={queryClient}>
{children}
</QueryClientProvider>
);

describe('useSelectedInvoiceVersion', () => {
beforeEach(() => {
kyMock.get.mockClear();
useOkapiKy.mockClear().mockReturnValue(kyMock);
useUsersBatch.mockClear().mockReturnValue({
isLoading: false,
users: [user],
});
});

it('should return Invoice version data', async () => {
const { result } = renderHook(() => useSelectedInvoiceVersion({
versionId: orderAuditEvent.id,
versions: [{
...orderAuditEvent,
orderSnapshot: {
...orderAuditEvent.orderSnapshot,
acqUnitIds: ['acqUnitId'],
billTo: address.id,
vendor: vendor.id,
},
}],
snapshotPath: 'orderSnapshot',
}), { wrapper });

await waitFor(() => expect(result.current.isLoading).toBeFalsy());

const {
id,
billTo,
createdByUser,
shipTo,
vendor: vendorField,
} = result.current.selectedVersion;

expect(id).toEqual(order.id);
expect(vendorField).toEqual(vendor.name);
expect(billTo).toEqual('stripes-acq-components.versionHistory.deletedRecord');
expect(shipTo).toEqual('stripes-acq-components.versionHistory.deletedRecord');
expect(createdByUser).toEqual(getFullName(user));
});
});
15 changes: 10 additions & 5 deletions src/invoices/VersionHistory/VersionHistory.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,21 @@ import ReactRouterPropTypes from 'react-router-prop-types';
import { TitleManager } from '@folio/stripes/core';
import {
VersionHistoryPane,
VersionView,
VersionViewContextProvider,
} from '@folio/stripes-acq-components';

import { INVOICE_ROUTE } from '../../common/constants';
import {
useInvoice,
useInvoiceVersions,
useSelectedInvoiceVersion,
} from '../../common/hooks';
import {
HIDDEN_INVOICE_FIELDS,
INVOICE_FIELDS_LABEL_MAP,
} from './constants';
import VersionView from './VersionView';
import { VersionHistoryView } from './VersionHistoryView';

const VersionHistory = ({
history,
Expand Down Expand Up @@ -60,7 +62,12 @@ const VersionHistory = ({
},
);

const isLoading = isInvoiceLoading || isInvoiceVersionsLoading;
const {
isLoading: isSelectedVersionLoading,
selectedVersion,
} = useSelectedInvoiceVersion({ versionId, versions, snapshotPath });

const isLoading = isInvoiceLoading || isInvoiceVersionsLoading || isSelectedVersionLoading;

return (
<VersionViewContextProvider
Expand All @@ -82,9 +89,7 @@ const VersionHistory = ({
)}
tags={get(invoice, 'tags.tagList', [])}
>
{
// TODO Implement displaying which fields have been edited UINV-470 - https://folio-org.atlassian.net/browse/UINV-470
}
<VersionHistoryView version={selectedVersion} />
</VersionView>

<VersionHistoryPane
Expand Down
Loading

0 comments on commit 9feae21

Please sign in to comment.