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

arnold 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 507e63435c FINERACT-2497: Vertical repayment alignment fix
507e63435c is described below

commit 507e63435ca912ae9d5d2b927f6b317905379d38
Author: Adam Saghy <[email protected]>
AuthorDate: Tue Feb 24 13:58:55 2026 +0100

    FINERACT-2497: Vertical repayment alignment fix
---
 .../test/data/loanproduct/DefaultLoanProduct.java  |   1 +
 .../fineract/test/factory/LoanRequestFactory.java  |   2 +-
 .../fineract/test/support/TestContextKey.java      |   1 +
 .../global/LoanProductGlobalInitializerStep.java   |  30 ++++
 .../test/resources/features/LoanRepayment.feature  | 174 +++++++++++++++++++++
 .../domain/LoanRepaymentScheduleInstallment.java   |  10 ++
 .../loanschedule/domain/LoanApplicationTerms.java  |   2 +-
 .../loanproduct/data/LoanConfigurationDetails.java |   7 +-
 .../domain/ILoanConfigurationDetails.java          |   3 +
 ...dvancedPaymentScheduleTransactionProcessor.java | 107 ++++++++++---
 .../mapper/LoanConfigurationDetailsMapper.java     |   2 +-
 .../loanproduct/calc/ProgressiveEMICalculator.java |  12 +-
 12 files changed, 318 insertions(+), 33 deletions(-)

diff --git 
a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/data/loanproduct/DefaultLoanProduct.java
 
b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/data/loanproduct/DefaultLoanProduct.java
index 331dba0206..6bbe66d4fb 100644
--- 
a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/data/loanproduct/DefaultLoanProduct.java
+++ 
b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/data/loanproduct/DefaultLoanProduct.java
@@ -191,6 +191,7 @@ public enum DefaultLoanProduct implements LoanProduct {
     
LP2_ADV_PYMNT_INT_DAILY_EMI_360_30_INT_RECALC_DAILY_MULTIDISB_FULL_TERM_TRANCHE_ZERO_INT_CHARGE_OFF,
 //
     
LP2_ADV_PYMNT_INT_DAILY_EMI_360_30_INT_RECALC_DAILY_MULTIDISB_FULL_TERM_TRANCHE_ACCELERATE_MATURITY,
 //
     LP2_PROGRESSIVE_ADVANCED_PAYMENT_ALLOCATION_BUYDOWN_FEES_FEE_INCOME, //
+    
LP2_DOWNPAYMENT_ADV_PMT_ALLOC_PROGRESSIVE_LOAN_SCHEDULE_VERTICAL_INTEREST_RECALC,
 //
     ;
 
     @Override
diff --git 
a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/factory/LoanRequestFactory.java
 
b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/factory/LoanRequestFactory.java
index 77c3089fe1..f3c2d4385e 100644
--- 
a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/factory/LoanRequestFactory.java
+++ 
b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/factory/LoanRequestFactory.java
@@ -120,7 +120,7 @@ public class LoanRequestFactory {
                 
.transactionProcessingStrategyCode(DEFAULT_TRANSACTION_PROCESSING_STRATEGY_CODE)//
                 .dateFormat(DATE_FORMAT)//
                 .graceOnArrearsAgeing(3)//
-                .maxOutstandingLoanBalance(new BigDecimal(10000));
+        ;
     }
 
     public PostLoansRequest defaultProgressiveLoansRequest(final Long 
clientId) {
diff --git 
a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/support/TestContextKey.java
 
b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/support/TestContextKey.java
index af71aeeefe..7820e74d0a 100644
--- 
a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/support/TestContextKey.java
+++ 
b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/support/TestContextKey.java
@@ -313,4 +313,5 @@ public abstract class TestContextKey {
     public static final String 
DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PYMNT_INT_DAILY_EMI_360_30_INT_RECALC_DAILY_MULTIDISB_FULL_TERM_TRANCHE_ZERO_CHARGE_OFF
 = 
"loanProductCreateResponseLP2AdvancedPaymentInterestDailyEmi36030InterestRecalculationDailyMultidisburseFullTermTrancheZeroIntChargeOff";
     public static final String 
DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PYMNT_INT_DAILY_EMI_360_30_INT_RECALC_DAILY_MULTIDISB_FULL_TERM_TRANCHE_ACCELERATE_MATURITY
 = 
"loanProductCreateResponseLP2AdvancedPaymentInterestDailyEmi36030InterestRecalculationDailyMultidisburseFullTermTrancheAccelerateMaturity";
     public static final String OFFICE_CREATE_RESPONSE = "officeCreateResponse";
+    public static final String 
DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_DOWNPAYMENT_ADVANCED_PAYMENT_ALLOCATION_PROGRESSIVE_LOAN_SCHEDULE_VERTICAL_INTEREST_RECALC
 = 
"loanProductCreateResponseLP2DownPaymentAdvancedPaymentAllocationProgressiveLoanScheduleVerticalInterestRecalc";
 }
diff --git 
a/fineract-e2e-tests-runner/src/test/java/org/apache/fineract/test/initializer/global/LoanProductGlobalInitializerStep.java
 
b/fineract-e2e-tests-runner/src/test/java/org/apache/fineract/test/initializer/global/LoanProductGlobalInitializerStep.java
index 04fdb78c46..687762fb7b 100644
--- 
a/fineract-e2e-tests-runner/src/test/java/org/apache/fineract/test/initializer/global/LoanProductGlobalInitializerStep.java
+++ 
b/fineract-e2e-tests-runner/src/test/java/org/apache/fineract/test/initializer/global/LoanProductGlobalInitializerStep.java
@@ -4609,6 +4609,36 @@ public class LoanProductGlobalInitializerStep implements 
FineractGlobalInitializ
                 
loanProductsRequestLP2ProgressiveAdvPaymentBuyDownFeesFeeIncome);
         
TestContext.INSTANCE.set(TestContextKey.DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_PROGRESSIVE_ADV_PYMNT_BUYDOWN_FEES_FEE_INCOME,
                 
responseLoanProductsRequestLP2ProgressiveAdvPaymentBuyDownFeesFeeIncome);
+
+        // LP2 with Down-payment + advanced payment allocation + progressive 
loan schedule + vertical
+        // (LP2_DOWNPAYMENT_ADV_PMT_ALLOC_PROGRESSIVE_LOAN_SCHEDULE_VERTICAL)
+        String name179 = 
DefaultLoanProduct.LP2_DOWNPAYMENT_ADV_PMT_ALLOC_PROGRESSIVE_LOAN_SCHEDULE_VERTICAL_INTEREST_RECALC.getName();
+        PostLoanProductsRequest 
loanProductsRequestDownPaymentAdvPaymentAllocationProgressiveLoanScheduleVerticalInterestRecalc
 = loanProductsRequestFactory
+                .defaultLoanProductsRequestLP2Emi()//
+                .name(name179)//
+                
.loanScheduleProcessingType("VERTICAL").daysInYearType(DaysInYearType.ACTUAL.value)//
+                .daysInMonthType(DaysInMonthType.ACTUAL.value)//
+                .isInterestRecalculationEnabled(true)//
+                .preClosureInterestCalculationStrategy(1)//
+                .rescheduleStrategyMethod(4)//
+                .interestRecalculationCompoundingMethod(0)//
+                .recalculationRestFrequencyType(2)//
+                .recalculationRestFrequencyInterval(1)//
+                .paymentAllocation(List.of(//
+                        createPaymentAllocation("DEFAULT", 
"NEXT_INSTALLMENT"), //
+                        createPaymentAllocation("REPAYMENT", 
"NEXT_INSTALLMENT"))) //
+                .chargeOffBehaviour(ACCELERATE_MATURITY.value)//
+                .multiDisburseLoan(true)//
+                .disallowExpectedDisbursements(true)//
+                .allowFullTermForTranche(true)//
+                .maxTrancheCount(10)//
+                .maxPrincipal(25000000.0)//
+                .outstandingLoanBalance(25000000.0);//
+        PostLoanProductsResponse 
responseLoanProductsRequestDownPaymentAdvPaymentAllocationProgressiveLoanScheduleVerticalInterestRecalc
 = createLoanProductIdempotent(
+                
loanProductsRequestDownPaymentAdvPaymentAllocationProgressiveLoanScheduleVerticalInterestRecalc);
+        TestContext.INSTANCE.set(
+                
TestContextKey.DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_DOWNPAYMENT_ADVANCED_PAYMENT_ALLOCATION_PROGRESSIVE_LOAN_SCHEDULE_VERTICAL_INTEREST_RECALC,
+                
responseLoanProductsRequestDownPaymentAdvPaymentAllocationProgressiveLoanScheduleVerticalInterestRecalc);
     }
 
     public static AdvancedPaymentData createPaymentAllocation(String 
transactionType, String futureInstallmentAllocationRule,
diff --git 
a/fineract-e2e-tests-runner/src/test/resources/features/LoanRepayment.feature 
b/fineract-e2e-tests-runner/src/test/resources/features/LoanRepayment.feature
index 53e9ffee7e..bd1f438376 100644
--- 
a/fineract-e2e-tests-runner/src/test/resources/features/LoanRepayment.feature
+++ 
b/fineract-e2e-tests-runner/src/test/resources/features/LoanRepayment.feature
@@ -6751,3 +6751,177 @@ Feature: LoanRepayment
     Then Customer undo "1"th transaction made on "01 February 2024" results a 
403 error and "update not allowed as loan transaction is linked to other 
transactions" error message
     When Loan Pay-off is made on "15 March 2024"
     Then Loan is closed with zero outstanding balance and it's all 
installments have obligations met
+
+  @TestRailId:C4683 @AdvancedPaymentAllocation @ProgressiveLoanSchedule
+  Scenario: Verify AdvancedPaymentAllocation behaviour: 
loanScheduleProcessingType-vertical, prepayment with NEXT_INSTALLMENT strategy
+    When Admin sets the business date to "23 February 2026"
+    When Admin creates a client with random data
+    When Admin set 
"LP2_DOWNPAYMENT_ADV_PMT_ALLOC_PROGRESSIVE_LOAN_SCHEDULE_VERTICAL_INTEREST_RECALC"
 loan product "REPAYMENT" transaction type to "NEXT_INSTALLMENT" future 
installment allocation rule
+    When 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_DOWNPAYMENT_ADV_PMT_ALLOC_PROGRESSIVE_LOAN_SCHEDULE_VERTICAL_INTEREST_RECALC
 | 01 January 2026   | 25000000       | 12                     | 
DECLINING_BALANCE  | DAILY                       | EQUAL_INSTALLMENTS | 12      
          | MONTHS                | 1              | MONTHS                 | 
12                  | 0                       | 0                      | 0      
             | ADVANCED_PAYMENT_ALLOCATION |
+    And Admin successfully approves the loan on "01 January 2026" with 
"25000000" amount and expected disbursement date on "01 January 2026"
+    When Admin successfully disburse the loan on "01 January 2026" with 
"25000000" EUR transaction amount
+    Then Loan Repayment schedule has 12 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 2026   |           | 25000000.0      |          
     |             | 0.0  |           | 0.0          | 0.0            |         
         |         |                |
+      | 1  | 31   | 01 February 2026  |           | 23034153.81     | 
1965846.19    | 254794.52   | 0.0  | 0.0       | 2220640.71   | 0.0            
| 0.0              | 0.0     | 2220640.71     |
+      | 2  | 28   | 01 March 2026     |           | 21039772.25     | 
1994381.56    | 226259.15   | 0.0  | 0.0       | 2220640.71   | 0.0            
| 0.0              | 0.0     | 2220640.71     |
+      | 3  | 31   | 01 April 2026     |           | 19033564.29     | 
2006207.96    | 214432.75   | 0.0  | 0.0       | 2220640.71   | 0.0            
| 0.0              | 0.0     | 2220640.71     |
+      | 4  | 30   | 01 May 2026       |           | 17000651.89     | 
2032912.4     | 187728.31   | 0.0  | 0.0       | 2220640.71   | 0.0            
| 0.0              | 0.0     | 2220640.71     |
+      | 5  | 31   | 01 June 2026      |           | 14953278.1      | 
2047373.79    | 173266.92   | 0.0  | 0.0       | 2220640.71   | 0.0            
| 0.0              | 0.0     | 2220640.71     |
+      | 6  | 30   | 01 July 2026      |           | 12880121.78     | 
2073156.32    | 147484.39   | 0.0  | 0.0       | 2220640.71   | 0.0            
| 0.0              | 0.0     | 2220640.71     |
+      | 7  | 31   | 01 August 2026    |           | 10790752.45     | 
2089369.33    | 131271.38   | 0.0  | 0.0       | 2220640.71   | 0.0            
| 0.0              | 0.0     | 2220640.71     |
+      | 8  | 31   | 01 September 2026 |           |  8680088.72     | 
2110663.73    | 109976.98   | 0.0  | 0.0       | 2220640.71   | 0.0            
| 0.0              | 0.0     | 2220640.71     |
+      | 9  | 30   | 01 October 2026   |           |  6545059.84     | 
2135028.88    |  85611.83   | 0.0  | 0.0       | 2220640.71   | 0.0            
| 0.0              | 0.0     | 2220640.71     |
+      | 10 | 31   | 01 November 2026  |           |  4391124.95     | 
2153934.89    |  66705.82   | 0.0  | 0.0       | 2220640.71   | 0.0            
| 0.0              | 0.0     | 2220640.71     |
+      | 11 | 30   | 01 December 2026  |           |  2213793.97     | 
2177330.98    |  43309.73   | 0.0  | 0.0       | 2220640.71   | 0.0            
| 0.0              | 0.0     | 2220640.71     |
+      | 12 | 31   | 01 January 2027   |           |        0.0      | 
2213793.97    |  22562.5    | 0.0  | 0.0       | 2236356.47   | 0.0            
| 0.0              | 0.0     | 2236356.47     |
+    Then Loan Repayment schedule has the following data in Total row:
+      | Principal due     | Interest      | Fees | Penalties | Due           | 
Paid | In advance | Late | Outstanding    |
+      | 25000000.0        | 1663404.28    | 0.0  | 0.0       | 26663404.28   | 
0.0  | 0.0        | 0.0  | 26663404.28    |
+    Then Loan Transactions tab has the following data:
+      | Transaction date  | Transaction Type | Amount     | Principal | 
Interest | Fees | Penalties | Loan Balance     |
+      | 01 January 2026   | Disbursement     | 25000000.0 | 0.0       | 0.0    
  | 0.0  | 0.0       | 25000000.0       |
+    When Loan Pay-off is made on "23 February 2026"
+    Then Loan Repayment schedule has 12 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 2026   |                  | 25000000.0      |   
            |             | 0.0  |           | 0.0          | 0.0            |  
                |              |                |
+      | 1  | 31   | 01 February 2026  | 23 February 2026 | 23034153.81     | 
1965846.19    | 254794.52   | 0.0  | 0.0       | 2220640.71   | 2220640.71     
| 0.0              | 2220640.71   | 0.0            |
+      | 2  | 28   | 01 March 2026     | 23 February 2026 | 20813513.1      | 
2220640.71    |      0.0    | 0.0  | 0.0       | 2220640.71   | 2220640.71     
| 2220640.71       | 0.0          | 0.0            |
+      | 3  | 31   | 01 April 2026     | 23 February 2026 | 18592872.39     | 
2220640.71    |      0.0    | 0.0  | 0.0       | 2220640.71   | 2220640.71     
| 2220640.71       | 0.0          | 0.0            |
+      | 4  | 30   | 01 May 2026       | 23 February 2026 | 16372231.68     | 
2220640.71    |      0.0    | 0.0  | 0.0       | 2220640.71   | 2220640.71     
| 2220640.71       | 0.0          | 0.0            |
+      | 5  | 31   | 01 June 2026      | 23 February 2026 | 14151590.97     | 
2220640.71    |      0.0    | 0.0  | 0.0       | 2220640.71   | 2220640.71     
| 2220640.71       | 0.0          | 0.0            |
+      | 6  | 30   | 01 July 2026      | 23 February 2026 | 11930950.26     | 
2220640.71    |      0.0    | 0.0  | 0.0       | 2220640.71   | 2220640.71     
| 2220640.71       | 0.0          | 0.0            |
+      | 7  | 31   | 01 August 2026    | 23 February 2026 |  9710309.55     | 
2220640.71    |      0.0    | 0.0  | 0.0       | 2220640.71   | 2220640.71     
| 2220640.71       | 0.0          | 0.0            |
+      | 8  | 31   | 01 September 2026 | 23 February 2026 |  7489668.84     | 
2220640.71    |      0.0    | 0.0  | 0.0       | 2220640.71   | 2220640.71     
| 2220640.71       | 0.0          | 0.0            |
+      | 9  | 30   | 01 October 2026   | 23 February 2026 |  5269028.13     | 
2220640.71    |      0.0    | 0.0  | 0.0       | 2220640.71   | 2220640.71     
| 2220640.71       | 0.0          | 0.0            |
+      | 10 | 31   | 01 November 2026  | 23 February 2026 |  3048387.42     | 
2220640.71    |      0.0    | 0.0  | 0.0       | 2220640.71   | 2220640.71     
| 2220640.71       | 0.0          | 0.0            |
+      | 11 | 30   | 01 December 2026  | 23 February 2026 |   827746.71     | 
2220640.71    |      0.0    | 0.0  | 0.0       | 2220640.71   | 2220640.71     
| 2220640.71       | 0.0          | 0.0            |
+      | 12 | 31   | 01 January 2027   | 23 February 2026 |        0.0      |  
827746.71    | 180821.92   | 0.0  | 0.0       | 1008568.63   | 1008568.63     | 
1008568.63       | 0.0          | 0.0            |
+    Then Loan Repayment schedule has the following data in Total row:
+      | Principal due     | Interest      | Fees | Penalties | Due           | 
Paid        | In advance   | Late        | Outstanding    |
+      | 25000000.0        | 435616.44     | 0.0  | 0.0       | 25435616.44   | 
25435616.44 | 23214975.73  | 2220640.71  | 0.0            |
+    Then Loan Transactions tab has the following data:
+      | Transaction date  | Transaction Type | Amount      | Principal  | 
Interest    | Fees | Penalties | Loan Balance     |
+      | 01 January 2026   | Disbursement     | 25000000.0  | 0.0        | 0.0  
       | 0.0  | 0.0       | 25000000.0       |
+      | 23 February 2026  | Repayment        | 25435616.44 | 25000000.0 | 
435616.44   | 0.0  | 0.0       |        0.0       |
+      | 23 February 2026  | Accrual          |   435616.44 |        0.0 | 
435616.44   | 0.0  | 0.0       |        0.0       |
+    Then Loan is closed with zero outstanding balance and it's all 
installments have obligations met
+    When Admin set 
"LP2_DOWNPAYMENT_ADV_PMT_ALLOC_PROGRESSIVE_LOAN_SCHEDULE_VERTICAL_INTEREST_RECALC"
 loan product "REPAYMENT" transaction type to "NEXT_INSTALLMENT" future 
installment allocation rule
+
+  @TestRailId:C4684 @AdvancedPaymentAllocation @ProgressiveLoanSchedule
+  Scenario: Verify AdvancedPaymentAllocation behaviour: 
loanScheduleProcessingType-vertical, prepayment with LAST_INSTALLMENT strategy
+    When Admin sets the business date to "23 February 2026"
+    When Admin creates a client with random data
+    When Admin set 
"LP2_DOWNPAYMENT_ADV_PMT_ALLOC_PROGRESSIVE_LOAN_SCHEDULE_VERTICAL_INTEREST_RECALC"
 loan product "REPAYMENT" transaction type to "LAST_INSTALLMENT" future 
installment allocation rule
+    When 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_DOWNPAYMENT_ADV_PMT_ALLOC_PROGRESSIVE_LOAN_SCHEDULE_VERTICAL_INTEREST_RECALC
 | 01 January 2026   | 25000000       | 12                     | 
DECLINING_BALANCE  | DAILY                       | EQUAL_INSTALLMENTS | 12      
          | MONTHS                | 1              | MONTHS                 | 
12                  | 0                       | 0                      | 0      
             | ADVANCED_PAYMENT_ALLOCATION |
+    And Admin successfully approves the loan on "01 January 2026" with 
"25000000" amount and expected disbursement date on "01 January 2026"
+    When Admin successfully disburse the loan on "01 January 2026" with 
"25000000" EUR transaction amount
+    Then Loan Repayment schedule has 12 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 2026   |           | 25000000.0      |          
     |             | 0.0  |           | 0.0          | 0.0            |         
         |         |                |
+      | 1  | 31   | 01 February 2026  |           | 23034153.81     | 
1965846.19    | 254794.52   | 0.0  | 0.0       | 2220640.71   | 0.0            
| 0.0              | 0.0     | 2220640.71     |
+      | 2  | 28   | 01 March 2026     |           | 21039772.25     | 
1994381.56    | 226259.15   | 0.0  | 0.0       | 2220640.71   | 0.0            
| 0.0              | 0.0     | 2220640.71     |
+      | 3  | 31   | 01 April 2026     |           | 19033564.29     | 
2006207.96    | 214432.75   | 0.0  | 0.0       | 2220640.71   | 0.0            
| 0.0              | 0.0     | 2220640.71     |
+      | 4  | 30   | 01 May 2026       |           | 17000651.89     | 
2032912.4     | 187728.31   | 0.0  | 0.0       | 2220640.71   | 0.0            
| 0.0              | 0.0     | 2220640.71     |
+      | 5  | 31   | 01 June 2026      |           | 14953278.1      | 
2047373.79    | 173266.92   | 0.0  | 0.0       | 2220640.71   | 0.0            
| 0.0              | 0.0     | 2220640.71     |
+      | 6  | 30   | 01 July 2026      |           | 12880121.78     | 
2073156.32    | 147484.39   | 0.0  | 0.0       | 2220640.71   | 0.0            
| 0.0              | 0.0     | 2220640.71     |
+      | 7  | 31   | 01 August 2026    |           | 10790752.45     | 
2089369.33    | 131271.38   | 0.0  | 0.0       | 2220640.71   | 0.0            
| 0.0              | 0.0     | 2220640.71     |
+      | 8  | 31   | 01 September 2026 |           |  8680088.72     | 
2110663.73    | 109976.98   | 0.0  | 0.0       | 2220640.71   | 0.0            
| 0.0              | 0.0     | 2220640.71     |
+      | 9  | 30   | 01 October 2026   |           |  6545059.84     | 
2135028.88    |  85611.83   | 0.0  | 0.0       | 2220640.71   | 0.0            
| 0.0              | 0.0     | 2220640.71     |
+      | 10 | 31   | 01 November 2026  |           |  4391124.95     | 
2153934.89    |  66705.82   | 0.0  | 0.0       | 2220640.71   | 0.0            
| 0.0              | 0.0     | 2220640.71     |
+      | 11 | 30   | 01 December 2026  |           |  2213793.97     | 
2177330.98    |  43309.73   | 0.0  | 0.0       | 2220640.71   | 0.0            
| 0.0              | 0.0     | 2220640.71     |
+      | 12 | 31   | 01 January 2027   |           |        0.0      | 
2213793.97    |  22562.5    | 0.0  | 0.0       | 2236356.47   | 0.0            
| 0.0              | 0.0     | 2236356.47     |
+    Then Loan Repayment schedule has the following data in Total row:
+      | Principal due     | Interest      | Fees | Penalties | Due           | 
Paid | In advance | Late | Outstanding    |
+      | 25000000.0        | 1663404.28    | 0.0  | 0.0       | 26663404.28   | 
0.0  | 0.0        | 0.0  | 26663404.28    |
+    Then Loan Transactions tab has the following data:
+      | Transaction date  | Transaction Type | Amount     | Principal | 
Interest | Fees | Penalties | Loan Balance     |
+      | 01 January 2026   | Disbursement     | 25000000.0 | 0.0       | 0.0    
  | 0.0  | 0.0       | 25000000.0       |
+    When Loan Pay-off is made on "23 February 2026"
+    Then Loan Repayment schedule has 12 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 2026   |                  | 25000000.0      |   
            |             | 0.0  |           | 0.0          | 0.0            |  
                |              |                |
+      | 1  | 31   | 01 February 2026  | 23 February 2026 | 23034153.81     | 
1965846.19    | 254794.52   | 0.0  | 0.0       | 2220640.71   | 2220640.71     
| 0.0              | 2220640.71   | 0.0            |
+      | 2  | 28   | 01 March 2026     | 23 February 2026 | 22206407.14     |  
827746.67    | 180821.92   | 0.0  | 0.0       | 1008568.59   | 1008568.59     | 
1008568.59       | 0.0          | 0.0            |
+      | 3  | 31   | 01 April 2026     | 23 February 2026 | 19985766.43     | 
2220640.71    |      0.0    | 0.0  | 0.0       | 2220640.71   | 2220640.71     
| 2220640.71       | 0.0          | 0.0            |
+      | 4  | 30   | 01 May 2026       | 23 February 2026 | 17765125.72     | 
2220640.71    |      0.0    | 0.0  | 0.0       | 2220640.71   | 2220640.71     
| 2220640.71       | 0.0          | 0.0            |
+      | 5  | 31   | 01 June 2026      | 23 February 2026 | 15544485.01     | 
2220640.71    |      0.0    | 0.0  | 0.0       | 2220640.71   | 2220640.71     
| 2220640.71       | 0.0          | 0.0            |
+      | 6  | 30   | 01 July 2026      | 23 February 2026 | 13323844.3      | 
2220640.71    |      0.0    | 0.0  | 0.0       | 2220640.71   | 2220640.71     
| 2220640.71       | 0.0          | 0.0            |
+      | 7  | 31   | 01 August 2026    | 23 February 2026 | 11103203.59     | 
2220640.71    |      0.0    | 0.0  | 0.0       | 2220640.71   | 2220640.71     
| 2220640.71       | 0.0          | 0.0            |
+      | 8  | 31   | 01 September 2026 | 23 February 2026 |  8882562.88     | 
2220640.71    |      0.0    | 0.0  | 0.0       | 2220640.71   | 2220640.71     
| 2220640.71       | 0.0          | 0.0            |
+      | 9  | 30   | 01 October 2026   | 23 February 2026 |  6661922.17     | 
2220640.71    |      0.0    | 0.0  | 0.0       | 2220640.71   | 2220640.71     
| 2220640.71       | 0.0          | 0.0            |
+      | 10 | 31   | 01 November 2026  | 23 February 2026 |  4441281.46     | 
2220640.71    |      0.0    | 0.0  | 0.0       | 2220640.71   | 2220640.71     
| 2220640.71       | 0.0          | 0.0            |
+      | 11 | 30   | 01 December 2026  | 23 February 2026 |  2220640.75     | 
2220640.71    |      0.0    | 0.0  | 0.0       | 2220640.71   | 2220640.71     
| 2220640.71       | 0.0          | 0.0            |
+      | 12 | 31   | 01 January 2027   | 23 February 2026 |        0.0      | 
2220640.75    |      0.0    | 0.0  | 0.0       | 2220640.75   | 2220640.75     
| 2220640.75       | 0.0          | 0.0            |
+    Then Loan Repayment schedule has the following data in Total row:
+      | Principal due     | Interest      | Fees | Penalties | Due           | 
Paid        | In advance   | Late        | Outstanding    |
+      | 25000000.0        | 435616.44     | 0.0  | 0.0       | 25435616.44   | 
25435616.44 | 23214975.73  | 2220640.71  | 0.0            |
+    Then Loan Transactions tab has the following data:
+      | Transaction date  | Transaction Type | Amount      | Principal  | 
Interest    | Fees | Penalties | Loan Balance     |
+      | 01 January 2026   | Disbursement     | 25000000.0  | 0.0        | 0.0  
       | 0.0  | 0.0       | 25000000.0       |
+      | 23 February 2026  | Repayment        | 25435616.44 | 25000000.0 | 
435616.44   | 0.0  | 0.0       |        0.0       |
+      | 23 February 2026  | Accrual          |   435616.44 |        0.0 | 
435616.44   | 0.0  | 0.0       |        0.0       |
+    Then Loan is closed with zero outstanding balance and it's all 
installments have obligations met
+    When Admin set 
"LP2_DOWNPAYMENT_ADV_PMT_ALLOC_PROGRESSIVE_LOAN_SCHEDULE_VERTICAL_INTEREST_RECALC"
 loan product "REPAYMENT" transaction type to "NEXT_INSTALLMENT" future 
installment allocation rule
+
+  @TestRailId:C4685 @AdvancedPaymentAllocation @ProgressiveLoanSchedule
+  Scenario: Verify AdvancedPaymentAllocation behaviour: 
loanScheduleProcessingType-vertical, prepayment with NEXT_LAST_INSTALLMENT 
strategy
+    When Admin sets the business date to "23 February 2026"
+    When Admin creates a client with random data
+    When Admin set 
"LP2_DOWNPAYMENT_ADV_PMT_ALLOC_PROGRESSIVE_LOAN_SCHEDULE_VERTICAL_INTEREST_RECALC"
 loan product "REPAYMENT" transaction type to "NEXT_LAST_INSTALLMENT" future 
installment allocation rule
+    When 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_DOWNPAYMENT_ADV_PMT_ALLOC_PROGRESSIVE_LOAN_SCHEDULE_VERTICAL_INTEREST_RECALC
 | 01 January 2026   | 25000000       | 12                     | 
DECLINING_BALANCE  | DAILY                       | EQUAL_INSTALLMENTS | 12      
          | MONTHS                | 1              | MONTHS                 | 
12                  | 0                       | 0                      | 0      
             | ADVANCED_PAYMENT_ALLOCATION |
+    And Admin successfully approves the loan on "01 January 2026" with 
"25000000" amount and expected disbursement date on "01 January 2026"
+    When Admin successfully disburse the loan on "01 January 2026" with 
"25000000" EUR transaction amount
+    Then Loan Repayment schedule has 12 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 2026   |           | 25000000.0      |          
     |             | 0.0  |           | 0.0          | 0.0            |         
         |         |                |
+      | 1  | 31   | 01 February 2026  |           | 23034153.81     | 
1965846.19    | 254794.52   | 0.0  | 0.0       | 2220640.71   | 0.0            
| 0.0              | 0.0     | 2220640.71     |
+      | 2  | 28   | 01 March 2026     |           | 21039772.25     | 
1994381.56    | 226259.15   | 0.0  | 0.0       | 2220640.71   | 0.0            
| 0.0              | 0.0     | 2220640.71     |
+      | 3  | 31   | 01 April 2026     |           | 19033564.29     | 
2006207.96    | 214432.75   | 0.0  | 0.0       | 2220640.71   | 0.0            
| 0.0              | 0.0     | 2220640.71     |
+      | 4  | 30   | 01 May 2026       |           | 17000651.89     | 
2032912.4     | 187728.31   | 0.0  | 0.0       | 2220640.71   | 0.0            
| 0.0              | 0.0     | 2220640.71     |
+      | 5  | 31   | 01 June 2026      |           | 14953278.1      | 
2047373.79    | 173266.92   | 0.0  | 0.0       | 2220640.71   | 0.0            
| 0.0              | 0.0     | 2220640.71     |
+      | 6  | 30   | 01 July 2026      |           | 12880121.78     | 
2073156.32    | 147484.39   | 0.0  | 0.0       | 2220640.71   | 0.0            
| 0.0              | 0.0     | 2220640.71     |
+      | 7  | 31   | 01 August 2026    |           | 10790752.45     | 
2089369.33    | 131271.38   | 0.0  | 0.0       | 2220640.71   | 0.0            
| 0.0              | 0.0     | 2220640.71     |
+      | 8  | 31   | 01 September 2026 |           |  8680088.72     | 
2110663.73    | 109976.98   | 0.0  | 0.0       | 2220640.71   | 0.0            
| 0.0              | 0.0     | 2220640.71     |
+      | 9  | 30   | 01 October 2026   |           |  6545059.84     | 
2135028.88    |  85611.83   | 0.0  | 0.0       | 2220640.71   | 0.0            
| 0.0              | 0.0     | 2220640.71     |
+      | 10 | 31   | 01 November 2026  |           |  4391124.95     | 
2153934.89    |  66705.82   | 0.0  | 0.0       | 2220640.71   | 0.0            
| 0.0              | 0.0     | 2220640.71     |
+      | 11 | 30   | 01 December 2026  |           |  2213793.97     | 
2177330.98    |  43309.73   | 0.0  | 0.0       | 2220640.71   | 0.0            
| 0.0              | 0.0     | 2220640.71     |
+      | 12 | 31   | 01 January 2027   |           |        0.0      | 
2213793.97    |  22562.5    | 0.0  | 0.0       | 2236356.47   | 0.0            
| 0.0              | 0.0     | 2236356.47     |
+    Then Loan Repayment schedule has the following data in Total row:
+      | Principal due     | Interest      | Fees | Penalties | Due           | 
Paid | In advance | Late | Outstanding    |
+      | 25000000.0        | 1663404.28    | 0.0  | 0.0       | 26663404.28   | 
0.0  | 0.0        | 0.0  | 26663404.28    |
+    Then Loan Transactions tab has the following data:
+      | Transaction date  | Transaction Type | Amount     | Principal | 
Interest | Fees | Penalties | Loan Balance     |
+      | 01 January 2026   | Disbursement     | 25000000.0 | 0.0       | 0.0    
  | 0.0  | 0.0       | 25000000.0       |
+    When Loan Pay-off is made on "23 February 2026"
+    Then Loan Repayment schedule has 12 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 2026   |                  | 25000000.0      |   
            |             | 0.0  |           | 0.0          | 0.0            |  
                |              |                |
+      | 1  | 31   | 01 February 2026  | 23 February 2026 | 23034153.81     | 
1965846.19    | 254794.52   | 0.0  | 0.0       | 2220640.71   | 2220640.71     
| 0.0              | 2220640.71   | 0.0            |
+      | 2  | 28   | 01 March 2026     | 23 February 2026 | 20813513.1      | 
2220640.71    |      0.0    | 0.0  | 0.0       | 2220640.71   | 2220640.71     
| 2220640.71       | 0.0          | 0.0            |
+      | 3  | 31   | 01 April 2026     | 23 February 2026 | 19985766.43     |  
827746.67    | 180821.92   | 0.0  | 0.0       | 1008568.59   | 1008568.59     | 
1008568.59       | 0.0          | 0.0            |
+      | 4  | 30   | 01 May 2026       | 23 February 2026 | 17765125.72     | 
2220640.71    |      0.0    | 0.0  | 0.0       | 2220640.71   | 2220640.71     
| 2220640.71       | 0.0          | 0.0            |
+      | 5  | 31   | 01 June 2026      | 23 February 2026 | 15544485.01     | 
2220640.71    |      0.0    | 0.0  | 0.0       | 2220640.71   | 2220640.71     
| 2220640.71       | 0.0          | 0.0            |
+      | 6  | 30   | 01 July 2026      | 23 February 2026 | 13323844.3      | 
2220640.71    |      0.0    | 0.0  | 0.0       | 2220640.71   | 2220640.71     
| 2220640.71       | 0.0          | 0.0            |
+      | 7  | 31   | 01 August 2026    | 23 February 2026 | 11103203.59     | 
2220640.71    |      0.0    | 0.0  | 0.0       | 2220640.71   | 2220640.71     
| 2220640.71       | 0.0          | 0.0            |
+      | 8  | 31   | 01 September 2026 | 23 February 2026 |  8882562.88     | 
2220640.71    |      0.0    | 0.0  | 0.0       | 2220640.71   | 2220640.71     
| 2220640.71       | 0.0          | 0.0            |
+      | 9  | 30   | 01 October 2026   | 23 February 2026 |  6661922.17     | 
2220640.71    |      0.0    | 0.0  | 0.0       | 2220640.71   | 2220640.71     
| 2220640.71       | 0.0          | 0.0            |
+      | 10 | 31   | 01 November 2026  | 23 February 2026 |  4441281.46     | 
2220640.71    |      0.0    | 0.0  | 0.0       | 2220640.71   | 2220640.71     
| 2220640.71       | 0.0          | 0.0            |
+      | 11 | 30   | 01 December 2026  | 23 February 2026 |  2220640.75     | 
2220640.71    |      0.0    | 0.0  | 0.0       | 2220640.71   | 2220640.71     
| 2220640.71       | 0.0          | 0.0            |
+      | 12 | 31   | 01 January 2027   | 23 February 2026 |        0.0      | 
2220640.75    |      0.0    | 0.0  | 0.0       | 2220640.75   | 2220640.75     
| 2220640.75       | 0.0          | 0.0            |
+    Then Loan Repayment schedule has the following data in Total row:
+      | Principal due     | Interest      | Fees | Penalties | Due           | 
Paid        | In advance   | Late        | Outstanding    |
+      | 25000000.0        | 435616.44     | 0.0  | 0.0       | 25435616.44   | 
25435616.44 | 23214975.73  | 2220640.71  | 0.0            |
+    Then Loan Transactions tab has the following data:
+      | Transaction date  | Transaction Type | Amount      | Principal  | 
Interest    | Fees | Penalties | Loan Balance     |
+      | 01 January 2026   | Disbursement     | 25000000.0  | 0.0        | 0.0  
       | 0.0  | 0.0       | 25000000.0       |
+      | 23 February 2026  | Repayment        | 25435616.44 | 25000000.0 | 
435616.44   | 0.0  | 0.0       |        0.0       |
+      | 23 February 2026  | Accrual          |   435616.44 |        0.0 | 
435616.44   | 0.0  | 0.0       |        0.0       |
+    Then Loan is closed with zero outstanding balance and it's all 
installments have obligations met
+    When Admin set 
"LP2_DOWNPAYMENT_ADV_PMT_ALLOC_PROGRESSIVE_LOAN_SCHEDULE_VERTICAL_INTEREST_RECALC"
 loan product "REPAYMENT" transaction type to "NEXT_INSTALLMENT" future 
installment allocation rule
diff --git 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanRepaymentScheduleInstallment.java
 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanRepaymentScheduleInstallment.java
index d66c6bb2c6..eed2f08dc3 100644
--- 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanRepaymentScheduleInstallment.java
+++ 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanRepaymentScheduleInstallment.java
@@ -1126,6 +1126,16 @@ public class LoanRepaymentScheduleInstallment extends 
AbstractAuditableWithUTCDa
                 MathUtil.nullToZero(MathUtil.add(getPrincipal(), 
getInterestCharged(), getFeeChargesCharged(), getPenaltyCharges())));
     }
 
