From a72d3ff42313bc068f103e49346be439957e05b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Soma=20S=C3=B6r=C3=B6s?= Date: Tue, 25 Feb 2025 18:28:46 +0100 Subject: [PATCH] FINERACT-2187: Fix totalUnpaidPayableNotDueInterest not correct, when there is early payment on the loan --- .../ProgressiveLoanSummaryDataProvider.java | 13 ++- .../integrationtests/LoanSummaryTest.java | 94 +++++++++++++++++++ 2 files changed, 105 insertions(+), 2 deletions(-) create mode 100644 integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanSummaryTest.java diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/ProgressiveLoanSummaryDataProvider.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/ProgressiveLoanSummaryDataProvider.java index 767ae5c6e..d93d70204 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/ProgressiveLoanSummaryDataProvider.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/ProgressiveLoanSummaryDataProvider.java @@ -21,6 +21,7 @@ package org.apache.fineract.portfolio.loanaccount.service; import java.math.BigDecimal; import java.time.LocalDate; import java.util.Collection; +import java.util.Comparator; import java.util.List; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -104,10 +105,18 @@ public class ProgressiveLoanSummaryDataProvider extends CommonLoanSummaryDataPro replayedTransactions); } if (model != null) { - PeriodDueDetails dueAmounts = emiCalculator.getDueAmounts(model, loanRepaymentScheduleInstallment.getDueDate(), + LoanRepaymentScheduleInstallment nextUnpaidInAdvanceInstallment = loanRepaymentScheduleInstallment.isNotFullyPaidOff() + ? loanRepaymentScheduleInstallment + : loan.getRepaymentScheduleInstallments().stream().filter(LoanRepaymentScheduleInstallment::isNotFullyPaidOff) + .filter(i -> i.getInstallmentNumber() != null) + .min(Comparator.comparingInt(LoanRepaymentScheduleInstallment::getInstallmentNumber)).orElse(null); + if (nextUnpaidInAdvanceInstallment == null) { + return BigDecimal.ZERO; + } + PeriodDueDetails dueAmounts = emiCalculator.getDueAmounts(model, nextUnpaidInAdvanceInstallment.getDueDate(), businessDate); if (dueAmounts != null) { - BigDecimal interestPaid = loanRepaymentScheduleInstallment.getInterestPaid(); + BigDecimal interestPaid = nextUnpaidInAdvanceInstallment.getInterestPaid(); BigDecimal dueInterest = dueAmounts.getDueInterest().getAmount(); if (interestPaid == null) { return dueInterest; diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanSummaryTest.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanSummaryTest.java new file mode 100644 index 000000000..a410ab5c5 --- /dev/null +++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanSummaryTest.java @@ -0,0 +1,94 @@ +/** + * 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 java.math.BigDecimal; +import java.util.List; +import org.apache.fineract.client.models.GetLoansLoanIdResponse; +import org.apache.fineract.client.models.PostLoanProductsResponse; +import org.apache.fineract.client.models.PostLoansResponse; +import org.apache.fineract.integrationtests.common.ClientHelper; +import org.junit.Test; +import org.junit.jupiter.api.Assertions; + +public class LoanSummaryTest extends BaseLoanIntegrationTest { + + Long clientId = clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId(); + Long loanId; + + @Test + public void testUnpaidPayableNotDueInterestForProgressiveLoanInCaseOfEarlyRepayment() { + runAt("1 January 2024", () -> { + final PostLoanProductsResponse loanProductsResponse = loanProductHelper.createLoanProduct(create4IProgressive()); + PostLoansResponse postLoansResponse = loanTransactionHelper.applyLoan(applyLP2ProgressiveLoanRequest(clientId, + loanProductsResponse.getResourceId(), "01 January 2024", 1000.0, 9.99, 6, null)); + loanId = postLoansResponse.getLoanId(); + loanTransactionHelper.approveLoan(loanId, approveLoanRequest(1000.0, "01 January 2024")); + disburseLoan(loanId, BigDecimal.valueOf(250.0), "01 January 2024"); + }); + runAt("7 january 2024", () -> { + disburseLoan(loanId, BigDecimal.valueOf(350.0), "04 January 2024"); + disburseLoan(loanId, BigDecimal.valueOf(400.0), "05 January 2024"); + }); + runAt("15 January 2024", () -> { + inlineLoanCOBHelper.executeInlineCOB(List.of(loanId)); + GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanId); + Assertions.assertEquals(BigDecimal.valueOf(3.05), loanDetails.getSummary().getTotalUnpaidPayableNotDueInterest()); + loanTransactionHelper.makeLoanRepayment(loanId, "Repayment", "15 January 2024", 171.43); + loanDetails = loanTransactionHelper.getLoanDetails(loanId); + Assertions.assertEquals(0, loanDetails.getSummary().getTotalUnpaidPayableNotDueInterest().compareTo(BigDecimal.ZERO)); + }); + runAt("16 January 2024", () -> { + inlineLoanCOBHelper.executeInlineCOB(List.of(loanId)); + GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanId); + Assertions.assertEquals(BigDecimal.valueOf(0.22), loanDetails.getSummary().getTotalUnpaidPayableNotDueInterest()); + verifyTransactions(loanId, transaction(250.0, "Disbursement", "01 January 2024"), + transaction(350.0, "Disbursement", "04 January 2024"), transaction(400.0, "Disbursement", "05 January 2024"), + transaction(2.78, "Accrual", "14 January 2024"), transaction(171.43, "Repayment", "15 January 2024"), + transaction(0.27, "Accrual", "15 January 2024")); + }); + runAt("17 January 2024", () -> { + inlineLoanCOBHelper.executeInlineCOB(List.of(loanId)); + GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanId); + Assertions.assertEquals(BigDecimal.valueOf(0.44), loanDetails.getSummary().getTotalUnpaidPayableNotDueInterest()); + verifyTransactions(loanId, transaction(250.0, "Disbursement", "01 January 2024"), + transaction(350.0, "Disbursement", "04 January 2024"), transaction(400.0, "Disbursement", "05 January 2024"), + transaction(2.78, "Accrual", "14 January 2024"), transaction(171.43, "Repayment", "15 January 2024"), + transaction(0.27, "Accrual", "15 January 2024"), transaction(0.22, "Accrual", "16 January 2024")); + }); + runAt("18 January 2024", () -> { + inlineLoanCOBHelper.executeInlineCOB(List.of(loanId)); + GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanId); + Assertions.assertEquals(BigDecimal.valueOf(0.67), loanDetails.getSummary().getTotalUnpaidPayableNotDueInterest()); + verifyTransactions(loanId, transaction(250.0, "Disbursement", "01 January 2024"), + transaction(350.0, "Disbursement", "04 January 2024"), transaction(400.0, "Disbursement", "05 January 2024"), + transaction(2.78, "Accrual", "14 January 2024"), transaction(171.43, "Repayment", "15 January 2024"), + transaction(0.27, "Accrual", "15 January 2024"), transaction(0.22, "Accrual", "16 January 2024"), + transaction(0.22, "Accrual", "17 January 2024")); + }); + runAt("19 January 2024", () -> { + inlineLoanCOBHelper.executeInlineCOB(List.of(loanId)); + verifyTransactions(loanId, transaction(250.0, "Disbursement", "01 January 2024"), + transaction(350.0, "Disbursement", "04 January 2024"), transaction(400.0, "Disbursement", "05 January 2024"), + transaction(2.78, "Accrual", "14 January 2024"), transaction(171.43, "Repayment", "15 January 2024"), + transaction(0.27, "Accrual", "15 January 2024"), transaction(0.22, "Accrual", "16 January 2024"), + transaction(0.22, "Accrual", "17 January 2024"), transaction(0.23, "Accrual", "18 January 2024")); + }); + } +}