diff --git a/api-docs/apiLive.htm b/api-docs/apiLive.htm
index 67dfc7120..236fc25f8 100644
--- a/api-docs/apiLive.htm
+++ b/api-docs/apiLive.htm
@@ -4777,6 +4777,7 @@ Successful transaction response:
Activate a Pending Client
Approve a Pending Loan
Disburse a Loan
+ Repayment for a Loan
diff --git a/fineract-provider/src/integrationTest/java/org/apache/fineract/integrationtests/BatchApiTest.java b/fineract-provider/src/integrationTest/java/org/apache/fineract/integrationtests/BatchApiTest.java
index aae79a068..38f89e9de 100644
--- a/fineract-provider/src/integrationTest/java/org/apache/fineract/integrationtests/BatchApiTest.java
+++ b/fineract-provider/src/integrationTest/java/org/apache/fineract/integrationtests/BatchApiTest.java
@@ -21,6 +21,8 @@ package org.apache.fineract.integrationtests;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+import java.util.Map;
+import java.util.HashMap;
import org.apache.fineract.batch.domain.BatchRequest;
import org.apache.fineract.batch.domain.BatchResponse;
@@ -339,6 +341,70 @@ public class BatchApiTest {
Assert.assertEquals("Verify Status Code 200 for Create Loan Charge", 200L, (long) response.get(3).getStatusCode());
}
+ /**
+ * 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 200(OK) is returned for the repayment requests.
+ *
+ * @see org.apache.fineract.batch.command.internal.RepayLoanCommandStrategy
+ */
+ @Test
+ public void shouldReturnOkStatusForBatchRepayment() {
+
+ 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);
+
+ // Create a createClient Request
+ final BatchRequest br1 = BatchHelper.createClientRequest(4730L, "");
+
+ // Create a activateClient Request
+ final BatchRequest br2 = BatchHelper.activateClientRequest(4731L, 4730L);
+
+ // Create a ApplyLoan Request
+ final BatchRequest br3 = BatchHelper.applyLoanRequest(4732L, 4731L, productId);
+
+ // Create a approveLoan Request
+ final BatchRequest br4 = BatchHelper.approveLoanRequest(4733L, 4732L);
+
+ // Create a disburseLoan Request
+ final BatchRequest br5 = BatchHelper.disburseLoanRequest(4734L, 4733L);
+
+ // Create a loanRepay Request
+ final BatchRequest br6 = BatchHelper.repayLoanRequest(4735L, 4734L);
+
+ // Create a loanRepay Request
+ final BatchRequest br7 = BatchHelper.repayLoanRequest(4736L, 4734L);
+
+ final List batchRequests = new ArrayList<>();
+
+ batchRequests.add(br1);
+ batchRequests.add(br2);
+ batchRequests.add(br3);
+ batchRequests.add(br4);
+ batchRequests.add(br5);
+ batchRequests.add(br6);
+ batchRequests.add(br7);
+
+ final String jsonifiedRequest = BatchHelper.toJsonString(batchRequests);
+
+ final List response = BatchHelper.postBatchRequestsWithoutEnclosingTransaction(this.requestSpec, this.responseSpec,
+ jsonifiedRequest);
+
+ Assert.assertEquals("Verify Status Code 200 for Repayment", 200L, (long) response.get(5).getStatusCode());
+ Assert.assertEquals("Verify Status Code 200 for Repayment", 200L, (long) response.get(6).getStatusCode());
+ }
+
/**
* Test for the successful activation of a pending client using
* 'ActivateClientCommandStrategy'. A '200' status code is expected on
diff --git a/fineract-provider/src/integrationTest/java/org/apache/fineract/integrationtests/common/BatchHelper.java b/fineract-provider/src/integrationTest/java/org/apache/fineract/integrationtests/common/BatchHelper.java
index 5a9a4d3d1..170ede547 100644
--- a/fineract-provider/src/integrationTest/java/org/apache/fineract/integrationtests/common/BatchHelper.java
+++ b/fineract-provider/src/integrationTest/java/org/apache/fineract/integrationtests/common/BatchHelper.java
@@ -20,6 +20,7 @@ package org.apache.fineract.integrationtests.common;
import java.util.ArrayList;
import java.util.List;
+import java.util.Map;
import org.apache.fineract.batch.domain.BatchRequest;
import org.apache.fineract.batch.domain.BatchResponse;
@@ -58,6 +59,16 @@ public class BatchHelper {
return new Gson().toJson(batchRequests);
}
+ /**
+ * Returns a Map from Json String
+ *
+ * @param jsonBody
+ * @return Map
+ */
+ public static Map generateMapFromJsonString(final String jsonString) {
+ return new Gson().fromJson(jsonString, Map.class);
+ }
+
/**
* Returns the converted string response into JSON.
*
@@ -379,6 +390,29 @@ public class BatchHelper {
return br;
}
+
+ /**
+ * Creates and returns a
+ * {@link org.apache.fineract.batch.command.internal.RepayLoanCommandStrategy}
+ * Request with given requestId.
+ *
+ *
+ * @param requestId
+ * @param reference
+ * @return BatchRequest
+ */
+ public static BatchRequest repayLoanRequest(final Long requestId, final Long reference) {
+ final BatchRequest br = new BatchRequest();
+
+ br.setRequestId(requestId);
+ br.setReference(reference);
+ br.setRelativeUrl("loans/$.loanId/transactions?command=repayment");
+ br.setMethod("POST");
+ br.setBody("{\"locale\": \"en\", \"dateFormat\": \"dd MMMM yyyy\", " +
+ "\"transactionDate\": \"15 September 2013\", \"transactionAmount\": 500}");
+
+ return br;
+ }
/**
* Checks that the client with given externalId is not created on the
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 e506004a7..8a95fd84a 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/batch/command/CommandStrategyProvider.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/batch/command/CommandStrategyProvider.java
@@ -92,6 +92,8 @@ public class CommandStrategyProvider {
this.commandStrategies.put(CommandContext.resource("loans\\/\\d+\\/charges").method("POST").build(), "createChargeCommandStrategy");
this.commandStrategies
.put(CommandContext.resource("loans\\/\\d+\\/charges").method("GET").build(), "collectChargesCommandStrategy");
+ this.commandStrategies
+ .put(CommandContext.resource("loans\\/\\d+\\/transactions\\?command=repayment").method("POST").build(), "repayLoanCommandStrategy");
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/RepayLoanCommandStrategy.java b/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/RepayLoanCommandStrategy.java
new file mode 100644
index 000000000..16d710cf4
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/RepayLoanCommandStrategy.java
@@ -0,0 +1,90 @@
+/**
+ * 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 org.apache.fineract.batch.command.CommandStrategy;
+import org.apache.fineract.batch.domain.BatchRequest;
+import org.apache.fineract.batch.domain.BatchResponse;
+import org.apache.fineract.batch.exception.ErrorHandler;
+import org.apache.fineract.batch.exception.ErrorInfo;
+import org.apache.fineract.portfolio.loanaccount.api.LoanTransactionsApiResource;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import javax.ws.rs.core.UriInfo;
+
+/**
+ * Implements {@link CommandStrategy} and handles
+ * repayment for a Loan. It passes the contents of the body from the BatchRequest
+ * to {@link LoanTransactionsApiResource} and
+ * gets back the response. This class will also catch any errors raised by
+ * {@link LoanTransactionsApiResource} and map
+ * those errors to appropriate status codes in BatchResponse.
+ *
+ * @author Mohit Sinha
+ *
+ * @see CommandStrategy
+ * @see BatchRequest
+ * @see BatchResponse
+ */
+@Component
+public class RepayLoanCommandStrategy implements CommandStrategy {
+
+ private final LoanTransactionsApiResource loanTransactionsApiResource;
+
+ @Autowired
+ public RepayLoanCommandStrategy(final LoanTransactionsApiResource loanTransactionsApiResource) {
+ this.loanTransactionsApiResource = loanTransactionsApiResource;
+ }
+
+ @Override
+ public BatchResponse execute(BatchRequest request, @SuppressWarnings("unused") UriInfo uriInfo) {
+
+ final BatchResponse response = new BatchResponse();
+ final String responseBody;
+
+ response.setRequestId(request.getRequestId());
+ response.setHeaders(request.getHeaders());
+
+ final String[] pathParameters = request.getRelativeUrl().split("/");
+ Long loanId = Long.parseLong(pathParameters[1]);
+
+ // Try-catch blocks to map exceptions to appropriate status codes
+ try {
+
+ responseBody = loanTransactionsApiResource.executeLoanTransaction(loanId, "repayment", request.getBody());
+
+ response.setStatusCode(200);
+ // Sets the body of the response after Charge has been successfully
+ // created
+ response.setBody(responseBody);
+
+ } catch (RuntimeException e) {
+
+ // Gets an object of type ErrorInfo, containing information about
+ // raised exception
+ ErrorInfo ex = ErrorHandler.handler(e);
+
+ response.setStatusCode(ex.getStatusCode());
+ response.setBody(ex.getMessage());
+ }
+
+ return response;
+ }
+}