critical priority medium complexity backend pending backend specialist Tier 2

Acceptance Criteria

`buildOrderedFieldList(BufdirReportData data)` returns a `List<BufdirFieldEntry>` whose length equals the total number of fields across all canonical sections — no field is ever omitted
For a required field with no data value, the BufdirFieldEntry contains `value = 0` (numeric) or `value = null` with `isEmpty = true` — not a missing entry
The returned list is ordered identically to the canonical section/field order defined in kBufdirSections
BufdirFieldEntry contains: `definition` (BufdirFieldDefinition), `value` (dynamic), `isEmpty` (bool), `sectionIndex` (int), `fieldIndex` (int within section)
The function is pure — identical input always produces identical output
Calling the function with completely empty BufdirReportData still returns a full-length list of empty-sentinel entries
Calling the function with fully populated data returns a list where no entry has `isEmpty = true`

Technical Requirements

frameworks
Dart
Flutter
data models
BufdirReportData
BufdirFieldEntry
BufdirFieldDefinition
performance requirements
Function must run in O(n) where n = total number of canonical fields — no nested loops over data

Execution Context

Execution Tier
Tier 2

Tier 2 - 518 tasks

Can start after Tier 1 completes

Implementation Notes

Build a lookup Map from BufdirReportData first (fieldKey → value), then iterate kBufdirSections in order to produce BufdirFieldEntry objects — this keeps the function O(n). Use a numeric zero sentinel (0) for mandatory numeric fields and null for optional fields, distinguishing them via BufdirFieldDefinition.isMandatory. BufdirFieldEntry should be an immutable data class. This function is the primary adapter between raw aggregated data and the render layer — keep it free of any UI or formatting logic.

Testing Requirements

Unit tests: assert output length equals sum of all fields in kBufdirSections for any input. Test with empty BufdirReportData — all entries must have isEmpty = true. Test with fully populated data — no entries have isEmpty = true. Test with partial data — only fields with values have isEmpty = false.

Test output ordering matches kBufdirSections ordering by asserting sectionIndex and fieldIndex sequences. Edge case: test with a BufdirReportData that has extra unknown fields — they must not appear in output.

Component
Bufdir Report Structure Mapper
service medium
Epic Risks (2)
high impact medium prob integration

The preview repository depends on aggregated data produced by the Bufdir Data Aggregation feature. If the aggregation RPC schema or Supabase view columns change during parallel development, the repository's typed Dart models will break, causing compile errors or runtime null-dereference failures.

Mitigation & Contingency

Mitigation: Define a shared Dart interface (abstract class) for the aggregated data contract early and have both features code against it. Use Supabase typed generated clients so schema mismatches surface at code generation time rather than runtime.

Contingency: If the aggregation schema changes after the repository is complete, run `supabase gen types dart` immediately, update the repository model, and run repository unit tests before unblocking UI development. Keep a mock data fixture so UI work can continue during the fix.

high impact medium prob scope

The BufdirReportStructureMapper must map internal activity category IDs to the exact label strings used on the official Bufdir reporting form. If the mapping is incomplete or uses outdated labels, coordinators will see mismatches when cross-referencing the preview with the paper form, potentially leading to incorrect submissions.

Mitigation & Contingency

Mitigation: Obtain the current Bufdir reporting form PDF directly from Bufdir (Norse Digital Products has an existing Bufdir dialogue). Extract all field labels and section names into a static constants file reviewed by at least one coordinator from NHF or HLF before implementation begins.

Contingency: If incorrect labels are discovered during UAT on TestFlight, update the static constants file and redeploy. Because the mapper is a pure Dart class with no database storage, corrections require no migration — only a new build.