Define PeriodPreset domain model and interfaces
epic-bufdir-report-period-selection-services-task-001 — Create the PeriodPreset domain model class, the PeriodPresetIdentifier enum (e.g., currentCalendarYear, lastCalendarYear, currentGrantCycle), and the abstract interface for PeriodPresetService including the resolvePresets(orgId) and resolveRange(identifier, orgId) method signatures. Establish the DateTimeRange value object used across all three services.
Acceptance Criteria
Technical Requirements
Implementation Notes
Keep this layer entirely free of Flutter, Riverpod, and Supabase imports — it must be testable as plain Dart. Use Dart's built-in DateTimeRange if it satisfies requirements; if not, define a custom one and name it clearly (e.g., PeriodDateRange) to avoid import conflicts with Flutter's DateTimeRange. Define the abstract interface (abstract class PeriodPresetService) rather than a mixin or extension type so Riverpod providers can return concrete implementations without losing type safety. Place all domain models under lib/domain/period_selection/ to establish a clear domain boundary that implementation layers depend on, not the reverse.
This task produces the contract that tasks -002 and -003 implement — keep it stable after merging.
Testing Requirements
Unit tests for DateTimeRange value object: (1) two instances with identical start/end are equal (==) and share the same hashCode, (2) ArgumentError is thrown when start > end, (3) boundary case: start == end is valid (zero-duration range). Unit tests for PeriodPreset: (1) const constructor produces expected field values. No mocking required — pure domain logic only. 100% line coverage expected given the simplicity.
Detecting overlap with previously submitted reports requires querying a report history table that may not yet exist or may not have a reliable submitted_at / period_end field, making the validator dependent on an incomplete upstream feature (Bufdir Report History & Audit Log).
Mitigation & Contingency
Mitigation: Define the minimum interface (a single repository method: getSubmittedPeriods(orgId) → List<DateTimeRange>) as an abstract class in this epic. Implement a stub that returns an empty list until the history feature is available, so the validator compiles and passes tests without a real data source.
Contingency: If the history feature is delayed beyond this feature's delivery window, ship the validator with the stub returning an empty list (overlap check disabled) and surface a feature-flag-controlled warning banner explaining that overlap detection will be enabled in a future update.
Bufdir's structural requirements for reporting periods (complete calendar months, grant-year span restrictions) may be ambiguous or subject to change, causing the validator to enforce rules that are incorrect or overly restrictive for some organisations.
Mitigation & Contingency
Mitigation: Document the specific Bufdir rules being enforced in the validator's source code as named constants with references to the relevant Bufdir guidelines. Review the rules with at least one coordinator representative before implementation is finalised.
Contingency: Expose a per-org configuration flag (strict_bufdir_validation: bool) in the period configuration repository so that rule enforcement can be relaxed for specific organisations without a code deployment.