Skip to content

Commit

Permalink
UINV-522: display exchanged amount
Browse files Browse the repository at this point in the history
  • Loading branch information
alisher-epam committed Feb 5, 2024
1 parent cfffaa0 commit 9336bbf
Show file tree
Hide file tree
Showing 19 changed files with 307 additions and 93 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import PropTypes from 'prop-types';
import { FormattedMessage } from 'react-intl';

import { AmountWithCurrencyField } from '@folio/stripes-acq-components';
import { KeyValue } from '@folio/stripes/components';
import { useStripes } from '@folio/stripes/core';

import { useExchangeCalculation } from '../../hooks';

export const DisplayExchangedAmount = ({ currency, exchangeRate, total }) => {
const stripes = useStripes();

const systemCurrency = stripes.currency;
const enabled = systemCurrency !== currency;

const { exchangedAmount } = useExchangeCalculation({
to: systemCurrency,
from: currency,
amount: +total,
rate: +exchangeRate,
},
{ enabled });

if (!enabled) {
return null;
}

return (
<KeyValue label={<FormattedMessage id="ui-invoice.invoice.details.information.calculatedTotalExchangeAmount" />}>
<AmountWithCurrencyField
amount={exchangedAmount || total}
currency={systemCurrency}
/>
</KeyValue>
);
};

