Define ActivityType domain model and interfaces
epic-activity-type-configuration-business-logic-task-001 — Create the typed ActivityType domain object with all metadata fields: id, orgId, label key, isActive, triggersReimbursementWorkflow, isTravelExpenseEligible, requiresReport, sortOrder, and createdAt. Define the IActivityTypeRepository and IActivityTypeService interfaces that downstream implementations will satisfy.
Acceptance Criteria
Technical Requirements
Implementation Notes
Place domain model in lib/features/activity_type/domain/models/activity_type.dart and interfaces in lib/features/activity_type/domain/repositories/i_activity_type_repository.dart and lib/features/activity_type/domain/services/i_activity_type_service.dart following clean architecture layer separation. Use freezed or manual immutability — if the project already uses freezed for other models, stay consistent. The labelKey field stores a string key (e.g., 'peer_support_visit') that will be resolved to a human-readable label by OrganizationLabelsProvider in task-005; do NOT store the resolved label in this model. The triggersReimbursementWorkflow and isTravelExpenseEligible flags are metadata used for UI conditional rendering and business rule validation in task-004 — model them as required booleans with no default to force explicit handling at every call site.
Testing Requirements
Unit tests using flutter_test covering: (1) fromJson correctly maps all snake_case DB fields including edge cases (null requiresReport defaults to false), (2) toJson produces correct snake_case keys, (3) round-trip fromJson→toJson produces identical map, (4) copyWith correctly updates individual fields while preserving others, (5) equality: two ActivityType objects with same id are equal regardless of other field values, (6) interface contracts: mock classes implementing IActivityTypeRepository and IActivityTypeService compile without error. Minimum 90% line coverage on the domain model file.
Metadata flag combination rules differ between organisations (e.g., Blindeforbundet honorarium thresholds, HLF mutual exclusion of km and transit). Encoding these as generic service-level validation may be insufficient, forcing organisation-specific branching inside the service that becomes unmaintainable as new organisations are onboarded.
Mitigation & Contingency
Mitigation: Model flag validation as a pure function that accepts an ActivityTypeMetadata object and an org configuration record, making org-specific rules data-driven rather than hardcoded. Establish the validation contract in the foundation epic so the service just delegates to the validator.
Contingency: Defer complex cross-flag validation to a lightweight edge function that can be updated without a mobile app release, accepting that initial validation in the mobile service layer is permissive and corrected server-side.
Blindeforbundet users rely on VoiceOver and JAWS. If the selection screen is built with non-semantic widgets that fail accessibility audit late in the sprint, a significant rework of the widget tree may be required, blocking the registration wizard integration.
Mitigation & Contingency
Mitigation: Build the selection screen against the project's established accessibility design tokens and semantics wrapper conventions from the start. Run Flutter's semantic tree inspector and a manual VoiceOver pass before marking any widget task complete.
Contingency: Wrap all tappable items in the project's SemanticsWrapperWidget and schedule a dedicated accessibility review session with a screen reader user from Blindeforbundet before the epic is closed.