medium priority high complexity integration pending integration specialist Tier 1

Acceptance Criteria

AccountingIntegrationClient exposes a single exportExpenses(List<ExpenseClaim> claims, String organisationId) method that dispatches to the correct adapter based on org identity
XledgerExporter authenticates using the Xledger REST API key from the credential vault and posts expense records to the correct Xledger endpoint
DynamicsExporter authenticates using OAuth 2.0 client credentials flow against Microsoft Dynamics 365 REST API using credentials from the vault
XledgerExporter maps internal expense fields to Xledger schema: amount → Amount, activity_type → AccountCode (from chart-of-accounts mapping), organisation unit → CostCentre, VAT rate → VatCode
DynamicsExporter maps internal expense fields to Dynamics schema with equivalent field mappings per HLF's Dynamics configuration
Chart-of-accounts codes and cost centre mappings are loaded from a configurable Supabase table (accounting_field_mappings) — not hardcoded
XledgerExporter serialises records as JSON; DynamicsExporter serialises as JSON (or CSV if Dynamics endpoint requires — verified against HLF's actual endpoint)
Both adapters return a typed ExportResult containing: success (bool), exportedCount (int), failedClaimIds (List<String>), rawResponse (String, for audit)
Authentication tokens are refreshed automatically on 401 responses without exposing credentials to logs
All HTTP communication uses TLS; no HTTP fallback accepted
AccountingIntegrationClient is implemented server-side (Supabase Edge Function) — no accounting API calls originate from the mobile client

Technical Requirements

frameworks
Supabase Edge Functions (Deno)
Flutter (client invocation only)
apis
Xledger REST API
Microsoft Dynamics 365 REST API
Supabase Edge Functions (Deno)
Supabase PostgreSQL 15
data models
activity
activity_type
performance requirements
Batch export of up to 50 expense claims must complete within 30 seconds
Single expense export must complete within 5 seconds under normal network conditions
Field mapping lookup from Supabase must be cached per Edge Function invocation to avoid N+1 queries
security requirements
Xledger API key and Dynamics OAuth credentials retrieved from vault inside Edge Function only — never passed through mobile client
OAuth access tokens for Dynamics not persisted beyond the Edge Function invocation
All exported payloads transmitted over TLS only
Per-organisation credential isolation: XledgerExporter only accessible for Blindeforbundet org ID, DynamicsExporter only for HLF org ID
Exported financial data must not include unnecessary PII beyond what the accounting system requires

Execution Context

Execution Tier
Tier 1

Tier 1 - 540 tasks

Can start after Tier 0 completes

Implementation Notes

Implement as a Supabase Edge Function (export-expenses) that accepts a POST with { organisationId, claimIds[] } in the body. The function determines the adapter type from the organisation record. Use the adapter pattern: define an abstract ExportAdapter interface with exportBatch(claims, credentials) → ExportResult, then implement XledgerAdapter and DynamicsAdapter. For Dynamics OAuth, implement a lightweight client-credentials token fetch (POST to Azure AD token endpoint) at the start of each Edge Function invocation — do not persist the token.

For Xledger, use API key in the Authorization header per their REST API docs. The accounting_field_mappings table should be keyed by (organisation_id, internal_field_name) → external_value, loaded once per invocation. Use Deno's built-in fetch for HTTP calls. Keep adapters in separate files (xledger_adapter.ts, dynamics_adapter.ts) for maintainability.

Testing Requirements

Unit tests (Deno): XledgerExporter maps expense fields correctly for 5+ field types (amount, account code, cost centre, VAT, date). DynamicsExporter equivalent mapping tests. Authentication refresh test: 401 response triggers token refresh and retry. Unit tests (Dart): AccountingIntegrationClient dispatches to correct adapter by org ID.

Integration tests: mock Xledger and Dynamics HTTP endpoints (using Deno's fetch mock) return 200 → ExportResult.success=true; mock 500 → ExportResult.success=false with failed IDs populated. Credential vault integration test: verifies adapter retrieves credentials without exposing them in logs. Test with both JSON and CSV serialisation if applicable.

Component
Accounting Integration Client
infrastructure high
Epic Risks (3)
high impact high prob integration

The Dynamics portal (HLF) and Xledger (Blindeforbundet) APIs have organisation-managed API contracts that may change without notice. Field mapping requirements, authentication flows, and export formats are not fully documented and may only be clarified during integration testing.

Mitigation & Contingency

Mitigation: Engage HLF and Blindeforbundet technical contacts early to obtain API documentation, sandbox credentials, and example payloads before implementation starts. Design the accounting integration client as a thin adapter layer with organisation-specific mappers so that field mapping changes require only mapper updates, not core client changes.

Contingency: If API documentation is unavailable or the API is unstable during Phase 3, implement a CSV/JSON file export as an interim deliverable. Coordinators can manually upload the file to their respective accounting systems until the live API integration is completed.

high impact medium prob scope

The confidentiality declaration for Blindeforbundet drivers may have specific legal requirements around content, format, wording, and record-keeping that are not yet specified. Implementing the wrong declaration flow could expose Blindeforbundet to compliance risk.

Mitigation & Contingency

Mitigation: Treat the declaration content and acknowledgement flow as a Blindeforbundet-controlled configuration, not hardcoded text. Implement the declaration as a templated document fetched from Supabase and reviewed by Blindeforbundet before any production deployment. Obtain written sign-off on the declaration text and acknowledgement mechanism before the epic is considered complete.

Contingency: If legal requirements cannot be confirmed in time for the sprint, deliver the driver honorarium form without the confidentiality declaration and gate the entire driver feature behind its feature flag. The declaration can be added in a follow-up sprint once requirements are confirmed, without blocking other feature delivery.

high impact medium prob integration

If the accounting export can be triggered multiple times for the same approved claims batch, duplicate records may be created in Dynamics or Xledger, causing accounting reconciliation problems that are difficult to reverse.

Mitigation & Contingency

Mitigation: Implement idempotent export runs: each export batch is assigned a unique run ID stored in the database. The accounting integration client checks for an existing successful export run for the same claim IDs before submitting. Approved claims that have been exported are marked with exported_at timestamp to prevent re-export.

Contingency: If duplicate exports occur despite idempotency checks (e.g. network failure after API success but before local confirmation), provide coordinators with an export history panel showing run IDs and timestamps. Implement a reconciliation endpoint that can query the accounting system for existing records before re-submitting flagged claims.