Show Outstanding Balances split per direction per currency
When a user has entries in multiple currencies that go in opposite directions — e.g. an income entry in EUR (user owes the org) and an expense entry in CAD (org owes user) — the previous row collapsed both into a single "Owes you" / "You owe" label driven by the net sats balance. The fiat amounts were displayed via Math.abs(), hiding the per-currency signs the backend already returns, so the row was actively misleading: it showed €200 and CA$300 under one direction when in reality they point in opposite directions. Render up to two grouped lines instead — "Owes you €200.00" and "You owe CA$300.00" — using new owesYouFiat / youOweFiat helpers that filter the signed fiat_balances dict by sign. Net sats stays as a small caption with an explicit "(receivable)"/"(payable)" qualifier, since sats can be netted but distinct fiat currencies can't without a spot rate. Falls back to the old single-line render when there are no fiat balances (sats-only entries).
This commit is contained in:
parent
deeec7e2c5
commit
0a7c39adcb
2 changed files with 44 additions and 8 deletions
|
|
@ -1645,6 +1645,31 @@ window.app = Vue.createApp({
|
|||
isIncomeEntry(entry) {
|
||||
return Array.isArray(entry.tags) && entry.tags.includes('income-entry')
|
||||
},
|
||||
// Per-currency split for multi-currency balances. Sign convention from the
|
||||
// super-user perspective: positive fiat = user owes Libra (Receivable),
|
||||
// negative fiat = Libra owes user (Payable). Distinct currencies can't be
|
||||
// netted across each other (no spot rate), so we render them grouped by
|
||||
// direction instead of one collapsed label.
|
||||
owesYouFiat(fiatBalances) {
|
||||
if (!fiatBalances) return {}
|
||||
return Object.fromEntries(
|
||||
Object.entries(fiatBalances).filter(([_, amount]) => Number(amount) > 0.005)
|
||||
)
|
||||
},
|
||||
youOweFiat(fiatBalances) {
|
||||
if (!fiatBalances) return {}
|
||||
return Object.fromEntries(
|
||||
Object.entries(fiatBalances)
|
||||
.filter(([_, amount]) => Number(amount) < -0.005)
|
||||
.map(([cur, amount]) => [cur, Math.abs(Number(amount))])
|
||||
)
|
||||
},
|
||||
hasOwesYouFiat(fiatBalances) {
|
||||
return Object.keys(this.owesYouFiat(fiatBalances)).length > 0
|
||||
},
|
||||
hasYouOweFiat(fiatBalances) {
|
||||
return Object.keys(this.youOweFiat(fiatBalances)).length > 0
|
||||
},
|
||||
formatFiat(amount, currency) {
|
||||
return new Intl.NumberFormat('en-US', {
|
||||
style: 'currency',
|
||||
|
|
|
|||
|
|
@ -187,16 +187,27 @@
|
|||
</template>
|
||||
<template v-slot:body-cell-balance="props">
|
||||
<q-td :props="props">
|
||||
<div :class="props.row.balance > 0 ? 'text-positive' : 'text-negative'">
|
||||
{% raw %}{{ formatSats(Math.abs(props.row.balance)) }} sats{% endraw %}
|
||||
<!-- User owes you (org), per currency -->
|
||||
<div v-if="hasOwesYouFiat(props.row.fiat_balances)" class="text-positive">
|
||||
<div v-for="(amount, currency) in owesYouFiat(props.row.fiat_balances)" :key="'oy-' + currency">
|
||||
Owes you {% raw %}{{ formatFiat(amount, currency) }}{% endraw %}
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="props.row.fiat_balances && Object.keys(props.row.fiat_balances).length > 0" class="text-caption">
|
||||
<span v-for="(amount, currency) in props.row.fiat_balances" :key="currency" class="q-mr-sm">
|
||||
{% raw %}{{ formatFiat(Math.abs(amount), currency) }}{% endraw %}
|
||||
</span>
|
||||
<!-- You (org) owe user, per currency -->
|
||||
<div v-if="hasYouOweFiat(props.row.fiat_balances)" class="text-negative">
|
||||
<div v-for="(amount, currency) in youOweFiat(props.row.fiat_balances)" :key="'yo-' + currency">
|
||||
You owe {% raw %}{{ formatFiat(amount, currency) }}{% endraw %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-caption text-grey">
|
||||
{% raw %}{{ props.row.balance > 0 ? 'Owes you' : 'You owe' }}{% endraw %}
|
||||
<!-- Fallback when there are no fiat balances (sats-only entries) -->
|
||||
<div v-if="!hasOwesYouFiat(props.row.fiat_balances) && !hasYouOweFiat(props.row.fiat_balances)"
|
||||
:class="props.row.balance > 0 ? 'text-positive' : 'text-negative'">
|
||||
{% raw %}{{ props.row.balance > 0 ? 'Owes you' : 'You owe' }} {{ formatSats(Math.abs(props.row.balance)) }} sats{% endraw %}
|
||||
</div>
|
||||
<!-- Net sats footnote (current-rate-derived; can't be netted across currencies) -->
|
||||
<div v-if="hasOwesYouFiat(props.row.fiat_balances) || hasYouOweFiat(props.row.fiat_balances)"
|
||||
class="text-caption text-grey q-mt-xs">
|
||||
Net (current rates): {% raw %}{{ formatSats(Math.abs(props.row.balance)) }} sats {{ props.row.balance > 0 ? '(receivable)' : '(payable)' }}{% endraw %}
|
||||
</div>
|
||||
</q-td>
|
||||
</template>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue