Implement chapter affiliation retrieval and validation logic
epic-multi-chapter-membership-handling-core-services-task-003 — Implement the getAffiliationsForContact method in MultiChapterMembershipService. Fetch the current affiliation set from ContactChapterRepository, map results to ChapterAffiliation domain objects, and return the complete list. Validate that the contact exists before querying affiliations, throwing a typed domain error on failure.
Acceptance Criteria
Technical Requirements
Execution Context
Tier 1 - 540 tasks
Can start after Tier 0 completes
Implementation Notes
Implement the concrete class as MultiChapterMembershipServiceImpl extending or implementing AbstractMultiChapterMembershipService. Use Riverpod Provider or AsyncNotifierProvider to expose the service. The contact existence check should call a contactRepository.exists(contactId) method (or equivalent) — if ContactRepository is not yet abstracted, coordinate with the team to add a lightweight exists() method rather than fetching the full contact object. Handle the mapping from repository DTOs to ChapterAffiliation domain objects in a private static method or a dedicated mapper class to keep the service method readable.
Wrap all repository calls in try/catch and rethrow as ChapterMembershipFailure with an appropriate domain error subtype.
Testing Requirements
Write unit tests with a fake/mock ContactChapterRepository (implement the abstract interface in test, no actual Supabase calls). Test cases: (1) contact exists, returns 3 affiliations → success with list length 3; (2) contact exists, returns 0 affiliations → success with empty list; (3) contact not found → failure with ChapterNotFound error carrying correct contactId; (4) repository throws a network exception → failure with a generic infrastructure error type. Use flutter_test; no widget pump required. Mock the repository using Dart's mockito package or a hand-written fake.
Aim for 100% branch coverage of the getAffiliationsForContact method.
The ±1 day duplicate detection tolerance is specified in the acceptance criteria but timezone handling is not defined. A coordinator in UTC+2 submitting at 23:00 and another in UTC+0 submitting at 01:00 the next calendar day could trigger or miss a duplicate depending on which timezone the comparison uses.
Mitigation & Contingency
Mitigation: Define and document the authoritative timezone for all date comparisons (UTC stored in Supabase, all comparisons performed in UTC). Add timezone boundary unit tests covering the ambiguous ±1 day edges.
Contingency: If false positives or false negatives are reported in production, provide a coordinator-visible audit trail of duplicate detections so erroneous flags can be investigated and cleared manually.
The Duplicate Activity Detection Service performs a cross-chapter join query synchronously during the activity submission flow. On slow mobile connections this could cause a perceptible stall on the submission confirmation step, degrading user experience.
Mitigation & Contingency
Mitigation: Pre-fetch the cross-chapter activity dataset for the selected contact immediately when the contact is selected in the activity wizard (not only at submit time), storing the result in the state manager for instant comparison at submission.
Contingency: If latency is still unacceptable, implement a loading indicator on the submit action and add a configurable server-side timeout with graceful degradation: if the check times out, allow submission with a logged 'check skipped' audit entry rather than blocking the user.