Added account diffs and cleaned up transaction page.
This commit is contained in:
parent
30715947a3
commit
534071cbe0
|
|
@ -79,6 +79,23 @@ void handleDeleteAccount(ref ServerHttpRequest request, ref ServerHttpResponse r
|
||||||
ds.getAccountRepository().deleteById(accountId);
|
ds.getAccountRepository().deleteById(accountId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void handleGetAccountBalance(ref ServerHttpRequest request, ref ServerHttpResponse response) {
|
||||||
|
ulong accountId = request.getPathParamAs!ulong("accountId");
|
||||||
|
auto ds = getProfileDataSource(request);
|
||||||
|
SysTime timestamp = Clock.currTime(UTC());
|
||||||
|
string providedTimestamp = request.getParamAs!string("timestamp");
|
||||||
|
if (providedTimestamp != null && providedTimestamp.length > 0) {
|
||||||
|
timestamp = SysTime.fromISOExtString(providedTimestamp);
|
||||||
|
}
|
||||||
|
Optional!long balance = getBalance(ds, accountId, timestamp);
|
||||||
|
if (balance.isNull) {
|
||||||
|
response.writeBodyString("{\"balance\": null}", ContentTypes.APPLICATION_JSON);
|
||||||
|
} else {
|
||||||
|
import std.conv : to;
|
||||||
|
response.writeBodyString("{\"balance\": " ~ balance.value.to!string ~ "}", ContentTypes.APPLICATION_JSON);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void handleGetAccountHistory(ref ServerHttpRequest request, ref ServerHttpResponse response) {
|
void handleGetAccountHistory(ref ServerHttpRequest request, ref ServerHttpResponse response) {
|
||||||
ulong accountId = request.getPathParamOrThrow!ulong("accountId");
|
ulong accountId = request.getPathParamOrThrow!ulong("accountId");
|
||||||
PageRequest pagination = PageRequest.parse(request, PageRequest(1, 10, [Sort("timestamp", SortDir.DESC)]));
|
PageRequest pagination = PageRequest.parse(request, PageRequest(1, 10, [Sort("timestamp", SortDir.DESC)]));
|
||||||
|
|
|
||||||
|
|
@ -59,6 +59,7 @@ HttpRequestHandler mapApiHandlers(string webOrigin) {
|
||||||
a.map(HttpMethod.GET, ACCOUNT_PATH, &handleGetAccount);
|
a.map(HttpMethod.GET, ACCOUNT_PATH, &handleGetAccount);
|
||||||
a.map(HttpMethod.PUT, ACCOUNT_PATH, &handleUpdateAccount);
|
a.map(HttpMethod.PUT, ACCOUNT_PATH, &handleUpdateAccount);
|
||||||
a.map(HttpMethod.DELETE, ACCOUNT_PATH, &handleDeleteAccount);
|
a.map(HttpMethod.DELETE, ACCOUNT_PATH, &handleDeleteAccount);
|
||||||
|
a.map(HttpMethod.GET, ACCOUNT_PATH ~ "/balance", &handleGetAccountBalance);
|
||||||
a.map(HttpMethod.GET, ACCOUNT_PATH ~ "/history", &handleGetAccountHistory);
|
a.map(HttpMethod.GET, ACCOUNT_PATH ~ "/history", &handleGetAccountHistory);
|
||||||
a.map(HttpMethod.GET, ACCOUNT_PATH ~ "/value-records", &handleGetValueRecords);
|
a.map(HttpMethod.GET, ACCOUNT_PATH ~ "/value-records", &handleGetValueRecords);
|
||||||
a.map(HttpMethod.GET, ACCOUNT_PATH ~ "/value-records/:valueRecordId:ulong", &handleGetValueRecord);
|
a.map(HttpMethod.GET, ACCOUNT_PATH ~ "/value-records/:valueRecordId:ulong", &handleGetValueRecord);
|
||||||
|
|
|
||||||
|
|
@ -173,6 +173,14 @@ export class AccountApiClient extends ApiClient {
|
||||||
return super.delete(this.path + '/' + id)
|
return super.delete(this.path + '/' + id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getBalance(id: number, timestamp: Date): Promise<number | undefined> {
|
||||||
|
const result: { balance: number | null } = await super.getJson(
|
||||||
|
`${this.path}/${id}/balance?timestamp=${timestamp.toISOString()}`,
|
||||||
|
)
|
||||||
|
if (result.balance === null) return undefined
|
||||||
|
return result.balance
|
||||||
|
}
|
||||||
|
|
||||||
getHistory(id: number, pageRequest: PageRequest): Promise<Page<AccountHistoryItem>> {
|
getHistory(id: number, pageRequest: PageRequest): Promise<Page<AccountHistoryItem>> {
|
||||||
return super.getJsonPage(`${this.path}/${id}/history`, pageRequest)
|
return super.getJsonPage(`${this.path}/${id}/history`, pageRequest)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,3 +14,8 @@
|
||||||
margin-left: 0.5rem;
|
margin-left: 0.5rem;
|
||||||
margin-right: 0.5rem;
|
margin-right: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.my-1 {
|
||||||
|
margin-top: 0.5rem;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,17 +15,26 @@ import AttachmentRow from '@/components/common/AttachmentRow.vue'
|
||||||
import LineItemCard from '@/components/LineItemCard.vue'
|
import LineItemCard from '@/components/LineItemCard.vue'
|
||||||
import AppBadge from '@/components/common/AppBadge.vue'
|
import AppBadge from '@/components/common/AppBadge.vue'
|
||||||
import ButtonBar from '@/components/common/ButtonBar.vue'
|
import ButtonBar from '@/components/common/ButtonBar.vue'
|
||||||
|
import { AccountApiClient } from '@/api/account'
|
||||||
|
|
||||||
|
interface BalanceDiff {
|
||||||
|
before: number
|
||||||
|
after: number
|
||||||
|
}
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const transactionApi = new TransactionApiClient(getSelectedProfile(route))
|
const transactionApi = new TransactionApiClient(getSelectedProfile(route))
|
||||||
|
|
||||||
const transaction: Ref<TransactionDetail | undefined> = ref()
|
const transaction: Ref<TransactionDetail | undefined> = ref()
|
||||||
|
const creditedAccountBalanceDiff = ref<BalanceDiff | undefined>()
|
||||||
|
const debitedAccountBalanceDiff = ref<BalanceDiff | undefined>()
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
const transactionId = parseInt(route.params.id as string)
|
const transactionId = parseInt(route.params.id as string)
|
||||||
try {
|
try {
|
||||||
transaction.value = await transactionApi.getTransaction(transactionId)
|
transaction.value = await transactionApi.getTransaction(transactionId)
|
||||||
|
fetchBalanceDiffs()
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err)
|
console.error(err)
|
||||||
await router.replace('/')
|
await router.replace('/')
|
||||||
|
|
@ -35,6 +44,34 @@ onMounted(async () => {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
async function fetchBalanceDiffs() {
|
||||||
|
const txn = transaction.value
|
||||||
|
if (!txn) return
|
||||||
|
const txnTime = new Date(txn.timestamp).getTime()
|
||||||
|
const beforeTs = new Date(txnTime - 1000)
|
||||||
|
const afterTs = new Date(txnTime + 1000)
|
||||||
|
const accountApi = new AccountApiClient(route)
|
||||||
|
const p1: Promise<number | undefined> = txn.creditedAccount
|
||||||
|
? accountApi.getBalance(txn.creditedAccount.id, beforeTs)
|
||||||
|
: Promise.resolve(undefined)
|
||||||
|
const p2: Promise<number | undefined> = txn.creditedAccount
|
||||||
|
? accountApi.getBalance(txn.creditedAccount.id, afterTs)
|
||||||
|
: Promise.resolve(undefined)
|
||||||
|
const p3: Promise<number | undefined> = txn.debitedAccount
|
||||||
|
? accountApi.getBalance(txn.debitedAccount.id, beforeTs)
|
||||||
|
: Promise.resolve(undefined)
|
||||||
|
const p4: Promise<number | undefined> = txn.debitedAccount
|
||||||
|
? accountApi.getBalance(txn.debitedAccount.id, afterTs)
|
||||||
|
: Promise.resolve(undefined)
|
||||||
|
const results = await Promise.all([p1, p2, p3, p4])
|
||||||
|
if (results[0] !== undefined && results[1] !== undefined) {
|
||||||
|
creditedAccountBalanceDiff.value = { before: results[0], after: results[1] }
|
||||||
|
}
|
||||||
|
if (results[2] !== undefined && results[3] !== undefined) {
|
||||||
|
debitedAccountBalanceDiff.value = { before: results[2], after: results[3] }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function deleteTransaction() {
|
async function deleteTransaction() {
|
||||||
if (!transaction.value) return
|
if (!transaction.value) return
|
||||||
const conf = await showConfirm(
|
const conf = await showConfirm(
|
||||||
|
|
@ -68,18 +105,39 @@ async function deleteTransaction() {
|
||||||
|
|
||||||
<p>{{ transaction.description }}</p>
|
<p>{{ transaction.description }}</p>
|
||||||
|
|
||||||
<p v-if="transaction.creditedAccount">
|
<div v-if="transaction.creditedAccount" class="my-1">
|
||||||
<strong class="text-negative">Credited</strong> from
|
<strong class="text-negative">Credited</strong> from
|
||||||
<RouterLink :to="`/profiles/${getSelectedProfile(route)}/accounts/${transaction.creditedAccount.id}`">
|
<RouterLink :to="`/profiles/${getSelectedProfile(route)}/accounts/${transaction.creditedAccount.id}`">
|
||||||
{{ transaction.creditedAccount.name }} (#{{ transaction.creditedAccount.numberSuffix }})
|
{{ transaction.creditedAccount.name }} (#{{ transaction.creditedAccount.numberSuffix }})
|
||||||
</RouterLink>
|
</RouterLink>
|
||||||
</p>
|
<div v-if="creditedAccountBalanceDiff" class="font-size-xsmall">
|
||||||
<p v-if="transaction.debitedAccount">
|
Balance Before:
|
||||||
|
<span class="font-mono">
|
||||||
|
{{ formatMoney(creditedAccountBalanceDiff.before, transaction.currency) }}
|
||||||
|
</span>
|
||||||
|
/ After:
|
||||||
|
<span class="font-mono">
|
||||||
|
{{ formatMoney(creditedAccountBalanceDiff.after, transaction.currency) }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="transaction.debitedAccount" class="my-1">
|
||||||
<strong class="text-positive">Debited</strong> to
|
<strong class="text-positive">Debited</strong> to
|
||||||
<RouterLink :to="`/profiles/${getSelectedProfile(route)}/accounts/${transaction.debitedAccount.id}`">
|
<RouterLink :to="`/profiles/${getSelectedProfile(route)}/accounts/${transaction.debitedAccount.id}`">
|
||||||
{{ transaction.debitedAccount.name }} (#{{ transaction.debitedAccount.numberSuffix }})
|
{{ transaction.debitedAccount.name }} (#{{ transaction.debitedAccount.numberSuffix }})
|
||||||
</RouterLink>
|
</RouterLink>
|
||||||
</p>
|
<div v-if="debitedAccountBalanceDiff" class="font-size-xsmall">
|
||||||
|
Balance Before:
|
||||||
|
<span class="font-mono">
|
||||||
|
{{ formatMoney(debitedAccountBalanceDiff.before, transaction.currency) }}
|
||||||
|
</span>
|
||||||
|
/ After:
|
||||||
|
<span class="font-mono">
|
||||||
|
{{ formatMoney(debitedAccountBalanceDiff.after, transaction.currency) }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- All remaining properties are put in this table. -->
|
<!-- All remaining properties are put in this table. -->
|
||||||
<PropertiesTable>
|
<PropertiesTable>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue