diff --git a/models.py b/models.py index 8bcfb39..919b722 100644 --- a/models.py +++ b/models.py @@ -124,7 +124,7 @@ class CastleSettings(BaseModel): # Fava/Beancount integration - ALL accounting is done via Fava fava_url: str = "http://localhost:3333" # Base URL of Fava server - fava_ledger_slug: str = "castle-accounting" # Ledger identifier in Fava URL + fava_ledger_slug: str = "castle-ledger" # Ledger identifier in Fava URL fava_timeout: float = 10.0 # Request timeout in seconds updated_at: datetime = Field(default_factory=lambda: datetime.now()) diff --git a/views_api.py b/views_api.py index 7932eb9..cb4f4fb 100644 --- a/views_api.py +++ b/views_api.py @@ -235,9 +235,23 @@ async def api_get_account(account_id: str) -> Account: @castle_api_router.get("/api/v1/accounts/{account_id}/balance") async def api_get_account_balance(account_id: str) -> dict: - """Get account balance""" - balance = await get_account_balance(account_id) - return {"account_id": account_id, "balance": balance} + """Get account balance from Fava/Beancount""" + from .fava_client import get_fava_client + + # Get account to retrieve its name + account = await get_account(account_id) + if not account: + raise HTTPException(status_code=404, detail="Account not found") + + # Query Fava for balance + fava = get_fava_client() + balance_data = await fava.get_account_balance(account.name) + + return { + "account_id": account_id, + "balance": balance_data["sats"], # Balance in satoshis + "positions": balance_data["positions"] # Full Beancount positions with cost basis + } @castle_api_router.get("/api/v1/accounts/{account_id}/transactions") @@ -683,25 +697,28 @@ async def api_create_revenue_entry( async def api_get_my_balance( wallet: WalletTypeInfo = Depends(require_invoice_key), ) -> UserBalance: - """Get current user's balance with the Castle""" + """Get current user's balance with the Castle (from Fava/Beancount)""" from lnbits.settings import settings as lnbits_settings + from .fava_client import get_fava_client + + fava = get_fava_client() # If super user, show total castle position if wallet.wallet.user == lnbits_settings.super_user: - all_balances = await get_all_user_balances() + all_balances = await fava.get_all_user_balances() # Calculate total: # Positive balances = Castle owes users (liabilities) # Negative balances = Users owe Castle (receivables) # Net: positive means castle owes, negative means castle is owed - total_liabilities = sum(b.balance for b in all_balances if b.balance > 0) - total_receivables = sum(abs(b.balance) for b in all_balances if b.balance < 0) + total_liabilities = sum(b["balance"] for b in all_balances if b["balance"] > 0) + total_receivables = sum(abs(b["balance"]) for b in all_balances if b["balance"] < 0) net_balance = total_liabilities - total_receivables # Aggregate fiat balances from all users total_fiat_balances = {} for user_balance in all_balances: - for currency, amount in user_balance.fiat_balances.items(): + for currency, amount in user_balance["fiat_balances"].items(): if currency not in total_fiat_balances: total_fiat_balances[currency] = Decimal("0") # Add all balances (positive and negative) @@ -715,37 +732,56 @@ async def api_get_my_balance( fiat_balances=total_fiat_balances, ) - # For regular users, show their individual balance - return await get_user_balance(wallet.wallet.user) + # For regular users, show their individual balance from Fava + balance_data = await fava.get_user_balance(wallet.wallet.user) + + return UserBalance( + user_id=wallet.wallet.user, + balance=balance_data["balance"], + accounts=[], # Could populate from balance_data["accounts"] if needed + fiat_balances=balance_data["fiat_balances"], + ) @castle_api_router.get("/api/v1/balance/{user_id}") async def api_get_user_balance(user_id: str) -> UserBalance: - """Get a specific user's balance with the Castle""" - return await get_user_balance(user_id) + """Get a specific user's balance with the Castle (from Fava/Beancount)""" + from .fava_client import get_fava_client + + fava = get_fava_client() + balance_data = await fava.get_user_balance(user_id) + + return UserBalance( + user_id=user_id, + balance=balance_data["balance"], + accounts=[], + fiat_balances=balance_data["fiat_balances"], + ) @castle_api_router.get("/api/v1/balances/all") async def api_get_all_balances( wallet: WalletTypeInfo = Depends(require_admin_key), ) -> list[dict]: - """Get all user balances (admin/super user only)""" + """Get all user balances (admin/super user only) from Fava/Beancount""" from lnbits.core.crud.users import get_user + from .fava_client import get_fava_client - balances = await get_all_user_balances() + fava = get_fava_client() + balances = await fava.get_all_user_balances() # Enrich with username information result = [] for balance in balances: - user = await get_user(balance.user_id) - username = user.username if user and user.username else balance.user_id[:16] + "..." + user = await get_user(balance["user_id"]) + username = user.username if user and user.username else balance["user_id"][:16] + "..." result.append({ - "user_id": balance.user_id, + "user_id": balance["user_id"], "username": username, - "balance": balance.balance, - "fiat_balances": balance.fiat_balances, - "accounts": [acc.dict() for acc in balance.accounts], + "balance": balance["balance"], + "fiat_balances": balance["fiat_balances"], + "accounts": balance["accounts"], }) return result