You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@fineract.apache.org by ar...@apache.org on 2022/07/13 19:50:47 UTC

[fineract] branch develop updated: Batch API extensions and a couple of OpenAPI fixes + minor datatable fixes

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 8c023e74a Batch API extensions and a couple of OpenAPI fixes + minor datatable fixes
8c023e74a is described below

commit 8c023e74a8f070294f40db7708a9d479554564e6
Author: Arnold Galovics <ga...@gmail.com>
AuthorDate: Wed Jul 13 08:58:40 2022 +0200

    Batch API extensions and a couple of OpenAPI fixes + minor datatable fixes
---
 .../java/org/apache/fineract/client/util/JSON.java |   6 +
 .../api/GLAccountsApiResourceSwagger.java          |   2 +-
 .../batch/command/CommandStrategyProvider.java     |  10 +-
 .../internal/ActivateClientCommandStrategy.java    |  29 +-
 .../command/internal/ApplyLoanCommandStrategy.java |  27 +-
 .../internal/ApplySavingsCommandStrategy.java      |  31 +--
 .../internal/ApproveLoanCommandStrategy.java       |  29 +-
 .../ApproveLoanRescheduleCommandStrategy.java      |  31 +--
 .../internal/CollectChargesCommandStrategy.java    |  31 +--
 .../internal/CreateChargeCommandStrategy.java      |  31 +--
 .../internal/CreateClientCommandStrategy.java      |  29 +-
 ...a => CreateTransactionLoanCommandStrategy.java} |  47 ++--
 .../internal/DisburseLoanCommandStrategy.java      |  29 +-
 ...tDatatableEntryByAppTableIdCommandStrategy.java |  93 +++++++
 .../internal/GetLoanByIdCommandStrategy.java       |  96 +++----
 .../GetTransactionByIdCommandStrategy.java         |  24 +-
 .../internal/UpdateClientCommandStrategy.java      |  29 +-
 .../batch/service/BatchApiServiceImpl.java         |  31 ++-
 .../codes/api/CodeValuesApiResourceSwagger.java    |   2 +
 .../api/GlobalConfigurationApiResource.java        |   3 +-
 .../data/ResultsetColumnHeaderData.java            |   4 +
 .../service/ReadWriteNonCoreDataServiceImpl.java   |   8 +-
 .../api/LoanProductsApiResourceSwagger.java        |  22 +-
 .../batch/command/CommandStrategyProviderTest.java |  20 +-
 .../CreateTransactionLoanCommandStrategyTest.java  | 119 +++++++++
 ...atableEntryByAppTableIdCommandStrategyTest.java | 160 +++++++++++
 .../internal/GetLoanByIdCommandStrategyTest.java   |  60 -----
 .../GetTransactionByIdCommandStrategyTest.java     |  93 -------
 .../fineract/integrationtests/BatchApiTest.java    | 292 +++++++++++++++++++--
 .../integrationtests/common/BatchHelper.java       | 215 +++++++++++++--
 30 files changed, 1079 insertions(+), 524 deletions(-)

