Created
November 5, 2019 19:56
-
-
Save julian-klode/199f18a5419b40d39094c2721ac2e058 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
///bin/true; exec /usr/bin/env go run "$0" "$@" | |
package main | |
import ( | |
"flag" | |
"log" | |
"os" | |
"regexp" | |
"runtime/pprof" | |
"strings" | |
"github.com/julian-klode/goledger" | |
"github.com/julian-klode/goledger/importer" | |
) | |
var accounts = map[string]string{ | |
// IBAN : account | |
} | |
// categories maps importer categories to accounts | |
var categories = map[importer.Category]string{ | |
importer.CategoryATM: "expenses:cash", | |
importer.CategoryFoodGroceries: "expenses:shopping:food", | |
importer.CategoryBarsRestaurants: "expenses:restaurants", | |
importer.CategoryShopping: "expenses:shopping", | |
importer.CategoryTransportCar: "expenses:transport", | |
importer.CategoryBusiness: "expenses:business", | |
importer.CategoryTravelHolidays: "expenses:travel", | |
} | |
// Mapping maintains a mapping from a keyword to an account | |
type Mapping struct { | |
Keyword string | |
Account string | |
} | |
var names = []Mapping{ | |
{"PAYPAL", "expenses:shopping"}, | |
{"AMAZON", "expenses:shopping"}, | |
{"Spende", "expenses:donations"}, | |
{"Atmosfair", "expenses:donations"}, | |
} | |
var cpuprofile = flag.String("cpuprofile", "", "write cpu profile to file") | |
func main() { | |
flag.Parse() | |
if *cpuprofile != "" { | |
f, err := os.Create(*cpuprofile) | |
if err != nil { | |
log.Fatal(err) | |
} | |
pprof.StartCPUProfile(f) | |
defer pprof.StopCPUProfile() | |
} | |
var transactions []importer.Transaction | |
var seen = make(map[string]bool) | |
for _, arg := range os.Args { | |
var ts []importer.Transaction | |
var err error | |
switch { | |
case strings.HasSuffix(arg, ".csv") && strings.Contains(arg, "contexts"): | |
ts, err = importer.HBCIParseFile(arg) | |
case strings.HasSuffix(arg, ".csv"): | |
ts, err = importer.LBBParseFile(arg) | |
case strings.HasSuffix(arg, ".json"): | |
ts, err = importer.N26ParseFile(arg) | |
default: | |
continue | |
} | |
if err != nil { | |
panic(err) | |
} | |
seen2 := make(map[string]bool) | |
for k, v := range seen { | |
seen2[k] = v | |
} | |
for _, t := range ts { | |
id := t.ID() | |
if seen[id] { | |
continue | |
} | |
seen2[id] = true | |
transactions = append(transactions, t) | |
} | |
seen = seen2 | |
} | |
for _, t := range transactions { | |
remote := accounts[t.RemoteAccount()] | |
var remote2 string | |
var value1 = t.Amount() | |
var value2 goledger.Decimal | |
for i := range names { | |
names[i].Keyword = strings.ToLower(names[i].Keyword) | |
} | |
if remote == "" { | |
for _, m := range names { | |
rem := strings.ToLower(t.RemoteName()) | |
if strings.Contains(rem, (m.Keyword)) || strings.Contains(strings.Replace(rem, " ", "", -1), (m.Keyword)) { | |
remote = m.Account | |
continue | |
} | |
ref := strings.ToLower(t.ReferenceText()) | |
if strings.Contains(ref, (m.Keyword)) || strings.Contains(strings.Replace(ref, " ", "", -1), (m.Keyword)) { | |
remote = m.Account | |
continue | |
} | |
} | |
} | |
// Ignore self transactions | |
if t.RemoteAccount() == t.LocalAccount() { | |
continue | |
} | |
local := accounts[t.LocalAccount()] | |
remoteByCategory := categories[t.Category()] | |
switch { | |
case strings.HasPrefix(t.RemoteName(), "OFD/HCC"): | |
remote = "income:bafög" | |
remote2 = "assets:loans:bafög" | |
value1 /= 2 | |
value2 = value1 | |
case strings.HasPrefix(t.RemoteName(), "GA "): | |
remote = "expenses:cash" | |
case remote == "" && remoteByCategory != "": | |
remote = remoteByCategory | |
case remote == "" && t.Amount() < 0: | |
remote = "expenses:misc" | |
case remote == "" && t.Amount() > 0: | |
remote = "income:misc" | |
// Gebühren Bondora Abhebung | |
case remote == "assets:bank:savings:bondora" && t.Amount() > 0: | |
value2 = -100 | |
value1 += 100 | |
remote2 = "expenses:bondora" | |
// Credit Agricole Zinsen | |
case remote == "assets:bank:savings:cacf" && t.Amount() > 0: | |
remote = "income:bank:cacf" | |
} | |
// We identified a transfer between two of our own bank accounts, | |
// add a transit node in the middle. | |
if strings.HasPrefix(local, "assets:bank") && strings.HasPrefix(remote, "assets:bank") && !strings.HasPrefix(remote, "assets:bank:savings") && !strings.HasPrefix(remote, "assets:bank:tomorrow") && !strings.Contains(t.ReferenceText(), "space:") { | |
remote = "assets:bank:transit" | |
} | |
var lt goledger.Transaction | |
lt.Parts = make([]goledger.TransactionPart, 0, 3) | |
lt.Date = t.Date() | |
lt.ValutaDate = t.ValutaDate() | |
switch { | |
case t.RemoteName() == "": | |
lt.Description = t.ReferenceText() | |
case t.ReferenceText() == "": | |
lt.Description = t.RemoteName() | |
default: | |
lt.Description = t.RemoteName() + " | " + t.ReferenceText() | |
} | |
lt.Parts = append(lt.Parts, goledger.TransactionPart{ | |
Account: local, | |
Value: t.Amount(), | |
Currency: t.Currency(), | |
}) | |
if strings.HasPrefix(lt.Description, "Debeka") { | |
var rules = []struct { | |
re *regexp.Regexp | |
account string | |
}{ | |
{regexp.MustCompile("AuslR ([0-9,]+)"), "expenses:contracts:insurance:debeka:reise"}, | |
{regexp.MustCompile("Kranken ([0-9,]+)"), "expenses:contracts:insurance:debeka:zahn"}, | |
{regexp.MustCompile("Unfall (?:[0-9.]+) ([0-9,]+)"), "expenses:contracts:insurance:debeka:unfall"}, | |
} | |
for _, rule := range rules { | |
if vals := rule.re.FindStringSubmatch(lt.Description); vals != nil { | |
var val goledger.Decimal | |
val.UnmarshalString(vals[1], ",") | |
lt.Parts = append(lt.Parts, goledger.TransactionPart{ | |
Account: rule.account, | |
Value: val, | |
Currency: t.Currency(), | |
}) | |
} | |
} | |
} else { | |
lt.Parts = append(lt.Parts, goledger.TransactionPart{ | |
Account: remote, | |
Value: -value1, | |
Currency: t.Currency(), | |
}) | |
if remote2 != "" { | |
lt.Parts = append(lt.Parts, goledger.TransactionPart{ | |
Account: remote2, | |
Value: -value2, | |
Currency: t.Currency(), | |
}) | |
} | |
} | |
lt.Print(os.Stdout) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment