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 24bc7bb5f FINERACT-1905: New repayment strategy
24bc7bb5f is described below

commit 24bc7bb5fbee2e73639e7ba6ededcd7461140eef
Author: Adam Saghy <[email protected]>
AuthorDate: Tue Mar 21 14:21:12 2023 +0100

    FINERACT-1905: New repayment strategy
---
 .../core/config/FineractProperties.java            |  31 ++
 .../portfolio/loanaccount/domain/LoanCharge.java   |  18 +
 .../LoanChargeEffectiveDueDateComparator.java      |  45 +++
 ...tLoanRepaymentScheduleTransactionProcessor.java |  36 +-
 .../LoanRepaymentScheduleTransactionProcessor.java |   2 +-
 ...eLoanRepaymentScheduleTransactionProcessor.java |  21 +-
 ...LoanRepaymentScheduleTransactionProcessor.java} | 129 +++++--
 ...tLoanRepaymentScheduleTransactionProcessor.java |  15 +-
 ...eLoanRepaymentScheduleTransactionProcessor.java |  20 +-
 ...yLoanRepaymentScheduleTransactionProcessor.java |  17 +-
 ...rLoanRepaymentScheduleTransactionProcessor.java |  18 +-
 ...rLoanRepaymentScheduleTransactionProcessor.java |  18 +-
 ...ILoanRepaymentScheduleTransactionProcessor.java |  18 +-
 .../domain/AbstractLoanScheduleGenerator.java      |  17 +-
 ...ymentScheduleTransactionProcessorCondition.java |  30 ++
 ...ymentScheduleTransactionProcessorCondition.java |  31 ++
 ...ymentScheduleTransactionProcessorCondition.java |  30 ++
 ...ymentScheduleTransactionProcessorCondition.java |  30 ++
 ...ymentScheduleTransactionProcessorCondition.java |  30 ++
 .../starter/LoanAccountAutoStarter.java            |  23 +-
 ...ymentScheduleTransactionProcessorCondition.java |  30 ++
 ...ymentScheduleTransactionProcessorCondition.java |  30 ++
 ...ymentScheduleTransactionProcessorCondition.java |  30 ++
 .../src/main/resources/application.properties      |   1 +
 .../db/changelog/tenant/changelog-tenant.xml       |   1 +
 .../tenant/parts/0100_new_repayment_strategy.xml   |  42 ++
 .../LoanChargeEffectiveDueDateComparatorTest.java  | 149 +++++++
 .../src/test/resources/application-test.properties |   1 +
 ...DueDateRespectiveLoanRepaymentScheduleTest.java | 428 +++++++++++++++++++++
 .../common/loans/LoanApplicationTestBuilder.java   |   1 +
 .../common/loans/LoanProductTestBuilder.java       |   1 +
 31 files changed, 1162 insertions(+), 131 deletions(-)

diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/config/FineractProperties.java
 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/config/FineractProperties.java
index 7348d52e2..0b667d65c 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/config/FineractProperties.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/config/FineractProperties.java
@@ -60,6 +60,8 @@ public class FineractProperties {
 
     private FineractNotificationProperties notification;
 
+    private FineractLoanProperties loan;
+
     @Getter
     @Setter
     public static class FineractTenantProperties {
@@ -295,4 +297,33 @@ public class FineractProperties {
 
         private boolean enabled;
     }
+
+    @Getter
+    @Setter
+    public static class FineractLoanProperties {
+
+        private FineractTransactionProcessorProperties transactionProcessor;
+    }
+
+    @Getter
+    @Setter
+    public static class FineractTransactionProcessorProperties {
+
+        private FineractTransactionProcessorItemProperties creocore;
+        private FineractTransactionProcessorItemProperties earlyRepayment;
+        private FineractTransactionProcessorItemProperties mifosStandard;
+        private FineractTransactionProcessorItemProperties heavensFamily;
+        private FineractTransactionProcessorItemProperties 
interestPrincipalPenaltiesFees;
+        private FineractTransactionProcessorItemProperties 
principalInterestPenaltiesFees;
+        private FineractTransactionProcessorItemProperties rbiIndia;
+        private FineractTransactionProcessorItemProperties 
duePenaltyFeeInterestPrincipalInAdvancePrincipalPenaltyFeeInterest;
+        private boolean errorNotFoundFail;
+    }
+
+    @Getter
+    @Setter
+    public static class FineractTransactionProcessorItemProperties {
+
+        private boolean enabled;
+    }
 }
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanCharge.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanCharge.java
index 601c0bacd..eea10227a 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanCharge.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanCharge.java
@@ -28,6 +28,7 @@ import java.util.HashSet;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Set;
 import java.util.stream.Collectors;
 import javax.persistence.CascadeType;
@@ -1006,6 +1007,23 @@ public class LoanCharge extends 
AbstractPersistableCustom {
         return ChargeTimeType.fromInt(this.chargeTime);
     }
 
+    /**
+     * Return the effective due date of the loan charge. For installment fee 
we are using the earliest not fully paid
+     * installment due date
+     *
+     * @return LocalDate
+     */
+    public LocalDate getEffectiveDueDate() {
+        LocalDate dueDate;
+        if (Objects.requireNonNull(getChargeTimeType()) == 
ChargeTimeType.INSTALMENT_FEE) {
+            LoanInstallmentCharge firstUnpaidInstallment = 
getUnpaidInstallmentLoanCharge();
+            dueDate = firstUnpaidInstallment != null ? 
firstUnpaidInstallment.getInstallment().getDueDate() : null;
+        } else {
+            dueDate = getDueLocalDate();
+        }
+        return dueDate;
+    }
+
     public LoanChargeData toData() {
         EnumOptionData chargeTimeTypeData = new EnumOptionData((long) 
getChargeTimeType().ordinal(), getChargeTimeType().getCode(),
                 String.valueOf(getChargeTimeType().getValue()));
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanChargeEffectiveDueDateComparator.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanChargeEffectiveDueDateComparator.java
new file mode 100644
index 000000000..f5360d284
--- /dev/null
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanChargeEffectiveDueDateComparator.java
@@ -0,0 +1,45 @@
+/**
+ * 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.portfolio.loanaccount.domain;
+
+import java.util.Comparator;
+
+/**
+ * Sort loan charges by effective due date
+ *
+ * Null values go to the end
+ */
+public final class LoanChargeEffectiveDueDateComparator implements 
Comparator<LoanCharge> {
+
+    public static final LoanChargeEffectiveDueDateComparator INSTANCE = new 
LoanChargeEffectiveDueDateComparator();
+
+    private LoanChargeEffectiveDueDateComparator() {}
+
+    @Override
+    public int compare(final LoanCharge o1, final LoanCharge o2) {
+        if (o1.getEffectiveDueDate() == null && o2.getEffectiveDueDate() != 
null) {
+            return 1;
+        } else if (o1.getEffectiveDueDate() == null) {
+            return 0;
+        } else if (o2.getEffectiveDueDate() == null) {
+            return -1;
+        }
+        return o1.getEffectiveDueDate().compareTo(o2.getEffectiveDueDate());
+    }
+}
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/AbstractLoanRepaymentScheduleTransactionProcessor.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/AbstractLoanRepaymentScheduleTransactionProcessor.java
index 6ee1af846..6994ed8bd 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/AbstractLoanRepaymentScheduleTransactionProcessor.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/AbstractLoanRepaymentScheduleTransactionProcessor.java
@@ -431,7 +431,7 @@ public abstract class 
AbstractLoanRepaymentScheduleTransactionProcessor implemen
         if (loanTransaction.isRepaymentLikeType() || 
loanTransaction.isInterestWaiver() || loanTransaction.isRecoveryRepayment()) {
             loanTransaction.resetDerivedComponents();
         }
-        Money transactionAmountUnprocessed = 
processTransaction(loanTransaction, currency, installments, 
chargeAmountToProcess);
+        Money transactionAmountUnprocessed = 
processTransaction(loanTransaction, currency, installments, charges, 
chargeAmountToProcess);
 
         final Set<LoanCharge> loanFees = extractFeeCharges(charges);
         final Set<LoanCharge> loanPenalties = extractPenaltyCharges(charges);
@@ -462,7 +462,7 @@ public abstract class 
AbstractLoanRepaymentScheduleTransactionProcessor implemen
     }
 
     private Money processTransaction(final LoanTransaction loanTransaction, 
final MonetaryCurrency currency,
-            final List<LoanRepaymentScheduleInstallment> installments, Money 
amountToProcess) {
+            final List<LoanRepaymentScheduleInstallment> installments, final 
Set<LoanCharge> charges, Money amountToProcess) {
         int installmentIndex = 0;
 
         final LocalDate transactionDate = loanTransaction.getTransactionDate();
@@ -479,20 +479,18 @@ public abstract class 
AbstractLoanRepaymentScheduleTransactionProcessor implemen
                     // is this transaction early/late/on-time with respect to
                     // the
                     // current installment?
-                    if (isTransactionInAdvanceOfInstallment(installmentIndex, 
installments, transactionDate,
-                            transactionAmountUnprocessed)) {
+                    if (isTransactionInAdvanceOfInstallment(installmentIndex, 
installments, transactionDate)) {
                         transactionAmountUnprocessed = 
handleTransactionThatIsPaymentInAdvanceOfInstallment(currentInstallment,
-                                installments, loanTransaction, 
transactionDate, transactionAmountUnprocessed, transactionMappings);
-                    } else if 
(isTransactionALateRepaymentOnInstallment(installmentIndex, installments,
-                            loanTransaction.getTransactionDate())) {
+                                installments, loanTransaction, 
transactionAmountUnprocessed, transactionMappings, charges);
+                    } else if 
(isTransactionALateRepaymentOnInstallment(installmentIndex, installments, 
transactionDate)) {
                         // does this result in a late payment of existing
                         // installment?
                         transactionAmountUnprocessed = 
handleTransactionThatIsALateRepaymentOfInstallment(currentInstallment, 
installments,
-                                loanTransaction, transactionAmountUnprocessed, 
transactionMappings);
+                                loanTransaction, transactionAmountUnprocessed, 
transactionMappings, charges);
                     } else {
                         // standard transaction
                         transactionAmountUnprocessed = 
handleTransactionThatIsOnTimePaymentOfInstallment(currentInstallment,
-                                loanTransaction, transactionAmountUnprocessed, 
transactionMappings);
+                                loanTransaction, transactionAmountUnprocessed, 
transactionMappings, charges);
                     }
                 }
             }
@@ -630,10 +628,11 @@ public abstract class 
AbstractLoanRepaymentScheduleTransactionProcessor implemen
      *
      * @param transactionMappings
      *            TODO
+     * @param charges
      */
     protected abstract Money 
handleTransactionThatIsALateRepaymentOfInstallment(LoanRepaymentScheduleInstallment
 currentInstallment,
             List<LoanRepaymentScheduleInstallment> installments, 
LoanTransaction loanTransaction, Money transactionAmountUnprocessed,
-            List<LoanTransactionToRepaymentScheduleMapping> 
transactionMappings);
+            List<LoanTransactionToRepaymentScheduleMapping> 
transactionMappings, Set<LoanCharge> charges);
 
     /**
      * This method is responsible for checking if the current transaction is 
'an advance/early payment' based on the
@@ -642,8 +641,7 @@ public abstract class 
AbstractLoanRepaymentScheduleTransactionProcessor implemen
      * Default implementation is check transaction date is before installment 
due date.
      */
     protected boolean isTransactionInAdvanceOfInstallment(final int 
currentInstallmentIndex,
-            final List<LoanRepaymentScheduleInstallment> installments, final 
LocalDate transactionDate,
-            @SuppressWarnings("unused") final Money transactionAmount) {
+            final List<LoanRepaymentScheduleInstallment> installments, final 
LocalDate transactionDate) {
 
         final LoanRepaymentScheduleInstallment currentInstallment = 
installments.get(currentInstallmentIndex);
 
@@ -655,20 +653,22 @@ public abstract class 
AbstractLoanRepaymentScheduleTransactionProcessor implemen
      *
      * @param transactionMappings
      *            TODO
+     * @param charges
      */
     protected abstract Money 
handleTransactionThatIsPaymentInAdvanceOfInstallment(LoanRepaymentScheduleInstallment
 currentInstallment,
-            List<LoanRepaymentScheduleInstallment> installments, 
LoanTransaction loanTransaction, LocalDate transactionDate,
-            Money paymentInAdvance, 
List<LoanTransactionToRepaymentScheduleMapping> transactionMappings);
+            List<LoanRepaymentScheduleInstallment> installments, 
LoanTransaction loanTransaction, Money paymentInAdvance,
+            List<LoanTransactionToRepaymentScheduleMapping> 
transactionMappings, Set<LoanCharge> charges);
 
     /**
      * For normal on-time repayments.
      *
      * @param transactionMappings
      *            TODO
+     * @param charges
      */
     protected abstract Money 
handleTransactionThatIsOnTimePaymentOfInstallment(LoanRepaymentScheduleInstallment
 currentInstallment,
             LoanTransaction loanTransaction, Money 
transactionAmountUnprocessed,
-            List<LoanTransactionToRepaymentScheduleMapping> 
transactionMappings);
+            List<LoanTransactionToRepaymentScheduleMapping> 
transactionMappings, Set<LoanCharge> charges);
 
     /**
      * Invoked when a transaction results in an over-payment of the full loan.
@@ -682,7 +682,7 @@ public abstract class 
AbstractLoanRepaymentScheduleTransactionProcessor implemen
 
     @Override
     public Money handleRepaymentSchedule(final List<LoanTransaction> 
transactionsPostDisbursement, final MonetaryCurrency currency,
-            final List<LoanRepaymentScheduleInstallment> installments) {
+            final List<LoanRepaymentScheduleInstallment> installments, 
Set<LoanCharge> loanCharges) {
         Money unProcessed = Money.zero(currency);
         for (final LoanTransaction loanTransaction : 
transactionsPostDisbursement) {
             Money amountToProcess = null;
@@ -690,9 +690,9 @@ public abstract class 
AbstractLoanRepaymentScheduleTransactionProcessor implemen
                 loanTransaction.resetDerivedComponents();
             }
             if (loanTransaction.isInterestWaiver()) {
-                processTransaction(loanTransaction, currency, installments, 
amountToProcess);
+                processTransaction(loanTransaction, currency, installments, 
loanCharges, amountToProcess);
             } else {
-                unProcessed = processTransaction(loanTransaction, currency, 
installments, amountToProcess);
+                unProcessed = processTransaction(loanTransaction, currency, 
installments, loanCharges, amountToProcess);
             }
         }
         return unProcessed;
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/LoanRepaymentScheduleTransactionProcessor.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/LoanRepaymentScheduleTransactionProcessor.java
index 41b051283..52dd30b4b 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/LoanRepaymentScheduleTransactionProcessor.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/LoanRepaymentScheduleTransactionProcessor.java
@@ -46,7 +46,7 @@ public interface LoanRepaymentScheduleTransactionProcessor {
             List<LoanRepaymentScheduleInstallment> 
repaymentScheduleInstallments);
 
     Money handleRepaymentSchedule(List<LoanTransaction> 
transactionsPostDisbursement, MonetaryCurrency currency,
-            List<LoanRepaymentScheduleInstallment> installments);
+            List<LoanRepaymentScheduleInstallment> installments, 
Set<LoanCharge> loanCharges);
 
     /**
      * Used in interest recalculation to introduce new interest only 
installment.
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/CreocoreLoanRepaymentScheduleTransactionProcessor.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/CreocoreLoanRepaymentScheduleTransactionProcessor.java
index d00d6f3ed..88719e6f5 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/CreocoreLoanRepaymentScheduleTransactionProcessor.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/CreocoreLoanRepaymentScheduleTransactionProcessor.java
@@ -20,8 +20,10 @@ package 
org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.im
 
 import java.time.LocalDate;
 import java.util.List;
+import java.util.Set;
 import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
 import org.apache.fineract.organisation.monetary.domain.Money;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanCharge;
 import 
org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
 import 
org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionToRepaymentScheduleMapping;
@@ -57,10 +59,9 @@ public class 
CreocoreLoanRepaymentScheduleTransactionProcessor extends AbstractL
     /**
      * For creocore, early is defined as any date before the installment due 
date
      */
-    @SuppressWarnings("unused")
     @Override
     protected boolean isTransactionInAdvanceOfInstallment(final int 
currentInstallmentIndex,
-            final List<LoanRepaymentScheduleInstallment> installments, final 
LocalDate transactionDate, final Money transactionAmount) {
+            final List<LoanRepaymentScheduleInstallment> installments, final 
LocalDate transactionDate) {
 
         final LoanRepaymentScheduleInstallment currentInstallment = 
installments.get(currentInstallmentIndex);
 
@@ -73,12 +74,11 @@ public class 
CreocoreLoanRepaymentScheduleTransactionProcessor extends AbstractL
     @SuppressWarnings("unused")
     @Override
     protected Money handleTransactionThatIsPaymentInAdvanceOfInstallment(final 
LoanRepaymentScheduleInstallment currentInstallment,
-            final List<LoanRepaymentScheduleInstallment> installments, final 
LoanTransaction loanTransaction,
-            final LocalDate transactionDate, final Money paymentInAdvance,
-            final List<LoanTransactionToRepaymentScheduleMapping> 
transactionMappings) {
+            final List<LoanRepaymentScheduleInstallment> installments, final 
LoanTransaction loanTransaction, final Money paymentInAdvance,
+            final List<LoanTransactionToRepaymentScheduleMapping> 
transactionMappings, Set<LoanCharge> charges) {
 
-        return 
handleTransactionThatIsOnTimePaymentOfInstallment(currentInstallment, 
loanTransaction, paymentInAdvance,
-                transactionMappings);
+        return 
handleTransactionThatIsOnTimePaymentOfInstallment(currentInstallment, 
loanTransaction, paymentInAdvance, transactionMappings,
+                charges);
     }
 
     /**
@@ -88,10 +88,11 @@ public class 
CreocoreLoanRepaymentScheduleTransactionProcessor extends AbstractL
     @Override
     protected Money handleTransactionThatIsALateRepaymentOfInstallment(final 
LoanRepaymentScheduleInstallment currentInstallment,
             final List<LoanRepaymentScheduleInstallment> installments, final 
LoanTransaction loanTransaction,
-            final Money transactionAmountUnprocessed, final 
List<LoanTransactionToRepaymentScheduleMapping> transactionMappings) {
+            final Money transactionAmountUnprocessed, final 
List<LoanTransactionToRepaymentScheduleMapping> transactionMappings,
+            Set<LoanCharge> charges) {
 
         return 
handleTransactionThatIsOnTimePaymentOfInstallment(currentInstallment, 
loanTransaction, transactionAmountUnprocessed,
-                transactionMappings);
+                transactionMappings, charges);
     }
 
     /**
@@ -100,7 +101,7 @@ public class 
CreocoreLoanRepaymentScheduleTransactionProcessor extends AbstractL
     @Override
     protected Money handleTransactionThatIsOnTimePaymentOfInstallment(final 
LoanRepaymentScheduleInstallment currentInstallment,
             final LoanTransaction loanTransaction, final Money 
transactionAmountUnprocessed,
-            final List<LoanTransactionToRepaymentScheduleMapping> 
transactionMappings) {
+            final List<LoanTransactionToRepaymentScheduleMapping> 
transactionMappings, Set<LoanCharge> charges) {
 
         final LocalDate transactionDate = loanTransaction.getTransactionDate();
         final MonetaryCurrency currency = 
transactionAmountUnprocessed.getCurrency();
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/FineractStyleLoanRepaymentScheduleTransactionProcessor.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/DueDateRespectiveLoanRepaymentScheduleTransactionProcessor.java
similarity index 60%
copy from 
fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/FineractStyleLoanRepaymentScheduleTransactionProcessor.java
copy to 
fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/DueDateRespectiveLoanRepaymentScheduleTransactionProcessor.java
index b14158c23..7d5b9ec02 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/FineractStyleLoanRepaymentScheduleTransactionProcessor.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/DueDateRespectiveLoanRepaymentScheduleTransactionProcessor.java
@@ -20,8 +20,11 @@ package 
org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.im
 
 import java.time.LocalDate;
 import java.util.List;
+import java.util.Set;
 import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
 import org.apache.fineract.organisation.monetary.domain.Money;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanCharge;
+import 
org.apache.fineract.portfolio.loanaccount.domain.LoanChargeEffectiveDueDateComparator;
 import 
org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
 import 
org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionToRepaymentScheduleMapping;
@@ -29,20 +32,18 @@ import 
org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.Abs
 import 
org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.LoanRepaymentScheduleTransactionProcessor;
 
 /**
- * Old style {@link LoanRepaymentScheduleTransactionProcessor}.
+ * `First due/late charges, interest, principal, after in advance principal, 
charges, interest` style
+ * {@link LoanRepaymentScheduleTransactionProcessor}.
  *
- * For ALL types of transactions, pays off components in order of interest, 
then principal.
- *
- * Other formulas exist on fineract where you can choose 'Declining-Balance 
Interest Recalculation' which simply means,
- * recalculate the interest component based on the how much principal is 
outstanding at a point in time; but this isnt
- * trying to model that option only the basic one for now.
+ * For ALL types of transactions, pays off components in order of: Due/late 
penalty Due/late Fee Due/late interest
+ * Due/late principal In advance principal In advance penalty In advance fee 
In advance interest
  */
 @SuppressWarnings("unused")
-public class FineractStyleLoanRepaymentScheduleTransactionProcessor extends 
AbstractLoanRepaymentScheduleTransactionProcessor {
+public class DueDateRespectiveLoanRepaymentScheduleTransactionProcessor 
extends AbstractLoanRepaymentScheduleTransactionProcessor {
 
-    private static final String STRATEGY_CODE = "mifos-standard-strategy";
+    private static final String STRATEGY_CODE = 
"due-penalty-fee-interest-principal-in-advance-principal-penalty-fee-interest-strategy";
 
-    private static final String STRATEGY_NAME = "Penalties, Fees, Interest, 
Principal order";
+    private static final String STRATEGY_NAME = "Due penalty, fee, interest, 
principal, In advance principal, penalty, fee, interest";
 
     @Override
     public String getCode() {
@@ -56,7 +57,7 @@ public class 
FineractStyleLoanRepaymentScheduleTransactionProcessor extends Abst
 
     @Override
     protected boolean isTransactionInAdvanceOfInstallment(final int 
currentInstallmentIndex,
-            final List<LoanRepaymentScheduleInstallment> installments, final 
LocalDate transactionDate, final Money transactionAmount) {
+            final List<LoanRepaymentScheduleInstallment> installments, final 
LocalDate transactionDate) {
 
         final LoanRepaymentScheduleInstallment currentInstallment = 
installments.get(currentInstallmentIndex);
 
@@ -64,45 +65,46 @@ public class 
FineractStyleLoanRepaymentScheduleTransactionProcessor extends Abst
     }
 
     /**
-     * For early/'in advance' repayments, pay off in the same way as on-time 
payments, interest first then principal.
+     * For early/'in advance' repayments
      */
     @Override
     protected Money handleTransactionThatIsPaymentInAdvanceOfInstallment(final 
LoanRepaymentScheduleInstallment currentInstallment,
-            final List<LoanRepaymentScheduleInstallment> installments, final 
LoanTransaction loanTransaction,
-            final LocalDate transactionDate, final Money paymentInAdvance,
-            List<LoanTransactionToRepaymentScheduleMapping> 
transactionMappings) {
+            final List<LoanRepaymentScheduleInstallment> installments, final 
LoanTransaction loanTransaction, final Money paymentInAdvance,
+            List<LoanTransactionToRepaymentScheduleMapping> 
transactionMappings, Set<LoanCharge> charges) {
 
-        return 
handleTransactionThatIsOnTimePaymentOfInstallment(currentInstallment, 
loanTransaction, paymentInAdvance,
-                transactionMappings);
+        return 
handleTransactionThatIsOnTimePaymentOfInstallment(currentInstallment, 
loanTransaction, paymentInAdvance, transactionMappings,
+                charges);
     }
 
     /**
-     * For late repayments, pay off in the same way as on-time payments, 
interest first then principal.
+     * For late repayments
      */
     @Override
     protected Money handleTransactionThatIsALateRepaymentOfInstallment(final 
LoanRepaymentScheduleInstallment currentInstallment,
             final List<LoanRepaymentScheduleInstallment> installments, final 
LoanTransaction loanTransaction,
-            final Money transactionAmountUnprocessed, 
List<LoanTransactionToRepaymentScheduleMapping> transactionMappings) {
+            final Money transactionAmountUnprocessed, 
List<LoanTransactionToRepaymentScheduleMapping> transactionMappings,
+            Set<LoanCharge> charges) {
 
         return 
handleTransactionThatIsOnTimePaymentOfInstallment(currentInstallment, 
loanTransaction, transactionAmountUnprocessed,
-                transactionMappings);
+                transactionMappings, charges);
     }
 
     /**
-     * For normal on-time repayments, pays off interest first, then principal.
+     * For normal on-time repayments
      */
     @Override
     protected Money handleTransactionThatIsOnTimePaymentOfInstallment(final 
LoanRepaymentScheduleInstallment currentInstallment,
             final LoanTransaction loanTransaction, final Money 
transactionAmountUnprocessed,
-            List<LoanTransactionToRepaymentScheduleMapping> 
transactionMappings) {
+            List<LoanTransactionToRepaymentScheduleMapping> 
transactionMappings, Set<LoanCharge> charges) {
 
         final LocalDate transactionDate = loanTransaction.getTransactionDate();
+
         final MonetaryCurrency currency = 
transactionAmountUnprocessed.getCurrency();
         Money transactionAmountRemaining = transactionAmountUnprocessed;
-        Money principalPortion = 
Money.zero(transactionAmountRemaining.getCurrency());
-        Money interestPortion = 
Money.zero(transactionAmountRemaining.getCurrency());
-        Money feeChargesPortion = 
Money.zero(transactionAmountRemaining.getCurrency());
-        Money penaltyChargesPortion = 
Money.zero(transactionAmountRemaining.getCurrency());
+        Money principalPortion = Money.zero(currency);
+        Money interestPortion = Money.zero(currency);
+        Money feeChargesPortion = Money.zero(currency);
+        Money penaltyChargesPortion = Money.zero(currency);
 
         if (loanTransaction.isChargesWaiver()) {
             penaltyChargesPortion = 
currentInstallment.waivePenaltyChargesComponent(transactionDate,
@@ -128,18 +130,65 @@ public class 
FineractStyleLoanRepaymentScheduleTransactionProcessor extends Abst
             }
             loanTransaction.updateComponents(principalPortion, 
interestPortion, feeChargesPortion, penaltyChargesPortion);
         } else {
-            penaltyChargesPortion = 
currentInstallment.payPenaltyChargesComponent(transactionDate, 
transactionAmountRemaining);
-            transactionAmountRemaining = 
transactionAmountRemaining.minus(penaltyChargesPortion);
-
-            feeChargesPortion = 
currentInstallment.payFeeChargesComponent(transactionDate, 
transactionAmountRemaining);
-            transactionAmountRemaining = 
transactionAmountRemaining.minus(feeChargesPortion);
-
-            interestPortion = 
currentInstallment.payInterestComponent(transactionDate, 
transactionAmountRemaining);
-            transactionAmountRemaining = 
transactionAmountRemaining.minus(interestPortion);
-
-            principalPortion = 
currentInstallment.payPrincipalComponent(transactionDate, 
transactionAmountRemaining);
-            transactionAmountRemaining = 
transactionAmountRemaining.minus(principalPortion);
+            boolean ignoreDueDateCheck = false;
+            boolean rerun = false;
+
+            List<LoanCharge> orderedLoanChargesByDueDate = 
charges.stream().filter(LoanCharge::isActive).filter(LoanCharge::isNotFullyPaid)
+                    .filter(loanCharge -> loanCharge.getEffectiveDueDate() == 
null
+                            || 
!loanCharge.getEffectiveDueDate().isAfter(transactionDate))
+                    
.sorted(LoanChargeEffectiveDueDateComparator.INSTANCE).toList();
+            Money calculatedPenaltyCharge = Money.zero(currency);
+            Money calculatedFeeCharge = Money.zero(currency);
+            // Calculate the amount of due charges
+            for (LoanCharge charge : orderedLoanChargesByDueDate) {
+                if (charge.isPenaltyCharge()) {
+                    calculatedPenaltyCharge = 
calculatedPenaltyCharge.add(charge.getAmount(currency));
+                } else {
+                    calculatedFeeCharge = 
calculatedFeeCharge.add(charge.getAmount(currency));
+                }
+            }
 
+            do {
+                Money subPenaltyPortion;
+                if (!ignoreDueDateCheck) {
+                    if 
(calculatedPenaltyCharge.isGreaterThan(transactionAmountRemaining)) {
+                        calculatedPenaltyCharge = transactionAmountRemaining;
+                    }
+                } else {
+                    calculatedPenaltyCharge = transactionAmountUnprocessed;
+                }
+                subPenaltyPortion = 
currentInstallment.payPenaltyChargesComponent(transactionDate, 
calculatedPenaltyCharge);
+                transactionAmountRemaining = 
transactionAmountRemaining.minus(subPenaltyPortion);
+                penaltyChargesPortion = 
penaltyChargesPortion.add(subPenaltyPortion);
+
+                Money subFeePortion;
+
+                if (!ignoreDueDateCheck) {
+                    if 
(calculatedFeeCharge.isGreaterThan(transactionAmountRemaining)) {
+                        calculatedFeeCharge = transactionAmountRemaining;
+                    }
+                } else {
+                    calculatedFeeCharge = transactionAmountUnprocessed;
+                }
+                subFeePortion = 
currentInstallment.payFeeChargesComponent(transactionDate, calculatedFeeCharge);
+                transactionAmountRemaining = 
transactionAmountRemaining.minus(subFeePortion);
+                feeChargesPortion = feeChargesPortion.add(subFeePortion);
+
+                if (ignoreDueDateCheck || 
!transactionDate.isBefore(currentInstallment.getDueDate())) {
+                    interestPortion = 
currentInstallment.payInterestComponent(transactionDate, 
transactionAmountRemaining);
+                    transactionAmountRemaining = 
transactionAmountRemaining.minus(interestPortion);
+                }
+
+                principalPortion = principalPortion
+                        
.add(currentInstallment.payPrincipalComponent(transactionDate, 
transactionAmountRemaining));
+                transactionAmountRemaining = 
transactionAmountRemaining.minus(principalPortion);
+                // If the transactionAmountRemaining is greater than zero, 
rerun the allocation without due date check
+                // to distribute the in advance portions
+                if (transactionAmountRemaining.isGreaterThanZero()) {
+                    ignoreDueDateCheck = true;
+                }
+                rerun = !rerun;
+            } while (ignoreDueDateCheck && rerun);
             loanTransaction.updateComponents(principalPortion, 
interestPortion, feeChargesPortion, penaltyChargesPortion);
         }
         if 
(principalPortion.plus(interestPortion).plus(feeChargesPortion).plus(penaltyChargesPortion).isGreaterThanZero())
 {
@@ -162,10 +211,10 @@ public class 
FineractStyleLoanRepaymentScheduleTransactionProcessor extends Abst
         final LocalDate transactionDate = loanTransaction.getTransactionDate();
         final MonetaryCurrency currency = 
transactionAmountUnprocessed.getCurrency();
         Money transactionAmountRemaining = transactionAmountUnprocessed;
-        Money principalPortion = 
Money.zero(transactionAmountRemaining.getCurrency());
-        Money interestPortion = 
Money.zero(transactionAmountRemaining.getCurrency());
-        Money feeChargesPortion = 
Money.zero(transactionAmountRemaining.getCurrency());
-        Money penaltyChargesPortion = 
Money.zero(transactionAmountRemaining.getCurrency());
+        Money principalPortion = Money.zero(currency);
+        Money interestPortion = Money.zero(currency);
+        Money feeChargesPortion = Money.zero(currency);
+        Money penaltyChargesPortion = Money.zero(currency);
 
         principalPortion = 
currentInstallment.unpayPrincipalComponent(transactionDate, 
transactionAmountRemaining);
         transactionAmountRemaining = 
transactionAmountRemaining.minus(principalPortion);
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/EarlyPaymentLoanRepaymentScheduleTransactionProcessor.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/EarlyPaymentLoanRepaymentScheduleTransactionProcessor.java
index f569e7278..eb3c4273e 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/EarlyPaymentLoanRepaymentScheduleTransactionProcessor.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/EarlyPaymentLoanRepaymentScheduleTransactionProcessor.java
@@ -20,8 +20,10 @@ package 
org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.im
 
 import java.time.LocalDate;
 import java.util.List;
+import java.util.Set;
 import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
 import org.apache.fineract.organisation.monetary.domain.Money;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanCharge;
 import 
org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
 import 
org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionToRepaymentScheduleMapping;
@@ -55,10 +57,10 @@ public class 
EarlyPaymentLoanRepaymentScheduleTransactionProcessor extends Abstr
     @SuppressWarnings("unused")
     @Override
     protected Money handleTransactionThatIsPaymentInAdvanceOfInstallment(final 
LoanRepaymentScheduleInstallment currentInstallment,
-            final List<LoanRepaymentScheduleInstallment> installments, final 
LoanTransaction loanTransaction,
-            final LocalDate transactionDate, final Money paymentInAdvance,
-            final List<LoanTransactionToRepaymentScheduleMapping> 
transactionMappings) {
+            final List<LoanRepaymentScheduleInstallment> installments, final 
LoanTransaction loanTransaction, final Money paymentInAdvance,
+            final List<LoanTransactionToRepaymentScheduleMapping> 
transactionMappings, Set<LoanCharge> charges) {
 
+        final LocalDate transactionDate = loanTransaction.getTransactionDate();
         final MonetaryCurrency currency = paymentInAdvance.getCurrency();
         Money transactionAmountRemaining = paymentInAdvance;
         Money principalPortion = 
Money.zero(transactionAmountRemaining.getCurrency());
@@ -112,10 +114,11 @@ public class 
EarlyPaymentLoanRepaymentScheduleTransactionProcessor extends Abstr
     @Override
     protected Money handleTransactionThatIsALateRepaymentOfInstallment(final 
LoanRepaymentScheduleInstallment currentInstallment,
             final List<LoanRepaymentScheduleInstallment> installments, final 
LoanTransaction loanTransaction,
-            final Money transactionAmountUnprocessed, 
List<LoanTransactionToRepaymentScheduleMapping> transactionMappings) {
+            final Money transactionAmountUnprocessed, 
List<LoanTransactionToRepaymentScheduleMapping> transactionMappings,
+            Set<LoanCharge> charges) {
 
         return 
handleTransactionThatIsOnTimePaymentOfInstallment(currentInstallment, 
loanTransaction, transactionAmountUnprocessed,
-                transactionMappings);
+                transactionMappings, charges);
     }
 
     /**
@@ -124,7 +127,7 @@ public class 
EarlyPaymentLoanRepaymentScheduleTransactionProcessor extends Abstr
     @Override
     protected Money handleTransactionThatIsOnTimePaymentOfInstallment(final 
LoanRepaymentScheduleInstallment currentInstallment,
             final LoanTransaction loanTransaction, final Money 
transactionAmountUnprocessed,
-            final List<LoanTransactionToRepaymentScheduleMapping> 
transactionMappings) {
+            final List<LoanTransactionToRepaymentScheduleMapping> 
transactionMappings, Set<LoanCharge> charges) {
 
         final LocalDate transactionDate = loanTransaction.getTransactionDate();
         final MonetaryCurrency currency = 
transactionAmountUnprocessed.getCurrency();
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/FineractStyleLoanRepaymentScheduleTransactionProcessor.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/FineractStyleLoanRepaymentScheduleTransactionProcessor.java
index b14158c23..11d73813b 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/FineractStyleLoanRepaymentScheduleTransactionProcessor.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/FineractStyleLoanRepaymentScheduleTransactionProcessor.java
@@ -20,8 +20,10 @@ package 
org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.im
 
 import java.time.LocalDate;
 import java.util.List;
+import java.util.Set;
 import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
 import org.apache.fineract.organisation.monetary.domain.Money;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanCharge;
 import 
org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
 import 
org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionToRepaymentScheduleMapping;
@@ -56,7 +58,7 @@ public class 
FineractStyleLoanRepaymentScheduleTransactionProcessor extends Abst
 
     @Override
     protected boolean isTransactionInAdvanceOfInstallment(final int 
currentInstallmentIndex,
-            final List<LoanRepaymentScheduleInstallment> installments, final 
LocalDate transactionDate, final Money transactionAmount) {
+            final List<LoanRepaymentScheduleInstallment> installments, final 
LocalDate transactionDate) {
 
         final LoanRepaymentScheduleInstallment currentInstallment = 
installments.get(currentInstallmentIndex);
 
@@ -68,12 +70,11 @@ public class 
FineractStyleLoanRepaymentScheduleTransactionProcessor extends Abst
      */
     @Override
     protected Money handleTransactionThatIsPaymentInAdvanceOfInstallment(final 
LoanRepaymentScheduleInstallment currentInstallment,
-            final List<LoanRepaymentScheduleInstallment> installments, final 
LoanTransaction loanTransaction,
-            final LocalDate transactionDate, final Money paymentInAdvance,
-            List<LoanTransactionToRepaymentScheduleMapping> 
transactionMappings) {
+            final List<LoanRepaymentScheduleInstallment> installments, final 
LoanTransaction loanTransaction, final Money paymentInAdvance,
+            List<LoanTransactionToRepaymentScheduleMapping> 
transactionMappings, Set<LoanCharge> charges) {
 
-        return 
handleTransactionThatIsOnTimePaymentOfInstallment(currentInstallment, 
loanTransaction, paymentInAdvance,
-                transactionMappings);
+        return 
handleTransactionThatIsOnTimePaymentOfInstallment(currentInstallment, 
loanTransaction, paymentInAdvance, transactionMappings,
+                charges);
     }
 
     /**
@@ -82,10 +83,11 @@ public class 
FineractStyleLoanRepaymentScheduleTransactionProcessor extends Abst
     @Override
     protected Money handleTransactionThatIsALateRepaymentOfInstallment(final 
LoanRepaymentScheduleInstallment currentInstallment,
             final List<LoanRepaymentScheduleInstallment> installments, final 
LoanTransaction loanTransaction,
-            final Money transactionAmountUnprocessed, 
List<LoanTransactionToRepaymentScheduleMapping> transactionMappings) {
+            final Money transactionAmountUnprocessed, 
List<LoanTransactionToRepaymentScheduleMapping> transactionMappings,
+            Set<LoanCharge> charges) {
 
         return 
handleTransactionThatIsOnTimePaymentOfInstallment(currentInstallment, 
loanTransaction, transactionAmountUnprocessed,
-                transactionMappings);
+                transactionMappings, charges);
     }
 
     /**
@@ -94,7 +96,7 @@ public class 
FineractStyleLoanRepaymentScheduleTransactionProcessor extends Abst
     @Override
     protected Money handleTransactionThatIsOnTimePaymentOfInstallment(final 
LoanRepaymentScheduleInstallment currentInstallment,
             final LoanTransaction loanTransaction, final Money 
transactionAmountUnprocessed,
-            List<LoanTransactionToRepaymentScheduleMapping> 
transactionMappings) {
+            List<LoanTransactionToRepaymentScheduleMapping> 
transactionMappings, Set<LoanCharge> charges) {
 
         final LocalDate transactionDate = loanTransaction.getTransactionDate();
         final MonetaryCurrency currency = 
transactionAmountUnprocessed.getCurrency();
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/HeavensFamilyLoanRepaymentScheduleTransactionProcessor.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/HeavensFamilyLoanRepaymentScheduleTransactionProcessor.java
index b752e0c97..3e327c5cd 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/HeavensFamilyLoanRepaymentScheduleTransactionProcessor.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/HeavensFamilyLoanRepaymentScheduleTransactionProcessor.java
@@ -20,8 +20,10 @@ package 
org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.im
 
 import java.time.LocalDate;
 import java.util.List;
+import java.util.Set;
 import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
 import org.apache.fineract.organisation.monetary.domain.Money;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanCharge;
 import 
org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
 import 
org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionToRepaymentScheduleMapping;
@@ -61,15 +63,16 @@ public class 
HeavensFamilyLoanRepaymentScheduleTransactionProcessor extends Abst
     @Override
     protected Money handleTransactionThatIsALateRepaymentOfInstallment(final 
LoanRepaymentScheduleInstallment currentInstallment,
             final List<LoanRepaymentScheduleInstallment> installments, final 
LoanTransaction loanTransaction,
-            final Money transactionAmountUnprocessed, 
List<LoanTransactionToRepaymentScheduleMapping> transactionMappings) {
+            final Money transactionAmountUnprocessed, 
List<LoanTransactionToRepaymentScheduleMapping> transactionMappings,
+            Set<LoanCharge> charges) {
 
         return 
handleTransactionThatIsOnTimePaymentOfInstallment(currentInstallment, 
loanTransaction, transactionAmountUnprocessed,
-                transactionMappings);
+                transactionMappings, charges);
     }
 
     @Override
     protected boolean isTransactionInAdvanceOfInstallment(final int 
currentInstallmentIndex,
-            final List<LoanRepaymentScheduleInstallment> installments, final 
LocalDate transactionDate, final Money transactionAmount) {
+            final List<LoanRepaymentScheduleInstallment> installments, final 
LocalDate transactionDate) {
 
         boolean isInAdvance = false;
 
@@ -92,10 +95,10 @@ public class 
HeavensFamilyLoanRepaymentScheduleTransactionProcessor extends Abst
      */
     @Override
     protected Money handleTransactionThatIsPaymentInAdvanceOfInstallment(final 
LoanRepaymentScheduleInstallment currentInstallment,
-            final List<LoanRepaymentScheduleInstallment> installments, final 
LoanTransaction loanTransaction,
-            final LocalDate transactionDate, final Money paymentInAdvance,
-            final List<LoanTransactionToRepaymentScheduleMapping> 
transactionMappings) {
+            final List<LoanRepaymentScheduleInstallment> installments, final 
LoanTransaction loanTransaction, final Money paymentInAdvance,
+            final List<LoanTransactionToRepaymentScheduleMapping> 
transactionMappings, Set<LoanCharge> charges) {
 
+        final LocalDate transactionDate = loanTransaction.getTransactionDate();
         final MonetaryCurrency currency = paymentInAdvance.getCurrency();
         Money transactionAmountRemaining = paymentInAdvance;
         Money principalPortion = Money.zero(currency);
@@ -158,7 +161,7 @@ public class 
HeavensFamilyLoanRepaymentScheduleTransactionProcessor extends Abst
     @Override
     protected Money handleTransactionThatIsOnTimePaymentOfInstallment(final 
LoanRepaymentScheduleInstallment currentInstallment,
             final LoanTransaction loanTransaction, final Money 
transactionAmountUnprocessed,
-            List<LoanTransactionToRepaymentScheduleMapping> 
transactionMappings) {
+            List<LoanTransactionToRepaymentScheduleMapping> 
transactionMappings, Set<LoanCharge> charges) {
 
         final LocalDate transactionDate = loanTransaction.getTransactionDate();
         final MonetaryCurrency currency = 
transactionAmountUnprocessed.getCurrency();
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/InterestPrincipalPenaltyFeesOrderLoanRepaymentScheduleTransactionProcessor.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/InterestPrincipalPenaltyFeesOrderLoanRepaymentScheduleTransactionProcessor.java
index 23b9d9648..05af5e8f3 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/InterestPrincipalPenaltyFeesOrderLoanRepaymentScheduleTransactionProcessor.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/InterestPrincipalPenaltyFeesOrderLoanRepaymentScheduleTransactionProcessor.java
@@ -20,8 +20,10 @@ package 
org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.im
 
 import java.time.LocalDate;
 import java.util.List;
+import java.util.Set;
 import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
 import org.apache.fineract.organisation.monetary.domain.Money;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanCharge;
 import 
org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
 import 
org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionToRepaymentScheduleMapping;
@@ -56,12 +58,11 @@ public class 
InterestPrincipalPenaltyFeesOrderLoanRepaymentScheduleTransactionPr
     @SuppressWarnings("unused")
     @Override
     protected Money handleTransactionThatIsPaymentInAdvanceOfInstallment(final 
LoanRepaymentScheduleInstallment currentInstallment,
-            final List<LoanRepaymentScheduleInstallment> installments, final 
LoanTransaction loanTransaction,
-            final LocalDate transactionDate, final Money paymentInAdvance,
-            List<LoanTransactionToRepaymentScheduleMapping> 
transactionMappings) {
+            final List<LoanRepaymentScheduleInstallment> installments, final 
LoanTransaction loanTransaction, final Money paymentInAdvance,
+            List<LoanTransactionToRepaymentScheduleMapping> 
transactionMappings, Set<LoanCharge> charges) {
 
-        return 
handleTransactionThatIsOnTimePaymentOfInstallment(currentInstallment, 
loanTransaction, paymentInAdvance,
-                transactionMappings);
+        return 
handleTransactionThatIsOnTimePaymentOfInstallment(currentInstallment, 
loanTransaction, paymentInAdvance, transactionMappings,
+                charges);
     }
 
     /**
@@ -71,10 +72,11 @@ public class 
InterestPrincipalPenaltyFeesOrderLoanRepaymentScheduleTransactionPr
     @Override
     protected Money handleTransactionThatIsALateRepaymentOfInstallment(final 
LoanRepaymentScheduleInstallment currentInstallment,
             final List<LoanRepaymentScheduleInstallment> installments, final 
LoanTransaction loanTransaction,
-            final Money transactionAmountUnprocessed, 
List<LoanTransactionToRepaymentScheduleMapping> transactionMappings) {
+            final Money transactionAmountUnprocessed, 
List<LoanTransactionToRepaymentScheduleMapping> transactionMappings,
+            Set<LoanCharge> charges) {
 
         return 
handleTransactionThatIsOnTimePaymentOfInstallment(currentInstallment, 
loanTransaction, transactionAmountUnprocessed,
-                transactionMappings);
+                transactionMappings, charges);
     }
 
     /**
@@ -83,7 +85,7 @@ public class 
InterestPrincipalPenaltyFeesOrderLoanRepaymentScheduleTransactionPr
     @Override
     protected Money handleTransactionThatIsOnTimePaymentOfInstallment(final 
LoanRepaymentScheduleInstallment currentInstallment,
             final LoanTransaction loanTransaction, final Money 
transactionAmountUnprocessed,
-            List<LoanTransactionToRepaymentScheduleMapping> 
transactionMappings) {
+            List<LoanTransactionToRepaymentScheduleMapping> 
transactionMappings, Set<LoanCharge> charges) {
 
         final LocalDate transactionDate = loanTransaction.getTransactionDate();
         final MonetaryCurrency currency = 
transactionAmountUnprocessed.getCurrency();
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/PrincipalInterestPenaltyFeesOrderLoanRepaymentScheduleTransactionProcessor.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/PrincipalInterestPenaltyFeesOrderLoanRepaymentScheduleTransactionProcessor.java
index 725b1f774..898d909be 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/PrincipalInterestPenaltyFeesOrderLoanRepaymentScheduleTransactionProcessor.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/PrincipalInterestPenaltyFeesOrderLoanRepaymentScheduleTransactionProcessor.java
@@ -20,8 +20,10 @@ package 
org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.im
 
 import java.time.LocalDate;
 import java.util.List;
+import java.util.Set;
 import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
 import org.apache.fineract.organisation.monetary.domain.Money;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanCharge;
 import 
org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
 import 
org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionToRepaymentScheduleMapping;
@@ -56,12 +58,11 @@ public class 
PrincipalInterestPenaltyFeesOrderLoanRepaymentScheduleTransactionPr
     @SuppressWarnings("unused")
     @Override
     protected Money handleTransactionThatIsPaymentInAdvanceOfInstallment(final 
LoanRepaymentScheduleInstallment currentInstallment,
-            final List<LoanRepaymentScheduleInstallment> installments, final 
LoanTransaction loanTransaction,
-            final LocalDate transactionDate, final Money paymentInAdvance,
-            List<LoanTransactionToRepaymentScheduleMapping> 
transactionMappings) {
+            final List<LoanRepaymentScheduleInstallment> installments, final 
LoanTransaction loanTransaction, final Money paymentInAdvance,
+            List<LoanTransactionToRepaymentScheduleMapping> 
transactionMappings, Set<LoanCharge> charges) {
 
-        return 
handleTransactionThatIsOnTimePaymentOfInstallment(currentInstallment, 
loanTransaction, paymentInAdvance,
-                transactionMappings);
+        return 
handleTransactionThatIsOnTimePaymentOfInstallment(currentInstallment, 
loanTransaction, paymentInAdvance, transactionMappings,
+                charges);
     }
 
     /**
@@ -71,10 +72,11 @@ public class 
PrincipalInterestPenaltyFeesOrderLoanRepaymentScheduleTransactionPr
     @Override
     protected Money handleTransactionThatIsALateRepaymentOfInstallment(final 
LoanRepaymentScheduleInstallment currentInstallment,
             final List<LoanRepaymentScheduleInstallment> installments, final 
LoanTransaction loanTransaction,
-            final Money transactionAmountUnprocessed, 
List<LoanTransactionToRepaymentScheduleMapping> transactionMappings) {
+            final Money transactionAmountUnprocessed, 
List<LoanTransactionToRepaymentScheduleMapping> transactionMappings,
+            Set<LoanCharge> charges) {
 
         return 
handleTransactionThatIsOnTimePaymentOfInstallment(currentInstallment, 
loanTransaction, transactionAmountUnprocessed,
-                transactionMappings);
+                transactionMappings, charges);
     }
 
     /**
@@ -83,7 +85,7 @@ public class 
PrincipalInterestPenaltyFeesOrderLoanRepaymentScheduleTransactionPr
     @Override
     protected Money handleTransactionThatIsOnTimePaymentOfInstallment(final 
LoanRepaymentScheduleInstallment currentInstallment,
             final LoanTransaction loanTransaction, final Money 
transactionAmountUnprocessed,
-            List<LoanTransactionToRepaymentScheduleMapping> 
transactionMappings) {
+            List<LoanTransactionToRepaymentScheduleMapping> 
transactionMappings, Set<LoanCharge> charges) {
 
         final LocalDate transactionDate = loanTransaction.getTransactionDate();
         final MonetaryCurrency currency = 
transactionAmountUnprocessed.getCurrency();
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/RBILoanRepaymentScheduleTransactionProcessor.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/RBILoanRepaymentScheduleTransactionProcessor.java
index c09200357..97de4da3b 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/RBILoanRepaymentScheduleTransactionProcessor.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/RBILoanRepaymentScheduleTransactionProcessor.java
@@ -20,8 +20,10 @@ package 
org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.im
 
 import java.time.LocalDate;
 import java.util.List;
+import java.util.Set;
 import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
 import org.apache.fineract.organisation.monetary.domain.Money;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanCharge;
 import 
org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
 import 
org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionToRepaymentScheduleMapping;
@@ -63,7 +65,7 @@ public class RBILoanRepaymentScheduleTransactionProcessor 
extends AbstractLoanRe
     @SuppressWarnings("unused")
     @Override
     protected boolean isTransactionInAdvanceOfInstallment(final int 
currentInstallmentIndex,
-            final List<LoanRepaymentScheduleInstallment> installments, final 
LocalDate transactionDate, final Money transactionAmount) {
+            final List<LoanRepaymentScheduleInstallment> installments, final 
LocalDate transactionDate) {
 
         final LoanRepaymentScheduleInstallment currentInstallment = 
installments.get(currentInstallmentIndex);
 
@@ -76,12 +78,11 @@ public class RBILoanRepaymentScheduleTransactionProcessor 
extends AbstractLoanRe
     @SuppressWarnings("unused")
     @Override
     protected Money handleTransactionThatIsPaymentInAdvanceOfInstallment(final 
LoanRepaymentScheduleInstallment currentInstallment,
-            final List<LoanRepaymentScheduleInstallment> installments, final 
LoanTransaction loanTransaction,
-            final LocalDate transactionDate, final Money paymentInAdvance,
-            List<LoanTransactionToRepaymentScheduleMapping> 
transactionMappings) {
+            final List<LoanRepaymentScheduleInstallment> installments, final 
LoanTransaction loanTransaction, final Money paymentInAdvance,
+            List<LoanTransactionToRepaymentScheduleMapping> 
transactionMappings, Set<LoanCharge> charges) {
 
-        return 
handleTransactionThatIsOnTimePaymentOfInstallment(currentInstallment, 
loanTransaction, paymentInAdvance,
-                transactionMappings);
+        return 
handleTransactionThatIsOnTimePaymentOfInstallment(currentInstallment, 
loanTransaction, paymentInAdvance, transactionMappings,
+                charges);
     }
 
     /**
@@ -90,7 +91,8 @@ public class RBILoanRepaymentScheduleTransactionProcessor 
extends AbstractLoanRe
     @Override
     protected Money handleTransactionThatIsALateRepaymentOfInstallment(final 
LoanRepaymentScheduleInstallment currentInstallment,
             final List<LoanRepaymentScheduleInstallment> installments, final 
LoanTransaction loanTransaction,
-            final Money transactionAmountUnprocessed, 
List<LoanTransactionToRepaymentScheduleMapping> transactionMappings) {
+            final Money transactionAmountUnprocessed, 
List<LoanTransactionToRepaymentScheduleMapping> transactionMappings,
+            Set<LoanCharge> charges) {
 
         // pay of overdue and current interest due given transaction date
         final LocalDate transactionDate = loanTransaction.getTransactionDate();
@@ -203,7 +205,7 @@ public class RBILoanRepaymentScheduleTransactionProcessor 
extends AbstractLoanRe
     @Override
     protected Money handleTransactionThatIsOnTimePaymentOfInstallment(final 
LoanRepaymentScheduleInstallment currentInstallment,
             final LoanTransaction loanTransaction, final Money 
transactionAmountUnprocessed,
-            final List<LoanTransactionToRepaymentScheduleMapping> 
transactionMappings) {
+            final List<LoanTransactionToRepaymentScheduleMapping> 
transactionMappings, Set<LoanCharge> charges) {
 
         final LocalDate transactionDate = loanTransaction.getTransactionDate();
         final MonetaryCurrency currency = 
transactionAmountUnprocessed.getCurrency();
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/AbstractLoanScheduleGenerator.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/AbstractLoanScheduleGenerator.java
index 991e53d0c..796508e59 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/AbstractLoanScheduleGenerator.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/AbstractLoanScheduleGenerator.java
@@ -505,7 +505,7 @@ public abstract class AbstractLoanScheduleGenerator 
implements LoanScheduleGener
                     // on scheduled installments to identify the
                     // unprocessed(early payment ) amounts
                     Money unprocessed = 
loanRepaymentScheduleTransactionProcessor.handleRepaymentSchedule(currentTransactions,
 currency,
-                            scheduleParams.getInstallments());
+                            scheduleParams.getInstallments(), loanCharges);
 
                     if (unprocessed.isGreaterThanZero()) {
                         scheduleParams.reduceOutstandingBalance(unprocessed);
@@ -805,7 +805,7 @@ public abstract class AbstractLoanScheduleGenerator 
implements LoanScheduleGener
                         // on scheduled installments to identify the
                         // unprocessed(early payment ) amounts
                         Money unprocessed = 
scheduleParams.getLoanRepaymentScheduleTransactionProcessor()
-                                .handleRepaymentSchedule(currentTransactions, 
currency, scheduleParams.getInstallments());
+                                .handleRepaymentSchedule(currentTransactions, 
currency, scheduleParams.getInstallments(), loanCharges);
                         if (unprocessed.isGreaterThanZero()) {
 
                             if 
(loanApplicationTerms.getPreClosureInterestCalculationStrategy().calculateTillRestFrequencyEnabled())
 {
@@ -850,7 +850,7 @@ public abstract class AbstractLoanScheduleGenerator 
implements LoanScheduleGener
                         if (applicableDate.isBefore(scheduledDueDate)) {
                             List<LoanTransaction> currentTransactions = 
createCurrentTransactionList(detail);
                             Money unprocessed = 
scheduleParams.getLoanRepaymentScheduleTransactionProcessor()
-                                    
.handleRepaymentSchedule(currentTransactions, currency, 
scheduleParams.getInstallments());
+                                    
.handleRepaymentSchedule(currentTransactions, currency, 
scheduleParams.getInstallments(), loanCharges);
                             Money arrears = fetchArrears(loanApplicationTerms, 
currency, detail.getTransaction());
                             if (unprocessed.isGreaterThanZero()) {
                                 
updateMapWithAmount(scheduleParams.getPrincipalPortionMap(), unprocessed, 
applicableDate);
@@ -1377,7 +1377,8 @@ public abstract class AbstractLoanScheduleGenerator 
implements LoanScheduleGener
                     startDate = transactionDate;
                     additionalPeriodsStartDate = startDate;
                 }
-                
loanRepaymentScheduleTransactionProcessor.handleRepaymentSchedule(currentTransactions,
 currency, params.getInstallments());
+                
loanRepaymentScheduleTransactionProcessor.handleRepaymentSchedule(currentTransactions,
 currency, params.getInstallments(),
+                        loanCharges);
                 updateLatePaidAmountsToPrincipalMap(detail.getTransaction(), 
loanApplicationTerms, currency, holidayDetailDTO, lastRestDate,
                         params);
                 updateLatePaymentsToMap(loanApplicationTerms, 
holidayDetailDTO, currency, params.getLatePaymentMap(), currentDate,
@@ -2323,7 +2324,7 @@ public abstract class AbstractLoanScheduleGenerator 
implements LoanScheduleGener
 
                     Money unprocessed = 
updateEarlyPaidAmountsToMap(loanApplicationTerms, holidayDetailDTO,
                             loanRepaymentScheduleTransactionProcessor, 
newRepaymentScheduleInstallments, currency, principalPortionMap,
-                            installment, applicableTransactions, 
actualPrincipalPortion);
+                            installment, applicableTransactions, 
actualPrincipalPortion, loan.getActiveCharges());
 
                     // this block is to adjust the period number based on the
                     // actual
@@ -2413,7 +2414,7 @@ public abstract class AbstractLoanScheduleGenerator 
implements LoanScheduleGener
         return LoanScheduleDTO.from(retainedInstallments, 
loanScheduleModelwithPeriodChanges);
     }
 
-    public List<LoanRepaymentScheduleInstallment> fetchRetainedInstallments(
+    private List<LoanRepaymentScheduleInstallment> fetchRetainedInstallments(
             final List<LoanRepaymentScheduleInstallment> 
repaymentScheduleInstallments, final LocalDate rescheduleFrom,
             MonetaryCurrency currency) {
         List<LoanRepaymentScheduleInstallment> 
newRepaymentScheduleInstallments = new ArrayList<>();
@@ -2455,7 +2456,7 @@ public abstract class AbstractLoanScheduleGenerator 
implements LoanScheduleGener
             final LoanRepaymentScheduleTransactionProcessor 
loanRepaymentScheduleTransactionProcessor,
             final List<LoanRepaymentScheduleInstallment> 
newRepaymentScheduleInstallments, MonetaryCurrency currency,
             final Map<LocalDate, Money> principalPortionMap, 
LoanRepaymentScheduleInstallment installment,
-            Collection<RecalculationDetail> applicableTransactions, Money 
actualPrincipalPortion) {
+            Collection<RecalculationDetail> applicableTransactions, Money 
actualPrincipalPortion, Set<LoanCharge> loanCharges) {
         Money unprocessed = Money.zero(currency);
         Money totalUnprocessed = Money.zero(currency);
         for (RecalculationDetail detail : applicableTransactions) {
@@ -2467,7 +2468,7 @@ public abstract class AbstractLoanScheduleGenerator 
implements LoanScheduleGener
                 // on scheduled installments to identify the
                 // unprocessed(early payment ) amounts
                 
loanRepaymentScheduleTransactionProcessor.handleRepaymentSchedule(currentTransactions,
 currency,
-                        newRepaymentScheduleInstallments);
+                        newRepaymentScheduleInstallments, loanCharges);
 
                 // Identifies totalEarlyPayment and early paid amount with this
                 // transaction
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/starter/CreocoreLoanRepaymentScheduleTransactionProcessorCondition.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/starter/CreocoreLoanRepaymentScheduleTransactionProcessorCondition.java
new file mode 100644
index 000000000..2a2dd4f30
--- /dev/null
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/starter/CreocoreLoanRepaymentScheduleTransactionProcessorCondition.java
@@ -0,0 +1,30 @@
+/**
+ * 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.portfolio.loanaccount.starter;
+
+import org.apache.fineract.infrastructure.core.condition.PropertiesCondition;
+import org.apache.fineract.infrastructure.core.config.FineractProperties;
+
+public class CreocoreLoanRepaymentScheduleTransactionProcessorCondition 
extends PropertiesCondition {
+
+    @Override
+    protected boolean matches(FineractProperties properties) {
+        return 
properties.getLoan().getTransactionProcessor().getCreocore().isEnabled();
+    }
+}
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/starter/DueDateRespectiveLoanRepaymentScheduleTransactionProcessorCondition.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/starter/DueDateRespectiveLoanRepaymentScheduleTransactionProcessorCondition.java
new file mode 100644
index 000000000..7bead6d98
--- /dev/null
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/starter/DueDateRespectiveLoanRepaymentScheduleTransactionProcessorCondition.java
@@ -0,0 +1,31 @@
+/**
+ * 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.portfolio.loanaccount.starter;
+
+import org.apache.fineract.infrastructure.core.condition.PropertiesCondition;
+import org.apache.fineract.infrastructure.core.config.FineractProperties;
+
+public class 
DueDateRespectiveLoanRepaymentScheduleTransactionProcessorCondition extends 
PropertiesCondition {
+
+    @Override
+    protected boolean matches(FineractProperties properties) {
+        return 
properties.getLoan().getTransactionProcessor().getDuePenaltyFeeInterestPrincipalInAdvancePrincipalPenaltyFeeInterest()
+                .isEnabled();
+    }
+}
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/starter/EarlyRepaymentLoanRepaymentScheduleTransactionProcessorCondition.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/starter/EarlyRepaymentLoanRepaymentScheduleTransactionProcessorCondition.java
new file mode 100644
index 000000000..e7ac158c6
--- /dev/null
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/starter/EarlyRepaymentLoanRepaymentScheduleTransactionProcessorCondition.java
@@ -0,0 +1,30 @@
+/**
+ * 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.portfolio.loanaccount.starter;
+
+import org.apache.fineract.infrastructure.core.condition.PropertiesCondition;
+import org.apache.fineract.infrastructure.core.config.FineractProperties;
+
+public class EarlyRepaymentLoanRepaymentScheduleTransactionProcessorCondition 
extends PropertiesCondition {
+
+    @Override
+    protected boolean matches(FineractProperties properties) {
+        return 
properties.getLoan().getTransactionProcessor().getEarlyRepayment().isEnabled();
+    }
+}
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/starter/HeavensFamilyLoanRepaymentScheduleTransactionProcessorCondition.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/starter/HeavensFamilyLoanRepaymentScheduleTransactionProcessorCondition.java
new file mode 100644
index 000000000..9e681a40f
--- /dev/null
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/starter/HeavensFamilyLoanRepaymentScheduleTransactionProcessorCondition.java
@@ -0,0 +1,30 @@
+/**
+ * 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.portfolio.loanaccount.starter;
+
+import org.apache.fineract.infrastructure.core.condition.PropertiesCondition;
+import org.apache.fineract.infrastructure.core.config.FineractProperties;
+
+public class HeavensFamilyLoanRepaymentScheduleTransactionProcessorCondition 
extends PropertiesCondition {
+
+    @Override
+    protected boolean matches(FineractProperties properties) {
+        return 
properties.getLoan().getTransactionProcessor().getHeavensFamily().isEnabled();
+    }
+}
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/starter/InterestPrincipalPenaltiesFeesLoanRepaymentScheduleTransactionProcessorCondition.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/starter/InterestPrincipalPenaltiesFeesLoanRepaymentScheduleTransactionProcessorCondition.java
new file mode 100644
index 000000000..cac74e1f8
--- /dev/null
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/starter/InterestPrincipalPenaltiesFeesLoanRepaymentScheduleTransactionProcessorCondition.java
@@ -0,0 +1,30 @@
+/**
+ * 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.portfolio.loanaccount.starter;
+
+import org.apache.fineract.infrastructure.core.condition.PropertiesCondition;
+import org.apache.fineract.infrastructure.core.config.FineractProperties;
+
+public class 
InterestPrincipalPenaltiesFeesLoanRepaymentScheduleTransactionProcessorCondition
 extends PropertiesCondition {
+
+    @Override
+    protected boolean matches(FineractProperties properties) {
+        return 
properties.getLoan().getTransactionProcessor().getInterestPrincipalPenaltiesFees().isEnabled();
+    }
+}
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/starter/LoanAccountAutoStarter.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/starter/LoanAccountAutoStarter.java
index be365229f..8e04519cd 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/starter/LoanAccountAutoStarter.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/starter/LoanAccountAutoStarter.java
@@ -22,6 +22,7 @@ import java.util.List;
 import 
org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleTransactionProcessorFactory;
 import 
org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.LoanRepaymentScheduleTransactionProcessor;
 import 
org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.CreocoreLoanRepaymentScheduleTransactionProcessor;
+import 
org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.DueDateRespectiveLoanRepaymentScheduleTransactionProcessor;
 import 
org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.EarlyPaymentLoanRepaymentScheduleTransactionProcessor;
 import 
org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.FineractStyleLoanRepaymentScheduleTransactionProcessor;
 import 
org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.HeavensFamilyLoanRepaymentScheduleTransactionProcessor;
@@ -29,55 +30,61 @@ import 
org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.imp
 import 
org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.PrincipalInterestPenaltyFeesOrderLoanRepaymentScheduleTransactionProcessor;
 import 
org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.RBILoanRepaymentScheduleTransactionProcessor;
 import 
org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
 import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Conditional;
 import org.springframework.context.annotation.Configuration;
 
 @Configuration
 public class LoanAccountAutoStarter {
 
     @Bean
-    
@ConditionalOnProperty("fineract.loan.transactionprocessor.creocore.enabled")
+    
@Conditional(CreocoreLoanRepaymentScheduleTransactionProcessorCondition.class)
     public CreocoreLoanRepaymentScheduleTransactionProcessor 
creocoreLoanRepaymentScheduleTransactionProcessor() {
         return new CreocoreLoanRepaymentScheduleTransactionProcessor();
     }
 
     @Bean
-    
@ConditionalOnProperty("fineract.loan.transactionprocessor.early-repayment.enabled")
+    
@Conditional(EarlyRepaymentLoanRepaymentScheduleTransactionProcessorCondition.class)
     public EarlyPaymentLoanRepaymentScheduleTransactionProcessor 
earlyPaymentLoanRepaymentScheduleTransactionProcessor() {
         return new EarlyPaymentLoanRepaymentScheduleTransactionProcessor();
     }
 
     @Bean
-    
@ConditionalOnProperty("fineract.loan.transactionprocessor.mifos-standard.enabled")
+    
@Conditional(MifosStandardLoanRepaymentScheduleTransactionProcessorCondition.class)
     public FineractStyleLoanRepaymentScheduleTransactionProcessor 
fineractStyleLoanRepaymentScheduleTransactionProcessor() {
         return new FineractStyleLoanRepaymentScheduleTransactionProcessor();
     }
 
     @Bean
-    
@ConditionalOnProperty("fineract.loan.transactionprocessor.heavensfamily.enabled")
+    
@Conditional(HeavensFamilyLoanRepaymentScheduleTransactionProcessorCondition.class)
     public HeavensFamilyLoanRepaymentScheduleTransactionProcessor 
heavensFamilyLoanRepaymentScheduleTransactionProcessor() {
         return new HeavensFamilyLoanRepaymentScheduleTransactionProcessor();
     }
 
     @Bean
-    
@ConditionalOnProperty("fineract.loan.transactionprocessor.interest-principal-penalties-fees.enabled")
+    
@Conditional(InterestPrincipalPenaltiesFeesLoanRepaymentScheduleTransactionProcessorCondition.class)
     public 
InterestPrincipalPenaltyFeesOrderLoanRepaymentScheduleTransactionProcessor 
interestPrincipalPenaltyFeesOrderLoanRepaymentScheduleTransactionProcessor() {
         return new 
InterestPrincipalPenaltyFeesOrderLoanRepaymentScheduleTransactionProcessor();
     }
 
     @Bean
-    
@ConditionalOnProperty("fineract.loan.transactionprocessor.principal-interest-penalties-fees.enabled")
+    
@Conditional(PrincipalInterestPenaltiesFeesLoanRepaymentScheduleTransactionProcessorCondition.class)
     public 
PrincipalInterestPenaltyFeesOrderLoanRepaymentScheduleTransactionProcessor 
principalInterestPenaltyFeesOrderLoanRepaymentScheduleTransactionProcessor() {
         return new 
PrincipalInterestPenaltyFeesOrderLoanRepaymentScheduleTransactionProcessor();
     }
 
     @Bean
-    
@ConditionalOnProperty("fineract.loan.transactionprocessor.rbi-india.enabled")
+    
@Conditional(RBIIndiaLoanRepaymentScheduleTransactionProcessorCondition.class)
     public RBILoanRepaymentScheduleTransactionProcessor 
rbiLoanRepaymentScheduleTransactionProcessor() {
         return new RBILoanRepaymentScheduleTransactionProcessor();
     }
 
+    @Bean
+    
@Conditional(DueDateRespectiveLoanRepaymentScheduleTransactionProcessorCondition.class)
+    public DueDateRespectiveLoanRepaymentScheduleTransactionProcessor 
dueDateRespectiveTransactionProcessor() {
+        return new 
DueDateRespectiveLoanRepaymentScheduleTransactionProcessor();
+    }
+
     @Bean
     
@ConditionalOnMissingBean(LoanRepaymentScheduleTransactionProcessorFactory.class)
     public LoanRepaymentScheduleTransactionProcessorFactory 
loanRepaymentScheduleTransactionProcessorFactory(
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/starter/MifosStandardLoanRepaymentScheduleTransactionProcessorCondition.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/starter/MifosStandardLoanRepaymentScheduleTransactionProcessorCondition.java
new file mode 100644
index 000000000..196cf2f93
--- /dev/null
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/starter/MifosStandardLoanRepaymentScheduleTransactionProcessorCondition.java
@@ -0,0 +1,30 @@
+/**
+ * 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.portfolio.loanaccount.starter;
+
+import org.apache.fineract.infrastructure.core.condition.PropertiesCondition;
+import org.apache.fineract.infrastructure.core.config.FineractProperties;
+
+public class MifosStandardLoanRepaymentScheduleTransactionProcessorCondition 
extends PropertiesCondition {
+
+    @Override
+    protected boolean matches(FineractProperties properties) {
+        return 
properties.getLoan().getTransactionProcessor().getMifosStandard().isEnabled();
+    }
+}
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/starter/PrincipalInterestPenaltiesFeesLoanRepaymentScheduleTransactionProcessorCondition.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/starter/PrincipalInterestPenaltiesFeesLoanRepaymentScheduleTransactionProcessorCondition.java
new file mode 100644
index 000000000..2afa6cee6
--- /dev/null
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/starter/PrincipalInterestPenaltiesFeesLoanRepaymentScheduleTransactionProcessorCondition.java
@@ -0,0 +1,30 @@
+/**
+ * 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.portfolio.loanaccount.starter;
+
+import org.apache.fineract.infrastructure.core.condition.PropertiesCondition;
+import org.apache.fineract.infrastructure.core.config.FineractProperties;
+
+public class 
PrincipalInterestPenaltiesFeesLoanRepaymentScheduleTransactionProcessorCondition
 extends PropertiesCondition {
+
+    @Override
+    protected boolean matches(FineractProperties properties) {
+        return 
properties.getLoan().getTransactionProcessor().getPrincipalInterestPenaltiesFees().isEnabled();
+    }
+}
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/starter/RBIIndiaLoanRepaymentScheduleTransactionProcessorCondition.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/starter/RBIIndiaLoanRepaymentScheduleTransactionProcessorCondition.java
new file mode 100644
index 000000000..88754617b
--- /dev/null
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/starter/RBIIndiaLoanRepaymentScheduleTransactionProcessorCondition.java
@@ -0,0 +1,30 @@
+/**
+ * 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.portfolio.loanaccount.starter;
+
+import org.apache.fineract.infrastructure.core.condition.PropertiesCondition;
+import org.apache.fineract.infrastructure.core.config.FineractProperties;
+
+public class RBIIndiaLoanRepaymentScheduleTransactionProcessorCondition 
extends PropertiesCondition {
+
+    @Override
+    protected boolean matches(FineractProperties properties) {
+        return 
properties.getLoan().getTransactionProcessor().getRbiIndia().isEnabled();
+    }
+}
diff --git a/fineract-provider/src/main/resources/application.properties 
b/fineract-provider/src/main/resources/application.properties
index 195496ba0..4521a4791 100644
--- a/fineract-provider/src/main/resources/application.properties
+++ b/fineract-provider/src/main/resources/application.properties
@@ -81,6 +81,7 @@ 
fineract.loan.transactionprocessor.heavensfamily.enabled=${FINERACT_LOAN_TRANSAC
 
fineract.loan.transactionprocessor.interest-principal-penalties-fees.enabled=${FINERACT_LOAN_TRANSACTIONPROCESSOR_INTEREST_PRINCIPAL_PENALTIES_FEES_ENABLED:true}
 
fineract.loan.transactionprocessor.principal-interest-penalties-fees.enabled=${FINERACT_LOAN_TRANSACTIONPROCESSOR_PRINCIPAL_INTEREST_PENALTIES_FEES_ENABLED:true}
 
fineract.loan.transactionprocessor.rbi-india.enabled=${FINERACT_LOAN_TRANSACTIONPROCESSOR_RBI_INDIA_ENABLED:true}
+fineract.loan.transactionprocessor.due-penalty-fee-interest-principal-in-advance-principal-penalty-fee-interest.enabled=${FINERACT_LOAN_TRANSACTIONPROCESSOR_DUE_PENALTY_FEE_INTEREST_PRINCIPAL_IN_ADVANCE_PRINCIPAL_PENALTY_FEE_INTEREST_ENABLED:true}
 
fineract.loan.transactionprocessor.error-not-found-fail=${FINERACT_LOAN_TRANSACTIONPROCESSOR_ERROR_NOT_FOUND_FAIL:true}
 
 
fineract.content.regex-whitelist-enabled=${FINERACT_CONTENT_REGEX_WHITELIST_ENABLED:true}
diff --git 
a/fineract-provider/src/main/resources/db/changelog/tenant/changelog-tenant.xml 
b/fineract-provider/src/main/resources/db/changelog/tenant/changelog-tenant.xml
index 8d74fd24a..bcc49f35a 100644
--- 
a/fineract-provider/src/main/resources/db/changelog/tenant/changelog-tenant.xml
+++ 
b/fineract-provider/src/main/resources/db/changelog/tenant/changelog-tenant.xml
@@ -119,4 +119,5 @@
     <include file="parts/0097_update_accounting_summary_table_reports.xml" 
relativeToChangelogFile="true" />
     <include file="parts/0098_update_transaction_summary_table_report.xml" 
relativeToChangelogFile="true" />
     <include 
file="parts/0099_add_accrual_transaction_external_event_configuration.xml" 
relativeToChangelogFile="true" />
+    <include file="parts/0100_new_repayment_strategy.xml" 
relativeToChangelogFile="true" />
 </databaseChangeLog>
diff --git 
a/fineract-provider/src/main/resources/db/changelog/tenant/parts/0100_new_repayment_strategy.xml
 
b/fineract-provider/src/main/resources/db/changelog/tenant/parts/0100_new_repayment_strategy.xml
new file mode 100644
index 000000000..bc48a0c12
--- /dev/null
+++ 
b/fineract-provider/src/main/resources/db/changelog/tenant/parts/0100_new_repayment_strategy.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    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.
+
+-->
+<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog";
+                   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
+                   
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog 
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.1.xsd";>
+    <changeSet author="fineract" id="1">
+        <insert tableName="r_enum_value">
+            <column name="enum_name" value="loan_transaction_strategy_id"/>
+            <column name="enum_id" valueNumeric="8"/>
+            <column name="enum_message_property" value="Due penalty, fee, 
interest, principal, In advance principal, penalty, fee, interest"/>
+            <column name="enum_value" value="Due penalty, fee, interest, 
principal, In advance principal, penalty, fee, interest"/>
+            <column name="enum_type" valueBoolean="false"/>
+        </insert>
+    </changeSet>
+    <changeSet id="2" author="fineract">
+        <insert tableName="ref_loan_transaction_processing_strategy">
+            <column name="id" valueNumeric="8"/>
+            <column name="code" 
value="due-penalty-fee-interest-principal-in-advance-principal-penalty-fee-interest-strategy"/>
+            <column name="name" value="Due penalty, fee, interest, principal, 
In advance principal, penalty, fee, interest"/>
+            <column name="sort_order" valueNumeric="8"/>
+        </insert>
+    </changeSet>
+</databaseChangeLog>
diff --git 
a/fineract-provider/src/test/java/org/apache/fineract/portfolio/loanaccount/domain/LoanChargeEffectiveDueDateComparatorTest.java
 
b/fineract-provider/src/test/java/org/apache/fineract/portfolio/loanaccount/domain/LoanChargeEffectiveDueDateComparatorTest.java
new file mode 100644
index 000000000..a3172411b
--- /dev/null
+++ 
b/fineract-provider/src/test/java/org/apache/fineract/portfolio/loanaccount/domain/LoanChargeEffectiveDueDateComparatorTest.java
@@ -0,0 +1,149 @@
+/**
+ * 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.portfolio.loanaccount.domain;
+
+import static org.junit.Assert.assertEquals;
+
+import java.time.LocalDate;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+import org.apache.fineract.portfolio.charge.domain.ChargeTimeType;
+import org.junit.Test;
+import org.springframework.test.util.ReflectionTestUtils;
+
+public class LoanChargeEffectiveDueDateComparatorTest {
+
+    @Test
+    public void testLoanChargeEffectiveDueDateComparator() {
+        LoanCharge lc1 = new LoanCharge();
+        ReflectionTestUtils.setField(lc1, "chargeTime", 
ChargeTimeType.SPECIFIED_DUE_DATE.getValue());
+        ReflectionTestUtils.setField(lc1, "dueDate", LocalDate.of(2023, 3, 
17));
+
+        LoanCharge lc2 = new LoanCharge();
+        ReflectionTestUtils.setField(lc2, "chargeTime", 
ChargeTimeType.SPECIFIED_DUE_DATE.getValue());
+        ReflectionTestUtils.setField(lc2, "dueDate", LocalDate.of(2023, 3, 
18));
+
+        LoanCharge lc3 = new LoanCharge();
+        ReflectionTestUtils.setField(lc3, "chargeTime", 
ChargeTimeType.SPECIFIED_DUE_DATE.getValue());
+        ReflectionTestUtils.setField(lc3, "dueDate", LocalDate.of(2023, 3, 
16));
+
+        LoanCharge lc4 = new LoanCharge();
+        LoanInstallmentCharge installmentCharge = new LoanInstallmentCharge();
+        LoanRepaymentScheduleInstallment installment = new 
LoanRepaymentScheduleInstallment();
+        ReflectionTestUtils.setField(installment, "dueDate", 
LocalDate.of(2023, 3, 15));
+        ReflectionTestUtils.setField(installmentCharge, "installment", 
installment);
+        ReflectionTestUtils.setField(lc4, "chargeTime", 
ChargeTimeType.INSTALMENT_FEE.getValue());
+        ReflectionTestUtils.setField(lc4, "loanInstallmentCharge", 
Set.of(installmentCharge));
+
+        List<LoanCharge> list = new ArrayList<>();
+        list.add(lc1);
+        list.add(lc2);
+        list.add(lc3);
+        list.add(lc4);
+        list.sort(LoanChargeEffectiveDueDateComparator.INSTANCE);
+
+        assertEquals(LocalDate.of(2023, 3, 15), 
list.get(0).getEffectiveDueDate());
+        assertEquals(LocalDate.of(2023, 3, 16), 
list.get(1).getEffectiveDueDate());
+        assertEquals(LocalDate.of(2023, 3, 17), 
list.get(2).getEffectiveDueDate());
+        assertEquals(LocalDate.of(2023, 3, 18), 
list.get(3).getEffectiveDueDate());
+    }
+
+    @Test
+    public void testBothNull() {
+        LoanCharge lc1 = new LoanCharge();
+        ReflectionTestUtils.setField(lc1, "chargeTime", 
ChargeTimeType.SPECIFIED_DUE_DATE.getValue());
+        ReflectionTestUtils.setField(lc1, "dueDate", null);
+
+        LoanCharge lc2 = new LoanCharge();
+        ReflectionTestUtils.setField(lc2, "chargeTime", 
ChargeTimeType.SPECIFIED_DUE_DATE.getValue());
+        ReflectionTestUtils.setField(lc2, "dueDate", null);
+
+        List<LoanCharge> list = new ArrayList<>();
+        list.add(lc1);
+        list.add(lc2);
+        list.sort(LoanChargeEffectiveDueDateComparator.INSTANCE);
+
+        assertEquals(lc1, list.get(0));
+        assertEquals(lc2, list.get(1));
+
+        list = new ArrayList<>();
+        list.add(lc2);
+        list.add(lc1);
+        list.sort(LoanChargeEffectiveDueDateComparator.INSTANCE);
+
+        assertEquals(lc2, list.get(0));
+        assertEquals(lc1, list.get(1));
+    }
+
+    @Test
+    public void testOneOfThemIsNull() {
+        LoanCharge lc1 = new LoanCharge();
+        ReflectionTestUtils.setField(lc1, "chargeTime", 
ChargeTimeType.SPECIFIED_DUE_DATE.getValue());
+        ReflectionTestUtils.setField(lc1, "dueDate", null);
+
+        LoanCharge lc2 = new LoanCharge();
+        ReflectionTestUtils.setField(lc2, "chargeTime", 
ChargeTimeType.SPECIFIED_DUE_DATE.getValue());
+        ReflectionTestUtils.setField(lc2, "dueDate", LocalDate.of(2023, 3, 
17));
+
+        LoanCharge lc3 = new LoanCharge();
+        LoanInstallmentCharge installmentCharge1 = new LoanInstallmentCharge();
+        LoanRepaymentScheduleInstallment installment1 = new 
LoanRepaymentScheduleInstallment();
+        ReflectionTestUtils.setField(installment1, "dueDate", 
LocalDate.of(2023, 3, 15));
+        ReflectionTestUtils.setField(installmentCharge1, "installment", 
installment1);
+        ReflectionTestUtils.setField(lc3, "chargeTime", 
ChargeTimeType.INSTALMENT_FEE.getValue());
+        ReflectionTestUtils.setField(lc3, "loanInstallmentCharge", 
Set.of(installmentCharge1));
+
+        LoanCharge lc4 = new LoanCharge();
+        LoanInstallmentCharge installmentCharge2 = new LoanInstallmentCharge();
+        LoanRepaymentScheduleInstallment installment2 = new 
LoanRepaymentScheduleInstallment();
+        ReflectionTestUtils.setField(installment2, "dueDate", null);
+        ReflectionTestUtils.setField(installmentCharge2, "paid", Boolean.TRUE);
+        ReflectionTestUtils.setField(installmentCharge2, "installment", 
installment2);
+        ReflectionTestUtils.setField(lc4, "chargeTime", 
ChargeTimeType.INSTALMENT_FEE.getValue());
+        ReflectionTestUtils.setField(lc4, "loanInstallmentCharge", 
Set.of(installmentCharge2));
+
+        List<LoanCharge> list = new ArrayList<>();
+        list.add(lc1);
+        list.add(lc2);
+        list.add(lc3);
+        list.add(lc4);
+        list.sort(LoanChargeEffectiveDueDateComparator.INSTANCE);
+
+        assertEquals(lc3, list.get(0));
+        assertEquals(lc2, list.get(1));
+        assertEquals(lc1, list.get(2));
+        assertEquals(lc4, list.get(3));
+
+        // Reversed order
+        list = new ArrayList<>();
+        list.add(lc4);
+        list.add(lc3);
+        list.add(lc2);
+        list.add(lc1);
+        list.sort(LoanChargeEffectiveDueDateComparator.INSTANCE);
+
+        assertEquals(lc3, list.get(0));
+        assertEquals(lc2, list.get(1));
+        assertEquals(lc4, list.get(2));
+        assertEquals(lc1, list.get(3));
+
+    }
+
+}
diff --git a/fineract-provider/src/test/resources/application-test.properties 
b/fineract-provider/src/test/resources/application-test.properties
index 459034b08..cc3993557 100644
--- a/fineract-provider/src/test/resources/application-test.properties
+++ b/fineract-provider/src/test/resources/application-test.properties
@@ -60,6 +60,7 @@ fineract.loan.transactionprocessor.heavensfamily.enabled=true
 
fineract.loan.transactionprocessor.interest-principal-penalties-fees.enabled=true
 
fineract.loan.transactionprocessor.principal-interest-penalties-fees.enabled=true
 fineract.loan.transactionprocessor.rbi-india.enabled=true
+fineract.loan.transactionprocessor.due-penalty-fee-interest-principal-in-advance-principal-penalty-fee-interest.enabled=true
 fineract.loan.transactionprocessor.error-not-found-fail=true
 
 fineract.content.regex-whitelist-enabled=true
diff --git 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/DueDateRespectiveLoanRepaymentScheduleTest.java
 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/DueDateRespectiveLoanRepaymentScheduleTest.java
new file mode 100644
index 000000000..1bb1aca81
--- /dev/null
+++ 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/DueDateRespectiveLoanRepaymentScheduleTest.java
@@ -0,0 +1,428 @@
+/**
+ * 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.integrationtests;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import io.restassured.builder.RequestSpecBuilder;
+import io.restassured.builder.ResponseSpecBuilder;
+import io.restassured.http.ContentType;
+import io.restassured.specification.RequestSpecification;
+import io.restassured.specification.ResponseSpecification;
+import java.util.HashMap;
+import org.apache.fineract.client.models.BusinessDateRequest;
+import org.apache.fineract.client.models.GetLoansLoanIdResponse;
+import org.apache.fineract.infrastructure.businessdate.domain.BusinessDateType;
+import org.apache.fineract.integrationtests.common.BusinessDateHelper;
+import org.apache.fineract.integrationtests.common.ClientHelper;
+import org.apache.fineract.integrationtests.common.GlobalConfigurationHelper;
+import org.apache.fineract.integrationtests.common.Utils;
+import org.apache.fineract.integrationtests.common.accounting.Account;
+import org.apache.fineract.integrationtests.common.accounting.AccountHelper;
+import org.apache.fineract.integrationtests.common.charges.ChargesHelper;
+import 
org.apache.fineract.integrationtests.common.loans.LoanApplicationTestBuilder;
+import 
org.apache.fineract.integrationtests.common.loans.LoanProductTestBuilder;
+import org.apache.fineract.integrationtests.common.loans.LoanStatusChecker;
+import org.apache.fineract.integrationtests.common.loans.LoanTransactionHelper;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class DueDateRespectiveLoanRepaymentScheduleTest {
+
+    private static final Logger LOG = 
LoggerFactory.getLogger(DueDateRespectiveLoanRepaymentScheduleTest.class);
+    private ResponseSpecification responseSpec;
+    private RequestSpecification requestSpec;
+    private BusinessDateHelper businessDateHelper;
+    private LoanTransactionHelper loanTransactionHelper;
+
+    private AccountHelper accountHelper;
+
+    @BeforeEach
+    public void setup() {
+        Utils.initializeRESTAssured();
+        this.requestSpec = new 
RequestSpecBuilder().setContentType(ContentType.JSON).build();
+        this.requestSpec.header("Authorization", "Basic " + 
Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey());
+        this.responseSpec = new 
ResponseSpecBuilder().expectStatusCode(200).build();
+        this.requestSpec.header("Fineract-Platform-TenantId", "default");
+        this.loanTransactionHelper = new 
LoanTransactionHelper(this.requestSpec, this.responseSpec);
+        this.loanTransactionHelper = new 
LoanTransactionHelper(this.requestSpec, this.responseSpec);
+        this.businessDateHelper = new BusinessDateHelper();
+        this.accountHelper = new AccountHelper(this.requestSpec, 
this.responseSpec);
+    }
+
+    // Scenario1:
+    // 1. Disburse the loan
+    // 2. Adding a partial repayment
+    // 3. Adding a charge
+    // 3.1 No reverse-replay
+    // 4 Adding a partial repayment
+    // 4.1 Paying only principal portion
+    // 4.2 Adding a partial repayment
+    // 4.3 Paying only charge portion
+    @Test
+    public void scenario1() {
+        try {
+            GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, 
responseSpec, Boolean.TRUE);
+            businessDateHelper.updateBusinessDate(new 
BusinessDateRequest().type(BusinessDateType.BUSINESS_DATE.getName())
+                    .date("2023.02.01").dateFormat("yyyy.MM.dd").locale("en"));
+
+            final Account assetAccount = 
this.accountHelper.createAssetAccount();
+            final Account incomeAccount = 
this.accountHelper.createIncomeAccount();
+            final Account expenseAccount = 
this.accountHelper.createExpenseAccount();
+            final Account overpaymentAccount = 
this.accountHelper.createLiabilityAccount();
+
+            Integer penalty = ChargesHelper.createCharges(requestSpec, 
responseSpec,
+                    
ChargesHelper.getLoanSpecifiedDueDateJSON(ChargesHelper.CHARGE_CALCULATION_TYPE_FLAT,
 "50", true));
+            final Integer loanProductID = 
createLoanProductWithNoAccountingNoInterest("1000", "1", "1", "0",
+                    LoanProductTestBuilder.DUE_DATE_RESPECTIVE_STRATEGY, 
assetAccount, incomeAccount, expenseAccount, overpaymentAccount);
+            final Integer clientID = ClientHelper.createClient(requestSpec, 
responseSpec, "01 January 2023");
+
+            final Integer loanID = applyForLoanApplication(clientID, 
loanProductID, "1000", "1", "1", "1", "0",
+                    LoanApplicationTestBuilder.DUE_DATE_RESPECTIVE_STRATEGY, 
"01 January 2023", "01 January 2023");
+
+            HashMap<String, Object> loanStatusHashMap = 
LoanStatusChecker.getStatusOfLoan(requestSpec, responseSpec, loanID);
+            LoanStatusChecker.verifyLoanIsPending(loanStatusHashMap);
+
+            loanStatusHashMap = loanTransactionHelper.approveLoan("01 January 
2023", loanID);
+            LoanStatusChecker.verifyLoanIsApproved(loanStatusHashMap);
+            
LoanStatusChecker.verifyLoanIsWaitingForDisbursal(loanStatusHashMap);
+
+            loanStatusHashMap = 
loanTransactionHelper.disburseLoanWithTransactionAmount("01 January 2023", 
loanID, "1000");
+            LoanStatusChecker.verifyLoanIsActive(loanStatusHashMap);
+
+            Integer firstRepaymentId = (Integer) 
loanTransactionHelper.makeRepayment("10 January 2023", 
Float.parseFloat("500.00"), loanID)
+                    .get("resourceId");
+            Integer firstChargeId = 
loanTransactionHelper.addChargesForLoan(loanID,
+                    
LoanTransactionHelper.getSpecifiedDueDateChargesForLoanAsJSON(String.valueOf(penalty),
 "20 January 2023", "50"));
+            Integer secondRepaymentId = (Integer) 
loanTransactionHelper.makeRepayment("17 January 2023", 
Float.parseFloat("450.00"), loanID)
+                    .get("resourceId");
+
+            Integer thirdRepaymentId = (Integer) 
loanTransactionHelper.makeRepayment("21 January 2023", 
Float.parseFloat("50.00"), loanID)
+                    .get("resourceId");
+
+            GetLoansLoanIdResponse response = 
loanTransactionHelper.getLoanDetails((long) loanID);
+            assertEquals(50.0, response.getSummary().getTotalOutstanding());
+            assertEquals(50.0, 
response.getRepaymentSchedule().getTotalOutstanding());
+            assertEquals(50.0, 
response.getRepaymentSchedule().getPeriods().get(1).getPenaltyChargesDue());
+            assertEquals(50.0, 
response.getRepaymentSchedule().getPeriods().get(1).getPenaltyChargesPaid());
+            assertEquals(0.0, 
response.getRepaymentSchedule().getPeriods().get(1).getPenaltyChargesOutstanding());
+            assertEquals(1000.0, 
response.getRepaymentSchedule().getPeriods().get(1).getPrincipalDue());
+            assertEquals(950.0, 
response.getRepaymentSchedule().getPeriods().get(1).getPrincipalPaid());
+            assertEquals(50.0, 
response.getRepaymentSchedule().getPeriods().get(1).getPrincipalOutstanding());
+            assertTrue(response.getStatus().getActive());
+
+            assertEquals(firstRepaymentId, 
response.getTransactions().get(1).getId().intValue());
+            assertNull(response.getTransactions().get(1).getReversedOnDate());
+            
assertTrue(response.getTransactions().get(1).getTransactionRelations().isEmpty());
+            
assertTrue(response.getTransactions().get(1).getType().getRepayment());
+            assertEquals(500.0, response.getTransactions().get(1).getAmount());
+            assertEquals(500.0, 
response.getTransactions().get(1).getPrincipalPortion());
+            assertEquals(0.0, 
response.getTransactions().get(1).getPenaltyChargesPortion());
+            assertEquals(0.0, 
response.getTransactions().get(1).getOverpaymentPortion());
+            assertEquals(0.0, 
response.getTransactions().get(1).getInterestPortion());
+            assertEquals(0.0, 
response.getTransactions().get(1).getFeeChargesPortion());
+            assertEquals(500.0, 
response.getTransactions().get(1).getOutstandingLoanBalance());
+            assertEquals(secondRepaymentId, 
response.getTransactions().get(2).getId().intValue());
+            assertNull(response.getTransactions().get(2).getReversedOnDate());
+            
assertTrue(response.getTransactions().get(2).getTransactionRelations().isEmpty());
+            
assertTrue(response.getTransactions().get(2).getType().getRepayment());
+            assertEquals(450.0, response.getTransactions().get(2).getAmount());
+            assertEquals(450.0, 
response.getTransactions().get(2).getPrincipalPortion());
+            assertEquals(0.0, 
response.getTransactions().get(2).getPenaltyChargesPortion());
+            assertEquals(0.0, 
response.getTransactions().get(2).getOverpaymentPortion());
+            assertEquals(0.0, 
response.getTransactions().get(2).getInterestPortion());
+            assertEquals(0.0, 
response.getTransactions().get(2).getFeeChargesPortion());
+            assertEquals(50.0, 
response.getTransactions().get(2).getOutstandingLoanBalance());
+            assertEquals(thirdRepaymentId, 
response.getTransactions().get(3).getId().intValue());
+            assertNull(response.getTransactions().get(3).getReversedOnDate());
+            
assertTrue(response.getTransactions().get(3).getTransactionRelations().isEmpty());
+            
assertTrue(response.getTransactions().get(3).getType().getRepayment());
+            assertEquals(50.0, response.getTransactions().get(3).getAmount());
+            assertEquals(0.0, 
response.getTransactions().get(3).getPrincipalPortion());
+            assertEquals(50.0, 
response.getTransactions().get(3).getPenaltyChargesPortion());
+            assertEquals(0.0, 
response.getTransactions().get(3).getOverpaymentPortion());
+            assertEquals(0.0, 
response.getTransactions().get(3).getInterestPortion());
+            assertEquals(0.0, 
response.getTransactions().get(3).getFeeChargesPortion());
+            assertEquals(50.0, 
response.getTransactions().get(3).getOutstandingLoanBalance());
+            assertEquals(firstChargeId, 
response.getTransactions().get(3).getLoanChargePaidByList().get(0).getChargeId().intValue());
+            assertEquals(1, 
response.getTransactions().get(3).getLoanChargePaidByList().size());
+
+        } finally {
+            GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, 
responseSpec, Boolean.FALSE);
+        }
+    }
+
+    // Scenario2:
+    // 1. Disburse the loan
+    // 2. Adding a partial repayment
+    // 3. Adding a charge
+    // 3.1 No reverse-replay
+    // 4. Adding a partial repayment
+    // 4.1 Paying only principal portion
+    // 5. Adding a charge
+    // 5.1 No any reverse-replay
+    // 6. Adding a partial repayment
+    // 6.1 Paying the 1st charge
+    // 6.2 Paying secondly the in advance principal
+    // 6.3 Not paying the 2nd charge (id: #5)
+    @Test
+    public void scenario2() {
+        try {
+            GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, 
responseSpec, Boolean.TRUE);
+            businessDateHelper.updateBusinessDate(new 
BusinessDateRequest().type(BusinessDateType.BUSINESS_DATE.getName())
+                    .date("2023.02.01").dateFormat("yyyy.MM.dd").locale("en"));
+
+            final Account assetAccount = 
this.accountHelper.createAssetAccount();
+            final Account incomeAccount = 
this.accountHelper.createIncomeAccount();
+            final Account expenseAccount = 
this.accountHelper.createExpenseAccount();
+            final Account overpaymentAccount = 
this.accountHelper.createLiabilityAccount();
+
+            Integer penalty = ChargesHelper.createCharges(requestSpec, 
responseSpec,
+                    
ChargesHelper.getLoanSpecifiedDueDateJSON(ChargesHelper.CHARGE_CALCULATION_TYPE_FLAT,
 "50", true));
+
+            Integer fee = ChargesHelper.createCharges(requestSpec, 
responseSpec,
+                    
ChargesHelper.getLoanSpecifiedDueDateJSON(ChargesHelper.CHARGE_CALCULATION_TYPE_FLAT,
 "50", false));
+            final Integer loanProductID = 
createLoanProductWithNoAccountingNoInterest("1000", "1", "1", "0",
+                    LoanProductTestBuilder.DUE_DATE_RESPECTIVE_STRATEGY, 
assetAccount, incomeAccount, expenseAccount, overpaymentAccount);
+            final Integer clientID = ClientHelper.createClient(requestSpec, 
responseSpec, "01 January 2023");
+
+            final Integer loanID = applyForLoanApplication(clientID, 
loanProductID, "1000", "1", "1", "1", "0",
+                    LoanApplicationTestBuilder.DUE_DATE_RESPECTIVE_STRATEGY, 
"01 January 2023", "01 January 2023");
+
+            HashMap<String, Object> loanStatusHashMap = 
LoanStatusChecker.getStatusOfLoan(requestSpec, responseSpec, loanID);
+            LoanStatusChecker.verifyLoanIsPending(loanStatusHashMap);
+
+            loanStatusHashMap = loanTransactionHelper.approveLoan("01 January 
2023", loanID);
+            LoanStatusChecker.verifyLoanIsApproved(loanStatusHashMap);
+            
LoanStatusChecker.verifyLoanIsWaitingForDisbursal(loanStatusHashMap);
+
+            loanStatusHashMap = 
loanTransactionHelper.disburseLoanWithTransactionAmount("01 January 2023", 
loanID, "1000");
+            LoanStatusChecker.verifyLoanIsActive(loanStatusHashMap);
+
+            Integer firstRepaymentId = (Integer) 
loanTransactionHelper.makeRepayment("10 January 2023", 
Float.parseFloat("500.00"), loanID)
+                    .get("resourceId");
+            Integer firstChargeId = 
loanTransactionHelper.addChargesForLoan(loanID,
+                    
LoanTransactionHelper.getSpecifiedDueDateChargesForLoanAsJSON(String.valueOf(fee),
 "20 January 2023", "50"));
+            Integer secondRepaymentId = (Integer) 
loanTransactionHelper.makeRepayment("17 January 2023", 
Float.parseFloat("100.00"), loanID)
+                    .get("resourceId");
+
+            Integer secondChargeId = 
loanTransactionHelper.addChargesForLoan(loanID,
+                    
LoanTransactionHelper.getSpecifiedDueDateChargesForLoanAsJSON(String.valueOf(penalty),
 "23 January 2023", "10"));
+
+            Integer thirdRepaymentId = (Integer) 
loanTransactionHelper.makeRepayment("20 January 2023", 
Float.parseFloat("100.00"), loanID)
+                    .get("resourceId");
+
+            GetLoansLoanIdResponse response = 
loanTransactionHelper.getLoanDetails((long) loanID);
+            assertEquals(360.0, response.getSummary().getTotalOutstanding());
+            assertEquals(360.0, 
response.getRepaymentSchedule().getTotalOutstanding());
+            assertEquals(10.0, 
response.getRepaymentSchedule().getPeriods().get(1).getPenaltyChargesDue());
+            assertEquals(0.0, 
response.getRepaymentSchedule().getPeriods().get(1).getPenaltyChargesPaid());
+            assertEquals(10.0, 
response.getRepaymentSchedule().getPeriods().get(1).getPenaltyChargesOutstanding());
+            assertEquals(50.0, 
response.getRepaymentSchedule().getPeriods().get(1).getFeeChargesDue());
+            assertEquals(50.0, 
response.getRepaymentSchedule().getPeriods().get(1).getFeeChargesPaid());
+            assertEquals(0.0, 
response.getRepaymentSchedule().getPeriods().get(1).getFeeChargesOutstanding());
+            assertEquals(1000.0, 
response.getRepaymentSchedule().getPeriods().get(1).getPrincipalDue());
+            assertEquals(650.0, 
response.getRepaymentSchedule().getPeriods().get(1).getPrincipalPaid());
+            assertEquals(350.0, 
response.getRepaymentSchedule().getPeriods().get(1).getPrincipalOutstanding());
+            assertTrue(response.getStatus().getActive());
+
+            assertEquals(firstRepaymentId, 
response.getTransactions().get(1).getId().intValue());
+            assertNull(response.getTransactions().get(1).getReversedOnDate());
+            
assertTrue(response.getTransactions().get(1).getTransactionRelations().isEmpty());
+            
assertTrue(response.getTransactions().get(1).getType().getRepayment());
+            assertEquals(500.0, response.getTransactions().get(1).getAmount());
+            assertEquals(500.0, 
response.getTransactions().get(1).getPrincipalPortion());
+            assertEquals(0.0, 
response.getTransactions().get(1).getPenaltyChargesPortion());
+            assertEquals(0.0, 
response.getTransactions().get(1).getOverpaymentPortion());
+            assertEquals(0.0, 
response.getTransactions().get(1).getInterestPortion());
+            assertEquals(0.0, 
response.getTransactions().get(1).getFeeChargesPortion());
+            assertEquals(500.0, 
response.getTransactions().get(1).getOutstandingLoanBalance());
+            assertEquals(secondRepaymentId, 
response.getTransactions().get(2).getId().intValue());
+            assertNull(response.getTransactions().get(2).getReversedOnDate());
+            
assertTrue(response.getTransactions().get(2).getTransactionRelations().isEmpty());
+            
assertTrue(response.getTransactions().get(2).getType().getRepayment());
+            assertEquals(100.0, response.getTransactions().get(2).getAmount());
+            assertEquals(100.0, 
response.getTransactions().get(2).getPrincipalPortion());
+            assertEquals(0.0, 
response.getTransactions().get(2).getPenaltyChargesPortion());
+            assertEquals(0.0, 
response.getTransactions().get(2).getOverpaymentPortion());
+            assertEquals(0.0, 
response.getTransactions().get(2).getInterestPortion());
+            assertEquals(0.0, 
response.getTransactions().get(2).getFeeChargesPortion());
+            assertEquals(400.0, 
response.getTransactions().get(2).getOutstandingLoanBalance());
+            assertEquals(thirdRepaymentId, 
response.getTransactions().get(3).getId().intValue());
+            assertNull(response.getTransactions().get(3).getReversedOnDate());
+            
assertTrue(response.getTransactions().get(3).getTransactionRelations().isEmpty());
+            
assertTrue(response.getTransactions().get(3).getType().getRepayment());
+            assertEquals(100.0, response.getTransactions().get(3).getAmount());
+            assertEquals(50.0, 
response.getTransactions().get(3).getPrincipalPortion());
+            assertEquals(50.0, 
response.getTransactions().get(3).getFeeChargesPortion());
+            assertEquals(0.0, 
response.getTransactions().get(3).getOverpaymentPortion());
+            assertEquals(0.0, 
response.getTransactions().get(3).getInterestPortion());
+            assertEquals(0.0, 
response.getTransactions().get(3).getPenaltyChargesPortion());
+            assertEquals(350.0, 
response.getTransactions().get(3).getOutstandingLoanBalance());
+            assertEquals(firstChargeId, 
response.getTransactions().get(3).getLoanChargePaidByList().get(0).getChargeId().intValue());
+            assertEquals(1, 
response.getTransactions().get(3).getLoanChargePaidByList().size());
+        } finally {
+            GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, 
responseSpec, Boolean.FALSE);
+        }
+    }
+
+    // Scenario3:
+    // 1. Disburse the loan
+    // 2. Adding a partial repayment
+    // 3. Adding a charge
+    // 3.1 No reverse-replay
+    // 4. Adding a full repayment
+    // 4.1 Paying first the in advanced principal portion, and after the in 
advanced charges
+    @Test
+    public void scenario3() {
+        try {
+            GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, 
responseSpec, Boolean.TRUE);
+            businessDateHelper.updateBusinessDate(new 
BusinessDateRequest().type(BusinessDateType.BUSINESS_DATE.getName())
+                    .date("2023.02.01").dateFormat("yyyy.MM.dd").locale("en"));
+
+            final Account assetAccount = 
this.accountHelper.createAssetAccount();
+            final Account incomeAccount = 
this.accountHelper.createIncomeAccount();
+            final Account expenseAccount = 
this.accountHelper.createExpenseAccount();
+            final Account overpaymentAccount = 
this.accountHelper.createLiabilityAccount();
+
+            Integer fee = ChargesHelper.createCharges(requestSpec, 
responseSpec,
+                    
ChargesHelper.getLoanSpecifiedDueDateJSON(ChargesHelper.CHARGE_CALCULATION_TYPE_FLAT,
 "50", false));
+            final Integer loanProductID = 
createLoanProductWithNoAccountingNoInterest("1000", "1", "1", "0",
+                    LoanProductTestBuilder.DUE_DATE_RESPECTIVE_STRATEGY, 
assetAccount, incomeAccount, expenseAccount, overpaymentAccount);
+            final Integer clientID = ClientHelper.createClient(requestSpec, 
responseSpec, "01 January 2023");
+
+            final Integer loanID = applyForLoanApplication(clientID, 
loanProductID, "1000", "1", "1", "1", "0",
+                    LoanApplicationTestBuilder.DUE_DATE_RESPECTIVE_STRATEGY, 
"01 January 2023", "01 January 2023");
+
+            HashMap<String, Object> loanStatusHashMap = 
LoanStatusChecker.getStatusOfLoan(requestSpec, responseSpec, loanID);
+            LoanStatusChecker.verifyLoanIsPending(loanStatusHashMap);
+
+            loanStatusHashMap = loanTransactionHelper.approveLoan("01 January 
2023", loanID);
+            LoanStatusChecker.verifyLoanIsApproved(loanStatusHashMap);
+            
LoanStatusChecker.verifyLoanIsWaitingForDisbursal(loanStatusHashMap);
+
+            loanStatusHashMap = 
loanTransactionHelper.disburseLoanWithTransactionAmount("01 January 2023", 
loanID, "1000");
+            LoanStatusChecker.verifyLoanIsActive(loanStatusHashMap);
+
+            Integer firstRepaymentId = (Integer) 
loanTransactionHelper.makeRepayment("10 January 2023", 
Float.parseFloat("500.00"), loanID)
+                    .get("resourceId");
+            Integer firstChargeId = 
loanTransactionHelper.addChargesForLoan(loanID,
+                    
LoanTransactionHelper.getSpecifiedDueDateChargesForLoanAsJSON(String.valueOf(fee),
 "20 January 2023", "50"));
+            Integer secondRepaymentId = (Integer) 
loanTransactionHelper.makeRepayment("17 January 2023", 
Float.parseFloat("550.00"), loanID)
+                    .get("resourceId");
+
+            GetLoansLoanIdResponse response = 
loanTransactionHelper.getLoanDetails((long) loanID);
+            assertEquals(0.0, response.getSummary().getTotalOutstanding());
+            assertEquals(0.0, 
response.getRepaymentSchedule().getTotalOutstanding());
+            assertEquals(0.0, 
response.getRepaymentSchedule().getPeriods().get(1).getPenaltyChargesDue());
+            assertEquals(0.0, 
response.getRepaymentSchedule().getPeriods().get(1).getPenaltyChargesPaid());
+            assertEquals(0.0, 
response.getRepaymentSchedule().getPeriods().get(1).getPenaltyChargesOutstanding());
+            assertEquals(50.0, 
response.getRepaymentSchedule().getPeriods().get(1).getFeeChargesDue());
+            assertEquals(50.0, 
response.getRepaymentSchedule().getPeriods().get(1).getFeeChargesPaid());
+            assertEquals(0.0, 
response.getRepaymentSchedule().getPeriods().get(1).getFeeChargesOutstanding());
+            assertEquals(1000.0, 
response.getRepaymentSchedule().getPeriods().get(1).getPrincipalDue());
+            assertEquals(1000.0, 
response.getRepaymentSchedule().getPeriods().get(1).getPrincipalPaid());
+            assertEquals(0.0, 
response.getRepaymentSchedule().getPeriods().get(1).getPrincipalOutstanding());
+            assertTrue(response.getStatus().getClosedObligationsMet());
+
+            assertEquals(firstRepaymentId, 
response.getTransactions().get(1).getId().intValue());
+            assertNull(response.getTransactions().get(1).getReversedOnDate());
+            
assertTrue(response.getTransactions().get(1).getTransactionRelations().isEmpty());
+            
assertTrue(response.getTransactions().get(1).getType().getRepayment());
+            assertEquals(500.0, response.getTransactions().get(1).getAmount());
+            assertEquals(500.0, 
response.getTransactions().get(1).getPrincipalPortion());
+            assertEquals(0.0, 
response.getTransactions().get(1).getPenaltyChargesPortion());
+            assertEquals(0.0, 
response.getTransactions().get(1).getOverpaymentPortion());
+            assertEquals(0.0, 
response.getTransactions().get(1).getInterestPortion());
+            assertEquals(0.0, 
response.getTransactions().get(1).getFeeChargesPortion());
+            assertEquals(500.0, 
response.getTransactions().get(1).getOutstandingLoanBalance());
+
+            int repaymentOrderNo;
+            int accrualOrderNo;
+
+            if (response.getTransactions().get(2).getType().getAccrual()) {
+                accrualOrderNo = 2;
+                repaymentOrderNo = 3;
+            } else {
+                accrualOrderNo = 3;
+                repaymentOrderNo = 2;
+            }
+
+            
assertNull(response.getTransactions().get(accrualOrderNo).getReversedOnDate());
+            
assertTrue(response.getTransactions().get(accrualOrderNo).getTransactionRelations().isEmpty());
+            
assertTrue(response.getTransactions().get(accrualOrderNo).getType().getAccrual());
+            assertEquals(50.0, 
response.getTransactions().get(accrualOrderNo).getAmount());
+            assertEquals(0.0, 
response.getTransactions().get(accrualOrderNo).getPrincipalPortion());
+            assertEquals(0.0, 
response.getTransactions().get(accrualOrderNo).getPenaltyChargesPortion());
+            assertEquals(0.0, 
response.getTransactions().get(accrualOrderNo).getOverpaymentPortion());
+            assertEquals(0.0, 
response.getTransactions().get(accrualOrderNo).getInterestPortion());
+            assertEquals(50.0, 
response.getTransactions().get(accrualOrderNo).getFeeChargesPortion());
+            assertEquals(0.0, 
response.getTransactions().get(accrualOrderNo).getOutstandingLoanBalance());
+            assertEquals(firstChargeId,
+                    
response.getTransactions().get(accrualOrderNo).getLoanChargePaidByList().get(0).getChargeId().intValue());
+            assertEquals(1, 
response.getTransactions().get(accrualOrderNo).getLoanChargePaidByList().size());
+
+            assertEquals(secondRepaymentId, 
response.getTransactions().get(repaymentOrderNo).getId().intValue());
+            
assertNull(response.getTransactions().get(repaymentOrderNo).getReversedOnDate());
+            
assertTrue(response.getTransactions().get(repaymentOrderNo).getTransactionRelations().isEmpty());
+            
assertTrue(response.getTransactions().get(repaymentOrderNo).getType().getRepayment());
+            assertEquals(550.0, 
response.getTransactions().get(repaymentOrderNo).getAmount());
+            assertEquals(500.0, 
response.getTransactions().get(repaymentOrderNo).getPrincipalPortion());
+            assertEquals(0.0, 
response.getTransactions().get(repaymentOrderNo).getPenaltyChargesPortion());
+            assertEquals(0.0, 
response.getTransactions().get(repaymentOrderNo).getOverpaymentPortion());
+            assertEquals(0.0, 
response.getTransactions().get(repaymentOrderNo).getInterestPortion());
+            assertEquals(50.0, 
response.getTransactions().get(repaymentOrderNo).getFeeChargesPortion());
+            assertEquals(0.0, 
response.getTransactions().get(repaymentOrderNo).getOutstandingLoanBalance());
+            assertEquals(firstChargeId,
+                    
response.getTransactions().get(repaymentOrderNo).getLoanChargePaidByList().get(0).getChargeId().intValue());
+            assertEquals(1, 
response.getTransactions().get(repaymentOrderNo).getLoanChargePaidByList().size());
+        } finally {
+            GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, 
responseSpec, Boolean.FALSE);
+        }
+    }
+
+    private Integer applyForLoanApplication(final Integer clientID, final 
Integer loanProductID, final String principal,
+            final String loanTermFrequency, final String repaymentAfterEvery, 
final String numberOfRepayments, final String interestRate,
+            final String repaymentStrategy, final String 
expectedDisbursementDate, final String submittedOnDate) {
+        LOG.info("--------------------------------APPLYING FOR LOAN 
APPLICATION--------------------------------");
+        final String loanApplicationJSON = new 
LoanApplicationTestBuilder().withPrincipal(principal)
+                
.withLoanTermFrequency(loanTermFrequency).withLoanTermFrequencyAsMonths().withNumberOfRepayments(numberOfRepayments)
+                
.withRepaymentEveryAfter(repaymentAfterEvery).withRepaymentFrequencyTypeAsMonths().withInterestRatePerPeriod(interestRate)
+                
.withInterestTypeAsFlatBalance().withAmortizationTypeAsEqualPrincipalPayments().withRepaymentStrategy(repaymentStrategy)
+                
.withInterestCalculationPeriodTypeSameAsRepaymentPeriod().withExpectedDisbursementDate(expectedDisbursementDate)
+                
.withSubmittedOnDate(submittedOnDate).withLoanType("individual").build(clientID.toString(),
 loanProductID.toString(), null);
+        return loanTransactionHelper.getLoanId(loanApplicationJSON);
+    }
+
+    private Integer createLoanProductWithNoAccountingNoInterest(final String 
principal, final String repaymentAfterEvery,
+            final String numberOfRepayments, final String interestRate, final 
String repaymentStrategy, final Account... accounts) {
+        LOG.info("------------------------------CREATING NEW LOAN PRODUCT 
---------------------------------------");
+        final String loanProductJSON = new 
LoanProductTestBuilder().withPrincipal(principal).withRepaymentTypeAsMonth()
+                
.withRepaymentAfterEvery(repaymentAfterEvery).withNumberOfRepayments(numberOfRepayments).withRepaymentTypeAsMonth()
+                
.withinterestRatePerPeriod(interestRate).withInterestRateFrequencyTypeAsMonths().withRepaymentStrategy(repaymentStrategy)
+                
.withAmortizationTypeAsEqualPrincipalPayment().withInterestTypeAsFlat().withAccountingRulePeriodicAccrual(accounts)
+                
.withDaysInMonth("30").withDaysInYear("365").withMoratorium("0", 
"0").build(null);
+        return loanTransactionHelper.getLoanProductId(loanProductJSON);
+    }
+
+}
diff --git 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanApplicationTestBuilder.java
 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanApplicationTestBuilder.java
index aaedefa19..3fd737bda 100644
--- 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanApplicationTestBuilder.java
+++ 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanApplicationTestBuilder.java
@@ -42,6 +42,7 @@ public class LoanApplicationTestBuilder {
     public static final String DEFAULT_STRATEGY = "mifos-standard-strategy";
     public static final String RBI_INDIA_STRATEGY = "rbi-india-strategy";
     public static final String 
INTEREST_PRINCIPAL_PENALTIES_FEES_ORDER_STRATEGY = 
"interest-principal-penalties-fees-order-strategy";
+    public static final String DUE_DATE_RESPECTIVE_STRATEGY = 
"due-penalty-fee-interest-principal-in-advance-principal-penalty-fee-interest-strategy";
 
     private String externalId = null;
     private String principal = "10,000";
diff --git 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanProductTestBuilder.java
 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanProductTestBuilder.java
index d5f9fb637..1c4a38e04 100644
--- 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanProductTestBuilder.java
+++ 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanProductTestBuilder.java
@@ -42,6 +42,7 @@ public class LoanProductTestBuilder {
     private static final String FLAT_BALANCE = "1";
     public static final String DEFAULT_STRATEGY = "mifos-standard-strategy";
     public static final String 
INTEREST_PRINCIPAL_PENALTIES_FEES_ORDER_STRATEGY = 
"interest-principal-penalties-fees-order-strategy";
+    public static final String DUE_DATE_RESPECTIVE_STRATEGY = 
"due-penalty-fee-interest-principal-in-advance-principal-penalty-fee-interest-strategy";
     // private static final String HEAVENS_FAMILY_STRATEGY 
="heavensfamily-strategy";
     // private static final String CREO_CORE_STRATEGY ="creocore-strategy";
     public static final String RBI_INDIA_STRATEGY = "rbi-india-strategy";

Reply via email to