diff --git a/fineract-client/src/main/java/org/apache/fineract/client/util/JSON.java b/fineract-client/src/main/java/org/apache/fineract/client/util/JSON.java
index de7611f16..c478a7bb5 100644
--- a/fineract-client/src/main/java/org/apache/fineract/client/util/JSON.java
+++ b/fineract-client/src/main/java/org/apache/fineract/client/util/JSON.java
@@ -120,6 +120,12 @@ public class JSON {
                 case NULL:
                     in.nextNull();
                     return null;
+                case STRING:
+                    String dateString = in.nextString();
+                    if (dateString != null && dateString.length() != 0) {
+                        return LocalDate.parse(dateString);
+                    }
+                    return null;
                 case BEGIN_ARRAY:
                     in.beginArray();
                     int year = in.nextInt();
diff --git a/fineract-provider/src/main/java/org/apache/fineract/accounting/glaccount/api/GLAccountsApiResourceSwagger.java b/fineract-provider/src/main/java/org/apache/fineract/accounting/glaccount/api/GLAccountsApiResourceSwagger.java
index 7a1d54236..34c85fe2e 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/accounting/glaccount/api/GLAccountsApiResourceSwagger.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/accounting/glaccount/api/GLAccountsApiResourceSwagger.java
@@ -129,7 +129,7 @@ final class GLAccountsApiResourceSwagger {
         public Long parentId;
 
         @Schema(example = "1")
-        public EnumOptionData usage;
+        public String usage;
 
         @Schema(example = "Desc")
         public String description;
diff --git a/fineract-provider/src/main/java/org/apache/fineract/batch/command/CommandStrategyProvider.java b/fineract-provider/src/main/java/org/apache/fineract/batch/command/CommandStrategyProvider.java
index d14afd0d7..634450304 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/batch/command/CommandStrategyProvider.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/batch/command/CommandStrategyProvider.java
@@ -92,7 +92,10 @@ public class CommandStrategyProvider {
         this.commandStrategies.put(CommandContext.resource("loans\\/\\d+\\/charges").method("GET").build(),
                 "collectChargesCommandStrategy");
         this.commandStrategies.put(CommandContext.resource("loans\\/\\d+\\/transactions\\?command=repayment").method("POST").build(),
-                "repayLoanCommandStrategy");
+                "createTransactionLoanCommandStrategy");
+        this.commandStrategies.put(
+                CommandContext.resource("loans\\/\\d+\\/transactions\\?command=creditBalanceRefund").method("POST").build(),
+                "createTransactionLoanCommandStrategy");
         this.commandStrategies.put(CommandContext.resource("clients\\/\\d+\\?command=activate").method("POST").build(),
                 "activateClientCommandStrategy");
         this.commandStrategies.put(CommandContext.resource("loans\\/\\d+\\?command=approve").method("POST").build(),
@@ -103,6 +106,11 @@ public class CommandStrategyProvider {
                 "approveLoanRescheduleCommandStrategy");
         this.commandStrategies.put(CommandContext.resource("loans\\/\\d+\\/transactions\\/\\d+").method("GET").build(),
                 "getTransactionByIdCommandStrategy");
+        this.commandStrategies.put(CommandContext.resource("datatables\\/[a-zA-Z0-9_]*\\/\\d+").method("GET").build(),
+                "getDatatableEntryByAppTableIdCommandStrategy");
+        this.commandStrategies.put(
+                CommandContext.resource("datatables\\/[a-zA-Z0-9_]*\\/\\d+(\\?(\\w+(?:\\=[\\w,]+|&)+)+)").method("GET").build(),
+                "getDatatableEntryByAppTableIdCommandStrategy");
     }
 
 }
diff --git a/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/ActivateClientCommandStrategy.java b/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/ActivateClientCommandStrategy.java
index d5601e06f..20ab7e3ea 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/ActivateClientCommandStrategy.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/ActivateClientCommandStrategy.java
@@ -25,8 +25,6 @@ import lombok.RequiredArgsConstructor;
 import org.apache.fineract.batch.command.CommandStrategy;
 import org.apache.fineract.batch.domain.BatchRequest;
 import org.apache.fineract.batch.domain.BatchResponse;
-import org.apache.fineract.batch.exception.ErrorHandler;
-import org.apache.fineract.batch.exception.ErrorInfo;
 import org.apache.fineract.portfolio.client.api.ClientsApiResource;
 import org.springframework.stereotype.Component;
 
@@ -61,27 +59,14 @@ public class ActivateClientCommandStrategy implements CommandStrategy {
         final List<String> pathParameters = Splitter.on('/').splitToList(request.getRelativeUrl());
         Long clientId = Long.parseLong(pathParameters.get(1).substring(0, pathParameters.get(1).indexOf("?")));
 
-        // Try-catch blocks to map exceptions to appropriate status codes
-        try {
+        // Calls 'activate' function from 'ClientsApiResource' to activate a
+        // client
+        responseBody = clientsApiResource.activate(clientId, "activate", request.getBody());
 
-            // Calls 'activate' function from 'ClientsApiResource' to activate a
-            // client
-            responseBody = clientsApiResource.activate(clientId, "activate", request.getBody());
-
-            response.setStatusCode(200);
-            // Sets the body of the response after the successful activation of
-            // the client
-            response.setBody(responseBody);
-
-        } catch (RuntimeException e) {
-
-            // Gets an object of type ErrorInfo, containing information about
-            // raised exception
-            ErrorInfo ex = ErrorHandler.handler(e);
-
-            response.setStatusCode(ex.getStatusCode());
-            response.setBody(ex.getMessage());
-        }
+        response.setStatusCode(200);
+        // Sets the body of the response after the successful activation of
+        // the client
+        response.setBody(responseBody);
 
         return response;
     }
diff --git a/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/ApplyLoanCommandStrategy.java b/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/ApplyLoanCommandStrategy.java
index 0a0d45cea..c6e9639b5 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/ApplyLoanCommandStrategy.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/ApplyLoanCommandStrategy.java
@@ -23,8 +23,6 @@ import lombok.RequiredArgsConstructor;
 import org.apache.fineract.batch.command.CommandStrategy;
 import org.apache.fineract.batch.domain.BatchRequest;
 import org.apache.fineract.batch.domain.BatchResponse;
-import org.apache.fineract.batch.exception.ErrorHandler;
-import org.apache.fineract.batch.exception.ErrorInfo;
 import org.apache.fineract.portfolio.loanaccount.api.LoansApiResource;
 import org.springframework.stereotype.Component;
 
@@ -56,26 +54,13 @@ public class ApplyLoanCommandStrategy implements CommandStrategy {
         response.setRequestId(request.getRequestId());
         response.setHeaders(request.getHeaders());
 
-        // Try-catch blocks to map exceptions to appropriate status codes
-        try {
+        // Calls 'SubmitLoanFunction' function from 'LoansApiResource' to
+        // Apply Loan to an existing client
+        responseBody = loansApiResource.calculateLoanScheduleOrSubmitLoanApplication(null, null, request.getBody());
 
-            // Calls 'SubmitLoanFunction' function from 'LoansApiResource' to
-            // Apply Loan to an existing client
-            responseBody = loansApiResource.calculateLoanScheduleOrSubmitLoanApplication(null, null, request.getBody());
-
-            response.setStatusCode(200);
-            // Sets the body of the response after loan is successfully applied
-            response.setBody(responseBody);
-
-        } catch (RuntimeException e) {
-
-            // Gets an object of type ErrorInfo, containing information about
-            // raised exception
-            ErrorInfo ex = ErrorHandler.handler(e);
-
-            response.setStatusCode(ex.getStatusCode());
-            response.setBody(ex.getMessage());
-        }
+        response.setStatusCode(200);
+        // Sets the body of the response after loan is successfully applied
+        response.setBody(responseBody);
 
         return response;
     }
diff --git a/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/ApplySavingsCommandStrategy.java b/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/ApplySavingsCommandStrategy.java
index 32359c81f..a5c2a914f 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/ApplySavingsCommandStrategy.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/ApplySavingsCommandStrategy.java
@@ -23,8 +23,6 @@ import lombok.RequiredArgsConstructor;
 import org.apache.fineract.batch.command.CommandStrategy;
 import org.apache.fineract.batch.domain.BatchRequest;
 import org.apache.fineract.batch.domain.BatchResponse;
-import org.apache.fineract.batch.exception.ErrorHandler;
-import org.apache.fineract.batch.exception.ErrorInfo;
 import org.apache.fineract.portfolio.savings.api.SavingsAccountsApiResource;
 import org.springframework.stereotype.Component;
 
@@ -56,28 +54,15 @@ public class ApplySavingsCommandStrategy implements CommandStrategy {
         response.setRequestId(request.getRequestId());
         response.setHeaders(request.getHeaders());
 
-        // Try-catch blocks to map exceptions to appropriate status codes
-        try {
+        // Calls 'submitApplication' function from
+        // 'SavingsAccountsApiResource' to Apply Savings to an existing
+        // client
+        responseBody = savingsAccountsApiResource.submitApplication(request.getBody());
 
-            // Calls 'submitApplication' function from
-            // 'SavingsAccountsApiResource' to Apply Savings to an existing
-            // client
-            responseBody = savingsAccountsApiResource.submitApplication(request.getBody());
-
-            response.setStatusCode(200);
-            // Sets the body of the response after savings is successfully
-            // applied
-            response.setBody(responseBody);
-
-        } catch (RuntimeException e) {
-
-            // Gets an object of type ErrorInfo, containing information about
-            // raised exception
-            ErrorInfo ex = ErrorHandler.handler(e);
-
-            response.setStatusCode(ex.getStatusCode());
-            response.setBody(ex.getMessage());
-        }
+        response.setStatusCode(200);
+        // Sets the body of the response after savings is successfully
+        // applied
+        response.setBody(responseBody);
 
         return response;
     }
diff --git a/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/ApproveLoanCommandStrategy.java b/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/ApproveLoanCommandStrategy.java
index 26dac2941..c517e4119 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/ApproveLoanCommandStrategy.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/ApproveLoanCommandStrategy.java
@@ -25,8 +25,6 @@ import lombok.RequiredArgsConstructor;
 import org.apache.fineract.batch.command.CommandStrategy;
 import org.apache.fineract.batch.domain.BatchRequest;
 import org.apache.fineract.batch.domain.BatchResponse;
-import org.apache.fineract.batch.exception.ErrorHandler;
-import org.apache.fineract.batch.exception.ErrorInfo;
 import org.apache.fineract.portfolio.loanaccount.api.LoansApiResource;
 import org.springframework.stereotype.Component;
 
@@ -61,27 +59,14 @@ public class ApproveLoanCommandStrategy implements CommandStrategy {
         final List<String> pathParameters = Splitter.on('/').splitToList(request.getRelativeUrl());
         Long loanId = Long.parseLong(pathParameters.get(1).substring(0, pathParameters.get(1).indexOf("?")));
 
-        // Try-catch blocks to map exceptions to appropriate status codes
-        try {
+        // Calls 'approve' function from 'LoansApiResource' to approve a
+        // loan
+        responseBody = loansApiResource.stateTransitions(loanId, "approve", request.getBody());
 
-            // Calls 'approve' function from 'LoansApiResource' to approve a
-            // loan
-            responseBody = loansApiResource.stateTransitions(loanId, "approve", request.getBody());
-
-            response.setStatusCode(200);
-            // Sets the body of the response after the successful approval of a
-            // loan
-            response.setBody(responseBody);
-
-        } catch (RuntimeException e) {
-
-            // Gets an object of type ErrorInfo, containing information about
-            // raised exception
-            ErrorInfo ex = ErrorHandler.handler(e);
-
-            response.setStatusCode(ex.getStatusCode());
-            response.setBody(ex.getMessage());
-        }
+        response.setStatusCode(200);
+        // Sets the body of the response after the successful approval of a
+        // loan
+        response.setBody(responseBody);
 
         return response;
     }
diff --git a/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/ApproveLoanRescheduleCommandStrategy.java b/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/ApproveLoanRescheduleCommandStrategy.java
index 4220f9c13..15748e7da 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/ApproveLoanRescheduleCommandStrategy.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/ApproveLoanRescheduleCommandStrategy.java
@@ -25,8 +25,6 @@ import lombok.RequiredArgsConstructor;
 import org.apache.fineract.batch.command.CommandStrategy;
 import org.apache.fineract.batch.domain.BatchRequest;
 import org.apache.fineract.batch.domain.BatchResponse;
-import org.apache.fineract.batch.exception.ErrorHandler;
-import org.apache.fineract.batch.exception.ErrorInfo;
 import org.apache.fineract.portfolio.loanaccount.rescheduleloan.api.RescheduleLoansApiResource;
 import org.springframework.stereotype.Component;
 
@@ -47,28 +45,15 @@ public class ApproveLoanRescheduleCommandStrategy implements CommandStrategy {
         final List<String> pathParameters = Splitter.on('/').splitToList(request.getRelativeUrl());
         Long scheduleId = Long.parseLong(pathParameters.get(1).substring(0, pathParameters.get(1).indexOf("?")));
 
-        // Try-catch blocks to map exceptions to appropriate status codes
-        try {
+        // Calls 'approve' function from 'Loans reschedule Request' to
+        // approve a
+        // loan
+        responseBody = rescheduleLoansApiResource.updateLoanRescheduleRequest(scheduleId, "approve", request.getBody());
 
-            // Calls 'approve' function from 'Loans reschedule Request' to
-            // approve a
-            // loan
-            responseBody = rescheduleLoansApiResource.updateLoanRescheduleRequest(scheduleId, "approve", request.getBody());
-
-            response.setStatusCode(200);
-            // Sets the body of the response after the successful approval of a
-            // Loans reschedule Request
-            response.setBody(responseBody);
-
-        } catch (RuntimeException e) {
-
-            // Gets an object of type ErrorInfo, containing information about
-            // raised exception
-            ErrorInfo ex = ErrorHandler.handler(e);
-
-            response.setStatusCode(ex.getStatusCode());
-            response.setBody(ex.getMessage());
-        }
+        response.setStatusCode(200);
+        // Sets the body of the response after the successful approval of a
+        // Loans reschedule Request
+        response.setBody(responseBody);
 
         return response;
     }
diff --git a/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/CollectChargesCommandStrategy.java b/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/CollectChargesCommandStrategy.java
index 9d516cb7d..593ed0c69 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/CollectChargesCommandStrategy.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/CollectChargesCommandStrategy.java
@@ -25,8 +25,6 @@ import lombok.RequiredArgsConstructor;
 import org.apache.fineract.batch.command.CommandStrategy;
 import org.apache.fineract.batch.domain.BatchRequest;
 import org.apache.fineract.batch.domain.BatchResponse;
-import org.apache.fineract.batch.exception.ErrorHandler;
-import org.apache.fineract.batch.exception.ErrorInfo;
 import org.apache.fineract.portfolio.loanaccount.api.LoanChargesApiResource;
 import org.springframework.stereotype.Component;
 
@@ -63,28 +61,15 @@ public class CollectChargesCommandStrategy implements CommandStrategy {
         // Pluck out the loanId out of the relative path
         Long loanId = Long.parseLong(pathParameters.get(1));
 
-        // Try-catch blocks to map exceptions to appropriate status codes
-        try {
+        // Calls 'retrieveAllLoanCharges' function from
+        // 'LoanChargesApiResource' to Collect
+        // Charges for a loan
+        responseBody = loanChargesApiResource.retrieveAllLoanCharges(loanId, uriInfo);
 
-            // Calls 'retrieveAllLoanCharges' function from
-            // 'LoanChargesApiResource' to Collect
-            // Charges for a loan
-            responseBody = loanChargesApiResource.retrieveAllLoanCharges(loanId, uriInfo);
-
-            response.setStatusCode(200);
-            // Sets the body of the response after Charges have been
-            // successfully collected
-            response.setBody(responseBody);
-
-        } catch (RuntimeException e) {
-
-            // Gets an object of type ErrorInfo, containing information about
-            // raised exception
-            ErrorInfo ex = ErrorHandler.handler(e);
-
-            response.setStatusCode(ex.getStatusCode());
-            response.setBody(ex.getMessage());
-        }
+        response.setStatusCode(200);
+        // Sets the body of the response after Charges have been
+        // successfully collected
+        response.setBody(responseBody);
 
         return response;
     }
diff --git a/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/CreateChargeCommandStrategy.java b/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/CreateChargeCommandStrategy.java
index 0edc2b9a8..7e7be136e 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/CreateChargeCommandStrategy.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/CreateChargeCommandStrategy.java
@@ -25,8 +25,6 @@ import lombok.RequiredArgsConstructor;
 import org.apache.fineract.batch.command.CommandStrategy;
 import org.apache.fineract.batch.domain.BatchRequest;
 import org.apache.fineract.batch.domain.BatchResponse;
-import org.apache.fineract.batch.exception.ErrorHandler;
-import org.apache.fineract.batch.exception.ErrorInfo;
 import org.apache.fineract.portfolio.loanaccount.api.LoanChargesApiResource;
 import org.springframework.stereotype.Component;
 
@@ -61,28 +59,15 @@ public class CreateChargeCommandStrategy implements CommandStrategy {
         final List<String> pathParameters = Splitter.on('/').splitToList(request.getRelativeUrl());
         Long loanId = Long.parseLong(pathParameters.get(1));
 
-        // Try-catch blocks to map exceptions to appropriate status codes
-        try {
+        // Calls 'executeLoanCharge' function from 'LoanChargesApiResource'
+        // to create
+        // a new charge for a loan
+        responseBody = loanChargesApiResource.executeLoanCharge(loanId, null, request.getBody());
 
-            // Calls 'executeLoanCharge' function from 'LoanChargesApiResource'
-            // to create
-            // a new charge for a loan
-            responseBody = loanChargesApiResource.executeLoanCharge(loanId, null, request.getBody());
-
-            response.setStatusCode(200);
-            // Sets the body of the response after Charge has been successfully
-            // created
-            response.setBody(responseBody);
-
-        } catch (RuntimeException e) {
-
-            // Gets an object of type ErrorInfo, containing information about
-            // raised exception
-            ErrorInfo ex = ErrorHandler.handler(e);
-
-            response.setStatusCode(ex.getStatusCode());
-            response.setBody(ex.getMessage());
-        }
+        response.setStatusCode(200);
+        // Sets the body of the response after Charge has been successfully
+        // created
+        response.setBody(responseBody);
 
         return response;
     }
diff --git a/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/CreateClientCommandStrategy.java b/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/CreateClientCommandStrategy.java
index 3020a2f20..fc0d71757 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/CreateClientCommandStrategy.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/CreateClientCommandStrategy.java
@@ -23,8 +23,6 @@ import lombok.RequiredArgsConstructor;
 import org.apache.fineract.batch.command.CommandStrategy;
 import org.apache.fineract.batch.domain.BatchRequest;
 import org.apache.fineract.batch.domain.BatchResponse;
-import org.apache.fineract.batch.exception.ErrorHandler;
-import org.apache.fineract.batch.exception.ErrorInfo;
 import org.apache.fineract.portfolio.client.api.ClientsApiResource;
 import org.springframework.stereotype.Component;
 
@@ -56,27 +54,14 @@ public class CreateClientCommandStrategy implements CommandStrategy {
         response.setRequestId(request.getRequestId());
         response.setHeaders(request.getHeaders());
 
-        // Try-catch blocks to map exceptions to appropriate status codes
-        try {
+        // Calls 'create' function from 'ClientsApiResource' to create a new
+        // client
+        responseBody = clientsApiResource.create(request.getBody());
 
-            // Calls 'create' function from 'ClientsApiResource' to create a new
-            // client
-            responseBody = clientsApiResource.create(request.getBody());
-
-            response.setStatusCode(200);
-            // Sets the body of the response after the successful creation of
-            // the client
-            response.setBody(responseBody);
-
-        } catch (RuntimeException e) {
-
-            // Gets an object of type ErrorInfo, containing information about
-            // raised exception
-            ErrorInfo ex = ErrorHandler.handler(e);
-
-            response.setStatusCode(ex.getStatusCode());
-            response.setBody(ex.getMessage());
-        }
+        response.setStatusCode(200);
+        // Sets the body of the response after the successful creation of
+        // the client
+        response.setBody(responseBody);
 
         return response;
     }
diff --git a/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/RepayLoanCommandStrategy.java b/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/CreateTransactionLoanCommandStrategy.java
similarity index 56%
rename from fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/RepayLoanCommandStrategy.java
rename to fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/CreateTransactionLoanCommandStrategy.java
index c91269cf7..a192b2624 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/RepayLoanCommandStrategy.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/CreateTransactionLoanCommandStrategy.java
@@ -20,20 +20,22 @@ package org.apache.fineract.batch.command.internal;
 
 import com.google.common.base.Splitter;
 import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 import javax.ws.rs.core.UriInfo;
 import lombok.RequiredArgsConstructor;
 import org.apache.fineract.batch.command.CommandStrategy;
 import org.apache.fineract.batch.domain.BatchRequest;
 import org.apache.fineract.batch.domain.BatchResponse;
-import org.apache.fineract.batch.exception.ErrorHandler;
-import org.apache.fineract.batch.exception.ErrorInfo;
 import org.apache.fineract.portfolio.loanaccount.api.LoanTransactionsApiResource;
+import org.apache.http.HttpStatus;
 import org.springframework.stereotype.Component;
 
 /**
- * Implements {@link CommandStrategy} and handles repayment for a Loan. It passes the contents of the body from the
- * BatchRequest to {@link LoanTransactionsApiResource} and gets back the response. This class will also catch any errors
- * raised by {@link LoanTransactionsApiResource} and map those errors to appropriate status codes in BatchResponse.
+ * Implements {@link CommandStrategy} and handles the creation of a transaction for a Loan. It passes the contents of
+ * the body from the BatchRequest to {@link LoanTransactionsApiResource} and gets back the response. This class will
+ * also catch any errors raised by {@link LoanTransactionsApiResource} and map those errors to appropriate status codes
+ * in BatchResponse.
  *
  * @author Mohit Sinha
  *
@@ -43,7 +45,7 @@ import org.springframework.stereotype.Component;
  */
 @Component
 @RequiredArgsConstructor
-public class RepayLoanCommandStrategy implements CommandStrategy {
+public class CreateTransactionLoanCommandStrategy implements CommandStrategy {
 
     private final LoanTransactionsApiResource loanTransactionsApiResource;
 
@@ -59,25 +61,26 @@ public class RepayLoanCommandStrategy implements CommandStrategy {
         final List<String> pathParameters = Splitter.on('/').splitToList(request.getRelativeUrl());
         Long loanId = Long.parseLong(pathParameters.get(1));
 
-        // Try-catch blocks to map exceptions to appropriate status codes
-        try {
+        Pattern commandPattern = Pattern.compile("^?command=[a-zA-Z]+");
+        Matcher commandMatcher = commandPattern.matcher(pathParameters.get(2));
 
-            responseBody = loanTransactionsApiResource.executeLoanTransaction(loanId, "repayment", request.getBody());
-
-            response.setStatusCode(200);
-            // Sets the body of the response after Charge has been successfully
-            // created
-            response.setBody(responseBody);
-
-        } catch (RuntimeException e) {
+        if (!commandMatcher.find()) {
+            // This would only occur if the CommandStrategyProvider is incorrectly configured.
+            response.setRequestId(request.getRequestId());
+            response.setStatusCode(HttpStatus.SC_NOT_IMPLEMENTED);
+            response.setBody(
+                    "Resource with method " + request.getMethod() + " and relativeUrl " + request.getRelativeUrl() + " doesn't exist");
+            return response;
+        }
+        String commandQueryParam = commandMatcher.group(0);
+        String command = commandQueryParam.substring(commandQueryParam.indexOf("=") + 1);
 
-            // Gets an object of type ErrorInfo, containing information about
-            // raised exception
-            ErrorInfo ex = ErrorHandler.handler(e);
+        responseBody = loanTransactionsApiResource.executeLoanTransaction(loanId, command, request.getBody());
 
-            response.setStatusCode(ex.getStatusCode());
-            response.setBody(ex.getMessage());
-        }
+        response.setStatusCode(200);
+        // Sets the body of the response after Charge has been successfully
+        // created
+        response.setBody(responseBody);
 
         return response;
     }
diff --git a/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/DisburseLoanCommandStrategy.java b/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/DisburseLoanCommandStrategy.java
index e4244be90..cffd07bd2 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/DisburseLoanCommandStrategy.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/DisburseLoanCommandStrategy.java
@@ -25,8 +25,6 @@ import lombok.RequiredArgsConstructor;
 import org.apache.fineract.batch.command.CommandStrategy;
 import org.apache.fineract.batch.domain.BatchRequest;
 import org.apache.fineract.batch.domain.BatchResponse;
-import org.apache.fineract.batch.exception.ErrorHandler;
-import org.apache.fineract.batch.exception.ErrorInfo;
 import org.apache.fineract.portfolio.loanaccount.api.LoansApiResource;
 import org.springframework.stereotype.Component;
 
@@ -61,28 +59,15 @@ public class DisburseLoanCommandStrategy implements CommandStrategy {
         final List<String> pathParameters = Splitter.on('/').splitToList(request.getRelativeUrl());
         Long loanId = Long.parseLong(pathParameters.get(1).substring(0, pathParameters.get(1).indexOf("?")));
 
-        // Try-catch blocks to map exceptions to appropriate status codes
-        try {
+        // Calls 'disburse' function from 'LoansApiResource' to disburse a
+        // loan
+        responseBody = loansApiResource.stateTransitions(loanId, "disburse", request.getBody());
 
-            // Calls 'disburse' function from 'LoansApiResource' to disburse a
-            // loan
-            responseBody = loansApiResource.stateTransitions(loanId, "disburse", request.getBody());
+        response.setStatusCode(200);
 
-            response.setStatusCode(200);
-
-            // Sets the body of the response after the successful disbursal of
-            // the loan
-            response.setBody(responseBody);
-
-        } catch (RuntimeException e) {
-
-            // Gets an object of type ErrorInfo, containing information about
-            // raised exception
-            ErrorInfo ex = ErrorHandler.handler(e);
-
-            response.setStatusCode(ex.getStatusCode());
-            response.setBody(ex.getMessage());
-        }
+        // Sets the body of the response after the successful disbursal of
+        // the loan
+        response.setBody(responseBody);
 
         return response;
     }
diff --git a/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/GetDatatableEntryByAppTableIdCommandStrategy.java b/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/GetDatatableEntryByAppTableIdCommandStrategy.java
new file mode 100644
index 000000000..58c8e5b5c
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/GetDatatableEntryByAppTableIdCommandStrategy.java
@@ -0,0 +1,93 @@
+/**
+ * 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.batch.command.internal;
+
+import java.util.Map;
+import javax.ws.rs.core.UriInfo;
+import lombok.RequiredArgsConstructor;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.fineract.batch.command.CommandStrategy;
+import org.apache.fineract.batch.command.CommandStrategyUtils;
+import org.apache.fineract.batch.domain.BatchRequest;
+import org.apache.fineract.batch.domain.BatchResponse;
+import org.apache.fineract.infrastructure.core.api.MutableUriInfo;
+import org.apache.fineract.infrastructure.dataqueries.api.DatatablesApiResource;
+import org.apache.http.HttpStatus;
+import org.springframework.stereotype.Component;
+
+/**
+ * Implements {@link CommandStrategy} and get datatable by appTableId. It passes the contents of the body from the
+ * BatchRequest to {@link DatatablesApiResource} and gets back the response. This class will also catch any errors
+ * raised by {@link DatatablesApiResource} and map those errors to appropriate status codes in BatchResponse.
+ */
+@Component
+@RequiredArgsConstructor
+public class GetDatatableEntryByAppTableIdCommandStrategy implements CommandStrategy {
+
+    /**
+     * Data table api resource {@link DatatablesApiResource}.
+     */
+    private final DatatablesApiResource dataTablesApiResource;
+
+    @Override
+    public BatchResponse execute(final BatchRequest request, @SuppressWarnings("unused") UriInfo uriInfo) {
+        final MutableUriInfo parameterizedUriInfo = new MutableUriInfo(uriInfo);
+        final BatchResponse response = new BatchResponse();
+        final String responseBody;
+
+        response.setRequestId(request.getRequestId());
+        response.setHeaders(request.getHeaders());
+
+        final String relativeUrl = request.getRelativeUrl();
+        final String relativeUrlSubString = StringUtils.substringAfter(relativeUrl, "/");
+
+        // uriInfo will contain the query parameter value(s) that are sent in the actual batch uri.
+        // for example: batches?enclosingTransaction=true
+        // But the query parameters that are sent in the batch relative url has to be sent to
+        // datatablesApiResource.getDatatable
+        // To use the relative url query parameters
+        // - Parse and fetch the query parameters sent in the relative url
+        // (datatables/dt_123/1?genericResultSet=true)
+        // - Add them to the MutableUriInfo query parameters list
+        // - Call datatablesApiResource.getDatatable(dataTable, appTableId, null, uriInfo)
+        long appTableId;
+        if (relativeUrl.indexOf('?') > 0) {
+            appTableId = Long.parseLong(StringUtils.substringBetween(relativeUrlSubString, "/", "?"));
+            Map<String, String> queryParameters = CommandStrategyUtils.getQueryParameters(relativeUrl);
+
+            // Add the query parameters sent in the relative URL to MutableUriInfo
+            CommandStrategyUtils.addQueryParametersToUriInfo(parameterizedUriInfo, queryParameters);
+        } else {
+            appTableId = Long.parseLong(StringUtils.substringAfter(relativeUrlSubString, "/"));
+        }
+
+        String dataTableName = relativeUrlSubString.substring(0, relativeUrlSubString.indexOf("/"));
+
+        // Calls 'getDatatable' function from 'DatatablesApiResource' to
+        // get the datatable details based on the appTableId
+        responseBody = dataTablesApiResource.getDatatable(dataTableName, appTableId, null, parameterizedUriInfo);
+
+        response.setStatusCode(HttpStatus.SC_OK);
+
+        // Sets the response after retrieving the datatable
+        response.setBody(responseBody);
+
+        return response;
+    }
+}
diff --git a/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/GetLoanByIdCommandStrategy.java b/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/GetLoanByIdCommandStrategy.java
index 66781ec3a..15cea55f0 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/GetLoanByIdCommandStrategy.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/GetLoanByIdCommandStrategy.java
@@ -26,8 +26,6 @@ import org.apache.commons.lang3.StringUtils;
 import org.apache.fineract.batch.command.CommandStrategy;
 import org.apache.fineract.batch.domain.BatchRequest;
 import org.apache.fineract.batch.domain.BatchResponse;
-import org.apache.fineract.batch.exception.ErrorHandler;
-import org.apache.fineract.batch.exception.ErrorInfo;
 import org.apache.fineract.infrastructure.core.api.MutableUriInfo;
 import org.apache.fineract.portfolio.loanaccount.api.LoansApiResource;
 import org.apache.http.HttpStatus;
@@ -60,64 +58,52 @@ public class GetLoanByIdCommandStrategy implements CommandStrategy {
         final String relativeUrl = request.getRelativeUrl();
         Long loanId;
 
-        // Try-catch blocks to map exceptions to appropriate status codes
-        try {
-            // uriInfo will contain the query parameter value(s) that are sent in the actual batch uri.
-            // for example: batches?enclosingTransaction=true
-            // But the query parameters that are sent in the batch relative url has to be sent to
-            // LoansApiResource.retrieveLoan
-            // To use the relative url query parameters
-            // - Parse and fetch the query parameters sent in the relative url
-            // (loans/66?fields=id,principal,annualInterestRate)
-            // - Add them to the UriInfo query parameters list
-            // - Call loansApiResource.retrieveLoan(loanId, false, uriInfo)
-            // - Remove the relative url query parameters from UriInfo in the finally (after loan details are retrieved)
-            Map<String, String> queryParameters = null;
-            if (relativeUrl.indexOf('?') > 0) {
-                loanId = Long.parseLong(StringUtils.substringBetween(relativeUrl, "/", "?"));
-                queryParameters = getQueryParameters(relativeUrl);
-
-                // Add the query parameters sent in the relative URL to UriInfo
-                addQueryParametersToUriInfo(parameterizedUriInfo, queryParameters);
-            } else {
-                loanId = Long.parseLong(StringUtils.substringAfter(relativeUrl, "/"));
-            }
+        // uriInfo will contain the query parameter value(s) that are sent in the actual batch uri.
+        // for example: batches?enclosingTransaction=true
+        // But the query parameters that are sent in the batch relative url has to be sent to
+        // LoansApiResource.retrieveLoan
+        // To use the relative url query parameters
+        // - Parse and fetch the query parameters sent in the relative url
+        // (loans/66?fields=id,principal,annualInterestRate)
+        // - Add them to the UriInfo query parameters list
+        // - Call loansApiResource.retrieveLoan(loanId, false, uriInfo)
+        // - Remove the relative url query parameters from UriInfo in the finally (after loan details are retrieved)
+        Map<String, String> queryParameters = null;
+        if (relativeUrl.indexOf('?') > 0) {
+            loanId = Long.parseLong(StringUtils.substringBetween(relativeUrl, "/", "?"));
+            queryParameters = getQueryParameters(relativeUrl);
+
+            // Add the query parameters sent in the relative URL to UriInfo
+            addQueryParametersToUriInfo(parameterizedUriInfo, queryParameters);
+        } else {
+            loanId = Long.parseLong(StringUtils.substringAfter(relativeUrl, "/"));
+        }
 
-            // Calls 'retrieveLoan' function from 'LoansApiResource' to
-            // get the loan details based on the loan id
-            final boolean staffInSelectedOfficeOnly = false;
-            String associations = null;
-            String exclude = null;
-            String fields = null;
-            if (queryParameters != null && queryParameters.size() > 0) {
-                if (queryParameters.containsKey("associations")) {
-                    associations = queryParameters.get("associations");
-                }
-                if (queryParameters.containsKey("exclude")) {
-                    exclude = queryParameters.get("exclude");
-                }
-                if (queryParameters.containsKey("fields")) {
-                    fields = queryParameters.get("fields");
-                }
+        // Calls 'retrieveLoan' function from 'LoansApiResource' to
+        // get the loan details based on the loan id
+        final boolean staffInSelectedOfficeOnly = false;
+        String associations = null;
+        String exclude = null;
+        String fields = null;
+        if (queryParameters != null && queryParameters.size() > 0) {
+            if (queryParameters.containsKey("associations")) {
+                associations = queryParameters.get("associations");
             }
+            if (queryParameters.containsKey("exclude")) {
+                exclude = queryParameters.get("exclude");
+            }
+            if (queryParameters.containsKey("fields")) {
+                fields = queryParameters.get("fields");
+            }
+        }
 
-            responseBody = loansApiResource.retrieveLoan(loanId, staffInSelectedOfficeOnly, associations, exclude, fields,
-                    parameterizedUriInfo);
-
-            response.setStatusCode(HttpStatus.SC_OK);
-
-            // Sets the response after retrieving the loan
-            response.setBody(responseBody);
-
-        } catch (RuntimeException e) {
+        responseBody = loansApiResource.retrieveLoan(loanId, staffInSelectedOfficeOnly, associations, exclude, fields,
+                parameterizedUriInfo);
 
-            // Gets an object of type ErrorInfo, containing information about
-            // raised exception
-            ErrorInfo ex = ErrorHandler.handler(e);
+        response.setStatusCode(HttpStatus.SC_OK);
 
-            response.setStatusCode(ex.getStatusCode());
-            response.setBody(ex.getMessage());
-        }
+        // Sets the response after retrieving the loan
+        response.setBody(responseBody);
 
         return response;
     }
diff --git a/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/GetTransactionByIdCommandStrategy.java b/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/GetTransactionByIdCommandStrategy.java
index 3f7f22a89..bec9b431c 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/GetTransactionByIdCommandStrategy.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/GetTransactionByIdCommandStrategy.java
@@ -29,8 +29,6 @@ import org.apache.fineract.batch.command.CommandStrategy;
 import org.apache.fineract.batch.command.CommandStrategyUtils;
 import org.apache.fineract.batch.domain.BatchRequest;
 import org.apache.fineract.batch.domain.BatchResponse;
-import org.apache.fineract.batch.exception.ErrorHandler;
-import org.apache.fineract.batch.exception.ErrorInfo;
 import org.apache.fineract.infrastructure.core.api.MutableUriInfo;
 import org.apache.fineract.portfolio.loanaccount.api.LoanTransactionsApiResource;
 import org.apache.http.HttpStatus;
@@ -88,25 +86,13 @@ public class GetTransactionByIdCommandStrategy implements CommandStrategy {
             }
         }
 
-        // Try-catch blocks to map exceptions to appropriate status codes
-        try {
-            // Calls 'retrieveTransaction' function from 'loanTransactionsApiResource'
-            responseBody = loanTransactionsApiResource.retrieveTransaction(loanId, transactionId, fields, uriInfo);
+        // Calls 'retrieveTransaction' function from 'loanTransactionsApiResource'
+        responseBody = loanTransactionsApiResource.retrieveTransaction(loanId, transactionId, fields, uriInfo);
 
-            response.setStatusCode(HttpStatus.SC_OK);
+        response.setStatusCode(HttpStatus.SC_OK);
 
-            // Sets the body of the response after retrieving the transaction
-            response.setBody(responseBody);
-
-        } catch (RuntimeException e) {
-
-            // Gets an object of type ErrorInfo, containing information about
-            // raised exception
-            ErrorInfo ex = ErrorHandler.handler(e);
-
-            response.setStatusCode(ex.getStatusCode());
-            response.setBody(ex.getMessage());
-        }
+        // Sets the body of the response after retrieving the transaction
+        response.setBody(responseBody);
 
         return response;
     }
diff --git a/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/UpdateClientCommandStrategy.java b/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/UpdateClientCommandStrategy.java
index 2977ed58b..9d0e3aae7 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/UpdateClientCommandStrategy.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/UpdateClientCommandStrategy.java
@@ -23,8 +23,6 @@ import lombok.RequiredArgsConstructor;
 import org.apache.fineract.batch.command.CommandStrategy;
 import org.apache.fineract.batch.domain.BatchRequest;
 import org.apache.fineract.batch.domain.BatchResponse;
-import org.apache.fineract.batch.exception.ErrorHandler;
-import org.apache.fineract.batch.exception.ErrorInfo;
 import org.apache.fineract.portfolio.client.api.ClientsApiResource;
 import org.springframework.stereotype.Component;
 
@@ -60,27 +58,14 @@ public class UpdateClientCommandStrategy implements CommandStrategy {
         final String relativeUrl = request.getRelativeUrl();
         final Long clientId = Long.parseLong(relativeUrl.substring(relativeUrl.indexOf('/') + 1));
 
-        // Try-catch blocks to map exceptions to appropriate status codes
-        try {
+        // Calls 'update' function from 'ClientsApiResource' to update a
+        // client
+        responseBody = clientsApiResource.update(clientId, request.getBody());
 
-            // Calls 'update' function from 'ClientsApiResource' to update a
-            // client
-            responseBody = clientsApiResource.update(clientId, request.getBody());
-
-            response.setStatusCode(200);
-            // Sets the body of the response after the successful update of
-            // client information
-            response.setBody(responseBody);
-
-        } catch (RuntimeException e) {
-
-            // Gets an object of type ErrorInfo, containing information about
-            // raised exception
-            ErrorInfo ex = ErrorHandler.handler(e);
-
-            response.setStatusCode(ex.getStatusCode());
-            response.setBody(ex.getMessage());
-        }
+        response.setStatusCode(200);
+        // Sets the body of the response after the successful update of
+        // client information
+        response.setBody(responseBody);
 
         return response;
     }
diff --git a/fineract-provider/src/main/java/org/apache/fineract/batch/service/BatchApiServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/batch/service/BatchApiServiceImpl.java
index 44ef98191..20502e3c1 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/batch/service/BatchApiServiceImpl.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/batch/service/BatchApiServiceImpl.java
@@ -27,6 +27,7 @@ import javax.ws.rs.core.Response;
 import javax.ws.rs.core.Response.Status;
 import javax.ws.rs.core.UriInfo;
 import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
 import org.apache.fineract.batch.command.CommandContext;
 import org.apache.fineract.batch.command.CommandStrategy;
 import org.apache.fineract.batch.command.CommandStrategyProvider;
@@ -55,6 +56,7 @@ import org.springframework.transaction.support.TransactionTemplate;
  */
 @Service
 @RequiredArgsConstructor
+@Slf4j
 public class BatchApiServiceImpl implements BatchApiService {
 
     private final CommandStrategyProvider strategyProvider;
@@ -86,24 +88,35 @@ public class BatchApiServiceImpl implements BatchApiService {
             final BatchRequest rootRequest = rootNode.getRequest();
             final CommandStrategy commandStrategy = this.strategyProvider
                     .getCommandStrategy(CommandContext.resource(rootRequest.getRelativeUrl()).method(rootRequest.getMethod()).build());
-            final BatchResponse rootResponse = commandStrategy.execute(rootRequest, uriInfo);
+            final BatchResponse rootResponse = safelyExecuteStrategy(commandStrategy, rootRequest, uriInfo);
 
             responseList.add(rootResponse);
             responseList.addAll(this.processChildRequests(rootNode, rootResponse, uriInfo));
         }
 
-        Collections.sort(responseList, new Comparator<BatchResponse>() {
-
-            @Override
-            public int compare(BatchResponse source, BatchResponse testee) {
-                return source.getRequestId().compareTo(testee.getRequestId());
-            }
-        });
+        Collections.sort(responseList, Comparator.comparing(BatchResponse::getRequestId));
 
         return responseList;
 
     }
 
+    private BatchResponse safelyExecuteStrategy(CommandStrategy commandStrategy, BatchRequest request, UriInfo uriInfo) {
+        try {
+            return commandStrategy.execute(request, uriInfo);
+        } catch (RuntimeException e) {
+            log.warn("Exception while executing batch strategy {}", commandStrategy.getClass().getSimpleName(), e);
+
+            ErrorInfo ex = ErrorHandler.handler(e);
+
+            final BatchResponse response = new BatchResponse();
+            response.setRequestId(request.getRequestId());
+            response.setHeaders(request.getHeaders());
+            response.setStatusCode(ex.getStatusCode());
+            response.setBody(ex.getMessage());
+            return response;
+        }
+    }
+
     private List<BatchResponse> processChildRequests(final BatchRequestNode rootRequest, BatchResponse rootResponse, UriInfo uriInfo) {
 
         final List<BatchResponse> childResponses = new ArrayList<>();
@@ -121,7 +134,7 @@ public class BatchApiServiceImpl implements BatchApiService {
                         final CommandStrategy commandStrategy = this.strategyProvider.getCommandStrategy(
                                 CommandContext.resource(childRequest.getRelativeUrl()).method(childRequest.getMethod()).build());
 
-                        childResponse = commandStrategy.execute(childRequest, uriInfo);
+                        childResponse = safelyExecuteStrategy(commandStrategy, childRequest, uriInfo);
 
                     } else {
                         // Something went wrong with the parent request, create
diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/codes/api/CodeValuesApiResourceSwagger.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/codes/api/CodeValuesApiResourceSwagger.java
index ee8e48af1..26df57634 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/codes/api/CodeValuesApiResourceSwagger.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/codes/api/CodeValuesApiResourceSwagger.java
@@ -55,6 +55,8 @@ final class CodeValuesApiResourceSwagger {
 
         @Schema(example = "Passport")
         public String name;
+        @Schema(example = "true")
+        public Boolean isActive;
         @Schema(example = "Passport information")
         public String description;
         @Schema(example = "0")
diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/api/GlobalConfigurationApiResource.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/api/GlobalConfigurationApiResource.java
index 7d6fc0778..f26438d48 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/api/GlobalConfigurationApiResource.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/api/GlobalConfigurationApiResource.java
@@ -20,7 +20,6 @@ package org.apache.fineract.infrastructure.configuration.api;
 
 import io.swagger.v3.oas.annotations.Operation;
 import io.swagger.v3.oas.annotations.Parameter;
-import io.swagger.v3.oas.annotations.media.ArraySchema;
 import io.swagger.v3.oas.annotations.media.Content;
 import io.swagger.v3.oas.annotations.media.Schema;
 import io.swagger.v3.oas.annotations.parameters.RequestBody;
@@ -103,7 +102,7 @@ public class GlobalConfigurationApiResource {
             + "Returns the list global enable/disable survey configurations.\n" + "\n" + "Example Requests:\n" + "\n"
             + "configurations/survey")
     @ApiResponses({
-            @ApiResponse(responseCode = "200", description = "List of example \\n response \\nsurveys response   \\ngiven below", content = @Content(array = @ArraySchema(schema = @Schema(implementation = GlobalConfigurationApiResourceSwagger.GetGlobalConfigurationsResponse.class)))) })
+            @ApiResponse(responseCode = "200", description = "List of example \\n response \\nsurveys response   \\ngiven below", content = @Content(schema = @Schema(implementation = GlobalConfigurationApiResourceSwagger.GetGlobalConfigurationsResponse.class))) })
     public String retrieveConfiguration(@Context final UriInfo uriInfo,
             @DefaultValue("false") @QueryParam("survey") @Parameter(description = "survey") final boolean survey) {
 
diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/data/ResultsetColumnHeaderData.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/data/ResultsetColumnHeaderData.java
index e318cd02d..7ae84bf38 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/data/ResultsetColumnHeaderData.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/data/ResultsetColumnHeaderData.java
@@ -326,4 +326,8 @@ public final class ResultsetColumnHeaderData implements Serializable {
     public String getColumnCode() {
         return this.columnCode;
     }
+
+    public List<ResultsetColumnValueData> getColumnValues() {
+        return this.columnValues;
+    }
 }
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 cbae01e23..ca887a6b0 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
@@ -1652,7 +1652,13 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
                     pValueWrite = "null";
                 } else {
                     if ("bit".equalsIgnoreCase(pColumnHeader.getColumnType())) {
-                        pValueWrite = BooleanUtils.toString(BooleanUtils.toBooleanObject(pValue), "1", "0", "null");
+                        if (databaseTypeResolver.isMySQL()) {
+                            pValueWrite = BooleanUtils.toString(BooleanUtils.toBooleanObject(pValue), "1", "0", "null");
+                        } else if (databaseTypeResolver.isPostgreSQL()) {
+                            pValueWrite = BooleanUtils.toString(BooleanUtils.toBooleanObject(pValue), "B'1'", "B'0'", "null");
+                        } else {
+                            throw new IllegalStateException("Current database is not supported");
+                        }
                     } else {
                         pValueWrite = singleQuote + this.genericDataService.replace(pValue, singleQuote, singleQuote + singleQuote)
                                 + singleQuote;
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/api/LoanProductsApiResourceSwagger.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/api/LoanProductsApiResourceSwagger.java
index e786f9bad..3aa826ef5 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/api/LoanProductsApiResourceSwagger.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/api/LoanProductsApiResourceSwagger.java
@@ -25,7 +25,7 @@ import java.util.Collection;
 import java.util.List;
 import java.util.Set;
 import org.apache.fineract.accounting.producttoaccountmapping.data.ChargeToGLAccountMapper;
-import org.apache.fineract.portfolio.rate.data.RateData;
+import org.apache.fineract.infrastructure.core.data.EnumOptionData;
 
 /**
  * Created by Chirag Gupta on 12/27/17.
@@ -69,6 +69,26 @@ final class LoanProductsApiResourceSwagger {
             public Integer id;
         }
 
+        static final class RateData {
+
+            private RateData() {}
+
+            @Schema(example = "1")
+            public Long id;
+
+            @Schema(example = "some name")
+            public String name;
+
+            @Schema(example = "20")
+            public BigDecimal percentage;
+
+            @Schema(description = "Apply specific product using its id, code, and value.")
+            public EnumOptionData productApply;
+
+            @Schema(example = "false")
+            public boolean active;
+        }
+
         @Schema(example = "LP Accrual Accounting")
         public String name;
         @Schema(example = "LPAA")
diff --git a/fineract-provider/src/test/java/org/apache/fineract/batch/command/CommandStrategyProviderTest.java b/fineract-provider/src/test/java/org/apache/fineract/batch/command/CommandStrategyProviderTest.java
index 7ce584cd1..22e8811d8 100644
--- a/fineract-provider/src/test/java/org/apache/fineract/batch/command/CommandStrategyProviderTest.java
+++ b/fineract-provider/src/test/java/org/apache/fineract/batch/command/CommandStrategyProviderTest.java
@@ -32,10 +32,11 @@ import org.apache.fineract.batch.command.internal.ApproveLoanRescheduleCommandSt
 import org.apache.fineract.batch.command.internal.CollectChargesCommandStrategy;
 import org.apache.fineract.batch.command.internal.CreateChargeCommandStrategy;
 import org.apache.fineract.batch.command.internal.CreateClientCommandStrategy;
+import org.apache.fineract.batch.command.internal.CreateTransactionLoanCommandStrategy;
 import org.apache.fineract.batch.command.internal.DisburseLoanCommandStrategy;
+import org.apache.fineract.batch.command.internal.GetDatatableEntryByAppTableIdCommandStrategy;
 import org.apache.fineract.batch.command.internal.GetLoanByIdCommandStrategy;
 import org.apache.fineract.batch.command.internal.GetTransactionByIdCommandStrategy;
-import org.apache.fineract.batch.command.internal.RepayLoanCommandStrategy;
 import org.apache.fineract.batch.command.internal.UnknownCommandStrategy;
 import org.apache.fineract.batch.command.internal.UpdateClientCommandStrategy;
 import org.junit.jupiter.params.ParameterizedTest;
@@ -66,8 +67,10 @@ public class CommandStrategyProviderTest {
                 Arguments.of("loans/123/charges", HttpMethod.POST, "createChargeCommandStrategy", mock(CreateChargeCommandStrategy.class)),
                 Arguments.of("loans/123/charges", HttpMethod.GET, "collectChargesCommandStrategy",
                         mock(CollectChargesCommandStrategy.class)),
-                Arguments.of("loans/123/transactions?command=repayment", HttpMethod.POST, "repayLoanCommandStrategy",
-                        mock(RepayLoanCommandStrategy.class)),
+                Arguments.of("loans/123/transactions?command=repayment", HttpMethod.POST, "createTransactionLoanCommandStrategy",
+                        mock(CreateTransactionLoanCommandStrategy.class)),
+                Arguments.of("loans/123/transactions?command=creditBalanceRefund", HttpMethod.POST, "createTransactionLoanCommandStrategy",
+                        mock(CreateTransactionLoanCommandStrategy.class)),
                 Arguments.of("clients/456?command=activate", HttpMethod.POST, "activateClientCommandStrategy",
                         mock(ActivateClientCommandStrategy.class)),
                 Arguments.of("loans/123?command=approve", HttpMethod.POST, "approveLoanCommandStrategy",
@@ -77,7 +80,11 @@ public class CommandStrategyProviderTest {
                 Arguments.of("rescheduleloans/123?command=approve", HttpMethod.POST, "approveLoanRescheduleCommandStrategy",
                         mock(ApproveLoanRescheduleCommandStrategy.class)),
                 Arguments.of("loans/123/transactions/123", HttpMethod.GET, "getTransactionByIdCommandStrategy",
-                        mock(GetTransactionByIdCommandStrategy.class)));
+                        mock(GetTransactionByIdCommandStrategy.class)),
+                Arguments.of("datatables/test_dt_table/123", HttpMethod.GET, "getDatatableEntryByAppTableIdCommandStrategy",
+                        mock(GetDatatableEntryByAppTableIdCommandStrategy.class)),
+                Arguments.of("datatables/test_dt_table/123?genericResultSet=true", HttpMethod.GET,
+                        "getDatatableEntryByAppTableIdCommandStrategy", mock(GetDatatableEntryByAppTableIdCommandStrategy.class)));
     }
 
     /**
@@ -99,7 +106,6 @@ public class CommandStrategyProviderTest {
         final ApplicationContext applicationContext = mock(ApplicationContext.class);
         final CommandStrategyProvider commandStrategyProvider = new CommandStrategyProvider(applicationContext);
         when(applicationContext.getBean(beanName)).thenReturn(commandStrategy);
-
         final CommandStrategy result = commandStrategyProvider.getCommandStrategy(CommandContext.resource(url).method(httpMethod).build());
         assertEquals(commandStrategy, result);
     }
@@ -111,7 +117,9 @@ public class CommandStrategyProviderTest {
      */
     private static Stream<Arguments> provideCommandStrategyResourceDetailsForErrors() {
         return Stream.of(Arguments.of("loans/123?command=reject", HttpMethod.POST),
-                Arguments.of("loans/glimAccount/746?command=approve", HttpMethod.POST), Arguments.of("loans/123", HttpMethod.PUT));
+                Arguments.of("loans/glimAccount/746?command=approve", HttpMethod.POST), Arguments.of("loans/123", HttpMethod.PUT),
+                Arguments.of("datatables/test_dt_table", HttpMethod.GET), Arguments.of("datatables", HttpMethod.GET));
+
     }
 
     /**
diff --git a/fineract-provider/src/test/java/org/apache/fineract/batch/command/internal/CreateTransactionLoanCommandStrategyTest.java b/fineract-provider/src/test/java/org/apache/fineract/batch/command/internal/CreateTransactionLoanCommandStrategyTest.java
new file mode 100644
index 000000000..c081a2e0c
--- /dev/null
+++ b/fineract-provider/src/test/java/org/apache/fineract/batch/command/internal/CreateTransactionLoanCommandStrategyTest.java
@@ -0,0 +1,119 @@
+/**
+ * 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.batch.command.internal;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertSame;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import javax.ws.rs.HttpMethod;
+import javax.ws.rs.core.UriInfo;
+import org.apache.commons.lang3.RandomStringUtils;
+import org.apache.fineract.batch.domain.BatchRequest;
+import org.apache.fineract.batch.domain.BatchResponse;
+import org.apache.fineract.portfolio.loanaccount.api.LoanTransactionsApiResource;
+import org.apache.http.HttpStatus;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Test class fpr {@link CreateTransactionLoanCommandStrategy}.
+ */
+public class CreateTransactionLoanCommandStrategyTest {
+
+    /**
+     * Test {@link CreateTransactionLoanCommandStrategy#execute} happy path scenario.
+     */
+    @Test
+    public void testExecuteSuccessScenario() {
+        final TestContext testContext = new TestContext();
+        final Long loanId = Long.valueOf(RandomStringUtils.randomNumeric(4));
+        final String command = "myCommand";
+        final BatchRequest batchRequest = getBatchRequest(loanId, command);
+        final String responseBody = "myResponseBody";
+
+        when(testContext.loanTransactionsApiResource.executeLoanTransaction(loanId, command, batchRequest.getBody()))
+                .thenReturn(responseBody);
+
+        BatchResponse batchResponse = testContext.subjectToTest.execute(batchRequest, testContext.uriInfo);
+
+        assertEquals(HttpStatus.SC_OK, batchResponse.getStatusCode());
+        assertSame(responseBody, batchResponse.getBody());
+        assertEquals(batchRequest.getRequestId(), batchResponse.getRequestId());
+        assertEquals(batchRequest.getHeaders(), batchResponse.getHeaders());
+
+        verify(testContext.loanTransactionsApiResource).executeLoanTransaction(loanId, command, batchRequest.getBody());
+    }
+
+    /**
+     * Creates and returns a request with the given loan id and command value.
+     *
+     * @param loanId
+     *            the loan id
+     * @param command
+     *            the transaction id
+     * @return BatchRequest
+     */
+    private BatchRequest getBatchRequest(final Long loanId, final String command) {
+
+        final BatchRequest br = new BatchRequest();
+        String relativeUrl = "loans/" + loanId + "/transactions?command=" + command;
+
+        br.setRequestId(Long.valueOf(RandomStringUtils.randomNumeric(5)));
+        br.setRelativeUrl(relativeUrl);
+        br.setMethod(HttpMethod.POST);
+        br.setReference(Long.valueOf(RandomStringUtils.randomNumeric(5)));
+        br.setBody("{}");
+
+        return br;
+    }
+
+    /**
+     * Private test context class used since testng runs in parallel to avoid state between tests
+     */
+    private static class TestContext {
+
+        /**
+         * Mock URI info.
+         */
+        @Mock
+        private UriInfo uriInfo;
+
+        /**
+         * Mock loan transactions API resource.
+         */
+        @Mock
+        private LoanTransactionsApiResource loanTransactionsApiResource;
+
+        /**
+         * The {@link CreateTransactionLoanCommandStrategy} under test.
+         */
+        private final CreateTransactionLoanCommandStrategy subjectToTest;
+
+        /**
+         * Constructor.
+         */
+        TestContext() {
+            MockitoAnnotations.openMocks(this);
+            subjectToTest = new CreateTransactionLoanCommandStrategy(loanTransactionsApiResource);
+        }
+    }
+}
diff --git a/fineract-provider/src/test/java/org/apache/fineract/batch/command/internal/GetDatatableEntryByAppTableIdCommandStrategyTest.java b/fineract-provider/src/test/java/org/apache/fineract/batch/command/internal/GetDatatableEntryByAppTableIdCommandStrategyTest.java
new file mode 100644
index 000000000..7699dec0c
--- /dev/null
+++ b/fineract-provider/src/test/java/org/apache/fineract/batch/command/internal/GetDatatableEntryByAppTableIdCommandStrategyTest.java
@@ -0,0 +1,160 @@
+/**
+ * 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.batch.command.internal;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.BDDMockito.given;
+import static org.mockito.Mockito.verify;
+
+import java.util.List;
+import java.util.stream.Stream;
+import javax.ws.rs.HttpMethod;
+import javax.ws.rs.core.UriInfo;
+import org.apache.commons.lang3.RandomUtils;
+import org.apache.fineract.batch.domain.BatchRequest;
+import org.apache.fineract.batch.domain.BatchResponse;
+import org.apache.fineract.infrastructure.core.api.MutableUriInfo;
+import org.apache.fineract.infrastructure.dataqueries.api.DatatablesApiResource;
+import org.apache.http.HttpStatus;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Tests {GetDatatableEntryByAppTableIdCommandStrategy}.
+ */
+public class GetDatatableEntryByAppTableIdCommandStrategyTest {
+
+    /**
+     * Query parameter provider.
+     *
+     * @return the test data stream
+     */
+    private static Stream<Arguments> provideQueryParameters() {
+        return Stream.of(Arguments.of(null, 0), Arguments.of("genericResultSet=true", 1));
+    }
+
+    /**
+     * Test {@link GetDatatableEntryByAppTableIdCommandStrategy#execute} happy path scenario.
+     *
+     * @param queryParameter
+     *            the query parameter
+     * @param numberOfQueryParams
+     *            number of query params are provided
+     */
+    @ParameterizedTest
+    @MethodSource("provideQueryParameters")
+    public void testExecuteSuccessScenario(final String queryParameter, final int numberOfQueryParams) {
+        final TestContext testContext = new TestContext();
+
+        final Long loanId = RandomUtils.nextLong();
+        final String datatableName = "dt_loan_xyz";
+        final BatchRequest request = getBatchRequest(loanId, queryParameter, datatableName);
+        final String responseBody = "{\\\"columnHeaders\\\":[{}],\\\"data\\\":\\\"{}\\\"}";
+
+        given(testContext.dataTableApiResource.getDatatable(eq(datatableName), eq(loanId), eq(null), any(UriInfo.class)))
+                .willReturn(responseBody);
+
+        final BatchResponse response = testContext.subjectToTest.execute(request, testContext.uriInfo);
+
+        assertEquals(HttpStatus.SC_OK, response.getStatusCode());
+        assertEquals(request.getRequestId(), response.getRequestId());
+        assertEquals(request.getHeaders(), response.getHeaders());
+        assertEquals(responseBody, response.getBody());
+
+        verify(testContext.dataTableApiResource).getDatatable(eq(datatableName), eq(loanId), eq(null), testContext.uriInfoCaptor.capture());
+        MutableUriInfo mutableUriInfo = testContext.uriInfoCaptor.getValue();
+        assertThat(mutableUriInfo.getAdditionalQueryParameters()).hasSize(numberOfQueryParams);
+        if (numberOfQueryParams > 0) {
+            List<String> param = mutableUriInfo.getAdditionalQueryParameters().get("genericResultSet");
+            assertEquals(param.get(0), "true");
+        }
+    }
+
+    /**
+     * Creates and returns a request with the given loan id.
+     *
+     * @param loanId
+     *            the loan id
+     * @param queryParameter
+     *            the query parameter
+     * @param datatableName
+     *            the datatable name
+     * @return the {@link BatchRequest}
+     */
+    private BatchRequest getBatchRequest(final Long loanId, final String queryParameter, final String datatableName) {
+        final BatchRequest br = new BatchRequest();
+        String relativeUrl = String.format("datatables/%s/%s", datatableName, loanId);
+        if (queryParameter != null) {
+            relativeUrl = relativeUrl + "?" + queryParameter;
+        }
+
+        br.setRequestId(RandomUtils.nextLong());
+        br.setRelativeUrl(relativeUrl);
+        br.setMethod(HttpMethod.GET);
+        br.setReference(RandomUtils.nextLong());
+        br.setBody("{}");
+
+        return br;
+    }
+
+    /**
+     * Private test context class used since testng runs in parallel to avoid state between tests
+     */
+    private static final class TestContext {
+
+        /**
+         * The subject under test.
+         */
+        private final GetDatatableEntryByAppTableIdCommandStrategy subjectToTest;
+
+        /**
+         * Mock of {@link UriInfo}
+         */
+        @Mock
+        private UriInfo uriInfo;
+
+        /**
+         * Captor of {@link MutableUriInfo}.
+         */
+        @Captor
+        private ArgumentCaptor<MutableUriInfo> uriInfoCaptor;
+
+        /**
+         * {@link DatatablesApiResource} mock.
+         */
+        @Mock
+        private DatatablesApiResource dataTableApiResource;
+
+        /**
+         * Constructor.
+         */
+        private TestContext() {
+            MockitoAnnotations.openMocks(this);
+            subjectToTest = new GetDatatableEntryByAppTableIdCommandStrategy(dataTableApiResource);
+        }
+    }
+}
diff --git a/fineract-provider/src/test/java/org/apache/fineract/batch/command/internal/GetLoanByIdCommandStrategyTest.java b/fineract-provider/src/test/java/org/apache/fineract/batch/command/internal/GetLoanByIdCommandStrategyTest.java
index 40a381488..d8327f89c 100644
--- a/fineract-provider/src/test/java/org/apache/fineract/batch/command/internal/GetLoanByIdCommandStrategyTest.java
+++ b/fineract-provider/src/test/java/org/apache/fineract/batch/command/internal/GetLoanByIdCommandStrategyTest.java
@@ -34,9 +34,7 @@ import org.apache.fineract.batch.domain.BatchRequest;
 import org.apache.fineract.batch.domain.BatchResponse;
 import org.apache.fineract.infrastructure.core.api.MutableUriInfo;
 import org.apache.fineract.portfolio.loanaccount.api.LoansApiResource;
-import org.apache.fineract.portfolio.loanaccount.exception.LoanNotFoundException;
 import org.apache.http.HttpStatus;
-import org.junit.jupiter.api.Test;
 import org.junit.jupiter.params.ParameterizedTest;
 import org.junit.jupiter.params.provider.Arguments;
 import org.junit.jupiter.params.provider.MethodSource;
@@ -87,64 +85,6 @@ public class GetLoanByIdCommandStrategyTest {
         assertThat(mutableUriInfo.getAdditionalQueryParameters()).hasSize(noOfQueryParams);
     }
 
-    /**
-     * Test {@link GetLoanByIdCommandStrategy#execute} for internal server error.
-     */
-    @Test
-    public void testExecuteForInternalServerError() {
-        // given
-        final TestContext testContext = new TestContext();
-        final Long loanId = Long.valueOf(RandomStringUtils.randomNumeric(4));
-        final BatchRequest request = getBatchRequest(loanId, null, null, null);
-
-        given(testContext.loansApiResource.retrieveLoan(eq(loanId), eq(false), eq(null), eq(null), eq(null), any(UriInfo.class)))
-                .willThrow(new RuntimeException("Some error"));
-
-        // when
-        final BatchResponse response = testContext.underTest.execute(request, testContext.uriInfo);
-
-        // then
-        assertThat(response.getStatusCode()).isEqualTo(HttpStatus.SC_INTERNAL_SERVER_ERROR);
-        assertThat(response.getBody()).isEqualTo("{\"Exception\": java.lang.RuntimeException: Some error}");
-        assertThat(response.getRequestId()).isEqualTo(request.getRequestId());
-        assertThat(response.getHeaders()).isEqualTo(request.getHeaders());
-    }
-
-    /**
-     * Test {@link GetLoanByIdCommandStrategy#execute} for loan not found exception.
-     */
-    @Test
-    public void testExecuteForLoanNotFoundException() {
-        // given
-        final TestContext testContext = new TestContext();
-        final Long loanId = Long.valueOf(RandomStringUtils.randomNumeric(4));
-        final BatchRequest request = getBatchRequest(loanId, null, null, null);
-
-        given(testContext.loansApiResource.retrieveLoan(eq(loanId), eq(false), eq(null), eq(null), eq(null), any(UriInfo.class)))
-                .willThrow(new LoanNotFoundException(loanId));
-
-        // when
-        final BatchResponse response = testContext.underTest.execute(request, testContext.uriInfo);
-
-        // then
-        assertThat(response.getStatusCode()).isEqualTo(HttpStatus.SC_NOT_FOUND);
-        assertThat(response.getBody()).isEqualTo(buildLoanNotFoundError(loanId));
-        assertThat(response.getRequestId()).isEqualTo(request.getRequestId());
-        assertThat(response.getHeaders()).isEqualTo(request.getHeaders());
-    }
-
-    private String buildLoanNotFoundError(final Long loanId) {
-        return String.format(
-                "{\n  \"developerMessage\": \"The requested resource is not available.\",\n"
-                        + "  \"httpStatusCode\": \"404\",\n  \"defaultUserMessage\": \"The requested resource is not available.\",\n"
-                        + "  \"userMessageGlobalisationCode\": \"error.msg.resource.not.found\",\n  \"errors\": [\n    {\n"
-                        + "      \"developerMessage\": \"Loan with identifier %s does not exist\",\n"
-                        + "      \"defaultUserMessage\": \"Loan with identifier %s does not exist\",\n"
-                        + "      \"userMessageGlobalisationCode\": \"error.msg.loan.id.invalid\",\n      \"parameterName\": \"id\",\n"
-                        + "      \"args\": [\n        {\n          \"value\": %s\n        }\n      ]\n    }\n  ]\n" + "}",
-                loanId, loanId, loanId);
-    }
-
     /**
      * Creates and returns a request with the given loan id.
      *
diff --git a/fineract-provider/src/test/java/org/apache/fineract/batch/command/internal/GetTransactionByIdCommandStrategyTest.java b/fineract-provider/src/test/java/org/apache/fineract/batch/command/internal/GetTransactionByIdCommandStrategyTest.java
index fe92be9dc..60ef582f8 100644
--- a/fineract-provider/src/test/java/org/apache/fineract/batch/command/internal/GetTransactionByIdCommandStrategyTest.java
+++ b/fineract-provider/src/test/java/org/apache/fineract/batch/command/internal/GetTransactionByIdCommandStrategyTest.java
@@ -19,11 +19,9 @@
 package org.apache.fineract.batch.command.internal;
 
 import static org.assertj.core.api.Assertions.assertThat;
-import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.BDDMockito.given;
-import static org.mockito.Mockito.when;
 
 import java.util.stream.Stream;
 import javax.ws.rs.HttpMethod;
@@ -32,10 +30,7 @@ import org.apache.commons.lang3.RandomStringUtils;
 import org.apache.fineract.batch.domain.BatchRequest;
 import org.apache.fineract.batch.domain.BatchResponse;
 import org.apache.fineract.portfolio.loanaccount.api.LoanTransactionsApiResource;
-import org.apache.fineract.portfolio.loanaccount.exception.LoanNotFoundException;
-import org.apache.fineract.portfolio.loanaccount.exception.LoanTransactionNotFoundException;
 import org.apache.http.HttpStatus;
-import org.junit.jupiter.api.Test;
 import org.junit.jupiter.params.ParameterizedTest;
 import org.junit.jupiter.params.provider.Arguments;
 import org.junit.jupiter.params.provider.MethodSource;
@@ -84,75 +79,6 @@ public class GetTransactionByIdCommandStrategyTest {
         assertThat(response.getBody()).isEqualTo(responseBody);
     }
 
-    /**
-     * Test {@link GetTransactionByIdCommandStrategy#execute} for internal server error.
-     */
-    @Test
-    public void testExecuteForInternalServerError() {
-        // given
-        final TestContext testContext = new TestContext();
-        final Long loanId = Long.valueOf(RandomStringUtils.randomNumeric(4));
-        final Long transactionId = Long.valueOf(RandomStringUtils.randomNumeric(4));
-        final BatchRequest request = getBatchRequest(loanId, transactionId, null);
-
-        given(testContext.loanTransactionsApiResource.retrieveTransaction(loanId, transactionId, null, testContext.uriInfo))
-                .willThrow(new RuntimeException("Some error"));
-
-        // when
-        final BatchResponse response = testContext.subjectToTest.execute(request, testContext.uriInfo);
-
-        // then
-        assertThat(response.getStatusCode()).isEqualTo(HttpStatus.SC_INTERNAL_SERVER_ERROR);
-        assertThat(response.getRequestId()).isEqualTo(request.getRequestId());
-        assertThat(response.getHeaders()).isEqualTo(request.getHeaders());
-        assertThat(response.getBody()).isEqualTo("{\"Exception\": java.lang.RuntimeException: Some error}");
-    }
-
-    /**
-     * Test {@link GetTransactionByIdCommandStrategy#execute} for loan not found exception.
-     */
-    @Test
-    public void testExecuteForLoanNotFoundException() {
-        // given
-        final TestContext testContext = new TestContext();
-        final Long loanId = Long.valueOf(RandomStringUtils.randomNumeric(4));
-        final Long transactionId = Long.valueOf(RandomStringUtils.randomNumeric(4));
-        final BatchRequest request = getBatchRequest(loanId, transactionId, null);
-
-        given(testContext.loanTransactionsApiResource.retrieveTransaction(loanId, transactionId, null, testContext.uriInfo))
-                .willThrow(new LoanNotFoundException(loanId));
-
-        // when
-        final BatchResponse response = testContext.subjectToTest.execute(request, testContext.uriInfo);
-
-        // then
-        assertThat(response.getStatusCode()).isEqualTo(HttpStatus.SC_NOT_FOUND);
-        assertThat(response.getRequestId()).isEqualTo(request.getRequestId());
-        assertThat(response.getHeaders()).isEqualTo(request.getHeaders());
-        assertThat(response.getBody()).isEqualTo(build404NotFoundError("Loan", loanId));
-    }
-
-    /**
-     * Test {@link GetTransactionByIdCommandStrategy#execute} for transaction not found exception.
-     */
-    @Test
-    public void testExecuteForTransactionNotFoundException() {
-        final TestContext testContext = new TestContext();
-        final Long loanId = Long.valueOf(RandomStringUtils.randomNumeric(4));
-        final Long transactionId = Long.valueOf(RandomStringUtils.randomNumeric(4));
-        final BatchRequest request = getBatchRequest(loanId, transactionId, null);
-
-        when(testContext.loanTransactionsApiResource.retrieveTransaction(loanId, transactionId, null, testContext.uriInfo))
-                .thenThrow(new LoanTransactionNotFoundException(transactionId));
-
-        final BatchResponse response = testContext.subjectToTest.execute(request, testContext.uriInfo);
-
-        assertEquals(HttpStatus.SC_NOT_FOUND, response.getStatusCode());
-        assertEquals(build404NotFoundError("Transaction", transactionId), response.getBody());
-        assertEquals(request.getRequestId(), response.getRequestId());
-        assertEquals(request.getHeaders(), response.getHeaders());
-    }
-
     /**
      * Creates and returns a request with the given loan id and transaction id.
      *
@@ -179,25 +105,6 @@ public class GetTransactionByIdCommandStrategyTest {
         return br;
     }
 
-    /**
-     * Builds the 404 not found error.
-     *
-     * @param field
-     *            the field name
-     * @param id
-     *            the id
-     */
-    private String build404NotFoundError(final String field, final Long id) {
-        return String.format("{\n" + "  \"developerMessage\": \"The requested resource is not available.\",\n"
-                + "  \"httpStatusCode\": \"404\",\n" + "  \"defaultUserMessage\": \"The requested resource is not available.\",\n"
-                + "  \"userMessageGlobalisationCode\": \"error.msg.resource.not.found\",\n" + "  \"errors\": [\n" + "    {\n"
-                + "      \"developerMessage\": \"%s with identifier %s does not exist\",\n"
-                + "      \"defaultUserMessage\": \"%s with identifier %s does not exist\",\n"
-                + "      \"userMessageGlobalisationCode\": \"error.msg.loan.id.invalid\",\n" + "      \"parameterName\": \"id\",\n"
-                + "      \"args\": [\n" + "        {\n" + "          \"value\": %s\n" + "        }\n" + "      ]\n" + "    }\n" + "  ]\n"
-                + "}", field, id, field, id, id);
-    }
-
     /**
      * Private test context class used since testng runs in parallel to avoid state between tests
      */
diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/BatchApiTest.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/BatchApiTest.java
index 59d0ce052..49bded81a 100644
--- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/BatchApiTest.java
+++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/BatchApiTest.java
@@ -18,6 +18,8 @@
  */
 package org.apache.fineract.integrationtests;
 
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
 import com.google.gson.JsonElement;
 import com.google.gson.JsonObject;
 import io.restassured.builder.RequestSpecBuilder;
@@ -28,6 +30,7 @@ import io.restassured.specification.ResponseSpecification;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
+import org.apache.fineract.batch.command.internal.CreateTransactionLoanCommandStrategy;
 import org.apache.fineract.batch.domain.BatchRequest;
 import org.apache.fineract.batch.domain.BatchResponse;
 import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
@@ -38,6 +41,7 @@ import org.apache.fineract.integrationtests.common.Utils;
 import org.apache.fineract.integrationtests.common.loans.LoanProductTestBuilder;
 import org.apache.fineract.integrationtests.common.loans.LoanTransactionHelper;
 import org.apache.fineract.integrationtests.common.savings.SavingsProductHelper;
+import org.apache.fineract.integrationtests.common.system.DatatableHelper;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanStatus;
 import org.apache.http.HttpStatus;
 import org.junit.jupiter.api.Assertions;
@@ -55,9 +59,26 @@ import org.junit.jupiter.api.Test;
  */
 public class BatchApiTest {
 
+    /**
+     * The response specification
+     */
     private ResponseSpecification responseSpec;
+
+    /**
+     * The request specification
+     */
     private RequestSpecification requestSpec;
 
+    /**
+     * The datatable helper
+     */
+    private DatatableHelper datatableHelper;
+
+    /**
+     * Loan app datatable
+     */
+    private static final String LOAN_APP_TABLE_NAME = "m_loan";
+
     /**
      * Sets up the essential settings for the TEST like contentType, expectedStatusCode. It uses the '@BeforeEach'
      * annotation provided by jUnit.
@@ -68,6 +89,7 @@ public class BatchApiTest {
         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);
     }
 
     /**
@@ -240,7 +262,8 @@ public class BatchApiTest {
         // Get the clientId parameter from createClient Response
         final JsonElement clientId = new FromJsonHelper().parse(response.get(0).getBody()).getAsJsonObject().get("clientId");
 
-        Assertions.assertEquals(200L, (long) response.get(1).getStatusCode(), "Verify Status Code 200" + clientId.getAsString());
+        Assertions.assertEquals(HttpStatus.SC_OK, (long) response.get(1).getStatusCode(),
+                "Verify Status Code 200" + clientId.getAsString());
     }
 
     /**
@@ -281,7 +304,7 @@ public class BatchApiTest {
         final List<BatchResponse> response = BatchHelper.postBatchRequestsWithoutEnclosingTransaction(this.requestSpec, this.responseSpec,
                 jsonifiedRequest);
 
-        Assertions.assertEquals(200L, (long) response.get(1).getStatusCode(), "Verify Status Code 200");
+        Assertions.assertEquals(HttpStatus.SC_OK, (long) response.get(1).getStatusCode(), "Verify Status Code 200");
     }
 
     /**
@@ -341,7 +364,7 @@ public class BatchApiTest {
         final List<BatchResponse> response = BatchHelper.postBatchRequestsWithoutEnclosingTransaction(this.requestSpec, this.responseSpec,
                 jsonifiedRequest);
 
-        Assertions.assertEquals(200L, (long) response.get(3).getStatusCode(), "Verify Status Code 200 for Create Loan Charge");
+        Assertions.assertEquals(HttpStatus.SC_OK, (long) response.get(3).getStatusCode(), "Verify Status Code 200 for Create Loan Charge");
     }
 
     /**
@@ -349,7 +372,7 @@ public class BatchApiTest {
      * successful responses. It first creates a new loan and then makes two repayments for it and then verifies that
      * 200(OK) is returned for the repayment requests.
      *
-     * @see org.apache.fineract.batch.command.internal.RepayLoanCommandStrategy
+     * @see CreateTransactionLoanCommandStrategy
      */
     @Test
     public void shouldReturnOkStatusForBatchRepayment() {
@@ -392,10 +415,10 @@ public class BatchApiTest {
         final BatchRequest br5 = BatchHelper.disburseLoanRequest(4734L, 4733L);
 
         // Create a loanRepay Request
-        final BatchRequest br6 = BatchHelper.repayLoanRequest(4735L, 4734L);
+        final BatchRequest br6 = BatchHelper.repayLoanRequest(4735L, 4734L, "500");
 
         // Create a loanRepay Request
-        final BatchRequest br7 = BatchHelper.repayLoanRequest(4736L, 4734L);
+        final BatchRequest br7 = BatchHelper.repayLoanRequest(4736L, 4734L, "500");
 
         final List<BatchRequest> batchRequests = new ArrayList<>();
 
@@ -412,8 +435,85 @@ public class BatchApiTest {
         final List<BatchResponse> response = BatchHelper.postBatchRequestsWithoutEnclosingTransaction(this.requestSpec, this.responseSpec,
                 jsonifiedRequest);
 
-        Assertions.assertEquals(200L, (long) response.get(5).getStatusCode(), "Verify Status Code 200 for Repayment");
-        Assertions.assertEquals(200L, (long) response.get(6).getStatusCode(), "Verify Status Code 200 for Repayment");
+        Assertions.assertEquals(HttpStatus.SC_OK, (long) response.get(5).getStatusCode(), "Verify Status Code 200 for Repayment");
+        Assertions.assertEquals(HttpStatus.SC_OK, (long) response.get(6).getStatusCode(), "Verify Status Code 200 for Repayment");
+    }
+
+    /**
+     * Tests that batch credit balance refund for loans is happening properly. Collected properly 200(OK) status was
+     * returned for successful responses. It first creates a new loan and then makes an overpayment, before creating a
+     * credit balance refund to refund a portion of the over-payment.
+     *
+     * @see CreateTransactionLoanCommandStrategy
+     */
+    @Test
+    public void shouldReturnOkStatusForBatchCreditBalanceRefund() {
+
+        final String loanProductJSON = new LoanProductTestBuilder() //
+                .withPrincipal("1000.00") //
+                .withNumberOfRepayments("24") //
+                .withRepaymentAfterEvery("1") //
+                .withRepaymentTypeAsMonth() //
+                .withinterestRatePerPeriod("2") //
+                .withInterestRateFrequencyTypeAsMonths() //
+                .withAmortizationTypeAsEqualPrincipalPayment() //
+                .withInterestTypeAsDecliningBalance() //
+                .currencyDetails("0", "100").build(null);
+
+        final Integer clientID = ClientHelper.createClient(this.requestSpec, this.responseSpec);
+        ClientHelper.verifyClientCreatedOnServer(this.requestSpec, this.responseSpec, clientID);
+
+        final Integer collateralId = CollateralManagementHelper.createCollateralProduct(this.requestSpec, this.responseSpec);
+        Assertions.assertNotNull(collateralId);
+        final Integer clientCollateralId = CollateralManagementHelper.createClientCollateral(this.requestSpec, this.responseSpec,
+                clientID.toString(), collateralId);
+        Assertions.assertNotNull(clientCollateralId);
+
+        final Integer productId = new LoanTransactionHelper(this.requestSpec, this.responseSpec).getLoanProductId(loanProductJSON);
+
+        final Long createActiveClientRequestId = 4730L;
+        final Long applyLoanRequestId = createActiveClientRequestId + 1;
+        final Long approveLoanRequestId = applyLoanRequestId + 1;
+        final Long disburseLoanRequestId = approveLoanRequestId + 1;
+        final Long repayLoanRequestId = disburseLoanRequestId + 1;
+        final Long creditBalanceRefundRequestId = repayLoanRequestId + 1;
+
+        // Create a createClient Request
+        final BatchRequest br1 = BatchHelper.createActiveClientRequest(createActiveClientRequestId, "");
+
+        // Create a ApplyLoan Request
+        final BatchRequest br2 = BatchHelper.applyLoanRequest(applyLoanRequestId, createActiveClientRequestId, productId,
+                clientCollateralId);
+
+        // Create a approveLoan Request
+        final BatchRequest br3 = BatchHelper.approveLoanRequest(approveLoanRequestId, applyLoanRequestId);
+
+        // Create a disburseLoan Request
+        final BatchRequest br4 = BatchHelper.disburseLoanRequest(disburseLoanRequestId, approveLoanRequestId);
+
+        // Create a loanRepay Request which will result in an overpay.
+        final BatchRequest br5 = BatchHelper.repayLoanRequest(repayLoanRequestId, disburseLoanRequestId, "20000");
+
+        // Create a credit balance refund request
+        final BatchRequest br6 = BatchHelper.creditBalanceRefundRequest(creditBalanceRefundRequestId, repayLoanRequestId, "500");
+
+        final List<BatchRequest> batchRequests = new ArrayList<>();
+
+        batchRequests.add(br1);
+        batchRequests.add(br2);
+        batchRequests.add(br3);
+        batchRequests.add(br4);
+        batchRequests.add(br5);
+        batchRequests.add(br6);
+
+        final String jsonifiedRequest = BatchHelper.toJsonString(batchRequests);
+
+        final List<BatchResponse> response = BatchHelper.postBatchRequestsWithoutEnclosingTransaction(this.requestSpec, this.responseSpec,
+                jsonifiedRequest);
+
+        Assertions.assertEquals(HttpStatus.SC_OK, (long) response.get(4).getStatusCode(), "Verify Status Code 200 for Repayment");
+        Assertions.assertEquals(HttpStatus.SC_OK, (long) response.get(5).getStatusCode(),
+                "Verify Status Code 200 for Credit Balance Refund");
     }
 
     /**
@@ -441,8 +541,8 @@ public class BatchApiTest {
         final List<BatchResponse> response = BatchHelper.postBatchRequestsWithoutEnclosingTransaction(this.requestSpec, this.responseSpec,
                 jsonifiedRequest);
 
-        Assertions.assertEquals(200L, (long) response.get(0).getStatusCode(), "Verify Status Code 200 for Create Client");
-        Assertions.assertEquals(200L, (long) response.get(1).getStatusCode(), "Verify Status Code 200 for Activate Client");
+        Assertions.assertEquals(HttpStatus.SC_OK, (long) response.get(0).getStatusCode(), "Verify Status Code 200 for Create Client");
+        Assertions.assertEquals(HttpStatus.SC_OK, (long) response.get(1).getStatusCode(), "Verify Status Code 200 for Activate Client");
     }
 
     /**
@@ -504,8 +604,8 @@ public class BatchApiTest {
         final List<BatchResponse> response = BatchHelper.postBatchRequestsWithoutEnclosingTransaction(this.requestSpec, this.responseSpec,
                 jsonifiedRequest);
 
-        Assertions.assertEquals(200L, (long) response.get(3).getStatusCode(), "Verify Status Code 200 for Approve Loan");
-        Assertions.assertEquals(200L, (long) response.get(4).getStatusCode(), "Verify Status Code 200 for Disburse Loan");
+        Assertions.assertEquals(HttpStatus.SC_OK, (long) response.get(3).getStatusCode(), "Verify Status Code 200 for Approve Loan");
+        Assertions.assertEquals(HttpStatus.SC_OK, (long) response.get(4).getStatusCode(), "Verify Status Code 200 for Disburse Loan");
     }
 
     /**
@@ -558,10 +658,10 @@ public class BatchApiTest {
         final List<BatchResponse> response = BatchHelper.postBatchRequestsWithEnclosingTransaction(this.requestSpec, this.responseSpec,
                 jsonifiedRequest);
 
-        Assertions.assertEquals(200L, (long) response.get(0).getStatusCode(), "Verify Status Code 200 for create client");
-        Assertions.assertEquals(200L, (long) response.get(1).getStatusCode(), "Verify Status Code 200 for apply Loan");
-        Assertions.assertEquals(200L, (long) response.get(2).getStatusCode(), "Verify Status Code 200 for approve Loan");
-        Assertions.assertEquals(200L, (long) response.get(3).getStatusCode(), "Verify Status Code 200 for disburse Loan");
+        Assertions.assertEquals(HttpStatus.SC_OK, (long) response.get(0).getStatusCode(), "Verify Status Code 200 for create client");
+        Assertions.assertEquals(HttpStatus.SC_OK, (long) response.get(1).getStatusCode(), "Verify Status Code 200 for apply Loan");
+        Assertions.assertEquals(HttpStatus.SC_OK, (long) response.get(2).getStatusCode(), "Verify Status Code 200 for approve Loan");
+        Assertions.assertEquals(HttpStatus.SC_OK, (long) response.get(3).getStatusCode(), "Verify Status Code 200 for disburse Loan");
     }
 
     /**
@@ -754,4 +854,164 @@ public class BatchApiTest {
         // Repayment schedule information will be available in the response based on the query parameter
         Assertions.assertTrue(responses.get(3).getBody().contains("repaymentSchedule"));
     }
+
+    /**
+     * Test for the successful get loan and get datatable entry. A '200' status code is expected on successful
+     * responses.
+     *
+     * @see org.apache.fineract.batch.command.internal.ApplyLoanCommandStrategy
+     * @see org.apache.fineract.batch.command.internal.ApproveLoanCommandStrategy
+     * @see org.apache.fineract.batch.command.internal.GetLoanByIdCommandStrategy
+     */
+    @Test
+    public void shouldReturnOkStatusOnSuccessfulGetDataTableEntry() {
+        final FromJsonHelper jsonHelper = new FromJsonHelper();
+        final Long loanId = jsonHelper.extractLongNamed("loanId", jsonHelper.parse(setupAccount()).getAsJsonObject());
+        final String datatableName = this.datatableHelper.createDatatable(LOAN_APP_TABLE_NAME, false);
+        try {
+
+            // Get loan by id Request with query param
+            final BatchRequest getLoanBatchRequest = BatchHelper.getLoanByIdRequest(loanId, "associations=repaymentSchedule,transactions");
+
+            // Get datatable batch request
+            final BatchRequest getDatatableBatchRequest = BatchHelper.getDatatableByIdRequest(loanId, datatableName,
+                    "genericResultSet=true");
+
+            final List<BatchRequest> batchRequestsGetLoan = Arrays.asList(getLoanBatchRequest, getDatatableBatchRequest);
+
+            final List<BatchResponse> responsesGetLoan = BatchHelper.postBatchRequestsWithEnclosingTransaction(this.requestSpec,
+                    this.responseSpec, BatchHelper.toJsonString(batchRequestsGetLoan));
+
+            final String getLoanResponse = responsesGetLoan.get(0).getBody();
+            final String getDatatableResponse = responsesGetLoan.get(1).getBody();
+
+            Assertions.assertEquals(HttpStatus.SC_OK, responsesGetLoan.get(0).getStatusCode(), "Verify Status Code 200 for get loan");
+            Assertions.assertEquals(HttpStatus.SC_OK, responsesGetLoan.get(1).getStatusCode(), "Verify Status Code 200 for datatable");
+
+            final Long loanIdInGetResponse = jsonHelper.extractLongNamed("id", jsonHelper.parse(getLoanResponse).getAsJsonObject());
+            Assertions.assertEquals(loanId, loanIdInGetResponse);
+
+            // Repayment schedule information will be available in the response based on the query parameter
+            Assertions.assertTrue(getLoanResponse.contains("repaymentSchedule"));
+
+            // Transaction will be available in the response based on the query parameter
+            Assertions.assertTrue(getLoanResponse.contains("transactions"));
+
+            // datatable info will be available in the response based on the query parameter
+            Assertions.assertTrue(getDatatableResponse.contains("columnHeaders"));
+
+            // datatable info will be available in the response based on the query parameter
+            Assertions.assertTrue(getDatatableResponse.contains("data"));
+        } finally {
+            deleteDatatable(datatableName);
+        }
+    }
+
+    /**
+     * Test for the successful get loan and get datatable entry where get datatable request have no query param. A '200'
+     * status code is expected on successful responses.
+     *
+     * @see org.apache.fineract.batch.command.internal.ApplyLoanCommandStrategy
+     * @see org.apache.fineract.batch.command.internal.ApproveLoanCommandStrategy
+     * @see org.apache.fineract.batch.command.internal.GetLoanByIdCommandStrategy
+     */
+    @Test
+    public void shouldReturnOkStatusOnSuccessfulGetDatatableEntryWithNoQueryParam() {
+        final FromJsonHelper jsonHelper = new FromJsonHelper();
+        final Long loanId = jsonHelper.extractLongNamed("loanId", jsonHelper.parse(setupAccount()).getAsJsonObject());
+        final String datatableName = this.datatableHelper.createDatatable(LOAN_APP_TABLE_NAME, false);
+        try {
+            // Get loan by id Request with query param
+            final BatchRequest getLoanBatchRequest = BatchHelper.getLoanByIdRequest(loanId, "associations=repaymentSchedule,transactions");
+
+            // Get datatable batch request
+            final BatchRequest getDatatableBatchRequest = BatchHelper.getDatatableByIdRequest(loanId, datatableName, null);
+
+            final List<BatchRequest> batchRequestsGetLoan = Arrays.asList(getLoanBatchRequest, getDatatableBatchRequest);
+
+            final List<BatchResponse> responsesGetLoan = BatchHelper.postBatchRequestsWithEnclosingTransaction(this.requestSpec,
+                    this.responseSpec, BatchHelper.toJsonString(batchRequestsGetLoan));
+
+            final String getLoanResponse = responsesGetLoan.get(0).getBody();
+
+            Assertions.assertEquals(HttpStatus.SC_OK, responsesGetLoan.get(0).getStatusCode(), "Verify Status Code 200 for Get Loan");
+            Assertions.assertEquals(HttpStatus.SC_OK, responsesGetLoan.get(1).getStatusCode(), "Verify Status Code 200 for Get Datatable");
+
+            final Long loanIdInGetResponse = jsonHelper.extractLongNamed("id", jsonHelper.parse(getLoanResponse).getAsJsonObject());
+            Assertions.assertEquals(loanId, loanIdInGetResponse);
+
+            Assertions.assertTrue(getLoanResponse.contains("repaymentSchedule"));
+
+            Assertions.assertTrue(getLoanResponse.contains("transactions"));
+        } finally {
+            deleteDatatable(datatableName);
+        }
+
+    }
+
+    /**
+     * Delete datatable
+     *
+     * @param datatableName
+     *            the datatable name
+     */
+    private void deleteDatatable(final String datatableName) {
+        String deletedDatatableName = this.datatableHelper.deleteDatatable(datatableName);
+        assertEquals(datatableName, deletedDatatableName, "Fail to delete the datatable");
+    }
+
+    /**
+     * Setup account to test get loan and get datatable batch call
+     *
+     * @return the response body
+     */
+    private String setupAccount() {
+        final String loanProductJSON = new LoanProductTestBuilder() //
+                .withPrincipal("10000000.00") //
+                .withNumberOfRepayments("24") //
+                .withRepaymentAfterEvery("1") //
+                .withRepaymentTypeAsMonth() //
+                .withinterestRatePerPeriod("2") //
+                .withInterestRateFrequencyTypeAsMonths() //
+                .withAmortizationTypeAsEqualPrincipalPayment() //
+                .withInterestTypeAsDecliningBalance() //
+                .currencyDetails("0", "100").build(null);
+
+        final Long applyLoanRequestId = 5730L;
+        final Long approveLoanRequestId = 5731L;
+        final Long disburseLoanRequestId = 5734L;
+        final Long repayLoanRequestId = 5735L;
+
+        // Create product
+        final Integer productId = new LoanTransactionHelper(this.requestSpec, this.responseSpec).getLoanProductId(loanProductJSON);
+
+        // Create client
+        final Integer clientId = ClientHelper.createClient(this.requestSpec, this.responseSpec);
+        ClientHelper.verifyClientCreatedOnServer(this.requestSpec, this.responseSpec, clientId);
+
+        // Create an ApplyLoan Request
+        final BatchRequest applyLoanBatchRequest = BatchHelper.applyLoanRequestWithClientId(applyLoanRequestId, clientId, productId);
+
+        // Create an approveLoan Request
+        final BatchRequest approveLoanBatchRequest = BatchHelper.approveLoanRequest(approveLoanRequestId, applyLoanRequestId);
+
+        // Create a disburseLoan Request
+        final BatchRequest disburseLoanBatchRequest = BatchHelper.disburseLoanRequest(disburseLoanRequestId, applyLoanRequestId);
+
+        // Create a repayment Request
+        final BatchRequest repaymentBatchRequest = BatchHelper.repayLoanRequest(repayLoanRequestId, applyLoanRequestId, "500");
+
+        final List<BatchRequest> batchRequests = Arrays.asList(applyLoanBatchRequest, approveLoanBatchRequest, disburseLoanBatchRequest,
+                repaymentBatchRequest);
+
+        final List<BatchResponse> responses = BatchHelper.postBatchRequestsWithEnclosingTransaction(this.requestSpec, this.responseSpec,
+                BatchHelper.toJsonString(batchRequests));
+
+        Assertions.assertEquals(HttpStatus.SC_OK, responses.get(0).getStatusCode(), "Verify Status Code 200 for Apply Loan");
+        Assertions.assertEquals(HttpStatus.SC_OK, responses.get(1).getStatusCode(), "Verify Status Code 200 for Approve Loan");
+        Assertions.assertEquals(HttpStatus.SC_OK, responses.get(2).getStatusCode(), "Verify Status Code 200 for Disburse Loan");
+        Assertions.assertEquals(HttpStatus.SC_OK, responses.get(3).getStatusCode(), "Verify Status Code 200 for Repay Loan");
+
+        return responses.get(0).getBody();
+    }
 }
diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/BatchHelper.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/BatchHelper.java
index 5b37a620d..ff45e9974 100644
--- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/BatchHelper.java
+++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/BatchHelper.java
@@ -23,10 +23,14 @@ import com.google.gson.reflect.TypeToken;
 import io.restassured.specification.RequestSpecification;
 import io.restassured.specification.ResponseSpecification;
 import java.security.SecureRandom;
+import java.time.LocalDate;
+import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
 import javax.ws.rs.HttpMethod;
+import org.apache.fineract.batch.command.internal.CreateTransactionLoanCommandStrategy;
 import org.apache.fineract.batch.domain.BatchRequest;
 import org.apache.fineract.batch.domain.BatchResponse;
 import org.junit.jupiter.api.Assertions;
@@ -223,12 +227,37 @@ public final class BatchHelper {
      * given requestId and reference.
      *
      * @param requestId
+     *            the request ID
      * @param reference
+     *            the reference ID
      * @param productId
-     * @return BatchRequest
+     *            the product ID
+     * @return BatchRequest the batch request
      */
     public static BatchRequest applyLoanRequest(final Long requestId, final Long reference, final Integer productId,
             final Integer clientCollateralId) {
+        return applyLoanRequest(requestId, reference, productId, clientCollateralId, LocalDate.now(ZoneId.systemDefault()).minusDays(10),
+                "10,000.00");
+    }
+
+    /**
+     * Creates and returns a {@link org.apache.fineract.batch.command.internal.ApplyLoanCommandStrategy} Request with
+     * given requestId and reference.
+     *
+     * @param requestId
+     *            the request ID
+     * @param reference
+     *            the reference ID
+     * @param productId
+     *            the product ID
+     * @param date
+     *            the loan submitted on date
+     * @param loanAmount
+     *            the loan amount
+     * @return BatchRequest the batch request
+     */
+    public static BatchRequest applyLoanRequest(final Long requestId, final Long reference, final Integer productId,
+            final Integer clientCollateralId, final LocalDate date, final String loanAmount) {
 
         final BatchRequest br = new BatchRequest();
 
@@ -236,19 +265,20 @@ public final class BatchHelper {
         br.setRelativeUrl("loans");
         br.setMethod("POST");
         br.setReference(reference);
+        String dateString = date.format(DateTimeFormatter.ofPattern("dd MMMM yyyy"));
 
         String body = "{\"dateFormat\": \"dd MMMM yyyy\", \"locale\": \"en_GB\", \"clientId\": \"$.clientId\"," + "\"productId\": "
-                + productId + ", \"principal\": \"10,000.00\", \"loanTermFrequency\": 10,"
+                + productId + ", \"principal\": \"" + loanAmount + "\", \"loanTermFrequency\": 10,"
                 + "\"loanTermFrequencyType\": 2, \"loanType\": \"individual\", \"numberOfRepayments\": 10,"
                 + "\"repaymentEvery\": 1, \"repaymentFrequencyType\": 2, \"interestRatePerPeriod\": 10,"
                 + "\"amortizationType\": 1, \"interestType\": 0, \"interestCalculationPeriodType\": 1,"
-                + "\"transactionProcessingStrategyId\": 1, \"expectedDisbursementDate\": \"10 Jun 2013\",";
+                + "\"transactionProcessingStrategyId\": 1, \"expectedDisbursementDate\": \"" + dateString + "\",";
 
         if (clientCollateralId != null) {
-            body = body + "\"collateral\": [{\"clientCollateralId\": \"" + clientCollateralId.toString() + "\", \"quantity\": \"1\"}],";
+            body = body + "\"collateral\": [{\"clientCollateralId\": \"" + clientCollateralId + "\", \"quantity\": \"1\"}],";
         }
 
-        body = body + "\"submittedOnDate\": \"10 Jun 2013\"}";
+        body = body + "\"submittedOnDate\": \"" + dateString + "\"}";
 
         br.setBody(body);
 
@@ -385,17 +415,37 @@ public final class BatchHelper {
      *
      *
      * @param requestId
+     *            the request ID
      * @param reference
-     * @return BatchRequest
+     *            the reference ID
+     * @return BatchRequest the batch request
      */
     public static BatchRequest approveLoanRequest(final Long requestId, final Long reference) {
+        return approveLoanRequest(requestId, reference, LocalDate.now(ZoneId.systemDefault()).minusDays(10));
+    }
+
+    /**
+     * Creates and returns a {@link org.apache.fineract.batch.command.internal.ApproveLoanCommandStrategy} Request with
+     * given requestId and reference.
+     *
+     *
+     * @param requestId
+     *            the request ID
+     * @param reference
+     *            the reference ID
+     * @param date
+     *            the approved on date
+     * @return BatchRequest the batch request
+     */
+    public static BatchRequest approveLoanRequest(final Long requestId, final Long reference, LocalDate date) {
         final BatchRequest br = new BatchRequest();
 
         br.setRequestId(requestId);
         br.setRelativeUrl("loans/$.loanId?command=approve");
         br.setReference(reference);
         br.setMethod("POST");
-        br.setBody("{\"locale\": \"en\", \"dateFormat\": \"dd MMMM yyyy\", \"approvedOnDate\": \"12 September 2013\","
+        String dateString = date.format(DateTimeFormatter.ofPattern("dd MMMM yyyy"));
+        br.setBody("{\"locale\": \"en\", \"dateFormat\": \"dd MMMM yyyy\", \"approvedOnDate\": \"" + dateString + "\","
                 + "\"note\": \"Loan approval note\"}");
 
         return br;
@@ -407,39 +457,123 @@ public final class BatchHelper {
      *
      *
      * @param requestId
+     *            the request ID
      * @param reference
-     * @return BatchRequest
+     *            the reference ID
+     * @return BatchRequest the batch request
      */
     public static BatchRequest disburseLoanRequest(final Long requestId, final Long reference) {
+        return disburseLoanRequest(requestId, reference, LocalDate.now(ZoneId.systemDefault()).minusDays(8));
+    }
+
+    /**
+     * Creates and returns a {@link org.apache.fineract.batch.command.internal.DisburseLoanCommandStrategy} Request with
+     * given requestId and reference.
+     *
+     *
+     * @param requestId
+     *            the request ID
+     * @param reference
+     *            the reference ID
+     * @param date
+     *            the actual disbursement date
+     * @return BatchRequest the batch request
+     */
+    public static BatchRequest disburseLoanRequest(final Long requestId, final Long reference, final LocalDate date) {
         final BatchRequest br = new BatchRequest();
 
         br.setRequestId(requestId);
         br.setRelativeUrl("loans/$.loanId?command=disburse");
         br.setReference(reference);
         br.setMethod("POST");
-        br.setBody("{\"locale\": \"en\", \"dateFormat\": \"dd MMMM yyyy\", \"actualDisbursementDate\": \"15 September 2013\"}");
+        String dateString = date.format(DateTimeFormatter.ofPattern("dd MMMM yyyy"));
+        br.setBody("{\"locale\": \"en\", \"dateFormat\": \"dd MMMM yyyy\", \"actualDisbursementDate\": \"" + dateString + "\"}");
 
         return br;
     }
 
     /**
-     * Creates and returns a {@link org.apache.fineract.batch.command.internal.RepayLoanCommandStrategy} Request with
-     * given requestId.
+     * Creates and returns a {@link CreateTransactionLoanCommandStrategy} Request with given requestId.
      *
+     * @param requestId
+     *            the request ID
+     * @param reference
+     *            the reference ID
+     * @param amount
+     *            the amount
+     * @param date
+     *            the transaction date
+     * @return BatchRequest the batch request
+     */
+    public static BatchRequest repayLoanRequest(final Long requestId, final Long reference, final String amount) {
+        return repayLoanRequest(requestId, reference, amount, LocalDate.now(ZoneId.systemDefault()));
+    }
+
+    /**
+     * Creates and returns a {@link CreateTransactionLoanCommandStrategy} Request with given requestId.
      *
      * @param requestId
+     *            the request ID
      * @param reference
-     * @return BatchRequest
+     *            the reference ID
+     * @param amount
+     *            the amount
+     * @param date
+     *            the transaction date
+     * @return BatchRequest the batch request
      */
-    public static BatchRequest repayLoanRequest(final Long requestId, final Long reference) {
+    public static BatchRequest repayLoanRequest(final Long requestId, final Long reference, final String amount, final LocalDate date) {
         final BatchRequest br = new BatchRequest();
 
         br.setRequestId(requestId);
         br.setReference(reference);
         br.setRelativeUrl("loans/$.loanId/transactions?command=repayment");
         br.setMethod("POST");
-        br.setBody("{\"locale\": \"en\", \"dateFormat\": \"dd MMMM yyyy\", "
-                + "\"transactionDate\": \"15 September 2013\",  \"transactionAmount\": 500}");
+        String dateString = date.format(DateTimeFormatter.ofPattern("dd MMMM yyyy"));
+        br.setBody(String.format(
+                "{\"locale\": \"en\", \"dateFormat\": \"dd MMMM yyyy\", " + "\"transactionDate\": \"%s\",  \"transactionAmount\": %s}",
+                dateString, amount));
+
+        return br;
+    }
+
+    /**
+     * Creates and returns a {@link CreateTransactionLoanCommandStrategy} request with given request ID.
+     *
+     *
+     * @param requestId
+     *            the request ID
+     * @param reference
+     *            teh reference
+     * @return BatchRequest the created {@link BatchRequest}
+     */
+    public static BatchRequest creditBalanceRefundRequest(final Long requestId, final Long reference, final String amount) {
+        return creditBalanceRefundRequest(requestId, reference, amount, LocalDate.now(ZoneId.systemDefault()));
+    }
+
+    /**
+     * Creates and returns a {@link CreateTransactionLoanCommandStrategy} request with given request ID.
+     *
+     *
+     * @param requestId
+     *            the request ID
+     * @param reference
+     *            teh reference
+     * @param date
+     *            the transaction date
+     * @return BatchRequest the created {@link BatchRequest}
+     */
+    public static BatchRequest creditBalanceRefundRequest(final Long requestId, final Long reference, final String amount, LocalDate date) {
+        final BatchRequest br = new BatchRequest();
+
+        br.setRequestId(requestId);
+        br.setReference(reference);
+        br.setRelativeUrl("loans/$.loanId/transactions?command=creditBalanceRefund");
+        br.setMethod("POST");
+        String dateString = date.format(DateTimeFormatter.ofPattern("dd MMMM yyyy"));
+        br.setBody(String.format(
+                "{\"locale\": \"en\", \"dateFormat\": \"dd MMMM yyyy\", " + "\"transactionDate\": \"%s\",  \"transactionAmount\": %s}",
+                dateString, amount));
 
         return br;
     }
@@ -511,4 +645,55 @@ public final class BatchHelper {
 
         return br;
     }
+
+    /**
+     * Creates and returns a {@link org.apache.fineract.batch.command.internal.GetLoanByIdCommandStrategy} request with
+     * given loan id and query param.
+     *
+     * @param loanId
+     *            the loan id
+     * @param queryParameter
+     *            the query parameters
+     * @return the {@link BatchRequest}
+     */
+    public static BatchRequest getLoanByIdRequest(final Long loanId, final String queryParameter) {
+        final BatchRequest br = new BatchRequest();
+        String relativeUrl = String.format("loans/%s", loanId);
+        if (queryParameter != null) {
+            relativeUrl = relativeUrl + "?" + queryParameter;
+        }
+
+        br.setRequestId(4567L);
+        br.setRelativeUrl(relativeUrl);
+        br.setMethod(HttpMethod.GET);
+        br.setBody("{}");
+
+        return br;
+    }
+
+    /**
+     * Creates and returns a batch request to get datatable entry.
+     *
+     * @param loanId
+     *            the loan id
+     * @param datatableName
+     *            the name of datatable
+     * @param queryParameter
+     *            the query parameters
+     * @return the {@link BatchRequest}
+     */
+    public static BatchRequest getDatatableByIdRequest(final Long loanId, final String datatableName, final String queryParameter) {
+        final BatchRequest br = new BatchRequest();
+        String relativeUrl = String.format("datatables/%s/%s", datatableName, loanId);
+        if (queryParameter != null) {
+            relativeUrl = relativeUrl + "?" + queryParameter;
+        }
+
+        br.setRequestId(4568L);
+        br.setRelativeUrl(relativeUrl);
+        br.setMethod(HttpMethod.GET);
+        br.setBody("{}");
+
+        return br;
+    }
 }