Logic fixes and general cleanup

This commit is contained in:
2023-03-11 02:37:54 -05:00
parent 746b7ec657
commit 8a0ad44041

538
main.go
View File

@@ -5,7 +5,10 @@ import (
"log"
"math"
"os"
"os/exec"
"regexp"
"sort"
"strconv"
"strings"
"time"
@@ -21,6 +24,22 @@ func (s timeSlice) Len() int { return len(s) }
func (s timeSlice) Less(i, j int) bool { return s[i].Before(s[j]) }
func (s timeSlice) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
type transactionList []*Transaction
func (t transactionList) Len() int { return len(t) }
func (t transactionList) Less(i, j int) bool {
if t[i].Date.Before(t[j].Date) {
return true
}
if t[i].Amount < 0 && t[j].Amount < 0 {
return math.Abs(t[i].Amount) > math.Abs(t[j].Amount)
}
return t[i].Amount > t[j].Amount
}
func (t transactionList) Swap(i, j int) { t[i], t[j] = t[j], t[i] }
type Account struct {
Name string `yaml:"name"`
Balance float64 `yaml:"balance"`
@@ -36,8 +55,7 @@ type Charge struct {
type Config struct {
Accounts []Account
Credit []Transaction
Transactions []Transaction
Transactions transactionList
}
type ConfigYaml struct {
@@ -49,23 +67,22 @@ type ConfigYaml struct {
}
type Transaction struct {
Amount float64
Date time.Time
Delayable bool
Frequency string
HardDate bool
ID string
Late bool
Name string
OrigDate time.Time
Shift string
Amount float64
Date time.Time
Delayable bool
Frequency string
ID string
Late bool
Name string
OriginDate time.Time
Shift string
}
type TransactionYaml struct {
Name string `yaml:"name"`
Amount float64 `yaml:"amount"`
Date string `yaml:"date"`
Delayable *bool `yaml:"delayable"`
Delayable string `yaml:"delayable"`
Frequency string `yaml:"frequency,omitempty"`
Shift string `yaml:"shift"`
}
@@ -85,9 +102,9 @@ type LogDay struct {
Transactions map[string]*Transaction
}
func (t TransactionLog) dates() timeSlice {
func (tl *TransactionLog) dates() timeSlice {
dateSlice := make(timeSlice, 0)
for date, _ := range t.LogDays {
for date := range tl.LogDays {
dateSlice = append(dateSlice, date)
}
@@ -102,17 +119,16 @@ func (t TransactionLog) first() time.Time {
func (t TransactionLog) last() time.Time {
return t.dates()[t.dates().Len()-1]
}
func (logDay LogDay) transactions() []*Transaction {
transactions := make([]*Transaction, 0)
func (logDay LogDay) transactions() transactionList {
transactions := transactionList{}
for _, t := range logDay.Transactions {
transactions = append(transactions, t)
}
sortTransactions(transactions)
sort.Sort(transactions)
return transactions
}
@@ -141,55 +157,75 @@ func previousLogDay(transactionLog *TransactionLog, currentDate time.Time) *LogD
return transactionLog.LogDays[previous]
}
func parseConfig(configYaml *ConfigYaml) Config {
config := Config{
func isWeekend(date time.Time) bool {
if date.Weekday() == 0 || date.Weekday() == 6 {
return true
}
return false
}
func parseDelayable(delayableString string) (bool, error) {
if delayableString == "" {
return true, nil
}
delayable, err := strconv.ParseBool(delayableString)
if err != nil {
return false, err
}
return delayable, nil
}
func parseTransaction(transactionYaml TransactionYaml) (*Transaction, error) {
date, err := parseDate(transactionYaml.Date)
if err != nil {
return nil, err
}
if transactionYaml.Shift == "" {
transactionYaml.Shift = "late"
}
delayable, err := parseDelayable(transactionYaml.Delayable)
if err != nil {
return nil, err
}
transaction := &Transaction{
Amount: transactionYaml.Amount,
Date: date,
Delayable: delayable,
Frequency: transactionYaml.Frequency,
ID: uuid.NewString(),
Late: false,
Name: transactionYaml.Name,
OriginDate: date,
Shift: transactionYaml.Shift,
}
return transaction, nil
}
func parseConfig(configYaml ConfigYaml) *Config {
config := &Config{
Accounts: configYaml.Accounts,
Credit: make([]Transaction, 0),
Transactions: make([]Transaction, 0),
Transactions: make([]*Transaction, 0),
}
for _, transaction := range configYaml.Credit {
formalDate, hardDate := parseDate(transaction.Date, transaction.Frequency)
if transaction.Delayable == nil {
transaction.Delayable = &[]bool{true}[0]
newTransaction, err := parseTransaction(transaction)
if err != nil {
logrus.Error(err)
}
if transaction.Shift == "" {
transaction.Shift = "late"
}
newTransaction := Transaction{
Amount: transaction.Amount,
Date: formalDate,
Delayable: *transaction.Delayable,
Frequency: transaction.Frequency,
HardDate: hardDate,
ID: uuid.NewString(),
Late: false,
Name: transaction.Name,
OrigDate: formalDate,
Shift: transaction.Shift,
}
config.Credit = append(config.Credit, newTransaction)
config.Transactions = append(config.Transactions, newTransaction)
}
for _, transaction := range configYaml.Transactions {
formalDate, hardDate := parseDate(transaction.Date, transaction.Frequency)
if transaction.Delayable == nil {
transaction.Delayable = &[]bool{true}[0]
}
if transaction.Shift == "" {
transaction.Shift = "late"
}
newTransaction := Transaction{
Amount: transaction.Amount,
Date: formalDate,
Delayable: *transaction.Delayable,
Frequency: transaction.Frequency,
HardDate: hardDate,
ID: uuid.NewString(),
Late: false,
Name: transaction.Name,
OrigDate: formalDate,
Shift: transaction.Shift,
newTransaction, err := parseTransaction(transaction)
if err != nil {
logrus.Error(err)
}
config.Transactions = append(config.Transactions, newTransaction)
}
@@ -197,125 +233,110 @@ func parseConfig(configYaml *ConfigYaml) Config {
return config
}
func getFirstDate(date time.Time, frequency string) time.Time {
var firstDate time.Time
current := time.Date(time.Now().Year(),
time.Now().Month(), time.Now().Day(), 0, 0, 0, 0, time.UTC)
if date.Before(current) && frequency != "" {
firstDate = calculateFrequency(frequency, date, date.AddDate(0, 1, 1), false)[0]
} else {
firstDate = date.AddDate(0, 1, 0)
}
return firstDate
func dateIsFixed(dateString string) (bool, error) {
return regexp.MatchString("^[0-9]{4}-[0-9]{1,2}-[0-9]{1,2}$", dateString)
}
func parseDate(date, frequency string) (time.Time, bool) {
hardDate := true
base := time.Now().AddDate(0, 0, -time.Now().Day()+1)
current := time.Date(base.Year(),
base.Month(), base.Day(), 0, 0, 0, 0, time.UTC)
dateString := string(date)
formalDate, err := time.Parse("2006-1-2", dateString)
func parseDate(dateString string) (time.Time, error) {
currentDay := currentDay()
regMatch, err := regexp.MatchString("^[0-9]{4}-[0-9]{1,2}-[0-9]{1,2}$", dateString)
if err != nil {
hardDate = false
if date == "first" {
formalDate = current
if formalDate.Before(time.Now()) {
formalDate = getFirstDate(formalDate, frequency)
}
} else if date == "last" {
formalDate = lastOfMonth(current)
return time.Time{}, err
}
if regMatch {
return time.Parse("2006-1-2", dateString)
}
if dateString == "first" {
if currentDay.Day() == 1 {
return currentDay, nil
} else {
dateString = fmt.Sprintf("%d-%d-%s",
time.Now().Year(), int(time.Now().Month()), date)
formalDate, _ = time.Parse("2006-1-2", dateString)
if formalDate.Before(time.Now()) {
formalDate = getFirstDate(formalDate, frequency)
}
return firstOfMonth(currentDay).AddDate(0, 1, 0), nil
}
}
return formalDate, hardDate
}
func preFetchDates(transactions []Transaction, transactionLog *TransactionLog) {
for _, transaction := range transactions {
if _, ok := transactionLog.LogDays[transaction.Date]; !ok && transaction.Amount != 0 {
logDay := LogDay{
Date: transaction.Date,
Transactions: make(map[string]*Transaction),
}
transactionLog.LogDays[transaction.Date] = &logDay
}
if dateString == "last" {
return lastOfMonth(currentDay), nil
}
logDates := transactionLog.dates()
transactionLog.StartDate = logDates[0]
transactionLog.EndDate = logDates[len(logDates)-1]
dateString = fmt.Sprintf("%d-%d-%s", currentDay.Year(), currentDay.Month(), dateString)
parsedDate, err := time.Parse("2006-1-2", dateString)
if err != nil {
return time.Time{}, err
}
if parsedDate.Before(currentDay) {
return parsedDate.AddDate(0, 1, 0), nil
}
return parsedDate, nil
}
func collectTransactions(transactions []Transaction, transactionLog *TransactionLog) {
func ingestTransactions(transactions []*Transaction, transactionLog *TransactionLog) {
for _, transaction := range transactions {
if math.Abs(transaction.Amount) != 0 {
addTransaction(transactionLog, &transaction)
transactionLog.addTransaction(transaction)
}
}
}
func sortTransactions(transactions []*Transaction) {
sort.Slice(transactions, func(i, j int) bool {
if transactions[i].Amount < 0 && transactions[j].Amount < 0 {
return math.Abs(transactions[i].Amount) > math.Abs(transactions[j].Amount)
} else {
return transactions[i].Amount > transactions[j].Amount
}
})
}
func (tl *TransactionLog) addTransaction(transaction *Transaction) {
if transaction.Frequency == "" {
tl.addDay(transaction.Date)
tl.LogDays[transaction.Date].Transactions[transaction.ID] = transaction
tl.Transactions[transaction.ID] = transaction
func addTransaction(transactionLog *TransactionLog, transaction *Transaction) {
var dateList []time.Time
if transaction.Frequency != "" {
dateList = calculateFrequency(transaction.Frequency,
transaction.Date, transactionLog.EndDate, transaction.HardDate)
} else {
dateList = []time.Time{transaction.Date}
logrus.Debugf("Adding transaction %s (%s | %f) to %s",
transaction.ID, transaction.Name, transaction.Amount, transaction.Date.String())
return
}
dateList := calculateFrequency(transaction.Frequency,
transaction.Date, tl.EndDate, transaction.Delayable)
for _, date := range dateList {
if _, ok := transactionLog.LogDays[date]; !ok {
logDay := LogDay{
Date: date,
Transactions: make(map[string]*Transaction),
}
transactionLog.LogDays[date] = &logDay
}
tl.addDay(date)
t := Transaction{
Amount: transaction.Amount,
Date: date,
Delayable: transaction.Delayable,
Frequency: "",
HardDate: false,
ID: uuid.NewString(),
Late: false,
Name: transaction.Name,
OrigDate: date,
Shift: transaction.Shift,
Amount: transaction.Amount,
Date: date,
Delayable: transaction.Delayable,
Frequency: "",
ID: uuid.NewString(),
Late: false,
Name: transaction.Name,
OriginDate: date,
Shift: transaction.Shift,
}
transactionLog.LogDays[date].Transactions[t.ID] = &t
transactionLog.Transactions[t.ID] = &t
tl.LogDays[date].Transactions[t.ID] = &t
tl.Transactions[t.ID] = &t
}
}
func (tl *TransactionLog) addDay(date time.Time) {
if _, ok := tl.LogDays[date]; !ok {
logrus.Debugf("Adding Log Day %s", date.String())
tl.LogDays[date] = &LogDay{
Date: stripTime(date),
Transactions: make(map[string]*Transaction),
}
}
}
func (tl *TransactionLog) deleteDay(date time.Time) {
if _, ok := tl.LogDays[date]; ok {
logrus.Debugf("Deleting Log Day %s", date.String())
delete(tl.LogDays, date)
}
}
func calculateShift(transactionLog TransactionLog, transaction Transaction) time.Time {
newDate := transaction.Date
if transaction.Late {
if transaction.Shift == "early" {
newDate = newDate.AddDate(0, 0, -1)
} else {
newDate = newDate.AddDate(0, 0, 1)
}
}
for newDate.Weekday() == 0 || newDate.Weekday() == 6 {
for isWeekend(newDate) {
if transaction.Shift == "early" {
newDate = newDate.AddDate(0, 0, -1)
} else {
@@ -326,74 +347,87 @@ func calculateShift(transactionLog TransactionLog, transaction Transaction) time
return newDate
}
func delayTransaction(transactionLog *TransactionLog, transaction *Transaction) {
if transaction.Date.Weekday() != 0 && transaction.Date.Weekday() != 6 {
transaction.Late = true
}
func (tl *TransactionLog) delayTransaction(transaction *Transaction) {
newDate := calculateShift(*tl, *transaction)
newDate := calculateShift(*transactionLog, *transaction)
if newDate.Before(transactionLog.first()) || newDate.After(transactionLog.last()) || newDate == transaction.Date {
if newDate.Before(tl.first()) || newDate == transaction.Date {
transaction.Delayable = false
return
}
if _, ok := transactionLog.LogDays[newDate]; !ok {
logDay := LogDay{
Date: newDate,
Transactions: map[string]*Transaction{},
}
transactionLog.LogDays[newDate] = &logDay
if newDate.After(tl.last()) {
newDate = tl.last()
}
delete(transactionLog.LogDays[transaction.Date].Transactions, transaction.ID)
tl.addDay(newDate)
delete(tl.LogDays[transaction.Date].Transactions, transaction.ID)
transaction.Date = newDate
transactionLog.LogDays[newDate].Transactions[transaction.ID] = transaction
tl.LogDays[newDate].Transactions[transaction.ID] = transaction
}
func executeTransaction(transactionLog *TransactionLog, transaction *Transaction) bool {
logDay := transactionLog.LogDays[transaction.Date]
func (tl *TransactionLog) executeTransaction(transaction *Transaction) bool {
logDay := tl.LogDays[transaction.Date]
accounts := make([]Account, len(logDay.EndingBalance))
copy(accounts, logDay.EndingBalance)
executed := false
finalDate := transactionLog.EndDate
if transaction.Date.Weekday() == 0 || transaction.Date.Weekday() == 6 && transaction.Delayable {
if isWeekend(transaction.Date) && transaction.Delayable {
logrus.Debugf("Delaying %s due to weekend scheduling\n", transaction.Name)
delayTransaction(transactionLog, transaction)
} else if transaction.Amount > 0 {
logrus.Debugf("Executing Credit: %s %s (%f)\n", logDay.Date, transaction.Name, transaction.Amount)
accounts[0].Balance = accounts[0].Balance + transaction.Amount
executed = true
} else if transaction.Amount < 0 && math.Abs(transaction.Amount) > (accounts[0].Balance-accounts[0].Minimum) && transaction.Date.Before(finalDate) && transaction.Delayable {
logrus.Debugf("Balance: %f | Minimum: %f\n", accounts[0].Balance, accounts[0].Minimum)
logrus.Debugf("Delaying %s: %f (%s) due to lack of funds\n", transaction.Name, transaction.Amount, transaction.Date)
delayTransaction(transactionLog, transaction)
} else if transaction.Amount < 0 {
logrus.Debugf("Executing Charge: %s %s (%f)\n", logDay.Date, transaction.Name, transaction.Amount)
accounts[0].Balance = accounts[0].Balance + transaction.Amount
executed = true
tl.delayTransaction(transaction)
return false
}
if !isAffordable(transaction.Amount, accounts[0].Balance, accounts[0].Minimum) && transaction.Delayable {
logrus.Debugf("Balance: %f | Minimum: %f\n", accounts[0].Balance, accounts[0].Minimum)
logrus.Debugf("Delaying %s: %f (%s) due to lack of funds\n",
transaction.Name, transaction.Amount, transaction.Date)
tl.delayTransaction(transaction)
return false
}
logrus.Debugf("Executing: %s (%s | %s | %f)",
transaction.ID, logDay.Date.String(), transaction.Name, transaction.Amount)
accounts[0].Balance = accounts[0].Balance + transaction.Amount
logDay.EndingBalance = make([]Account, len(accounts))
copy(logDay.EndingBalance, accounts)
return executed
return true
}
func isAffordable(charge, balance, minimum float64) bool {
if charge > 0 {
return true
}
return math.Abs(charge) > (balance - minimum)
}
func currentDay() time.Time {
return stripTime(time.Now())
}
func firstOfMonth(t time.Time) time.Time {
return time.Date(t.Year(), t.Month(), 1, 0, 0, 0, 0, time.UTC)
return time.Date(t.Year(), t.Month(), 1, 0, 0, 0, 0, time.Now().Location())
}
func lastOfMonth(t time.Time) time.Time {
return time.Date(t.Year(), t.Month()+1, 0, 0, 0, 0, 0, time.UTC)
return time.Date(t.Year(), t.Month()+1, 0, 0, 0, 0, 0, time.Now().Location())
}
func stripTime(t time.Time) time.Time {
return time.Date(t.Year(), t.Month(), t.Day(), 0, 0, 0, 0, time.Now().Location())
}
func calculateFrequency(frequency string, startDate time.Time, endDate time.Time, hardDate bool) []time.Time {
base := time.Now()
current := time.Date(base.Year(),
base.Month(), base.Day(), 0, 0, 0, 0, time.UTC)
current := currentDay()
initDate := startDate
dateList := make([]time.Time, 0)
for initDate.Before(endDate.AddDate(0, 0, 1)) {
if !initDate.Before(current) || hardDate {
dateList = append(dateList, initDate)
@@ -411,9 +445,10 @@ func calculateFrequency(frequency string, startDate time.Time, endDate time.Time
initDate = initDate.AddDate(0, 1, 0)
}
} else {
break
logrus.Error("Invalid Frequency")
}
}
return dateList
}
@@ -437,69 +472,75 @@ func categorizeTransactions(transactions map[string]*Transaction) map[string][]s
return categorized
}
func processTransactionLog(transactionLog *TransactionLog, accounts []Account) bool {
func (tl *TransactionLog) processTransactions(accounts []Account) bool {
shifted := false
endBalance := accounts
logrus.Debugf("\n\nITERATION %d\n\n", transactionLog.Iteration)
if transactionLog.Iteration == 0 {
for _, date := range transactionLog.dates() {
logDay := transactionLog.LogDays[date]
logrus.Debugf("\n\nITERATION %d\n\n", tl.Iteration)
if tl.Iteration == 0 {
for _, date := range tl.dates() {
logrus.Debugf("Processing Log Day %s", date.String())
logDay := tl.LogDays[date]
logDay.StartingBalance = make([]Account, len(endBalance))
logDay.EndingBalance = make([]Account, len(endBalance))
copy(logDay.StartingBalance, endBalance)
copy(logDay.EndingBalance, endBalance)
categorized := categorizeTransactions(logDay.Transactions)
logrus.Debugln(categorized)
if processTransactions(transactionLog, categorized["credits"]) {
shifted = true
}
logrus.Debugf("Starting Balance %s: %f\n", date, logDay.StartingBalance[0].Balance)
logrus.Debugf("Ending Balance %s: %f\n", date, logDay.EndingBalance[0].Balance)
processTransactionsByID(tl, categorized["credits"])
logrus.Debugf("Starting Balance %s: %f", date, logDay.StartingBalance[0].Balance)
logrus.Debugf("Ending Balance %s: %f", date, logDay.EndingBalance[0].Balance)
endBalance = logDay.EndingBalance
shifted = true
}
} else {
for _, date := range transactionLog.dates() {
logDay := transactionLog.LogDays[date]
for _, date := range tl.dates() {
logrus.Debugf("Processing Log Day %s", date.String())
logDay := tl.LogDays[date]
logDay.StartingBalance = make([]Account, len(endBalance))
logDay.EndingBalance = make([]Account, len(endBalance))
copy(logDay.StartingBalance, endBalance)
copy(logDay.EndingBalance, endBalance)
categorized := categorizeTransactions(logDay.Transactions)
logrus.Debugln(categorized)
if processTransactions(transactionLog, categorized["credits"]) {
logrus.Debugln(logDay.Transactions)
if processTransactionsByID(tl, categorized["credits"]) {
shifted = true
}
if processTransactions(transactionLog, categorized["late"]) {
if processTransactionsByID(tl, categorized["late"]) {
shifted = true
}
if processTransactions(transactionLog, categorized["standard"]) {
if processTransactionsByID(tl, categorized["standard"]) {
shifted = true
}
logrus.Debugf("Starting Balance %s: %f\n", date, logDay.StartingBalance[0].Balance)
logrus.Debugf("Ending Balance %s: %f\n", date, logDay.EndingBalance[0].Balance)
logrus.Debugf("Starting Balance %s: %f", date, logDay.StartingBalance[0].Balance)
logrus.Debugf("Ending Balance %s: %f", date, logDay.EndingBalance[0].Balance)
endBalance = logDay.EndingBalance
if shifted {
break
}
if len(logDay.Transactions) == 0 {
delete(transactionLog.LogDays, logDay.Date)
tl.deleteDay(date)
}
}
}
transactionLog.Iteration++
tl.Iteration++
return shifted
}
func processTransactions(transactionLog *TransactionLog, transactionIDs []string) bool {
func processTransactionsByID(transactionLog *TransactionLog, transactionIDs []string) bool {
shifted := false
for _, id := range transactionIDs {
transaction, ok := transactionLog.Transactions[id]
logrus.Debugf("Processing %s (%f)", transaction.Name, transaction.Amount)
if !ok {
logrus.Fatalf("Transaction ID: %s not found in log\n", id)
}
if !executeTransaction(transactionLog, transaction) {
logrus.Debugf("Processing %s (%s | %f)", id, transaction.Name, transaction.Amount)
if !transactionLog.executeTransaction(transaction) {
shifted = true
}
}
@@ -508,19 +549,34 @@ func processTransactions(transactionLog *TransactionLog, transactionIDs []string
}
func printOutput(logDay LogDay) {
delim := strings.Repeat("=", 67)
cmd := exec.Command("stty", "size")
cmd.Stdin = os.Stdin
out, err := cmd.Output()
if err != nil {
logrus.Fatal(err)
}
widthStr := string(out[:len(out)-1])
sttyOut := strings.Split(widthStr, " ")
fullWidth, err := strconv.Atoi(sttyOut[1])
fullWidth = fullWidth / 2
width := fullWidth - 2
if err != nil {
logrus.Fatal(err)
}
delim := strings.Repeat("=", fullWidth)
divider := delim
delim2 := strings.Repeat("-", 66)
delim2 := strings.Repeat("-", fullWidth-1)
divider2 := delim2 + "|"
fmt.Println(divider)
outString := fmt.Sprint(logDay.Date.Format("Jan 02 2006"))
rear_spacer := strings.Repeat(" ", 65-len(outString))
rear_spacer := strings.Repeat(" ", width-len(outString))
fmt.Printf("%s%s |\n", outString, rear_spacer)
for _, v := range logDay.StartingBalance {
spacer := strings.Repeat(" ", 19-len(fmt.Sprintf("%.2f", v.Balance)))
outString := fmt.Sprintf("$%.2f%s| %s", v.Balance, spacer, v.Name)
rear_spacer := strings.Repeat(" ", 65-len(outString))
rear_spacer := strings.Repeat(" ", width-len(outString))
fmt.Printf("%s%s |\n", outString, rear_spacer)
}
for _, charge := range logDay.transactions() {
@@ -533,15 +589,15 @@ func printOutput(logDay LogDay) {
}
outString = fmt.Sprintf("%s$%.2f%s| %s", outString, math.Abs(charge.Amount), spacer, charge.Name)
if charge.Late {
late_tag := fmt.Sprintf("(%s - %s)", "Late", charge.OrigDate.Format("Jan 02 2006"))
rear_spacer := strings.Repeat(" ", 65-len(late_tag)-len(outString))
late_tag := fmt.Sprintf("(%s - %s)", "Late", charge.OriginDate.Format("Jan 02 2006"))
rear_spacer := strings.Repeat(" ", width-len(late_tag)-len(outString))
fmt.Printf("%s%s%s |\n", outString, rear_spacer, late_tag)
} else if charge.Date != charge.OrigDate {
weekend_tag := fmt.Sprintf("(%s - %s)", "Weekend", charge.OrigDate.Format("Jan 02 2006"))
rear_spacer := strings.Repeat(" ", 65-len(weekend_tag)-len(outString))
} else if charge.Date != charge.OriginDate {
weekend_tag := fmt.Sprintf("(%s - %s)", "Weekend", charge.OriginDate.Format("Jan 02 2006"))
rear_spacer := strings.Repeat(" ", width-len(weekend_tag)-len(outString))
fmt.Printf("%s%s%s |\n", outString, rear_spacer, weekend_tag)
} else {
rear_spacer := strings.Repeat(" ", 65-len(outString))
rear_spacer := strings.Repeat(" ", width-len(outString))
fmt.Printf("%s%s |\n", outString, rear_spacer)
}
}
@@ -549,7 +605,7 @@ func printOutput(logDay LogDay) {
for _, v := range logDay.EndingBalance {
spacer := strings.Repeat(" ", 19-len(fmt.Sprintf("%.2f", v.Balance)))
outString := fmt.Sprintf("$%.2f%s| %s", v.Balance, spacer, v.Name)
rear_spacer := strings.Repeat(" ", 65-len(outString))
rear_spacer := strings.Repeat(" ", width-len(outString))
fmt.Printf("%s%s |\n", outString, rear_spacer)
}
fmt.Println(divider + "\n")
@@ -561,26 +617,21 @@ func main() {
}
yfile, err := os.ReadFile(os.Args[1])
if err != nil {
log.Fatal(err)
}
var configYaml ConfigYaml
transactionLog := TransactionLog{
LogDays: make(map[time.Time]*LogDay),
Transactions: make(map[string]*Transaction),
}
err = yaml.Unmarshal(yfile, &configYaml)
if err != nil {
log.Fatal(err)
}
config := parseConfig(&configYaml)
preFetchDates(config.Credit, &transactionLog)
preFetchDates(config.Transactions, &transactionLog)
transactionLog := &TransactionLog{
LogDays: make(map[time.Time]*LogDay),
Transactions: make(map[string]*Transaction),
}
if configYaml.Start != "" {
transactionLog.StartDate, err = time.Parse("2006-1-2", configYaml.Start)
@@ -596,13 +647,24 @@ func main() {
}
}
collectTransactions(config.Credit, &transactionLog)
collectTransactions(config.Transactions, &transactionLog)
config := parseConfig(configYaml)
sort.Sort(config.Transactions)
if transactionLog.StartDate == (time.Time{}) {
transactionLog.StartDate = config.Transactions[0].Date
}
if transactionLog.EndDate == (time.Time{}) {
transactionLog.EndDate = config.Transactions[len(config.Transactions)-1].Date
}
ingestTransactions(config.Transactions, transactionLog)
shifted := true
for shifted {
shifted = false
shifted = processTransactionLog(&transactionLog, config.Accounts)
shifted = transactionLog.processTransactions(config.Accounts)
}
for _, date := range transactionLog.dates() {