[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());
}