Skip to content

Commit 09b64cc

Browse files
author
Jose Alberto Hernandez
committed
FINERACT-2326: Loan contract termination same disbursement date
1 parent 553347b commit 09b64cc

File tree

7 files changed

+83
-9
lines changed

7 files changed

+83
-9
lines changed

fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1410,8 +1410,15 @@ public void addLoanRepaymentScheduleInstallment(final LoanRepaymentScheduleInsta
14101410
* @param date
14111411
* @return a schedule installment is related to the provided date
14121412
**/
1413-
public LoanRepaymentScheduleInstallment getRelatedRepaymentScheduleInstallment(LocalDate date) {
1414-
return getRepaymentScheduleInstallment(e -> DateUtils.isDateInRangeFromExclusiveToInclusive(date, e.getFromDate(), e.getDueDate()));
1413+
public LoanRepaymentScheduleInstallment getRelatedRepaymentScheduleInstallment(final LocalDate date) {
1414+
return getRepaymentScheduleInstallment(e -> (DateUtils.isDateInRangeFromExclusiveToInclusive(date, e.getFromDate(), e.getDueDate())
1415+
|| (e.isFirstNormalInstallment(getRepaymentScheduleInstallments()) && DateUtils.isDateInRangeInclusive(date, e.getFromDate(), e.getDueDate()))));
1416+
}
1417+
1418+
public List<LoanRepaymentScheduleInstallment> getInstallmentsUpToTransactionDate(final LocalDate transactionDate) {
1419+
return getRepaymentScheduleInstallments().stream().filter(i -> (transactionDate.isAfter(i.getFromDate())
1420+
|| (i.isFirstNormalInstallment(getRepaymentScheduleInstallments())) && !transactionDate.isBefore(i.getFromDate())))
1421+
.collect(Collectors.toCollection(ArrayList::new));
14151422
}
14161423

14171424
public LoanRepaymentScheduleInstallment fetchRepaymentScheduleInstallment(final Integer installmentNumber) {

fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanRepaymentScheduleInstallment.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1215,4 +1215,9 @@ private BigDecimal setScaleAndDefaultToNullIfZero(final BigDecimal value) {
12151215
}
12161216
return value.setScale(6, MoneyHelper.getRoundingMode());
12171217
}
1218+
1219+
public boolean isFirstNormalInstallment(List<LoanRepaymentScheduleInstallment> installments) {
1220+
return installments.stream().filter(rp -> !rp.isDownPayment())
1221+
.findFirst().stream().anyMatch(rp -> rp.equals(this));
1222+
}
12181223
}

fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/AdvancedPaymentScheduleTransactionProcessor.java

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1843,10 +1843,10 @@ private void handleAccelerateMaturityDate(final LoanTransaction loanTransaction,
18431843
final Loan loan = loanTransaction.getLoan();
18441844
final LoanRepaymentScheduleInstallment currentInstallment = loan.getRelatedRepaymentScheduleInstallment(transactionDate);
18451845

1846-
if (!installments.isEmpty() && transactionDate.isBefore(loan.getMaturityDate())) {
1846+
if (!installments.isEmpty() && transactionDate.isBefore(loan.getMaturityDate()) && currentInstallment != null) {
18471847
if (currentInstallment.isNotFullyPaidOff()) {
18481848
if (transactionCtx instanceof ProgressiveTransactionCtx progressiveTransactionCtx
1849-
&& loanTransaction.getLoan().isInterestBearingAndInterestRecalculationEnabled()) {
1849+
&& loan.isInterestBearingAndInterestRecalculationEnabled()) {
18501850
final BigDecimal interestOutstanding = currentInstallment.getInterestOutstanding(loan.getCurrency()).getAmount();
18511851
final BigDecimal newInterest = emiCalculator.getPeriodInterestTillDate(progressiveTransactionCtx.getModel(),
18521852
currentInstallment.getDueDate(), transactionDate, true).getAmount();
@@ -1900,9 +1900,7 @@ private void handleAccelerateMaturityDate(final LoanTransaction loanTransaction,
19001900
MathUtil.nullToZero(currentInstallment.getTotalPaidInAdvance()).add(futureTotalPaidInAdvance));
19011901
}
19021902

1903-
final List<LoanRepaymentScheduleInstallment> installmentsUpToTransactionDate = installments.stream()
1904-
.filter(installment -> transactionDate.isAfter(installment.getFromDate()))
1905-
.collect(Collectors.toCollection(ArrayList::new));
1903+
final List<LoanRepaymentScheduleInstallment> installmentsUpToTransactionDate = loan.getInstallmentsUpToTransactionDate(transactionDate);
19061904

19071905
final List<LoanTransaction> transactionsToBeReprocessed = loan.getLoanTransactions().stream()
19081906
.filter(transaction -> transaction.getTransactionDate().isBefore(transactionDate))

fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/calc/data/InterestPeriod.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,8 @@ public BigDecimal getCalculatedDueInterest() {
144144
long lengthTillPeriodDueDate = getLengthTillPeriodDueDate();
145145
final BigDecimal interestDueTillRepaymentDueDate = getCalculatedDueInterest(
146146
getRepaymentPeriod().getLoanProductRelatedDetail().getInterestMethod(), lengthTillPeriodDueDate); //
147-
return MathUtil.negativeToZero(MathUtil.add(getMc(), getCreditedInterest().getAmount(), interestDueTillRepaymentDueDate));
147+
BigDecimal calculatedDueInterest = MathUtil.negativeToZero(MathUtil.add(getMc(), getCreditedInterest().getAmount(), interestDueTillRepaymentDueDate));
148+
return calculatedDueInterest;
148149
}
149150

150151
public BigDecimal getCalculatedDueInterest(InterestMethod method, long lengthTillPeriodDueDate) {

fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/calc/data/ProgressiveLoanInterestScheduleModel.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -440,4 +440,8 @@ public Function<Long, LocalDate> resolveRepaymentPEriodLengthGeneratorFunction(L
440440
default -> throw new UnsupportedOperationException();
441441
};
442442
}
443+
444+
public boolean isInterestRecognitionOnDisbursementDate() {
445+
return loanProductRelatedDetail.isInterestRecognitionOnDisbursementDate();
446+
}
443447
}

integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanContractTerminationTest.java

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,19 @@
1818
*/
1919
package org.apache.fineract.integrationtests;
2020

21+
import static org.junit.jupiter.api.Assertions.assertEquals;
22+
2123
import java.math.BigDecimal;
2224
import java.util.concurrent.atomic.AtomicReference;
25+
import org.apache.fineract.client.models.GetLoansLoanIdResponse;
2326
import org.apache.fineract.client.models.PostClientsResponse;
2427
import org.apache.fineract.client.models.PostLoanProductsResponse;
2528
import org.apache.fineract.client.models.PostLoansLoanIdRequest;
29+
import org.apache.fineract.client.models.PutGlobalConfigurationsRequest;
2630
import org.apache.fineract.client.util.CallFailedRuntimeException;
31+
import org.apache.fineract.infrastructure.configuration.api.GlobalConfigurationConstants;
2732
import org.apache.fineract.integrationtests.common.ClientHelper;
33+
import org.apache.fineract.integrationtests.common.GlobalConfigurationHelper;
2834
import org.apache.fineract.integrationtests.common.Utils;
2935
import org.apache.fineract.integrationtests.common.loans.LoanProductTestBuilder;
3036
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleType;
@@ -118,4 +124,56 @@ public void testNegativeLoanContractTerminationInNoProgressiveLoan() {
118124
});
119125
}
120126

127+
@Test
128+
public void testLoanContractTerminationSameDisbursementDate() {
129+
final PostClientsResponse client = clientHelper.createClient(ClientHelper.defaultClientCreationRequest());
130+
final GlobalConfigurationHelper globalConfigurationHelper = new GlobalConfigurationHelper();
131+
132+
runAt("1 January 2024", () -> {
133+
/*
134+
PostLoanProductsResponse loanProductsResponse = loanProductHelper.createLoanProduct(create4IProgressive().interestRecognitionOnDisbursementDate(false));
135+
Long loanId = applyAndApproveProgressiveLoan(client.getClientId(), loanProductsResponse.getResourceId(), "1 January 2024",
136+
500.0, 7.0, 6, (request) -> request.interestRecognitionOnDisbursementDate(false));
137+
138+
disburseLoan(loanId, BigDecimal.valueOf(100), "1 January 2024");
139+
140+
loanTransactionHelper.moveLoanState(loanId,
141+
new PostLoansLoanIdRequest().note("Contract Termination Test").externalId(Utils.randomStringGenerator("", 20)),
142+
"contractTermination");
143+
144+
verifyTransactions(loanId, //
145+
transaction(100.0, "Disbursement", "01 January 2024"), //
146+
transaction(100.0, "Contract Termination", "01 January 2024"));
147+
148+
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanId);
149+
assertEquals(BigDecimal.ZERO.stripTrailingZeros(),
150+
loanDetails.getSummary().getInterestCharged().stripTrailingZeros());
151+
*/
152+
153+
globalConfigurationHelper.updateGlobalConfiguration(GlobalConfigurationConstants.INTEREST_CHARGED_FROM_DATE_SAME_AS_DISBURSAL_DATE,
154+
new PutGlobalConfigurationsRequest().enabled(true));
155+
156+
PostLoanProductsResponse loanProductsResponse = loanProductHelper.createLoanProduct(create4IProgressive().interestRecognitionOnDisbursementDate(true));
157+
Long loanId = applyAndApproveProgressiveLoan(client.getClientId(), loanProductsResponse.getResourceId(), "1 January 2024",
158+
500.0, 7.0, 6, (request) -> request.interestRecognitionOnDisbursementDate(true));
159+
160+
disburseLoan(loanId, BigDecimal.valueOf(200), "1 January 2024");
161+
162+
loanTransactionHelper.moveLoanState(loanId,
163+
new PostLoansLoanIdRequest().note("Contract Termination Test").externalId(Utils.randomStringGenerator("", 20)),
164+
"contractTermination");
165+
166+
verifyTransactions(loanId, //
167+
transaction(200.0, "Disbursement", "01 January 2024"), //
168+
transaction(204.11, "Contract Termination", "01 January 2024"));
169+
170+
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanId);
171+
assertEquals(BigDecimal.valueOf(4.11).stripTrailingZeros(),
172+
loanDetails.getSummary().getInterestCharged().stripTrailingZeros());
173+
174+
globalConfigurationHelper.updateGlobalConfiguration(GlobalConfigurationConstants.INTEREST_CHARGED_FROM_DATE_SAME_AS_DISBURSAL_DATE,
175+
new PutGlobalConfigurationsRequest().enabled(false));
176+
});
177+
}
178+
121179
}

integration-tests/src/test/java/org/apache/fineract/integrationtests/common/FineractClientHelper.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
*/
1919
package org.apache.fineract.integrationtests.common;
2020

21+
import java.time.Duration;
2122
import java.util.function.Consumer;
2223
import java.util.function.Function;
2324
import okhttp3.logging.HttpLoggingInterceptor;
@@ -43,7 +44,7 @@ public static FineractClient createNewFineractClient(String username, String pas
4344
String url = System.getProperty("fineract.it.url", buildURI());
4445
// insecure(true) should *ONLY* ever be used for https://localhost:8443, NOT in real clients!!
4546
FineractClient.Builder builder = FineractClient.builder().insecure(true).baseURL(url).tenant(ConfigProperties.Backend.TENANT)
46-
.basicAuth(username, password).logging(HttpLoggingInterceptor.Level.NONE);
47+
.basicAuth(username, password).logging(HttpLoggingInterceptor.Level.NONE).readTimeout(Duration.ofSeconds(0));
4748
customizer.accept(builder);
4849
return builder.build();
4950
}

0 commit comments

Comments
 (0)