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;
+
+ }
}