From 13993b6b5ac80b84b535d5da5b6256f79025f011 Mon Sep 17 00:00:00 2001 From: Jan Bader Date: Fri, 10 Dec 2021 18:56:56 +0000 Subject: [PATCH] Try to calculate balances locally --- http/budgeting.go | 75 ++++++++++++++++++++--- postgres/cumultative-balances.sql.go | 60 ++++++++++++++++++ postgres/queries/cumultative-balances.sql | 8 +++ 3 files changed, 135 insertions(+), 8 deletions(-) create mode 100644 postgres/cumultative-balances.sql.go create mode 100644 postgres/queries/cumultative-balances.sql diff --git a/http/budgeting.go b/http/budgeting.go index c13b31e..ec614b5 100644 --- a/http/budgeting.go +++ b/http/budgeting.go @@ -1,6 +1,7 @@ package http import ( + "fmt" "net/http" "strconv" "time" @@ -71,24 +72,82 @@ func (h *Handler) budgeting(c *gin.Context) { } categories, err := h.Service.DB.GetCategoriesWithBalance(c.Request.Context(), params) if err != nil { - c.AbortWithError(http.StatusInternalServerError, err) + c.AbortWithError(http.StatusInternalServerError, fmt.Errorf("load categories: %w", err)) return } - availableParams := postgres.GetAvailableBalanceParams{ - BudgetID: budgetUUID, - FromDate: firstOfNextMonth, - } - availableBalance, err := h.Service.DB.GetAvailableBalance(c.Request.Context(), availableParams) + cumultativeBalances, err := h.Service.DB.GetCumultativeBalances(c.Request.Context(), budgetUUID) if err != nil { - c.AbortWithError(http.StatusInternalServerError, err) + c.AbortWithError(http.StatusInternalServerError, fmt.Errorf("load balances: %w", err)) return } + var available float64 = 0 + var added float64 = 0 + var assigned float64 = 0 + for i := range categories { + cat := &categories[i] + var balance float64 = 0 + for _, bal := range cumultativeBalances { + if bal.CategoryID != cat.ID { + continue + } + + // skip everything in the future + if !bal.Date.Before(firstOfNextMonth) { + continue + } + + assigned += bal.Assignments.GetFloat64() + balance += bal.Assignments.GetFloat64() + balance += bal.Transactions.GetFloat64() + if balance < 0 && bal.Date.Before(firstOfMonth) { + added -= balance + balance = 0 + } + + if bal.Date.Before(firstOfMonth) { + available = balance + } + } + + num := postgres.Numeric{} + num.Set(balance) + cat.Available = num + + num2 := postgres.Numeric{} + num2.Set(available) + cat.AvailableLastMonth = num2 + } + + data := c.MustGet("data").(AlwaysNeededData) + var availableBalance float64 = 0 + for _, cat := range categories { + if cat.ID != data.Budget.IncomeCategoryID { + continue + } + availableBalance = -assigned - added + + for _, bal := range cumultativeBalances { + if bal.CategoryID != cat.ID { + continue + } + + if !bal.Date.Before(firstOfNextMonth) { + continue + } + + availableBalance += bal.Transactions.GetFloat64() + } + } + + var availableBalanceNum postgres.Numeric + availableBalanceNum.Set(availableBalance) + d := BudgetingData{ c.MustGet("data").(AlwaysNeededData), categories, - availableBalance, + availableBalanceNum, firstOfMonth, firstOfNextMonth, firstOfPreviousMonth, diff --git a/postgres/cumultative-balances.sql.go b/postgres/cumultative-balances.sql.go new file mode 100644 index 0000000..bad7a53 --- /dev/null +++ b/postgres/cumultative-balances.sql.go @@ -0,0 +1,60 @@ +// Code generated by sqlc. DO NOT EDIT. +// source: cumultative-balances.sql + +package postgres + +import ( + "context" + "time" + + "github.com/google/uuid" +) + +const getCumultativeBalances = `-- name: GetCumultativeBalances :many +SELECT COALESCE(ass.date, tra.date), COALESCE(ass.category_id, tra.category_id), + COALESCE(ass.amount, 0)::decimal(12,2) as assignments, SUM(ass.amount) OVER (PARTITION BY ass.category_id ORDER BY ass.date)::decimal(12,2) as assignments_cum, + COALESCE(tra.amount, 0)::decimal(12,2) as transactions, SUM(tra.amount) OVER (PARTITION BY tra.category_id ORDER BY tra.date)::decimal(12,2) as transactions_cum +FROM assignments_by_month as ass +FULL OUTER JOIN transactions_by_month as tra ON ass.date = tra.date AND ass.category_id = tra.category_id +WHERE (ass.budget_id IS NULL OR ass.budget_id = $1) AND (tra.budget_id IS NULL OR tra.budget_id = $1) +ORDER BY COALESCE(ass.date, tra.date), COALESCE(ass.category_id, tra.category_id) +` + +type GetCumultativeBalancesRow struct { + Date time.Time + CategoryID uuid.UUID + Assignments Numeric + AssignmentsCum Numeric + Transactions Numeric + TransactionsCum Numeric +} + +func (q *Queries) GetCumultativeBalances(ctx context.Context, budgetID uuid.UUID) ([]GetCumultativeBalancesRow, error) { + rows, err := q.db.QueryContext(ctx, getCumultativeBalances, budgetID) + if err != nil { + return nil, err + } + defer rows.Close() + var items []GetCumultativeBalancesRow + for rows.Next() { + var i GetCumultativeBalancesRow + if err := rows.Scan( + &i.Date, + &i.CategoryID, + &i.Assignments, + &i.AssignmentsCum, + &i.Transactions, + &i.TransactionsCum, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} diff --git a/postgres/queries/cumultative-balances.sql b/postgres/queries/cumultative-balances.sql new file mode 100644 index 0000000..5ba6f72 --- /dev/null +++ b/postgres/queries/cumultative-balances.sql @@ -0,0 +1,8 @@ +-- name: GetCumultativeBalances :many +SELECT COALESCE(ass.date, tra.date), COALESCE(ass.category_id, tra.category_id), + COALESCE(ass.amount, 0)::decimal(12,2) as assignments, SUM(ass.amount) OVER (PARTITION BY ass.category_id ORDER BY ass.date)::decimal(12,2) as assignments_cum, + COALESCE(tra.amount, 0)::decimal(12,2) as transactions, SUM(tra.amount) OVER (PARTITION BY tra.category_id ORDER BY tra.date)::decimal(12,2) as transactions_cum +FROM assignments_by_month as ass +FULL OUTER JOIN transactions_by_month as tra ON ass.date = tra.date AND ass.category_id = tra.category_id +WHERE (ass.budget_id IS NULL OR ass.budget_id = @budget_id) AND (tra.budget_id IS NULL OR tra.budget_id = @budget_id) +ORDER BY COALESCE(ass.date, tra.date), COALESCE(ass.category_id, tra.category_id); \ No newline at end of file