Mutual Exclusion Prevents Invalid Expense Combinations
The mutual exclusion rule engine evaluates the currently selected expense types against a set of organisation-defined exclusion rules. When a peer mentor selects an expense type that conflicts with an already-selected type, the conflicting option is immediately disabled (greyed out) with a short explanation tooltip, and the user cannot proceed until the conflict is resolved. The rules are loaded from the expense type configuration and can differ per organisation. For example, HLF prohibits claiming both kilometre reimbursement and public transport tickets for the same trip. The engine operates entirely on the client side for instant feedback, with server-side validation at submission as a safety net.
User Story
Audience Summaries
Mutual exclusion enforcement directly eliminates the category of invalid claims that most burdens HLF coordinators: duplicate-path reimbursements where a peer mentor claims both kilometre reimbursement and public transport for the same trip. By making erroneous combinations technically impossible at the point of entry, the system removes a class of errors entirely rather than relying on post-submission review. This enables the threshold-based auto-approval pipeline to function with confidence, freeing coordinators from routine rejection tasks and reducing average claim processing time. The business outcome is a leaner administrative operation, higher data quality for Bufdir compliance, and a reimbursement programme that can scale as the peer mentor network grows without increasing coordinator workload proportionally.
This story depends directly on the expense type selection story (455) and must be sequenced after it. Complexity is medium-high: the client-side rule engine must load organisation-specific exclusion rules and reactively disable conflicting types in real time, while the server-side validation layer must independently enforce the same rules at submission. Coordination is needed between frontend, backend, and the configuration team to agree on the exclusion rule schema. Acceptance criteria require tooltip/inline messaging for disabled states and screen reader announcements, adding an accessibility testing obligation.
A key delivery risk is rule configuration completeness — UAT must include a coordinator verifying that HLF-specific rules (mileage vs. public transport) are correctly enforced. Server-side rejection error messaging must be defined with the product owner to ensure coordinator-facing error copy is accurate and actionable.
The client-side rule engine should be implemented as a pure function that accepts the current selection set and the organisation's exclusion rule array, returning a set of disabled type IDs and their associated reason strings. This function should be called reactively on every selection change and its output used to set disabled state and tooltip content on each card. Exclusion rules must be fetched as part of the organisation configuration payload and cached for the session. The server-side validation layer must replicate the same exclusion logic independently — do not reuse client code directly; implement as a shared validation utility or duplicate intentionally to avoid client-bundle coupling.
Edge cases to handle: multiple rules firing simultaneously (all conflicting types must be disabled at once), re-enabling types when a conflicting selection is cleared, and graceful handling of missing or malformed rule configurations. Accessibility requires disabled cards to expose ARIA disabled state and a descriptive label including the conflict reason. Integration tests must cover single-rule, multi-rule, and rule-removal scenarios.
Acceptance Criteria
- Given the peer mentor has selected 'mileage' as an expense type, when the expense type picker is still visible, then 'public transport' is visually disabled and labelled with a reason such as 'Cannot be combined with mileage'
- Given a conflicting type is disabled, when the peer mentor taps it, then a tooltip or inline message explains why it cannot be selected without dismissing the picker
- Given the peer mentor deselects 'mileage', when the picker updates, then 'public transport' becomes enabled again
- Given multiple exclusion rules exist in the configuration, when the peer mentor selects any type, then all conflicting types across all rules are disabled simultaneously
- Given the peer mentor submits a claim, when the server receives the payload, then server-side validation confirms no mutual exclusion violations are present and rejects the claim with a specific error if any are found
- Given a screen reader is active, when an expense type is disabled due to mutual exclusion, then the accessibility label announces the type as unavailable and provides the reason
Business Value
Mutual exclusion enforcement directly addresses the core HLF requirement from the workshop: fixed choices that make erroneous combinations technically impossible. Without this, coordinators must manually review and reject duplicate-path claims, creating administrative burden. Automated prevention at the UI layer reduces invalid submissions to near zero, enabling the threshold-based auto-approval pipeline to function reliably and freeing coordinators to focus on exceptions rather than routine error correction.
Components
- Mutual Exclusion Rule Engine service
- Expense Type Picker Widget ui
- Expense Selection BLoC service
- Expense Type Configuration data
- Expense Validation Service service
- Expense Type Accessibility Service service