Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/go.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,14 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v3
with:
go-version: '>=1.20.0'
go-version: '>=1.26'
check-latest: true

- name: Build
run: go build -v ./...

- name: Test
run: go test -v -coverprofile=profile.cov . ./decimal
run: go test -v -coverprofile=profile.cov .

- uses: shogo82148/actions-goveralls@v1
with:
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ pkg
dist
html-book
src-book
.*
22 changes: 14 additions & 8 deletions balances.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import (
"slices"
"strings"

"github.com/howeyc/ledger/decimal"
"github.com/shopspring/decimal"
)

// GetBalances provided a list of transactions and filter strings, returns account balances of
Expand All @@ -14,13 +14,13 @@ import (
// Accounts are sorted by name.
func GetBalances(generalLedger []*Transaction, filterArr []string) []*Account {
var accList []*Account
balances := make(map[string]*Account)
balances := make(map[string]map[string]*Account)

// at every depth, for each account, track the parent account
depthMap := make(map[int]map[string]string)
var maxDepth int

incAccount := func(accName string, val decimal.Decimal) {
incAccount := func(accName string, currency string, val decimal.Decimal) {
// track parent
accDepth := strings.Count(accName, ":") + 1
pmap, pmapfound := depthMap[accDepth]
Expand All @@ -35,10 +35,14 @@ func GetBalances(generalLedger []*Transaction, filterArr []string) []*Account {
}

// add to balance
if acc, ok := balances[accName]; !ok {
acc := &Account{Name: accName, Balance: val}
if _, ok := balances[accName]; !ok {
balances[accName] = make(map[string]*Account)
}

if acc, ok := balances[accName][currency]; !ok {
acc := &Account{Name: accName, Currency: currency, Balance: val}
accList = append(accList, acc)
balances[accName] = acc
balances[accName][currency] = acc
} else {
acc.Balance = acc.Balance.Add(val)
}
Expand All @@ -53,15 +57,17 @@ func GetBalances(generalLedger []*Transaction, filterArr []string) []*Account {
}
}
if inFilter {
incAccount(accChange.Name, accChange.Balance)
incAccount(accChange.Name, accChange.Currency, accChange.Balance)
}
}
}

// roll-up balances
for curDepth := maxDepth; curDepth > 1; curDepth-- {
for accName, parentName := range depthMap[curDepth] {
incAccount(parentName, balances[accName].Balance)
for currency, acc := range balances[accName] {
incAccount(parentName, currency, acc.Balance)
}
}
}

Expand Down
52 changes: 49 additions & 3 deletions balances_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import (
"testing"
"time"

"github.com/howeyc/ledger/decimal"
"github.com/shopspring/decimal"
)

type testBalCase struct {
Expand Down Expand Up @@ -71,12 +71,58 @@ var testBalCases = []testBalCase{
},
nil,
},
{
"conversion",
`2026/01/21 Converted CZK to EUR
CZK -2000.00 @ 0.5
EUR 1000.00

2026/01/21 Converted CZK to EUR
CZK -2000.00 @@ 1000.00
EUR 1000.00
`,
[]Account{
{
Name: "CZK",
Balance: decimal.NewFromFloat(-4000),
},
{
Name: "EUR",
Balance: decimal.NewFromFloat(2000),
},
},
nil,
},
{
"conversion",
`2026/01/21 Converted CZK to EUR
CZK CZK -2000.00 @ 0.5
EUR EUR 1000.00

2026/01/21 Converted CZK to EUR
CZK CZK -2000.00 @@ 1000.00
EUR EUR 1000.00
`,
[]Account{
{
Name: "CZK",
Currency: "CZK",
Balance: decimal.NewFromFloat(-4000),
},
{
Name: "EUR",
Currency: "EUR",
Balance: decimal.NewFromFloat(2000),
},
},
nil,
},
}

func TestBalanceLedger(t *testing.T) {
for _, tc := range testBalCases {
b := bytes.NewBufferString(tc.data)
transactions, err := ParseLedger(b)
transactions, err := ParseLedger("", b)
bals := GetBalances(transactions, []string{})
if (err != nil && tc.err == nil) || (err != nil && tc.err != nil && err.Error() != tc.err.Error()) {
t.Errorf("Error: expected `%s`, got `%s`", tc.err, err)
Expand Down Expand Up @@ -143,7 +189,7 @@ func TestBalancesByPeriod(t *testing.T) {

`)

trans, _ := ParseLedger(b)
trans, _ := ParseLedger("", b)
partitionRb := BalancesByPeriod(trans, PeriodQuarter, RangePartition)
snapshotRb := BalancesByPeriod(trans, PeriodQuarter, RangeSnapshot)

Expand Down
Loading
Loading