diff --git a/api/src/main/java/org/openmrs/module/emrapi/EmrApiConstants.java b/api/src/main/java/org/openmrs/module/emrapi/EmrApiConstants.java index b2a9f5a4..2d410db0 100644 --- a/api/src/main/java/org/openmrs/module/emrapi/EmrApiConstants.java +++ b/api/src/main/java/org/openmrs/module/emrapi/EmrApiConstants.java @@ -184,6 +184,8 @@ public class EmrApiConstants { public static final int DEFAULT_VISIT_EXPIRE_HOURS = 12; + public static final String GP_INPATIENT_VISIT_EXPIRE_HOURS = "emrapi.inpatientVisitExpireHours"; + /*public static final String CONCEPT_CODE_DISPOSITION = "Disposition"; public static final String CONCEPTDISPOSITION_ANSWER_ADMIT = "Admit"; diff --git a/api/src/main/java/org/openmrs/module/emrapi/EmrApiProperties.java b/api/src/main/java/org/openmrs/module/emrapi/EmrApiProperties.java index 2141a8cf..7d7c1e5f 100644 --- a/api/src/main/java/org/openmrs/module/emrapi/EmrApiProperties.java +++ b/api/src/main/java/org/openmrs/module/emrapi/EmrApiProperties.java @@ -145,6 +145,11 @@ public int getVisitExpireHours() { return NumberUtils.toInt(getGlobalProperty(EmrApiConstants.GP_VISIT_EXPIRE_HOURS, false), EmrApiConstants.DEFAULT_VISIT_EXPIRE_HOURS); } + public Integer getInpatientVisitExpireHours() { + String gpVal = getGlobalProperty(EmrApiConstants.GP_INPATIENT_VISIT_EXPIRE_HOURS, false); + return StringUtils.hasText(gpVal) ? NumberUtils.toInt(gpVal) : null; + } + public VisitType getAtFacilityVisitType() { return getEmrApiMetadataByCode(VisitType.class, EmrApiConstants.GP_AT_FACILITY_VISIT_TYPE); } diff --git a/api/src/main/java/org/openmrs/module/emrapi/adt/AdtServiceImpl.java b/api/src/main/java/org/openmrs/module/emrapi/adt/AdtServiceImpl.java index 1dc73e74..00e3ab2c 100644 --- a/api/src/main/java/org/openmrs/module/emrapi/adt/AdtServiceImpl.java +++ b/api/src/main/java/org/openmrs/module/emrapi/adt/AdtServiceImpl.java @@ -14,7 +14,7 @@ package org.openmrs.module.emrapi.adt; -import org.apache.commons.lang.time.DateUtils; +import org.apache.commons.lang.BooleanUtils; import org.joda.time.DateTime; import org.openmrs.Concept; import org.openmrs.Encounter; @@ -73,6 +73,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.TimeUnit; public class AdtServiceImpl extends BaseOpenmrsService implements AdtService { @@ -187,31 +188,41 @@ public boolean shouldBeClosed(Visit visit) { VisitDomainWrapper visitDomainWrapper = domainWrapperFactory.newVisitDomainWrapper(visit); - if (visitDomainWrapper.isAdmitted() || visitDomainWrapper.isAwaitingAdmission()) { - return false; // don't close the visit if patient is admitted or waiting admission - } + Date now = new Date(); + Date lastActivity = getLastActivityDate(visit); + long hoursInactive = TimeUnit.HOURS.convert(Math.abs(lastActivity.getTime() - now.getTime()), TimeUnit.MILLISECONDS); - Disposition mostRecentDisposition = visitDomainWrapper.getMostRecentDisposition(); - if (mostRecentDisposition != null && mostRecentDisposition.getKeepsVisitOpen() != null && mostRecentDisposition.getKeepsVisitOpen()) { - return false; // don't close the visit if the most recent disposition is one that keeps visit opens + boolean inpatient = (visitDomainWrapper.isAdmitted() || visitDomainWrapper.isAwaitingAdmission()); + if (!inpatient) { + Disposition mostRecentDisposition = visitDomainWrapper.getMostRecentDisposition(); + inpatient = mostRecentDisposition != null && BooleanUtils.isTrue(mostRecentDisposition.getKeepsVisitOpen()); } - Date now = new Date(); - Date mustHaveSomethingAfter = DateUtils.addHours(now, -emrApiProperties.getVisitExpireHours()); - - if (OpenmrsUtil.compare(visit.getStartDatetime(), mustHaveSomethingAfter) >= 0) { - return false; + if (inpatient) { + Integer inpatientVisitExpireHours = emrApiProperties.getInpatientVisitExpireHours(); + return (inpatientVisitExpireHours != null && inpatientVisitExpireHours <= hoursInactive); } + return emrApiProperties.getVisitExpireHours() <= hoursInactive; + } + /** + * Returns the last activity date for the given visit + * Currently this checks the Visit startDatetime, and the encounterDatetime for each Encounter in the Visit + * @param visit the Visit to check + * @return the number of minutes since the last activity + */ + protected Date getLastActivityDate(Visit visit) { + Date lastActivityDate = visit.getStartDatetime(); if (visit.getEncounters() != null) { - for (Encounter candidate : visit.getEncounters()) { - if (!candidate.isVoided() && OpenmrsUtil.compare(candidate.getEncounterDatetime(), mustHaveSomethingAfter) >= 0) { - return false; + for (Encounter e : visit.getEncounters()) { + if (BooleanUtils.isNotTrue(e.getVoided())) { + if (lastActivityDate.before(e.getEncounterDatetime())) { + lastActivityDate = e.getEncounterDatetime(); + } } } } - - return true; + return lastActivityDate; } @Override diff --git a/api/src/test/java/org/openmrs/module/emrapi/EmrApiPropertiesTest.java b/api/src/test/java/org/openmrs/module/emrapi/EmrApiPropertiesTest.java index edd067d5..ca012353 100644 --- a/api/src/test/java/org/openmrs/module/emrapi/EmrApiPropertiesTest.java +++ b/api/src/test/java/org/openmrs/module/emrapi/EmrApiPropertiesTest.java @@ -9,6 +9,7 @@ import org.openmrs.api.ConceptService; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; import static org.mockito.Matchers.anyString; import static org.mockito.Mockito.when; import static org.mockito.MockitoAnnotations.initMocks; @@ -48,6 +49,24 @@ public void visitExpireHours_shouldBeDefaultValueWhenNotConfiguredAsNonInteger() assertEquals(EmrApiConstants.DEFAULT_VISIT_EXPIRE_HOURS, emrApiProperties.getVisitExpireHours()); } + + @Test + public void inpatientVisitExpireHours_shouldBeConfiguredValueFromGlobalProperty(){ + when(administrationService.getGlobalProperty(EmrApiConstants.GP_INPATIENT_VISIT_EXPIRE_HOURS)).thenReturn("72"); + assertEquals(72, emrApiProperties.getInpatientVisitExpireHours().intValue()); + } + + @Test + public void inpatientVisitExpireHours_shouldBeNullWhenBlank(){ + when(administrationService.getGlobalProperty(EmrApiConstants.GP_INPATIENT_VISIT_EXPIRE_HOURS)).thenReturn(" "); + assertNull(emrApiProperties.getInpatientVisitExpireHours()); + } + + @Test + public void inpatientVisitExpireHours_shouldBeNullWhenNull(){ + when(administrationService.getGlobalProperty(EmrApiConstants.GP_INPATIENT_VISIT_EXPIRE_HOURS)).thenReturn(null); + assertNull(emrApiProperties.getInpatientVisitExpireHours()); + } @Test public void getConceptSourcesForDiagnosisSearch_shouldNotReturnNull(){ diff --git a/api/src/test/java/org/openmrs/module/emrapi/adt/AdtServiceComponentTest.java b/api/src/test/java/org/openmrs/module/emrapi/adt/AdtServiceComponentTest.java index deefb27f..e2abd639 100644 --- a/api/src/test/java/org/openmrs/module/emrapi/adt/AdtServiceComponentTest.java +++ b/api/src/test/java/org/openmrs/module/emrapi/adt/AdtServiceComponentTest.java @@ -32,6 +32,7 @@ import org.openmrs.Patient; import org.openmrs.Provider; import org.openmrs.Visit; +import org.openmrs.api.AdministrationService; import org.openmrs.api.ConceptService; import org.openmrs.api.EncounterService; import org.openmrs.api.LocationService; @@ -46,6 +47,7 @@ import org.openmrs.module.emrapi.test.ContextSensitiveMetadataTestUtils; import org.openmrs.module.emrapi.visit.VisitDomainWrapper; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; import java.util.ArrayList; import java.util.Collections; @@ -63,7 +65,11 @@ import static org.hamcrest.core.Is.is; import static org.hamcrest.core.IsNull.notNullValue; import static org.hamcrest.core.IsNull.nullValue; -import static org.junit.Assert.*; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; import static org.openmrs.module.emrapi.TestUtils.hasProviders; import static org.openmrs.module.emrapi.adt.AdtAction.Type.ADMISSION; import static org.openmrs.module.emrapi.adt.AdtAction.Type.DISCHARGE; @@ -104,9 +110,14 @@ public boolean evaluate(Object o) { @Autowired EmrConceptService emrConceptService; + @Autowired + @Qualifier("adminService") + AdministrationService administrationService; + @Before public void setUp() throws Exception { executeDataSet("baseTestDataset.xml"); + administrationService.setGlobalProperty(EmrApiConstants.GP_INPATIENT_VISIT_EXPIRE_HOURS, ""); dispositionService.setDispositionConfig("testDispositionConfig.json"); // use demo disposition config from test resources } @@ -465,6 +476,16 @@ public void test_shouldNotCloseVisitIfMostRecentDispositionKeepsVisitOpen() thro activeVisit = service.getActiveVisit(patient, location); assertNotNull(activeVisit); + + administrationService.setGlobalProperty(EmrApiConstants.GP_INPATIENT_VISIT_EXPIRE_HOURS, "15"); + service.closeInactiveVisits(); + activeVisit = service.getActiveVisit(patient, location); + assertNotNull(activeVisit); + + administrationService.setGlobalProperty(EmrApiConstants.GP_INPATIENT_VISIT_EXPIRE_HOURS, "14"); + service.closeInactiveVisits(); + activeVisit = service.getActiveVisit(patient, location); + assertNull(activeVisit); } @Test diff --git a/omod/src/test/java/org/openmrs/module/emrapi/web/controller/EmrApiConfigurationControllerTest.java b/omod/src/test/java/org/openmrs/module/emrapi/web/controller/EmrApiConfigurationControllerTest.java index f6a2eceb..cc758a85 100644 --- a/omod/src/test/java/org/openmrs/module/emrapi/web/controller/EmrApiConfigurationControllerTest.java +++ b/omod/src/test/java/org/openmrs/module/emrapi/web/controller/EmrApiConfigurationControllerTest.java @@ -67,7 +67,7 @@ public void shouldGetAsJson() throws Exception { @Test public void shouldGetDefaultRepresentation() { SimpleObject config = emrApiConfigurationController.getEmrApiConfiguration(request, response); - assertEquals(50, config.keySet().size()); + assertEquals(51, config.keySet().size()); assertEquals("org.openmrs.module.emrapi", config.get("metadataSourceName")); assertEquals("50", config.get("lastViewedPatientSizeLimit").toString()); Map unknownLocation = mapNode(config, "unknownLocation");