This is an automated email from the ASF dual-hosted git repository.

adamsaghy pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/fineract.git


The following commit(s) were added to refs/heads/develop by this push:
     new 4f1184f20 FINERACT-2148: Stop recalculating interest if the loan is 
charged-off
4f1184f20 is described below

commit 4f1184f20b5378011812c49812dcf2f644f795ce
Author: Jose Alberto Hernandez <[email protected]>
AuthorDate: Sun Dec 22 11:39:10 2024 -0500

    FINERACT-2148: Stop recalculating interest if the loan is charged-off
---
 .../api/LoanTransactionsApiResourceSwagger.java    |  8 ++
 .../portfolio/loanaccount/domain/Loan.java         |  8 +-
 .../loanaccount/service/LoanScheduleService.java   |  4 +-
 .../LoanChargeWritePlatformServiceImpl.java        |  2 +-
 ...PaymentAllocationLoanRepaymentScheduleTest.java | 88 ++++++++++++++++++++++
 .../common/loans/LoanTransactionHelper.java        |  5 ++
 6 files changed, 111 insertions(+), 4 deletions(-)

diff --git 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanTransactionsApiResourceSwagger.java
 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanTransactionsApiResourceSwagger.java
index 87fe5f81e..a938039a8 100644
--- 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanTransactionsApiResourceSwagger.java
+++ 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanTransactionsApiResourceSwagger.java
@@ -79,6 +79,14 @@ final class LoanTransactionsApiResourceSwagger {
 
         @Schema(example = "200.000000")
         public Double amount;
+        @Schema(example = "100.000000")
+        public Double principalPortion;
+        @Schema(example = "80.000000")
+        public Double interestPortion;
+        @Schema(example = "20.000000")
+        public Double feeChargesPortion;
+        @Schema(example = "20.000000")
+        public Double penaltyChargesPortion;
 
         public GetLoanCurrency currency;
     }
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 e50e6822e..3a91044ea 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
@@ -2722,7 +2722,7 @@ public class Loan extends 
AbstractAuditableWithUTCDateTimeCustom<Long> {
     public OutstandingAmountsDTO fetchPrepaymentDetail(final 
ScheduleGeneratorDTO scheduleGeneratorDTO, final LocalDate onDate) {
         OutstandingAmountsDTO outstandingAmounts;
 
-        if (this.loanRepaymentScheduleDetail.isInterestRecalculationEnabled()) 
{
+        if (this.loanRepaymentScheduleDetail.isInterestRecalculationEnabled() 
&& !isChargeOffOnDate(onDate)) {
             final MathContext mc = MoneyHelper.getMathContext();
 
             final InterestMethod interestMethod = 
this.loanRepaymentScheduleDetail.getInterestMethod();
@@ -3571,4 +3571,10 @@ public class Loan extends 
AbstractAuditableWithUTCDateTimeCustom<Long> {
     public boolean isProgressiveSchedule() {
         return getLoanProductRelatedDetail().getLoanScheduleType() == 
PROGRESSIVE;
     }
+
+    public boolean isChargeOffOnDate(final LocalDate onDate) {
+        final LoanTransaction chargeOffTransaction = 
findChargedOffTransaction();
+        return (chargeOffTransaction == null) ? false : 
(chargeOffTransaction.getDateOf().compareTo(onDate) <= 0);
+    }
+
 }
diff --git 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanScheduleService.java
 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanScheduleService.java
index a5bf24e66..86cb820a7 100644
--- 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanScheduleService.java
+++ 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanScheduleService.java
@@ -54,7 +54,7 @@ public class LoanScheduleService {
     }
 
     public void recalculateScheduleFromLastTransaction(final Loan loan, final 
ScheduleGeneratorDTO generatorDTO) {
-        if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
+        if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled() && 
!loan.isChargedOff()) {
             regenerateRepaymentScheduleWithInterestRecalculation(loan, 
generatorDTO);
         } else {
             regenerateRepaymentSchedule(loan, generatorDTO);
@@ -73,7 +73,7 @@ public class LoanScheduleService {
          * loanTransaction.getTransactionDate().isAfter(recalculateFrom)) { 
recalculateFrom =
          * loanTransaction.getTransactionDate(); } } 
generatorDTO.setRecalculateFrom(recalculateFrom);
          */
-        if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
+        if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled() && 
!loan.isChargedOff()) {
             regenerateRepaymentScheduleWithInterestRecalculation(loan, 
generatorDTO);
         } else {
             regenerateRepaymentSchedule(loan, generatorDTO);
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanChargeWritePlatformServiceImpl.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanChargeWritePlatformServiceImpl.java
index 7751d18aa..d0e8495bc 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanChargeWritePlatformServiceImpl.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanChargeWritePlatformServiceImpl.java
@@ -1154,7 +1154,7 @@ public class LoanChargeWritePlatformServiceImpl 
implements LoanChargeWritePlatfo
     }
 
     public Loan runScheduleRecalculation(Loan loan, final LocalDate 
recalculateFrom) {
-        if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
+        if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled() && 
!loan.isChargedOff()) {
             final List<Long> existingTransactionIds = 
loan.findExistingTransactionIds();
             ScheduleGeneratorDTO generatorDTO = 
this.loanUtilService.buildScheduleGeneratorDTO(loan, recalculateFrom);
             ChangedTransactionDetail changedTransactionDetail = 
loanScheduleService
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 03002d86b..671eed4af 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
@@ -52,6 +52,7 @@ import 
org.apache.fineract.client.models.GetLoansLoanIdLoanChargeData;
 import org.apache.fineract.client.models.GetLoansLoanIdRepaymentPeriod;
 import org.apache.fineract.client.models.GetLoansLoanIdResponse;
 import org.apache.fineract.client.models.GetLoansLoanIdTransactions;
+import 
org.apache.fineract.client.models.GetLoansLoanIdTransactionsTemplateResponse;
 import org.apache.fineract.client.models.LoanProduct;
 import org.apache.fineract.client.models.PaymentAllocationOrder;
 import org.apache.fineract.client.models.PostClientsResponse;
@@ -82,6 +83,7 @@ import 
org.apache.fineract.integrationtests.common.charges.ChargesHelper;
 import 
org.apache.fineract.integrationtests.common.loans.LoanProductTestBuilder;
 import org.apache.fineract.integrationtests.common.loans.LoanTransactionHelper;
 import org.apache.fineract.integrationtests.common.organisation.StaffHelper;
+import org.apache.fineract.integrationtests.common.system.CodeHelper;
 import 
org.apache.fineract.integrationtests.useradministration.roles.RolesHelper;
 import 
org.apache.fineract.integrationtests.useradministration.users.UserHelper;
 import 
org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.AdvancedPaymentScheduleTransactionProcessor;
@@ -5802,6 +5804,92 @@ public class 
AdvancedPaymentAllocationLoanRepaymentScheduleTest extends BaseLoan
         });
     }
 
+    // UC153: Validate Stop recalculating interest if the loan is charged-off
+    // 1. Create a Loan product with Adv. Pment. Alloc. and with Interest 
Recalculation enabled
+    // 2. Submit Loan, approve and Disburse
+    // 3. Apply Charge-Off
+    // 4. Prepay the loan to get the same interest amount after Charge-Off
+    @Test
+    public void uc153() {
+        AtomicLong createdLoanId = new AtomicLong();
+        runAt("01 January 2024", () -> {
+            String operationDate = "01 January 2024";
+            Long clientId = client.getClientId();
+            BigDecimal interestRatePerPeriod = BigDecimal.valueOf(7.0);
+
+            final Integer rescheduleStrategyMethod = 4; // Adjust last, unpaid 
period
+            PostLoanProductsRequest loanProduct = 
createOnePeriod30DaysPeriodicAccrualProductWithAdvancedPaymentAllocationAndInterestRecalculation(
+                    (double) 80.0, rescheduleStrategyMethod);
+            final PostLoanProductsResponse loanProductResponse = 
loanProductHelper.createLoanProduct(loanProduct);
+            assertNotNull(loanProductResponse);
+
+            PostLoansRequest applicationRequest = applyLoanRequest(clientId, 
loanProductResponse.getResourceId(), operationDate, 100.0, 6);
+
+            applicationRequest = applicationRequest.numberOfRepayments(6)//
+                    .loanTermFrequency(6)//
+                    .loanTermFrequencyType(2)//
+                    .interestRatePerPeriod(interestRatePerPeriod)//
+                    .interestCalculationPeriodType(DAYS)//
+                    
.transactionProcessingStrategyCode(LoanProductTestBuilder.ADVANCED_PAYMENT_ALLOCATION_STRATEGY)//
+                    .repaymentEvery(1)//
+                    .repaymentFrequencyType(2)//
+                    .maxOutstandingLoanBalance(BigDecimal.valueOf(10000.0))//
+            ;//
+
+            PostLoansResponse loanResponse = 
loanTransactionHelper.applyLoan(applicationRequest);
+
+            loanTransactionHelper.approveLoan(loanResponse.getLoanId(), new 
PostLoansLoanIdRequest()//
+                    .approvedLoanAmount(BigDecimal.valueOf(100))//
+                    
.approvedOnDate(operationDate).dateFormat(DATETIME_PATTERN).locale("en"));//
+
+            loanTransactionHelper.disburseLoan(loanResponse.getLoanId(), new 
PostLoansLoanIdRequest()//
+                    .transactionAmount(BigDecimal.valueOf(100.0))//
+                    
.actualDisbursementDate(operationDate).dateFormat(DATETIME_PATTERN).locale("en"));//
+
+            GetLoansLoanIdResponse loanDetails = 
loanTransactionHelper.getLoanDetails(loanResponse.getLoanId());
+            validateLoanSummaryBalances(loanDetails, 125.67, 0.0, 100.0, 0.0, 
null);
+            createdLoanId.set(loanResponse.getLoanId());
+        });
+
+        runAt("01 February 2024", () -> {
+
+            loanTransactionHelper.makeLoanRepayment(createdLoanId.get(), new 
PostLoansLoanIdTransactionsRequest()
+                    .transactionDate("01 February 2024").dateFormat("dd MMMM 
yyyy").locale("en").transactionAmount(21.0));
+            GetLoansLoanIdResponse loanDetails = 
loanTransactionHelper.getLoanDetails(createdLoanId.get());
+            validateLoanSummaryBalances(loanDetails, 104.67, 21.0, 86.11, 
13.89, null);
+        });
+
+        runAt("01 March 2024", () -> {
+            String randomText = Utils.randomStringGenerator("en", 5) + 
Utils.randomNumberGenerator(6)
+                    + Utils.randomStringGenerator("is", 5);
+            String transactionExternalId = UUID.randomUUID().toString();
+            Integer chargeOffReasonId = 
CodeHelper.createChargeOffCodeValue(requestSpec, responseSpec, randomText, 1);
+
+            loanTransactionHelper.chargeOffLoan(createdLoanId.get(),
+                    new 
PostLoansLoanIdTransactionsRequest().transactionDate("01 March 
2024").locale("en").dateFormat("dd MMMM yyyy")
+                            
.externalId(transactionExternalId).chargeOffReasonId((long) chargeOffReasonId));
+
+            // Loan Prepayment (before) Charge-Off transaction - With Interest 
Recalculation
+            GetLoansLoanIdTransactionsTemplateResponse transactionBefore = 
loanTransactionHelper
+                    .retrieveTransactionTemplate(createdLoanId.get(), 
"prepayLoan", "dd MMMM yyyy", "15 February 2024", "en");
+            assertEquals(88.88, transactionBefore.getAmount());
+            assertEquals(86.11, transactionBefore.getPrincipalPortion());
+            assertEquals(2.77, transactionBefore.getInterestPortion());
+            assertEquals(0.00, transactionBefore.getFeeChargesPortion());
+            assertEquals(0.00, transactionBefore.getPenaltyChargesPortion());
+
+            // Loan Prepayment (after) Charge-Off transaction - WithOut 
Interest Recalculation
+            GetLoansLoanIdTransactionsTemplateResponse transactionAfter = 
loanTransactionHelper
+                    .retrieveTransactionTemplate(createdLoanId.get(), 
"prepayLoan", "dd MMMM yyyy", "01 March 2024", "en");
+            assertEquals(104.67, transactionAfter.getAmount());
+            assertEquals(86.11, transactionAfter.getPrincipalPortion());
+            assertEquals(18.56, transactionAfter.getInterestPortion());
+            assertEquals(0.00, transactionAfter.getFeeChargesPortion());
+            assertEquals(0.00, transactionAfter.getPenaltyChargesPortion());
+        });
+
+    }
+
     private Long 
applyAndApproveLoanProgressiveAdvancedPaymentAllocationStrategyMonthlyRepayments(Long
 clientId, Long loanProductId,
             Integer numberOfRepayments, String loanDisbursementDate, double 
amount) {
         LOG.info("------------------------------APPLY AND APPROVE LOAN 
---------------------------------------");
diff --git 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanTransactionHelper.java
 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanTransactionHelper.java
index 174886399..8785ec626 100644
--- 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanTransactionHelper.java
+++ 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanTransactionHelper.java
@@ -1920,6 +1920,11 @@ public class LoanTransactionHelper extends 
IntegrationTest {
         return chargebackPayload;
     }
 
+    public GetLoansLoanIdTransactionsTemplateResponse 
retrieveTransactionTemplate(Long loanId, String command, String dateFormat,
+            String transactionDate, String locale) {
+        return 
ok(fineract().loanTransactions.retrieveTransactionTemplate(loanId, command, 
dateFormat, transactionDate, locale));
+    }
+
     public GetLoansLoanIdTransactionsTemplateResponse 
retrieveTransactionTemplate(String loanExternalIdStr, String command,
             String dateFormat, String transactionDate, String locale) {
         return ok(

Reply via email to