Backend support for draft forms/encounters

Hi guys!

I have a requirement where we need to distinguish a form that is still in draft and can be edited at will from a form that is validated and signed and cannot be edited anymore.

I wanted to know if there is already some design done around this topic and if others might be interested in sharing their use case and ideas.

Note that this was achieved so far by using some JS logic in the form to prevent the user saving the edits based on the presence of the Validation obs or not in the encounter. Not ideal.


cc @burke @mseaton @mksd @jteich @grace @jdick @jesplana @ibacher


Let me add to this that when in draft, the required form fields validation is not applied.

Only upon final validation of the form the required fields are checked.

I think it would be good to combine this with some general notion of a “draft status” as its something we need. We kind of have draft orders, in the sense that the frontend can queue up orders that are just not submitted to the backend, but that’s obviously less useful than a proper “draft order” feature where the order can be saved but isn’t “live” until it’s moved out of draft status.

There’s also been some discussion around “draft encounters” so we can start removing the coupling 1 encounter = 1 form allowing, e.g., multiple frontend components to all write to the same encounters to capture, e.g. allergies, diagnoses, problem lists, orders, etc.

@burke @mseaton We’d really appreciate your input on this one.

Not much to add, other than I’m supportive of this. I feel like in the old Infopath world, this kind of thing might have been possible by persisting the HL7 message being generated by the form engine in a draft table that would allow the contents of the encounter to later be edited before finalizing and moving to a queue for submission, so my first reaction would be to try to do something similar to that but within the context of moving to FHIR and having OpenMRS support natively the idea of FHIR messages being constructed and in draft format before being finalized and submitted.

I do not think that it is likely to be doable / preferable to just add a “draft” status to the existing encounter table or Order table. This feels like a recipe for misinterpretation and a backwards incompatibility nightmare.

1 Like

Thanks for your input @mseaton. What I’ve had in mind was actually something less invasive, maybe using some configurable attributes to make an entity support draft. But that 's just an idea.

@ibacher how is this, if at all, modeled in FHIR?

In many EHR note-writing systems or ordering systems, an item can be

  • Draft – not seen by anyone other than the author, not posted, not live
  • Preliminary – posted, but not ‘official’ – usually refers to something that requires a cosignature before it becomes part of the orders / part of the record
  • Final
1 Like

This is going to be a bit of a complicated answer, because FHIR’s equivalent of forms is modelled somewhat differently than in OpenMRS.

In FHIR, a form would be represented by a “Questionnaire” resource. The form-as-filled-in is represented as a “QuestionnaireResponse” resource. Each questionnaire response has a status which can indicate whether the response is “completed” or “in-progress”. A typical way to use this would be something like this:

  1. The user opens the questionnaire and begins filling it in.
  2. The user does something else, so the questionnaire response is saved in “in-progress” status.
  3. The user comes back to the questionnaire and finishes it. The questionnaire response is moved to “completed” status.
  4. Once the questionnaire response is moved to “completed” status, it is then handled by some backend process that converts the questionnaire response into the resulting observations, etc. (an example of this is the FHIR SDC’s $process-response operation).

The problem is that this requires some kind of intermediate state (a questionnaire response in the above outline; an HL7 message in the workflow Mike mentioned) persisted somewhere that the form engine is able to recognise and process.

For Orders, FHIR’s modelling is just an single attribute on the order, the “status” attribute that can be in “draft”, “active” or a few other statuses.

I think this is less invasive to the form entry engine, but not less invasive to the OpenMRS system overall, which will have lots of modules, functions, and features that have no knowledge of how to distinguish between a draft or completed encounter (and associated obs and orders). But if the form data is saved in pre-submitted, serialized state (eg. as a serialization of the form data, or the FHIR resources that will ultimately get submitted), then this can become a feature of the form entry engine (to support authoring draft encounters) in a way that doesn’t require changes to the rest of the application.

1 Like

This makes a lot of sense!

Adding draft encounters including all of its components (draft orders, draft encounter diagnoses, draft notes, etc.) would be wonderful and powerful, but is not a small change. As @mseaton points out, all the business on the backend and anything retrieving these data would need to be aware of the possibility of data being draft status. But I think that’s different from @mksrom’s requirement.

I believe @mksrom wants to be able to discriminate between signed (i.e., completed) forms and those that are not yet complete.

@mksrom, in Java we distinguish between saved & unsaved resources by determining if a UUID has been assigned. Am I correct in assuming that we’re running into this problem because the frontend is assigning UUIDs to resources before they’re written to the database (saved)?

