Merge pull request #1419 from pramodn02/develop
MIFOSX-2006 : fixed emi amount for tranche loan
This commit is contained in:
+59
-13
@@ -113,6 +113,10 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
int instalmentNumber = 1;
|
int instalmentNumber = 1;
|
||||||
|
|
||||||
Money outstandingBalance = principalDisbursed;
|
Money outstandingBalance = principalDisbursed;
|
||||||
|
|
||||||
|
// Set fixed EMI Amount
|
||||||
|
updateFixedInstallmentAmount(mc, loanApplicationTerms, actualRepaymentDate, periodNumber, outstandingBalance);
|
||||||
|
|
||||||
// disbursement map for tranche details(will added to outstanding
|
// disbursement map for tranche details(will added to outstanding
|
||||||
// balance as per the start date)
|
// balance as per the start date)
|
||||||
final Map<LocalDate, Money> disburseDetailMap = new HashMap<>();
|
final Map<LocalDate, Money> disburseDetailMap = new HashMap<>();
|
||||||
@@ -160,9 +164,9 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
boolean isNextRepaymentAvailable = true;
|
boolean isNextRepaymentAvailable = true;
|
||||||
Boolean extendTermForDailyRepayments = false;
|
Boolean extendTermForDailyRepayments = false;
|
||||||
|
|
||||||
if(holidayDetailDTO.getWorkingDays().getExtendTermForDailyRepayments() == true &&
|
if (holidayDetailDTO.getWorkingDays().getExtendTermForDailyRepayments() == true
|
||||||
loanApplicationTerms.getRepaymentPeriodFrequencyType() == PeriodFrequencyType.DAYS &&
|
&& loanApplicationTerms.getRepaymentPeriodFrequencyType() == PeriodFrequencyType.DAYS
|
||||||
loanApplicationTerms.getRepaymentEvery() == 1){
|
&& loanApplicationTerms.getRepaymentEvery() == 1) {
|
||||||
holidayDetailDTO.getWorkingDays().setRepaymentReschedulingType(RepaymentRescheduleType.MOVE_TO_NEXT_WORKING_DAY.getValue());
|
holidayDetailDTO.getWorkingDays().setRepaymentReschedulingType(RepaymentRescheduleType.MOVE_TO_NEXT_WORKING_DAY.getValue());
|
||||||
extendTermForDailyRepayments = true;
|
extendTermForDailyRepayments = true;
|
||||||
}
|
}
|
||||||
@@ -180,7 +184,7 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
compoundingDateVariations.put(periodStartDate, new TreeMap<>(compoundingMap));
|
compoundingDateVariations.put(periodStartDate, new TreeMap<>(compoundingMap));
|
||||||
}
|
}
|
||||||
|
|
||||||
if(extendTermForDailyRepayments){
|
if (extendTermForDailyRepayments) {
|
||||||
actualRepaymentDate = scheduledDueDate;
|
actualRepaymentDate = scheduledDueDate;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -367,7 +371,8 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
|
|
||||||
// method applies early payment strategy
|
// method applies early payment strategy
|
||||||
reducePrincipal = reducePrincipal.plus(unprocessed);
|
reducePrincipal = reducePrincipal.plus(unprocessed);
|
||||||
reducePrincipal = applyEarlyPaymentStrategy(loanApplicationTerms, reducePrincipal);
|
reducePrincipal = applyEarlyPaymentStrategy(loanApplicationTerms, reducePrincipal,
|
||||||
|
totalCumulativePrincipal, periodNumber, mc);
|
||||||
}
|
}
|
||||||
// identify late payments and add compounding
|
// identify late payments and add compounding
|
||||||
// details to
|
// details to
|
||||||
@@ -501,10 +506,6 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
totalOutstandingInterestPaymentDueToGrace = principalInterestForThisPeriod.interestPaymentDueToGrace();
|
totalOutstandingInterestPaymentDueToGrace = principalInterestForThisPeriod.interestPaymentDueToGrace();
|
||||||
Money principalForThisPeriod = principalInterestForThisPeriod.principal();
|
Money principalForThisPeriod = principalInterestForThisPeriod.principal();
|
||||||
|
|
||||||
if (principalForThisPeriod.isZero()) {
|
|
||||||
loanApplicationTerms.resetFixedEmiAmount();
|
|
||||||
}
|
|
||||||
|
|
||||||
// applies early payments on principal portion
|
// applies early payments on principal portion
|
||||||
if (principalForThisPeriod.isGreaterThan(reducePrincipal)) {
|
if (principalForThisPeriod.isGreaterThan(reducePrincipal)) {
|
||||||
principalForThisPeriod = principalForThisPeriod.minus(reducePrincipal);
|
principalForThisPeriod = principalForThisPeriod.minus(reducePrincipal);
|
||||||
@@ -518,9 +519,15 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
// reducePrincipal.plus(unprocessed);
|
// reducePrincipal.plus(unprocessed);
|
||||||
Money reducedBalance = earlyPaidAmount;
|
Money reducedBalance = earlyPaidAmount;
|
||||||
earlyPaidAmount = earlyPaidAmount.minus(principalForThisPeriod);
|
earlyPaidAmount = earlyPaidAmount.minus(principalForThisPeriod);
|
||||||
|
boolean isEmiAmountChanged = false;
|
||||||
if (earlyPaidAmount.isGreaterThanZero()) {
|
if (earlyPaidAmount.isGreaterThanZero()) {
|
||||||
reducePrincipal = reducePrincipal.plus(earlyPaidAmount);
|
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);
|
principalForThisPeriod = principalForThisPeriod.plus(earlyPaidAmount);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -587,6 +594,11 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
compoundingMap.clear();
|
compoundingMap.clear();
|
||||||
compoundingMap.putAll(compoundingDateVariations.get(periodStartDateApplicableForInterest));
|
compoundingMap.putAll(compoundingDateVariations.get(periodStartDateApplicableForInterest));
|
||||||
}
|
}
|
||||||
|
if (isEmiAmountChanged) {
|
||||||
|
updateFixedInstallmentAmount(mc, loanApplicationTerms,
|
||||||
|
loanApplicationTerms.getExpectedDisbursementDate(), periodNumber, loanApplicationTerms
|
||||||
|
.getPrincipal().minus(totalCumulativePrincipal));
|
||||||
|
}
|
||||||
PrincipalInterest interestTillDate = calculatePrincipalInterestComponentsForPeriod(
|
PrincipalInterest interestTillDate = calculatePrincipalInterestComponentsForPeriod(
|
||||||
this.paymentPeriodsInOneYearCalculator, interestCalculationGraceOnRepaymentPeriodFraction,
|
this.paymentPeriodsInOneYearCalculator, interestCalculationGraceOnRepaymentPeriodFraction,
|
||||||
totalCumulativePrincipal, totalCumulativeInterest, totalInterestChargedForFullLoanTerm,
|
totalCumulativePrincipal, totalCumulativeInterest, totalInterestChargedForFullLoanTerm,
|
||||||
@@ -626,9 +638,15 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
totalCumulativePrincipal = totalCumulativePrincipal.plus(unprocessed.plus(addToPrinciapal));
|
totalCumulativePrincipal = totalCumulativePrincipal.plus(unprocessed.plus(addToPrinciapal));
|
||||||
|
|
||||||
reducePrincipal = reducePrincipal.plus(unprocessed);
|
reducePrincipal = reducePrincipal.plus(unprocessed);
|
||||||
reducePrincipal = applyEarlyPaymentStrategy(loanApplicationTerms, reducePrincipal);
|
|
||||||
principalForThisPeriod = principalForThisPeriod.plus(unprocessed.plus(addToPrinciapal));
|
principalForThisPeriod = principalForThisPeriod.plus(unprocessed.plus(addToPrinciapal));
|
||||||
principalProcessed = principalProcessed.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);
|
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,
|
private Money fetchCompoundedArrears(final LoanApplicationTerms loanApplicationTerms, final MonetaryCurrency currency,
|
||||||
final LoanTransaction transaction) {
|
final LoanTransaction transaction) {
|
||||||
Money arrears = transaction.getPrincipalPortion(currency);
|
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
|
* 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()) {
|
if (reducePrincipal.isGreaterThanZero()) {
|
||||||
switch (loanApplicationTerms.getRescheduleStrategyMethod()) {
|
switch (loanApplicationTerms.getRescheduleStrategyMethod()) {
|
||||||
case REDUCE_EMI_AMOUNT:
|
case REDUCE_EMI_AMOUNT:
|
||||||
// in this case emi amount will be reduced but number of
|
// in this case emi amount will be reduced but number of
|
||||||
// installments won't change
|
// installments won't change
|
||||||
if (!loanApplicationTerms.isMultiDisburseLoan()) {
|
if (loanApplicationTerms.getActualFixedEmiAmount() == null) {
|
||||||
loanApplicationTerms.setFixedEmiAmount(null);
|
loanApplicationTerms.setFixedEmiAmount(null);
|
||||||
|
updateFixedInstallmentAmount(mc, loanApplicationTerms, loanApplicationTerms.getExpectedDisbursementDate(),
|
||||||
|
periodNumber, loanApplicationTerms.getPrincipal().minus(totalCumulativePrincipal));
|
||||||
}
|
}
|
||||||
reducePrincipal = reducePrincipal.zero();
|
reducePrincipal = reducePrincipal.zero();
|
||||||
break;
|
break;
|
||||||
|
|||||||
+19
-8
@@ -458,14 +458,7 @@ public final class LoanApplicationTerms {
|
|||||||
case DECLINING_BALANCE:
|
case DECLINING_BALANCE:
|
||||||
switch (this.amortizationMethod) {
|
switch (this.amortizationMethod) {
|
||||||
case EQUAL_INSTALLMENTS:
|
case EQUAL_INSTALLMENTS:
|
||||||
// equal installments
|
Money totalPmtForThisInstallment = pmtForInstallment(calculator, daysInPeriod, outstandingBalance, periodNumber, mc);
|
||||||
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);
|
|
||||||
principalForInstallment = calculatePrincipalDueForInstallment(periodNumber, totalPmtForThisInstallment,
|
principalForInstallment = calculatePrincipalDueForInstallment(periodNumber, totalPmtForThisInstallment,
|
||||||
interestForThisInstallment);
|
interestForThisInstallment);
|
||||||
break;
|
break;
|
||||||
@@ -483,6 +476,19 @@ public final class LoanApplicationTerms {
|
|||||||
return principalForInstallment;
|
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,
|
public PrincipalInterest calculateTotalInterestForPeriod(final PaymentPeriodsInOneYearCalculator calculator,
|
||||||
final double interestCalculationGraceOnRepaymentPeriodFraction, final int periodNumber, final MathContext mc,
|
final double interestCalculationGraceOnRepaymentPeriodFraction, final int periodNumber, final MathContext mc,
|
||||||
final Money cumulatingInterestPaymentDueToGrace, final int daysInPeriodApplicableForInterest, final Money outstandingBalance) {
|
final Money cumulatingInterestPaymentDueToGrace, final int daysInPeriodApplicableForInterest, final Money outstandingBalance) {
|
||||||
@@ -1204,4 +1210,9 @@ public final class LoanApplicationTerms {
|
|||||||
public RecalculationFrequencyType getCompoundingFrequencyType() {
|
public RecalculationFrequencyType getCompoundingFrequencyType() {
|
||||||
return this.compoundingFrequencyType;
|
return this.compoundingFrequencyType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public BigDecimal getActualFixedEmiAmount() {
|
||||||
|
return this.actualFixedEmiAmount;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+5
@@ -5,6 +5,7 @@
|
|||||||
*/
|
*/
|
||||||
package org.mifosplatform.portfolio.loanproduct.domain;
|
package org.mifosplatform.portfolio.loanproduct.domain;
|
||||||
|
|
||||||
|
|
||||||
public enum InterestMethod {
|
public enum InterestMethod {
|
||||||
DECLINING_BALANCE(0, "interestType.declining.balance"), FLAT(1, "interestType.flat"), INVALID(2, "interestType.invalid");
|
DECLINING_BALANCE(0, "interestType.declining.balance"), FLAT(1, "interestType.flat"), INVALID(2, "interestType.invalid");
|
||||||
|
|
||||||
@@ -40,4 +41,8 @@ public enum InterestMethod {
|
|||||||
}
|
}
|
||||||
return repaymentMethod;
|
return repaymentMethod;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isDecliningBalnce() {
|
||||||
|
return this.value.equals(InterestMethod.DECLINING_BALANCE.getValue());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user