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

arnold 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 8e4c4d7c5 Rework datatable entry handling + added additional tests
8e4c4d7c5 is described below

commit 8e4c4d7c5c315fb1ab048a7da0ba14aae55f7552
Author: Adam Saghy <[email protected]>
AuthorDate: Mon Aug 15 16:08:02 2022 +0200

    Rework datatable entry handling + added additional tests
---
 .../service/GenericDataServiceImpl.java            |  34 +-
 .../service/ReadWriteNonCoreDataServiceImpl.java   | 129 ++----
 .../integrationtests/DatatableIntegrationTest.java | 487 +++++++++++++++++++++
 .../RepaymentWithPostDatedChecksTest.java          |   2 +-
 .../fineract/integrationtests/common/Utils.java    | 108 ++++-
 .../common/system/DatatableHelper.java             |  69 ++-
 .../common/system/DatatableIntegrationTest.java    | 133 ------
 7 files changed, 709 insertions(+), 253 deletions(-)

diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/service/GenericDataServiceImpl.java
 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/service/GenericDataServiceImpl.java
index 597bca73c..e28bd7701 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/service/GenericDataServiceImpl.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/service/GenericDataServiceImpl.java
@@ -24,7 +24,6 @@ import java.sql.Timestamp;
 import java.time.LocalDate;
 import java.time.LocalDateTime;
 import java.util.ArrayList;
-import java.util.Date;
 import java.util.List;
 import javax.sql.DataSource;
 import lombok.extern.slf4j.Slf4j;
@@ -82,10 +81,18 @@ public class GenericDataServiceImpl implements 
GenericDataService {
                 final List<Object> columnValues = new ArrayList<>();
                 for (int i = 0; i < rsmd.getColumnCount(); i++) {
                     final String columnName = rsmd.getColumnName(i + 1);
-                    final String columnValue = rs.getString(columnName);
-                    columnValues.add(columnValue);
+                    final String colType = 
columnHeaders.get(i).getColumnType();
+                    if ("DATE".equalsIgnoreCase(colType)) {
+                        java.sql.Date tmpDate = (java.sql.Date) 
rs.getObject(columnName);
+                        columnValues.add(tmpDate != null ? 
tmpDate.toLocalDate() : null);
+                    } else if ("TIMESTAMP WITHOUT TIME 
ZONE".equalsIgnoreCase(colType) // PostgreSQL
+                            || "DATETIME".equalsIgnoreCase(colType) || 
"TIMESTAMP".equalsIgnoreCase(colType)) {
+                        Timestamp tmpDate = (Timestamp) 
rs.getObject(columnName);
+                        columnValues.add(tmpDate != null ? 
tmpDate.toLocalDateTime() : null);
+                    } else {
+                        columnValues.add(rs.getObject(columnName));
+                    }
                 }
-
                 final ResultsetRowData resultsetDataRow = 
ResultsetRowData.create(columnValues);
                 resultsetDataRows.add(resultsetDataRow);
             }
