critical priority medium complexity backend pending backend specialist Tier 2

Acceptance Criteria

The `export()` method of `XledgerExporter` returns a populated `ExportPayloadWrapper` containing both CSV and JSON representations of all successfully mapped records
The CSV output has a header row as the first line with exact Xledger column names in the required order
All string fields in CSV are enclosed in double-quotes; double-quote characters within values are escaped as `""` (RFC 4180 compliant)
Decimal amounts in CSV are formatted as `1234.56` (dot as decimal separator, no thousands separator, exactly 2 decimal places)
Dates in CSV are formatted as `YYYY-MM-DD` unless Xledger specification requires a different format (document which format is used)
The JSON output is a JSON array where each object has keys matching the exact Xledger field names — identical structure to CSV columns
The `ExportPayloadWrapper.payload` contains an `XledgerExportPayload` value object with both `csvBytes` (UTF-8 encoded `Uint8List`) and `jsonString` fields
An empty list of mapped records (all claims were invalid) causes the method to return an `AccountingExportError.ValidationError` rather than an empty CSV file
The CSV/JSON File Generator component is used for file generation — no ad-hoc `StringBuffer` CSV building in `XledgerExporter` itself
The returned `ExportPayloadWrapper.exportedClaimIds` list matches exactly the claim IDs that were successfully mapped and serialized

Technical Requirements

frameworks
Flutter
Dart
apis
Xledger CSV import specification (column order + encoding requirements)
data models
XledgerMappedRecord
XledgerExportPayload
ExportPayloadWrapper
performance requirements
CSV serialization of 380 records must complete in under 100ms
Output must be UTF-8 encoded — Xledger must not receive Latin-1 or Windows-1252 bytes
Memory allocation must not exceed 10MB for a 380-record export batch
security requirements
CSV must not contain formula injection characters (`=`, `+`, `-`, `@` as first character in a field) — prepend a single-quote or strip these from string fields
The CSV byte output must not be written to the device filesystem unencrypted — pass as in-memory `Uint8List` to the caller for upload or secure storage
JSON output must not include any fields beyond those in the Xledger specification

Execution Context

Execution Tier
Tier 2

Tier 2 - 518 tasks

Can start after Tier 1 completes

Implementation Notes

Define a `CsvJsonFileGenerator` utility class (or use it if already defined by the component) that accepts a list of maps and column order config, and produces both `csvBytes` and `jsonString`. The column order must be declared as a `static const List` constant in `XledgerExporter` (or its config) — never derived dynamically — so the order is auditable and matches the Xledger spec exactly. For the `XledgerExportPayload`, use a sealed payload type rather than putting both CSV and JSON in `dynamic payload` of `ExportPayloadWrapper` — this avoids casting at the call site. The BOM (byte order mark) for UTF-8 is optional in CSV; check Xledger's import tool requirements — some older tools require a BOM for proper UTF-8 detection.

If in doubt, include it (`0xEF, 0xBB, 0xBF` prepended to `csvBytes`). Do not use the `csv` pub package if it doesn't support all escaping requirements — a hand-rolled RFC 4180 serializer for this use case is 30 lines and far more predictable.

Testing Requirements

Unit tests using `flutter_test`. Required scenarios: (1) 3-record batch produces valid CSV with correct header and 3 data rows; (2) a string field containing a comma is properly quoted in CSV; (3) a string field containing a double-quote is escaped as `""`; (4) amount 150.00 appears as `150.00` not `150` or `1.5E2`; (5) JSON output has correct field names matching Xledger spec; (6) empty mapped records list returns `ValidationError`, not empty file; (7) `exportedClaimIds` in wrapper matches input claim IDs; (8) UTF-8 encoding verified by decoding `csvBytes` with `utf8.decode`. Snapshot test: generate CSV from a fixed 2-record input and assert exact string output matches a stored golden fixture.

Component
Xledger 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.