castle/helper
padreug a6b67b7416 Improves Beancount entry generation and sanitization
Adds a function to sanitize strings for use as Beancount links,
ensuring compatibility with Beancount's link restrictions.

Refactors the journal entry creation process to use EUR-based
postings when fiat currency is provided, improving accuracy
and consistency. The legacy SATS-based fallback is retained for
cases without fiat currency information.

Adjusts reference generation for Beancount entries using the
sanitized description.
2025-11-10 11:35:41 +01:00
..
btc_eur_rates.csv Adds Beancount import helper script 2025-11-08 23:18:42 +01:00
import_beancount.py Improves Beancount entry generation and sanitization 2025-11-10 11:35:41 +01:00
README.md Adds Beancount import helper script 2025-11-08 23:18:42 +01:00

Castle Beancount Import Helper

Import Beancount ledger transactions into Castle accounting extension.

📁 Files

  • import_beancount.py - Main import script
  • btc_eur_rates.csv - Daily BTC/EUR rates (create your own)
  • README.md - This file

🚀 Setup

1. Create BTC/EUR Rates CSV

Create btc_eur_rates.csv in this directory with your actual rates:

date,btc_eur_rate
2025-07-01,86500
2025-07-02,87200
2025-07-03,87450

2. Update User Mappings

Edit import_beancount.py and update the USER_MAPPINGS dictionary:

USER_MAPPINGS = {
    "Pat": "actual_wallet_id_for_pat",
    "Alice": "actual_wallet_id_for_alice",
    "Bob": "actual_wallet_id_for_bob",
}

How to get wallet IDs:

  • Check your LNbits admin panel
  • Or query: curl -X GET http://localhost:5000/api/v1/wallet -H "X-Api-Key: user_invoice_key"

3. Set API Key

export CASTLE_ADMIN_KEY="your_lnbits_admin_invoice_key"
export LNBITS_URL="http://localhost:5000"  # Optional

📖 Usage

cd /path/to/castle/helper

# Test with dry run
python import_beancount.py ledger.beancount --dry-run

# Actually import
python import_beancount.py ledger.beancount

📄 Beancount File Format

Your Beancount transactions must have an Equity:<name> account:

2025-07-06 * "Foix market"
  Expenses:Groceries              69.40 EUR
  Equity:Pat

2025-07-07 * "Gas station"
  Expenses:Transport              45.00 EUR
  Equity:Alice

Requirements:

  • Every transaction must have an Equity:<name> account
  • Account names must match exactly what's in Castle
  • The name after Equity: must be in USER_MAPPINGS

🔄 How It Works

  1. Loads rates from btc_eur_rates.csv
  2. Loads accounts from Castle API automatically
  3. Maps users - Extracts user name from Equity:Name accounts
  4. Parses Beancount transactions
  5. Converts EUR → sats using daily rate
  6. Uploads to Castle with metadata

📊 Example Output

$ python import_beancount.py ledger.beancount
======================================================================
🏰 Beancount to Castle Import Script
======================================================================

📊 Loaded 15 daily rates from btc_eur_rates.csv
   Date range: 2025-07-01 to 2025-07-15

🏦 Loaded 28 accounts from Castle

👥 User ID mappings:
   - Pat → wallet_abc123
   - Alice → wallet_def456
   - Bob → wallet_ghi789

📄 Found 25 potential transactions in ledger.beancount

✅ Transaction 1: 2025-07-06 - Foix market (User: Pat) (Rate: 87,891 EUR/BTC)
✅ Transaction 2: 2025-07-07 - Gas station (User: Alice) (Rate: 88,100 EUR/BTC)
✅ Transaction 3: 2025-07-08 - Restaurant (User: Bob) (Rate: 88,350 EUR/BTC)

======================================================================
📊 Summary: 25 succeeded, 0 failed, 0 skipped
======================================================================

✅ Successfully imported 25 transactions to Castle!

Troubleshooting

"No account found in Castle"

Error: No account found in Castle with name 'Expenses:XYZ'

Solution: Create the account in Castle first with that exact name.

"No user ID mapping found"

Error: No user ID mapping found for 'Pat'

Solution: Add Pat to the USER_MAPPINGS dictionary in the script.

"No BTC/EUR rate found"

Error: No BTC/EUR rate found for 2025-07-15

Solution: Add that date to btc_eur_rates.csv.

"Could not determine user ID"

Error: Could not determine user ID for transaction

Solution: Every transaction needs an Equity:<name> account (e.g., Equity:Pat).

📝 Transaction Metadata

Each imported transaction includes:

{
  "meta": {
    "source": "beancount_import",
    "imported_at": "2025-11-08T12:00:00",
    "btc_eur_rate": 87891.0,
    "user_id": "wallet_abc123"
  }
}

And each line includes:

{
  "metadata": {
    "fiat_currency": "EUR",
    "fiat_amount": "69.400",
    "fiat_rate": 1137.88,
    "btc_rate": 87891.0
  }
}

This preserves the original EUR amount and exchange rate for auditing.