mirror of
https://github.com/maybe-finance/maybe.git
synced 2025-08-01 03:25:20 +02:00
* Balance reconcilations with new components * Fix materializer and test assumptions * Fix investment valuation calculations and recon display * Lint fixes * Balance series uses new component fields
89 lines
2.6 KiB
Ruby
89 lines
2.6 KiB
Ruby
class Balance::Materializer
|
|
attr_reader :account, :strategy
|
|
|
|
def initialize(account, strategy:)
|
|
@account = account
|
|
@strategy = strategy
|
|
end
|
|
|
|
def materialize_balances
|
|
Balance.transaction do
|
|
materialize_holdings
|
|
calculate_balances
|
|
|
|
Rails.logger.info("Persisting #{@balances.size} balances")
|
|
persist_balances
|
|
|
|
purge_stale_balances
|
|
|
|
if strategy == :forward
|
|
update_account_info
|
|
end
|
|
end
|
|
end
|
|
|
|
private
|
|
def materialize_holdings
|
|
@holdings = Holding::Materializer.new(account, strategy: strategy).materialize_holdings
|
|
end
|
|
|
|
def update_account_info
|
|
# Query fresh balance from DB to get generated column values
|
|
current_balance = account.balances
|
|
.where(currency: account.currency)
|
|
.order(date: :desc)
|
|
.first
|
|
|
|
if current_balance
|
|
calculated_balance = current_balance.end_balance
|
|
calculated_cash_balance = current_balance.end_cash_balance
|
|
else
|
|
# Fallback if no balance exists
|
|
calculated_balance = 0
|
|
calculated_cash_balance = 0
|
|
end
|
|
|
|
Rails.logger.info("Balance update: cash=#{calculated_cash_balance}, total=#{calculated_balance}")
|
|
|
|
account.update!(
|
|
balance: calculated_balance,
|
|
cash_balance: calculated_cash_balance
|
|
)
|
|
end
|
|
|
|
def calculate_balances
|
|
@balances = calculator.calculate
|
|
end
|
|
|
|
def persist_balances
|
|
current_time = Time.now
|
|
account.balances.upsert_all(
|
|
@balances.map { |b| b.attributes
|
|
.slice("date", "balance", "cash_balance", "currency",
|
|
"start_cash_balance", "start_non_cash_balance",
|
|
"cash_inflows", "cash_outflows",
|
|
"non_cash_inflows", "non_cash_outflows",
|
|
"net_market_flows",
|
|
"cash_adjustments", "non_cash_adjustments",
|
|
"flows_factor")
|
|
.merge("updated_at" => current_time) },
|
|
unique_by: %i[account_id date currency]
|
|
)
|
|
end
|
|
|
|
def purge_stale_balances
|
|
sorted_balances = @balances.sort_by(&:date)
|
|
oldest_calculated_balance_date = sorted_balances.first&.date
|
|
newest_calculated_balance_date = sorted_balances.last&.date
|
|
deleted_count = account.balances.delete_by("date < ? OR date > ?", oldest_calculated_balance_date, newest_calculated_balance_date)
|
|
Rails.logger.info("Purged #{deleted_count} stale balances") if deleted_count > 0
|
|
end
|
|
|
|
def calculator
|
|
if strategy == :reverse
|
|
Balance::ReverseCalculator.new(account)
|
|
else
|
|
Balance::ForwardCalculator.new(account)
|
|
end
|
|
end
|
|
end
|