Simplifies the representation of journal entry lines by replacing separate debit and credit fields with a single 'amount' field. Positive amounts represent debits, while negative amounts represent credits, aligning with Beancount's approach. This change improves code readability and simplifies calculations for balancing entries.
4.8 KiB
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
!tox(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(amount)
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
-
views_api.py
- Line 284: Set expenses to
JournalEntryFlag.PENDINGon creation - Lines 181-197: Added
/api/v1/entries/pendingendpoint - Lines 972-1011: Added approve endpoint
- Lines 1013-1053: Added reject endpoint
- Line 284: Set expenses to
-
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
- Lines 315-329: Updated
-
index.html
- Lines 157-209: Added "Pending Expense Approvals" card
-
index.js
- Line 68: Added
pendingExpensesdata 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
- Line 68: Added
User Experience
For Regular Users
- Submit expense via "Add Expense" button
- See expense with orange pending icon (⏱️) in transaction list
- Balance does NOT change yet
- Wait for admin approval
For Admin (Super User)
- See "Pending Expense Approvals" card at top of page
- Review expense details
- Click "Approve" → User's balance updates
- 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
metafield tracks who created and reviewed each entry
Testing
-
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 } -
Verify it's pending
- Check user balance → should NOT include this expense
- Check transaction list → should show orange pending icon
-
Login as admin and approve
- See expense in "Pending Expense Approvals"
- Click "Approve"
- Verify user balance updates
-
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