FINERACT-2232: add income capitalization principal handling and repayment schedule calculations on loans

This commit is contained in:
mark.vituska
2025-05-27 10:02:25 +02:00
committed by Adam Saghy
parent 07839951b9
commit e5329f6fe3
53 changed files with 1007 additions and 516 deletions
@@ -5938,7 +5938,7 @@ public class AdvancedPaymentAllocationLoanRepaymentScheduleTest extends BaseLoan
executeInlineCOB(createdLoanId.get());
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(createdLoanId.get());
// totalUnpaidPayableDueInterest → Total outstanding interest amount on all the periods that are before
assertEquals(BigDecimal.valueOf(loanDetails.getSummary().getInterestCharged()).stripTrailingZeros(),
assertEquals(loanDetails.getSummary().getInterestCharged().stripTrailingZeros(),
loanDetails.getSummary().getTotalUnpaidPayableDueInterest().stripTrailingZeros());
// Total outstanding interest amount on the current period, if the current period due date is after than the
// current date
@@ -6131,21 +6131,21 @@ public class AdvancedPaymentAllocationLoanRepaymentScheduleTest extends BaseLoan
GetLoansLoanIdRepaymentPeriod period = loanDetails.getRepaymentSchedule().getPeriods().get(index);
assertEquals(dueDate, period.getDueDate());
assertEquals(paidDate, period.getObligationsMetOnDate());
assertEquals(balanceOfLoan, period.getPrincipalLoanBalanceOutstanding());
assertEquals(principalDue, period.getPrincipalDue());
assertEquals(principalPaid, period.getPrincipalPaid());
assertEquals(principalOutstanding, period.getPrincipalOutstanding());
assertEquals(feeDue, period.getFeeChargesDue());
assertEquals(feePaid, period.getFeeChargesPaid());
assertEquals(feeOutstanding, period.getFeeChargesOutstanding());
assertEquals(penaltyDue, period.getPenaltyChargesDue());
assertEquals(penaltyPaid, period.getPenaltyChargesPaid());
assertEquals(penaltyOutstanding, period.getPenaltyChargesOutstanding());
assertEquals(interestDue, period.getInterestDue());
assertEquals(interestPaid, period.getInterestPaid());
assertEquals(interestOutstanding, period.getInterestOutstanding());
assertEquals(paidInAdvance, period.getTotalPaidInAdvanceForPeriod());
assertEquals(paidLate, period.getTotalPaidLateForPeriod());
assertEquals(balanceOfLoan, Utils.getDoubleValue(period.getPrincipalLoanBalanceOutstanding()));
assertEquals(principalDue, Utils.getDoubleValue(period.getPrincipalDue()));
assertEquals(principalPaid, Utils.getDoubleValue(period.getPrincipalPaid()));
assertEquals(principalOutstanding, Utils.getDoubleValue(period.getPrincipalOutstanding()));
assertEquals(feeDue, Utils.getDoubleValue(period.getFeeChargesDue()));
assertEquals(feePaid, Utils.getDoubleValue(period.getFeeChargesPaid()));
assertEquals(feeOutstanding, Utils.getDoubleValue(period.getFeeChargesOutstanding()));
assertEquals(penaltyDue, Utils.getDoubleValue(period.getPenaltyChargesDue()));
assertEquals(penaltyPaid, Utils.getDoubleValue(period.getPenaltyChargesPaid()));
assertEquals(penaltyOutstanding, Utils.getDoubleValue(period.getPenaltyChargesOutstanding()));
assertEquals(interestDue, Utils.getDoubleValue(period.getInterestDue()));
assertEquals(interestPaid, Utils.getDoubleValue(period.getInterestPaid()));
assertEquals(interestOutstanding, Utils.getDoubleValue(period.getInterestOutstanding()));
assertEquals(paidInAdvance, Utils.getDoubleValue(period.getTotalPaidInAdvanceForPeriod()));
assertEquals(paidLate, Utils.getDoubleValue(period.getTotalPaidLateForPeriod()));
}
private static PostLoansResponse applyForLoanApplication(final Long clientId, final Integer loanProductId, final BigDecimal principal,
@@ -6181,18 +6181,18 @@ public class AdvancedPaymentAllocationLoanRepaymentScheduleTest extends BaseLoan
private static void validateLoanTransaction(GetLoansLoanIdResponse loanDetails, int index, double transactionAmount,
double principalPortion, double overPaidPortion, double loanBalance) {
assertEquals(transactionAmount, loanDetails.getTransactions().get(index).getAmount());
assertEquals(principalPortion, loanDetails.getTransactions().get(index).getPrincipalPortion());
assertEquals(overPaidPortion, loanDetails.getTransactions().get(index).getOverpaymentPortion());
assertEquals(loanBalance, loanDetails.getTransactions().get(index).getOutstandingLoanBalance());
assertEquals(transactionAmount, Utils.getDoubleValue(loanDetails.getTransactions().get(index).getAmount()));
assertEquals(principalPortion, Utils.getDoubleValue(loanDetails.getTransactions().get(index).getPrincipalPortion()));
assertEquals(overPaidPortion, Utils.getDoubleValue(loanDetails.getTransactions().get(index).getOverpaymentPortion()));
assertEquals(loanBalance, Utils.getDoubleValue(loanDetails.getTransactions().get(index).getOutstandingLoanBalance()));
}
private void validateLoanCharge(GetLoansLoanIdResponse loanDetails, int index, LocalDate dueDate, double charged, double paid,
double outstanding) {
GetLoansLoanIdLoanChargeData chargeData = loanDetails.getCharges().get(index);
assertEquals(dueDate, chargeData.getDueDate());
assertEquals(charged, chargeData.getAmount());
assertEquals(paid, chargeData.getAmountPaid());
assertEquals(outstanding, chargeData.getAmountOutstanding());
assertEquals(charged, Utils.getDoubleValue(chargeData.getAmount()));
assertEquals(paid, Utils.getDoubleValue(chargeData.getAmountPaid()));
assertEquals(outstanding, Utils.getDoubleValue(chargeData.getAmountOutstanding()));
}
}
@@ -177,22 +177,22 @@ public abstract class BaseLoanIntegrationTest extends IntegrationTest {
GetLoansLoanIdRepaymentPeriod period = loanDetails.getRepaymentSchedule().getPeriods().stream()
.filter(p -> Objects.equals(p.getPeriod(), index)).findFirst().orElseThrow();
assertEquals(dueDate, period.getDueDate());
assertEquals(principalDue, period.getPrincipalDue());
assertEquals(principalPaid, period.getPrincipalPaid());
assertEquals(principalOutstanding, period.getPrincipalOutstanding());
assertEquals(paidInAdvance, period.getTotalPaidInAdvanceForPeriod());
assertEquals(paidLate, period.getTotalPaidLateForPeriod());
assertEquals(principalDue, Utils.getDoubleValue(period.getPrincipalDue()));
assertEquals(principalPaid, Utils.getDoubleValue(period.getPrincipalPaid()));
assertEquals(principalOutstanding, Utils.getDoubleValue(period.getPrincipalOutstanding()));
assertEquals(paidInAdvance, Utils.getDoubleValue(period.getTotalPaidInAdvanceForPeriod()));
assertEquals(paidLate, Utils.getDoubleValue(period.getTotalPaidLateForPeriod()));
}
protected static void validateRepaymentPeriod(GetLoansLoanIdResponse loanDetails, Integer index, double principalDue,
double principalPaid, double principalOutstanding, double paidInAdvance, double paidLate) {
GetLoansLoanIdRepaymentPeriod period = loanDetails.getRepaymentSchedule().getPeriods().stream()
.filter(p -> Objects.equals(p.getPeriod(), index)).findFirst().orElseThrow();
assertEquals(principalDue, period.getPrincipalDue());
assertEquals(principalPaid, period.getPrincipalPaid());
assertEquals(principalOutstanding, period.getPrincipalOutstanding());
assertEquals(paidInAdvance, period.getTotalPaidInAdvanceForPeriod());
assertEquals(paidLate, period.getTotalPaidLateForPeriod());
assertEquals(principalDue, Utils.getDoubleValue(period.getPrincipalDue()));
assertEquals(principalPaid, Utils.getDoubleValue(period.getPrincipalPaid()));
assertEquals(principalOutstanding, Utils.getDoubleValue(period.getPrincipalOutstanding()));
assertEquals(paidInAdvance, Utils.getDoubleValue(period.getTotalPaidInAdvanceForPeriod()));
assertEquals(paidLate, Utils.getDoubleValue(period.getTotalPaidLateForPeriod()));
}
protected static void validateFullyUnpaidRepaymentPeriod(GetLoansLoanIdResponse loanDetails, Integer index, String dueDate,
@@ -233,20 +233,20 @@ public abstract class BaseLoanIntegrationTest extends IntegrationTest {
GetLoansLoanIdRepaymentPeriod period = loanDetails.getRepaymentSchedule().getPeriods().stream()
.filter(p -> Objects.equals(p.getPeriod(), index)).findFirst().orElseThrow();
assertEquals(dueDate, period.getDueDate());
assertEquals(principalDue, period.getPrincipalDue());
assertEquals(principalPaid, period.getPrincipalPaid());
assertEquals(principalOutstanding, period.getPrincipalOutstanding());
assertEquals(feeDue, period.getFeeChargesDue());
assertEquals(feePaid, period.getFeeChargesPaid());
assertEquals(feeOutstanding, period.getFeeChargesOutstanding());
assertEquals(penaltyDue, period.getPenaltyChargesDue());
assertEquals(penaltyPaid, period.getPenaltyChargesPaid());
assertEquals(penaltyOutstanding, period.getPenaltyChargesOutstanding());
assertEquals(interestDue, period.getInterestDue());
assertEquals(interestPaid, period.getInterestPaid());
assertEquals(interestOutstanding, period.getInterestOutstanding());
assertEquals(paidInAdvance, period.getTotalPaidInAdvanceForPeriod());
assertEquals(paidLate, period.getTotalPaidLateForPeriod());
assertEquals(principalDue, Utils.getDoubleValue(period.getPrincipalDue()));
assertEquals(principalPaid, Utils.getDoubleValue(period.getPrincipalPaid()));
assertEquals(principalOutstanding, Utils.getDoubleValue(period.getPrincipalOutstanding()));
assertEquals(feeDue, Utils.getDoubleValue(period.getFeeChargesDue()));
assertEquals(feePaid, Utils.getDoubleValue(period.getFeeChargesPaid()));
assertEquals(feeOutstanding, Utils.getDoubleValue(period.getFeeChargesOutstanding()));
assertEquals(penaltyDue, Utils.getDoubleValue(period.getPenaltyChargesDue()));
assertEquals(penaltyPaid, Utils.getDoubleValue(period.getPenaltyChargesPaid()));
assertEquals(penaltyOutstanding, Utils.getDoubleValue(period.getPenaltyChargesOutstanding()));
assertEquals(interestDue, Utils.getDoubleValue(period.getInterestDue()));
assertEquals(interestPaid, Utils.getDoubleValue(period.getInterestPaid()));
assertEquals(interestOutstanding, Utils.getDoubleValue(period.getInterestOutstanding()));
assertEquals(paidInAdvance, Utils.getDoubleValue(period.getTotalPaidInAdvanceForPeriod()));
assertEquals(paidLate, Utils.getDoubleValue(period.getTotalPaidLateForPeriod()));
}
/**
@@ -410,6 +410,15 @@ public abstract class BaseLoanIntegrationTest extends IntegrationTest {
.loanScheduleProcessingType("HORIZONTAL");//
}
protected PostLoanProductsRequest create4IProgressiveWithCapitalizedIncome() {
return create4IProgressive().enableIncomeCapitalization(true)//
.capitalizedIncomeCalculationType(PostLoanProductsRequest.CapitalizedIncomeCalculationTypeEnum.FLAT)//
.capitalizedIncomeStrategy(PostLoanProductsRequest.CapitalizedIncomeStrategyEnum.EQUAL_AMORTIZATION)//
.deferredIncomeLiabilityAccountId(deferredIncomeLiabilityAccount.getAccountID().longValue())//
.incomeFromCapitalizationAccountId(feeIncomeAccount.getAccountID().longValue())//
.capitalizedIncomeType(PostLoanProductsRequest.CapitalizedIncomeTypeEnum.FEE);
}
// Loan product with proper accounting setup
protected PostLoanProductsRequest createOnePeriod30DaysPeriodicAccrualProduct(double interestRatePerPeriod) {
return new PostLoanProductsRequest().name(Utils.uniqueRandomStringGenerator("LOAN_PRODUCT_", 6))//
@@ -615,7 +624,7 @@ public abstract class BaseLoanIntegrationTest extends IntegrationTest {
Assertions.assertEquals(transactions.length, loanDetails.getTransactions().size());
Arrays.stream(transactions).forEach(tr -> {
Optional<GetLoansLoanIdTransactions> optTx = loanDetails.getTransactions().stream()
.filter(item -> Objects.equals(item.getAmount(), tr.amount) //
.filter(item -> Objects.equals(Utils.getDoubleValue(item.getAmount()), tr.amount) //
&& Objects.equals(item.getType().getValue(), tr.type) //
&& Objects.equals(item.getDate(), LocalDate.parse(tr.date, dateTimeFormatter)))
.findFirst();
@@ -638,16 +647,17 @@ public abstract class BaseLoanIntegrationTest extends IntegrationTest {
} else {
Assertions.assertEquals(transactions.length, loanDetails.getTransactions().size(), "Number of transactions on loan " + loanId);
Arrays.stream(transactions).forEach(tr -> {
boolean found = loanDetails.getTransactions().stream().anyMatch(item -> Objects.equals(item.getAmount(), tr.amount) //
&& Objects.equals(item.getType().getValue(), tr.type) //
&& Objects.equals(item.getDate(), LocalDate.parse(tr.date, dateTimeFormatter)) //
&& Objects.equals(item.getOutstandingLoanBalance(), tr.outstandingPrincipal) //
&& Objects.equals(item.getPrincipalPortion(), tr.principalPortion) //
&& Objects.equals(item.getInterestPortion(), tr.interestPortion) //
&& Objects.equals(item.getFeeChargesPortion(), tr.feePortion) //
&& Objects.equals(item.getPenaltyChargesPortion(), tr.penaltyPortion) //
&& Objects.equals(item.getOverpaymentPortion(), tr.overpaymentPortion) //
&& Objects.equals(item.getUnrecognizedIncomePortion(), tr.unrecognizedPortion) //
boolean found = loanDetails.getTransactions().stream()
.anyMatch(item -> Objects.equals(Utils.getDoubleValue(item.getAmount()), tr.amount) //
&& Objects.equals(item.getType().getValue(), tr.type) //
&& Objects.equals(item.getDate(), LocalDate.parse(tr.date, dateTimeFormatter)) //
&& Objects.equals(Utils.getDoubleValue(item.getOutstandingLoanBalance()), tr.outstandingPrincipal) //
&& Objects.equals(Utils.getDoubleValue(item.getPrincipalPortion()), tr.principalPortion) //
&& Objects.equals(Utils.getDoubleValue(item.getInterestPortion()), tr.interestPortion) //
&& Objects.equals(Utils.getDoubleValue(item.getFeeChargesPortion()), tr.feePortion) //
&& Objects.equals(Utils.getDoubleValue(item.getPenaltyChargesPortion()), tr.penaltyPortion) //
&& Objects.equals(Utils.getDoubleValue(item.getOverpaymentPortion()), tr.overpaymentPortion) //
&& Objects.equals(Utils.getDoubleValue(item.getUnrecognizedIncomePortion()), tr.unrecognizedPortion) //
);
Assertions.assertTrue(found, "Required transaction not found: " + tr + " on loan " + loanId);
});
@@ -823,8 +833,8 @@ public abstract class BaseLoanIntegrationTest extends IntegrationTest {
private void verifyPeriodsEquality(List<GetLoansLoanIdRepaymentPeriod> savedPeriods, List<GetLoansLoanIdRepaymentPeriod> actualPeriods,
int startIndex, int endIndex, boolean shouldEqual) {
for (int i = startIndex; i < endIndex; i++) {
Double savedTotalDue = savedPeriods.get(i).getTotalDueForPeriod();
Double actualTotalDue = actualPeriods.get(i).getTotalDueForPeriod();
Double savedTotalDue = Utils.getDoubleValue(savedPeriods.get(i).getTotalDueForPeriod());
Double actualTotalDue = Utils.getDoubleValue(actualPeriods.get(i).getTotalDueForPeriod());
if (shouldEqual) {
assertEquals(savedTotalDue, actualTotalDue, String.format(
@@ -848,19 +858,19 @@ public abstract class BaseLoanIntegrationTest extends IntegrationTest {
int installmentNumber = 0;
for (int i = 0; i < installments.length; i++) {
GetLoansLoanIdRepaymentPeriod period = loanResponse.getRepaymentSchedule().getPeriods().get(i);
Double principalDue = period.getPrincipalDue();
Double principalDue = Utils.getDoubleValue(period.getPrincipalDue());
Double amount = installments[i].principalAmount;
if (installments[i].completed == null) { // this is for the disbursement
Assertions.assertEquals(amount, period.getPrincipalLoanBalanceOutstanding(),
Assertions.assertEquals(amount, Utils.getDoubleValue(period.getPrincipalLoanBalanceOutstanding()),
"%d. installment's principal due is different, expected: %.2f, actual: %.2f".formatted(i, amount,
period.getPrincipalLoanBalanceOutstanding()));
Utils.getDoubleValue(period.getPrincipalLoanBalanceOutstanding())));
} else {
Assertions.assertEquals(amount, principalDue,
"%d. installment's principal due is different, expected: %.2f, actual: %.2f".formatted(i, amount, principalDue));
Double interestAmount = installments[i].interestAmount;
Double interestDue = period.getInterestDue();
Double interestDue = Utils.getDoubleValue(period.getInterestDue());
if (interestAmount != null) {
Assertions.assertEquals(interestAmount, interestDue,
"%d. installment's interest due is different, expected: %.2f, actual: %.2f".formatted(i, interestAmount,
@@ -868,14 +878,14 @@ public abstract class BaseLoanIntegrationTest extends IntegrationTest {
}
Double feeAmount = installments[i].feeAmount;
Double feeDue = period.getFeeChargesDue();
Double feeDue = Utils.getDoubleValue(period.getFeeChargesDue());
if (feeAmount != null) {
Assertions.assertEquals(feeAmount, feeDue,
"%d. installment's fee charges due is different, expected: %.2f, actual: %.2f".formatted(i, feeAmount, feeDue));
}
Double penaltyAmount = installments[i].penaltyAmount;
Double penaltyDue = period.getPenaltyChargesDue();
Double penaltyDue = Utils.getDoubleValue(period.getPenaltyChargesDue());
if (penaltyAmount != null) {
Assertions.assertEquals(penaltyAmount, penaltyDue,
"%d. installment's penalty charges due is different, expected: %.2f, actual: %.2f".formatted(i, penaltyAmount,
@@ -883,7 +893,7 @@ public abstract class BaseLoanIntegrationTest extends IntegrationTest {
}
Double outstandingAmount = installments[i].totalOutstandingAmount;
Double totalOutstanding = period.getTotalOutstandingForPeriod();
Double totalOutstanding = Utils.getDoubleValue(period.getTotalOutstandingForPeriod());
if (outstandingAmount != null) {
Assertions.assertEquals(outstandingAmount, totalOutstanding,
"%d. installment's total outstanding is different, expected: %.2f, actual: %.2f".formatted(i, outstandingAmount,
@@ -893,7 +903,7 @@ public abstract class BaseLoanIntegrationTest extends IntegrationTest {
Double outstandingPrincipalExpected = installments[i].outstandingAmounts != null
? installments[i].outstandingAmounts.principalOutstanding
: null;
Double outstandingPrincipal = period.getPrincipalOutstanding();
Double outstandingPrincipal = Utils.getDoubleValue(period.getPrincipalOutstanding());
if (outstandingPrincipalExpected != null) {
Assertions.assertEquals(outstandingPrincipalExpected, outstandingPrincipal,
"%d. installment's outstanding principal is different, expected: %.2f, actual: %.2f".formatted(i,
@@ -903,7 +913,7 @@ public abstract class BaseLoanIntegrationTest extends IntegrationTest {
Double outstandingFeeExpected = installments[i].outstandingAmounts != null
? installments[i].outstandingAmounts.feeOutstanding
: null;
Double outstandingFee = period.getFeeChargesOutstanding();
Double outstandingFee = Utils.getDoubleValue(period.getFeeChargesOutstanding());
if (outstandingFeeExpected != null) {
Assertions.assertEquals(outstandingFeeExpected, outstandingFee,
"%d. installment's outstanding fee is different, expected: %.2f, actual: %.2f".formatted(i,
@@ -913,7 +923,7 @@ public abstract class BaseLoanIntegrationTest extends IntegrationTest {
Double outstandingPenaltyExpected = installments[i].outstandingAmounts != null
? installments[i].outstandingAmounts.penaltyOutstanding
: null;
Double outstandingPenalty = period.getPenaltyChargesOutstanding();
Double outstandingPenalty = Utils.getDoubleValue(period.getPenaltyChargesOutstanding());
if (outstandingPenaltyExpected != null) {
Assertions.assertEquals(outstandingPenaltyExpected, outstandingPenalty,
"%d. installment's outstanding penalty is different, expected: %.2f, actual: %.2f".formatted(i,
@@ -923,7 +933,7 @@ public abstract class BaseLoanIntegrationTest extends IntegrationTest {
Double outstandingTotalExpected = installments[i].outstandingAmounts != null
? installments[i].outstandingAmounts.totalOutstanding
: null;
Double outstandingTotal = period.getTotalOutstandingForPeriod();
Double outstandingTotal = Utils.getDoubleValue(period.getTotalOutstandingForPeriod());
if (outstandingTotalExpected != null) {
Assertions.assertEquals(outstandingTotalExpected, outstandingTotal,
"%d. installment's total outstanding is different, expected: %.2f, actual: %.2f".formatted(i,
@@ -931,7 +941,7 @@ public abstract class BaseLoanIntegrationTest extends IntegrationTest {
}
Double loanBalanceExpected = installments[i].loanBalance;
Double loanBalance = period.getPrincipalLoanBalanceOutstanding();
Double loanBalance = Utils.getDoubleValue(period.getPrincipalLoanBalanceOutstanding());
if (loanBalanceExpected != null) {
Assertions.assertEquals(loanBalanceExpected, loanBalance,
"%d. installment's loan balance is different, expected: %.2f, actual: %.2f".formatted(i, loanBalanceExpected,
@@ -1226,11 +1236,11 @@ public abstract class BaseLoanIntegrationTest extends IntegrationTest {
protected void validateLoanSummaryBalances(GetLoansLoanIdResponse loanDetails, Double totalOutstanding, Double totalRepayment,
Double principalOutstanding, Double principalPaid, Double totalOverpaid) {
assertEquals(totalOutstanding, loanDetails.getSummary().getTotalOutstanding());
assertEquals(totalRepayment, loanDetails.getSummary().getTotalRepayment());
assertEquals(principalOutstanding, loanDetails.getSummary().getPrincipalOutstanding());
assertEquals(principalPaid, loanDetails.getSummary().getPrincipalPaid());
assertEquals(totalOverpaid, loanDetails.getTotalOverpaid());
assertEquals(totalOutstanding, Utils.getDoubleValue(loanDetails.getSummary().getTotalOutstanding()));
assertEquals(totalRepayment, Utils.getDoubleValue(loanDetails.getSummary().getTotalRepayment()));
assertEquals(principalOutstanding, Utils.getDoubleValue(loanDetails.getSummary().getPrincipalOutstanding()));
assertEquals(principalPaid, Utils.getDoubleValue(loanDetails.getSummary().getPrincipalPaid()));
assertEquals(totalOverpaid, Utils.getDoubleValue(loanDetails.getTotalOverpaid()));
}
protected void checkMaturityDates(long loanId, LocalDate expectedMaturityDate, LocalDate actualMaturityDate) {
@@ -38,6 +38,7 @@ import org.apache.fineract.client.models.PutLoansLoanIdResponse;
import org.apache.fineract.client.util.CallFailedRuntimeException;
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.products.DelinquencyBucketsHelper;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
@@ -619,7 +620,8 @@ public class InstallmentLevelDelinquencyAPIIntegrationTests extends BaseLoanInte
.getInstallmentLevelDelinquency();
assertThat(loan.getDelinquent().getDelinquentDays()).isEqualTo(loanLevelDelinquentDays);
assertThat(loan.getDelinquent().getDelinquentAmount()).isEqualByComparingTo(Double.valueOf(loanLevelDelinquentAmount));
assertThat(Utils.getDoubleValue(loan.getDelinquent().getDelinquentAmount()))
.isEqualByComparingTo(Double.valueOf(loanLevelDelinquentAmount));
if (expectedInstallmentLevelDelinquencyData != null && expectedInstallmentLevelDelinquencyData.length > 0) {
assertThat(installmentLevelDelinquency).isNotNull();
@@ -33,6 +33,7 @@ import org.apache.fineract.client.models.PostClientsResponse;
import org.apache.fineract.client.models.PostLoanProductsResponse;
import org.apache.fineract.client.models.PostLoansLoanIdTransactionsRequest;
import org.apache.fineract.integrationtests.common.ClientHelper;
import org.apache.fineract.integrationtests.common.Utils;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
@@ -468,8 +469,9 @@ public class LoanCOBCreateAccrualsTest extends BaseLoanIntegrationTest {
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanId);
// No unexpected big accruals or any accrual adjustments
Assertions.assertTrue(loanDetails.getTransactions().stream().noneMatch(t -> (t.getType().getAccrual() && t.getAmount() > 0.31)
|| "loanTransactionType.accrualAdjustment".equals(t.getType().getCode())));
Assertions.assertTrue(
loanDetails.getTransactions().stream().noneMatch(t -> (t.getType().getAccrual() && t.getAmount().doubleValue() > 0.31)
|| "loanTransactionType.accrualAdjustment".equals(t.getType().getCode())));
// Accruals around installment due dates are as expected
validateTransactionsExist(loanDetails, //
@@ -691,16 +693,17 @@ public class LoanCOBCreateAccrualsTest extends BaseLoanIntegrationTest {
private void validateTransactionsExist(GetLoansLoanIdResponse loanDetails, TransactionExt... transactions) {
Arrays.stream(transactions).forEach(tr -> {
boolean found = loanDetails.getTransactions().stream().anyMatch(item -> Objects.equals(item.getAmount(), tr.amount) //
&& Objects.equals(item.getType().getValue(), tr.type) //
&& Objects.equals(item.getDate(), LocalDate.parse(tr.date, dateTimeFormatter)) //
&& Objects.equals(item.getOutstandingLoanBalance(), tr.outstandingPrincipal) //
&& Objects.equals(item.getPrincipalPortion(), tr.principalPortion) //
&& Objects.equals(item.getInterestPortion(), tr.interestPortion) //
&& Objects.equals(item.getFeeChargesPortion(), tr.feePortion) //
&& Objects.equals(item.getPenaltyChargesPortion(), tr.penaltyPortion) //
&& Objects.equals(item.getOverpaymentPortion(), tr.overpaymentPortion) //
&& Objects.equals(item.getUnrecognizedIncomePortion(), tr.unrecognizedPortion) //
boolean found = loanDetails.getTransactions().stream()
.anyMatch(item -> Objects.equals(Utils.getDoubleValue(item.getAmount()), tr.amount) //
&& Objects.equals(item.getType().getValue(), tr.type) //
&& Objects.equals(item.getDate(), LocalDate.parse(tr.date, dateTimeFormatter)) //
&& Objects.equals(Utils.getDoubleValue(item.getOutstandingLoanBalance()), tr.outstandingPrincipal) //
&& Objects.equals(Utils.getDoubleValue(item.getPrincipalPortion()), tr.principalPortion) //
&& Objects.equals(Utils.getDoubleValue(item.getInterestPortion()), tr.interestPortion) //
&& Objects.equals(Utils.getDoubleValue(item.getFeeChargesPortion()), tr.feePortion) //
&& Objects.equals(Utils.getDoubleValue(item.getPenaltyChargesPortion()), tr.penaltyPortion) //
&& Objects.equals(Utils.getDoubleValue(item.getOverpaymentPortion()), tr.overpaymentPortion) //
&& Objects.equals(Utils.getDoubleValue(item.getUnrecognizedIncomePortion()), tr.unrecognizedPortion) //
);
Assertions.assertTrue(found, "Required transaction not found: " + tr + " on loan " + loanDetails.getId());
});
@@ -78,7 +78,7 @@ public class LoanCapitalizedIncomeTest extends BaseLoanIntegrationTest {
transaction(100.0, "Disbursement", "01 January 2024"), //
transaction(50.0, "Capitalized Income", "01 January 2024"), //
transaction(0.55, "Capitalized Income Amortization", "01 January 2024"), //
transaction(0.02, "Accrual", "02 January 2024"), //
transaction(0.03, "Accrual", "02 January 2024"), //
transaction(0.55, "Capitalized Income Amortization", "02 January 2024") //
);
@@ -89,8 +89,8 @@ public class LoanCapitalizedIncomeTest extends BaseLoanIntegrationTest {
journalEntry(50, deferredIncomeLiabilityAccount, "CREDIT"), //
journalEntry(0.55, deferredIncomeLiabilityAccount, "DEBIT"), //
journalEntry(0.55, feeIncomeAccount, "CREDIT"), //
journalEntry(0.02, interestReceivableAccount, "DEBIT"), //
journalEntry(0.02, interestIncomeAccount, "CREDIT"), //
journalEntry(0.03, interestReceivableAccount, "DEBIT"), //
journalEntry(0.03, interestIncomeAccount, "CREDIT"), //
journalEntry(0.55, deferredIncomeLiabilityAccount, "DEBIT"), //
journalEntry(0.55, feeIncomeAccount, "CREDIT") //
);
@@ -27,6 +27,7 @@ import org.apache.fineract.client.models.PostLoanProductsResponse;
import org.apache.fineract.client.models.PostLoansResponse;
import org.apache.fineract.infrastructure.configuration.api.GlobalConfigurationConstants;
import org.apache.fineract.integrationtests.common.ClientHelper;
import org.apache.fineract.integrationtests.common.Utils;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
@@ -78,8 +79,8 @@ public class LoanChargeProgressiveTest extends BaseLoanIntegrationTest {
final PostChargesResponse chargeResponse = createCharge(20.0d, "EUR");
addLoanCharge(loanId, chargeResponse.getResourceId(), "03 October 2024", 20.0d);
final GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanId);
Assertions.assertTrue(
loanDetails.getTransactions().stream().anyMatch(t -> t.getType().getAccrual() && t.getAmount().equals(20.0d)));
Assertions.assertTrue(loanDetails.getTransactions().stream()
.anyMatch(t -> t.getType().getAccrual() && Utils.getDoubleValue(t.getAmount()).equals(20.0d)));
});
runAt("04 October 2024", () -> {
globalConfigurationHelper.manageConfigurations(GlobalConfigurationConstants.ENABLE_IMMEDIATE_CHARGE_ACCRUAL_POST_MATURITY,
@@ -87,7 +88,7 @@ public class LoanChargeProgressiveTest extends BaseLoanIntegrationTest {
executeInlineCOB(loanId);
final GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanId);
Assertions.assertTrue(loanDetails.getTransactions().stream()
.anyMatch(t -> t.getType().getAccrual() && t.getFeeChargesPortion().equals(20.0d)));
.anyMatch(t -> t.getType().getAccrual() && Utils.getDoubleValue(t.getFeeChargesPortion()).equals(20.0d)));
});
}
@@ -28,6 +28,7 @@ import org.apache.fineract.client.models.PostLoanProductsRequest;
import org.apache.fineract.client.models.PostLoanProductsResponse;
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.products.DelinquencyBucketsHelper;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
@@ -231,12 +232,14 @@ public class LoanDelinquencyForNonActiveAccountsTest extends BaseLoanIntegration
.getInstallmentLevelDelinquency();
assertThat(loan.getDelinquent().getDelinquentDays()).isEqualTo(loanLevelDelinquentDays);
assertThat(loan.getDelinquent().getDelinquentAmount()).isEqualByComparingTo(Double.valueOf(loanLevelDelinquentAmount));
assertThat(Utils.getDoubleValue(loan.getDelinquent().getDelinquentAmount()))
.isEqualByComparingTo(Double.valueOf(loanLevelDelinquentAmount));
if (expectedLastRepaymentDate != null && expectedLastRepaymentAmount != null) {
assertThat(loan.getDelinquent().getLastRepaymentDate()).isNotNull();
Assertions.assertEquals(expectedLastRepaymentDate, loan.getDelinquent().getLastRepaymentDate().format(dateTimeFormatter));
assertThat(loan.getDelinquent().getLastRepaymentAmount()).isNotNull();
assertThat(loan.getDelinquent().getLastRepaymentAmount()).isEqualByComparingTo(Double.valueOf(expectedLastRepaymentAmount));
assertThat(Utils.getDoubleValue(loan.getDelinquent().getLastRepaymentAmount()))
.isEqualByComparingTo(Double.valueOf(expectedLastRepaymentAmount));
}
if (expectedInstallmentLevelDelinquencyData != null && expectedInstallmentLevelDelinquencyData.length > 0) {
@@ -314,7 +314,7 @@ public class LoanDisbursementDetailsIntegrationTest {
getLoansLoanIdResponse = this.loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId);
assertNotNull(getLoansLoanIdResponse);
this.loanTransactionHelper.printRepaymentSchedule(getLoansLoanIdResponse);
final Double limit = 2.0;
final BigDecimal limit = BigDecimal.TWO;
evaluateEqualInstallmentsForRepaymentSchedule(getLoansLoanIdResponse.getRepaymentSchedule(), limit);
log.info("-----------MULTI DISBURSAL LOAN EQUAL INSTALLMENTS SUCCESSFULLY-------");
}
@@ -665,9 +665,9 @@ public class LoanDisbursementDetailsIntegrationTest {
return collateral;
}
public void evaluateEqualInstallmentsForRepaymentSchedule(GetLoansLoanIdRepaymentSchedule getLoanRepaymentSchedule, Double limit) {
Double totalOutstandingForPeriod = 0.0;
Double totalInstallmentAmountForPeriod = 0.0;
public void evaluateEqualInstallmentsForRepaymentSchedule(GetLoansLoanIdRepaymentSchedule getLoanRepaymentSchedule, BigDecimal limit) {
BigDecimal totalOutstandingForPeriod = BigDecimal.ZERO;
BigDecimal totalInstallmentAmountForPeriod = BigDecimal.ZERO;
if (getLoanRepaymentSchedule != null) {
log.info("Loan with {} periods", getLoanRepaymentSchedule.getPeriods().size());
for (GetLoansLoanIdRepaymentPeriod period : getLoanRepaymentSchedule.getPeriods()) {
@@ -678,8 +678,9 @@ public class LoanDisbursementDetailsIntegrationTest {
totalOutstandingForPeriod = period.getTotalOutstandingForPeriod();
totalInstallmentAmountForPeriod = period.getTotalInstallmentAmountForPeriod();
} else {
assertTrue(Math.abs(period.getTotalOutstandingForPeriod() - totalOutstandingForPeriod) <= limit);
assertTrue(Math.abs(period.getTotalInstallmentAmountForPeriod() - totalInstallmentAmountForPeriod) <= limit);
assertTrue(period.getTotalOutstandingForPeriod().subtract(totalOutstandingForPeriod).abs().compareTo(limit) <= 0);
assertTrue(period.getTotalInstallmentAmountForPeriod().subtract(totalInstallmentAmountForPeriod).abs()
.compareTo(limit) <= 0);
}
}
}
@@ -149,7 +149,7 @@ public class LoanRefundTransactionTest extends BaseLoanIntegrationTest {
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanId);
Assertions.assertTrue(loanDetails.getTransactions().stream()
.filter(transaction -> transaction.getType().getCode().equals("loanTransactionType.interestRefund"))
.allMatch(transaction -> transaction.getAmount().compareTo(0.0) > 0));
.allMatch(transaction -> transaction.getAmount().doubleValue() > 0.0));
});
}
@@ -110,9 +110,9 @@ public class LoanRepaymentScheduleWithDownPaymentTest extends BaseLoanIntegratio
LocalDate expectedRepaymentDueDate = LocalDate.of(2022, 10, 3);
assertTrue(periods.stream() //
.anyMatch(period -> expectedDownPaymentAmount.equals(period.getTotalDueForPeriod()) //
.anyMatch(period -> expectedDownPaymentAmount.equals(Utils.getDoubleValue(period.getTotalDueForPeriod())) //
&& expectedDownPaymentDueDate.equals(period.getDueDate())));
assertTrue(periods.stream().anyMatch(period -> expectedRepaymentAmount.equals(period.getTotalDueForPeriod())
assertTrue(periods.stream().anyMatch(period -> expectedRepaymentAmount.equals(Utils.getDoubleValue(period.getTotalDueForPeriod()))
&& expectedRepaymentDueDate.equals(period.getDueDate())));
}
@@ -157,11 +157,11 @@ public class LoanRepaymentScheduleWithDownPaymentTest extends BaseLoanIntegratio
LocalDate expectedRepaymentDueDate = LocalDate.of(2022, 10, 3);
assertTrue(periods.stream() //
.anyMatch(period -> expectedDownPaymentAmount.equals(period.getTotalPaidForPeriod()) //
.anyMatch(period -> expectedDownPaymentAmount.equals(Utils.getDoubleValue(period.getTotalPaidForPeriod())) //
&& expectedDownPaymentDueDate.equals(period.getDueDate())));
assertEquals(expectedRepaymentAmount, summary.getTotalOutstanding());
assertEquals(expectedRepaymentAmount, Utils.getDoubleValue(summary.getTotalOutstanding()));
assertEquals(expectedDownPaymentAmount, summary.getTotalRepaymentTransaction());
assertTrue(periods.stream().anyMatch(period -> expectedRepaymentAmount.equals(period.getTotalDueForPeriod())
assertTrue(periods.stream().anyMatch(period -> expectedRepaymentAmount.equals(Utils.getDoubleValue(period.getTotalDueForPeriod()))
&& expectedRepaymentDueDate.equals(period.getDueDate())));
}
@@ -205,9 +205,9 @@ public class LoanRepaymentScheduleWithDownPaymentTest extends BaseLoanIntegratio
LocalDate expectedRepaymentDueDate = LocalDate.of(2022, 10, 3);
assertTrue(periods.stream() //
.anyMatch(period -> expectedDownPaymentAmount.equals(period.getTotalDueForPeriod()) //
.anyMatch(period -> expectedDownPaymentAmount.equals(Utils.getDoubleValue(period.getTotalDueForPeriod())) //
&& expectedDownPaymentDueDate.equals(period.getDueDate())));
assertTrue(periods.stream().anyMatch(period -> expectedRepaymentAmount.equals(period.getTotalDueForPeriod())
assertTrue(periods.stream().anyMatch(period -> expectedRepaymentAmount.equals(Utils.getDoubleValue(period.getTotalDueForPeriod()))
&& expectedRepaymentDueDate.equals(period.getDueDate())));
}
@@ -253,12 +253,12 @@ public class LoanRepaymentScheduleWithDownPaymentTest extends BaseLoanIntegratio
LocalDate expectedRepaymentDueDate = LocalDate.of(2022, 10, 3);
assertTrue(periods.stream() //
.anyMatch(period -> expectedFirstDownPaymentAmount.equals(period.getTotalDueForPeriod()) //
.anyMatch(period -> expectedFirstDownPaymentAmount.equals(Utils.getDoubleValue(period.getTotalDueForPeriod())) //
&& expectedFirstDownPaymentDueDate.equals(period.getDueDate())));
assertTrue(periods.stream() //
.anyMatch(period -> expectedSecondDownPaymentAmount.equals(period.getTotalDueForPeriod())
.anyMatch(period -> expectedSecondDownPaymentAmount.equals(Utils.getDoubleValue(period.getTotalDueForPeriod()))
&& expectedSecondDownPaymentDueDate.equals(period.getDueDate())));
assertTrue(periods.stream().anyMatch(period -> expectedRepaymentAmount.equals(period.getTotalDueForPeriod())
assertTrue(periods.stream().anyMatch(period -> expectedRepaymentAmount.equals(Utils.getDoubleValue(period.getTotalDueForPeriod()))
&& expectedRepaymentDueDate.equals(period.getDueDate())));
}
@@ -306,15 +306,15 @@ public class LoanRepaymentScheduleWithDownPaymentTest extends BaseLoanIntegratio
Double expectedTotalRepaymentAmount = expectedFirstDownPaymentAmount + expectedSecondDownPaymentAmount;
assertTrue(periods.stream() //
.anyMatch(period -> expectedFirstDownPaymentAmount.equals(period.getTotalPaidForPeriod()) //
.anyMatch(period -> expectedFirstDownPaymentAmount.equals(Utils.getDoubleValue(period.getTotalPaidForPeriod())) //
&& expectedFirstDownPaymentDueDate.equals(period.getDueDate())));
assertTrue(periods.stream() //
.anyMatch(period -> expectedSecondDownPaymentAmount.equals(period.getTotalPaidForPeriod())
.anyMatch(period -> expectedSecondDownPaymentAmount.equals(Utils.getDoubleValue(period.getTotalPaidForPeriod()))
&& expectedSecondDownPaymentDueDate.equals(period.getDueDate())));
assertTrue(periods.stream().anyMatch(period -> expectedRepaymentAmount.equals(period.getTotalDueForPeriod())
assertTrue(periods.stream().anyMatch(period -> expectedRepaymentAmount.equals(Utils.getDoubleValue(period.getTotalDueForPeriod()))
&& expectedRepaymentDueDate.equals(period.getDueDate())));
assertEquals(expectedRepaymentAmount, summary.getTotalOutstanding());
assertEquals(expectedTotalRepaymentAmount, summary.getTotalRepaymentTransaction());
assertEquals(expectedRepaymentAmount, Utils.getDoubleValue(summary.getTotalOutstanding()));
assertEquals(expectedTotalRepaymentAmount, Utils.getDoubleValue(summary.getTotalRepaymentTransaction()));
}
@Test
@@ -529,14 +529,14 @@ public class LoanRepaymentScheduleWithDownPaymentTest extends BaseLoanIntegratio
LocalDate expectedRepaymentDueDate = LocalDate.of(2022, 10, 3);
assertTrue(periods.stream() //
.anyMatch(period -> expectedDownPaymentAmount.equals(period.getTotalDueForPeriod()) //
.anyMatch(period -> expectedDownPaymentAmount.equals(Utils.getDoubleValue(period.getTotalDueForPeriod())) //
&& expectedDownPaymentDueDate.equals(period.getDueDate()) //
&& Double.valueOf(0.00).equals(period.getFeeChargesDue())));
&& Double.valueOf(0.00).equals(Utils.getDoubleValue(period.getFeeChargesDue()))));
assertTrue(periods.stream() //
.anyMatch(period -> expectedTotalDueForRepaymentInstallment.equals(period.getTotalDueForPeriod()) //
&& expectedRepaymentAmount.equals(period.getPrincipalDue()) //
.anyMatch(period -> expectedTotalDueForRepaymentInstallment.equals(Utils.getDoubleValue(period.getTotalDueForPeriod())) //
&& expectedRepaymentAmount.equals(Utils.getDoubleValue(period.getPrincipalDue())) //
&& expectedRepaymentDueDate.equals(period.getDueDate()) //
&& feeAmount.equals(period.getFeeChargesDue())));
&& feeAmount.equals(Utils.getDoubleValue(period.getFeeChargesDue()))));
}
@Test
@@ -694,16 +694,16 @@ public class LoanRepaymentScheduleWithDownPaymentTest extends BaseLoanIntegratio
LocalDate expectedRepaymentDueDate = LocalDate.of(2022, 10, 3);
assertTrue(periods.stream() //
.anyMatch(period -> expectedDownPaymentAmount.equals(period.getTotalDueForPeriod()) //
.anyMatch(period -> expectedDownPaymentAmount.equals(Utils.getDoubleValue(period.getTotalDueForPeriod())) //
&& expectedDownPaymentDueDate.equals(period.getDueDate()) //
&& Double.valueOf(0.00).equals(period.getFeeChargesDue()) //
&& Double.valueOf(0.00).equals(period.getInterestDue())));
&& Double.valueOf(0.00).equals(Utils.getDoubleValue(period.getFeeChargesDue())) //
&& Double.valueOf(0.00).equals(Utils.getDoubleValue(period.getInterestDue()))));
assertTrue(periods.stream() //
.anyMatch(period -> expectedTotalDueForRepaymentInstallment.equals(period.getTotalDueForPeriod()) //
&& expectedRepaymentAmount.equals(period.getPrincipalDue()) //
.anyMatch(period -> expectedTotalDueForRepaymentInstallment.equals(Utils.getDoubleValue(period.getTotalDueForPeriod())) //
&& expectedRepaymentAmount.equals(Utils.getDoubleValue(period.getPrincipalDue())) //
&& expectedRepaymentDueDate.equals(period.getDueDate()) //
&& feeAmount.equals(period.getFeeChargesDue()) //
&& Double.valueOf(10.0).equals(period.getInterestDue())));
&& feeAmount.equals(Utils.getDoubleValue(period.getFeeChargesDue())) //
&& Double.valueOf(10.0).equals(Utils.getDoubleValue(period.getInterestDue()))));
}
@Test
@@ -864,10 +864,11 @@ public class LoanRepaymentScheduleWithDownPaymentTest extends BaseLoanIntegratio
LocalDate expectedRepaymentDueDate = LocalDate.of(2022, 10, 3);
assertTrue(periods.stream() //
.anyMatch(period -> expectedDownPaymentAmount.equals(period.getTotalDueForPeriod()) //
.anyMatch(period -> expectedDownPaymentAmount.equals(Utils.getDoubleValue(period.getTotalDueForPeriod())) //
&& expectedDownPaymentDueDate.equals(period.getDueDate())));
assertTrue(periods.stream().anyMatch(period -> expectedRepaymentAmount.equals(period.getTotalDueForPeriod())
&& expectedRepaymentDueDate.equals(period.getDueDate())));
assertTrue(
periods.stream().anyMatch(period -> expectedRepaymentAmount.equals(Utils.getDoubleValue(period.getTotalDueForPeriod()))
&& expectedRepaymentDueDate.equals(period.getDueDate())));
assertNotNull(loanDetails.getDelinquencyRange());
assertEquals(2, loanDetails.getDelinquent().getDelinquentDays());
} finally {
@@ -1662,12 +1663,13 @@ public class LoanRepaymentScheduleWithDownPaymentTest extends BaseLoanIntegratio
Double expectedTotalRepaymentTransactionAmount = 0.00;
assertTrue(periods.stream() //
.anyMatch(period -> expectedDownPaymentAmount.equals(period.getTotalOutstandingForPeriod()) //
.anyMatch(period -> expectedDownPaymentAmount.equals(Utils.getDoubleValue(period.getTotalOutstandingForPeriod())) //
&& expectedDownPaymentDueDate.equals(period.getDueDate())));
assertEquals(expectedTotalOutstandingAmount, summary.getTotalOutstanding());
assertEquals(expectedTotalOutstandingAmount, Utils.getDoubleValue(summary.getTotalOutstanding()));
assertEquals(expectedTotalRepaymentTransactionAmount, summary.getTotalRepaymentTransaction());
assertTrue(periods.stream().anyMatch(period -> expectedRepaymentAmount.equals(period.getTotalOutstandingForPeriod())
&& expectedRepaymentDueDate.equals(period.getDueDate())));
assertTrue(periods.stream()
.anyMatch(period -> expectedRepaymentAmount.equals(Utils.getDoubleValue(period.getTotalOutstandingForPeriod()))
&& expectedRepaymentDueDate.equals(period.getDueDate())));
}
private void checkNoDownPaymentTransaction(final Integer loanID) {
@@ -19,7 +19,9 @@
package org.apache.fineract.integrationtests;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.List;
import org.apache.fineract.client.models.GetLoansLoanIdRepaymentPeriod;
import org.apache.fineract.client.models.GetLoansLoanIdResponse;
import org.apache.fineract.client.models.PostLoanProductsResponse;
import org.apache.fineract.client.models.PostLoansResponse;
@@ -248,4 +250,38 @@ public class LoanSummaryTest extends BaseLoanIntegrationTest {
});
}
@Test
public void testCapitalizedIncomeExistsInRepaymentScheduleAndModifiesPrincipal() {
runAt("01 March 2023", () -> {
final PostLoanProductsResponse loanProductsResponse = loanProductHelper
.createLoanProduct(create4IProgressiveWithCapitalizedIncome());
PostLoansResponse postLoansResponse = loanTransactionHelper.applyLoan(applyLP2ProgressiveLoanRequest(clientId,
loanProductsResponse.getResourceId(), "01 March 2023", 10000.00, 12.00, 4, null));
loanId = postLoansResponse.getLoanId();
loanTransactionHelper.approveLoan(loanId, approveLoanRequest(10000.00, "01 March 2023"));
disburseLoan(loanId, BigDecimal.valueOf(1000.00), "01 March 2023");
});
runAt("02 March 2023", () -> {
loanTransactionHelper.addCapitalizedIncome(loanId, "02 March 2023", 100.00);
});
BigDecimal thousand = BigDecimal.valueOf(1000.0);
BigDecimal hundred = BigDecimal.valueOf(100.0);
BigDecimal thousandOneHundred = BigDecimal.valueOf(1100.0);
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanId);
Assertions.assertEquals(thousand, loanDetails.getPrincipal().setScale(1, RoundingMode.HALF_UP));
Assertions.assertEquals(thousand, loanDetails.getSummary().getPrincipalDisbursed().setScale(1, RoundingMode.HALF_UP));
Assertions.assertEquals(hundred,
loanDetails.getSummary().getTotalPrincipalFromCapitalizedIncome().setScale(1, RoundingMode.HALF_UP));
Assertions.assertEquals(thousandOneHundred, loanDetails.getSummary().getTotalPrincipal().setScale(1, RoundingMode.HALF_UP));
Assertions.assertEquals(thousandOneHundred, loanDetails.getSummary().getPrincipalOutstanding().setScale(1, RoundingMode.HALF_UP));
List<GetLoansLoanIdRepaymentPeriod> periods = loanDetails.getRepaymentSchedule().getPeriods();
Assertions.assertEquals(6, periods.size());
Assertions.assertEquals(thousand, periods.get(0).getPrincipalLoanBalanceOutstanding().setScale(1, RoundingMode.HALF_UP));
Assertions.assertEquals(hundred, periods.get(1).getPrincipalLoanBalanceOutstanding().setScale(1, RoundingMode.HALF_UP));
}
}
@@ -46,8 +46,8 @@ public class ProgressiveLoanTrancheTest extends BaseLoanIntegrationTest {
runAt("20 December 2024", () -> {
Long loanId = applyAndApproveProgressiveLoan(client.getClientId(), loanProductsResponse.getResourceId(), "20 December 2024",
500.0, 7.0, 6, (request) -> request.disbursementData(
List.of(new PostLoansDisbursementData().expectedDisbursementDate("20 December 2024").principal(100.0))));
500.0, 7.0, 6, (request) -> request.disbursementData(List.of(new PostLoansDisbursementData()
.expectedDisbursementDate("20 December 2024").principal(BigDecimal.valueOf(100.0)))));
loanIdRef.set(loanId);
@@ -39,6 +39,8 @@ import io.restassured.response.Response;
import io.restassured.specification.RequestSpecification;
import io.restassured.specification.ResponseSpecification;
import java.io.File;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.security.SecureRandom;
import java.text.DateFormat;
import java.text.ParseException;
@@ -569,4 +571,8 @@ public final class Utils {
public static long getDifferenceInMonths(final LocalDate localDateBefore, final LocalDate localDateAfter) {
return MONTHS.between(localDateBefore, localDateAfter);
}
public static Double getDoubleValue(BigDecimal amount) {
return amount == null ? null : amount.setScale(2, RoundingMode.HALF_UP).doubleValue();
}
}
@@ -2688,7 +2688,7 @@ public class LoanTransactionHelper {
log.info(" Id {} code {} date {} amount {}", transaction.getId(), transaction.getType().getCode(), transaction.getDate(),
transaction.getAmount());
if (transactionType.equals(transaction.getType().getCode())) {
transactionsAmount += transaction.getAmount();
transactionsAmount += Utils.getDoubleValue(transaction.getAmount());
}
}
assertEquals(amountExpected, transactionsAmount);
@@ -2707,7 +2707,7 @@ public class LoanTransactionHelper {
}
}
assertEquals(transactionExpected, Utils.dateFormatter.format(lastTransaction.getDate()));
assertEquals(amountExpected, lastTransaction.getAmount());
assertEquals(amountExpected, Utils.getDoubleValue(lastTransaction.getAmount()));
return lastTransaction.getId();
}
@@ -2722,7 +2722,7 @@ public class LoanTransactionHelper {
if (getLoansLoanIdSummary != null) {
log.info("Loan with Principal Outstanding Balance {} expected {}", getLoansLoanIdSummary.getPrincipalOutstanding(),
amountExpected);
assertEquals(amountExpected, getLoansLoanIdSummary.getPrincipalOutstanding());
assertEquals(amountExpected, Utils.getDoubleValue(getLoansLoanIdSummary.getPrincipalOutstanding()));
}
}
@@ -2730,7 +2730,7 @@ public class LoanTransactionHelper {
GetLoansLoanIdSummary getLoansLoanIdSummary = getLoansLoanIdResponse.getSummary();
if (getLoansLoanIdSummary != null) {
log.info("Loan with Fees Outstanding Balance {} expected {}", getLoansLoanIdSummary.getFeeChargesOutstanding(), amountExpected);
assertEquals(amountExpected, getLoansLoanIdSummary.getFeeChargesOutstanding());
assertEquals(amountExpected, Utils.getDoubleValue(getLoansLoanIdSummary.getFeeChargesOutstanding()));
}
}
@@ -2738,14 +2738,14 @@ public class LoanTransactionHelper {
GetLoansLoanIdSummary getLoansLoanIdSummary = getLoansLoanIdResponse.getSummary();
assertNotNull(getLoansLoanIdSummary);
log.info("Loan with Fees Outstanding Balance {} expected {}", getLoansLoanIdSummary.getFeeChargesOutstanding(), amountExpected);
assertEquals(amountExpected, getLoansLoanIdSummary.getPenaltyChargesOutstanding());
assertEquals(amountExpected, Utils.getDoubleValue(getLoansLoanIdSummary.getPenaltyChargesOutstanding()));
}
public void validateLoanTotalOustandingBalance(GetLoansLoanIdResponse getLoansLoanIdResponse, Double amountExpected) {
GetLoansLoanIdSummary getLoansLoanIdSummary = getLoansLoanIdResponse.getSummary();
if (getLoansLoanIdSummary != null) {
log.info("Loan with Total Outstanding Balance {} expected {}", getLoansLoanIdSummary.getTotalOutstanding(), amountExpected);
assertEquals(amountExpected, getLoansLoanIdSummary.getTotalOutstanding());
assertEquals(amountExpected, Utils.getDoubleValue(getLoansLoanIdSummary.getTotalOutstanding()));
}
}