From d06b0562f15d09f4f192162262d53a0158fefb7c Mon Sep 17 00:00:00 2001 From: Adam Saghy Date: Wed, 14 Feb 2024 10:56:40 +0100 Subject: [PATCH] FINERACT-1971: Include downpayment as last repayment in Collection data --- .../portfolio/loanaccount/domain/Loan.java | 4 +- .../DelinquencyReadPlatformServiceImpl.java | 2 +- ...ntDelinquencyRangeEventSerializerTest.java | 69 +++++++++++++++++++ 3 files changed, 72 insertions(+), 3 deletions(-) diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java index 9d2dca1a6..8d9369443 100644 --- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java +++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java @@ -5085,10 +5085,10 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom { .orElse(null); } - public LoanTransaction getLastRepaymentTransaction() { + public LoanTransaction getLastRepaymentOrDownPaymentTransaction() { return loanTransactions.stream() // .filter(loanTransaction -> !loanTransaction.isReversed()) // - .filter(LoanTransaction::isRepayment) // + .filter(loanTransaction -> loanTransaction.isRepayment() || loanTransaction.isDownPayment()) // .reduce((first, second) -> second) // .orElse(null); } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/service/DelinquencyReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/service/DelinquencyReadPlatformServiceImpl.java index e12ab385b..876609bea 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/service/DelinquencyReadPlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/service/DelinquencyReadPlatformServiceImpl.java @@ -147,7 +147,7 @@ public class DelinquencyReadPlatformServiceImpl implements DelinquencyReadPlatfo collectionData.setLastPaymentAmount(lastPayment.getAmount()); } - final LoanTransaction lastRepaymentTransaction = loan.getLastRepaymentTransaction(); + final LoanTransaction lastRepaymentTransaction = loan.getLastRepaymentOrDownPaymentTransaction(); if (lastRepaymentTransaction != null) { collectionData.setLastRepaymentDate(lastRepaymentTransaction.getTransactionDate()); collectionData.setLastRepaymentAmount(lastRepaymentTransaction.getAmount()); diff --git a/fineract-provider/src/test/java/org/apache/fineract/infrastructure/event/external/service/serialization/serializer/loan/LoanAccountDelinquencyRangeEventSerializerTest.java b/fineract-provider/src/test/java/org/apache/fineract/infrastructure/event/external/service/serialization/serializer/loan/LoanAccountDelinquencyRangeEventSerializerTest.java index 62d8f49e2..3abad3fe7 100644 --- a/fineract-provider/src/test/java/org/apache/fineract/infrastructure/event/external/service/serialization/serializer/loan/LoanAccountDelinquencyRangeEventSerializerTest.java +++ b/fineract-provider/src/test/java/org/apache/fineract/infrastructure/event/external/service/serialization/serializer/loan/LoanAccountDelinquencyRangeEventSerializerTest.java @@ -23,6 +23,8 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import java.io.IOException; @@ -35,11 +37,13 @@ import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; import org.apache.fineract.avro.loan.v1.LoanAccountDelinquencyRangeDataV1; import org.apache.fineract.avro.loan.v1.LoanInstallmentDelinquencyBucketDataV1; import org.apache.fineract.infrastructure.businessdate.domain.BusinessDateType; +import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService; import org.apache.fineract.infrastructure.core.domain.ActionContext; import org.apache.fineract.infrastructure.core.domain.ExternalId; import org.apache.fineract.infrastructure.core.domain.FineractPlatformTenant; @@ -61,13 +65,27 @@ import org.apache.fineract.portfolio.charge.domain.ChargePaymentMode; import org.apache.fineract.portfolio.charge.domain.ChargeTimeType; import org.apache.fineract.portfolio.delinquency.data.DelinquencyRangeData; import org.apache.fineract.portfolio.delinquency.data.LoanInstallmentDelinquencyTagData; +import org.apache.fineract.portfolio.delinquency.domain.DelinquencyBucketRepository; +import org.apache.fineract.portfolio.delinquency.domain.DelinquencyRangeRepository; +import org.apache.fineract.portfolio.delinquency.domain.LoanDelinquencyActionRepository; +import org.apache.fineract.portfolio.delinquency.domain.LoanDelinquencyTagHistoryRepository; +import org.apache.fineract.portfolio.delinquency.domain.LoanInstallmentDelinquencyTagRepository; +import org.apache.fineract.portfolio.delinquency.helper.DelinquencyEffectivePauseHelper; +import org.apache.fineract.portfolio.delinquency.mapper.DelinquencyBucketMapper; +import org.apache.fineract.portfolio.delinquency.mapper.DelinquencyRangeMapper; +import org.apache.fineract.portfolio.delinquency.mapper.LoanDelinquencyTagMapper; import org.apache.fineract.portfolio.delinquency.service.DelinquencyReadPlatformService; +import org.apache.fineract.portfolio.delinquency.service.DelinquencyReadPlatformServiceImpl; +import org.apache.fineract.portfolio.delinquency.service.LoanDelinquencyDomainService; import org.apache.fineract.portfolio.loanaccount.data.CollectionData; import org.apache.fineract.portfolio.loanaccount.data.LoanAccountData; import org.apache.fineract.portfolio.loanaccount.domain.Loan; import org.apache.fineract.portfolio.loanaccount.domain.LoanCharge; import org.apache.fineract.portfolio.loanaccount.domain.LoanInstallmentCharge; import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment; +import org.apache.fineract.portfolio.loanaccount.domain.LoanRepository; +import org.apache.fineract.portfolio.loanaccount.domain.LoanStatus; +import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction; import org.apache.fineract.portfolio.loanaccount.service.LoanChargeReadPlatformService; import org.apache.fineract.portfolio.loanaccount.service.LoanReadPlatformService; import org.junit.jupiter.api.AfterEach; @@ -299,6 +317,57 @@ public class LoanAccountDelinquencyRangeEventSerializerTest { moneyHelper.close(); } + @Test + public void testLastRepaymentInCollectionData() { + // given + DelinquencyRangeRepository repositoryRange = Mockito.mock(DelinquencyRangeRepository.class); + DelinquencyBucketRepository repositoryBucket = Mockito.mock(DelinquencyBucketRepository.class); + LoanDelinquencyTagHistoryRepository repositoryLoanDelinquencyTagHistory = Mockito.mock(LoanDelinquencyTagHistoryRepository.class); + DelinquencyRangeMapper mapperRange = Mockito.mock(DelinquencyRangeMapper.class); + DelinquencyBucketMapper mapperBucket = Mockito.mock(DelinquencyBucketMapper.class); + LoanDelinquencyTagMapper mapperLoanDelinquencyTagHistory = Mockito.mock(LoanDelinquencyTagMapper.class); + LoanRepository loanRepository = Mockito.mock(LoanRepository.class); + LoanDelinquencyDomainService loanDelinquencyDomainService = Mockito.mock(LoanDelinquencyDomainService.class); + LoanInstallmentDelinquencyTagRepository repositoryLoanInstallmentDelinquencyTag = Mockito + .mock(LoanInstallmentDelinquencyTagRepository.class); + LoanDelinquencyActionRepository loanDelinquencyActionRepository = Mockito.mock(LoanDelinquencyActionRepository.class); + DelinquencyEffectivePauseHelper delinquencyEffectivePauseHelper = Mockito.mock(DelinquencyEffectivePauseHelper.class); + ConfigurationDomainService configurationDomainService = Mockito.mock(ConfigurationDomainService.class); + + DelinquencyReadPlatformService delinquencyReadPlatformService = new DelinquencyReadPlatformServiceImpl(repositoryRange, + repositoryBucket, repositoryLoanDelinquencyTagHistory, mapperRange, mapperBucket, mapperLoanDelinquencyTagHistory, + loanRepository, loanDelinquencyDomainService, repositoryLoanInstallmentDelinquencyTag, loanDelinquencyActionRepository, + delinquencyEffectivePauseHelper, configurationDomainService); + + Loan loan = Mockito.spy(Loan.class); + ReflectionTestUtils.setField(loan, "loanStatus", 300); + LoanTransaction transaction1 = Mockito.mock(LoanTransaction.class); + LoanTransaction transaction2 = Mockito.mock(LoanTransaction.class); + CollectionData collectionData = Mockito.mock(CollectionData.class); + when(transaction1.isRepayment()).thenReturn(true); + when(transaction1.isReversed()).thenReturn(false); + LocalDate transactionDate1 = LocalDate.of(2024, 1, 1); + when(transaction1.getTransactionDate()).thenReturn(transactionDate1); + when(transaction1.getAmount()).thenReturn(BigDecimal.ONE); + when(transaction2.isDownPayment()).thenReturn(true); + when(transaction2.isReversed()).thenReturn(false); + LocalDate transactionDate2 = LocalDate.of(2024, 1, 2); + when(transaction2.getTransactionDate()).thenReturn(transactionDate2); + when(transaction2.getAmount()).thenReturn(BigDecimal.TEN); + when(loan.getStatus()).thenReturn(LoanStatus.ACTIVE); + when(loan.getApprovedPrincipal()).thenReturn(BigDecimal.TEN); + when(loan.getDisbursedAmount()).thenReturn(BigDecimal.ONE); + ReflectionTestUtils.setField(loan, "loanTransactions", List.of(transaction1, transaction2)); + when(loan.getLoanTransactions()).thenReturn(List.of(transaction1, transaction2)); + when(loanDelinquencyDomainService.getOverdueCollectionData(Mockito.any(), Mockito.anyList())).thenReturn(collectionData); + when(loanRepository.findById(1L)).thenReturn(Optional.of(loan)); + // when + delinquencyReadPlatformService.calculateLoanCollectionData(1L); + // then + verify(collectionData, times(1)).setLastRepaymentDate(LocalDate.of(2024, 1, 2)); + verify(collectionData, times(1)).setLastRepaymentAmount(BigDecimal.TEN); + } + private LoanInstallmentDelinquencyTagData buildInstallmentDelinquencyTag(long installmentId, long rangeId) { LoanInstallmentDelinquencyTagData.InstallmentDelinquencyRange delinquencyRange = mock( LoanInstallmentDelinquencyTagData.InstallmentDelinquencyRange.class);