Skip to content

Commit

Permalink
balance transaction during parsing
Browse files Browse the repository at this point in the history
  • Loading branch information
howeyc committed Jul 21, 2024
1 parent 3519f56 commit 7562606
Show file tree
Hide file tree
Showing 2 changed files with 33 additions and 44 deletions.
73 changes: 31 additions & 42 deletions parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,11 @@ func (lp *parser) parseTransaction(dateString, payeeString, payeeComment string)
return nil, derr
}

transBal := decimal.Zero
var numEmpty int
var emptyAccIndex int
var accIndex int

postings := make([]Account, 0, 2)
for lp.scanner.Scan() {
trimmedLine := lp.scanner.Text()
Expand Down Expand Up @@ -228,8 +233,33 @@ func (lp *parser) parseTransaction(dateString, payeeString, payeeComment string)
} else {
accChange.Name = strings.TrimSpace(trimmedLine)
}

postings = append(postings, accChange)

if accChange.Balance.IsZero() {
numEmpty++
emptyAccIndex = accIndex
}
accIndex++

transBal = transBal.Add(accChange.Balance)
}

if len(postings) < 2 {
err = errors.New("need at least two postings")
return
}

if !transBal.IsZero() {
switch numEmpty {
case 0:
return nil, errors.New("unable to balance transaction: no empty account to place extra balance")
case 1:
// If there is a single empty account, then it is obvious where to
// place the remaining balance.
postings[emptyAccIndex].Balance = transBal.Neg()
default:
return nil, errors.New("unable to balance transaction: more than one account empty")
}
}

trans = &Transaction{
Expand All @@ -241,46 +271,5 @@ func (lp *parser) parseTransaction(dateString, payeeString, payeeComment string)
}
lp.comments = nil

if transErr := balanceTransaction(trans); transErr != nil {
err = fmt.Errorf("unable to balance transaction: %w", transErr)
return
}
return
}

// Takes a transaction and balances it. This is mainly to fill in the empty part
// with the remaining balance.
func balanceTransaction(input *Transaction) error {
balance := decimal.Zero
var numEmpty int
var emptyAccIndex int
if len(input.AccountChanges) < 2 {
return errors.New("need at least two postings")
}
for accIndex, accChange := range input.AccountChanges {
if accChange.Balance.IsZero() {
numEmpty++
emptyAccIndex = accIndex
} else {
balance = balance.Add(accChange.Balance)
}
}

if balance.IsZero() {
// If everything adds up, then all is well.
return nil
}

switch numEmpty {
case 0:
return errors.New("no empty account to place extra balance")
case 1:
// If there is a single empty account, then it is obvious where to
// place the remaining balance.
input.AccountChanges[emptyAccIndex].Balance = balance.Neg()
default:
return errors.New("more than one account empty")
}

return nil
}
4 changes: 2 additions & 2 deletions parse_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,14 +75,14 @@ var testCases = []testCase{
`1970/01/01 Payee
Assets:Account 5`,
nil,
errors.New(":2: unable to parse transaction: unable to balance transaction: need at least two postings"),
errors.New(":2: unable to parse transaction: need at least two postings"),
},
{
"no posting",
`1970/01/01 Payee
`,
nil,
errors.New(":1: unable to parse transaction: unable to balance transaction: need at least two postings"),
errors.New(":1: unable to parse transaction: need at least two postings"),
},
{
"multiple empty",
Expand Down

0 comments on commit 7562606

Please sign in to comment.