high priority low complexity backend pending backend specialist Tier 1

Acceptance Criteria

PeriodPresetServiceImpl implements the PeriodPresetService interface defined in task-001
resolvePresets(orgId) returns all presets applicable to the organisation, including any custom grant-cycle presets defined in the org's PeriodConfigurationRepository record
resolveRange(currentCalendarYear, orgId) returns a DateTimeRange from Jan 1 00:00:00 to Dec 31 23:59:59 of the current UTC year, calendar-month-aligned
resolveRange(lastCalendarYear, orgId) returns the equivalent range for the previous UTC year
resolveRange(currentGrantCycle, orgId) reads the org's grant cycle start month from PeriodConfigurationRepository and computes the current cycle boundaries correctly (handles cycles crossing year boundaries, e.g. July–June)
resolveRange(customRange, orgId) throws an UnsupportedError — custom ranges are specified by the caller, not resolved by the service
periodPresetServiceProvider is a Riverpod Provider<PeriodPresetService> (or AsyncNotifierProvider if async initialisation is required) registered in the providers barrel file
Unit tests cover: standard calendar year, previous calendar year, a non-standard grant cycle (e.g., April–March), and the UnsupportedError for customRange

Technical Requirements

frameworks
Dart
Riverpod
flutter_test
data models
annual_summary
activity
bufdir_column_schema
performance requirements
resolvePresets() must complete within 500ms including the PeriodConfigurationRepository read
Date arithmetic must use UTC DateTime to avoid daylight-saving-time boundary errors
security requirements
orgId is validated as a non-empty string before any repository call — throw ArgumentError on empty/null
Repository access is scoped to the authenticated user's organisation via RLS — do not pass arbitrary orgId values from untrusted input

Execution Context

Execution Tier
Tier 1

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.

Component
Period Preset Service
service low
Epic Risks (2)
medium impact high prob dependency

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.

medium impact medium prob scope

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.