Skip to content

Commit

Permalink
Merge pull request #1392 from fiterlatam/feature/FBR-735
Browse files Browse the repository at this point in the history
FBR-735 Adjust accrual job to not accrued after early payment.
  • Loading branch information
Deepika1095 authored Dec 19, 2024
2 parents 57e588a + 926d753 commit c693d3d
Show file tree
Hide file tree
Showing 3 changed files with 43 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.Map;

import lombok.Getter;
import lombok.Setter;
import org.apache.fineract.organisation.monetary.data.CurrencyData;
import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType;

Expand Down Expand Up @@ -51,6 +54,9 @@ public class LoanScheduleAccrualData {
private BigDecimal dueDatePenaltyIncome;
private BigDecimal accruableIncome;

@Setter@Getter
private BigDecimal interestCompletedIncome;

public LoanScheduleAccrualData(final Long loanId, final Long officeId, final Integer installmentNumber, final LocalDate accruedTill,
final PeriodFrequencyType repaymentFrequency, final Integer repayEvery, final LocalDate dueDate, final LocalDate fromDate,
final Long repaymentScheduleId, final Long loanProductId, final BigDecimal interestIncome, final BigDecimal feeIncome,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ public void addPeriodicAccruals(final LocalDate tilldate, Long loanId, Collectio

private void addAccrualTillSpecificDate(final LocalDate tilldate, final LoanScheduleAccrualData accrualData) throws Exception {
LocalDate interestStartDate = accrualData.getFromDateAsLocaldate();

if (accrualData.getInterestCalculatedFrom() != null
&& accrualData.getFromDateAsLocaldate().isBefore(accrualData.getInterestCalculatedFrom())) {
if (accrualData.getInterestCalculatedFrom().isBefore(accrualData.getDueDateAsLocaldate())) {
Expand All @@ -146,26 +147,45 @@ private void addAccrualTillSpecificDate(final LocalDate tilldate, final LoanSche
}

int totalNumberOfDays = Math.toIntExact(ChronoUnit.DAYS.between(interestStartDate, accrualData.getDueDateAsLocaldate()));
LocalDate startDate = accrualData.getFromDateAsLocaldate();
double totalInterest = accrualData.getAccruableIncome().doubleValue();
double paidInterest = accrualData.getInterestCompletedIncome() != null ? accrualData.getInterestCompletedIncome().doubleValue() : 0.0;
double remainingInterest = totalInterest - paidInterest;

if (remainingInterest <= 0) {
return; // No remaining interest to accrue.
}

double interestPerDay = totalInterest / totalNumberOfDays;
int daysPaid = (int) Math.ceil(paidInterest / interestPerDay);

// Adjust start date based on paid days
LocalDate adjustedStartDate = interestStartDate.plusDays(daysPaid);
if (adjustedStartDate.isAfter(tilldate)) {
return; // No accrual needed as paid interest covers till the given date.
}

LocalDate startDate = adjustedStartDate;
if (accrualData.getInterestCalculatedFrom() != null && startDate.isBefore(accrualData.getInterestCalculatedFrom())) {
if (accrualData.getInterestCalculatedFrom().isBefore(tilldate)) {
startDate = accrualData.getInterestCalculatedFrom();
} else {
startDate = tilldate;
}
}

int daysToBeAccrued = Math.toIntExact(ChronoUnit.DAYS.between(startDate, tilldate));
double interestPerDay = accrualData.getAccruableIncome().doubleValue() / totalNumberOfDays;
BigDecimal interestportion;
BigDecimal amount = BigDecimal.ZERO;
BigDecimal interestportion = null;
BigDecimal feeportion = accrualData.getDueDateFeeIncome();
BigDecimal penaltyportion = accrualData.getDueDatePenaltyIncome();
if (daysToBeAccrued >= totalNumberOfDays) {
interestportion = accrualData.getAccruableIncome();

if (daysToBeAccrued >= totalNumberOfDays - daysPaid) {
interestportion = BigDecimal.valueOf(remainingInterest);
} else {
double iterest = interestPerDay * daysToBeAccrued;
interestportion = BigDecimal.valueOf(iterest);
double interest = interestPerDay * daysToBeAccrued;
interestportion = BigDecimal.valueOf(interest);
}

interestportion = interestportion.setScale(accrualData.getCurrencyData().decimalPlaces(), MoneyHelper.getRoundingMode());

BigDecimal totalAccInterest = accrualData.getAccruedInterestIncome();
Expand All @@ -183,6 +203,7 @@ private void addAccrualTillSpecificDate(final LocalDate tilldate, final LoanSche
interestportion = null;
}
}

if (feeportion != null) {
if (totalAccFee == null) {
totalAccFee = BigDecimal.ZERO;
Expand All @@ -206,12 +227,14 @@ private void addAccrualTillSpecificDate(final LocalDate tilldate, final LoanSche
penaltyportion = null;
}
}

if (amount.compareTo(BigDecimal.ZERO) > 0) {
addAccrualAccounting(accrualData, amount, interestportion, totalAccInterest, feeportion, totalAccFee, penaltyportion,
totalAccPenalty, tilldate);
}
}


@Transactional
public void addAccrualAccounting(LoanScheduleAccrualData scheduleAccrualData) throws Exception {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1905,7 +1905,8 @@ public Collection<LoanScheduleAccrualData> retrivePeriodicAccrualData(final Loca
" where (recaldet.is_compounding_to_be_posted_as_transaction is null or recaldet.is_compounding_to_be_posted_as_transaction = false) ")
.append(" and (((ls.fee_charges_amount <> COALESCE(ls.accrual_fee_charges_derived, 0))")
.append(" or (ls.penalty_charges_amount <> COALESCE(ls.accrual_penalty_charges_derived, 0))")
.append(" or (ls.interest_amount <> COALESCE(ls.accrual_interest_derived, 0)))")
.append(" or (ls.interest_amount > COALESCE(ls.interest_completed_derived, 0)")
.append(" and (ls.interest_amount <> COALESCE(ls.accrual_interest_derived, 0))))")
.append(" and loan.loan_status_id=:active and mpl.accounting_type=:type and (loan.closedon_date <= :tillDate or loan.closedon_date is null)")
.append(" and loan.is_npa=false and (ls.duedate <= :tillDate or (ls.duedate > :tillDate and ls.fromdate < :tillDate))) ");
Map<String, Object> paramMap = new HashMap<>(4);
Expand All @@ -1930,7 +1931,7 @@ public String schema() {
.append("loan.interest_calculated_from_date as interestCalculatedFrom, ").append("loan.repay_every as repayEvery,")
.append("ls.installment as installmentNumber, ")
.append("ls.duedate as duedate,ls.fromdate as fromdate ,ls.id as scheduleId,loan.product_id as productId,")
.append("ls.interest_amount as interest, ls.interest_waived_derived as interestWaived,")
.append("ls.interest_amount as interest, ls.interest_completed_derived as interestCompleted, ls.interest_waived_derived as interestWaived,")
.append("ls.penalty_charges_amount as penalty, ").append("ls.fee_charges_amount as charges, ")
.append("ls.accrual_interest_derived as accinterest,ls.accrual_fee_charges_derived as accfeecharege,ls.accrual_penalty_charges_derived as accpenalty,")
.append(" loan.currency_code as currencyCode,loan.currency_digits as currencyDigits,loan.currency_multiplesof as inMultiplesOf,")
Expand Down Expand Up @@ -1960,6 +1961,7 @@ public LoanScheduleAccrualData mapRow(ResultSet rs, @SuppressWarnings("unused")
final Long repaymentScheduleId = rs.getLong("scheduleId");
final Long loanProductId = rs.getLong("productId");
final BigDecimal interestIncome = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "interest");
final BigDecimal interestCompleted = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "interestCompleted");
final BigDecimal feeIncome = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "charges");
final BigDecimal penaltyIncome = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "penalty");
final BigDecimal interestIncomeWaived = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "interestWaived");
Expand All @@ -1977,9 +1979,11 @@ public LoanScheduleAccrualData mapRow(ResultSet rs, @SuppressWarnings("unused")
final CurrencyData currencyData = new CurrencyData(currencyCode, currencyName, currencyDigits, inMultiplesOf,
currencyDisplaySymbol, currencyNameCode, intCode);

return new LoanScheduleAccrualData(loanId, officeId, installmentNumber, accruedTill, frequency, repayEvery, dueDate, fromDate,
LoanScheduleAccrualData loanScheduleAccrualData = new LoanScheduleAccrualData(loanId, officeId, installmentNumber, accruedTill, frequency, repayEvery, dueDate, fromDate,
repaymentScheduleId, loanProductId, interestIncome, feeIncome, penaltyIncome, accruedInterestIncome, accruedFeeIncome,
accruedPenaltyIncome, currencyData, interestCalculatedFrom, interestIncomeWaived);
loanScheduleAccrualData.setInterestCompletedIncome(interestCompleted);
return loanScheduleAccrualData;
}

}
Expand Down

0 comments on commit c693d3d

Please sign in to comment.