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 bd19f28cb FINERACT-2111: Performance issue on Retrieve Loan API - 
Transactions
bd19f28cb is described below

commit bd19f28cb682a30af1838f0e2ee53661b341b9f2
Author: Jose Alberto Hernandez <[email protected]>
AuthorDate: Tue Jul 30 13:41:26 2024 -0600

    FINERACT-2111: Performance issue on Retrieve Loan API - Transactions
---
 .../service/LoanDelinquencyDomainServiceImpl.java  |  9 ++-
 .../starter/DelinquencyConfiguration.java          |  6 +-
 .../loanaccount/data/LoanChargePaidByData.java     | 27 +++++---
 .../loanaccount/data/LoanTransactionData.java      |  4 ++
 .../domain/LoanTransactionRelationRepository.java  |  3 -
 .../LoanChargePaidByMapper.java}                   | 17 ++++-
 .../LoanChargePaidByReadPlatformServiceImpl.java   | 68 -------------------
 .../service/LoanChargePaidByReadService.java       | 76 +++++++++++++++++++++
 .../service/LoanTransactionReadService.java        | 79 ++++++++++++++++++++++
 .../LoanTransactionRelationReadService.java        | 76 +++++++++++++++++++++
 ...anAdjustTransactionBusinessEventSerializer.java | 10 +--
 .../loan/LoanChargeOffBusinessEventSerializer.java |  6 +-
 .../LoanTransactionBusinessEventSerializer.java    |  6 +-
 .../api/LoanTransactionsApiResource.java           |  8 +--
 .../loanaccount/api/LoansApiResource.java          |  5 +-
 .../service/LoanReadPlatformService.java           |  3 -
 .../service/LoanReadPlatformServiceImpl.java       | 36 +++++-----
 .../starter/LoanAccountConfiguration.java          | 22 ++----
 ...justTransactionBusinessEventSerializerTest.java |  8 +--
 .../LoanDelinquencyDomainServiceTest.java          | 14 ++--
 .../integrationtests/BaseLoanIntegrationTest.java  |  2 +-
 21 files changed, 331 insertions(+), 154 deletions(-)

diff --git 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/delinquency/service/LoanDelinquencyDomainServiceImpl.java
 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/delinquency/service/LoanDelinquencyDomainServiceImpl.java
index c09a71479..0158f8dad 100644
--- 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/delinquency/service/LoanDelinquencyDomainServiceImpl.java
+++ 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/delinquency/service/LoanDelinquencyDomainServiceImpl.java
@@ -35,6 +35,8 @@ import 
org.apache.fineract.portfolio.loanaccount.data.LoanDelinquencyData;
 import org.apache.fineract.portfolio.loanaccount.domain.Loan;
 import 
org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType;
+import 
org.apache.fineract.portfolio.loanaccount.service.LoanTransactionReadService;
 import org.springframework.transaction.annotation.Transactional;
 
 @Slf4j
@@ -42,6 +44,7 @@ import 
org.springframework.transaction.annotation.Transactional;
 public class LoanDelinquencyDomainServiceImpl implements 
