diff --git a/config.go b/config.go index 33b6394..69c2f64 100644 --- a/config.go +++ b/config.go @@ -112,6 +112,12 @@ type YNAB struct { // They'd still be unapproved until approved in YNAB. Cleared string `envconfig:"YNAB_CLEARED" default:"uncleared"` + // SwapFlow changes inflow to outflow and vice versa for any account with a + // IBAN number in the list. This maybe be relevant for credit card accounts. + // + // Example: "DK9520000123456789,NO8330001234567" + SwapFlow []string `envconfig:"YNAB_SWAPFLOW"` + ImportID ImportID } diff --git a/writer/ynab/ynab.go b/writer/ynab/ynab.go index d773120..57e50ed 100644 --- a/writer/ynab/ynab.go +++ b/writer/ynab/ynab.go @@ -94,7 +94,6 @@ func ynabberToYNAB(cfg ynabber.Config, t ynabber.Transaction) (Ytransaction, err } date := t.Date.Format("2006-01-02") - amount := t.Amount.String() // Trim consecutive spaces from memo and truncate if too long memo := strings.TrimSpace(space.ReplaceAllString(t.Memo, " ")) @@ -112,11 +111,21 @@ func ynabberToYNAB(cfg ynabber.Config, t ynabber.Transaction) (Ytransaction, err payee = payee[0:(maxPayeeSize - 1)] } + // If SwapFlow is defined check if the account is configured to swap inflow + // to outflow. If so swap it by using the Negate method. + if cfg.YNAB.SwapFlow != nil { + for _, account := range cfg.YNAB.SwapFlow { + if account == t.Account.IBAN { + t.Amount = t.Amount.Negate() + } + } + } + return Ytransaction{ ImportID: importIDMaker(cfg, t), AccountID: accountID, Date: date, - Amount: amount, + Amount: t.Amount.String(), PayeeName: payee, Memo: memo, Cleared: cfg.YNAB.Cleared, diff --git a/writer/ynab/ynab_test.go b/writer/ynab/ynab_test.go index 71cab38..335f9ff 100644 --- a/writer/ynab/ynab_test.go +++ b/writer/ynab/ynab_test.go @@ -1,6 +1,7 @@ package ynab import ( + "reflect" "testing" "time" @@ -106,3 +107,74 @@ func TestAccountParser(t *testing.T) { }) } } + +func TestYnabberToYNAB(t *testing.T) { + type args struct { + cfg ynabber.Config + t ynabber.Transaction + } + tests := []struct { + name string + args args + want Ytransaction + wantErr bool + }{ + { + name: "Default", + args: args{ + cfg: ynabber.Config{ + YNAB: ynabber.YNAB{ + AccountMap: map[string]string{"foobar": "abc"}, + }, + }, + t: ynabber.Transaction{ + Account: ynabber.Account{IBAN: "foobar"}, + Amount: 10000, + }, + }, + want: Ytransaction{ + AccountID: "abc", + Date: "0001-01-01", + Amount: "10000", + ImportID: "YBBR:e066d58050f67a602720e5f123f", + Approved: false, + }, + wantErr: false, + }, + { + name: "SwapFlow", + args: args{ + cfg: ynabber.Config{ + YNAB: ynabber.YNAB{ + SwapFlow: []string{"foobar"}, + AccountMap: map[string]string{"foobar": "abc"}, + }, + }, + t: ynabber.Transaction{ + Account: ynabber.Account{IBAN: "foobar"}, + Amount: 10000, + }, + }, + want: Ytransaction{ + AccountID: "abc", + Date: "0001-01-01", + Amount: "-10000", + ImportID: "YBBR:2e18b15a1a51f0c2278147a4ca5", + Approved: false, + }, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := ynabberToYNAB(tt.args.cfg, tt.args.t) + if (err != nil) != tt.wantErr { + t.Errorf("ynabberToYNAB() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("ynabberToYNAB() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/ynabber.go b/ynabber.go index d9901a6..20c94a5 100644 --- a/ynabber.go +++ b/ynabber.go @@ -27,6 +27,11 @@ func (p Payee) Strip(s []string) Payee { type Milliunits int64 +// Negate changes the sign of m to the opposite +func (m Milliunits) Negate() Milliunits { + return m * -1 +} + type Transaction struct { Account Account ID ID