Build ExportAuditRepository Dart class
epic-bufdir-reporting-export-foundation-task-007 — Implement the ExportAuditRepository class in Dart using Supabase client. Expose methods: insertAuditEntry(ExportAuditEntry), getAuditLogForOrg(String orgId, DateTimeRange period), and getEntryById(String exportId). Ensure all calls include org_id scoping and that the repository throws on any attempted mutation of existing records.
Acceptance Criteria
Technical Requirements
Execution Context
Tier 2 - 518 tasks
Can start after Tier 1 completes
Implementation Notes
Place the class at lib/features/bufdir_reporting/data/repositories/export_audit_repository.dart following the existing feature-layer folder convention. Define a custom AuditInsertException extending AppException for consistent error handling. The ExportAuditEntry data class should use json_serializable or freezed with fromJson/toJson so it can be deserialized directly from Supabase's Map
When calling .insert(), use .select() chained after insert to retrieve the server-generated id and created_at in a single round trip (Supabase supports this). Avoid DateTime.now() for created_at — always use the server-returned value.
Testing Requirements
Unit tests using flutter_test with a mocked SupabaseClient (use mockito or mocktail). Test: (1) insertAuditEntry calls insert with correct payload including org_id; (2) insertAuditEntry propagates PostgrestException as AuditInsertException; (3) getAuditLogForOrg applies eq('org_id'), gte('created_at'), lte('created_at'), and order('created_at', ascending: false); (4) getAuditLogForOrg throws ArgumentError for empty orgId; (5) getAuditLogForOrg throws ArgumentError when period.start > period.end; (6) getEntryById returns null when maybeSingle() returns null; (7) getEntryById throws ArgumentError for non-UUID string. Integration tests (separate file) against real Supabase test instance: verify actual insert, read-back, and that a direct UPDATE attempt via the Dart client throws due to RLS. Minimum 80% line coverage on the repository class.
RLS policies for the audit log and schema config tables must correctly handle multi-chapter membership hierarchies (up to 1,400 local chapters for NHF). Incorrect policies could either over-expose data across organisations or prevent legitimate coordinator access, both of which are serious compliance failures.
Mitigation & Contingency
Mitigation: Design RLS policies using the existing org hierarchy resolver pattern. Write integration tests that verify cross-organisation isolation with representative fixture data covering NHF's multi-level hierarchy before merging.
Contingency: If RLS policies prove too complex to express safely in Postgres, implement a Supabase Edge Function as a data access proxy that enforces isolation in application code, with RLS serving as a secondary defence layer.
Bufdir's column schema is expected to evolve as Norse Digital Products negotiates a simplified digital reporting format. If the schema config versioning model is too rigid, applying Bufdir schema updates without a code deployment could fail, forcing emergency releases.
Mitigation & Contingency
Mitigation: Design the schema config table to store the full JSON column mapping as a JSONB field with a version number. Provide an admin API to upsert new versions without any schema migration required.
Contingency: If the versioning model is insufficient for a Bufdir schema change, fall back to a code deployment with the updated default schema, using the database config only for org-specific overrides.