Skip to content

Commit 4e98261

Browse files
Vinay Venuwluyima
Vinay Venu
authored andcommittedJan 29, 2014
TRUNK-4133 - Implement discontinueOrder API in OrderService.
TRUNK-4133 - Modify OrderService.saveOrder(Order order) to correctly handle discontinue orders Minor documentation change TRUNK-4133 - Ensure discontinue and discontinued orders have the same concept TRUNK-4133 - Changes based on review comments. Add annotations for junit tests. Some extra documentation. Remove unnecessary executeDataset calls in OrderServiceTest Make concept for discontinuation a normal concept, not a numeric Additional assert statements
1 parent 73cb57c commit 4e98261

File tree

7 files changed

+359
-5
lines changed

7 files changed

+359
-5
lines changed
 

‎api/src/main/java/org/openmrs/Order.java

+20
Original file line numberDiff line numberDiff line change
@@ -529,4 +529,24 @@ public CareSetting getCareSetting() {
529529
public void setCareSetting(CareSetting careSetting) {
530530
this.careSetting = careSetting;
531531
}
532+
533+
/**
534+
* Creates a discontinuation order for this order, sets the previousOrder and action fields,
535+
* note that the discontinuation order needs to be saved for the discontinuation to take effect
536+
*
537+
* @return the newly created order
538+
* @since 1.10
539+
* @should set previousOrder on new order
540+
* @should set action to discontinue on new order
541+
* @should set this care setting to new order
542+
*/
543+
public Order cloneForDiscontinuing() {
544+
Order newOrder = new Order();
545+
newOrder.setCareSetting(this.getCareSetting());
546+
newOrder.setConcept(this.getConcept());
547+
newOrder.setAction(Action.DISCONTINUE);
548+
newOrder.setPreviousOrder(this);
549+
550+
return newOrder;
551+
}
532552
}

‎api/src/main/java/org/openmrs/api/OrderService.java

+36-3
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,10 @@ public interface OrderService extends OpenmrsService {
4848
* @throws APIException
4949
* @should not save order if order doesnt validate
5050
* @should save discontinued reason non coded
51-
*/
51+
* @should discontinue existing active order if new order being saved with action to discontinue
52+
* @should discontinue previousOrder if it is not already discontinued
53+
* @should fail if concept in previous order does not match this concept
54+
*/
5255
@Authorized( { PrivilegeConstants.EDIT_ORDERS, PrivilegeConstants.ADD_ORDERS })
5356
public Order saveOrder(Order order) throws APIException;
5457

@@ -224,8 +227,8 @@ public <Ord extends Order> List<Ord> getActiveOrders(Patient patient, Class<Ord>
224227
Date asOfDate);
225228

226229
/**
227-
* Retrieve care setting by type
228-
*
230+
* Retrieve care setting
231+
*
229232
* @param careSettingId
230233
* @return the care setting
231234
* @since 1.10
@@ -241,4 +244,34 @@ public <Ord extends Order> List<Ord> getActiveOrders(Patient patient, Class<Ord>
241244
* @should return the order frequency that matched the specified id
242245
*/
243246
public OrderFrequency getOrderFrequency(Integer orderFrequencyId);
247+
248+
/**
249+
* Discontinues an order.
250+
* Creates a new order that discontinues the orderToDiscontinue
251+
*
252+
* @param orderToDiscontinue
253+
* @param reasonCoded
254+
* @param discontinueDate
255+
* @return the new order that discontinued orderToDiscontinue
256+
* @throws APIException if the <code>action</code> of orderToDiscontinue is <code>Order.Action.DISCONTINUE</code>
257+
* @since 1.10
258+
* @should populate correct attributes on the discontinue and discontinued orders
259+
* @should fail for a discontinue order
260+
*/
261+
public Order discontinueOrder(Order orderToDiscontinue, Concept reasonCoded, Date discontinueDate);
262+
263+
/**
264+
* Discontinues an order.
265+
* Creates a new order that discontinues the orderToDiscontinue.
266+
*
267+
* @param orderToDiscontinue
268+
* @param reasonNonCoded
269+
* @param discontinueDate
270+
* @return the new order that discontinued orderToDiscontinue
271+
* @throws APIException if the <code>action</code> of orderToDiscontinue is <code>Order.Action.DISCONTINUE</code>
272+
* @since 1.10
273+
* @should populate correct attributes on the discontinue and discontinued orders
274+
* @should fail for a discontinue order
275+
*/
276+
public Order discontinueOrder(Order orderToDiscontinue, String reasonNonCoded, Date discontinueDate);
244277
}

