FINERACT-2081: Loan account summary with Unpaid Payable Interest

This commit is contained in:
Jose Alberto Hernandez
2024-08-01 23:12:38 +02:00
committed by Adam Saghy
parent 526fb6b3a8
commit 06edb4c7d9
5 changed files with 193 additions and 94 deletions
@@ -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"
@@ -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();
} }
} }
@@ -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);
} }
@@ -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;
@@ -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());
}); });
} }