FINERACT-1971: Fix for not properly resolving delinquency range data for loan in case there's an installment delinquency
This commit is contained in:
committed by
Arnold Gálovics
parent
ef465562b5
commit
cbe8f029e2
+268
@@ -0,0 +1,268 @@
|
||||
/**
|
||||
* 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.delinquency.service;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
||||
import org.apache.fineract.infrastructure.event.business.domain.loan.LoanDelinquencyRangeChangeBusinessEvent;
|
||||
import org.apache.fineract.infrastructure.event.business.service.BusinessEventNotifierService;
|
||||
import org.apache.fineract.portfolio.delinquency.domain.DelinquencyBucket;
|
||||
import org.apache.fineract.portfolio.delinquency.domain.DelinquencyRange;
|
||||
import org.apache.fineract.portfolio.delinquency.domain.DelinquencyRangeRepository;
|
||||
import org.apache.fineract.portfolio.delinquency.domain.LoanDelinquencyTagHistory;
|
||||
import org.apache.fineract.portfolio.delinquency.domain.LoanDelinquencyTagHistoryRepository;
|
||||
import org.apache.fineract.portfolio.delinquency.domain.LoanInstallmentDelinquencyTag;
|
||||
import org.apache.fineract.portfolio.delinquency.domain.LoanInstallmentDelinquencyTagRepository;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.CollectionData;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
@Component
|
||||
@Slf4j
|
||||
public class DelinquencyWritePlatformServiceHelper {
|
||||
|
||||
private final BusinessEventNotifierService businessEventNotifierService;
|
||||
private final LoanDelinquencyTagHistoryRepository loanDelinquencyTagRepository;
|
||||
private final DelinquencyRangeRepository repositoryRange;
|
||||
private final LoanInstallmentDelinquencyTagRepository loanInstallmentDelinquencyTagRepository;
|
||||
|
||||
public Map<String, Object> applyDelinquencyForLoan(final Loan loan, final DelinquencyBucket delinquencyBucket, long overdueDays) {
|
||||
Map<String, Object> changes = new HashMap<>();
|
||||
|
||||
if (overdueDays <= 0) { // No Delinquency
|
||||
log.debug("Loan {} without delinquency range with {} days", loan.getId(), overdueDays);
|
||||
changes = setLoanDelinquencyTag(loan, null);
|
||||
|
||||
} else {
|
||||
// Sort the ranges based on the minAgeDays
|
||||
final List<DelinquencyRange> ranges = sortDelinquencyRangesByMinAge(delinquencyBucket.getRanges());
|
||||
|
||||
for (final DelinquencyRange delinquencyRange : ranges) {
|
||||
if (delinquencyRange.getMaximumAgeDays() == null) { // Last Range in the Bucket
|
||||
if (delinquencyRange.getMinimumAgeDays() <= overdueDays) {
|
||||
log.debug("Loan {} with delinquency range {} with {} days", loan.getId(), delinquencyRange.getClassification(),
|
||||
overdueDays);
|
||||
changes = setLoanDelinquencyTag(loan, delinquencyRange.getId());
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if (delinquencyRange.getMinimumAgeDays() <= overdueDays && delinquencyRange.getMaximumAgeDays() >= overdueDays) {
|
||||
log.debug("Loan {} with delinquency range {} with {} days", loan.getId(), delinquencyRange.getClassification(),
|
||||
overdueDays);
|
||||
changes = setLoanDelinquencyTag(loan, delinquencyRange.getId());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
changes.put("overdueDays", overdueDays);
|
||||
return changes;
|
||||
}
|
||||
|
||||
public Map<String, Object> setLoanDelinquencyTag(Loan loan, Long delinquencyRangeId) {
|
||||
Map<String, Object> changes = new HashMap<>();
|
||||
List<LoanDelinquencyTagHistory> loanDelinquencyTagHistory = new ArrayList<>();
|
||||
final LocalDate transactionDate = DateUtils.getBusinessLocalDate();
|
||||
Optional<LoanDelinquencyTagHistory> optLoanDelinquencyTag = this.loanDelinquencyTagRepository.findByLoanAndLiftedOnDate(loan, null);
|
||||
// The delinquencyRangeId in null means just goes out from Delinquency
|
||||
LoanDelinquencyTagHistory loanDelinquencyTagPrev = null;
|
||||
if (delinquencyRangeId == null) {
|
||||
// The Loan will go out from Delinquency
|
||||
if (optLoanDelinquencyTag.isPresent()) {
|
||||
loanDelinquencyTagPrev = optLoanDelinquencyTag.get();
|
||||
loanDelinquencyTagPrev.setLiftedOnDate(transactionDate);
|
||||
loanDelinquencyTagHistory.add(loanDelinquencyTagPrev);
|
||||
changes.put("previous", loanDelinquencyTagPrev.getDelinquencyRange());
|
||||
// event when loan goes out of delinquency we do not calculate at
|
||||
// installment level and remove all installment tags, so event needs to raised here.
|
||||
if (loan.isEnableInstallmentLevelDelinquency()) {
|
||||
businessEventNotifierService.notifyPostBusinessEvent(new LoanDelinquencyRangeChangeBusinessEvent(loan));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (optLoanDelinquencyTag.isPresent()) {
|
||||
loanDelinquencyTagPrev = optLoanDelinquencyTag.get();
|
||||
}
|
||||
// If the Delinquency Tag has not changed
|
||||
if (loanDelinquencyTagPrev != null && loanDelinquencyTagPrev.getDelinquencyRange().getId().equals(delinquencyRangeId)) {
|
||||
changes.put("current", loanDelinquencyTagPrev.getDelinquencyRange());
|
||||
} else {
|
||||
// The previous Loan Delinquency Tag will set as Lifted
|
||||
if (loanDelinquencyTagPrev != null) {
|
||||
loanDelinquencyTagPrev.setLiftedOnDate(transactionDate);
|
||||
loanDelinquencyTagHistory.add(loanDelinquencyTagPrev);
|
||||
changes.put("previous", loanDelinquencyTagPrev.getDelinquencyRange());
|
||||
}
|
||||
|
||||
final DelinquencyRange delinquencyRange = repositoryRange.getReferenceById(delinquencyRangeId);
|
||||
LoanDelinquencyTagHistory loanDelinquencyTag = new LoanDelinquencyTagHistory(delinquencyRange, loan, transactionDate, null);
|
||||
loanDelinquencyTagHistory.add(loanDelinquencyTag);
|
||||
changes.put("current", loanDelinquencyTag.getDelinquencyRange());
|
||||
}
|
||||
}
|
||||
if (loanDelinquencyTagHistory.size() > 0) {
|
||||
this.loanDelinquencyTagRepository.saveAllAndFlush(loanDelinquencyTagHistory);
|
||||
// if installment level delinquency is enabled event will be raised at installment level calculation, no
|
||||
// need to raise the event here
|
||||
if (!loan.isEnableInstallmentLevelDelinquency()) {
|
||||
businessEventNotifierService.notifyPostBusinessEvent(new LoanDelinquencyRangeChangeBusinessEvent(loan));
|
||||
}
|
||||
}
|
||||
return changes;
|
||||
}
|
||||
|
||||
public List<DelinquencyRange> sortDelinquencyRangesByMinAge(List<DelinquencyRange> ranges) {
|
||||
final Comparator<DelinquencyRange> orderByMinAge = new Comparator<DelinquencyRange>() {
|
||||
|
||||
@Override
|
||||
public int compare(DelinquencyRange o1, DelinquencyRange o2) {
|
||||
return o1.getMinimumAgeDays().compareTo(o2.getMinimumAgeDays());
|
||||
}
|
||||
};
|
||||
Collections.sort(ranges, orderByMinAge);
|
||||
return ranges;
|
||||
}
|
||||
|
||||
public void applyDelinquencyForLoanInstallments(final Loan loan, final DelinquencyBucket delinquencyBucket,
|
||||
final Map<Long, CollectionData> installmentsCollectionData) {
|
||||
boolean isDelinquencyRangeChangedForAnyOfInstallment = false;
|
||||
for (LoanRepaymentScheduleInstallment installment : loan.getRepaymentScheduleInstallments()) {
|
||||
if (installmentsCollectionData.containsKey(installment.getId())) {
|
||||
boolean isDelinquencySetForInstallment = setInstallmentDelinquencyDetails(loan, installment, delinquencyBucket,
|
||||
installmentsCollectionData.get(installment.getId()));
|
||||
isDelinquencyRangeChangedForAnyOfInstallment = isDelinquencyRangeChangedForAnyOfInstallment
|
||||
|| isDelinquencySetForInstallment;
|
||||
}
|
||||
}
|
||||
// remove tags for non-existing installments that got deleted due to re-schedule
|
||||
removeDelinquencyTagsForNonExistingInstallments(loan.getId());
|
||||
// raise event if there is any change at installment level delinquency
|
||||
if (isDelinquencyRangeChangedForAnyOfInstallment) {
|
||||
businessEventNotifierService.notifyPostBusinessEvent(new LoanDelinquencyRangeChangeBusinessEvent(loan));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void removeDelinquencyTagsForNonExistingInstallments(Long loanId) {
|
||||
List<LoanInstallmentDelinquencyTag> currentLoanInstallmentDelinquencyTags = loanInstallmentDelinquencyTagRepository
|
||||
.findByLoanId(loanId);
|
||||
if (currentLoanInstallmentDelinquencyTags != null && currentLoanInstallmentDelinquencyTags.size() > 0) {
|
||||
List<Long> loanInstallmentTagsForDelete = currentLoanInstallmentDelinquencyTags.stream()
|
||||
.filter(tag -> tag.getInstallment() == null).map(tag -> tag.getId()).toList();
|
||||
if (loanInstallmentTagsForDelete.size() > 0) {
|
||||
loanInstallmentDelinquencyTagRepository.deleteAllLoanInstallmentsTagsByIds(loanInstallmentTagsForDelete);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean setInstallmentDelinquencyDetails(final Loan loan, final LoanRepaymentScheduleInstallment installment,
|
||||
final DelinquencyBucket delinquencyBucket, final CollectionData installmentDelinquencyData) {
|
||||
DelinquencyRange delinquencyRangeForInstallment = getInstallmentDelinquencyRange(delinquencyBucket,
|
||||
installmentDelinquencyData.getDelinquentDays());
|
||||
return setDelinquencyDetailsForInstallment(loan, installment, installmentDelinquencyData, delinquencyRangeForInstallment);
|
||||
}
|
||||
|
||||
private DelinquencyRange getInstallmentDelinquencyRange(final DelinquencyBucket delinquencyBucket, Long overDueDays) {
|
||||
DelinquencyRange delinquencyRangeForInstallment = null;
|
||||
if (overDueDays > 0) {
|
||||
// Sort the ranges based on the minAgeDays
|
||||
final List<DelinquencyRange> ranges = sortDelinquencyRangesByMinAge(delinquencyBucket.getRanges());
|
||||
for (final DelinquencyRange delinquencyRange : ranges) {
|
||||
if (delinquencyRange.getMaximumAgeDays() == null) { // Last Range in the Bucket
|
||||
if (delinquencyRange.getMinimumAgeDays() <= overDueDays) {
|
||||
delinquencyRangeForInstallment = delinquencyRange;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if (delinquencyRange.getMinimumAgeDays() <= overDueDays && delinquencyRange.getMaximumAgeDays() >= overDueDays) {
|
||||
delinquencyRangeForInstallment = delinquencyRange;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return delinquencyRangeForInstallment;
|
||||
}
|
||||
|
||||
private boolean setDelinquencyDetailsForInstallment(final Loan loan, final LoanRepaymentScheduleInstallment installment,
|
||||
CollectionData installmentDelinquencyData, final DelinquencyRange delinquencyRangeForInstallment) {
|
||||
List<LoanInstallmentDelinquencyTag> installmentDelinquencyTags = new ArrayList<>();
|
||||
LocalDate delinquencyCalculationDate = DateUtils.getBusinessLocalDate();
|
||||
boolean isDelinquencyRangeChanged = false;
|
||||
|
||||
LoanInstallmentDelinquencyTag previousInstallmentDelinquencyTag = loanInstallmentDelinquencyTagRepository
|
||||
.findByLoanAndInstallment(loan, installment).orElse(null);
|
||||
|
||||
if (delinquencyRangeForInstallment == null) {
|
||||
// if currentInstallmentDelinquencyTag exists and range is null, installment is out of delinquency, delete
|
||||
// delinquency details
|
||||
if (previousInstallmentDelinquencyTag != null) {
|
||||
// event installment out of delinquency
|
||||
loanInstallmentDelinquencyTagRepository.delete(previousInstallmentDelinquencyTag);
|
||||
isDelinquencyRangeChanged = true;
|
||||
}
|
||||
} else {
|
||||
LoanInstallmentDelinquencyTag installmentDelinquency = null;
|
||||
if (previousInstallmentDelinquencyTag != null) {
|
||||
if (!previousInstallmentDelinquencyTag.getDelinquencyRange().getId().equals(delinquencyRangeForInstallment.getId())) {
|
||||
// if current delinquency range exists and there is range change, delete previous delinquency
|
||||
// details and add new range details
|
||||
installmentDelinquency = new LoanInstallmentDelinquencyTag(delinquencyRangeForInstallment, loan, installment,
|
||||
delinquencyCalculationDate, null, previousInstallmentDelinquencyTag.getFirstOverdueDate(),
|
||||
installmentDelinquencyData.getDelinquentAmount());
|
||||
loanInstallmentDelinquencyTagRepository.delete(previousInstallmentDelinquencyTag);
|
||||
// event installment delinquency range change
|
||||
isDelinquencyRangeChanged = true;
|
||||
} else {
|
||||
previousInstallmentDelinquencyTag.setOutstandingAmount(installmentDelinquencyData.getDelinquentAmount());
|
||||
installmentDelinquency = previousInstallmentDelinquencyTag;
|
||||
}
|
||||
} else {
|
||||
// add new range, first time delinquent
|
||||
installmentDelinquency = new LoanInstallmentDelinquencyTag(delinquencyRangeForInstallment, loan, installment,
|
||||
delinquencyCalculationDate, null, installmentDelinquencyData.getDelinquentDate(),
|
||||
installmentDelinquencyData.getDelinquentAmount());
|
||||
// event installment delinquent
|
||||
isDelinquencyRangeChanged = true;
|
||||
}
|
||||
|
||||
if (installmentDelinquency != null) {
|
||||
installmentDelinquencyTags.add(installmentDelinquency);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (installmentDelinquencyTags.size() > 0) {
|
||||
loanInstallmentDelinquencyTagRepository.saveAllAndFlush(installmentDelinquencyTags);
|
||||
}
|
||||
return isDelinquencyRangeChanged;
|
||||
}
|
||||
}
|
||||
+29
-236
@@ -20,8 +20,6 @@ package org.apache.fineract.portfolio.delinquency.service;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -50,7 +48,6 @@ import org.apache.fineract.portfolio.delinquency.domain.LoanDelinquencyAction;
|
||||
import org.apache.fineract.portfolio.delinquency.domain.LoanDelinquencyActionRepository;
|
||||
import org.apache.fineract.portfolio.delinquency.domain.LoanDelinquencyTagHistory;
|
||||
import org.apache.fineract.portfolio.delinquency.domain.LoanDelinquencyTagHistoryRepository;
|
||||
import org.apache.fineract.portfolio.delinquency.domain.LoanInstallmentDelinquencyTag;
|
||||
import org.apache.fineract.portfolio.delinquency.domain.LoanInstallmentDelinquencyTagRepository;
|
||||
import org.apache.fineract.portfolio.delinquency.exception.DelinquencyBucketAgesOverlapedException;
|
||||
import org.apache.fineract.portfolio.delinquency.exception.DelinquencyRangeInvalidAgesException;
|
||||
@@ -63,7 +60,6 @@ import org.apache.fineract.portfolio.loanaccount.data.CollectionData;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanDelinquencyData;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanScheduleDelinquencyData;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepositoryWrapper;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.LoanProductRepository;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
@@ -74,20 +70,20 @@ public class DelinquencyWritePlatformServiceImpl implements DelinquencyWritePlat
|
||||
|
||||
private final DelinquencyBucketParseAndValidator dataValidatorBucket;
|
||||
private final DelinquencyRangeParseAndValidator dataValidatorRange;
|
||||
|
||||
private final DelinquencyRangeRepository repositoryRange;
|
||||
private final DelinquencyBucketRepository repositoryBucket;
|
||||
private final DelinquencyBucketMappingsRepository repositoryBucketMappings;
|
||||
private final LoanDelinquencyTagHistoryRepository loanDelinquencyTagRepository;
|
||||
private final LoanRepositoryWrapper loanRepository;
|
||||
private final LoanProductRepository loanProductRepository;
|
||||
private final BusinessEventNotifierService businessEventNotifierService;
|
||||
private final LoanDelinquencyDomainService loanDelinquencyDomainService;
|
||||
private final LoanInstallmentDelinquencyTagRepository loanInstallmentDelinquencyTagRepository;
|
||||
private final DelinquencyReadPlatformService delinquencyReadPlatformService;
|
||||
private final LoanDelinquencyActionRepository loanDelinquencyActionRepository;
|
||||
private final DelinquencyActionParseAndValidator delinquencyActionParseAndValidator;
|
||||
private final DelinquencyEffectivePauseHelper delinquencyEffectivePauseHelper;
|
||||
private final BusinessEventNotifierService businessEventNotifierService;
|
||||
private final DelinquencyWritePlatformServiceHelper delinquencyHelper;
|
||||
|
||||
@Override
|
||||
public CommandProcessingResult createDelinquencyRange(JsonCommand command) {
|
||||
@@ -186,16 +182,17 @@ public class DelinquencyWritePlatformServiceImpl implements DelinquencyWritePlat
|
||||
final CollectionData collectionData = loanDelinquencyData.getLoanCollectionData();
|
||||
// loan installments delinquent data
|
||||
final Map<Long, CollectionData> installmentsCollectionData = loanDelinquencyData.getLoanInstallmentsCollectionData();
|
||||
// delinquency for installments
|
||||
if (installmentsCollectionData.size() > 0) {
|
||||
applyDelinquencyDetailsForLoanInstallments(loan, delinquencyBucket, installmentsCollectionData);
|
||||
}
|
||||
// delinquency for loan
|
||||
changes = lookUpDelinquencyRange(loan, delinquencyBucket, collectionData.getDelinquentDays());
|
||||
log.debug("Delinquency {}", collectionData);
|
||||
|
||||
changes = applyDelinquencyToLoanAndInstallments(loan, delinquencyBucket, collectionData, installmentsCollectionData);
|
||||
|
||||
}
|
||||
return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(loan.getId())
|
||||
.withEntityExternalId(loan.getExternalId()).with(changes).build();
|
||||
return new CommandProcessingResultBuilder() //
|
||||
.withCommandId(command.commandId()) //
|
||||
.withEntityId(loan.getId()) //
|
||||
.withEntityExternalId(loan.getExternalId()) //
|
||||
.with(changes) //
|
||||
.build(); //
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -210,16 +207,24 @@ public class DelinquencyWritePlatformServiceImpl implements DelinquencyWritePlat
|
||||
final CollectionData collectionData = loanDelinquentData.getLoanCollectionData();
|
||||
// loan installments delinquent data
|
||||
final Map<Long, CollectionData> installmentsCollectionData = loanDelinquentData.getLoanInstallmentsCollectionData();
|
||||
// delinquency for installments
|
||||
if (installmentsCollectionData.size() > 0) {
|
||||
applyDelinquencyDetailsForLoanInstallments(loan, delinquencyBucket, installmentsCollectionData);
|
||||
}
|
||||
log.debug("Delinquency {}", collectionData);
|
||||
// delinquency for loan
|
||||
lookUpDelinquencyRange(loan, delinquencyBucket, collectionData.getDelinquentDays());
|
||||
|
||||
applyDelinquencyToLoanAndInstallments(loan, delinquencyBucket, collectionData, installmentsCollectionData);
|
||||
}
|
||||
}
|
||||
|
||||
private Map<String, Object> applyDelinquencyToLoanAndInstallments(Loan loan, DelinquencyBucket delinquencyBucket,
|
||||
CollectionData collectionData, Map<Long, CollectionData> installmentsCollectionData) {
|
||||
// Order is important: first calculate loan level delinquency, then the installment level
|
||||
// delinquency for loan
|
||||
Map<String, Object> result = delinquencyHelper.applyDelinquencyForLoan(loan, delinquencyBucket, collectionData.getDelinquentDays());
|
||||
// delinquency for installments
|
||||
if (!installmentsCollectionData.isEmpty()) {
|
||||
delinquencyHelper.applyDelinquencyForLoanInstallments(loan, delinquencyBucket, installmentsCollectionData);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public CommandProcessingResult createDelinquencyAction(Long loanId, JsonCommand command) {
|
||||
@@ -242,7 +247,8 @@ public class DelinquencyWritePlatformServiceImpl implements DelinquencyWritePlat
|
||||
}
|
||||
}
|
||||
businessEventNotifierService.notifyPostBusinessEvent(new LoanAccountDelinquencyPauseChangedBusinessEvent(loan));
|
||||
return new CommandProcessingResultBuilder().withCommandId(command.commandId()) //
|
||||
return new CommandProcessingResultBuilder() //
|
||||
.withCommandId(command.commandId()) //
|
||||
.withEntityId(saved.getId()) //
|
||||
.withOfficeId(loan.getOfficeId()) //
|
||||
.withClientId(loan.getClientId()) //
|
||||
@@ -271,7 +277,7 @@ public class DelinquencyWritePlatformServiceImpl implements DelinquencyWritePlat
|
||||
if (loan.isEnableInstallmentLevelDelinquency()) {
|
||||
cleanLoanInstallmentsDelinquencyTags(loan);
|
||||
}
|
||||
setLoanDelinquencyTag(loan, null);
|
||||
delinquencyHelper.setLoanDelinquencyTag(loan, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -365,7 +371,7 @@ public class DelinquencyWritePlatformServiceImpl implements DelinquencyWritePlat
|
||||
|
||||
private void validateDelinquencyRanges(List<DelinquencyRange> ranges) {
|
||||
// Sort the ranges based on the minAgeDays
|
||||
ranges = sortDelinquencyRangesByMinAge(ranges);
|
||||
ranges = delinquencyHelper.sortDelinquencyRangesByMinAge(ranges);
|
||||
|
||||
DelinquencyRange prevDelinquencyRange = null;
|
||||
for (DelinquencyRange delinquencyRange : ranges) {
|
||||
@@ -387,220 +393,7 @@ public class DelinquencyWritePlatformServiceImpl implements DelinquencyWritePlat
|
||||
}
|
||||
}
|
||||
|
||||
private Map<String, Object> lookUpDelinquencyRange(final Loan loan, final DelinquencyBucket delinquencyBucket, long overdueDays) {
|
||||
Map<String, Object> changes = new HashMap<>();
|
||||
|
||||
if (overdueDays <= 0) { // No Delinquency
|
||||
log.debug("Loan {} without delinquency range with {} days", loan.getId(), overdueDays);
|
||||
changes = setLoanDelinquencyTag(loan, null);
|
||||
|
||||
} else {
|
||||
// Sort the ranges based on the minAgeDays
|
||||
final List<DelinquencyRange> ranges = sortDelinquencyRangesByMinAge(delinquencyBucket.getRanges());
|
||||
|
||||
for (final DelinquencyRange delinquencyRange : ranges) {
|
||||
if (delinquencyRange.getMaximumAgeDays() == null) { // Last Range in the Bucket
|
||||
if (delinquencyRange.getMinimumAgeDays() <= overdueDays) {
|
||||
log.debug("Loan {} with delinquency range {} with {} days", loan.getId(), delinquencyRange.getClassification(),
|
||||
overdueDays);
|
||||
changes = setLoanDelinquencyTag(loan, delinquencyRange.getId());
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if (delinquencyRange.getMinimumAgeDays() <= overdueDays && delinquencyRange.getMaximumAgeDays() >= overdueDays) {
|
||||
log.debug("Loan {} with delinquency range {} with {} days", loan.getId(), delinquencyRange.getClassification(),
|
||||
overdueDays);
|
||||
changes = setLoanDelinquencyTag(loan, delinquencyRange.getId());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
changes.put("overdueDays", overdueDays);
|
||||
return changes;
|
||||
}
|
||||
|
||||
private Map<String, Object> setLoanDelinquencyTag(Loan loan, Long delinquencyRangeId) {
|
||||
Map<String, Object> changes = new HashMap<>();
|
||||
List<LoanDelinquencyTagHistory> loanDelinquencyTagHistory = new ArrayList<>();
|
||||
final LocalDate transactionDate = DateUtils.getBusinessLocalDate();
|
||||
Optional<LoanDelinquencyTagHistory> optLoanDelinquencyTag = this.loanDelinquencyTagRepository.findByLoanAndLiftedOnDate(loan, null);
|
||||
// The delinquencyRangeId in null means just goes out from Delinquency
|
||||
LoanDelinquencyTagHistory loanDelinquencyTagPrev = null;
|
||||
if (delinquencyRangeId == null) {
|
||||
// The Loan will go out from Delinquency
|
||||
if (optLoanDelinquencyTag.isPresent()) {
|
||||
loanDelinquencyTagPrev = optLoanDelinquencyTag.get();
|
||||
loanDelinquencyTagPrev.setLiftedOnDate(transactionDate);
|
||||
loanDelinquencyTagHistory.add(loanDelinquencyTagPrev);
|
||||
changes.put("previous", loanDelinquencyTagPrev.getDelinquencyRange());
|
||||
// event when loan goes out of delinquency we do not calculate at
|
||||
// installment level and remove all installment tags, so event needs to raised here.
|
||||
if (loan.isEnableInstallmentLevelDelinquency()) {
|
||||
businessEventNotifierService.notifyPostBusinessEvent(new LoanDelinquencyRangeChangeBusinessEvent(loan));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (optLoanDelinquencyTag.isPresent()) {
|
||||
loanDelinquencyTagPrev = optLoanDelinquencyTag.get();
|
||||
}
|
||||
// If the Delinquency Tag has not changed
|
||||
if (loanDelinquencyTagPrev != null && loanDelinquencyTagPrev.getDelinquencyRange().getId().equals(delinquencyRangeId)) {
|
||||
changes.put("current", loanDelinquencyTagPrev.getDelinquencyRange());
|
||||
} else {
|
||||
// The previous Loan Delinquency Tag will set as Lifted
|
||||
if (loanDelinquencyTagPrev != null) {
|
||||
loanDelinquencyTagPrev.setLiftedOnDate(transactionDate);
|
||||
loanDelinquencyTagHistory.add(loanDelinquencyTagPrev);
|
||||
changes.put("previous", loanDelinquencyTagPrev.getDelinquencyRange());
|
||||
}
|
||||
|
||||
final DelinquencyRange delinquencyRange = repositoryRange.getReferenceById(delinquencyRangeId);
|
||||
LoanDelinquencyTagHistory loanDelinquencyTag = new LoanDelinquencyTagHistory(delinquencyRange, loan, transactionDate, null);
|
||||
loanDelinquencyTagHistory.add(loanDelinquencyTag);
|
||||
changes.put("current", loanDelinquencyTag.getDelinquencyRange());
|
||||
}
|
||||
}
|
||||
if (loanDelinquencyTagHistory.size() > 0) {
|
||||
this.loanDelinquencyTagRepository.saveAllAndFlush(loanDelinquencyTagHistory);
|
||||
// if installment level delinquency is enabled event will be raised at installment level calculation, no
|
||||
// need to raise the event here
|
||||
if (!loan.isEnableInstallmentLevelDelinquency()) {
|
||||
businessEventNotifierService.notifyPostBusinessEvent(new LoanDelinquencyRangeChangeBusinessEvent(loan));
|
||||
}
|
||||
}
|
||||
return changes;
|
||||
}
|
||||
|
||||
private List<DelinquencyRange> sortDelinquencyRangesByMinAge(List<DelinquencyRange> ranges) {
|
||||
final Comparator<DelinquencyRange> orderByMinAge = new Comparator<DelinquencyRange>() {
|
||||
|
||||
@Override
|
||||
public int compare(DelinquencyRange o1, DelinquencyRange o2) {
|
||||
return o1.getMinimumAgeDays().compareTo(o2.getMinimumAgeDays());
|
||||
}
|
||||
};
|
||||
Collections.sort(ranges, orderByMinAge);
|
||||
return ranges;
|
||||
}
|
||||
|
||||
private void applyDelinquencyDetailsForLoanInstallments(final Loan loan, final DelinquencyBucket delinquencyBucket,
|
||||
final Map<Long, CollectionData> installmentsCollectionData) {
|
||||
boolean isDelinquencyRangeChangedForAnyOfInstallment = false;
|
||||
for (LoanRepaymentScheduleInstallment installment : loan.getRepaymentScheduleInstallments()) {
|
||||
if (installmentsCollectionData.containsKey(installment.getId())) {
|
||||
boolean isDelinquencySetForInstallment = setInstallmentDelinquencyDetails(loan, installment, delinquencyBucket,
|
||||
installmentsCollectionData.get(installment.getId()));
|
||||
isDelinquencyRangeChangedForAnyOfInstallment = isDelinquencyRangeChangedForAnyOfInstallment
|
||||
|| isDelinquencySetForInstallment;
|
||||
}
|
||||
}
|
||||
// remove tags for non existing installments that got deleted due to re-schedule
|
||||
removeDelinquencyTagsForNonExistingInstallments(loan.getId());
|
||||
// raise event if there is any change at installment level delinquency
|
||||
if (isDelinquencyRangeChangedForAnyOfInstallment) {
|
||||
businessEventNotifierService.notifyPostBusinessEvent(new LoanDelinquencyRangeChangeBusinessEvent(loan));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private boolean setInstallmentDelinquencyDetails(final Loan loan, final LoanRepaymentScheduleInstallment installment,
|
||||
final DelinquencyBucket delinquencyBucket, final CollectionData installmentDelinquencyData) {
|
||||
DelinquencyRange delinquencyRangeForInstallment = getInstallmentDelinquencyRange(delinquencyBucket,
|
||||
installmentDelinquencyData.getDelinquentDays());
|
||||
return setDelinquencyDetailsForInstallment(loan, installment, installmentDelinquencyData, delinquencyRangeForInstallment);
|
||||
}
|
||||
|
||||
private DelinquencyRange getInstallmentDelinquencyRange(final DelinquencyBucket delinquencyBucket, Long overDueDays) {
|
||||
DelinquencyRange delinquencyRangeForInstallment = null;
|
||||
if (overDueDays > 0) {
|
||||
// Sort the ranges based on the minAgeDays
|
||||
final List<DelinquencyRange> ranges = sortDelinquencyRangesByMinAge(delinquencyBucket.getRanges());
|
||||
for (final DelinquencyRange delinquencyRange : ranges) {
|
||||
if (delinquencyRange.getMaximumAgeDays() == null) { // Last Range in the Bucket
|
||||
if (delinquencyRange.getMinimumAgeDays() <= overDueDays) {
|
||||
delinquencyRangeForInstallment = delinquencyRange;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if (delinquencyRange.getMinimumAgeDays() <= overDueDays && delinquencyRange.getMaximumAgeDays() >= overDueDays) {
|
||||
delinquencyRangeForInstallment = delinquencyRange;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return delinquencyRangeForInstallment;
|
||||
}
|
||||
|
||||
private boolean setDelinquencyDetailsForInstallment(final Loan loan, final LoanRepaymentScheduleInstallment installment,
|
||||
CollectionData installmentDelinquencyData, final DelinquencyRange delinquencyRangeForInstallment) {
|
||||
List<LoanInstallmentDelinquencyTag> installmentDelinquencyTags = new ArrayList<>();
|
||||
LocalDate delinquencyCalculationDate = DateUtils.getBusinessLocalDate();
|
||||
boolean isDelinquencyRangeChanged = false;
|
||||
|
||||
LoanInstallmentDelinquencyTag previousInstallmentDelinquencyTag = loanInstallmentDelinquencyTagRepository
|
||||
.findByLoanAndInstallment(loan, installment).orElse(null);
|
||||
|
||||
if (delinquencyRangeForInstallment == null) {
|
||||
// if currentInstallmentDelinquencyTag exists and range is null, installment is out of delinquency, delete
|
||||
// delinquency details
|
||||
if (previousInstallmentDelinquencyTag != null) {
|
||||
// event installment out of delinquency
|
||||
loanInstallmentDelinquencyTagRepository.delete(previousInstallmentDelinquencyTag);
|
||||
isDelinquencyRangeChanged = true;
|
||||
}
|
||||
} else {
|
||||
LoanInstallmentDelinquencyTag installmentDelinquency = null;
|
||||
if (previousInstallmentDelinquencyTag != null) {
|
||||
if (!previousInstallmentDelinquencyTag.getDelinquencyRange().getId().equals(delinquencyRangeForInstallment.getId())) {
|
||||
// if current delinquency range exists and there is range change, delete previous delinquency
|
||||
// details and add new range details
|
||||
installmentDelinquency = new LoanInstallmentDelinquencyTag(delinquencyRangeForInstallment, loan, installment,
|
||||
delinquencyCalculationDate, null, previousInstallmentDelinquencyTag.getFirstOverdueDate(),
|
||||
installmentDelinquencyData.getDelinquentAmount());
|
||||
loanInstallmentDelinquencyTagRepository.delete(previousInstallmentDelinquencyTag);
|
||||
// event installment delinquency range change
|
||||
isDelinquencyRangeChanged = true;
|
||||
} else {
|
||||
previousInstallmentDelinquencyTag.setOutstandingAmount(installmentDelinquencyData.getDelinquentAmount());
|
||||
installmentDelinquency = previousInstallmentDelinquencyTag;
|
||||
}
|
||||
} else {
|
||||
// add new range, first time delinquent
|
||||
installmentDelinquency = new LoanInstallmentDelinquencyTag(delinquencyRangeForInstallment, loan, installment,
|
||||
delinquencyCalculationDate, null, installmentDelinquencyData.getDelinquentDate(),
|
||||
installmentDelinquencyData.getDelinquentAmount());
|
||||
// event installment delinquent
|
||||
isDelinquencyRangeChanged = true;
|
||||
}
|
||||
|
||||
if (installmentDelinquency != null) {
|
||||
installmentDelinquencyTags.add(installmentDelinquency);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (installmentDelinquencyTags.size() > 0) {
|
||||
loanInstallmentDelinquencyTagRepository.saveAllAndFlush(installmentDelinquencyTags);
|
||||
}
|
||||
return isDelinquencyRangeChanged;
|
||||
}
|
||||
|
||||
private void cleanLoanInstallmentsDelinquencyTags(Loan loan) {
|
||||
loanInstallmentDelinquencyTagRepository.deleteAllLoanInstallmentsTags(loan.getId());
|
||||
}
|
||||
|
||||
private void removeDelinquencyTagsForNonExistingInstallments(Long loanId) {
|
||||
List<LoanInstallmentDelinquencyTag> currentLoanInstallmentDelinquencyTags = loanInstallmentDelinquencyTagRepository
|
||||
.findByLoanId(loanId);
|
||||
if (currentLoanInstallmentDelinquencyTags != null && currentLoanInstallmentDelinquencyTags.size() > 0) {
|
||||
List<Long> loanInstallmentTagsForDelete = currentLoanInstallmentDelinquencyTags.stream()
|
||||
.filter(tag -> tag.getInstallment() == null).map(tag -> tag.getId()).toList();
|
||||
if (loanInstallmentTagsForDelete.size() > 0) {
|
||||
loanInstallmentDelinquencyTagRepository.deleteAllLoanInstallmentsTagsByIds(loanInstallmentTagsForDelete);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+7
-4
@@ -33,6 +33,7 @@ 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.DelinquencyWritePlatformService;
|
||||
import org.apache.fineract.portfolio.delinquency.service.DelinquencyWritePlatformServiceHelper;
|
||||
import org.apache.fineract.portfolio.delinquency.service.DelinquencyWritePlatformServiceImpl;
|
||||
import org.apache.fineract.portfolio.delinquency.service.LoanDelinquencyDomainService;
|
||||
import org.apache.fineract.portfolio.delinquency.service.LoanDelinquencyDomainServiceImpl;
|
||||
@@ -76,11 +77,13 @@ public class DelinquencyConfiguration {
|
||||
LoanInstallmentDelinquencyTagRepository loanInstallmentDelinquencyTagRepository,
|
||||
DelinquencyReadPlatformService delinquencyReadPlatformService, LoanDelinquencyActionRepository loanDelinquencyActionRepository,
|
||||
DelinquencyActionParseAndValidator delinquencyActionParseAndValidator,
|
||||
DelinquencyEffectivePauseHelper delinquencyEffectivePauseHelper) {
|
||||
DelinquencyEffectivePauseHelper delinquencyEffectivePauseHelper,
|
||||
DelinquencyWritePlatformServiceHelper delinquencyWritePlatformServiceHelper) {
|
||||
return new DelinquencyWritePlatformServiceImpl(dataValidatorBucket, dataValidatorRange, repositoryRange, repositoryBucket,
|
||||
repositoryBucketMappings, loanDelinquencyTagRepository, loanRepository, loanProductRepository, businessEventNotifierService,
|
||||
loanDelinquencyDomainService, loanInstallmentDelinquencyTagRepository, delinquencyReadPlatformService,
|
||||
loanDelinquencyActionRepository, delinquencyActionParseAndValidator, delinquencyEffectivePauseHelper);
|
||||
repositoryBucketMappings, loanDelinquencyTagRepository, loanRepository, loanProductRepository, loanDelinquencyDomainService,
|
||||
loanInstallmentDelinquencyTagRepository, delinquencyReadPlatformService, loanDelinquencyActionRepository,
|
||||
delinquencyActionParseAndValidator, delinquencyEffectivePauseHelper, businessEventNotifierService,
|
||||
delinquencyWritePlatformServiceHelper);
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
||||
+76
-2
@@ -24,6 +24,8 @@ import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyIterable;
|
||||
import static org.mockito.ArgumentMatchers.anyList;
|
||||
import static org.mockito.ArgumentMatchers.anyLong;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.inOrder;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
@@ -62,6 +64,7 @@ import org.apache.fineract.portfolio.delinquency.domain.LoanInstallmentDelinquen
|
||||
import org.apache.fineract.portfolio.delinquency.domain.LoanInstallmentDelinquencyTagRepository;
|
||||
import org.apache.fineract.portfolio.delinquency.helper.DelinquencyEffectivePauseHelper;
|
||||
import org.apache.fineract.portfolio.delinquency.service.DelinquencyReadPlatformService;
|
||||
import org.apache.fineract.portfolio.delinquency.service.DelinquencyWritePlatformServiceHelper;
|
||||
import org.apache.fineract.portfolio.delinquency.service.DelinquencyWritePlatformServiceImpl;
|
||||
import org.apache.fineract.portfolio.delinquency.service.LoanDelinquencyDomainService;
|
||||
import org.apache.fineract.portfolio.delinquency.validator.DelinquencyActionParseAndValidator;
|
||||
@@ -81,7 +84,7 @@ import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.InOrder;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
@@ -120,7 +123,8 @@ public class DelinquencyWritePlatformServiceRangeChangeEventTest {
|
||||
@Mock
|
||||
private DelinquencyEffectivePauseHelper delinquencyEffectivePauseHelper;
|
||||
|
||||
@InjectMocks
|
||||
private DelinquencyWritePlatformServiceHelper delinquencyWritePlatformServiceHelper;
|
||||
|
||||
private DelinquencyWritePlatformServiceImpl underTest;
|
||||
|
||||
@BeforeEach
|
||||
@@ -129,6 +133,14 @@ public class DelinquencyWritePlatformServiceRangeChangeEventTest {
|
||||
ThreadLocalContextUtil.setActionContext(ActionContext.DEFAULT);
|
||||
ThreadLocalContextUtil
|
||||
.setBusinessDates(new HashMap<>(Map.of(BusinessDateType.BUSINESS_DATE, LocalDate.now(ZoneId.systemDefault()))));
|
||||
|
||||
delinquencyWritePlatformServiceHelper = Mockito.spy(new DelinquencyWritePlatformServiceHelper(businessEventNotifierService,
|
||||
loanDelinquencyTagRepository, repositoryRange, loanInstallmentDelinquencyTagRepository));
|
||||
underTest = new DelinquencyWritePlatformServiceImpl(dataValidatorBucket, dataValidatorRange, repositoryRange, repositoryBucket,
|
||||
repositoryBucketMappings, loanDelinquencyTagRepository, loanRepository, loanProductRepository, loanDelinquencyDomainService,
|
||||
loanInstallmentDelinquencyTagRepository, delinquencyReadPlatformService, loanDelinquencyActionRepository,
|
||||
delinquencyActionParseAndValidator, delinquencyEffectivePauseHelper, businessEventNotifierService,
|
||||
delinquencyWritePlatformServiceHelper);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
@@ -180,6 +192,68 @@ public class DelinquencyWritePlatformServiceRangeChangeEventTest {
|
||||
assertEquals(loanForProcessing, loanPayloadForEvent);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_ApplyDelinquencyTagToLoan_ExecutesDelinquencyApplication_InTheRightOrder() {
|
||||
// given
|
||||
final List<LoanDelinquencyActionData> effectiveDelinquencyList = Collections.emptyList();
|
||||
Loan loanForProcessing = Mockito.mock(Loan.class);
|
||||
LoanProduct loanProduct = Mockito.mock(LoanProduct.class);
|
||||
DelinquencyRange range1 = DelinquencyRange.instance("Range1", 1, 2);
|
||||
range1.setId(1L);
|
||||
DelinquencyRange range2 = DelinquencyRange.instance("Range30", 3, 30);
|
||||
range2.setId(2L);
|
||||
List<DelinquencyRange> listDelinquencyRanges = Arrays.asList(range1, range2);
|
||||
DelinquencyBucket delinquencyBucket = new DelinquencyBucket("test Bucket");
|
||||
delinquencyBucket.setRanges(listDelinquencyRanges);
|
||||
|
||||
final Long daysDiff = 2L;
|
||||
final LocalDate fromDate = DateUtils.getBusinessLocalDate().minusMonths(1).minusDays(daysDiff);
|
||||
final LocalDate dueDate = DateUtils.getBusinessLocalDate().minusDays(daysDiff);
|
||||
final BigDecimal installmentPrincipalAmount = BigDecimal.valueOf(100);
|
||||
final BigDecimal zeroAmount = BigDecimal.ZERO;
|
||||
|
||||
LoanRepaymentScheduleInstallment installment = new LoanRepaymentScheduleInstallment(loanForProcessing, 1, fromDate, dueDate,
|
||||
installmentPrincipalAmount, zeroAmount, zeroAmount, zeroAmount, false, new HashSet<>(), zeroAmount);
|
||||
installment.setId(1L);
|
||||
|
||||
List<LoanRepaymentScheduleInstallment> repaymentScheduleInstallments = Arrays.asList(installment);
|
||||
|
||||
LocalDate overDueSinceDate = DateUtils.getBusinessLocalDate().minusDays(2);
|
||||
LoanScheduleDelinquencyData loanScheduleDelinquencyData = new LoanScheduleDelinquencyData(1L, overDueSinceDate, 1L,
|
||||
loanForProcessing);
|
||||
CollectionData collectionData = new CollectionData(BigDecimal.ZERO, 2L, null, 2L, overDueSinceDate, BigDecimal.ZERO, null, null,
|
||||
null, null, null, null);
|
||||
|
||||
CollectionData installmentCollectionData = new CollectionData(BigDecimal.ZERO, 2L, null, 2L, overDueSinceDate,
|
||||
installmentPrincipalAmount, null, null, null, null, null, null);
|
||||
|
||||
Map<Long, CollectionData> installmentsCollection = new HashMap<>();
|
||||
installmentsCollection.put(1L, installmentCollectionData);
|
||||
|
||||
LoanDelinquencyData loanDelinquencyData = new LoanDelinquencyData(collectionData, installmentsCollection);
|
||||
|
||||
when(loanForProcessing.getLoanProduct()).thenReturn(loanProduct);
|
||||
when(loanProduct.getDelinquencyBucket()).thenReturn(delinquencyBucket);
|
||||
when(loanForProcessing.hasDelinquencyBucket()).thenReturn(true);
|
||||
when(loanForProcessing.getRepaymentScheduleInstallments()).thenReturn(repaymentScheduleInstallments);
|
||||
when(loanForProcessing.isEnableInstallmentLevelDelinquency()).thenReturn(true);
|
||||
when(loanDelinquencyTagRepository.findByLoanAndLiftedOnDate(any(), any())).thenReturn(Optional.empty());
|
||||
when(loanDelinquencyDomainService.getLoanDelinquencyData(loanForProcessing, effectiveDelinquencyList))
|
||||
.thenReturn(loanDelinquencyData);
|
||||
when(loanInstallmentDelinquencyTagRepository.findByLoanAndInstallment(loanForProcessing, repaymentScheduleInstallments.get(0)))
|
||||
.thenReturn(Optional.empty());
|
||||
|
||||
// when
|
||||
underTest.applyDelinquencyTagToLoan(loanScheduleDelinquencyData, effectiveDelinquencyList);
|
||||
|
||||
// then
|
||||
InOrder inOrder = inOrder(delinquencyWritePlatformServiceHelper);
|
||||
inOrder.verify(delinquencyWritePlatformServiceHelper).applyDelinquencyForLoan(eq(loanForProcessing), eq(delinquencyBucket),
|
||||
anyLong());
|
||||
inOrder.verify(delinquencyWritePlatformServiceHelper).applyDelinquencyForLoanInstallments(eq(loanForProcessing),
|
||||
eq(delinquencyBucket), eq(installmentsCollection));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenLoanAccountWithDelinquencyBucketWhenNoRangeChangeThenNoEventIsRaised() {
|
||||
// given
|
||||
|
||||
Reference in New Issue
Block a user