mirror of
https://github.com/maybe-finance/maybe.git
synced 2025-07-18 20:59:39 +02:00
* Add kind field to valuation * Fix schema conflict * Add kind to valuation * Scaffold opening balance manager * Opening balance manager implementation * Update account import to use opening balance manager + tests * Update account to use opening balance manager * Fix test assertions, usage of current balance manager * Lint fixes * Add Opening Balance manager, add tests to forward calculator * Add credit card to "all cash" designation * Simplify valuation model * Add current balance manager with tests * Add current balance logic to reverse calculator and plaid sync * Tweaks to initial calc logic * Ledger testing helper, tweak assertions for reverse calculator * Update test assertions * Extract balance transformer, simplify calculators * Algo simplifications * Final tweaks to calculators * Cleanup * Fix error, propagate sync errors up to parent * Update migration script, valuation naming
79 lines
3.5 KiB
Ruby
79 lines
3.5 KiB
Ruby
class Balance::ReverseCalculator < Balance::BaseCalculator
|
|
def calculate
|
|
Rails.logger.tagged("Balance::ReverseCalculator") do
|
|
# Since it's a reverse sync, we're starting with the "end of day" balance components and
|
|
# calculating backwards to derive the "start of day" balance components.
|
|
end_cash_balance = derive_cash_balance_on_date_from_total(
|
|
total_balance: account.current_anchor_balance,
|
|
date: account.current_anchor_date
|
|
)
|
|
end_non_cash_balance = account.current_anchor_balance - end_cash_balance
|
|
|
|
# Calculates in reverse-chronological order (End of day -> Start of day)
|
|
account.current_anchor_date.downto(account.opening_anchor_date).map do |date|
|
|
if use_opening_anchor_for_date?(date)
|
|
end_cash_balance = derive_cash_balance_on_date_from_total(
|
|
total_balance: account.opening_anchor_balance,
|
|
date: date
|
|
)
|
|
end_non_cash_balance = account.opening_anchor_balance - end_cash_balance
|
|
|
|
start_cash_balance = end_cash_balance
|
|
start_non_cash_balance = end_non_cash_balance
|
|
|
|
build_balance(
|
|
date: date,
|
|
cash_balance: end_cash_balance,
|
|
non_cash_balance: end_non_cash_balance
|
|
)
|
|
else
|
|
start_cash_balance = derive_start_cash_balance(end_cash_balance: end_cash_balance, date: date)
|
|
start_non_cash_balance = derive_start_non_cash_balance(end_non_cash_balance: end_non_cash_balance, date: date)
|
|
|
|
# Even though we've just calculated "start" balances, we set today equal to end of day, then use those
|
|
# in our next iteration (slightly confusing, but just the nature of a "reverse" sync)
|
|
output_balance = build_balance(
|
|
date: date,
|
|
cash_balance: end_cash_balance,
|
|
non_cash_balance: end_non_cash_balance
|
|
)
|
|
|
|
end_cash_balance = start_cash_balance
|
|
end_non_cash_balance = start_non_cash_balance
|
|
|
|
output_balance
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
private
|
|
|
|
# Negative entries amount on an "asset" account means, "account value has increased"
|
|
# Negative entries amount on a "liability" account means, "account debt has decreased"
|
|
# Positive entries amount on an "asset" account means, "account value has decreased"
|
|
# Positive entries amount on a "liability" account means, "account debt has increased"
|
|
def signed_entry_flows(entries)
|
|
entry_flows = entries.sum(&:amount)
|
|
account.asset? ? entry_flows : -entry_flows
|
|
end
|
|
|
|
# Reverse syncs are a bit different than forward syncs because we do not allow "reconciliation" valuations
|
|
# to be used at all. This is primarily to keep the code and the UI easy to understand. For a more detailed
|
|
# explanation, see the test suite.
|
|
def use_opening_anchor_for_date?(date)
|
|
account.has_opening_anchor? && date == account.opening_anchor_date
|
|
end
|
|
|
|
# Alias method, for algorithmic clarity
|
|
# Derives cash balance, starting from the end-of-day, applying entries in reverse to get the start-of-day balance
|
|
def derive_start_cash_balance(end_cash_balance:, date:)
|
|
derive_cash_balance(end_cash_balance, date)
|
|
end
|
|
|
|
# Alias method, for algorithmic clarity
|
|
# Derives non-cash balance, starting from the end-of-day, applying entries in reverse to get the start-of-day balance
|
|
def derive_start_non_cash_balance(end_non_cash_balance:, date:)
|
|
derive_non_cash_balance(end_non_cash_balance, date, direction: :reverse)
|
|
end
|
|
end
|