Class: ExchangeRate
- Inherits:
-
ApplicationRecord
- Object
- ActiveRecord::Base
- ApplicationRecord
- ExchangeRate
- Includes:
- Models::Auditable
- Defined in:
- app/models/exchange_rate.rb
Overview
== Schema Information
Table name: exchange_rates
Database name: primary
id :integer not null, primary key
effective_date :date
from_currency :string(255)
rate :decimal(8, 6)
to_currency :string(255)
created_at :datetime
updated_at :datetime
Indexes
idx_unique_exchange_rate (from_currency,to_currency,effective_date) UNIQUE
Constant Summary
Constants included from Models::Auditable
Models::Auditable::ALWAYS_IGNORED
Constants included from Schedulable
Schedulable::SIMPLE_FORM_OPTIONS
Class Method Summary collapse
-
.add_rate(from_currency, to_currency, rate) ⇒ Object
For the money interface so this store can beused by Money::Bank::VariableExchange.
-
.convert_currency(from_currency, to_currency, date, amount) ⇒ BigDecimal
Convert
amountfromfrom_currencytoto_currencyat the FX rate effective ondate, rounded to 2 decimals. -
.download_rate(from_currency, to_currency, effective_date = nil) ⇒ ExchangeRate?
Find or create the ExchangeRate record for the
(from, to, date)triple. -
.each_rate ⇒ Object
For the money interface so this store can beused by Money::Bank::VariableExchange.
-
.get_all_exchange_rates(logger = Rails.logger) ⇒ void
Backfill / catch-up: for every day so far this calendar year (and the entire current month for non-USD companies), cache USD↔company-currency rates.
-
.get_exchange_rate(from_currency, to_currency, date = nil) ⇒ BigDecimal?
Numeric rate from
from_currency→to_currencyondate(defaults to today). -
.get_exchange_rates_for_today(_logger = Rails.logger) ⇒ void
Just-today version of ExchangeRate.get_all_exchange_rates.
-
.get_rate(from_currency, to_currency) ⇒ Object
For the money interface so this store can beused by Money::Bank::VariableExchange.
Methods included from Models::Auditable
#all_skipped_columns, #audit_reference_data, #creator, #should_not_save_version, #stamp_record, #updater
Methods inherited from ApplicationRecord
ransackable_associations, ransackable_attributes, ransackable_scopes, ransortable_attributes, #to_relation
Methods included from Schedulable
Methods included from Models::AfterCommittable
Methods included from Models::EventPublishable
Class Method Details
.add_rate(from_currency, to_currency, rate) ⇒ Object
For the money interface so this store can beused by Money::Bank::VariableExchange
29 30 31 |
# File 'app/models/exchange_rate.rb', line 29 def self.add_rate(from_currency, to_currency, rate) create_with(rate: rate).find_or_create_by(from_currency: from_currency, to_currency: to_currency, effective_date: Date.current) end |
.convert_currency(from_currency, to_currency, date, amount) ⇒ BigDecimal
Convert amount from from_currency to to_currency at the
FX rate effective on date, rounded to 2 decimals.
50 51 52 53 |
# File 'app/models/exchange_rate.rb', line 50 def self.convert_currency(from_currency, to_currency, date, amount) conversion_rate = download_rate(from_currency, to_currency, date) (amount * conversion_rate.rate).round(2) end |
.download_rate(from_currency, to_currency, effective_date = nil) ⇒ ExchangeRate?
Find or create the ExchangeRate record for the
(from, to, date) triple. On a miss, fetches the rate from
OpenExchangeRatesClient and caches both directions
(forward + inverse) so subsequent lookups are local.
effective_date is clamped to today so future-dated requests
don't poison the cache with bad data.
78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 |
# File 'app/models/exchange_rate.rb', line 78 def self.download_rate(from_currency, to_currency, effective_date = nil) effective_date = Date.current if effective_date.nil? || effective_date > Date.current rate = find_by(from_currency: from_currency, to_currency: to_currency, effective_date: effective_date) if rate.nil? # On a cache miss, fetch the historical rate from Open Exchange Rates. ex_rate = OpenExchangeRatesClient.new.rate(effective_date, from_currency, to_currency) return nil if ex_rate.nil? logger.info "Getting rate for #{effective_date} (#{from_currency} to #{to_currency})" rate = ExchangeRate.create!(from_currency: from_currency, to_currency: to_currency, rate: ex_rate, effective_date: effective_date) inverse_rate = 1.0 / ex_rate create_with(rate: inverse_rate).find_or_create_by(from_currency: to_currency, to_currency: from_currency, effective_date: effective_date) end rate end |
.each_rate ⇒ Object
For the money interface so this store can beused by Money::Bank::VariableExchange
34 35 36 37 38 39 40 |
# File 'app/models/exchange_rate.rb', line 34 def self.each_rate return find_each unless block_given? find_each do |rate| yield rate.from_currency, rate.to_currency, rate.rate end end |
.get_all_exchange_rates(logger = Rails.logger) ⇒ void
This method returns an undefined value.
Backfill / catch-up: for every day so far this calendar year
(and the entire current month for non-USD companies),
cache USD↔company-currency rates. Run by the daily ops job.
100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 |
# File 'app/models/exchange_rate.rb', line 100 def self.get_all_exchange_rates(logger = Rails.logger) days = (1..Date.current.day) days.each do |day| date = Date.new(Date.current.year, Date.current.month, day) logger.info "Getting day rates for #{date}" companies = Company.where.not(id: 3) companies.each do |company| ExchangeRate.download_rate('USD', company.currency, date) ExchangeRate.download_rate(company.currency, 'USD', date) end end return if Date.current.month == 1 months = (1..(Date.current.month - 1)) months.each do |month| days = (1..Time.days_in_month(month, Date.current.year)) days.each do |day| date = Date.new(Date.current.year, month, day) logger.info "Getting month rates for #{date}" companies = Company.where.not(id: [1, 3]) companies.each do |company| ExchangeRate.download_rate('USD', company.currency, date) ExchangeRate.download_rate(company.currency, 'USD', date) end end end end |
.get_exchange_rate(from_currency, to_currency, date = nil) ⇒ BigDecimal?
Numeric rate from from_currency → to_currency on date
(defaults to today). Returns nil only when the Open Exchange
Rates lookup also fails.
63 64 65 |
# File 'app/models/exchange_rate.rb', line 63 def self.get_exchange_rate(from_currency, to_currency, date = nil) download_rate(from_currency, to_currency, date)&.rate end |
.get_exchange_rates_for_today(_logger = Rails.logger) ⇒ void
This method returns an undefined value.
Just-today version of get_all_exchange_rates. Skips US
(Company::USA) and the placeholder company id 3 since their
functional currency is already USD.
135 136 137 138 139 140 141 |
# File 'app/models/exchange_rate.rb', line 135 def self.get_exchange_rates_for_today(_logger = Rails.logger) companies = Company.where.not(id: [1, 3]) companies.each do |company| ExchangeRate.download_rate('USD', company.currency, Date.current) ExchangeRate.download_rate(company.currency, 'USD', Date.current) end end |
.get_rate(from_currency, to_currency) ⇒ Object
For the money interface so this store can beused by Money::Bank::VariableExchange
24 25 26 |
# File 'app/models/exchange_rate.rb', line 24 def self.get_rate(from_currency, to_currency) get_exchange_rate(from_currency, to_currency) end |