From 3cb39d978a17e9d2e19d5ea8bbddd8ffe4c2b371 Mon Sep 17 00:00:00 2001 From: Jan Bader Date: Sun, 20 Feb 2022 22:33:29 +0000 Subject: [PATCH] Fix multiple linter errors --- postgres/numeric.go | 2 +- postgres/ynab-import.go | 136 +++++++++++++++++++++++----------------- server/budgeting.go | 93 +++++++++++++++------------ 3 files changed, 132 insertions(+), 99 deletions(-) diff --git a/postgres/numeric.go b/postgres/numeric.go index bcec717..b4e5631 100644 --- a/postgres/numeric.go +++ b/postgres/numeric.go @@ -45,7 +45,7 @@ func (n Numeric) IsZero() bool { func (n Numeric) MatchExp(exp int32) Numeric { diffExp := n.Exp - exp - factor := big.NewInt(0).Exp(big.NewInt(10), big.NewInt(int64(diffExp)), nil) + factor := big.NewInt(0).Exp(big.NewInt(10), big.NewInt(int64(diffExp)), nil) //nolint:gomnd return Numeric{pgtype.Numeric{ Exp: exp, Int: big.NewInt(0).Mul(n.Int, factor), diff --git a/postgres/ynab-import.go b/postgres/ynab-import.go index 4879026..8aa512b 100644 --- a/postgres/ynab-import.go +++ b/postgres/ynab-import.go @@ -116,8 +116,7 @@ type Transfer struct { ToAccount string } -// ImportTransactions expects a TSV-file as exported by YNAB in the following format: - +// ImportTransactions expects a TSV-file as exported by YNAB. func (ynab *YNABImport) ImportTransactions(context context.Context, r io.Reader) error { csv := csv.NewReader(r) csv.Comma = '\t' @@ -132,60 +131,18 @@ func (ynab *YNABImport) ImportTransactions(context context.Context, r io.Reader) count := 0 for _, record := range csvData[1:] { - accountName := record[0] - account, err := ynab.GetAccount(context, accountName) + transaction, err := ynab.GetTransaction(context, record) if err != nil { - return fmt.Errorf("get account %s: %w", accountName, err) - } - - // flag := record[1] - - dateString := record[2] - date, err := time.Parse("02.01.2006", dateString) - if err != nil { - return fmt.Errorf("parse date %s: %w", dateString, err) - } - - categoryGroup, categoryName := record[5], record[6] // also in 4 joined by : - category, err := ynab.GetCategory(context, categoryGroup, categoryName) - if err != nil { - return fmt.Errorf("get category %s/%s: %w", categoryGroup, categoryName, err) - } - - memo := record[7] - - outflow := record[8] - inflow := record[9] - amount, err := GetAmount(inflow, outflow) - if err != nil { - return fmt.Errorf("parse amount from (%s/%s): %w", inflow, outflow, err) - } - - statusEnum := TransactionStatusUncleared - status := record[10] - switch status { - case "Cleared": - statusEnum = TransactionStatusCleared - case "Reconciled": - statusEnum = TransactionStatusReconciled - case "Uncleared": - } - - transaction := CreateTransactionParams{ - Date: date, - Memo: memo, - AccountID: account.ID, - CategoryID: category, - Amount: amount, - Status: statusEnum, + return err } payeeName := record[3] // Transaction is a transfer if strings.HasPrefix(payeeName, "Transfer : ") { - err = ynab.ImportTransferTransaction(payeeName, context, transaction, accountName, &openTransfers, account, amount) + err = ynab.ImportTransferTransaction(context, payeeName, transaction.CreateTransactionParams, + &openTransfers, transaction.Account, transaction.Amount) } else { - err = ynab.ImportRegularTransaction(context, payeeName, transaction) + err = ynab.ImportRegularTransaction(context, payeeName, transaction.CreateTransactionParams) } if err != nil { return err @@ -208,7 +165,66 @@ func (ynab *YNABImport) ImportTransactions(context context.Context, r io.Reader) return nil } -func (ynab *YNABImport) ImportRegularTransaction(context context.Context, payeeName string, transaction CreateTransactionParams) error { +type NewTransaction struct { + CreateTransactionParams + Account *Account +} + +func (ynab *YNABImport) GetTransaction(context context.Context, record []string) (NewTransaction, error) { + accountName := record[0] + account, err := ynab.GetAccount(context, accountName) + if err != nil { + return NewTransaction{}, fmt.Errorf("get account %s: %w", accountName, err) + } + + // flag := record[1] + + dateString := record[2] + date, err := time.Parse("02.01.2006", dateString) + if err != nil { + return NewTransaction{}, fmt.Errorf("parse date %s: %w", dateString, err) + } + + categoryGroup, categoryName := record[5], record[6] // also in 4 joined by : + category, err := ynab.GetCategory(context, categoryGroup, categoryName) + if err != nil { + return NewTransaction{}, fmt.Errorf("get category %s/%s: %w", categoryGroup, categoryName, err) + } + + memo := record[7] + + outflow := record[8] + inflow := record[9] + amount, err := GetAmount(inflow, outflow) + if err != nil { + return NewTransaction{}, fmt.Errorf("parse amount from (%s/%s): %w", inflow, outflow, err) + } + + statusEnum := TransactionStatusUncleared + status := record[10] + switch status { + case "Cleared": + statusEnum = TransactionStatusCleared + case "Reconciled": + statusEnum = TransactionStatusReconciled + case "Uncleared": + } + + return NewTransaction{ + CreateTransactionParams: CreateTransactionParams{ + Date: date, + Memo: memo, + AccountID: account.ID, + CategoryID: category, + Amount: amount, + Status: statusEnum, + }, + Account: account, + }, nil +} + +func (ynab *YNABImport) ImportRegularTransaction(context context.Context, payeeName string, + transaction CreateTransactionParams) error { payeeID, err := ynab.GetPayee(context, payeeName) if err != nil { return fmt.Errorf("get payee %s: %w", payeeName, err) @@ -222,7 +238,9 @@ func (ynab *YNABImport) ImportRegularTransaction(context context.Context, payeeN return nil } -func (ynab *YNABImport) ImportTransferTransaction(payeeName string, context context.Context, transaction CreateTransactionParams, accountName string, openTransfers *[]Transfer, account *Account, amount Numeric) error { +func (ynab *YNABImport) ImportTransferTransaction(context context.Context, payeeName string, + transaction CreateTransactionParams, openTransfers *[]Transfer, + account *Account, amount Numeric) error { transferToAccountName := payeeName[11:] transferToAccount, err := ynab.GetAccount(context, transferToAccountName) if err != nil { @@ -232,7 +250,7 @@ func (ynab *YNABImport) ImportTransferTransaction(payeeName string, context cont transfer := Transfer{ transaction, transferToAccount, - accountName, + account.Name, transferToAccountName, } @@ -342,7 +360,7 @@ func (ynab *YNABImport) GetPayee(context context.Context, name string) (uuid.Nul return uuid.NullUUID{UUID: payee.ID, Valid: true}, nil } -func (ynab *YNABImport) GetCategory(context context.Context, group string, name string) (uuid.NullUUID, error) { +func (ynab *YNABImport) GetCategory(context context.Context, group string, name string) (uuid.NullUUID, error) { //nolint if group == "" || name == "" { return uuid.NullUUID{}, nil } @@ -353,21 +371,21 @@ func (ynab *YNABImport) GetCategory(context context.Context, group string, name } } - var categoryGroup *CategoryGroup + var categoryGroup CategoryGroup for _, existingGroup := range ynab.categoryGroups { if existingGroup.Name == group { - categoryGroup = &existingGroup + categoryGroup = existingGroup } } - if categoryGroup == nil { + if categoryGroup.Name == "" { newGroup := CreateCategoryGroupParams{Name: group, BudgetID: ynab.budgetID} - newCategoryGroup, err := ynab.queries.CreateCategoryGroup(context, newGroup) + var err error + categoryGroup, err = ynab.queries.CreateCategoryGroup(context, newGroup) if err != nil { return uuid.NullUUID{}, err } - ynab.categoryGroups = append(ynab.categoryGroups, newCategoryGroup) - categoryGroup = &newCategoryGroup + ynab.categoryGroups = append(ynab.categoryGroups, categoryGroup) } newCategory := CreateCategoryParams{Name: name, CategoryGroupID: categoryGroup.ID} diff --git a/server/budgeting.go b/server/budgeting.go index 6addb2e..da7204a 100644 --- a/server/budgeting.go +++ b/server/budgeting.go @@ -91,6 +91,18 @@ func (h *Handler) budgetingForMonth(c *gin.Context) { return } + availableBalance := h.getAvailableBalance(categories, budget, moneyUsed, cumultativeBalances, firstOfNextMonth) + + data := struct { + Categories []CategoryWithBalance + AvailableBalance postgres.Numeric + }{categoriesWithBalance, availableBalance} + c.JSON(http.StatusOK, data) +} + +func (*Handler) getAvailableBalance(categories []postgres.GetCategoriesRow, budget postgres.Budget, + moneyUsed postgres.Numeric, cumultativeBalances []postgres.GetCumultativeBalancesRow, + firstOfNextMonth time.Time) postgres.Numeric { availableBalance := postgres.NewZeroNumeric() for _, cat := range categories { if cat.ID != budget.IncomeCategoryID { @@ -110,12 +122,7 @@ func (h *Handler) budgetingForMonth(c *gin.Context) { availableBalance = availableBalance.Add(bal.Transactions) } } - - data := struct { - Categories []CategoryWithBalance - AvailableBalance postgres.Numeric - }{categoriesWithBalance, availableBalance} - c.JSON(http.StatusOK, data) + return availableBalance } func (h *Handler) budgeting(c *gin.Context) { @@ -146,7 +153,9 @@ func (h *Handler) budgeting(c *gin.Context) { c.JSON(http.StatusOK, data) } -func (h *Handler) calculateBalances(c *gin.Context, budget postgres.Budget, firstOfNextMonth time.Time, firstOfMonth time.Time, categories []postgres.GetCategoriesRow, cumultativeBalances []postgres.GetCumultativeBalancesRow) ([]CategoryWithBalance, postgres.Numeric, error) { +func (h *Handler) calculateBalances(c *gin.Context, budget postgres.Budget, + firstOfNextMonth time.Time, firstOfMonth time.Time, categories []postgres.GetCategoriesRow, + cumultativeBalances []postgres.GetCumultativeBalancesRow) ([]CategoryWithBalance, postgres.Numeric, error) { categoriesWithBalance := []CategoryWithBalance{} hiddenCategory := CategoryWithBalance{ GetCategoriesRow: &postgres.GetCategoriesRow{ @@ -162,39 +171,9 @@ func (h *Handler) calculateBalances(c *gin.Context, budget postgres.Budget, firs moneyUsed := postgres.NewZeroNumeric() for i := range categories { cat := &categories[i] - categoryWithBalance := CategoryWithBalance{ - GetCategoriesRow: cat, - Available: postgres.NewZeroNumeric(), - AvailableLastMonth: postgres.NewZeroNumeric(), - Activity: postgres.NewZeroNumeric(), - Assigned: postgres.NewZeroNumeric(), - } - for _, bal := range cumultativeBalances { - if bal.CategoryID != cat.ID { - continue - } - - if !bal.Date.Before(firstOfNextMonth) { - continue - } - - moneyUsed = moneyUsed.Sub(bal.Assignments) - categoryWithBalance.Available = categoryWithBalance.Available.Add(bal.Assignments) - categoryWithBalance.Available = categoryWithBalance.Available.Add(bal.Transactions) - if !categoryWithBalance.Available.IsPositive() && bal.Date.Before(firstOfMonth) { - moneyUsed = moneyUsed.Add(categoryWithBalance.Available) - categoryWithBalance.Available = postgres.NewZeroNumeric() - } - - if bal.Date.Before(firstOfMonth) { - categoryWithBalance.AvailableLastMonth = categoryWithBalance.Available - } else if bal.Date.Before(firstOfNextMonth) { - categoryWithBalance.Activity = bal.Transactions - categoryWithBalance.Assigned = bal.Assignments - } - } - // do not show hidden categories + categoryWithBalance := h.CalculateCategoryBalances(cat, cumultativeBalances, + firstOfNextMonth, &moneyUsed, firstOfMonth, hiddenCategory, budget) if cat.Group == "Hidden Categories" { hiddenCategory.Available = hiddenCategory.Available.Add(categoryWithBalance.Available) hiddenCategory.AvailableLastMonth = hiddenCategory.AvailableLastMonth.Add(categoryWithBalance.AvailableLastMonth) @@ -214,3 +193,39 @@ func (h *Handler) calculateBalances(c *gin.Context, budget postgres.Budget, firs return categoriesWithBalance, moneyUsed, nil } + +func (*Handler) CalculateCategoryBalances(cat *postgres.GetCategoriesRow, cumultativeBalances []postgres.GetCumultativeBalancesRow, firstOfNextMonth time.Time, moneyUsed *postgres.Numeric, firstOfMonth time.Time, hiddenCategory CategoryWithBalance, budget postgres.Budget) CategoryWithBalance { + categoryWithBalance := CategoryWithBalance{ + GetCategoriesRow: cat, + Available: postgres.NewZeroNumeric(), + AvailableLastMonth: postgres.NewZeroNumeric(), + Activity: postgres.NewZeroNumeric(), + Assigned: postgres.NewZeroNumeric(), + } + for _, bal := range cumultativeBalances { + if bal.CategoryID != cat.ID { + continue + } + + if !bal.Date.Before(firstOfNextMonth) { + continue + } + + *moneyUsed = moneyUsed.Sub(bal.Assignments) + categoryWithBalance.Available = categoryWithBalance.Available.Add(bal.Assignments) + categoryWithBalance.Available = categoryWithBalance.Available.Add(bal.Transactions) + if !categoryWithBalance.Available.IsPositive() && bal.Date.Before(firstOfMonth) { + *moneyUsed = moneyUsed.Add(categoryWithBalance.Available) + categoryWithBalance.Available = postgres.NewZeroNumeric() + } + + if bal.Date.Before(firstOfMonth) { + categoryWithBalance.AvailableLastMonth = categoryWithBalance.Available + } else if bal.Date.Before(firstOfNextMonth) { + categoryWithBalance.Activity = bal.Transactions + categoryWithBalance.Assigned = bal.Assignments + } + } + + return categoryWithBalance +}