From 7cede0358143272bf77023a8e255d70082e5e4d9 Mon Sep 17 00:00:00 2001 From: Jose Alberto Hernandez Date: Sun, 26 Jan 2025 23:03:05 -0500 Subject: [PATCH] FINERACT-1971: Loan totalUnpaidPayableNotDueInterest field has wrong value after maturity --- .../portfolio/loanaccount/domain/Loan.java | 4 ++ .../ProgressiveLoanSummaryDataProvider.java | 3 + ...ntAllocationLoanRepaymentScheduleTest.java | 57 +++++++++++++++++++ 3 files changed, 64 insertions(+) diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java index 3a91044ea73..aac59acdfdc 100644 --- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java +++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java @@ -2666,6 +2666,10 @@ public LocalDate getMaturityDate() { return this.actualMaturityDate; } + public boolean isMatured(final LocalDate referenceDate) { + return (referenceDate.compareTo(this.actualMaturityDate) <= 0); + } + public ChangedTransactionDetail processTransactions() { final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = getTransactionProcessor(); final List allNonContraTransactionsPostDisbursement = retrieveListOfTransactionsForReprocessing(); 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 4eb588fb25d..418d011afc7 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 @@ -80,6 +80,9 @@ private LoanRepaymentScheduleInstallment getRelatedRepaymentScheduleInstallment( @Override public BigDecimal computeTotalUnpaidPayableNotDueInterestAmountOnActualPeriod(final Loan loan, final Collection periods, final LocalDate businessDate, final CurrencyData currency) { + if (loan.isMatured(businessDate)) { + return BigDecimal.ZERO; + } LoanRepaymentScheduleInstallment loanRepaymentScheduleInstallment = getRelatedRepaymentScheduleInstallment(loan, businessDate); if (loan.isInterestBearing() && loanRepaymentScheduleInstallment != null) { diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/AdvancedPaymentAllocationLoanRepaymentScheduleTest.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/AdvancedPaymentAllocationLoanRepaymentScheduleTest.java index 671eed4afd5..fb3b0e4f42e 100644 --- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/AdvancedPaymentAllocationLoanRepaymentScheduleTest.java +++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/AdvancedPaymentAllocationLoanRepaymentScheduleTest.java @@ -5890,6 +5890,63 @@ public void uc153() { } + // UC154: On the maturity date or after that all the outstanding interest is due + // 1. Submit Loan, approve and Disburse + // 2. Run the COB daily until maturity date + // 3. After maturity, totalUnpaidPayableNotDueInterest returns must be not a positive value. + @Test + public void uc154() { + final String operationDate = "23 December 2024"; + AtomicLong createdLoanId = new AtomicLong(); + runAt(operationDate, () -> { + Long clientId = clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId(); + PostLoanProductsRequest product = createOnePeriod30DaysLongNoInterestPeriodicAccrualProductWithAdvancedPaymentAllocation() + .interestRatePerPeriod(4.0).interestCalculationPeriodType(RepaymentFrequencyType.DAYS).interestRateFrequencyType(YEARS) + .daysInMonthType(DaysInMonthType.ACTUAL).daysInYearType(DaysInYearType.DAYS_360).numberOfRepayments(3)// + .repaymentEvery(1)// + .repaymentFrequencyType(1L)// + .allowPartialPeriodInterestCalcualtion(false)// + .multiDisburseLoan(false)// + .disallowExpectedDisbursements(null)// + .allowApprovedDisbursedAmountsOverApplied(null)// + .overAppliedCalculationType(null)// + .overAppliedNumber(null)// + .installmentAmountInMultiplesOf(null)// + ;// + PostLoanProductsResponse loanProductResponse = loanProductHelper.createLoanProduct(product); + PostLoansRequest applicationRequest = applyLoanRequest(clientId, loanProductResponse.getResourceId(), operationDate, 100.0, 3) + .interestRatePerPeriod(BigDecimal.valueOf(4.0)); + + applicationRequest = applicationRequest.interestCalculationPeriodType(DAYS) + .transactionProcessingStrategyCode(LoanProductTestBuilder.ADVANCED_PAYMENT_ALLOCATION_STRATEGY); + + PostLoansResponse loanResponse = loanTransactionHelper.applyLoan(applicationRequest); + createdLoanId.set(loanResponse.getLoanId()); + + loanTransactionHelper.approveLoan(loanResponse.getLoanId(), new PostLoansLoanIdRequest() + .approvedLoanAmount(BigDecimal.valueOf(100.0)).dateFormat(DATETIME_PATTERN).approvedOnDate(operationDate).locale("en")); + + loanTransactionHelper.disburseLoan(loanResponse.getLoanId(), new PostLoansLoanIdRequest().actualDisbursementDate(operationDate) + .dateFormat(DATETIME_PATTERN).transactionAmount(BigDecimal.valueOf(100.0)).locale("en")); + + // After Disbursement we are expecting amount in Zero + GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanResponse.getLoanId()); + assertEquals(BigDecimal.ZERO, loanDetails.getSummary().getTotalUnpaidPayableDueInterest().stripTrailingZeros()); + assertEquals(BigDecimal.ZERO, loanDetails.getSummary().getTotalUnpaidPayableNotDueInterest().stripTrailingZeros()); + }); + + runAt("23 March 2025", () -> { + executeInlineCOB(createdLoanId.get()); + GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(createdLoanId.get()); + // totalUnpaidPayableDueInterest → Total outstanding interest amount on all the periods that are before + assertEquals(BigDecimal.valueOf(loanDetails.getSummary().getInterestCharged()).stripTrailingZeros(), + loanDetails.getSummary().getTotalUnpaidPayableDueInterest().stripTrailingZeros()); + // Total outstanding interest amount on the current period, if the current period due date is after than the + // current date + assertEquals(BigDecimal.ZERO, loanDetails.getSummary().getTotalUnpaidPayableNotDueInterest().stripTrailingZeros()); + }); + } + private Long applyAndApproveLoanProgressiveAdvancedPaymentAllocationStrategyMonthlyRepayments(Long clientId, Long loanProductId, Integer numberOfRepayments, String loanDisbursementDate, double amount) { LOG.info("------------------------------APPLY AND APPROVE LOAN ---------------------------------------");