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

commit 9dc417efb10f60f08f0d3f8d4ba9c55f3ff3314d
Author: Mkkor <mu...@korconnection.com>
AuthorDate: Wed Jul 12 13:51:18 2023 +0530

    FINERACT-1911 Assign datatable to Savings transaction
---
 .../DatatableCommandFromApiJsonDeserializer.java   |  34 ++-
 .../dataqueries/data/EntityTables.java             |  11 +-
 ...ityDatatableChecksWritePlatformServiceImpl.java |   5 +-
 .../service/ReadWriteNonCoreDataServiceImpl.java   | 159 ++++++-------
 ...AccountTransactionDatatableIntegrationTest.java | 249 +++++++++++++++++++++
 5 files changed, 357 insertions(+), 101 deletions(-)

diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/serialization/DatatableCommandFromApiJsonDeserializer.java
 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/serialization/DatatableCommandFromApiJsonDeserializer.java
index 9d3c666b0..2bea7c024 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/serialization/DatatableCommandFromApiJsonDeserializer.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/serialization/DatatableCommandFromApiJsonDeserializer.java
@@ -34,6 +34,7 @@ import 
org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
 import org.apache.fineract.infrastructure.core.exception.InvalidJsonException;
 import 
org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
 import 
org.apache.fineract.infrastructure.core.exception.PlatformDataIntegrityException;
+import org.apache.fineract.infrastructure.dataqueries.data.EntityTables;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 
@@ -55,15 +56,6 @@ public class DatatableCommandFromApiJsonDeserializer {
     public static final String DROP_COLUMNS = "dropColumns";
     public static final String AFTER = "after";
     public static final String NEW_CODE = "newCode";
-    public static final String M_LOAN = "m_loan";
-    public static final String M_SAVINGS_ACCOUNT = "m_savings_account";
-    public static final String M_CLIENT = "m_client";
-    public static final String M_GROUP = "m_group";
-    public static final String M_CENTER = "m_center";
-    public static final String M_OFFICE = "m_office";
-    public static final String M_SAVINGS_PRODUCT = "m_savings_product";
-    public static final String M_PRODUCT_LOAN = "m_product_loan";
-    public static final String M_SHARE_PRODUCT = "m_share_product";
     public static final String NEW_NAME = "newName";
     public static final String STRING = "string";
     public static final String NUMBER = "number";
@@ -92,8 +84,6 @@ public class DatatableCommandFromApiJsonDeserializer {
             Arrays.asList(NAME, NEW_NAME, LENGTH, MANDATORY, AFTER, CODE, 
NEW_CODE, UNIQUE, INDEXED));
     private static final Set<String> SUPPORTED_PARAMETERS_FOR_DROP_COLUMNS = 
new HashSet<>(List.of(NAME));
     private static final Object[] SUPPORTED_COLUMN_TYPES = { STRING, NUMBER, 
BOOLEAN, DECIMAL, DATE, DATETIME, TEXT, DROPDOWN };
-    private static final Object[] SUPPORTED_APPTABLE_NAMES = { M_LOAN, 
M_SAVINGS_ACCOUNT, M_CLIENT, M_GROUP, M_CENTER, M_OFFICE,
-            M_SAVINGS_PRODUCT, M_PRODUCT_LOAN, M_SHARE_PRODUCT };
 
     private final FromJsonHelper fromApiJsonHelper;
 
@@ -158,12 +148,9 @@ public class DatatableCommandFromApiJsonDeserializer {
 
         final String apptableName = 
this.fromApiJsonHelper.extractStringNamed(APPTABLE_NAME, element);
         
baseDataValidator.reset().parameter(APPTABLE_NAME).value(apptableName).notBlank().notExceedingLengthOf(50)
-                .isOneOfTheseValues(SUPPORTED_APPTABLE_NAMES);
+                .isOneOfTheseStringValues(EntityTables.getEntitiesList());
 
-        if (M_CLIENT.equals(apptableName)) {
-            String entitySubType = 
this.fromApiJsonHelper.extractStringNamed(ENTITY_SUB_TYPE, element);
-            
baseDataValidator.reset().parameter(ENTITY_SUB_TYPE).value(entitySubType).notBlank();
 // Person or Entity
-        }
+        validateEntitySubType(baseDataValidator, element, apptableName);
         final String fkColumnName = (apptableName != null) ? 
apptableName.substring(2) + "_id" : "";
 
         final Boolean multiRow = 
this.fromApiJsonHelper.extractBooleanNamed(MULTI_ROW, element);
@@ -194,6 +181,14 @@ public class DatatableCommandFromApiJsonDeserializer {
         throwExceptionIfValidationWarningsExist(dataValidationErrors);
     }
 
+    private void validateEntitySubType(final DataValidatorBuilder 
baseDataValidator, final JsonElement element, final String apptableName) {
+        EntityTables entityTable = EntityTables.fromName(apptableName);
+        if (entityTable != null && EntityTables.CLIENT == entityTable) {
+            String entitySubType = 
this.fromApiJsonHelper.extractStringNamed(ENTITY_SUB_TYPE, element);
+            
baseDataValidator.reset().parameter(ENTITY_SUB_TYPE).value(entitySubType).notBlank();
 // Person or Entity
+        }
+    }
+
     public void validateForUpdate(final String json) {
         if (StringUtils.isBlank(json)) {
             throw new InvalidJsonException();
@@ -218,12 +213,9 @@ public class DatatableCommandFromApiJsonDeserializer {
         final JsonElement element = this.fromApiJsonHelper.parse(json);
         final String apptableName = 
this.fromApiJsonHelper.extractStringNamed(APPTABLE_NAME, element);
         
baseDataValidator.reset().parameter(APPTABLE_NAME).value(apptableName).ignoreIfNull().notBlank()
-                .isOneOfTheseValues(SUPPORTED_APPTABLE_NAMES);
+                .isOneOfTheseStringValues(EntityTables.getEntitiesList());
 
-        if (M_CLIENT.equals(apptableName)) {
-            String entitySubType = 
this.fromApiJsonHelper.extractStringNamed(ENTITY_SUB_TYPE, element);
-            
baseDataValidator.reset().parameter(ENTITY_SUB_TYPE).value(entitySubType).notBlank();
 // Person or Entity
-        }
+        validateEntitySubType(baseDataValidator, element, apptableName);
 
         final String fkColumnName = (apptableName != null) ? 
apptableName.substring(2) + "_id" : "";
 
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/data/EntityTables.java
 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/data/EntityTables.java
index a41c632b2..bd0b5a399 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/data/EntityTables.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/data/EntityTables.java
@@ -36,7 +36,12 @@ public enum EntityTables {
                                     
ImmutableList.of(StatusEnum.CREATE.getCode(), StatusEnum.APPROVE.getCode(),
                                             StatusEnum.ACTIVATE.getCode(), 
StatusEnum.WITHDRAWN.getCode(), StatusEnum.REJECTED.getCode(),
                                             StatusEnum.CLOSE.getCode()),
-                                    "savings_account_id");
+                                    "savings_account_id"), 
SAVINGS_TRANSACTION("m_savings_account_transaction", ImmutableList.of(),
+                                            "savings_account_transcation_id"), 
OFFICE("m_office", ImmutableList.of(),
+                                                    "office_id"), 
PRODUCT_LOAN("m_product_loan", ImmutableList.of(),
+                                                            
"product_loan_id"), SAVINGS_PRODUCT("m_savings_product", ImmutableList.of(),
+                                                                    
"savings_product_id"), SHARE_PRODUCT("m_share_product",
+                                                                            
ImmutableList.of(), "share_product_id");
 
     private static final Map<String, EntityTables> lookup = new 
HashMap<String, EntityTables>();
 
@@ -77,6 +82,10 @@ public enum EntityTables {
         return ImmutableList.of();
     }
 
+    public static EntityTables fromName(String name) {
+        return name == null ? null : lookup.get(name.toLowerCase());
+    }
+
     public ImmutableList<Integer> getCodes() {
         return this.codes;
     }
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/service/EntityDatatableChecksWritePlatformServiceImpl.java
 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/service/EntityDatatableChecksWritePlatformServiceImpl.java
index 7ff3c824f..9279a12c5 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/service/EntityDatatableChecksWritePlatformServiceImpl.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/service/EntityDatatableChecksWritePlatformServiceImpl.java
@@ -105,10 +105,11 @@ public class 
EntityDatatableChecksWritePlatformServiceImpl implements EntityData
                     throw new 
EntityDatatableCheckAlreadyExistsException(entity, status, datatableName);
                 }
             } else {
-                if (entity.equals("m_loan")) {
+                EntityTables entityTable = EntityTables.fromName(entity);
+                if (EntityTables.LOAN == entityTable) {
                     // if invalid loan product id, throws exception
                     
this.loanProductReadPlatformService.retrieveLoanProduct(productId);
-                } else if (entity.equals("m_savings_account")) {
+                } else if (EntityTables.SAVING == entityTable) {
                     // if invalid savings product id, throws exception
                     
this.savingsProductReadPlatformService.retrieveOne(productId);
                 } else {
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/service/ReadWriteNonCoreDataServiceImpl.java
 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/service/ReadWriteNonCoreDataServiceImpl.java
index 06f2f0521..ae5f88700 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/service/ReadWriteNonCoreDataServiceImpl.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/service/ReadWriteNonCoreDataServiceImpl.java
@@ -70,6 +70,7 @@ import 
org.apache.fineract.infrastructure.core.service.database.DatabaseTypeReso
 import org.apache.fineract.infrastructure.dataqueries.api.DataTableApiConstant;
 import org.apache.fineract.infrastructure.dataqueries.data.DataTableValidator;
 import org.apache.fineract.infrastructure.dataqueries.data.DatatableData;
+import org.apache.fineract.infrastructure.dataqueries.data.EntityTables;
 import 
org.apache.fineract.infrastructure.dataqueries.data.GenericResultsetData;
 import 
org.apache.fineract.infrastructure.dataqueries.data.ResultsetColumnHeaderData;
 import org.apache.fineract.infrastructure.dataqueries.data.ResultsetRowData;
@@ -737,7 +738,7 @@ public class ReadWriteNonCoreDataServiceImpl implements 
ReadWriteNonCoreDataServ
             }
 
             validateDatatableName(datatableName);
-            validateAppTable(apptableName);
+            validateAppTable(actualAppTableName);
             final boolean isConstraintApproach = 
this.configurationDomainService.isConstraintApproachEnabledForDatatables();
             final String fkColumnName = apptableName.substring(2) + "_id";
             final String dataTableNameAlias = 
datatableName.toLowerCase().replaceAll("\\s", "_");
@@ -1714,98 +1715,102 @@ public class ReadWriteNonCoreDataServiceImpl 
implements ReadWriteNonCoreDataServ
          * unfortunately have to, one way or another, be able to restrict data 
to the users office hierarchy. Here, a
          * few key tables are done. But if additional fields are needed on 
other tables the same pattern applies
          */
-
         final AppUser currentUser = this.context.authenticatedUser();
+        String currentUserOfficeHierarchy = 
currentUser.getOffice().getHierarchy();
+        String officeHierarchyCondition = " o.hierarchy like '" + 
currentUserOfficeHierarchy + "%'";
         String scopedSQL = null;
         /*
          * m_loan and m_savings_account are connected to an m_office thru 
either an m_client or an m_group If both it
          * means it relates to an m_client that is in a group (still an 
m_client account)
          */
-        if (appTable.equalsIgnoreCase("m_loan")) {
-            scopedSQL = "select distinct x.* from ("
-                    + " (select o.id as officeId, l.group_id as groupId, 
l.client_id as clientId, null as savingsId, l.id as loanId, null as entityId 
from m_loan l "
-                    + " join m_client c on c.id = l.client_id " + " join 
m_office o on o.id = c.office_id and o.hierarchy like '"
-                    + currentUser.getOffice().getHierarchy() + "%'" + " where 
l.id = " + appTableId + ")" + " union all "
-                    + " (select o.id as officeId, l.group_id as groupId, 
l.client_id as clientId, null as savingsId, l.id as loanId, null as entityId 
from m_loan l "
-                    + " join m_group g on g.id = l.group_id " + " join 
m_office o on o.id = g.office_id and o.hierarchy like '"
-                    + currentUser.getOffice().getHierarchy() + "%'" + " where 
l.id = " + appTableId + ")" + " ) as x";
-        }
-        if (appTable.equalsIgnoreCase("m_savings_account")) {
-            scopedSQL = "select distinct x.* from ("
-                    + " (select o.id as officeId, s.group_id as groupId, 
s.client_id as clientId, s.id as savingsId, null as loanId, null as entityId 
from m_savings_account s "
-                    + " join m_client c on c.id = s.client_id " + " join 
m_office o on o.id = c.office_id and o.hierarchy like '"
-                    + currentUser.getOffice().getHierarchy() + "%'" + " where 
s.id = " + appTableId + ")" + " union all "
-                    + " (select o.id as officeId, s.group_id as groupId, 
s.client_id as clientId, s.id as savingsId, null as loanId, null as entityId 
from m_savings_account s "
-                    + " join m_group g on g.id = s.group_id " + " join 
m_office o on o.id = g.office_id and o.hierarchy like '"
-                    + currentUser.getOffice().getHierarchy() + "%'" + " where 
s.id = " + appTableId + ")" + " ) as x";
-        }
-        if (appTable.equalsIgnoreCase("m_client")) {
-            scopedSQL = "select o.id as officeId, null as groupId, c.id as 
clientId, null as savingsId, null as loanId, null as entityId from m_client c "
-                    + " join m_office o on o.id = c.office_id and o.hierarchy 
like '" + currentUser.getOffice().getHierarchy() + "%'"
-                    + " where c.id = " + appTableId;
-        }
-        if (appTable.equalsIgnoreCase("m_group") || 
appTable.equalsIgnoreCase("m_center")) {
-            scopedSQL = "select o.id as officeId, g.id as groupId, null as 
clientId, null as savingsId, null as loanId, null as entityId from m_group g "
-                    + " join m_office o on o.id = g.office_id and o.hierarchy 
like '" + currentUser.getOffice().getHierarchy() + "%'"
-                    + " where g.id = " + appTableId;
-        }
-        if (appTable.equalsIgnoreCase("m_office")) {
-            scopedSQL = "select o.id as officeId, null as groupId, null as 
clientId, null as savingsId, null as loanId, null as entityId from m_office o "
-                    + " where o.hierarchy like '" + 
currentUser.getOffice().getHierarchy() + "%'" + " and o.id = " + appTableId;
-        }
-
-        if (appTable.equalsIgnoreCase("m_product_loan") || 
appTable.equalsIgnoreCase("m_savings_product")
-                || appTable.equalsIgnoreCase("m_share_product")) {
-            scopedSQL = "select null as officeId, null as groupId, null as 
clientId, null as savingsId, null as loanId, p.id as entityId from "
-                    + appTable + " as p WHERE p.id = " + appTableId;
-        }
-
-        if (scopedSQL == null) {
-            throw new 
PlatformDataIntegrityException("error.msg.invalid.dataScopeCriteria",
-                    "Application Table: " + appTable + " not catered for in 
data Scoping");
+        EntityTables entityTable = EntityTables.fromName(appTable);
+        switch (entityTable) {
+            case LOAN:
+                scopedSQL = "select distinct x.* from ("
+                        + " (select o.id as officeId, l.group_id as groupId, 
l.client_id as clientId, null as savingsId, l.id as loanId, null as entityId 
from m_loan l "
+                        + 
getClientOfficeJoinCondition(currentUserOfficeHierarchy, appTableId, "l") + " 
where l.id = " + appTableId + ")"
+                        + " union all "
+                        + " (select o.id as officeId, l.group_id as groupId, 
l.client_id as clientId, null as savingsId, l.id as loanId, null as entityId 
from m_loan l "
+                        + 
getGroupOfficeJoinCondition(currentUserOfficeHierarchy, appTableId, "l") + " 
where l.id = " + appTableId + ")"
+                        + " ) as x";
+            break;
+            case SAVING:
+                scopedSQL = "select distinct x.* from ("
+                        + " (select o.id as officeId, s.group_id as groupId, 
s.client_id as clientId, s.id as savingsId, null as loanId, null as entityId 
from m_savings_account s "
+                        + 
getClientOfficeJoinCondition(currentUserOfficeHierarchy, appTableId, "s") + " 
where s.id = " + appTableId + ")"
+                        + " union all "
+                        + " (select o.id as officeId, s.group_id as groupId, 
s.client_id as clientId, s.id as savingsId, null as loanId, null as entityId 
from m_savings_account s "
+                        + 
getGroupOfficeJoinCondition(currentUserOfficeHierarchy, appTableId, "s") + " 
where s.id = " + appTableId + ")"
+                        + " ) as x";
+            break;
+            case SAVINGS_TRANSACTION:
+                scopedSQL = "select distinct x.* from ("
+                        + " (select o.id as officeId, s.group_id as groupId, 
s.client_id as clientId, s.id as savingsId, null as loanId, t.id as entityId 
from m_savings_account_transaction t"
+                        + " join m_savings_account s on t.savings_account_id = 
s.id "
+                        + 
getClientOfficeJoinCondition(currentUserOfficeHierarchy, appTableId, "s") + " 
where t.id = " + appTableId + ")"
+                        + " union all "
+                        + " (select o.id as officeId, s.group_id as groupId, 
s.client_id as clientId, s.id as savingsId, null as loanId, t.id as entityId 
from m_savings_account_transaction t "
+                        + " join m_savings_account s on t.savings_account_id = 
s.id "
+                        + 
getGroupOfficeJoinCondition(currentUserOfficeHierarchy, appTableId, "s") + " 
where t.id = " + appTableId + ")"
+                        + " ) as x";
+            break;
+            case CLIENT:
+                scopedSQL = "select o.id as officeId, null as groupId, c.id as 
clientId, null as savingsId, null as loanId, null as entityId from m_client c "
+                        + getOfficeJoinCondition(currentUserOfficeHierarchy, 
appTableId, "c") + " where c.id = " + appTableId;
+            break;
+            case GROUP:
+                scopedSQL = "select o.id as officeId, g.id as groupId, null as 
clientId, null as savingsId, null as loanId, null as entityId from m_group g "
+                        + getOfficeJoinCondition(currentUserOfficeHierarchy, 
appTableId, "g") + " where g.id = " + appTableId;
+            break;
+            case OFFICE:
+                scopedSQL = "select o.id as officeId, null as groupId, null as 
clientId, null as savingsId, null as loanId, null as entityId from m_office o "
+                        + " where " + officeHierarchyCondition + " and o.id = 
" + appTableId;
+            break;
+            case PRODUCT_LOAN:
+            case SAVINGS_PRODUCT:
+            case SHARE_PRODUCT:
+                scopedSQL = "select null as officeId, null as groupId, null as 
clientId, null as savingsId, null as loanId, p.id as entityId from "
+                        + appTable + " as p WHERE p.id = " + appTableId;
+            break;
+            default:
+                throw new 
PlatformDataIntegrityException("error.msg.invalid.dataScopeCriteria",
+                        "Application Table: " + appTable + " not catered for 
in data Scoping");
         }