‎api/src/main/java/org/openmrs/api/impl/OrderServiceImpl.java

+82
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ public void setOrderDAO(OrderDAO dao) {
6969
*/
7070
public Order saveOrder(Order order) throws APIException {
7171
if (order.getOrderId() == null) {
72+
discontinueExistingOrdersIfRequired(order);
7273
//TODO call module registered order number generators
7374
//and if there is none, use the default below
7475
try {
@@ -83,6 +84,46 @@ public Order saveOrder(Order order) throws APIException {
8384

8485
return dao.saveOrder(order);
8586
}
87+
88+
/**
89+
* If this is a discontinue order, ensure that the previous order is discontinued.
90+
* If a previousOrder is present, then ensure this is discontinued.
91+
* If no previousOrder is present, then try to find a previousOrder and discontinue it.
92+
* If cannot find a previousOrder, throw exception
93+
*
94+
* @param order
95+
*/
96+
private void discontinueExistingOrdersIfRequired(Order order) {
97+
//Ignore and return if this is not an order to discontinue
98+
if (!Order.Action.DISCONTINUE.equals(order.getAction()))
99+
return;
100+
101+
//Discontinue previousOrder if it is not already
102+
Order previousOrder = order.getPreviousOrder();
103+
if (previousOrder != null) {
104+
if (!previousOrder.getConcept().equals(order.getConcept())) {
Has a conversation. Original line has a conversation.
105+
throw new APIException("Concept of previous order and this order should be the same");
106+
}
107+
108+
if (previousOrder.getDateStopped() == null) {
109+
discontinue(previousOrder, order.getStartDate());
110+
}
111+
return;
112+
}
113+
114+
//Discontinue the first found order corresponding to this DC order.
115+
List<? extends Order> orders = getActiveOrders(order.getPatient(), order.getClass(), order.getCareSetting(), null);
116+
for (Order activeOrder : orders) {
117+
if (activeOrder.getConcept().equals(order.getConcept())) {
Has conversations. Original line has conversations.
118+
order.setPreviousOrder(activeOrder);
119+
discontinue(activeOrder, order.getStartDate());
120+
return;
121+
}
122+
}
123+
124+
throw new APIException("Could not find an active order with the concept " + order.getConcept()
125+
+ " to discontinue. ");
126+
}
86127

87128
/**
88129
* @see org.openmrs.api.OrderService#purgeOrder(org.openmrs.Order)
@@ -272,4 +313,45 @@ public CareSetting getCareSetting(Integer careSettingId) {
272313
public OrderFrequency getOrderFrequency(Integer orderFrequencyId) {
273314
return dao.getOrderFrequency(orderFrequencyId);
274315
}
316+
317+
/**
318+
* @see org.openmrs.api.OrderService#discontinueOrder(org.openmrs.Order, org.openmrs.Concept, java.util.Date)
319+
*/
320+
@Override
321+
public Order discontinueOrder(Order orderToDiscontinue, Concept reasonCoded, Date discontinueDate) {
322+
discontinue(orderToDiscontinue, discontinueDate);
323+
324+
Order newOrder = orderToDiscontinue.cloneForDiscontinuing();
325+
newOrder.setDiscontinuedReason(reasonCoded);
326+
327+
return saveOrder(newOrder);
328+
}
329+
330+
/**
331+
* @see org.openmrs.api.OrderService#discontinueOrder(org.openmrs.Order, String, java.util.Date)
332+
*/
333+
@Override
334+
public Order discontinueOrder(Order orderToDiscontinue, String reasonNonCoded, Date discontinueDate) {
335+
discontinue(orderToDiscontinue, discontinueDate);
336+
337+
Order newOrder = orderToDiscontinue.cloneForDiscontinuing();
338+
newOrder.setDiscontinuedReasonNonCoded(reasonNonCoded);
339+
340+
return saveOrder(newOrder);
341+
}
342+
343+
/**
344+
* Make necessary checks, set necessary fields for discontinuing <code>orderToDiscontinue</code> and save.
345+
*
346+
* @param orderToDiscontinue
347+
* @param discontinueDate
348+
*/
349+
private void discontinue(Order orderToDiscontinue, Date discontinueDate) {
350+
if (orderToDiscontinue.getAction().equals(Order.Action.DISCONTINUE)) {
351+
throw new APIException("An order with action " + Order.Action.DISCONTINUE + " cannot be discontinued. ");
352+
}
353+
354+
orderToDiscontinue.setDateStopped(discontinueDate);
355+
saveOrder(orderToDiscontinue);
356+
}
275357
}

‎api/src/test/java/org/openmrs/OrderTest.java

+47-1
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,13 @@
1414
package org.openmrs;
1515

1616
import org.junit.Test;
17+
import org.openmrs.test.Verifies;
1718

1819
import java.text.DateFormat;
1920
import java.text.SimpleDateFormat;
21+
import java.util.UUID;
2022

23+
import static org.junit.Assert.assertEquals;
2124
import static org.junit.Assert.assertFalse;
2225
import static org.junit.Assert.assertTrue;
2326

@@ -90,5 +93,48 @@ public void shouldIsCurrent() throws Exception {
9093
assertTrue("should be current between startDate and dateStopped", o.isCurrent(ymd.parse("2007-10-26")));
9194
assertFalse("shouldn't be current after dateStopped", o.isCurrent(ymd.parse("2007-11-26")));
9295
}
93-
96+
97+
/**
98+
* @see {@link Order#cloneForDiscontinuing()}
99+
*/
100+
@Test
101+
@Verifies(value = "set previousOrder on new order", method = "cloneForDiscontinuing(Order)")
102+
public void cloneForDiscontinuing_shouldSetPreviousOrderOnNewOrder() {
103+
Order anOrder = new Order();
104+
anOrder.setUuid(UUID.randomUUID().toString());
105+
106+
Order orderThatCanDiscontinueTheOrder = anOrder.cloneForDiscontinuing();
107+
108+
assertEquals("should set previous order to anOrder", orderThatCanDiscontinueTheOrder.getPreviousOrder(), anOrder);
109+
}
110+
111+
/**
112+
* @see {@link Order#cloneForDiscontinuing()}
113+
*/
114+
@Test
115+
@Verifies(value = "set action to discontinue on new order", method = "cloneForDiscontinuing(Order)")
116+
public void cloneForDiscontinuing_shouldSetActionToDiscontinueOnNewOrder() {
117+
Order anOrder = new Order();
118+
anOrder.setUuid(UUID.randomUUID().toString());
119+
120+
Order orderThatCanDiscontinueTheOrder = anOrder.cloneForDiscontinuing();
121+
122+
assertEquals("should set new order action to new", orderThatCanDiscontinueTheOrder.getAction(),
123+
Order.Action.DISCONTINUE);
124+
}
125+
126+
/**
127+
* @see {@link Order#cloneForDiscontinuing()}
128+
*/
129+
@Test
130+
@Verifies(value = "set this care setting to new order", method = "cloneForDiscontinuing(Order)")
131+
public void cloneForDiscontinuing_shouldSetThisCareSettingToNewOrder() {
132+
Order anOrder = new Order();
133+
CareSetting careSetting = new CareSetting();
134+
anOrder.setCareSetting(careSetting);
135+
136+
Order orderThatCanDiscontinueTheOrder = anOrder.cloneForDiscontinuing();
137+
138+
assertEquals(anOrder.getCareSetting(), orderThatCanDiscontinueTheOrder.getCareSetting());
139+
}
94140
}

‎api/src/test/java/org/openmrs/api/OrderServiceTest.java

+163-1
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,12 @@
4141
import org.openmrs.test.Verifies;
4242
import org.openmrs.util.PrivilegeConstants;
4343

44+
import java.util.ArrayList;
45+
import java.util.Date;
46+
import java.util.HashSet;
47+
import java.util.List;
48+
import java.util.Set;
49+
4450
/**
4551
* TODO clean up and test all methods in OrderService
4652
*/
@@ -322,7 +328,7 @@ public void getActiveOrders_shouldReturnAllActiveDrugOrdersForTheSpecifiedPatien
322328
DrugOrder[] expectedOrders = { (DrugOrder) orderService.getOrder(3), (DrugOrder) orderService.getOrder(5) };
323329
assertThat(orders, hasItems(expectedOrders));
324330
}
325-
331+
326332
/**
327333
* @verifies return all active test orders for the specified patient
328334
* @see OrderService#getActiveOrders(org.openmrs.Patient, Class, org.openmrs.CareSetting,
@@ -415,5 +421,161 @@ public void getActiveOrders_shouldReturnActiveOrdersAsOfTheSpecifiedDate() throw
415421
Order[] expectedOrders5 = { orderService.getOrder(222), orderService.getOrder(3), orderService.getOrder(444),
416422
orderService.getOrder(5), orderService.getOrder(7) };
417423
assertThat(orders, hasItems(expectedOrders5));
424+
}
425+
426+
/**
427+
* @see {@link OrderService#discontinueOrder(org.openmrs.Order, String, java.util.Date)}
428+
*/
429+
@Test
430+
@Verifies(value = "populate correct attributes on the discontinue and discontinued orders", method = "discontinueOrder(Order, String, Date)")
431+
public void discontinueOrderWithNonCodedReason_shouldPopulateCorrectAttributesOnBothOrders() throws Exception {
432+
executeDataSet("org/openmrs/api/include/OrderServiceTest-globalProperties.xml");
433+
434+
OrderService orderService = Context.getOrderService();
435+
Order order = orderService.getOrderByOrderNumber("111");
436+
Date discontinueDate = new Date();
437+
String discontinueReasonNonCoded = "Test if I can discontinue this";
438+
439+
Order discontinueOrder = orderService.discontinueOrder(order, discontinueReasonNonCoded, discontinueDate);
440+
441+
Assert.assertEquals(order.getDateStopped(), discontinueDate);
442+
Assert.assertNotNull(discontinueOrder);
443+
Assert.assertNotNull(discontinueOrder.getId());
444+
Assert.assertEquals(discontinueOrder.getAction(), Action.DISCONTINUE);
445+
Assert.assertEquals(discontinueOrder.getDiscontinuedReasonNonCoded(), discontinueReasonNonCoded);
446+
Assert.assertEquals(discontinueOrder.getPreviousOrder(), order);
447+
}
448+
449+
/**
450+
* @see {@link OrderService#discontinueOrder(org.openmrs.Order, org.openmrs.Concept, java.util.Date)}
451+
*/
452+
@Test
453+
@Verifies(value = "populate correct attributes on the discontinue and discontinued orders", method = "discontinueOrder(Order, Concept, Date)")
454+
public void discontinueOrderWithConcept_shouldPopulateCorrectAttributesOnBothOrders() throws Exception {
455+
executeDataSet("org/openmrs/api/include/OrderServiceTest-globalProperties.xml");
456+
executeDataSet("org/openmrs/api/include/OrderServiceTest-discontinueReason.xml");
457+
458+
OrderService orderService = Context.getOrderService();
459+
Order order = orderService.getOrderByOrderNumber("111");
460+
Date discontinueDate = new Date();
461+
Concept concept = Context.getConceptService().getConcept(1);
462+
463+
Order discontinueOrder = orderService.discontinueOrder(order, concept, discontinueDate);
464+
465+
Assert.assertEquals(order.getDateStopped(), discontinueDate);
466+
Assert.assertNotNull(discontinueOrder);
467+
Assert.assertNotNull(discontinueOrder.getId());
468+
Assert.assertEquals(discontinueOrder.getAction(), Action.DISCONTINUE);
469+
Assert.assertEquals(discontinueOrder.getDiscontinuedReason(), concept);
470+
Assert.assertEquals(discontinueOrder.getPreviousOrder(), order);
471+
}
472+
473+
/**
474+
* @see {@link OrderService#discontinueOrder(org.openmrs.Order, String, java.util.Date)}
475+
*/
476+
@Test(expected = APIException.class)
477+
@Verifies(value = "fail when for a discontinue order", method = "discontinueOrder(Order, String, Date)")
478+
public void discontinueOrderWithNonCodedReason_shouldFailForADiscontinueOrder() throws Exception {
479+
executeDataSet("org/openmrs/api/include/OrderServiceTest-globalProperties.xml");
480+
executeDataSet("org/openmrs/api/include/OrderServiceTest-discontinuedOrder.xml");
481+
OrderService orderService = Context.getOrderService();
482+
Order discontinueOrder = orderService.getOrder(26);
483+
484+
orderService.discontinueOrder(discontinueOrder, "Test if I can discontinue this", null);
485+
}
486+
487+
488+
/**
489+
* @see {@link OrderService#discontinueOrder(org.openmrs.Order, org.openmrs.Concept, java.util.Date)}
490+
*/
491+
@Test(expected = APIException.class)
492+
@Verifies(value = "fail for a discontinue order", method = "discontinueOrder(Order, Concept, Date)")
493+
public void discontinueOrderWithConcept_shouldFailForADiscontinueOrder() throws Exception {
494+
executeDataSet("org/openmrs/api/include/OrderServiceTest-globalProperties.xml");
495+
executeDataSet("org/openmrs/api/include/OrderServiceTest-discontinuedOrder.xml");
496+
executeDataSet("org/openmrs/api/include/OrderServiceTest-discontinueReason.xml");
497+
OrderService orderService = Context.getOrderService();
498+
Order discontinueOrder = orderService.getOrder(26);
499+
500+
orderService.discontinueOrder(discontinueOrder, (Concept) null, null);
501+
}
502+
503+
/**
504+
* @see {@link OrderService#saveOrder(org.openmrs.Order)}
505+
*/
506+
@Test
507+
@Verifies(value = "discontinue existing active order if new order being saved with action to discontinue", method = "saveOrder(Order)")
508+
public void saveOrder_shouldDiscontinueExistingActiveOrderIfNewOrderBeingSavedWithActionToDiscontinue() throws Exception {
509+
executeDataSet("org/openmrs/api/include/OrderServiceTest-globalProperties.xml");
510+
OrderService orderService = Context.getOrderService();
511+
Order order = new Order();
512+
order.setAction(Order.Action.DISCONTINUE);
513+
order.setDiscontinuedReasonNonCoded("Discontinue this");
514+
order.setPatient(Context.getPatientService().getPatient(7));
515+
order.setConcept(Context.getConceptService().getConcept(88));
516+
order.setCareSetting(orderService.getCareSetting(1));
517+
order.setStartDate(new Date());
518+
519+
//We are trying to discontinue order id 111 in standardTestDataset.xml
520+
Order expectedPreviousOrder = orderService.getOrder(111);
521+
Assert.assertNull(expectedPreviousOrder.getDateStopped());
522+
523+
order = orderService.saveOrder(order);
524+
525+
Assert.assertNotNull("should populate dateStopped in previous order", expectedPreviousOrder.getDateStopped());
526+
Assert.assertNotNull("should save discontinue order", order.getId());
527+
Assert.assertEquals(expectedPreviousOrder, order.getPreviousOrder());
528+
Assert.assertNotNull(expectedPreviousOrder.getDateStopped());
529+
}
530+
531+
/**
532+
* @see {@link OrderService#saveOrder(org.openmrs.Order)}
533+
*/
534+
@Test
535+
@Verifies(value = "discontinue previousOrder if it is not already discontinued", method = "saveOrder(Order)")
536+
public void saveOrder_shouldDiscontinuePreviousOrderIfItIsNotAlreadyDiscontinued() throws Exception {
537+
executeDataSet("org/openmrs/api/include/OrderServiceTest-globalProperties.xml");
538+
OrderService orderService = Context.getOrderService();
539+
//We are trying to discontinue order id 111 in standardTestDataset.xml
540+
Order order = new Order();
541+
order.setAction(Order.Action.DISCONTINUE);
542+
order.setDiscontinuedReasonNonCoded("Discontinue this");
543+
order.setPatient(Context.getPatientService().getPatient(7));
544+
order.setConcept(Context.getConceptService().getConcept(88));
545+
order.setCareSetting(orderService.getCareSetting(1));
546+
order.setStartDate(new Date());
547+
Order previousOrder = orderService.getOrder(111);
548+
order.setPreviousOrder(previousOrder);
549+
550+
orderService.saveOrder(order);
551+
552+
Assert.assertNotNull("previous order should be discontinued", previousOrder.getDateStopped());
553+
}
554+
555+
/**
556+
* @see {@link OrderService#saveOrder(org.openmrs.Order)}
557+
*/
558+
@Test(expected = APIException.class)
559+
@Verifies(value = "fail if concept in previous order does not match this concept", method = "saveOrder(Order)")
560+
public void saveOrder_shouldFailIfConceptInPreviousOrderDoesNotMatchThisConcept() throws Exception {
561+
executeDataSet("org/openmrs/api/include/OrderServiceTest-globalProperties.xml");
562+
OrderService orderService = Context.getOrderService();
563+
//We are trying to discontinue order id 111 in standardTestDataset.xml
564+
Order order = new Order();
565+
order.setAction(Order.Action.DISCONTINUE);
566+
order.setDiscontinuedReasonNonCoded("Discontinue this");
567+
order.setPatient(Context.getPatientService().getPatient(7));
568+
order.setConcept(Context.getConceptService().getConcept(3));
569+
order.setCareSetting(orderService.getCareSetting(1));
570+
order.setStartDate(new Date());
571+
Order previousOrder = orderService.getOrder(111);
572+
order.setPreviousOrder(previousOrder);
573+
574+
orderService.saveOrder(order);
575+
}
576+
577+
private boolean isOrderActive(Order order) {
578+
return order.getDateStopped() == null && order.getAutoExpireDate() == null
579+
&& order.getAction() != Action.DISCONTINUE;
418580
}
419581
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<?xml version='1.0' encoding='UTF-8'?>
2+
<dataset>
3+
<concept concept_id="1" retired="false" datatype_id="1" class_id="11" is_set="false" creator="1" date_created="2005-01-01 00:00:00.0" uuid="4a5048b1-cf85-4c64-9339-7cab41e5e364"/>
4+
<concept_description concept_description_id="1" concept_id="1" description="this is a description" locale="en" creator="1" date_created="2005-01-01 00:00:00.0" uuid="8b46218d-8dcf-4f45-91b5-837d11486a53"/>
5+
<concept_name concept_id="1" name="Discontinue reason" locale="en" creator="1" date_created="2005-01-01 00:00:00.0" concept_name_id="1" voided="false" uuid="8add472b-156b-468f-ad62-d9333fb86976" concept_name_type="FULLY_SPECIFIED" locale_preferred="0"/>
6+
</dataset>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<?xml version='1.0' encoding='UTF-8'?>
2+
<dataset>
3+
<orders order_id="25" order_number="1" urgency="ROUTINE" order_action="NEW" concept_id="88" orderer="1" instructions="2x daily" start_date="2008-08-08 00:00:00.0" date_stopped="2008-08-15 00:00:00.0" creator="1" date_created="2008-08-19 12:20:22.0" voided="false" patient_id="7" uuid="921de0a3-05c4-444a-be03-e01b4c4b9152" care_setting="1"/>
4+
<orders order_id="26" previous_order_id="1" order_number="111" urgency="ROUTINE" order_action="DISCONTINUE" concept_id="88" creator="1" date_created="2008-08-15 00:00:00.0" voided="false" patient_id="7" uuid="e1f95924-697a-11e3-bd76-0800271c1b55" care_setting="1"/>
5+
</dataset>

0 commit comments

Comments
 (0)
Please sign in to comment.