DisplayExchangedAmount.propTypes = {
currency: PropTypes.string,
exchangeRate: PropTypes.number,
total: PropTypes.number,
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { render, screen } from '@folio/jest-config-stripes/testing-library/react';

import { useExchangeCalculation } from '../../hooks';
import { DisplayExchangedAmount } from './DisplayExchangedAmount';

jest.mock('@folio/stripes-acq-components', () => ({
...jest.requireActual('@folio/stripes-acq-components'),
AmountWithCurrencyField: jest.fn(() => 'AmountWithCurrencyField'),
}));
jest.mock('../../hooks', () => ({
...jest.requireActual('../../hooks'),
useExchangeCalculation: jest.fn(),
}));

const renderComponent = (props = {}) => render(
<DisplayExchangedAmount {...props} />,
);

describe('DisplayExchangedAmount', () => {
beforeEach(() => {
useExchangeCalculation.mockClear().mockReturnValue({
isLoading: false,
exchangedAmount: 30,
});
});

it('should not render component', async () => {
renderComponent({
currency: 'USD',
exchangeRate: 1,
total: 30,
});

expect(screen.queryByText(/30/)).not.toBeInTheDocument();
});

it('should render calculated exchange amount', async () => {
renderComponent({
currency: 'EUR',
exchangeRate: 1,
total: 30,
});

expect(screen.getByText('ui-invoice.invoice.details.information.calculatedTotalExchangeAmount')).toBeInTheDocument();
});
});
1 change: 1 addition & 0 deletions src/common/components/DisplayExchangedAmount/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { DisplayExchangedAmount } from './DisplayExchangedAmount';
1 change: 1 addition & 0 deletions src/common/components/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export {
CredentialsFieldsGroup,
CredentialsToggle,
} from './credentials';
export { DisplayExchangedAmount } from './DisplayExchangedAmount';
export { default as DuplicateInvoiceList } from './DuplicateInvoiceList';
export * from './FieldFiscalYear';
export * from './FiscalYearValue';
Expand Down
1 change: 1 addition & 0 deletions src/common/constants/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ export const BATCH_GROUPS_API = 'batch-groups';
export const BATCH_VOUCHERS_API = 'batch-voucher/batch-vouchers';
export const BATCH_VOUCHER_EXPORTS_API = 'batch-voucher/batch-voucher-exports';
export const CATEGORIES_API = 'organizations-storage/categories';
export const CALCULATE_EXCHANGE_API = 'finance/calculate-exchange';
export const EXPORT_CONFIGURATIONS_API = 'batch-voucher/export-configurations';
export const FISCAL_YEARS_API = 'finance/fiscal-years';
export const INVOICE_DOCUMENTS_API = '/documents';
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 @@ -4,6 +4,7 @@ export * from './useConfigsAdjustments';
export * from './useFiscalYear';
export * from './useFiscalYears';
export * from './useFundDistributionValidation';
export * from './useExchangeCalculation';
export * from './useInvoice';
export * from './useInvoiceMutation';
export * from './useInvoiceLine';
Expand Down
1 change: 1 addition & 0 deletions src/common/hooks/useExchangeCalculation/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { useExchangeCalculation } from './useExchangeCalculation';
36 changes: 36 additions & 0 deletions src/common/hooks/useExchangeCalculation/useExchangeCalculation.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { useQuery } from 'react-query';

import {
useOkapiKy,
useNamespace,
} from '@folio/stripes/core';

import { CALCULATE_EXCHANGE_API } from '../../constants';

export const useExchangeCalculation = ({ from, to, amount, rate }, options = {}) => {
const { enabled = true, ...otherOptions } = options;
const ky = useOkapiKy();
const [namespace] = useNamespace({ key: 'exchange-calculation' });

const searchParams = {
from,
to,
amount,
rate,
};

const { data, isLoading } = useQuery(
[namespace, amount, rate, from],
({ signal }) => ky.get(`${CALCULATE_EXCHANGE_API}`, { searchParams, signal }).json(),
{
keepPreviousData: true,
...otherOptions,
enabled: enabled && Boolean(amount && from && to),
},
);

return ({
exchangedAmount: data,
isLoading,
});
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import {
QueryClient,
QueryClientProvider,
} from 'react-query';

import { renderHook, waitFor } from '@folio/jest-config-stripes/testing-library/react';
import { useOkapiKy } from '@folio/stripes/core';

import { CALCULATE_EXCHANGE_API } from '../../constants';
import { useExchangeCalculation } from './useExchangeCalculation';

const queryClient = new QueryClient();

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

const kyMock = {
get: jest.fn(() => ({
json: () => Promise.resolve(30),
})),
};

const searchParams = {
from: 'USD',
to: 'EUR',
amount: 100,
rate: 1.2,
};

describe('useExchangeCalculation', () => {
beforeEach(() => {
kyMock.get.mockClear();
useOkapiKy.mockClear().mockReturnValue(kyMock);
});

it('should return calculated exchange amount', async () => {
const { result } = renderHook(() => useExchangeCalculation(searchParams), { wrapper });

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

expect(kyMock.get).toHaveBeenCalledWith(
`${CALCULATE_EXCHANGE_API}`,
expect.objectContaining({
searchParams,
}),
);
expect(result.current.exchangedAmount).toEqual(30);
});
});
10 changes: 10 additions & 0 deletions src/invoices/InvoiceDetails/Information/Information.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {

import {
ApprovedBy,
DisplayExchangedAmount,
FiscalYearValueContainer as FiscalYearValue,
StatusValue,
} from '../../../common/components';
Expand All @@ -30,6 +31,7 @@ const Information = ({
approvalDate,
approvedBy,
batchGroupId,
exchangeRate,
fiscalYearId,
invoiceDate,
paymentDue,
Expand Down Expand Up @@ -182,6 +184,13 @@ const Information = ({
</Row>

<Row>
<Col xs={3}>
<DisplayExchangedAmount
currency={currency}
exchangeRate={exchangeRate}
total={total}
/>
</Col>
{isLockTotal && (
<Col xs={3} data-testid="lock-total-amount">
<KeyValue label={<FormattedMessage id="ui-invoice.invoice.lockTotalAmount" />}>
Expand All @@ -202,6 +211,7 @@ Information.propTypes = {
approvalDate: PropTypes.string,
approvedBy: PropTypes.string,
batchGroupId: PropTypes.string.isRequired,
exchangeRate: PropTypes.number,
invoiceDate: PropTypes.string.isRequired,
fiscalYearId: PropTypes.string,
paymentDue: PropTypes.string,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import Information from './Information';
jest.mock('../../../common/hooks', () => ({
...jest.requireActual('../../../common/hooks'),
useFiscalYear: jest.fn(() => ({ fiscalYear: { code: 'FY2023' } })),
useExchangeCalculation: jest.fn(() => ({ isLoading: false, exchangedAmount: 30 })),
}));
jest.mock('../BatchGroupValue', () => {
return () => <span>BatchGroupValue</span>;
Expand Down
1 change: 1 addition & 0 deletions src/invoices/InvoiceDetails/InvoiceDetails.js
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,7 @@ function InvoiceDetails({
approvalDate={invoice.approvalDate}
approvedBy={invoice.approvedBy}
batchGroupId={invoice.batchGroupId}
exchangeRate={invoice.exchangeRate}
fiscalYearId={invoice.fiscalYearId}
invoiceDate={invoice.invoiceDate}
paymentDate={invoice.paymentDate}
Expand Down
Loading

0 comments on commit 9336bbf

Please sign in to comment.