-
         return scopedSQL;
 
     }
 
-    private void validateAppTable(final String appTable) {
+    private String getClientOfficeJoinCondition(String 
currentUserOfficeHierarchy, Long appTableId, String appTableAlias) {
+        StringBuilder clientOfficeJoinBuilder = new StringBuilder();
+        clientOfficeJoinBuilder.append(" join m_client c on c.id = 
").append(appTableAlias).append(".client_id ")
+                .append(getOfficeJoinCondition(currentUserOfficeHierarchy, 
appTableId, "c"));
+        return clientOfficeJoinBuilder.toString();
+    }
 
-        if (appTable.equalsIgnoreCase("m_loan")) {
-            return;
-        }
-        if (appTable.equalsIgnoreCase("m_savings_account")) {
-            return;
-        }
-        if (appTable.equalsIgnoreCase("m_client")) {
-            return;
-        }
-        if (appTable.equalsIgnoreCase("m_group")) {
-            return;
-        }
-        if (appTable.equalsIgnoreCase("m_center")) {
-            return;
-        }
-        if (appTable.equalsIgnoreCase("m_office")) {
-            return;
-        }
-        if (appTable.equalsIgnoreCase("m_product_loan")) {
-            return;
-        }
-        if (appTable.equalsIgnoreCase("m_savings_product")) {
-            return;
-        }
-        if (appTable.equalsIgnoreCase("m_share_product")) {
-            return;
-        }
+    private String getGroupOfficeJoinCondition(String 
currentUserOfficeHierarchy, Long appTableId, String appTableAlias) {
+        StringBuilder clientOfficeJoinBuilder = new StringBuilder();
+        clientOfficeJoinBuilder.append(" join m_group g on g.id = 
").append(appTableAlias).append(".client_id ")
+                .append(getOfficeJoinCondition(currentUserOfficeHierarchy, 
appTableId, "g"));
+        return clientOfficeJoinBuilder.toString();
+    }
 
-        throw new 
PlatformDataIntegrityException("error.msg.invalid.application.table", "Invalid 
Application Table: " + appTable, "name",
-                appTable);
+    private String getOfficeJoinCondition(String currentUserOfficeHierarchy, 
Long appTableId, String joiningTableAlias) {
+        StringBuilder officeJoinBuilder = new StringBuilder();
+        officeJoinBuilder.append(" join m_office o on o.id = 
").append(joiningTableAlias).append(".office_id and ")
+                .append(" o.hierarchy like 
'").append(currentUserOfficeHierarchy).append("%' ");
+        return officeJoinBuilder.toString();
+    }
+
+    private void validateAppTable(final String appTable) {
+        if (EntityTables.fromName(appTable) == null) {
+            throw new 
PlatformDataIntegrityException("error.msg.invalid.application.table", "Invalid 
Application Table: " + appTable,
+                    "name", appTable);
+        }
     }
 
     private String mapToActualAppTable(final String appTable) {
-        if (appTable.equalsIgnoreCase("m_center")) {
-            return "m_group";
+        if ("m_center".equalsIgnoreCase(appTable)) {
+            return EntityTables.GROUP.getName();
         }
         return appTable;
     }
diff --git 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/SavingsAccountTransactionDatatableIntegrationTest.java
 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/SavingsAccountTransactionDatatableIntegrationTest.java
new file mode 100644
index 000000000..52ca3180d
--- /dev/null
+++ 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/SavingsAccountTransactionDatatableIntegrationTest.java
@@ -0,0 +1,249 @@
+/**
+ * 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.integrationtests;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+import com.google.gson.Gson;
+import io.restassured.builder.RequestSpecBuilder;
+import io.restassured.builder.ResponseSpecBuilder;
+import io.restassured.http.ContentType;
+import io.restassured.specification.RequestSpecification;
+import io.restassured.specification.ResponseSpecification;
+import java.util.HashMap;
+import java.util.List;
+import org.apache.fineract.client.models.GetDataTablesResponse;
+import org.apache.fineract.client.models.PostColumnHeaderData;
+import org.apache.fineract.client.models.PostDataTablesRequest;
+import org.apache.fineract.client.models.PostDataTablesResponse;
+import org.apache.fineract.client.models.PutDataTablesRequest;
+import org.apache.fineract.client.models.PutDataTablesRequestAddColumns;
+import org.apache.fineract.client.models.PutDataTablesResponse;
+import org.apache.fineract.client.models.ResultsetColumnHeaderData;
+import org.apache.fineract.infrastructure.dataqueries.data.EntityTables;
+import org.apache.fineract.integrationtests.common.ClientHelper;
+import org.apache.fineract.integrationtests.common.CommonConstants;
+import org.apache.fineract.integrationtests.common.GlobalConfigurationHelper;
+import org.apache.fineract.integrationtests.common.Utils;
+import 
org.apache.fineract.integrationtests.common.savings.SavingsAccountHelper;
+import 
org.apache.fineract.integrationtests.common.savings.SavingsProductHelper;
+import 
org.apache.fineract.integrationtests.common.savings.SavingsStatusChecker;
+import org.apache.fineract.integrationtests.common.system.DatatableHelper;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+public class SavingsAccountTransactionDatatableIntegrationTest {
+
+    private static final String SAVINGS_TRANSACTION_APP_TABLE_NAME = 
EntityTables.SAVINGS_TRANSACTION.getName();
+    public static final String ACCOUNT_TYPE_INDIVIDUAL = "INDIVIDUAL";
+    public static final String DEFAULT_DATE_FORMAT = "dd MMM yyyy";
+    final String startDate = "01 Jun 2023";
+    final String firstDepositDate = "05 Jun 2023";
+    final String secondDepositDate = "09 Jun 2023";
+    private RequestSpecification requestSpec;
+    private ResponseSpecification responseSpec;
+    private DatatableHelper datatableHelper;
+    private SavingsProductHelper savingsProductHelper;
+    private SavingsAccountHelper savingsAccountHelper;
+
+    @BeforeEach
+    public void setup() {
+        Utils.initializeRESTAssured();
+        this.requestSpec = new 
RequestSpecBuilder().setContentType(ContentType.JSON).build();
+        this.requestSpec.header("Authorization", "Basic " + 
Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey());
+        this.responseSpec = new 
ResponseSpecBuilder().expectStatusCode(200).build();
+        this.datatableHelper = new DatatableHelper(this.requestSpec, 
this.responseSpec);
+        this.savingsAccountHelper = new SavingsAccountHelper(this.requestSpec, 
this.responseSpec);
+        this.savingsProductHelper = new SavingsProductHelper();
+    }
+
+    @Test
+    public void 
testDatatableCreateReadUpdateDeleteForSavingsAccountTransaction() {
+        // create dataTable
+        String datatableName = 
Utils.uniqueRandomStringGenerator("dt_savings_transaction_", 
5).toLowerCase().toLowerCase();
+        String column1Name = "aNumber";
+        String column2Name = "aString";
+        String column3Name = "aBoolean";
+
+        PostDataTablesRequest request = new PostDataTablesRequest();
+        request.setDatatableName(datatableName);
+        request.setApptableName(SAVINGS_TRANSACTION_APP_TABLE_NAME);
+        request.setMultiRow(false);
+
+        PostColumnHeaderData column1HeaderRequestData = new 
PostColumnHeaderData();
+        column1HeaderRequestData.setName(column1Name);
+        column1HeaderRequestData.setType("Number");
+        column1HeaderRequestData.setMandatory(false);
+        column1HeaderRequestData.setLength(10L);
+        column1HeaderRequestData.setCode("");
+        column1HeaderRequestData.setUnique(false);
+        column1HeaderRequestData.setIndexed(false);
+
+        request.addColumnsItem(column1HeaderRequestData);
+
+        PostColumnHeaderData column2HeaderRequestData = new 
PostColumnHeaderData();
+        column2HeaderRequestData.setName(column2Name);
+        column2HeaderRequestData.setType("String");
+        column2HeaderRequestData.setMandatory(false);
+        column2HeaderRequestData.setLength(10L);
+        column2HeaderRequestData.setCode("");
+        column2HeaderRequestData.setUnique(false);
+        column2HeaderRequestData.setIndexed(false);
+
+        request.addColumnsItem(column2HeaderRequestData);
+
+        PostDataTablesResponse response = 
datatableHelper.createDatatable(request);
+        assertNotNull(response.getResourceIdentifier());
+
+        // update datatable
+        PutDataTablesRequest putRequest = new PutDataTablesRequest();
+        putRequest.setApptableName(SAVINGS_TRANSACTION_APP_TABLE_NAME);
+        PutDataTablesRequestAddColumns column3HeaderPutRequestData = new 
PutDataTablesRequestAddColumns();
+        column3HeaderPutRequestData.setName(column3Name);
+        column3HeaderPutRequestData.setType("Boolean");
+        column3HeaderPutRequestData.setMandatory(false);
+
+        putRequest.addAddColumnsItem(column3HeaderPutRequestData);
+
+        PutDataTablesResponse updateResponse = 
datatableHelper.updateDatatable(datatableName, putRequest);
+        assertNotNull(updateResponse.getResourceIdentifier());
+
+        // verify Datatable got created
+        GetDataTablesResponse dataTable = 
datatableHelper.getDataTableDetails(datatableName);
+
+        // verfify columns
+        List<ResultsetColumnHeaderData> columnHeaderData = 
dataTable.getColumnHeaderData();
+        assertNotNull(columnHeaderData);
+
+        // two columns with 1 primary key and 2 audit columns created
+        assertEquals(columnHeaderData.size(), 6);
+
+        // deleting the datatable
+        String deletedDataTableName = 
this.datatableHelper.deleteDatatable(datatableName);
+        assertEquals(datatableName, deletedDataTableName, "ERROR IN DELETING 
THE DATATABLE");
+    }
+
+    @Test
+    public void 
testDatatableCreateReadUpdateDeleteEntryForSavingsAccountTransaction() {
+        // Create Client
+        final Integer clientID = ClientHelper.createClient(this.requestSpec, 
this.responseSpec, startDate);
+        Assertions.assertNotNull(clientID);
+        // Create savings product and account
+        final Integer savingsId = createSavingsAccountDailyPosting(clientID, 
startDate);
+
+        final Integer transactionId = (Integer) 
this.savingsAccountHelper.depositToSavingsAccount(savingsId, "100", 
firstDepositDate,
+                CommonConstants.RESPONSE_RESOURCE_ID);
+
+        assertNotNull(transactionId);
+
+        // create dataTable
+        String datatableName = 
Utils.uniqueRandomStringGenerator("dt_savings_transaction_", 
5).toLowerCase().toLowerCase();
+        String column1Name = "aNumber";
+
+        PostDataTablesRequest request = new PostDataTablesRequest();
+        request.setDatatableName(datatableName);
+        request.setApptableName(SAVINGS_TRANSACTION_APP_TABLE_NAME);
+        request.setMultiRow(true);
+
+        PostColumnHeaderData column1HeaderRequestData = new 
PostColumnHeaderData();
+        column1HeaderRequestData.setName(column1Name);
+        column1HeaderRequestData.setType("Number");
+        column1HeaderRequestData.setMandatory(false);
+        column1HeaderRequestData.setLength(10L);
+        column1HeaderRequestData.setCode("");
+        column1HeaderRequestData.setUnique(false);
+        column1HeaderRequestData.setIndexed(false);
+
+        request.addColumnsItem(column1HeaderRequestData);
+
+        PostDataTablesResponse response = 
datatableHelper.createDatatable(request);
+
+        assertNotNull(response);
+
+        String datatableId = response.getResourceIdentifier();
+
+        // add entries
+        final HashMap<String, Object> datatableEntryMap = new HashMap<>();
+        datatableEntryMap.put(column1Name, Utils.randomNumberGenerator(5));
+        datatableEntryMap.put("locale", "en");
+        datatableEntryMap.put("dateFormat", "yyyy-MM-dd");
+
+        String datatabelEntryRequestJsonString = new 
Gson().toJson(datatableEntryMap);
+
+        final boolean genericResultSet = true;
+
+        HashMap<String, Object> datatableEntryResponseFirst = 
this.datatableHelper.createDatatableEntry(datatableId, transactionId,
+                genericResultSet, datatabelEntryRequestJsonString);
+
+        assertNotNull(datatableEntryResponseFirst.get("resourceId"));
+
+        // Read the Datatable entry generated with genericResultSet
+        HashMap<String, Object> items = 
this.datatableHelper.readDatatableEntry(datatableId, transactionId, 
genericResultSet, null, "");
+        assertNotNull(items);
+        assertEquals(1, ((List) items.get("data")).size());
+
+        // update datatable entry
+        datatableEntryMap.put(column1Name, 100);
+        datatableEntryMap.put("locale", "en");
+        datatableEntryMap.put("dateFormat", "yyyy-MM-dd");
+        datatabelEntryRequestJsonString = new Gson().toJson(datatableEntryMap);
+        HashMap<String, Object> updatedDatatableEntryResponse = 
this.datatableHelper.updateDatatableEntry(datatableName, transactionId,
+                false, datatabelEntryRequestJsonString);
+
+        assertEquals(transactionId, 
updatedDatatableEntryResponse.get("resourceId"));
+
+        // deleting datatable entries
+        Integer appTableId = 
this.datatableHelper.deleteDatatableEntries(datatableName, transactionId, 
"resourceId");
+        assertEquals(transactionId, appTableId, "ERROR IN DELETING THE 
DATATABLE ENTRIES");
+
+        // deleting the datatable
+        String deletedDataTableName = 
this.datatableHelper.deleteDatatable(datatableName);
+        assertEquals(datatableName, deletedDataTableName, "ERROR IN DELETING 
THE DATATABLE");
+    }
+
+    private Integer createSavingsAccountDailyPosting(final Integer clientID, 
final String startDate) {
+        final Integer savingsProductID = createSavingsProductDailyPosting();
+        Assertions.assertNotNull(savingsProductID);
+        final Integer savingsId = 
this.savingsAccountHelper.applyForSavingsApplicationOnDate(clientID, 
savingsProductID,
+                ACCOUNT_TYPE_INDIVIDUAL, startDate);
+        Assertions.assertNotNull(savingsId);
+        HashMap savingsStatusHashMap = 
this.savingsAccountHelper.approveSavingsOnDate(savingsId, startDate);
+        SavingsStatusChecker.verifySavingsIsApproved(savingsStatusHashMap);
+        savingsStatusHashMap = 
this.savingsAccountHelper.activateSavingsAccount(savingsId, startDate);
+        SavingsStatusChecker.verifySavingsIsActive(savingsStatusHashMap);
+        return savingsId;
+    }
+
+    private Integer createSavingsProductDailyPosting() {
+        final String savingsProductJSON = 
this.savingsProductHelper.withInterestCompoundingPeriodTypeAsDaily()
+                
.withInterestPostingPeriodTypeAsDaily().withInterestCalculationPeriodTypeAsDailyBalance().build();
+        return SavingsProductHelper.createSavingsProduct(savingsProductJSON, 
requestSpec, responseSpec);
+    }
+
+    // Reset configuration fields
+    @AfterEach
+    public void tearDown() {
+        
GlobalConfigurationHelper.resetAllDefaultGlobalConfigurations(this.requestSpec, 
this.responseSpec);
+        
GlobalConfigurationHelper.verifyAllDefaultGlobalConfigurations(this.requestSpec,
 this.responseSpec);
+    }
+}


Reply via email to