Fixed category balance calculations.
This commit is contained in:
parent
1e35494f9d
commit
78ebbac9ca
|
|
@ -190,7 +190,7 @@ class SqliteTransactionCategoryRepository : TransactionCategoryRepository {
|
||||||
stmt.bind(idx++, beforeTimestamp.value.toISOExtString());
|
stmt.bind(idx++, beforeTimestamp.value.toISOExtString());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
string query = qb.build() ~ " ORDER BY je.currency ASC, je.type ASC";
|
string query = qb.build() ~ " GROUP BY je.currency, je.type ORDER BY je.currency ASC, je.type ASC";
|
||||||
Statement stmt = db.prepare(query);
|
Statement stmt = db.prepare(query);
|
||||||
qb.applyArgBindings(stmt);
|
qb.applyArgBindings(stmt);
|
||||||
ResultRange result = stmt.execute();
|
ResultRange result = stmt.execute();
|
||||||
|
|
@ -201,7 +201,6 @@ class SqliteTransactionCategoryRepository : TransactionCategoryRepository {
|
||||||
Currency currency = Currency.ofCode(row.peek!(string, PeekMode.slice)(0));
|
Currency currency = Currency.ofCode(row.peek!(string, PeekMode.slice)(0));
|
||||||
string journalEntryType = row.peek!(string, PeekMode.slice)(1);
|
string journalEntryType = row.peek!(string, PeekMode.slice)(1);
|
||||||
ulong amountSum = row.peek!ulong(2);
|
ulong amountSum = row.peek!ulong(2);
|
||||||
|
|
||||||
|
|
||||||
TransactionCategoryBalance balance;
|
TransactionCategoryBalance balance;
|
||||||
if (currency.numericCode in balancesGroupedByCurrency) {
|
if (currency.numericCode in balancesGroupedByCurrency) {
|
||||||
|
|
|
||||||
|
|
@ -187,8 +187,10 @@ export class TransactionApiClient extends ApiClient {
|
||||||
return super.getJson(this.path + '/categories/' + parentId + '/children')
|
return super.getJson(this.path + '/categories/' + parentId + '/children')
|
||||||
}
|
}
|
||||||
|
|
||||||
getCategoryBalances(id: number): Promise<TransactionCategoryBalance[]> {
|
getCategoryBalances(id: number, includeChildren: boolean): Promise<TransactionCategoryBalance[]> {
|
||||||
return super.getJson(this.path + '/categories/' + id + '/balances')
|
return super.getJson(
|
||||||
|
this.path + '/categories/' + id + '/balances?includeChildren=' + includeChildren,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
createCategory(data: CreateCategoryPayload): Promise<TransactionCategory> {
|
createCategory(data: CreateCategoryPayload): Promise<TransactionCategory> {
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,7 @@ watch(balancesIncludeSubcategories, async () => {
|
||||||
if (!category.value) return
|
if (!category.value) return
|
||||||
balances.value = await new TransactionApiClient(getSelectedProfile(route)).getCategoryBalances(
|
balances.value = await new TransactionApiClient(getSelectedProfile(route)).getCategoryBalances(
|
||||||
category.value.id,
|
category.value.id,
|
||||||
|
balancesIncludeSubcategories.value
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -49,6 +50,8 @@ async function loadCategory(id: number) {
|
||||||
category.value = undefined
|
category.value = undefined
|
||||||
parentCategory.value = undefined
|
parentCategory.value = undefined
|
||||||
childCategories.value = []
|
childCategories.value = []
|
||||||
|
balances.value = []
|
||||||
|
balancesIncludeSubcategories.value = true
|
||||||
relatedTransactionsPage.value = defaultPage()
|
relatedTransactionsPage.value = defaultPage()
|
||||||
try {
|
try {
|
||||||
const api = new TransactionApiClient(getSelectedProfile(route))
|
const api = new TransactionApiClient(getSelectedProfile(route))
|
||||||
|
|
@ -57,7 +60,7 @@ async function loadCategory(id: number) {
|
||||||
parentCategory.value = await api.getCategory(category.value.parentId)
|
parentCategory.value = await api.getCategory(category.value.parentId)
|
||||||
}
|
}
|
||||||
childCategories.value = await api.getChildCategories(category.value.id)
|
childCategories.value = await api.getChildCategories(category.value.id)
|
||||||
balances.value = await api.getCategoryBalances(category.value.id)
|
balances.value = await api.getCategoryBalances(category.value.id, balancesIncludeSubcategories.value)
|
||||||
await fetchPage(1)
|
await fetchPage(1)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err)
|
console.error(err)
|
||||||
|
|
@ -84,32 +87,16 @@ async function fetchPage(pg: number) {
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<AppPage
|
<AppPage v-if="category" :title="'Category - ' + category.name">
|
||||||
v-if="category"
|
|
||||||
:title="'Category - ' + category.name"
|
|
||||||
>
|
|
||||||
<!-- Initial subtext with color & parent (if available). -->
|
<!-- Initial subtext with color & parent (if available). -->
|
||||||
<div>
|
<div>
|
||||||
<span
|
<span class="category-color-indicator" :style="{ 'background-color': '#' + category.color }"></span>
|
||||||
class="category-color-indicator"
|
<span v-if="parentCategory" class="text-muted" style="vertical-align: middle">
|
||||||
:style="{ 'background-color': '#' + category.color }"
|
|
||||||
></span>
|
|
||||||
<span
|
|
||||||
v-if="parentCategory"
|
|
||||||
class="text-muted"
|
|
||||||
style="vertical-align: middle"
|
|
||||||
>
|
|
||||||
A subcategory of
|
A subcategory of
|
||||||
<RouterLink
|
<RouterLink :to="`/profiles/${getSelectedProfile(route)}/categories/${parentCategory.id}`">{{
|
||||||
:to="`/profiles/${getSelectedProfile(route)}/categories/${parentCategory.id}`"
|
parentCategory.name }}</RouterLink>.
|
||||||
>{{ parentCategory.name }}</RouterLink
|
|
||||||
>.
|
|
||||||
</span>
|
</span>
|
||||||
<span
|
<span v-if="!parentCategory" class="text-muted" style="vertical-align: middle">
|
||||||
v-if="!parentCategory"
|
|
||||||
class="text-muted"
|
|
||||||
style="vertical-align: middle"
|
|
||||||
>
|
|
||||||
This is a top-level category.
|
This is a top-level category.
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -121,12 +108,8 @@ async function fetchPage(pg: number) {
|
||||||
<div v-if="childCategories.length > 0">
|
<div v-if="childCategories.length > 0">
|
||||||
<h3>Subcategories</h3>
|
<h3>Subcategories</h3>
|
||||||
<ul>
|
<ul>
|
||||||
<li
|
<li v-for="child in childCategories" :key="child.id">
|
||||||
v-for="child in childCategories"
|
<RouterLink :to="`/profiles/${getSelectedProfile(route)}/categories/${child.id}`">{{ child.name }}
|
||||||
:key="child.id"
|
|
||||||
>
|
|
||||||
<RouterLink :to="`/profiles/${getSelectedProfile(route)}/categories/${child.id}`"
|
|
||||||
>{{ child.name }}
|
|
||||||
</RouterLink>
|
</RouterLink>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
@ -135,33 +118,22 @@ async function fetchPage(pg: number) {
|
||||||
<!-- Display total balances. -->
|
<!-- Display total balances. -->
|
||||||
<div v-if="balances.length > 0">
|
<div v-if="balances.length > 0">
|
||||||
<h3>Balances</h3>
|
<h3>Balances</h3>
|
||||||
<div
|
<div v-for="balance in balances" :key="balance.currency.code">
|
||||||
v-for="balance in balances"
|
|
||||||
:key="balance.currency.code"
|
|
||||||
>
|
|
||||||
USD:
|
USD:
|
||||||
<AppBadge
|
<AppBadge>Debits:
|
||||||
>Debits:
|
|
||||||
<span class="text-positive">{{ formatMoney(balance.debits, balance.currency) }}</span>
|
<span class="text-positive">{{ formatMoney(balance.debits, balance.currency) }}</span>
|
||||||
</AppBadge>
|
</AppBadge>
|
||||||
<AppBadge
|
<AppBadge>Credits:
|
||||||
>Credits:
|
|
||||||
<span class="text-negative">{{ formatMoney(balance.credits, balance.currency) }}</span>
|
<span class="text-negative">{{ formatMoney(balance.credits, balance.currency) }}</span>
|
||||||
</AppBadge>
|
</AppBadge>
|
||||||
<AppBadge
|
<AppBadge>Balance:
|
||||||
>Balance:
|
<span :class="{ 'text-positive': balance.balance > 0, 'text-negative': balance.balance < 0 }">{{
|
||||||
<span
|
formatMoney(balance.balance, balance.currency) }}</span>
|
||||||
:class="{ 'text-positive': balance.balance > 0, 'text-negative': balance.balance < 0 }"
|
</AppBadge>
|
||||||
>{{ formatMoney(balance.balance, balance.currency) }}</span
|
|
||||||
></AppBadge
|
|
||||||
>
|
|
||||||
</div>
|
</div>
|
||||||
<FormGroup>
|
<FormGroup>
|
||||||
<FormControl label="Include Subcategories">
|
<FormControl label="Include Subcategories" v-if="childCategories.length > 0">
|
||||||
<input
|
<input type="checkbox" v-model="balancesIncludeSubcategories" />
|
||||||
type="checkbox"
|
|
||||||
v-model="balancesIncludeSubcategories"
|
|
||||||
/>
|
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<!--
|
<!--
|
||||||
<FormControl label="After">
|
<FormControl label="After">
|
||||||
|
|
@ -179,20 +151,9 @@ async function fetchPage(pg: number) {
|
||||||
<p class="text-muted font-size-small mt-0">
|
<p class="text-muted font-size-small mt-0">
|
||||||
Below is a list of all transactions recorded with this category, or any subcategory.
|
Below is a list of all transactions recorded with this category, or any subcategory.
|
||||||
</p>
|
</p>
|
||||||
<PaginationControls
|
<PaginationControls :page="relatedTransactionsPage" @update="(pr) => fetchPage(pr.page)" class="align-right" />
|
||||||
:page="relatedTransactionsPage"
|
<TransactionCard v-for="txn in relatedTransactionsPage.items" :key="txn.id" :tx="txn" />
|
||||||
@update="(pr) => fetchPage(pr.page)"
|
<p v-if="relatedTransactionsPage.totalElements === 0" class="text-muted font-italic">
|
||||||
class="align-right"
|
|
||||||
/>
|
|
||||||
<TransactionCard
|
|
||||||
v-for="txn in relatedTransactionsPage.items"
|
|
||||||
:key="txn.id"
|
|
||||||
:tx="txn"
|
|
||||||
/>
|
|
||||||
<p
|
|
||||||
v-if="relatedTransactionsPage.totalElements === 0"
|
|
||||||
class="text-muted font-italic"
|
|
||||||
>
|
|
||||||
There are no transactions linked to this category.
|
There are no transactions linked to this category.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue