Skip to content

Support using multiple accounts with moneyflow#33

Merged
wesm merged 15 commits intomainfrom
multiple-backends
Nov 8, 2025
Merged

Support using multiple accounts with moneyflow#33
wesm merged 15 commits intomainfrom
multiple-backends

Conversation

@wesm
Copy link
Owner

@wesm wesm commented Nov 7, 2025

An account selector is shown on login now, and each backend connection is stored in a separate profile so you can have multiple Monarch or YNAB accounts, for example. If you have imported Amazon transactions, it is shown in the account selector (but moneyflow amazon also works). If we want to support multiple Amazon databases, that will have to be another PR. The user's existing Amazon database or Monarch/YNAB credentials will be automatically migrated to the new profile system

account-selector

wesm and others added 13 commits November 8, 2025 07:12
Adds foundational components for managing multiple backend accounts
simultaneously (e.g., multiple Monarch Money accounts, multiple YNAB budgets).

**New Components:**

1. **account_manager.py** - Account profile management
   - Account & AccountRegistry dataclasses for account metadata
   - Profile directory management (~/.moneyflow/profiles/{account_id}/)
   - CRUD operations: create, delete, list, get accounts
   - Tracks last_used timestamp for sorting accounts
   - 44 tests with 100% coverage

2. **credentials.py** - Multi-account credential isolation
   - Added `profile_dir` parameter for profile-scoped credentials
   - Each account gets isolated credentials.enc and salt files
   - Maintains backward compatibility (legacy single-account mode)
   - 5 new tests for profile directory mode (38 existing tests still pass)

3. **data_manager.py** - Documentation update
   - Clarified merchant_cache_dir supports profile isolation
   - No code changes needed (already flexible)

4. **screens/account_selector_screen.py** - Account selection UI (not integrated yet)
   - Shows list of configured accounts with backend type and last used
   - Add new account, demo mode, and exit options
   - Returns selected account_id when dismissed

5. **screens/account_name_input_screen.py** - Account naming UI (not integrated yet)
   - Prompts for user-friendly account name when adding new account
   - Validates name (2-50 characters)
   - Returns account name for ID generation

**Architecture:**

Directory structure (per account):
```
~/.moneyflow/
├── accounts.json           # Account registry
└── profiles/
    ├── monarch-personal/   # Isolated profile for each account
    │   ├── credentials.enc
    │   ├── salt
    │   ├── merchants.json
    │   └── cache/
    └── ynab-budget-2025/
        ├── credentials.enc
        └── ...
```

**Solves:**
- Issue: Single credentials file supports only one backend at a time
- Issue: merchants.json is shared (Amazon mode overwrites Monarch/YNAB data)
- Issue: No way to have multiple accounts of same backend type

**Testing:**
- All 960 tests pass (44 new account_manager tests + 5 new credential tests)
- Type checking clean (pyright)
- Code formatting and linting pass (ruff)

**Next Steps:**
- Integrate account selector into app.py startup flow
- Add migration utility for existing single-account credentials
- Wire up add account flow (selector → name input → backend selection → credential setup)

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

Co-Authored-By: Claude <noreply@anthropic.com>
Completes multi-account feature by wiring account selection into app.

**New Functionality:**

1. **Account Selection on Startup**
   - Shows AccountSelectorScreen listing all configured accounts
   - Displays backend type (🏦 Monarch, 💰 YNAB, etc.) and last used date
   - Sorted by most recently used
   - Options: Select account, Add new account, Demo mode, Exit

2. **Add New Account Flow**
   - Step 1: User enters friendly account name (validated 2-50 chars)
   - Step 2: Select backend type (Monarch, YNAB, Amazon)
   - Step 3: Account profile created in ~/.moneyflow/profiles/{id}/
   - Step 4: Credential setup (if backend requires auth)
   - Returns to account selector on cancel (cleans up partial account)

3. **Profile-Scoped Data Isolation**
   - Each account gets isolated:
     - credentials.enc & salt (in profile dir)
     - merchants.json (in profile dir)
     - cache/ (in profile dir)
   - Solves Amazon mode overwriting merchants.json issue
   - Enables multiple accounts of same backend type

4. **Legacy Credential Migration**
   - Automatically detects old ~/.moneyflow/credentials.enc
   - Migrates to ~/.moneyflow/profiles/default/ on first run
   - Preserves all data (credentials, merchant cache, transaction cache)
   - Only runs if no profiles exist yet (safe for new users)
   - 13 tests with 97% coverage

**Implementation Details:**

- Updated `app.py::initialize_data()` to use `_handle_account_selection()`
- Updated `_initialize_managers()` to accept `profile_dir` parameter
- Profile-scoped merchant cache and transaction cache paths
- Backward compatible: pre-configured backends (Amazon CLI mode) still work
- Demo mode accessible from both CLI flag and account selector

**Testing:**
- All 973 tests pass (960 original + 13 migration)
- account_manager: 100% coverage
- migration: 97% coverage
- credentials profile mode: 100% coverage
- All existing tests pass (backward compatibility maintained)
- Type checking clean (pyright)
- Code formatting and linting pass (ruff)

**User Experience:**

Before (single account):
```
$ moneyflow
[Shows credential unlock screen]
```

After (multi-account):
```
$ moneyflow
[Shows account selector with list of accounts]
- 🏦 Monarch - Personal (Last used: 2025-11-07)
- 💰 YNAB - Budget 2025 (Last used: 2025-11-06)
[+ Add New Account] [🎮 Demo Mode] [Exit]
```

Legacy users: Automatically migrated to "Default Account" profile on first run.

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

Co-Authored-By: Claude <noreply@anthropic.com>
Updates CredentialSetupScreen and CredentialUnlockScreen to accept
profile_dir parameter for multi-account support.

**Changes:**

1. CredentialSetupScreen - Added profile_dir parameter
   - Passes profile_dir to CredentialManager on save
   - Credentials saved to profile directory instead of config root

2. CredentialUnlockScreen - Added profile_dir parameter
   - Passes profile_dir to CredentialManager on load/delete
   - Reads credentials from profile directory

3. app.py - Pass profile_dir when creating credential screens
   - _handle_account_selection passes profile_dir to unlock screen
   - _handle_add_new_account passes profile_dir to setup screen
   - Ensures credentials are isolated per account

**Testing:**
- All 973 tests pass
- Type checking clean
- Linting passes

This completes the profile-scoped credential isolation for multi-account support.

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

Co-Authored-By: Claude <noreply@anthropic.com>
Fixes two bugs found during manual testing:

**Bug 1: Migrated account not showing in selector**
- Problem: AccountSelectorScreen loaded accounts in on_mount()
- on_mount() runs AFTER compose(), so account list was empty during rendering
- Fix: Load accounts in __init__() instead, before compose() runs
- Now migrated "Default Account" appears immediately

**Bug 2: Exit button shows blank screen instead of exiting**
- Problem: When user clicks Exit from account selector, app just returned
- This left app running with no data loaded (blank table)
- Fix: Call self.exit() when account selection returns None
- Now Exit properly quits the application

**Testing:**
- All 973 tests pass
- Type checking clean
- Linting passes
- Manual testing confirms both issues resolved

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

Co-Authored-By: Claude <noreply@anthropic.com>
Adds comprehensive keyboard support for account selection flow.

**AccountSelectorScreen:**
- ↑/↓: Navigate through accounts with arrow keys
- Enter: Select currently highlighted account
- a/n: Add new account
- d: Demo mode
- Esc/q: Exit
- Auto-focuses first account on load
- Wraps around when navigating past first/last account

**AccountNameInputScreen:**
- Enter: Continue with account name (from input field or button)
- Esc: Cancel and return to previous screen
- Auto-focuses input field on load

**UX Improvements:**
- Help text shows available keyboard shortcuts
- Arrow keys more intuitive than number keys (1-9)
- Consistent keyboard patterns across all account screens
- No mouse required for account selection

**Example workflow (keyboard only):**
```
App starts → Account selector
↓ (navigate to desired account)
Enter (select)
→ Credential unlock screen
[type password]
Enter (unlock)
→ App loads
```

**Testing:**
- All 973 tests pass
- Type checking clean
- Linting passes

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

Co-Authored-By: Claude <noreply@anthropic.com>
Adds clear visual feedback showing which account is currently focused/selected.

**Visual Indicators:**
- Focused account button: Accent background color + thick accent border
- Focused button text: Bold styling
- Unfocused buttons: Default background
- Hover state: Primary background (works for mouse users)

**CSS Changes:**
- Added :focus pseudo-class styling for .account-item buttons
- background: $accent (bright highlight)
- border: thick $accent (clear border)
- text-style: bold (makes text stand out)

**Result:**
When navigating with ↑/↓ arrow keys, the currently selected account
is now clearly highlighted with a colored background and border, making
keyboard navigation much easier to follow visually.

**Testing:**
- All 973 tests pass
- Type checking clean
- Linting passes

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

Co-Authored-By: Claude <noreply@anthropic.com>
Adds comprehensive keyboard support to BackendSelectionScreen.

**Keyboard Shortcuts:**
- ↑/↓: Navigate between Monarch Money and YNAB options
- Enter: Select currently highlighted backend
- m: Quick select Monarch Money
- y: Quick select YNAB
- Esc: Cancel/go back

**Visual Feedback:**
- Focused backend button: Accent background + thick border + bold text
- Auto-focuses Monarch Money option on screen load
- Clear visual indication of current selection

**Behavior Change:**
- Exit button now returns None (cancel) instead of exiting app
- Allows user to go back to previous screen in multi-step flow
- Changed button label: "Exit" → "Cancel" for clarity

**Example Workflow (keyboard only):**
```
Account Selector
a (add new account)
→ Account Name Input
[type "Business"]
Enter
→ Backend Selection (now with keyboard nav!)
↓ (navigate to YNAB)
Enter (or just press 'y')
→ Credential Setup
```

**Testing:**
- All 973 tests pass
- Type checking clean
- Linting passes

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

Co-Authored-By: Claude <noreply@anthropic.com>
Fixes arrow keys being captured by ScrollableContainer instead of
navigating between accounts.

**Problem:**
- ScrollableContainer captures ↑/↓ arrow keys for scrolling
- User's arrow key presses scroll the container instead of moving between accounts
- Makes keyboard navigation unusable when multiple accounts exist

**Solution:**
- Added `priority=True` to up/down bindings
- This gives Screen-level bindings priority over widget-level handlers
- Arrow keys now navigate accounts as intended
- Added j/k as alternative vim-style navigation (bonus!)

**Keyboard Navigation Now:**
- ↑/↓: Navigate between accounts (takes priority over scrolling)
- j/k: Alternative vim-style navigation (also works)
- ScrollableContainer will still scroll via mouse/trackpad

**Updated help text:**
"Keys: ↑/↓ or j/k=Navigate | Enter=Select | a=Add | d=Demo | Esc/q=Exit"

**Testing:**
- All 973 tests pass
- Type checking clean
- Linting passes

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

Co-Authored-By: Claude <noreply@anthropic.com>
Automatically migrates legacy ~/.moneyflow/amazon.db to the new
multi-account profile system on first startup. Amazon now appears
as a regular account alongside Monarch/YNAB in the account selector.

Changes:
- Add migrate_legacy_amazon_db() for automatic migration on startup
- Create "Amazon" account in accounts.json during migration
- Move database to ~/.moneyflow/profiles/amazon/amazon.db
- Skip credential unlock flow for Amazon accounts (local-only)
- Initialize Amazon backend with profile-scoped database path
- Update CLI to detect and use migrated Amazon profile
- Add 9 comprehensive migration tests (96% coverage)

Amazon accounts are now:
- Displayed in account selector with 📦 icon
- Tracked in accounts.json like other accounts
- Fully integrated with multi-account system
- Still accessible via 'moneyflow amazon' CLI command

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

Co-Authored-By: Claude <noreply@anthropic.com>
Updated mkdocs documentation to reflect new multi-account system:
- Amazon mode integration with account selector
- Multi-account feature highlights
- Database location changes
- Account selector navigation instructions
- Changelog for v0.7.0

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

Co-Authored-By: Claude <noreply@anthropic.com>
Added account selector screenshot generation showing the multi-account
interface with Monarch, YNAB, and Amazon accounts.

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

Co-Authored-By: Claude <noreply@anthropic.com>
- Fix line length issues in quickstart and amazon-mode docs
- Change bold headers to proper H4 headers
- Add language specification to fenced code block

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

Co-Authored-By: Claude <noreply@anthropic.com>
@wesm wesm force-pushed the multiple-backends branch from 2cdf1b9 to 7432588 Compare November 8, 2025 13:46
wesm and others added 2 commits November 8, 2025 07:49
Fixed credential setup screenshots to work with the new multi-account
system. Now directly shows CredentialSetupScreen instead of trying to
navigate through account selector and backend selection.

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

Co-Authored-By: Claude <noreply@anthropic.com>
Changed backend selection and credential setup screenshots to use
a minimal textual.App instead of MoneyflowApp to prevent the account
selector from appearing in these screenshots.

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

Co-Authored-By: Claude <noreply@anthropic.com>
@wesm wesm merged commit 2b376db into main Nov 8, 2025
6 checks passed
@wesm wesm deleted the multiple-backends branch November 8, 2025 13:57
@Aynekulu-Dev
Copy link

good

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants