Have you implemented OpenMRS? Please participate in the Implementation Site Survey. If you already have, thank you!
Child pages
  • Order Entry Module Design
Skip to end of metadata
Go to start of metadata

We did not release this module. Ultimately, we introduced a new Order Entry API in OpenMRS Platform 1.10.0, and there is no module to add this support in earlier OpenMRS platform versions.

Background

A new 'Order Entry' module will be authored to provide a new API and features necessary to manage patient specific orders, the module is intended to be a cleaner version of the existing order entry API with several additions and subtractions. For more background information, please visit api support for order entry page. This module will use the "org.openmrs" namespace, rather than the conventional "org.openmrs.module" namespace in order to more easily support the goal of eventually incorporating this module into core. By keeping it as a module initially, this allows implementations running older versions of OpenMRS (1.7, 1.8, 1.9, etc) to take advantage of, and test out, this work, without requiring them to upgrade. It also will allow the module to evolve at a different pace than the core code.

The module will use existing orders tables, installing the module will essentially lead to modification of the related tables, 

Can we have the OrderService in core marked as deprecated prior  to releasing 1.9?

Module id: orderentry

Module name: Order Entry

Package namespace: org.openmrs.orderentry

Module design

We are getting rid of the OrderType class including the associated database table, the broader categorization of  will by the concept class of each order's backing concept. A more granular break down of these can be done through attribute types. (Though this is not supported in older versions)

Order

( * = required)

class Order extends BaseOpenmrsData{

  public enum OrderAction {
     DISCONTINUE, ORDER
  }

  *Integer orderId
  *Patient patient
  *Concept concept
  *String orderNumber
  String previousOrderNumber
  *OrderAction orderAction
  *Date startDate
  Date autoExpireDate 
  *Encounter encounter
  String instructions
  *User orderer
  Boolean discontinued
  User discontinuedBy
  Date discontinuedDate
  String discontinuedReason
}

DrugOrder

class DrugOrder extends Order{
  Drug drug
  Concept concept;//should be drug.concept if drug is not null
  Double dose
  String doseUnits
  Boolean isStructuredDosing
  Double strength
  String strengthUnits  
  Integer quantity
  String quantityUnits
  String frequency
  String brandName
  String dosageForm
  String route
  Boolean asNeeded  - Indicated PRN
  String asNeededCondition
  String additionalInstructions
  Integer duration
  String durationUnits
  Integer numRefills
}

TestOrder

class TestOrder extends Order{  
  Concept specimenSource  
  Concept laterality  
  String clinicalHistory
}

Orderable

Marker interface for things that a User can order

public interface Orderable {
 String getUniqueIdentifier(); //The unique identifier for this Orderable.
 Concept getConcept(); //The concept of the order that will ultimately be created from this Orderable.
 String getName(); //display name of this orderable, typically should be inherited from the concept. Intended for use in the UI.
 String getDescription(); //display descripion of this orderable, typically should be inherited from the concept. Intended for use in the UI.
}

BaseOrderable

Base implementation of the Orderable interface, should define the  properties and provide the default implementation of the inherited methods

Is Drug.concept nullable?

abstract class BaseOrderable{
   Concept concept; //will this be required?
   String name; //We really need this if drug.concept is nullable and fetch it from the drug?
   String description; //same here
}

SimpleOrderable

Acts a wrapper for a concept or Drug(if drug.concept is nullable) with concept class as 'Drug' or 'Test' or 'Misc Order' or a Drug with no

class GenericOrderable extends BaseOrderable{
  //constructor that takes in a concept
  public SimpleOrderable(Concept concept) {
     ..............
  }

  //constructor that takes in a drug if drug.concept is nullable
  public SimpleOrderable(Drug drug) {
     ..............
  }

 //return something unique with type included i.e Concept Vs Drug(if drug.concept is nullable)
 @Override
  public String getUniqueIdentifier() {
     ...........
  }

}

OrderEntryService

interface OrderService extends OpenmrsService{
  saveOrder(Order);//Should not let you change an Order.  can only do an sql insert from here.
  voidOrder(Order, String voidReason);
  unvoidOrder(Order order);
  purgeOrder(Order);
  //Creates a new order with a new order number that is a "discontinue" order. previous_order_number on this
  //should point to old Order, also finds old Order and marks it as discontinued with the given parameters
  discontinueOrder(Order order, Concept discontinueReason, Date discontinueDate);
  discontinueOrder(Order order, String discontinueReason, Date discontinueDate);
  createOrdersAndEncounter(Patient p, Collection<Order> orders);// activates the orders given, do we need this?
  getOrder(Integer orderId);
  getOrderByUuid(String uuid);
  String getNewOrderNumber();
  getOrderByOrderNumber(String orderNumber);
  Class<T extends Order> getOrder(Integer orderId, Class<T> orderClassType);
  getOrders(Class<T extends Order> orderClassType, List<Patient> patients, List<Concept> concepts, OrderAction action, List<User> orderers, List<Encounter> encounters, List<ConceptClass> conceptClasses);
  getOrdersByOrderer(User user);
  getOrdersByPatient(Patient patient);
  getDrugOrdersByPatient(Patient patient, OrderAction orderAction);
  getDrugOrdersByPatient(Patient patient, OrderAction orderAction, boolean includeVoided);
  getDrugOrdersByPatient(Patient patient);
  getOrdersByEncounter(Encounter encounter);
  List<Order> getOrderHistoryByConcept(Patient patient, Concept concept);
  getOrderables(String query);
  getOrderables(String uniqueIdentifier);
}

