core PK: id 5 required 2 unique

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.

12
Attributes
5
Indexes
5
Validation Rules
12
CRUD Operations

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

Columns: expense_id

idx_reimbursement_approvals_status
btree

Columns: status

idx_reimbursement_approvals_approved_by
btree

Columns: approved_by

idx_reimbursement_approvals_reviewed_at
btree

Columns: reviewed_at

idx_reimbursement_approvals_status_created_at
btree

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
on_create

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
on_create

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
on_update

When status is set to rejected, rejection_reason must be provided. Rejections without a stated reason must be blocked.

approved_record_immutable
on_update

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
on_update

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
on_update

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
on_update

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

Storage Configuration

Storage Type
primary_table
Location
main_db
Partitioning
No Partitioning
Retention
Permanent Storage