Expense Type
Data Entity
Description
Defines the catalogue of allowable expense categories for peer mentor reimbursement claims. Each expense type carries organization-specific business rules including mutual exclusivity constraints, documentation requirements, amount thresholds, and auto-approval eligibility. Expense types are managed per organization to support divergent reimbursement policies across NHF, Blindeforbundet, HLF, and Barnekreftforeningen.
Data Structure
| Name | Type | Description | Constraints |
|---|---|---|---|
id |
uuid |
Primary key, generated at creation | PKrequiredunique |
organization_id |
uuid |
FK to organizations. Expense types are always scoped to a single organization; different orgs may define entirely different catalogues. | required |
code |
string |
Short machine-readable identifier for this type within the organization (e.g. 'km_allowance', 'toll', 'parking', 'public_transit', 'driver_honorarium'). Used as stable reference in business rule definitions and API payloads. | required |
name |
string |
Human-readable display label shown in the mobile app expense form and admin portal. May be overridden by organization terminology system. | required |
description |
text |
Optional longer description explaining when this expense type applies, shown as helper text in the expense form. | - |
category |
enum |
High-level grouping for display and reporting purposes. | required |
requires_receipt |
boolean |
When true, a receipt photo attachment is mandatory before the expense can be submitted. HLF requires receipts for claims above 100 NOK. | required |
receipt_required_above_amount |
decimal |
If set, receipt is only required when the claimed amount exceeds this threshold (in NOK). NULL means receipt requirement is unconditional when requires_receipt=true. HLF configures this as 100. | - |
requires_distance_km |
boolean |
When true, the expense form must capture a kilometre value. Applicable to km_allowance and toll types. | required |
max_amount |
decimal |
Optional per-claim ceiling in NOK. Claims exceeding this are rejected at validation time. | - |
max_distance_km |
decimal |
Optional per-claim maximum distance in km. HLF uses this for auto-approval threshold (claims ≤ configured km auto-approved). | - |
auto_approvable |
boolean |
When true and auto-approval rules are configured for the organization, claims of this type may bypass manual review if they satisfy all rule thresholds. | required |
requires_confidentiality_declaration |
boolean |
When true, the peer mentor must submit a confidentiality declaration before the expense is accepted. Used by Blindeforbundet for driver honorarium types. | required |
exclusivity_group |
string |
Mutual exclusivity group identifier. Expense types sharing the same non-null group value cannot be selected simultaneously in a single expense claim. E.g. 'travel_mode' groups km_allowance and public_transit so both cannot be claimed for the same trip. | - |
sort_order |
integer |
Display order within the expense type selector widget. Lower values appear first. | required |
is_active |
boolean |
Soft-delete flag. Inactive types are hidden from the expense form but preserved for historical claim validation and reporting. | required |
version |
integer |
Optimistic lock version counter incremented on every update. Enables the Expense Rule Validation Engine to detect and reject stale rule evaluations. | required |
metadata |
json |
Extensible key-value store for organization-specific configuration that does not warrant a dedicated column (e.g. Xledger account code, Dynamics voucher code for accounting export mapping). | - |
created_at |
datetime |
Timestamp when the record was first created. | required |
updated_at |
datetime |
Timestamp of the most recent update, maintained by application logic on every write. | required |
Database Indexes
idx_expense_type_org_active
Columns: organization_id, is_active
idx_expense_type_org_code
Columns: organization_id, code
idx_expense_type_exclusivity_group
Columns: organization_id, exclusivity_group
idx_expense_type_org_sort
Columns: organization_id, sort_order
Validation Rules
code_format_valid
error
Validation failed
name_not_empty
error
Validation failed
max_amount_positive
error
Validation failed
receipt_threshold_consistent
error
Validation failed
distance_fields_consistent
error
Validation failed
exclusivity_group_format
error
Validation failed
org_exists
error
Validation failed
cache_staleness_check
warning
Validation failed
Business Rules
mutual_exclusivity_enforced
Two or more expense types sharing the same exclusivity_group cannot be selected within a single expense claim. For example, kilometre allowance and public transit cannot be claimed simultaneously for the same trip. This prevents double-dipping on travel reimbursement and is technically enforced at both the mobile client and server-side validation layers.
org_scoped_catalogue
Every expense type belongs to exactly one organization. Queries for expense type catalogues must always include organization_id in the WHERE clause. Cross-organization type leakage is a data privacy violation.
active_only_in_new_claims
Only expense types with is_active=true are offered to users when creating a new expense claim. Inactive types may still appear on historical claims for read-only display.
version_increment_on_update
The version counter must be incremented on every UPDATE to support optimistic locking in the Expense Rule Validation Engine. The engine captures the version at the start of validation; if the version has changed by the time the expense is submitted, the validation is re-run.
confidentiality_declaration_gate
When requires_confidentiality_declaration=true, the peer mentor must have a valid, non-expired confidentiality declaration on file before the expense claim can be submitted. If no valid declaration exists, the Declaration Screen is presented inline before the expense form can be finalized.
receipt_threshold_enforced
When receipt_required_above_amount is set, the receipt requirement is evaluated dynamically at form submission time against the claimed amount. Below the threshold, the receipt is optional; at or above it, submission is blocked until a receipt is attached.
deactivation_preserves_history
Expense types are never hard-deleted. Setting is_active=false is the only permitted removal path. This ensures all historical expense claims retain a valid expense_type_id reference for audit and Bufdir reporting.
code_unique_per_org
The combination of (organization_id, code) must be unique. This enables deterministic referencing of expense types in API payloads, accounting export mappings, and auto-approval rule configurations without relying on UUIDs.
auto_approvable_requires_rule
Setting auto_approvable=true on an expense type has no effect unless at least one enabled auto-approval rule exists for the organization that references this type. The Auto-Approval Rule Engine silently falls through to manual approval when no matching rule is found.