Attach file references and metadata to audit records
epic-bufdir-reporting-export-core-logic-task-007 — Extend BufdirExportAuditService with attachFileRef method that records the storage path, MIME type, file size, and checksum of generated export files against the audit record. Also persist export metadata (row count, column schema version, organisation config snapshot) for reconstruction in case of Bufdir disputes.
Acceptance Criteria
Technical Requirements
Execution Context
Tier 2 - 518 tasks
Can start after Tier 1 completes
Implementation Notes
Model ExportFileReference and ExportMetadata as immutable Dart value objects (use freezed or plain final fields). The attachFileRef method should perform a Supabase `.update()` scoped by `eq('id', auditId)` inside a `.rpc()` transaction or a Dart-level try/catch that rolls back on failure using Supabase's PostgreSQL functions. Store organisationConfigSnapshot via `jsonEncode()` into a JSONB column — do not store as TEXT to preserve queryability. Define a migration script that adds file_storage_path, mime_type, file_size_bytes, sha256_checksum, and export_metadata (JSONB) columns to the bufdir_export_audits table with NOT NULL constraints only on file_storage_path and sha256_checksum (others nullable for pre-attachment state).
Keep the method signature in an abstract IBufdirAuditRepository so the service stays testable without a real Supabase connection.
Testing Requirements
Unit tests (flutter_test): cover attachFileRef happy path, idempotency on duplicate call, AuditRecordNotFoundException when auditId missing, InvalidAuditStateException on terminal-status record, ChecksumFormatException on malformed checksum, and correct JSONB round-trip for organisationConfigSnapshot. Use a fake in-memory repository implementing the same IBufdirAuditRepository interface to isolate all Supabase calls. Integration test: verify against a Supabase local emulator that the upsert correctly sets all columns and that RLS blocks an unauthenticated caller. Target ≥90% line coverage for the service class.
Bufdir's column schema may have per-field business rules (conditional required fields, cross-field validation, organisation-specific category taxonomies) that cannot be expressed in a simple key-value mapping configuration. If the configuration model is too simple, supporting NHF's specific requirements will require hardcoded organisation logic, undermining the configuration-driven design.
Mitigation & Contingency
Mitigation: Design the column configuration schema as a full JSON document supporting field-level transformation rules, conditional expressions, and org-specific value enumerations. Validate the design against a real NHF Bufdir Excel template before implementation begins.
Contingency: If the configuration model cannot express all required rules, implement a thin transformation plugin interface where org-specific logic can be added as a named Dart class registered against the organisation ID, with the JSON config covering only the common cases.
For large organisations like NHF with potentially tens of thousands of activity records, the full export pipeline (query + map + generate + bundle + upload) may exceed Supabase Edge Function execution time limits (typically 150s), causing silent timeouts that leave audit records in a pending state indefinitely.
Mitigation & Contingency
Mitigation: Implement the orchestrator as a background Dart isolate with progress streaming rather than a synchronous Edge Function call. Use chunked processing for the query and mapping phases to reduce peak memory usage. Profile against realistic NHF data volumes in a staging environment.
Contingency: If processing time cannot be reduced below the timeout threshold, implement an asynchronous job model where the export is queued, processed in the background, and the user is notified via push notification when the download is ready — treating it as an eventual rather than synchronous operation.