Example usage for org.joda.time LocalDate isAfter

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

Introduction

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

Prototype

public boolean isAfter(ReadablePartial partial) 

Source Link

Document

Is this partial later than the specified partial.

Usage

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

License:Apache License

private ChangedTransactionDetail handleRefundTransaction(final LoanTransaction loanTransaction,
        final LoanLifecycleStateMachine loanLifecycleStateMachine, final LoanTransaction adjustedTransaction) {

    ChangedTransactionDetail changedTransactionDetail = null;

    final LoanStatus statusEnum = loanLifecycleStateMachine.transition(LoanEvent.LOAN_REFUND,
            LoanStatus.fromInt(this.loanStatus));
    this.loanStatus = statusEnum.getValue();

    loanTransaction.updateLoan(this);

    // final boolean isTransactionChronologicallyLatest =
    // isChronologicallyLatestRefund(loanTransaction,
    // this.loanTransactions);

    if (status().isOverpaid() || status().isClosed()) {

        final String errorMessage = "This refund option is only for active loans ";
        throw new InvalidLoanStateTransitionException("transaction", "is.exceeding.overpaid.amount",
                errorMessage, this.totalOverpaid, loanTransaction.getAmount(getCurrency()).getAmount());

    } else if (this.getTotalPaidInRepayments().isZero()) {
        final String errorMessage = "Cannot refund when no payment has been made";
        throw new InvalidLoanStateTransitionException("transaction", "no.payment.yet.made.for.loan",
                errorMessage);/*from w  ww .j a  v a 2 s.com*/
    }

    if (loanTransaction.isNotZero(loanCurrency())) {
        this.loanTransactions.add(loanTransaction);
    }

    if (loanTransaction.isNotRefundForActiveLoan()) {
        final String errorMessage = "A transaction of type refund was expected but not received.";
        throw new InvalidLoanTransactionTypeException("transaction", "is.not.a.refund.transaction",
                errorMessage);
    }

    final LocalDate loanTransactionDate = loanTransaction.getTransactionDate();
    if (loanTransactionDate.isBefore(getDisbursementDate())) {
        final String errorMessage = "The transaction date cannot be before the loan disbursement date: "
                + getApprovedOnDate().toString();
        throw new InvalidLoanStateTransitionException("transaction", "cannot.be.before.disbursement.date",
                errorMessage, loanTransactionDate, getDisbursementDate());
    }

    if (loanTransactionDate.isAfter(DateUtils.getLocalDateOfTenant())) {
        final String errorMessage = "The transaction date cannot be in the future.";
        throw new InvalidLoanStateTransitionException("transaction", "cannot.be.a.future.date", errorMessage,
                loanTransactionDate);
    }

    if (this.loanProduct.isMultiDisburseLoan() && adjustedTransaction == null) {
        BigDecimal totalDisbursed = getDisbursedAmount();
        if (totalDisbursed.compareTo(this.summary.getTotalPrincipalRepaid()) < 0) {
            final String errorMessage = "The transaction cannot be done before the loan disbursement: "
                    + getApprovedOnDate().toString();
            throw new InvalidLoanStateTransitionException("transaction", "cannot.be.done.before.disbursement",
                    errorMessage);
        }
    }

    final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory
            .determineProcessor(this.transactionProcessingStrategy);

    // If is a refund
    if (adjustedTransaction == null) {
        loanRepaymentScheduleTransactionProcessor.handleRefund(loanTransaction, getCurrency(),
                this.repaymentScheduleInstallments, charges());
    } else {
        final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retreiveListOfTransactionsPostDisbursement();
        changedTransactionDetail = loanRepaymentScheduleTransactionProcessor.handleTransaction(
                getDisbursementDate(), allNonContraTransactionsPostDisbursement, getCurrency(),
                this.repaymentScheduleInstallments, charges());
        for (final Map.Entry<Long, LoanTransaction> mapEntry : changedTransactionDetail
                .getNewTransactionMappings().entrySet()) {
            mapEntry.getValue().updateLoan(this);
        }

    }

    updateLoanSummaryDerivedFields();

    doPostLoanTransactionChecks(loanTransaction.getTransactionDate(), loanLifecycleStateMachine);

    return changedTransactionDetail;
}

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

License:Apache License

@Override
public void recalculateAccruals(Loan loan) {
    LocalDate accruedTill = loan.getAccruedTill();
    if (!loan.isPeriodicAccrualAccountingEnabledOnLoanProduct()
            || !loan.repaymentScheduleDetail().isInterestRecalculationEnabled() || accruedTill == null
            || loan.isNpa() || !loan.status().isActive()) {
        return;/*  w w  w  .j  ava 2s  . co  m*/
    }
    Collection<LoanScheduleAccrualData> loanScheduleAccrualDatas = new ArrayList<>();
    List<LoanRepaymentScheduleInstallment> installments = loan.fetchRepaymentScheduleInstallments();
    Long loanId = loan.getId();
    Long officeId = loan.getOfficeId();
    LocalDate accrualStartDate = null;
    PeriodFrequencyType repaymentFrequency = loan.repaymentScheduleDetail().getRepaymentPeriodFrequencyType();
    Integer repayEvery = loan.repaymentScheduleDetail().getRepayEvery();
    LocalDate interestCalculatedFrom = loan.getInterestChargedFromDate();
    Long loanProductId = loan.productId();
    MonetaryCurrency currency = loan.getCurrency();
    ApplicationCurrency applicationCurrency = this.applicationCurrencyRepository
            .findOneWithNotFoundDetection(currency);
    CurrencyData currencyData = applicationCurrency.toData();
    Set<LoanCharge> loanCharges = loan.charges();

    for (LoanRepaymentScheduleInstallment installment : installments) {
        if (!accruedTill.isBefore(installment.getDueDate()) || (accruedTill.isAfter(installment.getFromDate())
                && !accruedTill.isAfter(installment.getDueDate()))) {
            BigDecimal dueDateFeeIncome = BigDecimal.ZERO;
            BigDecimal dueDatePenaltyIncome = BigDecimal.ZERO;
            LocalDate chargesTillDate = installment.getDueDate();
            if (!accruedTill.isAfter(installment.getDueDate())) {
                chargesTillDate = accruedTill;
            }

            for (final LoanCharge loanCharge : loanCharges) {
                if (loanCharge.isDueForCollectionFromAndUpToAndIncluding(installment.getFromDate(),
                        chargesTillDate)) {
                    if (loanCharge.isFeeCharge()) {
                        dueDateFeeIncome = dueDateFeeIncome.add(loanCharge.amount());
                    } else if (loanCharge.isPenaltyCharge()) {
                        dueDatePenaltyIncome = dueDatePenaltyIncome.add(loanCharge.amount());
                    }
                }
            }
            LoanScheduleAccrualData accrualData = new LoanScheduleAccrualData(loanId, officeId,
                    installment.getInstallmentNumber(), accrualStartDate, repaymentFrequency, repayEvery,
                    installment.getDueDate(), installment.getFromDate(), installment.getId(), loanProductId,
                    installment.getInterestCharged(currency).getAmount(),
                    installment.getFeeChargesCharged(currency).getAmount(),
                    installment.getPenaltyChargesCharged(currency).getAmount(),
                    installment.getInterestAccrued(currency).getAmount(),
                    installment.getFeeAccrued(currency).getAmount(),
                    installment.getPenaltyAccrued(currency).getAmount(), currencyData, interestCalculatedFrom,
                    installment.getInterestWaived(currency).getAmount());
            loanScheduleAccrualDatas.add(accrualData);

        }
    }

    if (!loanScheduleAccrualDatas.isEmpty()) {
        String error = this.loanAccrualPlatformService.addPeriodicAccruals(accruedTill,
                loanScheduleAccrualDatas);
        if (error.length() > 0) {
            String globalisationMessageCode = "error.msg.accrual.exception";
            throw new GeneralPlatformDomainRuleException(globalisationMessageCode, error, error);
        }
    }
}

From source file:org.apache.fineract.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  w w  w.ja v a  2  s .co 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();

    final Collection<LoanScheduleModelPeriod> periods = createNewLoanScheduleListWithDisbursementDetails(
            numberOfRepayments, loanApplicationTerms, chargesDueAtTimeOfDisbursement);

    // Determine the total interest owed over the full loan for FLAT
    // interest method .
    final Money totalInterestChargedForFullLoanTerm = loanApplicationTerms
            .calculateTotalInterestCharged(this.paymentPeriodsInOneYearCalculator, mc);

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

    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();

    // 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);

        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);

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

        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 (!scheduleParams.getLatePaymentMap().isEmpty()) {
            populateCompoundingDatesInPeriod(scheduleParams.getPeriodStartDate(), scheduledDueDate, currentDate,
                    loanApplicationTerms, holidayDetailDTO, scheduleParams.getCompoundingMap(), loanCharges,
                    currency);
            scheduleParams.getCompoundingDateVariations().put(scheduleParams.getPeriodStartDate(),
                    new TreeMap<>(scheduleParams.getCompoundingMap()));
        }

        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;
        }

        // 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, totalInterestChargedForFullLoanTerm, idealDisbursementDate,
                firstRepaymentdate, lastRestDate, scheduledDueDate, periodStartDateApplicableForInterest,
                applicableTransactions, currentPeriodParams);

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

        // 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(), totalInterestChargedForFullLoanTerm,
                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);

        // apply loan transactions on installments to identify early/late
        // payments for interest recalculation
        installment = handleRecalculationForTransactions(mc, loanApplicationTerms, holidayDetailDTO, currency,
                scheduleParams, loanRepaymentScheduleTransactionProcessor, totalInterestChargedForFullLoanTerm,
                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);

        // handle cumulative fields

        scheduleParams.addTotalCumulativePrincipal(currentPeriodParams.getPrincipalForThisPeriod());
        scheduleParams.addTotalRepaymentExpected(totalInstallmentDue);
        scheduleParams.addTotalCumulativeInterest(currentPeriodParams.getInterestForThisPeriod());
        scheduleParams.setPeriodStartDate(scheduledDueDate);
        scheduleParams.incrementInstalmentNumber();
        scheduleParams.incrementPeriodNumber();
        scheduleParams.getCompoundingDateVariations().clear();
        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;

    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:org.apache.fineract.portfolio.loanaccount.loanschedule.domain.AbstractLoanScheduleGenerator.java

License:Apache License

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

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

License:Apache License

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

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

License:Apache License

private LocalDate deriveFirstRepaymentDate(final AccountType loanType, final Integer repaymentEvery,
        final LocalDate expectedDisbursementDate, final PeriodFrequencyType repaymentPeriodFrequencyType,
        final Integer minimumDaysBetweenDisbursalAndFirstRepayment, final Calendar calendar) {

    LocalDate derivedFirstRepayment = null;

    final LocalDate dateBasedOnMinimumDaysBetweenDisbursalAndFirstRepayment = expectedDisbursementDate
            .plusDays(minimumDaysBetweenDisbursalAndFirstRepayment);

    if ((loanType.isJLGAccount() || loanType.isGroupAccount()) && calendar != null) {

        final LocalDate refernceDateForCalculatingFirstRepaymentDate = expectedDisbursementDate;
        derivedFirstRepayment = deriveFirstRepaymentDateForJLGLoans(repaymentEvery, expectedDisbursementDate,
                refernceDateForCalculatingFirstRepaymentDate, repaymentPeriodFrequencyType,
                minimumDaysBetweenDisbursalAndFirstRepayment, calendar);

    } /*** Individual or group account, or JLG not linked to a meeting ***/
    else {//  w  w w  .  j a va  2  s .c om
        LocalDate dateBasedOnRepaymentFrequency;
        // Derive the first repayment date as greater date among
        // (disbursement date + plus frequency) or
        // (disbursement date + minimum between disbursal and first
        // repayment )
        if (repaymentPeriodFrequencyType.isDaily()) {
            dateBasedOnRepaymentFrequency = expectedDisbursementDate.plusDays(repaymentEvery);
        } else if (repaymentPeriodFrequencyType.isWeekly()) {
            dateBasedOnRepaymentFrequency = expectedDisbursementDate.plusWeeks(repaymentEvery);
        } else if (repaymentPeriodFrequencyType.isMonthly()) {
            dateBasedOnRepaymentFrequency = expectedDisbursementDate.plusMonths(repaymentEvery);
        } /** yearly loan **/
        else {
            dateBasedOnRepaymentFrequency = expectedDisbursementDate.plusYears(repaymentEvery);
        }
        derivedFirstRepayment = dateBasedOnRepaymentFrequency.isAfter(
                dateBasedOnMinimumDaysBetweenDisbursalAndFirstRepayment) ? dateBasedOnRepaymentFrequency
                        : dateBasedOnMinimumDaysBetweenDisbursalAndFirstRepayment;
    }

    return derivedFirstRepayment;
}

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

License:Apache License

@Transactional
@Override//from  w w w  .j  ava 2  s.  c  o m
public CommandProcessingResult addLoanCharge(final Long loanId, final JsonCommand command) {

    this.loanEventApiJsonValidator.validateAddLoanCharge(command.json());

    final Loan loan = this.loanAssembler.assembleFrom(loanId);
    checkClientOrGroupActive(loan);

    Set<LoanDisbursementDetails> loanDisburseDetails = loan.getDisbursementDetails();
    final Long chargeDefinitionId = command.longValueOfParameterNamed("chargeId");
    final Charge chargeDefinition = this.chargeRepository.findOneWithNotFoundDetection(chargeDefinitionId);

    if (loan.isDisbursed() && chargeDefinition.isDisbursementCharge()) {
        validateAddingNewChargeAllowed(loanDisburseDetails); // validates
                                                             // whether any
                                                             // pending
                                                             // disbursements
                                                             // are
                                                             // available to
                                                             // apply this
                                                             // charge
    }
    final List<Long> existingTransactionIds = new ArrayList<>(loan.findExistingTransactionIds());
    final List<Long> existingReversedTransactionIds = new ArrayList<>(
            loan.findExistingReversedTransactionIds());

    boolean isAppliedOnBackDate = false;
    LoanCharge loanCharge = null;
    LocalDate recalculateFrom = loan.fetchInterestRecalculateFromDate();
    if (chargeDefinition.isPercentageOfDisbursementAmount()) {
        LoanTrancheDisbursementCharge loanTrancheDisbursementCharge = null;
        for (LoanDisbursementDetails disbursementDetail : loanDisburseDetails) {
            if (disbursementDetail.actualDisbursementDate() == null) {
                loanCharge = LoanCharge.createNewWithoutLoan(chargeDefinition, disbursementDetail.principal(),
                        null, null, null, disbursementDetail.expectedDisbursementDateAsLocalDate(), null, null);
                loanTrancheDisbursementCharge = new LoanTrancheDisbursementCharge(loanCharge,
                        disbursementDetail);
                loanCharge.updateLoanTrancheDisbursementCharge(loanTrancheDisbursementCharge);
                this.businessEventNotifierService.notifyBusinessEventToBeExecuted(
                        BUSINESS_EVENTS.LOAN_ADD_CHARGE,
                        constructEntityMap(BUSINESS_ENTITY.LOAN_CHARGE, loanCharge));
                validateAddLoanCharge(loan, chargeDefinition, loanCharge);
                addCharge(loan, chargeDefinition, loanCharge);
                isAppliedOnBackDate = true;
                if (recalculateFrom.isAfter(disbursementDetail.expectedDisbursementDateAsLocalDate())) {
                    recalculateFrom = disbursementDetail.expectedDisbursementDateAsLocalDate();
                }
            }
        }
        loan.addTrancheLoanCharge(chargeDefinition);
    } else {
        loanCharge = LoanCharge.createNewFromJson(loan, chargeDefinition, command);
        this.businessEventNotifierService.notifyBusinessEventToBeExecuted(BUSINESS_EVENTS.LOAN_ADD_CHARGE,
                constructEntityMap(BUSINESS_ENTITY.LOAN_CHARGE, loanCharge));

        validateAddLoanCharge(loan, chargeDefinition, loanCharge);
        isAppliedOnBackDate = addCharge(loan, chargeDefinition, loanCharge);
        if (loanCharge.getDueLocalDate() == null || recalculateFrom.isAfter(loanCharge.getDueLocalDate())) {
            isAppliedOnBackDate = true;
            recalculateFrom = loanCharge.getDueLocalDate();
        }
    }

    boolean reprocessRequired = true;
    if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
        if (isAppliedOnBackDate && loan.isFeeCompoundingEnabledForInterestRecalculation()) {

            runScheduleRecalculation(loan, recalculateFrom);
            reprocessRequired = false;
        }
        updateOriginalSchedule(loan);
    }
    if (reprocessRequired) {
        ChangedTransactionDetail changedTransactionDetail = loan.reprocessTransactions();
        if (changedTransactionDetail != null) {
            for (final Map.Entry<Long, LoanTransaction> mapEntry : changedTransactionDetail
                    .getNewTransactionMappings().entrySet()) {
                this.loanTransactionRepository.save(mapEntry.getValue());
                // update loan with references to the newly created
                // transactions
                loan.getLoanTransactions().add(mapEntry.getValue());
                this.accountTransfersWritePlatformService.updateLoanTransaction(mapEntry.getKey(),
                        mapEntry.getValue());
            }
        }
        saveLoanWithDataIntegrityViolationChecks(loan);
    }

    postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);

    if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled() && isAppliedOnBackDate
            && loan.isFeeCompoundingEnabledForInterestRecalculation()) {
        this.loanAccountDomainService.recalculateAccruals(loan);
    }
    this.businessEventNotifierService.notifyBusinessEventWasExecuted(BUSINESS_EVENTS.LOAN_ADD_CHARGE,
            constructEntityMap(BUSINESS_ENTITY.LOAN_CHARGE, loanCharge));
    return new CommandProcessingResultBuilder() //
            .withCommandId(command.commandId()) //
            .withEntityId(loanCharge.getId()) //
            .withOfficeId(loan.getOfficeId()) //
            .withClientId(loan.getClientId()) //
            .withGroupId(loan.getGroupId()) //
            .withLoanId(loanId) //
            .build();
}

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

License:Apache License

@Override
@Transactional//from  w  w w  .  j a v  a  2s. c o  m
public void applyOverdueChargesForLoan(final Long loanId,
        Collection<OverdueLoanScheduleData> overdueLoanScheduleDatas) {

    Loan loan = null;
    final List<Long> existingTransactionIds = new ArrayList<>();
    final List<Long> existingReversedTransactionIds = new ArrayList<>();
    boolean runInterestRecalculation = false;
    LocalDate recalculateFrom = DateUtils.getLocalDateOfTenant();
    LocalDate lastChargeDate = null;
    for (final OverdueLoanScheduleData overdueInstallment : overdueLoanScheduleDatas) {

        final JsonElement parsedCommand = this.fromApiJsonHelper.parse(overdueInstallment.toString());
        final JsonCommand command = JsonCommand.from(overdueInstallment.toString(), parsedCommand,
                this.fromApiJsonHelper, null, null, null, null, null, loanId, null, null, null, null);
        LoanOverdueDTO overdueDTO = applyChargeToOverdueLoanInstallment(loanId,
                overdueInstallment.getChargeId(), overdueInstallment.getPeriodNumber(), command, loan,
                existingTransactionIds, existingReversedTransactionIds);
        loan = overdueDTO.getLoan();
        runInterestRecalculation = runInterestRecalculation || overdueDTO.isRunInterestRecalculation();
        if (recalculateFrom.isAfter(overdueDTO.getRecalculateFrom())) {
            recalculateFrom = overdueDTO.getRecalculateFrom();
        }
        if (lastChargeDate == null || overdueDTO.getLastChargeAppliedDate().isAfter(lastChargeDate)) {
            lastChargeDate = overdueDTO.getLastChargeAppliedDate();
        }
    }
    if (loan != null) {
        boolean reprocessRequired = true;
        LocalDate recalculatedTill = loan.fetchInterestRecalculateFromDate();
        if (recalculateFrom.isAfter(recalculatedTill)) {
            recalculateFrom = recalculatedTill;
        }

        if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
            if (runInterestRecalculation && loan.isFeeCompoundingEnabledForInterestRecalculation()) {
                runScheduleRecalculation(loan, recalculateFrom);
                reprocessRequired = false;
            }
            updateOriginalSchedule(loan);
        }

        if (reprocessRequired) {
            addInstallmentIfPenaltyAppliedAfterLastDueDate(loan, lastChargeDate);
            ChangedTransactionDetail changedTransactionDetail = loan.reprocessTransactions();
            if (changedTransactionDetail != null) {
                for (final Map.Entry<Long, LoanTransaction> mapEntry : changedTransactionDetail
                        .getNewTransactionMappings().entrySet()) {
                    this.loanTransactionRepository.save(mapEntry.getValue());
                    // update loan with references to the newly created
                    // transactions
                    loan.getLoanTransactions().add(mapEntry.getValue());
                    this.accountTransfersWritePlatformService.updateLoanTransaction(mapEntry.getKey(),
                            mapEntry.getValue());
                }
            }
            saveLoanWithDataIntegrityViolationChecks(loan);
        }

        postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);

        if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled() && runInterestRecalculation
                && loan.isFeeCompoundingEnabledForInterestRecalculation()) {
            this.loanAccountDomainService.recalculateAccruals(loan);
        }
        this.businessEventNotifierService.notifyBusinessEventWasExecuted(
                BUSINESS_EVENTS.LOAN_APPLY_OVERDUE_CHARGE, constructEntityMap(BUSINESS_ENTITY.LOAN, loan));

    }
}

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

License:Apache License

private void addInstallmentIfPenaltyAppliedAfterLastDueDate(Loan loan, LocalDate lastChargeDate) {
    if (lastChargeDate != null) {
        List<LoanRepaymentScheduleInstallment> installments = loan.fetchRepaymentScheduleInstallments();
        LoanRepaymentScheduleInstallment lastInstallment = loan
                .fetchRepaymentScheduleInstallment(installments.size());
        if (lastChargeDate.isAfter(lastInstallment.getDueDate())) {
            if (lastInstallment.isRecalculatedInterestComponent()) {
                installments.remove(lastInstallment);
                lastInstallment = loan.fetchRepaymentScheduleInstallment(installments.size());
            }//  w  w  w .  j  a  v  a 2  s. c om
            boolean recalculatedInterestComponent = true;
            BigDecimal principal = BigDecimal.ZERO;
            BigDecimal interest = BigDecimal.ZERO;
            BigDecimal feeCharges = BigDecimal.ZERO;
            BigDecimal penaltyCharges = BigDecimal.ONE;
            LoanRepaymentScheduleInstallment newEntry = new LoanRepaymentScheduleInstallment(loan,
                    installments.size() + 1, lastInstallment.getDueDate(), lastChargeDate, principal, interest,
                    feeCharges, penaltyCharges, recalculatedInterestComponent);
            installments.add(newEntry);
        }
    }
}

From source file:org.apache.fineract.portfolio.savings.domain.FixedDepositAccount.java

License:Apache License

public void postMaturityInterest(final boolean isSavingsInterestPostingAtCurrentPeriodEnd,
        final Integer financialYearBeginningMonth) {
    final LocalDate interestPostingUpToDate = maturityDate();
    final MathContext mc = MathContext.DECIMAL64;
    final boolean isInterestTransfer = false;
    final List<PostingPeriod> postingPeriods = calculateInterestUsing(mc, interestPostingUpToDate,
            isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth);

    Money interestPostedToDate = Money.zero(this.currency);

    boolean recalucateDailyBalanceDetails = false;

    for (final PostingPeriod interestPostingPeriod : postingPeriods) {

        LocalDate interestPostingTransactionDate = interestPostingPeriod.dateOfPostingTransaction();

        interestPostingTransactionDate = interestPostingTransactionDate.isAfter(interestPostingUpToDate)
                ? interestPostingUpToDate
                : interestPostingTransactionDate;

        final Money interestEarnedToBePostedForPeriod = interestPostingPeriod.getInterestEarned();

        interestPostedToDate = interestPostedToDate.plus(interestEarnedToBePostedForPeriod);

        final SavingsAccountTransaction postingTransaction = findInterestPostingTransactionFor(
                interestPostingTransactionDate);
        if (postingTransaction == null) {
            final SavingsAccountTransaction newPostingTransaction = SavingsAccountTransaction.interestPosting(
                    this, office(), interestPostingTransactionDate, interestEarnedToBePostedForPeriod);
            this.transactions.add(newPostingTransaction);
            recalucateDailyBalanceDetails = true;
        } else {//from  w  w  w .  ja v  a 2  s. co m
            final boolean correctionRequired = postingTransaction
                    .hasNotAmount(interestEarnedToBePostedForPeriod);
            if (correctionRequired) {
                postingTransaction.reverse();
                final SavingsAccountTransaction newPostingTransaction = SavingsAccountTransaction
                        .interestPosting(this, office(), interestPostingTransactionDate,
                                interestEarnedToBePostedForPeriod);
                this.transactions.add(newPostingTransaction);
                recalucateDailyBalanceDetails = true;
            }
        }
    }

    if (recalucateDailyBalanceDetails) {
        // update existing transactions so derived balance fields are
        // correct.
        recalculateDailyBalances(Money.zero(this.currency), interestPostingUpToDate);
    }

    this.summary.updateSummary(this.currency, this.savingsAccountTransactionSummaryWrapper, this.transactions);
}