From cb6a15c3d222a7c199450a4aa803ee3f546ac15a Mon Sep 17 00:00:00 2001 From: Oleksii Novikov Date: Wed, 25 Jun 2025 10:08:57 +0300 Subject: [PATCH] FINERACT-2234: Replace in-memory transaction loops with repository queries --- .../DelinquencyReadPlatformServiceImpl.java | 11 +- .../starter/DelinquencyConfiguration.java | 6 +- .../portfolio/loanaccount/domain/Loan.java | 100 +++-------------- .../loanaccount/domain/LoanTransaction.java | 4 - .../domain/LoanTransactionRepository.java | 102 ++++++++++++++++-- ...rgeRepaymentScheduleProcessingWrapper.java | 1 - ...RepaymentScheduleTransactionProcessor.java | 25 ++--- ...stractCumulativeLoanScheduleGenerator.java | 39 +++---- ...gBalanceInterestLoanScheduleGenerator.java | 8 +- ...tiveFlatInterestLoanScheduleGenerator.java | 8 +- .../serialization/LoanChargeValidator.java | 20 ++-- .../service/LoanBalanceService.java | 63 ++++++++++- ...edPaymentScheduleTransactionProcessor.java | 22 ++-- ...talizedIncomeWritePlatformServiceImpl.java | 2 +- .../InternalProgressiveLoanApiResource.java | 7 +- ...ymentScheduleTransactionProcessorTest.java | 4 +- .../domain/LoanAccountDomainServiceJpa.java | 3 +- .../LoanAccrualsProcessingServiceImpl.java | 4 +- .../service/LoanAssemblerImpl.java | 8 +- .../service/LoanDisbursementService.java | 3 +- .../LoanTransactionProcessingServiceImpl.java | 2 +- ...WritePlatformServiceJpaRepositoryImpl.java | 14 +-- .../ProgressiveLoanSummaryDataProvider.java | 7 +- .../ReprocessLoanTransactionsServiceImpl.java | 21 +--- .../LoanContractTerminationServiceImpl.java | 2 +- .../starter/LoanAccountAutoStarter.java | 6 +- .../starter/LoanAccountConfiguration.java | 4 +- ...ntDelinquencyRangeEventSerializerTest.java | 2 +- .../loanaccount/domain/LoanBuilder.java | 11 +- 29 files changed, 281 insertions(+), 228 deletions(-) diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/delinquency/service/DelinquencyReadPlatformServiceImpl.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/delinquency/service/DelinquencyReadPlatformServiceImpl.java index 29e0b0843..7406584af 100644 --- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/delinquency/service/DelinquencyReadPlatformServiceImpl.java +++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/delinquency/service/DelinquencyReadPlatformServiceImpl.java @@ -58,6 +58,7 @@ 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.LoanTransactionRepository; import org.springframework.lang.NonNull; import org.springframework.transaction.annotation.Transactional; @@ -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 retrieveAllDelinquencyRanges() { @@ -174,7 +176,7 @@ public class DelinquencyReadPlatformServiceImpl implements DelinquencyReadPlatfo private void addInstallmentLevelDelinquencyData(CollectionData collectionData, Long loanId) { Collection 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 installmentLevelDelinquencies = loanInstallmentDelinquencyTagData.stream() @@ -254,12 +256,7 @@ public class DelinquencyReadPlatformServiceImpl implements DelinquencyReadPlatfo } } - 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)) { diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/delinquency/starter/DelinquencyConfiguration.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/delinquency/starter/DelinquencyConfiguration.java index 71e638219..530ce477f 100644 --- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/delinquency/starter/DelinquencyConfiguration.java +++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/delinquency/starter/DelinquencyConfiguration.java @@ -42,6 +42,7 @@ import org.apache.fineract.portfolio.delinquency.validator.DelinquencyBucketPars 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; @@ -60,11 +61,12 @@ public class DelinquencyConfiguration { 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 diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java index 3c8d29df2..8951bdd50 100644 --- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java +++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java @@ -59,7 +59,6 @@ import org.apache.fineract.infrastructure.codes.domain.CodeValue; 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; @@ -74,7 +73,6 @@ import org.apache.fineract.portfolio.fund.domain.Fund; 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; @@ -433,13 +431,13 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom { final List disbursementDetails, final BigDecimal maxOutstandingLoanBalance, final Boolean createStandingInstructionAtDisbursement, final Boolean isFloatingInterestRate, final BigDecimal interestRateDifferential, final List 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, @@ -450,13 +448,13 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom { final List disbursementDetails, final BigDecimal maxOutstandingLoanBalance, final Boolean createStandingInstructionAtDisbursement, final Boolean isFloatingInterestRate, final BigDecimal interestRateDifferential, final List 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, @@ -467,13 +465,13 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom { final List disbursementDetails, final BigDecimal maxOutstandingLoanBalance, final Boolean createStandingInstructionAtDisbursement, final Boolean isFloatingInterestRate, final BigDecimal interestRateDifferential, final List 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() { @@ -488,8 +486,8 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom { final List disbursementDetails, final BigDecimal maxOutstandingLoanBalance, final Boolean createStandingInstructionAtDisbursement, final Boolean isFloatingInterestRate, final BigDecimal interestRateDifferential, final List 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; @@ -850,17 +848,6 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom { this.postDatedChecks = new ArrayList<>(); } - public List retrieveListOfTransactionsExcludeAccruals() { - final List 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 retrieveListOfTransactionsByType(final LoanTransactionType transactionType) { return this.loanTransactions.stream() .filter(transaction -> transaction.isNotReversed() && transaction.getTypeOf().equals(transactionType)) @@ -880,11 +867,6 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom { .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(); } @@ -1033,17 +1015,6 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom { 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)) @@ -1236,7 +1207,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom { .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()); } @@ -1391,47 +1362,6 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom { return recalculatedOn; } - public void updateLoanOutstandingBalances() { - Money outstanding = Money.zero(getCurrency()); - List 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; } diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransaction.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransaction.java index 397343026..9023a8347 100644 --- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransaction.java +++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransaction.java @@ -911,10 +911,6 @@ public class LoanTransaction extends AbstractAuditableWithUTCDateTimeCustom getLoanTransactionRelations(Predicate predicate) { return loanTransactionRelations.stream().filter(predicate).toList(); } diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransactionRepository.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransactionRepository.java index 04b791eb4..3934f0253 100644 --- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransactionRepository.java +++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransactionRepository.java @@ -127,7 +127,8 @@ public interface LoanTransactionRepository extends JpaRepository findLastTransactionDateForReprocessing(@Param("loan") Loan loan); @@ -174,7 +175,7 @@ public interface LoanTransactionRepository extends JpaRepository findNonReversedByLoanAndTypesAndDate(@Param("loan") Loan loan, @Param("type") Set types, + Optional findNonReversedByLoanAndTypesAndDate(@Param("loan") Loan loan, @Param("types") Set types, @Param("transactionDate") LocalDate transactionDate); @Query(""" @@ -184,7 +185,7 @@ public interface LoanTransactionRepository extends JpaRepository :transactionDate - ORDER BY lt.dateOf + ORDER BY lt.dateOf, lt.createdDate, lt.id """) List findNonReversedByLoanAndTypesAndAfterDate(@Param("loan") Loan loan, @Param("types") Set types, @Param("transactionDate") LocalDate transactionDate); @@ -196,7 +197,7 @@ public interface LoanTransactionRepository extends JpaRepository :transactionDate - ORDER BY lt.dateOf + ORDER BY lt.dateOf, lt.createdDate, lt.id """) List findNonReversedByLoanAndTypeAndAfterDate(@Param("loan") Loan loan, @Param("type") LoanTransactionType type, @Param("transactionDate") LocalDate transactionDate); @@ -219,6 +220,7 @@ public interface LoanTransactionRepository extends JpaRepository findNonReversedByLoanAndTypesAndNotInIds(@Param("loan") Loan loan, @Param("types") Set types, @Param("existingTransactionIds") List existingTransactionIds); @@ -229,6 +231,7 @@ public interface LoanTransactionRepository extends JpaRepository findNonReversedByLoanAndTypes(@Param("loan") Loan loan, @Param("types") Set types); @@ -238,6 +241,7 @@ public interface LoanTransactionRepository extends JpaRepository findNonReversedByLoanAndType(@Param("loan") Loan loan, @Param("type") LoanTransactionType type); @@ -248,7 +252,7 @@ public interface LoanTransactionRepository extends JpaRepository= :date AND lt.typeOf IN :types - ORDER BY lt.dateOf + ORDER BY lt.dateOf, lt.createdDate, lt.id """) List findNonReversedByLoanAndTypesAndOnOrAfterDate(@Param("loan") Loan loan, @Param("types") Set types, @Param("date") LocalDate date); @@ -333,7 +337,7 @@ public interface LoanTransactionRepository extends JpaRepository findNonReversedLoanAndTypeAndDates(@Param("loan") Loan loan, @Param("type") LoanTransactionType type, @Param("transactionDates") Set transactionDates); @@ -367,4 +371,90 @@ public interface LoanTransactionRepository extends JpaRepository findAdjustmentsForCapitalizedIncome(@Param("capitalizedIncome") LoanTransaction capitalizedIncome); + + @Query(""" + SELECT lt FROM LoanTransaction lt + WHERE lt.loan = :loan + AND lt.reversed = false + AND lt.typeOf NOT IN ( + org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType.CONTRA, + org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType.MARKED_FOR_RESCHEDULING, + org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType.ACCRUAL, + org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType.ACCRUAL_ADJUSTMENT, + org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType.APPROVE_TRANSFER, + org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType.INITIATE_TRANSFER, + org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType.REJECT_TRANSFER, + org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType.WITHDRAW_TRANSFER, + org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType.CAPITALIZED_INCOME_AMORTIZATION, + org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType.CAPITALIZED_INCOME_AMORTIZATION_ADJUSTMENT + ) + ORDER BY lt.dateOf, lt.createdDate, lt.id + """) + List findNonReversedTransactionsForReprocessingByLoan(@Param("loan") Loan loan); + + @Query(""" + SELECT MAX(lt.dateOf) FROM LoanTransaction lt + WHERE lt.loan = :loan + AND lt.reversed = false + AND lt.amount > 0 + AND lt.typeOf IN ( + org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType.REPAYMENT, + org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType.MERCHANT_ISSUED_REFUND, + org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType.PAYOUT_REFUND, + org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType.GOODWILL_CREDIT, + org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType.CHARGE_REFUND, + org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType.CHARGE_ADJUSTMENT, + org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType.DOWN_PAYMENT, + org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType.INTEREST_PAYMENT_WAIVER, + org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType.INTEREST_REFUND, + org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType.CAPITALIZED_INCOME_ADJUSTMENT + ) + """) + Optional findLastRepaymentLikeTransactionDate(@Param("loan") Loan loan); + + @Query(""" + SELECT CASE WHEN COUNT(lt) > 0 THEN true ELSE false END + FROM LoanTransaction lt + WHERE lt.loan = :loan + AND lt.reversed = false + AND lt.typeOf = :type + AND lt.dateOf > :transactionDate + """) + boolean existsNonReversedByLoanAndTypeAndAfterDate(@Param("loan") Loan loan, @Param("type") LoanTransactionType type, + @Param("transactionDate") LocalDate transactionDate); + + @Query(""" + SELECT lt FROM LoanTransaction lt + WHERE lt.loan = :loan + AND lt.reversed = false + AND lt.typeOf NOT IN ( + org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType.CONTRA, + org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType.MARKED_FOR_RESCHEDULING, + org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType.ACCRUAL, + org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType.ACCRUAL_ADJUSTMENT, + org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType.ACCRUAL_ACTIVITY, + org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType.APPROVE_TRANSFER, + org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType.INITIATE_TRANSFER, + org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType.REJECT_TRANSFER, + org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType.WITHDRAW_TRANSFER, + org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType.CHARGE_OFF, + org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType.REAMORTIZE, + org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType.REAGE, + org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType.CAPITALIZED_INCOME_AMORTIZATION, + org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType.CONTRACT_TERMINATION, + org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType.CAPITALIZED_INCOME_AMORTIZATION_ADJUSTMENT + ) + ORDER BY lt.dateOf, lt.createdDate, lt.id + """) + List findNonReversedMonetaryTransactionsByLoan(@Param("loan") Loan loan); + + @Query(""" + SELECT COALESCE(SUM(lt.amount), 0) + FROM LoanTransaction lt + WHERE lt.loan = :loan + AND lt.reversed = false + AND lt.typeOf = org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType.RECOVERY_REPAYMENT + """) + BigDecimal calculateTotalRecoveryPaymentAmount(@Param("loan") Loan loan); + } diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/SingleLoanChargeRepaymentScheduleProcessingWrapper.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/SingleLoanChargeRepaymentScheduleProcessingWrapper.java index d12449b89..f73ed71b6 100644 --- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/SingleLoanChargeRepaymentScheduleProcessingWrapper.java +++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/SingleLoanChargeRepaymentScheduleProcessingWrapper.java @@ -37,7 +37,6 @@ public class SingleLoanChargeRepaymentScheduleProcessingWrapper { public void reprocess(final MonetaryCurrency currency, final LocalDate disbursementDate, final List installments, LoanCharge loanCharge) { - Loan loan = loanCharge.getLoan(); Money zero = Money.zero(currency); Money totalInterest = zero; Money totalPrincipal = zero; diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/AbstractLoanRepaymentScheduleTransactionProcessor.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/AbstractLoanRepaymentScheduleTransactionProcessor.java index a6aaa89ca..b733c459d 100644 --- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/AbstractLoanRepaymentScheduleTransactionProcessor.java +++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/AbstractLoanRepaymentScheduleTransactionProcessor.java @@ -217,11 +217,9 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen loanTransaction.resetDerivedComponents(); handleRefund(loanTransaction, currency, installments, charges); } else if (loanTransaction.isCreditBalanceRefund()) { - recalculateCreditTransaction(changedTransactionDetail, loanTransaction, currency, installments, transactionsToBeProcessed, - overpaymentHolder); + recalculateCreditTransaction(changedTransactionDetail, loanTransaction, currency, installments, overpaymentHolder); } else if (loanTransaction.isChargeback()) { - recalculateCreditTransaction(changedTransactionDetail, loanTransaction, currency, installments, transactionsToBeProcessed, - overpaymentHolder); + recalculateCreditTransaction(changedTransactionDetail, loanTransaction, currency, installments, overpaymentHolder); reprocessChargebackTransactionRelation(changedTransactionDetail, transactionsToBeProcessed); } else if (loanTransaction.isChargeOff()) { recalculateChargeOffTransaction(changedTransactionDetail, loanTransaction, currency, installments); @@ -475,7 +473,7 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen protected void reprocessInstallments(LocalDate disbursementDate, List transactions, List installments, MonetaryCurrency currency) { - LoanRepaymentScheduleInstallment lastInstallment = installments.get(installments.size() - 1); + LoanRepaymentScheduleInstallment lastInstallment = installments.getLast(); if (lastInstallment.isAdditional() && lastInstallment.getDue(currency).isZero()) { installments.remove(lastInstallment); } @@ -519,8 +517,7 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen } private void recalculateCreditTransaction(ChangedTransactionDetail changedTransactionDetail, LoanTransaction loanTransaction, - MonetaryCurrency currency, List installments, List transactionsToBeProcessed, - MoneyHolder overpaymentHolder) { + MonetaryCurrency currency, List installments, MoneyHolder overpaymentHolder) { // pass through for new transactions if (loanTransaction.getId() == null) { return; @@ -606,7 +603,7 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen // New installment will be added (N+1 scenario) if (!loanTransactionMapped) { if (loanTransaction.getTransactionDate().equals(pastDueDate)) { - LoanRepaymentScheduleInstallment currentInstallment = installmentToBeProcessed.get(installmentToBeProcessed.size() - 1); + LoanRepaymentScheduleInstallment currentInstallment = installmentToBeProcessed.getLast(); currentInstallment.addToCreditedPrincipal(transactionAmount.getAmount()); currentInstallment.addToPrincipal(transactionDate, transactionAmount); if (repaidAmount.isGreaterThanZero()) { @@ -647,7 +644,7 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen final Set loanPenalties = extractPenaltyCharges(charges); Integer installmentNumber = null; if (loanTransaction.isChargePayment() && installments.size() == 1) { - installmentNumber = installments.get(0).getInstallmentNumber(); + installmentNumber = installments.getFirst().getInstallmentNumber(); } if (loanTransaction.isNotWaiver() && !loanTransaction.isAccrual() && !loanTransaction.isAccrualActivity()) { @@ -944,12 +941,12 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen return null; } LoanCharge latestCharge = null; - List chargesWithSpecificDueDate = new ArrayList<>(); - chargesWithSpecificDueDate.addAll(charges.stream().filter(charge -> charge.isSpecifiedDueDate()).toList()); + final List chargesWithSpecificDueDate = new ArrayList<>( + charges.stream().filter(LoanCharge::isSpecifiedDueDate).toList()); if (!CollectionUtils.isEmpty(chargesWithSpecificDueDate)) { - Collections.sort(chargesWithSpecificDueDate, - (charge1, charge2) -> DateUtils.compare(charge1.getEffectiveDueDate(), charge2.getEffectiveDueDate())); - latestCharge = chargesWithSpecificDueDate.get(chargesWithSpecificDueDate.size() - 1); + chargesWithSpecificDueDate + .sort((charge1, charge2) -> DateUtils.compare(charge1.getEffectiveDueDate(), charge2.getEffectiveDueDate())); + latestCharge = chargesWithSpecificDueDate.getLast(); } return latestCharge; } diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/AbstractCumulativeLoanScheduleGenerator.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/AbstractCumulativeLoanScheduleGenerator.java index af07a66b4..9dc0ea794 100644 --- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/AbstractCumulativeLoanScheduleGenerator.java +++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/AbstractCumulativeLoanScheduleGenerator.java @@ -56,6 +56,7 @@ import org.apache.fineract.portfolio.loanaccount.domain.LoanCharge; import org.apache.fineract.portfolio.loanaccount.domain.LoanInterestRecalcualtionAdditionalDetails; import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment; import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction; +import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository; import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.LoanRepaymentScheduleTransactionProcessor; import org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanScheduleDTO; import org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanScheduleModelDownPaymentPeriod; @@ -63,13 +64,12 @@ import org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanScheduleP import org.apache.fineract.portfolio.loanaccount.loanschedule.exception.MultiDisbursementEmiAmountException; import org.apache.fineract.portfolio.loanaccount.loanschedule.exception.MultiDisbursementOutstandingAmoutException; import org.apache.fineract.portfolio.loanaccount.loanschedule.exception.ScheduleDateException; -import org.apache.fineract.portfolio.loanaccount.service.LoanTransactionService; import org.apache.fineract.portfolio.loanproduct.domain.RepaymentStartDateType; @RequiredArgsConstructor public abstract class AbstractCumulativeLoanScheduleGenerator implements LoanScheduleGenerator { - private final LoanTransactionService loanTransactionService; + private final LoanTransactionRepository loanTransactionRepository; private final CurrencyMapper currencyMapper; @Override @@ -404,7 +404,7 @@ public abstract class AbstractCumulativeLoanScheduleGenerator implements LoanSch // this condition is to add the interest from grace period if not // already applied. if (scheduleParams.getTotalOutstandingInterestPaymentDueToGrace().isGreaterThanZero()) { - LoanScheduleModelPeriod installment = periods.get(periods.size() - 1); + LoanScheduleModelPeriod installment = periods.getLast(); installment.addInterestAmount(scheduleParams.getTotalOutstandingInterestPaymentDueToGrace()); scheduleParams.addTotalRepaymentExpected(scheduleParams.getTotalOutstandingInterestPaymentDueToGrace()); scheduleParams.addTotalCumulativeInterest(scheduleParams.getTotalOutstandingInterestPaymentDueToGrace()); @@ -421,7 +421,7 @@ public abstract class AbstractCumulativeLoanScheduleGenerator implements LoanSch if (scheduleParams.getScheduleTillDate() != null) { currentDate = scheduleParams.getScheduleTillDate(); } - if (scheduleParams.applyInterestRecalculation() && scheduleParams.getLatePaymentMap().size() > 0 + if (scheduleParams.applyInterestRecalculation() && !scheduleParams.getLatePaymentMap().isEmpty() && DateUtils.isAfter(currentDate, scheduleParams.getPeriodStartDate())) { Money totalInterest = addInterestOnlyRepaymentScheduleForCurrentDate(mc, loanApplicationTerms, holidayDetailDTO, monetaryCurrency, periods, currentDate, loanRepaymentScheduleTransactionProcessor, transactions, loanCharges, @@ -1345,9 +1345,8 @@ public abstract class AbstractCumulativeLoanScheduleGenerator implements LoanSch return principalToBeScheduled.minus(loanApplicationTerms.getDownPaymentAmount()); } - private boolean updateFixedInstallmentAmount(final MathContext mc, final LoanApplicationTerms loanApplicationTerms, int periodNumber, + private void updateFixedInstallmentAmount(final MathContext mc, final LoanApplicationTerms loanApplicationTerms, int periodNumber, Money outstandingBalance) { - boolean isAmountChanged = false; if (loanApplicationTerms.getActualFixedEmiAmount() == null && loanApplicationTerms.getInterestMethod().isDecliningBalance() && loanApplicationTerms.getAmortizationMethod().isEqualInstallment()) { if (periodNumber < loanApplicationTerms.getPrincipalGrace() + 1) { @@ -1356,9 +1355,7 @@ public abstract class AbstractCumulativeLoanScheduleGenerator implements LoanSch Money emiAmount = loanApplicationTerms.pmtForInstallment(getPaymentPeriodsInOneYearCalculator(), outstandingBalance, periodNumber, mc); loanApplicationTerms.setFixedEmiAmount(emiAmount.getAmount()); - isAmountChanged = true; } - return isAmountChanged; } private Money fetchArrears(final LoanApplicationTerms loanApplicationTerms, final MonetaryCurrency currency, @@ -1984,7 +1981,7 @@ public abstract class AbstractCumulativeLoanScheduleGenerator implements LoanSch // this method relates to multi-disbursement loans BigDecimal principal = BigDecimal.ZERO; - if (loanApplicationTerms.getDisbursementDatas().size() == 0) { + if (loanApplicationTerms.getDisbursementDatas().isEmpty()) { // non tranche loans have no disbursement data entries in submitted and approved status // the appropriate approved amount or applied for amount is used to show a proposed schedule if (loanApplicationTerms.getApprovedPrincipal().getAmount().compareTo(BigDecimal.ZERO) > 0) { @@ -2521,10 +2518,9 @@ public abstract class AbstractCumulativeLoanScheduleGenerator implements LoanSch } - if (retainedInstallments.size() > 0 - && retainedInstallments.get(retainedInstallments.size() - 1).getRescheduleInterestPortion() != null) { + if (!retainedInstallments.isEmpty() && retainedInstallments.getLast().getRescheduleInterestPortion() != null) { loanApplicationTerms.setInterestTobeApproppriated( - Money.of(loan.getCurrency(), retainedInstallments.get(retainedInstallments.size() - 1).getRescheduleInterestPortion())); + Money.of(loan.getCurrency(), retainedInstallments.getLast().getRescheduleInterestPortion())); } LoanScheduleModel loanScheduleModel = generate(mc, loanApplicationTerms, loan.getActiveCharges(), holidayDetailDTO, loanScheduleParams); @@ -2723,19 +2719,18 @@ public abstract class AbstractCumulativeLoanScheduleGenerator implements LoanSch return principalPortionCalculated; } - private LoanRepaymentScheduleInstallment addLoanRepaymentScheduleInstallment(final List installments, + private void addLoanRepaymentScheduleInstallment(final List installments, final LoanScheduleModelPeriod scheduledLoanInstallment) { - LoanRepaymentScheduleInstallment installment = null; if (scheduledLoanInstallment.isRepaymentPeriod() || scheduledLoanInstallment.isDownPaymentPeriod()) { - installment = new LoanRepaymentScheduleInstallment(null, scheduledLoanInstallment.periodNumber(), - scheduledLoanInstallment.periodFromDate(), scheduledLoanInstallment.periodDueDate(), - scheduledLoanInstallment.principalDue(), scheduledLoanInstallment.interestDue(), - scheduledLoanInstallment.feeChargesDue(), scheduledLoanInstallment.penaltyChargesDue(), - scheduledLoanInstallment.isRecalculatedInterestComponent(), scheduledLoanInstallment.getLoanCompoundingDetails(), - scheduledLoanInstallment.rescheduleInterestPortion(), scheduledLoanInstallment.isDownPaymentPeriod()); + final LoanRepaymentScheduleInstallment installment = new LoanRepaymentScheduleInstallment(null, + scheduledLoanInstallment.periodNumber(), scheduledLoanInstallment.periodFromDate(), + scheduledLoanInstallment.periodDueDate(), scheduledLoanInstallment.principalDue(), + scheduledLoanInstallment.interestDue(), scheduledLoanInstallment.feeChargesDue(), + scheduledLoanInstallment.penaltyChargesDue(), scheduledLoanInstallment.isRecalculatedInterestComponent(), + scheduledLoanInstallment.getLoanCompoundingDetails(), scheduledLoanInstallment.rescheduleInterestPortion(), + scheduledLoanInstallment.isDownPaymentPeriod()); installments.add(installment); } - return installment; } private LoanScheduleModelPeriod createLoanScheduleModelDownPaymentPeriod(final LoanRepaymentScheduleInstallment installment, @@ -2804,7 +2799,7 @@ public abstract class AbstractCumulativeLoanScheduleGenerator implements LoanSch LoanScheduleDTO loanScheduleDTO = rescheduleNextInstallments(mc, loanApplicationTerms, loan, holidayDetailDTO, loanRepaymentScheduleTransactionProcessor, onDate, calculateTill); - final List loanTransactions = loanTransactionService.retrieveListOfTransactionsForReprocessing(loan); + final List loanTransactions = loanTransactionRepository.findNonReversedTransactionsForReprocessingByLoan(loan); loanRepaymentScheduleTransactionProcessor.reprocessLoanTransactions(loanApplicationTerms.getExpectedDisbursementDate(), loanTransactions, currency, loanScheduleDTO.getInstallments(), loan.getActiveCharges()); diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/CumulativeDecliningBalanceInterestLoanScheduleGenerator.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/CumulativeDecliningBalanceInterestLoanScheduleGenerator.java index 6087a6bd8..c68596013 100644 --- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/CumulativeDecliningBalanceInterestLoanScheduleGenerator.java +++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/CumulativeDecliningBalanceInterestLoanScheduleGenerator.java @@ -29,7 +29,7 @@ import org.apache.fineract.infrastructure.core.service.DateUtils; import org.apache.fineract.organisation.monetary.domain.Money; import org.apache.fineract.organisation.monetary.mapper.CurrencyMapper; import org.apache.fineract.portfolio.loanaccount.data.LoanTermVariationsData; -import org.apache.fineract.portfolio.loanaccount.service.LoanTransactionService; +import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository; import org.apache.fineract.portfolio.loanproduct.domain.AmortizationMethod; import org.springframework.stereotype.Component; @@ -63,9 +63,9 @@ public class CumulativeDecliningBalanceInterestLoanScheduleGenerator extends Abs private final PaymentPeriodsInOneYearCalculator paymentPeriodsInOneYearCalculator; public CumulativeDecliningBalanceInterestLoanScheduleGenerator(final ScheduledDateGenerator scheduledDateGenerator, - final PaymentPeriodsInOneYearCalculator paymentPeriodsInOneYearCalculator, final LoanTransactionService loanTransactionService, - final CurrencyMapper currencyMapper) { - super(loanTransactionService, currencyMapper); + final PaymentPeriodsInOneYearCalculator paymentPeriodsInOneYearCalculator, + final LoanTransactionRepository loanTransactionRepository, final CurrencyMapper currencyMapper) { + super(loanTransactionRepository, currencyMapper); this.scheduledDateGenerator = scheduledDateGenerator; this.paymentPeriodsInOneYearCalculator = paymentPeriodsInOneYearCalculator; } diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/CumulativeFlatInterestLoanScheduleGenerator.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/CumulativeFlatInterestLoanScheduleGenerator.java index 4d1be6331..1c3cb3999 100644 --- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/CumulativeFlatInterestLoanScheduleGenerator.java +++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/CumulativeFlatInterestLoanScheduleGenerator.java @@ -27,7 +27,7 @@ import java.util.TreeMap; import org.apache.fineract.organisation.monetary.domain.Money; import org.apache.fineract.organisation.monetary.mapper.CurrencyMapper; import org.apache.fineract.portfolio.loanaccount.data.LoanTermVariationsData; -import org.apache.fineract.portfolio.loanaccount.service.LoanTransactionService; +import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository; import org.springframework.stereotype.Component; @Component @@ -37,9 +37,9 @@ public class CumulativeFlatInterestLoanScheduleGenerator extends AbstractCumulat private final PaymentPeriodsInOneYearCalculator paymentPeriodsInOneYearCalculator; public CumulativeFlatInterestLoanScheduleGenerator(final ScheduledDateGenerator scheduledDateGenerator, - final PaymentPeriodsInOneYearCalculator paymentPeriodsInOneYearCalculator, final LoanTransactionService loanTransactionService, - final CurrencyMapper currencyMapper) { - super(loanTransactionService, currencyMapper); + final PaymentPeriodsInOneYearCalculator paymentPeriodsInOneYearCalculator, + final LoanTransactionRepository loanTransactionRepository, final CurrencyMapper currencyMapper) { + super(loanTransactionRepository, currencyMapper); this.scheduledDateGenerator = scheduledDateGenerator; this.paymentPeriodsInOneYearCalculator = paymentPeriodsInOneYearCalculator; } diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/serialization/LoanChargeValidator.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/serialization/LoanChargeValidator.java index 34e2eb060..e84dc7841 100644 --- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/serialization/LoanChargeValidator.java +++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/serialization/LoanChargeValidator.java @@ -19,18 +19,24 @@ package org.apache.fineract.portfolio.loanaccount.serialization; import java.time.LocalDate; +import lombok.RequiredArgsConstructor; import org.apache.fineract.infrastructure.core.service.DateUtils; import org.apache.fineract.portfolio.charge.exception.LoanChargeCannotBeAddedException; import org.apache.fineract.portfolio.loanaccount.domain.Loan; import org.apache.fineract.portfolio.loanaccount.domain.LoanCharge; import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction; +import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository; +import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType; import org.apache.fineract.portfolio.loanaccount.exception.InvalidLoanStateTransitionException; import org.apache.fineract.portfolio.loanaccount.exception.LoanChargeRefundException; import org.springframework.stereotype.Component; @Component +@RequiredArgsConstructor public final class LoanChargeValidator { + private final LoanTransactionRepository loanTransactionRepository; + public void validateLoanIsNotClosed(final Loan loan, final LoanCharge loanCharge) { if (loan.isClosed()) { final String defaultUserMessage = "This charge cannot be added as the loan is already closed."; @@ -80,13 +86,13 @@ public final class LoanChargeValidator { public void validateRepaymentTypeTransactionNotBeforeAChargeRefund(final Loan loan, final LoanTransaction repaymentTransaction, final String reversedOrCreated) { if (repaymentTransaction.isRepaymentLikeType() && !repaymentTransaction.isChargeRefund()) { - for (LoanTransaction txn : loan.getLoanTransactions()) { - if (txn.isChargeRefund() && DateUtils.isBefore(repaymentTransaction.getTransactionDate(), txn.getTransactionDate())) { - final String errorMessage = "loan.transaction.cant.be." + reversedOrCreated + ".because.later.charge.refund.exists"; - final String details = "Loan Transaction: " + loan.getId() + " Can't be " + reversedOrCreated - + " because a Later Charge Refund Exists."; - throw new LoanChargeRefundException(errorMessage, details); - } + final boolean existsChargeRefund = loanTransactionRepository.existsNonReversedByLoanAndTypeAndAfterDate(loan, + LoanTransactionType.CHARGE_REFUND, repaymentTransaction.getTransactionDate()); + if (existsChargeRefund) { + final String errorMessage = "loan.transaction.cant.be." + reversedOrCreated + ".because.later.charge.refund.exists"; + final String details = "Loan Transaction: " + loan.getId() + " Can't be " + reversedOrCreated + + " because a Later Charge Refund Exists."; + throw new LoanChargeRefundException(errorMessage, details); } } } diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanBalanceService.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanBalanceService.java index cbacb2a24..a93d21b69 100644 --- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanBalanceService.java +++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanBalanceService.java @@ -21,10 +21,12 @@ package org.apache.fineract.portfolio.loanaccount.service; import jakarta.persistence.FlushModeType; import java.math.BigDecimal; import java.time.LocalDate; +import java.util.ArrayList; import java.util.List; import lombok.RequiredArgsConstructor; import org.apache.fineract.infrastructure.core.persistence.FlushModeHandler; import org.apache.fineract.infrastructure.core.service.DateUtils; +import org.apache.fineract.infrastructure.core.service.MathUtil; import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency; import org.apache.fineract.organisation.monetary.domain.Money; import org.apache.fineract.portfolio.loanaccount.domain.Loan; @@ -34,6 +36,8 @@ import org.apache.fineract.portfolio.loanaccount.domain.LoanInstallmentCharge; import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment; import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleProcessingWrapper; import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction; +import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionComparator; +import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository; import org.apache.fineract.portfolio.loanproduct.domain.CreditAllocationTransactionType; import org.springframework.stereotype.Service; @@ -43,6 +47,7 @@ public class LoanBalanceService { private final CapitalizedIncomeBalanceService capitalizedIncomeBalanceService; private final FlushModeHandler flushModeHandler; + private final LoanTransactionRepository loanTransactionRepository; public Money calculateTotalOverpayment(final Loan loan) { Money totalPaidInRepayments = loan.getTotalPaidInRepayments(); @@ -107,7 +112,7 @@ public class LoanBalanceService { loan.setTotalOverpaid(overpaidBy.getAmountDefaultedToNullIfZero()); } - final Money recoveredAmount = loan.calculateTotalRecoveredPayments(); + final Money recoveredAmount = calculateTotalRecoveredPayments(loan); loan.setTotalRecovered(recoveredAmount.getAmountDefaultedToNullIfZero()); final Money principal = loan.getLoanRepaymentScheduleDetail().getPrincipal(); @@ -115,7 +120,61 @@ public class LoanBalanceService { final Money capitalizedIncomeAdjustment = capitalizedIncomeBalanceService.calculateCapitalizedIncomeAdjustment(loan); loan.getSummary().updateSummary(loan.getCurrency(), principal, loan.getRepaymentScheduleInstallments(), loan.getLoanCharges(), capitalizedIncome, capitalizedIncomeAdjustment); - loan.updateLoanOutstandingBalances(); + updateLoanOutstandingBalances(loan); + } + + private Money calculateTotalRecoveredPayments(Loan loan) { + // in case logic for reversing recovered payment is implemented handle subtraction from totalRecoveredPayments + final BigDecimal totalRecoveryAmount = loanTransactionRepository.calculateTotalRecoveryPaymentAmount(loan); + return Money.of(loan.getCurrency(), totalRecoveryAmount); + } + + public void updateLoanOutstandingBalances(Loan loan) { + Money outstanding = Money.zero(loan.getCurrency()); + final List loanTransactions = new ArrayList<>(); + for (final LoanTransaction transaction : loan.getLoanTransactions()) { + if (transaction.isNotReversed() && !transaction.isNonMonetaryTransaction()) { + loanTransactions.add(transaction); + } + } + loanTransactions.sort(LoanTransactionComparator.INSTANCE); + + for (LoanTransaction loanTransaction : loanTransactions) { + if (loanTransaction.isDisbursement() || loanTransaction.isIncomePosting() || loanTransaction.isCapitalizedIncome()) { + outstanding = outstanding.plus(loanTransaction.getAmount(loan.getCurrency())) + .minus(loanTransaction.getOverPaymentPortion(loan.getCurrency())); + loanTransaction.updateOutstandingLoanBalance(MathUtil.negativeToZero(outstanding.getAmount())); + } else if (loanTransaction.isChargeback() || loanTransaction.isCreditBalanceRefund()) { + Money transactionOutstanding = loanTransaction.getPrincipalPortion(loan.getCurrency()); + if (loanTransaction.isOverPaid()) { + // in case of advanced payment strategy and creditAllocations the full amount is recognized first + if (loan.getCreditAllocationRules() != null && !loan.getCreditAllocationRules().isEmpty()) { + Money payedPrincipal = loanTransaction.getLoanTransactionToRepaymentScheduleMappings().stream() // + .map(mapping -> mapping.getPrincipalPortion(loan.getCurrency())) // + .reduce(Money.zero(loan.getCurrency()), Money::plus); + transactionOutstanding = loanTransaction.getPrincipalPortion(loan.getCurrency()).minus(payedPrincipal); + } else { + // in case legacy payment strategy + transactionOutstanding = loanTransaction.getAmount(loan.getCurrency()) + .minus(loanTransaction.getOverPaymentPortion(loan.getCurrency())); + } + if (transactionOutstanding.isLessThanZero()) { + transactionOutstanding = Money.zero(loan.getCurrency()); + } + } + outstanding = outstanding.plus(transactionOutstanding); + loanTransaction.updateOutstandingLoanBalance(MathUtil.negativeToZero(outstanding.getAmount())); + } else if (!loanTransaction.isAccrualActivity()) { + if (loan.getLoanInterestRecalculationDetails() != null + && loan.getLoanInterestRecalculationDetails().isCompoundingToBePostedAsTransaction() + && !loanTransaction.isRepaymentAtDisbursement()) { + outstanding = outstanding.minus(loanTransaction.getAmount(loan.getCurrency())); + } else { + outstanding = outstanding.minus(loanTransaction.getPrincipalPortion(loan.getCurrency())); + } + loanTransaction.updateOutstandingLoanBalance(MathUtil.negativeToZero(outstanding.getAmount())); + } + } } public void updateLoanToLastDisbursalState(final Loan loan, final LoanDisbursementDetails disbursementDetail) { diff --git a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/AdvancedPaymentScheduleTransactionProcessor.java b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/AdvancedPaymentScheduleTransactionProcessor.java index d8d4bda2f..f9ed025ea 100644 --- a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/AdvancedPaymentScheduleTransactionProcessor.java +++ b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/AdvancedPaymentScheduleTransactionProcessor.java @@ -84,6 +84,7 @@ import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction; import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionComparator; import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRelation; import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRelationTypeEnum; +import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository; import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionToRepaymentScheduleMapping; import org.apache.fineract.portfolio.loanaccount.domain.reaging.LoanReAgeParameter; import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.AbstractLoanRepaymentScheduleTransactionProcessor; @@ -93,7 +94,6 @@ import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanSchedul import org.apache.fineract.portfolio.loanaccount.serialization.LoanChargeValidator; import org.apache.fineract.portfolio.loanaccount.service.InterestRefundService; import org.apache.fineract.portfolio.loanaccount.service.LoanBalanceService; -import org.apache.fineract.portfolio.loanaccount.service.LoanTransactionService; import org.apache.fineract.portfolio.loanaccount.service.schedule.LoanScheduleComponent; import org.apache.fineract.portfolio.loanproduct.calc.EMICalculator; import org.apache.fineract.portfolio.loanproduct.calc.data.PeriodDueDetails; @@ -121,17 +121,17 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep private final LoanRepositoryWrapper loanRepositoryWrapper; private final InterestRefundService interestRefundService; private final LoanScheduleComponent loanSchedule; - private final LoanTransactionService loanTransactionService; + private final LoanTransactionRepository loanTransactionRepository; public AdvancedPaymentScheduleTransactionProcessor(final EMICalculator emiCalculator, final LoanRepositoryWrapper loanRepositoryWrapper, final InterestRefundService interestRefundService, final ExternalIdFactory externalIdFactory, - final LoanScheduleComponent loanSchedule, final LoanTransactionService loanTransactionRepository, + final LoanScheduleComponent loanSchedule, final LoanTransactionRepository loanTransactionRepository, final LoanChargeValidator loanChargeValidator, final LoanBalanceService loanBalanceService) { super(externalIdFactory, loanChargeValidator, loanBalanceService); this.emiCalculator = emiCalculator; this.loanRepositoryWrapper = loanRepositoryWrapper; this.interestRefundService = interestRefundService; - this.loanTransactionService = loanTransactionRepository; + this.loanTransactionRepository = loanTransactionRepository; this.loanSchedule = loanSchedule; } @@ -212,7 +212,7 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep } MoneyHolder overpaymentHolder = new MoneyHolder(Money.zero(currency)); - final Loan loan = loanTransactions.get(0).getLoan(); + final Loan loan = loanTransactions.getFirst().getLoan(); List loanTermVariations = loan.getActiveLoanTermVariations().stream().map(LoanTermVariations::toData) .collect(Collectors.toCollection(ArrayList::new)); final Integer installmentAmountInMultiplesOf = loan.getLoanProductRelatedDetail().getInstallmentAmountInMultiplesOf(); @@ -276,7 +276,7 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep @Transactional(propagation = Propagation.REQUIRES_NEW, readOnly = true) public ProgressiveLoanInterestScheduleModel calculateInterestScheduleModel(@NotNull Long loanId, LocalDate targetDate) { Loan loan = loanRepositoryWrapper.findOneWithNotFoundDetection(loanId); - final List transactions = loanTransactionService.retrieveListOfTransactionsForReprocessing(loan); + final List transactions = loanTransactionRepository.findNonReversedTransactionsForReprocessingByLoan(loan); MonetaryCurrency currency = loan.getLoanRepaymentScheduleDetail().getCurrency(); List installments = loan.getRepaymentScheduleInstallments(); Set charges = loan.getActiveCharges(); @@ -552,8 +552,7 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep if (!loanTransactionMapped) { if (transactionDate.equals(pastDueDate)) { // Transaction is on Maturity date, no additional installment is needed - LoanRepaymentScheduleInstallment currentInstallment = installmentToBeProcessed - .get(installmentToBeProcessed.size() - 1); + LoanRepaymentScheduleInstallment currentInstallment = installmentToBeProcessed.getLast(); emiCalculator.creditPrincipal(model, transactionDate, transactionAmount); updateRepaymentPeriods(loanTransaction, progressiveTransactionCtx); @@ -827,7 +826,7 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep // New installment will be added (N+1 scenario) if (!loanTransactionMapped) { if (loanTransaction.getTransactionDate().equals(pastDueDate)) { - LoanRepaymentScheduleInstallment currentInstallment = ctx.getInstallments().get(ctx.getInstallments().size() - 1); + LoanRepaymentScheduleInstallment currentInstallment = ctx.getInstallments().getLast(); recognizeAmountsAfterChargeback(ctx, transactionDate, currentInstallment, chargebackAllocation); } else { Loan loan = loanTransaction.getLoan(); @@ -1450,7 +1449,7 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep public void recalculateInterestForDate(LocalDate targetDate, ProgressiveTransactionCtx ctx) { if (ctx.getInstallments() != null && !ctx.getInstallments().isEmpty()) { - Loan loan = ctx.getInstallments().get(0).getLoan(); + Loan loan = ctx.getInstallments().getFirst().getLoan(); if (loan.isInterestBearingAndInterestRecalculationEnabled() && !loan.isNpa() && !ctx.isChargedOff() && !ctx.isContractTerminated()) { @@ -1761,8 +1760,7 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep .max(LocalDate::compareTo); if (latestDueDate.isPresent()) { - final LoanRepaymentScheduleInstallment lastInstallment = installmentsUpToTransactionDate - .get(installmentsUpToTransactionDate.size() - 1); + final LoanRepaymentScheduleInstallment lastInstallment = installmentsUpToTransactionDate.getLast(); final LoanRepaymentScheduleInstallment installmentForCharges = new LoanRepaymentScheduleInstallment(loan, lastInstallment.getInstallmentNumber() + 1, currentInstallment.getDueDate(), latestDueDate.get(), diff --git a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/CapitalizedIncomeWritePlatformServiceImpl.java b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/CapitalizedIncomeWritePlatformServiceImpl.java index f9a7d6133..840fd690a 100644 --- a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/CapitalizedIncomeWritePlatformServiceImpl.java +++ b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/CapitalizedIncomeWritePlatformServiceImpl.java @@ -148,7 +148,7 @@ public class CapitalizedIncomeWritePlatformServiceImpl implements CapitalizedInc LoanTransaction savedCapitalizedIncomeAdjustment = loanTransactionRepository.saveAndFlush(capitalizedIncomeAdjustment); // Update outstanding loan balances - loan.updateLoanOutstandingBalances(); + loanBalanceService.updateLoanOutstandingBalances(loan); // Create a note if provided final String noteText = command.stringValueOfParameterNamed("note"); diff --git a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/InternalProgressiveLoanApiResource.java b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/InternalProgressiveLoanApiResource.java index f7bbec635..60f8cd263 100644 --- a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/InternalProgressiveLoanApiResource.java +++ b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/InternalProgressiveLoanApiResource.java @@ -40,6 +40,7 @@ import org.apache.fineract.portfolio.loanaccount.domain.ChangedTransactionDetail import org.apache.fineract.portfolio.loanaccount.domain.Loan; import org.apache.fineract.portfolio.loanaccount.domain.LoanRepositoryWrapper; import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction; +import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository; import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.AdvancedPaymentScheduleTransactionProcessor; import org.apache.fineract.portfolio.loanproduct.calc.data.ProgressiveLoanInterestScheduleModel; import org.springframework.beans.factory.InitializingBean; @@ -56,9 +57,8 @@ public class InternalProgressiveLoanApiResource implements InitializingBean { private final LoanRepositoryWrapper loanRepository; private final AdvancedPaymentScheduleTransactionProcessor advancedPaymentScheduleTransactionProcessor; - private final ProgressiveLoanInterestScheduleModelParserService progressiveLoanInterestScheduleModelParserService; private final InterestScheduleModelRepositoryWrapper writePlatformService; - private final LoanTransactionService loanTransactionService; + private final LoanTransactionRepository loanTransactionRepository; @Override @SuppressFBWarnings("SLF4J_SIGN_ONLY_FORMAT") @@ -87,7 +87,8 @@ public class InternalProgressiveLoanApiResource implements InitializingBean { } private ProgressiveLoanInterestScheduleModel reprocessTransactionsAndGetModel(final Loan loan) { - final List transactionsToReprocess = loanTransactionService.retrieveListOfTransactionsForReprocessing(loan); + final List transactionsToReprocess = loanTransactionRepository + .findNonReversedTransactionsForReprocessingByLoan(loan); final LocalDate businessDate = ThreadLocalContextUtil.getBusinessDate(); final Pair changedTransactionDetailProgressiveLoanInterestScheduleModelPair = advancedPaymentScheduleTransactionProcessor .reprocessProgressiveLoanTransactionsTransactional(loan.getDisbursementDate(), businessDate, transactionsToReprocess, diff --git a/fineract-progressive-loan/src/test/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/AdvancedPaymentScheduleTransactionProcessorTest.java b/fineract-progressive-loan/src/test/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/AdvancedPaymentScheduleTransactionProcessorTest.java index 7efc6ad18..af7516f3b 100644 --- a/fineract-progressive-loan/src/test/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/AdvancedPaymentScheduleTransactionProcessorTest.java +++ b/fineract-progressive-loan/src/test/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/AdvancedPaymentScheduleTransactionProcessorTest.java @@ -67,6 +67,7 @@ import org.apache.fineract.portfolio.loanaccount.domain.LoanRepositoryWrapper; import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction; import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRelation; import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRelationTypeEnum; +import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository; import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType; import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.MoneyHolder; import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.TransactionCtx; @@ -74,7 +75,6 @@ import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanSchedul import org.apache.fineract.portfolio.loanaccount.serialization.LoanChargeValidator; import org.apache.fineract.portfolio.loanaccount.service.InterestRefundService; import org.apache.fineract.portfolio.loanaccount.service.LoanBalanceService; -import org.apache.fineract.portfolio.loanaccount.service.LoanTransactionService; import org.apache.fineract.portfolio.loanaccount.service.schedule.LoanScheduleComponent; import org.apache.fineract.portfolio.loanproduct.calc.EMICalculator; import org.apache.fineract.portfolio.loanproduct.calc.data.ProgressiveLoanInterestScheduleModel; @@ -120,7 +120,7 @@ class AdvancedPaymentScheduleTransactionProcessorTest { public void setUp() { underTest = new AdvancedPaymentScheduleTransactionProcessor(emiCalculator, Mockito.mock(LoanRepositoryWrapper.class), Mockito.mock(InterestRefundService.class), Mockito.mock(ExternalIdFactory.class), Mockito.mock(LoanScheduleComponent.class), - Mockito.mock(LoanTransactionService.class), Mockito.mock(LoanChargeValidator.class), + Mockito.mock(LoanTransactionRepository.class), Mockito.mock(LoanChargeValidator.class), Mockito.mock(LoanBalanceService.class)); ThreadLocalContextUtil.setTenant(new FineractPlatformTenant(1L, "default", "Default", "Asia/Kolkata", null)); diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanAccountDomainServiceJpa.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanAccountDomainServiceJpa.java index c546060ab..b36039fdb 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanAccountDomainServiceJpa.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanAccountDomainServiceJpa.java @@ -134,7 +134,6 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService { private final ConfigurationDomainService configurationDomainService; private final HolidayRepository holidayRepository; private final WorkingDaysRepositoryWrapper workingDaysRepository; - private final JournalEntryWritePlatformService journalEntryWritePlatformService; private final NoteRepository noteRepository; private final BusinessEventNotifierService businessEventNotifierService; @@ -464,7 +463,7 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService { chargesPayment.updateComponents(zero, zero, charge.getAmount(loan.getCurrency()), zero); chargesPayment.updateLoan(loan); loan.addLoanTransaction(chargesPayment); - loan.updateLoanOutstandingBalances(); + loanBalanceService.updateLoanOutstandingBalances(loan); charge.markAsFullyPaid(); } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAccrualsProcessingServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAccrualsProcessingServiceImpl.java index c3534e441..4f3aeb9b0 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAccrualsProcessingServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAccrualsProcessingServiceImpl.java @@ -931,7 +931,7 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing createUpdateIncomePostingTransaction(loan, compoundingDetail, interest, fee, penalties, externalId); createUpdateAccrualTransaction(loan, compoundingDetail, interest, fee, penalties, feeDetails, externalId); - loan.updateLoanOutstandingBalances(); + loanBalanceService.updateLoanOutstandingBalances(loan); } private void createUpdateIncomePostingTransaction(Loan loan, LoanInterestRecalcualtionAdditionalDetails compoundingDetail, @@ -1000,7 +1000,7 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing createIncomePostingAndAccrualTransactionOnLoanClosure(loan, closedDate, interestToPost, feeToPost, penaltyToPost, amountToPost); } - loan.updateLoanOutstandingBalances(); + loanBalanceService.updateLoanOutstandingBalances(loan); } private void determineCumulativeIncomeFromInstallments(final Loan loan, diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAssemblerImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAssemblerImpl.java index abcce34a1..af94f04d3 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAssemblerImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAssemblerImpl.java @@ -261,20 +261,20 @@ public class LoanAssemblerImpl implements LoanAssembler { loanOfficer, loanPurpose, transactionProcessingStrategy, loanProductRelatedDetail, loanCharges, syncDisbursementWithMeeting, fixedEmiAmount, disbursementDetails, maxOutstandingLoanBalance, createStandingInstructionAtDisbursement, isFloatingInterestRate, interestRateDifferential, rates, - fixedPrincipalPercentagePerInstallment, externalId, loanApplicationTerms, loanScheduleModel, - isEnableInstallmentLevelDelinquency, submittedOnDate); + fixedPrincipalPercentagePerInstallment, externalId, loanApplicationTerms, isEnableInstallmentLevelDelinquency, + submittedOnDate); } else if (group != null) { loanApplication = Loan.newGroupLoanApplication(accountNo, group, loanAccountType, loanProduct, fund, loanOfficer, loanPurpose, transactionProcessingStrategy, loanProductRelatedDetail, loanCharges, syncDisbursementWithMeeting, fixedEmiAmount, disbursementDetails, maxOutstandingLoanBalance, createStandingInstructionAtDisbursement, isFloatingInterestRate, interestRateDifferential, rates, fixedPrincipalPercentagePerInstallment, externalId, loanApplicationTerms, - loanScheduleModel, isEnableInstallmentLevelDelinquency, submittedOnDate); + isEnableInstallmentLevelDelinquency, submittedOnDate); } else if (client != null) { loanApplication = Loan.newIndividualLoanApplication(accountNo, client, loanAccountType, loanProduct, fund, loanOfficer, loanPurpose, transactionProcessingStrategy, loanProductRelatedDetail, loanCharges, collateral, fixedEmiAmount, disbursementDetails, maxOutstandingLoanBalance, createStandingInstructionAtDisbursement, isFloatingInterestRate, interestRateDifferential, rates, fixedPrincipalPercentagePerInstallment, externalId, loanApplicationTerms, - loanScheduleModel, isEnableInstallmentLevelDelinquency, submittedOnDate); + isEnableInstallmentLevelDelinquency, submittedOnDate); } else { throw new IllegalStateException("No loan application exists for either a client or group (or both)."); } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanDisbursementService.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanDisbursementService.java index 8daee2dc6..428f0d4b2 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanDisbursementService.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanDisbursementService.java @@ -63,6 +63,7 @@ public class LoanDisbursementService { private final LoanDisbursementValidator loanDisbursementValidator; private final ReprocessLoanTransactionsService reprocessLoanTransactionsService; private final LoanChargeService loanChargeService; + private final LoanBalanceService loanBalanceService; public void updateDisbursementDetails(final Loan loan, final JsonCommand jsonCommand, final Map actualChanges) { final List disbursementList = loan.fetchDisbursementIds(); @@ -217,7 +218,7 @@ public class LoanDisbursementService { chargesPayment.updateComponentsAndTotal(zero, zero, disbursentMoney, zero); chargesPayment.updateLoan(loan); loan.addLoanTransaction(chargesPayment); - loan.updateLoanOutstandingBalances(); + loanBalanceService.updateLoanOutstandingBalances(loan); } final LocalDate expectedDate = loan.getExpectedFirstRepaymentOnDate(); diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanTransactionProcessingServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanTransactionProcessingServiceImpl.java index a8a358a3f..825880c7e 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanTransactionProcessingServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanTransactionProcessingServiceImpl.java @@ -61,8 +61,8 @@ public class LoanTransactionProcessingServiceImpl implements LoanTransactionProc private final LoanRepaymentScheduleTransactionProcessorFactory transactionProcessorFactory; private final LoanTermVariationsMapper loanMapper; private final InterestScheduleModelRepositoryWrapper modelRepository; - private final LoanTransactionService loanTransactionService; private final LoanBalanceService loanBalanceService; + private final LoanTransactionService loanTransactionService; @Override public boolean canProcessLatestTransactionOnly(Loan loan, LoanTransaction loanTransaction, diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java index 43b64bf74..1dabf678f 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java @@ -594,8 +594,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf } if (loan.getLoanProduct().isMultiDisburseLoan() || loan.isProgressiveSchedule()) { - final List allNonContraTransactionsPostDisbursement = loanTransactionService - .retrieveListOfTransactionsForReprocessing(loan); + final List allNonContraTransactionsPostDisbursement = loanTransactionRepository + .findNonReversedTransactionsForReprocessingByLoan(loan); if (!allNonContraTransactionsPostDisbursement.isEmpty()) { reprocessLoanTransactionsService.reprocessTransactions(loan); } @@ -2246,7 +2246,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf throw new MultiDisbursementDataNotAllowedException(LoanApiConstants.disbursementDataParameterName, errorMessage); } } else { - if (disbursementDataArray == null || disbursementDataArray.size() == 0) { + if (disbursementDataArray == null || disbursementDataArray.isEmpty()) { final String errorMessage = "For this loan product, disbursement details must be provided"; throw new MultiDisbursementDataRequiredException(LoanApiConstants.disbursementDataParameterName, errorMessage); } @@ -2821,7 +2821,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf final ScheduleGeneratorDTO scheduleGeneratorDTO = this.loanUtilService.buildScheduleGeneratorDTO(loan, null, null); loanScheduleService.regenerateRepaymentScheduleWithInterestRecalculation(loan, scheduleGeneratorDTO); } - final List loanTransactions = loanTransactionService.retrieveListOfTransactionsForReprocessing(loan); + final List loanTransactions = loanTransactionRepository.findNonReversedTransactionsForReprocessingByLoan(loan); loanTransactions.add(chargeOffTransaction); reprocessLoanTransactionsService.reprocessParticularTransactions(loan, loanTransactions); loan.addLoanTransaction(chargeOffTransaction); @@ -3031,7 +3031,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf final String errorMessage = "loan.product.does.not.support.multiple.disbursals.cannot.undo.last.disbursal"; throw new LoanMultiDisbursementException(errorMessage); } - Integer trancheDisbursedCount = 0; + int trancheDisbursedCount = 0; for (LoanDisbursementDetails disbursementDetails : loan.getDisbursementDetails()) { if (disbursementDetails.actualDisbursementDate() != null) { trancheDisbursedCount++; @@ -3361,13 +3361,13 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf final Map actualChanges = new LinkedHashMap<>(); List loanTransactions = loan.retrieveListOfTransactionsByType(LoanTransactionType.DISBURSEMENT); loanTransactions.sort(Comparator.comparing(LoanTransaction::getId)); - final LoanTransaction lastDisbursalTransaction = loanTransactions.get(loanTransactions.size() - 1); + final LoanTransaction lastDisbursalTransaction = loanTransactions.getLast(); final LocalDate lastTransactionDate = lastDisbursalTransaction.getTransactionDate(); existingTransactionIds.addAll(loanTransactionRepository.findTransactionIdsByLoan(loan)); existingReversedTransactionIds.addAll(loanTransactionRepository.findReversedTransactionIdsByLoan(loan)); - loanTransactions = loan.retrieveListOfTransactionsExcludeAccruals(); + loanTransactions = loanTransactionRepository.findNonReversedMonetaryTransactionsByLoan(loan); Collections.reverse(loanTransactions); for (final LoanTransaction previousTransaction : loanTransactions) { if (DateUtils.isBefore(lastTransactionDate, previousTransaction.getTransactionDate()) diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/ProgressiveLoanSummaryDataProvider.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/ProgressiveLoanSummaryDataProvider.java index 3cfcd1cef..2606174fd 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/ProgressiveLoanSummaryDataProvider.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/ProgressiveLoanSummaryDataProvider.java @@ -36,6 +36,7 @@ import org.apache.fineract.portfolio.loanaccount.domain.Loan; import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment; import org.apache.fineract.portfolio.loanaccount.domain.LoanRepositoryWrapper; import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction; +import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository; import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.AdvancedPaymentScheduleTransactionProcessor; import org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanScheduleData; import org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanSchedulePeriodData; @@ -53,7 +54,7 @@ public class ProgressiveLoanSummaryDataProvider extends CommonLoanSummaryDataPro private final AdvancedPaymentScheduleTransactionProcessor advancedPaymentScheduleTransactionProcessor; private final EMICalculator emiCalculator; private final LoanRepositoryWrapper loanRepository; - private final LoanTransactionService loanTransactionService; + private final LoanTransactionRepository loanTransactionRepository; private final InterestScheduleModelRepositoryWrapper modelRepository; @Override @@ -81,8 +82,8 @@ public class ProgressiveLoanSummaryDataProvider extends CommonLoanSummaryDataPro } private ProgressiveLoanInterestScheduleModel calculateModel(Loan loan, LocalDate businessDate) { - final List transactionsToReprocess = loanTransactionService.retrieveListOfTransactionsForReprocessing(loan) - .stream().filter(t -> !t.isAccrualActivity()).toList(); + final List transactionsToReprocess = loanTransactionRepository + .findNonReversedTransactionsForReprocessingByLoan(loan).stream().filter(t -> !t.isAccrualActivity()).toList(); Pair changedTransactionDetailProgressiveLoanInterestScheduleModelPair = advancedPaymentScheduleTransactionProcessor .reprocessProgressiveLoanTransactions(loan.getDisbursementDate(), businessDate, transactionsToReprocess, loan.getCurrency(), loan.getRepaymentScheduleInstallments(), loan.getActiveCharges()); diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/ReprocessLoanTransactionsServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/ReprocessLoanTransactionsServiceImpl.java index 5092c3a62..acf391aab 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/ReprocessLoanTransactionsServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/ReprocessLoanTransactionsServiceImpl.java @@ -22,8 +22,6 @@ import java.time.LocalDate; import java.util.List; import java.util.Optional; import java.util.Set; -import java.util.function.Predicate; -import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil; import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency; @@ -37,7 +35,6 @@ import org.apache.fineract.portfolio.loanaccount.domain.LoanCharge; import org.apache.fineract.portfolio.loanaccount.domain.LoanChargePaidBy; import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleProcessingWrapper; import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction; -import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionComparator; import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository; import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.MoneyHolder; import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.TransactionCtx; @@ -55,26 +52,18 @@ public class ReprocessLoanTransactionsServiceImpl implements ReprocessLoanTransa private final InterestScheduleModelRepositoryWrapper interestScheduleModelRepositoryWrapper; private final LoanBalanceService loanBalanceService; private final LoanTransactionRepository loanTransactionRepository; + private final LoanTransactionService loanTransactionService; @Override public void reprocessTransactions(final Loan loan) { - final List allNonContraTransactionsPostDisbursement = retrieveListOfTransactionsForReprocessing(loan); + final List allNonContraTransactionsPostDisbursement = loanTransactionService + .retrieveListOfTransactionsForReprocessing(loan); + final ChangedTransactionDetail changedTransactionDetail = reprocessTransactionsAndFetchChangedTransactions(loan, allNonContraTransactionsPostDisbursement); handleChangedDetail(changedTransactionDetail); } - private List retrieveListOfTransactionsForReprocessing(final Loan loan) { - return loan.getLoanTransactions().stream().filter(loanTransactionForReprocessingPredicate()) - .sorted(LoanTransactionComparator.INSTANCE).collect(Collectors.toList()); - } - - private Predicate loanTransactionForReprocessingPredicate() { - return transaction -> transaction.isNotReversed() - && (transaction.isChargeOff() || transaction.isReAge() || transaction.isAccrualActivity() || transaction.isReAmortize() - || !transaction.isNonMonetaryTransaction() || transaction.isContractTermination()); - } - @Override public void reprocessParticularTransactions(final Loan loan, final List loanTransactions) { final ChangedTransactionDetail changedTransactionDetail = reprocessTransactionsAndFetchChangedTransactions(loan, loanTransactions); @@ -83,7 +72,7 @@ public class ReprocessLoanTransactionsServiceImpl implements ReprocessLoanTransa @Override public void reprocessTransactionsWithPostTransactionChecks(final Loan loan, final LocalDate transactionDate) { - final List transactions = retrieveListOfTransactionsForReprocessing(loan); + final List transactions = loanTransactionRepository.findNonReversedTransactionsForReprocessingByLoan(loan); final ChangedTransactionDetail changedTransactionDetail = reprocessTransactionsAndFetchChangedTransactions(loan, transactions); handleChangedDetail(changedTransactionDetail); } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/contracttermination/LoanContractTerminationServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/contracttermination/LoanContractTerminationServiceImpl.java index 28ee5ba50..b9cb52797 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/contracttermination/LoanContractTerminationServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/contracttermination/LoanContractTerminationServiceImpl.java @@ -70,10 +70,10 @@ public class LoanContractTerminationServiceImpl { private final LoanUtilService loanUtilService; private final ExternalIdFactory externalIdFactory; private final BusinessEventNotifierService businessEventNotifierService; - private final LoanTransactionService loanTransactionService; private final LoanScheduleService loanScheduleService; private final LoanChargeValidator loanChargeValidator; private final ProgressiveLoanTransactionValidator loanTransactionValidator; + private final LoanTransactionService loanTransactionService; public CommandProcessingResult applyContractTermination(final JsonCommand command) { final Loan loan = loanAssembler.assembleFrom(command.getLoanId()); diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/starter/LoanAccountAutoStarter.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/starter/LoanAccountAutoStarter.java index 04dd3aecb..e3f52a4d7 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/starter/LoanAccountAutoStarter.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/starter/LoanAccountAutoStarter.java @@ -22,6 +22,7 @@ import java.util.List; import org.apache.fineract.infrastructure.core.service.ExternalIdFactory; import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleTransactionProcessorFactory; import org.apache.fineract.portfolio.loanaccount.domain.LoanRepositoryWrapper; +import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository; import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.LoanRepaymentScheduleTransactionProcessor; import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.AdvancedPaymentScheduleTransactionProcessor; import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.CreocoreLoanRepaymentScheduleTransactionProcessor; @@ -35,7 +36,6 @@ import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.imp import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.RBILoanRepaymentScheduleTransactionProcessor; import org.apache.fineract.portfolio.loanaccount.serialization.LoanChargeValidator; import org.apache.fineract.portfolio.loanaccount.service.LoanBalanceService; -import org.apache.fineract.portfolio.loanaccount.service.LoanTransactionService; import org.apache.fineract.portfolio.loanaccount.service.ProgressiveLoanInterestRefundServiceImpl; import org.apache.fineract.portfolio.loanaccount.service.schedule.LoanScheduleComponent; import org.apache.fineract.portfolio.loanproduct.calc.EMICalculator; @@ -138,9 +138,9 @@ public class LoanAccountAutoStarter { final LoanRepositoryWrapper loanRepositoryWrapper, final @Lazy ProgressiveLoanInterestRefundServiceImpl progressiveLoanInterestRefundService, final ExternalIdFactory externalIdFactory, final LoanScheduleComponent loanSchedule, - final LoanTransactionService loanTransactionService, final LoanChargeValidator loanChargeValidator, + final LoanTransactionRepository loanTransactionRepository, final LoanChargeValidator loanChargeValidator, final LoanBalanceService loanBalanceService) { return new AdvancedPaymentScheduleTransactionProcessor(emiCalculator, loanRepositoryWrapper, progressiveLoanInterestRefundService, - externalIdFactory, loanSchedule, loanTransactionService, loanChargeValidator, loanBalanceService); + externalIdFactory, loanSchedule, loanTransactionRepository, loanChargeValidator, loanBalanceService); } } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/starter/LoanAccountConfiguration.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/starter/LoanAccountConfiguration.java index 15a96513e..ae9d3b255 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/starter/LoanAccountConfiguration.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/starter/LoanAccountConfiguration.java @@ -457,9 +457,9 @@ public class LoanAccountConfiguration { @ConditionalOnMissingBean(LoanDisbursementService.class) public LoanDisbursementService loanDisbursementService(LoanChargeValidator loanChargeValidator, LoanDisbursementValidator loanDisbursementValidator, ReprocessLoanTransactionsService reprocessLoanTransactionsService, - LoanChargeService loanChargeService) { + LoanChargeService loanChargeService, LoanBalanceService loanBalanceService) { return new LoanDisbursementService(loanChargeValidator, loanDisbursementValidator, reprocessLoanTransactionsService, - loanChargeService); + loanChargeService, loanBalanceService); } @Bean diff --git a/fineract-provider/src/test/java/org/apache/fineract/infrastructure/event/external/service/serialization/serializer/loan/LoanAccountDelinquencyRangeEventSerializerTest.java b/fineract-provider/src/test/java/org/apache/fineract/infrastructure/event/external/service/serialization/serializer/loan/LoanAccountDelinquencyRangeEventSerializerTest.java index a909e638a..9d41557b7 100644 --- a/fineract-provider/src/test/java/org/apache/fineract/infrastructure/event/external/service/serialization/serializer/loan/LoanAccountDelinquencyRangeEventSerializerTest.java +++ b/fineract-provider/src/test/java/org/apache/fineract/infrastructure/event/external/service/serialization/serializer/loan/LoanAccountDelinquencyRangeEventSerializerTest.java @@ -363,7 +363,7 @@ public class LoanAccountDelinquencyRangeEventSerializerTest { DelinquencyReadPlatformService delinquencyReadPlatformService = new DelinquencyReadPlatformServiceImpl(repositoryRange, repositoryBucket, repositoryLoanDelinquencyTagHistory, mapperRange, mapperBucket, mapperLoanDelinquencyTagHistory, loanRepository, loanDelinquencyDomainService, repositoryLoanInstallmentDelinquencyTag, loanDelinquencyActionRepository, - delinquencyEffectivePauseHelper, configurationDomainService); + delinquencyEffectivePauseHelper, configurationDomainService, Mockito.mock(LoanTransactionRepository.class)); LoanProduct loanProduct = Mockito.mock(LoanProduct.class); when(loanProduct.isMultiDisburseLoan()).thenReturn(false); diff --git a/fineract-provider/src/test/java/org/apache/fineract/portfolio/loanaccount/domain/LoanBuilder.java b/fineract-provider/src/test/java/org/apache/fineract/portfolio/loanaccount/domain/LoanBuilder.java index fa7807957..fc5a72094 100644 --- a/fineract-provider/src/test/java/org/apache/fineract/portfolio/loanaccount/domain/LoanBuilder.java +++ b/fineract-provider/src/test/java/org/apache/fineract/portfolio/loanaccount/domain/LoanBuilder.java @@ -40,7 +40,6 @@ import org.apache.fineract.portfolio.group.domain.Group; import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.LoanRepaymentScheduleTransactionProcessor; import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.InterestPrincipalPenaltyFeesOrderLoanRepaymentScheduleTransactionProcessor; import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanApplicationTerms; -import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleModel; import org.apache.fineract.portfolio.loanaccount.serialization.LoanChargeValidator; import org.apache.fineract.portfolio.loanaccount.service.LoanBalanceService; import org.apache.fineract.portfolio.loanproduct.domain.LoanProduct; @@ -80,7 +79,6 @@ public class LoanBuilder { private BigDecimal fixedPrincipalPercentagePerInstallment; private ExternalId externalId = ExternalId.empty(); private LoanApplicationTerms loanApplicationTerms = mock(LoanApplicationTerms.class); - private LoanScheduleModel loanScheduleModel = mock(LoanScheduleModel.class); private Boolean enableInstallmentLevelDelinquency = false; private LocalDate submittedOnDate = LocalDate.now(ZoneId.systemDefault()); private LocalDate approvedOnDate; @@ -116,8 +114,8 @@ public class LoanBuilder { Loan loan = Loan.newIndividualLoanApplication(accountNo, client, loanType, loanProduct, fund, loanOfficer, loanPurpose, transactionProcessor, loanRepaymentScheduleDetail, charges, collateral, fixedEmiAmount, disbursementDetails, maxOutstandingLoanBalance, createStandingInstructionAtDisbursement, isFloatingInterestRate, interestRateDifferential, - rates, fixedPrincipalPercentagePerInstallment, externalId, loanApplicationTerms, loanScheduleModel, - enableInstallmentLevelDelinquency, submittedOnDate); + rates, fixedPrincipalPercentagePerInstallment, externalId, loanApplicationTerms, enableInstallmentLevelDelinquency, + submittedOnDate); if (id != null) { loan.setId(id); @@ -335,11 +333,6 @@ public class LoanBuilder { return this; } - public LoanBuilder withLoanScheduleModel(LoanScheduleModel loanScheduleModel) { - this.loanScheduleModel = loanScheduleModel; - return this; - } - public LoanBuilder withEnableInstallmentLevelDelinquency(Boolean enableInstallmentLevelDelinquency) { this.enableInstallmentLevelDelinquency = enableInstallmentLevelDelinquency; return this;