# CLAUDE.md This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. ## Project Overview Castle Accounting is a double-entry bookkeeping extension for LNbits that enables collectives (co-living spaces, makerspaces, community projects) to track finances with proper accounting principles. It integrates Lightning Network payments with traditional accounting, supporting both cryptocurrency and fiat currency tracking. ## Architecture ### Core Design Principles **Double-Entry Accounting**: Every transaction affects at least two accounts. Debits must equal credits. Five account types: Assets, Liabilities, Equity, Revenue (Income), Expenses. **Pure Functional Core**: The `core/` directory contains pure accounting logic independent of the database layer: - `core/balance.py` - Balance calculation from journal entries - `core/inventory.py` - Multi-currency position tracking (similar to Beancount's Inventory) - `core/validation.py` - Entry validation rules **Account Hierarchy**: Beancount-style hierarchical naming with `:` separators: - `Assets:Lightning:Balance` - `Assets:Receivable:User-af983632` - `Liabilities:Payable:User-af983632` - `Expenses:Food:Supplies` **Metadata System**: Each `entry_line` stores JSON metadata preserving original fiat amounts. Critical: fiat balances are calculated by summing `fiat_amount` from metadata, NOT by converting current satoshi balances. This prevents exchange rate fluctuations from affecting historical records. ### Key Files - `models.py` - Pydantic models for API I/O and data structures - `crud.py` - Database operations (create/read/update accounts, journal entries) - `views_api.py` - FastAPI endpoints for all operations - `views.py` - Web interface routing - `services.py` - Settings management layer - `migrations.py` - Database schema migrations - `tasks.py` - Background tasks (daily reconciliation checks) - `account_utils.py` - Hierarchical account naming utilities ### Database Schema **accounts**: Chart of accounts with hierarchical names - `user_id` field for per-user accounts (Receivable, Payable, Equity) - Indexed on `user_id` and `account_type` **journal_entries**: Transaction headers - `flag` field: `*` (cleared), `!` (pending), `#` (flagged), `x` (void) - `meta` field: JSON storing source, tags, audit info - `reference` field: Links to payment_hash, invoice numbers, etc. **entry_lines**: Individual debit/credit lines - Always balanced (sum of debits = sum of credits per entry) - `metadata` field stores fiat currency info as JSON - Indexed on `journal_entry_id` and `account_id` **balance_assertions**: Reconciliation checkpoints (Beancount-style) - Assert expected balance at a date - Status: pending, passed, failed - Used for daily reconciliation checks **extension_settings**: Castle wallet configuration (admin-only) **user_wallet_settings**: Per-user wallet configuration **manual_payment_requests**: User requests for cash/manual payments ## Transaction Flows ### User Adds Expense (Liability) User pays cash for groceries, Castle owes them: ``` DR Expenses:Food 39,669 sats CR Liabilities:Payable:User-af983632 39,669 sats ``` Metadata preserves: `{"fiat_currency": "EUR", "fiat_amount": "36.93", "fiat_rate": "1074.192"}` ### Castle Adds Receivable User owes Castle for accommodation: ``` DR Assets:Receivable:User-af983632 268,548 sats CR Income:Accommodation 268,548 sats ``` ### User Pays with Lightning Invoice generated on **Castle's wallet** (not user's). After payment: ``` DR Assets:Lightning:Balance 268,548 sats CR Assets:Receivable:User-af983632 268,548 sats ``` ### Manual Payment Approval User requests cash payment → Admin approves → Journal entry created: ``` DR Liabilities:Payable:User-af983632 39,669 sats CR Assets:Lightning:Balance 39,669 sats ``` ## Balance Calculation Logic **User Balance**: - Positive = Castle owes user (LIABILITY accounts have credit balance) - Negative = User owes Castle (ASSET accounts have debit balance) - Calculated from sum of all entry lines across user's accounts - Fiat balances summed from metadata, NOT converted from sats **Perspective-Based UI**: - **User View**: Green = Castle owes them, Red = They owe Castle - **Castle Admin View**: Green = User owes Castle, Red = Castle owes user ## API Endpoints ### Accounts - `GET /api/v1/accounts` - List all accounts - `POST /api/v1/accounts` - Create account (admin) - `GET /api/v1/accounts/{id}/balance` - Get account balance ### Journal Entries - `POST /api/v1/entries/expense` - User adds expense (creates liability or equity) - `POST /api/v1/entries/receivable` - Admin records what user owes (admin only) - `POST /api/v1/entries/revenue` - Admin records direct revenue (admin only) - `GET /api/v1/entries/user` - Get user's journal entries - `POST /api/v1/entries` - Create raw journal entry (admin only) ### Payments & Balances - `GET /api/v1/balance` - Get user balance (or Castle total if super user) - `GET /api/v1/balances/all` - Get all user balances (admin, enriched with usernames) - `POST /api/v1/generate-payment-invoice` - Generate invoice for user to pay Castle - `POST /api/v1/record-payment` - Record Lightning payment from user to Castle - `POST /api/v1/settle-receivable` - Manually settle receivable (cash/bank) - `POST /api/v1/pay-user` - Castle pays user (cash/bank/lightning) ### Manual Payment Requests - `POST /api/v1/manual-payment-requests` - User requests payment - `GET /api/v1/manual-payment-requests` - User's requests - `GET /api/v1/manual-payment-requests/all` - All requests (admin) - `POST /api/v1/manual-payment-requests/{id}/approve` - Approve (admin) - `POST /api/v1/manual-payment-requests/{id}/reject` - Reject (admin) ### Reconciliation - `POST /api/v1/assertions/balance` - Create balance assertion - `GET /api/v1/assertions/balance` - List balance assertions - `POST /api/v1/assertions/balance/{id}/check` - Check assertion - `POST /api/v1/tasks/daily-reconciliation` - Run daily reconciliation (admin) ### Settings - `GET /api/v1/settings` - Get Castle settings (super user) - `PUT /api/v1/settings` - Update Castle settings (super user) - `GET /api/v1/user/wallet` - Get user wallet settings - `PUT /api/v1/user/wallet` - Update user wallet settings ## Development Notes ### Testing Entry Creation When creating journal entries programmatically, use the helper endpoints: - `POST /api/v1/entries/expense` for user expenses (handles account creation automatically) - `POST /api/v1/entries/receivable` for what users owe - `POST /api/v1/entries/revenue` for direct revenue For custom entries, use `POST /api/v1/entries` with properly balanced lines. ### User Account Management User-specific accounts are created automatically with format: - Assets: `Assets:Receivable:User-{user_id[:8]}` - Liabilities: `Liabilities:Payable:User-{user_id[:8]}` - Equity: `Equity:MemberEquity:User-{user_id[:8]}` Use `get_or_create_user_account()` in crud.py to ensure consistency. ### Currency Handling **CRITICAL**: Use `Decimal` for all fiat amounts, never `float`. Fiat amounts are stored in metadata as strings to preserve precision: ```python from decimal import Decimal metadata = { "fiat_currency": "EUR", "fiat_amount": str(Decimal("250.00")), "fiat_rate": str(Decimal("1074.192")), "btc_rate": str(Decimal("0.000931")) } ``` When reading: `fiat_amount = Decimal(metadata["fiat_amount"])` ### Balance Assertions for Reconciliation Create balance assertions to verify accounting accuracy: ```python await create_balance_assertion( account_id="lightning_account_id", expected_balance_sats=1000000, expected_balance_fiat=Decimal("500.00"), fiat_currency="EUR", tolerance_sats=100 ) ``` Run `POST /api/v1/tasks/daily-reconciliation` to check all assertions. ### Permission Model - **Super User**: Full access (check via `wallet.wallet.user == lnbits_settings.super_user`) - **Admin Key**: Required for creating receivables, approving payments, viewing all balances - **Invoice Key**: Read access to user's own data - **Users**: Can only see/manage their own accounts and transactions ### Extension as LNbits Module This extension follows LNbits extension structure: - Registered via `castle_ext` router in `__init__.py` - Static files served from `static/` directory - Templates in `templates/castle/` - Database accessed via `db = Database("ext_castle")` ## Common Tasks ### Add New Expense Account ```python await create_account(CreateAccount( name="Expenses:Internet", account_type=AccountType.EXPENSE, description="Internet service costs" )) ``` ### Manually Record Cash Payment ```python await create_journal_entry(CreateJournalEntry( description="Cash payment for groceries", lines=[ CreateEntryLine(account_id=expense_account_id, debit=50000), CreateEntryLine(account_id=cash_account_id, credit=50000) ], flag=JournalEntryFlag.CLEARED, meta={"source": "manual", "payment_method": "cash"} )) ``` ### Check User Balance ```python balance = await get_user_balance(user_id) print(f"Sats: {balance.balance}") # Positive = Castle owes user print(f"Fiat: {balance.fiat_balances}") # {"EUR": Decimal("36.93")} ``` ### Export to Beancount (Future) Follow patterns in `docs/BEANCOUNT_PATTERNS.md` for implementing Beancount export. Use hierarchical account names and preserve metadata in Beancount comments. ## Data Integrity **Critical Invariants**: 1. Every journal entry MUST have balanced debits and credits 2. Fiat balances calculated from metadata, not from converting sats 3. User accounts use `user_id` (NOT `wallet_id`) for consistency 4. Balance assertions checked daily via background task **Validation** is performed in `core/validation.py`: - `validate_journal_entry()` - Checks balance, minimum lines - `validate_balance()` - Verifies account balance calculation - `validate_receivable_entry()` - Ensures receivable entries are valid - `validate_expense_entry()` - Ensures expense entries are valid ## Known Issues & Future Work See `docs/DOCUMENTATION.md` for comprehensive list. Key items: - No journal entry editing/deletion (use reversing entries) - No date range filtering on list endpoints (hardcoded limit of 100) - No batch operations for bulk imports - Plugin system architecture designed but not implemented - Beancount export endpoint not yet implemented ## Related Documentation - `docs/README.md` - User-facing overview - `docs/DOCUMENTATION.md` - Comprehensive technical documentation - `docs/BEANCOUNT_PATTERNS.md` - Beancount-inspired design patterns - `docs/PHASE1_COMPLETE.md`, `PHASE2_COMPLETE.md`, `PHASE3_COMPLETE.md` - Development milestones - `docs/EXPENSE_APPROVAL.md` - Manual payment request workflow - `docs/DAILY_RECONCILIATION.md` - Automated reconciliation system