Skip to content

Commit 21c98ae

Browse files
authored
(BREAKING) O3-2748 add endpoint for un-doing transition of queue entries; rename endpoints related to transitions
1 parent 210af0d commit 21c98ae

File tree

10 files changed

+237
-31
lines changed

10 files changed

+237
-31
lines changed

api/src/main/java/org/openmrs/module/queue/api/QueueEntryService.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,18 @@ public interface QueueEntryService {
5959
*/
6060
QueueEntry transitionQueueEntry(@NotNull QueueEntryTransition queueEntryTransition);
6161

62+
/**
63+
* Undos a transition to the input queue entry by voiding it and making its previous queue entry
64+
* active by setting the previous entry's end time to null.
65+
*
66+
* @see QueueEntryService#getPreviousQueueEntry(QueueEntry)
67+
* @param queueEntry the queue entry to undo transition to. Must be active
68+
* @return the previous queue entry, re-activated
69+
* @throws IllegalArgumentException if the previous queue entry does not exist
70+
* @throws IllegalStateException if multiple previous entries are identified
71+
*/
72+
QueueEntry undoTransition(@NotNull QueueEntry queueEntry);
73+
6274
/**
6375
* Voids a queue entry
6476
*
@@ -113,4 +125,15 @@ String generateVisitQueueNumber(@NotNull Location location, @NotNull Queue queue
113125
* @param sortWeightGenerator the SortWeightGenerator to set
114126
*/
115127
void setSortWeightGenerator(SortWeightGenerator sortWeightGenerator);
128+
129+
/**
130+
* Given a specified queue entry Q, return its previous queue entry P, where P has same patient and
131+
* visit as Q, and P.endedAt time is same as Q.startedAt time, and P.queue is same as
132+
* Q.queueComingFrom
133+
*
134+
* @param queueEntry
135+
* @return the previous queue entry, null otherwise.
136+
* @throws IllegalStateException if multiple previous queue entries are identified
137+
*/
138+
QueueEntry getPreviousQueueEntry(@NotNull QueueEntry queueEntry);
116139
}