Correct. And we would need that any user can come back later to the “not yet completed” form and continue filling it in. Or, finally decide to “Sign it”.

That would be just great! Though that’d mean the data saved in draft is nowhere shown in the application (as there is no encounter nor obs saved yet). But maybe that’s OK for our use case. I’ll check.

(btw @grace would it be possible to add this topic to next TAC?)

1 Like

Done :slight_smile: Is it okay to combine with the OpenEHR CDS topic?

Thanks. I would think that this draft encounters status is worth a 1h call, but let’s try to keep it short.

1 Like

Either CDS or draft encounters could easily fill the full hour. As it turns out the CDS discussion is scheduled for 25 April, so we can use next Monday to focus on draft forms/encounters.

1 Like

To clarify, there are two very different approaches being discussed here:

  1. Draft forms. An incomplete form is serialized and persisted (somewhere, location TBD) so it can be restored to its incomplete state and someone can finish it later. An encounter is not created until the form is finally submitted.

  2. Draft forms with draft encounters. An incomplete form is stored within an incomplete encounter. The encounter, any form(s) belonging to the encounter, and eventually any other encounter data (orders, diagnoses, etc.) would be stored alongside all other encounters, but marked as in “draft” status. This is the ‘just add a “draft” status to the existing encounter table’ @mseaton was referring to in his above comment.

We would love to have #2 (draft encounters) and will eventually need to tackle that, but, like @mseaton implied, it will take a significant amount of work to ensure the long tail of existing code is either made aware of the possibility of draft encounters or uses methods & SQL that filter out unfinished encounter data. But this is the “invasive” approach.

I believe what you are looking for is #1; however, we can’t store these incomplete “draft” forms within completed encounters (any encounter in the database is currently considered complete), so we’ll need to come up with another place to store them.

So, just got back with the client. Indeed that is not a problem.

Hello everyone, I know I’m late to the party but maybe have an interesting proposal.

I would propose persisting draft objects in an unserialized state. Any frontend module can dump draft blobs in JSON format to a /draft endpoint in some format like

#POST /draft
   module_name: 'form7',
   data: {JSON OBJECT}

being stored in table drafts with columns: [user_id (clinician, not patient), module_name, data]

and then retrieve data by calling GET /draft/{module_name}. When a clinician resumes working with a patient they can check the draft data and load up ui elements appropriately. When they save real data this goes through normal workflow as usual and /draft/{module_name} gets emptied with a DELETE

This has the advantage of 1. allowing the saving of unverified data if a clinician doesn’t have time to clean data to the point of database validation and 2. existing tables are left as-is, all data in those tables is always valid.

This technique could be extended to support encounters by adding an optional ‘encounter_id’ to the draft table and a query param in the GET like /draft/{module_name}?encounter={encounter_id} and a DELETE like /draft/encounter/{encounter_id}

Let me know if this is helpful


We had a good discussion on today’s TAC call. Some outcomes:

  • Draft forms or draft encounter?
    • While we’d like to have draft encounters (including support for draft orders, forms, encounter diagnosis, allergy changes, etc.), this will require significant changes to the API/Platform, since adding status to encounter that allows for “planned” encounters could easily break all existing code if it doesn’t take backwards-compatibility into account and we aren’t careful to identify queries in existing code that need refactoring.
  • What architecture to use?
    • We should consider the application state feature @zacbutko suggested as an architectural approach, since it could have multiple other uses.
    • For @mksrom’s use case, this would be something like /draft/patient/form (i.e., a draft form specific to a patient).
  • How long are drafts persisted?
    • Default behavior would be they are persisted forever. As long as each draft has a update_at timestamp, then local business rules could be applied to void or purge drafts.
  • Who can access drafts? Can they be shared?
    • Permissions can be applied on top of draft feature as long as we know who created the draft and for whom.
  • Making forms uneditable is a separate issue (@mksrom’s use case suggested forms would reach a state of being uneditable).
    • We would suggesting using permissions to control who can edit forms.
  • Design ideas
    • Form content
    • User (creator)
    • updated_at timestamp
    • Patient (optional)
    • We can’t really link to encounter, since we wouldn’t have encounter in common use case (not created until form validated/submitted/saved).

Here’s the recording:


Hi Everyone,

It’s been two months since we discussed this and it sounds like generally everyone is in favor of having draft encounters. It sounds like the best approach would be to implement it “for real” instead of just using a frontend cache.

What are the next steps here? How should we proceed to bring this feature to life?

Thanks, Zac