# Expense Approval Workflow ## Overview The Castle extension now requires admin approval for all user-submitted expenses. This prevents invalid or incorrect expenses from affecting balances until they are verified by the Castle admin. ## How It Works ### 1. User Submits Expense - User fills out the expense form with description, amount, category, etc. - Expense is created with `flag='!'` (PENDING status) - Entry is saved to the database but **does not affect balances** ### 2. Admin Reviews Pending Expenses - Admin sees "Pending Expense Approvals" card on the main page - Card shows all pending expenses with: - Description and amount - User who submitted it - Date submitted - Fiat amount (if applicable) - Reference number ### 3. Admin Takes Action #### Option A: Approve - Admin clicks "Approve" button - Entry flag changes from `!` to `*` (CLEARED) - Entry **now affects balances** (user's balance updates) - User sees the expense in their transaction history - Entry appears with green checkmark icon #### Option B: Reject - Admin clicks "Reject" button - Entry flag changes from `!` to `x` (VOID) - Entry **never affects balances** - Entry appears with grey cancel icon (voided) ## Balance Calculation Only entries with `flag='*'` (CLEARED) are included in balance calculations: ```sql -- Balance query excludes pending/flagged/voided entries SELECT SUM(debit), SUM(credit) FROM entry_lines el JOIN journal_entries je ON el.journal_entry_id = je.id WHERE el.account_id = :account_id AND je.flag = '*' -- Only cleared entries ``` ## Transaction Flags | Flag | Symbol | Status | Affects Balance | Description | |------|--------|--------|----------------|-------------| | `*` | ✅ | CLEARED | Yes | Confirmed and reconciled | | `!` | ⏱️ | PENDING | No | Awaiting approval | | `#` | 🚩 | FLAGGED | No | Needs review | | `x` | ❌ | VOID | No | Cancelled/rejected | ## API Endpoints ### Get Pending Entries (Admin Only) ``` GET /castle/api/v1/entries/pending Authorization: Admin Key Returns: list[JournalEntry] ``` ### Approve Expense (Admin Only) ``` POST /castle/api/v1/entries/{entry_id}/approve Authorization: Admin Key Returns: JournalEntry (with flag='*') ``` ### Reject Expense (Admin Only) ``` POST /castle/api/v1/entries/{entry_id}/reject Authorization: Admin Key Returns: JournalEntry (with flag='x') ``` ## Implementation Details ### Files Modified 1. **views_api.py** - Line 284: Set expenses to `JournalEntryFlag.PENDING` on creation - Lines 181-197: Added `/api/v1/entries/pending` endpoint - Lines 972-1011: Added approve endpoint - Lines 1013-1053: Added reject endpoint 2. **crud.py** - Lines 315-329: Updated `get_account_balance()` to filter by flag - Lines 367-376: Updated fiat balance calculation to filter by flag - Lines 238-269: Fixed `get_all_journal_entries()` to parse flag/meta 3. **index.html** - Lines 157-209: Added "Pending Expense Approvals" card 4. **index.js** - Line 68: Added `pendingExpenses` data property - Lines 497-511: Added `loadPendingExpenses()` method - Lines 545-563: Added `approveExpense()` method - Lines 564-580: Added `rejectExpense()` method - Line 731: Load pending expenses on page load for admins ## User Experience ### For Regular Users 1. Submit expense via "Add Expense" button 2. See expense with orange pending icon (⏱️) in transaction list 3. Balance does NOT change yet 4. Wait for admin approval ### For Admin (Super User) 1. See "Pending Expense Approvals" card at top of page 2. Review expense details 3. Click "Approve" → User's balance updates 4. Click "Reject" → Expense is voided, no balance change ## Security - All approval endpoints require admin key - Super user check prevents regular users from approving their own expenses - Voided entries are never included in balance calculations - Full audit trail in `meta` field tracks who created and reviewed each entry ## Testing 1. **Submit test expense as regular user** ``` POST /castle/api/v1/entries/expense { "description": "Test groceries", "amount": 50.00, "currency": "EUR", "expense_account": "utilities", "is_equity": false } ``` 2. **Verify it's pending** - Check user balance → should NOT include this expense - Check transaction list → should show orange pending icon 3. **Login as admin and approve** - See expense in "Pending Expense Approvals" - Click "Approve" - Verify user balance updates 4. **Submit another expense and reject it** - Submit expense - Admin clicks "Reject" - Verify balance never changed - Entry shows grey cancel icon ## Future Enhancements - [ ] Email notifications when expense is approved/rejected - [ ] Bulk approve/reject multiple expenses - [ ] Admin notes when rejecting (reason for rejection) - [ ] Expense revision system (user can edit and resubmit rejected expenses) - [ ] Approval workflow with multiple approvers