Adds fiat currency support for expenses
Extends expense entry functionality to support fiat currencies. Users can now specify a currency (e.g., EUR, USD) when creating expense entries. The specified amount is converted to satoshis using exchange rates. The converted amount and currency information are stored in the journal entry metadata. Also adds an API endpoint to retrieve allowed currencies and updates the UI to allow currency selection when creating expense entries.
This commit is contained in:
parent
4bd83d6937
commit
cd083114b4
5 changed files with 97 additions and 10 deletions
44
views_api.py
44
views_api.py
|
|
@ -3,6 +3,7 @@ from http import HTTPStatus
|
|||
from fastapi import APIRouter, Depends, HTTPException
|
||||
from lnbits.core.models import WalletTypeInfo
|
||||
from lnbits.decorators import require_admin_key, require_invoice_key
|
||||
from lnbits.utils.exchange_rates import allowed_currencies, fiat_amount_as_satoshis
|
||||
|
||||
from .crud import (
|
||||
create_account,
|
||||
|
|
@ -34,6 +35,15 @@ from .models import (
|
|||
castle_api_router = APIRouter(prefix="/api/v1", tags=["castle"])
|
||||
|
||||
|
||||
# ===== UTILITY ENDPOINTS =====
|
||||
|
||||
|
||||
@castle_api_router.get("/currencies")
|
||||
async def api_get_currencies() -> list[str]:
|
||||
"""Get list of allowed currencies for fiat conversion"""
|
||||
return allowed_currencies()
|
||||
|
||||
|
||||
# ===== ACCOUNT ENDPOINTS =====
|
||||
|
||||
|
||||
|
|
@ -136,7 +146,32 @@ async def api_create_expense_entry(
|
|||
Create an expense entry for a user.
|
||||
If is_equity=True, records as equity contribution.
|
||||
If is_equity=False, records as liability (castle owes user).
|
||||
|
||||
If currency is provided, amount is converted from fiat to satoshis.
|
||||
"""
|
||||
# Handle currency conversion
|
||||
amount_sats = int(data.amount)
|
||||
metadata = {}
|
||||
|
||||
if data.currency:
|
||||
# Validate currency
|
||||
if data.currency.upper() not in allowed_currencies():
|
||||
raise HTTPException(
|
||||
status_code=HTTPStatus.BAD_REQUEST,
|
||||
detail=f"Currency '{data.currency}' not allowed. Use one of: {', '.join(allowed_currencies())}",
|
||||
)
|
||||
|
||||
# Convert fiat to satoshis
|
||||
amount_sats = await fiat_amount_as_satoshis(data.amount, data.currency)
|
||||
|
||||
# Store currency metadata
|
||||
metadata = {
|
||||
"fiat_currency": data.currency.upper(),
|
||||
"fiat_amount": round(data.amount, ndigits=3),
|
||||
"fiat_rate": amount_sats / data.amount if data.amount > 0 else 0,
|
||||
"btc_rate": (data.amount / amount_sats * 100_000_000) if amount_sats > 0 else 0,
|
||||
}
|
||||
|
||||
# Get or create expense account
|
||||
expense_account = await get_account_by_name(data.expense_account)
|
||||
if not expense_account:
|
||||
|
|
@ -162,21 +197,24 @@ async def api_create_expense_entry(
|
|||
|
||||
# Create journal entry
|
||||
# DR Expense, CR User Account (Liability or Equity)
|
||||
description_suffix = f" ({metadata['fiat_amount']} {metadata['fiat_currency']})" if metadata else ""
|
||||
entry_data = CreateJournalEntry(
|
||||
description=data.description,
|
||||
description=data.description + description_suffix,
|
||||
reference=data.reference,
|
||||
lines=[
|
||||
CreateEntryLine(
|
||||
account_id=expense_account.id,
|
||||
debit=data.amount,
|
||||
debit=amount_sats,
|
||||
credit=0,
|
||||
description=f"Expense paid by user {data.user_wallet[:8]}",
|
||||
metadata=metadata,
|
||||
),
|
||||
CreateEntryLine(
|
||||
account_id=user_account.id,
|
||||
debit=0,
|
||||
credit=data.amount,
|
||||
credit=amount_sats,
|
||||
description=f"{'Equity contribution' if data.is_equity else 'Amount owed to user'}",
|
||||
metadata=metadata,
|
||||
),
|
||||
],
|
||||
)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue