medium priority high complexity infrastructure pending infrastructure specialist Tier 0

Acceptance Criteria

accounting_credentials table created in Supabase with columns: id (uuid), organisation_id (uuid, FK), integration_type (enum: xledger | dynamics), encrypted_payload (text), created_at, updated_at, created_by_user_id
Credentials stored using Supabase Vault (pgsodium) or pgcrypto encrypted columns — plaintext credentials never stored in any table column
RLS policy on accounting_credentials restricts read to service-role only — no mobile client can query this table directly
Dart model AccountingCredentialConfig is typed with fields: organisationId, integrationType, xledgerApiKey (nullable), dynamicsClientId (nullable), dynamicsClientSecret (nullable), dynamicsTenantId (nullable)
Write operation for credentials is implemented as a Supabase Edge Function (set-accounting-credentials) accepting org-scoped JWT — never a direct table insert from mobile
Read operation for credentials is implemented as a Supabase Edge Function (get-accounting-credentials) returning decrypted payload only to server-side callers (validated by service role context)
Credentials are never logged: all logging statements in AccountingIntegrationClient omit credential fields; a lint rule or custom analysis option enforces this
Organisation-scoped isolation verified: querying credentials for org A while authenticated as org B returns empty result (RLS enforced)
Migration script included that creates the table, vault key, RLS policies, and index on organisation_id
Dart credential model has a toRedactedString() method that masks all sensitive fields for safe debug logging

Technical Requirements

frameworks
Flutter
Supabase Edge Functions (Deno)
apis
Supabase PostgreSQL 15
Supabase Edge Functions (Deno)
Xledger REST API
Microsoft Dynamics 365 REST API
performance requirements
Credential fetch Edge Function must respond within 500ms (credentials cached in Edge Function memory for session duration)
Migration script must be idempotent and complete within 10 seconds
security requirements
Supabase Vault (pgsodium) or pgcrypto used for column-level encryption — never plaintext storage
Service role key available only in Edge Function environment variables — never in mobile app bundle
RLS: accounting_credentials table accessible only via service role — all anon/user role queries return empty
Credential rotation supported: update operation replaces encrypted payload and updates updated_at
Audit log entry created on every credential read (for compliance)
Xledger API key and Dynamics OAuth credentials treated as secrets under GDPR data minimisation principle

Execution Context

Execution Tier
Tier 0

Tier 0 - 440 tasks

Implementation Notes

Use Supabase Vault (available via the pgsodium extension in Supabase Pro) for key management. If Vault is unavailable on the current plan, use pgcrypto with a key stored in Supabase secrets (vault.secrets or environment variable). The Edge Functions set-accounting-credentials and get-accounting-credentials should validate the organisation_id claim from the incoming JWT against the target credential record — never trust a client-supplied org ID without verification. In Dart, use sealed classes for AccountingCredentialConfig to enforce type safety between XledgerCredentials and DynamicsCredentials subtypes.

Ensure the migration is placed in a numbered migration file (e.g. 0025_accounting_credentials.sql) following the project's existing Supabase migration convention. Do not store credentials in flutter_secure_storage — they must remain server-side only.

Testing Requirements

Integration tests (Deno/Jest for Edge Functions): (1) set-accounting-credentials stores encrypted payload for org A, (2) get-accounting-credentials returns decrypted payload for org A, (3) get-accounting-credentials returns 403 when called without service role context, (4) org B cannot read org A credentials via RLS. Dart unit tests: AccountingCredentialConfig serialises/deserialises correctly, toRedactedString() masks all sensitive fields. Database migration test: migration script is idempotent (run twice, no errors). Manual security test: confirm no plaintext credentials visible in Supabase table editor.

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.