FINERACT-2081: Interest recalculation only for interest bearing loans

This commit is contained in:
Adam Saghy
2025-02-06 14:11:06 +01:00
parent 493012bf8c
commit 79c5fc1649
18 changed files with 128 additions and 123 deletions
@@ -2658,6 +2658,10 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> {
return BigDecimal.ZERO.compareTo(getLoanRepaymentScheduleDetail().getAnnualNominalInterestRate()) < 0; return BigDecimal.ZERO.compareTo(getLoanRepaymentScheduleDetail().getAnnualNominalInterestRate()) < 0;
} }
public boolean isInterestBearingAndInterestRecalculationEnabled() {
return isInterestBearing() && isInterestRecalculationEnabled();
}
public boolean isInterestRecalculationEnabled() { public boolean isInterestRecalculationEnabled() {
return this.loanRepaymentScheduleDetail.isInterestRecalculationEnabled(); return this.loanRepaymentScheduleDetail.isInterestRecalculationEnabled();
} }
@@ -2705,8 +2709,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> {
} }
public LoanScheduleDTO getRecalculatedSchedule(final ScheduleGeneratorDTO generatorDTO) { public LoanScheduleDTO getRecalculatedSchedule(final ScheduleGeneratorDTO generatorDTO) {
if (!this.repaymentScheduleDetail().isEnableDownPayment() if (!isInterestBearingAndInterestRecalculationEnabled() || isNpa || isChargedOff()) {
&& (!this.repaymentScheduleDetail().isInterestRecalculationEnabled() || isNpa || isChargedOff())) {
return null; return null;
} }
final InterestMethod interestMethod = this.loanRepaymentScheduleDetail.getInterestMethod(); final InterestMethod interestMethod = this.loanRepaymentScheduleDetail.getInterestMethod();
@@ -2726,7 +2729,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> {
public OutstandingAmountsDTO fetchPrepaymentDetail(final ScheduleGeneratorDTO scheduleGeneratorDTO, final LocalDate onDate) { public OutstandingAmountsDTO fetchPrepaymentDetail(final ScheduleGeneratorDTO scheduleGeneratorDTO, final LocalDate onDate) {
OutstandingAmountsDTO outstandingAmounts; OutstandingAmountsDTO outstandingAmounts;
if (this.loanRepaymentScheduleDetail.isInterestRecalculationEnabled() && !isChargeOffOnDate(onDate)) { if (isInterestBearingAndInterestRecalculationEnabled() && !isChargeOffOnDate(onDate)) {
final MathContext mc = MoneyHelper.getMathContext(); final MathContext mc = MoneyHelper.getMathContext();
final InterestMethod interestMethod = this.loanRepaymentScheduleDetail.getInterestMethod(); final InterestMethod interestMethod = this.loanRepaymentScheduleDetail.getInterestMethod();
@@ -250,7 +250,7 @@ public abstract class AbstractCumulativeLoanScheduleGenerator implements LoanSch
isNextRepaymentAvailable = false; isNextRepaymentAvailable = false;
} }
if (loanApplicationTerms.isInterestRecalculationEnabled()) { if (loanApplicationTerms.isInterestBearingAndInterestRecalculationEnabled()) {
populateCompoundingDatesInPeriod(scheduleParams.getPeriodStartDate(), scheduledDueDate, loanApplicationTerms, populateCompoundingDatesInPeriod(scheduleParams.getPeriodStartDate(), scheduledDueDate, loanApplicationTerms,
holidayDetailDTO, scheduleParams, loanCharges, monetaryCurrency, mc); holidayDetailDTO, scheduleParams, loanCharges, monetaryCurrency, mc);
} }
@@ -495,7 +495,7 @@ public abstract class AbstractCumulativeLoanScheduleGenerator implements LoanSch
LoanScheduleParams scheduleParams, LocalDate scheduledDueDate, ScheduleCurrentPeriodParams currentPeriodParams, LoanScheduleParams scheduleParams, LocalDate scheduledDueDate, ScheduleCurrentPeriodParams currentPeriodParams,
LoanScheduleModelPeriod installment, LocalDate lastRestDate) { LoanScheduleModelPeriod installment, LocalDate lastRestDate) {
LocalDate amountApplicableDate = installment.periodDueDate(); LocalDate amountApplicableDate = installment.periodDueDate();
if (loanApplicationTerms.isInterestRecalculationEnabled()) { if (loanApplicationTerms.isInterestBearingAndInterestRecalculationEnabled()) {
amountApplicableDate = getNextRestScheduleDate(installment.periodDueDate().minusDays(1), loanApplicationTerms, amountApplicableDate = getNextRestScheduleDate(installment.periodDueDate().minusDays(1), loanApplicationTerms,
holidayDetailDTO); holidayDetailDTO);
} }
@@ -1681,7 +1681,7 @@ public abstract class AbstractCumulativeLoanScheduleGenerator implements LoanSch
private void updateCompoundingMap(final LoanApplicationTerms loanApplicationTerms, final HolidayDetailDTO holidayDetailDTO, private void updateCompoundingMap(final LoanApplicationTerms loanApplicationTerms, final HolidayDetailDTO holidayDetailDTO,
final LoanScheduleParams params, final LocalDate lastRestDate, final LocalDate scheduledDueDate) { final LoanScheduleParams params, final LocalDate lastRestDate, final LocalDate scheduledDueDate) {
if (loanApplicationTerms.isInterestRecalculationEnabled() if (loanApplicationTerms.isInterestBearingAndInterestRecalculationEnabled()
&& loanApplicationTerms.getInterestRecalculationCompoundingMethod().isCompoundingEnabled()) { && loanApplicationTerms.getInterestRecalculationCompoundingMethod().isCompoundingEnabled()) {
final MonetaryCurrency currency = MonetaryCurrency.fromCurrencyData(params.getCurrency()); final MonetaryCurrency currency = MonetaryCurrency.fromCurrencyData(params.getCurrency());
Money totalCompoundedAmount = Money.zero(currency); Money totalCompoundedAmount = Money.zero(currency);
@@ -2186,7 +2186,7 @@ public abstract class AbstractCumulativeLoanScheduleGenerator implements LoanSch
LoanTransaction.copyTransactionProperties(loanTransaction))); LoanTransaction.copyTransactionProperties(loanTransaction)));
} }
} }
final boolean applyInterestRecalculation = loanApplicationTerms.isInterestRecalculationEnabled(); final boolean applyInterestRecalculation = loanApplicationTerms.isInterestBearingAndInterestRecalculationEnabled();
// for complete schedule generation // for complete schedule generation
LoanScheduleParams loanScheduleParams = LoanScheduleParams.createLoanScheduleParamsForCompleteUpdate(recalculationDetails, LoanScheduleParams loanScheduleParams = LoanScheduleParams.createLoanScheduleParamsForCompleteUpdate(recalculationDetails,
@@ -2239,7 +2239,7 @@ public abstract class AbstractCumulativeLoanScheduleGenerator implements LoanSch
final Map<LocalDate, Map<LocalDate, Money>> compoundingDateVariations = new HashMap<>(); final Map<LocalDate, Map<LocalDate, Money>> compoundingDateVariations = new HashMap<>();
LocalDate currentDate = DateUtils.getBusinessLocalDate(); LocalDate currentDate = DateUtils.getBusinessLocalDate();
LocalDate lastRestDate = currentDate; LocalDate lastRestDate = currentDate;
if (loanApplicationTerms.isInterestRecalculationEnabled()) { if (loanApplicationTerms.isInterestBearingAndInterestRecalculationEnabled()) {
lastRestDate = getNextRestScheduleDate(currentDate.minusDays(1), loanApplicationTerms, holidayDetailDTO); lastRestDate = getNextRestScheduleDate(currentDate.minusDays(1), loanApplicationTerms, holidayDetailDTO);
} }
LocalDate actualRepaymentDate = RepaymentStartDateType.DISBURSEMENT_DATE LocalDate actualRepaymentDate = RepaymentStartDateType.DISBURSEMENT_DATE
@@ -2412,7 +2412,7 @@ public abstract class AbstractCumulativeLoanScheduleGenerator implements LoanSch
instalmentNumber++; instalmentNumber++;
loanTermInDays = DateUtils.getExactDifferenceInDays(installment.getFromDate(), installment.getDueDate()); loanTermInDays = DateUtils.getExactDifferenceInDays(installment.getFromDate(), installment.getDueDate());
if (loanApplicationTerms.isInterestRecalculationEnabled()) { if (loanApplicationTerms.isInterestBearingAndInterestRecalculationEnabled()) {
// populates the collection with transactions till the due // populates the collection with transactions till the due
// date // date
@@ -1677,6 +1677,14 @@ public final class LoanApplicationTerms {
return LoanRescheduleStrategyMethod.REDUCE_EMI_AMOUNT; return LoanRescheduleStrategyMethod.REDUCE_EMI_AMOUNT;
} }
public boolean isInterestBearing() {
return BigDecimal.ZERO.compareTo(getAnnualNominalInterestRate()) < 0;
}
public boolean isInterestBearingAndInterestRecalculationEnabled() {
return isInterestBearing() && isInterestRecalculationEnabled();
}
public boolean isInterestRecalculationEnabled() { public boolean isInterestRecalculationEnabled() {
return this.interestRecalculationEnabled; return this.interestRecalculationEnabled;
} }
@@ -385,7 +385,7 @@ public class LoanArrearsAgingServiceImpl implements LoanArrearsAgingService {
} }
private void handleArrearsForLoan(Loan loan) { private void handleArrearsForLoan(Loan loan) {
if (loan != null && loan.isOpen() && loan.repaymentScheduleDetail().isInterestRecalculationEnabled() if (loan != null && loan.isOpen() && loan.isInterestBearingAndInterestRecalculationEnabled()
&& loan.loanProduct().isArrearsBasedOnOriginalSchedule()) { && loan.loanProduct().isArrearsBasedOnOriginalSchedule()) {
updateLoanArrearsAgeingDetailsWithOriginalSchedule(loan); updateLoanArrearsAgeingDetailsWithOriginalSchedule(loan);
} else { } else {
@@ -137,12 +137,12 @@ public class LoanDownPaymentHandlerServiceImpl implements LoanDownPaymentHandler
|| !currentInstallment.getTotalOutstanding(loan.getCurrency()).isEqualTo(loanTransaction.getAmount(loan.getCurrency())); || !currentInstallment.getTotalOutstanding(loan.getCurrency()).isEqualTo(loanTransaction.getAmount(loan.getCurrency()));
if (isTransactionChronologicallyLatest && adjustedTransaction == null if (isTransactionChronologicallyLatest && adjustedTransaction == null
&& (!reprocess || !loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) && !loan.isForeclosure()) { && (!reprocess || !loan.isInterestBearingAndInterestRecalculationEnabled()) && !loan.isForeclosure()) {
loanRepaymentScheduleTransactionProcessor.processLatestTransaction(loanTransaction, loanRepaymentScheduleTransactionProcessor.processLatestTransaction(loanTransaction,
new TransactionCtx(loan.getCurrency(), loan.getRepaymentScheduleInstallments(), loan.getActiveCharges(), new TransactionCtx(loan.getCurrency(), loan.getRepaymentScheduleInstallments(), loan.getActiveCharges(),
new MoneyHolder(loan.getTotalOverpaidAsMoney()), null)); new MoneyHolder(loan.getTotalOverpaidAsMoney()), null));
reprocess = false; reprocess = false;
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) { if (loan.isInterestBearingAndInterestRecalculationEnabled()) {
if (currentInstallment == null || currentInstallment.isNotFullyPaidOff()) { if (currentInstallment == null || currentInstallment.isNotFullyPaidOff()) {
reprocess = true; reprocess = true;
} else { } else {
@@ -155,7 +155,7 @@ public class LoanDownPaymentHandlerServiceImpl implements LoanDownPaymentHandler
} }
} }
if (reprocess) { if (reprocess) {
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled() if (loan.isInterestBearingAndInterestRecalculationEnabled()
&& !loan.getLoanProductRelatedDetail().getLoanScheduleType().equals(LoanScheduleType.PROGRESSIVE)) { && !loan.getLoanProductRelatedDetail().getLoanScheduleType().equals(LoanScheduleType.PROGRESSIVE)) {
loanScheduleService.regenerateRepaymentScheduleWithInterestRecalculation(loan, scheduleGeneratorDTO); loanScheduleService.regenerateRepaymentScheduleWithInterestRecalculation(loan, scheduleGeneratorDTO);
} else if (loan.getLoanProductRelatedDetail() != null } else if (loan.getLoanProductRelatedDetail() != null
@@ -54,7 +54,7 @@ public class LoanScheduleService {
} }
public void recalculateScheduleFromLastTransaction(final Loan loan, final ScheduleGeneratorDTO generatorDTO) { public void recalculateScheduleFromLastTransaction(final Loan loan, final ScheduleGeneratorDTO generatorDTO) {
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled() && !loan.isChargedOff()) { if (loan.isInterestBearingAndInterestRecalculationEnabled() && !loan.isChargedOff()) {
regenerateRepaymentScheduleWithInterestRecalculation(loan, generatorDTO); regenerateRepaymentScheduleWithInterestRecalculation(loan, generatorDTO);
} else { } else {
regenerateRepaymentSchedule(loan, generatorDTO); regenerateRepaymentSchedule(loan, generatorDTO);
@@ -73,7 +73,7 @@ public class LoanScheduleService {
* loanTransaction.getTransactionDate().isAfter(recalculateFrom)) { recalculateFrom = * loanTransaction.getTransactionDate().isAfter(recalculateFrom)) { recalculateFrom =
* loanTransaction.getTransactionDate(); } } generatorDTO.setRecalculateFrom(recalculateFrom); * loanTransaction.getTransactionDate(); } } generatorDTO.setRecalculateFrom(recalculateFrom);
*/ */
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled() && !loan.isChargedOff()) { if (loan.isInterestBearingAndInterestRecalculationEnabled() && !loan.isChargedOff()) {
regenerateRepaymentScheduleWithInterestRecalculation(loan, generatorDTO); regenerateRepaymentScheduleWithInterestRecalculation(loan, generatorDTO);
} else { } else {
regenerateRepaymentSchedule(loan, generatorDTO); regenerateRepaymentSchedule(loan, generatorDTO);
@@ -987,7 +987,7 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
private void recalculateInterestForDate(LocalDate targetDate, ProgressiveTransactionCtx ctx) { private void recalculateInterestForDate(LocalDate targetDate, ProgressiveTransactionCtx ctx) {
if (ctx.getInstallments() != null && !ctx.getInstallments().isEmpty()) { if (ctx.getInstallments() != null && !ctx.getInstallments().isEmpty()) {
Loan loan = ctx.getInstallments().get(0).getLoan(); Loan loan = ctx.getInstallments().get(0).getLoan();
if (loan.isInterestRecalculationEnabled() && !loan.isNpa() && !ctx.isChargedOff()) { if (loan.isInterestBearingAndInterestRecalculationEnabled() && !loan.isNpa() && !ctx.isChargedOff()) {
List<LoanRepaymentScheduleInstallment> overdueInstallmentsSortedByInstallmentNumber = findOverdueInstallmentsBeforeDateSortedByInstallmentNumber( List<LoanRepaymentScheduleInstallment> overdueInstallmentsSortedByInstallmentNumber = findOverdueInstallmentsBeforeDateSortedByInstallmentNumber(
targetDate, ctx); targetDate, ctx);
@@ -1226,7 +1226,7 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
if (!installments.isEmpty() && transactionDate.isBefore(loan.getMaturityDate())) { if (!installments.isEmpty() && transactionDate.isBefore(loan.getMaturityDate())) {
if (transactionCtx instanceof ProgressiveTransactionCtx progressiveTransactionCtx if (transactionCtx instanceof ProgressiveTransactionCtx progressiveTransactionCtx
&& loanTransaction.getLoan().isInterestRecalculationEnabled()) { && loanTransaction.getLoan().isInterestBearingAndInterestRecalculationEnabled()) {
final BigDecimal newInterest = emiCalculator final BigDecimal newInterest = emiCalculator
.getPeriodInterestTillDate(progressiveTransactionCtx.getModel(), currentInstallment.getDueDate(), transactionDate) .getPeriodInterestTillDate(progressiveTransactionCtx.getModel(), currentInstallment.getDueDate(), transactionDate)
.getAmount(); .getAmount();
@@ -1291,7 +1291,7 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
if (!installments.isEmpty()) { if (!installments.isEmpty()) {
if (transactionCtx instanceof ProgressiveTransactionCtx progressiveTransactionCtx if (transactionCtx instanceof ProgressiveTransactionCtx progressiveTransactionCtx
&& loanTransaction.getLoan().isInterestRecalculationEnabled()) { && loanTransaction.getLoan().isInterestBearingAndInterestRecalculationEnabled()) {
installments.stream().filter(installment -> !installment.getFromDate().isAfter(transactionDate) installments.stream().filter(installment -> !installment.getFromDate().isAfter(transactionDate)
&& installment.getDueDate().isAfter(transactionDate)).forEach(installment -> { && installment.getDueDate().isAfter(transactionDate)).forEach(installment -> {
final BigDecimal newInterest = emiCalculator.getPeriodInterestTillDate(progressiveTransactionCtx.getModel(), final BigDecimal newInterest = emiCalculator.getPeriodInterestTillDate(progressiveTransactionCtx.getModel(),
@@ -1683,8 +1683,8 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
LoanTransactionToRepaymentScheduleMapping loanTransactionToRepaymentScheduleMapping = getTransactionMapping( LoanTransactionToRepaymentScheduleMapping loanTransactionToRepaymentScheduleMapping = getTransactionMapping(
transactionMappings, loanTransaction, oldestPastDueInstallment, currency); transactionMappings, loanTransaction, oldestPastDueInstallment, currency);
Loan loan = loanTransaction.getLoan(); Loan loan = loanTransaction.getLoan();
if (transactionCtx instanceof ProgressiveTransactionCtx ctx && loan.isInterestBearing() if (transactionCtx instanceof ProgressiveTransactionCtx ctx
&& loan.getLoanProductRelatedDetail().isInterestRecalculationEnabled() && !ctx.isChargedOff()) { && loan.isInterestBearingAndInterestRecalculationEnabled() && !ctx.isChargedOff()) {
paidPortion = handlingPaymentAllocationForInterestBearingProgressiveLoan(loanTransaction, paidPortion = handlingPaymentAllocationForInterestBearingProgressiveLoan(loanTransaction,
transactionAmountUnprocessed, balances, paymentAllocationType, oldestPastDueInstallment, ctx, transactionAmountUnprocessed, balances, paymentAllocationType, oldestPastDueInstallment, ctx,
loanTransactionToRepaymentScheduleMapping, oldestPastDueInstallmentCharges); loanTransactionToRepaymentScheduleMapping, oldestPastDueInstallmentCharges);
@@ -1705,8 +1705,8 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
LoanTransactionToRepaymentScheduleMapping loanTransactionToRepaymentScheduleMapping = getTransactionMapping( LoanTransactionToRepaymentScheduleMapping loanTransactionToRepaymentScheduleMapping = getTransactionMapping(
transactionMappings, loanTransaction, dueInstallment, currency); transactionMappings, loanTransaction, dueInstallment, currency);
Loan loan = loanTransaction.getLoan(); Loan loan = loanTransaction.getLoan();
if (transactionCtx instanceof ProgressiveTransactionCtx ctx && loan.isInterestBearing() if (transactionCtx instanceof ProgressiveTransactionCtx ctx
&& loan.getLoanProductRelatedDetail().isInterestRecalculationEnabled() && !ctx.isChargedOff()) { && loan.isInterestBearingAndInterestRecalculationEnabled() && !ctx.isChargedOff()) {
paidPortion = handlingPaymentAllocationForInterestBearingProgressiveLoan(loanTransaction, paidPortion = handlingPaymentAllocationForInterestBearingProgressiveLoan(loanTransaction,
transactionAmountUnprocessed, balances, paymentAllocationType, dueInstallment, ctx, transactionAmountUnprocessed, balances, paymentAllocationType, dueInstallment, ctx,
loanTransactionToRepaymentScheduleMapping, dueInstallmentCharges); loanTransactionToRepaymentScheduleMapping, dueInstallmentCharges);
@@ -1740,8 +1740,8 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
if (inAdvanceInstallment.equals(inAdvanceInstallments.get(numberOfInstallments - 1))) { if (inAdvanceInstallment.equals(inAdvanceInstallments.get(numberOfInstallments - 1))) {
evenPortion = evenPortion.add(balanceAdjustment); evenPortion = evenPortion.add(balanceAdjustment);
} }
if (transactionCtx instanceof ProgressiveTransactionCtx ctx && loan.isInterestBearing() if (transactionCtx instanceof ProgressiveTransactionCtx ctx
&& loan.getLoanProductRelatedDetail().isInterestRecalculationEnabled() && !ctx.isChargedOff()) { && loan.isInterestBearingAndInterestRecalculationEnabled() && !ctx.isChargedOff()) {
paidPortion = handlingPaymentAllocationForInterestBearingProgressiveLoan(loanTransaction, evenPortion, paidPortion = handlingPaymentAllocationForInterestBearingProgressiveLoan(loanTransaction, evenPortion,
balances, paymentAllocationType, inAdvanceInstallment, ctx, balances, paymentAllocationType, inAdvanceInstallment, ctx,
loanTransactionToRepaymentScheduleMapping, inAdvanceInstallmentCharges); loanTransactionToRepaymentScheduleMapping, inAdvanceInstallmentCharges);
@@ -449,16 +449,13 @@ class AdvancedPaymentScheduleTransactionProcessorTest {
Loan loan = mock(Loan.class); Loan loan = mock(Loan.class);
LoanProductRelatedDetail loanProductRelatedDetail = mock(LoanProductRelatedDetail.class); LoanProductRelatedDetail loanProductRelatedDetail = mock(LoanProductRelatedDetail.class);
LoanPaymentAllocationRule loanPaymentAllocationRule = mock(LoanPaymentAllocationRule.class); LoanPaymentAllocationRule loanPaymentAllocationRule = mock(LoanPaymentAllocationRule.class);
when(loan.isInterestBearing()).thenReturn(true); when(loan.getLoanProductRelatedDetail()).thenReturn(loanProductRelatedDetail);
when(loanProductRelatedDetail.isInterestRecalculationEnabled()).thenReturn(true); when(loanProductRelatedDetail.getLoanScheduleProcessingType()).thenReturn(LoanScheduleProcessingType.HORIZONTAL);
when(loan.isInterestBearingAndInterestRecalculationEnabled()).thenReturn(Boolean.TRUE);
when(loanTransaction.getLoan()).thenReturn(loan); when(loanTransaction.getLoan()).thenReturn(loan);
when(loan.getCurrency()).thenReturn(currency); when(loan.getCurrency()).thenReturn(currency);
when(loan.getLoanProductRelatedDetail()).thenReturn(loanProductRelatedDetail);
when(loanProductRelatedDetail.getLoanScheduleProcessingType()).thenReturn(LoanScheduleProcessingType.HORIZONTAL);
when(loan.getPaymentAllocationRules()).thenReturn(List.of(loanPaymentAllocationRule)); when(loan.getPaymentAllocationRules()).thenReturn(List.of(loanPaymentAllocationRule));
when(loan.isInterestBearing()).thenReturn(true);
when(loanProductRelatedDetail.isInterestRecalculationEnabled()).thenReturn(true);
when(loanPaymentAllocationRule.getTransactionType()).thenReturn(PaymentAllocationTransactionType.DEFAULT); when(loanPaymentAllocationRule.getTransactionType()).thenReturn(PaymentAllocationTransactionType.DEFAULT);
when(loanPaymentAllocationRule.getAllocationTypes()) when(loanPaymentAllocationRule.getAllocationTypes())
@@ -35,8 +35,7 @@ public class LoanInterestRecalculationCOBBusinessStep implements LoanCOBBusiness
@Override @Override
public Loan execute(Loan loan) { public Loan execute(Loan loan) {
if (!loan.isInterestBearing() || !loan.getStatus().isActive() || loan.isNpa() || loan.isChargedOff() if (!loan.getStatus().isActive() || loan.isNpa() || loan.isChargedOff() || !loan.isInterestBearingAndInterestRecalculationEnabled()
|| !loan.isInterestRecalculationEnabled()
|| loan.getLoanInterestRecalculationDetails().disallowInterestCalculationOnPastDue()) { || loan.getLoanInterestRecalculationDetails().disallowInterestCalculationOnPastDue()) {
log.debug( 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", "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",
@@ -246,7 +246,7 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
} }
LocalDate recalculateFrom = null; LocalDate recalculateFrom = null;
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) { if (loan.isInterestBearingAndInterestRecalculationEnabled()) {
recalculateFrom = transactionDate; recalculateFrom = transactionDate;
} }
final ScheduleGeneratorDTO scheduleGeneratorDTO = this.loanUtilService.buildScheduleGeneratorDTO(loan, recalculateFrom, final ScheduleGeneratorDTO scheduleGeneratorDTO = this.loanUtilService.buildScheduleGeneratorDTO(loan, recalculateFrom,
@@ -267,7 +267,7 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
final ChangedTransactionDetail changedTransactionDetail = makeRepayment(loan, newRepaymentTransaction, final ChangedTransactionDetail changedTransactionDetail = makeRepayment(loan, newRepaymentTransaction,
defaultLoanLifecycleStateMachine, existingTransactionIds, existingReversedTransactionIds, scheduleGeneratorDTO); defaultLoanLifecycleStateMachine, existingTransactionIds, existingReversedTransactionIds, scheduleGeneratorDTO);
if (loan.getLoanRepaymentScheduleDetail().isInterestRecalculationEnabled()) { if (loan.isInterestBearingAndInterestRecalculationEnabled()) {
loanAccrualsProcessingService.reprocessExistingAccruals(loan); loanAccrualsProcessingService.reprocessExistingAccruals(loan);
loanAccrualsProcessingService.processIncomePostingAndAccruals(loan); loanAccrualsProcessingService.processIncomePostingAndAccruals(loan);
} }
@@ -294,8 +294,8 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
this.noteRepository.save(note); this.noteRepository.save(note);
} }
loanAccrualsProcessingService.processAccrualsOnInterestRecalculation(loan, loanAccrualsProcessingService.processAccrualsOnInterestRecalculation(loan, loan.isInterestBearingAndInterestRecalculationEnabled(),
loan.repaymentScheduleDetail().isInterestRecalculationEnabled(), false); false);
setLoanDelinquencyTag(loan, transactionDate); setLoanDelinquencyTag(loan, transactionDate);
@@ -495,8 +495,8 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
this.noteRepository.save(note); this.noteRepository.save(note);
} }
loanAccrualsProcessingService.processAccrualsOnInterestRecalculation(loan, loanAccrualsProcessingService.processAccrualsOnInterestRecalculation(loan, loan.isInterestBearingAndInterestRecalculationEnabled(),
loan.repaymentScheduleDetail().isInterestRecalculationEnabled(), false); false);
businessEventNotifierService.notifyPostBusinessEvent(new LoanBalanceChangedBusinessEvent(loan)); businessEventNotifierService.notifyPostBusinessEvent(new LoanBalanceChangedBusinessEvent(loan));
businessEventNotifierService.notifyPostBusinessEvent(new LoanChargePaymentPostBusinessEvent(newPaymentTransaction)); businessEventNotifierService.notifyPostBusinessEvent(new LoanChargePaymentPostBusinessEvent(newPaymentTransaction));
@@ -729,8 +729,8 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
this.noteRepository.save(note); this.noteRepository.save(note);
} }
loanAccrualsProcessingService.processAccrualsOnInterestRecalculation(loan, loanAccrualsProcessingService.processAccrualsOnInterestRecalculation(loan, loan.isInterestBearingAndInterestRecalculationEnabled(),
loan.repaymentScheduleDetail().isInterestRecalculationEnabled(), false); false);
businessEventNotifierService.notifyPostBusinessEvent(new LoanBalanceChangedBusinessEvent(loan)); businessEventNotifierService.notifyPostBusinessEvent(new LoanBalanceChangedBusinessEvent(loan));
businessEventNotifierService businessEventNotifierService
.notifyPostBusinessEvent(new LoanCreditBalanceRefundPostBusinessEvent(newCreditBalanceRefundTransaction)); .notifyPostBusinessEvent(new LoanCreditBalanceRefundPostBusinessEvent(newCreditBalanceRefundTransaction));
@@ -783,8 +783,8 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
this.noteRepository.save(note); this.noteRepository.save(note);
} }
loanAccrualsProcessingService.processAccrualsOnInterestRecalculation(loan, loanAccrualsProcessingService.processAccrualsOnInterestRecalculation(loan, loan.isInterestBearingAndInterestRecalculationEnabled(),
loan.repaymentScheduleDetail().isInterestRecalculationEnabled(), false); false);
businessEventNotifierService.notifyPostBusinessEvent(new LoanBalanceChangedBusinessEvent(loan)); businessEventNotifierService.notifyPostBusinessEvent(new LoanBalanceChangedBusinessEvent(loan));
businessEventNotifierService.notifyPostBusinessEvent(new LoanRefundPostBusinessEvent(newRefundTransaction)); businessEventNotifierService.notifyPostBusinessEvent(new LoanRefundPostBusinessEvent(newRefundTransaction));
@@ -845,7 +845,7 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
defaultLoanLifecycleStateMachine, scheduleGeneratorDTO); defaultLoanLifecycleStateMachine, scheduleGeneratorDTO);
loanAccrualsProcessingService.reprocessExistingAccruals(loan); loanAccrualsProcessingService.reprocessExistingAccruals(loan);
if (loan.getLoanRepaymentScheduleDetail().isInterestRecalculationEnabled()) { if (loan.isInterestBearingAndInterestRecalculationEnabled()) {
loanAccrualsProcessingService.processIncomePostingAndAccruals(loan); loanAccrualsProcessingService.processIncomePostingAndAccruals(loan);
} }
@@ -959,7 +959,7 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
final LoanRepaymentScheduleInstallment currentInstallment = loan.fetchLoanRepaymentScheduleInstallmentByDueDate(transactionDate); final LoanRepaymentScheduleInstallment currentInstallment = loan.fetchLoanRepaymentScheduleInstallmentByDueDate(transactionDate);
boolean reprocess = loan.getLoanRepaymentScheduleDetail().isInterestRecalculationEnabled() // boolean reprocess = loan.isInterestBearingAndInterestRecalculationEnabled() //
|| loan.isForeclosure() // || loan.isForeclosure() //
|| !isTransactionChronologicallyLatest // || !isTransactionChronologicallyLatest //
|| !DateUtils.isEqualBusinessDate(transactionDate) // || !DateUtils.isEqualBusinessDate(transactionDate) //
@@ -978,7 +978,7 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
new MoneyHolder(loan.getTotalOverpaidAsMoney()), null)); new MoneyHolder(loan.getTotalOverpaidAsMoney()), null));
} }
} else { } else {
if (loan.getLoanRepaymentScheduleDetail().isInterestRecalculationEnabled()) { if (loan.isInterestBearingAndInterestRecalculationEnabled()) {
loanScheduleService.regenerateRepaymentScheduleWithInterestRecalculation(loan, scheduleGeneratorDTO); loanScheduleService.regenerateRepaymentScheduleWithInterestRecalculation(loan, scheduleGeneratorDTO);
} else if (loan.getLoanProductRelatedDetail() != null } else if (loan.getLoanProductRelatedDetail() != null
&& loan.getLoanProductRelatedDetail().getLoanScheduleType().equals(LoanScheduleType.PROGRESSIVE) && loan.getLoanProductRelatedDetail().getLoanScheduleType().equals(LoanScheduleType.PROGRESSIVE)
@@ -88,8 +88,7 @@ public class LoanScheduleCalculationPlatformServiceImpl implements LoanScheduleC
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = loanRepaymentScheduleTransactionProcessorFactory final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = loanRepaymentScheduleTransactionProcessorFactory
.determineProcessor(loan.transactionProcessingStrategy()); .determineProcessor(loan.transactionProcessingStrategy());
if (!loan.repaymentScheduleDetail().isInterestRecalculationEnabled() || loan.isNpa() || loan.isChargedOff() if (!loan.isInterestBearingAndInterestRecalculationEnabled() || loan.isNpa() || loan.isChargedOff() || !loan.getStatus().isActive()
|| !loan.getStatus().isActive()
|| !loanRepaymentScheduleTransactionProcessor.isInterestFirstRepaymentScheduleTransactionProcessor()) { || !loanRepaymentScheduleTransactionProcessor.isInterestFirstRepaymentScheduleTransactionProcessor()) {
return; return;
} }
@@ -28,7 +28,7 @@ import org.springframework.stereotype.Component;
public final class LoanForeclosureValidator { public final class LoanForeclosureValidator {
public void validateForForeclosure(final Loan loan, final LocalDate transactionDate) { public void validateForForeclosure(final Loan loan, final LocalDate transactionDate) {
if (loan.isInterestRecalculationEnabled()) { if (loan.isInterestBearingAndInterestRecalculationEnabled()) {
final String defaultUserMessage = "The loan with interest recalculation enabled cannot be foreclosed."; final String defaultUserMessage = "The loan with interest recalculation enabled cannot be foreclosed.";
throw new LoanForeclosureException("loan.with.interest.recalculation.enabled.cannot.be.foreclosured", defaultUserMessage, throw new LoanForeclosureException("loan.with.interest.recalculation.enabled.cannot.be.foreclosured", defaultUserMessage,
loan.getId()); loan.getId());
@@ -685,7 +685,7 @@ public final class LoanTransactionValidator {
} }
public void validateActivityNotBeforeLastTransactionDate(final Loan loan, final LocalDate activityDate, final LoanEvent event) { public void validateActivityNotBeforeLastTransactionDate(final Loan loan, final LocalDate activityDate, final LoanEvent event) {
if (!(loan.repaymentScheduleDetail().isInterestRecalculationEnabled() || loan.loanProduct().isHoldGuaranteeFunds()) if (!(loan.isInterestBearingAndInterestRecalculationEnabled() || loan.loanProduct().isHoldGuaranteeFunds())
|| !loan.getLoanRepaymentScheduleDetail().getLoanScheduleType().equals(LoanScheduleType.CUMULATIVE)) { || !loan.getLoanRepaymentScheduleDetail().getLoanScheduleType().equals(LoanScheduleType.CUMULATIVE)) {
return; return;
} }
@@ -985,8 +985,7 @@ public final class LoanTransactionValidator {
private void validateTransactionNotBeforeLastTransactionDate(final Loan loan, LoanTransactionType loanTransactionType, private void validateTransactionNotBeforeLastTransactionDate(final Loan loan, LoanTransactionType loanTransactionType,
final LocalDate transactionDate) { final LocalDate transactionDate) {
if (!((LoanScheduleType.CUMULATIVE.equals(loan.getLoanProductRelatedDetail().getLoanScheduleType()) if (!((LoanScheduleType.CUMULATIVE.equals(loan.getLoanProductRelatedDetail().getLoanScheduleType())
&& loan.getLoanRepaymentScheduleDetail().isInterestRecalculationEnabled()) && loan.isInterestBearingAndInterestRecalculationEnabled()) || loan.getLoanProduct().isHoldGuaranteeFunds())) {
|| loan.getLoanProduct().isHoldGuaranteeFunds())) {
return; return;
} }
LocalDate lastTransactionDate = loan.getLastUserTransactionDate(); LocalDate lastTransactionDate = loan.getLastUserTransactionDate();
@@ -280,7 +280,7 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
// TODO: check whether this is needed! // TODO: check whether this is needed!
this.loanRepositoryWrapper.saveAndFlush(loan); this.loanRepositoryWrapper.saveAndFlush(loan);
// Save interest recalculation calendar // Save interest recalculation calendar
if (loan.getLoanProductRelatedDetail().isInterestRecalculationEnabled() if (loan.isInterestBearingAndInterestRecalculationEnabled()
&& changes.containsKey(LoanProductConstants.IS_INTEREST_RECALCULATION_ENABLED_PARAMETER_NAME)) { && changes.containsKey(LoanProductConstants.IS_INTEREST_RECALCULATION_ENABLED_PARAMETER_NAME)) {
createAndPersistCalendarInstanceForInterestRecalculation(loan); createAndPersistCalendarInstanceForInterestRecalculation(loan);
} }
@@ -268,7 +268,7 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo
boolean reprocessRequired = true; boolean reprocessRequired = true;
// overpaid transactions will be reprocessed and pay this charge // overpaid transactions will be reprocessed and pay this charge
boolean overpaidReprocess = !loanCharge.isDueAtDisbursement() && !loanCharge.isPaid() && loan.getStatus().isOverpaid(); boolean overpaidReprocess = !loanCharge.isDueAtDisbursement() && !loanCharge.isPaid() && loan.getStatus().isOverpaid();
if (!overpaidReprocess && loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) { if (!overpaidReprocess && loan.isInterestBearingAndInterestRecalculationEnabled()) {
if (isAppliedOnBackDate && loan.isFeeCompoundingEnabledForInterestRecalculation()) { if (isAppliedOnBackDate && loan.isFeeCompoundingEnabledForInterestRecalculation()) {
loan = runScheduleRecalculation(loan, recalculateFrom); loan = runScheduleRecalculation(loan, recalculateFrom);
reprocessRequired = false; reprocessRequired = false;
@@ -308,7 +308,7 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo
loan = loanAccountDomainService.saveAndFlushLoanWithDataIntegrityViolationChecks(loan); loan = loanAccountDomainService.saveAndFlushLoanWithDataIntegrityViolationChecks(loan);
} }
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled() && isAppliedOnBackDate if (loan.isInterestBearingAndInterestRecalculationEnabled() && isAppliedOnBackDate
&& loan.isFeeCompoundingEnabledForInterestRecalculation()) { && loan.isFeeCompoundingEnabledForInterestRecalculation()) {
loanAccrualsProcessingService.processAccrualsOnInterestRecalculation(loan, true, false); loanAccrualsProcessingService.processAccrualsOnInterestRecalculation(loan, true, false);
} }
@@ -558,7 +558,7 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo
existingTransactionIds, existingReversedTransactionIds, loanInstallmentNumber, scheduleGeneratorDTO, accruedCharge, existingTransactionIds, existingReversedTransactionIds, loanInstallmentNumber, scheduleGeneratorDTO, accruedCharge,
externalId); externalId);
if (loan.getLoanRepaymentScheduleDetail().isInterestRecalculationEnabled() if (loan.isInterestBearingAndInterestRecalculationEnabled()
&& DateUtils.isBefore(loanCharge.getDueLocalDate(), DateUtils.getBusinessLocalDate())) { && DateUtils.isBefore(loanCharge.getDueLocalDate(), DateUtils.getBusinessLocalDate())) {
loanAccrualsProcessingService.reprocessExistingAccruals(loan); loanAccrualsProcessingService.reprocessExistingAccruals(loan);
loanAccrualsProcessingService.processIncomePostingAndAccruals(loan); loanAccrualsProcessingService.processIncomePostingAndAccruals(loan);
@@ -810,7 +810,7 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo
recalculateFrom = recalculatedTill; recalculateFrom = recalculatedTill;
} }
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) { if (loan.isInterestBearingAndInterestRecalculationEnabled()) {
if (runInterestRecalculation && loan.isFeeCompoundingEnabledForInterestRecalculation()) { if (runInterestRecalculation && loan.isFeeCompoundingEnabledForInterestRecalculation()) {
loan = runScheduleRecalculation(loan, recalculateFrom); loan = runScheduleRecalculation(loan, recalculateFrom);
reprocessRequired = false; reprocessRequired = false;
@@ -839,7 +839,7 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo
loan = loanAccountDomainService.saveAndFlushLoanWithDataIntegrityViolationChecks(loan); loan = loanAccountDomainService.saveAndFlushLoanWithDataIntegrityViolationChecks(loan);
} }
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled() && runInterestRecalculation if (loan.isInterestBearingAndInterestRecalculationEnabled() && runInterestRecalculation
&& loan.isFeeCompoundingEnabledForInterestRecalculation()) { && loan.isFeeCompoundingEnabledForInterestRecalculation()) {
loanAccrualsProcessingService.processAccrualsOnInterestRecalculation(loan, true, false); loanAccrualsProcessingService.processAccrualsOnInterestRecalculation(loan, true, false);
} }
@@ -865,7 +865,7 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = loanRepaymentScheduleTransactionProcessorFactory final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = loanRepaymentScheduleTransactionProcessorFactory
.determineProcessor(loan.transactionProcessingStrategy()); .determineProcessor(loan.transactionProcessingStrategy());
loan.addLoanTransaction(loanChargeAdjustmentTransaction); loan.addLoanTransaction(loanChargeAdjustmentTransaction);
if (loan.isInterestBearing() && loan.getLoanProductRelatedDetail().isInterestRecalculationEnabled()) { if (loan.isInterestBearingAndInterestRecalculationEnabled()) {
if (loan.getLoanProductRelatedDetail() != null if (loan.getLoanProductRelatedDetail() != null
&& loan.getLoanProductRelatedDetail().getLoanScheduleType().equals(LoanScheduleType.PROGRESSIVE) && loan.getLoanProductRelatedDetail().getLoanScheduleType().equals(LoanScheduleType.PROGRESSIVE)
&& loan.getLoanTransactions().stream().anyMatch(LoanTransaction::isChargeOff)) { && loan.getLoanTransactions().stream().anyMatch(LoanTransaction::isChargeOff)) {
@@ -1019,7 +1019,7 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo
} else if (loanCharge.getDueLocalDate() != null) { } else if (loanCharge.getDueLocalDate() != null) {
// TODO: Review, error message seems not valid if interest recalculation is not enabled. // TODO: Review, error message seems not valid if interest recalculation is not enabled.
boolean isCumulative = loan.getLoanRepaymentScheduleDetail().getLoanScheduleType().equals(LoanScheduleType.CUMULATIVE); boolean isCumulative = loan.getLoanRepaymentScheduleDetail().getLoanScheduleType().equals(LoanScheduleType.CUMULATIVE);
LocalDate validationDate = loan.repaymentScheduleDetail().isInterestRecalculationEnabled() && isCumulative LocalDate validationDate = loan.isInterestBearingAndInterestRecalculationEnabled() && isCumulative
? loan.getLastUserTransactionDate() ? loan.getLastUserTransactionDate()
: loan.getDisbursementDate(); : loan.getDisbursementDate();
if (DateUtils.isBefore(loanCharge.getDueLocalDate(), validationDate)) { if (DateUtils.isBefore(loanCharge.getDueLocalDate(), validationDate)) {
@@ -1027,7 +1027,7 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo
throw new LoanChargeCannotBeAddedException("loanCharge", "date.is.before.last.transaction.date", defaultUserMessage, null, throw new LoanChargeCannotBeAddedException("loanCharge", "date.is.before.last.transaction.date", defaultUserMessage, null,
chargeDefinition.getName()); chargeDefinition.getName());
} }
} else if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) { } else if (loan.isInterestBearingAndInterestRecalculationEnabled()) {
if (loanCharge.isInstalmentFee() && loan.getStatus().isActive()) { if (loanCharge.isInstalmentFee() && loan.getStatus().isActive()) {
final String defaultUserMessage = "installment charge addition not allowed after disbursement"; final String defaultUserMessage = "installment charge addition not allowed after disbursement";
throw new LoanChargeCannotBeAddedException("loanCharge", "installment.charge", defaultUserMessage, null, throw new LoanChargeCannotBeAddedException("loanCharge", "installment.charge", defaultUserMessage, null,
@@ -1173,7 +1173,7 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo
} }
public Loan runScheduleRecalculation(Loan loan, final LocalDate recalculateFrom) { public Loan runScheduleRecalculation(Loan loan, final LocalDate recalculateFrom) {
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled() && !loan.isChargedOff()) { if (loan.isInterestBearingAndInterestRecalculationEnabled() && !loan.isChargedOff()) {
final List<Long> existingTransactionIds = loan.findExistingTransactionIds(); final List<Long> existingTransactionIds = loan.findExistingTransactionIds();
ScheduleGeneratorDTO generatorDTO = this.loanUtilService.buildScheduleGeneratorDTO(loan, recalculateFrom); ScheduleGeneratorDTO generatorDTO = this.loanUtilService.buildScheduleGeneratorDTO(loan, recalculateFrom);
ChangedTransactionDetail changedTransactionDetail = loanScheduleService ChangedTransactionDetail changedTransactionDetail = loanScheduleService
@@ -1456,8 +1456,7 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo
waiveLoanChargeTransaction.getAmount(loan.getCurrency()).getAmount(), loanInstallmentNumber); waiveLoanChargeTransaction.getAmount(loan.getCurrency()).getAmount(), loanInstallmentNumber);
waiveLoanChargeTransaction.getLoanChargesPaid().add(loanChargePaidBy); waiveLoanChargeTransaction.getLoanChargesPaid().add(loanChargePaidBy);
loan.addLoanTransaction(waiveLoanChargeTransaction); loan.addLoanTransaction(waiveLoanChargeTransaction);
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled() if (loan.isInterestBearingAndInterestRecalculationEnabled() && DateUtils.isBefore(loanCharge.getDueLocalDate(), businessDate)) {
&& DateUtils.isBefore(loanCharge.getDueLocalDate(), businessDate)) {
loanScheduleService.regenerateRepaymentScheduleWithInterestRecalculation(loan, scheduleGeneratorDTO); loanScheduleService.regenerateRepaymentScheduleWithInterestRecalculation(loan, scheduleGeneratorDTO);
} else if (loan.getLoanProductRelatedDetail() != null } else if (loan.getLoanProductRelatedDetail() != null
&& loan.getLoanProductRelatedDetail().getLoanScheduleType().equals(LoanScheduleType.PROGRESSIVE) && loan.getLoanProductRelatedDetail().getLoanScheduleType().equals(LoanScheduleType.PROGRESSIVE)
@@ -92,7 +92,7 @@ public class LoanUtilService {
CalendarInstance restCalendarInstance = null; CalendarInstance restCalendarInstance = null;
CalendarInstance compoundingCalendarInstance = null; CalendarInstance compoundingCalendarInstance = null;
Long overdurPenaltyWaitPeriod = null; Long overdurPenaltyWaitPeriod = null;
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) { if (loan.isInterestBearingAndInterestRecalculationEnabled()) {
restCalendarInstance = calendarInstanceRepository.findCalendarInstaneByEntityId(loan.loanInterestRecalculationDetailId(), restCalendarInstance = calendarInstanceRepository.findCalendarInstaneByEntityId(loan.loanInterestRecalculationDetailId(),
CalendarEntityType.LOAN_RECALCULATION_REST_DETAIL.getValue()); CalendarEntityType.LOAN_RECALCULATION_REST_DETAIL.getValue());
compoundingCalendarInstance = calendarInstanceRepository.findCalendarInstaneByEntityId(loan.loanInterestRecalculationDetailId(), compoundingCalendarInstance = calendarInstanceRepository.findCalendarInstaneByEntityId(loan.loanInterestRecalculationDetailId(),
@@ -426,7 +426,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
regenerateScheduleOnDisbursement(command, loan, recalculateSchedule, scheduleGeneratorDTO, nextPossibleRepaymentDate, regenerateScheduleOnDisbursement(command, loan, recalculateSchedule, scheduleGeneratorDTO, nextPossibleRepaymentDate,
rescheduledRepaymentDate); rescheduledRepaymentDate);
boolean downPaymentEnabled = loan.repaymentScheduleDetail().isEnableDownPayment(); boolean downPaymentEnabled = loan.repaymentScheduleDetail().isEnableDownPayment();
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled() || downPaymentEnabled) { if (loan.isInterestBearingAndInterestRecalculationEnabled() || downPaymentEnabled) {
createAndSaveLoanScheduleArchive(loan, scheduleGeneratorDTO); createAndSaveLoanScheduleArchive(loan, scheduleGeneratorDTO);
} }
ChangedTransactionDetail changedTransactionDetail = disburseLoan(command, isPaymentTypeApplicableForDisbursementCharge, ChangedTransactionDetail changedTransactionDetail = disburseLoan(command, isPaymentTypeApplicableForDisbursementCharge,
@@ -436,7 +436,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
loanAccrualsProcessingService.reprocessExistingAccruals(loan); loanAccrualsProcessingService.reprocessExistingAccruals(loan);
LocalDate firstInstallmentDueDate = loan.fetchRepaymentScheduleInstallment(1).getDueDate(); LocalDate firstInstallmentDueDate = loan.fetchRepaymentScheduleInstallment(1).getDueDate();
if (loan.getLoanRepaymentScheduleDetail().isInterestRecalculationEnabled() if (loan.isInterestBearingAndInterestRecalculationEnabled()
&& (DateUtils.isBeforeBusinessDate(firstInstallmentDueDate) || loan.isDisbursementMissed())) { && (DateUtils.isBeforeBusinessDate(firstInstallmentDueDate) || loan.isDisbursementMissed())) {
loanAccrualsProcessingService.processIncomePostingAndAccruals(loan); loanAccrualsProcessingService.processIncomePostingAndAccruals(loan);
} }
@@ -481,7 +481,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
} else { } else {
loanDownPaymentHandlerService.handleDownPayment(scheduleGeneratorDTO, command, disbursementTransaction, loan); loanDownPaymentHandlerService.handleDownPayment(scheduleGeneratorDTO, command, disbursementTransaction, loan);
loanAccrualsProcessingService.reprocessExistingAccruals(loan); loanAccrualsProcessingService.reprocessExistingAccruals(loan);
if (loan.getLoanRepaymentScheduleDetail().isInterestRecalculationEnabled()) { if (loan.isInterestBearingAndInterestRecalculationEnabled()) {
loanAccrualsProcessingService.processIncomePostingAndAccruals(loan); loanAccrualsProcessingService.processIncomePostingAndAccruals(loan);
} }
} }
@@ -518,8 +518,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
this.accountTransfersWritePlatformService.transferFunds(accountTransferDTO); this.accountTransfersWritePlatformService.transferFunds(accountTransferDTO);
} }
updateRecurringCalendarDatesForInterestRecalculation(loan); updateRecurringCalendarDatesForInterestRecalculation(loan);
loanAccrualsProcessingService.processAccrualsOnInterestRecalculation(loan, loanAccrualsProcessingService.processAccrualsOnInterestRecalculation(loan, loan.isInterestBearingAndInterestRecalculationEnabled(),
loan.repaymentScheduleDetail().isInterestRecalculationEnabled(), false); false);
this.loanAccountDomainService.setLoanDelinquencyTag(loan, DateUtils.getBusinessLocalDate()); this.loanAccountDomainService.setLoanDelinquencyTag(loan, DateUtils.getBusinessLocalDate());
// Post Dated Checks // Post Dated Checks
@@ -584,7 +584,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
boolean disbursementMissedParam = loan.isDisbursementMissed(); boolean disbursementMissedParam = loan.isDisbursementMissed();
LocalDate firstInstallmentDueDate = loan.fetchRepaymentScheduleInstallment(1).getDueDate(); LocalDate firstInstallmentDueDate = loan.fetchRepaymentScheduleInstallment(1).getDueDate();
if ((loan.repaymentScheduleDetail().isInterestRecalculationEnabled() if ((loan.isInterestBearingAndInterestRecalculationEnabled()
&& (DateUtils.isBeforeBusinessDate(firstInstallmentDueDate) || disbursementMissedParam))) { && (DateUtils.isBeforeBusinessDate(firstInstallmentDueDate) || disbursementMissedParam))) {
loanScheduleService.regenerateRepaymentScheduleWithInterestRecalculation(loan, scheduleGeneratorDTO); loanScheduleService.regenerateRepaymentScheduleWithInterestRecalculation(loan, scheduleGeneratorDTO);
} }
@@ -681,7 +681,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
private void updateRecurringCalendarDatesForInterestRecalculation(final Loan loan) { private void updateRecurringCalendarDatesForInterestRecalculation(final Loan loan) {
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled() if (loan.isInterestBearingAndInterestRecalculationEnabled()
&& loan.loanInterestRecalculationDetails().getRestFrequencyType().isSameAsRepayment()) { && loan.loanInterestRecalculationDetails().getRestFrequencyType().isSameAsRepayment()) {
final CalendarInstance calendarInstanceForInterestRecalculation = this.calendarInstanceRepository final CalendarInstance calendarInstanceForInterestRecalculation = this.calendarInstanceRepository
.findByEntityIdAndEntityTypeIdAndCalendarTypeId(loan.loanInterestRecalculationDetailId(), .findByEntityIdAndEntityTypeIdAndCalendarTypeId(loan.loanInterestRecalculationDetailId(),
@@ -810,7 +810,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
regenerateScheduleOnDisbursement(command, loan, recalculateSchedule, scheduleGeneratorDTO, nextPossibleRepaymentDate, regenerateScheduleOnDisbursement(command, loan, recalculateSchedule, scheduleGeneratorDTO, nextPossibleRepaymentDate,
rescheduledRepaymentDate); rescheduledRepaymentDate);
boolean downPaymentEnabled = loan.repaymentScheduleDetail().isEnableDownPayment(); boolean downPaymentEnabled = loan.repaymentScheduleDetail().isEnableDownPayment();
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled() || downPaymentEnabled) { if (loan.isInterestBearingAndInterestRecalculationEnabled() || downPaymentEnabled) {
createAndSaveLoanScheduleArchive(loan, scheduleGeneratorDTO); createAndSaveLoanScheduleArchive(loan, scheduleGeneratorDTO);
} }
changedTransactionDetail = disburseLoan(command, configurationDomainService.isPaymentTypeApplicableForDisbursementCharge(), changedTransactionDetail = disburseLoan(command, configurationDomainService.isPaymentTypeApplicableForDisbursementCharge(),
@@ -819,7 +819,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
loanAccrualsProcessingService.reprocessExistingAccruals(loan); loanAccrualsProcessingService.reprocessExistingAccruals(loan);
LocalDate firstInstallmentDueDate = loan.fetchRepaymentScheduleInstallment(1).getDueDate(); LocalDate firstInstallmentDueDate = loan.fetchRepaymentScheduleInstallment(1).getDueDate();
if (loan.getLoanRepaymentScheduleDetail().isInterestRecalculationEnabled() if (loan.isInterestBearingAndInterestRecalculationEnabled()
&& (DateUtils.isBeforeBusinessDate(firstInstallmentDueDate) || loan.isDisbursementMissed())) { && (DateUtils.isBeforeBusinessDate(firstInstallmentDueDate) || loan.isDisbursementMissed())) {
loanAccrualsProcessingService.processIncomePostingAndAccruals(loan); loanAccrualsProcessingService.processIncomePostingAndAccruals(loan);
} }
@@ -865,7 +865,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
} }
updateRecurringCalendarDatesForInterestRecalculation(loan); updateRecurringCalendarDatesForInterestRecalculation(loan);
loanAccrualsProcessingService.processAccrualsOnInterestRecalculation(loan, loanAccrualsProcessingService.processAccrualsOnInterestRecalculation(loan,
loan.repaymentScheduleDetail().isInterestRecalculationEnabled(), true); loan.isInterestBearingAndInterestRecalculationEnabled(), true);
loanAccountDomainService.setLoanDelinquencyTag(loan, DateUtils.getBusinessLocalDate()); loanAccountDomainService.setLoanDelinquencyTag(loan, DateUtils.getBusinessLocalDate());
businessEventNotifierService.notifyPostBusinessEvent(new LoanDisbursalBusinessEvent(loan)); businessEventNotifierService.notifyPostBusinessEvent(new LoanDisbursalBusinessEvent(loan));
} }
@@ -1081,7 +1081,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
LoanTransaction newInterestPaymentWaiverTransaction) { LoanTransaction newInterestPaymentWaiverTransaction) {
LocalDate recalculateFrom = null; LocalDate recalculateFrom = null;
LocalDate transactionDate = newInterestPaymentWaiverTransaction.getTransactionDate(); LocalDate transactionDate = newInterestPaymentWaiverTransaction.getTransactionDate();
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) { if (loan.isInterestBearingAndInterestRecalculationEnabled()) {
recalculateFrom = transactionDate; recalculateFrom = transactionDate;
} }
final ScheduleGeneratorDTO scheduleGeneratorDTO = this.loanUtilService.buildScheduleGeneratorDTO(loan, recalculateFrom, null); final ScheduleGeneratorDTO scheduleGeneratorDTO = this.loanUtilService.buildScheduleGeneratorDTO(loan, recalculateFrom, null);
@@ -1107,13 +1107,13 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
reprocess = false; reprocess = false;
} }
if (isTransactionChronologicallyLatest && (!reprocess || !loan.getLoanRepaymentScheduleDetail().isInterestRecalculationEnabled()) if (isTransactionChronologicallyLatest && (!reprocess || !loan.isInterestBearingAndInterestRecalculationEnabled())
&& !loan.isForeclosure()) { && !loan.isForeclosure()) {
loanRepaymentScheduleTransactionProcessor.processLatestTransaction(newInterestPaymentWaiverTransaction, loanRepaymentScheduleTransactionProcessor.processLatestTransaction(newInterestPaymentWaiverTransaction,
new TransactionCtx(loan.getCurrency(), loan.getRepaymentScheduleInstallments(), loan.getActiveCharges(), new TransactionCtx(loan.getCurrency(), loan.getRepaymentScheduleInstallments(), loan.getActiveCharges(),
new MoneyHolder(loan.getTotalOverpaidAsMoney()), null)); new MoneyHolder(loan.getTotalOverpaidAsMoney()), null));
reprocess = false; reprocess = false;
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) { if (loan.isInterestBearingAndInterestRecalculationEnabled()) {
if (currentInstallment == null || currentInstallment.isNotFullyPaidOff()) { if (currentInstallment == null || currentInstallment.isNotFullyPaidOff()) {
reprocess = true; reprocess = true;
} else { } else {
@@ -1140,7 +1140,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
private ChangedTransactionDetail reprocessChangedLoanTransactions(Loan loan, private ChangedTransactionDetail reprocessChangedLoanTransactions(Loan loan,
LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor, LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor,
ScheduleGeneratorDTO scheduleGeneratorDTO) { ScheduleGeneratorDTO scheduleGeneratorDTO) {
if (loan.getLoanRepaymentScheduleDetail().isInterestRecalculationEnabled()) { if (loan.isInterestBearingAndInterestRecalculationEnabled()) {
loanScheduleService.regenerateRepaymentScheduleWithInterestRecalculation(loan, scheduleGeneratorDTO); loanScheduleService.regenerateRepaymentScheduleWithInterestRecalculation(loan, scheduleGeneratorDTO);
} else if (loan.getLoanProductRelatedDetail() != null } else if (loan.getLoanProductRelatedDetail() != null
@@ -1151,7 +1151,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
ChangedTransactionDetail changedTransactionDetail = loan.reprocessTransactions(); ChangedTransactionDetail changedTransactionDetail = loan.reprocessTransactions();
if (loan.getLoanRepaymentScheduleDetail().isInterestRecalculationEnabled()) { if (loan.isInterestBearingAndInterestRecalculationEnabled()) {
loanAccrualsProcessingService.reprocessExistingAccruals(loan); loanAccrualsProcessingService.reprocessExistingAccruals(loan);
loanAccrualsProcessingService.processIncomePostingAndAccruals(loan); loanAccrualsProcessingService.processIncomePostingAndAccruals(loan);
} }
@@ -1192,8 +1192,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
saveNote(noteText, loan, newInterestPaymentWaiverTransaction); saveNote(noteText, loan, newInterestPaymentWaiverTransaction);
loanAccrualsProcessingService.processAccrualsOnInterestRecalculation(loan, loanAccrualsProcessingService.processAccrualsOnInterestRecalculation(loan, loan.isInterestBearingAndInterestRecalculationEnabled(),
loan.repaymentScheduleDetail().isInterestRecalculationEnabled(), false); false);
loanAccountDomainService.setLoanDelinquencyTag(loan, newInterestPaymentWaiverTransaction.getTransactionDate()); loanAccountDomainService.setLoanDelinquencyTag(loan, newInterestPaymentWaiverTransaction.getTransactionDate());
// disable all active standing orders linked to this loan if status changes to closed // disable all active standing orders linked to this loan if status changes to closed
@@ -1432,7 +1432,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
LocalDate recalculateFrom = null; LocalDate recalculateFrom = null;
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) { if (loan.isInterestBearingAndInterestRecalculationEnabled()) {
recalculateFrom = DateUtils.isAfter(transactionToAdjust.getTransactionDate(), transactionDate) ? transactionDate recalculateFrom = DateUtils.isAfter(transactionToAdjust.getTransactionDate(), transactionDate) ? transactionDate
: transactionToAdjust.getTransactionDate(); : transactionToAdjust.getTransactionDate();
} }
@@ -1456,7 +1456,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
scheduleGeneratorDTO, reversalTxnExternalId); scheduleGeneratorDTO, reversalTxnExternalId);
loanAccrualsProcessingService.reprocessExistingAccruals(loan); loanAccrualsProcessingService.reprocessExistingAccruals(loan);
if (loan.getLoanRepaymentScheduleDetail().isInterestRecalculationEnabled()) { if (loan.isInterestBearingAndInterestRecalculationEnabled()) {
loanAccrualsProcessingService.processIncomePostingAndAccruals(loan); loanAccrualsProcessingService.processIncomePostingAndAccruals(loan);
} }
@@ -1512,8 +1512,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
loan.updateLoanSummaryAndStatus(); loan.updateLoanSummaryAndStatus();
} }
loanAccrualsProcessingService.processAccrualsOnInterestRecalculation(loan, loanAccrualsProcessingService.processAccrualsOnInterestRecalculation(loan, loan.isInterestBearingAndInterestRecalculationEnabled(),
loan.repaymentScheduleDetail().isInterestRecalculationEnabled(), false); false);
this.loanAccountDomainService.setLoanDelinquencyTag(loan, DateUtils.getBusinessLocalDate()); this.loanAccountDomainService.setLoanDelinquencyTag(loan, DateUtils.getBusinessLocalDate());
@@ -1635,7 +1635,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
throw new PlatformServiceUnavailableException("error.msg.loan.chargeback.operation.not.allowed", throw new PlatformServiceUnavailableException("error.msg.loan.chargeback.operation.not.allowed",
"Loan transaction:" + transactionId + " chargeback not allowed as loan status is written off", transactionId); "Loan transaction:" + transactionId + " chargeback not allowed as loan status is written off", transactionId);
} }
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) { if (loan.isInterestBearingAndInterestRecalculationEnabled()) {
throw new PlatformServiceUnavailableException("error.msg.loan.chargeback.operation.not.allowed", throw new PlatformServiceUnavailableException("error.msg.loan.chargeback.operation.not.allowed",
"Loan transaction:" + transactionId + " chargeback not allowed as loan product is interest recalculation enabled", "Loan transaction:" + transactionId + " chargeback not allowed as loan product is interest recalculation enabled",
transactionId); transactionId);
@@ -1753,7 +1753,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
transactionDate, interestComponent, unrecognizedIncome, externalId); transactionDate, interestComponent, unrecognizedIncome, externalId);
businessEventNotifierService.notifyPreBusinessEvent(new LoanWaiveInterestBusinessEvent(waiveInterestTransaction)); businessEventNotifierService.notifyPreBusinessEvent(new LoanWaiveInterestBusinessEvent(waiveInterestTransaction));
LocalDate recalculateFrom = null; LocalDate recalculateFrom = null;
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) { if (loan.isInterestBearingAndInterestRecalculationEnabled()) {
recalculateFrom = transactionDate; recalculateFrom = transactionDate;
} }
@@ -1766,7 +1766,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
final ChangedTransactionDetail changedTransactionDetail = waiveInterest(loan, waiveInterestTransaction, loanLifecycleStateMachine, final ChangedTransactionDetail changedTransactionDetail = waiveInterest(loan, waiveInterestTransaction, loanLifecycleStateMachine,
existingTransactionIds, existingReversedTransactionIds, scheduleGeneratorDTO); existingTransactionIds, existingReversedTransactionIds, scheduleGeneratorDTO);
if (loan.getLoanRepaymentScheduleDetail().isInterestRecalculationEnabled()) { if (loan.isInterestBearingAndInterestRecalculationEnabled()) {
loanAccrualsProcessingService.reprocessExistingAccruals(loan); loanAccrualsProcessingService.reprocessExistingAccruals(loan);
loanAccrualsProcessingService.processIncomePostingAndAccruals(loan); loanAccrualsProcessingService.processIncomePostingAndAccruals(loan);
} }
@@ -1796,8 +1796,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
this.noteRepository.save(note); this.noteRepository.save(note);
} }
loanAccrualsProcessingService.processAccrualsOnInterestRecalculation(loan, loanAccrualsProcessingService.processAccrualsOnInterestRecalculation(loan, loan.isInterestBearingAndInterestRecalculationEnabled(),
loan.repaymentScheduleDetail().isInterestRecalculationEnabled(), false); false);
loanAccountDomainService.setLoanDelinquencyTag(loan, DateUtils.getBusinessLocalDate()); loanAccountDomainService.setLoanDelinquencyTag(loan, DateUtils.getBusinessLocalDate());
businessEventNotifierService.notifyPostBusinessEvent(new LoanBalanceChangedBusinessEvent(loan)); businessEventNotifierService.notifyPostBusinessEvent(new LoanBalanceChangedBusinessEvent(loan));
@@ -1856,7 +1856,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
updateLoanCounters(loan, loan.getDisbursementDate()); updateLoanCounters(loan, loan.getDisbursementDate());
LocalDate recalculateFrom = null; LocalDate recalculateFrom = null;
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) { if (loan.isInterestBearingAndInterestRecalculationEnabled()) {
recalculateFrom = command.localDateValueOfParameterNamed("transactionDate"); recalculateFrom = command.localDateValueOfParameterNamed("transactionDate");
} }
@@ -1879,7 +1879,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
if (changedTransactionDetail != null) { if (changedTransactionDetail != null) {
loanAccrualsProcessingService.reprocessExistingAccruals(loan); loanAccrualsProcessingService.reprocessExistingAccruals(loan);
if (loan.getLoanRepaymentScheduleDetail().isInterestRecalculationEnabled()) { if (loan.isInterestBearingAndInterestRecalculationEnabled()) {
loanAccrualsProcessingService.processIncomePostingAndAccruals(loan); loanAccrualsProcessingService.processIncomePostingAndAccruals(loan);
} }
LoanTransaction writeOff = changedTransactionDetail.getNewTransactionMappings().remove(0L); LoanTransaction writeOff = changedTransactionDetail.getNewTransactionMappings().remove(0L);
@@ -1896,7 +1896,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
this.noteRepository.save(note); this.noteRepository.save(note);
} }
loanAccrualsProcessingService.processAccrualsOnInterestRecalculation(loan, loanAccrualsProcessingService.processAccrualsOnInterestRecalculation(loan,
loan.repaymentScheduleDetail().isInterestRecalculationEnabled(), false); loan.isInterestBearingAndInterestRecalculationEnabled(), false);
loanAccountDomainService.setLoanDelinquencyTag(loan, DateUtils.getBusinessLocalDate()); loanAccountDomainService.setLoanDelinquencyTag(loan, DateUtils.getBusinessLocalDate());
businessEventNotifierService.notifyPostBusinessEvent(new LoanBalanceChangedBusinessEvent(loan)); businessEventNotifierService.notifyPostBusinessEvent(new LoanBalanceChangedBusinessEvent(loan));
businessEventNotifierService.notifyPostBusinessEvent(new LoanWrittenOffPostBusinessEvent(writeOff)); businessEventNotifierService.notifyPostBusinessEvent(new LoanWrittenOffPostBusinessEvent(writeOff));
@@ -1938,7 +1938,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
updateLoanCounters(loan, loan.getDisbursementDate()); updateLoanCounters(loan, loan.getDisbursementDate());
LocalDate recalculateFrom = null; LocalDate recalculateFrom = null;
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) { if (loan.isInterestBearingAndInterestRecalculationEnabled()) {
recalculateFrom = command.localDateValueOfParameterNamed("transactionDate"); recalculateFrom = command.localDateValueOfParameterNamed("transactionDate");
} }
@@ -1950,7 +1950,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
existingReversedTransactionIds, scheduleGeneratorDTO); existingReversedTransactionIds, scheduleGeneratorDTO);
loanAccrualsProcessingService.reprocessExistingAccruals(loan); loanAccrualsProcessingService.reprocessExistingAccruals(loan);
if (loan.getLoanRepaymentScheduleDetail().isInterestRecalculationEnabled()) { if (loan.isInterestBearingAndInterestRecalculationEnabled()) {
loanAccrualsProcessingService.processIncomePostingAndAccruals(loan); loanAccrualsProcessingService.processIncomePostingAndAccruals(loan);
} }
@@ -1972,8 +1972,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
} }
loanAccrualTransactionBusinessEventService.raiseBusinessEventForAccrualTransactions(loan, existingTransactionIds); loanAccrualTransactionBusinessEventService.raiseBusinessEventForAccrualTransactions(loan, existingTransactionIds);
loanAccrualsProcessingService.processAccrualsOnInterestRecalculation(loan, loanAccrualsProcessingService.processAccrualsOnInterestRecalculation(loan, loan.isInterestBearingAndInterestRecalculationEnabled(),
loan.repaymentScheduleDetail().isInterestRecalculationEnabled(), false); false);
loanAccountDomainService.setLoanDelinquencyTag(loan, DateUtils.getBusinessLocalDate()); loanAccountDomainService.setLoanDelinquencyTag(loan, DateUtils.getBusinessLocalDate());
@@ -2387,7 +2387,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
} }
holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(loan.getOfficeId(), loan.getDisbursementDate()); holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(loan.getOfficeId(), loan.getDisbursementDate());
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) { if (loan.isInterestBearingAndInterestRecalculationEnabled()) {
ScheduleGeneratorDTO scheduleGeneratorDTO = loanUtilService.buildScheduleGeneratorDTO(loan, recalculateFrom); ScheduleGeneratorDTO scheduleGeneratorDTO = loanUtilService.buildScheduleGeneratorDTO(loan, recalculateFrom);
loan.setHelpers(null, this.transactionProcessingStrategy); loan.setHelpers(null, this.transactionProcessingStrategy);
loanScheduleService.recalculateScheduleFromLastTransaction(loan, scheduleGeneratorDTO, existingTransactionIds, loanScheduleService.recalculateScheduleFromLastTransaction(loan, scheduleGeneratorDTO, existingTransactionIds,
@@ -2402,7 +2402,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
} }
loanAccrualsProcessingService.reprocessExistingAccruals(loan); loanAccrualsProcessingService.reprocessExistingAccruals(loan);
if (loan.getLoanRepaymentScheduleDetail().isInterestRecalculationEnabled()) { if (loan.isInterestBearingAndInterestRecalculationEnabled()) {
loanAccrualsProcessingService.processIncomePostingAndAccruals(loan); loanAccrualsProcessingService.processIncomePostingAndAccruals(loan);
} }
saveLoanWithDataIntegrityViolationChecks(loan); saveLoanWithDataIntegrityViolationChecks(loan);
@@ -2592,7 +2592,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
ChangedTransactionDetail changedTransactionDetail = undoWrittenOff(loan, loanLifecycleStateMachine, existingTransactionIds, ChangedTransactionDetail changedTransactionDetail = undoWrittenOff(loan, loanLifecycleStateMachine, existingTransactionIds,
existingReversedTransactionIds); existingReversedTransactionIds);
loanAccrualsProcessingService.reprocessExistingAccruals(loan); loanAccrualsProcessingService.reprocessExistingAccruals(loan);
if (loan.getLoanRepaymentScheduleDetail().isInterestRecalculationEnabled()) { if (loan.isInterestBearingAndInterestRecalculationEnabled()) {
loanAccrualsProcessingService.processIncomePostingAndAccruals(loan); loanAccrualsProcessingService.processIncomePostingAndAccruals(loan);
} }
if (changedTransactionDetail != null) { if (changedTransactionDetail != null) {
@@ -2605,8 +2605,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
} }
loan = saveAndFlushLoanWithDataIntegrityViolationChecks(loan); loan = saveAndFlushLoanWithDataIntegrityViolationChecks(loan);
loanAccrualsProcessingService.processAccrualsOnInterestRecalculation(loan, loanAccrualsProcessingService.processAccrualsOnInterestRecalculation(loan, loan.isInterestBearingAndInterestRecalculationEnabled(),
loan.repaymentScheduleDetail().isInterestRecalculationEnabled(), false); false);
businessEventNotifierService.notifyPostBusinessEvent(new LoanBalanceChangedBusinessEvent(loan)); businessEventNotifierService.notifyPostBusinessEvent(new LoanBalanceChangedBusinessEvent(loan));
businessEventNotifierService.notifyPostBusinessEvent(new LoanUndoWrittenOffBusinessEvent(writeOffTransaction)); businessEventNotifierService.notifyPostBusinessEvent(new LoanUndoWrittenOffBusinessEvent(writeOffTransaction));
@@ -2739,7 +2739,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
} else { } else {
loan.repaymentScheduleDetail().setPrincipal(loan.getPrincipalAmountForRepaymentSchedule()); loan.repaymentScheduleDetail().setPrincipal(loan.getPrincipalAmountForRepaymentSchedule());
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) { if (loan.isInterestBearingAndInterestRecalculationEnabled()) {
loanScheduleService.regenerateRepaymentScheduleWithInterestRecalculation(loan, scheduleGeneratorDTO); loanScheduleService.regenerateRepaymentScheduleWithInterestRecalculation(loan, scheduleGeneratorDTO);
} else { } else {
loanScheduleService.regenerateRepaymentSchedule(loan, scheduleGeneratorDTO); loanScheduleService.regenerateRepaymentSchedule(loan, scheduleGeneratorDTO);
@@ -2748,7 +2748,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
} }
loanAccrualsProcessingService.reprocessExistingAccruals(loan); loanAccrualsProcessingService.reprocessExistingAccruals(loan);
if (loan.getLoanRepaymentScheduleDetail().isInterestRecalculationEnabled()) { if (loan.isInterestBearingAndInterestRecalculationEnabled()) {
loanAccrualsProcessingService.processIncomePostingAndAccruals(loan); loanAccrualsProcessingService.processIncomePostingAndAccruals(loan);
} }
@@ -2761,11 +2761,11 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
replayedTransactionBusinessEventService.raiseTransactionReplayedEvents(changedTransactionDetail); replayedTransactionBusinessEventService.raiseTransactionReplayedEvents(changedTransactionDetail);
} }
loan = saveAndFlushLoanWithDataIntegrityViolationChecks(loan); loan = saveAndFlushLoanWithDataIntegrityViolationChecks(loan);
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) { if (loan.isInterestBearingAndInterestRecalculationEnabled()) {
createLoanScheduleArchive(loan, scheduleGeneratorDTO); createLoanScheduleArchive(loan, scheduleGeneratorDTO);
} }
loanAccrualsProcessingService.processAccrualsOnInterestRecalculation(loan, loanAccrualsProcessingService.processAccrualsOnInterestRecalculation(loan, loan.isInterestBearingAndInterestRecalculationEnabled(),
loan.repaymentScheduleDetail().isInterestRecalculationEnabled(), false); false);
this.loanAccountDomainService.setLoanDelinquencyTag(loan, DateUtils.getBusinessLocalDate()); this.loanAccountDomainService.setLoanDelinquencyTag(loan, DateUtils.getBusinessLocalDate());
postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds); postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
@@ -2821,7 +2821,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
existingTransactionIds, existingReversedTransactionIds); existingTransactionIds, existingReversedTransactionIds);
loanAccrualsProcessingService.reprocessExistingAccruals(loan); loanAccrualsProcessingService.reprocessExistingAccruals(loan);
if (loan.getLoanRepaymentScheduleDetail().isInterestRecalculationEnabled()) { if (loan.isInterestBearingAndInterestRecalculationEnabled()) {
loanAccrualsProcessingService.processIncomePostingAndAccruals(loan); loanAccrualsProcessingService.processIncomePostingAndAccruals(loan);
} }
@@ -2834,8 +2834,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
replayedTransactionBusinessEventService.raiseTransactionReplayedEvents(changedTransactionDetail); replayedTransactionBusinessEventService.raiseTransactionReplayedEvents(changedTransactionDetail);
} }
loan = saveAndFlushLoanWithDataIntegrityViolationChecks(loan); loan = saveAndFlushLoanWithDataIntegrityViolationChecks(loan);
loanAccrualsProcessingService.processAccrualsOnInterestRecalculation(loan, loanAccrualsProcessingService.processAccrualsOnInterestRecalculation(loan, loan.isInterestBearingAndInterestRecalculationEnabled(),
loan.repaymentScheduleDetail().isInterestRecalculationEnabled(), false); false);
businessEventNotifierService.notifyPostBusinessEvent(new LoanInterestRecalculationBusinessEvent(loan)); businessEventNotifierService.notifyPostBusinessEvent(new LoanInterestRecalculationBusinessEvent(loan));
postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds); postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
@@ -2860,7 +2860,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
@Override @Override
public void updateOriginalSchedule(Loan loan) { public void updateOriginalSchedule(Loan loan) {
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) { if (loan.isInterestBearingAndInterestRecalculationEnabled()) {
final LocalDate recalculateFrom = null; final LocalDate recalculateFrom = null;
ScheduleGeneratorDTO scheduleGeneratorDTO = this.loanUtilService.buildScheduleGeneratorDTO(loan, recalculateFrom); ScheduleGeneratorDTO scheduleGeneratorDTO = this.loanUtilService.buildScheduleGeneratorDTO(loan, recalculateFrom);
createLoanScheduleArchive(loan, scheduleGeneratorDTO); createLoanScheduleArchive(loan, scheduleGeneratorDTO);
@@ -2909,7 +2909,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
if (loan.isActualDisbursedOnDateEarlierOrLaterThanExpected(actualDisbursementDate) || recalculateSchedule || isEmiAmountChanged if (loan.isActualDisbursedOnDateEarlierOrLaterThanExpected(actualDisbursementDate) || recalculateSchedule || isEmiAmountChanged
|| rescheduledRepaymentDate != null) { || rescheduledRepaymentDate != null) {
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) { if (loan.isInterestBearingAndInterestRecalculationEnabled()) {
loanScheduleService.regenerateRepaymentScheduleWithInterestRecalculation(loan, scheduleGeneratorDTO); loanScheduleService.regenerateRepaymentScheduleWithInterestRecalculation(loan, scheduleGeneratorDTO);
} else { } else {
loanScheduleService.regenerateRepaymentSchedule(loan, scheduleGeneratorDTO); loanScheduleService.regenerateRepaymentSchedule(loan, scheduleGeneratorDTO);
@@ -2917,7 +2917,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
} }
loanAccrualsProcessingService.reprocessExistingAccruals(loan); loanAccrualsProcessingService.reprocessExistingAccruals(loan);
if (loan.getLoanRepaymentScheduleDetail().isInterestRecalculationEnabled()) { if (loan.isInterestBearingAndInterestRecalculationEnabled()) {
loanAccrualsProcessingService.processIncomePostingAndAccruals(loan); loanAccrualsProcessingService.processIncomePostingAndAccruals(loan);
} }
@@ -3098,7 +3098,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
loan); loan);
loanAccrualsProcessingService.reprocessExistingAccruals(loan); loanAccrualsProcessingService.reprocessExistingAccruals(loan);
if (loan.getLoanRepaymentScheduleDetail().isInterestRecalculationEnabled()) { if (loan.isInterestBearingAndInterestRecalculationEnabled()) {
loanAccrualsProcessingService.processIncomePostingAndAccruals(loan); loanAccrualsProcessingService.processIncomePostingAndAccruals(loan);
} }
@@ -3224,7 +3224,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
final LoanTransaction chargeOffTransaction = LoanTransaction.chargeOff(loan, transactionDate, txnExternalId); final LoanTransaction chargeOffTransaction = LoanTransaction.chargeOff(loan, transactionDate, txnExternalId);
if (loan.isInterestBearing() && loan.isInterestRecalculationEnabled()) { if (loan.isInterestBearingAndInterestRecalculationEnabled()) {
final ScheduleGeneratorDTO scheduleGeneratorDTO = this.loanUtilService.buildScheduleGeneratorDTO(loan, null, null); final ScheduleGeneratorDTO scheduleGeneratorDTO = this.loanUtilService.buildScheduleGeneratorDTO(loan, null, null);
loanScheduleService.regenerateRepaymentScheduleWithInterestRecalculation(loan, scheduleGeneratorDTO); loanScheduleService.regenerateRepaymentScheduleWithInterestRecalculation(loan, scheduleGeneratorDTO);
loan.addLoanTransaction(chargeOffTransaction); loan.addLoanTransaction(chargeOffTransaction);
@@ -3310,7 +3310,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
if (loan.isProgressiveSchedule()) { if (loan.isProgressiveSchedule()) {
final ScheduleGeneratorDTO scheduleGeneratorDTO = this.loanUtilService.buildScheduleGeneratorDTO(loan, null, null); final ScheduleGeneratorDTO scheduleGeneratorDTO = this.loanUtilService.buildScheduleGeneratorDTO(loan, null, null);
if (loan.isInterestRecalculationEnabled()) { if (loan.isInterestBearingAndInterestRecalculationEnabled()) {
loanScheduleService.regenerateRepaymentScheduleWithInterestRecalculation(loan, scheduleGeneratorDTO); loanScheduleService.regenerateRepaymentScheduleWithInterestRecalculation(loan, scheduleGeneratorDTO);
} else { } else {
loanScheduleService.regenerateRepaymentSchedule(loan, scheduleGeneratorDTO); loanScheduleService.regenerateRepaymentSchedule(loan, scheduleGeneratorDTO);
@@ -3359,7 +3359,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
Loan loan = this.loanAssembler.assembleFrom(loanId); Loan loan = this.loanAssembler.assembleFrom(loanId);
// Build loan schedule generator dto // Build loan schedule generator dto
LocalDate recalculateFrom = loan.repaymentScheduleDetail().isInterestRecalculationEnabled() ? transactionDate : null; LocalDate recalculateFrom = loan.isInterestBearingAndInterestRecalculationEnabled() ? transactionDate : null;
final ScheduleGeneratorDTO scheduleGeneratorDTO = this.loanUtilService.buildScheduleGeneratorDTO(loan, recalculateFrom, null); final ScheduleGeneratorDTO scheduleGeneratorDTO = this.loanUtilService.buildScheduleGeneratorDTO(loan, recalculateFrom, null);
// Domain rule validations // Domain rule validations
this.loanTransactionValidator.validateRefund(loan, loanTransactionType, transactionDate, scheduleGeneratorDTO); this.loanTransactionValidator.validateRefund(loan, loanTransactionType, transactionDate, scheduleGeneratorDTO);
@@ -3376,7 +3376,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
LoanTransaction refundTransaction = refundTransactions.getLeft(); LoanTransaction refundTransaction = refundTransactions.getLeft();
LoanTransaction interestRefundTransaction = refundTransactions.getRight(); LoanTransaction interestRefundTransaction = refundTransactions.getRight();
// Accrual reprocessing // Accrual reprocessing
if (loan.getLoanRepaymentScheduleDetail().isInterestRecalculationEnabled()) { if (loan.isInterestBearingAndInterestRecalculationEnabled()) {
loanAccrualsProcessingService.reprocessExistingAccruals(loan); loanAccrualsProcessingService.reprocessExistingAccruals(loan);
loanAccrualsProcessingService.processIncomePostingAndAccruals(loan); loanAccrualsProcessingService.processIncomePostingAndAccruals(loan);
} }
@@ -3398,8 +3398,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
loanAccountDomainService.updateAndSaveLoanCollateralTransactionsForIndividualAccounts(loan, refundTransaction); loanAccountDomainService.updateAndSaveLoanCollateralTransactionsForIndividualAccounts(loan, refundTransaction);
} }
// Raise business events // Raise business events
loanAccrualsProcessingService.processAccrualsOnInterestRecalculation(loan, loanAccrualsProcessingService.processAccrualsOnInterestRecalculation(loan, loan.isInterestBearingAndInterestRecalculationEnabled(),
loan.repaymentScheduleDetail().isInterestRecalculationEnabled(), false); false);
businessEventNotifierService.notifyPostBusinessEvent(new LoanBalanceChangedBusinessEvent(loan)); businessEventNotifierService.notifyPostBusinessEvent(new LoanBalanceChangedBusinessEvent(loan));
// Create journal entries for the new transaction(s) // Create journal entries for the new transaction(s)
@@ -3504,7 +3504,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
updateLoanToPreDisbursalState(loan); updateLoanToPreDisbursalState(loan);
if (isScheduleRegenerateRequired || isDisbursedAmountChanged || isEmiAmountChanged if (isScheduleRegenerateRequired || isDisbursedAmountChanged || isEmiAmountChanged
|| loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) { || loan.isInterestBearingAndInterestRecalculationEnabled()) {
// clear off actual disbusrement date so schedule regeneration // clear off actual disbusrement date so schedule regeneration
// uses expected date. // uses expected date.
@@ -3644,7 +3644,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
loan.getLoanRepaymentScheduleDetail().setPrincipal(loan.getDisbursedAmount()); loan.getLoanRepaymentScheduleDetail().setPrincipal(loan.getDisbursedAmount());
loan.removeDisbursementDetail(); loan.removeDisbursementDetail();
loanScheduleService.regenerateRepaymentSchedule(loan, scheduleGeneratorDTO); loanScheduleService.regenerateRepaymentSchedule(loan, scheduleGeneratorDTO);
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) { if (loan.isInterestBearingAndInterestRecalculationEnabled()) {
loanScheduleService.regenerateRepaymentScheduleWithInterestRecalculation(loan, scheduleGeneratorDTO); loanScheduleService.regenerateRepaymentScheduleWithInterestRecalculation(loan, scheduleGeneratorDTO);
} }
if (loan.getLoanProductRelatedDetail() != null if (loan.getLoanProductRelatedDetail() != null
@@ -3766,7 +3766,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
loan.getLoanRepaymentScheduleDetail().setPrincipal(loan.getPrincipalAmountForRepaymentSchedule()); loan.getLoanRepaymentScheduleDetail().setPrincipal(loan.getPrincipalAmountForRepaymentSchedule());
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled() && !(loan.getLoanProductRelatedDetail() != null if (loan.isInterestBearingAndInterestRecalculationEnabled() && !(loan.getLoanProductRelatedDetail() != null
&& loan.getLoanProductRelatedDetail().getLoanScheduleType().equals(LoanScheduleType.PROGRESSIVE) && loan.getLoanProductRelatedDetail().getLoanScheduleType().equals(LoanScheduleType.PROGRESSIVE)
&& loan.getLoanTransactions().stream().anyMatch(LoanTransaction::isChargeOff))) { && loan.getLoanTransactions().stream().anyMatch(LoanTransaction::isChargeOff))) {
loanScheduleService.regenerateRepaymentScheduleWithInterestRecalculation(loan, scheduleGeneratorDTO); loanScheduleService.regenerateRepaymentScheduleWithInterestRecalculation(loan, scheduleGeneratorDTO);
@@ -145,6 +145,7 @@ public class LoanDownPaymentHandlerServiceImplTest {
when(loanForProcessing.getLoanRepaymentScheduleDetail()).thenReturn(loanRepaymentRelatedDetail); when(loanForProcessing.getLoanRepaymentScheduleDetail()).thenReturn(loanRepaymentRelatedDetail);
when(loanForProcessing.repaymentScheduleDetail()).thenReturn(loanRepaymentRelatedDetail); when(loanForProcessing.repaymentScheduleDetail()).thenReturn(loanRepaymentRelatedDetail);
when(loanRepaymentRelatedDetail.isInterestRecalculationEnabled()).thenReturn(true); when(loanRepaymentRelatedDetail.isInterestRecalculationEnabled()).thenReturn(true);
when(loanForProcessing.isInterestBearingAndInterestRecalculationEnabled()).thenReturn(true);
when(loanRepaymentRelatedDetail.getDisbursedAmountPercentageForDownPayment()).thenReturn(BigDecimal.valueOf(10)); when(loanRepaymentRelatedDetail.getDisbursedAmountPercentageForDownPayment()).thenReturn(BigDecimal.valueOf(10));
when(loanForProcessing.getCurrency()).thenReturn(loanCurrency); when(loanForProcessing.getCurrency()).thenReturn(loanCurrency);
when(loanForProcessing.loanCurrency()).thenReturn(loanCurrency); when(loanForProcessing.loanCurrency()).thenReturn(loanCurrency);