feat(investments): break converted totals down by currency

Replace the note on converted totals with a collapsible per currency, and
drop the uppercase labels from the summary cards.
This commit is contained in:
Herculino Trotta committed 2026-11-07 01:30:23 -03:00
1 parent 99c6ca08a2
commit 1f9ba8eba8
3 files changed
+29 -6

No files matched your search

+11
View File
@@ -373,8 +373,10 @@ def summarize(performances, convert=False):
"has_rate_history": True,
"flows": [],
"as_of": perf.as_of,
"holdings": [],
},
)
total["holdings"].append(perf)
for key in ("cost_basis", "total_return", "income", "currency_effect"):
total[key] += figures[key]
total["has_rate_history"] &= figures.get("has_rate_history", True)
@@ -388,4 +390,13 @@ def summarize(performances, convert=False):
total["money_weighted"] = money_weighted_return(
total["flows"], total["market_value"], total["as_of"]
)
# The same holdings in their own currencies, for a converted total
total["breakdown"] = (
sorted(
summarize(total["holdings"]),
key=lambda part: part["currency"] != total["currency"],
)
if total["converted_from"]
else []
)
return list(totals.values())
@@ -4,16 +4,15 @@
<div class="card-body p-5 gap-4">
<div class="flex flex-wrap items-end justify-between gap-x-6 gap-y-3">
<div>
<div class="text-xs uppercase tracking-wide text-base-content/60">{% translate 'Market value' %} · {{ c.name }}</div>
<div class="text-3xl font-bold tabular-nums">
<c-amount.display :amount="total.market_value" :prefix="c.prefix" :suffix="c.suffix" :decimal_places="c.decimal_places" divless></c-amount.display>
</div>
</div>
<div class="sm:text-right">
<div class="text-xs uppercase tracking-wide text-base-content/60">{% translate 'Total return' %}</div>
<div class="text-lg tabular-nums flex items-baseline gap-2 sm:justify-end">
<c-amount.display :amount="total.total_return" :prefix="c.prefix" :suffix="c.suffix" :decimal_places="c.decimal_places" color="auto" divless></c-amount.display>
<span class="text-sm">{% include 'investments/fragments/_mwr.html' with mw=total.money_weighted %}</span>
<span class="text-sm text-base-content/50">{% translate 'total return' %}</span>
</div>
</div>
</div>
@@ -28,9 +27,23 @@
{% endif %}
{% endif %}
</div>
{% if total.converted_from %}
<div class="text-xs text-base-content/40 -mt-2">
{% translate 'Includes holdings in' %} {% for currency in total.converted_from %}{{ currency.name }}{% if not forloop.last %}, {% endif %}{% endfor %}, {% translate 'converted at the rate of each date' %}
{% if total.breakdown %}
<div class="flex flex-col gap-1 -mx-2 -mb-2">
{% for part in total.breakdown %}
{% with p=part.currency %}
<details class="collapse collapse-arrow rounded-box">
<summary class="collapse-title min-h-0 py-2 px-2 pe-10 text-sm flex items-baseline justify-between gap-4">
<span class="text-base-content/60">{{ p.name }}</span>
<span class="tabular-nums"><c-amount.display :amount="part.market_value" :prefix="p.prefix" :suffix="p.suffix" :decimal_places="p.decimal_places" divless></c-amount.display></span>
</summary>
<div class="collapse-content px-2 text-sm text-base-content/60 flex flex-wrap gap-x-6 gap-y-1">
<div>{% translate 'Return' %} <span class="tabular-nums"><c-amount.display :amount="part.total_return" :prefix="p.prefix" :suffix="p.suffix" :decimal_places="p.decimal_places" color="auto" divless></c-amount.display></span> {% include 'investments/fragments/_mwr.html' with mw=part.money_weighted %}</div>
<div>{% translate 'Invested' %} <span class="text-base-content tabular-nums"><c-amount.display :amount="part.cost_basis" :prefix="p.prefix" :suffix="p.suffix" :decimal_places="p.decimal_places" divless></c-amount.display></span></div>
<div>{% translate 'Income' %} <span class="text-base-content tabular-nums"><c-amount.display :amount="part.income" :prefix="p.prefix" :suffix="p.suffix" :decimal_places="p.decimal_places" divless></c-amount.display></span></div>
</div>
</details>
{% endwith %}
{% endfor %}
</div>
{% endif %}
</div>
@@ -54,7 +54,6 @@
<div class="card-body p-5 gap-5">
<div class="flex flex-wrap items-end justify-between gap-x-6 gap-y-3">
<div>
<div class="text-xs uppercase tracking-wide text-base-content/60">{% translate 'Market value' %}</div>
<div class="text-4xl font-bold tabular-nums">
{% if perf.market_value is not None %}<c-amount.display :amount="perf.market_value" :prefix="c.prefix" :suffix="c.suffix" :decimal_places="c.decimal_places" divless></c-amount.display>{% else %}-{% endif %}
</div>