Implement LocalDistanceCache with SharedPreferences backend
epic-mileage-reimbursement-entry-infrastructure-task-004 — Build the LocalDistanceCache class backed by SharedPreferences to persist previously entered origin-destination distance values keyed by a route hash. Implement read, write, invalidate, and TTL-expiry logic for cached entries. The cache must be synchronous and safe to call from widget-layer code.
Acceptance Criteria
Technical Requirements
Implementation Notes
Use shared_preferences package (already a standard Flutter dependency). Store each cache entry as two keys: distance_cache_v1_{hash}_km (double as string) and distance_cache_v1_{hash}_ts (int epoch milliseconds). This avoids JSON serialization overhead for a simple two-field structure. For synchronous reads, require the SharedPreferences instance to be passed in at construction (constructor injection) — the caller is responsible for awaiting SharedPreferences.getInstance() before constructing LocalDistanceCache.
This keeps the public API synchronous while remaining testable. The Riverpod provider for this class should be a FutureProvider that resolves after SharedPreferences.getInstance() completes, then exposes the constructed LocalDistanceCache synchronously.
Testing Requirements
Write unit tests using flutter_test with SharedPreferences.setMockInitialValues() to simulate pre-populated cache state. Cover: (1) getDistance returns null for missing key; (2) saveDistance then getDistance returns correct value; (3) saveDistance then getDistance after TTL expiry returns null and removes entry; (4) invalidate removes only the targeted entry; (5) clearAll removes all namespaced entries without affecting unrelated SharedPreferences keys; (6) saveDistance with distanceKm <= 0 throws ArgumentError; (7) getDistance with empty routeHash throws ArgumentError; (8) two saves with same routeHash — second overwrites first. Target 100% branch 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.