MIFOSX-729: For flat loans, daily interest calculation not using daily periods when calulating interest, it was still using the installment period and as a result the interest calculated was not correct.
This commit is contained in:
+5
-2
@@ -38,6 +38,9 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
final List<LocalDate> scheduledDates = this.scheduledDateGenerator.generate(loanApplicationTerms, isHolidayEnabled, holidays,
|
final List<LocalDate> scheduledDates = this.scheduledDateGenerator.generate(loanApplicationTerms, isHolidayEnabled, holidays,
|
||||||
workingDays);
|
workingDays);
|
||||||
|
|
||||||
|
final LocalDate loanEndDate = scheduledDates.get(scheduledDates.size() - 1);
|
||||||
|
loanApplicationTerms.updateLoanEndDate(loanEndDate);
|
||||||
|
|
||||||
// 2. determine the total charges due at time of disbursement
|
// 2. determine the total charges due at time of disbursement
|
||||||
final BigDecimal chargesDueAtTimeOfDisbursement = deriveTotalChargesDueAtTimeOfDisbursement(loanCharges);
|
final BigDecimal chargesDueAtTimeOfDisbursement = deriveTotalChargesDueAtTimeOfDisbursement(loanCharges);
|
||||||
|
|
||||||
@@ -141,8 +144,8 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
|
|
||||||
public abstract PrincipalInterest calculatePrincipalInterestComponentsForPeriod(PaymentPeriodsInOneYearCalculator calculator,
|
public abstract PrincipalInterest calculatePrincipalInterestComponentsForPeriod(PaymentPeriodsInOneYearCalculator calculator,
|
||||||
double interestCalculationGraceOnRepaymentPeriodFraction, Money totalCumulativePrincipal, Money totalCumulativeInterest,
|
double interestCalculationGraceOnRepaymentPeriodFraction, Money totalCumulativePrincipal, Money totalCumulativeInterest,
|
||||||
Money totalInterestDueForLoan, Money cumulatingInterestPaymentDueToGrace, int daysInPeriodApplicableForInterest, Money outstandingBalance,
|
Money totalInterestDueForLoan, Money cumulatingInterestPaymentDueToGrace, int daysInPeriodApplicableForInterest,
|
||||||
LoanApplicationTerms loanApplicationTerms, int periodNumber, MathContext mc);
|
Money outstandingBalance, LoanApplicationTerms loanApplicationTerms, int periodNumber, MathContext mc);
|
||||||
|
|
||||||
protected final boolean isLastRepaymentPeriod(final int numberOfRepayments, final int periodNumber) {
|
protected final boolean isLastRepaymentPeriod(final int numberOfRepayments, final int periodNumber) {
|
||||||
return periodNumber == numberOfRepayments;
|
return periodNumber == numberOfRepayments;
|
||||||
|
|||||||
+4
-4
@@ -18,12 +18,12 @@ public class FlatInterestLoanScheduleGenerator extends AbstractLoanScheduleGener
|
|||||||
final int daysInPeriodApplicableForInterest, final Money outstandingBalance, final LoanApplicationTerms loanApplicationTerms,
|
final int daysInPeriodApplicableForInterest, final Money outstandingBalance, final LoanApplicationTerms loanApplicationTerms,
|
||||||
final int periodNumber, final MathContext mc) {
|
final int periodNumber, final MathContext mc) {
|
||||||
|
|
||||||
Money principalForThisInstallment = loanApplicationTerms.calculateTotalPrincipalForPeriod(calculator, daysInPeriodApplicableForInterest,
|
Money principalForThisInstallment = loanApplicationTerms.calculateTotalPrincipalForPeriod(calculator,
|
||||||
outstandingBalance, periodNumber, mc);
|
daysInPeriodApplicableForInterest, outstandingBalance, periodNumber, mc);
|
||||||
|
|
||||||
final PrincipalInterest result = loanApplicationTerms.calculateTotalInterestForPeriod(calculator,
|
final PrincipalInterest result = loanApplicationTerms.calculateTotalInterestForPeriod(calculator,
|
||||||
interestCalculationGraceOnRepaymentPeriodFraction, periodNumber, mc, cumulatingInterestPaymentDueToGrace, daysInPeriodApplicableForInterest,
|
interestCalculationGraceOnRepaymentPeriodFraction, periodNumber, mc, cumulatingInterestPaymentDueToGrace,
|
||||||
outstandingBalance);
|
daysInPeriodApplicableForInterest, outstandingBalance);
|
||||||
Money interestForThisInstallment = result.interest();
|
Money interestForThisInstallment = result.interest();
|
||||||
|
|
||||||
// update cumulative fields for principal & interest
|
// update cumulative fields for principal & interest
|
||||||
|
|||||||
+56
-11
@@ -9,6 +9,7 @@ import java.math.BigDecimal;
|
|||||||
import java.math.MathContext;
|
import java.math.MathContext;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
|
||||||
|
import org.joda.time.Days;
|
||||||
import org.joda.time.LocalDate;
|
import org.joda.time.LocalDate;
|
||||||
import org.mifosplatform.organisation.monetary.domain.ApplicationCurrency;
|
import org.mifosplatform.organisation.monetary.domain.ApplicationCurrency;
|
||||||
import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency;
|
import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency;
|
||||||
@@ -50,7 +51,7 @@ public final class LoanApplicationTerms {
|
|||||||
* Integer representing the number of 'repayment frequencies' or
|
* Integer representing the number of 'repayment frequencies' or
|
||||||
* installments where 'grace' should apply to the payment of interest in a
|
* installments where 'grace' should apply to the payment of interest in a
|
||||||
* loans repayment period (installment).
|
* loans repayment period (installment).
|
||||||
*
|
*
|
||||||
* <b>Note:</b> Interest is still calculated taking into account the full
|
* <b>Note:</b> Interest is still calculated taking into account the full
|
||||||
* loan term, the interest is simply offset to a later period.
|
* loan term, the interest is simply offset to a later period.
|
||||||
*/
|
*/
|
||||||
@@ -60,7 +61,7 @@ public final class LoanApplicationTerms {
|
|||||||
* Integer representing the number of 'repayment frequencies' or
|
* Integer representing the number of 'repayment frequencies' or
|
||||||
* installments where 'grace' should apply to the charging of interest in a
|
* installments where 'grace' should apply to the charging of interest in a
|
||||||
* loans repayment period (installment).
|
* loans repayment period (installment).
|
||||||
*
|
*
|
||||||
* <b>Note:</b> The loan is <i>interest-free</i> for the period of time
|
* <b>Note:</b> The loan is <i>interest-free</i> for the period of time
|
||||||
* indicated.
|
* indicated.
|
||||||
*/
|
*/
|
||||||
@@ -68,7 +69,7 @@ public final class LoanApplicationTerms {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Legacy method of support 'grace' on the charging of interest on a loan.
|
* Legacy method of support 'grace' on the charging of interest on a loan.
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* For the typical structured loan, its reasonable to use an integer to
|
* For the typical structured loan, its reasonable to use an integer to
|
||||||
* indicate the number of 'repayment frequency' periods the 'grace' should
|
* indicate the number of 'repayment frequency' periods the 'grace' should
|
||||||
@@ -82,6 +83,9 @@ public final class LoanApplicationTerms {
|
|||||||
private final LocalDate interestChargedFromDate;
|
private final LocalDate interestChargedFromDate;
|
||||||
private final Money inArrearsTolerance;
|
private final Money inArrearsTolerance;
|
||||||
|
|
||||||
|
// added
|
||||||
|
private LocalDate loanEndDate;
|
||||||
|
|
||||||
public static LoanApplicationTerms assembleFrom(final ApplicationCurrency currency, final Integer loanTermFrequency,
|
public static LoanApplicationTerms assembleFrom(final ApplicationCurrency currency, final Integer loanTermFrequency,
|
||||||
final PeriodFrequencyType loanTermPeriodFrequencyType, final Integer numberOfRepayments, final Integer repaymentEvery,
|
final PeriodFrequencyType loanTermPeriodFrequencyType, final Integer numberOfRepayments, final Integer repaymentEvery,
|
||||||
final PeriodFrequencyType repaymentPeriodFrequencyType, final AmortizationMethod amortizationMethod,
|
final PeriodFrequencyType repaymentPeriodFrequencyType, final AmortizationMethod amortizationMethod,
|
||||||
@@ -216,7 +220,7 @@ public final class LoanApplicationTerms {
|
|||||||
/**
|
/**
|
||||||
* Calculates the total interest to be charged on loan taking into account
|
* Calculates the total interest to be charged on loan taking into account
|
||||||
* grace settings.
|
* grace settings.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public Money calculateTotalInterestCharged(final PaymentPeriodsInOneYearCalculator calculator, final MathContext mc) {
|
public Money calculateTotalInterestCharged(final PaymentPeriodsInOneYearCalculator calculator, final MathContext mc) {
|
||||||
|
|
||||||
@@ -224,7 +228,7 @@ public final class LoanApplicationTerms {
|
|||||||
|
|
||||||
switch (this.interestMethod) {
|
switch (this.interestMethod) {
|
||||||
case FLAT:
|
case FLAT:
|
||||||
final Money totalInterestChargedForLoanTerm = calculateTotalInterestDueWithoutGrace(calculator, mc);
|
final Money totalInterestChargedForLoanTerm = calculateTotalFlatInterestDueWithoutGrace(calculator, mc);
|
||||||
|
|
||||||
final Money totalInterestPerInstallment = calculateTotalInterestPerInstallmentWithoutGrace(calculator, mc);
|
final Money totalInterestPerInstallment = calculateTotalInterestPerInstallmentWithoutGrace(calculator, mc);
|
||||||
|
|
||||||
@@ -321,7 +325,8 @@ public final class LoanApplicationTerms {
|
|||||||
calculator, mc, daysInPeriodApplicableForInterest, outstandingBalance);
|
calculator, mc, daysInPeriodApplicableForInterest, outstandingBalance);
|
||||||
|
|
||||||
final Money interestForThisInstallmentAfterGrace = calculateDecliningInterestDueForInstallmentAfterApplyingGrace(
|
final Money interestForThisInstallmentAfterGrace = calculateDecliningInterestDueForInstallmentAfterApplyingGrace(
|
||||||
calculator, interestCalculationGraceOnRepaymentPeriodFraction, mc, daysInPeriodApplicableForInterest, outstandingBalance, periodNumber);
|
calculator, interestCalculationGraceOnRepaymentPeriodFraction, mc, daysInPeriodApplicableForInterest,
|
||||||
|
outstandingBalance, periodNumber);
|
||||||
|
|
||||||
interestForInstallment = interestForThisInstallmentAfterGrace;
|
interestForInstallment = interestForThisInstallmentAfterGrace;
|
||||||
if (interestForThisInstallmentAfterGrace.isGreaterThanZero()) {
|
if (interestForThisInstallmentAfterGrace.isGreaterThanZero()) {
|
||||||
@@ -359,7 +364,7 @@ public final class LoanApplicationTerms {
|
|||||||
* general method to calculate totalInterestDue discounting any grace
|
* general method to calculate totalInterestDue discounting any grace
|
||||||
* settings
|
* settings
|
||||||
*/
|
*/
|
||||||
private Money calculateTotalInterestDueWithoutGrace(final PaymentPeriodsInOneYearCalculator calculator, final MathContext mc) {
|
private Money calculateTotalFlatInterestDueWithoutGrace(final PaymentPeriodsInOneYearCalculator calculator, final MathContext mc) {
|
||||||
|
|
||||||
Money totalInterestDue = this.principal.zero();
|
Money totalInterestDue = this.principal.zero();
|
||||||
|
|
||||||
@@ -385,15 +390,43 @@ public final class LoanApplicationTerms {
|
|||||||
final long loanTermPeriodsInOneYear = calculatePeriodsInOneYear(calculator);
|
final long loanTermPeriodsInOneYear = calculatePeriodsInOneYear(calculator);
|
||||||
final BigDecimal loanTermPeriodsInYearBigDecimal = BigDecimal.valueOf(loanTermPeriodsInOneYear);
|
final BigDecimal loanTermPeriodsInYearBigDecimal = BigDecimal.valueOf(loanTermPeriodsInOneYear);
|
||||||
|
|
||||||
final BigDecimal loanTermFrequencyBigDecimal = BigDecimal.valueOf(this.loanTermFrequency);
|
final BigDecimal loanTermFrequencyBigDecimal = calculatePeriodsInLoanTerm();
|
||||||
|
|
||||||
return this.annualNominalInterestRate.divide(loanTermPeriodsInYearBigDecimal, mc).divide(divisor, mc)
|
return this.annualNominalInterestRate.divide(loanTermPeriodsInYearBigDecimal, mc).divide(divisor, mc)
|
||||||
.multiply(loanTermFrequencyBigDecimal);
|
.multiply(loanTermFrequencyBigDecimal);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private BigDecimal calculatePeriodsInLoanTerm() {
|
||||||
|
|
||||||
|
BigDecimal periodsInLoanTerm = BigDecimal.valueOf(this.loanTermFrequency);
|
||||||
|
switch (this.interestCalculationPeriodMethod) {
|
||||||
|
case DAILY:
|
||||||
|
// number of days from 'ideal disbursement' to final date
|
||||||
|
|
||||||
|
LocalDate loanStartDate = getExpectedDisbursementDate();
|
||||||
|
if (getInterestChargedFromDate() != null && loanStartDate.isBefore(getInterestChargedFromLocalDate())) {
|
||||||
|
loanStartDate = getInterestChargedFromLocalDate();
|
||||||
|
}
|
||||||
|
|
||||||
|
final int periodsInLoanTermInteger = Days.daysBetween(loanStartDate, this.loanEndDate).getDays();
|
||||||
|
periodsInLoanTerm = BigDecimal.valueOf(periodsInLoanTermInteger);
|
||||||
|
break;
|
||||||
|
case INVALID:
|
||||||
|
break;
|
||||||
|
case SAME_AS_REPAYMENT_PERIOD:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return periodsInLoanTerm;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateLoanEndDate(final LocalDate loanEndDate) {
|
||||||
|
this.loanEndDate = loanEndDate;
|
||||||
|
}
|
||||||
|
|
||||||
private Money calculateTotalInterestPerInstallmentWithoutGrace(final PaymentPeriodsInOneYearCalculator calculator, final MathContext mc) {
|
private Money calculateTotalInterestPerInstallmentWithoutGrace(final PaymentPeriodsInOneYearCalculator calculator, final MathContext mc) {
|
||||||
|
|
||||||
final Money totalInterestForLoanTerm = calculateTotalInterestDueWithoutGrace(calculator, mc);
|
final Money totalInterestForLoanTerm = calculateTotalFlatInterestDueWithoutGrace(calculator, mc);
|
||||||
|
|
||||||
return totalInterestForLoanTerm.dividedBy(Long.valueOf(this.numberOfRepayments), mc.getRoundingMode());
|
return totalInterestForLoanTerm.dividedBy(Long.valueOf(this.numberOfRepayments), mc.getRoundingMode());
|
||||||
}
|
}
|
||||||
@@ -439,7 +472,7 @@ public final class LoanApplicationTerms {
|
|||||||
interestForInstallment = interestForInstallment.zero();
|
interestForInstallment = interestForInstallment.zero();
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
final Money totalInterestForLoanTerm = calculateTotalInterestDueWithoutGrace(calculator, mc);
|
final Money totalInterestForLoanTerm = calculateTotalFlatInterestDueWithoutGrace(calculator, mc);
|
||||||
|
|
||||||
final Money interestPerGracePeriod = calculateTotalInterestPerInstallmentWithoutGrace(calculator, mc);
|
final Money interestPerGracePeriod = calculateTotalInterestPerInstallmentWithoutGrace(calculator, mc);
|
||||||
|
|
||||||
@@ -469,7 +502,19 @@ public final class LoanApplicationTerms {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private long calculatePeriodsInOneYear(final PaymentPeriodsInOneYearCalculator calculator) {
|
private long calculatePeriodsInOneYear(final PaymentPeriodsInOneYearCalculator calculator) {
|
||||||
return calculator.calculate(this.repaymentPeriodFrequencyType).longValue();
|
|
||||||
|
long periodsInOneYear = calculator.calculate(this.repaymentPeriodFrequencyType).longValue();
|
||||||
|
switch (this.interestCalculationPeriodMethod) {
|
||||||
|
case DAILY:
|
||||||
|
periodsInOneYear = calculator.calculate(PeriodFrequencyType.DAYS).longValue();
|
||||||
|
break;
|
||||||
|
case INVALID:
|
||||||
|
break;
|
||||||
|
case SAME_AS_REPAYMENT_PERIOD:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return periodsInOneYear;
|
||||||
}
|
}
|
||||||
|
|
||||||
private int calculateNumberOfRepaymentsWithPrincipalPayment() {
|
private int calculateNumberOfRepaymentsWithPrincipalPayment() {
|
||||||
|
|||||||
Reference in New Issue
Block a user