Example usage for org.joda.time LocalDate isBefore

List of usage examples for org.joda.time LocalDate isBefore

Introduction

In this page you can find the example usage for org.joda.time LocalDate isBefore.

Prototype

public boolean isBefore(ReadablePartial partial) 

Source Link

Document

Is this partial earlier than the specified partial.

Usage

From source file:org.apache.fineract.portfolio.loanaccount.loanschedule.domain.AbstractLoanScheduleGenerator.java

License:Apache License

/**
 * this Method updates late/ not paid installment components to Map with
 * effective date as per REST(for principal portion ) and compounding
 * (interest or fee or interest and fee portions) frequency
 * //from w w  w. j  a v  a 2  s . c o m
 */
private void updateLatePaymentsToMap(final LoanApplicationTerms loanApplicationTerms,
        final HolidayDetailDTO holidayDetailDTO, final MonetaryCurrency currency,
        final Map<LocalDate, Money> latePaymentMap, final LocalDate scheduledDueDate,
        List<LoanRepaymentScheduleInstallment> installments, boolean applyRestFrequencyForPrincipal,
        final LocalDate lastRestDate, final TreeMap<LocalDate, Money> compoundingMap) {
    latePaymentMap.clear();
    LocalDate currentDate = DateUtils.getLocalDateOfTenant();

    Money totalCompoundingAmount = Money.zero(currency);
    Money compoundedMoney = Money.zero(currency);
    if (!compoundingMap.isEmpty()) {
        compoundedMoney = compoundingMap.get(lastRestDate);
    }
    boolean clearCompoundingMap = true;
    for (LoanRepaymentScheduleInstallment loanRepaymentScheduleInstallment : installments) {
        if (loanRepaymentScheduleInstallment.isNotFullyPaidOff()
                && !loanRepaymentScheduleInstallment.getDueDate().isAfter(scheduledDueDate)
                && !loanRepaymentScheduleInstallment.isRecalculatedInterestComponent()) {
            LocalDate principalEffectiveDate = loanRepaymentScheduleInstallment.getDueDate();
            if (applyRestFrequencyForPrincipal) {
                principalEffectiveDate = getNextRestScheduleDate(
                        loanRepaymentScheduleInstallment.getDueDate().minusDays(1), loanApplicationTerms,
                        holidayDetailDTO);
            }
            if (principalEffectiveDate.isBefore(currentDate)) {
                updateMapWithAmount(latePaymentMap,
                        loanRepaymentScheduleInstallment.getPrincipalOutstanding(currency),
                        principalEffectiveDate);
                totalCompoundingAmount = totalCompoundingAmount
                        .plus(loanRepaymentScheduleInstallment.getPrincipalOutstanding(currency));
            }

            final Money changedCompoundedMoney = updateMapWithCompoundingDetails(loanApplicationTerms,
                    holidayDetailDTO, currency, compoundingMap, loanRepaymentScheduleInstallment, lastRestDate,
                    compoundedMoney, scheduledDueDate);
            if (compoundedMoney.isZero() || !compoundedMoney.isEqualTo(changedCompoundedMoney)) {
                compoundedMoney = changedCompoundedMoney;
                clearCompoundingMap = false;
            }
        }
    }
    if (totalCompoundingAmount.isGreaterThanZero()) {
        updateMapWithAmount(latePaymentMap, totalCompoundingAmount.negated(), lastRestDate);
    }
    if (clearCompoundingMap) {
        compoundingMap.clear();
    }
}

From source file:org.apache.fineract.portfolio.loanaccount.loanschedule.domain.AbstractLoanScheduleGenerator.java

License:Apache License

private Money updateMapWithCompoundingDetails(final LoanApplicationTerms loanApplicationTerms,
        final HolidayDetailDTO holidayDetailDTO, final MonetaryCurrency currency,
        final TreeMap<LocalDate, Money> compoundingMap,
        final LoanRepaymentScheduleInstallment loanRepaymentScheduleInstallment, final LocalDate lastRestDate,
        final Money compoundedMoney, final LocalDate scheduledDueDate) {
    Money ignoreMoney = compoundedMoney;
    if (loanApplicationTerms.getInterestRecalculationCompoundingMethod().isCompoundingEnabled()) {
        LocalDate compoundingEffectiveDate = getNextCompoundScheduleDate(
                loanRepaymentScheduleInstallment.getDueDate().minusDays(1), loanApplicationTerms,
                holidayDetailDTO);// w ww.  j a v a  2s .c  om

        if (compoundingEffectiveDate.isBefore(DateUtils.getLocalDateOfTenant())) {
            Money amount = Money.zero(currency);
            switch (loanApplicationTerms.getInterestRecalculationCompoundingMethod()) {
            case INTEREST:
                amount = amount.plus(loanRepaymentScheduleInstallment.getInterestOutstanding(currency));
                break;
            case FEE:
                amount = amount.plus(loanRepaymentScheduleInstallment.getFeeChargesOutstanding(currency));
                amount = amount.plus(loanRepaymentScheduleInstallment.getPenaltyChargesOutstanding(currency));
                break;
            case INTEREST_AND_FEE:
                amount = amount.plus(loanRepaymentScheduleInstallment.getInterestOutstanding(currency));
                amount = amount.plus(loanRepaymentScheduleInstallment.getFeeChargesOutstanding(currency));
                amount = amount.plus(loanRepaymentScheduleInstallment.getPenaltyChargesOutstanding(currency));
                break;
            default:
                break;
            }
            if (compoundingEffectiveDate.isBefore(scheduledDueDate)) {
                ignoreMoney = ignoreMoney.plus(amount);
                if (ignoreMoney.isGreaterThanZero()) {
                    updateMapWithAmount(compoundingMap, ignoreMoney, compoundingEffectiveDate);
                    updateMapWithAmount(compoundingMap, ignoreMoney.negated(), lastRestDate);
                    ignoreMoney = ignoreMoney.zero();
                }
            } else {
                if (ignoreMoney.isLessThanZero()) {
                    LocalDate firstKey = compoundingMap.firstKey();
                    updateMapWithAmount(compoundingMap, ignoreMoney, firstKey);
                    updateMapWithAmount(compoundingMap, ignoreMoney.negated(), lastRestDate);
                    ignoreMoney = ignoreMoney.zero();
                }
                updateMapWithAmount(compoundingMap, amount, compoundingEffectiveDate);
                updateMapWithAmount(compoundingMap, amount.negated(), lastRestDate);
            }
        }
    }
    return ignoreMoney;
}

From source file:org.apache.fineract.portfolio.loanaccount.loanschedule.domain.AbstractLoanScheduleGenerator.java

