FINERACT-2148: Backdated Charge-off with interest recalculation

This commit is contained in:
Adam Saghy
2024-12-06 16:49:55 +01:00
parent a2c01d25e5
commit 3490219577
7 changed files with 102 additions and 37 deletions
@@ -36,7 +36,7 @@ public class LoanInterestRecalculationCOBBusinessStep implements LoanCOBBusiness
@Override
public Loan execute(Loan loan) {
if (!loan.isInterestBearing() || !loan.getStatus().isActive() || loan.isNpa() || loan.isChargedOff()
|| !loan.isInterestRecalculationEnabledForProduct()
|| !loan.isInterestRecalculationEnabled()
|| loan.getLoanInterestRecalculationDetails().disallowInterestCalculationOnPastDue()) {
log.debug(
"Skip processing loan interest recalculation [{}] - Possible reasons: Loan is not an interest bearing loan, Loan is not active, Interest recalculation on past due is disabled on this loan",
@@ -28,7 +28,7 @@ import org.springframework.stereotype.Component;
public final class LoanForeclosureValidator {
public void validateForForeclosure(final Loan loan, final LocalDate transactionDate) {
if (loan.isInterestRecalculationEnabledForProduct()) {
if (loan.isInterestRecalculationEnabled()) {
final String defaultUserMessage = "The loan with interest recalculation enabled cannot be foreclosed.";
throw new LoanForeclosureException("loan.with.interest.recalculation.enabled.cannot.be.foreclosured", defaultUserMessage,
loan.getId());
@@ -3261,8 +3261,26 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
final List<Long> existingReversedTransactionIds = loan.findExistingReversedTransactionIds();
LoanTransaction chargeOffTransaction = LoanTransaction.chargeOff(loan, transactionDate, txnExternalId);
if (loan.isInterestBearing() && loan.isInterestRecalculationEnabled()
&& DateUtils.isBefore(loan.getInterestRecalculatedOn(), DateUtils.getBusinessLocalDate())) {
final ScheduleGeneratorDTO scheduleGeneratorDTO = this.loanUtilService.buildScheduleGeneratorDTO(loan, null, null);
loanScheduleService.regenerateRepaymentScheduleWithInterestRecalculation(loan, scheduleGeneratorDTO);
loan.addLoanTransaction(chargeOffTransaction);
ChangedTransactionDetail changedTransactionDetail = loan.reprocessTransactions();
if (changedTransactionDetail != null) {
for (final Map.Entry<Long, LoanTransaction> mapEntry : changedTransactionDetail.getNewTransactionMappings().entrySet()) {
loanAccountDomainService.saveLoanTransactionWithDataIntegrityViolationChecks(mapEntry.getValue());
accountTransfersWritePlatformService.updateLoanTransaction(mapEntry.getKey(), mapEntry.getValue());
}
// Trigger transaction replayed event
replayedTransactionBusinessEventService.raiseTransactionReplayedEvents(changedTransactionDetail);
}
} else {
loan.addLoanTransaction(chargeOffTransaction);
}
loanTransactionRepository.saveAndFlush(chargeOffTransaction);
loan.addLoanTransaction(chargeOffTransaction);
saveAndFlushLoanWithDataIntegrityViolationChecks(loan);
String noteText = command.stringValueOfParameterNamed(LoanApiConstants.noteParameterName);
@@ -23,6 +23,7 @@ import java.time.LocalDate;
import java.util.Collection;
import java.util.List;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.fineract.organisation.monetary.data.CurrencyData;
import org.apache.fineract.portfolio.loanaccount.data.LoanSummaryData;
@@ -42,6 +43,7 @@ import org.springframework.stereotype.Component;
@Component
@AllArgsConstructor
@Slf4j
public class ProgressiveLoanSummaryDataProvider extends CommonLoanSummaryDataProvider {
private final AdvancedPaymentScheduleTransactionProcessor advancedPaymentScheduleTransactionProcessor;
@@ -89,7 +91,10 @@ public class ProgressiveLoanSummaryDataProvider extends CommonLoanSummaryDataPro
ProgressiveLoanInterestScheduleModel model = changedTransactionDetailProgressiveLoanInterestScheduleModelPair.getRight();
if (!changedTransactionDetailProgressiveLoanInterestScheduleModelPair.getLeft().getCurrentTransactionToOldId().isEmpty()
|| !changedTransactionDetailProgressiveLoanInterestScheduleModelPair.getLeft().getNewTransactionMappings().isEmpty()) {
throw new RuntimeException("Transactions should not be reverse replayed!");
List<Long> replayedTransactions = changedTransactionDetailProgressiveLoanInterestScheduleModelPair.getLeft()
.getNewTransactionMappings().keySet().stream().toList();
log.warn("Reprocessed transactions show differences: There are unsaved changes of the following transactions: {}",
replayedTransactions);
}
if (model != null) {
PeriodDueDetails dueAmounts = emiCalculator.getDueAmounts(model, loanRepaymentScheduleInstallment.getDueDate(),