Example usage for org.joda.time LocalDate minusDays

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

Introduction

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

Prototype

public LocalDate minusDays(int days) 

Source Link

Document

Returns a copy of this date minus the specified number of days.

Usage

From source file:com.gst.portfolio.calendar.service.CalendarWritePlatformServiceJpaRepositoryImpl.java

License:Apache License

@Override
public CommandProcessingResult updateCalendar(final JsonCommand command) {

    /** Validate to check if Edit is Allowed **/
    this.validateIsEditMeetingAllowed(command.getGroupId());
    /*//  ww  w .j  ava 2s .  co  m
     * Validate all the data for updating the calendar
     */
    this.fromApiJsonDeserializer.validateForUpdate(command.json());

    Boolean areActiveEntitiesSynced = false;
    final Long calendarId = command.entityId();

    final Collection<Integer> loanStatuses = new ArrayList<>(
            Arrays.asList(LoanStatus.SUBMITTED_AND_PENDING_APPROVAL.getValue(), LoanStatus.APPROVED.getValue(),
                    LoanStatus.ACTIVE.getValue()));

    final Integer numberOfActiveLoansSyncedWithThisCalendar = this.calendarInstanceRepository
            .countOfLoansSyncedWithCalendar(calendarId, loanStatuses);

    /*
     * areActiveEntitiesSynced is set to true, if there are any active loans
     * synced to this calendar.
     */

    if (numberOfActiveLoansSyncedWithThisCalendar > 0) {
        areActiveEntitiesSynced = true;
    }

    final Calendar calendarForUpdate = this.calendarRepository.findOne(calendarId);
    if (calendarForUpdate == null) {
        throw new CalendarNotFoundException(calendarId);
    }

    final Date oldStartDate = calendarForUpdate.getStartDate();
    // create calendar history before updating calendar
    final CalendarHistory calendarHistory = new CalendarHistory(calendarForUpdate, oldStartDate);

    Map<String, Object> changes = null;

    final Boolean reschedulebasedOnMeetingDates = command.booleanObjectValueOfParameterNamed(
            CALENDAR_SUPPORTED_PARAMETERS.RESCHEDULE_BASED_ON_MEETING_DATES.getValue());

    /*
     * System allows to change the meeting date by two means,
     * 
     * Option 1: reschedulebasedOnMeetingDates = false or reschedulebasedOnMeetingDates is not passed 
     * By directly editing the recurring day with effective from
     * date and system decides the next meeting date based on some sensible
     * logic (i.e., number of minimum days between two repayments)
     * 
     * 
     * Option 2: reschedulebasedOnMeetingDates = true 
     * By providing alternative meeting date for one of future
     * meeting date and derive the day of recurrence from the new meeting
     * date. Ex: User proposes new meeting date say "14/Nov/2014" for
     * present meeting date "12/Nov/2014", based on this input other values
     * re derived and loans are rescheduled
     * 
     */

    LocalDate newMeetingDate = null;
    LocalDate presentMeetingDate = null;

    if (reschedulebasedOnMeetingDates != null && reschedulebasedOnMeetingDates) {

        newMeetingDate = command
                .localDateValueOfParameterNamed(CALENDAR_SUPPORTED_PARAMETERS.NEW_MEETING_DATE.getValue());
        presentMeetingDate = command
                .localDateValueOfParameterNamed(CALENDAR_SUPPORTED_PARAMETERS.PRESENT_MEETING_DATE.getValue());

        /*
         * New meeting date proposed will become the new start date for the
         * updated calendar
         */

        changes = calendarForUpdate.updateStartDateAndDerivedFeilds(newMeetingDate);

    } else {
        changes = calendarForUpdate.update(command, areActiveEntitiesSynced);
    }

    if (!changes.isEmpty()) {
        // update calendar history table only if there is a change in
        // calendar start date.
        if (reschedulebasedOnMeetingDates == null) {
            presentMeetingDate = command
                    .localDateValueOfParameterNamed(CALENDAR_SUPPORTED_PARAMETERS.START_DATE.getValue());
        }
        if (null != newMeetingDate) {
            final Date endDate = presentMeetingDate.minusDays(1).toDate();
            calendarHistory.updateEndDate(endDate);
        }
        this.calendarHistoryRepository.save(calendarHistory);
        Set<CalendarHistory> history = calendarForUpdate.getCalendarHistory();
        history.add(calendarHistory);
        calendarForUpdate.updateCalendarHistory(history);
        this.calendarRepository.saveAndFlush(calendarForUpdate);

        if (this.configurationDomainService.isRescheduleFutureRepaymentsEnabled()
                && calendarForUpdate.isRepeating()) {
            // fetch all loan calendar instances associated with modifying
            // calendar.
            final Collection<CalendarInstance> loanCalendarInstances = this.calendarInstanceRepository
                    .findByCalendarIdAndEntityTypeId(calendarId, CalendarEntityType.LOANS.getValue());

            if (!CollectionUtils.isEmpty(loanCalendarInstances)) {
                // update all loans associated with modifying calendar
                this.loanWritePlatformService.applyMeetingDateChanges(calendarForUpdate, loanCalendarInstances,
                        reschedulebasedOnMeetingDates, presentMeetingDate, newMeetingDate);

            }
        }
    }

    return new CommandProcessingResultBuilder() //
            .withCommandId(command.commandId()) //
            .withEntityId(calendarForUpdate.getId()) //
            .with(changes) //
            .build();
}

From source file:com.gst.portfolio.loanaccount.domain.Loan.java

License:Apache License

private LocalDate getMaxDateLimitForNewRepayment(final PeriodFrequencyType periodFrequencyType,
        final Integer loanRepaymentInterval, final LocalDate startDate) {
    LocalDate dueRepaymentPeriodDate = startDate;
    final Integer repaidEvery = 2 * loanRepaymentInterval;
    switch (periodFrequencyType) {
    case DAYS:/*w w  w .  j a  v  a 2s .  c om*/
        dueRepaymentPeriodDate = startDate.plusDays(repaidEvery);
        break;
    case WEEKS:
        dueRepaymentPeriodDate = startDate.plusWeeks(repaidEvery);
        break;
    case MONTHS:
        dueRepaymentPeriodDate = startDate.plusMonths(repaidEvery);
        break;
    case YEARS:
        dueRepaymentPeriodDate = startDate.plusYears(repaidEvery);
        break;
    case INVALID:
        break;
    }
    return dueRepaymentPeriodDate.minusDays(1);// get 2n-1 range date from
                                               // startDate
}

From source file:com.gst.portfolio.loanaccount.loanschedule.domain.AbstractLoanScheduleGenerator.java

License:Apache License

private LoanScheduleModel generate(final MathContext mc, final LoanApplicationTerms loanApplicationTerms,
        final Set<LoanCharge> loanCharges, final HolidayDetailDTO holidayDetailDTO,
        final LoanScheduleParams loanScheduleParams) {

    final ApplicationCurrency applicationCurrency = loanApplicationTerms.getApplicationCurrency();
    // generate list of proposed schedule due dates
    LocalDate loanEndDate = this.scheduledDateGenerator.getLastRepaymentDate(loanApplicationTerms,
            holidayDetailDTO);/*from www  . j  a  v  a 2 s.  com*/
    LoanTermVariationsData lastDueDateVariation = loanApplicationTerms.getLoanTermVariations()
            .fetchLoanTermDueDateVariationsData(loanEndDate);
    if (lastDueDateVariation != null) {
        loanEndDate = lastDueDateVariation.getDateValue();
    }
    loanApplicationTerms.updateLoanEndDate(loanEndDate);

    // determine the total charges due at time of disbursement
    final BigDecimal chargesDueAtTimeOfDisbursement = deriveTotalChargesDueAtTimeOfDisbursement(loanCharges);

    // setup variables for tracking important facts required for loan
    // schedule generation.

    final MonetaryCurrency currency = loanApplicationTerms.getCurrency();
    final int numberOfRepayments = loanApplicationTerms.fetchNumberOfRepaymentsAfterExceptions();
    LoanScheduleParams scheduleParams = null;
    if (loanScheduleParams == null) {
        scheduleParams = LoanScheduleParams.createLoanScheduleParams(currency,
                Money.of(currency, chargesDueAtTimeOfDisbursement),
                loanApplicationTerms.getExpectedDisbursementDate(),
                getPrincipalToBeScheduled(loanApplicationTerms));
    } else if (!loanScheduleParams.isPartialUpdate()) {
        scheduleParams = LoanScheduleParams.createLoanScheduleParams(currency,
                Money.of(currency, chargesDueAtTimeOfDisbursement),
                loanApplicationTerms.getExpectedDisbursementDate(),
                getPrincipalToBeScheduled(loanApplicationTerms), loanScheduleParams);
    } else {
        scheduleParams = loanScheduleParams;
    }

    final Collection<RecalculationDetail> transactions = scheduleParams.getRecalculationDetails();
    final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = scheduleParams
            .getLoanRepaymentScheduleTransactionProcessor();

    Collection<LoanScheduleModelPeriod> periods = new ArrayList<>();
    if (!scheduleParams.isPartialUpdate()) {
        periods = createNewLoanScheduleListWithDisbursementDetails(numberOfRepayments, loanApplicationTerms,
                chargesDueAtTimeOfDisbursement);
    }

    // Determine the total interest owed over the full loan for FLAT
    // interest method .
    if (!scheduleParams.isPartialUpdate()) {
        Money totalInterestChargedForFullLoanTerm = loanApplicationTerms
                .calculateTotalInterestCharged(this.paymentPeriodsInOneYearCalculator, mc);
        loanApplicationTerms.updateTotalInterestDue(totalInterestChargedForFullLoanTerm);
    }

    boolean isFirstRepayment = true;
    LocalDate firstRepaymentdate = this.scheduledDateGenerator.generateNextRepaymentDate(
            loanApplicationTerms.getExpectedDisbursementDate(), loanApplicationTerms, isFirstRepayment,
            holidayDetailDTO);
    final LocalDate idealDisbursementDate = this.scheduledDateGenerator
            .idealDisbursementDateBasedOnFirstRepaymentDate(
                    loanApplicationTerms.getLoanTermPeriodFrequencyType(),
                    loanApplicationTerms.getRepaymentEvery(), firstRepaymentdate,
                    loanApplicationTerms.getLoanCalendar(), loanApplicationTerms.getHolidayDetailDTO(),
                    loanApplicationTerms);

    if (!scheduleParams.isPartialUpdate()) {
        // Set Fixed Principal Amount
        updateAmortization(mc, loanApplicationTerms, scheduleParams.getPeriodNumber(),
                scheduleParams.getOutstandingBalance());

        if (loanApplicationTerms.isMultiDisburseLoan()) {
            // fetches the first tranche amount and also updates other
            // tranche
            // details to map
            BigDecimal disburseAmt = getDisbursementAmount(loanApplicationTerms,
                    scheduleParams.getPeriodStartDate(), periods, chargesDueAtTimeOfDisbursement,
                    scheduleParams.getDisburseDetailMap(), scheduleParams.applyInterestRecalculation());
            scheduleParams.setPrincipalToBeScheduled(Money.of(currency, disburseAmt));
            loanApplicationTerms.setPrincipal(loanApplicationTerms.getPrincipal().zero().plus(disburseAmt));
            scheduleParams.setOutstandingBalance(Money.of(currency, disburseAmt));
            scheduleParams.setOutstandingBalanceAsPerRest(Money.of(currency, disburseAmt));
        }
    }

    // charges which depends on total loan interest will be added to this
    // set and handled separately after all installments generated
    final Set<LoanCharge> nonCompoundingCharges = seperateTotalCompoundingPercentageCharges(loanCharges);

    LocalDate currentDate = DateUtils.getLocalDateOfTenant();
    LocalDate lastRestDate = currentDate;
    if (loanApplicationTerms.getRestCalendarInstance() != null) {
        lastRestDate = getNextRestScheduleDate(currentDate.minusDays(1), loanApplicationTerms,
                holidayDetailDTO);
    }

    boolean isNextRepaymentAvailable = true;
    Boolean extendTermForDailyRepayments = false;

    if (holidayDetailDTO.getWorkingDays().getExtendTermForDailyRepayments() == true
            && loanApplicationTerms.getRepaymentPeriodFrequencyType() == PeriodFrequencyType.DAYS
            && loanApplicationTerms.getRepaymentEvery() == 1) {
        holidayDetailDTO.getWorkingDays()
                .setRepaymentReschedulingType(RepaymentRescheduleType.MOVE_TO_NEXT_WORKING_DAY.getValue());
        extendTermForDailyRepayments = true;
    }

    final Collection<LoanTermVariationsData> interestRates = loanApplicationTerms.getLoanTermVariations()
            .getInterestRateChanges();
    final Collection<LoanTermVariationsData> interestRatesForInstallments = loanApplicationTerms
            .getLoanTermVariations().getInterestRateFromInstallment();

    // this block is to start the schedule generation from specified date
    if (scheduleParams.isPartialUpdate()) {
        if (loanApplicationTerms.isMultiDisburseLoan()) {
            loanApplicationTerms.setPrincipal(scheduleParams.getPrincipalToBeScheduled());
        }

        applyLoanVariationsForPartialScheduleGenerate(loanApplicationTerms, scheduleParams, interestRates,
                interestRatesForInstallments);

        isFirstRepayment = false;
    }
    while (!scheduleParams.getOutstandingBalance().isZero()
            || !scheduleParams.getDisburseDetailMap().isEmpty()) {
        LocalDate previousRepaymentDate = scheduleParams.getActualRepaymentDate();
        scheduleParams.setActualRepaymentDate(
                this.scheduledDateGenerator.generateNextRepaymentDate(scheduleParams.getActualRepaymentDate(),
                        loanApplicationTerms, isFirstRepayment, holidayDetailDTO));
        isFirstRepayment = false;
        LocalDate scheduledDueDate = this.scheduledDateGenerator.adjustRepaymentDate(
                scheduleParams.getActualRepaymentDate(), loanApplicationTerms, holidayDetailDTO);

        // calculated interest start date for the period
        LocalDate periodStartDateApplicableForInterest = calculateInterestStartDateForPeriod(
                loanApplicationTerms, scheduleParams.getPeriodStartDate(), idealDisbursementDate,
                firstRepaymentdate, loanApplicationTerms.isInterestChargedFromDateSameAsDisbursalDateEnabled(),
                loanApplicationTerms.getExpectedDisbursementDate());

        // Loan Schedule Exceptions that need to be applied for Loan Account
        LoanTermVariationParams termVariationParams = applyLoanTermVariations(loanApplicationTerms,
                scheduleParams, previousRepaymentDate, scheduledDueDate, interestRatesForInstallments,
                this.paymentPeriodsInOneYearCalculator, mc);

        scheduledDueDate = termVariationParams.getScheduledDueDate();
        // Updates total days in term
        scheduleParams.addLoanTermInDays(
                Days.daysBetween(scheduleParams.getPeriodStartDate(), scheduledDueDate).getDays());
        if (termVariationParams.isSkipPeriod()) {
            continue;
        }

        if (scheduleParams.getPeriodStartDate().isAfter(scheduledDueDate)) {
            throw new ScheduleDateException("Due date can't be before period start date", scheduledDueDate);
        }

        if (extendTermForDailyRepayments) {
            scheduleParams.setActualRepaymentDate(scheduledDueDate);
        }

        // this block is to generate the schedule till the specified
        // date(used for calculating preclosure)
        if (scheduleParams.getScheduleTillDate() != null
                && !scheduledDueDate.isBefore(scheduleParams.getScheduleTillDate())) {
            scheduledDueDate = scheduleParams.getScheduleTillDate();
            isNextRepaymentAvailable = false;
        }
        if (loanApplicationTerms.isInterestRecalculationEnabled()) {
            populateCompoundingDatesInPeriod(scheduleParams.getPeriodStartDate(), scheduledDueDate,
                    loanApplicationTerms, holidayDetailDTO, scheduleParams, loanCharges, currency);
        }

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

        final double interestCalculationGraceOnRepaymentPeriodFraction = this.paymentPeriodsInOneYearCalculator
                .calculatePortionOfRepaymentPeriodInterestChargingGrace(periodStartDateApplicableForInterest,
                        scheduledDueDate, loanApplicationTerms.getInterestChargedFromLocalDate(),
                        loanApplicationTerms.getLoanTermPeriodFrequencyType(),
                        loanApplicationTerms.getRepaymentEvery());
        ScheduleCurrentPeriodParams currentPeriodParams = new ScheduleCurrentPeriodParams(currency,
                interestCalculationGraceOnRepaymentPeriodFraction);

        if (loanApplicationTerms.isMultiDisburseLoan()) {
            updateBalanceBasedOnDisbursement(loanApplicationTerms, chargesDueAtTimeOfDisbursement,
                    scheduleParams, periods, scheduledDueDate);
        }

        // process repayments to the schedule as per the repayment
        // transaction processor configuration
        // will add a new schedule with interest till the transaction date
        // for a loan repayment which falls between the
        // two periods for interest first repayment strategies
        handleRecalculationForNonDueDateTransactions(mc, loanApplicationTerms, loanCharges, holidayDetailDTO,
                scheduleParams, periods, loanApplicationTerms.getTotalInterestDue(), idealDisbursementDate,
                firstRepaymentdate, lastRestDate, scheduledDueDate, periodStartDateApplicableForInterest,
                applicableTransactions, currentPeriodParams);

        if (currentPeriodParams.isSkipCurrentLoop()) {
            continue;
        }
        periodStartDateApplicableForInterest = calculateInterestStartDateForPeriod(loanApplicationTerms,
                scheduleParams.getPeriodStartDate(), idealDisbursementDate, firstRepaymentdate,
                loanApplicationTerms.isInterestChargedFromDateSameAsDisbursalDateEnabled(),
                loanApplicationTerms.getExpectedDisbursementDate());

        // backup for pre-close transaction
        updateCompoundingDetails(scheduleParams, periodStartDateApplicableForInterest);

        // 5 determine principal,interest of repayment period
        PrincipalInterest principalInterestForThisPeriod = calculatePrincipalInterestComponentsForPeriod(
                this.paymentPeriodsInOneYearCalculator,
                currentPeriodParams.getInterestCalculationGraceOnRepaymentPeriodFraction(),
                scheduleParams.getTotalCumulativePrincipal().minus(scheduleParams.getReducePrincipal()),
                scheduleParams.getTotalCumulativeInterest(), loanApplicationTerms.getTotalInterestDue(),
                scheduleParams.getTotalOutstandingInterestPaymentDueToGrace(),
                scheduleParams.getOutstandingBalanceAsPerRest(), loanApplicationTerms,
                scheduleParams.getPeriodNumber(), mc, mergeVariationsToMap(scheduleParams),
                scheduleParams.getCompoundingMap(), periodStartDateApplicableForInterest, scheduledDueDate,
                interestRates);

        // will check for EMI amount greater than interest calculated
        if (loanApplicationTerms.getFixedEmiAmount() != null && loanApplicationTerms.getFixedEmiAmount()
                .compareTo(principalInterestForThisPeriod.interest().getAmount()) == -1) {
            String errorMsg = "EMI amount must be greater than : "
                    + principalInterestForThisPeriod.interest().getAmount();
            throw new MultiDisbursementEmiAmountException(errorMsg,
                    principalInterestForThisPeriod.interest().getAmount(),
                    loanApplicationTerms.getFixedEmiAmount());
        }

        // update cumulative fields for principal & interest
        currentPeriodParams.setInterestForThisPeriod(principalInterestForThisPeriod.interest());
        Money lastTotalOutstandingInterestPaymentDueToGrace = scheduleParams
                .getTotalOutstandingInterestPaymentDueToGrace();
        scheduleParams.setTotalOutstandingInterestPaymentDueToGrace(
                principalInterestForThisPeriod.interestPaymentDueToGrace());
        currentPeriodParams.setPrincipalForThisPeriod(principalInterestForThisPeriod.principal());

        // applies early payments on principal portion
        updatePrincipalPortionBasedOnPreviousEarlyPayments(currency, scheduleParams, currentPeriodParams);

        // updates amounts with current earlyPaidAmount
        updateAmountsBasedOnCurrentEarlyPayments(mc, loanApplicationTerms, scheduleParams, currentPeriodParams);

        if (scheduleParams.getOutstandingBalance().isLessThanZero() || !isNextRepaymentAvailable) {
            currentPeriodParams.plusPrincipalForThisPeriod(scheduleParams.getOutstandingBalance());
            scheduleParams.setOutstandingBalance(Money.zero(currency));
        }

        if (!isNextRepaymentAvailable) {
            scheduleParams.getDisburseDetailMap().clear();
        }

        // applies charges for the period
        applyChargesForCurrentPeriod(loanCharges, currency, scheduleParams, scheduledDueDate,
                currentPeriodParams);

        // sum up real totalInstallmentDue from components
        final Money totalInstallmentDue = currentPeriodParams.fetchTotalAmountForPeriod();

        // if previous installment is last then add interest to same
        // installment
        if (currentPeriodParams.getLastInstallment() != null
                && currentPeriodParams.getPrincipalForThisPeriod().isZero()) {
            currentPeriodParams.getLastInstallment()
                    .addInterestAmount(currentPeriodParams.getInterestForThisPeriod());
            continue;
        }

        // create repayment period from parts
        LoanScheduleModelPeriod installment = LoanScheduleModelRepaymentPeriod.repayment(
                scheduleParams.getInstalmentNumber(), scheduleParams.getPeriodStartDate(), scheduledDueDate,
                currentPeriodParams.getPrincipalForThisPeriod(), scheduleParams.getOutstandingBalance(),
                currentPeriodParams.getInterestForThisPeriod(),
                currentPeriodParams.getFeeChargesForInstallment(),
                currentPeriodParams.getPenaltyChargesForInstallment(), totalInstallmentDue, false);

        addLoanRepaymentScheduleInstallment(scheduleParams.getInstallments(), installment);
        // apply loan transactions on installments to identify early/late
        // payments for interest recalculation
        installment = handleRecalculationForTransactions(mc, loanApplicationTerms, holidayDetailDTO, currency,
                scheduleParams, loanRepaymentScheduleTransactionProcessor,
                loanApplicationTerms.getTotalInterestDue(), lastRestDate, scheduledDueDate,
                periodStartDateApplicableForInterest, applicableTransactions, currentPeriodParams,
                lastTotalOutstandingInterestPaymentDueToGrace, installment, loanCharges);
        periods.add(installment);

        // Updates principal paid map with efective date for reducing
        // the amount from outstanding balance(interest calculation)
        updateAmountsWithEffectiveDate(loanApplicationTerms, holidayDetailDTO, scheduleParams, scheduledDueDate,
                currentPeriodParams, installment, lastRestDate);

        // handle cumulative fields

        scheduleParams.addTotalCumulativePrincipal(currentPeriodParams.getPrincipalForThisPeriod());
        scheduleParams.addTotalRepaymentExpected(totalInstallmentDue);
        scheduleParams.addTotalCumulativeInterest(currentPeriodParams.getInterestForThisPeriod());
        scheduleParams.setPeriodStartDate(scheduledDueDate);
        scheduleParams.incrementInstalmentNumber();
        scheduleParams.incrementPeriodNumber();
        if (termVariationParams.isRecalculateAmounts()) {
            loanApplicationTerms.setCurrentPeriodFixedEmiAmount(null);
            loanApplicationTerms.setCurrentPeriodFixedPrincipalAmount(null);
            adjustInstallmentOrPrincipalAmount(loanApplicationTerms,
                    scheduleParams.getTotalCumulativePrincipal(), scheduleParams.getPeriodNumber(), mc);
        }
    }

    // this condition is to add the interest from grace period if not
    // already applied.
    if (scheduleParams.getTotalOutstandingInterestPaymentDueToGrace().isGreaterThanZero()) {
        LoanScheduleModelPeriod installment = ((List<LoanScheduleModelPeriod>) periods).get(periods.size() - 1);
        installment.addInterestAmount(scheduleParams.getTotalOutstandingInterestPaymentDueToGrace());
        scheduleParams.addTotalRepaymentExpected(scheduleParams.getTotalOutstandingInterestPaymentDueToGrace());
        scheduleParams
                .addTotalCumulativeInterest(scheduleParams.getTotalOutstandingInterestPaymentDueToGrace());
        scheduleParams.setTotalOutstandingInterestPaymentDueToGrace(Money.zero(currency));
    }

    // determine fees and penalties for charges which depends on total
    // loan interest
    updatePeriodsWithCharges(currency, scheduleParams, periods, nonCompoundingCharges);

    // this block is to add extra re-payment schedules with interest portion
    // if the loan not paid with in loan term

    if (scheduleParams.getScheduleTillDate() != null) {
        currentDate = scheduleParams.getScheduleTillDate();
    }
    if (scheduleParams.applyInterestRecalculation() && scheduleParams.getLatePaymentMap().size() > 0
            && currentDate.isAfter(scheduleParams.getPeriodStartDate())) {
        Money totalInterest = addInterestOnlyRepaymentScheduleForCurrentdate(mc, loanApplicationTerms,
                holidayDetailDTO, currency, periods, currentDate, loanRepaymentScheduleTransactionProcessor,
                transactions, loanCharges, scheduleParams);
        scheduleParams.addTotalCumulativeInterest(totalInterest);
    }

    loanApplicationTerms.resetFixedEmiAmount();
    final BigDecimal totalPrincipalPaid = BigDecimal.ZERO;
    final BigDecimal totalOutstanding = BigDecimal.ZERO;

    updateCompoundingDetails(periods, scheduleParams, loanApplicationTerms);
    return LoanScheduleModel.from(periods, applicationCurrency, scheduleParams.getLoanTermInDays(),
            scheduleParams.getPrincipalToBeScheduled(),
            scheduleParams.getTotalCumulativePrincipal().getAmount(), totalPrincipalPaid,
            scheduleParams.getTotalCumulativeInterest().getAmount(),
            scheduleParams.getTotalFeeChargesCharged().getAmount(),
            scheduleParams.getTotalPenaltyChargesCharged().getAmount(),
            scheduleParams.getTotalRepaymentExpected().getAmount(), totalOutstanding);
}

