Fetches account balances from Fava/Beancount
Refactors account balance retrieval to fetch data from Fava/Beancount for improved accounting accuracy. Updates user balance retrieval to use Fava/Beancount data source. Updates Castle settings ledger slug name.
This commit is contained in:
parent
ff27f7ba01
commit
a88d7b4ea0
2 changed files with 57 additions and 21 deletions
|
|
@ -124,7 +124,7 @@ class CastleSettings(BaseModel):
|
||||||
|
|
||||||
# Fava/Beancount integration - ALL accounting is done via Fava
|
# Fava/Beancount integration - ALL accounting is done via Fava
|
||||||
fava_url: str = "http://localhost:3333" # Base URL of Fava server
|
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
|
fava_timeout: float = 10.0 # Request timeout in seconds
|
||||||
|
|
||||||
updated_at: datetime = Field(default_factory=lambda: datetime.now())
|
updated_at: datetime = Field(default_factory=lambda: datetime.now())
|
||||||
|
|
|
||||||
76
views_api.py
76
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")
|
@castle_api_router.get("/api/v1/accounts/{account_id}/balance")
|
||||||
async def api_get_account_balance(account_id: str) -> dict:
|
async def api_get_account_balance(account_id: str) -> dict:
|
||||||
"""Get account balance"""
|
"""Get account balance from Fava/Beancount"""
|
||||||
balance = await get_account_balance(account_id)
|
from .fava_client import get_fava_client
|
||||||
return {"account_id": account_id, "balance": balance}
|
|
||||||
|
# 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")
|
@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(
|
async def api_get_my_balance(
|
||||||
wallet: WalletTypeInfo = Depends(require_invoice_key),
|
wallet: WalletTypeInfo = Depends(require_invoice_key),
|
||||||
) -> UserBalance:
|
) -> 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 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 super user, show total castle position
|
||||||
if wallet.wallet.user == lnbits_settings.super_user:
|
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:
|
# Calculate total:
|
||||||
# Positive balances = Castle owes users (liabilities)
|
# Positive balances = Castle owes users (liabilities)
|
||||||
# Negative balances = Users owe Castle (receivables)
|
# Negative balances = Users owe Castle (receivables)
|
||||||
# Net: positive means castle owes, negative means castle is owed
|
# 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_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_receivables = sum(abs(b["balance"]) for b in all_balances if b["balance"] < 0)
|
||||||
net_balance = total_liabilities - total_receivables
|
net_balance = total_liabilities - total_receivables
|
||||||
|
|
||||||
# Aggregate fiat balances from all users
|
# Aggregate fiat balances from all users
|
||||||
total_fiat_balances = {}
|
total_fiat_balances = {}
|
||||||
for user_balance in all_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:
|
if currency not in total_fiat_balances:
|
||||||
total_fiat_balances[currency] = Decimal("0")
|
total_fiat_balances[currency] = Decimal("0")
|
||||||
# Add all balances (positive and negative)
|
# Add all balances (positive and negative)
|
||||||
|
|
@ -715,37 +732,56 @@ async def api_get_my_balance(
|
||||||
fiat_balances=total_fiat_balances,
|
fiat_balances=total_fiat_balances,
|
||||||
)
|
)
|
||||||
|
|
||||||
# For regular users, show their individual balance
|
# For regular users, show their individual balance from Fava
|
||||||
return await get_user_balance(wallet.wallet.user)
|
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}")
|
@castle_api_router.get("/api/v1/balance/{user_id}")
|
||||||
async def api_get_user_balance(user_id: str) -> UserBalance:
|
async def api_get_user_balance(user_id: str) -> UserBalance:
|
||||||
"""Get a specific user's balance with the Castle"""
|
"""Get a specific user's balance with the Castle (from Fava/Beancount)"""
|
||||||
return await get_user_balance(user_id)
|
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")
|
@castle_api_router.get("/api/v1/balances/all")
|
||||||
async def api_get_all_balances(
|
async def api_get_all_balances(
|
||||||
wallet: WalletTypeInfo = Depends(require_admin_key),
|
wallet: WalletTypeInfo = Depends(require_admin_key),
|
||||||
) -> list[dict]:
|
) -> 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 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
|
# Enrich with username information
|
||||||
result = []
|
result = []
|
||||||
for balance in balances:
|
for balance in balances:
|
||||||
user = await get_user(balance.user_id)
|
user = await get_user(balance["user_id"])
|
||||||
username = user.username if user and user.username else balance.user_id[:16] + "..."
|
username = user.username if user and user.username else balance["user_id"][:16] + "..."
|
||||||
|
|
||||||
result.append({
|
result.append({
|
||||||
"user_id": balance.user_id,
|
"user_id": balance["user_id"],
|
||||||
"username": username,
|
"username": username,
|
||||||
"balance": balance.balance,
|
"balance": balance["balance"],
|
||||||
"fiat_balances": balance.fiat_balances,
|
"fiat_balances": balance["fiat_balances"],
|
||||||
"accounts": [acc.dict() for acc in balance.accounts],
|
"accounts": balance["accounts"],
|
||||||
})
|
})
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue