Expense
Data Entity
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.
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
Columns: activity_id
idx_expenses_organization_id_status
Columns: organization_id, status
idx_expenses_submitted_by_user_id
Columns: submitted_by_user_id
idx_expenses_on_behalf_of_user_id
Columns: on_behalf_of_user_id
idx_expenses_status_submitted_at
Columns: status, submitted_at
idx_expenses_deleted_at
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
Every expense must be linked to a registered activity. Standalone expense claims without an activity are not permitted.
auto_approval_threshold
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
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
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
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_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
All expense reads and writes are scoped to organization_id. Cross-tenant access is never permitted.
approved_expense_immutable
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.