high priority medium complexity backend pending backend specialist Tier 2

Acceptance Criteria

BufdirCsvGenerator produces a valid UTF-8 with BOM (.csv) file that opens correctly in Microsoft Excel on Norwegian Windows locale without character corruption
File uses semicolon (;) as field delimiter β€” not comma β€” matching Norwegian Excel default CSV expectations
Field ordering matches exactly the column order defined in BufdirExcelColumnSchema (shared schema from task-004)
Header row uses the same Norwegian column labels as the Excel variant
Date values are formatted as DD.MM.YYYY strings in the CSV output
Duration values are formatted as decimal hours with comma as decimal separator (1,5 not 1.5) for Norwegian locale compatibility
Fields containing semicolons, double quotes, or newlines are properly quoted per RFC 4180
UTF-8 BOM (0xEF, 0xBB, 0xBF) is prepended as the first three bytes of the file
Generator returns Future<Uint8List> consistent with the Excel generator API
CSV output can be opened as a human-readable review document independent of any reporting workflow

Technical Requirements

frameworks
Dart
Flutter
data models
BufdirActivityRecord
BufdirExcelColumnSchema
performance requirements
CSV generation for 500 rows completes in under 500ms β€” CSV is simpler than xlsx and should be significantly faster
Use StringBuffer for row construction β€” avoid string concatenation in a loop
security requirements
Apply the same formula injection protection as the Excel generator: sanitize cell values starting with =, +, -, @
CSV files containing personal data must be treated as sensitive β€” file must not be cached in a public app storage directory

Execution Context

Execution Tier
Tier 2

Tier 2 - 518 tasks

Can start after Tier 1 completes

Implementation Notes

Reuse BufdirExcelColumnSchema from task-004 to extract field values β€” only the serialization format differs between Excel and CSV, not the field ordering or label logic. Implement BufdirCsvGenerator as a sibling class to BufdirExcelGenerator, both sharing the same schema. Use a simple custom serializer rather than a heavy CSV library since requirements are straightforward (semicolon delimiter, RFC 4180 quoting, BOM). The decimal separator for Norwegian locale is comma β€” ensure duration values use '1,5' format not '1.5' to prevent Excel from treating the field as text.

Prepend BOM by starting the StringBuffer with 'ο»Ώ' (Dart Unicode escape for BOM) before writing any content. For RFC 4180 compliance: wrap any field in double quotes if it contains a semicolon, double-quote, newline, or carriage return; escape embedded double-quotes by doubling them ('""'). Keep the generator stateless (pure function: input records β†’ output bytes) for testability.

Testing Requirements

Unit tests using flutter_test. Test cases: (1) UTF-8 BOM presence β€” verify first 3 bytes are [0xEF, 0xBB, 0xBF], (2) semicolon delimiter β€” verify no commas appear as delimiters in a simple row, (3) field with embedded semicolon is quoted correctly, (4) field with embedded double-quote is escaped as double-double-quote per RFC 4180, (5) Norwegian characters (Γ¦, ΓΈ, Γ₯) render correctly when decoded as UTF-8, (6) date format is DD.MM.YYYY, (7) duration decimal uses comma separator (1,5), (8) empty activity list produces header row only. Verify the CSV is parseable by a reference Dart CSV parser (csv package) after generation.

Component
Excel / CSV File Generator
infrastructure medium
Epic Risks (3)
high impact medium prob technical

NHF contacts can belong to up to five local chapters simultaneously. If the deduplication logic in the activity query service incorrectly attributes cross-chapter activities, organisations will either under-report or over-report to Bufdir, which could trigger grant clawback or compliance investigations.

Mitigation & Contingency

Mitigation: Implement deduplication using the existing multi-chapter membership service as the source of truth for chapter affiliation. Write test fixtures covering all known multi-chapter edge cases and validate outputs against manually prepared reference exports from NHF.

Contingency: If deduplication cannot be made deterministic for complex hierarchies before release, gate the export behind an org-level feature flag and require NHF to validate a preview export against their manual Excel before enabling in production.

medium impact medium prob dependency

Server-side Dart libraries for Excel generation are less mature than equivalents in Node.js or Python. The chosen library may lack support for Bufdir-required formatting features (merged cells, data validation, specific date formats), requiring significant workaround effort or a library switch mid-implementation.

Mitigation & Contingency

Mitigation: Evaluate the top two Dart xlsx libraries (excel, spreadsheet_decoder) against a Bufdir template sample file before committing. Identify all required formatting features and verify library support in a spike.

Contingency: If no Dart library meets requirements, implement the Excel generation as a Supabase Edge Function in TypeScript using the well-supported ExcelJS library, exposing it to the Dart backend via an internal RPC call.

medium impact medium prob integration

The attachment bundler must retrieve documents from Supabase Storage that were uploaded by the document attachments feature. If storage paths, RLS policies, or signed URL expiry have not been standardised across features, the bundler may fail to retrieve attachments at export time.

Mitigation & Contingency

Mitigation: Audit the document attachments feature's storage schema and RLS policies before implementing the bundler. Agree on a stable internal service-account access pattern for cross-feature storage reads.

Contingency: If cross-feature storage access cannot be made reliable, implement the bundler to include only attachments that can be retrieved successfully and produce a manifest listing any attachments that could not be bundled, rather than failing the entire export.