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 cb6a15c3d2 FINERACT-2234: Replace in-memory transaction loops with 
repository queries
cb6a15c3d2 is described below

commit cb6a15c3d222a7c199450a4aa803ee3f546ac15a
Author: Oleksii Novikov <[email protected]>
AuthorDate: Wed Jun 25 10:08:57 2025 +0300

    FINERACT-2234: Replace in-memory transaction loops with repository queries
---
 .../DelinquencyReadPlatformServiceImpl.java        |  11 +--
 .../starter/DelinquencyConfiguration.java          |   6 +-
 .../portfolio/loanaccount/domain/Loan.java         | 100 +++-----------------
 .../loanaccount/domain/LoanTransaction.java        |   4 -
 .../domain/LoanTransactionRepository.java          | 102 +++++++++++++++++++--
 ...anChargeRepaymentScheduleProcessingWrapper.java |   1 -
 ...tLoanRepaymentScheduleTransactionProcessor.java |  25 +++--
 .../AbstractCumulativeLoanScheduleGenerator.java   |  39 ++++----
 ...liningBalanceInterestLoanScheduleGenerator.java |   8 +-
 ...umulativeFlatInterestLoanScheduleGenerator.java |   8 +-
 .../serialization/LoanChargeValidator.java         |  20 ++--
 .../loanaccount/service/LoanBalanceService.java    |  63 ++++++++++++-
 ...dvancedPaymentScheduleTransactionProcessor.java |  22 ++---
 .../CapitalizedIncomeWritePlatformServiceImpl.java |   2 +-
 .../InternalProgressiveLoanApiResource.java        |   7 +-
 ...cedPaymentScheduleTransactionProcessorTest.java |   4 +-
 .../domain/LoanAccountDomainServiceJpa.java        |   3 +-
 .../service/LoanAccrualsProcessingServiceImpl.java |   4 +-
 .../loanaccount/service/LoanAssemblerImpl.java     |   8 +-
 .../service/LoanDisbursementService.java           |   3 +-
 .../LoanTransactionProcessingServiceImpl.java      |   2 +-
 .../LoanWritePlatformServiceJpaRepositoryImpl.java |  14 +--
 .../ProgressiveLoanSummaryDataProvider.java        |   7 +-
 .../ReprocessLoanTransactionsServiceImpl.java      |  21 +----
 .../LoanContractTerminationServiceImpl.java        |   2 +-
 .../starter/LoanAccountAutoStarter.java            |   6 +-
 .../starter/LoanAccountConfiguration.java          |   4 +-
 ...AccountDelinquencyRangeEventSerializerTest.java |   2 +-
 .../portfolio/loanaccount/domain/LoanBuilder.java  |  11 +--
 29 files changed, 281 insertions(+), 228 deletions(-)

diff --git 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/delinquency/service/DelinquencyReadPlatformServiceImpl.java
 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/delinquency/service/DelinquencyReadPlatformServiceImpl.java
index 29e0b0843d..7406584aff 100644
--- 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/delinquency/service/DelinquencyReadPlatformServiceImpl.java
+++ 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/delinquency/service/DelinquencyReadPlatformServiceImpl.java
@@ -58,6 +58,7 @@ import org.apache.fineract.portfolio.loanaccount.domain.Loan;
 import 
org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanRepository;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
+import 
org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository;
 import org.springframework.lang.NonNull;
 import org.springframework.transaction.annotation.Transactional;
 
@@ -77,6 +78,7 @@ public class DelinquencyReadPlatformServiceImpl implements 
DelinquencyReadPlatfo
     private final LoanDelinquencyActionRepository 
loanDelinquencyActionRepository;
     private final DelinquencyEffectivePauseHelper 
delinquencyEffectivePauseHelper;
     private final ConfigurationDomainService configurationDomainService;
