Logic fixes and general cleanup
This commit is contained in:
504
main.go
504
main.go
@@ -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 {
|
||||
@@ -53,11 +71,10 @@ type Transaction struct {
|
||||
Date time.Time
|
||||
Delayable bool
|
||||
Frequency string
|
||||
HardDate bool
|
||||
ID string
|
||||
Late bool
|
||||
Name string
|
||||
OrigDate time.Time
|
||||
OriginDate time.Time
|
||||
Shift string
|
||||
}
|
||||
|
||||
@@ -65,7 +82,7 @@ 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)
|
||||
return time.Time{}, err
|
||||
}
|
||||
} else if date == "last" {
|
||||
formalDate = lastOfMonth(current)
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
if dateString == "last" {
|
||||
return lastOfMonth(currentDay), nil
|
||||
}
|
||||
return formalDate, hardDate
|
||||
|
||||
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 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
|
||||
}
|
||||
}
|
||||
logDates := transactionLog.dates()
|
||||
transactionLog.StartDate = logDates[0]
|
||||
transactionLog.EndDate = logDates[len(logDates)-1]
|
||||
}
|
||||
|
||||
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,
|
||||
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{},
|
||||
if newDate.After(tl.last()) {
|
||||
newDate = tl.last()
|
||||
}
|
||||
|
||||
transactionLog.LogDays[newDate] = &logDay
|
||||
}
|
||||
tl.addDay(newDate)
|
||||
|
||||
delete(tl.LogDays[transaction.Date].Transactions, transaction.ID)
|
||||
|
||||
delete(transactionLog.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() {
|
||||
|
||||
Reference in New Issue
Block a user