Outstanding Balances actions: per-currency settlement when directions diverge #19

Open
opened 2026-05-17 18:22:54 +00:00 by padreug · 0 comments
Owner

Summary

With the multi-currency per-direction display landed, the Outstanding Balances row can now correctly show that the same user simultaneously owes the org in some currencies and is owed by the org in others. Example (CircularPenguin from the test):

  • Owes you CA$200.00
  • Owes you £50.00
  • You owe €300.00
  • You owe ¥700.00
  • Net (current rates): 180,485 sats (payable)

The action column, however, still picks one button based on the net sats balance — in this case the "Pay user" (send) icon, because in sats terms the org is net-payable to the user. That's a reasonable headline action, but it loses the per-currency reality:

  • Admin can't trigger "collect from user CA$200" or "collect from user £50" from this row.
  • Admin can't trigger "pay user €300" separately from "pay user ¥700".
  • The single net-sats action paths into the existing /settle-receivable or /pay-user endpoints, which operate on a single currency at a time — so even invoking the visible button requires the dialog to disambiguate currency.

Proposed shape

Two reasonable options, both worth evaluating before implementing:

A. Two action buttons per row when both directions are present. Show the "Settle receivable" button if the user owes the org in any currency, and the "Pay user" button if the org owes the user in any currency. Each opens a dialog that lets the admin pick the currency to settle. With CircularPenguin's row, both buttons render.

B. Single "Settle…" menu button that opens a list. The row shows one icon; clicking it lists each non-zero per-currency line and lets the admin pick which to act on. Visually less cluttered, more clicks deep. Better when many users have multi-direction balances.

In either case the dialog flow should:

  1. Pre-fill the currency from the clicked row/line.
  2. Pre-fill the amount with the current balance in that currency (admin can settle partial).
  3. Use the existing POST /api/v1/settle-receivable and POST /api/v1/pay-user endpoints — those already accept currency and amount per call.

Backend touch (small)

The backend endpoints already work per-currency. What's missing on the data side is making the per-account/per-currency breakdown available to the row UI without an extra request — the /balances/all response already includes fiat_balances (signed dict) used by the new display, so the action handlers have everything they need. The accounts array is also returned per-user and would let an even more granular UI (e.g., "settle Receivable from this specific income entry") work without further API changes.

Open questions

  1. Do we model per-currency settlement as a single dialog with currency picker, or distinct dialogs per direction? (Affects UX consistency vs. dialog reuse.)
  2. When admin settles a receivable in one currency, should the row refresh just that line or the whole user row? (Affects optimistic UI vs. server-truth reload.)
  3. Does this interact with #16 (cross-user receivable settlement)? Probably yes — a User B who collected $200 of income (owes the org $200) settling User A's $200 debt is a single transfer with two-sided effect, and the UI for it should live in the same neighborhood as per-currency settlement.
  4. #18 (unit-of-account convention) sits upstream of all of this — if we eventually flip to bitcoin-first, settlement semantics change (FX gain/loss routing on every cross-rate settlement). For now this issue is fiat-first under the current convention.

Severity

Quality-of-life — the data is correct and visible; the friction is admin workflow. Today admin would have to use the per-currency endpoints directly via API (or wait for a multi-currency-aware dialog) to actually settle any one of the four divergent currencies on CircularPenguin's row.

Related: #16 (cross-user debt assumption), #18 (unit-of-account convention).

## Summary With the multi-currency per-direction display landed, the Outstanding Balances row can now correctly show that the same user simultaneously owes the org in some currencies and is owed by the org in others. Example (CircularPenguin from the test): - Owes you CA$200.00 - Owes you £50.00 - You owe €300.00 - You owe ¥700.00 - Net (current rates): 180,485 sats (payable) The action column, however, still picks **one** button based on the net sats balance — in this case the "Pay user" (send) icon, because in sats terms the org is net-payable to the user. That's a reasonable headline action, but it loses the per-currency reality: - Admin can't trigger "collect from user CA$200" or "collect from user £50" from this row. - Admin can't trigger "pay user €300" *separately from* "pay user ¥700". - The single net-sats action paths into the existing `/settle-receivable` or `/pay-user` endpoints, which operate on a single currency at a time — so even invoking the visible button requires the dialog to disambiguate currency. ## Proposed shape Two reasonable options, both worth evaluating before implementing: **A. Two action buttons per row when both directions are present.** Show the "Settle receivable" button if the user owes the org in any currency, and the "Pay user" button if the org owes the user in any currency. Each opens a dialog that lets the admin pick the currency to settle. With CircularPenguin's row, both buttons render. **B. Single "Settle…" menu button that opens a list.** The row shows one icon; clicking it lists each non-zero per-currency line and lets the admin pick which to act on. Visually less cluttered, more clicks deep. Better when many users have multi-direction balances. In either case the dialog flow should: 1. Pre-fill the currency from the clicked row/line. 2. Pre-fill the amount with the current balance in that currency (admin can settle partial). 3. Use the existing `POST /api/v1/settle-receivable` and `POST /api/v1/pay-user` endpoints — those already accept `currency` and `amount` per call. ## Backend touch (small) The backend endpoints already work per-currency. What's missing on the data side is making the per-account/per-currency breakdown available to the row UI without an extra request — the `/balances/all` response already includes `fiat_balances` (signed dict) used by the new display, so the action handlers have everything they need. The `accounts` array is also returned per-user and would let an even more granular UI (e.g., "settle Receivable from this specific income entry") work without further API changes. ## Open questions 1. Do we model per-currency settlement as a single dialog with currency picker, or distinct dialogs per direction? (Affects UX consistency vs. dialog reuse.) 2. When admin settles a receivable in one currency, should the row refresh just that line or the whole user row? (Affects optimistic UI vs. server-truth reload.) 3. Does this interact with #16 (cross-user receivable settlement)? Probably yes — a User B who collected $200 of income (owes the org $200) settling User A's $200 debt is a single transfer with two-sided effect, and the UI for it should live in the same neighborhood as per-currency settlement. 4. #18 (unit-of-account convention) sits upstream of all of this — if we eventually flip to bitcoin-first, settlement semantics change (FX gain/loss routing on every cross-rate settlement). For now this issue is fiat-first under the current convention. ## Severity Quality-of-life — the data is correct and visible; the friction is admin workflow. Today admin would have to use the per-currency endpoints directly via API (or wait for a multi-currency-aware dialog) to actually settle any one of the four divergent currencies on CircularPenguin's row. Related: #16 (cross-user debt assumption), #18 (unit-of-account convention).
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
aiolabs/libra#19
No description provided.