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

Java tutorial

Introduction

Here is the source code for org.mifosplatform.portfolio.loanaccount.loanschedule.domain.FlatMethodLoanScheduleGenerator.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.BigDecimal;
import java.math.MathContext;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Set;

import org.joda.time.Days;
import org.joda.time.LocalDate;
import org.mifosplatform.organisation.monetary.data.CurrencyData;
import org.mifosplatform.organisation.monetary.domain.ApplicationCurrency;
import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency;
import org.mifosplatform.organisation.monetary.domain.Money;
import org.mifosplatform.portfolio.loanaccount.domain.LoanCharge;
import org.mifosplatform.portfolio.loanaccount.loanschedule.data.LoanScheduleData;
import org.mifosplatform.portfolio.loanaccount.loanschedule.data.LoanSchedulePeriodData;
import org.mifosplatform.portfolio.loanproduct.domain.LoanProductRelatedDetail;
import org.mifosplatform.portfolio.loanproduct.domain.PeriodFrequencyType;

public class FlatMethodLoanScheduleGenerator implements LoanScheduleGenerator {

    private final ScheduledDateGenerator scheduledDateGenerator = new DefaultScheduledDateGenerator();
    private final PaymentPeriodsInOneYearCalculator paymentPeriodsInOneYearCalculator = new DefaultPaymentPeriodsInOneYearCalculator();

    @Override
    public LoanScheduleData generate(final ApplicationCurrency currency,
            final LoanProductRelatedDetail loanScheduleInfo, final Integer loanTermFrequency,
            final PeriodFrequencyType loanTermFrequencyType, final LocalDate disbursementDate,
            final LocalDate firstRepaymentDate, final LocalDate interestCalculatedFrom,
            final Set<LoanCharge> loanCharges) {

        final Collection<LoanSchedulePeriodData> periods = new ArrayList<LoanSchedulePeriodData>();

        final List<LocalDate> scheduledDates = this.scheduledDateGenerator.generate(loanScheduleInfo,
                disbursementDate, firstRepaymentDate);

        MathContext mc = new MathContext(8, RoundingMode.HALF_EVEN);

        BigDecimal loanTermPeriodsInYear = BigDecimal
                .valueOf(this.paymentPeriodsInOneYearCalculator.calculate(loanTermFrequencyType));
        BigDecimal interestRateForLoanTerm = loanScheduleInfo.getAnnualNominalInterestRate()
                .divide(loanTermPeriodsInYear, mc).divide(BigDecimal.valueOf(Double.valueOf("100.0")), mc)
                .multiply(BigDecimal.valueOf(loanTermFrequency));

        final MonetaryCurrency monetaryCurrency = loanScheduleInfo.getPrincipal().getCurrency();
        Money totalInterestForLoanTerm = loanScheduleInfo.getPrincipal()
                .multiplyRetainScale(interestRateForLoanTerm, RoundingMode.HALF_EVEN);

        Money interestPerInstallment = totalInterestForLoanTerm
                .dividedBy(Long.valueOf(loanScheduleInfo.getNumberOfRepayments()), RoundingMode.HALF_EVEN);

        Money principalPerInstallment = loanScheduleInfo.getPrincipal()
                .dividedBy(loanScheduleInfo.getNumberOfRepayments(), RoundingMode.HALF_EVEN);

        Money outstandingBalance = loanScheduleInfo.getPrincipal();
        Money principalDisbursed = loanScheduleInfo.getPrincipal();
        Money totalPrincipal = Money.zero(outstandingBalance.getCurrency());
        Money totalInterest = Money.zero(outstandingBalance.getCurrency());

        BigDecimal chargesDueAtTimeOfDisbursement = BigDecimal.ZERO;
        for (LoanCharge loanCharge : loanCharges) {
            if (loanCharge.isDueAtDisbursement()) {
                chargesDueAtTimeOfDisbursement = chargesDueAtTimeOfDisbursement.add(loanCharge.amount());
            }
        }

        // create entries of disbursement period on loan schedule
        final LoanSchedulePeriodData disbursementPeriod = LoanSchedulePeriodData.disbursementOnlyPeriod(
                disbursementDate, principalDisbursed.getAmount(), chargesDueAtTimeOfDisbursement, false);
        periods.add(disbursementPeriod);

        int loanTermInDays = Integer.valueOf(0);
        BigDecimal totalPrincipalDisbursed = principalDisbursed.getAmount();
        BigDecimal totalPrincipalExpected = BigDecimal.ZERO;
        BigDecimal totalPrincipalPaid = BigDecimal.ZERO;
        BigDecimal totalInterestCharged = BigDecimal.ZERO;
        BigDecimal totalFeeChargesCharged = chargesDueAtTimeOfDisbursement;
        BigDecimal totalPenaltyChargesCharged = BigDecimal.ZERO;
        BigDecimal totalWaived = BigDecimal.ZERO;
        BigDecimal totalWrittenOff = BigDecimal.ZERO;
        BigDecimal totalRepaymentExpected = chargesDueAtTimeOfDisbursement;
        BigDecimal totalRepayment = BigDecimal.ZERO;
        BigDecimal totalOutstanding = BigDecimal.ZERO;

        LocalDate startDate = disbursementDate;
        int periodNumber = 1;
        for (LocalDate scheduledDueDate : scheduledDates) {
            totalPrincipal = totalPrincipal.plus(principalPerInstallment);
            totalInterest = totalInterest.plus(interestPerInstallment);

            // number of days from startDate to this scheduledDate
            int daysInPeriod = Days.daysBetween(startDate.toDateMidnight().toDateTime(),
                    scheduledDueDate.toDateMidnight().toDateTime()).getDays();

            if (periodNumber == loanScheduleInfo.getNumberOfRepayments()) {
                final Money difference = totalPrincipal.minus(loanScheduleInfo.getPrincipal());
                if (difference.isLessThanZero()) {
                    principalPerInstallment = principalPerInstallment.plus(difference.abs());
                } else if (difference.isGreaterThanZero()) {
                    principalPerInstallment = principalPerInstallment.minus(difference.abs());
                }

                final Money interestDifference = totalInterest.minus(totalInterestForLoanTerm);
                if (interestDifference.isLessThanZero()) {
                    interestPerInstallment = interestPerInstallment.plus(interestDifference.abs());
                } else if (interestDifference.isGreaterThanZero()) {
                    interestPerInstallment = interestPerInstallment.minus(interestDifference.abs());
                }
            }

            outstandingBalance = outstandingBalance.minus(principalPerInstallment);

            final Money feeChargesForInstallment = cumulativeFeeChargesDueWithin(startDate, scheduledDueDate,
                    loanCharges, monetaryCurrency);
            final Money penaltyChargesForInstallment = cumulativePenaltyChargesDueWithin(startDate,
                    scheduledDueDate, loanCharges, monetaryCurrency);

            final Money totalInstallmentDue = principalPerInstallment //
                    .plus(interestPerInstallment) //
                    .plus(feeChargesForInstallment) //
                    .plus(penaltyChargesForInstallment);

            LoanSchedulePeriodData installment = LoanSchedulePeriodData.repaymentOnlyPeriod(periodNumber, startDate,
                    scheduledDueDate, principalPerInstallment.getAmount(), outstandingBalance.getAmount(),
                    interestPerInstallment.getAmount(), feeChargesForInstallment.getAmount(),
                    penaltyChargesForInstallment.getAmount(), totalInstallmentDue.getAmount());

            periods.add(installment);

            // handle cumulative fields
            loanTermInDays += daysInPeriod;
            totalPrincipalExpected = totalPrincipalExpected.add(principalPerInstallment.getAmount());
            totalInterestCharged = totalInterestCharged.add(interestPerInstallment.getAmount());
            totalFeeChargesCharged = totalFeeChargesCharged.add(feeChargesForInstallment.getAmount());
            totalPenaltyChargesCharged = totalPenaltyChargesCharged.add(penaltyChargesForInstallment.getAmount());
            totalRepaymentExpected = totalRepaymentExpected.add(totalInstallmentDue.getAmount());
            startDate = scheduledDueDate;

            periodNumber++;
        }

        final CurrencyData currencyData = new CurrencyData(currency.getCode(), currency.getName(),
                monetaryCurrency.getDigitsAfterDecimal(), currency.getDisplaySymbol(), currency.getNameCode());

        return new LoanScheduleData(currencyData, periods, loanTermInDays, totalPrincipalDisbursed,
                totalPrincipalExpected, totalPrincipalPaid, totalInterestCharged, totalFeeChargesCharged,
                totalPenaltyChargesCharged, totalWaived, totalWrittenOff, totalRepaymentExpected, totalRepayment,
                totalOutstanding);
    }

    private Money cumulativeFeeChargesDueWithin(final LocalDate periodStart, final LocalDate periodEnd,
            final Set<LoanCharge> loanCharges, final MonetaryCurrency monetaryCurrency) {

        Money cumulative = Money.zero(monetaryCurrency);

        for (LoanCharge loanCharge : loanCharges) {
            if (loanCharge.isDueForCollectionFromAndUpToAndIncluding(periodStart, periodEnd)
                    && loanCharge.isFeeCharge()) {
                cumulative = cumulative.plus(loanCharge.amount());
            }
        }

        return cumulative;
    }

    private Money cumulativePenaltyChargesDueWithin(final LocalDate periodStart, final LocalDate periodEnd,
            final Set<LoanCharge> loanCharges, final MonetaryCurrency monetaryCurrency) {

        Money cumulative = Money.zero(monetaryCurrency);

        for (LoanCharge loanCharge : loanCharges) {
            if (loanCharge.isDueForCollectionFromAndUpToAndIncluding(periodStart, periodEnd)
                    && loanCharge.isPenaltyCharge()) {
                cumulative = cumulative.plus(loanCharge.amount());
            }
        }

        return cumulative;
    }
}