Add virtual parent accounts for permission inheritance

Implements metadata-only accounts (e.g., "Expenses", "Assets") that exist
solely in Castle DB for hierarchical permission management. These accounts
don't exist in Beancount but cascade permissions to all child accounts.

Changes:

**Migration (m003)**:
- Add `is_virtual` BOOLEAN field to accounts table
- Create index idx_accounts_is_virtual
- Insert 5 default virtual parents: Assets, Liabilities, Equity, Income, Expenses

**Models**:
- Add `is_virtual: bool = False` to Account, CreateAccount, AccountWithPermissions

**CRUD**:
- Update create_account() to pass is_virtual to Account constructor

**Account Sync**:
- Skip deactivating virtual accounts (they're intentionally metadata-only)
- Virtual accounts never get marked as inactive by sync

**Use Case**:
Admin grants permission on virtual "Expenses" account → user automatically
gets access to ALL real expense accounts:
- Expenses:Groceries
- Expenses:Gas:Kitchen
- Expenses:Maintenance:Property
- (and all other Expenses:* children)

This solves the limitation where Beancount doesn't allow single-level accounts
(e.g., bare "Expenses" can't exist in ledger), but admins need a way to grant
broad access without manually selecting dozens of accounts.

Hierarchical permission inheritance already works via account_name.startswith()
check - virtual accounts simply provide the parent nodes to grant permissions on.

🤖 Generated with Claude Code (https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
padreug 2025-11-11 02:41:05 +01:00
parent 217fee6664
commit 79849f5fb2
4 changed files with 64 additions and 0 deletions

View file

@ -65,6 +65,7 @@ async def create_account(data: CreateAccount) -> Account:
account_type=data.account_type,
description=data.description,
user_id=data.user_id,
is_virtual=data.is_virtual,
created_at=datetime.now(),
)
await db.insert("accounts", account)