[FINERACT-1958] Repayment schedule recalculation fix for multidisburse down payment
This commit is contained in:
+9
@@ -284,6 +284,15 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
// backup for pre-close transaction
|
// backup for pre-close transaction
|
||||||
updateCompoundingDetails(scheduleParams, periodStartDateApplicableForInterest);
|
updateCompoundingDetails(scheduleParams, periodStartDateApplicableForInterest);
|
||||||
|
|
||||||
|
if (loanApplicationTerms.isMultiDisburseLoan() && loanApplicationTerms.isDownPaymentEnabled()) {
|
||||||
|
long numberOfDownPaymentPeriods = periods.stream() //
|
||||||
|
.filter(LoanScheduleModelPeriod::isDownPaymentPeriod).count();
|
||||||
|
Long notApplicableForInstallmentRecalculationDownPaymentPeriods = numberOfDownPaymentPeriods - 1;
|
||||||
|
// Only the first down payment installment counts for the number of repayments
|
||||||
|
scheduleParams.setPeriodNumber(
|
||||||
|
scheduleParams.getPeriodNumber() - notApplicableForInstallmentRecalculationDownPaymentPeriods.intValue());
|
||||||
|
}
|
||||||
|
|
||||||
// 5 determine principal,interest of repayment period
|
// 5 determine principal,interest of repayment period
|
||||||
PrincipalInterest principalInterestForThisPeriod = calculatePrincipalInterestComponentsForPeriod(
|
PrincipalInterest principalInterestForThisPeriod = calculatePrincipalInterestComponentsForPeriod(
|
||||||
this.paymentPeriodsInOneYearCalculator, currentPeriodParams.getInterestCalculationGraceOnRepaymentPeriodFraction(),
|
this.paymentPeriodsInOneYearCalculator, currentPeriodParams.getInterestCalculationGraceOnRepaymentPeriodFraction(),
|
||||||
|
|||||||
+17
-1
@@ -1221,7 +1221,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService, Loa
|
|||||||
periods.add(periodData);
|
periods.add(periodData);
|
||||||
this.outstandingLoanPrincipalBalance = this.outstandingLoanPrincipalBalance.add(data.getPrincipal());
|
this.outstandingLoanPrincipalBalance = this.outstandingLoanPrincipalBalance.add(data.getPrincipal());
|
||||||
disbursementPeriodIds.add(data.getId());
|
disbursementPeriodIds.add(data.getId());
|
||||||
} else if (data.isDueForDisbursement(fromDate, dueDate)) {
|
} else if (data.isDueForDisbursement(fromDate, dueDate) && !disbursementPeriodIds.contains(data.getId())) {
|
||||||
if (!excludePastUndisbursed || data.isDisbursed()
|
if (!excludePastUndisbursed || data.isDisbursed()
|
||||||
|| !data.disbursementDate().isBefore(DateUtils.getBusinessLocalDate())) {
|
|| !data.disbursementDate().isBefore(DateUtils.getBusinessLocalDate())) {
|
||||||
principal = principal.add(data.getPrincipal());
|
principal = principal.add(data.getPrincipal());
|
||||||
@@ -1235,7 +1235,23 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService, Loa
|
|||||||
}
|
}
|
||||||
periods.add(periodData);
|
periods.add(periodData);
|
||||||
this.outstandingLoanPrincipalBalance = this.outstandingLoanPrincipalBalance.add(data.getPrincipal());
|
this.outstandingLoanPrincipalBalance = this.outstandingLoanPrincipalBalance.add(data.getPrincipal());
|
||||||
|
disbursementPeriodIds.add(data.getId());
|
||||||
}
|
}
|
||||||
|
} else if (fromDate.equals(dueDate) && data.disbursementDate().equals(fromDate)
|
||||||
|
&& !disbursementPeriodIds.contains(data.getId())) {
|
||||||
|
principal = principal.add(data.getPrincipal());
|
||||||
|
LoanSchedulePeriodData periodData = null;
|
||||||
|
if (data.getChargeAmount() == null) {
|
||||||
|
periodData = LoanSchedulePeriodData.disbursementOnlyPeriod(data.disbursementDate(), data.getPrincipal(),
|
||||||
|
disbursementChargeAmount, data.isDisbursed());
|
||||||
|
} else {
|
||||||
|
periodData = LoanSchedulePeriodData.disbursementOnlyPeriod(data.disbursementDate(), data.getPrincipal(),
|
||||||
|
disbursementChargeAmount.add(data.getChargeAmount()).subtract(waivedChargeAmount),
|
||||||
|
data.isDisbursed());
|
||||||
|
}
|
||||||
|
periods.add(periodData);
|
||||||
|
this.outstandingLoanPrincipalBalance = this.outstandingLoanPrincipalBalance.add(data.getPrincipal());
|
||||||
|
disbursementPeriodIds.add(data.getId());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+106
-16
@@ -338,6 +338,7 @@ public class LoanRepaymentScheduleWithDownPaymentTest {
|
|||||||
assertEquals(enableAutoRepaymentForDownPayment, loanDetails.getEnableAutoRepaymentForDownPayment());
|
assertEquals(enableAutoRepaymentForDownPayment, loanDetails.getEnableAutoRepaymentForDownPayment());
|
||||||
|
|
||||||
List<GetLoansLoanIdRepaymentPeriod> periods = loanDetails.getRepaymentSchedule().getPeriods();
|
List<GetLoansLoanIdRepaymentPeriod> periods = loanDetails.getRepaymentSchedule().getPeriods();
|
||||||
|
Double expectedOutstandingLoanBalanceOnDisbursement = 1000.00;
|
||||||
Double expectedDownPaymentAmount = 250.00;
|
Double expectedDownPaymentAmount = 250.00;
|
||||||
LocalDate expectedDownPaymentDueDate = LocalDate.of(2022, 9, 3);
|
LocalDate expectedDownPaymentDueDate = LocalDate.of(2022, 9, 3);
|
||||||
Double expectedRepaymentAmount = 250.00;
|
Double expectedRepaymentAmount = 250.00;
|
||||||
@@ -348,21 +349,110 @@ public class LoanRepaymentScheduleWithDownPaymentTest {
|
|||||||
LocalDate expectedThirdRepaymentDueDate = LocalDate.of(2022, 12, 3);
|
LocalDate expectedThirdRepaymentDueDate = LocalDate.of(2022, 12, 3);
|
||||||
Double outstandingBalanceOnThirdRepayment = 0.00;
|
Double outstandingBalanceOnThirdRepayment = 0.00;
|
||||||
|
|
||||||
assertTrue(periods.stream() //
|
GetLoansLoanIdRepaymentPeriod firstDisbursementPeriod = periods.get(0);
|
||||||
.anyMatch(period -> expectedDownPaymentAmount.equals(period.getTotalDueForPeriod()) //
|
assertEquals(expectedDownPaymentDueDate, firstDisbursementPeriod.getDueDate());
|
||||||
&& expectedDownPaymentDueDate.equals(period.getDueDate())));
|
assertEquals(expectedOutstandingLoanBalanceOnDisbursement, firstDisbursementPeriod.getPrincipalLoanBalanceOutstanding());
|
||||||
assertTrue(periods.stream()
|
|
||||||
.anyMatch(period -> expectedRepaymentAmount.equals(period.getTotalDueForPeriod())
|
GetLoansLoanIdRepaymentPeriod firstDownPaymentPeriod = periods.get(1);
|
||||||
&& expectedFirstRepaymentDueDate.equals(period.getDueDate())
|
assertEquals(expectedDownPaymentAmount, firstDownPaymentPeriod.getTotalDueForPeriod());
|
||||||
&& outstandingBalanceOnFirstRepayment.equals(period.getPrincipalLoanBalanceOutstanding())));
|
assertEquals(expectedDownPaymentDueDate, firstDownPaymentPeriod.getDueDate());
|
||||||
assertTrue(periods.stream()
|
|
||||||
.anyMatch(period -> expectedRepaymentAmount.equals(period.getTotalDueForPeriod())
|
GetLoansLoanIdRepaymentPeriod firstRepaymentPeriod = periods.get(2);
|
||||||
&& expectedSecondRepaymentDueDate.equals(period.getDueDate())
|
assertEquals(expectedRepaymentAmount, firstRepaymentPeriod.getTotalDueForPeriod());
|
||||||
&& outstandingBalanceOnSecondRepayment.equals(period.getPrincipalLoanBalanceOutstanding())));
|
assertEquals(expectedFirstRepaymentDueDate, firstRepaymentPeriod.getDueDate());
|
||||||
assertTrue(periods.stream()
|
assertEquals(outstandingBalanceOnFirstRepayment, firstRepaymentPeriod.getPrincipalLoanBalanceOutstanding());
|
||||||
.anyMatch(period -> expectedRepaymentAmount.equals(period.getTotalDueForPeriod())
|
|
||||||
&& expectedThirdRepaymentDueDate.equals(period.getDueDate())
|
GetLoansLoanIdRepaymentPeriod secondRepaymentPeriod = periods.get(3);
|
||||||
&& outstandingBalanceOnThirdRepayment.equals(period.getPrincipalLoanBalanceOutstanding())));
|
assertEquals(expectedRepaymentAmount, secondRepaymentPeriod.getTotalDueForPeriod());
|
||||||
|
assertEquals(expectedSecondRepaymentDueDate, secondRepaymentPeriod.getDueDate());
|
||||||
|
assertEquals(outstandingBalanceOnSecondRepayment, secondRepaymentPeriod.getPrincipalLoanBalanceOutstanding());
|
||||||
|
|
||||||
|
GetLoansLoanIdRepaymentPeriod thirdRepaymentPeriod = periods.get(4);
|
||||||
|
assertEquals(expectedRepaymentAmount, thirdRepaymentPeriod.getTotalDueForPeriod());
|
||||||
|
assertEquals(expectedThirdRepaymentDueDate, thirdRepaymentPeriod.getDueDate());
|
||||||
|
assertEquals(outstandingBalanceOnThirdRepayment, thirdRepaymentPeriod.getPrincipalLoanBalanceOutstanding());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void loanRepaymentScheduleWithMultiDisbursementProductTwoDisbursementAndThreeRepaymentsAndDownPayment() {
|
||||||
|
String loanExternalIdStr = UUID.randomUUID().toString();
|
||||||
|
|
||||||
|
final Integer delinquencyBucketId = DelinquencyBucketsHelper.createDelinquencyBucket(requestSpec, responseSpec);
|
||||||
|
final GetDelinquencyBucketsResponse delinquencyBucket = DelinquencyBucketsHelper.getDelinquencyBucket(requestSpec, responseSpec,
|
||||||
|
delinquencyBucketId);
|
||||||
|
|
||||||
|
Boolean enableDownPayment = true;
|
||||||
|
BigDecimal disbursedAmountPercentageForDownPayment = BigDecimal.valueOf(25);
|
||||||
|
Boolean enableAutoRepaymentForDownPayment = true;
|
||||||
|
|
||||||
|
final Integer clientId = clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId().intValue();
|
||||||
|
|
||||||
|
Integer loanProductId = createLoanProductWithDownPaymentConfiguration(loanTransactionHelper, delinquencyBucketId, enableDownPayment,
|
||||||
|
"25", enableAutoRepaymentForDownPayment, true);
|
||||||
|
|
||||||
|
final GetLoanProductsProductIdResponse getLoanProductsProductResponse = loanTransactionHelper.getLoanProduct(loanProductId);
|
||||||
|
assertNotNull(getLoanProductsProductResponse);
|
||||||
|
assertEquals(enableDownPayment, getLoanProductsProductResponse.getEnableDownPayment());
|
||||||
|
assertEquals(0, getLoanProductsProductResponse.getDisbursedAmountPercentageForDownPayment()
|
||||||
|
.compareTo(disbursedAmountPercentageForDownPayment));
|
||||||
|
assertEquals(enableAutoRepaymentForDownPayment, getLoanProductsProductResponse.getEnableAutoRepaymentForDownPayment());
|
||||||
|
|
||||||
|
final Integer loanId = createApproveAndDisburseTwiceLoanAccount(clientId, loanProductId.longValue(), loanExternalIdStr, "4");
|
||||||
|
|
||||||
|
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanId.longValue());
|
||||||
|
|
||||||
|
assertNotNull(loanDetails);
|
||||||
|
assertEquals(enableDownPayment, loanDetails.getEnableDownPayment());
|
||||||
|
assertEquals(0, loanDetails.getDisbursedAmountPercentageForDownPayment().compareTo(disbursedAmountPercentageForDownPayment));
|
||||||
|
assertEquals(enableAutoRepaymentForDownPayment, loanDetails.getEnableAutoRepaymentForDownPayment());
|
||||||
|
|
||||||
|
loanTransactionHelper.printRepaymentSchedule(loanDetails);
|
||||||
|
|
||||||
|
List<GetLoansLoanIdRepaymentPeriod> periods = loanDetails.getRepaymentSchedule().getPeriods();
|
||||||
|
Double expectedOutstandingLoanBalanceOnFirstDisbursement = 700.00;
|
||||||
|
Double expectedFirstDownPaymentAmount = 175.00;
|
||||||
|
LocalDate expectedFirstDownPaymentDueDate = LocalDate.of(2022, 9, 3);
|
||||||
|
Double expectedOutstandingLoanBalanceOnSecondDisbursement = 300.00;
|
||||||
|
Double expectedSecondDownPaymentAmount = 75.00;
|
||||||
|
LocalDate expectedSecondDownPaymentDueDate = LocalDate.of(2022, 9, 4);
|
||||||
|
Double expectedRepaymentAmount = 250.00;
|
||||||
|
LocalDate expectedFirstRepaymentDueDate = LocalDate.of(2022, 10, 3);
|
||||||
|
Double outstandingBalanceOnFirstRepayment = 500.00;
|
||||||
|
LocalDate expectedSecondRepaymentDueDate = LocalDate.of(2022, 11, 3);
|
||||||
|
Double outstandingBalanceOnSecondRepayment = 250.00;
|
||||||
|
LocalDate expectedThirdRepaymentDueDate = LocalDate.of(2022, 12, 3);
|
||||||
|
Double outstandingBalanceOnThirdRepayment = 0.00;
|
||||||
|
|
||||||
|
GetLoansLoanIdRepaymentPeriod firstDisbursementPeriod = periods.get(0);
|
||||||
|
assertEquals(expectedFirstDownPaymentDueDate, firstDisbursementPeriod.getDueDate());
|
||||||
|
assertEquals(expectedOutstandingLoanBalanceOnFirstDisbursement, firstDisbursementPeriod.getPrincipalLoanBalanceOutstanding());
|
||||||
|
|
||||||
|
GetLoansLoanIdRepaymentPeriod firstDownPaymentPeriod = periods.get(1);
|
||||||
|
assertEquals(expectedFirstDownPaymentAmount, firstDownPaymentPeriod.getTotalDueForPeriod());
|
||||||
|
assertEquals(expectedFirstDownPaymentDueDate, firstDownPaymentPeriod.getDueDate());
|
||||||
|
|
||||||
|
GetLoansLoanIdRepaymentPeriod secondDisbursementPeriod = periods.get(2);
|
||||||
|
assertEquals(expectedSecondDownPaymentDueDate, secondDisbursementPeriod.getDueDate());
|
||||||
|
assertEquals(expectedOutstandingLoanBalanceOnSecondDisbursement, secondDisbursementPeriod.getPrincipalLoanBalanceOutstanding());
|
||||||
|
|
||||||
|
GetLoansLoanIdRepaymentPeriod secondDownPaymentPeriod = periods.get(3);
|
||||||
|
assertEquals(expectedSecondDownPaymentAmount, secondDownPaymentPeriod.getTotalDueForPeriod());
|
||||||
|
assertEquals(expectedSecondDownPaymentDueDate, secondDownPaymentPeriod.getDueDate());
|
||||||
|
|
||||||
|
GetLoansLoanIdRepaymentPeriod firstRepaymentPeriod = periods.get(4);
|
||||||
|
assertEquals(expectedRepaymentAmount, firstRepaymentPeriod.getTotalDueForPeriod());
|
||||||
|
assertEquals(expectedFirstRepaymentDueDate, firstRepaymentPeriod.getDueDate());
|
||||||
|
assertEquals(outstandingBalanceOnFirstRepayment, firstRepaymentPeriod.getPrincipalLoanBalanceOutstanding());
|
||||||
|
|
||||||
|
GetLoansLoanIdRepaymentPeriod secondRepaymentPeriod = periods.get(5);
|
||||||
|
assertEquals(expectedRepaymentAmount, secondRepaymentPeriod.getTotalDueForPeriod());
|
||||||
|
assertEquals(expectedSecondRepaymentDueDate, secondRepaymentPeriod.getDueDate());
|
||||||
|
assertEquals(outstandingBalanceOnSecondRepayment, secondRepaymentPeriod.getPrincipalLoanBalanceOutstanding());
|
||||||
|
|
||||||
|
GetLoansLoanIdRepaymentPeriod thirdRepaymentPeriod = periods.get(6);
|
||||||
|
assertEquals(expectedRepaymentAmount, thirdRepaymentPeriod.getTotalDueForPeriod());
|
||||||
|
assertEquals(expectedThirdRepaymentDueDate, thirdRepaymentPeriod.getDueDate());
|
||||||
|
assertEquals(outstandingBalanceOnThirdRepayment, thirdRepaymentPeriod.getPrincipalLoanBalanceOutstanding());
|
||||||
}
|
}
|
||||||
|
|
||||||
private Integer createLoanProductWithDownPaymentConfiguration(final LoanTransactionHelper loanTransactionHelper,
|
private Integer createLoanProductWithDownPaymentConfiguration(final LoanTransactionHelper loanTransactionHelper,
|
||||||
@@ -405,7 +495,7 @@ public class LoanRepaymentScheduleWithDownPaymentTest {
|
|||||||
private Integer createApproveAndDisburseTwiceLoanAccount(final Integer clientID, final Long loanProductID, final String externalId,
|
private Integer createApproveAndDisburseTwiceLoanAccount(final Integer clientID, final Long loanProductID, final String externalId,
|
||||||
final String numberOfRepayments) {
|
final String numberOfRepayments) {
|
||||||
|
|
||||||
String loanApplicationJSON = new LoanApplicationTestBuilder().withPrincipal("1000").withLoanTermFrequency("1")
|
String loanApplicationJSON = new LoanApplicationTestBuilder().withPrincipal("1000").withLoanTermFrequency(numberOfRepayments)
|
||||||
.withLoanTermFrequencyAsMonths().withNumberOfRepayments(numberOfRepayments).withRepaymentEveryAfter("1")
|
.withLoanTermFrequencyAsMonths().withNumberOfRepayments(numberOfRepayments).withRepaymentEveryAfter("1")
|
||||||
.withRepaymentFrequencyTypeAsMonths().withInterestRatePerPeriod("0").withInterestTypeAsFlatBalance()
|
.withRepaymentFrequencyTypeAsMonths().withInterestRatePerPeriod("0").withInterestTypeAsFlatBalance()
|
||||||
.withAmortizationTypeAsEqualPrincipalPayments().withInterestCalculationPeriodTypeSameAsRepaymentPeriod()
|
.withAmortizationTypeAsEqualPrincipalPayments().withInterestCalculationPeriodTypeSameAsRepaymentPeriod()
|
||||||
|
|||||||
Reference in New Issue
Block a user