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

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


The following commit(s) were added to refs/heads/develop by this push:
     new 59c5ad450 FINERACT-1724: Refactor loan repayment transaction processor
59c5ad450 is described below

commit 59c5ad45003254accc62ed4c71941d5d83138f39
Author: Adam Saghy <[email protected]>
AuthorDate: Wed Aug 30 11:55:36 2023 +0200

    FINERACT-1724: Refactor loan repayment transaction processor
---
 .../portfolio/loanaccount/domain/Loan.java         |  61 +--
 .../LoanRepaymentScheduleTransactionProcessor.java |  29 +-
 ...tLoanRepaymentScheduleTransactionProcessor.java | 408 +++++++--------------
 ...dvancedPaymentScheduleTransactionProcessor.java |  21 ++
 ...eLoanRepaymentScheduleTransactionProcessor.java |  18 -
 ...tLoanRepaymentScheduleTransactionProcessor.java |   5 -
 ...eLoanRepaymentScheduleTransactionProcessor.java |   5 -
 ...eLoanRepaymentScheduleTransactionProcessor.java |  22 +-
 ...yLoanRepaymentScheduleTransactionProcessor.java |   3 -
 ...ILoanRepaymentScheduleTransactionProcessor.java |  19 -
 .../domain/AbstractLoanScheduleGenerator.java      |   4 +-
 .../LoanChargeWritePlatformServiceImpl.java        |   4 +-
 12 files changed, 213 insertions(+), 386 deletions(-)

diff --git 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java
 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java
