2024-02-29 08:32:52 -05:00
|
|
|
module Account::Syncable
|
2024-05-20 16:34:48 +02:00
|
|
|
extend ActiveSupport::Concern
|
|
|
|
|
|
|
|
def sync_later(start_date = nil)
|
|
|
|
AccountSyncJob.perform_later(self, start_date)
|
|
|
|
end
|
|
|
|
|
|
|
|
def sync(start_date = nil)
|
|
|
|
update!(status: "syncing")
|
|
|
|
|
2024-07-01 10:49:43 -04:00
|
|
|
if multi_currency? || foreign_currency?
|
|
|
|
sync_exchange_rates
|
|
|
|
end
|
2024-05-20 16:34:48 +02:00
|
|
|
|
2024-07-01 10:49:43 -04:00
|
|
|
calculator = Account::Balance::Calculator.new(self, { calc_start_date: start_date })
|
2024-05-20 16:34:48 +02:00
|
|
|
|
|
|
|
self.balances.upsert_all(calculator.daily_balances, unique_by: :index_account_balances_on_account_id_date_currency_unique)
|
|
|
|
self.balances.where("date < ?", effective_start_date).delete_all
|
|
|
|
new_balance = calculator.daily_balances.select { |b| b[:currency] == self.currency }.last[:balance]
|
|
|
|
|
2024-07-01 10:49:43 -04:00
|
|
|
update! \
|
|
|
|
status: "ok",
|
|
|
|
last_sync_date: Date.current,
|
|
|
|
balance: new_balance,
|
|
|
|
sync_errors: calculator.errors,
|
|
|
|
sync_warnings: calculator.warnings
|
2024-05-20 16:34:48 +02:00
|
|
|
rescue => e
|
2024-05-27 18:10:28 +02:00
|
|
|
update!(status: "error", sync_errors: [ :sync_message_unknown_error ])
|
|
|
|
logger.error("Failed to sync account #{id}: #{e.message}")
|
2024-05-20 16:34:48 +02:00
|
|
|
end
|
|
|
|
|
|
|
|
def can_sync?
|
|
|
|
# Skip account sync if account is not active or the sync process is already running
|
|
|
|
return false unless is_active
|
|
|
|
return false if syncing?
|
|
|
|
# If last_sync_date is blank (i.e. the account has never been synced before) allow syncing
|
|
|
|
return true if last_sync_date.blank?
|
|
|
|
|
|
|
|
# If last_sync_date is not today, allow syncing
|
|
|
|
last_sync_date != Date.today
|
|
|
|
end
|
|
|
|
|
|
|
|
# The earliest date we can calculate a balance for
|
|
|
|
def effective_start_date
|
2024-07-01 10:49:43 -04:00
|
|
|
@effective_start_date ||= entries.order(:date).first.try(:date) || Date.current
|
2024-05-20 16:34:48 +02:00
|
|
|
end
|
|
|
|
|
|
|
|
# Finds all the rate pairs that are required to calculate balances for an account and syncs them
|
|
|
|
def sync_exchange_rates
|
|
|
|
rate_candidates = []
|
|
|
|
|
|
|
|
if multi_currency?
|
2024-07-01 10:49:43 -04:00
|
|
|
transactions_in_foreign_currency = self.entries.where.not(currency: self.currency).pluck(:currency, :date).uniq
|
2024-05-20 16:34:48 +02:00
|
|
|
transactions_in_foreign_currency.each do |currency, date|
|
|
|
|
rate_candidates << { date: date, from_currency: currency, to_currency: self.currency }
|
|
|
|
end
|
2024-02-29 08:32:52 -05:00
|
|
|
end
|
|
|
|
|
2024-05-20 16:34:48 +02:00
|
|
|
if foreign_currency?
|
|
|
|
(effective_start_date..Date.current).each do |date|
|
|
|
|
rate_candidates << { date: date, from_currency: self.currency, to_currency: self.family.currency }
|
|
|
|
end
|
2024-02-29 08:32:52 -05:00
|
|
|
end
|
|
|
|
|
2024-07-01 10:49:43 -04:00
|
|
|
return if rate_candidates.blank?
|
|
|
|
|
2024-05-20 16:34:48 +02:00
|
|
|
existing_rates = ExchangeRate.where(
|
2024-07-08 09:04:59 -04:00
|
|
|
from_currency: rate_candidates.map { |rc| rc[:from_currency] },
|
|
|
|
to_currency: rate_candidates.map { |rc| rc[:to_currency] },
|
2024-05-20 16:34:48 +02:00
|
|
|
date: rate_candidates.map { |rc| rc[:date] }
|
2024-07-08 09:04:59 -04:00
|
|
|
).pluck(:from_currency, :to_currency, :date)
|
2024-04-04 23:00:12 +02:00
|
|
|
|
2024-05-20 16:34:48 +02:00
|
|
|
# Convert to a set for faster lookup
|
|
|
|
existing_rates_set = existing_rates.map { |er| [ er[0], er[1], er[2].to_s ] }.to_set
|
2024-04-04 23:00:12 +02:00
|
|
|
|
2024-05-20 16:34:48 +02:00
|
|
|
rate_candidates.each do |rate_candidate|
|
|
|
|
rc_from = rate_candidate[:from_currency]
|
|
|
|
rc_to = rate_candidate[:to_currency]
|
|
|
|
rc_date = rate_candidate[:date]
|
2024-02-29 08:32:52 -05:00
|
|
|
|
2024-05-20 16:34:48 +02:00
|
|
|
next if existing_rates_set.include?([ rc_from, rc_to, rc_date.to_s ])
|
2024-03-21 13:39:10 -04:00
|
|
|
|
2024-05-20 16:34:48 +02:00
|
|
|
logger.info "Fetching exchange rate from provider for account #{self.name}: #{self.id} (#{rc_from} to #{rc_to} on #{rc_date})"
|
2024-05-27 18:10:28 +02:00
|
|
|
ExchangeRate.find_rate_or_fetch from: rc_from, to: rc_to, date: rc_date
|
2024-03-21 13:39:10 -04:00
|
|
|
end
|
2024-05-20 16:34:48 +02:00
|
|
|
|
|
|
|
nil
|
|
|
|
end
|
2024-02-29 08:32:52 -05:00
|
|
|
end
|