MIFOSX-672: fix issue with FLAT interest calculation not correctly calculation prinicpal balance for final installments.
This commit is contained in:
+26
-35
@@ -50,7 +50,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 +60,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 +68,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
|
||||||
@@ -171,15 +171,10 @@ public final class LoanApplicationTerms {
|
|||||||
|
|
||||||
Money adjusted = principalForPeriod;
|
Money adjusted = principalForPeriod;
|
||||||
|
|
||||||
// adjust cumulative fields for principal & interest if needed
|
final Money totalPrincipalRemaining = this.principal.minus(totalCumulativePrincipalToDate);
|
||||||
if (this.principal.getAmount().subtract(totalCumulativePrincipalToDate.getAmount()).add(principalForPeriod.getAmount()).compareTo(principalForPeriod.getAmount()) == -1) {
|
if (totalPrincipalRemaining.isLessThanZero()) {
|
||||||
adjusted = adjusted.minus(adjusted);
|
// paid too much principal, subtract amount that overpays from principal paid for period.
|
||||||
MonetaryCurrency monetaryCurrency = new MonetaryCurrency(adjusted.getCurrencyCode(), 0, adjusted.getCurrencyInMultiplesOf());
|
adjusted = principalForPeriod.minus(totalPrincipalRemaining.abs());
|
||||||
BigDecimal diff = this.principal.getAmount().subtract(totalCumulativePrincipalToDate.getAmount()).add(principalForPeriod.getAmount());
|
|
||||||
if(diff.doubleValue()<0){
|
|
||||||
diff = BigDecimal.ZERO;
|
|
||||||
}
|
|
||||||
adjusted = adjusted.plus(Money.of(monetaryCurrency,diff));
|
|
||||||
} else if (isLastRepaymentPeriod(this.numberOfRepayments, periodNumber)) {
|
} else if (isLastRepaymentPeriod(this.numberOfRepayments, periodNumber)) {
|
||||||
|
|
||||||
final Money difference = totalCumulativePrincipalToDate.minus(this.principal);
|
final Money difference = totalCumulativePrincipalToDate.minus(this.principal);
|
||||||
@@ -197,15 +192,11 @@ public final class LoanApplicationTerms {
|
|||||||
final Money totalInterestDueForLoan, final int periodNumber) {
|
final Money totalInterestDueForLoan, final int periodNumber) {
|
||||||
|
|
||||||
Money adjusted = interestForThisPeriod;
|
Money adjusted = interestForThisPeriod;
|
||||||
if (totalInterestDueForLoan.getAmount().subtract(totalCumulativeInterestToDate.getAmount()).add(interestForThisPeriod.getAmount())
|
|
||||||
.compareTo(interestForThisPeriod.getAmount()) == -1) {
|
final Money totalInterestRemaining = totalInterestDueForLoan.minus(totalCumulativeInterestToDate);
|
||||||
adjusted = adjusted.minus(adjusted);
|
if (totalInterestRemaining.isLessThanZero()) {
|
||||||
MonetaryCurrency monetaryCurrency = new MonetaryCurrency(adjusted.getCurrencyCode(), 0, adjusted.getCurrencyInMultiplesOf());
|
// paid too much interest, subtract amount that overpays from interest paid for period.
|
||||||
BigDecimal diff = totalInterestDueForLoan.getAmount().subtract(totalCumulativeInterestToDate.getAmount()).add(interestForThisPeriod.getAmount());
|
adjusted = interestForThisPeriod.minus(totalInterestRemaining.abs());
|
||||||
if(diff.doubleValue()<0){
|
|
||||||
diff = BigDecimal.ZERO;
|
|
||||||
}
|
|
||||||
adjusted = adjusted.plus(Money.of(monetaryCurrency,diff));
|
|
||||||
} else if (isLastRepaymentPeriod(this.numberOfRepayments, periodNumber)) {
|
} else if (isLastRepaymentPeriod(this.numberOfRepayments, periodNumber)) {
|
||||||
final Money interestDifference = totalCumulativeInterestToDate.minus(totalInterestDueForLoan);
|
final Money interestDifference = totalCumulativeInterestToDate.minus(totalInterestDueForLoan);
|
||||||
if (interestDifference.isLessThanZero()) {
|
if (interestDifference.isLessThanZero()) {
|
||||||
@@ -223,7 +214,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) {
|
||||||
|
|
||||||
@@ -270,7 +261,7 @@ public final class LoanApplicationTerms {
|
|||||||
switch (this.amortizationMethod) {
|
switch (this.amortizationMethod) {
|
||||||
case EQUAL_INSTALLMENTS:
|
case EQUAL_INSTALLMENTS:
|
||||||
// equal installments
|
// equal installments
|
||||||
int periodsElapsed = periodNumber - 1;
|
final int periodsElapsed = periodNumber - 1;
|
||||||
final BigDecimal periodicInterestRateForRepaymentPeriod = periodicInterestRate(calculator, mc);
|
final BigDecimal periodicInterestRateForRepaymentPeriod = periodicInterestRate(calculator, mc);
|
||||||
|
|
||||||
final Money totalPmtForThisInstallment = calculateTotalDueForEqualInstallmentRepaymentPeriod(
|
final Money totalPmtForThisInstallment = calculateTotalDueForEqualInstallmentRepaymentPeriod(
|
||||||
@@ -313,7 +304,7 @@ public final class LoanApplicationTerms {
|
|||||||
case EQUAL_PRINCIPAL:
|
case EQUAL_PRINCIPAL:
|
||||||
// interest follows time-value of money and is brought
|
// interest follows time-value of money and is brought
|
||||||
// forward to next applicable interest payment period
|
// forward to next applicable interest payment period
|
||||||
PrincipalInterest result = calculateTotalFlatInterestForPeriod(calculator, periodNumber, mc,
|
final PrincipalInterest result = calculateTotalFlatInterestForPeriod(calculator, periodNumber, mc,
|
||||||
interestBroughtForwardDueToGrace);
|
interestBroughtForwardDueToGrace);
|
||||||
interestForInstallment = result.interest();
|
interestForInstallment = result.interest();
|
||||||
interestBroughtForwardDueToGrace = result.interestPaymentDueToGrace();
|
interestBroughtForwardDueToGrace = result.interestPaymentDueToGrace();
|
||||||
@@ -324,10 +315,10 @@ public final class LoanApplicationTerms {
|
|||||||
break;
|
break;
|
||||||
case DECLINING_BALANCE:
|
case DECLINING_BALANCE:
|
||||||
|
|
||||||
Money interestForThisInstallmentBeforeGrace = calculateDecliningInterestDueForInstallmentBeforeApplyingGrace(calculator,
|
final Money interestForThisInstallmentBeforeGrace = calculateDecliningInterestDueForInstallmentBeforeApplyingGrace(calculator,
|
||||||
mc, daysInPeriod, outstandingBalance);
|
mc, daysInPeriod, outstandingBalance);
|
||||||
|
|
||||||
Money interestForThisInstallmentAfterGrace = calculateDecliningInterestDueForInstallmentAfterApplyingGrace(calculator,
|
final Money interestForThisInstallmentAfterGrace = calculateDecliningInterestDueForInstallmentAfterApplyingGrace(calculator,
|
||||||
interestCalculationGraceOnRepaymentPeriodFraction, mc, daysInPeriod, outstandingBalance, periodNumber);
|
interestCalculationGraceOnRepaymentPeriodFraction, mc, daysInPeriod, outstandingBalance, periodNumber);
|
||||||
|
|
||||||
interestForInstallment = interestForThisInstallmentAfterGrace;
|
interestForInstallment = interestForThisInstallmentAfterGrace;
|
||||||
@@ -359,7 +350,7 @@ public final class LoanApplicationTerms {
|
|||||||
final double paymentPerRepaymentPeriod = paymentPerPeriod(periodicInterestRate, this.principal, periodsElapsed);
|
final double paymentPerRepaymentPeriod = paymentPerPeriod(periodicInterestRate, this.principal, periodsElapsed);
|
||||||
final double totalRepayment = paymentPerRepaymentPeriod * this.numberOfRepayments;
|
final double totalRepayment = paymentPerRepaymentPeriod * this.numberOfRepayments;
|
||||||
|
|
||||||
return Money.of(principal.getCurrency(), BigDecimal.valueOf(totalRepayment));
|
return Money.of(this.principal.getCurrency(), BigDecimal.valueOf(totalRepayment));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -448,12 +439,12 @@ public final class LoanApplicationTerms {
|
|||||||
|
|
||||||
final Money totalInterestForLoanTerm = calculateTotalInterestDueWithoutGrace(calculator, mc);
|
final Money totalInterestForLoanTerm = calculateTotalInterestDueWithoutGrace(calculator, mc);
|
||||||
|
|
||||||
Money interestPerGracePeriod = calculateTotalInterestPerInstallmentWithoutGrace(calculator, mc);
|
final Money interestPerGracePeriod = calculateTotalInterestPerInstallmentWithoutGrace(calculator, mc);
|
||||||
|
|
||||||
Money totalInterestFree = interestPerGracePeriod.multipliedBy(getInterestChargingGrace());
|
final Money totalInterestFree = interestPerGracePeriod.multipliedBy(getInterestChargingGrace());
|
||||||
Money realTotalInterestForLoan = totalInterestForLoanTerm.minus(totalInterestFree);
|
final Money realTotalInterestForLoan = totalInterestForLoanTerm.minus(totalInterestFree);
|
||||||
|
|
||||||
Integer interestPaymentDuePeriods = calculateNumberOfRepaymentPeriodsWhereInterestPaymentIsDue(this.numberOfRepayments);
|
final Integer interestPaymentDuePeriods = calculateNumberOfRepaymentPeriodsWhereInterestPaymentIsDue(this.numberOfRepayments);
|
||||||
|
|
||||||
interestForInstallment = realTotalInterestForLoan
|
interestForInstallment = realTotalInterestForLoan
|
||||||
.dividedBy(BigDecimal.valueOf(interestPaymentDuePeriods), mc.getRoundingMode());
|
.dividedBy(BigDecimal.valueOf(interestPaymentDuePeriods), mc.getRoundingMode());
|
||||||
@@ -548,10 +539,10 @@ public final class LoanApplicationTerms {
|
|||||||
|
|
||||||
switch (this.interestCalculationPeriodMethod) {
|
switch (this.interestCalculationPeriodMethod) {
|
||||||
case DAILY:
|
case DAILY:
|
||||||
BigDecimal dailyInterestRate = this.annualNominalInterestRate.divide(BigDecimal.valueOf(Long.valueOf(365)), mc)
|
final BigDecimal dailyInterestRate = this.annualNominalInterestRate.divide(BigDecimal.valueOf(Long.valueOf(365)), mc)
|
||||||
.divide(BigDecimal.valueOf(Double.valueOf("100.0")), mc).multiply(BigDecimal.valueOf(this.repaymentEvery));
|
.divide(BigDecimal.valueOf(Double.valueOf("100.0")), mc).multiply(BigDecimal.valueOf(this.repaymentEvery));
|
||||||
|
|
||||||
BigDecimal equivalentInterestRateForPeriod = dailyInterestRate.multiply(BigDecimal.valueOf(Long.valueOf(daysInPeriod)));
|
final BigDecimal equivalentInterestRateForPeriod = dailyInterestRate.multiply(BigDecimal.valueOf(Long.valueOf(daysInPeriod)));
|
||||||
|
|
||||||
interestDue = outstandingBalance.multiplyRetainScale(equivalentInterestRateForPeriod, mc.getRoundingMode());
|
interestDue = outstandingBalance.multiplyRetainScale(equivalentInterestRateForPeriod, mc.getRoundingMode());
|
||||||
break;
|
break;
|
||||||
@@ -589,7 +580,7 @@ public final class LoanApplicationTerms {
|
|||||||
} else if (interestCalculationGraceOnRepaymentPeriodFraction > Double.valueOf("0.25")
|
} else if (interestCalculationGraceOnRepaymentPeriodFraction > Double.valueOf("0.25")
|
||||||
&& interestCalculationGraceOnRepaymentPeriodFraction < Integer.valueOf(1).doubleValue()) {
|
&& interestCalculationGraceOnRepaymentPeriodFraction < Integer.valueOf(1).doubleValue()) {
|
||||||
|
|
||||||
Money graceOnInterestForRepaymentPeriod = interest.multipliedBy(interestCalculationGraceOnRepaymentPeriodFraction);
|
final Money graceOnInterestForRepaymentPeriod = interest.multipliedBy(interestCalculationGraceOnRepaymentPeriodFraction);
|
||||||
interest = interest.minus(graceOnInterestForRepaymentPeriod);
|
interest = interest.minus(graceOnInterestForRepaymentPeriod);
|
||||||
fraction = Double.valueOf("0");
|
fraction = Double.valueOf("0");
|
||||||
}
|
}
|
||||||
@@ -616,7 +607,7 @@ public final class LoanApplicationTerms {
|
|||||||
private Money calculatePrincipalDueForInstallment(final PaymentPeriodsInOneYearCalculator calculator, final MathContext mc,
|
private Money calculatePrincipalDueForInstallment(final PaymentPeriodsInOneYearCalculator calculator, final MathContext mc,
|
||||||
final int periodNumber, final Money totalDuePerInstallment, final int daysInPeriod, final Money outstandingBalance) {
|
final int periodNumber, final Money totalDuePerInstallment, final int daysInPeriod, final Money outstandingBalance) {
|
||||||
|
|
||||||
Money interestForThisInstallmentBeforeGrace = calculateDecliningInterestDueForInstallmentBeforeApplyingGrace(calculator, mc,
|
final Money interestForThisInstallmentBeforeGrace = calculateDecliningInterestDueForInstallmentBeforeApplyingGrace(calculator, mc,
|
||||||
daysInPeriod, outstandingBalance);
|
daysInPeriod, outstandingBalance);
|
||||||
|
|
||||||
Money principal = totalDuePerInstallment.minus(interestForThisInstallmentBeforeGrace);
|
Money principal = totalDuePerInstallment.minus(interestForThisInstallmentBeforeGrace);
|
||||||
|
|||||||
Reference in New Issue
Block a user