Skip to content

Commit

Permalink
FINERACT-2081: Loan account data additional fields for summary and de…
Browse files Browse the repository at this point in the history
…linquency
  • Loading branch information
Jose Alberto Hernandez committed Jul 10, 2024
1 parent abd61ed commit b306fd2
Show file tree
Hide file tree
Showing 8 changed files with 284 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,12 @@
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;

/**
Expand Down Expand Up @@ -100,7 +104,7 @@ public class LoanSummaryData {
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;
Expand All @@ -117,6 +121,8 @@ public static LoanSummaryData withTransactionAmountsSummary(final LoanSummaryDat
BigDecimal totalRepaymentTransactionReversed = BigDecimal.ZERO;
BigDecimal totalInterestRefund = BigDecimal.ZERO;
BigDecimal totalInterestPaymentWaiver = BigDecimal.ZERO;
BigDecimal totalUnpaidAccruedDueInterest = BigDecimal.ZERO;
BigDecimal totalUnpaidAccruedNotDueInterest = BigDecimal.ZERO;

if (!CollectionUtils.isEmpty(loanTransactions)) {

Expand All @@ -142,6 +148,20 @@ public static LoanSummaryData withTransactionAmountsSummary(final LoanSummaryDat
loanTransactions);
}

// 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)
.principalAdjustments(defaultSummaryData.principalAdjustments).principalPaid(defaultSummaryData.principalPaid)
.principalWrittenOff(defaultSummaryData.principalWrittenOff).principalOutstanding(defaultSummaryData.principalOutstanding)
Expand Down Expand Up @@ -173,7 +193,8 @@ public static LoanSummaryData withTransactionAmountsSummary(final LoanSummaryDat
.totalChargeback(totalChargeback).totalCreditBalanceRefund(totalCreditBalanceRefund)
.totalCreditBalanceRefundReversed(totalCreditBalanceRefundReversed).totalRepaymentTransaction(totalRepaymentTransaction)
.totalRepaymentTransactionReversed(totalRepaymentTransactionReversed).totalInterestRefund(totalInterestRefund)
.totalInterestPaymentWaiver(totalInterestPaymentWaiver).build();
.totalInterestPaymentWaiver(totalInterestPaymentWaiver).totalUnpaidAccruedDueInterest(totalUnpaidAccruedDueInterest)
.totalUnpaidAccruedNotDueInterest(totalUnpaidAccruedNotDueInterest).build();
}

public static LoanSummaryData withOnlyCurrencyData(CurrencyData currencyData) {
Expand Down Expand Up @@ -201,4 +222,45 @@ private static BigDecimal computeTotalRepaymentTransactionAmount(Collection<Loan
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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -109,15 +110,16 @@ public static LoanSchedulePeriodData periodWithPayments(@SuppressWarnings("unuse
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,
interestDueOnPrincipalOutstanding, interestPaid, interestWaived, interestWrittenOff, interestOutstanding, feeChargesDue,
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,
Expand All @@ -138,7 +140,8 @@ public static LoanSchedulePeriodData withPaidDetail(final LoanSchedulePeriodData
loanSchedulePeriodData.totalPaidLateForPeriod, loanSchedulePeriodData.totalWaivedForPeriod,
loanSchedulePeriodData.totalWrittenOffForPeriod, loanSchedulePeriodData.totalOutstandingForPeriod,
loanSchedulePeriodData.totalActualCostOfLoanForPeriod, loanSchedulePeriodData.totalInstallmentAmountForPeriod,
loanSchedulePeriodData.totalCredits, loanSchedulePeriodData.getDownPaymentPeriod());
loanSchedulePeriodData.totalCredits, loanSchedulePeriodData.getDownPaymentPeriod(),
loanSchedulePeriodData.totalAccruedInterest);
}

/*
Expand Down Expand Up @@ -202,6 +205,7 @@ private LoanSchedulePeriodData(final Integer periodNumber, final LocalDate fromD
this.totalInstallmentAmountForPeriod = null;
this.totalOverdue = DateUtils.isBeforeBusinessDate(dueDate) ? this.totalOutstandingForPeriod : null;
this.totalCredits = BigDecimal.ZERO;
this.totalAccruedInterest = BigDecimal.ZERO;
this.downPaymentPeriod = false;
}

Expand Down Expand Up @@ -261,6 +265,7 @@ private LoanSchedulePeriodData(final Integer periodNumber, final LocalDate fromD
this.totalInstallmentAmountForPeriod = totalInstallmentAmountForPeriod;
this.totalOverdue = DateUtils.isBeforeBusinessDate(dueDate) ? this.totalOutstandingForPeriod : null;
this.totalCredits = BigDecimal.ZERO;
this.totalAccruedInterest = BigDecimal.ZERO;
this.downPaymentPeriod = false;
}

Expand Down Expand Up @@ -316,6 +321,7 @@ private LoanSchedulePeriodData(Integer periodNumber, LocalDate fromDate, LocalDa
this.totalOverdue = DateUtils.isBeforeBusinessDate(dueDate) ? this.totalOutstandingForPeriod : null;
this.totalCredits = BigDecimal.ZERO;
this.downPaymentPeriod = true;
this.totalAccruedInterest = BigDecimal.ZERO;
}

/*
Expand All @@ -334,7 +340,7 @@ private LoanSchedulePeriodData(final Integer periodNumber, final LocalDate fromD
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;
Expand Down Expand Up @@ -385,6 +391,7 @@ private LoanSchedulePeriodData(final Integer periodNumber, final LocalDate fromD
this.totalOverdue = DateUtils.isBeforeBusinessDate(dueDate) ? this.totalOutstandingForPeriod : null;
this.totalCredits = totalCredits;
this.downPaymentPeriod = isDownPayment;
this.totalAccruedInterest = totalAccruedInterest;
}

private BigDecimal defaultToZeroIfNull(final BigDecimal possibleNullValue) {
Expand Down Expand Up @@ -482,4 +489,5 @@ public BigDecimal getTotalOverdue() {
public BigDecimal totalOutstandingForPeriod() {
return defaultToZeroIfNull(this.totalOutstandingForPeriod);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,8 @@ public <T> ByteBufferSerializable toAvroDTO(BusinessEvent<T> rawEvent) {

if (data.getSummary() != null) {
final Collection<LoanTransactionData> currentLoanTransactions = service.retrieveLoanTransactions(loanId);
data.setSummary(LoanSummaryData.withTransactionAmountsSummary(data.getSummary(), currentLoanTransactions));
data.setSummary(
LoanSummaryData.withTransactionAmountsSummary(data.getSummary(), currentLoanTransactions, data.getRepaymentSchedule()));
} else {
data.setSummary(LoanSummaryData.withOnlyCurrencyData(data.getCurrency()));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1114,8 +1114,8 @@ private String retrieveLoan(final Long loanId, final String loanExternalIdStr, b

// updating summary with transaction amounts summary
if (loanBasicDetails.getSummary() != null) {
loanBasicDetails
.setSummary(LoanSummaryData.withTransactionAmountsSummary(loanBasicDetails.getSummary(), currentLoanRepayments));
loanBasicDetails.setSummary(
LoanSummaryData.withTransactionAmountsSummary(loanBasicDetails.getSummary(), currentLoanRepayments, repaymentSchedule));
}

final LoanAccountData loanAccount = LoanAccountData.associationsAndTemplate(loanBasicDetails, repaymentSchedule, loanRepayments,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -641,6 +641,10 @@ private GetLoansLoanIdFeeFrequency() {}
public Double totalInterestPaymentWaiver;
@Schema(example = "0.000000")
public Double totalRepaymentTransactionReversed;
@Schema(example = "0.000000")
public Double totalUnpaidAccruedDueInterest;
@Schema(example = "0.000000")
public Double totalUnpaidAccruedNotDueInterest;
public Set<GetLoansLoanIdOverdueCharges> overdueCharges;
@Schema(example = "1")
public Long chargeOffReasonId;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1160,8 +1160,8 @@ public String schema() {
+ " ls.fee_charges_amount as feeChargesDue, ls.fee_charges_completed_derived as feeChargesPaid, ls.fee_charges_waived_derived as feeChargesWaived, ls.fee_charges_writtenoff_derived as feeChargesWrittenOff, "
+ " ls.penalty_charges_amount as penaltyChargesDue, ls.penalty_charges_completed_derived as penaltyChargesPaid, ls.penalty_charges_waived_derived as penaltyChargesWaived, "
+ " ls.penalty_charges_writtenoff_derived as penaltyChargesWrittenOff, ls.total_paid_in_advance_derived as totalPaidInAdvanceForPeriod, "
+ " ls.total_paid_late_derived as totalPaidLateForPeriod, ls.credits_amount as principalCredits, ls.credited_fee as feeCredits, ls.credited_penalty as penaltyCredits, ls.is_down_payment isDownPayment "
+ " from m_loan_repayment_schedule ls ";
+ " ls.total_paid_late_derived as totalPaidLateForPeriod, ls.credits_amount as principalCredits, ls.credited_fee as feeCredits, ls.credited_penalty as penaltyCredits, ls.is_down_payment isDownPayment, "
+ " ls.accrual_interest_derived as accrualInterest " + " from m_loan_repayment_schedule ls ";
}

@Override
Expand Down Expand Up @@ -1260,6 +1260,7 @@ public LoanScheduleData extractData(@NotNull final ResultSet rs) throws SQLExcep
final BigDecimal interestWrittenOff = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "interestWrittenOff");
final BigDecimal totalInstallmentAmount = totalPrincipalPaid.zero().plus(principalDue).plus(interestExpectedDue)
.getAmount();
final BigDecimal accrualInterest = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "accrualInterest");

final BigDecimal interestActualDue = interestExpectedDue.subtract(interestWaived).subtract(interestWrittenOff);
final BigDecimal interestOutstanding = interestActualDue.subtract(interestPaid);
Expand Down Expand Up @@ -1331,7 +1332,7 @@ public LoanScheduleData extractData(@NotNull final ResultSet rs) throws SQLExcep
penaltyChargesPaid, penaltyChargesWaived, penaltyChargesWrittenOff, penaltyChargesOutstanding, totalDueForPeriod,
totalPaidForPeriod, totalPaidInAdvanceForPeriod, totalPaidLateForPeriod, totalWaivedForPeriod,
totalWrittenOffForPeriod, totalOutstandingForPeriod, totalActualCostOfLoanForPeriod, totalInstallmentAmount,
credits, isDownPayment);
credits, isDownPayment, accrualInterest);

periods.add(periodData);
}
Expand Down Expand Up @@ -2313,14 +2314,15 @@ public LoanSchedulePeriodData mapRow(ResultSet rs, @SuppressWarnings("unused") i
final BigDecimal totalPaid = null;
final BigDecimal totalInstallmentAmount = null;
final BigDecimal totalCredits = null;
final BigDecimal totalAccruedInterest = null;

return LoanSchedulePeriodData.periodWithPayments(loanId, period, fromDate, dueDate, obligationsMetOnDate, complete,
principalOriginalDue, principalPaid, principalWrittenOff, principalOutstanding, outstandingPrincipalBalanceOfLoan,
interestDueOnPrincipalOutstanding, interestPaid, interestWaived, interestWrittenOff, interestOutstanding, feeChargesDue,
feeChargesPaid, feeChargesWaived, feeChargesWrittenOff, feeChargesOutstanding, penaltyChargesDue, penaltyChargesPaid,
penaltyChargesWaived, penaltyChargesWrittenOff, penaltyChargesOutstanding, totalDueForPeriod, totalPaid,
totalPaidInAdvanceForPeriod, totalPaidLateForPeriod, totalWaived, totalWrittenOff, totalOutstanding,
totalActualCostOfLoanForPeriod, totalInstallmentAmount, totalCredits, false);
totalActualCostOfLoanForPeriod, totalInstallmentAmount, totalCredits, false, totalAccruedInterest);
}
}

Expand Down
Loading

0 comments on commit b306fd2

Please sign in to comment.