LoanDelinquencyDomainService {
 
     private final DelinquencyEffectivePauseHelper 
delinquencyEffectivePauseHelper;
+    private final LoanTransactionReadService loanTransactionReadService;
 
     @Override
     @Transactional(readOnly = true)
@@ -262,7 +265,8 @@ public class LoanDelinquencyDomainServiceImpl implements 
LoanDelinquencyDomainSe
             final LoanRepaymentScheduleInstallment installment) {
         final MonetaryCurrency loanCurrency = loan.getCurrency();
         LoanRepaymentScheduleInstallment latestInstallment = 
loan.getLastLoanRepaymentScheduleInstallment();
-        List<LoanTransaction> chargebackTransactions = 
loan.getLoanTransactions(LoanTransaction::isChargeback);
+        List<LoanTransaction> chargebackTransactions = 
loanTransactionReadService.fetchLoanTransactionsByType(loan.getId(), null,
+                LoanTransactionType.CHARGEBACK.getValue());
         LocalDate overdueSinceDate = null;
         CollectionData collectionData = CollectionData.template();
         BigDecimal outstandingAmount = BigDecimal.ZERO;
@@ -319,7 +323,8 @@ public class LoanDelinquencyDomainServiceImpl implements 
LoanDelinquencyDomainSe
         BigDecimal delinquentFee = BigDecimal.ZERO;
         BigDecimal delinquentPenalty = BigDecimal.ZERO;
 
-        List<LoanTransaction> chargebackTransactions = 
loan.getLoanTransactions(LoanTransaction::isChargeback);
+        List<LoanTransaction> chargebackTransactions = 
loanTransactionReadService.fetchLoanTransactionsByType(loan.getId(), null,
+                LoanTransactionType.CHARGEBACK.getValue());
         BigDecimal amountAvailable = 
installment.getTotalPaid(loanCurrency).getAmount();
         for (LoanTransaction loanTransaction : chargebackTransactions) {
 
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 7848ec730..71e638219 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.service.LoanTransactionReadService;
 import org.apache.fineract.portfolio.loanproduct.domain.LoanProductRepository;
 import 
org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
 import org.springframework.context.annotation.Bean;
@@ -88,7 +89,8 @@ public class DelinquencyConfiguration {
 
     @Bean
     @ConditionalOnMissingBean(LoanDelinquencyDomainService.class)
-    public LoanDelinquencyDomainService 
loanDelinquencyDomainService(DelinquencyEffectivePauseHelper 
delinquencyEffectivePauseHelper) {
-        return new 
LoanDelinquencyDomainServiceImpl(delinquencyEffectivePauseHelper);
+    public LoanDelinquencyDomainService 
loanDelinquencyDomainService(DelinquencyEffectivePauseHelper 
delinquencyEffectivePauseHelper,
+            LoanTransactionReadService loanTransactionReadService) {
+        return new 
LoanDelinquencyDomainServiceImpl(delinquencyEffectivePauseHelper, 
loanTransactionReadService);
     }
 }
diff --git 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanChargePaidByData.java
 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanChargePaidByData.java
index abab243a9..7e13697b2 100644
--- 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanChargePaidByData.java
+++ 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanChargePaidByData.java
@@ -19,19 +19,30 @@
 package org.apache.fineract.portfolio.loanaccount.data;
 
 import java.math.BigDecimal;
+import lombok.Data;
 import lombok.Getter;
-import lombok.RequiredArgsConstructor;
+import org.springframework.integration.annotation.Default;
 
+@Data
 @Getter
-@RequiredArgsConstructor
 public class LoanChargePaidByData {
 
-    private final Long id;
-    private final BigDecimal amount;
-    private final Integer installmentNumber;
-    private final Long chargeId;
-    private final Long transactionId;
-    private final String name;
+    private Long id;
+    private BigDecimal amount;
+    private Integer installmentNumber;
+    private Long chargeId;
+    private Long transactionId;
+    private String name;
+
+    @Default
+    public LoanChargePaidByData(Long id, BigDecimal amount, Integer 
installmentNumber, Long chargeId, Long transactionId, String name) {
+        this.id = id;
+        this.amount = amount;
+        this.installmentNumber = installmentNumber;
+        this.chargeId = chargeId;
+        this.transactionId = transactionId;
+        this.name = name;
+    }
 
     public LoanChargePaidByData(final Long id, final BigDecimal amount, final 
Integer installmentNumber, final Long chargeId,
             final Long transactionId) {
diff --git 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanTransactionData.java
 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanTransactionData.java
index b27943529..8942201bc 100644
--- 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanTransactionData.java
+++ 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanTransactionData.java
@@ -381,4 +381,8 @@ public class LoanTransactionData {
     public void setLoanTransactionRelations(List<LoanTransactionRelationData> 
transactionRelations) {
         this.transactionRelations = transactionRelations;
     }
+
+    public boolean supportTransactionRelations() {
+        return !type.isAccrual();
+    }
 }
diff --git 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransactionRelationRepository.java
 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransactionRelationRepository.java
index 7d2afe217..e09628110 100644
--- 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransactionRelationRepository.java
+++ 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransactionRelationRepository.java
@@ -18,13 +18,10 @@
  */
 package org.apache.fineract.portfolio.loanaccount.domain;
 
-import java.util.List;
 import org.springframework.data.jpa.repository.JpaRepository;
 import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
 
 public interface LoanTransactionRelationRepository
         extends JpaRepository<LoanTransactionRelation, Long>, 
JpaSpecificationExecutor<LoanTransactionRelation> {
 
-    List<LoanTransactionRelation> findByFromTransaction(LoanTransaction 
fromTransaction);
-
 }
diff --git 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanChargePaidByReadPlatformService.java
 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/mapper/LoanChargePaidByMapper.java
similarity index 56%
rename from 
fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanChargePaidByReadPlatformService.java
rename to 
fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/mapper/LoanChargePaidByMapper.java
index 1ca6dc172..9e1ef978f 100644
--- 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanChargePaidByReadPlatformService.java
+++ 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/mapper/LoanChargePaidByMapper.java
@@ -16,12 +16,23 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.fineract.portfolio.loanaccount.service;
+package org.apache.fineract.portfolio.loanaccount.mapper;
 
 import java.util.List;
+import org.apache.fineract.infrastructure.core.config.MapstructMapperConfig;
 import org.apache.fineract.portfolio.loanaccount.data.LoanChargePaidByData;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanChargePaidBy;
+import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
 
-public interface LoanChargePaidByReadPlatformService {
+@Mapper(config = MapstructMapperConfig.class)
+public interface LoanChargePaidByMapper {
+
+    @Mapping(target = "transactionId", source = "source.loanTransaction.id")
+    @Mapping(target = "chargeId", source = "source.loanCharge.id")
+    @Mapping(target = "name", source = "source.loanCharge.charge.name")
+    LoanChargePaidByData map(LoanChargePaidBy source);
+
+    List<LoanChargePaidByData> map(List<LoanChargePaidBy> sources);
 
-    List<LoanChargePaidByData> getLoanChargesPaidByTransactionId(Long id);
 }
diff --git 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanChargePaidByReadPlatformServiceImpl.java
 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanChargePaidByReadPlatformServiceImpl.java
deleted file mode 100644
index 976c04f2e..000000000
--- 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanChargePaidByReadPlatformServiceImpl.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.apache.fineract.portfolio.loanaccount.service;
-
-import java.math.BigDecimal;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.util.List;
-import lombok.RequiredArgsConstructor;
-import 
org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
-import org.apache.fineract.portfolio.loanaccount.data.LoanChargePaidByData;
-import org.springframework.jdbc.core.JdbcTemplate;
-import org.springframework.jdbc.core.RowMapper;
-
-@RequiredArgsConstructor
-public class LoanChargePaidByReadPlatformServiceImpl implements 
LoanChargePaidByReadPlatformService {
-
-    private final JdbcTemplate jdbcTemplate;
-    private final PlatformSecurityContext context;
-
-    @Override
-    public List<LoanChargePaidByData> getLoanChargesPaidByTransactionId(Long 
transactionId) {
-        this.context.authenticatedUser();
-        final LoanChargePaidByMapper rm = new LoanChargePaidByMapper();
-        final String sql = "select " + rm.loanChargePaidBySchema() + " where 
lcpd.loan_transaction_id = ?";
-        return this.jdbcTemplate.query(sql, rm, transactionId); // NOSONAR
-    }
-
-    private static final class LoanChargePaidByMapper implements 
RowMapper<LoanChargePaidByData> {
-
-        public String loanChargePaidBySchema() {
-            return "lcpd.id as id, lcpd.amount as amount, 
lcpd.installment_number as installmentNumber,"
-                    + " lcpd.loan_charge_id as chargeId, 
lcpd.loan_transaction_id as transactionId, " + " c.name as chargeName"
-                    + " from m_loan_charge_paid_by lcpd" + " join 
m_loan_charge lc on lc.id=lcpd.loan_charge_Id"
-                    + " join m_charge c on c.id=lc.charge_id";
-        }
-
-        @Override
-        public LoanChargePaidByData mapRow(ResultSet rs, 
@SuppressWarnings("unused") int rowNum) throws SQLException {
-            final Long id = rs.getLong("id");
-            final BigDecimal amount = rs.getBigDecimal("amount");
-            final Integer installmentNumber = rs.getInt("installmentNumber");
-            final Long chargeId = rs.getLong("chargeId");
-            final Long transactionId = rs.getLong("transactionId");
-            final String chargeName = rs.getString("chargeName");
-            return new LoanChargePaidByData(id, amount, installmentNumber, 
chargeId, transactionId, chargeName);
-        }
-
-    }
-
-}
diff --git 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanChargePaidByReadService.java
 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanChargePaidByReadService.java
new file mode 100644
index 000000000..5e885251a
--- /dev/null
+++ 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanChargePaidByReadService.java
@@ -0,0 +1,76 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.loanaccount.service;
+
+import jakarta.persistence.EntityManager;
+import jakarta.persistence.TypedQuery;
+import jakarta.persistence.criteria.CriteriaBuilder;
+import jakarta.persistence.criteria.CriteriaQuery;
+import jakarta.persistence.criteria.JoinType;
+import jakarta.persistence.criteria.Order;
+import jakarta.persistence.criteria.Path;
+import jakarta.persistence.criteria.Root;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import lombok.RequiredArgsConstructor;
+import org.apache.fineract.portfolio.loanaccount.data.LoanChargePaidByData;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanChargePaidBy;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
+import org.apache.fineract.portfolio.loanaccount.mapper.LoanChargePaidByMapper;
+import org.springframework.stereotype.Component;
+import org.springframework.transaction.annotation.Transactional;
+
+@Component
+@Transactional(readOnly = true)
+@RequiredArgsConstructor
+public class LoanChargePaidByReadService {
+
+    private final EntityManager entityManager;
+    private final LoanChargePaidByMapper loanChargePaidByMapper;
+
+    public List<LoanChargePaidByData> 
fetchLoanChargesPaidByDataTransactionId(Long transactionId) {
+        final List<Long> transactionIds = Arrays.asList(transactionId);
+        return 
fetchLoanChargesPaidByTransactionId(transactionIds).stream().map(loanChargePaidByMapper::map).toList();
+    }
+
+    public List<LoanChargePaidByData> 
fetchLoanChargesPaidByDataTransactionId(final List<Long> transactionIds) {
+        return 
fetchLoanChargesPaidByTransactionId(transactionIds).stream().map(loanChargePaidByMapper::map).toList();
+    }
+
+    public List<LoanChargePaidBy> fetchLoanChargesPaidByTransactionId(final 
List<Long> transactionIds) {
+
+        final CriteriaBuilder cb = entityManager.getCriteriaBuilder();
+        final CriteriaQuery<LoanChargePaidBy> query = 
cb.createQuery(LoanChargePaidBy.class);
+
+        final Root<LoanChargePaidBy> root = query.from(LoanChargePaidBy.class);
+        root.fetch("loanTransaction", JoinType.INNER);
+        final Path<LoanTransaction> loanTransaction = 
root.join("loanTransaction", JoinType.INNER);
+
+        query.select(root).where(loanTransaction.get("id").in(transactionIds));
+
+        final List<Order> orders = new ArrayList<>();
+        orders.add(cb.desc(root.get("id")));
+        query.orderBy(orders);
+
+        final TypedQuery<LoanChargePaidBy> queryToExecute = 
entityManager.createQuery(query);
+        return queryToExecute.getResultList();
+    }
+
+}
diff --git 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanTransactionReadService.java
 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanTransactionReadService.java
new file mode 100644
index 000000000..926caba72
--- /dev/null
+++ 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanTransactionReadService.java
@@ -0,0 +1,79 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.loanaccount.service;
+
+import jakarta.persistence.EntityManager;
+import jakarta.persistence.TypedQuery;
+import jakarta.persistence.criteria.CriteriaBuilder;
+import jakarta.persistence.criteria.CriteriaQuery;
+import jakarta.persistence.criteria.JoinType;
+import jakarta.persistence.criteria.Order;
+import jakarta.persistence.criteria.Path;
+import jakarta.persistence.criteria.Predicate;
+import jakarta.persistence.criteria.Root;
+import java.util.ArrayList;
+import java.util.List;
+import lombok.RequiredArgsConstructor;
+import org.apache.fineract.portfolio.loanaccount.domain.Loan;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
+import org.springframework.stereotype.Component;
+import org.springframework.transaction.annotation.Transactional;
+
+@Component
+@Transactional(readOnly = true)
+@RequiredArgsConstructor
+public class LoanTransactionReadService {
+
+    private final EntityManager entityManager;
+
+    public List<LoanTransaction> fetchLoanTransactionsByType(final Long 
loanId, final String externalId, final Integer transactionType) {
+        final List<Integer> transactionTypes = new ArrayList<>();
+        transactionTypes.add(transactionType);
+        return fetchLoanTransactionsByTypes(loanId, externalId, 
transactionTypes);
+    }
+
+    public List<LoanTransaction> fetchLoanTransactionsByTypes(final Long 
loanId, final String externalId,
+            final List<Integer> transactionTypes) {
+
+        final CriteriaBuilder cb = entityManager.getCriteriaBuilder();
+        final CriteriaQuery<LoanTransaction> query = 
cb.createQuery(LoanTransaction.class);
+
+        final Root<LoanTransaction> root = query.from(LoanTransaction.class);
+        root.fetch("loan", JoinType.INNER);
+        final Path<Loan> loan = root.join("loan", JoinType.INNER);
+
+        Predicate loanPredicate = cb.equal(loan.get("id"), loanId);
+        if (externalId != null) {
+            loanPredicate = cb.equal(loan.get("externalId"), externalId);
+        }
+
+        query.select(root)
+                .where(cb.and(loanPredicate, 
root.get("typeOf").in(transactionTypes), cb.equal(root.get("reversed"), 
Boolean.FALSE)));
+
+        final List<Order> orders = new ArrayList<>();
+        orders.add(cb.desc(root.get("dateOf")));
+        orders.add(cb.desc(root.get("createdDate")));
+        orders.add(cb.desc(root.get("id")));
+        query.orderBy(orders);
+
+        final TypedQuery<LoanTransaction> queryToExecute = 
entityManager.createQuery(query);
+        return queryToExecute.getResultList();
+    }
+
+}
diff --git 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanTransactionRelationReadService.java
 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanTransactionRelationReadService.java
new file mode 100644
index 000000000..9a4ad1727
--- /dev/null
+++ 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanTransactionRelationReadService.java
@@ -0,0 +1,76 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.loanaccount.service;
+
+import jakarta.persistence.EntityManager;
+import jakarta.persistence.TypedQuery;
+import jakarta.persistence.criteria.CriteriaBuilder;
+import jakarta.persistence.criteria.CriteriaQuery;
+import jakarta.persistence.criteria.JoinType;
+import jakarta.persistence.criteria.Order;
+import jakarta.persistence.criteria.Path;
+import jakarta.persistence.criteria.Root;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import lombok.RequiredArgsConstructor;
+import 
org.apache.fineract.portfolio.loanaccount.data.LoanTransactionRelationData;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
+import 
org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRelation;
+import 
org.apache.fineract.portfolio.loanaccount.mapper.LoanTransactionRelationMapper;
+import org.springframework.stereotype.Component;
+import org.springframework.transaction.annotation.Transactional;
+
+@Component
+@Transactional(readOnly = true)
+@RequiredArgsConstructor
+public class LoanTransactionRelationReadService {
+
+    private final EntityManager entityManager;
+    private final LoanTransactionRelationMapper loanTransactionRelationMapper;
+
+    public List<LoanTransactionRelationData> 
fetchLoanTransactionRelationDataFrom(final Long transactionId) {
+        final List<Long> transactionIds = Arrays.asList(transactionId);
+        return 
fetchLoanTransactionRelationFrom(transactionIds).stream().map(loanTransactionRelationMapper::map).toList();
+    }
+
+    public List<LoanTransactionRelationData> 
fetchLoanTransactionRelationDataFrom(final List<Long> transactionIds) {
+        return 
fetchLoanTransactionRelationFrom(transactionIds).stream().map(loanTransactionRelationMapper::map).toList();
+    }
+
+    public List<LoanTransactionRelation> 
fetchLoanTransactionRelationFrom(final List<Long> transactionIds) {
+
+        final CriteriaBuilder cb = entityManager.getCriteriaBuilder();
+        final CriteriaQuery<LoanTransactionRelation> query = 
cb.createQuery(LoanTransactionRelation.class);
+
+        final Root<LoanTransactionRelation> root = 
query.from(LoanTransactionRelation.class);
+        root.fetch("fromTransaction", JoinType.INNER);
+        final Path<LoanTransaction> fromTransaction = 
root.join("fromTransaction", JoinType.INNER);
+
+        query.select(root).where(fromTransaction.get("id").in(transactionIds));
+
+        final List<Order> orders = new ArrayList<>();
+        orders.add(cb.desc(root.get("id")));
+        query.orderBy(orders);
+
+        final TypedQuery<LoanTransactionRelation> queryToExecute = 
entityManager.createQuery(query);
+        return queryToExecute.getResultList();
+    }
+
+}
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/event/external/service/serialization/serializer/loan/LoanAdjustTransactionBusinessEventSerializer.java
 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/event/external/service/serialization/serializer/loan/LoanAdjustTransactionBusinessEventSerializer.java
index 5c3465eab..8539d13cf 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/event/external/service/serialization/serializer/loan/LoanAdjustTransactionBusinessEventSerializer.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/event/external/service/serialization/serializer/loan/LoanAdjustTransactionBusinessEventSerializer.java
@@ -29,7 +29,7 @@ import 
org.apache.fineract.infrastructure.event.external.service.serialization.m
 import 
org.apache.fineract.infrastructure.event.external.service.serialization.serializer.BusinessEventSerializer;
 import org.apache.fineract.portfolio.loanaccount.data.LoanTransactionData;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
-import 
org.apache.fineract.portfolio.loanaccount.service.LoanChargePaidByReadPlatformService;
+import 
org.apache.fineract.portfolio.loanaccount.service.LoanChargePaidByReadService;
 import 
org.apache.fineract.portfolio.loanaccount.service.LoanReadPlatformService;
 import org.springframework.stereotype.Component;
 
@@ -39,7 +39,7 @@ public class LoanAdjustTransactionBusinessEventSerializer 
implements BusinessEve
 
     private final LoanReadPlatformService service;
     private final LoanTransactionDataMapper mapper;
-    private final LoanChargePaidByReadPlatformService 
loanChargePaidByReadPlatformService;
+    private final LoanChargePaidByReadService loanChargePaidByReadService;
 
     @Override
     public <T> boolean canSerialize(BusinessEvent<T> event) {
@@ -52,8 +52,8 @@ public class LoanAdjustTransactionBusinessEventSerializer 
implements BusinessEve
         LoanTransaction transactionToAdjust = 
event.get().getTransactionToAdjust();
         LoanTransactionData transactionToAdjustData = 
service.retrieveLoanTransaction(transactionToAdjust.getLoan().getId(),
                 transactionToAdjust.getId());
-        transactionToAdjustData.setLoanChargePaidByList(
-                
loanChargePaidByReadPlatformService.getLoanChargesPaidByTransactionId(transactionToAdjust.getId()));
+        transactionToAdjustData
+                
.setLoanChargePaidByList(loanChargePaidByReadService.fetchLoanChargesPaidByDataTransactionId(transactionToAdjust.getId()));
         LoanTransactionDataV1 transactionToAdjustAvroDto = 
mapper.map(transactionToAdjustData);
 
         LoanTransaction newTransactionDetail = 
event.get().getNewTransactionDetail();
@@ -62,7 +62,7 @@ public class LoanAdjustTransactionBusinessEventSerializer 
implements BusinessEve
             LoanTransactionData newTransactionDetailData = 
service.retrieveLoanTransaction(newTransactionDetail.getLoan().getId(),
                     newTransactionDetail.getId());
             newTransactionDetailData.setLoanChargePaidByList(
-                    
loanChargePaidByReadPlatformService.getLoanChargesPaidByTransactionId(newTransactionDetail.getId()));
+                    
loanChargePaidByReadService.fetchLoanChargesPaidByDataTransactionId(newTransactionDetail.getId()));
             newTransactionDetailAvroDto = mapper.map(newTransactionDetailData);
 
         }
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/event/external/service/serialization/serializer/loan/LoanChargeOffBusinessEventSerializer.java
 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/event/external/service/serialization/serializer/loan/LoanChargeOffBusinessEventSerializer.java
index 84c2a2777..2b8b70853 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/event/external/service/serialization/serializer/loan/LoanChargeOffBusinessEventSerializer.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/event/external/service/serialization/serializer/loan/LoanChargeOffBusinessEventSerializer.java
@@ -29,7 +29,7 @@ import 
org.apache.fineract.infrastructure.event.external.service.serialization.m
 import 
org.apache.fineract.infrastructure.event.external.service.serialization.mapper.loan.UnpaidChargeDataMapper;
 import org.apache.fineract.portfolio.loanaccount.data.UnpaidChargeData;
 import 
org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository;
-import 
org.apache.fineract.portfolio.loanaccount.service.LoanChargePaidByReadPlatformService;
+import 
org.apache.fineract.portfolio.loanaccount.service.LoanChargePaidByReadService;
 import 
org.apache.fineract.portfolio.loanaccount.service.LoanReadPlatformService;
 import org.springframework.core.Ordered;
 import org.springframework.core.annotation.Order;
@@ -43,9 +43,9 @@ public class LoanChargeOffBusinessEventSerializer extends 
LoanTransactionBusines
     private final LoanTransactionRepository loanTransactionRepository;
 
     public LoanChargeOffBusinessEventSerializer(LoanReadPlatformService 
loanReadPlatformService,
-            LoanTransactionDataMapper loanTransactionMapper, 
LoanChargePaidByReadPlatformService loanChargePaidByReadPlatformService,
+            LoanTransactionDataMapper loanTransactionMapper, 
LoanChargePaidByReadService loanChargePaidByReadService,
             UnpaidChargeDataMapper unpaidChargeDataMapper, 
LoanTransactionRepository loanTransactionRepository) {
-        super(loanReadPlatformService, loanTransactionMapper, 
loanChargePaidByReadPlatformService);
+        super(loanReadPlatformService, loanTransactionMapper, 
loanChargePaidByReadService);
         this.unpaidChargeDataMapper = unpaidChargeDataMapper;
         this.loanTransactionRepository = loanTransactionRepository;
     }
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/event/external/service/serialization/serializer/loan/LoanTransactionBusinessEventSerializer.java
 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/event/external/service/serialization/serializer/loan/LoanTransactionBusinessEventSerializer.java
index 5c1ac8b2e..9b5a1aea5 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/event/external/service/serialization/serializer/loan/LoanTransactionBusinessEventSerializer.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/event/external/service/serialization/serializer/loan/LoanTransactionBusinessEventSerializer.java
@@ -27,7 +27,7 @@ import 
org.apache.fineract.infrastructure.event.business.domain.loan.transaction
 import 
org.apache.fineract.infrastructure.event.external.service.serialization.mapper.loan.LoanTransactionDataMapper;
 import 
org.apache.fineract.infrastructure.event.external.service.serialization.serializer.BusinessEventSerializer;
 import org.apache.fineract.portfolio.loanaccount.data.LoanTransactionData;
-import 
org.apache.fineract.portfolio.loanaccount.service.LoanChargePaidByReadPlatformService;
+import 
org.apache.fineract.portfolio.loanaccount.service.LoanChargePaidByReadService;
 import 
org.apache.fineract.portfolio.loanaccount.service.LoanReadPlatformService;
 import org.springframework.stereotype.Component;
 
@@ -37,7 +37,7 @@ public class LoanTransactionBusinessEventSerializer 
implements BusinessEventSeri
 
     private final LoanReadPlatformService service;
     private final LoanTransactionDataMapper loanTransactionMapper;
-    private final LoanChargePaidByReadPlatformService 
loanChargePaidByReadPlatformService;
+    private final LoanChargePaidByReadService loanChargePaidByReadService;
 
     @Override
     public <T> boolean canSerialize(BusinessEvent<T> event) {
@@ -50,7 +50,7 @@ public class LoanTransactionBusinessEventSerializer 
implements BusinessEventSeri
         Long loanId = event.get().getLoan().getId();
         Long loanTransactionId = event.get().getId();
         LoanTransactionData transactionData = 
service.retrieveLoanTransaction(loanId, loanTransactionId);
-        
transactionData.setLoanChargePaidByList(loanChargePaidByReadPlatformService.getLoanChargesPaidByTransactionId(loanTransactionId));
+        
transactionData.setLoanChargePaidByList(loanChargePaidByReadService.fetchLoanChargesPaidByDataTransactionId(loanTransactionId));
         return loanTransactionMapper.map(transactionData);
     }
 
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanTransactionsApiResource.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanTransactionsApiResource.java
index 943f5ba54..4aa3e544c 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanTransactionsApiResource.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanTransactionsApiResource.java
@@ -66,7 +66,7 @@ import 
org.apache.fineract.portfolio.loanaccount.data.LoanTransactionData;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType;
 import 
org.apache.fineract.portfolio.loanaccount.exception.LoanNotFoundException;
 import 
org.apache.fineract.portfolio.loanaccount.exception.LoanTransactionNotFoundException;
-import 
org.apache.fineract.portfolio.loanaccount.service.LoanChargePaidByReadPlatformService;
+import 
org.apache.fineract.portfolio.loanaccount.service.LoanChargePaidByReadService;
 import 
org.apache.fineract.portfolio.loanaccount.service.LoanReadPlatformService;
 import org.apache.fineract.portfolio.paymenttype.data.PaymentTypeData;
 import 
org.apache.fineract.portfolio.paymenttype.service.PaymentTypeReadPlatformService;
@@ -96,7 +96,7 @@ public class LoanTransactionsApiResource {
     private final DefaultToApiJsonSerializer<LoanTransactionData> 
toApiJsonSerializer;
     private final PortfolioCommandSourceWritePlatformService 
commandsSourceWritePlatformService;
     private final PaymentTypeReadPlatformService 
paymentTypeReadPlatformService;
-    private final LoanChargePaidByReadPlatformService 
loanChargePaidByReadPlatformService;
+    private final LoanChargePaidByReadService loanChargePaidByReadService;
 
     @GET
     @Path("{loanId}/transactions/template")
@@ -428,8 +428,8 @@ public class LoanTransactionsApiResource {
 
         LoanTransactionData transactionData = 
this.loanReadPlatformService.retrieveLoanTransaction(resolvedLoanId,
                 resolvedLoanTransactionId);
-        transactionData.setLoanChargePaidByList(
-                
this.loanChargePaidByReadPlatformService.getLoanChargesPaidByTransactionId(transactionData.getId()));
+        transactionData
+                
.setLoanChargePaidByList(loanChargePaidByReadService.fetchLoanChargesPaidByDataTransactionId(transactionData.getId()));
         final ApiRequestJsonSerializationSettings settings = 
this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
         if (settings.isTemplate()) {
             final Collection<PaymentTypeData> paymentTypeOptions = 
this.paymentTypeReadPlatformService.retrieveAllPaymentTypes();
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoansApiResource.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoansApiResource.java
index d6b284a42..393ecbe7f 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoansApiResource.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoansApiResource.java
@@ -912,7 +912,6 @@ public class LoansApiResource {
 
         final Set<String> mandatoryResponseParameters = new HashSet<>();
         final Set<String> associationParameters = 
ApiParameterHelper.extractAssociationsForResponseIfProvided(uriInfo.getQueryParameters());
-        final Collection<LoanTransactionData> currentLoanRepayments = 
this.loanReadPlatformService.retrieveLoanTransactions(resolvedLoanId);
         if (!associationParameters.isEmpty()) {
             if 
(associationParameters.contains(DataTableApiConstant.allAssociateParamName)) {
                 
associationParameters.addAll(Arrays.asList(DataTableApiConstant.repaymentScheduleAssociateParamName,
@@ -935,9 +934,7 @@ public class LoansApiResource {
 
             if 
(associationParameters.contains(DataTableApiConstant.transactionsAssociateParamName))
 {
                 
mandatoryResponseParameters.add(DataTableApiConstant.transactionsAssociateParamName);
-                if (!CollectionUtils.isEmpty(currentLoanRepayments)) {
-                    loanRepayments = currentLoanRepayments;
-                }
+                loanRepayments = 
this.loanReadPlatformService.retrieveLoanTransactions(resolvedLoanId);
             }
 
             if 
(associationParameters.contains(DataTableApiConstant.multiDisburseDetailsAssociateParamName)
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformService.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformService.java
index 1b0c64e4d..278df26d6 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformService.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformService.java
@@ -34,7 +34,6 @@ import 
org.apache.fineract.portfolio.loanaccount.data.LoanRepaymentScheduleInsta
 import org.apache.fineract.portfolio.loanaccount.data.LoanScheduleAccrualData;
 import org.apache.fineract.portfolio.loanaccount.data.LoanTermVariationsData;
 import org.apache.fineract.portfolio.loanaccount.data.LoanTransactionData;
-import 
org.apache.fineract.portfolio.loanaccount.data.LoanTransactionRelationData;
 import org.apache.fineract.portfolio.loanaccount.data.PaidInAdvanceData;
 import 
org.apache.fineract.portfolio.loanaccount.data.RepaymentScheduleRelatedLoanData;
 import org.apache.fineract.portfolio.loanaccount.domain.Loan;
@@ -160,8 +159,6 @@ public interface LoanReadPlatformService {
 
     List<LoanRepaymentScheduleInstallmentData> getRepaymentDataResponse(Long 
loanId);
 
-    List<LoanTransactionRelationData> 
retrieveLoanTransactionRelationsByLoanTransactionId(Long loanTransactionId);
-
     Long retrieveLoanTransactionIdByExternalId(ExternalId externalId);
 
     Long retrieveLoanIdByExternalId(ExternalId externalId);
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java
index 2195253dd..b30c2e0a4 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java
@@ -38,6 +38,7 @@ import java.util.Map;
 import java.util.Objects;
 import java.util.Optional;
 import java.util.Set;
+import java.util.stream.Collectors;
 import lombok.AllArgsConstructor;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.fineract.accounting.common.AccountingRuleType;
@@ -93,6 +94,7 @@ import 
org.apache.fineract.portfolio.loanaccount.data.DisbursementData;
 import org.apache.fineract.portfolio.loanaccount.data.LoanAccountData;
 import 
org.apache.fineract.portfolio.loanaccount.data.LoanApplicationTimelineData;
 import org.apache.fineract.portfolio.loanaccount.data.LoanApprovalData;
+import org.apache.fineract.portfolio.loanaccount.data.LoanChargePaidByData;
 import 
org.apache.fineract.portfolio.loanaccount.data.LoanInterestRecalculationData;
 import 
org.apache.fineract.portfolio.loanaccount.data.LoanRepaymentScheduleInstallmentData;
 import org.apache.fineract.portfolio.loanaccount.data.LoanScheduleAccrualData;
@@ -113,8 +115,6 @@ import 
org.apache.fineract.portfolio.loanaccount.domain.LoanStatus;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanSubStatus;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanTermVariationType;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
-import 
org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRelation;
-import 
org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRelationRepository;
 import 
org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType;
 import 
org.apache.fineract.portfolio.loanaccount.exception.LoanNotFoundException;
@@ -124,7 +124,6 @@ import 
org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanScheduleP
 import 
org.apache.fineract.portfolio.loanaccount.loanschedule.data.OverdueLoanScheduleData;
 import 
org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleProcessingType;
 import 
org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleType;
-import 
org.apache.fineract.portfolio.loanaccount.mapper.LoanTransactionRelationMapper;
 import org.apache.fineract.portfolio.loanproduct.data.LoanProductData;
 import 
org.apache.fineract.portfolio.loanproduct.data.TransactionProcessingStrategyData;
 import org.apache.fineract.portfolio.loanproduct.domain.InterestMethod;
@@ -175,9 +174,8 @@ public class LoanReadPlatformServiceImpl implements 
LoanReadPlatformService, Loa
     private final DatabaseSpecificSQLGenerator sqlGenerator;
     private final DelinquencyReadPlatformService 
delinquencyReadPlatformService;
     private final LoanTransactionRepository loanTransactionRepository;
-    private final LoanTransactionRelationRepository 
loanTransactionRelationRepository;
-    private final LoanTransactionRelationMapper loanTransactionRelationMapper;
-    private final LoanChargePaidByReadPlatformService 
loanChargePaidByReadPlatformService;
+    private final LoanChargePaidByReadService loanChargePaidByReadService;
+    private final LoanTransactionRelationReadService 
loanTransactionRelationReadService;
 
     @Override
     public LoanAccountData retrieveOne(final Long loanId) {
@@ -286,11 +284,17 @@ public class LoanReadPlatformServiceImpl implements 
LoanReadPlatformService, Loa
             Collection<LoanTransactionData> loanTransactionData = 
this.jdbcTemplate.query(sql, rm, loanId); // NOSONAR
             // TODO: would worth to rework in the future. It is not nice to 
fetch relations one by one... might worth to
             // give a try to get rid of native queries
+            final List<Long> loanIds = 
loanTransactionData.stream().map(LoanTransactionData::getId).collect(Collectors.toList());
+            final List<LoanTransactionRelationData> 
loanTransactionRelationDatas = loanTransactionRelationReadService
+                    .fetchLoanTransactionRelationDataFrom(loanIds);
+            final List<LoanChargePaidByData> loanChargePaidByDatas = 
loanChargePaidByReadService
+                    .fetchLoanChargesPaidByDataTransactionId(loanIds);
             for (LoanTransactionData loanTransaction : loanTransactionData) {
-                loanTransaction
-                        
.setLoanTransactionRelations(this.retrieveLoanTransactionRelationsByLoanTransactionId(loanTransaction.getId()));
-                loanTransaction.setLoanChargePaidByList(
-                        
loanChargePaidByReadPlatformService.getLoanChargesPaidByTransactionId(loanTransaction.getId()));
+                
loanTransaction.setLoanTransactionRelations(loanTransactionRelationDatas.stream().filter(
+                        loanTransactionRelationData -> 
loanTransactionRelationData.getFromLoanTransaction().equals(loanTransaction.getId()))
+                        .toList());
+                
loanTransaction.setLoanChargePaidByList(loanChargePaidByDatas.stream()
+                        .filter(loanChargePaidByData -> 
loanChargePaidByData.getTransactionId().equals(loanTransaction.getId())).toList());
             }
             return loanTransactionData;
         } catch (final EmptyResultDataAccessException e) {
@@ -587,7 +591,8 @@ public class LoanReadPlatformServiceImpl implements 
LoanReadPlatformService, Loa
             final LoanTransactionsMapper rm = new 
LoanTransactionsMapper(sqlGenerator);
             final String sql = "select " + rm.loanPaymentsSchema() + " where 
l.id = ? and tr.id = ? ";
             LoanTransactionData loanTransactionData = 
this.jdbcTemplate.queryForObject(sql, rm, loanId, transactionId); // NOSONAR
-            
loanTransactionData.setLoanTransactionRelations(this.retrieveLoanTransactionRelationsByLoanTransactionId(transactionId));
+            loanTransactionData.setLoanTransactionRelations(
+                    
loanTransactionRelationReadService.fetchLoanTransactionRelationDataFrom(loanTransactionData.getId()));
             return loanTransactionData;
         } catch (final EmptyResultDataAccessException e) {
             throw new LoanTransactionNotFoundException(transactionId, e);
@@ -1224,7 +1229,6 @@ public class LoanReadPlatformServiceImpl implements 
LoanReadPlatformService, Loa
                 final LocalDate dueDate = JdbcSupport.getLocalDate(rs, 
"dueDate");
                 final LocalDate obligationsMetOnDate = 
JdbcSupport.getLocalDate(rs, "obligationsMetOnDate");
                 final boolean complete = rs.getBoolean("complete");
-                final boolean isAdditional = rs.getBoolean("isAdditional");
                 BigDecimal disbursedAmount = BigDecimal.ZERO;
 
                 disbursedAmount = processDisbursementData(loanScheduleType, 
disbursementData, fromDate, dueDate, disbursementPeriodIds,
@@ -2595,14 +2599,6 @@ public class LoanReadPlatformServiceImpl implements 
LoanReadPlatformService, Loa
         return this.jdbcTemplate.queryForObject(sql, Integer.class);
     }
 
-    @Override
-    public List<LoanTransactionRelationData> 
retrieveLoanTransactionRelationsByLoanTransactionId(Long loanTransactionId) {
-        final LoanTransaction loanTransaction = 
this.loanTransactionRepository.getReferenceById(loanTransactionId);
-        List<LoanTransactionRelation> loanTransactionRelations = 
this.loanTransactionRelationRepository
-                .findByFromTransaction(loanTransaction);
-        return loanTransactionRelationMapper.map(loanTransactionRelations);
-    }
-
     @Override
     public Long retrieveLoanTransactionIdByExternalId(ExternalId externalId) {
         return loanTransactionRepository.findIdByExternalId(externalId);
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 468547166..157daff3c 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
@@ -82,7 +82,6 @@ import 
org.apache.fineract.portfolio.loanaccount.loanschedule.service.LoanSchedu
 import 
org.apache.fineract.portfolio.loanaccount.loanschedule.service.LoanScheduleHistoryWritePlatformService;
 import org.apache.fineract.portfolio.loanaccount.mapper.LoanChargeMapper;
 import 
org.apache.fineract.portfolio.loanaccount.mapper.LoanCollateralManagementMapper;
-import 
org.apache.fineract.portfolio.loanaccount.mapper.LoanTransactionRelationMapper;
 import 
org.apache.fineract.portfolio.loanaccount.serialization.LoanApplicationTransitionValidator;
 import 
org.apache.fineract.portfolio.loanaccount.serialization.LoanApplicationValidator;
 import 
org.apache.fineract.portfolio.loanaccount.serialization.LoanChargeApiJsonValidator;
@@ -105,8 +104,7 @@ import 
org.apache.fineract.portfolio.loanaccount.service.LoanArrearsAgingService
 import org.apache.fineract.portfolio.loanaccount.service.LoanAssembler;
 import 
org.apache.fineract.portfolio.loanaccount.service.LoanCalculateRepaymentPastDueService;
 import org.apache.fineract.portfolio.loanaccount.service.LoanChargeAssembler;
-import 
org.apache.fineract.portfolio.loanaccount.service.LoanChargePaidByReadPlatformService;
-import 
org.apache.fineract.portfolio.loanaccount.service.LoanChargePaidByReadPlatformServiceImpl;
+import 
org.apache.fineract.portfolio.loanaccount.service.LoanChargePaidByReadService;
 import 
org.apache.fineract.portfolio.loanaccount.service.LoanChargeReadPlatformService;
 import 
org.apache.fineract.portfolio.loanaccount.service.LoanChargeReadPlatformServiceImpl;
 import 
org.apache.fineract.portfolio.loanaccount.service.LoanChargeWritePlatformService;
@@ -119,6 +117,7 @@ import 
org.apache.fineract.portfolio.loanaccount.service.LoanReadPlatformService
 import 
org.apache.fineract.portfolio.loanaccount.service.LoanStatusChangePlatformService;
 import 
org.apache.fineract.portfolio.loanaccount.service.LoanStatusChangePlatformServiceImpl;
 import 
org.apache.fineract.portfolio.loanaccount.service.LoanTransactionAssembler;
+import 
org.apache.fineract.portfolio.loanaccount.service.LoanTransactionRelationReadService;
 import org.apache.fineract.portfolio.loanaccount.service.LoanUtilService;
 import 
org.apache.fineract.portfolio.loanaccount.service.LoanWritePlatformService;
 import 
org.apache.fineract.portfolio.loanaccount.service.LoanWritePlatformServiceJpaRepositoryImpl;
@@ -252,13 +251,6 @@ public class LoanAccountConfiguration {
         return new LoanChargeAssembler(fromApiJsonHelper, chargeRepository, 
loanChargeRepository, loanProductRepository, externalIdFactory);
     }
 
-    @Bean
-    @ConditionalOnMissingBean(LoanChargePaidByReadPlatformService.class)
-    public LoanChargePaidByReadPlatformService 
loanChargePaidByReadPlatformService(JdbcTemplate jdbcTemplate,
-            PlatformSecurityContext context) {
-        return new LoanChargePaidByReadPlatformServiceImpl(jdbcTemplate, 
context);
-    }
-
     @Bean
     @ConditionalOnMissingBean(LoanChargeReadPlatformService.class)
     public LoanChargeReadPlatformService 
loanChargeReadPlatformService(JdbcTemplate jdbcTemplate,
@@ -312,19 +304,15 @@ public class LoanAccountConfiguration {
             ConfigurationDomainService configurationDomainService, 
AccountDetailsReadPlatformService accountDetailsReadPlatformService,
             ColumnValidator columnValidator, DatabaseSpecificSQLGenerator 
sqlGenerator,
             DelinquencyReadPlatformService delinquencyReadPlatformService, 
LoanTransactionRepository loanTransactionRepository,
-            LoanTransactionRelationRepository 
loanTransactionRelationRepository,
-            LoanTransactionRelationMapper loanTransactionRelationMapper,
-            LoanChargePaidByReadPlatformService 
loanChargePaidByReadPlatformService) {
+            LoanChargePaidByReadService loanChargePaidByReadService,
+            LoanTransactionRelationReadService 
loanTransactionRelationReadService) {
         return new LoanReadPlatformServiceImpl(jdbcTemplate, context, 
loanRepositoryWrapper, applicationCurrencyRepository,
                 loanProductReadPlatformService, clientReadPlatformService, 
groupReadPlatformService, loanDropdownReadPlatformService,
                 fundReadPlatformService, chargeReadPlatformService, 
codeValueReadPlatformService, calendarReadPlatformService,
                 staffReadPlatformService, paginationHelper, 
namedParameterJdbcTemplate, paymentTypeReadPlatformService,
                 loanRepaymentScheduleTransactionProcessorFactory, 
floatingRatesReadPlatformService, loanUtilService,
                 configurationDomainService, accountDetailsReadPlatformService, 
columnValidator, sqlGenerator,
-                delinquencyReadPlatformService, loanTransactionRepository, 
loanTransactionRelationRepository, loanTransactionRelationMapper,
-                loanChargePaidByReadPlatformService
-
-        );
+                delinquencyReadPlatformService, loanTransactionRepository, 
loanChargePaidByReadService, loanTransactionRelationReadService);
     }
 
     @Bean
diff --git 
a/fineract-provider/src/test/java/org/apache/fineract/infrastructure/event/external/service/serialization/serializer/loan/LoanAdjustTransactionBusinessEventSerializerTest.java
 
b/fineract-provider/src/test/java/org/apache/fineract/infrastructure/event/external/service/serialization/serializer/loan/LoanAdjustTransactionBusinessEventSerializerTest.java
index 9bb321ec9..af691bf31 100644
--- 
a/fineract-provider/src/test/java/org/apache/fineract/infrastructure/event/external/service/serialization/serializer/loan/LoanAdjustTransactionBusinessEventSerializerTest.java
+++ 
b/fineract-provider/src/test/java/org/apache/fineract/infrastructure/event/external/service/serialization/serializer/loan/LoanAdjustTransactionBusinessEventSerializerTest.java
@@ -44,7 +44,7 @@ import 
org.apache.fineract.infrastructure.event.external.service.serialization.m
 import org.apache.fineract.portfolio.loanaccount.data.LoanTransactionData;
 import org.apache.fineract.portfolio.loanaccount.domain.Loan;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
-import 
org.apache.fineract.portfolio.loanaccount.service.LoanChargePaidByReadPlatformService;
+import 
org.apache.fineract.portfolio.loanaccount.service.LoanChargePaidByReadService;
 import 
org.apache.fineract.portfolio.loanaccount.service.LoanReadPlatformService;
 import org.apache.fineract.portfolio.loanproduct.service.LoanEnumerations;
 import org.junit.jupiter.api.AfterEach;
@@ -65,7 +65,7 @@ public class LoanAdjustTransactionBusinessEventSerializerTest 
{
     @Mock
     private LoanReadPlatformService service;
     @Mock
-    private LoanChargePaidByReadPlatformService 
loanChargePaidByReadPlatformService;
+    private LoanChargePaidByReadService loanChargePaidByReadService;
     @Mock
     private AvroDateTimeMapper avroDateTimeMapper;
     @Mock
@@ -88,7 +88,7 @@ public class LoanAdjustTransactionBusinessEventSerializerTest 
{
     void loanTransactionReversedOnDateSerializationTest() {
         Loan loanForProcessing = Mockito.mock(Loan.class);
         LoanAdjustTransactionBusinessEventSerializer serializer = new 
LoanAdjustTransactionBusinessEventSerializer(service,
-                new LoanTransactionDataMapperImpl(avroDateTimeMapper, 
externalIdMapper), loanChargePaidByReadPlatformService);
+                new LoanTransactionDataMapperImpl(avroDateTimeMapper, 
externalIdMapper), loanChargePaidByReadService);
         LoanTransaction transactionToAdjust = 
Mockito.mock(LoanTransaction.class);
         LoanAdjustTransactionBusinessEvent.Data 
loanAdjustTransactionBusinessEventData = new 
LoanAdjustTransactionBusinessEvent.Data(
                 transactionToAdjust);
@@ -103,7 +103,7 @@ public class 
LoanAdjustTransactionBusinessEventSerializerTest {
                 true, new ExternalId("testReversalExternalId"), 
reversedOnDate, 1L, new ExternalId("testExternalLoanId"));
 
         when(service.retrieveLoanTransaction(anyLong(), 
anyLong())).thenReturn(transactionToAdjustData);
-        
when(loanChargePaidByReadPlatformService.getLoanChargesPaidByTransactionId(anyLong())).thenReturn(new
 ArrayList<>());
+        
when(loanChargePaidByReadService.fetchLoanChargesPaidByDataTransactionId(anyLong())).thenReturn(new
 ArrayList<>());
         when(transactionToAdjust.getLoan()).thenReturn(loanForProcessing);
         when(loanForProcessing.getId()).thenReturn(1L);
         when(transactionToAdjust.getId()).thenReturn(1L);
diff --git 
a/fineract-provider/src/test/java/org/apache/fineract/portfolio/deliquency/LoanDelinquencyDomainServiceTest.java
 
b/fineract-provider/src/test/java/org/apache/fineract/portfolio/deliquency/LoanDelinquencyDomainServiceTest.java
index dcd3dd42f..e4da2691a 100644
--- 
a/fineract-provider/src/test/java/org/apache/fineract/portfolio/deliquency/LoanDelinquencyDomainServiceTest.java
+++ 
b/fineract-provider/src/test/java/org/apache/fineract/portfolio/deliquency/LoanDelinquencyDomainServiceTest.java
@@ -32,7 +32,6 @@ import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
-import java.util.function.Predicate;
 import org.apache.fineract.infrastructure.businessdate.domain.BusinessDateType;
 import 
org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService;
 import org.apache.fineract.infrastructure.core.domain.ActionContext;
@@ -52,6 +51,8 @@ import 
org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleIns
 import org.apache.fineract.portfolio.loanaccount.domain.LoanStatus;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
 import 
org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionToRepaymentScheduleMapping;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType;
+import 
org.apache.fineract.portfolio.loanaccount.service.LoanTransactionReadService;
 import org.apache.fineract.portfolio.loanproduct.domain.LoanProduct;
 import 
org.apache.fineract.portfolio.loanproduct.domain.LoanProductRelatedDetail;
 import org.apache.fineract.portfolio.paymentdetail.domain.PaymentDetail;
@@ -82,6 +83,8 @@ public class LoanDelinquencyDomainServiceTest {
     private DelinquencyEffectivePauseHelper delinquencyEffectivePauseHelper;
     @InjectMocks
     private LoanDelinquencyDomainServiceImpl underTest;
+    @Mock
+    private LoanTransactionReadService loanTransactionReadService;
 
     private LocalDate businessDate;
     private MonetaryCurrency currency;
@@ -149,7 +152,8 @@ public class LoanDelinquencyDomainServiceTest {
         when(loanProductRelatedDetail.getGraceOnArrearsAgeing()).thenReturn(0);
         
when(loan.getLoanProductRelatedDetail()).thenReturn(loanProductRelatedDetail);
         
when(loan.getRepaymentScheduleInstallments()).thenReturn(repaymentScheduleInstallments);
-        
when(loan.getLoanTransactions(Mockito.any(Predicate.class))).thenReturn(Collections.emptyList());
+        
when(loanTransactionReadService.fetchLoanTransactionsByType(loan.getId(), null, 
LoanTransactionType.CHARGEBACK.getValue()))
+                .thenReturn(Collections.emptyList());
         
when(loan.getLastLoanRepaymentScheduleInstallment()).thenReturn(repaymentScheduleInstallments.get(0));
         when(loan.getCurrency()).thenReturn(currency);
         when(loan.getStatus()).thenReturn(LoanStatus.ACTIVE);
@@ -217,7 +221,8 @@ public class LoanDelinquencyDomainServiceTest {
         when(loanProductRelatedDetail.getGraceOnArrearsAgeing()).thenReturn(0);
         
when(loan.getLoanProductRelatedDetail()).thenReturn(loanProductRelatedDetail);
         
when(loan.getRepaymentScheduleInstallments()).thenReturn(repaymentScheduleInstallments);
-        
when(loan.getLoanTransactions(Mockito.any(Predicate.class))).thenReturn(Collections.emptyList());
+        
when(loanTransactionReadService.fetchLoanTransactionsByType(loan.getId(), null, 
LoanTransactionType.CHARGEBACK.getValue()))
+                .thenReturn(Collections.emptyList());
         
when(loan.getLastLoanRepaymentScheduleInstallment()).thenReturn(repaymentScheduleInstallments.get(0));
         when(loan.getCurrency()).thenReturn(currency);
         when(loan.isEnableInstallmentLevelDelinquency()).thenReturn(true);
@@ -272,7 +277,8 @@ public class LoanDelinquencyDomainServiceTest {
         when(loan.isEnableInstallmentLevelDelinquency()).thenReturn(true);
         when(loan.getCurrency()).thenReturn(currency);
         when(loan.getStatus()).thenReturn(LoanStatus.ACTIVE);
-        
when(loan.getLoanTransactions(Mockito.any(Predicate.class))).thenReturn(Arrays.asList(loanTransaction));
+        
when(loanTransactionReadService.fetchLoanTransactionsByType(loan.getId(), null, 
LoanTransactionType.CHARGEBACK.getValue()))
+                .thenReturn(Arrays.asList(loanTransaction));
         
when(delinquencyEffectivePauseHelper.getPausedDaysBeforeDate(effectiveDelinquencyList,
 businessDate)).thenReturn(0L);
 
         LoanDelinquencyData collectionData = 
underTest.getLoanDelinquencyData(loan, effectiveDelinquencyList);
diff --git 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/BaseLoanIntegrationTest.java
 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/BaseLoanIntegrationTest.java
index 359be4c55..94cb0e7ba 100644
--- 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/BaseLoanIntegrationTest.java
+++ 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/BaseLoanIntegrationTest.java
@@ -390,7 +390,7 @@ public abstract class BaseLoanIntegrationTest {
     protected void verifyTransactions(Long loanId, Transaction... 
transactions) {
         GetLoansLoanIdResponse loanDetails = 
loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId.intValue());
         if (transactions == null || transactions.length == 0) {
-            assertNull(loanDetails.getTransactions(), "No transaction is 
expected");
+            Assertions.assertTrue(loanDetails.getTransactions().isEmpty(), "No 
transaction is expected");
         } else {
             Assertions.assertEquals(transactions.length, 
loanDetails.getTransactions().size());
             Arrays.stream(transactions).forEach(tr -> {

Reply via email to