core PK: id 8 required 2 unique

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.

13
Attributes
5
Indexes
9
Validation Rules
14
CRUD Operations

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

Columns: expense_id

idx_expense_approvals_org_status
btree

Columns: organization_id, status

idx_expense_approvals_reviewer
btree

Columns: reviewer_id

idx_expense_approvals_org_submitted
btree

Columns: organization_id, submitted_at

idx_expense_approvals_status_submitted
btree

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
on_create

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
on_update

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
on_update

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
on_create

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
on_update

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
on_update

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
always

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
on_update

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
on_update

The reviewer_id must not match the user who submitted the expense. Peer mentors and coordinators cannot approve their own expense claims.

Storage Configuration

Storage Type
primary_table
Location
main_db
Partitioning
No Partitioning
Retention
Permanent Storage