List of usage examples for org.joda.time Days daysBetween
public static Days daysBetween(ReadablePartial start, ReadablePartial end)
Days
representing the number of whole days between the two specified partial datetimes. From source file:org.apache.fineract.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<>(); LoanScheduleModelPeriod installment = null; LocalDate periodStartDateApplicableForInterest = periodStartDateForInterest; for (RecalculationDetail detail : applicableTransactions) { if (detail.isProcessed()) { continue; }/*w w w . j av a 2 s . c om*/ boolean updateLatePaymentMap = false; final LocalDate transactionDate = detail.getTransactionDate(); if (transactionDate.isBefore(scheduledDueDate)) { if (scheduleParams.getLoanRepaymentScheduleTransactionProcessor() != null && scheduleParams.getLoanRepaymentScheduleTransactionProcessor() .isInterestFirstRepaymentScheduleTransactionProcessor()) { List<LoanTransaction> currentTransactions = 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); int daysInPeriodApplicable = Days .daysBetween(periodStartDateApplicableForInterest, transactionDate).getDays(); Money interestForThisinstallment = Money.zero(currency); if (daysInPeriodApplicable > 0) { // 5 determine interest till the transaction // date if (!scheduleParams.getCompoundingDateVariations() .containsKey(periodStartDateApplicableForInterest)) { scheduleParams.getCompoundingDateVariations().put( periodStartDateApplicableForInterest, new TreeMap<>(scheduleParams.getCompoundingMap())); } 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); // 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(); // creates and insert Loan repayment schedule // for // the period addLoanRepaymentScheduleInstallment(scheduleParams.getInstallments(), installment); } 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, periodStartDateApplicableForInterest, detail); if (updateLatePaymentMap) { updateLatePaymentsToMap(loanApplicationTerms, holidayDetailDTO, currency, scheduleParams.getLatePaymentMap(), scheduledDueDate, scheduleParams.getInstallments(), true, lastRestDate, scheduleParams.getCompoundingMap()); } } 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 = fetchCompoundedArrears(loanApplicationTerms, currency, detail.getTransaction()); if (unprocessed.isGreaterThanZero()) { arrears = getTotalAmount(scheduleParams.getLatePaymentMap(), currency); 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()) { LocalDate calculateTill = transactionDate; if (!scheduleParams.getCompoundingDateVariations() .containsKey(periodStartDateApplicableForInterest)) { scheduleParams.getCompoundingDateVariations().put( periodStartDateApplicableForInterest, new TreeMap<>(scheduleParams.getCompoundingMap())); } 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, periodStartDateApplicableForInterest, detail); } } } } } applicableTransactions.addAll(unprocessedTransactions); if (checkForOutstanding && scheduleParams.getOutstandingBalance().isZero() && scheduleParams.getDisburseDetailMap().isEmpty()) { currentPeriodParams.setSkipCurrentLoop(true); } } }
From source file:org.apache.fineract.portfolio.loanaccount.loanschedule.domain.AbstractLoanScheduleGenerator.java
License:Apache License
@Override public LoanRescheduleModel reschedule(final MathContext mathContext, final LoanRescheduleRequest loanRescheduleRequest, final ApplicationCurrency applicationCurrency, final HolidayDetailDTO holidayDetailDTO, final CalendarInstance restCalendarInstance, final CalendarInstance compoundingCalendarInstance, final Calendar loanCalendar, final FloatingRateDTO floatingRateDTO) { final Loan loan = loanRescheduleRequest.getLoan(); final LoanSummary loanSummary = loan.getSummary(); final LoanProductMinimumRepaymentScheduleRelatedDetail loanProductRelatedDetail = loan .getLoanRepaymentScheduleDetail(); final MonetaryCurrency currency = loanProductRelatedDetail.getCurrency(); // create an archive of the current loan schedule installments Collection<LoanRepaymentScheduleHistory> loanRepaymentScheduleHistoryList = null; // get the initial list of repayment installments List<LoanRepaymentScheduleInstallment> repaymentScheduleInstallments = loan .getRepaymentScheduleInstallments(); // sort list by installment number in ASC order Collections.sort(repaymentScheduleInstallments, LoanRepaymentScheduleInstallment.installmentNumberComparator); final Collection<LoanRescheduleModelRepaymentPeriod> periods = new ArrayList<>(); Money outstandingLoanBalance = loan.getPrincpal(); for (LoanRepaymentScheduleInstallment repaymentScheduleInstallment : repaymentScheduleInstallments) { Integer oldPeriodNumber = repaymentScheduleInstallment.getInstallmentNumber(); LocalDate fromDate = repaymentScheduleInstallment.getFromDate(); LocalDate dueDate = repaymentScheduleInstallment.getDueDate(); Money principalDue = repaymentScheduleInstallment.getPrincipal(currency); Money interestDue = repaymentScheduleInstallment.getInterestCharged(currency); Money feeChargesDue = repaymentScheduleInstallment.getFeeChargesCharged(currency); Money penaltyChargesDue = repaymentScheduleInstallment.getPenaltyChargesCharged(currency); Money totalDue = principalDue.plus(interestDue).plus(feeChargesDue).plus(penaltyChargesDue); outstandingLoanBalance = outstandingLoanBalance.minus(principalDue); LoanRescheduleModelRepaymentPeriod period = LoanRescheduleModelRepaymentPeriod.instance(oldPeriodNumber, oldPeriodNumber, fromDate, dueDate, principalDue, outstandingLoanBalance, interestDue, feeChargesDue, penaltyChargesDue, totalDue, false); periods.add(period);/* ww w.j ava2 s. co m*/ } Money outstandingBalance = loan.getPrincpal(); Money totalCumulativePrincipal = Money.zero(currency); Money totalCumulativeInterest = Money.zero(currency); Money actualTotalCumulativeInterest = Money.zero(currency); Money totalOutstandingInterestPaymentDueToGrace = Money.zero(currency); Money totalPrincipalBeforeReschedulePeriod = Money.zero(currency); LocalDate installmentDueDate = null; LocalDate adjustedInstallmentDueDate = null; LocalDate installmentFromDate = null; Integer rescheduleFromInstallmentNo = defaultToZeroIfNull( loanRescheduleRequest.getRescheduleFromInstallment()); Integer installmentNumber = rescheduleFromInstallmentNo; Integer graceOnPrincipal = defaultToZeroIfNull(loanRescheduleRequest.getGraceOnPrincipal()); Integer graceOnInterest = defaultToZeroIfNull(loanRescheduleRequest.getGraceOnInterest()); Integer extraTerms = defaultToZeroIfNull(loanRescheduleRequest.getExtraTerms()); final boolean recalculateInterest = loanRescheduleRequest.getRecalculateInterest(); Integer numberOfRepayments = repaymentScheduleInstallments.size(); Integer rescheduleNumberOfRepayments = numberOfRepayments; final Money principal = loan.getPrincpal(); final Money totalPrincipalOutstanding = Money.of(currency, loanSummary.getTotalPrincipalOutstanding()); LocalDate adjustedDueDate = loanRescheduleRequest.getAdjustedDueDate(); BigDecimal newInterestRate = loanRescheduleRequest.getInterestRate(); int loanTermInDays = Integer.valueOf(0); if (rescheduleFromInstallmentNo > 0) { // this will hold the loan repayment installment that is before the // reschedule start installment // (rescheduleFrominstallment) LoanRepaymentScheduleInstallment previousInstallment = null; // get the install number of the previous installment int previousInstallmentNo = rescheduleFromInstallmentNo - 1; // only fetch the installment if the number is greater than 0 if (previousInstallmentNo > 0) { previousInstallment = loan.fetchRepaymentScheduleInstallment(previousInstallmentNo); } LoanRepaymentScheduleInstallment firstInstallment = loan.fetchRepaymentScheduleInstallment(1); // the "installment from date" is equal to the due date of the // previous installment, if it exists if (previousInstallment != null) { installmentFromDate = previousInstallment.getDueDate(); } else { installmentFromDate = firstInstallment.getFromDate(); } installmentDueDate = installmentFromDate; LocalDate periodStartDateApplicableForInterest = installmentFromDate; Integer periodNumber = 1; outstandingLoanBalance = loan.getPrincpal(); for (LoanRescheduleModelRepaymentPeriod period : periods) { if (period.periodDueDate().isBefore(loanRescheduleRequest.getRescheduleFromDate())) { totalPrincipalBeforeReschedulePeriod = totalPrincipalBeforeReschedulePeriod .plus(period.principalDue()); actualTotalCumulativeInterest = actualTotalCumulativeInterest.plus(period.interestDue()); rescheduleNumberOfRepayments--; outstandingLoanBalance = outstandingLoanBalance.minus(period.principalDue()); outstandingBalance = outstandingBalance.minus(period.principalDue()); } } while (graceOnPrincipal > 0 || graceOnInterest > 0) { LoanRescheduleModelRepaymentPeriod period = LoanRescheduleModelRepaymentPeriod.instance(0, 0, new LocalDate(), new LocalDate(), Money.zero(currency), Money.zero(currency), Money.zero(currency), Money.zero(currency), Money.zero(currency), Money.zero(currency), true); periods.add(period); if (graceOnPrincipal > 0) { graceOnPrincipal--; } if (graceOnInterest > 0) { graceOnInterest--; } rescheduleNumberOfRepayments++; numberOfRepayments++; } while (extraTerms > 0) { LoanRescheduleModelRepaymentPeriod period = LoanRescheduleModelRepaymentPeriod.instance(0, 0, new LocalDate(), new LocalDate(), Money.zero(currency), Money.zero(currency), Money.zero(currency), Money.zero(currency), Money.zero(currency), Money.zero(currency), true); periods.add(period); extraTerms--; rescheduleNumberOfRepayments++; numberOfRepayments++; } // get the loan application terms from the Loan object final LoanApplicationTerms loanApplicationTerms = loan.getLoanApplicationTerms(applicationCurrency, restCalendarInstance, compoundingCalendarInstance, loanCalendar, floatingRateDTO); // for applying variations Collection<LoanTermVariationsData> loanTermVariations = loanApplicationTerms.getLoanTermVariations() .getInterestRateChanges(); // update the number of repayments loanApplicationTerms.updateNumberOfRepayments(numberOfRepayments); LocalDate loanEndDate = this.scheduledDateGenerator.getLastRepaymentDate(loanApplicationTerms, holidayDetailDTO); LoanTermVariationsData lastDueDateVariation = loanApplicationTerms.getLoanTermVariations() .fetchLoanTermDueDateVariationsData(loanEndDate); if (lastDueDateVariation != null) { loanEndDate = lastDueDateVariation.getDateValue(); } loanApplicationTerms.updateLoanEndDate(loanEndDate); if (newInterestRate != null) { loanApplicationTerms.updateAnnualNominalInterestRate(newInterestRate); loanApplicationTerms.updateInterestRatePerPeriod(newInterestRate); } graceOnPrincipal = defaultToZeroIfNull(loanRescheduleRequest.getGraceOnPrincipal()); graceOnInterest = defaultToZeroIfNull(loanRescheduleRequest.getGraceOnInterest()); loanApplicationTerms.updateInterestPaymentGrace(graceOnInterest); loanApplicationTerms.updatePrincipalGrace(graceOnPrincipal); loanApplicationTerms.setPrincipal(totalPrincipalOutstanding); loanApplicationTerms.updateNumberOfRepayments(rescheduleNumberOfRepayments); loanApplicationTerms.updateLoanTermFrequency(rescheduleNumberOfRepayments); loanApplicationTerms.updateInterestChargedFromDate(periodStartDateApplicableForInterest); Money totalInterestChargedForFullLoanTerm = loanApplicationTerms .calculateTotalInterestCharged(this.paymentPeriodsInOneYearCalculator, mathContext); if (!recalculateInterest && newInterestRate == null) { totalInterestChargedForFullLoanTerm = Money.of(currency, loanSummary.getTotalInterestCharged()); totalInterestChargedForFullLoanTerm = totalInterestChargedForFullLoanTerm .minus(actualTotalCumulativeInterest); loanApplicationTerms.updateTotalInterestDue(totalInterestChargedForFullLoanTerm); } for (LoanRescheduleModelRepaymentPeriod period : periods) { if (period.periodDueDate().isEqual(loanRescheduleRequest.getRescheduleFromDate()) || period.periodDueDate().isAfter(loanRescheduleRequest.getRescheduleFromDate()) || period.isNew()) { installmentDueDate = this.scheduledDateGenerator.generateNextRepaymentDate(installmentDueDate, loanApplicationTerms, false, holidayDetailDTO); if (adjustedDueDate != null && periodNumber == 1) { installmentDueDate = adjustedDueDate; } adjustedInstallmentDueDate = this.scheduledDateGenerator.adjustRepaymentDate(installmentDueDate, loanApplicationTerms, holidayDetailDTO); final int daysInInstallment = Days.daysBetween(installmentFromDate, adjustedInstallmentDueDate) .getDays(); period.updatePeriodNumber(installmentNumber); period.updatePeriodFromDate(installmentFromDate); period.updatePeriodDueDate(adjustedInstallmentDueDate); double interestCalculationGraceOnRepaymentPeriodFraction = this.paymentPeriodsInOneYearCalculator .calculatePortionOfRepaymentPeriodInterestChargingGrace( periodStartDateApplicableForInterest, adjustedInstallmentDueDate, periodStartDateApplicableForInterest, loanApplicationTerms.getLoanTermPeriodFrequencyType(), loanApplicationTerms.getRepaymentEvery()); // ========================= Calculate the interest due // ======================================== // change the principal to => Principal Disbursed - Total // Principal Paid // interest calculation is always based on the total // principal outstanding loanApplicationTerms.setPrincipal(totalPrincipalOutstanding); // for applying variations Collection<LoanTermVariationsData> applicableVariations = getApplicableTermVariationsForPeriod( installmentFromDate, adjustedInstallmentDueDate, loanTermVariations); // determine the interest & principal for the period PrincipalInterest principalInterestForThisPeriod = calculatePrincipalInterestComponentsForPeriod( this.paymentPeriodsInOneYearCalculator, interestCalculationGraceOnRepaymentPeriodFraction, totalCumulativePrincipal, totalCumulativeInterest, totalInterestChargedForFullLoanTerm, totalOutstandingInterestPaymentDueToGrace, outstandingBalance, loanApplicationTerms, periodNumber, mathContext, null, null, installmentFromDate, adjustedInstallmentDueDate, applicableVariations); // update the interest due for the period period.updateInterestDue(principalInterestForThisPeriod.interest()); // ============================================================================================= // ========================== Calculate the principal due // ====================================== // change the principal to => Principal Disbursed - Total // cumulative Principal Amount before the reschedule // installment loanApplicationTerms.setPrincipal(principal.minus(totalPrincipalBeforeReschedulePeriod)); principalInterestForThisPeriod = calculatePrincipalInterestComponentsForPeriod( this.paymentPeriodsInOneYearCalculator, interestCalculationGraceOnRepaymentPeriodFraction, totalCumulativePrincipal, totalCumulativeInterest, totalInterestChargedForFullLoanTerm, totalOutstandingInterestPaymentDueToGrace, outstandingBalance, loanApplicationTerms, periodNumber, mathContext, null, null, installmentFromDate, adjustedInstallmentDueDate, applicableVariations); period.updatePrincipalDue(principalInterestForThisPeriod.principal()); // ============================================================================================== outstandingLoanBalance = outstandingLoanBalance.minus(period.principalDue()); period.updateOutstandingLoanBalance(outstandingLoanBalance); Money principalDue = Money.of(currency, period.principalDue()); Money interestDue = Money.of(currency, period.interestDue()); if (principalDue.isZero() && interestDue.isZero()) { period.updateFeeChargesDue(Money.zero(currency)); period.updatePenaltyChargesDue(Money.zero(currency)); } Money feeChargesDue = Money.of(currency, period.feeChargesDue()); Money penaltyChargesDue = Money.of(currency, period.penaltyChargesDue()); Money totalDue = principalDue.plus(interestDue).plus(feeChargesDue).plus(penaltyChargesDue); period.updateTotalDue(totalDue); // update cumulative fields for principal & interest totalCumulativePrincipal = totalCumulativePrincipal.plus(period.principalDue()); totalCumulativeInterest = totalCumulativeInterest.plus(period.interestDue()); actualTotalCumulativeInterest = actualTotalCumulativeInterest.plus(period.interestDue()); totalOutstandingInterestPaymentDueToGrace = principalInterestForThisPeriod .interestPaymentDueToGrace(); installmentFromDate = adjustedInstallmentDueDate; installmentNumber++; periodNumber++; loanTermInDays += daysInInstallment; outstandingBalance = outstandingBalance.minus(period.principalDue()); } } } final Money totalRepaymentExpected = principal // get the loan Principal // amount .plus(actualTotalCumulativeInterest) // add the actual total // cumulative interest .plus(loanSummary.getTotalFeeChargesCharged()) // add the total // fees charged .plus(loanSummary.getTotalPenaltyChargesCharged()); // finally // add the // total // penalty // charged return LoanRescheduleModel.instance(periods, loanRepaymentScheduleHistoryList, applicationCurrency, loanTermInDays, loan.getPrincpal(), loan.getPrincpal().getAmount(), loanSummary.getTotalPrincipalRepaid(), actualTotalCumulativeInterest.getAmount(), loanSummary.getTotalFeeChargesCharged(), loanSummary.getTotalPenaltyChargesCharged(), totalRepaymentExpected.getAmount(), loanSummary.getTotalOutstanding()); }
From source file:org.apache.fineract.portfolio.loanaccount.loanschedule.domain.AbstractLoanScheduleGenerator.java
License:Apache License
private LoanScheduleDTO rescheduleNextInstallments(final MathContext mc, final LoanApplicationTerms loanApplicationTerms, final Set<LoanCharge> loanCharges, final HolidayDetailDTO holidayDetailDTO, final List<LoanTransaction> transactions, final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor, final List<LoanRepaymentScheduleInstallment> repaymentScheduleInstallments, final LocalDate rescheduleFrom, final LocalDate scheduleTillDate) { // Loan transactions to process and find the variation on payments Collection<RecalculationDetail> recalculationDetails = new ArrayList<>(); for (LoanTransaction loanTransaction : transactions) { recalculationDetails.add(new RecalculationDetail(loanTransaction.getTransactionDate(), LoanTransaction.copyTransactionProperties(loanTransaction))); }// ww w. ja v a 2s. co m final boolean applyInterestRecalculation = loanApplicationTerms.isInterestRecalculationEnabled(); LoanScheduleParams loanScheduleParams = null; Collection<LoanScheduleModelPeriod> periods = new ArrayList<>(); final List<LoanRepaymentScheduleInstallment> retainedInstallments = new ArrayList<>(); // this block is to retain the schedule installments prior to the // provided date and creates late and early payment details for further // calculations if (rescheduleFrom != null) { Money principalToBeScheduled = getPrincipalToBeScheduled(loanApplicationTerms); // actual outstanding balance for interest calculation Money outstandingBalance = principalToBeScheduled; // total outstanding balance as per rest for interest calculation. Money outstandingBalanceAsPerRest = outstandingBalance; // this is required to update total fee amounts in the // LoanScheduleModel final BigDecimal chargesDueAtTimeOfDisbursement = deriveTotalChargesDueAtTimeOfDisbursement( loanCharges); periods = createNewLoanScheduleListWithDisbursementDetails( loanApplicationTerms.fetchNumberOfRepaymentsAfterExceptions(), loanApplicationTerms, chargesDueAtTimeOfDisbursement); final List<LoanRepaymentScheduleInstallment> newRepaymentScheduleInstallments = new ArrayList<>(); MonetaryCurrency currency = outstandingBalance.getCurrency(); // early payments will be added here and as per the selected // strategy // action will be performed on this value Money reducePrincipal = outstandingBalanceAsPerRest.zero(); // principal changes will be added along with date(after applying // rest) // from when these amounts will effect the outstanding balance for // interest calculation final Map<LocalDate, Money> principalPortionMap = new HashMap<>(); // compounding(principal) amounts will be added along with // date(after applying compounding frequency) // from when these amounts will effect the outstanding balance for // interest calculation final Map<LocalDate, Money> latePaymentMap = new HashMap<>(); // compounding(interest/Fee) amounts will be added along with // date(after applying compounding frequency) // from when these amounts will effect the outstanding balance for // interest calculation final TreeMap<LocalDate, Money> compoundingMap = new TreeMap<>(); LocalDate currentDate = DateUtils.getLocalDateOfTenant(); LocalDate lastRestDate = currentDate; if (loanApplicationTerms.getRestCalendarInstance() != null) { lastRestDate = getNextRestScheduleDate(currentDate.minusDays(1), loanApplicationTerms, holidayDetailDTO); } LocalDate actualRepaymentDate = loanApplicationTerms.getExpectedDisbursementDate(); boolean isFirstRepayment = true; // cumulative fields Money totalCumulativePrincipal = principalToBeScheduled.zero(); Money totalCumulativeInterest = principalToBeScheduled.zero(); Money totalFeeChargesCharged = principalToBeScheduled.zero().plus(chargesDueAtTimeOfDisbursement); Money totalPenaltyChargesCharged = principalToBeScheduled.zero(); Money totalRepaymentExpected = principalToBeScheduled.zero(); // Actual period Number as per the schedule int periodNumber = 1; // Actual period Number plus interest only repayments int instalmentNumber = 1; LocalDate lastInstallmentDate = actualRepaymentDate; LocalDate periodStartDate = loanApplicationTerms.getExpectedDisbursementDate(); // Set fixed Amortization Amounts(either EMI or Principal ) updateAmortization(mc, loanApplicationTerms, periodNumber, outstandingBalance); final Map<LocalDate, Money> disburseDetailMap = new HashMap<>(); if (loanApplicationTerms.isMultiDisburseLoan()) { // fetches the first tranche amount and also updates other // tranche // details to map BigDecimal disburseAmt = getDisbursementAmount(loanApplicationTerms, loanApplicationTerms.getExpectedDisbursementDate(), periods, chargesDueAtTimeOfDisbursement, disburseDetailMap, true); outstandingBalance = outstandingBalance.zero().plus(disburseAmt); outstandingBalanceAsPerRest = outstandingBalance; principalToBeScheduled = principalToBeScheduled.zero().plus(disburseAmt); } int loanTermInDays = 0; // Block process the installment and creates the period if it falls // before reschedule from date // This will create the recalculation details by applying the // transactions for (LoanRepaymentScheduleInstallment installment : repaymentScheduleInstallments) { // this will generate the next schedule due date and allows to // process the installment only if recalculate from date is // greater than due date if (installment.getDueDate().isAfter(lastInstallmentDate)) { LocalDate previousRepaymentDate = actualRepaymentDate; actualRepaymentDate = this.scheduledDateGenerator.generateNextRepaymentDate(actualRepaymentDate, loanApplicationTerms, isFirstRepayment, holidayDetailDTO); isFirstRepayment = false; lastInstallmentDate = this.scheduledDateGenerator.adjustRepaymentDate(actualRepaymentDate, loanApplicationTerms, holidayDetailDTO); if (!lastInstallmentDate.isBefore(rescheduleFrom)) { actualRepaymentDate = previousRepaymentDate; break; } periodNumber++; // check for date changes while (loanApplicationTerms.getLoanTermVariations().hasDueDateVariation(lastInstallmentDate)) { LoanTermVariationsData variation = loanApplicationTerms.getLoanTermVariations() .nextDueDateVariation(); if (!variation.isSpecificToInstallment()) { actualRepaymentDate = variation.getDateValue(); } variation.setProcessed(true); } } for (Map.Entry<LocalDate, Money> disburseDetail : disburseDetailMap.entrySet()) { if (disburseDetail.getKey().isAfter(installment.getFromDate()) && !disburseDetail.getKey().isAfter(installment.getDueDate())) { // creates and add disbursement detail to the repayments // period final LoanScheduleModelDisbursementPeriod disbursementPeriod = LoanScheduleModelDisbursementPeriod .disbursement(disburseDetail.getKey(), disburseDetail.getValue(), chargesDueAtTimeOfDisbursement); periods.add(disbursementPeriod); // updates actual outstanding balance with new // disbursement detail outstandingBalance = outstandingBalance.plus(disburseDetail.getValue()); principalToBeScheduled = principalToBeScheduled.plus(disburseDetail.getValue()); } } // calculation of basic fields to start the schedule generation // from the middle periodStartDate = installment.getDueDate(); installment.resetDerivedComponents(); newRepaymentScheduleInstallments.add(installment); outstandingBalance = outstandingBalance.minus(installment.getPrincipal(currency)); final LoanScheduleModelPeriod loanScheduleModelPeriod = createLoanScheduleModelPeriod(installment, outstandingBalance); periods.add(loanScheduleModelPeriod); totalCumulativePrincipal = totalCumulativePrincipal.plus(installment.getPrincipal(currency)); totalCumulativeInterest = totalCumulativeInterest.plus(installment.getInterestCharged(currency)); totalFeeChargesCharged = totalFeeChargesCharged.plus(installment.getFeeChargesCharged(currency)); totalPenaltyChargesCharged = totalPenaltyChargesCharged .plus(installment.getPenaltyChargesCharged(currency)); instalmentNumber++; loanTermInDays = Days.daysBetween(installment.getFromDate(), installment.getDueDate()).getDays(); // populates the collection with transactions till the due date // of // the period for interest recalculation enabled loans Collection<RecalculationDetail> applicableTransactions = getApplicableTransactionsForPeriod( applyInterestRecalculation, installment.getDueDate(), recalculationDetails); // calculates the expected principal value for this repayment // schedule Money principalPortionCalculated = principalToBeScheduled.zero(); if (!installment.isRecalculatedInterestComponent()) { principalPortionCalculated = calculateExpectedPrincipalPortion( installment.getInterestCharged(currency), loanApplicationTerms); } // expected principal considering the previously paid excess // amount Money actualPrincipalPortion = principalPortionCalculated.minus(reducePrincipal); if (actualPrincipalPortion.isLessThanZero()) { actualPrincipalPortion = principalPortionCalculated.zero(); } Money unprocessed = updateEarlyPaidAmountsToMap(loanApplicationTerms, holidayDetailDTO, loanRepaymentScheduleTransactionProcessor, newRepaymentScheduleInstallments, currency, principalPortionMap, installment, applicableTransactions, actualPrincipalPortion); // this block is to adjust the period number based on the actual // schedule due date and installment due date // recalculatedInterestComponent installment shouldn't be // considered while calculating fixed EMI amounts int period = periodNumber; if (!lastInstallmentDate.isEqual(installment.getDueDate())) { period--; } reducePrincipal = fetchEarlyPaidAmount(installment.getPrincipal(currency), principalPortionCalculated, reducePrincipal, loanApplicationTerms, totalCumulativePrincipal, period, mc); // Updates principal paid map with efective date for reducing // the amount from outstanding balance(interest calculation) LocalDate amountApplicableDate = getNextRestScheduleDate(installment.getDueDate().minusDays(1), loanApplicationTerms, holidayDetailDTO); // updates map with the installment principal amount excluding // unprocessed amount since this amount is already accounted. updateMapWithAmount(principalPortionMap, installment.getPrincipal(currency).minus(unprocessed), amountApplicableDate); // update outstanding balance for interest calculation outstandingBalanceAsPerRest = updateBalanceForInterestCalculation(principalPortionMap, installment.getDueDate(), outstandingBalanceAsPerRest, false); outstandingBalanceAsPerRest = updateBalanceForInterestCalculation(disburseDetailMap, installment.getDueDate(), outstandingBalanceAsPerRest, true); } totalRepaymentExpected = totalCumulativePrincipal.plus(totalCumulativeInterest) .plus(totalFeeChargesCharged).plus(totalPenaltyChargesCharged); // updates the map with over due amounts updateLatePaymentsToMap(loanApplicationTerms, holidayDetailDTO, currency, latePaymentMap, lastInstallmentDate, newRepaymentScheduleInstallments, true, lastRestDate, compoundingMap); // for partial schedule generation if (!newRepaymentScheduleInstallments.isEmpty() && totalCumulativeInterest.isGreaterThanZero()) { Money totalOutstandingInterestPaymentDueToGrace = Money.zero(currency); loanScheduleParams = LoanScheduleParams.createLoanScheduleParamsForPartialUpdate(periodNumber, instalmentNumber, loanTermInDays, periodStartDate, actualRepaymentDate, totalCumulativePrincipal, totalCumulativeInterest, totalFeeChargesCharged, totalPenaltyChargesCharged, totalRepaymentExpected, totalOutstandingInterestPaymentDueToGrace, reducePrincipal, principalPortionMap, latePaymentMap, compoundingMap, disburseDetailMap, principalToBeScheduled, outstandingBalance, outstandingBalanceAsPerRest, newRepaymentScheduleInstallments, recalculationDetails, loanRepaymentScheduleTransactionProcessor, scheduleTillDate, currency, applyInterestRecalculation); retainedInstallments.addAll(newRepaymentScheduleInstallments); } } // for complete schedule generation if (loanScheduleParams == null) { loanScheduleParams = LoanScheduleParams.createLoanScheduleParamsForCompleteUpdate(recalculationDetails, loanRepaymentScheduleTransactionProcessor, scheduleTillDate, applyInterestRecalculation); } LoanScheduleModel loanScheduleModel = generate(mc, loanApplicationTerms, loanCharges, holidayDetailDTO, loanScheduleParams); for (LoanScheduleModelPeriod loanScheduleModelPeriod : loanScheduleModel.getPeriods()) { if (loanScheduleModelPeriod.isRepaymentPeriod()) { // adding newly created repayment periods to installments addLoanRepaymentScheduleInstallment(retainedInstallments, loanScheduleModelPeriod); } } periods.addAll(loanScheduleModel.getPeriods()); LoanScheduleModel loanScheduleModelwithPeriodChanges = LoanScheduleModel .withLoanScheduleModelPeriods(periods, loanScheduleModel); return LoanScheduleDTO.from(retainedInstallments, loanScheduleModelwithPeriodChanges); }
From source file:org.apache.fineract.portfolio.loanaccount.loanschedule.domain.DecliningBalanceInterestLoanScheduleGenerator.java
License:Apache License
@Override public PrincipalInterest calculatePrincipalInterestComponentsForPeriod( final PaymentPeriodsInOneYearCalculator calculator, final double interestCalculationGraceOnRepaymentPeriodFraction, final Money totalCumulativePrincipal, @SuppressWarnings("unused") final Money totalCumulativeInterest, @SuppressWarnings("unused") final Money totalInterestDueForLoan, final Money cumulatingInterestPaymentDueToGrace, final Money outstandingBalance, final LoanApplicationTerms loanApplicationTerms, final int periodNumber, final MathContext mc, final TreeMap<LocalDate, Money> principalVariation, final Map<LocalDate, Money> compoundingMap, final LocalDate periodStartDate, final LocalDate periodEndDate, final Collection<LoanTermVariationsData> termVariations) { LocalDate interestStartDate = periodStartDate; Money interestForThisInstallment = totalCumulativePrincipal.zero(); Money compoundedMoney = totalCumulativePrincipal.zero(); Money compoundedInterest = totalCumulativePrincipal.zero(); Money balanceForInterestCalculation = outstandingBalance; Money cumulatingInterestDueToGrace = cumulatingInterestPaymentDueToGrace; Map<LocalDate, BigDecimal> interestRates = new HashMap<>(termVariations.size()); for (LoanTermVariationsData loanTermVariation : termVariations) { if (loanTermVariation.getTermVariationType().isInterestRateVariation() && loanTermVariation.isApplicable(periodStartDate, periodEndDate)) { LocalDate fromDate = loanTermVariation.getTermApplicableFrom(); if (fromDate == null) { fromDate = periodStartDate; }//from w w w . j av a 2 s . co m interestRates.put(fromDate, loanTermVariation.getDecimalValue()); if (!principalVariation.containsKey(fromDate)) { principalVariation.put(fromDate, balanceForInterestCalculation.zero()); } } } if (principalVariation != null) { // identifies rest date after current date for reducing all // compounding // values LocalDate compoundingEndDate = principalVariation.ceilingKey(DateUtils.getLocalDateOfTenant()); if (compoundingEndDate == null) { compoundingEndDate = DateUtils.getLocalDateOfTenant(); } for (Map.Entry<LocalDate, Money> principal : principalVariation.entrySet()) { if (!principal.getKey().isAfter(periodEndDate)) { int interestForDays = Days.daysBetween(interestStartDate, principal.getKey()).getDays(); if (interestForDays > 0) { final PrincipalInterest result = loanApplicationTerms.calculateTotalInterestForPeriod( calculator, interestCalculationGraceOnRepaymentPeriodFraction, periodNumber, mc, cumulatingInterestDueToGrace, balanceForInterestCalculation, interestStartDate, principal.getKey()); interestForThisInstallment = interestForThisInstallment.plus(result.interest()); cumulatingInterestDueToGrace = result.interestPaymentDueToGrace(); interestStartDate = principal.getKey(); } Money compoundFee = totalCumulativePrincipal.zero(); if (compoundingMap.containsKey(principal.getKey())) { Money interestToBeCompounded = totalCumulativePrincipal.zero(); // for interest compounding if (loanApplicationTerms.getInterestRecalculationCompoundingMethod() .isInterestCompoundingEnabled()) { interestToBeCompounded = interestForThisInstallment.minus(compoundedInterest); balanceForInterestCalculation = balanceForInterestCalculation .plus(interestToBeCompounded); compoundedInterest = interestForThisInstallment; } // fee compounding will be done after calculation compoundFee = compoundingMap.get(principal.getKey()); compoundedMoney = compoundedMoney.plus(interestToBeCompounded).plus(compoundFee); } balanceForInterestCalculation = balanceForInterestCalculation.plus(principal.getValue()) .plus(compoundFee); if (interestRates.containsKey(principal.getKey())) { loanApplicationTerms.updateAnnualNominalInterestRate(interestRates.get(principal.getKey())); } } } if (!periodEndDate.isBefore(compoundingEndDate)) { balanceForInterestCalculation = balanceForInterestCalculation.minus(compoundedMoney); compoundingMap.clear(); } else if (compoundedMoney.isGreaterThanZero()) { compoundingMap.put(periodEndDate, compoundedMoney); compoundingMap.put(compoundingEndDate, compoundedMoney.negated()); clearMapDetails(periodEndDate, compoundingMap); } } final PrincipalInterest result = loanApplicationTerms.calculateTotalInterestForPeriod(calculator, interestCalculationGraceOnRepaymentPeriodFraction, periodNumber, mc, cumulatingInterestDueToGrace, balanceForInterestCalculation, interestStartDate, periodEndDate); interestForThisInstallment = interestForThisInstallment.plus(result.interest()); cumulatingInterestDueToGrace = result.interestPaymentDueToGrace(); Money interestForPeriod = interestForThisInstallment; if (interestForPeriod.isGreaterThanZero()) { interestForPeriod = interestForPeriod.minus(cumulatingInterestPaymentDueToGrace); } else { interestForPeriod = cumulatingInterestDueToGrace.minus(cumulatingInterestPaymentDueToGrace); } Money principalForThisInstallment = loanApplicationTerms.calculateTotalPrincipalForPeriod(calculator, outstandingBalance, periodNumber, mc, interestForPeriod); // update cumulative fields for principal & interest final Money interestBroughtFowardDueToGrace = cumulatingInterestDueToGrace; final Money totalCumulativePrincipalToDate = totalCumulativePrincipal.plus(principalForThisInstallment); // adjust if needed principalForThisInstallment = loanApplicationTerms.adjustPrincipalIfLastRepaymentPeriod( principalForThisInstallment, totalCumulativePrincipalToDate, periodNumber); return new PrincipalInterest(principalForThisInstallment, interestForThisInstallment, interestBroughtFowardDueToGrace); }
From source file:org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanApplicationTerms.java
License:Apache License
private BigDecimal calculatePeriodsInLoanTerm() { BigDecimal periodsInLoanTerm = BigDecimal.valueOf(this.loanTermFrequency); switch (this.interestCalculationPeriodMethod) { case DAILY:/* w w w . j a v a 2 s . co m*/ // number of days from 'ideal disbursement' to final date LocalDate loanStartDate = getExpectedDisbursementDate(); if (getInterestChargedFromDate() != null && loanStartDate.isBefore(getInterestChargedFromLocalDate())) { loanStartDate = getInterestChargedFromLocalDate(); } final int periodsInLoanTermInteger = Days.daysBetween(loanStartDate, this.loanEndDate).getDays(); periodsInLoanTerm = BigDecimal.valueOf(periodsInLoanTermInteger); break; case INVALID: break; case SAME_AS_REPAYMENT_PERIOD: LocalDate startDate = getExpectedDisbursementDate(); periodsInLoanTerm = calculatePeriodsBetweenDates(startDate, this.loanEndDate); break; } return periodsInLoanTerm; }
From source file:org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanApplicationTerms.java
License:Apache License
public BigDecimal calculatePeriodsBetweenDates(final LocalDate startDate, final LocalDate endDate) { BigDecimal numberOfPeriods = BigDecimal.ZERO; switch (this.repaymentPeriodFrequencyType) { case DAYS:/*from w ww .j a va 2 s . c om*/ int numberOfDays = Days.daysBetween(startDate, endDate).getDays(); numberOfPeriods = BigDecimal.valueOf((double) numberOfDays); break; case WEEKS: int numberOfWeeks = Weeks.weeksBetween(startDate, endDate).getWeeks(); int daysLeftAfterWeeks = Days.daysBetween(startDate.plusWeeks(numberOfWeeks), endDate).getDays(); numberOfPeriods = numberOfPeriods.add(BigDecimal.valueOf(numberOfWeeks)) .add(BigDecimal.valueOf((double) daysLeftAfterWeeks / 7)); break; case MONTHS: int numberOfMonths = Months.monthsBetween(startDate, endDate).getMonths(); LocalDate startDateAfterConsideringMonths = startDate.plusMonths(numberOfMonths); LocalDate endDateAfterConsideringMonths = startDate.plusMonths(numberOfMonths + 1); int daysLeftAfterMonths = Days.daysBetween(startDateAfterConsideringMonths, endDate).getDays(); int daysInPeriodAfterMonths = Days .daysBetween(startDateAfterConsideringMonths, endDateAfterConsideringMonths).getDays(); numberOfPeriods = numberOfPeriods.add(BigDecimal.valueOf(numberOfMonths)) .add(BigDecimal.valueOf((double) daysLeftAfterMonths / daysInPeriodAfterMonths)); break; case YEARS: int numberOfYears = Years.yearsBetween(startDate, endDate).getYears(); LocalDate startDateAfterConsideringYears = startDate.plusYears(numberOfYears); LocalDate endDateAfterConsideringYears = startDate.plusYears(numberOfYears + 1); int daysLeftAfterYears = Days.daysBetween(startDateAfterConsideringYears, endDate).getDays(); int daysInPeriodAfterYears = Days .daysBetween(startDateAfterConsideringYears, endDateAfterConsideringYears).getDays(); numberOfPeriods = numberOfPeriods.add(BigDecimal.valueOf(numberOfYears)) .add(BigDecimal.valueOf((double) daysLeftAfterYears / daysInPeriodAfterYears)); break; default: break; } return numberOfPeriods; }
From source file:org.apache.fineract.portfolio.loanaccount.loanschedule.service.LoanScheduleAssembler.java
License:Apache License
public void assempleVariableScheduleFrom(final Loan loan, final String json) { this.variableLoanScheduleFromApiJsonValidator.validateSchedule(json, loan); List<LoanTermVariations> variations = loan.getLoanTermVariations(); List<LoanTermVariations> newVariations = new ArrayList<>(); extractLoanTermVariations(loan, json, newVariations); final Map<LocalDate, LocalDate> adjustDueDateVariations = new HashMap<>(); if (!variations.isEmpty()) { List<LoanTermVariations> retainVariations = adjustExistingVariations(variations, newVariations, adjustDueDateVariations); newVariations = retainVariations; }/*w w w .ja va2 s . c om*/ variations.addAll(newVariations); Collections.sort(variations, new LoanTermVariationsComparator()); /* * List<LoanTermVariationsData> loanTermVariationsDatas = new * ArrayList<>(); * loanTermVariationsDatas.addAll(loanApplicationTerms.getLoanTermVariations * ().getExceptionData()); loanApplicationTerms = * LoanApplicationTerms.assembleFrom(loanApplicationTerms, * loanTermVariationsDatas); */ // date validations List<LoanRepaymentScheduleInstallment> installments = loan.fetchRepaymentScheduleInstallments(); Set<LocalDate> dueDates = new TreeSet<>(); LocalDate graceApplicable = loan.getExpectedDisbursedOnLocalDate(); Integer graceOnPrincipal = loan.getLoanProductRelatedDetail().graceOnPrincipalPayment(); if (graceOnPrincipal == null) { graceOnPrincipal = 0; } LocalDate lastDate = loan.getExpectedDisbursedOnLocalDate(); for (LoanRepaymentScheduleInstallment installment : installments) { dueDates.add(installment.getDueDate()); if (lastDate.isBefore(installment.getDueDate())) { lastDate = installment.getDueDate(); } if (graceOnPrincipal.equals(installment.getInstallmentNumber())) { graceApplicable = installment.getDueDate(); } } Collection<LocalDate> keySet = adjustDueDateVariations.keySet(); dueDates.addAll(keySet); for (final LocalDate date : keySet) { LocalDate removeDate = adjustDueDateVariations.get(date); if (removeDate != null) { dueDates.remove(removeDate); } } Set<LocalDate> actualDueDates = new TreeSet<>(dueDates); final List<ApiParameterError> dataValidationErrors = new ArrayList<>(); final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) .resource("loan"); List<LocalDate> overlappings = new ArrayList<>(); for (LoanTermVariations termVariations : variations) { switch (termVariations.getTermType()) { case INSERT_INSTALLMENT: if (dueDates.contains(termVariations.fetchTermApplicaDate())) { overlappings.add(termVariations.fetchTermApplicaDate()); } else { dueDates.add(termVariations.fetchTermApplicaDate()); } if (!graceApplicable.isBefore(termVariations.fetchTermApplicaDate())) { baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode( "variable.schedule.insert.not.allowed.before.grace.period", "Loan schedule insert request invalid"); } if (termVariations.fetchTermApplicaDate().isAfter(lastDate)) { baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode( "variable.schedule.insert.not.allowed.after.last.period.date", "Loan schedule insert request invalid"); } else if (termVariations.fetchTermApplicaDate().isBefore(loan.getExpectedDisbursedOnLocalDate())) { baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode( "variable.schedule.insert.not.allowed.before.disbursement.date", "Loan schedule insert request invalid"); } break; case DELETE_INSTALLMENT: if (dueDates.contains(termVariations.fetchTermApplicaDate())) { dueDates.remove(termVariations.fetchTermApplicaDate()); } else { baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode( "variable.schedule.remove.date.invalid", "Loan schedule remove request invalid"); } if (termVariations.fetchTermApplicaDate().isEqual(lastDate)) { baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode( "variable.schedule.delete.not.allowed.for.last.period.date", "Loan schedule remove request invalid"); } break; case DUE_DATE: if (dueDates.contains(termVariations.fetchTermApplicaDate())) { if (overlappings.contains(termVariations.fetchTermApplicaDate())) { overlappings.remove(termVariations.fetchTermApplicaDate()); } else { dueDates.remove(termVariations.fetchTermApplicaDate()); } } else { baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode( "variable.schedule.modify.date.invalid", "Loan schedule modify due date request invalid"); } if (dueDates.contains(termVariations.fetchDateValue())) { overlappings.add(termVariations.fetchDateValue()); } else { dueDates.add(termVariations.fetchDateValue()); } if (termVariations.fetchDateValue().isBefore(loan.getExpectedDisbursedOnLocalDate())) { baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode( "variable.schedule.insert.not.allowed.before.disbursement.date", "Loan schedule insert request invalid"); } if (termVariations.fetchTermApplicaDate().isEqual(lastDate)) { lastDate = termVariations.fetchDateValue(); } break; case PRINCIPAL_AMOUNT: case EMI_AMOUNT: if (!graceApplicable.isBefore(termVariations.fetchTermApplicaDate())) { baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode( "variable.schedule.amount.update.not.allowed.before.grace.period", "Loan schedule modify request invalid"); } if (!dueDates.contains(termVariations.fetchTermApplicaDate())) { baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode( "variable.schedule.amount.update.from.date.invalid", "Loan schedule modify request invalid"); } if (termVariations.fetchTermApplicaDate().isEqual(lastDate)) { baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode( "variable.schedule.amount.update.not.allowed.for.last.period", "Loan schedule modify request invalid"); } break; default: break; } } if (!overlappings.isEmpty()) { baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode( "variable.schedule.modify.date.can.not.be.due.date", overlappings); } LoanProductVariableInstallmentConfig installmentConfig = loan.loanProduct() .loanProductVariableInstallmentConfig(); final CalendarInstance loanCalendarInstance = calendarInstanceRepository .findCalendarInstaneByEntityId(loan.getId(), CalendarEntityType.LOANS.getValue()); Calendar loanCalendar = null; if (loanCalendarInstance != null) { loanCalendar = loanCalendarInstance.getCalendar(); } final Integer minGap = installmentConfig.getMinimumGap(); final Integer maxGap = installmentConfig.getMaximumGap(); LocalDate previousDate = loan.getDisbursementDate(); for (LocalDate duedate : dueDates) { int gap = Days.daysBetween(previousDate, duedate).getDays(); previousDate = duedate; if (gap < minGap || (maxGap != null && gap > maxGap)) { baseDataValidator.reset().value(duedate).failWithCodeNoParameterAddedToErrorCode( "variable.schedule.date.must.be.in.min.max.range", "Loan schedule date invalid"); } else if (loanCalendar != null && !actualDueDates.contains(duedate) && !loanCalendar.isValidRecurringDate(duedate)) { baseDataValidator.reset().value(duedate).failWithCodeNoParameterAddedToErrorCode( "variable.schedule.date.not.meeting.date", "Loan schedule date not in sync with meeting date"); } } if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } if (loan.getExpectedFirstRepaymentOnDate() == null) { loan.setExpectedFirstRepaymentOnDate(loan.fetchRepaymentScheduleInstallment(1).getDueDate().toDate()); } final LocalDate recalculateFrom = null; ScheduleGeneratorDTO scheduleGeneratorDTO = this.loanUtilService.buildScheduleGeneratorDTO(loan, recalculateFrom); AppUser currentUser = this.context.getAuthenticatedUserIfPresent(); loan.regenerateRepaymentSchedule(scheduleGeneratorDTO, currentUser); }
From source file:org.apache.james.transport.mailets.jsieve.VacationAction.java
License:Apache License
@Override public void execute(Action action, Mail mail, ActionContext context) throws MessagingException { ActionVacation actionVacation = (ActionVacation) action; int dayDifference = Days .daysBetween(context.getScriptActivationDate(), context.getScriptInterpretationDate()).getDays(); if (isStillInVacation(actionVacation, dayDifference)) { if (isValidForReply(mail, actionVacation, context)) { if (!isMailingList(mail)) { sendVacationNotification(mail, actionVacation, context); }// w w w.j a va2 s .c o m } } }
From source file:org.apache.pig.piggybank.evaluation.datetime.diff.ISODaysBetween.java
License:Apache License
@Override public Long exec(Tuple input) throws IOException { if (input == null || input.size() < 2) { return null; }// w w w .j a v a 2 s. c om // Set the time to default or the output is in UTC DateTimeZone.setDefault(DateTimeZone.UTC); DateTime startDate = new DateTime(input.get(0).toString()); DateTime endDate = new DateTime(input.get(1).toString()); // Larger date first Days d = Days.daysBetween(endDate, startDate); long days = d.getDays(); return days; }
From source file:org.apereo.portlet.hr.demo.timereporting.dao.DemoStaffTimeReportingImpl.java
License:Apache License
@Override public PayPeriodDailyLeaveTimeSummary getLeaveHoursReported(String emplId, LocalDate dateInPayPeriod) { initializeForEmployeeIfNeeded(emplId); LocalDate startDate = calculatePayperiodStartDate(dateInPayPeriod); PayPeriodDailyLeaveTimeSummary summary = new PayPeriodDailyLeaveTimeSummary(); summary.setPayPeriodStart(startDate); summary.setPayPeriodEnd(startDate.plusDays(DAYS_IN_PAY_PERIOD - 1)); summary.setJobDescriptions(jobDescriptions); // If more than 1 pay period back or forward, leave cannot be entered. int dayDelta = Days.daysBetween(new LocalDate(), startDate).getDays(); summary.setDisplayOnlyJobCodes(/* w w w. ja v a 2s . com*/ dayDelta <= DAYS_IN_PAY_PERIOD * -2 || dayDelta > DAYS_IN_PAY_PERIOD ? allUneditableJobs : uneditableJobs); summary.setTimePeriodEntries(getTimeEntries(emplId, startDate, summary.getPayPeriodEnd())); return summary; }