List of usage examples for org.joda.time LocalDate minusDays
public LocalDate minusDays(int days)
From source file:org.apache.fineract.portfolio.loanaccount.loanschedule.domain.AbstractLoanScheduleGenerator.java
License:Apache License
private LoanScheduleModelPeriod handlePrepaymentOfLoan(final MathContext mc, final LoanApplicationTerms loanApplicationTerms, final HolidayDetailDTO holidayDetailDTO, final LoanScheduleParams scheduleParams, final Money totalInterestChargedForFullLoanTerm, final LocalDate scheduledDueDate, LocalDate periodStartDateApplicableForInterest, final double interestCalculationGraceOnRepaymentPeriodFraction, final ScheduleCurrentPeriodParams currentPeriodParams, final Money lastTotalOutstandingInterestPaymentDueToGrace, final LocalDate transactionDate, final LoanScheduleModelPeriod installment, Set<LoanCharge> loanCharges) { LoanScheduleModelPeriod modifiedInstallment = installment; if (!scheduleParams.getOutstandingBalance().isGreaterThan(currentPeriodParams.getInterestForThisPeriod()) && !scheduledDueDate.equals(transactionDate)) { final Collection<LoanTermVariationsData> interestRates = loanApplicationTerms.getLoanTermVariations() .getInterestRateChanges(); LocalDate calculateTill = transactionDate; if (loanApplicationTerms.getPreClosureInterestCalculationStrategy() .calculateTillRestFrequencyEnabled()) { calculateTill = getNextRestScheduleDate(calculateTill.minusDays(1), loanApplicationTerms, holidayDetailDTO);//from www.java 2 s . co m } if (scheduleParams.getCompoundingDateVariations().containsKey(periodStartDateApplicableForInterest)) { scheduleParams.getCompoundingMap().clear(); scheduleParams.getCompoundingMap().putAll( scheduleParams.getCompoundingDateVariations().get(periodStartDateApplicableForInterest)); } if (currentPeriodParams.isEmiAmountChanged()) { updateFixedInstallmentAmount(mc, loanApplicationTerms, scheduleParams.getPeriodNumber(), loanApplicationTerms.getPrincipal().minus(scheduleParams.getTotalCumulativePrincipal())); } PrincipalInterest interestTillDate = calculatePrincipalInterestComponentsForPeriod( this.paymentPeriodsInOneYearCalculator, interestCalculationGraceOnRepaymentPeriodFraction, scheduleParams.getTotalCumulativePrincipal(), scheduleParams.getTotalCumulativeInterest(), totalInterestChargedForFullLoanTerm, lastTotalOutstandingInterestPaymentDueToGrace, scheduleParams.getOutstandingBalanceAsPerRest(), loanApplicationTerms, scheduleParams.getPeriodNumber(), mc, mergeVariationsToMap(scheduleParams), scheduleParams.getCompoundingMap(), periodStartDateApplicableForInterest, calculateTill, interestRates); // applies charges for the period final ScheduleCurrentPeriodParams tempPeriod = new ScheduleCurrentPeriodParams( totalInterestChargedForFullLoanTerm.getCurrency(), interestCalculationGraceOnRepaymentPeriodFraction); tempPeriod.setInterestForThisPeriod(interestTillDate.interest()); applyChargesForCurrentPeriod(loanCharges, totalInterestChargedForFullLoanTerm.getCurrency(), scheduleParams, calculateTill, tempPeriod); Money interestDiff = currentPeriodParams.getInterestForThisPeriod() .minus(tempPeriod.getInterestForThisPeriod()); Money chargeDiff = currentPeriodParams.getFeeChargesForInstallment() .minus(tempPeriod.getFeeChargesForInstallment()); Money penaltyDiff = currentPeriodParams.getPenaltyChargesForInstallment() .minus(tempPeriod.getPenaltyChargesForInstallment()); Money diff = interestDiff.plus(chargeDiff).plus(penaltyDiff); if (!scheduleParams.getOutstandingBalance().minus(diff).isGreaterThanZero()) { scheduleParams.reduceOutstandingBalance(diff); currentPeriodParams.minusInterestForThisPeriod(interestDiff); currentPeriodParams.minusFeeChargesForInstallment(chargeDiff); currentPeriodParams.minusPenaltyChargesForInstallment(penaltyDiff); currentPeriodParams.plusPrincipalForThisPeriod(diff); // create and replaces repayment period // from parts modifiedInstallment = LoanScheduleModelRepaymentPeriod.repayment( scheduleParams.getInstalmentNumber(), scheduleParams.getPeriodStartDate(), transactionDate, currentPeriodParams.getPrincipalForThisPeriod(), scheduleParams.getOutstandingBalance(), currentPeriodParams.getInterestForThisPeriod(), currentPeriodParams.getFeeChargesForInstallment(), currentPeriodParams.getPenaltyChargesForInstallment(), currentPeriodParams.fetchTotalAmountForPeriod(), false); scheduleParams .setTotalOutstandingInterestPaymentDueToGrace(interestTillDate.interestPaymentDueToGrace()); } } return modifiedInstallment; }
From source file: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; }/* ww w .j a v 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
/** * Method calculates interest on not paid outstanding principal and interest * (if compounding is enabled) till current date and adds new repayment * schedule detail/*from w ww . ja va 2 s. c o m*/ * * @param compoundingMap * TODO * @param loanCharges * TODO * @param principalPortioMap * TODO * */ private Money addInterestOnlyRepaymentScheduleForCurrentdate(final MathContext mc, final LoanApplicationTerms loanApplicationTerms, final HolidayDetailDTO holidayDetailDTO, final MonetaryCurrency currency, final Collection<LoanScheduleModelPeriod> periods, final LocalDate currentDate, LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor, final Collection<RecalculationDetail> transactions, final Set<LoanCharge> loanCharges, final LoanScheduleParams params) { boolean isFirstRepayment = false; LocalDate startDate = params.getPeriodStartDate(); Money outstanding = Money.zero(currency); Money totalInterest = Money.zero(currency); Money totalCumulativeInterest = Money.zero(currency); double interestCalculationGraceOnRepaymentPeriodFraction = Double.valueOf(0); int periodNumberTemp = 1; LocalDate lastRestDate = getNextRestScheduleDate(currentDate.minusDays(1), loanApplicationTerms, holidayDetailDTO); Collection<LoanTermVariationsData> applicableVariations = loanApplicationTerms.getLoanTermVariations() .getInterestRateChanges(); do { params.setActualRepaymentDate(this.scheduledDateGenerator.generateNextRepaymentDate( params.getActualRepaymentDate(), loanApplicationTerms, isFirstRepayment, holidayDetailDTO)); if (params.getActualRepaymentDate().isAfter(currentDate)) { params.setActualRepaymentDate(currentDate); } outstanding = updateOutstandingFromLatePayment(params.getPeriodStartDate(), params.getLatePaymentMap(), outstanding); Collection<RecalculationDetail> applicableTransactions = getApplicableTransactionsForPeriod( params.applyInterestRecalculation(), params.getActualRepaymentDate(), transactions); if (!params.getLatePaymentMap().isEmpty()) { populateCompoundingDatesInPeriod(params.getPeriodStartDate(), params.getActualRepaymentDate(), currentDate, loanApplicationTerms, holidayDetailDTO, params.getCompoundingMap(), loanCharges, currency); } for (RecalculationDetail detail : applicableTransactions) { if (detail.isProcessed()) { continue; } LocalDate transactionDate = detail.getTransactionDate(); List<LoanTransaction> currentTransactions = createCurrentTransactionList(detail); if (!params.getPeriodStartDate().isEqual(transactionDate)) { PrincipalInterest principalInterestForThisPeriod = calculatePrincipalInterestComponentsForPeriod( this.paymentPeriodsInOneYearCalculator, interestCalculationGraceOnRepaymentPeriodFraction, totalInterest.zero(), totalInterest.zero(), totalInterest.zero(), totalInterest.zero(), outstanding, loanApplicationTerms, periodNumberTemp, mc, mergeVariationsToMap(params), params.getCompoundingMap(), params.getPeriodStartDate(), transactionDate, applicableVariations); Money interest = principalInterestForThisPeriod.interest(); totalInterest = totalInterest.plus(interest); LoanScheduleModelRepaymentPeriod installment = LoanScheduleModelRepaymentPeriod.repayment( params.getInstalmentNumber(), startDate, transactionDate, totalInterest.zero(), totalInterest.zero(), totalInterest, totalInterest.zero(), totalInterest.zero(), totalInterest, true); params.incrementInstalmentNumber(); periods.add(installment); totalCumulativeInterest = totalCumulativeInterest.plus(totalInterest); totalInterest = totalInterest.zero(); addLoanRepaymentScheduleInstallment(params.getInstallments(), installment); params.setPeriodStartDate(transactionDate); startDate = transactionDate; } loanRepaymentScheduleTransactionProcessor.handleRepaymentSchedule(currentTransactions, currency, params.getInstallments()); updateLatePaymentsToMap(loanApplicationTerms, holidayDetailDTO, currency, params.getLatePaymentMap(), currentDate, params.getInstallments(), false, lastRestDate, params.getCompoundingMap()); outstanding = outstanding.zero(); outstanding = updateOutstandingFromLatePayment(params.getPeriodStartDate(), params.getLatePaymentMap(), outstanding); outstanding = updateBalanceForInterestCalculation(params.getPrincipalPortionMap(), params.getPeriodStartDate(), outstanding, false); if (params.getLatePaymentMap().isEmpty() && !outstanding.isGreaterThanZero()) { break; } } if (outstanding.isGreaterThanZero()) { PrincipalInterest principalInterestForThisPeriod = calculatePrincipalInterestComponentsForPeriod( this.paymentPeriodsInOneYearCalculator, interestCalculationGraceOnRepaymentPeriodFraction, totalInterest.zero(), totalInterest.zero(), totalInterest.zero(), totalInterest.zero(), outstanding, loanApplicationTerms, periodNumberTemp, mc, mergeVariationsToMap(params), params.getCompoundingMap(), params.getPeriodStartDate(), params.getActualRepaymentDate(), applicableVariations); Money interest = principalInterestForThisPeriod.interest(); totalInterest = totalInterest.plus(interest); if (loanApplicationTerms.getInterestRecalculationCompoundingMethod() .isInterestCompoundingEnabled()) { LocalDate compoundingEffectiveDate = getNextCompoundScheduleDate( params.getActualRepaymentDate().minusDays(1), loanApplicationTerms, holidayDetailDTO); params.getLatePaymentMap().put(compoundingEffectiveDate, interest); } } params.setPeriodStartDate(params.getActualRepaymentDate()); } while (params.getActualRepaymentDate().isBefore(currentDate) && outstanding.isGreaterThanZero()); if (totalInterest.isGreaterThanZero()) { LoanScheduleModelRepaymentPeriod installment = LoanScheduleModelRepaymentPeriod.repayment( params.getInstalmentNumber(), startDate, params.getActualRepaymentDate(), totalInterest.zero(), totalInterest.zero(), totalInterest, totalInterest.zero(), totalInterest.zero(), totalInterest, true); params.incrementInstalmentNumber(); periods.add(installment); totalCumulativeInterest = totalCumulativeInterest.plus(totalInterest); } return totalCumulativeInterest; }
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))); }/*from w w w . ja v a 2 s .c om*/ final boolean applyInterestRecalculation = loanApplicationTerms.isInterestRecalculationEnabled(); LoanScheduleParams loanScheduleParams = null; Collection<LoanScheduleModelPeriod> periods = new ArrayList<>(); final List<LoanRepaymentScheduleInstallment> retainedInstallments = new ArrayList<>(); // this block is to retain the schedule installments prior to the // provided date and creates late and early payment details for further // calculations if (rescheduleFrom != null) { Money principalToBeScheduled = getPrincipalToBeScheduled(loanApplicationTerms); // actual outstanding balance for interest calculation Money outstandingBalance = principalToBeScheduled; // total outstanding balance as per rest for interest calculation. Money outstandingBalanceAsPerRest = outstandingBalance; // this is required to update total fee amounts in the // LoanScheduleModel final BigDecimal chargesDueAtTimeOfDisbursement = deriveTotalChargesDueAtTimeOfDisbursement( loanCharges); periods = createNewLoanScheduleListWithDisbursementDetails( loanApplicationTerms.fetchNumberOfRepaymentsAfterExceptions(), loanApplicationTerms, chargesDueAtTimeOfDisbursement); final List<LoanRepaymentScheduleInstallment> newRepaymentScheduleInstallments = new ArrayList<>(); MonetaryCurrency currency = outstandingBalance.getCurrency(); // early payments will be added here and as per the selected // strategy // action will be performed on this value Money reducePrincipal = outstandingBalanceAsPerRest.zero(); // principal changes will be added along with date(after applying // rest) // from when these amounts will effect the outstanding balance for // interest calculation final Map<LocalDate, Money> principalPortionMap = new HashMap<>(); // compounding(principal) amounts will be added along with // date(after applying compounding frequency) // from when these amounts will effect the outstanding balance for // interest calculation final Map<LocalDate, Money> latePaymentMap = new HashMap<>(); // compounding(interest/Fee) amounts will be added along with // date(after applying compounding frequency) // from when these amounts will effect the outstanding balance for // interest calculation final TreeMap<LocalDate, Money> compoundingMap = new TreeMap<>(); LocalDate currentDate = DateUtils.getLocalDateOfTenant(); LocalDate lastRestDate = currentDate; if (loanApplicationTerms.getRestCalendarInstance() != null) { lastRestDate = getNextRestScheduleDate(currentDate.minusDays(1), loanApplicationTerms, holidayDetailDTO); } LocalDate actualRepaymentDate = loanApplicationTerms.getExpectedDisbursementDate(); boolean isFirstRepayment = true; // cumulative fields Money totalCumulativePrincipal = principalToBeScheduled.zero(); Money totalCumulativeInterest = principalToBeScheduled.zero(); Money totalFeeChargesCharged = principalToBeScheduled.zero().plus(chargesDueAtTimeOfDisbursement); Money totalPenaltyChargesCharged = principalToBeScheduled.zero(); Money totalRepaymentExpected = principalToBeScheduled.zero(); // Actual period Number as per the schedule int periodNumber = 1; // Actual period Number plus interest only repayments int instalmentNumber = 1; LocalDate lastInstallmentDate = actualRepaymentDate; LocalDate periodStartDate = loanApplicationTerms.getExpectedDisbursementDate(); // Set fixed Amortization Amounts(either EMI or Principal ) updateAmortization(mc, loanApplicationTerms, periodNumber, outstandingBalance); final Map<LocalDate, Money> disburseDetailMap = new HashMap<>(); if (loanApplicationTerms.isMultiDisburseLoan()) { // fetches the first tranche amount and also updates other // tranche // details to map BigDecimal disburseAmt = getDisbursementAmount(loanApplicationTerms, loanApplicationTerms.getExpectedDisbursementDate(), periods, chargesDueAtTimeOfDisbursement, disburseDetailMap, true); outstandingBalance = outstandingBalance.zero().plus(disburseAmt); outstandingBalanceAsPerRest = outstandingBalance; principalToBeScheduled = principalToBeScheduled.zero().plus(disburseAmt); } int loanTermInDays = 0; // Block process the installment and creates the period if it falls // before reschedule from date // This will create the recalculation details by applying the // transactions for (LoanRepaymentScheduleInstallment installment : repaymentScheduleInstallments) { // this will generate the next schedule due date and allows to // process the installment only if recalculate from date is // greater than due date if (installment.getDueDate().isAfter(lastInstallmentDate)) { LocalDate previousRepaymentDate = actualRepaymentDate; actualRepaymentDate = this.scheduledDateGenerator.generateNextRepaymentDate(actualRepaymentDate, loanApplicationTerms, isFirstRepayment, holidayDetailDTO); isFirstRepayment = false; lastInstallmentDate = this.scheduledDateGenerator.adjustRepaymentDate(actualRepaymentDate, loanApplicationTerms, holidayDetailDTO); if (!lastInstallmentDate.isBefore(rescheduleFrom)) { actualRepaymentDate = previousRepaymentDate; break; } periodNumber++; // check for date changes while (loanApplicationTerms.getLoanTermVariations().hasDueDateVariation(lastInstallmentDate)) { LoanTermVariationsData variation = loanApplicationTerms.getLoanTermVariations() .nextDueDateVariation(); if (!variation.isSpecificToInstallment()) { actualRepaymentDate = variation.getDateValue(); } variation.setProcessed(true); } } for (Map.Entry<LocalDate, Money> disburseDetail : disburseDetailMap.entrySet()) { if (disburseDetail.getKey().isAfter(installment.getFromDate()) && !disburseDetail.getKey().isAfter(installment.getDueDate())) { // creates and add disbursement detail to the repayments // period final LoanScheduleModelDisbursementPeriod disbursementPeriod = LoanScheduleModelDisbursementPeriod .disbursement(disburseDetail.getKey(), disburseDetail.getValue(), chargesDueAtTimeOfDisbursement); periods.add(disbursementPeriod); // updates actual outstanding balance with new // disbursement detail outstandingBalance = outstandingBalance.plus(disburseDetail.getValue()); principalToBeScheduled = principalToBeScheduled.plus(disburseDetail.getValue()); } } // calculation of basic fields to start the schedule generation // from the middle periodStartDate = installment.getDueDate(); installment.resetDerivedComponents(); newRepaymentScheduleInstallments.add(installment); outstandingBalance = outstandingBalance.minus(installment.getPrincipal(currency)); final LoanScheduleModelPeriod loanScheduleModelPeriod = createLoanScheduleModelPeriod(installment, outstandingBalance); periods.add(loanScheduleModelPeriod); totalCumulativePrincipal = totalCumulativePrincipal.plus(installment.getPrincipal(currency)); totalCumulativeInterest = totalCumulativeInterest.plus(installment.getInterestCharged(currency)); totalFeeChargesCharged = totalFeeChargesCharged.plus(installment.getFeeChargesCharged(currency)); totalPenaltyChargesCharged = totalPenaltyChargesCharged .plus(installment.getPenaltyChargesCharged(currency)); instalmentNumber++; loanTermInDays = Days.daysBetween(installment.getFromDate(), installment.getDueDate()).getDays(); // populates the collection with transactions till the due date // of // the period for interest recalculation enabled loans Collection<RecalculationDetail> applicableTransactions = getApplicableTransactionsForPeriod( applyInterestRecalculation, installment.getDueDate(), recalculationDetails); // calculates the expected principal value for this repayment // schedule Money principalPortionCalculated = principalToBeScheduled.zero(); if (!installment.isRecalculatedInterestComponent()) { principalPortionCalculated = calculateExpectedPrincipalPortion( installment.getInterestCharged(currency), loanApplicationTerms); } // expected principal considering the previously paid excess // amount Money actualPrincipalPortion = principalPortionCalculated.minus(reducePrincipal); if (actualPrincipalPortion.isLessThanZero()) { actualPrincipalPortion = principalPortionCalculated.zero(); } Money unprocessed = updateEarlyPaidAmountsToMap(loanApplicationTerms, holidayDetailDTO, loanRepaymentScheduleTransactionProcessor, newRepaymentScheduleInstallments, currency, principalPortionMap, installment, applicableTransactions, actualPrincipalPortion); // this block is to adjust the period number based on the actual // schedule due date and installment due date // recalculatedInterestComponent installment shouldn't be // considered while calculating fixed EMI amounts int period = periodNumber; if (!lastInstallmentDate.isEqual(installment.getDueDate())) { period--; } reducePrincipal = fetchEarlyPaidAmount(installment.getPrincipal(currency), principalPortionCalculated, reducePrincipal, loanApplicationTerms, totalCumulativePrincipal, period, mc); // Updates principal paid map with efective date for reducing // the amount from outstanding balance(interest calculation) LocalDate amountApplicableDate = getNextRestScheduleDate(installment.getDueDate().minusDays(1), loanApplicationTerms, holidayDetailDTO); // updates map with the installment principal amount excluding // unprocessed amount since this amount is already accounted. updateMapWithAmount(principalPortionMap, installment.getPrincipal(currency).minus(unprocessed), amountApplicableDate); // update outstanding balance for interest calculation outstandingBalanceAsPerRest = updateBalanceForInterestCalculation(principalPortionMap, installment.getDueDate(), outstandingBalanceAsPerRest, false); outstandingBalanceAsPerRest = updateBalanceForInterestCalculation(disburseDetailMap, installment.getDueDate(), outstandingBalanceAsPerRest, true); } totalRepaymentExpected = totalCumulativePrincipal.plus(totalCumulativeInterest) .plus(totalFeeChargesCharged).plus(totalPenaltyChargesCharged); // updates the map with over due amounts updateLatePaymentsToMap(loanApplicationTerms, holidayDetailDTO, currency, latePaymentMap, lastInstallmentDate, newRepaymentScheduleInstallments, true, lastRestDate, compoundingMap); // for partial schedule generation if (!newRepaymentScheduleInstallments.isEmpty() && totalCumulativeInterest.isGreaterThanZero()) { Money totalOutstandingInterestPaymentDueToGrace = Money.zero(currency); loanScheduleParams = LoanScheduleParams.createLoanScheduleParamsForPartialUpdate(periodNumber, instalmentNumber, loanTermInDays, periodStartDate, actualRepaymentDate, totalCumulativePrincipal, totalCumulativeInterest, totalFeeChargesCharged, totalPenaltyChargesCharged, totalRepaymentExpected, totalOutstandingInterestPaymentDueToGrace, reducePrincipal, principalPortionMap, latePaymentMap, compoundingMap, disburseDetailMap, principalToBeScheduled, outstandingBalance, outstandingBalanceAsPerRest, newRepaymentScheduleInstallments, recalculationDetails, loanRepaymentScheduleTransactionProcessor, scheduleTillDate, currency, applyInterestRecalculation); retainedInstallments.addAll(newRepaymentScheduleInstallments); } } // for complete schedule generation if (loanScheduleParams == null) { loanScheduleParams = LoanScheduleParams.createLoanScheduleParamsForCompleteUpdate(recalculationDetails, loanRepaymentScheduleTransactionProcessor, scheduleTillDate, applyInterestRecalculation); } LoanScheduleModel loanScheduleModel = generate(mc, loanApplicationTerms, loanCharges, holidayDetailDTO, loanScheduleParams); for (LoanScheduleModelPeriod loanScheduleModelPeriod : loanScheduleModel.getPeriods()) { if (loanScheduleModelPeriod.isRepaymentPeriod()) { // adding newly created repayment periods to installments addLoanRepaymentScheduleInstallment(retainedInstallments, loanScheduleModelPeriod); } } periods.addAll(loanScheduleModel.getPeriods()); LoanScheduleModel loanScheduleModelwithPeriodChanges = LoanScheduleModel .withLoanScheduleModelPeriods(periods, loanScheduleModel); return LoanScheduleDTO.from(retainedInstallments, loanScheduleModelwithPeriodChanges); }
From source file:org.apache.fineract.portfolio.loanaccount.loanschedule.domain.AbstractLoanScheduleGenerator.java
License:Apache License
/** * Method returns the amount payable to close the loan account as of today. *///from w ww.j av a 2 s.co m @Override public LoanRepaymentScheduleInstallment calculatePrepaymentAmount(final MonetaryCurrency currency, final LocalDate onDate, final LoanApplicationTerms loanApplicationTerms, final MathContext mc, final Set<LoanCharge> charges, final HolidayDetailDTO holidayDetailDTO, final List<LoanTransaction> loanTransactions, final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor, final List<LoanRepaymentScheduleInstallment> repaymentScheduleInstallments) { LocalDate calculateTill = onDate; if (loanApplicationTerms.getPreClosureInterestCalculationStrategy().calculateTillRestFrequencyEnabled()) { calculateTill = getNextRestScheduleDate(onDate.minusDays(1), loanApplicationTerms, holidayDetailDTO); } LoanScheduleDTO loanScheduleDTO = rescheduleNextInstallments(mc, loanApplicationTerms, charges, holidayDetailDTO, loanTransactions, loanRepaymentScheduleTransactionProcessor, repaymentScheduleInstallments, onDate, calculateTill); loanRepaymentScheduleTransactionProcessor.handleTransaction( loanApplicationTerms.getExpectedDisbursementDate(), loanTransactions, currency, loanScheduleDTO.getInstallments(), charges); Money feeCharges = Money.zero(currency); Money penaltyCharges = Money.zero(currency); Money totalPrincipal = Money.zero(currency); Money totalInterest = Money.zero(currency); for (final LoanRepaymentScheduleInstallment currentInstallment : loanScheduleDTO.getInstallments()) { if (currentInstallment.isNotFullyPaidOff()) { totalPrincipal = totalPrincipal.plus(currentInstallment.getPrincipalOutstanding(currency)); totalInterest = totalInterest.plus(currentInstallment.getInterestOutstanding(currency)); feeCharges = feeCharges.plus(currentInstallment.getFeeChargesOutstanding(currency)); penaltyCharges = penaltyCharges.plus(currentInstallment.getPenaltyChargesOutstanding(currency)); } } return new LoanRepaymentScheduleInstallment(null, 0, onDate, onDate, totalPrincipal.getAmount(), totalInterest.getAmount(), feeCharges.getAmount(), penaltyCharges.getAmount(), false); }
From source file:org.apache.fineract.portfolio.loanaccount.loanschedule.domain.DefaultScheduledDateGenerator.java
License:Apache License
@Override public LocalDate idealDisbursementDateBasedOnFirstRepaymentDate( final PeriodFrequencyType repaymentPeriodFrequencyType, final int repaidEvery, final LocalDate firstRepaymentDate) { LocalDate idealDisbursementDate = null; switch (repaymentPeriodFrequencyType) { case DAYS:/*from www . j a v a2 s . c om*/ idealDisbursementDate = firstRepaymentDate.minusDays(repaidEvery); break; case WEEKS: idealDisbursementDate = firstRepaymentDate.minusWeeks(repaidEvery); break; case MONTHS: idealDisbursementDate = firstRepaymentDate.minusMonths(repaidEvery); break; case YEARS: idealDisbursementDate = firstRepaymentDate.minusYears(repaidEvery); break; case INVALID: break; } return idealDisbursementDate; }
From source file:org.apache.fineract.portfolio.savings.domain.FixedDepositAccount.java
License:Apache License
public void postPreMaturityInterest(final LocalDate accountCloseDate, final boolean isPreMatureClosure, final boolean isSavingsInterestPostingAtCurrentPeriodEnd, final Integer financialYearBeginningMonth) { Money interestPostedToDate = totalInterestPosted(); // calculate interest before one day of closure date final LocalDate interestCalculatedToDate = accountCloseDate.minusDays(1); final Money interestOnMaturity = calculatePreMatureInterest(interestCalculatedToDate, retreiveOrderedNonInterestPostingTransactions(), isPreMatureClosure, isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth); boolean recalucateDailyBalance = false; // post remaining interest final Money remainigInterestToBePosted = interestOnMaturity.minus(interestPostedToDate); if (!remainigInterestToBePosted.isZero()) { final SavingsAccountTransaction newPostingTransaction = SavingsAccountTransaction.interestPosting(this, office(), accountCloseDate, remainigInterestToBePosted); this.transactions.add(newPostingTransaction); recalucateDailyBalance = true;/*from ww w. j av a 2s . c om*/ } if (recalucateDailyBalance) { // update existing transactions so derived balance fields are // correct. recalculateDailyBalances(Money.zero(this.currency), accountCloseDate); } this.summary.updateSummary(this.currency, this.savingsAccountTransactionSummaryWrapper, this.transactions); this.accountTermAndPreClosure.updateMaturityDetails(this.getAccountBalance(), accountCloseDate); }
From source file:org.apache.fineract.portfolio.savings.domain.RecurringDepositAccount.java
License:Apache License
public void postMaturityInterest(final boolean isSavingsInterestPostingAtCurrentPeriodEnd, final Integer financialYearBeginningMonth, final LocalDate closeDate) { LocalDate interestPostingUpToDate = maturityDate(); if (interestPostingUpToDate == null) { interestPostingUpToDate = closeDate; }/*from w w w . j a va 2s . c o m*/ final MathContext mc = MathContext.DECIMAL64; boolean isInterestTransfer = false; final List<PostingPeriod> postingPeriods = calculateInterestUsing(mc, interestPostingUpToDate.minusDays(1), 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 { 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); }
From source file:org.apache.fineract.portfolio.savings.domain.RecurringDepositAccount.java
License:Apache License
public void postPreMaturityInterest(final LocalDate accountCloseDate, final boolean isPreMatureClosure, final boolean isSavingsInterestPostingAtCurrentPeriodEnd, final Integer financialYearBeginningMonth) { final Money interestPostedToDate = totalInterestPosted(); // calculate interest before one day of closure date final LocalDate interestCalculatedToDate = accountCloseDate.minusDays(1); final Money interestOnMaturity = calculatePreMatureInterest(interestCalculatedToDate, retreiveOrderedNonInterestPostingTransactions(), isPreMatureClosure, isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth); boolean recalucateDailyBalance = false; // post remaining interest final Money remainigInterestToBePosted = interestOnMaturity.minus(interestPostedToDate); if (!remainigInterestToBePosted.isZero()) { final SavingsAccountTransaction newPostingTransaction = SavingsAccountTransaction.interestPosting(this, office(), accountCloseDate, remainigInterestToBePosted); this.transactions.add(newPostingTransaction); recalucateDailyBalance = true;/*from w w w. j av a 2s. c o m*/ } if (recalucateDailyBalance) { // update existing transactions so derived balance fields are // correct. recalculateDailyBalances(Money.zero(this.currency), accountCloseDate); } this.summary.updateSummary(this.currency, this.savingsAccountTransactionSummaryWrapper, this.transactions); this.accountTermAndPreClosure.updateMaturityDetails(this.getAccountBalance(), accountCloseDate); }
From source file:org.apache.fineract.portfolio.savings.domain.SavingsHelper.java
License:Apache License
public List<LocalDateInterval> determineInterestPostingPeriods( final LocalDate startInterestCalculationLocalDate, final LocalDate interestPostingUpToDate, final SavingsPostingInterestPeriodType postingPeriodType, final Integer financialYearBeginningMonth) { final List<LocalDateInterval> postingPeriods = new ArrayList<>(); LocalDate periodStartDate = startInterestCalculationLocalDate; LocalDate periodEndDate = periodStartDate; while (!periodStartDate.isAfter(interestPostingUpToDate) && !periodEndDate.isAfter(interestPostingUpToDate)) { final LocalDate interestPostingLocalDate = determineInterestPostingPeriodEndDateFrom(periodStartDate, postingPeriodType, interestPostingUpToDate, financialYearBeginningMonth); periodEndDate = interestPostingLocalDate.minusDays(1); postingPeriods.add(LocalDateInterval.create(periodStartDate, periodEndDate)); periodEndDate = interestPostingLocalDate; periodStartDate = interestPostingLocalDate; }// w ww . j a v a2s .c om return postingPeriods; }