critical priority low complexity backend pending backend specialist Tier 1

Acceptance Criteria

Abstract interface IClaimReceiptRepository is defined with methods: insert(ClaimReceiptAttachment), getByClaimId(String claimId), deleteById(int id), updateSyncStatus(int id, SyncStatus status)
ClaimReceiptRepository implements IClaimReceiptRepository against the sqflite/drift database instance
insert() returns the auto-generated integer id of the new row
getByClaimId() returns a List<ClaimReceiptAttachment> ordered by created_at DESC
deleteById() returns true if a row was deleted, false if no row matched the id
updateSyncStatus() returns true on success and throws a typed ReceiptRepositoryException on database error
ClaimReceiptAttachment is a Dart immutable data class (or freezed) with all schema fields as typed properties
A Riverpod Provider<IClaimReceiptRepository> is exported for use by BLoC/notifier layers
No raw SQL strings outside the repository implementation class — all queries are encapsulated
Repository constructor accepts the database instance via dependency injection for testability

Technical Requirements

frameworks
Flutter
Riverpod
sqflite or drift
data models
ClaimReceiptAttachment
performance requirements
getByClaimId() must complete in under 10ms for claims with up to 50 receipt attachments
security requirements
Repository must not log receipt_storage_path values to console or crash logs
All database operations must execute on a background isolate/thread, never the UI thread

Execution Context

Execution Tier
Tier 1

Tier 1 - 540 tasks

Can start after Tier 0 completes

Implementation Notes

Define an abstract interface first (IClaimReceiptRepository) before writing the implementation — this enables the Riverpod provider to return the interface type, making future mock substitution trivial. Use a SyncStatus enum (pending, synced, failed) mapped to/from TEXT in SQLite rather than raw strings throughout the Dart codebase. The ClaimReceiptAttachment model should be immutable — use either freezed or a hand-written copyWith. Keep the Riverpod provider in a separate providers file (claim_receipt_providers.dart) rather than inside the repository file to follow the project's existing provider structure.

If the project uses drift, define the table as a drift Table class so type safety is compile-time; if sqflite, write a ClaimReceiptAttachment.fromMap() factory and toMap() method on the model.

Testing Requirements

See task-003 for full unit test coverage. The repository implementation must be designed for testability: accept the database instance via constructor injection so tests can pass an in-memory database. Do not use a global singleton database reference inside the repository.

Component
Claim Receipt Repository
data low
Epic Risks (3)
high impact medium prob security

Supabase Storage RLS policies using org/user/claim path scoping may not enforce correctly if claim ownership is not present in the JWT or if path segments are constructed differently at upload vs. read time, leading to data leakage or access denial for legitimate users.

Mitigation & Contingency

Mitigation: Define and test RLS policies in isolation before wiring to app code. Write integration tests that assert cross-org and cross-user access is denied. Use service-role key only in edge functions, never in client code.

Contingency: If client-side RLS proves insufficient, route all storage reads through a Supabase Edge Function that validates ownership before generating signed URLs, adding a controlled server-side enforcement layer.

high impact medium prob technical

Aggressive image compression may reduce receipt legibility below the threshold required for financial auditing, causing claim rejections or compliance failures despite technically successful uploads.

Mitigation & Contingency

Mitigation: Define minimum legibility requirements with HLF finance team before implementation. Set compression targets conservatively (e.g., max 1MB, min 80% JPEG quality) and validate with sample receipt images. Provide compression statistics in verbose/debug mode.

Contingency: If post-compression quality is disputed by auditors, increase the quality floor at the cost of larger file sizes, and add a manual override allowing users to skip compression for PDFs and high-quality scans.

medium impact medium prob dependency

The Flutter image_picker package behaves differently on iOS 17+ (PHPicker) vs older Android (Intent-based), particularly for file types, permission flows, and PDF selection, which may cause platform-specific failures not caught in development.

Mitigation & Contingency

Mitigation: Test image picker integration on physical devices for both platforms early in the sprint. Pin the image_picker package version and review changelogs before updates. Write widget tests using mock file results for each platform branch.

Contingency: If PHPicker or Android Intent differences cause blocking issues, implement separate platform-specific picker delegates behind the unified interface, allowing platform-specific fixes without breaking the shared API.