From 124cad124926363cfed6f32b3e5c5af53a653404 Mon Sep 17 00:00:00 2001 From: Padreug Date: Sun, 17 May 2026 20:15:01 +0200 Subject: [PATCH] feat(libra/balance): split Net Balance card per direction per currency MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 Net Balance hero collapsed both into a single "You owe" / "Owed to you" label driven by the net sats. The fiat amounts were displayed via Math.abs(), hiding the per-currency signs the backend already returns, so the hero 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 sections — "You owe" with the user-owes currencies, "Owed to you" with the libra-owes currencies — using new youOweFiatEntries / libraOwesFiatEntries computeds that filter the signed fiat_balances dict by sign. Net sats moves to a small caption labelled "Net at current rates", since sats can be netted but distinct fiat currencies can't without a spot rate. Falls back to the old single-amount sats display when there are no fiat balances. --- src/accounting-app/views/BalancePage.vue | 84 +++++++++++++++++++----- 1 file changed, 67 insertions(+), 17 deletions(-) diff --git a/src/accounting-app/views/BalancePage.vue b/src/accounting-app/views/BalancePage.vue index 93ce27f..1335021 100644 --- a/src/accounting-app/views/BalancePage.vue +++ b/src/accounting-app/views/BalancePage.vue @@ -25,8 +25,24 @@ const totalIncomeFiat = ref>({}) const pendingTransactions = ref([]) const isLoading = ref(true) -const fiatBalanceEntries = computed(() => - Object.entries(fiatBalances.value).filter(([, amount]) => Math.abs(amount) > 0.005) +// Per-currency split: sign convention from the user perspective: +// positive fiat_balance = user owes Libra, negative = Libra owes user. +// Distinct currencies can't be netted across each other (no spot rate), +// so we render them grouped by direction instead of one collapsed label. +const youOweFiatEntries = computed(() => + Object.entries(fiatBalances.value) + .filter(([, amount]) => amount > 0.005) + .map(([currency, amount]) => [currency, amount] as [string, number]) +) + +const libraOwesFiatEntries = computed(() => + Object.entries(fiatBalances.value) + .filter(([, amount]) => amount < -0.005) + .map(([currency, amount]) => [currency, Math.abs(amount)] as [string, number]) +) + +const hasAnyFiatBalance = computed(() => + youOweFiatEntries.value.length > 0 || libraOwesFiatEntries.value.length > 0 ) const expensesFiatEntries = computed(() => @@ -126,30 +142,64 @@ function formatFiat(amount: number, currency: string): string {

{{ t('libra.balance.netBalance') }}

-
-
+
+ +
+
+

+ {{ t('libra.balance.youOwe') }} +

+
+ + {{ formatFiat(amount, currency) }} + +
+
+
+

+ {{ t('libra.balance.owedToYou') }} +

+
+ + {{ formatFiat(amount, currency) }} + +
+
+
+ + +
- - {{ formatAmount(balance) }} - + {{ formatAmount(balance) }} {{ balanceCurrency }}
-
- - {{ formatFiat(Math.abs(amount), currency) }} + + +
+ + + Net at current rates: {{ formatAmount(balance) }} {{ balanceCurrency }} + ({{ libraOwesUser ? t('libra.balance.owedToYou').toLowerCase() : t('libra.balance.youOwe').toLowerCase() }})
-

- {{ libraOwesUser ? t('libra.balance.owedToYou') : t('libra.balance.youOwe') }} -