2024-02-02 09:05:04 -06:00
|
|
|
class Account < ApplicationRecord
|
2024-02-29 08:32:52 -05:00
|
|
|
include Syncable
|
|
|
|
|
2024-02-24 02:18:30 +00:00
|
|
|
broadcasts_refreshes
|
2024-02-02 09:05:04 -06:00
|
|
|
belongs_to :family
|
2024-02-20 09:07:55 -05:00
|
|
|
has_many :balances, class_name: "AccountBalance"
|
|
|
|
has_many :valuations
|
2024-02-23 21:34:33 -05:00
|
|
|
has_many :transactions
|
2024-02-02 11:09:31 -06:00
|
|
|
|
2024-02-09 14:26:54 +00:00
|
|
|
delegated_type :accountable, types: Accountable::TYPES, dependent: :destroy
|
2024-02-02 23:06:29 +00:00
|
|
|
|
2024-02-10 16:18:56 -06:00
|
|
|
before_create :check_currency
|
|
|
|
|
2024-03-06 09:56:59 -05:00
|
|
|
def trend(period = Period.all)
|
|
|
|
first = balances.in_period(period).order(:date).first
|
|
|
|
last = balances.in_period(period).order(date: :desc).first
|
|
|
|
Trend.new(current: last.balance, previous: first.balance, type: classification)
|
2024-02-20 09:07:55 -05:00
|
|
|
end
|
|
|
|
|
2024-03-06 09:56:59 -05:00
|
|
|
# TODO: We will need a better way to encapsulate large queries & transformation logic, but leaving all in one spot until
|
|
|
|
# we have a better understanding of the requirements
|
|
|
|
def self.by_group(period = Period.all)
|
|
|
|
ranked_balances_cte = joins(:balances)
|
|
|
|
.select("
|
|
|
|
account_balances.account_id,
|
|
|
|
account_balances.balance,
|
|
|
|
account_balances.date,
|
|
|
|
ROW_NUMBER() OVER (PARTITION BY account_balances.account_id ORDER BY date ASC) AS rn_asc,
|
|
|
|
ROW_NUMBER() OVER (PARTITION BY account_balances.account_id ORDER BY date DESC) AS rn_desc
|
|
|
|
")
|
|
|
|
|
|
|
|
if period.date_range
|
|
|
|
ranked_balances_cte = ranked_balances_cte.where("account_balances.date BETWEEN ? AND ?", period.date_range.begin, period.date_range.end)
|
|
|
|
end
|
|
|
|
|
|
|
|
accounts_with_period_balances = AccountBalance.with(
|
|
|
|
ranked_balances: ranked_balances_cte
|
2024-03-01 17:17:34 -05:00
|
|
|
)
|
2024-03-06 09:56:59 -05:00
|
|
|
.from("ranked_balances AS rb")
|
|
|
|
.joins("JOIN accounts a ON a.id = rb.account_id")
|
|
|
|
.select("
|
|
|
|
a.name,
|
|
|
|
a.accountable_type,
|
|
|
|
a.classification,
|
|
|
|
SUM(CASE WHEN rb.rn_asc = 1 THEN rb.balance ELSE 0 END) AS start_balance,
|
|
|
|
MAX(CASE WHEN rb.rn_asc = 1 THEN rb.date ELSE NULL END) as start_date,
|
|
|
|
SUM(CASE WHEN rb.rn_desc = 1 THEN rb.balance ELSE 0 END) AS end_balance,
|
|
|
|
MAX(CASE WHEN rb.rn_desc = 1 THEN rb.date ELSE NULL END) as end_date
|
|
|
|
")
|
|
|
|
.where("rb.rn_asc = 1 OR rb.rn_desc = 1")
|
|
|
|
.group("a.id")
|
|
|
|
.order("end_balance")
|
|
|
|
.to_a
|
|
|
|
|
|
|
|
assets = accounts_with_period_balances.select { |row| row.classification == "asset" }
|
|
|
|
liabilities = accounts_with_period_balances.select { |row| row.classification == "liability" }
|
|
|
|
|
|
|
|
total_assets = assets.sum(&:end_balance)
|
|
|
|
total_liabilities = liabilities.sum(&:end_balance)
|
|
|
|
|
|
|
|
{
|
|
|
|
asset: {
|
|
|
|
total: total_assets,
|
|
|
|
groups: assets.group_by(&:accountable_type).transform_values do |rows|
|
|
|
|
end_balance = rows.sum(&:end_balance)
|
|
|
|
start_balance = rows.sum(&:start_balance)
|
|
|
|
{
|
|
|
|
start_balance: start_balance,
|
|
|
|
end_balance: end_balance,
|
|
|
|
allocation: (end_balance / total_assets * 100).round(2),
|
|
|
|
trend: Trend.new(current: end_balance, previous: start_balance, type: "asset"),
|
|
|
|
accounts: rows.map do |account|
|
|
|
|
{
|
|
|
|
name: account.name,
|
|
|
|
start_balance: account.start_balance,
|
|
|
|
end_balance: account.end_balance,
|
|
|
|
allocation: (account.end_balance / total_assets * 100).round(2),
|
|
|
|
trend: Trend.new(current: account.end_balance, previous: account.start_balance, type: "asset")
|
|
|
|
}
|
|
|
|
end
|
|
|
|
}
|
|
|
|
end
|
|
|
|
},
|
|
|
|
liability: {
|
|
|
|
total: total_liabilities,
|
|
|
|
groups: liabilities.group_by(&:accountable_type).transform_values do |rows|
|
|
|
|
end_balance = rows.sum(&:end_balance)
|
|
|
|
start_balance = rows.sum(&:start_balance)
|
|
|
|
{
|
|
|
|
start_balance: start_balance,
|
|
|
|
end_balance: end_balance,
|
|
|
|
allocation: (end_balance / total_liabilities * 100).round(2),
|
|
|
|
trend: Trend.new(current: end_balance, previous: start_balance, type: "liability"),
|
|
|
|
accounts: rows.map do |account|
|
|
|
|
{
|
|
|
|
name: account.name,
|
|
|
|
start_balance: account.start_balance,
|
|
|
|
end_balance: account.end_balance,
|
|
|
|
allocation: (account.end_balance / total_liabilities * 100).round(2),
|
|
|
|
trend: Trend.new(current: account.end_balance, previous: account.start_balance, type: "liability")
|
|
|
|
}
|
|
|
|
end
|
|
|
|
}
|
|
|
|
end
|
|
|
|
}
|
|
|
|
}
|
2024-02-20 09:07:55 -05:00
|
|
|
end
|
|
|
|
|
2024-03-06 09:56:59 -05:00
|
|
|
private
|
|
|
|
|
|
|
|
def check_currency
|
|
|
|
if self.currency == self.family.currency
|
|
|
|
self.converted_balance = self.balance
|
|
|
|
self.converted_currency = self.currency
|
|
|
|
else
|
|
|
|
self.converted_balance = ExchangeRate.convert(self.currency, self.family.currency, self.balance)
|
|
|
|
self.converted_currency = self.family.currency
|
|
|
|
end
|
2024-02-10 16:18:56 -06:00
|
|
|
end
|
2024-02-02 09:05:04 -06:00
|
|
|
end
|