critical priority low complexity backend pending backend specialist Tier 0

Acceptance Criteria

ActivityType Dart class defined with all required fields: id (String), orgId (String), labelKey (String), isActive (bool), triggersReimbursementWorkflow (bool), isTravelExpenseEligible (bool), requiresReport (bool), sortOrder (int), createdAt (DateTime)
ActivityType is immutable (all fields final, no setters) using Dart's const constructor where possible
ActivityType includes a copyWith() method for immutable updates
ActivityType includes fromJson(Map<String, dynamic>) factory constructor mapping snake_case DB fields to camelCase Dart fields
ActivityType includes toJson() method for serialization back to DB format
ActivityType overrides == and hashCode based on id field
IActivityTypeRepository interface declares: fetchActiveByOrg(String orgId), fetchAll(String orgId), fetchById(String orgId, String id), create(ActivityType type), update(ActivityType type), softDelete(String orgId, String id) — all returning Future<T>
IActivityTypeService interface declares: getActiveTypes(String orgId), createActivityType(...), updateActivityType(...), deactivateActivityType(String orgId, String id) — all returning Future<T>
All interface methods use typed return values (no dynamic or Object)
Interfaces are in separate files from the domain model (e.g., i_activity_type_repository.dart, i_activity_type_service.dart)
Unit tests pass for fromJson/toJson round-trip with all field types including nullable fields
Unit tests pass for == and hashCode equality checks

Technical Requirements

frameworks
Flutter
Riverpod
Dart
data models
activity_type
performance requirements
Domain model construction must be O(1) — no heavy computation in constructors
fromJson must handle null values for optional fields without throwing
security requirements
No sensitive org data embedded in domain model beyond orgId reference
labelKey must be treated as an opaque key, never evaluated as executable code

Execution Context

Execution Tier
Tier 0

Tier 0 - 440 tasks

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.

Component
Activity Type Service
service medium
Epic Risks (2)
medium impact medium prob scope

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.

high impact low prob technical

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.