Use Numeric in JSON output
This commit is contained in:
parent
5763409aa8
commit
487aa89f18
54
http/autocomplete.go
Normal file
54
http/autocomplete.go
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
package http
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"git.javil.eu/jacob1123/budgeteer/postgres"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/google/uuid"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (h *Handler) autocompleteCategories(c *gin.Context) {
|
||||||
|
budgetID := c.Param("budgetid")
|
||||||
|
budgetUUID, err := uuid.Parse(budgetID)
|
||||||
|
if err != nil {
|
||||||
|
c.AbortWithError(http.StatusBadRequest, fmt.Errorf("budgetid missing from URL"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
query := c.Request.URL.Query().Get("s")
|
||||||
|
searchParams := postgres.SearchCategoriesParams{
|
||||||
|
BudgetID: budgetUUID,
|
||||||
|
Search: "%" + query + "%",
|
||||||
|
}
|
||||||
|
categories, err := h.Service.SearchCategories(c.Request.Context(), searchParams)
|
||||||
|
if err != nil {
|
||||||
|
c.AbortWithError(http.StatusInternalServerError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.JSON(http.StatusOK, categories)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Handler) autocompletePayee(c *gin.Context) {
|
||||||
|
budgetID := c.Param("budgetid")
|
||||||
|
budgetUUID, err := uuid.Parse(budgetID)
|
||||||
|
if err != nil {
|
||||||
|
c.AbortWithError(http.StatusBadRequest, fmt.Errorf("budgetid missing from URL"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
query := c.Request.URL.Query().Get("s")
|
||||||
|
searchParams := postgres.SearchPayeesParams{
|
||||||
|
BudgetID: budgetUUID,
|
||||||
|
Search: query + "%",
|
||||||
|
}
|
||||||
|
payees, err := h.Service.SearchPayees(c.Request.Context(), searchParams)
|
||||||
|
if err != nil {
|
||||||
|
c.AbortWithError(http.StatusInternalServerError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.JSON(http.StatusOK, payees)
|
||||||
|
}
|
@ -24,10 +24,10 @@ func getFirstOfMonthTime(date time.Time) time.Time {
|
|||||||
|
|
||||||
type CategoryWithBalance struct {
|
type CategoryWithBalance struct {
|
||||||
*postgres.GetCategoriesRow
|
*postgres.GetCategoriesRow
|
||||||
Available float64
|
Available postgres.Numeric
|
||||||
AvailableLastMonth float64
|
AvailableLastMonth postgres.Numeric
|
||||||
Activity float64
|
Activity postgres.Numeric
|
||||||
Assigned float64
|
Assigned postgres.Numeric
|
||||||
}
|
}
|
||||||
|
|
||||||
func getDate(c *gin.Context) (time.Time, error) {
|
func getDate(c *gin.Context) (time.Time, error) {
|
||||||
@ -51,50 +51,6 @@ func getDate(c *gin.Context) (time.Time, error) {
|
|||||||
return getFirstOfMonth(year, month, time.Now().Location()), nil
|
return getFirstOfMonth(year, month, time.Now().Location()), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Handler) autocompleteCategories(c *gin.Context) {
|
|
||||||
budgetID := c.Param("budgetid")
|
|
||||||
budgetUUID, err := uuid.Parse(budgetID)
|
|
||||||
if err != nil {
|
|
||||||
c.AbortWithError(http.StatusBadRequest, fmt.Errorf("budgetid missing from URL"))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
query := c.Request.URL.Query().Get("s")
|
|
||||||
searchParams := postgres.SearchCategoriesParams{
|
|
||||||
BudgetID: budgetUUID,
|
|
||||||
Search: "%" + query + "%",
|
|
||||||
}
|
|
||||||
categories, err := h.Service.SearchCategories(c.Request.Context(), searchParams)
|
|
||||||
if err != nil {
|
|
||||||
c.AbortWithError(http.StatusInternalServerError, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
c.JSON(http.StatusOK, categories)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *Handler) autocompletePayee(c *gin.Context) {
|
|
||||||
budgetID := c.Param("budgetid")
|
|
||||||
budgetUUID, err := uuid.Parse(budgetID)
|
|
||||||
if err != nil {
|
|
||||||
c.AbortWithError(http.StatusBadRequest, fmt.Errorf("budgetid missing from URL"))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
query := c.Request.URL.Query().Get("s")
|
|
||||||
searchParams := postgres.SearchPayeesParams{
|
|
||||||
BudgetID: budgetUUID,
|
|
||||||
Search: query + "%",
|
|
||||||
}
|
|
||||||
payees, err := h.Service.SearchPayees(c.Request.Context(), searchParams)
|
|
||||||
if err != nil {
|
|
||||||
c.AbortWithError(http.StatusInternalServerError, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
c.JSON(http.StatusOK, payees)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *Handler) budgetingForMonth(c *gin.Context) {
|
func (h *Handler) budgetingForMonth(c *gin.Context) {
|
||||||
budgetID := c.Param("budgetid")
|
budgetID := c.Param("budgetid")
|
||||||
budgetUUID, err := uuid.Parse(budgetID)
|
budgetUUID, err := uuid.Parse(budgetID)
|
||||||
@ -134,7 +90,7 @@ func (h *Handler) budgetingForMonth(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var availableBalance float64 = 0
|
availableBalance := postgres.NewZeroNumeric()
|
||||||
for _, cat := range categories {
|
for _, cat := range categories {
|
||||||
if cat.ID != budget.IncomeCategoryID {
|
if cat.ID != budget.IncomeCategoryID {
|
||||||
continue
|
continue
|
||||||
@ -150,13 +106,13 @@ func (h *Handler) budgetingForMonth(c *gin.Context) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
availableBalance += bal.Transactions.GetFloat64()
|
availableBalance = availableBalance.Add(bal.Transactions)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data := struct {
|
data := struct {
|
||||||
Categories []CategoryWithBalance
|
Categories []CategoryWithBalance
|
||||||
AvailableBalance float64
|
AvailableBalance postgres.Numeric
|
||||||
}{categoriesWithBalance, availableBalance}
|
}{categoriesWithBalance, availableBalance}
|
||||||
c.JSON(http.StatusOK, data)
|
c.JSON(http.StatusOK, data)
|
||||||
|
|
||||||
@ -182,39 +138,36 @@ func (h *Handler) budgeting(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
categories, err := h.Service.GetCategories(c.Request.Context(), budgetUUID)
|
|
||||||
if err != nil {
|
|
||||||
c.AbortWithError(http.StatusInternalServerError, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
data := struct {
|
data := struct {
|
||||||
Budget postgres.Budget
|
Accounts []postgres.GetAccountsWithBalanceRow
|
||||||
Accounts []postgres.GetAccountsWithBalanceRow
|
Budget postgres.Budget
|
||||||
Categories []postgres.GetCategoriesRow
|
}{accounts, budget}
|
||||||
}{
|
|
||||||
Accounts: accounts,
|
|
||||||
Budget: budget,
|
|
||||||
Categories: categories,
|
|
||||||
}
|
|
||||||
|
|
||||||
c.JSON(http.StatusOK, data)
|
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, float64, 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{}
|
categoriesWithBalance := []CategoryWithBalance{}
|
||||||
hiddenCategory := CategoryWithBalance{
|
hiddenCategory := CategoryWithBalance{
|
||||||
GetCategoriesRow: &postgres.GetCategoriesRow{
|
GetCategoriesRow: &postgres.GetCategoriesRow{
|
||||||
Name: "",
|
Name: "",
|
||||||
Group: "Hidden Categories",
|
Group: "Hidden Categories",
|
||||||
},
|
},
|
||||||
|
Available: postgres.NewZeroNumeric(),
|
||||||
|
AvailableLastMonth: postgres.NewZeroNumeric(),
|
||||||
|
Activity: postgres.NewZeroNumeric(),
|
||||||
|
Assigned: postgres.NewZeroNumeric(),
|
||||||
}
|
}
|
||||||
|
|
||||||
var moneyUsed float64 = 0
|
moneyUsed := postgres.NewZeroNumeric()
|
||||||
for i := range categories {
|
for i := range categories {
|
||||||
cat := &categories[i]
|
cat := &categories[i]
|
||||||
categoryWithBalance := CategoryWithBalance{
|
categoryWithBalance := CategoryWithBalance{
|
||||||
GetCategoriesRow: cat,
|
GetCategoriesRow: cat,
|
||||||
|
Available: postgres.NewZeroNumeric(),
|
||||||
|
AvailableLastMonth: postgres.NewZeroNumeric(),
|
||||||
|
Activity: postgres.NewZeroNumeric(),
|
||||||
|
Assigned: postgres.NewZeroNumeric(),
|
||||||
}
|
}
|
||||||
for _, bal := range cumultativeBalances {
|
for _, bal := range cumultativeBalances {
|
||||||
if bal.CategoryID != cat.ID {
|
if bal.CategoryID != cat.ID {
|
||||||
@ -225,29 +178,29 @@ func (h *Handler) calculateBalances(c *gin.Context, budget postgres.Budget, firs
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
moneyUsed -= bal.Assignments.GetFloat64()
|
moneyUsed = moneyUsed.Sub(bal.Assignments)
|
||||||
categoryWithBalance.Available += bal.Assignments.GetFloat64()
|
categoryWithBalance.Available = categoryWithBalance.Available.Add(bal.Assignments)
|
||||||
categoryWithBalance.Available += bal.Transactions.GetFloat64()
|
categoryWithBalance.Available = categoryWithBalance.Available.Add(bal.Transactions)
|
||||||
if categoryWithBalance.Available < 0 && bal.Date.Before(firstOfMonth) {
|
if !categoryWithBalance.Available.IsPositive() && bal.Date.Before(firstOfMonth) {
|
||||||
moneyUsed += categoryWithBalance.Available
|
moneyUsed = moneyUsed.Add(categoryWithBalance.Available)
|
||||||
categoryWithBalance.Available = 0
|
categoryWithBalance.Available = postgres.NewZeroNumeric()
|
||||||
}
|
}
|
||||||
|
|
||||||
if bal.Date.Before(firstOfMonth) {
|
if bal.Date.Before(firstOfMonth) {
|
||||||
categoryWithBalance.AvailableLastMonth = categoryWithBalance.Available
|
categoryWithBalance.AvailableLastMonth = categoryWithBalance.Available
|
||||||
} else if bal.Date.Before(firstOfNextMonth) {
|
} else if bal.Date.Before(firstOfNextMonth) {
|
||||||
categoryWithBalance.Activity = bal.Transactions.GetFloat64()
|
categoryWithBalance.Activity = bal.Transactions
|
||||||
categoryWithBalance.Assigned = bal.Assignments.GetFloat64()
|
categoryWithBalance.Assigned = bal.Assignments
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// do not show hidden categories
|
// do not show hidden categories
|
||||||
if cat.Group == "Hidden Categories" {
|
if cat.Group == "Hidden Categories" {
|
||||||
hiddenCategory.Available += categoryWithBalance.Available
|
hiddenCategory.Available = hiddenCategory.Available.Add(categoryWithBalance.Available)
|
||||||
hiddenCategory.AvailableLastMonth += categoryWithBalance.AvailableLastMonth
|
hiddenCategory.AvailableLastMonth = hiddenCategory.AvailableLastMonth.Add(categoryWithBalance.AvailableLastMonth)
|
||||||
hiddenCategory.Activity += categoryWithBalance.Activity
|
hiddenCategory.Activity = hiddenCategory.Activity.Add(categoryWithBalance.Activity)
|
||||||
hiddenCategory.Assigned += categoryWithBalance.Assigned
|
hiddenCategory.Assigned = hiddenCategory.Assigned.Add(categoryWithBalance.Assigned)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,6 +64,7 @@ func (h *Handler) Serve() {
|
|||||||
authenticated.GET("/account/:accountid/transactions", h.transactionsForAccount)
|
authenticated.GET("/account/:accountid/transactions", h.transactionsForAccount)
|
||||||
authenticated.GET("/admin/clear-database", h.clearDatabase)
|
authenticated.GET("/admin/clear-database", h.clearDatabase)
|
||||||
authenticated.GET("/budget/:budgetid", h.budgeting)
|
authenticated.GET("/budget/:budgetid", h.budgeting)
|
||||||
|
authenticated.GET("/budget/:budgetid/:year/:month", h.budgetingForMonth)
|
||||||
authenticated.GET("/budget/:budgetid/autocomplete/payees", h.autocompletePayee)
|
authenticated.GET("/budget/:budgetid/autocomplete/payees", h.autocompletePayee)
|
||||||
authenticated.GET("/budget/:budgetid/autocomplete/categories", h.autocompleteCategories)
|
authenticated.GET("/budget/:budgetid/autocomplete/categories", h.autocompleteCategories)
|
||||||
authenticated.DELETE("/budget/:budgetid", h.deleteBudget)
|
authenticated.DELETE("/budget/:budgetid", h.deleteBudget)
|
||||||
|
@ -1,11 +1,20 @@
|
|||||||
package postgres
|
package postgres
|
||||||
|
|
||||||
import "github.com/jackc/pgtype"
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math/big"
|
||||||
|
|
||||||
|
"github.com/jackc/pgtype"
|
||||||
|
)
|
||||||
|
|
||||||
type Numeric struct {
|
type Numeric struct {
|
||||||
pgtype.Numeric
|
pgtype.Numeric
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewZeroNumeric() Numeric {
|
||||||
|
return Numeric{pgtype.Numeric{Exp: 0, Int: big.NewInt(0), Status: pgtype.Present, NaN: false}}
|
||||||
|
}
|
||||||
|
|
||||||
func (n Numeric) GetFloat64() float64 {
|
func (n Numeric) GetFloat64() float64 {
|
||||||
if n.Status != pgtype.Present {
|
if n.Status != pgtype.Present {
|
||||||
return 0
|
return 0
|
||||||
@ -33,3 +42,83 @@ func (n Numeric) IsZero() bool {
|
|||||||
float := n.GetFloat64()
|
float := n.GetFloat64()
|
||||||
return float == 0
|
return float == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (n Numeric) MatchExp(exp int32) Numeric {
|
||||||
|
diffExp := exp - n.Exp
|
||||||
|
factor := big.NewInt(0).Exp(big.NewInt(10), big.NewInt(int64(diffExp)), nil)
|
||||||
|
return Numeric{pgtype.Numeric{
|
||||||
|
Exp: exp,
|
||||||
|
Int: big.NewInt(0).Mul(n.Int, factor),
|
||||||
|
Status: n.Status,
|
||||||
|
NaN: n.NaN,
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n Numeric) Sub(o Numeric) Numeric {
|
||||||
|
if n.Exp > o.Exp {
|
||||||
|
o = o.MatchExp(n.Exp)
|
||||||
|
} else if n.Exp < o.Exp {
|
||||||
|
n = n.MatchExp(o.Exp)
|
||||||
|
}
|
||||||
|
|
||||||
|
if o.Exp == n.Exp {
|
||||||
|
return Numeric{pgtype.Numeric{
|
||||||
|
Exp: n.Exp,
|
||||||
|
Int: big.NewInt(0).Sub(o.Int, n.Int),
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
panic("Cannot subtract with different exponents")
|
||||||
|
}
|
||||||
|
func (n Numeric) Add(o Numeric) Numeric {
|
||||||
|
fmt.Println("N", n, "O", o)
|
||||||
|
if n.Exp > o.Exp {
|
||||||
|
o = o.MatchExp(n.Exp)
|
||||||
|
} else if n.Exp < o.Exp {
|
||||||
|
n = n.MatchExp(o.Exp)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println("NM", n, "OM", o)
|
||||||
|
if o.Exp == n.Exp {
|
||||||
|
return Numeric{pgtype.Numeric{
|
||||||
|
Exp: n.Exp,
|
||||||
|
Int: big.NewInt(0).Add(o.Int, n.Int),
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
panic("Cannot add with different exponents")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n Numeric) MarshalJSON() ([]byte, error) {
|
||||||
|
if n.Int.Int64() == 0 {
|
||||||
|
return []byte("\"0\""), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
s := fmt.Sprintf("%d", n.Int)
|
||||||
|
bytes := []byte(s)
|
||||||
|
|
||||||
|
exp := n.Exp
|
||||||
|
for exp > 0 {
|
||||||
|
bytes = append(bytes, byte('0'))
|
||||||
|
exp--
|
||||||
|
}
|
||||||
|
|
||||||
|
if exp == 0 {
|
||||||
|
return bytes, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
length := int32(len(bytes))
|
||||||
|
var bytesWithSeparator []byte
|
||||||
|
|
||||||
|
exp = -exp
|
||||||
|
for length <= exp {
|
||||||
|
bytes = append(bytes, byte('0'))
|
||||||
|
length++
|
||||||
|
}
|
||||||
|
|
||||||
|
split := length - exp
|
||||||
|
bytesWithSeparator = append(bytesWithSeparator, bytes[:split]...)
|
||||||
|
bytesWithSeparator = append(bytesWithSeparator, byte('.'))
|
||||||
|
bytesWithSeparator = append(bytesWithSeparator, bytes[split:]...)
|
||||||
|
return bytesWithSeparator, nil
|
||||||
|
}
|
||||||
|
12
web/src/components/Currency.vue
Normal file
12
web/src/components/Currency.vue
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent } from "vue";
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
props: ["value"]
|
||||||
|
})
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<span :class="value < 0 ? 'negative' : ''">{{value.toLocaleString(undefined, {minimumFractionDigits: 2,})}} €</span>
|
||||||
|
</template>
|
@ -14,11 +14,6 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
components: { Autocomplete },
|
components: { Autocomplete },
|
||||||
props: ["budgetid", "accountid"],
|
props: ["budgetid", "accountid"],
|
||||||
watch: {
|
|
||||||
Payee() {
|
|
||||||
console.log(this.$data.Payee);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
methods: {
|
||||||
saveTransaction(e : MouseEvent) {
|
saveTransaction(e : MouseEvent) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
@ -34,8 +29,7 @@ export default defineComponent({
|
|||||||
}),
|
}),
|
||||||
headers: this.$store.getters.AuthHeaders,
|
headers: this.$store.getters.AuthHeaders,
|
||||||
})
|
})
|
||||||
.then(x => x.json())
|
.then(x => x.json());
|
||||||
.then(x => console.log(x));
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent } from "vue"
|
import { defineComponent } from "vue"
|
||||||
|
import Currency from "../components/Currency.vue"
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
props: ['budgetid', 'accountid'],
|
props: ["budgetid", "accountid"],
|
||||||
|
components: { Currency }
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@ -21,14 +23,14 @@ export default defineComponent({
|
|||||||
On-Budget Accounts
|
On-Budget Accounts
|
||||||
<div v-for="account in $store.getters.OnBudgetAccounts" class="flex flex-row justify-between px-3">
|
<div v-for="account in $store.getters.OnBudgetAccounts" class="flex flex-row justify-between px-3">
|
||||||
<router-link :to="'/budget/'+budgetid+'/account/'+account.ID">{{account.Name}}</router-link>
|
<router-link :to="'/budget/'+budgetid+'/account/'+account.ID">{{account.Name}}</router-link>
|
||||||
<span :class="account.Balance.Int < 0 ? 'negative' : ''">{{(account.Balance.Int / 100).toLocaleString(undefined, {minimumFractionDigits: 2,})}} €</span>
|
<Currency :value="account.Balance" />
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
<li class="bg-red-200 rounded-lg m-1 p-1 px-3">
|
<li class="bg-red-200 rounded-lg m-1 p-1 px-3">
|
||||||
Off-Budget Accounts
|
Off-Budget Accounts
|
||||||
<div v-for="account in $store.getters.OffBudgetAccounts" class="flex flex-row justify-between px-3">
|
<div v-for="account in $store.getters.OffBudgetAccounts" class="flex flex-row justify-between px-3">
|
||||||
<router-link :to="'/budget/'+budgetid+'/account/'+account.ID">{{account.Name}}</router-link>
|
<router-link :to="'/budget/'+budgetid+'/account/'+account.ID">{{account.Name}}</router-link>
|
||||||
<span :class="account.Balance.Int < 0 ? 'negative' : ''">{{account.Balance.Int / 100}}</span>
|
<Currency :value="account.Balance" />
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
<li class="bg-red-200 rounded-lg m-1 p-1 px-3">
|
<li class="bg-red-200 rounded-lg m-1 p-1 px-3">
|
||||||
|
@ -1,38 +1,53 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent } from "vue";
|
import { defineComponent } from "vue";
|
||||||
|
import { FETCH_MONTH_BUDGET } from "../store/action-types";
|
||||||
import { TITLE } from "../store/mutation-types";
|
import { TITLE } from "../store/mutation-types";
|
||||||
|
import Currency from "../components/Currency.vue";
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
mounted() {
|
mounted() {
|
||||||
this.$store.commit(TITLE, "Budget for " + this.month + " " + this.year)
|
this.$store.commit(TITLE, "Budget for " + this.month + " " + this.year);
|
||||||
|
return this.$store.dispatch(FETCH_MONTH_BUDGET, { budgetid: this.budgetid, year: this.$data.Year, month: this.$data.Month });
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
year() {
|
||||||
|
return this.$store.dispatch(FETCH_MONTH_BUDGET, { budgetid: this.budgetid, year: this.$data.Year, month: this.$data.Month });
|
||||||
|
},
|
||||||
|
month() {
|
||||||
|
return this.$store.dispatch(FETCH_MONTH_BUDGET, { budgetid: this.budgetid, year: this.$data.Year, month: this.$data.Month });
|
||||||
|
},
|
||||||
},
|
},
|
||||||
props: ["budgetid", "year", "month"],
|
props: ["budgetid", "year", "month"],
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
Year: this.year || new Date().getFullYear(),
|
Year: (this.year || new Date().getFullYear()) as number,
|
||||||
Month: this.month || new Date().getMonth()
|
Month: (this.month || new Date().getMonth()) as number
|
||||||
}
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
Categories() {
|
||||||
|
return this.$store.getters.Categories(this.$data.Year, this.$data.Month);
|
||||||
|
},
|
||||||
previous() {
|
previous() {
|
||||||
return {
|
return {
|
||||||
Year: new Date(this.$data.Year, this.$data.Month - 1, 1).getFullYear(),
|
Year: new Date(this.$data.Year, this.$data.Month - 1, 1).getFullYear(),
|
||||||
Month: new Date(this.$data.Year, this.$data.Month - 1, 1).getMonth(),
|
Month: new Date(this.$data.Year, this.$data.Month - 1, 1).getMonth(),
|
||||||
}
|
};
|
||||||
},
|
},
|
||||||
current() {
|
current() {
|
||||||
return {
|
return {
|
||||||
Year: new Date().getFullYear(),
|
Year: new Date().getFullYear(),
|
||||||
Month: new Date().getMonth(),
|
Month: new Date().getMonth(),
|
||||||
}
|
};
|
||||||
},
|
},
|
||||||
next() {
|
next() {
|
||||||
return {
|
return {
|
||||||
Year: new Date(this.$data.Year, this.$data.Month + 1, 1).getFullYear(),
|
Year: new Date(this.$data.Year, this.$data.Month + 1, 1).getFullYear(),
|
||||||
Month: new Date(this.$data.Year, this.$data.Month + 1, 1).getMonth(),
|
Month: new Date(this.$data.Year, this.$data.Month + 1, 1).getMonth(),
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
components: { Currency }
|
||||||
})
|
})
|
||||||
|
|
||||||
/*{{define "title"}}
|
/*{{define "title"}}
|
||||||
@ -42,7 +57,7 @@ export default defineComponent({
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<h1>
|
<h1>
|
||||||
Budget for {{current.Month}}/{{current.Year}}
|
Budget for {{Month}}/{{Year}}
|
||||||
</h1>
|
</h1>
|
||||||
<div>
|
<div>
|
||||||
<router-link :to="'/budget/'+$store.getters.CurrentBudget.ID +'/' + previous.Year + '/' + previous.Month">Previous Month</router-link> -
|
<router-link :to="'/budget/'+$store.getters.CurrentBudget.ID +'/' + previous.Year + '/' + previous.Month">Previous Month</router-link> -
|
||||||
@ -60,15 +75,15 @@ export default defineComponent({
|
|||||||
<th>Activity</th>
|
<th>Activity</th>
|
||||||
<th>Available</th>
|
<th>Available</th>
|
||||||
</tr>
|
</tr>
|
||||||
<tr v-for="category in $store.getters.Categories(2022, 1)">
|
<tr v-for="category in Categories">
|
||||||
<td>{{category.Group}}</td>
|
<td>{{category.Group}}</td>
|
||||||
<td>{{category.Name}}</td>
|
<td>{{category.Name}}</td>
|
||||||
<td></td>
|
<td></td>
|
||||||
<td></td>
|
<td></td>
|
||||||
<td>{{category.AvailableLastMonth}}</td>
|
<td class="text-right"><Currency :value="category.AvailableLastMonth" /></td>
|
||||||
<td>{{category.Assigned}}</td>
|
<td class="text-right"><Currency :value="category.Assigned" /></td>
|
||||||
<td>{{category.Activity}}</td>
|
<td class="text-right"><Currency :value="category.Activity" /></td>
|
||||||
<td>{{category.Available}}</td>
|
<td class="text-right"><Currency :value="category.Available" /></td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
</template>
|
</template>
|
@ -5,6 +5,7 @@ export const NEW_BUDGET = "New budget";
|
|||||||
export const SET_CURRENT_BUDGET = "Set current budget";
|
export const SET_CURRENT_BUDGET = "Set current budget";
|
||||||
export const SET_CURRENT_ACCOUNT = "Set current account";
|
export const SET_CURRENT_ACCOUNT = "Set current account";
|
||||||
export const FETCH_BUDGET = "Fetch budget";
|
export const FETCH_BUDGET = "Fetch budget";
|
||||||
|
export const FETCH_MONTH_BUDGET = "Fetch budget for month";
|
||||||
export const LOGIN = 'Log in';
|
export const LOGIN = 'Log in';
|
||||||
export const REGISTER = 'Register';
|
export const REGISTER = 'Register';
|
||||||
export const FETCH_ACCOUNT = "Fetch account";
|
export const FETCH_ACCOUNT = "Fetch account";
|
@ -1,11 +1,12 @@
|
|||||||
import { Module } from "vuex";
|
import { Module } from "vuex";
|
||||||
import { FETCH_ACCOUNT, FETCH_BUDGET, SET_CURRENT_ACCOUNT } from "../action-types";
|
import { FETCH_ACCOUNT, FETCH_BUDGET, FETCH_MONTH_BUDGET, SET_CURRENT_ACCOUNT } from "../action-types";
|
||||||
import { LOGOUT, TITLE } from "../mutation-types";
|
import { LOGOUT, TITLE } from "../mutation-types";
|
||||||
|
|
||||||
export interface BudgetState {
|
export interface BudgetState {
|
||||||
Accounts: Map<string, Account>,
|
Accounts: Map<string, Account>,
|
||||||
CurrentAccountID?: string,
|
CurrentAccountID?: string,
|
||||||
Categories: Map<string, Category>,
|
Categories: Map<string, Category>,
|
||||||
|
Months: Map<number, Map<number, Map<string, Category>>>,
|
||||||
Transactions: [],
|
Transactions: [],
|
||||||
Assignments: []
|
Assignments: []
|
||||||
}
|
}
|
||||||
@ -28,6 +29,7 @@ export const budgetStore : Module<BudgetState, any> = {
|
|||||||
state: {
|
state: {
|
||||||
Accounts: new Map<string, Account>(),
|
Accounts: new Map<string, Account>(),
|
||||||
CurrentAccountID: undefined,
|
CurrentAccountID: undefined,
|
||||||
|
Months: new Map<number, Map<number, Map<string, Category>>>(),
|
||||||
Categories: new Map<string, Category>(),
|
Categories: new Map<string, Category>(),
|
||||||
Transactions: [],
|
Transactions: [],
|
||||||
Assignments: []
|
Assignments: []
|
||||||
@ -61,6 +63,17 @@ export const budgetStore : Module<BudgetState, any> = {
|
|||||||
addCategory(state, category) {
|
addCategory(state, category) {
|
||||||
state.Categories.set(category.ID, category);
|
state.Categories.set(category.ID, category);
|
||||||
},
|
},
|
||||||
|
addCategoriesForMonth(state, {year, month, categories}) {
|
||||||
|
const yearMap = state.Months.get(year) || new Map<number, Map<string, Category>>();
|
||||||
|
state.Months.set(year, yearMap);
|
||||||
|
|
||||||
|
const monthMap = yearMap.get(month) || new Map<string, Category>();
|
||||||
|
yearMap.set(month, monthMap);
|
||||||
|
|
||||||
|
for (const category of categories){
|
||||||
|
monthMap.set(category.ID, category);
|
||||||
|
}
|
||||||
|
},
|
||||||
setCurrentAccountID(state, accountid) {
|
setCurrentAccountID(state, accountid) {
|
||||||
state.CurrentAccountID = accountid;
|
state.CurrentAccountID = accountid;
|
||||||
},
|
},
|
||||||
@ -74,7 +87,8 @@ export const budgetStore : Module<BudgetState, any> = {
|
|||||||
return state.Accounts.values();
|
return state.Accounts.values();
|
||||||
},
|
},
|
||||||
Categories: (state) => (year : number, month : number) => {
|
Categories: (state) => (year : number, month : number) => {
|
||||||
return state.Categories.values();
|
const yearMap = state.Months.get(year);
|
||||||
|
return yearMap?.get(month)?.values();
|
||||||
},
|
},
|
||||||
CurrentAccount(state) : Account | undefined {
|
CurrentAccount(state) : Account | undefined {
|
||||||
if (state.CurrentAccountID == null)
|
if (state.CurrentAccountID == null)
|
||||||
@ -113,5 +127,20 @@ export const budgetStore : Module<BudgetState, any> = {
|
|||||||
const response = await result.json();
|
const response = await result.json();
|
||||||
commit("setTransactions", response.Transactions);
|
commit("setTransactions", response.Transactions);
|
||||||
},
|
},
|
||||||
|
async [FETCH_BUDGET]({ state, commit, dispatch, rootState }, budgetid) {
|
||||||
|
const result = await dispatch("GET", { path: "/budget/" + budgetid });
|
||||||
|
const response = await result.json();
|
||||||
|
for (const account of response.Accounts || []) {
|
||||||
|
commit("addAccount", account);
|
||||||
|
}
|
||||||
|
for (const category of response.Categories || []) {
|
||||||
|
commit("addCategory", category);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async [FETCH_MONTH_BUDGET]({state, commit, dispatch, rootState }, {budgetid, month, year}) {
|
||||||
|
const result = await dispatch("GET", { path: "/budget/" + budgetid + "/" + year + "/" + month});
|
||||||
|
const response = await result.json();
|
||||||
|
commit("addCategoriesForMonth", {year, month, categories: response.Categories})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -130,16 +130,6 @@ export const store = createStore<State>({
|
|||||||
|
|
||||||
await dispatch(FETCH_BUDGET, budgetid)
|
await dispatch(FETCH_BUDGET, budgetid)
|
||||||
},
|
},
|
||||||
async [FETCH_BUDGET]({ state, commit, dispatch, rootState }, budgetid) {
|
|
||||||
const result = await dispatch("GET", { path: "/budget/" + budgetid });
|
|
||||||
const response = await result.json();
|
|
||||||
for (const account of response.Accounts || []) {
|
|
||||||
commit("addAccount", account);
|
|
||||||
}
|
|
||||||
for (const category of response.Categories || []) {
|
|
||||||
commit("addCategory", category);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
getters: {
|
getters: {
|
||||||
Budgets(state) {
|
Budgets(state) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user