# 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: ```csv 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: ```python 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 ```bash export CASTLE_ADMIN_KEY="your_lnbits_admin_invoice_key" export LNBITS_URL="http://localhost:5000" # Optional ``` ## 📖 Usage ```bash 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:` account: ```beancount 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:` 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 ```bash $ 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:` account (e.g., `Equity:Pat`). ## 📝 Transaction Metadata Each imported transaction includes: ```json { "meta": { "source": "beancount_import", "imported_at": "2025-11-08T12:00:00", "btc_eur_rate": 87891.0, "user_id": "wallet_abc123" } } ``` And each line includes: ```json { "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.