mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-09-07 18:55:42 +00:00
Fare and route-coach, production checklist updates
This commit is contained in:
@@ -1,9 +1,10 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { HttpModule } from '@nestjs/axios';
|
||||
import { CurrencyService } from './currency.service';
|
||||
import { PrismaModule } from '../../common/prisma.module';
|
||||
|
||||
@Module({
|
||||
imports: [PrismaModule],
|
||||
imports: [PrismaModule, HttpModule],
|
||||
providers: [CurrencyService],
|
||||
exports: [CurrencyService],
|
||||
})
|
||||
|
||||
@@ -4,6 +4,9 @@ import {
|
||||
NotFoundException,
|
||||
BadRequestException,
|
||||
} from '@nestjs/common';
|
||||
import { HttpService } from '@nestjs/axios';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import { firstValueFrom } from 'rxjs';
|
||||
import { PrismaService } from '../../common/prisma.service';
|
||||
import { Currency } from '@prisma/client';
|
||||
|
||||
@@ -21,7 +24,11 @@ const CHARGE_CURRENCY_DECIMALS: Record<string, number> = {
|
||||
export class CurrencyService {
|
||||
private readonly logger = new Logger(CurrencyService.name);
|
||||
|
||||
constructor(private readonly prisma: PrismaService) {}
|
||||
constructor(
|
||||
private readonly prisma: PrismaService,
|
||||
private readonly httpService: HttpService,
|
||||
private readonly configService: ConfigService,
|
||||
) {}
|
||||
|
||||
async convertEtbMinorToChargeMajor(
|
||||
amountMinorEtb: number,
|
||||
@@ -81,14 +88,11 @@ export class CurrencyService {
|
||||
fromCurrency: Currency,
|
||||
toCurrency: Currency,
|
||||
): Promise<number> {
|
||||
if (fromCurrency === toCurrency) return 1;
|
||||
|
||||
const exchangeRate = await this.prisma.currencyExchangeRate.findFirst({
|
||||
where: {
|
||||
fromCurrency,
|
||||
toCurrency,
|
||||
},
|
||||
orderBy: {
|
||||
effectiveDate: 'desc',
|
||||
},
|
||||
where: { fromCurrency, toCurrency },
|
||||
orderBy: { effectiveDate: 'desc' },
|
||||
});
|
||||
|
||||
if (!exchangeRate) {
|
||||
@@ -98,26 +102,61 @@ export class CurrencyService {
|
||||
return 1.0;
|
||||
}
|
||||
|
||||
const ageMs = Date.now() - exchangeRate.effectiveDate.getTime();
|
||||
if (ageMs > 2 * 24 * 60 * 60 * 1000) {
|
||||
this.logger.warn(
|
||||
`Stale exchange rate for ${fromCurrency}->${toCurrency}: last updated ${exchangeRate.effectiveDate.toISOString()}`,
|
||||
);
|
||||
}
|
||||
|
||||
return Number(exchangeRate.rate);
|
||||
}
|
||||
|
||||
async syncExchangeRates(): Promise<void> {
|
||||
this.logger.log('Syncing exchange rates from external provider');
|
||||
this.logger.log('Syncing exchange rates from central bank API');
|
||||
|
||||
const today = this.todayUtc();
|
||||
const rates = [
|
||||
{ from: 'ETB', to: 'ETB', rate: 1.0 },
|
||||
{ from: 'ETB', to: 'DJF', rate: 3.25 },
|
||||
{ from: 'ETB', to: 'USD', rate: 0.018 },
|
||||
{ from: 'DJF', to: 'ETB', rate: 0.3077 },
|
||||
{ from: 'USD', to: 'ETB', rate: 55.56 },
|
||||
// Fallback rates used when the API is unreachable
|
||||
const fallbackRates = [
|
||||
{ from: Currency.ETB, to: Currency.ETB, rate: 1.0 },
|
||||
{ from: Currency.ETB, to: Currency.DJF, rate: 3.25 },
|
||||
{ from: Currency.ETB, to: Currency.USD, rate: 0.018 },
|
||||
{ from: Currency.DJF, to: Currency.ETB, rate: 0.3077 },
|
||||
{ from: Currency.USD, to: Currency.ETB, rate: 55.56 },
|
||||
];
|
||||
|
||||
for (const { from, to, rate } of rates) {
|
||||
await this.upsertRate(from as Currency, to as Currency, rate, today, 'EXTERNAL_API');
|
||||
const apiUrl = this.configService.get<string>('EXCHANGE_RATE_API_URL');
|
||||
if (apiUrl) {
|
||||
try {
|
||||
const response = await firstValueFrom(
|
||||
this.httpService.get<Record<string, number>>(apiUrl, { timeout: 5000 }),
|
||||
);
|
||||
// Expected response shape: { "ETB_DJF": 3.25, "ETB_USD": 0.018, ... }
|
||||
const data = response.data;
|
||||
const apiRates = [
|
||||
{ from: Currency.ETB, to: Currency.ETB, rate: 1.0 },
|
||||
{ from: Currency.ETB, to: Currency.DJF, rate: data['ETB_DJF'] ?? fallbackRates[1].rate },
|
||||
{ from: Currency.ETB, to: Currency.USD, rate: data['ETB_USD'] ?? fallbackRates[2].rate },
|
||||
{ from: Currency.DJF, to: Currency.ETB, rate: data['DJF_ETB'] ?? fallbackRates[3].rate },
|
||||
{ from: Currency.USD, to: Currency.ETB, rate: data['USD_ETB'] ?? fallbackRates[4].rate },
|
||||
];
|
||||
for (const { from, to, rate } of apiRates) {
|
||||
await this.upsertRate(from, to, rate, today, 'CENTRAL_BANK_API');
|
||||
}
|
||||
this.logger.log('Exchange rates synced from central bank API');
|
||||
return;
|
||||
} catch (err) {
|
||||
this.logger.warn(
|
||||
`Central bank API unreachable (${(err as Error).message}), falling back to configured rates`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
this.logger.log('Exchange rates synced successfully');
|
||||
// Fallback: persist the static rates so the DB always has a current row
|
||||
for (const { from, to, rate } of fallbackRates) {
|
||||
await this.upsertRate(from, to, rate, today, 'FALLBACK');
|
||||
}
|
||||
this.logger.log('Exchange rates synced using fallback values');
|
||||
}
|
||||
|
||||
async listRates() {
|
||||
|
||||
Reference in New Issue
Block a user