2024-02-02 09:05:04 -06:00
class Family < ApplicationRecord
has_many :users , dependent : :destroy
2024-05-23 08:09:33 -04:00
has_many :tags , dependent : :destroy
2024-02-02 09:05:04 -06:00
has_many :accounts , dependent : :destroy
2024-06-13 14:37:27 -04:00
has_many :institutions , dependent : :destroy
2024-02-23 21:34:33 -05:00
has_many :transactions , through : :accounts
2024-05-17 09:09:32 -04:00
has_many :imports , through : :accounts
2024-06-20 08:15:09 -04:00
has_many :categories , dependent : :destroy
2024-04-29 21:17:28 +02:00
has_many :transaction_merchants , dependent : :destroy , class_name : " Transaction::Merchant "
2024-03-04 08:31:22 -05:00
2024-03-11 16:32:13 -04:00
def snapshot ( period = Period . all )
query = accounts . active . joins ( :balances )
2024-06-19 06:52:08 -04:00
. where ( " account_balances.currency = ? " , self . currency )
. select (
" account_balances.currency " ,
" account_balances.date " ,
" SUM(CASE WHEN accounts.classification = 'liability' THEN account_balances.balance ELSE 0 END) AS liabilities " ,
" SUM(CASE WHEN accounts.classification = 'asset' THEN account_balances.balance ELSE 0 END) AS assets " ,
" SUM(CASE WHEN accounts.classification = 'asset' THEN account_balances.balance WHEN accounts.classification = 'liability' THEN -account_balances.balance ELSE 0 END) AS net_worth " ,
)
. group ( " account_balances.date, account_balances.currency " )
. order ( " account_balances.date " )
2024-03-11 16:32:13 -04:00
2024-03-21 13:39:10 -04:00
query = query . where ( " account_balances.date >= ? " , period . date_range . begin ) if period . date_range . begin
query = query . where ( " account_balances.date <= ? " , period . date_range . end ) if period . date_range . end
2024-03-19 09:10:40 -04:00
result = query . to_a
2024-03-11 16:32:13 -04:00
{
2024-03-19 09:10:40 -04:00
asset_series : TimeSeries . new ( result . map { | r | { date : r . date , value : Money . new ( r . assets , r . currency ) } } ) ,
liability_series : TimeSeries . new ( result . map { | r | { date : r . date , value : Money . new ( r . liabilities , r . currency ) } } ) ,
net_worth_series : TimeSeries . new ( result . map { | r | { date : r . date , value : Money . new ( r . net_worth , r . currency ) } } )
2024-03-11 16:32:13 -04:00
}
2024-03-04 08:31:22 -05:00
end
2024-04-24 13:34:50 +01:00
def snapshot_account_transactions
period = Period . last_30_days
results = accounts . active . joins ( :transactions )
2024-06-19 06:52:08 -04:00
. select (
" accounts.* " ,
" COALESCE(SUM(amount) FILTER (WHERE amount > 0), 0) AS spending " ,
" COALESCE(SUM(-amount) FILTER (WHERE amount < 0), 0) AS income "
)
. where ( " transactions.date >= ? " , period . date_range . begin )
. where ( " transactions.date <= ? " , period . date_range . end )
. where ( " transactions.marked_as_transfer = ? " , false )
. group ( " id " )
. to_a
2024-04-24 13:34:50 +01:00
2024-04-24 15:02:22 +01:00
results . each do | r |
r . define_singleton_method ( :savings_rate ) do
( income - spending ) / income
end
end
2024-04-24 13:34:50 +01:00
{
top_spenders : results . sort_by ( & :spending ) . select { | a | a . spending > 0 } . reverse ,
2024-04-24 15:02:22 +01:00
top_earners : results . sort_by ( & :income ) . select { | a | a . income > 0 } . reverse ,
top_savers : results . sort_by { | a | a . savings_rate } . reverse
2024-04-24 13:34:50 +01:00
}
end
def snapshot_transactions
rolling_totals = Transaction . daily_rolling_totals ( transactions , period : Period . last_30_days , currency : self . currency )
spending = [ ]
income = [ ]
2024-04-24 15:02:22 +01:00
savings = [ ]
2024-04-24 13:34:50 +01:00
rolling_totals . each do | r |
spending << {
date : r . date ,
value : Money . new ( r . rolling_spend , self . currency )
}
income << {
date : r . date ,
value : Money . new ( r . rolling_income , self . currency )
}
2024-04-24 15:02:22 +01:00
savings << {
date : r . date ,
value : r . rolling_income != 0 ? ( r . rolling_income - r . rolling_spend ) / r . rolling_income : 0 . to_d
}
2024-04-24 13:34:50 +01:00
end
{
income_series : TimeSeries . new ( income , favorable_direction : " up " ) ,
2024-04-24 15:02:22 +01:00
spending_series : TimeSeries . new ( spending , favorable_direction : " down " ) ,
savings_rate_series : TimeSeries . new ( savings , favorable_direction : " up " )
2024-04-24 13:34:50 +01:00
}
end
2024-03-11 16:32:13 -04:00
def effective_start_date
accounts . active . joins ( :balances ) . minimum ( " account_balances.date " ) || Date . current
2024-03-04 08:31:22 -05:00
end
2024-03-11 16:32:13 -04:00
def net_worth
2024-03-21 13:39:10 -04:00
assets - liabilities
2024-03-04 08:31:22 -05:00
end
2024-03-11 16:32:13 -04:00
def assets
2024-03-21 13:39:10 -04:00
Money . new ( accounts . active . assets . map { | account | account . balance_money . exchange_to ( currency ) || 0 } . sum , currency )
2024-03-04 08:31:22 -05:00
end
2024-03-11 16:32:13 -04:00
def liabilities
2024-03-21 13:39:10 -04:00
Money . new ( accounts . active . liabilities . map { | account | account . balance_money . exchange_to ( currency ) || 0 } . sum , currency )
2024-03-04 08:31:22 -05:00
end
2024-04-04 23:00:12 +02:00
def sync_accounts
accounts . each { | account | account . sync_later if account . can_sync? }
end
2024-02-02 09:05:04 -06:00
end