Implement XledgerExporter: Field Mapping and Validation
epic-accounting-system-export-engine-task-003 — Implement the XledgerExporter class that implements the AccountingExporter interface. Map approved claim fields (amount, date, category, cost center, org unit) to Xledger chart-of-account columns using the Chart of Accounts Mapper. Validate all Xledger-required fields before payload assembly, raising structured errors for missing or invalid values. This covers the mapping and validation layer only β payload serialization is a separate task.
Acceptance Criteria
Technical Requirements
Execution Context
Tier 1 - 540 tasks
Can start after Tier 0 completes
Implementation Notes
Financial amounts must NEVER use Dart's `double`. Use the `decimal` package (`decimal: ^2.x`) or represent amounts as integer ΓΈre (100 = 1.00 NOK) throughout the domain. Only convert to a formatted decimal string at serialization time. For the `ChartOfAccountsMapper`, define it as an abstract class (injected dependency) backed initially by a static JSON config file loaded at startup β this avoids blocking Xledger API availability during export.
The mapping configuration JSON should be versioned alongside the app so Blindeforbundet's accounting team can update it without a code change (loaded from assets). Collect all validation failures before returning (do not use exceptions mid-loop) β use a `List
Testing Requirements
Unit tests only using `flutter_test`. Required scenarios: (1) happy path β 5 valid claims map successfully to `XledgerMappedRecord` with correct field values; (2) claim with null cost_center β produces `ValidationError` naming the field; (3) claim with zero amount β produces `ValidationError`; (4) claim with unmapped category β produces `ValidationError` with category name in reason; (5) batch with 3 invalid and 2 valid claims β all 3 failures collected in single `ValidationError`, not only first; (6) amount correctly converted to 2 decimal places; (7) date formatted as ISO 8601. Use a stub `ChartOfAccountsMapper` returning predictable mappings. Minimum 12 unit tests.
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.
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.
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.