Implement local draft persistence for expense selection
epic-expense-type-selection-core-services-task-010 — Implement the local draft storage path in ExpenseTypeRepository using shared_preferences or Hive. On every validated selection change received from the BLoC, serialize the current selection set (type IDs + user-entered amounts) and write it to the draft store keyed by claim context ID. Implement the loadDraft method that restores a prior selection on BLoC initialisation so app backgrounding does not lose user input.
Acceptance Criteria
Technical Requirements
Execution Context
Tier 2 - 518 tasks
Can start after Tier 1 completes
Implementation Notes
Define an abstract ExpenseDraftRepository interface with saveDraft, loadDraft, and clearDraft methods. Implement it as SharedPreferencesDraftRepository (or HiveDraftRepository). Register the implementation in the dependency injection layer (e.g., a provider or GetIt) so it can be swapped for a fake in tests. Use jsonEncode/jsonDecode for serialisation — keep the draft model a plain Dart class with a toJson/fromJson pair.
Avoid using Hive if shared_preferences is sufficient — this is a simple key-value store. The HLF workshop requirement that 'systemene må leve side ved side' underscores that mid-flow data must survive app restarts during the rollout period when users may switch between old and new registration paths.
Testing Requirements
Unit tests with flutter_test. Mock the underlying shared_preferences or Hive adapter using a fake in-memory implementation of the repository interface. Test cases: (1) save draft → load draft returns same data, (2) load draft for unknown key returns null, (3) save invalid data → load returns null and entry is cleared, (4) clear draft → subsequent load returns null, (5) multiple claim context IDs stored independently without interference. Do not test shared_preferences or Hive internals — only test the repository contract.
The per-km reimbursement rate and transit zone amounts must be read from org-specific configuration stored in Supabase. If the rate configuration table or RLS policies are not yet deployed when this epic runs, the calculation service cannot be completed and integration tests will fail.
Mitigation & Contingency
Mitigation: Define a RateConfigRepository interface and inject a stub implementation with default HLF rates from day one; write the real Supabase adapter in parallel and swap via dependency injection before merge.
Contingency: If org rate config is delayed beyond this epic's window, ship with the default-rate stub and log a prominent warning; calculate with defaults and surface a 'rates not confirmed' notice in the UI preview.
If the peer mentor opens an expense claim on two devices simultaneously, the local draft and the Supabase record may diverge. The repository's last-write-wins strategy could silently overwrite a valid selection with a stale one.
Mitigation & Contingency
Mitigation: Add an updated_at timestamp to the draft record and reject saves where the server timestamp is newer than the local copy; surface a conflict resolution prompt rather than silently overwriting.
Contingency: If conflict resolution UI is out of scope, fall back to server-authoritative reads on app foreground resume and discard local draft, notifying the user that their draft was refreshed from the server.