Merge pull request #1066 from pramodn02/develop

MIFOSX-1517 : corrected recalculation for tranche loans
This commit is contained in:
Vishwas Babu A J
2014-09-12 14:13:44 -07:00
9 changed files with 229 additions and 94 deletions
@@ -8,7 +8,7 @@ CREATE TABLE `m_loan_transaction_temp` (
INSERT INTO m_loan_transaction_temp(`id`,`loan_id`,`transaction_date`,`amount`) select lt.id, lt.loan_id,lt.transaction_date,if(lt.transaction_type_enum = 1 , IFNULL(lt.amount,0),IFNULL(-lt.principal_portion_derived,0)) from m_loan_transaction lt where lt.is_reversed=0;
UPDATE m_loan_transaction lt SET lt.outstanding_loan_balance_derived = (select sum(ltt.amount) from m_loan_transaction_temp ltt where ltt.id <= lt.id and ltt.transaction_date <= lt.transaction_date and ltt.loan_id = lt.loan_id) where lt.transaction_type_enum != 10 and lt.is_reversed = 0;
UPDATE m_loan_transaction lt SET lt.outstanding_loan_balance_derived = (select sum(ltt.amount) from m_loan_transaction_temp ltt where ((ltt.transaction_date = lt.transaction_date and ltt.id <= lt.id) or ltt.transaction_date < lt.transaction_date) and ltt.loan_id = lt.loan_id) where lt.transaction_type_enum != 10 and lt.is_reversed = 0;
DROP TABLE `m_loan_transaction_temp`;
@@ -354,9 +354,9 @@ public class LoansApiResource {
final RepaymentScheduleRelatedLoanData repaymentScheduleRelatedData = loanBasicDetails.repaymentScheduleRelatedData();
repaymentSchedule = this.loanReadPlatformService.retrieveRepaymentSchedule(loanId, repaymentScheduleRelatedData,
disbursementData);
disbursementData, loanBasicDetails.isInterestRecalculationEnabled());
if (associationParameters.contains("futureSchedule")) {
if (associationParameters.contains("futureSchedule") && loanBasicDetails.isInterestRecalculationEnabled()) {
mandatoryResponseParameters.add("futureSchedule");
this.calculationPlatformService.updateFutureSchedule(repaymentSchedule, loanId);
}
@@ -1817,7 +1817,7 @@ public class Loan extends AbstractPersistable<Long> {
updateSummaryWithTotalFeeChargesDueAtDisbursement(deriveSumTotalOfChargesDueAtDisbursement());
}
if (this.repaymentScheduleDetail().isInterestRecalculationEnabled()
&& fetchRepaymentScheduleInstallment(1).getDueDate().isBefore(LocalDate.now())) {
&& (fetchRepaymentScheduleInstallment(1).getDueDate().isBefore(LocalDate.now()) || isDisbursementMissed())) {
LocalDate recalculateFrom = null;
regenerateRepaymentScheduleWithInterestRecalculation(loanScheduleFactory, currency, calculatedRepaymentsStartingFromDate,
isHolidayEnabled, holidays, workingDays, calendarInstanceForInterestRecalculation, recalculateFrom);
@@ -1960,6 +1960,18 @@ public class Loan extends AbstractPersistable<Long> {
return details;
}
private boolean isDisbursementMissed() {
boolean isDisbursementMissed = false;
for (LoanDisbursementDetails disbursementDetail : this.disbursementDetails) {
if (disbursementDetail.actualDisbursementDate() == null
&& LocalDate.now().isAfter(disbursementDetail.expectedDisbursementDateAsLocalDate())) {
isDisbursementMissed = true;
break;
}
}
return isDisbursementMissed;
}
private BigDecimal getDisbursedAmount() {
BigDecimal principal = BigDecimal.ZERO;
for (LoanDisbursementDetails disbursementDetail : this.disbursementDetails) {
@@ -24,6 +24,7 @@ import org.springframework.data.jpa.domain.AbstractPersistable;
@Table(name = "m_loan_disbursement_detail")
public class LoanDisbursementDetails extends AbstractPersistable<Long> {
@SuppressWarnings("unused")
@ManyToOne
@JoinColumn(name = "loan_id", nullable = false)
private Loan loan;
@@ -77,6 +78,14 @@ public class LoanDisbursementDetails extends AbstractPersistable<Long> {
return this.expectedDisbursementDate;
}
public LocalDate expectedDisbursementDateAsLocalDate() {
LocalDate expectedDisburseDate = null;
if (this.expectedDisbursementDate != null) {
expectedDisburseDate = new LocalDate(this.expectedDisbursementDate);
}
return expectedDisburseDate;
}
public Date actualDisbursementDate() {
return this.actualDisbursementDate;
}
@@ -102,14 +111,10 @@ public class LoanDisbursementDetails extends AbstractPersistable<Long> {
}
public DisbursementData toData() {
LocalDate expectedDisburseDate = null;
LocalDate expectedDisburseDate = expectedDisbursementDateAsLocalDate();
LocalDate actualDisburseDate = null;
if (this.expectedDisbursementDate != null) {
expectedDisburseDate = new LocalDate(this.expectedDisbursementDate);
actualDisburseDate = expectedDisburseDate;
}
if (getDisbursementDate() != null) {
actualDisburseDate = new LocalDate(getDisbursementDate());
if (this.actualDisbursementDate != null) {
actualDisburseDate = new LocalDate(this.actualDisbursementDate);
}
return new DisbursementData(getId(), expectedDisburseDate, actualDisburseDate, this.principal, this.approvedPrincipal);
}
@@ -158,20 +158,6 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
}
final int daysInPeriod = Days.daysBetween(periodStartDate, scheduledDueDate).getDays();
if (loanApplicationTerms.isMultiDisburseLoan()) {
loanApplicationTerms.setFixedEmiAmountForPeriod(scheduledDueDate);
BigDecimal disburseAmt = disbursementForPeriod(loanApplicationTerms, periodStartDate, scheduledDueDate, periods,
BigDecimal.ZERO);
principalDisbursed = principalDisbursed.plus(disburseAmt);
loanApplicationTerms.setPrincipal(loanApplicationTerms.getPrincipal().plus(disburseAmt));
outstandingBalance = outstandingBalance.plus(disburseAmt);
if (loanApplicationTerms.getMaxOutstandingBalance() != null
&& outstandingBalance.isGreaterThan(loanApplicationTerms.getMaxOutstandingBalance())) {
String errorMsg = "Outstanding balance must not exceed the amount: " + loanApplicationTerms.getMaxOutstandingBalance();
throw new MultiDisbursementOutstandingAmoutException(errorMsg, loanApplicationTerms.getMaxOutstandingBalance()
.getAmount(), disburseAmt);
}
}
int daysInPeriodApplicableForInterest = daysInPeriod;
if (periodStartDate.isBefore(idealDisbursementDate)) {
@@ -203,8 +189,37 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
.calculatePortionOfRepaymentPeriodInterestChargingGrace(periodStartDateApplicableForInterest,
scheduledDueDateAsPerFrequency, loanApplicationTerms.getInterestChargedFromLocalDate(),
loanApplicationTerms.getLoanTermPeriodFrequencyType(), loanApplicationTerms.getRepaymentEvery());
BigDecimal interestToBeAdded = BigDecimal.ZERO;
BigDecimal disburseAmt = BigDecimal.ZERO;
if (loanApplicationTerms.isMultiDisburseLoan()) {
loanApplicationTerms.setFixedEmiAmountForPeriod(scheduledDueDate);
final Collection<DisbursementData> disbursementDatas = new ArrayList<>();
disburseAmt = disbursementForPeriod(loanApplicationTerms, periodStartDate, scheduledDueDate, disbursementDatas,
diffAmt != null);
principalDisbursed = principalDisbursed.plus(disburseAmt);
loanApplicationTerms.setPrincipal(loanApplicationTerms.getPrincipal().plus(disburseAmt));
outstandingBalance = outstandingBalance.plus(disburseAmt);
if (loanApplicationTerms.getMaxOutstandingBalance() != null
&& outstandingBalance.isGreaterThan(loanApplicationTerms.getMaxOutstandingBalance())) {
String errorMsg = "Outstanding balance must not exceed the amount: " + loanApplicationTerms.getMaxOutstandingBalance();
throw new MultiDisbursementOutstandingAmoutException(errorMsg, loanApplicationTerms.getMaxOutstandingBalance()
.getAmount(), disburseAmt);
}
for (DisbursementData disbursementData : disbursementDatas) {
Money disbursedAmt = Money.of(currency, disbursementData.amount());
final LoanScheduleModelDisbursementPeriod disbursementPeriod = LoanScheduleModelDisbursementPeriod.disbursement(
disbursementData.disbursementDate(), disbursedAmt, chargesDueAtTimeOfDisbursement);
periods.add(disbursementPeriod);
if (disbursementData.disbursementDate().isAfter(periodStartDateApplicableForInterest)) {
interestToBeAdded = interestToBeAdded.add(calculateInterestForSpecificDays(mc, loanApplicationTerms, periodNumber,
totalOutstandingInterestPaymentDueToGrace, daysInPeriodApplicableInFullInstallment,
interestCalculationGraceOnRepaymentPeriodFraction, disbursedAmt, disbursementData.disbursementDate(),
scheduledDueDate));
}
}
}
Money balanceForcalculation = outstandingBalance;
Money balanceForcalculation = outstandingBalance.minus(disburseAmt);
// reduce principal is the early payment, will processed as per the
// reschedule strategy
if (reducePrincipal.isGreaterThanZero()) {
@@ -249,53 +264,36 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
}
// update cumulative fields for principal & interest
Money interestForThisinstallment = principalInterestForThisPeriod.interest();
if (daysCalcForInstallmentNumber == periodNumber) {
interestForThisinstallment = interestForThisinstallment.zero().plus(
calculateInterestForDays(daysInPeriodApplicableInFullInstallment, interestForThisinstallment.getAmount(),
daysInPeriodApplicableForInterest));
}
Money principalForThisPeriod = principalDisbursed.zero();
Money outstandingForThePeriod = outstandingBalance;
// Exclude principal portion for interest only installments
if (!recalculatedInterestComponent) {
principalForThisPeriod = principalInterestForThisPeriod.principal();
if (daysCalcForInstallmentNumber == periodNumber && loanApplicationTerms.getAmortizationMethod().isEqualInstallment()) {
principalForThisPeriod = principalForThisPeriod.plus(principalInterestForThisPeriod.interest()).minus(
interestForThisinstallment);
}
totalCumulativePrincipal = totalCumulativePrincipal.plus(principalForThisPeriod);
// 6. update outstandingLoanBlance using correct 'principalDue'
outstandingBalance = outstandingBalance.minus(principalForThisPeriod);
}
totalCumulativeInterest = totalCumulativeInterest.plus(interestForThisinstallment);
totalOutstandingInterestPaymentDueToGrace = principalInterestForThisPeriod.interestPaymentDueToGrace();
Money actualOutstandingbalance = outstandingBalance;
if (interestForThisinstallment.isGreaterThanZero()) {
interestForThisinstallment = interestForThisinstallment.plus(interestToBeAdded);
} else if (principalInterestForThisPeriod.interestPaymentDueToGrace().isGreaterThanZero()) {
totalOutstandingInterestPaymentDueToGrace = totalOutstandingInterestPaymentDueToGrace.minus(interestToBeAdded);
}
Money principalForThisPeriod = principalDisbursed.zero();
Money outstandingForThePeriod = outstandingBalance;
Money reducePrincipalForCurrentInstallment = reducePrincipal;
totalCumulativeInterest = totalCumulativeInterest.plus(interestForThisinstallment);
Money feeChargesForInstallment = principalDisbursed.zero();
Money penaltyChargesForInstallment = principalDisbursed.zero();
if (principalForThisPeriod.isGreaterThan(reducePrincipal)) {
principalForThisPeriod = principalForThisPeriod.minus(reducePrincipal);
reducePrincipal = reducePrincipal.zero();
} else {
reducePrincipal = reducePrincipal.minus(principalForThisPeriod);
principalForThisPeriod = principalForThisPeriod.zero();
}
if (periodNumber == 1) {
fixedEmiAmount = principalForThisPeriod.plus(interestForThisinstallment);
}
Money extraPrincipal = reducePrincipal.zero();
Money canBeReducedFromReducePrincipal = reducePrincipal.zero();
// this block is to identify interest based on late/early payment
if (diffAmt != null && !diffAmt.isEmpty()) {
BigDecimal interestDueToLatePayment = BigDecimal.ZERO;
BigDecimal interestReducedDueToEarlyPayment = BigDecimal.ZERO;
boolean isperiodPrincipalReduced = false;
for (RecalculationDetail detail : diffAmt) {
if (!detail.isInterestompound()) {
// will increase the principal portion and reduces
@@ -307,9 +305,8 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
reducePrincipal = reducePrincipal.plus(detail.getAmount());
extraPrincipal = extraPrincipal.plus(detail.getAmount());
int diffDays = Days.daysBetween(detail.getStartDate(), scheduledDueDate).getDays();
if (diffDays > 0 && !isperiodPrincipalReduced) {
reducePrincipal = reducePrincipal.minus(principalForThisPeriod);
isperiodPrincipalReduced = true;
if (diffDays > 0) {
canBeReducedFromReducePrincipal = canBeReducedFromReducePrincipal.plus(detail.getAmount());
}
LocalDate startDate = detail.getStartDate();
@@ -363,16 +360,6 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
}
}
if (reducePrincipal.isLessThanZero()) {
reducePrincipal = reducePrincipal.zero();
} else {
Money actualOutstanding = outstandingBalance.minus(reducePrincipal);
principalForThisPeriod = reducePrincipal.plus(principalForThisPeriod);
if (actualOutstanding.isLessThanZero()) {
principalForThisPeriod = principalForThisPeriod.plus(actualOutstanding);
}
}
if (totalOutstandingInterestPaymentDueToGrace.isGreaterThanZero()) {
totalOutstandingInterestPaymentDueToGrace = totalOutstandingInterestPaymentDueToGrace.plus(interestDueToLatePayment)
.minus(interestReducedDueToEarlyPayment);
@@ -384,7 +371,60 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
}
}
// Exclude principal portion for interest only installments
if (!recalculatedInterestComponent) {
principalForThisPeriod = principalInterestForThisPeriod.principal();
if (/* daysCalcForInstallmentNumber == periodNumber && */loanApplicationTerms.getAmortizationMethod().isEqualInstallment()) {
principalForThisPeriod = principalForThisPeriod.plus(principalInterestForThisPeriod.interest()).minus(
interestForThisinstallment);
principalForThisPeriod = loanApplicationTerms.adjustPrincipalIfLastRepaymentPeriod(principalForThisPeriod,
totalCumulativePrincipal.plus(principalForThisPeriod), periodNumber);
}
totalCumulativePrincipal = totalCumulativePrincipal.plus(principalForThisPeriod);
// 6. update outstandingLoanBlance using correct 'principalDue'
outstandingBalance = outstandingBalance.minus(principalForThisPeriod);
if (outstandingBalance.isLessThanZero()) {
principalForThisPeriod = principalForThisPeriod.plus(outstandingBalance);
outstandingBalance = outstandingBalance.zero();
}
}
Money actualOutstandingbalance = outstandingBalance;
if (diffAmt != null && !diffAmt.isEmpty()) {
reducePrincipal = reducePrincipal.minus(canBeReducedFromReducePrincipal);
Money pricipalAfterCurrentPrincipal = canBeReducedFromReducePrincipal.minus(principalForThisPeriod);
if (pricipalAfterCurrentPrincipal.isGreaterThanZero()) {
reducePrincipal = reducePrincipal.plus(pricipalAfterCurrentPrincipal);
}
if (reducePrincipal.isLessThanZero()) {
reducePrincipal = reducePrincipal.zero();
} else {
Money actualOutstanding = outstandingBalance.minus(reducePrincipal);
principalForThisPeriod = reducePrincipal.plus(principalForThisPeriod);
if (actualOutstanding.isLessThanZero()) {
principalForThisPeriod = principalForThisPeriod.plus(actualOutstanding);
}
}
}
if (principalForThisPeriod.isGreaterThan(reducePrincipalForCurrentInstallment)) {
principalForThisPeriod = principalForThisPeriod.minus(reducePrincipalForCurrentInstallment);
reducePrincipal = reducePrincipal.minus(reducePrincipalForCurrentInstallment);
} else {
reducePrincipal = reducePrincipal.minus(principalForThisPeriod);
principalForThisPeriod = principalForThisPeriod.zero();
}
if (periodNumber == 1) {
fixedEmiAmount = principalForThisPeriod.plus(interestForThisinstallment);
}
actualOutstandingbalance = actualOutstandingbalance.minus(reducePrincipal);
if (actualOutstandingbalance.isLessThanZero()) {
actualOutstandingbalance = actualOutstandingbalance.zero();
}
// 8. sum up real totalInstallmentDue from components
final Money totalInstallmentDue = principalForThisPeriod//
@@ -415,7 +455,7 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
instalmentNumber++;
}
if (principalDisbursed.isNotEqualTo(expectedPrincipalDisburse)) {
if (principalDisbursed.isNotEqualTo(expectedPrincipalDisburse) && diffAmt == null) {
final String errorMsg = "One of the Disbursement date is not falling on Loan Schedule";
throw new MultiDisbursementDisbursementDateException(errorMsg);
}
@@ -870,17 +910,31 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
}
private BigDecimal disbursementForPeriod(final LoanApplicationTerms loanApplicationTerms, LocalDate startDate, LocalDate endDate,
final Collection<LoanScheduleModelPeriod> periods, final BigDecimal chargesDueAtTimeOfDisbursement) {
final Collection<DisbursementData> disbursementDatas, boolean excludePastUndisbursed) {
BigDecimal principal = BigDecimal.ZERO;
MonetaryCurrency currency = loanApplicationTerms.getPrincipal().getCurrency();
for (DisbursementData disbursementData : loanApplicationTerms.getDisbursementDatas()) {
if (disbursementData.isDueForDisbursement(startDate, endDate)) {
final LoanScheduleModelDisbursementPeriod disbursementPeriod = LoanScheduleModelDisbursementPeriod.disbursement(
disbursementData.disbursementDate(), Money.of(currency, disbursementData.amount()), chargesDueAtTimeOfDisbursement);
periods.add(disbursementPeriod);
if (!excludePastUndisbursed
|| (excludePastUndisbursed && (disbursementData.isDisbursed() || !disbursementData.disbursementDate().isBefore(
LocalDate.now())))) {
if (disbursementData.isDueForDisbursement(startDate, endDate)) {
disbursementDatas.add(disbursementData);
principal = principal.add(disbursementData.amount());
}
}
}
return principal;
}
private BigDecimal disbursementAfterPeriod(final LoanApplicationTerms loanApplicationTerms, LocalDate endDate,
LocalDate lastinstallmentDueDate) {
BigDecimal principal = BigDecimal.ZERO;
for (DisbursementData disbursementData : loanApplicationTerms.getDisbursementDatas()) {
if (disbursementData.disbursementDate().isAfter(endDate) && lastinstallmentDueDate.isAfter(disbursementData.disbursementDate())) {
principal = principal.add(disbursementData.amount());
}
}
return principal;
}
@@ -1413,6 +1467,7 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
Money inerestForCurrentInstallment = Money.zero(currency);
Money principalOutstanding = Money.zero(currency);
Integer periodNumber = 0;
LocalDate lastDueDate = loanApplicationTerms.getExpectedDisbursementDate();
for (final LoanRepaymentScheduleInstallment currentInstallment : installments) {
if (currentInstallment.isNotFullyPaidOff()) {
if (!currentInstallment.getDueDate().isAfter(onDate)) {
@@ -1443,6 +1498,9 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
}
}
if (!currentInstallment.isRecalculatedInterestComponent() && currentInstallment.getDueDate().isAfter(lastDueDate)) {
lastDueDate = currentInstallment.getDueDate();
}
}
if (interestChargedFromLocalDate != null && calculateInterestFrom.isBefore(interestChargedFromLocalDate)) {
calculateInterestFrom = interestChargedFromLocalDate;
@@ -1463,6 +1521,8 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
if (useDailyInterest) {
interestForInstallment = loanApplicationTerms.interestRateFor(this.paymentPeriodsInOneYearCalculator, mc,
principalOutstanding, calculateInterestFrom, repaymentDueDate);
interest = interest.plus(inerestForCurrentInstallment.minus(interestForInstallment));
interest = interest.plus(calculateInterestForDays(daysInPeriodApplicableForInterest, interestForInstallment, days));
} else {
LocalDate firstRepaymentdate = this.scheduledDateGenerator.generateNextRepaymentDate(
@@ -1485,13 +1545,58 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
loanApplicationTerms.getInterestChargedFromLocalDate(),
loanApplicationTerms.getLoanTermPeriodFrequencyType(), loanApplicationTerms.getRepaymentEvery());
if (loanApplicationTerms.isMultiDisburseLoan()) {
BigDecimal reducePrincipal = disbursementAfterPeriod(loanApplicationTerms, repaymentDueDate, lastDueDate);
principalOutstanding = principalOutstanding.minus(reducePrincipal);
}
PrincipalInterest principalInterest = loanApplicationTerms.calculateTotalInterestForPeriod(
this.paymentPeriodsInOneYearCalculator, interestCalculationGraceOnRepaymentPeriodFraction, periodNumber, mc,
totalOutstandingInterestPaymentDueToGrace.zero(), daysInPeriodApplicableForInterest, principalOutstanding);
interestForInstallment = principalInterest.interest().getAmount();
Money alteredPrincipal = principalOutstanding;
Money reducePricipal = principalOutstanding.zero();
BigDecimal interestToBeReduced = BigDecimal.ZERO;
BigDecimal interestForExtraPrincipal = BigDecimal.ZERO;
if (loanApplicationTerms.isMultiDisburseLoan()) {
final Collection<DisbursementData> disbursementDatas = new ArrayList<>();
BigDecimal addedPrincipal = disbursementForPeriod(loanApplicationTerms, calculateInterestFrom, repaymentDueDate,
disbursementDatas, true);
alteredPrincipal = alteredPrincipal.minus(addedPrincipal);
for (DisbursementData disbursementData : disbursementDatas) {
Money disbursedAmt = Money.of(currency, disbursementData.amount());
if (disbursementData.disbursementDate().isAfter(calculateInterestFrom)) {
LocalDate calculateTill = disbursementData.disbursementDate();
interestToBeReduced = interestToBeReduced.add(calculateInterestForSpecificDays(mc, loanApplicationTerms,
periodNumber, totalOutstandingInterestPaymentDueToGrace, daysInPeriodApplicableForInterest,
interestCalculationGraceOnRepaymentPeriodFraction, disbursedAmt, calculateInterestFrom, calculateTill));
if (onDate.isAfter(calculateTill)) {
interestForExtraPrincipal = interestForExtraPrincipal.add(calculateInterestForSpecificDays(mc,
loanApplicationTerms, periodNumber, totalOutstandingInterestPaymentDueToGrace,
daysInPeriodApplicableForInterest, interestCalculationGraceOnRepaymentPeriodFraction, disbursedAmt,
calculateTill, onDate));
} else {
reducePricipal = reducePricipal.plus(disbursedAmt);
}
}
}
interest = interest.plus(inerestForCurrentInstallment.minus(interestForInstallment).plus(interestToBeReduced));
BigDecimal interestForCloseDate = BigDecimal.ZERO;
if (alteredPrincipal.isNotEqualTo(principalOutstanding)) {
interestForCloseDate = interestForExtraPrincipal;
interestForCloseDate = interestForCloseDate.add(calculateInterestForSpecificDays(mc, loanApplicationTerms,
periodNumber, totalOutstandingInterestPaymentDueToGrace, daysInPeriodApplicableForInterest,
interestCalculationGraceOnRepaymentPeriodFraction, alteredPrincipal, calculateInterestFrom, onDate));
} else {
interestForCloseDate = interestForCloseDate.add(BigDecimal.valueOf(calculateInterestForDays(
daysInPeriodApplicableForInterest, interestForInstallment, days)));
}
interest = interest.plus(interestForCloseDate);
principalOutstanding = principalOutstanding.minus(reducePricipal);
}
}
interest = interest.plus(inerestForCurrentInstallment.minus(interestForInstallment));
interest = interest.plus(calculateInterestForDays(daysInPeriodApplicableForInterest, interestForInstallment, days));
}
@@ -397,7 +397,7 @@ public class LoanScheduleAssembler {
final WorkingDays workingDays = this.workingDaysRepository.findOne();
CalendarInstance calendarInstance = loanApplicationTerms.getRestCalendarInstance();
LocalDate nextScheduleDate = CalendarUtils.getNextScheduleDate(calendarInstance.getCalendar(), onDate);
LocalDate nextScheduleDate = CalendarUtils.getNextScheduleDate(calendarInstance.getCalendar(), onDate.minusDays(1));
if (loanApplicationTerms.getRecalculationFrequencyType().isSameAsRepayment()) {
nextScheduleDate = scheduledDateGenerator.adjustRepaymentDate(nextScheduleDate, loanApplicationTerms, isHolidayEnabled,
holidays, workingDays);
@@ -26,7 +26,7 @@ public interface LoanReadPlatformService {
LoanAccountData retrieveOne(Long loanId);
LoanScheduleData retrieveRepaymentSchedule(Long loanId, RepaymentScheduleRelatedLoanData repaymentScheduleRelatedData,
Collection<DisbursementData> disbursementData);
Collection<DisbursementData> disbursementData, boolean isInterestRecalculationEnabled);
Collection<LoanTransactionData> retrieveLoanTransactions(Long loanId);
@@ -177,13 +177,14 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
@Override
public LoanScheduleData retrieveRepaymentSchedule(final Long loanId,
final RepaymentScheduleRelatedLoanData repaymentScheduleRelatedLoanData, Collection<DisbursementData> disbursementData) {
final RepaymentScheduleRelatedLoanData repaymentScheduleRelatedLoanData, Collection<DisbursementData> disbursementData,
boolean isInterestRecalculationEnabled) {
try {
this.context.authenticatedUser();
final LoanScheduleResultSetExtractor fullResultsetExtractor = new LoanScheduleResultSetExtractor(
repaymentScheduleRelatedLoanData, disbursementData);
repaymentScheduleRelatedLoanData, disbursementData, isInterestRecalculationEnabled);
final String sql = "select " + fullResultsetExtractor.schema() + " where ls.loan_id = ? order by ls.loan_id, ls.installment";
return this.jdbcTemplate.query(sql, fullResultsetExtractor, new Object[] { loanId });
@@ -862,15 +863,17 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
private final Collection<DisbursementData> disbursementData;
private LocalDate lastDueDate;
private BigDecimal outstandingLoanPrincipalBalance;
private boolean excludePastUndisbursed;
public LoanScheduleResultSetExtractor(final RepaymentScheduleRelatedLoanData repaymentScheduleRelatedLoanData,
Collection<DisbursementData> disbursementData) {
Collection<DisbursementData> disbursementData, boolean isInterestRecalculationEnabled) {
this.currency = repaymentScheduleRelatedLoanData.getCurrency();
this.disbursement = repaymentScheduleRelatedLoanData.disbursementData();
this.totalFeeChargesDueAtDisbursement = repaymentScheduleRelatedLoanData.getTotalFeeChargesAtDisbursement();
this.lastDueDate = this.disbursement.disbursementDate();
this.outstandingLoanPrincipalBalance = this.disbursement.amount();
this.disbursementData = disbursementData;
this.excludePastUndisbursed = isInterestRecalculationEnabled;
}
public String schema() {
@@ -899,6 +902,9 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
periods.add(disbursementPeriod);
totalPrincipalDisbursed = Money.of(monCurrency, this.disbursement.amount()).getAmount();
} else {
if (!this.disbursement.isDisbursed()) {
excludePastUndisbursed = false;
}
this.outstandingLoanPrincipalBalance = BigDecimal.ZERO;
}
@@ -930,7 +936,6 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
final LocalDate dueDate = JdbcSupport.getLocalDate(rs, "dueDate");
final LocalDate obligationsMetOnDate = JdbcSupport.getLocalDate(rs, "obligationsMetOnDate");
final boolean complete = rs.getBoolean("complete");
if (disbursementData != null) {
BigDecimal principal = BigDecimal.ZERO;
for (DisbursementData data : disbursementData) {
@@ -939,14 +944,19 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
final LoanSchedulePeriodData periodData = LoanSchedulePeriodData.disbursementOnlyPeriod(
data.disbursementDate(), data.amount(), this.totalFeeChargesDueAtDisbursement, data.isDisbursed());
periods.add(periodData);
} else if (data.isDueForDisbursement(fromDate, dueDate)) {
principal = principal.add(data.amount());
final LoanSchedulePeriodData periodData = LoanSchedulePeriodData.disbursementOnlyPeriod(
data.disbursementDate(), data.amount(), BigDecimal.ZERO, data.isDisbursed());
periods.add(periodData);
this.outstandingLoanPrincipalBalance = this.outstandingLoanPrincipalBalance.add(data.amount());
} else if (data.isDueForDisbursement(fromDate, dueDate)
&& this.outstandingLoanPrincipalBalance.compareTo(BigDecimal.ZERO) == 1) {
if (!excludePastUndisbursed
|| (excludePastUndisbursed && (data.isDisbursed() || !data.disbursementDate().isBefore(LocalDate.now())))) {
principal = principal.add(data.amount());
final LoanSchedulePeriodData periodData = LoanSchedulePeriodData.disbursementOnlyPeriod(
data.disbursementDate(), data.amount(), BigDecimal.ZERO, data.isDisbursed());
periods.add(periodData);
this.outstandingLoanPrincipalBalance = this.outstandingLoanPrincipalBalance.add(data.amount());
}
}
}
this.outstandingLoanPrincipalBalance = this.outstandingLoanPrincipalBalance.add(principal);
totalPrincipalDisbursed = totalPrincipalDisbursed.add(principal);
}
@@ -1588,15 +1598,18 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
StringBuilder sqlBuilder = new StringBuilder();
sqlBuilder.append("SELECT ml.id FROM m_loan ml ");
sqlBuilder.append(" INNER JOIN m_loan_repayment_schedule mr on mr.loan_id = ml.id ");
sqlBuilder.append(" LEFT JOIN m_loan_disbursement_detail dd on dd.loan_id=ml.id and dd.disbursedon_date is null ");
sqlBuilder.append(" WHERE ml.loan_status_id = ? ");
sqlBuilder.append(" and ml.interest_recalculation_enabled = 1 ");
sqlBuilder.append(" and ml.is_npa = 0 ");
sqlBuilder.append(" and mr.completed_derived is false ");
sqlBuilder.append(" and mr.duedate < ? ");
sqlBuilder.append(" and ((");
sqlBuilder.append(" mr.completed_derived is false ");
sqlBuilder.append(" and mr.duedate < ? )");
sqlBuilder.append(" or dd.expected_disburse_date < ? ) ");
sqlBuilder.append(" group by ml.id");
try {
return this.jdbcTemplate.queryForList(sqlBuilder.toString(), Long.class,
new Object[] { LoanStatus.ACTIVE.getValue(), formatter.print(LocalDate.now()) });
new Object[] { LoanStatus.ACTIVE.getValue(), formatter.print(LocalDate.now()),formatter.print(LocalDate.now()) });
} catch (final EmptyResultDataAccessException e) {
return null;
}
@@ -46,6 +46,6 @@ public enum AmortizationMethod {
}
public boolean isEqualInstallment() {
return this.value.equals(AmortizationMethod.EQUAL_INSTALLMENTS);
return this.value.equals(AmortizationMethod.EQUAL_INSTALLMENTS.getValue());
}
}