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 37d8e17c9..3d6cec432 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 @@ -107,13 +107,8 @@ import org.apache.fineract.portfolio.loanaccount.api.LoanApiConstants; import org.apache.fineract.portfolio.loanaccount.data.DisbursementData; import org.apache.fineract.portfolio.loanaccount.data.HolidayDetailDTO; import org.apache.fineract.portfolio.loanaccount.data.LoanTermVariationsData; -import org.apache.fineract.portfolio.loanaccount.data.OutstandingAmountsDTO; import org.apache.fineract.portfolio.loanaccount.data.ScheduleGeneratorDTO; -import org.apache.fineract.portfolio.loanaccount.data.TransactionChangeData; import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.LoanRepaymentScheduleTransactionProcessor; -import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.MoneyHolder; -import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.TransactionCtx; -import org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanScheduleDTO; import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanApplicationTerms; import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleGenerator; import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleModel; @@ -365,8 +360,6 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom { @Transient private boolean accountNumberRequiresAutoGeneration; - @Transient - private LoanRepaymentScheduleTransactionProcessorFactory transactionProcessorFactory; @Transient private LoanLifecycleStateMachine loanLifecycleStateMachine; @@ -769,37 +762,6 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom { return applyLoanChargeTransaction; } - public void handleChargePaidTransaction(final LoanCharge charge, final LoanTransaction chargesPayment, - final LoanLifecycleStateMachine loanLifecycleStateMachine, final Integer installmentNumber) { - chargesPayment.updateLoan(this); - final LoanChargePaidBy loanChargePaidBy = new LoanChargePaidBy(chargesPayment, charge, - chargesPayment.getAmount(getCurrency()).getAmount(), installmentNumber); - chargesPayment.getLoanChargesPaid().add(loanChargePaidBy); - addLoanTransaction(chargesPayment); - loanLifecycleStateMachine.transition(LoanEvent.LOAN_CHARGE_PAYMENT, this); - - final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = getTransactionProcessor(); - final List chargePaymentInstallments = new ArrayList<>(); - List installments = getRepaymentScheduleInstallments(); - int firstNormalInstallmentNumber = LoanRepaymentScheduleProcessingWrapper - .fetchFirstNormalInstallmentNumber(repaymentScheduleInstallments); - for (final LoanRepaymentScheduleInstallment installment : installments) { - boolean isFirstInstallment = installment.getInstallmentNumber().equals(firstNormalInstallmentNumber); - if (installment.getInstallmentNumber().equals(installmentNumber) || (installmentNumber == null - && charge.isDueInPeriod(installment.getFromDate(), installment.getDueDate(), isFirstInstallment))) { - chargePaymentInstallments.add(installment); - break; - } - } - final Set loanCharges = new HashSet<>(1); - loanCharges.add(charge); - loanRepaymentScheduleTransactionProcessor.processLatestTransaction(chargesPayment, new TransactionCtx(getCurrency(), - chargePaymentInstallments, loanCharges, new MoneyHolder(getTotalOverpaidAsMoney()), null)); - - updateLoanSummaryDerivedFields(); - doPostLoanTransactionChecks(chargesPayment.getTransactionDate(), loanLifecycleStateMachine); - } - public LocalDate getLastRepaymentPeriodDueDate(final boolean includeRecalculatedInterestComponent) { LocalDate lastRepaymentDate = getDisbursementDate(); List installments = getRepaymentScheduleInstallments(); @@ -2245,10 +2207,8 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom { return receivableInterest; } - public void setHelpers(final LoanLifecycleStateMachine loanLifecycleStateMachine, - final LoanRepaymentScheduleTransactionProcessorFactory transactionProcessorFactory) { + public void setHelpers(final LoanLifecycleStateMachine loanLifecycleStateMachine) { this.loanLifecycleStateMachine = loanLifecycleStateMachine; - this.transactionProcessorFactory = transactionProcessorFactory; } public boolean isSyncDisbursementWithMeeting() { @@ -2601,89 +2561,6 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom { return (this.actualMaturityDate != null) ? (referenceDate.compareTo(this.actualMaturityDate) >= 0) : false; } - public ChangedTransactionDetail processTransactions() { - final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = getTransactionProcessor(); - final List allNonContraTransactionsPostDisbursement = retrieveListOfTransactionsForReprocessing(); - ChangedTransactionDetail changedTransactionDetail = loanRepaymentScheduleTransactionProcessor.reprocessLoanTransactions( - getDisbursementDate(), allNonContraTransactionsPostDisbursement, getCurrency(), getRepaymentScheduleInstallments(), - getActiveCharges()); - for (TransactionChangeData change : changedTransactionDetail.getTransactionChanges()) { - change.getNewTransaction().updateLoan(this); - } - /* - * Commented since throwing exception if external id present for one of the transactions. for this need to save - * the reversed transactions first and then new transactions. - */ - final List newTransactions = changedTransactionDetail.getTransactionChanges().stream() - .map(TransactionChangeData::getNewTransaction).toList(); - this.loanTransactions.addAll(newTransactions); - updateLoanSummaryDerivedFields(); - - return changedTransactionDetail; - } - - /* - * Probably this is buggy when a chargeback transaction happens - */ - public Optional processPostDisbursementTransactions() { - final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = getTransactionProcessor(); - final List allNonContraTransactionsPostDisbursement = retrieveListOfTransactionsForReprocessing(); - final List copyTransactions = new ArrayList<>(); - - if (allNonContraTransactionsPostDisbursement.isEmpty()) { - return Optional.empty(); - } - - // TODO: Probably this is not needed and can be eliminated, make sure to double check it - for (LoanTransaction loanTransaction : allNonContraTransactionsPostDisbursement) { - copyTransactions.add(LoanTransaction.copyTransactionProperties(loanTransaction)); - } - final ChangedTransactionDetail changedTransactionDetail = loanRepaymentScheduleTransactionProcessor.reprocessLoanTransactions( - getDisbursementDate(), copyTransactions, getCurrency(), getRepaymentScheduleInstallments(), getActiveCharges()); - - updateLoanSummaryDerivedFields(); - - return Optional.of(changedTransactionDetail); - } - - public LoanScheduleDTO getRecalculatedSchedule(final ScheduleGeneratorDTO generatorDTO) { - if (!isInterestBearingAndInterestRecalculationEnabled() || isNpa || isChargedOff()) { - return null; - } - final InterestMethod interestMethod = this.loanRepaymentScheduleDetail.getInterestMethod(); - final LoanScheduleGenerator loanScheduleGenerator = generatorDTO.getLoanScheduleFactory() - .create(this.loanRepaymentScheduleDetail.getLoanScheduleType(), interestMethod); - - final MathContext mc = MoneyHelper.getMathContext(); - - final LoanApplicationTerms loanApplicationTerms = constructLoanApplicationTerms(generatorDTO); - - final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = getTransactionProcessor(); - - return loanScheduleGenerator.rescheduleNextInstallments(mc, loanApplicationTerms, this, generatorDTO.getHolidayDetailDTO(), - loanRepaymentScheduleTransactionProcessor, generatorDTO.getRecalculateFrom()); - } - - public OutstandingAmountsDTO fetchPrepaymentDetail(final ScheduleGeneratorDTO scheduleGeneratorDTO, final LocalDate onDate) { - OutstandingAmountsDTO outstandingAmounts; - - if (isInterestBearingAndInterestRecalculationEnabled() && !isChargeOffOnDate(onDate)) { - final MathContext mc = MoneyHelper.getMathContext(); - - final InterestMethod interestMethod = this.loanRepaymentScheduleDetail.getInterestMethod(); - final LoanApplicationTerms loanApplicationTerms = constructLoanApplicationTerms(scheduleGeneratorDTO); - - final LoanScheduleGenerator loanScheduleGenerator = scheduleGeneratorDTO.getLoanScheduleFactory() - .create(loanApplicationTerms.getLoanScheduleType(), interestMethod); - final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = getTransactionProcessor(); - outstandingAmounts = loanScheduleGenerator.calculatePrepaymentAmount(getCurrency(), onDate, loanApplicationTerms, mc, this, - scheduleGeneratorDTO.getHolidayDetailDTO(), loanRepaymentScheduleTransactionProcessor); - } else { - outstandingAmounts = this.getTotalOutstandingOnLoan(); - } - return outstandingAmounts; - } - public LoanApplicationTerms constructLoanApplicationTerms(final ScheduleGeneratorDTO scheduleGeneratorDTO) { final Integer loanTermFrequency = this.termFrequency; NthDayType nthDayType = null; @@ -2754,22 +2631,6 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom { return annualNominalInterestRate; } - private OutstandingAmountsDTO getTotalOutstandingOnLoan() { - Money totalPrincipal = Money.zero(getCurrency()); - Money totalInterest = Money.zero(getCurrency()); - Money feeCharges = Money.zero(getCurrency()); - Money penaltyCharges = Money.zero(getCurrency()); - List repaymentSchedule = getRepaymentScheduleInstallments(); - for (final LoanRepaymentScheduleInstallment scheduledRepayment : repaymentSchedule) { - totalPrincipal = totalPrincipal.plus(scheduledRepayment.getPrincipalOutstanding(getCurrency())); - totalInterest = totalInterest.plus(scheduledRepayment.getInterestOutstanding(getCurrency())); - feeCharges = feeCharges.plus(scheduledRepayment.getFeeChargesOutstanding(getCurrency())); - penaltyCharges = penaltyCharges.plus(scheduledRepayment.getPenaltyChargesOutstanding(getCurrency())); - } - return new OutstandingAmountsDTO(totalPrincipal.getCurrency()).principal(totalPrincipal).interest(totalInterest) - .feeCharges(feeCharges).penaltyCharges(penaltyCharges); - } - public LocalDate fetchInterestRecalculateFromDate() { LocalDate recalculatedOn; if (this.interestRecalculatedOn == null) { @@ -3510,10 +3371,6 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom { isTopup = topup; } - public LoanRepaymentScheduleTransactionProcessor getTransactionProcessor() { - return transactionProcessorFactory.determineProcessor(transactionProcessingStrategyCode); - } - public boolean isProgressiveSchedule() { return getLoanProductRelatedDetail().getLoanScheduleType() == PROGRESSIVE; } diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanChargeService.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanChargeService.java index 5718e7375..34a1613af 100644 --- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanChargeService.java +++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanChargeService.java @@ -19,19 +19,28 @@ package org.apache.fineract.portfolio.loanaccount.service; import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.HashSet; import java.util.List; import java.util.Set; import lombok.RequiredArgsConstructor; import org.apache.fineract.portfolio.loanaccount.domain.Loan; import org.apache.fineract.portfolio.loanaccount.domain.LoanCharge; +import org.apache.fineract.portfolio.loanaccount.domain.LoanChargePaidBy; +import org.apache.fineract.portfolio.loanaccount.domain.LoanEvent; import org.apache.fineract.portfolio.loanaccount.domain.LoanLifecycleStateMachine; +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.transactionprocessor.MoneyHolder; +import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.TransactionCtx; import org.apache.fineract.portfolio.loanaccount.serialization.LoanChargeValidator; @RequiredArgsConstructor public class LoanChargeService { private final LoanChargeValidator loanChargeValidator; + private final LoanTransactionProcessingService loanTransactionProcessingService; public void recalculateAllCharges(final Loan loan) { Set charges = loan.getActiveCharges(); @@ -79,6 +88,37 @@ public class LoanChargeService { charge = loanCharge; } } - loan.handleChargePaidTransaction(charge, paymentTransaction, loanLifecycleStateMachine, installmentNumber); + handleChargePaidTransaction(loan, charge, paymentTransaction, loanLifecycleStateMachine, installmentNumber); + } + + public void handleChargePaidTransaction(final Loan loan, final LoanCharge charge, final LoanTransaction chargesPayment, + final LoanLifecycleStateMachine loanLifecycleStateMachine, final Integer installmentNumber) { + chargesPayment.updateLoan(loan); + final LoanChargePaidBy loanChargePaidBy = new LoanChargePaidBy(chargesPayment, charge, + chargesPayment.getAmount(loan.getCurrency()).getAmount(), installmentNumber); + chargesPayment.getLoanChargesPaid().add(loanChargePaidBy); + loan.addLoanTransaction(chargesPayment); + loanLifecycleStateMachine.transition(LoanEvent.LOAN_CHARGE_PAYMENT, loan); + + final List chargePaymentInstallments = new ArrayList<>(); + List installments = loan.getRepaymentScheduleInstallments(); + int firstNormalInstallmentNumber = LoanRepaymentScheduleProcessingWrapper + .fetchFirstNormalInstallmentNumber(loan.getRepaymentScheduleInstallments()); + for (final LoanRepaymentScheduleInstallment installment : installments) { + boolean isFirstInstallment = installment.getInstallmentNumber().equals(firstNormalInstallmentNumber); + if (installment.getInstallmentNumber().equals(installmentNumber) || (installmentNumber == null + && charge.isDueInPeriod(installment.getFromDate(), installment.getDueDate(), isFirstInstallment))) { + chargePaymentInstallments.add(installment); + break; + } + } + final Set loanCharges = new HashSet<>(1); + loanCharges.add(charge); + loanTransactionProcessingService.processLatestTransaction(loan.getTransactionProcessingStrategyCode(), chargesPayment, + new TransactionCtx(loan.getCurrency(), chargePaymentInstallments, loanCharges, + new MoneyHolder(loan.getTotalOverpaidAsMoney()), null)); + + loan.updateLoanSummaryDerivedFields(); + loan.doPostLoanTransactionChecks(chargesPayment.getTransactionDate(), loanLifecycleStateMachine); } } diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanDownPaymentHandlerServiceImpl.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanDownPaymentHandlerServiceImpl.java index 743d13363..ccff84527 100644 --- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanDownPaymentHandlerServiceImpl.java +++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanDownPaymentHandlerServiceImpl.java @@ -42,7 +42,6 @@ import org.apache.fineract.portfolio.loanaccount.domain.LoanLifecycleStateMachin 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.domain.transactionprocessor.MoneyHolder; import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.TransactionCtx; import org.apache.fineract.portfolio.loanaccount.exception.InvalidLoanStateTransitionException; @@ -61,6 +60,7 @@ public class LoanDownPaymentHandlerServiceImpl implements LoanDownPaymentHandler private final LoanRefundService loanRefundService; private final LoanRefundValidator loanRefundValidator; private final ReprocessLoanTransactionsService reprocessLoanTransactionsService; + private final LoanTransactionProcessingService loanTransactionProcessingService; @Override public LoanTransaction handleDownPayment(ScheduleGeneratorDTO scheduleGeneratorDTO, JsonCommand command, @@ -124,8 +124,6 @@ public class LoanDownPaymentHandlerServiceImpl implements LoanDownPaymentHandler loanRefundValidator.validateTransactionAmountThreshold(loan, adjustedTransaction); - final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = loan.getTransactionProcessor(); - final LoanRepaymentScheduleInstallment currentInstallment = loan .fetchLoanRepaymentScheduleInstallmentByDueDate(loanTransaction.getTransactionDate()); @@ -135,7 +133,7 @@ public class LoanDownPaymentHandlerServiceImpl implements LoanDownPaymentHandler if (isTransactionChronologicallyLatest && adjustedTransaction == null && (!reprocess || !loan.isInterestBearingAndInterestRecalculationEnabled()) && !loan.isForeclosure()) { - loanRepaymentScheduleTransactionProcessor.processLatestTransaction(loanTransaction, + loanTransactionProcessingService.processLatestTransaction(loan.getTransactionProcessingStrategyCode(), loanTransaction, new TransactionCtx(loan.getCurrency(), loan.getRepaymentScheduleInstallments(), loan.getActiveCharges(), new MoneyHolder(loan.getTotalOverpaidAsMoney()), null)); reprocess = false; diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanRefundService.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanRefundService.java index b41b8b599..0cfb9bb38 100644 --- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanRefundService.java +++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanRefundService.java @@ -26,7 +26,6 @@ import org.apache.fineract.portfolio.loanaccount.domain.Loan; import org.apache.fineract.portfolio.loanaccount.domain.LoanEvent; import org.apache.fineract.portfolio.loanaccount.domain.LoanLifecycleStateMachine; import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction; -import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.LoanRepaymentScheduleTransactionProcessor; import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.MoneyHolder; import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.TransactionCtx; import org.apache.fineract.portfolio.loanaccount.serialization.LoanRefundValidator; @@ -35,6 +34,7 @@ import org.apache.fineract.portfolio.loanaccount.serialization.LoanRefundValidat public class LoanRefundService { private final LoanRefundValidator loanRefundValidator; + private final LoanTransactionProcessingService loadTransactionProcessingService; public void makeRefund(final Loan loan, final LoanTransaction loanTransaction, final LoanLifecycleStateMachine loanLifecycleStateMachine, final List existingTransactionIds, @@ -106,10 +106,9 @@ public class LoanRefundService { loanRefundValidator.validateTransactionDateNotInFuture(loanTransactionDate); loanRefundValidator.validateTransactionAmountThreshold(loan, null); - final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = loan.getTransactionProcessor(); - - loanRepaymentScheduleTransactionProcessor.processLatestTransaction(loanTransaction, new TransactionCtx(loan.getCurrency(), - loan.getRepaymentScheduleInstallments(), loan.getActiveCharges(), new MoneyHolder(loan.getTotalOverpaidAsMoney()), null)); + loadTransactionProcessingService.processLatestTransaction(loan.getTransactionProcessingStrategyCode(), loanTransaction, + new TransactionCtx(loan.getCurrency(), loan.getRepaymentScheduleInstallments(), loan.getActiveCharges(), + new MoneyHolder(loan.getTotalOverpaidAsMoney()), null)); loan.updateLoanSummaryDerivedFields(); loan.doPostLoanTransactionChecks(loanTransaction.getTransactionDate(), loanLifecycleStateMachine); diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanScheduleService.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanScheduleService.java index 2eab5a644..99c2e9763 100644 --- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanScheduleService.java +++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanScheduleService.java @@ -35,6 +35,7 @@ public class LoanScheduleService { private final LoanChargeService loanChargeService; private final ReprocessLoanTransactionsService reprocessLoanTransactionsService; + private final LoanTransactionProcessingService loadTransactionProcessingService; /** * Ability to regenerate the repayment schedule based on the loans current details/state. @@ -79,7 +80,7 @@ public class LoanScheduleService { public void regenerateRepaymentScheduleWithInterestRecalculation(final Loan loan, final ScheduleGeneratorDTO generatorDTO) { final LocalDate lastTransactionDate = loan.getLastUserTransactionDate(); - final LoanScheduleDTO loanSchedule = loan.getRecalculatedSchedule(generatorDTO); + final LoanScheduleDTO loanSchedule = loadTransactionProcessingService.getRecalculatedSchedule(generatorDTO, loan); if (loanSchedule == null) { return; } @@ -107,7 +108,7 @@ public class LoanScheduleService { } } } - loan.processPostDisbursementTransactions(); + loadTransactionProcessingService.processPostDisbursementTransactions(loan); } public void handleRegenerateRepaymentScheduleWithInterestRecalculation(final Loan loan, final ScheduleGeneratorDTO generatorDTO) { diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanTransactionProcessingService.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanTransactionProcessingService.java new file mode 100644 index 000000000..de9b8895a --- /dev/null +++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanTransactionProcessingService.java @@ -0,0 +1,151 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.fineract.portfolio.loanaccount.service; + +import java.math.MathContext; +import java.time.LocalDate; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import lombok.RequiredArgsConstructor; +import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency; +import org.apache.fineract.organisation.monetary.domain.Money; +import org.apache.fineract.organisation.monetary.domain.MoneyHelper; +import org.apache.fineract.portfolio.loanaccount.data.OutstandingAmountsDTO; +import org.apache.fineract.portfolio.loanaccount.data.ScheduleGeneratorDTO; +import org.apache.fineract.portfolio.loanaccount.domain.ChangedTransactionDetail; +import org.apache.fineract.portfolio.loanaccount.domain.Loan; +import org.apache.fineract.portfolio.loanaccount.domain.LoanCharge; +import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment; +import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleTransactionProcessorFactory; +import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction; +import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.LoanRepaymentScheduleTransactionProcessor; +import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.TransactionCtx; +import org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanScheduleDTO; +import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanApplicationTerms; +import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleGenerator; +import org.apache.fineract.portfolio.loanproduct.domain.InterestMethod; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class LoanTransactionProcessingService { + + private final LoanRepaymentScheduleTransactionProcessorFactory transactionProcessorFactory; + + public ChangedTransactionDetail processLatestTransaction(String transactionProcessingStrategyCode, LoanTransaction loanTransaction, + TransactionCtx ctx) { + final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = getTransactionProcessor( + transactionProcessingStrategyCode); + return loanRepaymentScheduleTransactionProcessor.processLatestTransaction(loanTransaction, ctx); + } + + public ChangedTransactionDetail reprocessLoanTransactions(String transactionProcessingStrategyCode, LocalDate disbursementDate, + List repaymentsOrWaivers, MonetaryCurrency currency, + List repaymentScheduleInstallments, Set charges) { + final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = getTransactionProcessor( + transactionProcessingStrategyCode); + return loanRepaymentScheduleTransactionProcessor.reprocessLoanTransactions(disbursementDate, repaymentsOrWaivers, currency, + repaymentScheduleInstallments, charges); + } + + public LoanRepaymentScheduleTransactionProcessor getTransactionProcessor(String transactionProcessingStrategyCode) { + return transactionProcessorFactory.determineProcessor(transactionProcessingStrategyCode); + } + + public Optional processPostDisbursementTransactions(Loan loan) { + final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = getTransactionProcessor( + loan.getTransactionProcessingStrategyCode()); + final List allNonContraTransactionsPostDisbursement = loan.retrieveListOfTransactionsForReprocessing(); + final List copyTransactions = new ArrayList<>(); + + if (allNonContraTransactionsPostDisbursement.isEmpty()) { + return Optional.empty(); + } + + // TODO: Probably this is not needed and can be eliminated, make sure to double check it + for (LoanTransaction loanTransaction : allNonContraTransactionsPostDisbursement) { + copyTransactions.add(LoanTransaction.copyTransactionProperties(loanTransaction)); + } + final ChangedTransactionDetail changedTransactionDetail = loanRepaymentScheduleTransactionProcessor.reprocessLoanTransactions( + loan.getDisbursementDate(), copyTransactions, loan.getCurrency(), loan.getRepaymentScheduleInstallments(), + loan.getActiveCharges()); + + loan.updateLoanSummaryDerivedFields(); + + return Optional.of(changedTransactionDetail); + } + + public LoanScheduleDTO getRecalculatedSchedule(final ScheduleGeneratorDTO generatorDTO, Loan loan) { + if (!loan.isInterestBearingAndInterestRecalculationEnabled() || loan.isNpa() || loan.isChargedOff()) { + return null; + } + final InterestMethod interestMethod = loan.getLoanRepaymentScheduleDetail().getInterestMethod(); + final LoanScheduleGenerator loanScheduleGenerator = generatorDTO.getLoanScheduleFactory() + .create(loan.getLoanRepaymentScheduleDetail().getLoanScheduleType(), interestMethod); + + final MathContext mc = MoneyHelper.getMathContext(); + + final LoanApplicationTerms loanApplicationTerms = loan.constructLoanApplicationTerms(generatorDTO); + + final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = getTransactionProcessor( + loan.getTransactionProcessingStrategyCode()); + + return loanScheduleGenerator.rescheduleNextInstallments(mc, loanApplicationTerms, loan, generatorDTO.getHolidayDetailDTO(), + loanRepaymentScheduleTransactionProcessor, generatorDTO.getRecalculateFrom()); + } + + public OutstandingAmountsDTO fetchPrepaymentDetail(final ScheduleGeneratorDTO scheduleGeneratorDTO, final LocalDate onDate, Loan loan) { + OutstandingAmountsDTO outstandingAmounts; + + if (loan.isInterestBearingAndInterestRecalculationEnabled() && !loan.isChargeOffOnDate(onDate)) { + final MathContext mc = MoneyHelper.getMathContext(); + + final InterestMethod interestMethod = loan.getLoanRepaymentScheduleDetail().getInterestMethod(); + final LoanApplicationTerms loanApplicationTerms = loan.constructLoanApplicationTerms(scheduleGeneratorDTO); + + final LoanScheduleGenerator loanScheduleGenerator = scheduleGeneratorDTO.getLoanScheduleFactory() + .create(loanApplicationTerms.getLoanScheduleType(), interestMethod); + final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = getTransactionProcessor( + loan.getTransactionProcessingStrategyCode()); + outstandingAmounts = loanScheduleGenerator.calculatePrepaymentAmount(loan.getCurrency(), onDate, loanApplicationTerms, mc, loan, + scheduleGeneratorDTO.getHolidayDetailDTO(), loanRepaymentScheduleTransactionProcessor); + } else { + outstandingAmounts = getTotalOutstandingOnLoan(loan); + } + return outstandingAmounts; + } + + private OutstandingAmountsDTO getTotalOutstandingOnLoan(Loan loan) { + Money totalPrincipal = Money.zero(loan.getCurrency()); + Money totalInterest = Money.zero(loan.getCurrency()); + Money feeCharges = Money.zero(loan.getCurrency()); + Money penaltyCharges = Money.zero(loan.getCurrency()); + List repaymentSchedule = loan.getRepaymentScheduleInstallments(); + for (final LoanRepaymentScheduleInstallment scheduledRepayment : repaymentSchedule) { + totalPrincipal = totalPrincipal.plus(scheduledRepayment.getPrincipalOutstanding(loan.getCurrency())); + totalInterest = totalInterest.plus(scheduledRepayment.getInterestOutstanding(loan.getCurrency())); + feeCharges = feeCharges.plus(scheduledRepayment.getFeeChargesOutstanding(loan.getCurrency())); + penaltyCharges = penaltyCharges.plus(scheduledRepayment.getPenaltyChargesOutstanding(loan.getCurrency())); + } + return new OutstandingAmountsDTO(totalPrincipal.getCurrency()).principal(totalPrincipal).interest(totalInterest) + .feeCharges(feeCharges).penaltyCharges(penaltyCharges); + } +} diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/ReprocessLoanTransactionsServiceImpl.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/ReprocessLoanTransactionsServiceImpl.java index afc9c0ed7..e39b0f57f 100644 --- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/ReprocessLoanTransactionsServiceImpl.java +++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/ReprocessLoanTransactionsServiceImpl.java @@ -29,7 +29,6 @@ import org.apache.fineract.portfolio.loanaccount.domain.LoanAccountService; import org.apache.fineract.portfolio.loanaccount.domain.LoanCharge; import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleProcessingWrapper; import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction; -import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.LoanRepaymentScheduleTransactionProcessor; import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.MoneyHolder; import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.TransactionCtx; import org.springframework.stereotype.Service; @@ -41,6 +40,7 @@ public class ReprocessLoanTransactionsServiceImpl implements ReprocessLoanTransa private final LoanAccountService loanAccountService; private final LoanAccountTransfersService loanAccountTransfersService; private final ReplayedTransactionBusinessEventService replayedTransactionBusinessEventService; + private final LoanTransactionProcessingService loadTransactionProcessingService; @Override public void reprocessTransactions(final Loan loan) { @@ -65,7 +65,7 @@ public class ReprocessLoanTransactionsServiceImpl implements ReprocessLoanTransa @Override public void processPostDisbursementTransactions(final Loan loan) { - loan.processPostDisbursementTransactions().ifPresent(this::handleChangedDetail); + loadTransactionProcessingService.processPostDisbursementTransactions(loan).ifPresent(this::handleChangedDetail); } @Override @@ -98,7 +98,8 @@ public class ReprocessLoanTransactionsServiceImpl implements ReprocessLoanTransa @Override public void processLatestTransaction(final LoanTransaction loanTransaction, final Loan loan) { - final ChangedTransactionDetail changedTransactionDetail = loan.getTransactionProcessor().processLatestTransaction(loanTransaction, + final ChangedTransactionDetail changedTransactionDetail = loadTransactionProcessingService.processLatestTransaction( + loan.getTransactionProcessingStrategyCode(), loanTransaction, new TransactionCtx(loan.getCurrency(), loan.getRepaymentScheduleInstallments(), loan.getActiveCharges(), new MoneyHolder(loan.getTotalOverpaidAsMoney()), new ChangedTransactionDetail())); final List newTransactions = changedTransactionDetail.getTransactionChanges().stream() @@ -125,10 +126,9 @@ public class ReprocessLoanTransactionsServiceImpl implements ReprocessLoanTransa private ChangedTransactionDetail reprocessTransactionsAndFetchChangedTransactions(final Loan loan, final List loanTransactions) { - final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = loan.getTransactionProcessor(); - final ChangedTransactionDetail changedTransactionDetail = loanRepaymentScheduleTransactionProcessor.reprocessLoanTransactions( - loan.getDisbursementDate(), loanTransactions, loan.getCurrency(), loan.getRepaymentScheduleInstallments(), - loan.getActiveCharges()); + final ChangedTransactionDetail changedTransactionDetail = loadTransactionProcessingService.reprocessLoanTransactions( + loan.getTransactionProcessingStrategyCode(), loan.getDisbursementDate(), loanTransactions, loan.getCurrency(), + loan.getRepaymentScheduleInstallments(), loan.getActiveCharges()); for (TransactionChangeData change : changedTransactionDetail.getTransactionChanges()) { change.getNewTransaction().updateLoan(loan); } diff --git a/fineract-progressive-loan-embeddable-schedule-generator/src/test/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/EmbeddableProgressiveLoanScheduleGeneratorTest.java b/fineract-progressive-loan-embeddable-schedule-generator/src/test/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/EmbeddableProgressiveLoanScheduleGeneratorTest.java index 32e7fccb4..2899cdf11 100644 --- a/fineract-progressive-loan-embeddable-schedule-generator/src/test/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/EmbeddableProgressiveLoanScheduleGeneratorTest.java +++ b/fineract-progressive-loan-embeddable-schedule-generator/src/test/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/EmbeddableProgressiveLoanScheduleGeneratorTest.java @@ -32,7 +32,10 @@ import org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanScheduleP import org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanSchedulePlanRepaymentPeriod; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.junit.jupiter.MockitoExtension; +@ExtendWith(MockitoExtension.class) class EmbeddableProgressiveLoanScheduleGeneratorTest { @Test diff --git a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/ProgressiveLoanScheduleGenerator.java b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/ProgressiveLoanScheduleGenerator.java index 6673d77a8..caa7ea5ed 100644 --- a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/ProgressiveLoanScheduleGenerator.java +++ b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/ProgressiveLoanScheduleGenerator.java @@ -30,7 +30,6 @@ import java.util.Comparator; import java.util.HashSet; import java.util.List; import java.util.Set; -import lombok.RequiredArgsConstructor; import org.apache.fineract.infrastructure.core.service.MathUtil; import org.apache.fineract.organisation.monetary.data.CurrencyData; import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency; @@ -49,19 +48,32 @@ import org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanScheduleM import org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanScheduleParams; import org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanSchedulePlan; import org.apache.fineract.portfolio.loanaccount.loanschedule.exception.MultiDisbursementOutstandingAmoutException; +import org.apache.fineract.portfolio.loanaccount.service.LoanTransactionProcessingService; import org.apache.fineract.portfolio.loanproduct.calc.EMICalculator; import org.apache.fineract.portfolio.loanproduct.calc.data.OutstandingDetails; import org.apache.fineract.portfolio.loanproduct.calc.data.ProgressiveLoanInterestScheduleModel; import org.apache.fineract.portfolio.loanproduct.domain.RepaymentStartDateType; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component -@RequiredArgsConstructor public class ProgressiveLoanScheduleGenerator implements LoanScheduleGenerator { private final ScheduledDateGenerator scheduledDateGenerator; private final EMICalculator emiCalculator; + private LoanTransactionProcessingService loanTransactionProcessingService; + + public ProgressiveLoanScheduleGenerator(ScheduledDateGenerator scheduledDateGenerator, EMICalculator emiCalculator) { + this.scheduledDateGenerator = scheduledDateGenerator; + this.emiCalculator = emiCalculator; + } + + @Autowired(required = false) + public void setLoanTransactionProcessingService(LoanTransactionProcessingService loanTransactionProcessingService) { + this.loanTransactionProcessingService = loanTransactionProcessingService; + } + public LoanSchedulePlan generate(final MathContext mc, final LoanRepaymentScheduleModelData modelData) { LoanApplicationTerms loanApplicationTerms = LoanApplicationTerms.assembleFrom(modelData, mc); return LoanSchedulePlan.from(generate(mc, loanApplicationTerms, null, null)); @@ -208,7 +220,8 @@ public class ProgressiveLoanScheduleGenerator implements LoanScheduleGenerator { @Override public Money getPeriodInterestTillDate(@NotNull LoanRepaymentScheduleInstallment installment, @NotNull LocalDate targetDate) { Loan loan = installment.getLoan(); - LoanRepaymentScheduleTransactionProcessor transactionProcessor = loan.getTransactionProcessor(); + LoanRepaymentScheduleTransactionProcessor transactionProcessor = loanTransactionProcessingService + .getTransactionProcessor(loan.getTransactionProcessingStrategyCode()); if (!(transactionProcessor instanceof AdvancedPaymentScheduleTransactionProcessor processor)) { throw new IllegalStateException("Expected an AdvancedPaymentScheduleTransactionProcessor"); } diff --git a/fineract-progressive-loan/src/test/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/LoanScheduleGeneratorTest.java b/fineract-progressive-loan/src/test/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/LoanScheduleGeneratorTest.java index 0548e6dd0..e56927d6b 100644 --- a/fineract-progressive-loan/src/test/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/LoanScheduleGeneratorTest.java +++ b/fineract-progressive-loan/src/test/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/LoanScheduleGeneratorTest.java @@ -19,6 +19,7 @@ package org.apache.fineract.portfolio.loanaccount.loanschedule.domain; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.mock; import java.math.BigDecimal; import java.math.MathContext; @@ -32,9 +33,13 @@ import org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanScheduleP import org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanSchedulePlanDisbursementPeriod; import org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanSchedulePlanDownPaymentPeriod; import org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanSchedulePlanRepaymentPeriod; +import org.apache.fineract.portfolio.loanaccount.service.LoanTransactionProcessingService; import org.apache.fineract.portfolio.loanproduct.calc.ProgressiveEMICalculator; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.junit.jupiter.MockitoExtension; +@ExtendWith(MockitoExtension.class) class LoanScheduleGeneratorTest { private static final ProgressiveEMICalculator emiCalculator = new ProgressiveEMICalculator(); @@ -49,6 +54,7 @@ class LoanScheduleGeneratorTest { private static final LocalDate DISBURSEMENT_DATE = LocalDate.of(2024, 1, 15); private static final MathContext mc = new MathContext(12, RoundingMode.HALF_EVEN); private static final BigDecimal DOWN_PAYMENT_PORTION = BigDecimal.valueOf(25); + private static final LoanTransactionProcessingService loanTransactionProcessingService = mock(LoanTransactionProcessingService.class); @Test void testGenerateLoanSchedule() { @@ -58,6 +64,7 @@ class LoanScheduleGeneratorTest { ScheduledDateGenerator scheduledDateGenerator = new DefaultScheduledDateGenerator(); ProgressiveLoanScheduleGenerator generator = new ProgressiveLoanScheduleGenerator(scheduledDateGenerator, emiCalculator); + generator.setLoanTransactionProcessingService(loanTransactionProcessingService); LoanSchedulePlan loanSchedule = generator.generate(mc, modelData); @@ -94,6 +101,7 @@ class LoanScheduleGeneratorTest { ScheduledDateGenerator scheduledDateGenerator = new DefaultScheduledDateGenerator(); ProgressiveLoanScheduleGenerator generator = new ProgressiveLoanScheduleGenerator(scheduledDateGenerator, emiCalculator); + generator.setLoanTransactionProcessingService(loanTransactionProcessingService); LoanSchedulePlan loanSchedule = generator.generate(mc, modelData); diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/service/LoanScheduleCalculationPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/service/LoanScheduleCalculationPlatformServiceImpl.java index f5d493511..077b32c0e 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/service/LoanScheduleCalculationPlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/service/LoanScheduleCalculationPlatformServiceImpl.java @@ -209,7 +209,7 @@ public class LoanScheduleCalculationPlatformServiceImpl implements LoanScheduleC private Loan fetchLoan(final Long accountId) { final Loan loanAccount = this.loanRepository.findOneWithNotFoundDetection(accountId, true); - loanAccount.setHelpers(defaultLoanLifecycleStateMachine, this.loanRepaymentScheduleTransactionProcessorFactory); + loanAccount.setHelpers(defaultLoanLifecycleStateMachine); return loanAccount; } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/service/LoanReschedulePreviewPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/service/LoanReschedulePreviewPlatformServiceImpl.java index f568095a1..9334fb7cc 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/service/LoanReschedulePreviewPlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/service/LoanReschedulePreviewPlatformServiceImpl.java @@ -129,7 +129,6 @@ public class LoanReschedulePreviewPlatformServiceImpl implements LoanRescheduleP .determineProcessor(loan.transactionProcessingStrategy()); final LoanScheduleGenerator loanScheduleGenerator = this.loanScheduleFactory.create(loanApplicationTerms.getLoanScheduleType(), loanApplicationTerms.getInterestMethod()); - loan.setHelpers(null, this.loanRepaymentScheduleTransactionProcessorFactory); final LoanScheduleDTO loanSchedule = loanScheduleGenerator.rescheduleNextInstallments(mathContext, loanApplicationTerms, loan, loanApplicationTerms.getHolidayDetailDTO(), loanRepaymentScheduleTransactionProcessor, rescheduleFromDate); final LoanScheduleModel loanScheduleModel = loanSchedule.getLoanScheduleModel(); diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/service/LoanRescheduleRequestWritePlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/service/LoanRescheduleRequestWritePlatformServiceImpl.java index cafa08562..815aaf991 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/service/LoanRescheduleRequestWritePlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/service/LoanRescheduleRequestWritePlatformServiceImpl.java @@ -422,7 +422,7 @@ public class LoanRescheduleRequestWritePlatformServiceImpl implements LoanResche final LoanScheduleGenerator loanScheduleGenerator = this.loanScheduleFactory.create(loanApplicationTerms.getLoanScheduleType(), loanApplicationTerms.getInterestMethod()); final LoanLifecycleStateMachine loanLifecycleStateMachine = null; - loan.setHelpers(loanLifecycleStateMachine, this.loanRepaymentScheduleTransactionProcessorFactory); + loan.setHelpers(loanLifecycleStateMachine); final LoanScheduleDTO loanSchedule = loanScheduleGenerator.rescheduleNextInstallments(mathContext, loanApplicationTerms, loan, loanApplicationTerms.getHolidayDetailDTO(), loanRepaymentScheduleTransactionProcessor, rescheduleFromDate); diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/serialization/LoanApplicationValidator.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/serialization/LoanApplicationValidator.java index e7d204bb4..a89681b28 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/serialization/LoanApplicationValidator.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/serialization/LoanApplicationValidator.java @@ -2022,7 +2022,7 @@ public final class LoanApplicationValidator { baseDataValidator.reset().parameter(LoanApiConstants.noteParameterName).value(note).notExceedingLengthOf(1000); final Loan loan = this.loanRepositoryWrapper.findOneWithNotFoundDetection(loanId, true); - loan.setHelpers(defaultLoanLifecycleStateMachine, this.loanRepaymentScheduleTransactionProcessorFactory); + loan.setHelpers(defaultLoanLifecycleStateMachine); final Client client = loan.client(); if (client != null && client.isNotActive()) { 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 13d325963..693933f9d 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 @@ -71,7 +71,6 @@ import org.apache.fineract.portfolio.loanaccount.domain.LoanInstallmentCharge; import org.apache.fineract.portfolio.loanaccount.domain.LoanInterestRecalcualtionAdditionalDetails; import org.apache.fineract.portfolio.loanaccount.domain.LoanInterestRecalculationDetails; import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment; -import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleTransactionProcessorFactory; import org.apache.fineract.portfolio.loanaccount.domain.LoanRepositoryWrapper; import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction; import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionComparator; @@ -106,7 +105,6 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing private final JournalEntryWritePlatformService journalEntryWritePlatformService; private final LoanTransactionRepository loanTransactionRepository; private final LoanScheduleGeneratorFactory loanScheduleFactory; - private final LoanRepaymentScheduleTransactionProcessorFactory transactionProcessorFactory; @Qualifier(TaskExecutorConstant.CONFIGURABLE_TASK_EXECUTOR_BEAN_NAME) private final ThreadPoolTaskExecutor taskExecutor; @@ -123,7 +121,6 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing List errors = new ArrayList<>(); for (Loan loan : loans) { try { - setSetHelpers(loan); addPeriodicAccruals(tillDate, loan); } catch (Exception e) { log.error("Failed to add accrual for loan {}", loan.getId(), e); @@ -166,7 +163,6 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing ThreadLocalContextUtil.init(context); transactionTemplate.executeWithoutResult(status -> { Loan loan = loanRepositoryWrapper.findOneWithNotFoundDetection(outerLoan.getId()); - setSetHelpers(loan); try { log.debug("Adding accruals for loan '{}'", loan.getId()); addAccruals(loan, tillDate, false, false, true); @@ -1293,8 +1289,4 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing public boolean isProgressiveAccrual(@NotNull Loan loan) { return loan.isProgressiveSchedule(); } - - private void setSetHelpers(Loan loan) { - loan.setHelpers(null, transactionProcessorFactory); - } } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanApplicationWritePlatformServiceJpaRepositoryImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanApplicationWritePlatformServiceJpaRepositoryImpl.java index c481fb163..bddd111a9 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanApplicationWritePlatformServiceJpaRepositoryImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanApplicationWritePlatformServiceJpaRepositoryImpl.java @@ -765,7 +765,7 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa private Loan retrieveLoanBy(final Long loanId) { final Loan loan = this.loanRepositoryWrapper.findOneWithNotFoundDetection(loanId, true); - loan.setHelpers(defaultLoanLifecycleStateMachine, this.loanRepaymentScheduleTransactionProcessorFactory); + loan.setHelpers(defaultLoanLifecycleStateMachine); return 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 64656b1a2..d87815943 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 @@ -169,7 +169,7 @@ public class LoanAssemblerImpl implements LoanAssembler { @Override public void setHelpers(final Loan loanAccount) { - loanAccount.setHelpers(defaultLoanLifecycleStateMachine, loanRepaymentScheduleTransactionProcessorFactory); + loanAccount.setHelpers(defaultLoanLifecycleStateMachine); } @Override @@ -294,7 +294,7 @@ public class LoanAssemblerImpl implements LoanAssembler { } copyAdvancedPaymentRulesIfApplicable(transactionProcessingStrategyCode, loanProduct, loanApplication); - loanApplication.setHelpers(defaultLoanLifecycleStateMachine, this.loanRepaymentScheduleTransactionProcessorFactory); + loanApplication.setHelpers(defaultLoanLifecycleStateMachine); // TODO: review loanChargeService.recalculateAllCharges(loanApplication); topUpLoanConfiguration(element, loanApplication); diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java index fd02ef971..63a7f29bb 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java @@ -179,6 +179,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService, Loa private final LoanTransactionRelationReadService loanTransactionRelationReadService; private final LoanForeclosureValidator loanForeclosureValidator; private final LoanTransactionMapper loanTransactionMapper; + private final LoanTransactionProcessingService loadTransactionProcessingService; @Override public LoanAccountData retrieveOne(final Long loanId) { @@ -466,7 +467,6 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService, Loa this.loanUtilService.validateRepaymentTransactionType(repaymentTransactionType); final Loan loan = this.loanRepositoryWrapper.findOneWithNotFoundDetection(loanId, true); - loan.setHelpers(null, loanRepaymentScheduleTransactionProcessorFactory); final MonetaryCurrency currency = loan.getCurrency(); final ApplicationCurrency applicationCurrency = this.applicationCurrencyRepository.findOneWithNotFoundDetection(currency); @@ -476,7 +476,8 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService, Loa final LocalDate earliestUnpaidInstallmentDate = DateUtils.getBusinessLocalDate(); final LocalDate recalculateFrom = null; final ScheduleGeneratorDTO scheduleGeneratorDTO = loanUtilService.buildScheduleGeneratorDTO(loan, recalculateFrom); - final OutstandingAmountsDTO outstandingAmounts = loan.fetchPrepaymentDetail(scheduleGeneratorDTO, onDate); + final OutstandingAmountsDTO outstandingAmounts = loadTransactionProcessingService.fetchPrepaymentDetail(scheduleGeneratorDTO, + onDate, loan); final LoanTransactionEnumData transactionType = LoanEnumerations.transactionType(repaymentTransactionType); final Collection paymentOptions = this.paymentTypeReadPlatformService.retrieveAllPaymentTypes(); final BigDecimal outstandingLoanBalance = outstandingAmounts.principal().getAmount(); 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 264046d73..5da8d282e 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 @@ -176,7 +176,6 @@ import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRelationR 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.LoanRepaymentScheduleTransactionProcessor; import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.MoneyHolder; import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.TransactionCtx; import org.apache.fineract.portfolio.loanaccount.exception.DateMismatchException; @@ -281,6 +280,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf private final LoanAccountService loanAccountService; private final LoanJournalEntryPoster journalEntryPoster; private final LoanAdjustmentService loanAdjustmentService; + private final LoanTransactionProcessingService loanTransactionProcessingService; @Transactional @Override @@ -1068,8 +1068,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf loan.addLoanTransaction(newInterestPaymentWaiverTransaction); } - final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = loan.getTransactionProcessor(); - final LoanRepaymentScheduleInstallment currentInstallment = loan.fetchLoanRepaymentScheduleInstallmentByDueDate(transactionDate); boolean reprocess = true; @@ -1081,9 +1079,9 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf if (isTransactionChronologicallyLatest && (!reprocess || !loan.isInterestBearingAndInterestRecalculationEnabled()) && !loan.isForeclosure()) { - loanRepaymentScheduleTransactionProcessor.processLatestTransaction(newInterestPaymentWaiverTransaction, - new TransactionCtx(loan.getCurrency(), loan.getRepaymentScheduleInstallments(), loan.getActiveCharges(), - new MoneyHolder(loan.getTotalOverpaidAsMoney()), null)); + loanTransactionProcessingService.processLatestTransaction(loan.getTransactionProcessingStrategyCode(), + newInterestPaymentWaiverTransaction, new TransactionCtx(loan.getCurrency(), loan.getRepaymentScheduleInstallments(), + loan.getActiveCharges(), new MoneyHolder(loan.getTotalOverpaidAsMoney()), null)); reprocess = false; if (loan.isInterestBearingAndInterestRecalculationEnabled()) { if (currentInstallment == null || currentInstallment.isNotFullyPaidOff()) { @@ -2105,7 +2103,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(loan.getOfficeId(), loan.getDisbursementDate()); if (loan.isInterestBearingAndInterestRecalculationEnabled()) { ScheduleGeneratorDTO scheduleGeneratorDTO = loanUtilService.buildScheduleGeneratorDTO(loan, recalculateFrom); - loan.setHelpers(null, this.transactionProcessingStrategy); loanScheduleService.recalculateScheduleFromLastTransaction(loan, scheduleGeneratorDTO, existingTransactionIds, existingReversedTransactionIds); createAndSaveLoanScheduleArchive(loan, scheduleGeneratorDTO); @@ -3106,15 +3103,14 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf public void handleChargebackTransaction(final Loan loan, LoanTransaction chargebackTransaction, final LoanLifecycleStateMachine loanLifecycleStateMachine) { loanTransactionValidator.validateIfTransactionIsChargeback(chargebackTransaction); - final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = loan.getTransactionProcessor(); loan.addLoanTransaction(chargebackTransaction); if (loan.isInterestBearing() && loan.isInterestRecalculationEnabled()) { - loanRepaymentScheduleTransactionProcessor.reprocessLoanTransactions(loan.getDisbursementDate(), - loan.retrieveListOfTransactionsForReprocessing(), loan.getCurrency(), loan.getRepaymentScheduleInstallments(), - loan.getActiveCharges()); + loanTransactionProcessingService.reprocessLoanTransactions(loan.getTransactionProcessingStrategyCode(), + loan.getDisbursementDate(), loan.retrieveListOfTransactionsForReprocessing(), loan.getCurrency(), + loan.getRepaymentScheduleInstallments(), loan.getActiveCharges()); } else { - loanRepaymentScheduleTransactionProcessor.processLatestTransaction(chargebackTransaction, + loanTransactionProcessingService.processLatestTransaction(loan.getTransactionProcessingStrategyCode(), chargebackTransaction, new TransactionCtx(loan.getCurrency(), loan.getRepaymentScheduleInstallments(), loan.getActiveCharges(), new MoneyHolder(loan.getTotalOverpaidAsMoney()), null)); } @@ -3261,7 +3257,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf final LoanLifecycleStateMachine loanLifecycleStateMachine, final Map changes, final List existingTransactionIds, final List existingReversedTransactionIds, final AppUser currentUser, final ScheduleGeneratorDTO scheduleGeneratorDTO) { - final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = loan.getTransactionProcessor(); closeDisbursements(loan, scheduleGeneratorDTO); final LocalDate writtenOffOnLocalDate = command.localDateValueOfParameterNamed(TRANSACTION_DATE); @@ -3313,8 +3308,9 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf } loan.addLoanTransaction(loanTransaction); - loanRepaymentScheduleTransactionProcessor.processLatestTransaction(loanTransaction, new TransactionCtx(loan.getCurrency(), - loan.getRepaymentScheduleInstallments(), loan.getActiveCharges(), new MoneyHolder(loan.getTotalOverpaidAsMoney()), null)); + loanTransactionProcessingService.processLatestTransaction(loan.getTransactionProcessingStrategyCode(), loanTransaction, + new TransactionCtx(loan.getCurrency(), loan.getRepaymentScheduleInstallments(), loan.getActiveCharges(), + new MoneyHolder(loan.getTotalOverpaidAsMoney()), null)); loan.updateLoanSummaryDerivedFields(); @@ -3366,7 +3362,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf final String errorMessage = "The date on which a loan is closed cannot be in the future."; throw new InvalidLoanStateTransitionException("close", "cannot.be.a.future.date", errorMessage, closureDate); } - final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = loan.getTransactionProcessor(); closeDisbursements(loan, scheduleGeneratorDTO); LoanTransaction loanTransaction = null; @@ -3390,7 +3385,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf } loan.addLoanTransaction(loanTransaction); - loanRepaymentScheduleTransactionProcessor.processLatestTransaction(loanTransaction, + loanTransactionProcessingService.processLatestTransaction(loan.getTransactionProcessingStrategyCode(), loanTransaction, new TransactionCtx(loan.getCurrency(), loan.getRepaymentScheduleInstallments(), loan.getActiveCharges(), new MoneyHolder(loan.getTotalOverpaidAsMoney()), null)); diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/ProgressiveLoanInterestRefundServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/ProgressiveLoanInterestRefundServiceImpl.java index bbeccfae6..17d0d3729 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/ProgressiveLoanInterestRefundServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/ProgressiveLoanInterestRefundServiceImpl.java @@ -60,6 +60,7 @@ public class ProgressiveLoanInterestRefundServiceImpl implements InterestRefundS private final LoanAssembler loanAssembler; private final LoanScheduleService loanScheduleService; private final LoanUtilService loanUtilService; + private final LoanTransactionProcessingService loanTransactionProcessingService; private static void simulateRepaymentForDisbursements(LoanTransaction lt, final AtomicReference refundFinal, List collect) { @@ -116,7 +117,7 @@ public class ProgressiveLoanInterestRefundServiceImpl implements InterestRefundS LocalDate relatedRefundTransactionDate, List newTransactions, List oldTransactionIds) { Loan loan = loanAssembler.assembleFrom(loanId); if (processor == null) { - processor = loan.getTransactionProcessor(); + processor = loanTransactionProcessingService.getTransactionProcessor(loan.getTransactionProcessingStrategyCode()); } if (!(processor instanceof AdvancedPaymentScheduleTransactionProcessor)) { throw new IllegalArgumentException( 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 120a13574..607dda3a7 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 @@ -137,6 +137,7 @@ import org.apache.fineract.portfolio.loanaccount.service.LoanScheduleService; import org.apache.fineract.portfolio.loanaccount.service.LoanStatusChangePlatformService; import org.apache.fineract.portfolio.loanaccount.service.LoanStatusChangePlatformServiceImpl; import org.apache.fineract.portfolio.loanaccount.service.LoanTransactionAssembler; +import org.apache.fineract.portfolio.loanaccount.service.LoanTransactionProcessingService; import org.apache.fineract.portfolio.loanaccount.service.LoanTransactionRelationReadService; import org.apache.fineract.portfolio.loanaccount.service.LoanUtilService; import org.apache.fineract.portfolio.loanaccount.service.LoanWritePlatformService; @@ -326,7 +327,8 @@ public class LoanAccountConfiguration { ColumnValidator columnValidator, DatabaseSpecificSQLGenerator sqlGenerator, DelinquencyReadPlatformService delinquencyReadPlatformService, LoanTransactionRepository loanTransactionRepository, LoanChargePaidByReadService loanChargePaidByReadService, LoanTransactionRelationReadService loanTransactionRelationReadService, - LoanForeclosureValidator loanForeclosureValidator, LoanTransactionMapper loanTransactionMapper) { + LoanForeclosureValidator loanForeclosureValidator, LoanTransactionMapper loanTransactionMapper, + LoanTransactionProcessingService loanTransactionProcessingService) { return new LoanReadPlatformServiceImpl(jdbcTemplate, context, loanRepositoryWrapper, applicationCurrencyRepository, loanProductReadPlatformService, clientReadPlatformService, groupReadPlatformService, loanDropdownReadPlatformService, fundReadPlatformService, chargeReadPlatformService, codeValueReadPlatformService, calendarReadPlatformService, @@ -334,7 +336,7 @@ public class LoanAccountConfiguration { loanRepaymentScheduleTransactionProcessorFactory, floatingRatesReadPlatformService, loanUtilService, configurationDomainService, accountDetailsReadPlatformService, columnValidator, sqlGenerator, delinquencyReadPlatformService, loanTransactionRepository, loanChargePaidByReadService, loanTransactionRelationReadService, - loanForeclosureValidator, loanTransactionMapper); + loanForeclosureValidator, loanTransactionMapper, loanTransactionProcessingService); } @Bean @@ -396,7 +398,8 @@ public class LoanAccountConfiguration { LoanDisbursementService loanDisbursementService, LoanScheduleService loanScheduleService, LoanChargeValidator loanChargeValidator, LoanOfficerService loanOfficerService, ReprocessLoanTransactionsService reprocessLoanTransactionsService, LoanAccountService loanAccountService, - LoanJournalEntryPoster journalEntryPoster, LoanAdjustmentService loanAdjustmentService) { + LoanJournalEntryPoster journalEntryPoster, LoanAdjustmentService loanAdjustmentService, + LoanTransactionProcessingService loanTransactionProcessingService) { return new LoanWritePlatformServiceJpaRepositoryImpl(context, loanTransactionValidator, loanUpdateCommandFromApiJsonDeserializer, loanRepositoryWrapper, loanAccountDomainService, noteRepository, loanTransactionRepository, loanTransactionRelationRepository, loanAssembler, journalEntryWritePlatformService, calendarInstanceRepository, @@ -410,7 +413,8 @@ public class LoanAccountConfiguration { loanAccountLockService, externalIdFactory, loanAccrualTransactionBusinessEventService, errorHandler, loanDownPaymentHandlerService, loanTransactionAssembler, loanAccrualsProcessingService, loanOfficerValidator, loanDownPaymentTransactionValidator, loanDisbursementService, loanScheduleService, loanChargeValidator, loanOfficerService, - reprocessLoanTransactionsService, loanAccountService, journalEntryPoster, loanAdjustmentService); + reprocessLoanTransactionsService, loanAccountService, journalEntryPoster, loanAdjustmentService, + loanTransactionProcessingService); } @Bean @@ -426,10 +430,11 @@ public class LoanAccountConfiguration { BusinessEventNotifierService businessEventNotifierService, LoanDownPaymentTransactionValidator loanDownPaymentTransactionValidator, LoanScheduleService loanScheduleService, LoanRefundService loanRefundService, LoanRefundValidator loanRefundValidator, - ReprocessLoanTransactionsService reprocessLoanTransactionsService) { + ReprocessLoanTransactionsService reprocessLoanTransactionsService, + LoanTransactionProcessingService loanTransactionProcessingService) { return new LoanDownPaymentHandlerServiceImpl(loanTransactionRepository, businessEventNotifierService, loanDownPaymentTransactionValidator, loanScheduleService, loanRefundService, loanRefundValidator, - reprocessLoanTransactionsService); + reprocessLoanTransactionsService, loanTransactionProcessingService); } @Bean @@ -447,15 +452,17 @@ public class LoanAccountConfiguration { @Bean @ConditionalOnMissingBean(LoanChargeService.class) - public LoanChargeService loanChargeService(LoanChargeValidator loanChargeValidator) { - return new LoanChargeService(loanChargeValidator); + public LoanChargeService loanChargeService(LoanChargeValidator loanChargeValidator, + LoanTransactionProcessingService loanTransactionProcessingService) { + return new LoanChargeService(loanChargeValidator, loanTransactionProcessingService); } @Bean @ConditionalOnMissingBean(LoanScheduleService.class) public LoanScheduleService loanScheduleService(LoanChargeService loanChargeService, - ReprocessLoanTransactionsService reprocessLoanTransactionsService) { - return new LoanScheduleService(loanChargeService, reprocessLoanTransactionsService); + ReprocessLoanTransactionsService reprocessLoanTransactionsService, + LoanTransactionProcessingService loanTransactionProcessingService) { + return new LoanScheduleService(loanChargeService, reprocessLoanTransactionsService, loanTransactionProcessingService); } @Bean @@ -466,8 +473,9 @@ public class LoanAccountConfiguration { @Bean @ConditionalOnMissingBean(LoanRefundService.class) - public LoanRefundService loanRefundService(LoanRefundValidator loanRefundValidator) { - return new LoanRefundService(loanRefundValidator); + public LoanRefundService loanRefundService(LoanRefundValidator loanRefundValidator, + LoanTransactionProcessingService loanTransactionProcessingService) { + return new LoanRefundService(loanRefundValidator, loanTransactionProcessingService); } @Bean diff --git a/fineract-provider/src/test/java/org/apache/fineract/portfolio/loanaccount/service/LoanDownPaymentHandlerServiceImplTest.java b/fineract-provider/src/test/java/org/apache/fineract/portfolio/loanaccount/service/LoanDownPaymentHandlerServiceImplTest.java index ade7b28df..93f4284de 100644 --- a/fineract-provider/src/test/java/org/apache/fineract/portfolio/loanaccount/service/LoanDownPaymentHandlerServiceImplTest.java +++ b/fineract-provider/src/test/java/org/apache/fineract/portfolio/loanaccount/service/LoanDownPaymentHandlerServiceImplTest.java @@ -104,13 +104,16 @@ public class LoanDownPaymentHandlerServiceImplTest { @Mock private ReprocessLoanTransactionsService reprocessLoanTransactionsService; + @Mock + private LoanTransactionProcessingService loanTransactionProcessingService; + private LoanDownPaymentHandlerServiceImpl underTest; @BeforeEach public void setUp() { underTest = new LoanDownPaymentHandlerServiceImpl(loanTransactionRepository, businessEventNotifierService, loanDownPaymentTransactionValidator, loanScheduleService, loanRefundService, loanRefundValidator, - reprocessLoanTransactionsService); + reprocessLoanTransactionsService, loanTransactionProcessingService); moneyHelper.when(MoneyHelper::getMathContext).thenReturn(new MathContext(12, RoundingMode.UP)); moneyHelper.when(MoneyHelper::getRoundingMode).thenReturn(RoundingMode.UP); tempConfigServiceMock.when(TemporaryConfigurationServiceContainer::isExternalIdAutoGenerationEnabled).thenReturn(true); @@ -156,8 +159,7 @@ public class LoanDownPaymentHandlerServiceImplTest { when(loanForProcessing.getLoanProduct()).thenReturn(loanProduct); when(loanProduct.getInstallmentAmountInMultiplesOf()).thenReturn(10); when(loanForProcessing.getLoanProductRelatedDetail()).thenReturn(loanProductRelatedDetail); - when(loanForProcessing.getTransactionProcessor()).thenReturn(loanRepaymentScheduleTransactionProcessor); - when(loanRepaymentScheduleTransactionProcessor.reprocessLoanTransactions(any(), any(), any(), any(), any())) + when(loanTransactionProcessingService.reprocessLoanTransactions(any(), any(), any(), any(), any(), any())) .thenReturn(changedTransactionDetail); when(loanProductRelatedDetail.getLoanScheduleType()).thenReturn(LoanScheduleType.PROGRESSIVE);