critical priority low complexity backend pending backend specialist Tier 0

Acceptance Criteria

MileageClaim class is a pure Dart domain entity with no Flutter or Supabase imports
All required fields present: claimId (String), userId (String), routeDescription (String), distanceKm (double), reimbursementAmount (double), status (MileageClaimStatus enum: pending/approved/rejected), createdAt (DateTime), updatedAt (DateTime), deletedAt (DateTime?)
MileageClaimStatus is defined as a sealed class or enum with all three states and a fromString factory
MileageClaim implements equality (== and hashCode) based on claimId
MileageClaim includes a copyWith method for immutable updates
Abstract MileageClaimRepository defines: Future<void> insert(MileageClaim claim), Future<List<MileageClaim>> fetchByUser(String userId, {int page, int pageSize}), Future<List<MileageClaim>> fetchForCoordinator(String orgId, {MileageClaimStatus? statusFilter}), Future<void> updateStatus(String claimId, MileageClaimStatus status), Future<void> softDelete(String claimId, DateTime correctionWindowEnd)
softDelete interface throws a domain-specific CorrectionWindowExpiredException if the window has passed
All method signatures use typed return values — no dynamic or Object
Domain model and interface are co-located in the same domain layer directory (e.g., lib/domain/mileage_claim/)

Technical Requirements

frameworks
Dart (latest)
Flutter
data models
MileageClaim
MileageClaimStatus
performance requirements
Domain model must be instantiable in O(1) with no I/O
copyWith must not perform deep copies of unchanged fields
security requirements
No sensitive user data (e.g., personnummer) stored directly in the domain model — use opaque userId reference only
reimbursementAmount must be validated as non-negative at construction time

Execution Context

Execution Tier
Tier 0

Tier 0 - 440 tasks

Implementation Notes

Keep the domain model completely free of infrastructure concerns. Do not import supabase_flutter, shared_preferences, or any Flutter framework package. Use a freezed-style immutable pattern if the team already uses the freezed package; otherwise write a manual copyWith. Define CorrectionWindowExpiredException as a typed exception in the domain layer.

The repository interface should follow the Repository pattern from Domain-Driven Design — the interface belongs in the domain layer, the concrete implementation belongs in the data layer. Name the file mileage_claim_repository.dart and place it under lib/domain/mileage_claim/.

Testing Requirements

Unit tests (flutter_test): Verify MileageClaim construction with all fields, equality based on claimId, copyWith produces correct updated instance, MileageClaimStatus.fromString parses all three values and throws on unknown input. The abstract interface itself does not require tests at this stage — concrete adapter tests are covered in task-005.

Component
Mileage Claim Repository
data medium
Epic Risks (2)
medium impact medium prob integration

If OrgRateConfigRepository caches the per-km rate aggressively and an admin updates the rate mid-session, ongoing form interactions will show the old rate until the Stream emits. This could result in the UI showing a rate that differs from what is stored when the claim is submitted, causing confusion or disputes.

Mitigation & Contingency

Mitigation: Subscribe to a Supabase Realtime channel for the org_configuration table so config changes propagate within seconds. Document the eventual-consistency window in code comments.

Contingency: If Realtime subscription proves unreliable in test, add a polling fallback with a configurable interval (default 5 minutes) and display a 'rate updated' toast when the stream emits a changed value.

medium impact medium prob scope

The correction window within which a claim can be deleted or voided is not explicitly specified in the feature documentation. Implementing the wrong window (e.g. 24 hours vs 7 days) could lock users out of corrections or allow inappropriate post-approval modifications.

Mitigation & Contingency

Mitigation: Raise the correction window definition as a blocking question to the HLF product owner before implementing the delete/void path in MileageClaimRepository. Implement the window duration as an org-level configuration value rather than a hardcoded constant.

Contingency: If the question cannot be resolved before implementation, default to 24 hours as the most conservative option and flag the value for review in the first user-acceptance test.