This is an automated email from the ASF dual-hosted git repository.

victorromero pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/fineract.git


The following commit(s) were added to refs/heads/develop by this push:
     new 8a795e83c7 FINERACT-2490: Add status field to StandingInstructionData 
serialization (#5493)
8a795e83c7 is described below

commit 8a795e83c79cb78516cd62247b9df6e2d40d84ee
Author: Ralph Hopman <[email protected]>
AuthorDate: Sat Feb 14 20:05:54 2026 +0100

    FINERACT-2490: Add status field to StandingInstructionData serialization 
(#5493)
---
 .../account/data/StandingInstructionData.java      |   1 +
 .../StandingInstructionDataSerializationTest.java  | 185 +++++++++++++++++++++
 2 files changed, 186 insertions(+)

diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/data/StandingInstructionData.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/data/StandingInstructionData.java
index 15229bcb09..09b61c6b96 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/data/StandingInstructionData.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/data/StandingInstructionData.java
@@ -63,6 +63,7 @@ public final class StandingInstructionData {
     private final EnumOptionData transferType;
     private final EnumOptionData priority;
     private final EnumOptionData instructionType;
+    @Getter
     private final EnumOptionData status;
     @Getter
     private final BigDecimal amount;
diff --git 
a/fineract-provider/src/test/java/org/apache/fineract/portfolio/account/data/StandingInstructionDataSerializationTest.java
 
b/fineract-provider/src/test/java/org/apache/fineract/portfolio/account/data/StandingInstructionDataSerializationTest.java
new file mode 100644
index 0000000000..4d3cf14e9e
--- /dev/null
+++ 
b/fineract-provider/src/test/java/org/apache/fineract/portfolio/account/data/StandingInstructionDataSerializationTest.java
@@ -0,0 +1,185 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.account.data;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import java.math.BigDecimal;
+import java.time.LocalDate;
+import org.apache.fineract.infrastructure.core.data.EnumOptionData;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Test class to verify JSON serialization of StandingInstructionData, 
particularly focusing on the status field
+ * visibility in API responses.
+ */
+public class StandingInstructionDataSerializationTest {
+
+    private ObjectMapper objectMapper;
+
+    @BeforeEach
+    public void setUp() {
+        objectMapper = new ObjectMapper();
+        objectMapper.findAndRegisterModules(); // Register JavaTimeModule for 
LocalDate support
+    }
+
+    /**
+     * This test verifies that the status field is properly serialized to JSON.
+     *
+     * BACKGROUND: The backend soft-delete implementation marks standing 
instructions as deleted (status=3). The
+     * database query correctly selects the status field, and the 
StandingInstructionMapper properly populates it.
+     * However, without the @Getter annotation on the private status field, 
Jackson cannot serialize it to JSON, making
+     * it impossible for frontend applications to: - Filter out deleted 
standing instructions (status=3) - Distinguish
+     * between ACTIVE (1), DISABLED (2), and DELETED (3) states
+     *
+     * EXPECTED BEHAVIOR: The status field should be included in JSON 
responses from GET /standinginstructions
+     */
+    @Test
+    public void testStatusFieldSerialization() throws Exception {
+        // Create a status enum representing DELETED state (status=3)
+        EnumOptionData deletedStatus = new EnumOptionData(3L, "DELETED", 
"Deleted");
+
+        // Create a StandingInstructionData instance with required fields
+        StandingInstructionData standingInstruction = 
StandingInstructionData.instance(1L, // id
+                100L, // accountDetailId
+                "Test Standing Instruction", // name
+                null, // fromOffice
+                null, // toOffice
+                null, // fromClient
+                null, // toClient
+                null, // fromAccountType
+                null, // fromAccount
+                null, // toAccountType
+                null, // toAccount
+                null, // transferType
+                null, // priority
+                null, // instructionType
+                deletedStatus, // status (THIS IS THE CRITICAL FIELD)
+                BigDecimal.valueOf(1000), // amount
+                LocalDate.of(2026, 1, 1), // validFrom
+                LocalDate.of(2026, 12, 31), // validTill
+                null, // recurrenceType
+                null, // recurrenceFrequency
+                null, // recurrenceInterval
+                null // recurrenceOnMonthDay
+        );
+
+        // Serialize to JSON
+        String json = objectMapper.writeValueAsString(standingInstruction);
+
+        // Parse the JSON to verify field presence
+        JsonNode jsonNode = objectMapper.readTree(json);
+
+        // Verify basic fields are present
+        assertNotNull(jsonNode.get("id"), "id field should be present in 
JSON");
+        assertEquals(1L, jsonNode.get("id").asLong());
+
+        assertNotNull(jsonNode.get("name"), "name field should be present in 
JSON");
+        assertEquals("Test Standing Instruction", 
jsonNode.get("name").asText());
+
+        assertNotNull(jsonNode.get("amount"), "amount field should be present 
in JSON");
+        assertEquals(1000, jsonNode.get("amount").asInt());
+
+        assertNotNull(jsonNode.get("status"),
+                "status field MUST be present in JSON response for frontend to 
filter deleted standing instructions");
+
+        // Verify the status field contains the correct data
+        JsonNode statusNode = jsonNode.get("status");
+        assertNotNull(statusNode.get("id"), "status.id should be present");
+        assertEquals(3L, statusNode.get("id").asLong(), "status.id should be 3 
for DELETED state");
+
+        assertNotNull(statusNode.get("code"), "status.code should be present");
+        assertEquals("DELETED", statusNode.get("code").asText());
+
+        assertNotNull(statusNode.get("value"), "status.value should be 
present");
+        assertEquals("Deleted", statusNode.get("value").asText());
+    }
+
+    /**
+     * Verify that the status field is properly serialized for different 
status values (ACTIVE, DISABLED, DELETED).
+     */
+    @Test
+    public void testStatusFieldSerializationForDifferentStates() throws 
Exception {
+        // Test ACTIVE state (status=1)
+        EnumOptionData activeStatus = new EnumOptionData(1L, "ACTIVE", 
"Active");
+        StandingInstructionData activeInstruction = 
createStandingInstructionWithStatus(activeStatus);
+
+        String activeJson = objectMapper.writeValueAsString(activeInstruction);
+        JsonNode activeNode = objectMapper.readTree(activeJson);
+
+        assertNotNull(activeNode.get("status"), "status field should be 
present for ACTIVE state");
+        assertEquals(1L, activeNode.get("status").get("id").asLong());
+        assertEquals("ACTIVE", activeNode.get("status").get("code").asText());
+
+        // Test DISABLED state (status=2)
+        EnumOptionData disabledStatus = new EnumOptionData(2L, "DISABLED", 
"Disabled");
+        StandingInstructionData disabledInstruction = 
createStandingInstructionWithStatus(disabledStatus);
+
+        String disabledJson = 
objectMapper.writeValueAsString(disabledInstruction);
+        JsonNode disabledNode = objectMapper.readTree(disabledJson);
+
+        assertNotNull(disabledNode.get("status"), "status field should be 
present for DISABLED state");
+        assertEquals(2L, disabledNode.get("status").get("id").asLong());
+        assertEquals("DISABLED", 
disabledNode.get("status").get("code").asText());
+
+        // Test DELETED state (status=3)
+        EnumOptionData deletedStatus = new EnumOptionData(3L, "DELETED", 
"Deleted");
+        StandingInstructionData deletedInstruction = 
createStandingInstructionWithStatus(deletedStatus);
+
+        String deletedJson = 
objectMapper.writeValueAsString(deletedInstruction);
+        JsonNode deletedNode = objectMapper.readTree(deletedJson);
+
+        assertNotNull(deletedNode.get("status"), "status field should be 
present for DELETED state");
+        assertEquals(3L, deletedNode.get("status").get("id").asLong());
+        assertEquals("DELETED", 
deletedNode.get("status").get("code").asText());
+    }
+
+    /**
+     * Helper method to create a StandingInstructionData with a specific 
status.
+     */
+    private StandingInstructionData 
createStandingInstructionWithStatus(EnumOptionData status) {
+        return StandingInstructionData.instance(1L, // id
+                100L, // accountDetailId
+                "Test Standing Instruction", // name
+                null, // fromOffice
+                null, // toOffice
+                null, // fromClient
+                null, // toClient
+                null, // fromAccountType
+                null, // fromAccount
+                null, // toAccountType
+                null, // toAccount
+                null, // transferType
+                null, // priority
+                null, // instructionType
+                status, // status
+                BigDecimal.valueOf(1000), // amount
+                LocalDate.of(2026, 1, 1), // validFrom
+                LocalDate.of(2026, 12, 31), // validTill
+                null, // recurrenceType
+                null, // recurrenceFrequency
+                null, // recurrenceInterval
+                null // recurrenceOnMonthDay
+        );
+    }
+}

Reply via email to