Browse Source

starting ofx and xml generators

master
Alexander Avery 2 years ago
parent
commit
3601f449c9
  1. 82
      generators.go
  2. 56
      main.go
  3. 19
      transactions.go

82
generators.go

@ -0,0 +1,82 @@
package main
import (
"encoding/xml"
"github.com/aclindsa/ofxgo"
"time"
"fmt"
"github.com/ochinchina/go-ini"
"io"
)
func transactionsFromXml(r io.Reader, assets ini.Section, vendors []ini.Key) ([]transaction, error) {
type xmlTransaction struct {
XMLName xml.Name `xml:"transaction"`
Date string `xml:"Date"`
TrnAmt float64 `xml:"amount"`
Asset string `xml:"Asset"`
Expense string `xml:"Expense,omitempty"`
VendorName string `xml:"VendorName,omitempty"`
Currency string `xml:"Currency"`
}
type transactionParent struct {
XMLName xml.Name `xml:"transactions"`
Transactions []xmlTransaction `xml:"transaction"`
}
root := new(transactionParent)
d := xml.NewDecoder(r)
err := d.Decode(&root)
if err != nil {
return nil, fmt.Errorf("failed unmarshaling xml: %w", err)
}
xtx := root.Transactions
transactions := make([]transaction, 0)
for _, t := range xtx {
var expense string
if t.VendorName != "" && t.Expense == "" {
expense = matchPartial(vendors, t.VendorName)
}
time, err := time.Parse("1/02/2006", t.Date)
if err != nil {
return nil, fmt.Errorf("failed parsing time: %w", err)
}
tx := transaction {
Date: time,
TrnAmt: t.TrnAmt,
Asset: t.Asset,
Expense: expense,
VendorName: t.VendorName,
Currency: t.Currency,
}
transactions = append(transactions, tx)
}
return transactions, nil
}
func transactionsFromOfx(r io.Reader, assets ini.Section, vendors []ini.Key) ([]transaction, error) {
resp, err := ofxgo.ParseResponse(r)
if err != nil {
return nil, 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()
amt, _ := t.TrnAmt.Rat.Float64()
tx := transaction{
Date: t.DtPosted.Time,
TrnAmt: amt,
Asset: assets.GetValueWithDefault(acctId, acctId),
Expense: matchPartial(vendors, t.Name.String()),
VendorName: t.Name.String(),
Currency: stmt.CurDef.String(),
}
transactions = append(transactions, tx)
}
}
}
return transactions, nil
}

56
main.go

@ -1,19 +1,18 @@
package main package main
import ( import (
"flag"
"os"
"bufio" "bufio"
"flag"
"github.com/ochinchina/go-ini"
"io"
"log" "log"
"os"
"sort"
"strings" "strings"
"io"
"text/template" "text/template"
"sort"
"github.com/aclindsa/ofxgo"
"github.com/ochinchina/go-ini"
) )
func matchVendor(keys []ini.Key, givenName string) string { func matchPartial(keys []ini.Key, givenName string) string {
for _, k := range keys { for _, k := range keys {
v := k.ValueWithDefault("") v := k.ValueWithDefault("")
if strings.Contains(givenName, k.Name()) { if strings.Contains(givenName, k.Name()) {
@ -34,20 +33,24 @@ func generateLedgerTransactions(transactions []transaction, w io.Writer) error {
return nil return nil
} }
var (
config = flag.String("config", "", "configuration file containing all your vendor and account mappings")
inputType = flag.String("T", "ofx", "input type of transaction data - supports ofx and xml")
)
func main() { func main() {
var config = flag.String("config", "", "configuration file containing all your vendor and account mappings")
flag.Parse() flag.Parse()
filename := flag.Arg(0) filename := flag.Arg(0)
r, err := os.Open(filename) r, err := os.Open(filename)
if err != nil { if err != nil {
log.Fatal(err) log.Fatalf("failed opening data file: %w", err)
} }
defer r.Close() defer r.Close()
c, err := os.Open(*config) c, err := os.Open(*config)
if err != nil { if err != nil {
log.Fatal(err) log.Fatalf("failed opening config file: %w", err)
} }
defer c.Close() defer c.Close()
@ -56,33 +59,24 @@ func main() {
assets, err := cfg.GetSection("Assets") assets, err := cfg.GetSection("Assets")
if err != nil { if err != nil {
log.Fatal(err) log.Fatalf("failed finding assets section of config: %w", err)
} }
vendors, err := cfg.GetSection("Vendors") vendors, err := cfg.GetSection("Vendors")
if err != nil { if err != nil {
log.Fatal(err) log.Fatalf("failed finding vendors section of config: %w", err)
}
resp, err := ofxgo.ParseResponse(r)
if err != nil {
log.Fatal(err)
} }
transactions := make([]transaction, 0) var transactions []transaction
for _, m := range resp.Bank { if *inputType == "ofx" {
if stmt, ok := m.(*ofxgo.StatementResponse); ok { transactions, err = transactionsFromOfx(r, *assets, vendors.Keys())
for _, t := range stmt.BankTranList.Transactions { if err != nil {
acctId := stmt.BankAcctFrom.AcctID.String() log.Fatalf("failed generating transactions from ofx: %w", err)
tx := transaction{ }
Date: t.DtPosted.Time, } else if *inputType == "xml" {
TrnAmt: t.TrnAmt, transactions, err = transactionsFromXml(r, *assets, vendors.Keys())
Asset: assets.GetValueWithDefault(acctId, acctId), if err != nil {
Vendor: matchVendor(vendors.Keys(), t.Name.String()), log.Fatalf("failed generating transactions from xml: %w", err)
VendorName: t.Name.String(),
}
transactions = append(transactions, tx)
}
} }
} }

19
transactions.go

@ -2,24 +2,23 @@ package main
import ( import (
"time" "time"
"github.com/aclindsa/ofxgo"
) )
var transactionTemplate = var transactionTemplate = `
`
{{ range . }} {{ range . }}
{{ .Date.Format "2006-01-02" }} {{ .VendorName }} {{ .Date.Format "2006-01-02" }} {{ .VendorName }}
{{ .Asset }} ${{ .TrnAmt }} {{ .Asset }} {{ .TrnAmt }} {{ .Currency }}
{{ .Vendor }} {{ .Expense }}
{{ end }} {{ end }}
` `
type transaction struct { type transaction struct {
Date time.Time Date time.Time `xml:"Date"`
TrnAmt ofxgo.Amount TrnAmt float64 `xml:"Amount"`
Asset string Asset string `xml:"Asset"`
Vendor string Expense string `xml:"Expense,omitempty"`
VendorName string VendorName string `xml:"VendorName,omitempty"`
Currency string `xml:"Currency"`
} }
type byDate []transaction type byDate []transaction

Loading…
Cancel
Save