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

commit 9ab08ec428d54c4a8b7520577df5f95de3f29caf
Author: Soma Sörös <[email protected]>
AuthorDate: Mon May 12 16:58:13 2025 +0200

    FINERACT-2280: Interest pause breaks repayment schedule while loan is added 
with interest rate zero
---
 .../InterestPauseWritePlatformServiceImpl.java     |  5 ++
 .../integrationtests/LoanInterestPauseApiTest.java | 90 ++++++++++++++++++----
 .../common/loans/LoanTransactionHelper.java        |  9 +++
 3 files changed, 87 insertions(+), 17 deletions(-)

diff --git 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/interestpauses/service/InterestPauseWritePlatformServiceImpl.java
 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/interestpauses/service/InterestPauseWritePlatformServiceImpl.java
index 18e84c3b0c..1524ae937d 100644
--- 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/interestpauses/service/InterestPauseWritePlatformServiceImpl.java
+++ 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/interestpauses/service/InterestPauseWritePlatformServiceImpl.java
@@ -184,6 +184,11 @@ public class InterestPauseWritePlatformServiceImpl 
implements InterestPauseWrite
                     "Interest pause is only supported for progressive loans.");
         }
 
+        if (!loan.isInterestBearing()) {
+            throw new 
GeneralPlatformDomainRuleException("loan.must.be.interest.bearing",
+                    "Interest pause is only supported for interest bearing 
loans.");
+        }
+
         if 
(!loan.getLoanRepaymentScheduleDetail().isInterestRecalculationEnabled()) {
             throw new 
GeneralPlatformDomainRuleException("loan.must.have.recalculate.interest.enabled",
                     "Interest pause is only supported for loans with 
recalculate interest enabled.");
diff --git 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanInterestPauseApiTest.java
 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanInterestPauseApiTest.java
index c85926ca32..e0cd6de3d5 100644
--- 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanInterestPauseApiTest.java
+++ 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanInterestPauseApiTest.java
@@ -27,6 +27,7 @@ import java.math.BigDecimal;
 import java.util.UUID;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.fineract.client.models.AdvancedPaymentData;
+import org.apache.fineract.client.models.CommandProcessingResult;
 import org.apache.fineract.client.models.PostLoansLoanIdRequest;
 import org.apache.fineract.client.models.PostLoansLoanIdTransactionsResponse;
 import org.apache.fineract.integrationtests.common.ClientHelper;
@@ -35,23 +36,17 @@ import 
org.apache.fineract.integrationtests.common.accounting.Account;
 import org.apache.fineract.integrationtests.common.accounting.AccountHelper;
 import 
org.apache.fineract.integrationtests.common.loans.LoanApplicationTestBuilder;
 import 
org.apache.fineract.integrationtests.common.loans.LoanProductTestBuilder;
-import 
org.apache.fineract.integrationtests.common.loans.LoanTestLifecycleExtension;
 import org.apache.fineract.integrationtests.common.loans.LoanTransactionHelper;
 import 
org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleProcessingType;
 import 
org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleType;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.ExtendWith;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+import retrofit2.Response;
 
 @Slf4j
-@ExtendWith(LoanTestLifecycleExtension.class)
 public class LoanInterestPauseApiTest extends BaseLoanIntegrationTest {
 
-    private static final Logger LOG = 
LoggerFactory.getLogger(LoanInterestPauseApiTest.class);
-
     private static RequestSpecification REQUEST_SPEC;
     private static ResponseSpecification RESPONSE_SPEC;
     private static ResponseSpecification RESPONSE_SPEC_403;
@@ -65,7 +60,7 @@ public class LoanInterestPauseApiTest extends 
BaseLoanIntegrationTest {
     private static final Integer nonExistLoanId = 99999;
     private static String externalId;
     private static final String nonExistExternalId = 
"7c4fb86f-a778-4d02-b7a8-ec3ec98941fa";
-    private Integer clientId;
+    private Long clientId;
     private Integer loanProductId;
     private Integer loanId;
     private final String loanPrincipalAmount = "10000.00";
@@ -614,20 +609,81 @@ public class LoanInterestPauseApiTest extends 
BaseLoanIntegrationTest {
         }
     }
 
+    Long loan;
+
+    @Test
+    public void testInterestPauseOnZeroInterestRate() {
+        runAt("1 September 2019", () -> {
+            Long loanProductId = 
loanProductHelper.createLoanProduct(create4IProgressive()).getResourceId();
+            loan = applyAndApproveProgressiveLoan(clientId, loanProductId, "1 
September 2019", 1200.0, 0.0, 4, null);
+            disburseLoan(loan, BigDecimal.valueOf(1200.0), "1 September 2019");
+        });
+        runAt("1 October 2019", () -> {
+            loanTransactionHelper.makeLoanRepayment(loan, "Repayment", "1 
October 2019", 300.0);
+        });
+        runAt("1 November 2019", () -> {
+            loanTransactionHelper.makeLoanRepayment(loan, "Repayment", "1 
November 2019", 300.0);
+        });
+        runAt("1 December 2019", () -> {
+            loanTransactionHelper.makeLoanRepayment(loan, "Repayment", "1 
December 2019", 300.0);
+            verifyTransactions(loan, //
+                    transaction(1200.0, "Disbursement", "01 September 2019", 
1200.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0), //
+                    transaction(300.0, "Repayment", "01 October 2019", 900.0, 
300.0, 0.0, 0.0, 0.0, 0.0, 0.0), //
+                    transaction(300.0, "Repayment", "01 November 2019", 600.0, 
300.0, 0.0, 0.0, 0.0, 0.0, 0.0), //
+                    transaction(300.0, "Repayment", "01 December 2019", 300.0, 
300.0, 0.0, 0.0, 0.0, 0.0, 0.0) //
+            );
+            Response<CommandProcessingResult> response = 
loanTransactionHelper.createInterestPause(loan, "1 October 2019",
+                    "15 December 2019");
+            Assertions.assertEquals(403, response.code());
+            verifyTransactions(loan, //
+                    transaction(1200.0, "Disbursement", "01 September 2019", 
1200.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0), //
+                    transaction(300.0, "Repayment", "01 October 2019", 900.0, 
300.0, 0.0, 0.0, 0.0, 0.0, 0.0), //
+                    transaction(300.0, "Repayment", "01 November 2019", 600.0, 
300.0, 0.0, 0.0, 0.0, 0.0, 0.0), //
+                    transaction(300.0, "Repayment", "01 December 2019", 300.0, 
300.0, 0.0, 0.0, 0.0, 0.0, 0.0) //
+            );
+        });
+    }
+
+    @Test
+    public void testInterestPauseOnZeroInterestRateRightAfterDisbursement() {
+        runAt("1 September 2019", () -> {
+            Long loanProductId = 
loanProductHelper.createLoanProduct(create4IProgressive()).getResourceId();
+            loan = applyAndApproveProgressiveLoan(clientId, loanProductId, "1 
September 2019", 1200.0, 0.0, 4, null);
+            disburseLoan(loan, BigDecimal.valueOf(1200.0), "1 September 2019");
+            verifyRepaymentSchedule(loan, //
+                    installment(1200.0, null, "01 September 2019"), //
+                    installment(300.0, 0.0, 300.0, false, "01 October 2019"), 
//
+                    installment(300.0, 0.0, 300.0, false, "01 November 2019"), 
//
+                    installment(300.0, 0.0, 300.0, false, "01 December 2019"), 
//
+                    installment(300.0, 0.0, 300.0, false, "01 January 2020") //
+            );
+            Response<CommandProcessingResult> response = 
loanTransactionHelper.createInterestPause(loan, "1 October 2019",
+                    "15 December 2019");
+            Assertions.assertEquals(403, response.code());
+            verifyRepaymentSchedule(loan, //
+                    installment(1200.0, null, "01 September 2019"), //
+                    installment(300.0, 0.0, 300.0, false, "01 October 2019"), 
//
+                    installment(300.0, 0.0, 300.0, false, "01 November 2019"), 
//
+                    installment(300.0, 0.0, 300.0, false, "01 December 2019"), 
//
+                    installment(300.0, 0.0, 300.0, false, "01 January 2020") //
+            );
+        });
+    }
+
     /**
      * create a new client
      **/
     private void createClientEntity() {
-        this.clientId = ClientHelper.createClient(REQUEST_SPEC, RESPONSE_SPEC);
-
-        ClientHelper.verifyClientCreatedOnServer(REQUEST_SPEC, RESPONSE_SPEC, 
clientId);
+        this.clientId = 
ClientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getResourceId();
+        Assertions.assertNotNull(clientId);
+        ClientHelper.verifyClientCreatedOnServer(REQUEST_SPEC, RESPONSE_SPEC, 
clientId.intValue());
     }
 
     /**
      * create a new loan product
      **/
     private void createLoanProductEntity() {
-        LOG.info("---------------------------------CREATING LOAN 
PRODUCT------------------------------------------");
+        log.info("---------------------------------CREATING LOAN 
PRODUCT------------------------------------------");
 
         final String interestRecalculationCompoundingMethod = 
LoanProductTestBuilder.RECALCULATION_COMPOUNDING_METHOD_NONE;
         final String rescheduleStrategyMethod = 
LoanProductTestBuilder.RECALCULATION_STRATEGY_ADJUST_LAST_UNPAID_PERIOD;
@@ -651,14 +707,14 @@ public class LoanInterestPauseApiTest extends 
BaseLoanIntegrationTest {
                 .build();
 
         loanProductId = 
LOAN_TRANSACTION_HELPER.getLoanProductId(loanProductJSON);
-        LOG.info("Successfully created loan product  (ID:{}) ", loanProductId);
+        log.info("Successfully created loan product  (ID:{}) ", loanProductId);
     }
 
     /**
      * submit a new loan application, approve and disburse the loan
      **/
     private void createLoanEntity() {
-        LOG.info("---------------------------------NEW LOAN 
APPLICATION------------------------------------------");
+        log.info("---------------------------------NEW LOAN 
APPLICATION------------------------------------------");
 
         String loanApplicationJSON = new 
LoanApplicationTestBuilder().withPrincipal(loanPrincipalAmount)
                 
.withLoanTermFrequency(numberOfRepayments).withLoanTermFrequencyAsDays().withNumberOfRepayments(numberOfRepayments)
@@ -670,7 +726,7 @@ public class LoanInterestPauseApiTest extends 
BaseLoanIntegrationTest {
 
         loanId = LOAN_TRANSACTION_HELPER.getLoanId(loanApplicationJSON);
 
-        LOG.info("Sucessfully created loan (ID: {} )", loanId);
+        log.info("Sucessfully created loan (ID: {} )", loanId);
 
         approveLoanApplication();
         disburseLoan();
@@ -683,7 +739,7 @@ public class LoanInterestPauseApiTest extends 
BaseLoanIntegrationTest {
 
         if (loanId != null) {
             LOAN_TRANSACTION_HELPER.approveLoan(dateString, loanId);
-            LOG.info("Successfully approved loan (ID: {} )", loanId);
+            log.info("Successfully approved loan (ID: {} )", loanId);
         }
     }
 
@@ -695,7 +751,7 @@ public class LoanInterestPauseApiTest extends 
BaseLoanIntegrationTest {
         if (loanId != null) {
             LOAN_TRANSACTION_HELPER.disburseLoan(externalId, new 
PostLoansLoanIdRequest().actualDisbursementDate(dateString)
                     .transactionAmount(new 
BigDecimal(loanPrincipalAmount)).locale("en").dateFormat("dd MMMM yyyy"));
-            LOG.info("Successfully disbursed loan (ID: {} )", loanId);
+            log.info("Successfully disbursed loan (ID: {} )", loanId);
         }
     }
 }
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 6fcc0b9347..87e78d3b5b 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
@@ -45,6 +45,7 @@ import java.util.Locale;
 import java.util.Map;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.fineract.client.models.AdvancedPaymentData;
+import org.apache.fineract.client.models.CommandProcessingResult;
 import 
org.apache.fineract.client.models.DeleteLoansLoanIdChargesChargeIdResponse;
 import org.apache.fineract.client.models.DeleteLoansLoanIdResponse;
 import org.apache.fineract.client.models.DisbursementDetail;
@@ -66,6 +67,7 @@ import 
org.apache.fineract.client.models.GetLoansLoanIdTransactionsResponse;
 import 
org.apache.fineract.client.models.GetLoansLoanIdTransactionsTemplateResponse;
 import 
org.apache.fineract.client.models.GetLoansLoanIdTransactionsTransactionIdResponse;
 import org.apache.fineract.client.models.GetLoansResponse;
+import org.apache.fineract.client.models.InterestPauseRequestDto;
 import org.apache.fineract.client.models.PaymentTypeData;
 import 
org.apache.fineract.client.models.PostAddAndDeleteDisbursementDetailRequest;
 import org.apache.fineract.client.models.PostLoanProductsRequest;
@@ -104,6 +106,7 @@ import 
org.apache.fineract.integrationtests.common.accounting.Account;
 import org.apache.fineract.portfolio.delinquency.domain.DelinquencyAction;
 import org.apache.poi.hssf.usermodel.HSSFWorkbook;
 import org.apache.poi.ss.usermodel.Workbook;
+import retrofit2.Response;
 
 @Slf4j
 @SuppressWarnings({ "rawtypes", "unchecked" })
@@ -919,6 +922,12 @@ public class LoanTransactionHelper {
                 .transactionDate(transactionDate).dateFormat("dd MMMM 
yyyy").locale("en"));
     }
 
+    public Response<CommandProcessingResult> createInterestPause(Long loanId, 
String startDate, String endDate) {
+        log.info("Creating interest pause for Loan {} from {} to {}", loanId, 
startDate, endDate);
+        return 
Calls.executeU(FineractClientHelper.getFineractClient().loanInterestPauseApi.createInterestPause(loanId,
+                new 
InterestPauseRequestDto().startDate(startDate).endDate(endDate).dateFormat(DATE_FORMAT).locale("en")));
+    }
+
     // TODO: Rewrite to use fineract-client instead!
     // Example: 
org.apache.fineract.integrationtests.common.loans.LoanTransactionHelper.disburseLoan(java.lang.Long,
     // org.apache.fineract.client.models.PostLoansLoanIdRequest)

Reply via email to