You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@fineract.apache.org by ra...@apache.org on 2016/05/04 14:26:16 UTC
[04/10] incubator-fineract git commit: FINERACT-60 : Interest
compounding, nth day rest frequency and meeting calendar date changes
http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/763cf18b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/LoanApplicationTerms.java
----------------------------------------------------------------------
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/LoanApplicationTerms.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/LoanApplicationTerms.java
old mode 100755
new mode 100644
index 7be11d3..c1f91ea
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/LoanApplicationTerms.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/LoanApplicationTerms.java
@@ -30,12 +30,14 @@ import org.apache.fineract.organisation.monetary.domain.MoneyHelper;
import org.apache.fineract.portfolio.calendar.data.CalendarHistoryDataWrapper;
import org.apache.fineract.portfolio.calendar.domain.Calendar;
import org.apache.fineract.portfolio.calendar.domain.CalendarInstance;
+import org.apache.fineract.portfolio.calendar.service.CalendarUtils;
import org.apache.fineract.portfolio.common.domain.DayOfWeekType;
import org.apache.fineract.portfolio.common.domain.DaysInMonthType;
import org.apache.fineract.portfolio.common.domain.DaysInYearType;
import org.apache.fineract.portfolio.common.domain.NthDayType;
import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType;
import org.apache.fineract.portfolio.loanaccount.data.DisbursementData;
+import org.apache.fineract.portfolio.loanaccount.data.HolidayDetailDTO;
import org.apache.fineract.portfolio.loanaccount.data.LoanTermVariationsData;
import org.apache.fineract.portfolio.loanaccount.data.LoanTermVariationsDataWrapper;
import org.apache.fineract.portfolio.loanaccount.domain.LoanInterestRecalculationDetails;
@@ -57,1864 +59,1521 @@ import org.joda.time.Years;
public final class LoanApplicationTerms {
- private final ApplicationCurrency currency;
+ private final ApplicationCurrency currency;
- private final Calendar loanCalendar;
- private Integer loanTermFrequency;
- private final PeriodFrequencyType loanTermPeriodFrequencyType;
- private Integer numberOfRepayments;
- private Integer actualNumberOfRepayments;
- private final Integer repaymentEvery;
- private final PeriodFrequencyType repaymentPeriodFrequencyType;
- private final Integer nthDay;
+ private final Calendar loanCalendar;
+ private Integer loanTermFrequency;
+ private final PeriodFrequencyType loanTermPeriodFrequencyType;
+ private Integer numberOfRepayments;
+ private Integer actualNumberOfRepayments;
+ private final Integer repaymentEvery;
+ private final PeriodFrequencyType repaymentPeriodFrequencyType;
+ private final Integer nthDay;
- private final DayOfWeekType weekDayType;
- private final AmortizationMethod amortizationMethod;
+ private final DayOfWeekType weekDayType;
+ private final AmortizationMethod amortizationMethod;
- private final InterestMethod interestMethod;
- private BigDecimal interestRatePerPeriod;
- private final PeriodFrequencyType interestRatePeriodFrequencyType;
- private BigDecimal annualNominalInterestRate;
- private final InterestCalculationPeriodMethod interestCalculationPeriodMethod;
- private final boolean allowPartialPeriodInterestCalcualtion;
+ private final InterestMethod interestMethod;
+ private BigDecimal interestRatePerPeriod;
+ private final PeriodFrequencyType interestRatePeriodFrequencyType;
+ private BigDecimal annualNominalInterestRate;
+ private final InterestCalculationPeriodMethod interestCalculationPeriodMethod;
+ private final boolean allowPartialPeriodInterestCalcualtion;
- private Money principal;
- private final LocalDate expectedDisbursementDate;
- private final LocalDate repaymentsStartingFromDate;
- private final LocalDate calculatedRepaymentsStartingFromDate;
- /**
- * Integer representing the number of 'repayment frequencies' or
- * installments where 'grace' should apply to the principal component of a
- * loans repayment period (installment).
- */
- private Integer principalGrace;
- private Integer recurringMoratoriumOnPrincipalPeriods;
+ private Money principal;
+ private final LocalDate expectedDisbursementDate;
+ private final LocalDate repaymentsStartingFromDate;
+ private final LocalDate calculatedRepaymentsStartingFromDate;
+ /**
+ * Integer representing the number of 'repayment frequencies' or
+ * installments where 'grace' should apply to the principal component of a
+ * loans repayment period (installment).
+ */
+ private Integer principalGrace;
+ private Integer recurringMoratoriumOnPrincipalPeriods;
- /**
- * Integer representing the number of 'repayment frequencies' or
- * installments where 'grace' should apply to the payment of interest in a
- * loans repayment period (installment).
- *
- * <b>Note:</b> Interest is still calculated taking into account the full
- * loan term, the interest is simply offset to a later period.
- */
- private Integer interestPaymentGrace;
+ /**
+ * Integer representing the number of 'repayment frequencies' or
+ * installments where 'grace' should apply to the payment of interest in a
+ * loans repayment period (installment).
+ *
+ * <b>Note:</b> Interest is still calculated taking into account the full
+ * loan term, the interest is simply offset to a later period.
+ */
+ private Integer interestPaymentGrace;
- /**
- * Integer representing the number of 'repayment frequencies' or
- * installments where 'grace' should apply to the charging of interest in a
- * loans repayment period (installment).
- *
- * <b>Note:</b> The loan is <i>interest-free</i> for the period of time
- * indicated.
- */
- private final Integer interestChargingGrace;
+ /**
+ * Integer representing the number of 'repayment frequencies' or
+ * installments where 'grace' should apply to the charging of interest in a
+ * loans repayment period (installment).
+ *
+ * <b>Note:</b> The loan is <i>interest-free</i> for the period of time
+ * indicated.
+ */
+ private final Integer interestChargingGrace;
- /**
- * Legacy method of support 'grace' on the charging of interest on a loan.
- *
- * <p>
- * For the typical structured loan, its reasonable to use an integer to
- * indicate the number of 'repayment frequency' periods the 'grace' should
- * apply to but for slightly <b>irregular</b> loans where the period between
- * disbursement and the date of the 'first repayment period' isnt doest
- * match the 'repayment frequency' but can be less (15days instead of 1
- * month) or more (6 weeks instead of 1 month) - The idea was to use a date
- * to indicate from whence interest should be charged.
- * </p>
- */
- private LocalDate interestChargedFromDate;
- private final Money inArrearsTolerance;
-
- private final Integer graceOnArrearsAgeing;
-
- // added
- private LocalDate loanEndDate;
-
- private final List<DisbursementData> disbursementDatas;
-
- private final boolean multiDisburseLoan;
-
- private BigDecimal fixedEmiAmount;
-
- private BigDecimal fixedPrincipalAmount;
-
- private BigDecimal currentPeriodFixedEmiAmount;
-
- private BigDecimal currentPeriodFixedPrincipalAmount;
-
- private final BigDecimal actualFixedEmiAmount;
-
- private final BigDecimal maxOutstandingBalance;
-
- private Money totalInterestDue;
-
- private final DaysInMonthType daysInMonthType;
-
- private final DaysInYearType daysInYearType;
-
- private final boolean interestRecalculationEnabled;
-
- private final LoanRescheduleStrategyMethod rescheduleStrategyMethod;
-
- private final InterestRecalculationCompoundingMethod interestRecalculationCompoundingMethod;
-
- private final CalendarInstance restCalendarInstance;
-
- private final RecalculationFrequencyType recalculationFrequencyType;
-
- private final CalendarInstance compoundingCalendarInstance;
-
- private final RecalculationFrequencyType compoundingFrequencyType;
-
- private final BigDecimal principalThresholdForLastInstalment;
- private final Integer installmentAmountInMultiplesOf;
-
- private final LoanPreClosureInterestCalculationStrategy preClosureInterestCalculationStrategy;
-
- private Money approvedPrincipal = null;
-
- private final LoanTermVariationsDataWrapper variationsDataWrapper;
-
- private Money adjustPrincipalForFlatLoans;
-
- private final LocalDate seedDate;
-
- private final CalendarHistoryDataWrapper calendarHistoryDataWrapper;
-
- private final Boolean isInterestChargedFromDateSameAsDisbursalDateEnabled;
-
- private final Integer numberOfDays;
-
- private final boolean isSkipRepaymentOnFirstDayOfMonth;
-
- public static LoanApplicationTerms assembleFrom(
- final ApplicationCurrency currency,
- final Integer loanTermFrequency,
- final PeriodFrequencyType loanTermPeriodFrequencyType,
- final Integer numberOfRepayments,
- final Integer repaymentEvery,
- final PeriodFrequencyType repaymentPeriodFrequencyType,
- Integer nthDay,
- DayOfWeekType weekDayType,
- final AmortizationMethod amortizationMethod,
- final InterestMethod interestMethod,
- final BigDecimal interestRatePerPeriod,
- final PeriodFrequencyType interestRatePeriodFrequencyType,
- final BigDecimal annualNominalInterestRate,
- final InterestCalculationPeriodMethod interestCalculationPeriodMethod,
- final boolean allowPartialPeriodInterestCalcualtion,
- final Money principalMoney,
- final LocalDate expectedDisbursementDate,
- final LocalDate repaymentsStartingFromDate,
- final LocalDate calculatedRepaymentsStartingFromDate,
- final Integer graceOnPrincipalPayment,
- final Integer recurringMoratoriumOnPrincipalPeriods,
- final Integer graceOnInterestPayment,
- final Integer graceOnInterestCharged,
- final LocalDate interestChargedFromDate,
- final Money inArrearsTolerance,
- final boolean multiDisburseLoan,
- final BigDecimal emiAmount,
- final List<DisbursementData> disbursementDatas,
- final BigDecimal maxOutstandingBalance,
- final Integer graceOnArrearsAgeing,
- final DaysInMonthType daysInMonthType,
- final DaysInYearType daysInYearType,
- final boolean isInterestRecalculationEnabled,
- final RecalculationFrequencyType recalculationFrequencyType,
- final CalendarInstance restCalendarInstance,
- final CalendarInstance compoundingCalendarInstance,
- final RecalculationFrequencyType compoundingFrequencyType,
- final BigDecimal principalThresholdForLastInstalment,
- final Integer installmentAmountInMultiplesOf,
- final LoanPreClosureInterestCalculationStrategy preClosureInterestCalculationStrategy,
- final Calendar loanCalendar, BigDecimal approvedAmount,
- List<LoanTermVariationsData> loanTermVariations,
- Boolean isInterestChargedFromDateSameAsDisbursalDateEnabled,
- final Integer numberOfdays, boolean isSkipRepaymentOnFirstDayofMonth) {
-
- final LoanRescheduleStrategyMethod rescheduleStrategyMethod = null;
- final InterestRecalculationCompoundingMethod interestRecalculationCompoundingMethod = null;
- final CalendarHistoryDataWrapper calendarHistoryDataWrapper = null;
- return new LoanApplicationTerms(currency, loanTermFrequency,
- loanTermPeriodFrequencyType, numberOfRepayments,
- repaymentEvery, repaymentPeriodFrequencyType, nthDay,
- weekDayType, amortizationMethod, interestMethod,
- interestRatePerPeriod, interestRatePeriodFrequencyType,
- annualNominalInterestRate, interestCalculationPeriodMethod,
- allowPartialPeriodInterestCalcualtion, principalMoney,
- expectedDisbursementDate, repaymentsStartingFromDate,
- calculatedRepaymentsStartingFromDate, graceOnPrincipalPayment,
- recurringMoratoriumOnPrincipalPeriods, graceOnInterestPayment,
- graceOnInterestCharged, interestChargedFromDate,
- inArrearsTolerance, multiDisburseLoan, emiAmount,
- disbursementDatas, maxOutstandingBalance, graceOnArrearsAgeing,
- daysInMonthType, daysInYearType,
- isInterestRecalculationEnabled, rescheduleStrategyMethod,
- interestRecalculationCompoundingMethod, restCalendarInstance,
- recalculationFrequencyType, compoundingCalendarInstance,
- compoundingFrequencyType, principalThresholdForLastInstalment,
- installmentAmountInMultiplesOf,
- preClosureInterestCalculationStrategy, loanCalendar,
- approvedAmount, loanTermVariations, calendarHistoryDataWrapper,
- isInterestChargedFromDateSameAsDisbursalDateEnabled,
- numberOfdays, isSkipRepaymentOnFirstDayofMonth);
-
- }
-
- public static LoanApplicationTerms assembleFrom(
- final ApplicationCurrency applicationCurrency,
- final Integer loanTermFrequency,
- final PeriodFrequencyType loanTermPeriodFrequencyType,
- NthDayType nthDay,
- DayOfWeekType dayOfWeek,
- final LocalDate expectedDisbursementDate,
- final LocalDate repaymentsStartingFromDate,
- final LocalDate calculatedRepaymentsStartingFromDate,
- final Money inArrearsTolerance,
- final LoanProductRelatedDetail loanProductRelatedDetail,
- final boolean multiDisburseLoan,
- final BigDecimal emiAmount,
- final List<DisbursementData> disbursementDatas,
- final BigDecimal maxOutstandingBalance,
- final LocalDate interestChargedFromDate,
- final BigDecimal principalThresholdForLastInstalment,
- final Integer installmentAmountInMultiplesOf,
- final RecalculationFrequencyType recalculationFrequencyType,
- final CalendarInstance restCalendarInstance,
- final InterestRecalculationCompoundingMethod compoundingMethod,
- final CalendarInstance compoundingCalendarInstance,
- final RecalculationFrequencyType compoundingFrequencyType,
- final LoanPreClosureInterestCalculationStrategy loanPreClosureInterestCalculationStrategy,
- final LoanRescheduleStrategyMethod rescheduleStrategyMethod,
- BigDecimal approvedAmount, BigDecimal annualNominalInterestRate,
- List<LoanTermVariationsData> loanTermVariations,
- final Integer numberOfdays,
- final boolean isSkipRepaymentOnFirstDayofMonth) {
- final Calendar loanCalendar = null;
- final CalendarHistoryDataWrapper calendarHistoryDataWrapper = null;
-
- return assembleFrom(applicationCurrency, loanTermFrequency,
- loanTermPeriodFrequencyType, nthDay, dayOfWeek,
- expectedDisbursementDate, repaymentsStartingFromDate,
- calculatedRepaymentsStartingFromDate, inArrearsTolerance,
- loanProductRelatedDetail, multiDisburseLoan, emiAmount,
- disbursementDatas, maxOutstandingBalance,
- interestChargedFromDate, principalThresholdForLastInstalment,
- installmentAmountInMultiplesOf, recalculationFrequencyType,
- restCalendarInstance, compoundingMethod,
- compoundingCalendarInstance, compoundingFrequencyType,
- loanPreClosureInterestCalculationStrategy,
- rescheduleStrategyMethod, loanCalendar, approvedAmount,
- annualNominalInterestRate, loanTermVariations,
- calendarHistoryDataWrapper, numberOfdays,
- isSkipRepaymentOnFirstDayofMonth);
- }
-
- public static LoanApplicationTerms assembleFrom(
- final ApplicationCurrency applicationCurrency,
- final Integer loanTermFrequency,
- final PeriodFrequencyType loanTermPeriodFrequencyType,
- NthDayType nthDay,
- DayOfWeekType dayOfWeek,
- final LocalDate expectedDisbursementDate,
- final LocalDate repaymentsStartingFromDate,
- final LocalDate calculatedRepaymentsStartingFromDate,
- final Money inArrearsTolerance,
- final LoanProductRelatedDetail loanProductRelatedDetail,
- final boolean multiDisburseLoan,
- final BigDecimal emiAmount,
- final List<DisbursementData> disbursementDatas,
- final BigDecimal maxOutstandingBalance,
- final LocalDate interestChargedFromDate,
- final BigDecimal principalThresholdForLastInstalment,
- final Integer installmentAmountInMultiplesOf,
- final RecalculationFrequencyType recalculationFrequencyType,
- final CalendarInstance restCalendarInstance,
- final InterestRecalculationCompoundingMethod compoundingMethod,
- final CalendarInstance compoundingCalendarInstance,
- final RecalculationFrequencyType compoundingFrequencyType,
- final LoanPreClosureInterestCalculationStrategy loanPreClosureInterestCalculationStrategy,
- final LoanRescheduleStrategyMethod rescheduleStrategyMethod,
- final Calendar loanCalendar, BigDecimal approvedAmount,
- BigDecimal annualNominalInterestRate,
- final List<LoanTermVariationsData> loanTermVariations,
- final CalendarHistoryDataWrapper calendarHistoryDataWrapper,
- final Integer numberOfdays,
- final boolean isSkipRepaymentOnFirstDayofMonth) {
-
- final Integer numberOfRepayments = loanProductRelatedDetail
- .getNumberOfRepayments();
- final Integer repaymentEvery = loanProductRelatedDetail.getRepayEvery();
- final PeriodFrequencyType repaymentPeriodFrequencyType = loanProductRelatedDetail
- .getRepaymentPeriodFrequencyType();
- final AmortizationMethod amortizationMethod = loanProductRelatedDetail
- .getAmortizationMethod();
- final InterestMethod interestMethod = loanProductRelatedDetail
- .getInterestMethod();
- final BigDecimal interestRatePerPeriod = loanProductRelatedDetail
- .getNominalInterestRatePerPeriod();
- final PeriodFrequencyType interestRatePeriodFrequencyType = loanProductRelatedDetail
- .getInterestPeriodFrequencyType();
- final InterestCalculationPeriodMethod interestCalculationPeriodMethod = loanProductRelatedDetail
- .getInterestCalculationPeriodMethod();
- final boolean allowPartialPeriodInterestCalcualtion = loanProductRelatedDetail
- .isAllowPartialPeriodInterestCalcualtion();
- final Money principalMoney = loanProductRelatedDetail.getPrincipal();
-
- //
- final Integer graceOnPrincipalPayment = loanProductRelatedDetail
- .graceOnPrincipalPayment();
- final Integer recurringMoratoriumOnPrincipalPeriods = loanProductRelatedDetail
- .recurringMoratoriumOnPrincipalPeriods();
- final Integer graceOnInterestPayment = loanProductRelatedDetail
- .graceOnInterestPayment();
- final Integer graceOnInterestCharged = loanProductRelatedDetail
- .graceOnInterestCharged();
-
- // Interest recalculation settings
- final DaysInMonthType daysInMonthType = loanProductRelatedDetail
- .fetchDaysInMonthType();
- final DaysInYearType daysInYearType = loanProductRelatedDetail
- .fetchDaysInYearType();
- final boolean isInterestRecalculationEnabled = loanProductRelatedDetail
- .isInterestRecalculationEnabled();
- final boolean isInterestChargedFromDateSameAsDisbursalDateEnabled = false;
- return new LoanApplicationTerms(applicationCurrency, loanTermFrequency,
- loanTermPeriodFrequencyType, numberOfRepayments,
- repaymentEvery, repaymentPeriodFrequencyType,
- nthDay.getValue(), dayOfWeek, amortizationMethod,
- interestMethod, interestRatePerPeriod,
- interestRatePeriodFrequencyType, annualNominalInterestRate,
- interestCalculationPeriodMethod,
- allowPartialPeriodInterestCalcualtion, principalMoney,
- expectedDisbursementDate, repaymentsStartingFromDate,
- calculatedRepaymentsStartingFromDate, graceOnPrincipalPayment,
- recurringMoratoriumOnPrincipalPeriods, graceOnInterestPayment,
- graceOnInterestCharged, interestChargedFromDate,
- inArrearsTolerance, multiDisburseLoan, emiAmount,
- disbursementDatas, maxOutstandingBalance,
- loanProductRelatedDetail.getGraceOnDueDate(), daysInMonthType,
- daysInYearType, isInterestRecalculationEnabled,
- rescheduleStrategyMethod, compoundingMethod,
- restCalendarInstance, recalculationFrequencyType,
- compoundingCalendarInstance, compoundingFrequencyType,
- principalThresholdForLastInstalment,
- installmentAmountInMultiplesOf,
- loanPreClosureInterestCalculationStrategy, loanCalendar,
- approvedAmount, loanTermVariations, calendarHistoryDataWrapper,
- isInterestChargedFromDateSameAsDisbursalDateEnabled,
- numberOfdays, isSkipRepaymentOnFirstDayofMonth);
- }
-
- public static LoanApplicationTerms assembleFrom(
- final ApplicationCurrency applicationCurrency,
- final Integer loanTermFrequency,
- final PeriodFrequencyType loanTermPeriodFrequencyType,
- final LocalDate expectedDisbursementDate,
- final LocalDate repaymentsStartingFromDate,
- final LocalDate calculatedRepaymentsStartingFromDate,
- final Money inArrearsTolerance,
- final LoanProductRelatedDetail loanProductRelatedDetail,
- final boolean multiDisburseLoan,
- final BigDecimal emiAmount,
- final List<DisbursementData> disbursementDatas,
- final BigDecimal maxOutstandingBalance,
- final LocalDate interestChargedFromDate,
- final LoanInterestRecalculationDetails interestRecalculationDetails,
- final CalendarInstance restCalendarInstance,
- final RecalculationFrequencyType recalculationFrequencyType,
- final CalendarInstance compoundingCalendarInstance,
- final RecalculationFrequencyType compoundingFrequencyType,
- final BigDecimal principalThresholdForLastInstalment,
- final Integer installmentAmountInMultiplesOf,
- final LoanPreClosureInterestCalculationStrategy loanPreClosureInterestCalculationStrategy,
- final Calendar loanCalendar, BigDecimal approvedAmount,
- final BigDecimal annualNominalInterestRate,
- final List<LoanTermVariationsData> loanTermVariations,
- Integer numberOfdays, boolean isSkipRepaymentOnFirstDayofMonth) {
-
- final Integer numberOfRepayments = loanProductRelatedDetail
- .getNumberOfRepayments();
- final Integer repaymentEvery = loanProductRelatedDetail.getRepayEvery();
- final PeriodFrequencyType repaymentPeriodFrequencyType = loanProductRelatedDetail
- .getRepaymentPeriodFrequencyType();
- final AmortizationMethod amortizationMethod = loanProductRelatedDetail
- .getAmortizationMethod();
- final InterestMethod interestMethod = loanProductRelatedDetail
- .getInterestMethod();
- final BigDecimal interestRatePerPeriod = loanProductRelatedDetail
- .getNominalInterestRatePerPeriod();
- final PeriodFrequencyType interestRatePeriodFrequencyType = loanProductRelatedDetail
- .getInterestPeriodFrequencyType();
- final InterestCalculationPeriodMethod interestCalculationPeriodMethod = loanProductRelatedDetail
- .getInterestCalculationPeriodMethod();
- final boolean allowPartialPeriodInterestCalcualtion = loanProductRelatedDetail
- .isAllowPartialPeriodInterestCalcualtion();
- final Money principalMoney = loanProductRelatedDetail.getPrincipal();
-
- //
- final Integer graceOnPrincipalPayment = loanProductRelatedDetail
- .graceOnPrincipalPayment();
- final Integer recurringMoratoriumOnPrincipalPeriods = loanProductRelatedDetail
- .recurringMoratoriumOnPrincipalPeriods();
- final Integer graceOnInterestPayment = loanProductRelatedDetail
- .graceOnInterestPayment();
- final Integer graceOnInterestCharged = loanProductRelatedDetail
- .graceOnInterestCharged();
-
- // Interest recalculation settings
- final DaysInMonthType daysInMonthType = loanProductRelatedDetail
- .fetchDaysInMonthType();
- final DaysInYearType daysInYearType = loanProductRelatedDetail
- .fetchDaysInYearType();
- final boolean isInterestRecalculationEnabled = loanProductRelatedDetail
- .isInterestRecalculationEnabled();
- LoanRescheduleStrategyMethod rescheduleStrategyMethod = null;
- InterestRecalculationCompoundingMethod interestRecalculationCompoundingMethod = null;
- if (isInterestRecalculationEnabled) {
- rescheduleStrategyMethod = interestRecalculationDetails
- .getRescheduleStrategyMethod();
- interestRecalculationCompoundingMethod = interestRecalculationDetails
- .getInterestRecalculationCompoundingMethod();
- }
- final CalendarHistoryDataWrapper calendarHistoryDataWrapper = null;
- final boolean isInterestChargedFromDateSameAsDisbursalDateEnabled = false;
-
- return new LoanApplicationTerms(applicationCurrency, loanTermFrequency,
- loanTermPeriodFrequencyType, numberOfRepayments,
- repaymentEvery, repaymentPeriodFrequencyType, null, null,
- amortizationMethod, interestMethod, interestRatePerPeriod,
- interestRatePeriodFrequencyType, annualNominalInterestRate,
- interestCalculationPeriodMethod,
- allowPartialPeriodInterestCalcualtion, principalMoney,
- expectedDisbursementDate, repaymentsStartingFromDate,
- calculatedRepaymentsStartingFromDate, graceOnPrincipalPayment,
- recurringMoratoriumOnPrincipalPeriods, graceOnInterestPayment,
- graceOnInterestCharged, interestChargedFromDate,
- inArrearsTolerance, multiDisburseLoan, emiAmount,
- disbursementDatas, maxOutstandingBalance,
- loanProductRelatedDetail.getGraceOnDueDate(), daysInMonthType,
- daysInYearType, isInterestRecalculationEnabled,
- rescheduleStrategyMethod,
- interestRecalculationCompoundingMethod, restCalendarInstance,
- recalculationFrequencyType, compoundingCalendarInstance,
- compoundingFrequencyType, principalThresholdForLastInstalment,
- installmentAmountInMultiplesOf,
- loanPreClosureInterestCalculationStrategy, loanCalendar,
- approvedAmount, loanTermVariations, calendarHistoryDataWrapper,
- isInterestChargedFromDateSameAsDisbursalDateEnabled,
- numberOfdays, isSkipRepaymentOnFirstDayofMonth);
-
- }
-
- public static LoanApplicationTerms assembleFrom(
- final LoanApplicationTerms applicationTerms,
- final List<LoanTermVariationsData> loanTermVariations) {
- return new LoanApplicationTerms(
- applicationTerms.currency,
- applicationTerms.loanTermFrequency,
- applicationTerms.loanTermPeriodFrequencyType,
- applicationTerms.numberOfRepayments,
- applicationTerms.repaymentEvery,
- applicationTerms.repaymentPeriodFrequencyType,
- applicationTerms.nthDay,
- applicationTerms.weekDayType,
- applicationTerms.amortizationMethod,
- applicationTerms.interestMethod,
- applicationTerms.interestRatePerPeriod,
- applicationTerms.interestRatePeriodFrequencyType,
- applicationTerms.annualNominalInterestRate,
- applicationTerms.interestCalculationPeriodMethod,
- applicationTerms.allowPartialPeriodInterestCalcualtion,
- applicationTerms.principal,
- applicationTerms.expectedDisbursementDate,
- applicationTerms.repaymentsStartingFromDate,
- applicationTerms.calculatedRepaymentsStartingFromDate,
- applicationTerms.principalGrace,
- applicationTerms.recurringMoratoriumOnPrincipalPeriods,
- applicationTerms.interestPaymentGrace,
- applicationTerms.interestChargingGrace,
- applicationTerms.interestChargedFromDate,
- applicationTerms.inArrearsTolerance,
- applicationTerms.multiDisburseLoan,
- applicationTerms.actualFixedEmiAmount,
- applicationTerms.disbursementDatas,
- applicationTerms.maxOutstandingBalance,
- applicationTerms.graceOnArrearsAgeing,
- applicationTerms.daysInMonthType,
- applicationTerms.daysInYearType,
- applicationTerms.interestRecalculationEnabled,
- applicationTerms.rescheduleStrategyMethod,
- applicationTerms.interestRecalculationCompoundingMethod,
- applicationTerms.restCalendarInstance,
- applicationTerms.recalculationFrequencyType,
- applicationTerms.compoundingCalendarInstance,
- applicationTerms.compoundingFrequencyType,
- applicationTerms.principalThresholdForLastInstalment,
- applicationTerms.installmentAmountInMultiplesOf,
- applicationTerms.preClosureInterestCalculationStrategy,
- applicationTerms.loanCalendar,
- applicationTerms.approvedPrincipal.getAmount(),
- loanTermVariations,
- applicationTerms.calendarHistoryDataWrapper,
- applicationTerms.isInterestChargedFromDateSameAsDisbursalDateEnabled,
- applicationTerms.numberOfDays,
- applicationTerms.isSkipRepaymentOnFirstDayOfMonth);
- }
-
- private LoanApplicationTerms(
- final ApplicationCurrency currency,
- final Integer loanTermFrequency,
- final PeriodFrequencyType loanTermPeriodFrequencyType,
- final Integer numberOfRepayments,
- final Integer repaymentEvery,
- final PeriodFrequencyType repaymentPeriodFrequencyType,
- final Integer nthDay,
- final DayOfWeekType weekDayType,
- final AmortizationMethod amortizationMethod,
- final InterestMethod interestMethod,
- final BigDecimal interestRatePerPeriod,
- final PeriodFrequencyType interestRatePeriodFrequencyType,
- final BigDecimal annualNominalInterestRate,
- final InterestCalculationPeriodMethod interestCalculationPeriodMethod,
- final boolean allowPartialPeriodInterestCalcualtion,
- final Money principal,
- final LocalDate expectedDisbursementDate,
- final LocalDate repaymentsStartingFromDate,
- final LocalDate calculatedRepaymentsStartingFromDate,
- final Integer principalGrace,
- final Integer recurringMoratoriumOnPrincipalPeriods,
- final Integer interestPaymentGrace,
- final Integer interestChargingGrace,
- final LocalDate interestChargedFromDate,
- final Money inArrearsTolerance,
- final boolean multiDisburseLoan,
- final BigDecimal emiAmount,
- final List<DisbursementData> disbursementDatas,
- final BigDecimal maxOutstandingBalance,
- final Integer graceOnArrearsAgeing,
- final DaysInMonthType daysInMonthType,
- final DaysInYearType daysInYearType,
- final boolean isInterestRecalculationEnabled,
- final LoanRescheduleStrategyMethod rescheduleStrategyMethod,
- final InterestRecalculationCompoundingMethod interestRecalculationCompoundingMethod,
- final CalendarInstance restCalendarInstance,
- final RecalculationFrequencyType recalculationFrequencyType,
- final CalendarInstance compoundingCalendarInstance,
- final RecalculationFrequencyType compoundingFrequencyType,
- final BigDecimal principalThresholdForLastInstalment,
- final Integer installmentAmountInMultiplesOf,
- final LoanPreClosureInterestCalculationStrategy preClosureInterestCalculationStrategy,
- final Calendar loanCalendar, BigDecimal approvedAmount,
- List<LoanTermVariationsData> loanTermVariations,
- final CalendarHistoryDataWrapper calendarHistoryDataWrapper,
- Boolean isInterestChargedFromDateSameAsDisbursalDateEnabled,
- final Integer numberOfdays,
- final boolean isSkipRepaymentOnFirstDayofMonth) {
-
- this.currency = currency;
- this.loanTermFrequency = loanTermFrequency;
- this.loanTermPeriodFrequencyType = loanTermPeriodFrequencyType;
- this.numberOfRepayments = numberOfRepayments;
- this.repaymentEvery = repaymentEvery;
- this.repaymentPeriodFrequencyType = repaymentPeriodFrequencyType;
- this.nthDay = nthDay;
- this.weekDayType = weekDayType;
- this.amortizationMethod = amortizationMethod;
-
- this.interestMethod = interestMethod;
- this.interestRatePerPeriod = interestRatePerPeriod;
- this.interestRatePeriodFrequencyType = interestRatePeriodFrequencyType;
- this.annualNominalInterestRate = annualNominalInterestRate;
- this.interestCalculationPeriodMethod = interestCalculationPeriodMethod;
- this.allowPartialPeriodInterestCalcualtion = allowPartialPeriodInterestCalcualtion;
-
- this.principal = principal;
- this.expectedDisbursementDate = expectedDisbursementDate;
- this.repaymentsStartingFromDate = repaymentsStartingFromDate;
- this.calculatedRepaymentsStartingFromDate = calculatedRepaymentsStartingFromDate;
-
- this.principalGrace = principalGrace;
- this.recurringMoratoriumOnPrincipalPeriods = recurringMoratoriumOnPrincipalPeriods;
- this.interestPaymentGrace = interestPaymentGrace;
- this.interestChargingGrace = interestChargingGrace;
- this.interestChargedFromDate = interestChargedFromDate;
-
- this.inArrearsTolerance = inArrearsTolerance;
- this.multiDisburseLoan = multiDisburseLoan;
- this.fixedEmiAmount = emiAmount;
- this.actualFixedEmiAmount = emiAmount;
- this.disbursementDatas = disbursementDatas;
- this.maxOutstandingBalance = maxOutstandingBalance;
- this.graceOnArrearsAgeing = graceOnArrearsAgeing;
- this.daysInMonthType = daysInMonthType;
- this.daysInYearType = daysInYearType;
- this.interestRecalculationEnabled = isInterestRecalculationEnabled;
- this.rescheduleStrategyMethod = rescheduleStrategyMethod;
- this.interestRecalculationCompoundingMethod = interestRecalculationCompoundingMethod;
- this.restCalendarInstance = restCalendarInstance;
- this.compoundingCalendarInstance = compoundingCalendarInstance;
- this.recalculationFrequencyType = recalculationFrequencyType;
- this.compoundingFrequencyType = compoundingFrequencyType;
- this.principalThresholdForLastInstalment = principalThresholdForLastInstalment;
- this.installmentAmountInMultiplesOf = installmentAmountInMultiplesOf;
- this.preClosureInterestCalculationStrategy = preClosureInterestCalculationStrategy;
- this.isSkipRepaymentOnFirstDayOfMonth = isSkipRepaymentOnFirstDayofMonth;
- this.numberOfDays = numberOfdays;
-
- this.loanCalendar = loanCalendar;
- this.approvedPrincipal = Money.of(principal.getCurrency(),
- approvedAmount);
- this.variationsDataWrapper = new LoanTermVariationsDataWrapper(
- loanTermVariations);
- this.actualNumberOfRepayments = numberOfRepayments
- + getLoanTermVariations().adjustNumberOfRepayments();
- this.adjustPrincipalForFlatLoans = principal.zero();
- if (this.calculatedRepaymentsStartingFromDate == null) {
- this.seedDate = this.expectedDisbursementDate;
- } else {
- this.seedDate = this.calculatedRepaymentsStartingFromDate;
- }
- this.calendarHistoryDataWrapper = calendarHistoryDataWrapper;
- this.isInterestChargedFromDateSameAsDisbursalDateEnabled = isInterestChargedFromDateSameAsDisbursalDateEnabled;
- }
-
- public Money adjustPrincipalIfLastRepaymentPeriod(
- final Money principalForPeriod,
- final Money totalCumulativePrincipalToDate, final int periodNumber) {
-
- Money adjusted = principalForPeriod;
-
- final Money totalPrincipalRemaining = this.principal
- .minus(totalCumulativePrincipalToDate);
- if (totalPrincipalRemaining.isLessThanZero()) {
- // paid too much principal, subtract amount that overpays from
- // principal paid for period.
- adjusted = principalForPeriod.minus(totalPrincipalRemaining.abs());
- } else if (this.actualFixedEmiAmount != null) {
- final Money difference = this.principal
- .minus(totalCumulativePrincipalToDate);
- final Money principalThreshold = principalForPeriod.multipliedBy(
- this.principalThresholdForLastInstalment).dividedBy(100,
- MoneyHelper.getRoundingMode());
- if (difference.isLessThan(principalThreshold)) {
- adjusted = principalForPeriod.plus(difference.abs());
- }
- } else if (isLastRepaymentPeriod(this.actualNumberOfRepayments,
- periodNumber)) {
-
- final Money difference = totalCumulativePrincipalToDate
- .minus(this.principal);
- if (difference.isLessThanZero()) {
- adjusted = principalForPeriod.plus(difference.abs());
- } else if (difference.isGreaterThanZero()) {
- adjusted = principalForPeriod.minus(difference.abs());
- }
- }
-
- return adjusted;
- }
-
- public Money adjustInterestIfLastRepaymentPeriod(
- final Money interestForThisPeriod,
- final Money totalCumulativeInterestToDate,
- final Money totalInterestDueForLoan, final int periodNumber) {
-
- Money adjusted = interestForThisPeriod;
-
- final Money totalInterestRemaining = totalInterestDueForLoan
- .minus(totalCumulativeInterestToDate);
- if (totalInterestRemaining.isLessThanZero()) {
- // paid too much interest, subtract amount that overpays from
- // interest paid for period.
- adjusted = interestForThisPeriod
- .minus(totalInterestRemaining.abs());
- } else if (isLastRepaymentPeriod(this.actualNumberOfRepayments,
- periodNumber)) {
- final Money interestDifference = totalCumulativeInterestToDate
- .minus(totalInterestDueForLoan);
- if (interestDifference.isLessThanZero()) {
- adjusted = interestForThisPeriod.plus(interestDifference.abs());
- } else if (interestDifference.isGreaterThanZero()) {
- adjusted = interestForThisPeriod
- .minus(interestDifference.abs());
- }
- }
- if (adjusted.isLessThanZero()) {
- adjusted = adjusted.plus(adjusted);
- }
- return adjusted;
- }
-
- /**
- * Calculates the total interest to be charged on loan taking into account
- * grace settings.
- *
- */
- public Money calculateTotalInterestCharged(
- final PaymentPeriodsInOneYearCalculator calculator,
- final MathContext mc) {
-
- Money totalInterestCharged = this.principal.zero();
-
- switch (this.interestMethod) {
- case FLAT:
- final Money totalInterestChargedForLoanTerm = calculateTotalFlatInterestDueWithoutGrace(
- calculator, mc);
-
- final Money totalInterestPerInstallment = calculateTotalInterestPerInstallmentWithoutGrace(
- calculator, mc);
-
- final Money totalGraceOnInterestCharged = totalInterestPerInstallment
- .multiplyRetainScale(getInterestChargingGrace(),
- mc.getRoundingMode());
-
- totalInterestCharged = totalInterestChargedForLoanTerm
- .minus(totalGraceOnInterestCharged);
- break;
- case DECLINING_BALANCE:
- case INVALID:
- break;
- }
-
- return totalInterestCharged;
- }
-
- public Money calculateTotalPrincipalForPeriod(
- final PaymentPeriodsInOneYearCalculator calculator,
- final Money outstandingBalance, final int periodNumber,
- final MathContext mc, Money interestForThisInstallment) {
-
- Money principalForInstallment = this.principal.zero();
-
- switch (this.interestMethod) {
- case FLAT:
- principalForInstallment = calculateTotalPrincipalPerPeriodWithoutGrace(
- mc, periodNumber);
- break;
- case DECLINING_BALANCE:
- switch (this.amortizationMethod) {
- case EQUAL_INSTALLMENTS:
- Money totalPmtForThisInstallment = pmtForInstallment(
- calculator, outstandingBalance, periodNumber, mc);
- principalForInstallment = calculatePrincipalDueForInstallment(
- periodNumber, totalPmtForThisInstallment,
- interestForThisInstallment);
- break;
- case EQUAL_PRINCIPAL:
- principalForInstallment = calculateEqualPrincipalDueForInstallment(
- mc, periodNumber);
- break;
- case INVALID:
- break;
- }
- break;
- case INVALID:
- break;
- }
-
- return principalForInstallment;
- }
-
- public Money pmtForInstallment(
- final PaymentPeriodsInOneYearCalculator calculator,
- final Money outstandingBalance, final int periodNumber,
- final MathContext mc) {
- // Calculate exact period from disbursement date
- final LocalDate periodStartDate = getExpectedDisbursementDate()
- .withDayOfMonth(1);
- final LocalDate periodEndDate = getPeriodEndDate(periodStartDate);
- // equal installments
- final int periodsElapsed = periodNumber - 1;
- // with periodic interest for default month and year for
- // equal installment
- final BigDecimal periodicInterestRateForRepaymentPeriod = periodicInterestRate(
- calculator, mc, DaysInMonthType.DAYS_30,
- DaysInYearType.DAYS_365, periodStartDate, periodEndDate);
- Money totalPmtForThisInstallment = calculateTotalDueForEqualInstallmentRepaymentPeriod(
- periodicInterestRateForRepaymentPeriod, outstandingBalance,
- periodsElapsed);
- return totalPmtForThisInstallment;
- }
-
- private LocalDate getPeriodEndDate(final LocalDate startDate) {
- LocalDate dueRepaymentPeriodDate = startDate;
- switch (this.repaymentPeriodFrequencyType) {
- case DAYS:
- dueRepaymentPeriodDate = startDate.plusDays(this.repaymentEvery);
- break;
- case WEEKS:
- dueRepaymentPeriodDate = startDate.plusWeeks(this.repaymentEvery);
- break;
- case MONTHS:
- dueRepaymentPeriodDate = startDate.plusMonths(this.repaymentEvery);
- break;
- case YEARS:
- dueRepaymentPeriodDate = startDate.plusYears(this.repaymentEvery);
- break;
- case INVALID:
- break;
- }
- return dueRepaymentPeriodDate;
- }
-
- public PrincipalInterest calculateTotalInterestForPeriod(
- final PaymentPeriodsInOneYearCalculator calculator,
- final double interestCalculationGraceOnRepaymentPeriodFraction,
- final int periodNumber, final MathContext mc,
- final Money cumulatingInterestPaymentDueToGrace,
- final Money outstandingBalance, final LocalDate periodStartDate,
- final LocalDate periodEndDate) {
-
- Money interestForInstallment = this.principal.zero();
- Money interestBroughtForwardDueToGrace = cumulatingInterestPaymentDueToGrace
- .copy();
-
- switch (this.interestMethod) {
- case FLAT:
-
- switch (this.amortizationMethod) {
- case EQUAL_INSTALLMENTS:
- // average out outstanding interest over remaining
- // instalments where interest is applicable
- interestForInstallment = calculateTotalFlatInterestForInstallmentAveragingOutGracePeriods(
- calculator, periodNumber, mc);
- break;
- case EQUAL_PRINCIPAL:
- // interest follows time-value of money and is brought
- // forward to next applicable interest payment period
- final PrincipalInterest result = calculateTotalFlatInterestForPeriod(
- calculator, periodNumber, mc,
- interestBroughtForwardDueToGrace);
- interestForInstallment = result.interest();
- interestBroughtForwardDueToGrace = result
- .interestPaymentDueToGrace();
- break;
- case INVALID:
- break;
- }
- break;
- case DECLINING_BALANCE:
-
- final Money interestForThisInstallmentBeforeGrace = calculateDecliningInterestDueForInstallmentBeforeApplyingGrace(
- calculator, mc, outstandingBalance, periodStartDate,
- periodEndDate);
-
- final Money interestForThisInstallmentAfterGrace = calculateDecliningInterestDueForInstallmentAfterApplyingGrace(
- calculator,
- interestCalculationGraceOnRepaymentPeriodFraction, mc,
- outstandingBalance, periodNumber, periodStartDate,
- periodEndDate);
-
- interestForInstallment = interestForThisInstallmentAfterGrace;
- if (interestForThisInstallmentAfterGrace.isGreaterThanZero()) {
- interestForInstallment = interestBroughtForwardDueToGrace
- .plus(interestForThisInstallmentAfterGrace);
- interestBroughtForwardDueToGrace = interestBroughtForwardDueToGrace
- .zero();
- } else if (isInterestFreeGracePeriod(periodNumber)) {
- interestForInstallment = interestForInstallment.zero();
- } else if (isInterestFreeGracePeriodFromDate(interestCalculationGraceOnRepaymentPeriodFraction)) {
- interestForInstallment = interestForThisInstallmentAfterGrace;
- } else {
- interestBroughtForwardDueToGrace = interestBroughtForwardDueToGrace
- .plus(interestForThisInstallmentBeforeGrace);
- }
- break;
- case INVALID:
- break;
- }
-
- return new PrincipalInterest(null, interestForInstallment,
- interestBroughtForwardDueToGrace);
- }
-
- private final boolean isLastRepaymentPeriod(final int numberOfRepayments,
- final int periodNumber) {
- return periodNumber == numberOfRepayments;
- }
-
- /**
- * general method to calculate totalInterestDue discounting any grace
- * settings
- */
- private Money calculateTotalFlatInterestDueWithoutGrace(
- final PaymentPeriodsInOneYearCalculator calculator,
- final MathContext mc) {
-
- Money totalInterestDue = this.principal.zero();
-
- switch (this.interestMethod) {
- case FLAT:
- final BigDecimal interestRateForLoanTerm = calculateFlatInterestRateForLoanTerm(
- calculator, mc);
- totalInterestDue = this.principal.multiplyRetainScale(
- interestRateForLoanTerm, mc.getRoundingMode());
-
- break;
- case DECLINING_BALANCE:
- break;
- case INVALID:
- break;
- }
-
- if (this.totalInterestDue != null) {
- totalInterestDue = this.totalInterestDue;
- }
-
- return totalInterestDue;
- }
-
- private BigDecimal calculateFlatInterestRateForLoanTerm(
- final PaymentPeriodsInOneYearCalculator calculator,
- final MathContext mc) {
-
- final BigDecimal divisor = BigDecimal.valueOf(Double.valueOf("100.0"));
-
- final long loanTermPeriodsInOneYear = calculatePeriodsInOneYear(calculator);
- final BigDecimal loanTermPeriodsInYearBigDecimal = BigDecimal
- .valueOf(loanTermPeriodsInOneYear);
-
- final BigDecimal loanTermFrequencyBigDecimal = calculatePeriodsInLoanTerm();
-
- return this.annualNominalInterestRate
- .divide(loanTermPeriodsInYearBigDecimal, mc)
- .divide(divisor, mc).multiply(loanTermFrequencyBigDecimal);
- }
-
- private BigDecimal calculatePeriodsInLoanTerm() {
-
- BigDecimal periodsInLoanTerm = BigDecimal
- .valueOf(this.loanTermFrequency);
- switch (this.interestCalculationPeriodMethod) {
- case DAILY:
- // number of days from 'ideal disbursement' to final date
-
- LocalDate loanStartDate = getExpectedDisbursementDate();
- if (getInterestChargedFromDate() != null
- && loanStartDate
- .isBefore(getInterestChargedFromLocalDate())) {
- loanStartDate = getInterestChargedFromLocalDate();
- }
-
- final int periodsInLoanTermInteger = Days.daysBetween(
- loanStartDate, this.loanEndDate).getDays();
- periodsInLoanTerm = BigDecimal.valueOf(periodsInLoanTermInteger);
- break;
- case INVALID:
- break;
- case SAME_AS_REPAYMENT_PERIOD:
- if (this.allowPartialPeriodInterestCalcualtion) {
- LocalDate startDate = getExpectedDisbursementDate();
- if (getInterestChargedFromDate() != null) {
- startDate = getInterestChargedFromLocalDate();
- }
- periodsInLoanTerm = calculatePeriodsBetweenDates(startDate,
- this.loanEndDate);
- }
- break;
- }
-
- return periodsInLoanTerm;
- }
-
- public BigDecimal calculatePeriodsBetweenDates(final LocalDate startDate,
- final LocalDate endDate) {
- BigDecimal numberOfPeriods = BigDecimal.ZERO;
- switch (this.repaymentPeriodFrequencyType) {
- case DAYS:
- int numberOfDays = Days.daysBetween(startDate, endDate).getDays();
- numberOfPeriods = BigDecimal.valueOf((double) numberOfDays);
- break;
- case WEEKS:
- int numberOfWeeks = Weeks.weeksBetween(startDate, endDate)
- .getWeeks();
- int daysLeftAfterWeeks = Days.daysBetween(
- startDate.plusWeeks(numberOfWeeks), endDate).getDays();
- numberOfPeriods = numberOfPeriods.add(
- BigDecimal.valueOf(numberOfWeeks)).add(
- BigDecimal.valueOf((double) daysLeftAfterWeeks / 7));
- break;
- case MONTHS:
- int numberOfMonths = Months.monthsBetween(startDate, endDate)
- .getMonths();
- LocalDate startDateAfterConsideringMonths = startDate
- .plusMonths(numberOfMonths);
- LocalDate endDateAfterConsideringMonths = startDate
- .plusMonths(numberOfMonths + 1);
- int daysLeftAfterMonths = Days.daysBetween(
- startDateAfterConsideringMonths, endDate).getDays();
- int daysInPeriodAfterMonths = Days.daysBetween(
- startDateAfterConsideringMonths,
- endDateAfterConsideringMonths).getDays();
- numberOfPeriods = numberOfPeriods.add(
- BigDecimal.valueOf(numberOfMonths)).add(
- BigDecimal.valueOf((double) daysLeftAfterMonths
- / daysInPeriodAfterMonths));
- break;
- case YEARS:
- int numberOfYears = Years.yearsBetween(startDate, endDate)
- .getYears();
- LocalDate startDateAfterConsideringYears = startDate
- .plusYears(numberOfYears);
- LocalDate endDateAfterConsideringYears = startDate
- .plusYears(numberOfYears + 1);
- int daysLeftAfterYears = Days.daysBetween(
- startDateAfterConsideringYears, endDate).getDays();
- int daysInPeriodAfterYears = Days.daysBetween(
- startDateAfterConsideringYears,
- endDateAfterConsideringYears).getDays();
- numberOfPeriods = numberOfPeriods.add(
- BigDecimal.valueOf(numberOfYears)).add(
- BigDecimal.valueOf((double) daysLeftAfterYears
- / daysInPeriodAfterYears));
- break;
- default:
- break;
- }
- return numberOfPeriods;
- }
-
- public void updateLoanEndDate(final LocalDate loanEndDate) {
- this.loanEndDate = loanEndDate;
- }
-
- private Money calculateTotalInterestPerInstallmentWithoutGrace(
- final PaymentPeriodsInOneYearCalculator calculator,
- final MathContext mc) {
-
- final Money totalInterestForLoanTerm = calculateTotalFlatInterestDueWithoutGrace(
- calculator, mc);
-
- return totalInterestForLoanTerm.dividedBy(
- Long.valueOf(this.actualNumberOfRepayments),
- mc.getRoundingMode());
- }
-
- private Money calculateTotalPrincipalPerPeriodWithoutGrace(
- final MathContext mc, final int periodNumber) {
- final int totalRepaymentsWithCapitalPayment = calculateNumberOfRepaymentsWithPrincipalPayment();
- Money principalPerPeriod = this.principal.dividedBy(
- totalRepaymentsWithCapitalPayment, mc.getRoundingMode()).plus(
- this.adjustPrincipalForFlatLoans);
- if (isPrincipalGraceApplicableForThisPeriod(periodNumber)) {
- principalPerPeriod = principalPerPeriod.zero();
- }
- if (!isPrincipalGraceApplicableForThisPeriod(periodNumber)
- && currentPeriodFixedPrincipalAmount != null) {
- this.adjustPrincipalForFlatLoans = this.adjustPrincipalForFlatLoans
- .plus(principalPerPeriod.minus(
- currentPeriodFixedPrincipalAmount).dividedBy(
- this.actualNumberOfRepayments - periodNumber,
- mc.getRoundingMode()));
- principalPerPeriod = this.principal.zero().plus(
- currentPeriodFixedPrincipalAmount);
-
- }
- return principalPerPeriod;
- }
-
- private PrincipalInterest calculateTotalFlatInterestForPeriod(
- final PaymentPeriodsInOneYearCalculator calculator,
- final int periodNumber, final MathContext mc,
- final Money cumulatingInterestPaymentDueToGrace) {
-
- Money interestBroughtForwardDueToGrace = cumulatingInterestPaymentDueToGrace
- .copy();
-
- Money interestForInstallment = calculateTotalInterestPerInstallmentWithoutGrace(
- calculator, mc);
- if (isInterestPaymentGraceApplicableForThisPeriod(periodNumber)) {
- interestBroughtForwardDueToGrace = interestBroughtForwardDueToGrace
- .plus(interestForInstallment);
- interestForInstallment = interestForInstallment.zero();
- } else if (isInterestFreeGracePeriod(periodNumber)) {
- interestForInstallment = interestForInstallment.zero();
- } else if (isFirstPeriodAfterInterestPaymentGracePeriod(periodNumber)) {
- interestForInstallment = cumulatingInterestPaymentDueToGrace
- .plus(interestForInstallment);
- interestBroughtForwardDueToGrace = interestBroughtForwardDueToGrace
- .zero();
- }
-
- return new PrincipalInterest(null, interestForInstallment,
- interestBroughtForwardDueToGrace);
- }
-
- /*
- * calculates the interest that should be due for a given scheduled loan
- * repayment period. It takes into account GRACE periods and calculates how
- * much interest is due per period by averaging the number of periods where
- * interest is due and should be paid against the total known interest that
- * is due without grace.
- */
- private Money calculateTotalFlatInterestForInstallmentAveragingOutGracePeriods(
- final PaymentPeriodsInOneYearCalculator calculator,
- final int periodNumber, final MathContext mc) {
-
- Money interestForInstallment = calculateTotalInterestPerInstallmentWithoutGrace(
- calculator, mc);
- if (isInterestPaymentGraceApplicableForThisPeriod(periodNumber)) {
- interestForInstallment = interestForInstallment.zero();
- } else if (isInterestFreeGracePeriod(periodNumber)) {
- interestForInstallment = interestForInstallment.zero();
- } else {
-
- final Money totalInterestForLoanTerm = calculateTotalFlatInterestDueWithoutGrace(
- calculator, mc);
-
- final Money interestPerGracePeriod = calculateTotalInterestPerInstallmentWithoutGrace(
- calculator, mc);
-
- final Money totalInterestFree = interestPerGracePeriod
- .multipliedBy(getInterestChargingGrace());
- final Money realTotalInterestForLoan = totalInterestForLoanTerm
- .minus(totalInterestFree);
-
- final Integer interestPaymentDuePeriods = calculateNumberOfRepaymentPeriodsWhereInterestPaymentIsDue(this.actualNumberOfRepayments);
-
- interestForInstallment = realTotalInterestForLoan.dividedBy(
- BigDecimal.valueOf(interestPaymentDuePeriods),
- mc.getRoundingMode());
- }
-
- return interestForInstallment;
- }
-
- private BigDecimal periodicInterestRate(
- final PaymentPeriodsInOneYearCalculator calculator,
- final MathContext mc, final DaysInMonthType daysInMonthType,
- final DaysInYearType daysInYearType, LocalDate periodStartDate,
- LocalDate periodEndDate) {
-
- final long loanTermPeriodsInOneYear = calculatePeriodsInOneYear(calculator);
-
- final BigDecimal divisor = BigDecimal.valueOf(Double.valueOf("100.0"));
- final BigDecimal loanTermPeriodsInYearBigDecimal = BigDecimal
- .valueOf(loanTermPeriodsInOneYear);
-
- BigDecimal periodicInterestRate = BigDecimal.ZERO;
- BigDecimal loanTermFrequencyBigDecimal = calculateLoanTermFrequency(
- periodStartDate, periodEndDate);
- switch (this.interestCalculationPeriodMethod) {
- case INVALID:
- break;
- case DAILY:
- // For daily work out number of days in the period
- BigDecimal numberOfDaysInPeriod = BigDecimal.valueOf(Days
- .daysBetween(periodStartDate, periodEndDate).getDays());
-
- final BigDecimal oneDayOfYearInterestRate = this.annualNominalInterestRate
- .divide(loanTermPeriodsInYearBigDecimal, mc).divide(
- divisor, mc);
-
- switch (this.repaymentPeriodFrequencyType) {
- case INVALID:
- break;
- case DAYS:
- periodicInterestRate = oneDayOfYearInterestRate.multiply(
- numberOfDaysInPeriod, mc);
- break;
- case WEEKS:
- periodicInterestRate = oneDayOfYearInterestRate.multiply(
- numberOfDaysInPeriod, mc);
- break;
- case MONTHS:
- if (daysInMonthType.isDaysInMonth_30()) {
- numberOfDaysInPeriod = loanTermFrequencyBigDecimal
- .multiply(BigDecimal.valueOf(30), mc);
- }
- periodicInterestRate = oneDayOfYearInterestRate.multiply(
- numberOfDaysInPeriod, mc);
- break;
- case YEARS:
- switch (daysInYearType) {
- case DAYS_360:
- numberOfDaysInPeriod = loanTermFrequencyBigDecimal
- .multiply(BigDecimal.valueOf(360), mc);
- break;
- case DAYS_364:
- numberOfDaysInPeriod = loanTermFrequencyBigDecimal
- .multiply(BigDecimal.valueOf(364), mc);
- break;
- case DAYS_365:
- numberOfDaysInPeriod = loanTermFrequencyBigDecimal
- .multiply(BigDecimal.valueOf(365), mc);
- break;
- default:
- break;
- }
- periodicInterestRate = oneDayOfYearInterestRate.multiply(
- numberOfDaysInPeriod, mc);
- break;
- }
- break;
- case SAME_AS_REPAYMENT_PERIOD:
- periodicInterestRate = this.annualNominalInterestRate
- .divide(loanTermPeriodsInYearBigDecimal, mc)
- .divide(divisor, mc).multiply(loanTermFrequencyBigDecimal);
- break;
- }
-
- return periodicInterestRate;
- }
-
- private BigDecimal calculateLoanTermFrequency(
- final LocalDate periodStartDate, final LocalDate periodEndDate) {
- BigDecimal loanTermFrequencyBigDecimal = BigDecimal
- .valueOf(this.repaymentEvery);
- if (this.interestCalculationPeriodMethod.isDaily()
- || this.allowPartialPeriodInterestCalcualtion) {
- loanTermFrequencyBigDecimal = calculatePeriodsBetweenDates(
- periodStartDate, periodEndDate);
- }
- return loanTermFrequencyBigDecimal;
- }
-
- public BigDecimal interestRateFor(
- final PaymentPeriodsInOneYearCalculator calculator,
- final MathContext mc, final Money outstandingBalance,
- final LocalDate fromDate, final LocalDate toDate) {
-
- long loanTermPeriodsInOneYear = calculator.calculate(
- PeriodFrequencyType.DAYS).longValue();
- int repaymentEvery = Days.daysBetween(fromDate, toDate).getDays();
- if (isFallingInRepaymentPeriod(fromDate, toDate)) {
- loanTermPeriodsInOneYear = calculatePeriodsInOneYear(calculator);
- repaymentEvery = getPeriodsBetween(fromDate, toDate);
- }
-
- final BigDecimal divisor = BigDecimal.valueOf(Double.valueOf("100.0"));
- final BigDecimal loanTermPeriodsInYearBigDecimal = BigDecimal
- .valueOf(loanTermPeriodsInOneYear);
- final BigDecimal oneDayOfYearInterestRate = this.annualNominalInterestRate
- .divide(loanTermPeriodsInYearBigDecimal, mc)
- .divide(divisor, mc);
- BigDecimal interestRate = oneDayOfYearInterestRate.multiply(
- BigDecimal.valueOf(repaymentEvery), mc);
- return outstandingBalance.getAmount().multiply(interestRate, mc);
- }
-
- private long calculatePeriodsInOneYear(
- final PaymentPeriodsInOneYearCalculator calculator) {
-
- // check if daysInYears is set if so change periodsInOneYear to days set
- // in db
- long periodsInOneYear;
- boolean daysInYearToUse = (this.repaymentPeriodFrequencyType.getCode()
- .equalsIgnoreCase("periodFrequencyType.days") && !this.daysInYearType
- .getCode().equalsIgnoreCase("DaysInYearType.actual"));
- if (daysInYearToUse) {
- periodsInOneYear = this.daysInYearType.getValue().longValue();
- } else {
- periodsInOneYear = calculator.calculate(
- this.repaymentPeriodFrequencyType).longValue();
- }
- switch (this.interestCalculationPeriodMethod) {
- case DAILY:
- periodsInOneYear = (!this.daysInYearType.getCode()
- .equalsIgnoreCase("DaysInYearType.actual")) ? this.daysInYearType
- .getValue().longValue() : calculator.calculate(
- PeriodFrequencyType.DAYS).longValue();
- break;
- case INVALID:
- break;
- case SAME_AS_REPAYMENT_PERIOD:
- break;
- }
-
- return periodsInOneYear;
- }
-
- private int calculateNumberOfRepaymentsWithPrincipalPayment() {
- int numPeriods = calculateNumberOfRemainingPrincipalPaymentPeriods(
- this.actualNumberOfRepayments,
- this.getRecurringMoratoriumOnPrincipalPeriods(),
- this.getPrincipalGrace(), 0);
- return numPeriods;
- }
-
- private Integer calculateNumberOfRepaymentPeriodsWhereInterestPaymentIsDue(
- final Integer totalNumberOfRepaymentPeriods) {
- return totalNumberOfRepaymentPeriods
- - Math.max(getInterestChargingGrace(),
- getInterestPaymentGrace());
- }
-
- public boolean isPrincipalGraceApplicableForThisPeriod(
- final int periodNumber) {
- if (this.getRecurringMoratoriumOnPrincipalPeriods() > 0) {
- return ((periodNumber > 0 && periodNumber <= getPrincipalGrace()) || (periodNumber > 0 && (((periodNumber - getPrincipalGrace()) % (this
- .getRecurringMoratoriumOnPrincipalPeriods() + 1)) != 1)));
- } else {
- return periodNumber > 0 && periodNumber <= getPrincipalGrace();
- }
- }
-
- private boolean isInterestPaymentGraceApplicableForThisPeriod(
- final int periodNumber) {
- return periodNumber > 0 && periodNumber <= getInterestPaymentGrace();
- }
-
- private boolean isFirstPeriodAfterInterestPaymentGracePeriod(
- final int periodNumber) {
- return periodNumber > 0
- && periodNumber == getInterestPaymentGrace() + 1;
- }
-
- private boolean isInterestFreeGracePeriod(final int periodNumber) {
- return periodNumber > 0 && periodNumber <= getInterestChargingGrace();
- }
-
- public Integer getPrincipalGrace() {
- Integer graceOnPrincipalPayments = Integer.valueOf(0);
- if (this.principalGrace != null) {
- graceOnPrincipalPayments = this.principalGrace;
- }
- return graceOnPrincipalPayments;
- }
-
- public Integer getRecurringMoratoriumOnPrincipalPeriods() {
- Integer recurringMoratoriumOnPrincipalPeriods = Integer.valueOf(0);
- if (this.recurringMoratoriumOnPrincipalPeriods != null) {
- recurringMoratoriumOnPrincipalPeriods = this.recurringMoratoriumOnPrincipalPeriods;
- }
- return recurringMoratoriumOnPrincipalPeriods;
- }
-
- public Integer getInterestPaymentGrace() {
- Integer graceOnInterestPayments = Integer.valueOf(0);
- if (this.interestPaymentGrace != null) {
- graceOnInterestPayments = this.interestPaymentGrace;
- }
- return graceOnInterestPayments;
- }
-
- public Integer getInterestChargingGrace() {
- Integer graceOnInterestCharged = Integer.valueOf(0);
- if (this.interestChargingGrace != null) {
- graceOnInterestCharged = this.interestChargingGrace;
- }
- return graceOnInterestCharged;
- }
-
- private double paymentPerPeriod(final BigDecimal periodicInterestRate,
- final Money balance, final int periodsElapsed) {
-
- if (getFixedEmiAmount() == null) {
- final double futureValue = 0;
- final double principalDouble = balance.getAmount()
- .multiply(BigDecimal.valueOf(-1)).doubleValue();
-
- final Integer periodsRemaining = calculateNumberOfRemainingPrincipalPaymentPeriods(
- this.actualNumberOfRepayments,
- this.getRecurringMoratoriumOnPrincipalPeriods(),
- this.getPrincipalGrace(), periodsElapsed);
-
- double installmentAmount = FinanicalFunctions.pmt(
- periodicInterestRate.doubleValue(),
- periodsRemaining.doubleValue(), principalDouble,
- futureValue, false);
-
- if (this.installmentAmountInMultiplesOf != null) {
- installmentAmount = Money.roundToMultiplesOf(installmentAmount,
- this.installmentAmountInMultiplesOf);
- }
- setFixedEmiAmount(BigDecimal.valueOf(installmentAmount));
- }
- return getFixedEmiAmount().doubleValue();
- }
-
- private Money calculateDecliningInterestDueForInstallmentBeforeApplyingGrace(
- final PaymentPeriodsInOneYearCalculator calculator,
- final MathContext mc, final Money outstandingBalance,
- LocalDate periodStartDate, LocalDate periodEndDate) {
-
- Money interestDue = Money.zero(outstandingBalance.getCurrency());
-
- final BigDecimal periodicInterestRate = periodicInterestRate(
- calculator, mc, this.daysInMonthType, this.daysInYearType,
- periodStartDate, periodEndDate);
- interestDue = outstandingBalance.multiplyRetainScale(
- periodicInterestRate, mc.getRoundingMode());
-
- return interestDue;
- }
-
- private Money calculateDecliningInterestDueForInstallmentAfterApplyingGrace(
- final PaymentPeriodsInOneYearCalculator calculator,
- final double interestCalculationGraceOnRepaymentPeriodFraction,
- final MathContext mc, final Money outstandingBalance,
- final int periodNumber, LocalDate periodStartDate,
- LocalDate periodEndDate) {
-
- Money interest = calculateDecliningInterestDueForInstallmentBeforeApplyingGrace(
- calculator, mc, outstandingBalance, periodStartDate,
- periodEndDate);
-
- if (isInterestPaymentGraceApplicableForThisPeriod(periodNumber)) {
- interest = interest.zero();
- }
-
- Double fraction = interestCalculationGraceOnRepaymentPeriodFraction;
-
- if (isInterestFreeGracePeriod(periodNumber)) {
- interest = interest.zero();
- } else if (isInterestFreeGracePeriodFromDate(interestCalculationGraceOnRepaymentPeriodFraction)) {
-
- if (interestCalculationGraceOnRepaymentPeriodFraction >= Integer
- .valueOf(1).doubleValue()) {
- interest = interest.zero();
- fraction = fraction - Integer.valueOf(1).doubleValue();
-
- } else if (interestCalculationGraceOnRepaymentPeriodFraction > Double
- .valueOf("0.25")
- && interestCalculationGraceOnRepaymentPeriodFraction < Integer
- .valueOf(1).doubleValue()) {
-
- final Money graceOnInterestForRepaymentPeriod = interest
- .multipliedBy(interestCalculationGraceOnRepaymentPeriodFraction);
- interest = interest.minus(graceOnInterestForRepaymentPeriod);
- fraction = Double.valueOf("0");
- }
- }
-
- return interest;
- }
-
- private boolean isInterestFreeGracePeriodFromDate(
- final double interestCalculationGraceOnRepaymentPeriodFraction) {
- return this.interestChargedFromDate != null
- && interestCalculationGraceOnRepaymentPeriodFraction > Double
- .valueOf("0.0");
- }
-
- private Money calculateEqualPrincipalDueForInstallment(
- final MathContext mc, final int periodNumber) {
- Money principal = this.principal;
- if (this.fixedPrincipalAmount == null) {
- final Integer numberOfPrincipalPaymentPeriods = calculateNumberOfRemainingPrincipalPaymentPeriods(
- this.actualNumberOfRepayments,
- this.getRecurringMoratoriumOnPrincipalPeriods(),
- this.getPrincipalGrace(), periodNumber);
- principal = this.principal.dividedBy(
- numberOfPrincipalPaymentPeriods, mc.getRoundingMode());
- this.fixedPrincipalAmount = principal.getAmount();
- }
- principal = Money.of(getCurrency(), getFixedPrincipalAmount());
-
- if (isPrincipalGraceApplicableForThisPeriod(periodNumber)) {
- principal = principal.zero();
- }
- return principal;
- }
-
- public void updateFixedPrincipalAmount(final MathContext mc,
- final int periodNumber, final Money outstandingAmount) {
- final Integer numberOfPrincipalPaymentPeriods = calculateNumberOfRemainingPrincipalPaymentPeriods(
- this.actualNumberOfRepayments,
- this.getRecurringMoratoriumOnPrincipalPeriods(),
- this.getPrincipalGrace(), periodNumber - 1);
- Money principal = outstandingAmount.dividedBy(
- numberOfPrincipalPaymentPeriods, mc.getRoundingMode());
- this.fixedPrincipalAmount = principal.getAmount();
- }
-
- private static Integer calculateNumberOfRemainingPrincipalPaymentPeriods(
- final Integer totalNumberOfRepaymentPeriods,
- final int recurringMoratoriumOnPrincipalPeriods,
- final int PrincipalGrace, int periodsElapsed) {
- if (PrincipalGrace > periodsElapsed) {
- periodsElapsed = PrincipalGrace;
- }
- Integer periodsRemaining = totalNumberOfRepaymentPeriods
- - periodsElapsed;
- if (recurringMoratoriumOnPrincipalPeriods > 0) {
- double periodsRemainingDouble = ((double) periodsRemaining / ((double) (recurringMoratoriumOnPrincipalPeriods + 1)));
- periodsRemaining = (int) Math.ceil(periodsRemainingDouble);
- }
- return periodsRemaining;
- }
-
- public void setFixedPrincipalAmount(BigDecimal fixedPrincipalAmount) {
- this.fixedPrincipalAmount = fixedPrincipalAmount;
- }
-
- private Money calculatePrincipalDueForInstallment(final int periodNumber,
- final Money totalDuePerInstallment, final Money periodInterest) {
-
- Money principal = totalDuePerInstallment.minus(periodInterest);
- if (isPrincipalGraceApplicableForThisPeriod(periodNumber)) {
- principal = principal.zero();
- }
- return principal;
- }
-
- private Money calculateTotalDueForEqualInstallmentRepaymentPeriod(
- final BigDecimal periodicInterestRate, final Money balance,
- final int periodsElapsed) {
-
- final double paymentPerRepaymentPeriod = paymentPerPeriod(
- periodicInterestRate, balance, periodsElapsed);
-
- return Money.of(balance.getCurrency(),
- BigDecimal.valueOf(paymentPerRepaymentPeriod));
- }
-
- public LoanProductRelatedDetail toLoanProductRelatedDetail() {
- final MonetaryCurrency currency = new MonetaryCurrency(
- this.currency.getCode(), this.currency.getDecimalPlaces(),
- this.currency.getCurrencyInMultiplesOf());
-
- return LoanProductRelatedDetail.createFrom(currency,
- this.principal.getAmount(), this.interestRatePerPeriod,
- this.interestRatePeriodFrequencyType,
- this.annualNominalInterestRate, this.interestMethod,
- this.interestCalculationPeriodMethod,
- this.allowPartialPeriodInterestCalcualtion,
- this.repaymentEvery, this.repaymentPeriodFrequencyType,
- this.numberOfRepayments, this.principalGrace,
- this.recurringMoratoriumOnPrincipalPeriods,
- this.interestPaymentGrace, this.interestChargingGrace,
- this.amortizationMethod, this.inArrearsTolerance.getAmount(),
- this.graceOnArrearsAgeing, this.daysInMonthType.getValue(),
- this.daysInYearType.getValue(),
- this.interestRecalculationEnabled);
- }
-
- public Integer getLoanTermFrequency() {
- return this.loanTermFrequency;
- }
-
- public PeriodFrequencyType getLoanTermPeriodFrequencyType() {
- return this.loanTermPeriodFrequencyType;
- }
-
- public Integer getRepaymentEvery() {
- return this.repaymentEvery;
- }
-
- public PeriodFrequencyType getRepaymentPeriodFrequencyType() {
- return this.repaymentPeriodFrequencyType;
- }
-
- public Date getRepaymentStartFromDate() {
- Date dateValue = null;
- if (this.repaymentsStartingFromDate != null) {
- dateValue = this.repaymentsStartingFromDate.toDate();
- }
- return dateValue;
- }
-
- public Date getInterestChargedFromDate() {
- Date dateValue = null;
- if (this.interestChargedFromDate != null) {
- dateValue = this.interestChargedFromDate.toDate();
- }
- return dateValue;
- }
-
- public void setPrincipal(Money principal) {
- this.principal = principal;
- }
-
- public LocalDate getInterestChargedFromLocalDate() {
- return this.interestChargedFromDate;
- }
-
- public InterestMethod getInterestMethod() {
- return this.interestMethod;
- }
-
- public AmortizationMethod getAmortizationMethod() {
- return this.amortizationMethod;
- }
-
- public MonetaryCurrency getCurrency() {
- return this.principal.getCurrency();
- }
-
- public Integer getNumberOfRepayments() {
- return this.numberOfRepayments;
- }
-
- public LocalDate getExpectedDisbursementDate() {
- return this.expectedDisbursementDate;
- }
-
- public LocalDate getRepaymentsStartingFromLocalDate() {
- return this.repaymentsStartingFromDate;
- }
-
- public LocalDate getCalculatedRepaymentsStartingFromLocalDate() {
- return this.calculatedRepaymentsStartingFromDate;
- }
-
- public Money getPrincipal() {
- return this.principal;
- }
-
- public Money getApprovedPrincipal() {
- return this.approvedPrincipal;
- }
-
- public List<DisbursementData> getDisbursementDatas() {
- return this.disbursementDatas;
- }
-
- public boolean isMultiDisburseLoan() {
- return this.multiDisburseLoan;
- }
-
- public Money getMaxOutstandingBalance() {
- return Money.of(getCurrency(), this.maxOutstandingBalance);
- }
-
- public BigDecimal getFixedEmiAmount() {
- BigDecimal fixedEmiAmount = this.fixedEmiAmount;
- if (getCurrentPeriodFixedEmiAmount() != null) {
- fixedEmiAmount = getCurrentPeriodFixedEmiAmount();
- }
- return fixedEmiAmount;
- }
-
- public Integer getNthDay() {
- return this.nthDay;
- }
-
- public DayOfWeekType getWeekDayType() {
- return this.weekDayType;
- }
-
- public void setFixedEmiAmount(BigDecimal fixedEmiAmount) {
- this.fixedEmiAmount = fixedEmiAmount;
- }
-
- public void resetFixedEmiAmount() {
- this.fixedEmiAmount = this.actualFixedEmiAmount;
- }
-
- public LoanRescheduleStrategyMethod getLoanRescheduleStrategyMethod() {
- return LoanRescheduleStrategyMethod.REDUCE_EMI_AMOUNT;
- }
-
- public boolean isInterestRecalculationEnabled() {
- return this.interestRecalculationEnabled;
- }
-
- public LoanRescheduleStrategyMethod getRescheduleStrategyMethod() {
- return this.rescheduleStrategyMethod;
- }
-
- public InterestRecalculationCompoundingMethod getInterestRecalculationCompoundingMethod() {
- return this.interestRecalculationCompoundingMethod;
- }
-
- public CalendarInstance getRestCalendarInstance() {
- return this.restCalendarInstance;
- }
-
- private boolean isFallingInRepaymentPeriod(LocalDate fromDate,
- LocalDate toDate) {
- boolean isSameAsRepaymentPeriod = false;
- if (this.interestCalculationPeriodMethod.getValue().equals(
- InterestCalculationPeriodMethod.SAME_AS_REPAYMENT_PERIOD
- .getValue())) {
- switch (this.repaymentPeriodFrequencyType) {
- case WEEKS:
- int days = Days.daysBetween(fromDate, toDate).getDays();
- isSameAsRepaymentPeriod = (days % 7) == 0;
- break;
- case MONTHS:
- boolean isFromDateOnEndDate = false;
- if (fromDate.getDayOfMonth() > fromDate.plusDays(1)
- .getDayOfMonth()) {
- isFromDateOnEndDate = true;
- }
- boolean isToDateOnEndDate = false;
- if (toDate.getDayOfMonth() > toDate.plusDays(1).getDayOfMonth()) {
- isToDateOnEndDate = true;
- }
-
- if (isFromDateOnEndDate && isToDateOnEndDate) {
- isSameAsRepaymentPeriod = true;
- } else {
-
- int months = getPeriodsBetween(fromDate, toDate);
- fromDate = fromDate.plusMonths(months);
- isSameAsRepaymentPeriod = fromDate.isEqual(toDate);
- }
-
- break;
- default:
- break;
- }
- }
- return isSameAsRepaymentPeriod;
- }
-
- private Integer getPeriodsBetween(LocalDate fromDate, LocalDate toDate) {
- Integer numberOfPeriods = 0;
- PeriodType periodType = PeriodType.yearMonthDay();
- Period difference = new Period(fromDate, toDate, periodType);
- switch (this.repaymentPeriodFrequencyType) {
- case DAYS:
- numberOfPeriods = difference.getDays();
- break;
- case WEEKS:
- periodType = PeriodType.weeks();
- difference = new Period(fromDate, toDate, periodType);
- numberOfPeriods = difference.getWeeks();
- break;
- case MONTHS:
- numberOfPeriods = difference.getMonths();
- break;
- case YEARS:
- numberOfPeriods = difference.getYears();
- break;
- default:
- break;
- }
- return numberOfPeriods;
- }
-
- public RecalculationFrequencyType getRecalculationFrequencyType() {
- return this.recalculationFrequencyType;
- }
-
- public void updateNumberOfRepayments(final Integer numberOfRepayments) {
- this.numberOfRepayments = numberOfRepayments;
- this.actualNumberOfRepayments = numberOfRepayments
- + getLoanTermVariations().adjustNumberOfRepayments();
-
- }
-
- public void updatePrincipalGrace(final Integer principalGrace) {
- this.principalGrace = principalGrace;
- }
-
- public void updateInterestPaymentGrace(final Integer interestPaymentGrace) {
- this.interestPaymentGrace = interestPaymentGrace;
- }
-
- public void updateInterestRatePerPeriod(BigDecimal interestRatePerPeriod) {
- if (interestRatePerPeriod != null) {
- this.interestRatePerPeriod = interestRatePerPeriod;
- }
- }
-
- public void updateAnnualNominalInterestRate(
- BigDecimal annualNominalInterestRate) {
- if (annualNominalInterestRate != null) {
- this.annualNominalInterestRate = annualNominalInterestRate;
- }
- }
-
- public BigDecimal getAnnualNominalInterestRate() {
- return this.annualNominalInterestRate;
- }
-
- public void updateInterestChargedFromDate(LocalDate interestChargedFromDate) {
- if (interestChargedFromDate != null) {
- this.interestChargedFromDate = interestChargedFromDate;
- }
- }
-
- public void updateLoanTermFrequency(Integer loanTermFrequency) {
- if (loanTermFrequency != null) {
- this.loanTermFrequency = loanTermFrequency;
- }
- }
-
- public void updateTotalInterestDue(Money totalInterestDue) {
-
- if (totalInterestDue != null) {
- this.totalInterestDue = totalInterestDue;
- }
- }
-
- public ApplicationCurrency getApplicationCurrency() {
- return this.currency;
- }
-
- public InterestCalculationPeriodMethod getInterestCalculationPeriodMethod() {
- return this.interestCalculationPeriodMethod;
- }
-
- public LoanPreClosureInterestCalculationStrategy getPreClosureInterestCalculationStrategy() {
- return this.preClosureInterestCalculationStrategy;
- }
-
- public CalendarInstance getCompoundingCalendarInstance() {
- return this.compoundingCalendarInstance;
- }
-
- public RecalculationFrequencyType getCompoundingFrequencyType() {
- return this.compoundingFrequencyType;
- }
-
- public BigDecimal getActualFixedEmiAmount() {
- return this.actualFixedEmiAmount;
- }
-
- public Calendar getLoanCalendar() {
- return loanCalendar;
- }
-
- public BigDecimal getFixedPrincipalAmount() {
- BigDecimal fixedPrincipalAmount = this.fixedPrincipalAmount;
- if (getCurrentPeriodFixedPrincipalAmount() != null) {
- fixedPrincipalAmount = getCurrentPeriodFixedPrincipalAmount();
- }
- return fixedPrincipalAmount;
- }
-
- public LoanTermVariationsDataWrapper getLoanTermVariations() {
- return this.variationsDataWrapper;
- }
-
- public BigDecimal getCurrentPeriodFixedEmiAmount() {
- return this.currentPeriodFixedEmiAmount;
- }
-
- public void setCurrentPeriodFixedEmiAmount(
- BigDecimal currentPeriodFixedEmiAmount) {
- this.currentPeriodFixedEmiAmount = currentPeriodFixedEmiAmount;
- }
-
- public BigDecimal getCurrentPeriodFixedPrincipalAmount() {
- return this.currentPeriodFixedPrincipalAmount;
- }
-
- public void setCurrentPeriodFixedPrincipalAmount(
- BigDecimal currentPeriodFixedPrincipalAmount) {
- this.currentPeriodFixedPrincipalAmount = currentPeriodFixedPrincipalAmount;
- }
-
- public Integer fetchNumberOfRepaymentsAfterExceptions() {
- return this.actualNumberOfRepayments;
- }
-
- public LocalDate getSeedDate() {
- return this.seedDate;
- }
-
- public CalendarHistoryDataWrapper getCalendarHistoryDataWrapper() {
- return this.calendarHistoryDataWrapper;
- }
-
- public Boolean isInterestChargedFromDateSameAsDisbursalDateEnabled() {
- return this.isInterestChargedFromDateSameAsDisbursalDateEnabled;
- }
-
- public Integer getNumberOfdays() {
- return numberOfDays;
- }
-
- public boolean isSkipRepaymentOnFirstDayofMonth() {
- return isSkipRepaymentOnFirstDayOfMonth;
- }
+ /**
+ * Legacy method of support 'grace' on the charging of interest on a loan.
+ *
+ * <p>
+ * For the typical structured loan, its reasonable to use an integer to
+ * indicate the number of 'repayment frequency' periods the 'grace' should
+ * apply to but for slightly <b>irregular</b> loans where the period between
+ * disbursement and the date of the 'first repayment period' isnt doest
+ * match the 'repayment frequency' but can be less (15days instead of 1
+ * month) or more (6 weeks instead of 1 month) - The idea was to use a date
+ * to indicate from whence interest should be charged.
+ * </p>
+ */
+ private LocalDate interestChargedFromDate;
+ private final Money inArrearsTolerance;
+
+ private final Integer graceOnArrearsAgeing;
+
+ // added
+ private LocalDate loanEndDate;
+ private final List<DisbursementData> disbursementDatas;
+
+ private final boolean multiDisburseLoan;
+
+ private BigDecimal fixedEmiAmount;
+
+ private BigDecimal fixedPrincipalAmount;
+
+ private BigDecimal currentPeriodFixedEmiAmount;
+
+ private BigDecimal currentPeriodFixedPrincipalAmount;
+
+ private final BigDecimal actualFixedEmiAmount;
+
+ private final BigDecimal maxOutstandingBalance;
+
+ private Money totalInterestDue;
+
+ private final DaysInMonthType daysInMonthType;
+
+ private final DaysInYearType daysInYearType;
+
+ private final boolean interestRecalculationEnabled;
+
+ private final LoanRescheduleStrategyMethod rescheduleStrategyMethod;
+
+ private final InterestRecalculationCompoundingMethod interestRecalculationCompoundingMethod;
+
+ private final CalendarInstance restCalendarInstance;
+
+ private final RecalculationFrequencyType recalculationFrequencyType;
+
+ private final CalendarInstance compoundingCalendarInstance;
+
+ private final RecalculationFrequencyType compoundingFrequencyType;
+ private final boolean allowCompoundingOnEod;
+
+ private final BigDecimal principalThresholdForLastInstalment;
+ private final Integer installmentAmountInMultiplesOf;
+
+ private final LoanPreClosureInterestCalculationStrategy preClosureInterestCalculationStrategy;
+
+ private Money approvedPrincipal = null;
+
+ private final LoanTermVariationsDataWrapper variationsDataWrapper;
+
+ private Money adjustPrincipalForFlatLoans;
+
+ private final LocalDate seedDate;
+
+ private final CalendarHistoryDataWrapper calendarHistoryDataWrapper;
+
+ private final Boolean isInterestChargedFromDateSameAsDisbursalDateEnabled;
+
+ private final Integer numberOfDays;
+
+ private final boolean isSkipRepaymentOnFirstDayOfMonth;
+
+ private final HolidayDetailDTO holidayDetailDTO;
+
+ public static LoanApplicationTerms assembleFrom(final ApplicationCurrency currency, final Integer loanTermFrequency,
+ final PeriodFrequencyType loanTermPeriodFrequencyType, final Integer numberOfRepayments, final Integer repaymentEvery,
+ final PeriodFrequencyType repaymentPeriodFrequencyType, Integer nthDay, DayOfWeekType weekDayType,
+ final AmortizationMethod amortizationMethod, final InterestMethod interestMethod, final BigDecimal interestRatePerPeriod,
+ final PeriodFrequencyType interestRatePeriodFrequencyType, final BigDecimal annualNominalInterestRate,
+ final InterestCalculationPeriodMethod interestCalculationPeriodMethod, final boolean allowPartialPeriodInterestCalcualtion,
+ final Money principalMoney, final LocalDate expectedDisbursementDate, final LocalDate repaymentsStartingFromDate,
+ final LocalDate calculatedRepaymentsStartingFromDate, final Integer graceOnPrincipalPayment,
+ final Integer recurringMoratoriumOnPrincipalPeriods, final Integer graceOnInterestPayment,
+ final Integer graceOnInterestCharged, final LocalDate interestChargedFromDate, final Money inArrearsTolerance,
+ final boolean multiDisburseLoan, final BigDecimal emiAmount, final List<DisbursementData> disbursementDatas,
+ final BigDecimal maxOutstandingBalance, final Integer graceOnArrearsAgeing, final DaysInMonthType daysInMonthType,
+ final DaysInYearType daysInYearType, final boolean isInterestRecalculationEnabled,
+ final RecalculationFrequencyType recalculationFrequencyType, final CalendarInstance restCalendarInstance,
+ final InterestRecalculationCompoundingMethod interestRecalculationCompoundingMethod,
+ final CalendarInstance compoundingCalendarInstance, final RecalculationFrequencyType compoundingFrequencyType,
+ final BigDecimal principalThresholdForLastInstalment, final Integer installmentAmountInMultiplesOf,
+ final LoanPreClosureInterestCalculationStrategy preClosureInterestCalculationStrategy, final Calendar loanCalendar,
+ BigDecimal approvedAmount, List<LoanTermVariationsData> loanTermVariations,
+ Boolean isInterestChargedFromDateSameAsDisbursalDateEnabled, final Integer numberOfdays,
+ boolean isSkipRepaymentOnFirstDayofMonth, final HolidayDetailDTO holidayDetailDTO, final boolean allowCompoundingOnEod) {
+
+ final LoanRescheduleStrategyMethod rescheduleStrategyMethod = null;
+ final CalendarHistoryDataWrapper calendarHistoryDataWrapper = null;
+ return new LoanApplicationTerms(currency, loanTermFrequency, loanTermPeriodFrequencyType, numberOfRepayments, repaymentEvery,
+ repaymentPeriodFrequencyType, nthDay, weekDayType, amortizationMethod, interestMethod, interestRatePerPeriod,
+ interestRatePeriodFrequencyType, annualNominalInterestRate, interestCalculationPeriodMethod,
+ allowPartialPeriodInterestCalcualtion, principalMoney, expectedDisbursementDate, repaymentsStartingFromDate,
+ calculatedRepaymentsStartingFromDate, graceOnPrincipalPayment, recurringMoratoriumOnPrincipalPeriods, graceOnInterestPayment, graceOnInterestCharged,
+ interestChargedFromDate, inArrearsTolerance, multiDisburseLoan, emiAmount, disbursementDatas, maxOutstandingBalance,
+ graceOnArrearsAgeing, daysInMonthType, daysInYearType, isInterestRecalculationEnabled, rescheduleStrategyMethod,
+ interestRecalculationCompoundingMethod, restCalendarInstance, recalculationFrequencyType, compoundingCalendarInstance,
+ compoundingFrequencyType, principalThresholdForLastInstalment, installmentAmountInMultiplesOf,
+ preClosureInterestCalculationStrategy, loanCalendar, approvedAmount, loanTermVariations, calendarHistoryDataWrapper,
+ isInterestChargedFromDateSameAsDisbursalDateEnabled, numberOfdays, isSkipRepaymentOnFirstDayofMonth, holidayDetailDTO,
+ allowCompoundingOnEod);
+
+ }
+
+ public static LoanApplicationTerms assembleFrom(final ApplicationCurrency applicationCurrency, final Integer loanTermFrequency,
+ final PeriodFrequencyType loanTermPeriodFrequencyType, NthDayType nthDay, DayOfWeekType dayOfWeek,
+ final LocalDate expectedDisbursementDate, final LocalDate repaymentsStartingFromDate,
+ final LocalDate calculatedRepaymentsStartingFromDate, final Money inArrearsTolerance,
+ final LoanProductRelatedDetail loanProductRelatedDetail, final boolean multiDisburseLoan, final BigDecimal emiAmount,
+ final List<DisbursementData> disbursementDatas, final BigDecimal maxOutstandingBalance,
+ final LocalDate interestChargedFromDate, final BigDecimal principalThresholdForLastInstalment,
+ final Integer installmentAmountInMultiplesOf, final RecalculationFrequencyType recalculationFrequencyType,
+ final CalendarInstance restCalendarInstance, final InterestRecalculationCompoundingMethod compoundingMethod,
+ final CalendarInstance compoundingCalendarInstance, final RecalculationFrequencyType compoundingFrequencyType,
+ final LoanPreClosureInterestCalculationStrategy loanPreClosureInterestCalculationStrategy,
+ final LoanRescheduleStrategyMethod rescheduleStrategyMethod, BigDecimal approvedAmount, BigDecimal annualNominalInterestRate,
+ List<LoanTermVariationsData> loanTermVariations, final Integer numberOfdays, final boolean isSkipRepaymentOnFirstDayofMonth,
+ final Calendar loanCalendar, final HolidayDetailDTO holidayDetailDTO, final boolean allowCompoundingOnEod) {
+ final CalendarHistoryDataWrapper calendarHistoryDataWrapper = null;
+
+ return assembleFrom(applicationCurrency, loanTermFrequency, loanTermPeriodFrequencyType, nthDay, dayOfWeek,
+ expectedDisbursementDate, repaymentsStartingFromDate, calculatedRepaymentsStartingFromDate, inArrearsTolerance,
+ loanProductRelatedDetail, multiDisburseLoan, emiAmount, disbursementDatas, maxOutstandingBalance, interestChargedFromDate,
+ principalThresholdForLastInstalment, installmentAmountInMultiplesOf, recalculationFrequencyType, restCalendarInstance,
+ compoundingMethod, compoundingCalendarInstance, compoundingFrequencyType, loanPreClosureInterestCalculationStrategy,
+ rescheduleStrategyMethod, loanCalendar, approvedAmount, annualNominalInterestRate, loanTermVariations,
+ calendarHistoryDataWrapper, numberOfdays, isSkipRepaymentOnFirstDayofMonth, holidayDetailDTO, allowCompoundingOnEod);
+ }
+
+ public static LoanApplicationTerms assembleFrom(final ApplicationCurrency applicationCurrency, final Integer loanTermFrequency,
+ final PeriodFrequencyType loanTermPeriodFrequencyType, NthDayType nthDay, DayOfWeekType dayOfWeek,
+ final LocalDate expectedDisbursementDate, final LocalDate repaymentsStartingFromDate,
+ final LocalDate calculatedRepaymentsStartingFromDate, final Money inArrearsTolerance,
+ final LoanProductRelatedDetail loanProductRelatedDetail, final boolean multiDisburseLoan, final BigDecimal emiAmount,
+ final List<DisbursementData> disbursementDatas, final BigDecimal maxOutstandingBalance,
+ final LocalDate interestChargedFromDate, final BigDecimal principalThresholdForLastInstalment,
+ final Integer installmentAmountInMultiplesOf, final RecalculationFrequencyType recalculationFrequencyType,
+ final CalendarInstance restCalendarInstance, final InterestRecalculationCompoundingMethod compoundingMethod,
+ final CalendarInstance compoundingCalendarInstance, final RecalculationFrequencyType compoundingFrequencyType,
+ final LoanPreClosureInterestCalculationStrategy loanPreClosureInterestCalculationStrategy,
+ final LoanRescheduleStrategyMethod rescheduleStrategyMethod, final Calendar loanCalendar, BigDecimal approvedAmount,
+ BigDecimal annualNominalInterestRate, final List<LoanTermVariationsData> loanTermVariations,
+ final CalendarHistoryDataWrapper calendarHistoryDataWrapper, final Integer numberOfdays,
+ final boolean isSkipRepaymentOnFirstDayofMonth, final HolidayDetailDTO holidayDetailDTO, final boolean allowCompoundingOnEod) {
+
+ final Integer numberOfRepayments = loanProductRelatedDetail.getNumberOfRepayments();
+ final Integer repaymentEvery = loanProductRelatedDetail.getRepayEvery();
+ final PeriodFrequencyType repaymentPeriodFrequencyType = loanProductRelatedDetail.getRepaymentPeriodFrequencyType();
+ final AmortizationMethod amortizationMethod = loanProductRelatedDetail.getAmortizationMethod();
+ final InterestMethod interestMethod = loanProductRelatedDetail.getInterestMethod();
+ final BigDecimal interestRatePerPeriod = loanProductRelatedDetail.getNominalInterestRatePerPeriod();
+ final PeriodFrequencyType interestRatePeriodFrequencyType = loanProductRelatedDetail.getInterestPeriodFrequencyType();
+ final InterestCalculationPeriodMethod interestCalculationPeriodMethod = loanProductRelatedDetail
+ .getInterestCalculationPeriodMethod();
+ final boolean allowPartialPeriodInterestCalcualtion = loanProductRelatedDetail.isAllowPartialPeriodInterestCalcualtion();
+ final Money principalMoney = loanProductRelatedDetail.getPrincipal();
+
+ //
+ final Integer graceOnPrincipalPayment = loanProductRelatedDetail.graceOnPrincipalPayment();
+ final Integer recurringMoratoriumOnPrincipalPeriods = loanProductRelatedDetail.recurringMoratoriumOnPrincipalPeriods();
+ final Integer graceOnInterestPayment = loanProductRelatedDetail.graceOnInterestPayment();
+ final Integer graceOnInterestCharged = loanProductRelatedDetail.graceOnInterestCharged();
+
+ // Interest recalculation settings
+ final DaysInMonthType daysInMonthType = loanProductRelatedDetail.fetchDaysInMonthType();
+ final DaysInYearType daysInYearType = loanProductRelatedDetail.fetchDaysInYearType();
+ final boolean isInterestRecalculationEnabled = loanProductRelatedDetail.isInterestRecalculationEnabled();
+ final boolean isInterestChargedFromDateSameAsDisbursalDateEnabled = false;
+ return new LoanApplicatio
<TRUNCATED>