critical priority low complexity backend pending backend specialist Tier 0

Acceptance Criteria

ExpenseType enum is defined in a dedicated Dart file and covers all reimbursable types specified in the HLF requirements: kilometre, transit, toll, parking, driverHonorarium (and any additional types from likeperson.md spec)
Each ExpenseType variant has a corresponding ExpenseTypeConfig entry in the exported const map
ExpenseTypeConfig contains at minimum: localisationKey (String), iconReference (String or IconData reference key), and formulaParameters (FormulaParameters object)
formulaParameters contains: rateMultiplier (double, NOK per km or fixed NOK), receiptThreshold (double, NOK), requiresDistance (bool), requiresReceipt (bool)
The const map is exported as a top-level constant (e.g., kExpenseTypeConfigs) accessible via import
All localisationKey values follow the app's existing ARB key naming convention (e.g., expenseType_kilometre_label)
All iconReference values reference valid icon identifiers consistent with the app's existing icon system
The enum and config compile without warnings under dart analyze
Mutual exclusion pairs are documented as a separate exported const (e.g., kMutualExclusionPairs) listing which types cannot be combined — this is required by tasks-012 and -013
No business logic or network calls in this file — pure data definitions only

Technical Requirements

frameworks
Flutter
Dart
data models
ExpenseType
ExpenseTypeConfig
FormulaParameters
performance requirements
All definitions are compile-time constants (const) to avoid runtime allocation
security requirements
No hardcoded monetary rates that differ per organisation — rates must be in FormulaParameters so they can be overridden from remote config in a later task

Execution Context

Execution Tier
Tier 0

Tier 0 - 440 tasks

Implementation Notes

This is the foundational data file for the entire expense selection epic — get the naming and structure right as all downstream tasks depend on it. Use an enhanced enum pattern in Dart: enum ExpenseType { kilometre, transit, toll, parking, driverHonorarium } with a separate const map rather than adding methods to the enum itself (this keeps the enum serialisable and avoids issues with Hive type adapters). Place the file at lib/features/expenses/domain/expense_type.dart. Define FormulaParameters as a simple immutable class with const constructor in a sibling file (task-002 will extend it).

For the mutual exclusion pairs, use a Set> or List<({ExpenseType a, ExpenseType b})> — ensure the representation is symmetric (kilometre conflicts with transit is the same as transit conflicts with kilometre). Seed the NOK rates from the HLF workshop notes: standard km rate (4.50 NOK/km per Norwegian state rate 2025), receipt threshold 100 NOK, auto-approval ceiling 50 km.

Testing Requirements

Unit tests verifying: (1) every ExpenseType variant has an entry in kExpenseTypeConfigs, (2) no config entry has a null or empty localisationKey, (3) requiresDistance is true only for kilometre type, (4) receiptThreshold is a positive number for all types where requiresReceipt is true, (5) kMutualExclusionPairs contains at least the kilometre/transit exclusion pair documented in the HLF spec. These are simple assertion-based tests with no mocking needed. Run with flutter test.

Component
Expense Type Configuration
data low
Epic Risks (2)
high impact medium prob scope

The compatibility matrix might be under-specified in source documentation. If a new organisation adds expense types or redefines rules, hardcoded pairwise logic becomes a maintenance liability and can silently allow previously excluded combinations.

Mitigation & Contingency

Mitigation: Model the matrix as a const Map<ExpenseType, Set<ExpenseType>> rather than if-else chains; add a unit test that exhaustively asserts every pair combination so any future matrix change forces explicit test updates.

Contingency: If per-organisation matrix variants are requested before the epic closes, extract matrix loading into expense-type-config with an org-override slot and defer per-org configuration to the repository epic.

medium impact medium prob technical

VoiceOver (iOS) and TalkBack (Android) handle Semantics widget announcements differently in Flutter. Live-region behaviour for disabled state changes is inconsistent across Flutter versions and may require platform-specific workarounds that are not yet documented.

Mitigation & Contingency

Mitigation: Write accessibility integration tests using Flutter's SemanticsController targeting both iOS and Android simulators from the outset; pin to a Flutter version known to handle Semantics.liveRegion correctly.

Contingency: If platform parity is unachievable before release, ship with a known gap documented in the WCAG audit log and schedule a dedicated accessibility sprint; do not block other epics.