License:Apache License

private void populateCompoundingDatesInPeriod(final LocalDate startDate, final LocalDate endDate,
        final LocalDate currentDate, final LoanApplicationTerms loanApplicationTerms,
        final HolidayDetailDTO holidayDetailDTO, final Map<LocalDate, Money> compoundingMap,
        final Set<LoanCharge> charges, MonetaryCurrency currency) {
    if (loanApplicationTerms.getInterestRecalculationCompoundingMethod().isCompoundingEnabled()) {
        LocalDate lastCompoundingDate = startDate;
        LocalDate compoundingDate = startDate;
        while (compoundingDate.isBefore(endDate) && compoundingDate.isBefore(currentDate)) {
            compoundingDate = getNextCompoundScheduleDate(compoundingDate, loanApplicationTerms,
                    holidayDetailDTO);/*from www. j a v  a 2  s.com*/
            if (!compoundingDate.isBefore(currentDate)) {
                break;
            } else if (compoundingDate.isAfter(endDate)) {
                updateMapWithAmount(compoundingMap, Money.zero(currency), compoundingDate);
            } else {
                Money feeChargesForInstallment = cumulativeFeeChargesDueWithin(lastCompoundingDate,
                        compoundingDate, charges, currency, null, loanApplicationTerms.getPrincipal(), null,
                        false);
                Money penaltyChargesForInstallment = cumulativePenaltyChargesDueWithin(lastCompoundingDate,
                        compoundingDate, charges, currency, null, loanApplicationTerms.getPrincipal(), null,
                        false);
                updateMapWithAmount(compoundingMap, feeChargesForInstallment.plus(penaltyChargesForInstallment),
                        compoundingDate);
            }
            lastCompoundingDate = compoundingDate;
        }
    }
}

From source file:org.apache.fineract.portfolio.loanaccount.loanschedule.domain.AbstractLoanScheduleGenerator.java

License:Apache License

protected void clearMapDetails(final LocalDate startDate, final Map<LocalDate, Money> compoundingMap) {
    Map<LocalDate, Money> temp = new HashMap<>();
    for (LocalDate date : compoundingMap.keySet()) {
        if (!date.isBefore(startDate)) {
            temp.put(date, compoundingMap.get(date));
        }//from   w ww  . ja v a2  s.com
    }
    compoundingMap.clear();
    compoundingMap.putAll(temp);
}

From source file:org.apache.fineract.portfolio.loanaccount.loanschedule.domain.AbstractLoanScheduleGenerator.java

License:Apache License

/**
 * calculates Interest stating date as per the settings
 * /*from  w w w  .java  2 s  .c o  m*/
 * @param firstRepaymentdate
 *            TODO
 */
private LocalDate calculateInterestStartDateForPeriod(final LoanApplicationTerms loanApplicationTerms,
        LocalDate periodStartDate, final LocalDate idealDisbursementDate, final LocalDate firstRepaymentdate) {
    LocalDate periodStartDateApplicableForInterest = periodStartDate;
    if (periodStartDate.isBefore(idealDisbursementDate) || firstRepaymentdate.isAfter(periodStartDate)) {
        if (loanApplicationTerms.getInterestChargedFromLocalDate() != null) {
            if (periodStartDate.isEqual(loanApplicationTerms.getExpectedDisbursementDate())
                    || loanApplicationTerms.getInterestChargedFromLocalDate().isAfter(periodStartDate)) {
                periodStartDateApplicableForInterest = loanApplicationTerms.getInterestChargedFromLocalDate();
            }
        } else if (periodStartDate.isEqual(loanApplicationTerms.getExpectedDisbursementDate())) {
            periodStartDateApplicableForInterest = idealDisbursementDate;
        }
    }
    return periodStartDateApplicableForInterest;
}

From source file:org.apache.fineract.portfolio.loanaccount.loanschedule.domain.AbstractLoanScheduleGenerator.java

License:Apache License

