import { Injectable, Logger, NotFoundException } from '@nestjs/common'; import { PrismaService } from '../../common/prisma.service'; import { Currency } from '@prisma/client'; @Injectable() export class CurrencyService { private readonly logger = new Logger(CurrencyService.name); constructor(private readonly prisma: PrismaService) {} async convertAmount( amountMinor: number, fromCurrency: Currency, toCurrency: Currency, ): Promise { if (fromCurrency === toCurrency) { return amountMinor; } const rate = await this.getExchangeRate(fromCurrency, toCurrency); return Math.round(amountMinor * rate); } async getExchangeRate( fromCurrency: Currency, toCurrency: Currency, ): Promise { const exchangeRate = await this.prisma.currencyExchangeRate.findFirst({ where: { fromCurrency, toCurrency, }, orderBy: { effectiveDate: 'desc', }, }); if (!exchangeRate) { this.logger.warn( `No exchange rate found for ${fromCurrency} to ${toCurrency}, using 1.0`, ); return 1.0; } return Number(exchangeRate.rate); } async syncExchangeRates(): Promise { this.logger.log('Syncing exchange rates from external provider'); 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 }, ]; for (const { from, to, rate } of rates) { await this.upsertRate(from as Currency, to as Currency, rate, today, 'EXTERNAL_API'); } this.logger.log('Exchange rates synced successfully'); } async listRates() { return this.prisma.currencyExchangeRate.findMany({ orderBy: [{ fromCurrency: 'asc' }, { toCurrency: 'asc' }, { effectiveDate: 'desc' }], }); } async upsertRate( fromCurrency: Currency, toCurrency: Currency, rate: number, effectiveDate?: Date, source = 'MANUAL', ) { const date = effectiveDate ?? this.todayUtc(); return this.prisma.currencyExchangeRate.upsert({ where: { fromCurrency_toCurrency_effectiveDate: { fromCurrency, toCurrency, effectiveDate: date } }, update: { rate, source }, create: { fromCurrency, toCurrency, rate, effectiveDate: date, source }, }); } async updateRateById(id: string, rate: number, source = 'MANUAL') { const existing = await this.prisma.currencyExchangeRate.findUnique({ where: { id } }); if (!existing) throw new NotFoundException('Exchange rate not found'); return this.prisma.currencyExchangeRate.update({ where: { id }, data: { rate, source } }); } async deleteRate(id: string) { return this.prisma.currencyExchangeRate.delete({ where: { id } }); } /** Returns midnight UTC for today — used as the date-only key for upserts. */ private todayUtc(): Date { const d = new Date(); d.setUTCHours(0, 0, 0, 0); return d; } }