Promote a Diagnosis to a Condition in OpenMRS

Promote a Diagnosis to a Condition in OpenMRS

4 Must-Do’s

1. Problem Description: Have you clearly defined the user problem(s) you intend to solve, and what value this creates? Write down a story, user insight, or quote about this problem (this is important because (1) this will motivate your team, and (2) without this your problem might not actually be a big problem for the users themselves).
2. User Stories: Have you clearly written at least 3 user stories and use cases
3. Market Analysis: Have you surveyed what the market is doing here (e.g. comparison to other EMRs, or paper approaches; and don’t forget about learning from historic/existing OMRS instances)? Have you written down any possible gaps in your understanding of your users or their workflows? Have you reviewed the topic in FHIR to see what requirements or fields the global community references? (Eg if working on insurance, should look here)
4. Technical Considerations & Dependencies: Have you outlined what you need from cross-functional areas for success of the feature? E.g. do you need the platform to support a new API call? Have you explained how you’ve addressed dev concerns, such as designs that may not be feasible, or will be extra time-intensive to implement? 

Optional/Encouraged

Sketches: Have you added a drawing or description of how the feature could work to solve the problem at hand? (Pictures of sketches are ok!) 
Project Management: Have you created the Epic and JIRA tasks so you can share work clearly? Roll-out plan: Do you have an idea whether this will be an experiment, gradual roll out, and when? Have you added this to the timeline view? Have you planned how you will promote and/or work with communications folks in order to help this feature reach the widest audience and have the biggest impact it can?

Later but should do

QA Plan: Have you mentioned the plan for QA, such as how you will discover and address edge cases? Does your team/squad have a plan for automated tests to be added to new components (unit tests) or workflows (e2e tests)?
Safety & Tech Risks: Is there any reason you could regret rolling out this feature? (e.g. possible patient harm, heavy tech debt like introducing an unsupported library) Have you thought through the risks for this particular solution? And, how to reduce/address those? 

This checklist was inspired by this article. Additional Business Analyst Resources here.

Status: REQUIREMENTS IN PROGRESS IN PROGRESS

Technical Complexity: EASY MEDIUM HARD / COMPLEX

Summary:

  • Clinicians recording a diagnosis in the Visit Note currently have to re-enter the same information in the Conditions module when the diagnosis is a long-term condition, or navigate there separately to update the status of an existing condition. This feature removes both round trips.

  • Below the diagnosis chips in the Visit Note, an Add to conditions list panel shows each diagnosis with the action appropriate to its current state on the patient's Conditions list:

    • Not on the listAdd as active condition

    • On the list as ActiveMark as inactive (in remission)

    • On the list as InactiveMark as active again

  • Each action is a single click. The Conditions widget refreshes immediately, a confirmation snackbar appears with an Undo affordance, and the row updates to reflect the new state.

  • Onset date is left blank on promotion. The standard OpenmrsDatePicker is shown as an optional input, alongside a note that the diagnosis date isn't necessarily when the condition began.

Promote Flow Summary

image-20260630-134624.png

 

1. Problem

A clinician in a busy outpatient clinic sees twelve patients before noon. Three are newly diagnosed with a lifelong condition like Type 2 Diabetes or Hypertension. For each, she documents the diagnosis in the Visit Note, then navigates to the Conditions module and re-types the same diagnosis so it appears on the patient's ongoing Conditions list. That is six entries for information captured three times. Over a week, the re-entry adds up, and when she is rushed, she sometimes skips the Conditions module altogether, leaving the record incomplete.

The same double-entry happens in reverse: a patient's known active condition often needs to be named again as an encounter diagnosis for today's visit. The Visit Note and the Conditions list are separate modules, but for a chronic problem they record the same clinical fact — the clinician should only have to enter it once, in either direction.

2. User Stories

Story 1: Add a diagnosis to the condition list

As a clinician, when I record a diagnosis in the Visit Note that is a lifelong condition, I want to add it to the patient's Conditions list from the diagnosis itself, so that I don't re-enter it in the Conditions module.

  • Each diagnosis renders as a pill with a ⋮ (overflow) menu.

  • The menu offers "Add to condition list"; one tap creates an active condition from that concept, with no further data entry.

  • Onset is left blank (see §5).

  • The Conditions widget refreshes instantly, a snackbar confirms with Undo, and the pill gains a ✓ badge.

Story 2: See at a glance whether a diagnosis is already on the list

As a clinician, when I record a diagnosis that is already on the patient's Conditions list, I want a clear cue on that diagnosis pill, so that I can tell at a glance it's a pre-existing condition rather than a new problem.

  • As each diagnosis is entered, a green ✓ badge appears on its pill if that concept is already on the Conditions list.

  • Detection uses an exact concept match, recomputed as diagnoses change (not stored).

Story 3: Add an existing condition as an encounter diagnosis (reverse)

As a clinician, I want to pull an existing active condition into today's note as an encounter diagnosis, so that I don't retype a problem the patient is already known to have.

  • A "Select from condition list" affordance beside the diagnosis search presents active conditions not already in the encounter's diagnoses.

  • Selecting one adds it as a diagnosis in two taps, reusing the note's existing add-diagnosis and save path.

Story 4: On-list diagnosis is read-only in the visit form

As a clinician, when a diagnosis is already on the Conditions list, I want the pill to show its current status without letting me edit it here, and to point me to the Conditions module for any change, so that status management stays in one place.

  • The pill's menu is read-only: "On condition list · Active" (or the relevant status), plus a "Manage in Conditions →" link.

  • No status editing happens inside the visit form.

3. Market Analysis

1: Bahmni: Diagnoses and Conditions appear together on the same screen rather than in separate modules. Bahmni doesn't separate Visit-Note diagnosis from Conditions-list management; the two coexist in one combined view without an explicit promote step.

image-20260406-190608.png

OpenMRS Talk discussion. Community input on the proposal can be found here.

4. Scope for V1

In scope for V1

  • Pill + ⋮ overflow menu on each diagnosis (low-footprint UI).

  • "Add to condition list" → create as active, onset blank, one tap.

  • Green ✓ at-a-glance on-list badge.

  • Read-only on-list state + "Manage in Conditions →".

  • Reverse: "Select from condition list" → add as encounter diagnosis.

  • Instant Conditions-widget refresh, snackbar, Undo.

  • Exact concept match throughout.

Out of scope for V1

  • In-form condition status management (mark inactive / reactivate) — handled in the Conditions module.

  • Chronic/acute eligibility classification — needs concept-level metadata the fhir2 module doesn't expose.

  • Related-concept matching (e.g. "Exacerbation of COPD" → "COPD"). V1 is exact match only. Post-V1 could use SNOMED / CIEL SAME-AS maps.

  • Auto-promotion on save — nothing reaches the list without an explicit action.

Frontend: V1 work breakdown

  1. Add to conditions list panel. Render one row per diagnosis chip in the Visit Note. Per-row state is determined by a single query: GET /ws/fhir2/R4/Condition?patient={uuid}&category=problem-list-item&code={conceptUUID}. Three branches:

    • No result → Add as active condition button. On click: POST /Condition with category=problem-list-item, clinicalStatus=active, code={concept}, subject={patient}, onset left blank.

    • Result with clinicalStatus=activeMark as inactive (in remission) button. On click: PATCH /Condition/{uuid} setting clinicalStatus=inactive.

    • Result with clinicalStatus=inactiveMark as active again button. On click: PATCH /Condition/{uuid} setting clinicalStatus=active.

  2. Typeahead annotation. For each concept search result, run the same exact concept-match check and surface an On conditions list marker on matches (regardless of Active/Inactive).

  3. Conditions widget refresh. Re-fetch the patient's conditions after any of the three actions. No backend change needed.

  4. Snackbar with Undo. On success, store the returned Condition UUID (create case) or the previous clinicalStatus (status-flip cases). Undo issues the inverse action: DELETE /Condition/{uuid} for a newly created condition, or a PATCH back to the previous status.

  5. State sync. After any action, the row re-evaluates and the button updates to the next-state action.

