Add exchange rate feature with multi-currency transactions and transfers support (#1099)

Co-authored-by: Pedro J. Aramburu <pedro@joakin.dev>
This commit is contained in:
Pedro J. Aramburu
2026-04-08 16:05:58 -03:00
committed by GitHub
parent 8e81e967fc
commit f699660479
48 changed files with 1886 additions and 73 deletions

View File

@@ -39,16 +39,30 @@ class Money
validate!
end
def exchange_to(other_currency, date: Date.current, fallback_rate: nil)
# Exchange money to another currency
# Params:
# other_currency: target currency code (e.g. "USD")
# date: date for historical rates (default: Date.current)
# custom_rate: explicit exchange rate to use (skips lookup if provided, including nil check)
# Priority:
# 1. Use custom_rate if explicitly provided (not nil)
# 2. Look up rate via store.find_or_fetch_rate
# 3. Raise ConversionError if no valid rate available
def exchange_to(other_currency, date: Date.current, custom_rate: nil)
iso_code = currency.iso_code
other_iso_code = Money::Currency.new(other_currency).iso_code
if iso_code == other_iso_code
self
else
exchange_rate = store.find_or_fetch_rate(from: iso_code, to: other_iso_code, date: date)&.rate || fallback_rate
# Use custom rate if provided, otherwise look it up
if custom_rate.present?
exchange_rate = custom_rate.to_d
else
exchange_rate = store.find_or_fetch_rate(from: iso_code, to: other_iso_code, date: date)&.rate
end
raise ConversionError.new(from_currency: iso_code, to_currency: other_iso_code, date: date) unless exchange_rate
raise ConversionError.new(from_currency: iso_code, to_currency: other_iso_code, date: date) unless exchange_rate && exchange_rate > 0
Money.new(amount * exchange_rate, other_iso_code)
end