critical priority medium complexity backend pending backend specialist Tier 2

Acceptance Criteria

FieldMappingResolver.transform(payload, fieldMapping) returns a new Map<String, dynamic> with all internal field names replaced by their target system aliases per the FieldMapping
Fields in the payload with no mapping entry are passed through unchanged (non-destructive transform)
Nested field path resolution: dotted path notation 'activity.duration_minutes' maps to nested map structures in the output payload
Default value injection: FieldMappingEntry may specify a defaultValue; if the internal field is absent from the payload, the default is injected using the external field name
Type coercion: FieldMappingEntry may specify a targetType (string, int, double, bool, isoDate); values are coerced accordingly — coercion failure produces a typed TransformError
transform() is synchronous and does not interact with the database or cache
TransformResult contains both the transformed payload and a list of TransformWarning objects for non-fatal issues (e.g., unmapped fields in strict mode)
Strict mode (optional flag): unmapped fields produce a TransformWarning with code UNMAPPED_FIELD
Unit test: transform activity payload for Xledger with known FieldMapping, assert output contains correct external field names and coerced types
Unit test: nested path 'activity.duration_minutes' correctly produces nested output map

Technical Requirements

frameworks
Flutter
Dart
data models
FieldMapping
FieldMappingEntry
TransformResult
TransformWarning
TransformError
performance requirements
Transformation of a typical activity payload (up to 50 fields) must complete in under 10ms
No memory allocation of intermediate copies beyond the output map
security requirements
Transform must not silently drop required fields — missing required fields must produce a TransformWarning or TransformError
Transformed payloads must not include internal fields marked as internal_only in the mapping definition

Execution Context

Execution Tier
Tier 2

Tier 2 - 518 tasks

Can start after Tier 1 completes

Integration Task

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

Implementation Notes

Implement transform() as a pure function — it takes a payload map and a FieldMapping and returns a TransformResult. Do not add any state or async logic here. For nested path resolution, split on '.' and recursively build/navigate the output map. Handle the case where an intermediate key already exists and is not a Map (conflict) by producing a TransformError with code PATH_CONFLICT.

For type coercion, implement a private _coerce(dynamic value, TargetType type) function that returns a Result. Keep each coercion case (string, int, double, bool, isoDate) as a separate private method for clarity and testability. This transform layer is called by the REST API adapters just before sending the payload to external systems — it must be reliable and produce no surprises. Ensure the output map is a deep copy, not a reference to the input, so callers can safely modify the result.

Testing Requirements

Unit tests using flutter_test. All tests must be synchronous. Required test cases: (1) flat payload with full mapping — all fields renamed correctly, (2) payload with unmapped field in non-strict mode — field passed through, no warning, (3) payload with unmapped field in strict mode — TransformWarning produced, (4) nested dotted path produces correct nested output map, (5) default value injected when field absent, (6) default value NOT injected when field present (even if null), (7) type coercion string→int success, (8) type coercion string→int failure produces TransformError, (9) isoDate coercion from DateTime.toString() format, (10) internal_only field excluded from output. Target 100% branch coverage on the transform() method.

Component
Field Mapping Resolver
service medium
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.