critical priority high complexity backend pending backend specialist Tier 1

Acceptance Criteria

`DynamicsExporter` class is implemented and passes static type analysis as a valid implementor of `AccountingExporter`
All four required Dynamics portal fields are mapped: `transaction_reference` (unique per claim, format TBD with HLF), `cost_type_code` (from claim category mapping), `gl_account` (General Ledger account code), `department` (from org unit mapping)
The `transaction_reference` is deterministically generated from the claim ID — same claim always produces same reference, enabling idempotent re-export detection at the Dynamics portal level
A `DynamicsSchemaMapper` dependency is injected — it provides category→cost_type_code and org_unit→department mappings specific to HLF's Dynamics portal configuration
A claim with any null required field produces a `ValidationError` naming the specific missing field
A claim category not present in `DynamicsSchemaMapper` produces a `ValidationError` with `reason: 'No Dynamics cost_type_code for category: {category}'`
Validation failures across all claims in a batch are collected and returned together (no fail-fast)
The intermediate mapping result is a `List<DynamicsMappedRecord>` — a value object that mirrors the Dynamics portal JSON schema field names exactly
The service does NOT coordinate with the Dynamics portal API directly — it only produces the payload; integration is a separate concern
HLF-specific business rule: travel reimbursement claims (`expense_type == 'travel'`) must include a non-null `distance_km` field — validated and mapped to a Dynamics-specific `travel_distance` field

Technical Requirements

frameworks
Flutter
Dart
apis
Microsoft Dynamics portal import JSON schema (field definitions from HLF)
data models
ApprovedClaim
DynamicsMappedRecord
DynamicsSchemaEntry
AccountingExportError
FieldValidationFailure
performance requirements
Mapping must be synchronous and complete under 150ms for batches up to 200 claims
Schema mapper must use an in-memory lookup — no per-claim I/O
security requirements
The `transaction_reference` must not encode personal identifiers — use a hash of `claim_id` + `org_id` only
HLF-specific: distance_km values must be validated as positive integers ≤ 1000 to prevent erroneous large reimbursements
No employee names or personal numbers are included in `DynamicsMappedRecord` — only financial and accounting fields

Execution Context

Execution Tier
Tier 1

Tier 1 - 540 tasks

Can start after Tier 0 completes

Implementation Notes

HLF is coordinating this app with an existing Dynamics portal project — the `DynamicsMappedRecord` field names MUST match the Dynamics portal's import JSON schema exactly. Obtain the schema definition from HLF before implementing. Like `XledgerExporter`, never use `double` for amounts — use `Decimal` or integer øre. The `transaction_reference` generation should use `sha256(claimId + orgId)` truncated to 20 characters and uppercased — this makes it deterministic, unique per org+claim, and free of PII.

Store the `DynamicsSchemaMapper` config in a versioned assets JSON file under `assets/accounting/dynamics_schema.json` — same pattern as the Xledger chart of accounts mapper. Both HLF-specific validation rules (travel distance, automatic approval threshold) should be isolated in `DynamicsExporter` and NOT leak into the shared `AccountingExporter` interface. Consider extracting a `HlfExpenseRuleValidator` helper class to keep the exporter's `export()` method readable.

Testing Requirements

Unit tests using `flutter_test`. Required scenarios: (1) happy path — 4 standard claims map to correct `DynamicsMappedRecord` values; (2) travel claim with distance_km maps `travel_distance` field correctly; (3) travel claim missing distance_km produces `ValidationError` for `distance_km` field; (4) claim with unmapped category produces `ValidationError` with category name; (5) batch with mixed valid/invalid claims collects all failures; (6) `transaction_reference` is deterministic — same claim ID always produces same reference; (7) transaction_reference does not contain personal data (assert it equals a hash, not a name string). Use a stub `DynamicsSchemaMapper`. Minimum 10 unit tests.

Component
Dynamics Exporter
service high
Epic Risks (3)
high impact medium prob dependency

The Xledger CSV/JSON import specification may not be available in full detail at implementation time. If the field format, column ordering, encoding requirements, or required fields differ from assumptions, the generated file will be rejected by Xledger on first production use.

Mitigation & Contingency

Mitigation: Obtain the official Xledger import specification document from Blindeforbundet before starting XledgerExporter implementation. Build a dedicated acceptance test that validates a sample export file against all documented constraints.

Contingency: If the spec arrives late, implement a configurable column-mapping layer so that field order and names can be adjusted via configuration without code changes. Ship a file-based export that coordinators can manually verify before connecting to Xledger import.

high impact low prob technical

The atomic claim-marking transaction in Double-Export Guard could fail under high concurrency if two coordinators trigger an export for overlapping date ranges simultaneously, potentially allowing duplicate exports to proceed past the guard.

Mitigation & Contingency

Mitigation: Use a database-level advisory lock or a SELECT FOR UPDATE on the relevant claim rows within the export transaction to serialize concurrent exports per organization. Add an integration test that simulates concurrent export triggers.

Contingency: If locking proves problematic at the database level, implement an application-level distributed lock using a Supabase row in a dedicated export_locks table with an expiry timestamp and automatic cleanup on failure.

medium impact high prob integration

HLF's Dynamics portal API endpoint may not be available or documented in time for Phase 1, leaving DynamicsExporter unable to be validated against a real system and potentially shipping with an incorrect field schema.

Mitigation & Contingency

Mitigation: Design DynamicsExporter for file-based export first (CSV/JSON download), with the API push implemented behind a feature flag. Request a Dynamics test environment or sandbox from HLF as early as possible.

Contingency: Ship DynamicsExporter as a file export only for Phase 1. Phase the API push integration into a follow-on task once the Dynamics sandbox is available, using the same AccountingExporter interface with no breaking changes.