Define MileageClaim and OrgConfiguration domain models
epic-mileage-reimbursement-entry-infrastructure-task-005 — Define the Dart domain model classes for MileageClaim (distance, route, claim status, expense attachments, submission metadata) and OrgConfiguration (rate per km, auto-approval threshold in km, receipt requirement threshold). Ensure immutability with copyWith support.
Acceptance Criteria
Technical Requirements
Implementation Notes
Do not use code generation (freezed, json_serializable) for domain models — keeping them hand-written keeps the domain layer free of build_runner dependencies and reduces compilation time. Implement copyWith manually using named optional parameters typed as Object? with a sentinel default (e.g. static const _unset = Object()) to distinguish null-as-value from not-provided.
This pattern supports nullable field overrides in copyWith without ambiguity. The MileageClaimStatus enum values should use camelCase (draft, submitted, approved, rejected) matching Dart conventions — the adapter layer (task-002) is responsible for mapping these to/from the snake_case strings stored in Supabase. Place both classes in separate files (mileage_claim.dart, org_configuration.dart) under lib/features/mileage/domain/models/.
Testing Requirements
Write unit tests using flutter_test. Cover: (1) MileageClaim constructor creates instance with all fields set correctly; (2) MileageClaim.copyWith changes only the specified field; (3) two MileageClaim instances with the same id are == regardless of other field values; (4) two MileageClaim instances with different ids are not ==; (5) same tests for OrgConfiguration with organizationId as the equality key; (6) MileageClaim constructor throws AssertionError (in debug) when distanceKm <= 0; (7) OrgConfiguration constructor throws AssertionError when ratePerKm <= 0; (8) MileageClaimStatus enum has exactly four values matching the database constraint set. Target 100% line coverage.
Supabase Row Level Security policies for mileage_claims may require complex join conditions to distinguish peer mentor (own claims only) from coordinator (chapter-scoped claims) access. If the RLS policy is misconfigured, coordinators could see claims outside their chapter scope or peer mentors could read other users' data, causing a data privacy incident.
Mitigation & Contingency
Mitigation: Write RLS policy SQL as part of this epic with explicit test cases for each role. Use Supabase's built-in policy testing tools and add integration tests that assert cross-user data isolation before merging.
Contingency: If RLS configuration proves too complex to test reliably within the epic, add an application-layer guard in the adapter that filters query results by authenticated user ID as a defence-in-depth measure while the policy is corrected.
Norwegian tax authority reimbursement rounding rules may change or may not be publicly documented in machine-readable form. Using the wrong rounding convention could cause systematic over- or under-payment, leading to compliance issues for the organisation.
Mitigation & Contingency
Mitigation: Source the exact rounding specification from HLF's finance team before implementing MileageCalculationService. Document the rule as a comment in the source code and link to the authoritative reference.
Contingency: If the rule is ambiguous, implement both truncation and half-up rounding behind a configuration flag so the organisation can switch without a code release.
SharedPreferences reads and writes are asynchronous. If the distance prefill service (built in the next epic) calls LocalDistanceCache concurrently with a post-submission write, a race condition could result in the cache returning a stale or partially written value, causing the next form load to show an incorrect default.
Mitigation & Contingency
Mitigation: Wrap all SharedPreferences access in LocalDistanceCache with sequential async operations and document the non-concurrent usage contract in the class API.
Contingency: If race conditions are observed in testing, introduce a simple mutex pattern using a Completer to serialise cache operations.