FINERACT-2338: Allow backdated interest change on progressive loans

- charge-off handling
- write-off handling
- closed loans
- external owner changes
This commit is contained in:
adam.magyari
2025-08-13 13:52:30 +02:00
committed by Adam Saghy
parent c2519c77c7
commit 6b7df527de
11 changed files with 998 additions and 37 deletions
@@ -28,6 +28,7 @@ import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@@ -40,7 +41,6 @@ import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder;
import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
import org.apache.fineract.infrastructure.core.exception.ErrorHandler;
import org.apache.fineract.infrastructure.core.exception.GeneralPlatformDomainRuleException;
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.infrastructure.event.business.domain.loan.LoanBalanceChangedBusinessEvent;
@@ -53,6 +53,7 @@ import org.apache.fineract.portfolio.loanaccount.data.AccountingBridgeDataDTO;
import org.apache.fineract.portfolio.loanaccount.data.LoanTermVariationsData;
import org.apache.fineract.portfolio.loanaccount.data.ScheduleGeneratorDTO;
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
import org.apache.fineract.portfolio.loanaccount.domain.LoanLifecycleStateMachine;
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallmentRepository;
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleTransactionProcessorFactory;
@@ -123,6 +124,7 @@ public class LoanRescheduleRequestWritePlatformServiceImpl implements LoanResche
private final LoanAccountingBridgeMapper loanAccountingBridgeMapper;
private final LoanScheduleComponent loanSchedule;
private final LoanTransactionRepository loanTransactionRepository;
private final LoanLifecycleStateMachine loanLifecycleStateMachine;
/**
* create a new instance of the LoanRescheduleRequest object from the JsonCommand object and persist
@@ -140,11 +142,6 @@ public class LoanRescheduleRequestWritePlatformServiceImpl implements LoanResche
// use the loan id to get a Loan entity object
final Loan loan = this.loanAssembler.assembleFrom(loanId);
if (loan.isChargedOff()) {
throw new GeneralPlatformDomainRuleException("error.msg.loan.is.charged.off",
"Loan: " + loanId + " reschedule installment is not allowed. Loan Account is Charged-off", loanId);
}
// validate the request in the JsonCommand object passed as
// parameter
this.loanRescheduleRequestDataValidator.validateForCreateAction(jsonCommand, loan);
@@ -457,6 +454,10 @@ public class LoanRescheduleRequestWritePlatformServiceImpl implements LoanResche
// update the status of the request
loanRescheduleRequest.approve(appUser, approvedOnDate);
Optional<LocalDate> lastTransactionDateForReprocessing = loanTransactionRepository.findLastTransactionDateForReprocessing(loan);
if (lastTransactionDateForReprocessing.isPresent()) {
loanLifecycleStateMachine.determineAndTransition(loan, lastTransactionDateForReprocessing.get());
}
loan = saveAndFlushLoanWithDataIntegrityViolationChecks(loan);
loanAccrualsProcessingService.processAccrualsOnInterestRecalculation(loan, true, false);
@@ -103,6 +103,7 @@ public class LoanTransactionProcessingServiceImpl implements LoanTransactionProc
new ChangedTransactionDetail(), model, getTotalRefundInterestAmount(loan));
progressiveContext.getAlreadyProcessedTransactions().addAll(loanTransactionService.retrieveListOfTransactionsForReprocessing(loan));
progressiveContext.setChargedOff(loan.isChargedOff());
progressiveContext.setWrittenOff(loan.isClosedWrittenOff());
progressiveContext.setContractTerminated(loan.isContractTermination());
ChangedTransactionDetail result = advancedProcessor.processLatestTransaction(loanTransaction, progressiveContext);
if (!TransactionSynchronizationManager.isCurrentTransactionReadOnly()) {