From c9dc87f62ab0935c06ebbd9d1c4d6f86609647ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Soma=20S=C3=B6r=C3=B6s?= Date: Thu, 14 Nov 2024 16:48:27 +0100 Subject: [PATCH] FINERACT-1981: Fix Charge Creation After Maturity Date on Interest Bearing Progressive Loan --- .../portfolio/loanaccount/domain/Loan.java | 3 +- ...rgeRepaymentScheduleProcessingWrapper.java | 2 +- .../LoanInterestRecalculationCOBTest.java | 124 ++++++++++++++++++ 3 files changed, 127 insertions(+), 2 deletions(-) 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 4ba2c3b2445..d607f2764c2 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 @@ -2446,7 +2446,8 @@ public ChangedTransactionDetail handleRepaymentOrRecoveryOrWaiverTransaction(fin } } if (reprocess) { - if (this.repaymentScheduleDetail().isInterestRecalculationEnabled()) { + if (this.repaymentScheduleDetail().isInterestRecalculationEnabled() + && !getLoanProductRelatedDetail().getLoanScheduleType().equals(LoanScheduleType.PROGRESSIVE)) { regenerateRepaymentScheduleWithInterestRecalculation(scheduleGeneratorDTO); } final List allNonContraTransactionsPostDisbursement = retrieveListOfTransactionsForReprocessing(); diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/SingleLoanChargeRepaymentScheduleProcessingWrapper.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/SingleLoanChargeRepaymentScheduleProcessingWrapper.java index a3237dd7e4e..e31f6c11358 100644 --- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/SingleLoanChargeRepaymentScheduleProcessingWrapper.java +++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/SingleLoanChargeRepaymentScheduleProcessingWrapper.java @@ -46,7 +46,7 @@ public void reprocess(final MonetaryCurrency currency, final LocalDate disbursem totalPrincipal = totalPrincipal.plus(installment.getPrincipal(currency)); } LoanChargePaidBy accrualBy = null; - if (!loan.isInterestBearing() && loanCharge.isSpecifiedDueDate()) { // TODO: why only if not interest bearing + if (loanCharge.isSpecifiedDueDate()) { LoanRepaymentScheduleInstallment addedPeriod = addChargeOnlyRepaymentInstallmentIfRequired(loanCharge, installments); if (addedPeriod != null) { addedPeriod.updateObligationsMet(currency, disbursementDate); diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanInterestRecalculationCOBTest.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanInterestRecalculationCOBTest.java index 3378f7b5c95..63395b1b927 100644 --- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanInterestRecalculationCOBTest.java +++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanInterestRecalculationCOBTest.java @@ -38,8 +38,10 @@ import java.util.stream.Stream; import lombok.extern.slf4j.Slf4j; import org.apache.fineract.client.models.GetLoansLoanIdResponse; +import org.apache.fineract.client.models.PostChargesResponse; import org.apache.fineract.client.models.PostClientsResponse; import org.apache.fineract.client.models.PostLoanProductsResponse; +import org.apache.fineract.client.models.PostLoansLoanIdChargesResponse; import org.apache.fineract.client.models.PostLoansLoanIdTransactionsResponse; import org.apache.fineract.integrationtests.common.BusinessStepHelper; import org.apache.fineract.integrationtests.common.ClientHelper; @@ -656,6 +658,128 @@ public void verifyEarlyLateRepaymentOnProgressiveLoanNextInstallmentAllocationRe }); } + @Test + public void verifyChargeCreationAfterMaturityDateOnInterestBearingProgressiveLoan() { + AtomicReference loanIdRef = new AtomicReference<>(); + runAt("1 January 2024", () -> { + PostLoanProductsResponse loanProduct = loanProductHelper.createLoanProduct(create4IProgressive() // + .recalculationRestFrequencyType(RecalculationRestFrequencyType.DAILY) // + .currencyCode("USD")); + + Long loanId = applyAndApproveProgressiveLoan(client.getClientId(), loanProduct.getResourceId(), "1 January 2024", 100.0, 7.0, 6, + null); + loanIdRef.set(loanId); + + disburseLoan(loanId, BigDecimal.valueOf(100), "1 January 2024"); + + GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanId); + logLoanDetails(loanDetails); + + validateFullyUnpaidRepaymentPeriod(loanDetails, 1, "01 February 2024", 16.43, 0.0, 0.0, 0.58); + validateFullyUnpaidRepaymentPeriod(loanDetails, 2, "01 March 2024", 16.52, 0.0, 0.0, 0.49); + validateFullyUnpaidRepaymentPeriod(loanDetails, 3, "01 April 2024", 16.62, 0.0, 0.0, 0.39); + validateFullyUnpaidRepaymentPeriod(loanDetails, 4, "01 May 2024", 16.72, 0.0, 0.0, 0.29); + validateFullyUnpaidRepaymentPeriod(loanDetails, 5, "01 June 2024", 16.81, 0.0, 0.0, 0.20); + validateFullyUnpaidRepaymentPeriod(loanDetails, 6, "01 July 2024", 16.90, 0.0, 0.0, 0.10); + + }); + runAt("10 July 2024", () -> { + Long loanId = loanIdRef.get(); + + inlineLoanCOBHelper.executeInlineCOB(List.of(loanId)); + + // create charge + PostChargesResponse chargeResult = createCharge(10.0); + Assertions.assertNotNull(chargeResult); + Long chargeId = chargeResult.getResourceId(); + Assertions.assertNotNull(chargeId); + + // add charge after maturity + PostLoansLoanIdChargesResponse loanChargeResult = addLoanCharge(loanId, chargeId, "15 July 2024", 10.0); + Assertions.assertNotNull(loanChargeResult.getResourceId()); + + // verify N+1 installment in schedule + GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanId); + logLoanDetails(loanDetails); + + validateFullyUnpaidRepaymentPeriod(loanDetails, 1, "01 February 2024", 16.43, 0.0, 0.0, 0.58); + validateFullyUnpaidRepaymentPeriod(loanDetails, 2, "01 March 2024", 16.43, 0.0, 0.0, 0.58); + validateFullyUnpaidRepaymentPeriod(loanDetails, 3, "01 April 2024", 16.43, 0.0, 0.0, 0.58); + validateFullyUnpaidRepaymentPeriod(loanDetails, 4, "01 May 2024", 16.43, 0.0, 0.0, 0.58); + validateFullyUnpaidRepaymentPeriod(loanDetails, 5, "01 June 2024", 16.43, 0.0, 0.0, 0.58); + validateFullyUnpaidRepaymentPeriod(loanDetails, 6, "01 July 2024", 17.85, 0.0, 0.0, 0.58); + validateFullyUnpaidRepaymentPeriod(loanDetails, 7, "15 July 2024", 0.0, 10.0, 0.0, 0.0); + + Assertions.assertNotNull(loanDetails.getStatus()); + Assertions.assertEquals(300, loanDetails.getStatus().getId()); + }); + runAt("15 July 2024", () -> { + Long loanId = loanIdRef.get(); + + loanTransactionHelper.makeLoanRepayment("15 July 2024", 113.48F, loanId.intValue()); + GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanId); + logLoanDetails(loanDetails); + + validateFullyPaidRepaymentPeriod(loanDetails, 1, "01 February 2024", 16.43, 0.0, 0.0, 0.58, 17.01); + validateFullyPaidRepaymentPeriod(loanDetails, 2, "01 March 2024", 16.43, 0.0, 0.0, 0.58, 17.01); + validateFullyPaidRepaymentPeriod(loanDetails, 3, "01 April 2024", 16.43, 0.0, 0.0, 0.58, 17.01); + validateFullyPaidRepaymentPeriod(loanDetails, 4, "01 May 2024", 16.43, 0.0, 0.0, 0.58, 17.01); + validateFullyPaidRepaymentPeriod(loanDetails, 5, "01 June 2024", 16.43, 0.0, 0.0, 0.58, 17.01); + validateFullyPaidRepaymentPeriod(loanDetails, 6, "01 July 2024", 17.85, 0.0, 0.0, 0.58, 18.43); + validateFullyPaidRepaymentPeriod(loanDetails, 7, "15 July 2024", 0.0, 10.0, 0.0, 0.0); + + Assertions.assertNotNull(loanDetails.getStatus()); + Assertions.assertEquals(600, loanDetails.getStatus().getId()); + }); + runAt("16 July 2024", () -> { + Long loanId = loanIdRef.get(); + + // create charge + PostChargesResponse chargeResult = createCharge(10.0); + Assertions.assertNotNull(chargeResult); + Long chargeId = chargeResult.getResourceId(); + Assertions.assertNotNull(chargeId); + + // add charge after maturity + PostLoansLoanIdChargesResponse loanChargeResult = addLoanCharge(loanId, chargeId, "20 July 2024", 15.0); + Assertions.assertNotNull(loanChargeResult.getResourceId()); + + // verify N+1 installment in schedule + GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanId); + logLoanDetails(loanDetails); + + validateFullyPaidRepaymentPeriod(loanDetails, 1, "01 February 2024", 16.43, 0.0, 0.0, 0.58, 17.01); + validateFullyPaidRepaymentPeriod(loanDetails, 2, "01 March 2024", 16.43, 0.0, 0.0, 0.58, 17.01); + validateFullyPaidRepaymentPeriod(loanDetails, 3, "01 April 2024", 16.43, 0.0, 0.0, 0.58, 17.01); + validateFullyPaidRepaymentPeriod(loanDetails, 4, "01 May 2024", 16.43, 0.0, 0.0, 0.58, 17.01); + validateFullyPaidRepaymentPeriod(loanDetails, 5, "01 June 2024", 16.43, 0.0, 0.0, 0.58, 17.01); + validateFullyPaidRepaymentPeriod(loanDetails, 6, "01 July 2024", 17.85, 0.0, 0.0, 0.58, 18.43); + validateRepaymentPeriod(loanDetails, 7, LocalDate.of(2024, 7, 20), 0.0, 0.0, 0.0, 25.0, 10.0, 15.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0); + + Assertions.assertNotNull(loanDetails.getStatus()); + Assertions.assertEquals(300, loanDetails.getStatus().getId()); + }); + runAt("20 July 2024", () -> { + Long loanId = loanIdRef.get(); + + loanTransactionHelper.makeLoanRepayment("20 July 2024", 15.0F, loanId.intValue()); + GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanId); + logLoanDetails(loanDetails); + + validateFullyPaidRepaymentPeriod(loanDetails, 1, "01 February 2024", 16.43, 0.0, 0.0, 0.58, 17.01); + validateFullyPaidRepaymentPeriod(loanDetails, 2, "01 March 2024", 16.43, 0.0, 0.0, 0.58, 17.01); + validateFullyPaidRepaymentPeriod(loanDetails, 3, "01 April 2024", 16.43, 0.0, 0.0, 0.58, 17.01); + validateFullyPaidRepaymentPeriod(loanDetails, 4, "01 May 2024", 16.43, 0.0, 0.0, 0.58, 17.01); + validateFullyPaidRepaymentPeriod(loanDetails, 5, "01 June 2024", 16.43, 0.0, 0.0, 0.58, 17.01); + validateFullyPaidRepaymentPeriod(loanDetails, 6, "01 July 2024", 17.85, 0.0, 0.0, 0.58, 18.43); + validateFullyPaidRepaymentPeriod(loanDetails, 7, "20 July 2024", 0.0, 25.0, 0.0, 0.0); + + Assertions.assertNotNull(loanDetails.getStatus()); + Assertions.assertEquals(600, loanDetails.getStatus().getId()); + }); + } + @Test public void verifyEarlyLateRepaymentOnProgressiveLoanNextInstallmentAllocationRepayEmi() { AtomicReference loanIdRef = new AtomicReference<>();