api/src/main/java/org/openmrs/module/queue/api/dao/impl/QueueEntryDaoImpl.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,10 @@ private Criteria createCriteriaFromSearchCriteria(QueueEntrySearchCriteria searc
6464
limitByCollectionProperty(c, "qe.queueComingFrom", searchCriteria.getQueuesComingFrom());
6565
limitToGreaterThanOrEqualToProperty(c, "qe.startedAt", searchCriteria.getStartedOnOrAfter());
6666
limitToLessThanOrEqualToProperty(c, "qe.startedAt", searchCriteria.getStartedOnOrBefore());
67+
limitToEqualsProperty(c, "qe.startedAt", searchCriteria.getStartedOn());
6768
limitToGreaterThanOrEqualToProperty(c, "qe.endedAt", searchCriteria.getEndedOnOrAfter());
6869
limitToLessThanOrEqualToProperty(c, "qe.endedAt", searchCriteria.getEndedOnOrBefore());
70+
limitToEqualsProperty(c, "qe.endedAt", searchCriteria.getEndedOn());
6971
if (searchCriteria.getHasVisit() == Boolean.TRUE) {
7072
c.add(Restrictions.isNotNull("qe.visit"));
7173
} else if (searchCriteria.getHasVisit() == Boolean.FALSE) {

api/src/main/java/org/openmrs/module/queue/api/impl/QueueEntryServiceImpl.java

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import java.time.LocalDateTime;
1818
import java.time.LocalTime;
1919
import java.time.ZoneId;
20+
import java.util.Arrays;
2021
import java.util.Collections;
2122
import java.util.Date;
2223
import java.util.List;
@@ -131,6 +132,32 @@ public QueueEntry transitionQueueEntry(QueueEntryTransition queueEntryTransition
131132
return getProxiedQueueEntryService().saveQueueEntry(queueEntryToStart);
132133
}
133134

135+
/**
136+
* @see QueueEntryService#undoTransition(QueueEntry)
137+
*/
138+
@Override
139+
public QueueEntry undoTransition(@NotNull QueueEntry queueEntry) {
140+
// TODO: Exceptions should be translatable and human readable on the frontend.
141+
// See: https://openmrs.atlassian.net/browse/O3-2988
142+
if (queueEntry.getVoided()) {
143+
throw new IllegalArgumentException("cannot undo transition on a voided queue entry");
144+
}
145+
if (queueEntry.getEndedAt() != null) {
146+
throw new IllegalArgumentException("cannot undo transition on an ended queue entry");
147+
}
148+
QueueEntry prevQueueEntry = getPreviousQueueEntry(queueEntry);
149+
if (prevQueueEntry == null) {
150+
throw new IllegalArgumentException("specified queue entry does not have a previous queue entry");
151+
}
152+
prevQueueEntry.setEndedAt(null);
153+
prevQueueEntry = dao.createOrUpdate(prevQueueEntry);
154+
155+
queueEntry.setVoided(true);
156+
queueEntry.setVoidReason("undo transition");
157+
dao.createOrUpdate(queueEntry);
158+
return prevQueueEntry;
159+
}
160+
134161
/**
135162
* @see QueueEntryService#voidQueueEntry(QueueEntry, String)
136163
*/
@@ -225,4 +252,28 @@ private void endQueueEntry(@NotNull QueueEntry queueEntry) {
225252
queueEntry.setEndedAt(new Date());
226253
dao.createOrUpdate(queueEntry);
227254
}
255+
256+
@Override
257+
@Transactional(readOnly = true)
258+
public QueueEntry getPreviousQueueEntry(@NotNull QueueEntry queueEntry) {
259+
Queue queueComingFrom = queueEntry.getQueueComingFrom();
260+
if(queueComingFrom == null) {
261+
return null;
262+
}
263+
QueueEntrySearchCriteria criteria = new QueueEntrySearchCriteria();
264+
criteria.setPatient(queueEntry.getPatient());
265+
criteria.setVisit(queueEntry.getVisit());
266+
criteria.setEndedOn(queueEntry.getStartedAt());
267+
criteria.setQueues(Arrays.asList(queueComingFrom));
268+
List<QueueEntry> prevQueueEntries = dao.getQueueEntries(criteria);
269+
if (prevQueueEntries.size() == 1) {
270+
return prevQueueEntries.get(0);
271+
} else if (prevQueueEntries.size() > 1) {
272+
// TODO: Exceptions should be translatable and human readable on the frontend.
273+
// See: https://openmrs.atlassian.net/browse/O3-2988
274+
throw new IllegalStateException("Multiple previous queue entries found");
275+
} else {
276+
return null;
277+
}
278+
}
228279
}

api/src/main/java/org/openmrs/module/queue/api/search/QueueEntrySearchCriteria.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,11 +61,15 @@ public class QueueEntrySearchCriteria implements Serializable {
6161

6262
private Date startedOnOrBefore;
6363

64+
private Date startedOn;
65+
6466
private Boolean isEnded = null;
6567

6668
private Date endedOnOrAfter;
6769

6870
private Date endedOnOrBefore;
6971

72+
private Date endedOn;
73+
7074
private boolean includedVoided = false;
7175
}

api/src/test/java/org/openmrs/module/queue/api/QueueEntryServiceTest.java

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import static org.mockito.ArgumentMatchers.any;
1717
import static org.mockito.Mockito.*;
1818

19+
import java.util.Arrays;
1920
import java.util.Collections;
2021
import java.util.Date;
2122
import java.util.Optional;
@@ -286,6 +287,57 @@ public void shouldTransitionQueueEntry() {
286287
assertNull(queueEntry3.getEndedAt());
287288
}
288289

290+
@Test
291+
public void shouldUndoTransitionQueueEntry() {
292+
Patient patient1 = new Patient();
293+
Visit visit1 = new Visit();
294+
visit1.setPatient(patient1);
295+
Queue queue0 = new Queue();
296+
Queue queue1 = new Queue();
297+
Concept concept1 = new Concept();
298+
String string1 = "starting";
299+
double double1 = 5.0;
300+
Location location1 = new Location();
301+
Provider provider1 = new Provider();
302+
Date date1 = DateUtils.addHours(new Date(), -12);
303+
Date date2 = DateUtils.addHours(date1, 6);
304+
305+
QueueEntry queueEntry1 = new QueueEntry();
306+
queueEntry1.setQueue(queue1);
307+
queueEntry1.setPatient(patient1);
308+
queueEntry1.setVisit(visit1);
309+
queueEntry1.setPriority(concept1);
310+
queueEntry1.setPriorityComment(string1);
311+
queueEntry1.setStatus(concept1);
312+
queueEntry1.setSortWeight(double1);
313+
queueEntry1.setLocationWaitingFor(location1);
314+
queueEntry1.setProviderWaitingFor(provider1);
315+
queueEntry1.setQueueComingFrom(queue0);
316+
queueEntry1.setStartedAt(date1);
317+
assertNull(queueEntry1.getEndedAt());
318+
319+
// Mock the DAO to return the object being saved
320+
when(dao.createOrUpdate(any())).thenAnswer(invocation -> invocation.getArguments()[0]);
321+
322+
// Create transition
323+
QueueEntryTransition transition1 = new QueueEntryTransition();
324+
transition1.setQueueEntryToTransition(queueEntry1);
325+
transition1.setTransitionDate(date2);
326+
QueueEntry queueEntry2 = queueEntryService.transitionQueueEntry(transition1);
327+
328+
// Mock the DAO to searches for previous queue entry correctly
329+
QueueEntrySearchCriteria criteria = new QueueEntrySearchCriteria();
330+
criteria.setPatient(patient1);
331+
criteria.setVisit(visit1);
332+
criteria.setEndedOn(date2);
333+
criteria.setQueues(Arrays.asList(queueEntry2.getQueueComingFrom()));
334+
when(dao.getQueueEntries(criteria)).thenReturn(Arrays.asList(queueEntry1));
335+
336+
queueEntryService.undoTransition(queueEntry2);
337+
assertThat(queueEntry2.getVoided(), equalTo(true));
338+
assertNull(queueEntry1.getEndedAt());
339+
}
340+
289341
@Test
290342
public void shouldGenerateVisitQueueNumber() {
291343
Visit visit = new Visit();

omod/src/main/java/org/openmrs/module/queue/web/QueueEntryTransitionRestController.java

Lines changed: 34 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,17 @@
1010
package org.openmrs.module.queue.web;
1111

1212
import java.util.Date;
13-
import java.util.Map;
1413
import java.util.Optional;
1514

1615
import org.openmrs.Concept;
1716
import org.openmrs.api.APIException;
17+
import org.openmrs.module.queue.api.QueueEntryService;
1818
import org.openmrs.module.queue.api.QueueServicesWrapper;
1919
import org.openmrs.module.queue.model.Queue;
2020
import org.openmrs.module.queue.model.QueueEntry;
2121
import org.openmrs.module.queue.model.QueueEntryTransition;
22+
import org.openmrs.module.queue.web.dto.QueueEntryTransitionRequest;
23+
import org.openmrs.module.queue.web.dto.UndoQueueEntryTransitionRequest;
2224
import org.openmrs.module.webservices.rest.web.ConversionUtil;
2325
import org.openmrs.module.webservices.rest.web.RestConstants;
2426
import org.openmrs.module.webservices.rest.web.representation.Representation;
@@ -34,80 +36,81 @@
3436
* The main controller that exposes additional end points for order entry
3537
*/
3638
@Controller
37-
@RequestMapping(value = "/rest/" + RestConstants.VERSION_1 + "/queue-entry-transition")
3839
public class QueueEntryTransitionRestController extends BaseRestController {
3940

40-
public static final String QUEUE_ENTRY_TO_TRANSITION = "queueEntryToTransition";
41-
42-
public static final String TRANSITION_DATE = "transitionDate";
43-
44-
public static final String NEW_QUEUE = "newQueue";
45-
46-
public static final String NEW_STATUS = "newStatus";
47-
48-
public static final String NEW_PRIORITY = "newPriority";
49-
50-
public static final String NEW_PRIORITY_COMMENT = "newPriorityComment";
51-
5241
private final QueueServicesWrapper services;
5342

5443
@Autowired
5544
public QueueEntryTransitionRestController(QueueServicesWrapper services) {
5645
this.services = services;
5746
}
5847

59-
@RequestMapping(method = { RequestMethod.PUT, RequestMethod.POST })
48+
@RequestMapping(value = "/rest/" + RestConstants.VERSION_1 + "/queue-entry/transition", method = { RequestMethod.PUT,
49+
RequestMethod.POST })
6050
@ResponseBody
61-
public Object transitionQueueEntry(@RequestBody Map<String, String> body) {
51+
public Object transitionQueueEntry(@RequestBody QueueEntryTransitionRequest body) {
6252
QueueEntryTransition transition = new QueueEntryTransition();
6353

6454
// Queue Entry to Transition
65-
String queueEntryUuid = body.get(QUEUE_ENTRY_TO_TRANSITION);
55+
String queueEntryUuid = body.getQueueEntryToTransition();
6656
QueueEntry queueEntry = services.getQueueEntryService().getQueueEntryByUuid(queueEntryUuid)
67-
.orElseThrow(() -> new APIException(QUEUE_ENTRY_TO_TRANSITION + " is a required parameter"));
57+
.orElseThrow(() -> new APIException("queueEntryToTransition not specified or found"));
6858
transition.setQueueEntryToTransition(queueEntry);
6959

7060
// Transition Date
7161
Date transitionDate = new Date();
72-
if (body.containsKey(TRANSITION_DATE)) {
73-
transitionDate = (Date) ConversionUtil.convert(body.get(TRANSITION_DATE), Date.class);
62+
if (body.getTransitionDate() != null) {
63+
transitionDate = (Date) ConversionUtil.convert(body.getTransitionDate(), Date.class);
7464
}
7565
if (transitionDate == null) {
76-
throw new APIException("Invalid transition date specified: " + body.get(TRANSITION_DATE));
66+
throw new APIException("Invalid transition date specified: " + body.getTransitionDate());
7767
}
7868
transition.setTransitionDate(transitionDate);
7969

8070
// Queue
81-
if (body.containsKey(NEW_QUEUE)) {
82-
Optional<Queue> queueOptional = services.getQueueService().getQueueByUuid(body.get(NEW_QUEUE));
71+
if (body.getNewQueue() != null) {
72+
Optional<Queue> queueOptional = services.getQueueService().getQueueByUuid(body.getNewQueue());
8373
if (!queueOptional.isPresent()) {
84-
throw new APIException("Invalid queue specified: " + body.get(NEW_QUEUE));
74+
throw new APIException("Invalid queue specified: " + body.getNewQueue());
8575
}
8676
transition.setNewQueue(queueOptional.get());
8777
}
8878

8979
// Status
90-
if (body.containsKey(NEW_STATUS)) {
91-
Concept concept = services.getConcept(body.get(NEW_STATUS));
80+
if (body.getNewStatus() != null) {
81+
Concept concept = services.getConcept(body.getNewStatus());
9282
if (concept == null) {
93-
throw new APIException("Invalid status specified: " + body.get(NEW_STATUS));
83+
throw new APIException("Invalid status specified: " + body.getNewStatus());
9484
}
9585
transition.setNewStatus(concept);
9686
}
9787

9888
// Priority
99-
if (body.containsKey(NEW_PRIORITY)) {
100-
Concept concept = services.getConcept(body.get(NEW_PRIORITY));
89+
if (body.getNewPriority() != null) {
90+
Concept concept = services.getConcept(body.getNewPriority());
10191
if (concept == null) {
102-
throw new APIException("Invalid priority specified: " + body.get(NEW_PRIORITY));
92+
throw new APIException("Invalid priority specified: " + body.getNewPriority());
10393
}
10494
transition.setNewPriority(concept);
10595
}
10696

107-
transition.setNewPriorityComment(body.get(NEW_PRIORITY_COMMENT));
97+
transition.setNewPriorityComment(body.getNewPriorityComment());
10898

10999
// Execute transition
110100
QueueEntry newQueueEntry = services.getQueueEntryService().transitionQueueEntry(transition);
111101
return ConversionUtil.convertToRepresentation(newQueueEntry, Representation.REF);
112102
}
103+
104+
@RequestMapping(value = "/rest/" + RestConstants.VERSION_1 + "/queue-entry/transition", method = RequestMethod.DELETE)
105+
@ResponseBody
106+
public Object undoTransition(@RequestBody UndoQueueEntryTransitionRequest body) {
107+
QueueEntryService qes = services.getQueueEntryService();
108+
Optional<QueueEntry> queueEntry = qes.getQueueEntryByUuid(body.getQueueEntry());
109+
if (queueEntry.isPresent()) {
110+
QueueEntry unEndedQueueEntry = services.getQueueEntryService().undoTransition(queueEntry.get());
111+
return ConversionUtil.convertToRepresentation(unEndedQueueEntry, Representation.REF);
112+
} else {
113+
throw new APIException("Invalid queue entry");
114+
}
115+
}
113116
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/*
2+
* This Source Code Form is subject to the terms of the Mozilla Public License,
3+
* v. 2.0. If a copy of the MPL was not distributed with this file, You can
4+
* obtain one at http://mozilla.org/MPL/2.0/. OpenMRS is also distributed under
5+
* the terms of the Healthcare Disclaimer located at http://openmrs.org/license.
6+
*
7+
* Copyright (C) OpenMRS Inc. OpenMRS is a registered trademark and the OpenMRS
8+
* graphic logo is a trademark of OpenMRS Inc.
9+
*/
10+
package org.openmrs.module.queue.web.dto;
11+
12+
import lombok.Getter;
13+
14+
@Getter
15+
public class QueueEntryTransitionRequest {
16+
17+
private String queueEntryToTransition;
18+
19+
private String transitionDate;
20+
21+
private String newQueue;
22+
23+
private String newStatus;
24+
25+
private String newPriority;
26+
27+
private String newPriorityComment;
28+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
/*
2+
* This Source Code Form is subject to the terms of the Mozilla Public License,
3+
* v. 2.0. If a copy of the MPL was not distributed with this file, You can
4+
* obtain one at http://mozilla.org/MPL/2.0/. OpenMRS is also distributed under
5+
* the terms of the Healthcare Disclaimer located at http://openmrs.org/license.
6+
*
7+
* Copyright (C) OpenMRS Inc. OpenMRS is a registered trademark and the OpenMRS
8+
* graphic logo is a trademark of OpenMRS Inc.
9+
*/
10+
package org.openmrs.module.queue.web.dto;
11+
12+
import lombok.Getter;
13+
14+
@Getter
15+
public class UndoQueueEntryTransitionRequest {
16+
17+
private String queueEntry;
18+
}

0 commit comments

Comments
 (0)