index 72eb5fe8d..d6756550e 100644
--- 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java
+++ 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java
@@ -702,7 +702,7 @@ public class Loan extends 
AbstractAuditableWithUTCDateTimeCustom {
         final LoanRepaymentScheduleTransactionProcessor 
loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory
                 .determineProcessor(this.transactionProcessingStrategyCode);
         final List<LoanTransaction> allNonContraTransactionsPostDisbursement = 
retrieveListOfTransactionsPostDisbursement();
-        changedTransactionDetail = 
loanRepaymentScheduleTransactionProcessor.handleTransaction(getDisbursementDate(),
+        changedTransactionDetail = 
loanRepaymentScheduleTransactionProcessor.reprocessLoanTranactions(getDisbursementDate(),
                 allNonContraTransactionsPostDisbursement, getCurrency(), 
getRepaymentScheduleInstallments(), getActiveCharges());
         for (final Map.Entry<Long, LoanTransaction> mapEntry : 
changedTransactionDetail.getNewTransactionMappings().entrySet()) {
 
@@ -789,7 +789,8 @@ public class Loan extends 
AbstractAuditableWithUTCDateTimeCustom {
         }
         final Set<LoanCharge> loanCharges = new HashSet<>(1);
         loanCharges.add(charge);
-        
loanRepaymentScheduleTransactionProcessor.handleTransaction(chargesPayment, 
getCurrency(), chargePaymentInstallments, loanCharges);
+        
loanRepaymentScheduleTransactionProcessor.processLatestTransaction(chargesPayment,
 getCurrency(), chargePaymentInstallments,
+                loanCharges, getTotalOverpaidAsMoney());
 
         updateLoanSummaryDerivedFields();
         doPostLoanTransactionChecks(chargesPayment.getTransactionDate(), 
loanLifecycleStateMachine);
@@ -862,8 +863,8 @@ public class Loan extends 
AbstractAuditableWithUTCDateTimeCustom {
              * affected Transactions
              ***/
             final List<LoanTransaction> 
allNonContraTransactionsPostDisbursement = 
retrieveListOfTransactionsPostDisbursement();
-            
loanRepaymentScheduleTransactionProcessor.handleTransaction(getDisbursementDate(),
 allNonContraTransactionsPostDisbursement,
-                    getCurrency(), getRepaymentScheduleInstallments(), 
getActiveCharges());
+            
loanRepaymentScheduleTransactionProcessor.reprocessLoanTranactions(getDisbursementDate(),
+                    allNonContraTransactionsPostDisbursement, getCurrency(), 
getRepaymentScheduleInstallments(), getActiveCharges());
         }
         this.charges.remove(loanCharge);
         updateLoanSummaryDerivedFields();
@@ -930,8 +931,8 @@ public class Loan extends 
AbstractAuditableWithUTCDateTimeCustom {
              * affected Transactions
              ***/
             final List<LoanTransaction> 
allNonContraTransactionsPostDisbursement = 
retrieveListOfTransactionsPostDisbursement();
-            
loanRepaymentScheduleTransactionProcessor.handleTransaction(getDisbursementDate(),
 allNonContraTransactionsPostDisbursement,
-                    getCurrency(), getRepaymentScheduleInstallments(), 
getActiveCharges());
+            
loanRepaymentScheduleTransactionProcessor.reprocessLoanTranactions(getDisbursementDate(),
+                    allNonContraTransactionsPostDisbursement, getCurrency(), 
getRepaymentScheduleInstallments(), getActiveCharges());
         } else {
             // reprocess loan schedule based on charge been waived.
             final LoanRepaymentScheduleProcessingWrapper wrapper = new 
LoanRepaymentScheduleProcessingWrapper();
@@ -1123,8 +1124,8 @@ public class Loan extends 
AbstractAuditableWithUTCDateTimeCustom {
              * affected Transactions
              ***/
             final List<LoanTransaction> 
allNonContraTransactionsPostDisbursement = 
retrieveListOfTransactionsPostDisbursement();
-            
loanRepaymentScheduleTransactionProcessor.handleTransaction(getDisbursementDate(),
 allNonContraTransactionsPostDisbursement,
-                    getCurrency(), getRepaymentScheduleInstallments(), 
getActiveCharges());
+            
loanRepaymentScheduleTransactionProcessor.reprocessLoanTranactions(getDisbursementDate(),
+                    allNonContraTransactionsPostDisbursement, getCurrency(), 
getRepaymentScheduleInstallments(), getActiveCharges());
         } else {
             // reprocess loan schedule based on charge been waived.
             final LoanRepaymentScheduleProcessingWrapper wrapper = new 
LoanRepaymentScheduleProcessingWrapper();
@@ -2705,7 +2706,7 @@ public class Loan extends 
AbstractAuditableWithUTCDateTimeCustom {
             if (!allNonContraTransactionsPostDisbursement.isEmpty()) {
                 final LoanRepaymentScheduleTransactionProcessor 
loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory
                         
.determineProcessor(this.transactionProcessingStrategyCode);
-                changedTransactionDetail = 
loanRepaymentScheduleTransactionProcessor.handleTransaction(getDisbursementDate(),
+                changedTransactionDetail = 
loanRepaymentScheduleTransactionProcessor.reprocessLoanTranactions(getDisbursementDate(),
                         allNonContraTransactionsPostDisbursement, 
getCurrency(), getRepaymentScheduleInstallments(), getActiveCharges());
                 for (final Map.Entry<Long, LoanTransaction> mapEntry : 
changedTransactionDetail.getNewTransactionMappings().entrySet()) {
                     mapEntry.getValue().updateLoan(this);
@@ -3330,8 +3331,8 @@ public class Loan extends 
AbstractAuditableWithUTCDateTimeCustom {
 
         if (isTransactionChronologicallyLatest && adjustedTransaction == null
                 && (!reprocess || 
!this.repaymentScheduleDetail().isInterestRecalculationEnabled()) && 
!isForeclosure()) {
-            
loanRepaymentScheduleTransactionProcessor.handleTransaction(loanTransaction, 
getCurrency(), getRepaymentScheduleInstallments(),
-                    getActiveCharges());
+            
loanRepaymentScheduleTransactionProcessor.processLatestTransaction(loanTransaction,
 getCurrency(),
+                    getRepaymentScheduleInstallments(), getActiveCharges(), 
getTotalOverpaidAsMoney());
             reprocess = false;
             if 
(this.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
                 if (currentInstallment == null || 
currentInstallment.isNotFullyPaidOff()) {
@@ -3350,7 +3351,7 @@ public class Loan extends 
AbstractAuditableWithUTCDateTimeCustom {
                 
regenerateRepaymentScheduleWithInterestRecalculation(scheduleGeneratorDTO);
             }
             final List<LoanTransaction> 
allNonContraTransactionsPostDisbursement = 
retrieveListOfTransactionsPostDisbursement();
-            changedTransactionDetail = 
loanRepaymentScheduleTransactionProcessor.handleTransaction(getDisbursementDate(),
+            changedTransactionDetail = 
loanRepaymentScheduleTransactionProcessor.reprocessLoanTranactions(getDisbursementDate(),
                     allNonContraTransactionsPostDisbursement, getCurrency(), 
getRepaymentScheduleInstallments(), getActiveCharges());
             for (final Map.Entry<Long, LoanTransaction> mapEntry : 
changedTransactionDetail.getNewTransactionMappings().entrySet()) {
                 mapEntry.getValue().updateLoan(this);
@@ -3778,7 +3779,7 @@ public class Loan extends 
AbstractAuditableWithUTCDateTimeCustom {
         if (this.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
             
regenerateRepaymentScheduleWithInterestRecalculation(scheduleGeneratorDTO);
         }
-        ChangedTransactionDetail changedTransactionDetail = 
loanRepaymentScheduleTransactionProcessor.handleTransaction(
+        ChangedTransactionDetail changedTransactionDetail = 
loanRepaymentScheduleTransactionProcessor.reprocessLoanTranactions(
                 getDisbursementDate(), 
allNonContraTransactionsPostDisbursement, getCurrency(), 
getRepaymentScheduleInstallments(),
                 getActiveCharges());
         updateLoanSummaryDerivedFields();
@@ -3904,7 +3905,8 @@ public class Loan extends 
AbstractAuditableWithUTCDateTimeCustom {
             }
 
             addLoanTransaction(loanTransaction);
-            
loanRepaymentScheduleTransactionProcessor.handleWriteOff(loanTransaction, 
loanCurrency(), getRepaymentScheduleInstallments());
+            
loanRepaymentScheduleTransactionProcessor.processLatestTransaction(loanTransaction,
 loanCurrency(),
+                    getRepaymentScheduleInstallments(), getActiveCharges(), 
getTotalOverpaidAsMoney());
 
             updateLoanSummaryDerivedFields();
         }
@@ -3926,7 +3928,7 @@ public class Loan extends 
AbstractAuditableWithUTCDateTimeCustom {
                 
regenerateRepaymentScheduleWithInterestRecalculation(scheduleGeneratorDTO);
             }
             final List<LoanTransaction> 
allNonContraTransactionsPostDisbursement = 
retrieveListOfTransactionsPostDisbursement();
-            changedTransactionDetail = 
loanRepaymentScheduleTransactionProcessor.handleTransaction(getDisbursementDate(),
+            changedTransactionDetail = 
loanRepaymentScheduleTransactionProcessor.reprocessLoanTranactions(getDisbursementDate(),
                     allNonContraTransactionsPostDisbursement, getCurrency(), 
getRepaymentScheduleInstallments(), getActiveCharges());
             for (final Map.Entry<Long, LoanTransaction> mapEntry : 
changedTransactionDetail.getNewTransactionMappings().entrySet()) {
                 mapEntry.getValue().updateLoan(this);
@@ -4008,8 +4010,8 @@ public class Loan extends 
AbstractAuditableWithUTCDateTimeCustom {
                 }
 
                 addLoanTransaction(loanTransaction);
-                
loanRepaymentScheduleTransactionProcessor.handleWriteOff(loanTransaction, 
loanCurrency(),
-                        getRepaymentScheduleInstallments());
+                
loanRepaymentScheduleTransactionProcessor.processLatestTransaction(loanTransaction,
 loanCurrency(),
+                        getRepaymentScheduleInstallments(), 
getActiveCharges(), getTotalOverpaidAsMoney());
 
                 updateLoanSummaryDerivedFields();
             } else if (totalOutstanding.isGreaterThanZero()) {
@@ -5400,7 +5402,7 @@ public class Loan extends 
AbstractAuditableWithUTCDateTimeCustom {
         final LoanRepaymentScheduleTransactionProcessor 
loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory
                 .determineProcessor(this.transactionProcessingStrategyCode);
         final List<LoanTransaction> allNonContraTransactionsPostDisbursement = 
retrieveListOfTransactionsPostDisbursement();
-        ChangedTransactionDetail changedTransactionDetail = 
loanRepaymentScheduleTransactionProcessor.handleTransaction(
+        ChangedTransactionDetail changedTransactionDetail = 
loanRepaymentScheduleTransactionProcessor.reprocessLoanTranactions(
                 getDisbursementDate(), 
allNonContraTransactionsPostDisbursement, getCurrency(), 
getRepaymentScheduleInstallments(),
                 getActiveCharges());
         for (final Map.Entry<Long, LoanTransaction> mapEntry : 
changedTransactionDetail.getNewTransactionMappings().entrySet()) {
@@ -5478,6 +5480,10 @@ public class Loan extends 
AbstractAuditableWithUTCDateTimeCustom {
         return this.totalOverpaid;
     }
 
+    public Money getTotalOverpaidAsMoney() {
+        return Money.of(this.repaymentScheduleDetail().getCurrency(), 
this.totalOverpaid);
+    }
+
     public LocalDate getOverpaidOnDate() {
         return this.overpaidOnDate;
     }
@@ -5539,7 +5545,7 @@ public class Loan extends 
AbstractAuditableWithUTCDateTimeCustom {
         final LoanRepaymentScheduleTransactionProcessor 
loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory
                 .determineProcessor(this.transactionProcessingStrategyCode);
         final List<LoanTransaction> allNonContraTransactionsPostDisbursement = 
retrieveListOfTransactionsPostDisbursement();
-        ChangedTransactionDetail changedTransactionDetail = 
loanRepaymentScheduleTransactionProcessor.handleTransaction(
+        ChangedTransactionDetail changedTransactionDetail = 
loanRepaymentScheduleTransactionProcessor.reprocessLoanTranactions(
                 getDisbursementDate(), 
allNonContraTransactionsPostDisbursement, getCurrency(), 
getRepaymentScheduleInstallments(),
                 getActiveCharges());
         for (final Map.Entry<Long, LoanTransaction> mapEntry : 
changedTransactionDetail.getNewTransactionMappings().entrySet()) {
@@ -5782,7 +5788,7 @@ public class Loan extends 
AbstractAuditableWithUTCDateTimeCustom {
             for (LoanTransaction loanTransaction : 
allNonContraTransactionsPostDisbursement) {
                 
copyTransactions.add(LoanTransaction.copyTransactionProperties(loanTransaction));
             }
-            
loanRepaymentScheduleTransactionProcessor.handleTransaction(getDisbursementDate(),
 copyTransactions, getCurrency(),
+            
loanRepaymentScheduleTransactionProcessor.reprocessLoanTranactions(getDisbursementDate(),
 copyTransactions, getCurrency(),
                     getRepaymentScheduleInstallments(), getActiveCharges());
 
             updateLoanSummaryDerivedFields();
@@ -6382,11 +6388,11 @@ public class Loan extends 
AbstractAuditableWithUTCDateTimeCustom {
 
         // If is a refund
         if (adjustedTransaction == null) {
-            
loanRepaymentScheduleTransactionProcessor.handleRefund(loanTransaction, 
getCurrency(), getRepaymentScheduleInstallments(),
-                    getActiveCharges());
+            
loanRepaymentScheduleTransactionProcessor.processLatestTransaction(loanTransaction,
 getCurrency(),
+                    getRepaymentScheduleInstallments(), getActiveCharges(), 
getTotalOverpaidAsMoney());
         } else {
             final List<LoanTransaction> 
allNonContraTransactionsPostDisbursement = 
retrieveListOfTransactionsPostDisbursement();
-            changedTransactionDetail = 
loanRepaymentScheduleTransactionProcessor.handleTransaction(getDisbursementDate(),
+            changedTransactionDetail = 
loanRepaymentScheduleTransactionProcessor.reprocessLoanTranactions(getDisbursementDate(),
                     allNonContraTransactionsPostDisbursement, getCurrency(), 
getRepaymentScheduleInstallments(), getActiveCharges());
             for (final Map.Entry<Long, LoanTransaction> mapEntry : 
changedTransactionDetail.getNewTransactionMappings().entrySet()) {
                 mapEntry.getValue().updateLoan(this);
@@ -6411,13 +6417,10 @@ public class Loan extends 
AbstractAuditableWithUTCDateTimeCustom {
 
         final LoanRepaymentScheduleTransactionProcessor 
loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory
                 .determineProcessor(this.transactionProcessingStrategyCode);
-        final Money overpaidAmount = 
MathUtil.negativeToZero(calculateTotalOverpayment()); // Before Transaction
 
-        if (chargebackTransaction.isNotZero(loanCurrency())) {
-            addLoanTransaction(chargebackTransaction);
-        }
-        
loanRepaymentScheduleTransactionProcessor.handleChargeback(chargebackTransaction,
 getCurrency(), overpaidAmount,
-                getRepaymentScheduleInstallments());
+        addLoanTransaction(chargebackTransaction);
+        
loanRepaymentScheduleTransactionProcessor.processLatestTransaction(chargebackTransaction,
 getCurrency(),
+                getRepaymentScheduleInstallments(), getActiveCharges(), 
getTotalOverpaidAsMoney());
 
         updateLoanSummaryDerivedFields();
         if 
(!doPostLoanTransactionChecks(chargebackTransaction.getTransactionDate(), 
loanLifecycleStateMachine)) {
diff --git 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/LoanRepaymentScheduleTransactionProcessor.java
 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/LoanRepaymentScheduleTransactionProcessor.java
index 52dd30b4b..9131646ea 100644
--- 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/LoanRepaymentScheduleTransactionProcessor.java
+++ 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/LoanRepaymentScheduleTransactionProcessor.java
@@ -36,15 +36,22 @@ public interface LoanRepaymentScheduleTransactionProcessor {
 
     boolean accept(String s);
 
-    void handleTransaction(LoanTransaction loanTransaction, MonetaryCurrency 
currency, List<LoanRepaymentScheduleInstallment> installments,
-            Set<LoanCharge> charges);
+    /**
+     * Provides support for processing the latest transaction (which should be 
latest transaction) against the loan
+     * schedule.
+     */
+    void processLatestTransaction(LoanTransaction loanTransaction, 
MonetaryCurrency currency,
+            List<LoanRepaymentScheduleInstallment> installments, 
Set<LoanCharge> charges, Money overpaidAmount);
 
-    ChangedTransactionDetail handleTransaction(LocalDate disbursementDate, 
List<LoanTransaction> repaymentsOrWaivers,
+    /**
+     * Provides support for passing all {@link LoanTransaction}'s so it will 
completely re-process the entire loan
+     * schedule. This is required in cases where the {@link LoanTransaction} 
being processed is in the past and falls
+     * before existing transactions or and adjustment is made to an existing 
in which case the entire loan schedule
+     * needs to be re-processed.
+     */
+    ChangedTransactionDetail reprocessLoanTranactions(LocalDate 
disbursementDate, List<LoanTransaction> repaymentsOrWaivers,
             MonetaryCurrency currency, List<LoanRepaymentScheduleInstallment> 
repaymentScheduleInstallments, Set<LoanCharge> charges);
 
-    void handleWriteOff(LoanTransaction loanTransaction, MonetaryCurrency 
loanCurrency,
-            List<LoanRepaymentScheduleInstallment> 
repaymentScheduleInstallments);
-
     Money handleRepaymentSchedule(List<LoanTransaction> 
transactionsPostDisbursement, MonetaryCurrency currency,
             List<LoanRepaymentScheduleInstallment> installments, 
Set<LoanCharge> loanCharges);
 
@@ -52,14 +59,4 @@ public interface LoanRepaymentScheduleTransactionProcessor {
      * Used in interest recalculation to introduce new interest only 
installment.
      */
     boolean isInterestFirstRepaymentScheduleTransactionProcessor();
-
-    void handleRefund(LoanTransaction loanTransaction, MonetaryCurrency 
currency, List<LoanRepaymentScheduleInstallment> installments,
-            Set<LoanCharge> charges);
-
-    void handleChargeback(LoanTransaction loanTransaction, MonetaryCurrency 
currency, Money overpaidAmount,
-            List<LoanRepaymentScheduleInstallment> installments);
-
-    void processTransactionsFromDerivedFields(List<LoanTransaction> 
transactionsPostDisbursement, MonetaryCurrency currency,
-            List<LoanRepaymentScheduleInstallment> installments, 
Set<LoanCharge> charges);
-
 }
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 3f448f738..7ac6dff24 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
@@ -63,15 +63,8 @@ public abstract class 
AbstractLoanRepaymentScheduleTransactionProcessor implemen
         return getCode().equalsIgnoreCase(s) || getName().equalsIgnoreCase(s);
     }
 
-    /**
-     * Provides support for passing all {@link LoanTransaction}'s so it will 
completely re-process the entire loan
-     * schedule. This is required in cases where the {@link LoanTransaction} 
being processed is in the past and falls
-     * before existing transactions or and adjustment is made to an existing 
in which case the entire loan schedule
-     * needs to be re-processed.
-     */
-
     @Override
-    public ChangedTransactionDetail handleTransaction(final LocalDate 
disbursementDate,
+    public ChangedTransactionDetail reprocessLoanTranactions(final LocalDate 
disbursementDate,
             final List<LoanTransaction> transactionsPostDisbursement, final 
MonetaryCurrency currency,
             final List<LoanRepaymentScheduleInstallment> installments, final 
Set<LoanCharge> charges) {
 
@@ -155,20 +148,15 @@ public abstract class 
AbstractLoanRepaymentScheduleTransactionProcessor implemen
         for (final LoanTransaction loanTransaction : 
transactionsToBeProcessed) {
             // TODO: analyze and remove this
             if 
(!loanTransaction.getTypeOf().equals(LoanTransactionType.REFUND_FOR_ACTIVE_LOAN))
 {
-                final Comparator<LoanRepaymentScheduleInstallment> byDate = 
new Comparator<LoanRepaymentScheduleInstallment>() {
-
-                    @Override
-                    public int compare(LoanRepaymentScheduleInstallment ord1, 
LoanRepaymentScheduleInstallment ord2) {
-                        return ord1.getDueDate().compareTo(ord2.getDueDate());
-                    }
-                };
-                Collections.sort(installments, byDate);
+                final Comparator<LoanRepaymentScheduleInstallment> byDate = 
Comparator
+                        
.comparing(LoanRepaymentScheduleInstallment::getDueDate);
+                installments.sort(byDate);
             }
 
             if (loanTransaction.isRepaymentLikeType() || 
loanTransaction.isInterestWaiver() || loanTransaction.isRecoveryRepayment()) {
                 // pass through for new transactions
                 if (loanTransaction.getId() == null) {
-                    handleTransaction(loanTransaction, currency, installments, 
charges);
+                    processLatestTransaction(loanTransaction, currency, 
installments, charges, null);
                     loanTransaction.adjustInterestComponent(currency);
                 } else {
                     /**
@@ -179,7 +167,7 @@ public abstract class 
AbstractLoanRepaymentScheduleTransactionProcessor implemen
 
                     // Reset derived component of new loan transaction and
                     // re-process transaction
-                    handleTransaction(newLoanTransaction, currency, 
installments, charges);
+                    processLatestTransaction(newLoanTransaction, currency, 
installments, charges, null);
                     newLoanTransaction.adjustInterestComponent(currency);
                     /**
                      * Check if the transaction amounts have changed. If so, 
reverse the original transaction and update
@@ -212,6 +200,134 @@ public abstract class 
AbstractLoanRepaymentScheduleTransactionProcessor implemen
         return changedTransactionDetail;
     }
 
+    @Override
+    public void processLatestTransaction(final LoanTransaction 
loanTransaction, final MonetaryCurrency currency,
+            final List<LoanRepaymentScheduleInstallment> installments, final 
Set<LoanCharge> charges, Money overpaidAmount) {
+        switch (loanTransaction.getTypeOf()) {
+            case WRITEOFF -> handleWriteOff(loanTransaction, currency, 
installments);
+            case REFUND_FOR_ACTIVE_LOAN -> handleRefund(loanTransaction, 
currency, installments, charges);
+            case CHARGEBACK -> handleChargeback(loanTransaction, currency, 
overpaidAmount, installments);
+            default -> {
+                Money transactionAmountUnprocessed = 
handleTransactionAndCharges(loanTransaction, currency, installments, charges, 
null,
+                        false);
+                if (transactionAmountUnprocessed.isGreaterThanZero()) {
+                    if (loanTransaction.isWaiver()) {
+                        
loanTransaction.updateComponentsAndTotal(transactionAmountUnprocessed.zero(), 
transactionAmountUnprocessed.zero(),
+                                transactionAmountUnprocessed.zero(), 
transactionAmountUnprocessed.zero());
+                    } else {
+                        onLoanOverpayment(loanTransaction, 
transactionAmountUnprocessed);
+                        
loanTransaction.updateOverPayments(transactionAmountUnprocessed);
+                    }
+                }
+            }
+        }
+    }
+
+    @Override
+    public Money handleRepaymentSchedule(final List<LoanTransaction> 
transactionsPostDisbursement, final MonetaryCurrency currency,
+            final List<LoanRepaymentScheduleInstallment> installments, 
Set<LoanCharge> loanCharges) {
+        Money unProcessed = Money.zero(currency);
+        for (final LoanTransaction loanTransaction : 
transactionsPostDisbursement) {
+            if (loanTransaction.isRepaymentLikeType() || 
loanTransaction.isInterestWaiver() || loanTransaction.isRecoveryRepayment()) {
+                loanTransaction.resetDerivedComponents();
+            }
+            if (loanTransaction.isInterestWaiver()) {
+                processTransaction(loanTransaction, currency, installments, 
loanCharges, null);
+            } else {
+                unProcessed = processTransaction(loanTransaction, currency, 
installments, loanCharges, null);
+            }
+        }
+        return unProcessed;
+    }
+
+    @Override
+    public boolean isInterestFirstRepaymentScheduleTransactionProcessor() {
+        return false;
+    }
+
+    // abstract interface
+
+    /**
+     * For early/'in advance' repayments.
+     *
+     * @param transactionMappings
+     *            TODO
+     * @param charges
+     */
+    protected abstract Money 
handleTransactionThatIsPaymentInAdvanceOfInstallment(LoanRepaymentScheduleInstallment
 currentInstallment,
+            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, Set<LoanCharge> charges);
+
+    /**
+     * For late repayments, how should components of installment be paid off
+     *
+     * @param transactionMappings
+     *            TODO
+     * @param charges
+     */
+    protected abstract Money 
handleTransactionThatIsALateRepaymentOfInstallment(LoanRepaymentScheduleInstallment
 currentInstallment,
+            List<LoanRepaymentScheduleInstallment> installments, 
LoanTransaction loanTransaction, Money transactionAmountUnprocessed,
+            List<LoanTransactionToRepaymentScheduleMapping> 
transactionMappings, Set<LoanCharge> charges);
+
+    /**
+     * Invoked when a transaction results in an over-payment of the full loan.
+     *
+     * transaction amount is greater than the total expected principal and 
interest of the loan.
+     */
+    @SuppressWarnings("unused")
+    protected void onLoanOverpayment(final LoanTransaction loanTransaction, 
final Money loanOverPaymentAmount) {
+        // empty implementation by default.
+    }
+
+    /**
+     * Invoked when a there is a refund of an active loan or undo of an active 
loan
+     *
+     * Undoes principal, interest, fees and charges of this transaction based 
on the repayment strategy
+     *
+     * @param transactionMappings
+     *            TODO
+     *
+     */
+    protected abstract Money 
handleRefundTransactionPaymentOfInstallment(LoanRepaymentScheduleInstallment 
currentInstallment,
+            LoanTransaction loanTransaction, Money 
transactionAmountUnprocessed,
+            List<LoanTransactionToRepaymentScheduleMapping> 
transactionMappings);
+
+    /**
+     * This method is responsible for checking if the current transaction is 
'an advance/early payment' based on the
+     * details passed through.
+     *
+     * Default implementation is check transaction date is before installment 
due date.
+     */
+    protected boolean isTransactionInAdvanceOfInstallment(final int 
installmentIndex,
+            final List<LoanRepaymentScheduleInstallment> installments, final 
LocalDate transactionDate) {
+        final LoanRepaymentScheduleInstallment currentInstallment = 
installments.get(installmentIndex);
+        return transactionDate.isBefore(currentInstallment.getDueDate());
+    }
+
+    /**
+     * This method is responsible for checking if the current transaction is 
'an advance/early payment' based on the
+     * details passed through.
+     *
+     * Default implementation simply processes transactions as 'Late' if the 
transaction date is after the installment
+     * due date.
+     */
+    protected boolean isTransactionALateRepaymentOnInstallment(final int 
installmentIndex,
+            final List<LoanRepaymentScheduleInstallment> installments, final 
LocalDate transactionDate) {
+        final LoanRepaymentScheduleInstallment currentInstallment = 
installments.get(installmentIndex);
+        return transactionDate.isAfter(currentInstallment.getDueDate());
+    }
+
     private void recalculateChargeOffTransaction(ChangedTransactionDetail 
changedTransactionDetail, LoanTransaction loanTransaction,
             MonetaryCurrency currency, List<LoanRepaymentScheduleInstallment> 
installments) {
         final LoanTransaction newLoanTransaction = 
LoanTransaction.copyTransactionProperties(loanTransaction);
@@ -422,42 +538,9 @@ public abstract class 
AbstractLoanRepaymentScheduleTransactionProcessor implemen
         }
     }
 
-    /**
-     * Provides support for processing the latest transaction (which should be 
latest transaction) against the loan
-     * schedule.
-     */
-    @Override
-    public void handleTransaction(final LoanTransaction loanTransaction, final 
MonetaryCurrency currency,
-            final List<LoanRepaymentScheduleInstallment> installments, final 
Set<LoanCharge> charges) {
-
-        final Money amountToProcess = null;
-        final boolean isChargeAmount = false;
-        handleTransaction(loanTransaction, currency, installments, charges, 
amountToProcess, isChargeAmount);
-
-    }
-
-    private void handleTransaction(final LoanTransaction loanTransaction, 
final MonetaryCurrency currency,
-            final List<LoanRepaymentScheduleInstallment> installments, final 
Set<LoanCharge> charges, final Money chargeAmountToProcess,
-            final boolean isFeeCharge) {
-
-        Money transactionAmountUnprocessed = 
handleTransactionAndCharges(loanTransaction, currency, installments, charges,
-                chargeAmountToProcess, isFeeCharge);
-
-        if (transactionAmountUnprocessed.isGreaterThanZero()) {
-            if (loanTransaction.isWaiver()) {
-                
loanTransaction.updateComponentsAndTotal(transactionAmountUnprocessed.zero(), 
transactionAmountUnprocessed.zero(),
-                        transactionAmountUnprocessed.zero(), 
transactionAmountUnprocessed.zero());
-            } else {
-                onLoanOverpayment(loanTransaction, 
transactionAmountUnprocessed);
-                
loanTransaction.updateOverPayments(transactionAmountUnprocessed);
-            }
-        }
-    }
-
     private Money handleTransactionAndCharges(final LoanTransaction 
loanTransaction, final MonetaryCurrency currency,
             final List<LoanRepaymentScheduleInstallment> installments, final 
Set<LoanCharge> charges, final Money chargeAmountToProcess,
             final boolean isFeeCharge) {
-        // to.
         if (loanTransaction.isRepaymentLikeType() || 
loanTransaction.isInterestWaiver() || loanTransaction.isRecoveryRepayment()) {
             loanTransaction.resetDerivedComponents();
         }
@@ -505,20 +588,13 @@ public abstract class 
AbstractLoanRepaymentScheduleTransactionProcessor implemen
         for (final LoanRepaymentScheduleInstallment currentInstallment : 
installments) {
             if (transactionAmountUnprocessed.isGreaterThanZero()) {
                 if (currentInstallment.isNotFullyPaidOff()) {
-
-                    // is this transaction early/late/on-time with respect to
-                    // the
-                    // current installment?
                     if (isTransactionInAdvanceOfInstallment(installmentIndex, 
installments, transactionDate)) {
                         transactionAmountUnprocessed = 
handleTransactionThatIsPaymentInAdvanceOfInstallment(currentInstallment,
                                 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, charges);
                     } else {
-                        // standard transaction
                         transactionAmountUnprocessed = 
handleTransactionThatIsOnTimePaymentOfInstallment(currentInstallment,
                                 loanTransaction, transactionAmountUnprocessed, 
transactionMappings, charges);
                     }
@@ -611,8 +687,7 @@ public abstract class 
AbstractLoanRepaymentScheduleTransactionProcessor implemen
         return earliestUnpaidCharge;
     }
 
-    @Override
-    public void handleWriteOff(final LoanTransaction loanTransaction, final 
MonetaryCurrency currency,
+    private void handleWriteOff(final LoanTransaction loanTransaction, final 
MonetaryCurrency currency,
             final List<LoanRepaymentScheduleInstallment> installments) {
 
         final LocalDate transactionDate = loanTransaction.getTransactionDate();
@@ -637,121 +712,16 @@ public abstract class 
AbstractLoanRepaymentScheduleTransactionProcessor implemen
         loanTransaction.updateComponentsAndTotal(principalPortion, 
interestPortion, feeChargesPortion, penaltychargesPortion);
     }
 
-    // abstract interface
-    /**
-     * This method is responsible for checking if the current transaction is 
'an advance/early payment' based on the
-     * details passed through.
-     *
-     * Default implementation simply processes transactions as 'Late' if the 
transaction date is after the installment
-     * due date.
-     */
-    protected boolean isTransactionALateRepaymentOnInstallment(final int 
installmentIndex,
-            final List<LoanRepaymentScheduleInstallment> installments, final 
LocalDate transactionDate) {
-
-        final LoanRepaymentScheduleInstallment currentInstallment = 
installments.get(installmentIndex);
-
-        return transactionDate.isAfter(currentInstallment.getDueDate());
-    }
-
-    /**
-     * For late repayments, how should components of installment be paid off
-     *
-     * @param transactionMappings
-     *            TODO
-     * @param charges
-     */
-    protected abstract Money 
handleTransactionThatIsALateRepaymentOfInstallment(LoanRepaymentScheduleInstallment
 currentInstallment,
-            List<LoanRepaymentScheduleInstallment> installments, 
LoanTransaction loanTransaction, Money transactionAmountUnprocessed,
-            List<LoanTransactionToRepaymentScheduleMapping> 
transactionMappings, Set<LoanCharge> charges);
-
-    /**
-     * This method is responsible for checking if the current transaction is 
'an advance/early payment' based on the
-     * details passed through.
-     *
-     * Default implementation is check transaction date is before installment 
due date.
-     */
-    protected boolean isTransactionInAdvanceOfInstallment(final int 
currentInstallmentIndex,
-            final List<LoanRepaymentScheduleInstallment> installments, final 
LocalDate transactionDate) {
-
-        final LoanRepaymentScheduleInstallment currentInstallment = 
installments.get(currentInstallmentIndex);
-
-        return transactionDate.isBefore(currentInstallment.getDueDate());
-    }
-
-    /**
-     * For early/'in advance' repayments.
-     *
-     * @param transactionMappings
-     *            TODO
-     * @param charges
-     */
-    protected abstract Money 
handleTransactionThatIsPaymentInAdvanceOfInstallment(LoanRepaymentScheduleInstallment
 currentInstallment,
-            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, Set<LoanCharge> charges);
-
-    /**
-     * Invoked when a transaction results in an over-payment of the full loan.
-     *
-     * transaction amount is greater than the total expected principal and 
interest of the loan.
-     */
-    @SuppressWarnings("unused")
-    protected void onLoanOverpayment(final LoanTransaction loanTransaction, 
final Money loanOverPaymentAmount) {
-        // empty implementation by default.
-    }
-
-    @Override
-    public Money handleRepaymentSchedule(final List<LoanTransaction> 
transactionsPostDisbursement, final MonetaryCurrency currency,
-            final List<LoanRepaymentScheduleInstallment> installments, 
Set<LoanCharge> loanCharges) {
-        Money unProcessed = Money.zero(currency);
-        for (final LoanTransaction loanTransaction : 
transactionsPostDisbursement) {
-            Money amountToProcess = null;
-            if (loanTransaction.isRepaymentLikeType() || 
loanTransaction.isInterestWaiver() || loanTransaction.isRecoveryRepayment()) {
-                loanTransaction.resetDerivedComponents();
-            }
-            if (loanTransaction.isInterestWaiver()) {
-                processTransaction(loanTransaction, currency, installments, 
loanCharges, amountToProcess);
-            } else {
-                unProcessed = processTransaction(loanTransaction, currency, 
installments, loanCharges, amountToProcess);
-            }
-        }
-        return unProcessed;
-    }
-
-    @Override
-    public boolean isInterestFirstRepaymentScheduleTransactionProcessor() {
-        return false;
-    }
-
-    @Override
-    public void handleChargeback(LoanTransaction loanTransaction, 
MonetaryCurrency currency, Money overpaidAmount,
+    private void handleChargeback(LoanTransaction loanTransaction, 
MonetaryCurrency currency, Money overpaidAmount,
             List<LoanRepaymentScheduleInstallment> installments) {
         processCreditTransaction(loanTransaction, overpaidAmount, currency, 
installments);
     }
 
-    @Override
-    public void handleRefund(LoanTransaction loanTransaction, MonetaryCurrency 
currency,
+    private void handleRefund(LoanTransaction loanTransaction, 
MonetaryCurrency currency,
             List<LoanRepaymentScheduleInstallment> installments, final 
Set<LoanCharge> charges) {
-        // TODO Auto-generated method stub
         List<LoanTransactionToRepaymentScheduleMapping> transactionMappings = 
new ArrayList<>();
-        final Comparator<LoanRepaymentScheduleInstallment> byDate = new 
Comparator<LoanRepaymentScheduleInstallment>() {
-
-            @Override
-            public int compare(LoanRepaymentScheduleInstallment ord1, 
LoanRepaymentScheduleInstallment ord2) {
-                return ord1.getDueDate().compareTo(ord2.getDueDate());
-            }
-        };
-        Collections.sort(installments, Collections.reverseOrder(byDate));
+        final Comparator<LoanRepaymentScheduleInstallment> byDate = 
Comparator.comparing(LoanRepaymentScheduleInstallment::getDueDate);
+        installments.sort(Collections.reverseOrder(byDate));
         Money transactionAmountUnprocessed = 
loanTransaction.getAmount(currency);
 
         for (final LoanRepaymentScheduleInstallment currentInstallment : 
installments) {
@@ -786,19 +756,6 @@ public abstract class 
AbstractLoanRepaymentScheduleTransactionProcessor implemen
         
loanTransaction.updateLoanTransactionToRepaymentScheduleMappings(transactionMappings);
     }
 
-    /**
-     * Invoked when a there is a refund of an active loan or undo of an active 
loan
-     *
-     * Undoes principal, interest, fees and charges of this transaction based 
on the repayment strategy
-     *
-     * @param transactionMappings
-     *            TODO
-     *
-     */
-    protected abstract Money 
handleRefundTransactionPaymentOfInstallment(LoanRepaymentScheduleInstallment 
currentInstallment,
-            LoanTransaction loanTransaction, Money 
transactionAmountUnprocessed,
-            List<LoanTransactionToRepaymentScheduleMapping> 
transactionMappings);
-
     private void undoChargesPaidAmountBy(final LoanTransaction 
loanTransaction, final Money feeCharges, final Set<LoanCharge> charges,
             final Integer installmentNumber) {
 
@@ -853,91 +810,4 @@ public abstract class 
AbstractLoanRepaymentScheduleTransactionProcessor implemen
 
         return latestPaidCharge;
     }
-
-    @Override
-    public void processTransactionsFromDerivedFields(List<LoanTransaction> 
transactionsPostDisbursement, MonetaryCurrency currency,
-            List<LoanRepaymentScheduleInstallment> installments, final 
Set<LoanCharge> charges) {
-        for (final LoanTransaction loanTransaction : 
transactionsPostDisbursement) {
-            if (!loanTransaction.isAccrualTransaction()) {
-                processTransactionFromDerivedFields(loanTransaction, currency, 
installments, charges);
-            }
-        }
-    }
-
-    private void processTransactionFromDerivedFields(final LoanTransaction 
loanTransaction, MonetaryCurrency currency,
-            List<LoanRepaymentScheduleInstallment> installments, final 
Set<LoanCharge> charges) {
-        Money principal = loanTransaction.getPrincipalPortion(currency);
-        Money interest = loanTransaction.getInterestPortion(currency);
-        if (loanTransaction.isInterestWaiver()) {
-            interest = loanTransaction.getAmount(currency);
-        }
-        Money feeCharges = loanTransaction.getFeeChargesPortion(currency);
-        Money penaltyCharges = 
loanTransaction.getPenaltyChargesPortion(currency);
-        final LocalDate transactionDate = loanTransaction.getTransactionDate();
-        if (principal.isGreaterThanZero() || interest.isGreaterThanZero() || 
feeCharges.isGreaterThanZero()
-                || penaltyCharges.isGreaterThanZero()) {
-            for (final LoanRepaymentScheduleInstallment currentInstallment : 
installments) {
-                if (currentInstallment.isNotFullyPaidOff()) {
-                    if (penaltyCharges.isGreaterThanZero()) {
-                        Money penaltyChargesPortion = Money.zero(currency);
-                        if (loanTransaction.isWaiver()) {
-                            penaltyChargesPortion = 
currentInstallment.waivePenaltyChargesComponent(transactionDate, 
penaltyCharges);
-                        } else {
-                            penaltyChargesPortion = 
currentInstallment.payPenaltyChargesComponent(transactionDate, penaltyCharges);
-                        }
-                        penaltyCharges = 
penaltyCharges.minus(penaltyChargesPortion);
-                    }
-
-                    if (feeCharges.isGreaterThanZero()) {
-                        Money feeChargesPortion = Money.zero(currency);
-                        if (loanTransaction.isWaiver()) {
-                            feeChargesPortion = 
currentInstallment.waiveFeeChargesComponent(transactionDate, feeCharges);
-                        } else {
-                            feeChargesPortion = 
currentInstallment.payFeeChargesComponent(transactionDate, feeCharges);
-                        }
-                        feeCharges = feeCharges.minus(feeChargesPortion);
-                    }
-
-                    if (interest.isGreaterThanZero()) {
-                        Money interestPortion = Money.zero(currency);
-                        if (loanTransaction.isWaiver()) {
-                            interestPortion = 
currentInstallment.waiveInterestComponent(transactionDate, interest);
-                        } else {
-                            interestPortion = 
currentInstallment.payInterestComponent(transactionDate, interest);
-                        }
-                        interest = interest.minus(interestPortion);
-                    }
-
-                    if (principal.isGreaterThanZero()) {
-                        Money principalPortion = 
currentInstallment.payPrincipalComponent(transactionDate, principal);
-                        principal = principal.minus(principalPortion);
-                    }
-                }
-                if (!(principal.isGreaterThanZero() || 
interest.isGreaterThanZero() || feeCharges.isGreaterThanZero()
-                        || penaltyCharges.isGreaterThanZero())) {
-                    break;
-                }
-            }
-        }
-
-        final Set<LoanCharge> loanFees = extractFeeCharges(charges);
-        final Set<LoanCharge> loanPenalties = extractPenaltyCharges(charges);
-        Integer installmentNumber = null;
-        if (loanTransaction.isChargePayment() && installments.size() == 1) {
-            installmentNumber = installments.get(0).getInstallmentNumber();
-        }
-
-        if (loanTransaction.isNotWaiver()) {
-            feeCharges = loanTransaction.getFeeChargesPortion(currency);
-            penaltyCharges = 
loanTransaction.getPenaltyChargesPortion(currency);
-            if (feeCharges.isGreaterThanZero()) {
-                updateChargesPaidAmountBy(loanTransaction, feeCharges, 
loanFees, installmentNumber);
-            }
-
-            if (penaltyCharges.isGreaterThanZero()) {
-                updateChargesPaidAmountBy(loanTransaction, penaltyCharges, 
loanPenalties, installmentNumber);
-            }
-        }
-    }
-
 }
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/AdvancedPaymentScheduleTransactionProcessor.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/AdvancedPaymentScheduleTransactionProcessor.java
index 5fc2bc35c..aa236efde 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/AdvancedPaymentScheduleTransactionProcessor.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/AdvancedPaymentScheduleTransactionProcessor.java
@@ -18,10 +18,13 @@
  */
 package 
org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl;
 
+import java.time.LocalDate;
 import java.util.List;
 import java.util.Set;
 import org.apache.commons.lang3.NotImplementedException;
+import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
 import org.apache.fineract.organisation.monetary.domain.Money;
+import 
org.apache.fineract.portfolio.loanaccount.domain.ChangedTransactionDetail;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanCharge;
 import 
org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
@@ -69,4 +72,22 @@ public class AdvancedPaymentScheduleTransactionProcessor 
extends AbstractLoanRep
             List<LoanTransactionToRepaymentScheduleMapping> 
transactionMappings) {
         throw new NotImplementedException();
     }
+
+    @Override
+    public ChangedTransactionDetail reprocessLoanTranactions(LocalDate 
disbursementDate, List<LoanTransaction> transactionsPostDisbursement,
+            MonetaryCurrency currency, List<LoanRepaymentScheduleInstallment> 
installments, Set<LoanCharge> charges) {
+        throw new NotImplementedException();
+    }
+
+    @Override
+    public void processLatestTransaction(LoanTransaction loanTransaction, 
MonetaryCurrency currency,
+            List<LoanRepaymentScheduleInstallment> installments, 
Set<LoanCharge> charges, Money overpaidAmount) {
+        throw new NotImplementedException();
+    }
+
+    @Override
+    public Money handleRepaymentSchedule(List<LoanTransaction> 
transactionsPostDisbursement, MonetaryCurrency currency,
+            List<LoanRepaymentScheduleInstallment> installments, 
Set<LoanCharge> loanCharges) {
+        return super.handleRepaymentSchedule(transactionsPostDisbursement, 
currency, installments, loanCharges);
+    }
 }
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 88719e6f5..501f93558 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
@@ -56,18 +56,6 @@ public class 
CreocoreLoanRepaymentScheduleTransactionProcessor extends AbstractL
         return STRATEGY_NAME;
     }
 
-    /**
-     * For creocore, early is defined as any date before the installment due 
date
-     */
-    @Override
-    protected boolean isTransactionInAdvanceOfInstallment(final int 
currentInstallmentIndex,
-            final List<LoanRepaymentScheduleInstallment> installments, final 
LocalDate transactionDate) {
-
-        final LoanRepaymentScheduleInstallment currentInstallment = 
installments.get(currentInstallmentIndex);
-
-        return transactionDate.isBefore(currentInstallment.getDueDate());
-    }
-
     /**
      * For early/'in advance' repayments, pay off in the same way as on-time 
payments, interest first then principal.
      */
@@ -153,12 +141,6 @@ public class 
CreocoreLoanRepaymentScheduleTransactionProcessor extends AbstractL
         return transactionAmountRemaining;
     }
 
-    @SuppressWarnings("unused")
-    @Override
-    protected void onLoanOverpayment(final LoanTransaction loanTransaction, 
final Money loanOverPaymentAmount) {
-        // dont do anything for with loan over-payment
-    }
-
     @Override
     protected Money handleRefundTransactionPaymentOfInstallment(final 
LoanRepaymentScheduleInstallment currentInstallment,
             final LoanTransaction loanTransaction, final Money 
transactionAmountUnprocessed,
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/DuePenFeeIntPriInAdvancePriPenFeeIntLoanRepaymentScheduleTransactionProcessor.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/DuePenFeeIntPriInAdvancePriPenFeeIntLoanRepaymentScheduleTransactionProcessor.java
index f9e038652..7916e894d 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/DuePenFeeIntPriInAdvancePriPenFeeIntLoanRepaymentScheduleTransactionProcessor.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/DuePenFeeIntPriInAdvancePriPenFeeIntLoanRepaymentScheduleTransactionProcessor.java
@@ -245,11 +245,6 @@ public class 
DuePenFeeIntPriInAdvancePriPenFeeIntLoanRepaymentScheduleTransactio
         return transactionAmountRemaining;
     }
 
-    @Override
-    protected void onLoanOverpayment(final LoanTransaction loanTransaction, 
final Money loanOverPaymentAmount) {
-        // TODO - KW - dont do anything with loan over-payment for now
-    }
-
     @Override
     protected Money handleRefundTransactionPaymentOfInstallment(final 
LoanRepaymentScheduleInstallment currentInstallment,
             final LoanTransaction loanTransaction, final Money 
transactionAmountUnprocessed,
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/DuePenIntPriFeeInAdvancePenIntPriFeeLoanRepaymentScheduleTransactionProcessor.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/DuePenIntPriFeeInAdvancePenIntPriFeeLoanRepaymentScheduleTransactionProcessor.java
index 6a8bf1e6a..a16e7f15f 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/DuePenIntPriFeeInAdvancePenIntPriFeeLoanRepaymentScheduleTransactionProcessor.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/DuePenIntPriFeeInAdvancePenIntPriFeeLoanRepaymentScheduleTransactionProcessor.java
@@ -247,11 +247,6 @@ public class 
DuePenIntPriFeeInAdvancePenIntPriFeeLoanRepaymentScheduleTransactio
         return transactionAmountRemaining;
     }
 
-    @Override
-    protected void onLoanOverpayment(final LoanTransaction loanTransaction, 
final Money loanOverPaymentAmount) {
-        // TODO - KW - dont do anything with loan over-payment for now
-    }
-
     @Override
     protected Money handleRefundTransactionPaymentOfInstallment(final 
LoanRepaymentScheduleInstallment currentInstallment,
             final LoanTransaction loanTransaction, final Money 
transactionAmountUnprocessed,
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 11d73813b..fb3ab2f02 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
@@ -56,15 +56,6 @@ public class 
FineractStyleLoanRepaymentScheduleTransactionProcessor extends Abst
         return STRATEGY_NAME;
     }
 
-    @Override
-    protected boolean isTransactionInAdvanceOfInstallment(final int 
currentInstallmentIndex,
-            final List<LoanRepaymentScheduleInstallment> installments, final 
LocalDate transactionDate) {
-
-        final LoanRepaymentScheduleInstallment currentInstallment = 
installments.get(currentInstallmentIndex);
-
-        return transactionDate.isBefore(currentInstallment.getDueDate());
-    }
-
     /**
      * For early/'in advance' repayments, pay off in the same way as on-time 
payments, interest first then principal.
      */
@@ -151,11 +142,6 @@ public class 
FineractStyleLoanRepaymentScheduleTransactionProcessor extends Abst
         return transactionAmountRemaining;
     }
 
-    @Override
-    protected void onLoanOverpayment(final LoanTransaction loanTransaction, 
final Money loanOverPaymentAmount) {
-        // TODO - KW - dont do anything with loan over-payment for now
-    }
-
     @Override
     protected Money handleRefundTransactionPaymentOfInstallment(final 
LoanRepaymentScheduleInstallment currentInstallment,
             final LoanTransaction loanTransaction, final Money 
transactionAmountUnprocessed,
@@ -164,10 +150,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/HeavensFamilyLoanRepaymentScheduleTransactionProcessor.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/HeavensFamilyLoanRepaymentScheduleTransactionProcessor.java
index 3e327c5cd..e94cee83a 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
@@ -216,9 +216,6 @@ public class 
HeavensFamilyLoanRepaymentScheduleTransactionProcessor extends Abst
         return transactionAmountRemaining;
     }
 
-    @Override
-    protected void onLoanOverpayment(final LoanTransaction loanTransaction, 
final Money loanOverPaymentAmount) {}
-
     @Override
     protected Money handleRefundTransactionPaymentOfInstallment(final 
LoanRepaymentScheduleInstallment currentInstallment,
             final LoanTransaction loanTransaction, final Money 
transactionAmountUnprocessed,
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 97de4da3b..9987180b9 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
@@ -59,19 +59,6 @@ public class RBILoanRepaymentScheduleTransactionProcessor 
extends AbstractLoanRe
         return STRATEGY_NAME;
     }
 
-    /**
-     * 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 LoanRepaymentScheduleInstallment currentInstallment = 
installments.get(currentInstallmentIndex);
-
-        return transactionDate.isBefore(currentInstallment.getDueDate());
-    }
-
     /**
      * For early/'in advance' repayments, pays off principal component only.
      */
@@ -259,12 +246,6 @@ public class RBILoanRepaymentScheduleTransactionProcessor 
extends AbstractLoanRe
         return transactionAmountRemaining;
     }
 
-    @SuppressWarnings("unused")
-    @Override
-    protected void onLoanOverpayment(final LoanTransaction loanTransaction, 
final Money loanOverPaymentAmount) {
-        // dont do anything for with loan over-payment
-    }
-
     @Override
     public boolean isInterestFirstRepaymentScheduleTransactionProcessor() {
         return true;
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 d76f75e3f..8229dbddd 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
@@ -2808,8 +2808,8 @@ public abstract class AbstractLoanScheduleGenerator 
implements LoanScheduleGener
                 loanRepaymentScheduleTransactionProcessor, onDate, 
calculateTill);
         List<LoanTransaction> loanTransactions = 
loan.retrieveListOfTransactionsPostDisbursementExcludeAccruals();
 
-        
loanRepaymentScheduleTransactionProcessor.handleTransaction(loanApplicationTerms.getExpectedDisbursementDate(),
 loanTransactions,
-                currency, loanScheduleDTO.getInstallments(), 
loan.getActiveCharges());
+        
loanRepaymentScheduleTransactionProcessor.reprocessLoanTranactions(loanApplicationTerms.getExpectedDisbursementDate(),
+                loanTransactions, currency, loanScheduleDTO.getInstallments(), 
loan.getActiveCharges());
         Money feeCharges = Money.zero(currency);
         Money penaltyCharges = Money.zero(currency);
         Money totalPrincipal = Money.zero(currency);
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanChargeWritePlatformServiceImpl.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanChargeWritePlatformServiceImpl.java
index 342ca5583..83b4c0508 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanChargeWritePlatformServiceImpl.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanChargeWritePlatformServiceImpl.java
@@ -809,8 +809,8 @@ public class LoanChargeWritePlatformServiceImpl implements 
LoanChargeWritePlatfo
         
defaultLoanLifecycleStateMachine.transition(LoanEvent.LOAN_REPAYMENT_OR_WAIVER, 
loan);
         final LoanRepaymentScheduleTransactionProcessor 
loanRepaymentScheduleTransactionProcessor = 
loanRepaymentScheduleTransactionProcessorFactory
                 .determineProcessor(loan.transactionProcessingStrategy());
-        
loanRepaymentScheduleTransactionProcessor.handleTransaction(loanChargeAdjustmentTransaction,
 loan.getCurrency(),
-                loan.getRepaymentScheduleInstallments(), 
loan.getActiveCharges());
+        
loanRepaymentScheduleTransactionProcessor.processLatestTransaction(loanChargeAdjustmentTransaction,
 loan.getCurrency(),
+                loan.getRepaymentScheduleInstallments(), 
loan.getActiveCharges(), loan.getTotalOverpaidAsMoney());
 
         loan.addLoanTransaction(loanChargeAdjustmentTransaction);
         loan.updateLoanSummaryAndStatus();

Reply via email to