Implement persistence via MileageClaimRepository with rollback on failure
epic-mileage-reimbursement-entry-claim-orchestration-task-005 — After status is resolved, persist the finalized MileageClaim (with its resolved ClaimStatus) via MileageClaimRepository. Wrap the persistence call in error handling that propagates a typed failure result to the caller on exception, preventing any partial state from being treated as success. Confirm the claim ID is returned on success.
Acceptance Criteria
Technical Requirements
Execution Context
Tier 4 - 323 tasks
Can start after Tier 3 completes
Implementation Notes
MileageClaimRepository.save() should return `Future
The 'rollback on failure' in the task description refers to the service-level guarantee: never return SubmissionSuccess unless save() completed without exception. There is no multi-step transaction to manually roll back. Document this clearly in the service code. After completing this task, run `flutter analyze` across all five tasks' files as a final integration check before marking the epic complete.
Ensure the Supabase RLS policy is defined in a migration file (not applied manually) so it is repeatable across environments.
Testing Requirements
Two levels of testing required. (1) Unit tests using `flutter_test` with mocked MileageClaimRepository: verify save() called exactly once; SubmissionSuccess returned with correct claimId on mock success; SubmissionFailure returned when save() throws a generic Exception; SubmissionFailure returned when save() throws a Supabase-specific exception (PostgrestException). (2) Integration test against a local Supabase instance (via Docker or Supabase CLI): insert a valid MileageClaim and assert the returned claimId is a valid UUID, then query the `mileage_claims` table and assert the row exists with correct field values including `resolved_status`. Integration tests are optional for CI but required before merging to main.
Tag integration tests with `@Tags(['integration'])` so they can be excluded from fast unit test runs.
The auto-approval rule requires checking whether any additional expense lines are attached to the claim. The interface between the mileage claim and any co-submitted expense items is not fully defined within this feature's component scope. If the domain model does not include an explicit additionalExpenses collection, the evaluator cannot make a correct determination, which could auto-approve claims that should require manual review.
Mitigation & Contingency
Mitigation: Define the MileageClaim domain object interface with an explicit additionalExpenses: List field (nullable/empty for mileage-only claims) before implementing the service. Coordinate with the Expense Type Selection feature team to agree on the shared domain contract.
Contingency: If the cross-feature contract cannot be finalised before implementation, implement the evaluator to treat any non-null additionalExpenses list as requiring manual review and document the assumption for review during integration testing.
A peer mentor who taps the submit button multiple times rapidly (e.g. due to slow network) could cause MileageClaimService to be invoked concurrently, resulting in duplicate claim records being persisted with the same trip data.
Mitigation & Contingency
Mitigation: Implement a submission-in-progress guard in MileageClaimService using a BLoC/Cubit state flag that prevents re-entrant calls. The UI layer (implemented in Epic 4) will also disable the submit button during processing.
Contingency: Add a Supabase-level unique constraint or idempotency key on (user_id, origin, distance, submitted_at truncated to minute) to prevent duplicate rows reaching the database even if the application guard fails.