Skip to content
This repository was archived by the owner on Jul 27, 2025. It is now read-only.

Commit f7f6ebb

Browse files
authored
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
1 parent 3f92fe0 commit f7f6ebb

17 files changed

+724
-540
lines changed

app/components/UI/account/activity_date.html.erb

Lines changed: 5 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -17,81 +17,20 @@
1717

1818
<div class="flex items-center gap-4">
1919
<div class="flex items-center gap-2">
20-
<span class="font-medium"><%= balance_trend.current.format %></span>
20+
<span class="font-medium"><%= end_balance_money.format %></span>
2121
<%= render DS::Tooltip.new(text: "The end of day balance, after all transactions and adjustments", placement: "left", size: "sm") %>
2222
</div>
2323
<%= helpers.icon "chevron-down", class: "group-open:rotate-180" %>
2424
</div>
2525
</div>
2626
</summary>
2727

28-
<div class="p-4 space-y-3">
29-
<dl class="flex gap-4 items-center text-sm text-primary">
30-
<dt class="flex items-center gap-2">
31-
Start of day balance
32-
<%= render DS::Tooltip.new(text: "The account balance at the beginning of this day, before any transactions or value changes", placement: "left", size: "sm") %>
33-
</dt>
34-
<hr class="grow border-dashed border-secondary">
35-
<dd class="font-bold"><%= start_balance_money.format %></dd>
36-
</dl>
37-
38-
<% if account.balance_type == :investment %>
39-
<dl class="flex gap-4 items-center text-sm text-primary">
40-
<dt class="flex items-center gap-2">
41-
&#916; Cash
42-
<%= render DS::Tooltip.new(text: "Net change in cash from deposits, withdrawals, and other cash transactions during the day", placement: "left", size: "sm") %>
43-
</dt>
44-
<hr class="grow border-dashed border-secondary">
45-
<dd><%= cash_change_money.format %></dd>
46-
</dl>
47-
48-
<dl class="flex gap-4 items-center text-sm text-primary">
49-
<dt class="flex items-center gap-2">
50-
&#916; Holdings
51-
<%= render DS::Tooltip.new(text: "Net change in investment holdings value from buying, selling, or market price movements", placement: "left", size: "sm") %>
52-
</dt>
53-
<hr class="grow border-dashed border-secondary">
54-
<dd><%= holdings_change_money.format %></dd>
55-
</dl>
28+
<div class="p-4">
29+
<% if balance %>
30+
<%= render UI::Account::BalanceReconciliation.new(balance: balance, account: account) %>
5631
<% else %>
57-
<dl class="flex gap-4 items-center text-sm text-primary">
58-
<dt class="flex items-center gap-2">
59-
&#916; Cash
60-
<%= render DS::Tooltip.new(text: "Net change in cash balance from all transactions during the day", placement: "left", size: "sm") %>
61-
</dt>
62-
<hr class="grow border-dashed border-secondary">
63-
<dd><%= cash_change_money.format %></dd>
64-
</dl>
32+
<p class="text-sm text-secondary">No balance data available for this date</p>
6533
<% end %>
66-
67-
<dl class="flex gap-4 items-center text-sm text-primary">
68-
<dt class="flex items-center gap-2">
69-
End of day balance
70-
<%= render DS::Tooltip.new(text: "The calculated balance after all transactions but before any manual adjustments or reconciliations", placement: "left", size: "sm") %>
71-
</dt>
72-
<hr class="grow border-dashed border-secondary">
73-
<dd class="font-medium"><%= end_balance_before_adjustments_money.format %></dd>
74-
</dl>
75-
76-
<hr class="border border-primary">
77-
78-
<dl class="flex gap-4 items-center text-sm text-primary">
79-
<dt class="flex items-center gap-2">
80-
&#916; Value adjustments
81-
<%= 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") %>
82-
</dt>
83-
<hr class="grow border-dashed border-secondary">
84-
<dd><%= adjustments_money.format %></dd>
85-
</dl>
86-
87-
<dl class="flex gap-4 items-center text-sm text-primary">
88-
<dt class="flex items-center gap-2">
89-
Closing balance
90-
<%= render DS::Tooltip.new(text: "The final account balance for the day, after all transactions and adjustments have been applied", placement: "left", size: "sm") %>
91-
</dt>
92-
<hr class="grow border-dashed border-primary">
93-
<dd class="font-bold"><%= end_balance_money.format %></dd>
94-
</dl>
9534
</div>
9635
</details>
9736

app/components/UI/account/activity_date.rb

