impl and test src.ledger.Files.Amend(old, new Delta)
cicd / ci (push) Successful in 1m15s
Details
cicd / ci (push) Successful in 1m15s
Details
parent
3e001b8ddd
commit
f6b8b92bff
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
Loading…
Reference in New Issue