FINERACT-2111: Performance issue on Retrieve Loan API - Transactions

This commit is contained in:
Jose Alberto Hernandez
2024-08-02 22:48:01 +02:00
committed by Adam Saghy
parent 141e26fda7
commit bd19f28cb6
21 changed files with 331 additions and 154 deletions
@@ -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.Loan;
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment; import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction; 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; import org.springframework.transaction.annotation.Transactional;
@Slf4j @Slf4j
@@ -42,6 +44,7 @@ import org.springframework.transaction.annotation.Transactional;
public class LoanDelinquencyDomainServiceImpl implements LoanDelinquencyDomainService { public class LoanDelinquencyDomainServiceImpl implements LoanDelinquencyDomainService {
private final DelinquencyEffectivePauseHelper delinquencyEffectivePauseHelper; private final DelinquencyEffectivePauseHelper delinquencyEffectivePauseHelper;
private final LoanTransactionReadService loanTransactionReadService;
@Override @Override
@Transactional(readOnly = true) @Transactional(readOnly = true)
@@ -262,7 +265,8 @@ public class LoanDelinquencyDomainServiceImpl implements LoanDelinquencyDomainSe
final LoanRepaymentScheduleInstallment installment) { final LoanRepaymentScheduleInstallment installment) {
final MonetaryCurrency loanCurrency = loan.getCurrency(); final MonetaryCurrency loanCurrency = loan.getCurrency();
LoanRepaymentScheduleInstallment latestInstallment = loan.getLastLoanRepaymentScheduleInstallment(); 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; LocalDate overdueSinceDate = null;
CollectionData collectionData = CollectionData.template(); CollectionData collectionData = CollectionData.template();
BigDecimal outstandingAmount = BigDecimal.ZERO; BigDecimal outstandingAmount = BigDecimal.ZERO;
@@ -319,7 +323,8 @@ public class LoanDelinquencyDomainServiceImpl implements LoanDelinquencyDomainSe
BigDecimal delinquentFee = BigDecimal.ZERO; BigDecimal delinquentFee = BigDecimal.ZERO;
BigDecimal delinquentPenalty = 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(); BigDecimal amountAvailable = installment.getTotalPaid(loanCurrency).getAmount();
for (LoanTransaction loanTransaction : chargebackTransactions) { for (LoanTransaction loanTransaction : chargebackTransactions) {
@@ -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.delinquency.validator.DelinquencyRangeParseAndValidator;
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepository; import org.apache.fineract.portfolio.loanaccount.domain.LoanRepository;
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepositoryWrapper; 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.apache.fineract.portfolio.loanproduct.domain.LoanProductRepository;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
@@ -88,7 +89,8 @@ public class DelinquencyConfiguration {
@Bean @Bean
@ConditionalOnMissingBean(LoanDelinquencyDomainService.class) @ConditionalOnMissingBean(LoanDelinquencyDomainService.class)
public LoanDelinquencyDomainService loanDelinquencyDomainService(DelinquencyEffectivePauseHelper delinquencyEffectivePauseHelper) { public LoanDelinquencyDomainService loanDelinquencyDomainService(DelinquencyEffectivePauseHelper delinquencyEffectivePauseHelper,
return new LoanDelinquencyDomainServiceImpl(delinquencyEffectivePauseHelper); LoanTransactionReadService loanTransactionReadService) {
return new LoanDelinquencyDomainServiceImpl(delinquencyEffectivePauseHelper, loanTransactionReadService);
} }
} }
@@ -19,19 +19,30 @@
package org.apache.fineract.portfolio.loanaccount.data; package org.apache.fineract.portfolio.loanaccount.data;
import java.math.BigDecimal; import java.math.BigDecimal;
import lombok.Data;
import lombok.Getter; import lombok.Getter;
import lombok.RequiredArgsConstructor; import org.springframework.integration.annotation.Default;
@Data
@Getter @Getter
@RequiredArgsConstructor
public class LoanChargePaidByData { public class LoanChargePaidByData {
private final Long id; private Long id;
private final BigDecimal amount; private BigDecimal amount;
private final Integer installmentNumber; private Integer installmentNumber;
private final Long chargeId; private Long chargeId;
private final Long transactionId; private Long transactionId;
private final String name; 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, public LoanChargePaidByData(final Long id, final BigDecimal amount, final Integer installmentNumber, final Long chargeId,
final Long transactionId) { final Long transactionId) {
@@ -381,4 +381,8 @@ public class LoanTransactionData {
public void setLoanTransactionRelations(List<LoanTransactionRelationData> transactionRelations) { public void setLoanTransactionRelations(List<LoanTransactionRelationData> transactionRelations) {
this.transactionRelations = transactionRelations; this.transactionRelations = transactionRelations;
} }
public boolean supportTransactionRelations() {
return !type.isAccrual();
}
} }
@@ -18,13 +18,10 @@
*/ */
package org.apache.fineract.portfolio.loanaccount.domain; package org.apache.fineract.portfolio.loanaccount.domain;
import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
public interface LoanTransactionRelationRepository public interface LoanTransactionRelationRepository
extends JpaRepository<LoanTransactionRelation, Long>, JpaSpecificationExecutor<LoanTransactionRelation> { extends JpaRepository<LoanTransactionRelation, Long>, JpaSpecificationExecutor<LoanTransactionRelation> {
List<LoanTransactionRelation> findByFromTransaction(LoanTransaction fromTransaction);
} }
@@ -16,12 +16,23 @@
* specific language governing permissions and limitations * specific language governing permissions and limitations
* under the License. * under the License.
*/ */
package org.apache.fineract.portfolio.loanaccount.service; package org.apache.fineract.portfolio.loanaccount.mapper;
import java.util.List; 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.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);
} }
@@ -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);
}
}
}
@@ -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();
}
}
@@ -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();
}
}
@@ -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();
}
}
@@ -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.infrastructure.event.external.service.serialization.serializer.BusinessEventSerializer;
import org.apache.fineract.portfolio.loanaccount.data.LoanTransactionData; import org.apache.fineract.portfolio.loanaccount.data.LoanTransactionData;
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction; 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.loanaccount.service.LoanReadPlatformService;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@@ -39,7 +39,7 @@ public class LoanAdjustTransactionBusinessEventSerializer implements BusinessEve
private final LoanReadPlatformService service; private final LoanReadPlatformService service;
private final LoanTransactionDataMapper mapper; private final LoanTransactionDataMapper mapper;
private final LoanChargePaidByReadPlatformService loanChargePaidByReadPlatformService; private final LoanChargePaidByReadService loanChargePaidByReadService;
@Override @Override
public <T> boolean canSerialize(BusinessEvent<T> event) { public <T> boolean canSerialize(BusinessEvent<T> event) {
@@ -52,8 +52,8 @@ public class LoanAdjustTransactionBusinessEventSerializer implements BusinessEve
LoanTransaction transactionToAdjust = event.get().getTransactionToAdjust(); LoanTransaction transactionToAdjust = event.get().getTransactionToAdjust();
LoanTransactionData transactionToAdjustData = service.retrieveLoanTransaction(transactionToAdjust.getLoan().getId(), LoanTransactionData transactionToAdjustData = service.retrieveLoanTransaction(transactionToAdjust.getLoan().getId(),
transactionToAdjust.getId()); transactionToAdjust.getId());
transactionToAdjustData.setLoanChargePaidByList( transactionToAdjustData
loanChargePaidByReadPlatformService.getLoanChargesPaidByTransactionId(transactionToAdjust.getId())); .setLoanChargePaidByList(loanChargePaidByReadService.fetchLoanChargesPaidByDataTransactionId(transactionToAdjust.getId()));
LoanTransactionDataV1 transactionToAdjustAvroDto = mapper.map(transactionToAdjustData); LoanTransactionDataV1 transactionToAdjustAvroDto = mapper.map(transactionToAdjustData);
LoanTransaction newTransactionDetail = event.get().getNewTransactionDetail(); LoanTransaction newTransactionDetail = event.get().getNewTransactionDetail();
@@ -62,7 +62,7 @@ public class LoanAdjustTransactionBusinessEventSerializer implements BusinessEve
LoanTransactionData newTransactionDetailData = service.retrieveLoanTransaction(newTransactionDetail.getLoan().getId(), LoanTransactionData newTransactionDetailData = service.retrieveLoanTransaction(newTransactionDetail.getLoan().getId(),
newTransactionDetail.getId()); newTransactionDetail.getId());
newTransactionDetailData.setLoanChargePaidByList( newTransactionDetailData.setLoanChargePaidByList(
loanChargePaidByReadPlatformService.getLoanChargesPaidByTransactionId(newTransactionDetail.getId())); loanChargePaidByReadService.fetchLoanChargesPaidByDataTransactionId(newTransactionDetail.getId()));
newTransactionDetailAvroDto = mapper.map(newTransactionDetailData); newTransactionDetailAvroDto = mapper.map(newTransactionDetailData);
} }
@@ -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.infrastructure.event.external.service.serialization.mapper.loan.UnpaidChargeDataMapper;
import org.apache.fineract.portfolio.loanaccount.data.UnpaidChargeData; import org.apache.fineract.portfolio.loanaccount.data.UnpaidChargeData;
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository; 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.apache.fineract.portfolio.loanaccount.service.LoanReadPlatformService;
import org.springframework.core.Ordered; import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order; import org.springframework.core.annotation.Order;
@@ -43,9 +43,9 @@ public class LoanChargeOffBusinessEventSerializer extends LoanTransactionBusines
private final LoanTransactionRepository loanTransactionRepository; private final LoanTransactionRepository loanTransactionRepository;
public LoanChargeOffBusinessEventSerializer(LoanReadPlatformService loanReadPlatformService, public LoanChargeOffBusinessEventSerializer(LoanReadPlatformService loanReadPlatformService,
LoanTransactionDataMapper loanTransactionMapper, LoanChargePaidByReadPlatformService loanChargePaidByReadPlatformService, LoanTransactionDataMapper loanTransactionMapper, LoanChargePaidByReadService loanChargePaidByReadService,
UnpaidChargeDataMapper unpaidChargeDataMapper, LoanTransactionRepository loanTransactionRepository) { UnpaidChargeDataMapper unpaidChargeDataMapper, LoanTransactionRepository loanTransactionRepository) {
super(loanReadPlatformService, loanTransactionMapper, loanChargePaidByReadPlatformService); super(loanReadPlatformService, loanTransactionMapper, loanChargePaidByReadService);
this.unpaidChargeDataMapper = unpaidChargeDataMapper; this.unpaidChargeDataMapper = unpaidChargeDataMapper;
this.loanTransactionRepository = loanTransactionRepository; this.loanTransactionRepository = loanTransactionRepository;
} }
@@ -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.mapper.loan.LoanTransactionDataMapper;
import org.apache.fineract.infrastructure.event.external.service.serialization.serializer.BusinessEventSerializer; 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.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.apache.fineract.portfolio.loanaccount.service.LoanReadPlatformService;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@@ -37,7 +37,7 @@ public class LoanTransactionBusinessEventSerializer implements BusinessEventSeri
private final LoanReadPlatformService service; private final LoanReadPlatformService service;
private final LoanTransactionDataMapper loanTransactionMapper; private final LoanTransactionDataMapper loanTransactionMapper;
private final LoanChargePaidByReadPlatformService loanChargePaidByReadPlatformService; private final LoanChargePaidByReadService loanChargePaidByReadService;
@Override @Override
public <T> boolean canSerialize(BusinessEvent<T> event) { public <T> boolean canSerialize(BusinessEvent<T> event) {
@@ -50,7 +50,7 @@ public class LoanTransactionBusinessEventSerializer implements BusinessEventSeri
Long loanId = event.get().getLoan().getId(); Long loanId = event.get().getLoan().getId();
Long loanTransactionId = event.get().getId(); Long loanTransactionId = event.get().getId();
LoanTransactionData transactionData = service.retrieveLoanTransaction(loanId, loanTransactionId); LoanTransactionData transactionData = service.retrieveLoanTransaction(loanId, loanTransactionId);
transactionData.setLoanChargePaidByList(loanChargePaidByReadPlatformService.getLoanChargesPaidByTransactionId(loanTransactionId)); transactionData.setLoanChargePaidByList(loanChargePaidByReadService.fetchLoanChargesPaidByDataTransactionId(loanTransactionId));
return loanTransactionMapper.map(transactionData); return loanTransactionMapper.map(transactionData);
} }
@@ -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.domain.LoanTransactionType;
import org.apache.fineract.portfolio.loanaccount.exception.LoanNotFoundException; import org.apache.fineract.portfolio.loanaccount.exception.LoanNotFoundException;
import org.apache.fineract.portfolio.loanaccount.exception.LoanTransactionNotFoundException; 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.loanaccount.service.LoanReadPlatformService;
import org.apache.fineract.portfolio.paymenttype.data.PaymentTypeData; import org.apache.fineract.portfolio.paymenttype.data.PaymentTypeData;
import org.apache.fineract.portfolio.paymenttype.service.PaymentTypeReadPlatformService; import org.apache.fineract.portfolio.paymenttype.service.PaymentTypeReadPlatformService;
@@ -96,7 +96,7 @@ public class LoanTransactionsApiResource {
private final DefaultToApiJsonSerializer<LoanTransactionData> toApiJsonSerializer; private final DefaultToApiJsonSerializer<LoanTransactionData> toApiJsonSerializer;
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService; private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
private final PaymentTypeReadPlatformService paymentTypeReadPlatformService; private final PaymentTypeReadPlatformService paymentTypeReadPlatformService;
private final LoanChargePaidByReadPlatformService loanChargePaidByReadPlatformService; private final LoanChargePaidByReadService loanChargePaidByReadService;
@GET @GET
@Path("{loanId}/transactions/template") @Path("{loanId}/transactions/template")
@@ -428,8 +428,8 @@ public class LoanTransactionsApiResource {
LoanTransactionData transactionData = this.loanReadPlatformService.retrieveLoanTransaction(resolvedLoanId, LoanTransactionData transactionData = this.loanReadPlatformService.retrieveLoanTransaction(resolvedLoanId,
resolvedLoanTransactionId); resolvedLoanTransactionId);
transactionData.setLoanChargePaidByList( transactionData
this.loanChargePaidByReadPlatformService.getLoanChargesPaidByTransactionId(transactionData.getId())); .setLoanChargePaidByList(loanChargePaidByReadService.fetchLoanChargesPaidByDataTransactionId(transactionData.getId()));
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters()); final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
if (settings.isTemplate()) { if (settings.isTemplate()) {
final Collection<PaymentTypeData> paymentTypeOptions = this.paymentTypeReadPlatformService.retrieveAllPaymentTypes(); final Collection<PaymentTypeData> paymentTypeOptions = this.paymentTypeReadPlatformService.retrieveAllPaymentTypes();
@@ -912,7 +912,6 @@ public class LoansApiResource {
final Set<String> mandatoryResponseParameters = new HashSet<>(); final Set<String> mandatoryResponseParameters = new HashSet<>();
final Set<String> associationParameters = ApiParameterHelper.extractAssociationsForResponseIfProvided(uriInfo.getQueryParameters()); final Set<String> associationParameters = ApiParameterHelper.extractAssociationsForResponseIfProvided(uriInfo.getQueryParameters());
final Collection<LoanTransactionData> currentLoanRepayments = this.loanReadPlatformService.retrieveLoanTransactions(resolvedLoanId);
if (!associationParameters.isEmpty()) { if (!associationParameters.isEmpty()) {
if (associationParameters.contains(DataTableApiConstant.allAssociateParamName)) { if (associationParameters.contains(DataTableApiConstant.allAssociateParamName)) {
associationParameters.addAll(Arrays.asList(DataTableApiConstant.repaymentScheduleAssociateParamName, associationParameters.addAll(Arrays.asList(DataTableApiConstant.repaymentScheduleAssociateParamName,
@@ -935,9 +934,7 @@ public class LoansApiResource {
if (associationParameters.contains(DataTableApiConstant.transactionsAssociateParamName)) { if (associationParameters.contains(DataTableApiConstant.transactionsAssociateParamName)) {
mandatoryResponseParameters.add(DataTableApiConstant.transactionsAssociateParamName); mandatoryResponseParameters.add(DataTableApiConstant.transactionsAssociateParamName);
if (!CollectionUtils.isEmpty(currentLoanRepayments)) { loanRepayments = this.loanReadPlatformService.retrieveLoanTransactions(resolvedLoanId);
loanRepayments = currentLoanRepayments;
}
} }
if (associationParameters.contains(DataTableApiConstant.multiDisburseDetailsAssociateParamName) if (associationParameters.contains(DataTableApiConstant.multiDisburseDetailsAssociateParamName)
@@ -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.LoanScheduleAccrualData;
import org.apache.fineract.portfolio.loanaccount.data.LoanTermVariationsData; import org.apache.fineract.portfolio.loanaccount.data.LoanTermVariationsData;
import org.apache.fineract.portfolio.loanaccount.data.LoanTransactionData; 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.PaidInAdvanceData;
import org.apache.fineract.portfolio.loanaccount.data.RepaymentScheduleRelatedLoanData; import org.apache.fineract.portfolio.loanaccount.data.RepaymentScheduleRelatedLoanData;
import org.apache.fineract.portfolio.loanaccount.domain.Loan; import org.apache.fineract.portfolio.loanaccount.domain.Loan;
@@ -160,8 +159,6 @@ public interface LoanReadPlatformService {
List<LoanRepaymentScheduleInstallmentData> getRepaymentDataResponse(Long loanId); List<LoanRepaymentScheduleInstallmentData> getRepaymentDataResponse(Long loanId);
List<LoanTransactionRelationData> retrieveLoanTransactionRelationsByLoanTransactionId(Long loanTransactionId);
Long retrieveLoanTransactionIdByExternalId(ExternalId externalId); Long retrieveLoanTransactionIdByExternalId(ExternalId externalId);
Long retrieveLoanIdByExternalId(ExternalId externalId); Long retrieveLoanIdByExternalId(ExternalId externalId);
@@ -38,6 +38,7 @@ import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.Optional; import java.util.Optional;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.fineract.accounting.common.AccountingRuleType; 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.LoanAccountData;
import org.apache.fineract.portfolio.loanaccount.data.LoanApplicationTimelineData; import org.apache.fineract.portfolio.loanaccount.data.LoanApplicationTimelineData;
import org.apache.fineract.portfolio.loanaccount.data.LoanApprovalData; 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.LoanInterestRecalculationData;
import org.apache.fineract.portfolio.loanaccount.data.LoanRepaymentScheduleInstallmentData; import org.apache.fineract.portfolio.loanaccount.data.LoanRepaymentScheduleInstallmentData;
import org.apache.fineract.portfolio.loanaccount.data.LoanScheduleAccrualData; 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.LoanSubStatus;
import org.apache.fineract.portfolio.loanaccount.domain.LoanTermVariationType; import org.apache.fineract.portfolio.loanaccount.domain.LoanTermVariationType;
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction; 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.LoanTransactionRepository;
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType; import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType;
import org.apache.fineract.portfolio.loanaccount.exception.LoanNotFoundException; 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.data.OverdueLoanScheduleData;
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleProcessingType; import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleProcessingType;
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleType; 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.LoanProductData;
import org.apache.fineract.portfolio.loanproduct.data.TransactionProcessingStrategyData; import org.apache.fineract.portfolio.loanproduct.data.TransactionProcessingStrategyData;
import org.apache.fineract.portfolio.loanproduct.domain.InterestMethod; import org.apache.fineract.portfolio.loanproduct.domain.InterestMethod;
@@ -175,9 +174,8 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService, Loa
private final DatabaseSpecificSQLGenerator sqlGenerator; private final DatabaseSpecificSQLGenerator sqlGenerator;
private final DelinquencyReadPlatformService delinquencyReadPlatformService; private final DelinquencyReadPlatformService delinquencyReadPlatformService;
private final LoanTransactionRepository loanTransactionRepository; private final LoanTransactionRepository loanTransactionRepository;
private final LoanTransactionRelationRepository loanTransactionRelationRepository; private final LoanChargePaidByReadService loanChargePaidByReadService;
private final LoanTransactionRelationMapper loanTransactionRelationMapper; private final LoanTransactionRelationReadService loanTransactionRelationReadService;
private final LoanChargePaidByReadPlatformService loanChargePaidByReadPlatformService;
@Override @Override
public LoanAccountData retrieveOne(final Long loanId) { 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 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 // 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 // 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) { for (LoanTransactionData loanTransaction : loanTransactionData) {
loanTransaction loanTransaction.setLoanTransactionRelations(loanTransactionRelationDatas.stream().filter(
.setLoanTransactionRelations(this.retrieveLoanTransactionRelationsByLoanTransactionId(loanTransaction.getId())); loanTransactionRelationData -> loanTransactionRelationData.getFromLoanTransaction().equals(loanTransaction.getId()))
loanTransaction.setLoanChargePaidByList( .toList());
loanChargePaidByReadPlatformService.getLoanChargesPaidByTransactionId(loanTransaction.getId())); loanTransaction.setLoanChargePaidByList(loanChargePaidByDatas.stream()
.filter(loanChargePaidByData -> loanChargePaidByData.getTransactionId().equals(loanTransaction.getId())).toList());
} }
return loanTransactionData; return loanTransactionData;
} catch (final EmptyResultDataAccessException e) { } catch (final EmptyResultDataAccessException e) {
@@ -587,7 +591,8 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService, Loa
final LoanTransactionsMapper rm = new LoanTransactionsMapper(sqlGenerator); final LoanTransactionsMapper rm = new LoanTransactionsMapper(sqlGenerator);
final String sql = "select " + rm.loanPaymentsSchema() + " where l.id = ? and tr.id = ? "; final String sql = "select " + rm.loanPaymentsSchema() + " where l.id = ? and tr.id = ? ";
LoanTransactionData loanTransactionData = this.jdbcTemplate.queryForObject(sql, rm, loanId, transactionId); // NOSONAR LoanTransactionData loanTransactionData = this.jdbcTemplate.queryForObject(sql, rm, loanId, transactionId); // NOSONAR
loanTransactionData.setLoanTransactionRelations(this.retrieveLoanTransactionRelationsByLoanTransactionId(transactionId)); loanTransactionData.setLoanTransactionRelations(
loanTransactionRelationReadService.fetchLoanTransactionRelationDataFrom(loanTransactionData.getId()));
return loanTransactionData; return loanTransactionData;
} catch (final EmptyResultDataAccessException e) { } catch (final EmptyResultDataAccessException e) {
throw new LoanTransactionNotFoundException(transactionId, 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 dueDate = JdbcSupport.getLocalDate(rs, "dueDate");
final LocalDate obligationsMetOnDate = JdbcSupport.getLocalDate(rs, "obligationsMetOnDate"); final LocalDate obligationsMetOnDate = JdbcSupport.getLocalDate(rs, "obligationsMetOnDate");
final boolean complete = rs.getBoolean("complete"); final boolean complete = rs.getBoolean("complete");
final boolean isAdditional = rs.getBoolean("isAdditional");
BigDecimal disbursedAmount = BigDecimal.ZERO; BigDecimal disbursedAmount = BigDecimal.ZERO;
disbursedAmount = processDisbursementData(loanScheduleType, disbursementData, fromDate, dueDate, disbursementPeriodIds, disbursedAmount = processDisbursementData(loanScheduleType, disbursementData, fromDate, dueDate, disbursementPeriodIds,
@@ -2595,14 +2599,6 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService, Loa
return this.jdbcTemplate.queryForObject(sql, Integer.class); 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 @Override
public Long retrieveLoanTransactionIdByExternalId(ExternalId externalId) { public Long retrieveLoanTransactionIdByExternalId(ExternalId externalId) {
return loanTransactionRepository.findIdByExternalId(externalId); return loanTransactionRepository.findIdByExternalId(externalId);
@@ -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.loanschedule.service.LoanScheduleHistoryWritePlatformService;
import org.apache.fineract.portfolio.loanaccount.mapper.LoanChargeMapper; import org.apache.fineract.portfolio.loanaccount.mapper.LoanChargeMapper;
import org.apache.fineract.portfolio.loanaccount.mapper.LoanCollateralManagementMapper; 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.LoanApplicationTransitionValidator;
import org.apache.fineract.portfolio.loanaccount.serialization.LoanApplicationValidator; import org.apache.fineract.portfolio.loanaccount.serialization.LoanApplicationValidator;
import org.apache.fineract.portfolio.loanaccount.serialization.LoanChargeApiJsonValidator; 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.LoanAssembler;
import org.apache.fineract.portfolio.loanaccount.service.LoanCalculateRepaymentPastDueService; import org.apache.fineract.portfolio.loanaccount.service.LoanCalculateRepaymentPastDueService;
import org.apache.fineract.portfolio.loanaccount.service.LoanChargeAssembler; import org.apache.fineract.portfolio.loanaccount.service.LoanChargeAssembler;
import org.apache.fineract.portfolio.loanaccount.service.LoanChargePaidByReadPlatformService; import org.apache.fineract.portfolio.loanaccount.service.LoanChargePaidByReadService;
import org.apache.fineract.portfolio.loanaccount.service.LoanChargePaidByReadPlatformServiceImpl;
import org.apache.fineract.portfolio.loanaccount.service.LoanChargeReadPlatformService; import org.apache.fineract.portfolio.loanaccount.service.LoanChargeReadPlatformService;
import org.apache.fineract.portfolio.loanaccount.service.LoanChargeReadPlatformServiceImpl; import org.apache.fineract.portfolio.loanaccount.service.LoanChargeReadPlatformServiceImpl;
import org.apache.fineract.portfolio.loanaccount.service.LoanChargeWritePlatformService; 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.LoanStatusChangePlatformService;
import org.apache.fineract.portfolio.loanaccount.service.LoanStatusChangePlatformServiceImpl; import org.apache.fineract.portfolio.loanaccount.service.LoanStatusChangePlatformServiceImpl;
import org.apache.fineract.portfolio.loanaccount.service.LoanTransactionAssembler; 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.LoanUtilService;
import org.apache.fineract.portfolio.loanaccount.service.LoanWritePlatformService; import org.apache.fineract.portfolio.loanaccount.service.LoanWritePlatformService;
import org.apache.fineract.portfolio.loanaccount.service.LoanWritePlatformServiceJpaRepositoryImpl; import org.apache.fineract.portfolio.loanaccount.service.LoanWritePlatformServiceJpaRepositoryImpl;
@@ -252,13 +251,6 @@ public class LoanAccountConfiguration {
return new LoanChargeAssembler(fromApiJsonHelper, chargeRepository, loanChargeRepository, loanProductRepository, externalIdFactory); 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 @Bean
@ConditionalOnMissingBean(LoanChargeReadPlatformService.class) @ConditionalOnMissingBean(LoanChargeReadPlatformService.class)
public LoanChargeReadPlatformService loanChargeReadPlatformService(JdbcTemplate jdbcTemplate, public LoanChargeReadPlatformService loanChargeReadPlatformService(JdbcTemplate jdbcTemplate,
@@ -312,19 +304,15 @@ public class LoanAccountConfiguration {
ConfigurationDomainService configurationDomainService, AccountDetailsReadPlatformService accountDetailsReadPlatformService, ConfigurationDomainService configurationDomainService, AccountDetailsReadPlatformService accountDetailsReadPlatformService,
ColumnValidator columnValidator, DatabaseSpecificSQLGenerator sqlGenerator, ColumnValidator columnValidator, DatabaseSpecificSQLGenerator sqlGenerator,
DelinquencyReadPlatformService delinquencyReadPlatformService, LoanTransactionRepository loanTransactionRepository, DelinquencyReadPlatformService delinquencyReadPlatformService, LoanTransactionRepository loanTransactionRepository,
LoanTransactionRelationRepository loanTransactionRelationRepository, LoanChargePaidByReadService loanChargePaidByReadService,
LoanTransactionRelationMapper loanTransactionRelationMapper, LoanTransactionRelationReadService loanTransactionRelationReadService) {
LoanChargePaidByReadPlatformService loanChargePaidByReadPlatformService) {
return new LoanReadPlatformServiceImpl(jdbcTemplate, context, loanRepositoryWrapper, applicationCurrencyRepository, return new LoanReadPlatformServiceImpl(jdbcTemplate, context, loanRepositoryWrapper, applicationCurrencyRepository,
loanProductReadPlatformService, clientReadPlatformService, groupReadPlatformService, loanDropdownReadPlatformService, loanProductReadPlatformService, clientReadPlatformService, groupReadPlatformService, loanDropdownReadPlatformService,
fundReadPlatformService, chargeReadPlatformService, codeValueReadPlatformService, calendarReadPlatformService, fundReadPlatformService, chargeReadPlatformService, codeValueReadPlatformService, calendarReadPlatformService,
staffReadPlatformService, paginationHelper, namedParameterJdbcTemplate, paymentTypeReadPlatformService, staffReadPlatformService, paginationHelper, namedParameterJdbcTemplate, paymentTypeReadPlatformService,
loanRepaymentScheduleTransactionProcessorFactory, floatingRatesReadPlatformService, loanUtilService, loanRepaymentScheduleTransactionProcessorFactory, floatingRatesReadPlatformService, loanUtilService,
configurationDomainService, accountDetailsReadPlatformService, columnValidator, sqlGenerator, configurationDomainService, accountDetailsReadPlatformService, columnValidator, sqlGenerator,
delinquencyReadPlatformService, loanTransactionRepository, loanTransactionRelationRepository, loanTransactionRelationMapper, delinquencyReadPlatformService, loanTransactionRepository, loanChargePaidByReadService, loanTransactionRelationReadService);
loanChargePaidByReadPlatformService
);
} }
@Bean @Bean
@@ -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.data.LoanTransactionData;
import org.apache.fineract.portfolio.loanaccount.domain.Loan; import org.apache.fineract.portfolio.loanaccount.domain.Loan;
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction; 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.loanaccount.service.LoanReadPlatformService;
import org.apache.fineract.portfolio.loanproduct.service.LoanEnumerations; import org.apache.fineract.portfolio.loanproduct.service.LoanEnumerations;
import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.AfterEach;
@@ -65,7 +65,7 @@ public class LoanAdjustTransactionBusinessEventSerializerTest {
@Mock @Mock
private LoanReadPlatformService service; private LoanReadPlatformService service;
@Mock @Mock
private LoanChargePaidByReadPlatformService loanChargePaidByReadPlatformService; private LoanChargePaidByReadService loanChargePaidByReadService;
@Mock @Mock
private AvroDateTimeMapper avroDateTimeMapper; private AvroDateTimeMapper avroDateTimeMapper;
@Mock @Mock
@@ -88,7 +88,7 @@ public class LoanAdjustTransactionBusinessEventSerializerTest {
void loanTransactionReversedOnDateSerializationTest() { void loanTransactionReversedOnDateSerializationTest() {
Loan loanForProcessing = Mockito.mock(Loan.class); Loan loanForProcessing = Mockito.mock(Loan.class);
LoanAdjustTransactionBusinessEventSerializer serializer = new LoanAdjustTransactionBusinessEventSerializer(service, LoanAdjustTransactionBusinessEventSerializer serializer = new LoanAdjustTransactionBusinessEventSerializer(service,
new LoanTransactionDataMapperImpl(avroDateTimeMapper, externalIdMapper), loanChargePaidByReadPlatformService); new LoanTransactionDataMapperImpl(avroDateTimeMapper, externalIdMapper), loanChargePaidByReadService);
LoanTransaction transactionToAdjust = Mockito.mock(LoanTransaction.class); LoanTransaction transactionToAdjust = Mockito.mock(LoanTransaction.class);
LoanAdjustTransactionBusinessEvent.Data loanAdjustTransactionBusinessEventData = new LoanAdjustTransactionBusinessEvent.Data( LoanAdjustTransactionBusinessEvent.Data loanAdjustTransactionBusinessEventData = new LoanAdjustTransactionBusinessEvent.Data(
transactionToAdjust); transactionToAdjust);
@@ -103,7 +103,7 @@ public class LoanAdjustTransactionBusinessEventSerializerTest {
true, new ExternalId("testReversalExternalId"), reversedOnDate, 1L, new ExternalId("testExternalLoanId")); true, new ExternalId("testReversalExternalId"), reversedOnDate, 1L, new ExternalId("testExternalLoanId"));
when(service.retrieveLoanTransaction(anyLong(), anyLong())).thenReturn(transactionToAdjustData); 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(transactionToAdjust.getLoan()).thenReturn(loanForProcessing);
when(loanForProcessing.getId()).thenReturn(1L); when(loanForProcessing.getId()).thenReturn(1L);
when(transactionToAdjust.getId()).thenReturn(1L); when(transactionToAdjust.getId()).thenReturn(1L);
@@ -32,7 +32,6 @@ import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.function.Predicate;
import org.apache.fineract.infrastructure.businessdate.domain.BusinessDateType; import org.apache.fineract.infrastructure.businessdate.domain.BusinessDateType;
import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService; import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService;
import org.apache.fineract.infrastructure.core.domain.ActionContext; 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.LoanStatus;
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction; import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionToRepaymentScheduleMapping; 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.LoanProduct;
import org.apache.fineract.portfolio.loanproduct.domain.LoanProductRelatedDetail; import org.apache.fineract.portfolio.loanproduct.domain.LoanProductRelatedDetail;
import org.apache.fineract.portfolio.paymentdetail.domain.PaymentDetail; import org.apache.fineract.portfolio.paymentdetail.domain.PaymentDetail;
@@ -82,6 +83,8 @@ public class LoanDelinquencyDomainServiceTest {
private DelinquencyEffectivePauseHelper delinquencyEffectivePauseHelper; private DelinquencyEffectivePauseHelper delinquencyEffectivePauseHelper;
@InjectMocks @InjectMocks
private LoanDelinquencyDomainServiceImpl underTest; private LoanDelinquencyDomainServiceImpl underTest;
@Mock
private LoanTransactionReadService loanTransactionReadService;
private LocalDate businessDate; private LocalDate businessDate;
private MonetaryCurrency currency; private MonetaryCurrency currency;
@@ -149,7 +152,8 @@ public class LoanDelinquencyDomainServiceTest {
when(loanProductRelatedDetail.getGraceOnArrearsAgeing()).thenReturn(0); when(loanProductRelatedDetail.getGraceOnArrearsAgeing()).thenReturn(0);
when(loan.getLoanProductRelatedDetail()).thenReturn(loanProductRelatedDetail); when(loan.getLoanProductRelatedDetail()).thenReturn(loanProductRelatedDetail);
when(loan.getRepaymentScheduleInstallments()).thenReturn(repaymentScheduleInstallments); 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.getLastLoanRepaymentScheduleInstallment()).thenReturn(repaymentScheduleInstallments.get(0));
when(loan.getCurrency()).thenReturn(currency); when(loan.getCurrency()).thenReturn(currency);
when(loan.getStatus()).thenReturn(LoanStatus.ACTIVE); when(loan.getStatus()).thenReturn(LoanStatus.ACTIVE);
@@ -217,7 +221,8 @@ public class LoanDelinquencyDomainServiceTest {
when(loanProductRelatedDetail.getGraceOnArrearsAgeing()).thenReturn(0); when(loanProductRelatedDetail.getGraceOnArrearsAgeing()).thenReturn(0);
when(loan.getLoanProductRelatedDetail()).thenReturn(loanProductRelatedDetail); when(loan.getLoanProductRelatedDetail()).thenReturn(loanProductRelatedDetail);
when(loan.getRepaymentScheduleInstallments()).thenReturn(repaymentScheduleInstallments); 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.getLastLoanRepaymentScheduleInstallment()).thenReturn(repaymentScheduleInstallments.get(0));
when(loan.getCurrency()).thenReturn(currency); when(loan.getCurrency()).thenReturn(currency);
when(loan.isEnableInstallmentLevelDelinquency()).thenReturn(true); when(loan.isEnableInstallmentLevelDelinquency()).thenReturn(true);
@@ -272,7 +277,8 @@ public class LoanDelinquencyDomainServiceTest {
when(loan.isEnableInstallmentLevelDelinquency()).thenReturn(true); when(loan.isEnableInstallmentLevelDelinquency()).thenReturn(true);
when(loan.getCurrency()).thenReturn(currency); when(loan.getCurrency()).thenReturn(currency);
when(loan.getStatus()).thenReturn(LoanStatus.ACTIVE); 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); when(delinquencyEffectivePauseHelper.getPausedDaysBeforeDate(effectiveDelinquencyList, businessDate)).thenReturn(0L);
LoanDelinquencyData collectionData = underTest.getLoanDelinquencyData(loan, effectiveDelinquencyList); LoanDelinquencyData collectionData = underTest.getLoanDelinquencyData(loan, effectiveDelinquencyList);
@@ -390,7 +390,7 @@ public abstract class BaseLoanIntegrationTest {
protected void verifyTransactions(Long loanId, Transaction... transactions) { protected void verifyTransactions(Long loanId, Transaction... transactions) {
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId.intValue()); GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId.intValue());
if (transactions == null || transactions.length == 0) { if (transactions == null || transactions.length == 0) {
assertNull(loanDetails.getTransactions(), "No transaction is expected"); Assertions.assertTrue(loanDetails.getTransactions().isEmpty(), "No transaction is expected");
} else { } else {
Assertions.assertEquals(transactions.length, loanDetails.getTransactions().size()); Assertions.assertEquals(transactions.length, loanDetails.getTransactions().size());
Arrays.stream(transactions).forEach(tr -> { Arrays.stream(transactions).forEach(tr -> {