@accounter/server
Version:
99 lines (90 loc) • 3.31 kB
text/typescript
import DataLoader from 'dataloader';
import { format } from 'date-fns';
import { Injectable, Scope } from 'graphql-modules';
import { DBProvider } from '@modules/app-providers/db.provider.js';
import type { ChargesTypes } from '@modules/charges';
import { sql } from '@pgtyped/runtime';
import type {
IGetExchangeRatesByDateQuery,
IGetExchangeRatesByDatesParams,
IGetExchangeRatesByDatesQuery,
IGetExchangeRatesByDatesResult,
} from '../types.js';
const getExchangeRatesByDate = sql<IGetExchangeRatesByDateQuery>`
select *
from accounter_schema.exchange_rates
where exchange_date <= to_date($date, 'YYYY-MM-DD')
order by exchange_date desc limit 1;
`;
const getExchangeRatesByDates = sql<IGetExchangeRatesByDatesQuery>`
SELECT *
FROM accounter_schema.exchange_rates
WHERE
exchange_date BETWEEN (
SELECT exchange_date FROM accounter_schema.exchange_rates
WHERE exchange_date <= to_date($fromDate, 'YYYY-MM-DD')
ORDER BY exchange_date DESC LIMIT 1
)
AND to_date($toDate, 'YYYY-MM-DD')
ORDER BY exchange_date DESC;
`;
({
scope: Scope.Singleton,
global: true,
})
export class FiatExchangeProvider {
constructor(private dbProvider: DBProvider) {}
public async getExchangeRates(date: Date) {
const formattedDate = format(date, 'yyyy-MM-dd');
try {
const result = await getExchangeRatesByDate.run({ date: formattedDate }, this.dbProvider);
return result[0];
} catch (error) {
throw new Error(`error in DB - ${error}`);
}
}
public getExchangeRatesByDates(params: IGetExchangeRatesByDatesParams) {
return getExchangeRatesByDates.run(params, this.dbProvider);
}
private async batchExchangeRatesByDates(dates: readonly Date[]) {
const fromDate = format(new Date(Math.min(...dates.map(d => d.getTime()))), 'yyyy-MM-dd');
const toDate = format(new Date(Math.max(...dates.map(d => d.getTime()))), 'yyyy-MM-dd');
const rates = await getExchangeRatesByDates.run(
{
fromDate,
toDate,
},
this.dbProvider,
);
return dates.map(date => {
const stringifiedDate = format(date, 'yyyy-MM-dd');
return rates
.filter(rate => format(rate.exchange_date!, 'yyyy-MM-dd') <= stringifiedDate)
.reduce((prev: IGetExchangeRatesByDatesResult, curr: IGetExchangeRatesByDatesResult) =>
(prev.exchange_date?.getTime() ?? 0) > (curr.exchange_date?.getTime() ?? 0) ? prev : curr,
);
});
}
public getExchangeRatesByDatesLoader = new DataLoader(
(keys: readonly Date[]) => this.batchExchangeRatesByDates(keys),
{
cache: false,
},
);
public async getChargeExchangeRates(charge: ChargesTypes.IGetChargesByIdsResult) {
if (!charge.transactions_min_debit_date) {
throw new Error(`Charge ID=${charge.id} has no debit date`);
}
if (!charge.documents_min_date) {
throw new Error(`Charge ID=${charge.id} has no tax invoice date`);
}
const results = await Promise.all([
this.getExchangeRatesByDatesLoader.load(charge.transactions_min_debit_date),
this.getExchangeRatesByDatesLoader.load(charge.documents_min_date),
]);
return {
debitExchangeRates: results[0],
invoiceExchangeRates: results[1],
};
}
}