Implement Supabase mileage adapter domain-to-JSON mapping
epic-mileage-reimbursement-entry-infrastructure-task-002 — Implement the SupabaseMileageAdapter class that translates MileageClaim and OrgConfiguration domain objects to and from Supabase JSON row format. Include serialisation, deserialisation, null-safety handling, and field mapping validation.
Acceptance Criteria
Technical Requirements
Execution Context
Tier 1 - 540 tasks
Can start after Tier 0 completes
Implementation Notes
Keep the adapter as a pure Dart class in the infrastructure layer (e.g. lib/features/mileage/infrastructure/supabase_mileage_adapter.dart). Do not use code generation (json_serializable) for this adapter — hand-written mapping gives explicit control over null handling and validation which is critical here. Define a MappingException class (or reuse an existing domain exception) with a final String field and message.
For the status enum, prefer a switch expression with an exhaustive default clause that throws rather than returning a fallback value — silent fallbacks mask data integrity issues. Document the field name mapping table (Dart name → Supabase column name) in a comment block at the top of the class for future maintainers.
Testing Requirements
Write unit tests using flutter_test (no Supabase connection required). Cover: (1) happy-path round-trip for MileageClaim with all fields populated; (2) happy-path round-trip for MileageClaim with all nullable fields set to null; (3) happy-path round-trip for OrgConfiguration; (4) fromJson throws MappingException when a required field is missing; (5) fromJson throws MappingException when status has an unrecognised value; (6) DateTime round-trip preserves UTC timezone; (7) numeric fields deserialise as double when Supabase returns them as int (common with integer columns). Aim for 100% line coverage of the adapter class.
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.