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/12/19 21:46:42 UTC

[fineract] branch develop updated: FINERACT-1724: Loan Charge API support for batch APIs and a couple of other batch API enhancements

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 8ab206aa4 FINERACT-1724: Loan Charge API support for batch APIs and a couple of other batch API enhancements
8ab206aa4 is described below

commit 8ab206aa4ac6dff61c400ef06459b59a1d1a27c7
Author: Arnold Galovics <ga...@gmail.com>
AuthorDate: Thu Dec 15 23:33:52 2022 +0100

    FINERACT-1724: Loan Charge API support for batch APIs and a couple of other batch API enhancements
---
 .../batch/command/CommandStrategyProvider.java     | 133 +++++--
 .../internal/ActivateClientCommandStrategy.java    |   5 +-
 ...stChargeByChargeExternalIdCommandStrategy.java} |  56 +--
 ...ategy.java => AdjustChargeCommandStrategy.java} |  53 ++-
 ...ustTransactionByExternalIdCommandStrategy.java} |  33 +-
 .../internal/AdjustTransactionCommandStrategy.java |  11 +-
 .../command/internal/ApplyLoanCommandStrategy.java |   3 +-
 .../internal/ApplySavingsCommandStrategy.java      |   3 +-
 .../internal/ApproveLoanCommandStrategy.java       |   5 +-
 .../ApproveLoanRescheduleCommandStrategy.java      |   7 +-
 ...ectChargesByLoanExternalIdCommandStrategy.java} |  37 +-
 .../internal/CollectChargesCommandStrategy.java    |  12 +-
 ...eateChargeByLoanExternalIdCommandStrategy.java} |  42 +-
 .../internal/CreateChargeCommandStrategy.java      |   5 +-
 .../internal/CreateClientCommandStrategy.java      |   3 +-
 .../CreateDatatableEntryCommandStrategy.java       |   3 +-
 ...CreateLoanRescheduleRequestCommandStrategy.java |   3 +-
 ...ransactionByLoanExternalIdCommandStrategy.java} |  30 +-
 .../CreateTransactionLoanCommandStrategy.java      |  17 +-
 .../internal/DisburseLoanCommandStrategy.java      |   5 +-
 ...etChargeByChargeExternalIdCommandStrategy.java} |  22 +-
 .../internal/GetChargeByIdCommandStrategy.java     |   5 +-
 .../GetLoanByExternalIdCommandStrategy.java        | 152 ++++++++
 ...GetTransactionByExternalIdCommandStrategy.java} |  28 +-
 .../GetTransactionByIdCommandStrategy.java         |   7 +-
 ...teTransistionsByExternalIdCommandStrategy.java} |  46 ++-
 ...oanApplicationByExternalIdCommandStrategy.java} |  57 +--
 ...a => ModifyLoanApplicationCommandStrategy.java} |  55 ++-
 .../internal/UpdateClientCommandStrategy.java      |   3 +-
 ...dateDatatableEntryOneToManyCommandStrategy.java |   3 +-
 .../api/LoanChargesApiResourceSwagger.java         |   2 +
 .../loanaccount/api/LoansApiResourceSwagger.java   |  28 +-
 .../batch/command/CommandStrategyProviderTest.java |  91 ++++-
 ...hargeByChargeExternalIdCommandStrategyTest.java | 158 ++++++++
 ...t.java => AdjustChargeCommandStrategyTest.java} |  91 +++--
 ...ransactionByExternalIdCommandStrategyTest.java} |  78 ++--
 .../AdjustTransactionCommandStrategyTest.java      |  38 +-
 ...hargesByLoanExternalIdCommandStrategyTest.java} |  95 ++---
 ...ChargeByLoanExternalIdCommandStrategyTest.java} |  65 ++--
 .../CreateDatatableEntryCommandStrategyTest.java   |  26 +-
 ...teLoanRescheduleRequestCommandStrategyTest.java |   2 +-
 ...actionByLoanExternalIdCommandStrategyTest.java} |  29 +-
 .../CreateTransactionLoanCommandStrategyTest.java  |   2 +-
 ...argeByChargeExternalIdCommandStrategyTest.java} |  98 ++---
 ...=> GetLoanByExternalIdCommandStrategyTest.java} |  91 +++--
 .../internal/GetLoanByIdCommandStrategyTest.java   |   3 +
 ...ransactionByExternalIdCommandStrategyTest.java} |  52 ++-
 .../GetTransactionByIdCommandStrategyTest.java     |  16 +
 ...ransistionsByExternalIdCommandStrategyTest.java | 170 ++++++++
 ...ApplicationByExternalIdCommandStrategyTest.java | 137 +++++++
 .../ModifyLoanApplicationCommandStrategyTest.java  | 135 +++++++
 ...DatatableEntryOneToManyCommandStrategyTest.java |  13 +-
 .../fineract/integrationtests/BatchApiTest.java    | 420 +++++++++++++++++++-
 .../ExternalIdSupportIntegrationTest.java          |  67 ++--
 .../integrationtests/common/BatchHelper.java       | 426 ++++++++++++++++++++-
 .../common/GlobalConfigurationHelper.java          |   6 +
 56 files changed, 2611 insertions(+), 572 deletions(-)

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 0a7009a00..0560934f1 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
@@ -18,6 +18,10 @@
  */
 package org.apache.fineract.batch.command;
 
+import static javax.ws.rs.HttpMethod.GET;
+import static javax.ws.rs.HttpMethod.POST;
+import static javax.ws.rs.HttpMethod.PUT;
+
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
 import org.apache.fineract.batch.command.internal.UnknownCommandStrategy;