+    private final LoanTransactionRepository loanTransactionRepository;
 
     @Override
     public List<DelinquencyRangeData> retrieveAllDelinquencyRanges() {
@@ -174,7 +176,7 @@ public class DelinquencyReadPlatformServiceImpl implements 
DelinquencyReadPlatfo
     private void addInstallmentLevelDelinquencyData(CollectionData 
collectionData, Long loanId) {
         Collection<LoanInstallmentDelinquencyTagData> 
loanInstallmentDelinquencyTagData = 
retrieveLoanInstallmentsCurrentDelinquencyTag(
                 loanId);
-        if (loanInstallmentDelinquencyTagData != null && 
loanInstallmentDelinquencyTagData.size() > 0) {
+        if (loanInstallmentDelinquencyTagData != null && 
!loanInstallmentDelinquencyTagData.isEmpty()) {
 
             // installment level delinquency grouped by rangeId, and summed up 
the delinquent amount
             Collection<InstallmentLevelDelinquency> 
installmentLevelDelinquencies = loanInstallmentDelinquencyTagData.stream()
@@ -254,12 +256,7 @@ public class DelinquencyReadPlatformServiceImpl implements 
DelinquencyReadPlatfo
             }
         }
 
-        LocalDate lastTransactionDate = null;
-        for (final LoanTransaction transaction : loan.getLoanTransactions()) {
-            if (transaction.isRepaymentLikeType() && 
transaction.isGreaterThanZero()) {
-                lastTransactionDate = transaction.getTransactionDate();
-            }
-        }
+        final LocalDate lastTransactionDate = 
loanTransactionRepository.findLastRepaymentLikeTransactionDate(loan).orElse(null);
 
         LocalDate possibleNextRepaymentDate = earliestUnpaidInstallmentDate;
         if (DateUtils.isAfter(lastTransactionDate, 
earliestUnpaidInstallmentDate)) {
diff --git 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/delinquency/starter/DelinquencyConfiguration.java
 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/delinquency/starter/DelinquencyConfiguration.java
index 71e6382196..530ce477f3 100644
--- 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/delinquency/starter/DelinquencyConfiguration.java
+++ 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/delinquency/starter/DelinquencyConfiguration.java
@@ -42,6 +42,7 @@ import 
org.apache.fineract.portfolio.delinquency.validator.DelinquencyBucketPars
 import 
org.apache.fineract.portfolio.delinquency.validator.DelinquencyRangeParseAndValidator;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanRepository;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanRepositoryWrapper;
+import 
org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository;
 import 
org.apache.fineract.portfolio.loanaccount.service.LoanTransactionReadService;
 import org.apache.fineract.portfolio.loanproduct.domain.LoanProductRepository;
 import 
org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
@@ -60,11 +61,12 @@ public class DelinquencyConfiguration {
             LoanDelinquencyDomainService loanDelinquencyDomainService,
             LoanInstallmentDelinquencyTagRepository 
repositoryLoanInstallmentDelinquencyTag,
             LoanDelinquencyActionRepository loanDelinquencyActionRepository,
-            DelinquencyEffectivePauseHelper delinquencyEffectivePauseHelper, 
ConfigurationDomainService configurationDomainService) {
+            DelinquencyEffectivePauseHelper delinquencyEffectivePauseHelper, 
ConfigurationDomainService configurationDomainService,
+            LoanTransactionRepository loanTransactionRepository) {
         return new DelinquencyReadPlatformServiceImpl(repositoryRange, 
repositoryBucket, repositoryLoanDelinquencyTagHistory, mapperRange,
                 mapperBucket, mapperLoanDelinquencyTagHistory, loanRepository, 
loanDelinquencyDomainService,
                 repositoryLoanInstallmentDelinquencyTag, 
loanDelinquencyActionRepository, delinquencyEffectivePauseHelper,
-                configurationDomainService);
+                configurationDomainService, loanTransactionRepository);
     }
 
     @Bean
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 3c8d29df2c..8951bdd50a 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
@@ -59,7 +59,6 @@ import 
org.apache.fineract.infrastructure.codes.domain.CodeValue;
 import 
org.apache.fineract.infrastructure.core.domain.AbstractAuditableWithUTCDateTimeCustom;
 import org.apache.fineract.infrastructure.core.domain.ExternalId;
 import org.apache.fineract.infrastructure.core.service.DateUtils;
-import org.apache.fineract.infrastructure.core.service.MathUtil;
 import 
org.apache.fineract.infrastructure.security.service.RandomPasswordGenerator;
 import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
 import org.apache.fineract.organisation.monetary.domain.Money;
@@ -74,7 +73,6 @@ import org.apache.fineract.portfolio.fund.domain.Fund;
 import org.apache.fineract.portfolio.group.domain.Group;
 import 
org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.LoanRepaymentScheduleTransactionProcessor;
 import 
org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanApplicationTerms;
-import 
org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleModel;
 import org.apache.fineract.portfolio.loanproduct.domain.LoanProduct;
 import 
org.apache.fineract.portfolio.loanproduct.domain.LoanProductRelatedDetail;
 import 
org.apache.fineract.portfolio.loanproduct.domain.LoanSupportedInterestRefundTypes;
@@ -433,13 +431,13 @@ public class Loan extends 
AbstractAuditableWithUTCDateTimeCustom<Long> {
             final List<LoanDisbursementDetails> disbursementDetails, final 
BigDecimal maxOutstandingLoanBalance,
             final Boolean createStandingInstructionAtDisbursement, final 
Boolean isFloatingInterestRate,
             final BigDecimal interestRateDifferential, final List<Rate> rates, 
final BigDecimal fixedPrincipalPercentagePerInstallment,
-            final ExternalId externalId, final LoanApplicationTerms 
loanApplicationTerms, final LoanScheduleModel loanScheduleModel,
-            final Boolean enableInstallmentLevelDelinquency, final LocalDate 
submittedOnDate) {
+            final ExternalId externalId, final LoanApplicationTerms 
loanApplicationTerms, final Boolean enableInstallmentLevelDelinquency,
+            final LocalDate submittedOnDate) {
         return new Loan(accountNo, client, null, loanType, fund, officer, 
loanPurpose, transactionProcessingStrategy, loanProduct,
                 loanRepaymentScheduleDetail, null, loanCharges, collateral, 
null, fixedEmiAmount, disbursementDetails,
                 maxOutstandingLoanBalance, 
createStandingInstructionAtDisbursement, isFloatingInterestRate, 
interestRateDifferential, rates,
-                fixedPrincipalPercentagePerInstallment, externalId, 
loanApplicationTerms, loanScheduleModel,
-                enableInstallmentLevelDelinquency, submittedOnDate);
+                fixedPrincipalPercentagePerInstallment, externalId, 
loanApplicationTerms, enableInstallmentLevelDelinquency,
+                submittedOnDate);
     }
 
     public static Loan newGroupLoanApplication(final String accountNo, final 
Group group, final AccountType loanType,
@@ -450,13 +448,13 @@ public class Loan extends 
AbstractAuditableWithUTCDateTimeCustom<Long> {
             final List<LoanDisbursementDetails> disbursementDetails, final 
BigDecimal maxOutstandingLoanBalance,
             final Boolean createStandingInstructionAtDisbursement, final 
Boolean isFloatingInterestRate,
             final BigDecimal interestRateDifferential, final List<Rate> rates, 
final BigDecimal fixedPrincipalPercentagePerInstallment,
-            final ExternalId externalId, final LoanApplicationTerms 
loanApplicationTerms, final LoanScheduleModel loanScheduleModel,
-            final Boolean enableInstallmentLevelDelinquency, final LocalDate 
submittedOnDate) {
+            final ExternalId externalId, final LoanApplicationTerms 
loanApplicationTerms, final Boolean enableInstallmentLevelDelinquency,
+            final LocalDate submittedOnDate) {
         return new Loan(accountNo, null, group, loanType, fund, officer, 
loanPurpose, transactionProcessingStrategy, loanProduct,
                 loanRepaymentScheduleDetail, null, loanCharges, null, 
syncDisbursementWithMeeting, fixedEmiAmount, disbursementDetails,
                 maxOutstandingLoanBalance, 
createStandingInstructionAtDisbursement, isFloatingInterestRate, 
interestRateDifferential, rates,
-                fixedPrincipalPercentagePerInstallment, externalId, 
loanApplicationTerms, loanScheduleModel,
-                enableInstallmentLevelDelinquency, submittedOnDate);
+                fixedPrincipalPercentagePerInstallment, externalId, 
loanApplicationTerms, enableInstallmentLevelDelinquency,
+                submittedOnDate);
     }
 
     public static Loan newIndividualLoanApplicationFromGroup(final String 
accountNo, final Client client, final Group group,
@@ -467,13 +465,13 @@ public class Loan extends 
AbstractAuditableWithUTCDateTimeCustom<Long> {
             final List<LoanDisbursementDetails> disbursementDetails, final 
BigDecimal maxOutstandingLoanBalance,
             final Boolean createStandingInstructionAtDisbursement, final 
Boolean isFloatingInterestRate,
             final BigDecimal interestRateDifferential, final List<Rate> rates, 
final BigDecimal fixedPrincipalPercentagePerInstallment,
-            final ExternalId externalId, final LoanApplicationTerms 
loanApplicationTerms, final LoanScheduleModel loanScheduleModel,
-            final Boolean enableInstallmentLevelDelinquency, final LocalDate 
submittedOnDate) {
+            final ExternalId externalId, final LoanApplicationTerms 
loanApplicationTerms, final Boolean enableInstallmentLevelDelinquency,
+            final LocalDate submittedOnDate) {
         return new Loan(accountNo, client, group, loanType, fund, officer, 
loanPurpose, transactionProcessingStrategy, loanProduct,
                 loanRepaymentScheduleDetail, null, loanCharges, null, 
syncDisbursementWithMeeting, fixedEmiAmount, disbursementDetails,
                 maxOutstandingLoanBalance, 
createStandingInstructionAtDisbursement, isFloatingInterestRate, 
interestRateDifferential, rates,
-                fixedPrincipalPercentagePerInstallment, externalId, 
loanApplicationTerms, loanScheduleModel,
-                enableInstallmentLevelDelinquency, submittedOnDate);
+                fixedPrincipalPercentagePerInstallment, externalId, 
loanApplicationTerms, enableInstallmentLevelDelinquency,
+                submittedOnDate);
     }
 
     protected Loan() {
@@ -488,8 +486,8 @@ public class Loan extends 
AbstractAuditableWithUTCDateTimeCustom<Long> {
             final List<LoanDisbursementDetails> disbursementDetails, final 
BigDecimal maxOutstandingLoanBalance,
             final Boolean createStandingInstructionAtDisbursement, final 
Boolean isFloatingInterestRate,
             final BigDecimal interestRateDifferential, final List<Rate> rates, 
final BigDecimal fixedPrincipalPercentagePerInstallment,
-            final ExternalId externalId, final LoanApplicationTerms 
loanApplicationTerms, final LoanScheduleModel loanScheduleModel,
-            final Boolean enableInstallmentLevelDelinquency, final LocalDate 
submittedOnDate) {
+            final ExternalId externalId, final LoanApplicationTerms 
loanApplicationTerms, final Boolean enableInstallmentLevelDelinquency,
+            final LocalDate submittedOnDate) {
         this.loanRepaymentScheduleDetail = loanRepaymentScheduleDetail;
 
         this.isFloatingInterestRate = isFloatingInterestRate;
@@ -850,17 +848,6 @@ public class Loan extends 
AbstractAuditableWithUTCDateTimeCustom<Long> {
         this.postDatedChecks = new ArrayList<>();
     }
 
-    public List<LoanTransaction> retrieveListOfTransactionsExcludeAccruals() {
-        final List<LoanTransaction> repaymentsOrWaivers = new ArrayList<>();
-        for (final LoanTransaction transaction : this.loanTransactions) {
-            if (transaction.isNotReversed() && 
!transaction.isNonMonetaryTransaction()) {
-                repaymentsOrWaivers.add(transaction);
-            }
-        }
-        repaymentsOrWaivers.sort(LoanTransactionComparator.INSTANCE);
-        return repaymentsOrWaivers;
-    }
-
     public List<LoanTransaction> retrieveListOfTransactionsByType(final 
LoanTransactionType transactionType) {
         return this.loanTransactions.stream()
                 .filter(transaction -> transaction.isNotReversed() && 
transaction.getTypeOf().equals(transactionType))
@@ -880,11 +867,6 @@ public class Loan extends 
AbstractAuditableWithUTCDateTimeCustom<Long> {
                 .orElse(null);
     }
 
-    public Money calculateTotalRecoveredPayments() {
-        // in case logic for reversing recovered payment is implemented handle 
subtraction from totalRecoveredPayments
-        return getTotalRecoveredPayments();
-    }
-
     public MonetaryCurrency loanCurrency() {
         return this.loanRepaymentScheduleDetail.getCurrency();
     }
@@ -1033,17 +1015,6 @@ public class Loan extends 
AbstractAuditableWithUTCDateTimeCustom<Long> {
         return cumulativePaid;
     }
 
-    public Money getTotalRecoveredPayments() {
-        Money cumulativePaid = Money.zero(getCurrency());
-
-        for (final LoanTransaction recoveredPayment : this.loanTransactions) {
-            if (recoveredPayment.isRecoveryRepayment()) {
-                cumulativePaid = 
cumulativePaid.plus(recoveredPayment.getAmount(getCurrency()));
-            }
-        }
-        return cumulativePaid;
-    }
-
     public Money getTotalPrincipalOutstandingUntil(LocalDate date) {
         return getRepaymentScheduleInstallments().stream()
                 .filter(installment -> installment.getDueDate().isBefore(date) 
|| installment.getDueDate().isEqual(date))
@@ -1236,7 +1207,7 @@ public class Loan extends 
AbstractAuditableWithUTCDateTimeCustom<Long> {
                 .filter(date -> DateUtils.isBefore(getDisbursementDate(), 
date)).max(LocalDate::compareTo).orElse(getDisbursementDate());
     }
 
-    private boolean isUserTransaction(LoanTransaction transaction) {
+    public boolean isUserTransaction(LoanTransaction transaction) {
         return !(transaction.isReversed() || transaction.isAccrualRelated() || 
transaction.isIncomePosting());
     }
 
@@ -1391,47 +1362,6 @@ public class Loan extends 
AbstractAuditableWithUTCDateTimeCustom<Long> {
         return recalculatedOn;
     }
 
-    public void updateLoanOutstandingBalances() {
-        Money outstanding = Money.zero(getCurrency());
-        List<LoanTransaction> loanTransactions = 
retrieveListOfTransactionsExcludeAccruals();
-        for (LoanTransaction loanTransaction : loanTransactions) {
-            if (loanTransaction.isDisbursement() || 
loanTransaction.isIncomePosting() || loanTransaction.isCapitalizedIncome()) {
-                outstanding = 
outstanding.plus(loanTransaction.getAmount(getCurrency()))
-                        
.minus(loanTransaction.getOverPaymentPortion(getCurrency()));
-                
loanTransaction.updateOutstandingLoanBalance(MathUtil.negativeToZero(outstanding.getAmount()));
-            } else if (loanTransaction.isChargeback() || 
loanTransaction.isCreditBalanceRefund()) {
-                Money transactionOutstanding = 
loanTransaction.getPrincipalPortion(getCurrency());
-                if (loanTransaction.isOverPaid()) {
-                    // in case of advanced payment strategy and 
creditAllocations the full amount is recognized first
-                    if (this.getCreditAllocationRules() != null && 
!this.getCreditAllocationRules().isEmpty()) {
-                        Money payedPrincipal = 
loanTransaction.getLoanTransactionToRepaymentScheduleMappings().stream() //
-                                .map(mapping -> 
mapping.getPrincipalPortion(getCurrency())) //
-                                .reduce(Money.zero(getCurrency()), 
Money::plus);
-                        transactionOutstanding = 
loanTransaction.getPrincipalPortion(getCurrency()).minus(payedPrincipal);
-                    } else {
-                        // in case legacy payment strategy
-                        transactionOutstanding = 
loanTransaction.getAmount(getCurrency())
-                                
.minus(loanTransaction.getOverPaymentPortion(getCurrency()));
-                    }
-                    if (transactionOutstanding.isLessThanZero()) {
-                        transactionOutstanding = Money.zero(getCurrency());
-                    }
-                }
-                outstanding = outstanding.plus(transactionOutstanding);
-                
loanTransaction.updateOutstandingLoanBalance(MathUtil.negativeToZero(outstanding.getAmount()));
-            } else if (!loanTransaction.isAccrualActivity()) {
-                if (this.loanInterestRecalculationDetails != null
-                        && 
this.loanInterestRecalculationDetails.isCompoundingToBePostedAsTransaction()
-                        && !loanTransaction.isRepaymentAtDisbursement()) {
-                    outstanding = 
outstanding.minus(loanTransaction.getAmount(getCurrency()));
-                } else {
-                    outstanding = 
outstanding.minus(loanTransaction.getPrincipalPortion(getCurrency()));
-                }
-                
loanTransaction.updateOutstandingLoanBalance(MathUtil.negativeToZero(outstanding.getAmount()));
-            }
-        }
-    }
-
     public String transactionProcessingStrategy() {
         return this.transactionProcessingStrategyCode;
     }
diff --git 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransaction.java
 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransaction.java
index 397343026e..9023a83471 100644
--- 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransaction.java
+++ 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransaction.java
@@ -911,10 +911,6 @@ public class LoanTransaction extends 
AbstractAuditableWithUTCDateTimeCustom<Long
                 || this.isIncomePosting());
     }
 
-    public boolean hasLoanTransactionRelations() {
-        return !loanTransactionRelations.isEmpty();
-    }
-
     public List<LoanTransactionRelation> 
getLoanTransactionRelations(Predicate<LoanTransactionRelation> predicate) {
         return loanTransactionRelations.stream().filter(predicate).toList();
     }
diff --git 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransactionRepository.java
 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransactionRepository.java
index 04b791eb49..3934f0253f 100644
--- 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransactionRepository.java
+++ 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransactionRepository.java
@@ -127,7 +127,8 @@ public interface LoanTransactionRepository extends 
JpaRepository<LoanTransaction
                     
org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType.INITIATE_TRANSFER,
                     
org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType.REJECT_TRANSFER,
                     
org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType.WITHDRAW_TRANSFER,
-                    
org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType.CAPITALIZED_INCOME_AMORTIZATION
+                    
org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType.CAPITALIZED_INCOME_AMORTIZATION,
+                    
org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType.CAPITALIZED_INCOME_AMORTIZATION_ADJUSTMENT
             )
             """)
     Optional<LocalDate> findLastTransactionDateForReprocessing(@Param("loan") 
Loan loan);
@@ -174,7 +175,7 @@ public interface LoanTransactionRepository extends 
JpaRepository<LoanTransaction
                 AND lt.typeOf IN :types
                 AND lt.dateOf = :transactionDate
             """)
-    Optional<LoanTransaction> 
findNonReversedByLoanAndTypesAndDate(@Param("loan") Loan loan, @Param("type") 
Set<LoanTransactionType> types,
+    Optional<LoanTransaction> 
findNonReversedByLoanAndTypesAndDate(@Param("loan") Loan loan, @Param("types") 
Set<LoanTransactionType> types,
             @Param("transactionDate") LocalDate transactionDate);
 
     @Query("""
@@ -184,7 +185,7 @@ public interface LoanTransactionRepository extends 
JpaRepository<LoanTransaction
                 AND lt.reversed = false
                 AND lt.typeOf IN :types
                 AND lt.dateOf > :transactionDate
-            ORDER BY lt.dateOf
+            ORDER BY lt.dateOf, lt.createdDate, lt.id
             """)
     List<LoanTransaction> 
findNonReversedByLoanAndTypesAndAfterDate(@Param("loan") Loan loan,
             @Param("types") Set<LoanTransactionType> types, 
@Param("transactionDate") LocalDate transactionDate);
@@ -196,7 +197,7 @@ public interface LoanTransactionRepository extends 
JpaRepository<LoanTransaction
                 AND lt.reversed = false
                 AND lt.typeOf = :type
                 AND lt.dateOf > :transactionDate
-            ORDER BY lt.dateOf
+            ORDER BY lt.dateOf, lt.createdDate, lt.id
             """)
     List<LoanTransaction> 
findNonReversedByLoanAndTypeAndAfterDate(@Param("loan") Loan loan, 
@Param("type") LoanTransactionType type,
             @Param("transactionDate") LocalDate transactionDate);
@@ -219,6 +220,7 @@ public interface LoanTransactionRepository extends 
JpaRepository<LoanTransaction
                 AND lt.reversed = false
                 AND lt.typeOf IN :types
                 AND lt.id NOT IN :existingTransactionIds
+            ORDER BY lt.dateOf, lt.createdDate, lt.id
             """)
     List<LoanTransaction> 
findNonReversedByLoanAndTypesAndNotInIds(@Param("loan") Loan loan, 
@Param("types") Set<LoanTransactionType> types,
             @Param("existingTransactionIds") List<Long> 
existingTransactionIds);
@@ -229,6 +231,7 @@ public interface LoanTransactionRepository extends 
JpaRepository<LoanTransaction
             WHERE lt.loan = :loan
                 AND lt.reversed = false
                 AND lt.typeOf IN :types
+            ORDER BY lt.dateOf, lt.createdDate, lt.id
             """)
     List<LoanTransaction> findNonReversedByLoanAndTypes(@Param("loan") Loan 
loan, @Param("types") Set<LoanTransactionType> types);
 
@@ -238,6 +241,7 @@ public interface LoanTransactionRepository extends 
JpaRepository<LoanTransaction
             WHERE lt.loan = :loan
                 AND lt.reversed = false
                 AND lt.typeOf = :type
+            ORDER BY lt.dateOf, lt.createdDate, lt.id
             """)
     List<LoanTransaction> findNonReversedByLoanAndType(@Param("loan") Loan 
loan, @Param("type") LoanTransactionType type);
 
@@ -248,7 +252,7 @@ public interface LoanTransactionRepository extends 
JpaRepository<LoanTransaction
                 AND lt.reversed = false
                 AND lt.dateOf >= :date
                 AND lt.typeOf IN :types
-            ORDER BY lt.dateOf
+            ORDER BY lt.dateOf, lt.createdDate, lt.id
             """)
     List<LoanTransaction> 
findNonReversedByLoanAndTypesAndOnOrAfterDate(@Param("loan") Loan loan,
             @Param("types") Set<LoanTransactionType> types, @Param("date") 
LocalDate date);
@@ -333,7 +337,7 @@ public interface LoanTransactionRepository extends 
JpaRepository<LoanTransaction
                 AND lt.reversed = false
                 AND lt.typeOf = :type
                 AND lt.dateOf IN :transactionDates
-            ORDER BY lt.id
+            ORDER BY lt.dateOf, lt.createdDate, lt.id
             """)
     List<LoanTransaction> findNonReversedLoanAndTypeAndDates(@Param("loan") 
Loan loan, @Param("type") LoanTransactionType type,
             @Param("transactionDates") Set<LocalDate> transactionDates);
