Added total balances API endpoint.
This commit is contained in:
parent
df4460d2ca
commit
25b715156b
|
|
@ -115,6 +115,12 @@ private void writeHistoryResponse(ref ServerHttpResponse response, in Page!Accou
|
|||
response.writeBodyString(jsonStr, ContentTypes.APPLICATION_JSON);
|
||||
}
|
||||
|
||||
void handleGetTotalBalances(ref ServerHttpRequest request, ref ServerHttpResponse response) {
|
||||
auto ds = getProfileDataSource(request);
|
||||
auto balances = getTotalBalanceForAllAccounts(ds);
|
||||
writeJsonBody(response, balances);
|
||||
}
|
||||
|
||||
// Value records:
|
||||
|
||||
const PageRequest VALUE_RECORD_DEFAULT_PAGE_REQUEST = PageRequest(1, 10, [Sort("timestamp", SortDir.DESC)]);
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import std.datetime;
|
|||
import account.model;
|
||||
import account.data;
|
||||
import profile.data;
|
||||
import util.money;
|
||||
|
||||
/**
|
||||
* Gets the balance for an account, at a given point in time.
|
||||
|
|
@ -46,6 +47,39 @@ Optional!long getBalance(ProfileDataSource ds, ulong accountId, SysTime timestam
|
|||
return Optional!long.empty;
|
||||
}
|
||||
|
||||
struct CurrencyBalance {
|
||||
Currency currency;
|
||||
long balance;
|
||||
}
|
||||
|
||||
CurrencyBalance[] getTotalBalanceForAllAccounts(ProfileDataSource ds, SysTime timestamp = Clock.currTime(UTC())) {
|
||||
auto accountRepo = ds.getAccountRepository();
|
||||
CurrencyBalance[] balances;
|
||||
foreach (Account account; accountRepo.findAll()) {
|
||||
Optional!long accountBalance = getBalance(ds, account.id, timestamp);
|
||||
if (!accountBalance.isNull) {
|
||||
long value = accountBalance.value;
|
||||
if (!account.type.debitsPositive) {
|
||||
value = -value;
|
||||
}
|
||||
// Add the balance to the relevant currency balance:
|
||||
bool added = false;
|
||||
foreach (ref cb; balances) {
|
||||
if (cb.currency.code == account.currency.code) {
|
||||
cb.balance += value;
|
||||
added = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!added) {
|
||||
balances ~= CurrencyBalance(account.currency, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
return balances;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method that derives a balance for an account, by using the nearest
|
||||
* value record, and all journal entries between that record and the desired
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@ HttpRequestHandler mapApiHandlers(string webOrigin) {
|
|||
import account.api;
|
||||
a.map(HttpMethod.GET, PROFILE_PATH ~ "/accounts", &handleGetAccounts);
|
||||
a.map(HttpMethod.POST, PROFILE_PATH ~ "/accounts", &handleCreateAccount);
|
||||
a.map(HttpMethod.GET, PROFILE_PATH ~ "/account-balances", &handleGetTotalBalances);
|
||||
const ACCOUNT_PATH = PROFILE_PATH ~ "/accounts/:accountId:ulong";
|
||||
a.map(HttpMethod.GET, ACCOUNT_PATH, &handleGetAccount);
|
||||
a.map(HttpMethod.PUT, ACCOUNT_PATH, &handleUpdateAccount);
|
||||
|
|
|
|||
|
|
@ -123,11 +123,18 @@ export interface AccountHistoryJournalEntryItem extends AccountHistoryItem {
|
|||
transactionDescription: string
|
||||
}
|
||||
|
||||
export interface CurrencyBalance {
|
||||
currency: Currency
|
||||
balance: number
|
||||
}
|
||||
|
||||
export class AccountApiClient extends ApiClient {
|
||||
readonly path: string
|
||||
readonly profileName: string
|
||||
|
||||
constructor(route: RouteLocation) {
|
||||
super()
|
||||
this.profileName = getSelectedProfile(route)
|
||||
this.path = `/profiles/${getSelectedProfile(route)}/accounts`
|
||||
}
|
||||
|
||||
|
|
@ -155,6 +162,10 @@ export class AccountApiClient extends ApiClient {
|
|||
return super.getJsonPage(`${this.path}/${id}/history`, pageRequest)
|
||||
}
|
||||
|
||||
getTotalBalances(): Promise<CurrencyBalance[]> {
|
||||
return super.getJson(`/profiles/${this.profileName}/account-balances`)
|
||||
}
|
||||
|
||||
getValueRecords(accountId: number, pageRequest: PageRequest): Promise<Page<AccountValueRecord>> {
|
||||
return super.getJsonPage(this.path + '/' + accountId + '/value-records', pageRequest)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<script setup lang="ts">
|
||||
import { AccountApiClient, type Account } from '@/api/account'
|
||||
import { AccountApiClient, type Account, type CurrencyBalance } from '@/api/account'
|
||||
import { formatMoney } from '@/api/data'
|
||||
import { getSelectedProfile } from '@/api/profile'
|
||||
import AppButton from '@/components/AppButton.vue'
|
||||
|
|
@ -11,11 +11,16 @@ const router = useRouter()
|
|||
const route = useRoute()
|
||||
|
||||
const accounts: Ref<Account[]> = ref([])
|
||||
const totalBalances: Ref<CurrencyBalance[]> = ref([])
|
||||
|
||||
onMounted(async () => {
|
||||
const accountApi = new AccountApiClient(route)
|
||||
accountApi.getAccounts().then(result => accounts.value = result)
|
||||
.catch(err => console.error(err))
|
||||
accountApi.getTotalBalances().then(result => {
|
||||
totalBalances.value = result
|
||||
})
|
||||
.catch(err => console.error(err))
|
||||
})
|
||||
</script>
|
||||
<template>
|
||||
|
|
@ -51,6 +56,16 @@ onMounted(async () => {
|
|||
<p v-if="accounts.length === 0">
|
||||
You haven't added any accounts. Add one to start tracking your finances.
|
||||
</p>
|
||||
|
||||
<div>
|
||||
<ul>
|
||||
<li v-for="bal in totalBalances" :key="bal.currency.code">
|
||||
<span>Total in {{ bal.currency.code }}</span>
|
||||
=
|
||||
<span>{{ formatMoney(bal.balance, bal.currency) }}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
<template v-slot:actions>
|
||||
<AppButton icon="plus" @click="router.push(`/profiles/${getSelectedProfile(route)}/add-account`)">Add Account
|
||||
|
|
|
|||
Loading…
Reference in New Issue