Reimbursement Approval
Data Entity
Description
Tracks the approval lifecycle for a submitted expense claim. Each expense has at most one reimbursement approval record, capturing status, reviewer identity, approval method (manual or auto), and audit metadata. Supports threshold-based auto-approval rules configured per organization.
Data Structure
| Name | Type | Description | Constraints |
|---|---|---|---|
id |
uuid |
Primary key, auto-generated UUID | PKrequiredunique |
expense_id |
uuid |
Foreign key to expenses table. One-to-one relationship — each expense may have exactly one approval record. | requiredunique |
status |
enum |
Current state of the reimbursement approval workflow | required |
approval_method |
enum |
How the approval decision was made — by a human reviewer or triggered automatically by an auto-approval rule | - |
approved_by |
uuid |
Foreign key to users table — the coordinator or org admin who manually approved or rejected. Null for auto-approved records. | - |
auto_approval_rule_id |
uuid |
Foreign key to the auto-approval rule that triggered automatic approval. Null for manually processed records. | - |
amount_approved |
decimal |
Final approved reimbursement amount in NOK. May differ from the requested amount if partially approved. | - |
rejection_reason |
text |
Required when status is rejected. Free-text explanation provided by the reviewer to the peer mentor. | - |
reviewer_notes |
text |
Internal notes added by the reviewer, not visible to the peer mentor. Used for coordinator context or audit trail. | - |
reviewed_at |
datetime |
Timestamp when the reviewer took an action (approve or reject). Null while pending. | - |
created_at |
datetime |
Timestamp when the approval record was created (either on expense submission or when queued for review) | required |
updated_at |
datetime |
Timestamp of the most recent change to this record | required |
Database Indexes
idx_reimbursement_approvals_expense_id
Columns: expense_id
idx_reimbursement_approvals_status
Columns: status
idx_reimbursement_approvals_approved_by
Columns: approved_by
idx_reimbursement_approvals_reviewed_at
Columns: reviewed_at
idx_reimbursement_approvals_status_created_at
Columns: status, created_at
Validation Rules
expense_id_exists
error
Validation failed
valid_status_transition
error
Validation failed
amount_approved_non_negative
error
Validation failed
reviewed_at_set_on_decision
error
Validation failed
approved_by_required_for_manual
error
Validation failed
Business Rules
one_approval_per_expense
Each expense record can have at most one reimbursement_approvals record. Attempting to create a second approval for the same expense_id must be rejected.
auto_approval_threshold
When an expense is submitted and matches the organization's auto-approval rules (e.g. distance under 50 km and no receipt required), the system must automatically set status to auto_approved and approval_method to auto without requiring manual reviewer action.
rejection_requires_reason
When status is set to rejected, rejection_reason must be provided. Rejections without a stated reason must be blocked.
approved_record_immutable
Once a record reaches status approved or auto_approved, it may not be changed except by an org admin performing an explicit correction (which must create an audit log entry).
reviewer_role_enforcement
Only users with coordinator or org_admin role within the same organization as the expense submitter may approve or reject. The approved_by field must reference a user with appropriate permissions.
amount_approved_within_requested
amount_approved must not exceed the amount claimed in the linked expense. Partial approvals are allowed but must be explicitly flagged to the reviewer.
accounting_sync_on_approval
When status transitions to approved or auto_approved, the accounting integration must be notified to queue the reimbursement for export to the organization's accounting system (Xledger or Dynamics).