+    public boolean isOutstandingBalanceNotZero(AllocationType allocationType, 
MonetaryCurrency currency) {
+        Money balance = switch (allocationType) {
+            case PENALTY -> this.getPenaltyChargesOutstanding(currency);
+            case FEE -> this.getFeeChargesOutstanding(currency);
+            case PRINCIPAL -> this.getPrincipalOutstanding(currency);
+            case INTEREST -> this.getInterestOutstanding(currency);
+        };
+        return MathUtil.isGreaterThanZero(balance);
+    }
+
     public void copyFrom(final LoanScheduleModelPeriod period) {
         // Reset fields and relations
         resetBalances();
diff --git 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/LoanApplicationTerms.java
 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/LoanApplicationTerms.java
index 297ce1b0e4..5538605d6e 100644
--- 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/LoanApplicationTerms.java
+++ 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/LoanApplicationTerms.java
@@ -1715,7 +1715,7 @@ public final class LoanApplicationTerms {
                 repaymentEvery, numberOfRepayments,
                 isInterestChargedFromDateSameAsDisbursalDateEnabled != null && 
isInterestChargedFromDateSameAsDisbursalDateEnabled,
                 daysInYearCustomStrategy, 
allowPartialPeriodInterestCalculation, interestRecalculationEnabled, 
recalculationFrequencyType,
-                preClosureInterestCalculationStrategy, 
allowFullTermForTranche);
+                preClosureInterestCalculationStrategy, 
allowFullTermForTranche, loanScheduleProcessingType);
     }
 
     public Integer getLoanTermFrequency() {
diff --git 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/data/LoanConfigurationDetails.java
 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/data/LoanConfigurationDetails.java
index 977e9a513c..3fdede017f 100644
--- 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/data/LoanConfigurationDetails.java
+++ 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/data/LoanConfigurationDetails.java
@@ -25,6 +25,7 @@ import 
org.apache.fineract.portfolio.common.domain.DaysInMonthType;
 import 
org.apache.fineract.portfolio.common.domain.DaysInYearCustomStrategyType;
 import org.apache.fineract.portfolio.common.domain.DaysInYearType;
 import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType;
+import 
org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleProcessingType;
 import org.apache.fineract.portfolio.loanproduct.domain.AmortizationMethod;
 import 
org.apache.fineract.portfolio.loanproduct.domain.ILoanConfigurationDetails;
 import 
org.apache.fineract.portfolio.loanproduct.domain.InterestCalculationPeriodMethod;
@@ -60,6 +61,8 @@ public class LoanConfigurationDetails implements 
ILoanConfigurationDetails {
     private final LoanPreCloseInterestCalculationStrategy 
preCloseInterestCalculationStrategy;
     @Getter
     private final boolean allowFullTermForTranche;
+    @Getter
+    private final LoanScheduleProcessingType loanScheduleProcessingType;
 
     public LoanConfigurationDetails(CurrencyData currency, BigDecimal 
interestRatePerPeriod, BigDecimal annualNominalInterestRate,
             Integer interestChargingGrace, Integer interestPaymentGrace, 
Integer principalGrace,
@@ -69,7 +72,8 @@ public class LoanConfigurationDetails implements 
ILoanConfigurationDetails {
             Integer numberOfRepayments, boolean 
interestRecognitionOnDisbursementDate,
             DaysInYearCustomStrategyType daysInYearCustomStrategy, boolean 
allowPartialPeriodInterestCalculation,
             boolean isInterestRecalculationEnabled, RecalculationFrequencyType 
restFrequencyType,
-            LoanPreCloseInterestCalculationStrategy 
preCloseInterestCalculationStrategy, boolean allowFullTermForTranche) {
+            LoanPreCloseInterestCalculationStrategy 
preCloseInterestCalculationStrategy, boolean allowFullTermForTranche,
+            LoanScheduleProcessingType loanScheduleProcessingType) {
         this.currency = currency;
         this.interestRatePerPeriod = interestRatePerPeriod;
         this.annualNominalInterestRate = annualNominalInterestRate;
@@ -92,6 +96,7 @@ public class LoanConfigurationDetails implements 
ILoanConfigurationDetails {
         this.restFrequencyType = restFrequencyType;
         this.preCloseInterestCalculationStrategy = 
preCloseInterestCalculationStrategy;
         this.allowFullTermForTranche = allowFullTermForTranche;
+        this.loanScheduleProcessingType = loanScheduleProcessingType;
     }
 
     private Integer defaultToNullIfZero(final Integer value) {
diff --git 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/domain/ILoanConfigurationDetails.java
 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/domain/ILoanConfigurationDetails.java
index 4ec89f0fd0..79fe75bc9e 100644
--- 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/domain/ILoanConfigurationDetails.java
+++ 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/domain/ILoanConfigurationDetails.java
@@ -22,6 +22,7 @@ import java.math.BigDecimal;
 import org.apache.fineract.organisation.monetary.data.CurrencyData;
 import 
org.apache.fineract.portfolio.common.domain.DaysInYearCustomStrategyType;
 import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType;
+import 
org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleProcessingType;
 
 /**
  * Represents the bare minimum repayment details needed for activities related 
to generating repayment schedules.
@@ -75,4 +76,6 @@ public interface ILoanConfigurationDetails {
     LoanPreCloseInterestCalculationStrategy 
getPreCloseInterestCalculationStrategy();
 
     boolean isAllowFullTermForTranche();
+
+    LoanScheduleProcessingType getLoanScheduleProcessingType();
 }
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 ea0aeedb42..5a93999d7c 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
@@ -2917,6 +2917,7 @@ public class AdvancedPaymentScheduleTransactionProcessor 
extends AbstractLoanRep
     private Money processPeriodsVertically(LoanTransaction loanTransaction, 
TransactionCtx ctx, Money transactionAmountUnprocessed,
             LoanPaymentAllocationRule paymentAllocationRule, 
List<LoanTransactionToRepaymentScheduleMapping> transactionMappings,
             Balances balances) {
+        Loan loan = loanTransaction.getLoan();
         VerticalPaymentAllocationContext paymentAllocationContext = new 
VerticalPaymentAllocationContext(ctx, loanTransaction,
                 paymentAllocationRule.getFutureInstallmentAllocationRule(), 
transactionMappings, balances);
         
paymentAllocationContext.setTransactionAmountUnprocessed(transactionAmountUnprocessed);
@@ -2924,6 +2925,18 @@ public class AdvancedPaymentScheduleTransactionProcessor 
extends AbstractLoanRep
             
paymentAllocationContext.setAllocatedAmount(Money.zero(ctx.getCurrency()));
             paymentAllocationContext.setInstallment(null);
             
paymentAllocationContext.setPaymentAllocationType(paymentAllocationType);
+            if (isInterestRecalculationSupported(ctx, 
loanTransaction.getLoan())) {
+                // Clear any previously skipped installments before 
re-evaluating
+                ProgressiveTransactionCtx progressiveTransactionCtx = 
(ProgressiveTransactionCtx) ctx;
+                
progressiveTransactionCtx.getSkipRepaymentScheduleInstallments().clear();
+                paymentAllocationContext
+                        .setInAdvanceInstallmentsFilteringRules(installment -> 
loanTransaction.isBefore(installment.getDueDate())
+                                && 
installment.isOutstandingBalanceNotZero(paymentAllocationType.getAllocationType(),
 ctx.getCurrency())
+                                && 
!progressiveTransactionCtx.getSkipRepaymentScheduleInstallments().contains(installment));
+            } else {
+                
paymentAllocationContext.setInAdvanceInstallmentsFilteringRules(getFilterPredicate(
+                        paymentAllocationContext.getPaymentAllocationType(), 
paymentAllocationContext.getCtx().getCurrency()));
+            }
             
LoopGuard.runSafeDoWhileLoop(paymentAllocationContext.getCtx().getInstallments().size()
 * 100, //
                     paymentAllocationContext, //
                     (VerticalPaymentAllocationContext context) -> 
context.getInstallment() != null
@@ -2944,11 +2957,20 @@ public class 
AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
                                     LoanTransactionToRepaymentScheduleMapping 
loanTransactionToRepaymentScheduleMapping = getTransactionMapping(
                                             context.getTransactionMappings(), 
context.getLoanTransaction(), context.getInstallment(),
                                             context.getCtx().getCurrency());
-                                    context.setAllocatedAmount(
-                                            
processPaymentAllocation(context.getPaymentAllocationType(), 
context.getInstallment(),
-                                                    
context.getLoanTransaction(), context.getTransactionAmountUnprocessed(),
-                                                    
loanTransactionToRepaymentScheduleMapping, oldestPastDueInstallmentCharges,
-                                                    context.getBalances(), 
LoanRepaymentScheduleInstallment.PaymentAction.PAY));
+
+                                    if 
(isInterestRecalculationSupported(context.getCtx(), loan)) {
+                                        
context.setAllocatedAmount(handlingPaymentAllocationForInterestBearingProgressiveLoan(
+                                                context.getLoanTransaction(), 
context.getTransactionAmountUnprocessed(),
+                                                context.getBalances(), 
paymentAllocationType, context.getInstallment(),
+                                                (ProgressiveTransactionCtx) 
context.getCtx(), loanTransactionToRepaymentScheduleMapping,
+                                                
oldestPastDueInstallmentCharges));
+                                    } else {
+                                        context.setAllocatedAmount(
+                                                
processPaymentAllocation(context.getPaymentAllocationType(), 
context.getInstallment(),
+                                                        
context.getLoanTransaction(), context.getTransactionAmountUnprocessed(),
+                                                        
loanTransactionToRepaymentScheduleMapping, oldestPastDueInstallmentCharges,
+                                                        context.getBalances(), 
LoanRepaymentScheduleInstallment.PaymentAction.PAY));
+                                    }
                                     context.setTransactionAmountUnprocessed(
                                             
context.getTransactionAmountUnprocessed().minus(context.getAllocatedAmount()));
                                 }
@@ -2963,11 +2985,19 @@ public class 
AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
                                     LoanTransactionToRepaymentScheduleMapping 
loanTransactionToRepaymentScheduleMapping = getTransactionMapping(
                                             context.getTransactionMappings(), 
context.getLoanTransaction(), context.getInstallment(),
                                             context.getCtx().getCurrency());
-                                    context.setAllocatedAmount(
-                                            
processPaymentAllocation(context.getPaymentAllocationType(), 
context.getInstallment(),
-                                                    
context.getLoanTransaction(), context.getTransactionAmountUnprocessed(),
-                                                    
loanTransactionToRepaymentScheduleMapping, dueInstallmentCharges, 
context.getBalances(),
-                                                    
LoanRepaymentScheduleInstallment.PaymentAction.PAY));
+                                    if 
(isInterestRecalculationSupported(context.getCtx(), loan)) {
+                                        
context.setAllocatedAmount(handlingPaymentAllocationForInterestBearingProgressiveLoan(
+                                                context.getLoanTransaction(), 
context.getTransactionAmountUnprocessed(),
+                                                context.getBalances(), 
paymentAllocationType, context.getInstallment(),
+                                                (ProgressiveTransactionCtx) 
context.getCtx(), loanTransactionToRepaymentScheduleMapping,
+                                                dueInstallmentCharges));
+                                    } else {
+                                        context.setAllocatedAmount(
+                                                
processPaymentAllocation(context.getPaymentAllocationType(), 
context.getInstallment(),
+                                                        
context.getLoanTransaction(), context.getTransactionAmountUnprocessed(),
+                                                        
loanTransactionToRepaymentScheduleMapping, dueInstallmentCharges,
+                                                        context.getBalances(), 
LoanRepaymentScheduleInstallment.PaymentAction.PAY));
+                                    }
                                     context.setTransactionAmountUnprocessed(
                                             
context.getTransactionAmountUnprocessed().minus(context.getAllocatedAmount()));
                                 }
@@ -2979,17 +3009,20 @@ public class 
AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
                                 // element.
                                 List<LoanRepaymentScheduleInstallment> 
currentInstallments = new ArrayList<>();
                                 if 
(FutureInstallmentAllocationRule.REAMORTIZATION.equals(context.getFutureInstallmentAllocationRule()))
 {
-                                    currentInstallments = 
context.getCtx().getInstallments().stream().filter(predicate)
+                                    currentInstallments = 
context.getCtx().getInstallments().stream()
+                                            
.filter(paymentAllocationContext.inAdvanceInstallmentsFilteringRules)
                                             .filter(e -> 
context.getLoanTransaction().isBefore(e.getDueDate())).toList();
                                 } else if 
(FutureInstallmentAllocationRule.NEXT_INSTALLMENT
                                         
.equals(context.getFutureInstallmentAllocationRule())) {
-                                    currentInstallments = 
context.getCtx().getInstallments().stream().filter(predicate)
+                                    currentInstallments = 
context.getCtx().getInstallments().stream()
+                                            
.filter(paymentAllocationContext.inAdvanceInstallmentsFilteringRules)
                                             .filter(e -> 
context.getLoanTransaction().isBefore(e.getDueDate()))
                                             
.min(Comparator.comparing(LoanRepaymentScheduleInstallment::getInstallmentNumber)).stream()
                                             .toList();
                                 } else if 
(FutureInstallmentAllocationRule.LAST_INSTALLMENT
                                         
.equals(context.getFutureInstallmentAllocationRule())) {
-                                    currentInstallments = 
context.getCtx().getInstallments().stream().filter(predicate)
+                                    currentInstallments = 
context.getCtx().getInstallments().stream()
+                                            
.filter(paymentAllocationContext.inAdvanceInstallmentsFilteringRules)
                                             .filter(e -> 
context.getLoanTransaction().isBefore(e.getDueDate()))
                                             
.max(Comparator.comparing(LoanRepaymentScheduleInstallment::getInstallmentNumber)).stream()
                                             .toList();
@@ -2998,14 +3031,16 @@ public class 
AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
                                     // get current installment where from date 
< transaction date < to date OR
                                     // transaction date
                                     // is on first installment's first day ( 
from day )
-                                    currentInstallments = 
context.getCtx().getInstallments().stream().filter(predicate)
+                                    currentInstallments = 
context.getCtx().getInstallments().stream()
+                                            
.filter(paymentAllocationContext.inAdvanceInstallmentsFilteringRules)
                                             .filter(e -> 
context.getLoanTransaction().isBefore(e.getDueDate()))
                                             .filter(f -> 
context.getLoanTransaction().isAfter(f.getFromDate())
                                                     || 
context.getLoanTransaction().isOn(f.getFromDate()))
                                             .toList();
                                     // if there is no current in advance 
installment resolve similar to LAST_INSTALLMENT
                                     if (currentInstallments.isEmpty()) {
-                                        currentInstallments = 
context.getCtx().getInstallments().stream().filter(predicate)
+                                        currentInstallments = 
context.getCtx().getInstallments().stream()
+                                                
.filter(paymentAllocationContext.inAdvanceInstallmentsFilteringRules)
                                                 .filter(e -> 
context.getLoanTransaction().isBefore(e.getDueDate()))
                                                 
.max(Comparator.comparing(LoanRepaymentScheduleInstallment::getInstallmentNumber)).stream()
                                                 .toList();
@@ -3034,18 +3069,37 @@ public class 
AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
                                         
LoanTransactionToRepaymentScheduleMapping 
loanTransactionToRepaymentScheduleMapping = getTransactionMapping(
                                                 
context.getTransactionMappings(), context.getLoanTransaction(), 
context.getInstallment(),
                                                 
context.getCtx().getCurrency());
-                                        Money internalPaidPortion = 
processPaymentAllocation(context.getPaymentAllocationType(),
-                                                context.getInstallment(), 
context.getLoanTransaction(), evenPortion,
-                                                
loanTransactionToRepaymentScheduleMapping, inAdvanceInstallmentCharges,
-                                                context.getBalances(), 
LoanRepaymentScheduleInstallment.PaymentAction.PAY);
-                                        // Some extra logic to allocate as 
much as possible across the installments if
-                                        // the
-                                        // outstanding balances are different
-                                        if 
(internalPaidPortion.isGreaterThanZero()) {
-                                            
context.setAllocatedAmount(internalPaidPortion);
+                                        if 
(isInterestRecalculationSupported(context.getCtx(), loan)) {
+                                            Money internalPaidPortion = 
handlingPaymentAllocationForInterestBearingProgressiveLoan(
+                                                    
context.getLoanTransaction(), context.getTransactionAmountUnprocessed(),
+                                                    context.getBalances(), 
paymentAllocationType, context.getInstallment(),
+                                                    
(ProgressiveTransactionCtx) context.getCtx(), 
loanTransactionToRepaymentScheduleMapping,
+                                                    
inAdvanceInstallmentCharges);
+                                            // Some extra logic to allocate as 
much as possible across the installments
+                                            // if
+                                            // the
+                                            // outstanding balances are 
different
+                                            if 
(internalPaidPortion.isGreaterThanZero()) {
+                                                
context.setAllocatedAmount(internalPaidPortion);
+                                            }
+                                            
context.setTransactionAmountUnprocessed(
+                                                    
context.getTransactionAmountUnprocessed().minus(internalPaidPortion));
+                                        } else {
+                                            Money internalPaidPortion = 
processPaymentAllocation(context.getPaymentAllocationType(),
+                                                    context.getInstallment(), 
context.getLoanTransaction(), evenPortion,
+                                                    
loanTransactionToRepaymentScheduleMapping, inAdvanceInstallmentCharges,
+                                                    context.getBalances(), 
LoanRepaymentScheduleInstallment.PaymentAction.PAY);
+
+                                            // Some extra logic to allocate as 
much as possible across the installments
+                                            // if
+                                            // the
+                                            // outstanding balances are 
different
+                                            if 
(internalPaidPortion.isGreaterThanZero()) {
+                                                
context.setAllocatedAmount(internalPaidPortion);
+                                            }
+                                            
context.setTransactionAmountUnprocessed(
+                                                    
context.getTransactionAmountUnprocessed().minus(internalPaidPortion));
                                         }
-                                        
context.setTransactionAmountUnprocessed(
-                                                
context.getTransactionAmountUnprocessed().minus(internalPaidPortion));
                                     }
                                 } else {
                                     context.setInstallment(null);
@@ -3569,6 +3623,7 @@ public class AdvancedPaymentScheduleTransactionProcessor 
extends AbstractLoanRep
         private Money transactionAmountUnprocessed;
         private Money allocatedAmount;
         private PaymentAllocationType paymentAllocationType;
+        private Predicate<LoanRepaymentScheduleInstallment> 
inAdvanceInstallmentsFilteringRules;
 
         VerticalPaymentAllocationContext(TransactionCtx ctx, LoanTransaction 
loanTransaction,
                 FutureInstallmentAllocationRule 
futureInstallmentAllocationRule,
diff --git 
a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/mapper/LoanConfigurationDetailsMapper.java
 
b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/mapper/LoanConfigurationDetailsMapper.java
index 487c7069ed..0254a6a51a 100644
--- 
a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/mapper/LoanConfigurationDetailsMapper.java
+++ 
b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/mapper/LoanConfigurationDetailsMapper.java
@@ -57,7 +57,7 @@ public final class LoanConfigurationDetailsMapper {
                 loanProductRelatedDetail.getNumberOfRepayments(), 
loanProductRelatedDetail.isInterestRecognitionOnDisbursementDate(),
                 loanProductRelatedDetail.getDaysInYearCustomStrategy(), 
loanProductRelatedDetail.isAllowPartialPeriodInterestCalculation(),
                 loan.isInterestRecalculationEnabled(), 
getRestFrequencyType(loan), getPreCloseInterestCalculationStrategy(loan),
-                loan.isAllowFullTermForTranche());
+                loan.isAllowFullTermForTranche(), 
loan.getLoanProductRelatedDetail().getLoanScheduleProcessingType());
     }
 
     private static RecalculationFrequencyType getRestFrequencyType(Loan loan) {
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 cf7cd8c34f..b5de8612bb 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
@@ -54,6 +54,7 @@ import 
org.apache.fineract.portfolio.loanaccount.domain.reaging.LoanReAgeInteres
 import 
org.apache.fineract.portfolio.loanaccount.domain.reaging.LoanReAgeParameter;
 import 
org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanApplicationTerms;
 import 
org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleModelRepaymentPeriod;
+import 
org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleProcessingType;
 import 
org.apache.fineract.portfolio.loanaccount.loanschedule.domain.ScheduledDateGenerator;
 import org.apache.fineract.portfolio.loanproduct.calc.data.EmiAdjustment;
 import org.apache.fineract.portfolio.loanproduct.calc.data.EmiChangeOperation;
@@ -524,10 +525,15 @@ public final class ProgressiveEMICalculator implements 
EMICalculator {
                         false)); //
             }
         }
-
+        Money duePrincipal = repaymentPeriod.getDuePrincipal();
+        Money dueInterest = repaymentPeriod.getDueInterest();
+        if 
(scheduleModel.loanProductRelatedDetail().getLoanScheduleProcessingType() == 
LoanScheduleProcessingType.VERTICAL
+                && notFullyRepaidRepaymentPeriodCount > 1) {
+            duePrincipal = 
repaymentPeriod.getEmiPlusCreditedAmountsPlusFutureUnrecognizedInterest();
+        }
         return new PeriodDueDetails(repaymentPeriod.getEmi(), //
-                repaymentPeriod.getDuePrincipal(), //
-                repaymentPeriod.getDueInterest()); //
+                duePrincipal, //
+                dueInterest); //
     }
 
     @Override

Reply via email to