castle/docs/EXPENSE_APPROVAL.md
2025-11-04 01:19:30 +01:00

4.8 KiB

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:

-- 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