Aggregate multi-type calculation results
epic-expense-type-selection-core-services-task-005 — Implement the aggregation method in ExpenseCalculationService that accepts a list of active expense types with their inputs and returns a single ExpenseCalculationResult containing all line items, a total reimbursement amount, an auto-approval eligibility flag (below the 50 km / no-receipt threshold from HLF spec), and an accounting export payload. This method is the primary entry point called by the BLoC on every selection change.
Acceptance Criteria
Technical Requirements
Execution Context
Tier 2 - 518 tasks
Can start after Tier 1 completes
Implementation Notes
Implement calculateAll using a fold/map over the inputs list: map each CalculationInput to its result via a switch on formulaType (exhaustive — sealed class guarantees all cases handled), wrap each call in a try/catch to collect errors without crashing. The auto-approval logic should be extracted into a private _isAutoApprovalEligible(List
This is the hot path called on every BLoC state change (every expense type toggle), so keep it lean — no logging, no async, no DB access.
Testing Requirements
Write unit tests using flutter_test covering: (1) empty input list — zero result, autoApprovalEligible true, (2) single per-km input — correct delegation and result, (3) mixed inputs (per-km + flat receipt) — correct total and line items, (4) mileage exactly at 50 km threshold — autoApprovalEligible true, (5) mileage at 51 km — autoApprovalEligible false, (6) flat receipt with missing receipt — isIncomplete true on result, autoApprovalEligible false, (7) one formula throws — error is captured in calculationErrors, other line items still computed, (8) accountingExportPayload contains required fields, (9) three transit zones + two flat receipts — correct total sum. Aim for 90%+ branch coverage.
The per-km reimbursement rate and transit zone amounts must be read from org-specific configuration stored in Supabase. If the rate configuration table or RLS policies are not yet deployed when this epic runs, the calculation service cannot be completed and integration tests will fail.
Mitigation & Contingency
Mitigation: Define a RateConfigRepository interface and inject a stub implementation with default HLF rates from day one; write the real Supabase adapter in parallel and swap via dependency injection before merge.
Contingency: If org rate config is delayed beyond this epic's window, ship with the default-rate stub and log a prominent warning; calculate with defaults and surface a 'rates not confirmed' notice in the UI preview.
If the peer mentor opens an expense claim on two devices simultaneously, the local draft and the Supabase record may diverge. The repository's last-write-wins strategy could silently overwrite a valid selection with a stale one.
Mitigation & Contingency
Mitigation: Add an updated_at timestamp to the draft record and reject saves where the server timestamp is newer than the local copy; surface a conflict resolution prompt rather than silently overwriting.
Contingency: If conflict resolution UI is out of scope, fall back to server-authoritative reads on app foreground resume and discard local draft, notifying the user that their draft was refreshed from the server.