Implement expense repository with offline cache
epic-travel-expense-registration-foundation-task-004 — Build the ExpenseRepository Dart class wrapping Supabase queries for CRUD operations on expense claims and line items. Implement offline write queue using a local SQLite or Hive store so claims created in low-connectivity areas are synced automatically when connectivity is restored. Expose typed streams for claim status changes via Supabase Realtime.
Acceptance Criteria
Technical Requirements
Execution Context
Tier 2 - 518 tasks
Can start after Tier 1 completes
Implementation Notes
Use the Repository pattern with a clear interface (abstract class IExpenseRepository) to allow easy mocking in BLoC/Cubit unit tests. For the offline queue, prefer Hive over SQLite to avoid schema migration complexity — the queue only needs a simple list of pending operations serialized as JSON. Use the connectivity_plus package's Stream
Be careful with Realtime — subscribe only to claims the current user owns or is authorised to observe (leverage RLS on realtime). Implement a SyncStatus sealed class (Pending / Syncing / Synced / Failed) and expose it as a stream so UI can show offline indicators. Use Riverpod's keepAlive on the repository provider to preserve Realtime subscriptions across widget rebuilds.
Testing Requirements
Unit tests (flutter_test + mocktail): mock the Supabase client, test each repository method for happy path and error cases including RLS denial (403), network timeout, and FK violation. Integration tests: spin up local Supabase instance via Docker, run actual CRUD operations against the expense_claims schema from task-003, verify RLS enforcement. Offline tests: simulate network unavailability using a mock connectivity stream, create a claim, verify it lands in the offline queue, restore connectivity, verify it syncs. Realtime tests: mock the Supabase channel and verify the stream emits correct status values.
Minimum 80% line coverage on repository class.
Row-level security policies for expense claims must correctly scope data to organisation, role (peer mentor sees own claims only, coordinator sees org-wide queue), and claim status. Incorrect RLS can expose claims cross-organisation or prevent coordinators from accessing the attestation queue.
Mitigation & Contingency
Mitigation: Define RLS policies in code-reviewed migration files. Write integration tests that attempt cross-org reads with different JWT roles and assert access denial. Review with a second engineer before merging migrations.
Contingency: If RLS is misconfigured post-deployment, disable the affected policy temporarily and apply a hotfix migration within the same release window. No claim data is exposed publicly due to Supabase project-level auth requirement.
The auto-approval Edge Function is triggered server-side on expense insert. Cold-start latency or Edge Function failures can block the submission response and degrade UX, especially on mobile networks.
Mitigation & Contingency
Mitigation: Implement the auto-approval Edge Function client with a timeout and graceful fallback: if no result is received within 5 seconds, treat the claim as 'pending' and poll for the status update via Supabase Realtime. Keep the Edge Function warm with a periodic ping.
Contingency: If Edge Function reliability is unacceptable, move auto-approval evaluation to a database trigger or Postgres function as an interim measure, accepting that threshold configuration changes require a migration rather than a settings update.
The expense type catalogue and threshold configuration are cached locally for offline use. If an organisation updates their catalogue exclusion rules or thresholds while a peer mentor is offline, the local cache may allow submissions that violate the new policy.
Mitigation & Contingency
Mitigation: Cache entries include a TTL (24 hours). On connectivity restore, refresh cache before allowing new submissions. Server-side validation in the Edge Function and save functions provides a second enforcement layer.
Contingency: If a stale-cache submission passes client validation but fails server validation, surface a clear error message explaining that the expense type rules have been updated and prompt the user to review their selection with the refreshed catalogue.