rename Checking to Assets:Checking
This commit is contained in:
parent
3ca646004f
commit
97b407b345
10
cmd/init.go
10
cmd/init.go
|
@ -103,8 +103,8 @@ func emitSalary(file *os.File, start time.Time) {
|
|||
%s Salary
|
||||
Income:Salary:%s
|
||||
Assets:Debt:EPF %s INR
|
||||
Tax %s INR
|
||||
Checking %s INR
|
||||
Expenses:Tax %s INR
|
||||
Assets:Checking %s INR
|
||||
`, start.Format("2006/01/02"), company, formatFloat(salary*0.12), formatFloat(salary*0.20), formatFloat(salary*0.68)))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
|
@ -134,7 +134,7 @@ func emitEquityMutualFund(file *os.File, start time.Time, pricesTree map[string]
|
|||
_, err := file.WriteString(fmt.Sprintf(`
|
||||
%s Mutual Fund Nifty
|
||||
Assets:Equity:NIFTY %s NIFTY @ %s INR
|
||||
Checking
|
||||
Assets:Checking
|
||||
`, start.Format("2006/01/02"), formatFloat(10000/pc.Value*multiplier), formatFloat(pc.Value)))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
|
@ -144,7 +144,7 @@ func emitEquityMutualFund(file *os.File, start time.Time, pricesTree map[string]
|
|||
_, err = file.WriteString(fmt.Sprintf(`
|
||||
%s Mutual Fund Nifty Next 50
|
||||
Assets:Equity:NIFTY_JR %s NIFTY_JR @ %s INR
|
||||
Checking
|
||||
Assets:Checking
|
||||
`, start.Format("2006/01/02"), formatFloat(10000/pc.Value*multiplier), formatFloat(pc.Value)))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
|
@ -161,7 +161,7 @@ func emitDebtMutualFund(file *os.File, start time.Time, pricesTree map[string]*b
|
|||
_, err := file.WriteString(fmt.Sprintf(`
|
||||
%s Mutual Fund Birla Corporate Fund
|
||||
Assets:Debt:ABCBF %s ABCBF @ %s INR
|
||||
Checking
|
||||
Assets:Checking
|
||||
`, start.Format("2006/01/02"), formatFloat(10000/pc.Value*multiplier), formatFloat(pc.Value)))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
|
|
|
@ -9,15 +9,15 @@ tracked as a commodity. Few example transactions can be found below.
|
|||
Assets:Equity:NPS:SBI:E 15.9378 NPS_SBI_E @ 23.5289 INR
|
||||
;// account name units commodity purchase price
|
||||
;// name per unit
|
||||
Checking
|
||||
Assets:Checking
|
||||
|
||||
2019/02/21 NPS
|
||||
Assets:Equity:NPS:SBI:E 1557.2175 NPS_SBI_E @ 23.8406 INR
|
||||
Checking
|
||||
Assets:Checking
|
||||
|
||||
2020/06/25 Gold
|
||||
Assets:Gold 40 GOLD @ 4650 INR
|
||||
Checking
|
||||
Assets:Checking
|
||||
```
|
||||
|
||||
**paisa** comes with inbuilt support for fetching the latest price of
|
||||
|
|
|
@ -14,13 +14,13 @@ content
|
|||
```go
|
||||
2022/01/01 Salary
|
||||
Income:Salary:Acme -100,000 INR
|
||||
Checking 100,000 INR
|
||||
Assets:Checking 100,000 INR
|
||||
```
|
||||
|
||||
**ledger** follows the double-entry accounting system. In simple terms, it
|
||||
tracks the movement of money from debit account to credit
|
||||
account. Here `Income:Salary:Acme` is the debit account and
|
||||
`Checking` is the credit account. The date at which the
|
||||
`Assets:Checking` is the credit account. The date at which the
|
||||
transaction took place and a description of the transaction is written
|
||||
in the first line followed by the list of credit or debit
|
||||
entry. Account [naming conventions](./accounts.md) are explained later. The `:` in the account name
|
||||
|
@ -31,23 +31,23 @@ accounts must be zero.
|
|||
```go
|
||||
2022/01/01 Salary
|
||||
Income:Salary:Acme -100,000 INR
|
||||
Checking 100,000 INR
|
||||
Assets:Checking 100,000 INR
|
||||
|
||||
2022/02/01 Salary
|
||||
Income:Salary:Acme -100,000 INR
|
||||
Checking 100,000 INR
|
||||
Assets:Checking 100,000 INR
|
||||
|
||||
2022/03/01 Salary
|
||||
Income:Salary:Acme -100,000 INR
|
||||
Checking 100,000 INR
|
||||
Assets:Checking 100,000 INR
|
||||
```
|
||||
|
||||
let's add few more entries. The total balance in your savings account
|
||||
could be found by
|
||||
|
||||
```go
|
||||
❯ ledger -f personal.ledger balance Checking
|
||||
300,000 INR Checking
|
||||
❯ ledger -f personal.ledger balance Assets:Checking
|
||||
300,000 INR Assets:Checking
|
||||
```
|
||||
|
||||
Let's say your company deducts 12,000 INR and contributes it to EPF,
|
||||
|
@ -56,25 +56,25 @@ we could represent it as follows
|
|||
```go
|
||||
2022/01/01 Salary
|
||||
Income:Salary:Acme -100,000 INR
|
||||
Checking 88,000 INR
|
||||
Assets:Checking 88,000 INR
|
||||
Assets:Debt:EPF 12,000 INR
|
||||
|
||||
2022/02/01 Salary
|
||||
Income:Salary:Acme -100,000 INR
|
||||
Checking 88,000 INR
|
||||
Assets:Checking 88,000 INR
|
||||
Assets:Debt:EPF 12,000 INR
|
||||
|
||||
2022/03/01 Salary
|
||||
Income:Salary:Acme -100,000 INR
|
||||
Checking 88,000 INR
|
||||
Assets:Checking 88,000 INR
|
||||
Assets:Debt:EPF 12,000 INR
|
||||
```
|
||||
|
||||
|
||||
```shell
|
||||
❯ ledger -f personal.ledger balance Assets Checking
|
||||
❯ ledger -f personal.ledger balance Assets
|
||||
36,000 INR Assets:Debt:EPF
|
||||
264,000 INR Checking
|
||||
264,000 INR Assets:Checking
|
||||
--------------------
|
||||
300,000 INR
|
||||
```
|
||||
|
@ -88,17 +88,17 @@ month.
|
|||
|
||||
```go
|
||||
2018/01/01 Investment
|
||||
Checking -20,000 INR
|
||||
Assets:Checking -20,000 INR
|
||||
Assets:Equity:NIFTY 148.0865 NIFTY @ 67.5281 INR
|
||||
Assets:Equity:NIFTY_JR 358.6659 NIFTY_JR @ 27.8811 INR
|
||||
|
||||
2018/02/01 Investment
|
||||
Checking -20,000 INR
|
||||
Assets:Checking -20,000 INR
|
||||
Assets:Equity:NIFTY 140.2870 NIFTY @ 71.2824 INR
|
||||
Assets:Equity:NIFTY_JR 363.2242 NIFTY_JR @ 27.5312 INR
|
||||
|
||||
2018/03/01 Investment
|
||||
Checking -20,000 INR
|
||||
Assets:Checking -20,000 INR
|
||||
Assets:Equity:NIFTY 147.5908 NIFTY @ 67.7549 INR
|
||||
Assets:Equity:NIFTY_JR 378.4323 NIFTY_JR @ 26.4248 INR
|
||||
```
|
||||
|
|
|
@ -18,7 +18,7 @@ type Gain struct {
|
|||
|
||||
func GetGain(db *gorm.DB) gin.H {
|
||||
var postings []posting.Posting
|
||||
result := db.Where("account like ?", "Assets:%").Order("date ASC").Find(&postings)
|
||||
result := db.Where("account like ? and account != ?", "Assets:%", "Assets:Checking").Order("date ASC").Find(&postings)
|
||||
if result.Error != nil {
|
||||
log.Fatal(result.Error)
|
||||
}
|
||||
|
|
|
@ -28,7 +28,7 @@ func GetInvestment(db *gorm.DB) gin.H {
|
|||
var assets []posting.Posting
|
||||
var incomes []posting.Posting
|
||||
var expenses []posting.Posting
|
||||
result := db.Where("account like ? order by date asc", "Assets:%").Find(&assets)
|
||||
result := db.Where("account like ? and account != ? order by date asc", "Assets:%", "Assets:Checking").Find(&assets)
|
||||
if result.Error != nil {
|
||||
log.Fatal(result.Error)
|
||||
}
|
||||
|
|
|
@ -30,7 +30,9 @@ func GetLedger(db *gorm.DB) gin.H {
|
|||
}
|
||||
|
||||
postings = service.PopulateMarketPrice(db, postings)
|
||||
breakdowns := computeBreakdown(db, lo.Filter(postings, func(p posting.Posting, _ int) bool { return strings.HasPrefix(p.Account, "Assets:") }))
|
||||
breakdowns := computeBreakdown(db, lo.Filter(postings, func(p posting.Posting, _ int) bool {
|
||||
return strings.HasPrefix(p.Account, "Assets:")
|
||||
}))
|
||||
return gin.H{"postings": postings, "breakdowns": breakdowns}
|
||||
}
|
||||
|
||||
|
@ -51,14 +53,14 @@ func computeBreakdown(db *gorm.DB, postings []posting.Posting) map[string]Breakd
|
|||
for group, leaf := range accounts {
|
||||
ps := lo.Filter(postings, func(p posting.Posting, _ int) bool { return strings.HasPrefix(p.Account, group) })
|
||||
investmentAmount := lo.Reduce(ps, func(acc float64, p posting.Posting, _ int) float64 {
|
||||
if p.Amount < 0 || service.IsInterest(db, p) {
|
||||
if p.Account == "Assets:Checking" || p.Amount < 0 || service.IsInterest(db, p) {
|
||||
return acc
|
||||
} else {
|
||||
return acc + p.Amount
|
||||
}
|
||||
}, 0.0)
|
||||
withdrawalAmount := lo.Reduce(ps, func(acc float64, p posting.Posting, _ int) float64 {
|
||||
if p.Amount > 0 || service.IsInterest(db, p) {
|
||||
if p.Account == "Assets:Checking" || p.Amount > 0 || service.IsInterest(db, p) {
|
||||
return acc
|
||||
} else {
|
||||
return acc + -p.Amount
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
)
|
||||
|
||||
func XIRR(db *gorm.DB, ps []posting.Posting) float64 {
|
||||
ps = lo.Filter(ps, func(p posting.Posting, _ int) bool { return p.Account != "Assets:Checking" })
|
||||
today := time.Now()
|
||||
marketAmount := lo.Reduce(ps, func(acc float64, p posting.Posting, _ int) float64 { return acc + p.MarketAmount }, 0.0)
|
||||
payments := lo.Reverse(lo.Map(ps, func(p posting.Posting, _ int) xirr.Payment {
|
||||
|
|
|
@ -114,14 +114,24 @@ function renderBreakdowns(breakdowns: Breakdown[]) {
|
|||
<td style='max-width: 200px; overflow: hidden;'>${indent}${lastName(
|
||||
b.group
|
||||
)}</td>
|
||||
<td class='has-text-right'>${formatCurrency(b.investment_amount)}</td>
|
||||
<td class='has-text-right'>${formatCurrency(b.withdrawal_amount)}</td>
|
||||
<td class='has-text-right'>${
|
||||
b.investment_amount != 0 ? formatCurrency(b.investment_amount) : ""
|
||||
}</td>
|
||||
<td class='has-text-right'>${
|
||||
b.withdrawal_amount != 0 ? formatCurrency(b.withdrawal_amount) : ""
|
||||
}</td>
|
||||
<td class='has-text-right'>${
|
||||
b.balance_units > 0 ? formatFloat(b.balance_units, 4) : ""
|
||||
}</td>
|
||||
<td class='has-text-right'>${formatCurrency(b.market_amount)}</td>
|
||||
<td class='${changeClass} has-text-right'>${formatCurrency(gain)}</td>
|
||||
<td class='${changeClass} has-text-right'>${formatFloat(b.xirr)}</td>
|
||||
<td class='has-text-right'>${
|
||||
b.market_amount != 0 ? formatCurrency(b.market_amount) : ""
|
||||
}</td>
|
||||
<td class='${changeClass} has-text-right'>${
|
||||
b.investment_amount != 0 && gain != 0 ? formatCurrency(gain) : ""
|
||||
}</td>
|
||||
<td class='${changeClass} has-text-right'>${
|
||||
b.xirr > 0.0001 ? formatFloat(b.xirr) : ""
|
||||
}</td>
|
||||
`;
|
||||
});
|
||||
}
|
||||
|
|
|
@ -24,8 +24,10 @@ export default async function () {
|
|||
current.withdrawal_amount
|
||||
)
|
||||
);
|
||||
setHtml("investment", formatCurrency(current.investment_amount));
|
||||
setHtml("withdrawal", formatCurrency(current.withdrawal_amount));
|
||||
setHtml(
|
||||
"investment",
|
||||
formatCurrency(current.investment_amount - current.withdrawal_amount)
|
||||
);
|
||||
setHtml("gains", formatCurrency(current.gain_amount));
|
||||
setHtml("xirr", formatFloat(xirr));
|
||||
|
||||
|
@ -48,22 +50,20 @@ function renderOverview(points: Overview[], element: Element) {
|
|||
const colors = ["#b2df8a", "#fb9a99"];
|
||||
const areaScale = d3.scaleOrdinal().domain(areaKeys).range(colors);
|
||||
|
||||
const lineKeys = ["networth", "investment", "withdrawal"];
|
||||
const lineKeys = ["networth", "investment"];
|
||||
const lineScale = d3
|
||||
.scaleOrdinal<string>()
|
||||
.domain(lineKeys)
|
||||
.range(["#1f77b4", "#17becf", "#ff7f0e"]);
|
||||
|
||||
const positions = _.flatMap(points, (p) => [
|
||||
p.gain_amount + p.investment_amount - p.withdrawal_amount,
|
||||
p.investment_amount - p.withdrawal_amount
|
||||
]);
|
||||
positions.push(0);
|
||||
|
||||
const x = d3.scaleTime().range([0, width]).domain([start, end]),
|
||||
y = d3
|
||||
.scaleLinear()
|
||||
.range([height, 0])
|
||||
.domain([
|
||||
0,
|
||||
d3.max<Overview, number>(
|
||||
points,
|
||||
(d) => d.gain_amount + d.investment_amount
|
||||
)
|
||||
]),
|
||||
y = d3.scaleLinear().range([height, 0]).domain(d3.extent(positions)),
|
||||
z = d3.scaleOrdinal<string>(colors).domain(areaKeys);
|
||||
|
||||
const area = (y0, y1) =>
|
||||
|
@ -103,7 +103,7 @@ function renderOverview(points: Overview[], element: Element) {
|
|||
.attr(
|
||||
"d",
|
||||
area(height, (d) => {
|
||||
return y(d.gain_amount + d.investment_amount);
|
||||
return y(d.gain_amount + d.investment_amount - d.withdrawal_amount);
|
||||
})
|
||||
);
|
||||
|
||||
|
@ -115,7 +115,7 @@ function renderOverview(points: Overview[], element: Element) {
|
|||
.attr(
|
||||
"d",
|
||||
area(0, (d) => {
|
||||
return y(d.gain_amount + d.investment_amount);
|
||||
return y(d.gain_amount + d.investment_amount - d.withdrawal_amount);
|
||||
})
|
||||
);
|
||||
|
||||
|
@ -130,7 +130,7 @@ function renderOverview(points: Overview[], element: Element) {
|
|||
.attr(
|
||||
"d",
|
||||
area(0, (d) => {
|
||||
return y(d.investment_amount);
|
||||
return y(d.investment_amount - d.withdrawal_amount);
|
||||
})
|
||||
);
|
||||
|
||||
|
@ -145,7 +145,7 @@ function renderOverview(points: Overview[], element: Element) {
|
|||
.attr(
|
||||
"d",
|
||||
area(height, (d) => {
|
||||
return y(d.investment_amount);
|
||||
return y(d.investment_amount - d.withdrawal_amount);
|
||||
})
|
||||
);
|
||||
|
||||
|
@ -159,21 +159,7 @@ function renderOverview(points: Overview[], element: Element) {
|
|||
.line<Overview>()
|
||||
.curve(d3.curveBasis)
|
||||
.x((d) => x(d.timestamp))
|
||||
.y((d) => y(d.investment_amount))
|
||||
);
|
||||
|
||||
layer
|
||||
.append("path")
|
||||
.style("stroke", lineScale("withdrawal"))
|
||||
.style("fill", "none")
|
||||
.attr(
|
||||
"d",
|
||||
d3
|
||||
.line<Overview>()
|
||||
.curve(d3.curveBasis)
|
||||
.defined((d) => d.withdrawal_amount > 0)
|
||||
.x((d) => x(d.timestamp))
|
||||
.y((d) => y(d.withdrawal_amount))
|
||||
.y((d) => y(d.investment_amount - d.withdrawal_amount))
|
||||
);
|
||||
|
||||
layer
|
||||
|
@ -192,7 +178,7 @@ function renderOverview(points: Overview[], element: Element) {
|
|||
svg
|
||||
.append("g")
|
||||
.attr("class", "legendOrdinal")
|
||||
.attr("transform", "translate(365,3)");
|
||||
.attr("transform", "translate(265,3)");
|
||||
|
||||
const legendOrdinal = legend
|
||||
.legendColor()
|
||||
|
|
|
@ -42,16 +42,10 @@
|
|||
</div>
|
||||
<div class="level-item has-text-centered">
|
||||
<div>
|
||||
<p class="heading">Investment</p>
|
||||
<p class="heading">Net Investment</p>
|
||||
<p class="d3-investment title"></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="level-item has-text-centered">
|
||||
<div>
|
||||
<p class="heading">Withdrawal</p>
|
||||
<p class="d3-withdrawal title"></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="level-item has-text-centered">
|
||||
<div>
|
||||
<p class="heading">Gain</p>
|
||||
|
|
Loading…
Reference in New Issue