private LoanScheduleDTO rescheduleNextInstallments(final MathContext mc,
        final LoanApplicationTerms loanApplicationTerms, final Set<LoanCharge> loanCharges,
        final HolidayDetailDTO holidayDetailDTO, final List<LoanTransaction> transactions,
        final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor,
        final List<LoanRepaymentScheduleInstallment> repaymentScheduleInstallments,
        final LocalDate rescheduleFrom, final LocalDate scheduleTillDate) {
    // Loan transactions to process and find the variation on payments
    Collection<RecalculationDetail> recalculationDetails = new ArrayList<>();
    for (LoanTransaction loanTransaction : transactions) {
        recalculationDetails.add(new RecalculationDetail(loanTransaction.getTransactionDate(),
                LoanTransaction.copyTransactionProperties(loanTransaction)));
    }/*w  w w. j  a  v a 2s . c om*/
    final boolean applyInterestRecalculation = loanApplicationTerms.isInterestRecalculationEnabled();

    LoanScheduleParams loanScheduleParams = null;
    Collection<LoanScheduleModelPeriod> periods = new ArrayList<>();
    final List<LoanRepaymentScheduleInstallment> retainedInstallments = new ArrayList<>();

    // this block is to retain the schedule installments prior to the
    // provided date and creates late and early payment details for further
    // calculations
    if (rescheduleFrom != null) {
        Money principalToBeScheduled = getPrincipalToBeScheduled(loanApplicationTerms);
        // actual outstanding balance for interest calculation
        Money outstandingBalance = principalToBeScheduled;
        // total outstanding balance as per rest for interest calculation.
        Money outstandingBalanceAsPerRest = outstandingBalance;

        // this is required to update total fee amounts in the
        // LoanScheduleModel
        final BigDecimal chargesDueAtTimeOfDisbursement = deriveTotalChargesDueAtTimeOfDisbursement(
                loanCharges);
        periods = createNewLoanScheduleListWithDisbursementDetails(
                loanApplicationTerms.fetchNumberOfRepaymentsAfterExceptions(), loanApplicationTerms,
                chargesDueAtTimeOfDisbursement);
        final List<LoanRepaymentScheduleInstallment> newRepaymentScheduleInstallments = new ArrayList<>();
        MonetaryCurrency currency = outstandingBalance.getCurrency();

        // early payments will be added here and as per the selected
        // strategy
        // action will be performed on this value
        Money reducePrincipal = outstandingBalanceAsPerRest.zero();

        // principal changes will be added along with date(after applying
        // rest)
        // from when these amounts will effect the outstanding balance for
        // interest calculation
        final Map<LocalDate, Money> principalPortionMap = new HashMap<>();
        // compounding(principal) amounts will be added along with
        // date(after applying compounding frequency)
        // from when these amounts will effect the outstanding balance for
        // interest calculation
        final Map<LocalDate, Money> latePaymentMap = new HashMap<>();

        // compounding(interest/Fee) amounts will be added along with
        // date(after applying compounding frequency)
        // from when these amounts will effect the outstanding balance for
        // interest calculation
        final TreeMap<LocalDate, Money> compoundingMap = new TreeMap<>();
        LocalDate currentDate = DateUtils.getLocalDateOfTenant();
        LocalDate lastRestDate = currentDate;
        if (loanApplicationTerms.getRestCalendarInstance() != null) {
            lastRestDate = getNextRestScheduleDate(currentDate.minusDays(1), loanApplicationTerms,
                    holidayDetailDTO);
        }
        LocalDate actualRepaymentDate = loanApplicationTerms.getExpectedDisbursementDate();
        boolean isFirstRepayment = true;

        // cumulative fields
        Money totalCumulativePrincipal = principalToBeScheduled.zero();
        Money totalCumulativeInterest = principalToBeScheduled.zero();
        Money totalFeeChargesCharged = principalToBeScheduled.zero().plus(chargesDueAtTimeOfDisbursement);
        Money totalPenaltyChargesCharged = principalToBeScheduled.zero();
        Money totalRepaymentExpected = principalToBeScheduled.zero();

        // Actual period Number as per the schedule
        int periodNumber = 1;
        // Actual period Number plus interest only repayments
        int instalmentNumber = 1;
        LocalDate lastInstallmentDate = actualRepaymentDate;
        LocalDate periodStartDate = loanApplicationTerms.getExpectedDisbursementDate();
        // Set fixed Amortization Amounts(either EMI or Principal )
        updateAmortization(mc, loanApplicationTerms, periodNumber, outstandingBalance);

        final Map<LocalDate, Money> disburseDetailMap = new HashMap<>();
        if (loanApplicationTerms.isMultiDisburseLoan()) {
            // fetches the first tranche amount and also updates other
            // tranche
            // details to map
            BigDecimal disburseAmt = getDisbursementAmount(loanApplicationTerms,
                    loanApplicationTerms.getExpectedDisbursementDate(), periods, chargesDueAtTimeOfDisbursement,
                    disburseDetailMap, true);
            outstandingBalance = outstandingBalance.zero().plus(disburseAmt);
            outstandingBalanceAsPerRest = outstandingBalance;
            principalToBeScheduled = principalToBeScheduled.zero().plus(disburseAmt);
        }
        int loanTermInDays = 0;

        // Block process the installment and creates the period if it falls
        // before reschedule from date
        // This will create the recalculation details by applying the
        // transactions
        for (LoanRepaymentScheduleInstallment installment : repaymentScheduleInstallments) {
            // this will generate the next schedule due date and allows to
            // process the installment only if recalculate from date is
            // greater than due date
            if (installment.getDueDate().isAfter(lastInstallmentDate)) {
                LocalDate previousRepaymentDate = actualRepaymentDate;
                actualRepaymentDate = this.scheduledDateGenerator.generateNextRepaymentDate(actualRepaymentDate,
                        loanApplicationTerms, isFirstRepayment, holidayDetailDTO);
                isFirstRepayment = false;
                lastInstallmentDate = this.scheduledDateGenerator.adjustRepaymentDate(actualRepaymentDate,
                        loanApplicationTerms, holidayDetailDTO);
                if (!lastInstallmentDate.isBefore(rescheduleFrom)) {
                    actualRepaymentDate = previousRepaymentDate;
                    break;
                }
                periodNumber++;
                // check for date changes
                while (loanApplicationTerms.getLoanTermVariations().hasDueDateVariation(lastInstallmentDate)) {
                    LoanTermVariationsData variation = loanApplicationTerms.getLoanTermVariations()
                            .nextDueDateVariation();
                    if (!variation.isSpecificToInstallment()) {
                        actualRepaymentDate = variation.getDateValue();
                    }
                    variation.setProcessed(true);
                }
            }

            for (Map.Entry<LocalDate, Money> disburseDetail : disburseDetailMap.entrySet()) {
                if (disburseDetail.getKey().isAfter(installment.getFromDate())
                        && !disburseDetail.getKey().isAfter(installment.getDueDate())) {
                    // creates and add disbursement detail to the repayments
                    // period
                    final LoanScheduleModelDisbursementPeriod disbursementPeriod = LoanScheduleModelDisbursementPeriod
                            .disbursement(disburseDetail.getKey(), disburseDetail.getValue(),
                                    chargesDueAtTimeOfDisbursement);
                    periods.add(disbursementPeriod);
                    // updates actual outstanding balance with new
                    // disbursement detail
                    outstandingBalance = outstandingBalance.plus(disburseDetail.getValue());
                    principalToBeScheduled = principalToBeScheduled.plus(disburseDetail.getValue());
                }
            }

            // calculation of basic fields to start the schedule generation
            // from the middle
            periodStartDate = installment.getDueDate();
            installment.resetDerivedComponents();
            newRepaymentScheduleInstallments.add(installment);
            outstandingBalance = outstandingBalance.minus(installment.getPrincipal(currency));
            final LoanScheduleModelPeriod loanScheduleModelPeriod = createLoanScheduleModelPeriod(installment,
                    outstandingBalance);
            periods.add(loanScheduleModelPeriod);
            totalCumulativePrincipal = totalCumulativePrincipal.plus(installment.getPrincipal(currency));
            totalCumulativeInterest = totalCumulativeInterest.plus(installment.getInterestCharged(currency));
            totalFeeChargesCharged = totalFeeChargesCharged.plus(installment.getFeeChargesCharged(currency));
            totalPenaltyChargesCharged = totalPenaltyChargesCharged
                    .plus(installment.getPenaltyChargesCharged(currency));
            instalmentNumber++;
            loanTermInDays = Days.daysBetween(installment.getFromDate(), installment.getDueDate()).getDays();

            // populates the collection with transactions till the due date
            // of
            // the period for interest recalculation enabled loans
            Collection<RecalculationDetail> applicableTransactions = getApplicableTransactionsForPeriod(
                    applyInterestRecalculation, installment.getDueDate(), recalculationDetails);

            // calculates the expected principal value for this repayment
            // schedule
            Money principalPortionCalculated = principalToBeScheduled.zero();
            if (!installment.isRecalculatedInterestComponent()) {
                principalPortionCalculated = calculateExpectedPrincipalPortion(
                        installment.getInterestCharged(currency), loanApplicationTerms);
            }

            // expected principal considering the previously paid excess
            // amount
            Money actualPrincipalPortion = principalPortionCalculated.minus(reducePrincipal);
            if (actualPrincipalPortion.isLessThanZero()) {
                actualPrincipalPortion = principalPortionCalculated.zero();
            }

            Money unprocessed = updateEarlyPaidAmountsToMap(loanApplicationTerms, holidayDetailDTO,
                    loanRepaymentScheduleTransactionProcessor, newRepaymentScheduleInstallments, currency,
                    principalPortionMap, installment, applicableTransactions, actualPrincipalPortion);

            // this block is to adjust the period number based on the actual
            // schedule due date and installment due date
            // recalculatedInterestComponent installment shouldn't be
            // considered while calculating fixed EMI amounts
            int period = periodNumber;
            if (!lastInstallmentDate.isEqual(installment.getDueDate())) {
                period--;
            }
            reducePrincipal = fetchEarlyPaidAmount(installment.getPrincipal(currency),
                    principalPortionCalculated, reducePrincipal, loanApplicationTerms, totalCumulativePrincipal,
                    period, mc);
            // Updates principal paid map with efective date for reducing
            // the amount from outstanding balance(interest calculation)
            LocalDate amountApplicableDate = getNextRestScheduleDate(installment.getDueDate().minusDays(1),
                    loanApplicationTerms, holidayDetailDTO);
            // updates map with the installment principal amount excluding
            // unprocessed amount since this amount is already accounted.
            updateMapWithAmount(principalPortionMap, installment.getPrincipal(currency).minus(unprocessed),
                    amountApplicableDate);
            // update outstanding balance for interest calculation
            outstandingBalanceAsPerRest = updateBalanceForInterestCalculation(principalPortionMap,
                    installment.getDueDate(), outstandingBalanceAsPerRest, false);
            outstandingBalanceAsPerRest = updateBalanceForInterestCalculation(disburseDetailMap,
                    installment.getDueDate(), outstandingBalanceAsPerRest, true);

        }
        totalRepaymentExpected = totalCumulativePrincipal.plus(totalCumulativeInterest)
                .plus(totalFeeChargesCharged).plus(totalPenaltyChargesCharged);

        // updates the map with over due amounts
        updateLatePaymentsToMap(loanApplicationTerms, holidayDetailDTO, currency, latePaymentMap,
                lastInstallmentDate, newRepaymentScheduleInstallments, true, lastRestDate, compoundingMap);

        // for partial schedule generation
        if (!newRepaymentScheduleInstallments.isEmpty() && totalCumulativeInterest.isGreaterThanZero()) {
            Money totalOutstandingInterestPaymentDueToGrace = Money.zero(currency);
            loanScheduleParams = LoanScheduleParams.createLoanScheduleParamsForPartialUpdate(periodNumber,
                    instalmentNumber, loanTermInDays, periodStartDate, actualRepaymentDate,
                    totalCumulativePrincipal, totalCumulativeInterest, totalFeeChargesCharged,
                    totalPenaltyChargesCharged, totalRepaymentExpected,
                    totalOutstandingInterestPaymentDueToGrace, reducePrincipal, principalPortionMap,
                    latePaymentMap, compoundingMap, disburseDetailMap, principalToBeScheduled,
                    outstandingBalance, outstandingBalanceAsPerRest, newRepaymentScheduleInstallments,
                    recalculationDetails, loanRepaymentScheduleTransactionProcessor, scheduleTillDate, currency,
                    applyInterestRecalculation);
            retainedInstallments.addAll(newRepaymentScheduleInstallments);
        }

    }
    // for complete schedule generation
    if (loanScheduleParams == null) {
        loanScheduleParams = LoanScheduleParams.createLoanScheduleParamsForCompleteUpdate(recalculationDetails,
                loanRepaymentScheduleTransactionProcessor, scheduleTillDate, applyInterestRecalculation);
    }

    LoanScheduleModel loanScheduleModel = generate(mc, loanApplicationTerms, loanCharges, holidayDetailDTO,
            loanScheduleParams);
    for (LoanScheduleModelPeriod loanScheduleModelPeriod : loanScheduleModel.getPeriods()) {
        if (loanScheduleModelPeriod.isRepaymentPeriod()) {
            // adding newly created repayment periods to installments
            addLoanRepaymentScheduleInstallment(retainedInstallments, loanScheduleModelPeriod);
        }
    }
    periods.addAll(loanScheduleModel.getPeriods());
    LoanScheduleModel loanScheduleModelwithPeriodChanges = LoanScheduleModel
            .withLoanScheduleModelPeriods(periods, loanScheduleModel);
    return LoanScheduleDTO.from(retainedInstallments, loanScheduleModelwithPeriodChanges);
}

From source file:org.apache.fineract.portfolio.loanaccount.loanschedule.domain.DecliningBalanceInterestLoanScheduleGenerator.java

License:Apache License

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

    LocalDate interestStartDate = periodStartDate;
    Money interestForThisInstallment = totalCumulativePrincipal.zero();
    Money compoundedMoney = totalCumulativePrincipal.zero();
    Money compoundedInterest = totalCumulativePrincipal.zero();
    Money balanceForInterestCalculation = outstandingBalance;
    Money cumulatingInterestDueToGrace = cumulatingInterestPaymentDueToGrace;
    Map<LocalDate, BigDecimal> interestRates = new HashMap<>(termVariations.size());
    for (LoanTermVariationsData loanTermVariation : termVariations) {
        if (loanTermVariation.getTermVariationType().isInterestRateVariation()
                && loanTermVariation.isApplicable(periodStartDate, periodEndDate)) {
            LocalDate fromDate = loanTermVariation.getTermApplicableFrom();
            if (fromDate == null) {
                fromDate = periodStartDate;
            }//from   www .  j a va 2 s. com
            interestRates.put(fromDate, loanTermVariation.getDecimalValue());
            if (!principalVariation.containsKey(fromDate)) {
                principalVariation.put(fromDate, balanceForInterestCalculation.zero());
            }
        }
    }
    if (principalVariation != null) {
        // identifies rest date after current date for reducing all
        // compounding
        // values
        LocalDate compoundingEndDate = principalVariation.ceilingKey(DateUtils.getLocalDateOfTenant());
        if (compoundingEndDate == null) {
            compoundingEndDate = DateUtils.getLocalDateOfTenant();
        }

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

            if (!principal.getKey().isAfter(periodEndDate)) {
                int interestForDays = Days.daysBetween(interestStartDate, principal.getKey()).getDays();
                if (interestForDays > 0) {
                    final PrincipalInterest result = loanApplicationTerms.calculateTotalInterestForPeriod(
                            calculator, interestCalculationGraceOnRepaymentPeriodFraction, periodNumber, mc,
                            cumulatingInterestDueToGrace, balanceForInterestCalculation, interestStartDate,
                            principal.getKey());
                    interestForThisInstallment = interestForThisInstallment.plus(result.interest());
                    cumulatingInterestDueToGrace = result.interestPaymentDueToGrace();
                    interestStartDate = principal.getKey();
                }
                Money compoundFee = totalCumulativePrincipal.zero();
                if (compoundingMap.containsKey(principal.getKey())) {
                    Money interestToBeCompounded = totalCumulativePrincipal.zero();
                    // for interest compounding
                    if (loanApplicationTerms.getInterestRecalculationCompoundingMethod()
                            .isInterestCompoundingEnabled()) {
                        interestToBeCompounded = interestForThisInstallment.minus(compoundedInterest);
                        balanceForInterestCalculation = balanceForInterestCalculation
                                .plus(interestToBeCompounded);
                        compoundedInterest = interestForThisInstallment;
                    }
                    // fee compounding will be done after calculation
                    compoundFee = compoundingMap.get(principal.getKey());
                    compoundedMoney = compoundedMoney.plus(interestToBeCompounded).plus(compoundFee);
                }
                balanceForInterestCalculation = balanceForInterestCalculation.plus(principal.getValue())
                        .plus(compoundFee);
                if (interestRates.containsKey(principal.getKey())) {
                    loanApplicationTerms.updateAnnualNominalInterestRate(interestRates.get(principal.getKey()));
                }
            }

        }
        if (!periodEndDate.isBefore(compoundingEndDate)) {
            balanceForInterestCalculation = balanceForInterestCalculation.minus(compoundedMoney);
            compoundingMap.clear();
        } else if (compoundedMoney.isGreaterThanZero()) {
            compoundingMap.put(periodEndDate, compoundedMoney);
            compoundingMap.put(compoundingEndDate, compoundedMoney.negated());
            clearMapDetails(periodEndDate, compoundingMap);
        }
    }

    final PrincipalInterest result = loanApplicationTerms.calculateTotalInterestForPeriod(calculator,
            interestCalculationGraceOnRepaymentPeriodFraction, periodNumber, mc, cumulatingInterestDueToGrace,
            balanceForInterestCalculation, interestStartDate, periodEndDate);
    interestForThisInstallment = interestForThisInstallment.plus(result.interest());
    cumulatingInterestDueToGrace = result.interestPaymentDueToGrace();

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

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

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

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

From source file:org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanApplicationTerms.java

License:Apache License

private BigDecimal calculatePeriodsInLoanTerm() {

    BigDecimal periodsInLoanTerm = BigDecimal.valueOf(this.loanTermFrequency);
    switch (this.interestCalculationPeriodMethod) {
    case DAILY://from   www . ja  va 2  s. co m
        // 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:
        LocalDate startDate = getExpectedDisbursementDate();
        periodsInLoanTerm = calculatePeriodsBetweenDates(startDate, this.loanEndDate);
        break;
    }

    return periodsInLoanTerm;
}

From source file:org.apache.fineract.portfolio.loanaccount.loanschedule.service.LoanScheduleAssembler.java

License:Apache License

private LoanApplicationTerms assembleLoanApplicationTermsFrom(final JsonElement element,
        final LoanProduct loanProduct) {

    final MonetaryCurrency currency = loanProduct.getCurrency();
    final ApplicationCurrency applicationCurrency = this.applicationCurrencyRepository
            .findOneWithNotFoundDetection(currency);

    // loan terms
    final Integer loanTermFrequency = this.fromApiJsonHelper.extractIntegerWithLocaleNamed("loanTermFrequency",
            element);/*from  www  .jav a2s.  c  o m*/
    final Integer loanTermFrequencyType = this.fromApiJsonHelper
            .extractIntegerWithLocaleNamed("loanTermFrequencyType", element);
    final PeriodFrequencyType loanTermPeriodFrequencyType = PeriodFrequencyType.fromInt(loanTermFrequencyType);

    final Integer numberOfRepayments = this.fromApiJsonHelper
            .extractIntegerWithLocaleNamed("numberOfRepayments", element);
    final Integer repaymentEvery = this.fromApiJsonHelper.extractIntegerWithLocaleNamed("repaymentEvery",
            element);
    final Integer repaymentFrequencyType = this.fromApiJsonHelper
            .extractIntegerWithLocaleNamed("repaymentFrequencyType", element);
    final PeriodFrequencyType repaymentPeriodFrequencyType = PeriodFrequencyType
            .fromInt(repaymentFrequencyType);
    final Integer nthDay = this.fromApiJsonHelper.extractIntegerWithLocaleNamed("repaymentFrequencyNthDayType",
            element);
    final Integer dayOfWeek = this.fromApiJsonHelper
            .extractIntegerWithLocaleNamed("repaymentFrequencyDayOfWeekType", element);
    final DayOfWeekType weekDayType = DayOfWeekType.fromInt(dayOfWeek);

    final Integer amortizationType = this.fromApiJsonHelper.extractIntegerWithLocaleNamed("amortizationType",
            element);
    final AmortizationMethod amortizationMethod = AmortizationMethod.fromInt(amortizationType);

    // interest terms
    final Integer interestType = this.fromApiJsonHelper.extractIntegerWithLocaleNamed("interestType", element);
    final InterestMethod interestMethod = InterestMethod.fromInt(interestType);

    final Integer interestCalculationPeriodType = this.fromApiJsonHelper
            .extractIntegerWithLocaleNamed("interestCalculationPeriodType", element);
    final InterestCalculationPeriodMethod interestCalculationPeriodMethod = InterestCalculationPeriodMethod
            .fromInt(interestCalculationPeriodType);
    Boolean allowPartialPeriodInterestCalcualtion = this.fromApiJsonHelper
            .extractBooleanNamed(LoanProductConstants.allowPartialPeriodInterestCalcualtionParamName, element);
    if (allowPartialPeriodInterestCalcualtion == null) {
        allowPartialPeriodInterestCalcualtion = loanProduct.getLoanProductRelatedDetail()
                .isAllowPartialPeriodInterestCalcualtion();
    }

    final BigDecimal interestRatePerPeriod = this.fromApiJsonHelper
            .extractBigDecimalWithLocaleNamed("interestRatePerPeriod", element);
    final PeriodFrequencyType interestRatePeriodFrequencyType = loanProduct.getInterestPeriodFrequencyType();

    BigDecimal annualNominalInterestRate = BigDecimal.ZERO;
    if (interestRatePerPeriod != null) {
        annualNominalInterestRate = this.aprCalculator.calculateFrom(interestRatePeriodFrequencyType,
                interestRatePerPeriod);
    }

    // disbursement details
    final BigDecimal principal = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed("principal", element);
    final Money principalMoney = Money.of(currency, principal);

    final LocalDate expectedDisbursementDate = this.fromApiJsonHelper
            .extractLocalDateNamed("expectedDisbursementDate", element);
    final LocalDate repaymentsStartingFromDate = this.fromApiJsonHelper
            .extractLocalDateNamed("repaymentsStartingFromDate", element);
    LocalDate calculatedRepaymentsStartingFromDate = repaymentsStartingFromDate;

    final Boolean synchDisbursement = this.fromApiJsonHelper.extractBooleanNamed("syncDisbursementWithMeeting",
            element);
    final Long calendarId = this.fromApiJsonHelper.extractLongNamed("calendarId", element);
    Calendar calendar = null;

    final String loanTypeParameterName = "loanType";
    final String loanTypeStr = this.fromApiJsonHelper.extractStringNamed(loanTypeParameterName, element);

    final AccountType loanType = AccountType.fromName(loanTypeStr);

    /*
     * If it is JLG loan/Group Loan then make sure loan frequency is same as
     * Group/Center meeting frequency or multiple of it. TODO: Check should
     * be either same frequency or loan freq is multiple of center/group
     * meeting freq multiples
     */
    if ((loanType.isJLGAccount() || loanType.isGroupAccount()) && calendarId != null) {
        calendar = this.calendarRepository.findOne(calendarId);
        if (calendar == null) {
            throw new CalendarNotFoundException(calendarId);
        }
        final PeriodFrequencyType meetingPeriodFrequency = CalendarUtils
                .getMeetingPeriodFrequencyType(calendar.getRecurrence());
        validateRepaymentFrequencyIsSameAsMeetingFrequency(meetingPeriodFrequency.getValue(),
                repaymentFrequencyType, CalendarUtils.getInterval(calendar.getRecurrence()), repaymentEvery);
    }

    /*
     * If user has not passed the first repayments date then then derive the
     * same based on loan type.
     */
    if (calculatedRepaymentsStartingFromDate == null) {
        calculatedRepaymentsStartingFromDate = deriveFirstRepaymentDate(loanType, repaymentEvery,
                expectedDisbursementDate, repaymentPeriodFrequencyType,
                loanProduct.getMinimumDaysBetweenDisbursalAndFirstRepayment(), calendar);
    }

    /*
     * If it is JLG loan/Group Loan synched with a meeting, then make sure
     * first repayment falls on meeting date
     */
    if ((loanType.isJLGAccount() || loanType.isGroupAccount()) && calendar != null) {
        validateRepaymentsStartDateWithMeetingDates(calculatedRepaymentsStartingFromDate, calendar);
        /*
         * If disbursement is synced on meeting, make sure disbursement date
         * is on a meeting date
         */
        if (synchDisbursement != null && synchDisbursement.booleanValue()) {
            validateDisbursementDateWithMeetingDates(expectedDisbursementDate, calendar);
        }
    }

    validateMinimumDaysBetweenDisbursalAndFirstRepayment(expectedDisbursementDate,
            calculatedRepaymentsStartingFromDate,
            loanProduct.getMinimumDaysBetweenDisbursalAndFirstRepayment());

    // grace details
    final Integer graceOnPrincipalPayment = this.fromApiJsonHelper
            .extractIntegerWithLocaleNamed("graceOnPrincipalPayment", element);
    final Integer graceOnInterestPayment = this.fromApiJsonHelper
            .extractIntegerWithLocaleNamed("graceOnInterestPayment", element);
    final Integer graceOnInterestCharged = this.fromApiJsonHelper
            .extractIntegerWithLocaleNamed("graceOnInterestCharged", element);
    final LocalDate interestChargedFromDate = this.fromApiJsonHelper
            .extractLocalDateNamed("interestChargedFromDate", element);

    final Integer graceOnArrearsAgeing = this.fromApiJsonHelper
            .extractIntegerWithLocaleNamed(LoanProductConstants.graceOnArrearsAgeingParameterName, element);

    // other
    final BigDecimal inArrearsTolerance = this.fromApiJsonHelper
            .extractBigDecimalWithLocaleNamed("inArrearsTolerance", element);
    final Money inArrearsToleranceMoney = Money.of(currency, inArrearsTolerance);

    final BigDecimal emiAmount = this.fromApiJsonHelper
            .extractBigDecimalWithLocaleNamed(LoanApiConstants.emiAmountParameterName, element);
    final BigDecimal maxOutstandingBalance = this.fromApiJsonHelper
            .extractBigDecimalWithLocaleNamed(LoanApiConstants.maxOutstandingBalanceParameterName, element);

    final List<DisbursementData> disbursementDatas = fetchDisbursementData(element.getAsJsonObject());

    /**
     * Interest recalculation settings copy from product definition
     */
    final DaysInMonthType daysInMonthType = loanProduct.fetchDaysInMonthType();

    final DaysInYearType daysInYearType = loanProduct.fetchDaysInYearType();

    final boolean isInterestRecalculationEnabled = loanProduct.isInterestRecalculationEnabled();
    RecalculationFrequencyType recalculationFrequencyType = null;
    CalendarInstance restCalendarInstance = null;
    RecalculationFrequencyType compoundingFrequencyType = null;
    CalendarInstance compoundingCalendarInstance = null;
    if (isInterestRecalculationEnabled) {
        LoanProductInterestRecalculationDetails loanProductInterestRecalculationDetails = loanProduct
                .getProductInterestRecalculationDetails();
        recalculationFrequencyType = loanProductInterestRecalculationDetails.getRestFrequencyType();
        if (recalculationFrequencyType.isSameAsRepayment()) {
            restCalendarInstance = createCalendarForSameAsRepayment(repaymentEvery,
                    repaymentPeriodFrequencyType, expectedDisbursementDate);
        } else {
            LocalDate calendarStartDate = this.fromApiJsonHelper.extractLocalDateNamed(
                    LoanProductConstants.recalculationRestFrequencyDateParamName, element);
            if (calendarStartDate == null) {
                calendarStartDate = expectedDisbursementDate;
            }
            restCalendarInstance = createInterestRecalculationCalendarInstance(calendarStartDate,
                    recalculationFrequencyType, loanProductInterestRecalculationDetails.getRestInterval());
        }
        InterestRecalculationCompoundingMethod compoundingMethod = InterestRecalculationCompoundingMethod
                .fromInt(loanProductInterestRecalculationDetails.getInterestRecalculationCompoundingMethod());
        if (compoundingMethod.isCompoundingEnabled()) {
            compoundingFrequencyType = loanProductInterestRecalculationDetails.getCompoundingFrequencyType();
            if (compoundingFrequencyType.isSameAsRepayment()) {
                compoundingCalendarInstance = createCalendarForSameAsRepayment(repaymentEvery,
                        repaymentPeriodFrequencyType, expectedDisbursementDate);
            } else {
                LocalDate calendarStartDate = this.fromApiJsonHelper.extractLocalDateNamed(
                        LoanProductConstants.recalculationCompoundingFrequencyDateParamName, element);
                if (calendarStartDate == null) {
                    calendarStartDate = expectedDisbursementDate;
                }
                compoundingCalendarInstance = createInterestRecalculationCalendarInstance(calendarStartDate,
                        compoundingFrequencyType,
                        loanProductInterestRecalculationDetails.getCompoundingInterval());
            }

        }
    }

    final BigDecimal principalThresholdForLastInstalment = loanProduct
            .getPrincipalThresholdForLastInstallment();

    final Integer installmentAmountInMultiplesOf = loanProduct.getInstallmentAmountInMultiplesOf();

    List<LoanTermVariationsData> loanTermVariations = new ArrayList<>();
    if (loanProduct.isLinkedToFloatingInterestRate()) {
        final BigDecimal interestRateDiff = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(
                LoanApiConstants.interestRateDifferentialParameterName, element);
        final Boolean isFloatingInterestRate = this.fromApiJsonHelper
                .extractBooleanNamed(LoanApiConstants.isFloatingInterestRateParameterName, element);
        List<FloatingRatePeriodData> baseLendingRatePeriods = null;
        try {
            baseLendingRatePeriods = this.floatingRatesReadPlatformService.retrieveBaseLendingRate()
                    .getRatePeriods();
        } catch (final FloatingRateNotFoundException ex) {
            // Do not do anything
        }
        FloatingRateDTO floatingRateDTO = new FloatingRateDTO(isFloatingInterestRate, expectedDisbursementDate,
                interestRateDiff, baseLendingRatePeriods);
        Collection<FloatingRatePeriodData> applicableRates = loanProduct.fetchInterestRates(floatingRateDTO);

        LocalDate interestRateStartDate = DateUtils.getLocalDateOfTenant();
        final LocalDate dateValue = null;
        final boolean isSpecificToInstallment = false;
        for (FloatingRatePeriodData periodData : applicableRates) {
            LoanTermVariationsData loanTermVariation = new LoanTermVariationsData(
                    LoanEnumerations.loanvariationType(LoanTermVariationType.INTEREST_RATE),
                    periodData.getFromDateAsLocalDate(), periodData.getInterestRate(), dateValue,
                    isSpecificToInstallment);
            if (!interestRateStartDate.isBefore(periodData.getFromDateAsLocalDate())) {
                interestRateStartDate = periodData.getFromDateAsLocalDate();
                annualNominalInterestRate = periodData.getInterestRate();
            }
            loanTermVariations.add(loanTermVariation);
        }
    }

    return LoanApplicationTerms.assembleFrom(applicationCurrency, loanTermFrequency,
            loanTermPeriodFrequencyType, numberOfRepayments, repaymentEvery, repaymentPeriodFrequencyType,
            nthDay, weekDayType, amortizationMethod, interestMethod, interestRatePerPeriod,
            interestRatePeriodFrequencyType, annualNominalInterestRate, interestCalculationPeriodMethod,
            allowPartialPeriodInterestCalcualtion, principalMoney, expectedDisbursementDate,
            repaymentsStartingFromDate, calculatedRepaymentsStartingFromDate, graceOnPrincipalPayment,
            graceOnInterestPayment, graceOnInterestCharged, interestChargedFromDate, inArrearsToleranceMoney,
            loanProduct.isMultiDisburseLoan(), emiAmount, disbursementDatas, maxOutstandingBalance,
            graceOnArrearsAgeing, daysInMonthType, daysInYearType, isInterestRecalculationEnabled,
            recalculationFrequencyType, restCalendarInstance, compoundingCalendarInstance,
            compoundingFrequencyType, principalThresholdForLastInstalment, installmentAmountInMultiplesOf,
            loanProduct.preCloseInterestCalculationStrategy(), calendar, BigDecimal.ZERO, loanTermVariations);
}

From source file:org.apache.fineract.portfolio.loanaccount.loanschedule.service.LoanScheduleAssembler.java

License:Apache License

public void assempleVariableScheduleFrom(final Loan loan, final String json) {
    this.variableLoanScheduleFromApiJsonValidator.validateSchedule(json, loan);

    List<LoanTermVariations> variations = loan.getLoanTermVariations();
    List<LoanTermVariations> newVariations = new ArrayList<>();
    extractLoanTermVariations(loan, json, newVariations);

    final Map<LocalDate, LocalDate> adjustDueDateVariations = new HashMap<>();

    if (!variations.isEmpty()) {
        List<LoanTermVariations> retainVariations = adjustExistingVariations(variations, newVariations,
                adjustDueDateVariations);
        newVariations = retainVariations;
    }/*from  w w w. j  a  va 2 s . com*/
    variations.addAll(newVariations);
    Collections.sort(variations, new LoanTermVariationsComparator());

    /*
     * List<LoanTermVariationsData> loanTermVariationsDatas = new
     * ArrayList<>();
     * loanTermVariationsDatas.addAll(loanApplicationTerms.getLoanTermVariations
     * ().getExceptionData()); loanApplicationTerms =
     * LoanApplicationTerms.assembleFrom(loanApplicationTerms,
     * loanTermVariationsDatas);
     */

    // date validations
    List<LoanRepaymentScheduleInstallment> installments = loan.fetchRepaymentScheduleInstallments();
    Set<LocalDate> dueDates = new TreeSet<>();
    LocalDate graceApplicable = loan.getExpectedDisbursedOnLocalDate();
    Integer graceOnPrincipal = loan.getLoanProductRelatedDetail().graceOnPrincipalPayment();
    if (graceOnPrincipal == null) {
        graceOnPrincipal = 0;
    }
    LocalDate lastDate = loan.getExpectedDisbursedOnLocalDate();
    for (LoanRepaymentScheduleInstallment installment : installments) {
        dueDates.add(installment.getDueDate());
        if (lastDate.isBefore(installment.getDueDate())) {
            lastDate = installment.getDueDate();
        }
        if (graceOnPrincipal.equals(installment.getInstallmentNumber())) {
            graceApplicable = installment.getDueDate();
        }
    }
    Collection<LocalDate> keySet = adjustDueDateVariations.keySet();
    dueDates.addAll(keySet);
    for (final LocalDate date : keySet) {
        LocalDate removeDate = adjustDueDateVariations.get(date);
        if (removeDate != null) {
            dueDates.remove(removeDate);
        }
    }

    Set<LocalDate> actualDueDates = new TreeSet<>(dueDates);
    final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
    final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
            .resource("loan");
    List<LocalDate> overlappings = new ArrayList<>();
    for (LoanTermVariations termVariations : variations) {
        switch (termVariations.getTermType()) {
        case INSERT_INSTALLMENT:
            if (dueDates.contains(termVariations.fetchTermApplicaDate())) {
                overlappings.add(termVariations.fetchTermApplicaDate());
            } else {
                dueDates.add(termVariations.fetchTermApplicaDate());
            }
            if (!graceApplicable.isBefore(termVariations.fetchTermApplicaDate())) {
                baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode(
                        "variable.schedule.insert.not.allowed.before.grace.period",
                        "Loan schedule insert request invalid");
            }
            if (termVariations.fetchTermApplicaDate().isAfter(lastDate)) {
                baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode(
                        "variable.schedule.insert.not.allowed.after.last.period.date",
                        "Loan schedule insert request invalid");
            } else if (termVariations.fetchTermApplicaDate().isBefore(loan.getExpectedDisbursedOnLocalDate())) {
                baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode(
                        "variable.schedule.insert.not.allowed.before.disbursement.date",
                        "Loan schedule insert request invalid");
            }
            break;
        case DELETE_INSTALLMENT:
            if (dueDates.contains(termVariations.fetchTermApplicaDate())) {
                dueDates.remove(termVariations.fetchTermApplicaDate());
            } else {
                baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode(
                        "variable.schedule.remove.date.invalid", "Loan schedule remove request invalid");
            }
            if (termVariations.fetchTermApplicaDate().isEqual(lastDate)) {
                baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode(
                        "variable.schedule.delete.not.allowed.for.last.period.date",
                        "Loan schedule remove request invalid");
            }
            break;
        case DUE_DATE:
            if (dueDates.contains(termVariations.fetchTermApplicaDate())) {

                if (overlappings.contains(termVariations.fetchTermApplicaDate())) {
                    overlappings.remove(termVariations.fetchTermApplicaDate());
                } else {
                    dueDates.remove(termVariations.fetchTermApplicaDate());
                }
            } else {
                baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode(
                        "variable.schedule.modify.date.invalid",
                        "Loan schedule modify due date request invalid");
            }
            if (dueDates.contains(termVariations.fetchDateValue())) {
                overlappings.add(termVariations.fetchDateValue());
            } else {
                dueDates.add(termVariations.fetchDateValue());
            }
            if (termVariations.fetchDateValue().isBefore(loan.getExpectedDisbursedOnLocalDate())) {
                baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode(
                        "variable.schedule.insert.not.allowed.before.disbursement.date",
                        "Loan schedule insert request invalid");
            }
            if (termVariations.fetchTermApplicaDate().isEqual(lastDate)) {
                lastDate = termVariations.fetchDateValue();
            }
            break;
        case PRINCIPAL_AMOUNT:
        case EMI_AMOUNT:
            if (!graceApplicable.isBefore(termVariations.fetchTermApplicaDate())) {
                baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode(
                        "variable.schedule.amount.update.not.allowed.before.grace.period",
                        "Loan schedule modify request invalid");
            }
            if (!dueDates.contains(termVariations.fetchTermApplicaDate())) {
                baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode(
                        "variable.schedule.amount.update.from.date.invalid",
                        "Loan schedule modify request invalid");
            }
            if (termVariations.fetchTermApplicaDate().isEqual(lastDate)) {
                baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode(
                        "variable.schedule.amount.update.not.allowed.for.last.period",
                        "Loan schedule modify request invalid");
            }
            break;

        default:
            break;

        }

    }
    if (!overlappings.isEmpty()) {
        baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode(
                "variable.schedule.modify.date.can.not.be.due.date", overlappings);
    }
    LoanProductVariableInstallmentConfig installmentConfig = loan.loanProduct()
            .loanProductVariableInstallmentConfig();
    final CalendarInstance loanCalendarInstance = calendarInstanceRepository
            .findCalendarInstaneByEntityId(loan.getId(), CalendarEntityType.LOANS.getValue());
    Calendar loanCalendar = null;
    if (loanCalendarInstance != null) {
        loanCalendar = loanCalendarInstance.getCalendar();
    }
    final Integer minGap = installmentConfig.getMinimumGap();
    final Integer maxGap = installmentConfig.getMaximumGap();

    LocalDate previousDate = loan.getDisbursementDate();
    for (LocalDate duedate : dueDates) {
        int gap = Days.daysBetween(previousDate, duedate).getDays();
        previousDate = duedate;
        if (gap < minGap || (maxGap != null && gap > maxGap)) {
            baseDataValidator.reset().value(duedate).failWithCodeNoParameterAddedToErrorCode(
                    "variable.schedule.date.must.be.in.min.max.range", "Loan schedule date invalid");
        } else if (loanCalendar != null && !actualDueDates.contains(duedate)
                && !loanCalendar.isValidRecurringDate(duedate)) {
            baseDataValidator.reset().value(duedate).failWithCodeNoParameterAddedToErrorCode(
                    "variable.schedule.date.not.meeting.date",
                    "Loan schedule date not in sync with meeting date");
        }
    }
    if (!dataValidationErrors.isEmpty()) {
        throw new PlatformApiDataValidationException(dataValidationErrors);
    }
    if (loan.getExpectedFirstRepaymentOnDate() == null) {
        loan.setExpectedFirstRepaymentOnDate(loan.fetchRepaymentScheduleInstallment(1).getDueDate().toDate());
    }
    final LocalDate recalculateFrom = null;
    ScheduleGeneratorDTO scheduleGeneratorDTO = this.loanUtilService.buildScheduleGeneratorDTO(loan,
            recalculateFrom);
    AppUser currentUser = this.context.getAuthenticatedUserIfPresent();
    loan.regenerateRepaymentSchedule(scheduleGeneratorDTO, currentUser);
}