Merge pull request 'Improve available balance and show overspent last month' (#54) from available-balance into master
All checks were successful
continuous-integration/drone/push Build is passing

Reviewed-on: #54
This commit is contained in:
Jan Bader 2022-04-24 21:23:17 +02:00
commit e1c6bd22d5
6 changed files with 92 additions and 52 deletions

View File

@ -11,10 +11,10 @@ RETURNING id;
-- name: UpdateTransaction :exec -- name: UpdateTransaction :exec
UPDATE transactions UPDATE transactions
SET date = $1, SET date = $1,
memo = $2, memo = $2,
amount = $3, amount = $3,
payee_id = $4, payee_id = $4,
category_id = $5 category_id = $5
WHERE id = $6; WHERE id = $6;
-- name: SetTransactionReconciled :exec -- name: SetTransactionReconciled :exec

View File

@ -294,10 +294,10 @@ func (q *Queries) SetTransactionReconciled(ctx context.Context, id uuid.UUID) er
const updateTransaction = `-- name: UpdateTransaction :exec const updateTransaction = `-- name: UpdateTransaction :exec
UPDATE transactions UPDATE transactions
SET date = $1, SET date = $1,
memo = $2, memo = $2,
amount = $3, amount = $3,
payee_id = $4, payee_id = $4,
category_id = $5 category_id = $5
WHERE id = $6 WHERE id = $6
` `

View File

@ -66,16 +66,17 @@ func (h *Handler) getBudgetingViewForMonth(ctx context.Context, budget postgres.
return BudgetingForMonthResponse{}, fmt.Errorf("error loading balances: %w", err) return BudgetingForMonthResponse{}, fmt.Errorf("error loading balances: %w", err)
} }
categoriesWithBalance, moneyUsed := h.calculateBalances(budget, month, categories, cumultativeBalances) categoriesWithBalance, moneyUsed, overspentLastMonth := h.calculateBalances(budget, month, categories, cumultativeBalances)
availableBalance := h.getAvailableBalance(budget, month, moneyUsed, cumultativeBalances) availableBalance := h.getAvailableBalance(budget, month, moneyUsed, cumultativeBalances)
data := BudgetingForMonthResponse{categoriesWithBalance, availableBalance} data := BudgetingForMonthResponse{categoriesWithBalance, availableBalance, overspentLastMonth}
return data, nil return data, nil
} }
type BudgetingForMonthResponse struct { type BudgetingForMonthResponse struct {
Categories []CategoryWithBalance Categories []CategoryWithBalance
AvailableBalance numeric.Numeric AvailableBalance numeric.Numeric
OverspentLastMonth numeric.Numeric
} }
func (*Handler) getAvailableBalance(budget postgres.Budget, month Month, func (*Handler) getAvailableBalance(budget postgres.Budget, month Month,
@ -135,10 +136,11 @@ func (h *Handler) getBudget(c *gin.Context, budgetUUID uuid.UUID) {
func (h *Handler) calculateBalances(budget postgres.Budget, month Month, func (h *Handler) calculateBalances(budget postgres.Budget, month Month,
categories []postgres.GetCategoriesRow, cumultativeBalances []postgres.GetCumultativeBalancesRow, categories []postgres.GetCategoriesRow, cumultativeBalances []postgres.GetCumultativeBalancesRow,
) ([]CategoryWithBalance, numeric.Numeric) { ) ([]CategoryWithBalance, numeric.Numeric, numeric.Numeric) {
categoriesWithBalance := []CategoryWithBalance{} categoriesWithBalance := []CategoryWithBalance{}
moneyUsed := numeric.Zero() moneyUsed := numeric.Zero()
overspentLastMonth := numeric.Zero()
categories = append(categories, postgres.GetCategoriesRow{ categories = append(categories, postgres.GetCategoriesRow{
Group: "Income", Group: "Income",
Name: "No Category", Name: "No Category",
@ -172,6 +174,9 @@ func (h *Handler) calculateBalances(budget postgres.Budget, month Month,
categoryWithBalance.AvailableLastMonth.AddI(bal.Transactions) categoryWithBalance.AvailableLastMonth.AddI(bal.Transactions)
if !categoryWithBalance.AvailableLastMonth.IsPositive() { if !categoryWithBalance.AvailableLastMonth.IsPositive() {
moneyUsed.AddI(categoryWithBalance.AvailableLastMonth) moneyUsed.AddI(categoryWithBalance.AvailableLastMonth)
if month.Previous().InPresent(bal.Date) {
overspentLastMonth.AddI(categoryWithBalance.AvailableLastMonth)
}
categoryWithBalance.AvailableLastMonth = numeric.Zero() categoryWithBalance.AvailableLastMonth = numeric.Zero()
} }
} }
@ -179,5 +184,5 @@ func (h *Handler) calculateBalances(budget postgres.Budget, month Month,
categoriesWithBalance = append(categoriesWithBalance, categoryWithBalance) categoriesWithBalance = append(categoriesWithBalance, categoryWithBalance)
} }
return categoriesWithBalance, moneyUsed return categoriesWithBalance, moneyUsed, overspentLastMonth
} }

View File

@ -22,6 +22,22 @@ func (m Month) FirstOfMonth() time.Time {
return time.Date(m.Year, time.Month(m.Month), 1, 0, 0, 0, 0, time.Now().Location()) return time.Date(m.Year, time.Month(m.Month), 1, 0, 0, 0, 0, time.Now().Location())
} }
func (m Month) Previous() Month {
if m.Month == int(time.January) {
return Month{Year: m.Year - 1, Month: int(time.December)}
}
return Month{Year: m.Year, Month: m.Month - 1}
}
func (m Month) Next() Month {
if m.Month == int(time.December) {
return Month{Year: m.Year + 1, Month: int(time.January)}
}
return Month{Year: m.Year, Month: m.Month + 1}
}
func (m Month) InFuture(date time.Time) bool { func (m Month) InFuture(date time.Time) bool {
if m.Year < date.Year() { if m.Year < date.Year() {
return true return true

View File

@ -78,29 +78,54 @@ const budgeted = computed(() => accountStore.GetBudgeted(selected.value.Year, se
<template> <template>
<h1>Budget for {{ selected.Month + 1 }}/{{ selected.Year }}</h1> <h1>Budget for {{ selected.Month + 1 }}/{{ selected.Year }}</h1>
<span> <table class="inline-block">
Available last month: <tr>
<Currency <td>
:value="accountStore.GetIncomeAvailable(previous.Year, previous.Month)" Available last month:
/> </td>
</span><br> <td class="text-right">
<span>Available balance: <Currency :value="accountStore.Available-accountStore.OverspentLastMonth+budgeted.Assigned+budgeted.Deassigned" />
<Currency </td>
:value="accountStore.GetIncomeAvailable(selected.Year, selected.Month)" </tr>
/> <tr>
</span><br> <td>
<span>Budgeted this month: Overspent last month:
<Currency :value="budgeted.Assigned" /> - <Currency :value="-budgeted.Deassigned" /> = <Currency :value="budgeted.Assigned+budgeted.Deassigned" /> </td>
</span><br> <td class="text-right">
<span>Income: <Currency :value="accountStore.OverspentLastMonth" />
<Currency </td>
:value="budgeted.Income" </tr>
/> <Currency <tr>
:value="budgeted.Spent" <td>
/> = <Currency Budgeted this month:
:value="budgeted.Income + budgeted.Spent" </td>
/> <td class="text-right">
</span><br> <Currency :value="budgeted.Assigned+budgeted.Deassigned" />
</td>
<td class="text-sm pl-2">
= <Currency :value="budgeted.Assigned" /> - <Currency :value="-budgeted.Deassigned" />
</td>
</tr>
<tr class="font-bold">
<td class="py-2">
Available balance:
</td>
<td class="text-right">
<Currency :value="accountStore.Available" />
</td>
</tr>
<tr>
<td>
Activity:
</td>
<td class="text-right">
<Currency :value="budgeted.Income + budgeted.Spent" />
</td>
<td class="text-sm pl-2">
= <Currency :value="budgeted.Income" /> - <Currency :value="-1 * budgeted.Spent" />
</td>
</tr>
</table>
<div> <div>
<router-link <router-link
:to="'/budget/' + CurrentBudgetID + '/budgeting/' + previous.Year + '/' + previous.Month" :to="'/budget/' + CurrentBudgetID + '/budgeting/' + previous.Year + '/' + previous.Month"

View File

@ -9,7 +9,8 @@ interface State {
CurrentAccountID: string | null; CurrentAccountID: string | null;
Categories: Map<string, Category>; Categories: Map<string, Category>;
Months: Map<number, Map<number, Map<string, Category>>>; Months: Map<number, Map<number, Map<string, Category>>>;
Available: Map<number, Map<number, number>>; Available: number,
OverspentLastMonth: number,
Assignments: []; Assignments: [];
} }
@ -51,7 +52,8 @@ export const useAccountStore = defineStore("budget/account", {
Accounts: new Map<string, Account>(), Accounts: new Map<string, Account>(),
CurrentAccountID: null, CurrentAccountID: null,
Months: new Map<number, Map<number, Map<string, Category>>>(), Months: new Map<number, Map<number, Map<string, Category>>>(),
Available: new Map<number, Map<number, number>>(), Available: 0,
OverspentLastMonth: 0,
Categories: new Map<string, Category>(), Categories: new Map<string, Category>(),
Assignments: [], Assignments: [],
}), }),
@ -107,12 +109,6 @@ export const useAccountStore = defineStore("budget/account", {
}; };
}; };
}, },
GetIncomeAvailable(state) {
return (year: number, month: number) => {
const yearMapAv = this.Available.get(year);
return yearMapAv?.get(month);
};
},
CategoryGroupsForMonth(state) { CategoryGroupsForMonth(state) {
return (year: number, month: number) => { return (year: number, month: number) => {
const categories = this.AllCategoriesForMonth(year, month); const categories = this.AllCategoriesForMonth(year, month);
@ -215,7 +211,7 @@ export const useAccountStore = defineStore("budget/account", {
response.Categories.length <= 0 response.Categories.length <= 0
) )
return; return;
this.addCategoriesForMonth(year, month, response.Categories, response.AvailableBalance); this.addCategoriesForMonth(year, month, response.Categories, response.AvailableBalance, response.OverspentLastMonth);
}, },
async EditAccount( async EditAccount(
accountid: string, accountid: string,
@ -242,7 +238,8 @@ export const useAccountStore = defineStore("budget/account", {
year: number, year: number,
month: number, month: number,
categories: Category[], categories: Category[],
available: number available: number,
overspentLastMonth: number,
): void { ): void {
this.$patch((state) => { this.$patch((state) => {
const yearMap = const yearMap =
@ -257,11 +254,8 @@ export const useAccountStore = defineStore("budget/account", {
yearMap.set(month, monthMap); yearMap.set(month, monthMap);
state.Months.set(year, yearMap); state.Months.set(year, yearMap);
const yearMapAv = state.Available = available;
state.Available.get(year) || state.OverspentLastMonth = overspentLastMonth;
new Map<number, number>();
yearMapAv.set(month, available);
state.Available.set(year, yearMapAv);
}); });
}, },
logout() { logout() {