FINERACT-2081: Loan account summary with Unpaid Payable Interest
This commit is contained in:
committed by
Adam Saghy
parent
526fb6b3a8
commit
06edb4c7d9
@@ -437,7 +437,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"default": null,
|
"default": null,
|
||||||
"name": "totalUnpaidAccruedDueInterest",
|
"name": "totalUnpaidPayableDueInterest",
|
||||||
"type": [
|
"type": [
|
||||||
"null",
|
"null",
|
||||||
"bigdecimal"
|
"bigdecimal"
|
||||||
@@ -445,7 +445,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"default": null,
|
"default": null,
|
||||||
"name": "totalUnpaidAccruedNotDueInterest",
|
"name": "totalUnpaidPayableNotDueInterest",
|
||||||
"type": [
|
"type": [
|
||||||
"null",
|
"null",
|
||||||
"bigdecimal"
|
"bigdecimal"
|
||||||
|
|||||||
+45
-58
@@ -26,8 +26,9 @@ import lombok.Builder;
|
|||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.experimental.Accessors;
|
import lombok.experimental.Accessors;
|
||||||
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
||||||
import org.apache.fineract.infrastructure.core.service.MathUtil;
|
|
||||||
import org.apache.fineract.organisation.monetary.data.CurrencyData;
|
import org.apache.fineract.organisation.monetary.data.CurrencyData;
|
||||||
|
import org.apache.fineract.organisation.monetary.domain.Money;
|
||||||
|
import org.apache.fineract.organisation.monetary.domain.MoneyHelper;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType;
|
||||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanScheduleData;
|
import org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanScheduleData;
|
||||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanSchedulePeriodData;
|
import org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanSchedulePeriodData;
|
||||||
@@ -100,11 +101,12 @@ public class LoanSummaryData {
|
|||||||
private final Long chargeOffReasonId;
|
private final Long chargeOffReasonId;
|
||||||
private final String chargeOffReason;
|
private final String chargeOffReason;
|
||||||
|
|
||||||
private BigDecimal totalUnpaidAccruedDueInterest;
|
private BigDecimal totalUnpaidPayableDueInterest;
|
||||||
private BigDecimal totalUnpaidAccruedNotDueInterest;
|
private BigDecimal totalUnpaidPayableNotDueInterest;
|
||||||
|
|
||||||
public static LoanSummaryData withTransactionAmountsSummary(final LoanSummaryData defaultSummaryData,
|
public static LoanSummaryData withTransactionAmountsSummary(final LoanSummaryData defaultSummaryData,
|
||||||
final LoanScheduleData repaymentSchedule, final Collection<LoanTransactionBalance> loanTransactionBalances) {
|
final LoanScheduleData repaymentSchedule, final Collection<LoanTransactionBalance> loanTransactionBalances) {
|
||||||
|
final LocalDate businessDate = DateUtils.getBusinessLocalDate();
|
||||||
|
|
||||||
BigDecimal totalMerchantRefund = BigDecimal.ZERO;
|
BigDecimal totalMerchantRefund = BigDecimal.ZERO;
|
||||||
BigDecimal totalMerchantRefundReversed = BigDecimal.ZERO;
|
BigDecimal totalMerchantRefundReversed = BigDecimal.ZERO;
|
||||||
@@ -121,8 +123,8 @@ public class LoanSummaryData {
|
|||||||
BigDecimal totalRepaymentTransactionReversed = BigDecimal.ZERO;
|
BigDecimal totalRepaymentTransactionReversed = BigDecimal.ZERO;
|
||||||
BigDecimal totalInterestPaymentWaiver = BigDecimal.ZERO;
|
BigDecimal totalInterestPaymentWaiver = BigDecimal.ZERO;
|
||||||
BigDecimal totalInterestRefund = BigDecimal.ZERO;
|
BigDecimal totalInterestRefund = BigDecimal.ZERO;
|
||||||
BigDecimal totalUnpaidAccruedDueInterest = BigDecimal.ZERO;
|
BigDecimal totalUnpaidPayableDueInterest = BigDecimal.ZERO;
|
||||||
BigDecimal totalUnpaidAccruedNotDueInterest = BigDecimal.ZERO;
|
BigDecimal totalUnpaidPayableNotDueInterest = BigDecimal.ZERO;
|
||||||
|
|
||||||
totalChargeAdjustment = fetchLoanTransactionBalanceByType(loanTransactionBalances,
|
totalChargeAdjustment = fetchLoanTransactionBalanceByType(loanTransactionBalances,
|
||||||
LoanTransactionType.CHARGE_ADJUSTMENT.getValue());
|
LoanTransactionType.CHARGE_ADJUSTMENT.getValue());
|
||||||
@@ -160,25 +162,12 @@ public class LoanSummaryData {
|
|||||||
LoanTransactionType.REPAYMENT.getValue());
|
LoanTransactionType.REPAYMENT.getValue());
|
||||||
|
|
||||||
if (repaymentSchedule != null) {
|
if (repaymentSchedule != null) {
|
||||||
// Accrued Due Interest on Past due installments
|
// Outstanding Interest on Past due installments
|
||||||
totalUnpaidAccruedDueInterest = computeTotalAccruedDueInterestAmount(repaymentSchedule.getPeriods());
|
totalUnpaidPayableDueInterest = computeTotalUnpaidPayableDueInterestAmount(repaymentSchedule.getPeriods(), businessDate);
|
||||||
if (MathUtil.isGreaterThanZero(totalUnpaidAccruedDueInterest)) {
|
|
||||||
totalUnpaidAccruedDueInterest = totalUnpaidAccruedDueInterest
|
|
||||||
.subtract(computeTotalInterestPaidDueAmount(repaymentSchedule.getPeriods()));
|
|
||||||
if (MathUtil.isLessThanZero(totalUnpaidAccruedDueInterest)) {
|
|
||||||
totalUnpaidAccruedDueInterest = BigDecimal.ZERO;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Accrued Due Interest on Actual Installment
|
// Accumulated daily interest of the current Installment period
|
||||||
totalUnpaidAccruedNotDueInterest = computeTotalAccruedNotDueInterestAmountOnActualPeriod(repaymentSchedule.getPeriods());
|
totalUnpaidPayableNotDueInterest = computeTotalUnpaidPayableNotDueInterestAmountOnActualPeriod(repaymentSchedule.getPeriods(),
|
||||||
if (MathUtil.isGreaterThanZero(totalUnpaidAccruedNotDueInterest)) {
|
businessDate, defaultSummaryData.currency);
|
||||||
totalUnpaidAccruedNotDueInterest = totalUnpaidAccruedNotDueInterest
|
|
||||||
.subtract(computeTotalInterestPaidNotDueAmountOnActualPeriod(repaymentSchedule.getPeriods()));
|
|
||||||
if (MathUtil.isLessThanZero(totalUnpaidAccruedNotDueInterest)) {
|
|
||||||
totalUnpaidAccruedNotDueInterest = BigDecimal.ZERO;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return LoanSummaryData.builder().currency(defaultSummaryData.currency).principalDisbursed(defaultSummaryData.principalDisbursed)
|
return LoanSummaryData.builder().currency(defaultSummaryData.currency).principalDisbursed(defaultSummaryData.principalDisbursed)
|
||||||
@@ -212,8 +201,8 @@ public class LoanSummaryData {
|
|||||||
.totalChargeback(totalChargeback).totalCreditBalanceRefund(totalCreditBalanceRefund)
|
.totalChargeback(totalChargeback).totalCreditBalanceRefund(totalCreditBalanceRefund)
|
||||||
.totalCreditBalanceRefundReversed(totalCreditBalanceRefundReversed).totalRepaymentTransaction(totalRepaymentTransaction)
|
.totalCreditBalanceRefundReversed(totalCreditBalanceRefundReversed).totalRepaymentTransaction(totalRepaymentTransaction)
|
||||||
.totalRepaymentTransactionReversed(totalRepaymentTransactionReversed).totalInterestPaymentWaiver(totalInterestPaymentWaiver)
|
.totalRepaymentTransactionReversed(totalRepaymentTransactionReversed).totalInterestPaymentWaiver(totalInterestPaymentWaiver)
|
||||||
.totalUnpaidAccruedDueInterest(totalUnpaidAccruedDueInterest)
|
.totalUnpaidPayableDueInterest(totalUnpaidPayableDueInterest)
|
||||||
.totalUnpaidAccruedNotDueInterest(totalUnpaidAccruedNotDueInterest).totalInterestRefund(totalInterestRefund).build();
|
.totalUnpaidPayableNotDueInterest(totalUnpaidPayableNotDueInterest).totalInterestRefund(totalInterestRefund).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static BigDecimal fetchLoanTransactionBalanceByType(final Collection<LoanTransactionBalance> loanTransactionBalances,
|
private static BigDecimal fetchLoanTransactionBalanceByType(final Collection<LoanTransactionBalance> loanTransactionBalances,
|
||||||
@@ -236,44 +225,42 @@ public class LoanSummaryData {
|
|||||||
return LoanSummaryData.builder().currency(currencyData).build();
|
return LoanSummaryData.builder().currency(currencyData).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static BigDecimal computeTotalAccruedDueInterestAmount(Collection<LoanSchedulePeriodData> periods) {
|
private static BigDecimal computeTotalUnpaidPayableDueInterestAmount(Collection<LoanSchedulePeriodData> periods,
|
||||||
final LocalDate businessDate = DateUtils.getBusinessLocalDate();
|
final LocalDate businessDate) {
|
||||||
return periods.stream().filter(period -> !period.getDownPaymentPeriod() && businessDate.isAfter(period.getDueDate()))
|
return periods.stream().filter(period -> !period.getDownPaymentPeriod() && businessDate.compareTo(period.getDueDate()) >= 0)
|
||||||
.map(period -> period.getTotalAccruedInterest()).reduce(BigDecimal.ZERO, BigDecimal::add);
|
.map(period -> period.getInterestOutstanding()).reduce(BigDecimal.ZERO, BigDecimal::add);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static BigDecimal computeTotalInterestPaidDueAmount(Collection<LoanSchedulePeriodData> periods) {
|
private static BigDecimal computeTotalUnpaidPayableNotDueInterestAmountOnActualPeriod(final Collection<LoanSchedulePeriodData> periods,
|
||||||
final LocalDate businessDate = DateUtils.getBusinessLocalDate();
|
final LocalDate businessDate, final CurrencyData currency) {
|
||||||
return periods.stream().filter(period -> !period.getDownPaymentPeriod() && businessDate.isAfter(period.getDueDate()))
|
// Find the current Period (If exists one) based on the Business date
|
||||||
.map(period -> period.getInterestPaid()).reduce(BigDecimal.ZERO, BigDecimal::add);
|
final Optional<LoanSchedulePeriodData> optCurrentPeriod = periods.stream()
|
||||||
|
.filter(period -> !period.getDownPaymentPeriod() && period.isActualPeriodForNotDuePayableCalculation(businessDate))
|
||||||
|
.findFirst();
|
||||||
|
|
||||||
|
if (optCurrentPeriod.isPresent()) {
|
||||||
|
final LoanSchedulePeriodData currentPeriod = optCurrentPeriod.get();
|
||||||
|
final long remainingDays = currentPeriod.getDaysInPeriod()
|
||||||
|
- DateUtils.getDifferenceInDays(currentPeriod.getFromDate(), businessDate);
|
||||||
|
|
||||||
|
return computeAccruedInterestTillDay(currentPeriod, remainingDays, currency);
|
||||||
|
}
|
||||||
|
// Default value equal to Zero
|
||||||
|
return BigDecimal.ZERO;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static BigDecimal computeTotalAccruedNotDueInterestAmountOnActualPeriod(Collection<LoanSchedulePeriodData> periods) {
|
private static BigDecimal computeAccruedInterestTillDay(final LoanSchedulePeriodData period, final long untilDay,
|
||||||
final LocalDate businessDate = DateUtils.getBusinessLocalDate();
|
final CurrencyData currency) {
|
||||||
return periods.stream()
|
Integer remainingDays = period.getDaysInPeriod();
|
||||||
.filter(period -> !period.getDownPaymentPeriod() && isActualPeriod(period) && businessDate.isBefore(period.getDueDate()))
|
BigDecimal totalAccruedInterest = BigDecimal.ZERO;
|
||||||
.map(period -> period.getTotalAccruedInterest()).reduce(BigDecimal.ZERO, BigDecimal::add);
|
while (remainingDays > untilDay) {
|
||||||
}
|
final BigDecimal accruedInterest = period.getInterestDue().subtract(totalAccruedInterest)
|
||||||
|
.divide(BigDecimal.valueOf(remainingDays), MoneyHelper.getMathContext());
|
||||||
private static BigDecimal computeTotalInterestPaidNotDueAmountOnActualPeriod(Collection<LoanSchedulePeriodData> periods) {
|
totalAccruedInterest = totalAccruedInterest.add(accruedInterest);
|
||||||
final LocalDate businessDate = DateUtils.getBusinessLocalDate();
|
remainingDays--;
|
||||||
return periods.stream()
|
|
||||||
.filter(period -> !period.getDownPaymentPeriod() && isActualPeriod(period) && businessDate.isBefore(period.getDueDate()))
|
|
||||||
.map(period -> period.getInterestPaid()).reduce(BigDecimal.ZERO, BigDecimal::add);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean isActualPeriod(LoanSchedulePeriodData period) {
|
|
||||||
final LocalDate businessDate = DateUtils.getBusinessLocalDate();
|
|
||||||
boolean actualPeriod = false;
|
|
||||||
if (period.getPeriod() != null) {
|
|
||||||
if (period.getPeriod() == 1) {
|
|
||||||
actualPeriod = ((businessDate.isEqual(period.getFromDate()) || businessDate.isAfter(period.getFromDate()))
|
|
||||||
&& businessDate.isBefore(period.getDueDate()));
|
|
||||||
} else {
|
|
||||||
actualPeriod = (businessDate.isAfter(period.getFromDate()) && businessDate.isBefore(period.getDueDate()));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return actualPeriod;
|
return Money.of(currency, totalAccruedInterest).getAmount();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+13
@@ -402,6 +402,19 @@ public final class LoanSchedulePeriodData {
|
|||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isActualPeriodForNotDuePayableCalculation(final LocalDate businessDate) {
|
||||||
|
boolean actualPeriod = false;
|
||||||
|
if (getPeriod() != null) {
|
||||||
|
if (getPeriod() == 1) {
|
||||||
|
actualPeriod = ((businessDate.compareTo(getFromDate()) >= 0) && businessDate.compareTo(getDueDate()) < 0);
|
||||||
|
} else {
|
||||||
|
actualPeriod = ((businessDate.compareTo(getFromDate()) >= 0) && businessDate.compareTo(getDueDate()) < 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return actualPeriod;
|
||||||
|
}
|
||||||
|
|
||||||
public BigDecimal getPrincipalDisbursed() {
|
public BigDecimal getPrincipalDisbursed() {
|
||||||
return defaultToZeroIfNull(this.principalDisbursed);
|
return defaultToZeroIfNull(this.principalDisbursed);
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -640,9 +640,9 @@ final class LoansApiResourceSwagger {
|
|||||||
@Schema(example = "0.000000")
|
@Schema(example = "0.000000")
|
||||||
public Double totalRepaymentTransactionReversed;
|
public Double totalRepaymentTransactionReversed;
|
||||||
@Schema(example = "0.000000")
|
@Schema(example = "0.000000")
|
||||||
public BigDecimal totalUnpaidAccruedDueInterest;
|
public BigDecimal totalUnpaidPayableDueInterest;
|
||||||
@Schema(example = "0.000000")
|
@Schema(example = "0.000000")
|
||||||
public BigDecimal totalUnpaidAccruedNotDueInterest;
|
public BigDecimal totalUnpaidPayableNotDueInterest;
|
||||||
@Schema(example = "0.000000")
|
@Schema(example = "0.000000")
|
||||||
public BigDecimal totalInterestRefund;
|
public BigDecimal totalInterestRefund;
|
||||||
public Set<GetLoansLoanIdOverdueCharges> overdueCharges;
|
public Set<GetLoansLoanIdOverdueCharges> overdueCharges;
|
||||||
|
|||||||
+131
-32
@@ -4720,8 +4720,8 @@ public class AdvancedPaymentAllocationLoanRepaymentScheduleTest extends BaseLoan
|
|||||||
|
|
||||||
// After Disbursement we are expecting no Accrual transactions
|
// After Disbursement we are expecting no Accrual transactions
|
||||||
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanResponse.getLoanId());
|
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanResponse.getLoanId());
|
||||||
assertEquals(BigDecimal.ZERO, loanDetails.getSummary().getTotalUnpaidAccruedDueInterest().stripTrailingZeros());
|
assertEquals(BigDecimal.ZERO, loanDetails.getSummary().getTotalUnpaidPayableDueInterest().stripTrailingZeros());
|
||||||
assertEquals(BigDecimal.ZERO, loanDetails.getSummary().getTotalUnpaidAccruedNotDueInterest().stripTrailingZeros());
|
assertEquals(BigDecimal.ZERO, loanDetails.getSummary().getTotalUnpaidPayableNotDueInterest().stripTrailingZeros());
|
||||||
});
|
});
|
||||||
|
|
||||||
// Not Due yet
|
// Not Due yet
|
||||||
@@ -4732,14 +4732,14 @@ public class AdvancedPaymentAllocationLoanRepaymentScheduleTest extends BaseLoan
|
|||||||
periodicAccrualAccountingHelper.runPeriodicAccrualAccounting("30 January 2024");
|
periodicAccrualAccountingHelper.runPeriodicAccrualAccounting("30 January 2024");
|
||||||
|
|
||||||
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(createdLoanId.get());
|
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(createdLoanId.get());
|
||||||
assertEquals(BigDecimal.ZERO, loanDetails.getSummary().getTotalUnpaidAccruedDueInterest().stripTrailingZeros());
|
assertEquals(BigDecimal.ZERO, loanDetails.getSummary().getTotalUnpaidPayableDueInterest().stripTrailingZeros());
|
||||||
assertEquals(new BigDecimal("0.97"), loanDetails.getSummary().getTotalUnpaidAccruedNotDueInterest().stripTrailingZeros());
|
assertEquals(new BigDecimal("0.97"), loanDetails.getSummary().getTotalUnpaidPayableNotDueInterest().stripTrailingZeros());
|
||||||
|
|
||||||
// Partial interest repayment
|
// Partial interest repayment
|
||||||
addRepaymentForLoan(createdLoanId.get(), 20.50, "30 January 2024");
|
addRepaymentForLoan(createdLoanId.get(), 20.50, "30 January 2024");
|
||||||
loanDetails = loanTransactionHelper.getLoanDetails(createdLoanId.get());
|
loanDetails = loanTransactionHelper.getLoanDetails(createdLoanId.get());
|
||||||
assertEquals(BigDecimal.ZERO, loanDetails.getSummary().getTotalUnpaidAccruedDueInterest().stripTrailingZeros());
|
assertEquals(BigDecimal.ZERO, loanDetails.getSummary().getTotalUnpaidPayableDueInterest().stripTrailingZeros());
|
||||||
assertEquals(new BigDecimal("0.05"), loanDetails.getSummary().getTotalUnpaidAccruedNotDueInterest().stripTrailingZeros());
|
assertEquals(new BigDecimal("0.97"), loanDetails.getSummary().getTotalUnpaidPayableNotDueInterest().stripTrailingZeros());
|
||||||
});
|
});
|
||||||
|
|
||||||
// Not Due and Due Interest
|
// Not Due and Due Interest
|
||||||
@@ -4749,8 +4749,8 @@ public class AdvancedPaymentAllocationLoanRepaymentScheduleTest extends BaseLoan
|
|||||||
periodicAccrualAccountingHelper.runPeriodicAccrualAccounting("20 February 2024");
|
periodicAccrualAccountingHelper.runPeriodicAccrualAccounting("20 February 2024");
|
||||||
|
|
||||||
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(createdLoanId.get());
|
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(createdLoanId.get());
|
||||||
assertEquals(new BigDecimal("0.12"), loanDetails.getSummary().getTotalUnpaidAccruedDueInterest().stripTrailingZeros());
|
assertEquals(new BigDecimal("0.12"), loanDetails.getSummary().getTotalUnpaidPayableDueInterest().stripTrailingZeros());
|
||||||
assertEquals(new BigDecimal("0.52"), loanDetails.getSummary().getTotalUnpaidAccruedNotDueInterest().stripTrailingZeros());
|
assertEquals(new BigDecimal("0.52"), loanDetails.getSummary().getTotalUnpaidPayableNotDueInterest().stripTrailingZeros());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4767,11 +4767,12 @@ public class AdvancedPaymentAllocationLoanRepaymentScheduleTest extends BaseLoan
|
|||||||
runAt(operationDate, () -> {
|
runAt(operationDate, () -> {
|
||||||
Long clientId = clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId();
|
Long clientId = clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId();
|
||||||
PostLoanProductsRequest product = createOnePeriod30DaysLongNoInterestPeriodicAccrualProductWithAdvancedPaymentAllocation()
|
PostLoanProductsRequest product = createOnePeriod30DaysLongNoInterestPeriodicAccrualProductWithAdvancedPaymentAllocation()
|
||||||
.interestRatePerPeriod(12.0).interestCalculationPeriodType(RepaymentFrequencyType.DAYS).interestRateFrequencyType(YEARS)
|
.interestRatePerPeriod(108.0).interestCalculationPeriodType(RepaymentFrequencyType.DAYS)
|
||||||
.daysInMonthType(DaysInMonthType.ACTUAL.getValue()).daysInYearType(DaysInYearType.DAYS_365.getValue())
|
.interestRateFrequencyType(YEARS).daysInMonthType(DaysInMonthType.ACTUAL.getValue())
|
||||||
.numberOfRepayments(4)//
|
.daysInYearType(DaysInYearType.DAYS_360.getValue()).numberOfRepayments(4)//
|
||||||
.repaymentEvery(5)//
|
.maxInterestRatePerPeriod((double) 110)//
|
||||||
.repaymentFrequencyType(0L)//
|
.repaymentEvery(1)//
|
||||||
|
.repaymentFrequencyType(1L)//
|
||||||
.allowPartialPeriodInterestCalcualtion(false)//
|
.allowPartialPeriodInterestCalcualtion(false)//
|
||||||
.multiDisburseLoan(false)//
|
.multiDisburseLoan(false)//
|
||||||
.disallowExpectedDisbursements(null)//
|
.disallowExpectedDisbursements(null)//
|
||||||
@@ -4781,7 +4782,8 @@ public class AdvancedPaymentAllocationLoanRepaymentScheduleTest extends BaseLoan
|
|||||||
.installmentAmountInMultiplesOf(null)//
|
.installmentAmountInMultiplesOf(null)//
|
||||||
;//
|
;//
|
||||||
PostLoanProductsResponse loanProductResponse = loanProductHelper.createLoanProduct(product);
|
PostLoanProductsResponse loanProductResponse = loanProductHelper.createLoanProduct(product);
|
||||||
PostLoansRequest applicationRequest = applyLoanRequest(clientId, loanProductResponse.getResourceId(), operationDate, 1000.0, 4);
|
PostLoansRequest applicationRequest = applyLoanRequest(clientId, loanProductResponse.getResourceId(), operationDate, 1000.0, 4)
|
||||||
|
.interestRatePerPeriod(BigDecimal.valueOf(108.0));
|
||||||
|
|
||||||
applicationRequest = applicationRequest.interestCalculationPeriodType(DAYS)
|
applicationRequest = applicationRequest.interestCalculationPeriodType(DAYS)
|
||||||
.transactionProcessingStrategyCode(LoanProductTestBuilder.ADVANCED_PAYMENT_ALLOCATION_STRATEGY);
|
.transactionProcessingStrategyCode(LoanProductTestBuilder.ADVANCED_PAYMENT_ALLOCATION_STRATEGY);
|
||||||
@@ -4796,12 +4798,13 @@ public class AdvancedPaymentAllocationLoanRepaymentScheduleTest extends BaseLoan
|
|||||||
loanTransactionHelper.disburseLoan(loanResponse.getLoanId(), new PostLoansLoanIdRequest().actualDisbursementDate(operationDate)
|
loanTransactionHelper.disburseLoan(loanResponse.getLoanId(), new PostLoansLoanIdRequest().actualDisbursementDate(operationDate)
|
||||||
.dateFormat(DATETIME_PATTERN).transactionAmount(BigDecimal.valueOf(1000.0)).locale("en"));
|
.dateFormat(DATETIME_PATTERN).transactionAmount(BigDecimal.valueOf(1000.0)).locale("en"));
|
||||||
|
|
||||||
// After Disbursement we are expecting no Accrual transactions
|
// After Disbursement we are expecting amount in Zero (first day)
|
||||||
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanResponse.getLoanId());
|
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanResponse.getLoanId());
|
||||||
assertEquals(BigDecimal.ZERO, loanDetails.getSummary().getTotalUnpaidAccruedDueInterest().stripTrailingZeros());
|
assertEquals(BigDecimal.ZERO, loanDetails.getSummary().getTotalUnpaidPayableDueInterest().stripTrailingZeros());
|
||||||
assertEquals(BigDecimal.ZERO, loanDetails.getSummary().getTotalUnpaidAccruedNotDueInterest().stripTrailingZeros());
|
assertEquals(BigDecimal.ZERO, loanDetails.getSummary().getTotalUnpaidPayableNotDueInterest().stripTrailingZeros());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// First day on First Period, then TotalUnpaidPayableDueInterest = 0 and TotalUnpaidPayableNotDueInterest = 3
|
||||||
runAt("2 January 2024", () -> {
|
runAt("2 January 2024", () -> {
|
||||||
// Generate the Accruals
|
// Generate the Accruals
|
||||||
final PeriodicAccrualAccountingHelper periodicAccrualAccountingHelper = new PeriodicAccrualAccountingHelper(requestSpec,
|
final PeriodicAccrualAccountingHelper periodicAccrualAccountingHelper = new PeriodicAccrualAccountingHelper(requestSpec,
|
||||||
@@ -4809,30 +4812,126 @@ public class AdvancedPaymentAllocationLoanRepaymentScheduleTest extends BaseLoan
|
|||||||
periodicAccrualAccountingHelper.runPeriodicAccrualAccounting("2 January 2024");
|
periodicAccrualAccountingHelper.runPeriodicAccrualAccounting("2 January 2024");
|
||||||
|
|
||||||
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(createdLoanId.get());
|
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(createdLoanId.get());
|
||||||
assertEquals(BigDecimal.ZERO, loanDetails.getSummary().getTotalUnpaidAccruedDueInterest().stripTrailingZeros());
|
assertEquals(BigDecimal.ZERO, loanDetails.getSummary().getTotalUnpaidPayableDueInterest().stripTrailingZeros());
|
||||||
assertEquals(BigDecimal.ZERO, loanDetails.getSummary().getTotalUnpaidAccruedNotDueInterest().stripTrailingZeros());
|
assertEquals(BigDecimal.valueOf(3.0).stripTrailingZeros(),
|
||||||
|
loanDetails.getSummary().getTotalUnpaidPayableNotDueInterest().stripTrailingZeros());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Second day on First Period, then TotalUnpaidPayableDueInterest = 0 and TotalUnpaidPayableNotDueInterest = 6
|
||||||
runAt("3 January 2024", () -> {
|
runAt("3 January 2024", () -> {
|
||||||
// Add a Charge
|
|
||||||
addCharge(createdLoanId.get(), false, 10, "6 January 2024");
|
|
||||||
|
|
||||||
final PeriodicAccrualAccountingHelper periodicAccrualAccountingHelper = new PeriodicAccrualAccountingHelper(requestSpec,
|
|
||||||
responseSpec);
|
|
||||||
periodicAccrualAccountingHelper.runPeriodicAccrualAccounting("2 January 2024");
|
|
||||||
|
|
||||||
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(createdLoanId.get());
|
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(createdLoanId.get());
|
||||||
assertEquals(BigDecimal.ZERO, loanDetails.getSummary().getTotalUnpaidAccruedDueInterest().stripTrailingZeros());
|
assertEquals(BigDecimal.ZERO, loanDetails.getSummary().getTotalUnpaidPayableDueInterest().stripTrailingZeros());
|
||||||
assertEquals(BigDecimal.ZERO, loanDetails.getSummary().getTotalUnpaidAccruedNotDueInterest().stripTrailingZeros());
|
assertEquals(BigDecimal.valueOf(6.0).stripTrailingZeros(),
|
||||||
|
loanDetails.getSummary().getTotalUnpaidPayableNotDueInterest().stripTrailingZeros());
|
||||||
});
|
});
|
||||||
|
|
||||||
// Add Payment higher than accrued amount
|
// Third day on First Period, then TotalUnpaidPayableDueInterest = 0 and TotalUnpaidPayableNotDueInterest = 9
|
||||||
runAt("4 January 2024", () -> {
|
runAt("4 January 2024", () -> {
|
||||||
addRepaymentForLoan(createdLoanId.get(), 150.0, "4 January 2024");
|
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(createdLoanId.get());
|
||||||
|
assertEquals(BigDecimal.ZERO, loanDetails.getSummary().getTotalUnpaidPayableDueInterest().stripTrailingZeros());
|
||||||
|
assertEquals(BigDecimal.valueOf(9.0).stripTrailingZeros(),
|
||||||
|
loanDetails.getSummary().getTotalUnpaidPayableNotDueInterest().stripTrailingZeros());
|
||||||
|
});
|
||||||
|
|
||||||
|
// Fourth day on First Period, then TotalUnpaidPayableDueInterest = 0 and TotalUnpaidPayableNotDueInterest = 12
|
||||||
|
runAt("5 January 2024", () -> {
|
||||||
|
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(createdLoanId.get());
|
||||||
|
assertEquals(BigDecimal.ZERO, loanDetails.getSummary().getTotalUnpaidPayableDueInterest().stripTrailingZeros());
|
||||||
|
assertEquals(BigDecimal.valueOf(12.0).stripTrailingZeros(),
|
||||||
|
loanDetails.getSummary().getTotalUnpaidPayableNotDueInterest().stripTrailingZeros());
|
||||||
|
});
|
||||||
|
|
||||||
|
// Last day on First Period, then TotalUnpaidPayableDueInterest = 90 and TotalUnpaidPayableNotDueInterest = 0
|
||||||
|
runAt("31 January 2024", () -> {
|
||||||
|
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(createdLoanId.get());
|
||||||
|
assertEquals(BigDecimal.valueOf(90.0).stripTrailingZeros(),
|
||||||
|
loanDetails.getSummary().getTotalUnpaidPayableDueInterest().stripTrailingZeros());
|
||||||
|
assertEquals(BigDecimal.ZERO.stripTrailingZeros(),
|
||||||
|
loanDetails.getSummary().getTotalUnpaidPayableNotDueInterest().stripTrailingZeros());
|
||||||
|
});
|
||||||
|
|
||||||
|
// First day on Second Period, then TotalUnpaidPayableDueInterest = 90 and TotalUnpaidPayableNotDueInterest =
|
||||||
|
// 2.34
|
||||||
|
runAt("1 February 2024", () -> {
|
||||||
|
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(createdLoanId.get());
|
||||||
|
assertEquals(BigDecimal.valueOf(90.0).stripTrailingZeros(),
|
||||||
|
loanDetails.getSummary().getTotalUnpaidPayableDueInterest().stripTrailingZeros());
|
||||||
|
assertEquals(BigDecimal.valueOf(2.34), loanDetails.getSummary().getTotalUnpaidPayableNotDueInterest().stripTrailingZeros());
|
||||||
|
});
|
||||||
|
|
||||||
|
// Second day on Second Period, then TotalUnpaidPayableDueInterest = 90 and TotalUnpaidPayableNotDueInterest =
|
||||||
|
// 4.69
|
||||||
|
runAt("2 February 2024", () -> {
|
||||||
|
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(createdLoanId.get());
|
||||||
|
assertEquals(BigDecimal.valueOf(90.0).stripTrailingZeros(),
|
||||||
|
loanDetails.getSummary().getTotalUnpaidPayableDueInterest().stripTrailingZeros());
|
||||||
|
assertEquals(BigDecimal.valueOf(4.69), loanDetails.getSummary().getTotalUnpaidPayableNotDueInterest().stripTrailingZeros());
|
||||||
|
});
|
||||||
|
|
||||||
|
// Third day on Second Period, then TotalUnpaidPayableDueInterest = 90 and TotalUnpaidPayableNotDueInterest =
|
||||||
|
// 7.03
|
||||||
|
runAt("3 February 2024", () -> {
|
||||||
|
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(createdLoanId.get());
|
||||||
|
assertEquals(BigDecimal.valueOf(90.0).stripTrailingZeros(),
|
||||||
|
loanDetails.getSummary().getTotalUnpaidPayableDueInterest().stripTrailingZeros());
|
||||||
|
assertEquals(BigDecimal.valueOf(7.03), loanDetails.getSummary().getTotalUnpaidPayableNotDueInterest().stripTrailingZeros());
|
||||||
|
});
|
||||||
|
|
||||||
|
// N day on Second Period, then TotalUnpaidPayableDueInterest = 90 and TotalUnpaidPayableNotDueInterest = 21.096
|
||||||
|
runAt("10 February 2024", () -> {
|
||||||
|
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(createdLoanId.get());
|
||||||
|
assertEquals(BigDecimal.valueOf(90.0).stripTrailingZeros(),
|
||||||
|
loanDetails.getSummary().getTotalUnpaidPayableDueInterest().stripTrailingZeros());
|
||||||
|
assertEquals(BigDecimal.valueOf(23.44).stripTrailingZeros(),
|
||||||
|
loanDetails.getSummary().getTotalUnpaidPayableNotDueInterest().stripTrailingZeros());
|
||||||
|
});
|
||||||
|
|
||||||
|
// First day on Third Period, then TotalUnpaidPayableDueInterest = 90 + 70.32 and
|
||||||
|
// TotalUnpaidPayableNotDueInterest = 0
|
||||||
|
runAt("1 March 2024", () -> {
|
||||||
|
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(createdLoanId.get());
|
||||||
|
assertEquals(BigDecimal.valueOf(160.32).stripTrailingZeros(),
|
||||||
|
loanDetails.getSummary().getTotalUnpaidPayableDueInterest().stripTrailingZeros());
|
||||||
|
assertEquals(BigDecimal.ZERO, loanDetails.getSummary().getTotalUnpaidPayableNotDueInterest().stripTrailingZeros());
|
||||||
|
});
|
||||||
|
|
||||||
|
// Second day on Third Period, then TotalUnpaidPayableDueInterest = 90 + 70.32 and
|
||||||
|
// TotalUnpaidPayableNotDueInterest = 1.63
|
||||||
|
runAt("2 March 2024", () -> {
|
||||||
|
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(createdLoanId.get());
|
||||||
|
assertEquals(BigDecimal.valueOf(160.32).stripTrailingZeros(),
|
||||||
|
loanDetails.getSummary().getTotalUnpaidPayableDueInterest().stripTrailingZeros());
|
||||||
|
assertEquals(BigDecimal.valueOf(1.63), loanDetails.getSummary().getTotalUnpaidPayableNotDueInterest().stripTrailingZeros());
|
||||||
|
});
|
||||||
|
|
||||||
|
// Third day on Third Period, then TotalUnpaidPayableDueInterest = 90 + 70.32 and
|
||||||
|
// TotalUnpaidPayableNotDueInterest = 3.26
|
||||||
|
runAt("3 March 2024", () -> {
|
||||||
|
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(createdLoanId.get());
|
||||||
|
assertEquals(BigDecimal.valueOf(160.32).stripTrailingZeros(),
|
||||||
|
loanDetails.getSummary().getTotalUnpaidPayableDueInterest().stripTrailingZeros());
|
||||||
|
assertEquals(BigDecimal.valueOf(3.26), loanDetails.getSummary().getTotalUnpaidPayableNotDueInterest().stripTrailingZeros());
|
||||||
|
});
|
||||||
|
|
||||||
|
// Last day on Third Period, then TotalUnpaidPayableDueInterest = 90 + 70.32 + 48.87 and
|
||||||
|
// TotalUnpaidPayableNotDueInterest = 0
|
||||||
|
runAt("31 March 2024", () -> {
|
||||||
|
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(createdLoanId.get());
|
||||||
|
assertEquals(BigDecimal.valueOf(209.19).stripTrailingZeros(),
|
||||||
|
loanDetails.getSummary().getTotalUnpaidPayableDueInterest().stripTrailingZeros());
|
||||||
|
assertEquals(BigDecimal.ZERO, loanDetails.getSummary().getTotalUnpaidPayableNotDueInterest().stripTrailingZeros());
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add Payment to pay the First installment, then TotalUnpaidPayableDueInterest = 70.32 and
|
||||||
|
// TotalUnpaidPayableNotDueInterest = 16.29
|
||||||
|
runAt("11 March 2024", () -> {
|
||||||
|
addRepaymentForLoan(createdLoanId.get(), 340.00, "11 March 2024");
|
||||||
|
|
||||||
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(createdLoanId.get());
|
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(createdLoanId.get());
|
||||||
assertEquals(BigDecimal.ZERO, loanDetails.getSummary().getTotalUnpaidAccruedDueInterest().stripTrailingZeros());
|
assertEquals(BigDecimal.valueOf(70.32).stripTrailingZeros(),
|
||||||
assertEquals(BigDecimal.ZERO, loanDetails.getSummary().getTotalUnpaidAccruedNotDueInterest().stripTrailingZeros());
|
loanDetails.getSummary().getTotalUnpaidPayableDueInterest().stripTrailingZeros());
|
||||||
|
assertEquals(BigDecimal.valueOf(16.29).stripTrailingZeros(),
|
||||||
|
loanDetails.getSummary().getTotalUnpaidPayableNotDueInterest().stripTrailingZeros());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user