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

aleks 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 37205bef3 FINERACT-1724: Enhancements on batch API support for 
adjusting a loan transaction
37205bef3 is described below

commit 37205bef39e45aa3880c22df2faebb9c62806c6f
Author: Arnold Galovics <[email protected]>
AuthorDate: Mon Oct 10 10:29:32 2022 +0200

    FINERACT-1724: Enhancements on batch API support for adjusting a loan 
transaction
---
 build.gradle                                       |   4 +-
 .../fineract/client/util/FineractClient.java       |   6 +
 .../batch/command/CommandStrategyProvider.java     |   2 +
 .../internal/AdjustTransactionCommandStrategy.java |  76 +++++++
 .../businessdate/api/BusinessDateApiResource.java  |   2 +-
 .../batch/command/CommandStrategyProviderTest.java |   3 +
 .../AdjustTransactionCommandStrategyTest.java      | 107 +++++++++
 .../fineract/integrationtests/BatchApiTest.java    | 245 ++++++++++++++++++++-
 .../integrationtests/common/BatchHelper.java       |  17 ++
 9 files changed, 459 insertions(+), 3 deletions(-)

diff --git a/build.gradle b/build.gradle
index 831dc3761..87272d239 100644
--- a/build.gradle
+++ b/build.gradle
@@ -155,6 +155,7 @@ allprojects  {
             "**/banner.txt",
             "**/build.gradle.mustache",
             "**/pom.mustache",
+            "**/avro/**/*.java",
         ])
         strictCheck true
     }
@@ -249,7 +250,8 @@ allprojects  {
             "**/git.properties",
             ".mailmap",
             '**/images/diag-*.svg',
-            '**/*.avsc'
+            '**/*.avsc',
+            "**/generated/**/*MapperImpl.java"
         ]
     }
 }
diff --git 
a/fineract-client/src/main/java/org/apache/fineract/client/util/FineractClient.java
 
b/fineract-client/src/main/java/org/apache/fineract/client/util/FineractClient.java
index 06062d49c..7b2f30eaa 100644
--- 
a/fineract-client/src/main/java/org/apache/fineract/client/util/FineractClient.java
+++ 
b/fineract-client/src/main/java/org/apache/fineract/client/util/FineractClient.java
@@ -42,6 +42,7 @@ import org.apache.fineract.client.services.AdhocQueryApiApi;
 import org.apache.fineract.client.services.AuditsApi;
 import org.apache.fineract.client.services.AuthenticationHttpBasicApi;
 import org.apache.fineract.client.services.BatchApiApi;
+import org.apache.fineract.client.services.BusinessDateManagementApi;
 import org.apache.fineract.client.services.CacheApi;
 import org.apache.fineract.client.services.CashierJournalsApi;
 import org.apache.fineract.client.services.CashiersApi;
@@ -57,6 +58,7 @@ import org.apache.fineract.client.services.CodesApi;
 import org.apache.fineract.client.services.CurrencyApi;
 import org.apache.fineract.client.services.DataTablesApi;
 import org.apache.fineract.client.services.DefaultApi;
+import 
org.apache.fineract.client.services.DelinquencyRangeAndBucketsManagementApi;
 import org.apache.fineract.client.services.DocumentsApiFixed;
 import org.apache.fineract.client.services.EntityDataTableApi;
 import org.apache.fineract.client.services.EntityFieldConfigurationApi;