Lines changed: 2 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
class UI::Account::ActivityDate < ApplicationComponent
22
attr_reader :account, :data
33

4-
delegate :date, :entries, :balance_trend, :cash_balance_trend, :holdings_value_trend, :transfers, to: :data
4+
delegate :date, :entries, :balance, :transfers, to: :data
55

66
def initialize(account:, data:)
77
@account = account
@@ -16,28 +16,8 @@ def broadcast_channel
1616
account
1717
end
1818

19-
def start_balance_money
20-
balance_trend.previous
21-
end
22-
23-
def cash_change_money
24-
cash_balance_trend.value
25-
end
26-
27-
def holdings_change_money
28-
holdings_value_trend.value
29-
end
30-
31-
def end_balance_before_adjustments_money
32-
balance_trend.previous + cash_change_money + holdings_change_money
33-
end
34-
35-
def adjustments_money
36-
end_balance_money - end_balance_before_adjustments_money
37-
end
38-
3919
def end_balance_money
40-
balance_trend.current
20+
balance&.end_balance_money || Money.new(0, account.currency)
4121
end
4222

4323
def broadcast_refresh!
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<div class="space-y-3">
2+
<% reconciliation_items.each_with_index do |item, index| %>
3+
<% if item[:style] == :subtotal %>
4+
<hr class="border border-primary">
5+
<% end %>
6+
7+
<dl class="flex gap-4 items-center text-sm text-primary">
8+
<dt class="flex items-center gap-2">
9+
<%= item[:label] %>
10+
<%= render DS::Tooltip.new(text: item[:tooltip], placement: "left", size: "sm") %>
11+
</dt>
12+
<hr class="grow border-dashed <%= item[:style] == :final ? "border-primary" : "border-secondary" %>">
13+
<dd class="<%= item[:style] == :start || item[:style] == :final ? "font-bold" : item[:style] == :subtotal ? "font-medium" : "" %>">
14+
<%= item[:value].format %>
15+
</dd>
16+
</dl>
17+
18+
<% if item[:style] == :adjustment %>
19+
<hr class="border border-primary">
20+
<% end %>
21+
<% end %>
22+
</div>
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
class UI::Account::BalanceReconciliation < ApplicationComponent
2+
attr_reader :balance, :account
3+
4+
def initialize(balance:, account:)
5+
@balance = balance
6+
@account = account
7+
end
8+
9+
def reconciliation_items
10+
case account.accountable_type
11+
when "Depository", "OtherAsset", "OtherLiability"
12+
default_items
13+
when "CreditCard"
14+
credit_card_items
15+
when "Investment"
16+
investment_items
17+
when "Loan"
18+
loan_items
19+
when "Property", "Vehicle"
20+
asset_items
21+
when "Crypto"
22+
crypto_items
23+
else
24+
default_items
25+
end
26+
end
27+
28+
private
29+
30+
def default_items
31+
items = [
32+
{ label: "Start balance", value: balance.start_balance_money, tooltip: "The account balance at the beginning of this day", style: :start },
33+
{ label: "Net cash flow", value: net_cash_flow, tooltip: "Net change in balance from all transactions during the day", style: :flow }
34+
]
35+
36+
if has_adjustments?
37+
items << { label: "End balance", value: end_balance_before_adjustments, tooltip: "The calculated balance after all transactions", style: :subtotal }
38+
items << { label: "Adjustments", value: total_adjustments, tooltip: "Manual reconciliations or other adjustments", style: :adjustment }
39+
end
40+
41+
items << { label: "Final balance", value: balance.end_balance_money, tooltip: "The final account balance for the day", style: :final }
42+
items
43+
end
44+
45+
def credit_card_items
46+
items = [
47+
{ label: "Start balance", value: balance.start_balance_money, tooltip: "The balance owed at the beginning of this day", style: :start },
48+
{ label: "Charges", value: balance.cash_outflows_money, tooltip: "New charges made during the day", style: :flow },
49+
{ label: "Payments", value: balance.cash_inflows_money * -1, tooltip: "Payments made to the card during the day", style: :flow }
50+
]
51+
52+
if has_adjustments?
53+
items << { label: "End balance", value: end_balance_before_adjustments, tooltip: "The calculated balance after all transactions", style: :subtotal }
54+
items << { label: "Adjustments", value: total_adjustments, tooltip: "Manual reconciliations or other adjustments", style: :adjustment }
55+
end
56+
57+
items << { label: "Final balance", value: balance.end_balance_money, tooltip: "The final balance owed for the day", style: :final }
58+
items
59+
end
60+
61+
def investment_items
62+
items = [
63+
{ label: "Start balance", value: balance.start_balance_money, tooltip: "The total portfolio value at the beginning of this day", style: :start }
64+
]
65+
66+
# Change in brokerage cash (includes deposits, withdrawals, and cash from trades)
67+
items << { label: "Change in brokerage cash", value: net_cash_flow, tooltip: "Net change in cash from deposits, withdrawals, and trades", style: :flow }
68+
69+
# Change in holdings from trading activity
70+
items << { label: "Change in holdings (buys/sells)", value: net_non_cash_flow, tooltip: "Impact on holdings from buying and selling securities", style: :flow }
71+
72+
# Market price changes
73+
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 }
74+
75+
if has_adjustments?
76+
items << { label: "End balance", value: end_balance_before_adjustments, tooltip: "The calculated balance after all activity", style: :subtotal }
77+
items << { label: "Adjustments", value: total_adjustments, tooltip: "Manual reconciliations or other adjustments", style: :adjustment }
78+
end
79+
80+
items << { label: "Final balance", value: balance.end_balance_money, tooltip: "The final portfolio value for the day", style: :final }
81+
items
82+
end
83+
84+
def loan_items
85+
items = [
86+
{ label: "Start principal", value: balance.start_balance_money, tooltip: "The principal balance at the beginning of this day", style: :start },
87+
{ label: "Net principal change", value: net_non_cash_flow, tooltip: "Principal payments and new borrowing during the day", style: :flow }
88+
]
89+
90+
if has_adjustments?
91+
items << { label: "End principal", value: end_balance_before_adjustments, tooltip: "The calculated principal after all transactions", style: :subtotal }
92+
items << { label: "Adjustments", value: balance.non_cash_adjustments_money, tooltip: "Manual reconciliations or other adjustments", style: :adjustment }
93+
end
94+
95+
items << { label: "Final principal", value: balance.end_balance_money, tooltip: "The final principal balance for the day", style: :final }
96+
items
97+
end
98+
99+
def asset_items # Property/Vehicle
100+
items = [
101+
{ label: "Start value", value: balance.start_balance_money, tooltip: "The asset value at the beginning of this day", style: :start },
102+
{ label: "Net value change", value: net_total_flow, tooltip: "All value changes including improvements and depreciation", style: :flow }
103+
]
104+
105+
if has_adjustments?
106+
items << { label: "End value", value: end_balance_before_adjustments, tooltip: "The calculated value after all changes", style: :subtotal }
107+
items << { label: "Adjustments", value: total_adjustments, tooltip: "Manual value adjustments or appraisals", style: :adjustment }
108+
end
109+
110+
items << { label: "Final value", value: balance.end_balance_money, tooltip: "The final asset value for the day", style: :final }
111+
items
112+
end
113+
114+
def crypto_items
115+
items = [
116+
{ label: "Start balance", value: balance.start_balance_money, tooltip: "The crypto holdings value at the beginning of this day", style: :start }
117+
]
118+
119+
items << { label: "Buys", value: balance.cash_outflows_money * -1, tooltip: "Crypto purchases during the day", style: :flow } if balance.cash_outflows != 0
120+
items << { label: "Sells", value: balance.cash_inflows_money, tooltip: "Crypto sales during the day", style: :flow } if balance.cash_inflows != 0
121+
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
122+
123+
if has_adjustments?
124+
items << { label: "End balance", value: end_balance_before_adjustments, tooltip: "The calculated balance after all activity", style: :subtotal }
125+
items << { label: "Adjustments", value: total_adjustments, tooltip: "Manual reconciliations or other adjustments", style: :adjustment }
126+
end
127+
128+
items << { label: "Final balance", value: balance.end_balance_money, tooltip: "The final crypto holdings value for the day", style: :final }
129+
items
130+
end
131+
132+
def net_cash_flow
133+
balance.cash_inflows_money - balance.cash_outflows_money
134+
end
135+
136+
def net_non_cash_flow
137+
balance.non_cash_inflows_money - balance.non_cash_outflows_money
138+
end
139+
140+
def net_total_flow
141+
net_cash_flow + net_non_cash_flow + balance.net_market_flows_money
142+
end
143+
144+
def total_adjustments
145+
balance.cash_adjustments_money + balance.non_cash_adjustments_money
146+
end
147+
148+
def has_adjustments?
149+
balance.cash_adjustments != 0 || balance.non_cash_adjustments != 0
150+
end
151+
152+
def end_balance_before_adjustments
153+
balance.end_balance_money - total_adjustments
154+
end
155+
end

0 commit comments

Comments
 (0)