Adds Fava integration for journal entries

Integrates Fava/Beancount for managing journal entries.

This change introduces functions to format entries into Beancount
format and submit them to a Fava instance.

It replaces the previous direct database entry creation with Fava
submission for expense, receivable, and revenue entries. The existing
create_journal_entry function is also updated to submit generic
journal entries to Fava.
This commit is contained in:
padreug 2025-11-09 22:56:56 +01:00
parent a88d7b4ea0
commit e3acc53e20
2 changed files with 411 additions and 110 deletions

View file

@ -462,3 +462,86 @@ def format_payment_entry(
links=links,
meta=entry_meta
)
def format_revenue_entry(
payment_account: str,
revenue_account: str,
amount_sats: int,
description: str,
entry_date: date,
fiat_currency: Optional[str] = None,
fiat_amount: Optional[Decimal] = None,
reference: Optional[str] = None
) -> Dict[str, Any]:
"""
Format a revenue entry (castle receives payment directly).
Creates a cleared transaction (flag="*") since payment was received.
Example: Cash sale, Lightning payment received, bank transfer received.
Args:
payment_account: Payment method account (e.g., "Assets:Bitcoin:Lightning", "Assets:Cash")
revenue_account: Revenue account name (e.g., "Income:Sales", "Income:Services")
amount_sats: Amount in satoshis (unsigned)
description: Entry description
entry_date: Date of payment
fiat_currency: Optional fiat currency
fiat_amount: Optional fiat amount (unsigned)
reference: Optional reference
Returns:
Fava API entry dict
Example:
entry = format_revenue_entry(
payment_account="Assets:Cash",
revenue_account="Income:Sales",
amount_sats=100000,
description="Product sale",
entry_date=date.today(),
fiat_currency="EUR",
fiat_amount=Decimal("50.00")
)
"""
amount_sats_abs = abs(amount_sats)
fiat_amount_abs = abs(fiat_amount) if fiat_amount else None
narration = description
if fiat_currency and fiat_amount_abs:
narration += f" ({fiat_amount_abs:.2f} {fiat_currency})"
postings = [
format_posting_with_cost(
account=payment_account,
amount_sats=amount_sats_abs, # Positive = debit (asset increase)
fiat_currency=fiat_currency,
fiat_amount=fiat_amount_abs
),
format_posting_with_cost(
account=revenue_account,
amount_sats=-amount_sats_abs, # Negative = credit (revenue increase)
fiat_currency=fiat_currency,
fiat_amount=fiat_amount_abs
)
]
entry_meta = {
"source": "castle-api",
"created-via": "revenue_entry"
}
links = []
if reference:
links.append(reference)
return format_transaction(
date_val=entry_date,
flag="*", # Cleared (payment received)
narration=narration,
postings=postings,
tags=["revenue-entry"],
links=links,
meta=entry_meta
)