MIFOSX-721: fix issue with daily interest calculation when using interest charged from date feature.

This commit is contained in:
Keith Woodlock
2013-10-25 13:15:06 +01:00
parent 1d61eef369
commit bc3e107bfd
4 changed files with 33 additions and 25 deletions
@@ -65,10 +65,11 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
this.paymentPeriodsInOneYearCalculator, mc);
// 5. loop over the known set of 'scheduled due dates'
LocalDate repaymentPeriodStartDate = this.scheduledDateGenerator.idealDisbursementDateBasedOnFirstRepaymentDate(
final LocalDate idealDisbursementDate = this.scheduledDateGenerator.idealDisbursementDateBasedOnFirstRepaymentDate(
loanApplicationTerms.getLoanTermPeriodFrequencyType(), loanApplicationTerms.getRepaymentEvery(), scheduledDates);
LocalDate startDate = loanApplicationTerms.getExpectedDisbursementDate();
LocalDate periodStartDate = loanApplicationTerms.getExpectedDisbursementDate();
LocalDate periodStartDateApplicableForInterest = periodStartDate;
int periodNumber = 1;
Money totalCumulativePrincipal = principalDisbursed.zero();
Money totalCumulativeInterest = principalDisbursed.zero();
@@ -76,18 +77,24 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
Money outstandingBalance = principalDisbursed;
for (final LocalDate scheduledDueDate : scheduledDates) {
final int daysInPeriod = Days.daysBetween(startDate, scheduledDueDate).getDays();
final int daysInPeriod = Days.daysBetween(periodStartDate, scheduledDueDate).getDays();
int daysInPeriodApplicableForInterest = daysInPeriod;
if (periodStartDate.isBefore(idealDisbursementDate)) {
periodStartDateApplicableForInterest = idealDisbursementDate;
daysInPeriodApplicableForInterest = Days.daysBetween(periodStartDateApplicableForInterest, scheduledDueDate).getDays();
}
final double interestCalculationGraceOnRepaymentPeriodFraction = this.paymentPeriodsInOneYearCalculator
.calculatePortionOfRepaymentPeriodInterestChargingGrace(repaymentPeriodStartDate, scheduledDueDate,
.calculatePortionOfRepaymentPeriodInterestChargingGrace(periodStartDateApplicableForInterest, scheduledDueDate,
loanApplicationTerms.getInterestChargedFromLocalDate(), loanApplicationTerms.getLoanTermPeriodFrequencyType(),
loanApplicationTerms.getRepaymentEvery());
// 5 determine principal,interest of repayment period
final PrincipalInterest principalInterestForThisPeriod = calculatePrincipalInterestComponentsForPeriod(
this.paymentPeriodsInOneYearCalculator, interestCalculationGraceOnRepaymentPeriodFraction, totalCumulativePrincipal,
totalCumulativeInterest, totalInterestChargedForFullLoanTerm, totalOutstandingInterestPaymentDueToGrace, daysInPeriod,
outstandingBalance, loanApplicationTerms, periodNumber, mc);
totalCumulativeInterest, totalInterestChargedForFullLoanTerm, totalOutstandingInterestPaymentDueToGrace,
daysInPeriodApplicableForInterest, outstandingBalance, loanApplicationTerms, periodNumber, mc);
// update cumulative fields for principal & interest
totalCumulativePrincipal = totalCumulativePrincipal.plus(principalInterestForThisPeriod.principal());
@@ -98,8 +105,9 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
outstandingBalance = outstandingBalance.minus(principalInterestForThisPeriod.principal());
// 7. determine fees and penalties
final Money feeChargesForInstallment = cumulativeFeeChargesDueWithin(startDate, scheduledDueDate, loanCharges, currency);
final Money penaltyChargesForInstallment = cumulativePenaltyChargesDueWithin(startDate, scheduledDueDate, loanCharges, currency);
final Money feeChargesForInstallment = cumulativeFeeChargesDueWithin(periodStartDate, scheduledDueDate, loanCharges, currency);
final Money penaltyChargesForInstallment = cumulativePenaltyChargesDueWithin(periodStartDate, scheduledDueDate, loanCharges,
currency);
// 8. sum up real totalInstallmentDue from components
final Money totalInstallmentDue = principalInterestForThisPeriod.principal() //
@@ -108,7 +116,7 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
.plus(penaltyChargesForInstallment);
// 9. create repayment period from parts
final LoanScheduleModelPeriod installment = LoanScheduleModelRepaymentPeriod.repayment(periodNumber, startDate,
final LoanScheduleModelPeriod installment = LoanScheduleModelRepaymentPeriod.repayment(periodNumber, periodStartDate,
scheduledDueDate, principalInterestForThisPeriod.principal(), outstandingBalance,
principalInterestForThisPeriod.interest(), feeChargesForInstallment, penaltyChargesForInstallment, totalInstallmentDue);
periods.add(installment);
@@ -120,8 +128,8 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
totalFeeChargesCharged = totalFeeChargesCharged.add(feeChargesForInstallment.getAmount());
totalPenaltyChargesCharged = totalPenaltyChargesCharged.add(penaltyChargesForInstallment.getAmount());
totalRepaymentExpected = totalRepaymentExpected.add(totalInstallmentDue.getAmount());
startDate = scheduledDueDate;
repaymentPeriodStartDate = startDate;
periodStartDate = scheduledDueDate;
periodStartDateApplicableForInterest = periodStartDate;
periodNumber++;
}
@@ -133,7 +141,7 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
public abstract PrincipalInterest calculatePrincipalInterestComponentsForPeriod(PaymentPeriodsInOneYearCalculator calculator,
double interestCalculationGraceOnRepaymentPeriodFraction, Money totalCumulativePrincipal, Money totalCumulativeInterest,
Money totalInterestDueForLoan, Money cumulatingInterestPaymentDueToGrace, int daysInPeriod, Money outstandingBalance,
Money totalInterestDueForLoan, Money cumulatingInterestPaymentDueToGrace, int daysInPeriodApplicableForInterest, Money outstandingBalance,
LoanApplicationTerms loanApplicationTerms, int periodNumber, MathContext mc);
protected final boolean isLastRepaymentPeriod(final int numberOfRepayments, final int periodNumber) {
@@ -19,14 +19,14 @@ import org.mifosplatform.portfolio.loanproduct.domain.AmortizationMethod;
* <li>Equal installment payments</li>
* </ol>
* </p>
*
*
* <p>
* When amortized using <i>equal principal payments</i>, the <b>principal
* component</b> of each installment is fixed and <b>interest due</b> is
* calculated from the <b>outstanding principal balance</b> resulting in a
* different <b>total payment due</b> for each installment.
* </p>
*
*
* <p>
* When amortized using <i>equal installments</i>, the <b>total payment due</b>
* for each installment is fixed and is calculated using the excel like
@@ -42,17 +42,17 @@ public class DecliningBalanceInterestLoanScheduleGenerator extends AbstractLoanS
final double interestCalculationGraceOnRepaymentPeriodFraction, final Money totalCumulativePrincipal,
@SuppressWarnings("unused") final Money totalCumulativeInterest,
@SuppressWarnings("unused") final Money totalInterestDueForLoan, final Money cumulatingInterestPaymentDueToGrace,
final int daysInPeriod, final Money outstandingBalance, final LoanApplicationTerms loanApplicationTerms,
final int daysInPeriodApplicableForInterest, final Money outstandingBalance, final LoanApplicationTerms loanApplicationTerms,
final int periodNumber, final MathContext mc) {
final PrincipalInterest result = loanApplicationTerms.calculateTotalInterestForPeriod(calculator,
interestCalculationGraceOnRepaymentPeriodFraction, periodNumber, mc, cumulatingInterestPaymentDueToGrace, daysInPeriod,
outstandingBalance);
interestCalculationGraceOnRepaymentPeriodFraction, periodNumber, mc, cumulatingInterestPaymentDueToGrace,
daysInPeriodApplicableForInterest, outstandingBalance);
final Money interestForThisInstallment = result.interest();
Money principalForThisInstallment = loanApplicationTerms.calculateTotalPrincipalForPeriod(calculator, daysInPeriod,
outstandingBalance, periodNumber, mc);
Money principalForThisInstallment = loanApplicationTerms.calculateTotalPrincipalForPeriod(calculator,
daysInPeriodApplicableForInterest, outstandingBalance, periodNumber, mc);
// update cumulative fields for principal & interest
final Money interestBroughtFowardDueToGrace = result.interestPaymentDueToGrace();
@@ -15,14 +15,14 @@ public class FlatInterestLoanScheduleGenerator extends AbstractLoanScheduleGener
public PrincipalInterest calculatePrincipalInterestComponentsForPeriod(final PaymentPeriodsInOneYearCalculator calculator,
final double interestCalculationGraceOnRepaymentPeriodFraction, final Money totalCumulativePrincipal,
final Money totalCumulativeInterest, final Money totalInterestDueForLoan, final Money cumulatingInterestPaymentDueToGrace,
final int daysInPeriod, final Money outstandingBalance, final LoanApplicationTerms loanApplicationTerms,
final int daysInPeriodApplicableForInterest, final Money outstandingBalance, final LoanApplicationTerms loanApplicationTerms,
final int periodNumber, final MathContext mc) {
Money principalForThisInstallment = loanApplicationTerms.calculateTotalPrincipalForPeriod(calculator, daysInPeriod,
Money principalForThisInstallment = loanApplicationTerms.calculateTotalPrincipalForPeriod(calculator, daysInPeriodApplicableForInterest,
outstandingBalance, periodNumber, mc);
final PrincipalInterest result = loanApplicationTerms.calculateTotalInterestForPeriod(calculator,
interestCalculationGraceOnRepaymentPeriodFraction, periodNumber, mc, cumulatingInterestPaymentDueToGrace, daysInPeriod,
interestCalculationGraceOnRepaymentPeriodFraction, periodNumber, mc, cumulatingInterestPaymentDueToGrace, daysInPeriodApplicableForInterest,
outstandingBalance);
Money interestForThisInstallment = result.interest();
@@ -288,7 +288,7 @@ public final class LoanApplicationTerms {
public PrincipalInterest calculateTotalInterestForPeriod(final PaymentPeriodsInOneYearCalculator calculator,
final double interestCalculationGraceOnRepaymentPeriodFraction, final int periodNumber, final MathContext mc,
final Money cumulatingInterestPaymentDueToGrace, final int daysInPeriod, final Money outstandingBalance) {
final Money cumulatingInterestPaymentDueToGrace, final int daysInPeriodApplicableForInterest, final Money outstandingBalance) {
Money interestForInstallment = this.principal.zero();
Money interestBroughtForwardDueToGrace = cumulatingInterestPaymentDueToGrace.copy();
@@ -318,10 +318,10 @@ public final class LoanApplicationTerms {
case DECLINING_BALANCE:
final Money interestForThisInstallmentBeforeGrace = calculateDecliningInterestDueForInstallmentBeforeApplyingGrace(
calculator, mc, daysInPeriod, outstandingBalance);
calculator, mc, daysInPeriodApplicableForInterest, outstandingBalance);
final Money interestForThisInstallmentAfterGrace = calculateDecliningInterestDueForInstallmentAfterApplyingGrace(
calculator, interestCalculationGraceOnRepaymentPeriodFraction, mc, daysInPeriod, outstandingBalance, periodNumber);
calculator, interestCalculationGraceOnRepaymentPeriodFraction, mc, daysInPeriodApplicableForInterest, outstandingBalance, periodNumber);
interestForInstallment = interestForThisInstallmentAfterGrace;
if (interestForThisInstallmentAfterGrace.isGreaterThanZero()) {