FINERACT-2384: Incorrect reage transaction date and repayment schedule

This commit is contained in:
Soma Sörös
2025-10-01 12:12:43 +02:00
committed by Adam Saghy
parent d6323be7dd
commit d50906bef2
5 changed files with 186 additions and 84 deletions
@@ -37,6 +37,7 @@ import org.apache.fineract.infrastructure.event.business.domain.loan.reaging.Loa
import org.apache.fineract.infrastructure.event.business.domain.loan.transaction.reaging.LoanReAgeTransactionBusinessEvent;
import org.apache.fineract.infrastructure.event.business.domain.loan.transaction.reaging.LoanUndoReAgeTransactionBusinessEvent;
import org.apache.fineract.infrastructure.event.business.service.BusinessEventNotifierService;
import org.apache.fineract.infrastructure.event.business.service.TransactionHelper;
import org.apache.fineract.organisation.monetary.domain.Money;
import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType;
import org.apache.fineract.portfolio.loanaccount.api.LoanReAgingApiConstants;
@@ -77,6 +78,7 @@ public class LoanReAgingServiceImpl {
private final LoanUtilService loanUtilService;
private final LoanScheduleService loanScheduleService;
private final ReprocessLoanTransactionsService reprocessLoanTransactionsService;
private final TransactionHelper transactionHelper;
public CommandProcessingResult reAge(Long loanId, JsonCommand command) {
Loan loan = loanAssembler.assembleFrom(loanId);
@@ -89,14 +91,18 @@ public class LoanReAgingServiceImpl {
LoanTransaction reAgeTransaction = createReAgeTransaction(loan, command);
LoanReAgeParameter reAgeParameter = createReAgeParameter(reAgeTransaction, command);
reAgeTransaction.setLoanReAgeParameter(reAgeParameter);
loanTransactionRepository.saveAndFlush(reAgeTransaction);
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = loanRepaymentScheduleTransactionProcessorFactory
.determineProcessor(loan.transactionProcessingStrategy());
loanRepaymentScheduleTransactionProcessor.processLatestTransaction(reAgeTransaction, new TransactionCtx(loan.getCurrency(),
loan.getRepaymentScheduleInstallments(), loan.getActiveCharges(), new MoneyHolder(loan.getTotalOverpaidAsMoney()), null));
if (reAgeTransaction.getTransactionDate().isBefore(reAgeTransaction.getSubmittedOnDate())) {
reprocessLoanTransactionsService.reprocessTransactionsWithPostTransactionChecks(loan, reAgeTransaction.getTransactionDate());
} else {
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = loanRepaymentScheduleTransactionProcessorFactory
.determineProcessor(loan.transactionProcessingStrategy());
loanRepaymentScheduleTransactionProcessor.processLatestTransaction(reAgeTransaction,
new TransactionCtx(loan.getCurrency(), loan.getRepaymentScheduleInstallments(), loan.getActiveCharges(),
new MoneyHolder(loan.getTotalOverpaidAsMoney()), null));
}
loan.updateLoanScheduleDependentDerivedFields();
persistNote(loan, command, changes);
@@ -172,7 +178,10 @@ public class LoanReAgingServiceImpl {
// reaging transaction date is always the current business date
LocalDate transactionDate = DateUtils.getBusinessLocalDate();
LocalDate startDate = command.dateValueOfParameterNamed(LoanReAgingApiConstants.startDate);
if (transactionDate.isAfter(startDate)) {
transactionDate = startDate;
}
// in case of a reaging transaction, only the outstanding principal amount until the business date is considered
Money txPrincipal = loan.getTotalPrincipalOutstandingUntil(transactionDate);
BigDecimal txPrincipalAmount = txPrincipal.getAmount();
@@ -182,7 +191,6 @@ public class LoanReAgingServiceImpl {
}
private LoanReAgeParameter createReAgeParameter(LoanTransaction reAgeTransaction, JsonCommand command) {
// TODO: these parameters should be checked when the validations are implemented
PeriodFrequencyType periodFrequencyType = command.enumValueOfParameterNamed(LoanReAgingApiConstants.frequencyType,
PeriodFrequencyType.class);
LocalDate startDate = command.dateValueOfParameterNamed(LoanReAgingApiConstants.startDate);
@@ -62,9 +62,13 @@ public class LoanReAgingValidator {
.notExceedingLengthOf(100);
LocalDate startDate = command.localDateValueOfParameterNamed(LoanReAgingApiConstants.startDate);
baseDataValidator.reset().parameter(LoanReAgingApiConstants.startDate).value(startDate).notNull()
.validateDateAfter(loan.getMaturityDate());
if (loan.isProgressiveSchedule() && !loan.isInterestBearing()) {
baseDataValidator.reset().parameter(LoanReAgingApiConstants.startDate).value(startDate).notNull()
.validateDateAfterOrEqual(loan.getDisbursementDate());
} else {
baseDataValidator.reset().parameter(LoanReAgingApiConstants.startDate).value(startDate).notNull()
.validateDateAfter(loan.getMaturityDate());
}
String frequencyType = command.stringValueOfParameterNamedAllowingNull(LoanReAgingApiConstants.frequencyType);
baseDataValidator.reset().parameter(LoanReAgingApiConstants.frequencyType).value(frequencyType).notNull();
@@ -81,7 +85,9 @@ public class LoanReAgingValidator {
private void validateReAgeBusinessRules(Loan loan) {
// validate reaging shouldn't happen before maturity
if (DateUtils.isBefore(getBusinessLocalDate(), loan.getMaturityDate())) {
// on progressive loans it can
if (!(loan.isProgressiveSchedule() && !loan.isInterestBearing())
&& DateUtils.isBefore(getBusinessLocalDate(), loan.getMaturityDate())) {
throw new GeneralPlatformDomainRuleException("error.msg.loan.reage.cannot.be.submitted.before.maturity",
"Loan cannot be re-aged before maturity", loan.getId());
}