(fix) Scope concept-only fallback in findObsByFormField to respect obsGroup boundaries#722
Open
oisujeh wants to merge 3 commits into
Open
(fix) Scope concept-only fallback in findObsByFormField to respect obsGroup boundaries#722oisujeh wants to merge 3 commits into
oisujeh wants to merge 3 commits into
Conversation
…sGroup boundaries
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Requirements
Summary
Fixes a data contamination bug in
findObsByFormFieldwhere the concept-only fallback mechanism does not respectobsGroupboundaries. When editing an encounter whose form contains multipleobsGroupsections with child fields that share the same concept UUID, observations from one group bleed into the fields of a different group.The problem in detail
findObsByFormFieldresolves which saved observation belongs to which form field using a two-stage strategy:Primary match — match by
formFieldPathand concept UUID.This works when the saved obs has a
formFieldPaththat exactly matches the field's current ID.Concept-only fallback — if the primary match returns nothing, search the entire flattened obs list for any obs whose concept UUID matches the field's concept.
The
claimedObsIdsset is used to avoid double-assignment.The fallback is necessary because form field IDs can change across schema revisions (a field may be renamed or re-ordered), so the engine cannot always rely on path-based matching.
However, the fallback is currently unscoped. It searches every obs in the encounter regardless of which
obsGroupthat obs belongs to. This causes cross-group contamination when:obsGroupsections (e.g., "Blood Tests" and "Urine Tests", or in real-world pharmacy forms: "ARV Medication" and "PrEP Drugs").During the edit-mode hydration the engine flattens all obs (including
groupMembers) into one list. When a field in Group A fails its primary path match, the fallback grabs the first unclaimed obs with a matching concept — which may belong to Group B. Group A's fields are now populated with Group B's data.The fix
When a field belongs to an
obsGroup(field.meta.groupIdis set), the concept-only fallback now checks whether each candidate obs is a member of the correct parent group before accepting it. For each candidate obs the fix determines its owner group by scanning the flat obs list for a parent whosegroupMembersarray contains the candidate. Four cases are handled:formFieldPathmatches the expected parentformFieldPathdiffers from the expected parentformFieldPath(old encounter data)The scoping is implemented as an additional
.filter()step applied toobsByConceptonly whenfield.meta?.groupIdis truthy. Fields that do not belong to an obsGroup are completely unaffected.Code change
Tests
Added 3 new test cases to
obs-adapter.test.tsinside the existingfindObsByFormFielddescribe block. The tests use a generic "Lab Results" analogy (Blood Tests vs. Urine Tests sharing a "Result Value" concept) to validate the fix without domain-specific dependencies:Should scope concept fallback to the parent obsGroup when field has a groupId
A Blood Test field must NOT steal a Urine Test obs when they share the same concept.
Should allow concept fallback within the correct parent obsGroup
A Urine Test field with a changed ID should still match obs in its own group via concept fallback.
Should preserve backward compatibility when parent obsGroup has no formFieldPath
Encounters saved before
formFieldPathwas recorded on groups must continue to hydrate correctly.All existing tests continue to pass.
Screenshots
No UI changes — this is a logic-only fix in the obs adapter.
Related Issue
No existing Jira ticket. This was discovered while building pharmacy forms with multiple repeating obsGroups that share child concepts (Drug Strength, Unit, Frequency, Duration, Quantity Prescribed, Quantity Dispensed, Duration Dispensed).
Other
formFieldPathon parent obsGroups continue to work via the existing concept-only fallback (case 4 above)..filter()and inner.find()only execute during the concept fallback path for fields that belong to an obsGroup. The primary path-based match is not touched.obs-adapter.ts(fix) andobs-adapter.test.ts(tests).