From source file:com.gst.portfolio.loanaccount.loanschedule.domain.AbstractLoanScheduleGenerator.java

License:Apache License

private void updateCompoundingDetails(final Collection<LoanScheduleModelPeriod> periods,
        final LoanScheduleParams params, final LoanApplicationTerms loanApplicationTerms) {
    final Map<LocalDate, Map<LocalDate, Money>> compoundingDetails = params.getCompoundingDateVariations();
    if (compoundingDetails.isEmpty()) {
        return;//from w  w  w .  jav a  2s. c om
    }
    for (LoanScheduleModelPeriod loanScheduleModelPeriod : periods) {
        if (loanScheduleModelPeriod.isRepaymentPeriod()
                && loanScheduleModelPeriod.getLoanCompoundingDetails().isEmpty()) {
            Map<LocalDate, Money> periodCompoundingDetails = compoundingDetails
                    .get(loanScheduleModelPeriod.periodFromDate());
            if (periodCompoundingDetails != null) {
                for (Map.Entry<LocalDate, Money> entry : periodCompoundingDetails.entrySet()) {
                    if (entry.getValue().isGreaterThanZero()
                            && !entry.getKey().isAfter(loanScheduleModelPeriod.periodDueDate())) {
                        LocalDate effectiveDate = entry.getKey();
                        if (loanApplicationTerms.allowCompoundingOnEod()) {
                            effectiveDate = effectiveDate.minusDays(1);
                        }
                        LoanInterestRecalcualtionAdditionalDetails additionalDetails = new LoanInterestRecalcualtionAdditionalDetails(
                                effectiveDate, entry.getValue().getAmount());
                        loanScheduleModelPeriod.getLoanCompoundingDetails().add(additionalDetails);
                    }
                }
            }
        }
    }
}

