Implements a script to import Beancount ledger transactions into the Castle accounting extension. The script fetches BTC/EUR rates, retrieves accounts from the Castle API, maps users, parses Beancount transactions, converts EUR to sats, and uploads the data to Castle. Adds error handling, dry-run mode, and detailed logging for improved usability. Displays equity account status and validates the existence of user equity accounts.
168 lines
4.1 KiB
Markdown
168 lines
4.1 KiB
Markdown
# 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:<name>` 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:<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
|
|
|
|
```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:<name>` 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.
|