high priority medium complexity backend pending backend specialist Tier 5

Acceptance Criteria

saveFieldMapping(orgId, integrationType, mappingBlob) persists a valid JSONB blob to Supabase with org_id and integration_type as composite key scope
updateFieldMapping(orgId, integrationType, mappingBlob) performs an upsert and returns the updated record with updated_at timestamp
On save, the service queries the registered adapter's getCapabilities() to retrieve required fields and verifies all required target fields have a source mapping
If required fields are missing from the mapping blob, a structured MissingRequiredMappingError domain error is returned (not thrown) listing the missing field names
On successful save or update, FieldMappingResolver.invalidateCache(orgId, integrationType) is called synchronously before returning success
Concurrent upsert requests for the same org+integrationType are handled safely using Supabase upsert with on_conflict behavior
Mapping blob schema is validated as valid JSON before persistence; malformed JSON returns a MalformedMappingError
The service method is idempotent: saving the same mapping twice yields one record, not two
All operations are scoped to the requesting organization — cross-org access is rejected with an AuthorizationError
Audit log entry is written to the integration_config_audit table on every save or update

Technical Requirements

frameworks
Dart
Supabase Dart SDK
BLoC
Riverpod
apis
Supabase PostgREST upsert API
Adapter Registry getCapabilities() interface
FieldMappingResolver cache invalidation API
data models
IntegrationConfig
FieldMapping
AdapterCapabilities
IntegrationConfigAuditLog
performance requirements
Upsert round-trip to Supabase must complete within 500ms under normal network conditions
Cache invalidation must complete synchronously within the save transaction boundary
JSONB blob size must not exceed 64KB per mapping configuration
security requirements
Row-Level Security (RLS) policies on integration_config table must restrict reads and writes to the org's own records
Mapping blobs must not contain credential values — enforce schema-level rejection of known credential field names
All write operations must require an authenticated Supabase session with a valid org-scoped JWT
Audit log entries must be written using a service-role client, not the user-facing client, to prevent tampering

Execution Context

Execution Tier
Tier 5

Tier 5 - 253 tasks

Can start after Tier 4 completes

Integration Task

Handles integration between different epics or system components. Requires coordination across multiple development streams.

Implementation Notes

Use Supabase's upsert() with onConflict: 'org_id,integration_type' to handle concurrent saves safely. The FieldMapping domain object should be a Dart sealed class or freezed union to distinguish 'not yet saved', 'saved', and 'validation error' states cleanly for BLoC consumption. Store the JSONB blob as a Map in Dart; serialize with jsonEncode before persistence. Validation against adapter capabilities should use a pure function (no side effects) that takes the blob and a List from getCapabilities() — this makes it independently testable.

Keep the Config Service as a pure Dart class (no Flutter dependency) in the domain/services layer so it can be tested without Flutter test infrastructure. Cache invalidation should be injected as a dependency (FieldMappingResolverInvalidator interface) so it can be mocked in tests. Do not call getCapabilities() more than once per save operation — cache the result in a local variable.

Testing Requirements

Unit tests: mock the Supabase client and adapter registry to test save/update logic in isolation. Cover: (1) happy path save with all required fields mapped, (2) save missing required fields returns correct error structure, (3) malformed JSON blob is rejected, (4) cache invalidation is called exactly once on successful save, (5) cache invalidation is NOT called when save fails validation, (6) cross-org save attempt returns AuthorizationError, (7) idempotent upsert does not create duplicate records. Integration tests (against Supabase test instance): verify JSONB is stored and retrieved correctly, confirm RLS prevents cross-org reads, confirm audit log entry is created. Target 90%+ branch coverage on the save/update methods.

Component
Integration Configuration Service
service high
Epic Risks (3)
medium impact high prob technical

Each of the five external systems (Xledger, Dynamics, Cornerstone, Consio, Bufdir) has a different authentication flow, field schema, and error format. Forcing them into a uniform adapter interface may require compromises that result in leaky abstractions or make the adapter contract too complex to maintain.

Mitigation & Contingency

Mitigation: Design the IntegrationAdapter interface with a loose invoke() payload rather than a typed one, allowing each adapter to declare its own input/output schema. Use integration type metadata in the registry to document per-adapter quirks. Build Xledger first as the most documented API, then adapt the interface based on learnings.

Contingency: If the uniform interface cannot accommodate all five systems, split into two interface tiers: a simple polling/export adapter and a richer bidirectional adapter, with the registry declaring which tier each system implements.

medium impact high prob dependency

Development and testing of the Cornerstone and Consio adapters depends on NHF providing sandbox API access. If credentials or documentation are delayed, these adapters cannot be validated, blocking the epic's acceptance criteria.

Mitigation & Contingency

Mitigation: Implement Xledger and Dynamics adapters first (better-documented, sandbox available). Create a mock adapter for Cornerstone/Consio using recorded API responses for CI testing. Proactively request sandbox access from NHF at project kickoff.

Contingency: Ship the epic with Cornerstone/Consio adapters in a 'stub' state (connectivity test returns a simulated success, invoke() is not production-wired) and gate the NHF integration behind a feature flag until real API access is obtained.

medium impact medium prob scope

Real-world field mappings may include nested transformations, conditional logic, and data type coercions (e.g., Norwegian date formats, currency rounding rules) that the Field Mapping Resolver's initial design does not accommodate, requiring scope expansion mid-epic.

Mitigation & Contingency

Mitigation: Gather actual field mapping examples from Blindeforbundet (Xledger) and HLF (Dynamics) before designing the resolver. Identify the most complex transformation required and ensure the resolver design handles it. Limit Phase 1 to direct field renaming and format conversion only.

Contingency: If complex transformations are required, implement a simple expression evaluator (e.g., JSONata or a custom mini-DSL) as an extension point in the resolver, delivering basic mappings first and complex ones in a follow-up task.