From source file:com.gst.portfolio.loanaccount.loanschedule.domain.AbstractLoanScheduleGenerator.java

License:Apache License

private LoanScheduleModelPeriod handlePrepaymentOfLoan(final MathContext mc,
        final LoanApplicationTerms loanApplicationTerms, final HolidayDetailDTO holidayDetailDTO,
        final LoanScheduleParams scheduleParams, final Money totalInterestChargedForFullLoanTerm,
        final LocalDate scheduledDueDate, LocalDate periodStartDateApplicableForInterest,
        final double interestCalculationGraceOnRepaymentPeriodFraction,
        final ScheduleCurrentPeriodParams currentPeriodParams,
        final Money lastTotalOutstandingInterestPaymentDueToGrace, final LocalDate transactionDate,
        final LoanScheduleModelPeriod installment, Set<LoanCharge> loanCharges) {
    LoanScheduleModelPeriod modifiedInstallment = installment;
    if (!scheduleParams.getOutstandingBalance().isGreaterThan(currentPeriodParams.getInterestForThisPeriod())
            && !scheduledDueDate.equals(transactionDate)) {
        final Collection<LoanTermVariationsData> interestRates = loanApplicationTerms.getLoanTermVariations()
                .getInterestRateChanges();
        LocalDate calculateTill = transactionDate;
        if (loanApplicationTerms.getPreClosureInterestCalculationStrategy()
                .calculateTillRestFrequencyEnabled()) {
            calculateTill = getNextRestScheduleDate(calculateTill.minusDays(1), loanApplicationTerms,
                    holidayDetailDTO);/*  ww  w  .ja v a2  s .  c om*/
        }
        if (scheduleParams.getCompoundingDateVariations().containsKey(periodStartDateApplicableForInterest)) {
            scheduleParams.getCompoundingMap().clear();
            scheduleParams.getCompoundingMap().putAll(
                    scheduleParams.getCompoundingDateVariations().get(periodStartDateApplicableForInterest));
        }
        if (currentPeriodParams.isEmiAmountChanged()) {
            updateFixedInstallmentAmount(mc, loanApplicationTerms, scheduleParams.getPeriodNumber(),
                    loanApplicationTerms.getPrincipal().minus(scheduleParams.getTotalCumulativePrincipal()));
        }

        scheduleParams.getCompoundingDateVariations().put(periodStartDateApplicableForInterest,
                new TreeMap<>(scheduleParams.getCompoundingMap()));
        scheduleParams.getCompoundingMap().clear();
        populateCompoundingDatesInPeriod(periodStartDateApplicableForInterest, calculateTill,
                loanApplicationTerms, holidayDetailDTO, scheduleParams, loanCharges,
                totalInterestChargedForFullLoanTerm.getCurrency());

        // this is to make sure we are recalculating using correct interest rate 
        // once calculation is done system will set the actual interest rate
        BigDecimal currentInterestRate = loanApplicationTerms.getAnnualNominalInterestRate();
        for (LoanTermVariationsData interestRate : interestRates) {
            if (interestRate.isApplicable(periodStartDateApplicableForInterest)) {
                loanApplicationTerms.updateAnnualNominalInterestRate(interestRate.getDecimalValue());
            }
        }

        PrincipalInterest interestTillDate = calculatePrincipalInterestComponentsForPeriod(
                this.paymentPeriodsInOneYearCalculator, interestCalculationGraceOnRepaymentPeriodFraction,
                scheduleParams.getTotalCumulativePrincipal(), scheduleParams.getTotalCumulativeInterest(),
                totalInterestChargedForFullLoanTerm, lastTotalOutstandingInterestPaymentDueToGrace,
                scheduleParams.getOutstandingBalanceAsPerRest(), loanApplicationTerms,
                scheduleParams.getPeriodNumber(), mc, mergeVariationsToMap(scheduleParams),
                scheduleParams.getCompoundingMap(), periodStartDateApplicableForInterest, calculateTill,
                interestRates);
        loanApplicationTerms.updateAnnualNominalInterestRate(currentInterestRate);

        // applies charges for the period
        final ScheduleCurrentPeriodParams tempPeriod = new ScheduleCurrentPeriodParams(
                totalInterestChargedForFullLoanTerm.getCurrency(),
                interestCalculationGraceOnRepaymentPeriodFraction);
        tempPeriod.setInterestForThisPeriod(interestTillDate.interest());
        applyChargesForCurrentPeriod(loanCharges, totalInterestChargedForFullLoanTerm.getCurrency(),
                scheduleParams, calculateTill, tempPeriod);
        Money interestDiff = currentPeriodParams.getInterestForThisPeriod()
                .minus(tempPeriod.getInterestForThisPeriod());
        Money chargeDiff = currentPeriodParams.getFeeChargesForInstallment()
                .minus(tempPeriod.getFeeChargesForInstallment());
        Money penaltyDiff = currentPeriodParams.getPenaltyChargesForInstallment()
                .minus(tempPeriod.getPenaltyChargesForInstallment());

        Money diff = interestDiff.plus(chargeDiff).plus(penaltyDiff);
        if (scheduleParams.getOutstandingBalance().minus(diff).isGreaterThanZero()) {
            updateCompoundingDetails(scheduleParams, periodStartDateApplicableForInterest);
        } else {
            scheduleParams.reduceOutstandingBalance(diff);
            currentPeriodParams.minusInterestForThisPeriod(interestDiff);
            currentPeriodParams.minusFeeChargesForInstallment(chargeDiff);
            currentPeriodParams.minusPenaltyChargesForInstallment(penaltyDiff);
            currentPeriodParams.plusPrincipalForThisPeriod(diff);

            // create and replaces repayment period
            // from parts
            modifiedInstallment = LoanScheduleModelRepaymentPeriod.repayment(
                    scheduleParams.getInstalmentNumber(), scheduleParams.getPeriodStartDate(), transactionDate,
                    currentPeriodParams.getPrincipalForThisPeriod(), scheduleParams.getOutstandingBalance(),
                    currentPeriodParams.getInterestForThisPeriod(),
                    currentPeriodParams.getFeeChargesForInstallment(),
                    currentPeriodParams.getPenaltyChargesForInstallment(),
                    currentPeriodParams.fetchTotalAmountForPeriod(), false);
            scheduleParams
                    .setTotalOutstandingInterestPaymentDueToGrace(interestTillDate.interestPaymentDueToGrace());
        }
    }
    return modifiedInstallment;
}

From source file:com.gst.portfolio.loanaccount.loanschedule.domain.AbstractLoanScheduleGenerator.java

License:Apache License

private void handleRecalculationForNonDueDateTransactions(final MathContext mc,
        final LoanApplicationTerms loanApplicationTerms, final Set<LoanCharge> loanCharges,
        final HolidayDetailDTO holidayDetailDTO, LoanScheduleParams scheduleParams,
        final Collection<LoanScheduleModelPeriod> periods, final Money totalInterestChargedForFullLoanTerm,
        final LocalDate idealDisbursementDate, LocalDate firstRepaymentdate, final LocalDate lastRestDate,
        final LocalDate scheduledDueDate, final LocalDate periodStartDateForInterest,
        final Collection<RecalculationDetail> applicableTransactions,
        final ScheduleCurrentPeriodParams currentPeriodParams) {
    if (scheduleParams.applyInterestRecalculation()) {
        final MonetaryCurrency currency = scheduleParams.getCurrency();
        final Collection<LoanTermVariationsData> interestRates = loanApplicationTerms.getLoanTermVariations()
                .getInterestRateChanges();
        boolean checkForOutstanding = true;
        List<RecalculationDetail> unprocessedTransactions = new ArrayList<>();
        List<RecalculationDetail> processTransactions = new ArrayList<>();
        LoanScheduleModelPeriod installment = null;
        LocalDate periodStartDateApplicableForInterest = periodStartDateForInterest;
        for (RecalculationDetail detail : applicableTransactions) {
            if (detail.isProcessed()) {
                continue;
            }//  w  ww .  j a va 2 s.co  m
            boolean updateLatePaymentMap = false;
            final LocalDate transactionDate = detail.getTransactionDate();
            if (transactionDate.isBefore(scheduledDueDate)) {
                if (scheduleParams.getLoanRepaymentScheduleTransactionProcessor() != null
                        && scheduleParams.getLoanRepaymentScheduleTransactionProcessor()
                                .isInterestFirstRepaymentScheduleTransactionProcessor()) {
                    if (detail.getTransaction().isWaiver()) {
                        processTransactions.add(detail);
                        continue;
                    }
                    List<LoanTransaction> currentTransactions = new ArrayList<>();
                    for (RecalculationDetail processDetail : processTransactions) {
                        currentTransactions.addAll(createCurrentTransactionList(processDetail));
                    }
                    processTransactions.clear();
                    currentTransactions.addAll(createCurrentTransactionList(detail));

                    if (!transactionDate.isEqual(scheduleParams.getPeriodStartDate())
                            || scheduleParams.getInstalmentNumber() == 1) {

                        int periodDays = Days.daysBetween(scheduleParams.getPeriodStartDate(), transactionDate)
                                .getDays();
                        // calculates period start date for interest
                        // calculation as per the configuration
                        periodStartDateApplicableForInterest = calculateInterestStartDateForPeriod(
                                loanApplicationTerms, scheduleParams.getPeriodStartDate(),
                                idealDisbursementDate, firstRepaymentdate,
                                loanApplicationTerms.isInterestChargedFromDateSameAsDisbursalDateEnabled(),
                                loanApplicationTerms.getExpectedDisbursementDate());

                        int daysInPeriodApplicable = Days
                                .daysBetween(periodStartDateApplicableForInterest, transactionDate).getDays();
                        Money interestForThisinstallment = Money.zero(currency);
                        if (daysInPeriodApplicable > 0) {
                            // 5 determine interest till the transaction
                            // date
                            PrincipalInterest principalInterestForThisPeriod = calculatePrincipalInterestComponentsForPeriod(
                                    this.paymentPeriodsInOneYearCalculator,
                                    currentPeriodParams.getInterestCalculationGraceOnRepaymentPeriodFraction(),
                                    scheduleParams.getTotalCumulativePrincipal()
                                            .minus(scheduleParams.getReducePrincipal()),
                                    scheduleParams.getTotalCumulativeInterest(),
                                    totalInterestChargedForFullLoanTerm,
                                    scheduleParams.getTotalOutstandingInterestPaymentDueToGrace(),
                                    scheduleParams.getOutstandingBalanceAsPerRest(), loanApplicationTerms,
                                    scheduleParams.getPeriodNumber(), mc, mergeVariationsToMap(scheduleParams),
                                    scheduleParams.getCompoundingMap(), periodStartDateApplicableForInterest,
                                    transactionDate, interestRates);
                            interestForThisinstallment = principalInterestForThisPeriod.interest();

                            scheduleParams.setTotalOutstandingInterestPaymentDueToGrace(
                                    principalInterestForThisPeriod.interestPaymentDueToGrace());
                        }

                        Money principalForThisPeriod = Money.zero(currency);

                        // applies all the applicable charges to the
                        // newly
                        // created installment
                        PrincipalInterest principalInterest = new PrincipalInterest(principalForThisPeriod,
                                interestForThisinstallment, null);
                        Money feeChargesForInstallment = cumulativeFeeChargesDueWithin(
                                scheduleParams.getPeriodStartDate(), transactionDate, loanCharges, currency,
                                principalInterest, scheduleParams.getPrincipalToBeScheduled(),
                                scheduleParams.getTotalCumulativeInterest(), false);
                        Money penaltyChargesForInstallment = cumulativePenaltyChargesDueWithin(
                                scheduleParams.getPeriodStartDate(), transactionDate, loanCharges, currency,
                                principalInterest, scheduleParams.getPrincipalToBeScheduled(),
                                scheduleParams.getTotalCumulativeInterest(), false);

                        // sum up real totalInstallmentDue from
                        // components
                        final Money totalInstallmentDue = principalForThisPeriod
                                .plus(interestForThisinstallment).plus(feeChargesForInstallment)
                                .plus(penaltyChargesForInstallment);
                        // create repayment period from parts
                        installment = LoanScheduleModelRepaymentPeriod.repayment(
                                scheduleParams.getInstalmentNumber(), scheduleParams.getPeriodStartDate(),
                                transactionDate, principalForThisPeriod, scheduleParams.getOutstandingBalance(),
                                interestForThisinstallment, feeChargesForInstallment,
                                penaltyChargesForInstallment, totalInstallmentDue, true);
                        periods.add(installment);
                        addLoanRepaymentScheduleInstallment(scheduleParams.getInstallments(), installment);
                        updateCompoundingMap(loanApplicationTerms, holidayDetailDTO, scheduleParams,
                                lastRestDate, scheduledDueDate);

                        // update outstanding balance for interest
                        // calculation as per the rest
                        updateOutstandingBalanceAsPerRest(scheduleParams, transactionDate);

                        // handle cumulative fields
                        scheduleParams.addLoanTermInDays(periodDays);
                        scheduleParams.addTotalRepaymentExpected(totalInstallmentDue);
                        scheduleParams.addTotalCumulativeInterest(interestForThisinstallment);
                        scheduleParams.addTotalFeeChargesCharged(feeChargesForInstallment);
                        scheduleParams.addTotalPenaltyChargesCharged(penaltyChargesForInstallment);

                        scheduleParams.setPeriodStartDate(transactionDate);
                        periodStartDateApplicableForInterest = scheduleParams.getPeriodStartDate();
                        updateLatePaymentMap = true;
                        scheduleParams.incrementInstalmentNumber();
                        populateCompoundingDatesInPeriod(scheduleParams.getPeriodStartDate(), scheduledDueDate,
                                loanApplicationTerms, holidayDetailDTO, scheduleParams, loanCharges, currency);
                        // creates and insert Loan repayment schedule
                        // for
                        // the period

                    } else if (installment == null) {
                        installment = ((List<LoanScheduleModelPeriod>) periods).get(periods.size() - 1);
                    }
                    // applies the transaction as per transaction
                    // strategy
                    // on scheduled installments to identify the
                    // unprocessed(early payment ) amounts
                    Money unprocessed = scheduleParams.getLoanRepaymentScheduleTransactionProcessor()
                            .handleRepaymentSchedule(currentTransactions, currency,
                                    scheduleParams.getInstallments());
                    if (unprocessed.isGreaterThanZero()) {

                        if (loanApplicationTerms.getPreClosureInterestCalculationStrategy()
                                .calculateTillRestFrequencyEnabled()) {
                            LocalDate applicableDate = getNextRestScheduleDate(transactionDate.minusDays(1),
                                    loanApplicationTerms, holidayDetailDTO);
                            checkForOutstanding = transactionDate.isEqual(applicableDate);

                        }
                        // reduces actual outstanding balance
                        scheduleParams.reduceOutstandingBalance(unprocessed);
                        // if outstanding balance becomes less than zero
                        // then adjusts the princiapal
                        Money addToPrinciapal = Money.zero(currency);
                        if (!scheduleParams.getOutstandingBalance().isGreaterThanZero()) {
                            addToPrinciapal = addToPrinciapal.plus(scheduleParams.getOutstandingBalance());
                            scheduleParams.setOutstandingBalance(Money.zero(currency));
                            currentPeriodParams.setLastInstallment(installment);
                        }
                        // updates principal portion map with the early
                        // payment amounts and applicable date as per
                        // rest
                        updateAmountsBasedOnEarlyPayment(loanApplicationTerms, holidayDetailDTO, scheduleParams,
                                installment, detail, unprocessed, addToPrinciapal);

                        // method applies early payment strategy
                        scheduleParams.addReducePrincipal(unprocessed);
                        scheduleParams.setReducePrincipal(applyEarlyPaymentStrategy(loanApplicationTerms,
                                scheduleParams.getReducePrincipal(),
                                scheduleParams.getTotalCumulativePrincipal(), scheduleParams.getPeriodNumber(),
                                mc));
                    }
                    // identify late payments and add compounding
                    // details to
                    // map for interest calculation
                    handleLatePayments(loanApplicationTerms, holidayDetailDTO, currency, scheduleParams,
                            lastRestDate, detail);
                    if (updateLatePaymentMap) {
                        updateLatePaymentsToMap(loanApplicationTerms, holidayDetailDTO, currency,
                                scheduleParams.getLatePaymentMap(), scheduledDueDate,
                                scheduleParams.getInstallments(), true, lastRestDate);
                    }
                } else if (scheduleParams.getLoanRepaymentScheduleTransactionProcessor() != null) {
                    LocalDate applicableDate = getNextRestScheduleDate(transactionDate.minusDays(1),
                            loanApplicationTerms, holidayDetailDTO);
                    if (applicableDate.isBefore(scheduledDueDate)) {
                        List<LoanTransaction> currentTransactions = createCurrentTransactionList(detail);
                        Money unprocessed = scheduleParams.getLoanRepaymentScheduleTransactionProcessor()
                                .handleRepaymentSchedule(currentTransactions, currency,
                                        scheduleParams.getInstallments());
                        Money arrears = fetchArrears(loanApplicationTerms, currency, detail.getTransaction());
                        if (unprocessed.isGreaterThanZero()) {
                            updateMapWithAmount(scheduleParams.getPrincipalPortionMap(), unprocessed,
                                    applicableDate);
                            currentPeriodParams.plusEarlyPaidAmount(unprocessed);

                            // this check is to identify pre-closure and
                            // apply interest calculation as per
                            // configuration for non due date payments
                            if (!scheduleParams.getOutstandingBalance().isGreaterThan(unprocessed)
                                    && !loanApplicationTerms.getPreClosureInterestCalculationStrategy()
                                            .calculateTillRestFrequencyEnabled()) {

                                scheduleParams.getCompoundingDateVariations().put(
                                        periodStartDateApplicableForInterest,
                                        new TreeMap<>(scheduleParams.getCompoundingMap()));
                                LocalDate calculateTill = transactionDate;
                                PrincipalInterest principalInterestForThisPeriod = calculatePrincipalInterestComponentsForPeriod(
                                        this.paymentPeriodsInOneYearCalculator,
                                        currentPeriodParams
                                                .getInterestCalculationGraceOnRepaymentPeriodFraction(),
                                        scheduleParams.getTotalCumulativePrincipal()
                                                .minus(scheduleParams.getReducePrincipal()),
                                        scheduleParams.getTotalCumulativeInterest(),
                                        totalInterestChargedForFullLoanTerm,
                                        scheduleParams.getTotalOutstandingInterestPaymentDueToGrace(),
                                        scheduleParams.getOutstandingBalanceAsPerRest(), loanApplicationTerms,
                                        scheduleParams.getPeriodNumber(), mc,
                                        mergeVariationsToMap(scheduleParams),
                                        scheduleParams.getCompoundingMap(),
                                        periodStartDateApplicableForInterest, calculateTill, interestRates);
                                if (!principalInterestForThisPeriod.interest()
                                        .plus(principalInterestForThisPeriod.interestPaymentDueToGrace())
                                        .plus(scheduleParams.getOutstandingBalance())
                                        .isGreaterThan(unprocessed)) {
                                    currentPeriodParams.minusEarlyPaidAmount(unprocessed);
                                    updateMapWithAmount(scheduleParams.getPrincipalPortionMap(),
                                            unprocessed.negated(), applicableDate);
                                    LoanTransaction loanTransaction = LoanTransaction.repayment(null,
                                            unprocessed, null, transactionDate, null,
                                            DateUtils.getLocalDateTimeOfTenant(), null);
                                    RecalculationDetail recalculationDetail = new RecalculationDetail(
                                            transactionDate, loanTransaction);
                                    unprocessedTransactions.add(recalculationDetail);
                                    break;
                                }
                            }
                            LoanTransaction loanTransaction = LoanTransaction.repayment(null, unprocessed, null,
                                    scheduledDueDate, null, DateUtils.getLocalDateTimeOfTenant(), null);
                            RecalculationDetail recalculationDetail = new RecalculationDetail(scheduledDueDate,
                                    loanTransaction);
                            unprocessedTransactions.add(recalculationDetail);
                            checkForOutstanding = false;

                            scheduleParams.reduceOutstandingBalance(unprocessed);
                            // if outstanding balance becomes less than
                            // zero
                            // then adjusts the princiapal
                            Money addToPrinciapal = Money.zero(currency);
                            if (scheduleParams.getOutstandingBalance().isLessThanZero()) {
                                addToPrinciapal = addToPrinciapal.plus(scheduleParams.getOutstandingBalance());
                                scheduleParams.setOutstandingBalance(Money.zero(currency));
                                updateMapWithAmount(scheduleParams.getPrincipalPortionMap(), addToPrinciapal,
                                        applicableDate);
                                currentPeriodParams.plusEarlyPaidAmount(addToPrinciapal);
                            }

                        }
                        if (arrears.isGreaterThanZero() && applicableDate.isBefore(lastRestDate)) {
                            handleLatePayments(loanApplicationTerms, holidayDetailDTO, currency, scheduleParams,
                                    lastRestDate, detail);
                        }
                    }

                }
            }

        }
        applicableTransactions.addAll(unprocessedTransactions);
        if (checkForOutstanding && scheduleParams.getOutstandingBalance().isZero()
                && scheduleParams.getDisburseDetailMap().isEmpty()) {
            currentPeriodParams.setSkipCurrentLoop(true);
        }
    }
}

From source file:com.gst.portfolio.loanaccount.loanschedule.domain.AbstractLoanScheduleGenerator.java

License:Apache License

/**
 * Method calculates interest on not paid outstanding principal and interest
 * (if compounding is enabled) till current date and adds new repayment
 * schedule detail/*from   w w w  .  j ava2  s  . co m*/
 * 
 * @param compoundingMap
 *            TODO
 * @param loanCharges
 *            TODO
 * @param principalPortioMap
 *            TODO
 * 
 */
private Money addInterestOnlyRepaymentScheduleForCurrentdate(final MathContext mc,
        final LoanApplicationTerms loanApplicationTerms, final HolidayDetailDTO holidayDetailDTO,
        final MonetaryCurrency currency, final Collection<LoanScheduleModelPeriod> periods,
        final LocalDate currentDate,
        LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor,
        final Collection<RecalculationDetail> transactions, final Set<LoanCharge> loanCharges,
        final LoanScheduleParams params) {
    boolean isFirstRepayment = false;
    LocalDate startDate = params.getPeriodStartDate();
    Money outstanding = params.getOutstandingBalanceAsPerRest();
    Money totalInterest = Money.zero(currency);
    Money totalCumulativeInterest = Money.zero(currency);
    double interestCalculationGraceOnRepaymentPeriodFraction = Double.valueOf(0);
    int periodNumberTemp = 1;
    LocalDate lastRestDate = getNextRestScheduleDate(currentDate.minusDays(1), loanApplicationTerms,
            holidayDetailDTO);
    Collection<LoanTermVariationsData> applicableVariations = loanApplicationTerms.getLoanTermVariations()
            .getInterestRateChanges();
    Money uncompoundedFromLastInstallment = params.getUnCompoundedAmount();
    LocalDate additionalPeriodsStartDate = params.getPeriodStartDate();

    do {

        params.setActualRepaymentDate(this.scheduledDateGenerator.generateNextRepaymentDate(
                params.getActualRepaymentDate(), loanApplicationTerms, isFirstRepayment, holidayDetailDTO));
        if (params.getActualRepaymentDate().isAfter(currentDate)) {
            params.setActualRepaymentDate(currentDate);
        }

        Collection<RecalculationDetail> applicableTransactions = getApplicableTransactionsForPeriod(
                params.applyInterestRecalculation(), params.getActualRepaymentDate(), transactions);

        populateCompoundingDatesInPeriod(params.getPeriodStartDate(), params.getActualRepaymentDate(),
                loanApplicationTerms, holidayDetailDTO, params, loanCharges, currency);

        for (RecalculationDetail detail : applicableTransactions) {
            if (detail.isProcessed()) {
                continue;
            }
            LocalDate transactionDate = detail.getTransactionDate();
            List<LoanTransaction> currentTransactions = createCurrentTransactionList(detail);

            if (!params.getPeriodStartDate().isEqual(transactionDate)) {
                PrincipalInterest principalInterestForThisPeriod = calculatePrincipalInterestComponentsForPeriod(
                        this.paymentPeriodsInOneYearCalculator,
                        interestCalculationGraceOnRepaymentPeriodFraction, totalInterest.zero(),
                        totalInterest.zero(), totalInterest.zero(), totalInterest.zero(), outstanding,
                        loanApplicationTerms, periodNumberTemp, mc, mergeVariationsToMap(params),
                        params.getCompoundingMap(), params.getPeriodStartDate(), transactionDate,
                        applicableVariations);

                Money interest = principalInterestForThisPeriod.interest();
                totalInterest = totalInterest.plus(interest);

                LoanScheduleModelRepaymentPeriod installment = LoanScheduleModelRepaymentPeriod.repayment(
                        params.getInstalmentNumber(), startDate, transactionDate, totalInterest.zero(),
                        totalInterest.zero(), totalInterest, totalInterest.zero(), totalInterest.zero(),
                        totalInterest, true);
                params.incrementInstalmentNumber();
                periods.add(installment);
                totalCumulativeInterest = totalCumulativeInterest.plus(totalInterest);
                totalInterest = totalInterest.zero();
                addLoanRepaymentScheduleInstallment(params.getInstallments(), installment);
                updateCompoundingMap(loanApplicationTerms, holidayDetailDTO, params, lastRestDate,
                        transactionDate);
                populateCompoundingDatesInPeriod(installment.periodDueDate(), params.getActualRepaymentDate(),
                        loanApplicationTerms, holidayDetailDTO, params, loanCharges, currency);
                uncompoundedFromLastInstallment = params.getUnCompoundedAmount();
                params.setPeriodStartDate(transactionDate);
                startDate = transactionDate;
                additionalPeriodsStartDate = startDate;
            }
            loanRepaymentScheduleTransactionProcessor.handleRepaymentSchedule(currentTransactions, currency,
                    params.getInstallments());
            updateLatePaidAmountsToPrincipalMap(detail.getTransaction(), loanApplicationTerms, currency,
                    holidayDetailDTO, lastRestDate, params);
            updateLatePaymentsToMap(loanApplicationTerms, holidayDetailDTO, currency,
                    params.getLatePaymentMap(), currentDate, params.getInstallments(), false, lastRestDate);
            if (params.getLatePaymentMap().isEmpty() && isCompleted(params.getInstallments())) {
                outstanding = outstanding.zero();
            } else {
                outstanding = updateBalanceForInterestCalculation(params.getPrincipalPortionMap(),
                        params.getPeriodStartDate(), outstanding, false);
            }
            if (params.getLatePaymentMap().isEmpty() && outstanding.isZero()) {
                break;
            }
        }

        if (!outstanding.isZero()) {
            PrincipalInterest principalInterestForThisPeriod = calculatePrincipalInterestComponentsForPeriod(
                    this.paymentPeriodsInOneYearCalculator, interestCalculationGraceOnRepaymentPeriodFraction,
                    totalInterest.zero(), totalInterest.zero(), totalInterest.zero(), totalInterest.zero(),
                    outstanding, loanApplicationTerms, periodNumberTemp, mc, mergeVariationsToMap(params),
                    params.getCompoundingMap(), params.getPeriodStartDate(), params.getActualRepaymentDate(),
                    applicableVariations);
            Money interest = principalInterestForThisPeriod.interest();
            totalInterest = totalInterest.plus(interest);

            if (loanApplicationTerms.getInterestRecalculationCompoundingMethod().isCompoundingEnabled()) {
                Money uncompounded = params.getUnCompoundedAmount();
                Money compounded = uncompounded.zero();
                for (Map.Entry<LocalDate, Money> mapEntry : params.getCompoundingMap().entrySet()) {
                    if (mapEntry.getKey().isAfter(params.getPeriodStartDate())) {
                        compounded = compounded.plus(mapEntry.getValue());
                    }
                }
                if (compounded.isGreaterThanZero() && startDate.isEqual(additionalPeriodsStartDate)) {
                    params.setCompoundedInLastInstallment(uncompoundedFromLastInstallment);// uncompounded in last installment
                    additionalPeriodsStartDate = additionalPeriodsStartDate.plusDays(1);
                }
                Money compoundedForThisPeriod = compounded.minus(uncompounded);
                Money uncompoundedForThisPeriod = interest.minus(compoundedForThisPeriod);
                params.setUnCompoundedAmount(uncompoundedForThisPeriod);
                LocalDate compoundingDate = params.getPeriodStartDate();
                if (loanApplicationTerms.allowCompoundingOnEod()) {
                    compoundingDate = compoundingDate.minusDays(1);
                }
                compoundingDate = getNextCompoundScheduleDate(compoundingDate, loanApplicationTerms,
                        holidayDetailDTO);
                if (compoundingDate.isEqual(params.getActualRepaymentDate())) {
                    params.getCompoundingMap().put(compoundingDate, uncompoundedForThisPeriod);
                    params.setUnCompoundedAmount(uncompoundedForThisPeriod.zero());
                }
            }
        }
        params.setPeriodStartDate(params.getActualRepaymentDate());
    } while (params.getActualRepaymentDate().isBefore(currentDate) && !outstanding.isZero());

    if (totalInterest.isGreaterThanZero()) {
        LoanScheduleModelRepaymentPeriod installment = LoanScheduleModelRepaymentPeriod.repayment(
                params.getInstalmentNumber(), startDate, params.getActualRepaymentDate(), totalInterest.zero(),
                totalInterest.zero(), totalInterest, totalInterest.zero(), totalInterest.zero(), totalInterest,
                true);
        params.incrementInstalmentNumber();
        periods.add(installment);
        params.getCompoundingDateVariations().put(startDate, new TreeMap<>(params.getCompoundingMap()));
        totalCumulativeInterest = totalCumulativeInterest.plus(totalInterest);
    }
    return totalCumulativeInterest;
}

From source file:com.gst.portfolio.loanaccount.loanschedule.domain.AbstractLoanScheduleGenerator.java

License:Apache License

private void updateCompoundingMap(final LoanApplicationTerms loanApplicationTerms,
        final HolidayDetailDTO holidayDetailDTO, final LoanScheduleParams params, final LocalDate lastRestDate,
        final LocalDate scheduledDueDate) {
    if (loanApplicationTerms.isInterestRecalculationEnabled()
            && loanApplicationTerms.getInterestRecalculationCompoundingMethod().isCompoundingEnabled()) {
        final MonetaryCurrency currency = params.getCurrency();
        Money totalCompoundedAmount = Money.zero(currency);
        boolean lastInstallmentIsPastDate = false;
        for (LoanRepaymentScheduleInstallment loanRepaymentScheduleInstallment : params.getInstallments()) {
            if (params.getCompoundingDateVariations()
                    .containsKey(loanRepaymentScheduleInstallment.getFromDate())) {
                lastInstallmentIsPastDate = params.applyInterestRecalculation()
                        && loanRepaymentScheduleInstallment.getDueDate()
                                .isBefore(DateUtils.getLocalDateOfTenant());
            } else {
                final boolean isPastDate = params.applyInterestRecalculation()
                        && loanRepaymentScheduleInstallment.getDueDate()
                                .isBefore(DateUtils.getLocalDateOfTenant());
                boolean periodHasCompoundingDate = false;
                Money amountCharged = Money.zero(currency);
                if (loanApplicationTerms.getInterestRecalculationCompoundingMethod() != null) {
                    amountCharged = getIncomeForCompounding(loanApplicationTerms, currency,
                            loanRepaymentScheduleInstallment);
                }//  w  w w.  jav  a2 s  . co m
                final Map<LocalDate, Money> compoundingMap = params.getCompoundingMap();
                LocalDate effectiveStartDate = loanRepaymentScheduleInstallment.getFromDate();
                if (loanApplicationTerms.allowCompoundingOnEod()) {
                    effectiveStartDate = loanRepaymentScheduleInstallment.getFromDate().minusDays(1);
                }
                LocalDate compoundingEffectiveDate = getNextCompoundScheduleDate(effectiveStartDate,
                        loanApplicationTerms, holidayDetailDTO);
                final LocalDate restDate = getNextRestScheduleDate(scheduledDueDate.minusDays(1),
                        loanApplicationTerms, holidayDetailDTO);
                if (!compoundingEffectiveDate.isAfter(loanRepaymentScheduleInstallment.getDueDate())) {
                    Money amountCompoundedFromLastPeriod = params.getCompoundedInLastInstallment();
                    if (amountCompoundedFromLastPeriod.isZero()) {
                        amountCompoundedFromLastPeriod = params.getUnCompoundedAmount();
                    }
                    totalCompoundedAmount = totalCompoundedAmount.minus(amountCompoundedFromLastPeriod);
                    periodHasCompoundingDate = true;
                }
                while (!compoundingEffectiveDate.isAfter(loanRepaymentScheduleInstallment.getDueDate())) {
                    if (compoundingEffectiveDate.isEqual(loanRepaymentScheduleInstallment.getDueDate())) {
                        Money amountToBeCompounding = amountCharged.minus(totalCompoundedAmount);
                        updateMapWithAmount(compoundingMap, amountToBeCompounding, compoundingEffectiveDate);
                        totalCompoundedAmount = totalCompoundedAmount.plus(amountToBeCompounding);

                    } else if (compoundingMap.containsKey(compoundingEffectiveDate)) {
                        Money compounedAmount = compoundingMap.get(compoundingEffectiveDate);
                        totalCompoundedAmount = totalCompoundedAmount.plus(compounedAmount);
                    }

                    if (!loanApplicationTerms.allowCompoundingOnEod()) {
                        compoundingEffectiveDate = compoundingEffectiveDate.plusDays(1);
                    }
                    compoundingEffectiveDate = getNextCompoundScheduleDate(compoundingEffectiveDate,
                            loanApplicationTerms, holidayDetailDTO);
                }
                if (periodHasCompoundingDate) {
                    if (isPastDate) {
                        updateMapWithAmount(params.getPrincipalPortionMap(),
                                totalCompoundedAmount.plus(params.getUnCompoundedAmount()), lastRestDate);
                    } else {
                        Money amountToBeEffected = amountCharged;
                        if (lastInstallmentIsPastDate) {
                            amountToBeEffected = amountToBeEffected.plus(params.getUnCompoundedAmount());
                        }
                        updateMapWithAmount(params.getPrincipalPortionMap(), amountToBeEffected, restDate);
                    }
                }
                if (totalCompoundedAmount.isGreaterThanZero()) {
                    params.getCompoundingDateVariations().put(loanRepaymentScheduleInstallment.getFromDate(),
                            new TreeMap<>(params.getCompoundingMap()));
                    for (Map.Entry<LocalDate, Money> mapEntry : params.getCompoundingMap().entrySet()) {
                        if (!mapEntry.getKey().isAfter(loanRepaymentScheduleInstallment.getDueDate())) {
                            updateMapWithAmount(params.getPrincipalPortionMap(), mapEntry.getValue().negated(),
                                    mapEntry.getKey());
                        }
                    }
                    params.minusUnCompoundedAmount(params.getUnCompoundedAmount());
                    params.getCompoundingMap().clear();
                    params.addUnCompoundedAmount(amountCharged.minus(totalCompoundedAmount));
                } else {
                    params.getCompoundingMap().clear();
                    params.getCompoundingDateVariations().put(loanRepaymentScheduleInstallment.getFromDate(),
                            new TreeMap<>(params.getCompoundingMap()));
                    params.addUnCompoundedAmount(amountCharged);
                }
                params.setCompoundedInLastInstallment(amountCharged.zero());
                lastInstallmentIsPastDate = isPastDate;
            }

        }
    }

}

From source file:com.gst.portfolio.loanaccount.loanschedule.domain.AbstractLoanScheduleGenerator.java

License:Apache License

private void populateCompoundingDatesInPeriod(final LocalDate startDate, final LocalDate endDate,
        final LoanApplicationTerms loanApplicationTerms, final HolidayDetailDTO holidayDetailDTO,
        final LoanScheduleParams scheduleParams, final Set<LoanCharge> charges, MonetaryCurrency currency) {
    if (loanApplicationTerms.getInterestRecalculationCompoundingMethod().isCompoundingEnabled()) {
        final Map<LocalDate, Money> compoundingMap = scheduleParams.getCompoundingMap();
        LocalDate lastCompoundingDate = startDate;
        LocalDate compoundingDate = startDate;
        boolean addUncompounded = true;
        while (compoundingDate.isBefore(endDate)) {
            if (loanApplicationTerms.allowCompoundingOnEod()) {
                compoundingDate = compoundingDate.minusDays(1);
            }//from ww  w  .  j a v  a2s  .c om
            compoundingDate = getNextCompoundScheduleDate(compoundingDate, loanApplicationTerms,
                    holidayDetailDTO);

            if (compoundingDate.isBefore(endDate)) {
                Money feeChargesForInstallment = cumulativeFeeChargesDueWithin(lastCompoundingDate,
                        compoundingDate, charges, currency, null, loanApplicationTerms.getPrincipal(), null,
                        false);
                Money penaltyChargesForInstallment = cumulativePenaltyChargesDueWithin(lastCompoundingDate,
                        compoundingDate, charges, currency, null, loanApplicationTerms.getPrincipal(), null,
                        false);
                Money compoundAmount = feeChargesForInstallment.plus(penaltyChargesForInstallment);
                if (addUncompounded) {
                    compoundAmount = compoundAmount.plus(scheduleParams.getUnCompoundedAmount());
                    addUncompounded = false;
                }
                updateMapWithAmount(compoundingMap, compoundAmount, compoundingDate);
            }

            lastCompoundingDate = compoundingDate;
        }
    }
}

From source file:com.gst.portfolio.loanaccount.loanschedule.domain.AbstractLoanScheduleGenerator.java

License:Apache License

private LoanScheduleDTO rescheduleNextInstallments(final MathContext mc,
        final LoanApplicationTerms loanApplicationTerms, Loan loan, final HolidayDetailDTO holidayDetailDTO,
        final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor,
        final LocalDate rescheduleFrom, final LocalDate scheduleTillDate) {
    // Loan transactions to process and find the variation on payments
    Collection<RecalculationDetail> recalculationDetails = new ArrayList<>();
    List<LoanTransaction> transactions = loan.getLoanTransactions();
    for (LoanTransaction loanTransaction : transactions) {
        if (loanTransaction.isPaymentTransaction()) {
            recalculationDetails.add(new RecalculationDetail(loanTransaction.getTransactionDate(),
                    LoanTransaction.copyTransactionProperties(loanTransaction)));
        }//from w  ww.j av  a 2s .c o m
    }
    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(
                loan.charges());
        periods = createNewLoanScheduleListWithDisbursementDetails(
                loanApplicationTerms.fetchNumberOfRepaymentsAfterExceptions(), loanApplicationTerms,
                chargesDueAtTimeOfDisbursement);
        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();

        Money uncompoundedAmount = 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<>();
        final Map<LocalDate, Map<LocalDate, Money>> compoundingDateVariations = new HashMap<>();
        LocalDate currentDate = DateUtils.getLocalDateOfTenant();
        LocalDate lastRestDate = currentDate;
        if (loanApplicationTerms.isInterestRecalculationEnabled()) {
            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);

        // count periods without interest grace to exclude for flat loan calculations

        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;

        List<LoanTermVariationsData> exceptionDataList = loanApplicationTerms.getLoanTermVariations()
                .getExceptionData();
        final ListIterator<LoanTermVariationsData> exceptionDataListIterator = exceptionDataList.listIterator();
        LoanTermVariationParams loanTermVariationParams = null;

        // identify retain installments
        final List<LoanRepaymentScheduleInstallment> processInstallmentsInstallments = fetchRetainedInstallments(
                loan.getRepaymentScheduleInstallments(), rescheduleFrom, currency);
        final List<LoanRepaymentScheduleInstallment> newRepaymentScheduleInstallments = new ArrayList<>();

        // 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 : processInstallmentsInstallments) {
            // 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)) {
                if (totalCumulativePrincipal
                        .isGreaterThanOrEqualTo(loanApplicationTerms.getTotalDisbursedAmount())) {
                    break;
                }
                ArrayList<LoanTermVariationsData> dueDateVariationsDataList = new ArrayList<>();

                // check for date changes

                do {
                    actualRepaymentDate = this.scheduledDateGenerator.generateNextRepaymentDate(
                            actualRepaymentDate, loanApplicationTerms, isFirstRepayment, holidayDetailDTO);
                    isFirstRepayment = false;
                    lastInstallmentDate = this.scheduledDateGenerator.adjustRepaymentDate(actualRepaymentDate,
                            loanApplicationTerms, holidayDetailDTO);
                    while (loanApplicationTerms.getLoanTermVariations()
                            .hasDueDateVariation(lastInstallmentDate)) {
                        LoanTermVariationsData variation = loanApplicationTerms.getLoanTermVariations()
                                .nextDueDateVariation();
                        if (!variation.isSpecificToInstallment()) {
                            actualRepaymentDate = variation.getDateValue();
                            /*if (!isDueDateChangeApplied) {
                            previousRepaymentDate = actualRepaymentDate;
                            isDueDateChangeApplied = true;
                            }*/
                            lastInstallmentDate = actualRepaymentDate;
                        }
                        dueDateVariationsDataList.add(variation);
                    }
                    loanTermVariationParams = applyExceptionLoanTermVariations(loanApplicationTerms,
                            lastInstallmentDate, exceptionDataListIterator, instalmentNumber,
                            totalCumulativePrincipal, totalCumulativeInterest, mc);
                } while (loanTermVariationParams != null && loanTermVariationParams.isSkipPeriod());

                /*if (!lastInstallmentDate.isBefore(rescheduleFrom)) {
                actualRepaymentDate = previousRepaymentDate;
                if(isDueDateChangeApplied){
                    int numberOfDateChangesApplied = dueDateVariationsDataList.size();
                    while(numberOfDateChangesApplied >0 ){
                        loanApplicationTerms.getLoanTermVariations().previousDueDateVariation();
                        numberOfDateChangesApplied--;
                    }
                }
                break;
                }*/
                periodNumber++;

                for (LoanTermVariationsData dueDateVariation : dueDateVariationsDataList) {
                    dueDateVariation.setProcessed(true);
                }

                if (loanTermVariationParams != null && loanTermVariationParams.isSkipPeriod()) {
                    ArrayList<LoanTermVariationsData> variationsDataList = loanTermVariationParams
                            .getVariationsDataList();
                    for (LoanTermVariationsData variationsData : variationsDataList) {
                        variationsData.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();

            if (loanApplicationTerms.isInterestRecalculationEnabled()) {

                // 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 = null;
                if (loanApplicationTerms.getRestCalendarInstance() != null) {
                    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);
                uncompoundedAmount = updateCompoundingDetailsForPartialScheduleGeneration(installment,
                        loanApplicationTerms, principalPortionMap, compoundingDateVariations,
                        uncompoundedAmount, applicableTransactions, lastRestDate, holidayDetailDTO);

                // update outstanding balance for interest calculation
                outstandingBalanceAsPerRest = updateBalanceForInterestCalculation(principalPortionMap,
                        installment.getDueDate(), outstandingBalanceAsPerRest, false);
                outstandingBalanceAsPerRest = updateBalanceForInterestCalculation(disburseDetailMap,
                        installment.getDueDate(), outstandingBalanceAsPerRest, true);
                // updates the map with over due amounts
                updateLatePaymentsToMap(loanApplicationTerms, holidayDetailDTO, currency, latePaymentMap,
                        lastInstallmentDate, newRepaymentScheduleInstallments, true, lastRestDate);
            } else {
                outstandingBalanceAsPerRest = outstandingBalance;
            }
        }
        totalRepaymentExpected = totalCumulativePrincipal.plus(totalCumulativeInterest)
                .plus(totalFeeChargesCharged).plus(totalPenaltyChargesCharged);

        // 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, uncompoundedAmount, disburseDetailMap,
                    principalToBeScheduled, outstandingBalance, outstandingBalanceAsPerRest,
                    newRepaymentScheduleInstallments, recalculationDetails,
                    loanRepaymentScheduleTransactionProcessor, scheduleTillDate, currency,
                    applyInterestRecalculation);
            retainedInstallments.addAll(newRepaymentScheduleInstallments);
            loanScheduleParams.getCompoundingDateVariations().putAll(compoundingDateVariations);
            loanApplicationTerms.updateTotalInterestDue(
                    Money.of(currency, loan.getLoanSummary().getTotalInterestCharged()));
        } else {
            loanApplicationTerms.getLoanTermVariations().resetVariations();
        }

    }
    // for complete schedule generation
    if (loanScheduleParams == null) {
        loanScheduleParams = LoanScheduleParams.createLoanScheduleParamsForCompleteUpdate(recalculationDetails,
                loanRepaymentScheduleTransactionProcessor, scheduleTillDate, applyInterestRecalculation);
        periods.clear();
    }
    LoanScheduleModel loanScheduleModel = generate(mc, loanApplicationTerms, loan.charges(), 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);
}