Implement cleaning to set all historic negative balances to zero

This commit is contained in:
Jan Bader 2021-12-07 21:59:06 +00:00
parent edd1319222
commit a0ebdd01aa
7 changed files with 107 additions and 9 deletions

View File

@ -3,7 +3,9 @@ package http
import ( import (
"fmt" "fmt"
"net/http" "net/http"
"time"
"git.javil.eu/jacob1123/budgeteer/postgres"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/google/uuid" "github.com/google/uuid"
"github.com/pressly/goose/v3" "github.com/pressly/goose/v3"
@ -56,3 +58,50 @@ func (h *Handler) clearBudget(c *gin.Context) {
fmt.Printf("Deleted %d transactions\n", rows) 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
}
}
}

View File

@ -19,6 +19,17 @@ type BudgetingData struct {
Previous time.Time 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) { func (h *Handler) budgeting(c *gin.Context) {
budgetID := c.Param("budgetid") budgetID := c.Param("budgetid")
budgetUUID, err := uuid.Parse(budgetID) budgetUUID, err := uuid.Parse(budgetID)
@ -27,7 +38,7 @@ func (h *Handler) budgeting(c *gin.Context) {
return return
} }
now := time.Now() var firstOfMonth time.Time
var year, month int var year, month int
yearString := c.Param("year") yearString := c.Param("year")
monthString := c.Param("month") monthString := c.Param("month")
@ -44,13 +55,11 @@ func (h *Handler) budgeting(c *gin.Context) {
return return
} }
firstOfMonth = getFirstOfMonth(year, month, time.Now().Location())
} else { } else {
var monthM time.Month firstOfMonth = getFirstOfMonthTime(time.Now())
year, monthM, _ = now.Date()
month = int(monthM)
} }
firstOfMonth := time.Date(year, time.Month(month), 1, 0, 0, 0, 0, now.Location())
firstOfNextMonth := firstOfMonth.AddDate(0, 1, 0) firstOfNextMonth := firstOfMonth.AddDate(0, 1, 0)
firstOfPreviousMonth := firstOfMonth.AddDate(0, -1, 0) firstOfPreviousMonth := firstOfMonth.AddDate(0, -1, 0)

View File

@ -62,6 +62,7 @@ func (h *Handler) Serve() {
withBudget.GET("/budget/:budgetid/accounts", h.accounts) withBudget.GET("/budget/:budgetid/accounts", h.accounts)
withBudget.GET("/budget/:budgetid/account/:accountid", h.account) withBudget.GET("/budget/:budgetid/account/:accountid", h.account)
withBudget.GET("/budget/:budgetid/clear", h.clearBudget) withBudget.GET("/budget/:budgetid/clear", h.clearBudget)
withBudget.GET("/budget/:budgetid/clean-negative", h.cleanNegativeBudget)
api := router.Group("/api/v1") api := router.Group("/api/v1")

View File

@ -5,6 +5,7 @@ package postgres
import ( import (
"context" "context"
"time"
"github.com/google/uuid" "github.com/google/uuid"
) )
@ -78,3 +79,26 @@ func (q *Queries) GetBudgetsForUser(ctx context.Context, userID uuid.UUID) ([]Bu
} }
return items, nil 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
}

View File

@ -18,7 +18,7 @@ func (n Numeric) GetFloat64() float64 {
return balance return balance
} }
func (n Numeric) GetPositive() bool { func (n Numeric) IsPositive() bool {
if n.Status != pgtype.Present { if n.Status != pgtype.Present {
return true return true
} }

View File

@ -11,4 +11,19 @@ WHERE user_budgets.user_id = $1;
-- name: GetBudget :one -- name: GetBudget :one
SELECT * FROM budgets SELECT * FROM budgets
WHERE id = $1; 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;

View File

@ -1,11 +1,11 @@
{{define "amount"}} {{define "amount"}}
<span class="right {{if .IsZero}}zero{{else if not .GetPositive}}negative{{end}}"> <span class="right {{if .IsZero}}zero{{else if not .IsPositive}}negative{{end}}">
{{printf "%.2f" .GetFloat64}} {{printf "%.2f" .GetFloat64}}
</span> </span>
{{end}} {{end}}
{{define "amount-cell"}} {{define "amount-cell"}}
<td class="right {{if .IsZero}}zero{{else if not .GetPositive}}negative{{end}}"> <td class="right {{if .IsZero}}zero{{else if not .IsPositive}}negative{{end}}">
{{printf "%.2f" .GetFloat64}} {{printf "%.2f" .GetFloat64}}
</td> </td>
{{end}} {{end}}