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 af37e07f3 FINERACT-2152: Interest pause during one period and between 
two periods
af37e07f3 is described below

commit af37e07f31a97154e1ccae2c678ba6ca8ef5c08f
Author: Oleksii Novikov <[email protected]>
AuthorDate: Thu Jan 23 18:15:33 2025 +0200

    FINERACT-2152: Interest pause during one period and between two periods
---
 .../fineract/client/util/FineractClient.java       |  3 +
 .../apache/fineract/test/api/ApiConfiguration.java |  6 ++
 .../stepdef/loan/LoanInterestPauseStepDef.java     | 53 +++++++++++++
 .../resources/features/LoanInterestPause.feature   | 74 +++++++++++++++++
 .../InterestPauseWritePlatformServiceImpl.java     | 30 ++++---
 .../data/LoanTermVariationsDataWrapper.java        | 33 ++++----
 .../loanaccount/domain/LoanTermVariationType.java  |  4 +
 .../loanaccount/service/LoanAssembler.java         |  7 ++
 ...dvancedPaymentScheduleTransactionProcessor.java | 18 ++++-
 .../loanschedule/data/InterestPeriod.java          | 24 +++---
 .../data/ProgressiveLoanInterestScheduleModel.java | 92 ++++++++++++++++++----
 .../loanschedule/data/RepaymentPeriod.java         |  2 +-
 .../domain/ProgressiveLoanScheduleGenerator.java   | 24 +++---
 .../portfolio/loanproduct/calc/EMICalculator.java  |  2 +
 .../loanproduct/calc/ProgressiveEMICalculator.java | 14 ++++
 .../calc/ProgressiveEMICalculatorTest.java         | 76 ++++++++++++++++++
 .../loanaccount/service/LoanAssemblerImpl.java     | 27 ++++++-
 .../starter/LoanAccountConfiguration.java          |  4 +-
 18 files changed, 414 insertions(+), 79 deletions(-)

diff --git 
a/fineract-client/src/main/java/org/apache/fineract/client/util/FineractClient.java
 
b/fineract-client/src/main/java/org/apache/fineract/client/util/FineractClient.java
index f722c3488..ec6aa161b 100644
--- 
a/fineract-client/src/main/java/org/apache/fineract/client/util/FineractClient.java
+++ 
b/fineract-client/src/main/java/org/apache/fineract/client/util/FineractClient.java
@@ -88,6 +88,7 @@ import org.apache.fineract.client.services.LoanChargesApi;
 import org.apache.fineract.client.services.LoanCobCatchUpApi;
 import org.apache.fineract.client.services.LoanCollateralApi;
 import org.apache.fineract.client.services.LoanDisbursementDetailsApi;
+import org.apache.fineract.client.services.LoanInterestPauseApi;
 import org.apache.fineract.client.services.LoanProductsApi;
 import org.apache.fineract.client.services.LoanReschedulingApi;
 import org.apache.fineract.client.services.LoanTransactionsApi;
