core PK: id 11 required 1 unique

Description

A travel or reimbursement expense claim submitted by a peer mentor or coordinator, linked to an activity. Captures expense header metadata including type, status, approval state, and confidentiality declaration requirements. Child expense items hold line-level amounts.

20
Attributes
6
Indexes
6
Validation Rules
22
CRUD Operations

Data Structure

Name Type Description Constraints
id uuid Primary key
PKrequiredunique
activity_id uuid FK to activities. Expense is always linked to a registered activity.
required
organization_id uuid FK to organizations. Denormalized for fast tenant-scoped queries and approval routing.
required
submitted_by_user_id uuid FK to users. The user who submitted the expense (peer mentor or coordinator acting as proxy).
required
on_behalf_of_user_id uuid FK to users. Set when a coordinator submits a proxy expense on behalf of a peer mentor. NULL if self-submitted.
-
status enum Approval lifecycle state of the expense claim.
required
total_amount decimal Sum of all expense item amounts in NOK. Computed and stored for fast approval threshold evaluation.
required
currency string ISO 4217 currency code. Defaults to NOK.
required
requires_confidentiality_declaration boolean True when the expense type or role (e.g. driver) requires a signed confidentiality declaration before reimbursement is processed.
required
confidentiality_declaration_id uuid FK to confidentiality_declarations when a declaration is required and has been submitted.
-
auto_approval_applied boolean True when the expense was approved automatically by matching an auto-approval rule (e.g. under 50 km / no receipts).
required
auto_approval_rule_id uuid FK to auto_approval_rules. Populated when auto_approval_applied is true.
-
reviewer_user_id uuid FK to users. The coordinator or org admin who manually reviewed the expense. NULL for auto-approved.
-
reviewed_at datetime Timestamp when the expense was approved or rejected by a reviewer.
-
rejection_reason text Free-text reason provided by the reviewer when status = rejected.
-
notes text Optional free-text notes from the submitter about this expense claim.
-
submitted_at datetime Timestamp when the expense was submitted for approval (status changed from draft to submitted).
-
created_at datetime Record creation timestamp.
required
updated_at datetime Last modification timestamp.
required
deleted_at datetime Soft-delete timestamp. NULL = active record.
-

Database Indexes

idx_expenses_activity_id
btree

Columns: activity_id

idx_expenses_organization_id_status
btree

Columns: organization_id, status

idx_expenses_submitted_by_user_id
btree

Columns: submitted_by_user_id

idx_expenses_on_behalf_of_user_id
btree

Columns: on_behalf_of_user_id

idx_expenses_status_submitted_at
btree

Columns: status, submitted_at

idx_expenses_deleted_at
btree

Columns: deleted_at

Validation Rules

total_amount_non_negative error

Validation failed

status_transition_valid error

Validation failed

reviewed_at_required_on_approval error

Validation failed

rejection_reason_required error

Validation failed

currency_iso4217 error

Validation failed

submitted_at_set_on_submit error

Validation failed

Business Rules

expense_requires_activity
on_create

Every expense must be linked to a registered activity. Standalone expense claims without an activity are not permitted.

auto_approval_threshold
on_create

Expenses below the organization's configured auto-approval threshold (e.g. under 50 km / no receipts requiring manual review) are automatically approved without human intervention. The matching auto_approval_rule_id is recorded.

receipt_required_above_threshold
on_update

When total_amount exceeds the organization's receipt-required threshold (e.g. 100 NOK for HLF), at least one expense item must have an associated receipt before the expense can be submitted.

mutually_exclusive_expense_types
on_create

Certain expense types are mutually exclusive within a single expense (e.g. kilometre reimbursement and public transport cannot both be selected). The constraint is enforced per organization expense type rules.

confidentiality_declaration_required_for_driver_role
on_update

When an expense includes a driver honorarium expense type, requires_confidentiality_declaration is set to true and a linked confidentiality declaration must exist before the expense can be submitted.

proxy_expense_coordinator_only
on_create

on_behalf_of_user_id may only be set when submitted_by_user_id belongs to a user with a Coordinator role within the same organization.

tenant_isolation
always

All expense reads and writes are scoped to organization_id. Cross-tenant access is never permitted.

approved_expense_immutable
on_update

Once an expense reaches status approved or auto_approved, its items and amounts cannot be modified. A new expense must be created to correct errors.

Storage Configuration

Storage Type
primary_table
Location
main_db
Partitioning
No Partitioning
Retention
Permanent Storage