Expense Types
Data Entity
Description
Organisation-scoped catalogue of allowable expense categories with mutual-exclusivity rules, documentation requirements, amount thresholds, and approval policies. Each organisation configures its own set of expense types to enforce reimbursement business rules specific to their Bufdir funding agreements.
Data Structure
| Name | Type | Description | Constraints |
|---|---|---|---|
id |
uuid |
Unique identifier for the expense type record | PKrequiredunique |
organization_id |
uuid |
Foreign key to the owning organisation — expense types are tenant-scoped | required |
name |
string |
Display name of the expense type shown in selector widgets (e.g. 'Kilometre Allowance', 'Public Transport', 'Parking') | required |
slug |
string |
URL-safe machine identifier used in API payloads and rule references, unique within an organisation | required |
description |
text |
User-facing explanation of what this expense type covers, displayed in the selector widget tooltip | - |
category |
enum |
High-level classification of the expense type for grouping and reporting | required |
is_active |
boolean |
Soft-delete flag — inactive types are hidden from selectors but preserved for historical expense records | required |
requires_receipt |
boolean |
Whether a receipt photo attachment is mandatory when submitting an expense of this type | required |
receipt_threshold_amount |
decimal |
Amount (in NOK) above which a receipt becomes mandatory even if requires_receipt is false. Null means no threshold-based receipt requirement. | - |
max_amount |
decimal |
Maximum claimable amount per single expense of this type. Null means no cap. | - |
unit |
enum |
Unit of measurement for the expense — determines which input fields the form renders | required |
rate_per_unit |
decimal |
Default reimbursement rate per unit (e.g. NOK per km). Null for fixed_amount types where the user enters the total. | - |
mutual_exclusivity_group |
string |
Group key for mutual exclusivity — expense types sharing the same group key cannot be combined on a single expense claim (e.g. 'transport' group prevents selecting both km and bus ticket) | - |
requires_declaration |
boolean |
Whether a signed confidentiality or driver declaration is required before submitting this expense type | required |
declaration_type |
enum |
Type of declaration required when requires_declaration is true | - |
auto_approval_eligible |
boolean |
Whether expenses of this type can be auto-approved by the auto-approval rule engine | required |
auto_approval_max_amount |
decimal |
Maximum amount for automatic approval of this type. Claims above this go to manual review. Null defers to org-level auto-approval rules. | - |
auto_approval_max_distance_km |
decimal |
Maximum distance in km for automatic approval of per_km types (HLF: under 50 km auto-approved). Null means no distance-based auto-approval. | - |
accounting_code |
string |
Default account code mapping for accounting system export (Xledger/Dynamics). Can be overridden per-org in accounting integration settings. | - |
bufdir_category_code |
string |
Bufdir reporting category code for mapping this expense type to the required Bufdir report format | - |
display_order |
integer |
Sort position in the expense type selector widget. Lower numbers appear first. | required |
rules_json |
json |
Extensible JSON object encoding additional business rules — field visibility conditions, conditional requirements, custom validation logic | - |
created_at |
datetime |
Timestamp when the expense type was created | required |
updated_at |
datetime |
Timestamp of the last modification to this expense type record | required |
created_by |
uuid |
User ID of the administrator who created this expense type | - |
Database Indexes
idx_expense_types_organization_id
Columns: organization_id
idx_expense_types_org_slug
Columns: organization_id, slug
idx_expense_types_org_active
Columns: organization_id, is_active
idx_expense_types_mutual_exclusivity
Columns: organization_id, mutual_exclusivity_group
idx_expense_types_org_display_order
Columns: organization_id, display_order
idx_expense_types_category
Columns: organization_id, category
Validation Rules
unique_slug_per_organisation
error
Validation failed
valid_organisation_reference
error
Validation failed
rate_required_for_unit_types
error
Validation failed
declaration_type_consistency
error
Validation failed
threshold_amount_non_negative
error
Validation failed
name_not_blank
error
Validation failed
slug_format_validation
error
Validation failed
auto_approval_distance_only_for_per_km
error
Validation failed
rules_json_schema_validation
error
Validation failed
display_order_non_negative
error
Validation failed
Business Rules
organisation_scoped_isolation
Expense types are always scoped to a single organisation. Queries must filter by organization_id to enforce multi-tenant data isolation. A user can only see and use expense types belonging to their own organisation.
mutual_exclusivity_enforcement
Expense types sharing the same mutual_exclusivity_group value cannot be combined on a single expense submission. When a user selects one type from a group, all other types in that group must be disabled in the selector. Example: 'km allowance' and 'public transport' in group 'transport' cannot coexist on one claim.
receipt_threshold_escalation
If an expense amount exceeds receipt_threshold_amount for its type, a receipt attachment becomes mandatory regardless of the requires_receipt flag. HLF requires receipts for claims over 100 NOK.
soft_delete_preservation
Expense types must never be hard-deleted if any expense records reference them. Deactivation (is_active = false) hides the type from new submissions while preserving historical data integrity for reporting and audit.
auto_approval_eligibility_gating
Only expense types with auto_approval_eligible = true can be evaluated by the auto-approval rule engine. Types with this flag set to false always require manual coordinator or admin review regardless of amount or distance.
distance_auto_approval_for_travel
For per_km expense types, claims under auto_approval_max_distance_km are eligible for automatic approval. Claims above the threshold are routed to manual approval. Derived from HLF requirement: under 50 km without additional expenses = auto-approved.
declaration_prerequisite
When requires_declaration is true, the user must have a valid, unexpired declaration of the specified declaration_type on file before an expense of this type can be submitted. Derived from Blindeforbundet driver honorarium and confidentiality requirements.
max_amount_cap
If max_amount is set, any expense claim of this type exceeding the cap must be rejected at submission time with a clear error message indicating the maximum allowed amount.
rate_per_unit_calculation
For unit-based types (per_km, per_hour, per_day), the total reimbursement amount is calculated as quantity * rate_per_unit. The user enters the quantity; the rate is fixed by the organisation configuration.
admin_only_configuration
Only Organisation Administrators and Global Administrators can create, update, or deactivate expense types. Coordinators and Peer Mentors have read-only access to the active type catalogue.