OrderValidator

class OrderValidator implements Validator{
  public void validate(Object obj, Errors errors) {
    //Enforce all required fields
    //TODO: What are the extra validation rules?
  }
}

DrugOrderValidator

class DrugOrderValidator extends OrderValidator{
  public void validate(Object obj, Errors errors) {
    super.validate(obj, errors);

    //drug and concept cannot both be null, drug.concept should match concept if not null
    //Check that the class of the concept is Drug
    //TODO: Add other validation rules specific to drug orders
  }
}

TestOrderValidator

class TestOrderValidator extends OrderValidator{
  public void validate(Object obj, Errors errors) {
    super.validate(obj, errors);

    //Check that the class of the concept is Test
    //TODO: Add other validation rules specific to test orders
  }
}
  • No labels

6 Comments

  1. I think we should get rid of all references to ORDER_STATUS.

    What's the OrderAction for a "regular" order?

    What does getOrdersByUser do? Is that ordered by the user? If so, indicate that in the method name.

    1. OrderAction for a "regular" order will be NULL, only DC orders will have a different status, in future we can add more enum constants as needed.

      I think User should be changed to Provider, though this means the required version will change to 1.9

  2. Couple of questions / comments:

    • I think we should rename the "concept" property to something a bit more descriptive. "orderableConcept" maybe?
    • In the current Order object we have discontinueReason (Concept) and discontinueReasonNonCoded (String). Did we decide to change this?
    • I'm not sure that Drug should be required on DrugOrder. I would think you could order a "generic" drug based on the Concept on the Order, but still want to represent this as a DrugOrder with the associated data like dose, etc.
    • Don't we want "accessionNumber" on TestOrder, since we previously had it directly on Order (we use this field in some modules)?
    • Do we need the "Orderable" interface and subclasses for our first pass here? What exactly is this going to do? Can you provide some concrete examples of how we would use this interface to make some of our Concepts implement Orderable? Would we make Drug implement Orderable? How would we do this until this functionality moves out of a module and into core? If we can't answer these questions, I would punt this to a later phase...
    1. For the first pass, we will be using Orderable in a orders autocomplete just to provide users with what they can order, the only concrete implementation acting as just a wrapper for Concepts or Drugs.

  3. 1. By the string order number, I believe the idea is that an Order record represents something in the physical world – a notation in a chart, a prescription, a lab request form, an EKG request form, etc. These physical forms can contain more than one item – a prescription can contain several drugs, a lab request form can request several panels, an EKG request can contain several types of EKG test. So I would be clear that the order number by itself would not necessarily be unique. Whether to add a sortOrder or line number field that could make it unique or just that order number plus ID is a natural key is something to be decided.

    2. I think the baby got thrown out with the bath water in getting rid of STAT as a boolean or an enum or something. It had somehow gotten tied up with conditional orders and other complexities.

    3. Using enums with Hibernate in modules hasn't worked through early 1.8s. I know there was a fix that was supposed to be propagated, but haven't found the ticket. Please make sure that this is handled before using enums.

    4. Test order assumes that the relationship between tests and specimens is 1:1. However, this is not true. Some tests require multiple specimens, for example, skin tests where both a suspect and a control specimen are taken from the patient; uptake tests, where a fasting specimen is compared to a specimen taken 1 hour after eating. The specimen is another real item that needs tracking, it needs a table of its own. I can see where a radiology order would have a different perspective, you want to orer multiple views of the same body part.

    5. Having looked at concept and its REST implementation, I think things would work out a lot easier if drug were a subclass of concept rather than a standalone table. That could be done with no change of field contents (although drug.uuid would become meaningless).

  4. I have just come out of a discussion of an in-patient module, and I want to re-emphasize point 1 of my April 24 post. I think that we have to acknowledge that ordering multiple things rather than a single thing is the rule rather than the exception, and that we are talking about more than just fixed sets of things, but collections of sets of things and individual things. I think we need to have an object for the ordering that relates to the things ordered in the same way that visit relates to encounter or a customer order relates to line items.

    In the case of the in-patient module, one of the important things is the ward nurse's task list. There may be a standing order that all in-patients are to have vitals taken every 4 hours, as well as a doctor's orders to measure urine output every 4 hours and to give medication x twice a day before meals. What the nurse needs to see is a time- and patient-ordered list of what has to be done (measure temp, BP, pulse, urine; dispense drug x), along with special instructions (urine: notify if <50 ml; drug x: before meal). For this, it would be useful if the order item had a recurrence specification (like drug frequency but in a machine readable form) and a date-time of last occurrence. For the recurrence spec, I would base it on something like the recurrence page of Outlook or some other appointment scheduler. This would also be aided by the generalized queueing capability which I suggested in a recent e-mail.