Compare commits

...

7 Commits

Author SHA1 Message Date
Bel LaPointe
4831914251 o my register is a balance series but not a transaction series try again
All checks were successful
cicd / ci (push) Successful in 1m52s
2024-12-12 22:18:38 -07:00
Bel LaPointe
eb3af4b54f ledger balances helpers like Sub, Sum, Invert 2024-12-12 22:17:08 -07:00
Bel LaPointe
849a8696f5 a reg 2024-12-12 22:10:16 -07:00
Bel LaPointe
1a2c88687f progress i guess 2024-12-12 21:59:51 -07:00
Bel LaPointe
889dc48d6c oo more tood 2024-12-12 21:53:18 -07:00
Bel LaPointe
881162357b no debug log 2024-12-12 21:50:18 -07:00
Bel LaPointe
62e65c47df support --period 2024-12-12 21:50:03 -07:00
5 changed files with 195 additions and 29 deletions

View File

@@ -3,7 +3,7 @@ package cli
type Config struct {
Files FileList
Query struct {
Period string
Period Period
Sort string
NoRounding bool
Depth int

View File

@@ -4,6 +4,7 @@ import (
"fmt"
"os"
"strings"
"time"
)
type FlagStringArray []string
@@ -33,3 +34,35 @@ func (fileList *FileList) Set(s string) error {
}
return (*FlagStringArray)(fileList).Set(s)
}
type Period struct {
Start time.Time
Stop time.Time
}
func (period Period) Empty() bool {
return period.Stop.Sub(period.Start) == 0
}
func (period *Period) String() string {
return fmt.Sprintf("%s..%s", period.Start, period.Stop)
}
func (period *Period) Set(s string) error {
if result, err := time.Parse("2006", s); err == nil {
period.Start = result
period.Stop = result.AddDate(1, 0, 0).Add(-1 * time.Minute)
return nil
}
if result, err := time.Parse("2006-01", s); err == nil {
period.Start = result
period.Stop = result.AddDate(0, 1, 0).Add(-1 * time.Minute)
return nil
}
if result, err := time.Parse("2006-01-02", s); err == nil {
period.Start = result
period.Stop = result.AddDate(0, 0, 1).Add(-1 * time.Minute)
return nil
}
return fmt.Errorf("unimplemented format: %s", s)
}

View File

@@ -3,6 +3,7 @@ package cli
import (
"flag"
"fmt"
"io"
"os"
"slices"
@@ -14,7 +15,7 @@ func Main() {
fs := flag.NewFlagSet(os.Args[0], flag.ContinueOnError)
fs.Var(&config.Files, "f", "paths to files")
fs.StringVar(&config.Query.Period, "period", "", "period")
fs.Var(&config.Query.Period, "period", "period")
fs.StringVar(&config.Query.Sort, "S", "", "sort ie date")
fs.BoolVar(&config.Query.NoRounding, "no-rounding", false, "no rounding")
fs.IntVar(&config.Query.Depth, "depth", 0, "depth grouping")
@@ -37,18 +38,38 @@ func Main() {
}
cmd := positional[0]
likes := []ledger.Like{}
for _, query := range positional[1:] {
panic(query)
likePattern := ""
notLikePattern := ""
i := 1
for i < len(positional) {
switch positional[i] {
case "not":
_ = notLikePattern
panic(positional[i:])
case "and":
panic(positional[i:])
default:
if likePattern == "" {
likePattern = positional[i]
} else {
likePattern = fmt.Sprintf("%s|%s", likePattern, positional[i])
}
}
i += 1
}
deltas, err := ledgerFiles.Deltas(likes...)
deltas, err := ledgerFiles.Deltas()
if err != nil {
panic(err)
}
if period := config.Query.Period; period != "" {
panic(period)
if period := config.Query.Period; !period.Empty() {
after := period.Start.Format("2006-01-02")
before := period.Stop.Format("2006-01-02")
deltas = deltas.Like(
ledger.LikeAfter(after),
ledger.LikeBefore(before),
)
}
if depth := config.Query.Depth; depth > 0 {
@@ -58,32 +79,67 @@ func Main() {
switch cmd[:3] {
case "bal":
balances := deltas.Balances()
keys := []string{}
for k := range balances {
keys = append(keys, k)
if likePattern != "" {
balances = balances.Like(likePattern)
}
slices.Sort(keys)
max := 0
for _, k := range keys {
if n := len(k); n > max {
max = n
}
if notLikePattern != "" {
balances = balances.NotLike(notLikePattern)
}
format := fmt.Sprintf("%%-%ds\t%%s%%.2f\n", max)
for _, key := range keys {
for currency, v := range balances[key] {
if currency != "$" {
currency += " "
}
fmt.Printf(format, key, currency, v)
}
}
FPrintBalances(os.Stdout, balances)
case "reg":
// register := deltas.Register()
register := deltas.Register()
if likePattern != "" {
register = register.Like(likePattern)
}
if notLikePattern != "" {
register = register.NotLike(notLikePattern)
}
var prev ledger.Balances
for _, date := range register.Dates() {
balances := register[date]
if newBalances := balances.Sub(prev).Nonzero(); len(newBalances) > 0 {
fmt.Println(date)
FPrintBalances(os.Stdout, newBalances)
}
prev = balances
}
default:
panic("unknown command " + positional[0])
}
}
func FPrintBalances(w io.Writer, balances ledger.Balances) {
keys := []string{}
for k := range balances {
keys = append(keys, k)
}
slices.Sort(keys)
max := 0
for _, k := range keys {
if n := len(k); n > max {
max = n
}
}
format := fmt.Sprintf("%%-%ds\t%%s%%.2f\n", max)
for _, key := range keys {
currencies := []ledger.Currency{}
for currency := range balances[key] {
currencies = append(currencies, currency)
}
slices.Sort(currencies)
for _, currency := range currencies {
printableCurrency := currency
if printableCurrency != "$" {
printableCurrency += " "
}
fmt.Fprintf(w, format, key, printableCurrency, balances[key][currency])
}
}
}

View File

@@ -11,6 +11,30 @@ type Balances map[string]Balance
type Balance map[Currency]float64
func (balances Balances) Sub(other Balances) Balances {
result := make(Balances)
for k, v := range balances {
result[k] = v.Sub(other[k])
}
for k, v := range other {
if _, ok := balances[k]; ok {
continue
}
result[k] = v.Invert()
}
return result
}
func (balances Balances) Nonzero() Balances {
result := make(Balances)
for k, v := range balances {
if nonzero := v.Nonzero(); len(nonzero) > 0 {
result[k] = nonzero
}
}
return result
}
func (balances Balances) NotLike(pattern string) Balances {
result := make(Balances)
p := regexp.MustCompile(pattern)
@@ -98,6 +122,39 @@ func (balances Balances) Push(d Delta) {
balances[d.Name].Push(d)
}
func (balance Balance) Sub(other Balance) Balance {
return balance.Sum(other.Invert())
}
func (balance Balance) Sum(other Balance) Balance {
result := make(Balance)
for k, v := range balance {
result[k] += v
}
for k, v := range other {
result[k] += v
}
return result
}
func (balance Balance) Nonzero() Balance {
result := make(Balance)
for k, v := range balance {
if v != 0 {
result[k] = v
}
}
return result
}
func (balance Balance) Invert() Balance {
result := make(Balance)
for k, v := range balance {
result[k] = v * -1.0
}
return result
}
func (balance Balance) Push(d Delta) {
if _, ok := balance[d.Currency]; !ok {
balance[d.Currency] = 0

View File

@@ -51,6 +51,26 @@ func (register Register) Names() []string {
return result
}
func (register Register) Like(pattern string) Register {
result := make(Register)
for k, v := range register {
if got := v.Like(pattern); len(got) > 0 {
result[k] = got
}
}
return result
}
func (register Register) NotLike(pattern string) Register {
result := make(Register)
for k, v := range register {
if got := v.NotLike(pattern); len(got) > 0 {
result[k] = got
}
}
return result
}
func (register Register) Dates() []string {
result := make([]string, 0, len(register))
for k := range register {