Expense Approvals
Data Entity
Description
Records the approval or rejection decision for each expense claim submitted by peer mentors and coordinators. Each expense has at most one approval record, forming a one-to-one relationship with the expenses table. Supports manual coordinator/admin review, auto-approval via rule engine, and batch approval workflows.
Data Structure
| Name | Type | Description | Constraints |
|---|---|---|---|
id |
uuid |
Unique identifier for the approval record | PKrequiredunique |
expense_id |
uuid |
Foreign key referencing the expense claim being approved or rejected | requiredunique |
status |
enum |
Current approval status of the expense claim | required |
reviewer_id |
uuid |
Foreign key referencing the user (coordinator or org admin) who reviewed the expense. Null for auto-approved claims. | - |
reviewed_at |
datetime |
Timestamp when the approval or rejection decision was made. Null while pending. | - |
decision_notes |
text |
Optional reviewer notes explaining the approval or rejection reason | - |
rejection_reason |
text |
Required explanation when an expense is rejected, displayed to the submitter | - |
auto_approval_rule_id |
uuid |
Reference to the auto-approval rule that triggered automatic approval. Null for manually reviewed claims. | - |
organization_id |
uuid |
Foreign key referencing the organization owning this approval, used for multi-tenant isolation | required |
submitted_at |
datetime |
Timestamp when the expense was submitted for approval (denormalized for queue sorting) | required |
version |
integer |
Optimistic locking version counter to prevent concurrent modification conflicts | required |
created_at |
datetime |
Record creation timestamp | required |
updated_at |
datetime |
Record last-update timestamp, auto-updated on every modification | required |
Database Indexes
idx_expense_approvals_expense_id
Columns: expense_id
idx_expense_approvals_org_status
Columns: organization_id, status
idx_expense_approvals_reviewer
Columns: reviewer_id
idx_expense_approvals_org_submitted
Columns: organization_id, submitted_at
idx_expense_approvals_status_submitted
Columns: status, submitted_at
Validation Rules
expense_exists
error
Validation failed
reviewer_exists
error
Validation failed
status_enum_valid
error
Validation failed
reviewed_at_consistency
error
Validation failed
rejection_reason_required_on_reject
error
Validation failed
decision_notes_max_length
error
Validation failed
auto_approval_rule_consistency
error
Validation failed
organization_matches_expense
error
Validation failed
version_positive_integer
error
Validation failed
Business Rules
one_approval_per_expense
Each expense claim can have at most one approval record. The unique constraint on expense_id enforces a strict one-to-one relationship.
reviewer_role_required
Only users with Coordinator or Organization Administrator roles may approve or reject expenses. Role-based access control is enforced at the API layer.
rejection_requires_reason
When status transitions to 'rejected', the rejection_reason field must be non-empty to inform the submitter why their claim was denied.
auto_approval_threshold
Expenses matching organization-specific auto-approval rules (amount under threshold, distance under limit, receipt attached if required) are automatically approved at submission time without manual review.
status_transition_enforcement
Status can only transition from pending_review to approved, rejected, or auto_approved. Once approved or rejected, status is final and cannot be changed.
optimistic_locking
Updates must include the current version number. If the version in the database does not match, the update is rejected to prevent concurrent modification conflicts in batch approval workflows.
tenant_isolation
All approval queries and mutations are scoped to the requesting user's organization_id. Coordinators see only their local association's expenses; Org Admins see the full organization.
audit_trail_on_decision
Every approval or rejection action writes an entry to the audit_logs table recording the actor, action, expense ID, and timestamp for compliance traceability.
reviewer_cannot_self_approve
The reviewer_id must not match the user who submitted the expense. Peer mentors and coordinators cannot approve their own expense claims.