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