mirror of
https://github.com/maybe-finance/maybe.git
synced 2025-08-05 05:25:24 +02:00
Use new balance components in activity feed (#2511)
* 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
This commit is contained in:
parent
3f92fe0f6f
commit
f7f6ebb091
17 changed files with 723 additions and 539 deletions
|
@ -17,7 +17,7 @@
|
|||
|
||||
<div class="flex items-center gap-4">
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="font-medium"><%= balance_trend.current.format %></span>
|
||||
<span class="font-medium"><%= end_balance_money.format %></span>
|
||||
<%= render DS::Tooltip.new(text: "The end of day balance, after all transactions and adjustments", placement: "left", size: "sm") %>
|
||||
</div>
|
||||
<%= helpers.icon "chevron-down", class: "group-open:rotate-180" %>
|
||||
|
@ -25,73 +25,12 @@
|
|||
</div>
|
||||
</summary>
|
||||
|
||||
<div class="p-4 space-y-3">
|
||||
<dl class="flex gap-4 items-center text-sm text-primary">
|
||||
<dt class="flex items-center gap-2">
|
||||
Start of day balance
|
||||
<%= render DS::Tooltip.new(text: "The account balance at the beginning of this day, before any transactions or value changes", placement: "left", size: "sm") %>
|
||||
</dt>
|
||||
<hr class="grow border-dashed border-secondary">
|
||||
<dd class="font-bold"><%= start_balance_money.format %></dd>
|
||||
</dl>
|
||||
|
||||
<% if account.balance_type == :investment %>
|
||||
<dl class="flex gap-4 items-center text-sm text-primary">
|
||||
<dt class="flex items-center gap-2">
|
||||
Δ Cash
|
||||
<%= render DS::Tooltip.new(text: "Net change in cash from deposits, withdrawals, and other cash transactions during the day", placement: "left", size: "sm") %>
|
||||
</dt>
|
||||
<hr class="grow border-dashed border-secondary">
|
||||
<dd><%= cash_change_money.format %></dd>
|
||||
</dl>
|
||||
|
||||
<dl class="flex gap-4 items-center text-sm text-primary">
|
||||
<dt class="flex items-center gap-2">
|
||||
Δ Holdings
|
||||
<%= render DS::Tooltip.new(text: "Net change in investment holdings value from buying, selling, or market price movements", placement: "left", size: "sm") %>
|
||||
</dt>
|
||||
<hr class="grow border-dashed border-secondary">
|
||||
<dd><%= holdings_change_money.format %></dd>
|
||||
</dl>
|
||||
<div class="p-4">
|
||||
<% if balance %>
|
||||
<%= render UI::Account::BalanceReconciliation.new(balance: balance, account: account) %>
|
||||
<% else %>
|
||||
<dl class="flex gap-4 items-center text-sm text-primary">
|
||||
<dt class="flex items-center gap-2">
|
||||
Δ Cash
|
||||
<%= render DS::Tooltip.new(text: "Net change in cash balance from all transactions during the day", placement: "left", size: "sm") %>
|
||||
</dt>
|
||||
<hr class="grow border-dashed border-secondary">
|
||||
<dd><%= cash_change_money.format %></dd>
|
||||
</dl>
|
||||
<p class="text-sm text-secondary">No balance data available for this date</p>
|
||||
<% end %>
|
||||
|
||||
<dl class="flex gap-4 items-center text-sm text-primary">
|
||||
<dt class="flex items-center gap-2">
|
||||
End of day balance
|
||||
<%= render DS::Tooltip.new(text: "The calculated balance after all transactions but before any manual adjustments or reconciliations", placement: "left", size: "sm") %>
|
||||
</dt>
|
||||
<hr class="grow border-dashed border-secondary">
|
||||
<dd class="font-medium"><%= end_balance_before_adjustments_money.format %></dd>
|
||||
</dl>
|
||||
|
||||
<hr class="border border-primary">
|
||||
|
||||
<dl class="flex gap-4 items-center text-sm text-primary">
|
||||
<dt class="flex items-center gap-2">
|
||||
Δ Value adjustments
|
||||
<%= render DS::Tooltip.new(text: "Adjustments are either manual reconciliations made by the user or adjustments due to market price changes throughout the day", placement: "left", size: "sm") %>
|
||||
</dt>
|
||||
<hr class="grow border-dashed border-secondary">
|
||||
<dd><%= adjustments_money.format %></dd>
|
||||
</dl>
|
||||
|
||||
<dl class="flex gap-4 items-center text-sm text-primary">
|
||||
<dt class="flex items-center gap-2">
|
||||
Closing balance
|
||||
<%= render DS::Tooltip.new(text: "The final account balance for the day, after all transactions and adjustments have been applied", placement: "left", size: "sm") %>
|
||||
</dt>
|
||||
<hr class="grow border-dashed border-primary">
|
||||
<dd class="font-bold"><%= end_balance_money.format %></dd>
|
||||
</dl>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
class UI::Account::ActivityDate < ApplicationComponent
|
||||
attr_reader :account, :data
|
||||
|
||||
delegate :date, :entries, :balance_trend, :cash_balance_trend, :holdings_value_trend, :transfers, to: :data
|
||||
delegate :date, :entries, :balance, :transfers, to: :data
|
||||
|
||||
def initialize(account:, data:)
|
||||
@account = account
|
||||
|
@ -16,28 +16,8 @@ class UI::Account::ActivityDate < ApplicationComponent
|
|||
account
|
||||
end
|
||||
|
||||
def start_balance_money
|
||||
balance_trend.previous
|
||||
end
|
||||
|
||||
def cash_change_money
|
||||
cash_balance_trend.value
|
||||
end
|
||||
|
||||
def holdings_change_money
|
||||
holdings_value_trend.value
|
||||
end
|
||||
|
||||
def end_balance_before_adjustments_money
|
||||
balance_trend.previous + cash_change_money + holdings_change_money
|
||||
end
|
||||
|
||||
def adjustments_money
|
||||
end_balance_money - end_balance_before_adjustments_money
|
||||
end
|
||||
|
||||
def end_balance_money
|
||||
balance_trend.current
|
||||
balance&.end_balance_money || Money.new(0, account.currency)
|
||||
end
|
||||
|
||||
def broadcast_refresh!
|
||||
|
|
22
app/components/UI/account/balance_reconciliation.html.erb
Normal file
22
app/components/UI/account/balance_reconciliation.html.erb
Normal file
|
@ -0,0 +1,22 @@
|
|||
<div class="space-y-3">
|
||||
<% reconciliation_items.each_with_index do |item, index| %>
|
||||
<% if item[:style] == :subtotal %>
|
||||
<hr class="border border-primary">
|
||||
<% end %>
|
||||
|
||||
<dl class="flex gap-4 items-center text-sm text-primary">
|
||||
<dt class="flex items-center gap-2">
|
||||
<%= item[:label] %>
|
||||
<%= render DS::Tooltip.new(text: item[:tooltip], placement: "left", size: "sm") %>
|
||||
</dt>
|
||||
<hr class="grow border-dashed <%= item[:style] == :final ? "border-primary" : "border-secondary" %>">
|
||||
<dd class="<%= item[:style] == :start || item[:style] == :final ? "font-bold" : item[:style] == :subtotal ? "font-medium" : "" %>">
|
||||
<%= item[:value].format %>
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
<% if item[:style] == :adjustment %>
|
||||
<hr class="border border-primary">
|
||||
<% end %>
|
||||
<% end %>
|
||||
</div>
|
155
app/components/UI/account/balance_reconciliation.rb
Normal file
155
app/components/UI/account/balance_reconciliation.rb
Normal file
|
@ -0,0 +1,155 @@
|
|||
class UI::Account::BalanceReconciliation < ApplicationComponent
|
||||
attr_reader :balance, :account
|
||||
|
||||
def initialize(balance:, account:)
|
||||
@balance = balance
|
||||
@account = account
|
||||
end
|
||||
|
||||
def reconciliation_items
|
||||
case account.accountable_type
|
||||
when "Depository", "OtherAsset", "OtherLiability"
|
||||
default_items
|
||||
when "CreditCard"
|
||||
credit_card_items
|
||||
when "Investment"
|
||||
investment_items
|
||||
when "Loan"
|
||||
loan_items
|
||||
when "Property", "Vehicle"
|
||||
asset_items
|
||||
when "Crypto"
|
||||
crypto_items
|
||||
else
|
||||
default_items
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def default_items
|
||||
items = [
|
||||
{ label: "Start balance", value: balance.start_balance_money, tooltip: "The account balance at the beginning of this day", style: :start },
|
||||
{ label: "Net cash flow", value: net_cash_flow, tooltip: "Net change in balance from all transactions during the day", style: :flow }
|
||||
]
|
||||
|
||||
if has_adjustments?
|
||||
items << { label: "End balance", value: end_balance_before_adjustments, tooltip: "The calculated balance after all transactions", style: :subtotal }
|
||||
items << { label: "Adjustments", value: total_adjustments, tooltip: "Manual reconciliations or other adjustments", style: :adjustment }
|
||||
end
|
||||
|
||||
items << { label: "Final balance", value: balance.end_balance_money, tooltip: "The final account balance for the day", style: :final }
|
||||
items
|
||||
end
|
||||
|
||||
def credit_card_items
|
||||
items = [
|
||||
{ label: "Start balance", value: balance.start_balance_money, tooltip: "The balance owed at the beginning of this day", style: :start },
|
||||
{ label: "Charges", value: balance.cash_outflows_money, tooltip: "New charges made during the day", style: :flow },
|
||||
{ label: "Payments", value: balance.cash_inflows_money * -1, tooltip: "Payments made to the card during the day", style: :flow }
|
||||
]
|
||||
|
||||
if has_adjustments?
|
||||
items << { label: "End balance", value: end_balance_before_adjustments, tooltip: "The calculated balance after all transactions", style: :subtotal }
|
||||
items << { label: "Adjustments", value: total_adjustments, tooltip: "Manual reconciliations or other adjustments", style: :adjustment }
|
||||
end
|
||||
|
||||
items << { label: "Final balance", value: balance.end_balance_money, tooltip: "The final balance owed for the day", style: :final }
|
||||
items
|
||||
end
|
||||
|
||||
def investment_items
|
||||
items = [
|
||||
{ label: "Start balance", value: balance.start_balance_money, tooltip: "The total portfolio value at the beginning of this day", style: :start }
|
||||
]
|
||||
|
||||
# Change in brokerage cash (includes deposits, withdrawals, and cash from trades)
|
||||
items << { label: "Change in brokerage cash", value: net_cash_flow, tooltip: "Net change in cash from deposits, withdrawals, and trades", style: :flow }
|
||||
|
||||
# Change in holdings from trading activity
|
||||
items << { label: "Change in holdings (buys/sells)", value: net_non_cash_flow, tooltip: "Impact on holdings from buying and selling securities", style: :flow }
|
||||
|
||||
# Market price changes
|
||||
items << { label: "Change in holdings (market price activity)", value: balance.net_market_flows_money, tooltip: "Change in holdings value from market price movements", style: :flow }
|
||||
|
||||
if has_adjustments?
|
||||
items << { label: "End balance", value: end_balance_before_adjustments, tooltip: "The calculated balance after all activity", style: :subtotal }
|
||||
items << { label: "Adjustments", value: total_adjustments, tooltip: "Manual reconciliations or other adjustments", style: :adjustment }
|
||||
end
|
||||
|
||||
items << { label: "Final balance", value: balance.end_balance_money, tooltip: "The final portfolio value for the day", style: :final }
|
||||
items
|
||||
end
|
||||
|
||||
def loan_items
|
||||
items = [
|
||||
{ label: "Start principal", value: balance.start_balance_money, tooltip: "The principal balance at the beginning of this day", style: :start },
|
||||
{ label: "Net principal change", value: net_non_cash_flow, tooltip: "Principal payments and new borrowing during the day", style: :flow }
|
||||
]
|
||||
|
||||
if has_adjustments?
|
||||
items << { label: "End principal", value: end_balance_before_adjustments, tooltip: "The calculated principal after all transactions", style: :subtotal }
|
||||
items << { label: "Adjustments", value: balance.non_cash_adjustments_money, tooltip: "Manual reconciliations or other adjustments", style: :adjustment }
|
||||
end
|
||||
|
||||
items << { label: "Final principal", value: balance.end_balance_money, tooltip: "The final principal balance for the day", style: :final }
|
||||
items
|
||||
end
|
||||
|
||||
def asset_items # Property/Vehicle
|
||||
items = [
|
||||
{ label: "Start value", value: balance.start_balance_money, tooltip: "The asset value at the beginning of this day", style: :start },
|
||||
{ label: "Net value change", value: net_total_flow, tooltip: "All value changes including improvements and depreciation", style: :flow }
|
||||
]
|
||||
|
||||
if has_adjustments?
|
||||
items << { label: "End value", value: end_balance_before_adjustments, tooltip: "The calculated value after all changes", style: :subtotal }
|
||||
items << { label: "Adjustments", value: total_adjustments, tooltip: "Manual value adjustments or appraisals", style: :adjustment }
|
||||
end
|
||||
|
||||
items << { label: "Final value", value: balance.end_balance_money, tooltip: "The final asset value for the day", style: :final }
|
||||
items
|
||||
end
|
||||
|
||||
def crypto_items
|
||||
items = [
|
||||
{ label: "Start balance", value: balance.start_balance_money, tooltip: "The crypto holdings value at the beginning of this day", style: :start }
|
||||
]
|
||||
|
||||
items << { label: "Buys", value: balance.cash_outflows_money * -1, tooltip: "Crypto purchases during the day", style: :flow } if balance.cash_outflows != 0
|
||||
items << { label: "Sells", value: balance.cash_inflows_money, tooltip: "Crypto sales during the day", style: :flow } if balance.cash_inflows != 0
|
||||
items << { label: "Market changes", value: balance.net_market_flows_money, tooltip: "Value changes from market price movements", style: :flow } if balance.net_market_flows != 0
|
||||
|
||||
if has_adjustments?
|
||||
items << { label: "End balance", value: end_balance_before_adjustments, tooltip: "The calculated balance after all activity", style: :subtotal }
|
||||
items << { label: "Adjustments", value: total_adjustments, tooltip: "Manual reconciliations or other adjustments", style: :adjustment }
|
||||
end
|
||||
|
||||
items << { label: "Final balance", value: balance.end_balance_money, tooltip: "The final crypto holdings value for the day", style: :final }
|
||||
items
|
||||
end
|
||||
|
||||
def net_cash_flow
|
||||
balance.cash_inflows_money - balance.cash_outflows_money
|
||||
end
|
||||
|
||||
def net_non_cash_flow
|
||||
balance.non_cash_inflows_money - balance.non_cash_outflows_money
|
||||
end
|
||||
|
||||
def net_total_flow
|
||||
net_cash_flow + net_non_cash_flow + balance.net_market_flows_money
|
||||
end
|
||||
|
||||
def total_adjustments
|
||||
balance.cash_adjustments_money + balance.non_cash_adjustments_money
|
||||
end
|
||||
|
||||
def has_adjustments?
|
||||
balance.cash_adjustments != 0 || balance.non_cash_adjustments != 0
|
||||
end
|
||||
|
||||
def end_balance_before_adjustments
|
||||
balance.end_balance_money - total_adjustments
|
||||
end
|
||||
end
|
Loading…
Add table
Add a link
Reference in a new issue