From bf20e26b700762a60b14fe64792c187777f6629b Mon Sep 17 00:00:00 2001 From: Enrico Date: Thu, 17 Oct 2024 10:16:14 +0200 Subject: [PATCH] fix: Map completed data in preheat for enrollment [DHIS2-18225] --- .../mappers/ProgramInstanceMapper.java | 2 + .../tracker/bundle/EnrollmentImportTest.java | 147 ++++++++++++++++ .../dhis/tracker/bundle/EventImportTest.java | 158 ++++++++++++++++++ .../tracker/te_enrollment_event.json | 109 ++++++++++++ 4 files changed, 416 insertions(+) create mode 100644 dhis-2/dhis-test-integration/src/test/java/org/hisp/dhis/tracker/bundle/EnrollmentImportTest.java create mode 100644 dhis-2/dhis-test-integration/src/test/java/org/hisp/dhis/tracker/bundle/EventImportTest.java create mode 100644 dhis-2/dhis-test-integration/src/test/resources/tracker/te_enrollment_event.json diff --git a/dhis-2/dhis-services/dhis-service-tracker/src/main/java/org/hisp/dhis/tracker/preheat/mappers/ProgramInstanceMapper.java b/dhis-2/dhis-services/dhis-service-tracker/src/main/java/org/hisp/dhis/tracker/preheat/mappers/ProgramInstanceMapper.java index 50f6a3017ab3..f7477a3d98a6 100644 --- a/dhis-2/dhis-services/dhis-service-tracker/src/main/java/org/hisp/dhis/tracker/preheat/mappers/ProgramInstanceMapper.java +++ b/dhis-2/dhis-services/dhis-service-tracker/src/main/java/org/hisp/dhis/tracker/preheat/mappers/ProgramInstanceMapper.java @@ -59,6 +59,8 @@ public interface ProgramInstanceMapper extends PreheatMapper { @Mapping(target = "externalAccess") @Mapping(target = "userGroupAccesses", qualifiedByName = "userGroupAccessesPi") @Mapping(target = "userAccesses", qualifiedByName = "userAccessesPi") + @Mapping(target = "completedBy") + @Mapping(target = "endDate") @Mapping(target = "program", qualifiedByName = "program") @Mapping(target = "entityInstance") @Mapping(target = "organisationUnit") diff --git a/dhis-2/dhis-test-integration/src/test/java/org/hisp/dhis/tracker/bundle/EnrollmentImportTest.java b/dhis-2/dhis-test-integration/src/test/java/org/hisp/dhis/tracker/bundle/EnrollmentImportTest.java new file mode 100644 index 000000000000..d978054bc16c --- /dev/null +++ b/dhis-2/dhis-test-integration/src/test/java/org/hisp/dhis/tracker/bundle/EnrollmentImportTest.java @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2004-2022, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.hisp.dhis.tracker.bundle; + +import static org.hisp.dhis.tracker.Assertions.assertNoErrors; +import static org.hisp.dhis.tracker.domain.EnrollmentStatus.ACTIVE; +import static org.hisp.dhis.tracker.domain.EnrollmentStatus.CANCELLED; +import static org.hisp.dhis.tracker.domain.EnrollmentStatus.COMPLETED; +import static org.junit.jupiter.api.Assertions.assertAll; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; + +import java.io.IOException; +import java.util.stream.Stream; +import org.hisp.dhis.program.ProgramInstance; +import org.hisp.dhis.tracker.TrackerImportParams; +import org.hisp.dhis.tracker.TrackerImportService; +import org.hisp.dhis.tracker.TrackerImportStrategy; +import org.hisp.dhis.tracker.TrackerTest; +import org.hisp.dhis.tracker.domain.EnrollmentStatus; +import org.hisp.dhis.tracker.report.TrackerImportReport; +import org.hisp.dhis.user.User; +import org.hisp.dhis.user.UserService; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.springframework.beans.factory.annotation.Autowired; + +class EnrollmentImportTest extends TrackerTest { + @Autowired private TrackerImportService trackerImportService; + + private User importUser; + + @Autowired protected UserService _userService; + + protected void initTest() throws IOException { + userService = _userService; + setUpMetadata("tracker/simple_metadata.json"); + + importUser = userService.getUser("M5zQapPyTZI"); + injectSecurityContext(importUser); + } + + @ParameterizedTest + @MethodSource("statuses") + void shouldCorrectlyPopulateCompletedDataWhenCreatingAnEnrollment(EnrollmentStatus status) + throws IOException { + TrackerImportParams params = fromJson("tracker/te_enrollment_event.json"); + params.setImportStrategy(TrackerImportStrategy.CREATE_AND_UPDATE); + params.getEnrollments().get(0).setStatus(status); + + TrackerImportReport importReport = trackerImportService.importTracker(params); + + assertNoErrors(importReport); + + ProgramInstance enrollment = + manager.get(ProgramInstance.class, params.getEnrollments().get(0).getUid()); + + assertEnrollmentCompletedData(enrollment); + } + + @ParameterizedTest + @MethodSource("transitionStatuses") + void shouldCorrectlyPopulateCompletedDataWhenUpdatingAnEnrollment( + EnrollmentStatus savedStatus, EnrollmentStatus updatedStatus) throws IOException { + TrackerImportParams params = fromJson("tracker/te_enrollment_event.json"); + params.setImportStrategy(TrackerImportStrategy.CREATE_AND_UPDATE); + params.getEnrollments().get(0).setStatus(savedStatus); + + TrackerImportReport importReport = trackerImportService.importTracker(params); + + assertNoErrors(importReport); + + params.getEnrollments().get(0).setStatus(updatedStatus); + importReport = trackerImportService.importTracker(params); + + assertNoErrors(importReport); + + ProgramInstance enrollment = + manager.get(ProgramInstance.class, params.getEnrollments().get(0).getUid()); + + assertEnrollmentCompletedData(enrollment); + } + + public Stream statuses() { + return Stream.of(Arguments.of(ACTIVE), Arguments.of(CANCELLED), Arguments.of(COMPLETED)); + } + + public Stream transitionStatuses() { + return Stream.of( + Arguments.of(ACTIVE, COMPLETED), + Arguments.of(ACTIVE, CANCELLED), + Arguments.of(ACTIVE, COMPLETED), + Arguments.of(CANCELLED, COMPLETED), + Arguments.of(CANCELLED, CANCELLED), + Arguments.of(CANCELLED, COMPLETED), + Arguments.of(COMPLETED, COMPLETED), + Arguments.of(COMPLETED, CANCELLED), + Arguments.of(COMPLETED, COMPLETED)); + } + + private void assertEnrollmentCompletedData(ProgramInstance enrollment) { + switch (enrollment.getStatus()) { + case ACTIVE: + assertAll( + () -> assertNull(enrollment.getCompletedBy()), + () -> assertNull(enrollment.getEndDate())); + break; + case COMPLETED: + assertAll( + () -> assertEquals(importUser.getUsername(), enrollment.getCompletedBy()), + () -> assertNotNull(enrollment.getEndDate())); + break; + case CANCELLED: + assertAll( + () -> assertNull(enrollment.getCompletedBy()), + () -> assertNotNull(enrollment.getEndDate())); + break; + } + } +} diff --git a/dhis-2/dhis-test-integration/src/test/java/org/hisp/dhis/tracker/bundle/EventImportTest.java b/dhis-2/dhis-test-integration/src/test/java/org/hisp/dhis/tracker/bundle/EventImportTest.java new file mode 100644 index 000000000000..97080b03d65c --- /dev/null +++ b/dhis-2/dhis-test-integration/src/test/java/org/hisp/dhis/tracker/bundle/EventImportTest.java @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2004-2022, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.hisp.dhis.tracker.bundle; + +import static org.hisp.dhis.tracker.Assertions.assertNoErrors; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; + +import java.io.IOException; +import java.util.stream.Stream; +import org.hisp.dhis.event.EventStatus; +import org.hisp.dhis.program.ProgramStageInstance; +import org.hisp.dhis.tracker.TrackerImportParams; +import org.hisp.dhis.tracker.TrackerImportService; +import org.hisp.dhis.tracker.TrackerImportStrategy; +import org.hisp.dhis.tracker.TrackerTest; +import org.hisp.dhis.tracker.report.TrackerImportReport; +import org.hisp.dhis.user.User; +import org.hisp.dhis.user.UserService; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.springframework.beans.factory.annotation.Autowired; + +class EventImportTest extends TrackerTest { + @Autowired private TrackerImportService trackerImportService; + + private User importUser; + + @Autowired protected UserService _userService; + + public void initTest() throws IOException { + userService = _userService; + setUpMetadata("tracker/simple_metadata.json"); + + importUser = userService.getUser("M5zQapPyTZI"); + injectSecurityContext(importUser); + } + + @Test + void shouldPopulateCompletedDataWhenCreatingAnEventWithStatusCompleted() throws IOException { + TrackerImportParams params = fromJson("tracker/te_enrollment_event.json"); + params.setImportStrategy(TrackerImportStrategy.CREATE_AND_UPDATE); + params.getEvents().get(0).setStatus(EventStatus.COMPLETED); + + TrackerImportReport importReport = trackerImportService.importTracker(params); + + assertNoErrors(importReport); + + ProgramStageInstance event = + manager.get(ProgramStageInstance.class, params.getEvents().get(0).getUid()); + + assertEquals(importUser.getUsername(), event.getCompletedBy()); + assertNotNull(event.getCompletedDate()); + } + + @ParameterizedTest + @MethodSource("notCompletedStatus") + void shouldNotPopulateCompletedDataWhenCreatingAnEventWithNotCompletedStatus(EventStatus status) + throws IOException { + TrackerImportParams params = fromJson("tracker/te_enrollment_event.json"); + params.setImportStrategy(TrackerImportStrategy.CREATE_AND_UPDATE); + params.getEvents().get(0).setStatus(status); + + TrackerImportReport importReport = trackerImportService.importTracker(params); + + assertNoErrors(importReport); + + ProgramStageInstance event = + manager.get(ProgramStageInstance.class, params.getEvents().get(0).getUid()); + + assertNull(event.getCompletedBy()); + assertNull(event.getCompletedDate()); + } + + @Test + void shouldDeleteCompletedDataWhenUpdatingAnEventWithStatusActive() throws IOException { + TrackerImportParams params = fromJson("tracker/te_enrollment_event.json"); + params.setImportStrategy(TrackerImportStrategy.CREATE_AND_UPDATE); + params.getEvents().get(0).setStatus(EventStatus.COMPLETED); + + TrackerImportReport importReport = trackerImportService.importTracker(params); + + assertNoErrors(importReport); + + params.getEvents().get(0).setStatus(EventStatus.ACTIVE); + + importReport = trackerImportService.importTracker(params); + + assertNoErrors(importReport); + + ProgramStageInstance event = + manager.get(ProgramStageInstance.class, params.getEvents().get(0).getUid()); + + assertNull(event.getCompletedBy()); + assertNull(event.getCompletedDate()); + } + + @ParameterizedTest + @MethodSource("notCompletedStatus") + void shouldPopulateCompletedDataWhenUpdatingAnEventWithStatusCompleted(EventStatus status) + throws IOException { + TrackerImportParams params = fromJson("tracker/te_enrollment_event.json"); + params.setImportStrategy(TrackerImportStrategy.CREATE_AND_UPDATE); + params.getEvents().get(0).setStatus(status); + + TrackerImportReport importReport = trackerImportService.importTracker(params); + + assertNoErrors(importReport); + + params.getEvents().get(0).setStatus(EventStatus.COMPLETED); + + importReport = trackerImportService.importTracker(params); + + assertNoErrors(importReport); + + ProgramStageInstance event = + manager.get(ProgramStageInstance.class, params.getEvents().get(0).getUid()); + + assertEquals(importUser.getUsername(), event.getCompletedBy()); + assertNotNull(event.getCompletedDate()); + } + + public Stream notCompletedStatus() { + return Stream.of( + Arguments.of(EventStatus.ACTIVE), + Arguments.of(EventStatus.SCHEDULE), + Arguments.of(EventStatus.OVERDUE), + Arguments.of(EventStatus.SKIPPED)); + } +} diff --git a/dhis-2/dhis-test-integration/src/test/resources/tracker/te_enrollment_event.json b/dhis-2/dhis-test-integration/src/test/resources/tracker/te_enrollment_event.json new file mode 100644 index 000000000000..5c7cab09ab40 --- /dev/null +++ b/dhis-2/dhis-test-integration/src/test/resources/tracker/te_enrollment_event.json @@ -0,0 +1,109 @@ +{ + "importMode": "COMMIT", + "idSchemes": { + "dataElementIdScheme": { + "idScheme": "UID" + }, + "orgUnitIdScheme": { + "idScheme": "UID" + }, + "programIdScheme": { + "idScheme": "UID" + }, + "programStageIdScheme": { + "idScheme": "UID" + }, + "idScheme": { + "idScheme": "UID" + }, + "categoryOptionComboIdScheme": { + "idScheme": "UID" + }, + "categoryOptionIdScheme": { + "idScheme": "UID" + } + }, + "importStrategy": "CREATE", + "atomicMode": "ALL", + "flushMode": "AUTO", + "validationMode": "FULL", + "skipPatternValidation": false, + "skipSideEffects": false, + "skipRuleEngine": false, + "trackedEntities": [ + { + "trackedEntity": "IOR1AXXl24H", + "trackedEntityType": { + "idScheme": "UID", + "identifier": "ja8NY4PW7Xm" + }, + "orgUnit": { + "idScheme": "UID", + "identifier": "h4w96yEMlzO" + }, + "inactive": false, + "deleted": false, + "potentialDuplicate": false, + "relationships": [], + "attributes": [], + "enrollments": [] + } + ], + "enrollments": [ + { + "enrollment": "TvctPPhpD8u", + "createdAtClient": "2017-01-26T13:48:13.363", + "trackedEntity": "IOR1AXXl24H", + "program": { + "idScheme": "UID", + "identifier": "BFcipDERJnf" + }, + "status": "ACTIVE", + "orgUnit": { + "idScheme": "UID", + "identifier": "h4w96yEMlzO" + }, + "orgUnitName": "Mbokie CHP", + "enrolledAt": "2019-03-28T12:05:00.000", + "occurredAt": "2019-03-28T12:05:00.000", + "followUp": false, + "deleted": false, + "events": [], + "relationships": [], + "attributes": [], + "notes": [] + } + ], + "events": [ + { + "event": "D9PbzJY8bJO", + "program": { + "idScheme": "UID", + "identifier": "BFcipDERJnf" + }, + "programStage": { + "idScheme": "UID", + "identifier": "NpsdDv6kKSO" + }, + "enrollment": "TvctPPhpD8u", + "orgUnit": { + "idScheme": "UID", + "identifier": "h4w96yEMlzO" + }, + "relationships": [], + "occurredAt": "2019-01-28T00:00:00.000", + "scheduledAt": "2019-01-28T12:10:38.100", + "storedBy": "admin", + "deleted": false, + "attributeOptionCombo": { + "idScheme": "UID" + }, + "attributeCategoryOptions": [], + + "dataValues": [], + "notes": [] + } + ], + "relationships": [], + "username": "system-process" +} \ No newline at end of file