critical priority high complexity integration pending integration specialist Tier 2

Acceptance Criteria

XledgerAdapter class implements the uniform adapter interface defined in task-005 with all required methods (connect, disconnect, execute, getCapabilities, healthCheck)
OAuth2 client credentials flow completes successfully against Xledger authorization server and stores token in credential vault (never in mobile client)
Token refresh is handled automatically before expiry without requiring re-authentication
Accounting entry creation endpoint constructs valid Xledger REST payloads including all required fields (organization code, account codes, amounts, descriptions, date)
Expense claim exports from Blindeforbundet map correctly to Xledger's accounting entry schema (activity → cost center, amount → debit/credit, peer mentor → employee reference)
All HTTP 4xx errors from Xledger are normalized to adapter-level error types with user-readable messages in Norwegian/English
Transient failures (HTTP 429, 503, network timeouts) trigger exponential backoff retry with maximum 3 attempts before surfacing error
Xledger API key is retrieved from the credential vault server-side and never transmitted to mobile clients
Adapter declares capabilities: ['accounting_export', 'expense_sync'] in getCapabilities() response
healthCheck() returns latency and last-successful-call timestamp
All outbound financial data is transmitted over TLS only — enforced at adapter level
Per-organisation credential isolation is enforced: Blindeforbundet credentials cannot be used by any other organisation's adapter instance
Unit tests pass for payload serialization, error normalization, token refresh, and retry logic
Integration smoke test against Xledger sandbox environment succeeds for create accounting entry operation

Technical Requirements

frameworks
Dart
Flutter
Supabase Edge Functions (Deno)
apis
Xledger REST API
Supabase Edge Functions REST API
Supabase PostgreSQL 15
data models
activity
annual_summary
assignment
performance requirements
API call latency under 3 seconds for single accounting entry creation
Token refresh must complete within 2 seconds to avoid blocking export operations
Retry with exponential backoff: 1s, 2s, 4s — max 3 retries before error surface
Adapter initialization (credential load + auth) must complete within 5 seconds
security requirements
Xledger API key stored in accounting credentials vault server-side — never in mobile app binary or client memory
All adapter execution occurs inside Supabase Edge Functions — zero direct mobile-to-Xledger communication
Exported financial data transmitted over TLS only — enforce HTTPS in all HTTP client configurations
Per-organisation credential isolation: validate organisation context from JWT claims before loading credentials
OAuth2 access tokens stored in-memory only within Edge Function lifecycle — not persisted to database
Audit log entry created for every successful and failed export operation

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 inside a Supabase Edge Function (Deno) — never in the Flutter client. The adapter should be a Deno class that accepts an AdapterConfig object at construction (credentials resolved from vault, not passed directly). Use Deno's built-in fetch for HTTP calls. For OAuth2, implement a lightweight client credentials grant without third-party OAuth libraries to keep the Edge Function bundle small.

Xledger uses a REST API with JSON bodies — construct payloads by mapping from internal expense/activity models. Key pitfall: Xledger's cost centre and account code fields are organisation-specific — store these as part of the integration configuration (not hardcoded). Map the activity's organization_id to the Xledger company code stored in the credential vault entry. Implement the adapter's execute() method with a command pattern: accept an AdapterCommand enum (EXPORT_EXPENSE, EXPORT_ACTIVITY) and dispatch to private handler methods.

This keeps the public interface clean while allowing internal routing. For error normalization, create an XledgerErrorMapper that converts HTTP status + body to AdapterError with a code, message, and retryable boolean. Use the retryable flag in the retry loop to skip retrying on 401/403.

Testing Requirements

Unit tests (flutter_test / Deno test): payload serialization for Xledger accounting entry format, OAuth2 token acquisition and refresh flow (mocked HTTP), error normalization for all Xledger error response shapes (400, 401, 403, 429, 500, 503), retry logic verifying exponential backoff timing and max-retry enforcement. Integration tests: full OAuth2 flow against Xledger sandbox, accounting entry creation with valid Blindeforbundet test data, credential isolation test verifying cross-org access is rejected. Test coverage target: 90% on adapter business logic. All tests must run in CI without live Xledger credentials (use recorded HTTP fixtures for unit tests).

Component
REST API Adapter Registry
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.