@@ -167,19 +174,20 @@ public class GenericDataServiceImpl implements 
GenericDataService {
                 }
                 currVal = row.get(j);
                 if (currVal != null && currColType != null) {
-                    if (currColType.equals("DECIMAL") || 
currColType.equals("INTEGER")) {
+                    if (currColType.equalsIgnoreCase("DECIMAL") || 
currColType.equalsIgnoreCase("INTEGER")
+                            || currColType.equalsIgnoreCase("CODELOOKUP")) {
                         writer.append(currVal);
                     } else {
-                        if (currColType.equals("DATE")) {
-                            final LocalDate localDate = new 
java.sql.Date(((Date) currVal).getTime()).toLocalDate();
+                        if (currColType.equalsIgnoreCase("DATE")) {
+                            final LocalDate localDate = (LocalDate) currVal;
                             writer.append(format("[%d,%d,%d]", 
localDate.getYear(), localDate.getMonthValue(), localDate.getDayOfMonth()));
-                        } else if (currColType.equals("DATETIME")) {
-                            final LocalDateTime ldt = currVal instanceof 
Timestamp ? ((Timestamp) currVal).toLocalDateTime()
-                                    : (LocalDateTime) currVal;
-                            writer.append(format("[%d,%d,%d,%d,%d,%d,%d]", 
ldt.getYear(), ldt.getMonthValue(), ldt.getDayOfMonth(),
-                                    ldt.getHour(), ldt.getMinute(), 
ldt.getSecond(), ldt.getNano()));
+                        } else if (currColType.equalsIgnoreCase("DATETIME")) {
+                            final LocalDateTime localDateTime = 
(LocalDateTime) currVal;
+                            writer.append(format("[%d,%d,%d,%d,%d,%d,%d]", 
localDateTime.getYear(), localDateTime.getMonthValue(),
+                                    localDateTime.getDayOfMonth(), 
localDateTime.getHour(), localDateTime.getMinute(),
+                                    localDateTime.getSecond(), 
localDateTime.getNano()));
                         } else {
-                            writer.append(doubleQuote + replace((String) 
currVal, doubleQuote, slashDoubleQuote) + doubleQuote);
+                            writer.append(doubleQuote + 
replace(String.valueOf(currVal), doubleQuote, slashDoubleQuote) + doubleQuote);
                         }
                     }
                 } 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 5c9ad7fef..8f7d54879 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
@@ -26,10 +26,10 @@ import com.google.gson.JsonObject;
 import com.google.gson.reflect.TypeToken;
 import java.lang.reflect.Type;
 import java.math.BigDecimal;
+import java.sql.Date;
 import java.sql.SQLException;
 import java.sql.Statement;
-import java.time.LocalDate;
-import java.time.LocalDateTime;
+import java.sql.Timestamp;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
@@ -1231,8 +1231,6 @@ public class ReadWriteNonCoreDataServiceImpl implements 
ReadWriteNonCoreDataServ
             LOG.info("Update sql: {}", sql);
             if (StringUtils.isNotBlank(sql)) {
                 this.jdbcTemplate.update(sql);
-                changes.put("locale", dataParams.get("locale"));
-                changes.put("dateFormat", "yyyy-MM-dd");
             } else {
                 LOG.info("No Changes");
             }
@@ -1484,13 +1482,18 @@ public class ReadWriteNonCoreDataServiceImpl implements 
ReadWriteNonCoreDataServ
             for (int i = 0; i < rsmd.getColumnCount(); i++) {
                 final String columnName = rsmd.getColumnName(i + 1);
                 final String colType = 
genericResultsetData.getColTypeOfColumnNamed(columnName);
-                if ("DECIMAL".equalsIgnoreCase(colType)) {
-                    columnValues.add(rowSet.getBigDecimal(columnName));
-                } else if ("DATE".equalsIgnoreCase(colType) || ("TIMESTAMP 
WITHOUT TIME ZONE".equalsIgnoreCase(colType) // PostgreSQL
-                        || "DATETIME".equalsIgnoreCase(colType) || 
"TIMESTAMP".equalsIgnoreCase(colType))) {
-                    columnValues.add(rowSet.getObject(columnName));
+
+                if ("DATE".equalsIgnoreCase(colType)) {
+                    Date tmpDate = (Date) rowSet.getObject(columnName);
+                    columnValues.add(tmpDate != null ? tmpDate.toLocalDate() : 
null);
+                } else if ("TIMESTAMP WITHOUT TIME 
ZONE".equalsIgnoreCase(colType) // PostgreSQL
+                        || "DATETIME".equalsIgnoreCase(colType) || 
"TIMESTAMP".equalsIgnoreCase(colType)) {
+                    Object tmpDate = rowSet.getObject(columnName);
+
+                    columnValues
+                            .add(tmpDate != null ? tmpDate instanceof 
Timestamp ? ((Timestamp) tmpDate).toLocalDateTime() : tmpDate : null);
                 } else {
-                    columnValues.add(rowSet.getString(columnName));
+                    columnValues.add(rowSet.getObject(columnName));
                 }
             }
 
@@ -1506,7 +1509,7 @@ public class ReadWriteNonCoreDataServiceImpl implements 
ReadWriteNonCoreDataServ
 
         String applicationTableName = "";
 
-        final SqlRowSet rowSet = jdbcTemplate.queryForRowSet(sql, new Object[] 
{ datatable }); // NOSONAR
+        final SqlRowSet rowSet = jdbcTemplate.queryForRowSet(sql, datatable); 
// NOSONAR
         if (rowSet.next()) {
             applicationTableName = rowSet.getString("application_table_name");
         } else {
@@ -1523,7 +1526,7 @@ public class ReadWriteNonCoreDataServiceImpl implements 
ReadWriteNonCoreDataServ
     private String getAddSql(final List<ResultsetColumnHeaderData> 
columnHeaders, final String datatable, final String fkName,
             final Long appTableId, final Map<String, String> queryParams) {
 
-        final Map<String, String> affectedColumns = 
getAffectedColumns(columnHeaders, queryParams, fkName);
+        final Map<String, Object> affectedColumns = 
getAffectedColumns(columnHeaders, queryParams, fkName);
 
         String pValueWrite = "";
         String addSql = "";
@@ -1536,7 +1539,7 @@ public class ReadWriteNonCoreDataServiceImpl implements 
ReadWriteNonCoreDataServ
         for (final ResultsetColumnHeaderData pColumnHeader : columnHeaders) {
             final String key = pColumnHeader.getColumnName();
             if (affectedColumns.containsKey(key)) {
-                pValue = affectedColumns.get(key);
+                pValue = String.valueOf(affectedColumns.get(key));
                 if (StringUtils.isEmpty(pValue)) {
                     pValueWrite = "null";
                 } else {
@@ -1589,7 +1592,7 @@ public class ReadWriteNonCoreDataServiceImpl implements 
ReadWriteNonCoreDataServ
     public String getAddSqlWithScore(final List<ResultsetColumnHeaderData> 
columnHeaders, final String datatable, final String fkName,
             final Long appTableId, final Map<String, String> queryParams) {
 
-        final Map<String, String> affectedColumns = 
getAffectedColumns(columnHeaders, queryParams, fkName);
+        final Map<String, Object> affectedColumns = 
getAffectedColumns(columnHeaders, queryParams, fkName);
 
         String pValueWrite = "";
         String scoresId = " ";
@@ -1600,7 +1603,7 @@ public class ReadWriteNonCoreDataServiceImpl implements 
ReadWriteNonCoreDataServ
         String columnName = "";
         String pValue = null;
         for (final String key : affectedColumns.keySet()) {
-            pValue = affectedColumns.get(key);
+            pValue = String.valueOf(affectedColumns.get(key));
 
             if (StringUtils.isEmpty(pValue)) {
                 pValueWrite = "null";
@@ -1655,7 +1658,7 @@ public class ReadWriteNonCoreDataServiceImpl implements 
ReadWriteNonCoreDataServ
                     sql += ", ";
                 }
 
-                pValue = (String) changedColumns.get(key);
+                pValue = String.valueOf(changedColumns.get(key));
                 if (StringUtils.isEmpty(pValue)) {
                     pValueWrite = "null";
                 } else {
@@ -1688,13 +1691,12 @@ public class ReadWriteNonCoreDataServiceImpl implements 
ReadWriteNonCoreDataServ
     private Map<String, Object> getAffectedAndChangedColumns(final 
GenericResultsetData grs, final Map<String, String> queryParams,
             final String fkName) {
 
-        final Map<String, String> affectedColumns = 
getAffectedColumns(grs.getColumnHeaders(), queryParams, fkName);
+        final Map<String, Object> affectedColumns = 
getAffectedColumns(grs.getColumnHeaders(), queryParams, fkName);
         final Map<String, Object> affectedAndChangedColumns = new HashMap<>();
 
         for (final String key : affectedColumns.keySet()) {
-            final String columnValue = affectedColumns.get(key);
-            final String colType = grs.getColTypeOfColumnNamed(key);
-            if (columnChanged(key, columnValue, colType, grs)) {
+            final Object columnValue = affectedColumns.get(key);
+            if (columnChanged(key, columnValue, grs)) {
                 affectedAndChangedColumns.put(key, columnValue);
             }
         }
@@ -1702,27 +1704,24 @@ public class ReadWriteNonCoreDataServiceImpl implements 
ReadWriteNonCoreDataServ
         return affectedAndChangedColumns;
     }
 
-    private boolean columnChanged(final String key, final String keyValue, 
final String colType, final GenericResultsetData grs) {
+    private boolean columnChanged(final String key, final Object value, final 
GenericResultsetData grs) {
 
         final List<Object> columnValues = grs.getData().get(0).getRow();
 
-        String columnValue = null;
+        Object columnValue = null;
         for (int i = 0; i < grs.getColumnHeaders().size(); i++) {
 
             if (key.equals(grs.getColumnHeaders().get(i).getColumnName())) {
-                columnValue = (String) columnValues.get(i);
+                columnValue = columnValues.get(i);
 
-                if (notTheSame(columnValue, keyValue, colType)) {
-                    return true;
-                }
-                return false;
+                return notTheSame(columnValue, value);
             }
         }
 
         throw new 
PlatformDataIntegrityException("error.msg.invalid.columnName", "Parameter 
Column Name: " + key + " not found");
     }
 
-    public Map<String, String> getAffectedColumns(final 
List<ResultsetColumnHeaderData> columnHeaders,
+    public Map<String, Object> getAffectedColumns(final 
List<ResultsetColumnHeaderData> columnHeaders,
             final Map<String, String> queryParams, final String keyFieldName) {
 
         final String dateFormat = queryParams.get("dateFormat");
@@ -1735,11 +1734,12 @@ public class ReadWriteNonCoreDataServiceImpl implements 
ReadWriteNonCoreDataServ
         final String underscore = "_";
         final String space = " ";
         String pValue = null;
+        Object validatedValue = null;
         String queryParamColumnUnderscored;
         String columnHeaderUnderscored;
         boolean notFound;
 
-        final Map<String, String> affectedColumns = new HashMap<>();
+        final Map<String, Object> affectedColumns = new HashMap<>();
         final Set<String> keys = queryParams.keySet();
         for (final String key : keys) {
             // ignores id and foreign key fields
@@ -1756,8 +1756,8 @@ public class ReadWriteNonCoreDataServiceImpl implements 
ReadWriteNonCoreDataServ
                         columnHeaderUnderscored = 
this.genericDataService.replace(columnHeader.getColumnName(), space, 
underscore);
                         if 
(queryParamColumnUnderscored.equalsIgnoreCase(columnHeaderUnderscored)) {
                             pValue = queryParams.get(key);
-                            pValue = validateColumn(columnHeader, pValue, 
dateFormat, clientApplicationLocale);
-                            affectedColumns.put(columnHeader.getColumnName(), 
pValue);
+                            validatedValue = validateColumn(columnHeader, 
pValue, dateFormat, clientApplicationLocale);
+                            affectedColumns.put(columnHeader.getColumnName(), 
validatedValue);
                             notFound = false;
                         }
                     }
@@ -1771,7 +1771,7 @@ public class ReadWriteNonCoreDataServiceImpl implements 
ReadWriteNonCoreDataServ
         return affectedColumns;
     }
 
-    private String validateColumn(final ResultsetColumnHeaderData 
columnHeader, final String pValue, final String dateFormat,
+    private Object validateColumn(final ResultsetColumnHeaderData 
columnHeader, final String pValue, final String dateFormat,
             final Locale clientApplicationLocale) {
 
         String paramValue = pValue;
@@ -1793,7 +1793,6 @@ public class ReadWriteNonCoreDataServiceImpl implements 
ReadWriteNonCoreDataServ
         }
 
         if (StringUtils.isNotEmpty(paramValue)) {
-
             if (columnHeader.hasColumnValues()) {
                 if (columnHeader.isCodeValueDisplayType()) {
 
@@ -1819,7 +1818,7 @@ public class ReadWriteNonCoreDataServiceImpl implements 
ReadWriteNonCoreDataServ
                                 dataValidationErrors);
                     }
 
-                    return paramValue;
+                    return codeLookup;
                 } else {
                     throw new 
PlatformDataIntegrityException("error.msg.invalid.columnType.", "Code: " + 
columnHeader.getColumnName()
                             + " - Invalid Type " + 
columnHeader.getColumnType() + " (neither varchar nor int)");
@@ -1827,35 +1826,13 @@ public class ReadWriteNonCoreDataServiceImpl implements 
ReadWriteNonCoreDataServ
             }
 
             if (columnHeader.isDateDisplayType()) {
-                final LocalDate tmpDate = 
JsonParserHelper.convertFrom(paramValue, columnHeader.getColumnName(), 
dateFormat,
-                        clientApplicationLocale);
-                if (tmpDate == null) {
-                    paramValue = null;
-                } else {
-                    paramValue = tmpDate.toString();
-                }
+                return JsonParserHelper.convertFrom(paramValue, 
columnHeader.getColumnName(), dateFormat, clientApplicationLocale);
             } else if (columnHeader.isDateTimeDisplayType()) {
-                final LocalDateTime tmpDateTime = 
JsonParserHelper.convertDateTimeFrom(paramValue, columnHeader.getColumnName(), 
dateFormat,
-                        clientApplicationLocale);
-                if (tmpDateTime == null) {
-                    paramValue = null;
-                } else {
-                    paramValue = tmpDateTime.toString();
-                }
+                return JsonParserHelper.convertDateTimeFrom(paramValue, 
columnHeader.getColumnName(), dateFormat, clientApplicationLocale);
             } else if (columnHeader.isIntegerDisplayType()) {
-                final Integer tmpInt = 
this.helper.convertToInteger(paramValue, columnHeader.getColumnName(), 
clientApplicationLocale);
-                if (tmpInt == null) {
-                    paramValue = null;
-                } else {
-                    paramValue = tmpInt.toString();
-                }
+                return this.helper.convertToInteger(paramValue, 
columnHeader.getColumnName(), clientApplicationLocale);
             } else if (columnHeader.isDecimalDisplayType()) {
-                final BigDecimal tmpDecimal = 
this.helper.convertFrom(paramValue, columnHeader.getColumnName(), 
clientApplicationLocale);
-                if (tmpDecimal == null) {
-                    paramValue = null;
-                } else {
-                    paramValue = tmpDecimal.toString();
-                }
+                return this.helper.convertFrom(paramValue, 
columnHeader.getColumnName(), clientApplicationLocale);
             } else if (columnHeader.isBooleanDisplayType()) {
 
                 final Boolean tmpBoolean = 
BooleanUtils.toBooleanObject(paramValue);
@@ -1870,7 +1847,7 @@ public class ReadWriteNonCoreDataServiceImpl implements 
ReadWriteNonCoreDataServ
                     throw new 
PlatformApiDataValidationException("validation.msg.validation.errors.exist", 
"Validation errors exist.",
                             dataValidationErrors);
                 }
-                paramValue = tmpBoolean.toString();
+                return tmpBoolean;
             } else if (columnHeader.isString()) {
                 if (columnHeader.getColumnLength() > 0 && paramValue.length() 
> columnHeader.getColumnLength()) {
                     final ApiParameterError error = 
ApiParameterError.parameterError(
@@ -1884,7 +1861,6 @@ public class ReadWriteNonCoreDataServiceImpl implements 
ReadWriteNonCoreDataServ
                 }
             }
         }
-
         return paramValue;
     }
 
@@ -1911,31 +1887,18 @@ public class ReadWriteNonCoreDataServiceImpl implements 
ReadWriteNonCoreDataServ
         return multiRow;
     }
 
-    private boolean notTheSame(final String currValue, final String pValue, 
final String colType) {
-        if (StringUtils.isEmpty(currValue) && StringUtils.isEmpty(pValue)) {
+    private boolean notTheSame(final Object currValue, final Object pValue) {
+        if (currValue == null && pValue == null) {
             return false;
-        }
-
-        if (StringUtils.isEmpty(currValue)) {
-            return true;
-        }
-
-        if (StringUtils.isEmpty(pValue)) {
+        } else if (currValue == null || pValue == null) {
             return true;
         }
-
-        if ("DECIMAL".equalsIgnoreCase(colType)) {
-            final BigDecimal currentDecimal = 
BigDecimal.valueOf(Double.parseDouble(currValue));
-            final BigDecimal newDecimal = 
BigDecimal.valueOf(Double.parseDouble(pValue));
-
-            return currentDecimal.compareTo(newDecimal) != 0;
-        }
-
-        if (currValue.equals(pValue)) {
-            return false;
+        // Equals would fail if the scale is not the same
+        if (currValue instanceof BigDecimal && pValue instanceof BigDecimal) {
+            return !(((BigDecimal) currValue).compareTo((BigDecimal) pValue) 
== 0);
+        } else {
+            return !currValue.equals(pValue);
         }
-
-        return true;
     }
 
     @Override
diff --git 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/DatatableIntegrationTest.java
 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/DatatableIntegrationTest.java
new file mode 100644
index 000000000..e3263b0bd
--- /dev/null
+++ 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/DatatableIntegrationTest.java
@@ -0,0 +1,487 @@
+/**
+ * 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.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.apache.fineract.integrationtests.common.ClientHelper;
+import org.apache.fineract.integrationtests.common.Utils;
+import 
org.apache.fineract.integrationtests.common.loans.LoanApplicationTestBuilder;
+import 
org.apache.fineract.integrationtests.common.loans.LoanProductTestBuilder;
+import org.apache.fineract.integrationtests.common.loans.LoanTransactionHelper;
+import org.apache.fineract.integrationtests.common.system.CodeHelper;
+import org.apache.fineract.integrationtests.common.system.DatatableHelper;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class DatatableIntegrationTest {
+
+    private static final Logger LOG = 
LoggerFactory.getLogger(DatatableIntegrationTest.class);
+
+    private static final String CLIENT_APP_TABLE_NAME = "m_client";
+    private static final String LOAN_APP_TABLE_NAME = "m_loan";
+
+    private static final Float LP_PRINCIPAL = 10000.0f;
+    private static final String LP_REPAYMENTS = "5";
+    private static final String LP_REPAYMENT_PERIOD = "2";
+    private static final String LP_INTEREST_RATE = "1";
+    private static final String EXPECTED_DISBURSAL_DATE = "14 March 2011";
+    private static final String LOAN_APPLICATION_SUBMISSION_DATE = "13 March 
2011";
+    private static final String LOAN_TERM_FREQUENCY = "10";
+    private static final String INDIVIDUAL_LOAN = "individual";
+    public static final String ACCOUNT_TYPE_INDIVIDUAL = "INDIVIDUAL";
+    public static final String MINIMUM_OPENING_BALANCE = "1000.0";
+    public static final String DEPOSIT_AMOUNT = "7000";
+    private RequestSpecification requestSpec;
+    private ResponseSpecification responseSpec;
+    private DatatableHelper datatableHelper;
+
+    private LoanTransactionHelper loanTransactionHelper;
+
+    @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.loanTransactionHelper = new LoanTransactionHelper(requestSpec, 
responseSpec);
+    }
+
+    @Test
+    public void validateCreateReadDeleteDatatable() {
+        // Fetch / Create tst code
+        HashMap<String, Object> codeResponse = 
CodeHelper.getCodeByName(this.requestSpec, this.responseSpec, "TST_TST_TST");
+
+        Integer createdCodeId = (Integer) codeResponse.get("id");
+        Integer createdCodeValueId;
+        Integer createdCodeValueIdSecond;
+        if (createdCodeId == null) {
+            createdCodeId = (Integer) CodeHelper.createCode(this.requestSpec, 
this.responseSpec, "TST_TST_TST", "resourceId");
+
+            createdCodeValueId = CodeHelper.createCodeValue(this.requestSpec, 
this.responseSpec, createdCodeId,
+                    Utils.randomStringGenerator("cv_", 8), 1);
+            createdCodeValueIdSecond = 
CodeHelper.createCodeValue(this.requestSpec, this.responseSpec, createdCodeId,
+                    Utils.randomStringGenerator("cv_", 8), 2);
+        } else {
+            List<HashMap<String, Object>> codeValuesForCode = 
CodeHelper.getCodeValuesForCode(this.requestSpec, this.responseSpec,
+                    createdCodeId, "");
+            createdCodeValueId = (Integer) codeValuesForCode.get(0).get("id");
+            createdCodeValueIdSecond = (Integer) 
codeValuesForCode.get(1).get("id");
+        }
+
+        // creating datatable for client entity
+        final HashMap<String, Object> columnMap = new HashMap<>();
+        final List<HashMap<String, Object>> datatableColumnsList = new 
ArrayList<>();
+        columnMap.put("datatableName", 
Utils.randomNameGenerator(CLIENT_APP_TABLE_NAME + "_", 5));
+        columnMap.put("apptableName", CLIENT_APP_TABLE_NAME);
+        columnMap.put("entitySubType", "PERSON");
+        columnMap.put("multiRow", false);
+        DatatableHelper.addDatatableColumns(datatableColumnsList, 
"itsABoolean", "Boolean", false, null, null);
+        DatatableHelper.addDatatableColumns(datatableColumnsList, "itsADate", 
"Date", true, null, null);
+        DatatableHelper.addDatatableColumns(datatableColumnsList, 
"itsADatetime", "Datetime", true, null, null);
+        DatatableHelper.addDatatableColumns(datatableColumnsList, 
"itsADecimal", "Decimal", true, null, null);
+        DatatableHelper.addDatatableColumns(datatableColumnsList, 
"itsADropdown", "Dropdown", false, null, "TST_TST_TST");
+        DatatableHelper.addDatatableColumns(datatableColumnsList, 
"itsANumber", "Number", true, null, null);
+        DatatableHelper.addDatatableColumns(datatableColumnsList, 
"itsAString", "String", true, 10, null);
+        DatatableHelper.addDatatableColumns(datatableColumnsList, "itsAText", 
"Text", true, null, null);
+        columnMap.put("columns", datatableColumnsList);
+        String datatabelRequestJsonString = new Gson().toJson(columnMap);
+        LOG.info("map : {}", datatabelRequestJsonString);
+
+        HashMap<String, Object> datatableResponse = 
this.datatableHelper.createDatatable(datatabelRequestJsonString, "");
+        String datatableName = (String) 
datatableResponse.get("resourceIdentifier");
+        DatatableHelper.verifyDatatableCreatedOnServer(this.requestSpec, 
this.responseSpec, datatableName);
+
+        // try to create with the same name
+        ResponseSpecification responseSpecError400 = new 
ResponseSpecBuilder().expectStatusCode(400).build();
+        this.datatableHelper = new DatatableHelper(this.requestSpec, 
responseSpecError400);
+        HashMap<String, Object> response = 
this.datatableHelper.createDatatable(datatabelRequestJsonString, "");
+        assertEquals("validation.msg.validation.errors.exist", ((Map) 
response).get("userMessageGlobalisationCode"));
+        this.datatableHelper = new DatatableHelper(this.requestSpec, 
this.responseSpec);
+
+        // creating client with datatables
+        final Integer clientID = 
ClientHelper.createClientAsPerson(requestSpec, responseSpec);
+
+        // creating new client datatable entry
+        final boolean genericResultSet = true;
+
+        final HashMap<String, Object> datatableEntryMap = new HashMap<>();
+        datatableEntryMap.put("itsABoolean", Utils.randomNumberGenerator(1) % 
2 == 0);
+        datatableEntryMap.put("itsADate", 
Utils.randomDateGenerator("yyyy-MM-dd"));
+        datatableEntryMap.put("itsADatetime", 
Utils.randomDateTimeGenerator("yyyy-MM-dd"));
+        datatableEntryMap.put("itsADecimal", Utils.randomDecimalGenerator(4, 
3));
+        datatableEntryMap.put("TST_TST_TST_cd_itsADropdown", 
createdCodeValueId);
+        datatableEntryMap.put("itsANumber", Utils.randomNumberGenerator(5));
+        datatableEntryMap.put("itsAString", Utils.randomStringGenerator("", 
8));
+        datatableEntryMap.put("itsAText", Utils.randomStringGenerator("", 
1000));
+
+        datatableEntryMap.put("locale", "en");
+        datatableEntryMap.put("dateFormat", "yyyy-MM-dd");
+
+        String datatabelEntryRequestJsonString = new 
Gson().toJson(datatableEntryMap);
+        LOG.info("map : {}", datatabelEntryRequestJsonString);
+
+        HashMap<String, Object> datatableEntryResponse = 
this.datatableHelper.createDatatableEntry(datatableName, clientID,
+                genericResultSet, datatabelEntryRequestJsonString);
+        assertNotNull(datatableEntryResponse.get("resourceId"), "ERROR IN 
CREATING THE ENTITY DATATABLE RECORD");
+
+        // Read the Datatable entry generated with genericResultSet in true 
(default)
+        final HashMap<String, Object> items = 
this.datatableHelper.readDatatableEntry(datatableName, clientID, 
genericResultSet,
+                (Integer) datatableEntryResponse.get("resourceId"), "");
+        assertNotNull(items);
+        assertEquals(1, ((List) items.get("data")).size());
+
+        assertEquals("client_id", ((Map) ((List) 
items.get("columnHeaders")).get(0)).get("columnName"));
+        assertEquals(clientID, ((List) ((Map) ((List) 
items.get("data")).get(0)).get("row")).get(0));
+
+        assertEquals("itsABoolean", ((Map) ((List) 
items.get("columnHeaders")).get(1)).get("columnName"));
+        assertEquals(datatableEntryMap.get("itsABoolean"), ((List) ((Map) 
((List) items.get("data")).get(0)).get("row")).get(1));
+
+        assertEquals("itsADate", ((Map) ((List) 
items.get("columnHeaders")).get(2)).get("columnName"));
+        assertEquals(datatableEntryMap.get("itsADate"),
+                Utils.arrayDateToString((List) ((List) ((Map) ((List) 
items.get("data")).get(0)).get("row")).get(2)));
+
+        assertEquals("itsADatetime", ((Map) ((List) 
items.get("columnHeaders")).get(3)).get("columnName"));
+        assertEquals(datatableEntryMap.get("itsADatetime"),
+                Utils.arrayDateTimeToString((List) ((List) ((Map) ((List) 
items.get("data")).get(0)).get("row")).get(3)));
+
+        assertEquals("itsADecimal", ((Map) ((List) 
items.get("columnHeaders")).get(4)).get("columnName"));
+        assertEquals(datatableEntryMap.get("itsADecimal"), ((List) ((Map) 
((List) items.get("data")).get(0)).get("row")).get(4));
+
+        assertEquals("TST_TST_TST_cd_itsADropdown", ((Map) ((List) 
items.get("columnHeaders")).get(5)).get("columnName"));
+        assertEquals(datatableEntryMap.get("TST_TST_TST_cd_itsADropdown"),
+                ((List) ((Map) ((List) 
items.get("data")).get(0)).get("row")).get(5));
+
+        assertEquals("itsANumber", ((Map) ((List) 
items.get("columnHeaders")).get(6)).get("columnName"));
+        assertEquals(datatableEntryMap.get("itsANumber"), ((List) ((Map) 
((List) items.get("data")).get(0)).get("row")).get(6));
+
+        assertEquals("itsAString", ((Map) ((List) 
items.get("columnHeaders")).get(7)).get("columnName"));
+        assertEquals(datatableEntryMap.get("itsAString"), ((List) ((Map) 
((List) items.get("data")).get(0)).get("row")).get(7));
+
+        assertEquals("itsAText", ((Map) ((List) 
items.get("columnHeaders")).get(8)).get("columnName"));
+        assertEquals(datatableEntryMap.get("itsAText"), ((List) ((Map) ((List) 
items.get("data")).get(0)).get("row")).get(8));
+
+        // Read the Datatable entry generated with genericResultSet in false
+        List<HashMap<String, Object>> datatableEntryResponseNoGenericResult = 
this.datatableHelper.readDatatableEntry(datatableName,
+                clientID, !genericResultSet, (Integer) 
datatableEntryResponse.get("resourceId"), "");
+        assertNotNull(datatableEntryResponseNoGenericResult, "ERROR IN GETTING 
THE DATE VALUE FROM DATATABLE RECORD");
+        assertEquals(1, datatableEntryResponseNoGenericResult.size());
+
+        assertEquals(clientID, 
datatableEntryResponseNoGenericResult.get(0).get("client_id"));
+        assertEquals(datatableEntryMap.get("itsABoolean"),
+                Boolean.valueOf((String) 
datatableEntryResponseNoGenericResult.get(0).get("itsABoolean")));
+        assertEquals(datatableEntryMap.get("itsADate"),
+                Utils.arrayDateToString((List) 
datatableEntryResponseNoGenericResult.get(0).get("itsADate")));
+        assertEquals(datatableEntryMap.get("itsADecimal"), 
datatableEntryResponseNoGenericResult.get(0).get("itsADecimal"));
+        assertEquals(datatableEntryMap.get("itsADatetime"),
+                Utils.arrayDateTimeToString((List<Integer>) 
datatableEntryResponseNoGenericResult.get(0).get("itsADatetime")));
+        assertEquals(datatableEntryMap.get("TST_TST_TST_cd_itsADropdown"),
+                
datatableEntryResponseNoGenericResult.get(0).get("TST_TST_TST_cd_itsADropdown"));
+        assertEquals(datatableEntryMap.get("itsANumber"), 
datatableEntryResponseNoGenericResult.get(0).get("itsANumber"));
+        assertEquals(datatableEntryMap.get("itsAString"), 
datatableEntryResponseNoGenericResult.get(0).get("itsAString"));
+        assertEquals(datatableEntryMap.get("itsAText"), 
datatableEntryResponseNoGenericResult.get(0).get("itsAText"));
+
+        // Update datatable entry
+        Boolean previousBoolean = (Boolean) 
datatableEntryMap.get("itsABoolean");
+        datatableEntryMap.put("itsABoolean", !previousBoolean);
+        datatableEntryMap.put("itsADate", 
Utils.randomDateGenerator("yyyy-MM-dd"));
+        datatableEntryMap.put("itsADatetime", 
Utils.randomDateTimeGenerator("yyyy-MM-dd"));
+        datatableEntryMap.put("itsADecimal", Utils.randomDecimalGenerator(4, 
3));
+        datatableEntryMap.put("TST_TST_TST_cd_itsADropdown", null);
+        datatableEntryMap.put("itsANumber", Utils.randomNumberGenerator(5));
+        datatableEntryMap.put("itsAString", Utils.randomStringGenerator("", 
8));
+        datatableEntryMap.put("itsAText", Utils.randomStringGenerator("", 
1000));
+
+        datatableEntryMap.put("locale", "en");
+        datatableEntryMap.put("dateFormat", "yyyy-MM-dd");
+
+        datatabelEntryRequestJsonString = new Gson().toJson(datatableEntryMap);
+        LOG.info("map : {}", datatabelEntryRequestJsonString);
+
+        HashMap<String, Object> updatedDatatableEntryResponse = 
this.datatableHelper.updateDatatableEntry(datatableName, clientID, false,
+                datatabelEntryRequestJsonString);
+
+        assertEquals(clientID, updatedDatatableEntryResponse.get("clientId"));
+
+        assertEquals(datatableEntryMap.get("itsABoolean"), ((Map) 
updatedDatatableEntryResponse.get("changes")).get("itsABoolean"));
+        assertEquals(datatableEntryMap.get("itsADate"),
+                Utils.arrayDateToString((List) ((Map) 
updatedDatatableEntryResponse.get("changes")).get("itsADate")));
+        assertEquals(datatableEntryMap.get("itsADecimal"), ((Map) 
updatedDatatableEntryResponse.get("changes")).get("itsADecimal"));
+        assertEquals(datatableEntryMap.get("itsADatetime"),
+                Utils.arrayDateTimeToString((List<Integer>) ((Map) 
updatedDatatableEntryResponse.get("changes")).get("itsADatetime")));
+        assertEquals(datatableEntryMap.get("TST_TST_TST_cd_itsADropdown"),
+                ((Map) 
updatedDatatableEntryResponse.get("changes")).get("TST_TST_TST_cd_itsADropdown"));
+        assertEquals(datatableEntryMap.get("itsANumber"), ((Map) 
updatedDatatableEntryResponse.get("changes")).get("itsANumber"));
+        assertEquals(datatableEntryMap.get("itsAString"), ((Map) 
updatedDatatableEntryResponse.get("changes")).get("itsAString"));
+        assertEquals(datatableEntryMap.get("itsAText"), ((Map) 
updatedDatatableEntryResponse.get("changes")).get("itsAText"));
+
+        // deleting datatable entries
+        Integer appTableId = 
this.datatableHelper.deleteDatatableEntries(datatableName, clientID, 
"clientId");
+        assertEquals(clientID, appTableId, "ERROR IN DELETING THE DATATABLE 
ENTRIES");
+
+        // deleting the datatable
+        String deletedDataTableName = 
this.datatableHelper.deleteDatatable(datatableName);
+        assertEquals(datatableName, deletedDataTableName, "ERROR IN DELETING 
THE DATATABLE");
+    }
+
+    @Test
+    public void validateReadDatatableMultirow() {
+        // Fetch / Create tst code
+        HashMap<String, Object> codeResponse = 
CodeHelper.getCodeByName(this.requestSpec, this.responseSpec, "TST_TST_TST");
+
+        Integer createdCodeId = (Integer) codeResponse.get("id");
+        Integer createdCodeValueId;
+        Integer createdCodeValueIdSecond;
+        if (createdCodeId == null) {
+            createdCodeId = (Integer) CodeHelper.createCode(this.requestSpec, 
this.responseSpec, "TST_TST_TST", "resourceId");
+
+            createdCodeValueId = CodeHelper.createCodeValue(this.requestSpec, 
this.responseSpec, createdCodeId,
+                    Utils.randomStringGenerator("cv_", 8), 1);
+            createdCodeValueIdSecond = 
CodeHelper.createCodeValue(this.requestSpec, this.responseSpec, createdCodeId,
+                    Utils.randomStringGenerator("cv_", 8), 2);
+        } else {
+            List<HashMap<String, Object>> codeValuesForCode = 
CodeHelper.getCodeValuesForCode(this.requestSpec, this.responseSpec,
+                    createdCodeId, "");
+            createdCodeValueId = (Integer) codeValuesForCode.get(0).get("id");
+            createdCodeValueIdSecond = (Integer) 
codeValuesForCode.get(1).get("id");
+        }
+
+        // creating datatable for client entity
+        final HashMap<String, Object> columnMap = new HashMap<>();
+        final List<HashMap<String, Object>> datatableColumnsList = new 
ArrayList<>();
+        columnMap.put("datatableName", 
Utils.randomNameGenerator(LOAN_APP_TABLE_NAME + "_", 5));
+        columnMap.put("apptableName", LOAN_APP_TABLE_NAME);
+        columnMap.put("entitySubType", "");
+        columnMap.put("multiRow", true);
+        DatatableHelper.addDatatableColumns(datatableColumnsList, 
"itsABoolean", "Boolean", false, null, null);
+        DatatableHelper.addDatatableColumns(datatableColumnsList, "itsADate", 
"Date", true, null, null);
+        DatatableHelper.addDatatableColumns(datatableColumnsList, 
"itsADatetime", "Datetime", true, null, null);
+        DatatableHelper.addDatatableColumns(datatableColumnsList, 
"itsADecimal", "Decimal", true, null, null);
+        DatatableHelper.addDatatableColumns(datatableColumnsList, 
"itsADropdown", "Dropdown", false, null, "TST_TST_TST");
+        DatatableHelper.addDatatableColumns(datatableColumnsList, 
"itsANumber", "Number", true, null, null);
+        DatatableHelper.addDatatableColumns(datatableColumnsList, 
"itsAString", "String", true, 10, null);
+        DatatableHelper.addDatatableColumns(datatableColumnsList, "itsAText", 
"Text", true, null, null);
+        columnMap.put("columns", datatableColumnsList);
+        String datatabelRequestJsonString = new Gson().toJson(columnMap);
+        LOG.info("map : {}", datatabelRequestJsonString);
+
+        HashMap<String, Object> datatableResponse = 
this.datatableHelper.createDatatable(datatabelRequestJsonString, "");
+        String datatableName = (String) 
datatableResponse.get("resourceIdentifier");
+        DatatableHelper.verifyDatatableCreatedOnServer(this.requestSpec, 
this.responseSpec, datatableName);
+
+        // try to create with the same name
+        ResponseSpecification responseSpecError400 = new 
ResponseSpecBuilder().expectStatusCode(400).build();
+        this.datatableHelper = new DatatableHelper(this.requestSpec, 
responseSpecError400);
+        HashMap<String, Object> response = 
this.datatableHelper.createDatatable(datatabelRequestJsonString, "");
+        assertEquals("validation.msg.validation.errors.exist", ((Map) 
response).get("userMessageGlobalisationCode"));
+        this.datatableHelper = new DatatableHelper(this.requestSpec, 
this.responseSpec);
+
+        // creating client with datatables
+        final Integer clientID = 
ClientHelper.createClientAsPerson(requestSpec, responseSpec);
+        final Integer loanProductID = 
createLoanProductWithPeriodicAccrualAccountingEnabled();
+        final Integer loanID = applyForLoanApplication(clientID, 
loanProductID);
+
+        // creating new client datatable entry
+        final boolean genericResultSet = true;
+
+        final HashMap<String, Object> datatableEntryMap = new HashMap<>();
+        datatableEntryMap.put("itsABoolean", Utils.randomNumberGenerator(1) % 
2 == 0);
+        datatableEntryMap.put("itsADate", 
Utils.randomDateGenerator("yyyy-MM-dd"));
+        datatableEntryMap.put("itsADatetime", 
Utils.randomDateTimeGenerator("yyyy-MM-dd"));
+        datatableEntryMap.put("itsADecimal", Utils.randomDecimalGenerator(4, 
3));
+        datatableEntryMap.put("TST_TST_TST_cd_itsADropdown", 
createdCodeValueId);
+        datatableEntryMap.put("itsANumber", Utils.randomNumberGenerator(5));
+        datatableEntryMap.put("itsAString", Utils.randomStringGenerator("", 
8));
+        datatableEntryMap.put("itsAText", Utils.randomStringGenerator("", 
1000));
+
+        datatableEntryMap.put("locale", "en");
+        datatableEntryMap.put("dateFormat", "yyyy-MM-dd");
+
+        String datatabelEntryRequestJsonString = new 
Gson().toJson(datatableEntryMap);
+        LOG.info("map : {}", datatabelEntryRequestJsonString);
+
+        HashMap<String, Object> datatableEntryResponseFirst = 
this.datatableHelper.createDatatableEntry(datatableName, loanID,
+                genericResultSet, datatabelEntryRequestJsonString);
+        HashMap<String, Object> datatableEntryResponseSecond = 
this.datatableHelper.createDatatableEntry(datatableName, loanID,
+                genericResultSet, datatabelEntryRequestJsonString);
+        assertNotNull(datatableEntryResponseFirst.get("resourceId"), "ERROR IN 
CREATING THE ENTITY DATATABLE RECORD");
+        assertNotNull(datatableEntryResponseSecond.get("resourceId"), "ERROR 
IN CREATING THE ENTITY DATATABLE RECORD");
+
+        // Read the Datatable entry generated with genericResultSet in true 
(default)
+        final HashMap<String, Object> items = 
this.datatableHelper.readDatatableEntry(datatableName, loanID, 
genericResultSet, null, "");
+        assertNotNull(items);
+        assertEquals(2, ((List) items.get("data")).size());
+
+        assertEquals("id", ((Map) ((List) 
items.get("columnHeaders")).get(0)).get("columnName"));
+        assertEquals(1, ((List) ((Map) ((List) 
items.get("data")).get(0)).get("row")).get(0));
+        assertEquals("loan_id", ((Map) ((List) 
items.get("columnHeaders")).get(1)).get("columnName"));
+        assertEquals(loanID, ((List) ((Map) ((List) 
items.get("data")).get(0)).get("row")).get(1));
+        assertEquals("itsABoolean", ((Map) ((List) 
items.get("columnHeaders")).get(2)).get("columnName"));
+        assertEquals(datatableEntryMap.get("itsABoolean"), ((List) ((Map) 
((List) items.get("data")).get(0)).get("row")).get(2));
+        assertEquals("itsADate", ((Map) ((List) 
items.get("columnHeaders")).get(3)).get("columnName"));
+        assertEquals(datatableEntryMap.get("itsADate"),
+                Utils.arrayDateToString((List) ((List) ((Map) ((List) 
items.get("data")).get(0)).get("row")).get(3)));
+        assertEquals("itsADatetime", ((Map) ((List) 
items.get("columnHeaders")).get(4)).get("columnName"));
+        assertEquals(datatableEntryMap.get("itsADatetime"),
+                Utils.arrayDateTimeToString((List) ((List) ((Map) ((List) 
items.get("data")).get(0)).get("row")).get(4)));
+        assertEquals("itsADecimal", ((Map) ((List) 
items.get("columnHeaders")).get(5)).get("columnName"));
+        assertEquals(datatableEntryMap.get("itsADecimal"), ((List) ((Map) 
((List) items.get("data")).get(0)).get("row")).get(5));
+        assertEquals("TST_TST_TST_cd_itsADropdown", ((Map) ((List) 
items.get("columnHeaders")).get(6)).get("columnName"));
+        assertEquals(datatableEntryMap.get("TST_TST_TST_cd_itsADropdown"),
+                ((List) ((Map) ((List) 
items.get("data")).get(0)).get("row")).get(6));
+        assertEquals("itsANumber", ((Map) ((List) 
items.get("columnHeaders")).get(7)).get("columnName"));
+        assertEquals(datatableEntryMap.get("itsANumber"), ((List) ((Map) 
((List) items.get("data")).get(0)).get("row")).get(7));
+        assertEquals("itsAString", ((Map) ((List) 
items.get("columnHeaders")).get(8)).get("columnName"));
+        assertEquals(datatableEntryMap.get("itsAString"), ((List) ((Map) 
((List) items.get("data")).get(0)).get("row")).get(8));
+        assertEquals("itsAText", ((Map) ((List) 
items.get("columnHeaders")).get(9)).get("columnName"));
+        assertEquals(datatableEntryMap.get("itsAText"), ((List) ((Map) ((List) 
items.get("data")).get(0)).get("row")).get(9));
+
+        assertEquals(2, ((List) ((Map) ((List) 
items.get("data")).get(1)).get("row")).get(0));
+        assertEquals(loanID, ((List) ((Map) ((List) 
items.get("data")).get(1)).get("row")).get(1));
+        assertEquals(datatableEntryMap.get("itsABoolean"), ((List) ((Map) 
((List) items.get("data")).get(1)).get("row")).get(2));
+        assertEquals(datatableEntryMap.get("itsADate"),
+                Utils.arrayDateToString((List) ((List) ((Map) ((List) 
items.get("data")).get(1)).get("row")).get(3)));
+        assertEquals(datatableEntryMap.get("itsADatetime"),
+                Utils.arrayDateTimeToString((List) ((List) ((Map) ((List) 
items.get("data")).get(1)).get("row")).get(4)));
+        assertEquals(datatableEntryMap.get("itsADecimal"), ((List) ((Map) 
((List) items.get("data")).get(1)).get("row")).get(5));
+        assertEquals(datatableEntryMap.get("TST_TST_TST_cd_itsADropdown"),
+                ((List) ((Map) ((List) 
items.get("data")).get(1)).get("row")).get(6));
+        assertEquals(datatableEntryMap.get("itsANumber"), ((List) ((Map) 
((List) items.get("data")).get(1)).get("row")).get(7));
+        assertEquals(datatableEntryMap.get("itsAString"), ((List) ((Map) 
((List) items.get("data")).get(1)).get("row")).get(8));
+        assertEquals(datatableEntryMap.get("itsAText"), ((List) ((Map) ((List) 
items.get("data")).get(1)).get("row")).get(9));
+
+        // Read the Datatable entry generated with genericResultSet in false
+        List<HashMap<String, Object>> datatableEntryResponseNoGenericResult = 
this.datatableHelper.readDatatableEntry(datatableName, loanID,
+                !genericResultSet, (Integer) 
datatableEntryResponseFirst.get("resourceId"), "");
+        assertNotNull(datatableEntryResponseNoGenericResult, "ERROR IN GETTING 
THE DATE VALUE FROM DATATABLE RECORD");
+        assertEquals(1, datatableEntryResponseNoGenericResult.size());
+
+        assertEquals(loanID, 
datatableEntryResponseNoGenericResult.get(0).get("loan_id"));
+        assertEquals(datatableEntryMap.get("itsABoolean"),
+                Boolean.valueOf((String) 
datatableEntryResponseNoGenericResult.get(0).get("itsABoolean")));
+        assertEquals(datatableEntryMap.get("itsADate"),
+                Utils.arrayDateToString((List) 
datatableEntryResponseNoGenericResult.get(0).get("itsADate")));
+        assertEquals(datatableEntryMap.get("itsADecimal"), 
datatableEntryResponseNoGenericResult.get(0).get("itsADecimal"));
+        assertEquals(datatableEntryMap.get("itsADatetime"),
+                Utils.arrayDateTimeToString((List<Integer>) 
datatableEntryResponseNoGenericResult.get(0).get("itsADatetime")));
+        assertEquals(datatableEntryMap.get("TST_TST_TST_cd_itsADropdown"),
+                
datatableEntryResponseNoGenericResult.get(0).get("TST_TST_TST_cd_itsADropdown"));
+        assertEquals(datatableEntryMap.get("itsANumber"), 
datatableEntryResponseNoGenericResult.get(0).get("itsANumber"));
+        assertEquals(datatableEntryMap.get("itsAString"), 
datatableEntryResponseNoGenericResult.get(0).get("itsAString"));
+        assertEquals(datatableEntryMap.get("itsAText"), 
datatableEntryResponseNoGenericResult.get(0).get("itsAText"));
+
+        // Update datatable entry
+
+        Boolean previousBoolean = (Boolean) 
datatableEntryMap.get("itsABoolean");
+
+        datatableEntryMap.put("itsABoolean", !previousBoolean);
+        datatableEntryMap.put("itsADate", 
Utils.randomDateGenerator("yyyy-MM-dd"));
+        datatableEntryMap.put("itsADatetime", 
Utils.randomDateTimeGenerator("yyyy-MM-dd"));
+        datatableEntryMap.put("itsADecimal", Utils.randomDecimalGenerator(4, 
3));
+        datatableEntryMap.put("TST_TST_TST_cd_itsADropdown", 
createdCodeValueIdSecond);
+        datatableEntryMap.put("itsANumber", Utils.randomNumberGenerator(5));
+        datatableEntryMap.put("itsAString", Utils.randomStringGenerator("", 
8));
+        datatableEntryMap.put("itsAText", Utils.randomStringGenerator("", 
1000));
+
+        datatableEntryMap.put("locale", "en");
+        datatableEntryMap.put("dateFormat", "yyyy-MM-dd");
+
+        datatabelEntryRequestJsonString = new Gson().toJson(datatableEntryMap);
+        LOG.info("map : {}", datatabelEntryRequestJsonString);
+
+        HashMap<String, Object> updatedDatatableEntryResponse = 
this.datatableHelper.updateDatatableEntry(datatableName, loanID, 1, false,
+                datatabelEntryRequestJsonString);
+
+        assertEquals(loanID, updatedDatatableEntryResponse.get("loanId"));
+
+        assertEquals(datatableEntryMap.get("itsABoolean"), ((Map) 
updatedDatatableEntryResponse.get("changes")).get("itsABoolean"));
+        assertEquals(datatableEntryMap.get("itsADate"),
+                Utils.arrayDateToString((List) ((Map) 
updatedDatatableEntryResponse.get("changes")).get("itsADate")));
+        assertEquals(datatableEntryMap.get("itsADecimal"), ((Map) 
updatedDatatableEntryResponse.get("changes")).get("itsADecimal"));
+        assertEquals(datatableEntryMap.get("itsADatetime"),
+                Utils.arrayDateTimeToString((List<Integer>) ((Map) 
updatedDatatableEntryResponse.get("changes")).get("itsADatetime")));
+        assertEquals(datatableEntryMap.get("TST_TST_TST_cd_itsADropdown"),
+                ((Map) 
updatedDatatableEntryResponse.get("changes")).get("TST_TST_TST_cd_itsADropdown"));
+        assertEquals(datatableEntryMap.get("itsANumber"), ((Map) 
updatedDatatableEntryResponse.get("changes")).get("itsANumber"));
+        assertEquals(datatableEntryMap.get("itsAString"), ((Map) 
updatedDatatableEntryResponse.get("changes")).get("itsAString"));
+        assertEquals(datatableEntryMap.get("itsAText"), ((Map) 
updatedDatatableEntryResponse.get("changes")).get("itsAText"));
+
+        updatedDatatableEntryResponse = 
this.datatableHelper.updateDatatableEntry(datatableName, loanID, 2, false,
+                datatabelEntryRequestJsonString);
+
+        assertEquals(loanID, updatedDatatableEntryResponse.get("loanId"));
+
+        assertEquals(datatableEntryMap.get("itsABoolean"), ((Map) 
updatedDatatableEntryResponse.get("changes")).get("itsABoolean"));
+        assertEquals(datatableEntryMap.get("itsADate"),
+                Utils.arrayDateToString((List) ((Map) 
updatedDatatableEntryResponse.get("changes")).get("itsADate")));
+        assertEquals(datatableEntryMap.get("itsADecimal"), ((Map) 
updatedDatatableEntryResponse.get("changes")).get("itsADecimal"));
+        assertEquals(datatableEntryMap.get("itsADatetime"),
+                Utils.arrayDateTimeToString((List<Integer>) ((Map) 
updatedDatatableEntryResponse.get("changes")).get("itsADatetime")));
+        assertEquals(datatableEntryMap.get("TST_TST_TST_cd_itsADropdown"),
+                ((Map) 
updatedDatatableEntryResponse.get("changes")).get("TST_TST_TST_cd_itsADropdown"));
+        assertEquals(datatableEntryMap.get("itsANumber"), ((Map) 
updatedDatatableEntryResponse.get("changes")).get("itsANumber"));
+        assertEquals(datatableEntryMap.get("itsAString"), ((Map) 
updatedDatatableEntryResponse.get("changes")).get("itsAString"));
+        assertEquals(datatableEntryMap.get("itsAText"), ((Map) 
updatedDatatableEntryResponse.get("changes")).get("itsAText"));
+
+        // deleting datatable entries
+        Integer appTableId = 
this.datatableHelper.deleteDatatableEntries(datatableName, loanID, "loanId");
+        assertEquals(loanID, 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 applyForLoanApplication(final Integer clientID, final 
Integer loanProductID) {
+        LOG.info("--------------------------------APPLYING FOR LOAN 
APPLICATION--------------------------------");
+        final String loanApplicationJSON = new 
LoanApplicationTestBuilder().withPrincipal(LP_PRINCIPAL.toString())
+                
.withLoanTermFrequency(LOAN_TERM_FREQUENCY).withLoanTermFrequencyAsMonths().withNumberOfRepayments(LP_REPAYMENTS)
+                
.withRepaymentEveryAfter(LP_REPAYMENT_PERIOD).withRepaymentFrequencyTypeAsMonths()
+                
.withInterestRatePerPeriod(LP_INTEREST_RATE).withInterestTypeAsFlatBalance().withAmortizationTypeAsEqualPrincipalPayments()
+                
.withInterestCalculationPeriodTypeSameAsRepaymentPeriod().withExpectedDisbursementDate(EXPECTED_DISBURSAL_DATE)
+                
.withSubmittedOnDate(LOAN_APPLICATION_SUBMISSION_DATE).withLoanType(INDIVIDUAL_LOAN)
+                .build(clientID.toString(), loanProductID.toString(), null);
+        return this.loanTransactionHelper.getLoanId(loanApplicationJSON);
+    }
+
+    private Integer createLoanProductWithPeriodicAccrualAccountingEnabled() {
+        LOG.info("------------------------------CREATING NEW LOAN PRODUCT 
---------------------------------------");
+        final String loanProductJSON = new 
LoanProductTestBuilder().withPrincipal(LP_PRINCIPAL.toString()).withRepaymentTypeAsMonth()
+                
.withRepaymentAfterEvery(LP_REPAYMENT_PERIOD).withNumberOfRepayments(LP_REPAYMENTS).withRepaymentTypeAsMonth()
+                
.withinterestRatePerPeriod(LP_INTEREST_RATE).withInterestRateFrequencyTypeAsMonths()
+                
.withAmortizationTypeAsEqualPrincipalPayment().withInterestTypeAsFlat().withAccountingRuleAsNone().withDaysInMonth("30")
+                .withDaysInYear("365").build(null);
+        return this.loanTransactionHelper.getLoanProductId(loanProductJSON);
+    }
+
+}
diff --git 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/RepaymentWithPostDatedChecksTest.java
 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/RepaymentWithPostDatedChecksTest.java
index 2b24987a8..3b34dc5bd 100644
--- 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/RepaymentWithPostDatedChecksTest.java
+++ 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/RepaymentWithPostDatedChecksTest.java
@@ -180,7 +180,7 @@ public class RepaymentWithPostDatedChecksTest {
         map.put("name", "AMANA BANK");
         map.put("amount", amount.toString());
         map.put("accountNo", "900400500621");
-        map.put("checkNo", Utils.randomNumberGenerator(12).toString());
+        map.put("checkNo", Utils.randomNumberGenerator(9).toString());
 
         return map;
     }
diff --git 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/Utils.java
 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/Utils.java
index 44ccd7fdf..04d33843c 100644
--- 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/Utils.java
+++ 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/Utils.java
@@ -39,15 +39,18 @@ import java.text.DateFormat;
 import java.text.ParseException;
 import java.text.SimpleDateFormat;
 import java.time.LocalDate;
+import java.time.LocalDateTime;
 import java.time.ZoneId;
 import java.time.format.DateTimeFormatter;
 import java.time.format.DateTimeFormatterBuilder;
+import java.time.temporal.ChronoField;
 import java.util.ArrayList;
 import java.util.Calendar;
 import java.util.Date;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
+import java.util.Random;
 import java.util.TimeZone;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.http.conn.HttpHostConnectException;
@@ -61,27 +64,25 @@ import org.slf4j.LoggerFactory;
 @SuppressWarnings("unchecked")
 public final class Utils {
 
-    private Utils() {
-
-    }
-
-    private static final Logger LOG = LoggerFactory.getLogger(Utils.class);
-    private static final SecureRandom random = new SecureRandom();
-    private static final Gson gson = new Gson();
-
     public static final String TENANT_PARAM_NAME = "tenantIdentifier";
     public static final String DEFAULT_TENANT = "default";
     public static final String TENANT_IDENTIFIER = TENANT_PARAM_NAME + '=' + 
DEFAULT_TENANT;
-
-    public static final String TENANT_TIME_ZONE = "Asia/Kolkata";
-
-    private static final String HEALTH_URL = 
"/fineract-provider/actuator/health";
     private static final String LOGIN_URL = 
"/fineract-provider/api/v1/authentication?" + TENANT_IDENTIFIER;
-
+    public static final String TENANT_TIME_ZONE = "Asia/Kolkata";
     public static final String DATE_FORMAT = "dd MMMM yyyy";
     public static final String DATE_TIME_FORMAT = "dd MMMM yyyy HH:mm";
     public static final DateTimeFormatter dateFormatter = new 
DateTimeFormatterBuilder().appendPattern(DATE_FORMAT).toFormatter();
     public static final DateTimeFormatter dateTimeFormatter = new 
DateTimeFormatterBuilder().appendPattern(DATE_TIME_FORMAT).toFormatter();
+    private static final Logger LOG = LoggerFactory.getLogger(Utils.class);
+    private static final SecureRandom random = new SecureRandom();
+    private static final Gson gson = new Gson();
+    private static final String HEALTH_URL = 
"/fineract-provider/actuator/health";
+
+    private static final Random r = new Random();
+
+    private Utils() {
+
+    }
 
     public static void initializeRESTAssured() {
         RestAssured.baseURI = "https://localhost";;
@@ -270,7 +271,7 @@ public final class Utils {
 
     @SuppressFBWarnings(value = {
             "DMI_RANDOM_USED_ONLY_ONCE" }, justification = "False positive for 
random object created and used only once")
-    public static Long randomNumberGenerator(final int expectedLength) {
+    public static Integer randomNumberGenerator(final int expectedLength) {
         final String source = "1234567890";
         final int lengthOfSource = source.length();
 
@@ -278,7 +279,22 @@ public final class Utils {
         for (int i = 0; i < expectedLength; i++) {
             
stringBuilder.append(source.charAt(random.nextInt(lengthOfSource)));
         }
-        return Long.parseLong(stringBuilder.toString());
+        return Integer.parseInt(stringBuilder.toString());
+    }
+
+    public static Float randomDecimalGenerator(final int expectedWholeLength, 
final int expectedFractionLength) {
+        final String source = "1234567890";
+        final int lengthOfSource = source.length();
+
+        StringBuilder stringBuilder = new StringBuilder(expectedWholeLength + 
expectedFractionLength + 1);
+        for (int i = 0; i < expectedWholeLength; i++) {
+            
stringBuilder.append(source.charAt(random.nextInt(lengthOfSource)));
+        }
+        stringBuilder.append(".");
+        for (int i = 0; i < expectedFractionLength; i++) {
+            
stringBuilder.append(source.charAt(random.nextInt(lengthOfSource)));
+        }
+        return Float.parseFloat(stringBuilder.toString());
     }
 
     public static String convertDateToURLFormat(final Calendar 
dateToBeConvert) {
@@ -343,4 +359,66 @@ public final class Utils {
         return "{}";
     }
 
+    public static String randomDateGenerator(String dateFormat) {
+        DateTimeFormatter dateTimeFormatterBuilder = new 
DateTimeFormatterBuilder().parseCaseInsensitive().parseLenient()
+                .appendPattern(dateFormat).optionalStart().appendPattern(" 
HH:mm:ss").optionalEnd()
+                .parseDefaulting(ChronoField.HOUR_OF_DAY, 
0).parseDefaulting(ChronoField.MINUTE_OF_HOUR, 0)
+                .parseDefaulting(ChronoField.SECOND_OF_MINUTE, 
0).toFormatter();
+        LocalDate localDate = LocalDate.of(getYear(), getMonth(), getDay());
+        return dateTimeFormatterBuilder.format(localDate);
+    }
+
+    public static String randomDateTimeGenerator(String dateFormat) {
+        DateTimeFormatter dateTimeFormatterBuilder = new 
DateTimeFormatterBuilder().parseCaseInsensitive().parseLenient()
+                .appendPattern(dateFormat).optionalStart().appendPattern(" 
HH:mm:ss").optionalEnd()
+                .parseDefaulting(ChronoField.HOUR_OF_DAY, 
0).parseDefaulting(ChronoField.MINUTE_OF_HOUR, 0)
+                .parseDefaulting(ChronoField.SECOND_OF_MINUTE, 
0).toFormatter();
+        LocalDateTime localDate = LocalDateTime.of(getYear(), getMonth(), 
getDay(), getHour(), getMinute(), getSecond());
+        return dateTimeFormatterBuilder.format(localDate);
+    }
+
+    private static int getYear() {
+        return 1000 + r.nextInt(1001);
+    }
+
+    private static int getMonth() {
+        return 10 + r.nextInt(3);
+    }
+
+    private static int getDay() {
+        return 10 + r.nextInt(16);
+    }
+
+    private static int getHour() {
+        return 10 + r.nextInt(14);
+    }
+
+    private static int getMinute() {
+        return 10 + r.nextInt(50);
+    }
+
+    private static int getSecond() {
+        return 10 + r.nextInt(50);
+    }
+
+    public static String arrayDateToString(List intArray) {
+        String[] strArray = (String[]) 
intArray.stream().map(String::valueOf).toArray(String[]::new);
+        return String.join("-", strArray);
+    }
+
+    public static String arrayDateTimeToString(List<Integer> integerList) {
+        StringBuilder stringBuilder = new StringBuilder();
+        for (int i = 0; i < 6; i++) {
+            if (i < 2) {
+                stringBuilder.append(integerList.get(i)).append("-");
+            } else if (i == 2) {
+                stringBuilder.append(integerList.get(i)).append(" ");
+            } else if (i == 3) {
+                stringBuilder.append(integerList.get(i));
+            } else {
+                stringBuilder.append(":").append(integerList.get(i));
+            }
+        }
+        return stringBuilder.toString();
+    }
 }
diff --git 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/system/DatatableHelper.java
 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/system/DatatableHelper.java
index 2adca9d32..d3c426335 100644
--- 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/system/DatatableHelper.java
+++ 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/system/DatatableHelper.java
@@ -29,6 +29,7 @@ import java.util.Calendar;
 import java.util.Date;
 import java.util.HashMap;
 import java.util.List;
+import java.util.Objects;
 import org.apache.fineract.integrationtests.common.Utils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -46,11 +47,34 @@ public class DatatableHelper {
         this.responseSpec = responseSpec;
     }
 
+    public <T> T createDatatable(final String json, final String 
jsonAttributeToGetBack) {
+        return Utils.performServerPost(this.requestSpec, this.responseSpec, 
DATATABLE_URL + "?" + Utils.TENANT_IDENTIFIER, json,
+                jsonAttributeToGetBack);
+    }
+
     public String createDatatable(final String apptableName, final boolean 
multiRow) {
         return Utils.performServerPost(this.requestSpec, this.responseSpec, 
DATATABLE_URL + "?" + Utils.TENANT_IDENTIFIER,
                 getTestDatatableAsJSON(apptableName, multiRow), 
"resourceIdentifier");
     }
 
+    public <T> T createDatatableEntry(final String datatableName, final 
Integer apptableId, final boolean genericResultSet,
+            final String json) {
+        return Utils.performServerPost(this.requestSpec, this.responseSpec, 
DATATABLE_URL + "/" + datatableName + "/" + apptableId
+                + "?genericResultSet=" + genericResultSet + "&" + 
Utils.TENANT_IDENTIFIER, json, "");
+    }
+
+    public <T> T updateDatatableEntry(final String datatableName, final 
Integer apptableId, final boolean genericResultSet,
+            final String json) {
+        return Utils.performServerPut(this.requestSpec, this.responseSpec, 
DATATABLE_URL + "/" + datatableName + "/" + apptableId
+                + "?genericResultSet=" + genericResultSet + "&" + 
Utils.TENANT_IDENTIFIER, json, "");
+    }
+
+    public <T> T updateDatatableEntry(final String datatableName, final 
Integer apptableId, final Integer entryId,
+            final boolean genericResultSet, final String json) {
+        return Utils.performServerPut(this.requestSpec, this.responseSpec, 
DATATABLE_URL + "/" + datatableName + "/" + apptableId + "/"
+                + entryId + "?genericResultSet=" + genericResultSet + "&" + 
Utils.TENANT_IDENTIFIER, json, "");
+    }
+
     public Integer createDatatableEntry(final String apptableName, final 
String datatableName, final Integer apptableId,
             final boolean genericResultSet, final String dateFormat, final 
String jsonAttributeToGetBack) {
         return Utils.performServerPost(
@@ -64,13 +88,13 @@ public class DatatableHelper {
                 + "?genericResultSet=" + String.valueOf(genericResultset) + 
"&" + Utils.TENANT_IDENTIFIER);
     }
 
-    public List<String> readDatatableEntry(final String datatableName, final 
Integer resourceId, final boolean genericResultset,
+    public <T> T readDatatableEntry(final String datatableName, final Integer 
resourceId, final boolean genericResultset,
             final Integer datatableResourceId, final String 
jsonAttributeToGetBack) {
         if (datatableResourceId == null) {
-            return Utils.performServerGetList(this.requestSpec, 
this.responseSpec, DATATABLE_URL + "/" + datatableName + "/" + resourceId
+            return Utils.performServerGet(this.requestSpec, this.responseSpec, 
DATATABLE_URL + "/" + datatableName + "/" + resourceId
                     + "?genericResultSet=" + String.valueOf(genericResultset) 
+ "&" + Utils.TENANT_IDENTIFIER, jsonAttributeToGetBack);
         } else {
-            return Utils.performServerGetList(
+            return Utils.performServerGet(
                     this.requestSpec, this.responseSpec, DATATABLE_URL + "/" + 
datatableName + "/" + resourceId + "/" + datatableResourceId
                             + "?genericResultSet=" + 
String.valueOf(genericResultset) + "&" + Utils.TENANT_IDENTIFIER,
                     jsonAttributeToGetBack);
@@ -104,6 +128,32 @@ public class DatatableHelper {
         assertEquals(generatedDatatableName, responseRegisteredTableName, 
"ERROR IN CREATING THE DATATABLE");
     }
 
+    public static String getTestDatatableAsJSON(final String apptableName, 
final String datatableName, final String codeName,
+            final boolean multiRow) {
+        final HashMap<String, Object> map = new HashMap<>();
+        final List<HashMap<String, Object>> datatableColumnsList = new 
ArrayList<>();
+        map.put("datatableName", Objects.requireNonNullElseGet(datatableName, 
() -> Utils.randomNameGenerator(apptableName + "_", 5)));
+        map.put("apptableName", apptableName);
+        if ("m_client".equalsIgnoreCase(apptableName)) {
+            map.put("entitySubType", "PERSON");
+        } else {
+            map.put("entitySubType", "");
+        }
+        map.put("multiRow", multiRow);
+        addDatatableColumns(datatableColumnsList, "itsABoolean", "Boolean", 
false, null, null);
+        addDatatableColumns(datatableColumnsList, "itsADate", "Date", true, 
null, null);
+        addDatatableColumns(datatableColumnsList, "itsADatetime", "Datetime", 
true, null, null);
+        addDatatableColumns(datatableColumnsList, "itsADecimal", "Decimal", 
true, null, null);
+        addDatatableColumns(datatableColumnsList, "itsADropdown", "Dropdown", 
false, null, codeName);
+        addDatatableColumns(datatableColumnsList, "itsANumber", "Number", 
true, null, null);
+        addDatatableColumns(datatableColumnsList, "itsAString", "String", 
true, 10, null);
+        addDatatableColumns(datatableColumnsList, "itsAText", "Text", true, 
null, null);
+        map.put("columns", datatableColumnsList);
+        String requestJsonString = new Gson().toJson(map);
+        LOG.info("map : {}", requestJsonString);
+        return requestJsonString;
+    }
+
     public static String getTestDatatableAsJSON(final String apptableName, 
final boolean multiRow) {
         final HashMap<String, Object> map = new HashMap<>();
         final List<HashMap<String, Object>> datatableColumnsList = new 
ArrayList<>();
@@ -111,10 +161,10 @@ public class DatatableHelper {
         map.put("apptableName", apptableName);
         map.put("entitySubType", "PERSON");
         map.put("multiRow", multiRow);
-        addDatatableColumns(datatableColumnsList, "Spouse Name", "String", 
true, 25);
-        addDatatableColumns(datatableColumnsList, "Number of Dependents", 
"Number", true, null);
-        addDatatableColumns(datatableColumnsList, "Time of Visit", "DateTime", 
false, null);
-        addDatatableColumns(datatableColumnsList, "Date of Approval", "Date", 
false, null);
+        addDatatableColumns(datatableColumnsList, "Spouse Name", "String", 
true, 25, null);
+        addDatatableColumns(datatableColumnsList, "Number of Dependents", 
"Number", true, null, null);
+        addDatatableColumns(datatableColumnsList, "Time of Visit", "DateTime", 
false, null, null);
+        addDatatableColumns(datatableColumnsList, "Date of Approval", "Date", 
false, null, null);
         map.put("columns", datatableColumnsList);
         String requestJsonString = new Gson().toJson(map);
         LOG.info("map : {}", requestJsonString);
@@ -135,7 +185,7 @@ public class DatatableHelper {
     }
 
     public static List<HashMap<String, Object>> 
addDatatableColumns(List<HashMap<String, Object>> datatableColumnsList, String 
columnName,
-            String columnType, boolean isMandatory, Integer length) {
+            String columnType, boolean isMandatory, Integer length, String 
codeName) {
 
         final HashMap<String, Object> datatableColumnMap = new HashMap<>();
 
@@ -145,6 +195,9 @@ public class DatatableHelper {
         if (length != null) {
             datatableColumnMap.put("length", length);
         }
+        if (codeName != null) {
+            datatableColumnMap.put("code", codeName);
+        }
 
         datatableColumnsList.add(datatableColumnMap);
         return datatableColumnsList;
diff --git 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/system/DatatableIntegrationTest.java
 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/system/DatatableIntegrationTest.java
deleted file mode 100644
index d3a127ae1..000000000
--- 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/system/DatatableIntegrationTest.java
+++ /dev/null
@@ -1,133 +0,0 @@
-/**
- * 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.common.system;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertInstanceOf;
-import static org.junit.jupiter.api.Assertions.assertNotNull;
-
-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.Date;
-import java.util.List;
-import org.apache.fineract.integrationtests.common.ClientHelper;
-import org.apache.fineract.integrationtests.common.Utils;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-
-public class DatatableIntegrationTest {
-
-    private RequestSpecification requestSpec;
-    private ResponseSpecification responseSpec;
-    private DatatableHelper datatableHelper;
-
-    private static final String CLIENT_APP_TABLE_NAME = "m_client";
-
-    @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);
-    }
-
-    @Test
-    public void validateCreateReadDeleteDatatable() {
-        // creating datatable for client entity
-        String datatableName = 
this.datatableHelper.createDatatable(CLIENT_APP_TABLE_NAME, false);
-        DatatableHelper.verifyDatatableCreatedOnServer(this.requestSpec, 
this.responseSpec, datatableName);
-
-        // creating client with datatables
-        final Integer clientID = 
ClientHelper.createClientAsPerson(requestSpec, responseSpec);
-
-        // creating new client datatable entry
-        final boolean genericResultSet = true;
-        Integer datatableResourceID = 
this.datatableHelper.createDatatableEntry(CLIENT_APP_TABLE_NAME, datatableName, 
clientID,
-                genericResultSet, "yyyy-MM-dd", "resourceId");
-        assertNotNull(datatableResourceID, "ERROR IN CREATING THE ENTITY 
DATATABLE RECORD");
-
-        // Read the Datatable entry generated with genericResultSet in true 
(default)
-        final List<String> items = 
this.datatableHelper.readDatatableEntry(datatableName, clientID, 
genericResultSet, null, "data");
-        assertEquals(1, items.size());
-
-        // Read the Datatable entry generated with genericResultSet in false
-        final Date valueDate = 
this.datatableHelper.readDatatableEntry(datatableName, clientID, 
!genericResultSet, 0, "Date of Approval");
-        assertNotNull(valueDate, "ERROR IN GETTING THE DATE VALUE FROM 
DATATABLE RECORD");
-        assertInstanceOf(Date.class, valueDate);
-
-        // deleting datatable entries
-        Integer appTableId = 
this.datatableHelper.deleteDatatableEntries(datatableName, clientID, 
"clientId");
-        assertEquals(clientID, appTableId, "ERROR IN DELETING THE DATATABLE 
ENTRIES");
-
-        // deleting the datatable
-        String deletedDataTableName = 
this.datatableHelper.deleteDatatable(datatableName);
-        assertEquals(datatableName, deletedDataTableName, "ERROR IN DELETING 
THE DATATABLE");
-    }
-
-    @Test
-    public void validateReadDatatableMultirow() {
-        // creating multirow datatable for client entity
-        String datatableName = 
this.datatableHelper.createDatatable(CLIENT_APP_TABLE_NAME, true);
-        DatatableHelper.verifyDatatableCreatedOnServer(this.requestSpec, 
this.responseSpec, datatableName);
-
-        // creating first client with datatables
-        final Integer clientIdA = 
ClientHelper.createClientAsPerson(requestSpec, responseSpec);
-
-        // creating second client with datatables
-        final Integer clientIdB = 
ClientHelper.createClientAsPerson(requestSpec, responseSpec);
-
-        // creating new client datatable entry for first client
-        final boolean genericResultSet = true;
-        final Integer datatableResourceIdA = 
this.datatableHelper.createDatatableEntry(CLIENT_APP_TABLE_NAME, datatableName, 
clientIdA,
-                genericResultSet, "yyyy-MM-dd", "resourceId");
-        assertNotNull(datatableResourceIdA, "ERROR IN CREATING THE ENTITY 
DATATABLE RECORD");
-
-        // creating new client datatable entry for second client
-        final Integer datatableResourceIdB = 
this.datatableHelper.createDatatableEntry(CLIENT_APP_TABLE_NAME, datatableName, 
clientIdB,
-                genericResultSet, "yyyy-MM-dd", "resourceId");
-        assertNotNull(datatableResourceIdB, "ERROR IN CREATING THE ENTITY 
DATATABLE RECORD");
-
-        // Read the Datatable entry generated for first client
-        List<String> items;
-        items = this.datatableHelper.readDatatableEntry(datatableName, 
clientIdA, genericResultSet, datatableResourceIdA, "data");
-        assertEquals(1, items.size());
-
-        // Read the Datatable entry generated for second client
-        items = this.datatableHelper.readDatatableEntry(datatableName, 
clientIdB, genericResultSet, datatableResourceIdB, "data");
-        assertEquals(1, items.size());
-
-        // Read the Datatable entry generated for first client and second 
client's record Id
-        items = this.datatableHelper.readDatatableEntry(datatableName, 
clientIdA, genericResultSet, datatableResourceIdB, "data");
-        assertEquals(0, items.size());
-
-        // deleting datatable entries
-        Integer appTableIdA = 
this.datatableHelper.deleteDatatableEntries(datatableName, clientIdA, 
"clientId");
-        assertEquals(clientIdA, appTableIdA, "ERROR IN DELETING THE DATATABLE 
ENTRIES");
-        Integer appTableIdB = 
this.datatableHelper.deleteDatatableEntries(datatableName, clientIdB, 
"clientId");
-        assertEquals(clientIdB, appTableIdB, "ERROR IN DELETING THE DATATABLE 
ENTRIES");
-
-        // deleting the datatable
-        String deletedDataTableName = 
this.datatableHelper.deleteDatatable(datatableName);
-        assertEquals(datatableName, deletedDataTableName, "ERROR IN DELETING 
THE DATATABLE");
-    }
-}

Reply via email to