Files
edr-platform/packages/api-common
Nathnael ae1e13b07e feat(exchange): give every currency the rate support USD had
DJF was priced off a rate nobody could see or set. The rate itself worked —
CBE quotes DJF and the provider read it — but `exchange_settings` held a
single USD→ETB row, `getStatus()` hardcoded `rates.get("USD")`, and the
settings card was titled "Exchange rate (USD → ETB)". So an operator could
enable DJF billing, have invoices priced in francs, and have no way to see
which rate produced them and no way to override it during an outage. Those
are exactly the two levers they have for USD.

The original reasoning — DJF is pegged to USD, so a second stored number is
a second thing that goes stale — justified not storing a second FALLBACK.
It never justified not displaying the rate. Those got conflated.

`exchange_settings` becomes one row per currency. `fallback_rate` means the
same thing in every row: ETB per one unit of `currency`. The ETB row is 1
and exists so the table describes the whole set rather than "the others".
`enabled` generalises `djf_enabled` from 3790000000000 — availability is a
property of a currency, not a column named after one — and the migration
carries the old flag onto the DJF row so an operator who had already
switched it on does not find it off after deploying.

loadFallbackRate/saveFallbackRate take a currency. The provider persists
every quoted rate from the one payload it already fetched, so DJF has its
own last-known-good value to fall back on, and reads that currency's own
stored rate first — a rate an operator typed by hand during an outage must
not be silently re-derived from USD. The peg survives one rung lower: when
a currency has nothing stored, USD's stored rate through the peg still
beats the compiled-in default, which is a year-old number by definition.

The DTO's `@Min(1)` is now `@Min(0.0001)`. It would have rejected every
legitimate DJF rate — birr per franc is about 0.92.

Verified against the dev database: one live fetch stamped last_synced_at on
both the USD and DJF rows, which is the behaviour that was missing.
2026-08-31 10:54:00 +00:00
..
2026-05-12 16:50:18 +03:00
2026-05-12 16:50:18 +03:00