Implement disabled expense types computation
epic-expense-type-selection-core-services-task-008 — Implement the computeDisabledTypes method in ExpenseSelectionBloc that, given the current selected set, queries the mutual-exclusion-rule-engine for all type IDs that are now forbidden by active exclusion rules. The resulting disabled set is included in each emitted state so the UI layer can grey out incompatible options without additional lookups. Must recompute on every selection change and on initial load.
Acceptance Criteria
Technical Requirements
Execution Context
Tier 3 - 413 tasks
Can start after Tier 2 completes
Implementation Notes
Add a computeDisabledTypes(Set
Avoid duplicating rule logic inside the BLoC — all constraint knowledge lives in the rule engine. The HLF requirement from the workshop (km + bussbillett cannot coexist) is the canonical example of an exclusion rule; ensure the rule engine interface supports this pattern.
Testing Requirements
Unit tests with flutter_test and bloc_test. Use a fake MutualExclusionRuleEngine that maps specific selected sets to known forbidden sets. Test cases: (1) empty selection produces empty disabled set, (2) selecting km-reimbursement disables public-transport, (3) deselecting km-reimbursement re-enables public-transport, (4) multiple selected types produce the union of all their exclusion constraints. Assert disabledTypeIds on every emitted state, not just the final one.
Verify computeDisabledTypes is invoked on initialisation by checking the initial state's disabledTypeIds field.
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.