high priority medium complexity backend pending backend specialist Tier 4

Acceptance Criteria

The PDF template is implemented using the Dart `pdf` package (pub.dev/packages/pdf) which runs in Deno/server context without Flutter widgets
Every page carries a consistent header: organisation name, org number, reporting period (formatted DD.MM.YYYY–DD.MM.YYYY), and page number
The first page includes an org logo placeholder area (60×60 pt box with 'ORG LOGO' text) that is replaced when a logo URL is provided
Each Bufdir category appears as its own named section with a table: columns Peer Mentor Name, Activity Count, Participant Count
A subtotals row at the bottom of each category table shows category-level sums in bold
A grand totals section on the final data page aggregates across all categories
If duplicate_warnings is non-empty, an appendix page is added titled 'Duplikatvarsler' with a table listing each warning
Page margins are 20 mm on all sides. Body font is a standard sans-serif (Helvetica or equivalent). Minimum body font size is 10 pt for accessibility
A4 page size (210×297 mm) is used throughout
The PDF document metadata includes Title ('Bufdir Rapport'), Author (org name), and CreationDate
The template renders correctly with zero data rows (empty tables show 'Ingen registreringer' placeholder text)

Technical Requirements

frameworks
Dart `pdf` package (dart pub package for server-side PDF generation)
Dart (Supabase Edge Function / Deno runtime)
apis
Supabase Storage (for retrieving org logo if stored there)
data models
BufdirPayload
CategoryGroup
PeerMentorTotal
DuplicateWarning
OrgMetadata
performance requirements
Template construction (without data binding) should complete in under 500 ms
Font embedding must use standard PDF fonts (Helvetica, Times) to avoid loading large font files in the edge function
security requirements
Logo images must be fetched from a pre-signed Supabase Storage URL — never from a user-supplied arbitrary URL to prevent SSRF
PDF must not embed JavaScript or interactive form fields

Execution Context

Execution Tier
Tier 4

Tier 4 - 323 tasks

Can start after Tier 3 completes

Implementation Notes

The Dart `pdf` package (not `printing`) works in server/Deno contexts and is the correct choice here — do not use Flutter's rendering pipeline. Import it as a pure Dart dependency. Define a `PdfReportTemplate` class with a `buildDocument(BufdirPayload payload, {Uint8List? logoBytes}) -> pw.Document` method.

Separate template construction (this task) from data binding (task-010) by using a builder pattern: the template class defines the page structure, header, footer, and section layouts; data binding populates the tables. Use `pw.MultiPage` for automatic page breaks. For the header/footer, use the `header` and `footer` parameters of `pw.MultiPage`. Norwegian text strings should be defined as constants in a separate `pdf_strings_no.dart` file to simplify future internationalisation.

Keep logo fetching out of the template class — the caller passes pre-fetched bytes or null.

Testing Requirements

Unit tests (dart:test) for the template layer: (1) calling buildTemplate() with a minimal mock payload returns a non-null Document object without exception, (2) document page count is at least 1 for any non-empty payload, (3) appendix page is added when duplicate_warnings is non-empty, (4) appendix page is absent when duplicate_warnings is empty, (5) document with empty category groups renders without exception and contains 'Ingen registreringer' text. Manual/visual test: generate a PDF from representative fixture data and review layout in a standard PDF viewer. Integration test: generate the full PDF via the edge function and assert Content-Type is 'application/pdf' and Content-Length > 0.

Component
PDF Generation Service
service medium
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.