@@ -174,6 +176,7 @@ public final class FineractClient {
     public final AuditsApi audits;
     public final AuthenticationHttpBasicApi authentication;
     public final BatchApiApi batches;
+    public final BusinessDateManagementApi businessDateManagement;
     public final CacheApi caches;
     public final CashierJournalsApi cashiersJournal;
     public final CashiersApi cashiers;
@@ -190,6 +193,7 @@ public final class FineractClient {
     public final DataTablesApi dataTables;
     public final @Deprecated DefaultApi legacy; // TODO FINERACT-1222
     public final DocumentsApiFixed documents;
+    public final DelinquencyRangeAndBucketsManagementApi 
delinquencyRangeAndBucketsManagement;
     public final EntityDataTableApi entityDatatableChecks;
     public final EntityFieldConfigurationApi entityFieldConfigurations;
     public final ExternalServicesApi externalServices;
@@ -286,6 +290,7 @@ public final class FineractClient {
         audits = retrofit.create(AuditsApi.class);
         authentication = retrofit.create(AuthenticationHttpBasicApi.class);
         batches = retrofit.create(BatchApiApi.class);
+        businessDateManagement = 
retrofit.create(BusinessDateManagementApi.class);
         caches = retrofit.create(CacheApi.class);
         cashiersJournal = retrofit.create(CashierJournalsApi.class);
         cashiers = retrofit.create(CashiersApi.class);
@@ -300,6 +305,7 @@ public final class FineractClient {
         codeValues = retrofit.create(CodeValuesApi.class);
         currencies = retrofit.create(CurrencyApi.class);
         dataTables = retrofit.create(DataTablesApi.class);
+        delinquencyRangeAndBucketsManagement = 
retrofit.create(DelinquencyRangeAndBucketsManagementApi.class);
         legacy = retrofit.create(DefaultApi.class);
         documents = retrofit.create(DocumentsApiFixed.class);
         entityDatatableChecks = retrofit.create(EntityDataTableApi.class);
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 ea96f5c50..581681ac3 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
@@ -103,6 +103,8 @@ public class CommandStrategyProvider {
                 "createTransactionLoanCommandStrategy");
         
this.commandStrategies.put(CommandContext.resource("loans\\/\\d+\\/transactions\\?command=payoutRefund").method("POST").build(),
                 "createTransactionLoanCommandStrategy");
+        
this.commandStrategies.put(CommandContext.resource("loans\\/\\d+\\/transactions\\/\\d+").method("POST").build(),
+                "adjustTransactionCommandStrategy");
         
this.commandStrategies.put(CommandContext.resource("clients\\/\\d+\\?command=activate").method("POST").build(),
                 "activateClientCommandStrategy");
         
this.commandStrategies.put(CommandContext.resource("loans\\/\\d+\\?command=approve").method("POST").build(),
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
new file mode 100644
index 000000000..d5341d191
--- /dev/null
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/AdjustTransactionCommandStrategy.java
@@ -0,0 +1,76 @@
+/**
+ * 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.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.LoanTransactionsApiResource;
+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.
+ *
+ * @see CommandStrategy
+ * @see BatchRequest
+ * @see BatchResponse
+ */
+@Component
+@RequiredArgsConstructor
+public class AdjustTransactionCommandStrategy implements CommandStrategy {
+
+    private final LoanTransactionsApiResource loanTransactionsApiResource;
+
+    @Override
+    public BatchResponse execute(final BatchRequest request, UriInfo 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 transaction ids for use in 
loanTransactionsApiResource
+        final List<String> pathParameters = 
Splitter.on('/').splitToList(relativeUrl);
+        Long loanId = Long.parseLong(pathParameters.get(1));
+        Long transactionId = Long.parseLong(pathParameters.get(3));
+        Map<String, String> queryParameters = 
CommandStrategyUtils.getQueryParameters(relativeUrl);
+        String command = queryParameters.get("command");
+
+        // Calls 'adjustLoanTransaction' function from 
'loanTransactionsApiResource'
+        responseBody = 
loanTransactionsApiResource.adjustLoanTransaction(loanId, transactionId, 
request.getBody(), command);
+
+        response.setStatusCode(HttpStatus.SC_OK);
+
+        // Sets the body of the response after retrieving the transaction
+        response.setBody(responseBody);
+
+        return response;
+    }
+}
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/businessdate/api/BusinessDateApiResource.java
 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/businessdate/api/BusinessDateApiResource.java
index 6fde1caff..94f0678a6 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/businessdate/api/BusinessDateApiResource.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/businessdate/api/BusinessDateApiResource.java
@@ -95,7 +95,7 @@ public class BusinessDateApiResource {
     @Operation(summary = "Update Business Date", description = "")
     @RequestBody(required = true, content = @Content(schema = 
@Schema(implementation = 
BusinessDateApiResourceSwagger.BusinessDateRequest.class)))
     @ApiResponses({
-            @ApiResponse(responseCode = "200", description = "OK", content = 
@Content(schema = @Schema(implementation = 
BusinessDateApiResourceSwagger.BusinessDateRequest.class))) })
+            @ApiResponse(responseCode = "200", description = "OK", content = 
@Content(schema = @Schema(implementation = 
BusinessDateApiResourceSwagger.BusinessDateResponse.class))) })
     public String updateBusinessDate(final String jsonRequestBody, @Context 
UriInfo uriInfo) {
         
securityContext.authenticatedUser().validateHasUpdatePermission("BUSINESS_DATE");
         final CommandWrapper commandRequest = new 
CommandWrapperBuilder().updateBusinessDate().withJson(jsonRequestBody).build();
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 c435ca22c..d8ae731d4 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,6 +25,7 @@ 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.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;
@@ -78,6 +79,8 @@ public class CommandStrategyProviderTest {
                         mock(CreateTransactionLoanCommandStrategy.class)),
                 Arguments.of("loans/123/transactions?command=payoutRefund", 
HttpMethod.POST, "createTransactionLoanCommandStrategy",
                         mock(CreateTransactionLoanCommandStrategy.class)),
+                Arguments.of("loans/123/transactions/123", HttpMethod.POST, 
"adjustTransactionCommandStrategy",
+                        mock(AdjustTransactionCommandStrategy.class)),
                 Arguments.of("clients/456?command=activate", HttpMethod.POST, 
"activateClientCommandStrategy",
                         mock(ActivateClientCommandStrategy.class)),
                 Arguments.of("loans/123?command=approve", HttpMethod.POST, 
"approveLoanCommandStrategy",
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
new file mode 100644
index 000000000..17fb6da2e
--- /dev/null
+++ 
b/fineract-provider/src/test/java/org/apache/fineract/batch/command/internal/AdjustTransactionCommandStrategyTest.java
@@ -0,0 +1,107 @@
+/**
+ * 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 javax.ws.rs.HttpMethod;
+import javax.ws.rs.core.UriInfo;
+import org.apache.commons.lang3.RandomStringUtils;
+import org.apache.fineract.batch.domain.BatchRequest;
+import org.apache.fineract.batch.domain.BatchResponse;
+import 
org.apache.fineract.portfolio.loanaccount.api.LoanTransactionsApiResource;
+import org.apache.http.HttpStatus;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+public class AdjustTransactionCommandStrategyTest {
+
+    /**
+     * Test {@link AdjustTransactionCommandStrategy#execute} happy path 
scenario.
+     */
+    @Test
+    public void testExecuteSuccessScenario() {
+        // 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);
+        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);
+
+        // 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);
+    }
+
+    /**
+     * Creates and returns a request with the given loan id and transaction id.
+     *
+     * @param loanId
+     *            the loan id
+     * @param transactionId
+     *            the transaction id
+     * @return BatchRequest
+     */
+    private BatchRequest getBatchRequest(final Long loanId, final Long 
transactionId) {
+
+        final BatchRequest br = new BatchRequest();
+        String relativeUrl = String.format("loans/%s/transactions/%s", loanId, 
transactionId);
+
+        br.setRequestId(Long.valueOf(RandomStringUtils.randomNumeric(5)));
+        br.setRelativeUrl(relativeUrl);
+        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}");
+
+        return br;
+    }
+
+    /**
+     * Private test context class used since testng runs in parallel to avoid 
state between tests
+     */
+    private static class TestContext {
+
+        @Mock
+        private UriInfo uriInfo;
+
+        @Mock
+        private LoanTransactionsApiResource loanTransactionsApiResource;
+
+        private final AdjustTransactionCommandStrategy subjectToTest;
+
+        TestContext() {
+            MockitoAnnotations.openMocks(this);
+            subjectToTest = new 
AdjustTransactionCommandStrategy(loanTransactionsApiResource);
+        }
+    }
+}
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 20bbf03fe..1d1ca330d 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
@@ -20,6 +20,7 @@ package org.apache.fineract.integrationtests;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
 
+import com.google.gson.JsonArray;
 import com.google.gson.JsonElement;
 import com.google.gson.JsonObject;
 import io.restassured.builder.RequestSpecBuilder;
@@ -33,6 +34,7 @@ import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.List;
+import 
org.apache.fineract.batch.command.internal.AdjustTransactionCommandStrategy;
 import 
org.apache.fineract.batch.command.internal.CreateTransactionLoanCommandStrategy;
 import org.apache.fineract.batch.domain.BatchRequest;
 import org.apache.fineract.batch.domain.BatchResponse;
@@ -147,7 +149,7 @@ public class BatchApiTest {
      * rolled back.
      *
      * @see 
org.apache.fineract.batch.command.internal.CreateClientCommandStrategy
-     * @see org.apache.fineract.batch.api.BatchesApiResource
+     * @see org.apache.fineract.batch.api.BatchApiResource
      * @see org.apache.fineract.batch.service.BatchApiService
      */
     @Test
@@ -1166,6 +1168,247 @@ public class BatchApiTest {
         Assertions.assertEquals(HttpStatus.SC_OK, 
responses.get(4).getStatusCode(), "Verify Status Code 200 for payout refund");
     }
 
+    /**
+     * Test for the successful repayment reversal transaction. A '200' status 
code is expected on successful responses.
+     *
+     * @see AdjustTransactionCommandStrategy
+     */
+    @Test
+    public void shouldReturnOkStatusForBatchRepaymentReversal() {
+
+        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 = 5730L;
+        final Long approveLoanRequestId = 5731L;
+        final Long disburseLoanRequestId = 5732L;
+        final Long repayLoanRequestId = 5733L;
+        final Long repayReversalRequestId = 5734L;
+        final Long getLoanRequestId = 5735L;
+
+        // Create client
+        final Integer clientId = ClientHelper.createClient(this.requestSpec, 
this.responseSpec);
+        ClientHelper.verifyClientCreatedOnServer(this.requestSpec, 
this.responseSpec, clientId);
+
+        // Create an apply loan request
+        final BatchRequest applyLoanRequest = 
BatchHelper.applyLoanRequestWithClientId(applyLoanRequestId, clientId, 
productId);
+
+        // Create an approve loan request
+        final BatchRequest approveLoanRequest = 
BatchHelper.approveLoanRequest(approveLoanRequestId, applyLoanRequestId);
+
+        // Create a disburse loan request
+        final BatchRequest disburseLoanRequest = 
BatchHelper.disburseLoanRequest(disburseLoanRequestId, approveLoanRequestId);
+
+        // Create a repayment request.
+        final BatchRequest repaymentRequest = 
BatchHelper.repayLoanRequest(repayLoanRequestId, disburseLoanRequestId, "500");
+
+        // Create a repayment reversal request
+        final BatchRequest repaymentReversalRequest = 
BatchHelper.createAdjustTransactionRequest(repayReversalRequestId, 
repayLoanRequestId,
+                "0", date);
+
+        // Get loan transactions request
+        final BatchRequest getLoanTransactionsRequest = 
BatchHelper.getLoanByIdRequest(getLoanRequestId, applyLoanRequestId,
+                "associations=transactions");
+
+        final List<BatchRequest> batchRequests = 
Arrays.asList(applyLoanRequest, approveLoanRequest, disburseLoanRequest, 
repaymentRequest,
+                repaymentReversalRequest, getLoanTransactionsRequest);
+
+        final List<BatchResponse> responses = 
BatchHelper.postBatchRequestsWithoutEnclosingTransaction(this.requestSpec, 
this.responseSpec,
+                BatchHelper.toJsonString(batchRequests));
+
+        final FromJsonHelper jsonHelper = new FromJsonHelper();
+        final JsonObject repayment = 
jsonHelper.parse(responses.get(5).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) 
responses.get(4).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);
+    }
+
+    /**
+     * Tests successful run of batch goodwill credit reversal for loans. A 
'200' status code is expected on successful
+     * responses.
+     *
+     * @see AdjustTransactionCommandStrategy
+     */
+    @Test
+    public void shouldReturnOkStatusForBatchGoodwillCreditReversal() {
+
+        final String loanProductJSON = new LoanProductTestBuilder() //
+                .withPrincipal("1000.00") //
+                .withNumberOfRepayments("24") //
+                .withRepaymentAfterEvery("1") //
+                .withRepaymentTypeAsMonth() //
+                .withinterestRatePerPeriod("2") //
+                .withInterestRateFrequencyTypeAsMonths() //
+                .withAmortizationTypeAsEqualPrincipalPayment() //
+                .withInterestTypeAsDecliningBalance() //
+                .currencyDetails("0", "100").build(null);
+
+        final Integer productId = new LoanTransactionHelper(this.requestSpec, 
this.responseSpec).getLoanProductId(loanProductJSON);
+
+        final LocalDate date = LocalDate.now(Utils.getZoneIdOfTenant());
+        final Long applyLoanRequestId = 5730L;
+        final Long approveLoanRequestId = 5731L;
+        final Long disburseLoanRequestId = 5732L;
+        final Long goodwillCreditRequestId = 5733L;
+        final Long goodwillCreditReversalRequestId = 5734L;
+        final Long getLoanRequestId = 5735L;
+
+        // Create client
+        final Integer clientId = ClientHelper.createClient(this.requestSpec, 
this.responseSpec);
+        ClientHelper.verifyClientCreatedOnServer(this.requestSpec, 
this.responseSpec, clientId);
+
+        // Create an apply loan request
+        final BatchRequest applyLoanRequest = 
BatchHelper.applyLoanRequestWithClientId(applyLoanRequestId, clientId, 
productId);
+
+        // Create an approve loan request
+        final BatchRequest approveLoanRequest = 
BatchHelper.approveLoanRequest(approveLoanRequestId, applyLoanRequestId);
+
+        // Create a disburse loan request
+        final BatchRequest disburseLoanRequest = 
BatchHelper.disburseLoanRequest(disburseLoanRequestId, approveLoanRequestId);
+
+        // Create a good will credit request
+        final BatchRequest goodwillCreditRequest = 
BatchHelper.goodwillCreditRequest(goodwillCreditRequestId, 
disburseLoanRequestId, "500");
+
+        // Create a good will credit reversal request
+        final BatchRequest goodwillCreditReversalRequest = 
BatchHelper.createAdjustTransactionRequest(goodwillCreditReversalRequestId,
+                goodwillCreditRequestId, "0", date);
+
+        // Get loan transactions request
+        final BatchRequest getLoanTransactionsRequest = 
BatchHelper.getLoanByIdRequest(getLoanRequestId, applyLoanRequestId,
+                "associations=transactions");
+
+        final List<BatchRequest> batchRequests = 
Arrays.asList(applyLoanRequest, approveLoanRequest, disburseLoanRequest,
+                goodwillCreditRequest, goodwillCreditReversalRequest, 
getLoanTransactionsRequest);
+
+        final List<BatchResponse> responses = 
BatchHelper.postBatchRequestsWithoutEnclosingTransaction(this.requestSpec, 
this.responseSpec,
+                BatchHelper.toJsonString(batchRequests));
+
+        final FromJsonHelper jsonHelper = new FromJsonHelper();
+        final JsonObject goodWillCredit = 
jsonHelper.parse(responses.get(5).getBody()).getAsJsonObject().get("transactions")
+                .getAsJsonArray().get(2).getAsJsonObject();
+        final JsonArray dateArray = 
goodWillCredit.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) 
responses.get(4).getStatusCode(),
+                "Verify Status Code 200 for goodwill credit reversal");
+        Assertions.assertEquals("Goodwill Credit", 
goodWillCredit.get("type").getAsJsonObject().get("value").getAsString());
+        
Assertions.assertTrue(goodWillCredit.get("manuallyReversed").getAsBoolean());
+        Assertions.assertEquals(date, reversedOnDate);
+    }
+
+    /**
+     * Test for the successful merchant issued refund and payout refund 
reversal transaction. A '200' status code is
+     * expected on successful responses.
+     *
+     * @see AdjustTransactionCommandStrategy
+     */
+    @Test
+    public void 
shouldReturnOkStatusOnSuccessfulTransactionMerchantIssuedAndPayoutRefundReversal()
 {
+        final String loanProductJSON = new LoanProductTestBuilder() //
+                .withPrincipal("10000000.00") //
+                .withNumberOfRepayments("24") //
+                .withRepaymentAfterEvery("1") //
+                .withRepaymentTypeAsMonth() //
+                .withinterestRatePerPeriod("2") //
+                .withInterestRateFrequencyTypeAsMonths() //
+                .withAmortizationTypeAsEqualPrincipalPayment() //
+                .withInterestTypeAsDecliningBalance() //
+                .currencyDetails("0", "100").build(null);
+
+        final LocalDate date = LocalDate.now(Utils.getZoneIdOfTenant());
+        final Long applyLoanRequestId = 5730L;
+        final Long approveLoanRequestId = 5731L;
+        final Long disburseLoanRequestId = 5732L;
+        final Long merchantIssuedRefundRequestId = 5733L;
+        final Long payoutRefundRequestId = 5734L;
+        final Long merchantIssuedRefundReversalRequestId = 5735L;
+        final Long payoutRefundReversalRequestId = 5736L;
+        final Long getLoanRequestId = 5737L;
+
+        // Create product
+        final Integer productId = new LoanTransactionHelper(this.requestSpec, 
this.responseSpec).getLoanProductId(loanProductJSON);
+
+        // Create client
+        final Integer clientId = ClientHelper.createClient(this.requestSpec, 
this.responseSpec);
+        ClientHelper.verifyClientCreatedOnServer(this.requestSpec, 
this.responseSpec, clientId);
+
+        // Create an apply loan request
+        final BatchRequest applyLoanRequest = 
BatchHelper.applyLoanRequestWithClientId(applyLoanRequestId, clientId, 
productId);
+
+        // Create an approve loan request
+        final BatchRequest approveLoanRequest = 
BatchHelper.approveLoanRequest(approveLoanRequestId, applyLoanRequestId);
+
+        // Create a disburse loan request
+        final BatchRequest disburseLoanRequest = 
BatchHelper.disburseLoanRequest(disburseLoanRequestId, approveLoanRequestId,
+                date.minusDays(1));
+
+        // Create a merchant issued refund request
+        final BatchRequest merchantIssuedRefundRequest = 
BatchHelper.merchantIssuedRefundRequest(merchantIssuedRefundRequestId,
+                applyLoanRequestId, "10");
+
+        // Create a payout refund request
+        final BatchRequest payoutRefundRequest = 
BatchHelper.payoutRefundRequest(payoutRefundRequestId, applyLoanRequestId, 
"10");
+
+        // Create a merchant issued refund reversal request
+        final BatchRequest merchantIssuedRefundReversalRequest = BatchHelper
+                
.createAdjustTransactionRequest(merchantIssuedRefundReversalRequestId, 
merchantIssuedRefundRequestId, "0", date);
+
+        // Create a payout refund reversal request
+        final BatchRequest payoutRefundReversalRequest = 
BatchHelper.createAdjustTransactionRequest(payoutRefundReversalRequestId,
+                payoutRefundRequestId, "0", date);
+
+        // Get loan transactions request
+        final BatchRequest getLoanTransactionsRequest = 
BatchHelper.getLoanByIdRequest(getLoanRequestId, applyLoanRequestId,
+                "associations=transactions");
+
+        final List<BatchRequest> batchRequests = 
Arrays.asList(applyLoanRequest, approveLoanRequest, disburseLoanRequest,
+                merchantIssuedRefundRequest, payoutRefundRequest, 
merchantIssuedRefundReversalRequest, payoutRefundReversalRequest,
+                getLoanTransactionsRequest);
+
+        final List<BatchResponse> responses = 
BatchHelper.postBatchRequestsWithEnclosingTransaction(this.requestSpec, 
this.responseSpec,
+                BatchHelper.toJsonString(batchRequests));
+
+        final FromJsonHelper jsonHelper = new FromJsonHelper();
+        final JsonObject merchantIssuedRefund = 
jsonHelper.parse(responses.get(7).getBody()).getAsJsonObject().get("transactions")
+                .getAsJsonArray().get(2).getAsJsonObject();
+        final JsonObject payoutRefund = 
jsonHelper.parse(responses.get(7).getBody()).getAsJsonObject().get("transactions").getAsJsonArray()
+                .get(3).getAsJsonObject();
+        final JsonArray merchantIssuedDateArray = 
merchantIssuedRefund.get("reversedOnDate").getAsJsonArray();
+        final LocalDate merchantIssuedDate = 
LocalDate.of(merchantIssuedDateArray.get(0).getAsInt(),
+                merchantIssuedDateArray.get(1).getAsInt(), 
merchantIssuedDateArray.get(2).getAsInt());
+        final JsonArray payoutRefundDateArray = 
payoutRefund.getAsJsonObject().get("reversedOnDate").getAsJsonArray();
+        final LocalDate payoutRefundDate = 
LocalDate.of(payoutRefundDateArray.get(0).getAsInt(), 
payoutRefundDateArray.get(1).getAsInt(),
+                payoutRefundDateArray.get(2).getAsInt());
+
+        Assertions.assertEquals(HttpStatus.SC_OK, 
responses.get(5).getStatusCode(),
+                "Verify Status Code 200 for merchant issued refund reversal");
+        Assertions.assertEquals(HttpStatus.SC_OK, 
responses.get(6).getStatusCode(), "Verify Status Code 200 for payout refund 
reversal");
+        Assertions.assertEquals("Merchant Issued Refund", 
merchantIssuedRefund.get("type").getAsJsonObject().get("value").getAsString());
+        Assertions.assertEquals("Payout Refund", 
payoutRefund.get("type").getAsJsonObject().get("value").getAsString());
+        
Assertions.assertTrue(merchantIssuedRefund.get("manuallyReversed").getAsBoolean());
+        
Assertions.assertTrue(payoutRefund.get("manuallyReversed").getAsBoolean());
+        Assertions.assertEquals(date, merchantIssuedDate);
+        Assertions.assertEquals(date, payoutRefundDate);
+    }
+
     /**
      * Delete datatable
      *
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 2d5fb2963..93693c882 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
@@ -785,4 +785,21 @@ public final class BatchHelper {
 
         return br;
     }
+
+    public static BatchRequest createAdjustTransactionRequest(final Long 
requestId, final Long reference, final String amount,
+            final LocalDate date) {
+        final BatchRequest br = new BatchRequest();
+
+        br.setRequestId(requestId);
+        br.setReference(reference);
+        br.setRelativeUrl("loans/$.loanId/transactions/$.resourceId");
+        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;
+
+    }
 }


Reply via email to