From 23f7a9fae6b2ba9c9a87b5865c754920f7274480 Mon Sep 17 00:00:00 2001 From: Azizjon Nurov Date: Tue, 24 Dec 2024 17:14:41 +0500 Subject: [PATCH 1/4] Exclude failed exchange rates --- lib/utils/invoiceExport/getInvoiceExportData.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/utils/invoiceExport/getInvoiceExportData.js b/lib/utils/invoiceExport/getInvoiceExportData.js index 1cb4e724..bbeeff3f 100644 --- a/lib/utils/invoiceExport/getInvoiceExportData.js +++ b/lib/utils/invoiceExport/getInvoiceExportData.js @@ -81,9 +81,10 @@ export const getInvoiceExportData = async ({ ky, intl, query, currency: to }) => }); const addresses = getAddresses(addressRecords); const currencies = uniq(exportInvoices.map(({ currency }) => currency)); - const exchangeRates = await Promise.all( + const allExchangeRates = await Promise.allSettled( currencies.map(from => ky.get(EXCHANGE_RATE_API, { searchParams: { from, to } }).json()), ); + const exchangeRates = allExchangeRates.filter(({ status }) => status === 'fulfilled'); return (createInvoiceExportReport({ acqUnitMap: keyBy(acqUnits, 'id'), From 374e3df9ec46ac21cb3ab97a6c641a9a47f63912 Mon Sep 17 00:00:00 2001 From: Azizjon Nurov Date: Tue, 24 Dec 2024 17:30:49 +0500 Subject: [PATCH 2/4] Add change log --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 26df450f..c97c4135 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ * Move reusable claiming code from `ui-receiving` to the shared library. Refs UISACQCOMP-236. * Support `CLAIMS` export type in the `useIntegrationConfigs` hook. Refs UISACQCOMP-238. * Claim locations from all members for locations filter when `crossTenant` equals `true`. Refs UISACQCOMP-239. +* Exclude unsupported currencies on `getInvoiceExportData`. Refs UISACQCOMP-237. ## [6.0.2](https://github.com/folio-org/stripes-acq-components/tree/v6.0.2) (2024-12-04) [Full Changelog](https://github.com/folio-org/stripes-acq-components/compare/v6.0.1...v6.0.2) From a28548259aeb39af8311de3701893404850810c7 Mon Sep 17 00:00:00 2001 From: Azizjon Nurov Date: Tue, 24 Dec 2024 18:33:56 +0500 Subject: [PATCH 3/4] Extract `value` from Promise array --- lib/utils/invoiceExport/getInvoiceExportData.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/utils/invoiceExport/getInvoiceExportData.js b/lib/utils/invoiceExport/getInvoiceExportData.js index bbeeff3f..48579921 100644 --- a/lib/utils/invoiceExport/getInvoiceExportData.js +++ b/lib/utils/invoiceExport/getInvoiceExportData.js @@ -84,7 +84,9 @@ export const getInvoiceExportData = async ({ ky, intl, query, currency: to }) => const allExchangeRates = await Promise.allSettled( currencies.map(from => ky.get(EXCHANGE_RATE_API, { searchParams: { from, to } }).json()), ); - const exchangeRates = allExchangeRates.filter(({ status }) => status === 'fulfilled'); + const exchangeRates = allExchangeRates + .filter(({ status }) => status === 'fulfilled') + .map(({ value }) => value); return (createInvoiceExportReport({ acqUnitMap: keyBy(acqUnits, 'id'), From edc3eaf167fe5cdbd352596e9d3e2cdfb8650f60 Mon Sep 17 00:00:00 2001 From: Azizjon Nurov Date: Wed, 25 Dec 2024 22:15:22 +0500 Subject: [PATCH 4/4] Add test case --- .../getInvoiceExportData.test.js | 119 ++++++++++++++++-- 1 file changed, 109 insertions(+), 10 deletions(-) diff --git a/lib/utils/invoiceExport/getInvoiceExportData.test.js b/lib/utils/invoiceExport/getInvoiceExportData.test.js index b492c2e8..2a3e132e 100644 --- a/lib/utils/invoiceExport/getInvoiceExportData.test.js +++ b/lib/utils/invoiceExport/getInvoiceExportData.test.js @@ -1,17 +1,116 @@ -import { renderHook } from '@testing-library/react-hooks'; -import { useIntl } from 'react-intl'; +/* Developed collaboratively using AI (ChatGPT) */ import { getInvoiceExportData } from './getInvoiceExportData'; +import { fetchAllRecords } from '../fetchAllRecords'; +import { fetchExportDataByIds } from '../fetchExportDataByIds'; +import { getAddresses } from '../getAddresses'; +import { createInvoiceExportReport } from './createInvoiceExportReport'; -jest.mock('./createInvoiceExportReport', () => ({ - createInvoiceExportReport: jest.fn().mockReturnValue('test report'), -})); +jest.mock('../fetchAllRecords'); +jest.mock('../fetchExportDataByIds'); +jest.mock('../getAddresses'); +jest.mock('./createInvoiceExportReport'); -test('should create export report', async () => { - const { result } = renderHook(() => useIntl()); - const intl = result.current; +describe('getInvoiceExportData', () => { + const mockKy = { + get: jest.fn().mockImplementation(() => ({ + json: jest.fn().mockResolvedValue({ from: 'USD', to: 'EUR', rate: 0.85 }), + })), + }; - const report = await getInvoiceExportData({ intl }); + const mockIntl = {}; + const mockQuery = {}; - expect(report).toEqual('test report'); + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('should fetch and process invoice export data correctly', async () => { + fetchAllRecords.mockResolvedValue([{ id: '1', vendorId: 'v1', currency: 'USD' }]); + fetchExportDataByIds.mockResolvedValue([]); + getAddresses.mockReturnValue([]); + createInvoiceExportReport.mockReturnValue('Export Report'); + + const result = await getInvoiceExportData({ + ky: mockKy, + intl: mockIntl, + query: mockQuery, + currency: 'USD', + }); + + expect(fetchAllRecords).toHaveBeenCalled(); + expect(fetchExportDataByIds).toHaveBeenCalled(); + expect(createInvoiceExportReport).toHaveBeenCalledWith( + expect.objectContaining({ + invoiceMap: { '1': { id: '1', vendorId: 'v1', currency: 'USD' } }, + }), + ); + expect(result).toBe('Export Report'); + }); + + it('should handle exchange rate fetching correctly', async () => { + const mockExportInvoices = [ + { id: '1', vendorId: 'v1', currency: 'GBP' }, + { id: '2', vendorId: 'v2', currency: 'PLN' }, + ]; + + fetchAllRecords.mockResolvedValue(mockExportInvoices); + + mockKy.get + .mockImplementationOnce(() => ({ + json: jest.fn().mockResolvedValue({ from: 'GBP', to: 'USD', rate: 1.26 }), + })) + .mockImplementationOnce(() => ({ + json: jest.fn().mockResolvedValue({ from: 'PLN', to: 'USD', rate: 0.24 }), + })); + + const result = await getInvoiceExportData({ + ky: mockKy, + intl: mockIntl, + query: mockQuery, + currency: 'USD', + }); + + expect(result).toBe('Export Report'); + expect(createInvoiceExportReport).toHaveBeenCalledWith( + expect.objectContaining({ + exchangeRateMap: { + GBP: { from: 'GBP', rate: 1.26, to: 'USD' }, + PLN: { from: 'PLN', rate: 0.24, to: 'USD' }, + }, + }), + ); + }); + + it('should filter out failed exchange rate requests', async () => { + const mockExportInvoices = [ + { id: '1', vendorId: 'v1', currency: 'USD' }, + { id: '2', vendorId: 'v2', currency: 'TJS' }, + ]; + + fetchAllRecords.mockResolvedValue(mockExportInvoices); + + mockKy.get.mockImplementationOnce(() => ({ + json: jest.fn().mockResolvedValue({ from: 'USD', to: 'USD', rate: 1 }), + })); + mockKy.get.mockImplementationOnce(() => ({ + json: jest.fn().mockRejectedValue(new Error('Cannot convert TJS into USD: Rate Provider did not return data though at check before data was flagged as available, provider=ECB, query=ConversionQuery (\n{Query.termCurrency=USD, Query.baseCurrency=TJS})')), + })); + + const result = await getInvoiceExportData({ + ky: mockKy, + intl: mockIntl, + query: mockQuery, + currency: 'USD', + }); + + expect(result).toBe('Export Report'); + expect(createInvoiceExportReport).toHaveBeenCalledWith( + expect.objectContaining({ + exchangeRateMap: { + USD: { from: 'USD', rate: 1, to: 'USD' }, + }, + }), + ); + }); });