critical priority high complexity backend pending backend specialist Tier 1

Acceptance Criteria

BufdirExcelGenerator produces a valid .xlsx binary that opens without errors in Microsoft Excel, LibreOffice Calc, and Google Sheets
Output file contains exactly the columns specified in Bufdir's reporting template in the correct order (columns must be confirmed against the actual Bufdir Word/Excel template provided by the organizations)
Header row is present on row 1 with exact Norwegian column labels as required by Bufdir
Each data row maps BufdirActivityRecord fields to the correct column positions with no field transposition errors
Date columns are formatted as DD.MM.YYYY (Norwegian date format) using Excel number format, not as plain text strings
Duration columns display in hours with one decimal place (e.g., 1.5 for 90 minutes)
Column widths are set to accommodate the longest expected value in each column without truncation
Numeric columns (participant counts, hours) use numeric cell types — not string — so Excel can sum them
Generator accepts List<BufdirActivityRecord> and returns Future<Uint8List> (raw .xlsx bytes)
Generated file is deterministic: same input always produces byte-for-byte equivalent output (excluding embedded timestamps)
Unit test compares generated Excel structure (sheet name, column headers, row count, cell values) against expected values

Technical Requirements

frameworks
Dart
excel (pub.dev dart Excel library)
Flutter
data models
BufdirActivityRecord
BufdirExcelColumnMapping
BufdirReportingPeriod
performance requirements
Generate an Excel file for 500 activity rows in under 2 seconds on a mid-range device
Keep peak memory usage under 50MB during generation — avoid loading all bytes into memory twice
Use streaming or chunked row writing if the excel library supports it for large datasets
security requirements
Generated files must not embed user credentials, auth tokens, or Supabase connection strings in Excel metadata
Strip or sanitize any formula-like strings in cell values (values starting with =, +, -, @) to prevent CSV/formula injection
File is generated in memory (Uint8List) and handed to the caller — it is never written to a permanent app directory without explicit user action

Execution Context

Execution Tier
Tier 1

Tier 1 - 540 tasks

Can start after Tier 0 completes

Implementation Notes

Use the 'excel' package (pub.dev) as it is the most mature Dart-native xlsx library with no native code dependencies — suitable for both Flutter mobile and potential server-side Dart use. Define a BufdirColumnDefinition list that maps each column to: {columnIndex, headerLabel, fieldExtractor: (BufdirActivityRecord) -> dynamic, cellFormat}. This makes the column ordering the single source of truth and prevents transposition bugs. The column definitions should live in a separate BufdirExcelColumnSchema class so they can be reviewed and updated independently when Bufdir changes their template.

For date formatting: store dates as actual Excel date serial numbers (use the excel library's TextCellValue with pre-formatted string if native date type is not supported, otherwise use DoubleCellValue with the Excel date format '31/12/1899' epoch). Norwegian date format (DD.MM.YYYY) must be verified to display correctly regardless of the opening user's locale settings — prefer embedding the format in the cell's number format style rather than relying on the system locale. Confirm the exact Bufdir column template with the client (NHF/HLF/Blindeforbundet) before finalizing the column schema, as requirements state this should match Bufdir's official layout.

Testing Requirements

Unit tests using flutter_test with fixture-based approach: create a known List input, generate the Excel file, then parse the output bytes back using the same excel library and assert cell values, types, and positions. Test cases: (1) single activity row — verify all column values and date formatting, (2) 50-row dataset — verify row count matches input, (3) empty input — verify only header row present with no data rows, (4) activity with null optional fields — verify empty cells rather than 'null' string, (5) Norwegian characters in activity type — verify UTF-8 encoding preserved in xlsx, (6) numeric column type verification — assert cells are CellType.Int or CellType.Double not CellType.String. Additionally create a golden file test that saves the generated Excel to a test fixture and compares structure on CI.

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.