Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

O3-2862 - Queue Module - validate that queue entry falls within visit… #52

Merged
merged 5 commits into from
Feb 15, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public License,
* v. 2.0. If a copy of the MPL was not distributed with this file, You can
* obtain one at http://mozilla.org/MPL/2.0/. OpenMRS is also distributed under
* the terms of the Healthcare Disclaimer located at http://openmrs.org/license.
*
* Copyright (C) OpenMRS Inc. OpenMRS is a registered trademark and the OpenMRS
* graphic logo is a trademark of OpenMRS Inc.
*/
package org.openmrs.module.queue.validators;

import java.util.Date;
import java.util.List;

import org.openmrs.Visit;
import org.openmrs.annotation.Handler;
import org.openmrs.module.queue.api.QueueEntryService;
import org.openmrs.module.queue.api.search.QueueEntrySearchCriteria;
import org.openmrs.module.queue.model.QueueEntry;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.validation.Errors;
import org.springframework.validation.Validator;

@Handler(supports = { Visit.class }, order = 60)
public class VisitWithQueueEntriesValidator implements Validator {

private final QueueEntryService queueEntryService;

@Autowired
public VisitWithQueueEntriesValidator(@Qualifier("queue.QueueEntryService") QueueEntryService queueEntryService) {
this.queueEntryService = queueEntryService;
}

@Override
public boolean supports(Class<?> clazz) {
return Visit.class.isAssignableFrom(clazz);
}

@Override
public void validate(Object target, Errors errors) {
if (!(target instanceof Visit)) {
throw new IllegalArgumentException("the parameter target must be of type " + Visit.class);
}
Visit visit = (Visit) target;

// This implementation is copied to match the core validator approach to nested encounters
if (visit.getId() != null) {
Date startDateTime = visit.getStartDatetime();
Date stopDateTime = visit.getStopDatetime();

QueueEntrySearchCriteria criteria = new QueueEntrySearchCriteria();
criteria.setIsEnded(null);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume setting this to null means returns all queues, both those that are ended and those that aren't ended.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct, the default (which I don't love, but the way it is implemented) is to only return active entries by default. So to get all entries, you need to explicitly null out this property :/

criteria.setVisit(visit);
List<QueueEntry> queueEntries = queueEntryService.getQueueEntries(criteria);
for (QueueEntry queueEntry : queueEntries) {
if (queueEntry.getStartedAt().before(startDateTime)) {
errors.rejectValue("startDatetime", "queue.entry.error.cannotStartBeforeVisitStartDate",
"This visit has queue entries whose dates cannot be before the start date");
break;
}
if (stopDateTime != null) {
if (queueEntry.getStartedAt().after(stopDateTime)) {
errors.rejectValue("stopDatetime", "queue.entry.error.cannotStartAfterVisitStopDate",
"This visit has queue entries which start after the stop date");
break;
}
if (queueEntry.getEndedAt() != null && queueEntry.getEndedAt().after(stopDateTime)) {
errors.rejectValue("stopDatetime", "queue.entry.error.cannotEndAfterVisitStopDate",
"This visit has queue entries which end after the stop date");
break;
}
}
}
}
}
}
3 changes: 3 additions & 0 deletions api/src/main/resources/messages.properties
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,6 @@
#

queue.entry.duplicate.patient=Patient already in the queue
queue.entry.error.cannotStartBeforeVisitStartDate=Queue entry cannot start before the visit start date
queue.entry.error.cannotStartAfterVisitStopDate=Queue entry cannot start after the visit stop date
queue.entry.error.cannotEndAfterVisitStopDate=Queue entry cannot end after the visit stop date
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public License,
* v. 2.0. If a copy of the MPL was not distributed with this file, You can
* obtain one at http://mozilla.org/MPL/2.0/. OpenMRS is also distributed under
* the terms of the Healthcare Disclaimer located at http://openmrs.org/license.
*
* Copyright (C) OpenMRS Inc. OpenMRS is a registered trademark and the OpenMRS
* graphic logo is a trademark of OpenMRS Inc.
*/
package org.openmrs.module.queue.validators;

import static org.hamcrest.Matchers.is;
import static org.junit.Assert.*;

import java.util.Arrays;
import java.util.List;

import org.apache.commons.lang.time.DateUtils;
import org.junit.Before;
import org.junit.Test;
import org.openmrs.Visit;
import org.openmrs.module.queue.SpringTestConfiguration;
import org.openmrs.module.queue.api.QueueEntryService;
import org.openmrs.module.queue.model.QueueEntry;
import org.openmrs.test.BaseModuleContextSensitiveTest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.validation.BindException;
import org.springframework.validation.Errors;
import org.springframework.validation.FieldError;

