From 85721d7815458c08b78d4b5a455386c1d5d0f683 Mon Sep 17 00:00:00 2001 From: Samuel Attwood Date: Mon, 28 Apr 2025 22:13:00 -0400 Subject: [PATCH] Add delayed reason --- example.yaml | 50 +++++++++++++++++++++++++++++++++ go.mod | 6 ++-- go.sum | 11 +++----- main.go | 78 ++++++++++++++++++++++++++++++++++++++++------------ 4 files changed, 118 insertions(+), 27 deletions(-) create mode 100644 example.yaml diff --git a/example.yaml b/example.yaml new file mode 100644 index 0000000..38808f9 --- /dev/null +++ b/example.yaml @@ -0,0 +1,50 @@ +end: 2025-05-01 +accounts: + - name: Capital One + balance: 4500 + minimum: 500 +credit: + - name: Amazon Store Card + amount: -438.35 + date: 2023-03-28 + - name: Amex Platinum + amount: -3263.42 + date: 2023-04-19 + - name: Apple Card + amount: -80.07 + date: 2023-04-03 + - name: Chase + amount: -0 + date: 2023-03-16 + - name: Citi + amount: -2307.84 + date: 2023-04-06 + - name: Discover + amount: -197.90 + date: 2023-04-03 +transactions: + - name: Bills + amount: -1690.00 + date: first + frequency: monthly + delayable: false + - name: Car Insurance + amount: -140.92 + date: first + frequency: monthly + delayable: false + - name: Savings + amount: -1000 + date: 14 + frequency: monthly + delayable: false + - name: Brokerage + amount: -1000 + date: 2023-04-30 + frequency: monthly + delayable: true + - name: Paycheck + amount: 3000 + date: 15 + frequency: biweekly + shift: early diff --git a/go.mod b/go.mod index 065c382..dda1eec 100644 --- a/go.mod +++ b/go.mod @@ -1,11 +1,11 @@ module github.com/samuelattwood/cashflow -go 1.20 +go 1.24 require ( - github.com/google/uuid v1.3.0 + github.com/google/uuid v1.6.0 github.com/sirupsen/logrus v1.9.3 gopkg.in/yaml.v3 v3.0.1 ) -require golang.org/x/sys v0.9.0 // indirect +require golang.org/x/sys v0.32.0 // indirect diff --git a/go.sum b/go.sum index 5d8792f..bef14f2 100644 --- a/go.sum +++ b/go.sum @@ -1,21 +1,18 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= -github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= -github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 h1:0A+M6Uqn+Eje4kHMK80dtF3JCXC4ykBgQG4Fe06QRhQ= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.9.0 h1:KS/R3tvhPqvJvwcKfnBHJwwthS11LRhmM5D59eEXa0s= -golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20= +golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/main.go b/main.go index db10cb6..9a5c3ed 100644 --- a/main.go +++ b/main.go @@ -19,9 +19,13 @@ import ( ) const ( - maxWidth = 100 + maxWidth = 100 + DelayWeekend DelayReason = "Weekend" + DelayInsufficient DelayReason = "Insufficient Funds" ) +type DelayReason string + type timeSlice []time.Time func (s timeSlice) Len() int { return len(s) } @@ -50,6 +54,12 @@ type Account struct { Minimum float64 `yaml:"minimum"` } +type AccountYaml struct { + Name string `yaml:"name"` + Balance string `yaml:"balance"` + Minimum string `yaml:"minimum"` +} + type Charge struct { Name string Late bool @@ -65,21 +75,22 @@ type Config struct { type ConfigYaml struct { Start string End string - Accounts []Account + Accounts []AccountYaml Credit []TransactionYaml Transactions []TransactionYaml } type Transaction struct { - Amount float64 - Date time.Time - Delayable bool - Frequency string - ID string - Delayed bool - Name string - OriginDate time.Time - Shift string + Amount float64 + Date time.Time + Delayable bool + Frequency string + ID string + Delayed bool + DelayedReason DelayReason + Name string + OriginDate time.Time + Shift string } type TransactionYaml struct { @@ -228,8 +239,33 @@ func parseTransaction(transactionYaml TransactionYaml) (*Transaction, error) { } func parseConfig(configYaml ConfigYaml) *Config { + accounts := make([]Account, 0) + for _, account := range configYaml.Accounts { + balanceStr := strings.ReplaceAll(account.Balance, "$", "") + balanceStr = strings.ReplaceAll(balanceStr, ",", "") + balance, err := strconv.ParseFloat(balanceStr, 32) + if err != nil { + logrus.Fatal(err) + } + var minimum float64 = 0 + if account.Minimum != "" { + minimumStr := strings.ReplaceAll(account.Minimum, "$", "") + minimumStr = strings.ReplaceAll(minimumStr, ",", "") + minimum, err = strconv.ParseFloat(minimumStr, 32) + if err != nil { + logrus.Fatal(err) + } + } + + accounts = append(accounts, Account{ + Name: account.Name, + Balance: balance, + Minimum: minimum, + }) + } + config := &Config{ - Accounts: configYaml.Accounts, + Accounts: accounts, Transactions: make([]*Transaction, 0), } @@ -368,6 +404,12 @@ func calculateShift(transactionLog TransactionLog, transaction Transaction) time newDate = newDate.AddDate(0, 0, add) } + if newDate.Before(transactionLog.first()) || newDate.After(transactionLog.last()) { + for isWeekend(newDate) { + newDate = newDate.AddDate(0, 0, add) + } + } + return newDate } @@ -391,7 +433,7 @@ func (tl *TransactionLog) delayTransaction(transaction *Transaction) { newDate = tl.last() } - if isWeekend(newDate) { + for isWeekend(newDate) { newDate = newDate.AddDate(0, 0, 1) } @@ -403,6 +445,9 @@ func (tl *TransactionLog) delayTransaction(transaction *Transaction) { tl.LogDays[newDate].Transactions[transaction.ID] = transaction transaction.Delayed = true + if transaction.DelayedReason == "" { + transaction.DelayedReason = DelayInsufficient + } } @@ -414,6 +459,7 @@ func (tl *TransactionLog) executeTransaction(transaction *Transaction) bool { if isWeekend(transaction.Date) && transaction.Delayable { logrus.Debugf("Delaying %s due to weekend scheduling\n", transaction.Name) tl.delayTransaction(transaction) + transaction.DelayedReason = DelayWeekend return false } @@ -658,10 +704,8 @@ func printOutput(logDay LogDay) { prefix = "+" } - if charge.Delayed { - suffix = fmt.Sprintf("(%s - %s) |", "Late", charge.OriginDate.Format("Jan 02 2006")) - } else if charge.Date != charge.OriginDate { - suffix = fmt.Sprintf("(%s - %s) |", "Weekend", charge.OriginDate.Format("Jan 02 2006")) + if charge.DelayedReason != "" { + suffix = fmt.Sprintf("(%s: %s - %s) |", "Delayed", charge.DelayedReason, charge.OriginDate.Format("Jan 02 2006")) } printTransactionLine(charge.Amount, charge.Name, prefix, suffix, fullWidth)