[FINERACT-1992] Delinquency calculation with pause

This commit is contained in:
taskain7
2023-12-05 16:21:33 +01:00
parent 977fb20171
commit cb40f230b1
22 changed files with 1005 additions and 196 deletions
@@ -20,13 +20,22 @@ package org.apache.fineract.cob.loan;
import static org.apache.fineract.infrastructure.core.diagnostics.performance.MeasuringUtil.measure;
import java.time.LocalDate;
import java.util.List;
import java.util.Optional;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.fineract.infrastructure.businessdate.domain.BusinessDateType;
import org.apache.fineract.infrastructure.core.domain.ActionContext;
import org.apache.fineract.infrastructure.core.domain.ExternalId;
import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil;
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.LoanDelinquencyAction;
import org.apache.fineract.portfolio.delinquency.helper.DelinquencyEffectivePauseHelper;
import org.apache.fineract.portfolio.delinquency.service.DelinquencyReadPlatformService;
import org.apache.fineract.portfolio.delinquency.validator.LoanDelinquencyActionData;
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
import org.apache.fineract.portfolio.loanaccount.domain.LoanAccountDomainService;
import org.springframework.stereotype.Component;
@@ -37,6 +46,9 @@ import org.springframework.stereotype.Component;
public class SetLoanDelinquencyTagsBusinessStep implements LoanCOBBusinessStep {
private final LoanAccountDomainService loanAccountDomainService;
private final DelinquencyEffectivePauseHelper delinquencyEffectivePauseHelper;
private final DelinquencyReadPlatformService delinquencyReadPlatformService;
private final BusinessEventNotifierService businessEventNotifierService;
@Override
public Loan execute(Loan loan) {
@@ -55,7 +67,17 @@ public class SetLoanDelinquencyTagsBusinessStep implements LoanCOBBusinessStep {
// the
// current date and not the previous (COB) date.
ThreadLocalContextUtil.setActionContext(ActionContext.DEFAULT);
loanAccountDomainService.setLoanDelinquencyTag(loan, DateUtils.getBusinessLocalDate());
final List<LoanDelinquencyAction> savedDelinquencyList = delinquencyReadPlatformService
.retrieveLoanDelinquencyActions(loan.getId());
List<LoanDelinquencyActionData> effectiveDelinquencyList = delinquencyEffectivePauseHelper
.calculateEffectiveDelinquencyList(savedDelinquencyList);
if (!isDelinquencyOnPause(loan, effectiveDelinquencyList)) {
loanAccountDomainService.setLoanDelinquencyTag(loan, DateUtils.getBusinessLocalDate(), effectiveDelinquencyList);
} else {
log.debug("Delinquency is on pause for loan with ID [{}]", loan.getId());
}
} catch (RuntimeException re) {
log.error(
"Received [{}] exception while processing delinquency tag for loan with Id [{}], account number [{}], external Id [{}]",
@@ -74,6 +96,21 @@ public class SetLoanDelinquencyTagsBusinessStep implements LoanCOBBusinessStep {
return loan;
}
private boolean isDelinquencyOnPause(Loan loan, List<LoanDelinquencyActionData> effectiveDelinquencyList) {
LocalDate cobBusinessDate = ThreadLocalContextUtil.getBusinessDateByType(BusinessDateType.COB_DATE);
boolean isPaused = isPausedOnDate(cobBusinessDate, effectiveDelinquencyList);
boolean wasPausedOneDayBefore = isPausedOnDate(cobBusinessDate.minusDays(1), effectiveDelinquencyList);
if ((isPaused && !wasPausedOneDayBefore) || (!isPaused && wasPausedOneDayBefore)) {
businessEventNotifierService.notifyPostBusinessEvent(new LoanDelinquencyRangeChangeBusinessEvent(loan));
}
return isPaused;
}
private static boolean isPausedOnDate(LocalDate date, List<LoanDelinquencyActionData> effectiveDelinquencyList) {
return effectiveDelinquencyList.stream()
.anyMatch(pausePeriod -> !pausePeriod.getStartDate().isAfter(date) && !pausePeriod.getEndDate().isBefore(date));
}
@Override
public String getEnumStyledName() {
return "LOAN_DELINQUENCY_CLASSIFICATION";
@@ -0,0 +1,31 @@
/**
* 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.helper;
import java.time.LocalDate;
import java.util.List;
import org.apache.fineract.portfolio.delinquency.domain.LoanDelinquencyAction;
import org.apache.fineract.portfolio.delinquency.validator.LoanDelinquencyActionData;
public interface DelinquencyEffectivePauseHelper {
List<LoanDelinquencyActionData> calculateEffectiveDelinquencyList(List<LoanDelinquencyAction> savedDelinquencyActions);
Long getPausedDaysBeforeDate(List<LoanDelinquencyActionData> effectiveDelinquencyList, LocalDate date);
}
@@ -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.delinquency.helper;
import static org.apache.fineract.portfolio.delinquency.domain.DelinquencyAction.RESUME;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.portfolio.delinquency.domain.DelinquencyAction;
import org.apache.fineract.portfolio.delinquency.domain.LoanDelinquencyAction;
import org.apache.fineract.portfolio.delinquency.validator.LoanDelinquencyActionData;
import org.springframework.stereotype.Service;
@Service
public class DelinquencyEffectivePauseHelperImpl implements DelinquencyEffectivePauseHelper {
@Override
public List<LoanDelinquencyActionData> calculateEffectiveDelinquencyList(List<LoanDelinquencyAction> savedDelinquencyActions) {
// partition them based on type
Map<DelinquencyAction, List<LoanDelinquencyAction>> partitioned = savedDelinquencyActions.stream()
.collect(Collectors.groupingBy(LoanDelinquencyAction::getAction));
List<LoanDelinquencyActionData> effective = new ArrayList<>();
List<LoanDelinquencyAction> pauses = partitioned.get(DelinquencyAction.PAUSE);
if (pauses != null && pauses.size() > 0) {
for (LoanDelinquencyAction loanDelinquencyAction : pauses) {
Optional<LoanDelinquencyAction> resume = findMatchingResume(loanDelinquencyAction, partitioned.get(RESUME));
LoanDelinquencyActionData loanDelinquencyActionData = new LoanDelinquencyActionData(loanDelinquencyAction);
resume.ifPresent(r -> loanDelinquencyActionData.setEndDate(r.getStartDate()));
effective.add(loanDelinquencyActionData);
}
}
return effective;
}
@Override
public Long getPausedDaysBeforeDate(List<LoanDelinquencyActionData> effectiveDelinquencyList, LocalDate date) {
Long pausedDaysClosedPausePeriods = effectiveDelinquencyList.stream() //
.filter(pausePeriod -> pausePeriod.getStartDate().isBefore(date) && pausePeriod.getEndDate().isBefore(date))
.map(pausePeriod -> DateUtils.getDifferenceInDays(pausePeriod.getStartDate(), pausePeriod.getEndDate())) //
.reduce(0L, Long::sum);
Long pausedDaysRunningPausePeriods = effectiveDelinquencyList.stream() //
.filter(pausePeriod -> pausePeriod.getStartDate().isBefore(date) && !pausePeriod.getEndDate().isBefore(date))
.map(pausePeriod -> DateUtils.getDifferenceInDays(pausePeriod.getStartDate(), date)) //
.reduce(0L, Long::sum);
return Long.sum(pausedDaysClosedPausePeriods, pausedDaysRunningPausePeriods);
}
private Optional<LoanDelinquencyAction> findMatchingResume(LoanDelinquencyAction pause, List<LoanDelinquencyAction> resumes) {
if (resumes != null && resumes.size() > 0) {
for (LoanDelinquencyAction resume : resumes) {
if (!pause.getStartDate().isAfter(resume.getStartDate()) && !resume.getStartDate().isAfter(pause.getEndDate())) {
return Optional.of(resume);
}
}
}
return Optional.empty();
}
}
@@ -19,6 +19,7 @@
package org.apache.fineract.portfolio.delinquency.service;
import java.util.Collection;
import java.util.List;
import org.apache.fineract.portfolio.delinquency.data.DelinquencyBucketData;
import org.apache.fineract.portfolio.delinquency.data.DelinquencyRangeData;
import org.apache.fineract.portfolio.delinquency.data.LoanDelinquencyTagHistoryData;
@@ -44,6 +45,6 @@ public interface DelinquencyReadPlatformService {
Collection<LoanInstallmentDelinquencyTagData> retrieveLoanInstallmentsCurrentDelinquencyTag(Long loanId);
Collection<LoanDelinquencyAction> retrieveLoanDelinquencyActions(Long loanId);
List<LoanDelinquencyAction> retrieveLoanDelinquencyActions(Long loanId);
}
@@ -18,14 +18,10 @@
*/
package org.apache.fineract.portfolio.delinquency.service;
import static org.apache.fineract.portfolio.delinquency.domain.DelinquencyAction.RESUME;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collector;
import java.util.stream.Collectors;
@@ -36,7 +32,6 @@ import org.apache.fineract.portfolio.delinquency.data.DelinquencyBucketData;
import org.apache.fineract.portfolio.delinquency.data.DelinquencyRangeData;
import org.apache.fineract.portfolio.delinquency.data.LoanDelinquencyTagHistoryData;
import org.apache.fineract.portfolio.delinquency.data.LoanInstallmentDelinquencyTagData;
import org.apache.fineract.portfolio.delinquency.domain.DelinquencyAction;
import org.apache.fineract.portfolio.delinquency.domain.DelinquencyBucket;
import org.apache.fineract.portfolio.delinquency.domain.DelinquencyBucketRepository;
import org.apache.fineract.portfolio.delinquency.domain.DelinquencyRange;
@@ -46,6 +41,7 @@ import org.apache.fineract.portfolio.delinquency.domain.LoanDelinquencyActionRep
import org.apache.fineract.portfolio.delinquency.domain.LoanDelinquencyTagHistory;
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;
@@ -73,6 +69,7 @@ public class DelinquencyReadPlatformServiceImpl implements DelinquencyReadPlatfo
private final LoanDelinquencyDomainService loanDelinquencyDomainService;
private final LoanInstallmentDelinquencyTagRepository repositoryLoanInstallmentDelinquencyTag;
private final LoanDelinquencyActionRepository loanDelinquencyActionRepository;
private final DelinquencyEffectivePauseHelper delinquencyEffectivePauseHelper;
@Override
public Collection<DelinquencyRangeData> retrieveAllDelinquencyRanges() {
@@ -127,7 +124,11 @@ public class DelinquencyReadPlatformServiceImpl implements DelinquencyReadPlatfo
if (optLoan.isPresent()) {
final Loan loan = optLoan.get();
collectionData = loanDelinquencyDomainService.getOverdueCollectionData(loan);
final List<LoanDelinquencyAction> savedDelinquencyList = retrieveLoanDelinquencyActions(loanId);
List<LoanDelinquencyActionData> effectiveDelinquencyList = delinquencyEffectivePauseHelper
.calculateEffectiveDelinquencyList(savedDelinquencyList);
collectionData = loanDelinquencyDomainService.getOverdueCollectionData(loan, effectiveDelinquencyList);
collectionData.setAvailableDisbursementAmount(loan.getApprovedPrincipal().subtract(loan.getDisbursedAmount()));
collectionData.setNextPaymentDueDate(loan.possibleNextRepaymentDate());
@@ -143,8 +144,7 @@ public class DelinquencyReadPlatformServiceImpl implements DelinquencyReadPlatfo
collectionData.setLastRepaymentAmount(lastRepaymentTransaction.getAmount());
}
enrichWithDelinquencyPausePeriodInfo(collectionData, retrieveLoanDelinquencyActions(loanId),
ThreadLocalContextUtil.getBusinessDate());
enrichWithDelinquencyPausePeriodInfo(collectionData, effectiveDelinquencyList, ThreadLocalContextUtil.getBusinessDate());
if (optLoan.get().isEnableInstallmentLevelDelinquency()) {
addInstallmentLevelDelinquencyData(collectionData, loanId);
@@ -188,29 +188,12 @@ public class DelinquencyReadPlatformServiceImpl implements DelinquencyReadPlatfo
});
}
void enrichWithDelinquencyPausePeriodInfo(CollectionData collectionData, Collection<LoanDelinquencyAction> delinquencyActions,
void enrichWithDelinquencyPausePeriodInfo(CollectionData collectionData, Collection<LoanDelinquencyActionData> effectiveDelinquencyList,
LocalDate businessDate) {
// partition them based on type
Map<DelinquencyAction, List<LoanDelinquencyAction>> partitioned = delinquencyActions.stream()
.collect(Collectors.groupingBy(LoanDelinquencyAction::getAction));
// add the possible resumes to it to create the effective pause periods
if (partitioned.containsKey(DelinquencyAction.PAUSE)) {
List<LoanDelinquencyActionData> effective = new ArrayList<>();
List<LoanDelinquencyAction> pauses = partitioned.get(DelinquencyAction.PAUSE);
for (LoanDelinquencyAction loanDelinquencyAction : pauses) {
Optional<LoanDelinquencyAction> resume = findMatchingResume(loanDelinquencyAction, partitioned.get(RESUME));
LoanDelinquencyActionData loanDelinquencyActionData = new LoanDelinquencyActionData(loanDelinquencyAction);
resume.ifPresent(r -> loanDelinquencyActionData.setEndDate(r.getStartDate()));
effective.add(loanDelinquencyActionData);
}
// order them by start date, and convert to DelinquencyPausePeriod objects
List<DelinquencyPausePeriod> result = effective.stream() //
.sorted(Comparator.comparing(LoanDelinquencyActionData::getStartDate)) //
.map(lda -> toDelinquencyPausePeriod(businessDate, lda)).toList(); //
collectionData.setDelinquencyPausePeriods(result);
}
List<DelinquencyPausePeriod> result = effectiveDelinquencyList.stream() //
.sorted(Comparator.comparing(LoanDelinquencyActionData::getStartDate)) //
.map(lda -> toDelinquencyPausePeriod(businessDate, lda)).toList(); //
collectionData.setDelinquencyPausePeriods(result);
}
@NotNull
@@ -219,24 +202,13 @@ public class DelinquencyReadPlatformServiceImpl implements DelinquencyReadPlatfo
lda.getStartDate(), lda.getEndDate());
}
private Optional<LoanDelinquencyAction> findMatchingResume(LoanDelinquencyAction pause, List<LoanDelinquencyAction> resumes) {
if (resumes != null && resumes.size() > 0) {
for (LoanDelinquencyAction resume : resumes) {
if (!pause.getStartDate().isAfter(resume.getStartDate()) && !resume.getStartDate().isAfter(pause.getEndDate())) {
return Optional.of(resume);
}
}
}
return Optional.empty();
}
@Override
public Collection<LoanInstallmentDelinquencyTagData> retrieveLoanInstallmentsCurrentDelinquencyTag(Long loanId) {
return repositoryLoanInstallmentDelinquencyTag.findInstallmentDelinquencyTags(loanId);
}
@Override
public Collection<LoanDelinquencyAction> retrieveLoanDelinquencyActions(Long loanId) {
public List<LoanDelinquencyAction> retrieveLoanDelinquencyActions(Long loanId) {
final Optional<Loan> optLoan = this.loanRepository.findById(loanId);
if (optLoan.isPresent()) {
return loanDelinquencyActionRepository.findByLoanOrderById(optLoan.get());
@@ -18,8 +18,10 @@
*/
package org.apache.fineract.portfolio.delinquency.service;
import java.util.List;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
import org.apache.fineract.portfolio.delinquency.validator.LoanDelinquencyActionData;
import org.apache.fineract.portfolio.loanaccount.data.LoanScheduleDelinquencyData;
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
@@ -43,9 +45,11 @@ public interface DelinquencyWritePlatformService {
void cleanLoanDelinquencyTags(Loan loan);
LoanScheduleDelinquencyData calculateDelinquencyData(LoanScheduleDelinquencyData loanScheduleDelinquencyData);
LoanScheduleDelinquencyData calculateDelinquencyData(LoanScheduleDelinquencyData loanScheduleDelinquencyData,
List<LoanDelinquencyActionData> effectiveDelinquencyList);
void applyDelinquencyTagToLoan(LoanScheduleDelinquencyData loanDelinquencyData);
void applyDelinquencyTagToLoan(LoanScheduleDelinquencyData loanDelinquencyData,
List<LoanDelinquencyActionData> effectiveDelinquencyList);
CommandProcessingResult createDelinquencyAction(Long loanId, JsonCommand command);
@@ -53,9 +53,11 @@ import org.apache.fineract.portfolio.delinquency.domain.LoanInstallmentDelinquen
import org.apache.fineract.portfolio.delinquency.domain.LoanInstallmentDelinquencyTagRepository;
import org.apache.fineract.portfolio.delinquency.exception.DelinquencyBucketAgesOverlapedException;
import org.apache.fineract.portfolio.delinquency.exception.DelinquencyRangeInvalidAgesException;
import org.apache.fineract.portfolio.delinquency.helper.DelinquencyEffectivePauseHelper;
import org.apache.fineract.portfolio.delinquency.validator.DelinquencyActionParseAndValidator;
import org.apache.fineract.portfolio.delinquency.validator.DelinquencyBucketParseAndValidator;
import org.apache.fineract.portfolio.delinquency.validator.DelinquencyRangeParseAndValidator;
import org.apache.fineract.portfolio.delinquency.validator.LoanDelinquencyActionData;
import org.apache.fineract.portfolio.loanaccount.data.CollectionData;
import org.apache.fineract.portfolio.loanaccount.data.LoanDelinquencyData;
import org.apache.fineract.portfolio.loanaccount.data.LoanScheduleDelinquencyData;
@@ -81,8 +83,10 @@ public class DelinquencyWritePlatformServiceImpl implements DelinquencyWritePlat
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;
@Override
public CommandProcessingResult createDelinquencyRange(JsonCommand command) {
@@ -153,12 +157,13 @@ public class DelinquencyWritePlatformServiceImpl implements DelinquencyWritePlat
}
@Override
public LoanScheduleDelinquencyData calculateDelinquencyData(LoanScheduleDelinquencyData loanScheduleDelinquencyData) {
public LoanScheduleDelinquencyData calculateDelinquencyData(LoanScheduleDelinquencyData loanScheduleDelinquencyData,
List<LoanDelinquencyActionData> effectiveDelinquencyList) {
Loan loan = loanScheduleDelinquencyData.getLoan();
if (loan == null) {
loan = this.loanRepository.findOneWithNotFoundDetection(loanScheduleDelinquencyData.getLoanId());
}
final CollectionData collectionData = loanDelinquencyDomainService.getOverdueCollectionData(loan);
final CollectionData collectionData = loanDelinquencyDomainService.getOverdueCollectionData(loan, effectiveDelinquencyList);
log.debug("Delinquency {}", collectionData);
return new LoanScheduleDelinquencyData(loan.getId(), collectionData.getDelinquentDate(), collectionData.getDelinquentDays(), loan);
}
@@ -168,9 +173,14 @@ public class DelinquencyWritePlatformServiceImpl implements DelinquencyWritePlat
Map<String, Object> changes = new HashMap<>();
final Loan loan = this.loanRepository.findOneWithNotFoundDetection(loanId);
final List<LoanDelinquencyAction> savedDelinquencyList = delinquencyReadPlatformService
.retrieveLoanDelinquencyActions(loan.getId());
List<LoanDelinquencyActionData> effectiveDelinquencyList = delinquencyEffectivePauseHelper
.calculateEffectiveDelinquencyList(savedDelinquencyList);
final DelinquencyBucket delinquencyBucket = loan.getLoanProduct().getDelinquencyBucket();
if (delinquencyBucket != null) {
final LoanDelinquencyData loanDelinquencyData = loanDelinquencyDomainService.getLoanDelinquencyData(loan);
final LoanDelinquencyData loanDelinquencyData = loanDelinquencyDomainService.getLoanDelinquencyData(loan,
effectiveDelinquencyList);
// loan delinquent data
final CollectionData collectionData = loanDelinquencyData.getLoanCollectionData();
// loan installments delinquent data
@@ -188,11 +198,13 @@ public class DelinquencyWritePlatformServiceImpl implements DelinquencyWritePlat
}
@Override
public void applyDelinquencyTagToLoan(LoanScheduleDelinquencyData loanDelinquencyData) {
public void applyDelinquencyTagToLoan(LoanScheduleDelinquencyData loanDelinquencyData,
List<LoanDelinquencyActionData> effectiveDelinquencyList) {
final Loan loan = loanDelinquencyData.getLoan();
if (loan.hasDelinquencyBucket()) {
final DelinquencyBucket delinquencyBucket = loan.getLoanProduct().getDelinquencyBucket();
final LoanDelinquencyData loanDelinquentData = loanDelinquencyDomainService.getLoanDelinquencyData(loan);
final LoanDelinquencyData loanDelinquentData = loanDelinquencyDomainService.getLoanDelinquencyData(loan,
effectiveDelinquencyList);
// loan delinquent data
final CollectionData collectionData = loanDelinquentData.getLoanCollectionData();
// loan installments delinquent data
@@ -212,7 +224,7 @@ public class DelinquencyWritePlatformServiceImpl implements DelinquencyWritePlat
public CommandProcessingResult createDelinquencyAction(Long loanId, JsonCommand command) {
final Loan loan = this.loanRepository.findOneWithNotFoundDetection(loanId);
final LocalDate businessDate = DateUtils.getBusinessLocalDate();
final List<LoanDelinquencyAction> savedDelinquencyList = loanDelinquencyActionRepository.findByLoanOrderById(loan);
final List<LoanDelinquencyAction> savedDelinquencyList = delinquencyReadPlatformService.retrieveLoanDelinquencyActions(loanId);
LoanDelinquencyAction parsedDelinquencyAction = delinquencyActionParseAndValidator.validateAndParseUpdate(command, loan,
savedDelinquencyList, businessDate);
@@ -18,6 +18,8 @@
*/
package org.apache.fineract.portfolio.delinquency.service;
import java.util.List;
import org.apache.fineract.portfolio.delinquency.validator.LoanDelinquencyActionData;
import org.apache.fineract.portfolio.loanaccount.data.CollectionData;
import org.apache.fineract.portfolio.loanaccount.data.LoanDelinquencyData;
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
@@ -29,9 +31,10 @@ public interface LoanDelinquencyDomainService {
* Charge back transaction
*
* @param loan
* @param effectiveDelinquencyList
*/
CollectionData getOverdueCollectionData(Loan loan);
CollectionData getOverdueCollectionData(Loan loan, List<LoanDelinquencyActionData> effectiveDelinquencyList);
LoanDelinquencyData getLoanDelinquencyData(Loan loan);
LoanDelinquencyData getLoanDelinquencyData(Loan loan, List<LoanDelinquencyActionData> effectiveDelinquencyList);
}
@@ -24,9 +24,12 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
import org.apache.fineract.portfolio.delinquency.helper.DelinquencyEffectivePauseHelper;
import org.apache.fineract.portfolio.delinquency.validator.LoanDelinquencyActionData;
import org.apache.fineract.portfolio.loanaccount.data.CollectionData;
import org.apache.fineract.portfolio.loanaccount.data.LoanDelinquencyData;
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
@@ -35,11 +38,14 @@ import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
import org.springframework.transaction.annotation.Transactional;
@Slf4j
@RequiredArgsConstructor
public class LoanDelinquencyDomainServiceImpl implements LoanDelinquencyDomainService {
private final DelinquencyEffectivePauseHelper delinquencyEffectivePauseHelper;
@Override
@Transactional(readOnly = true)
public CollectionData getOverdueCollectionData(final Loan loan) {
public CollectionData getOverdueCollectionData(final Loan loan, List<LoanDelinquencyActionData> effectiveDelinquencyList) {
final LocalDate businessDate = DateUtils.getBusinessLocalDate();
final MonetaryCurrency loanCurrency = loan.getCurrency();
@@ -99,7 +105,7 @@ public class LoanDelinquencyDomainServiceImpl implements LoanDelinquencyDomainSe
collectionData.setDelinquentDays(0L);
Long delinquentDays = overdueDays - graceDays;
if (delinquentDays > 0) {
collectionData.setDelinquentDays(delinquentDays);
calculateDelinquentDays(effectiveDelinquencyList, businessDate, collectionData, delinquentDays);
}
log.debug("Result: {}", collectionData.toString());
@@ -107,7 +113,7 @@ public class LoanDelinquencyDomainServiceImpl implements LoanDelinquencyDomainSe
}
@Override
public LoanDelinquencyData getLoanDelinquencyData(final Loan loan) {
public LoanDelinquencyData getLoanDelinquencyData(final Loan loan, List<LoanDelinquencyActionData> effectiveDelinquencyList) {
final LocalDate businessDate = DateUtils.getBusinessLocalDate();
LocalDate overdueSinceDate = null;
@@ -121,7 +127,7 @@ public class LoanDelinquencyDomainServiceImpl implements LoanDelinquencyDomainSe
for (LoanRepaymentScheduleInstallment installment : loan.getRepaymentScheduleInstallments()) {
CollectionData installmentCollectionData = CollectionData.template();
if (!installment.isObligationsMet()) {
installmentCollectionData = getInstallmentOverdueCollectionData(loan, installment);
installmentCollectionData = getInstallmentOverdueCollectionData(loan, installment, effectiveDelinquencyList);
outstandingAmount = outstandingAmount.add(installmentCollectionData.getDelinquentAmount());
// Get the oldest overdue installment if exists
if (DateUtils.isBefore(installment.getDueDate(), businessDate)) {
@@ -165,12 +171,20 @@ public class LoanDelinquencyDomainServiceImpl implements LoanDelinquencyDomainSe
collectionData.setDelinquentDays(0L);
Long delinquentDays = overdueDays - graceDays;
if (delinquentDays > 0) {
collectionData.setDelinquentDays(delinquentDays);
calculateDelinquentDays(effectiveDelinquencyList, businessDate, collectionData, delinquentDays);
}
return new LoanDelinquencyData(collectionData, loanInstallmentsCollectionData);
}
private CollectionData getInstallmentOverdueCollectionData(final Loan loan, final LoanRepaymentScheduleInstallment installment) {
private void calculateDelinquentDays(List<LoanDelinquencyActionData> effectiveDelinquencyList, LocalDate businessDate,
CollectionData collectionData, Long delinquentDays) {
Long pausedDays = delinquencyEffectivePauseHelper.getPausedDaysBeforeDate(effectiveDelinquencyList, businessDate);
Long calculatedDelinquentDays = delinquentDays - pausedDays;
collectionData.setDelinquentDays(calculatedDelinquentDays > 0 ? calculatedDelinquentDays : 0L);
}
private CollectionData getInstallmentOverdueCollectionData(final Loan loan, final LoanRepaymentScheduleInstallment installment,
List<LoanDelinquencyActionData> effectiveDelinquencyList) {
final LocalDate businessDate = DateUtils.getBusinessLocalDate();
LocalDate overdueSinceDate = null;
CollectionData collectionData = CollectionData.template();
@@ -193,7 +207,6 @@ public class LoanDelinquencyDomainServiceImpl implements LoanDelinquencyDomainSe
Long overdueDays = 0L;
if (overdueSinceDate != null) {
// TODO : Changes for considering paused delinquency days for overdue days calculation
overdueDays = DateUtils.getDifferenceInDays(overdueSinceDate, businessDate);
if (overdueDays < 0) {
overdueDays = 0L;
@@ -205,7 +218,7 @@ public class LoanDelinquencyDomainServiceImpl implements LoanDelinquencyDomainSe
collectionData.setDelinquentDays(0L);
Long delinquentDays = overdueDays;
if (delinquentDays > 0) {
collectionData.setDelinquentDays(delinquentDays);
calculateDelinquentDays(effectiveDelinquencyList, businessDate, collectionData, delinquentDays);
}
return collectionData;
@@ -25,6 +25,7 @@ import org.apache.fineract.portfolio.delinquency.domain.DelinquencyRangeReposito
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;
@@ -55,10 +56,11 @@ public class DelinquencyConfiguration {
LoanDelinquencyTagMapper mapperLoanDelinquencyTagHistory, LoanRepository loanRepository,
LoanDelinquencyDomainService loanDelinquencyDomainService,
LoanInstallmentDelinquencyTagRepository repositoryLoanInstallmentDelinquencyTag,
LoanDelinquencyActionRepository loanDelinquencyActionRepository) {
LoanDelinquencyActionRepository loanDelinquencyActionRepository,
DelinquencyEffectivePauseHelper delinquencyEffectivePauseHelper) {
return new DelinquencyReadPlatformServiceImpl(repositoryRange, repositoryBucket, repositoryLoanDelinquencyTagHistory, mapperRange,
mapperBucket, mapperLoanDelinquencyTagHistory, loanRepository, loanDelinquencyDomainService,
repositoryLoanInstallmentDelinquencyTag, loanDelinquencyActionRepository);
repositoryLoanInstallmentDelinquencyTag, loanDelinquencyActionRepository, delinquencyEffectivePauseHelper);
}
@Bean
@@ -70,17 +72,18 @@ public class DelinquencyConfiguration {
LoanProductRepository loanProductRepository, BusinessEventNotifierService businessEventNotifierService,
LoanDelinquencyDomainService loanDelinquencyDomainService,
LoanInstallmentDelinquencyTagRepository loanInstallmentDelinquencyTagRepository,
LoanDelinquencyActionRepository loanDelinquencyActionRepository,
DelinquencyActionParseAndValidator delinquencyActionParseAndValidator) {
DelinquencyReadPlatformService delinquencyReadPlatformService, LoanDelinquencyActionRepository loanDelinquencyActionRepository,
DelinquencyActionParseAndValidator delinquencyActionParseAndValidator,
DelinquencyEffectivePauseHelper delinquencyEffectivePauseHelper) {
return new DelinquencyWritePlatformServiceImpl(dataValidatorBucket, dataValidatorRange, repositoryRange, repositoryBucket,
repositoryBucketMappings, loanDelinquencyTagRepository, loanRepository, loanProductRepository, businessEventNotifierService,
loanDelinquencyDomainService, loanInstallmentDelinquencyTagRepository, loanDelinquencyActionRepository,
delinquencyActionParseAndValidator);
loanDelinquencyDomainService, loanInstallmentDelinquencyTagRepository, delinquencyReadPlatformService,
loanDelinquencyActionRepository, delinquencyActionParseAndValidator, delinquencyEffectivePauseHelper);
}
@Bean
@ConditionalOnMissingBean(LoanDelinquencyDomainService.class)
public LoanDelinquencyDomainService loanDelinquencyDomainService() {
return new LoanDelinquencyDomainServiceImpl();
public LoanDelinquencyDomainService loanDelinquencyDomainService(DelinquencyEffectivePauseHelper delinquencyEffectivePauseHelper) {
return new LoanDelinquencyDomainServiceImpl(delinquencyEffectivePauseHelper);
}
}
@@ -18,7 +18,6 @@
*/
package org.apache.fineract.portfolio.delinquency.validator;
import static org.apache.fineract.portfolio.delinquency.domain.DelinquencyAction.RESUME;
import static org.apache.fineract.portfolio.delinquency.validator.DelinquencyActionParameters.ACTION;
import static org.apache.fineract.portfolio.delinquency.validator.DelinquencyActionParameters.END_DATE;
import static org.apache.fineract.portfolio.delinquency.validator.DelinquencyActionParameters.START_DATE;
@@ -26,11 +25,7 @@ import static org.apache.fineract.portfolio.delinquency.validator.DelinquencyAct
import com.google.gson.JsonElement;
import jakarta.validation.constraints.NotNull;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
@@ -42,6 +37,7 @@ import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.infrastructure.core.validator.ParseAndValidator;
import org.apache.fineract.portfolio.delinquency.domain.DelinquencyAction;
import org.apache.fineract.portfolio.delinquency.domain.LoanDelinquencyAction;
import org.apache.fineract.portfolio.delinquency.helper.DelinquencyEffectivePauseHelper;
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
import org.springframework.stereotype.Component;
@@ -50,10 +46,12 @@ import org.springframework.stereotype.Component;
public class DelinquencyActionParseAndValidator extends ParseAndValidator {
private final FromJsonHelper jsonHelper;
private final DelinquencyEffectivePauseHelper delinquencyEffectivePauseHelper;
public LoanDelinquencyAction validateAndParseUpdate(@NotNull final JsonCommand command, Loan loan,
List<LoanDelinquencyAction> savedDelinquencyActions, LocalDate businessDate) {
List<LoanDelinquencyActionData> effectiveDelinquencyList = calculateEffectiveDelinquencyList(savedDelinquencyActions);
List<LoanDelinquencyActionData> effectiveDelinquencyList = delinquencyEffectivePauseHelper
.calculateEffectiveDelinquencyList(savedDelinquencyActions);
LoanDelinquencyAction parsedDelinquencyAction = parseCommand(command);
validateLoanIsActive(loan);
if (DelinquencyAction.PAUSE.equals(parsedDelinquencyAction.getAction())) {
@@ -78,34 +76,6 @@ public class DelinquencyActionParseAndValidator extends ParseAndValidator {
}
}
private List<LoanDelinquencyActionData> calculateEffectiveDelinquencyList(List<LoanDelinquencyAction> savedDelinquencyActions) {
// partition them based on type
Map<DelinquencyAction, List<LoanDelinquencyAction>> partitioned = savedDelinquencyActions.stream()
.collect(Collectors.groupingBy(LoanDelinquencyAction::getAction));
List<LoanDelinquencyActionData> effective = new ArrayList<>();
List<LoanDelinquencyAction> pauses = partitioned.get(DelinquencyAction.PAUSE);
if (pauses != null && pauses.size() > 0) {
for (LoanDelinquencyAction loanDelinquencyAction : pauses) {
Optional<LoanDelinquencyAction> resume = findMatchingResume(loanDelinquencyAction, partitioned.get(RESUME));
LoanDelinquencyActionData loanDelinquencyActionData = new LoanDelinquencyActionData(loanDelinquencyAction);
resume.ifPresent(r -> loanDelinquencyActionData.setEndDate(r.getStartDate()));
effective.add(loanDelinquencyActionData);
}
}
return effective;
}
private Optional<LoanDelinquencyAction> findMatchingResume(LoanDelinquencyAction pause, List<LoanDelinquencyAction> resumes) {
if (resumes != null && resumes.size() > 0) {
for (LoanDelinquencyAction resume : resumes) {
if (!pause.getStartDate().isAfter(resume.getStartDate()) && !resume.getStartDate().isAfter(pause.getEndDate())) {
return Optional.of(resume);
}
}
}
return Optional.empty();
}
private void validateResumeShouldBeOnActivePause(LoanDelinquencyAction parsedDelinquencyAction,
List<LoanDelinquencyActionData> savedDelinquencyActions) {
boolean match = savedDelinquencyActions.stream()
@@ -20,10 +20,12 @@ package org.apache.fineract.portfolio.loanaccount.domain;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder;
import org.apache.fineract.infrastructure.core.domain.ExternalId;
import org.apache.fineract.portfolio.delinquency.validator.LoanDelinquencyActionData;
import org.apache.fineract.portfolio.loanaccount.data.HolidayDetailDTO;
import org.apache.fineract.portfolio.paymentdetail.domain.PaymentDetail;
@@ -72,6 +74,8 @@ public interface LoanAccountDomainService {
*/
void setLoanDelinquencyTag(Loan loan, LocalDate transactionDate);
void setLoanDelinquencyTag(Loan loan, LocalDate transactionDate, List<LoanDelinquencyActionData> effectiveDelinquencyList);
LoanTransaction makeRepayment(LoanTransactionType repaymentTransactionType, Loan loan, LocalDate transactionDate,
BigDecimal transactionAmount, PaymentDetail paymentDetail, String noteText, ExternalId txnExternalId,
boolean isRecoveryRepayment, String chargeRefundChargeType, boolean isAccountTransfer, HolidayDetailDTO holidayDetailDto,
@@ -85,7 +85,11 @@ import org.apache.fineract.portfolio.accountdetails.domain.AccountType;
import org.apache.fineract.portfolio.client.domain.Client;
import org.apache.fineract.portfolio.client.exception.ClientNotActiveException;
import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType;
import org.apache.fineract.portfolio.delinquency.domain.LoanDelinquencyAction;
import org.apache.fineract.portfolio.delinquency.helper.DelinquencyEffectivePauseHelper;
import org.apache.fineract.portfolio.delinquency.service.DelinquencyReadPlatformService;
import org.apache.fineract.portfolio.delinquency.service.DelinquencyWritePlatformService;
import org.apache.fineract.portfolio.delinquency.validator.LoanDelinquencyActionData;
import org.apache.fineract.portfolio.group.domain.Group;
import org.apache.fineract.portfolio.group.exception.GroupNotActiveException;
import org.apache.fineract.portfolio.loanaccount.data.HolidayDetailDTO;
@@ -135,6 +139,8 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
private final ExternalIdFactory externalIdFactory;
private final ReplayedTransactionBusinessEventService replayedTransactionBusinessEventService;
private final LoanAccrualTransactionBusinessEventService loanAccrualTransactionBusinessEventService;
private final DelinquencyEffectivePauseHelper delinquencyEffectivePauseHelper;
private final DelinquencyReadPlatformService delinquencyReadPlatformService;
@Transactional
@Override
@@ -575,12 +581,30 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
@Override
public void setLoanDelinquencyTag(final Loan loan, final LocalDate transactionDate) {
LoanScheduleDelinquencyData loanDelinquencyData = new LoanScheduleDelinquencyData(loan.getId(), transactionDate, null, loan);
loanDelinquencyData = this.delinquencyWritePlatformService.calculateDelinquencyData(loanDelinquencyData);
final List<LoanDelinquencyAction> savedDelinquencyList = delinquencyReadPlatformService
.retrieveLoanDelinquencyActions(loan.getId());
List<LoanDelinquencyActionData> effectiveDelinquencyList = delinquencyEffectivePauseHelper
.calculateEffectiveDelinquencyList(savedDelinquencyList);
loanDelinquencyData = this.delinquencyWritePlatformService.calculateDelinquencyData(loanDelinquencyData, effectiveDelinquencyList);
log.debug("Processing Loan {} with {} overdue days since date {}", loanDelinquencyData.getLoanId(),
loanDelinquencyData.getOverdueDays(), loanDelinquencyData.getOverdueSinceDate());
// Set or Unset the Delinquency Classification Tag
if (loanDelinquencyData.getOverdueDays() > 0) {
this.delinquencyWritePlatformService.applyDelinquencyTagToLoan(loanDelinquencyData);
this.delinquencyWritePlatformService.applyDelinquencyTagToLoan(loanDelinquencyData, effectiveDelinquencyList);
} else {
this.delinquencyWritePlatformService.removeDelinquencyTagToLoan(loanDelinquencyData.getLoan());
}
}
@Override
public void setLoanDelinquencyTag(Loan loan, LocalDate transactionDate, List<LoanDelinquencyActionData> effectiveDelinquencyList) {
LoanScheduleDelinquencyData loanDelinquencyData = new LoanScheduleDelinquencyData(loan.getId(), transactionDate, null, loan);
loanDelinquencyData = this.delinquencyWritePlatformService.calculateDelinquencyData(loanDelinquencyData, effectiveDelinquencyList);
log.debug("Processing Loan {} with {} overdue days since date {}", loanDelinquencyData.getLoanId(),
loanDelinquencyData.getOverdueDays(), loanDelinquencyData.getOverdueSinceDate());
// Set or Unset the Delinquency Classification Tag
if (loanDelinquencyData.getOverdueDays() > 0) {
this.delinquencyWritePlatformService.applyDelinquencyTagToLoan(loanDelinquencyData, effectiveDelinquencyList);
} else {
this.delinquencyWritePlatformService.removeDelinquencyTagToLoan(loanDelinquencyData.getLoan());
}
@@ -20,6 +20,8 @@ package org.apache.fineract.portfolio.loanaccount.jobs.setloandelinquencytags;
import lombok.AllArgsConstructor;
import org.apache.fineract.infrastructure.jobs.service.JobName;
import org.apache.fineract.portfolio.delinquency.helper.DelinquencyEffectivePauseHelper;
import org.apache.fineract.portfolio.delinquency.service.DelinquencyReadPlatformService;
import org.apache.fineract.portfolio.delinquency.service.DelinquencyWritePlatformService;
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallmentRepository;
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository;
@@ -42,6 +44,10 @@ public class SetLoanDelinquencyTagsConfig {
private JobRepository jobRepository;
@Autowired
private PlatformTransactionManager transactionManager;
@Autowired
private DelinquencyEffectivePauseHelper delinquencyEffectivePauseHelper;
@Autowired
private DelinquencyReadPlatformService delinquencyReadPlatformService;
private DelinquencyWritePlatformService delinquencyWritePlatformService;
private LoanRepaymentScheduleInstallmentRepository loanRepaymentScheduleInstallmentRepository;
@@ -62,7 +68,7 @@ public class SetLoanDelinquencyTagsConfig {
@Bean
public SetLoanDelinquencyTagsTasklet setLoanDelinquencyTagsTasklet() {
return new SetLoanDelinquencyTagsTasklet(delinquencyWritePlatformService, loanRepaymentScheduleInstallmentRepository,
loanTransactionRepository);
loanTransactionRepository, delinquencyEffectivePauseHelper, delinquencyReadPlatformService);
}
}
@@ -27,7 +27,11 @@ import lombok.extern.slf4j.Slf4j;
import org.apache.fineract.infrastructure.core.domain.ActionContext;
import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil;
import org.apache.fineract.portfolio.delinquency.domain.LoanDelinquencyAction;
import org.apache.fineract.portfolio.delinquency.helper.DelinquencyEffectivePauseHelper;
import org.apache.fineract.portfolio.delinquency.service.DelinquencyReadPlatformService;
import org.apache.fineract.portfolio.delinquency.service.DelinquencyWritePlatformService;
import org.apache.fineract.portfolio.delinquency.validator.LoanDelinquencyActionData;
import org.apache.fineract.portfolio.loanaccount.data.LoanScheduleDelinquencyData;
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallmentRepository;
import org.apache.fineract.portfolio.loanaccount.domain.LoanStatus;
@@ -45,6 +49,8 @@ public class SetLoanDelinquencyTagsTasklet implements Tasklet {
private final DelinquencyWritePlatformService delinquencyWritePlatformService;
private final LoanRepaymentScheduleInstallmentRepository loanRepaymentScheduleInstallmentRepository;
private final LoanTransactionRepository loanTransactionRepository;
private final DelinquencyEffectivePauseHelper delinquencyEffectivePauseHelper;
private final DelinquencyReadPlatformService delinquencyReadPlatformService;
@Override
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
@@ -80,12 +86,18 @@ public class SetLoanDelinquencyTagsTasklet implements Tasklet {
log.debug("Were found {} items", loanScheduleDelinquencyData.size());
for (LoanScheduleDelinquencyData loanDelinquencyData : loanScheduleDelinquencyData) {
// Set the data used by Delinquency Classification method
loanDelinquencyData = this.delinquencyWritePlatformService.calculateDelinquencyData(loanDelinquencyData);
List<LoanDelinquencyAction> savedDelinquencyList = delinquencyReadPlatformService
.retrieveLoanDelinquencyActions(loanDelinquencyData.getLoanId());
List<LoanDelinquencyActionData> effectiveDelinquencyList = delinquencyEffectivePauseHelper
.calculateEffectiveDelinquencyList(savedDelinquencyList);
loanDelinquencyData = this.delinquencyWritePlatformService.calculateDelinquencyData(loanDelinquencyData,
effectiveDelinquencyList);
log.debug("Processing Loan {} with {} overdue days since date {}", loanDelinquencyData.getLoanId(),
loanDelinquencyData.getOverdueDays(), loanDelinquencyData.getOverdueSinceDate());
// Set or Unset the Delinquency Classification Tag
if (loanDelinquencyData.getOverdueDays() > 0) {
this.delinquencyWritePlatformService.applyDelinquencyTagToLoan(loanDelinquencyData);
this.delinquencyWritePlatformService.applyDelinquencyTagToLoan(loanDelinquencyData, effectiveDelinquencyList);
} else {
this.delinquencyWritePlatformService.removeDelinquencyTagToLoan(loanDelinquencyData.getLoan());
}
@@ -24,6 +24,7 @@ import static org.junit.Assert.assertTrue;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyList;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.verify;
@@ -41,6 +42,9 @@ import org.apache.fineract.infrastructure.core.domain.ActionContext;
import org.apache.fineract.infrastructure.core.domain.ExternalId;
import org.apache.fineract.infrastructure.core.domain.FineractPlatformTenant;
import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil;
import org.apache.fineract.infrastructure.event.business.service.BusinessEventNotifierService;
import org.apache.fineract.portfolio.delinquency.helper.DelinquencyEffectivePauseHelper;
import org.apache.fineract.portfolio.delinquency.service.DelinquencyReadPlatformService;
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
import org.apache.fineract.portfolio.loanaccount.domain.LoanAccountDomainService;
import org.junit.jupiter.api.BeforeEach;
@@ -61,6 +65,12 @@ public class SetLoanDelinquencyTagsBusinessStepTest {
*/
@Mock
private LoanAccountDomainService loanAccountDomainService;
@Mock
private DelinquencyEffectivePauseHelper delinquencyEffectivePauseHelper;
@Mock
private DelinquencyReadPlatformService delinquencyReadPlatformService;
@Mock
private BusinessEventNotifierService businessEventNotifierService;
/**
* The class under test.
@@ -84,9 +94,10 @@ public class SetLoanDelinquencyTagsBusinessStepTest {
public void setUp() {
ThreadLocalContextUtil.setTenant(new FineractPlatformTenant(1L, "default", "Default", "Asia/Kolkata", null));
ThreadLocalContextUtil.setActionContext(ActionContext.DEFAULT);
ThreadLocalContextUtil
.setBusinessDates(new HashMap<>(Map.of(BusinessDateType.BUSINESS_DATE, LocalDate.now(ZoneId.systemDefault()))));
underTest = new SetLoanDelinquencyTagsBusinessStep(loanAccountDomainService);
ThreadLocalContextUtil.setBusinessDates(new HashMap<>(Map.of(BusinessDateType.BUSINESS_DATE, LocalDate.now(ZoneId.systemDefault()),
BusinessDateType.COB_DATE, LocalDate.now(ZoneId.systemDefault()))));
underTest = new SetLoanDelinquencyTagsBusinessStep(loanAccountDomainService, delinquencyEffectivePauseHelper,
delinquencyReadPlatformService, businessEventNotifierService);
}
/**
@@ -98,14 +109,14 @@ public class SetLoanDelinquencyTagsBusinessStepTest {
@Test
public void testExecuteSuccessScenario() throws Exception {
// given
doNothing().when(loanAccountDomainService).setLoanDelinquencyTag(any(Loan.class), any(LocalDate.class));
doNothing().when(loanAccountDomainService).setLoanDelinquencyTag(any(Loan.class), any(LocalDate.class), anyList());
Loan loanForProcessing = createLoan();
// when
Loan processedLoan = underTest.execute(loanForProcessing);
// then
verify(loanAccountDomainService).setLoanDelinquencyTag(any(Loan.class), any(LocalDate.class));
verify(loanAccountDomainService).setLoanDelinquencyTag(any(Loan.class), any(LocalDate.class), anyList());
assertEquals(processedLoan, loanForProcessing);
}
@@ -137,14 +148,15 @@ public class SetLoanDelinquencyTagsBusinessStepTest {
@Test
public void testExecuteWhenSetLoanDelinquencyTagFails() throws Exception {
// given
doThrow(new RuntimeException()).when(loanAccountDomainService).setLoanDelinquencyTag(any(Loan.class), any(LocalDate.class));
doThrow(new RuntimeException()).when(loanAccountDomainService).setLoanDelinquencyTag(any(Loan.class), any(LocalDate.class),
anyList());
Loan loanForProcessing = createLoan();
// when
final Throwable thrownException = assertThrows(RuntimeException.class, () -> underTest.execute(loanForProcessing));
// then
verify(loanAccountDomainService).setLoanDelinquencyTag(any(Loan.class), any(LocalDate.class));
verify(loanAccountDomainService).setLoanDelinquencyTag(any(Loan.class), any(LocalDate.class), anyList());
assertTrue(thrownException.getClass().isAssignableFrom(RuntimeException.class));
}
@@ -0,0 +1,82 @@
/**
* 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.helper;
import static java.time.Month.JANUARY;
import static org.apache.fineract.portfolio.delinquency.domain.DelinquencyAction.PAUSE;
import static org.apache.fineract.portfolio.delinquency.domain.DelinquencyAction.RESUME;
import static org.junit.jupiter.api.Assertions.assertEquals;
import java.time.LocalDate;
import java.util.List;
import org.apache.fineract.portfolio.delinquency.domain.LoanDelinquencyAction;
import org.apache.fineract.portfolio.delinquency.validator.LoanDelinquencyActionData;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.junit.jupiter.MockitoExtension;
@ExtendWith(MockitoExtension.class)
class DelinquencyEffectivePauseHelperTest {
@InjectMocks
private DelinquencyEffectivePauseHelperImpl underTest;
@Test
public void testMultiplePausesWithoutResumeActionCurrentlyInPauseFirstDay() {
// given
List<LoanDelinquencyAction> delinquencyActions = List.of(
new LoanDelinquencyAction(null, PAUSE, LocalDate.of(2023, JANUARY, 10), LocalDate.of(2023, JANUARY, 11)),
new LoanDelinquencyAction(null, PAUSE, LocalDate.of(2023, JANUARY, 12), LocalDate.of(2023, JANUARY, 13)),
new LoanDelinquencyAction(null, PAUSE, LocalDate.of(2023, JANUARY, 15), LocalDate.of(2023, JANUARY, 20)));
// when
List<LoanDelinquencyActionData> effectivePeriods = underTest.calculateEffectiveDelinquencyList(delinquencyActions);
// then
assertEquals(effectivePeriods.get(0).getStartDate(), LocalDate.of(2023, 1, 10));
assertEquals(effectivePeriods.get(0).getEndDate(), LocalDate.of(2023, 1, 11));
assertEquals(effectivePeriods.get(1).getStartDate(), LocalDate.of(2023, 1, 12));
assertEquals(effectivePeriods.get(1).getEndDate(), LocalDate.of(2023, 1, 13));
assertEquals(effectivePeriods.get(2).getStartDate(), LocalDate.of(2023, 1, 15));
assertEquals(effectivePeriods.get(2).getEndDate(), LocalDate.of(2023, 1, 20));
}
@Test
public void testResumeIsAppliedToOneOfThePauseNotActive() {
// given
List<LoanDelinquencyAction> delinquencyActions = List.of(
new LoanDelinquencyAction(null, PAUSE, LocalDate.of(2023, JANUARY, 10), LocalDate.of(2023, JANUARY, 20)),
new LoanDelinquencyAction(null, RESUME, LocalDate.of(2023, JANUARY, 11), null),
new LoanDelinquencyAction(null, PAUSE, LocalDate.of(2023, JANUARY, 13), LocalDate.of(2023, JANUARY, 14)),
new LoanDelinquencyAction(null, PAUSE, LocalDate.of(2023, JANUARY, 15), LocalDate.of(2023, JANUARY, 20)));
// when
List<LoanDelinquencyActionData> effectivePeriods = underTest.calculateEffectiveDelinquencyList(delinquencyActions);
// then
assertEquals(effectivePeriods.get(0).getStartDate(), LocalDate.of(2023, 1, 10));
assertEquals(effectivePeriods.get(0).getEndDate(), LocalDate.of(2023, 1, 11));
assertEquals(effectivePeriods.get(1).getStartDate(), LocalDate.of(2023, 1, 13));
assertEquals(effectivePeriods.get(1).getEndDate(), LocalDate.of(2023, 1, 14));
assertEquals(effectivePeriods.get(2).getStartDate(), LocalDate.of(2023, 1, 15));
assertEquals(effectivePeriods.get(2).getEndDate(), LocalDate.of(2023, 1, 20));
}
}
@@ -20,7 +20,6 @@ package org.apache.fineract.portfolio.delinquency.service;
import static java.time.Month.JANUARY;
import static org.apache.fineract.portfolio.delinquency.domain.DelinquencyAction.PAUSE;
import static org.apache.fineract.portfolio.delinquency.domain.DelinquencyAction.RESUME;
import java.time.LocalDate;
import java.util.Arrays;
@@ -35,6 +34,7 @@ import org.apache.fineract.portfolio.delinquency.domain.LoanInstallmentDelinquen
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.validator.LoanDelinquencyActionData;
import org.apache.fineract.portfolio.loanaccount.data.CollectionData;
import org.apache.fineract.portfolio.loanaccount.data.DelinquencyPausePeriod;
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepository;
@@ -82,22 +82,25 @@ class DelinquencyReadPlatformServiceImplTest {
public void testNoEnrichmentWhenThereIsNoDelinquencyAction() {
// given
CollectionData collectionData = CollectionData.template();
Collection<LoanDelinquencyAction> delinquencyActions = List.of();
Collection<LoanDelinquencyActionData> delinquencyActions = List.of();
// when
underTest.enrichWithDelinquencyPausePeriodInfo(collectionData, delinquencyActions, LocalDate.of(2023, JANUARY, 12));
Assertions.assertNull(collectionData.getDelinquencyPausePeriods());
Assertions.assertTrue(collectionData.getDelinquencyPausePeriods().isEmpty());
}
@Test
public void testMultiplePausesWithoutResumeActionCurrentlyInPauseFirstDay() {
// given
CollectionData collectionData = CollectionData.template();
Collection<LoanDelinquencyAction> delinquencyActions = List.of(
new LoanDelinquencyAction(null, PAUSE, LocalDate.of(2023, JANUARY, 10), LocalDate.of(2023, JANUARY, 11)),
new LoanDelinquencyAction(null, PAUSE, LocalDate.of(2023, JANUARY, 12), LocalDate.of(2023, JANUARY, 13)),
new LoanDelinquencyAction(null, PAUSE, LocalDate.of(2023, JANUARY, 15), LocalDate.of(2023, JANUARY, 20)));
Collection<LoanDelinquencyActionData> delinquencyActions = List.of(
new LoanDelinquencyActionData(
new LoanDelinquencyAction(null, PAUSE, LocalDate.of(2023, JANUARY, 10), LocalDate.of(2023, JANUARY, 11))),
new LoanDelinquencyActionData(
new LoanDelinquencyAction(null, PAUSE, LocalDate.of(2023, JANUARY, 12), LocalDate.of(2023, JANUARY, 13))),
new LoanDelinquencyActionData(
new LoanDelinquencyAction(null, PAUSE, LocalDate.of(2023, JANUARY, 15), LocalDate.of(2023, JANUARY, 20))));
// when
underTest.enrichWithDelinquencyPausePeriodInfo(collectionData, delinquencyActions, LocalDate.of(2023, JANUARY, 12));
@@ -114,10 +117,13 @@ class DelinquencyReadPlatformServiceImplTest {
public void testMultiplePausesWithoutResumeActionCurrentlyInPauseLastDay() {
// given
CollectionData collectionData = CollectionData.template();
Collection<LoanDelinquencyAction> delinquencyActions = List.of(
new LoanDelinquencyAction(null, PAUSE, LocalDate.of(2023, JANUARY, 10), LocalDate.of(2023, JANUARY, 11)),
new LoanDelinquencyAction(null, PAUSE, LocalDate.of(2023, JANUARY, 12), LocalDate.of(2023, JANUARY, 13)),
new LoanDelinquencyAction(null, PAUSE, LocalDate.of(2023, JANUARY, 15), LocalDate.of(2023, JANUARY, 20)));
Collection<LoanDelinquencyActionData> delinquencyActions = List.of(
new LoanDelinquencyActionData(
new LoanDelinquencyAction(null, PAUSE, LocalDate.of(2023, JANUARY, 10), LocalDate.of(2023, JANUARY, 11))),
new LoanDelinquencyActionData(
new LoanDelinquencyAction(null, PAUSE, LocalDate.of(2023, JANUARY, 12), LocalDate.of(2023, JANUARY, 13))),
new LoanDelinquencyActionData(
new LoanDelinquencyAction(null, PAUSE, LocalDate.of(2023, JANUARY, 15), LocalDate.of(2023, JANUARY, 20))));
// when
underTest.enrichWithDelinquencyPausePeriodInfo(collectionData, delinquencyActions, LocalDate.of(2023, JANUARY, 13));
@@ -133,10 +139,13 @@ class DelinquencyReadPlatformServiceImplTest {
public void testMultiplePausesWithoutResumeActionCurrentBusinessDateBetweenStartAndEndDate() {
// given
CollectionData collectionData = CollectionData.template();
Collection<LoanDelinquencyAction> delinquencyActions = List.of(
new LoanDelinquencyAction(null, PAUSE, LocalDate.of(2023, JANUARY, 10), LocalDate.of(2023, JANUARY, 11)),
new LoanDelinquencyAction(null, PAUSE, LocalDate.of(2023, JANUARY, 12), LocalDate.of(2023, JANUARY, 14)),
new LoanDelinquencyAction(null, PAUSE, LocalDate.of(2023, JANUARY, 15), LocalDate.of(2023, JANUARY, 20)));
Collection<LoanDelinquencyActionData> delinquencyActions = List.of(
new LoanDelinquencyActionData(
new LoanDelinquencyAction(null, PAUSE, LocalDate.of(2023, JANUARY, 10), LocalDate.of(2023, JANUARY, 11))),
new LoanDelinquencyActionData(
new LoanDelinquencyAction(null, PAUSE, LocalDate.of(2023, JANUARY, 12), LocalDate.of(2023, JANUARY, 14))),
new LoanDelinquencyActionData(
new LoanDelinquencyAction(null, PAUSE, LocalDate.of(2023, JANUARY, 15), LocalDate.of(2023, JANUARY, 20))));
// when
underTest.enrichWithDelinquencyPausePeriodInfo(collectionData, delinquencyActions, LocalDate.of(2023, JANUARY, 13));
@@ -152,10 +161,13 @@ class DelinquencyReadPlatformServiceImplTest {
public void testMultiplePausesWithoutResumeCurrentBusinessDateIsNotOverlappingWithAnyOfThePauses() {
// given
CollectionData collectionData = CollectionData.template();
Collection<LoanDelinquencyAction> delinquencyActions = List.of(
new LoanDelinquencyAction(null, PAUSE, LocalDate.of(2023, JANUARY, 10), LocalDate.of(2023, JANUARY, 11)),
new LoanDelinquencyAction(null, PAUSE, LocalDate.of(2023, JANUARY, 13), LocalDate.of(2023, JANUARY, 14)),
new LoanDelinquencyAction(null, PAUSE, LocalDate.of(2023, JANUARY, 15), LocalDate.of(2023, JANUARY, 20)));
Collection<LoanDelinquencyActionData> delinquencyActions = List.of(
new LoanDelinquencyActionData(
new LoanDelinquencyAction(null, PAUSE, LocalDate.of(2023, JANUARY, 10), LocalDate.of(2023, JANUARY, 11))),
new LoanDelinquencyActionData(
new LoanDelinquencyAction(null, PAUSE, LocalDate.of(2023, JANUARY, 13), LocalDate.of(2023, JANUARY, 14))),
new LoanDelinquencyActionData(
new LoanDelinquencyAction(null, PAUSE, LocalDate.of(2023, JANUARY, 15), LocalDate.of(2023, JANUARY, 20))));
// when
underTest.enrichWithDelinquencyPausePeriodInfo(collectionData, delinquencyActions, LocalDate.of(2023, JANUARY, 12));
@@ -168,48 +180,6 @@ class DelinquencyReadPlatformServiceImplTest {
);
}
@Test
public void testResumeIsAppliedToOneOfThePauseNotActive() {
// given
CollectionData collectionData = CollectionData.template();
Collection<LoanDelinquencyAction> delinquencyActions = List.of(
new LoanDelinquencyAction(null, PAUSE, LocalDate.of(2023, JANUARY, 10), LocalDate.of(2023, JANUARY, 20)),
new LoanDelinquencyAction(null, RESUME, LocalDate.of(2023, JANUARY, 11), null),
new LoanDelinquencyAction(null, PAUSE, LocalDate.of(2023, JANUARY, 13), LocalDate.of(2023, JANUARY, 14)),
new LoanDelinquencyAction(null, PAUSE, LocalDate.of(2023, JANUARY, 15), LocalDate.of(2023, JANUARY, 20)));
// when
underTest.enrichWithDelinquencyPausePeriodInfo(collectionData, delinquencyActions, LocalDate.of(2023, JANUARY, 12));
// then
verifyPausePeriods(collectionData, //
pausePeriod(false, "2023-01-10", "2023-01-11"), //
pausePeriod(false, "2023-01-13", "2023-01-14"), //
pausePeriod(false, "2023-01-15", "2023-01-20") //
);
}
@Test
public void testResumeIsAppliedToOneOfThePauseActive() {
// given
CollectionData collectionData = CollectionData.template();
Collection<LoanDelinquencyAction> delinquencyActions = List.of(
new LoanDelinquencyAction(null, PAUSE, LocalDate.of(2023, JANUARY, 10), LocalDate.of(2023, JANUARY, 20)),
new LoanDelinquencyAction(null, RESUME, LocalDate.of(2023, JANUARY, 11), null),
new LoanDelinquencyAction(null, PAUSE, LocalDate.of(2023, JANUARY, 13), LocalDate.of(2023, JANUARY, 14)),
new LoanDelinquencyAction(null, PAUSE, LocalDate.of(2023, JANUARY, 15), LocalDate.of(2023, JANUARY, 20)));
// when
underTest.enrichWithDelinquencyPausePeriodInfo(collectionData, delinquencyActions, LocalDate.of(2023, JANUARY, 11));
// then
verifyPausePeriods(collectionData, //
pausePeriod(true, "2023-01-10", "2023-01-11"), //
pausePeriod(false, "2023-01-13", "2023-01-14"), //
pausePeriod(false, "2023-01-15", "2023-01-20") //
);
}
private void verifyPausePeriods(CollectionData collectionData, DelinquencyPausePeriod... pausePeriods) {
if (pausePeriods.length > 0) {
Assertions.assertEquals(Arrays.asList(pausePeriods), collectionData.getDelinquencyPausePeriods());
@@ -42,6 +42,7 @@ import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidati
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
import org.apache.fineract.portfolio.delinquency.domain.DelinquencyAction;
import org.apache.fineract.portfolio.delinquency.domain.LoanDelinquencyAction;
import org.apache.fineract.portfolio.delinquency.helper.DelinquencyEffectivePauseHelper;
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
import org.apache.fineract.portfolio.loanaccount.domain.LoanStatus;
import org.jetbrains.annotations.NotNull;
@@ -54,7 +55,9 @@ import org.mockito.Mockito;
class DelinquencyActionParseAndValidatorTest {
private final FromJsonHelper fromJsonHelper = new FromJsonHelper();
private final DelinquencyActionParseAndValidator underTest = new DelinquencyActionParseAndValidator(fromJsonHelper);
private final DelinquencyEffectivePauseHelper delinquencyEffectivePauseHelper = Mockito.mock(DelinquencyEffectivePauseHelper.class);
private final DelinquencyActionParseAndValidator underTest = new DelinquencyActionParseAndValidator(fromJsonHelper,
delinquencyEffectivePauseHelper);
private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("dd MMMM yyyy", Locale.US);
@Test
@@ -77,9 +80,12 @@ class DelinquencyActionParseAndValidatorTest {
Mockito.when(loan.getStatus()).thenReturn(LoanStatus.ACTIVE);
JsonCommand command = delinquencyAction("resume", "09 September 2022", null);
List<LoanDelinquencyAction> existing = List.of(loanDelinquencyAction(PAUSE, "05 September 2022", "15 September 2022"));
List<LoanDelinquencyActionData> effectiveList = List.of(loanDelinquencyActionData(existing.get(0)));
Mockito.when(delinquencyEffectivePauseHelper.calculateEffectiveDelinquencyList(existing)).thenReturn(effectiveList);
LoanDelinquencyAction parsedDelinquencyAction = underTest.validateAndParseUpdate(command, loan,
List.of(loanDelinquencyAction(PAUSE, "05 September 2022", "15 September 2022")), localDate("09 September 2022"));
LoanDelinquencyAction parsedDelinquencyAction = underTest.validateAndParseUpdate(command, loan, existing,
localDate("09 September 2022"));
Assertions.assertEquals(RESUME, parsedDelinquencyAction.getAction());
Assertions.assertEquals(localDate("09 September 2022"), parsedDelinquencyAction.getStartDate());
Assertions.assertNull(parsedDelinquencyAction.getEndDate());
@@ -92,6 +98,8 @@ class DelinquencyActionParseAndValidatorTest {
List<LoanDelinquencyAction> existing = List.of(loanDelinquencyAction(PAUSE, "14 September 2022", "22 September 2022"));
JsonCommand command = delinquencyAction("pause", "09 September 2022", "15 September 2022");
List<LoanDelinquencyActionData> effectiveList = List.of(loanDelinquencyActionData(existing.get(0)));
Mockito.when(delinquencyEffectivePauseHelper.calculateEffectiveDelinquencyList(existing)).thenReturn(effectiveList);
assertPlatformValidationException("Delinquency pause period cannot overlap with another pause period",
"loan-delinquency-action-overlapping",
@@ -105,6 +113,8 @@ class DelinquencyActionParseAndValidatorTest {
List<LoanDelinquencyAction> existing = List.of(loanDelinquencyAction(PAUSE, "14 September 2022", "22 September 2022"));
JsonCommand command = delinquencyAction("pause", "15 September 2022", "23 September 2022");
List<LoanDelinquencyActionData> effectiveList = List.of(loanDelinquencyActionData(existing.get(0)));
Mockito.when(delinquencyEffectivePauseHelper.calculateEffectiveDelinquencyList(existing)).thenReturn(effectiveList);
assertPlatformValidationException("Delinquency pause period cannot overlap with another pause period",
"loan-delinquency-action-overlapping",
@@ -118,6 +128,8 @@ class DelinquencyActionParseAndValidatorTest {
List<LoanDelinquencyAction> existing = List.of(loanDelinquencyAction(PAUSE, "15 September 2022", "22 September 2022"));
JsonCommand command = delinquencyAction("pause", "13 September 2022", "20 September 2022");
List<LoanDelinquencyActionData> effectiveList = List.of(loanDelinquencyActionData(existing.get(0)));
Mockito.when(delinquencyEffectivePauseHelper.calculateEffectiveDelinquencyList(existing)).thenReturn(effectiveList);
assertPlatformValidationException("Delinquency pause period cannot overlap with another pause period",
"loan-delinquency-action-overlapping",
@@ -339,6 +351,10 @@ class DelinquencyActionParseAndValidatorTest {
return new LoanDelinquencyAction(null, action, localDate(startTime), localDate(endTime));
}
private LoanDelinquencyActionData loanDelinquencyActionData(LoanDelinquencyAction loanDelinquencyAction) {
return new LoanDelinquencyActionData(loanDelinquencyAction);
}
private LoanDelinquencyAction loanDelinquencyAction(DelinquencyAction action, String startTime) {
return new LoanDelinquencyAction(null, action, localDate(startTime), null);
}
@@ -31,6 +31,7 @@ import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.ZoneId;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@@ -56,6 +57,7 @@ import org.apache.fineract.portfolio.delinquency.service.DelinquencyWritePlatfor
import org.apache.fineract.portfolio.delinquency.service.LoanDelinquencyDomainService;
import org.apache.fineract.portfolio.delinquency.validator.DelinquencyBucketParseAndValidator;
import org.apache.fineract.portfolio.delinquency.validator.DelinquencyRangeParseAndValidator;
import org.apache.fineract.portfolio.delinquency.validator.LoanDelinquencyActionData;
import org.apache.fineract.portfolio.loanaccount.data.CollectionData;
import org.apache.fineract.portfolio.loanaccount.data.LoanDelinquencyData;
import org.apache.fineract.portfolio.loanaccount.data.LoanScheduleDelinquencyData;
@@ -115,6 +117,7 @@ public class DelinquencyWritePlatformServiceRangeChangeEventTest {
ArgumentCaptor<LoanDelinquencyRangeChangeBusinessEvent> loanDeliquencyRangeChangeEvent = ArgumentCaptor
.forClass(LoanDelinquencyRangeChangeBusinessEvent.class);
// 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);
@@ -140,10 +143,11 @@ public class DelinquencyWritePlatformServiceRangeChangeEventTest {
when(loanForProcessing.hasDelinquencyBucket()).thenReturn(true);
when(loanForProcessing.isEnableInstallmentLevelDelinquency()).thenReturn(false);
when(loanDelinquencyTagRepository.findByLoanAndLiftedOnDate(any(), any())).thenReturn(Optional.empty());
when(loanDelinquencyDomainService.getLoanDelinquencyData(loanForProcessing)).thenReturn(loanDelinquencyData);
when(loanDelinquencyDomainService.getLoanDelinquencyData(loanForProcessing, effectiveDelinquencyList))
.thenReturn(loanDelinquencyData);
// when
underTest.applyDelinquencyTagToLoan(loanScheduleDelinquencyData);
underTest.applyDelinquencyTagToLoan(loanScheduleDelinquencyData, effectiveDelinquencyList);
// then
verify(loanDelinquencyTagRepository, times(1)).saveAllAndFlush(anyIterable());
@@ -155,6 +159,7 @@ public class DelinquencyWritePlatformServiceRangeChangeEventTest {
@Test
public void givenLoanAccountWithDelinquencyBucketWhenNoRangeChangeThenNoEventIsRaised() {
// given
final List<LoanDelinquencyActionData> effectiveDelinquencyList = Collections.emptyList();
Loan loanForProcessing = Mockito.mock(Loan.class);
LoanProduct loanProduct = Mockito.mock(LoanProduct.class);
@@ -181,10 +186,11 @@ public class DelinquencyWritePlatformServiceRangeChangeEventTest {
when(loanProduct.getDelinquencyBucket()).thenReturn(delinquencyBucket);
when(loanForProcessing.hasDelinquencyBucket()).thenReturn(true);
when(loanDelinquencyTagRepository.findByLoanAndLiftedOnDate(any(), any())).thenReturn(Optional.empty());
when(loanDelinquencyDomainService.getLoanDelinquencyData(loanForProcessing)).thenReturn(loanDelinquencyData);
when(loanDelinquencyDomainService.getLoanDelinquencyData(loanForProcessing, effectiveDelinquencyList))
.thenReturn(loanDelinquencyData);
// when
underTest.applyDelinquencyTagToLoan(loanScheduleDelinquencyData);
underTest.applyDelinquencyTagToLoan(loanScheduleDelinquencyData, effectiveDelinquencyList);
// then
verify(loanDelinquencyTagRepository, times(0)).saveAllAndFlush(anyIterable());
@@ -194,6 +200,7 @@ public class DelinquencyWritePlatformServiceRangeChangeEventTest {
@Test
public void givenLoanAccountWithNoDelinquencyBucketThenNoEventIsRaised() {
// given
final List<LoanDelinquencyActionData> effectiveDelinquencyList = Collections.emptyList();
Loan loanForProcessing = Mockito.mock(Loan.class);
LocalDate overDueSinceDate = DateUtils.getBusinessLocalDate();
@@ -203,7 +210,7 @@ public class DelinquencyWritePlatformServiceRangeChangeEventTest {
when(loanForProcessing.hasDelinquencyBucket()).thenReturn(false);
// when
underTest.applyDelinquencyTagToLoan(loanScheduleDelinquencyData);
underTest.applyDelinquencyTagToLoan(loanScheduleDelinquencyData, effectiveDelinquencyList);
// then
verify(loanDelinquencyTagRepository, times(0)).saveAllAndFlush(anyIterable());
@@ -218,6 +225,7 @@ public class DelinquencyWritePlatformServiceRangeChangeEventTest {
ArgumentCaptor<LoanDelinquencyRangeChangeBusinessEvent> loanDelinquencyRangeChangeEvent = ArgumentCaptor
.forClass(LoanDelinquencyRangeChangeBusinessEvent.class);
// 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);
@@ -260,12 +268,13 @@ public class DelinquencyWritePlatformServiceRangeChangeEventTest {
when(loanForProcessing.getRepaymentScheduleInstallments()).thenReturn(repaymentScheduleInstallments);
when(loanForProcessing.isEnableInstallmentLevelDelinquency()).thenReturn(true);
when(loanDelinquencyTagRepository.findByLoanAndLiftedOnDate(any(), any())).thenReturn(Optional.empty());
when(loanDelinquencyDomainService.getLoanDelinquencyData(loanForProcessing)).thenReturn(loanDelinquencyData);
when(loanDelinquencyDomainService.getLoanDelinquencyData(loanForProcessing, effectiveDelinquencyList))
.thenReturn(loanDelinquencyData);
when(loanInstallmentDelinquencyTagRepository.findByLoanAndInstallment(loanForProcessing, repaymentScheduleInstallments.get(0)))
.thenReturn(Optional.empty());
// when
underTest.applyDelinquencyTagToLoan(loanScheduleDelinquencyData);
underTest.applyDelinquencyTagToLoan(loanScheduleDelinquencyData, effectiveDelinquencyList);
// then
verify(loanDelinquencyTagRepository, times(1)).saveAllAndFlush(anyIterable());
@@ -293,6 +302,7 @@ public class DelinquencyWritePlatformServiceRangeChangeEventTest {
ArgumentCaptor<LoanDelinquencyRangeChangeBusinessEvent> loanDelinquencyRangeChangeEvent = ArgumentCaptor
.forClass(LoanDelinquencyRangeChangeBusinessEvent.class);
// 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);
@@ -338,12 +348,13 @@ public class DelinquencyWritePlatformServiceRangeChangeEventTest {
when(loanForProcessing.getRepaymentScheduleInstallments()).thenReturn(repaymentScheduleInstallments);
when(loanForProcessing.isEnableInstallmentLevelDelinquency()).thenReturn(true);
when(loanDelinquencyTagRepository.findByLoanAndLiftedOnDate(any(), any())).thenReturn(Optional.empty());
when(loanDelinquencyDomainService.getLoanDelinquencyData(loanForProcessing)).thenReturn(loanDelinquencyData);
when(loanDelinquencyDomainService.getLoanDelinquencyData(loanForProcessing, effectiveDelinquencyList))
.thenReturn(loanDelinquencyData);
when(loanInstallmentDelinquencyTagRepository.findByLoanAndInstallment(loanForProcessing, repaymentScheduleInstallments.get(0)))
.thenReturn(Optional.of(previousInstallmentTag));
// when
underTest.applyDelinquencyTagToLoan(loanScheduleDelinquencyData);
underTest.applyDelinquencyTagToLoan(loanScheduleDelinquencyData, effectiveDelinquencyList);
// then
verify(loanDelinquencyTagRepository, times(1)).saveAllAndFlush(anyIterable());
@@ -374,6 +385,7 @@ public class DelinquencyWritePlatformServiceRangeChangeEventTest {
ArgumentCaptor<LoanDelinquencyRangeChangeBusinessEvent> loanDelinquencyRangeChangeEvent = ArgumentCaptor
.forClass(LoanDelinquencyRangeChangeBusinessEvent.class);
final List<LoanDelinquencyActionData> effectiveDelinquencyList = Collections.emptyList();
// given
Loan loanForProcessing = Mockito.mock(Loan.class);
LoanProduct loanProduct = Mockito.mock(LoanProduct.class);
@@ -431,14 +443,15 @@ public class DelinquencyWritePlatformServiceRangeChangeEventTest {
when(loanForProcessing.getRepaymentScheduleInstallments()).thenReturn(repaymentScheduleInstallments);
when(loanForProcessing.isEnableInstallmentLevelDelinquency()).thenReturn(true);
when(loanDelinquencyTagRepository.findByLoanAndLiftedOnDate(any(), any())).thenReturn(Optional.empty());
when(loanDelinquencyDomainService.getLoanDelinquencyData(loanForProcessing)).thenReturn(loanDelinquencyData);
when(loanDelinquencyDomainService.getLoanDelinquencyData(loanForProcessing, effectiveDelinquencyList))
.thenReturn(loanDelinquencyData);
when(loanInstallmentDelinquencyTagRepository.findByLoanAndInstallment(loanForProcessing, repaymentScheduleInstallments.get(0)))
.thenReturn(Optional.of(previousInstallmentTag_1));
when(loanInstallmentDelinquencyTagRepository.findByLoanAndInstallment(loanForProcessing, repaymentScheduleInstallments.get(1)))
.thenReturn(Optional.of(previousInstallmentTag));
// when
underTest.applyDelinquencyTagToLoan(loanScheduleDelinquencyData);
underTest.applyDelinquencyTagToLoan(loanScheduleDelinquencyData, effectiveDelinquencyList);
// then
verify(loanDelinquencyTagRepository, times(1)).saveAllAndFlush(anyIterable());
@@ -42,7 +42,9 @@ import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil;
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
import org.apache.fineract.organisation.monetary.domain.Money;
import org.apache.fineract.organisation.monetary.domain.MoneyHelper;
import org.apache.fineract.portfolio.delinquency.helper.DelinquencyEffectivePauseHelper;
import org.apache.fineract.portfolio.delinquency.service.LoanDelinquencyDomainServiceImpl;
import org.apache.fineract.portfolio.delinquency.validator.LoanDelinquencyActionData;
import org.apache.fineract.portfolio.loanaccount.data.CollectionData;
import org.apache.fineract.portfolio.loanaccount.data.LoanDelinquencyData;
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
@@ -75,6 +77,8 @@ public class LoanDelinquencyDomainServiceTest {
private Loan loan;
@Mock
private LoanProduct loanProduct;
@Mock
private DelinquencyEffectivePauseHelper delinquencyEffectivePauseHelper;
@InjectMocks
private LoanDelinquencyDomainServiceImpl underTest;
@@ -107,6 +111,7 @@ public class LoanDelinquencyDomainServiceTest {
@Test
public void givenLoanAccountWithoutOverdueThenCalculateDelinquentData() {
// given
final List<LoanDelinquencyActionData> effectiveDelinquencyList = Collections.emptyList();
final LocalDate fromDate = businessDate.minusMonths(1);
final LocalDate dueDate = businessDate;
List<LoanRepaymentScheduleInstallment> repaymentScheduleInstallments = Arrays.asList(new LoanRepaymentScheduleInstallment(loan, 1,
@@ -118,7 +123,7 @@ public class LoanDelinquencyDomainServiceTest {
when(loan.getRepaymentScheduleInstallments()).thenReturn(repaymentScheduleInstallments);
when(loan.getCurrency()).thenReturn(currency);
CollectionData collectionData = underTest.getOverdueCollectionData(loan);
CollectionData collectionData = underTest.getOverdueCollectionData(loan, effectiveDelinquencyList);
// then
assertEquals(0L, collectionData.getDelinquentDays());
@@ -130,6 +135,7 @@ public class LoanDelinquencyDomainServiceTest {
@Test
public void givenLoanAccountWithOverdueThenCalculateDelinquentData() {
// given
final List<LoanDelinquencyActionData> effectiveDelinquencyList = Collections.emptyList();
final Long daysDiff = 2L;
final LocalDate fromDate = businessDate.minusMonths(1).minusDays(daysDiff);
final LocalDate dueDate = businessDate.minusDays(daysDiff);
@@ -143,8 +149,9 @@ public class LoanDelinquencyDomainServiceTest {
when(loan.getLoanTransactions(Mockito.any(Predicate.class))).thenReturn(Collections.emptyList());
when(loan.getLastLoanRepaymentScheduleInstallment()).thenReturn(repaymentScheduleInstallments.get(0));
when(loan.getCurrency()).thenReturn(currency);
when(delinquencyEffectivePauseHelper.getPausedDaysBeforeDate(effectiveDelinquencyList, businessDate)).thenReturn(0L);
CollectionData collectionData = underTest.getOverdueCollectionData(loan);
CollectionData collectionData = underTest.getOverdueCollectionData(loan, effectiveDelinquencyList);
// then
assertEquals(daysDiff, collectionData.getDelinquentDays());
@@ -156,6 +163,7 @@ public class LoanDelinquencyDomainServiceTest {
@Test
public void givenLoanAccountWithoutOverdueWithChargebackThenCalculateDelinquentData() {
// given
final List<LoanDelinquencyActionData> effectiveDelinquencyList = Collections.emptyList();
PaymentDetail paymentDetail = Mockito.mock(PaymentDetail.class);
Long daysDiff = 2L;
final LocalDate fromDate = businessDate.minusMonths(1).plusDays(daysDiff);
@@ -178,7 +186,7 @@ public class LoanDelinquencyDomainServiceTest {
when(loan.getRepaymentScheduleInstallments()).thenReturn(repaymentScheduleInstallments);
when(loan.getCurrency()).thenReturn(currency);
CollectionData collectionData = underTest.getOverdueCollectionData(loan);
CollectionData collectionData = underTest.getOverdueCollectionData(loan, effectiveDelinquencyList);
// then
assertEquals(0L, collectionData.getDelinquentDays());
@@ -190,6 +198,7 @@ public class LoanDelinquencyDomainServiceTest {
@Test
public void givenLoanInstallmentWithOverdueEnableInstallmentDelinquencyThenCalculateDelinquentData() {
// given
final List<LoanDelinquencyActionData> effectiveDelinquencyList = Collections.emptyList();
final Long daysDiff = 2L;
final LocalDate fromDate = businessDate.minusMonths(1).minusDays(daysDiff);
final LocalDate dueDate = businessDate.minusDays(daysDiff);
@@ -207,8 +216,9 @@ public class LoanDelinquencyDomainServiceTest {
when(loan.getLastLoanRepaymentScheduleInstallment()).thenReturn(repaymentScheduleInstallments.get(0));
when(loan.getCurrency()).thenReturn(currency);
when(loan.isEnableInstallmentLevelDelinquency()).thenReturn(true);
when(delinquencyEffectivePauseHelper.getPausedDaysBeforeDate(effectiveDelinquencyList, businessDate)).thenReturn(0L);
LoanDelinquencyData collectionData = underTest.getLoanDelinquencyData(loan);
LoanDelinquencyData collectionData = underTest.getLoanDelinquencyData(loan, effectiveDelinquencyList);
// then
assertNotNull(collectionData);
@@ -232,6 +242,7 @@ public class LoanDelinquencyDomainServiceTest {
public void givenLoanInstallmentWithoutOverdueWithChargebackAndEnableInstallmentDelinquencyThenCalculateDelinquentData() {
// given
final List<LoanDelinquencyActionData> effectiveDelinquencyList = Collections.emptyList();
PaymentDetail paymentDetail = Mockito.mock(PaymentDetail.class);
Long daysDiff = 2L;
final LocalDate fromDate = businessDate.minusMonths(1).plusDays(daysDiff);
@@ -255,8 +266,9 @@ public class LoanDelinquencyDomainServiceTest {
when(loan.isEnableInstallmentLevelDelinquency()).thenReturn(true);
when(loan.getCurrency()).thenReturn(currency);
when(loan.getLoanTransactions(Mockito.any(Predicate.class))).thenReturn(Arrays.asList(loanTransaction));
when(delinquencyEffectivePauseHelper.getPausedDaysBeforeDate(effectiveDelinquencyList, businessDate)).thenReturn(0L);
LoanDelinquencyData collectionData = underTest.getLoanDelinquencyData(loan);
LoanDelinquencyData collectionData = underTest.getLoanDelinquencyData(loan, effectiveDelinquencyList);
// then
assertNotNull(collectionData);
@@ -18,6 +18,8 @@
*/
package org.apache.fineract.integrationtests;
import static org.apache.fineract.portfolio.delinquency.domain.DelinquencyAction.PAUSE;
import static org.apache.fineract.portfolio.delinquency.domain.DelinquencyAction.RESUME;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
@@ -30,6 +32,7 @@ import io.restassured.builder.ResponseSpecBuilder;
import io.restassured.http.ContentType;
import io.restassured.specification.RequestSpecification;
import io.restassured.specification.ResponseSpecification;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.HashMap;
@@ -42,11 +45,13 @@ import org.apache.fineract.client.models.GetDelinquencyRangesResponse;
import org.apache.fineract.client.models.GetDelinquencyTagHistoryResponse;
import org.apache.fineract.client.models.GetLoanProductsProductIdResponse;
import org.apache.fineract.client.models.GetLoansLoanIdDelinquencySummary;
import org.apache.fineract.client.models.GetLoansLoanIdLoanInstallmentLevelDelinquency;
import org.apache.fineract.client.models.GetLoansLoanIdRepaymentPeriod;
import org.apache.fineract.client.models.GetLoansLoanIdRepaymentSchedule;
import org.apache.fineract.client.models.GetLoansLoanIdResponse;
import org.apache.fineract.client.models.PostDelinquencyBucketResponse;
import org.apache.fineract.client.models.PostDelinquencyRangeResponse;
import org.apache.fineract.client.models.PostLoansDelinquencyActionResponse;
import org.apache.fineract.client.models.PostLoansLoanIdTransactionsResponse;
import org.apache.fineract.client.models.PutDelinquencyBucketResponse;
import org.apache.fineract.client.models.PutDelinquencyRangeResponse;
@@ -1002,6 +1007,525 @@ public class DelinquencyBucketsIntegrationTest {
}
}
@Test
public void testDelinquencyWithPauseLettingPauseExpire() {
try {
GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.TRUE);
LocalDate bussinesLocalDate = Utils.getDateAsLocalDate("01 January 2012");
log.info("Current date {}", bussinesLocalDate);
BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec, BusinessDateType.BUSINESS_DATE, bussinesLocalDate);
final LoanTransactionHelper loanTransactionHelper = new LoanTransactionHelper(this.requestSpec, this.responseSpec);
final SchedulerJobHelper schedulerJobHelper = new SchedulerJobHelper(requestSpec);
ArrayList<Integer> rangeIds = new ArrayList<>();
String jsonRange = DelinquencyRangesHelper.getAsJSON(1, 3);
PostDelinquencyRangeResponse delinquencyRangeResponse = DelinquencyRangesHelper.createDelinquencyRange(requestSpec,
responseSpec, jsonRange);
rangeIds.add(delinquencyRangeResponse.getResourceId());
final GetDelinquencyRangesResponse range = DelinquencyRangesHelper.getDelinquencyRange(requestSpec, responseSpec,
delinquencyRangeResponse.getResourceId());
final String classificationExpected = range.getClassification();
log.info("Expected Delinquency Range classification {}", classificationExpected);
jsonRange = DelinquencyRangesHelper.getAsJSON(4, 60);
delinquencyRangeResponse = DelinquencyRangesHelper.createDelinquencyRange(requestSpec, responseSpec, jsonRange);
rangeIds.add(delinquencyRangeResponse.getResourceId());
String jsonBucket = DelinquencyBucketsHelper.getAsJSON(rangeIds);
PostDelinquencyBucketResponse delinquencyBucketResponse = DelinquencyBucketsHelper.createDelinquencyBucket(requestSpec,
responseSpec, jsonBucket);
final GetDelinquencyBucketsResponse delinquencyBucket = DelinquencyBucketsHelper.getDelinquencyBucket(requestSpec, responseSpec,
delinquencyBucketResponse.getResourceId());
final Integer clientId = ClientHelper.createClient(this.requestSpec, this.responseSpec, "01 January 2012");
final GetLoanProductsProductIdResponse getLoanProductsProductResponse = createLoanProduct(loanTransactionHelper,
Math.toIntExact(delinquencyBucket.getId()), "3");
assertNotNull(getLoanProductsProductResponse);
log.info("Loan Product Arrears: {}", getLoanProductsProductResponse.getInArrearsTolerance());
assertEquals(3, getLoanProductsProductResponse.getInArrearsTolerance());
final LocalDate transactionDate = bussinesLocalDate;
String operationDate = Utils.dateFormatter.format(transactionDate);
final Integer loanId = createLoanAccount(loanTransactionHelper, clientId.toString(),
getLoanProductsProductResponse.getId().toString(), operationDate, "3");
GetLoansLoanIdResponse getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId);
final GetDelinquencyRangesResponse firstTestCase = getLoansLoanIdResponse.getDelinquencyRange();
log.info("Loan Delinquency Range is null {}", (firstTestCase == null));
loanTransactionHelper.printRepaymentSchedule(getLoansLoanIdResponse);
log.info("Loan Account Arrears {}", getLoansLoanIdResponse.getInArrearsTolerance());
assertEquals(3, getLoansLoanIdResponse.getInArrearsTolerance());
final String jobName = "Loan COB";
bussinesLocalDate = Utils.getDateAsLocalDate("06 February 2012");
LocalDate lastLoanCOBBusinessDate = bussinesLocalDate.minusDays(1);
schedulerJobHelper.fastForwardTime(lastLoanCOBBusinessDate, bussinesLocalDate, jobName, responseSpec);
log.info("Current date {}", bussinesLocalDate);
BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec, BusinessDateType.BUSINESS_DATE, bussinesLocalDate);
schedulerJobHelper.executeAndAwaitJob(jobName);
getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId);
loanTransactionHelper.printDelinquencyData(getLoansLoanIdResponse);
GetLoansLoanIdDelinquencySummary delinquent = getLoansLoanIdResponse.getDelinquent();
assertEquals(1033.33, delinquent.getDelinquentAmount());
assertEquals(LocalDate.of(2012, 2, 1), delinquent.getDelinquentDate());
assertEquals(5, delinquent.getDelinquentDays());
PostLoansDelinquencyActionResponse pauseDelinquencyResponse = loanTransactionHelper
.createLoanDelinquencyAction(loanId.longValue(), PAUSE, "06 February 2012", "10 February 2012");
bussinesLocalDate = Utils.getDateAsLocalDate("09 February 2012");
schedulerJobHelper.fastForwardTime(lastLoanCOBBusinessDate, bussinesLocalDate, jobName, responseSpec);
lastLoanCOBBusinessDate = bussinesLocalDate.minusDays(1);
log.info("Current date {}", bussinesLocalDate);
BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec, BusinessDateType.BUSINESS_DATE, bussinesLocalDate);
schedulerJobHelper.executeAndAwaitJob(jobName);
getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId);
loanTransactionHelper.printDelinquencyData(getLoansLoanIdResponse);
delinquent = getLoansLoanIdResponse.getDelinquent();
assertEquals(1033.33, delinquent.getDelinquentAmount());
assertEquals(LocalDate.of(2012, 2, 1), delinquent.getDelinquentDate());
assertEquals(5, delinquent.getDelinquentDays());
bussinesLocalDate = Utils.getDateAsLocalDate("12 March 2012");
schedulerJobHelper.fastForwardTime(lastLoanCOBBusinessDate, bussinesLocalDate, jobName, responseSpec);
log.info("Current date {}", bussinesLocalDate);
BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec, BusinessDateType.BUSINESS_DATE, bussinesLocalDate);
schedulerJobHelper.executeAndAwaitJob(jobName);
getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId);
loanTransactionHelper.printDelinquencyData(getLoansLoanIdResponse);
delinquent = getLoansLoanIdResponse.getDelinquent();
assertEquals(2049.99, delinquent.getDelinquentAmount());
assertEquals(LocalDate.of(2012, 2, 1), delinquent.getDelinquentDate());
assertEquals(36, delinquent.getDelinquentDays());
} finally {
GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.FALSE);
}
}
@Test
public void testDelinquencyWithPauseResumeBeforePauseExpires() {
try {
GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.TRUE);
LocalDate bussinesLocalDate = Utils.getDateAsLocalDate("01 January 2012");
log.info("Current date {}", bussinesLocalDate);
BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec, BusinessDateType.BUSINESS_DATE, bussinesLocalDate);
final LoanTransactionHelper loanTransactionHelper = new LoanTransactionHelper(this.requestSpec, this.responseSpec);
final SchedulerJobHelper schedulerJobHelper = new SchedulerJobHelper(requestSpec);
ArrayList<Integer> rangeIds = new ArrayList<>();
String jsonRange = DelinquencyRangesHelper.getAsJSON(1, 3);
PostDelinquencyRangeResponse delinquencyRangeResponse = DelinquencyRangesHelper.createDelinquencyRange(requestSpec,
responseSpec, jsonRange);
rangeIds.add(delinquencyRangeResponse.getResourceId());
final GetDelinquencyRangesResponse range = DelinquencyRangesHelper.getDelinquencyRange(requestSpec, responseSpec,
delinquencyRangeResponse.getResourceId());
final String classificationExpected = range.getClassification();
log.info("Expected Delinquency Range classification {}", classificationExpected);
jsonRange = DelinquencyRangesHelper.getAsJSON(4, 60);
delinquencyRangeResponse = DelinquencyRangesHelper.createDelinquencyRange(requestSpec, responseSpec, jsonRange);
rangeIds.add(delinquencyRangeResponse.getResourceId());
String jsonBucket = DelinquencyBucketsHelper.getAsJSON(rangeIds);
PostDelinquencyBucketResponse delinquencyBucketResponse = DelinquencyBucketsHelper.createDelinquencyBucket(requestSpec,
responseSpec, jsonBucket);
final GetDelinquencyBucketsResponse delinquencyBucket = DelinquencyBucketsHelper.getDelinquencyBucket(requestSpec, responseSpec,
delinquencyBucketResponse.getResourceId());
final Integer clientId = ClientHelper.createClient(this.requestSpec, this.responseSpec, "01 January 2012");
final GetLoanProductsProductIdResponse getLoanProductsProductResponse = createLoanProduct(loanTransactionHelper,
Math.toIntExact(delinquencyBucket.getId()), "3");
assertNotNull(getLoanProductsProductResponse);
log.info("Loan Product Arrears: {}", getLoanProductsProductResponse.getInArrearsTolerance());
assertEquals(3, getLoanProductsProductResponse.getInArrearsTolerance());
final LocalDate transactionDate = bussinesLocalDate;
String operationDate = Utils.dateFormatter.format(transactionDate);
final Integer loanId = createLoanAccount(loanTransactionHelper, clientId.toString(),
getLoanProductsProductResponse.getId().toString(), operationDate, "3");
GetLoansLoanIdResponse getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId);
final GetDelinquencyRangesResponse firstTestCase = getLoansLoanIdResponse.getDelinquencyRange();
log.info("Loan Delinquency Range is null {}", (firstTestCase == null));
loanTransactionHelper.printRepaymentSchedule(getLoansLoanIdResponse);
log.info("Loan Account Arrears {}", getLoansLoanIdResponse.getInArrearsTolerance());
assertEquals(3, getLoansLoanIdResponse.getInArrearsTolerance());
final String jobName = "Loan COB";
bussinesLocalDate = Utils.getDateAsLocalDate("06 February 2012");
LocalDate lastLoanCOBBusinessDate = bussinesLocalDate.minusDays(1);
schedulerJobHelper.fastForwardTime(lastLoanCOBBusinessDate, bussinesLocalDate, jobName, responseSpec);
log.info("Current date {}", bussinesLocalDate);
BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec, BusinessDateType.BUSINESS_DATE, bussinesLocalDate);
schedulerJobHelper.executeAndAwaitJob(jobName);
getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId);
loanTransactionHelper.printDelinquencyData(getLoansLoanIdResponse);
GetLoansLoanIdDelinquencySummary delinquent = getLoansLoanIdResponse.getDelinquent();
assertEquals(1033.33, delinquent.getDelinquentAmount());
assertEquals(LocalDate.of(2012, 2, 1), delinquent.getDelinquentDate());
assertEquals(5, delinquent.getDelinquentDays());
PostLoansDelinquencyActionResponse pauseDelinquencyResponse = loanTransactionHelper
.createLoanDelinquencyAction(loanId.longValue(), PAUSE, "06 February 2012", "10 March 2012");
bussinesLocalDate = Utils.getDateAsLocalDate("09 February 2012");
schedulerJobHelper.fastForwardTime(lastLoanCOBBusinessDate, bussinesLocalDate, jobName, responseSpec);
lastLoanCOBBusinessDate = bussinesLocalDate.minusDays(1);
log.info("Current date {}", bussinesLocalDate);
BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec, BusinessDateType.BUSINESS_DATE, bussinesLocalDate);
schedulerJobHelper.executeAndAwaitJob(jobName);
getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId);
loanTransactionHelper.printDelinquencyData(getLoansLoanIdResponse);
delinquent = getLoansLoanIdResponse.getDelinquent();
assertEquals(1033.33, delinquent.getDelinquentAmount());
assertEquals(LocalDate.of(2012, 2, 1), delinquent.getDelinquentDate());
assertEquals(5, delinquent.getDelinquentDays());
bussinesLocalDate = Utils.getDateAsLocalDate("10 February 2012");
BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec, BusinessDateType.BUSINESS_DATE, bussinesLocalDate);
loanTransactionHelper.createLoanDelinquencyAction(loanId.longValue(), RESUME, "10 February 2012");
bussinesLocalDate = Utils.getDateAsLocalDate("12 March 2012");
schedulerJobHelper.fastForwardTime(lastLoanCOBBusinessDate, bussinesLocalDate, jobName, responseSpec);
log.info("Current date {}", bussinesLocalDate);
BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec, BusinessDateType.BUSINESS_DATE, bussinesLocalDate);
schedulerJobHelper.executeAndAwaitJob(jobName);
getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId);
loanTransactionHelper.printDelinquencyData(getLoansLoanIdResponse);
delinquent = getLoansLoanIdResponse.getDelinquent();
assertEquals(2049.99, delinquent.getDelinquentAmount());
assertEquals(LocalDate.of(2012, 2, 1), delinquent.getDelinquentDate());
assertEquals(36, delinquent.getDelinquentDays());
} finally {
GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.FALSE);
}
}
@Test
public void testDelinquencyWithMultiplePausePeriods() {
try {
GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.TRUE);
LocalDate bussinesLocalDate = Utils.getDateAsLocalDate("01 January 2012");
log.info("Current date {}", bussinesLocalDate);
BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec, BusinessDateType.BUSINESS_DATE, bussinesLocalDate);
final LoanTransactionHelper loanTransactionHelper = new LoanTransactionHelper(this.requestSpec, this.responseSpec);
final SchedulerJobHelper schedulerJobHelper = new SchedulerJobHelper(requestSpec);
ArrayList<Integer> rangeIds = new ArrayList<>();
String jsonRange = DelinquencyRangesHelper.getAsJSON(1, 3);
PostDelinquencyRangeResponse delinquencyRangeResponse = DelinquencyRangesHelper.createDelinquencyRange(requestSpec,
responseSpec, jsonRange);
rangeIds.add(delinquencyRangeResponse.getResourceId());
final GetDelinquencyRangesResponse range = DelinquencyRangesHelper.getDelinquencyRange(requestSpec, responseSpec,
delinquencyRangeResponse.getResourceId());
final String classificationExpected = range.getClassification();
log.info("Expected Delinquency Range classification {}", classificationExpected);
jsonRange = DelinquencyRangesHelper.getAsJSON(4, 60);
delinquencyRangeResponse = DelinquencyRangesHelper.createDelinquencyRange(requestSpec, responseSpec, jsonRange);
rangeIds.add(delinquencyRangeResponse.getResourceId());
String jsonBucket = DelinquencyBucketsHelper.getAsJSON(rangeIds);
PostDelinquencyBucketResponse delinquencyBucketResponse = DelinquencyBucketsHelper.createDelinquencyBucket(requestSpec,
responseSpec, jsonBucket);
final GetDelinquencyBucketsResponse delinquencyBucket = DelinquencyBucketsHelper.getDelinquencyBucket(requestSpec, responseSpec,
delinquencyBucketResponse.getResourceId());
final Integer clientId = ClientHelper.createClient(this.requestSpec, this.responseSpec, "01 January 2012");
final GetLoanProductsProductIdResponse getLoanProductsProductResponse = createLoanProduct(loanTransactionHelper,
Math.toIntExact(delinquencyBucket.getId()), "3");
assertNotNull(getLoanProductsProductResponse);
log.info("Loan Product Arrears: {}", getLoanProductsProductResponse.getInArrearsTolerance());
assertEquals(3, getLoanProductsProductResponse.getInArrearsTolerance());
final LocalDate transactionDate = bussinesLocalDate;
String operationDate = Utils.dateFormatter.format(transactionDate);
final Integer loanId = createLoanAccount(loanTransactionHelper, clientId.toString(),
getLoanProductsProductResponse.getId().toString(), operationDate, "3");
GetLoansLoanIdResponse getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId);
final GetDelinquencyRangesResponse firstTestCase = getLoansLoanIdResponse.getDelinquencyRange();
log.info("Loan Delinquency Range is null {}", (firstTestCase == null));
loanTransactionHelper.printRepaymentSchedule(getLoansLoanIdResponse);
log.info("Loan Account Arrears {}", getLoansLoanIdResponse.getInArrearsTolerance());
assertEquals(3, getLoansLoanIdResponse.getInArrearsTolerance());
final String jobName = "Loan COB";
// delinqut days: 5
bussinesLocalDate = Utils.getDateAsLocalDate("06 February 2012");
LocalDate lastLoanCOBBusinessDate = bussinesLocalDate.minusDays(1);
schedulerJobHelper.fastForwardTime(lastLoanCOBBusinessDate, bussinesLocalDate, jobName, responseSpec);
log.info("Current date {}", bussinesLocalDate);
BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec, BusinessDateType.BUSINESS_DATE, bussinesLocalDate);
schedulerJobHelper.executeAndAwaitJob(jobName);
getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId);
loanTransactionHelper.printDelinquencyData(getLoansLoanIdResponse);
GetLoansLoanIdDelinquencySummary delinquent = getLoansLoanIdResponse.getDelinquent();
assertEquals(1033.33, delinquent.getDelinquentAmount());
assertEquals(LocalDate.of(2012, 2, 1), delinquent.getDelinquentDate());
assertEquals(5, delinquent.getDelinquentDays());
PostLoansDelinquencyActionResponse pauseDelinquencyResponse = loanTransactionHelper
.createLoanDelinquencyAction(loanId.longValue(), PAUSE, "06 February 2012", "10 March 2012");
bussinesLocalDate = Utils.getDateAsLocalDate("09 February 2012");
schedulerJobHelper.fastForwardTime(lastLoanCOBBusinessDate, bussinesLocalDate, jobName, responseSpec);
lastLoanCOBBusinessDate = bussinesLocalDate.minusDays(1);
log.info("Current date {}", bussinesLocalDate);
BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec, BusinessDateType.BUSINESS_DATE, bussinesLocalDate);
schedulerJobHelper.executeAndAwaitJob(jobName);
getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId);
loanTransactionHelper.printDelinquencyData(getLoansLoanIdResponse);
delinquent = getLoansLoanIdResponse.getDelinquent();
assertEquals(1033.33, delinquent.getDelinquentAmount());
assertEquals(LocalDate.of(2012, 2, 1), delinquent.getDelinquentDate());
assertEquals(5, delinquent.getDelinquentDays());
bussinesLocalDate = Utils.getDateAsLocalDate("10 February 2012");
BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec, BusinessDateType.BUSINESS_DATE, bussinesLocalDate);
loanTransactionHelper.createLoanDelinquencyAction(loanId.longValue(), RESUME, "10 February 2012");
bussinesLocalDate = Utils.getDateAsLocalDate("13 February 2012");
schedulerJobHelper.fastForwardTime(lastLoanCOBBusinessDate, bussinesLocalDate, jobName, responseSpec);
lastLoanCOBBusinessDate = bussinesLocalDate.minusDays(1);
log.info("Current date {}", bussinesLocalDate);
BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec, BusinessDateType.BUSINESS_DATE, bussinesLocalDate);
schedulerJobHelper.executeAndAwaitJob(jobName);
getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId);
loanTransactionHelper.printDelinquencyData(getLoansLoanIdResponse);
delinquent = getLoansLoanIdResponse.getDelinquent();
assertEquals(1033.33, delinquent.getDelinquentAmount());
assertEquals(LocalDate.of(2012, 2, 1), delinquent.getDelinquentDate());
assertEquals(8, delinquent.getDelinquentDays());
pauseDelinquencyResponse = loanTransactionHelper.createLoanDelinquencyAction(loanId.longValue(), PAUSE, "13 February 2012",
"18 February 2012");
bussinesLocalDate = Utils.getDateAsLocalDate("23 February 2012");
schedulerJobHelper.fastForwardTime(lastLoanCOBBusinessDate, bussinesLocalDate, jobName, responseSpec);
lastLoanCOBBusinessDate = bussinesLocalDate.minusDays(1);
log.info("Current date {}", bussinesLocalDate);
BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec, BusinessDateType.BUSINESS_DATE, bussinesLocalDate);
schedulerJobHelper.executeAndAwaitJob(jobName);
getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId);
loanTransactionHelper.printDelinquencyData(getLoansLoanIdResponse);
delinquent = getLoansLoanIdResponse.getDelinquent();
assertEquals(1033.33, delinquent.getDelinquentAmount());
assertEquals(LocalDate.of(2012, 2, 1), delinquent.getDelinquentDate());
assertEquals(13, delinquent.getDelinquentDays());
pauseDelinquencyResponse = loanTransactionHelper.createLoanDelinquencyAction(loanId.longValue(), PAUSE, "23 February 2012",
"28 February 2012");
bussinesLocalDate = Utils.getDateAsLocalDate("25 February 2012");
BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec, BusinessDateType.BUSINESS_DATE, bussinesLocalDate);
loanTransactionHelper.createLoanDelinquencyAction(loanId.longValue(), RESUME, "25 February 2012");
bussinesLocalDate = Utils.getDateAsLocalDate("12 March 2012");
schedulerJobHelper.fastForwardTime(lastLoanCOBBusinessDate, bussinesLocalDate, jobName, responseSpec);
log.info("Current date {}", bussinesLocalDate);
BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec, BusinessDateType.BUSINESS_DATE, bussinesLocalDate);
schedulerJobHelper.executeAndAwaitJob(jobName);
getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId);
loanTransactionHelper.printDelinquencyData(getLoansLoanIdResponse);
delinquent = getLoansLoanIdResponse.getDelinquent();
assertEquals(2049.99, delinquent.getDelinquentAmount());
assertEquals(LocalDate.of(2012, 2, 1), delinquent.getDelinquentDate());
assertEquals(29, delinquent.getDelinquentDays());
} finally {
GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.FALSE);
}
}
@Test
public void testDelinquencyWithMultiplePausePeriodsWithInstallmentLevelDelinquency() {
try {
GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.TRUE);
LocalDate bussinesLocalDate = Utils.getDateAsLocalDate("01 January 2012");
log.info("Current date {}", bussinesLocalDate);
BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec, BusinessDateType.BUSINESS_DATE, bussinesLocalDate);
final LoanTransactionHelper loanTransactionHelper = new LoanTransactionHelper(this.requestSpec, this.responseSpec);
final SchedulerJobHelper schedulerJobHelper = new SchedulerJobHelper(requestSpec);
ArrayList<Integer> rangeIds = new ArrayList<>();
String jsonRange = DelinquencyRangesHelper.getAsJSON(1, 3);
PostDelinquencyRangeResponse delinquencyRangeResponse = DelinquencyRangesHelper.createDelinquencyRange(requestSpec,
responseSpec, jsonRange);
rangeIds.add(delinquencyRangeResponse.getResourceId());
final GetDelinquencyRangesResponse range = DelinquencyRangesHelper.getDelinquencyRange(requestSpec, responseSpec,
delinquencyRangeResponse.getResourceId());
final String classificationExpected = range.getClassification();
log.info("Expected Delinquency Range classification {}", classificationExpected);
jsonRange = DelinquencyRangesHelper.getAsJSON(4, 60);
delinquencyRangeResponse = DelinquencyRangesHelper.createDelinquencyRange(requestSpec, responseSpec, jsonRange);
rangeIds.add(delinquencyRangeResponse.getResourceId());
String jsonBucket = DelinquencyBucketsHelper.getAsJSON(rangeIds);
PostDelinquencyBucketResponse delinquencyBucketResponse = DelinquencyBucketsHelper.createDelinquencyBucket(requestSpec,
responseSpec, jsonBucket);
final GetDelinquencyBucketsResponse delinquencyBucket = DelinquencyBucketsHelper.getDelinquencyBucket(requestSpec, responseSpec,
delinquencyBucketResponse.getResourceId());
final Integer clientId = ClientHelper.createClient(this.requestSpec, this.responseSpec, "01 January 2012");
final GetLoanProductsProductIdResponse getLoanProductsProductResponse = createLoanProductWithInstallmentLevelDelinquency(
loanTransactionHelper, Math.toIntExact(delinquencyBucket.getId()), "3");
assertNotNull(getLoanProductsProductResponse);
log.info("Loan Product Arrears: {}", getLoanProductsProductResponse.getInArrearsTolerance());
assertEquals(3, getLoanProductsProductResponse.getInArrearsTolerance());
final LocalDate transactionDate = bussinesLocalDate;
String operationDate = Utils.dateFormatter.format(transactionDate);
final Integer loanId = createLoanAccount(loanTransactionHelper, clientId.toString(),
getLoanProductsProductResponse.getId().toString(), operationDate, "3");
GetLoansLoanIdResponse getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId);
final GetDelinquencyRangesResponse firstTestCase = getLoansLoanIdResponse.getDelinquencyRange();
log.info("Loan Delinquency Range is null {}", (firstTestCase == null));
loanTransactionHelper.printRepaymentSchedule(getLoansLoanIdResponse);
log.info("Loan Account Arrears {}", getLoansLoanIdResponse.getInArrearsTolerance());
assertEquals(3, getLoansLoanIdResponse.getInArrearsTolerance());
final String jobName = "Loan COB";
// delinqut days: 5
bussinesLocalDate = Utils.getDateAsLocalDate("06 February 2012");
LocalDate lastLoanCOBBusinessDate = bussinesLocalDate.minusDays(1);
schedulerJobHelper.fastForwardTime(lastLoanCOBBusinessDate, bussinesLocalDate, jobName, responseSpec);
log.info("Current date {}", bussinesLocalDate);
BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec, BusinessDateType.BUSINESS_DATE, bussinesLocalDate);
schedulerJobHelper.executeAndAwaitJob(jobName);
getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId);
loanTransactionHelper.printDelinquencyData(getLoansLoanIdResponse);
GetLoansLoanIdDelinquencySummary delinquent = getLoansLoanIdResponse.getDelinquent();
assertEquals(1033.33, delinquent.getDelinquentAmount());
assertEquals(LocalDate.of(2012, 2, 1), delinquent.getDelinquentDate());
assertEquals(5, delinquent.getDelinquentDays());
PostLoansDelinquencyActionResponse pauseDelinquencyResponse = loanTransactionHelper
.createLoanDelinquencyAction(loanId.longValue(), PAUSE, "06 February 2012", "10 March 2012");
bussinesLocalDate = Utils.getDateAsLocalDate("09 February 2012");
schedulerJobHelper.fastForwardTime(lastLoanCOBBusinessDate, bussinesLocalDate, jobName, responseSpec);
lastLoanCOBBusinessDate = bussinesLocalDate.minusDays(1);
log.info("Current date {}", bussinesLocalDate);
BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec, BusinessDateType.BUSINESS_DATE, bussinesLocalDate);
schedulerJobHelper.executeAndAwaitJob(jobName);
getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId);
loanTransactionHelper.printDelinquencyData(getLoansLoanIdResponse);
delinquent = getLoansLoanIdResponse.getDelinquent();
assertEquals(1033.33, delinquent.getDelinquentAmount());
assertEquals(LocalDate.of(2012, 2, 1), delinquent.getDelinquentDate());
assertEquals(5, delinquent.getDelinquentDays());
bussinesLocalDate = Utils.getDateAsLocalDate("10 February 2012");
BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec, BusinessDateType.BUSINESS_DATE, bussinesLocalDate);
loanTransactionHelper.createLoanDelinquencyAction(loanId.longValue(), RESUME, "10 February 2012");
bussinesLocalDate = Utils.getDateAsLocalDate("13 February 2012");
schedulerJobHelper.fastForwardTime(lastLoanCOBBusinessDate, bussinesLocalDate, jobName, responseSpec);
lastLoanCOBBusinessDate = bussinesLocalDate.minusDays(1);
log.info("Current date {}", bussinesLocalDate);
BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec, BusinessDateType.BUSINESS_DATE, bussinesLocalDate);
schedulerJobHelper.executeAndAwaitJob(jobName);
getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId);
loanTransactionHelper.printDelinquencyData(getLoansLoanIdResponse);
delinquent = getLoansLoanIdResponse.getDelinquent();
assertEquals(1033.33, delinquent.getDelinquentAmount());
assertEquals(LocalDate.of(2012, 2, 1), delinquent.getDelinquentDate());
assertEquals(8, delinquent.getDelinquentDays());
pauseDelinquencyResponse = loanTransactionHelper.createLoanDelinquencyAction(loanId.longValue(), PAUSE, "13 February 2012",
"18 February 2012");
bussinesLocalDate = Utils.getDateAsLocalDate("23 February 2012");
schedulerJobHelper.fastForwardTime(lastLoanCOBBusinessDate, bussinesLocalDate, jobName, responseSpec);
lastLoanCOBBusinessDate = bussinesLocalDate.minusDays(1);
log.info("Current date {}", bussinesLocalDate);
BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec, BusinessDateType.BUSINESS_DATE, bussinesLocalDate);
schedulerJobHelper.executeAndAwaitJob(jobName);
getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId);
loanTransactionHelper.printDelinquencyData(getLoansLoanIdResponse);
delinquent = getLoansLoanIdResponse.getDelinquent();
assertEquals(1033.33, delinquent.getDelinquentAmount());
assertEquals(LocalDate.of(2012, 2, 1), delinquent.getDelinquentDate());
assertEquals(13, delinquent.getDelinquentDays());
pauseDelinquencyResponse = loanTransactionHelper.createLoanDelinquencyAction(loanId.longValue(), PAUSE, "23 February 2012",
"28 February 2012");
bussinesLocalDate = Utils.getDateAsLocalDate("25 February 2012");
BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec, BusinessDateType.BUSINESS_DATE, bussinesLocalDate);
loanTransactionHelper.createLoanDelinquencyAction(loanId.longValue(), RESUME, "25 February 2012");
bussinesLocalDate = Utils.getDateAsLocalDate("14 March 2012");
schedulerJobHelper.fastForwardTime(lastLoanCOBBusinessDate, bussinesLocalDate.minusDays(1), jobName, responseSpec);
log.info("Current date {}", bussinesLocalDate);
BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec, BusinessDateType.BUSINESS_DATE, bussinesLocalDate);
schedulerJobHelper.executeAndAwaitJob(jobName);
getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId);
loanTransactionHelper.printDelinquencyData(getLoansLoanIdResponse);
delinquent = getLoansLoanIdResponse.getDelinquent();
assertEquals(2049.99, delinquent.getDelinquentAmount());
assertEquals(LocalDate.of(2012, 2, 1), delinquent.getDelinquentDate());
assertEquals(31, delinquent.getDelinquentDays());
assertEquals(2, delinquent.getInstallmentLevelDelinquency().size());
GetLoansLoanIdLoanInstallmentLevelDelinquency firstInstallmentDelinquent = delinquent.getInstallmentLevelDelinquency().get(0);
assertEquals(BigDecimal.valueOf(1016.66), firstInstallmentDelinquent.getDelinquentAmount().stripTrailingZeros());
GetLoansLoanIdLoanInstallmentLevelDelinquency secondInstallmentDelinquent = delinquent.getInstallmentLevelDelinquency().get(1);
assertEquals(BigDecimal.valueOf(1033.33), secondInstallmentDelinquent.getDelinquentAmount().stripTrailingZeros());
} finally {
GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.FALSE);
}
}
private GetLoanProductsProductIdResponse createLoanProduct(final LoanTransactionHelper loanTransactionHelper,
final Integer delinquencyBucketId, final String inArrearsTolerance) {
final HashMap<String, Object> loanProductMap = new LoanProductTestBuilder().withInArrearsTolerance(inArrearsTolerance).build(null,
@@ -1010,6 +1534,15 @@ public class DelinquencyBucketsIntegrationTest {
return loanTransactionHelper.getLoanProduct(loanProductId);
}
private GetLoanProductsProductIdResponse createLoanProductWithInstallmentLevelDelinquency(
final LoanTransactionHelper loanTransactionHelper, final Integer delinquencyBucketId, final String inArrearsTolerance) {
final HashMap<String, Object> loanProductMap = new LoanProductTestBuilder().withInArrearsTolerance(inArrearsTolerance).build(null,
delinquencyBucketId);
loanProductMap.put("enableInstallmentLevelDelinquency", true);
final Integer loanProductId = loanTransactionHelper.getLoanProductId(Utils.convertToJson(loanProductMap));
return loanTransactionHelper.getLoanProduct(loanProductId);
}
private PutLoanProductsProductIdResponse updateLoanProduct(LoanTransactionHelper loanTransactionHelper, Long id,
final Integer inArrearsTolerance) {
final PutLoanProductsProductIdRequest requestModifyLoan = new PutLoanProductsProductIdRequest()