Implement PeriodPresetService with Riverpod provider
epic-bufdir-report-period-selection-services-task-002 — Implement the PeriodPresetService class that reads org-specific preset configuration from the PeriodConfigurationRepository, maps PeriodPresetIdentifier values to concrete DateTimeRange instances (calendar-month-aligned), and handles organisations with non-standard grant cycles via config overrides. Register as an injectable Riverpod provider (periodPresetServiceProvider).
Acceptance Criteria
Technical Requirements
Execution Context
Tier 1 - 540 tasks
Can start after Tier 0 completes
Implementation Notes
Inject a clock abstraction (e.g., a ClockService or simply a DateTime Function() nowFn parameter) rather than calling DateTime.now() directly — this makes all date logic fully testable without time-of-day flakiness. Grant cycles crossing year boundaries require careful arithmetic: if grantCycleStartMonth > currentMonth, the current cycle started in the previous calendar year. Use UTC throughout to avoid DST ambiguity, especially for Norway's CET/CEST transitions. Register the provider with a family modifier if different org configurations are needed simultaneously: periodPresetServiceProvider(orgId).
Keep business logic (date arithmetic) in pure methods extractable for unit testing without Riverpod infrastructure.
Testing Requirements
Unit tests with mocked PeriodConfigurationRepository (use Riverpod ProviderContainer with overrides). Test cases: (1) standard calendar year 2025 returns Jan 1 – Dec 31 2025 UTC, (2) last calendar year during January 2026 returns Jan 1 – Dec 31 2025, (3) grant cycle starting April returns April 1 current year – March 31 next year, (4) grant cycle starting July where current date is before July returns July 1 previous year – June 30 current year (cycle not yet started), (5) customRange identifier throws UnsupportedError. Use clock injection or DateTime.now() override (pass a clock parameter) to make date-dependent tests deterministic.
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.