@@ -38,6 +42,41 @@ public class CommandStrategyProvider {
     private final ApplicationContext applicationContext;
     private static final Map<CommandContext, String> commandStrategies = new ConcurrentHashMap<>();
 
+    /**
+     * Regex pattern for specifying any number of query params or not specific any query param
+     */
+    private static final String OPTIONAL_QUERY_PARAM_REGEX = "(\\?(\\w+(?:\\=[\\w,]+|&)+)+)?";
+
+    /**
+     * Regex pattern for specifying query params
+     */
+    private static final String MANDATORY_QUERY_PARAM_REGEX = "(\\?(\\w+(?:\\=[\\w,]+|&)+)+)";
+
+    /**
+     * Regex pattern for specifying any query param that has key = 'command' or not specific anything.
+     */
+    private static final String OPTIONAL_COMMAND_PARAM_REGEX = "(\\?command=[\\w]+)?";
+
+    /**
+     * Regex pattern for specifying a mandatory query param that has key = 'command'.
+     */
+    private static final String MANDATORY_COMMAND_PARAM_REGEX = "\\?command=[\\w]+";
+
+    /**
+     * Regex pattern for specifying a UUID param.
+     */
+    private static final String UUID_PARAM_REGEX = "[\\w\\d-]+";
+
+    /**
+     * Regex pattern for specifying a param that's should be a number.
+     */
+    private static final String NUMBER_REGEX = "\\d+";
+
+    /**
+     * Regex pattern for specifying a param that contains case in-sensitive alphanumeric characters with underscores.
+     */
+    private static final String ALPHANUMBERIC_WITH_UNDERSCORE_REGEX = "[a-zA-Z0-9_]*";
+
     /**
      * Constructs a CommandStrategyProvider with argument of ApplicationContext type. It also initializes
      * commandStrategies using init() function by filling it with available CommandStrategies in
@@ -81,46 +120,88 @@ public class CommandStrategyProvider {
      * command Strategy will have to be added within this function in order to initiate it within the constructor.
      */
     private static void init() {
-        commandStrategies.put(CommandContext.resource("clients").method("POST").build(), "createClientCommandStrategy");
-        commandStrategies.put(CommandContext.resource("clients\\/\\d+").method("PUT").build(), "updateClientCommandStrategy");
-        commandStrategies.put(CommandContext.resource("loans").method("POST").build(), "applyLoanCommandStrategy");
-        commandStrategies.put(CommandContext.resource("loans\\/\\d+").method("GET").build(), "getLoanByIdCommandStrategy");
-        commandStrategies.put(CommandContext.resource("loans/\\d+(\\?(\\w+(?:\\=[\\w,]+|&)+)+)").method("GET").build(),
+        commandStrategies.put(CommandContext.resource("clients").method(POST).build(), "createClientCommandStrategy");
+        commandStrategies.put(CommandContext.resource("clients\\/" + NUMBER_REGEX).method(PUT).build(), "updateClientCommandStrategy");
+        commandStrategies.put(CommandContext.resource("loans").method(POST).build(), "applyLoanCommandStrategy");
+        commandStrategies.put(CommandContext.resource("loans\\/" + NUMBER_REGEX + OPTIONAL_QUERY_PARAM_REGEX).method(GET).build(),
                 "getLoanByIdCommandStrategy");
-        commandStrategies.put(CommandContext.resource("savingsaccounts").method("POST").build(), "applySavingsCommandStrategy");
-        commandStrategies.put(CommandContext.resource("loans\\/\\d+\\/charges").method("POST").build(), "createChargeCommandStrategy");
-        commandStrategies.put(CommandContext.resource("loans\\/\\d+\\/charges").method("GET").build(), "collectChargesCommandStrategy");
-        commandStrategies.put(CommandContext.resource("loans\\/\\d+\\/charges\\/\\d+").method("GET").build(),
+        commandStrategies.put(
+                CommandContext.resource("loans\\/external-id\\/" + UUID_PARAM_REGEX + OPTIONAL_QUERY_PARAM_REGEX).method(GET).build(),
+                "getLoanByExternalIdCommandStrategy");
+        commandStrategies.put(CommandContext.resource("savingsaccounts").method(POST).build(), "applySavingsCommandStrategy");
+        commandStrategies.put(CommandContext.resource("loans\\/" + NUMBER_REGEX + "\\/charges").method(POST).build(),
+                "createChargeCommandStrategy");
+        commandStrategies
+                .put(CommandContext.resource("loans\\/external-id\\/" + UUID_PARAM_REGEX + "\\/charges" + OPTIONAL_COMMAND_PARAM_REGEX + "")
+                        .method(POST).build(), "createChargeByLoanExternalIdCommandStrategy");
+        commandStrategies.put(CommandContext.resource("loans\\/" + NUMBER_REGEX + "\\/charges").method(GET).build(),
+                "collectChargesCommandStrategy");
+        commandStrategies.put(CommandContext.resource("loans\\/external-id\\/" + UUID_PARAM_REGEX + "\\/charges").method(GET).build(),
+                "collectChargesByLoanExternalIdCommandStrategy");
+        commandStrategies.put(CommandContext.resource("loans\\/" + NUMBER_REGEX + "\\/charges\\/" + NUMBER_REGEX).method(GET).build(),
                 "getChargeByIdCommandStrategy");
-        commandStrategies.put(CommandContext.resource("loans\\/\\d+\\/transactions\\?(\\w+(\\=[\\w]+))").method("POST").build(),
+        commandStrategies.put(CommandContext.resource(
+                "loans\\/external-id\\/" + UUID_PARAM_REGEX + "\\/charges\\/external-id\\/" + UUID_PARAM_REGEX + OPTIONAL_QUERY_PARAM_REGEX)
+                .method(GET).build(), "getChargeByChargeExternalIdCommandStrategy");
+        commandStrategies.put(CommandContext
+                .resource("loans\\/" + NUMBER_REGEX + "\\/charges\\/" + NUMBER_REGEX + MANDATORY_COMMAND_PARAM_REGEX).method(POST).build(),
+                "adjustChargeCommandStrategy");
+        commandStrategies.put(CommandContext.resource("loans\\/external-id\\/" + UUID_PARAM_REGEX + "\\/charges\\/external-id\\/"
+                + UUID_PARAM_REGEX + MANDATORY_COMMAND_PARAM_REGEX).method(POST).build(), "adjustChargeByChargeExternalIdCommandStrategy");
+        commandStrategies.put(
+                CommandContext.resource("loans\\/" + NUMBER_REGEX + "\\/transactions" + MANDATORY_COMMAND_PARAM_REGEX).method(POST).build(),
                 "createTransactionLoanCommandStrategy");
-        commandStrategies.put(CommandContext.resource("loans\\/\\d+\\/transactions\\/\\d+(\\?command=[\\w]+)?").method("POST").build(),
-                "adjustTransactionCommandStrategy");
-        commandStrategies.put(CommandContext.resource("clients\\/\\d+\\?command=activate").method("POST").build(),
+        commandStrategies.put(
+                CommandContext.resource("loans\\/external-id\\/" + UUID_PARAM_REGEX + "\\/transactions" + MANDATORY_COMMAND_PARAM_REGEX)
+                        .method(POST).build(),
+                "createTransactionByLoanExternalIdCommandStrategy");
+        commandStrategies
+                .put(CommandContext.resource("loans\\/" + NUMBER_REGEX + "\\/transactions\\/" + NUMBER_REGEX + OPTIONAL_COMMAND_PARAM_REGEX)
+                        .method(POST).build(), "adjustTransactionCommandStrategy");
+        commandStrategies.put(CommandContext.resource("loans\\/external-id\\/" + UUID_PARAM_REGEX + "\\/transactions\\/external-id\\/"
+                + UUID_PARAM_REGEX + OPTIONAL_COMMAND_PARAM_REGEX).method(POST).build(), "adjustTransactionByExternalIdCommandStrategy");
+        commandStrategies.put(CommandContext.resource("clients\\/" + NUMBER_REGEX + "\\?command=activate").method(POST).build(),
                 "activateClientCommandStrategy");
-        commandStrategies.put(CommandContext.resource("loans\\/\\d+\\?command=approve").method("POST").build(),
+        commandStrategies.put(CommandContext.resource("loans\\/" + NUMBER_REGEX + "\\?command=approve").method(POST).build(),
                 "approveLoanCommandStrategy");
-        commandStrategies.put(CommandContext.resource("loans\\/\\d+\\?command=disburse").method("POST").build(),
+        commandStrategies.put(CommandContext.resource("loans\\/" + NUMBER_REGEX + "\\?command=disburse").method(POST).build(),
                 "disburseLoanCommandStrategy");
-        commandStrategies.put(CommandContext.resource("rescheduleloans").method("POST").build(),
+        commandStrategies.put(
+                CommandContext.resource("loans\\/external-id\\/" + UUID_PARAM_REGEX + MANDATORY_COMMAND_PARAM_REGEX).method(POST).build(),
+                "loanStateTransistionsByExternalIdCommandStrategy");
+        commandStrategies.put(CommandContext.resource("rescheduleloans").method(POST).build(),
                 "createLoanRescheduleRequestCommandStrategy");
-        commandStrategies.put(CommandContext.resource("rescheduleloans\\/\\d+\\?command=approve").method("POST").build(),
+        commandStrategies.put(CommandContext.resource("rescheduleloans\\/" + NUMBER_REGEX + "\\?command=approve").method(POST).build(),
                 "approveLoanRescheduleCommandStrategy");
-        commandStrategies.put(CommandContext.resource("loans\\/\\d+\\/transactions\\/\\d+").method("GET").build(),
+        commandStrategies.put(CommandContext.resource("loans\\/" + NUMBER_REGEX + "\\/transactions\\/" + NUMBER_REGEX).method(GET).build(),
                 "getTransactionByIdCommandStrategy");
-        commandStrategies.put(CommandContext.resource("datatables\\/[a-zA-Z0-9_]*\\/\\d+").method("GET").build(),
-                "getDatatableEntryByAppTableIdCommandStrategy");
-        commandStrategies.put(CommandContext.resource("datatables\\/[a-zA-Z0-9_]*\\/\\d+").method("POST").build(),
+        commandStrategies.put(CommandContext.resource("loans\\/external-id\\/" + UUID_PARAM_REGEX + "\\/transactions\\/external-id\\/"
+                + UUID_PARAM_REGEX + OPTIONAL_QUERY_PARAM_REGEX).method(GET).build(), "getTransactionByExternalIdCommandStrategy");
+        commandStrategies.put(
+                CommandContext.resource("datatables\\/" + ALPHANUMBERIC_WITH_UNDERSCORE_REGEX + "\\/" + NUMBER_REGEX).method(POST).build(),
                 "createDatatableEntryCommandStrategy");
-        commandStrategies.put(CommandContext.resource("datatables\\/[a-zA-Z0-9_]*\\/\\d+\\/\\d+").method("PUT").build(),
+        commandStrategies.put(
+                CommandContext.resource("datatables\\/" + ALPHANUMBERIC_WITH_UNDERSCORE_REGEX + "\\/" + NUMBER_REGEX + "\\/" + NUMBER_REGEX)
+                        .method(PUT).build(),
                 "updateDatatableEntryOneToManyCommandStrategy");
-        commandStrategies.put(CommandContext.resource("datatables\\/[a-zA-Z0-9_]*\\/\\d+").method("PUT").build(),
+        commandStrategies.put(
+                CommandContext.resource("datatables\\/" + ALPHANUMBERIC_WITH_UNDERSCORE_REGEX + "\\/" + NUMBER_REGEX).method(PUT).build(),
                 "updateDatatableEntryOneToOneCommandStrategy");
         commandStrategies.put(
-                CommandContext.resource("datatables\\/[a-zA-Z0-9_]*\\/\\d+(\\?(\\w+(?:\\=[\\w,]+|&)+)+)").method("GET").build(),
-                "getDatatableEntryByAppTableIdCommandStrategy");
+                CommandContext.resource("datatables\\/" + ALPHANUMBERIC_WITH_UNDERSCORE_REGEX + "\\/" + NUMBER_REGEX + "\\/" + NUMBER_REGEX)
+                        .method(PUT).build(),
+                "updateDatatableEntryOneToManyCommandStrategy");
+        commandStrategies.put(CommandContext
+                .resource("datatables\\/" + ALPHANUMBERIC_WITH_UNDERSCORE_REGEX + "\\/" + NUMBER_REGEX + OPTIONAL_QUERY_PARAM_REGEX)
+                .method(GET).build(), "getDatatableEntryByAppTableIdCommandStrategy");
+        commandStrategies.put(CommandContext.resource("loans\\/" + NUMBER_REGEX + OPTIONAL_COMMAND_PARAM_REGEX).method(PUT).build(),
+                "modifyLoanApplicationCommandStrategy");
+        commandStrategies.put(
+                CommandContext.resource("loans\\/external-id\\/" + UUID_PARAM_REGEX + OPTIONAL_COMMAND_PARAM_REGEX).method(PUT).build(),
+                "modifyLoanApplicationByExternalIdCommandStrategy");
         commandStrategies.put(
-                CommandContext.resource("datatables\\/[a-zA-Z0-9_]*\\/query(\\?(\\w+(?:\\=[\\w,]+|&)+)+)").method("GET").build(),
+                CommandContext.resource("datatables\\/" + ALPHANUMBERIC_WITH_UNDERSCORE_REGEX + "\\/query" + MANDATORY_QUERY_PARAM_REGEX)
+                        .method(GET).build(),
                 "getDatatableEntryByQueryCommandStrategy");
     }
 
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 20ab7e3ea..b0b8eb88b 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
@@ -26,6 +26,7 @@ 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.portfolio.client.api.ClientsApiResource;
+import org.apache.http.HttpStatus;
 import org.springframework.stereotype.Component;
 
 /**
@@ -57,13 +58,13 @@ public class ActivateClientCommandStrategy implements CommandStrategy {
         response.setHeaders(request.getHeaders());
 
         final List<String> pathParameters = Splitter.on('/').splitToList(request.getRelativeUrl());
-        Long clientId = Long.parseLong(pathParameters.get(1).substring(0, pathParameters.get(1).indexOf("?")));
+        final Long clientId = Long.parseLong(pathParameters.get(1).substring(0, pathParameters.get(1).indexOf("?")));
 
         // Calls 'activate' function from 'ClientsApiResource' to activate a
         // client
         responseBody = clientsApiResource.activate(clientId, "activate", request.getBody());
 
-        response.setStatusCode(200);
+        response.setStatusCode(HttpStatus.SC_OK);
         // Sets the body of the response after the successful activation of
         // the client
         response.setBody(responseBody);
diff --git a/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/GetChargeByIdCommandStrategy.java b/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/AdjustChargeByChargeExternalIdCommandStrategy.java
similarity index 53%
copy from fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/GetChargeByIdCommandStrategy.java
copy to fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/AdjustChargeByChargeExternalIdCommandStrategy.java
index 2df109b1d..eb6e4961a 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/GetChargeByIdCommandStrategy.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/AdjustChargeByChargeExternalIdCommandStrategy.java
@@ -20,21 +20,19 @@ package org.apache.fineract.batch.command.internal;
 
 import com.google.common.base.Splitter;
 import java.util.List;
-import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 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.portfolio.loanaccount.api.LoanChargesApiResource;
 import org.apache.http.HttpStatus;
 import org.springframework.stereotype.Component;
 
 /**
- * Implements {@link CommandStrategy} to retrieve a charge by id. It passes the contents of the body from the
+ * Implements {@link CommandStrategy} to adjust a charge using external id. It passes the contents of the body from the
  * BatchRequest to {@link LoanChargesApiResource} and gets back the response. This class will also catch any errors
  * raised by {@link LoanChargesApiResource} and map those errors to appropriate status codes in BatchResponse.
  *
@@ -44,14 +42,15 @@ import org.springframework.stereotype.Component;
  */
 @Component
 @RequiredArgsConstructor
-public class GetChargeByIdCommandStrategy implements CommandStrategy {
+public class AdjustChargeByChargeExternalIdCommandStrategy implements CommandStrategy {
 
+    /**
+     * Loan charges api resource {@link LoanChargesApiResource}.
+     */
     private final LoanChargesApiResource loanChargesApiResource;
 
     @Override
-    public BatchResponse execute(final BatchRequest request, UriInfo uriInfo) {
-        final MutableUriInfo parameterizedUriInfo = new MutableUriInfo(uriInfo);
-
+    public BatchResponse execute(final BatchRequest request, final UriInfo uriInfo) {
         final BatchResponse response = new BatchResponse();
         final String responseBody;
 
@@ -60,26 +59,39 @@ public class GetChargeByIdCommandStrategy implements CommandStrategy {
 
         final String relativeUrl = request.getRelativeUrl();
 
+        // Expected pattern - loans\/external-id\/[\w\d_-]+\/charges\/external-id\/[\w\d_-]+(\?command=[\w]+)?
         // Get the loan and charge ids for use in loanChargesApiResource
         final List<String> pathParameters = Splitter.on('/').splitToList(relativeUrl);
-        final Long loanId = Long.parseLong(pathParameters.get(1));
-        Long chargeId;
-        if (relativeUrl.indexOf('?') > 0) {
-            chargeId = Long.parseLong(StringUtils.substringBeforeLast(pathParameters.get(3), "?"));
-        } else {
-            chargeId = Long.parseLong(pathParameters.get(3));
-        }
+        String loanExternalId = pathParameters.get(2);
+
+        final Pattern commandPattern = Pattern.compile("^?command=[a-zA-Z]+");
+        final Matcher commandMatcher = commandPattern.matcher(pathParameters.get(5));
 
-        Map<String, String> queryParameters;
-        if (relativeUrl.indexOf('?') > 0) {
-            queryParameters = CommandStrategyUtils.getQueryParameters(relativeUrl);
+        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;
+        }
+        final String commandQueryParam = commandMatcher.group(0);
+        final String command = commandQueryParam.substring(commandQueryParam.indexOf("=") + 1);
 
-            // Add the query parameters sent in the relative URL to UriInfo
-            CommandStrategyUtils.addQueryParametersToUriInfo(parameterizedUriInfo, queryParameters);
+        final String chargeExternalIdPathParameter = pathParameters.get(5);
+        String loanChargeExternalId;
+        if (chargeExternalIdPathParameter.contains("?")) {
+            loanChargeExternalId = pathParameters.get(5).substring(0, pathParameters.get(5).indexOf("?"));
+        } else {
+            loanChargeExternalId = pathParameters.get(5);
         }
 
-        responseBody = loanChargesApiResource.retrieveLoanCharge(loanId, chargeId, parameterizedUriInfo);
+        // Calls 'executeLoanCharge' function from 'loanChargesApiResource' using external id
+        responseBody = loanChargesApiResource.executeLoanCharge(loanExternalId, loanChargeExternalId, command, request.getBody());
+
         response.setStatusCode(HttpStatus.SC_OK);
+
+        // Sets the body of the response after retrieving the charge
         response.setBody(responseBody);
 
         return response;
diff --git a/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/CreateTransactionLoanCommandStrategy.java b/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/AdjustChargeCommandStrategy.java
similarity index 53%
copy from fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/CreateTransactionLoanCommandStrategy.java
copy to fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/AdjustChargeCommandStrategy.java
index a192b2624..1ee3f437e 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/CreateTransactionLoanCommandStrategy.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/AdjustChargeCommandStrategy.java
@@ -27,17 +27,14 @@ 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.portfolio.loanaccount.api.LoanTransactionsApiResource;
+import org.apache.fineract.portfolio.loanaccount.api.LoanChargesApiResource;
 import org.apache.http.HttpStatus;
 import org.springframework.stereotype.Component;
 
 /**
- * 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
+ * Implements {@link CommandStrategy} to adjust a charge. It passes the contents of the body from the BatchRequest to
+ * {@link LoanChargesApiResource} and gets back the response. This class will also catch any errors raised by
+ * {@link LoanChargesApiResource} and map those errors to appropriate status codes in BatchResponse.
  *
  * @see CommandStrategy
  * @see BatchRequest
@@ -45,24 +42,29 @@ import org.springframework.stereotype.Component;
  */
 @Component
 @RequiredArgsConstructor
-public class CreateTransactionLoanCommandStrategy implements CommandStrategy {
+public class AdjustChargeCommandStrategy implements CommandStrategy {
 
-    private final LoanTransactionsApiResource loanTransactionsApiResource;
+    /**
+     * Loan charges api resource {@link LoanChargesApiResource}.
+     */
+    private final LoanChargesApiResource loanChargesApiResource;
 
     @Override
-    public BatchResponse execute(BatchRequest request, @SuppressWarnings("unused") UriInfo uriInfo) {
-
+    public BatchResponse execute(final BatchRequest request, final UriInfo uriInfo) {
         final BatchResponse response = new BatchResponse();
         final String responseBody;
 
         response.setRequestId(request.getRequestId());
         response.setHeaders(request.getHeaders());
 
-        final List<String> pathParameters = Splitter.on('/').splitToList(request.getRelativeUrl());
-        Long loanId = Long.parseLong(pathParameters.get(1));
+        final String relativeUrl = request.getRelativeUrl();
+
+        // Get the loan and charge ids for use in loanChargesApiResource
+        final List<String> pathParameters = Splitter.on('/').splitToList(relativeUrl);
+        final Long loanId = Long.parseLong(pathParameters.get(1));
 
-        Pattern commandPattern = Pattern.compile("^?command=[a-zA-Z]+");
-        Matcher commandMatcher = commandPattern.matcher(pathParameters.get(2));
+        final Pattern commandPattern = Pattern.compile("^?command=[a-zA-Z]+");
+        final Matcher commandMatcher = commandPattern.matcher(pathParameters.get(3));
 
         if (!commandMatcher.find()) {
             // This would only occur if the CommandStrategyProvider is incorrectly configured.
@@ -72,14 +74,23 @@ public class CreateTransactionLoanCommandStrategy implements CommandStrategy {
                     "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);
+        final String commandQueryParam = commandMatcher.group(0);
+        final String command = commandQueryParam.substring(commandQueryParam.indexOf("=") + 1);
+
+        final String chargeIdPathParameter = pathParameters.get(3);
+        Long loanChargeId;
+        if (chargeIdPathParameter.contains("?")) {
+            loanChargeId = Long.parseLong(pathParameters.get(3).substring(0, pathParameters.get(3).indexOf("?")));
+        } else {
+            loanChargeId = Long.parseLong(pathParameters.get(3));
+        }
+
+        // Calls 'executeLoanCharge' function from 'loanChargesApiResource'
+        responseBody = loanChargesApiResource.executeLoanCharge(loanId, loanChargeId, command, request.getBody());
 
-        responseBody = loanTransactionsApiResource.executeLoanTransaction(loanId, command, request.getBody());
+        response.setStatusCode(HttpStatus.SC_OK);
 
-        response.setStatusCode(200);
-        // Sets the body of the response after Charge has been successfully
-        // created
+        // Sets the body of the response after retrieving the charge
         response.setBody(responseBody);
 
         return response;
diff --git a/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/AdjustTransactionCommandStrategy.java b/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/AdjustTransactionByExternalIdCommandStrategy.java
similarity index 65%
copy from fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/AdjustTransactionCommandStrategy.java
copy to fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/AdjustTransactionByExternalIdCommandStrategy.java
index 038a9064a..d0d5f21c3 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/AdjustTransactionCommandStrategy.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/AdjustTransactionByExternalIdCommandStrategy.java
@@ -32,9 +32,10 @@ import org.apache.http.HttpStatus;
 import org.springframework.stereotype.Component;
 
 /**
- * Implements {@link CommandStrategy} to adjust a transaction. 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} to adjust a transaction by external id. 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.
  *
  * @see CommandStrategy
  * @see BatchRequest
@@ -42,12 +43,15 @@ import org.springframework.stereotype.Component;
  */
 @Component
 @RequiredArgsConstructor
-public class AdjustTransactionCommandStrategy implements CommandStrategy {
+public class AdjustTransactionByExternalIdCommandStrategy implements CommandStrategy {
 
+    /**
+     * Loan transactions api resource {@link LoanTransactionsApiResource}.
+     */
     private final LoanTransactionsApiResource loanTransactionsApiResource;
 
     @Override
-    public BatchResponse execute(final BatchRequest request, UriInfo uriInfo) {
+    public BatchResponse execute(final BatchRequest request, final UriInfo uriInfo) {
         final BatchResponse response = new BatchResponse();
         final String responseBody;
 
@@ -56,23 +60,24 @@ public class AdjustTransactionCommandStrategy implements CommandStrategy {
 
         final String relativeUrl = request.getRelativeUrl();
 
+        // Expected URL pattern - loans\/external-id\/[\w\d_-]+\/transactions\/external-id\/[\w\d_-]+(\?command=[\w]+)?
         // Get the loan and transaction ids for use in loanTransactionsApiResource
         final List<String> pathParameters = Splitter.on('/').splitToList(relativeUrl);
-        Long loanId = Long.parseLong(pathParameters.get(1));
+        final String loanExternalId = pathParameters.get(2);
 
-        final String transactionIdPathParameter = pathParameters.get(3);
-        Long transactionId;
+        final String transactionIdPathParameter = pathParameters.get(5);
+        String transactionExternalId;
         if (transactionIdPathParameter.contains("?")) {
-            transactionId = Long.parseLong(pathParameters.get(3).substring(0, pathParameters.get(3).indexOf("?")));
+            transactionExternalId = transactionIdPathParameter.substring(0, transactionIdPathParameter.indexOf("?"));
         } else {
-            transactionId = Long.parseLong(pathParameters.get(3));
+            transactionExternalId = transactionIdPathParameter;
         }
 
-        Map<String, String> queryParameters = CommandStrategyUtils.getQueryParameters(relativeUrl);
-        String command = queryParameters.get("command");
+        final Map<String, String> queryParameters = CommandStrategyUtils.getQueryParameters(relativeUrl);
+        final String command = queryParameters.get("command");
 
-        // Calls 'adjustLoanTransaction' function from 'loanTransactionsApiResource'
-        responseBody = loanTransactionsApiResource.adjustLoanTransaction(loanId, transactionId, request.getBody(), command);
+        // Calls 'adjustLoanTransaction' function from 'loanTransactionsApiResource' using external-id
+        responseBody = loanTransactionsApiResource.adjustLoanTransaction(loanExternalId, transactionExternalId, request.getBody(), command);
 
         response.setStatusCode(HttpStatus.SC_OK);
 
diff --git a/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/AdjustTransactionCommandStrategy.java b/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/AdjustTransactionCommandStrategy.java
index 038a9064a..572ff88bd 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/AdjustTransactionCommandStrategy.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/AdjustTransactionCommandStrategy.java
@@ -44,10 +44,13 @@ import org.springframework.stereotype.Component;
 @RequiredArgsConstructor
 public class AdjustTransactionCommandStrategy implements CommandStrategy {
 
+    /**
+     * Loan transactions api resource {@link LoanTransactionsApiResource}.
+     */
     private final LoanTransactionsApiResource loanTransactionsApiResource;
 
     @Override
-    public BatchResponse execute(final BatchRequest request, UriInfo uriInfo) {
+    public BatchResponse execute(final BatchRequest request, final UriInfo uriInfo) {
         final BatchResponse response = new BatchResponse();
         final String responseBody;
 
@@ -58,7 +61,7 @@ public class AdjustTransactionCommandStrategy implements CommandStrategy {
 
         // Get the loan and transaction ids for use in loanTransactionsApiResource
         final List<String> pathParameters = Splitter.on('/').splitToList(relativeUrl);
-        Long loanId = Long.parseLong(pathParameters.get(1));
+        final Long loanId = Long.parseLong(pathParameters.get(1));
 
         final String transactionIdPathParameter = pathParameters.get(3);
         Long transactionId;
@@ -68,8 +71,8 @@ public class AdjustTransactionCommandStrategy implements CommandStrategy {
             transactionId = Long.parseLong(pathParameters.get(3));
         }
 
-        Map<String, String> queryParameters = CommandStrategyUtils.getQueryParameters(relativeUrl);
-        String command = queryParameters.get("command");
+        final Map<String, String> queryParameters = CommandStrategyUtils.getQueryParameters(relativeUrl);
+        final String command = queryParameters.get("command");
 
         // Calls 'adjustLoanTransaction' function from 'loanTransactionsApiResource'
         responseBody = loanTransactionsApiResource.adjustLoanTransaction(loanId, transactionId, request.getBody(), command);
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 c6e9639b5..946fc2036 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
@@ -24,6 +24,7 @@ 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.portfolio.loanaccount.api.LoansApiResource;
+import org.apache.http.HttpStatus;
 import org.springframework.stereotype.Component;
 
 /**
@@ -58,7 +59,7 @@ public class ApplyLoanCommandStrategy implements CommandStrategy {
         // Apply Loan to an existing client
         responseBody = loansApiResource.calculateLoanScheduleOrSubmitLoanApplication(null, null, request.getBody());
 
-        response.setStatusCode(200);
+        response.setStatusCode(HttpStatus.SC_OK);
         // Sets the body of the response after loan is successfully applied
         response.setBody(responseBody);
 
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 a5c2a914f..17d069d47 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
@@ -24,6 +24,7 @@ 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.portfolio.savings.api.SavingsAccountsApiResource;
+import org.apache.http.HttpStatus;
 import org.springframework.stereotype.Component;
 
 /**
@@ -59,7 +60,7 @@ public class ApplySavingsCommandStrategy implements CommandStrategy {
         // client
         responseBody = savingsAccountsApiResource.submitApplication(request.getBody());
 
-        response.setStatusCode(200);
+        response.setStatusCode(HttpStatus.SC_OK);
         // Sets the body of the response after savings is successfully
         // applied
         response.setBody(responseBody);
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 c517e4119..ef98ff6c1 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
@@ -26,6 +26,7 @@ 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.portfolio.loanaccount.api.LoansApiResource;
+import org.apache.http.HttpStatus;
 import org.springframework.stereotype.Component;
 
 /**
@@ -57,13 +58,13 @@ public class ApproveLoanCommandStrategy implements CommandStrategy {
         response.setHeaders(request.getHeaders());
 
         final List<String> pathParameters = Splitter.on('/').splitToList(request.getRelativeUrl());
-        Long loanId = Long.parseLong(pathParameters.get(1).substring(0, pathParameters.get(1).indexOf("?")));
+        final Long loanId = Long.parseLong(pathParameters.get(1).substring(0, pathParameters.get(1).indexOf("?")));
 
         // Calls 'approve' function from 'LoansApiResource' to approve a
         // loan
         responseBody = loansApiResource.stateTransitions(loanId, "approve", request.getBody());
 
-        response.setStatusCode(200);
+        response.setStatusCode(HttpStatus.SC_OK);
         // Sets the body of the response after the successful approval of a
         // loan
         response.setBody(responseBody);
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 15748e7da..a901015fb 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
@@ -26,6 +26,7 @@ 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.portfolio.loanaccount.rescheduleloan.api.RescheduleLoansApiResource;
+import org.apache.http.HttpStatus;
 import org.springframework.stereotype.Component;
 
 @Component
@@ -35,7 +36,7 @@ public class ApproveLoanRescheduleCommandStrategy implements CommandStrategy {
     private final RescheduleLoansApiResource rescheduleLoansApiResource;
 
     @Override
-    public BatchResponse execute(BatchRequest request, UriInfo uriInfo) {
+    public BatchResponse execute(BatchRequest request, final UriInfo uriInfo) {
         final BatchResponse response = new BatchResponse();
         final String responseBody;
 
@@ -43,14 +44,14 @@ public class ApproveLoanRescheduleCommandStrategy implements CommandStrategy {
         response.setHeaders(request.getHeaders());
 
         final List<String> pathParameters = Splitter.on('/').splitToList(request.getRelativeUrl());
-        Long scheduleId = Long.parseLong(pathParameters.get(1).substring(0, pathParameters.get(1).indexOf("?")));
+        final Long scheduleId = Long.parseLong(pathParameters.get(1).substring(0, pathParameters.get(1).indexOf("?")));
 
         // Calls 'approve' function from 'Loans reschedule Request' to
         // approve a
         // loan
         responseBody = rescheduleLoansApiResource.updateLoanRescheduleRequest(scheduleId, "approve", request.getBody());
 
-        response.setStatusCode(200);
+        response.setStatusCode(HttpStatus.SC_OK);
         // Sets the body of the response after the successful approval of a
         // Loans reschedule Request
         response.setBody(responseBody);
diff --git a/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/GetChargeByIdCommandStrategy.java b/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/CollectChargesByLoanExternalIdCommandStrategy.java
similarity index 69%
copy from fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/GetChargeByIdCommandStrategy.java
copy to fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/CollectChargesByLoanExternalIdCommandStrategy.java
index 2df109b1d..bd411a994 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/GetChargeByIdCommandStrategy.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/CollectChargesByLoanExternalIdCommandStrategy.java
@@ -23,7 +23,6 @@ import java.util.List;
 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;
@@ -34,9 +33,9 @@ import org.apache.http.HttpStatus;
 import org.springframework.stereotype.Component;
 
 /**
- * Implements {@link CommandStrategy} to retrieve a charge by id. It passes the contents of the body from the
- * BatchRequest to {@link LoanChargesApiResource} and gets back the response. This class will also catch any errors
- * raised by {@link LoanChargesApiResource} and map those errors to appropriate status codes in BatchResponse.
+ * Implements {@link CommandStrategy} and Collect Charges for a Loan by external id. It passes the contents of the body
+ * from the BatchRequest to {@link LoanChargesApiResource} and gets back the response. This class will also catch any
+ * errors raised by {@link LoanChargesApiResource} and map those errors to appropriate status codes in BatchResponse.
  *
  * @see CommandStrategy
  * @see BatchRequest
@@ -44,31 +43,27 @@ import org.springframework.stereotype.Component;
  */
 @Component
 @RequiredArgsConstructor
-public class GetChargeByIdCommandStrategy implements CommandStrategy {
+public class CollectChargesByLoanExternalIdCommandStrategy implements CommandStrategy {
 
+    /**
+     * Loan charges api resource {@link LoanChargesApiResource}.
+     */
     private final LoanChargesApiResource loanChargesApiResource;
 
     @Override
-    public BatchResponse execute(final BatchRequest request, UriInfo uriInfo) {
+    public BatchResponse execute(BatchRequest request, final 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();
-
-        // Get the loan and charge ids for use in loanChargesApiResource
+        // Expected pattern - loans\/external-id\/[\w\d_-]+\/charges
         final List<String> pathParameters = Splitter.on('/').splitToList(relativeUrl);
-        final Long loanId = Long.parseLong(pathParameters.get(1));
-        Long chargeId;
-        if (relativeUrl.indexOf('?') > 0) {
-            chargeId = Long.parseLong(StringUtils.substringBeforeLast(pathParameters.get(3), "?"));
-        } else {
-            chargeId = Long.parseLong(pathParameters.get(3));
-        }
+
+        // Pluck out the loanExternalId out of the relative path
+        final String loanExternalId = pathParameters.get(2);
 
         Map<String, String> queryParameters;
         if (relativeUrl.indexOf('?') > 0) {
@@ -78,8 +73,14 @@ public class GetChargeByIdCommandStrategy implements CommandStrategy {
             CommandStrategyUtils.addQueryParametersToUriInfo(parameterizedUriInfo, queryParameters);
         }
 
-        responseBody = loanChargesApiResource.retrieveLoanCharge(loanId, chargeId, parameterizedUriInfo);
+        // Calls 'retrieveAllLoanCharges' function from
+        // 'LoanChargesApiResource' to Collect
+        // Charges for a loan
+        responseBody = loanChargesApiResource.retrieveAllLoanCharges(loanExternalId, parameterizedUriInfo);
+
         response.setStatusCode(HttpStatus.SC_OK);
+        // 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/CollectChargesCommandStrategy.java b/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/CollectChargesCommandStrategy.java
index 593ed0c69..72abb5018 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
@@ -26,6 +26,7 @@ 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.portfolio.loanaccount.api.LoanChargesApiResource;
+import org.apache.http.HttpStatus;
 import org.springframework.stereotype.Component;
 
 /**
@@ -35,8 +36,6 @@ import org.springframework.stereotype.Component;
  * will also catch any errors raised by {@link org.apache.fineract.portfolio.loanaccount.api.LoanChargesApiResource} and
  * map those errors to appropriate status codes in BatchResponse.
  *
- * @author Rishabh Shukla
- *
  * @see org.apache.fineract.batch.command.CommandStrategy
  * @see org.apache.fineract.batch.domain.BatchRequest
  * @see org.apache.fineract.batch.domain.BatchResponse
@@ -45,10 +44,13 @@ import org.springframework.stereotype.Component;
 @RequiredArgsConstructor
 public class CollectChargesCommandStrategy implements CommandStrategy {
 
+    /**
+     * Loan charges api resource {@link LoanChargesApiResource}.
+     */
     private final LoanChargesApiResource loanChargesApiResource;
 
     @Override
-    public BatchResponse execute(BatchRequest request, UriInfo uriInfo) {
+    public BatchResponse execute(BatchRequest request, final UriInfo uriInfo) {
 
         final BatchResponse response = new BatchResponse();
         final String responseBody;
@@ -59,14 +61,14 @@ public class CollectChargesCommandStrategy implements CommandStrategy {
         final List<String> pathParameters = Splitter.on('/').splitToList(request.getRelativeUrl());
 
         // Pluck out the loanId out of the relative path
-        Long loanId = Long.parseLong(pathParameters.get(1));
+        final Long loanId = Long.parseLong(pathParameters.get(1));
 
         // Calls 'retrieveAllLoanCharges' function from
         // 'LoanChargesApiResource' to Collect
         // Charges for a loan
         responseBody = loanChargesApiResource.retrieveAllLoanCharges(loanId, uriInfo);
 
-        response.setStatusCode(200);
+        response.setStatusCode(HttpStatus.SC_OK);
         // Sets the body of the response after Charges have been
         // successfully collected
         response.setBody(responseBody);
diff --git a/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/GetChargeByIdCommandStrategy.java b/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/CreateChargeByLoanExternalIdCommandStrategy.java
similarity index 57%
copy from fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/GetChargeByIdCommandStrategy.java
copy to fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/CreateChargeByLoanExternalIdCommandStrategy.java
index 2df109b1d..b935cd0a4 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/GetChargeByIdCommandStrategy.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/CreateChargeByLoanExternalIdCommandStrategy.java
@@ -23,20 +23,18 @@ import java.util.List;
 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.portfolio.loanaccount.api.LoanChargesApiResource;
 import org.apache.http.HttpStatus;
 import org.springframework.stereotype.Component;
 
 /**
- * Implements {@link CommandStrategy} to retrieve a charge by id. It passes the contents of the body from the
- * BatchRequest to {@link LoanChargesApiResource} and gets back the response. This class will also catch any errors
- * raised by {@link LoanChargesApiResource} and map those errors to appropriate status codes in BatchResponse.
+ * Implements {@link CommandStrategy} and Create Charge for a Loan by external id. It passes the contents of the body
+ * from the BatchRequest to {@link LoanChargesApiResource} and gets back the response. This class will also catch any
+ * errors raised by {@link LoanChargesApiResource} and map those errors to appropriate status codes in BatchResponse.
  *
  * @see CommandStrategy
  * @see BatchRequest
@@ -44,13 +42,15 @@ import org.springframework.stereotype.Component;
  */
 @Component
 @RequiredArgsConstructor
-public class GetChargeByIdCommandStrategy implements CommandStrategy {
+public class CreateChargeByLoanExternalIdCommandStrategy implements CommandStrategy {
 
+    /**
+     * Loan charges api resource {@link LoanChargesApiResource}.
+     */
     private final LoanChargesApiResource loanChargesApiResource;
 
     @Override
-    public BatchResponse execute(final BatchRequest request, UriInfo uriInfo) {
-        final MutableUriInfo parameterizedUriInfo = new MutableUriInfo(uriInfo);
+    public BatchResponse execute(BatchRequest request, @SuppressWarnings("unused") final UriInfo uriInfo) {
 
         final BatchResponse response = new BatchResponse();
         final String responseBody;
@@ -60,26 +60,26 @@ public class GetChargeByIdCommandStrategy implements CommandStrategy {
 
         final String relativeUrl = request.getRelativeUrl();
 
-        // Get the loan and charge ids for use in loanChargesApiResource
+        // Expected pattern - loans\/external-id\/[\w\d_-]+\/charges
         final List<String> pathParameters = Splitter.on('/').splitToList(relativeUrl);
-        final Long loanId = Long.parseLong(pathParameters.get(1));
-        Long chargeId;
-        if (relativeUrl.indexOf('?') > 0) {
-            chargeId = Long.parseLong(StringUtils.substringBeforeLast(pathParameters.get(3), "?"));
+        final String loanExternalIdPathParameter = pathParameters.get(2);
+        String loanExternalId;
+        if (loanExternalIdPathParameter.contains("?")) {
+            loanExternalId = pathParameters.get(2).substring(0, pathParameters.get(2).indexOf("?"));
         } else {
-            chargeId = Long.parseLong(pathParameters.get(3));
+            loanExternalId = pathParameters.get(2);
         }
 
-        Map<String, String> queryParameters;
-        if (relativeUrl.indexOf('?') > 0) {
-            queryParameters = CommandStrategyUtils.getQueryParameters(relativeUrl);
+        final Map<String, String> queryParameters = CommandStrategyUtils.getQueryParameters(relativeUrl);
+        final String command = queryParameters.get("command");
 
-            // Add the query parameters sent in the relative URL to UriInfo
-            CommandStrategyUtils.addQueryParametersToUriInfo(parameterizedUriInfo, queryParameters);
-        }
+        // Calls 'executeLoanCharge' function from 'LoanChargesApiResource'
+        // to create a new charge for based on loan external id
+        responseBody = loanChargesApiResource.executeLoanCharge(loanExternalId, command, request.getBody());
 
-        responseBody = loanChargesApiResource.retrieveLoanCharge(loanId, chargeId, parameterizedUriInfo);
         response.setStatusCode(HttpStatus.SC_OK);
+        // 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/CreateChargeCommandStrategy.java b/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/CreateChargeCommandStrategy.java
index 7e7be136e..470f284c9 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
@@ -26,6 +26,7 @@ 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.portfolio.loanaccount.api.LoanChargesApiResource;
+import org.apache.http.HttpStatus;
 import org.springframework.stereotype.Component;
 
 /**
@@ -57,14 +58,14 @@ public class CreateChargeCommandStrategy implements CommandStrategy {
         response.setHeaders(request.getHeaders());
 
         final List<String> pathParameters = Splitter.on('/').splitToList(request.getRelativeUrl());
-        Long loanId = Long.parseLong(pathParameters.get(1));
+        final Long loanId = Long.parseLong(pathParameters.get(1));
 
         // Calls 'executeLoanCharge' function from 'LoanChargesApiResource'
         // to create
         // a new charge for a loan
         responseBody = loanChargesApiResource.executeLoanCharge(loanId, null, request.getBody());
 
-        response.setStatusCode(200);
+        response.setStatusCode(HttpStatus.SC_OK);
         // Sets the body of the response after Charge has been successfully
         // created
         response.setBody(responseBody);
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 fc0d71757..76bae0f46 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
@@ -24,6 +24,7 @@ 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.portfolio.client.api.ClientsApiResource;
+import org.apache.http.HttpStatus;
 import org.springframework.stereotype.Component;
 
 /**
@@ -58,7 +59,7 @@ public class CreateClientCommandStrategy implements CommandStrategy {
         // client
         responseBody = clientsApiResource.create(request.getBody());
 
-        response.setStatusCode(200);
+        response.setStatusCode(HttpStatus.SC_OK);
         // Sets the body of the response after the successful creation of
         // the client
         response.setBody(responseBody);
diff --git a/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/CreateDatatableEntryCommandStrategy.java b/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/CreateDatatableEntryCommandStrategy.java
index e757c073d..5bd41b630 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/CreateDatatableEntryCommandStrategy.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/CreateDatatableEntryCommandStrategy.java
@@ -26,6 +26,7 @@ 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.infrastructure.dataqueries.api.DatatablesApiResource;
+import org.apache.http.HttpStatus;
 import org.springframework.stereotype.Component;
 
 /**
@@ -61,7 +62,7 @@ public class CreateDatatableEntryCommandStrategy implements CommandStrategy {
         // 'DatatablesApiResource' to create a datatable entry on an existing loan
         responseBody = datatablesApiResource.createDatatableEntry(datatableName, loanId, request.getBody());
 
-        response.setStatusCode(200);
+        response.setStatusCode(HttpStatus.SC_OK);
         // Sets the body of the response after datatable entry is successfully
         // created
         response.setBody(responseBody);
diff --git a/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/CreateLoanRescheduleRequestCommandStrategy.java b/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/CreateLoanRescheduleRequestCommandStrategy.java
index 256c5f050..a218593fb 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/CreateLoanRescheduleRequestCommandStrategy.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/CreateLoanRescheduleRequestCommandStrategy.java
@@ -24,6 +24,7 @@ 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.portfolio.loanaccount.rescheduleloan.api.RescheduleLoansApiResource;
+import org.apache.http.HttpStatus;
 import org.springframework.stereotype.Component;
 
 /**
@@ -55,7 +56,7 @@ public class CreateLoanRescheduleRequestCommandStrategy implements CommandStrate
         // 'RescheduleLoansApiResource' to create a reschedule request on an existing loan
         responseBody = rescheduleLoansApiResource.createLoanRescheduleRequest(request.getBody());
 
-        response.setStatusCode(200);
+        response.setStatusCode(HttpStatus.SC_OK);
         // Sets the body of the response after savings is successfully
         // applied
         response.setBody(responseBody);
diff --git a/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/CreateTransactionLoanCommandStrategy.java b/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/CreateTransactionByLoanExternalIdCommandStrategy.java
similarity index 71%
copy from fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/CreateTransactionLoanCommandStrategy.java
copy to fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/CreateTransactionByLoanExternalIdCommandStrategy.java
index a192b2624..550009817 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/CreateTransactionLoanCommandStrategy.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/CreateTransactionByLoanExternalIdCommandStrategy.java
@@ -32,12 +32,10 @@ import org.apache.http.HttpStatus;
 import org.springframework.stereotype.Component;
 
 /**
- * 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
+ * Implements {@link CommandStrategy} and handles the creation of a transaction for a Loan using External Id. 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.
  *
  * @see CommandStrategy
  * @see BatchRequest
@@ -45,8 +43,11 @@ import org.springframework.stereotype.Component;
  */
 @Component
 @RequiredArgsConstructor
-public class CreateTransactionLoanCommandStrategy implements CommandStrategy {
+public class CreateTransactionByLoanExternalIdCommandStrategy implements CommandStrategy {
 
+    /**
+     * Loan transactions api resource {@link LoanTransactionsApiResource}.
+     */
     private final LoanTransactionsApiResource loanTransactionsApiResource;
 
     @Override
@@ -58,11 +59,12 @@ public class CreateTransactionLoanCommandStrategy implements CommandStrategy {
         response.setRequestId(request.getRequestId());
         response.setHeaders(request.getHeaders());
 
+        // Expected Pattern - loans\/external-id\/[a-zA-Z0-9_-]*\/transactions\?command=[\w]+
         final List<String> pathParameters = Splitter.on('/').splitToList(request.getRelativeUrl());
-        Long loanId = Long.parseLong(pathParameters.get(1));
+        final String loanExternalId = pathParameters.get(2);
 
-        Pattern commandPattern = Pattern.compile("^?command=[a-zA-Z]+");
-        Matcher commandMatcher = commandPattern.matcher(pathParameters.get(2));
+        final Pattern commandPattern = Pattern.compile("^?command=[a-zA-Z]+");
+        final Matcher commandMatcher = commandPattern.matcher(pathParameters.get(3));
 
         if (!commandMatcher.find()) {
             // This would only occur if the CommandStrategyProvider is incorrectly configured.
@@ -72,12 +74,12 @@ public class CreateTransactionLoanCommandStrategy implements CommandStrategy {
                     "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);
+        final String commandQueryParam = commandMatcher.group(0);
+        final String command = commandQueryParam.substring(commandQueryParam.indexOf("=") + 1);
 
-        responseBody = loanTransactionsApiResource.executeLoanTransaction(loanId, command, request.getBody());
+        responseBody = loanTransactionsApiResource.executeLoanTransaction(loanExternalId, command, request.getBody());
 
-        response.setStatusCode(200);
+        response.setStatusCode(HttpStatus.SC_OK);
         // Sets the body of the response after Charge has been successfully
         // created
         response.setBody(responseBody);
diff --git a/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/CreateTransactionLoanCommandStrategy.java b/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/CreateTransactionLoanCommandStrategy.java
index a192b2624..1cb4cac28 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/CreateTransactionLoanCommandStrategy.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/CreateTransactionLoanCommandStrategy.java
@@ -37,8 +37,6 @@ import org.springframework.stereotype.Component;
  * also catch any errors raised by {@link LoanTransactionsApiResource} and map those errors to appropriate status codes
  * in BatchResponse.
  *
- * @author Mohit Sinha
- *
  * @see CommandStrategy
  * @see BatchRequest
  * @see BatchResponse
@@ -47,6 +45,9 @@ import org.springframework.stereotype.Component;
 @RequiredArgsConstructor
 public class CreateTransactionLoanCommandStrategy implements CommandStrategy {
 
+    /**
+     * Loan transactions api resource {@link LoanTransactionsApiResource}.
+     */
     private final LoanTransactionsApiResource loanTransactionsApiResource;
 
     @Override
@@ -59,10 +60,10 @@ public class CreateTransactionLoanCommandStrategy implements CommandStrategy {
         response.setHeaders(request.getHeaders());
 
         final List<String> pathParameters = Splitter.on('/').splitToList(request.getRelativeUrl());
-        Long loanId = Long.parseLong(pathParameters.get(1));
+        final Long loanId = Long.parseLong(pathParameters.get(1));
 
-        Pattern commandPattern = Pattern.compile("^?command=[a-zA-Z]+");
-        Matcher commandMatcher = commandPattern.matcher(pathParameters.get(2));
+        final Pattern commandPattern = Pattern.compile("^?command=[a-zA-Z]+");
+        final Matcher commandMatcher = commandPattern.matcher(pathParameters.get(2));
 
         if (!commandMatcher.find()) {
             // This would only occur if the CommandStrategyProvider is incorrectly configured.
@@ -72,12 +73,12 @@ public class CreateTransactionLoanCommandStrategy implements CommandStrategy {
                     "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);
+        final String commandQueryParam = commandMatcher.group(0);
+        final String command = commandQueryParam.substring(commandQueryParam.indexOf("=") + 1);
 
         responseBody = loanTransactionsApiResource.executeLoanTransaction(loanId, command, request.getBody());
 
-        response.setStatusCode(200);
+        response.setStatusCode(HttpStatus.SC_OK);
         // Sets the body of the response after Charge has been successfully
         // created
         response.setBody(responseBody);
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 cffd07bd2..88fa9d60a 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
@@ -26,6 +26,7 @@ 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.portfolio.loanaccount.api.LoansApiResource;
+import org.apache.http.HttpStatus;
 import org.springframework.stereotype.Component;
 
 /**
@@ -57,13 +58,13 @@ public class DisburseLoanCommandStrategy implements CommandStrategy {
         response.setHeaders(request.getHeaders());
 
         final List<String> pathParameters = Splitter.on('/').splitToList(request.getRelativeUrl());
-        Long loanId = Long.parseLong(pathParameters.get(1).substring(0, pathParameters.get(1).indexOf("?")));
+        final Long loanId = Long.parseLong(pathParameters.get(1).substring(0, pathParameters.get(1).indexOf("?")));
 
         // Calls 'disburse' function from 'LoansApiResource' to disburse a
         // loan
         responseBody = loansApiResource.stateTransitions(loanId, "disburse", request.getBody());
 
-        response.setStatusCode(200);
+        response.setStatusCode(HttpStatus.SC_OK);
 
         // Sets the body of the response after the successful disbursal of
         // the loan
diff --git a/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/GetChargeByIdCommandStrategy.java b/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/GetChargeByChargeExternalIdCommandStrategy.java
similarity index 78%
copy from fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/GetChargeByIdCommandStrategy.java
copy to fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/GetChargeByChargeExternalIdCommandStrategy.java
index 2df109b1d..2ca8f6321 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/GetChargeByIdCommandStrategy.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/GetChargeByChargeExternalIdCommandStrategy.java
@@ -34,7 +34,7 @@ import org.apache.http.HttpStatus;
 import org.springframework.stereotype.Component;
 
 /**
- * Implements {@link CommandStrategy} to retrieve a charge by id. It passes the contents of the body from the
+ * Implements {@link CommandStrategy} to retrieve a charge by external id. It passes the contents of the body from the
  * BatchRequest to {@link LoanChargesApiResource} and gets back the response. This class will also catch any errors
  * raised by {@link LoanChargesApiResource} and map those errors to appropriate status codes in BatchResponse.
  *
@@ -44,12 +44,15 @@ import org.springframework.stereotype.Component;
  */
 @Component
 @RequiredArgsConstructor
-public class GetChargeByIdCommandStrategy implements CommandStrategy {
+public class GetChargeByChargeExternalIdCommandStrategy implements CommandStrategy {
 
+    /**
+     * Loan charges api resource {@link LoanChargesApiResource}.
+     */
     private final LoanChargesApiResource loanChargesApiResource;
 
     @Override
-    public BatchResponse execute(final BatchRequest request, UriInfo uriInfo) {
+    public BatchResponse execute(final BatchRequest request, final UriInfo uriInfo) {
         final MutableUriInfo parameterizedUriInfo = new MutableUriInfo(uriInfo);
 
         final BatchResponse response = new BatchResponse();
@@ -60,14 +63,15 @@ public class GetChargeByIdCommandStrategy implements CommandStrategy {
 
         final String relativeUrl = request.getRelativeUrl();
 
-        // Get the loan and charge ids for use in loanChargesApiResource
+        // Expected pattern - loans\/external-id\/[\w\d_-]+\/charges\/external-id\/[\w\d_-]+
+        // Get the loan and charge external ids for use in loanChargesApiResource
         final List<String> pathParameters = Splitter.on('/').splitToList(relativeUrl);
-        final Long loanId = Long.parseLong(pathParameters.get(1));
-        Long chargeId;
+        final String loanExternalId = pathParameters.get(2);
+        String chargeExternalId;
         if (relativeUrl.indexOf('?') > 0) {
-            chargeId = Long.parseLong(StringUtils.substringBeforeLast(pathParameters.get(3), "?"));
+            chargeExternalId = StringUtils.substringBeforeLast(pathParameters.get(5), "?");
         } else {
-            chargeId = Long.parseLong(pathParameters.get(3));
+            chargeExternalId = pathParameters.get(5);
         }
 
         Map<String, String> queryParameters;
@@ -78,7 +82,7 @@ public class GetChargeByIdCommandStrategy implements CommandStrategy {
             CommandStrategyUtils.addQueryParametersToUriInfo(parameterizedUriInfo, queryParameters);
         }
 
-        responseBody = loanChargesApiResource.retrieveLoanCharge(loanId, chargeId, parameterizedUriInfo);
+        responseBody = loanChargesApiResource.retrieveLoanCharge(loanExternalId, chargeExternalId, parameterizedUriInfo);
         response.setStatusCode(HttpStatus.SC_OK);
         response.setBody(responseBody);
 
diff --git a/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/GetChargeByIdCommandStrategy.java b/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/GetChargeByIdCommandStrategy.java
index 2df109b1d..0fd4d3036 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/GetChargeByIdCommandStrategy.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/GetChargeByIdCommandStrategy.java
@@ -46,10 +46,13 @@ import org.springframework.stereotype.Component;
 @RequiredArgsConstructor
 public class GetChargeByIdCommandStrategy implements CommandStrategy {
 
+    /**
+     * Loan charges api resource {@link LoanChargesApiResource}.
+     */
     private final LoanChargesApiResource loanChargesApiResource;
 
     @Override
-    public BatchResponse execute(final BatchRequest request, UriInfo uriInfo) {
+    public BatchResponse execute(final BatchRequest request, final UriInfo uriInfo) {
         final MutableUriInfo parameterizedUriInfo = new MutableUriInfo(uriInfo);
 
         final BatchResponse response = new BatchResponse();
diff --git a/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/GetLoanByExternalIdCommandStrategy.java b/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/GetLoanByExternalIdCommandStrategy.java
new file mode 100644
index 000000000..9daac5f8c
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/GetLoanByExternalIdCommandStrategy.java
@@ -0,0 +1,152 @@
+/**
+ * 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 com.google.common.base.Splitter;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import javax.ws.rs.core.UriInfo;
+import lombok.RequiredArgsConstructor;
+import org.apache.commons.lang3.BooleanUtils;
+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.infrastructure.core.api.MutableUriInfo;
+import org.apache.fineract.portfolio.loanaccount.api.LoansApiResource;
+import org.apache.http.HttpStatus;
+import org.springframework.stereotype.Component;
+
+/**
+ * Implements {@link CommandStrategy} and get loan by external id. It passes the contents of the body from the
+ * BatchRequest to {@link LoansApiResource} and gets back the response. This class will also catch any errors raised by
+ * {@link LoansApiResource} and map those errors to appropriate status codes in BatchResponse.
+ */
+@Component
+@RequiredArgsConstructor
+public class GetLoanByExternalIdCommandStrategy implements CommandStrategy {
+
+    /**
+     * Loans api resource {@link LoansApiResource}.
+     */
+    private final LoansApiResource loansApiResource;
+
+    @Override
+    public BatchResponse execute(final BatchRequest request, final UriInfo uriInfo) {
+        final MutableUriInfo parameterizedUriInfo = new MutableUriInfo(uriInfo);
+
+        final BatchResponse response = new BatchResponse();
+        final String responseBody;
+
+        response.setRequestId(request.getRequestId());
+        response.setHeaders(request.getHeaders());
+
+        // Expected pattern - loans\/external-id\/[\w\d_-]+\?associations=transactions
+        final String relativeUrl = request.getRelativeUrl();
+        final List<String> pathParameters = Splitter.on('/').splitToList(relativeUrl);
+        final String loanExternalIdPathParameter = pathParameters.get(2);
+
+        String loanExternalId;
+
+        // 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/external-id/ff62fc65-1bba-4bb0-b090-5f9ecf0a66f1?fields=id,principal,annualInterestRate)
+        // - Add them to the UriInfo query parameters list
+        // - Call loansApiResource.retrieveLoan(loanExternalId, false, uriInfo)
+        // - Remove the relative url query parameters from UriInfo in the finally (after loan details are retrieved)
+        Map<String, String> queryParameters = null;
+        if (loanExternalIdPathParameter.indexOf('?') > 0) {
+            loanExternalId = StringUtils.substringBefore(loanExternalIdPathParameter, "?");
+            queryParameters = getQueryParameters(relativeUrl);
+
+            // Add the query parameters sent in the relative URL to UriInfo
+            addQueryParametersToUriInfo(parameterizedUriInfo, queryParameters);
+        } else {
+            loanExternalId = loanExternalIdPathParameter;
+        }
+
+        // Calls 'retrieveLoan' function from 'LoansApiResource' to
+        // get the loan details based on the loan external id
+        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");
+            }
+            if (queryParameters.containsKey("staffInSelectedOfficeOnly")) {
+                staffInSelectedOfficeOnly = BooleanUtils.toBoolean(queryParameters.get("staffInSelectedOfficeOnly"));
+            }
+        }
+
+        responseBody = loansApiResource.retrieveLoan(loanExternalId, staffInSelectedOfficeOnly, associations, exclude, fields,
+                parameterizedUriInfo);
+
+        response.setStatusCode(HttpStatus.SC_OK);
+
+        // Sets the response after retrieving the loan
+        response.setBody(responseBody);
+
+        return response;
+    }
+
+    /**
+     * Get query parameters from relative URL.
+     *
+     * @param relativeUrl
+     *            the relative URL
+     * @return the query parameters in a map
+     */
+    private Map<String, String> getQueryParameters(final String relativeUrl) {
+        final String queryParameterStr = StringUtils.substringAfter(relativeUrl, "?");
+        final String[] queryParametersArray = StringUtils.split(queryParameterStr, "&");
+        final Map<String, String> queryParametersMap = new HashMap<>();
+        for (String parameterStr : queryParametersArray) {
+            String[] keyValue = StringUtils.split(parameterStr, "=");
+            queryParametersMap.put(keyValue[0], keyValue[1]);
+        }
+        return queryParametersMap;
+    }
+
+    /**
+     * Add query parameters(received in the relative URL) to URI info query parameters.
+     *
+     * @param uriInfo
+     *            the URI info
+     * @param queryParameters
+     *            the query parameters
+     */
+    private void addQueryParametersToUriInfo(final MutableUriInfo uriInfo, final Map<String, String> queryParameters) {
+        for (Map.Entry<String, String> entry : queryParameters.entrySet()) {
+            uriInfo.addAdditionalQueryParameter(entry.getKey(), entry.getValue());
+        }
+    }
+}
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/GetTransactionByExternalIdCommandStrategy.java
similarity index 75%
copy from fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/GetTransactionByIdCommandStrategy.java
copy to fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/GetTransactionByExternalIdCommandStrategy.java
index bec9b431c..12a5505fc 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/GetTransactionByExternalIdCommandStrategy.java
@@ -35,9 +35,10 @@ import org.apache.http.HttpStatus;
 import org.springframework.stereotype.Component;
 
 /**
- * Implements {@link CommandStrategy} to retrieve a transaction by id. 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} to retrieve a transaction by external id. 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.
  *
  * @see CommandStrategy
  * @see BatchRequest
@@ -45,12 +46,15 @@ import org.springframework.stereotype.Component;
  */
 @Component
 @RequiredArgsConstructor
-public class GetTransactionByIdCommandStrategy implements CommandStrategy {
+public class GetTransactionByExternalIdCommandStrategy implements CommandStrategy {
 
+    /**
+     * Loan transactions api resource {@link LoanTransactionsApiResource}.
+     */
     private final LoanTransactionsApiResource loanTransactionsApiResource;
 
     @Override
-    public BatchResponse execute(final BatchRequest request, UriInfo uriInfo) {
+    public BatchResponse execute(final BatchRequest request, final UriInfo uriInfo) {
         final MutableUriInfo parameterizedUriInfo = new MutableUriInfo(uriInfo);
 
         final BatchResponse response = new BatchResponse();
@@ -61,14 +65,15 @@ public class GetTransactionByIdCommandStrategy implements CommandStrategy {
 
         final String relativeUrl = request.getRelativeUrl();
 
+        // Expected pattern - loans\/external-id\/[\w\d_-]+\/transactions\/external-id\/[\w\d_-]+
         // Get the loan and transaction ids for use in loanTransactionsApiResource
         final List<String> pathParameters = Splitter.on('/').splitToList(relativeUrl);
-        Long loanId = Long.parseLong(pathParameters.get(1));
-        Long transactionId;
+        final String loanExternalId = pathParameters.get(2);
+        String transactionExternalId;
         if (relativeUrl.indexOf('?') > 0) {
-            transactionId = Long.parseLong(StringUtils.substringBeforeLast(pathParameters.get(3), "?"));
+            transactionExternalId = StringUtils.substringBeforeLast(pathParameters.get(5), "?");
         } else {
-            transactionId = Long.parseLong(pathParameters.get(3));
+            transactionExternalId = pathParameters.get(5);
         }
 
         Map<String, String> queryParameters = new HashMap<>();
@@ -86,8 +91,9 @@ public class GetTransactionByIdCommandStrategy implements CommandStrategy {
             }
         }
 
-        // Calls 'retrieveTransaction' function from 'loanTransactionsApiResource'
-        responseBody = loanTransactionsApiResource.retrieveTransaction(loanId, transactionId, fields, uriInfo);
+        // Calls 'retrieveTransaction' function from 'loanTransactionsApiResource' using external id
+        responseBody = loanTransactionsApiResource.retrieveTransactionByLoanExternalIdAndTransactionExternalId(loanExternalId,
+                transactionExternalId, fields, uriInfo);
 
         response.setStatusCode(HttpStatus.SC_OK);
 
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 bec9b431c..ea06239c8 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
@@ -47,10 +47,13 @@ import org.springframework.stereotype.Component;
 @RequiredArgsConstructor
 public class GetTransactionByIdCommandStrategy implements CommandStrategy {
 
+    /**
+     * Loan transactions api resource {@link LoanTransactionsApiResource}.
+     */
     private final LoanTransactionsApiResource loanTransactionsApiResource;
 
     @Override
-    public BatchResponse execute(final BatchRequest request, UriInfo uriInfo) {
+    public BatchResponse execute(final BatchRequest request, final UriInfo uriInfo) {
         final MutableUriInfo parameterizedUriInfo = new MutableUriInfo(uriInfo);
 
         final BatchResponse response = new BatchResponse();
@@ -63,7 +66,7 @@ public class GetTransactionByIdCommandStrategy implements CommandStrategy {
 
         // Get the loan and transaction ids for use in loanTransactionsApiResource
         final List<String> pathParameters = Splitter.on('/').splitToList(relativeUrl);
-        Long loanId = Long.parseLong(pathParameters.get(1));
+        final Long loanId = Long.parseLong(pathParameters.get(1));
         Long transactionId;
         if (relativeUrl.indexOf('?') > 0) {
             transactionId = Long.parseLong(StringUtils.substringBeforeLast(pathParameters.get(3), "?"));
diff --git a/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/CreateTransactionLoanCommandStrategy.java b/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/LoanStateTransistionsByExternalIdCommandStrategy.java
similarity index 55%
copy from fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/CreateTransactionLoanCommandStrategy.java
copy to fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/LoanStateTransistionsByExternalIdCommandStrategy.java
index a192b2624..8177ee965 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/CreateTransactionLoanCommandStrategy.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/LoanStateTransistionsByExternalIdCommandStrategy.java
@@ -24,20 +24,18 @@ import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 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.domain.BatchRequest;
 import org.apache.fineract.batch.domain.BatchResponse;
-import org.apache.fineract.portfolio.loanaccount.api.LoanTransactionsApiResource;
+import org.apache.fineract.portfolio.loanaccount.api.LoansApiResource;
 import org.apache.http.HttpStatus;
 import org.springframework.stereotype.Component;
 
 /**
- * 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
+ * Implements {@link CommandStrategy} to handle approval of a pending loan by its external id. It passes the contents of
+ * the body from the BatchRequest to {@link LoansApiResource} and gets back the response. This class will also catch any
+ * errors raised by {@link LoansApiResource} and map those errors to appropriate status codes in BatchResponse.
  *
  * @see CommandStrategy
  * @see BatchRequest
@@ -45,12 +43,15 @@ import org.springframework.stereotype.Component;
  */
 @Component
 @RequiredArgsConstructor
-public class CreateTransactionLoanCommandStrategy implements CommandStrategy {
+public class LoanStateTransistionsByExternalIdCommandStrategy implements CommandStrategy {
 
-    private final LoanTransactionsApiResource loanTransactionsApiResource;
+    /**
+     * Loans api resource {@link LoansApiResource}.
+     */
+    private final LoansApiResource loansApiResource;
 
     @Override
-    public BatchResponse execute(BatchRequest request, @SuppressWarnings("unused") UriInfo uriInfo) {
+    public BatchResponse execute(final BatchRequest request, @SuppressWarnings("unused") final UriInfo uriInfo) {
 
         final BatchResponse response = new BatchResponse();
         final String responseBody;
@@ -58,11 +59,13 @@ public class CreateTransactionLoanCommandStrategy implements CommandStrategy {
         response.setRequestId(request.getRequestId());
         response.setHeaders(request.getHeaders());
 
-        final List<String> pathParameters = Splitter.on('/').splitToList(request.getRelativeUrl());
-        Long loanId = Long.parseLong(pathParameters.get(1));
+        // Expected pattern - loans\/external-id\/[\w\d_-]+\?command=***
+        final String relativeUrl = request.getRelativeUrl();
+        final List<String> pathParameters = Splitter.on('/').splitToList(relativeUrl);
+        final String loanExternalIdPathParameter = pathParameters.get(2);
 
-        Pattern commandPattern = Pattern.compile("^?command=[a-zA-Z]+");
-        Matcher commandMatcher = commandPattern.matcher(pathParameters.get(2));
+        final Pattern commandPattern = Pattern.compile("^?command=[a-zA-Z]+");
+        final Matcher commandMatcher = commandPattern.matcher(loanExternalIdPathParameter);
 
         if (!commandMatcher.find()) {
             // This would only occur if the CommandStrategyProvider is incorrectly configured.
@@ -72,16 +75,19 @@ public class CreateTransactionLoanCommandStrategy implements CommandStrategy {
                     "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);
+        final String commandQueryParam = commandMatcher.group(0);
+        final String command = commandQueryParam.substring(commandQueryParam.indexOf("=") + 1);
+
+        final String loanExternalId = StringUtils.substringBefore(loanExternalIdPathParameter, "?");
 
-        responseBody = loanTransactionsApiResource.executeLoanTransaction(loanId, command, request.getBody());
+        // Calls 'approve'/'disburse' function from 'LoansApiResource' to approve/disburse loan
+        responseBody = loansApiResource.stateTransitions(loanExternalId, command, request.getBody());
 
-        response.setStatusCode(200);
-        // Sets the body of the response after Charge has been successfully
-        // created
+        response.setStatusCode(HttpStatus.SC_OK);
+        // 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/GetChargeByIdCommandStrategy.java b/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/ModifyLoanApplicationByExternalIdCommandStrategy.java
similarity index 53%
copy from fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/GetChargeByIdCommandStrategy.java
copy to fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/ModifyLoanApplicationByExternalIdCommandStrategy.java
index 2df109b1d..9efd78149 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/GetChargeByIdCommandStrategy.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/ModifyLoanApplicationByExternalIdCommandStrategy.java
@@ -28,15 +28,14 @@ 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.portfolio.loanaccount.api.LoanChargesApiResource;
+import org.apache.fineract.portfolio.loanaccount.api.LoansApiResource;
 import org.apache.http.HttpStatus;
 import org.springframework.stereotype.Component;
 
 /**
- * Implements {@link CommandStrategy} to retrieve a charge by id. It passes the contents of the body from the
- * BatchRequest to {@link LoanChargesApiResource} and gets back the response. This class will also catch any errors
- * raised by {@link LoanChargesApiResource} and map those errors to appropriate status codes in BatchResponse.
+ * Implements {@link CommandStrategy} to handle any updates to the loan application. It passes the contents of the body
+ * from the BatchRequest to {@link LoansApiResource} and gets back the response. This class will also catch any errors
+ * raised by {@link LoansApiResource} and map those errors to appropriate status codes in BatchResponse.
  *
  * @see CommandStrategy
  * @see BatchRequest
@@ -44,44 +43,54 @@ import org.springframework.stereotype.Component;
  */
 @Component
 @RequiredArgsConstructor
-public class GetChargeByIdCommandStrategy implements CommandStrategy {
+public class ModifyLoanApplicationByExternalIdCommandStrategy implements CommandStrategy {
 
-    private final LoanChargesApiResource loanChargesApiResource;
+    /**
+     * {@link LoansApiResource} object
+     */
+    private final LoansApiResource loansApiResource;
 
+    /**
+     * Returns {@link BatchResponse} object by taking in and executing {@link BatchRequest} object.
+     *
+     * @param request
+     *            the {@link BatchRequest} object
+     * @param uriInfo
+     *            the {@link UriInfo} object
+     * @return response the {@link BatchResponse} object
+     */
     @Override
-    public BatchResponse execute(final BatchRequest request, UriInfo uriInfo) {
-        final MutableUriInfo parameterizedUriInfo = new MutableUriInfo(uriInfo);
-
+    public BatchResponse execute(final BatchRequest request, final UriInfo uriInfo) {
         final BatchResponse response = new BatchResponse();
         final String responseBody;
 
         response.setRequestId(request.getRequestId());
         response.setHeaders(request.getHeaders());
 
+        // Expected pattern - loans\/external-id\/[\w\d_-]+\?command=adjust
         final String relativeUrl = request.getRelativeUrl();
 
-        // Get the loan and charge ids for use in loanChargesApiResource
+        // Get the loan id for use in loansApiResource
         final List<String> pathParameters = Splitter.on('/').splitToList(relativeUrl);
-        final Long loanId = Long.parseLong(pathParameters.get(1));
-        Long chargeId;
-        if (relativeUrl.indexOf('?') > 0) {
-            chargeId = Long.parseLong(StringUtils.substringBeforeLast(pathParameters.get(3), "?"));
+
+        final String loanIdPathParameter = pathParameters.get(2);
+        String loanExternalId;
+        if (loanIdPathParameter.contains("?")) {
+            loanExternalId = StringUtils.substringBefore(loanIdPathParameter, "?");
         } else {
-            chargeId = Long.parseLong(pathParameters.get(3));
+            loanExternalId = loanIdPathParameter;
         }
 
-        Map<String, String> queryParameters;
-        if (relativeUrl.indexOf('?') > 0) {
-            queryParameters = CommandStrategyUtils.getQueryParameters(relativeUrl);
+        final Map<String, String> queryParameters = CommandStrategyUtils.getQueryParameters(relativeUrl);
+        final String command = queryParameters.get("command");
 
-            // Add the query parameters sent in the relative URL to UriInfo
-            CommandStrategyUtils.addQueryParametersToUriInfo(parameterizedUriInfo, queryParameters);
-        }
+        responseBody = loansApiResource.modifyLoanApplication(loanExternalId, command, request.getBody());
 
-        responseBody = loanChargesApiResource.retrieveLoanCharge(loanId, chargeId, parameterizedUriInfo);
         response.setStatusCode(HttpStatus.SC_OK);
-        response.setBody(responseBody);
 
+        // Sets the body of the response modifying the loan application
+        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/ModifyLoanApplicationCommandStrategy.java
similarity index 56%
copy from fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/ApproveLoanCommandStrategy.java
copy to fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/ModifyLoanApplicationCommandStrategy.java
index c517e4119..922a8cf9e 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/ModifyLoanApplicationCommandStrategy.java
@@ -20,54 +20,77 @@ package org.apache.fineract.batch.command.internal;
 
 import com.google.common.base.Splitter;
 import java.util.List;
+import java.util.Map;
 import javax.ws.rs.core.UriInfo;
 import lombok.RequiredArgsConstructor;
 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.portfolio.loanaccount.api.LoansApiResource;
+import org.apache.http.HttpStatus;
 import org.springframework.stereotype.Component;
 
 /**
- * Implements {@link org.apache.fineract.batch.command.CommandStrategy} to handle approval of a pending loan. It passes
- * the contents of the body from the BatchRequest to
+ * Implements {@link org.apache.fineract.batch.command.CommandStrategy} to handle any updates to the loan application.
+ * It passes the contents of the body from the BatchRequest to
  * {@link org.apache.fineract.portfolio.loanaccount.api.LoansApiResource} and gets back the response. This class will
  * also catch any errors raised by {@link org.apache.fineract.portfolio.loanaccount.api.LoansApiResource} and map those
  * errors to appropriate status codes in BatchResponse.
  *
- * @author Rishabh Shukla
- *
  * @see org.apache.fineract.batch.command.CommandStrategy
  * @see org.apache.fineract.batch.domain.BatchRequest
  * @see org.apache.fineract.batch.domain.BatchResponse
  */
 @Component
 @RequiredArgsConstructor
-public class ApproveLoanCommandStrategy implements CommandStrategy {
+public class ModifyLoanApplicationCommandStrategy implements CommandStrategy {
 
+    /**
+     * {@link LoansApiResource} object
+     */
     private final LoansApiResource loansApiResource;
 
+    /**
+     * Returns {@link org.apache.fineract.batch.domain.BatchResponse} object by taking in and executing
+     * {@link org.apache.fineract.batch.domain.BatchRequest} object.
+     *
+     * @param request
+     *            the {@link org.apache.fineract.batch.domain.BatchRequest} object
+     * @param uriInfo
+     *            the {@link UriInfo} object
+     * @return response the {@link org.apache.fineract.batch.domain.BatchResponse} object
+     */
     @Override
-    public BatchResponse execute(final BatchRequest request, @SuppressWarnings("unused") UriInfo uriInfo) {
-
+    public BatchResponse execute(final BatchRequest request, final UriInfo uriInfo) {
         final BatchResponse response = new BatchResponse();
         final String responseBody;
 
         response.setRequestId(request.getRequestId());
         response.setHeaders(request.getHeaders());
 
-        final List<String> pathParameters = Splitter.on('/').splitToList(request.getRelativeUrl());
-        Long loanId = Long.parseLong(pathParameters.get(1).substring(0, pathParameters.get(1).indexOf("?")));
+        final String relativeUrl = request.getRelativeUrl();
 
-        // Calls 'approve' function from 'LoansApiResource' to approve a
-        // loan
-        responseBody = loansApiResource.stateTransitions(loanId, "approve", request.getBody());
+        // Get the loan id for use in loansApiResource
+        final List<String> pathParameters = Splitter.on('/').splitToList(relativeUrl);
 
-        response.setStatusCode(200);
-        // Sets the body of the response after the successful approval of a
-        // loan
-        response.setBody(responseBody);
+        final String loanIdPathParameter = pathParameters.get(1);
+        Long loanId;
+        if (loanIdPathParameter.contains("?")) {
+            loanId = Long.parseLong(loanIdPathParameter.substring(0, loanIdPathParameter.indexOf("?")));
+        } else {
+            loanId = Long.parseLong(loanIdPathParameter);
+        }
+
+        final Map<String, String> queryParameters = CommandStrategyUtils.getQueryParameters(relativeUrl);
+        final String command = queryParameters.get("command");
 
+        responseBody = loansApiResource.modifyLoanApplication(loanId, command, request.getBody());
+
+        response.setStatusCode(HttpStatus.SC_OK);
+
+        // Sets the body of the response modifying the loan application
+        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 9d0e3aae7..1e53d313f 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
@@ -24,6 +24,7 @@ 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.portfolio.client.api.ClientsApiResource;
+import org.apache.http.HttpStatus;
 import org.springframework.stereotype.Component;
 
 /**
@@ -62,7 +63,7 @@ public class UpdateClientCommandStrategy implements CommandStrategy {
         // client
         responseBody = clientsApiResource.update(clientId, request.getBody());
 
-        response.setStatusCode(200);
+        response.setStatusCode(HttpStatus.SC_OK);
         // Sets the body of the response after the successful update of
         // client information
         response.setBody(responseBody);
diff --git a/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/UpdateDatatableEntryOneToManyCommandStrategy.java b/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/UpdateDatatableEntryOneToManyCommandStrategy.java
index 96e990ccd..de903a371 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/UpdateDatatableEntryOneToManyCommandStrategy.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/UpdateDatatableEntryOneToManyCommandStrategy.java
@@ -26,6 +26,7 @@ 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.infrastructure.dataqueries.api.DatatablesApiResource;
+import org.apache.http.HttpStatus;
 import org.springframework.stereotype.Component;
 
 /**
@@ -62,7 +63,7 @@ public class UpdateDatatableEntryOneToManyCommandStrategy implements CommandStra
         // 'DatatablesApiResource' to update a datatable entry on an existing entity in a one-many relationship
         responseBody = datatablesApiResource.updateDatatableEntryOneToMany(datatableName, entityId, datatableEntryId, request.getBody());
 
-        response.setStatusCode(200);
+        response.setStatusCode(HttpStatus.SC_OK);
         // Sets the body of the response after datatable entry is successfully
         // updated
         response.setBody(responseBody);
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanChargesApiResourceSwagger.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanChargesApiResourceSwagger.java
index f991d4894..17ee409a3 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanChargesApiResourceSwagger.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanChargesApiResourceSwagger.java
@@ -107,6 +107,8 @@ final class LoanChargesApiResourceSwagger {
         public LocalDate submittedOnDate;
         @Schema(example = "95174ff9-1a75-4d72-a413-6f9b1cb988b7")
         public String externalId;
+        @Schema(example = "26 March 2013")
+        public LocalDate dueDate;
     }
 
     @Schema(description = "GetLoansLoanIdChargesTemplateResponse")
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoansApiResourceSwagger.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoansApiResourceSwagger.java
index da2b2801a..caaae16de 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoansApiResourceSwagger.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoansApiResourceSwagger.java
@@ -957,6 +957,8 @@ final class LoansApiResourceSwagger {
         public boolean disallowExpectedDisbursements;
         @Schema(example = "1")
         public Integer clientId;
+        @Schema(example = "5e77989e-aa11-11bc-b109-0242ac120004")
+        public String clientExternalId;
         @Schema(example = "Kampala first Client")
         public String clientName;
         @Schema(example = "2")
@@ -981,6 +983,8 @@ final class LoansApiResourceSwagger {
         public BigDecimal principal;
         @Schema(example = "1000.000000")
         public Double approvedPrincipal;
+        @Schema(example = "1001.000000")
+        public Double proposedPrincipal;
         @Schema(example = "200.000000")
         public Double netDisbursalAmount;
         @Schema(example = "12")
@@ -996,6 +1000,8 @@ final class LoansApiResourceSwagger {
         public GetLoansLoanIdInterestRateFrequencyType interestRateFrequencyType;
         @Schema(example = "24")
         public Integer annualInterestRate;
+        @Schema(example = "false")
+        public Boolean isFloatingInterestRate;
         public GetLoansLoanIdAmortizationType amortizationType;
         @Schema(example = "5.5")
         public BigDecimal fixedPrincipalPercentagePerInstallment;
@@ -1057,8 +1063,8 @@ final class LoansApiResourceSwagger {
         public String locale;
         @Schema(example = "1")
         public Integer productId;
-        @Schema(example = "100,000.00")
-        public String principal;
+        @Schema(example = "1000.00")
+        public BigDecimal principal;
         @Schema(example = "12")
         public Integer loanTermFrequency;
         @Schema(example = "2")
@@ -1163,9 +1169,11 @@ final class LoansApiResourceSwagger {
         @Schema(example = "1")
         public Integer clientId;
         @Schema(example = "1")
-        public Integer loanId;
+        public Long loanId;
         @Schema(example = "1")
-        public Integer resourceId;
+        public Long resourceId;
+        @Schema(example = "95174ff9-1a75-4d72-a413-6f9b1cb988b7")
+        public String resourceExternalId;
     }
 
     @Schema(description = "PutLoansLoanIdRequest")
@@ -1314,9 +1322,9 @@ final class LoansApiResourceSwagger {
         @Schema(example = "1")
         public Integer clientId;
         @Schema(example = "1")
-        public Integer loanId;
+        public Long loanId;
         @Schema(example = "1")
-        public Integer resourceId;
+        public Long resourceId;
         @Schema(example = "95174ff9-1a75-4d72-a413-6f9b1cb988b7")
         public String resourceExternalId;
         public PutLoansLoanIdChanges changes;
@@ -1332,9 +1340,9 @@ final class LoansApiResourceSwagger {
         @Schema(example = "1")
         public Integer clientId;
         @Schema(example = "1")
-        public Integer loanId;
+        public Long loanId;
         @Schema(example = "1")
-        public Integer resourceId;
+        public Long resourceId;
         @Schema(example = "95174ff9-1a75-4d72-a413-6f9b1cb988b7")
         public String resourceExternalId;
     }
@@ -1370,7 +1378,7 @@ final class LoansApiResourceSwagger {
         @Schema(example = "3e7791ce-aa10-11ec-b909-0242ac120002")
         public String externalId;
         @Schema(example = "5000.33")
-        public String transactionAmount;
+        public BigDecimal transactionAmount;
         @Schema(example = "Description of disbursement details.")
         public String note;
         @Schema(example = "28 June 2022")
@@ -1380,7 +1388,7 @@ final class LoansApiResourceSwagger {
         @Schema(example = "28 June 2022")
         public String approvedOnDate;
         @Schema(example = "1000")
-        public String approvedLoanAmount;
+        public BigDecimal approvedLoanAmount;
         @Schema(example = "28 June 2022")
         public String expectedDisbursementDate;
         @Schema(example = "28 June 2022")
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 507e615e9..cd9cff795 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
@@ -25,25 +25,38 @@ import static org.mockito.Mockito.when;
 import java.util.stream.Stream;
 import javax.ws.rs.HttpMethod;
 import org.apache.fineract.batch.command.internal.ActivateClientCommandStrategy;
+import org.apache.fineract.batch.command.internal.AdjustChargeByChargeExternalIdCommandStrategy;
+import org.apache.fineract.batch.command.internal.AdjustChargeCommandStrategy;
+import org.apache.fineract.batch.command.internal.AdjustTransactionByExternalIdCommandStrategy;
 import org.apache.fineract.batch.command.internal.AdjustTransactionCommandStrategy;
 import org.apache.fineract.batch.command.internal.ApplyLoanCommandStrategy;
 import org.apache.fineract.batch.command.internal.ApplySavingsCommandStrategy;
 import org.apache.fineract.batch.command.internal.ApproveLoanCommandStrategy;
 import org.apache.fineract.batch.command.internal.ApproveLoanRescheduleCommandStrategy;
+import org.apache.fineract.batch.command.internal.CollectChargesByLoanExternalIdCommandStrategy;
 import org.apache.fineract.batch.command.internal.CollectChargesCommandStrategy;
+import org.apache.fineract.batch.command.internal.CreateChargeByLoanExternalIdCommandStrategy;
 import org.apache.fineract.batch.command.internal.CreateChargeCommandStrategy;
 import org.apache.fineract.batch.command.internal.CreateClientCommandStrategy;
 import org.apache.fineract.batch.command.internal.CreateDatatableEntryCommandStrategy;
 import org.apache.fineract.batch.command.internal.CreateLoanRescheduleRequestCommandStrategy;
+import org.apache.fineract.batch.command.internal.CreateTransactionByLoanExternalIdCommandStrategy;
 import org.apache.fineract.batch.command.internal.CreateTransactionLoanCommandStrategy;
 import org.apache.fineract.batch.command.internal.DisburseLoanCommandStrategy;
+import org.apache.fineract.batch.command.internal.GetChargeByChargeExternalIdCommandStrategy;
 import org.apache.fineract.batch.command.internal.GetChargeByIdCommandStrategy;
 import org.apache.fineract.batch.command.internal.GetDatatableEntryByAppTableIdCommandStrategy;
+import org.apache.fineract.batch.command.internal.GetDatatableEntryByQueryCommandStrategy;
+import org.apache.fineract.batch.command.internal.GetLoanByExternalIdCommandStrategy;
 import org.apache.fineract.batch.command.internal.GetLoanByIdCommandStrategy;
+import org.apache.fineract.batch.command.internal.GetTransactionByExternalIdCommandStrategy;
 import org.apache.fineract.batch.command.internal.GetTransactionByIdCommandStrategy;
+import org.apache.fineract.batch.command.internal.LoanStateTransistionsByExternalIdCommandStrategy;
+import org.apache.fineract.batch.command.internal.ModifyLoanApplicationCommandStrategy;
 import org.apache.fineract.batch.command.internal.UnknownCommandStrategy;
 import org.apache.fineract.batch.command.internal.UpdateClientCommandStrategy;
 import org.apache.fineract.batch.command.internal.UpdateDatatableEntryOneToManyCommandStrategy;
+import org.apache.fineract.batch.command.internal.UpdateDatatableEntryOneToOneCommandStrategy;
 import org.junit.jupiter.params.ParameterizedTest;
 import org.junit.jupiter.params.provider.Arguments;
 import org.junit.jupiter.params.provider.MethodSource;
@@ -68,12 +81,52 @@ public class CommandStrategyProviderTest {
                         mock(GetLoanByIdCommandStrategy.class)),
                 Arguments.of("loans/123?associations=all&exclude=guarantors", HttpMethod.GET, "getLoanByIdCommandStrategy",
                         mock(GetLoanByIdCommandStrategy.class)),
+                Arguments.of("loans/external-id/8dfad438-2319-48ce-8520-10a62801e9a1", HttpMethod.GET, "getLoanByExternalIdCommandStrategy",
+                        mock(GetLoanByExternalIdCommandStrategy.class)),
+                Arguments.of("loans/external-id/8dfad438-2319-48ce-8520-10a62801e9a1?associations=all", HttpMethod.GET,
+                        "getLoanByExternalIdCommandStrategy", mock(GetLoanByExternalIdCommandStrategy.class)),
+                Arguments.of("loans/external-id/8dfad438-2319-48ce-8520-10a62801e9a1?associations=all&exclude=guarantors", HttpMethod.GET,
+                        "getLoanByExternalIdCommandStrategy", mock(GetLoanByExternalIdCommandStrategy.class)),
                 Arguments.of("savingsaccounts", HttpMethod.POST, "applySavingsCommandStrategy", mock(ApplySavingsCommandStrategy.class)),
                 Arguments.of("loans/123/charges", HttpMethod.POST, "createChargeCommandStrategy", mock(CreateChargeCommandStrategy.class)),
+                Arguments.of("loans/external-id/8dfad438-2319-48ce-8520-10a62801e9a1/charges", HttpMethod.POST,
+                        "createChargeByLoanExternalIdCommandStrategy", mock(CreateChargeByLoanExternalIdCommandStrategy.class)),
+                Arguments.of("loans/external-id/8dfad438-2319-48ce-8520-10a62801e9a1/charges?command=mycommand", HttpMethod.POST,
+                        "createChargeByLoanExternalIdCommandStrategy", mock(CreateChargeByLoanExternalIdCommandStrategy.class)),
                 Arguments.of("loans/123/charges", HttpMethod.GET, "collectChargesCommandStrategy",
                         mock(CollectChargesCommandStrategy.class)),
+                Arguments.of("loans/external-id/8dfad438-2319-48ce-8520-10a62801e9a1/charges", HttpMethod.GET,
+                        "collectChargesByLoanExternalIdCommandStrategy", mock(CollectChargesByLoanExternalIdCommandStrategy.class)),
                 Arguments.of("loans/123/charges/123", HttpMethod.GET, "getChargeByIdCommandStrategy",
                         mock(GetChargeByIdCommandStrategy.class)),
+                Arguments.of(
+                        "loans/external-id/8dfad438-2319-48ce-8520-10a62801e9a1/charges/external-id/7dfad438-2319-48ce-8520-10a62801e9ab",
+                        HttpMethod.GET, "getChargeByChargeExternalIdCommandStrategy",
+                        mock(GetChargeByChargeExternalIdCommandStrategy.class)),
+                Arguments.of(
+                        "loans/external-id/8dfad438-2319-48ce-8520-10a62801e9a1/charges/external-id/7dfad438-2319-48ce-8520-10a62801e9ab?fields=id",
+                        HttpMethod.GET, "getChargeByChargeExternalIdCommandStrategy",
+                        mock(GetChargeByChargeExternalIdCommandStrategy.class)),
+                Arguments.of("loans/123/charges/123?command=adjustment", HttpMethod.POST, "adjustChargeCommandStrategy",
+                        mock(AdjustChargeCommandStrategy.class)),
+                Arguments.of(
+                        "loans/external-id/8dfad438-2319-48ce-8520-10a62801e9a1/charges/external-id/7dfad438-2319-48ce-8520-10a62801e9ab?command=adjustment",
+                        HttpMethod.POST, "adjustChargeByChargeExternalIdCommandStrategy",
+                        mock(AdjustChargeByChargeExternalIdCommandStrategy.class)),
+                Arguments.of("loans/external-id/8dfad438-2319-48ce-8520-10a62801e9a1/transactions?command=repayment", HttpMethod.POST,
+                        "createTransactionByLoanExternalIdCommandStrategy", mock(CreateTransactionByLoanExternalIdCommandStrategy.class)),
+                Arguments.of("loans/external-id/8dfad438-2319-48ce-8520-10a62801e9a1/transactions?command=creditBalanceRefund",
+                        HttpMethod.POST, "createTransactionByLoanExternalIdCommandStrategy",
+                        mock(CreateTransactionByLoanExternalIdCommandStrategy.class)),
+                Arguments.of("loans/external-id/8dfad438-2319-48ce-8520-10a62801e9a1/transactions?command=goodwillCredit", HttpMethod.POST,
+                        "createTransactionByLoanExternalIdCommandStrategy", mock(CreateTransactionByLoanExternalIdCommandStrategy.class)),
+                Arguments.of("loans/external-id/8dfad438-2319-48ce-8520-10a62801e9a1/transactions?command=merchantIssuedRefund",
+                        HttpMethod.POST, "createTransactionByLoanExternalIdCommandStrategy",
+                        mock(CreateTransactionByLoanExternalIdCommandStrategy.class)),
+                Arguments.of("loans/external-id/8dfad438-2319-48ce-8520-10a62801e9a1/transactions?command=payoutRefund", HttpMethod.POST,
+                        "createTransactionByLoanExternalIdCommandStrategy", mock(CreateTransactionByLoanExternalIdCommandStrategy.class)),
+                Arguments.of("loans/external-id/8dfad438-2319-48ce-8520-10a62801e9a1/transactions?command=chargeRefund", HttpMethod.POST,
+                        "createTransactionByLoanExternalIdCommandStrategy", mock(CreateTransactionByLoanExternalIdCommandStrategy.class)),
                 Arguments.of("loans/123/transactions?command=repayment", HttpMethod.POST, "createTransactionLoanCommandStrategy",
                         mock(CreateTransactionLoanCommandStrategy.class)),
                 Arguments.of("loans/123/transactions?command=creditBalanceRefund", HttpMethod.POST, "createTransactionLoanCommandStrategy",
@@ -90,18 +143,33 @@ public class CommandStrategyProviderTest {
                         mock(AdjustTransactionCommandStrategy.class)),
                 Arguments.of("loans/123/transactions/123?command=chargeback", HttpMethod.POST, "adjustTransactionCommandStrategy",
                         mock(AdjustTransactionCommandStrategy.class)),
+                Arguments.of(
+                        "loans/external-id/8dfad438-2319-48ce-8520-10a62801e9a1/transactions/external-id/7dfad438-2319-48ce-8520-10a62801e9ab",
+                        HttpMethod.POST, "adjustTransactionByExternalIdCommandStrategy",
+                        mock(AdjustTransactionByExternalIdCommandStrategy.class)),
+                Arguments.of(
+                        "loans/external-id/8dfad438-2319-48ce-8520-10a62801e9a1/transactions/external-id/7dfad438-2319-48ce-8520-10a62801e9ab?command=chargeback",
+                        HttpMethod.POST, "adjustTransactionByExternalIdCommandStrategy",
+                        mock(AdjustTransactionByExternalIdCommandStrategy.class)),
                 Arguments.of("clients/456?command=activate", HttpMethod.POST, "activateClientCommandStrategy",
                         mock(ActivateClientCommandStrategy.class)),
                 Arguments.of("loans/123?command=approve", HttpMethod.POST, "approveLoanCommandStrategy",
                         mock(ApproveLoanCommandStrategy.class)),
                 Arguments.of("loans/123?command=disburse", HttpMethod.POST, "disburseLoanCommandStrategy",
                         mock(DisburseLoanCommandStrategy.class)),
+                Arguments.of("loans/external-id/8dfad438-2319-48ce-8520-10a62801e9a1?command=approve", HttpMethod.POST,
+                        "loanStateTransistionsByExternalIdCommandStrategy", mock(LoanStateTransistionsByExternalIdCommandStrategy.class)),
+                Arguments.of("loans/external-id/8dfad438-2319-48ce-8520-10a62801e9a1?command=disburse", HttpMethod.POST,
+                        "loanStateTransistionsByExternalIdCommandStrategy", mock(LoanStateTransistionsByExternalIdCommandStrategy.class)),
                 Arguments.of("rescheduleloans", HttpMethod.POST, "createLoanRescheduleRequestCommandStrategy",
                         mock(CreateLoanRescheduleRequestCommandStrategy.class)),
                 Arguments.of("rescheduleloans/123?command=approve", HttpMethod.POST, "approveLoanRescheduleCommandStrategy",
                         mock(ApproveLoanRescheduleCommandStrategy.class)),
                 Arguments.of("loans/123/transactions/123", HttpMethod.GET, "getTransactionByIdCommandStrategy",
                         mock(GetTransactionByIdCommandStrategy.class)),
+                Arguments.of(
+                        "loans/external-id/8dfad438-2319-48ce-8520-10a62801e9a1/transactions/external-id/7dfad438-2319-48ce-8520-10a62801e9ab?fields=id",
+                        HttpMethod.GET, "getTransactionByExternalIdCommandStrategy", mock(GetTransactionByExternalIdCommandStrategy.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,
@@ -109,7 +177,15 @@ public class CommandStrategyProviderTest {
                 Arguments.of("datatables/test_dt_table/123", HttpMethod.POST, "createDatatableEntryCommandStrategy",
                         mock(CreateDatatableEntryCommandStrategy.class)),
                 Arguments.of("datatables/test_dt_table/123/1", HttpMethod.PUT, "updateDatatableEntryOneToManyCommandStrategy",
-                        mock(UpdateDatatableEntryOneToManyCommandStrategy.class)));
+                        mock(UpdateDatatableEntryOneToManyCommandStrategy.class)),
+                Arguments.of("datatables/test_dt_table/123", HttpMethod.PUT, "updateDatatableEntryOneToOneCommandStrategy",
+                        mock(UpdateDatatableEntryOneToOneCommandStrategy.class)),
+                Arguments.of("loans/123?command=markAsFraud", HttpMethod.PUT, "modifyLoanApplicationCommandStrategy",
+                        mock(ModifyLoanApplicationCommandStrategy.class)),
+                Arguments.of("loans/123", HttpMethod.PUT, "modifyLoanApplicationCommandStrategy",
+                        mock(ModifyLoanApplicationCommandStrategy.class)),
+                Arguments.of("datatables/test_dt_table/query?columnFilter=id&valueFilter=12&resultColumns=id", HttpMethod.GET,
+                        "getDatatableEntryByQueryCommandStrategy", mock(GetDatatableEntryByQueryCommandStrategy.class)));
     }
 
     /**
@@ -136,15 +212,22 @@ public class CommandStrategyProviderTest {
     }
 
     /**
-     * Command strategy provider for error scenarioss.
+     * Command strategy provider for error scenarios.
      *
      * @return the test data stream
      */
     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("datatables/test_dt_table", HttpMethod.GET), Arguments.of("datatables", HttpMethod.GET),
-                Arguments.of("loans//charges/123", HttpMethod.GET), Arguments.of("loans/123/charges/", HttpMethod.GET));
+                Arguments.of("loans//charges/123", HttpMethod.GET), Arguments.of("loans/123/charges/", HttpMethod.GET),
+                Arguments.of("loans/123/charges/123", HttpMethod.POST),
+                Arguments.of(
+                        "loans/external-id/8dfad438-2319-48ce-8520-10a62801e9a1/charges/external-id/7dfad438-2319-48ce-8520-10a62801e9ab",
+                        HttpMethod.POST),
+                Arguments.of("loans/external-id/8dfad438-2319-48ce-8520-10a62801e9a1/transactions", HttpMethod.POST),
+                Arguments.of("loans/external-id/8dfad438-2319-48ce-8520-10a62801e9a1", HttpMethod.POST),
+                Arguments.of("datatables/test_dt_table/query", HttpMethod.GET));
 
     }
 
diff --git a/fineract-provider/src/test/java/org/apache/fineract/batch/command/internal/AdjustChargeByChargeExternalIdCommandStrategyTest.java b/fineract-provider/src/test/java/org/apache/fineract/batch/command/internal/AdjustChargeByChargeExternalIdCommandStrategyTest.java
new file mode 100644
index 000000000..8dac1ef83
--- /dev/null
+++ b/fineract-provider/src/test/java/org/apache/fineract/batch/command/internal/AdjustChargeByChargeExternalIdCommandStrategyTest.java
@@ -0,0 +1,158 @@
+/**
+ * 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.mockito.ArgumentMatchers.eq;
+import static org.mockito.BDDMockito.given;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoInteractions;
+
+import java.util.UUID;
+import javax.ws.rs.HttpMethod;
+import javax.ws.rs.core.UriInfo;
+import org.apache.commons.lang3.RandomStringUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.fineract.batch.domain.BatchRequest;
+import org.apache.fineract.batch.domain.BatchResponse;
+import org.apache.fineract.portfolio.loanaccount.api.LoanChargesApiResource;
+import org.apache.http.HttpStatus;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Test class for {@link AdjustChargeByChargeExternalIdCommandStrategy}.
+ */
+public class AdjustChargeByChargeExternalIdCommandStrategyTest {
+
+    /**
+     * Test {@link AdjustChargeByChargeExternalIdCommandStrategy#execute} happy path scenario.
+     *
+     */
+    @Test
+    public void testExecuteAdjustChargeByExternalIdWithCommandSuccessScenario() {
+        // given
+        final TestContext testContext = new TestContext();
+
+        final String loanExternalId = UUID.randomUUID().toString();
+        final String loanChargeExternalId = UUID.randomUUID().toString();
+        final String command = "adjustment";
+        final BatchRequest request = getBatchRequest(loanExternalId, loanChargeExternalId, command);
+        final String responseBody = "{\"loanId\":13,\"resourceId\":16,\"subResourceId\":26,\"changes\":{\"amount\":10.0,"
+                + "\"transactionDate\":[2022,12,7],\"locale\":\"de_DE\"}}";
+
+        given(testContext.loanChargesApiResource.executeLoanCharge(eq(loanExternalId), eq(loanChargeExternalId), eq(command),
+                eq(request.getBody()))).willReturn(responseBody);
+
+        // when
+        final BatchResponse response = testContext.subjectToTest.execute(request, testContext.uriInfo);
+
+        // then
+        assertEquals(HttpStatus.SC_OK, response.getStatusCode());
+        assertEquals(request.getRequestId(), response.getRequestId());
+        assertEquals(request.getHeaders(), response.getHeaders());
+        assertEquals(responseBody, response.getBody());
+        verify(testContext.loanChargesApiResource).executeLoanCharge(eq(loanExternalId), eq(loanChargeExternalId), eq(command),
+                eq(request.getBody()));
+    }
+
+    /**
+     * Test {@link AdjustChargeByChargeExternalIdCommandStrategy#execute} error scenario.
+     *
+     */
+    @Test
+    public void testExecuteAdjustChargeByExternalIdWithoutCommandErrorScenario() {
+        // given
+        final TestContext testContext = new TestContext();
+
+        final String loanExternalId = UUID.randomUUID().toString();
+        final String loanChargeExternalId = UUID.randomUUID().toString();
+        final BatchRequest request = getBatchRequest(loanExternalId, loanChargeExternalId, null);
+
+        // when
+        final BatchResponse response = testContext.subjectToTest.execute(request, testContext.uriInfo);
+
+        // then
+        assertEquals(HttpStatus.SC_NOT_IMPLEMENTED, response.getStatusCode());
+        assertEquals(request.getRequestId(), response.getRequestId());
+        assertEquals("Resource with method " + request.getMethod() + " and relativeUrl " + request.getRelativeUrl() + " doesn't exist",
+                response.getBody());
+        verifyNoInteractions(testContext.loanChargesApiResource);
+    }
+
+    /**
+     * Creates and returns a request with the given loan external id and charge external id.
+     *
+     * @param loanExternalId
+     *            the loan external id
+     * @param chargeExternalId
+     *            the charge external id
+     * @param chargeCommand
+     *            the charge command
+     * @return BatchRequest
+     */
+    private BatchRequest getBatchRequest(final String loanExternalId, final String chargeExternalId, final String chargeCommand) {
+
+        final BatchRequest br = new BatchRequest();
+        String relativeUrl = String.format("loans/external-id/%s/charges/external-id/%s", loanExternalId, chargeExternalId);
+
+        br.setRequestId(Long.valueOf(RandomStringUtils.randomNumeric(5)));
+        br.setRelativeUrl(relativeUrl);
+        if (StringUtils.isNotBlank(chargeCommand)) {
+            br.setRelativeUrl(br.getRelativeUrl() + String.format("?command=%s", chargeCommand));
+        }
+        br.setMethod(HttpMethod.POST);
+        br.setReference(Long.valueOf(RandomStringUtils.randomNumeric(5)));
+        br.setBody("{\"amount\":7.00,\"locale\":\"en\"}");
+
+        return br;
+    }
+
+    /**
+     * Private test context class used since testng runs in parallel to avoid state between tests
+     */
+    private static class TestContext {
+
+        /**
+         * The Mock UriInfo
+         */
+        @Mock
+        private UriInfo uriInfo;
+
+        /**
+         * The Mock {@link LoanChargesApiResource}
+         */
+        @Mock
+        private LoanChargesApiResource loanChargesApiResource;
+
+        /**
+         * The class under test.
+         */
+        private final AdjustChargeByChargeExternalIdCommandStrategy subjectToTest;
+
+        /**
+         * Constructor.
+         */
+        TestContext() {
+            MockitoAnnotations.openMocks(this);
+            subjectToTest = new AdjustChargeByChargeExternalIdCommandStrategy(loanChargesApiResource);
+        }
+    }
+}
diff --git a/fineract-provider/src/test/java/org/apache/fineract/batch/command/internal/AdjustTransactionCommandStrategyTest.java b/fineract-provider/src/test/java/org/apache/fineract/batch/command/internal/AdjustChargeCommandStrategyTest.java
similarity index 51%
copy from fineract-provider/src/test/java/org/apache/fineract/batch/command/internal/AdjustTransactionCommandStrategyTest.java
copy to fineract-provider/src/test/java/org/apache/fineract/batch/command/internal/AdjustChargeCommandStrategyTest.java
index 20dae9a60..7bb5d040c 100644
--- a/fineract-provider/src/test/java/org/apache/fineract/batch/command/internal/AdjustTransactionCommandStrategyTest.java
+++ b/fineract-provider/src/test/java/org/apache/fineract/batch/command/internal/AdjustChargeCommandStrategyTest.java
@@ -21,6 +21,8 @@ package org.apache.fineract.batch.command.internal;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.BDDMockito.given;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoInteractions;
 
 import javax.ws.rs.HttpMethod;
 import javax.ws.rs.core.UriInfo;
@@ -28,68 +30,67 @@ import org.apache.commons.lang3.RandomStringUtils;
 import org.apache.commons.lang3.StringUtils;
 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.api.LoanChargesApiResource;
 import org.apache.http.HttpStatus;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
-public class AdjustTransactionCommandStrategyTest {
+/**
+ * Test class for {@link AdjustChargeCommandStrategy}.
+ */
+public class AdjustChargeCommandStrategyTest {
 
     /**
-     * Test {@link AdjustTransactionCommandStrategy#execute} happy path scenario.
+     * Test {@link AdjustChargeCommandStrategy#execute} happy path scenario.
      */
     @Test
-    public void testExecuteWithoutCommandSuccessScenario() {
+    public void testExecuteWithAdjustmentCommandSuccessScenario() {
         // 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);
-        final String responseBody = "{\"officeId\":1,\"clientId\":107,\"loanId\":71,\"resourceId\":193,\"changes\""
-                + ":{\"transactionDate\":\"03 October 2022\",\"transactionAmount\":\"500\",\"locale\":\"en\",\"dateFormat\":"
-                + "\"dd MMMM yyyy\",\"paymentTypeId\":\"\"}}";
+        final Long loanChargeId = Long.valueOf(RandomStringUtils.randomNumeric(4));
+        final String command = "adjustment";
+        final BatchRequest request = getBatchRequest(loanId, loanChargeId, command);
+        final String responseBody = "{\"loanId\":13,\"resourceId\":16,\"subResourceId\":26,\"changes\":{\"amount\":10.0,"
+                + "\"transactionDate\":[2022,12,7],\"locale\":\"de_DE\"}}";
 
-        given(testContext.loanTransactionsApiResource.adjustLoanTransaction(eq(loanId), eq(transactionId), eq(request.getBody()), eq(null)))
+        given(testContext.loanChargesApiResource.executeLoanCharge(eq(loanId), eq(loanChargeId), eq(command), eq(request.getBody())))
                 .willReturn(responseBody);
 
         // when
         final BatchResponse response = testContext.subjectToTest.execute(request, testContext.uriInfo);
 
         // then
-        assertEquals(response.getStatusCode(), HttpStatus.SC_OK);
-        assertEquals(response.getRequestId(), request.getRequestId());
-        assertEquals(response.getHeaders(), request.getHeaders());
-        assertEquals(response.getBody(), responseBody);
+        assertEquals(HttpStatus.SC_OK, response.getStatusCode());
+        assertEquals(request.getRequestId(), response.getRequestId());
+        assertEquals(request.getHeaders(), response.getHeaders());
+        assertEquals(responseBody, response.getBody());
+        verify(testContext.loanChargesApiResource).executeLoanCharge(eq(loanId), eq(loanChargeId), eq(command), eq(request.getBody()));
     }
 
     /**
-     * Test {@link AdjustTransactionCommandStrategy#execute} happy path scenario.
+     * Test {@link AdjustChargeCommandStrategy#execute} error scenario.
      */
     @Test
-    public void testExecuteWithCommandSuccessScenario() {
+    public void testExecuteWithoutCommandErrorScenario() {
         // 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, "chargeback");
-        final String responseBody = "{\"officeId\":1,\"clientId\":107,\"loanId\":71,\"resourceId\":193,\"changes\""
-                + ":{\"transactionDate\":\"03 October 2022\",\"transactionAmount\":\"500\",\"locale\":\"en\",\"dateFormat\":"
-                + "\"dd MMMM yyyy\",\"paymentTypeId\":\"\"}}";
-
-        given(testContext.loanTransactionsApiResource.adjustLoanTransaction(eq(loanId), eq(transactionId), eq(request.getBody()),
-                eq("chargeback"))).willReturn(responseBody);
+        final Long loanChargeId = Long.valueOf(RandomStringUtils.randomNumeric(4));
+        final BatchRequest request = getBatchRequest(loanId, loanChargeId, null);
 
         // when
         final BatchResponse response = testContext.subjectToTest.execute(request, testContext.uriInfo);
 
         // then
-        assertEquals(response.getStatusCode(), HttpStatus.SC_OK);
-        assertEquals(response.getRequestId(), request.getRequestId());
-        assertEquals(response.getHeaders(), request.getHeaders());
-        assertEquals(response.getBody(), responseBody);
+        assertEquals(HttpStatus.SC_NOT_IMPLEMENTED, response.getStatusCode());
+        assertEquals(request.getRequestId(), response.getRequestId());
+        assertEquals("Resource with method " + request.getMethod() + " and relativeUrl " + request.getRelativeUrl() + " doesn't exist",
+                response.getBody());
+        verifyNoInteractions(testContext.loanChargesApiResource);
     }
 
     /**
@@ -99,23 +100,23 @@ public class AdjustTransactionCommandStrategyTest {
      *            the loan id
      * @param transactionId
      *            the transaction id
-     * @param transactionCommand
-     *            the optional transaction command
+     * @param chargeCommand
+     *            the charge command
      * @return BatchRequest
      */
-    private BatchRequest getBatchRequest(final Long loanId, final Long transactionId, final String transactionCommand) {
+    private BatchRequest getBatchRequest(final Long loanId, final Long transactionId, final String chargeCommand) {
 
         final BatchRequest br = new BatchRequest();
-        String relativeUrl = String.format("loans/%s/transactions/%s", loanId, transactionId);
+        String relativeUrl = String.format("loans/%s/charges/%s", loanId, transactionId);
 
         br.setRequestId(Long.valueOf(RandomStringUtils.randomNumeric(5)));
         br.setRelativeUrl(relativeUrl);
-        if (StringUtils.isNotBlank(transactionCommand)) {
-            br.setRelativeUrl(br.getRelativeUrl() + String.format("?command=%s", transactionCommand));
+        if (StringUtils.isNotBlank(chargeCommand)) {
+            br.setRelativeUrl(br.getRelativeUrl() + String.format("?command=%s", chargeCommand));
         }
         br.setMethod(HttpMethod.POST);
         br.setReference(Long.valueOf(RandomStringUtils.randomNumeric(5)));
-        br.setBody("{\"locale\":\"en\",\"dateFormat\":\"dd MMMM yyyy\",\"transactionDate\":\"03 October 2022\",\"transactionAmount\":500}");
+        br.setBody("{\"amount\":7.00,\"locale\":\"en\"}");
 
         return br;
     }
@@ -125,17 +126,29 @@ public class AdjustTransactionCommandStrategyTest {
      */
     private static class TestContext {
 
+        /**
+         * The Mock UriInfo
+         */
         @Mock
         private UriInfo uriInfo;
 
+        /**
+         * The Mock {@link LoanChargesApiResource}
+         */
         @Mock
-        private LoanTransactionsApiResource loanTransactionsApiResource;
+        private LoanChargesApiResource loanChargesApiResource;
 
-        private final AdjustTransactionCommandStrategy subjectToTest;
+        /**
+         * The class under test.
+         */
+        private final AdjustChargeCommandStrategy subjectToTest;
 
+        /**
+         * Constructor.
+         */
         TestContext() {
             MockitoAnnotations.openMocks(this);
-            subjectToTest = new AdjustTransactionCommandStrategy(loanTransactionsApiResource);
+            subjectToTest = new AdjustChargeCommandStrategy(loanChargesApiResource);
         }
     }
 }
diff --git a/fineract-provider/src/test/java/org/apache/fineract/batch/command/internal/AdjustTransactionCommandStrategyTest.java b/fineract-provider/src/test/java/org/apache/fineract/batch/command/internal/AdjustTransactionByExternalIdCommandStrategyTest.java
similarity index 58%
copy from fineract-provider/src/test/java/org/apache/fineract/batch/command/internal/AdjustTransactionCommandStrategyTest.java
copy to fineract-provider/src/test/java/org/apache/fineract/batch/command/internal/AdjustTransactionByExternalIdCommandStrategyTest.java
index 20dae9a60..ae2618fb5 100644
--- a/fineract-provider/src/test/java/org/apache/fineract/batch/command/internal/AdjustTransactionCommandStrategyTest.java
+++ b/fineract-provider/src/test/java/org/apache/fineract/batch/command/internal/AdjustTransactionByExternalIdCommandStrategyTest.java
@@ -20,8 +20,11 @@ package org.apache.fineract.batch.command.internal;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.isNull;
 import static org.mockito.BDDMockito.given;
+import static org.mockito.Mockito.verify;
 
+import java.util.UUID;
 import javax.ws.rs.HttpMethod;
 import javax.ws.rs.core.UriInfo;
 import org.apache.commons.lang3.RandomStringUtils;
@@ -30,83 +33,90 @@ 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.Test;
+import org.junit.jupiter.api.Test;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
-public class AdjustTransactionCommandStrategyTest {
+/**
+ * Test class for {@link AdjustTransactionByExternalIdCommandStrategy}.
+ */
+public class AdjustTransactionByExternalIdCommandStrategyTest {
 
     /**
-     * Test {@link AdjustTransactionCommandStrategy#execute} happy path scenario.
+     * Test {@link AdjustTransactionByExternalIdCommandStrategy#execute} happy path scenario.
      */
     @Test
     public void testExecuteWithoutCommandSuccessScenario() {
         // 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);
+        final String loanExternalId = UUID.randomUUID().toString();
+        final String transactionExternalId = UUID.randomUUID().toString();
+        final BatchRequest request = getBatchRequest(loanExternalId, transactionExternalId, null);
         final String responseBody = "{\"officeId\":1,\"clientId\":107,\"loanId\":71,\"resourceId\":193,\"changes\""
                 + ":{\"transactionDate\":\"03 October 2022\",\"transactionAmount\":\"500\",\"locale\":\"en\",\"dateFormat\":"
                 + "\"dd MMMM yyyy\",\"paymentTypeId\":\"\"}}";
 
-        given(testContext.loanTransactionsApiResource.adjustLoanTransaction(eq(loanId), eq(transactionId), eq(request.getBody()), eq(null)))
-                .willReturn(responseBody);
+        given(testContext.loanTransactionsApiResource.adjustLoanTransaction(eq(loanExternalId), eq(transactionExternalId),
+                eq(request.getBody()), eq(null))).willReturn(responseBody);
 
         // when
         final BatchResponse response = testContext.subjectToTest.execute(request, testContext.uriInfo);
 
         // then
-        assertEquals(response.getStatusCode(), HttpStatus.SC_OK);
-        assertEquals(response.getRequestId(), request.getRequestId());
-        assertEquals(response.getHeaders(), request.getHeaders());
-        assertEquals(response.getBody(), responseBody);
+        assertEquals(HttpStatus.SC_OK, response.getStatusCode());
+        assertEquals(request.getRequestId(), response.getRequestId());
+        assertEquals(request.getHeaders(), response.getHeaders());
+        assertEquals(responseBody, response.getBody());
+        verify(testContext.loanTransactionsApiResource).adjustLoanTransaction(eq(loanExternalId), eq(transactionExternalId),
+                eq(request.getBody()), isNull());
     }
 
     /**
-     * Test {@link AdjustTransactionCommandStrategy#execute} happy path scenario.
+     * Test {@link AdjustTransactionByExternalIdCommandStrategy#execute} happy path scenario.
      */
     @Test
     public void testExecuteWithCommandSuccessScenario() {
         // 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, "chargeback");
+        final String loanExternalId = UUID.randomUUID().toString();
+        final String transactionExternalId = UUID.randomUUID().toString();
+        final BatchRequest request = getBatchRequest(loanExternalId, transactionExternalId, "chargeback");
         final String responseBody = "{\"officeId\":1,\"clientId\":107,\"loanId\":71,\"resourceId\":193,\"changes\""
                 + ":{\"transactionDate\":\"03 October 2022\",\"transactionAmount\":\"500\",\"locale\":\"en\",\"dateFormat\":"
                 + "\"dd MMMM yyyy\",\"paymentTypeId\":\"\"}}";
 
-        given(testContext.loanTransactionsApiResource.adjustLoanTransaction(eq(loanId), eq(transactionId), eq(request.getBody()),
-                eq("chargeback"))).willReturn(responseBody);
+        given(testContext.loanTransactionsApiResource.adjustLoanTransaction(eq(loanExternalId), eq(transactionExternalId),
+                eq(request.getBody()), eq("chargeback"))).willReturn(responseBody);
 
         // when
         final BatchResponse response = testContext.subjectToTest.execute(request, testContext.uriInfo);
 
         // then
-        assertEquals(response.getStatusCode(), HttpStatus.SC_OK);
-        assertEquals(response.getRequestId(), request.getRequestId());
-        assertEquals(response.getHeaders(), request.getHeaders());
-        assertEquals(response.getBody(), responseBody);
+        assertEquals(HttpStatus.SC_OK, response.getStatusCode());
+        assertEquals(request.getRequestId(), response.getRequestId());
+        assertEquals(request.getHeaders(), response.getHeaders());
+        assertEquals(responseBody, response.getBody());
+        verify(testContext.loanTransactionsApiResource).adjustLoanTransaction(eq(loanExternalId), eq(transactionExternalId),
+                eq(request.getBody()), eq("chargeback"));
     }
 
     /**
      * Creates and returns a request with the given loan id and transaction id.
      *
-     * @param loanId
+     * @param loanExternalId
      *            the loan id
-     * @param transactionId
+     * @param transactionExternalId
      *            the transaction id
      * @param transactionCommand
      *            the optional transaction command
      * @return BatchRequest
      */
-    private BatchRequest getBatchRequest(final Long loanId, final Long transactionId, final String transactionCommand) {
+    private BatchRequest getBatchRequest(final String loanExternalId, final String transactionExternalId, final String transactionCommand) {
 
         final BatchRequest br = new BatchRequest();
-        String relativeUrl = String.format("loans/%s/transactions/%s", loanId, transactionId);
+        String relativeUrl = String.format("loans/external-id/%s/transactions/external-id/%s", loanExternalId, transactionExternalId);
 
         br.setRequestId(Long.valueOf(RandomStringUtils.randomNumeric(5)));
         br.setRelativeUrl(relativeUrl);
@@ -125,17 +135,29 @@ public class AdjustTransactionCommandStrategyTest {
      */
     private static class TestContext {
 
+        /**
+         * The Mock UriInfo
+         */
         @Mock
         private UriInfo uriInfo;
 
+        /**
+         * The Mock {@link LoanTransactionsApiResource}
+         */
         @Mock
         private LoanTransactionsApiResource loanTransactionsApiResource;
 
-        private final AdjustTransactionCommandStrategy subjectToTest;
+        /**
+         * The class under test.
+         */
+        private final AdjustTransactionByExternalIdCommandStrategy subjectToTest;
 
+        /**
+         * Constructor.
+         */
         TestContext() {
             MockitoAnnotations.openMocks(this);
-            subjectToTest = new AdjustTransactionCommandStrategy(loanTransactionsApiResource);
+            subjectToTest = new AdjustTransactionByExternalIdCommandStrategy(loanTransactionsApiResource);
         }
     }
 }
diff --git a/fineract-provider/src/test/java/org/apache/fineract/batch/command/internal/AdjustTransactionCommandStrategyTest.java b/fineract-provider/src/test/java/org/apache/fineract/batch/command/internal/AdjustTransactionCommandStrategyTest.java
index 20dae9a60..9d3faf448 100644
--- a/fineract-provider/src/test/java/org/apache/fineract/batch/command/internal/AdjustTransactionCommandStrategyTest.java
+++ b/fineract-provider/src/test/java/org/apache/fineract/batch/command/internal/AdjustTransactionCommandStrategyTest.java
@@ -21,6 +21,7 @@ package org.apache.fineract.batch.command.internal;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.BDDMockito.given;
+import static org.mockito.Mockito.verify;
 
 import javax.ws.rs.HttpMethod;
 import javax.ws.rs.core.UriInfo;
@@ -30,10 +31,13 @@ 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.Test;
+import org.junit.jupiter.api.Test;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+/**
+ * Test class for {@link AdjustTransactionCommandStrategy}.
+ */
 public class AdjustTransactionCommandStrategyTest {
 
     /**
@@ -58,10 +62,12 @@ public class AdjustTransactionCommandStrategyTest {
         final BatchResponse response = testContext.subjectToTest.execute(request, testContext.uriInfo);
 
         // then
-        assertEquals(response.getStatusCode(), HttpStatus.SC_OK);
-        assertEquals(response.getRequestId(), request.getRequestId());
-        assertEquals(response.getHeaders(), request.getHeaders());
-        assertEquals(response.getBody(), responseBody);
+        assertEquals(HttpStatus.SC_OK, response.getStatusCode());
+        assertEquals(request.getRequestId(), response.getRequestId());
+        assertEquals(request.getHeaders(), response.getHeaders());
+        assertEquals(responseBody, response.getBody());
+        verify(testContext.loanTransactionsApiResource).adjustLoanTransaction(eq(loanId), eq(transactionId), eq(request.getBody()),
+                eq(null));
     }
 
     /**
@@ -86,10 +92,12 @@ public class AdjustTransactionCommandStrategyTest {
         final BatchResponse response = testContext.subjectToTest.execute(request, testContext.uriInfo);
 
         // then
-        assertEquals(response.getStatusCode(), HttpStatus.SC_OK);
-        assertEquals(response.getRequestId(), request.getRequestId());
-        assertEquals(response.getHeaders(), request.getHeaders());
-        assertEquals(response.getBody(), responseBody);
+        assertEquals(HttpStatus.SC_OK, response.getStatusCode());
+        assertEquals(request.getRequestId(), response.getRequestId());
+        assertEquals(request.getHeaders(), response.getHeaders());
+        assertEquals(responseBody, response.getBody());
+        verify(testContext.loanTransactionsApiResource).adjustLoanTransaction(eq(loanId), eq(transactionId), eq(request.getBody()),
+                eq("chargeback"));
     }
 
     /**
@@ -125,14 +133,26 @@ public class AdjustTransactionCommandStrategyTest {
      */
     private static class TestContext {
 
+        /**
+         * The Mock UriInfo
+         */
         @Mock
         private UriInfo uriInfo;
 
+        /**
+         * The Mock {@link LoanTransactionsApiResource}
+         */
         @Mock
         private LoanTransactionsApiResource loanTransactionsApiResource;
 
+        /**
+         * The class under test.
+         */
         private final AdjustTransactionCommandStrategy subjectToTest;
 
+        /**
+         * Constructor.
+         */
         TestContext() {
             MockitoAnnotations.openMocks(this);
             subjectToTest = new AdjustTransactionCommandStrategy(loanTransactionsApiResource);
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/CollectChargesByLoanExternalIdCommandStrategyTest.java
similarity index 55%
copy from fineract-provider/src/test/java/org/apache/fineract/batch/command/internal/GetLoanByIdCommandStrategyTest.java
copy to fineract-provider/src/test/java/org/apache/fineract/batch/command/internal/CollectChargesByLoanExternalIdCommandStrategyTest.java
index d8327f89c..351e62434 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/CollectChargesByLoanExternalIdCommandStrategyTest.java
@@ -24,8 +24,7 @@ import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.BDDMockito.given;
 import static org.mockito.Mockito.verify;
 
-import java.util.HashSet;
-import java.util.Set;
+import java.util.UUID;
 import java.util.stream.Stream;
 import javax.ws.rs.HttpMethod;
 import javax.ws.rs.core.UriInfo;
@@ -33,7 +32,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.infrastructure.core.api.MutableUriInfo;
-import org.apache.fineract.portfolio.loanaccount.api.LoansApiResource;
+import org.apache.fineract.portfolio.loanaccount.api.LoanChargesApiResource;
 import org.apache.http.HttpStatus;
 import org.junit.jupiter.params.ParameterizedTest;
 import org.junit.jupiter.params.provider.Arguments;
@@ -43,72 +42,61 @@ import org.mockito.Captor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
-public class GetLoanByIdCommandStrategyTest {
+/**
+ * The {@link CollectChargesByLoanExternalIdCommandStrategy} test class.
+ */
+public class CollectChargesByLoanExternalIdCommandStrategyTest {
 
+    /**
+     * Query parameter provider.
+     *
+     * @return the test data stream
+     */
     private static Stream<Arguments> provideQueryParameters() {
-        return Stream.of(Arguments.of(null, null, null, 0), Arguments.of("all", null, null, 1),
-                Arguments.of("repaymentSchedule,transactions", null, "guarantors,futureSchedule", 2),
-                Arguments.of("repaymentSchedule,transactions", "id,principal,annualInterestRate", null, 2),
-                Arguments.of("repaymentSchedule,transactions", "id,principal,annualInterestRate", "guarantors,futureSchedule", 3));
+        return Stream.of(Arguments.of(null, 0), Arguments.of("fields=name,amountOrPercentage", 1));
     }
 
     /**
-     * Test {@link GetLoanByIdCommandStrategy#execute} happy path scenario.
-     *
+     * Test {@link CollectChargesByLoanExternalIdCommandStrategy#execute} happy path scenario.
      */
     @ParameterizedTest
     @MethodSource("provideQueryParameters")
-    public void testExecuteSuccessScenario(final String associations, final String fields, final String exclude,
-            final int noOfQueryParams) {
-        // given
+    public void testExecuteSuccessScenario(final String queryParameter, final int numberOfQueryParams) {
         final TestContext testContext = new TestContext();
 
-        final Long loanId = Long.valueOf(RandomStringUtils.randomNumeric(4));
-        final BatchRequest request = getBatchRequest(loanId, associations, exclude, fields);
-        final String responseBody = "{\\\"id\\\":2,\\\"accountNo\\\":\\\"000000002\\\"}";
+        final String loanExternalId = UUID.randomUUID().toString();
+        final BatchRequest request = getBatchRequest(loanExternalId, queryParameter);
+        final String responseBody = "someResponseBody";
 
-        given(testContext.loansApiResource.retrieveLoan(eq(loanId), eq(false), eq(associations), eq(exclude), eq(fields),
-                any(UriInfo.class))).willReturn(responseBody);
+        given(testContext.loanChargesApiResource.retrieveAllLoanCharges(eq(loanExternalId), any(UriInfo.class))).willReturn(responseBody);
 
-        // when
-        final BatchResponse response = testContext.underTest.execute(request, testContext.uriInfo);
+        final BatchResponse response = testContext.subjectToTest.execute(request, testContext.uriInfo);
 
-        // then
         assertThat(response.getStatusCode()).isEqualTo(HttpStatus.SC_OK);
         assertThat(response.getRequestId()).isEqualTo(request.getRequestId());
         assertThat(response.getHeaders()).isEqualTo(request.getHeaders());
         assertThat(response.getBody()).isEqualTo(responseBody);
 
-        verify(testContext.loansApiResource).retrieveLoan(eq(loanId), eq(false), eq(associations), eq(exclude), eq(fields),
-                testContext.uriInfoCaptor.capture());
+        verify(testContext.loanChargesApiResource).retrieveAllLoanCharges(eq(loanExternalId), testContext.uriInfoCaptor.capture());
         MutableUriInfo mutableUriInfo = testContext.uriInfoCaptor.getValue();
-        assertThat(mutableUriInfo.getAdditionalQueryParameters()).hasSize(noOfQueryParams);
+        assertThat(mutableUriInfo.getAdditionalQueryParameters()).hasSize(numberOfQueryParams);
     }
 
     /**
-     * Creates and returns a request with the given loan id.
+     * Creates and returns a request with the given loan external id.
      *
-     * @param loanId
-     *            the loan id
+     * @param loanExternalId
+     *            the loan external id
+     * @param queryParameter
+     *            the query parameter
      * @return BatchRequest
      */
-    private BatchRequest getBatchRequest(final Long loanId, final String associations, final String exclude, final String fields) {
-
+    private BatchRequest getBatchRequest(final String loanExternalId, final String queryParameter) {
         final BatchRequest br = new BatchRequest();
-        String relativeUrl = "loans/" + loanId;
+        String relativeUrl = "loans/external-id/" + loanExternalId + "/charges";
 
-        Set<String> queryParams = new HashSet<>();
-        if (associations != null) {
-            queryParams.add("associations=" + associations);
-        }
-        if (exclude != null) {
-            queryParams.add("exclude=" + exclude);
-        }
-        if (fields != null) {
-            queryParams.add("fields=" + fields);
-        }
-        if (!queryParams.isEmpty()) {
-            relativeUrl = relativeUrl + "?" + String.join("&", queryParams);
+        if (queryParameter != null) {
+            relativeUrl = relativeUrl + "?" + queryParameter;
         }
 
         br.setRequestId(Long.valueOf(RandomStringUtils.randomNumeric(5)));
@@ -125,20 +113,35 @@ public class GetLoanByIdCommandStrategyTest {
      */
     private static class TestContext {
 
+        /**
+         * The subject to test
+         */
+        private final CollectChargesByLoanExternalIdCommandStrategy subjectToTest;
+
+        /**
+         * The mock uri info
+         */
         @Mock
         private UriInfo uriInfo;
 
+        /**
+         * The mock {@link LoanChargesApiResource} object.
+         */
         @Mock
-        private LoansApiResource loansApiResource;
+        private LoanChargesApiResource loanChargesApiResource;
 
+        /**
+         * The uri info captor
+         */
         @Captor
         private ArgumentCaptor<MutableUriInfo> uriInfoCaptor;
 
-        private final GetLoanByIdCommandStrategy underTest;
-
+        /**
+         * Test Context constructor
+         */
         TestContext() {
             MockitoAnnotations.openMocks(this);
-            underTest = new GetLoanByIdCommandStrategy(loansApiResource);
+            subjectToTest = new CollectChargesByLoanExternalIdCommandStrategy(loanChargesApiResource);
         }
     }
 }
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/CreateChargeByLoanExternalIdCommandStrategyTest.java
similarity index 56%
copy from fineract-provider/src/test/java/org/apache/fineract/batch/command/internal/CreateTransactionLoanCommandStrategyTest.java
copy to fineract-provider/src/test/java/org/apache/fineract/batch/command/internal/CreateChargeByLoanExternalIdCommandStrategyTest.java
index c081a2e0c..7f812443d 100644
--- 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/CreateChargeByLoanExternalIdCommandStrategyTest.java
@@ -23,34 +23,53 @@ import static org.junit.jupiter.api.Assertions.assertSame;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import java.util.UUID;
+import java.util.stream.Stream;
 import javax.ws.rs.HttpMethod;
 import javax.ws.rs.core.UriInfo;
 import org.apache.commons.lang3.RandomStringUtils;
+import org.apache.commons.lang3.StringUtils;
 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.api.LoanChargesApiResource;
 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;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
 /**
- * Test class fpr {@link CreateTransactionLoanCommandStrategy}.
+ * Test class for {@link CreateChargeByLoanExternalIdCommandStrategy}.
  */
-public class CreateTransactionLoanCommandStrategyTest {
+public class CreateChargeByLoanExternalIdCommandStrategyTest {
 
     /**
-     * Test {@link CreateTransactionLoanCommandStrategy#execute} happy path scenario.
+     * Command arguments provider.
+     *
+     * @return command argument
      */
-    @Test
-    public void testExecuteSuccessScenario() {
+    private static Stream<Arguments> provideCommandParameters() {
+        return Stream.of(Arguments.of(null, 0), Arguments.of("adjustment", 1));
+    }
+
+    /**
+     * Test {@link CreateChargeByLoanExternalIdCommandStrategy#execute} happy path scenario.
+     *
+     * @param command
+     *            the command to pass
+     * @param noOfArguments
+     *            the number of arguments
+     */
+    @ParameterizedTest
+    @MethodSource("provideCommandParameters")
+    public void testExecuteSuccessScenario(final String command, final int noOfArguments) {
         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 loanExternalId = UUID.randomUUID().toString();
+        final BatchRequest batchRequest = getBatchRequest(loanExternalId, command);
         final String responseBody = "myResponseBody";
 
-        when(testContext.loanTransactionsApiResource.executeLoanTransaction(loanId, command, batchRequest.getBody()))
+        when(testContext.loanChargesApiResource.executeLoanCharge(loanExternalId, command, batchRequest.getBody()))
                 .thenReturn(responseBody);
 
         BatchResponse batchResponse = testContext.subjectToTest.execute(batchRequest, testContext.uriInfo);
@@ -60,23 +79,25 @@ public class CreateTransactionLoanCommandStrategyTest {
         assertEquals(batchRequest.getRequestId(), batchResponse.getRequestId());
         assertEquals(batchRequest.getHeaders(), batchResponse.getHeaders());
 
-        verify(testContext.loanTransactionsApiResource).executeLoanTransaction(loanId, command, batchRequest.getBody());
+        verify(testContext.loanChargesApiResource).executeLoanCharge(loanExternalId, command, batchRequest.getBody());
     }
 
     /**
      * Creates and returns a request with the given loan id and command value.
      *
-     * @param loanId
-     *            the loan id
+     * @param loanExternalId
+     *            the loan external id
      * @param command
      *            the transaction id
      * @return BatchRequest
      */
-    private BatchRequest getBatchRequest(final Long loanId, final String command) {
+    private BatchRequest getBatchRequest(final String loanExternalId, final String command) {
 
         final BatchRequest br = new BatchRequest();
-        String relativeUrl = "loans/" + loanId + "/transactions?command=" + command;
-
+        String relativeUrl = "loans/external-id/" + loanExternalId + "/charges";
+        if (StringUtils.isNotBlank(command)) {
+            relativeUrl = relativeUrl + String.format("?command=%s", command);
+        }
         br.setRequestId(Long.valueOf(RandomStringUtils.randomNumeric(5)));
         br.setRelativeUrl(relativeUrl);
         br.setMethod(HttpMethod.POST);
@@ -98,22 +119,22 @@ public class CreateTransactionLoanCommandStrategyTest {
         private UriInfo uriInfo;
 
         /**
-         * Mock loan transactions API resource.
+         * Mock loan charges API resource.
          */
         @Mock
-        private LoanTransactionsApiResource loanTransactionsApiResource;
+        private LoanChargesApiResource loanChargesApiResource;
 
         /**
-         * The {@link CreateTransactionLoanCommandStrategy} under test.
+         * The {@link CreateChargeByLoanExternalIdCommandStrategy} under test.
          */
-        private final CreateTransactionLoanCommandStrategy subjectToTest;
+        private final CreateChargeByLoanExternalIdCommandStrategy subjectToTest;
 
         /**
          * Constructor.
          */
         TestContext() {
             MockitoAnnotations.openMocks(this);
-            subjectToTest = new CreateTransactionLoanCommandStrategy(loanTransactionsApiResource);
+            subjectToTest = new CreateChargeByLoanExternalIdCommandStrategy(loanChargesApiResource);
         }
     }
 }
diff --git a/fineract-provider/src/test/java/org/apache/fineract/batch/command/internal/CreateDatatableEntryCommandStrategyTest.java b/fineract-provider/src/test/java/org/apache/fineract/batch/command/internal/CreateDatatableEntryCommandStrategyTest.java
index ff63a7dda..2f58fb210 100644
--- a/fineract-provider/src/test/java/org/apache/fineract/batch/command/internal/CreateDatatableEntryCommandStrategyTest.java
+++ b/fineract-provider/src/test/java/org/apache/fineract/batch/command/internal/CreateDatatableEntryCommandStrategyTest.java
@@ -29,10 +29,13 @@ import org.apache.fineract.batch.domain.BatchRequest;
 import org.apache.fineract.batch.domain.BatchResponse;
 import org.apache.fineract.infrastructure.dataqueries.api.DatatablesApiResource;
 import org.apache.http.HttpStatus;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+/**
+ * Test class for {@link CreateDatatableEntryCommandStrategy}.
+ */
 public class CreateDatatableEntryCommandStrategyTest {
 
     /**
@@ -55,10 +58,10 @@ public class CreateDatatableEntryCommandStrategyTest {
         final BatchResponse response = testContext.subjectToTest.execute(request, testContext.uriInfo);
 
         // then
-        assertEquals(response.getStatusCode(), HttpStatus.SC_OK);
-        assertEquals(response.getRequestId(), request.getRequestId());
-        assertEquals(response.getHeaders(), request.getHeaders());
-        assertEquals(response.getBody(), responseBody);
+        assertEquals(HttpStatus.SC_OK, response.getStatusCode());
+        assertEquals(request.getRequestId(), response.getRequestId());
+        assertEquals(request.getHeaders(), response.getHeaders());
+        assertEquals(responseBody, response.getBody());
     }
 
     /**
@@ -88,14 +91,27 @@ public class CreateDatatableEntryCommandStrategyTest {
      */
     private static class TestContext {
 
+        /**
+         * The Mock UriInfo
+         */
         @Mock
         private UriInfo uriInfo;
 
+        /**
+         * The Mock {@link DatatablesApiResource}
+         */
         @Mock
         private DatatablesApiResource datatablesApiResource;
 
+        /**
+         * The class under test.
+         */
         private final CreateDatatableEntryCommandStrategy subjectToTest;
 
+        /**
+         * Constructor.
+         */
+
         TestContext() {
             MockitoAnnotations.openMocks(this);
             subjectToTest = new CreateDatatableEntryCommandStrategy(datatablesApiResource);
diff --git a/fineract-provider/src/test/java/org/apache/fineract/batch/command/internal/CreateLoanRescheduleRequestCommandStrategyTest.java b/fineract-provider/src/test/java/org/apache/fineract/batch/command/internal/CreateLoanRescheduleRequestCommandStrategyTest.java
index 0cdf40599..fc342dc63 100644
--- a/fineract-provider/src/test/java/org/apache/fineract/batch/command/internal/CreateLoanRescheduleRequestCommandStrategyTest.java
+++ b/fineract-provider/src/test/java/org/apache/fineract/batch/command/internal/CreateLoanRescheduleRequestCommandStrategyTest.java
@@ -35,7 +35,7 @@ import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
 /**
- * Test class fpr {@link CreateLoanRescheduleRequestCommandStrategy}.
+ * Test class for {@link CreateLoanRescheduleRequestCommandStrategy}.
  */
 public class CreateLoanRescheduleRequestCommandStrategyTest {
 
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/CreateTransactionByLoanExternalIdCommandStrategyTest.java
similarity index 75%
copy from fineract-provider/src/test/java/org/apache/fineract/batch/command/internal/CreateTransactionLoanCommandStrategyTest.java
copy to fineract-provider/src/test/java/org/apache/fineract/batch/command/internal/CreateTransactionByLoanExternalIdCommandStrategyTest.java
index c081a2e0c..a43238663 100644
--- 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/CreateTransactionByLoanExternalIdCommandStrategyTest.java
@@ -23,6 +23,7 @@ import static org.junit.jupiter.api.Assertions.assertSame;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import java.util.UUID;
 import javax.ws.rs.HttpMethod;
 import javax.ws.rs.core.UriInfo;
 import org.apache.commons.lang3.RandomStringUtils;
@@ -35,22 +36,22 @@ import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
 /**
- * Test class fpr {@link CreateTransactionLoanCommandStrategy}.
+ * Test class for {@link CreateTransactionByLoanExternalIdCommandStrategy}.
  */
-public class CreateTransactionLoanCommandStrategyTest {
+public class CreateTransactionByLoanExternalIdCommandStrategyTest {
 
     /**
-     * Test {@link CreateTransactionLoanCommandStrategy#execute} happy path scenario.
+     * Test {@link CreateTransactionByLoanExternalIdCommandStrategy#execute} happy path scenario.
      */
     @Test
     public void testExecuteSuccessScenario() {
         final TestContext testContext = new TestContext();
-        final Long loanId = Long.valueOf(RandomStringUtils.randomNumeric(4));
+        final String loanExternalId = UUID.randomUUID().toString();
         final String command = "myCommand";
-        final BatchRequest batchRequest = getBatchRequest(loanId, command);
+        final BatchRequest batchRequest = getBatchRequest(loanExternalId, command);
         final String responseBody = "myResponseBody";
 
-        when(testContext.loanTransactionsApiResource.executeLoanTransaction(loanId, command, batchRequest.getBody()))
+        when(testContext.loanTransactionsApiResource.executeLoanTransaction(loanExternalId, command, batchRequest.getBody()))
                 .thenReturn(responseBody);
 
         BatchResponse batchResponse = testContext.subjectToTest.execute(batchRequest, testContext.uriInfo);
@@ -60,22 +61,22 @@ public class CreateTransactionLoanCommandStrategyTest {
         assertEquals(batchRequest.getRequestId(), batchResponse.getRequestId());
         assertEquals(batchRequest.getHeaders(), batchResponse.getHeaders());
 
-        verify(testContext.loanTransactionsApiResource).executeLoanTransaction(loanId, command, batchRequest.getBody());
+        verify(testContext.loanTransactionsApiResource).executeLoanTransaction(loanExternalId, command, batchRequest.getBody());
     }
 
     /**
      * Creates and returns a request with the given loan id and command value.
      *
-     * @param loanId
-     *            the loan id
+     * @param loanExternalId
+     *            the loan external id
      * @param command
      *            the transaction id
      * @return BatchRequest
      */
-    private BatchRequest getBatchRequest(final Long loanId, final String command) {
+    private BatchRequest getBatchRequest(final String loanExternalId, final String command) {
 
         final BatchRequest br = new BatchRequest();
-        String relativeUrl = "loans/" + loanId + "/transactions?command=" + command;
+        String relativeUrl = "loans/external-id/" + loanExternalId + "/transactions?command=" + command;
 
         br.setRequestId(Long.valueOf(RandomStringUtils.randomNumeric(5)));
         br.setRelativeUrl(relativeUrl);
@@ -104,16 +105,16 @@ public class CreateTransactionLoanCommandStrategyTest {
         private LoanTransactionsApiResource loanTransactionsApiResource;
 
         /**
-         * The {@link CreateTransactionLoanCommandStrategy} under test.
+         * The {@link CreateTransactionByLoanExternalIdCommandStrategy} under test.
          */
-        private final CreateTransactionLoanCommandStrategy subjectToTest;
+        private final CreateTransactionByLoanExternalIdCommandStrategy subjectToTest;
 
         /**
          * Constructor.
          */
         TestContext() {
             MockitoAnnotations.openMocks(this);
-            subjectToTest = new CreateTransactionLoanCommandStrategy(loanTransactionsApiResource);
+            subjectToTest = new CreateTransactionByLoanExternalIdCommandStrategy(loanTransactionsApiResource);
         }
     }
 }
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
index c081a2e0c..29636669f 100644
--- 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
@@ -35,7 +35,7 @@ import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
 /**
- * Test class fpr {@link CreateTransactionLoanCommandStrategy}.
+ * Test class for {@link CreateTransactionLoanCommandStrategy}.
  */
 public class CreateTransactionLoanCommandStrategyTest {
 
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/GetChargeByChargeExternalIdCommandStrategyTest.java
similarity index 56%
copy from fineract-provider/src/test/java/org/apache/fineract/batch/command/internal/GetLoanByIdCommandStrategyTest.java
copy to fineract-provider/src/test/java/org/apache/fineract/batch/command/internal/GetChargeByChargeExternalIdCommandStrategyTest.java
index d8327f89c..f484602c5 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/GetChargeByChargeExternalIdCommandStrategyTest.java
@@ -24,8 +24,7 @@ import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.BDDMockito.given;
 import static org.mockito.Mockito.verify;
 
-import java.util.HashSet;
-import java.util.Set;
+import java.util.UUID;
 import java.util.stream.Stream;
 import javax.ws.rs.HttpMethod;
 import javax.ws.rs.core.UriInfo;
@@ -33,7 +32,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.infrastructure.core.api.MutableUriInfo;
-import org.apache.fineract.portfolio.loanaccount.api.LoansApiResource;
+import org.apache.fineract.portfolio.loanaccount.api.LoanChargesApiResource;
 import org.apache.http.HttpStatus;
 import org.junit.jupiter.params.ParameterizedTest;
 import org.junit.jupiter.params.provider.Arguments;
@@ -43,72 +42,66 @@ import org.mockito.Captor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
-public class GetLoanByIdCommandStrategyTest {
+/**
+ * The {@link GetChargeByChargeExternalIdCommandStrategy} test class.
+ */
+public class GetChargeByChargeExternalIdCommandStrategyTest {
 
+    /**
+     * Query parameter provider.
+     *
+     * @return the test data stream
+     */
     private static Stream<Arguments> provideQueryParameters() {
-        return Stream.of(Arguments.of(null, null, null, 0), Arguments.of("all", null, null, 1),
-                Arguments.of("repaymentSchedule,transactions", null, "guarantors,futureSchedule", 2),
-                Arguments.of("repaymentSchedule,transactions", "id,principal,annualInterestRate", null, 2),
-                Arguments.of("repaymentSchedule,transactions", "id,principal,annualInterestRate", "guarantors,futureSchedule", 3));
+        return Stream.of(Arguments.of(null, 0), Arguments.of("fields=name,amountOrPercentage", 1));
     }
 
     /**
-     * Test {@link GetLoanByIdCommandStrategy#execute} happy path scenario.
-     *
+     * Test {@link GetChargeByChargeExternalIdCommandStrategy#execute} happy path scenario.
      */
     @ParameterizedTest
     @MethodSource("provideQueryParameters")
-    public void testExecuteSuccessScenario(final String associations, final String fields, final String exclude,
-            final int noOfQueryParams) {
-        // given
+    public void testExecuteSuccessScenario(final String queryParameter, final int numberOfQueryParams) {
         final TestContext testContext = new TestContext();
 
-        final Long loanId = Long.valueOf(RandomStringUtils.randomNumeric(4));
-        final BatchRequest request = getBatchRequest(loanId, associations, exclude, fields);
-        final String responseBody = "{\\\"id\\\":2,\\\"accountNo\\\":\\\"000000002\\\"}";
+        final String loanExternalId = UUID.randomUUID().toString();
+        final String chargeExternalId = UUID.randomUUID().toString();
+        final BatchRequest request = getBatchRequest(loanExternalId, chargeExternalId, queryParameter);
+        final String responseBody = "someResponseBody";
 
-        given(testContext.loansApiResource.retrieveLoan(eq(loanId), eq(false), eq(associations), eq(exclude), eq(fields),
-                any(UriInfo.class))).willReturn(responseBody);
+        given(testContext.loanChargesApiResource.retrieveLoanCharge(eq(loanExternalId), eq(chargeExternalId), any(UriInfo.class)))
+                .willReturn(responseBody);
 
-        // when
-        final BatchResponse response = testContext.underTest.execute(request, testContext.uriInfo);
+        final BatchResponse response = testContext.subjectToTest.execute(request, testContext.uriInfo);
 
-        // then
         assertThat(response.getStatusCode()).isEqualTo(HttpStatus.SC_OK);
         assertThat(response.getRequestId()).isEqualTo(request.getRequestId());
         assertThat(response.getHeaders()).isEqualTo(request.getHeaders());
         assertThat(response.getBody()).isEqualTo(responseBody);
 
-        verify(testContext.loansApiResource).retrieveLoan(eq(loanId), eq(false), eq(associations), eq(exclude), eq(fields),
+        verify(testContext.loanChargesApiResource).retrieveLoanCharge(eq(loanExternalId), eq(chargeExternalId),
                 testContext.uriInfoCaptor.capture());
         MutableUriInfo mutableUriInfo = testContext.uriInfoCaptor.getValue();
-        assertThat(mutableUriInfo.getAdditionalQueryParameters()).hasSize(noOfQueryParams);
+        assertThat(mutableUriInfo.getAdditionalQueryParameters()).hasSize(numberOfQueryParams);
     }
 
     /**
-     * Creates and returns a request with the given loan id.
+     * Creates and returns a request with the given loan external id and charge external id.
      *
-     * @param loanId
-     *            the loan id
+     * @param loanExternalId
+     *            the loan external id
+     * @param chargeExternalId
+     *            the charge external id
+     * @param queryParameter
+     *            the query parameter
      * @return BatchRequest
      */
-    private BatchRequest getBatchRequest(final Long loanId, final String associations, final String exclude, final String fields) {
-
+    private BatchRequest getBatchRequest(final String loanExternalId, final String chargeExternalId, final String queryParameter) {
         final BatchRequest br = new BatchRequest();
-        String relativeUrl = "loans/" + loanId;
+        String relativeUrl = "loans/external-id/" + loanExternalId + "/charges/external-id/" + chargeExternalId;
 
-        Set<String> queryParams = new HashSet<>();
-        if (associations != null) {
-            queryParams.add("associations=" + associations);
-        }
-        if (exclude != null) {
-            queryParams.add("exclude=" + exclude);
-        }
-        if (fields != null) {
-            queryParams.add("fields=" + fields);
-        }
-        if (!queryParams.isEmpty()) {
-            relativeUrl = relativeUrl + "?" + String.join("&", queryParams);
+        if (queryParameter != null) {
+            relativeUrl = relativeUrl + "?" + queryParameter;
         }
 
         br.setRequestId(Long.valueOf(RandomStringUtils.randomNumeric(5)));
@@ -125,20 +118,35 @@ public class GetLoanByIdCommandStrategyTest {
      */
     private static class TestContext {
 
+        /**
+         * The subject to test
+         */
+        private final GetChargeByChargeExternalIdCommandStrategy subjectToTest;
+
+        /**
+         * The mock uri info
+         */
         @Mock
         private UriInfo uriInfo;
 
+        /**
+         * The mock {@link LoanChargesApiResource} object.
+         */
         @Mock
-        private LoansApiResource loansApiResource;
+        private LoanChargesApiResource loanChargesApiResource;
 
+        /**
+         * The uri info captor
+         */
         @Captor
         private ArgumentCaptor<MutableUriInfo> uriInfoCaptor;
 
-        private final GetLoanByIdCommandStrategy underTest;
-
+        /**
+         * Test Context constructor
+         */
         TestContext() {
             MockitoAnnotations.openMocks(this);
-            underTest = new GetLoanByIdCommandStrategy(loansApiResource);
+            subjectToTest = new GetChargeByChargeExternalIdCommandStrategy(loanChargesApiResource);
         }
     }
 }
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/GetLoanByExternalIdCommandStrategyTest.java
similarity index 55%
copy from fineract-provider/src/test/java/org/apache/fineract/batch/command/internal/GetLoanByIdCommandStrategyTest.java
copy to fineract-provider/src/test/java/org/apache/fineract/batch/command/internal/GetLoanByExternalIdCommandStrategyTest.java
index d8327f89c..73f87d65d 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/GetLoanByExternalIdCommandStrategyTest.java
@@ -18,7 +18,7 @@
  */
 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;
@@ -26,9 +26,11 @@ import static org.mockito.Mockito.verify;
 
 import java.util.HashSet;
 import java.util.Set;
+import java.util.UUID;
 import java.util.stream.Stream;
 import javax.ws.rs.HttpMethod;
 import javax.ws.rs.core.UriInfo;
+import org.apache.commons.lang3.BooleanUtils;
 import org.apache.commons.lang3.RandomStringUtils;
 import org.apache.fineract.batch.domain.BatchRequest;
 import org.apache.fineract.batch.domain.BatchResponse;
@@ -43,59 +45,77 @@ import org.mockito.Captor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
-public class GetLoanByIdCommandStrategyTest {
+/**
+ * Test class for {@link GetLoanByExternalIdCommandStrategy}.
+ */
+public class GetLoanByExternalIdCommandStrategyTest {
 
+    /**
+     * The query parameter provider.
+     *
+     * @return Arguments.
+     */
     private static Stream<Arguments> provideQueryParameters() {
-        return Stream.of(Arguments.of(null, null, null, 0), Arguments.of("all", null, null, 1),
-                Arguments.of("repaymentSchedule,transactions", null, "guarantors,futureSchedule", 2),
-                Arguments.of("repaymentSchedule,transactions", "id,principal,annualInterestRate", null, 2),
-                Arguments.of("repaymentSchedule,transactions", "id,principal,annualInterestRate", "guarantors,futureSchedule", 3));
+        return Stream.of(Arguments.of(null, null, null, null, 0), Arguments.of("all", null, null, null, 1),
+                Arguments.of("repaymentSchedule,transactions", null, "guarantors,futureSchedule", "true", 3),
+                Arguments.of("repaymentSchedule,transactions", "id,principal,annualInterestRate", null, "false", 3),
+                Arguments.of("repaymentSchedule,transactions", "id,principal,annualInterestRate", "guarantors,futureSchedule", "false", 4));
     }
 
     /**
-     * Test {@link GetLoanByIdCommandStrategy#execute} happy path scenario.
+     * Test {@link GetLoanByExternalIdCommandStrategy#execute} happy path scenario.
      *
      */
     @ParameterizedTest
     @MethodSource("provideQueryParameters")
     public void testExecuteSuccessScenario(final String associations, final String fields, final String exclude,
-            final int noOfQueryParams) {
+            final String staffInSelectedOfficeOnlyFlag, final int noOfQueryParams) {
         // given
         final TestContext testContext = new TestContext();
 
-        final Long loanId = Long.valueOf(RandomStringUtils.randomNumeric(4));
-        final BatchRequest request = getBatchRequest(loanId, associations, exclude, fields);
+        final String loanExternalId = UUID.randomUUID().toString();
+        final BatchRequest request = getBatchRequest(loanExternalId, associations, exclude, fields, staffInSelectedOfficeOnlyFlag);
+        final Boolean staffInSelectedOfficeOnlyBooleanFlag = BooleanUtils.toBoolean(staffInSelectedOfficeOnlyFlag);
         final String responseBody = "{\\\"id\\\":2,\\\"accountNo\\\":\\\"000000002\\\"}";
 
-        given(testContext.loansApiResource.retrieveLoan(eq(loanId), eq(false), eq(associations), eq(exclude), eq(fields),
-                any(UriInfo.class))).willReturn(responseBody);
+        given(testContext.loansApiResource.retrieveLoan(eq(loanExternalId), eq(staffInSelectedOfficeOnlyBooleanFlag), eq(associations),
+                eq(exclude), eq(fields), any(UriInfo.class))).willReturn(responseBody);
 
         // when
         final BatchResponse response = testContext.underTest.execute(request, testContext.uriInfo);
 
         // then
-        assertThat(response.getStatusCode()).isEqualTo(HttpStatus.SC_OK);
-        assertThat(response.getRequestId()).isEqualTo(request.getRequestId());
-        assertThat(response.getHeaders()).isEqualTo(request.getHeaders());
-        assertThat(response.getBody()).isEqualTo(responseBody);
-
-        verify(testContext.loansApiResource).retrieveLoan(eq(loanId), eq(false), eq(associations), eq(exclude), eq(fields),
-                testContext.uriInfoCaptor.capture());
-        MutableUriInfo mutableUriInfo = testContext.uriInfoCaptor.getValue();
-        assertThat(mutableUriInfo.getAdditionalQueryParameters()).hasSize(noOfQueryParams);
+        assertEquals(HttpStatus.SC_OK, response.getStatusCode());
+        assertEquals(request.getRequestId(), response.getRequestId());
+        assertEquals(request.getHeaders(), response.getHeaders());
+        assertEquals(responseBody, response.getBody());
+
+        verify(testContext.loansApiResource).retrieveLoan(eq(loanExternalId), eq(staffInSelectedOfficeOnlyBooleanFlag), eq(associations),
+                eq(exclude), eq(fields), testContext.uriInfoCaptor.capture());
+        final MutableUriInfo mutableUriInfo = testContext.uriInfoCaptor.getValue();
+        assertEquals(noOfQueryParams, mutableUriInfo.getAdditionalQueryParameters().size());
     }
 
     /**
-     * Creates and returns a request with the given loan id.
+     * Creates and returns a request with the given loan external id.
      *
-     * @param loanId
-     *            the loan id
+     * @param loanExternalId
+     *            the loan external id
+     * @param associations
+     *            the associations query param
+     * @param exclude
+     *            exclude query param
+     * @param fields
+     *            fields query param
+     * @param staffInSelectedOfficeOnlyFlag
+     *            staff in selected office only query param
      * @return BatchRequest
      */
-    private BatchRequest getBatchRequest(final Long loanId, final String associations, final String exclude, final String fields) {
+    private BatchRequest getBatchRequest(final String loanExternalId, final String associations, final String exclude, final String fields,
+            final String staffInSelectedOfficeOnlyFlag) {
 
         final BatchRequest br = new BatchRequest();
-        String relativeUrl = "loans/" + loanId;
+        String relativeUrl = "loans/external-id/" + loanExternalId;
 
         Set<String> queryParams = new HashSet<>();
         if (associations != null) {
@@ -107,6 +127,9 @@ public class GetLoanByIdCommandStrategyTest {
         if (fields != null) {
             queryParams.add("fields=" + fields);
         }
+        if (staffInSelectedOfficeOnlyFlag != null) {
+            queryParams.add("staffInSelectedOfficeOnly=" + staffInSelectedOfficeOnlyFlag);
+        }
         if (!queryParams.isEmpty()) {
             relativeUrl = relativeUrl + "?" + String.join("&", queryParams);
         }
@@ -125,20 +148,32 @@ public class GetLoanByIdCommandStrategyTest {
      */
     private static class TestContext {
 
+        /**
+         * The Mock UriInfo
+         */
         @Mock
         private UriInfo uriInfo;
 
+        /**
+         * The Mock {@link LoansApiResource}
+         */
         @Mock
         private LoansApiResource loansApiResource;
 
+        /**
+         * The Captor for UriInfo
+         */
         @Captor
         private ArgumentCaptor<MutableUriInfo> uriInfoCaptor;
 
-        private final GetLoanByIdCommandStrategy underTest;
+        /**
+         * The class under test.
+         */
+        private final GetLoanByExternalIdCommandStrategy underTest;
 
         TestContext() {
             MockitoAnnotations.openMocks(this);
-            underTest = new GetLoanByIdCommandStrategy(loansApiResource);
+            underTest = new GetLoanByExternalIdCommandStrategy(loansApiResource);
         }
     }
 }
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 d8327f89c..38eddf866 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
@@ -43,6 +43,9 @@ import org.mockito.Captor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+/**
+ * Test class for {@link GetLoanByIdCommandStrategy}.
+ */
 public class GetLoanByIdCommandStrategyTest {
 
     private static Stream<Arguments> provideQueryParameters() {
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/GetTransactionByExternalIdCommandStrategyTest.java
similarity index 71%
copy from fineract-provider/src/test/java/org/apache/fineract/batch/command/internal/GetTransactionByIdCommandStrategyTest.java
copy to fineract-provider/src/test/java/org/apache/fineract/batch/command/internal/GetTransactionByExternalIdCommandStrategyTest.java
index 60ef582f8..de72e79c1 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/GetTransactionByExternalIdCommandStrategyTest.java
@@ -22,7 +22,9 @@ import static org.assertj.core.api.Assertions.assertThat;
 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.UUID;
 import java.util.stream.Stream;
 import javax.ws.rs.HttpMethod;
 import javax.ws.rs.core.UriInfo;
@@ -37,14 +39,17 @@ import org.junit.jupiter.params.provider.MethodSource;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
-public class GetTransactionByIdCommandStrategyTest {
+/**
+ * Test class for {@link GetTransactionByExternalIdCommandStrategy}.
+ */
+public class GetTransactionByExternalIdCommandStrategyTest {
 
     private static Stream<Arguments> provideQueryParameters() {
         return Stream.of(Arguments.of(null, 0), Arguments.of("id,date,amount", 1));
     }
 
     /**
-     * Test {@link GetTransactionByIdCommandStrategy#execute} happy path scenario.
+     * Test {@link GetTransactionByExternalIdCommandStrategy#execute} happy path scenario.
      */
     @ParameterizedTest
     @MethodSource("provideQueryParameters")
@@ -52,9 +57,9 @@ public class GetTransactionByIdCommandStrategyTest {
         // 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, fields);
+        final String loanExternalId = UUID.randomUUID().toString();
+        final String transactionExternalId = UUID.randomUUID().toString();
+        final BatchRequest request = getBatchRequest(loanExternalId, transactionExternalId, fields);
         final String responseBody = "{\"id\":12,\"officeId\":1,\"officeName\":\"Head Office\",\"type\":{\"id\":10,\"code\":"
                 + "\"loanTransactionType.accrual\",\"value\":\"Accrual\",\"disbursement\":false,\"repaymentAtDisbursement\":false,"
                 + "\"repayment\":false,\"contra\":false,\"waiveInterest\":false,\"waiveCharges\":false,\"accrual\":true,\"writeOff\":false,"
@@ -66,8 +71,8 @@ public class GetTransactionByIdCommandStrategyTest {
                 + "\"unrecognizedIncomePortion\":0,\"outstandingLoanBalance\":0,\"submittedOnDate\":[2022,3,29],\"manuallyReversed\":false,"
                 + "\"loanChargePaidByList\":[],\"numberOfRepayments\":0}";
 
-        given(testContext.loanTransactionsApiResource.retrieveTransaction(eq(loanId), eq(transactionId), eq(fields), any(UriInfo.class)))
-                .willReturn(responseBody);
+        given(testContext.loanTransactionsApiResource.retrieveTransactionByLoanExternalIdAndTransactionExternalId(eq(loanExternalId),
+                eq(transactionExternalId), eq(fields), any(UriInfo.class))).willReturn(responseBody);
 
         // when
         final BatchResponse response = testContext.subjectToTest.execute(request, testContext.uriInfo);
@@ -77,21 +82,23 @@ public class GetTransactionByIdCommandStrategyTest {
         assertThat(response.getRequestId()).isEqualTo(request.getRequestId());
         assertThat(response.getHeaders()).isEqualTo(request.getHeaders());
         assertThat(response.getBody()).isEqualTo(responseBody);
+        verify(testContext.loanTransactionsApiResource).retrieveTransactionByLoanExternalIdAndTransactionExternalId(eq(loanExternalId),
+                eq(transactionExternalId), eq(fields), any(UriInfo.class));
     }
 
     /**
-     * Creates and returns a request with the given loan id and transaction id.
+     * Creates and returns a request with the given loan external id and transaction external id.
      *
-     * @param loanId
-     *            the loan id
-     * @param transactionId
-     *            the transaction id
+     * @param loanExternalId
+     *            the loan external id
+     * @param transactionExternalId
+     *            the transaction external id
      * @return BatchRequest
      */
-    private BatchRequest getBatchRequest(final Long loanId, final Long transactionId, final String fields) {
+    private BatchRequest getBatchRequest(final String loanExternalId, final String transactionExternalId, final String fields) {
 
         final BatchRequest br = new BatchRequest();
-        String relativeUrl = "loans/" + loanId + "/transactions/" + transactionId;
+        String relativeUrl = "loans/external-id/" + loanExternalId + "/transactions/external-id/" + transactionExternalId;
         if (fields != null) {
             relativeUrl = relativeUrl + "?fields=" + fields;
         }
@@ -110,17 +117,30 @@ public class GetTransactionByIdCommandStrategyTest {
      */
     private static class TestContext {
 
+        /**
+         * The Mock UriInfo
+         */
         @Mock
         private UriInfo uriInfo;
 
+        /**
+         * The Mock {@link LoanTransactionsApiResource}
+         */
         @Mock
         private LoanTransactionsApiResource loanTransactionsApiResource;
 
-        private final GetTransactionByIdCommandStrategy subjectToTest;
+        /**
+         * The class under test.
+         */
+        private final GetTransactionByExternalIdCommandStrategy subjectToTest;
+
+        /**
+         * Constructor.
+         */
 
         TestContext() {
             MockitoAnnotations.openMocks(this);
-            subjectToTest = new GetTransactionByIdCommandStrategy(loanTransactionsApiResource);
+            subjectToTest = new GetTransactionByExternalIdCommandStrategy(loanTransactionsApiResource);
         }
     }
 }
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 60ef582f8..6d8fc136d 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
@@ -37,6 +37,9 @@ import org.junit.jupiter.params.provider.MethodSource;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+/**
+ * Test class for {@link GetTransactionByIdCommandStrategy}.
+ */
 public class GetTransactionByIdCommandStrategyTest {
 
     private static Stream<Arguments> provideQueryParameters() {
@@ -110,14 +113,27 @@ public class GetTransactionByIdCommandStrategyTest {
      */
     private static class TestContext {
 
+        /**
+         * The Mock UriInfo
+         */
         @Mock
         private UriInfo uriInfo;
 
+        /**
+         * The Mock {@link LoanTransactionsApiResource}
+         */
         @Mock
         private LoanTransactionsApiResource loanTransactionsApiResource;
 
+        /**
+         * The class under test.
+         */
         private final GetTransactionByIdCommandStrategy subjectToTest;
 
+        /**
+         * Constructor.
+         */
+
         TestContext() {
             MockitoAnnotations.openMocks(this);
             subjectToTest = new GetTransactionByIdCommandStrategy(loanTransactionsApiResource);
diff --git a/fineract-provider/src/test/java/org/apache/fineract/batch/command/internal/LoanStateTransistionsByExternalIdCommandStrategyTest.java b/fineract-provider/src/test/java/org/apache/fineract/batch/command/internal/LoanStateTransistionsByExternalIdCommandStrategyTest.java
new file mode 100644
index 000000000..ba402c1ba
--- /dev/null
+++ b/fineract-provider/src/test/java/org/apache/fineract/batch/command/internal/LoanStateTransistionsByExternalIdCommandStrategyTest.java
@@ -0,0 +1,170 @@
+/**
+ * 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.mockito.ArgumentMatchers.eq;
+import static org.mockito.BDDMockito.given;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoInteractions;
+
+import java.util.UUID;
+import java.util.stream.Stream;
+import javax.ws.rs.HttpMethod;
+import javax.ws.rs.core.UriInfo;
+import org.apache.commons.lang3.RandomStringUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.fineract.batch.domain.BatchRequest;
+import org.apache.fineract.batch.domain.BatchResponse;
+import org.apache.fineract.portfolio.loanaccount.api.LoansApiResource;
+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;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Test class for {@link LoanStateTransistionsByExternalIdCommandStrategy}.
+ */
+public class LoanStateTransistionsByExternalIdCommandStrategyTest {
+
+    /**
+     * Test {@link LoanStateTransistionsByExternalIdCommandStrategy#execute} happy path scenario.
+     *
+     * @param command
+     *            the command
+     * @param noOfArguments
+     *            the no. of arguments
+     */
+    @ParameterizedTest
+    @MethodSource("provideQueryParameters")
+    public void testLoanStateTransistionsByExternalIdWithCommandSuccessScenario(final String command, final int noOfArguments) {
+        // given
+        final TestContext testContext = new TestContext();
+
+        final String loanExternalId = UUID.randomUUID().toString();
+        final BatchRequest request = getBatchRequest(loanExternalId, command);
+        final String responseBody = "{\"loanId\":13,\"resourceId\":16,\"subResourceId\":26,\"changes\":{\"amount\":10.0,"
+                + "\"transactionDate\":[2022,12,7],\"locale\":\"de_DE\"}}";
+
+        given(testContext.loansApiResource.stateTransitions(eq(loanExternalId), eq(command), eq(request.getBody())))
+                .willReturn(responseBody);
+
+        // when
+        final BatchResponse response = testContext.subjectToTest.execute(request, testContext.uriInfo);
+
+        // then
+        assertEquals(HttpStatus.SC_OK, response.getStatusCode());
+        assertEquals(request.getRequestId(), response.getRequestId());
+        assertEquals(request.getHeaders(), response.getHeaders());
+        assertEquals(responseBody, response.getBody());
+        verify(testContext.loansApiResource).stateTransitions(eq(loanExternalId), eq(command), eq(request.getBody()));
+    }
+
+    /**
+     * Query parameter provider.
+     *
+     * @return the test data stream
+     */
+    private static Stream<Arguments> provideQueryParameters() {
+        return Stream.of(Arguments.of("approve", 1), Arguments.of("disburse", 1));
+    }
+
+    /**
+     * Test {@link LoanStateTransistionsByExternalIdCommandStrategy#execute} error scenario.
+     *
+     */
+    @Test
+    public void testLoanStateTransistionsByExternalIdWithoutCommandErrorScenario() {
+        // given
+        final TestContext testContext = new TestContext();
+
+        final String loanExternalId = UUID.randomUUID().toString();
+        final BatchRequest request = getBatchRequest(loanExternalId, null);
+
+        // when
+        final BatchResponse response = testContext.subjectToTest.execute(request, testContext.uriInfo);
+
+        // then
+        assertEquals(HttpStatus.SC_NOT_IMPLEMENTED, response.getStatusCode());
+        assertEquals(request.getRequestId(), response.getRequestId());
+        assertEquals("Resource with method " + request.getMethod() + " and relativeUrl " + request.getRelativeUrl() + " doesn't exist",
+                response.getBody());
+        verifyNoInteractions(testContext.loansApiResource);
+    }
+
+    /**
+     * Creates and returns a request with the given loan external id.
+     *
+     * @param loanExternalId
+     *            the loan external id
+     * @param chargeCommand
+     *            the charge command
+     * @return BatchRequest
+     */
+    private BatchRequest getBatchRequest(final String loanExternalId, final String chargeCommand) {
+
+        final BatchRequest br = new BatchRequest();
+        String relativeUrl = String.format("loans/external-id/%s", loanExternalId);
+
+        br.setRequestId(Long.valueOf(RandomStringUtils.randomNumeric(5)));
+        br.setRelativeUrl(relativeUrl);
+        if (StringUtils.isNotBlank(chargeCommand)) {
+            br.setRelativeUrl(br.getRelativeUrl() + String.format("?command=%s", chargeCommand));
+        }
+        br.setMethod(HttpMethod.POST);
+        br.setReference(Long.valueOf(RandomStringUtils.randomNumeric(5)));
+        br.setBody("{\"amount\":7.00,\"locale\":\"en\"}");
+
+        return br;
+    }
+
+    /**
+     * Private test context class used since testng runs in parallel to avoid state between tests
+     */
+    private static class TestContext {
+
+        /**
+         * The Mock UriInfo
+         */
+        @Mock
+        private UriInfo uriInfo;
+
+        /**
+         * The Mock {@link LoansApiResource}
+         */
+        @Mock
+        private LoansApiResource loansApiResource;
+
+        /**
+         * The class under test.
+         */
+        private final LoanStateTransistionsByExternalIdCommandStrategy subjectToTest;
+
+        /**
+         * Constructor.
+         */
+        TestContext() {
+            MockitoAnnotations.openMocks(this);
+            subjectToTest = new LoanStateTransistionsByExternalIdCommandStrategy(loansApiResource);
+        }
+    }
+}
diff --git a/fineract-provider/src/test/java/org/apache/fineract/batch/command/internal/ModifyLoanApplicationByExternalIdCommandStrategyTest.java b/fineract-provider/src/test/java/org/apache/fineract/batch/command/internal/ModifyLoanApplicationByExternalIdCommandStrategyTest.java
new file mode 100644
index 000000000..ad6e2304f
--- /dev/null
+++ b/fineract-provider/src/test/java/org/apache/fineract/batch/command/internal/ModifyLoanApplicationByExternalIdCommandStrategyTest.java
@@ -0,0 +1,137 @@
+/**
+ * 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.mockito.ArgumentMatchers.eq;
+import static org.mockito.BDDMockito.given;
+import static org.mockito.Mockito.verify;
+
+import java.util.UUID;
+import java.util.stream.Stream;
+import javax.ws.rs.HttpMethod;
+import javax.ws.rs.core.UriInfo;
+import org.apache.commons.lang3.RandomStringUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.fineract.batch.domain.BatchRequest;
+import org.apache.fineract.batch.domain.BatchResponse;
+import org.apache.fineract.portfolio.loanaccount.api.LoansApiResource;
+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.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Test class for {@link ModifyLoanApplicationByExternalIdCommandStrategy}
+ */
+public class ModifyLoanApplicationByExternalIdCommandStrategyTest {
+
+    /**
+     * Test {@link ModifyLoanApplicationByExternalIdCommandStrategy#execute(BatchRequest, UriInfo)} happy path scenario.
+     */
+    @ParameterizedTest
+    @MethodSource("commandParamDataProvider")
+    public void testExecuteSuccessScenario(final String command, final String responseBody) {
+        final TestContext testContext = new TestContext();
+        final String loanExternalId = UUID.randomUUID().toString();
+        final BatchRequest request = getBatchRequest(loanExternalId, command);
+
+        given(testContext.loansApiResource.modifyLoanApplication(eq(loanExternalId), eq(command), eq(request.getBody())))
+                .willReturn(responseBody);
+
+        final BatchResponse response = testContext.testSubject.execute(request, testContext.uriInfo);
+
+        // then
+        assertEquals(HttpStatus.SC_OK, response.getStatusCode());
+        assertEquals(request.getRequestId(), response.getRequestId());
+        assertEquals(request.getHeaders(), response.getHeaders());
+        assertEquals(responseBody, response.getBody());
+        verify(testContext.loansApiResource).modifyLoanApplication(eq(loanExternalId), eq(command), eq(request.getBody()));
+    }
+
+    /**
+     * Command Param data provider
+     *
+     * @return test data stream
+     */
+    private static Stream<Arguments> commandParamDataProvider() {
+        return Stream.of(
+                Arguments.of("markAsFraud", "{\"officeId\":1,\"clientId\":2,\"loanId\":2,\"resourceId\":2,\"changes\":{\"fraud\":true}}"),
+                Arguments.of(null, "body"));
+    }
+
+    /**
+     * Creates and returns a request with the given loan external id.
+     *
+     * @param loanExternalId
+     *            the loan external id
+     * @param queryParameter
+     *            the command query param
+     * @return {@link BatchRequest}
+     */
+    private BatchRequest getBatchRequest(final String loanExternalId, final String queryParameter) {
+        final BatchRequest batchRequest = new BatchRequest();
+
+        String relativeUrl = String.format("loans/external-id/%s", loanExternalId);
+
+        if (StringUtils.isNotBlank(queryParameter)) {
+            relativeUrl = relativeUrl + "?command=" + queryParameter;
+        }
+
+        batchRequest.setRequestId(Long.valueOf(RandomStringUtils.randomNumeric(5)));
+        batchRequest.setRelativeUrl(relativeUrl);
+        batchRequest.setMethod(HttpMethod.PUT);
+        batchRequest.setReference(Long.valueOf(RandomStringUtils.randomNumeric(5)));
+        batchRequest.setBody("{\"fraud\": \"true\"}");
+        return batchRequest;
+    }
+
+    /**
+     * 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 loans api resource
+         */
+        @Mock
+        private LoansApiResource loansApiResource;
+
+        /**
+         * {@link ModifyLoanApplicationByExternalIdCommandStrategy} under test
+         */
+        private final ModifyLoanApplicationByExternalIdCommandStrategy testSubject;
+
+        /**
+         * Constructor
+         */
+        TestContext() {
+            MockitoAnnotations.openMocks(this);
+            testSubject = new ModifyLoanApplicationByExternalIdCommandStrategy(loansApiResource);
+        }
+    }
+}
diff --git a/fineract-provider/src/test/java/org/apache/fineract/batch/command/internal/ModifyLoanApplicationCommandStrategyTest.java b/fineract-provider/src/test/java/org/apache/fineract/batch/command/internal/ModifyLoanApplicationCommandStrategyTest.java
new file mode 100644
index 000000000..ab43b0783
--- /dev/null
+++ b/fineract-provider/src/test/java/org/apache/fineract/batch/command/internal/ModifyLoanApplicationCommandStrategyTest.java
@@ -0,0 +1,135 @@
+/**
+ * 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.mockito.ArgumentMatchers.eq;
+import static org.mockito.BDDMockito.given;
+import static org.mockito.Mockito.verify;
+
+import java.util.stream.Stream;
+import javax.ws.rs.HttpMethod;
+import javax.ws.rs.core.UriInfo;
+import org.apache.commons.lang3.RandomStringUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.fineract.batch.domain.BatchRequest;
+import org.apache.fineract.batch.domain.BatchResponse;
+import org.apache.fineract.portfolio.loanaccount.api.LoansApiResource;
+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.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Test class for {@link ModifyLoanApplicationCommandStrategy}
+ */
+public class ModifyLoanApplicationCommandStrategyTest {
+
+    /**
+     * Test {@link ModifyLoanApplicationCommandStrategy#execute(BatchRequest, UriInfo)} happy path scenario.
+     */
+    @ParameterizedTest
+    @MethodSource("commandParamDataProvider")
+    public void testExecuteSuccessScenario(final String command, final String responseBody) {
+        final TestContext testContext = new TestContext();
+        final Long loanId = Long.valueOf(RandomStringUtils.randomNumeric(4));
+        final BatchRequest request = getBatchRequest(loanId, command);
+
+        given(testContext.loansApiResource.modifyLoanApplication(eq(loanId), eq(command), eq(request.getBody()))).willReturn(responseBody);
+
+        final BatchResponse response = testContext.testSubject.execute(request, testContext.uriInfo);
+
+        // then
+        assertEquals(HttpStatus.SC_OK, response.getStatusCode());
+        assertEquals(request.getRequestId(), response.getRequestId());
+        assertEquals(request.getHeaders(), response.getHeaders());
+        assertEquals(responseBody, response.getBody());
+        verify(testContext.loansApiResource).modifyLoanApplication(eq(loanId), eq(command), eq(request.getBody()));
+    }
+
+    /**
+     * Command Param data provider
+     *
+     * @return test data stream
+     */
+    private static Stream<Arguments> commandParamDataProvider() {
+        return Stream.of(
+                Arguments.of("markAsFraud", "{\"officeId\":1,\"clientId\":2,\"loanId\":2,\"resourceId\":2,\"changes\":{\"fraud\":true}}"),
+                Arguments.of(null, "body"));
+    }
+
+    /**
+     * Creates and returns a request with the given loan id.
+     *
+     * @param loanId
+     *            the loan id
+     * @param queryParameter
+     *            the command query param
+     * @return {@link BatchRequest}
+     */
+    private BatchRequest getBatchRequest(final Long loanId, final String queryParameter) {
+        final BatchRequest batchRequest = new BatchRequest();
+
+        String relativeUrl = String.format("loans/%s", loanId);
+
+        if (StringUtils.isNotBlank(queryParameter)) {
+            relativeUrl = relativeUrl + "?command=" + queryParameter;
+        }
+
+        batchRequest.setRequestId(Long.valueOf(RandomStringUtils.randomNumeric(5)));
+        batchRequest.setRelativeUrl(relativeUrl);
+        batchRequest.setMethod(HttpMethod.PUT);
+        batchRequest.setReference(Long.valueOf(RandomStringUtils.randomNumeric(5)));
+        batchRequest.setBody("{\"fraud\": \"true\"}");
+        return batchRequest;
+    }
+
+    /**
+     * 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 loans api resource
+         */
+        @Mock
+        private LoansApiResource loansApiResource;
+
+        /**
+         * {@link ModifyLoanApplicationCommandStrategy} under test
+         */
+        private final ModifyLoanApplicationCommandStrategy testSubject;
+
+        /**
+         * Constructor
+         */
+        TestContext() {
+            MockitoAnnotations.openMocks(this);
+            testSubject = new ModifyLoanApplicationCommandStrategy(loansApiResource);
+        }
+    }
+}
diff --git a/fineract-provider/src/test/java/org/apache/fineract/batch/command/internal/UpdateDatatableEntryOneToManyCommandStrategyTest.java b/fineract-provider/src/test/java/org/apache/fineract/batch/command/internal/UpdateDatatableEntryOneToManyCommandStrategyTest.java
index 7300252f5..4cec6c4db 100644
--- a/fineract-provider/src/test/java/org/apache/fineract/batch/command/internal/UpdateDatatableEntryOneToManyCommandStrategyTest.java
+++ b/fineract-provider/src/test/java/org/apache/fineract/batch/command/internal/UpdateDatatableEntryOneToManyCommandStrategyTest.java
@@ -29,10 +29,13 @@ import org.apache.fineract.batch.domain.BatchRequest;
 import org.apache.fineract.batch.domain.BatchResponse;
 import org.apache.fineract.infrastructure.dataqueries.api.DatatablesApiResource;
 import org.apache.http.HttpStatus;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+/**
+ * Test class for {@link UpdateDatatableEntryOneToManyCommandStrategy}.
+ */
 public class UpdateDatatableEntryOneToManyCommandStrategyTest {
 
     /**
@@ -56,10 +59,10 @@ public class UpdateDatatableEntryOneToManyCommandStrategyTest {
         final BatchResponse response = testContext.subjectToTest.execute(request, testContext.uriInfo);
 
         // then
-        assertEquals(response.getStatusCode(), HttpStatus.SC_OK);
-        assertEquals(response.getRequestId(), request.getRequestId());
-        assertEquals(response.getHeaders(), request.getHeaders());
-        assertEquals(response.getBody(), responseBody);
+        assertEquals(HttpStatus.SC_OK, response.getStatusCode());
+        assertEquals(request.getRequestId(), response.getRequestId());
+        assertEquals(request.getHeaders(), response.getHeaders());
+        assertEquals(responseBody, response.getBody());
     }
 
     /**
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 62d2fd93f..7b2ca1f65 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
@@ -37,7 +37,9 @@ import java.util.Arrays;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.UUID;
 import org.apache.commons.lang3.RandomStringUtils;
+import org.apache.commons.lang3.RandomUtils;
 import org.apache.fineract.batch.command.internal.AdjustTransactionCommandStrategy;
 import org.apache.fineract.batch.command.internal.CreateTransactionLoanCommandStrategy;
 import org.apache.fineract.batch.domain.BatchRequest;
@@ -46,6 +48,7 @@ import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
 import org.apache.fineract.integrationtests.common.BatchHelper;
 import org.apache.fineract.integrationtests.common.ClientHelper;
 import org.apache.fineract.integrationtests.common.CollateralManagementHelper;
+import org.apache.fineract.integrationtests.common.GlobalConfigurationHelper;
 import org.apache.fineract.integrationtests.common.Utils;
 import org.apache.fineract.integrationtests.common.charges.ChargesHelper;
 import org.apache.fineract.integrationtests.common.loans.LoanProductTestBuilder;
@@ -105,6 +108,7 @@ public class BatchApiTest {
         this.requestSpec.header("Authorization", "Basic " + Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey());
         this.responseSpec = new ResponseSpecBuilder().expectStatusCode(200).build();
         this.datatableHelper = new DatatableHelper(this.requestSpec, this.responseSpec);
+        GlobalConfigurationHelper.updateIsAutomaticExternalIdGenerationEnabled(this.requestSpec, this.responseSpec, true);
     }
 
     /**
@@ -365,7 +369,7 @@ public class BatchApiTest {
         final BatchRequest br3 = BatchHelper.applyLoanRequest(4724L, 4723L, productId, clientCollateralId);
 
         // Create a Collect Charges Request
-        final BatchRequest br4 = BatchHelper.collectChargesRequest(4725L, 4724L);
+        final BatchRequest br4 = BatchHelper.collectChargesByLoanIdRequest(4725L, 4724L);
 
         final List<BatchRequest> batchRequests = new ArrayList<>();
 
@@ -426,9 +430,10 @@ public class BatchApiTest {
 
         final BatchRequest disburseLoanRequest = BatchHelper.disburseLoanRequest(disburseLoanRequestId, approveLoanRequestId);
 
-        final BatchRequest createChargeRequest = BatchHelper.createChargeRequest(createChargeRequestId, disburseLoanRequestId, chargeId);
+        final BatchRequest createChargeRequest = BatchHelper.createChargeByLoanIdRequest(createChargeRequestId, disburseLoanRequestId,
+                chargeId);
 
-        final BatchRequest getChargeByIdRequest = BatchHelper.getChargeByIdCommandStrategy(getChargeByIdRequestId, createChargeRequestId);
+        final BatchRequest getChargeByIdRequest = BatchHelper.getChargeByLoanIdChargeId(getChargeByIdRequestId, createChargeRequestId);
 
         // Create batch requests list
         final List<BatchRequest> batchRequests = Arrays.asList(applyLoanRequest, approveLoanRequest, disburseLoanRequest,
@@ -445,6 +450,183 @@ public class BatchApiTest {
         Assertions.assertEquals(HttpStatus.SC_OK, responses.get(4).getStatusCode(), "Verify Status Code 200 for Get Charge By Id");
     }
 
+    /**
+     * Test for a successful charge adjustment. A '200' status code is expected on successful responses.
+     *
+     * @see AdjustTransactionCommandStrategy
+     */
+    @Test
+    public void shouldReturnOkStatusOnSuccessfulChargeAdjustment() {
+        final String loanProductJSON = new LoanProductTestBuilder() //
+                .withPrincipal("1000.00") //
+                .withNumberOfRepayments("24") //
+                .withRepaymentAfterEvery("1") //
+                .withRepaymentTypeAsMonth() //
+                .withinterestRatePerPeriod("2") //
+                .withInterestRateFrequencyTypeAsMonths() //
+                .withAmortizationTypeAsEqualPrincipalPayment() //
+                .withInterestTypeAsDecliningBalance() //
+                .currencyDetails("0", "100").build(null);
+
+        final Long applyLoanRequestId = Long.valueOf(RandomStringUtils.randomNumeric(4));
+        final Long approveLoanRequestId = applyLoanRequestId + 1;
+        final Long disburseLoanRequestId = approveLoanRequestId + 1;
+        final Long createChargeRequestId = disburseLoanRequestId + 1;
+        final Long adjustChargeRequestId = createChargeRequestId + 1;
+        final Long getTransactionRequestId = adjustChargeRequestId + 1;
+
+        // 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 charge object and get id
+        final Integer chargeId = ChargesHelper.createCharges(this.requestSpec, this.responseSpec,
+                ChargesHelper.getLoanSpecifiedDueDateJSON());
+
+        final BatchRequest applyLoanRequest = BatchHelper.applyLoanRequestWithClientId(applyLoanRequestId, clientId, productId);
+
+        final BatchRequest approveLoanRequest = BatchHelper.approveLoanRequest(approveLoanRequestId, applyLoanRequestId);
+
+        final BatchRequest disburseLoanRequest = BatchHelper.disburseLoanRequest(disburseLoanRequestId, approveLoanRequestId);
+
+        final BatchRequest createChargeRequest = BatchHelper.createChargeByLoanIdRequest(createChargeRequestId, disburseLoanRequestId,
+                chargeId);
+
+        final BatchRequest adjustChargeRequest = BatchHelper.adjustChargeRequest(adjustChargeRequestId, createChargeRequestId);
+
+        final BatchRequest getTransactionRequest = BatchHelper.getTransactionByIdRequest(getTransactionRequestId, adjustChargeRequestId,
+                true);
+
+        // Create batch requests list
+        final List<BatchRequest> batchRequests = Arrays.asList(applyLoanRequest, approveLoanRequest, disburseLoanRequest,
+                createChargeRequest, adjustChargeRequest, getTransactionRequest);
+
+        // Create batch responses list
+        final List<BatchResponse> responses = BatchHelper.postBatchRequestsWithoutEnclosingTransaction(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 Create Charge");
+        Assertions.assertEquals(HttpStatus.SC_OK, responses.get(4).getStatusCode(), "Verify Status Code 200 for Adjust Charge");
+        Assertions.assertEquals(HttpStatus.SC_OK, responses.get(5).getStatusCode(), "Verify Status Code 200 for Get Transaction By Id");
+
+        final FromJsonHelper jsonHelper = new FromJsonHelper();
+        final JsonObject chargeAdjustment = jsonHelper.parse(responses.get(5).getBody()).getAsJsonObject().get("type").getAsJsonObject();
+
+        Assertions.assertEquals("Charge Adjustment", chargeAdjustment.get("value").getAsString());
+        Assertions.assertTrue(chargeAdjustment.get("chargeAdjustment").getAsBoolean());
+    }
+
+    /**
+     * Tests that a new charge was added to a newly created loan and charges are Collected properly 200(OK) status was
+     * returned for successful responses. It first creates a new client and apply a loan, then creates a new charge for
+     * the create loan and then fetches all the applied charges using external id
+     *
+     * @see org.apache.fineract.batch.command.internal.CollectChargesCommandStrategy
+     * @see org.apache.fineract.batch.command.internal.CreateChargeCommandStrategy
+     */
+    @Test
+    public void shouldReturnOkStatusForCreateAndGetChargeByExternalIdCommand() {
+        final String loanProductJSON = new LoanProductTestBuilder() //
+                .withPrincipal("1000.00") //
+                .withNumberOfRepayments("24") //
+                .withRepaymentAfterEvery("1") //
+                .withRepaymentTypeAsMonth() //
+                .withinterestRatePerPeriod("2") //
+                .withInterestRateFrequencyTypeAsMonths() //
+                .withAmortizationTypeAsEqualPrincipalPayment() //
+                .withInterestTypeAsDecliningBalance() //
+                .currencyDetails("0", "100").build(null);
+
+        final Long applyLoanRequestId = Long.valueOf(RandomStringUtils.randomNumeric(4));
+        final Long approveLoanRequestId = applyLoanRequestId + 1;
+        final Long disburseLoanRequestId = approveLoanRequestId + 1;
+        final Long getLoanRequestId = disburseLoanRequestId + 1;
+        final Long createChargeRequestId = getLoanRequestId + 1;
+        final Long collectChargesRequestId = createChargeRequestId + 1;
+        final Long adjustChargeRequestId = createChargeRequestId + 1;
+        final Long getTransactionByExternalIdRequestId = adjustChargeRequestId + 1;
+        final Long getChargeByIdRequestId = getTransactionByExternalIdRequestId + 1;
+
+        // 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 charge object and get id
+        final Integer chargeId = ChargesHelper.createCharges(this.requestSpec, this.responseSpec,
+                ChargesHelper.getLoanSpecifiedDueDateJSON());
+
+        final BatchRequest applyLoanRequest = BatchHelper.applyLoanRequestWithClientId(applyLoanRequestId, clientId, productId);
+
+        final BatchRequest approveLoanRequest = BatchHelper.transistionLoanStateByExternalId(approveLoanRequestId, applyLoanRequestId,
+                LocalDate.now(ZoneId.systemDefault()).minusDays(10), "approve");
+
+        final BatchRequest disburseLoanRequest = BatchHelper.transistionLoanStateByExternalId(disburseLoanRequestId, approveLoanRequestId,
+                LocalDate.now(ZoneId.systemDefault()).minusDays(8), "disburse");
+
+        final BatchRequest getLoanRequest = BatchHelper.getLoanByExternalIdRequest(getLoanRequestId, approveLoanRequestId,
+                "associations=all");
+
+        final BatchRequest createChargeRequest = BatchHelper.createChargeByLoanExternalIdRequest(createChargeRequestId, getLoanRequestId,
+                chargeId);
+
+        final BatchRequest collectChargesRequest = BatchHelper.collectChargesByLoanExternalIdRequest(collectChargesRequestId,
+                getLoanRequestId);
+
+        // Create batch requests list
+        final List<BatchRequest> batchRequests = Arrays.asList(applyLoanRequest, approveLoanRequest, disburseLoanRequest, getLoanRequest,
+                createChargeRequest, collectChargesRequest);
+
+        // Create batch responses list
+        final List<BatchResponse> responses = BatchHelper.postBatchRequestsWithoutEnclosingTransaction(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 Get Loan");
+        Assertions.assertEquals(HttpStatus.SC_OK, responses.get(4).getStatusCode(), "Verify Status Code 200 for Create Charge");
+        Assertions.assertEquals(HttpStatus.SC_OK, responses.get(5).getStatusCode(), "Verify Status Code 200 for Collect charges");
+
+        final FromJsonHelper jsonHelper = new FromJsonHelper();
+        final String loanExternalId = jsonHelper.parse(responses.get(3).getBody()).getAsJsonObject().get("externalId").getAsString();
+        final String chargeExternalId = jsonHelper.parse(responses.get(4).getBody()).getAsJsonObject().get("resourceExternalId")
+                .getAsString();
+
+        final BatchRequest adjustChargeByExternalId = BatchHelper.adjustChargeByExternalIdRequest(adjustChargeRequestId, null,
+                loanExternalId, chargeExternalId);
+        final BatchRequest getTransactionByExternalIdRequest = BatchHelper
+                .getTransactionByExternalIdRequest(getTransactionByExternalIdRequestId, adjustChargeRequestId, loanExternalId, true);
+        final BatchRequest getChargeByIdRequest = BatchHelper.getChargeByLoanExternalIdChargeExternalId(getChargeByIdRequestId,
+                getTransactionByExternalIdRequestId, loanExternalId, chargeExternalId);
+
+        // Create batch responses list
+        final List<BatchResponse> adjustChargeAndGetResponses = BatchHelper.postBatchRequestsWithoutEnclosingTransaction(this.requestSpec,
+                this.responseSpec,
+                BatchHelper.toJsonString(Arrays.asList(adjustChargeByExternalId, getTransactionByExternalIdRequest, getChargeByIdRequest)));
+
+        Assertions.assertEquals(HttpStatus.SC_OK, adjustChargeAndGetResponses.get(0).getStatusCode(),
+                "Verify Status Code 200 for Adjust Charge By External Id");
+        Assertions.assertEquals(HttpStatus.SC_OK, adjustChargeAndGetResponses.get(1).getStatusCode(),
+                "Verify Status Code 200 for Get Transaction By Id");
+        Assertions.assertEquals(HttpStatus.SC_OK, adjustChargeAndGetResponses.get(2).getStatusCode(),
+                "Verify Status Code 200 for Get Charge By Id");
+
+        final JsonObject chargeAdjustment = jsonHelper.parse(adjustChargeAndGetResponses.get(1).getBody()).getAsJsonObject().get("type")
+                .getAsJsonObject();
+
+        Assertions.assertEquals("Charge Adjustment", chargeAdjustment.get("value").getAsString());
+        Assertions.assertTrue(chargeAdjustment.get("chargeAdjustment").getAsBoolean());
+    }
+
     /**
      * Tests that batch repayment for loans is happening properly. Collected properly 200(OK) status was returned for
      * successful responses. It first creates a new loan and then makes two repayments for it and then verifies that
@@ -852,7 +1034,7 @@ public class BatchApiTest {
         final BatchRequest batchRequest3 = BatchHelper.disburseLoanRequest(disburseLoanRequestId, approveLoanRequestId);
 
         // Create a getTransaction Request
-        final BatchRequest batchRequest4 = BatchHelper.getTransactionByIdRequest(getTransactionRequestId, disburseLoanRequestId);
+        final BatchRequest batchRequest4 = BatchHelper.getTransactionByIdRequest(getTransactionRequestId, disburseLoanRequestId, false);
 
         final List<BatchRequest> batchRequests = Arrays.asList(batchRequest1, batchRequest2, batchRequest3, batchRequest4);
 
@@ -1391,6 +1573,111 @@ public class BatchApiTest {
         Assertions.assertEquals(date, reversedOnDate);
     }
 
+    /**
+     * Test for the successful repayment reversal transaction using loan external id and transaction external id. A
+     * '200' status code is expected on successful responses.
+     *
+     * @see AdjustTransactionCommandStrategy
+     */
+    @Test
+    public void shouldReturnOkStatusForBatchRepaymentReversalUsingExternalId() {
+
+        final String loanProductJSON = new LoanProductTestBuilder() //
+                .withPrincipal("10000000.00") //
+                .withNumberOfRepayments("24") //
+                .withRepaymentAfterEvery("1") //
+                .withRepaymentTypeAsMonth() //
+                .withinterestRatePerPeriod("2") //
+                .withInterestRateFrequencyTypeAsMonths() //
+                .withAmortizationTypeAsEqualPrincipalPayment() //
+                .withInterestTypeAsDecliningBalance() //
+                .currencyDetails("0", "100").build(null);
+
+        final Integer productId = new LoanTransactionHelper(this.requestSpec, this.responseSpec).getLoanProductId(loanProductJSON);
+
+        final LocalDate date = LocalDate.now(Utils.getZoneIdOfTenant());
+        final Long applyLoanRequestId = Long.valueOf(RandomStringUtils.randomNumeric(4));
+        final Long approveLoanRequestId = applyLoanRequestId + 1;
+        final Long disburseLoanRequestId = approveLoanRequestId + 1;
+        final Long getLoanBeforeTxnRequestId = disburseLoanRequestId + 1;
+        final Long repayLoanRequestId = getLoanBeforeTxnRequestId + 1;
+        final Long getLoanAfterTxnRequestId = repayLoanRequestId + 1;
+        final Long repayReversalRequestId = getLoanAfterTxnRequestId + 1;
+        final Long getLoanAfterReversal = repayReversalRequestId + 1;
+
+        // Create client
+        final Integer clientId = ClientHelper.createClient(this.requestSpec, this.responseSpec);
+        ClientHelper.verifyClientCreatedOnServer(this.requestSpec, this.responseSpec, clientId);
+        final String loanExternalId = UUID.randomUUID().toString();
+
+        // Create an apply loan request
+        final BatchRequest applyLoanRequest = BatchHelper.applyLoanRequestWithClientIdAndExternalId(applyLoanRequestId, clientId, productId,
+                loanExternalId);
+
+        // Create an approve loan request
+        final BatchRequest approveLoanRequest = BatchHelper.approveLoanRequest(approveLoanRequestId, applyLoanRequestId);
+
+        // Create a disburse loan request
+        final BatchRequest disburseLoanRequest = BatchHelper.disburseLoanRequest(disburseLoanRequestId, approveLoanRequestId);
+
+        // Get loan transactions request
+        final BatchRequest getLoanTransactionsRequestBeforeTxn = BatchHelper.getLoanByIdRequest(getLoanBeforeTxnRequestId,
+                disburseLoanRequestId, "associations=transactions");
+
+        // Create a repayment request by external id
+        final BatchRequest repaymentRequest = BatchHelper.createTransactionRequestByLoanExternalId(repayLoanRequestId,
+                getLoanBeforeTxnRequestId, "repayment", "500", LocalDate.now(ZoneId.systemDefault()));
+
+        // Get loan transactions request
+        final BatchRequest getLoanTransactionsRequestAfterTxn = BatchHelper.getLoanByIdRequest(getLoanAfterTxnRequestId, repayLoanRequestId,
+                "associations=transactions");
+
+        final List<BatchRequest> batchRequests = Arrays.asList(applyLoanRequest, approveLoanRequest, disburseLoanRequest,
+                getLoanTransactionsRequestBeforeTxn, repaymentRequest, getLoanTransactionsRequestAfterTxn);
+
+        // Because loanExternalId & transactionExternalId are coming from 2 different responses, there is no easy way to
+        // use them as reference in 1 batch api call.
+        // So we are splitting repayment & reversal into 2 different batch api invocations
+        final List<BatchResponse> responses = BatchHelper.postBatchRequestsWithoutEnclosingTransaction(this.requestSpec, this.responseSpec,
+                BatchHelper.toJsonString(batchRequests));
+
+        final FromJsonHelper jsonHelper = new FromJsonHelper();
+        final String loanExternalIdDisburseLoanResponse = jsonHelper.parse(responses.get(3).getBody()).getAsJsonObject().get("externalId")
+                .getAsString();
+        final Long loanId = jsonHelper.parse(responses.get(3).getBody()).getAsJsonObject().get("id").getAsLong();
+        final String transactionExternalId = jsonHelper.parse(responses.get(4).getBody()).getAsJsonObject().get("resourceExternalId")
+                .getAsString();
+        Assertions.assertNotNull(loanExternalIdDisburseLoanResponse);
+        Assertions.assertEquals(loanExternalId, loanExternalIdDisburseLoanResponse);
+        Assertions.assertNotNull(transactionExternalId);
+
+        // Create a repayment reversal request by external id
+        final BatchRequest repaymentReversalRequest = BatchHelper.createAdjustTransactionByExternalIdRequest(repayReversalRequestId, null,
+                loanExternalIdDisburseLoanResponse, transactionExternalId, "0", date);
+
+        final BatchRequest getLoanByIdWithTransactions = BatchHelper.getLoanByIdRequest(loanId, getLoanAfterReversal,
+                repayReversalRequestId, "associations=transactions");
+
+        final List<BatchRequest> reversalAndGetBatchRequest = Arrays.asList(repaymentReversalRequest, getLoanByIdWithTransactions);
+
+        final List<BatchResponse> reversalResponses = BatchHelper.postBatchRequestsWithoutEnclosingTransaction(this.requestSpec,
+                this.responseSpec, BatchHelper.toJsonString(reversalAndGetBatchRequest));
+
+        final JsonObject repayment = jsonHelper.parse(reversalResponses.get(1).getBody()).getAsJsonObject().get("transactions")
+                .getAsJsonArray().get(2).getAsJsonObject();
+
+        final JsonArray dateArray = repayment.get("reversedOnDate").getAsJsonArray();
+        final LocalDate reversedOnDate = LocalDate.of(dateArray.get(0).getAsInt(), dateArray.get(1).getAsInt(),
+                dateArray.get(2).getAsInt());
+
+        Assertions.assertEquals(HttpStatus.SC_OK, (long) reversalResponses.get(0).getStatusCode(),
+                "Verify Status Code 200 for repayment reversal");
+        Assertions.assertEquals("Repayment", repayment.get("type").getAsJsonObject().get("value").getAsString());
+
+        Assertions.assertTrue(repayment.get("manuallyReversed").getAsBoolean());
+        Assertions.assertEquals(date, reversedOnDate);
+    }
+
     /**
      * Test for the successful repayment chargeback transaction. A '200' status code is expected on successful
      * responses.
@@ -1798,6 +2085,131 @@ public class BatchApiTest {
         Assertions.assertEquals(changes.get(columnName2).getAsString(), columnValue2 + "1");
     }
 
+    /**
+     * Tests that a loan information was successfully updated through updateLoanCommand. A 'changes' parameter is
+     * returned as part of response after successful update of loan information. In this test, we are marking an active
+     * loan account as fraud.
+     *
+     * @see org.apache.fineract.batch.command.internal.ModifyLoanApplicationCommandStrategy
+     */
+    @Test
+    public void shouldReflectChangesOnLoanUpdate() {
+
+        final String loanProductJSON = new LoanProductTestBuilder() //
+                .withPrincipal("1000.00") //
+                .withNumberOfRepayments("24") //
+                .withRepaymentAfterEvery("1") //
+                .withRepaymentTypeAsMonth() //
+                .withinterestRatePerPeriod("2") //
+                .withInterestRateFrequencyTypeAsMonths() //
+                .withAmortizationTypeAsEqualPrincipalPayment() //
+                .withInterestTypeAsDecliningBalance() //
+                .currencyDetails("0", "100").build(null);
+
+        final Long applyLoanRequestId = RandomUtils.nextLong(100, 1000);
+        final Long approveLoanRequestId = applyLoanRequestId + 1;
+        final Long disburseLoanRequestId = approveLoanRequestId + 1;
+        final Long updateLoanRequestId = disburseLoanRequestId + 1;
+
+        // 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);
+
+        final BatchRequest applyLoanRequest = BatchHelper.applyLoanRequestWithClientId(applyLoanRequestId, clientId, productId);
+
+        final BatchRequest approveLoanRequest = BatchHelper.approveLoanRequest(approveLoanRequestId, applyLoanRequestId);
+
+        final BatchRequest disburseLoanRequest = BatchHelper.disburseLoanRequest(disburseLoanRequestId, approveLoanRequestId);
+
+        final BatchRequest updateLoanRequest = BatchHelper.createLoanRequestMarkAsFraud(updateLoanRequestId, disburseLoanRequestId);
+
+        // Create batch requests list
+        final List<BatchRequest> batchRequests = Arrays.asList(applyLoanRequest, approveLoanRequest, disburseLoanRequest,
+                updateLoanRequest);
+
+        final String jsonifiedRequest = BatchHelper.toJsonString(batchRequests);
+
+        final List<BatchResponse> response = BatchHelper.postBatchRequestsWithoutEnclosingTransaction(this.requestSpec, this.responseSpec,
+                jsonifiedRequest);
+
+        // Get the changes parameter from updateLoan Response
+        final JsonObject changes = new FromJsonHelper().parse(response.get(3).getBody()).getAsJsonObject().get("changes").getAsJsonObject();
+
+        Assertions.assertEquals(HttpStatus.SC_OK, response.get(3).getStatusCode(), "Verify Status Code 200 for update loan application");
+        Assertions.assertEquals("true", changes.get("fraud").getAsString());
+    }
+
+    /**
+     * Tests that a loan information was successfully updated through updateLoanCommand using external id. A 'changes'
+     * parameter is returned as part of response after successful update of loan information. In this test, we are
+     * marking an active loan account as fraud.
+     *
+     * @see org.apache.fineract.batch.command.internal.ModifyLoanApplicationByExternalIdCommandStrategy
+     */
+    @Test
+    public void shouldReflectChangesOnLoanUpdateByExternalId() {
+
+        final String loanProductJSON = new LoanProductTestBuilder() //
+                .withPrincipal("1000.00") //
+                .withNumberOfRepayments("24") //
+                .withRepaymentAfterEvery("1") //
+                .withRepaymentTypeAsMonth() //
+                .withinterestRatePerPeriod("2") //
+                .withInterestRateFrequencyTypeAsMonths() //
+                .withAmortizationTypeAsEqualPrincipalPayment() //
+                .withInterestTypeAsDecliningBalance() //
+                .currencyDetails("0", "100").build(null);
+
+        final Long applyLoanRequestId = RandomUtils.nextLong(100, 1000);
+        final Long approveLoanRequestId = applyLoanRequestId + 1;
+        final Long disburseLoanRequestId = approveLoanRequestId + 1;
+        final Long updateLoanRequestId = disburseLoanRequestId + 1;
+        final Long getLoanRequestId = updateLoanRequestId + 1;
+
+        // 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);
+
+        final BatchRequest applyLoanRequest = BatchHelper.applyLoanRequestWithClientId(applyLoanRequestId, clientId, productId);
+
+        final BatchRequest approveLoanRequest = BatchHelper.transistionLoanStateByExternalId(approveLoanRequestId, applyLoanRequestId,
+                LocalDate.now(ZoneId.systemDefault()).minusDays(10), "approve");
+
+        final BatchRequest disburseLoanRequest = BatchHelper.transistionLoanStateByExternalId(disburseLoanRequestId, approveLoanRequestId,
+                LocalDate.now(ZoneId.systemDefault()).minusDays(8), "disburse");
+
+        final BatchRequest updateLoanRequest = BatchHelper.modifyLoanByExternalIdRequest(updateLoanRequestId, approveLoanRequestId);
+
+        final BatchRequest getLoanRequest = BatchHelper.getLoanByExternalIdRequest(getLoanRequestId, approveLoanRequestId,
+                "associations=all");
+
+        // Create batch requests list
+        final List<BatchRequest> batchRequests = Arrays.asList(applyLoanRequest, approveLoanRequest, disburseLoanRequest, updateLoanRequest,
+                getLoanRequest);
+
+        final String jsonifiedRequest = BatchHelper.toJsonString(batchRequests);
+
+        final List<BatchResponse> responses = BatchHelper.postBatchRequestsWithoutEnclosingTransaction(this.requestSpec, this.responseSpec,
+                jsonifiedRequest);
+
+        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 update loan application");
+        Assertions.assertEquals(HttpStatus.SC_OK, responses.get(4).getStatusCode(), "Verify Status Code 200 for Get Loan");
+
+        // Get the changes parameter from updateLoan Response
+        final JsonObject changes = new FromJsonHelper().parse(responses.get(3).getBody()).getAsJsonObject().get("changes")
+                .getAsJsonObject();
+        Assertions.assertEquals("true", changes.get("fraud").getAsString());
+    }
+
     /**
      * Delete datatable
      *
diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/ExternalIdSupportIntegrationTest.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/ExternalIdSupportIntegrationTest.java
index 25ba263d8..203f99e38 100644
--- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/ExternalIdSupportIntegrationTest.java
+++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/ExternalIdSupportIntegrationTest.java
@@ -28,6 +28,7 @@ import io.restassured.builder.ResponseSpecBuilder;
 import io.restassured.http.ContentType;
 import io.restassured.specification.RequestSpecification;
 import io.restassured.specification.ResponseSpecification;
+import java.math.BigDecimal;
 import java.time.LocalDate;
 import java.time.format.DateTimeFormatter;
 import java.time.format.DateTimeFormatterBuilder;
@@ -952,17 +953,20 @@ public class ExternalIdSupportIntegrationTest extends IntegrationTest {
 
             String loanExternalIdStr5 = UUID.randomUUID().toString();
             applyForLoanApplication(client.getClientId().intValue(), loanProductID, loanExternalIdStr5);
-            this.loanTransactionHelper.approveLoan(loanExternalIdStr5, new PostLoansLoanIdRequest().approvedOnDate("2 September 2022")
-                    .approvedLoanAmount("1000").expectedDisbursementDate("2 September 2022").locale("en").dateFormat("dd MMMM yyyy"));
-            result = this.loanTransactionHelper.disburseLoan(loanExternalIdStr5, new PostLoansLoanIdRequest()
-                    .actualDisbursementDate("2 September 2022").transactionAmount("1000").locale("en").dateFormat("dd MMMM yyyy"));
+            this.loanTransactionHelper.approveLoan(loanExternalIdStr5,
+                    new PostLoansLoanIdRequest().approvedOnDate("2 September 2022").approvedLoanAmount(new BigDecimal("1000"))
+                            .expectedDisbursementDate("2 September 2022").locale("en").dateFormat("dd MMMM yyyy"));
+            result = this.loanTransactionHelper.disburseLoan(loanExternalIdStr5,
+                    new PostLoansLoanIdRequest().actualDisbursementDate("2 September 2022").transactionAmount(new BigDecimal("1000"))
+                            .locale("en").dateFormat("dd MMMM yyyy"));
             // It's commented out for now, till it got fixed to return the loan externalId as well
             // assertEquals(loanExternalIdStr5, result.getResourceExternalId());
 
             String loanExternalIdStr6 = UUID.randomUUID().toString();
             applyForLoanApplication(client.getClientId().intValue(), loanProductID, loanExternalIdStr6);
-            this.loanTransactionHelper.approveLoan(loanExternalIdStr6, new PostLoansLoanIdRequest().approvedOnDate("2 September 2022")
-                    .approvedLoanAmount("1000").expectedDisbursementDate("2 September 2022").locale("en").dateFormat("dd MMMM yyyy"));
+            this.loanTransactionHelper.approveLoan(loanExternalIdStr6,
+                    new PostLoansLoanIdRequest().approvedOnDate("2 September 2022").approvedLoanAmount(new BigDecimal("1000"))
+                            .expectedDisbursementDate("2 September 2022").locale("en").dateFormat("dd MMMM yyyy"));
             result = this.loanTransactionHelper.undoApprovalLoan(loanExternalIdStr6, new PostLoansLoanIdRequest());
             assertEquals(loanExternalIdStr6, result.getResourceExternalId());
 
@@ -971,29 +975,36 @@ public class ExternalIdSupportIntegrationTest extends IntegrationTest {
 
             String loanExternalIdStr7 = UUID.randomUUID().toString();
             applyForLoanApplication(client.getClientId().intValue(), loanProductID, loanExternalIdStr7, savingsId.toString());
-            this.loanTransactionHelper.approveLoan(loanExternalIdStr7, new PostLoansLoanIdRequest().approvedOnDate("2 September 2022")
-                    .approvedLoanAmount("1000").expectedDisbursementDate("2 September 2022").locale("en").dateFormat("dd MMMM yyyy"));
-            result = this.loanTransactionHelper.disburseToSavingsLoan(loanExternalIdStr7, new PostLoansLoanIdRequest()
-                    .actualDisbursementDate("2 September 2022").transactionAmount("1000").locale("en").dateFormat("dd MMMM yyyy"));
+            this.loanTransactionHelper.approveLoan(loanExternalIdStr7,
+                    new PostLoansLoanIdRequest().approvedOnDate("2 September 2022").approvedLoanAmount(new BigDecimal("1000"))
+                            .expectedDisbursementDate("2 September 2022").locale("en").dateFormat("dd MMMM yyyy"));
+            result = this.loanTransactionHelper.disburseToSavingsLoan(loanExternalIdStr7,
+                    new PostLoansLoanIdRequest().actualDisbursementDate("2 September 2022").transactionAmount(new BigDecimal("1000"))
+                            .locale("en").dateFormat("dd MMMM yyyy"));
             assertEquals(loanExternalIdStr7, result.getResourceExternalId());
 
             String loanExternalIdStr8 = UUID.randomUUID().toString();
             applyForLoanApplication(client.getClientId().intValue(), loanProductID, loanExternalIdStr8);
-            this.loanTransactionHelper.approveLoan(loanExternalIdStr8, new PostLoansLoanIdRequest().approvedOnDate("2 September 2022")
-                    .approvedLoanAmount("1000").expectedDisbursementDate("2 September 2022").locale("en").dateFormat("dd MMMM yyyy"));
-            this.loanTransactionHelper.disburseLoan(loanExternalIdStr8, new PostLoansLoanIdRequest()
-                    .actualDisbursementDate("2 September 2022").transactionAmount("1000").locale("en").dateFormat("dd MMMM yyyy"));
+            this.loanTransactionHelper.approveLoan(loanExternalIdStr8,
+                    new PostLoansLoanIdRequest().approvedOnDate("2 September 2022").approvedLoanAmount(new BigDecimal("1000"))
+                            .expectedDisbursementDate("2 September 2022").locale("en").dateFormat("dd MMMM yyyy"));
+            this.loanTransactionHelper.disburseLoan(loanExternalIdStr8,
+                    new PostLoansLoanIdRequest().actualDisbursementDate("2 September 2022").transactionAmount(new BigDecimal("1000"))
+                            .locale("en").dateFormat("dd MMMM yyyy"));
             result = this.loanTransactionHelper.undoDisbursalLoan(loanExternalIdStr8, new PostLoansLoanIdRequest());
             assertEquals(loanExternalIdStr8, result.getResourceExternalId());
 
             String loanExternalIdStr9 = UUID.randomUUID().toString();
             applyForLoanApplication(client.getClientId().intValue(), loanProductID, loanExternalIdStr9);
-            this.loanTransactionHelper.approveLoan(loanExternalIdStr9, new PostLoansLoanIdRequest().approvedOnDate("2 September 2022")
-                    .approvedLoanAmount("1000").expectedDisbursementDate("2 September 2022").locale("en").dateFormat("dd MMMM yyyy"));
-            this.loanTransactionHelper.disburseLoan(loanExternalIdStr9, new PostLoansLoanIdRequest()
-                    .actualDisbursementDate("2 September 2022").transactionAmount("1000").locale("en").dateFormat("dd MMMM yyyy"));
-            this.loanTransactionHelper.disburseLoan(loanExternalIdStr9, new PostLoansLoanIdRequest()
-                    .actualDisbursementDate("3 September 2022").transactionAmount("1000").locale("en").dateFormat("dd MMMM yyyy"));
+            this.loanTransactionHelper.approveLoan(loanExternalIdStr9,
+                    new PostLoansLoanIdRequest().approvedOnDate("2 September 2022").approvedLoanAmount(new BigDecimal("1000"))
+                            .expectedDisbursementDate("2 September 2022").locale("en").dateFormat("dd MMMM yyyy"));
+            this.loanTransactionHelper.disburseLoan(loanExternalIdStr9,
+                    new PostLoansLoanIdRequest().actualDisbursementDate("2 September 2022").transactionAmount(new BigDecimal("1000"))
+                            .locale("en").dateFormat("dd MMMM yyyy"));
+            this.loanTransactionHelper.disburseLoan(loanExternalIdStr9,
+                    new PostLoansLoanIdRequest().actualDisbursementDate("3 September 2022").transactionAmount(new BigDecimal("1000"))
+                            .locale("en").dateFormat("dd MMMM yyyy"));
             result = this.loanTransactionHelper.undoLastDisbursalLoan(loanExternalIdStr9, new PostLoansLoanIdRequest());
             assertEquals(loanExternalIdStr9, result.getResourceExternalId());
 
@@ -1020,7 +1031,7 @@ public class ExternalIdSupportIntegrationTest extends IntegrationTest {
             String loanExternalIdStr13 = UUID.randomUUID().toString();
             applyForLoanApplication(client.getClientId().intValue(), loanProductID, loanExternalIdStr13);
             result = this.loanTransactionHelper.approveLoan(loanExternalIdStr13,
-                    new PostLoansLoanIdRequest().approvedOnDate("2 September 2022").approvedLoanAmount("1000")
+                    new PostLoansLoanIdRequest().approvedOnDate("2 September 2022").approvedLoanAmount(new BigDecimal("1000"))
                             .expectedDisbursementDate("2 September 2022").locale("en").dateFormat("dd MMMM yyyy"));
             assertEquals(loanExternalIdStr13, result.getResourceExternalId());
 
@@ -1032,11 +1043,12 @@ public class ExternalIdSupportIntegrationTest extends IntegrationTest {
             applyForLoanApplication(client.getClientId().intValue(), loanProductID, loanExternalIdStr14);
             String transactionExternalId = UUID.randomUUID().toString();
             result = this.loanTransactionHelper.approveLoan(loanExternalIdStr14,
-                    new PostLoansLoanIdRequest().approvedOnDate("2 September 2022").approvedLoanAmount("1000")
+                    new PostLoansLoanIdRequest().approvedOnDate("2 September 2022").approvedLoanAmount(new BigDecimal("1000"))
                             .expectedDisbursementDate("2 September 2022").locale("en").dateFormat("dd MMMM yyyy"));
             assertEquals(loanExternalIdStr14, result.getResourceExternalId());
-            this.loanTransactionHelper.disburseLoan(loanExternalIdStr14, new PostLoansLoanIdRequest()
-                    .actualDisbursementDate("2 September 2022").transactionAmount("1000").locale("en").dateFormat("dd MMMM yyyy"));
+            this.loanTransactionHelper.disburseLoan(loanExternalIdStr14,
+                    new PostLoansLoanIdRequest().actualDisbursementDate("2 September 2022").transactionAmount(new BigDecimal("1000"))
+                            .locale("en").dateFormat("dd MMMM yyyy"));
             PostLoansLoanIdTransactionsResponse closeResult = this.loanTransactionHelper.closeLoan(loanExternalIdStr14,
                     new PostLoansLoanIdTransactionsRequest().transactionDate("3 September 2022").locale("en").dateFormat("dd MMMM yyyy")
                             .externalId(transactionExternalId));
@@ -1046,10 +1058,11 @@ public class ExternalIdSupportIntegrationTest extends IntegrationTest {
             String transactionExternalId2 = UUID.randomUUID().toString();
             applyForLoanApplication(client.getClientId().intValue(), loanProductID, loanExternalIdStr15);
             result = this.loanTransactionHelper.approveLoan(loanExternalIdStr15,
-                    new PostLoansLoanIdRequest().approvedOnDate("2 September 2022").approvedLoanAmount("1000")
+                    new PostLoansLoanIdRequest().approvedOnDate("2 September 2022").approvedLoanAmount(new BigDecimal("1000"))
                             .expectedDisbursementDate("2 September 2022").locale("en").dateFormat("dd MMMM yyyy"));
-            this.loanTransactionHelper.disburseLoan(loanExternalIdStr15, new PostLoansLoanIdRequest()
-                    .actualDisbursementDate("2 September 2022").transactionAmount("1000").locale("en").dateFormat("dd MMMM yyyy"));
+            this.loanTransactionHelper.disburseLoan(loanExternalIdStr15,
+                    new PostLoansLoanIdRequest().actualDisbursementDate("2 September 2022").transactionAmount(new BigDecimal("1000"))
+                            .locale("en").dateFormat("dd MMMM yyyy"));
             assertEquals(loanExternalIdStr15, result.getResourceExternalId());
             PostLoansLoanIdTransactionsResponse forecloseResult = this.loanTransactionHelper.forecloseLoan(loanExternalIdStr15,
                     new PostLoansLoanIdTransactionsRequest().transactionDate("2 September 2022").locale("en").dateFormat("dd MMMM yyyy")
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 3f8fca807..e2b38aa6f 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
@@ -30,6 +30,7 @@ import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.UUID;
 import java.util.stream.Collectors;
 import javax.ws.rs.HttpMethod;
 import org.apache.fineract.batch.command.internal.CreateTransactionLoanCommandStrategy;
@@ -302,6 +303,25 @@ public final class BatchHelper {
      * @return {@link BatchRequest}
      */
     public static BatchRequest applyLoanRequestWithClientId(final Long requestId, final Integer clientId, final Integer productId) {
+        return applyLoanRequestWithClientIdAndExternalId(requestId, clientId, productId, UUID.randomUUID().toString());
+    }
+
+    /**
+     * Creates and returns a {@link org.apache.fineract.batch.command.internal.ApplyLoanCommandStrategy} request with
+     * given clientId, external id and product id.
+     *
+     * @param requestId
+     *            the request id
+     * @param clientId
+     *            the client id
+     * @param productId
+     *            the product id
+     * @param externalId
+     *            the external id
+     * @return {@link BatchRequest}
+     */
+    public static BatchRequest applyLoanRequestWithClientIdAndExternalId(final Long requestId, final Integer clientId,
+            final Integer productId, final String externalId) {
 
         final BatchRequest br = new BatchRequest();
 
@@ -315,7 +335,7 @@ public final class BatchHelper {
                 + "\"repaymentEvery\": 1, \"repaymentFrequencyType\": 2, \"interestRatePerPeriod\": 10,"
                 + "\"amortizationType\": 1, \"interestType\": 0, \"interestCalculationPeriodType\": 1,"
                 + "\"transactionProcessingStrategyCode\": \"mifos-standard-strategy\", \"expectedDisbursementDate\": \"10 Jun 2013\","
-                + "\"submittedOnDate\": \"10 Jun 2013\"}", clientId, productId);
+                + "\"submittedOnDate\": \"10 Jun 2013\", \"externalId\": \"%s\"}", clientId, productId, externalId);
 
         br.setBody(body);
 
@@ -349,21 +369,56 @@ public final class BatchHelper {
 
     /**
      * Creates and returns a {@link org.apache.fineract.batch.command.internal.CreateChargeCommandStrategy} Request with
-     * given requestId and reference
+     * given requestId and reference based on loan id
+     *
+     * @param requestId
+     *            the batch request id.
+     * @param reference
+     *            the reference id.
+     * @param chargeId
+     *            the charge id used for getting charge type.
+     * @return BatchRequest
+     */
+    public static BatchRequest createChargeByLoanIdRequest(final Long requestId, final Long reference, final Integer chargeId) {
+        return createChargeRequest(requestId, reference, "loans/$.loanId/charges", chargeId);
+    }
+
+    /**
+     * Creates and returns a
+     * {@link org.apache.fineract.batch.command.internal.CreateChargeByLoanExternalIdCommandStrategy} Request with given
+     * requestId and reference based on loan external id
+     *
+     * @param requestId
+     *            the batch request id.
+     * @param reference
+     *            the reference id.
+     * @param chargeId
+     *            the charge id used for getting charge type.
+     * @return BatchRequest
+     */
+    public static BatchRequest createChargeByLoanExternalIdRequest(final Long requestId, final Long reference, final Integer chargeId) {
+        return createChargeRequest(requestId, reference, "loans/external-id/$.externalId/charges", chargeId);
+    }
+
+    /**
+     * Creates and returns a Batch Request with given requestId and reference
      *
      * @param requestId
      *            the batch request id.
      * @param reference
      *            the reference id.
+     * @param relativeUrl
+     *            the relative url reference.
      * @param chargeId
      *            the charge id used for getting charge type.
      * @return BatchRequest
      */
-    public static BatchRequest createChargeRequest(final Long requestId, final Long reference, final Integer chargeId) {
+    private static BatchRequest createChargeRequest(final Long requestId, final Long reference, final String relativeUrl,
+            final Integer chargeId) {
 
         final BatchRequest br = new BatchRequest();
         br.setRequestId(requestId);
-        br.setRelativeUrl("loans/$.loanId/charges");
+        br.setRelativeUrl(relativeUrl);
         br.setMethod("POST");
         br.setReference(reference);
 
@@ -378,20 +433,103 @@ public final class BatchHelper {
         return br;
     }
 
+    /**
+     * Creates and returns a {@link org.apache.fineract.batch.command.internal.AdjustChargeCommandStrategy} Request with
+     * given requestId and reference
+     *
+     * @param requestId
+     *            the batch request id.
+     * @param reference
+     *            the reference id.
+     * @return BatchRequest
+     */
+    public static BatchRequest adjustChargeRequest(final Long requestId, final Long reference) {
+
+        final BatchRequest br = new BatchRequest();
+        br.setRequestId(requestId);
+        br.setRelativeUrl("loans/$.loanId/charges/$.resourceId?command=adjustment");
+        br.setMethod("POST");
+        br.setReference(reference);
+        br.setBody("{\"amount\":7.00,\"locale\":\"en\"}");
+
+        return br;
+    }
+
+    /**
+     * Creates and returns a
+     * {@link org.apache.fineract.batch.command.internal.AdjustChargeByChargeExternalIdCommandStrategy} Request with
+     * given requestId and reference
+     *
+     * @param requestId
+     *            the batch request id.
+     * @param reference
+     *            the reference id.
+     * @param loanExternalId
+     *            the loan external id.
+     * @param chargeExternalId
+     *            the charge external id
+     * @return BatchRequest
+     */
+    public static BatchRequest adjustChargeByExternalIdRequest(final Long requestId, final Long reference, final String loanExternalId,
+            final String chargeExternalId) {
+
+        final BatchRequest br = new BatchRequest();
+        br.setRequestId(requestId);
+        br.setRelativeUrl(
+                String.format("loans/external-id/%s/charges/external-id/%s?command=adjustment", loanExternalId, chargeExternalId));
+        br.setMethod("POST");
+        br.setReference(reference);
+        br.setBody("{\"amount\":7.00,\"locale\":\"en\"}");
+
+        return br;
+    }
+
     /**
      * Creates and returns a {@link org.apache.fineract.batch.command.internal.CollectChargesCommandStrategy} Request
      * with given requestId and reference.
      *
      * @param requestId
+     *            the request id
+     * @param reference
+     *            the reference id
+     * @return BatchRequest
+     */
+    public static BatchRequest collectChargesByLoanIdRequest(final Long requestId, final Long reference) {
+        return collectChargesRequest(requestId, reference, "loans/$.loanId/charges");
+    }
+
+    /**
+     * Creates and returns a
+     * {@link org.apache.fineract.batch.command.internal.CollectChargesByLoanExternalIdCommandStrategy} Request with
+     * given requestId and reference.
+     *
+     * @param requestId
+     *            the request id
      * @param reference
+     *            the reference id
      * @return BatchRequest
      */
-    public static BatchRequest collectChargesRequest(final Long requestId, final Long reference) {
+    public static BatchRequest collectChargesByLoanExternalIdRequest(final Long requestId, final Long reference) {
+        return collectChargesRequest(requestId, reference, "loans/external-id/$.externalId/charges");
+    }
+
+    /**
+     * Creates and returns a Batch Request with given requestId and reference.
+     *
+     * @param requestId
+     *            the request id
+     * @param reference
+     *            the reference id
+     * @param relativeUrl
+     *            the relative url
+     * @return BatchRequest
+     */
+    private static BatchRequest collectChargesRequest(final Long requestId, final Long reference, final String relativeUrl) {
 
         final BatchRequest br = new BatchRequest();
 
         br.setRequestId(requestId);
-        br.setRelativeUrl("loans/$.loanId/charges");
+        br.setRelativeUrl(relativeUrl);
         br.setReference(reference);
         br.setMethod("GET");
         br.setBody("{ }");
@@ -400,20 +538,19 @@ public final class BatchHelper {
     }
 
     /**
-     * Creates and returns a {@link org.apache.fineract.batch.command.internal.GetChargeByIdCommandStrategy} request
-     * with given requestId and reference.
+     * Creates and returns a Batch request with given requestId and reference.
      *
      * @param requestId
      *            the request id
      * @param reference
      *            the reference
+     * @param relativeUrl
+     *            the relative url
      * @return the {@link BatchRequest}
      */
-    public static BatchRequest getChargeByIdCommandStrategy(final Long requestId, final Long reference) {
+    private static BatchRequest getChargeById(final Long requestId, final Long reference, final String relativeUrl) {
 
         final BatchRequest br = new BatchRequest();
-        String relativeUrl = "loans/$.loanId/charges/$.resourceId";
-
         br.setRequestId(requestId);
         br.setRelativeUrl(relativeUrl);
         br.setMethod(HttpMethod.GET);
@@ -423,6 +560,37 @@ public final class BatchHelper {
         return br;
     }
 
+    /**
+     * Creates and returns a {@link org.apache.fineract.batch.command.internal.GetChargeByIdCommandStrategy} request
+     * with given requestId and reference.
+     *
+     * @param requestId
+     *            the request id
+     * @param reference
+     *            the reference
+     * @return the {@link BatchRequest}
+     */
+    public static BatchRequest getChargeByLoanIdChargeId(final Long requestId, final Long reference) {
+        return getChargeById(requestId, reference, "loans/$.loanId/charges/$.resourceId");
+    }
+
+    /**
+     * Creates and returns a
+     * {@link org.apache.fineract.batch.command.internal.GetChargeByChargeExternalIdCommandStrategy} request with given
+     * requestId and reference.
+     *
+     * @param requestId
+     *            the request id
+     * @param reference
+     *            the reference
+     * @return the {@link BatchRequest}
+     */
+    public static BatchRequest getChargeByLoanExternalIdChargeExternalId(final Long requestId, final Long reference,
+            final String loanExternalId, final String chargeExternalId) {
+        return getChargeById(requestId, reference,
+                String.format("loans/external-id/%s/charges/external-id/%s", loanExternalId, chargeExternalId));
+    }
+
     /**
      * Creates and returns a {@link org.apache.fineract.batch.command.internal.ActivateClientCommandStrategy} Request
      * with given requestId and reference.
@@ -528,6 +696,41 @@ public final class BatchHelper {
         return br;
     }
 
+    /**
+     * Creates and returns a
+     * {@link org.apache.fineract.batch.command.internal.LoanStateTransistionsByExternalIdCommandStrategy} Request with
+     * given requestId and reference.
+     *
+     *
+     * @param requestId
+     *            the request ID
+     * @param reference
+     *            the reference ID
+     * @param date
+     *            the actual disbursement date
+     * @param command
+     *            the action to transistion
+     * @return BatchRequest the batch request
+     */
+    public static BatchRequest transistionLoanStateByExternalId(final Long requestId, final Long reference, final LocalDate date,
+            final String command) {
+        final BatchRequest br = new BatchRequest();
+
+        br.setRequestId(requestId);
+        br.setRelativeUrl("loans/external-id/$.resourceExternalId?command=" + command);
+        br.setReference(reference);
+        br.setMethod("POST");
+        String dateString = date.format(DateTimeFormatter.ofPattern("dd MMMM yyyy"));
+        if ("disburse".equals(command)) {
+            br.setBody("{\"locale\": \"en\", \"dateFormat\": \"dd MMMM yyyy\", \"actualDisbursementDate\": \"" + dateString + "\"}");
+        } else if ("approve".equals(command)) {
+            br.setBody("{\"locale\": \"en\", \"dateFormat\": \"dd MMMM yyyy\", \"approvedOnDate\": \"" + dateString + "\","
+                    + "\"note\": \"Loan approval note\"}");
+        }
+
+        return br;
+    }
+
     /**
      * Creates and returns a {@link CreateTransactionLoanCommandStrategy} Request with given requestId.
      *
@@ -572,6 +775,37 @@ public final class BatchHelper {
         return br;
     }
 
+    /**
+     * Creates and returns a {@link CreateTransactionLoanCommandStrategy} Request with given requestId.
+     *
+     * @param requestId
+     *            the request ID
+     * @param reference
+     *            the reference ID
+     * @param transactionCommand
+     *            the transaction command to process
+     * @param amount
+     *            the amount
+     * @param date
+     *            the transaction date
+     * @return BatchRequest the batch request
+     */
+    public static BatchRequest createTransactionRequestByLoanExternalId(final Long requestId, final Long reference,
+            final String transactionCommand, final String amount, final LocalDate date) {
+        final BatchRequest br = new BatchRequest();
+
+        br.setRequestId(requestId);
+        br.setReference(reference);
+        br.setRelativeUrl(String.format("loans/external-id/$.externalId/transactions?command=%s", transactionCommand));
+        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;
+    }
+
     /**
      * Creates and returns a {@link CreateTransactionLoanCommandStrategy} request with given request ID.
      *
@@ -725,12 +959,50 @@ public final class BatchHelper {
      *            the request id
      * @param reference
      *            the reference
+     * @param subResourceId
+     *            whether the subResourceId is used
+     * @return the {@link BatchRequest}
+     */
+    public static BatchRequest getTransactionByIdRequest(final Long requestId, final Long reference, final Boolean subResourceId) {
+
+        final BatchRequest br = new BatchRequest();
+        String relativeUrl;
+        if (subResourceId) {
+            relativeUrl = "loans/$.loanId/transactions/$.subResourceId";
+        } else {
+            relativeUrl = "loans/$.loanId/transactions/$.resourceId";
+        }
+
+        br.setRequestId(requestId);
+        br.setRelativeUrl(relativeUrl);
+        br.setMethod(HttpMethod.GET);
+        br.setReference(reference);
+        br.setBody("{}");
+
+        return br;
+    }
+
+    /**
+     * Creates and returns a
+     * {@link org.apache.fineract.batch.command.internal.GetTransactionByExternalIdCommandStrategy} request with given
+     * requestId and reference.
+     *
+     * @param requestId
+     *            the request id
+     * @param reference
+     *            the reference
      * @return the {@link BatchRequest}
      */
-    public static BatchRequest getTransactionByIdRequest(final Long requestId, final Long reference) {
+    public static BatchRequest getTransactionByExternalIdRequest(final Long requestId, final Long reference, final String loanExternalId,
+            final Boolean subResourceExternalId) {
 
         final BatchRequest br = new BatchRequest();
-        String relativeUrl = "loans/$.loanId/transactions/$.resourceId";
+        String relativeUrl;
+        if (subResourceExternalId) {
+            relativeUrl = String.format("loans/external-id/%s/transactions/external-id/$.subResourceExternalId", loanExternalId);
+        } else {
+            relativeUrl = String.format("loans/external-id/%s/transactions/external-id/$.resourceExternalId", loanExternalId);
+        }
 
         br.setRequestId(requestId);
         br.setRelativeUrl(relativeUrl);
@@ -770,6 +1042,35 @@ public final class BatchHelper {
         return br;
     }
 
+    /**
+     * Creates and returns a {@link org.apache.fineract.batch.command.internal.GetLoanByExternalIdCommandStrategy}
+     * request with given requestId and reference.
+     *
+     * @param requestId
+     *            the request id
+     * @param reference
+     *            the reference
+     * @param queryParameter
+     *            the query parameters
+     * @return the {@link BatchRequest}
+     */
+    public static BatchRequest getLoanByExternalIdRequest(final Long requestId, final Long reference, final String queryParameter) {
+
+        final BatchRequest br = new BatchRequest();
+        String relativeUrl = "loans/external-id/$.resourceExternalId";
+        if (queryParameter != null) {
+            relativeUrl = relativeUrl + "?" + queryParameter;
+        }
+
+        br.setRequestId(requestId);
+        br.setRelativeUrl(relativeUrl);
+        br.setMethod(HttpMethod.GET);
+        br.setReference(reference);
+        br.setBody("{}");
+
+        return br;
+    }
+
     /**
      * Creates and returns a {@link org.apache.fineract.batch.command.internal.GetLoanByIdCommandStrategy} request with
      * given loan id and query param.
@@ -781,13 +1082,33 @@ public final class BatchHelper {
      * @return the {@link BatchRequest}
      */
     public static BatchRequest getLoanByIdRequest(final Long loanId, final String queryParameter) {
+        return getLoanByIdRequest(loanId, 4567L, null, queryParameter);
+    }
+
+    /**
+     * 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 requestId
+     *            the request id
+     * @param referenceId
+     *            the reference id
+     * @param queryParameter
+     *            the query parameters
+     * @return the {@link BatchRequest}
+     */
+    public static BatchRequest getLoanByIdRequest(final Long loanId, final Long requestId, final Long referenceId,
+            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.setRequestId(requestId);
+        br.setReference(referenceId);
         br.setRelativeUrl(relativeUrl);
         br.setMethod(HttpMethod.GET);
         br.setBody("{}");
@@ -912,6 +1233,39 @@ public final class BatchHelper {
         return br;
     }
 
+    /**
+     * Creates and returns a batch request to create an adjust transaction request using external id.
+     *
+     * @param requestId
+     *            the request ID
+     * @param reference
+     *            the reference
+     * @param loanExternalId
+     *            the loan external id
+     * @param transactionExternalId
+     *            the transaction external id
+     * @param amount
+     *            the amount
+     * @param date
+     *            the date
+     * @return the {@link BatchRequest}
+     */
+    public static BatchRequest createAdjustTransactionByExternalIdRequest(final Long requestId, final Long reference,
+            final String loanExternalId, final String transactionExternalId, final String amount, final LocalDate date) {
+        final BatchRequest br = new BatchRequest();
+
+        br.setRequestId(requestId);
+        br.setReference(reference);
+        br.setRelativeUrl(String.format("loans/external-id/%s/transactions/external-id/%s", loanExternalId, transactionExternalId));
+        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;
+    }
+
     /**
      * Creates and returns a batch request to create a chargeback transaction request.
      *
@@ -937,6 +1291,50 @@ public final class BatchHelper {
 
     }
 
+    /**
+     * Creates and returns a batch request to update a Loan account as fraud.
+     *
+     * @param requestId
+     *            the request ID
+     * @param reference
+     *            the reference
+     * @return the {@link BatchRequest}
+     */
+    public static BatchRequest createLoanRequestMarkAsFraud(final Long requestId, final Long reference) {
+
+        final BatchRequest br = new BatchRequest();
+
+        br.setRequestId(requestId);
+        br.setRelativeUrl("loans/$.loanId?command=markAsFraud");
+        br.setMethod("PUT");
+        br.setReference(reference);
+        br.setBody("{\"fraud\": \"true\"}");
+
+        return br;
+    }
+
+    /**
+     * Creates and returns a batch request to update a Loan account as fraud by loan external id.
+     *
+     * @param requestId
+     *            the request ID
+     * @param reference
+     *            the reference
+     * @return the {@link BatchRequest}
+     */
+    public static BatchRequest modifyLoanByExternalIdRequest(final Long requestId, final Long reference) {
+
+        final BatchRequest br = new BatchRequest();
+
+        br.setRequestId(requestId);
+        br.setRelativeUrl("loans/external-id/$.resourceExternalId?command=markAsFraud");
+        br.setMethod("PUT");
+        br.setReference(reference);
+        br.setBody("{\"fraud\": \"true\"}");
+
+        return br;
+    }
+
     /**
      * Creates and returns a batch request to query datatable entry.
      *
diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/GlobalConfigurationHelper.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/GlobalConfigurationHelper.java
index 041e0b1f1..d1100d57a 100644
--- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/GlobalConfigurationHelper.java
+++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/GlobalConfigurationHelper.java
@@ -629,4 +629,10 @@ public class GlobalConfigurationHelper {
         assertEquals(updatedConfiguration.getEnabled(), enabled);
     }
 
+    public static Integer updateIsAutomaticExternalIdGenerationEnabled(final RequestSpecification requestSpec,
+            final ResponseSpecification responseSpec, final boolean enabled) {
+        long configId = 50;
+        return updateEnabledFlagForGlobalConfiguration(requestSpec, responseSpec, configId, enabled);
+    }
+
 }