FINERACT-1971: Fixing Loan repayment re-schedule with down payment.
This commit is contained in:
committed by
Arnold Gálovics
parent
0b1ca2dced
commit
9b2c24289c
+2
@@ -2186,8 +2186,10 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
||||
Money principalToBeScheduled = getPrincipalToBeScheduled(loanApplicationTerms);
|
||||
// actual outstanding balance for interest calculation
|
||||
Money outstandingBalance = principalToBeScheduled;
|
||||
loanScheduleParams.setOutstandingBalance(outstandingBalance);
|
||||
// total outstanding balance as per rest for interest calculation.
|
||||
Money outstandingBalanceAsPerRest = outstandingBalance;
|
||||
loanScheduleParams.setOutstandingBalanceAsPerRest(outstandingBalanceAsPerRest);
|
||||
|
||||
// this is required to update total fee amounts in the
|
||||
// LoanScheduleModel
|
||||
|
||||
+20
-15
@@ -70,13 +70,13 @@ public abstract class BaseLoanIntegrationTest {
|
||||
|
||||
protected static final String DATETIME_PATTERN = "dd MMMM yyyy";
|
||||
|
||||
protected final ResponseSpecification requestSpec = createResponseSpecification(200);
|
||||
protected final RequestSpecification responseSpec = createRequestSpecification();
|
||||
protected final ResponseSpecification responseSpec = createResponseSpecification(200);
|
||||
protected final RequestSpecification requestSpec = createRequestSpecification();
|
||||
|
||||
protected final AccountHelper accountHelper = new AccountHelper(responseSpec, requestSpec);
|
||||
protected final LoanTransactionHelper loanTransactionHelper = new LoanTransactionHelper(responseSpec, requestSpec);
|
||||
protected final AccountHelper accountHelper = new AccountHelper(requestSpec, responseSpec);
|
||||
protected final LoanTransactionHelper loanTransactionHelper = new LoanTransactionHelper(requestSpec, responseSpec);
|
||||
protected final LoanProductHelper loanProductHelper = new LoanProductHelper();
|
||||
protected JournalEntryHelper journalEntryHelper = new JournalEntryHelper(responseSpec, requestSpec);
|
||||
protected JournalEntryHelper journalEntryHelper = new JournalEntryHelper(requestSpec, responseSpec);
|
||||
protected BusinessDateHelper businessDateHelper = new BusinessDateHelper();
|
||||
|
||||
// asset
|
||||
@@ -196,7 +196,7 @@ public abstract class BaseLoanIntegrationTest {
|
||||
|
||||
protected void verifyUndoLastDisbursalShallFail(Long loanId, String expectedError) {
|
||||
ResponseSpecification errorResponse = new ResponseSpecBuilder().expectStatusCode(403).build();
|
||||
LoanTransactionHelper validationErrorHelper = new LoanTransactionHelper(this.responseSpec, errorResponse);
|
||||
LoanTransactionHelper validationErrorHelper = new LoanTransactionHelper(this.requestSpec, errorResponse);
|
||||
CallFailedRuntimeException exception = assertThrows(CallFailedRuntimeException.class, () -> {
|
||||
validationErrorHelper.undoLastDisbursalLoan(loanId, new PostLoansLoanIdRequest());
|
||||
});
|
||||
@@ -208,7 +208,7 @@ public abstract class BaseLoanIntegrationTest {
|
||||
}
|
||||
|
||||
protected void verifyTransactions(Long loanId, Transaction... transactions) {
|
||||
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoan(responseSpec, requestSpec, loanId.intValue());
|
||||
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId.intValue());
|
||||
if (transactions == null || transactions.length == 0) {
|
||||
assertNull(loanDetails.getTransactions(), "No transaction is expected");
|
||||
} else {
|
||||
@@ -241,7 +241,7 @@ public abstract class BaseLoanIntegrationTest {
|
||||
}
|
||||
|
||||
protected void verifyRepaymentSchedule(Long loanId, Installment... installments) {
|
||||
GetLoansLoanIdResponse loanResponse = loanTransactionHelper.getLoan(responseSpec, requestSpec, loanId.intValue());
|
||||
GetLoansLoanIdResponse loanResponse = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId.intValue());
|
||||
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(DATETIME_PATTERN);
|
||||
|
||||
Assertions.assertNotNull(loanResponse.getRepaymentSchedule());
|
||||
@@ -264,25 +264,26 @@ public abstract class BaseLoanIntegrationTest {
|
||||
|
||||
protected void runAt(String date, Runnable runnable) {
|
||||
try {
|
||||
GlobalConfigurationHelper.updateEnabledFlagForGlobalConfiguration(responseSpec, requestSpec, 42, true);
|
||||
GlobalConfigurationHelper.updateIsBusinessDateEnabled(responseSpec, requestSpec, TRUE);
|
||||
GlobalConfigurationHelper.updateEnabledFlagForGlobalConfiguration(requestSpec, responseSpec, 42, true);
|
||||
GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, TRUE);
|
||||
businessDateHelper.updateBusinessDate(
|
||||
new BusinessDateRequest().type(BUSINESS_DATE.getName()).date(date).dateFormat(DATETIME_PATTERN).locale("en"));
|
||||
runnable.run();
|
||||
} finally {
|
||||
GlobalConfigurationHelper.updateIsBusinessDateEnabled(responseSpec, requestSpec, FALSE);
|
||||
GlobalConfigurationHelper.updateEnabledFlagForGlobalConfiguration(responseSpec, requestSpec, 42, false);
|
||||
GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, FALSE);
|
||||
GlobalConfigurationHelper.updateEnabledFlagForGlobalConfiguration(requestSpec, responseSpec, 42, false);
|
||||
}
|
||||
}
|
||||
|
||||
protected Long applyAndApproveLoan(Long clientId, Long loanProductId, String loanDisbursementDate, Double amount) {
|
||||
protected Long applyAndApproveLoan(Long clientId, Long loanProductId, String loanDisbursementDate, Double amount,
|
||||
int numberOfRepayments) {
|
||||
PostLoansResponse postLoansResponse = loanTransactionHelper.applyLoan(new PostLoansRequest().clientId(clientId)
|
||||
.productId(loanProductId).expectedDisbursementDate(loanDisbursementDate).dateFormat(DATETIME_PATTERN)
|
||||
.transactionProcessingStrategyCode(DUE_PENALTY_INTEREST_PRINCIPAL_FEE_IN_ADVANCE_PENALTY_INTEREST_PRINCIPAL_FEE_STRATEGY)
|
||||
.locale("en").submittedOnDate(loanDisbursementDate).amortizationType(1).interestRatePerPeriod(0)
|
||||
.interestCalculationPeriodType(1).interestType(0).repaymentFrequencyType(0).repaymentEvery(30).repaymentFrequencyType(0)
|
||||
.numberOfRepayments(1).loanTermFrequency(30).loanTermFrequencyType(0).maxOutstandingLoanBalance(BigDecimal.valueOf(amount))
|
||||
.principal(BigDecimal.valueOf(amount)).loanType("individual"));
|
||||
.numberOfRepayments(numberOfRepayments).loanTermFrequency(numberOfRepayments * 30).loanTermFrequencyType(0)
|
||||
.maxOutstandingLoanBalance(BigDecimal.valueOf(amount)).principal(BigDecimal.valueOf(amount)).loanType("individual"));
|
||||
|
||||
PostLoansLoanIdResponse approvedLoanResult = loanTransactionHelper.approveLoan(postLoansResponse.getResourceId(),
|
||||
new PostLoansLoanIdRequest().approvedLoanAmount(BigDecimal.valueOf(amount)).dateFormat(DATETIME_PATTERN)
|
||||
@@ -291,6 +292,10 @@ public abstract class BaseLoanIntegrationTest {
|
||||
return approvedLoanResult.getLoanId();
|
||||
}
|
||||
|
||||
protected Long applyAndApproveLoan(Long clientId, Long loanProductId, String loanDisbursementDate, Double amount) {
|
||||
return applyAndApproveLoan(clientId, loanProductId, loanDisbursementDate, amount, 1);
|
||||
}
|
||||
|
||||
protected void addRepaymentForLoan(Long loanId, Double amount, String date) {
|
||||
String firstRepaymentUUID = UUID.randomUUID().toString();
|
||||
loanTransactionHelper.makeLoanRepayment(loanId, new PostLoansLoanIdTransactionsRequest().dateFormat(DATETIME_PATTERN)
|
||||
|
||||
+138
@@ -0,0 +1,138 @@
|
||||
/**
|
||||
* 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.integrationtests;
|
||||
|
||||
import static java.lang.Boolean.TRUE;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import org.apache.fineract.client.models.GetLoanProductsProductIdResponse;
|
||||
import org.apache.fineract.client.models.PostLoanProductsRequest;
|
||||
import org.apache.fineract.client.models.PostLoanProductsResponse;
|
||||
import org.apache.fineract.integrationtests.common.ClientHelper;
|
||||
import org.apache.fineract.integrationtests.common.LoanRescheduleRequestHelper;
|
||||
import org.apache.fineract.integrationtests.common.loans.LoanRescheduleRequestTestBuilder;
|
||||
import org.apache.fineract.integrationtests.common.loans.LoanTestLifecycleExtension;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
|
||||
@ExtendWith(LoanTestLifecycleExtension.class)
|
||||
public class LoanRescheduleTestWithDownpayment extends BaseLoanIntegrationTest {
|
||||
|
||||
public static final BigDecimal DOWN_PAYMENT_PERCENTAGE = new BigDecimal(25);
|
||||
private final ClientHelper clientHelper = new ClientHelper(this.requestSpec, this.responseSpec);
|
||||
|
||||
private final LoanRescheduleRequestHelper loanRescheduleRequestHelper = new LoanRescheduleRequestHelper(this.requestSpec,
|
||||
this.responseSpec);
|
||||
|
||||
@Test
|
||||
public void testRescheduleWithDownpayment() {
|
||||
runAt("01 January 2023", () -> {
|
||||
// Create Client
|
||||
Long clientId = clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId();
|
||||
|
||||
// Create Loan Product
|
||||
Long loanProductId = createLoanProductWith25PctDownPayment(true, true);
|
||||
|
||||
// Apply and Approve Loan
|
||||
Long loanId = applyAndApproveLoan(clientId, loanProductId, "01 January 2023", 1500.0, 2);
|
||||
|
||||
// Verify Repayment Schedule
|
||||
verifyRepaymentSchedule(loanId, //
|
||||
installment(1500.0, null, "01 January 2023"), //
|
||||
installment(375.0, false, "01 January 2023"), //
|
||||
installment(563.0, false, "31 January 2023"), //
|
||||
installment(562.0, false, "02 March 2023") //
|
||||
);
|
||||
|
||||
// 1st Disburse Loan
|
||||
disburseLoan(loanId, BigDecimal.valueOf(1000.00), "01 January 2023");
|
||||
|
||||
// verify transactions
|
||||
verifyTransactions(loanId, //
|
||||
transaction(250.0, "Down Payment", "01 January 2023"), //
|
||||
transaction(1000.0, "Disbursement", "01 January 2023") //
|
||||
);
|
||||
|
||||
// verify journal entries
|
||||
verifyJournalEntries(loanId, journalEntry(250.0, loansReceivableAccount, "CREDIT"), //
|
||||
journalEntry(250.0, suspenseClearingAccount, "DEBIT"), //
|
||||
journalEntry(1000.0, loansReceivableAccount, "DEBIT"), //
|
||||
journalEntry(1000.0, suspenseClearingAccount, "CREDIT") //
|
||||
);
|
||||
|
||||
// Verify Repayment Schedule
|
||||
verifyRepaymentSchedule(loanId, //
|
||||
installment(1000.0, null, "01 January 2023"), //
|
||||
installment(250.0, true, "01 January 2023"), //
|
||||
installment(375.0, false, "31 January 2023"), //
|
||||
installment(375.0, false, "02 March 2023") //
|
||||
);
|
||||
|
||||
String requestJSON = new LoanRescheduleRequestTestBuilder().updateGraceOnInterest(null).updateGraceOnPrincipal(null)
|
||||
.updateExtraTerms(null).updateNewInterestRate(null).updateRescheduleFromDate("31 January 2023")
|
||||
.updateAdjustedDueDate("15 February 2023").updateSubmittedOnDate("01 January 2023").updateRescheduleReasonId("1")
|
||||
.build(loanId.toString());
|
||||
|
||||
Integer loanRescheduleRequest = loanRescheduleRequestHelper.createLoanRescheduleRequest(requestJSON);
|
||||
requestJSON = new LoanRescheduleRequestTestBuilder().updateSubmittedOnDate("01 January 2023")
|
||||
.getApproveLoanRescheduleRequestJSON();
|
||||
Integer approveLoanRescheduleRequest = loanRescheduleRequestHelper.approveLoanRescheduleRequest(loanRescheduleRequest,
|
||||
requestJSON);
|
||||
|
||||
verifyRepaymentSchedule(loanId, //
|
||||
installment(1000.0, null, "01 January 2023"), //
|
||||
installment(250.0, true, "01 January 2023"), //
|
||||
installment(375.0, false, "15 February 2023"), //
|
||||
installment(375.0, false, "17 March 2023") //
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
private Long createLoanProductWith25PctDownPayment(boolean autoDownPaymentEnabled, boolean multiDisburseEnabled) {
|
||||
PostLoanProductsRequest product = createOnePeriod30DaysLongNoInterestPeriodicAccrualProduct();
|
||||
product.setMultiDisburseLoan(multiDisburseEnabled);
|
||||
|
||||
if (!multiDisburseEnabled) {
|
||||
product.disallowExpectedDisbursements(null);
|
||||
product.setAllowApprovedDisbursedAmountsOverApplied(null);
|
||||
product.overAppliedCalculationType(null);
|
||||
product.overAppliedNumber(null);
|
||||
}
|
||||
|
||||
product.setEnableDownPayment(true);
|
||||
product.setDisbursedAmountPercentageForDownPayment(DOWN_PAYMENT_PERCENTAGE);
|
||||
product.setEnableAutoRepaymentForDownPayment(autoDownPaymentEnabled);
|
||||
|
||||
PostLoanProductsResponse loanProductResponse = loanProductHelper.createLoanProduct(product);
|
||||
GetLoanProductsProductIdResponse getLoanProductsProductIdResponse = loanProductHelper
|
||||
.retrieveLoanProductById(loanProductResponse.getResourceId());
|
||||
|
||||
Long loanProductId = loanProductResponse.getResourceId();
|
||||
|
||||
assertEquals(TRUE, getLoanProductsProductIdResponse.getEnableDownPayment());
|
||||
assertNotNull(getLoanProductsProductIdResponse.getDisbursedAmountPercentageForDownPayment());
|
||||
assertEquals(0, getLoanProductsProductIdResponse.getDisbursedAmountPercentageForDownPayment().compareTo(DOWN_PAYMENT_PERCENTAGE));
|
||||
assertEquals(autoDownPaymentEnabled, getLoanProductsProductIdResponse.getEnableAutoRepaymentForDownPayment());
|
||||
assertEquals(multiDisburseEnabled, getLoanProductsProductIdResponse.getMultiDisburseLoan());
|
||||
return loanProductId;
|
||||
}
|
||||
|
||||
}
|
||||
+1
-1
@@ -37,7 +37,7 @@ import org.junit.jupiter.api.extension.ExtendWith;
|
||||
public class UndoLoanDisbursalWithDownPaymentIntegrationTest extends BaseLoanIntegrationTest {
|
||||
|
||||
public static final BigDecimal DOWN_PAYMENT_PERCENTAGE = new BigDecimal(25);
|
||||
private final ClientHelper clientHelper = new ClientHelper(this.responseSpec, this.requestSpec);
|
||||
private final ClientHelper clientHelper = new ClientHelper(this.requestSpec, this.responseSpec);
|
||||
|
||||
@Test
|
||||
public void testUndoDisbursalForLoanWithSingleDisbursalAutoDownPaymentEnabledAndNoManualTransactions() {
|
||||
|
||||
Reference in New Issue
Block a user