audit PK: id 6 required 1 unique

Description

An immutable audit trail entry recording a state transition in an expense claim's lifecycle (submitted, auto_approved, coordinator_approved, rejected, exported). Each event captures the actor's identity, timestamp, from/to status, and an optional coordinator comment. Together the events form the complete approval history shown in the claim status audit timeline.

8
Attributes
5
Indexes
7
Validation Rules
11
CRUD Operations

Data Structure

Name Type Description Constraints
id uuid Surrogate primary key generated by the database on insert. Never reused or reassigned.
PKrequiredunique
expense_claim_id uuid Foreign key referencing the expense_claim this event belongs to. All events for a claim share this value and are fetched together to reconstruct the full approval timeline.
required
actor_id uuid UUID of the user who triggered the state transition. References the Supabase auth.users table. For system-initiated transitions (e.g., auto-approval), this is the service account UUID defined in environment configuration.
required
actor_role enum Role of the actor at the time of the transition. Stored redundantly so the audit log remains self-contained even if role assignments change later.
required
from_status enum Claim status immediately before this transition. NULL for the very first event (initial submission) where no prior status existed.
-
to_status enum Claim status after this transition. Must differ from from_status (except for the initial submission event where from_status is NULL).
required
comment text Optional free-text comment provided by the actor, typically a coordinator explaining a rejection or approval condition. Maximum 500 characters. Required when to_status is 'rejected' by business rule (enforced at application layer).
-
created_at datetime UTC timestamp of the moment the event was persisted. Set by the database using NOW() and never overridable by application code. Used to order events chronologically in the audit timeline.
required

Database Indexes

idx_claim_event_expense_claim_id_created_at
btree

Columns: expense_claim_id, created_at

idx_claim_event_expense_claim_id
btree

Columns: expense_claim_id

idx_claim_event_actor_id
btree

Columns: actor_id

idx_claim_event_to_status
btree

Columns: to_status

idx_claim_event_created_at
btree

Columns: created_at

Validation Rules

expense_claim_id_exists error

Validation failed

actor_id_exists error

Validation failed

to_status_not_equal_from_status error

Validation failed

comment_max_length error

Validation failed

actor_role_enum_value error

Validation failed

to_status_enum_value error

Validation failed

from_status_enum_value_or_null error

Validation failed

Business Rules

immutable_audit_record
always

Once a claim_event row is inserted it must never be updated or deleted. The table must have no UPDATE or DELETE RLS policies. The immutability preserves the integrity of the approval audit trail for regulatory and accounting compliance.

server_side_timestamp
on_create

created_at must always be supplied by the database server via DEFAULT NOW(). Application code must never pass a created_at value in the INSERT payload. This prevents timestamp forgery in the audit trail.

rejection_requires_comment
on_create

When to_status is 'rejected', comment must be a non-empty string of at least 5 characters. This ensures coordinators provide a meaningful reason visible to peer mentors and auditors. Enforced at the service layer before the repository INSERT.

valid_status_transition
on_create

The from_status → to_status pair must follow the allowed state machine: NULL → submitted, submitted → auto_approved, submitted → coordinator_approved, submitted → rejected, auto_approved → exported, coordinator_approved → exported, rejected → submitted (resubmission). All other combinations are illegal.

actor_role_matches_transition
on_create

Only a 'system' actor may record auto_approved transitions. Only 'coordinator' or 'org_admin' actors may record coordinator_approved, rejected, or exported transitions. Only a 'peer_mentor' or 'coordinator' (proxy) actor may record the initial submitted transition.

single_open_transition_per_claim
on_create

A new event may only be inserted for a claim if the claim's current status (latest to_status) logically permits the requested to_status. The approval-workflow-service reads the latest event before inserting to enforce this sequencing and prevent race conditions.

first_event_null_from_status
on_create

The very first event for a claim (submission) must have from_status = NULL. Any subsequent event must have a non-null from_status matching the to_status of the immediately preceding event for that claim.

Storage Configuration

Storage Type
primary_table
Location
main_db
Partitioning
No Partitioning
Retention
Permanent Storage

Entity Relationships

expense_claim
incoming one_to_many

Every state transition of an expense claim generates an immutable audit event forming a complete approval history timeline

optional cascade delete