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