core PK: id 13 required 1 unique

Description

Records travel and other reimbursable costs submitted by peer mentors, including kilometre allowances, tolls, parking, and public transport. Supports threshold-based auto-approval, receipt attachment, confidentiality declarations, and accounting export.

22
Attributes
6
Indexes
7
Validation Rules
17
CRUD Operations

Data Structure

Name Type Description Constraints
id uuid Primary key
PKrequiredunique
user_id uuid Peer mentor who submitted the expense
required
organization_id uuid Tenant the expense belongs to, derived from user's active organization context
required
expense_type_id uuid Foreign key to expense_types — determines allowed fields and receipt requirements
required
activity_id uuid Optional link to the activity this expense was incurred for
-
status enum Lifecycle state of the expense claim
required
amount_nok decimal Total claimed amount in Norwegian Kroner (NOK), computed from type-specific fields
required
distance_km decimal Distance in kilometres for kilometre-allowance expense types; null for other types
-
rate_per_km decimal Organisation-configured rate per kilometre at time of submission; snapshotted to preserve history
-
description text Optional free-text note explaining the purpose or context of the expense
-
expense_date datetime Date the cost was incurred (not necessarily the submission date)
required
submitted_at datetime Timestamp when peer mentor submitted the claim for review
-
requires_receipt boolean Derived from expense_type and amount threshold; cached at submission time to lock approval requirements
required
receipt_uploaded boolean True when at least one expense_receipt record is linked and confirmed uploaded
required
confidentiality_declaration_id uuid Foreign key to confidentiality_declarations for driver/transport expenses that require a signed declaration (Blindeforbundet)
-
auto_approved boolean True when the expense was automatically approved by the rules engine without manual review
required
auto_approval_rule_snapshot json Snapshot of the auto-approval rule that triggered automatic approval, for audit purposes
-
rejection_reason text Reason provided by the approver when rejecting the expense
-
offline_id string Client-generated UUID used when expense was created offline; used by the ID-mapping service to reconcile after sync
-
sync_status enum Tracks whether the record originated offline and has been successfully synced
required
created_at datetime Record creation timestamp (server-side)
required
updated_at datetime Last modification timestamp
required

Database Indexes

idx_expenses_user_id
btree

Columns: user_id

idx_expenses_organization_status
btree

Columns: organization_id, status

idx_expenses_expense_date
btree

Columns: expense_date

idx_expenses_activity_id
btree

Columns: activity_id

idx_expenses_offline_id
btree unique

Columns: offline_id

idx_expenses_submitted_at
btree

Columns: organization_id, submitted_at

Validation Rules

amount_positive error

Validation failed

distance_required_for_km_type error

Validation failed

expense_date_not_future error

Validation failed

expense_type_belongs_to_org error

Validation failed

receipt_count_when_required error

Validation failed

description_max_length error

Validation failed

offline_id_uniqueness error

Validation failed

Business Rules

no_type_combination
on_create

An expense may have only one expense_type. Kilometre allowance and public-transport ticket cannot be claimed on the same record. Enforced by selecting from a controlled expense_types list, not free text.

receipt_required_above_threshold
on_update

Expenses with amount_nok above the organisation-configured threshold (default 100 NOK for HLF) must have at least one receipt uploaded before submission can be finalised.

auto_approval_below_threshold
on_create

Expenses under the organisation-configured kilometre limit (default 50 km) or with no monetary outlay are automatically approved by the rules engine without manual review.

confidentiality_declaration_required_for_driver
on_update

Driver/chauffeur expenses (Blindeforbundet) require a linked confidentiality declaration before the expense can be submitted.

status_transition_guard
on_update

Status transitions must follow the defined lifecycle: draft → submitted → (auto_approved | pending_review) → (approved | rejected) → paid. Backwards transitions are not permitted.

immutable_after_approval
on_update

Once an expense reaches approved or paid status, its amount_nok, distance_km, expense_type_id, and expense_date fields are immutable.

tenant_isolation
always

All reads and writes must be scoped to the user's active organization_id. Cross-tenant access is rejected at the API layer.

accounting_export_eligibility
always

Only expenses with status 'approved' or 'paid' are included in accounting API exports and Bufdir reporting.

Storage Configuration

Storage Type
primary_table
Location
main_db
Partitioning
by_user
Retention
archive_after_1year