From a0ebdd01aae4a4a54dfe257cd2f687ef5b13c04d Mon Sep 17 00:00:00 2001 From: Jan Bader Date: Tue, 7 Dec 2021 21:59:06 +0000 Subject: [PATCH] Implement cleaning to set all historic negative balances to zero --- http/admin.go | 49 ++++++++++++++++++++++++++++++++++++ http/budgeting.go | 19 ++++++++++---- http/http.go | 1 + postgres/budgets.sql.go | 24 ++++++++++++++++++ postgres/numeric.go | 2 +- postgres/queries/budgets.sql | 17 ++++++++++++- web/amount.tpl | 4 +-- 7 files changed, 107 insertions(+), 9 deletions(-) diff --git a/http/admin.go b/http/admin.go index d7b4467..c2cf9e8 100644 --- a/http/admin.go +++ b/http/admin.go @@ -3,7 +3,9 @@ package http import ( "fmt" "net/http" + "time" + "git.javil.eu/jacob1123/budgeteer/postgres" "github.com/gin-gonic/gin" "github.com/google/uuid" "github.com/pressly/goose/v3" @@ -56,3 +58,50 @@ func (h *Handler) clearBudget(c *gin.Context) { fmt.Printf("Deleted %d transactions\n", rows) } + +func (h *Handler) cleanNegativeBudget(c *gin.Context) { + budgetID := c.Param("budgetid") + budgetUUID, err := uuid.Parse(budgetID) + if err != nil { + c.Redirect(http.StatusTemporaryRedirect, "/login") + return + } + + min_date, err := h.Service.DB.GetFirstActivity(c.Request.Context(), budgetUUID) + date := getFirstOfMonthTime(min_date) + for { + nextDate := date.AddDate(0, 1, 0) + params := postgres.GetCategoriesWithBalanceParams{ + BudgetID: budgetUUID, + ToDate: nextDate, + FromDate: date, + } + categories, err := h.Service.DB.GetCategoriesWithBalance(c.Request.Context(), params) + if err != nil { + c.AbortWithError(http.StatusInternalServerError, err) + return + } + + for _, category := range categories { + available := category.Available.GetFloat64() + if available >= 0 { + continue + } + var negativeAvailable postgres.Numeric + negativeAvailable.Set(-available) + createAssignment := postgres.CreateAssignmentParams{ + Date: nextDate.AddDate(0, 0, -1), + Amount: negativeAvailable, + CategoryID: category.ID, + } + h.Service.DB.CreateAssignment(c.Request.Context(), createAssignment) + } + + if nextDate.Before(time.Now()) { + date = nextDate + } else { + break + } + } + +} diff --git a/http/budgeting.go b/http/budgeting.go index 458dde4..7e95332 100644 --- a/http/budgeting.go +++ b/http/budgeting.go @@ -19,6 +19,17 @@ type BudgetingData struct { Previous time.Time } +func getFirstOfMonth(year, month int, location *time.Location) time.Time { + return time.Date(year, time.Month(month), 1, 0, 0, 0, 0, location) +} + +func getFirstOfMonthTime(date time.Time) time.Time { + var monthM time.Month + year, monthM, _ := date.Date() + month := int(monthM) + return getFirstOfMonth(year, month, date.Location()) +} + func (h *Handler) budgeting(c *gin.Context) { budgetID := c.Param("budgetid") budgetUUID, err := uuid.Parse(budgetID) @@ -27,7 +38,7 @@ func (h *Handler) budgeting(c *gin.Context) { return } - now := time.Now() + var firstOfMonth time.Time var year, month int yearString := c.Param("year") monthString := c.Param("month") @@ -44,13 +55,11 @@ func (h *Handler) budgeting(c *gin.Context) { return } + firstOfMonth = getFirstOfMonth(year, month, time.Now().Location()) } else { - var monthM time.Month - year, monthM, _ = now.Date() - month = int(monthM) + firstOfMonth = getFirstOfMonthTime(time.Now()) } - firstOfMonth := time.Date(year, time.Month(month), 1, 0, 0, 0, 0, now.Location()) firstOfNextMonth := firstOfMonth.AddDate(0, 1, 0) firstOfPreviousMonth := firstOfMonth.AddDate(0, -1, 0) diff --git a/http/http.go b/http/http.go index 1f414e3..72251be 100644 --- a/http/http.go +++ b/http/http.go @@ -62,6 +62,7 @@ func (h *Handler) Serve() { withBudget.GET("/budget/:budgetid/accounts", h.accounts) withBudget.GET("/budget/:budgetid/account/:accountid", h.account) withBudget.GET("/budget/:budgetid/clear", h.clearBudget) + withBudget.GET("/budget/:budgetid/clean-negative", h.cleanNegativeBudget) api := router.Group("/api/v1") diff --git a/postgres/budgets.sql.go b/postgres/budgets.sql.go index 5dde96c..0e85a95 100644 --- a/postgres/budgets.sql.go +++ b/postgres/budgets.sql.go @@ -5,6 +5,7 @@ package postgres import ( "context" + "time" "github.com/google/uuid" ) @@ -78,3 +79,26 @@ func (q *Queries) GetBudgetsForUser(ctx context.Context, userID uuid.UUID) ([]Bu } return items, nil } + +const getFirstActivity = `-- name: GetFirstActivity :one +SELECT MIN(dates.min_date)::date as min_date +FROM ( + SELECT MIN(assignments.date) as min_date + FROM assignments + INNER JOIN categories ON categories.id = assignments.category_id + INNER JOIN category_groups ON category_groups.id = categories.category_group_id + WHERE category_groups.budget_id = $1 + UNION + SELECT MIN(transactions.date) as min_date + FROM transactions + INNER JOIN accounts ON accounts.id = transactions.account_id + WHERE accounts.budget_id = $1 +) dates +` + +func (q *Queries) GetFirstActivity(ctx context.Context, budgetID uuid.UUID) (time.Time, error) { + row := q.db.QueryRowContext(ctx, getFirstActivity, budgetID) + var min_date time.Time + err := row.Scan(&min_date) + return min_date, err +} diff --git a/postgres/numeric.go b/postgres/numeric.go index 21e24cc..5df7bba 100644 --- a/postgres/numeric.go +++ b/postgres/numeric.go @@ -18,7 +18,7 @@ func (n Numeric) GetFloat64() float64 { return balance } -func (n Numeric) GetPositive() bool { +func (n Numeric) IsPositive() bool { if n.Status != pgtype.Present { return true } diff --git a/postgres/queries/budgets.sql b/postgres/queries/budgets.sql index 2ee4a08..6df5dac 100644 --- a/postgres/queries/budgets.sql +++ b/postgres/queries/budgets.sql @@ -11,4 +11,19 @@ WHERE user_budgets.user_id = $1; -- name: GetBudget :one SELECT * FROM budgets -WHERE id = $1; \ No newline at end of file +WHERE id = $1; + +-- name: GetFirstActivity :one +SELECT MIN(dates.min_date)::date as min_date +FROM ( + SELECT MIN(assignments.date) as min_date + FROM assignments + INNER JOIN categories ON categories.id = assignments.category_id + INNER JOIN category_groups ON category_groups.id = categories.category_group_id + WHERE category_groups.budget_id = @budget_id + UNION + SELECT MIN(transactions.date) as min_date + FROM transactions + INNER JOIN accounts ON accounts.id = transactions.account_id + WHERE accounts.budget_id = @budget_id +) dates; \ No newline at end of file diff --git a/web/amount.tpl b/web/amount.tpl index fd68a36..27f46d6 100644 --- a/web/amount.tpl +++ b/web/amount.tpl @@ -1,11 +1,11 @@ {{define "amount"}} - + {{printf "%.2f" .GetFloat64}} {{end}} {{define "amount-cell"}} - + {{printf "%.2f" .GetFloat64}} {{end}} \ No newline at end of file