impl and test src.ledger.Files.Amend(old, new Delta)
cicd / ci (push) Successful in 1m15s Details

main
Bel LaPointe 2024-07-14 14:24:29 -06:00
parent 3e001b8ddd
commit f6b8b92bff
4 changed files with 155 additions and 45 deletions

View File

@ -26,10 +26,15 @@ func NewFiles(p string, q ...string) (Files, error) {
}
func (files Files) Amend(old, now Delta) error {
if now.isSet {
return fmt.Errorf("cannot ammend: immutable isSet is set")
}
xactions, err := files.transactions()
if err != nil {
return err
}
var transaction transaction
for _, xaction := range xactions {
if xaction.name != old.transaction {
@ -39,23 +44,13 @@ func (files Files) Amend(old, now Delta) error {
break
}
inverted := transaction.deltas()
for i := range inverted {
inverted[i].Value *= -1
}
if now.isSet {
return fmt.Errorf("cannot ammend: immutable isSet is set")
}
transaction.date = now.Date
transaction.description = now.Description
if transaction.payee == old.Name {
if len(transaction.recipients) != 1 {
return fmt.Errorf("cannot amend: modifying original payee, but many recipients cant share new value")
}
old.Value *= -1
old.Name = transaction.recipients[0].name
transaction.payee, transaction.recipients[0].name, transaction.recipients[0].value = transaction.recipients[0].name, transaction.payee, transaction.recipients[0].value*-1.0
}
idx := -1
for i, recipient := range transaction.recipients {
if recipient.name == old.Name && recipient.value == old.Value {
@ -63,16 +58,11 @@ func (files Files) Amend(old, now Delta) error {
}
}
if idx == -1 {
return fmt.Errorf("cannot amend: no recipient with name %s found to set new value", old.Name)
return fmt.Errorf("cannot amend: no recipient with name %q value %.2f found in %+v to set new value", old.Name, old.Value, transaction)
}
transaction.recipients[idx].name = now.Name
transaction.recipients[idx].value = now.Value
transaction.recipients[idx].currency = string(now.Currency)
amendedDeltas := transaction.deltas().Like(func(d Delta) bool {
return d.Name != transaction.payee
})
return files.Add(transaction.payee, append(inverted, amendedDeltas...)...)
old.Value *= -1
return files.Add(transaction.payee, []Delta{old, now}...)
}
func (files Files) TempGetLastNLines(n int) ([]string, error) {

View File

@ -17,13 +17,94 @@ func TestFileAmend(t *testing.T) {
now Delta
want string
}{
//"was set payee": {
//"payee": {
//"recipient": {
//"multi recipient": {
"multi recipient": {
from: `
2006-01-02 description
recipientA $3.45
recipientB $6.45
payee
`,
old: Delta{
Date: "2006-01-02",
Name: "recipientB",
Value: 6.45,
Currency: "$",
Description: "description",
},
now: Delta{
Date: "2106-11-12",
Name: "recipientC",
Value: 16.45,
Currency: "T",
Description: "1description",
},
want: `
2006-01-02 description
recipientB $-6.45
payee
2106-11-12 1description
recipientC 16.45 T
payee`,
},
"recipient": {
from: `
2006-01-02 description
recipient $3.45
payee $-3.45
`,
old: Delta{
Date: "2006-01-02",
Name: "recipient",
Value: 3.45,
Currency: "$",
Description: "description",
},
now: Delta{
Date: "2106-11-12",
Name: "1recipient",
Value: 13.45,
Currency: "T",
Description: "1description",
},
want: `
2006-01-02 description
recipient $-3.45
payee
2106-11-12 1description
1recipient 13.45 T
payee`,
},
"payee": {
from: `
2006-01-02 description
recipient $3.45
payee
`,
old: Delta{
Date: "2006-01-02",
Name: "payee",
Value: -3.45,
Currency: "$",
Description: "description",
},
now: Delta{
Date: "2106-11-12",
Name: "1payee",
Value: -13.45,
Currency: "T",
Description: "1description",
},
want: `
2006-01-02 description
payee $3.45
recipient
2106-11-12 1description
1payee -13.45 T
recipient`,
},
"was set": {
from: `
2006-01-02 description
2006-01-02 description
recipient $3.45
payee
`,
@ -36,11 +117,18 @@ func TestFileAmend(t *testing.T) {
},
now: Delta{
Date: "2106-11-12",
Name: "recipient",
Value: 3.45,
Currency: "$",
Description: "description",
Name: "1recipient",
Value: 13.45,
Currency: "T",
Description: "1description",
},
want: `
2006-01-02 description
recipient $-3.45
payee
2106-11-12 1description
1recipient 13.45 T
payee`,
},
}
@ -48,21 +136,28 @@ func TestFileAmend(t *testing.T) {
c := d
t.Run(name, func(t *testing.T) {
p := path.Join(t.TempDir(), "dat")
if err := os.WriteFile(p, bytes.TrimSpace([]byte(c.from)), os.ModePerm); err != nil {
if err := os.WriteFile(p, []byte(c.from), os.ModePerm); err != nil {
t.Fatal(err)
}
files, err := NewFiles(p)
if err != nil {
t.Fatal(err)
} else if deltas, err := files.Deltas(); err != nil {
t.Fatal(err)
} else if filtered := deltas.Like(func(d Delta) bool {
c.old.transaction = d.transaction
return d == c.old
}); len(filtered) != 1 {
t.Fatalf("input %s didnt include old %+v in %+v", c.from, c.old, deltas)
}
if err := files.Amend(c.old, c.now); err != nil {
t.Fatal(err)
} else if b, err := os.ReadFile(path.Join(path.Dir(p), "inbox.dat")); err != nil {
} else if b, err := os.ReadFile(path.Join(path.Dir(p), "inbox.txt")); err != nil {
t.Fatal(err)
} else if b := bytes.TrimSpace(b); string(b) != c.want {
t.Fatalf("expected \n\t%s\nbut got\n\t%s", c.want, b)
} else if string(b) != c.want {
t.Fatalf("expected \n\t%q\nbut got\n\t%q\n\t%s", c.want, b, b)
}
})
}

View File

@ -3,10 +3,12 @@ package ledger
import (
"bufio"
"bytes"
"errors"
"fmt"
"io"
"os"
"regexp"
"slices"
"strconv"
"unicode"
)
@ -112,19 +114,47 @@ func (files Files) _transactions(file string) ([]transaction, error) {
func readTransaction(name string, r *bufio.Reader) (transaction, error) {
result, err := _readTransaction(name, r)
if err != nil {
if err != nil && !errors.Is(err, io.EOF) {
return result, err
}
if result.empty() {
return result, nil
}
if result.payee == "" && len(result.recipients) < 2 {
return result, fmt.Errorf("found a transaction with no payee and less than 2 recipeints: %+v", result)
return result, err
}
if result.payee != "" && len(result.recipients) < 1 {
return result, fmt.Errorf("found a transaction with payee but no recipeints: %+v", result)
}
return result, nil
if result.payee == "" {
if len(result.recipients) < 2 {
return result, fmt.Errorf("found a transaction with no payee and less than 2 recipeints: %+v", result)
}
func() {
sumPerRecipient := map[string]float64{}
recipients := []string{}
for _, recipient := range result.recipients {
recipients = append(recipients, recipient.name)
sumPerRecipient[recipient.name] += recipient.value
}
slices.Sort(recipients)
for _, k := range recipients {
v := sumPerRecipient[k]
everyoneElse := 0.0
for j := range sumPerRecipient {
if k != j {
everyoneElse += sumPerRecipient[j]
}
}
if -1.0*v == everyoneElse {
result.payee = k
result.recipients = slices.DeleteFunc(result.recipients, func(recipient transactionRecipient) bool {
return recipient.name == k
})
return
}
}
return
}()
}
return result, err
}
func _readTransaction(name string, r *bufio.Reader) (transaction, error) {

View File

@ -33,13 +33,8 @@ func TestReadTransaction(t *testing.T) {
want: transaction{
date: "2003-04-05",
description: "Reasoning here",
payee: "",
payee: "A:B",
recipients: []transactionRecipient{
{
name: "A:B",
value: 1.0,
currency: "$",
},
{
name: "C:D",
value: -1.0,