FINERACT-2354: In case full transaction reprocessing, we need to recalculate the schedule as well

This commit is contained in:
Adam Saghy
2025-11-13 08:44:22 +01:00
parent 051571003e
commit c79c076ee6
39 changed files with 361 additions and 479 deletions
@@ -51,11 +51,10 @@ public class ProgressivePossibleNextRepaymentCalculationServiceImpl extends Abst
Optional<ProgressiveLoanModel> progressiveLoanModel = interestScheduleModelRepository.findOneByLoan(loan);
Optional<ProgressiveLoanInterestScheduleModel> optionalScheduleModel = interestScheduleModelRepository
.extractModel(progressiveLoanModel);
ProgressiveLoanInterestScheduleModel scheduleModel = optionalScheduleModel.orElseGet(
() -> advancedPaymentScheduleTransactionProcessor.calculateInterestScheduleModel(loan.getId(), nextPaymentDueDate));
if (scheduleModel == null) {
if (optionalScheduleModel.isEmpty()) {
return BigDecimal.ZERO;
}
ProgressiveLoanInterestScheduleModel scheduleModel = optionalScheduleModel.get();
List<LoanRepaymentScheduleInstallment> repaymentScheduleInstallments = loan.getRepaymentScheduleInstallments();
ProgressiveTransactionCtx ctx = new ProgressiveTransactionCtx(loan.getCurrency(), repaymentScheduleInstallments, Set.of(),
new MoneyHolder(loan.getTotalOverpaidAsMoney()), new ChangedTransactionDetail(), scheduleModel);
@@ -84,14 +84,12 @@ import org.apache.fineract.portfolio.loanaccount.domain.LoanInterestRecalculatio
import org.apache.fineract.portfolio.loanaccount.domain.LoanPaymentAllocationRule;
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleProcessingWrapper;
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepositoryWrapper;
import org.apache.fineract.portfolio.loanaccount.domain.LoanTermVariationType;
import org.apache.fineract.portfolio.loanaccount.domain.LoanTermVariations;
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.SingleLoanChargeRepaymentScheduleProcessingWrapper;
import org.apache.fineract.portfolio.loanaccount.domain.reaging.LoanReAgeParameter;
@@ -119,8 +117,6 @@ import org.apache.fineract.portfolio.loanproduct.domain.LoanProductRelatedDetail
import org.apache.fineract.portfolio.loanproduct.domain.PaymentAllocationType;
import org.apache.fineract.util.LoopContext;
import org.apache.fineract.util.LoopGuard;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
@Slf4j
public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRepaymentScheduleTransactionProcessor {
@@ -129,23 +125,18 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
public static final String ADVANCED_PAYMENT_ALLOCATION_STRATEGY_NAME = "Advanced payment allocation strategy";
private final EMICalculator emiCalculator;
private final LoanRepositoryWrapper loanRepositoryWrapper;
private final InterestRefundService interestRefundService;
private final LoanScheduleComponent loanSchedule;
private final LoanTransactionRepository loanTransactionRepository;
private final LoanChargeService loanChargeService;
private final SingleLoanChargeRepaymentScheduleProcessingWrapper loanChargeRepaymentScheduleProcessing;
public AdvancedPaymentScheduleTransactionProcessor(final EMICalculator emiCalculator, final LoanRepositoryWrapper loanRepositoryWrapper,
final InterestRefundService interestRefundService, final ExternalIdFactory externalIdFactory,
final LoanScheduleComponent loanSchedule, final LoanTransactionRepository loanTransactionRepository,
public AdvancedPaymentScheduleTransactionProcessor(final EMICalculator emiCalculator, final InterestRefundService interestRefundService,
final ExternalIdFactory externalIdFactory, final LoanScheduleComponent loanSchedule,
final LoanChargeValidator loanChargeValidator, final LoanBalanceService loanBalanceService,
final LoanChargeService loanChargeService) {
super(externalIdFactory, loanChargeValidator, loanBalanceService);
this.emiCalculator = emiCalculator;
this.loanRepositoryWrapper = loanRepositoryWrapper;
this.interestRefundService = interestRefundService;
this.loanTransactionRepository = loanTransactionRepository;
this.loanSchedule = loanSchedule;
this.loanChargeService = loanChargeService;
this.loanChargeRepaymentScheduleProcessing = new SingleLoanChargeRepaymentScheduleProcessingWrapper();
@@ -195,13 +186,6 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
throw new NotImplementedException();
}
@Transactional
public Pair<ChangedTransactionDetail, ProgressiveLoanInterestScheduleModel> reprocessProgressiveLoanTransactionsTransactional(
final LocalDate disbursementDate, final LocalDate targetDate, final List<LoanTransaction> loanTransactions,
final MonetaryCurrency currency, final List<LoanRepaymentScheduleInstallment> installments, final Set<LoanCharge> charges) {
return reprocessProgressiveLoanTransactions(disbursementDate, targetDate, loanTransactions, currency, installments, charges);
}
// only for progressive loans
public Pair<ChangedTransactionDetail, ProgressiveLoanInterestScheduleModel> reprocessProgressiveLoanTransactions(
LocalDate disbursementDate, LocalDate targetDate, List<LoanTransaction> loanTransactions, MonetaryCurrency currency,
@@ -302,18 +286,6 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
return result.getLeft();
}
@NotNull
@Transactional(propagation = Propagation.REQUIRES_NEW, readOnly = true)
public ProgressiveLoanInterestScheduleModel calculateInterestScheduleModel(@NotNull Long loanId, LocalDate targetDate) {
Loan loan = loanRepositoryWrapper.findOneWithNotFoundDetection(loanId);
final List<LoanTransaction> transactions = loanTransactionRepository.findNonReversedTransactionsForReprocessingByLoan(loan);
MonetaryCurrency currency = loan.getLoanRepaymentScheduleDetail().getCurrency();
List<LoanRepaymentScheduleInstallment> installments = loan.getRepaymentScheduleInstallments();
Set<LoanCharge> charges = loan.getActiveCharges();
return reprocessProgressiveLoanTransactions(loan.getDisbursementDate(), targetDate, transactions, currency, installments, charges)
.getRight();
}
@NotNull
private static LoanTransaction getProcessedTransaction(final ChangedTransactionDetail changedTransactionDetail,
final LoanTransaction transaction) {
@@ -2937,9 +2909,8 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
private void updateInstallmentsByModelForReAging(final LoanTransaction loanTransaction, final ProgressiveTransactionCtx ctx) {
ctx.getModel().repaymentPeriods().forEach(rp -> {
final LoanRepaymentScheduleInstallment installment = ctx.getInstallments().stream().filter(
ri -> ri.getFromDate().equals(rp.getFromDate()) && ri.getDueDate().equals(rp.getDueDate()) && !ri.isDownPayment())
.findFirst().orElse(null);
final LoanRepaymentScheduleInstallment installment = ctx.getInstallments().stream()
.filter(ri -> ri.getFromDate().equals(rp.getFromDate()) && !ri.isDownPayment()).findFirst().orElse(null);
final LocalDate transactionDate = loanTransaction.getTransactionDate();
if (installment != null) {
installment.setFromDate(rp.getFromDate());
@@ -2953,6 +2924,7 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
installment.updateInterestCharged(rp.getDueInterest().getAmount());
}
installment.setReAged(true);
installment.setAdditional(false);
installment.updateObligationsMet(ctx.getCurrency(), transactionDate);
} else {
final LoanRepaymentScheduleInstallment lastInstallment = ctx.getInstallments().getLast();
@@ -3247,7 +3219,7 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
final BigDecimal interestRate = loan.getLoanRepaymentScheduleDetail().getAnnualNominalInterestRate();
final Money totalOutstandingPrincipal = ctx.getModel().getTotalOutstandingPrincipal();
final LoanApplicationTerms loanApplicationTerms = new LoanApplicationTerms.Builder().currency(currency.getCurrencyData())
final LoanApplicationTerms loanApplicationTerms = new LoanApplicationTerms.Builder().currency(currency.toData())
.repaymentsStartingFromDate(reAgingStartDate).principal(totalOutstandingPrincipal)
.loanTermFrequency(loanTransaction.getLoanReAgeParameter().getNumberOfInstallments())
.loanTermPeriodFrequencyType(loanTransaction.getLoanReAgeParameter().getFrequencyType())
@@ -202,8 +202,7 @@ public class ProgressiveLoanScheduleGenerator implements LoanScheduleGenerator {
Optional<ProgressiveLoanInterestScheduleModel> savedModel = interestScheduleModelRepositoryWrapper.getSavedModel(loan,
transactionDate);
ProgressiveLoanInterestScheduleModel model = savedModel
.orElseGet(() -> processor.calculateInterestScheduleModel(loan.getId(), transactionDate));
ProgressiveLoanInterestScheduleModel model = savedModel.orElseThrow();
OutstandingDetails outstandingAmounts = emiCalculator.getOutstandingAmountsTillDate(model, transactionDate);
// TODO: We should add all the past due outstanding amounts as well
OutstandingAmountsDTO result = new OutstandingAmountsDTO(currency) //
@@ -245,8 +244,7 @@ public class ProgressiveLoanScheduleGenerator implements LoanScheduleGenerator {
return Money.zero(loan.getCurrency());
}
Optional<ProgressiveLoanInterestScheduleModel> savedModel = interestScheduleModelRepositoryWrapper.getSavedModel(loan, targetDate);
ProgressiveLoanInterestScheduleModel model = savedModel
.orElseGet(() -> processor.calculateInterestScheduleModel(loan.getId(), targetDate));
ProgressiveLoanInterestScheduleModel model = savedModel.orElseThrow();
return emiCalculator.getPeriodInterestTillDate(model, installment.getDueDate(), targetDate, false);
}
@@ -438,7 +436,8 @@ public class ProgressiveLoanScheduleGenerator implements LoanScheduleGenerator {
} else {
amount = amount.add(principalInterestForThisPeriod.principal().getAmount());
}
BigDecimal loanChargeAmt = amount.multiply(loanCharge.getPercentage()).divide(BigDecimal.valueOf(100), mc);
Money loanChargeAmt = Money.of(cumulative.getCurrency(),
amount.multiply(loanCharge.getPercentage()).divide(BigDecimal.valueOf(100), mc));
cumulative = cumulative.plus(loanChargeAmt);
} else {
cumulative = cumulative.plus(loanCharge.amountOrPercentage());
@@ -456,7 +455,8 @@ public class ProgressiveLoanScheduleGenerator implements LoanScheduleGenerator {
} else {
amount = amount.add(principalDisbursed.getAmount());
}
BigDecimal loanChargeAmt = amount.multiply(loanCharge.getPercentage()).divide(BigDecimal.valueOf(100), mc);
Money loanChargeAmt = Money.of(cumulative.getCurrency(),
amount.multiply(loanCharge.getPercentage()).divide(BigDecimal.valueOf(100), mc));
cumulative = cumulative.plus(loanChargeAmt);
return cumulative;
}
@@ -1,3 +1,21 @@
/**
* 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.mapper;
import org.apache.fineract.organisation.monetary.data.CurrencyData;
@@ -7,7 +25,9 @@ import org.apache.fineract.portfolio.common.domain.DaysInYearType;
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
import org.apache.fineract.portfolio.loanproduct.data.LoanConfigurationDetails;
import org.apache.fineract.portfolio.loanproduct.domain.ILoanConfigurationDetails;
import org.apache.fineract.portfolio.loanproduct.domain.LoanPreCloseInterestCalculationStrategy;
import org.apache.fineract.portfolio.loanproduct.domain.LoanProductRelatedDetail;
import org.apache.fineract.portfolio.loanproduct.domain.RecalculationFrequencyType;
public final class LoanConfigurationDetailsMapper {
@@ -36,7 +56,22 @@ public final class LoanConfigurationDetailsMapper {
loanProductRelatedDetail.getRepaymentPeriodFrequencyType(), loanProductRelatedDetail.getRepayEvery(),
loanProductRelatedDetail.getNumberOfRepayments(), loanProductRelatedDetail.isInterestRecognitionOnDisbursementDate(),
loanProductRelatedDetail.getDaysInYearCustomStrategy(), loanProductRelatedDetail.isAllowPartialPeriodInterestCalculation(),
loan.isInterestRecalculationEnabled(), loan.getLoanInterestRecalculationDetails().getRestFrequencyType(),
loan.getLoanInterestRecalculationDetails().getPreCloseInterestCalculationStrategy());
loan.isInterestRecalculationEnabled(), getRestFrequencyType(loan), getPreCloseInterestCalculationStrategy(loan));
}
private static RecalculationFrequencyType getRestFrequencyType(Loan loan) {
if (loan.getLoanInterestRecalculationDetails() != null) {
return loan.getLoanInterestRecalculationDetails().getRestFrequencyType();
} else {
return RecalculationFrequencyType.INVALID;
}
}
private static LoanPreCloseInterestCalculationStrategy getPreCloseInterestCalculationStrategy(Loan loan) {
if (loan.getLoanInterestRecalculationDetails() != null) {
return loan.getLoanInterestRecalculationDetails().getPreCloseInterestCalculationStrategy();
} else {
return LoanPreCloseInterestCalculationStrategy.NONE;
}
}
}
@@ -21,6 +21,7 @@ package org.apache.fineract.portfolio.loanaccount.service;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import lombok.RequiredArgsConstructor;
@@ -69,6 +70,7 @@ public class CapitalizedIncomeWritePlatformServiceImpl implements CapitalizedInc
private final LoanLifecycleStateMachine loanLifecycleStateMachine;
private final BusinessEventNotifierService businessEventNotifierService;
private final CodeValueRepository codeValueRepository;
private final LoanScheduleService loanScheduleService;
@Transactional
@Override
@@ -87,13 +89,13 @@ public class CapitalizedIncomeWritePlatformServiceImpl implements CapitalizedInc
final Money capitalizedIncomeAmount = calculateCapitalizedIncomeAmount(loan, transactionAmount);
final LoanTransaction capitalizedIncomeTransaction = LoanTransaction.capitalizedIncome(loan, capitalizedIncomeAmount, paymentDetail,
transactionDate, txnExternalId);
// Update loan with capitalized income
loan.addLoanTransaction(capitalizedIncomeTransaction);
// Add Loan Transaction classification
addClassificationCodeToTransaction(command, LoanTransactionApiConstants.CAPITALIZED_INCOME_CLASSIFICATION_CODE,
capitalizedIncomeTransaction);
// Recalculate loan transactions
recalculateLoanTransactions(loan, transactionDate, capitalizedIncomeTransaction);
recalculateLoanTransactions(loan, capitalizedIncomeTransaction);
// Update loan with capitalized income
loan.addLoanTransaction(capitalizedIncomeTransaction);
// Save and flush (PK is set)
loanTransactionRepository.saveAndFlush(capitalizedIncomeTransaction);
// Create capitalized income balances
@@ -144,8 +146,8 @@ public class CapitalizedIncomeWritePlatformServiceImpl implements CapitalizedInc
capitalizedIncomeAdjustment.getLoanTransactionRelations().add(LoanTransactionRelation.linkToTransaction(capitalizedIncomeAdjustment,
capitalizedIncome.get(), LoanTransactionRelationTypeEnum.ADJUSTMENT));
capitalizedIncomeAdjustment.setClassification(capitalizedIncome.get().getClassification());
recalculateLoanTransactions(loan, capitalizedIncomeAdjustment);
loan.addLoanTransaction(capitalizedIncomeAdjustment);
recalculateLoanTransactions(loan, transactionDate, capitalizedIncomeAdjustment);
LoanTransaction savedCapitalizedIncomeAdjustment = loanTransactionRepository.saveAndFlush(capitalizedIncomeAdjustment);
// Update outstanding loan balances
@@ -182,9 +184,10 @@ public class CapitalizedIncomeWritePlatformServiceImpl implements CapitalizedInc
.build();
}
private void recalculateLoanTransactions(Loan loan, LocalDate transactionDate, LoanTransaction transaction) {
if (loan.isInterestRecalculationEnabled() || DateUtils.isBeforeBusinessDate(transactionDate)) {
reprocessLoanTransactionsService.reprocessTransactions(loan);
private void recalculateLoanTransactions(Loan loan, LoanTransaction transaction) {
if (loan.isInterestRecalculationEnabled() || DateUtils.isBeforeBusinessDate(transaction.getTransactionDate())) {
loanScheduleService.regenerateRepaymentSchedule(loan);
reprocessLoanTransactionsService.reprocessTransactions(loan, List.of(transaction));
} else {
reprocessLoanTransactionsService.processLatestTransaction(transaction, loan);
}
@@ -108,7 +108,7 @@ public class InterestScheduleModelRepositoryWrapperImpl implements InterestSched
public Optional<ProgressiveLoanInterestScheduleModel> getSavedModel(Loan loan, LocalDate businessDate) {
Optional<ProgressiveLoanModel> progressiveLoanModel = findOneByLoanId(loan.getId());
Optional<ProgressiveLoanInterestScheduleModel> savedModel;
if (progressiveLoanModel.isPresent() && !progressiveLoanModel.get().getBusinessDate().isAfter(businessDate)) {
if (progressiveLoanModel.isPresent()) {
savedModel = extractModel(progressiveLoanModel);
if (savedModel.isPresent() && progressiveLoanModel.get().getBusinessDate().isBefore(businessDate)) {
ProgressiveTransactionCtx ctx = new ProgressiveTransactionCtx(loan.getCurrency(), loan.getRepaymentScheduleInstallments(),
@@ -28,26 +28,18 @@ import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;
import java.time.LocalDate;
import java.util.List;
import java.util.Objects;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.fineract.infrastructure.core.boot.FineractProfiles;
import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil;
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.loanaccount.mapper.LoanConfigurationDetailsMapper;
import org.apache.fineract.portfolio.loanproduct.calc.data.ProgressiveLoanInterestScheduleModel;
import org.apache.fineract.portfolio.loanproduct.domain.ILoanConfigurationDetails;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
@Slf4j
@RequiredArgsConstructor
@@ -58,9 +50,9 @@ import org.springframework.stereotype.Component;
public class InternalProgressiveLoanApiResource implements InitializingBean {
private final LoanRepositoryWrapper loanRepository;
private final AdvancedPaymentScheduleTransactionProcessor advancedPaymentScheduleTransactionProcessor;
private final InterestScheduleModelRepositoryWrapper writePlatformService;
private final LoanTransactionRepository loanTransactionRepository;
private final InterestScheduleModelRepositoryWrapper interestScheduleModelRepositoryWrapper;
private final LoanScheduleService loanScheduleService;
@Override
@SuppressFBWarnings("SLF4J_SIGN_ONLY_FORMAT")
@@ -89,28 +81,15 @@ public class InternalProgressiveLoanApiResource implements InitializingBean {
}
private ProgressiveLoanInterestScheduleModel reprocessTransactionsAndGetModel(final Loan loan) {
final List<LoanTransaction> transactionsToReprocess = loanTransactionRepository
.findNonReversedTransactionsForReprocessingByLoan(loan);
final LocalDate businessDate = ThreadLocalContextUtil.getBusinessDate();
final Pair<ChangedTransactionDetail, ProgressiveLoanInterestScheduleModel> changedTransactionDetailProgressiveLoanInterestScheduleModelPair = advancedPaymentScheduleTransactionProcessor
.reprocessProgressiveLoanTransactionsTransactional(loan.getDisbursementDate(), businessDate, transactionsToReprocess,
loan.getCurrency(), loan.getRepaymentScheduleInstallments(), loan.getActiveCharges());
final ProgressiveLoanInterestScheduleModel model = changedTransactionDetailProgressiveLoanInterestScheduleModelPair.getRight();
final List<Long> replayedTransactions = changedTransactionDetailProgressiveLoanInterestScheduleModelPair.getLeft()
.getTransactionChanges().stream().filter(change -> change.getOldTransaction() != null)
.map(change -> change.getNewTransaction().getId()).filter(Objects::nonNull).toList();
if (!replayedTransactions.isEmpty()) {
log.warn("Reprocessed transactions show differences: There are unsaved changes of the following transactions: {}",
replayedTransactions);
}
return model;
loanScheduleService.regenerateScheduleWithReprocessingTransactions(loan);
return interestScheduleModelRepositoryWrapper.extractModel(interestScheduleModelRepositoryWrapper.findOneByLoan(loan)).get();
}
@POST
@Path("{loanId}/model")
@Produces({ MediaType.APPLICATION_JSON })
@Operation(summary = "Update and Save ProgressiveLoanInterestScheduleModel", description = "DO NOT USE THIS IN PRODUCTION!")
@Transactional
public ProgressiveLoanInterestScheduleModel updateModel(@PathParam("loanId") @Parameter(description = "loanId") long loanId) {
Loan loan = loanRepository.findOneWithNotFoundDetection(loanId);
if (!loan.isProgressiveSchedule()) {
@@ -38,6 +38,7 @@ import org.apache.fineract.portfolio.loanaccount.service.LoanAssembler;
import org.apache.fineract.portfolio.loanaccount.service.LoanBalanceService;
import org.apache.fineract.portfolio.loanaccount.service.LoanJournalEntryPoster;
import org.apache.fineract.portfolio.loanaccount.service.LoanMaximumAmountCalculator;
import org.apache.fineract.portfolio.loanaccount.service.LoanScheduleService;
import org.apache.fineract.portfolio.loanaccount.service.ProgressiveLoanTransactionValidator;
import org.apache.fineract.portfolio.loanaccount.service.ProgressiveLoanTransactionValidatorImpl;
import org.apache.fineract.portfolio.loanaccount.service.ReprocessLoanTransactionsService;
@@ -59,11 +60,11 @@ public class ProgressiveLoanAccountConfiguration {
LoanCapitalizedIncomeBalanceRepository capitalizedIncomeBalanceRepository,
ReprocessLoanTransactionsService reprocessLoanTransactionsService, LoanBalanceService loanBalanceService,
LoanLifecycleStateMachine loanLifecycleStateMachine, BusinessEventNotifierService businessEventNotifierService,
CodeValueRepository codeValueRepository) {
CodeValueRepository codeValueRepository, LoanScheduleService loanScheduleService) {
return new CapitalizedIncomeWritePlatformServiceImpl(loanTransactionValidator, loanAssembler, loanTransactionRepository,
paymentDetailWritePlatformService, journalEntryPoster, noteWritePlatformService, externalIdFactory,
capitalizedIncomeBalanceRepository, reprocessLoanTransactionsService, loanBalanceService, loanLifecycleStateMachine,
businessEventNotifierService, codeValueRepository);
businessEventNotifierService, codeValueRepository, loanScheduleService);
}
@Bean
@@ -64,11 +64,9 @@ import org.apache.fineract.portfolio.loanaccount.domain.LoanCreditAllocationRule
import org.apache.fineract.portfolio.loanaccount.domain.LoanInterestRecalculationDetails;
import org.apache.fineract.portfolio.loanaccount.domain.LoanPaymentAllocationRule;
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.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;
@@ -120,9 +118,8 @@ class AdvancedPaymentScheduleTransactionProcessorTest {
@BeforeEach
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(LoanTransactionRepository.class), Mockito.mock(LoanChargeValidator.class),
underTest = new AdvancedPaymentScheduleTransactionProcessor(emiCalculator, Mockito.mock(InterestRefundService.class),
Mockito.mock(ExternalIdFactory.class), Mockito.mock(LoanScheduleComponent.class), Mockito.mock(LoanChargeValidator.class),
Mockito.mock(LoanBalanceService.class), Mockito.mock(LoanChargeService.class));
ThreadLocalContextUtil.setTenant(new FineractPlatformTenant(1L, "default", "Default", "Asia/Kolkata", null));