FINERACT-2326: Add the ability to reschedule non-interest-bearing progressive loans to have interest

This commit is contained in:
mark.vituska
2025-08-04 14:03:00 +02:00
committed by Adam Saghy
parent 6a67e58a3a
commit 3428e33035
14 changed files with 171 additions and 271 deletions
@@ -95,16 +95,18 @@ public class LoanTransactionProcessingServiceImpl implements LoanTransactionProc
AdvancedPaymentScheduleTransactionProcessor advancedProcessor, Loan loan, LoanTransaction loanTransaction) {
Optional<ProgressiveLoanInterestScheduleModel> savedModel = modelRepository.getSavedModel(loan,
loanTransaction.getTransactionDate());
ProgressiveLoanInterestScheduleModel model = savedModel
.orElseGet(() -> advancedProcessor.calculateInterestScheduleModel(loan.getId(), loanTransaction.getTransactionDate()));
ProgressiveTransactionCtx progressiveContext = new ProgressiveTransactionCtx(loan.getCurrency(),
loan.getRepaymentScheduleInstallments(), loan.getActiveCharges(), new MoneyHolder(loan.getTotalOverpaidAsMoney()),
new ChangedTransactionDetail(), savedModel.orElse(null), getTotalRefundInterestAmount(loan));
new ChangedTransactionDetail(), model, getTotalRefundInterestAmount(loan));
progressiveContext.getAlreadyProcessedTransactions().addAll(loanTransactionService.retrieveListOfTransactionsForReprocessing(loan));
progressiveContext.setChargedOff(loan.isChargedOff());
progressiveContext.setContractTerminated(loan.isContractTermination());
ChangedTransactionDetail result = advancedProcessor.processLatestTransaction(loanTransaction, progressiveContext);
if (savedModel.isPresent() && !TransactionSynchronizationManager.isCurrentTransactionReadOnly()) {
modelRepository.writeInterestScheduleModel(loan, savedModel.get());
if (!TransactionSynchronizationManager.isCurrentTransactionReadOnly()) {
modelRepository.writeInterestScheduleModel(loan, model);
}
return result;
}
@@ -124,7 +126,7 @@ public class LoanTransactionProcessingServiceImpl implements LoanTransactionProc
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = getTransactionProcessor(
transactionProcessingStrategyCode);
if (loanRepaymentScheduleTransactionProcessor instanceof AdvancedPaymentScheduleTransactionProcessor advancedProcessor
&& loanTransaction.getLoan().isInterestBearingAndInterestRecalculationEnabled()) {
&& loanTransaction.getLoan().isInterestRecalculationEnabled()) {
return processLatestTransactionProgressiveInterestRecalculation(advancedProcessor, loanTransaction.getLoan(), loanTransaction);
}
return loanRepaymentScheduleTransactionProcessor.processLatestTransaction(loanTransaction, ctx);
@@ -36,8 +36,11 @@ import org.apache.fineract.portfolio.loanaccount.domain.LoanChargePaidBy;
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleProcessingWrapper;
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
import org.apache.fineract.portfolio.loanaccount.domain.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.domain.transactionprocessor.impl.AdvancedPaymentScheduleTransactionProcessor;
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.ProgressiveTransactionCtx;
import org.apache.fineract.portfolio.loanproduct.calc.data.ProgressiveLoanInterestScheduleModel;
import org.springframework.stereotype.Service;
@@ -145,10 +148,25 @@ public class ReprocessLoanTransactionsServiceImpl implements ReprocessLoanTransa
@Override
public void processLatestTransaction(final LoanTransaction loanTransaction, final Loan loan) {
final ChangedTransactionDetail changedTransactionDetail = loanTransactionProcessingService.processLatestTransaction(
loan.getTransactionProcessingStrategyCode(), loanTransaction,
new TransactionCtx(loan.getCurrency(), loan.getRepaymentScheduleInstallments(), loan.getActiveCharges(),
new MoneyHolder(loan.getTotalOverpaidAsMoney()), new ChangedTransactionDetail()));
LoanRepaymentScheduleTransactionProcessor transactionProcessor = loanTransactionProcessingService
.getTransactionProcessor(loan.getTransactionProcessingStrategyCode());
TransactionCtx transactionCtx;
if (transactionProcessor instanceof AdvancedPaymentScheduleTransactionProcessor advancedProcessor) {
Optional<ProgressiveLoanInterestScheduleModel> savedModel = interestScheduleModelRepositoryWrapper.getSavedModel(loan,
loanTransaction.getTransactionDate());
ProgressiveLoanInterestScheduleModel model = savedModel
.orElseGet(() -> advancedProcessor.calculateInterestScheduleModel(loan.getId(), loanTransaction.getTransactionDate()));
transactionCtx = new ProgressiveTransactionCtx(loan.getCurrency(), loan.getRepaymentScheduleInstallments(),
loan.getActiveCharges(), new MoneyHolder(loan.getTotalOverpaidAsMoney()), new ChangedTransactionDetail(), model);
} else {
transactionCtx = new TransactionCtx(loan.getCurrency(), loan.getRepaymentScheduleInstallments(), loan.getActiveCharges(),
new MoneyHolder(loan.getTotalOverpaidAsMoney()), new ChangedTransactionDetail());
}
final ChangedTransactionDetail changedTransactionDetail = loanTransactionProcessingService
.processLatestTransaction(loan.getTransactionProcessingStrategyCode(), loanTransaction, transactionCtx);
final List<LoanTransaction> newTransactions = changedTransactionDetail.getTransactionChanges().stream()
.map(TransactionChangeData::getNewTransaction).peek(transaction -> transaction.updateLoan(loan)).toList();
loan.getLoanTransactions().addAll(newTransactions);