high priority low complexity backend pending backend specialist Tier 4

Acceptance Criteria

The service accepts a validated BufdirPayload and returns a Uint8List representing the UTF-8 encoded CSV
The first row is a header row with Norwegian column labels matching Bufdir's expected field names exactly
Each subsequent row represents one (peer_mentor, bufdir_category) combination
Columns include: peer_mentor_name, peer_mentor_id, category_code, category_label_no, activity_count, participant_count, reporting_period_start, reporting_period_end, organisation_number
Dates are formatted as DD.MM.YYYY (Norwegian convention)
Numeric fields (counts) are plain integers with no thousand separators and no decimal points
Text fields containing commas or double-quotes are properly RFC 4180 quoted
The CSV uses semicolons as delimiters (Norwegian Excel convention), not commas
The file begins with a UTF-8 BOM (EF BB BF) so Norwegian Excel opens it with correct encoding without manual import steps
An empty payload (zero categories) produces a CSV with only the header row β€” no exception
A summary row at the end aggregates grand totals across all peer mentors and categories
The returned filename suggestion is `bufdir_rapport_{org_number}_{YYYY-MM}.csv`

Technical Requirements

frameworks
Dart (Supabase Edge Function / Deno runtime)
dart:convert (utf8 encoder)
dart:typed_data (Uint8List)
apis
Bufdir CSV column specification (internal reference)
data models
BufdirPayload
CategoryGroup
PeerMentorTotal
performance requirements
CSV generation for 500 rows must complete in under 100 ms
Build the CSV as a StringBuffer, convert to bytes in one pass β€” avoid repeated string concatenation
security requirements
All string fields must be sanitised for CSV injection: values starting with =, +, -, @ must be prefixed with a tab character or single quote per OWASP CSV injection prevention guidance
Do not include any internal Supabase UUIDs in exported columns

Execution Context

Execution Tier
Tier 4

Tier 4 - 323 tasks

Can start after Tier 3 completes

Implementation Notes

Use a StringBuffer for construction efficiency. Define the column order as a Dart const List of header labels β€” this becomes the single source of truth for both the header row and the data row field ordering. Use semicolons as delimiters because Norwegian Excel defaults to semicolons. The UTF-8 BOM is critical for coordinator usability β€” without it, Norwegian characters (Γ¦, ΓΈ, Γ₯) appear corrupted when the file is opened directly in Excel.

Add the BOM as the first three bytes before encoding the StringBuffer content: `final bom = Uint8List.fromList([0xEF, 0xBB, 0xBF])`. For the summary row, use a distinct label in the peer_mentor_name column (e.g. 'TOTALT') so it is visually distinct. Keep the service as a pure function: `Uint8List generateCsv(BufdirPayload payload)` β€” no I/O, no Supabase client.

Testing Requirements

Unit tests (dart:test): (1) output begins with UTF-8 BOM bytes [0xEF, 0xBB, 0xBF], (2) header row contains all required Norwegian column labels in correct order, (3) date fields are formatted DD.MM.YYYY not ISO format, (4) numeric fields contain no commas or dots, (5) a field containing a semicolon is properly double-quoted per RFC 4180, (6) empty payload produces header-only output with no exception, (7) summary row totals match sum of all data rows (arithmetic assertion), (8) output is valid UTF-8 (decoded without error), (9) CSV injection prevention: field starting with '=' is prefixed. Integration test: generate CSV from the task-006 serializer output and parse it back with a CSV parser; assert row count equals number of (peer_mentor Γ— category) combinations in the input.

Component
CSV Generation Service
service low
Epic Risks (3)
high impact medium prob technical

Supabase Edge Functions have a default execution timeout. For large national-scope exports aggregating tens of thousands of activities across 1,400 chapters, the edge function may time out before completing, leaving coordinators with a failed export and no partial output.

Mitigation & Contingency

Mitigation: Optimise the aggregation SQL using pre-materialised aggregation views or RPC functions that run inside the database rather than iterating records in Deno. Profile query execution time against realistic production data volumes early. Request an elevated timeout limit from Supabase if needed. Implement progress checkpointing so the export can be resumed from the last completed aggregation batch.

Contingency: For organisations exceeding a configurable threshold (e.g. >5,000 activities), switch to an asynchronous export pattern: the edge function writes a 'pending' audit record and enqueues the job; the client polls for completion and is notified via Supabase Realtime when the file is ready.

medium impact medium prob technical

Server-side PDF generation in a Deno Edge Function environment restricts library choices. Many popular PDF libraries require Node.js APIs not available in Deno, or produce large bundle sizes that exceed edge function limits. Choosing the wrong library could block the entire PDF generation path.

Mitigation & Contingency

Mitigation: Spike PDF library selection as the first task of this epic, evaluating at least two Deno-compatible options (e.g. pdf-lib, jsPDF with Deno compatibility shim). Test bundle size and basic rendering before committing to an implementation. Document the chosen library's constraints.

Contingency: If no suitable Deno-native PDF library is found, generate a well-structured HTML report from the edge function and use a headless Chromium service (e.g. Browserless, Gotenberg) for HTML-to-PDF conversion, or temporarily ship CSV-only export while the PDF path is resolved.

high impact high prob technical

Peer mentors affiliated with multiple chapters (a documented NHF scenario) must not be double-counted in participant totals. Incorrect deduplication logic would overreport participation figures to Bufdir, which could be discovered during audit and damage organisational credibility.

Mitigation & Contingency

Mitigation: Define and document the deduplication contract explicitly before coding: deduplication is per-person per-period, not per-activity. Build dedicated unit tests with fixtures containing the exact multi-chapter membership patterns described in NHF's documentation. Have a NHF representative validate test fixture outputs against known-good manual counts.

Contingency: If deduplication logic produces results that cannot be verified against manual counts before launch, surface a deduplication warning in the export preview listing the affected peer mentor IDs, and require explicit coordinator acknowledgement before finalising the export.