@@ -367,4 +371,90 @@ public interface LoanTransactionRepository extends 
JpaRepository<LoanTransaction
             AND ltr.relationType = 
org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRelationTypeEnum.ADJUSTMENT
             """)
     List<LoanTransaction> 
findAdjustmentsForCapitalizedIncome(@Param("capitalizedIncome") LoanTransaction 
capitalizedIncome);
+
+    @Query("""
+            SELECT lt FROM LoanTransaction lt
+            WHERE lt.loan = :loan
+                AND lt.reversed = false
+                AND lt.typeOf NOT IN (
+                        
org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType.CONTRA,
+                        
org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType.MARKED_FOR_RESCHEDULING,
+                        
org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType.ACCRUAL,
+                        
org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType.ACCRUAL_ADJUSTMENT,
+                        
org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType.APPROVE_TRANSFER,
+                        
org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType.INITIATE_TRANSFER,
+                        
org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType.REJECT_TRANSFER,
+                        
org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType.WITHDRAW_TRANSFER,
+                        
org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType.CAPITALIZED_INCOME_AMORTIZATION,
+                        
org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType.CAPITALIZED_INCOME_AMORTIZATION_ADJUSTMENT
+                )
+            ORDER BY lt.dateOf, lt.createdDate, lt.id
+            """)
+    List<LoanTransaction> 
findNonReversedTransactionsForReprocessingByLoan(@Param("loan") Loan loan);
+
+    @Query("""
+            SELECT MAX(lt.dateOf) FROM LoanTransaction lt
+            WHERE lt.loan = :loan
+                AND lt.reversed = false
+                AND lt.amount > 0
+                AND lt.typeOf IN (
+                    
org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType.REPAYMENT,
+                    
org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType.MERCHANT_ISSUED_REFUND,
+                    
org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType.PAYOUT_REFUND,
+                    
org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType.GOODWILL_CREDIT,
+                    
org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType.CHARGE_REFUND,
+                    
org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType.CHARGE_ADJUSTMENT,
+                    
org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType.DOWN_PAYMENT,
+                    
org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType.INTEREST_PAYMENT_WAIVER,
+                    
org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType.INTEREST_REFUND,
+                    
org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType.CAPITALIZED_INCOME_ADJUSTMENT
+                )
+            """)
+    Optional<LocalDate> findLastRepaymentLikeTransactionDate(@Param("loan") 
Loan loan);
+
+    @Query("""
+            SELECT CASE WHEN COUNT(lt) > 0 THEN true ELSE false END
+            FROM LoanTransaction lt
+            WHERE lt.loan = :loan
+                AND lt.reversed = false
+                AND lt.typeOf = :type
+                AND lt.dateOf > :transactionDate
+            """)
+    boolean existsNonReversedByLoanAndTypeAndAfterDate(@Param("loan") Loan 
loan, @Param("type") LoanTransactionType type,
+            @Param("transactionDate") LocalDate transactionDate);
+
+    @Query("""
+            SELECT lt FROM LoanTransaction lt
+            WHERE lt.loan = :loan
+                AND lt.reversed = false
+                AND lt.typeOf NOT IN (
+                    
org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType.CONTRA,
+                    
org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType.MARKED_FOR_RESCHEDULING,
+                    
org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType.ACCRUAL,
+                    
org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType.ACCRUAL_ADJUSTMENT,
+                    
org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType.ACCRUAL_ACTIVITY,
+                    
org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType.APPROVE_TRANSFER,
+                    
org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType.INITIATE_TRANSFER,
+                    
org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType.REJECT_TRANSFER,
+                    
org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType.WITHDRAW_TRANSFER,
+                    
org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType.CHARGE_OFF,
+                    
org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType.REAMORTIZE,
+                    
org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType.REAGE,
+                    
org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType.CAPITALIZED_INCOME_AMORTIZATION,
+                    
org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType.CONTRACT_TERMINATION,
+                    
org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType.CAPITALIZED_INCOME_AMORTIZATION_ADJUSTMENT
+                )
+            ORDER BY lt.dateOf, lt.createdDate, lt.id
+            """)
+    List<LoanTransaction> 
findNonReversedMonetaryTransactionsByLoan(@Param("loan") Loan loan);
+
+    @Query("""
+            SELECT COALESCE(SUM(lt.amount), 0)
+            FROM LoanTransaction lt
+            WHERE lt.loan = :loan
+                AND lt.reversed = false
+                AND lt.typeOf = 
org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType.RECOVERY_REPAYMENT
+            """)
+    BigDecimal calculateTotalRecoveryPaymentAmount(@Param("loan") Loan loan);
+
 }
diff --git 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/SingleLoanChargeRepaymentScheduleProcessingWrapper.java
 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/SingleLoanChargeRepaymentScheduleProcessingWrapper.java
index d12449b894..f73ed71b6d 100644
--- 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/SingleLoanChargeRepaymentScheduleProcessingWrapper.java
+++ 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/SingleLoanChargeRepaymentScheduleProcessingWrapper.java
@@ -37,7 +37,6 @@ public class 
SingleLoanChargeRepaymentScheduleProcessingWrapper {
 
     public void reprocess(final MonetaryCurrency currency, final LocalDate 
disbursementDate,
             final List<LoanRepaymentScheduleInstallment> installments, 
LoanCharge loanCharge) {
-        Loan loan = loanCharge.getLoan();
         Money zero = Money.zero(currency);
         Money totalInterest = zero;
         Money totalPrincipal = zero;
diff --git 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/AbstractLoanRepaymentScheduleTransactionProcessor.java
 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/AbstractLoanRepaymentScheduleTransactionProcessor.java
index a6aaa89ca6..b733c459da 100644
--- 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/AbstractLoanRepaymentScheduleTransactionProcessor.java
+++ 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/AbstractLoanRepaymentScheduleTransactionProcessor.java
@@ -217,11 +217,9 @@ public abstract class 
AbstractLoanRepaymentScheduleTransactionProcessor implemen
                 loanTransaction.resetDerivedComponents();
                 handleRefund(loanTransaction, currency, installments, charges);
             } else if (loanTransaction.isCreditBalanceRefund()) {
-                recalculateCreditTransaction(changedTransactionDetail, 
loanTransaction, currency, installments, transactionsToBeProcessed,
-                        overpaymentHolder);
+                recalculateCreditTransaction(changedTransactionDetail, 
loanTransaction, currency, installments, overpaymentHolder);
             } else if (loanTransaction.isChargeback()) {
-                recalculateCreditTransaction(changedTransactionDetail, 
loanTransaction, currency, installments, transactionsToBeProcessed,
-                        overpaymentHolder);
+                recalculateCreditTransaction(changedTransactionDetail, 
loanTransaction, currency, installments, overpaymentHolder);
                 
reprocessChargebackTransactionRelation(changedTransactionDetail, 
transactionsToBeProcessed);
             } else if (loanTransaction.isChargeOff()) {
                 recalculateChargeOffTransaction(changedTransactionDetail, 
loanTransaction, currency, installments);
@@ -475,7 +473,7 @@ public abstract class 
AbstractLoanRepaymentScheduleTransactionProcessor implemen
 
     protected void reprocessInstallments(LocalDate disbursementDate, 
List<LoanTransaction> transactions,
             List<LoanRepaymentScheduleInstallment> installments, 
MonetaryCurrency currency) {
-        LoanRepaymentScheduleInstallment lastInstallment = 
installments.get(installments.size() - 1);
+        LoanRepaymentScheduleInstallment lastInstallment = 
installments.getLast();
         if (lastInstallment.isAdditional() && 
lastInstallment.getDue(currency).isZero()) {
             installments.remove(lastInstallment);
         }
@@ -519,8 +517,7 @@ public abstract class 
AbstractLoanRepaymentScheduleTransactionProcessor implemen
     }
 
     private void recalculateCreditTransaction(ChangedTransactionDetail 
changedTransactionDetail, LoanTransaction loanTransaction,
-            MonetaryCurrency currency, List<LoanRepaymentScheduleInstallment> 
installments, List<LoanTransaction> transactionsToBeProcessed,
-            MoneyHolder overpaymentHolder) {
+            MonetaryCurrency currency, List<LoanRepaymentScheduleInstallment> 
installments, MoneyHolder overpaymentHolder) {
         // pass through for new transactions
         if (loanTransaction.getId() == null) {
             return;
@@ -606,7 +603,7 @@ public abstract class 
AbstractLoanRepaymentScheduleTransactionProcessor implemen
             // New installment will be added (N+1 scenario)
             if (!loanTransactionMapped) {
                 if (loanTransaction.getTransactionDate().equals(pastDueDate)) {
-                    LoanRepaymentScheduleInstallment currentInstallment = 
installmentToBeProcessed.get(installmentToBeProcessed.size() - 1);
+                    LoanRepaymentScheduleInstallment currentInstallment = 
installmentToBeProcessed.getLast();
                     
currentInstallment.addToCreditedPrincipal(transactionAmount.getAmount());
                     currentInstallment.addToPrincipal(transactionDate, 
transactionAmount);
                     if (repaidAmount.isGreaterThanZero()) {
@@ -647,7 +644,7 @@ public abstract class 
AbstractLoanRepaymentScheduleTransactionProcessor implemen
         final Set<LoanCharge> loanPenalties = extractPenaltyCharges(charges);
         Integer installmentNumber = null;
         if (loanTransaction.isChargePayment() && installments.size() == 1) {
-            installmentNumber = installments.get(0).getInstallmentNumber();
+            installmentNumber = installments.getFirst().getInstallmentNumber();
         }
 
         if (loanTransaction.isNotWaiver() && !loanTransaction.isAccrual() && 
!loanTransaction.isAccrualActivity()) {
@@ -944,12 +941,12 @@ public abstract class 
AbstractLoanRepaymentScheduleTransactionProcessor implemen
             return null;
         }
         LoanCharge latestCharge = null;
-        List<LoanCharge> chargesWithSpecificDueDate = new ArrayList<>();
-        chargesWithSpecificDueDate.addAll(charges.stream().filter(charge -> 
charge.isSpecifiedDueDate()).toList());
+        final List<LoanCharge> chargesWithSpecificDueDate = new ArrayList<>(
+                
charges.stream().filter(LoanCharge::isSpecifiedDueDate).toList());
         if (!CollectionUtils.isEmpty(chargesWithSpecificDueDate)) {
-            Collections.sort(chargesWithSpecificDueDate,
-                    (charge1, charge2) -> 
DateUtils.compare(charge1.getEffectiveDueDate(), 
charge2.getEffectiveDueDate()));
-            latestCharge = 
chargesWithSpecificDueDate.get(chargesWithSpecificDueDate.size() - 1);
+            chargesWithSpecificDueDate
+                    .sort((charge1, charge2) -> 
DateUtils.compare(charge1.getEffectiveDueDate(), 
charge2.getEffectiveDueDate()));
+            latestCharge = chargesWithSpecificDueDate.getLast();
         }
         return latestCharge;
     }
diff --git 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/AbstractCumulativeLoanScheduleGenerator.java
 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/AbstractCumulativeLoanScheduleGenerator.java
index af07a66b44..9dc0ea7948 100644
--- 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/AbstractCumulativeLoanScheduleGenerator.java
+++ 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/AbstractCumulativeLoanScheduleGenerator.java
@@ -56,6 +56,7 @@ import 
org.apache.fineract.portfolio.loanaccount.domain.LoanCharge;
 import 
org.apache.fineract.portfolio.loanaccount.domain.LoanInterestRecalcualtionAdditionalDetails;
 import 
org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
+import 
org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository;
 import 
org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.LoanRepaymentScheduleTransactionProcessor;
 import 
org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanScheduleDTO;
 import 
org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanScheduleModelDownPaymentPeriod;
@@ -63,13 +64,12 @@ import 
org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanScheduleP
 import 
org.apache.fineract.portfolio.loanaccount.loanschedule.exception.MultiDisbursementEmiAmountException;
 import 
org.apache.fineract.portfolio.loanaccount.loanschedule.exception.MultiDisbursementOutstandingAmoutException;
 import 
org.apache.fineract.portfolio.loanaccount.loanschedule.exception.ScheduleDateException;
-import 
org.apache.fineract.portfolio.loanaccount.service.LoanTransactionService;
 import org.apache.fineract.portfolio.loanproduct.domain.RepaymentStartDateType;
 
 @RequiredArgsConstructor
 public abstract class AbstractCumulativeLoanScheduleGenerator implements 
LoanScheduleGenerator {
 
-    private final LoanTransactionService loanTransactionService;
+    private final LoanTransactionRepository loanTransactionRepository;
     private final CurrencyMapper currencyMapper;
 
     @Override
@@ -404,7 +404,7 @@ public abstract class 
AbstractCumulativeLoanScheduleGenerator implements LoanSch
         // this condition is to add the interest from grace period if not
         // already applied.
         if 
(scheduleParams.getTotalOutstandingInterestPaymentDueToGrace().isGreaterThanZero())
 {
-            LoanScheduleModelPeriod installment = periods.get(periods.size() - 
1);
+            LoanScheduleModelPeriod installment = periods.getLast();
             
installment.addInterestAmount(scheduleParams.getTotalOutstandingInterestPaymentDueToGrace());
             
scheduleParams.addTotalRepaymentExpected(scheduleParams.getTotalOutstandingInterestPaymentDueToGrace());
             
scheduleParams.addTotalCumulativeInterest(scheduleParams.getTotalOutstandingInterestPaymentDueToGrace());
@@ -421,7 +421,7 @@ public abstract class 
AbstractCumulativeLoanScheduleGenerator implements LoanSch
         if (scheduleParams.getScheduleTillDate() != null) {
             currentDate = scheduleParams.getScheduleTillDate();
         }
-        if (scheduleParams.applyInterestRecalculation() && 
scheduleParams.getLatePaymentMap().size() > 0
+        if (scheduleParams.applyInterestRecalculation() && 
!scheduleParams.getLatePaymentMap().isEmpty()
                 && DateUtils.isAfter(currentDate, 
scheduleParams.getPeriodStartDate())) {
             Money totalInterest = 
addInterestOnlyRepaymentScheduleForCurrentDate(mc, loanApplicationTerms, 
holidayDetailDTO,
                     monetaryCurrency, periods, currentDate, 
loanRepaymentScheduleTransactionProcessor, transactions, loanCharges,
@@ -1345,9 +1345,8 @@ public abstract class 
AbstractCumulativeLoanScheduleGenerator implements LoanSch
         return 
principalToBeScheduled.minus(loanApplicationTerms.getDownPaymentAmount());
     }
 
-    private boolean updateFixedInstallmentAmount(final MathContext mc, final 
LoanApplicationTerms loanApplicationTerms, int periodNumber,
+    private void updateFixedInstallmentAmount(final MathContext mc, final 
LoanApplicationTerms loanApplicationTerms, int periodNumber,
             Money outstandingBalance) {
-        boolean isAmountChanged = false;
         if (loanApplicationTerms.getActualFixedEmiAmount() == null && 
loanApplicationTerms.getInterestMethod().isDecliningBalance()
                 && 
loanApplicationTerms.getAmortizationMethod().isEqualInstallment()) {
             if (periodNumber < loanApplicationTerms.getPrincipalGrace() + 1) {
@@ -1356,9 +1355,7 @@ public abstract class 
AbstractCumulativeLoanScheduleGenerator implements LoanSch
             Money emiAmount = 
loanApplicationTerms.pmtForInstallment(getPaymentPeriodsInOneYearCalculator(), 
outstandingBalance,
                     periodNumber, mc);
             loanApplicationTerms.setFixedEmiAmount(emiAmount.getAmount());
-            isAmountChanged = true;
         }
-        return isAmountChanged;
     }
 
     private Money fetchArrears(final LoanApplicationTerms 
loanApplicationTerms, final MonetaryCurrency currency,
@@ -1984,7 +1981,7 @@ public abstract class 
AbstractCumulativeLoanScheduleGenerator implements LoanSch
 
         // this method relates to multi-disbursement loans
         BigDecimal principal = BigDecimal.ZERO;
-        if (loanApplicationTerms.getDisbursementDatas().size() == 0) {
+        if (loanApplicationTerms.getDisbursementDatas().isEmpty()) {
             // non tranche loans have no disbursement data entries in 
submitted and approved status
             // the appropriate approved amount or applied for amount is used 
to show a proposed schedule
             if 
(loanApplicationTerms.getApprovedPrincipal().getAmount().compareTo(BigDecimal.ZERO)
 > 0) {
@@ -2521,10 +2518,9 @@ public abstract class 
AbstractCumulativeLoanScheduleGenerator implements LoanSch
 
         }
 
-        if (retainedInstallments.size() > 0
-                && retainedInstallments.get(retainedInstallments.size() - 
1).getRescheduleInterestPortion() != null) {
+        if (!retainedInstallments.isEmpty() && 
retainedInstallments.getLast().getRescheduleInterestPortion() != null) {
             loanApplicationTerms.setInterestTobeApproppriated(
-                    Money.of(loan.getCurrency(), 
retainedInstallments.get(retainedInstallments.size() - 
1).getRescheduleInterestPortion()));
+                    Money.of(loan.getCurrency(), 
retainedInstallments.getLast().getRescheduleInterestPortion()));
         }
         LoanScheduleModel loanScheduleModel = generate(mc, 
loanApplicationTerms, loan.getActiveCharges(), holidayDetailDTO,
                 loanScheduleParams);
@@ -2723,19 +2719,18 @@ public abstract class 
AbstractCumulativeLoanScheduleGenerator implements LoanSch
         return principalPortionCalculated;
     }
 
-    private LoanRepaymentScheduleInstallment 
addLoanRepaymentScheduleInstallment(final 
List<LoanRepaymentScheduleInstallment> installments,
+    private void addLoanRepaymentScheduleInstallment(final 
List<LoanRepaymentScheduleInstallment> installments,
             final LoanScheduleModelPeriod scheduledLoanInstallment) {
-        LoanRepaymentScheduleInstallment installment = null;
         if (scheduledLoanInstallment.isRepaymentPeriod() || 
scheduledLoanInstallment.isDownPaymentPeriod()) {
-            installment = new LoanRepaymentScheduleInstallment(null, 
scheduledLoanInstallment.periodNumber(),
-                    scheduledLoanInstallment.periodFromDate(), 
scheduledLoanInstallment.periodDueDate(),
-                    scheduledLoanInstallment.principalDue(), 
scheduledLoanInstallment.interestDue(),
-                    scheduledLoanInstallment.feeChargesDue(), 
scheduledLoanInstallment.penaltyChargesDue(),
-                    
scheduledLoanInstallment.isRecalculatedInterestComponent(), 
scheduledLoanInstallment.getLoanCompoundingDetails(),
-                    scheduledLoanInstallment.rescheduleInterestPortion(), 
scheduledLoanInstallment.isDownPaymentPeriod());
+            final LoanRepaymentScheduleInstallment installment = new 
LoanRepaymentScheduleInstallment(null,
+                    scheduledLoanInstallment.periodNumber(), 
scheduledLoanInstallment.periodFromDate(),
+                    scheduledLoanInstallment.periodDueDate(), 
scheduledLoanInstallment.principalDue(),
+                    scheduledLoanInstallment.interestDue(), 
scheduledLoanInstallment.feeChargesDue(),
+                    scheduledLoanInstallment.penaltyChargesDue(), 
scheduledLoanInstallment.isRecalculatedInterestComponent(),
+                    scheduledLoanInstallment.getLoanCompoundingDetails(), 
scheduledLoanInstallment.rescheduleInterestPortion(),
+                    scheduledLoanInstallment.isDownPaymentPeriod());
             installments.add(installment);
         }
-        return installment;
     }
 
     private LoanScheduleModelPeriod 
createLoanScheduleModelDownPaymentPeriod(final LoanRepaymentScheduleInstallment 
installment,
@@ -2804,7 +2799,7 @@ public abstract class 
AbstractCumulativeLoanScheduleGenerator implements LoanSch
 
         LoanScheduleDTO loanScheduleDTO = rescheduleNextInstallments(mc, 
loanApplicationTerms, loan, holidayDetailDTO,
                 loanRepaymentScheduleTransactionProcessor, onDate, 
calculateTill);
-        final List<LoanTransaction> loanTransactions = 
loanTransactionService.retrieveListOfTransactionsForReprocessing(loan);
+        final List<LoanTransaction> loanTransactions = 
loanTransactionRepository.findNonReversedTransactionsForReprocessingByLoan(loan);
 
         
loanRepaymentScheduleTransactionProcessor.reprocessLoanTransactions(loanApplicationTerms.getExpectedDisbursementDate(),
                 loanTransactions, currency, loanScheduleDTO.getInstallments(), 
loan.getActiveCharges());
diff --git 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/CumulativeDecliningBalanceInterestLoanScheduleGenerator.java
 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/CumulativeDecliningBalanceInterestLoanScheduleGenerator.java
index 6087a6bd8e..c68596013e 100644
--- 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/CumulativeDecliningBalanceInterestLoanScheduleGenerator.java
+++ 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/CumulativeDecliningBalanceInterestLoanScheduleGenerator.java
@@ -29,7 +29,7 @@ import 
org.apache.fineract.infrastructure.core.service.DateUtils;
 import org.apache.fineract.organisation.monetary.domain.Money;
 import org.apache.fineract.organisation.monetary.mapper.CurrencyMapper;
 import org.apache.fineract.portfolio.loanaccount.data.LoanTermVariationsData;
-import 
org.apache.fineract.portfolio.loanaccount.service.LoanTransactionService;
+import 
org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository;
 import org.apache.fineract.portfolio.loanproduct.domain.AmortizationMethod;
 import org.springframework.stereotype.Component;
 
@@ -63,9 +63,9 @@ public class 
CumulativeDecliningBalanceInterestLoanScheduleGenerator extends Abs
     private final PaymentPeriodsInOneYearCalculator 
paymentPeriodsInOneYearCalculator;
 
     public CumulativeDecliningBalanceInterestLoanScheduleGenerator(final 
ScheduledDateGenerator scheduledDateGenerator,
-            final PaymentPeriodsInOneYearCalculator 
paymentPeriodsInOneYearCalculator, final LoanTransactionService 
loanTransactionService,
-            final CurrencyMapper currencyMapper) {
-        super(loanTransactionService, currencyMapper);
+            final PaymentPeriodsInOneYearCalculator 
paymentPeriodsInOneYearCalculator,
+            final LoanTransactionRepository loanTransactionRepository, final 
CurrencyMapper currencyMapper) {
+        super(loanTransactionRepository, currencyMapper);
         this.scheduledDateGenerator = scheduledDateGenerator;
         this.paymentPeriodsInOneYearCalculator = 
paymentPeriodsInOneYearCalculator;
     }
diff --git 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/CumulativeFlatInterestLoanScheduleGenerator.java
 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/CumulativeFlatInterestLoanScheduleGenerator.java
index 4d1be6331b..1c3cb39999 100644
--- 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/CumulativeFlatInterestLoanScheduleGenerator.java
+++ 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/CumulativeFlatInterestLoanScheduleGenerator.java
@@ -27,7 +27,7 @@ import java.util.TreeMap;
 import org.apache.fineract.organisation.monetary.domain.Money;
 import org.apache.fineract.organisation.monetary.mapper.CurrencyMapper;
 import org.apache.fineract.portfolio.loanaccount.data.LoanTermVariationsData;
-import 
org.apache.fineract.portfolio.loanaccount.service.LoanTransactionService;
+import 
org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository;
 import org.springframework.stereotype.Component;
 
 @Component
@@ -37,9 +37,9 @@ public class CumulativeFlatInterestLoanScheduleGenerator 
extends AbstractCumulat
     private final PaymentPeriodsInOneYearCalculator 
paymentPeriodsInOneYearCalculator;
 
     public CumulativeFlatInterestLoanScheduleGenerator(final 
ScheduledDateGenerator scheduledDateGenerator,
-            final PaymentPeriodsInOneYearCalculator 
paymentPeriodsInOneYearCalculator, final LoanTransactionService 
loanTransactionService,
-            final CurrencyMapper currencyMapper) {
-        super(loanTransactionService, currencyMapper);
+            final PaymentPeriodsInOneYearCalculator 
paymentPeriodsInOneYearCalculator,
+            final LoanTransactionRepository loanTransactionRepository, final 
CurrencyMapper currencyMapper) {
+        super(loanTransactionRepository, currencyMapper);
         this.scheduledDateGenerator = scheduledDateGenerator;
         this.paymentPeriodsInOneYearCalculator = 
paymentPeriodsInOneYearCalculator;
     }
diff --git 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/serialization/LoanChargeValidator.java
 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/serialization/LoanChargeValidator.java
index 34e2eb0603..e84dc7841d 100644
--- 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/serialization/LoanChargeValidator.java
+++ 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/serialization/LoanChargeValidator.java
@@ -19,18 +19,24 @@
 package org.apache.fineract.portfolio.loanaccount.serialization;
 
 import java.time.LocalDate;
+import lombok.RequiredArgsConstructor;
 import org.apache.fineract.infrastructure.core.service.DateUtils;
 import 
org.apache.fineract.portfolio.charge.exception.LoanChargeCannotBeAddedException;
 import org.apache.fineract.portfolio.loanaccount.domain.Loan;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanCharge;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
+import 
org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType;
 import 
org.apache.fineract.portfolio.loanaccount.exception.InvalidLoanStateTransitionException;
 import 
org.apache.fineract.portfolio.loanaccount.exception.LoanChargeRefundException;
 import org.springframework.stereotype.Component;
 
 @Component
+@RequiredArgsConstructor
 public final class LoanChargeValidator {
 
+    private final LoanTransactionRepository loanTransactionRepository;
+
     public void validateLoanIsNotClosed(final Loan loan, final LoanCharge 
loanCharge) {
         if (loan.isClosed()) {
             final String defaultUserMessage = "This charge cannot be added as 
the loan is already closed.";
@@ -80,13 +86,13 @@ public final class LoanChargeValidator {
     public void validateRepaymentTypeTransactionNotBeforeAChargeRefund(final 
Loan loan, final LoanTransaction repaymentTransaction,
             final String reversedOrCreated) {
         if (repaymentTransaction.isRepaymentLikeType() && 
!repaymentTransaction.isChargeRefund()) {
-            for (LoanTransaction txn : loan.getLoanTransactions()) {
-                if (txn.isChargeRefund() && 
DateUtils.isBefore(repaymentTransaction.getTransactionDate(), 
txn.getTransactionDate())) {
-                    final String errorMessage = "loan.transaction.cant.be." + 
reversedOrCreated + ".because.later.charge.refund.exists";
-                    final String details = "Loan Transaction: " + loan.getId() 
+ " Can't be " + reversedOrCreated
-                            + " because a Later Charge Refund Exists.";
-                    throw new LoanChargeRefundException(errorMessage, details);
-                }
+            final boolean existsChargeRefund = 
loanTransactionRepository.existsNonReversedByLoanAndTypeAndAfterDate(loan,
+                    LoanTransactionType.CHARGE_REFUND, 
repaymentTransaction.getTransactionDate());
+            if (existsChargeRefund) {
+                final String errorMessage = "loan.transaction.cant.be." + 
reversedOrCreated + ".because.later.charge.refund.exists";
+                final String details = "Loan Transaction: " + loan.getId() + " 
Can't be " + reversedOrCreated
+                        + " because a Later Charge Refund Exists.";
+                throw new LoanChargeRefundException(errorMessage, details);
             }
         }
     }
diff --git 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanBalanceService.java
 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanBalanceService.java
index cbacb2a24e..a93d21b696 100644
--- 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanBalanceService.java
+++ 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanBalanceService.java
@@ -21,10 +21,12 @@ package org.apache.fineract.portfolio.loanaccount.service;
 import jakarta.persistence.FlushModeType;
 import java.math.BigDecimal;
 import java.time.LocalDate;
+import java.util.ArrayList;
 import java.util.List;
 import lombok.RequiredArgsConstructor;
 import org.apache.fineract.infrastructure.core.persistence.FlushModeHandler;
 import org.apache.fineract.infrastructure.core.service.DateUtils;
+import org.apache.fineract.infrastructure.core.service.MathUtil;
 import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
 import org.apache.fineract.organisation.monetary.domain.Money;
 import org.apache.fineract.portfolio.loanaccount.domain.Loan;
@@ -34,6 +36,8 @@ import 
org.apache.fineract.portfolio.loanaccount.domain.LoanInstallmentCharge;
 import 
org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
 import 
org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleProcessingWrapper;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
+import 
org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionComparator;
+import 
org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository;
 import 
org.apache.fineract.portfolio.loanproduct.domain.CreditAllocationTransactionType;
 import org.springframework.stereotype.Service;
 
@@ -43,6 +47,7 @@ public class LoanBalanceService {
 
     private final CapitalizedIncomeBalanceService 
capitalizedIncomeBalanceService;
     private final FlushModeHandler flushModeHandler;
+    private final LoanTransactionRepository loanTransactionRepository;
 
     public Money calculateTotalOverpayment(final Loan loan) {
         Money totalPaidInRepayments = loan.getTotalPaidInRepayments();
@@ -107,7 +112,7 @@ public class LoanBalanceService {
             loan.setTotalOverpaid(overpaidBy.getAmountDefaultedToNullIfZero());
         }
 
-        final Money recoveredAmount = loan.calculateTotalRecoveredPayments();
+        final Money recoveredAmount = calculateTotalRecoveredPayments(loan);
         
loan.setTotalRecovered(recoveredAmount.getAmountDefaultedToNullIfZero());
 
         final Money principal = 
loan.getLoanRepaymentScheduleDetail().getPrincipal();
@@ -115,7 +120,61 @@ public class LoanBalanceService {
         final Money capitalizedIncomeAdjustment = 
capitalizedIncomeBalanceService.calculateCapitalizedIncomeAdjustment(loan);
         loan.getSummary().updateSummary(loan.getCurrency(), principal, 
loan.getRepaymentScheduleInstallments(), loan.getLoanCharges(),
                 capitalizedIncome, capitalizedIncomeAdjustment);
-        loan.updateLoanOutstandingBalances();
+        updateLoanOutstandingBalances(loan);
+    }
+
+    private Money calculateTotalRecoveredPayments(Loan loan) {
+        // in case logic for reversing recovered payment is implemented handle 
subtraction from totalRecoveredPayments
+        final BigDecimal totalRecoveryAmount = 
loanTransactionRepository.calculateTotalRecoveryPaymentAmount(loan);
+        return Money.of(loan.getCurrency(), totalRecoveryAmount);
+    }
+
+    public void updateLoanOutstandingBalances(Loan loan) {
+        Money outstanding = Money.zero(loan.getCurrency());
+        final List<LoanTransaction> loanTransactions = new ArrayList<>();
+        for (final LoanTransaction transaction : loan.getLoanTransactions()) {
+            if (transaction.isNotReversed() && 
!transaction.isNonMonetaryTransaction()) {
+                loanTransactions.add(transaction);
+            }
+        }
+        loanTransactions.sort(LoanTransactionComparator.INSTANCE);
+
+        for (LoanTransaction loanTransaction : loanTransactions) {
+            if (loanTransaction.isDisbursement() || 
loanTransaction.isIncomePosting() || loanTransaction.isCapitalizedIncome()) {
+                outstanding = 
outstanding.plus(loanTransaction.getAmount(loan.getCurrency()))
+                        
.minus(loanTransaction.getOverPaymentPortion(loan.getCurrency()));
+                
loanTransaction.updateOutstandingLoanBalance(MathUtil.negativeToZero(outstanding.getAmount()));
+            } else if (loanTransaction.isChargeback() || 
loanTransaction.isCreditBalanceRefund()) {
+                Money transactionOutstanding = 
loanTransaction.getPrincipalPortion(loan.getCurrency());
+                if (loanTransaction.isOverPaid()) {
+                    // in case of advanced payment strategy and 
creditAllocations the full amount is recognized first
+                    if (loan.getCreditAllocationRules() != null && 
!loan.getCreditAllocationRules().isEmpty()) {
+                        Money payedPrincipal = 
loanTransaction.getLoanTransactionToRepaymentScheduleMappings().stream() //
+                                .map(mapping -> 
mapping.getPrincipalPortion(loan.getCurrency())) //
+                                .reduce(Money.zero(loan.getCurrency()), 
Money::plus);
+                        transactionOutstanding = 
loanTransaction.getPrincipalPortion(loan.getCurrency()).minus(payedPrincipal);
+                    } else {
+                        // in case legacy payment strategy
+                        transactionOutstanding = 
loanTransaction.getAmount(loan.getCurrency())
+                                
.minus(loanTransaction.getOverPaymentPortion(loan.getCurrency()));
+                    }
+                    if (transactionOutstanding.isLessThanZero()) {
+                        transactionOutstanding = 
Money.zero(loan.getCurrency());
+                    }
+                }
+                outstanding = outstanding.plus(transactionOutstanding);
+                
loanTransaction.updateOutstandingLoanBalance(MathUtil.negativeToZero(outstanding.getAmount()));
+            } else if (!loanTransaction.isAccrualActivity()) {
+                if (loan.getLoanInterestRecalculationDetails() != null
+                        && 
loan.getLoanInterestRecalculationDetails().isCompoundingToBePostedAsTransaction()
+                        && !loanTransaction.isRepaymentAtDisbursement()) {
+                    outstanding = 
outstanding.minus(loanTransaction.getAmount(loan.getCurrency()));
+                } else {
+                    outstanding = 
outstanding.minus(loanTransaction.getPrincipalPortion(loan.getCurrency()));
+                }
+                
loanTransaction.updateOutstandingLoanBalance(MathUtil.negativeToZero(outstanding.getAmount()));
+            }
+        }
     }
 
     public void updateLoanToLastDisbursalState(final Loan loan, final 
LoanDisbursementDetails disbursementDetail) {
diff --git 
a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/AdvancedPaymentScheduleTransactionProcessor.java
 
b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/AdvancedPaymentScheduleTransactionProcessor.java
index d8d4bda2fe..f9ed025ea2 100644
--- 
a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/AdvancedPaymentScheduleTransactionProcessor.java
+++ 
b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/AdvancedPaymentScheduleTransactionProcessor.java
@@ -84,6 +84,7 @@ import 
org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
 import 
org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionComparator;
 import 
org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRelation;
 import 
org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRelationTypeEnum;
+import 
org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository;
 import 
org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionToRepaymentScheduleMapping;
 import 
org.apache.fineract.portfolio.loanaccount.domain.reaging.LoanReAgeParameter;
 import 
org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.AbstractLoanRepaymentScheduleTransactionProcessor;
@@ -93,7 +94,6 @@ import 
org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanSchedul
 import 
org.apache.fineract.portfolio.loanaccount.serialization.LoanChargeValidator;
 import org.apache.fineract.portfolio.loanaccount.service.InterestRefundService;
 import org.apache.fineract.portfolio.loanaccount.service.LoanBalanceService;
-import 
org.apache.fineract.portfolio.loanaccount.service.LoanTransactionService;
 import 
org.apache.fineract.portfolio.loanaccount.service.schedule.LoanScheduleComponent;
 import org.apache.fineract.portfolio.loanproduct.calc.EMICalculator;
 import org.apache.fineract.portfolio.loanproduct.calc.data.PeriodDueDetails;
@@ -121,17 +121,17 @@ public class AdvancedPaymentScheduleTransactionProcessor 
extends AbstractLoanRep
     private final LoanRepositoryWrapper loanRepositoryWrapper;
     private final InterestRefundService interestRefundService;
     private final LoanScheduleComponent loanSchedule;
-    private final LoanTransactionService loanTransactionService;
+    private final LoanTransactionRepository loanTransactionRepository;
 
     public AdvancedPaymentScheduleTransactionProcessor(final EMICalculator 
emiCalculator, final LoanRepositoryWrapper loanRepositoryWrapper,
             final InterestRefundService interestRefundService, final 
ExternalIdFactory externalIdFactory,
-            final LoanScheduleComponent loanSchedule, final 
LoanTransactionService loanTransactionRepository,
+            final LoanScheduleComponent loanSchedule, final 
LoanTransactionRepository loanTransactionRepository,
             final LoanChargeValidator loanChargeValidator, final 
LoanBalanceService loanBalanceService) {
         super(externalIdFactory, loanChargeValidator, loanBalanceService);
         this.emiCalculator = emiCalculator;
         this.loanRepositoryWrapper = loanRepositoryWrapper;
         this.interestRefundService = interestRefundService;
-        this.loanTransactionService = loanTransactionRepository;
+        this.loanTransactionRepository = loanTransactionRepository;
         this.loanSchedule = loanSchedule;
     }
 
@@ -212,7 +212,7 @@ public class AdvancedPaymentScheduleTransactionProcessor 
extends AbstractLoanRep
         }
 
         MoneyHolder overpaymentHolder = new MoneyHolder(Money.zero(currency));
-        final Loan loan = loanTransactions.get(0).getLoan();
+        final Loan loan = loanTransactions.getFirst().getLoan();
         List<LoanTermVariationsData> loanTermVariations = 
loan.getActiveLoanTermVariations().stream().map(LoanTermVariations::toData)
                 .collect(Collectors.toCollection(ArrayList::new));
         final Integer installmentAmountInMultiplesOf = 
loan.getLoanProductRelatedDetail().getInstallmentAmountInMultiplesOf();
@@ -276,7 +276,7 @@ public class AdvancedPaymentScheduleTransactionProcessor 
extends AbstractLoanRep
     @Transactional(propagation = Propagation.REQUIRES_NEW, readOnly = true)
     public ProgressiveLoanInterestScheduleModel 
calculateInterestScheduleModel(@NotNull Long loanId, LocalDate targetDate) {
         Loan loan = loanRepositoryWrapper.findOneWithNotFoundDetection(loanId);
-        final List<LoanTransaction> transactions = 
loanTransactionService.retrieveListOfTransactionsForReprocessing(loan);
+        final List<LoanTransaction> transactions = 
loanTransactionRepository.findNonReversedTransactionsForReprocessingByLoan(loan);
         MonetaryCurrency currency = 
loan.getLoanRepaymentScheduleDetail().getCurrency();
         List<LoanRepaymentScheduleInstallment> installments = 
loan.getRepaymentScheduleInstallments();
         Set<LoanCharge> charges = loan.getActiveCharges();
@@ -552,8 +552,7 @@ public class AdvancedPaymentScheduleTransactionProcessor 
extends AbstractLoanRep
                 if (!loanTransactionMapped) {
                     if (transactionDate.equals(pastDueDate)) {
                         // Transaction is on Maturity date, no additional 
installment is needed
-                        LoanRepaymentScheduleInstallment currentInstallment = 
installmentToBeProcessed
-                                .get(installmentToBeProcessed.size() - 1);
+                        LoanRepaymentScheduleInstallment currentInstallment = 
installmentToBeProcessed.getLast();
 
                         emiCalculator.creditPrincipal(model, transactionDate, 
transactionAmount);
                         updateRepaymentPeriods(loanTransaction, 
progressiveTransactionCtx);
@@ -827,7 +826,7 @@ public class AdvancedPaymentScheduleTransactionProcessor 
extends AbstractLoanRep
             // New installment will be added (N+1 scenario)
             if (!loanTransactionMapped) {
                 if (loanTransaction.getTransactionDate().equals(pastDueDate)) {
-                    LoanRepaymentScheduleInstallment currentInstallment = 
ctx.getInstallments().get(ctx.getInstallments().size() - 1);
+                    LoanRepaymentScheduleInstallment currentInstallment = 
ctx.getInstallments().getLast();
                     recognizeAmountsAfterChargeback(ctx, transactionDate, 
currentInstallment, chargebackAllocation);
                 } else {
                     Loan loan = loanTransaction.getLoan();
@@ -1450,7 +1449,7 @@ public class AdvancedPaymentScheduleTransactionProcessor 
extends AbstractLoanRep
 
     public void recalculateInterestForDate(LocalDate targetDate, 
ProgressiveTransactionCtx ctx) {
         if (ctx.getInstallments() != null && !ctx.getInstallments().isEmpty()) 
{
-            Loan loan = ctx.getInstallments().get(0).getLoan();
+            Loan loan = ctx.getInstallments().getFirst().getLoan();
             if (loan.isInterestBearingAndInterestRecalculationEnabled() && 
!loan.isNpa() && !ctx.isChargedOff()
                     && !ctx.isContractTerminated()) {
 
@@ -1761,8 +1760,7 @@ public class AdvancedPaymentScheduleTransactionProcessor 
extends AbstractLoanRep
                         .max(LocalDate::compareTo);
 
                 if (latestDueDate.isPresent()) {
-                    final LoanRepaymentScheduleInstallment lastInstallment = 
installmentsUpToTransactionDate
-                            .get(installmentsUpToTransactionDate.size() - 1);
+                    final LoanRepaymentScheduleInstallment lastInstallment = 
installmentsUpToTransactionDate.getLast();
 
                     final LoanRepaymentScheduleInstallment 
installmentForCharges = new LoanRepaymentScheduleInstallment(loan,
                             lastInstallment.getInstallmentNumber() + 1, 
currentInstallment.getDueDate(), latestDueDate.get(),
diff --git 
a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/CapitalizedIncomeWritePlatformServiceImpl.java
 
b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/CapitalizedIncomeWritePlatformServiceImpl.java
index f9a7d6133f..840fd690ac 100644
--- 
a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/CapitalizedIncomeWritePlatformServiceImpl.java
+++ 
b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/CapitalizedIncomeWritePlatformServiceImpl.java
@@ -148,7 +148,7 @@ public class CapitalizedIncomeWritePlatformServiceImpl 
implements CapitalizedInc
         LoanTransaction savedCapitalizedIncomeAdjustment = 
loanTransactionRepository.saveAndFlush(capitalizedIncomeAdjustment);
 
         // Update outstanding loan balances
-        loan.updateLoanOutstandingBalances();
+        loanBalanceService.updateLoanOutstandingBalances(loan);
 
         // Create a note if provided
         final String noteText = command.stringValueOfParameterNamed("note");
diff --git 
a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/InternalProgressiveLoanApiResource.java
 
b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/InternalProgressiveLoanApiResource.java
index f7bbec6356..60f8cd2630 100644
--- 
a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/InternalProgressiveLoanApiResource.java
+++ 
b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/InternalProgressiveLoanApiResource.java
@@ -40,6 +40,7 @@ import 
org.apache.fineract.portfolio.loanaccount.domain.ChangedTransactionDetail
 import org.apache.fineract.portfolio.loanaccount.domain.Loan;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanRepositoryWrapper;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
+import 
org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository;
 import 
org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.AdvancedPaymentScheduleTransactionProcessor;
 import 
org.apache.fineract.portfolio.loanproduct.calc.data.ProgressiveLoanInterestScheduleModel;
 import org.springframework.beans.factory.InitializingBean;
@@ -56,9 +57,8 @@ public class InternalProgressiveLoanApiResource implements 
InitializingBean {
 
     private final LoanRepositoryWrapper loanRepository;
     private final AdvancedPaymentScheduleTransactionProcessor 
advancedPaymentScheduleTransactionProcessor;
-    private final ProgressiveLoanInterestScheduleModelParserService 
progressiveLoanInterestScheduleModelParserService;
     private final InterestScheduleModelRepositoryWrapper writePlatformService;
-    private final LoanTransactionService loanTransactionService;
+    private final LoanTransactionRepository loanTransactionRepository;
 
     @Override
     @SuppressFBWarnings("SLF4J_SIGN_ONLY_FORMAT")
@@ -87,7 +87,8 @@ public class InternalProgressiveLoanApiResource implements 
InitializingBean {
     }
 
     private ProgressiveLoanInterestScheduleModel 
reprocessTransactionsAndGetModel(final Loan loan) {
-        final List<LoanTransaction> transactionsToReprocess = 
loanTransactionService.retrieveListOfTransactionsForReprocessing(loan);
+        final List<LoanTransaction> transactionsToReprocess = 
loanTransactionRepository
+                .findNonReversedTransactionsForReprocessingByLoan(loan);
         final LocalDate businessDate = 
ThreadLocalContextUtil.getBusinessDate();
         final Pair<ChangedTransactionDetail, 
ProgressiveLoanInterestScheduleModel> 
changedTransactionDetailProgressiveLoanInterestScheduleModelPair = 
advancedPaymentScheduleTransactionProcessor
                 
.reprocessProgressiveLoanTransactionsTransactional(loan.getDisbursementDate(), 
businessDate, transactionsToReprocess,
diff --git 
a/fineract-progressive-loan/src/test/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/AdvancedPaymentScheduleTransactionProcessorTest.java
 
b/fineract-progressive-loan/src/test/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/AdvancedPaymentScheduleTransactionProcessorTest.java
index 7efc6ad18c..af7516f3bd 100644
--- 
a/fineract-progressive-loan/src/test/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/AdvancedPaymentScheduleTransactionProcessorTest.java
+++ 
b/fineract-progressive-loan/src/test/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/AdvancedPaymentScheduleTransactionProcessorTest.java
@@ -67,6 +67,7 @@ import 
org.apache.fineract.portfolio.loanaccount.domain.LoanRepositoryWrapper;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
 import 
org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRelation;
 import 
org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRelationTypeEnum;
+import 
org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType;
 import 
org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.MoneyHolder;
 import 
org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.TransactionCtx;
@@ -74,7 +75,6 @@ import 
org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanSchedul
 import 
org.apache.fineract.portfolio.loanaccount.serialization.LoanChargeValidator;
 import org.apache.fineract.portfolio.loanaccount.service.InterestRefundService;
 import org.apache.fineract.portfolio.loanaccount.service.LoanBalanceService;
-import 
org.apache.fineract.portfolio.loanaccount.service.LoanTransactionService;
 import 
org.apache.fineract.portfolio.loanaccount.service.schedule.LoanScheduleComponent;
 import org.apache.fineract.portfolio.loanproduct.calc.EMICalculator;
 import 
org.apache.fineract.portfolio.loanproduct.calc.data.ProgressiveLoanInterestScheduleModel;
@@ -120,7 +120,7 @@ class AdvancedPaymentScheduleTransactionProcessorTest {
     public void setUp() {
         underTest = new 
AdvancedPaymentScheduleTransactionProcessor(emiCalculator, 
Mockito.mock(LoanRepositoryWrapper.class),
                 Mockito.mock(InterestRefundService.class), 
Mockito.mock(ExternalIdFactory.class), 
Mockito.mock(LoanScheduleComponent.class),
-                Mockito.mock(LoanTransactionService.class), 
Mockito.mock(LoanChargeValidator.class),
+                Mockito.mock(LoanTransactionRepository.class), 
Mockito.mock(LoanChargeValidator.class),
                 Mockito.mock(LoanBalanceService.class));
 
         ThreadLocalContextUtil.setTenant(new FineractPlatformTenant(1L, 
"default", "Default", "Asia/Kolkata", null));
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanAccountDomainServiceJpa.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanAccountDomainServiceJpa.java
index c546060ab1..b36039fdb1 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanAccountDomainServiceJpa.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanAccountDomainServiceJpa.java
@@ -134,7 +134,6 @@ public class LoanAccountDomainServiceJpa implements 
LoanAccountDomainService {
     private final ConfigurationDomainService configurationDomainService;
     private final HolidayRepository holidayRepository;
     private final WorkingDaysRepositoryWrapper workingDaysRepository;
-
     private final JournalEntryWritePlatformService 
journalEntryWritePlatformService;
     private final NoteRepository noteRepository;
     private final BusinessEventNotifierService businessEventNotifierService;
@@ -464,7 +463,7 @@ public class LoanAccountDomainServiceJpa implements 
LoanAccountDomainService {
         chargesPayment.updateComponents(zero, zero, 
charge.getAmount(loan.getCurrency()), zero);
         chargesPayment.updateLoan(loan);
         loan.addLoanTransaction(chargesPayment);
-        loan.updateLoanOutstandingBalances();
+        loanBalanceService.updateLoanOutstandingBalances(loan);
         charge.markAsFullyPaid();
     }
 
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAccrualsProcessingServiceImpl.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAccrualsProcessingServiceImpl.java
index c3534e4411..4f3aeb9b04 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAccrualsProcessingServiceImpl.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAccrualsProcessingServiceImpl.java
@@ -931,7 +931,7 @@ public class LoanAccrualsProcessingServiceImpl implements 
LoanAccrualsProcessing
 
         createUpdateIncomePostingTransaction(loan, compoundingDetail, 
interest, fee, penalties, externalId);
         createUpdateAccrualTransaction(loan, compoundingDetail, interest, fee, 
penalties, feeDetails, externalId);
-        loan.updateLoanOutstandingBalances();
+        loanBalanceService.updateLoanOutstandingBalances(loan);
     }
 
     private void createUpdateIncomePostingTransaction(Loan loan, 
LoanInterestRecalcualtionAdditionalDetails compoundingDetail,
@@ -1000,7 +1000,7 @@ public class LoanAccrualsProcessingServiceImpl implements 
LoanAccrualsProcessing
 
             createIncomePostingAndAccrualTransactionOnLoanClosure(loan, 
closedDate, interestToPost, feeToPost, penaltyToPost, amountToPost);
         }
-        loan.updateLoanOutstandingBalances();
+        loanBalanceService.updateLoanOutstandingBalances(loan);
     }
 
     private void determineCumulativeIncomeFromInstallments(final Loan loan,
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAssemblerImpl.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAssemblerImpl.java
index abcce34a1d..af94f04d30 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAssemblerImpl.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAssemblerImpl.java
@@ -261,20 +261,20 @@ public class LoanAssemblerImpl implements LoanAssembler {
                     loanOfficer, loanPurpose, transactionProcessingStrategy, 
loanProductRelatedDetail, loanCharges,
                     syncDisbursementWithMeeting, fixedEmiAmount, 
disbursementDetails, maxOutstandingLoanBalance,
                     createStandingInstructionAtDisbursement, 
isFloatingInterestRate, interestRateDifferential, rates,
-                    fixedPrincipalPercentagePerInstallment, externalId, 
loanApplicationTerms, loanScheduleModel,
-                    isEnableInstallmentLevelDelinquency, submittedOnDate);
+                    fixedPrincipalPercentagePerInstallment, externalId, 
loanApplicationTerms, isEnableInstallmentLevelDelinquency,
+                    submittedOnDate);
         } else if (group != null) {
             loanApplication = Loan.newGroupLoanApplication(accountNo, group, 
loanAccountType, loanProduct, fund, loanOfficer, loanPurpose,
                     transactionProcessingStrategy, loanProductRelatedDetail, 
loanCharges, syncDisbursementWithMeeting, fixedEmiAmount,
                     disbursementDetails, maxOutstandingLoanBalance, 
createStandingInstructionAtDisbursement, isFloatingInterestRate,
                     interestRateDifferential, rates, 
fixedPrincipalPercentagePerInstallment, externalId, loanApplicationTerms,
-                    loanScheduleModel, isEnableInstallmentLevelDelinquency, 
submittedOnDate);
+                    isEnableInstallmentLevelDelinquency, submittedOnDate);
         } else if (client != null) {
             loanApplication = Loan.newIndividualLoanApplication(accountNo, 
client, loanAccountType, loanProduct, fund, loanOfficer,
                     loanPurpose, transactionProcessingStrategy, 
loanProductRelatedDetail, loanCharges, collateral, fixedEmiAmount,
                     disbursementDetails, maxOutstandingLoanBalance, 
createStandingInstructionAtDisbursement, isFloatingInterestRate,
                     interestRateDifferential, rates, 
fixedPrincipalPercentagePerInstallment, externalId, loanApplicationTerms,
-                    loanScheduleModel, isEnableInstallmentLevelDelinquency, 
submittedOnDate);
+                    isEnableInstallmentLevelDelinquency, submittedOnDate);
         } else {
             throw new IllegalStateException("No loan application exists for 
either a client or group (or both).");
         }
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanDisbursementService.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanDisbursementService.java
index 8daee2dc69..428f0d4b2b 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanDisbursementService.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanDisbursementService.java
@@ -63,6 +63,7 @@ public class LoanDisbursementService {
     private final LoanDisbursementValidator loanDisbursementValidator;
     private final ReprocessLoanTransactionsService 
reprocessLoanTransactionsService;
     private final LoanChargeService loanChargeService;
+    private final LoanBalanceService loanBalanceService;
 
     public void updateDisbursementDetails(final Loan loan, final JsonCommand 
jsonCommand, final Map<String, Object> actualChanges) {
         final List<Long> disbursementList = loan.fetchDisbursementIds();
@@ -217,7 +218,7 @@ public class LoanDisbursementService {
             chargesPayment.updateComponentsAndTotal(zero, zero, 
disbursentMoney, zero);
             chargesPayment.updateLoan(loan);
             loan.addLoanTransaction(chargesPayment);
-            loan.updateLoanOutstandingBalances();
+            loanBalanceService.updateLoanOutstandingBalances(loan);
         }
 
         final LocalDate expectedDate = loan.getExpectedFirstRepaymentOnDate();
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanTransactionProcessingServiceImpl.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanTransactionProcessingServiceImpl.java
index a8a358a3f8..825880c7ed 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanTransactionProcessingServiceImpl.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanTransactionProcessingServiceImpl.java
@@ -61,8 +61,8 @@ public class LoanTransactionProcessingServiceImpl implements 
LoanTransactionProc
     private final LoanRepaymentScheduleTransactionProcessorFactory 
transactionProcessorFactory;
     private final LoanTermVariationsMapper loanMapper;
     private final InterestScheduleModelRepositoryWrapper modelRepository;
-    private final LoanTransactionService loanTransactionService;
     private final LoanBalanceService loanBalanceService;
+    private final LoanTransactionService loanTransactionService;
 
     @Override
     public boolean canProcessLatestTransactionOnly(Loan loan, LoanTransaction 
loanTransaction,
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java
index 43b64bf748..1dabf678f5 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java
@@ -594,8 +594,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl 
implements LoanWritePlatf
         }
 
         if (loan.getLoanProduct().isMultiDisburseLoan() || 
loan.isProgressiveSchedule()) {
-            final List<LoanTransaction> 
allNonContraTransactionsPostDisbursement = loanTransactionService
-                    .retrieveListOfTransactionsForReprocessing(loan);
+            final List<LoanTransaction> 
allNonContraTransactionsPostDisbursement = loanTransactionRepository
+                    .findNonReversedTransactionsForReprocessingByLoan(loan);
             if (!allNonContraTransactionsPostDisbursement.isEmpty()) {
                 reprocessLoanTransactionsService.reprocessTransactions(loan);
             }
@@ -2246,7 +2246,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl 
implements LoanWritePlatf
                 throw new 
MultiDisbursementDataNotAllowedException(LoanApiConstants.disbursementDataParameterName,
 errorMessage);
             }
         } else {
-            if (disbursementDataArray == null || disbursementDataArray.size() 
== 0) {
+            if (disbursementDataArray == null || 
disbursementDataArray.isEmpty()) {
                 final String errorMessage = "For this loan product, 
disbursement details must be provided";
                 throw new 
MultiDisbursementDataRequiredException(LoanApiConstants.disbursementDataParameterName,
 errorMessage);
             }
@@ -2821,7 +2821,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl 
implements LoanWritePlatf
                 final ScheduleGeneratorDTO scheduleGeneratorDTO = 
this.loanUtilService.buildScheduleGeneratorDTO(loan, null, null);
                 
loanScheduleService.regenerateRepaymentScheduleWithInterestRecalculation(loan, 
scheduleGeneratorDTO);
             }
-            final List<LoanTransaction> loanTransactions = 
loanTransactionService.retrieveListOfTransactionsForReprocessing(loan);
+            final List<LoanTransaction> loanTransactions = 
loanTransactionRepository.findNonReversedTransactionsForReprocessingByLoan(loan);
             loanTransactions.add(chargeOffTransaction);
             
reprocessLoanTransactionsService.reprocessParticularTransactions(loan, 
loanTransactions);
             loan.addLoanTransaction(chargeOffTransaction);
@@ -3031,7 +3031,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl 
implements LoanWritePlatf
             final String errorMessage = 
"loan.product.does.not.support.multiple.disbursals.cannot.undo.last.disbursal";
             throw new LoanMultiDisbursementException(errorMessage);
         }
-        Integer trancheDisbursedCount = 0;
+        int trancheDisbursedCount = 0;
         for (LoanDisbursementDetails disbursementDetails : 
loan.getDisbursementDetails()) {
             if (disbursementDetails.actualDisbursementDate() != null) {
                 trancheDisbursedCount++;
@@ -3361,13 +3361,13 @@ public class LoanWritePlatformServiceJpaRepositoryImpl 
implements LoanWritePlatf
         final Map<String, Object> actualChanges = new LinkedHashMap<>();
         List<LoanTransaction> loanTransactions = 
loan.retrieveListOfTransactionsByType(LoanTransactionType.DISBURSEMENT);
         loanTransactions.sort(Comparator.comparing(LoanTransaction::getId));
-        final LoanTransaction lastDisbursalTransaction = 
loanTransactions.get(loanTransactions.size() - 1);
+        final LoanTransaction lastDisbursalTransaction = 
loanTransactions.getLast();
         final LocalDate lastTransactionDate = 
lastDisbursalTransaction.getTransactionDate();
 
         
existingTransactionIds.addAll(loanTransactionRepository.findTransactionIdsByLoan(loan));
         
existingReversedTransactionIds.addAll(loanTransactionRepository.findReversedTransactionIdsByLoan(loan));
 
-        loanTransactions = loan.retrieveListOfTransactionsExcludeAccruals();
+        loanTransactions = 
loanTransactionRepository.findNonReversedMonetaryTransactionsByLoan(loan);
         Collections.reverse(loanTransactions);
         for (final LoanTransaction previousTransaction : loanTransactions) {
             if (DateUtils.isBefore(lastTransactionDate, 
previousTransaction.getTransactionDate())
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/ProgressiveLoanSummaryDataProvider.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/ProgressiveLoanSummaryDataProvider.java
index 3cfcd1cef3..2606174fdc 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/ProgressiveLoanSummaryDataProvider.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/ProgressiveLoanSummaryDataProvider.java
@@ -36,6 +36,7 @@ import org.apache.fineract.portfolio.loanaccount.domain.Loan;
 import 
org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanRepositoryWrapper;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
+import 
org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository;
 import 
org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.AdvancedPaymentScheduleTransactionProcessor;
 import 
org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanScheduleData;
 import 
org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanSchedulePeriodData;
@@ -53,7 +54,7 @@ public class ProgressiveLoanSummaryDataProvider extends 
CommonLoanSummaryDataPro
     private final AdvancedPaymentScheduleTransactionProcessor 
advancedPaymentScheduleTransactionProcessor;
     private final EMICalculator emiCalculator;
     private final LoanRepositoryWrapper loanRepository;
-    private final LoanTransactionService loanTransactionService;
+    private final LoanTransactionRepository loanTransactionRepository;
     private final InterestScheduleModelRepositoryWrapper modelRepository;
 
     @Override
@@ -81,8 +82,8 @@ public class ProgressiveLoanSummaryDataProvider extends 
CommonLoanSummaryDataPro
     }
 
     private ProgressiveLoanInterestScheduleModel calculateModel(Loan loan, 
LocalDate businessDate) {
-        final List<LoanTransaction> transactionsToReprocess = 
loanTransactionService.retrieveListOfTransactionsForReprocessing(loan)
-                .stream().filter(t -> !t.isAccrualActivity()).toList();
+        final List<LoanTransaction> transactionsToReprocess = 
loanTransactionRepository
+                
.findNonReversedTransactionsForReprocessingByLoan(loan).stream().filter(t -> 
!t.isAccrualActivity()).toList();
         Pair<ChangedTransactionDetail, ProgressiveLoanInterestScheduleModel> 
changedTransactionDetailProgressiveLoanInterestScheduleModelPair = 
advancedPaymentScheduleTransactionProcessor
                 
.reprocessProgressiveLoanTransactions(loan.getDisbursementDate(), businessDate, 
transactionsToReprocess, loan.getCurrency(),
                         loan.getRepaymentScheduleInstallments(), 
loan.getActiveCharges());
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/ReprocessLoanTransactionsServiceImpl.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/ReprocessLoanTransactionsServiceImpl.java
index 5092c3a62d..acf391aab5 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/ReprocessLoanTransactionsServiceImpl.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/ReprocessLoanTransactionsServiceImpl.java
@@ -22,8 +22,6 @@ import java.time.LocalDate;
 import java.util.List;
 import java.util.Optional;
 import java.util.Set;
-import java.util.function.Predicate;
-import java.util.stream.Collectors;
 import lombok.RequiredArgsConstructor;
 import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil;
 import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
@@ -37,7 +35,6 @@ import 
org.apache.fineract.portfolio.loanaccount.domain.LoanCharge;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanChargePaidBy;
 import 
org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleProcessingWrapper;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
-import 
org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionComparator;
 import 
org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository;
 import 
org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.MoneyHolder;
 import 
org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.TransactionCtx;
@@ -55,26 +52,18 @@ public class ReprocessLoanTransactionsServiceImpl 
implements ReprocessLoanTransa
     private final InterestScheduleModelRepositoryWrapper 
interestScheduleModelRepositoryWrapper;
     private final LoanBalanceService loanBalanceService;
     private final LoanTransactionRepository loanTransactionRepository;
+    private final LoanTransactionService loanTransactionService;
 
     @Override
     public void reprocessTransactions(final Loan loan) {
-        final List<LoanTransaction> allNonContraTransactionsPostDisbursement = 
retrieveListOfTransactionsForReprocessing(loan);
+        final List<LoanTransaction> allNonContraTransactionsPostDisbursement = 
loanTransactionService
+                .retrieveListOfTransactionsForReprocessing(loan);
+
         final ChangedTransactionDetail changedTransactionDetail = 
reprocessTransactionsAndFetchChangedTransactions(loan,
                 allNonContraTransactionsPostDisbursement);
         handleChangedDetail(changedTransactionDetail);
     }
 
-    private List<LoanTransaction> 
retrieveListOfTransactionsForReprocessing(final Loan loan) {
-        return 
loan.getLoanTransactions().stream().filter(loanTransactionForReprocessingPredicate())
-                
.sorted(LoanTransactionComparator.INSTANCE).collect(Collectors.toList());
-    }
-
-    private Predicate<LoanTransaction> 
loanTransactionForReprocessingPredicate() {
-        return transaction -> transaction.isNotReversed()
-                && (transaction.isChargeOff() || transaction.isReAge() || 
transaction.isAccrualActivity() || transaction.isReAmortize()
-                        || !transaction.isNonMonetaryTransaction() || 
transaction.isContractTermination());
-    }
-
     @Override
     public void reprocessParticularTransactions(final Loan loan, final 
List<LoanTransaction> loanTransactions) {
         final ChangedTransactionDetail changedTransactionDetail = 
reprocessTransactionsAndFetchChangedTransactions(loan, loanTransactions);
@@ -83,7 +72,7 @@ public class ReprocessLoanTransactionsServiceImpl implements 
ReprocessLoanTransa
 
     @Override
     public void reprocessTransactionsWithPostTransactionChecks(final Loan 
loan, final LocalDate transactionDate) {
-        final List<LoanTransaction> transactions = 
retrieveListOfTransactionsForReprocessing(loan);
+        final List<LoanTransaction> transactions = 
loanTransactionRepository.findNonReversedTransactionsForReprocessingByLoan(loan);
         final ChangedTransactionDetail changedTransactionDetail = 
reprocessTransactionsAndFetchChangedTransactions(loan, transactions);
         handleChangedDetail(changedTransactionDetail);
     }
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/contracttermination/LoanContractTerminationServiceImpl.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/contracttermination/LoanContractTerminationServiceImpl.java
index 28ee5ba502..b9cb527976 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/contracttermination/LoanContractTerminationServiceImpl.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/contracttermination/LoanContractTerminationServiceImpl.java
@@ -70,10 +70,10 @@ public class LoanContractTerminationServiceImpl {
     private final LoanUtilService loanUtilService;
     private final ExternalIdFactory externalIdFactory;
     private final BusinessEventNotifierService businessEventNotifierService;
-    private final LoanTransactionService loanTransactionService;
     private final LoanScheduleService loanScheduleService;
     private final LoanChargeValidator loanChargeValidator;
     private final ProgressiveLoanTransactionValidator loanTransactionValidator;
+    private final LoanTransactionService loanTransactionService;
 
     public CommandProcessingResult applyContractTermination(final JsonCommand 
command) {
         final Loan loan = loanAssembler.assembleFrom(command.getLoanId());
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 04dd3aecb8..e3f52a4d73 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.infrastructure.core.service.ExternalIdFactory;
 import 
org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleTransactionProcessorFactory;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanRepositoryWrapper;
+import 
org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository;
 import 
org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.LoanRepaymentScheduleTransactionProcessor;
 import 
org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.AdvancedPaymentScheduleTransactionProcessor;
 import 
org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.CreocoreLoanRepaymentScheduleTransactionProcessor;
@@ -35,7 +36,6 @@ import 
org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.imp
 import 
org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.RBILoanRepaymentScheduleTransactionProcessor;
 import 
org.apache.fineract.portfolio.loanaccount.serialization.LoanChargeValidator;
 import org.apache.fineract.portfolio.loanaccount.service.LoanBalanceService;
-import 
org.apache.fineract.portfolio.loanaccount.service.LoanTransactionService;
 import 
org.apache.fineract.portfolio.loanaccount.service.ProgressiveLoanInterestRefundServiceImpl;
 import 
org.apache.fineract.portfolio.loanaccount.service.schedule.LoanScheduleComponent;
 import org.apache.fineract.portfolio.loanproduct.calc.EMICalculator;
@@ -138,9 +138,9 @@ public class LoanAccountAutoStarter {
             final LoanRepositoryWrapper loanRepositoryWrapper,
             final @Lazy ProgressiveLoanInterestRefundServiceImpl 
progressiveLoanInterestRefundService,
             final ExternalIdFactory externalIdFactory, final 
LoanScheduleComponent loanSchedule,
-            final LoanTransactionService loanTransactionService, final 
LoanChargeValidator loanChargeValidator,
+            final LoanTransactionRepository loanTransactionRepository, final 
LoanChargeValidator loanChargeValidator,
             final LoanBalanceService loanBalanceService) {
         return new AdvancedPaymentScheduleTransactionProcessor(emiCalculator, 
loanRepositoryWrapper, progressiveLoanInterestRefundService,
-                externalIdFactory, loanSchedule, loanTransactionService, 
loanChargeValidator, loanBalanceService);
+                externalIdFactory, loanSchedule, loanTransactionRepository, 
loanChargeValidator, loanBalanceService);
     }
 }
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/starter/LoanAccountConfiguration.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/starter/LoanAccountConfiguration.java
index 15a96513ef..ae9d3b255f 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/starter/LoanAccountConfiguration.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/starter/LoanAccountConfiguration.java
@@ -457,9 +457,9 @@ public class LoanAccountConfiguration {
     @ConditionalOnMissingBean(LoanDisbursementService.class)
     public LoanDisbursementService loanDisbursementService(LoanChargeValidator 
loanChargeValidator,
             LoanDisbursementValidator loanDisbursementValidator, 
ReprocessLoanTransactionsService reprocessLoanTransactionsService,
-            LoanChargeService loanChargeService) {
+            LoanChargeService loanChargeService, LoanBalanceService 
loanBalanceService) {
         return new LoanDisbursementService(loanChargeValidator, 
loanDisbursementValidator, reprocessLoanTransactionsService,
-                loanChargeService);
+                loanChargeService, loanBalanceService);
     }
 
     @Bean
diff --git 
a/fineract-provider/src/test/java/org/apache/fineract/infrastructure/event/external/service/serialization/serializer/loan/LoanAccountDelinquencyRangeEventSerializerTest.java
 
b/fineract-provider/src/test/java/org/apache/fineract/infrastructure/event/external/service/serialization/serializer/loan/LoanAccountDelinquencyRangeEventSerializerTest.java
index a909e638a0..9d41557b70 100644
--- 
a/fineract-provider/src/test/java/org/apache/fineract/infrastructure/event/external/service/serialization/serializer/loan/LoanAccountDelinquencyRangeEventSerializerTest.java
+++ 
b/fineract-provider/src/test/java/org/apache/fineract/infrastructure/event/external/service/serialization/serializer/loan/LoanAccountDelinquencyRangeEventSerializerTest.java
@@ -363,7 +363,7 @@ public class LoanAccountDelinquencyRangeEventSerializerTest 
{
         DelinquencyReadPlatformService delinquencyReadPlatformService = new 
DelinquencyReadPlatformServiceImpl(repositoryRange,
                 repositoryBucket, repositoryLoanDelinquencyTagHistory, 
mapperRange, mapperBucket, mapperLoanDelinquencyTagHistory,
                 loanRepository, loanDelinquencyDomainService, 
repositoryLoanInstallmentDelinquencyTag, loanDelinquencyActionRepository,
-                delinquencyEffectivePauseHelper, configurationDomainService);
+                delinquencyEffectivePauseHelper, configurationDomainService, 
Mockito.mock(LoanTransactionRepository.class));
 
         LoanProduct loanProduct = Mockito.mock(LoanProduct.class);
         when(loanProduct.isMultiDisburseLoan()).thenReturn(false);
diff --git 
a/fineract-provider/src/test/java/org/apache/fineract/portfolio/loanaccount/domain/LoanBuilder.java
 
b/fineract-provider/src/test/java/org/apache/fineract/portfolio/loanaccount/domain/LoanBuilder.java
index fa78079574..fc5a720942 100644
--- 
a/fineract-provider/src/test/java/org/apache/fineract/portfolio/loanaccount/domain/LoanBuilder.java
+++ 
b/fineract-provider/src/test/java/org/apache/fineract/portfolio/loanaccount/domain/LoanBuilder.java
@@ -40,7 +40,6 @@ import org.apache.fineract.portfolio.group.domain.Group;
 import 
org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.LoanRepaymentScheduleTransactionProcessor;
 import 
org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.InterestPrincipalPenaltyFeesOrderLoanRepaymentScheduleTransactionProcessor;
 import 
org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanApplicationTerms;
-import 
org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleModel;
 import 
org.apache.fineract.portfolio.loanaccount.serialization.LoanChargeValidator;
 import org.apache.fineract.portfolio.loanaccount.service.LoanBalanceService;
 import org.apache.fineract.portfolio.loanproduct.domain.LoanProduct;
@@ -80,7 +79,6 @@ public class LoanBuilder {
     private BigDecimal fixedPrincipalPercentagePerInstallment;
     private ExternalId externalId = ExternalId.empty();
     private LoanApplicationTerms loanApplicationTerms = 
mock(LoanApplicationTerms.class);
-    private LoanScheduleModel loanScheduleModel = 
mock(LoanScheduleModel.class);
     private Boolean enableInstallmentLevelDelinquency = false;
     private LocalDate submittedOnDate = LocalDate.now(ZoneId.systemDefault());
     private LocalDate approvedOnDate;
@@ -116,8 +114,8 @@ public class LoanBuilder {
             Loan loan = Loan.newIndividualLoanApplication(accountNo, client, 
loanType, loanProduct, fund, loanOfficer, loanPurpose,
                     transactionProcessor, loanRepaymentScheduleDetail, 
charges, collateral, fixedEmiAmount, disbursementDetails,
                     maxOutstandingLoanBalance, 
createStandingInstructionAtDisbursement, isFloatingInterestRate, 
interestRateDifferential,
-                    rates, fixedPrincipalPercentagePerInstallment, externalId, 
loanApplicationTerms, loanScheduleModel,
-                    enableInstallmentLevelDelinquency, submittedOnDate);
+                    rates, fixedPrincipalPercentagePerInstallment, externalId, 
loanApplicationTerms, enableInstallmentLevelDelinquency,
+                    submittedOnDate);
 
             if (id != null) {
                 loan.setId(id);
@@ -335,11 +333,6 @@ public class LoanBuilder {
         return this;
     }
 
-    public LoanBuilder withLoanScheduleModel(LoanScheduleModel 
loanScheduleModel) {
-        this.loanScheduleModel = loanScheduleModel;
-        return this;
-    }
-
     public LoanBuilder withEnableInstallmentLevelDelinquency(Boolean 
enableInstallmentLevelDelinquency) {
         this.enableInstallmentLevelDelinquency = 
enableInstallmentLevelDelinquency;
         return this;

Reply via email to