Backend (FHIR2 Module): V1 work breakdown

  • No changes required. Read, create, search by concept+patient, status update, and delete are already implemented and working.

5. Technical Considerations & Dependencies

  • Single panel, state-driven actions. V1 deliberately avoids auto-classifying diagnoses. Each diagnosis row gets one action based on the current state of its concept on the patient's Conditions list. No chronic/acute auto-detection.

  • Explicit action only. Nothing on the Conditions list changes automatically. The Conditions list updates only when the clinician explicitly clicks one of the three actions. Saving the visit note alone changes nothing.

  • Curation by intent. The opt-in step keeps acute, one-off diagnoses (pharyngitis, a sprain) off the list. The list stays curated by clinician intent rather than by default, without the system having to classify anything.

  • Onset date. Onset is left blank on the create action, with the standard OpenmrsDatePicker available as an optional input. The picker is day-precision; month-only inputs are not modelled by the backend in V1.

  • Live updates. All three actions are real writes. The Conditions widget refreshes instantly with no reload, a snackbar confirms, and Undo reverts the action.

  • Dedupe and state coherence. The same exact concept-match query drives all three states, so a diagnosis cannot be promoted twice — a successful create flips the row to "Mark as inactive" mode, removing the option to create again.

  • FHIR semantics. The feature maps onto the FHIR Condition.category distinction between encounter-diagnosis and problem-list-item, and the clinicalStatus distinction between active and inactive. Promotion is the move from an encounter diagnosis to a problem-list item; the remission and reactivation actions are status flips on the existing problem-list item.

Feasibility

The fhir2 module already treats org.openmrs.Diagnosis and org.openmrs.Condition as two separate data models mapped to the same FHIR resource type (Condition) via the category field. "Promotion" is reading a Diagnosis and writing a new Condition from it; "Mark as inactive" and "Mark as active again" are PATCH operations on clinicalStatus.

What already exists with no new code needed:

  • Category-based routing — encounter-diagnosis → Diagnosis DAO, problem-list-item → Condition DAO.

  • Condition creation via FHIR (POST /ws/fhir2/R4/Condition).

  • Condition status update via FHIR (PATCH /ws/fhir2/R4/Condition/{uuid}) for clinicalStatus.

  • Condition deletion via FHIR for Undo (DELETE /ws/fhir2/R4/Condition/{uuid}).

  • Search by concept+patient against the Conditions DAO.

FhirConditionServiceImpl and FhirUtils.getOpenmrsConditionType() already route reads, writes, and searches between the Diagnosis and Condition entities based on category. TwoSearchQueryBundleProvider already merges search results from both. No new API endpoint, no new service method, no new translator logic is needed.

Post-V1 considerations

Concept-level eligibility (chronic-conditions filter). Rather than offering the affordance on every diagnosis, an implementer could restrict it to concepts flagged problem-list-eligible — via a concept attribute, membership in a "Chronic conditions" concept set, or a Condition.category distinction. Diagnoses outside that set would still be recorded as diagnoses but show no action in the Add to conditions list panel. Requires concept-level metadata to be exposed in fhir2.

Related-concept matching. As Andrew Kanter raised, an encounter diagnosis is sometimes more specific than the condition it should promote to ("Exacerbation of COPD" on the visit, "COPD" on the conditions list). V1 only matches exact concept. Post-V1 work could leverage SNOMED maps from CIEL or a curated map of common terms; AI-assisted curation is another avenue worth exploring.

6. Sketches

image-20260630-133329.png
All actions
image-20260630-133357.png
Added Diabetes Mellitus as an Active Condition

 

image-20260630-133528.png
Marked Asthma Inactive

 

image-20260630-133613.png
Marked Epilepsy Active