From 53c51ceb8df0b972e67c005d452e620ffaae2e90 Mon Sep 17 00:00:00 2001 From: Jan Bader Date: Tue, 7 Dec 2021 13:32:29 +0000 Subject: [PATCH] Add budgeting --- http/budgeting.go | 70 +++++++++++++++++++++++++++++++++ http/http.go | 1 + postgres/categories.sql.go | 20 ++++++++-- postgres/queries/categories.sql | 20 ++++++++-- web/budgeting.html | 37 +++++++++++++++++ 5 files changed, 142 insertions(+), 6 deletions(-) create mode 100644 http/budgeting.go create mode 100644 web/budgeting.html diff --git a/http/budgeting.go b/http/budgeting.go new file mode 100644 index 0000000..2715db2 --- /dev/null +++ b/http/budgeting.go @@ -0,0 +1,70 @@ +package http + +import ( + "context" + "net/http" + "strconv" + "time" + + "git.javil.eu/jacob1123/budgeteer/postgres" + "github.com/gin-gonic/gin" + "github.com/google/uuid" +) + +type BudgetingData struct { + AlwaysNeededData + Categories []postgres.GetCategoriesWithBalanceRow +} + +func (h *Handler) budgeting(c *gin.Context) { + budgetID := c.Param("budgetid") + budgetUUID, err := uuid.Parse(budgetID) + if err != nil { + c.Redirect(http.StatusTemporaryRedirect, "/login") + return + } + + now := time.Now() + var year, month int + yearString := c.Param("year") + monthString := c.Param("month") + if yearString != "" && monthString != "" { + year, err = strconv.Atoi(yearString) + if err != nil { + c.Redirect(http.StatusTemporaryRedirect, "/budget/"+budgetUUID.String()) + return + } + + month, err = strconv.Atoi(monthString) + if err != nil { + c.Redirect(http.StatusTemporaryRedirect, "/budget/"+budgetUUID.String()) + return + } + + } else { + var monthM time.Month + year, monthM, _ = now.Date() + month = int(monthM) + } + + firstOfMonth := time.Date(year, time.Month(month), 1, 0, 0, 0, 0, now.Location()) + lastOfMonth := firstOfMonth.AddDate(0, 1, -1) + + params := postgres.GetCategoriesWithBalanceParams{ + BudgetID: budgetUUID, + FromDate: firstOfMonth, + ToDate: lastOfMonth, + } + categories, err := h.Service.DB.GetCategoriesWithBalance(context.Background(), params) + if err != nil { + c.AbortWithError(http.StatusInternalServerError, err) + return + } + + d := BudgetingData{ + c.MustGet("data").(AlwaysNeededData), + categories, + } + + c.HTML(http.StatusOK, "budgeting.html", d) +} diff --git a/http/http.go b/http/http.go index b71f69b..6c2ebbc 100644 --- a/http/http.go +++ b/http/http.go @@ -57,6 +57,7 @@ func (h *Handler) Serve() { withBudget.Use(h.verifyLoginWithRedirect) withBudget.Use(h.getImportantData) withBudget.GET("/budget/:budgetid", h.budgeting) + withBudget.GET("/budget/:budgetid/:year/:month", h.budgeting) withBudget.GET("/budget/:budgetid/all-accounts", h.budget) withBudget.GET("/budget/:budgetid/accounts", h.accounts) withBudget.GET("/budget/:budgetid/account/:accountid", h.account) diff --git a/postgres/categories.sql.go b/postgres/categories.sql.go index c6642c7..395ac2d 100644 --- a/postgres/categories.sql.go +++ b/postgres/categories.sql.go @@ -90,11 +90,25 @@ func (q *Queries) GetCategories(ctx context.Context, budgetID uuid.UUID) ([]GetC } const getCategoriesWithBalance = `-- name: GetCategoriesWithBalance :many -SELECT categories.id, categories.name, category_groups.name as group, SUM(t_hist.amount)::decimal(12,2) as balance, SUM(t_this.amount)::decimal(12,2) as activity +SELECT categories.id, categories.name, category_groups.name as group, + COALESCE( + ( + SELECT SUM(t_hist.amount) + FROM transactions t_hist + WHERE categories.id = t_hist.category_id + AND t_hist.date < $1 + ) + , 0)::decimal(12,2) as balance, + COALESCE( + ( + SELECT SUM(t_this.amount) + FROM transactions t_this + WHERE categories.id = t_this.category_id + AND t_this.date BETWEEN $1 AND $2 + ) + , 0)::decimal(12,2) as activity FROM categories INNER JOIN category_groups ON categories.category_group_id = category_groups.id -INNER JOIN transactions t_hist ON categories.id = t_hist.category_id AND t_hist.date < $1 -INNER JOIN transactions t_this ON categories.id = t_this.category_id AND t_this.date >= $1 AND t_this.date < $2 WHERE category_groups.budget_id = $3 GROUP BY categories.id, categories.name, category_groups.name ` diff --git a/postgres/queries/categories.sql b/postgres/queries/categories.sql index f9e5511..9d03541 100644 --- a/postgres/queries/categories.sql +++ b/postgres/queries/categories.sql @@ -20,10 +20,24 @@ INNER JOIN category_groups ON categories.category_group_id = category_groups.id WHERE category_groups.budget_id = $1; -- name: GetCategoriesWithBalance :many -SELECT categories.id, categories.name, category_groups.name as group, SUM(t_hist.amount)::decimal(12,2) as balance, SUM(t_this.amount)::decimal(12,2) as activity +SELECT categories.id, categories.name, category_groups.name as group, + COALESCE( + ( + SELECT SUM(t_hist.amount) + FROM transactions t_hist + WHERE categories.id = t_hist.category_id + AND t_hist.date < @from_date + ) + , 0)::decimal(12,2) as balance, + COALESCE( + ( + SELECT SUM(t_this.amount) + FROM transactions t_this + WHERE categories.id = t_this.category_id + AND t_this.date BETWEEN @from_date AND @to_date + ) + , 0)::decimal(12,2) as activity FROM categories INNER JOIN category_groups ON categories.category_group_id = category_groups.id -INNER JOIN transactions t_hist ON categories.id = t_hist.category_id AND t_hist.date < @from_date -INNER JOIN transactions t_this ON categories.id = t_this.category_id AND t_this.date >= @from_date AND t_this.date < @to_date WHERE category_groups.budget_id = @budget_id GROUP BY categories.id, categories.name, category_groups.name; \ No newline at end of file diff --git a/web/budgeting.html b/web/budgeting.html new file mode 100644 index 0000000..5b26e3f --- /dev/null +++ b/web/budgeting.html @@ -0,0 +1,37 @@ + +{{template "base" .}} + +{{define "title"}}Budget{{end}} + +{{define "new"}} + {{template "transaction-new"}} +{{end}} + +{{define "main"}} +
+ New Transaction + +
+
+ Next Month + Previous Month +
+ + {{range .Categories}} + + + + + + + + + {{end}} +
{{.Group}}{{.Name}} + + + {{printf "%.2f" .Balance.GetFloat64}} + + {{printf "%.2f" .Activity.GetFloat64}} +
+{{end}} \ No newline at end of file