configuration PK: id 9 required 1 unique

Description

A classification for the nature of an expense claim — mileage, toll, parking, transit, or other — with associated validation rules and mutual-exclusion constraints. Organizations configure which types are available and which combinations are prohibited to prevent invalid submissions.

18
Attributes
4
Indexes
8
Validation Rules
12
CRUD Operations

Data Structure

Name Type Description Constraints
id uuid Primary key — globally unique identifier for the expense type record
PKrequiredunique
organization_id uuid Foreign key to the organizations table — expense types are scoped per organization; each org defines its own permitted types and rules
required
name string Human-readable display label for the expense type, subject to organization terminology overrides (e.g., 'Mileage', 'Toll', 'Parking', 'Public Transit', 'Other')
required
slug string Machine-readable identifier used in API payloads, validation logic, and mutual-exclusion rule references. Immutable after creation to preserve referential integrity in expense records.
required
category enum Classifies the expense type into a billing category that drives valuation rules, receipt requirements, and Bufdir reporting field mapping
required
requires_receipt_above_nok decimal Monetary threshold (in NOK) above which a receipt photo must be attached before the expense can be submitted. NULL means receipt is never required for this type. HLF configures this at 100 NOK for toll, parking, and transit types.
-
incompatible_with json Array of expense type slugs that cannot be combined with this type in a single reimbursement claim. Enforces HLF's requirement that mileage and public transit cannot be claimed simultaneously. Stored as a JSON array of slug strings.
-
auto_approve_below_km decimal For mileage-category types only: distance threshold (in km) below which claims are automatically approved without coordinator attestation. NULL means all claims require manual approval. HLF configures this at 50 km.
-
auto_approve_below_nok decimal For non-mileage types: monetary amount (in NOK) below which claims are automatically approved. NULL means all claims require manual approval. Complements auto_approve_below_km for mixed reimbursement bundles.
-
min_km decimal Minimum distance in km required for a mileage claim to be valid. Applicable only when category = 'mileage'. NULL means no minimum enforced.
-
max_km decimal Maximum distance in km allowed per single mileage claim. Applicable only when category = 'mileage'. Claims exceeding this value are rejected at submission time.
-
max_amount_nok decimal Maximum monetary amount (in NOK) allowed per single claim of this type. Claims exceeding this value are rejected at submission time. Prevents runaway expense submissions.
-
is_enabled boolean Whether this expense type is currently available for selection by peer mentors in the organization. Disabled types remain in the database for historical expense record integrity but are hidden from the type selector widget.
required
display_order integer Controls the display order of expense types in the Expense Type Selector Widget. Lower values appear first. Organizations can reorder types to surface the most common ones.
required
icon_key string Design system icon identifier to render alongside the expense type label in the selector widget (e.g., 'car', 'highway', 'parking', 'bus', 'receipt'). Must match a key defined in the design token system.
-
bufdir_field_code string The official Bufdir reporting field code this expense type maps to. Used by the Bufdir Field Mapper Service to aggregate expenses into the correct report columns. NULL if this type does not map to a Bufdir reporting field.
-
created_at datetime Timestamp when this expense type configuration record was created
required
updated_at datetime Timestamp of the most recent modification to this expense type's configuration
required

Database Indexes

idx_expense_type_organization_id
btree

Columns: organization_id

idx_expense_type_org_slug_unique
btree unique

Columns: organization_id, slug

idx_expense_type_org_enabled_order
btree

Columns: organization_id, is_enabled, display_order

idx_expense_type_category
btree

Columns: category

Validation Rules

slug_format_check error

Validation failed

slug_unique_within_org error

Validation failed

incompatible_with_references_valid_slugs error

Validation failed

incompatible_with_is_symmetric warning

Validation failed

auto_approve_km_only_for_mileage error

Validation failed

receipt_threshold_non_negative error

Validation failed

km_range_consistency error

Validation failed

name_not_blank error

Validation failed

Business Rules

mutual_exclusion_enforcement
on_create

An expense claim cannot include two expense types that are listed in each other's incompatible_with arrays. For example, if 'mileage' lists 'transit' as incompatible and 'transit' lists 'mileage', a reimbursement bundle cannot contain both. This prevents HLF's case of claiming both km reimbursement and a bus ticket for the same trip.

receipt_required_above_threshold
on_create

When an expense of this type is submitted with an amount exceeding requires_receipt_above_nok, a receipt photo must be attached before the submission is accepted. The Expense Registration Screen prompts for receipt upload when the threshold is crossed.

auto_approval_threshold_evaluation
on_create

When a reimbursement claim is submitted, the Reimbursement Approval Service checks auto_approve_below_km (for mileage types) and auto_approve_below_nok (for all types) to determine if automatic approval applies. Claims below both applicable thresholds are auto-approved without coordinator action.

disabled_type_submission_blocked
always

Expense types with is_enabled = false must not appear in the Expense Type Selector Widget and must be rejected at the API level if submitted. Historical expense records referencing a disabled type remain valid and intact.

slug_immutability
on_update

Once created, the slug field must not be changed. Existing expense records reference this type by slug for validation and mutual-exclusion rule evaluation. Changing a slug would silently break historical cross-reference integrity.

organization_scoped_type_visibility
always

Expense types must only be returned to users whose JWT organization_id matches the expense type's organization_id. Cross-organization type leakage is prevented at the repository query level.

mileage_distance_range_validation
on_create

For expense types in the 'mileage' category, the submitted km distance must fall within [min_km, max_km] when those bounds are configured. Claims outside the range are rejected with a descriptive error message.

amount_ceiling_enforcement
on_create

For non-mileage expense types, the submitted NOK amount must not exceed max_amount_nok when configured. This prevents unreasonably large individual toll, parking, or transit claims from bypassing coordinator review.

Storage Configuration

Storage Type
lookup_table
Location
main_db
Partitioning
No Partitioning
Retention
Permanent Storage

Entity Relationships

expense
outgoing one_to_many

An expense type classifies each expense claim and drives validation and mutual-exclusion rules

required
organization
incoming one_to_many

An organization configures the available expense types and their validation rules for its peer mentors

required