diff --git a/fava_client.py b/fava_client.py index 8dcf31c..c61e1d9 100644 --- a/fava_client.py +++ b/fava_client.py @@ -820,10 +820,15 @@ class FavaClient: # GROUP BY currency prevents mixing EUR and SATS face values in sum(number). # sum(weight) gives SATS for both EUR @@ SATS entries and plain SATS entries. # sum(number) on EUR rows gives the fiat amount; on SATS rows gives sats paid. + # Credit is the overpay-absorbing liability per libra-#41 — it lives + # on the same per-user namespace as Payable and contributes to the + # user's net obligation with the same sign as Payable (negative on + # Liabilities means libra owes user). Folding it into the same query + # means the displayed net always already accounts for credit. query = f""" SELECT account, currency, sum(number), sum(weight) WHERE account ~ ':User-{user_id_prefix}' - AND (account ~ 'Payable' OR account ~ 'Receivable') + AND (account ~ 'Payable' OR account ~ 'Receivable' OR account ~ 'Credit') AND flag = '*' GROUP BY account, currency """ @@ -970,10 +975,11 @@ class FavaClient: """ from decimal import Decimal - # GROUP BY currency prevents mixing EUR and SATS face values in sum(number) + # GROUP BY currency prevents mixing EUR and SATS face values in sum(number). + # Credit per libra-#41 — see get_user_balance_bql for the rationale. query = """ SELECT account, currency, sum(number), sum(weight) - WHERE (account ~ 'Payable:User-' OR account ~ 'Receivable:User-') + WHERE (account ~ 'Payable:User-' OR account ~ 'Receivable:User-' OR account ~ 'Credit:User-') AND flag = '*' GROUP BY account, currency """ diff --git a/models.py b/models.py index 70abca4..8be64c9 100644 --- a/models.py +++ b/models.py @@ -96,6 +96,11 @@ class UserBalance(BaseModel): user_id: str balance: int # positive = libra owes user, negative = user owes libra accounts: list[Account] = [] + # Per-account breakdown surfaced from get_user_balance_bql so UIs (libra + # extension dashboard + webapp) can render Payable / Receivable / Credit + # as distinct line items. Each entry: {"account": str, "sats": int, + # "eur": Decimal}. Wired up for libra-#41's display contract. + account_balances: list[dict] = [] fiat_balances: dict[str, Decimal] = {} # e.g. {"EUR": Decimal("250.0"), "USD": Decimal("100.0")} # Lifetime totals (original entries only; not net of reconciliation) total_expenses_sats: int = 0 diff --git a/views_api.py b/views_api.py index 2f41cec..032c15d 100644 --- a/views_api.py +++ b/views_api.py @@ -1609,7 +1609,8 @@ async def api_get_my_balance( return UserBalance( user_id=wallet.wallet.user, balance=balance_data["balance"], - accounts=[], # Could populate from balance_data["accounts"] if needed + accounts=[], + account_balances=balance_data.get("accounts", []), fiat_balances=balance_data["fiat_balances"], total_expenses_sats=totals["total_expenses_sats"], total_expenses_fiat=totals["total_expenses_fiat"],