@ContextConfiguration(classes = SpringTestConfiguration.class, inheritLocations = false)
public class VisitWithQueueEntriesValidatorTest extends BaseModuleContextSensitiveTest {

private static final List<String> INITIAL_DATASET_XML = Arrays.asList(
"org/openmrs/module/queue/api/dao/QueueDaoTest_locationInitialDataset.xml",
"org/openmrs/module/queue/api/dao/QueueEntryDaoTest_conceptsInitialDataset.xml",
"org/openmrs/module/queue/api/dao/QueueEntryDaoTest_patientInitialDataset.xml",
"org/openmrs/module/queue/api/dao/VisitQueueEntryDaoTest_visitInitialDataset.xml",
"org/openmrs/module/queue/api/dao/QueueDaoTest_initialDataset.xml",
"org/openmrs/module/queue/api/dao/QueueEntryDaoTest_initialDataset.xml",
"org/openmrs/module/queue/validators/QueueEntryValidatorTest_globalPropertyInitialDataset.xml");

private Visit visit;

private QueueEntry queueEntry;

private Errors errors;

@Autowired
@Qualifier("queue.QueueEntryService")
private QueueEntryService queueEntryService;

@Autowired
private VisitWithQueueEntriesValidator validator;

@Before
public void setup() {
INITIAL_DATASET_XML.forEach(this::executeDataSet);
queueEntry = queueEntryService.getQueueEntryById(3).get();
visit = queueEntry.getVisit();
errors = new BindException(visit, visit.getClass().getName());
}

@Test
public void validatorNotNull() {
assertNotNull(validator);
}

@Test
public void shouldSupportVisit() {
assertTrue(validator.supports(Visit.class));
}

@Test
public void shouldNotRejectQueueEntryByDefault() {
validator.validate(visit, errors);
assertFalse(errors.hasErrors());
}

@Test
public void shouldNotRejectIfQueueEntryStartedAtEqualsVisitStartDate() {
visit.setStartDatetime(queueEntry.getStartedAt());
validator.validate(visit, errors);
assertFalse(errors.hasErrors());
}

@Test
public void shouldNotRejectIfQueueEntryStartedAtEqualsVisitEndDate() {
visit.setStopDatetime(queueEntry.getStartedAt());
validator.validate(visit, errors);
assertFalse(errors.hasErrors());
}

@Test
public void shouldNotRejectIfQueueEntryEndedAtAtEqualsVisitEndDate() {
queueEntry.setEndedAt(queueEntry.getStartedAt());
queueEntryService.saveQueueEntry(queueEntry);
visit.setStopDatetime(queueEntry.getEndedAt());
validator.validate(visit, errors);
assertFalse(errors.hasErrors());
}

@Test
public void shouldRejectIfQueueEntryStartedBeforeVisitStartDate() {
visit.setStartDatetime(DateUtils.addMilliseconds(queueEntry.getStartedAt(), 1));
validator.validate(visit, errors);
FieldError queueEntryStatusFieldError = errors.getFieldError("startDatetime");
assertNotNull(queueEntryStatusFieldError);
assertThat(queueEntryStatusFieldError.getCode(), is("queue.entry.error.cannotStartBeforeVisitStartDate"));
}

@Test
public void shouldRejectIfQueueEntryStartedAfterVisitEndDate() {
visit.setStopDatetime(DateUtils.addMilliseconds(queueEntry.getStartedAt(), -1));
validator.validate(visit, errors);
FieldError queueEntryStatusFieldError = errors.getFieldError("stopDatetime");
assertNotNull(queueEntryStatusFieldError);
assertThat(queueEntryStatusFieldError.getCode(), is("queue.entry.error.cannotStartAfterVisitStopDate"));
}

@Test
public void shouldRejectIfQueueEntryEndedAfterVisitEndDate() {
queueEntry.setEndedAt(DateUtils.addHours(queueEntry.getStartedAt(), 1));
queueEntryService.saveQueueEntry(queueEntry);
visit.setStopDatetime(DateUtils.addMilliseconds(queueEntry.getEndedAt(), -1));
validator.validate(visit, errors);
FieldError queueEntryStatusFieldError = errors.getFieldError("stopDatetime");
assertNotNull(queueEntryStatusFieldError);
assertThat(queueEntryStatusFieldError.getCode(), is("queue.entry.error.cannotEndAfterVisitStopDate"));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,6 @@
-->
<dataset>
<visit_type visit_type_id="1" name="Test visit type" description="This is a test description" creator="1" date_created="2022-02-08 05:20:00.0" retired="false" uuid="hj898a34-1ade-11e1-9c71-00248140a5eb"/>
<visit visit_id="101" patient_id="100" visit_type_id="1" date_started="2022-02-07 02:10:00.0" creator="1" date_created="2022-02-07 02:10:00.0" voided="0" uuid="j848b0c0-1ade-11e1-9c71-00248140a6eb" />
<visit visit_id="102" patient_id="100" visit_type_id="1" date_started="2022-03-07 15:10:00.0" creator="1" date_created="2022-03-07 15:10:00.0" voided="0" uuid="p858b0c0-1ade-11e1-9c71-00248140a6eb" />
<visit visit_id="101" patient_id="100" visit_type_id="1" date_started="2022-02-02 02:10:00.0" creator="1" date_created="2022-02-07 02:10:00.0" voided="0" uuid="j848b0c0-1ade-11e1-9c71-00248140a6eb" />
<visit visit_id="102" patient_id="100" visit_type_id="1" date_started="2022-02-03 15:10:00.0" creator="1" date_created="2022-03-07 15:10:00.0" voided="0" uuid="p858b0c0-1ade-11e1-9c71-00248140a6eb" />
</dataset>
Loading