blob: 132ab63869a21735aba0f9f8e835ee49e3308ccd [file] [log] [blame]
/**
* 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 org.apache.fineract.integrationtests.common.loans.LoanApplicationTestBuilder.DUE_PENALTY_INTEREST_PRINCIPAL_FEE_IN_ADVANCE_PENALTY_INTEREST_PRINCIPAL_FEE_STRATEGY;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.stream.Stream;
import org.apache.fineract.client.models.PostLoanProductsRequest;
import org.apache.fineract.client.models.PostLoanProductsResponse;
import org.apache.fineract.client.models.PostLoansRequest;
import org.apache.fineract.client.models.PostLoansResponse;
import org.apache.fineract.integrationtests.common.ClientHelper;
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.AdvancedPaymentScheduleTransactionProcessor;
import org.apache.fineract.portfolio.loanproduct.domain.RepaymentStartDateType;
import org.junit.jupiter.api.Named;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
public class LoanDueCalculationTest extends BaseLoanIntegrationTest {
private static Stream<Arguments> processingStrategy() {
return Stream.of(
Arguments.of(Named.of("originalStrategy",
DUE_PENALTY_INTEREST_PRINCIPAL_FEE_IN_ADVANCE_PENALTY_INTEREST_PRINCIPAL_FEE_STRATEGY)), //
Arguments.of(Named.of("advancedStrategy", AdvancedPaymentScheduleTransactionProcessor.ADVANCED_PAYMENT_ALLOCATION_STRATEGY)) //
);
}
// Repayment dates are calculated from the provided date (2024-02-29). As repayment starting date was provided, it
// overrules `repayment start date type` configuration
@ParameterizedTest
@MethodSource("processingStrategy")
public void dueDateBasedOnFirstRepaymentDate(String repaymentProcessor) {
runAt("2 February 2024", () -> {
// Client and Loan account creation
final Long clientId = clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId();
PostLoanProductsRequest loanProductsRequest = create4Period1MonthLongWithoutInterestProduct(repaymentProcessor);
PostLoanProductsResponse loanProductResponse = loanProductHelper.createLoanProduct(loanProductsRequest);
PostLoansRequest loanRequest = applyLoanRequest(clientId, loanProductResponse.getResourceId(), "2024-01-31", 1000.0, 4,
(postLoansRequest) -> {
postLoansRequest.transactionProcessingStrategyCode(repaymentProcessor).repaymentEvery(1).repaymentFrequencyType(2)
.loanTermFrequency(4).loanTermFrequencyType(2).dateFormat("yyyy-MM-dd")
.repaymentsStartingFromDate(LocalDate.of(2024, 2, 29));
});
PostLoansResponse postLoansResponse = loanTransactionHelper.applyLoan(loanRequest);
verifyRepaymentSchedule(postLoansResponse.getLoanId(), installment(1000.0, null, "31 January 2024"), //
installment(250.0, false, "29 February 2024"), //
installment(250.0, false, "29 March 2024"), //
installment(250.0, false, "29 April 2024"), //
installment(250.0, false, "29 May 2024")) //
;
loanTransactionHelper.approveLoan(postLoansResponse.getResourceId(), approveLoanRequest(1000.0, "31 January 2024"));
verifyRepaymentSchedule(postLoansResponse.getLoanId(), installment(1000.0, null, "31 January 2024"), //
installment(250.0, false, "29 February 2024"), //
installment(250.0, false, "29 March 2024"), //
installment(250.0, false, "29 April 2024"), //
installment(250.0, false, "29 May 2024")) //
;
disburseLoan(postLoansResponse.getLoanId(), BigDecimal.valueOf(1000.00), "31 January 2024");
verifyRepaymentSchedule(postLoansResponse.getLoanId(), installment(1000.0, null, "31 January 2024"), //
installment(250.0, false, "29 February 2024"), //
installment(250.0, false, "29 March 2024"), //
installment(250.0, false, "29 April 2024"), //
installment(250.0, false, "29 May 2024")) //
;
});
}
// Repayment dates are calculated based on `repayment start date type` configuration(=Expected disbursement date).
// Expected disbursement date `2024-01-30`,
// which is used to generate repayment due date when loan got submitted and approved, however the loan got disbursed
// on `2024-01-31`,
// the repayment schedule reflects the "new date" after it got disbursed
@ParameterizedTest
@MethodSource("processingStrategy")
public void dueDateBasedOnExpectedDisbursementDate(String repaymentProcessor) {
runAt("31 March 2024", () -> {
// Client and Loan account creation
final Long clientId = clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId();
PostLoanProductsRequest loanProductsRequest = create4Period1MonthLongWithoutInterestProduct(repaymentProcessor)
.repaymentStartDateType(RepaymentStartDateType.DISBURSEMENT_DATE.getValue());
PostLoanProductsResponse loanProductResponse = loanProductHelper.createLoanProduct(loanProductsRequest);
PostLoansRequest loanRequest = applyLoanRequest(clientId, loanProductResponse.getResourceId(), "2024-01-30", 1000.0, 4,
(postLoansRequest) -> {
postLoansRequest.transactionProcessingStrategyCode(repaymentProcessor).repaymentEvery(1).repaymentFrequencyType(2)
.loanTermFrequency(4).loanTermFrequencyType(2).dateFormat("yyyy-MM-dd");
});
PostLoansResponse postLoansResponse = loanTransactionHelper.applyLoan(loanRequest);
verifyRepaymentSchedule(postLoansResponse.getLoanId(), installment(1000.0, null, "30 January 2024"), //
installment(250.0, false, "29 February 2024"), //
installment(250.0, false, "30 March 2024"), //
installment(250.0, false, "30 April 2024"), //
installment(250.0, false, "30 May 2024")) //
;
loanTransactionHelper.approveLoan(postLoansResponse.getResourceId(), approveLoanRequest(1000.0, "31 January 2024"));
verifyRepaymentSchedule(postLoansResponse.getLoanId(), installment(1000.0, null, "30 January 2024"), //
installment(250.0, false, "29 February 2024"), //
installment(250.0, false, "30 March 2024"), //
installment(250.0, false, "30 April 2024"), //
installment(250.0, false, "30 May 2024")) //
;
disburseLoan(postLoansResponse.getLoanId(), BigDecimal.valueOf(1000.00), "31 March 2024");
verifyRepaymentSchedule(postLoansResponse.getLoanId(), installment(1000.0, null, "31 March 2024"), //
installment(250.0, false, "30 April 2024"), //
installment(250.0, false, "31 May 2024"), //
installment(250.0, false, "30 June 2024"), //
installment(250.0, false, "31 July 2024")) //
;
});
}
// Repayment dates are calculated based on `repayment start date type` configuration(=Submitted on date). Submitted
// on date is `2024-01-31`,
// and even the expected disbursement date is `2024-02-01`, the generated repayment schedule honors the submitted on
// date
// when it got disbursed on `2024-02-03`, the repayment schedule due dates got no changed.
@ParameterizedTest
@MethodSource("processingStrategy")
public void dueDateBasedOnSubmittedOnDate(String repaymentProcessor) {
runAt("03 February 2024", () -> {
// Client and Loan account creation
final Long clientId = clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId();
PostLoanProductsRequest loanProductsRequest = create4Period1MonthLongWithoutInterestProduct(repaymentProcessor)
.repaymentStartDateType(RepaymentStartDateType.SUBMITTED_ON_DATE.getValue());
PostLoanProductsResponse loanProductResponse = loanProductHelper.createLoanProduct(loanProductsRequest);
PostLoansRequest loanRequest = applyLoanRequest(clientId, loanProductResponse.getResourceId(), "2024-02-01", 1000.0, 4,
(postLoansRequest) -> {
postLoansRequest.transactionProcessingStrategyCode(repaymentProcessor).repaymentEvery(1).repaymentFrequencyType(2)
.loanTermFrequency(4).loanTermFrequencyType(2).submittedOnDate("2024-01-31").dateFormat("yyyy-MM-dd");
});
PostLoansResponse postLoansResponse = loanTransactionHelper.applyLoan(loanRequest);
verifyRepaymentSchedule(postLoansResponse.getLoanId(), installment(1000.0, null, "31 January 2024"), //
installment(250.0, false, "29 February 2024"), //
installment(250.0, false, "31 March 2024"), //
installment(250.0, false, "30 April 2024"), //
installment(250.0, false, "31 May 2024")) //
;
loanTransactionHelper.approveLoan(postLoansResponse.getResourceId(), approveLoanRequest(1000.0, "31 January 2024"));
verifyRepaymentSchedule(postLoansResponse.getLoanId(), installment(1000.0, null, "31 January 2024"), //
installment(250.0, false, "29 February 2024"), //
installment(250.0, false, "31 March 2024"), //
installment(250.0, false, "30 April 2024"), //
installment(250.0, false, "31 May 2024")) //
;
disburseLoan(postLoansResponse.getLoanId(), BigDecimal.valueOf(1000.00), "03 February 2024");
verifyRepaymentSchedule(postLoansResponse.getLoanId(), installment(1000.0, null, "01 February 2024"), //
installment(250.0, false, "29 February 2024"), //
installment(250.0, false, "31 March 2024"), //
installment(250.0, false, "30 April 2024"), //
installment(250.0, false, "31 May 2024")) //
;
});
}
// Repayment dates are calculated based on `repayment start date type` configuration(=Submitted on date). Submitted
// on date is `2024-01-31 the expected disbursement date is `2024-02-26`, the minimum days between disbursement and
// first repayment is 10 days
// so the repayment schedule got amended accordingly
@ParameterizedTest
@MethodSource("processingStrategy")
public void dueDateBasedOnSubmittedOnDateButThereShallBeMinimumDaysBetweenDisbursementAndFirstRepayment(String repaymentProcessor) {
runAt("31 January 2024", () -> {
// Client and Loan account creation
final Long clientId = clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId();
PostLoanProductsRequest loanProductsRequest = create4Period1MonthLongWithoutInterestProduct(repaymentProcessor)
.repaymentStartDateType(RepaymentStartDateType.SUBMITTED_ON_DATE.getValue())
.minimumDaysBetweenDisbursalAndFirstRepayment(10);
PostLoanProductsResponse loanProductResponse = loanProductHelper.createLoanProduct(loanProductsRequest);
PostLoansRequest loanRequest = applyLoanRequest(clientId, loanProductResponse.getResourceId(), "2024-02-26", 1000.0, 4,
(postLoansRequest) -> {
postLoansRequest.transactionProcessingStrategyCode(repaymentProcessor).repaymentEvery(1).repaymentFrequencyType(2)
.loanTermFrequency(4).loanTermFrequencyType(2).submittedOnDate("2024-01-31").dateFormat("yyyy-MM-dd");
});
PostLoansResponse postLoansResponse = loanTransactionHelper.applyLoan(loanRequest);
verifyRepaymentSchedule(postLoansResponse.getLoanId(), installment(1000.0, null, "31 January 2024"), //
installment(250.0, false, "07 March 2024"), //
installment(250.0, false, "07 April 2024"), //
installment(250.0, false, "07 May 2024"), //
installment(250.0, false, "07 June 2024")) //
;
loanTransactionHelper.approveLoan(postLoansResponse.getResourceId(), approveLoanRequest(1000.0, "31 January 2024"));
verifyRepaymentSchedule(postLoansResponse.getLoanId(), installment(1000.0, null, "31 January 2024"), //
installment(250.0, false, "07 March 2024"), //
installment(250.0, false, "07 April 2024"), //
installment(250.0, false, "07 May 2024"), //
installment(250.0, false, "07 June 2024")) //
;
disburseLoan(postLoansResponse.getLoanId(), BigDecimal.valueOf(1000.00), "31 January 2024");
verifyRepaymentSchedule(postLoansResponse.getLoanId(), installment(1000.0, null, "31 January 2024"), //
installment(250.0, false, "07 March 2024"), //
installment(250.0, false, "07 April 2024"), //
installment(250.0, false, "07 May 2024"), //
installment(250.0, false, "07 June 2024")) //
;
});
}
// Repayment dates are calculated based on `repayment start date type` configuration(=Disbursement date). Submitted
// on date is `2024-01-31 the expected disbursement date is `2024-02-26`, the minimum days between disbursement and
// first repayment is 36 days
// so the repayment schedule got amended accordingly
@ParameterizedTest
@MethodSource("processingStrategy")
public void dueDateBasedOnExpectedDisbursalDateButThereShallBeMinimumDaysBetweenDisbursementAndFirstRepayment(
String repaymentProcessor) {
runAt("31 January 2024", () -> {
// Client and Loan account creation
final Long clientId = clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId();
PostLoanProductsRequest loanProductsRequest = create4Period1MonthLongWithoutInterestProduct(repaymentProcessor)
.repaymentStartDateType(RepaymentStartDateType.DISBURSEMENT_DATE.getValue())
.minimumDaysBetweenDisbursalAndFirstRepayment(36);
PostLoanProductsResponse loanProductResponse = loanProductHelper.createLoanProduct(loanProductsRequest);
PostLoansRequest loanRequest = applyLoanRequest(clientId, loanProductResponse.getResourceId(), "2024-01-31", 1000.0, 4,
(postLoansRequest) -> {
postLoansRequest.transactionProcessingStrategyCode(repaymentProcessor).repaymentEvery(1).repaymentFrequencyType(2)
.loanTermFrequency(4).loanTermFrequencyType(2).submittedOnDate("2024-01-31").dateFormat("yyyy-MM-dd");
});
PostLoansResponse postLoansResponse = loanTransactionHelper.applyLoan(loanRequest);
verifyRepaymentSchedule(postLoansResponse.getLoanId(), installment(1000.0, null, "31 January 2024"), //
installment(250.0, false, "07 March 2024"), //
installment(250.0, false, "07 April 2024"), //
installment(250.0, false, "07 May 2024"), //
installment(250.0, false, "07 June 2024")) //
;
loanTransactionHelper.approveLoan(postLoansResponse.getResourceId(), approveLoanRequest(1000.0, "31 January 2024"));
verifyRepaymentSchedule(postLoansResponse.getLoanId(), installment(1000.0, null, "31 January 2024"), //
installment(250.0, false, "07 March 2024"), //
installment(250.0, false, "07 April 2024"), //
installment(250.0, false, "07 May 2024"), //
installment(250.0, false, "07 June 2024")) //
;
disburseLoan(postLoansResponse.getLoanId(), BigDecimal.valueOf(1000.00), "31 January 2024");
verifyRepaymentSchedule(postLoansResponse.getLoanId(), installment(1000.0, null, "31 January 2024"), //
installment(250.0, false, "07 March 2024"), //
installment(250.0, false, "07 April 2024"), //
installment(250.0, false, "07 May 2024"), //
installment(250.0, false, "07 June 2024")) //
;
});
}
}