Add soft delete support for accounts (is_active field)

- Migration m002: Add is_active column to castle_accounts table
- Updated Account and AccountWithPermissions models with is_active field
- Default value: TRUE (all existing accounts remain active)
- Index added for performance on is_active queries

Next steps (to be completed):
- Update account sync to mark orphaned accounts as inactive
- Filter inactive accounts in get_all_accounts queries
- Prevent permissions from being granted on inactive accounts
- Add API endpoint to list/reactivate orphaned accounts

This implements soft delete strategy where accounts removed from
Beancount are marked inactive rather than deleted, preserving
historical data and permissions while preventing new activity.

🤖 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 01:48:23 +01:00
parent ee2df73bcb
commit 3af9b44e39
2 changed files with 29 additions and 0 deletions

View file

@ -325,3 +325,30 @@ async def m001_initial(db):
"description": description "description": description
} }
) )
async def m002_add_account_is_active(db):
"""
Add is_active field to accounts table for soft delete functionality.
This enables marking accounts as inactive when they're removed from Beancount
while preserving historical data and permissions. Inactive accounts:
- Cannot have new permissions granted
- Are filtered out of default queries
- Can be reactivated if account is re-added to Beancount
Default: All existing accounts are marked as active (TRUE).
"""
await db.execute(
"""
ALTER TABLE castle_accounts
ADD COLUMN is_active BOOLEAN NOT NULL DEFAULT TRUE
"""
)
# Create index for faster queries filtering by is_active
await db.execute(
"""
CREATE INDEX idx_castle_accounts_is_active ON castle_accounts (is_active)
"""
)

View file

@ -36,6 +36,7 @@ class Account(BaseModel):
description: Optional[str] = None description: Optional[str] = None
user_id: Optional[str] = None # For user-specific accounts user_id: Optional[str] = None # For user-specific accounts
created_at: datetime created_at: datetime
is_active: bool = True # Soft delete flag
class CreateAccount(BaseModel): class CreateAccount(BaseModel):
@ -322,6 +323,7 @@ class AccountWithPermissions(BaseModel):
description: Optional[str] = None description: Optional[str] = None
user_id: Optional[str] = None user_id: Optional[str] = None
created_at: datetime created_at: datetime
is_active: bool = True # Soft delete flag
# Only included when filter_by_user=true # Only included when filter_by_user=true
user_permissions: Optional[list[PermissionType]] = None user_permissions: Optional[list[PermissionType]] = None
inherited_from: Optional[str] = None # Parent account ID if inherited inherited_from: Optional[str] = None # Parent account ID if inherited