Enhances Beancount import and API entry creation
Improves the Beancount import process to send SATS amounts with fiat metadata to the API, enabling automatic conversion to EUR-based postings. Updates the API to store entries in Fava instead of the Castle DB, simplifying the JournalEntry creation process. Adds error handling to the upload entry function. Includes a note about imported transactions being stored in EUR with SATS in metadata.
This commit is contained in:
parent
1d605be021
commit
0e93fc5ffc
2 changed files with 41 additions and 50 deletions
|
|
@ -245,18 +245,19 @@ def eur_to_sats(eur_amount: Decimal, btc_eur_rate: float) -> int:
|
|||
return int(sats.quantize(Decimal('1')))
|
||||
|
||||
def build_metadata(eur_amount: Decimal, btc_eur_rate: float) -> dict:
|
||||
"""Build metadata dict for Castle entry line"""
|
||||
"""
|
||||
Build metadata dict for Castle entry line.
|
||||
|
||||
The API will extract fiat_currency and fiat_amount and use them
|
||||
to create proper EUR-based postings with SATS in metadata.
|
||||
"""
|
||||
abs_eur = abs(eur_amount)
|
||||
abs_sats = abs(eur_to_sats(abs_eur, btc_eur_rate))
|
||||
|
||||
# fiat_rate = sats per EUR
|
||||
fiat_rate = float(abs_sats) / float(abs_eur) if abs_eur > 0 else 0
|
||||
|
||||
return {
|
||||
"fiat_currency": "EUR",
|
||||
"fiat_amount": str(abs_eur.quantize(Decimal("0.001"))),
|
||||
"fiat_rate": fiat_rate,
|
||||
"btc_rate": btc_eur_rate
|
||||
"fiat_amount": str(abs_eur.quantize(Decimal("0.01"))), # Store as string for JSON
|
||||
"btc_rate": str(btc_eur_rate) # Store exchange rate for reference
|
||||
}
|
||||
|
||||
# ===== BEANCOUNT PARSER =====
|
||||
|
|
@ -407,7 +408,12 @@ def determine_user_id(postings: list) -> Optional[str]:
|
|||
# ===== CASTLE CONVERTER =====
|
||||
|
||||
def convert_to_castle_entry(parsed: dict, btc_eur_rate: float, account_lookup: AccountLookup) -> dict:
|
||||
"""Convert parsed Beancount transaction to Castle format"""
|
||||
"""
|
||||
Convert parsed Beancount transaction to Castle format.
|
||||
|
||||
Sends SATS amounts with fiat metadata. The Castle API will automatically
|
||||
convert to EUR-based postings with SATS stored in metadata.
|
||||
"""
|
||||
|
||||
# Determine which user this transaction is for (based on user-specific accounts)
|
||||
user_id = determine_user_id(parsed['postings'])
|
||||
|
|
@ -435,10 +441,10 @@ def convert_to_castle_entry(parsed: dict, btc_eur_rate: float, account_lookup: A
|
|||
if eur_amount is None:
|
||||
raise ValueError(f"Could not determine amount for {posting['account']}")
|
||||
|
||||
# Convert EUR to sats
|
||||
# Convert EUR to sats (amount sent to API)
|
||||
sats = eur_to_sats(eur_amount, btc_eur_rate)
|
||||
|
||||
# Build metadata
|
||||
# Build metadata (API will extract fiat_currency and fiat_amount)
|
||||
metadata = build_metadata(eur_amount, btc_eur_rate)
|
||||
|
||||
lines.append({
|
||||
|
|
@ -456,7 +462,7 @@ def convert_to_castle_entry(parsed: dict, btc_eur_rate: float, account_lookup: A
|
|||
"meta": {
|
||||
"source": "beancount_import",
|
||||
"imported_at": datetime.now().isoformat(),
|
||||
"btc_eur_rate": btc_eur_rate,
|
||||
"btc_eur_rate": str(btc_eur_rate),
|
||||
"user_id": user_id # Track which user this transaction is for
|
||||
},
|
||||
"lines": lines
|
||||
|
|
@ -486,9 +492,18 @@ def upload_entry(entry: dict, api_key: str, dry_run: bool = False) -> dict:
|
|||
"Content-Type": "application/json"
|
||||
}
|
||||
|
||||
response = requests.post(url, json=entry, headers=headers)
|
||||
response.raise_for_status()
|
||||
return response.json()
|
||||
try:
|
||||
response = requests.post(url, json=entry, headers=headers)
|
||||
response.raise_for_status()
|
||||
return response.json()
|
||||
except requests.exceptions.HTTPError as e:
|
||||
print(f" ❌ HTTP Error: {e}")
|
||||
if response.text:
|
||||
print(f" Response: {response.text}")
|
||||
raise
|
||||
except Exception as e:
|
||||
print(f" ❌ Error: {e}")
|
||||
raise
|
||||
|
||||
# ===== MAIN IMPORT FUNCTION =====
|
||||
|
||||
|
|
@ -590,6 +605,8 @@ def import_beancount_file(beancount_file: str, dry_run: bool = False):
|
|||
|
||||
if success_count > 0 and not dry_run:
|
||||
print(f"\n✅ Successfully imported {success_count} transactions to Castle!")
|
||||
print(f"\n💡 Note: Transactions are stored in EUR with SATS in metadata.")
|
||||
print(f" Check Fava to see the imported entries.")
|
||||
|
||||
# ===== MAIN =====
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue