Example usage for org.joda.time LocalDate isBefore

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

Introduction

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

Prototype

public boolean isBefore(ReadablePartial partial) 

Source Link

Document

Is this partial earlier than the specified partial.

Usage

From source file: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);//w  w  w.  jav a 2 s  .  c  o  m
    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 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;
            }//from w w w .  j a v  a2s  .  com
            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

private void updateLatePaymentCompoundingAmount(final Map<LocalDate, Money> principalVariationMap,
        final Map<LocalDate, Money> latePaymentCompoundingMap, final MonetaryCurrency currency,
        final LocalDate lastRestDate, Money compoundedPortion, final LocalDate applicableDate) {
    Money appliedOnPrincipalVariationMap = Money.zero(currency);
    Map<LocalDate, Money> temp = new HashMap<>();
    for (LocalDate date : latePaymentCompoundingMap.keySet()) {
        if (date.isBefore(lastRestDate)) {
            Money money = latePaymentCompoundingMap.get(date);
            appliedOnPrincipalVariationMap = appliedOnPrincipalVariationMap.plus(money);
            if (appliedOnPrincipalVariationMap.isLessThan(compoundedPortion)) {
                if (date.isBefore(applicableDate)) {
                    updateMapWithAmount(principalVariationMap, money.negated(), date);
                    updateMapWithAmount(principalVariationMap, money, applicableDate);
                }/*from w w  w. j  a  v a 2s.  co m*/
            } else if (temp.isEmpty()) {
                Money diff = money.minus(appliedOnPrincipalVariationMap.minus(compoundedPortion));
                updateMapWithAmount(principalVariationMap, diff.negated(), date);
                updateMapWithAmount(principalVariationMap, diff, applicableDate);
                updateMapWithAmount(temp, money.minus(diff), date);
                updateMapWithAmount(temp, money.minus(diff).negated(), lastRestDate);
            } else {
                updateMapWithAmount(temp, money, date);
                updateMapWithAmount(temp, money.negated(), lastRestDate);
            }
        }
    }
    latePaymentCompoundingMap.clear();
    latePaymentCompoundingMap.putAll(temp);
}

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

License:Apache License

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

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

        }
    }
    if (totalCompoundingAmount.isGreaterThanZero()) {
        updateMapWithAmount(latePaymentMap, totalCompoundingAmount.negated(), lastRestDate);
    }
}

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);
            }//  w w  w .  j av  a 2  s .  c  o  m
            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

/**
 * calculates Interest stating date as per the settings
 * //from  www .  ja va  2  s  . com
 * @param firstRepaymentdate
 *            TODO
 * @param boolean1
 * @param localDate
 */
private LocalDate calculateInterestStartDateForPeriod(final LoanApplicationTerms loanApplicationTerms,
        LocalDate periodStartDate, final LocalDate idealDisbursementDate, final LocalDate firstRepaymentdate,
        final Boolean isInterestChargedFromDateSameAsDisbursalDateEnabled,
        final LocalDate expectedDisbursementDate) {
    LocalDate periodStartDateApplicableForInterest = periodStartDate;
    if (periodStartDate.isBefore(idealDisbursementDate) || firstRepaymentdate.isAfter(periodStartDate)) {
        if (loanApplicationTerms.getInterestChargedFromLocalDate() != null) {
            if (periodStartDate.isEqual(loanApplicationTerms.getExpectedDisbursementDate())
                    || loanApplicationTerms.getInterestChargedFromLocalDate().isAfter(periodStartDate)) {
                periodStartDateApplicableForInterest = loanApplicationTerms.getInterestChargedFromLocalDate();
            }
        } else if (loanApplicationTerms.getInterestChargedFromLocalDate() == null
                && isInterestChargedFromDateSameAsDisbursalDateEnabled) {
            periodStartDateApplicableForInterest = expectedDisbursementDate;
        } else if (periodStartDate.isEqual(loanApplicationTerms.getExpectedDisbursementDate())) {
            periodStartDateApplicableForInterest = idealDisbursementDate;
        }
    }
    return periodStartDateApplicableForInterest;
}

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

License:Apache License

private BigDecimal calculatePeriodsInLoanTerm() {

    BigDecimal periodsInLoanTerm = BigDecimal.valueOf(this.loanTermFrequency);
    switch (this.interestCalculationPeriodMethod) {
    case DAILY://from  w ww. j  av a2s. co m
        // number of days from 'ideal disbursement' to final date

        LocalDate loanStartDate = getExpectedDisbursementDate();
        if (getInterestChargedFromDate() != null && loanStartDate.isBefore(getInterestChargedFromLocalDate())) {
            loanStartDate = getInterestChargedFromLocalDate();
        }

        final int periodsInLoanTermInteger = Days.daysBetween(loanStartDate, this.loanEndDate).getDays();
        periodsInLoanTerm = BigDecimal.valueOf(periodsInLoanTermInteger);
        break;
    case INVALID:
        break;
    case SAME_AS_REPAYMENT_PERIOD:
        if (this.allowPartialPeriodInterestCalcualtion) {
            LocalDate startDate = getExpectedDisbursementDate();
            if (getInterestChargedFromDate() != null) {
                startDate = getInterestChargedFromLocalDate();
            }
            periodsInLoanTerm = calculatePeriodsBetweenDates(startDate, this.loanEndDate);
        }
        break;
    }

    return periodsInLoanTerm;
}

From source file:com.gst.portfolio.loanaccount.loanschedule.service.LoanScheduleAssembler.java

License:Apache License

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

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

    // loan terms
    final Integer loanTermFrequency = this.fromApiJsonHelper.extractIntegerWithLocaleNamed("loanTermFrequency",
            element);//  ww w.jav a  2  s  .com
    final Integer loanTermFrequencyType = this.fromApiJsonHelper
            .extractIntegerWithLocaleNamed("loanTermFrequencyType", element);
    final PeriodFrequencyType loanTermPeriodFrequencyType = PeriodFrequencyType.fromInt(loanTermFrequencyType);

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

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

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

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

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

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

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

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

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

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

    final AccountType loanType = AccountType.fromName(loanTypeStr);

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

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

    /*
     * If it is JLG loan/Group Loan synched with a meeting, then make sure
     * first repayment falls on meeting date
     */
    final Long groupId = this.fromApiJsonHelper.extractLongNamed("groupId", element);
    Group group = null;
    if (groupId != null) {
        group = this.groupRepository.findOneWithNotFoundDetection(groupId);
    }

    Boolean isSkipMeetingOnFirstDay = false;
    Integer numberOfDays = 0;
    boolean isSkipRepaymentOnFirstMonthEnabled = configurationDomainService
            .isSkippingMeetingOnFirstDayOfMonthEnabled();
    if (isSkipRepaymentOnFirstMonthEnabled) {
        isSkipMeetingOnFirstDay = this.loanUtilService.isLoanRepaymentsSyncWithMeeting(group, calendar);
        if (isSkipMeetingOnFirstDay) {
            numberOfDays = configurationDomainService.retreivePeroidInNumberOfDaysForSkipMeetingDate()
                    .intValue();
        }
    }
    if ((loanType.isJLGAccount() || loanType.isGroupAccount()) && calendar != null) {
        validateRepaymentsStartDateWithMeetingDates(calculatedRepaymentsStartingFromDate, calendar,
                isSkipMeetingOnFirstDay, numberOfDays);

        /*
         * If disbursement is synced on meeting, make sure disbursement date
         * is on a meeting date
         */
        if (synchDisbursement != null && synchDisbursement.booleanValue()) {
            validateDisbursementDateWithMeetingDates(expectedDisbursementDate, calendar,
                    isSkipMeetingOnFirstDay, numberOfDays);
        }
    }

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

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

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

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

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

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

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

    final DaysInYearType daysInYearType = loanProduct.fetchDaysInYearType();

    final boolean isInterestRecalculationEnabled = loanProduct.isInterestRecalculationEnabled();
    RecalculationFrequencyType recalculationFrequencyType = null;
    CalendarInstance restCalendarInstance = null;
    RecalculationFrequencyType compoundingFrequencyType = null;
    CalendarInstance compoundingCalendarInstance = null;
    InterestRecalculationCompoundingMethod compoundingMethod = null;
    boolean allowCompoundingOnEod = false;
    if (isInterestRecalculationEnabled) {
        LoanProductInterestRecalculationDetails loanProductInterestRecalculationDetails = loanProduct
                .getProductInterestRecalculationDetails();
        recalculationFrequencyType = loanProductInterestRecalculationDetails.getRestFrequencyType();
        Integer repeatsOnDay = null;
        Integer recalculationFrequencyNthDay = loanProductInterestRecalculationDetails.getRestFrequencyOnDay();
        if (recalculationFrequencyNthDay == null) {
            recalculationFrequencyNthDay = loanProductInterestRecalculationDetails.getRestFrequencyNthDay();
            repeatsOnDay = loanProductInterestRecalculationDetails.getRestFrequencyWeekday();
        }
        Integer frequency = loanProductInterestRecalculationDetails.getRestInterval();
        if (recalculationFrequencyType.isSameAsRepayment()) {
            restCalendarInstance = createCalendarForSameAsRepayment(repaymentEvery,
                    repaymentPeriodFrequencyType, expectedDisbursementDate);
        } else {
            LocalDate calendarStartDate = expectedDisbursementDate;
            restCalendarInstance = createInterestRecalculationCalendarInstance(calendarStartDate,
                    recalculationFrequencyType, frequency, recalculationFrequencyNthDay, repeatsOnDay);
        }
        compoundingMethod = InterestRecalculationCompoundingMethod
                .fromInt(loanProductInterestRecalculationDetails.getInterestRecalculationCompoundingMethod());
        if (compoundingMethod.isCompoundingEnabled()) {
            Integer compoundingRepeatsOnDay = null;
            Integer recalculationCompoundingFrequencyNthDay = loanProductInterestRecalculationDetails
                    .getCompoundingFrequencyOnDay();
            if (recalculationCompoundingFrequencyNthDay == null) {
                recalculationCompoundingFrequencyNthDay = loanProductInterestRecalculationDetails
                        .getCompoundingFrequencyNthDay();
                compoundingRepeatsOnDay = loanProductInterestRecalculationDetails
                        .getCompoundingFrequencyWeekday();
            }
            compoundingFrequencyType = loanProductInterestRecalculationDetails.getCompoundingFrequencyType();
            if (compoundingFrequencyType.isSameAsRepayment()) {
                compoundingCalendarInstance = createCalendarForSameAsRepayment(repaymentEvery,
                        repaymentPeriodFrequencyType, expectedDisbursementDate);
            } else {
                LocalDate calendarStartDate = expectedDisbursementDate;
                compoundingCalendarInstance = createInterestRecalculationCalendarInstance(calendarStartDate,
                        compoundingFrequencyType,
                        loanProductInterestRecalculationDetails.getCompoundingInterval(),
                        recalculationCompoundingFrequencyNthDay, compoundingRepeatsOnDay);
            }
            allowCompoundingOnEod = loanProductInterestRecalculationDetails.allowCompoundingOnEod();
        }
    }

    final BigDecimal principalThresholdForLastInstalment = loanProduct
            .getPrincipalThresholdForLastInstallment();

    final Integer installmentAmountInMultiplesOf = loanProduct.getInstallmentAmountInMultiplesOf();

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

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

    final Long clientId = this.fromApiJsonHelper.extractLongNamed("clientId", element);
    Client client = null;
    Long officeId = null;
    if (clientId != null) {
        client = this.clientRepository.findOneWithNotFoundDetection(clientId);
        officeId = client.getOffice().getId();
    } else if (groupId != null) {
        group = this.groupRepository.findOneWithNotFoundDetection(groupId);
        officeId = group.getOffice().getId();
    }
    final boolean isHolidayEnabled = this.configurationDomainService.isRescheduleRepaymentsOnHolidaysEnabled();
    final List<Holiday> holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(officeId,
            expectedDisbursementDate.toDate(), HolidayStatusType.ACTIVE.getValue());
    final WorkingDays workingDays = this.workingDaysRepository.findOne();
    HolidayDetailDTO detailDTO = new HolidayDetailDTO(isHolidayEnabled, holidays, workingDays);
    return LoanApplicationTerms.assembleFrom(applicationCurrency, loanTermFrequency,
            loanTermPeriodFrequencyType, numberOfRepayments, repaymentEvery, repaymentPeriodFrequencyType,
            nthDay, weekDayType, amortizationMethod, interestMethod, interestRatePerPeriod,
            interestRatePeriodFrequencyType, annualNominalInterestRate, interestCalculationPeriodMethod,
            allowPartialPeriodInterestCalcualtion, principalMoney, expectedDisbursementDate,
            repaymentsStartingFromDate, calculatedRepaymentsStartingFromDate, graceOnPrincipalPayment,
            recurringMoratoriumOnPrincipalPeriods, graceOnInterestPayment, graceOnInterestCharged,
            interestChargedFromDate, inArrearsToleranceMoney, loanProduct.isMultiDisburseLoan(), emiAmount,
            disbursementDatas, maxOutstandingBalance, graceOnArrearsAgeing, daysInMonthType, daysInYearType,
            isInterestRecalculationEnabled, recalculationFrequencyType, restCalendarInstance, compoundingMethod,
            compoundingCalendarInstance, compoundingFrequencyType, principalThresholdForLastInstalment,
            installmentAmountInMultiplesOf, loanProduct.preCloseInterestCalculationStrategy(), calendar,
            BigDecimal.ZERO, loanTermVariations, isInterestChargedFromDateSameAsDisbursalDateEnabled,
            numberOfDays, isSkipMeetingOnFirstDay, detailDTO, allowCompoundingOnEod);
}

From source file:com.gst.portfolio.loanaccount.loanschedule.service.LoanScheduleAssembler.java

License:Apache License

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

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

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

    if (!variations.isEmpty()) {
        List<LoanTermVariations> retainVariations = adjustExistingVariations(variations, newVariations,
                adjustDueDateVariations);
        newVariations = retainVariations;
    }/*  www. ja v a2s .co m*/
    variations.addAll(newVariations);
    //Collections.sort(variations, new LoanTermVariationsComparator());

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

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

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

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

        default:
            break;

        }

    }
    if (!overlappings.isEmpty()) {
        baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode(
                "variable.schedule.modify.date.can.not.be.due.date", overlappings);
    }
    LoanProductVariableInstallmentConfig installmentConfig = loan.loanProduct()
            .loanProductVariableInstallmentConfig();
    final CalendarInstance loanCalendarInstance = calendarInstanceRepository
            .findCalendarInstaneByEntityId(loan.getId(), CalendarEntityType.LOANS.getValue());
    Calendar loanCalendar = null;
    if (loanCalendarInstance != null) {
        loanCalendar = loanCalendarInstance.getCalendar();
    }
    Boolean isSkipRepaymentOnFirstMonth = false;
    Integer numberOfDays = 0;
    boolean isSkipRepaymentOnFirstMonthEnabled = configurationDomainService
            .isSkippingMeetingOnFirstDayOfMonthEnabled();
    if (isSkipRepaymentOnFirstMonthEnabled) {
        isSkipRepaymentOnFirstMonth = this.loanUtilService.isLoanRepaymentsSyncWithMeeting(loan.group(),
                loanCalendar);
        if (isSkipRepaymentOnFirstMonth) {
            numberOfDays = configurationDomainService.retreivePeroidInNumberOfDaysForSkipMeetingDate()
                    .intValue();
        }
    }
    final Integer minGap = installmentConfig.getMinimumGap();
    final Integer maxGap = installmentConfig.getMaximumGap();

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

From source file:com.gst.portfolio.loanaccount.loanschedule.service.LoanScheduleAssembler.java

License:Apache License

private LocalDate deriveFirstRepaymentDateForLoans(final Integer repaymentEvery,
        final LocalDate expectedDisbursementDate, final LocalDate refernceDateForCalculatingFirstRepaymentDate,
        final PeriodFrequencyType repaymentPeriodFrequencyType,
        final Integer minimumDaysBetweenDisbursalAndFirstRepayment, final Calendar calendar) {
    boolean isMeetingSkipOnFirstDayOfMonth = configurationDomainService
            .isSkippingMeetingOnFirstDayOfMonthEnabled();
    int numberOfDays = configurationDomainService.retreivePeroidInNumberOfDaysForSkipMeetingDate().intValue();
    final String frequency = CalendarUtils
            .getMeetingFrequencyFromPeriodFrequencyType(repaymentPeriodFrequencyType);
    final LocalDate derivedFirstRepayment = CalendarUtils.getFirstRepaymentMeetingDate(calendar,
            refernceDateForCalculatingFirstRepaymentDate, repaymentEvery, frequency,
            isMeetingSkipOnFirstDayOfMonth, numberOfDays);
    final LocalDate minimumFirstRepaymentDate = expectedDisbursementDate
            .plusDays(minimumDaysBetweenDisbursalAndFirstRepayment);
    return minimumFirstRepaymentDate.isBefore(derivedFirstRepayment) ? derivedFirstRepayment
            : deriveFirstRepaymentDateForLoans(repaymentEvery, expectedDisbursementDate, derivedFirstRepayment,
                    repaymentPeriodFrequencyType, minimumDaysBetweenDisbursalAndFirstRepayment, calendar);
}