http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/437de26c/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 index d2341ba..7be11d3 100755 --- 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 @@ -57,1424 +57,1864 @@ import org.joda.time.Years; public final class LoanApplicationTerms { - 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 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 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 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 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, 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 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, 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 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, 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.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 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.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() { - return this.actualNumberOfRepayments - getPrincipalGrace(); - } - - private Integer calculateNumberOfRepaymentPeriodsWhereInterestPaymentIsDue(final Integer totalNumberOfRepaymentPeriods) { - return totalNumberOfRepaymentPeriods - Math.max(getInterestChargingGrace(), getInterestPaymentGrace()); - } - - private Integer calculateNumberOfPrincipalPaymentPeriods(final Integer totalNumberOfRepaymentPeriods) { - return totalNumberOfRepaymentPeriods - getPrincipalGrace(); - } - - public boolean isPrincipalGraceApplicableForThisPeriod(final int periodNumber) { - 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 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 = this.actualNumberOfRepayments - 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 = calculateNumberOfPrincipalPaymentPeriods(this.actualNumberOfRepayments); - 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 = calculateNumberOfPrincipalPaymentPeriods(this.actualNumberOfRepayments); - Money principal = outstandingAmount.dividedBy(numberOfPrincipalPaymentPeriods - periodNumber + 1, mc.getRoundingMode()); - this.fixedPrincipalAmount = principal.getAmount(); - } - - 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.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 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 res
<TRUNCATED>