Bufdir Report
Data Entity
Description
A generated compliance report conforming to Bufdir (Norwegian Directorate for Children, Youth and Family Affairs) grant reporting requirements. Aggregates activity counts, contact categories, and participant totals across the organization hierarchy for a given reporting period. All four partner organizations depend on Bufdir funding and must submit these reports.
Data Structure
| Name | Type | Description | Constraints |
|---|---|---|---|
id |
uuid |
Primary key. Universally unique identifier for the report record. | PKrequiredunique |
organization_id |
uuid |
Foreign key referencing the organization that owns this report. All data is scoped to this organization and its full hierarchy (regions + local associations). | required |
reporting_period_start |
datetime |
Inclusive start date of the Bufdir reporting period (typically Jan 1 or start of a quarter). Stored as date-only at midnight UTC. | required |
reporting_period_end |
datetime |
Inclusive end date of the Bufdir reporting period (typically Dec 31 or end of a quarter). Must be after reporting_period_start. | required |
period_label |
string |
Human-readable label for the reporting period, e.g. '2025', 'Q1 2025', 'H1 2025'. Computed at generation time and stored for display. | required |
status |
enum |
Lifecycle state of the report. 'generating' is set when the async job starts; 'complete' when the file is ready; 'failed' when aggregation or export encountered an unrecoverable error. | required |
format |
enum |
Output file format requested by the user. XLSX uses semicolon delimiter matching Norwegian Excel locale; CSV for interoperability; JSON for in-app preview rendering. | required |
generated_at |
datetime |
Timestamp when the report generation job completed successfully and the file was written to storage. NULL while status is draft or generating. | - |
generated_by |
uuid |
Foreign key referencing the user (Coordinator, Org Admin, or Global Admin) who triggered this report generation. | required |
storage_key |
string |
Object storage path for the exported report file (e.g. 'bufdir-reports/{org_id}/{year}/{report_id}.xlsx'). NULL until status is complete. | unique |
report_data |
json |
Full aggregated Bufdir-mapped data payload as structured JSON. Contains all Bufdir field values, activity breakdowns by type and contact category, participant totals, and hierarchy rollups. Used for in-app preview and as the source for file export. | - |
total_activity_count |
integer |
Denormalized total count of approved activities included in the report period for quick display without parsing report_data. | - |
total_participant_count |
integer |
Denormalized total count of unique participants (contacts + event attendees) across all activities in the period. | - |
total_hours |
decimal |
Denormalized sum of activity durations in hours across the reporting period. Used for KPI display on the report screen. | - |
bufdir_schema_version |
string |
The Bufdir reporting schema version used when generating this report (e.g. '2025-v1'). Required to interpret report_data correctly if the schema evolves. | required |
validation_warnings |
json |
Array of pre-export validation warning objects (e.g. missing activity types, unapproved activities, incomplete contact categories). Each entry has: { code, message, severity, affected_count }. Empty array means no warnings. | - |
error_message |
text |
Human-readable error description when status = 'failed'. Contains the root cause of the generation failure for admin review. NULL for non-failed reports. | - |
hierarchy_scope |
json |
Snapshot of the organizational hierarchy nodes included in this report at generation time: { organization_id, region_ids[], local_association_ids[] }. Stored to ensure report reproducibility even if hierarchy changes later. | - |
created_at |
datetime |
Timestamp when the report record was first created (when the generation job was enqueued). | required |
updated_at |
datetime |
Timestamp of the last status update or data write. Updated on every status transition. | required |
Database Indexes
idx_bufdir_report_org_period
Columns: organization_id, reporting_period_start, reporting_period_end, format
idx_bufdir_report_org_status
Columns: organization_id, status
idx_bufdir_report_org_generated
Columns: organization_id, generated_at
idx_bufdir_report_generated_by
Columns: generated_by
idx_bufdir_report_status
Columns: status
idx_bufdir_report_storage_key
Columns: storage_key
Validation Rules
period_dates_chronological
error
Validation failed
organization_exists
error
Validation failed
period_not_in_future
error
Validation failed
format_is_valid_enum
error
Validation failed
generated_by_has_membership
error
Validation failed
period_minimum_duration
error
Validation failed
validation_warnings_is_valid_json_array
error
Validation failed
duplicate_in_progress_check
error
Validation failed
report_data_schema_conformance
error
Validation failed
Business Rules
authorized_roles_only
Only users with Coordinator, Organization Administrator, or Global Administrator roles may create or access Bufdir reports. Peer Mentor role is explicitly blocked. Enforced at the API layer before any database operation.
organization_scope_isolation
A report may only be generated for an organization that the requesting user belongs to with a sufficient role. Global Administrators may generate for any organization; Org Admins and Coordinators are restricted to their own organization_id.
full_hierarchy_inclusion
Report aggregation MUST include all local associations and regions under the organization, not just the requesting user's local association. The Report Aggregation Service traverses the full org hierarchy to ensure complete Bufdir submission coverage.
approved_activities_only
Only activities with approval_status = 'approved' are included in Bufdir report aggregation. Pending, flagged, or rejected activities are excluded. A pre-export validation warning is surfaced if significant unapproved activity count is detected.
status_transition_enforcement
Report status must follow the defined lifecycle: draft → generating → complete OR draft → generating → failed. Direct transitions skipping intermediate states are rejected. A 'complete' or 'failed' report cannot be moved back to 'generating'.
bufdir_field_completeness
Before finalizing a report as 'complete', all mandatory Bufdir reporting fields must be populated. The Bufdir Field Mapper validates required fields and blocks completion if any are missing. Missing optional fields produce warnings in validation_warnings.
organization_terminology_mapping
Each organization uses different terminology for activity types and contact categories. The Bufdir Field Mapper applies organization-specific label overrides from organization_labels to ensure field names in the exported file match the Bufdir schema regardless of internal terminology.
no_future_periods
A report cannot be generated for a reporting period whose end date is in the future. Partial-period generation is blocked to prevent submission of incomplete data to Bufdir.
storage_key_immutable_after_completion
Once a report reaches status = 'complete' and a storage_key is assigned, the storage_key must not be overwritten. A new report record must be created for any re-generation. This ensures audit trail integrity.
hierarchy_snapshot_on_creation
When a report is created, the current organizational hierarchy (organization, regions, local associations) is snapshotted into hierarchy_scope. This ensures the report remains reproducible and auditable even if the hierarchy changes after generation.
CRUD Operations
Storage Configuration
Entity Relationships
An organization generates multiple Bufdir compliance reports across reporting periods