Expense Type
Data Entity
Description
Defines the fixed categories of allowable expenses (kilometers driven, toll road fees, parking, public transit). Expense types carry mutual exclusion group codes that prevent incompatible combinations from being selected simultaneously (e.g., kilometers and public transit cannot be claimed together). Configured per organization with reimbursement formula parameters.
Data Structure
| Name | Type | Description | Constraints |
|---|---|---|---|
id |
uuid |
Primary key for the expense type record | PKrequiredunique |
organization_id |
uuid |
Foreign key to the organization this expense type is configured for. Expense types are scoped per organization to allow different formula parameters and labels. | required |
key |
enum |
Machine-readable identifier for the expense type. Fixed set of four categories across the entire system. | required |
label |
string |
Organization-specific display label for this expense type. Resolved through the organization labels system to support different terminology per org. | required |
exclusive_group |
string |
Group code used to enforce mutual exclusion. Expense types sharing the same exclusive_group cannot be selected simultaneously. E.g., 'transport_mode' groups kilometers and public_transit as mutually exclusive. Null means no exclusion applies. | - |
reimbursement_formula |
json |
Structured formula parameters governing how reimbursement is calculated for this expense type. Schema varies by key: kilometers uses {type: 'per_km_rate', rate_per_km: decimal}; toll and parking use {type: 'receipt_amount', requires_receipt_above: decimal}; public_transit uses {type: 'declared_amount', max_claimable: decimal}. | required |
requires_distance |
boolean |
When true, the expense registration UI must collect a distance value in kilometers. Applies to the kilometers expense type. | required |
requires_amount |
boolean |
When true, the expense registration UI must collect a monetary amount. Applies to toll, parking, and public_transit expense types. | required |
receipt_threshold |
decimal |
Organization-configured monetary threshold above which a receipt image attachment becomes mandatory for this expense type. Null means no receipt is ever required for this type. | - |
is_active |
boolean |
Soft-delete flag. When false, this expense type is hidden from registration UI and excluded from validation but preserved for historical records. | required |
sort_order |
integer |
Display ordering of expense types within the picker UI. Lower values appear first. | required |
created_at |
datetime |
Timestamp when the expense type record was created. | required |
updated_at |
datetime |
Timestamp of last modification. Updated automatically on any field change. | required |
Database Indexes
idx_expense_type_org_id
Columns: organization_id
idx_expense_type_org_key
Columns: organization_id, key
idx_expense_type_exclusive_group
Columns: organization_id, exclusive_group
idx_expense_type_active
Columns: organization_id, is_active
Validation Rules
key_enum_constraint
error
Validation failed
label_non_empty
error
Validation failed
reimbursement_formula_json_parseable
error
Validation failed
receipt_threshold_non_negative
error
Validation failed
sort_order_non_negative
error
Validation failed
organization_exists
error
Validation failed
exclusive_group_max_length
error
Validation failed
formula_type_field_required
error
Validation failed
Business Rules
fixed_key_catalogue
Only four expense type keys are permitted system-wide: kilometers, toll, parking, public_transit. No organization may add custom expense type keys. This ensures the mutual exclusion matrix and reimbursement formula logic remain consistent across all feature components.
organization_unique_key
Each organization may have at most one active expense type record per key value. Attempting to create a duplicate (organization_id, key) pair must be rejected.
mutual_exclusion_group_consistency
Expense types assigned to the same exclusive_group within an organization are mutually exclusive at selection time. The kilometers and public_transit types must share an exclusive_group (e.g., 'transport_mode') because claiming mileage reimbursement and public transit reimbursement for the same trip is not permitted.
requires_distance_only_for_kilometers
The requires_distance flag must be true only for the kilometers expense type. All other types must have requires_distance set to false.
requires_amount_for_non_km_types
The requires_amount flag must be true for toll, parking, and public_transit expense types. The kilometers type calculates its amount from distance × rate and does not require a direct amount input.
reimbursement_formula_schema_valid
The reimbursement_formula JSON must conform to the schema for the given key: kilometers requires 'rate_per_km'; toll and parking require 'requires_receipt_above'; public_transit requires 'max_claimable'. Missing or malformed formula fields block expense calculations.
soft_delete_preserves_history
Expense types must never be hard-deleted. Setting is_active to false hides the type from new registrations while preserving all historical expense records that reference this type.
receipt_threshold_currency_consistency
The receipt_threshold value is always expressed in Norwegian Krone (NOK) and must be a non-negative decimal. A value of 0 means a receipt is always required; null means a receipt is never required regardless of amount.