Add delayed reason

This commit is contained in:
2025-04-28 22:13:00 -04:00
parent fc228d329e
commit 85721d7815
4 changed files with 118 additions and 27 deletions

50
example.yaml Normal file
View File

@@ -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

6
go.mod
View File

@@ -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

11
go.sum
View File

@@ -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=

78
main.go
View File

@@ -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)