Merge pull request #1419 from pramodn02/develop

MIFOSX-2006 : fixed emi amount for tranche loan
This commit is contained in:
Vishwas Babu A J
2015-06-16 10:44:11 -07:00
3 changed files with 86 additions and 24 deletions
@@ -113,6 +113,10 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
int instalmentNumber = 1;
Money outstandingBalance = principalDisbursed;
// Set fixed EMI Amount
updateFixedInstallmentAmount(mc, loanApplicationTerms, actualRepaymentDate, periodNumber, outstandingBalance);
// disbursement map for tranche details(will added to outstanding
// balance as per the start date)
final Map<LocalDate, Money> disburseDetailMap = new HashMap<>();
@@ -159,10 +163,10 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
final Map<LocalDate, TreeMap<LocalDate, Money>> compoundingDateVariations = new HashMap<>();
boolean isNextRepaymentAvailable = true;
Boolean extendTermForDailyRepayments = false;
if(holidayDetailDTO.getWorkingDays().getExtendTermForDailyRepayments() == true &&
loanApplicationTerms.getRepaymentPeriodFrequencyType() == PeriodFrequencyType.DAYS &&
loanApplicationTerms.getRepaymentEvery() == 1){
if (holidayDetailDTO.getWorkingDays().getExtendTermForDailyRepayments() == true
&& loanApplicationTerms.getRepaymentPeriodFrequencyType() == PeriodFrequencyType.DAYS
&& loanApplicationTerms.getRepaymentEvery() == 1) {
holidayDetailDTO.getWorkingDays().setRepaymentReschedulingType(RepaymentRescheduleType.MOVE_TO_NEXT_WORKING_DAY.getValue());
extendTermForDailyRepayments = true;
}
@@ -170,7 +174,7 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
actualRepaymentDate = this.scheduledDateGenerator.generateNextRepaymentDate(actualRepaymentDate, loanApplicationTerms,
isFirstRepayment);
isFirstRepayment = false;
isFirstRepayment = false;
LocalDate scheduledDueDate = this.scheduledDateGenerator.adjustRepaymentDate(actualRepaymentDate, loanApplicationTerms,
holidayDetailDTO);
@@ -179,8 +183,8 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
compoundingMap, loanCharges, currency);
compoundingDateVariations.put(periodStartDate, new TreeMap<>(compoundingMap));
}
if(extendTermForDailyRepayments){
if (extendTermForDailyRepayments) {
actualRepaymentDate = scheduledDueDate;
}
@@ -367,7 +371,8 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
// method applies early payment strategy
reducePrincipal = reducePrincipal.plus(unprocessed);
reducePrincipal = applyEarlyPaymentStrategy(loanApplicationTerms, reducePrincipal);
reducePrincipal = applyEarlyPaymentStrategy(loanApplicationTerms, reducePrincipal,
totalCumulativePrincipal, periodNumber, mc);
}
// identify late payments and add compounding
// details to
@@ -501,10 +506,6 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
totalOutstandingInterestPaymentDueToGrace = principalInterestForThisPeriod.interestPaymentDueToGrace();
Money principalForThisPeriod = principalInterestForThisPeriod.principal();
if (principalForThisPeriod.isZero()) {
loanApplicationTerms.resetFixedEmiAmount();
}
// applies early payments on principal portion
if (principalForThisPeriod.isGreaterThan(reducePrincipal)) {
principalForThisPeriod = principalForThisPeriod.minus(reducePrincipal);
@@ -518,9 +519,15 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
// reducePrincipal.plus(unprocessed);
Money reducedBalance = earlyPaidAmount;
earlyPaidAmount = earlyPaidAmount.minus(principalForThisPeriod);
boolean isEmiAmountChanged = false;
if (earlyPaidAmount.isGreaterThanZero()) {
reducePrincipal = reducePrincipal.plus(earlyPaidAmount);
reducePrincipal = applyEarlyPaymentStrategy(loanApplicationTerms, reducePrincipal);
BigDecimal fixedEmiAmount = loanApplicationTerms.getFixedEmiAmount();
reducePrincipal = applyEarlyPaymentStrategy(loanApplicationTerms, reducePrincipal, totalCumulativePrincipal,
periodNumber + 1, mc);
if (fixedEmiAmount.compareTo(loanApplicationTerms.getFixedEmiAmount()) != 0) {
isEmiAmountChanged = true;
}
principalForThisPeriod = principalForThisPeriod.plus(earlyPaidAmount);
}
@@ -587,6 +594,11 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
compoundingMap.clear();
compoundingMap.putAll(compoundingDateVariations.get(periodStartDateApplicableForInterest));
}
if (isEmiAmountChanged) {
updateFixedInstallmentAmount(mc, loanApplicationTerms,
loanApplicationTerms.getExpectedDisbursementDate(), periodNumber, loanApplicationTerms
.getPrincipal().minus(totalCumulativePrincipal));
}
PrincipalInterest interestTillDate = calculatePrincipalInterestComponentsForPeriod(
this.paymentPeriodsInOneYearCalculator, interestCalculationGraceOnRepaymentPeriodFraction,
totalCumulativePrincipal, totalCumulativeInterest, totalInterestChargedForFullLoanTerm,
@@ -626,9 +638,15 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
totalCumulativePrincipal = totalCumulativePrincipal.plus(unprocessed.plus(addToPrinciapal));
reducePrincipal = reducePrincipal.plus(unprocessed);
reducePrincipal = applyEarlyPaymentStrategy(loanApplicationTerms, reducePrincipal);
principalForThisPeriod = principalForThisPeriod.plus(unprocessed.plus(addToPrinciapal));
principalProcessed = principalProcessed.plus(unprocessed.plus(addToPrinciapal));
BigDecimal fixedEmiAmount = loanApplicationTerms.getFixedEmiAmount();
reducePrincipal = applyEarlyPaymentStrategy(loanApplicationTerms, reducePrincipal,
totalCumulativePrincipal.plus(principalForThisPeriod.minus(principalProcessed)), periodNumber + 1, mc);
if (fixedEmiAmount.compareTo(loanApplicationTerms.getFixedEmiAmount()) != 0) {
isEmiAmountChanged = true;
}
}
}
}
@@ -717,6 +735,24 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
totalPenaltyChargesCharged, totalRepaymentExpected, totalOutstanding);
}
private boolean updateFixedInstallmentAmount(final MathContext mc, final LoanApplicationTerms loanApplicationTerms,
LocalDate actualRepaymentDate, int periodNumber, Money outstandingBalance) {
boolean isAmountChanged = false;
if (loanApplicationTerms.getActualFixedEmiAmount() == null && loanApplicationTerms.getInterestMethod().isDecliningBalnce()
&& loanApplicationTerms.getAmortizationMethod().isEqualInstallment()) {
LocalDate nextPeriodDate = this.scheduledDateGenerator.generateNextRepaymentDate(actualRepaymentDate, loanApplicationTerms,
false);
if (periodNumber < loanApplicationTerms.getPrincipalGrace() + 1) {
periodNumber = loanApplicationTerms.getPrincipalGrace() + 1;
}
Money emiAmount = loanApplicationTerms.pmtForInstallment(this.paymentPeriodsInOneYearCalculator,
Days.daysBetween(actualRepaymentDate, nextPeriodDate).getDays(), outstandingBalance, periodNumber, mc);
loanApplicationTerms.setFixedEmiAmount(emiAmount.getAmount());
isAmountChanged = true;
}
return isAmountChanged;
}
private Money fetchCompoundedArrears(final LoanApplicationTerms loanApplicationTerms, final MonetaryCurrency currency,
final LoanTransaction transaction) {
Money arrears = transaction.getPrincipalPortion(currency);
@@ -887,15 +923,25 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
/**
* method applies early payment strategy as per the configurations provided
*
* @param totalCumulativePrincipal
* TODO
* @param periodNumber
* TODO
* @param mc
* TODO
*/
private Money applyEarlyPaymentStrategy(final LoanApplicationTerms loanApplicationTerms, Money reducePrincipal) {
private Money applyEarlyPaymentStrategy(final LoanApplicationTerms loanApplicationTerms, Money reducePrincipal,
final Money totalCumulativePrincipal, int periodNumber, final MathContext mc) {
if (reducePrincipal.isGreaterThanZero()) {
switch (loanApplicationTerms.getRescheduleStrategyMethod()) {
case REDUCE_EMI_AMOUNT:
// in this case emi amount will be reduced but number of
// installments won't change
if (!loanApplicationTerms.isMultiDisburseLoan()) {
if (loanApplicationTerms.getActualFixedEmiAmount() == null) {
loanApplicationTerms.setFixedEmiAmount(null);
updateFixedInstallmentAmount(mc, loanApplicationTerms, loanApplicationTerms.getExpectedDisbursementDate(),
periodNumber, loanApplicationTerms.getPrincipal().minus(totalCumulativePrincipal));
}
reducePrincipal = reducePrincipal.zero();
break;
@@ -458,14 +458,7 @@ public final class LoanApplicationTerms {
case DECLINING_BALANCE:
switch (this.amortizationMethod) {
case EQUAL_INSTALLMENTS:
// equal installments
final int periodsElapsed = periodNumber - 1;
// with periodic interest for default month and year for
// equal installment
final BigDecimal periodicInterestRateForRepaymentPeriod = periodicInterestRate(calculator, mc, daysInPeriod,
DaysInMonthType.DAYS_30, DaysInYearType.DAYS_365);
Money totalPmtForThisInstallment = calculateTotalDueForEqualInstallmentRepaymentPeriod(
periodicInterestRateForRepaymentPeriod, outstandingBalance, periodsElapsed);
Money totalPmtForThisInstallment = pmtForInstallment(calculator, daysInPeriod, outstandingBalance, periodNumber, mc);
principalForInstallment = calculatePrincipalDueForInstallment(periodNumber, totalPmtForThisInstallment,
interestForThisInstallment);
break;
@@ -483,6 +476,19 @@ public final class LoanApplicationTerms {
return principalForInstallment;
}
public Money pmtForInstallment(final PaymentPeriodsInOneYearCalculator calculator, final int daysInPeriod,
final Money outstandingBalance, final int periodNumber, final MathContext mc) {
// equal installments
final int periodsElapsed = periodNumber - 1;
// with periodic interest for default month and year for
// equal installment
final BigDecimal periodicInterestRateForRepaymentPeriod = periodicInterestRate(calculator, mc, daysInPeriod,
DaysInMonthType.DAYS_30, DaysInYearType.DAYS_365);
Money totalPmtForThisInstallment = calculateTotalDueForEqualInstallmentRepaymentPeriod(periodicInterestRateForRepaymentPeriod,
outstandingBalance, periodsElapsed);
return totalPmtForThisInstallment;
}
public PrincipalInterest calculateTotalInterestForPeriod(final PaymentPeriodsInOneYearCalculator calculator,
final double interestCalculationGraceOnRepaymentPeriodFraction, final int periodNumber, final MathContext mc,
final Money cumulatingInterestPaymentDueToGrace, final int daysInPeriodApplicableForInterest, final Money outstandingBalance) {
@@ -1204,4 +1210,9 @@ public final class LoanApplicationTerms {
public RecalculationFrequencyType getCompoundingFrequencyType() {
return this.compoundingFrequencyType;
}
public BigDecimal getActualFixedEmiAmount() {
return this.actualFixedEmiAmount;
}
}
@@ -5,6 +5,7 @@
*/
package org.mifosplatform.portfolio.loanproduct.domain;
public enum InterestMethod {
DECLINING_BALANCE(0, "interestType.declining.balance"), FLAT(1, "interestType.flat"), INVALID(2, "interestType.invalid");
@@ -40,4 +41,8 @@ public enum InterestMethod {
}
return repaymentMethod;
}
public boolean isDecliningBalnce() {
return this.value.equals(InterestMethod.DECLINING_BALANCE.getValue());
}
}