Creates accounts in Fava if they don't exist
This change ensures that user-specific accounts are automatically created in the Fava/Beancount ledger when they are first requested. It checks for the existence of the account via a Fava query and creates it via an Open directive if it's missing. This simplifies account management and ensures that all necessary accounts are available for transactions. This implementation adds a new `add_account` method to the `FavaClient` class which makes use of the /add_entries endpoint to create an account using an Open Directive.
This commit is contained in:
parent
51ae2e8e47
commit
b6886793ee
2 changed files with 113 additions and 2 deletions
|
|
@ -6,9 +6,11 @@ All accounting logic is delegated to Fava/Beancount.
|
|||
|
||||
Fava provides a REST API for:
|
||||
- Adding transactions (PUT /api/add_entries)
|
||||
- Adding accounts via Open directives (PUT /api/add_entries)
|
||||
- Querying balances (GET /api/query)
|
||||
- Balance sheets (GET /api/balance_sheet)
|
||||
- Account reports (GET /api/account_report)
|
||||
- Updating/deleting entries (PUT/DELETE /api/source_slice)
|
||||
|
||||
See: https://github.com/beancount/fava/blob/main/src/fava/json_api.py
|
||||
"""
|
||||
|
|
@ -679,6 +681,73 @@ class FavaClient:
|
|||
logger.error(f"Fava connection error: {e}")
|
||||
raise
|
||||
|
||||
async def add_account(
|
||||
self,
|
||||
account_name: str,
|
||||
currencies: list[str],
|
||||
opening_date: Optional[date] = None,
|
||||
metadata: Optional[Dict[str, Any]] = None
|
||||
) -> Dict[str, Any]:
|
||||
"""
|
||||
Add an account to the Beancount ledger via an Open directive.
|
||||
|
||||
Args:
|
||||
account_name: Full account name (e.g., "Assets:Receivable:User-abc123")
|
||||
currencies: List of currencies for this account (e.g., ["EUR", "SATS"])
|
||||
opening_date: Date to open the account (defaults to today)
|
||||
metadata: Optional metadata for the account
|
||||
|
||||
Returns:
|
||||
Response from Fava ({"data": "Stored 1 entries.", "mtime": "..."})
|
||||
|
||||
Example:
|
||||
# Add a user's receivable account
|
||||
result = await fava.add_account(
|
||||
account_name="Assets:Receivable:User-abc123",
|
||||
currencies=["EUR", "SATS", "USD"],
|
||||
metadata={"user_id": "abc123", "description": "User receivables"}
|
||||
)
|
||||
|
||||
# Add a user's payable account
|
||||
result = await fava.add_account(
|
||||
account_name="Liabilities:Payable:User-abc123",
|
||||
currencies=["EUR", "SATS"]
|
||||
)
|
||||
"""
|
||||
from datetime import date as date_type
|
||||
|
||||
if opening_date is None:
|
||||
opening_date = date_type.today()
|
||||
|
||||
# Format Open directive for Fava
|
||||
open_directive = {
|
||||
"t": "Open",
|
||||
"date": opening_date.isoformat(),
|
||||
"account": account_name,
|
||||
"currencies": currencies,
|
||||
"meta": metadata or {}
|
||||
}
|
||||
|
||||
try:
|
||||
async with httpx.AsyncClient(timeout=self.timeout) as client:
|
||||
response = await client.put(
|
||||
f"{self.base_url}/add_entries",
|
||||
json={"entries": [open_directive]},
|
||||
headers={"Content-Type": "application/json"}
|
||||
)
|
||||
response.raise_for_status()
|
||||
result = response.json()
|
||||
|
||||
logger.info(f"Added account {account_name} to Fava with currencies {currencies}")
|
||||
return result
|
||||
|
||||
except httpx.HTTPStatusError as e:
|
||||
logger.error(f"Fava HTTP error adding account: {e.response.status_code} - {e.response.text}")
|
||||
raise
|
||||
except httpx.RequestError as e:
|
||||
logger.error(f"Fava connection error: {e}")
|
||||
raise
|
||||
|
||||
|
||||
# Singleton instance (configured from settings)
|
||||
_fava_client: Optional[FavaClient] = None
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue