org.mifosplatform.portfolio.loanaccount.loanschedule.domain.DecliningBalanceInterestLoanScheduleGenerator.java Source code

Java tutorial

Introduction

Here is the source code for org.mifosplatform.portfolio.loanaccount.loanschedule.domain.DecliningBalanceInterestLoanScheduleGenerator.java

Source

/**
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/.
 */
package org.mifosplatform.portfolio.loanaccount.loanschedule.domain;

import java.math.MathContext;
import java.util.Map;
import java.util.TreeMap;

import org.joda.time.Days;
import org.joda.time.LocalDate;
import org.mifosplatform.infrastructure.core.service.DateUtils;
import org.mifosplatform.organisation.monetary.domain.Money;
import org.mifosplatform.portfolio.loanproduct.domain.AmortizationMethod;

/**
 * <p>
 * Declining balance can be amortized (see {@link AmortizationMethod}) in two
 * ways at present:
 * <ol>
 * <li>Equal principal payments</li>
 * <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
 * <code>pmt</code> function. The <b>interest due</b> is calculated from the
 * <b>outstanding principal balance</b> which results in a <b>principal
 * component</b> that is <b>total payment due</b> minus <b>interest due</b>.
 * </p>
 */
public class DecliningBalanceInterestLoanScheduleGenerator extends AbstractLoanScheduleGenerator {

    @Override
    public PrincipalInterest calculatePrincipalInterestComponentsForPeriod(
            final PaymentPeriodsInOneYearCalculator calculator,
            final double interestCalculationGraceOnRepaymentPeriodFraction, final Money totalCumulativePrincipal,
            @SuppressWarnings("unused") final Money totalCumulativeInterest,
            @SuppressWarnings("unused") final Money totalInterestDueForLoan,
            final Money cumulatingInterestPaymentDueToGrace, final Money outstandingBalance,
            final LoanApplicationTerms loanApplicationTerms, final int periodNumber, final MathContext mc,
            final TreeMap<LocalDate, Money> principalVariation, final Map<LocalDate, Money> compoundingMap,
            final LocalDate periodStartDate, final LocalDate periodEndDate, final int daysForInterestInFullPeriod) {

        LocalDate interestStartDate = periodStartDate;
        Money interestForThisInstallment = totalCumulativePrincipal.zero();
        Money compoundedMoney = totalCumulativePrincipal.zero();
        Money compoundedInterest = totalCumulativePrincipal.zero();
        Money balanceForInterestCalculation = outstandingBalance;
        Money cumulatingInterestDueToGrace = cumulatingInterestPaymentDueToGrace;
        final int daysInPeriodApplicableForInterest = Days.daysBetween(periodStartDate, periodEndDate).getDays();
        if (principalVariation != null) {
            // identifies rest date after current date for reducing all
            // compounding
            // values
            LocalDate compoundingEndDate = principalVariation.ceilingKey(DateUtils.getLocalDateOfTenant());
            if (compoundingEndDate == null) {
                compoundingEndDate = DateUtils.getLocalDateOfTenant();
            }

            for (Map.Entry<LocalDate, Money> principal : principalVariation.entrySet()) {

                if (!principal.getKey().isAfter(periodEndDate)) {
                    int interestForDays = Days.daysBetween(interestStartDate, principal.getKey()).getDays();
                    if (interestForDays > 0) {
                        final PrincipalInterest result = loanApplicationTerms.calculateTotalInterestForPeriod(
                                calculator, interestCalculationGraceOnRepaymentPeriodFraction, periodNumber, mc,
                                cumulatingInterestDueToGrace, interestForDays, balanceForInterestCalculation);
                        if (loanApplicationTerms.getInterestCalculationPeriodMethod().isDaily()) {
                            interestForThisInstallment = interestForThisInstallment.plus(result.interest());
                            cumulatingInterestDueToGrace = result.interestPaymentDueToGrace();
                        } else {
                            interestForThisInstallment = interestForThisInstallment.plus(calculateInterestForDays(
                                    daysForInterestInFullPeriod, result.interest().getAmount(), interestForDays));
                            cumulatingInterestDueToGrace = cumulatingInterestDueToGrace
                                    .plus(calculateInterestForDays(
                                            daysForInterestInFullPeriod, result.interestPaymentDueToGrace()
                                                    .minus(cumulatingInterestDueToGrace).getAmount(),
                                            interestForDays));
                        }

                        interestStartDate = principal.getKey();
                    }
                    Money compoundFee = totalCumulativePrincipal.zero();
                    if (compoundingMap.containsKey(principal.getKey())) {
                        Money interestToBeCompounded = totalCumulativePrincipal.zero();
                        // for interest compounding
                        if (loanApplicationTerms.getInterestRecalculationCompoundingMethod()
                                .isInterestCompoundingEnabled()) {
                            interestToBeCompounded = interestForThisInstallment.minus(compoundedInterest);
                            balanceForInterestCalculation = balanceForInterestCalculation
                                    .plus(interestToBeCompounded);
                            compoundedInterest = interestForThisInstallment;
                        }
                        // fee compounding will be done after calculation
                        compoundFee = compoundingMap.get(principal.getKey());
                        compoundedMoney = compoundedMoney.plus(interestToBeCompounded).plus(compoundFee);
                    }
                    balanceForInterestCalculation = balanceForInterestCalculation.plus(principal.getValue())
                            .plus(compoundFee);
                }

            }
            if (!periodEndDate.isBefore(compoundingEndDate)) {
                balanceForInterestCalculation = balanceForInterestCalculation.minus(compoundedMoney);
                compoundingMap.clear();
            } else if (compoundedMoney.isGreaterThanZero()) {
                compoundingMap.put(periodEndDate, compoundedMoney);
                compoundingMap.put(compoundingEndDate, compoundedMoney.negated());
                clearMapDetails(periodEndDate, compoundingMap);
            }
        }
        int interestForDays = Days.daysBetween(interestStartDate, periodEndDate).getDays();

        final PrincipalInterest result = loanApplicationTerms.calculateTotalInterestForPeriod(calculator,
                interestCalculationGraceOnRepaymentPeriodFraction, periodNumber, mc, cumulatingInterestDueToGrace,
                interestForDays, balanceForInterestCalculation);
        if (loanApplicationTerms.getInterestCalculationPeriodMethod().isDaily()) {
            interestForThisInstallment = interestForThisInstallment.plus(result.interest());
            cumulatingInterestDueToGrace = result.interestPaymentDueToGrace();
        } else {
            interestForThisInstallment = interestForThisInstallment.plus(calculateInterestForDays(
                    daysForInterestInFullPeriod, result.interest().getAmount(), interestForDays));
            cumulatingInterestDueToGrace = cumulatingInterestDueToGrace
                    .plus(calculateInterestForDays(daysForInterestInFullPeriod,
                            result.interestPaymentDueToGrace().minus(cumulatingInterestDueToGrace).getAmount(),
                            interestForDays));
        }

        Money interestForPeriod = interestForThisInstallment;
        if (interestForPeriod.isGreaterThanZero()) {
            interestForPeriod = interestForPeriod.minus(cumulatingInterestPaymentDueToGrace);
        } else {
            interestForPeriod = cumulatingInterestDueToGrace.minus(cumulatingInterestPaymentDueToGrace);
        }
        Money principalForThisInstallment = loanApplicationTerms.calculateTotalPrincipalForPeriod(calculator,
                daysInPeriodApplicableForInterest, outstandingBalance, periodNumber, mc, interestForPeriod);

        // update cumulative fields for principal & interest
        final Money interestBroughtFowardDueToGrace = cumulatingInterestDueToGrace;
        final Money totalCumulativePrincipalToDate = totalCumulativePrincipal.plus(principalForThisInstallment);

        // adjust if needed
        principalForThisInstallment = loanApplicationTerms.adjustPrincipalIfLastRepaymentPeriod(
                principalForThisInstallment, totalCumulativePrincipalToDate, periodNumber);

        return new PrincipalInterest(principalForThisInstallment, interestForThisInstallment,
                interestBroughtFowardDueToGrace);
    }
}