From 0a7c39adcbe45824bbf2ee4a6e2f8ce9dcc443e7 Mon Sep 17 00:00:00 2001 From: Padreug Date: Sun, 17 May 2026 20:12:51 +0200 Subject: [PATCH] Show Outstanding Balances split 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 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). --- static/js/index.js | 25 +++++++++++++++++++++++++ templates/libra/index.html | 27 +++++++++++++++++++-------- 2 files changed, 44 insertions(+), 8 deletions(-) diff --git a/static/js/index.js b/static/js/index.js index 1f6ccb9..3905bf3 100644 --- a/static/js/index.js +++ b/static/js/index.js @@ -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', diff --git a/templates/libra/index.html b/templates/libra/index.html index 04e7688..01de17a 100644 --- a/templates/libra/index.html +++ b/templates/libra/index.html @@ -187,16 +187,27 @@