FINERACT-1981: Interest recalculation - Recalculate Interest For Loan - Business step for COB - progressive loan
This commit is contained in:
+110
-7
@@ -60,6 +60,7 @@ import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
||||
import org.apache.fineract.infrastructure.core.service.MathUtil;
|
||||
import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil;
|
||||
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
|
||||
import org.apache.fineract.organisation.monetary.domain.Money;
|
||||
import org.apache.fineract.organisation.monetary.domain.MoneyHelper;
|
||||
@@ -180,9 +181,11 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
|
||||
ProgressiveLoanInterestScheduleModel scheduleModel = emiCalculator.generateModel(loanProductRelatedDetail,
|
||||
installmentAmountInMultiplesOf, installments, mc);
|
||||
|
||||
ProgressiveTransactionCtx ctx = new ProgressiveTransactionCtx(currency, installments, charges, overpaymentHolder,
|
||||
changedTransactionDetail, scheduleModel);
|
||||
|
||||
for (final ChargeOrTransaction chargeOrTransaction : chargeOrTransactions) {
|
||||
chargeOrTransaction.getLoanTransaction().ifPresent(loanTransaction -> processSingleTransaction(loanTransaction, currency,
|
||||
installments, charges, changedTransactionDetail, overpaymentHolder, scheduleModel));
|
||||
chargeOrTransaction.getLoanTransaction().ifPresent(loanTransaction -> processSingleTransaction(loanTransaction, ctx));
|
||||
chargeOrTransaction.getLoanCharge()
|
||||
.ifPresent(loanCharge -> processSingleCharge(loanCharge, currency, installments, disbursementDate));
|
||||
}
|
||||
@@ -190,6 +193,7 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
|
||||
.map(ChargeOrTransaction::getLoanTransaction) //
|
||||
.filter(Optional::isPresent) //
|
||||
.map(Optional::get).toList();
|
||||
recalculateInterestForDate(ThreadLocalContextUtil.getBusinessDate(), ctx, true);
|
||||
reprocessInstallments(disbursementDate, txs, installments, currency);
|
||||
return Pair.of(changedTransactionDetail, scheduleModel);
|
||||
}
|
||||
@@ -562,11 +566,10 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
|
||||
loanTransaction.updateLoanTransactionToRepaymentScheduleMappings(transactionMappings);
|
||||
}
|
||||
|
||||
private void processSingleTransaction(LoanTransaction loanTransaction, MonetaryCurrency currency,
|
||||
List<LoanRepaymentScheduleInstallment> installments, Set<LoanCharge> charges, ChangedTransactionDetail changedTransactionDetail,
|
||||
MoneyHolder overpaymentHolder, ProgressiveLoanInterestScheduleModel scheduleModel) {
|
||||
TransactionCtx ctx = new ProgressiveTransactionCtx(currency, installments, charges, overpaymentHolder, changedTransactionDetail,
|
||||
scheduleModel);
|
||||
private void processSingleTransaction(LoanTransaction loanTransaction, final ProgressiveTransactionCtx ctx) {
|
||||
final MonetaryCurrency currency = ctx.getCurrency();
|
||||
final ChangedTransactionDetail changedTransactionDetail = ctx.getChangedTransactionDetail();
|
||||
|
||||
if (loanTransaction.getId() == null) {
|
||||
processLatestTransaction(loanTransaction, ctx);
|
||||
if (loanTransaction.isInterestWaiver()) {
|
||||
@@ -765,7 +768,107 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
|
||||
}
|
||||
}
|
||||
|
||||
private List<LoanRepaymentScheduleInstallment> findOverdueInstallmentsBeforeDateSortedByInstallmentNumber(LocalDate currentDate,
|
||||
ProgressiveTransactionCtx transactionCtx) {
|
||||
final LocalDate fromDate = transactionCtx.getInstallments().get(0).getLoan().getApprovedOnDate();
|
||||
return transactionCtx.getInstallments().stream().filter(installment -> !installment.getFromDate().isBefore(fromDate))
|
||||
.filter(installment -> installment.getDueDate().isBefore(currentDate))
|
||||
.filter(installment -> installment.isOverdueOn(currentDate))
|
||||
.sorted(Comparator.comparing(LoanRepaymentScheduleInstallment::getInstallmentNumber)).toList();
|
||||
}
|
||||
|
||||
private void recalculateInterestForDate(LocalDate currentDate, ProgressiveTransactionCtx ctx, boolean isLastRecalculation) {
|
||||
if (ctx.getInstallments() != null && !ctx.getInstallments().isEmpty()
|
||||
&& ctx.getInstallments().get(0).getLoan().isInterestRecalculationEnabledForProduct()
|
||||
&& !ctx.getInstallments().get(0).getLoan().isNpa() && !ctx.getInstallments().get(0).getLoan().isChargedOff()) {
|
||||
List<LoanRepaymentScheduleInstallment> overdueInstallmentsSortedByInstallmentNumber = findOverdueInstallmentsBeforeDateSortedByInstallmentNumber(
|
||||
currentDate, ctx);
|
||||
if (!overdueInstallmentsSortedByInstallmentNumber.isEmpty()) {
|
||||
List<LoanRepaymentScheduleInstallment> possibleCurrentInstallment = ctx.getInstallments().stream().filter(
|
||||
installment -> installment.getFromDate().isBefore(currentDate) && !installment.getDueDate().isBefore(currentDate))
|
||||
.toList();
|
||||
|
||||
// get DUE installment or last installment
|
||||
LoanRepaymentScheduleInstallment currentInstallment = !possibleCurrentInstallment.isEmpty()
|
||||
? possibleCurrentInstallment.get(0)
|
||||
: ctx.getInstallments().stream().max(Comparator.comparing(LoanRepaymentScheduleInstallment::getInstallmentNumber))
|
||||
.orElseThrow();
|
||||
|
||||
Money overDuePrincipal = Money.zero(ctx.getCurrency());
|
||||
for (LoanRepaymentScheduleInstallment processingInstallment : overdueInstallmentsSortedByInstallmentNumber) {
|
||||
// add and subtract outstanding principal
|
||||
if (overDuePrincipal.compareTo(Money.zero(ctx.getCurrency())) != 0) {
|
||||
adjustOverduePrincipalForInstallment(currentDate, isLastRecalculation, processingInstallment, overDuePrincipal,
|
||||
ctx);
|
||||
}
|
||||
|
||||
overDuePrincipal = overDuePrincipal.add(processingInstallment.getPrincipalOutstanding(ctx.getCurrency()).getAmount());
|
||||
}
|
||||
adjustOverduePrincipalForInstallment(currentDate, isLastRecalculation, currentInstallment, overDuePrincipal, ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void adjustOverduePrincipalForInstallment(LocalDate currentDate, boolean isLastRecalculation,
|
||||
LoanRepaymentScheduleInstallment currentInstallment, Money overduePrincipal, ProgressiveTransactionCtx ctx) {
|
||||
|
||||
LocalDate fromDate = currentInstallment.getFromDate();
|
||||
boolean hasUpdate = false;
|
||||
|
||||
if (currentInstallment.getLoan().getLoanInterestRecalculationDetails().getRestFrequencyType().isSameAsRepayment()) {
|
||||
// if we have same date for fromDate & last overdue balance change then it meas we have the up-to-date
|
||||
// model.
|
||||
if (ctx.getLastOverdueBalanceChange() == null || fromDate.isAfter(ctx.getLastOverdueBalanceChange())) {
|
||||
emiCalculator.addBalanceCorrection(ctx.getModel(), fromDate, overduePrincipal);
|
||||
ctx.setLastOverdueBalanceChange(fromDate);
|
||||
hasUpdate = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (currentInstallment.getLoan().getLoanInterestRecalculationDetails().getRestFrequencyType().isDaily()
|
||||
// if we have same date for currentDate & last overdue balance change then it meas we have the
|
||||
// up-to-date model.
|
||||
&& !currentDate.equals(ctx.getLastOverdueBalanceChange())) {
|
||||
if (ctx.getLastOverdueBalanceChange() == null || currentInstallment.getFromDate().isAfter(ctx.getLastOverdueBalanceChange())) {
|
||||
// first overdue hit for installment. setting overdue balance correction from instalment from date.
|
||||
emiCalculator.addBalanceCorrection(ctx.getModel(), fromDate, overduePrincipal);
|
||||
} else {
|
||||
// not the first balance correction on installment period, then setting overdue balance correction from
|
||||
// last balance change's current date. previous interest period already has the correct balanec
|
||||
// correction
|
||||
emiCalculator.addBalanceCorrection(ctx.getModel(), ctx.getLastOverdueBalanceChange(), overduePrincipal);
|
||||
}
|
||||
|
||||
// setting negative correction for the period from current date, expecting the overdue balance's full
|
||||
// repayment on that day.
|
||||
if (currentDate.isAfter(currentInstallment.getFromDate()) && currentDate.isBefore(currentInstallment.getDueDate())) {
|
||||
emiCalculator.addBalanceCorrection(ctx.getModel(), currentDate, overduePrincipal.negated());
|
||||
ctx.setLastOverdueBalanceChange(currentDate);
|
||||
}
|
||||
hasUpdate = true;
|
||||
}
|
||||
|
||||
if (hasUpdate) {
|
||||
updateInstallmentsPrincipalAndInterestByModel(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateInstallmentsPrincipalAndInterestByModel(ProgressiveTransactionCtx ctx) {
|
||||
ctx.getModel().repayments().forEach(rm -> {
|
||||
LoanRepaymentScheduleInstallment installment = ctx.getInstallments().stream()
|
||||
.filter(ri -> !ri.isDownPayment() && Objects.equals(ri.getFromDate(), rm.getFromDate())).findFirst().orElse(null);
|
||||
if (installment != null) {
|
||||
installment.updatePrincipal(rm.getPrincipalDue().getAmount());
|
||||
installment.updateInterestCharged(rm.getInterestDue().getAmount());
|
||||
installment.setRecalculatedInterestComponent(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void handleRepayment(LoanTransaction loanTransaction, TransactionCtx transactionCtx) {
|
||||
if (transactionCtx instanceof ProgressiveTransactionCtx) {
|
||||
recalculateInterestForDate(loanTransaction.getTransactionDate(), (ProgressiveTransactionCtx) transactionCtx, false);
|
||||
}
|
||||
if (loanTransaction.isRepaymentLikeType() || loanTransaction.isInterestWaiver() || loanTransaction.isRecoveryRepayment()) {
|
||||
loanTransaction.resetDerivedComponents();
|
||||
}
|
||||
|
||||
+6
-3
@@ -18,8 +18,11 @@
|
||||
*/
|
||||
package org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.ChangedTransactionDetail;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanCharge;
|
||||
@@ -28,9 +31,12 @@ import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.Mon
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.TransactionCtx;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.data.ProgressiveLoanInterestScheduleModel;
|
||||
|
||||
@Getter
|
||||
public class ProgressiveTransactionCtx extends TransactionCtx {
|
||||
|
||||
private final ProgressiveLoanInterestScheduleModel model;
|
||||
@Setter
|
||||
private LocalDate lastOverdueBalanceChange = null;
|
||||
|
||||
public ProgressiveTransactionCtx(MonetaryCurrency currency, List<LoanRepaymentScheduleInstallment> installments,
|
||||
Set<LoanCharge> charges, MoneyHolder overpaymentHolder, ChangedTransactionDetail changedTransactionDetail,
|
||||
@@ -39,7 +45,4 @@ public class ProgressiveTransactionCtx extends TransactionCtx {
|
||||
this.model = model;
|
||||
}
|
||||
|
||||
public ProgressiveLoanInterestScheduleModel getModel() {
|
||||
return model;
|
||||
}
|
||||
}
|
||||
|
||||
+165
@@ -21,6 +21,7 @@ package org.apache.fineract.integrationtests;
|
||||
import static java.lang.Boolean.FALSE;
|
||||
import static java.lang.Boolean.TRUE;
|
||||
import static org.apache.fineract.infrastructure.businessdate.domain.BusinessDateType.BUSINESS_DATE;
|
||||
import static org.apache.fineract.integrationtests.BaseLoanIntegrationTest.TransactionProcessingStrategyCode.ADVANCED_PAYMENT_ALLOCATION_STRATEGY;
|
||||
import static org.apache.fineract.integrationtests.common.loans.LoanApplicationTestBuilder.DUE_PENALTY_INTEREST_PRINCIPAL_FEE_IN_ADVANCE_PENALTY_INTEREST_PRINCIPAL_FEE_STRATEGY;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
@@ -41,6 +42,7 @@ import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
@@ -174,6 +176,24 @@ public abstract class BaseLoanIntegrationTest {
|
||||
assertEquals(paidLate, period.getTotalPaidLateForPeriod());
|
||||
}
|
||||
|
||||
protected static void validateFullyUnpaidRepaymentPeriod(GetLoansLoanIdResponse loanDetails, Integer index, String dueDate,
|
||||
double principalDue, double feeDue, double penaltyDue, double interestDue) {
|
||||
validateRepaymentPeriod(loanDetails, index, LocalDate.parse(dueDate, DateTimeFormatter.ofPattern(DATETIME_PATTERN, Locale.ENGLISH)),
|
||||
principalDue, 0, principalDue, feeDue, 0, feeDue, penaltyDue, 0, penaltyDue, interestDue, 0, interestDue, 0, 0);
|
||||
}
|
||||
|
||||
protected static void validateFullyPaidRepaymentPeriod(GetLoansLoanIdResponse loanDetails, Integer index, String dueDate,
|
||||
double principalDue, double feeDue, double penaltyDue, double interestDue) {
|
||||
validateRepaymentPeriod(loanDetails, index, LocalDate.parse(dueDate, DateTimeFormatter.ofPattern(DATETIME_PATTERN, Locale.ENGLISH)),
|
||||
principalDue, principalDue, 0, feeDue, feeDue, 0, penaltyDue, penaltyDue, 0, interestDue, interestDue, 0, 0, 0);
|
||||
}
|
||||
|
||||
protected static void validateFullyPaidRepaymentPeriod(GetLoansLoanIdResponse loanDetails, Integer index, String dueDate,
|
||||
double principalDue, double feeDue, double penaltyDue, double interestDue, double paidLate) {
|
||||
validateRepaymentPeriod(loanDetails, index, LocalDate.parse(dueDate, DateTimeFormatter.ofPattern(DATETIME_PATTERN, Locale.ENGLISH)),
|
||||
principalDue, principalDue, 0, feeDue, feeDue, 0, penaltyDue, penaltyDue, 0, interestDue, interestDue, 0, 0, paidLate);
|
||||
}
|
||||
|
||||
protected static void validateRepaymentPeriod(GetLoansLoanIdResponse loanDetails, Integer index, LocalDate dueDate, double principalDue,
|
||||
double feeDue, double penaltyDue, double interestDue) {
|
||||
validateRepaymentPeriod(loanDetails, index, dueDate, principalDue, 0, principalDue, feeDue, 0, feeDue, penaltyDue, 0, penaltyDue,
|
||||
@@ -217,6 +237,106 @@ public abstract class BaseLoanIntegrationTest {
|
||||
return createOnePeriod30DaysPeriodicAccrualProduct((double) 0);
|
||||
}
|
||||
|
||||
protected PostLoanProductsRequest create4IProgressive() {
|
||||
return new PostLoanProductsRequest().name(Utils.uniqueRandomStringGenerator("4I_PROGRESSIVE_", 6))//
|
||||
.shortName(Utils.uniqueRandomStringGenerator("", 4))//
|
||||
.description("4 installment product - progressive")//
|
||||
.includeInBorrowerCycle(false)//
|
||||
.useBorrowerCycle(false)//
|
||||
.currencyCode("EUR")//
|
||||
.digitsAfterDecimal(2)//
|
||||
.principal(1000.0)//
|
||||
.minPrincipal(100.0)//
|
||||
.maxPrincipal(10000.0)//
|
||||
.numberOfRepayments(4)//
|
||||
.repaymentEvery(1)//
|
||||
.repaymentFrequencyType(RepaymentFrequencyType.MONTHS_L)//
|
||||
.interestRatePerPeriod(10D)//
|
||||
.minInterestRatePerPeriod(0D)//
|
||||
.maxInterestRatePerPeriod(120D)//
|
||||
.interestRateFrequencyType(InterestRateFrequencyType.YEARS)//
|
||||
.isLinkedToFloatingInterestRates(false)//
|
||||
.isLinkedToFloatingInterestRates(false)//
|
||||
.allowVariableInstallments(false)//
|
||||
.amortizationType(AmortizationType.EQUAL_INSTALLMENTS)//
|
||||
.interestType(InterestType.DECLINING_BALANCE)//
|
||||
.interestCalculationPeriodType(InterestCalculationPeriodType.DAILY)//
|
||||
.allowPartialPeriodInterestCalcualtion(false)//
|
||||
.transactionProcessingStrategyCode(ADVANCED_PAYMENT_ALLOCATION_STRATEGY)//
|
||||
.paymentAllocation(List.of(createDefaultPaymentAllocation("NEXT_INSTALLMENT")))//
|
||||
.creditAllocation(List.of())//
|
||||
.overdueDaysForNPA(179)//
|
||||
.daysInMonthType(30)//
|
||||
.daysInYearType(360)//
|
||||
.isInterestRecalculationEnabled(true)//
|
||||
.interestRecalculationCompoundingMethod(0)//
|
||||
.rescheduleStrategyMethod(RescheduleStrategyMethod.ADJUST_LAST_UNPAID_PERIOD)//
|
||||
.recalculationRestFrequencyType(RecalculationRestFrequencyType.SAME_AS_REPAYMENT_PERIOD)//
|
||||
.recalculationRestFrequencyInterval(0)//
|
||||
.isArrearsBasedOnOriginalSchedule(false)//
|
||||
.isCompoundingToBePostedAsTransaction(false)//
|
||||
.preClosureInterestCalculationStrategy(1)//
|
||||
.allowCompoundingOnEod(false)//
|
||||
.canDefineInstallmentAmount(true)//
|
||||
.repaymentStartDateType(1)//
|
||||
.supportedInterestRefundTypes(List.of())//
|
||||
.charges(List.of())//
|
||||
.principalVariationsForBorrowerCycle(List.of())//
|
||||
.interestRateVariationsForBorrowerCycle(List.of())//
|
||||
.numberOfRepaymentVariationsForBorrowerCycle(List.of())//
|
||||
.accountingRule(3)//
|
||||
.canUseForTopup(false)//
|
||||
.fundSourceAccountId(fundSource.getAccountID().longValue())//
|
||||
.loanPortfolioAccountId(loansReceivableAccount.getAccountID().longValue())//
|
||||
.transfersInSuspenseAccountId(suspenseAccount.getAccountID().longValue())//
|
||||
.interestOnLoanAccountId(interestIncomeAccount.getAccountID().longValue())//
|
||||
.incomeFromFeeAccountId(feeIncomeAccount.getAccountID().longValue())//
|
||||
.incomeFromPenaltyAccountId(penaltyIncomeAccount.getAccountID().longValue())//
|
||||
.incomeFromRecoveryAccountId(recoveriesAccount.getAccountID().longValue())//
|
||||
.writeOffAccountId(writtenOffAccount.getAccountID().longValue())//
|
||||
.overpaymentLiabilityAccountId(overpaymentAccount.getAccountID().longValue())//
|
||||
.receivableInterestAccountId(interestReceivableAccount.getAccountID().longValue())//
|
||||
.receivableFeeAccountId(feeReceivableAccount.getAccountID().longValue())//
|
||||
.receivablePenaltyAccountId(penaltyReceivableAccount.getAccountID().longValue())//
|
||||
.goodwillCreditAccountId(goodwillExpenseAccount.getAccountID().longValue())//
|
||||
.incomeFromGoodwillCreditInterestAccountId(interestIncomeChargeOffAccount.getAccountID().longValue())//
|
||||
.incomeFromGoodwillCreditFeesAccountId(feeChargeOffAccount.getAccountID().longValue())//
|
||||
.incomeFromGoodwillCreditPenaltyAccountId(feeChargeOffAccount.getAccountID().longValue())//
|
||||
.incomeFromChargeOffInterestAccountId(interestIncomeChargeOffAccount.getAccountID().longValue())//
|
||||
.incomeFromChargeOffFeesAccountId(feeChargeOffAccount.getAccountID().longValue())//
|
||||
.incomeFromChargeOffPenaltyAccountId(penaltyChargeOffAccount.getAccountID().longValue())//
|
||||
.chargeOffExpenseAccountId(chargeOffExpenseAccount.getAccountID().longValue())//
|
||||
.chargeOffFraudExpenseAccountId(chargeOffFraudExpenseAccount.getAccountID().longValue())//
|
||||
.dateFormat(DATETIME_PATTERN)//
|
||||
.locale("en")//
|
||||
.enableAccrualActivityPosting(false)//
|
||||
.multiDisburseLoan(true)//
|
||||
.maxTrancheCount(10)//
|
||||
.outstandingLoanBalance(10000.0)//
|
||||
.disallowExpectedDisbursements(true)//
|
||||
.allowApprovedDisbursedAmountsOverApplied(true)//
|
||||
.overAppliedCalculationType("percentage")//
|
||||
.overAppliedNumber(50)//
|
||||
.principalThresholdForLastInstallment(50)//
|
||||
.holdGuaranteeFunds(false)//
|
||||
.accountMovesOutOfNPAOnlyOnArrearsCompletion(false)//
|
||||
.allowAttributeOverrides(new AllowAttributeOverrides()//
|
||||
.amortizationType(true)//
|
||||
.interestType(true)//
|
||||
.transactionProcessingStrategyCode(true)//
|
||||
.interestCalculationPeriodType(true)//
|
||||
.inArrearsTolerance(true)//
|
||||
.repaymentEvery(true)//
|
||||
.graceOnPrincipalAndInterestPayment(true)//
|
||||
.graceOnArrearsAgeing(true)//
|
||||
).isEqualAmortization(false)//
|
||||
.delinquencyBucketId(1L)//
|
||||
.enableDownPayment(false)//
|
||||
.enableInstallmentLevelDelinquency(false)//
|
||||
.loanScheduleType("PROGRESSIVE")//
|
||||
.loanScheduleProcessingType("HORIZONTAL");//
|
||||
}
|
||||
|
||||
// Loan product with proper accounting setup
|
||||
protected PostLoanProductsRequest createOnePeriod30DaysPeriodicAccrualProduct(double interestRatePerPeriod) {
|
||||
return new PostLoanProductsRequest().name(Utils.uniqueRandomStringGenerator("LOAN_PRODUCT_", 6))//
|
||||
@@ -700,6 +820,23 @@ public abstract class BaseLoanIntegrationTest {
|
||||
return postLoansRequest;
|
||||
}
|
||||
|
||||
protected PostLoansRequest applyPin4ProgressiveLoanRequest(Long clientId, Long loanProductId, String loanDisbursementDate,
|
||||
Double amount, Double interestRate, int numberOfRepayments, Consumer<PostLoansRequest> customizer) {
|
||||
|
||||
PostLoansRequest postLoansRequest = new PostLoansRequest().clientId(clientId)
|
||||
.transactionProcessingStrategyCode(ADVANCED_PAYMENT_ALLOCATION_STRATEGY).productId(loanProductId)
|
||||
.expectedDisbursementDate(loanDisbursementDate).dateFormat(DATETIME_PATTERN).locale("en")
|
||||
.submittedOnDate(loanDisbursementDate).amortizationType(1).interestRatePerPeriod(BigDecimal.valueOf(interestRate))
|
||||
.numberOfRepayments(numberOfRepayments).principal(BigDecimal.valueOf(amount)).loanTermFrequency(numberOfRepayments)
|
||||
.repaymentEvery(1).repaymentFrequencyType(RepaymentFrequencyType.MONTHS)
|
||||
.loanTermFrequencyType(RepaymentFrequencyType.MONTHS).interestType(InterestType.DECLINING_BALANCE)
|
||||
.interestCalculationPeriodType(InterestCalculationPeriodType.DAILY).loanType("individual");
|
||||
if (customizer != null) {
|
||||
customizer.accept(postLoansRequest);
|
||||
}
|
||||
return postLoansRequest;
|
||||
}
|
||||
|
||||
protected PostLoansLoanIdRequest approveLoanRequest(Double amount, String approvalDate) {
|
||||
return new PostLoansLoanIdRequest().approvedLoanAmount(BigDecimal.valueOf(amount)).dateFormat(DATETIME_PATTERN)
|
||||
.approvedOnDate(approvalDate).locale("en");
|
||||
@@ -726,6 +863,17 @@ public abstract class BaseLoanIntegrationTest {
|
||||
return approvedLoanResult.getLoanId();
|
||||
}
|
||||
|
||||
protected Long applyAndApproveProgressiveLoan(Long clientId, Long loanProductId, String loanDisbursementDate, Double amount,
|
||||
Double interestRate, int numberOfRepayments, Consumer<PostLoansRequest> customizer) {
|
||||
PostLoansResponse postLoansResponse = loanTransactionHelper.applyLoan(applyPin4ProgressiveLoanRequest(clientId, loanProductId,
|
||||
loanDisbursementDate, amount, interestRate, numberOfRepayments, customizer));
|
||||
|
||||
PostLoansLoanIdResponse approvedLoanResult = loanTransactionHelper.approveLoan(postLoansResponse.getResourceId(),
|
||||
approveLoanRequest(amount, loanDisbursementDate));
|
||||
|
||||
return approvedLoanResult.getLoanId();
|
||||
}
|
||||
|
||||
protected Long applyAndApproveLoan(Long clientId, Long loanProductId, String loanDisbursementDate, Double amount) {
|
||||
return applyAndApproveLoan(clientId, loanProductId, loanDisbursementDate, amount, 1);
|
||||
}
|
||||
@@ -1039,11 +1187,18 @@ public abstract class BaseLoanIntegrationTest {
|
||||
public static class RepaymentFrequencyType {
|
||||
|
||||
public static final Integer MONTHS = 2;
|
||||
public static final Long MONTHS_L = 2L;
|
||||
public static final String MONTHS_STRING = "MONTHS";
|
||||
public static final Integer DAYS = 0;
|
||||
public static final String DAYS_STRING = "DAYS";
|
||||
}
|
||||
|
||||
public static class RecalculationRestFrequencyType {
|
||||
|
||||
public static final Integer SAME_AS_REPAYMENT_PERIOD = 1;
|
||||
public static final Integer DAILY = 2;
|
||||
}
|
||||
|
||||
public static class InterestCalculationPeriodType {
|
||||
|
||||
public static final Integer DAILY = 0;
|
||||
@@ -1056,6 +1211,16 @@ public abstract class BaseLoanIntegrationTest {
|
||||
public static final Integer YEARS = 3;
|
||||
}
|
||||
|
||||
public static class TransactionProcessingStrategyCode {
|
||||
|
||||
public static final String ADVANCED_PAYMENT_ALLOCATION_STRATEGY = "advanced-payment-allocation-strategy";
|
||||
}
|
||||
|
||||
public static class RescheduleStrategyMethod {
|
||||
|
||||
public static final Integer ADJUST_LAST_UNPAID_PERIOD = 4;
|
||||
}
|
||||
|
||||
public static class DaysInYearType {
|
||||
|
||||
public static final Integer INVALID = 0;
|
||||
|
||||
+793
-56
@@ -18,6 +18,7 @@
|
||||
*/
|
||||
package org.apache.fineract.integrationtests;
|
||||
|
||||
import static org.apache.fineract.integrationtests.BaseLoanIntegrationTest.InterestCalculationPeriodType.SAME_AS_REPAYMENT_PERIOD;
|
||||
import static org.apache.fineract.integrationtests.BaseLoanIntegrationTest.RepaymentFrequencyType.DAYS;
|
||||
import static org.apache.fineract.integrationtests.BaseLoanIntegrationTest.RepaymentFrequencyType.MONTHS;
|
||||
|
||||
@@ -28,19 +29,25 @@ import io.restassured.specification.RequestSpecification;
|
||||
import io.restassured.specification.ResponseSpecification;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.stream.Stream;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.fineract.client.models.GetLoansLoanIdResponse;
|
||||
import org.apache.fineract.client.models.PostClientsResponse;
|
||||
import org.apache.fineract.client.models.PostLoanProductsResponse;
|
||||
import org.apache.fineract.client.models.PostLoansLoanIdTransactionsResponse;
|
||||
import org.apache.fineract.integrationtests.common.BusinessStepHelper;
|
||||
import org.apache.fineract.integrationtests.common.ClientHelper;
|
||||
import org.apache.fineract.integrationtests.common.SchedulerJobHelper;
|
||||
import org.apache.fineract.integrationtests.common.Utils;
|
||||
import org.apache.fineract.integrationtests.common.loans.LoanProductTestBuilder;
|
||||
import org.apache.fineract.integrationtests.common.loans.LoanTransactionHelper;
|
||||
import org.apache.fineract.integrationtests.inlinecob.InlineLoanCOBHelper;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
@@ -74,35 +81,373 @@ public class LoanInterestRecalculationCOBTest extends BaseLoanIntegrationTest {
|
||||
"EXTERNAL_ASSET_OWNER_TRANSFER", "CHECK_DUE_INSTALLMENTS", "ACCRUAL_ACTIVITY_POSTING", "LOAN_INTEREST_RECALCULATION");
|
||||
}
|
||||
|
||||
private void logLoanDetails(GetLoansLoanIdResponse loanDetails) {
|
||||
log.info("index, dueDate, principal, fee, penalty, interest");
|
||||
Assertions.assertNotNull(loanDetails.getRepaymentSchedule());
|
||||
Assertions.assertNotNull(loanDetails.getRepaymentSchedule().getPeriods());
|
||||
loanDetails.getRepaymentSchedule().getPeriods()
|
||||
.forEach(period -> log.info("{}, \"{}\", {}, {}, {}, {}", period.getPeriod(),
|
||||
DateTimeFormatter.ofPattern(DATETIME_PATTERN, Locale.ENGLISH).format(Objects.requireNonNull(period.getDueDate())),
|
||||
period.getPrincipalDue(), period.getFeeChargesDue(), period.getPenaltyChargesDue(), period.getInterestDue()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void verifyLoanInstallmentRecalculatedIfThereIsOverdueInstallmentOnProgressiveLoanCOB() {
|
||||
public void verifyLoanInstallmentRecalculatedIfThereIsOverdueInstallmentOn4IProgressiveLoanCOBStepDaily() {
|
||||
AtomicReference<Long> loanIdRef = new AtomicReference<>();
|
||||
runAt("1 January 2023", () -> {
|
||||
PostLoanProductsResponse loanProduct = loanProductHelper.createLoanProduct(
|
||||
createOnePeriod30DaysPeriodicAccrualProductWithAdvancedPaymentAllocationAndInterestRecalculation(16.0, 4)
|
||||
.maxInterestRatePerPeriod(120.0).maxPrincipal(10000.0));
|
||||
PostLoanProductsResponse loanProduct = loanProductHelper
|
||||
.createLoanProduct(create4IProgressive().recalculationRestFrequencyType(RecalculationRestFrequencyType.DAILY) //
|
||||
);
|
||||
|
||||
Long loanId = applyAndApproveLoan(client.getClientId(), loanProduct.getResourceId(), "1 January 2023", 8000.0, 4,
|
||||
postLoansRequest -> postLoansRequest.loanTermFrequency(4)//
|
||||
.loanTermFrequencyType(MONTHS)//
|
||||
.interestRatePerPeriod(BigDecimal.valueOf(120.0)).interestCalculationPeriodType(DAYS)//
|
||||
.transactionProcessingStrategyCode(LoanProductTestBuilder.ADVANCED_PAYMENT_ALLOCATION_STRATEGY)//
|
||||
.repaymentEvery(1)//
|
||||
.repaymentFrequencyType(MONTHS)//
|
||||
.principal(BigDecimal.valueOf(8000.0)).maxOutstandingLoanBalance(BigDecimal.valueOf(100000.0)));
|
||||
Long loanId = applyAndApproveProgressiveLoan(client.getClientId(), loanProduct.getResourceId(), "1 January 2023", 8000.0, 10.0,
|
||||
4, null);
|
||||
loanIdRef.set(loanId);
|
||||
|
||||
disburseLoan(loanId, BigDecimal.valueOf(8000), "1 January 2023");
|
||||
|
||||
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanId);
|
||||
logLoanDetails(loanDetails);
|
||||
|
||||
loanDetails.getRepaymentSchedule().getPeriods().forEach(p -> log.info("validateRepaymentPeriod before: {} {} {}", p.getPeriod(),
|
||||
p.getPrincipalOriginalDue(), p.getInterestOriginalDue()));//
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 1, "01 February 2023", 1975.17, 0.0, 0.0, 66.67);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 2, "01 March 2023", 1991.63, 0.0, 0.0, 50.21);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 3, "01 April 2023", 2008.23, 0.0, 0.0, 33.61);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 4, "01 May 2023", 2024.97, 0.0, 0.0, 16.87);
|
||||
|
||||
validateRepaymentPeriod(loanDetails, 1, LocalDate.of(2023, 2, 1), 1700.66, 0.0, 0.0, 815.34);
|
||||
validateRepaymentPeriod(loanDetails, 2, LocalDate.of(2023, 3, 1), 1936.12, 0.0, 0.0, 579.88);
|
||||
validateRepaymentPeriod(loanDetails, 3, LocalDate.of(2023, 4, 1), 2071.31, 0.0, 0.0, 444.69);
|
||||
validateRepaymentPeriod(loanDetails, 4, LocalDate.of(2023, 5, 1), 2291.91, 0.0, 0.0, 226.05);
|
||||
});
|
||||
|
||||
runAt("1 February 2023", () -> {
|
||||
Long loanId = loanIdRef.get();
|
||||
|
||||
inlineLoanCOBHelper.executeInlineCOB(List.of(loanId));
|
||||
|
||||
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanId);
|
||||
logLoanDetails(loanDetails);
|
||||
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 1, "01 February 2023", 1975.17, 0.0, 0.0, 66.67);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 2, "01 March 2023", 1991.63, 0.0, 0.0, 50.21);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 3, "01 April 2023", 2008.23, 0.0, 0.0, 33.61);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 4, "01 May 2023", 2024.97, 0.0, 0.0, 16.87);
|
||||
});
|
||||
runAt("2 February 2023", () -> {
|
||||
Long loanId = loanIdRef.get();
|
||||
|
||||
inlineLoanCOBHelper.executeInlineCOB(List.of(loanId));
|
||||
|
||||
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanId);
|
||||
logLoanDetails(loanDetails);
|
||||
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 1, "01 February 2023", 1975.17, 0.0, 0.0, 66.67);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 2, "01 March 2023", 1991.05, 0.0, 0.0, 50.79);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 3, "01 April 2023", 2008.23, 0.0, 0.0, 33.61);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 4, "01 May 2023", 2025.55, 0.0, 0.0, 16.88);
|
||||
});
|
||||
runAt("20 February 2023", () -> {
|
||||
Long loanId = loanIdRef.get();
|
||||
|
||||
inlineLoanCOBHelper.executeInlineCOB(List.of(loanId));
|
||||
|
||||
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanId);
|
||||
logLoanDetails(loanDetails);
|
||||
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 1, "01 February 2023", 1975.17, 0.0, 0.0, 66.67);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 2, "01 March 2023", 1980.46, 0.0, 0.0, 61.38);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 3, "01 April 2023", 2008.14, 0.0, 0.0, 33.7);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 4, "01 May 2023", 2036.23, 0.0, 0.0, 16.97);
|
||||
|
||||
});
|
||||
runAt("1 March 2023", () -> {
|
||||
Long loanId = loanIdRef.get();
|
||||
|
||||
inlineLoanCOBHelper.executeInlineCOB(List.of(loanId));
|
||||
|
||||
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanId);
|
||||
logLoanDetails(loanDetails);
|
||||
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 1, "01 February 2023", 1975.17, 0.0, 0.0, 66.67);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 2, "01 March 2023", 1975.17, 0.0, 0.0, 66.67);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 3, "01 April 2023", 2008.09, 0.0, 0.0, 33.75);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 4, "01 May 2023", 2041.57, 0.0, 0.0, 17.01);
|
||||
|
||||
});
|
||||
payoffOnDateAndVerifyStatus("1 February 2023", loanIdRef.get());
|
||||
}
|
||||
|
||||
private void payoffOnDateAndVerifyStatus(final String date, final Long loanId) {
|
||||
runAt(date, () -> {
|
||||
HashMap prepayAmount = loanTransactionHelper.getPrepayAmount(requestSpec, responseSpec, loanId.intValue());
|
||||
Assertions.assertNotNull(prepayAmount);
|
||||
Float amount = (Float) prepayAmount.get("amount");
|
||||
PostLoansLoanIdTransactionsResponse response = loanTransactionHelper.makeLoanRepayment(date, amount, loanId.intValue());
|
||||
Assertions.assertNotNull(response);
|
||||
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanId);
|
||||
Assertions.assertNotNull(loanDetails);
|
||||
Assertions.assertNotNull(loanDetails.getStatus());
|
||||
log.info("Loan status {}", loanDetails.getStatus().getId());
|
||||
Assertions.assertTrue(Stream.of(600).anyMatch(v -> loanDetails.getStatus().getId().intValue() == v));
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void verifyLoanInstallmentRecalculatedIfThereIsOverdueInstallmentOn4IProgressiveLoanCOBStepLatePayPayOnDuePayLatePayOnDateDaily() {
|
||||
AtomicReference<Long> loanIdRef = new AtomicReference<>();
|
||||
runAt("1 January 2023", () -> {
|
||||
PostLoanProductsResponse loanProduct = loanProductHelper
|
||||
.createLoanProduct(create4IProgressive().recalculationRestFrequencyType(RecalculationRestFrequencyType.DAILY) //
|
||||
);
|
||||
|
||||
Long loanId = applyAndApproveProgressiveLoan(client.getClientId(), loanProduct.getResourceId(), "1 January 2023", 8000.0, 10.0,
|
||||
4, null);
|
||||
loanIdRef.set(loanId);
|
||||
|
||||
disburseLoan(loanId, BigDecimal.valueOf(8000), "1 January 2023");
|
||||
|
||||
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanId);
|
||||
logLoanDetails(loanDetails);
|
||||
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 1, "01 February 2023", 1975.17, 0.0, 0.0, 66.67);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 2, "01 March 2023", 1991.63, 0.0, 0.0, 50.21);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 3, "01 April 2023", 2008.23, 0.0, 0.0, 33.61);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 4, "01 May 2023", 2024.97, 0.0, 0.0, 16.87);
|
||||
|
||||
});
|
||||
runAt("20 February 2023", () -> {
|
||||
Long loanId = loanIdRef.get();
|
||||
|
||||
inlineLoanCOBHelper.executeInlineCOB(List.of(loanId));
|
||||
|
||||
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanId);
|
||||
logLoanDetails(loanDetails);
|
||||
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 1, "01 February 2023", 1975.17, 0.0, 0.0, 66.67);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 2, "01 March 2023", 1980.46, 0.0, 0.0, 61.38);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 3, "01 April 2023", 2008.14, 0.0, 0.0, 33.7);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 4, "01 May 2023", 2036.23, 0.0, 0.0, 16.97);
|
||||
|
||||
});
|
||||
runAt("1 March 2023", () -> {
|
||||
Long loanId = loanIdRef.get();
|
||||
|
||||
inlineLoanCOBHelper.executeInlineCOB(List.of(loanId));
|
||||
|
||||
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanId);
|
||||
logLoanDetails(loanDetails);
|
||||
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 1, "01 February 2023", 1975.17, 0.0, 0.0, 66.67);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 2, "01 March 2023", 1975.17, 0.0, 0.0, 66.67);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 3, "01 April 2023", 2008.09, 0.0, 0.0, 33.75);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 4, "01 May 2023", 2041.57, 0.0, 0.0, 17.01);
|
||||
|
||||
loanTransactionHelper.makeLoanRepayment("20 February 2023", 2041.84f, loanId.intValue());
|
||||
loanTransactionHelper.makeLoanRepayment("01 March 2023", 2041.84f, loanId.intValue());
|
||||
|
||||
loanDetails = loanTransactionHelper.getLoanDetails(loanId);
|
||||
logLoanDetails(loanDetails);
|
||||
|
||||
validateFullyPaidRepaymentPeriod(loanDetails, 1, "01 February 2023", 1975.17, 0.0, 0.0, 66.67, 2041.84);
|
||||
validateFullyPaidRepaymentPeriod(loanDetails, 2, "01 March 2023", 1980.46, 0.0, 0.0, 61.38);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 3, "01 April 2023", 2008.14, 0.0, 0.0, 33.7);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 4, "01 May 2023", 2036.23, 0.0, 0.0, 16.97);
|
||||
});
|
||||
runAt("10 April 2023", () -> {
|
||||
Long loanId = loanIdRef.get();
|
||||
|
||||
inlineLoanCOBHelper.executeInlineCOB(List.of(loanId));
|
||||
|
||||
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanId);
|
||||
logLoanDetails(loanDetails);
|
||||
|
||||
validateFullyPaidRepaymentPeriod(loanDetails, 1, "01 February 2023", 1975.17, 0.0, 0.0, 66.67, 2041.84);
|
||||
validateFullyPaidRepaymentPeriod(loanDetails, 2, "01 March 2023", 1980.46, 0.0, 0.0, 61.38);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 3, "01 April 2023", 2008.14, 0.0, 0.0, 33.7);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 4, "01 May 2023", 2036.23, 0.0, 0.0, 21.99);
|
||||
|
||||
loanTransactionHelper.makeLoanRepayment("10 April 2023", 2041.84f, loanId.intValue());
|
||||
|
||||
loanDetails = loanTransactionHelper.getLoanDetails(loanId);
|
||||
logLoanDetails(loanDetails);
|
||||
|
||||
validateFullyPaidRepaymentPeriod(loanDetails, 1, "01 February 2023", 1975.17, 0.0, 0.0, 66.67, 2041.84);
|
||||
validateFullyPaidRepaymentPeriod(loanDetails, 2, "01 March 2023", 1980.46, 0.0, 0.0, 61.38);
|
||||
validateFullyPaidRepaymentPeriod(loanDetails, 3, "01 April 2023", 2008.14, 0.0, 0.0, 33.7, 2041.84);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 4, "01 May 2023", 2036.23, 0.0, 0.0, 21.99);
|
||||
});
|
||||
runAt("20 April 2023", () -> {
|
||||
Long loanId = loanIdRef.get();
|
||||
|
||||
inlineLoanCOBHelper.executeInlineCOB(List.of(loanId));
|
||||
|
||||
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanId);
|
||||
logLoanDetails(loanDetails);
|
||||
|
||||
validateFullyPaidRepaymentPeriod(loanDetails, 1, "01 February 2023", 1975.17, 0.0, 0.0, 66.67, 2041.84);
|
||||
validateFullyPaidRepaymentPeriod(loanDetails, 2, "01 March 2023", 1980.46, 0.0, 0.0, 61.38);
|
||||
validateFullyPaidRepaymentPeriod(loanDetails, 3, "01 April 2023", 2008.14, 0.0, 0.0, 33.7, 2041.84);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 4, "01 May 2023", 2036.23, 0.0, 0.0, 21.99);
|
||||
});
|
||||
payoffOnDateAndVerifyStatus("20 April 2023", loanIdRef.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void verifyLoanInstallmentRecalculatedIfThereIsOverdueInstallmentOn4IProgressiveLoanCOBStepLatePartialRepaymentDailyInterestCalculation() {
|
||||
AtomicReference<Long> loanIdRef = new AtomicReference<>();
|
||||
runAt("1 January 2023", () -> {
|
||||
PostLoanProductsResponse loanProduct = loanProductHelper
|
||||
.createLoanProduct(create4IProgressive().recalculationRestFrequencyType(RecalculationRestFrequencyType.DAILY) //
|
||||
);
|
||||
|
||||
Long loanId = applyAndApproveProgressiveLoan(client.getClientId(), loanProduct.getResourceId(), "1 January 2023", 8000.0, 10.0,
|
||||
4, null);
|
||||
loanIdRef.set(loanId);
|
||||
|
||||
disburseLoan(loanId, BigDecimal.valueOf(8000), "1 January 2023");
|
||||
|
||||
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanId);
|
||||
logLoanDetails(loanDetails);
|
||||
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 1, "01 February 2023", 1975.17, 0.0, 0.0, 66.67);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 2, "01 March 2023", 1991.63, 0.0, 0.0, 50.21);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 3, "01 April 2023", 2008.23, 0.0, 0.0, 33.61);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 4, "01 May 2023", 2024.97, 0.0, 0.0, 16.87);
|
||||
|
||||
});
|
||||
runAt("1 February 2023", () -> {
|
||||
Long loanId = loanIdRef.get();
|
||||
|
||||
inlineLoanCOBHelper.executeInlineCOB(List.of(loanId));
|
||||
|
||||
loanTransactionHelper.makeLoanRepayment("01 February 2023", 2041.84f, loanId.intValue());
|
||||
|
||||
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanId);
|
||||
logLoanDetails(loanDetails);
|
||||
|
||||
validateFullyPaidRepaymentPeriod(loanDetails, 1, "01 February 2023", 1975.17, 0.0, 0.0, 66.67);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 2, "01 March 2023", 1991.63, 0.0, 0.0, 50.21);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 3, "01 April 2023", 2008.23, 0.0, 0.0, 33.61);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 4, "01 May 2023", 2024.97, 0.0, 0.0, 16.87);
|
||||
|
||||
});
|
||||
runAt("10 March 2023", () -> {
|
||||
Long loanId = loanIdRef.get();
|
||||
|
||||
inlineLoanCOBHelper.executeInlineCOB(List.of(loanId));
|
||||
|
||||
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanId);
|
||||
logLoanDetails(loanDetails);
|
||||
|
||||
validateFullyPaidRepaymentPeriod(loanDetails, 1, "01 February 2023", 1975.17, 0.0, 0.0, 66.67);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 2, "01 March 2023", 1991.63, 0.0, 0.0, 50.21);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 3, "01 April 2023", 2003.41, 0.0, 0.0, 38.43);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 4, "01 May 2023", 2029.79, 0.0, 0.0, 16.91);
|
||||
|
||||
loanTransactionHelper.makeLoanRepayment("10 March 2023", 500.00f, loanId.intValue());
|
||||
|
||||
loanDetails = loanTransactionHelper.getLoanDetails(loanId);
|
||||
logLoanDetails(loanDetails);
|
||||
|
||||
validateFullyPaidRepaymentPeriod(loanDetails, 1, "01 February 2023", 1975.17, 0.0, 0.0, 66.67);
|
||||
validateRepaymentPeriod(loanDetails, 2, LocalDate.of(2023, 3, 1), 1991.63, 500.0, 1491.63, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 50.21,
|
||||
0, 50.21, 0, 500.0);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 3, "01 April 2023", 2003.41, 0.0, 0.0, 38.43);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 4, "01 May 2023", 2029.79, 0.0, 0.0, 16.91);
|
||||
|
||||
loanTransactionHelper.makeLoanRepayment("10 March 2023", 541.84f, loanId.intValue());
|
||||
|
||||
loanDetails = loanTransactionHelper.getLoanDetails(loanId);
|
||||
logLoanDetails(loanDetails);
|
||||
|
||||
validateFullyPaidRepaymentPeriod(loanDetails, 1, "01 February 2023", 1975.17, 0.0, 0.0, 66.67);
|
||||
validateRepaymentPeriod(loanDetails, 2, LocalDate.of(2023, 3, 1), 1991.63, 1041.84, 949.79, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 50.21,
|
||||
0, 50.21, 0, 1041.84);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 3, "01 April 2023", 2003.41, 0.0, 0.0, 38.43);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 4, "01 May 2023", 2029.79, 0.0, 0.0, 16.91);
|
||||
});
|
||||
runAt("20 March 2023", () -> {
|
||||
Long loanId = loanIdRef.get();
|
||||
|
||||
inlineLoanCOBHelper.executeInlineCOB(List.of(loanId));
|
||||
|
||||
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanId);
|
||||
logLoanDetails(loanDetails);
|
||||
|
||||
validateFullyPaidRepaymentPeriod(loanDetails, 1, "01 February 2023", 1975.17, 0.0, 0.0, 66.67);
|
||||
validateRepaymentPeriod(loanDetails, 2, LocalDate.of(2023, 3, 1), 1991.63, 1041.84, 949.79, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 50.21,
|
||||
0, 50.21, 0, 1041.84);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 3, "01 April 2023", 2000.85, 0.0, 0.0, 40.99);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 4, "01 May 2023", 2032.35, 0.0, 0.0, 16.94);
|
||||
|
||||
loanTransactionHelper.makeLoanRepayment("20 March 2023", 1000f, loanId.intValue());
|
||||
|
||||
loanDetails = loanTransactionHelper.getLoanDetails(loanId);
|
||||
logLoanDetails(loanDetails);
|
||||
|
||||
validateFullyPaidRepaymentPeriod(loanDetails, 1, "01 February 2023", 1975.17, 0.0, 0.0, 66.67);
|
||||
validateFullyPaidRepaymentPeriod(loanDetails, 2, "01 March 2023", 1991.63, 0.0, 0.0, 50.21, 2041.84);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 3, "01 April 2023", 2000.85, 0.0, 0.0, 40.99);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 4, "01 May 2023", 2032.35, 0.0, 0.0, 16.94);
|
||||
});
|
||||
payoffOnDateAndVerifyStatus("1 April 2023", loanIdRef.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void verifyLoanInstallmentRecalculatedIfThereIsOverdueInstallmentOn4IProgressiveLoanCOBStep() {
|
||||
AtomicReference<Long> loanIdRef = new AtomicReference<>();
|
||||
runAt("1 January 2023", () -> {
|
||||
PostLoanProductsResponse loanProduct = loanProductHelper.createLoanProduct(create4IProgressive());
|
||||
|
||||
Long loanId = applyAndApproveProgressiveLoan(client.getClientId(), loanProduct.getResourceId(), "1 January 2023", 8000.0, 10.0,
|
||||
4, null);
|
||||
loanIdRef.set(loanId);
|
||||
|
||||
disburseLoan(loanId, BigDecimal.valueOf(8000), "1 January 2023");
|
||||
|
||||
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanId);
|
||||
logLoanDetails(loanDetails);
|
||||
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 1, "01 February 2023", 1975.17, 0.0, 0.0, 66.67);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 2, "01 March 2023", 1991.63, 0.0, 0.0, 50.21);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 3, "01 April 2023", 2008.23, 0.0, 0.0, 33.61);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 4, "01 May 2023", 2024.97, 0.0, 0.0, 16.87);
|
||||
|
||||
});
|
||||
|
||||
runAt("1 February 2023", () -> {
|
||||
Long loanId = loanIdRef.get();
|
||||
|
||||
inlineLoanCOBHelper.executeInlineCOB(List.of(loanId));
|
||||
|
||||
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanId);
|
||||
logLoanDetails(loanDetails);
|
||||
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 1, "01 February 2023", 1975.17, 0.0, 0.0, 66.67);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 2, "01 March 2023", 1991.63, 0.0, 0.0, 50.21);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 3, "01 April 2023", 2008.23, 0.0, 0.0, 33.61);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 4, "01 May 2023", 2024.97, 0.0, 0.0, 16.87);
|
||||
});
|
||||
runAt("2 February 2023", () -> {
|
||||
Long loanId = loanIdRef.get();
|
||||
|
||||
inlineLoanCOBHelper.executeInlineCOB(List.of(loanId));
|
||||
|
||||
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanId);
|
||||
logLoanDetails(loanDetails);
|
||||
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 1, "01 February 2023", 1975.17, 0.0, 0.0, 66.67);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 2, "01 March 2023", 1975.17, 0.0, 0.0, 66.67);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 3, "01 April 2023", 2008.09, 0.0, 0.0, 33.75);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 4, "01 May 2023", 2041.57, 0.0, 0.0, 17.01);
|
||||
});
|
||||
runAt("20 February 2023", () -> {
|
||||
Long loanId = loanIdRef.get();
|
||||
|
||||
inlineLoanCOBHelper.executeInlineCOB(List.of(loanId));
|
||||
|
||||
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanId);
|
||||
logLoanDetails(loanDetails);
|
||||
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 1, "01 February 2023", 1975.17, 0.0, 0.0, 66.67);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 2, "01 March 2023", 1975.17, 0.0, 0.0, 66.67);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 3, "01 April 2023", 2008.09, 0.0, 0.0, 33.75);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 4, "01 May 2023", 2041.57, 0.0, 0.0, 17.01);
|
||||
});
|
||||
runAt("2 March 2023", () -> {
|
||||
Long loanId = loanIdRef.get();
|
||||
@@ -110,16 +455,225 @@ public class LoanInterestRecalculationCOBTest extends BaseLoanIntegrationTest {
|
||||
inlineLoanCOBHelper.executeInlineCOB(List.of(loanId));
|
||||
|
||||
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanId);
|
||||
logLoanDetails(loanDetails);
|
||||
|
||||
loanDetails.getRepaymentSchedule().getPeriods().forEach(p -> log.info("validateRepaymentPeriod after: {} {} {}", p.getPeriod(),
|
||||
p.getPrincipalOriginalDue(), p.getInterestOriginalDue()));//
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 1, "01 February 2023", 1975.17, 0.0, 0.0, 66.67);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 2, "01 March 2023", 1975.17, 0.0, 0.0, 66.67);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 3, "01 April 2023", 1975.17, 0.0, 0.0, 66.67);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 4, "01 May 2023", 2074.49, 0.0, 0.0, 17.29);
|
||||
});
|
||||
payoffOnDateAndVerifyStatus("1 February 2023", loanIdRef.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void verifyLoanInstallmentRecalculatedIfThereIsOverdueInstallmentOn4IProgressiveLoanCOBStepLatePaidPaidOnTimeLatePaidPayoffOnTime() {
|
||||
AtomicReference<Long> loanIdRef = new AtomicReference<>();
|
||||
runAt("1 January 2023", () -> {
|
||||
PostLoanProductsResponse loanProduct = loanProductHelper.createLoanProduct(create4IProgressive());
|
||||
|
||||
Long loanId = applyAndApproveProgressiveLoan(client.getClientId(), loanProduct.getResourceId(), "1 January 2023", 8000.0, 10.0,
|
||||
4, null);
|
||||
loanIdRef.set(loanId);
|
||||
|
||||
disburseLoan(loanId, BigDecimal.valueOf(8000), "1 January 2023");
|
||||
|
||||
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanId);
|
||||
logLoanDetails(loanDetails);
|
||||
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 1, "01 February 2023", 1975.17, 0.0, 0.0, 66.67);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 2, "01 March 2023", 1991.63, 0.0, 0.0, 50.21);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 3, "01 April 2023", 2008.23, 0.0, 0.0, 33.61);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 4, "01 May 2023", 2024.97, 0.0, 0.0, 16.87);
|
||||
|
||||
validateRepaymentPeriod(loanDetails, 1, LocalDate.of(2023, 2, 1), 1700.66, 0.0, 0.0, 815.34);
|
||||
validateRepaymentPeriod(loanDetails, 2, LocalDate.of(2023, 3, 1), 1936.12, 0.0, 0.0, 579.88);
|
||||
validateRepaymentPeriod(loanDetails, 3, LocalDate.of(2023, 4, 1), 2071.31, 0.0, 0.0, 444.69);
|
||||
validateRepaymentPeriod(loanDetails, 4, LocalDate.of(2023, 5, 1), 2291.91, 0.0, 0.0, 226.05);
|
||||
});
|
||||
|
||||
runAt("2 February 2023", () -> {
|
||||
Long loanId = loanIdRef.get();
|
||||
|
||||
inlineLoanCOBHelper.executeInlineCOB(List.of(loanId));
|
||||
|
||||
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanId);
|
||||
logLoanDetails(loanDetails);
|
||||
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 1, "01 February 2023", 1975.17, 0.0, 0.0, 66.67);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 2, "01 March 2023", 1975.17, 0.0, 0.0, 66.67);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 3, "01 April 2023", 2008.09, 0.0, 0.0, 33.75);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 4, "01 May 2023", 2041.57, 0.0, 0.0, 17.01);
|
||||
});
|
||||
runAt("15 February 2023", () -> {
|
||||
Long loanId = loanIdRef.get();
|
||||
inlineLoanCOBHelper.executeInlineCOB(List.of(loanId));
|
||||
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanId);
|
||||
logLoanDetails(loanDetails);
|
||||
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 1, "01 February 2023", 1975.17, 0.0, 0.0, 66.67);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 2, "01 March 2023", 1975.17, 0.0, 0.0, 66.67);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 3, "01 April 2023", 2008.09, 0.0, 0.0, 33.75);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 4, "01 May 2023", 2041.57, 0.0, 0.0, 17.01);
|
||||
|
||||
loanTransactionHelper.makeLoanRepayment("15 February 2023", 500.0F, loanId.intValue());
|
||||
|
||||
loanDetails = loanTransactionHelper.getLoanDetails(loanId);
|
||||
logLoanDetails(loanDetails);
|
||||
|
||||
validateRepaymentPeriod(loanDetails, 1, LocalDate.of(2023, 2, 1), 1975.17, 500, 1475.17, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 66.67, 0,
|
||||
66.67, 0, 500);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 2, "01 March 2023", 1975.17, 0.0, 0.0, 66.67);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 3, "01 April 2023", 2008.09, 0.0, 0.0, 33.75);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 4, "01 May 2023", 2041.57, 0.0, 0.0, 17.01);
|
||||
|
||||
loanTransactionHelper.makeLoanRepayment("15 February 2023", 500f, loanId.intValue());
|
||||
|
||||
loanDetails = loanTransactionHelper.getLoanDetails(loanId);
|
||||
logLoanDetails(loanDetails);
|
||||
|
||||
validateRepaymentPeriod(loanDetails, 1, LocalDate.of(2023, 2, 1), 1975.17, 1000, 975.17, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 66.67, 0,
|
||||
66.67, 0, 1000);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 2, "01 March 2023", 1975.17, 0.0, 0.0, 66.67);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 3, "01 April 2023", 2008.09, 0.0, 0.0, 33.75);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 4, "01 May 2023", 2041.57, 0.0, 0.0, 17.01);
|
||||
|
||||
});
|
||||
runAt("20 February 2023", () -> {
|
||||
Long loanId = loanIdRef.get();
|
||||
|
||||
loanTransactionHelper.makeLoanRepayment("20 February 2023", 1041.84f, loanId.intValue());
|
||||
|
||||
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanId);
|
||||
logLoanDetails(loanDetails);
|
||||
|
||||
validateFullyPaidRepaymentPeriod(loanDetails, 1, "01 February 2023", 1975.17, 0.0, 0.0, 66.67, 2041.84);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 2, "01 March 2023", 1975.17, 0.0, 0.0, 66.67);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 3, "01 April 2023", 2008.09, 0.0, 0.0, 33.75);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 4, "01 May 2023", 2041.57, 0.0, 0.0, 17.01);
|
||||
|
||||
});
|
||||
runAt("1 March 2023", () -> {
|
||||
Long loanId = loanIdRef.get();
|
||||
|
||||
loanTransactionHelper.makeLoanRepayment("01 March 2023", 2041.84f, loanId.intValue());
|
||||
|
||||
inlineLoanCOBHelper.executeInlineCOB(List.of(loanId));
|
||||
|
||||
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanId);
|
||||
logLoanDetails(loanDetails);
|
||||
|
||||
validateFullyPaidRepaymentPeriod(loanDetails, 1, "01 February 2023", 1975.17, 0.0, 0.0, 66.67, 2041.84);
|
||||
validateFullyPaidRepaymentPeriod(loanDetails, 2, "01 March 2023", 1975.17, 0.0, 0.0, 66.67);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 3, "01 April 2023", 2008.09, 0.0, 0.0, 33.75);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 4, "01 May 2023", 2041.57, 0.0, 0.0, 17.01);
|
||||
});
|
||||
runAt("2 April 2023", () -> {
|
||||
Long loanId = loanIdRef.get();
|
||||
|
||||
inlineLoanCOBHelper.executeInlineCOB(List.of(loanId));
|
||||
|
||||
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanId);
|
||||
logLoanDetails(loanDetails);
|
||||
|
||||
validateRepaymentPeriod(loanDetails, 1, LocalDate.of(2023, 2, 1), 1975.17, 1975.17, 0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 66.67,
|
||||
66.67, 0, 0, 2041.84);
|
||||
validateFullyPaidRepaymentPeriod(loanDetails, 2, "01 March 2023", 1975.17, 0.0, 0.0, 66.67);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 3, "01 April 2023", 2008.09, 0.0, 0.0, 33.75);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 4, "01 May 2023", 2041.57, 0.0, 0.0, 33.75);
|
||||
});
|
||||
runAt("1 May 2023", () -> {
|
||||
Long loanId = loanIdRef.get();
|
||||
|
||||
loanTransactionHelper.makeLoanRepayment("15 April 2023", 2041.84f, loanId.intValue());
|
||||
loanTransactionHelper.makeLoanRepayment("01 May 2023", 2075.32f, loanId.intValue());
|
||||
|
||||
inlineLoanCOBHelper.executeInlineCOB(List.of(loanId));
|
||||
|
||||
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanId);
|
||||
logLoanDetails(loanDetails);
|
||||
|
||||
validateRepaymentPeriod(loanDetails, 1, LocalDate.of(2023, 2, 1), 1975.17, 1975.17, 0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 66.67,
|
||||
66.67, 0, 0, 2041.84);
|
||||
validateFullyPaidRepaymentPeriod(loanDetails, 2, "01 March 2023", 1975.17, 0.0, 0.0, 66.67);
|
||||
validateRepaymentPeriod(loanDetails, 3, LocalDate.of(2023, 4, 1), 2008.09, 2008.09, 0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 33.75,
|
||||
33.75, 0, 0, 2041.84);
|
||||
validateFullyPaidRepaymentPeriod(loanDetails, 4, "01 May 2023", 2041.57, 0.0, 0.0, 33.75);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void verifyLoanInstallmentRecalculatedIfThereIsOverdueInstallmentOn4IProgressiveLoanCOBStepOnePaid() {
|
||||
AtomicReference<Long> loanIdRef = new AtomicReference<>();
|
||||
runAt("1 January 2023", () -> {
|
||||
PostLoanProductsResponse loanProduct = loanProductHelper.createLoanProduct(create4IProgressive());
|
||||
|
||||
Long loanId = applyAndApproveProgressiveLoan(client.getClientId(), loanProduct.getResourceId(), "1 January 2023", 8000.0, 10.0,
|
||||
4, null);
|
||||
loanIdRef.set(loanId);
|
||||
|
||||
disburseLoan(loanId, BigDecimal.valueOf(8000), "1 January 2023");
|
||||
|
||||
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanId);
|
||||
logLoanDetails(loanDetails);
|
||||
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 1, "01 February 2023", 1975.17, 0.0, 0.0, 66.67);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 2, "01 March 2023", 1991.63, 0.0, 0.0, 50.21);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 3, "01 April 2023", 2008.23, 0.0, 0.0, 33.61);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 4, "01 May 2023", 2024.97, 0.0, 0.0, 16.87);
|
||||
|
||||
});
|
||||
|
||||
runAt("1 February 2023", () -> {
|
||||
Long loanId = loanIdRef.get();
|
||||
|
||||
inlineLoanCOBHelper.executeInlineCOB(List.of(loanId));
|
||||
|
||||
loanTransactionHelper.makeLoanRepayment("01 February 2023", 2041.84f, loanId.intValue());
|
||||
|
||||
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanId);
|
||||
logLoanDetails(loanDetails);
|
||||
|
||||
validateFullyPaidRepaymentPeriod(loanDetails, 1, "01 February 2023", 1975.17, 0.0, 0.0, 66.67);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 2, "01 March 2023", 1991.63, 0.0, 0.0, 50.21);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 3, "01 April 2023", 2008.23, 0.0, 0.0, 33.61);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 4, "01 May 2023", 2024.97, 0.0, 0.0, 16.87);
|
||||
});
|
||||
runAt("1 March 2023", () -> {
|
||||
Long loanId = loanIdRef.get();
|
||||
|
||||
inlineLoanCOBHelper.executeInlineCOB(List.of(loanId));
|
||||
|
||||
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanId);
|
||||
logLoanDetails(loanDetails);
|
||||
|
||||
validateFullyPaidRepaymentPeriod(loanDetails, 1, "01 February 2023", 1975.17, 0.0, 0.0, 66.67);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 2, "01 March 2023", 1991.63, 0.0, 0.0, 50.21);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 3, "01 April 2023", 2008.23, 0.0, 0.0, 33.61);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 4, "01 May 2023", 2024.97, 0.0, 0.0, 16.87);
|
||||
});
|
||||
runAt("2 March 2023", () -> {
|
||||
Long loanId = loanIdRef.get();
|
||||
|
||||
inlineLoanCOBHelper.executeInlineCOB(List.of(loanId));
|
||||
|
||||
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanId);
|
||||
logLoanDetails(loanDetails);
|
||||
|
||||
validateFullyPaidRepaymentPeriod(loanDetails, 1, "01 February 2023", 1975.17, 0.0, 0.0, 66.67);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 2, "01 March 2023", 1991.63, 0.0, 0.0, 50.21);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 3, "01 April 2023", 1991.63, 0.0, 0.0, 50.21);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 4, "01 May 2023", 2041.57, 0.0, 0.0, 17.01);
|
||||
});
|
||||
runAt("20 March 2023", () -> {
|
||||
Long loanId = loanIdRef.get();
|
||||
|
||||
inlineLoanCOBHelper.executeInlineCOB(List.of(loanId));
|
||||
|
||||
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanId);
|
||||
logLoanDetails(loanDetails);
|
||||
|
||||
validateFullyPaidRepaymentPeriod(loanDetails, 1, "01 February 2023", 1975.17, 0.0, 0.0, 66.67);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 2, "01 March 2023", 1991.63, 0.0, 0.0, 50.21);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 3, "01 April 2023", 1991.63, 0.0, 0.0, 50.21);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 4, "01 May 2023", 2041.57, 0.0, 0.0, 17.01);
|
||||
});
|
||||
payoffOnDateAndVerifyStatus("1 March 2023", loanIdRef.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -162,38 +716,72 @@ public class LoanInterestRecalculationCOBTest extends BaseLoanIntegrationTest {
|
||||
validateRepaymentPeriod(loanDetails, 2, LocalDate.of(2023, 3, 1), 4216.94, 0.0, 4216.94, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 403.23,
|
||||
0.0, 403.23, 0.0, 0.0);
|
||||
});
|
||||
|
||||
payoffOnDateAndVerifyStatus("1 March 2023", loanIdRef.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void verifyLoanInstallmentRecalculatedIfThereIsOverdueInstallmentOnProgressiveLoanJob() {
|
||||
AtomicReference<Long> loanIdRef = new AtomicReference<>();
|
||||
runAt("1 January 2023", () -> {
|
||||
PostLoanProductsResponse loanProduct = loanProductHelper.createLoanProduct(
|
||||
createOnePeriod30DaysPeriodicAccrualProductWithAdvancedPaymentAllocationAndInterestRecalculation(16.0, 4)
|
||||
.maxInterestRatePerPeriod(120.0).maxPrincipal(10000.0));
|
||||
PostLoanProductsResponse loanProduct = loanProductHelper.createLoanProduct(create4IProgressive());
|
||||
|
||||
Long loanId = applyAndApproveLoan(client.getClientId(), loanProduct.getResourceId(), "1 January 2023", 8000.0, 4,
|
||||
postLoansRequest -> postLoansRequest.loanTermFrequency(4)//
|
||||
.loanTermFrequencyType(MONTHS)//
|
||||
.interestRatePerPeriod(BigDecimal.valueOf(120.0)).interestCalculationPeriodType(DAYS)//
|
||||
.transactionProcessingStrategyCode(LoanProductTestBuilder.ADVANCED_PAYMENT_ALLOCATION_STRATEGY)//
|
||||
.repaymentEvery(1)//
|
||||
.repaymentFrequencyType(MONTHS)//
|
||||
.principal(BigDecimal.valueOf(8000.0)).maxOutstandingLoanBalance(BigDecimal.valueOf(100000.0)));
|
||||
Long loanId = applyAndApproveProgressiveLoan(client.getClientId(), loanProduct.getResourceId(), "1 January 2023", 8000.0, 10.0,
|
||||
4, null);
|
||||
loanIdRef.set(loanId);
|
||||
|
||||
disburseLoan(loanId, BigDecimal.valueOf(8000), "1 January 2023");
|
||||
|
||||
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanId);
|
||||
logLoanDetails(loanDetails);
|
||||
|
||||
loanDetails.getRepaymentSchedule().getPeriods().forEach(p -> log.info("validateRepaymentPeriod before: {} {} {}", p.getPeriod(),
|
||||
p.getPrincipalOriginalDue(), p.getInterestOriginalDue()));//
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 1, "01 February 2023", 1975.17, 0.0, 0.0, 66.67);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 2, "01 March 2023", 1991.63, 0.0, 0.0, 50.21);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 3, "01 April 2023", 2008.23, 0.0, 0.0, 33.61);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 4, "01 May 2023", 2024.97, 0.0, 0.0, 16.87);
|
||||
|
||||
validateRepaymentPeriod(loanDetails, 1, LocalDate.of(2023, 2, 1), 1700.66, 0.0, 0.0, 815.34);
|
||||
validateRepaymentPeriod(loanDetails, 2, LocalDate.of(2023, 3, 1), 1936.12, 0.0, 0.0, 579.88);
|
||||
validateRepaymentPeriod(loanDetails, 3, LocalDate.of(2023, 4, 1), 2071.31, 0.0, 0.0, 444.69);
|
||||
validateRepaymentPeriod(loanDetails, 4, LocalDate.of(2023, 5, 1), 2291.91, 0.0, 0.0, 226.05);
|
||||
});
|
||||
|
||||
runAt("1 February 2023", () -> {
|
||||
Long loanId = loanIdRef.get();
|
||||
|
||||
schedulerJobHelper.executeAndAwaitJob("Update Loan Arrears Ageing");
|
||||
schedulerJobHelper.executeAndAwaitJob("Recalculate Interest For Loans");
|
||||
|
||||
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanId);
|
||||
logLoanDetails(loanDetails);
|
||||
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 1, "01 February 2023", 1975.17, 0.0, 0.0, 66.67);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 2, "01 March 2023", 1991.63, 0.0, 0.0, 50.21);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 3, "01 April 2023", 2008.23, 0.0, 0.0, 33.61);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 4, "01 May 2023", 2024.97, 0.0, 0.0, 16.87);
|
||||
});
|
||||
runAt("2 February 2023", () -> {
|
||||
Long loanId = loanIdRef.get();
|
||||
|
||||
schedulerJobHelper.executeAndAwaitJob("Update Loan Arrears Ageing");
|
||||
schedulerJobHelper.executeAndAwaitJob("Recalculate Interest For Loans");
|
||||
|
||||
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanId);
|
||||
logLoanDetails(loanDetails);
|
||||
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 1, "01 February 2023", 1975.17, 0.0, 0.0, 66.67);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 2, "01 March 2023", 1975.17, 0.0, 0.0, 66.67);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 3, "01 April 2023", 2008.09, 0.0, 0.0, 33.75);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 4, "01 May 2023", 2041.57, 0.0, 0.0, 17.01);
|
||||
});
|
||||
runAt("20 February 2023", () -> {
|
||||
Long loanId = loanIdRef.get();
|
||||
|
||||
schedulerJobHelper.executeAndAwaitJob("Update Loan Arrears Ageing");
|
||||
schedulerJobHelper.executeAndAwaitJob("Recalculate Interest For Loans");
|
||||
|
||||
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanId);
|
||||
logLoanDetails(loanDetails);
|
||||
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 1, "01 February 2023", 1975.17, 0.0, 0.0, 66.67);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 2, "01 March 2023", 1975.17, 0.0, 0.0, 66.67);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 3, "01 April 2023", 2008.09, 0.0, 0.0, 33.75);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 4, "01 May 2023", 2041.57, 0.0, 0.0, 17.01);
|
||||
});
|
||||
runAt("2 March 2023", () -> {
|
||||
Long loanId = loanIdRef.get();
|
||||
@@ -202,27 +790,30 @@ public class LoanInterestRecalculationCOBTest extends BaseLoanIntegrationTest {
|
||||
schedulerJobHelper.executeAndAwaitJob("Recalculate Interest For Loans");
|
||||
|
||||
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanId);
|
||||
logLoanDetails(loanDetails);
|
||||
|
||||
loanDetails.getRepaymentSchedule().getPeriods().forEach(p -> log.info("validateRepaymentPeriod after: {} {} {}", p.getPeriod(),
|
||||
p.getPrincipalOriginalDue(), p.getInterestOriginalDue()));//
|
||||
|
||||
validateRepaymentPeriod(loanDetails, 1, LocalDate.of(2023, 2, 1), 1700.66, 0.0, 0.0, 815.34);
|
||||
validateRepaymentPeriod(loanDetails, 2, LocalDate.of(2023, 3, 1), 1936.12, 0.0, 0.0, 579.88);
|
||||
validateRepaymentPeriod(loanDetails, 3, LocalDate.of(2023, 4, 1), 2071.31, 0.0, 0.0, 444.69);
|
||||
validateRepaymentPeriod(loanDetails, 4, LocalDate.of(2023, 5, 1), 2291.91, 0.0, 0.0, 226.05);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 1, "01 February 2023", 1975.17, 0.0, 0.0, 66.67);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 2, "01 March 2023", 1975.17, 0.0, 0.0, 66.67);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 3, "01 April 2023", 1975.17, 0.0, 0.0, 66.67);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 4, "01 May 2023", 2074.49, 0.0, 0.0, 17.29);
|
||||
});
|
||||
|
||||
payoffOnDateAndVerifyStatus("1 February 2023", loanIdRef.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void verifyLoanInstallmentRecalculatedIfThereIsOverdueInstallmentOnCumulativeLoanJob() {
|
||||
AtomicReference<Long> loanIdRef = new AtomicReference<>();
|
||||
runAt("1 January 2023", () -> {
|
||||
PostLoanProductsResponse loanProduct = loanProductHelper.createLoanProduct(createOnePeriod30DaysPeriodicAccrualProduct(10.0)
|
||||
.isInterestRecalculationEnabled(true)//
|
||||
.maxPrincipal(10000.0).minNumberOfRepayments(1).rescheduleStrategyMethod(1).recalculationRestFrequencyType(MONTHS)
|
||||
.recalculationRestFrequencyInterval(1).recalculationCompoundingFrequencyType(MONTHS)
|
||||
.recalculationCompoundingFrequencyInterval(30).interestRecalculationCompoundingMethod(1));
|
||||
PostLoanProductsResponse loanProduct = loanProductHelper
|
||||
.createLoanProduct(createOnePeriod30DaysPeriodicAccrualProduct(10.0).isInterestRecalculationEnabled(true)//
|
||||
.maxPrincipal(10000.0) //
|
||||
.minNumberOfRepayments(1) //
|
||||
.rescheduleStrategyMethod(1) //
|
||||
.recalculationRestFrequencyType(MONTHS) //
|
||||
.recalculationRestFrequencyInterval(1) //
|
||||
.recalculationCompoundingFrequencyType(MONTHS) //
|
||||
.recalculationCompoundingFrequencyInterval(30) //
|
||||
.interestRecalculationCompoundingMethod(1)); //
|
||||
|
||||
Long loanId = applyAndApproveLoan(client.getClientId(), loanProduct.getResourceId(), "1 January 2023", 8000.0, 2,
|
||||
postLoansRequest -> postLoansRequest.loanTermFrequency(2)//
|
||||
@@ -255,7 +846,153 @@ public class LoanInterestRecalculationCOBTest extends BaseLoanIntegrationTest {
|
||||
validateRepaymentPeriod(loanDetails, 2, LocalDate.of(2023, 3, 1), 4216.94, 0.0, 4216.94, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 403.23,
|
||||
0.0, 403.23, 0.0, 0.0);
|
||||
});
|
||||
payoffOnDateAndVerifyStatus("1 March 2023", loanIdRef.get());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void verifyLoanInstallmentRecalculatedIfThereIsOverdueInstallmentOn4IProgressiveLoanMultiOverdueSingleRepayment() {
|
||||
AtomicReference<Long> loanIdRef = new AtomicReference<>();
|
||||
runAt("1 January 2023", () -> {
|
||||
PostLoanProductsResponse loanProduct = loanProductHelper.createLoanProduct(create4IProgressive());
|
||||
|
||||
Long loanId = applyAndApproveProgressiveLoan(client.getClientId(), loanProduct.getResourceId(), "1 January 2023", 8000.0, 10.0,
|
||||
4, null);
|
||||
loanIdRef.set(loanId);
|
||||
|
||||
disburseLoan(loanId, BigDecimal.valueOf(8000), "1 January 2023");
|
||||
|
||||
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanId);
|
||||
logLoanDetails(loanDetails);
|
||||
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 1, "01 February 2023", 1975.17, 0.0, 0.0, 66.67);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 2, "01 March 2023", 1991.63, 0.0, 0.0, 50.21);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 3, "01 April 2023", 2008.23, 0.0, 0.0, 33.61);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 4, "01 May 2023", 2024.97, 0.0, 0.0, 16.87);
|
||||
|
||||
});
|
||||
runAt("1 March 2023", () -> {
|
||||
Long loanId = loanIdRef.get();
|
||||
|
||||
loanTransactionHelper.makeLoanRepayment("01 March 2023", 4083.68f, loanId.intValue());
|
||||
|
||||
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanId);
|
||||
logLoanDetails(loanDetails);
|
||||
|
||||
validateFullyPaidRepaymentPeriod(loanDetails, 1, "01 February 2023", 1975.17, 0.0, 0.0, 66.67, 2041.84);
|
||||
validateFullyPaidRepaymentPeriod(loanDetails, 2, "01 March 2023", 1975.17, 0.0, 0.0, 66.67);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 3, "01 April 2023", 2008.09, 0.0, 0.0, 33.75);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 4, "01 May 2023", 2041.57, 0.0, 0.0, 17.01);
|
||||
});
|
||||
payoffOnDateAndVerifyStatus("1 April 2023", loanIdRef.get());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void verifyLoanInstallmentRecalculatedIfThereIsOverdueInstallmentOnCumulativeLoanJobSameAsRepaymentPeriod() {
|
||||
AtomicReference<Long> loanIdRef = new AtomicReference<>();
|
||||
runAt("1 January 2023", () -> {
|
||||
PostLoanProductsResponse loanProduct = loanProductHelper
|
||||
.createLoanProduct(createOnePeriod30DaysPeriodicAccrualProduct(10.0).isInterestRecalculationEnabled(true)//
|
||||
.maxPrincipal(10000.0) //
|
||||
.minNumberOfRepayments(1) //
|
||||
.maxNumberOfRepayments(10) //
|
||||
.rescheduleStrategyMethod(1) //
|
||||
.daysInYearType(360) //
|
||||
.daysInMonthType(30) //
|
||||
.recalculationRestFrequencyType(SAME_AS_REPAYMENT_PERIOD) //
|
||||
.recalculationRestFrequencyInterval(1) //
|
||||
.recalculationCompoundingFrequencyType(1) //
|
||||
.recalculationCompoundingFrequencyInterval(1) //
|
||||
.interestRecalculationCompoundingMethod(0)); //
|
||||
|
||||
Long loanId = applyAndApproveLoan(client.getClientId(), loanProduct.getResourceId(), "1 January 2023", 8000.0, 2,
|
||||
postLoansRequest -> postLoansRequest.loanTermFrequency(4)//
|
||||
.loanTermFrequencyType(MONTHS)//
|
||||
.numberOfRepayments(4).interestRatePerPeriod(BigDecimal.valueOf(10.0)).interestCalculationPeriodType(DAYS)//
|
||||
.repaymentEvery(1)//
|
||||
.repaymentFrequencyType(MONTHS)//
|
||||
.principal(BigDecimal.valueOf(8000.0)).maxOutstandingLoanBalance(BigDecimal.valueOf(100000.0)));
|
||||
loanIdRef.set(loanId);
|
||||
|
||||
disburseLoan(loanId, BigDecimal.valueOf(8000), "1 January 2023");
|
||||
|
||||
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanId);
|
||||
logLoanDetails(loanDetails);
|
||||
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 1, "01 February 2023", 1724.0, 0.0, 0.0, 800.0);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 2, "01 March 2023", 1896.4, 0.0, 0.0, 627.6);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 3, "01 April 2023", 2086.04, 0.0, 0.0, 437.96);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 4, "01 May 2023", 2293.56, 0.0, 0.0, 229.36);
|
||||
});
|
||||
runAt("8 February 2023", () -> {
|
||||
Long loanId = loanIdRef.get();
|
||||
|
||||
schedulerJobHelper.executeAndAwaitJob("Update Loan Arrears Ageing");
|
||||
schedulerJobHelper.executeAndAwaitJob("Recalculate Interest For Loans");
|
||||
|
||||
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanId);
|
||||
logLoanDetails(loanDetails);
|
||||
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 1, "01 February 2023", 1724.0, 0.0, 0.0, 800.0);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 2, "01 March 2023", 1724.0, 0.0, 0.0, 800.0);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 3, "01 April 2023", 2068.8, 0.0, 0.0, 455.2);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 4, "01 May 2023", 2483.2, 0.0, 0.0, 248.32);
|
||||
});
|
||||
payoffOnDateAndVerifyStatus("1 May 2023", loanIdRef.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void verifyLoanInstallmentRecalculatedIfThereIsOverdueInstallmentOnCumulativeLoanCOBSameAsRepaymentPeriod() {
|
||||
AtomicReference<Long> loanIdRef = new AtomicReference<>();
|
||||
runAt("1 January 2023", () -> {
|
||||
PostLoanProductsResponse loanProduct = loanProductHelper
|
||||
.createLoanProduct(createOnePeriod30DaysPeriodicAccrualProduct(10.0).isInterestRecalculationEnabled(true)//
|
||||
.maxPrincipal(10000.0) //
|
||||
.minNumberOfRepayments(1) //
|
||||
.maxNumberOfRepayments(10) //
|
||||
.rescheduleStrategyMethod(1) //
|
||||
.daysInYearType(360) //
|
||||
.daysInMonthType(30) //
|
||||
.recalculationRestFrequencyType(SAME_AS_REPAYMENT_PERIOD) //
|
||||
.recalculationRestFrequencyInterval(1) //
|
||||
.recalculationCompoundingFrequencyType(1) //
|
||||
.recalculationCompoundingFrequencyInterval(1) //
|
||||
.interestRecalculationCompoundingMethod(0)); //
|
||||
|
||||
Long loanId = applyAndApproveLoan(client.getClientId(), loanProduct.getResourceId(), "1 January 2023", 8000.0, 2,
|
||||
postLoansRequest -> postLoansRequest.loanTermFrequency(4)//
|
||||
.loanTermFrequencyType(MONTHS)//
|
||||
.numberOfRepayments(4).interestRatePerPeriod(BigDecimal.valueOf(10.0)).interestCalculationPeriodType(DAYS)//
|
||||
.repaymentEvery(1)//
|
||||
.repaymentFrequencyType(MONTHS)//
|
||||
.principal(BigDecimal.valueOf(8000.0)).maxOutstandingLoanBalance(BigDecimal.valueOf(100000.0)));
|
||||
loanIdRef.set(loanId);
|
||||
|
||||
disburseLoan(loanId, BigDecimal.valueOf(8000), "1 January 2023");
|
||||
|
||||
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanId);
|
||||
logLoanDetails(loanDetails);
|
||||
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 1, "01 February 2023", 1724.0, 0.0, 0.0, 800.0);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 2, "01 March 2023", 1896.4, 0.0, 0.0, 627.6);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 3, "01 April 2023", 2086.04, 0.0, 0.0, 437.96);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 4, "01 May 2023", 2293.56, 0.0, 0.0, 229.36);
|
||||
});
|
||||
runAt("8 February 2023", () -> {
|
||||
Long loanId = loanIdRef.get();
|
||||
|
||||
inlineLoanCOBHelper.executeInlineCOB(List.of(loanId));
|
||||
|
||||
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanId);
|
||||
logLoanDetails(loanDetails);
|
||||
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 1, "01 February 2023", 1724.0, 0.0, 0.0, 800.0);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 2, "01 March 2023", 1724.0, 0.0, 0.0, 800.0);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 3, "01 April 2023", 2068.8, 0.0, 0.0, 455.2);
|
||||
validateFullyUnpaidRepaymentPeriod(loanDetails, 4, "01 May 2023", 2483.2, 0.0, 0.0, 248.32);
|
||||
});
|
||||
payoffOnDateAndVerifyStatus("1 May 2023", loanIdRef.get());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user