Count hand-valued assets into the wealth figure
A wealth figure that ignores the house is not a wealth figure. Assets without a market feed - a house, a car, a private loan - are now added by hand on the Wealth page with a stated value, a currency and the day the estimate was made; a negative value records a liability. They are registry entities in assets.finance like everything else, join the per-currency totals immediately, and a currency held only in an asset earns its own line.
This commit is contained in:
+47
-9
@@ -15,8 +15,11 @@ import (
|
||||
// same journal derives.
|
||||
type Wealth struct {
|
||||
Accounts []WealthAccount `json:"accounts"`
|
||||
// Totals is cash, position value and their sum per currency, across every
|
||||
// account.
|
||||
// Assets are the hand-valued possessions outside any account, echoed here
|
||||
// so the page that shows the total also shows what the total contains.
|
||||
Assets []WealthAsset `json:"assets"`
|
||||
// Totals is cash, position value, hand-valued assets and their sum per
|
||||
// currency, across every account.
|
||||
Totals []WealthTotal `json:"totals"`
|
||||
}
|
||||
|
||||
@@ -28,8 +31,11 @@ type WealthTotal struct {
|
||||
// in Unpriced, because valuing them at cost would report a number the
|
||||
// journal cannot support.
|
||||
Positions domain.Money `json:"positions"`
|
||||
Wealth domain.Money `json:"wealth"`
|
||||
Unpriced int `json:"unpriced"`
|
||||
// Assets is the stated value of every hand-valued asset in this currency,
|
||||
// and Wealth is cash, positions and assets together.
|
||||
Assets domain.Money `json:"assets"`
|
||||
Wealth domain.Money `json:"wealth"`
|
||||
Unpriced int `json:"unpriced"`
|
||||
}
|
||||
|
||||
// WealthAccount is one account's position as the journal records it.
|
||||
@@ -113,6 +119,17 @@ type WealthHolding struct {
|
||||
Records int `json:"records"`
|
||||
}
|
||||
|
||||
// WealthAsset is one hand-valued asset as the journal records it. The value is
|
||||
// stated, never quoted, and carries the day it was stated.
|
||||
type WealthAsset struct {
|
||||
AssetID string `json:"asset_id"`
|
||||
Name string `json:"name"`
|
||||
Kind string `json:"kind,omitempty"`
|
||||
Currency string `json:"currency"`
|
||||
Value domain.Money `json:"value"`
|
||||
ValuedAt string `json:"valued_at"`
|
||||
}
|
||||
|
||||
// WealthCheck is one named verification with its evidence. Failed marks a
|
||||
// disagreement inside the journal; the rest are notes that explain a figure
|
||||
// before it is compared with a broker's screen.
|
||||
@@ -282,11 +299,18 @@ func WealthOf(data domain.Dataset) Wealth {
|
||||
}
|
||||
}
|
||||
|
||||
report := Wealth{Accounts: []WealthAccount{}, Totals: []WealthTotal{}}
|
||||
report := Wealth{Accounts: []WealthAccount{}, Assets: []WealthAsset{}, Totals: []WealthTotal{}}
|
||||
totals := map[string]int64{}
|
||||
positionTotals := map[string]int64{}
|
||||
assetTotals := map[string]int64{}
|
||||
unpricedTotals := map[string]int{}
|
||||
currencies := []string{}
|
||||
seen := func(currency string) {
|
||||
if _, ok := totals[currency]; !ok {
|
||||
currencies = append(currencies, currency)
|
||||
totals[currency] = 0
|
||||
}
|
||||
}
|
||||
for _, account := range data.Accounts {
|
||||
st := state(account.ID)
|
||||
kind := account.Kind
|
||||
@@ -308,9 +332,7 @@ func WealthOf(data domain.Dataset) Wealth {
|
||||
})
|
||||
}
|
||||
}
|
||||
if _, seen := totals[account.Currency]; !seen {
|
||||
currencies = append(currencies, account.Currency)
|
||||
}
|
||||
seen(account.Currency)
|
||||
totals[account.Currency] += st.cash
|
||||
positions, unpriced, stale := int64(0), 0, []string{}
|
||||
for _, id := range st.order {
|
||||
@@ -391,11 +413,27 @@ func WealthOf(data domain.Dataset) Wealth {
|
||||
}
|
||||
report.Accounts = append(report.Accounts, entry)
|
||||
}
|
||||
// Hand-valued assets join the totals after the accounts: they belong to no
|
||||
// account, and a currency held only in an asset still earns its own line.
|
||||
for _, asset := range data.Assets {
|
||||
value, err := asset.Value.Minor()
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
seen(asset.Currency)
|
||||
assetTotals[asset.Currency] += value
|
||||
report.Assets = append(report.Assets, WealthAsset{
|
||||
AssetID: asset.ID, Name: asset.Name, Kind: asset.Kind,
|
||||
Currency: asset.Currency, Value: domain.FormatMoney(value), ValuedAt: asset.ValuedAt,
|
||||
})
|
||||
}
|
||||
slices.SortStableFunc(report.Assets, func(x, y WealthAsset) int { return strings.Compare(x.Name, y.Name) })
|
||||
for _, currency := range currencies {
|
||||
report.Totals = append(report.Totals, WealthTotal{
|
||||
Currency: currency, Cash: domain.FormatMoney(totals[currency]),
|
||||
Positions: domain.FormatMoney(positionTotals[currency]),
|
||||
Wealth: domain.FormatMoney(totals[currency] + positionTotals[currency]),
|
||||
Assets: domain.FormatMoney(assetTotals[currency]),
|
||||
Wealth: domain.FormatMoney(totals[currency] + positionTotals[currency] + assetTotals[currency]),
|
||||
Unpriced: unpricedTotals[currency],
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user