FINERACT-2081: Loan account data additional fields for summary and delinquency
This commit is contained in:
committed by
Adam Saghy
parent
4ac102f4e0
commit
79f32d32a3
+39
@@ -64,6 +64,11 @@ public class LoanDelinquencyDomainServiceImpl implements LoanDelinquencyDomainSe
|
||||
return CollectionData.template();
|
||||
}
|
||||
|
||||
BigDecimal delinquentPrincipal = BigDecimal.ZERO;
|
||||
BigDecimal delinquentInterest = BigDecimal.ZERO;
|
||||
BigDecimal delinquentFee = BigDecimal.ZERO;
|
||||
BigDecimal delinquentPenalty = BigDecimal.ZERO;
|
||||
|
||||
// Get the oldest overdue installment if exists one
|
||||
for (LoanRepaymentScheduleInstallment installment : loan.getRepaymentScheduleInstallments()) {
|
||||
if (!installment.isObligationsMet()) {
|
||||
@@ -71,6 +76,10 @@ public class LoanDelinquencyDomainServiceImpl implements LoanDelinquencyDomainSe
|
||||
log.debug("Loan Id: {} with installment {} due date {}", loan.getId(), installment.getInstallmentNumber(),
|
||||
installment.getDueDate());
|
||||
outstandingAmount = outstandingAmount.add(installment.getTotalOutstanding(loanCurrency).getAmount());
|
||||
delinquentPrincipal = delinquentPrincipal.add(installment.getPrincipalOutstanding(loanCurrency).getAmount());
|
||||
delinquentInterest = delinquentInterest.add(installment.getInterestOutstanding(loanCurrency).getAmount());
|
||||
delinquentFee = delinquentFee.add(installment.getFeeChargesOutstanding(loanCurrency).getAmount());
|
||||
delinquentPenalty = delinquentPenalty.add(installment.getPenaltyChargesOutstanding(loanCurrency).getAmount());
|
||||
if (!oldestOverdueInstallment) {
|
||||
log.debug("Oldest installment {} {}", installment.getInstallmentNumber(), installment.getDueDate());
|
||||
CollectionData overDueInstallmentDelinquentData = calculateDelinquencyDataForOverdueInstallment(loan, installment);
|
||||
@@ -85,6 +94,10 @@ public class LoanDelinquencyDomainServiceImpl implements LoanDelinquencyDomainSe
|
||||
CollectionData nonOverDueInstallmentDelinquentData = calculateDelinquencyDataForNonOverdueInstallment(loan,
|
||||
installment);
|
||||
outstandingAmount = outstandingAmount.add(nonOverDueInstallmentDelinquentData.getDelinquentAmount());
|
||||
delinquentPrincipal = delinquentPrincipal.add(nonOverDueInstallmentDelinquentData.getDelinquentPrincipal());
|
||||
delinquentInterest = delinquentInterest.add(nonOverDueInstallmentDelinquentData.getDelinquentInterest());
|
||||
delinquentFee = delinquentFee.add(nonOverDueInstallmentDelinquentData.getDelinquentFee());
|
||||
delinquentPenalty = delinquentPenalty.add(nonOverDueInstallmentDelinquentData.getDelinquentPenalty());
|
||||
if (!overdueSinceDateWasSet) {
|
||||
overdueSinceDate = nonOverDueInstallmentDelinquentData.getDelinquentDate();
|
||||
overdueSinceDateWasSet = true;
|
||||
@@ -110,6 +123,11 @@ public class LoanDelinquencyDomainServiceImpl implements LoanDelinquencyDomainSe
|
||||
collectionData.setDelinquentDate(overdueSinceDate);
|
||||
}
|
||||
collectionData.setDelinquentAmount(outstandingAmount);
|
||||
collectionData.setDelinquentPrincipal(delinquentPrincipal);
|
||||
collectionData.setDelinquentInterest(delinquentInterest);
|
||||
collectionData.setDelinquentFee(delinquentFee);
|
||||
collectionData.setDelinquentPenalty(delinquentPenalty);
|
||||
|
||||
collectionData.setDelinquentDays(0L);
|
||||
Long delinquentDays = overdueDays - graceDays;
|
||||
if (delinquentDays > 0) {
|
||||
@@ -248,8 +266,17 @@ public class LoanDelinquencyDomainServiceImpl implements LoanDelinquencyDomainSe
|
||||
LocalDate overdueSinceDate = null;
|
||||
CollectionData collectionData = CollectionData.template();
|
||||
BigDecimal outstandingAmount = BigDecimal.ZERO;
|
||||
BigDecimal delinquentPrincipal = BigDecimal.ZERO;
|
||||
BigDecimal delinquentInterest = BigDecimal.ZERO;
|
||||
BigDecimal delinquentFee = BigDecimal.ZERO;
|
||||
BigDecimal delinquentPenalty = BigDecimal.ZERO;
|
||||
|
||||
outstandingAmount = outstandingAmount.add(installment.getTotalOutstanding(loanCurrency).getAmount());
|
||||
delinquentPrincipal = delinquentPrincipal.add(installment.getPrincipalOutstanding(loanCurrency).getAmount());
|
||||
delinquentInterest = delinquentInterest.add(installment.getInterestOutstanding(loanCurrency).getAmount());
|
||||
delinquentFee = delinquentFee.add(installment.getFeeChargesOutstanding(loanCurrency).getAmount());
|
||||
delinquentPenalty = delinquentPenalty.add(installment.getPenaltyChargesOutstanding(loanCurrency).getAmount());
|
||||
|
||||
overdueSinceDate = installment.getDueDate();
|
||||
BigDecimal amountAvailable = installment.getTotalPaid(loanCurrency).getAmount();
|
||||
boolean isLatestInstallment = Objects.equals(installment.getId(), latestInstallment.getId());
|
||||
@@ -272,6 +299,10 @@ public class LoanDelinquencyDomainServiceImpl implements LoanDelinquencyDomainSe
|
||||
}
|
||||
collectionData.setDelinquentDate(overdueSinceDate);
|
||||
collectionData.setDelinquentAmount(outstandingAmount);
|
||||
collectionData.setDelinquentPrincipal(delinquentPrincipal);
|
||||
collectionData.setDelinquentInterest(delinquentInterest);
|
||||
collectionData.setDelinquentFee(delinquentFee);
|
||||
collectionData.setDelinquentPenalty(delinquentPenalty);
|
||||
return collectionData;
|
||||
}
|
||||
|
||||
@@ -283,6 +314,10 @@ public class LoanDelinquencyDomainServiceImpl implements LoanDelinquencyDomainSe
|
||||
LocalDate overdueSinceDate = null;
|
||||
CollectionData collectionData = CollectionData.template();
|
||||
BigDecimal outstandingAmount = BigDecimal.ZERO;
|
||||
BigDecimal delinquentPrincipal = BigDecimal.ZERO;
|
||||
BigDecimal delinquentInterest = BigDecimal.ZERO;
|
||||
BigDecimal delinquentFee = BigDecimal.ZERO;
|
||||
BigDecimal delinquentPenalty = BigDecimal.ZERO;
|
||||
|
||||
List<LoanTransaction> chargebackTransactions = loan.getLoanTransactions(LoanTransaction::isChargeback);
|
||||
BigDecimal amountAvailable = installment.getTotalPaid(loanCurrency).getAmount();
|
||||
@@ -306,6 +341,10 @@ public class LoanDelinquencyDomainServiceImpl implements LoanDelinquencyDomainSe
|
||||
}
|
||||
collectionData.setDelinquentDate(overdueSinceDate);
|
||||
collectionData.setDelinquentAmount(outstandingAmount);
|
||||
collectionData.setDelinquentPrincipal(delinquentPrincipal);
|
||||
collectionData.setDelinquentInterest(delinquentInterest);
|
||||
collectionData.setDelinquentFee(delinquentFee);
|
||||
collectionData.setDelinquentPenalty(delinquentPenalty);
|
||||
return collectionData;
|
||||
}
|
||||
|
||||
|
||||
+6
-1
@@ -47,9 +47,14 @@ public final class CollectionData {
|
||||
public Collection<DelinquencyPausePeriod> delinquencyPausePeriods;
|
||||
public Collection<InstallmentLevelDelinquency> installmentLevelDelinquency;
|
||||
|
||||
private BigDecimal delinquentPrincipal;
|
||||
private BigDecimal delinquentInterest;
|
||||
private BigDecimal delinquentFee;
|
||||
private BigDecimal delinquentPenalty;
|
||||
|
||||
public static CollectionData template() {
|
||||
final BigDecimal zero = BigDecimal.ZERO;
|
||||
return new CollectionData(zero, 0L, null, 0L, null, zero, null, zero, null, zero, null, null);
|
||||
return new CollectionData(zero, 0L, null, 0L, null, zero, null, zero, null, zero, null, null, zero, zero, zero, zero);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+74
-2
@@ -24,8 +24,12 @@ import java.util.Collection;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
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.portfolio.loanaccount.domain.LoanTransactionType;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanScheduleData;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanSchedulePeriodData;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
/**
|
||||
@@ -91,11 +95,15 @@ public class LoanSummaryData {
|
||||
private BigDecimal totalCreditBalanceRefundReversed;
|
||||
private BigDecimal totalRepaymentTransaction;
|
||||
private BigDecimal totalRepaymentTransactionReversed;
|
||||
private BigDecimal totalInterestPaymentWaiver;
|
||||
private final Long chargeOffReasonId;
|
||||
private final String chargeOffReason;
|
||||
|
||||
private BigDecimal totalUnpaidAccruedDueInterest;
|
||||
private BigDecimal totalUnpaidAccruedNotDueInterest;
|
||||
|
||||
public static LoanSummaryData withTransactionAmountsSummary(final LoanSummaryData defaultSummaryData,
|
||||
final Collection<LoanTransactionData> loanTransactions) {
|
||||
final Collection<LoanTransactionData> loanTransactions, final LoanScheduleData repaymentSchedule) {
|
||||
|
||||
BigDecimal totalMerchantRefund = BigDecimal.ZERO;
|
||||
BigDecimal totalMerchantRefundReversed = BigDecimal.ZERO;
|
||||
@@ -110,6 +118,9 @@ public class LoanSummaryData {
|
||||
BigDecimal totalCreditBalanceRefundReversed = BigDecimal.ZERO;
|
||||
BigDecimal totalRepaymentTransaction = BigDecimal.ZERO;
|
||||
BigDecimal totalRepaymentTransactionReversed = BigDecimal.ZERO;
|
||||
BigDecimal totalInterestPaymentWaiver = BigDecimal.ZERO;
|
||||
BigDecimal totalUnpaidAccruedDueInterest = BigDecimal.ZERO;
|
||||
BigDecimal totalUnpaidAccruedNotDueInterest = BigDecimal.ZERO;
|
||||
|
||||
if (!CollectionUtils.isEmpty(loanTransactions)) {
|
||||
|
||||
@@ -131,6 +142,24 @@ public class LoanSummaryData {
|
||||
loanTransactions);
|
||||
totalRepaymentTransaction = computeTotalRepaymentTransactionAmount(loanTransactions);
|
||||
totalRepaymentTransactionReversed = computeTotalAmountForReversedTransactions(LoanTransactionType.REPAYMENT, loanTransactions);
|
||||
totalInterestPaymentWaiver = computeTotalAmountForNonReversedTransactions(LoanTransactionType.INTEREST_PAYMENT_WAIVER,
|
||||
loanTransactions);
|
||||
}
|
||||
|
||||
if (repaymentSchedule != null) {
|
||||
// Accrued Due Interest on Past due installments
|
||||
totalUnpaidAccruedDueInterest = computeTotalAccruedDueInterestAmount(repaymentSchedule.getPeriods());
|
||||
if (MathUtil.isGreaterThanZero(totalUnpaidAccruedDueInterest)) {
|
||||
totalUnpaidAccruedDueInterest = totalUnpaidAccruedDueInterest
|
||||
.subtract(computeTotalInterestPaidDueAmount(repaymentSchedule.getPeriods()));
|
||||
}
|
||||
|
||||
// Accrued Due Interest on Actual Installment
|
||||
totalUnpaidAccruedNotDueInterest = computeTotalAccruedNotDueInterestAmountOnActualPeriod(repaymentSchedule.getPeriods());
|
||||
if (MathUtil.isGreaterThanZero(totalUnpaidAccruedNotDueInterest)) {
|
||||
totalUnpaidAccruedNotDueInterest = totalUnpaidAccruedNotDueInterest
|
||||
.subtract(computeTotalInterestPaidNotDueAmountOnActualPeriod(repaymentSchedule.getPeriods()));
|
||||
}
|
||||
}
|
||||
|
||||
return LoanSummaryData.builder().currency(defaultSummaryData.currency).principalDisbursed(defaultSummaryData.principalDisbursed)
|
||||
@@ -163,7 +192,9 @@ public class LoanSummaryData {
|
||||
.totalChargeAdjustment(totalChargeAdjustment).totalChargeAdjustmentReversed(totalChargeAdjustmentReversed)
|
||||
.totalChargeback(totalChargeback).totalCreditBalanceRefund(totalCreditBalanceRefund)
|
||||
.totalCreditBalanceRefundReversed(totalCreditBalanceRefundReversed).totalRepaymentTransaction(totalRepaymentTransaction)
|
||||
.totalRepaymentTransactionReversed(totalRepaymentTransactionReversed).build();
|
||||
.totalRepaymentTransactionReversed(totalRepaymentTransactionReversed).totalInterestPaymentWaiver(totalInterestPaymentWaiver)
|
||||
.totalUnpaidAccruedDueInterest(totalUnpaidAccruedDueInterest)
|
||||
.totalUnpaidAccruedNotDueInterest(totalUnpaidAccruedNotDueInterest).build();
|
||||
}
|
||||
|
||||
public static LoanSummaryData withOnlyCurrencyData(CurrencyData currencyData) {
|
||||
@@ -191,4 +222,45 @@ public class LoanSummaryData {
|
||||
loanTransactions);
|
||||
return totalRepaymentTransaction.add(totalDownPaymentTransaction);
|
||||
}
|
||||
|
||||
private static BigDecimal computeTotalAccruedDueInterestAmount(Collection<LoanSchedulePeriodData> periods) {
|
||||
final LocalDate businessDate = DateUtils.getBusinessLocalDate();
|
||||
return periods.stream().filter(period -> !period.getDownPaymentPeriod() && businessDate.isAfter(period.getDueDate()))
|
||||
.map(period -> period.getTotalAccruedInterest()).reduce(BigDecimal.ZERO, BigDecimal::add);
|
||||
}
|
||||
|
||||
private static BigDecimal computeTotalInterestPaidDueAmount(Collection<LoanSchedulePeriodData> periods) {
|
||||
final LocalDate businessDate = DateUtils.getBusinessLocalDate();
|
||||
return periods.stream().filter(period -> !period.getDownPaymentPeriod() && businessDate.isAfter(period.getDueDate()))
|
||||
.map(period -> period.getInterestPaid()).reduce(BigDecimal.ZERO, BigDecimal::add);
|
||||
}
|
||||
|
||||
private static BigDecimal computeTotalAccruedNotDueInterestAmountOnActualPeriod(Collection<LoanSchedulePeriodData> periods) {
|
||||
final LocalDate businessDate = DateUtils.getBusinessLocalDate();
|
||||
return periods.stream()
|
||||
.filter(period -> !period.getDownPaymentPeriod() && isActualPeriod(period) && businessDate.isBefore(period.getDueDate()))
|
||||
.map(period -> period.getTotalAccruedInterest()).reduce(BigDecimal.ZERO, BigDecimal::add);
|
||||
}
|
||||
|
||||
private static BigDecimal computeTotalInterestPaidNotDueAmountOnActualPeriod(Collection<LoanSchedulePeriodData> periods) {
|
||||
final LocalDate businessDate = DateUtils.getBusinessLocalDate();
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
+12
-4
@@ -72,6 +72,7 @@ public final class LoanSchedulePeriodData {
|
||||
private final BigDecimal totalActualCostOfLoanForPeriod;
|
||||
private final BigDecimal totalInstallmentAmountForPeriod;
|
||||
private final BigDecimal totalCredits;
|
||||
private final BigDecimal totalAccruedInterest;
|
||||
private final Boolean downPaymentPeriod;
|
||||
|
||||
public static LoanSchedulePeriodData disbursementOnlyPeriod(final LocalDate disbursementDate, final BigDecimal principalDisbursed,
|
||||
@@ -109,7 +110,8 @@ public final class LoanSchedulePeriodData {
|
||||
final BigDecimal totalDueForPeriod, final BigDecimal totalPaid, final BigDecimal totalPaidInAdvanceForPeriod,
|
||||
final BigDecimal totalPaidLateForPeriod, final BigDecimal totalWaived, final BigDecimal totalWrittenOff,
|
||||
final BigDecimal totalOutstanding, final BigDecimal totalActualCostOfLoanForPeriod,
|
||||
final BigDecimal totalInstallmentAmountForPeriod, final BigDecimal totalCredits, final boolean isDownPayment) {
|
||||
final BigDecimal totalInstallmentAmountForPeriod, final BigDecimal totalCredits, final boolean isDownPayment,
|
||||
final BigDecimal totalAccruedInterest) {
|
||||
|
||||
return new LoanSchedulePeriodData(periodNumber, fromDate, dueDate, obligationsMetOnDate, complete, principalOriginalDue,
|
||||
principalPaid, principalWrittenOff, principalOutstanding, outstandingPrincipalBalanceOfLoan,
|
||||
@@ -117,7 +119,7 @@ public final class LoanSchedulePeriodData {
|
||||
feeChargesPaid, feeChargesWaived, feeChargesWrittenOff, feeChargesOutstanding, penaltyChargesDue, penaltyChargesPaid,
|
||||
penaltyChargesWaived, penaltyChargesWrittenOff, penaltyChargesOutstanding, totalDueForPeriod, totalPaid,
|
||||
totalPaidInAdvanceForPeriod, totalPaidLateForPeriod, totalWaived, totalWrittenOff, totalOutstanding,
|
||||
totalActualCostOfLoanForPeriod, totalInstallmentAmountForPeriod, totalCredits, isDownPayment);
|
||||
totalActualCostOfLoanForPeriod, totalInstallmentAmountForPeriod, totalCredits, isDownPayment, totalAccruedInterest);
|
||||
}
|
||||
|
||||
public static LoanSchedulePeriodData withPaidDetail(final LoanSchedulePeriodData loanSchedulePeriodData, final boolean complete,
|
||||
@@ -138,7 +140,8 @@ public final class LoanSchedulePeriodData {
|
||||
loanSchedulePeriodData.totalPaidLateForPeriod, loanSchedulePeriodData.totalWaivedForPeriod,
|
||||
loanSchedulePeriodData.totalWrittenOffForPeriod, loanSchedulePeriodData.totalOutstandingForPeriod,
|
||||
loanSchedulePeriodData.totalActualCostOfLoanForPeriod, loanSchedulePeriodData.totalInstallmentAmountForPeriod,
|
||||
loanSchedulePeriodData.totalCredits, loanSchedulePeriodData.getDownPaymentPeriod());
|
||||
loanSchedulePeriodData.totalCredits, loanSchedulePeriodData.getDownPaymentPeriod(),
|
||||
loanSchedulePeriodData.totalAccruedInterest);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -202,6 +205,7 @@ public final class LoanSchedulePeriodData {
|
||||
this.totalInstallmentAmountForPeriod = null;
|
||||
this.totalOverdue = DateUtils.isBeforeBusinessDate(dueDate) ? this.totalOutstandingForPeriod : null;
|
||||
this.totalCredits = BigDecimal.ZERO;
|
||||
this.totalAccruedInterest = BigDecimal.ZERO;
|
||||
this.downPaymentPeriod = false;
|
||||
}
|
||||
|
||||
@@ -261,6 +265,7 @@ public final class LoanSchedulePeriodData {
|
||||
this.totalInstallmentAmountForPeriod = totalInstallmentAmountForPeriod;
|
||||
this.totalOverdue = DateUtils.isBeforeBusinessDate(dueDate) ? this.totalOutstandingForPeriod : null;
|
||||
this.totalCredits = BigDecimal.ZERO;
|
||||
this.totalAccruedInterest = BigDecimal.ZERO;
|
||||
this.downPaymentPeriod = false;
|
||||
}
|
||||
|
||||
@@ -316,6 +321,7 @@ public final class LoanSchedulePeriodData {
|
||||
this.totalOverdue = DateUtils.isBeforeBusinessDate(dueDate) ? this.totalOutstandingForPeriod : null;
|
||||
this.totalCredits = BigDecimal.ZERO;
|
||||
this.downPaymentPeriod = true;
|
||||
this.totalAccruedInterest = BigDecimal.ZERO;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -334,7 +340,7 @@ public final class LoanSchedulePeriodData {
|
||||
final BigDecimal totalPaid, final BigDecimal totalPaidInAdvanceForPeriod, final BigDecimal totalPaidLateForPeriod,
|
||||
final BigDecimal totalWaived, final BigDecimal totalWrittenOff, final BigDecimal totalOutstanding,
|
||||
final BigDecimal totalActualCostOfLoanForPeriod, final BigDecimal totalInstallmentAmountForPeriod,
|
||||
final BigDecimal totalCredits, final boolean isDownPayment) {
|
||||
final BigDecimal totalCredits, final boolean isDownPayment, final BigDecimal totalAccruedInterest) {
|
||||
this.period = periodNumber;
|
||||
this.fromDate = fromDate;
|
||||
this.dueDate = dueDate;
|
||||
@@ -385,6 +391,7 @@ public final class LoanSchedulePeriodData {
|
||||
this.totalOverdue = DateUtils.isBeforeBusinessDate(dueDate) ? this.totalOutstandingForPeriod : null;
|
||||
this.totalCredits = totalCredits;
|
||||
this.downPaymentPeriod = isDownPayment;
|
||||
this.totalAccruedInterest = totalAccruedInterest;
|
||||
}
|
||||
|
||||
private BigDecimal defaultToZeroIfNull(final BigDecimal possibleNullValue) {
|
||||
@@ -482,4 +489,5 @@ public final class LoanSchedulePeriodData {
|
||||
public BigDecimal totalOutstandingForPeriod() {
|
||||
return defaultToZeroIfNull(this.totalOutstandingForPeriod);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user