From 89d6a6988bb1f1a290e1021b11f8cd5cbc0bbc77 Mon Sep 17 00:00:00 2001 From: Enrico Colasante Date: Mon, 26 Jan 2026 06:18:50 -0300 Subject: [PATCH] fix: Fix patch for nextScheduledDate in PS [DHIS2-20042] (#22806) --- .../hooks/ProgramStageObjectBundleHook.java | 3 +- .../ProgramStageControllerTest.java | 87 +++++++++++++++++++ 2 files changed, 89 insertions(+), 1 deletion(-) diff --git a/dhis-2/dhis-services/dhis-service-dxf2/src/main/java/org/hisp/dhis/dxf2/metadata/objectbundle/hooks/ProgramStageObjectBundleHook.java b/dhis-2/dhis-services/dhis-service-dxf2/src/main/java/org/hisp/dhis/dxf2/metadata/objectbundle/hooks/ProgramStageObjectBundleHook.java index 239035b4a004..aff1f7beaaf3 100644 --- a/dhis-2/dhis-services/dhis-service-dxf2/src/main/java/org/hisp/dhis/dxf2/metadata/objectbundle/hooks/ProgramStageObjectBundleHook.java +++ b/dhis-2/dhis-services/dhis-service-dxf2/src/main/java/org/hisp/dhis/dxf2/metadata/objectbundle/hooks/ProgramStageObjectBundleHook.java @@ -69,7 +69,8 @@ public void validate( DataElement.class, programStage.getNextScheduleDate().getUid()); - if (!programStage.getDataElements().contains(programStage.getNextScheduleDate()) + if (programStage.getDataElements().stream() + .noneMatch(de -> de.getUid().equals(programStage.getNextScheduleDate().getUid())) || nextScheduleDate == null || !nextScheduleDate.getValueType().equals(ValueType.DATE)) { addReports.accept( diff --git a/dhis-2/dhis-test-web-api/src/test/java/org/hisp/dhis/webapi/controller/ProgramStageControllerTest.java b/dhis-2/dhis-test-web-api/src/test/java/org/hisp/dhis/webapi/controller/ProgramStageControllerTest.java index 7e8d37c1be65..a80743306a67 100644 --- a/dhis-2/dhis-test-web-api/src/test/java/org/hisp/dhis/webapi/controller/ProgramStageControllerTest.java +++ b/dhis-2/dhis-test-web-api/src/test/java/org/hisp/dhis/webapi/controller/ProgramStageControllerTest.java @@ -32,6 +32,9 @@ import static org.hisp.dhis.http.HttpAssertions.assertStatus; import static org.junit.jupiter.api.Assertions.assertEquals; +import org.hisp.dhis.common.ValueType; +import org.hisp.dhis.dataelement.DataElement; +import org.hisp.dhis.dataelement.DataElementService; import org.hisp.dhis.feedback.ErrorCode; import org.hisp.dhis.http.HttpStatus; import org.hisp.dhis.jsontree.JsonObject; @@ -39,6 +42,7 @@ import org.hisp.dhis.test.webapi.json.domain.JsonTypeReport; import org.hisp.dhis.test.webapi.json.domain.JsonWebMessage; import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.transaction.annotation.Transactional; /** @@ -47,6 +51,8 @@ @Transactional class ProgramStageControllerTest extends H2ControllerIntegrationTestBase { + @Autowired private DataElementService dataElementService; + @Test void testCreateProgramStageWithoutProgram() { JsonWebMessage message = @@ -82,4 +88,85 @@ void testCreateProgramStageOk() { assertEquals("programstagelabel", programStage.getString("programStageLabel").string()); assertEquals("eventlabel", programStage.getString("eventLabel").string()); } + + @Test + void shouldSuccessfullyPatchNextScheduledDateField() { + DataElement dataElement = createDataElement('A'); + dataElement.setValueType(ValueType.DATE); + dataElementService.addDataElement(dataElement); + POST( + "/programs/", + """ + {'name':'test program', 'id':'VoZMWi7rBgj', 'shortName':'test program','programType':'WITH_REGISTRATION'}""") + .content(HttpStatus.CREATED); + String programStageId = + assertStatus( + HttpStatus.CREATED, + POST( + "/programStages/", + """ + {'name':'test programStage', 'program':{'id':'VoZMWi7rBgj'}, + 'programStageDataElements': [ + { + 'dataElement': { + 'id': '%s' + } + }]}""" + .formatted(dataElement.getUid()))); + JsonWebMessage message = + PATCH( + "/programStages/" + programStageId, + """ + [{"op": "replace", + "path": "/nextScheduleDate", + "value": {"id": "%s"}}] + """ + .formatted(dataElement.getUid())) + .content(HttpStatus.OK) + .as(JsonWebMessage.class); + assertEquals("OK", message.getStatus()); + JsonObject programStage = GET("/programStages/{id}", programStageId).content(); + assertEquals( + dataElement.getUid(), programStage.getObject("nextScheduleDate").getString("id").string()); + } + + @Test + void shouldFailToPatchNextScheduledDateWhenItIsReferencingADataElementWithWrongValueType() { + DataElement dataElement = createDataElement('A'); + dataElement.setValueType(ValueType.TEXT); + dataElementService.addDataElement(dataElement); + POST( + "/programs/", + """ + {'name':'test program', 'id':'VoZMWi7rBgj', 'shortName':'test program','programType':'WITH_REGISTRATION'}""") + .content(HttpStatus.CREATED); + String programStageId = + assertStatus( + HttpStatus.CREATED, + POST( + "/programStages/", + """ + {'name':'test programStage', 'program':{'id':'VoZMWi7rBgj'}, + 'programStageDataElements': [ + { + 'dataElement': { + 'id': '%s' + } + }]}""" + .formatted(dataElement.getUid()))); + JsonWebMessage message = + PATCH( + "/programStages/" + programStageId, + """ + [{"op": "replace", + "path": "/nextScheduleDate", + "value": {"id": "%s"}}] + """ + .formatted(dataElement.getUid())) + .content(HttpStatus.CONFLICT) + .as(JsonWebMessage.class); + JsonTypeReport response = message.get("response", JsonTypeReport.class); + assertEquals(1, response.getErrorReports().size()); + assertEquals(ErrorCode.E6001, response.getErrorReports().get(0).getErrorCode()); + } }