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

adamsaghy 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 c9a41794e8 FINERACT-2457: Share transaction chronological validation 
incorrectly blocks new transactions based on rejected/reversed transactions
c9a41794e8 is described below

commit c9a41794e80c9f1474169df4042104b0a93028fc
Author: Wilfred Kigenyi <[email protected]>
AuthorDate: Tue Jan 27 23:45:43 2026 +0300

    FINERACT-2457: Share transaction chronological validation incorrectly 
blocks new transactions based on rejected/reversed transactions
---
 .../serialization/ShareAccountDataSerializer.java  |  54 ++-
 .../shares/ShareAccountIntegrationTests.java       | 415 +++++++++++++++++++++
 2 files changed, 439 insertions(+), 30 deletions(-)

diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/serialization/ShareAccountDataSerializer.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/serialization/ShareAccountDataSerializer.java
index dc648318db..bb9477fd01 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/serialization/ShareAccountDataSerializer.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/serialization/ShareAccountDataSerializer.java
@@ -29,6 +29,7 @@ import java.time.LocalDateTime;
 import java.time.format.DateTimeFormatter;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
@@ -706,16 +707,8 @@ public class ShareAccountDataSerializer {
             }
         }
         boolean isTransactionBeforeExistingTransactions = false;
-        Set<ShareAccountTransaction> transactions = 
account.getShareAccountTransactions();
-        for (ShareAccountTransaction transaction : transactions) {
-            if (!transaction.isChargeTransaction()) {
-                LocalDate transactionDate = transaction.getPurchasedDate();
-                if (DateUtils.isBefore(requestedDate, transactionDate)) {
-                    isTransactionBeforeExistingTransactions = true;
-                    break;
-                }
-            }
-        }
+        isTransactionBeforeExistingTransactions = 
isTransactionBeforeExistingTransactions(requestedDate,
+                isTransactionBeforeExistingTransactions, account);
         if (isTransactionBeforeExistingTransactions) {
             
baseDataValidator.reset().parameter(ShareAccountApiConstants.requesteddate_paramname).value(requestedDate)
                     
.failWithCodeNoParameterAddedToErrorCode("purchase.transaction.date.cannot.be.before.existing.transactions");
@@ -732,6 +725,23 @@ public class ShareAccountDataSerializer {
         return actualChanges;
     }
 
+    private boolean isTransactionBeforeExistingTransactions(LocalDate 
requestedDate, boolean isTransactionBeforeExistingTransactions,
+            ShareAccount shareAccount) {
+        Collection<ShareAccountTransaction> activeTransactions = 
shareAccount.getShareAccountTransactions().stream()
+                .filter(tr -> tr.isActive() && !tr.isChargeTransaction() && 
!tr.isPurchaseRejectedTransaction()).toList();
+
+        for (ShareAccountTransaction transaction : activeTransactions) {
+            if (!transaction.isChargeTransaction() && transaction.isActive() 
&& !transaction.isPurchaseRejectedTransaction()) {
+                LocalDate transactionDate = transaction.getPurchasedDate();
+                if (DateUtils.isBefore(requestedDate, transactionDate)) {
+                    isTransactionBeforeExistingTransactions = true;
+                    break;
+                }
+            }
+        }
+        return isTransactionBeforeExistingTransactions;
+    }
+
     private void handleAdditionalSharesChargeTransactions(final ShareAccount 
account, final ShareAccountTransaction purchaseTransaction) {
         Set<ShareAccountCharge> charges = account.getCharges();
         BigDecimal totalChargeAmount = BigDecimal.ZERO;
@@ -863,16 +873,8 @@ public class ShareAccountDataSerializer {
         
baseDataValidator.reset().parameter(ShareAccountApiConstants.requestedshares_paramname).value(sharesRequested).notNull()
                 .longGreaterThanZero();
         boolean isTransactionBeforeExistingTransactions = false;
-        Set<ShareAccountTransaction> transactions = 
account.getShareAccountTransactions();
-        for (ShareAccountTransaction transaction : transactions) {
-            if (!transaction.isChargeTransaction() && transaction.isActive()) {
-                LocalDate transactionDate = transaction.getPurchasedDate();
-                if (DateUtils.isBefore(requestedDate, transactionDate)) {
-                    isTransactionBeforeExistingTransactions = true;
-                    break;
-                }
-            }
-        }
+        isTransactionBeforeExistingTransactions = 
isTransactionBeforeExistingTransactions(requestedDate,
+                isTransactionBeforeExistingTransactions, account);
         if (isTransactionBeforeExistingTransactions) {
             
baseDataValidator.reset().parameter(ShareAccountApiConstants.requesteddate_paramname).value(requestedDate)
                     
.failWithCodeNoParameterAddedToErrorCode("redeem.transaction.date.cannot.be.before.existing.transactions");
@@ -1018,16 +1020,8 @@ public class ShareAccountDataSerializer {
             throw new PlatformApiDataValidationException(dataValidationErrors);
         }
         boolean isTransactionBeforeExistingTransactions = false;
-        Set<ShareAccountTransaction> transactions = 
account.getShareAccountTransactions();
-        for (ShareAccountTransaction transaction : transactions) {
-            if (!transaction.isChargeTransaction()) {
-                LocalDate transactionDate = transaction.getPurchasedDate();
-                if (DateUtils.isBefore(closedDate, transactionDate)) {
-                    isTransactionBeforeExistingTransactions = true;
-                    break;
-                }
-            }
-        }
+        isTransactionBeforeExistingTransactions = 
isTransactionBeforeExistingTransactions(closedDate,
+                isTransactionBeforeExistingTransactions, account);
         if (isTransactionBeforeExistingTransactions) {
             
baseDataValidator.reset().parameter(ShareAccountApiConstants.closeddate_paramname).value(closedDate)
                     
.failWithCodeNoParameterAddedToErrorCode("share.account.cannot.be.closed.before.existing.transactions");
diff --git 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/shares/ShareAccountIntegrationTests.java
 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/shares/ShareAccountIntegrationTests.java
index 7826136e36..fbc2331c3f 100644
--- 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/shares/ShareAccountIntegrationTests.java
+++ 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/shares/ShareAccountIntegrationTests.java
@@ -983,6 +983,285 @@ public class ShareAccountIntegrationTests {
         Assertions.assertEquals("0", 
String.valueOf(summaryMap.get("totalPendingForApprovalShares")));
     }
 
+    // Refactored Test 1
+    @Test
+    @SuppressWarnings("unchecked")
+    public void testChronologicalAdditionalSharesAfterRejectedTransaction() {
+        shareProductHelper = new ShareProductHelper();
+        final Integer productId = createShareProduct();
+        Assertions.assertNotNull(productId);
+        final Integer clientId = ClientHelper.createClient(this.requestSpec, 
this.responseSpec);
+        Assertions.assertNotNull(clientId);
+        Integer savingsAccountId = 
SavingsAccountHelper.openSavingsAccount(requestSpec, responseSpec, clientId, 
"1000");
+        Assertions.assertNotNull(savingsAccountId);
+
+        // Setup and activate share account with initial shares on 01 March 
2016
+        final Integer shareAccountId = setupAndActivateShareAccount(clientId, 
productId, savingsAccountId, "01 March 2016");
+
+        // Apply additional shares on 15 April 2016
+        applyAdditionalShares(shareAccountId, "15 April 2016", "20");
+
+        // Retrieve transactions and find the additional shares request
+        Map<String, Object> shareAccountData = 
ShareAccountTransactionHelper.retrieveShareAccount(shareAccountId, requestSpec,
+                responseSpec);
+        List<Map<String, Object>> transactions = (List<Map<String, Object>>) 
shareAccountData.get("purchasedShares");
+        Assertions.assertNotNull(transactions);
+
+        // Find and reject the additional shares request (15 April 2016)
+        String additionalSharesRequestId = findTransactionId(transactions, 
"purchasedSharesType.purchased", "15 April 2016");
+        Assertions.assertNotNull(additionalSharesRequestId, "Additional shares 
request for 15 April 2016 should exist");
+
+        // Reject the additional shares request
+        rejectAdditionalSharesRequest(shareAccountId, 
additionalSharesRequestId);
+
+        // Verify transaction is rejected
+        shareAccountData = 
ShareAccountTransactionHelper.retrieveShareAccount(shareAccountId, requestSpec, 
responseSpec);
+        transactions = (List<Map<String, Object>>) 
shareAccountData.get("purchasedShares");
+        verifyTransactionStatus(transactions, "purchasedSharesType.purchased", 
"15 April 2016", "purchasedSharesStatusType.rejected");
+
+        // Now try to apply additional shares with a date BEFORE the rejected 
transaction (10 April 2016)
+        // This should succeed because rejected transactions should be ignored 
in chronological validation
+        applyAdditionalShares(shareAccountId, "10 April 2016", "15");
+
+        // Verify the new transaction was successfully added
+        shareAccountData = 
ShareAccountTransactionHelper.retrieveShareAccount(shareAccountId, requestSpec, 
responseSpec);
+        transactions = (List<Map<String, Object>>) 
shareAccountData.get("purchasedShares");
+        verifyTransactionWithShares(transactions, 
"purchasedSharesType.purchased", "10 April 2016", "15",
+                "purchasedSharesStatusType.applied");
+    }
+
+    @Test
+    @SuppressWarnings("unchecked")
+    public void testChronologicalAccountClosureBeforeRejectedTransaction() {
+        // FINERACT-2457: Account closure validation should ignore 
rejected/reversed transactions
+        shareProductHelper = new ShareProductHelper();
+        final Integer productId = createShareProduct();
+        Assertions.assertNotNull(productId);
+        final Integer clientId = ClientHelper.createClient(this.requestSpec, 
this.responseSpec);
+        Assertions.assertNotNull(clientId);
+        Integer savingsAccountId = 
SavingsAccountHelper.openSavingsAccount(requestSpec, responseSpec, clientId, 
"1000");
+        Assertions.assertNotNull(savingsAccountId);
+
+        // Setup and activate share account with initial shares on 01 March 
2016
+        final Integer shareAccountId = setupAndActivateShareAccount(clientId, 
productId, savingsAccountId, "01 March 2016");
+
+        // Apply additional shares on 20 May 2016
+        applyAdditionalShares(shareAccountId, "20 May 2016", "30");
+
+        // Retrieve transactions and find the additional shares request
+        Map<String, Object> shareAccountData = 
ShareAccountTransactionHelper.retrieveShareAccount(shareAccountId, requestSpec,
+                responseSpec);
+        List<Map<String, Object>> transactions = (List<Map<String, Object>>) 
shareAccountData.get("purchasedShares");
+        Assertions.assertNotNull(transactions);
+
+        // Find and reject the additional shares request (20 May 2016)
+        String additionalSharesRequestId = findTransactionId(transactions, 
"purchasedSharesType.purchased", "20 May 2016");
+        Assertions.assertNotNull(additionalSharesRequestId, "Additional shares 
request for 20 May 2016 should exist");
+
+        // Reject the additional shares request
+        rejectAdditionalSharesRequest(shareAccountId, 
additionalSharesRequestId);
+
+        // Verify transaction is rejected
+        shareAccountData = 
ShareAccountTransactionHelper.retrieveShareAccount(shareAccountId, requestSpec, 
responseSpec);
+        transactions = (List<Map<String, Object>>) 
shareAccountData.get("purchasedShares");
+        verifyTransactionStatus(transactions, "purchasedSharesType.purchased", 
"20 May 2016", "purchasedSharesStatusType.rejected");
+
+        // Now try to close the account with a date BEFORE the rejected 
transaction (15 May 2016)
+        // This should succeed because rejected transactions should be ignored 
in chronological validation
+        Map<String, Object> closeAccountMap = new HashMap<>();
+        closeAccountMap.put("note", "Share Account Close Note");
+        closeAccountMap.put("dateFormat", "dd MMMM yyyy");
+        closeAccountMap.put("closedDate", "15 May 2016");
+        closeAccountMap.put("locale", "en");
+        String closeJson = new Gson().toJson(closeAccountMap);
+        ShareAccountTransactionHelper.postCommand("close", shareAccountId, 
closeJson, requestSpec, responseSpec);
+
+        // Verify the account was successfully closed
+        shareAccountData = 
ShareAccountTransactionHelper.retrieveShareAccount(shareAccountId, requestSpec, 
responseSpec);
+        Map<String, Object> statusMap = (Map<String, Object>) 
shareAccountData.get("status");
+        Assertions.assertEquals("shareAccountStatusType.closed", 
String.valueOf(statusMap.get("code")));
+
+        Map<String, Object> timelineMap = (Map<String, Object>) 
shareAccountData.get("timeline");
+        List<Integer> closedDateList = (List<Integer>) 
timelineMap.get("closedDate");
+        LocalDate closedDate = LocalDate.of(closedDateList.get(0), 
closedDateList.get(1), closedDateList.get(2));
+        Assertions.assertEquals("15 May 2016", 
closedDate.format(Utils.dateFormatter));
+    }
+
+    // Additional Test 1: Verify original validation still works (Negative 
Test)
+    @Test
+    @SuppressWarnings("unchecked")
+    public void 
testChronologicalAdditionalSharesBeforeActiveTransactionShouldFail() {
+        // FINERACT-2457: Verify that the fix didn't break the original 
chronological validation
+        // Transactions BEFORE active/approved transactions should still be 
REJECTED
+        shareProductHelper = new ShareProductHelper();
+        final Integer productId = createShareProduct();
+        Assertions.assertNotNull(productId);
+        final Integer clientId = ClientHelper.createClient(this.requestSpec, 
this.responseSpec);
+        Assertions.assertNotNull(clientId);
+        Integer savingsAccountId = 
SavingsAccountHelper.openSavingsAccount(requestSpec, responseSpec, clientId, 
"1000");
+        Assertions.assertNotNull(savingsAccountId);
+
+        // Setup and activate share account with initial shares on 01 March 
2016
+        final Integer shareAccountId = setupAndActivateShareAccount(clientId, 
productId, savingsAccountId, "01 March 2016");
+
+        // Apply additional shares on 15 April 2016 (this remains 
active/approved)
+        applyAdditionalShares(shareAccountId, "15 April 2016", "20");
+
+        // Verify the transaction exists and is in applied/active state
+        Map<String, Object> shareAccountData = 
ShareAccountTransactionHelper.retrieveShareAccount(shareAccountId, requestSpec,
+                responseSpec);
+        List<Map<String, Object>> transactions = (List<Map<String, Object>>) 
shareAccountData.get("purchasedShares");
+        Assertions.assertNotNull(transactions);
+
+        String transactionId = findTransactionId(transactions, 
"purchasedSharesType.purchased", "15 April 2016");
+        Assertions.assertNotNull(transactionId, "Transaction for 15 April 2016 
should exist");
+
+        // Try to apply additional shares BEFORE the active transaction (10 
April 2016)
+        // This should FAIL because the April 15 transaction is ACTIVE/APPROVED
+        Map<String, Object> additionalSharesRequestMap = new HashMap<>();
+        additionalSharesRequestMap.put("requestedDate", "10 April 2016");
+        additionalSharesRequestMap.put("dateFormat", "dd MMMM yyyy");
+        additionalSharesRequestMap.put("locale", "en");
+        additionalSharesRequestMap.put("requestedShares", "15");
+        String additionalSharesRequestJson = new 
Gson().toJson(additionalSharesRequestMap);
+
+        ResponseSpecification errorResponse = new 
ResponseSpecBuilder().expectStatusCode(400).build();
+
+        try {
+            ShareAccountTransactionHelper.postCommand("applyadditionalshares", 
shareAccountId, additionalSharesRequestJson, requestSpec,
+                    errorResponse);
+
+        } catch (Exception e) {
+            Assertions.assertTrue(
+                    e.getMessage().contains("chronological") || 
e.getMessage().contains("date") || e.getMessage().contains("before"),
+                    "Error message should indicate chronological validation 
failure");
+        }
+    }
+
+    // Additional Test 3: Test edge case - transaction on same date as 
rejected transaction
+    @Test
+    @SuppressWarnings("unchecked")
+    public void 
testChronologicalAdditionalSharesOnSameDateAsRejectedTransaction() {
+        // FINERACT-2457: Test behavior when applying shares on the SAME date 
as a rejected transaction
+        shareProductHelper = new ShareProductHelper();
+        final Integer productId = createShareProduct();
+        Assertions.assertNotNull(productId);
+        final Integer clientId = ClientHelper.createClient(this.requestSpec, 
this.responseSpec);
+        Assertions.assertNotNull(clientId);
+        Integer savingsAccountId = 
SavingsAccountHelper.openSavingsAccount(requestSpec, responseSpec, clientId, 
"1000");
+        Assertions.assertNotNull(savingsAccountId);
+
+        // Setup and activate share account with initial shares on 01 March 
2016
+        final Integer shareAccountId = setupAndActivateShareAccount(clientId, 
productId, savingsAccountId, "01 March 2016");
+
+        // Apply and reject shares on 15 April 2016
+        applyAdditionalShares(shareAccountId, "15 April 2016", "20");
+        Map<String, Object> shareAccountData = 
ShareAccountTransactionHelper.retrieveShareAccount(shareAccountId, requestSpec,
+                responseSpec);
+        List<Map<String, Object>> transactions = (List<Map<String, Object>>) 
shareAccountData.get("purchasedShares");
+        String txId = findTransactionId(transactions, 
"purchasedSharesType.purchased", "15 April 2016");
+        Assertions.assertNotNull(txId);
+        rejectAdditionalSharesRequest(shareAccountId, txId);
+
+        // Verify transaction is rejected
+        shareAccountData = 
ShareAccountTransactionHelper.retrieveShareAccount(shareAccountId, requestSpec, 
responseSpec);
+        transactions = (List<Map<String, Object>>) 
shareAccountData.get("purchasedShares");
+        verifyTransactionStatus(transactions, "purchasedSharesType.purchased", 
"15 April 2016", "purchasedSharesStatusType.rejected");
+
+        // Try to apply shares on the SAME date as the rejected transaction
+        // This should succeed since rejected transactions are ignored
+        applyAdditionalShares(shareAccountId, "15 April 2016", "15");
+
+        // Verify the new transaction was successfully added
+        shareAccountData = 
ShareAccountTransactionHelper.retrieveShareAccount(shareAccountId, requestSpec, 
responseSpec);
+        transactions = (List<Map<String, Object>>) 
shareAccountData.get("purchasedShares");
+
+        // Count how many transactions exist for 15 April 2016
+        DateFormat simple = new SimpleDateFormat("dd MMMM yyyy");
+        int countForDate = 0;
+        int appliedCountForDate = 0;
+
+        for (Map<String, Object> transaction : transactions) {
+            Map<String, Object> transactionTypeMap = (Map<String, Object>) 
transaction.get("type");
+            List<Integer> dateList = (List<Integer>) 
transaction.get("purchasedDate");
+            String transactionType = (String) transactionTypeMap.get("code");
+            String transactionDate = formatTransactionDate(dateList, simple);
+
+            if (transactionType.equals("purchasedSharesType.purchased") && 
transactionDate.equals("15 April 2016")) {
+                countForDate++;
+                Map<String, Object> transactionStatusMap = (Map<String, 
Object>) transaction.get("status");
+                String status = 
String.valueOf(transactionStatusMap.get("code"));
+                if (status.equals("purchasedSharesStatusType.applied")) {
+                    appliedCountForDate++;
+                    Assertions.assertEquals("15", 
String.valueOf(transaction.get("numberOfShares")));
+                }
+            }
+        }
+
+        // Should have 2 transactions for this date: 1 rejected, 1 applied
+        Assertions.assertEquals(2, countForDate, "Should have 2 transactions 
for 15 April 2016");
+        Assertions.assertEquals(1, appliedCountForDate, "Should have 1 applied 
transaction for 15 April 2016");
+    }
+
+    // Additional Test 4: Test account closure before active transaction 
should still fail
+    @Test
+    @SuppressWarnings("unchecked")
+    public void 
testChronologicalAccountClosureBeforeActiveTransactionShouldFail() {
+        // FINERACT-2457: Verify that closing account before active 
transactions is still blocked
+        shareProductHelper = new ShareProductHelper();
+        final Integer productId = createShareProduct();
+        Assertions.assertNotNull(productId);
+        final Integer clientId = ClientHelper.createClient(this.requestSpec, 
this.responseSpec);
+        Assertions.assertNotNull(clientId);
+        Integer savingsAccountId = 
SavingsAccountHelper.openSavingsAccount(requestSpec, responseSpec, clientId, 
"1000");
+        Assertions.assertNotNull(savingsAccountId);
+
+        // Setup and activate share account with initial shares on 01 March 
2016
+        final Integer shareAccountId = setupAndActivateShareAccount(clientId, 
productId, savingsAccountId, "01 March 2016");
+
+        // Apply additional shares on 20 May 2016 (and keep it active/approved)
+        applyAdditionalShares(shareAccountId, "20 May 2016", "30");
+
+        // Verify the transaction exists and is active
+        Map<String, Object> shareAccountData = 
ShareAccountTransactionHelper.retrieveShareAccount(shareAccountId, requestSpec,
+                responseSpec);
+        List<Map<String, Object>> transactions = (List<Map<String, Object>>) 
shareAccountData.get("purchasedShares");
+        String transactionId = findTransactionId(transactions, 
"purchasedSharesType.purchased", "20 May 2016");
+        Assertions.assertNotNull(transactionId, "Transaction for 20 May 2016 
should exist");
+
+        // Try to close account on 15 May 2016 (before the active transaction)
+        // This should FAIL
+        Map<String, Object> closeAccountMap = new HashMap<>();
+        closeAccountMap.put("note", "Share Account Close Note");
+        closeAccountMap.put("dateFormat", "dd MMMM yyyy");
+        closeAccountMap.put("closedDate", "15 May 2016");
+        closeAccountMap.put("locale", "en");
+        String closeJson = new Gson().toJson(closeAccountMap);
+
+        ResponseSpecification errorResponse = new 
ResponseSpecBuilder().expectStatusCode(400).build();
+
+        try {
+            ShareAccountTransactionHelper.postCommand("close", shareAccountId, 
closeJson, requestSpec, errorResponse);
+
+            // If we get here, the validation worked correctly (request was 
rejected)
+            // This is the expected behavior
+        } catch (Exception e) {
+            // Depending on the framework, the error might be thrown as an 
exception
+            // We expect this to fail, so this is acceptable
+            Assertions
+                    .assertTrue(
+                            e.getMessage().contains("chronological") || 
e.getMessage().contains("date") || e.getMessage().contains("before")
+                                    || e.getMessage().contains("transaction"),
+                            "Error message should indicate chronological 
validation failure");
+        }
+
+        // Verify account is still active (not closed)
+        shareAccountData = 
ShareAccountTransactionHelper.retrieveShareAccount(shareAccountId, requestSpec, 
responseSpec);
+        Map<String, Object> statusMap = (Map<String, Object>) 
shareAccountData.get("status");
+        Assertions.assertEquals("shareAccountStatusType.active", 
String.valueOf(statusMap.get("code")),
+                "Account should still be active since closure was rejected");
+    }
+
     private Integer createShareProduct() {
         String shareProductJson = shareProductHelper.build();
         return 
ShareProductTransactionHelper.createShareProduct(shareProductJson, requestSpec, 
responseSpec);
@@ -1009,4 +1288,140 @@ public class ShareAccountIntegrationTests {
         map.put("amount", amount);
         return map;
     }
+
+    private void updateShareAccountWithInitialData(Integer shareAccountId, 
Integer requestedShares, String applicationDate) {
+        Map<String, Object> shareAccountDataForUpdate = new HashMap<>();
+        shareAccountDataForUpdate.put("requestedShares", requestedShares);
+        shareAccountDataForUpdate.put("applicationDate", applicationDate);
+        shareAccountDataForUpdate.put("dateFormat", "dd MMMM yyyy");
+        shareAccountDataForUpdate.put("locale", "en_GB");
+        String updateShareAccountJsonString = new 
Gson().toJson(shareAccountDataForUpdate);
+        ShareAccountTransactionHelper.updateShareAccount(shareAccountId, 
updateShareAccountJsonString, requestSpec, responseSpec);
+    }
+
+    private void approveShareAccount(Integer shareAccountId, String 
approvalDate) {
+        Map<String, Object> approveMap = new HashMap<>();
+        approveMap.put("note", "Share Account Approval Note");
+        approveMap.put("dateFormat", "dd MMMM yyyy");
+        approveMap.put("approvedDate", approvalDate);
+        approveMap.put("locale", "en");
+        String approve = new Gson().toJson(approveMap);
+        ShareAccountTransactionHelper.postCommand("approve", shareAccountId, 
approve, requestSpec, responseSpec);
+    }
+
+    private void activateShareAccount(Integer shareAccountId, String 
activationDate) {
+        Map<String, Object> activateMap = new HashMap<>();
+        activateMap.put("dateFormat", "dd MMMM yyyy");
+        activateMap.put("activatedDate", activationDate);
+        activateMap.put("locale", "en");
+        String activateJson = new Gson().toJson(activateMap);
+        ShareAccountTransactionHelper.postCommand("activate", shareAccountId, 
activateJson, requestSpec, responseSpec);
+    }
+
+    private void applyAdditionalShares(Integer shareAccountId, String 
requestedDate, String requestedShares) {
+        Map<String, Object> additionalSharesRequestMap = new HashMap<>();
+        additionalSharesRequestMap.put("requestedDate", requestedDate);
+        additionalSharesRequestMap.put("dateFormat", "dd MMMM yyyy");
+        additionalSharesRequestMap.put("locale", "en");
+        additionalSharesRequestMap.put("requestedShares", requestedShares);
+        String additionalSharesRequestJson = new 
Gson().toJson(additionalSharesRequestMap);
+        ShareAccountTransactionHelper.postCommand("applyadditionalshares", 
shareAccountId, additionalSharesRequestJson, requestSpec,
+                responseSpec);
+    }
+
+    private String formatTransactionDate(List<Integer> dateList, DateFormat 
formatter) {
+        Calendar cal = Calendar.getInstance();
+        cal.set(dateList.get(0), dateList.get(1) - 1, dateList.get(2));
+        Date date = cal.getTime();
+        return formatter.format(date);
+    }
+
+    private String findTransactionId(List<Map<String, Object>> transactions, 
String transactionTypeCode, String expectedDate) {
+        DateFormat simple = new SimpleDateFormat("dd MMMM yyyy");
+        for (Map<String, Object> transaction : transactions) {
+            Map<String, Object> transactionTypeMap = (Map<String, Object>) 
transaction.get("type");
+            List<Integer> dateList = (List<Integer>) 
transaction.get("purchasedDate");
+            String transactionType = (String) transactionTypeMap.get("code");
+            String transactionDate = formatTransactionDate(dateList, simple);
+
+            if (transactionType.equals(transactionTypeCode) && 
transactionDate.equals(expectedDate)) {
+                return String.valueOf(transaction.get("id"));
+            }
+        }
+        return null;
+    }
+
+    private void rejectAdditionalSharesRequest(Integer shareAccountId, String 
transactionId) {
+        Map<String, List<Map<String, Object>>> rejectMap = new HashMap<>();
+        List<Map<String, Object>> list = new ArrayList<>();
+        Map<String, Object> idsMap = new HashMap<>();
+        idsMap.put("id", transactionId);
+        list.add(idsMap);
+        rejectMap.put("requestedShares", list);
+        String rejectJson = new Gson().toJson(rejectMap);
+        ShareAccountTransactionHelper.postCommand("rejectadditionalshares", 
shareAccountId, rejectJson, requestSpec, responseSpec);
+    }
+
+    private void verifyTransactionStatus(List<Map<String, Object>> 
transactions, String transactionTypeCode, String expectedDate,
+            String expectedStatus) {
+        DateFormat simple = new SimpleDateFormat("dd MMMM yyyy");
+        boolean transactionFound = false;
+
+        for (Map<String, Object> transaction : transactions) {
+            Map<String, Object> transactionTypeMap = (Map<String, Object>) 
transaction.get("type");
+            List<Integer> dateList = (List<Integer>) 
transaction.get("purchasedDate");
+            String transactionType = (String) transactionTypeMap.get("code");
+            String transactionDate = formatTransactionDate(dateList, simple);
+
+            if (transactionType.equals(transactionTypeCode) && 
transactionDate.equals(expectedDate)) {
+                Map<String, Object> transactionStatusMap = (Map<String, 
Object>) transaction.get("status");
+                Assertions.assertEquals(expectedStatus, 
String.valueOf(transactionStatusMap.get("code")));
+                transactionFound = true;
+                break;
+            }
+        }
+
+        Assertions.assertTrue(transactionFound,
+                String.format("Transaction with type %s for %s should exist", 
transactionTypeCode, expectedDate));
+    }
+
+    private void verifyTransactionWithShares(List<Map<String, Object>> 
transactions, String transactionTypeCode, String expectedDate,
+            String expectedShares, String expectedStatus) {
+        DateFormat simple = new SimpleDateFormat("dd MMMM yyyy");
+        boolean transactionFound = false;
+
+        for (Map<String, Object> transaction : transactions) {
+            Map<String, Object> transactionTypeMap = (Map<String, Object>) 
transaction.get("type");
+            List<Integer> dateList = (List<Integer>) 
transaction.get("purchasedDate");
+            String transactionType = (String) transactionTypeMap.get("code");
+            String transactionDate = formatTransactionDate(dateList, simple);
+
+            if (transactionType.equals(transactionTypeCode) && 
transactionDate.equals(expectedDate)) {
+                Assertions.assertEquals(expectedShares, 
String.valueOf(transaction.get("numberOfShares")));
+                Map<String, Object> transactionStatusMap = (Map<String, 
Object>) transaction.get("status");
+                Assertions.assertEquals(expectedStatus, 
String.valueOf(transactionStatusMap.get("code")));
+                transactionFound = true;
+                break;
+            }
+        }
+
+        Assertions.assertTrue(transactionFound, String.format("Transaction for 
%s should be successfully created", expectedDate));
+    }
+
+    private Integer setupAndActivateShareAccount(Integer clientId, Integer 
productId, Integer savingsAccountId, String initialDate) {
+        final Integer shareAccountId = createShareAccount(clientId, productId, 
savingsAccountId);
+        Assertions.assertNotNull(shareAccountId);
+
+        updateShareAccountWithInitialData(shareAccountId, 25, initialDate);
+        approveShareAccount(shareAccountId, initialDate);
+        activateShareAccount(shareAccountId, initialDate);
+
+        // Verify account is active
+        Map<String, Object> shareAccountData = 
ShareAccountTransactionHelper.retrieveShareAccount(shareAccountId, requestSpec,
+                responseSpec);
+        Map<String, Object> statusMap = (Map<String, Object>) 
shareAccountData.get("status");
+        Assertions.assertEquals("shareAccountStatusType.active", 
String.valueOf(statusMap.get("code")));
+
+        return shareAccountId;
+    }
 }

Reply via email to