FINERACT-2413: Allow multiple Re-amortization on a loan account

This commit is contained in:
Soma Sörös
2026-02-02 18:59:07 +01:00
committed by Adam Saghy
parent 01d3049538
commit 2ee19f9825
2 changed files with 28 additions and 7 deletions
@@ -150,7 +150,7 @@ public class LoanReAmortizationService {
}
private LoanScheduleData previewReAmortization(final Loan loan, final ReAmortizationPreviewRequest reAmortizationPreviewRequest) {
reAmortizationValidator.validateReAmortize(loan);
reAmortizationValidator.validateReAmortize(loan, reAmortizationPreviewRequest);
final LoanTransaction reAmortizeTransaction = createReAmortizeTransactionFromPreviewRequest(loan, reAmortizationPreviewRequest);
processReAmortizationTransaction(loan, reAmortizeTransaction, false);
@@ -34,9 +34,11 @@ import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidati
import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.portfolio.loanaccount.api.LoanApiConstants;
import org.apache.fineract.portfolio.loanaccount.api.LoanReAmortizationApiConstants;
import org.apache.fineract.portfolio.loanaccount.api.request.ReAmortizationPreviewRequest;
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
import org.apache.fineract.portfolio.loanaccount.domain.reamortization.LoanReAmortizationInterestHandlingType;
import org.apache.fineract.portfolio.loanaccount.domain.reamortization.LoanReAmortizationParameter;
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.AdvancedPaymentScheduleTransactionProcessor;
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleType;
import org.springframework.stereotype.Component;
@@ -49,11 +51,9 @@ public class LoanReAmortizationValidator {
public void validateReAmortize(Loan loan, JsonCommand command) {
validateReAmortizeRequest(command);
validateReAmortizeBusinessRules(loan);
}
public void validateReAmortize(final Loan loan) {
validateReAmortizeBusinessRules(loan);
LoanReAmortizationInterestHandlingType interestHandlingType = command.enumValueOfParameterNamed(
LoanReAmortizationApiConstants.reAmortizationInterestHandlingParamName, LoanReAmortizationInterestHandlingType.class);
validateReAmortizeBusinessRules(loan, interestHandlingType);
}
private void validateReAmortizeRequest(JsonCommand command) {
@@ -84,7 +84,7 @@ public class LoanReAmortizationValidator {
throwExceptionIfValidationErrorsExist(dataValidationErrors);
}
private void validateReAmortizeBusinessRules(Loan loan) {
private void validateReAmortizeBusinessRules(Loan loan, LoanReAmortizationInterestHandlingType interestHandlingType) {
// validate reamortization shouldn't happen after maturity
if (DateUtils.isAfter(getBusinessLocalDate(), loan.getMaturityDate())) {
throw new GeneralPlatformDomainRuleException("error.msg.loan.reamortize.cannot.be.submitted.after.maturity",
@@ -120,6 +120,22 @@ public class LoanReAmortizationValidator {
loan.getId());
}
// validate if there is active re-amortization transaction, it should have the same interest handling strategy
Optional<LoanTransaction> previousReAmortizationTransaction = loan.getLoanTransactions().stream()
.filter(LoanTransaction::isNotReversed).filter(LoanTransaction::isReAmortize).findAny();
if (previousReAmortizationTransaction.isPresent()) {
LoanReAmortizationInterestHandlingType previousInterestHandlingType = Optional
.ofNullable(previousReAmortizationTransaction.get().getLoanReAmortizationParameter())
.map(LoanReAmortizationParameter::getInterestHandlingType).orElse(LoanReAmortizationInterestHandlingType.DEFAULT);
LoanReAmortizationInterestHandlingType currentInterestHandlingType = Optional.ofNullable(interestHandlingType)
.orElse(LoanReAmortizationInterestHandlingType.DEFAULT);
if (!previousInterestHandlingType.equals(currentInterestHandlingType)) {
throw new GeneralPlatformDomainRuleException(
"error.msg.loan.reamortize.reamortize.transaction.interest.handling.strategy.missmatch",
"Previous active reamortization transactiuon has different interest handling strategy.", loan.getId());
}
}
// validate loan is not charged-off
if (loan.isChargedOff()) {
throw new GeneralPlatformDomainRuleException("error.msg.loan.reamortize.not.allowed.on.charged.off",
@@ -163,4 +179,9 @@ public class LoanReAmortizationValidator {
}
}
public void validateReAmortize(Loan loan, ReAmortizationPreviewRequest reAmortizationPreviewRequest) {
LoanReAmortizationInterestHandlingType interestHandlingType = LoanReAmortizationInterestHandlingType
.valueOf(reAmortizationPreviewRequest.getReAmortizationInterestHandling());
validateReAmortizeBusinessRules(loan, interestHandlingType);
}
}