high priority low complexity backend pending backend specialist Tier 2

Acceptance Criteria

`ReportPeriodValidatorImpl` is a concrete class implementing `ReportPeriodValidator`.
Rule 1: a range starting on day 1 of any month at midnight (00:00:00.000) and ending on the last day of any month at 23:59:59.999 passes the boundary check.
Rule 1: a range starting on any day other than the 1st returns `ValidationFailure(error: ReportPeriodValidationError.invalidBoundary)`.
Rule 1: a range ending on any day that is not the last day of its calendar month returns `ValidationFailure(error: ReportPeriodValidationError.invalidBoundary)`.
Rule 1: a range whose start time is not 00:00:00 or whose end time is not 23:59:59 returns `ValidationFailure(error: ReportPeriodValidationError.invalidBoundary)`.
Rule 2: a range where `start == end` or `start.isAfter(end)` returns `ValidationFailure(error: ReportPeriodValidationError.emptyRange)`.
Rule 2 is checked before rule 1 — an empty range returns `emptyRange`, not `invalidBoundary`.
The overlap rule (rule 3) is not yet invoked in this task — the impl defers to a placeholder or throws `UnimplementedError` for that rule.
All boundary checks are timezone-safe — dates are compared in local time, not UTC, unless the project standard specifies UTC (document the decision).
`dart analyze` reports zero errors or warnings on the implementation file.

Technical Requirements

frameworks
Flutter
Dart
data models
DateTimeRange
performance requirements
Boundary and empty-range checks must complete synchronously (no async overhead) before the async overlap check is invoked.
security requirements
Do not include orgId in any exception messages or debug output — it is a tenant identifier.

Execution Context

Execution Tier
Tier 2

Tier 2 - 518 tasks

Can start after Tier 1 completes

Implementation Notes

To compute the last day of a month in Dart: `DateTime(year, month + 1, 0)` returns day 0 of the next month, which is equivalent to the last day of the current month. Compare with `date.day == lastDay.day`. For midnight check: `date.hour == 0 && date.minute == 0 && date.second == 0 && date.millisecond == 0`. For end-of-day check: `date.hour == 23 && date.minute == 59 && date.second == 59`.

Do NOT use 23:59:59.999 as the end — confirm with the team whether end is inclusive-last-second or exclusive-midnight-next-day, then document the decision in a code comment. Ordering rules: check emptyRange first (start >= end), then boundary alignment. The third rule (overlap) will be added in task-006 — stub it with `throw UnimplementedError('Overlap check not yet implemented')` to make the partial implementation explicit.

Testing Requirements

Unit tests are written in the subsequent task (task-008). However, create a test file stub `test/features/bufdir/report_period/domain/report_period_validator_impl_test.dart` with placeholder `// TODO: task-008` comments so CI can discover the file.

The implementation itself must be fully testable with no static state.

Component
Report Period Validator
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.