high priority medium complexity integration pending integration specialist Tier 3

Acceptance Criteria

An abstract Dart class (or interface) `BufdirApiIntegrationService` is defined with method signatures for `submitExportPackage`, `getSubmissionStatus`, and `handleResponseCode`
A concrete `BufdirFileDeliveryService` class implements the interface using file-based delivery: writes the export package to Supabase Storage and returns a `BufdirDeliveryResult` containing download URLs and package metadata
A configuration enum `BufdirDeliveryMode` with values `fileDelivery` and `apiSubmission` controls which implementation is injected at runtime
The active delivery mode is read from a server-side configuration value (e.g. Supabase Edge Function environment variable `BUFDIR_DELIVERY_MODE`) — never hardcoded
Switching the configuration flag from `fileDelivery` to `apiSubmission` requires no changes to calling code — only the injected implementation changes
The `BufdirDeliveryResult` model includes: delivery_mode, timestamp, organisation_id, report_period_id, package_urls (list of signed URLs), and status (`pending_review`, `submitted`, `failed`)
The file delivery stub records its output in the `bufdir_export_audit_log` table with status `pending_review` to indicate manual coordinator download is required
All methods on the interface return typed `Future` results — no dynamic or untyped returns
A placeholder `BufdirDirectApiService` class exists (throws `UnimplementedError`) for the future direct submission implementation, satisfying the interface contract
The service is registered in the Riverpod provider graph so it can be overridden in tests without modifying production code

Technical Requirements

frameworks
Flutter
Riverpod
Supabase Dart SDK
apis
Supabase Storage API
Supabase PostgreSQL (bufdir_export_audit_log write)
Bufdir Reporting API (interface contract only — not yet implemented)
data models
bufdir_export_audit_log
bufdir_column_schema
performance requirements
File delivery stub must complete its Supabase Storage write and audit log update within 5 seconds for packages up to 50 MB
Service instantiation via Riverpod must be lazy — not eagerly initialised at app startup
security requirements
Configuration flag `BUFDIR_DELIVERY_MODE` must only be readable server-side (Edge Function env var) — mobile client never knows the delivery mode directly
Supabase Storage writes in the stub use the organisation-scoped service role — no public bucket access
Audit log entries must record actor identity (triggered_by_user_id) from the JWT claims for every delivery attempt
The `BufdirDirectApiService` placeholder must document that Bufdir API credentials must be stored in the integration credential vault, never in the mobile binary

Execution Context

Execution Tier
Tier 3

Tier 3 - 413 tasks

Can start after Tier 2 completes

Implementation Notes

Define `abstract class BufdirApiIntegrationService` with three async methods. Use Riverpod's `Provider` with a factory that reads the `BUFDIR_DELIVERY_MODE` env var (passed as a Dart const or via Supabase Edge Function environment) to select the implementation. The `BufdirFileDeliveryService` constructor should accept a `SupabaseClient` and a `BufdirAuditLogger` — both injected via Riverpod. The `BufdirDeliveryResult` class should be immutable (use `@immutable` annotation or a Dart record).

Document the future `BufdirDirectApiService` with a detailed `// TODO` block referencing the interface contract task (task-011) and listing the expected Bufdir API endpoint path, auth header format, and payload schema placeholder. Avoid using `dynamic` anywhere — all method signatures must be strongly typed. Register the provider in a dedicated `bufdir_providers.dart` file to keep provider definitions co-located with the service.

Testing Requirements

Unit tests (flutter_test with mocktail) must cover: (1) file delivery — mock Supabase Storage upload succeeds, assert `BufdirDeliveryResult` has status `pending_review` and non-empty `package_urls`; (2) file delivery — mock Supabase Storage upload throws, assert result has status `failed` and error message is populated; (3) configuration routing — with `BUFDIR_DELIVERY_MODE=fileDelivery`, assert `BufdirFileDeliveryService` is injected; (4) interface contract — instantiate `BufdirDirectApiService` and assert calling any method throws `UnimplementedError` with a descriptive message; (5) audit log write — assert `bufdir_export_audit_log` insert is called with correct fields on both success and failure paths. Riverpod provider override pattern must be demonstrated in at least one test to confirm testability. Test coverage for the stub implementation must be at least 90%.

Component
Bufdir API Integration Service (Future)
service 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.