feat(freight-api): add DJF as a supported currency

Adds DJF to freight.payments_currency_enum (migration 3860000000000,
alone in its own migration per Postgres's ADD VALUE-in-a-transaction
restriction) and to manual_payment_settings (djf_enabled column,
migration 3870000000000, default true).

Replaces the binary ETB/USD assumptions that would have silently
mispriced or discarded a DJF booking:
- booking-pricing / contract-pricing: usdToEtb scalar -> a rate table
  keyed by source currency (ExchangeService.getRateTable), so a
  contract-frozen rate converts into whatever currency the booking is
  paid in instead of being dropped when neither leg is ETB or USD.
- warehouse-fee / booking-wagon-cancellation: normalizeCurrency no
  longer coerces anything non-ETB to USD.
- additional-charge: convertAmount no longer bails out for a currency
  that isn't literally ETB or USD.
- manual-payment-settings: isEnabled/enabledCurrencies cover DJF.

Widens the three @IsIn(['ETB','USD']) DTO validators, and adds DJF to
the export/report currency filter option lists.

Claude-Session: https://claude.ai/code/session_01CZy77vCWhka3pnmVF9NDkL
This commit is contained in:
ghost2023
2026-09-04 11:52:37 +03:00
parent 21dc28a708
commit 3734ca3897
20 changed files with 183 additions and 75 deletions

View File

@@ -1,6 +1,6 @@
import { BadRequestException, Injectable, Logger, NotFoundException } from '@nestjs/common';
import { Cron, CronExpression } from '@nestjs/schedule';
import { ExchangeService } from '@edr/api-common';
import { CURRENCY_CODES, CurrencyCode, ExchangeService } from '@edr/api-common';
import { NotificationAudience, NotificationType } from '@edr/types';
import { DataSource } from 'typeorm';
@@ -430,8 +430,11 @@ export class WarehouseFeeService {
};
}
private normalizeCurrency(currency?: string | null): 'ETB' | 'USD' {
return currency === 'ETB' ? 'ETB' : 'USD';
private normalizeCurrency(currency?: string | null): CurrencyCode {
const code = currency?.toUpperCase();
return (CURRENCY_CODES as readonly string[]).includes(code ?? '')
? (code as CurrencyCode)
: 'USD';
}
private async convertAmount(amount: number, fromCurrency: string, toCurrency: string): Promise<number> {