List of usage examples for org.joda.time LocalDate isAfter
public boolean isAfter(ReadablePartial partial)
From source file:org.apache.fineract.portfolio.loanaccount.domain.Loan.java
License:Apache License
private ChangedTransactionDetail handleRefundTransaction(final LoanTransaction loanTransaction, final LoanLifecycleStateMachine loanLifecycleStateMachine, final LoanTransaction adjustedTransaction) { ChangedTransactionDetail changedTransactionDetail = null; final LoanStatus statusEnum = loanLifecycleStateMachine.transition(LoanEvent.LOAN_REFUND, LoanStatus.fromInt(this.loanStatus)); this.loanStatus = statusEnum.getValue(); loanTransaction.updateLoan(this); // final boolean isTransactionChronologicallyLatest = // isChronologicallyLatestRefund(loanTransaction, // this.loanTransactions); if (status().isOverpaid() || status().isClosed()) { final String errorMessage = "This refund option is only for active loans "; throw new InvalidLoanStateTransitionException("transaction", "is.exceeding.overpaid.amount", errorMessage, this.totalOverpaid, loanTransaction.getAmount(getCurrency()).getAmount()); } else if (this.getTotalPaidInRepayments().isZero()) { final String errorMessage = "Cannot refund when no payment has been made"; throw new InvalidLoanStateTransitionException("transaction", "no.payment.yet.made.for.loan", errorMessage);/*from w ww .j a v a 2 s.com*/ } if (loanTransaction.isNotZero(loanCurrency())) { this.loanTransactions.add(loanTransaction); } if (loanTransaction.isNotRefundForActiveLoan()) { final String errorMessage = "A transaction of type refund was expected but not received."; throw new InvalidLoanTransactionTypeException("transaction", "is.not.a.refund.transaction", errorMessage); } final LocalDate loanTransactionDate = loanTransaction.getTransactionDate(); if (loanTransactionDate.isBefore(getDisbursementDate())) { final String errorMessage = "The transaction date cannot be before the loan disbursement date: " + getApprovedOnDate().toString(); throw new InvalidLoanStateTransitionException("transaction", "cannot.be.before.disbursement.date", errorMessage, loanTransactionDate, getDisbursementDate()); } if (loanTransactionDate.isAfter(DateUtils.getLocalDateOfTenant())) { final String errorMessage = "The transaction date cannot be in the future."; throw new InvalidLoanStateTransitionException("transaction", "cannot.be.a.future.date", errorMessage, loanTransactionDate); } if (this.loanProduct.isMultiDisburseLoan() && adjustedTransaction == null) { BigDecimal totalDisbursed = getDisbursedAmount(); if (totalDisbursed.compareTo(this.summary.getTotalPrincipalRepaid()) < 0) { final String errorMessage = "The transaction cannot be done before the loan disbursement: " + getApprovedOnDate().toString(); throw new InvalidLoanStateTransitionException("transaction", "cannot.be.done.before.disbursement", errorMessage); } } final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory .determineProcessor(this.transactionProcessingStrategy); // If is a refund if (adjustedTransaction == null) { loanRepaymentScheduleTransactionProcessor.handleRefund(loanTransaction, getCurrency(), this.repaymentScheduleInstallments, charges()); } else { final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retreiveListOfTransactionsPostDisbursement(); changedTransactionDetail = loanRepaymentScheduleTransactionProcessor.handleTransaction( getDisbursementDate(), allNonContraTransactionsPostDisbursement, getCurrency(), this.repaymentScheduleInstallments, charges()); for (final Map.Entry<Long, LoanTransaction> mapEntry : changedTransactionDetail .getNewTransactionMappings().entrySet()) { mapEntry.getValue().updateLoan(this); } } updateLoanSummaryDerivedFields(); doPostLoanTransactionChecks(loanTransaction.getTransactionDate(), loanLifecycleStateMachine); return changedTransactionDetail; }
From source file:org.apache.fineract.portfolio.loanaccount.domain.LoanAccountDomainServiceJpa.java
License:Apache License
@Override public void recalculateAccruals(Loan loan) { LocalDate accruedTill = loan.getAccruedTill(); if (!loan.isPeriodicAccrualAccountingEnabledOnLoanProduct() || !loan.repaymentScheduleDetail().isInterestRecalculationEnabled() || accruedTill == null || loan.isNpa() || !loan.status().isActive()) { return;/* w w w .j ava 2s . co m*/ } Collection<LoanScheduleAccrualData> loanScheduleAccrualDatas = new ArrayList<>(); List<LoanRepaymentScheduleInstallment> installments = loan.fetchRepaymentScheduleInstallments(); Long loanId = loan.getId(); Long officeId = loan.getOfficeId(); LocalDate accrualStartDate = null; PeriodFrequencyType repaymentFrequency = loan.repaymentScheduleDetail().getRepaymentPeriodFrequencyType(); Integer repayEvery = loan.repaymentScheduleDetail().getRepayEvery(); LocalDate interestCalculatedFrom = loan.getInterestChargedFromDate(); Long loanProductId = loan.productId(); MonetaryCurrency currency = loan.getCurrency(); ApplicationCurrency applicationCurrency = this.applicationCurrencyRepository .findOneWithNotFoundDetection(currency); CurrencyData currencyData = applicationCurrency.toData(); Set<LoanCharge> loanCharges = loan.charges(); for (LoanRepaymentScheduleInstallment installment : installments) { if (!accruedTill.isBefore(installment.getDueDate()) || (accruedTill.isAfter(installment.getFromDate()) && !accruedTill.isAfter(installment.getDueDate()))) { BigDecimal dueDateFeeIncome = BigDecimal.ZERO; BigDecimal dueDatePenaltyIncome = BigDecimal.ZERO; LocalDate chargesTillDate = installment.getDueDate(); if (!accruedTill.isAfter(installment.getDueDate())) { chargesTillDate = accruedTill; } for (final LoanCharge loanCharge : loanCharges) { if (loanCharge.isDueForCollectionFromAndUpToAndIncluding(installment.getFromDate(), chargesTillDate)) { if (loanCharge.isFeeCharge()) { dueDateFeeIncome = dueDateFeeIncome.add(loanCharge.amount()); } else if (loanCharge.isPenaltyCharge()) { dueDatePenaltyIncome = dueDatePenaltyIncome.add(loanCharge.amount()); } } } LoanScheduleAccrualData accrualData = new LoanScheduleAccrualData(loanId, officeId, installment.getInstallmentNumber(), accrualStartDate, repaymentFrequency, repayEvery, installment.getDueDate(), installment.getFromDate(), installment.getId(), loanProductId, installment.getInterestCharged(currency).getAmount(), installment.getFeeChargesCharged(currency).getAmount(), installment.getPenaltyChargesCharged(currency).getAmount(), installment.getInterestAccrued(currency).getAmount(), installment.getFeeAccrued(currency).getAmount(), installment.getPenaltyAccrued(currency).getAmount(), currencyData, interestCalculatedFrom, installment.getInterestWaived(currency).getAmount()); loanScheduleAccrualDatas.add(accrualData); } } if (!loanScheduleAccrualDatas.isEmpty()) { String error = this.loanAccrualPlatformService.addPeriodicAccruals(accruedTill, loanScheduleAccrualDatas); if (error.length() > 0) { String globalisationMessageCode = "error.msg.accrual.exception"; throw new GeneralPlatformDomainRuleException(globalisationMessageCode, error, error); } } }
From source file:org.apache.fineract.portfolio.loanaccount.loanschedule.domain.AbstractLoanScheduleGenerator.java
License:Apache License
private LoanScheduleModel generate(final MathContext mc, final LoanApplicationTerms loanApplicationTerms, final Set<LoanCharge> loanCharges, final HolidayDetailDTO holidayDetailDTO, final LoanScheduleParams loanScheduleParams) { final ApplicationCurrency applicationCurrency = loanApplicationTerms.getApplicationCurrency(); // generate list of proposed schedule due dates LocalDate loanEndDate = this.scheduledDateGenerator.getLastRepaymentDate(loanApplicationTerms, holidayDetailDTO);//from w w w.ja v a 2 s .co m LoanTermVariationsData lastDueDateVariation = loanApplicationTerms.getLoanTermVariations() .fetchLoanTermDueDateVariationsData(loanEndDate); if (lastDueDateVariation != null) { loanEndDate = lastDueDateVariation.getDateValue(); } loanApplicationTerms.updateLoanEndDate(loanEndDate); // determine the total charges due at time of disbursement final BigDecimal chargesDueAtTimeOfDisbursement = deriveTotalChargesDueAtTimeOfDisbursement(loanCharges); // setup variables for tracking important facts required for loan // schedule generation. final MonetaryCurrency currency = loanApplicationTerms.getCurrency(); final int numberOfRepayments = loanApplicationTerms.fetchNumberOfRepaymentsAfterExceptions(); LoanScheduleParams scheduleParams = null; if (loanScheduleParams == null) { scheduleParams = LoanScheduleParams.createLoanScheduleParams(currency, Money.of(currency, chargesDueAtTimeOfDisbursement), loanApplicationTerms.getExpectedDisbursementDate(), getPrincipalToBeScheduled(loanApplicationTerms)); } else if (!loanScheduleParams.isPartialUpdate()) { scheduleParams = LoanScheduleParams.createLoanScheduleParams(currency, Money.of(currency, chargesDueAtTimeOfDisbursement), loanApplicationTerms.getExpectedDisbursementDate(), getPrincipalToBeScheduled(loanApplicationTerms), loanScheduleParams); } else { scheduleParams = loanScheduleParams; } final Collection<RecalculationDetail> transactions = scheduleParams.getRecalculationDetails(); final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = scheduleParams .getLoanRepaymentScheduleTransactionProcessor(); final Collection<LoanScheduleModelPeriod> periods = createNewLoanScheduleListWithDisbursementDetails( numberOfRepayments, loanApplicationTerms, chargesDueAtTimeOfDisbursement); // Determine the total interest owed over the full loan for FLAT // interest method . final Money totalInterestChargedForFullLoanTerm = loanApplicationTerms .calculateTotalInterestCharged(this.paymentPeriodsInOneYearCalculator, mc); boolean isFirstRepayment = true; LocalDate firstRepaymentdate = this.scheduledDateGenerator.generateNextRepaymentDate( loanApplicationTerms.getExpectedDisbursementDate(), loanApplicationTerms, isFirstRepayment, holidayDetailDTO); final LocalDate idealDisbursementDate = this.scheduledDateGenerator .idealDisbursementDateBasedOnFirstRepaymentDate( loanApplicationTerms.getLoanTermPeriodFrequencyType(), loanApplicationTerms.getRepaymentEvery(), firstRepaymentdate); if (!scheduleParams.isPartialUpdate()) { // Set Fixed Principal Amount updateAmortization(mc, loanApplicationTerms, scheduleParams.getPeriodNumber(), scheduleParams.getOutstandingBalance()); if (loanApplicationTerms.isMultiDisburseLoan()) { // fetches the first tranche amount and also updates other // tranche // details to map BigDecimal disburseAmt = getDisbursementAmount(loanApplicationTerms, scheduleParams.getPeriodStartDate(), periods, chargesDueAtTimeOfDisbursement, scheduleParams.getDisburseDetailMap(), scheduleParams.applyInterestRecalculation()); scheduleParams.setPrincipalToBeScheduled(Money.of(currency, disburseAmt)); loanApplicationTerms.setPrincipal(loanApplicationTerms.getPrincipal().zero().plus(disburseAmt)); scheduleParams.setOutstandingBalance(Money.of(currency, disburseAmt)); scheduleParams.setOutstandingBalanceAsPerRest(Money.of(currency, disburseAmt)); } } // charges which depends on total loan interest will be added to this // set and handled separately after all installments generated final Set<LoanCharge> nonCompoundingCharges = seperateTotalCompoundingPercentageCharges(loanCharges); LocalDate currentDate = DateUtils.getLocalDateOfTenant(); LocalDate lastRestDate = currentDate; if (loanApplicationTerms.getRestCalendarInstance() != null) { lastRestDate = getNextRestScheduleDate(currentDate.minusDays(1), loanApplicationTerms, holidayDetailDTO); } boolean isNextRepaymentAvailable = true; Boolean extendTermForDailyRepayments = false; if (holidayDetailDTO.getWorkingDays().getExtendTermForDailyRepayments() == true && loanApplicationTerms.getRepaymentPeriodFrequencyType() == PeriodFrequencyType.DAYS && loanApplicationTerms.getRepaymentEvery() == 1) { holidayDetailDTO.getWorkingDays() .setRepaymentReschedulingType(RepaymentRescheduleType.MOVE_TO_NEXT_WORKING_DAY.getValue()); extendTermForDailyRepayments = true; } final Collection<LoanTermVariationsData> interestRates = loanApplicationTerms.getLoanTermVariations() .getInterestRateChanges(); // this block is to start the schedule generation from specified date if (scheduleParams.isPartialUpdate()) { if (loanApplicationTerms.isMultiDisburseLoan()) { loanApplicationTerms.setPrincipal(scheduleParams.getPrincipalToBeScheduled()); } applyLoanVariationsForPartialScheduleGenerate(loanApplicationTerms, scheduleParams, interestRates); isFirstRepayment = false; } while (!scheduleParams.getOutstandingBalance().isZero() || !scheduleParams.getDisburseDetailMap().isEmpty()) { LocalDate previousRepaymentDate = scheduleParams.getActualRepaymentDate(); scheduleParams.setActualRepaymentDate( this.scheduledDateGenerator.generateNextRepaymentDate(scheduleParams.getActualRepaymentDate(), loanApplicationTerms, isFirstRepayment, holidayDetailDTO)); isFirstRepayment = false; LocalDate scheduledDueDate = this.scheduledDateGenerator.adjustRepaymentDate( scheduleParams.getActualRepaymentDate(), loanApplicationTerms, holidayDetailDTO); // calculated interest start date for the period LocalDate periodStartDateApplicableForInterest = calculateInterestStartDateForPeriod( loanApplicationTerms, scheduleParams.getPeriodStartDate(), idealDisbursementDate, firstRepaymentdate); // Loan Schedule Exceptions that need to be applied for Loan Account LoanTermVariationParams termVariationParams = applyLoanTermVariations(loanApplicationTerms, scheduleParams, previousRepaymentDate, scheduledDueDate); scheduledDueDate = termVariationParams.getScheduledDueDate(); // Updates total days in term scheduleParams.addLoanTermInDays( Days.daysBetween(scheduleParams.getPeriodStartDate(), scheduledDueDate).getDays()); if (termVariationParams.isSkipPeriod()) { continue; } if (scheduleParams.getPeriodStartDate().isAfter(scheduledDueDate)) { throw new ScheduleDateException("Due date can't be before period start date", scheduledDueDate); } if (!scheduleParams.getLatePaymentMap().isEmpty()) { populateCompoundingDatesInPeriod(scheduleParams.getPeriodStartDate(), scheduledDueDate, currentDate, loanApplicationTerms, holidayDetailDTO, scheduleParams.getCompoundingMap(), loanCharges, currency); scheduleParams.getCompoundingDateVariations().put(scheduleParams.getPeriodStartDate(), new TreeMap<>(scheduleParams.getCompoundingMap())); } if (extendTermForDailyRepayments) { scheduleParams.setActualRepaymentDate(scheduledDueDate); } // this block is to generate the schedule till the specified // date(used for calculating preclosure) if (scheduleParams.getScheduleTillDate() != null && !scheduledDueDate.isBefore(scheduleParams.getScheduleTillDate())) { scheduledDueDate = scheduleParams.getScheduleTillDate(); isNextRepaymentAvailable = false; } // populates the collection with transactions till the due date of // the period for interest recalculation enabled loans Collection<RecalculationDetail> applicableTransactions = getApplicableTransactionsForPeriod( scheduleParams.applyInterestRecalculation(), scheduledDueDate, transactions); final double interestCalculationGraceOnRepaymentPeriodFraction = this.paymentPeriodsInOneYearCalculator .calculatePortionOfRepaymentPeriodInterestChargingGrace(periodStartDateApplicableForInterest, scheduledDueDate, loanApplicationTerms.getInterestChargedFromLocalDate(), loanApplicationTerms.getLoanTermPeriodFrequencyType(), loanApplicationTerms.getRepaymentEvery()); ScheduleCurrentPeriodParams currentPeriodParams = new ScheduleCurrentPeriodParams(currency, interestCalculationGraceOnRepaymentPeriodFraction); if (loanApplicationTerms.isMultiDisburseLoan()) { updateBalanceBasedOnDisbursement(loanApplicationTerms, chargesDueAtTimeOfDisbursement, scheduleParams, periods, scheduledDueDate); } // process repayments to the schedule as per the repayment // transaction processor configuration // will add a new schedule with interest till the transaction date // for a loan repayment which falls between the // two periods for interest first repayment strategies handleRecalculationForNonDueDateTransactions(mc, loanApplicationTerms, loanCharges, holidayDetailDTO, scheduleParams, periods, totalInterestChargedForFullLoanTerm, idealDisbursementDate, firstRepaymentdate, lastRestDate, scheduledDueDate, periodStartDateApplicableForInterest, applicableTransactions, currentPeriodParams); if (currentPeriodParams.isSkipCurrentLoop()) { continue; } periodStartDateApplicableForInterest = calculateInterestStartDateForPeriod(loanApplicationTerms, scheduleParams.getPeriodStartDate(), idealDisbursementDate, firstRepaymentdate); // backup for pre-close transaction updateCompoundingDetails(scheduleParams, periodStartDateApplicableForInterest); // 5 determine principal,interest of repayment period PrincipalInterest principalInterestForThisPeriod = calculatePrincipalInterestComponentsForPeriod( this.paymentPeriodsInOneYearCalculator, currentPeriodParams.getInterestCalculationGraceOnRepaymentPeriodFraction(), scheduleParams.getTotalCumulativePrincipal().minus(scheduleParams.getReducePrincipal()), scheduleParams.getTotalCumulativeInterest(), totalInterestChargedForFullLoanTerm, scheduleParams.getTotalOutstandingInterestPaymentDueToGrace(), scheduleParams.getOutstandingBalanceAsPerRest(), loanApplicationTerms, scheduleParams.getPeriodNumber(), mc, mergeVariationsToMap(scheduleParams), scheduleParams.getCompoundingMap(), periodStartDateApplicableForInterest, scheduledDueDate, interestRates); // will check for EMI amount greater than interest calculated if (loanApplicationTerms.getFixedEmiAmount() != null && loanApplicationTerms.getFixedEmiAmount() .compareTo(principalInterestForThisPeriod.interest().getAmount()) == -1) { String errorMsg = "EMI amount must be greater than : " + principalInterestForThisPeriod.interest().getAmount(); throw new MultiDisbursementEmiAmountException(errorMsg, principalInterestForThisPeriod.interest().getAmount(), loanApplicationTerms.getFixedEmiAmount()); } // update cumulative fields for principal & interest currentPeriodParams.setInterestForThisPeriod(principalInterestForThisPeriod.interest()); Money lastTotalOutstandingInterestPaymentDueToGrace = scheduleParams .getTotalOutstandingInterestPaymentDueToGrace(); scheduleParams.setTotalOutstandingInterestPaymentDueToGrace( principalInterestForThisPeriod.interestPaymentDueToGrace()); currentPeriodParams.setPrincipalForThisPeriod(principalInterestForThisPeriod.principal()); // applies early payments on principal portion updatePrincipalPortionBasedOnPreviousEarlyPayments(currency, scheduleParams, currentPeriodParams); // updates amounts with current earlyPaidAmount updateAmountsBasedOnCurrentEarlyPayments(mc, loanApplicationTerms, scheduleParams, currentPeriodParams); if (scheduleParams.getOutstandingBalance().isLessThanZero() || !isNextRepaymentAvailable) { currentPeriodParams.plusPrincipalForThisPeriod(scheduleParams.getOutstandingBalance()); scheduleParams.setOutstandingBalance(Money.zero(currency)); } if (!isNextRepaymentAvailable) { scheduleParams.getDisburseDetailMap().clear(); } // applies charges for the period applyChargesForCurrentPeriod(loanCharges, currency, scheduleParams, scheduledDueDate, currentPeriodParams); // sum up real totalInstallmentDue from components final Money totalInstallmentDue = currentPeriodParams.fetchTotalAmountForPeriod(); // if previous installment is last then add interest to same // installment if (currentPeriodParams.getLastInstallment() != null && currentPeriodParams.getPrincipalForThisPeriod().isZero()) { currentPeriodParams.getLastInstallment() .addInterestAmount(currentPeriodParams.getInterestForThisPeriod()); continue; } // create repayment period from parts LoanScheduleModelPeriod installment = LoanScheduleModelRepaymentPeriod.repayment( scheduleParams.getInstalmentNumber(), scheduleParams.getPeriodStartDate(), scheduledDueDate, currentPeriodParams.getPrincipalForThisPeriod(), scheduleParams.getOutstandingBalance(), currentPeriodParams.getInterestForThisPeriod(), currentPeriodParams.getFeeChargesForInstallment(), currentPeriodParams.getPenaltyChargesForInstallment(), totalInstallmentDue, false); // apply loan transactions on installments to identify early/late // payments for interest recalculation installment = handleRecalculationForTransactions(mc, loanApplicationTerms, holidayDetailDTO, currency, scheduleParams, loanRepaymentScheduleTransactionProcessor, totalInterestChargedForFullLoanTerm, lastRestDate, scheduledDueDate, periodStartDateApplicableForInterest, applicableTransactions, currentPeriodParams, lastTotalOutstandingInterestPaymentDueToGrace, installment, loanCharges); periods.add(installment); // Updates principal paid map with efective date for reducing // the amount from outstanding balance(interest calculation) updateAmountsWithEffectiveDate(loanApplicationTerms, holidayDetailDTO, scheduleParams, scheduledDueDate, currentPeriodParams, installment); // handle cumulative fields scheduleParams.addTotalCumulativePrincipal(currentPeriodParams.getPrincipalForThisPeriod()); scheduleParams.addTotalRepaymentExpected(totalInstallmentDue); scheduleParams.addTotalCumulativeInterest(currentPeriodParams.getInterestForThisPeriod()); scheduleParams.setPeriodStartDate(scheduledDueDate); scheduleParams.incrementInstalmentNumber(); scheduleParams.incrementPeriodNumber(); scheduleParams.getCompoundingDateVariations().clear(); if (termVariationParams.isRecalculateAmounts()) { loanApplicationTerms.setCurrentPeriodFixedEmiAmount(null); loanApplicationTerms.setCurrentPeriodFixedPrincipalAmount(null); adjustInstallmentOrPrincipalAmount(loanApplicationTerms, scheduleParams.getTotalCumulativePrincipal(), scheduleParams.getPeriodNumber(), mc); } } // this condition is to add the interest from grace period if not // already applied. if (scheduleParams.getTotalOutstandingInterestPaymentDueToGrace().isGreaterThanZero()) { LoanScheduleModelPeriod installment = ((List<LoanScheduleModelPeriod>) periods).get(periods.size() - 1); installment.addInterestAmount(scheduleParams.getTotalOutstandingInterestPaymentDueToGrace()); scheduleParams.addTotalRepaymentExpected(scheduleParams.getTotalOutstandingInterestPaymentDueToGrace()); scheduleParams .addTotalCumulativeInterest(scheduleParams.getTotalOutstandingInterestPaymentDueToGrace()); scheduleParams.setTotalOutstandingInterestPaymentDueToGrace(Money.zero(currency)); } // determine fees and penalties for charges which depends on total // loan interest updatePeriodsWithCharges(currency, scheduleParams, periods, nonCompoundingCharges); // this block is to add extra re-payment schedules with interest portion // if the loan not paid with in loan term if (scheduleParams.getScheduleTillDate() != null) { currentDate = scheduleParams.getScheduleTillDate(); } if (scheduleParams.applyInterestRecalculation() && scheduleParams.getLatePaymentMap().size() > 0 && currentDate.isAfter(scheduleParams.getPeriodStartDate())) { Money totalInterest = addInterestOnlyRepaymentScheduleForCurrentdate(mc, loanApplicationTerms, holidayDetailDTO, currency, periods, currentDate, loanRepaymentScheduleTransactionProcessor, transactions, loanCharges, scheduleParams); scheduleParams.addTotalCumulativeInterest(totalInterest); } loanApplicationTerms.resetFixedEmiAmount(); final BigDecimal totalPrincipalPaid = BigDecimal.ZERO; final BigDecimal totalOutstanding = BigDecimal.ZERO; return LoanScheduleModel.from(periods, applicationCurrency, scheduleParams.getLoanTermInDays(), scheduleParams.getPrincipalToBeScheduled(), scheduleParams.getTotalCumulativePrincipal().getAmount(), totalPrincipalPaid, scheduleParams.getTotalCumulativeInterest().getAmount(), scheduleParams.getTotalFeeChargesCharged().getAmount(), scheduleParams.getTotalPenaltyChargesCharged().getAmount(), scheduleParams.getTotalRepaymentExpected().getAmount(), totalOutstanding); }
From source file:org.apache.fineract.portfolio.loanaccount.loanschedule.domain.AbstractLoanScheduleGenerator.java
License:Apache License
private void populateCompoundingDatesInPeriod(final LocalDate startDate, final LocalDate endDate, final LocalDate currentDate, final LoanApplicationTerms loanApplicationTerms, final HolidayDetailDTO holidayDetailDTO, final Map<LocalDate, Money> compoundingMap, final Set<LoanCharge> charges, MonetaryCurrency currency) { if (loanApplicationTerms.getInterestRecalculationCompoundingMethod().isCompoundingEnabled()) { LocalDate lastCompoundingDate = startDate; LocalDate compoundingDate = startDate; while (compoundingDate.isBefore(endDate) && compoundingDate.isBefore(currentDate)) { compoundingDate = getNextCompoundScheduleDate(compoundingDate, loanApplicationTerms, holidayDetailDTO);/*w w w.ja v a2 s. co m*/ if (!compoundingDate.isBefore(currentDate)) { break; } else if (compoundingDate.isAfter(endDate)) { updateMapWithAmount(compoundingMap, Money.zero(currency), compoundingDate); } else { Money feeChargesForInstallment = cumulativeFeeChargesDueWithin(lastCompoundingDate, compoundingDate, charges, currency, null, loanApplicationTerms.getPrincipal(), null, false); Money penaltyChargesForInstallment = cumulativePenaltyChargesDueWithin(lastCompoundingDate, compoundingDate, charges, currency, null, loanApplicationTerms.getPrincipal(), null, false); updateMapWithAmount(compoundingMap, feeChargesForInstallment.plus(penaltyChargesForInstallment), compoundingDate); } lastCompoundingDate = compoundingDate; } } }
From source file:org.apache.fineract.portfolio.loanaccount.loanschedule.domain.AbstractLoanScheduleGenerator.java
License:Apache License
/** * calculates Interest stating date as per the settings * /*from w w w.j a va 2s. c o m*/ * @param firstRepaymentdate * TODO */ private LocalDate calculateInterestStartDateForPeriod(final LoanApplicationTerms loanApplicationTerms, LocalDate periodStartDate, final LocalDate idealDisbursementDate, final LocalDate firstRepaymentdate) { LocalDate periodStartDateApplicableForInterest = periodStartDate; if (periodStartDate.isBefore(idealDisbursementDate) || firstRepaymentdate.isAfter(periodStartDate)) { if (loanApplicationTerms.getInterestChargedFromLocalDate() != null) { if (periodStartDate.isEqual(loanApplicationTerms.getExpectedDisbursementDate()) || loanApplicationTerms.getInterestChargedFromLocalDate().isAfter(periodStartDate)) { periodStartDateApplicableForInterest = loanApplicationTerms.getInterestChargedFromLocalDate(); } } else if (periodStartDate.isEqual(loanApplicationTerms.getExpectedDisbursementDate())) { periodStartDateApplicableForInterest = idealDisbursementDate; } } return periodStartDateApplicableForInterest; }
From source file:org.apache.fineract.portfolio.loanaccount.loanschedule.service.LoanScheduleAssembler.java
License:Apache License
private LocalDate deriveFirstRepaymentDate(final AccountType loanType, final Integer repaymentEvery, final LocalDate expectedDisbursementDate, final PeriodFrequencyType repaymentPeriodFrequencyType, final Integer minimumDaysBetweenDisbursalAndFirstRepayment, final Calendar calendar) { LocalDate derivedFirstRepayment = null; final LocalDate dateBasedOnMinimumDaysBetweenDisbursalAndFirstRepayment = expectedDisbursementDate .plusDays(minimumDaysBetweenDisbursalAndFirstRepayment); if ((loanType.isJLGAccount() || loanType.isGroupAccount()) && calendar != null) { final LocalDate refernceDateForCalculatingFirstRepaymentDate = expectedDisbursementDate; derivedFirstRepayment = deriveFirstRepaymentDateForJLGLoans(repaymentEvery, expectedDisbursementDate, refernceDateForCalculatingFirstRepaymentDate, repaymentPeriodFrequencyType, minimumDaysBetweenDisbursalAndFirstRepayment, calendar); } /*** Individual or group account, or JLG not linked to a meeting ***/ else {// w w w . j a va 2 s .c om LocalDate dateBasedOnRepaymentFrequency; // Derive the first repayment date as greater date among // (disbursement date + plus frequency) or // (disbursement date + minimum between disbursal and first // repayment ) if (repaymentPeriodFrequencyType.isDaily()) { dateBasedOnRepaymentFrequency = expectedDisbursementDate.plusDays(repaymentEvery); } else if (repaymentPeriodFrequencyType.isWeekly()) { dateBasedOnRepaymentFrequency = expectedDisbursementDate.plusWeeks(repaymentEvery); } else if (repaymentPeriodFrequencyType.isMonthly()) { dateBasedOnRepaymentFrequency = expectedDisbursementDate.plusMonths(repaymentEvery); } /** yearly loan **/ else { dateBasedOnRepaymentFrequency = expectedDisbursementDate.plusYears(repaymentEvery); } derivedFirstRepayment = dateBasedOnRepaymentFrequency.isAfter( dateBasedOnMinimumDaysBetweenDisbursalAndFirstRepayment) ? dateBasedOnRepaymentFrequency : dateBasedOnMinimumDaysBetweenDisbursalAndFirstRepayment; } return derivedFirstRepayment; }
From source file:org.apache.fineract.portfolio.loanaccount.service.LoanWritePlatformServiceJpaRepositoryImpl.java
License:Apache License
@Transactional @Override//from w w w .j ava 2 s. c o m public CommandProcessingResult addLoanCharge(final Long loanId, final JsonCommand command) { this.loanEventApiJsonValidator.validateAddLoanCharge(command.json()); final Loan loan = this.loanAssembler.assembleFrom(loanId); checkClientOrGroupActive(loan); Set<LoanDisbursementDetails> loanDisburseDetails = loan.getDisbursementDetails(); final Long chargeDefinitionId = command.longValueOfParameterNamed("chargeId"); final Charge chargeDefinition = this.chargeRepository.findOneWithNotFoundDetection(chargeDefinitionId); if (loan.isDisbursed() && chargeDefinition.isDisbursementCharge()) { validateAddingNewChargeAllowed(loanDisburseDetails); // validates // whether any // pending // disbursements // are // available to // apply this // charge } final List<Long> existingTransactionIds = new ArrayList<>(loan.findExistingTransactionIds()); final List<Long> existingReversedTransactionIds = new ArrayList<>( loan.findExistingReversedTransactionIds()); boolean isAppliedOnBackDate = false; LoanCharge loanCharge = null; LocalDate recalculateFrom = loan.fetchInterestRecalculateFromDate(); if (chargeDefinition.isPercentageOfDisbursementAmount()) { LoanTrancheDisbursementCharge loanTrancheDisbursementCharge = null; for (LoanDisbursementDetails disbursementDetail : loanDisburseDetails) { if (disbursementDetail.actualDisbursementDate() == null) { loanCharge = LoanCharge.createNewWithoutLoan(chargeDefinition, disbursementDetail.principal(), null, null, null, disbursementDetail.expectedDisbursementDateAsLocalDate(), null, null); loanTrancheDisbursementCharge = new LoanTrancheDisbursementCharge(loanCharge, disbursementDetail); loanCharge.updateLoanTrancheDisbursementCharge(loanTrancheDisbursementCharge); this.businessEventNotifierService.notifyBusinessEventToBeExecuted( BUSINESS_EVENTS.LOAN_ADD_CHARGE, constructEntityMap(BUSINESS_ENTITY.LOAN_CHARGE, loanCharge)); validateAddLoanCharge(loan, chargeDefinition, loanCharge); addCharge(loan, chargeDefinition, loanCharge); isAppliedOnBackDate = true; if (recalculateFrom.isAfter(disbursementDetail.expectedDisbursementDateAsLocalDate())) { recalculateFrom = disbursementDetail.expectedDisbursementDateAsLocalDate(); } } } loan.addTrancheLoanCharge(chargeDefinition); } else { loanCharge = LoanCharge.createNewFromJson(loan, chargeDefinition, command); this.businessEventNotifierService.notifyBusinessEventToBeExecuted(BUSINESS_EVENTS.LOAN_ADD_CHARGE, constructEntityMap(BUSINESS_ENTITY.LOAN_CHARGE, loanCharge)); validateAddLoanCharge(loan, chargeDefinition, loanCharge); isAppliedOnBackDate = addCharge(loan, chargeDefinition, loanCharge); if (loanCharge.getDueLocalDate() == null || recalculateFrom.isAfter(loanCharge.getDueLocalDate())) { isAppliedOnBackDate = true; recalculateFrom = loanCharge.getDueLocalDate(); } } boolean reprocessRequired = true; if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) { if (isAppliedOnBackDate && loan.isFeeCompoundingEnabledForInterestRecalculation()) { runScheduleRecalculation(loan, recalculateFrom); reprocessRequired = false; } updateOriginalSchedule(loan); } if (reprocessRequired) { ChangedTransactionDetail changedTransactionDetail = loan.reprocessTransactions(); if (changedTransactionDetail != null) { for (final Map.Entry<Long, LoanTransaction> mapEntry : changedTransactionDetail .getNewTransactionMappings().entrySet()) { this.loanTransactionRepository.save(mapEntry.getValue()); // update loan with references to the newly created // transactions loan.getLoanTransactions().add(mapEntry.getValue()); this.accountTransfersWritePlatformService.updateLoanTransaction(mapEntry.getKey(), mapEntry.getValue()); } } saveLoanWithDataIntegrityViolationChecks(loan); } postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds); if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled() && isAppliedOnBackDate && loan.isFeeCompoundingEnabledForInterestRecalculation()) { this.loanAccountDomainService.recalculateAccruals(loan); } this.businessEventNotifierService.notifyBusinessEventWasExecuted(BUSINESS_EVENTS.LOAN_ADD_CHARGE, constructEntityMap(BUSINESS_ENTITY.LOAN_CHARGE, loanCharge)); return new CommandProcessingResultBuilder() // .withCommandId(command.commandId()) // .withEntityId(loanCharge.getId()) // .withOfficeId(loan.getOfficeId()) // .withClientId(loan.getClientId()) // .withGroupId(loan.getGroupId()) // .withLoanId(loanId) // .build(); }
From source file:org.apache.fineract.portfolio.loanaccount.service.LoanWritePlatformServiceJpaRepositoryImpl.java
License:Apache License
@Override @Transactional//from w w w . j a v a 2s. c o m public void applyOverdueChargesForLoan(final Long loanId, Collection<OverdueLoanScheduleData> overdueLoanScheduleDatas) { Loan loan = null; final List<Long> existingTransactionIds = new ArrayList<>(); final List<Long> existingReversedTransactionIds = new ArrayList<>(); boolean runInterestRecalculation = false; LocalDate recalculateFrom = DateUtils.getLocalDateOfTenant(); LocalDate lastChargeDate = null; for (final OverdueLoanScheduleData overdueInstallment : overdueLoanScheduleDatas) { final JsonElement parsedCommand = this.fromApiJsonHelper.parse(overdueInstallment.toString()); final JsonCommand command = JsonCommand.from(overdueInstallment.toString(), parsedCommand, this.fromApiJsonHelper, null, null, null, null, null, loanId, null, null, null, null); LoanOverdueDTO overdueDTO = applyChargeToOverdueLoanInstallment(loanId, overdueInstallment.getChargeId(), overdueInstallment.getPeriodNumber(), command, loan, existingTransactionIds, existingReversedTransactionIds); loan = overdueDTO.getLoan(); runInterestRecalculation = runInterestRecalculation || overdueDTO.isRunInterestRecalculation(); if (recalculateFrom.isAfter(overdueDTO.getRecalculateFrom())) { recalculateFrom = overdueDTO.getRecalculateFrom(); } if (lastChargeDate == null || overdueDTO.getLastChargeAppliedDate().isAfter(lastChargeDate)) { lastChargeDate = overdueDTO.getLastChargeAppliedDate(); } } if (loan != null) { boolean reprocessRequired = true; LocalDate recalculatedTill = loan.fetchInterestRecalculateFromDate(); if (recalculateFrom.isAfter(recalculatedTill)) { recalculateFrom = recalculatedTill; } if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) { if (runInterestRecalculation && loan.isFeeCompoundingEnabledForInterestRecalculation()) { runScheduleRecalculation(loan, recalculateFrom); reprocessRequired = false; } updateOriginalSchedule(loan); } if (reprocessRequired) { addInstallmentIfPenaltyAppliedAfterLastDueDate(loan, lastChargeDate); ChangedTransactionDetail changedTransactionDetail = loan.reprocessTransactions(); if (changedTransactionDetail != null) { for (final Map.Entry<Long, LoanTransaction> mapEntry : changedTransactionDetail .getNewTransactionMappings().entrySet()) { this.loanTransactionRepository.save(mapEntry.getValue()); // update loan with references to the newly created // transactions loan.getLoanTransactions().add(mapEntry.getValue()); this.accountTransfersWritePlatformService.updateLoanTransaction(mapEntry.getKey(), mapEntry.getValue()); } } saveLoanWithDataIntegrityViolationChecks(loan); } postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds); if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled() && runInterestRecalculation && loan.isFeeCompoundingEnabledForInterestRecalculation()) { this.loanAccountDomainService.recalculateAccruals(loan); } this.businessEventNotifierService.notifyBusinessEventWasExecuted( BUSINESS_EVENTS.LOAN_APPLY_OVERDUE_CHARGE, constructEntityMap(BUSINESS_ENTITY.LOAN, loan)); } }
From source file:org.apache.fineract.portfolio.loanaccount.service.LoanWritePlatformServiceJpaRepositoryImpl.java
License:Apache License
private void addInstallmentIfPenaltyAppliedAfterLastDueDate(Loan loan, LocalDate lastChargeDate) { if (lastChargeDate != null) { List<LoanRepaymentScheduleInstallment> installments = loan.fetchRepaymentScheduleInstallments(); LoanRepaymentScheduleInstallment lastInstallment = loan .fetchRepaymentScheduleInstallment(installments.size()); if (lastChargeDate.isAfter(lastInstallment.getDueDate())) { if (lastInstallment.isRecalculatedInterestComponent()) { installments.remove(lastInstallment); lastInstallment = loan.fetchRepaymentScheduleInstallment(installments.size()); }// w w w . j a v a 2 s. c om boolean recalculatedInterestComponent = true; BigDecimal principal = BigDecimal.ZERO; BigDecimal interest = BigDecimal.ZERO; BigDecimal feeCharges = BigDecimal.ZERO; BigDecimal penaltyCharges = BigDecimal.ONE; LoanRepaymentScheduleInstallment newEntry = new LoanRepaymentScheduleInstallment(loan, installments.size() + 1, lastInstallment.getDueDate(), lastChargeDate, principal, interest, feeCharges, penaltyCharges, recalculatedInterestComponent); installments.add(newEntry); } } }
From source file:org.apache.fineract.portfolio.savings.domain.FixedDepositAccount.java
License:Apache License
public void postMaturityInterest(final boolean isSavingsInterestPostingAtCurrentPeriodEnd, final Integer financialYearBeginningMonth) { final LocalDate interestPostingUpToDate = maturityDate(); final MathContext mc = MathContext.DECIMAL64; final boolean isInterestTransfer = false; final List<PostingPeriod> postingPeriods = calculateInterestUsing(mc, interestPostingUpToDate, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth); Money interestPostedToDate = Money.zero(this.currency); boolean recalucateDailyBalanceDetails = false; for (final PostingPeriod interestPostingPeriod : postingPeriods) { LocalDate interestPostingTransactionDate = interestPostingPeriod.dateOfPostingTransaction(); interestPostingTransactionDate = interestPostingTransactionDate.isAfter(interestPostingUpToDate) ? interestPostingUpToDate : interestPostingTransactionDate; final Money interestEarnedToBePostedForPeriod = interestPostingPeriod.getInterestEarned(); interestPostedToDate = interestPostedToDate.plus(interestEarnedToBePostedForPeriod); final SavingsAccountTransaction postingTransaction = findInterestPostingTransactionFor( interestPostingTransactionDate); if (postingTransaction == null) { final SavingsAccountTransaction newPostingTransaction = SavingsAccountTransaction.interestPosting( this, office(), interestPostingTransactionDate, interestEarnedToBePostedForPeriod); this.transactions.add(newPostingTransaction); recalucateDailyBalanceDetails = true; } else {//from w w w . ja v a 2 s. co m final boolean correctionRequired = postingTransaction .hasNotAmount(interestEarnedToBePostedForPeriod); if (correctionRequired) { postingTransaction.reverse(); final SavingsAccountTransaction newPostingTransaction = SavingsAccountTransaction .interestPosting(this, office(), interestPostingTransactionDate, interestEarnedToBePostedForPeriod); this.transactions.add(newPostingTransaction); recalucateDailyBalanceDetails = true; } } } if (recalucateDailyBalanceDetails) { // update existing transactions so derived balance fields are // correct. recalculateDailyBalances(Money.zero(this.currency), interestPostingUpToDate); } this.summary.updateSummary(this.currency, this.savingsAccountTransactionSummaryWrapper, this.transactions); }