@@ -290,6 +291,7 @@ public final class FineractClient {
     public final UserGeneratedDocumentsApi templates;
     public final UsersApi users;
     public final WorkingDaysApi workingDays;
+    public final LoanInterestPauseApi loanInterestPauseApi;
 
     public final ExternalAssetOwnersApi externalAssetOwners;
     public final ExternalAssetOwnerLoanProductAttributesApi 
externalAssetOwnerLoanProductAttributes;
@@ -415,6 +417,7 @@ public final class FineractClient {
         templates = retrofit.create(UserGeneratedDocumentsApi.class);
         users = retrofit.create(UsersApi.class);
         workingDays = retrofit.create(WorkingDaysApi.class);
+        loanInterestPauseApi = retrofit.create(LoanInterestPauseApi.class);
     }
 
     public static Builder builder() {
diff --git 
a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/api/ApiConfiguration.java
 
b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/api/ApiConfiguration.java
index 0607b76b9..8054d746a 100644
--- 
a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/api/ApiConfiguration.java
+++ 
b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/api/ApiConfiguration.java
@@ -40,6 +40,7 @@ import org.apache.fineract.client.services.JournalEntriesApi;
 import org.apache.fineract.client.services.LoanAccountLockApi;
 import org.apache.fineract.client.services.LoanChargesApi;
 import org.apache.fineract.client.services.LoanCobCatchUpApi;
+import org.apache.fineract.client.services.LoanInterestPauseApi;
 import org.apache.fineract.client.services.LoanProductsApi;
 import org.apache.fineract.client.services.LoanTransactionsApi;
 import org.apache.fineract.client.services.LoansApi;
@@ -244,4 +245,9 @@ public class ApiConfiguration {
     public RescheduleLoansApi rescheduleLoansApi() {
         return fineractClient.createService(RescheduleLoansApi.class);
     }
+
+    @Bean
+    public LoanInterestPauseApi loanInterestPauseApi() {
+        return fineractClient.createService(LoanInterestPauseApi.class);
+    }
 }
diff --git 
a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/loan/LoanInterestPauseStepDef.java
 
b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/loan/LoanInterestPauseStepDef.java
new file mode 100644
index 000000000..39932fdaf
--- /dev/null
+++ 
b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/loan/LoanInterestPauseStepDef.java
@@ -0,0 +1,53 @@
+/**
+ * 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.test.stepdef.loan;
+
+import io.cucumber.java.en.And;
+import java.io.IOException;
+import org.apache.fineract.client.models.CommandProcessingResult;
+import org.apache.fineract.client.models.InterestPauseRequestDto;
+import org.apache.fineract.client.models.PostLoansResponse;
+import org.apache.fineract.client.services.LoanInterestPauseApi;
+import org.apache.fineract.test.helper.ErrorHelper;
+import org.apache.fineract.test.stepdef.AbstractStepDef;
+import org.apache.fineract.test.support.TestContextKey;
+import org.springframework.beans.factory.annotation.Autowired;
+import retrofit2.Response;
+
+public class LoanInterestPauseStepDef extends AbstractStepDef {
+
+    @Autowired
+    private LoanInterestPauseApi loanInterestPauseApi;
+
+    @And("Customer creates interest pause with start date {string} and end 
date {string}")
+    public void createInterestPause(final String startDate, final String 
endDate) throws IOException {
+        final Response<PostLoansResponse> loanResponse = 
testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
+        assert loanResponse.body() != null;
+        final long loanId = loanResponse.body().getLoanId();
+
+        final InterestPauseRequestDto request = new InterestPauseRequestDto()//
+                .startDate(startDate)//
+                .endDate(endDate)//
+                .dateFormat("dd MMMM yyyy")//
+                .locale("en");//
+
+        final Response<CommandProcessingResult> createResponse = 
loanInterestPauseApi.createInterestPause(loanId, request).execute();
+        ErrorHelper.checkSuccessfulApiCall(createResponse);
+    }
+}
diff --git 
a/fineract-e2e-tests-runner/src/test/resources/features/LoanInterestPause.feature
 
b/fineract-e2e-tests-runner/src/test/resources/features/LoanInterestPause.feature
new file mode 100644
index 000000000..311e7ba25
--- /dev/null
+++ 
b/fineract-e2e-tests-runner/src/test/resources/features/LoanInterestPause.feature
@@ -0,0 +1,74 @@
+@InterestPauseFeature
+Feature: Loan interest pause on repayment schedule
+
+  Scenario: S1 - pause calculation within same period, interestRecalculation = 
true
+    When Admin sets the business date to "1 January 2024"
+    And Admin creates a client with random data
+    And Admin creates a fully customized loan with the following data:
+      | LoanProduct                                                   | 
submitted on date | with Principal | ANNUAL interest rate % | interest type     
| interest calculation period | amortization type  | loanTermFrequency | 
loanTermFrequencyType | repaymentEvery | repaymentFrequencyType | 
numberOfRepayments | graceOnPrincipalPayment | graceOnInterestPayment | 
interest free period | Payment strategy            |
+      | LP2_ADV_CUSTOM_PMT_ALLOC_PROGRESSIVE_LOAN_SCHEDULE_HORIZONTAL | 01 
January 2024   | 100            | 7                      | DECLINING_BALANCE | 
DAILY                       | EQUAL_INSTALLMENTS | 6                 | MONTHS   
             | 1              | MONTHS                 | 6                  | 0 
                      | 0                      | 0                    | 
ADVANCED_PAYMENT_ALLOCATION |
+    Then Loan Repayment schedule has 6 periods, with the following data for 
periods:
+      | Nr | Days | Date             | Paid date | Balance of loan | Principal 
due | Interest | Fees | Penalties | Due   | Paid | In advance | Late | 
Outstanding |
+      |    |      | 01 January 2024  |           | 100.0           |           
    |          | 0.0  |           | 0.0   |      |            |      | 0.0      
   |
+      | 1  | 31   | 01 February 2024 |           | 83.57           | 16.43     
    | 0.58     | 0.0  | 0.0       | 17.01 | 0.0  | 0.0        | 0.0  | 17.01    
   |
+      | 2  | 29   | 01 March 2024    |           | 67.05           | 16.52     
    | 0.49     | 0.0  | 0.0       | 17.01 | 0.0  | 0.0        | 0.0  | 17.01    
   |
+      | 3  | 31   | 01 April 2024    |           | 50.43           | 16.62     
    | 0.39     | 0.0  | 0.0       | 17.01 | 0.0  | 0.0        | 0.0  | 17.01    
   |
+      | 4  | 30   | 01 May 2024      |           | 33.71           | 16.72     
    | 0.29     | 0.0  | 0.0       | 17.01 | 0.0  | 0.0        | 0.0  | 17.01    
   |
+      | 5  | 31   | 01 June 2024     |           | 16.9            | 16.81     
    | 0.2      | 0.0  | 0.0       | 17.01 | 0.0  | 0.0        | 0.0  | 17.01    
   |
+      | 6  | 30   | 01 July 2024     |           | 0.0             | 16.9      
    | 0.1      | 0.0  | 0.0       | 17.0  | 0.0  | 0.0        | 0.0  | 17.0     
   |
+    Then Loan Repayment schedule has the following data in Total row:
+      | Principal due | Interest | Fees | Penalties | Due    | Paid | In 
advance | Late | Outstanding |
+      | 100           | 2.05     | 0    | 0         | 102.05 | 0    | 0        
  | 0    | 102.05      |
+    And Admin successfully approves the loan on "1 January 2024" with "100" 
amount and expected disbursement date on "1 January 2024"
+    And Admin successfully disburse the loan on "1 January 2024" with "100" 
EUR transaction amount
+    When Admin sets the business date to "1 February 2024"
+    And Customer makes "AUTOPAY" repayment on "01 February 2024" with 17.01 
EUR transaction amount
+    And Customer creates interest pause with start date "05 February 2024" and 
end date "10 February 2024"
+    Then Loan Repayment schedule has 6 periods, with the following data for 
periods:
+      | Nr | Days | Date             | Paid date        | Balance of loan | 
Principal due | Interest | Fees | Penalties | Due   | Paid  | In advance | Late 
| Outstanding |
+      |    |      | 01 January 2024  |                  | 100.0           |    
           |          | 0.0  |           | 0.0   | 0.0   |            |      |  
           |
+      | 1  | 31   | 01 February 2024 | 01 February 2024 | 83.57           | 
16.43         | 0.58     | 0.0  | 0.0       | 17.01 | 17.01 | 0.0        | 0.0  
| 0.0         |
+      | 2  | 29   | 01 March 2024    |                  | 66.95           | 
16.62         | 0.39     | 0.0  | 0.0       | 17.01 | 0.0   | 0.0        | 0.0  
| 17.01       |
+      | 3  | 31   | 01 April 2024    |                  | 50.33           | 
16.62         | 0.39     | 0.0  | 0.0       | 17.01 | 0.0   | 0.0        | 0.0  
| 17.01       |
+      | 4  | 30   | 01 May 2024      |                  | 33.61           | 
16.72         | 0.29     | 0.0  | 0.0       | 17.01 | 0.0   | 0.0        | 0.0  
| 17.01       |
+      | 5  | 31   | 01 June 2024     |                  | 16.8            | 
16.81         | 0.2      | 0.0  | 0.0       | 17.01 | 0.0   | 0.0        | 0.0  
| 17.01       |
+      | 6  | 30   | 01 July 2024     |                  | 0.0             | 
16.8          | 0.1      | 0.0  | 0.0       | 16.9  | 0.0   | 0.0        | 0.0  
| 16.9        |
+    Then Loan Repayment schedule has the following data in Total row:
+      | Principal due | Interest | Fees | Penalties | Due    | Paid  | In 
advance | Late | Outstanding |
+      | 100           | 1.95     | 0    | 0         | 101.95 | 17.01 | 0       
   | 0    | 84.94       |
+
+  Scenario: S2 - pause calculation between two periods, interestRecalculation 
= true
+    When Admin sets the business date to "1 January 2024"
+    And Admin creates a client with random data
+    And Admin creates a fully customized loan with the following data:
+      | LoanProduct                                                   | 
submitted on date | with Principal | ANNUAL interest rate % | interest type     
| interest calculation period | amortization type  | loanTermFrequency | 
loanTermFrequencyType | repaymentEvery | repaymentFrequencyType | 
numberOfRepayments | graceOnPrincipalPayment | graceOnInterestPayment | 
interest free period | Payment strategy            |
+      | LP2_ADV_CUSTOM_PMT_ALLOC_PROGRESSIVE_LOAN_SCHEDULE_HORIZONTAL | 01 
January 2024   | 100            | 7                      | DECLINING_BALANCE | 
DAILY                       | EQUAL_INSTALLMENTS | 6                 | MONTHS   
             | 1              | MONTHS                 | 6                  | 0 
                      | 0                      | 0                    | 
ADVANCED_PAYMENT_ALLOCATION |
+    Then Loan Repayment schedule has 6 periods, with the following data for 
periods:
+      | Nr | Days | Date             | Paid date | Balance of loan | Principal 
due | Interest | Fees | Penalties | Due   | Paid | In advance | Late | 
Outstanding |
+      |    |      | 01 January 2024  |           | 100.0           |           
    |          | 0.0  |           | 0.0   |      |            |      | 0.0      
   |
+      | 1  | 31   | 01 February 2024 |           | 83.57           | 16.43     
    | 0.58     | 0.0  | 0.0       | 17.01 | 0.0  | 0.0        | 0.0  | 17.01    
   |
+      | 2  | 29   | 01 March 2024    |           | 67.05           | 16.52     
    | 0.49     | 0.0  | 0.0       | 17.01 | 0.0  | 0.0        | 0.0  | 17.01    
   |
+      | 3  | 31   | 01 April 2024    |           | 50.43           | 16.62     
    | 0.39     | 0.0  | 0.0       | 17.01 | 0.0  | 0.0        | 0.0  | 17.01    
   |
+      | 4  | 30   | 01 May 2024      |           | 33.71           | 16.72     
    | 0.29     | 0.0  | 0.0       | 17.01 | 0.0  | 0.0        | 0.0  | 17.01    
   |
+      | 5  | 31   | 01 June 2024     |           | 16.9            | 16.81     
    | 0.2      | 0.0  | 0.0       | 17.01 | 0.0  | 0.0        | 0.0  | 17.01    
   |
+      | 6  | 30   | 01 July 2024     |           | 0.0             | 16.9      
    | 0.1      | 0.0  | 0.0       | 17.0  | 0.0  | 0.0        | 0.0  | 17.0     
   |
+    Then Loan Repayment schedule has the following data in Total row:
+      | Principal due | Interest | Fees | Penalties | Due    | Paid | In 
advance | Late | Outstanding |
+      | 100           | 2.05     | 0    | 0         | 102.05 | 0    | 0        
  | 0    | 102.05      |
+    And Admin successfully approves the loan on "1 January 2024" with "100" 
amount and expected disbursement date on "1 January 2024"
+    And Admin successfully disburse the loan on "1 January 2024" with "100" 
EUR transaction amount
+    When Admin sets the business date to "1 February 2024"
+    And Customer makes "AUTOPAY" repayment on "01 February 2024" with 17.01 
EUR transaction amount
+    And Customer creates interest pause with start date "10 February 2024" and 
end date "10 March 2024"
+    Then Loan Repayment schedule has 6 periods, with the following data for 
periods:
+      | Nr | Days | Date             | Paid date        | Balance of loan | 
Principal due | Interest | Fees | Penalties | Due   | Paid  | In advance | Late 
| Outstanding |
+      |    |      | 01 January 2024  |                  | 100.0           |    
           |          | 0.0  |           | 0.0   | 0.0   |            |      |  
           |
+      | 1  | 31   | 01 February 2024 | 01 February 2024 | 83.57           | 
16.43         | 0.58     | 0.0  | 0.0       | 17.01 | 17.01 | 0.0        | 0.0  
| 0.0         |
+      | 2  | 29   | 01 March 2024    |                  | 66.69           | 
16.88         | 0.13     | 0.0  | 0.0       | 17.01 | 0.0   | 0.0        | 0.0  
| 17.01       |
+      | 3  | 31   | 01 April 2024    |                  | 49.96           | 
16.73         | 0.28     | 0.0  | 0.0       | 17.01 | 0.0   | 0.0        | 0.0  
| 17.01       |
+      | 4  | 30   | 01 May 2024      |                  | 33.24           | 
16.72         | 0.29     | 0.0  | 0.0       | 17.01 | 0.0   | 0.0        | 0.0  
| 17.01       |
+      | 5  | 31   | 01 June 2024     |                  | 16.42           | 
16.82         | 0.19     | 0.0  | 0.0       | 17.01 | 0.0   | 0.0        | 0.0  
| 17.01       |
+      | 6  | 30   | 01 July 2024     |                  | 0.0             | 
16.42         | 0.1      | 0.0  | 0.0       | 16.52 | 0.0   | 0.0        | 0.0  
| 16.52       |
+    Then Loan Repayment schedule has the following data in Total row:
+      | Principal due | Interest | Fees | Penalties | Due    | Paid  | In 
advance | Late | Outstanding |
+      | 100           | 1.57     | 0    | 0         | 101.57 | 17.01 | 0       
   | 0    | 84.56       |
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 3d5b0e520..b7e1ec1fa 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
@@ -22,6 +22,7 @@ import static 
org.apache.fineract.portfolio.loanaccount.domain.LoanStatus.ACTIVE
 import static 
org.apache.fineract.portfolio.loanaccount.domain.LoanTermVariationType.INTEREST_PAUSE;
 import static 
org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleType.PROGRESSIVE;
 
+import java.math.BigDecimal;
 import java.time.LocalDate;
 import java.time.format.DateTimeFormatter;
 import java.time.format.DateTimeParseException;
@@ -43,6 +44,7 @@ import org.apache.fineract.portfolio.loanaccount.domain.Loan;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanRepositoryWrapper;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanTermVariations;
 import 
org.apache.fineract.portfolio.loanaccount.rescheduleloan.domain.LoanTermVariationsRepository;
+import org.apache.fineract.portfolio.loanaccount.service.LoanAssembler;
 import org.springframework.transaction.annotation.Transactional;
 
 @AllArgsConstructor
@@ -51,24 +53,26 @@ public class InterestPauseWritePlatformServiceImpl 
implements InterestPauseWrite
 
     private final LoanTermVariationsRepository loanTermVariationsRepository;
     private final LoanRepositoryWrapper loanRepositoryWrapper;
+    private final LoanAssembler loanAssembler;
 
     @Override
-    public CommandProcessingResult createInterestPause(ExternalId 
loanExternalId, String startDateString, String endDateString,
-            String dateFormat, String locale) {
+    public CommandProcessingResult createInterestPause(final ExternalId 
loanExternalId, final String startDateString,
+            final String endDateString, final String dateFormat, final String 
locale) {
         final LocalDate startDate = parseDate(startDateString, dateFormat, 
locale);
         final LocalDate endDate = parseDate(endDateString, dateFormat, locale);
+        final Loan loan = loanAssembler.assembleFrom(loanExternalId, false);
 
-        return 
processInterestPause(loanRepositoryWrapper.findOneWithNotFoundDetection(loanExternalId),
 startDate, endDate, dateFormat,
-                locale);
+        return processInterestPause(loan, startDate, endDate, dateFormat, 
locale);
     }
 
     @Override
-    public CommandProcessingResult createInterestPause(Long loanId, String 
startDateString, String endDateString, String dateFormat,
-            String locale) {
+    public CommandProcessingResult createInterestPause(final Long loanId, 
final String startDateString, final String endDateString,
+            final String dateFormat, final String locale) {
         final LocalDate startDate = parseDate(startDateString, dateFormat, 
locale);
         final LocalDate endDate = parseDate(endDateString, dateFormat, locale);
+        final Loan loan = loanAssembler.assembleFrom(loanId, false);
 
-        return 
processInterestPause(loanRepositoryWrapper.findOneWithNotFoundDetection(loanId),
 startDate, endDate, dateFormat, locale);
+        return processInterestPause(loan, startDate, endDate, dateFormat, 
locale);
     }
 
     @Override
@@ -131,14 +135,18 @@ public class InterestPauseWritePlatformServiceImpl 
implements InterestPauseWrite
                 .with(Map.of("startDate", startDate.toString(), "endDate", 
endDate.toString())).build();
     }
 
-    private CommandProcessingResult processInterestPause(Loan loan, LocalDate 
startDate, LocalDate endDate, String dateFormat,
-            String locale) {
+    private CommandProcessingResult processInterestPause(final Loan loan, 
final LocalDate startDate, final LocalDate endDate,
+            String dateFormat, String locale) {
         validateActiveLoan(loan);
         validateInterestPauseDates(loan, startDate, endDate, dateFormat, 
locale, null);
 
-        LoanTermVariations variation = new 
LoanTermVariations(INTEREST_PAUSE.getValue(), startDate, null, endDate, false, 
loan);
+        final LoanTermVariations variation = new 
LoanTermVariations(INTEREST_PAUSE.getValue(), startDate, BigDecimal.ZERO, 
endDate, false,
+                loan);
+
+        final LoanTermVariations savedVariation = 
loanTermVariationsRepository.saveAndFlush(variation);
 
-        LoanTermVariations savedVariation = 
loanTermVariationsRepository.saveAndFlush(variation);
+        loan.getLoanTermVariations().add(savedVariation);
+        loan.reprocessTransactions();
 
         return new 
CommandProcessingResultBuilder().withEntityId(savedVariation.getId()).build();
     }
diff --git 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanTermVariationsDataWrapper.java
 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanTermVariationsDataWrapper.java
index 8a9320154..5b8e0e79a 100644
--- 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanTermVariationsDataWrapper.java
+++ 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanTermVariationsDataWrapper.java
@@ -23,16 +23,23 @@ import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 import java.util.ListIterator;
+import lombok.Getter;
 import org.apache.fineract.infrastructure.core.service.DateUtils;
 
 public class LoanTermVariationsDataWrapper {
 
+    @Getter
     private final List<LoanTermVariationsData> exceptionData;
     private ListIterator<LoanTermVariationsData> iterator;
+    @Getter
     private final List<LoanTermVariationsData> interestRateChanges;
+    @Getter
     private final List<LoanTermVariationsData> interestRateFromInstallment;
+    @Getter
     private final List<LoanTermVariationsData> dueDateVariation;
     private ListIterator<LoanTermVariationsData> dueDateIterator;
+    @Getter
+    private final List<LoanTermVariationsData> interestPauseVariations;
 
     public LoanTermVariationsDataWrapper(final List<LoanTermVariationsData> 
exceptionData) {
         if (exceptionData == null) {
@@ -43,6 +50,7 @@ public class LoanTermVariationsDataWrapper {
         this.interestRateChanges = new ArrayList<>();
         this.dueDateVariation = new ArrayList<>();
         this.interestRateFromInstallment = new ArrayList<>();
+        this.interestPauseVariations = new ArrayList<>();
         deriveLoanTermVariations();
     }
 
@@ -80,18 +88,6 @@ public class LoanTermVariationsDataWrapper {
         return this.dueDateIterator.previous();
     }
 
-    public List<LoanTermVariationsData> getInterestRateChanges() {
-        return this.interestRateChanges;
-    }
-
-    public List<LoanTermVariationsData> getDueDateVariation() {
-        return this.dueDateVariation;
-    }
-
-    public List<LoanTermVariationsData> getExceptionData() {
-        return this.exceptionData;
-    }
-
     public void setExceptionData(final List<LoanTermVariationsData> 
exceptionData) {
         clearTerms();
         this.exceptionData.addAll(exceptionData);
@@ -103,10 +99,7 @@ public class LoanTermVariationsDataWrapper {
         this.interestRateChanges.clear();
         this.dueDateVariation.clear();
         this.interestRateFromInstallment.clear();
-    }
-
-    public List<LoanTermVariationsData> getInterestRateFromInstallment() {
-        return this.interestRateFromInstallment;
+        this.interestPauseVariations.clear();
     }
 
     public int adjustNumberOfRepayments() {
@@ -133,12 +126,11 @@ public class LoanTermVariationsDataWrapper {
     }
 
     public boolean hasExceptionVariation(final LocalDate date, 
ListIterator<LoanTermVariationsData> exceptionDataListIterator) {
-        ListIterator<LoanTermVariationsData> iterator = 
exceptionDataListIterator;
-        return hasNext(date, iterator);
+        return hasNext(date, exceptionDataListIterator);
     }
 
     public void updateLoanTermVariationsData(final 
List<LoanTermVariationsData> exceptionData) {
-        if (this.exceptionData != null && exceptionData != null && 
exceptionData.size() > 0) {
+        if (this.exceptionData != null && exceptionData != null && 
!exceptionData.isEmpty()) {
             this.exceptionData.addAll(exceptionData);
             deriveLoanTermVariations();
         }
@@ -153,12 +145,15 @@ public class LoanTermVariationsDataWrapper {
                 this.dueDateVariation.add(loanTermVariationsData);
             } else if 
(loanTermVariationsData.getTermVariationType().isInterestRateFromInstallment()) 
{
                 this.interestRateFromInstallment.add(loanTermVariationsData);
+            } else if 
(loanTermVariationsData.getTermVariationType().isInterestPauseVariation()) {
+                this.interestPauseVariations.add(loanTermVariationsData);
             }
         }
         Collections.sort(this.dueDateVariation);
         this.exceptionData.removeAll(this.interestRateChanges);
         this.exceptionData.removeAll(this.dueDateVariation);
         this.exceptionData.removeAll(this.interestRateFromInstallment);
+        this.exceptionData.removeAll(this.interestPauseVariations);
         this.iterator = this.exceptionData.listIterator();
         this.dueDateIterator = this.dueDateVariation.listIterator();
     }
diff --git 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTermVariationType.java
 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTermVariationType.java
index 21e4655d3..67fd9bf1f 100644
--- 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTermVariationType.java
+++ 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTermVariationType.java
@@ -98,6 +98,10 @@ public enum LoanTermVariationType {
         return 
this.value.equals(LoanTermVariationType.INTEREST_RATE.getValue());
     }
 
+    public boolean isInterestPauseVariation() {
+        return 
this.value.equals(LoanTermVariationType.INTEREST_PAUSE.getValue());
+    }
+
     public boolean isPrincipalAmountVariation() {
         return 
this.value.equals(LoanTermVariationType.PRINCIPAL_AMOUNT.getValue());
     }
diff --git 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAssembler.java
 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAssembler.java
index d8c12da83..59f8dc92b 100644
--- 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAssembler.java
+++ 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAssembler.java
@@ -21,6 +21,7 @@ package org.apache.fineract.portfolio.loanaccount.service;
 import java.util.Map;
 import org.apache.fineract.infrastructure.codes.domain.CodeValue;
 import org.apache.fineract.infrastructure.core.api.JsonCommand;
+import org.apache.fineract.infrastructure.core.domain.ExternalId;
 import org.apache.fineract.organisation.staff.domain.Staff;
 import org.apache.fineract.portfolio.fund.domain.Fund;
 import org.apache.fineract.portfolio.loanaccount.domain.Loan;
@@ -30,6 +31,12 @@ public interface LoanAssembler {
 
     Loan assembleFrom(Long accountId);
 
+    Loan assembleFrom(Long accountId, boolean loadLazyCollections);
+
+    Loan assembleFrom(ExternalId externalId);
+
+    Loan assembleFrom(ExternalId externalId, boolean loadLazyCollections);
+
     Loan assembleFrom(JsonCommand command);
 
     void setHelpers(Loan loanAccount);
diff --git 
a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/AdvancedPaymentScheduleTransactionProcessor.java
 
b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/AdvancedPaymentScheduleTransactionProcessor.java
index 57b50552b..e743db6fd 100644
--- 
a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/AdvancedPaymentScheduleTransactionProcessor.java
+++ 
b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/AdvancedPaymentScheduleTransactionProcessor.java
@@ -261,7 +261,12 @@ public class AdvancedPaymentScheduleTransactionProcessor 
extends AbstractLoanRep
             final LoanTermVariationsData interestRateChange, final 
ProgressiveLoanInterestScheduleModel scheduleModel) {
         final LocalDate interestRateChangeSubmittedOnDate = 
interestRateChange.getTermVariationApplicableFrom();
         final BigDecimal newInterestRate = 
interestRateChange.getDecimalValue();
-        emiCalculator.changeInterestRate(scheduleModel, 
interestRateChangeSubmittedOnDate, newInterestRate);
+        if 
(interestRateChange.getTermVariationType().isInterestPauseVariation()) {
+            final LocalDate pauseEndDate = interestRateChange.getDateValue();
+            emiCalculator.applyInterestPause(scheduleModel, 
interestRateChangeSubmittedOnDate, pauseEndDate);
+        } else {
+            emiCalculator.changeInterestRate(scheduleModel, 
interestRateChangeSubmittedOnDate, newInterestRate);
+        }
         processInterestRateChangeOnInstallments(scheduleModel, 
interestRateChangeSubmittedOnDate, installments);
     }
 
@@ -820,9 +825,14 @@ public class AdvancedPaymentScheduleTransactionProcessor 
extends AbstractLoanRep
     @NotNull
     private List<ChangeOperation> createSortedChangeList(final 
LoanTermVariationsDataWrapper loanTermVariations,
             final List<LoanTransaction> loanTransactions, final 
Set<LoanCharge> charges) {
-        List<ChangeOperation> changeOperations = new ArrayList<>();
-        if (loanTermVariations != null && 
!loanTermVariations.getInterestRateFromInstallment().isEmpty()) {
-            
changeOperations.addAll(loanTermVariations.getInterestRateFromInstallment().stream().map(ChangeOperation::new).toList());
+        final List<ChangeOperation> changeOperations = new ArrayList<>();
+        if (loanTermVariations != null) {
+            if (!loanTermVariations.getInterestPauseVariations().isEmpty()) {
+                
changeOperations.addAll(loanTermVariations.getInterestPauseVariations().stream().map(ChangeOperation::new).toList());
+            }
+            if 
(!loanTermVariations.getInterestRateFromInstallment().isEmpty()) {
+                
changeOperations.addAll(loanTermVariations.getInterestRateFromInstallment().stream().map(ChangeOperation::new).toList());
+            }
         }
         if (charges != null) {
             
changeOperations.addAll(charges.stream().map(ChangeOperation::new).toList());
diff --git 
a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/data/InterestPeriod.java
 
b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/data/InterestPeriod.java
index 3d4824195..d21b1defa 100644
--- 
a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/data/InterestPeriod.java
+++ 
b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/data/InterestPeriod.java
@@ -23,6 +23,7 @@ import java.math.BigDecimal;
 import java.math.MathContext;
 import java.time.LocalDate;
 import java.util.Optional;
+import lombok.AllArgsConstructor;
 import lombok.EqualsAndHashCode;
 import lombok.Getter;
 import lombok.Setter;
@@ -34,6 +35,7 @@ import org.apache.fineract.organisation.monetary.domain.Money;
 @Getter
 @ToString(exclude = { "repaymentPeriod" })
 @EqualsAndHashCode(exclude = { "repaymentPeriod" })
+@AllArgsConstructor
 public class InterestPeriod implements Comparable<InterestPeriod> {
 
     private final RepaymentPeriod repaymentPeriod;
@@ -49,25 +51,13 @@ public class InterestPeriod implements 
Comparable<InterestPeriod> {
     private Money balanceCorrectionAmount;
     private Money outstandingLoanBalance;
     private final MathContext mc;
-
-    public InterestPeriod(RepaymentPeriod repaymentPeriod, LocalDate fromDate, 
LocalDate dueDate, BigDecimal rateFactor,
-            BigDecimal rateFactorTillPeriodDueDate, Money disbursementAmount, 
Money balanceCorrectionAmount, Money outstandingLoanBalance,
-            MathContext mc) {
-        this.repaymentPeriod = repaymentPeriod;
-        this.fromDate = fromDate;
-        this.dueDate = dueDate;
-        this.rateFactor = rateFactor;
-        this.rateFactorTillPeriodDueDate = rateFactorTillPeriodDueDate;
-        this.disbursementAmount = disbursementAmount;
-        this.balanceCorrectionAmount = balanceCorrectionAmount;
-        this.outstandingLoanBalance = outstandingLoanBalance;
-        this.mc = mc;
-    }
+    private final boolean isPaused;
 
     public InterestPeriod(RepaymentPeriod repaymentPeriod, InterestPeriod 
interestPeriod) {
         this(repaymentPeriod, interestPeriod.getFromDate(), 
interestPeriod.getDueDate(), interestPeriod.getRateFactor(),
                 interestPeriod.getRateFactorTillPeriodDueDate(), 
interestPeriod.getDisbursementAmount(),
-                interestPeriod.getBalanceCorrectionAmount(), 
interestPeriod.getOutstandingLoanBalance(), interestPeriod.getMc());
+                interestPeriod.getBalanceCorrectionAmount(), 
interestPeriod.getOutstandingLoanBalance(), interestPeriod.getMc(),
+                interestPeriod.isPaused());
     }
 
     @Override
@@ -84,6 +74,10 @@ public class InterestPeriod implements 
Comparable<InterestPeriod> {
     }
 
     public Money getCalculatedDueInterest() {
+        if (isPaused) {
+            return Money.zero(outstandingLoanBalance.getCurrencyData(), mc);
+        }
+
         long lengthTillPeriodDueDate = getLengthTillPeriodDueDate();
         final BigDecimal interestDueTillRepaymentDueDate = 
lengthTillPeriodDueDate == 0 //
                 ? BigDecimal.ZERO //
diff --git 
a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/data/ProgressiveLoanInterestScheduleModel.java
 
b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/data/ProgressiveLoanInterestScheduleModel.java
index 543ecb776..8c9e88091 100644
--- 
a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/data/ProgressiveLoanInterestScheduleModel.java
+++ 
b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/data/ProgressiveLoanInterestScheduleModel.java
@@ -160,6 +160,24 @@ public class ProgressiveLoanInterestScheduleModel {
                 .findFirst();//
     }
 
+    public Optional<RepaymentPeriod> 
updateInterestPeriodsForInterestPause(final LocalDate fromDate, final LocalDate 
endDate) {
+        if (fromDate == null || endDate == null) {
+            return Optional.empty();
+        }
+
+        final List<RepaymentPeriod> affectedPeriods = 
repaymentPeriods.stream()//
+                .filter(period -> isPeriodInRange(period, fromDate, endDate))//
+                .toList();
+        affectedPeriods.forEach(period -> insertInterestPausePeriods(period, 
fromDate, endDate));
+
+        return affectedPeriods.stream().findFirst();
+    }
+
+    private boolean isPeriodInRange(final RepaymentPeriod repaymentPeriod, 
final LocalDate fromDate, final LocalDate endDate) {
+        return DateUtils.isDateInRangeFromExclusiveToInclusive(fromDate, 
repaymentPeriod.getFromDate(), repaymentPeriod.getDueDate())
+                || DateUtils.isDateInRangeFromExclusiveToInclusive(endDate, 
repaymentPeriod.getFromDate(), repaymentPeriod.getDueDate());
+    }
+
     Optional<RepaymentPeriod> findRepaymentPeriodForBalanceChange(final 
LocalDate balanceChangeDate) {
         if (balanceChangeDate == null) {
             return Optional.empty();
@@ -192,7 +210,8 @@ public class ProgressiveLoanInterestScheduleModel {
         };
     }
 
-    Optional<InterestPeriod> findInterestPeriodForBalanceChange(final 
RepaymentPeriod repaymentPeriod, final LocalDate balanceChangeDate) {
+    private Optional<InterestPeriod> findInterestPeriodForBalanceChange(final 
RepaymentPeriod repaymentPeriod,
+            final LocalDate balanceChangeDate) {
         if (repaymentPeriod == null || balanceChangeDate == null) {
             return Optional.empty();
         }
@@ -203,26 +222,63 @@ public class ProgressiveLoanInterestScheduleModel {
 
     void insertInterestPeriod(final RepaymentPeriod repaymentPeriod, final 
LocalDate balanceChangeDate, final Money disbursedAmount,
             final Money correctionAmount) {
-        final InterestPeriod previousInterestPeriod;
-        if (balanceChangeDate.isAfter(repaymentPeriod.getFromDate())) {
-            previousInterestPeriod = 
repaymentPeriod.getInterestPeriods().get(repaymentPeriod.getInterestPeriods().size()
 - 1);//
-        } else {
-            previousInterestPeriod = 
repaymentPeriod.getInterestPeriods().stream()
-                    .filter(ip -> balanceChangeDate.isAfter(ip.getFromDate()) 
&& !balanceChangeDate.isAfter(ip.getDueDate()))//
-                    .reduce((first, second) -> second)//
-                    .orElse(repaymentPeriod.getInterestPeriods().get(0));
-        }
-        LocalDate originalDueDate = previousInterestPeriod.getDueDate();
-        LocalDate newDueDate = 
balanceChangeDate.isBefore(previousInterestPeriod.getFromDate()) ? 
previousInterestPeriod.getFromDate()
-                : 
balanceChangeDate.isAfter(previousInterestPeriod.getDueDate()) ? 
previousInterestPeriod.getDueDate() : balanceChangeDate;
+        final InterestPeriod previousInterestPeriod = 
findPreviousInterestPeriod(repaymentPeriod, balanceChangeDate);
+        final LocalDate originalDueDate = previousInterestPeriod.getDueDate();
+        final LocalDate newDueDate = 
calculateNewDueDate(previousInterestPeriod, balanceChangeDate);
+
         previousInterestPeriod.setDueDate(newDueDate);
         previousInterestPeriod.addDisbursementAmount(disbursedAmount);
         previousInterestPeriod.addBalanceCorrectionAmount(correctionAmount);
-        final InterestPeriod interestPeriod = new 
InterestPeriod(repaymentPeriod, previousInterestPeriod.getDueDate(), 
originalDueDate,
-                BigDecimal.ZERO, BigDecimal.ZERO, zero, zero, zero, mc);
+
+        final InterestPeriod interestPeriod = new 
InterestPeriod(repaymentPeriod, newDueDate, originalDueDate, BigDecimal.ZERO,
+                BigDecimal.ZERO, zero, zero, zero, mc, false);
         repaymentPeriod.getInterestPeriods().add(interestPeriod);
     }
 
+    private void insertInterestPausePeriods(final RepaymentPeriod 
repaymentPeriod, final LocalDate fromDate, final LocalDate endDate) {
+        final InterestPeriod previousInterestPeriod = 
findPreviousInterestPeriod(repaymentPeriod, fromDate);
+        final LocalDate originalFromDate = 
previousInterestPeriod.getFromDate();
+        final LocalDate originalDueDate = previousInterestPeriod.getDueDate();
+        final LocalDate newDueDate = 
calculateNewDueDate(previousInterestPeriod, fromDate.minusDays(1));
+
+        if (fromDate.isAfter(originalFromDate) && 
endDate.isBefore(originalDueDate)) {
+            previousInterestPeriod.setDueDate(newDueDate);
+            final InterestPeriod interestPausePeriod = new 
InterestPeriod(repaymentPeriod, newDueDate, endDate, BigDecimal.ZERO,
+                    BigDecimal.ZERO, zero, zero, zero, mc, true);
+            repaymentPeriod.getInterestPeriods().add(interestPausePeriod);
+            final InterestPeriod interestAfterPausePeriod = new 
InterestPeriod(repaymentPeriod, endDate, originalDueDate, BigDecimal.ZERO,
+                    BigDecimal.ZERO, zero, zero, zero, mc, false);
+            repaymentPeriod.getInterestPeriods().add(interestAfterPausePeriod);
+        }
+
+        if (fromDate.isAfter(originalFromDate) && 
endDate.isAfter(originalDueDate)) {
+            previousInterestPeriod.setDueDate(newDueDate);
+            final InterestPeriod interestPausePeriod = new 
InterestPeriod(repaymentPeriod, newDueDate, originalDueDate, BigDecimal.ZERO,
+                    BigDecimal.ZERO, zero, zero, zero, mc, true);
+            repaymentPeriod.getInterestPeriods().add(interestPausePeriod);
+        }
+
+        if (fromDate.isBefore(originalFromDate) && 
endDate.isBefore(originalDueDate)) {
+            repaymentPeriod.getInterestPeriods().clear();
+            final InterestPeriod interestPausePeriod = new 
InterestPeriod(repaymentPeriod, newDueDate, originalDueDate, BigDecimal.ZERO,
+                    BigDecimal.ZERO, zero, zero, zero, mc, true);
+            repaymentPeriod.getInterestPeriods().add(interestPausePeriod);
+            InterestPeriod interestAfterPausePeriod = new 
InterestPeriod(repaymentPeriod, endDate, originalDueDate, BigDecimal.ZERO,
+                    BigDecimal.ZERO, zero, zero, zero, mc, false);
+            repaymentPeriod.getInterestPeriods().add(interestAfterPausePeriod);
+        }
+    }
+
+    private InterestPeriod findPreviousInterestPeriod(final RepaymentPeriod 
repaymentPeriod, final LocalDate date) {
+        if (date.isAfter(repaymentPeriod.getFromDate())) {
+            return 
repaymentPeriod.getInterestPeriods().get(repaymentPeriod.getInterestPeriods().size()
 - 1);
+        } else {
+            return repaymentPeriod.getInterestPeriods().stream()
+                    .filter(ip -> date.isAfter(ip.getFromDate()) && 
!date.isAfter(ip.getDueDate())).reduce((first, second) -> second)
+                    .orElse(repaymentPeriod.getInterestPeriods().get(0));
+        }
+    }
+
     public Money getTotalDueInterest() {
         return repaymentPeriods().stream().flatMap(rp -> 
rp.getInterestPeriods().stream().map(InterestPeriod::getCalculatedDueInterest))
                 .reduce(zero(), Money::plus);
@@ -282,4 +338,10 @@ public class ProgressiveLoanInterestScheduleModel {
             }
         }
     }
+
+    private LocalDate calculateNewDueDate(final InterestPeriod 
previousInterestPeriod, final LocalDate date) {
+        return date.isBefore(previousInterestPeriod.getFromDate()) ? 
previousInterestPeriod.getFromDate()
+                : date.isAfter(previousInterestPeriod.getDueDate()) ? 
previousInterestPeriod.getDueDate() : date;
+    }
+
 }
diff --git 
a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/data/RepaymentPeriod.java
 
b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/data/RepaymentPeriod.java
index 5ecd47ca6..9edba49c0 100644
--- 
a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/data/RepaymentPeriod.java
+++ 
b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/data/RepaymentPeriod.java
@@ -73,7 +73,7 @@ public class RepaymentPeriod {
         this.interestPeriods = new ArrayList<>();
         // There is always at least 1 interest period, by default with same 
from-due date as repayment period
         getInterestPeriods().add(new InterestPeriod(this, getFromDate(), 
getDueDate(), BigDecimal.ZERO, BigDecimal.ZERO, getZero(mc),
-                getZero(mc), getZero(mc), mc));
+                getZero(mc), getZero(mc), mc, false));
         this.paidInterest = getZero(mc);
         this.paidPrincipal = getZero(mc);
     }
diff --git 
a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/ProgressiveLoanScheduleGenerator.java
 
b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/ProgressiveLoanScheduleGenerator.java
index d6053752c..18126360b 100644
--- 
a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/ProgressiveLoanScheduleGenerator.java
+++ 
b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/ProgressiveLoanScheduleGenerator.java
@@ -209,16 +209,22 @@ public class ProgressiveLoanScheduleGenerator implements 
LoanScheduleGenerator {
 
     private void applyInterestRateChangesOnPeriod(final LoanApplicationTerms 
loanApplicationTerms,
             final LoanScheduleModelRepaymentPeriod repaymentPeriod, final 
ProgressiveLoanInterestScheduleModel interestScheduleModel) {
-        if (loanApplicationTerms.getLoanTermVariations() != null) {
-            for (var interestRateChange : 
loanApplicationTerms.getLoanTermVariations().getInterestRateFromInstallment()) {
-                final LocalDate interestRateSubmittedOnDate = 
interestRateChange.getTermVariationApplicableFrom();
-                final BigDecimal newInterestRate = 
interestRateChange.getDecimalValue();
-                if 
(interestRateSubmittedOnDate.isAfter(repaymentPeriod.getFromDate())
-                        && 
!interestRateSubmittedOnDate.isAfter(repaymentPeriod.getDueDate())) {
-                    emiCalculator.changeInterestRate(interestScheduleModel, 
interestRateSubmittedOnDate, newInterestRate);
-                }
-            }
+        if (loanApplicationTerms.getLoanTermVariations() == null) {
+            return;
         }
+
+        
loanApplicationTerms.getLoanTermVariations().getInterestRateFromInstallment().stream()
+                .filter(change -> 
isDateWithinPeriod(change.getTermVariationApplicableFrom(), repaymentPeriod))
+                .forEach(change -> 
emiCalculator.changeInterestRate(interestScheduleModel, 
change.getTermVariationApplicableFrom(),
+                        change.getDecimalValue()));
+
+        
loanApplicationTerms.getLoanTermVariations().getInterestPauseVariations().stream()
+                .filter(pause -> 
isDateWithinPeriod(pause.getTermVariationApplicableFrom(), 
repaymentPeriod)).forEach(pause -> emiCalculator
+                        .applyInterestPause(interestScheduleModel, 
pause.getTermVariationApplicableFrom(), pause.getDateValue()));
+    }
+
+    private boolean isDateWithinPeriod(final LocalDate date, final 
LoanScheduleModelRepaymentPeriod period) {
+        return date.isAfter(period.getFromDate()) && 
!date.isAfter(period.getDueDate());
     }
 
     private void prepareDisbursementsOnLoanApplicationTerms(final 
LoanApplicationTerms loanApplicationTerms) {
diff --git 
a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/calc/EMICalculator.java
 
b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/calc/EMICalculator.java
index 193e859da..f9b206a69 100644
--- 
a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/calc/EMICalculator.java
+++ 
b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/calc/EMICalculator.java
@@ -77,4 +77,6 @@ public interface EMICalculator {
     OutstandingDetails 
getOutstandingAmountsTillDate(ProgressiveLoanInterestScheduleModel model, 
LocalDate targetDate);
 
     Money getSumOfDueInterestsOnDate(ProgressiveLoanInterestScheduleModel 
scheduleModel, LocalDate subjectDate);
+
+    void applyInterestPause(ProgressiveLoanInterestScheduleModel 
scheduleModel, LocalDate fromDate, LocalDate endDate);
 }
diff --git 
a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/calc/ProgressiveEMICalculator.java
 
b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/calc/ProgressiveEMICalculator.java
index e9d67f72d..fa349cbf0 100644
--- 
a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/calc/ProgressiveEMICalculator.java
+++ 
b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/calc/ProgressiveEMICalculator.java
@@ -792,6 +792,20 @@ public final class ProgressiveEMICalculator implements 
EMICalculator {
                 .reduce(scheduleModel.zero(), Money::add); //
     }
 
+    @Override
+    public void applyInterestPause(final ProgressiveLoanInterestScheduleModel 
scheduleModel, final LocalDate fromDate,
+            final LocalDate endDate) {
+        scheduleModel.updateInterestPeriodsForInterestPause(fromDate, endDate)
+                .ifPresent(repaymentPeriod -> 
calculateRateFactorsForInterestPause(scheduleModel, 
repaymentPeriod.getFromDate()));
+    }
+
+    private void calculateRateFactorsForInterestPause(final 
ProgressiveLoanInterestScheduleModel scheduleModel, final LocalDate startDate) {
+        final List<RepaymentPeriod> relatedRepaymentPeriods = 
scheduleModel.getRelatedRepaymentPeriods(startDate);
+        calculateRateFactorForPeriods(relatedRepaymentPeriods, scheduleModel);
+        calculateOutstandingBalance(scheduleModel);
+        calculateLastUnpaidRepaymentPeriodEMI(scheduleModel);
+    }
+
     private long getUncountablePeriods(final List<RepaymentPeriod> 
relatedRepaymentPeriods, final Money originalEmi) {
         return relatedRepaymentPeriods.stream() //
                 .filter(repaymentPeriod -> 
originalEmi.isLessThan(repaymentPeriod.getTotalPaidAmount())) //
diff --git 
a/fineract-progressive-loan/src/test/java/org/apache/fineract/portfolio/loanproduct/calc/ProgressiveEMICalculatorTest.java
 
b/fineract-progressive-loan/src/test/java/org/apache/fineract/portfolio/loanproduct/calc/ProgressiveEMICalculatorTest.java
index 852e778a6..694cb11c9 100644
--- 
a/fineract-progressive-loan/src/test/java/org/apache/fineract/portfolio/loanproduct/calc/ProgressiveEMICalculatorTest.java
+++ 
b/fineract-progressive-loan/src/test/java/org/apache/fineract/portfolio/loanproduct/calc/ProgressiveEMICalculatorTest.java
@@ -1342,6 +1342,82 @@ class ProgressiveEMICalculatorTest {
         checkDailyInterest(interestModel, dueDate, startDay, 31, 0.38, 9.03);
     }
 
+    @Test
+    public void 
test_singleInterestPauseAmt100_dayInYears360_daysInMonth30_repayEvery1Month() {
+        final List<LoanScheduleModelRepaymentPeriod> expectedRepaymentPeriods 
= List.of(
+                repayment(1, LocalDate.of(2024, 1, 1), LocalDate.of(2024, 2, 
1)),
+                repayment(2, LocalDate.of(2024, 2, 1), LocalDate.of(2024, 3, 
1)),
+                repayment(3, LocalDate.of(2024, 3, 1), LocalDate.of(2024, 4, 
1)),
+                repayment(4, LocalDate.of(2024, 4, 1), LocalDate.of(2024, 5, 
1)),
+                repayment(5, LocalDate.of(2024, 5, 1), LocalDate.of(2024, 6, 
1)),
+                repayment(6, LocalDate.of(2024, 6, 1), LocalDate.of(2024, 7, 
1)));
+
+        final BigDecimal interestRate = BigDecimal.valueOf(7.0);
+        final Integer installmentAmountInMultiplesOf = null;
+
+        
Mockito.when(loanProductRelatedDetail.getAnnualNominalInterestRate()).thenReturn(interestRate);
+        
Mockito.when(loanProductRelatedDetail.getDaysInYearType()).thenReturn(DaysInYearType.DAYS_360.getValue());
+        
Mockito.when(loanProductRelatedDetail.getDaysInMonthType()).thenReturn(DaysInMonthType.DAYS_30.getValue());
+        
Mockito.when(loanProductRelatedDetail.getRepaymentPeriodFrequencyType()).thenReturn(PeriodFrequencyType.MONTHS);
+        Mockito.when(loanProductRelatedDetail.getRepayEvery()).thenReturn(1);
+        
Mockito.when(loanProductRelatedDetail.getCurrencyData()).thenReturn(currency);
+
+        final ProgressiveLoanInterestScheduleModel interestSchedule = 
emiCalculator.generatePeriodInterestScheduleModel(
+                expectedRepaymentPeriods, loanProductRelatedDetail, 
loanTermVariations, installmentAmountInMultiplesOf, mc);
+
+        final Money disbursedAmount = toMoney(100.0);
+        emiCalculator.addDisbursement(interestSchedule, LocalDate.of(2024, 1, 
1), disbursedAmount);
+        emiCalculator.applyInterestPause(interestSchedule, LocalDate.of(2024, 
2, 5), LocalDate.of(2024, 2, 10));
+
+        checkPeriod(interestSchedule, 0, 0, 17.01, 0.0, 0.0, 0.58, 16.43, 
83.57);
+        checkPeriod(interestSchedule, 0, 1, 17.01, 0.005833333333, 0.58, 
16.43, 83.57);
+        checkPeriod(interestSchedule, 1, 0, 17.01, 0.000603448276, 0.05, 0.39, 
16.62, 66.95);
+        checkPeriod(interestSchedule, 1, 1, 17.01, 0.001206896552, 0.0, 0.39, 
16.62, 66.95);
+        checkPeriod(interestSchedule, 1, 2, 17.01, 0.004022988506, 0.34, 0.39, 
16.62, 66.95);
+        checkPeriod(interestSchedule, 2, 0, 17.01, 0.005833333333, 0.39, 0.39, 
16.62, 50.33);
+        checkPeriod(interestSchedule, 3, 0, 17.01, 0.005833333333, 0.29, 0.29, 
16.72, 33.61);
+        checkPeriod(interestSchedule, 4, 0, 17.01, 0.005833333333, 0.2, 0.2, 
16.81, 16.8);
+        checkPeriod(interestSchedule, 5, 0, 16.9, 0.005833333333, 0.1, 0.1, 
16.8, 0.0);
+    }
+
+    @Test
+    public void 
test_interestPauseBetweenTwoPeriodsAmt100_dayInYears360_daysInMonth30_repayEvery1Month()
 {
+        final List<LoanScheduleModelRepaymentPeriod> expectedRepaymentPeriods 
= List.of(
+                repayment(1, LocalDate.of(2024, 1, 1), LocalDate.of(2024, 2, 
1)),
+                repayment(2, LocalDate.of(2024, 2, 1), LocalDate.of(2024, 3, 
1)),
+                repayment(3, LocalDate.of(2024, 3, 1), LocalDate.of(2024, 4, 
1)),
+                repayment(4, LocalDate.of(2024, 4, 1), LocalDate.of(2024, 5, 
1)),
+                repayment(5, LocalDate.of(2024, 5, 1), LocalDate.of(2024, 6, 
1)),
+                repayment(6, LocalDate.of(2024, 6, 1), LocalDate.of(2024, 7, 
1)));
+
+        final BigDecimal interestRate = BigDecimal.valueOf(7.0);
+        final Integer installmentAmountInMultiplesOf = null;
+
+        
Mockito.when(loanProductRelatedDetail.getAnnualNominalInterestRate()).thenReturn(interestRate);
+        
Mockito.when(loanProductRelatedDetail.getDaysInYearType()).thenReturn(DaysInYearType.DAYS_360.getValue());
+        
Mockito.when(loanProductRelatedDetail.getDaysInMonthType()).thenReturn(DaysInMonthType.DAYS_30.getValue());
+        
Mockito.when(loanProductRelatedDetail.getRepaymentPeriodFrequencyType()).thenReturn(PeriodFrequencyType.MONTHS);
+        Mockito.when(loanProductRelatedDetail.getRepayEvery()).thenReturn(1);
+        
Mockito.when(loanProductRelatedDetail.getCurrencyData()).thenReturn(currency);
+
+        final ProgressiveLoanInterestScheduleModel interestSchedule = 
emiCalculator.generatePeriodInterestScheduleModel(
+                expectedRepaymentPeriods, loanProductRelatedDetail, 
loanTermVariations, installmentAmountInMultiplesOf, mc);
+
+        final Money disbursedAmount = toMoney(100.0);
+        emiCalculator.addDisbursement(interestSchedule, LocalDate.of(2024, 1, 
1), disbursedAmount);
+        emiCalculator.applyInterestPause(interestSchedule, LocalDate.of(2024, 
2, 10), LocalDate.of(2024, 3, 10));
+
+        checkPeriod(interestSchedule, 0, 0, 17.01, 0.0, 0.0, 0.58, 16.43, 
83.57);
+        checkPeriod(interestSchedule, 0, 1, 17.01, 0.005833333333, 0.58, 
16.43, 83.57);
+        checkPeriod(interestSchedule, 1, 0, 17.01, 0.001609195402, 0.13, 0.13, 
16.88, 66.69);
+        checkPeriod(interestSchedule, 1, 1, 17.01, 0.004224137931, 0.0, 0.13, 
16.88, 66.69);
+        checkPeriod(interestSchedule, 2, 0, 17.01, 0.005833333333, 0.0, 0.28, 
16.73, 49.96);
+        checkPeriod(interestSchedule, 2, 1, 17.01, 0.004139784946, 0.28, 0.28, 
16.73, 49.96);
+        checkPeriod(interestSchedule, 3, 0, 17.01, 0.005833333333, 0.29, 0.29, 
16.72, 33.24);
+        checkPeriod(interestSchedule, 4, 0, 17.01, 0.005833333333, 0.19, 0.19, 
16.82, 16.42);
+        checkPeriod(interestSchedule, 5, 0, 16.52, 0.005833333333, 0.1, 0.1, 
16.42, 0.0);
+    }
+
     @Test
     public void 
test_reschedule_disbursedAmt100_dayInYears360_daysInMonth30_repayEvery1Month() {
         final List<LoanScheduleModelRepaymentPeriod> expectedRepaymentPeriods 
= List.of(
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAssemblerImpl.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAssemblerImpl.java
index 8c08bae3a..d56285305 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAssemblerImpl.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAssemblerImpl.java
@@ -139,15 +139,36 @@ public class LoanAssemblerImpl implements LoanAssembler {
 
     @Override
     public Loan assembleFrom(final Long accountId) {
-        final Loan loanAccount = 
this.loanRepository.findOneWithNotFoundDetection(accountId, true);
-        loanAccount.setHelpers(defaultLoanLifecycleStateMachine, 
this.loanRepaymentScheduleTransactionProcessorFactory);
+        return assembleFrom(accountId, true);
+    }
+
+    @Override
+    public Loan assembleFrom(final Long accountId, final boolean 
loadLazyCollections) {
+        final Loan loanAccount = 
loanRepository.findOneWithNotFoundDetection(accountId, loadLazyCollections);
+        setHelpers(loanAccount);
+
+        return loanAccount;
+    }
+
+    @Override
+    public Loan assembleFrom(final ExternalId externalId) {
+        final Loan loanAccount = 
loanRepository.findOneWithNotFoundDetection(externalId, true);
+        setHelpers(loanAccount);
+
+        return loanAccount;
+    }
+
+    @Override
+    public Loan assembleFrom(final ExternalId externalId, final boolean 
loadLazyCollections) {
+        final Loan loanAccount = 
loanRepository.findOneWithNotFoundDetection(externalId, loadLazyCollections);
+        setHelpers(loanAccount);
 
         return loanAccount;
     }
 
     @Override
     public void setHelpers(final Loan loanAccount) {
-        loanAccount.setHelpers(defaultLoanLifecycleStateMachine, 
this.loanRepaymentScheduleTransactionProcessorFactory);
+        loanAccount.setHelpers(defaultLoanLifecycleStateMachine, 
loanRepaymentScheduleTransactionProcessorFactory);
     }
 
     @Override
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/starter/LoanAccountConfiguration.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/starter/LoanAccountConfiguration.java
index b7b9a955d..1c1e75fe7 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/starter/LoanAccountConfiguration.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/starter/LoanAccountConfiguration.java
@@ -471,7 +471,7 @@ public class LoanAccountConfiguration {
     @Bean
     @ConditionalOnMissingBean(InterestPauseWritePlatformService.class)
     public InterestPauseWritePlatformService 
interestPauseWritePlatformService(LoanTermVariationsRepository 
loanTermVariationsRepository,
-            LoanRepositoryWrapper loanRepositoryWrapper) {
-        return new 
InterestPauseWritePlatformServiceImpl(loanTermVariationsRepository, 
loanRepositoryWrapper);
+            LoanRepositoryWrapper loanRepositoryWrapper, LoanAssembler 
loanAssembler) {
+        return new 
InterestPauseWritePlatformServiceImpl(loanTermVariationsRepository, 
loanRepositoryWrapper, loanAssembler);
     }
 }

Reply via email to