Delinquency classification including Chargeback transactions
This commit is contained in:
committed by
Arnold Gálovics
parent
cce7902054
commit
90f1eccd4b
+2
-5
@@ -478,7 +478,7 @@ public class AccountDetailsReadPlatformServiceJpaRepositoryImpl implements Accou
|
||||
|
||||
.append(" l.closedon_date as closedOnDate,")
|
||||
.append(" cbu.username as closedByUsername, cbu.firstname as closedByFirstname, cbu.lastname as closedByLastname,")
|
||||
.append(" la.overdue_since_date_derived as overdueSinceDate,")
|
||||
.append(" la.overdue_since_date_derived as overdueSinceDate, ")
|
||||
.append(" l.writtenoffon_date as writtenOffOnDate, l.expected_maturedon_date as expectedMaturityDate")
|
||||
|
||||
.append(" from m_loan l ").append("LEFT JOIN m_product_loan AS lp ON lp.id = l.product_id")
|
||||
@@ -550,10 +550,7 @@ public class AccountDetailsReadPlatformServiceJpaRepositoryImpl implements Accou
|
||||
final LocalDate expectedMaturityDate = JdbcSupport.getLocalDate(rs, "expectedMaturityDate");
|
||||
|
||||
final LocalDate overdueSinceDate = JdbcSupport.getLocalDate(rs, "overdueSinceDate");
|
||||
Boolean inArrears = true;
|
||||
if (overdueSinceDate == null) {
|
||||
inArrears = false;
|
||||
}
|
||||
Boolean inArrears = (overdueSinceDate != null);
|
||||
|
||||
final LoanApplicationTimelineData timeline = new LoanApplicationTimelineData(submittedOnDate, submittedByUsername,
|
||||
submittedByFirstname, submittedByLastname, rejectedOnDate, rejectedByUsername, rejectedByFirstname, rejectedByLastname,
|
||||
|
||||
+3
@@ -22,6 +22,7 @@ import java.util.Collection;
|
||||
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.loanaccount.data.CollectionData;
|
||||
|
||||
public interface DelinquencyReadPlatformService {
|
||||
|
||||
@@ -37,4 +38,6 @@ public interface DelinquencyReadPlatformService {
|
||||
|
||||
Collection<LoanDelinquencyTagHistoryData> retrieveDelinquencyRangeHistory(Long loanId);
|
||||
|
||||
CollectionData calculateLoanCollectionData(Long loanId);
|
||||
|
||||
}
|
||||
|
||||
+100
@@ -18,10 +18,15 @@
|
||||
*/
|
||||
package org.apache.fineract.portfolio.delinquency.service;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
||||
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
|
||||
import org.apache.fineract.portfolio.delinquency.data.DelinquencyBucketData;
|
||||
import org.apache.fineract.portfolio.delinquency.data.DelinquencyRangeData;
|
||||
import org.apache.fineract.portfolio.delinquency.data.LoanDelinquencyTagHistoryData;
|
||||
@@ -34,8 +39,13 @@ import org.apache.fineract.portfolio.delinquency.domain.LoanDelinquencyTagHistor
|
||||
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.loanaccount.data.CollectionData;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepository;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionToRepaymentScheduleMapping;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@@ -97,4 +107,94 @@ public class DelinquencyReadPlatformServiceImpl implements DelinquencyReadPlatfo
|
||||
return mapperLoanDelinquencyTagHistory.map(loanDelinquencyTagData);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CollectionData calculateLoanCollectionData(final Long loanId) {
|
||||
final LocalDate businessDate = DateUtils.getBusinessLocalDate();
|
||||
final Optional<Loan> optLoan = this.loanRepository.findById(loanId);
|
||||
|
||||
CollectionData collectionData = CollectionData.template();
|
||||
if (optLoan.isPresent()) {
|
||||
final Loan loan = optLoan.get();
|
||||
|
||||
collectionData.setAvailableDisbursementAmount(loan.getApprovedPrincipal().subtract(loan.getDisbursedAmount()));
|
||||
collectionData.setNextPaymentDueDate(loan.possibleNextRepaymentDate());
|
||||
|
||||
final MonetaryCurrency currency = loan.getCurrency();
|
||||
BigDecimal delinquentAmount = BigDecimal.ZERO;
|
||||
// Overdue Days calculation
|
||||
Long overdueDays = 0L;
|
||||
LocalDate overdueSinceDate = null;
|
||||
final List<LoanTransaction> chargebackTransactions = loan.retrieveListOfTransactionsByType(LoanTransactionType.CHARGEBACK);
|
||||
for (LoanTransaction loanTransaction : chargebackTransactions) {
|
||||
Set<LoanTransactionToRepaymentScheduleMapping> loanTransactionToRepaymentScheduleMappings = loanTransaction
|
||||
.getLoanTransactionToRepaymentScheduleMappings();
|
||||
LoanTransactionToRepaymentScheduleMapping loanTransactionToRepaymentScheduleMapping = loanTransactionToRepaymentScheduleMappings
|
||||
.iterator().next();
|
||||
if (!loanTransactionToRepaymentScheduleMapping.getLoanRepaymentScheduleInstallment().isPrincipalCompleted(currency)) {
|
||||
overdueSinceDate = loanTransaction.getTransactionDate();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
final List<LoanRepaymentScheduleInstallment> installments = loan.getRepaymentScheduleInstallments();
|
||||
if (overdueSinceDate == null) {
|
||||
for (LoanRepaymentScheduleInstallment installment : installments) {
|
||||
if (installment.isNotFullyPaidOff()) {
|
||||
overdueSinceDate = installment.getDueDate();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Integer graceDays = 0;
|
||||
if (overdueSinceDate != null) {
|
||||
if (loan.loanProduct().getLoanProductConfigurableAttributes().getGraceOnArrearsAgingBoolean()) {
|
||||
graceDays = loan.loanProduct().getLoanProductRelatedDetail().getGraceOnArrearsAgeing();
|
||||
if (graceDays == null) {
|
||||
graceDays = 0;
|
||||
}
|
||||
overdueSinceDate = overdueSinceDate.plusDays(graceDays);
|
||||
}
|
||||
overdueDays = DateUtils.getDifferenceInDays(overdueSinceDate, businessDate);
|
||||
if (overdueDays < 0) {
|
||||
overdueDays = 0L;
|
||||
}
|
||||
collectionData.setDelinquentDate(overdueSinceDate);
|
||||
}
|
||||
|
||||
collectionData.setPastDueDays(overdueDays);
|
||||
if (overdueDays > 0) {
|
||||
collectionData.setDelinquentDays(overdueDays - graceDays);
|
||||
}
|
||||
|
||||
final LoanTransaction lastRepaymenTransaction = loan.getLastRepaymentTransaction();
|
||||
if (lastRepaymenTransaction != null) {
|
||||
collectionData.setLastPaymentDate(lastRepaymenTransaction.getTransactionDate());
|
||||
collectionData.setLastPaymentAmount(lastRepaymenTransaction.getAmount());
|
||||
}
|
||||
|
||||
// Calculate Delinquency Amount
|
||||
for (LoanRepaymentScheduleInstallment installment : installments) {
|
||||
if (installment.getDueDate().isBefore(businessDate)) {
|
||||
delinquentAmount = delinquentAmount.add(installment.getTotalOutstanding(currency).getAmount());
|
||||
}
|
||||
}
|
||||
for (LoanTransaction loanTransaction : chargebackTransactions) {
|
||||
Set<LoanTransactionToRepaymentScheduleMapping> loanTransactionToRepaymentScheduleMappings = loanTransaction
|
||||
.getLoanTransactionToRepaymentScheduleMappings();
|
||||
for (LoanTransactionToRepaymentScheduleMapping loanTransactionToRepaymentScheduleMapping : loanTransactionToRepaymentScheduleMappings) {
|
||||
LoanRepaymentScheduleInstallment loanRepaymentScheduleInstallment = loanTransactionToRepaymentScheduleMapping
|
||||
.getLoanRepaymentScheduleInstallment();
|
||||
if (!loanRepaymentScheduleInstallment.isPrincipalCompleted(currency)) {
|
||||
delinquentAmount = delinquentAmount.add(loanTransaction.getAmount());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
collectionData.setDelinquentAmount(delinquentAmount);
|
||||
}
|
||||
|
||||
return collectionData;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+8
-4
@@ -18,8 +18,10 @@
|
||||
*/
|
||||
package org.apache.fineract.portfolio.delinquency.service;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanScheduleDelinquencyData;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||
|
||||
public interface DelinquencyWritePlatformService {
|
||||
@@ -38,12 +40,14 @@ public interface DelinquencyWritePlatformService {
|
||||
|
||||
CommandProcessingResult applyDelinquencyTagToLoan(Long loanId, JsonCommand command);
|
||||
|
||||
void applyDelinquencyTagToLoan(Long loanId, Long ageDays);
|
||||
|
||||
void applyDelinquencyTagToLoan(Loan loan, Long ageDays);
|
||||
|
||||
void removeDelinquencyTagToLoan(Loan loan);
|
||||
|
||||
void cleanLoanDelinquencyTags(Loan loan);
|
||||
|
||||
LoanScheduleDelinquencyData calculateDelinquencyData(LoanScheduleDelinquencyData loanScheduleDelinquencyData);
|
||||
|
||||
void applyDelinquencyTagToLoan(LoanScheduleDelinquencyData loanDelinquencyData);
|
||||
|
||||
LocalDate getOverdueSinceDate(Loan loan, LocalDate businessDate, Integer graceOnArrearAgeing);
|
||||
|
||||
}
|
||||
|
||||
+98
-27
@@ -33,6 +33,7 @@ import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder;
|
||||
import org.apache.fineract.infrastructure.core.exception.PlatformDataIntegrityException;
|
||||
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
||||
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
|
||||
import org.apache.fineract.portfolio.delinquency.api.DelinquencyApiConstants;
|
||||
import org.apache.fineract.portfolio.delinquency.data.DelinquencyBucketData;
|
||||
import org.apache.fineract.portfolio.delinquency.data.DelinquencyRangeData;
|
||||
@@ -48,10 +49,13 @@ import org.apache.fineract.portfolio.delinquency.exception.DelinquencyBucketAges
|
||||
import org.apache.fineract.portfolio.delinquency.exception.DelinquencyRangeInvalidAgesException;
|
||||
import org.apache.fineract.portfolio.delinquency.validator.DelinquencyBucketParseAndValidator;
|
||||
import org.apache.fineract.portfolio.delinquency.validator.DelinquencyRangeParseAndValidator;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanScheduleDelinquencyData;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallmentRepository;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepositoryWrapper;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionToRepaymentScheduleMapping;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.LoanProductRepository;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@@ -69,7 +73,6 @@ public class DelinquencyWritePlatformServiceImpl implements DelinquencyWritePlat
|
||||
private final LoanDelinquencyTagHistoryRepository loanDelinquencyTagRepository;
|
||||
private final LoanRepositoryWrapper loanRepository;
|
||||
private final LoanProductRepository loanProductRepository;
|
||||
private final LoanRepaymentScheduleInstallmentRepository loanRepaymentScheduleInstallmentRepository;
|
||||
|
||||
@Override
|
||||
public CommandProcessingResult createDelinquencyRange(JsonCommand command) {
|
||||
@@ -100,8 +103,9 @@ public class DelinquencyWritePlatformServiceImpl implements DelinquencyWritePlat
|
||||
"Data integrity issue with resource: " + delinquencyRange.getId());
|
||||
}
|
||||
repositoryRange.delete(delinquencyRange);
|
||||
return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(delinquencyRange.getId()).build();
|
||||
}
|
||||
return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(delinquencyRange.getId()).build();
|
||||
return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(delinquencyRangeId).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -135,7 +139,71 @@ public class DelinquencyWritePlatformServiceImpl implements DelinquencyWritePlat
|
||||
}
|
||||
repositoryBucket.delete(delinquencyBucket);
|
||||
}
|
||||
return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(delinquencyBucket.getId()).build();
|
||||
return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(delinquencyBucketId).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoanScheduleDelinquencyData calculateDelinquencyData(LoanScheduleDelinquencyData loanScheduleDelinquencyData) {
|
||||
final LocalDate businessDate = DateUtils.getBusinessLocalDate();
|
||||
Loan loan = loanScheduleDelinquencyData.getLoan();
|
||||
if (loan == null) {
|
||||
loan = this.loanRepository.findOneWithNotFoundDetection(loanScheduleDelinquencyData.getLoanId());
|
||||
}
|
||||
|
||||
final Integer graceOnArrearAgeing = loan.getLoanProduct().getLoanProductRelatedDetail().getGraceOnArrearsAgeing();
|
||||
final LocalDate overdueSinceDate = getOverdueSinceDate(loan, businessDate, graceOnArrearAgeing);
|
||||
final Long overdueDays = calculateOverdueDays(businessDate, overdueSinceDate);
|
||||
return new LoanScheduleDelinquencyData(loan.getId(), overdueSinceDate, overdueDays, loan);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LocalDate getOverdueSinceDate(final Loan loan, final LocalDate businessDate, final Integer graceOnArrearAgeing) {
|
||||
LoanRepaymentScheduleInstallment loanRepaymentSchedule = null;
|
||||
for (LoanRepaymentScheduleInstallment installment : loan.getRepaymentScheduleInstallments()) {
|
||||
if (installment.getDueDate().isBefore(businessDate) && installment.isNotFullyPaidOff()) {
|
||||
loanRepaymentSchedule = installment;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
LocalDate overdueSinceDate = null;
|
||||
final MonetaryCurrency loanCurrency = loan.getCurrency();
|
||||
final List<LoanTransaction> loanTransactions = loan.retrieveListOfTransactionsByType(LoanTransactionType.CHARGEBACK);
|
||||
if (loanRepaymentSchedule != null) {
|
||||
// Default Due date
|
||||
overdueSinceDate = loanRepaymentSchedule.getDueDate();
|
||||
}
|
||||
|
||||
// If there is some Loan Transaction Chargeback
|
||||
for (LoanTransaction loanTransaction : loanTransactions) {
|
||||
if (loanTransaction.getLoanTransactionToRepaymentScheduleMappings().iterator().hasNext()) {
|
||||
final LoanTransactionToRepaymentScheduleMapping transactionMapping = loanTransaction
|
||||
.getLoanTransactionToRepaymentScheduleMappings().iterator().next();
|
||||
|
||||
if (transactionMapping != null
|
||||
&& !transactionMapping.getLoanRepaymentScheduleInstallment().isPrincipalCompleted(loanCurrency)) {
|
||||
overdueSinceDate = loanTransaction.getTransactionDate();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Include grace days If It's defined
|
||||
if (overdueSinceDate != null && graceOnArrearAgeing != null) {
|
||||
overdueSinceDate = overdueSinceDate.plusDays(graceOnArrearAgeing.longValue());
|
||||
}
|
||||
return overdueSinceDate;
|
||||
}
|
||||
|
||||
private Long calculateOverdueDays(final LocalDate businessDate, LocalDate overdueSinceDate) {
|
||||
Long overdueDays = 0L;
|
||||
if (overdueSinceDate != null) {
|
||||
overdueDays = DateUtils.getDifferenceInDays(overdueSinceDate, businessDate);
|
||||
if (overdueDays < 0) {
|
||||
overdueDays = 0L;
|
||||
}
|
||||
}
|
||||
return overdueDays;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -146,28 +214,31 @@ public class DelinquencyWritePlatformServiceImpl implements DelinquencyWritePlat
|
||||
final DelinquencyBucket delinquencyBucket = loan.getLoanProduct().getDelinquencyBucket();
|
||||
if (delinquencyBucket != null) {
|
||||
final LocalDate businessDate = DateUtils.getBusinessLocalDate();
|
||||
LoanRepaymentScheduleInstallment loanRepaymentSchedule = this.loanRepaymentScheduleInstallmentRepository
|
||||
.findFirstByLoanAndDueDateLessThanEqualAndObligationsMetOnDateOrderByDueDate(loan, businessDate, null);
|
||||
|
||||
Long overdueDays = 0L;
|
||||
if (loanRepaymentSchedule != null) {
|
||||
overdueDays = DateUtils.getDifferenceInDays(loanRepaymentSchedule.getDueDate(), businessDate);
|
||||
}
|
||||
final Integer graceOnArrearAgeing = loan.getLoanProduct().getLoanProductRelatedDetail().getGraceOnArrearsAgeing();
|
||||
final LocalDate overdueSinceDate = getOverdueSinceDate(loan, businessDate, graceOnArrearAgeing);
|
||||
|
||||
final Long overdueDays = calculateOverdueDays(businessDate, overdueSinceDate);
|
||||
changes = lookUpDelinquencyRange(loan, delinquencyBucket, overdueDays);
|
||||
}
|
||||
return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(loan.getId()).with(changes).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyDelinquencyTagToLoan(Long loanId, Long ageDays) {
|
||||
final Loan loan = this.loanRepository.findOneWithNotFoundDetection(loanId);
|
||||
applyDelinquencyTagToLoan(loan, ageDays);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyDelinquencyTagToLoan(final Loan loan, Long ageDays) {
|
||||
public void applyDelinquencyTagToLoan(LoanScheduleDelinquencyData loanDelinquencyData) {
|
||||
final Loan loan = loanDelinquencyData.getLoan();
|
||||
if (loan.hasDelinquencyBucket()) {
|
||||
lookUpDelinquencyRange(loan, loan.getLoanProduct().getDelinquencyBucket(), ageDays);
|
||||
final LocalDate businessDate = DateUtils.getBusinessLocalDate();
|
||||
final DelinquencyBucket delinquencyBucket = loan.getLoanProduct().getDelinquencyBucket();
|
||||
final Integer graceOnArrearAgeing = loan.getLoanProduct().getLoanProductRelatedDetail().getGraceOnArrearsAgeing();
|
||||
|
||||
LocalDate overdueSinceDate = loanDelinquencyData.getOverdueSinceDate();
|
||||
if (overdueSinceDate == null) {
|
||||
getOverdueSinceDate(loan, businessDate, graceOnArrearAgeing);
|
||||
}
|
||||
|
||||
final Long overdueDays = calculateOverdueDays(businessDate, overdueSinceDate);
|
||||
lookUpDelinquencyRange(loan, delinquencyBucket, overdueDays);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -289,11 +360,11 @@ public class DelinquencyWritePlatformServiceImpl implements DelinquencyWritePlat
|
||||
}
|
||||
}
|
||||
|
||||
private Map<String, Object> lookUpDelinquencyRange(final Loan loan, final DelinquencyBucket delinquencyBucket, long ageDays) {
|
||||
private Map<String, Object> lookUpDelinquencyRange(final Loan loan, final DelinquencyBucket delinquencyBucket, long overdueDays) {
|
||||
Map<String, Object> changes = new HashMap<>();
|
||||
|
||||
if (ageDays <= 0) { // No Delinquency
|
||||
log.debug("Loan {} without delinquency range with {} days", loan.getId(), ageDays);
|
||||
if (overdueDays <= 0) { // No Delinquency
|
||||
log.debug("Loan {} without delinquency range with {} days", loan.getId(), overdueDays);
|
||||
changes = setLoanDelinquencyTag(loan, null);
|
||||
|
||||
} else {
|
||||
@@ -302,27 +373,27 @@ public class DelinquencyWritePlatformServiceImpl implements DelinquencyWritePlat
|
||||
|
||||
for (final DelinquencyRange delinquencyRange : ranges) {
|
||||
if (delinquencyRange.getMaximumAgeDays() == null) { // Last Range in the Bucket
|
||||
if (delinquencyRange.getMinimumAgeDays() <= ageDays) {
|
||||
if (delinquencyRange.getMinimumAgeDays() <= overdueDays) {
|
||||
log.debug("Loan {} with delinquency range {} with {} days", loan.getId(), delinquencyRange.getClassification(),
|
||||
ageDays);
|
||||
overdueDays);
|
||||
changes = setLoanDelinquencyTag(loan, delinquencyRange.getId());
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if (delinquencyRange.getMinimumAgeDays() <= ageDays && delinquencyRange.getMaximumAgeDays() >= ageDays) {
|
||||
if (delinquencyRange.getMinimumAgeDays() <= overdueDays && delinquencyRange.getMaximumAgeDays() >= overdueDays) {
|
||||
log.debug("Loan {} with delinquency range {} with {} days", loan.getId(), delinquencyRange.getClassification(),
|
||||
ageDays);
|
||||
overdueDays);
|
||||
changes = setLoanDelinquencyTag(loan, delinquencyRange.getId());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
changes.put("ageDays", ageDays);
|
||||
changes.put("overdueDays", overdueDays);
|
||||
return changes;
|
||||
}
|
||||
|
||||
public Map<String, Object> setLoanDelinquencyTag(Loan loan, Long delinquencyRangeId) {
|
||||
private Map<String, Object> setLoanDelinquencyTag(Loan loan, Long delinquencyRangeId) {
|
||||
Map<String, Object> changes = new HashMap<>();
|
||||
List<LoanDelinquencyTagHistory> loanDelinquencyTagHistory = new ArrayList<>();
|
||||
final LocalDate transactionDate = DateUtils.getBusinessLocalDate();
|
||||
|
||||
+2
-2
@@ -610,8 +610,8 @@ public class LoansApiResource {
|
||||
|
||||
if (associationParameters.contains(DataTableApiConstant.collectionAssociateParamName)) {
|
||||
mandatoryResponseParameters.add(DataTableApiConstant.collectionAssociateParamName);
|
||||
if (LoanStatus.fromInt(loanBasicDetails.getStatus().getId().intValue()).isActive()) {
|
||||
collectionData = this.loanReadPlatformService.retrieveLoanCollectionData(loanId);
|
||||
if (loanBasicDetails.isActive()) {
|
||||
collectionData = this.delinquencyReadPlatformService.calculateLoanCollectionData(loanId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+10
-28
@@ -20,45 +20,27 @@ package org.apache.fineract.portfolio.loanaccount.data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.experimental.Accessors;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@Accessors(chain = true)
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
@Setter
|
||||
public final class CollectionData {
|
||||
|
||||
private BigDecimal availableDisbursementAmount;
|
||||
private int pastDueDays;
|
||||
private Long pastDueDays;
|
||||
private LocalDate nextPaymentDueDate;
|
||||
private int delinquentDays;
|
||||
private Long delinquentDays;
|
||||
private LocalDate delinquentDate;
|
||||
private BigDecimal delinquentAmount;
|
||||
private LocalDate lastPaymentDate;
|
||||
private BigDecimal lastPaymentAmount;
|
||||
|
||||
private CollectionData(BigDecimal availableDisbursementAmount, int pastDueDays, LocalDate nextPaymentDueDate, int delinquentDays,
|
||||
LocalDate delinquentDate, BigDecimal delinquentAmount, LocalDate lastPaymentDate, BigDecimal lastPaymentAmount) {
|
||||
this.availableDisbursementAmount = availableDisbursementAmount;
|
||||
this.pastDueDays = pastDueDays;
|
||||
this.nextPaymentDueDate = nextPaymentDueDate;
|
||||
this.delinquentDays = delinquentDays;
|
||||
this.delinquentDate = delinquentDate;
|
||||
this.delinquentAmount = delinquentAmount;
|
||||
this.lastPaymentDate = lastPaymentDate;
|
||||
this.lastPaymentAmount = lastPaymentAmount;
|
||||
}
|
||||
|
||||
public static CollectionData instance(BigDecimal availableDisbursementAmount, int pastDueDays, LocalDate nextPaymentDueDate,
|
||||
int delinquentDays, LocalDate delinquentDate, BigDecimal delinquentAmount, LocalDate lastPaymentDate,
|
||||
BigDecimal lastPaymentAmount) {
|
||||
return new CollectionData(availableDisbursementAmount, pastDueDays, nextPaymentDueDate, delinquentDays, delinquentDate,
|
||||
delinquentAmount, lastPaymentDate, lastPaymentAmount);
|
||||
}
|
||||
|
||||
public static CollectionData template() {
|
||||
final BigDecimal zero = BigDecimal.ZERO;
|
||||
return new CollectionData(zero, 0, null, 0, null, zero, null, zero);
|
||||
return new CollectionData(zero, 0L, null, 0L, null, zero, null, zero);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+6
@@ -44,6 +44,7 @@ import org.apache.fineract.portfolio.delinquency.data.DelinquencyRangeData;
|
||||
import org.apache.fineract.portfolio.floatingrates.data.InterestRatePeriodData;
|
||||
import org.apache.fineract.portfolio.fund.data.FundData;
|
||||
import org.apache.fineract.portfolio.group.data.GroupGeneralData;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanStatus;
|
||||
import org.apache.fineract.portfolio.loanaccount.guarantor.data.GuarantorData;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanScheduleData;
|
||||
import org.apache.fineract.portfolio.loanproduct.data.LoanProductBorrowerCycleVariationData;
|
||||
@@ -56,6 +57,7 @@ import org.apache.fineract.portfolio.rate.data.RateData;
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@Accessors(chain = true)
|
||||
@SuppressWarnings("ObjectToString")
|
||||
public class LoanAccountData {
|
||||
|
||||
// basic loan details
|
||||
@@ -1167,4 +1169,8 @@ public class LoanAccountData {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean isActive() {
|
||||
return LoanStatus.fromInt(getStatus().getId().intValue()).isActive();
|
||||
}
|
||||
}
|
||||
|
||||
+9
-4
@@ -22,14 +22,19 @@ import java.io.Serializable;
|
||||
import java.time.LocalDate;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Getter
|
||||
@Setter
|
||||
public class LoanScheduleDelinquencyData implements Serializable {
|
||||
|
||||
private final Long loanId;
|
||||
private final Long productId;
|
||||
private final LocalDate dueDate;
|
||||
private final Long ageDays;
|
||||
private Long loanId;
|
||||
private LocalDate overdueSinceDate;
|
||||
private Long overdueDays;
|
||||
private Loan loan;
|
||||
|
||||
}
|
||||
|
||||
+48
-27
@@ -672,7 +672,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
|
||||
ChangedTransactionDetail changedTransactionDetail = null;
|
||||
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory
|
||||
.determineProcessor(this.transactionProcessingStrategyCode);
|
||||
final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retreiveListOfTransactionsPostDisbursement();
|
||||
final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retrieveListOfTransactionsPostDisbursement();
|
||||
changedTransactionDetail = loanRepaymentScheduleTransactionProcessor.handleTransaction(getDisbursementDate(),
|
||||
allNonContraTransactionsPostDisbursement, getCurrency(), getRepaymentScheduleInstallments(), getActiveCharges());
|
||||
for (final Map.Entry<Long, LoanTransaction> mapEntry : changedTransactionDetail.getNewTransactionMappings().entrySet()) {
|
||||
@@ -832,7 +832,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
|
||||
* Consider removing this block of code or logically completing it for the future by getting the list of
|
||||
* affected Transactions
|
||||
***/
|
||||
final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retreiveListOfTransactionsPostDisbursement();
|
||||
final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retrieveListOfTransactionsPostDisbursement();
|
||||
loanRepaymentScheduleTransactionProcessor.handleTransaction(getDisbursementDate(), allNonContraTransactionsPostDisbursement,
|
||||
getCurrency(), getRepaymentScheduleInstallments(), getActiveCharges());
|
||||
}
|
||||
@@ -900,7 +900,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
|
||||
* Consider removing this block of code or logically completing it for the future by getting the list of
|
||||
* affected Transactions
|
||||
***/
|
||||
final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retreiveListOfTransactionsPostDisbursement();
|
||||
final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retrieveListOfTransactionsPostDisbursement();
|
||||
loanRepaymentScheduleTransactionProcessor.handleTransaction(getDisbursementDate(), allNonContraTransactionsPostDisbursement,
|
||||
getCurrency(), getRepaymentScheduleInstallments(), getActiveCharges());
|
||||
} else {
|
||||
@@ -1087,7 +1087,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
|
||||
* Consider removing this block of code or logically completing it for the future by getting the list of
|
||||
* affected Transactions
|
||||
***/
|
||||
final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retreiveListOfTransactionsPostDisbursement();
|
||||
final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retrieveListOfTransactionsPostDisbursement();
|
||||
loanRepaymentScheduleTransactionProcessor.handleTransaction(getDisbursementDate(), allNonContraTransactionsPostDisbursement,
|
||||
getCurrency(), getRepaymentScheduleInstallments(), getActiveCharges());
|
||||
} else {
|
||||
@@ -1284,7 +1284,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
|
||||
* method updates accrual derived fields on installments and reverse the unprocessed transactions
|
||||
*/
|
||||
private void applyAccurals() {
|
||||
Collection<LoanTransaction> accruals = retreiveListOfAccrualTransactions();
|
||||
Collection<LoanTransaction> accruals = retrieveListOfAccrualTransactions();
|
||||
if (!accruals.isEmpty()) {
|
||||
if (isPeriodicAccrualAccountingEnabledOnLoanProduct()) {
|
||||
applyPeriodicAccruals(accruals);
|
||||
@@ -2634,7 +2634,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
|
||||
private ChangedTransactionDetail reprocessTransactionForDisbursement() {
|
||||
ChangedTransactionDetail changedTransactionDetail = null;
|
||||
if (this.loanProduct.isMultiDisburseLoan()) {
|
||||
final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retreiveListOfTransactionsPostDisbursement();
|
||||
final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retrieveListOfTransactionsPostDisbursement();
|
||||
if (!allNonContraTransactionsPostDisbursement.isEmpty()) {
|
||||
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory
|
||||
.determineProcessor(this.transactionProcessingStrategyCode);
|
||||
@@ -3250,7 +3250,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
|
||||
if (this.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
||||
regenerateRepaymentScheduleWithInterestRecalculation(scheduleGeneratorDTO);
|
||||
}
|
||||
final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retreiveListOfTransactionsPostDisbursement();
|
||||
final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retrieveListOfTransactionsPostDisbursement();
|
||||
changedTransactionDetail = loanRepaymentScheduleTransactionProcessor.handleTransaction(getDisbursementDate(),
|
||||
allNonContraTransactionsPostDisbursement, getCurrency(), getRepaymentScheduleInstallments(), getActiveCharges());
|
||||
for (final Map.Entry<Long, LoanTransaction> mapEntry : changedTransactionDetail.getNewTransactionMappings().entrySet()) {
|
||||
@@ -3300,7 +3300,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
|
||||
return installment;
|
||||
}
|
||||
|
||||
private List<LoanTransaction> retreiveListOfIncomePostingTransactions() {
|
||||
private List<LoanTransaction> retrieveListOfIncomePostingTransactions() {
|
||||
final List<LoanTransaction> incomePostTransactions = new ArrayList<>();
|
||||
List<LoanTransaction> trans = getLoanTransactions();
|
||||
for (final LoanTransaction transaction : trans) {
|
||||
@@ -3313,7 +3313,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
|
||||
return incomePostTransactions;
|
||||
}
|
||||
|
||||
private List<LoanTransaction> retreiveListOfTransactionsPostDisbursement() {
|
||||
private List<LoanTransaction> retrieveListOfTransactionsPostDisbursement() {
|
||||
final List<LoanTransaction> repaymentsOrWaivers = new ArrayList<>();
|
||||
List<LoanTransaction> trans = getLoanTransactions();
|
||||
for (final LoanTransaction transaction : trans) {
|
||||
@@ -3326,7 +3326,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
|
||||
return repaymentsOrWaivers;
|
||||
}
|
||||
|
||||
public List<LoanTransaction> retreiveListOfTransactionsPostDisbursementExcludeAccruals() {
|
||||
public List<LoanTransaction> retrieveListOfTransactionsPostDisbursementExcludeAccruals() {
|
||||
final List<LoanTransaction> repaymentsOrWaivers = new ArrayList<>();
|
||||
for (final LoanTransaction transaction : this.loanTransactions) {
|
||||
if (transaction.isNotReversed()
|
||||
@@ -3340,7 +3340,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
|
||||
return repaymentsOrWaivers;
|
||||
}
|
||||
|
||||
private List<LoanTransaction> retreiveListOfTransactionsExcludeAccruals() {
|
||||
private List<LoanTransaction> retrieveListOfTransactionsExcludeAccruals() {
|
||||
final List<LoanTransaction> repaymentsOrWaivers = new ArrayList<>();
|
||||
for (final LoanTransaction transaction : this.loanTransactions) {
|
||||
if (transaction.isNotReversed() && !(transaction.isAccrual() || transaction.isNonMonetaryTransaction())) {
|
||||
@@ -3352,7 +3352,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
|
||||
return repaymentsOrWaivers;
|
||||
}
|
||||
|
||||
private List<LoanTransaction> retreiveListOfAccrualTransactions() {
|
||||
private List<LoanTransaction> retrieveListOfAccrualTransactions() {
|
||||
final List<LoanTransaction> transactions = new ArrayList<>();
|
||||
for (final LoanTransaction transaction : this.loanTransactions) {
|
||||
if (transaction.isNotReversed() && transaction.isAccrual()) {
|
||||
@@ -3364,6 +3364,18 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
|
||||
return transactions;
|
||||
}
|
||||
|
||||
public List<LoanTransaction> retrieveListOfTransactionsByType(LoanTransactionType transactionType) {
|
||||
final List<LoanTransaction> transactions = new ArrayList<>();
|
||||
for (final LoanTransaction transaction : this.loanTransactions) {
|
||||
if (transaction.isNotReversed() && transaction.getTypeOf().equals(transactionType)) {
|
||||
transactions.add(transaction);
|
||||
}
|
||||
}
|
||||
final LoanTransactionComparator transactionComparator = new LoanTransactionComparator();
|
||||
Collections.sort(transactions, transactionComparator);
|
||||
return transactions;
|
||||
}
|
||||
|
||||
private boolean doPostLoanTransactionChecks(final LocalDate transactionDate,
|
||||
final LoanLifecycleStateMachine loanLifecycleStateMachine) {
|
||||
boolean statusChanged = false;
|
||||
@@ -3403,12 +3415,12 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
|
||||
if (this.loanInterestRecalculationDetails != null && this.loanInterestRecalculationDetails.isCompoundingToBePostedAsTransaction()
|
||||
&& this.getStatus().isClosedObligationsMet()) {
|
||||
LocalDate closedDate = this.getClosedOnDate();
|
||||
reverseTransactionsOnOrAfter(retreiveListOfIncomePostingTransactions(), closedDate);
|
||||
reverseTransactionsOnOrAfter(retreiveListOfAccrualTransactions(), closedDate);
|
||||
reverseTransactionsOnOrAfter(retrieveListOfIncomePostingTransactions(), closedDate);
|
||||
reverseTransactionsOnOrAfter(retrieveListOfAccrualTransactions(), closedDate);
|
||||
HashMap<String, BigDecimal> cumulativeIncomeFromInstallments = new HashMap<>();
|
||||
determineCumulativeIncomeFromInstallments(cumulativeIncomeFromInstallments);
|
||||
HashMap<String, BigDecimal> cumulativeIncomeFromIncomePosting = new HashMap<>();
|
||||
determineCumulativeIncomeDetails(retreiveListOfIncomePostingTransactions(), cumulativeIncomeFromIncomePosting);
|
||||
determineCumulativeIncomeDetails(retrieveListOfIncomePostingTransactions(), cumulativeIncomeFromIncomePosting);
|
||||
BigDecimal interestToPost = cumulativeIncomeFromInstallments.get(INTEREST)
|
||||
.subtract(cumulativeIncomeFromIncomePosting.get(INTEREST));
|
||||
BigDecimal feeToPost = cumulativeIncomeFromInstallments.get(FEE).subtract(cumulativeIncomeFromIncomePosting.get(FEE));
|
||||
@@ -3419,7 +3431,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
|
||||
interestToPost, feeToPost, penaltyToPost);
|
||||
addLoanTransaction(finalIncomeTransaction);
|
||||
if (isPeriodicAccrualAccountingEnabledOnLoanProduct()) {
|
||||
List<LoanTransaction> updatedAccrualTransactions = retreiveListOfAccrualTransactions();
|
||||
List<LoanTransaction> updatedAccrualTransactions = retrieveListOfAccrualTransactions();
|
||||
LocalDate lastAccruedDate = this.getDisbursementDate();
|
||||
if (!updatedAccrualTransactions.isEmpty()) {
|
||||
lastAccruedDate = updatedAccrualTransactions.get(updatedAccrualTransactions.size() - 1).getTransactionDate();
|
||||
@@ -3646,7 +3658,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
|
||||
loanLifecycleStateMachine.transition(LoanEvent.WRITE_OFF_OUTSTANDING_UNDO, this);
|
||||
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory
|
||||
.determineProcessor(this.transactionProcessingStrategyCode);
|
||||
final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retreiveListOfTransactionsPostDisbursement();
|
||||
final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retrieveListOfTransactionsPostDisbursement();
|
||||
if (this.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
||||
regenerateRepaymentScheduleWithInterestRecalculation(scheduleGeneratorDTO);
|
||||
}
|
||||
@@ -3789,7 +3801,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
|
||||
if (this.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
||||
regenerateRepaymentScheduleWithInterestRecalculation(scheduleGeneratorDTO);
|
||||
}
|
||||
final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retreiveListOfTransactionsPostDisbursement();
|
||||
final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retrieveListOfTransactionsPostDisbursement();
|
||||
changedTransactionDetail = loanRepaymentScheduleTransactionProcessor.handleTransaction(getDisbursementDate(),
|
||||
allNonContraTransactionsPostDisbursement, getCurrency(), getRepaymentScheduleInstallments(), getActiveCharges());
|
||||
for (final Map.Entry<Long, LoanTransaction> mapEntry : changedTransactionDetail.getNewTransactionMappings().entrySet()) {
|
||||
@@ -4856,6 +4868,15 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
|
||||
return currentTransactionDate;
|
||||
}
|
||||
|
||||
public LoanTransaction getLastRepaymentTransaction() {
|
||||
for (final LoanTransaction loanTransaction : this.loanTransactions) {
|
||||
if (!loanTransaction.isReversed() && loanTransaction.isRepaymentType()) {
|
||||
return loanTransaction;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public LocalDate getLastUserTransactionForChargeCalc() {
|
||||
LocalDate lastTransaction = getDisbursementDate();
|
||||
if (this.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
||||
@@ -5130,7 +5151,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
|
||||
|
||||
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory
|
||||
.determineProcessor(this.transactionProcessingStrategyCode);
|
||||
final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retreiveListOfTransactionsPostDisbursement();
|
||||
final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retrieveListOfTransactionsPostDisbursement();
|
||||
ChangedTransactionDetail changedTransactionDetail = loanRepaymentScheduleTransactionProcessor.handleTransaction(
|
||||
getDisbursementDate(), allNonContraTransactionsPostDisbursement, getCurrency(), getRepaymentScheduleInstallments(),
|
||||
getActiveCharges());
|
||||
@@ -5243,7 +5264,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
|
||||
existingReversedTransactionIds.addAll(findExistingReversedTransactionIds());
|
||||
/*
|
||||
* LocalDate recalculateFrom = null; List<LoanTransaction> loanTransactions =
|
||||
* this.retreiveListOfTransactionsPostDisbursementExcludeAccruals(); for (LoanTransaction loanTransaction :
|
||||
* this.retrieveListOfTransactionsPostDisbursementExcludeAccruals(); for (LoanTransaction loanTransaction :
|
||||
* loanTransactions) { if (recalculateFrom == null ||
|
||||
* loanTransaction.getTransactionDate().isAfter(recalculateFrom)) { recalculateFrom =
|
||||
* loanTransaction.getTransactionDate(); } } generatorDTO.setRecalculateFrom(recalculateFrom);
|
||||
@@ -5266,7 +5287,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
|
||||
public ChangedTransactionDetail processTransactions() {
|
||||
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory
|
||||
.determineProcessor(this.transactionProcessingStrategyCode);
|
||||
final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retreiveListOfTransactionsPostDisbursement();
|
||||
final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retrieveListOfTransactionsPostDisbursement();
|
||||
ChangedTransactionDetail changedTransactionDetail = loanRepaymentScheduleTransactionProcessor.handleTransaction(
|
||||
getDisbursementDate(), allNonContraTransactionsPostDisbursement, getCurrency(), getRepaymentScheduleInstallments(),
|
||||
getActiveCharges());
|
||||
@@ -5344,8 +5365,8 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
|
||||
if (this.loanInterestRecalculationDetails != null && this.loanInterestRecalculationDetails.isCompoundingToBePostedAsTransaction()) {
|
||||
LocalDate lastCompoundingDate = this.getDisbursementDate();
|
||||
List<LoanInterestRecalcualtionAdditionalDetails> compoundingDetails = extractInterestRecalculationAdditionalDetails();
|
||||
List<LoanTransaction> incomeTransactions = retreiveListOfIncomePostingTransactions();
|
||||
List<LoanTransaction> accrualTransactions = retreiveListOfAccrualTransactions();
|
||||
List<LoanTransaction> incomeTransactions = retrieveListOfIncomePostingTransactions();
|
||||
List<LoanTransaction> accrualTransactions = retrieveListOfAccrualTransactions();
|
||||
for (LoanInterestRecalcualtionAdditionalDetails compoundingDetail : compoundingDetails) {
|
||||
if (!compoundingDetail.getEffectiveDate().isBefore(DateUtils.getBusinessLocalDate())) {
|
||||
break;
|
||||
@@ -5495,7 +5516,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
|
||||
public void processPostDisbursementTransactions() {
|
||||
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory
|
||||
.determineProcessor(this.transactionProcessingStrategyCode);
|
||||
final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retreiveListOfTransactionsPostDisbursement();
|
||||
final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retrieveListOfTransactionsPostDisbursement();
|
||||
final List<LoanTransaction> copyTransactions = new ArrayList<>();
|
||||
if (!allNonContraTransactionsPostDisbursement.isEmpty()) {
|
||||
for (LoanTransaction loanTransaction : allNonContraTransactionsPostDisbursement) {
|
||||
@@ -5653,7 +5674,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
|
||||
|
||||
private void updateLoanOutstandingBalances() {
|
||||
Money outstanding = Money.zero(getCurrency());
|
||||
List<LoanTransaction> loanTransactions = retreiveListOfTransactionsExcludeAccruals();
|
||||
List<LoanTransaction> loanTransactions = retrieveListOfTransactionsExcludeAccruals();
|
||||
for (LoanTransaction loanTransaction : loanTransactions) {
|
||||
if (loanTransaction.isDisbursement() || loanTransaction.isIncomePosting()) {
|
||||
outstanding = outstanding.plus(loanTransaction.getAmount(getCurrency()));
|
||||
@@ -6099,7 +6120,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
|
||||
loanRepaymentScheduleTransactionProcessor.handleRefund(loanTransaction, getCurrency(), getRepaymentScheduleInstallments(),
|
||||
getActiveCharges());
|
||||
} else {
|
||||
final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retreiveListOfTransactionsPostDisbursement();
|
||||
final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retrieveListOfTransactionsPostDisbursement();
|
||||
changedTransactionDetail = loanRepaymentScheduleTransactionProcessor.handleTransaction(getDisbursementDate(),
|
||||
allNonContraTransactionsPostDisbursement, getCurrency(), getRepaymentScheduleInstallments(), getActiveCharges());
|
||||
for (final Map.Entry<Long, LoanTransaction> mapEntry : changedTransactionDetail.getNewTransactionMappings().entrySet()) {
|
||||
@@ -6191,7 +6212,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
|
||||
validateActivityNotBeforeClientOrGroupTransferDate(LoanEvent.LOAN_DISBURSAL_UNDO_LAST, getDisbursementDate());
|
||||
LocalDate actualDisbursementDate = null;
|
||||
LocalDate lastTransactionDate = getDisbursementDate();
|
||||
List<LoanTransaction> loanTransactions = retreiveListOfTransactionsExcludeAccruals();
|
||||
List<LoanTransaction> loanTransactions = retrieveListOfTransactionsExcludeAccruals();
|
||||
Collections.reverse(loanTransactions);
|
||||
for (final LoanTransaction previousTransaction : loanTransactions) {
|
||||
if (lastTransactionDate.isBefore(previousTransaction.getTransactionDate())
|
||||
|
||||
+9
-6
@@ -84,6 +84,7 @@ import org.apache.fineract.portfolio.group.domain.Group;
|
||||
import org.apache.fineract.portfolio.group.exception.GroupNotActiveException;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.HolidayDetailDTO;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanScheduleAccrualData;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanScheduleDelinquencyData;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.ScheduleGeneratorDTO;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanAccrualPlatformService;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanAssembler;
|
||||
@@ -541,13 +542,15 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
|
||||
|
||||
@Override
|
||||
public void setLoanDelinquencyTag(final Loan loan, final LocalDate transactionDate) {
|
||||
// Revalidate the Delinquency Classification
|
||||
final Long ageOfOverdueDays = loan.getAgeOfOverdueDays(transactionDate);
|
||||
log.debug("Loan {} with {} days and current classification {}", loan.getId(), ageOfOverdueDays);
|
||||
if (ageOfOverdueDays > 0L) { // If loan is overdue
|
||||
this.delinquencyWritePlatformService.applyDelinquencyTagToLoan(loan, ageOfOverdueDays);
|
||||
LoanScheduleDelinquencyData loanDelinquencyData = new LoanScheduleDelinquencyData(loan.getId(), transactionDate, null, loan);
|
||||
loanDelinquencyData = this.delinquencyWritePlatformService.calculateDelinquencyData(loanDelinquencyData);
|
||||
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);
|
||||
} else {
|
||||
this.delinquencyWritePlatformService.removeDelinquencyTagToLoan(loan);
|
||||
this.delinquencyWritePlatformService.removeDelinquencyTagToLoan(loanDelinquencyData.getLoan());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+4
-2
@@ -637,7 +637,10 @@ public class LoanRepaymentScheduleInstallment extends AbstractAuditableWithUTCDa
|
||||
private void checkIfRepaymentPeriodObligationsAreMet(final LocalDate transactionDate, final MonetaryCurrency currency) {
|
||||
this.obligationsMet = getTotalOutstanding(currency).isZero();
|
||||
if (this.obligationsMet) {
|
||||
this.obligationsMetOnDate = transactionDate;
|
||||
this.obligationsMet = getCredits(currency).isZero();
|
||||
if (this.obligationsMet) {
|
||||
this.obligationsMetOnDate = transactionDate;
|
||||
}
|
||||
} else {
|
||||
this.obligationsMetOnDate = null;
|
||||
}
|
||||
@@ -704,7 +707,6 @@ public class LoanRepaymentScheduleInstallment extends AbstractAuditableWithUTCDa
|
||||
this.principal = this.principal.add(transactionAmount.getAmount());
|
||||
}
|
||||
checkIfRepaymentPeriodObligationsAreMet(transactionDate, transactionAmount.getCurrency());
|
||||
|
||||
}
|
||||
|
||||
public void addToCredits(final BigDecimal amount) {
|
||||
|
||||
+35
-4
@@ -19,16 +19,47 @@
|
||||
package org.apache.fineract.portfolio.loanaccount.domain;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanScheduleDelinquencyData;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
|
||||
public interface LoanRepaymentScheduleInstallmentRepository
|
||||
extends JpaRepository<LoanRepaymentScheduleInstallment, Long>, JpaSpecificationExecutor<LoanRepaymentScheduleInstallment> {
|
||||
|
||||
LoanRepaymentScheduleInstallment findFirstByLoanAndDueDateLessThanEqualAndObligationsMetOnDateOrderByDueDate(Loan loan,
|
||||
LocalDate businnessDate, LocalDate obligationsMetOnDate);
|
||||
@Query("""
|
||||
SELECT new org.apache.fineract.portfolio.loanaccount.data.LoanScheduleDelinquencyData(
|
||||
lrs.loan.id,
|
||||
min(lrs.dueDate),
|
||||
0L,
|
||||
lrs.loan
|
||||
) FROM LoanRepaymentScheduleInstallment lrs
|
||||
WHERE lrs.loan.loanStatus = :loanStatus AND
|
||||
lrs.dueDate <= :businessDate AND
|
||||
lrs.obligationsMet = :obligationsMet AND
|
||||
lrs.loan.loanProduct.delinquencyBucket IS NOT NULL
|
||||
GROUP BY lrs.loan
|
||||
""")
|
||||
Collection<LoanScheduleDelinquencyData> fetchLoanScheduleDataByDueDateAndObligationsMet(Integer loanStatus, LocalDate businessDate,
|
||||
boolean obligationsMet);
|
||||
|
||||
@Query("""
|
||||
SELECT new org.apache.fineract.portfolio.loanaccount.data.LoanScheduleDelinquencyData(
|
||||
lrs.loan.id,
|
||||
min(lrs.dueDate),
|
||||
0L,
|
||||
lrs.loan
|
||||
) FROM LoanRepaymentScheduleInstallment lrs
|
||||
WHERE lrs.loan.loanStatus = :loanStatus AND
|
||||
lrs.dueDate <= :businessDate AND
|
||||
lrs.obligationsMet = :obligationsMet AND
|
||||
lrs.loan.loanProduct.delinquencyBucket IS NOT NULL AND
|
||||
lrs.loan.id NOT IN :loanIds
|
||||
GROUP BY lrs.loan
|
||||
""")
|
||||
Collection<LoanScheduleDelinquencyData> fetchLoanScheduleDataByDueDateAndObligationsMet(Integer loanStatus, LocalDate businessDate,
|
||||
boolean obligationsMet, List<Long> loanIds);
|
||||
|
||||
List<LoanRepaymentScheduleInstallment> findByLoanAndDueDateLessThanEqualAndObligationsMetOnDateOrderByDueDate(Loan loan,
|
||||
LocalDate businnessDate, LocalDate obligationsMetOnDate);
|
||||
}
|
||||
|
||||
+19
@@ -18,11 +18,30 @@
|
||||
*/
|
||||
package org.apache.fineract.portfolio.loanaccount.domain;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.Collection;
|
||||
import java.util.Optional;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanScheduleDelinquencyData;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
|
||||
public interface LoanTransactionRepository extends JpaRepository<LoanTransaction, Long>, JpaSpecificationExecutor<LoanTransaction> {
|
||||
|
||||
Optional<LoanTransaction> findByIdAndLoanId(Long transactionId, Long loanId);
|
||||
|
||||
@Query("""
|
||||
SELECT new org.apache.fineract.portfolio.loanaccount.data.LoanScheduleDelinquencyData(
|
||||
lt.loan.id,
|
||||
min(lt.dateOf),
|
||||
0L,
|
||||
lt.loan
|
||||
) FROM LoanTransaction lt
|
||||
WHERE lt.typeOf = :transactionType and
|
||||
lt.dateOf <= :businessDate and
|
||||
lt.loan.loanProduct.delinquencyBucket is not null
|
||||
GROUP BY lt.loan
|
||||
""")
|
||||
Collection<LoanScheduleDelinquencyData> fetchLoanTransactionsByTypeAndLessOrEqualDate(Integer transactionType, LocalDate businessDate);
|
||||
|
||||
}
|
||||
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.loanaccount.domain;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
|
||||
public interface LoanTransactionToRepaymentScheduleMappingRepository extends JpaRepository<LoanTransactionToRepaymentScheduleMapping, Long>,
|
||||
JpaSpecificationExecutor<LoanTransactionToRepaymentScheduleMapping> {
|
||||
|
||||
LoanTransactionToRepaymentScheduleMapping findByLoanTransaction(LoanTransaction loanTransaction);
|
||||
|
||||
}
|
||||
+1
-1
@@ -540,8 +540,8 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen
|
||||
for (final LoanRepaymentScheduleInstallment currentInstallment : installments) {
|
||||
pastDueDate = currentInstallment.getDueDate();
|
||||
if (!currentInstallment.isAdditional() && currentInstallment.getDueDate().isAfter(transactionDate)) {
|
||||
currentInstallment.addToPrincipal(transactionDate, transactionAmountUnprocessed);
|
||||
currentInstallment.addToCredits(transactionAmountUnprocessed.getAmount());
|
||||
currentInstallment.addToPrincipal(transactionDate, transactionAmountUnprocessed);
|
||||
transactionMappings.add(LoanTransactionToRepaymentScheduleMapping.createFrom(loanTransaction, currentInstallment,
|
||||
transactionAmountUnprocessed, zeroMoney, zeroMoney, zeroMoney));
|
||||
|
||||
|
||||
+6
-3
@@ -21,7 +21,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.service.DelinquencyWritePlatformService;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanReadPlatformService;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallmentRepository;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository;
|
||||
import org.springframework.batch.core.Job;
|
||||
import org.springframework.batch.core.Step;
|
||||
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
|
||||
@@ -38,7 +39,8 @@ public class SetLoanDelinquencyTagsConfig {
|
||||
private StepBuilderFactory steps;
|
||||
|
||||
private DelinquencyWritePlatformService delinquencyWritePlatformService;
|
||||
private LoanReadPlatformService loanReadPlatformService;
|
||||
private LoanRepaymentScheduleInstallmentRepository loanRepaymentScheduleInstallmentRepository;
|
||||
private LoanTransactionRepository loanTransactionRepository;
|
||||
|
||||
@Bean
|
||||
public Step setLoanDelinquencyTagsStep() {
|
||||
@@ -53,7 +55,8 @@ public class SetLoanDelinquencyTagsConfig {
|
||||
|
||||
@Bean
|
||||
public SetLoanDelinquencyTagsTasklet setLoanDelinquencyTagsTasklet() {
|
||||
return new SetLoanDelinquencyTagsTasklet(delinquencyWritePlatformService, loanReadPlatformService);
|
||||
return new SetLoanDelinquencyTagsTasklet(delinquencyWritePlatformService, loanRepaymentScheduleInstallmentRepository,
|
||||
loanTransactionRepository);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+49
-13
@@ -18,14 +18,20 @@
|
||||
*/
|
||||
package org.apache.fineract.portfolio.loanaccount.jobs.setloandelinquencytags;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
||||
import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil;
|
||||
import org.apache.fineract.portfolio.delinquency.service.DelinquencyWritePlatformService;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanScheduleDelinquencyData;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanReadPlatformService;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallmentRepository;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanStatus;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType;
|
||||
import org.springframework.batch.core.StepContribution;
|
||||
import org.springframework.batch.core.scope.context.ChunkContext;
|
||||
import org.springframework.batch.core.step.tasklet.Tasklet;
|
||||
@@ -36,24 +42,54 @@ import org.springframework.batch.repeat.RepeatStatus;
|
||||
public class SetLoanDelinquencyTagsTasklet implements Tasklet {
|
||||
|
||||
private final DelinquencyWritePlatformService delinquencyWritePlatformService;
|
||||
private final LoanReadPlatformService loanReadPlatformService;
|
||||
private final LoanRepaymentScheduleInstallmentRepository loanRepaymentScheduleInstallmentRepository;
|
||||
private final LoanTransactionRepository loanTransactionRepository;
|
||||
|
||||
@Override
|
||||
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
|
||||
log.debug("Run job for date {}", DateUtils.getBusinessLocalDate());
|
||||
Collection<LoanScheduleDelinquencyData> loanScheduleDelinquencyData = this.loanReadPlatformService
|
||||
.retrieveScheduleDelinquencyData(DateUtils.getBusinessLocalDate());
|
||||
log.debug("Were found {} items", loanScheduleDelinquencyData.size());
|
||||
for (LoanScheduleDelinquencyData loanDelinquencyData : loanScheduleDelinquencyData) {
|
||||
log.debug("Processing Loan {} with due date {} and {} overdue days", loanDelinquencyData.getLoanId(),
|
||||
loanDelinquencyData.getDueDate(), loanDelinquencyData.getAgeDays());
|
||||
this.delinquencyWritePlatformService.applyDelinquencyTagToLoan(loanDelinquencyData.getLoanId(),
|
||||
loanDelinquencyData.getAgeDays());
|
||||
}
|
||||
|
||||
final LocalDate businessDate = DateUtils.getBusinessLocalDate();
|
||||
log.debug("Run job for date {}", businessDate);
|
||||
|
||||
// Read Loan Ids with Loan Transaction Charge back
|
||||
Collection<LoanScheduleDelinquencyData> loanScheduleDelinquencyData = this.loanTransactionRepository
|
||||
.fetchLoanTransactionsByTypeAndLessOrEqualDate(LoanTransactionType.CHARGEBACK.getValue(), businessDate);
|
||||
List<Long> processedLoans = applyDelinquencyTagToLoans(loanScheduleDelinquencyData);
|
||||
log.debug("{}: Records affected by setLoanDelinquencyTags: {}", ThreadLocalContextUtil.getTenant().getName(),
|
||||
loanScheduleDelinquencyData.size());
|
||||
processedLoans.size());
|
||||
|
||||
// Read Loan Ids with overdue installments
|
||||
if (processedLoans.isEmpty()) {
|
||||
loanScheduleDelinquencyData = this.loanRepaymentScheduleInstallmentRepository
|
||||
.fetchLoanScheduleDataByDueDateAndObligationsMet(LoanStatus.ACTIVE.getValue(), businessDate, false);
|
||||
} else {
|
||||
loanScheduleDelinquencyData = this.loanRepaymentScheduleInstallmentRepository
|
||||
.fetchLoanScheduleDataByDueDateAndObligationsMet(LoanStatus.ACTIVE.getValue(), businessDate, false, processedLoans);
|
||||
}
|
||||
processedLoans = applyDelinquencyTagToLoans(loanScheduleDelinquencyData);
|
||||
|
||||
return RepeatStatus.FINISHED;
|
||||
}
|
||||
|
||||
private List<Long> applyDelinquencyTagToLoans(Collection<LoanScheduleDelinquencyData> loanScheduleDelinquencyData) {
|
||||
List<Long> processedLoans = new ArrayList<>();
|
||||
|
||||
log.debug("Were found {} items", loanScheduleDelinquencyData.size());
|
||||
for (LoanScheduleDelinquencyData loanDelinquencyData : loanScheduleDelinquencyData) {
|
||||
// Set the data used by Delinquency Classification method
|
||||
loanDelinquencyData = this.delinquencyWritePlatformService.calculateDelinquencyData(loanDelinquencyData);
|
||||
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);
|
||||
} else {
|
||||
this.delinquencyWritePlatformService.removeDelinquencyTagToLoan(loanDelinquencyData.getLoan());
|
||||
}
|
||||
|
||||
processedLoans.add(loanDelinquencyData.getLoanId());
|
||||
}
|
||||
return processedLoans;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+1
-1
@@ -2671,7 +2671,7 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
||||
|
||||
LoanScheduleDTO loanScheduleDTO = rescheduleNextInstallments(mc, loanApplicationTerms, loan, holidayDetailDTO,
|
||||
loanRepaymentScheduleTransactionProcessor, onDate, calculateTill);
|
||||
List<LoanTransaction> loanTransactions = loan.retreiveListOfTransactionsPostDisbursementExcludeAccruals();
|
||||
List<LoanTransaction> loanTransactions = loan.retrieveListOfTransactionsPostDisbursementExcludeAccruals();
|
||||
|
||||
loanRepaymentScheduleTransactionProcessor.handleTransaction(loanApplicationTerms.getExpectedDisbursementDate(), loanTransactions,
|
||||
currency, loanScheduleDTO.getInstallments(), loan.getActiveCharges());
|
||||
|
||||
-6
@@ -27,13 +27,11 @@ import org.apache.fineract.infrastructure.core.service.SearchParameters;
|
||||
import org.apache.fineract.organisation.staff.data.StaffData;
|
||||
import org.apache.fineract.portfolio.calendar.data.CalendarData;
|
||||
import org.apache.fineract.portfolio.floatingrates.data.InterestRatePeriodData;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.CollectionData;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.DisbursementData;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanAccountData;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanApprovalData;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanRepaymentScheduleInstallmentData;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanScheduleAccrualData;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanScheduleDelinquencyData;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanTermVariationsData;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanTransactionData;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanTransactionRelationData;
|
||||
@@ -109,8 +107,6 @@ public interface LoanReadPlatformService {
|
||||
|
||||
Collection<LoanScheduleAccrualData> retriveScheduleAccrualData();
|
||||
|
||||
Collection<LoanScheduleDelinquencyData> retrieveScheduleDelinquencyData(LocalDate businessLocalDate);
|
||||
|
||||
LoanTransactionData retrieveRecoveryPaymentTemplate(Long loanId);
|
||||
|
||||
LoanTransactionData retrieveLoanWriteoffTemplate(Long loanId);
|
||||
@@ -157,8 +153,6 @@ public interface LoanReadPlatformService {
|
||||
|
||||
List<LoanRepaymentScheduleInstallmentData> getRepaymentDataResponse(Long loanId);
|
||||
|
||||
CollectionData retrieveLoanCollectionData(Long loanId);
|
||||
|
||||
List<LoanTransactionRelationData> retrieveLoanTransactionRelationsByLoanTransactionId(Long loanTransactionId);
|
||||
|
||||
}
|
||||
|
||||
+8
-115
@@ -84,7 +84,6 @@ import org.apache.fineract.portfolio.group.data.GroupGeneralData;
|
||||
import org.apache.fineract.portfolio.group.data.GroupRoleData;
|
||||
import org.apache.fineract.portfolio.group.service.GroupReadPlatformService;
|
||||
import org.apache.fineract.portfolio.loanaccount.api.LoanApiConstants;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.CollectionData;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.DisbursementData;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanAccountData;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanApplicationTimelineData;
|
||||
@@ -92,7 +91,6 @@ import org.apache.fineract.portfolio.loanaccount.data.LoanApprovalData;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanInterestRecalculationData;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanRepaymentScheduleInstallmentData;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanScheduleAccrualData;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanScheduleDelinquencyData;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanStatusEnumData;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanSummaryData;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanTermVariationsData;
|
||||
@@ -240,7 +238,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
||||
this.context.authenticatedUser();
|
||||
|
||||
final LoanScheduleResultSetExtractor fullResultsetExtractor = new LoanScheduleResultSetExtractor(
|
||||
repaymentScheduleRelatedLoanData, disbursementData, isInterestRecalculationEnabled, totalPaidFeeCharges);
|
||||
repaymentScheduleRelatedLoanData, disbursementData, isInterestRecalculationEnabled);
|
||||
final String sql = "select " + fullResultsetExtractor.schema() + " where ls.loan_id = ? order by ls.loan_id, ls.installment";
|
||||
|
||||
return this.jdbcTemplate.query(sql, fullResultsetExtractor, loanId); // NOSONAR
|
||||
@@ -639,7 +637,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
||||
+ " l.loan_sub_status_id as loanSubStatusId, la.principal_overdue_derived as principalOverdue, l.is_fraud as isFraud, "
|
||||
+ " la.interest_overdue_derived as interestOverdue, la.fee_charges_overdue_derived as feeChargesOverdue,"
|
||||
+ " la.penalty_charges_overdue_derived as penaltyChargesOverdue, la.total_overdue_derived as totalOverdue,"
|
||||
+ " la.overdue_since_date_derived as overdueSinceDate,"
|
||||
+ " la.overdue_since_date_derived as overdueSinceDate, "
|
||||
+ " l.sync_disbursement_with_meeting as syncDisbursementWithMeeting,"
|
||||
+ " l.loan_counter as loanCounter, l.loan_product_counter as loanProductCounter,"
|
||||
+ " l.is_npa as isNPA, l.days_in_month_enum as daysInMonth, l.days_in_year_enum as daysInYear, "
|
||||
@@ -678,7 +676,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
||||
+ " left join m_code_value codev on codev.id = l.writeoff_reason_cv_id"
|
||||
+ " left join m_product_loan_variable_installment_config lpvi on lpvi.loan_product_id = l.product_id"
|
||||
+ " left join m_loan_topup as topup on l.id = topup.loan_id"
|
||||
+ " left join m_loan as topuploan on topuploan.id = topup.closure_loan_id";
|
||||
+ " left join m_loan as topuploan on topuploan.id = topup.closure_loan_id ";
|
||||
|
||||
}
|
||||
|
||||
@@ -888,9 +886,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
||||
final BigDecimal totalRecovered = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "totalRecovered");
|
||||
|
||||
final LocalDate overdueSinceDate = JdbcSupport.getLocalDate(rs, "overdueSinceDate");
|
||||
if (overdueSinceDate != null) {
|
||||
inArrears = true;
|
||||
}
|
||||
inArrears = (overdueSinceDate != null);
|
||||
|
||||
loanSummary = new LoanSummaryData(currencyData, principalDisbursed, principalAdjustments, principalPaid,
|
||||
principalWrittenOff, principalOutstanding, principalOverdue, interestCharged, interestPaid, interestWaived,
|
||||
@@ -1062,10 +1058,9 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
||||
private LocalDate lastDueDate;
|
||||
private BigDecimal outstandingLoanPrincipalBalance;
|
||||
private boolean excludePastUndisbursed;
|
||||
private final BigDecimal totalPaidFeeCharges;
|
||||
|
||||
LoanScheduleResultSetExtractor(final RepaymentScheduleRelatedLoanData repaymentScheduleRelatedLoanData,
|
||||
Collection<DisbursementData> disbursementData, boolean isInterestRecalculationEnabled, BigDecimal totalPaidFeeCharges) {
|
||||
Collection<DisbursementData> disbursementData, boolean isInterestRecalculationEnabled) {
|
||||
this.currency = repaymentScheduleRelatedLoanData.getCurrency();
|
||||
this.disbursement = repaymentScheduleRelatedLoanData.disbursementData();
|
||||
this.totalFeeChargesDueAtDisbursement = repaymentScheduleRelatedLoanData.getTotalFeeChargesAtDisbursement();
|
||||
@@ -1073,7 +1068,6 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
||||
this.outstandingLoanPrincipalBalance = this.disbursement.getPrincipal();
|
||||
this.disbursementData = disbursementData;
|
||||
this.excludePastUndisbursed = isInterestRecalculationEnabled;
|
||||
this.totalPaidFeeCharges = totalPaidFeeCharges;
|
||||
}
|
||||
|
||||
public String schema() {
|
||||
@@ -1699,25 +1693,6 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<LoanScheduleDelinquencyData> retrieveScheduleDelinquencyData(LocalDate businessLocalDate) {
|
||||
LoanScheduleDelinquencyMapper mapper = new LoanScheduleDelinquencyMapper(DateUtils.getBusinessLocalDate());
|
||||
final StringBuilder sqlBuilder = new StringBuilder(400);
|
||||
sqlBuilder.append("select ").append(mapper.schema())
|
||||
// Just get the overdued installments
|
||||
.append(" where loan.loan_status_id=:active and ls.duedate <= :businessLocalDate and ")
|
||||
// Just get the unpaid installments using the completed_derived flag
|
||||
.append(" ls.completed_derived = false and ")
|
||||
// Just the Loan Product linked to delinquency bucket
|
||||
.append(" mpl.delinquency_bucket_id is not null ");
|
||||
sqlBuilder.append(" group by ls.loan_id, loan.product_id ");
|
||||
Map<String, Object> paramMap = new HashMap<>(3);
|
||||
paramMap.put("active", LoanStatus.ACTIVE.getValue());
|
||||
paramMap.put("businessLocalDate", businessLocalDate);
|
||||
|
||||
return this.namedParameterJdbcTemplate.query(sqlBuilder.toString(), paramMap, mapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<LoanScheduleAccrualData> retriveScheduleAccrualData() {
|
||||
|
||||
@@ -1887,33 +1862,6 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
||||
}
|
||||
}
|
||||
|
||||
private static final class LoanScheduleDelinquencyMapper implements RowMapper<LoanScheduleDelinquencyData> {
|
||||
|
||||
private LocalDate businessDate;
|
||||
|
||||
LoanScheduleDelinquencyMapper(LocalDate businessDate) {
|
||||
this.businessDate = businessDate;
|
||||
}
|
||||
|
||||
public String schema() {
|
||||
final StringBuilder sqlBuilder = new StringBuilder(400);
|
||||
sqlBuilder.append(" ls.loan_id as loanId, loan.product_id as productId, min(ls.duedate) as duedate ")
|
||||
.append(" from m_loan_repayment_schedule ls left join m_loan loan on loan.id=ls.loan_id ")
|
||||
.append(" left join m_product_loan mpl on mpl.id = loan.product_id ");
|
||||
return sqlBuilder.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoanScheduleDelinquencyData mapRow(ResultSet rs, @SuppressWarnings("unused") int rowNum) throws SQLException {
|
||||
|
||||
final Long loanId = rs.getLong("loanId");
|
||||
final Long productId = rs.getLong("productId");
|
||||
final LocalDate dueDate = JdbcSupport.getLocalDate(rs, "duedate");
|
||||
final long ageDays = DateUtils.getDifferenceInDays(dueDate, businessDate);
|
||||
return new LoanScheduleDelinquencyData(loanId, productId, dueDate, ageDays);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoanTransactionData retrieveRecoveryPaymentTemplate(Long loanId) {
|
||||
final Loan loan = this.loanRepositoryWrapper.findOneWithNotFoundDetection(loanId, true);
|
||||
@@ -2283,7 +2231,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
||||
@Override
|
||||
public Collection<Long> retrieveLoanIdsWithPendingIncomePostingTransactions() {
|
||||
LocalDate currentdate = DateUtils.getBusinessLocalDate();
|
||||
StringBuilder sqlBuilder = new StringBuilder().append(" select distinct loan.id ").append(" from m_loan as loan ").append(
|
||||
StringBuilder sqlBuilder = new StringBuilder().append(" select distinct loan.id from m_loan as loan ").append(
|
||||
" inner join m_loan_recalculation_details as recdet on (recdet.loan_id = loan.id and recdet.is_compounding_to_be_posted_as_transaction is not null and recdet.is_compounding_to_be_posted_as_transaction = true) ")
|
||||
.append(" inner join m_loan_repayment_schedule as repsch on repsch.loan_id = loan.id ")
|
||||
.append(" inner join m_loan_interest_recalculation_additional_details as adddet on adddet.loan_repayment_schedule_id = repsch.id ")
|
||||
@@ -2292,7 +2240,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
||||
.append(" and adddet.effective_date is not null ").append(" and trans.transaction_date is null ")
|
||||
.append(" and adddet.effective_date < ? ");
|
||||
try {
|
||||
return this.jdbcTemplate.queryForList(sqlBuilder.toString(), new Object[] { currentdate }, Long.class);
|
||||
return this.jdbcTemplate.queryForList(sqlBuilder.toString(), Long.class, new Object[] { currentdate });
|
||||
} catch (final EmptyResultDataAccessException e) {
|
||||
return null;
|
||||
}
|
||||
@@ -2438,62 +2386,6 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
||||
return this.jdbcTemplate.queryForObject(sql, Integer.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CollectionData retrieveLoanCollectionData(Long loanId) {
|
||||
final CollectionDataMapper mapper = new CollectionDataMapper(sqlGenerator);
|
||||
String sql = "select " + mapper.schema();
|
||||
CollectionData collectionData = this.jdbcTemplate.queryForObject(sql, mapper, loanId, loanId); // NOSONAR
|
||||
return collectionData;
|
||||
}
|
||||
|
||||
private static final class CollectionDataMapper implements RowMapper<CollectionData> {
|
||||
|
||||
private final DatabaseSpecificSQLGenerator sqlGenerator;
|
||||
|
||||
CollectionDataMapper(DatabaseSpecificSQLGenerator sqlGenerator) {
|
||||
this.sqlGenerator = sqlGenerator;
|
||||
}
|
||||
|
||||
public String schema() {
|
||||
StringBuilder sqlBuilder = new StringBuilder();
|
||||
|
||||
sqlBuilder.append(
|
||||
"l.id as loanId, coalesce((l.approved_principal - l.principal_disbursed_derived), 0) as availableDisbursementAmount, ");
|
||||
sqlBuilder.append(
|
||||
sqlGenerator.dateDiff(sqlGenerator.currentBusinessDate(), "laa.overdue_since_date_derived") + " as pastDueDays, ");
|
||||
sqlBuilder.append(
|
||||
"(select coalesce(min(lrs.duedate), null) as duedate from m_loan_repayment_schedule lrs where lrs.loan_id=l.id and lrs.completed_derived is false and lrs.duedate >= "
|
||||
+ sqlGenerator.currentBusinessDate() + ") as nextPaymentDueDate, ");
|
||||
sqlBuilder.append(
|
||||
sqlGenerator.dateDiff(sqlGenerator.currentBusinessDate(), "laa.overdue_since_date_derived") + " as delinquentDays, ");
|
||||
sqlBuilder.append(sqlGenerator.currentBusinessDate()
|
||||
+ " as delinquentDate, coalesce(laa.total_overdue_derived, 0) as delinquentAmount, ");
|
||||
sqlBuilder.append("lre.transactionDate as lastPaymentDate, coalesce(lre.amount, 0) as lastPaymentAmount ");
|
||||
sqlBuilder.append("from m_loan l left join m_loan_arrears_aging laa on laa.loan_id = l.id ");
|
||||
sqlBuilder.append(
|
||||
"left join (select lt.loan_id, lt.transaction_date as transactionDate, lt.amount as amount from m_loan_transaction lt ");
|
||||
sqlBuilder.append(
|
||||
"where lt.loan_id =? and lt.is_reversed = false and lt.transaction_type_enum=2 order by lt.transaction_date desc, lt.id desc limit 1) lre on lre.loan_id = l.id ");
|
||||
sqlBuilder.append("where l.id=? ");
|
||||
return sqlBuilder.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CollectionData mapRow(ResultSet rs, int rowNum) throws SQLException {
|
||||
final LocalDate nextPaymentDueDate = JdbcSupport.getLocalDate(rs, "nextPaymentDueDate");
|
||||
final LocalDate delinquentDate = JdbcSupport.getLocalDate(rs, "delinquentDate");
|
||||
final LocalDate lastPaymentDate = JdbcSupport.getLocalDate(rs, "lastPaymentDate");
|
||||
final BigDecimal availableDisbursementAmount = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "availableDisbursementAmount");
|
||||
final BigDecimal delinquentAmount = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "delinquentAmount");
|
||||
final BigDecimal lastPaymentAmount = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "lastPaymentAmount");
|
||||
final int pastDueDays = rs.getInt("pastDueDays");
|
||||
final int delinquentDays = rs.getInt("delinquentDays");
|
||||
|
||||
return CollectionData.instance(availableDisbursementAmount, pastDueDays, nextPaymentDueDate, delinquentDays, delinquentDate,
|
||||
delinquentAmount, lastPaymentDate, lastPaymentAmount);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<LoanTransactionRelationData> retrieveLoanTransactionRelationsByLoanTransactionId(Long loanTransactionId) {
|
||||
final LoanTransaction loanTransaction = this.loanTransactionRepository.getReferenceById(loanTransactionId);
|
||||
@@ -2501,4 +2393,5 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
||||
.findByFromTransactionAndRelationType(loanTransaction, LoanTransactionRelationTypeEnum.CHARGEBACK);
|
||||
return loanTransactionRelationMapper.map(loanTransactionRelations);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+1
-1
@@ -1289,7 +1289,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
}
|
||||
|
||||
postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
|
||||
this.loanAccountDomainService.setLoanDelinquencyTag(loan, DateUtils.getBusinessLocalDate());
|
||||
this.loanAccountDomainService.setLoanDelinquencyTag(loan, transactionDate);
|
||||
|
||||
businessEventNotifierService.notifyPostBusinessEvent(new LoanChargebackTransactionBusinessEvent(loanTransaction));
|
||||
|
||||
|
||||
+2
-2
@@ -388,7 +388,7 @@ public class FixedDepositAccount extends SavingsAccount {
|
||||
throw new PlatformApiDataValidationException(dataValidationErrors);
|
||||
}
|
||||
}
|
||||
final List<SavingsAccountTransaction> savingsAccountTransactions = retreiveListOfTransactions();
|
||||
final List<SavingsAccountTransaction> savingsAccountTransactions = retrieveListOfTransactions();
|
||||
if (savingsAccountTransactions.size() > 0) {
|
||||
final SavingsAccountTransaction accountTransaction = savingsAccountTransactions.get(savingsAccountTransactions.size() - 1);
|
||||
if (accountTransaction.isAfter(closedDate)) {
|
||||
@@ -473,7 +473,7 @@ public class FixedDepositAccount extends SavingsAccount {
|
||||
}
|
||||
}
|
||||
|
||||
final List<SavingsAccountTransaction> savingsAccountTransactions = retreiveListOfTransactions();
|
||||
final List<SavingsAccountTransaction> savingsAccountTransactions = retrieveListOfTransactions();
|
||||
if (savingsAccountTransactions.size() > 0) {
|
||||
final SavingsAccountTransaction accountTransaction = savingsAccountTransactions.get(savingsAccountTransactions.size() - 1);
|
||||
if (accountTransaction.isAfter(closedDate)) {
|
||||
|
||||
+3
-3
@@ -492,7 +492,7 @@ public class RecurringDepositAccount extends SavingsAccount {
|
||||
}
|
||||
}
|
||||
|
||||
final List<SavingsAccountTransaction> savingsAccountTransactions = retreiveListOfTransactions();
|
||||
final List<SavingsAccountTransaction> savingsAccountTransactions = retrieveListOfTransactions();
|
||||
if (savingsAccountTransactions.size() > 0) {
|
||||
final SavingsAccountTransaction accountTransaction = savingsAccountTransactions.get(savingsAccountTransactions.size() - 1);
|
||||
if (accountTransaction.isAfter(closedDate)) {
|
||||
@@ -593,7 +593,7 @@ public class RecurringDepositAccount extends SavingsAccount {
|
||||
throw new PlatformApiDataValidationException(dataValidationErrors);
|
||||
}
|
||||
}
|
||||
final List<SavingsAccountTransaction> savingsAccountTransactions = retreiveListOfTransactions();
|
||||
final List<SavingsAccountTransaction> savingsAccountTransactions = retrieveListOfTransactions();
|
||||
if (savingsAccountTransactions.size() > 0) {
|
||||
final SavingsAccountTransaction accountTransaction = savingsAccountTransactions.get(savingsAccountTransactions.size() - 1);
|
||||
if (accountTransaction.isAfter(closedDate)) {
|
||||
@@ -934,7 +934,7 @@ public class RecurringDepositAccount extends SavingsAccount {
|
||||
}
|
||||
|
||||
private List<SavingsAccountTransaction> retreiveOrderedDepositTransactions() {
|
||||
final List<SavingsAccountTransaction> listOfTransactionsSorted = retreiveListOfTransactions();
|
||||
final List<SavingsAccountTransaction> listOfTransactionsSorted = retrieveListOfTransactions();
|
||||
|
||||
final List<SavingsAccountTransaction> orderedDepositTransactions = new ArrayList<>();
|
||||
|
||||
|
||||
+10
-10
@@ -928,7 +928,7 @@ public class SavingsAccount extends AbstractPersistableCustom {
|
||||
}
|
||||
|
||||
protected List<SavingsAccountTransaction> retreiveOrderedNonInterestPostingTransactions() {
|
||||
final List<SavingsAccountTransaction> listOfTransactionsSorted = retreiveListOfTransactions();
|
||||
final List<SavingsAccountTransaction> listOfTransactionsSorted = retrieveListOfTransactions();
|
||||
|
||||
final List<SavingsAccountTransaction> orderedNonInterestPostingTransactions = new ArrayList<>();
|
||||
|
||||
@@ -965,7 +965,7 @@ public class SavingsAccount extends AbstractPersistableCustom {
|
||||
return listOfTransactionsSorted;
|
||||
}
|
||||
|
||||
protected List<SavingsAccountTransaction> retreiveListOfTransactions() {
|
||||
protected List<SavingsAccountTransaction> retrieveListOfTransactions() {
|
||||
final List<SavingsAccountTransaction> listOfTransactionsSorted = new ArrayList<>();
|
||||
listOfTransactionsSorted.addAll(this.transactions);
|
||||
|
||||
@@ -983,7 +983,7 @@ public class SavingsAccount extends AbstractPersistableCustom {
|
||||
if (backdatedTxnsAllowedTill) {
|
||||
accountTransactionsSorted = retrieveSortedTransactions();
|
||||
} else {
|
||||
accountTransactionsSorted = retreiveListOfTransactions();
|
||||
accountTransactionsSorted = retrieveListOfTransactions();
|
||||
}
|
||||
|
||||
boolean isTransactionsModified = false;
|
||||
@@ -1058,7 +1058,7 @@ public class SavingsAccount extends AbstractPersistableCustom {
|
||||
if (backdatedTxnsAllowedTill) {
|
||||
accountTransactionsSorted = retrieveSortedTransactions();
|
||||
} else {
|
||||
accountTransactionsSorted = retreiveListOfTransactions();
|
||||
accountTransactionsSorted = retrieveListOfTransactions();
|
||||
}
|
||||
}
|
||||
resetAccountTransactionsEndOfDayBalances(accountTransactionsSorted, interestPostingUpToDate);
|
||||
@@ -1415,7 +1415,7 @@ public class SavingsAccount extends AbstractPersistableCustom {
|
||||
boolean transactionBeforeLastInterestPosting = false;
|
||||
|
||||
if (!backdatedTxnsAllowedTill) {
|
||||
for (final SavingsAccountTransaction transaction : retreiveListOfTransactions()) {
|
||||
for (final SavingsAccountTransaction transaction : retrieveListOfTransactions()) {
|
||||
if ((transaction.isInterestPostingAndNotReversed() || transaction.isOverdraftInterestAndNotReversed())
|
||||
&& transaction.isAfter(transactionDate) && !transaction.isReversalTransaction()) {
|
||||
transactionBeforeLastInterestPosting = true;
|
||||
@@ -1441,7 +1441,7 @@ public class SavingsAccount extends AbstractPersistableCustom {
|
||||
if (backdatedTxnsAllowedTill) {
|
||||
transactionsSortedByDate = retrieveSortedTransactions();
|
||||
} else {
|
||||
transactionsSortedByDate = retreiveListOfTransactions();
|
||||
transactionsSortedByDate = retrieveListOfTransactions();
|
||||
}
|
||||
|
||||
Money runningBalance = Money.zero(this.currency);
|
||||
@@ -1524,7 +1524,7 @@ public class SavingsAccount extends AbstractPersistableCustom {
|
||||
if (backdatedTxnsAllowedTill) {
|
||||
transactionsSortedByDate = retrieveSortedTransactions();
|
||||
} else {
|
||||
transactionsSortedByDate = retreiveListOfTransactions();
|
||||
transactionsSortedByDate = retrieveListOfTransactions();
|
||||
}
|
||||
Money runningBalance = Money.zero(this.currency);
|
||||
|
||||
@@ -2507,7 +2507,7 @@ public class SavingsAccount extends AbstractPersistableCustom {
|
||||
}
|
||||
|
||||
public void validateAccountBalanceDoesNotBecomeNegativeMinimal(final BigDecimal transactionAmount, final boolean isException) {
|
||||
// final List<SavingsAccountTransaction> transactionsSortedByDate = retreiveListOfTransactions();
|
||||
// final List<SavingsAccountTransaction> transactionsSortedByDate = retrieveListOfTransactions();
|
||||
Money runningBalance = this.summary.getAccountBalance(getCurrency());
|
||||
Money minRequiredBalance = minRequiredBalanceDerived(getCurrency());
|
||||
org.joda.time.LocalDate lastSavingsDate = null;
|
||||
@@ -2846,7 +2846,7 @@ public class SavingsAccount extends AbstractPersistableCustom {
|
||||
throw new PlatformApiDataValidationException(dataValidationErrors);
|
||||
}
|
||||
}
|
||||
final List<SavingsAccountTransaction> savingsAccountTransactions = retreiveListOfTransactions();
|
||||
final List<SavingsAccountTransaction> savingsAccountTransactions = retrieveListOfTransactions();
|
||||
if (savingsAccountTransactions.size() > 0) {
|
||||
final SavingsAccountTransaction accountTransaction = savingsAccountTransactions.get(savingsAccountTransactions.size() - 1);
|
||||
if (accountTransaction.isAfter(closedDate)) {
|
||||
@@ -3806,7 +3806,7 @@ public class SavingsAccount extends AbstractPersistableCustom {
|
||||
}
|
||||
|
||||
public LocalDate retrieveLastTransactionDate() {
|
||||
final List<SavingsAccountTransaction> transactionsSortedByDate = retreiveListOfTransactions();
|
||||
final List<SavingsAccountTransaction> transactionsSortedByDate = retrieveListOfTransactions();
|
||||
SavingsAccountTransaction lastTransaction = null;
|
||||
if (transactionsSortedByDate.size() > 0) {
|
||||
lastTransaction = transactionsSortedByDate.get(transactionsSortedByDate.size() - 1);
|
||||
|
||||
+4
-4
@@ -305,7 +305,7 @@ public class SavingsAccountInterestPostingServiceImpl implements SavingsAccountI
|
||||
}
|
||||
|
||||
private List<SavingsAccountTransactionData> retreiveOrderedNonInterestPostingTransactions(final SavingsAccountData savingsAccountData) {
|
||||
final List<SavingsAccountTransactionData> listOfTransactionsSorted = retreiveListOfTransactions(savingsAccountData);
|
||||
final List<SavingsAccountTransactionData> listOfTransactionsSorted = retrieveListOfTransactions(savingsAccountData);
|
||||
|
||||
final List<SavingsAccountTransactionData> orderedNonInterestPostingTransactions = new ArrayList<>();
|
||||
|
||||
@@ -319,7 +319,7 @@ public class SavingsAccountInterestPostingServiceImpl implements SavingsAccountI
|
||||
return orderedNonInterestPostingTransactions;
|
||||
}
|
||||
|
||||
private List<SavingsAccountTransactionData> retreiveListOfTransactions(final SavingsAccountData savingsAccountData) {
|
||||
private List<SavingsAccountTransactionData> retrieveListOfTransactions(final SavingsAccountData savingsAccountData) {
|
||||
final List<SavingsAccountTransactionData> listOfTransactionsSorted = new ArrayList<>();
|
||||
listOfTransactionsSorted.addAll(savingsAccountData.getSavingsAccountTransactionData());
|
||||
|
||||
@@ -407,7 +407,7 @@ public class SavingsAccountInterestPostingServiceImpl implements SavingsAccountI
|
||||
|
||||
Money runningBalance = openingAccountBalance.copy();
|
||||
|
||||
List<SavingsAccountTransactionData> accountTransactionsSorted = retreiveListOfTransactions(savingsAccountData);
|
||||
List<SavingsAccountTransactionData> accountTransactionsSorted = retrieveListOfTransactions(savingsAccountData);
|
||||
boolean isTransactionsModified = false;
|
||||
|
||||
for (final SavingsAccountTransactionData transaction : accountTransactionsSorted) {
|
||||
@@ -466,7 +466,7 @@ public class SavingsAccountInterestPostingServiceImpl implements SavingsAccountI
|
||||
}
|
||||
|
||||
if (isTransactionsModified) {
|
||||
accountTransactionsSorted = retreiveListOfTransactions(savingsAccountData);
|
||||
accountTransactionsSorted = retrieveListOfTransactions(savingsAccountData);
|
||||
}
|
||||
resetAccountTransactionsEndOfDayBalances(accountTransactionsSorted, interestPostingUpToDate, savingsAccountData);
|
||||
}
|
||||
|
||||
@@ -85,4 +85,5 @@
|
||||
<include file="parts/0063_add_permissions_for_external_event_configuration.xml" relativeToChangelogFile="true"/>
|
||||
<include file="parts/0064_refactor_loan_transaction_strategy.xml" relativeToChangelogFile="true"/>
|
||||
<include file="parts/0065_add_bypass_loan_write_transaction_permission.xml" relativeToChangelogFile="true"/>
|
||||
<include file="parts/0066_delinquency_classification_chargeback.xml" relativeToChangelogFile="true"/>
|
||||
</databaseChangeLog>
|
||||
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
|
||||
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.
|
||||
|
||||
-->
|
||||
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.1.xsd">
|
||||
<changeSet id="1" author="fineract">
|
||||
<createIndex indexName="ind_m_loan_delinquency_tag_history_loan_id" tableName="m_loan_delinquency_tag_history">
|
||||
<column name="loan_id"/>
|
||||
</createIndex>
|
||||
<createIndex indexName="ind_m_loan_delinquency_tag_history_liftedon_date" tableName="m_loan_delinquency_tag_history">
|
||||
<column name="liftedon_date"/>
|
||||
</createIndex>
|
||||
</changeSet>
|
||||
</databaseChangeLog>
|
||||
Reference in New Issue
Block a user