Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
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.LoanTransactionRepository;
import org.springframework.lang.NonNull;
import org.springframework.transaction.annotation.Transactional;

Expand All @@ -77,6 +78,7 @@ public class DelinquencyReadPlatformServiceImpl implements DelinquencyReadPlatfo
private final LoanDelinquencyActionRepository loanDelinquencyActionRepository;
private final DelinquencyEffectivePauseHelper delinquencyEffectivePauseHelper;
private final ConfigurationDomainService configurationDomainService;
private final LoanTransactionRepository loanTransactionRepository;

@Override
public List<DelinquencyRangeData> retrieveAllDelinquencyRanges() {
Expand Down Expand Up @@ -174,7 +176,7 @@ public CollectionData calculateLoanCollectionData(final Long loanId) {
private void addInstallmentLevelDelinquencyData(CollectionData collectionData, Long loanId) {
Collection<LoanInstallmentDelinquencyTagData> loanInstallmentDelinquencyTagData = retrieveLoanInstallmentsCurrentDelinquencyTag(
loanId);
if (loanInstallmentDelinquencyTagData != null && loanInstallmentDelinquencyTagData.size() > 0) {
if (loanInstallmentDelinquencyTagData != null && !loanInstallmentDelinquencyTagData.isEmpty()) {

// installment level delinquency grouped by rangeId, and summed up the delinquent amount
Collection<InstallmentLevelDelinquency> installmentLevelDelinquencies = loanInstallmentDelinquencyTagData.stream()
Expand Down Expand Up @@ -254,12 +256,7 @@ private LocalDate getEarliestUnpaidInstallmentDate(final Loan loan) {
}
}

LocalDate lastTransactionDate = null;
for (final LoanTransaction transaction : loan.getLoanTransactions()) {
if (transaction.isRepaymentLikeType() && transaction.isGreaterThanZero()) {
lastTransactionDate = transaction.getTransactionDate();
}
}
final LocalDate lastTransactionDate = loanTransactionRepository.findLastRepaymentLikeTransactionDate(loan).orElse(null);

LocalDate possibleNextRepaymentDate = earliestUnpaidInstallmentDate;
if (DateUtils.isAfter(lastTransactionDate, earliestUnpaidInstallmentDate)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
import org.apache.fineract.portfolio.delinquency.validator.DelinquencyRangeParseAndValidator;
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepository;
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepositoryWrapper;
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository;
import org.apache.fineract.portfolio.loanaccount.service.LoanTransactionReadService;
import org.apache.fineract.portfolio.loanproduct.domain.LoanProductRepository;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
Expand All @@ -60,11 +61,12 @@ public DelinquencyReadPlatformService delinquencyReadPlatformService(Delinquency
LoanDelinquencyDomainService loanDelinquencyDomainService,
LoanInstallmentDelinquencyTagRepository repositoryLoanInstallmentDelinquencyTag,
LoanDelinquencyActionRepository loanDelinquencyActionRepository,
DelinquencyEffectivePauseHelper delinquencyEffectivePauseHelper, ConfigurationDomainService configurationDomainService) {
DelinquencyEffectivePauseHelper delinquencyEffectivePauseHelper, ConfigurationDomainService configurationDomainService,
LoanTransactionRepository loanTransactionRepository) {
return new DelinquencyReadPlatformServiceImpl(repositoryRange, repositoryBucket, repositoryLoanDelinquencyTagHistory, mapperRange,
mapperBucket, mapperLoanDelinquencyTagHistory, loanRepository, loanDelinquencyDomainService,
repositoryLoanInstallmentDelinquencyTag, loanDelinquencyActionRepository, delinquencyEffectivePauseHelper,
configurationDomainService);
configurationDomainService, loanTransactionRepository);
}

@Bean
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,6 @@
import org.apache.fineract.infrastructure.core.domain.AbstractAuditableWithUTCDateTimeCustom;
import org.apache.fineract.infrastructure.core.domain.ExternalId;
import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.infrastructure.core.service.MathUtil;
import org.apache.fineract.infrastructure.security.service.RandomPasswordGenerator;
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
import org.apache.fineract.organisation.monetary.domain.Money;
Expand All @@ -74,7 +73,6 @@
import org.apache.fineract.portfolio.group.domain.Group;
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.LoanRepaymentScheduleTransactionProcessor;
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanApplicationTerms;
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleModel;
import org.apache.fineract.portfolio.loanproduct.domain.LoanProduct;
import org.apache.fineract.portfolio.loanproduct.domain.LoanProductRelatedDetail;
import org.apache.fineract.portfolio.loanproduct.domain.LoanSupportedInterestRefundTypes;
Expand Down Expand Up @@ -433,13 +431,13 @@ public static Loan newIndividualLoanApplication(final String accountNo, final Cl
final List<LoanDisbursementDetails> disbursementDetails, final BigDecimal maxOutstandingLoanBalance,
final Boolean createStandingInstructionAtDisbursement, final Boolean isFloatingInterestRate,
final BigDecimal interestRateDifferential, final List<Rate> rates, final BigDecimal fixedPrincipalPercentagePerInstallment,
final ExternalId externalId, final LoanApplicationTerms loanApplicationTerms, final LoanScheduleModel loanScheduleModel,
final Boolean enableInstallmentLevelDelinquency, final LocalDate submittedOnDate) {
final ExternalId externalId, final LoanApplicationTerms loanApplicationTerms, final Boolean enableInstallmentLevelDelinquency,
final LocalDate submittedOnDate) {
return new Loan(accountNo, client, null, loanType, fund, officer, loanPurpose, transactionProcessingStrategy, loanProduct,
loanRepaymentScheduleDetail, null, loanCharges, collateral, null, fixedEmiAmount, disbursementDetails,
maxOutstandingLoanBalance, createStandingInstructionAtDisbursement, isFloatingInterestRate, interestRateDifferential, rates,
fixedPrincipalPercentagePerInstallment, externalId, loanApplicationTerms, loanScheduleModel,
enableInstallmentLevelDelinquency, submittedOnDate);
fixedPrincipalPercentagePerInstallment, externalId, loanApplicationTerms, enableInstallmentLevelDelinquency,
submittedOnDate);
}

public static Loan newGroupLoanApplication(final String accountNo, final Group group, final AccountType loanType,
Expand All @@ -450,13 +448,13 @@ public static Loan newGroupLoanApplication(final String accountNo, final Group g
final List<LoanDisbursementDetails> disbursementDetails, final BigDecimal maxOutstandingLoanBalance,
final Boolean createStandingInstructionAtDisbursement, final Boolean isFloatingInterestRate,
final BigDecimal interestRateDifferential, final List<Rate> rates, final BigDecimal fixedPrincipalPercentagePerInstallment,
final ExternalId externalId, final LoanApplicationTerms loanApplicationTerms, final LoanScheduleModel loanScheduleModel,
final Boolean enableInstallmentLevelDelinquency, final LocalDate submittedOnDate) {
final ExternalId externalId, final LoanApplicationTerms loanApplicationTerms, final Boolean enableInstallmentLevelDelinquency,
final LocalDate submittedOnDate) {
return new Loan(accountNo, null, group, loanType, fund, officer, loanPurpose, transactionProcessingStrategy, loanProduct,
loanRepaymentScheduleDetail, null, loanCharges, null, syncDisbursementWithMeeting, fixedEmiAmount, disbursementDetails,
maxOutstandingLoanBalance, createStandingInstructionAtDisbursement, isFloatingInterestRate, interestRateDifferential, rates,
fixedPrincipalPercentagePerInstallment, externalId, loanApplicationTerms, loanScheduleModel,
enableInstallmentLevelDelinquency, submittedOnDate);
fixedPrincipalPercentagePerInstallment, externalId, loanApplicationTerms, enableInstallmentLevelDelinquency,
submittedOnDate);
}

public static Loan newIndividualLoanApplicationFromGroup(final String accountNo, final Client client, final Group group,
Expand All @@ -467,13 +465,13 @@ public static Loan newIndividualLoanApplicationFromGroup(final String accountNo,
final List<LoanDisbursementDetails> disbursementDetails, final BigDecimal maxOutstandingLoanBalance,
final Boolean createStandingInstructionAtDisbursement, final Boolean isFloatingInterestRate,
final BigDecimal interestRateDifferential, final List<Rate> rates, final BigDecimal fixedPrincipalPercentagePerInstallment,
final ExternalId externalId, final LoanApplicationTerms loanApplicationTerms, final LoanScheduleModel loanScheduleModel,
final Boolean enableInstallmentLevelDelinquency, final LocalDate submittedOnDate) {
final ExternalId externalId, final LoanApplicationTerms loanApplicationTerms, final Boolean enableInstallmentLevelDelinquency,
final LocalDate submittedOnDate) {
return new Loan(accountNo, client, group, loanType, fund, officer, loanPurpose, transactionProcessingStrategy, loanProduct,
loanRepaymentScheduleDetail, null, loanCharges, null, syncDisbursementWithMeeting, fixedEmiAmount, disbursementDetails,
maxOutstandingLoanBalance, createStandingInstructionAtDisbursement, isFloatingInterestRate, interestRateDifferential, rates,
fixedPrincipalPercentagePerInstallment, externalId, loanApplicationTerms, loanScheduleModel,
enableInstallmentLevelDelinquency, submittedOnDate);
fixedPrincipalPercentagePerInstallment, externalId, loanApplicationTerms, enableInstallmentLevelDelinquency,
submittedOnDate);
}

protected Loan() {
Expand All @@ -488,8 +486,8 @@ private Loan(final String accountNo, final Client client, final Group group, fin
final List<LoanDisbursementDetails> disbursementDetails, final BigDecimal maxOutstandingLoanBalance,
final Boolean createStandingInstructionAtDisbursement, final Boolean isFloatingInterestRate,
final BigDecimal interestRateDifferential, final List<Rate> rates, final BigDecimal fixedPrincipalPercentagePerInstallment,
final ExternalId externalId, final LoanApplicationTerms loanApplicationTerms, final LoanScheduleModel loanScheduleModel,
final Boolean enableInstallmentLevelDelinquency, final LocalDate submittedOnDate) {
final ExternalId externalId, final LoanApplicationTerms loanApplicationTerms, final Boolean enableInstallmentLevelDelinquency,
final LocalDate submittedOnDate) {
this.loanRepaymentScheduleDetail = loanRepaymentScheduleDetail;

this.isFloatingInterestRate = isFloatingInterestRate;
Expand Down Expand Up @@ -850,17 +848,6 @@ public void removePostDatedChecks() {
this.postDatedChecks = new ArrayList<>();
}

public List<LoanTransaction> retrieveListOfTransactionsExcludeAccruals() {
final List<LoanTransaction> repaymentsOrWaivers = new ArrayList<>();
for (final LoanTransaction transaction : this.loanTransactions) {
if (transaction.isNotReversed() && !transaction.isNonMonetaryTransaction()) {
repaymentsOrWaivers.add(transaction);
}
}
repaymentsOrWaivers.sort(LoanTransactionComparator.INSTANCE);
return repaymentsOrWaivers;
}

public List<LoanTransaction> retrieveListOfTransactionsByType(final LoanTransactionType transactionType) {
return this.loanTransactions.stream()
.filter(transaction -> transaction.isNotReversed() && transaction.getTypeOf().equals(transactionType))
Expand All @@ -880,11 +867,6 @@ public LoanTransaction findWriteOffTransaction() {
.orElse(null);
}

public Money calculateTotalRecoveredPayments() {
// in case logic for reversing recovered payment is implemented handle subtraction from totalRecoveredPayments
return getTotalRecoveredPayments();
}

public MonetaryCurrency loanCurrency() {
return this.loanRepaymentScheduleDetail.getCurrency();
}
Expand Down Expand Up @@ -1033,17 +1015,6 @@ public Money getTotalPaidInRepayments() {
return cumulativePaid;
}

public Money getTotalRecoveredPayments() {
Money cumulativePaid = Money.zero(getCurrency());

for (final LoanTransaction recoveredPayment : this.loanTransactions) {
if (recoveredPayment.isRecoveryRepayment()) {
cumulativePaid = cumulativePaid.plus(recoveredPayment.getAmount(getCurrency()));
}
}
return cumulativePaid;
}

public Money getTotalPrincipalOutstandingUntil(LocalDate date) {
return getRepaymentScheduleInstallments().stream()
.filter(installment -> installment.getDueDate().isBefore(date) || installment.getDueDate().isEqual(date))
Expand Down Expand Up @@ -1236,7 +1207,7 @@ public LocalDate getLastUserTransactionDate() {
.filter(date -> DateUtils.isBefore(getDisbursementDate(), date)).max(LocalDate::compareTo).orElse(getDisbursementDate());
}

private boolean isUserTransaction(LoanTransaction transaction) {
public boolean isUserTransaction(LoanTransaction transaction) {
return !(transaction.isReversed() || transaction.isAccrualRelated() || transaction.isIncomePosting());
}

Expand Down Expand Up @@ -1391,47 +1362,6 @@ public LocalDate fetchInterestRecalculateFromDate() {
return recalculatedOn;
}

public void updateLoanOutstandingBalances() {
Money outstanding = Money.zero(getCurrency());
List<LoanTransaction> loanTransactions = retrieveListOfTransactionsExcludeAccruals();
for (LoanTransaction loanTransaction : loanTransactions) {
if (loanTransaction.isDisbursement() || loanTransaction.isIncomePosting() || loanTransaction.isCapitalizedIncome()) {
outstanding = outstanding.plus(loanTransaction.getAmount(getCurrency()))
.minus(loanTransaction.getOverPaymentPortion(getCurrency()));
loanTransaction.updateOutstandingLoanBalance(MathUtil.negativeToZero(outstanding.getAmount()));
} else if (loanTransaction.isChargeback() || loanTransaction.isCreditBalanceRefund()) {
Money transactionOutstanding = loanTransaction.getPrincipalPortion(getCurrency());
if (loanTransaction.isOverPaid()) {
// in case of advanced payment strategy and creditAllocations the full amount is recognized first
if (this.getCreditAllocationRules() != null && !this.getCreditAllocationRules().isEmpty()) {
Money payedPrincipal = loanTransaction.getLoanTransactionToRepaymentScheduleMappings().stream() //
.map(mapping -> mapping.getPrincipalPortion(getCurrency())) //
.reduce(Money.zero(getCurrency()), Money::plus);
transactionOutstanding = loanTransaction.getPrincipalPortion(getCurrency()).minus(payedPrincipal);
} else {
// in case legacy payment strategy
transactionOutstanding = loanTransaction.getAmount(getCurrency())
.minus(loanTransaction.getOverPaymentPortion(getCurrency()));
}
if (transactionOutstanding.isLessThanZero()) {
transactionOutstanding = Money.zero(getCurrency());
}
}
outstanding = outstanding.plus(transactionOutstanding);
loanTransaction.updateOutstandingLoanBalance(MathUtil.negativeToZero(outstanding.getAmount()));
} else if (!loanTransaction.isAccrualActivity()) {
if (this.loanInterestRecalculationDetails != null
&& this.loanInterestRecalculationDetails.isCompoundingToBePostedAsTransaction()
&& !loanTransaction.isRepaymentAtDisbursement()) {
outstanding = outstanding.minus(loanTransaction.getAmount(getCurrency()));
} else {
outstanding = outstanding.minus(loanTransaction.getPrincipalPortion(getCurrency()));
}
loanTransaction.updateOutstandingLoanBalance(MathUtil.negativeToZero(outstanding.getAmount()));
}
}
}

public String transactionProcessingStrategy() {
return this.transactionProcessingStrategyCode;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -911,10 +911,6 @@ public boolean isPaymentTransaction() {
|| this.isIncomePosting());
}

public boolean hasLoanTransactionRelations() {
return !loanTransactionRelations.isEmpty();
}

public List<LoanTransactionRelation> getLoanTransactionRelations(Predicate<LoanTransactionRelation> predicate) {
return loanTransactionRelations.stream().filter(predicate).toList();
}
Expand Down
Loading
Loading