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
UPDATE transactions
SET date = $1,
memo = $2,
amount = $3,
payee_id = $4,
category_id = $5
memo = $2,
amount = $3,
payee_id = $4,
category_id = $5
WHERE id = $6;
-- 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
UPDATE transactions
SET date = $1,
memo = $2,
amount = $3,
payee_id = $4,
category_id = $5
memo = $2,
amount = $3,
payee_id = $4,
category_id = $5
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)
}
categoriesWithBalance, moneyUsed := h.calculateBalances(budget, month, categories, cumultativeBalances)
categoriesWithBalance, moneyUsed, overspentLastMonth := h.calculateBalances(budget, month, categories, cumultativeBalances)
availableBalance := h.getAvailableBalance(budget, month, moneyUsed, cumultativeBalances)
data := BudgetingForMonthResponse{categoriesWithBalance, availableBalance}
data := BudgetingForMonthResponse{categoriesWithBalance, availableBalance, overspentLastMonth}
return data, nil
}
type BudgetingForMonthResponse struct {
Categories []CategoryWithBalance
AvailableBalance numeric.Numeric
Categories []CategoryWithBalance
AvailableBalance numeric.Numeric
OverspentLastMonth numeric.Numeric
}
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,
categories []postgres.GetCategoriesRow, cumultativeBalances []postgres.GetCumultativeBalancesRow,
) ([]CategoryWithBalance, numeric.Numeric) {
) ([]CategoryWithBalance, numeric.Numeric, numeric.Numeric) {
categoriesWithBalance := []CategoryWithBalance{}
moneyUsed := numeric.Zero()
overspentLastMonth := numeric.Zero()
categories = append(categories, postgres.GetCategoriesRow{
Group: "Income",
Name: "No Category",
@ -172,6 +174,9 @@ func (h *Handler) calculateBalances(budget postgres.Budget, month Month,
categoryWithBalance.AvailableLastMonth.AddI(bal.Transactions)
if !categoryWithBalance.AvailableLastMonth.IsPositive() {
moneyUsed.AddI(categoryWithBalance.AvailableLastMonth)
if month.Previous().InPresent(bal.Date) {
overspentLastMonth.AddI(categoryWithBalance.AvailableLastMonth)
}
categoryWithBalance.AvailableLastMonth = numeric.Zero()
}
}
@ -179,5 +184,5 @@ func (h *Handler) calculateBalances(budget postgres.Budget, month Month,
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())
}
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 {
if m.Year < date.Year() {
return true

View File

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

View File

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