Browse Source

generate simple transactions

master
Alexander Avery 2 years ago
parent
commit
4feece9549
  1. 10
      README.md
  2. 10
      go.mod
  3. 11
      go.sum
  4. 7
      lox.cfg.example
  5. 99
      main.go
  6. 37
      transactions.go

10
README.md

@ -1,3 +1,9 @@
# lox
# lox - ledger from ofx
utility for generating ledger transactions from ofx
Simple command line utility to generate ledger transactions from OFX xml data. The generated ledger transactions are printed to stdout. The OFX data can come from other programs or the disk, lox does not support connecting to your bank or other financial institutions; it simply reads from the disk, or stdin.
You can specify a configuration file to map vendor names and account numbers to specific account names in your ledger files. A basic example configuration is provided in this repository as `lox.cfg.example`
Programs and libraries you can use with lox:
* [ofxtools](https://pypi.org/project/ofxtools/) - Python library and cli for downloading financial information
* [ofxgo](https://github.com/aclindsa/ofxgo) - Go library for downloading financial information (lox uses ofxgo to parse ofx data)

10
go.mod

@ -0,0 +1,10 @@
module beetbox.io/lox
go 1.17
require (
github.com/aclindsa/ofxgo v0.1.3 // indirect
github.com/aclindsa/xml v0.0.0-20201125035057-bbd5c9ec99ac // indirect
github.com/ochinchina/go-ini v1.0.1 // indirect
golang.org/x/text v0.3.7 // indirect
)

11
go.sum

@ -0,0 +1,11 @@
github.com/aclindsa/ofxgo v0.1.3 h1:20Ckjpg5gG4rdGh2juGfa5I1gnWULMXGWxpseVLCVaM=
github.com/aclindsa/ofxgo v0.1.3/go.mod h1:q2mYxGiJr5X3rlyoQjQq+qqHAQ8cTLntPOtY0Dq0pzE=
github.com/aclindsa/xml v0.0.0-20201125035057-bbd5c9ec99ac h1:xCNSfPWpcx3Sdz/+aB/Re4L8oA6Y4kRRRuTh1CHCDEw=
github.com/aclindsa/xml v0.0.0-20201125035057-bbd5c9ec99ac/go.mod h1:GjqOUT8xlg5+T19lFv6yAGNrtMKkZ839Gt4e16mBXlY=
github.com/ochinchina/go-ini v1.0.1 h1:qrKGrgxJjY+4H8aV7B2HPohShzHGrymW+/X1Gx933zU=
github.com/ochinchina/go-ini v1.0.1/go.mod h1:Tqs5+JmccLSNMX1KXbbyG/B3ro4J9uXVYC5U5VOeRE8=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=

7
lox.cfg.example

@ -0,0 +1,7 @@
[Assets]
XXXXXXXXX0 = Assets:Banks:Checking
XXXXXXXXX1 = Assets:Banks:Savings
[Vendors]
TRADER JOE = Expenses:Food:Groceries
DUNKIN = Expenses:Food:Restaurants

99
main.go

@ -0,0 +1,99 @@
package main
import (
"flag"
"os"
"bufio"
"log"
"strings"
"io"
"text/template"
"sort"
"github.com/aclindsa/ofxgo"
"github.com/ochinchina/go-ini"
)
func matchVendor(keys []ini.Key, givenName string) string {
for _, k := range keys {
v := k.ValueWithDefault("")
if strings.Contains(givenName, k.Name()) {
return v
}
}
return "Expenses"
}
func generateLedgerTransactions(transactions []transaction, w io.Writer) error {
t := template.New("ledger")
_, err := t.Parse(transactionTemplate)
if err != nil {
return err
}
t.Execute(w, transactions)
return nil
}
func main() {
var config = flag.String("config", "", "configuration file containing all your vendor and account mappings")
flag.Parse()
filename := flag.Arg(0)
r, err := os.Open(filename)
if err != nil {
log.Fatal(err)
}
defer r.Close()
c, err := os.Open(*config)
if err != nil {
log.Fatal(err)
}
defer c.Close()
cfg := ini.NewIni()
cfg.LoadReader(c)
assets, err := cfg.GetSection("Assets")
if err != nil {
log.Fatal(err)
}
vendors, err := cfg.GetSection("Vendors")
if err != nil {
log.Fatal(err)
}
resp, err := ofxgo.ParseResponse(r)
if err != nil {
log.Fatal(err)
}
transactions := make([]transaction, 0)
for _, m := range resp.Bank {
if stmt, ok := m.(*ofxgo.StatementResponse); ok {
for _, t := range stmt.BankTranList.Transactions {
acctId := stmt.BankAcctFrom.AcctID.String()
tx := transaction{
Date: t.DtPosted.Time,
TrnAmt: t.TrnAmt,
Asset: assets.GetValueWithDefault(acctId, acctId),
Vendor: matchVendor(vendors.Keys(), t.Name.String()),
VendorName: t.Name.String(),
}
transactions = append(transactions, tx)
}
}
}
w := bufio.NewWriter(os.Stdout)
sort.Sort(byDate(transactions))
err = generateLedgerTransactions(transactions, w)
if err != nil {
log.Fatal(err)
}
err = w.Flush()
if err != nil {
log.Fatal(err)
}
}

37
transactions.go

@ -0,0 +1,37 @@
package main
import (
"time"
"github.com/aclindsa/ofxgo"
)
var transactionTemplate =
`
{{ range . }}
{{ .Date.Format "2006-01-02" }} {{ .VendorName }}
{{ .Asset }} ${{ .TrnAmt }}
{{ .Vendor }}
{{ end }}
`
type transaction struct {
Date time.Time
TrnAmt ofxgo.Amount
Asset string
Vendor string
VendorName string
}
type byDate []transaction
func (b byDate) Len() int {
return len(b)
}
func (b byDate) Less(i, j int) bool {
return b[i].Date.Before(b[j].Date)
}
func (b byDate) Swap(i, j int) {
b[i], b[j] = b[j], b[i]
}
Loading…
Cancel
Save