List of usage examples for org.joda.time LocalDate minusDays
public LocalDate minusDays(int days)
From source file:org.libreplan.web.common.components.NewAllocationSelector.java
License:Open Source License
public void open(LocalDate start, LocalDate end) { LocalDate newStart = start.minusDays(DAYS_LEAD_LAG_TO_TASK_LIMITS_DATES_FILTERING_INITIALIZATION); LocalDate newEnd = end.plusDays(DAYS_LEAD_LAG_TO_TASK_LIMITS_DATES_FILTERING_INITIALIZATION); getController().open(newStart, newEnd); }
From source file:org.mifos.accounts.productdefinition.business.SavingsOfferingBO.java
License:Open Source License
public void updateSavingsDetails(Money recommendedAmount, RecommendedAmntUnitEntity recommendedAmntUnit, Money maxAmntWithdrawl, Double interestRate, Money minAmountRequiredForInterestToBeCalculated, LocalDate updateDate) {//from w ww. j ava 2s . c o m this.maxAmntWithdrawl = maxAmntWithdrawl; this.recommendedAmount = recommendedAmount; this.recommendedAmntUnit = recommendedAmntUnit; if (interestDetailsHaveChanged(interestRate, minAmountRequiredForInterestToBeCalculated)) { InterestScheduledEvent interestCalculationEvent = new SavingsInterestScheduledEventFactory() .createScheduledEventFrom(this.getTimePerForInstcalc().getMeeting()); LocalDate nextInterestCalculationDate = interestCalculationEvent .nextMatchingDateAfter(startOfFiscalYear(updateDate), updateDate); LocalDate startOfPeriodDate = interestCalculationEvent .findFirstDateOfPeriodForMatchingDate(nextInterestCalculationDate); LocalDate previousInterestCalculationDate = startOfPeriodDate.minusDays(1); LocalDate startOfPreviousPeriodDate = interestCalculationEvent .findFirstDateOfPeriodForMatchingDate(previousInterestCalculationDate); CalendarPeriod previousInterestCalculationPeriod = new CalendarPeriod(startOfPreviousPeriodDate, previousInterestCalculationDate); if (noHistoricalRecordExistsFor(previousInterestCalculationPeriod)) { SavingsProductHistoricalInterestDetail historicalInterestDetail = new SavingsProductHistoricalInterestDetail( previousInterestCalculationPeriod, this.interestRate, this.minAmntForInt, this); this.historicalInterestDetails.add(historicalInterestDetail); } this.interestRate = interestRate; this.minAmntForInt = minAmountRequiredForInterestToBeCalculated; } }
From source file:org.mifos.accounts.savings.interest.schedule.internal.DailyInterestScheduledEvent.java
License:Open Source License
@Override public List<LocalDate> findAllMatchingDatesFromBaseDateUpToAndIncludingNearestMatchingEndDate( LocalDate baseDate, LocalDate cutOffDate) { List<LocalDate> allMatchingDates = new ArrayList<LocalDate>(); LocalDate previousMatchingDate = baseDate.minusDays(1); LocalDate matchingDate = nextMatchingDateFromAlreadyMatchingDate(previousMatchingDate); while (matchingDate.isEqual(cutOffDate) || matchingDate.isBefore(cutOffDate)) { allMatchingDates.add(matchingDate); matchingDate = nextMatchingDateFromAlreadyMatchingDate(matchingDate); }/* ww w . ja va 2 s . c o m*/ if (!allMatchingDates.contains(matchingDate)) { allMatchingDates.add(matchingDate); } return allMatchingDates; }
From source file:org.mifos.accounts.savings.interest.schedule.internal.DailyInterestScheduledEvent.java
License:Open Source License
@Override public LocalDate findFirstDateOfPeriodForMatchingDate(LocalDate matchingDate) { LocalDate previousMatchingDate; if (every > 1) { previousMatchingDate = matchingDate.minusDays(every - 1); } else {//from ww w.ja v a 2s. c o m previousMatchingDate = matchingDate; } return previousMatchingDate; }
From source file:org.mifos.accounts.savings.interest.schedule.internal.MonthlyOnLastDayOfMonthInterestScheduledEvent.java
License:Open Source License
@Override public List<LocalDate> findAllMatchingDatesFromBaseDateUpToAndIncludingNearestMatchingEndDate( LocalDate baseDate, LocalDate cutOffDate) { List<LocalDate> allMatchingDates = new ArrayList<LocalDate>(); LocalDate previousMatchingDate = baseDate.minusDays(1); LocalDate matchingDate = nextMatchingDateFromAlreadyMatchingDate(previousMatchingDate); while (matchingDate.isEqual(cutOffDate) || matchingDate.isBefore(cutOffDate)) { allMatchingDates.add(matchingDate); matchingDate = nextMatchingDateFromAlreadyMatchingDate(matchingDate); }/*w ww . j av a2 s . c o m*/ if (!allMatchingDates.contains(matchingDate)) { allMatchingDates.add(matchingDate); } return allMatchingDates; }
From source file:org.mifos.application.meeting.business.MeetingBO.java
License:Open Source License
public LocalDate startDateForMeetingInterval(LocalDate date) { LocalDate startOfMeetingInterval = date; if (isWeekly()) { int weekDay = WeekDay.getJodaDayOfWeekThatMatchesMifosWeekDay( getFiscalCalendarRules().getStartOfWeekWeekDay().getValue()); while (startOfMeetingInterval.getDayOfWeek() != weekDay) { startOfMeetingInterval = startOfMeetingInterval.minusDays(1); }// ww w .j a v a 2s .c o m } else if (isMonthly()) { int dayOfMonth = date.getDayOfMonth(); startOfMeetingInterval = startOfMeetingInterval.minusDays(dayOfMonth - 1); } else { // for days we return the same day startOfMeetingInterval = date; } return startOfMeetingInterval; }
From source file:org.mifos.clientportfolio.newloan.domain.VariableInstallmentsLoanDisbursementStrategyImpl.java
License:Open Source License
@Override public LocalDate findClosestMatchingDateFromAndInclusiveOf(LocalDate fromAndInclusiveOf) { LocalDate nearestMatchingDate = new LocalDate( scheduledEvent.nearestMatchNotTakingIntoAccountScheduleFrequency( fromAndInclusiveOf.minusDays(1).toDateMidnight().toDateTime())); if (nearestMatchingDate.isBefore(new LocalDate())) { nearestMatchingDate = new LocalDate(scheduledEvent.nearestMatchNotTakingIntoAccountScheduleFrequency( fromAndInclusiveOf.toDateMidnight().toDateTime())); }// ww w .ja v a 2 s . c o m return nearestMatchingDate; }
From source file:org.mifosplatform.portfolio.loanaccount.loanschedule.domain.AbstractLoanScheduleGenerator.java
License:Mozilla Public License
private LoanScheduleModel generate(final MathContext mc, final LoanApplicationTerms loanApplicationTerms, final Set<LoanCharge> loanCharges, final HolidayDetailDTO holidayDetailDTO, final Collection<RecalculationDetail> transactions, final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor, final LocalDate scheduleTillDate) { final ApplicationCurrency applicationCurrency = loanApplicationTerms.getApplicationCurrency(); // 1. generate list of proposed schedule due dates final LocalDate loanEndDate = this.scheduledDateGenerator.getLastRepaymentDate(loanApplicationTerms, holidayDetailDTO);//from ww w. j a v a 2 s.c om loanApplicationTerms.updateLoanEndDate(loanEndDate); // 2. determine the total charges due at time of disbursement final BigDecimal chargesDueAtTimeOfDisbursement = deriveTotalChargesDueAtTimeOfDisbursement(loanCharges); // 3. setup variables for tracking important facts required for loan // schedule generation. Money principalDisbursed = loanApplicationTerms.getPrincipal(); final MonetaryCurrency currency = principalDisbursed.getCurrency(); final int numberOfRepayments = loanApplicationTerms.getNumberOfRepayments(); // variables for cumulative totals int loanTermInDays = Integer.valueOf(0); final BigDecimal totalPrincipalPaid = BigDecimal.ZERO; BigDecimal totalFeeChargesCharged = chargesDueAtTimeOfDisbursement; BigDecimal totalPenaltyChargesCharged = BigDecimal.ZERO; BigDecimal totalRepaymentExpected = chargesDueAtTimeOfDisbursement; final BigDecimal totalOutstanding = BigDecimal.ZERO; Money totalCumulativePrincipal = principalDisbursed.zero(); Money totalCumulativeInterest = principalDisbursed.zero(); Money totalOutstandingInterestPaymentDueToGrace = principalDisbursed.zero(); final Collection<LoanScheduleModelPeriod> periods = createNewLoanScheduleListWithDisbursementDetails( numberOfRepayments, loanApplicationTerms, chargesDueAtTimeOfDisbursement); // 4. Determine the total interest owed over the full loan for FLAT // interest method . Money totalInterestChargedForFullLoanTerm = loanApplicationTerms .calculateTotalInterestCharged(this.paymentPeriodsInOneYearCalculator, mc); LocalDate periodStartDate = loanApplicationTerms.getExpectedDisbursementDate(); LocalDate actualRepaymentDate = periodStartDate; boolean isFirstRepayment = true; LocalDate firstRepaymentdate = this.scheduledDateGenerator.generateNextRepaymentDate(periodStartDate, loanApplicationTerms, isFirstRepayment); final LocalDate idealDisbursementDate = this.scheduledDateGenerator .idealDisbursementDateBasedOnFirstRepaymentDate( loanApplicationTerms.getLoanTermPeriodFrequencyType(), loanApplicationTerms.getRepaymentEvery(), firstRepaymentdate); LocalDate periodStartDateApplicableForInterest = periodStartDate; // Actual period Number as per the schedule int periodNumber = 1; // Actual period Number and interest only repayments int instalmentNumber = 1; Money outstandingBalance = principalDisbursed; // disbursement map for tranche details(will added to outstanding // balance as per the start date) 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, periodStartDate, periods, chargesDueAtTimeOfDisbursement, disburseDetailMap, isInterestRecalculationRequired(loanApplicationTerms, transactions)); principalDisbursed = principalDisbursed.zero().plus(disburseAmt); loanApplicationTerms.setPrincipal(loanApplicationTerms.getPrincipal().zero().plus(disburseAmt)); outstandingBalance = outstandingBalance.zero().plus(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); // total outstanding balance as per rest for interest calculation. Money outstandingBalanceAsPerRest = outstandingBalance; // early payments will be added here and as per the selected strategy // action will be performed on this value Money reducePrincipal = totalCumulativePrincipal.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<>(); final List<LoanRepaymentScheduleInstallment> installments = new ArrayList<>(); LocalDate currentDate = DateUtils.getLocalDateOfTenant(); LocalDate lastRestDate = currentDate; if (loanApplicationTerms.getRestCalendarInstance() != null) { lastRestDate = getNextRestScheduleDate(currentDate.minusDays(1), loanApplicationTerms, holidayDetailDTO); } // 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<>(); final Map<LocalDate, TreeMap<LocalDate, Money>> compoundingDateVariations = new HashMap<>(); 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; } while (!outstandingBalance.isZero() || !disburseDetailMap.isEmpty()) { actualRepaymentDate = this.scheduledDateGenerator.generateNextRepaymentDate(actualRepaymentDate, loanApplicationTerms, isFirstRepayment); isFirstRepayment = false; LocalDate scheduledDueDate = this.scheduledDateGenerator.adjustRepaymentDate(actualRepaymentDate, loanApplicationTerms, holidayDetailDTO); if (!latePaymentMap.isEmpty()) { populateCompoundingDatesInPeriod(periodStartDate, scheduledDueDate, currentDate, loanApplicationTerms, holidayDetailDTO, compoundingMap, loanCharges, currency); compoundingDateVariations.put(periodStartDate, new TreeMap<>(compoundingMap)); } if (extendTermForDailyRepayments) { actualRepaymentDate = scheduledDueDate; } // calculated interest start date for the period periodStartDateApplicableForInterest = calculateInterestStartDateForPeriod(loanApplicationTerms, periodStartDate, idealDisbursementDate, periodStartDateApplicableForInterest); int daysInPeriodApplicableForInterest = Days .daysBetween(periodStartDateApplicableForInterest, scheduledDueDate).getDays(); if (scheduleTillDate != null && !scheduledDueDate.isBefore(scheduleTillDate)) { scheduledDueDate = scheduleTillDate; isNextRepaymentAvailable = false; } // populates the collection with transactions till the due date of // the period for interest recalculation enabled loans Collection<RecalculationDetail> applicableTransactions = getApplicableTransactionsForPeriod( loanApplicationTerms, scheduledDueDate, transactions); double interestCalculationGraceOnRepaymentPeriodFraction = this.paymentPeriodsInOneYearCalculator .calculatePortionOfRepaymentPeriodInterestChargingGrace(periodStartDateApplicableForInterest, scheduledDueDate, loanApplicationTerms.getInterestChargedFromLocalDate(), loanApplicationTerms.getLoanTermPeriodFrequencyType(), loanApplicationTerms.getRepaymentEvery()); if (loanApplicationTerms.isMultiDisburseLoan()) { // Updates fixed emi amount as the date if multiple amounts // provided loanApplicationTerms.setFixedEmiAmountForPeriod(scheduledDueDate); for (Map.Entry<LocalDate, Money> disburseDetail : disburseDetailMap.entrySet()) { if (disburseDetail.getKey().isAfter(periodStartDate) && !disburseDetail.getKey().isAfter(scheduledDueDate)) { // validation check for amount not exceeds specified max // amount as per the configuration if (loanApplicationTerms.getMaxOutstandingBalance() != null && outstandingBalance.plus(disburseDetail.getValue()) .isGreaterThan(loanApplicationTerms.getMaxOutstandingBalance())) { String errorMsg = "Outstanding balance must not exceed the amount: " + loanApplicationTerms.getMaxOutstandingBalance(); throw new MultiDisbursementOutstandingAmoutException(errorMsg, loanApplicationTerms.getMaxOutstandingBalance().getAmount(), disburseDetail.getValue()); } // 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()); principalDisbursed = principalDisbursed.plus(disburseDetail.getValue()); loanApplicationTerms .setPrincipal(loanApplicationTerms.getPrincipal().plus(disburseDetail.getValue())); } } } // Adds new interest repayment to the schedule as per the repayment // transaction processor configuration // will be added only if there is a loan repayment between the // period for interest first repayment strategies Money earlyPaidAmount = Money.zero(currency); LoanScheduleModelPeriod lastInstallment = null; if (isInterestRecalculationRequired(loanApplicationTerms, transactions)) { boolean checkForOutstanding = true; List<RecalculationDetail> unprocessedTransactions = new ArrayList<>(); LoanScheduleModelPeriod installment = null; for (RecalculationDetail detail : applicableTransactions) { if (detail.isProcessed()) { continue; } boolean updateLatePaymentMap = false; if (detail.getTransactionDate().isBefore(scheduledDueDate)) { if (loanRepaymentScheduleTransactionProcessor != null && loanRepaymentScheduleTransactionProcessor .isInterestFirstRepaymentScheduleTransactionProcessor()) { List<LoanTransaction> currentTransactions = createCurrentTransactionList(detail); if (!detail.getTransactionDate().isEqual(periodStartDate)) { int periodDays = Days.daysBetween(periodStartDate, detail.getTransactionDate()) .getDays(); // calculates period start date for interest // calculation as per the configuration periodStartDateApplicableForInterest = calculateInterestStartDateForPeriod( loanApplicationTerms, periodStartDate, idealDisbursementDate, periodStartDateApplicableForInterest); int daysInPeriodApplicable = Days.daysBetween(periodStartDateApplicableForInterest, detail.getTransactionDate()).getDays(); Money interestForThisinstallment = Money.zero(currency); if (daysInPeriodApplicable > 0) { // 5 determine interest till the transaction // date if (!compoundingDateVariations .containsKey(periodStartDateApplicableForInterest)) { compoundingDateVariations.put(periodStartDateApplicableForInterest, new TreeMap<>(compoundingMap)); } PrincipalInterest principalInterestForThisPeriod = calculatePrincipalInterestComponentsForPeriod( this.paymentPeriodsInOneYearCalculator, interestCalculationGraceOnRepaymentPeriodFraction, totalCumulativePrincipal.minus(reducePrincipal), totalCumulativeInterest, totalInterestChargedForFullLoanTerm, totalOutstandingInterestPaymentDueToGrace, outstandingBalanceAsPerRest, loanApplicationTerms, periodNumber, mc, mergeVariationsToMap(principalPortionMap, latePaymentMap, disburseDetailMap, compoundingMap), compoundingMap, periodStartDateApplicableForInterest, detail.getTransactionDate(), daysInPeriodApplicableForInterest); interestForThisinstallment = principalInterestForThisPeriod.interest(); totalOutstandingInterestPaymentDueToGrace = principalInterestForThisPeriod .interestPaymentDueToGrace(); } Money principalForThisPeriod = principalDisbursed.zero(); // applies all the applicable charges to the // newly // created installment PrincipalInterest principalInterest = new PrincipalInterest(principalForThisPeriod, interestForThisinstallment, null); Money feeChargesForInstallment = cumulativeFeeChargesDueWithin(periodStartDate, detail.getTransactionDate(), loanCharges, currency, principalInterest, principalDisbursed, totalCumulativeInterest, numberOfRepayments, true); Money penaltyChargesForInstallment = cumulativePenaltyChargesDueWithin( periodStartDate, detail.getTransactionDate(), loanCharges, currency, principalInterest, principalDisbursed, totalCumulativeInterest, numberOfRepayments, true); // 8. sum up real totalInstallmentDue from // components final Money totalInstallmentDue = principalForThisPeriod .plus(interestForThisinstallment).plus(feeChargesForInstallment) .plus(penaltyChargesForInstallment); // 9. create repayment period from parts installment = LoanScheduleModelRepaymentPeriod.repayment(instalmentNumber, periodStartDate, detail.getTransactionDate(), principalForThisPeriod, outstandingBalance, interestForThisinstallment, feeChargesForInstallment, penaltyChargesForInstallment, totalInstallmentDue, true); periods.add(installment); // update outstanding balance for interest // calculation as per the rest outstandingBalanceAsPerRest = updateBalanceForInterestCalculation( principalPortionMap, detail.getTransactionDate(), outstandingBalanceAsPerRest, false); outstandingBalanceAsPerRest = updateBalanceForInterestCalculation(disburseDetailMap, detail.getTransactionDate(), outstandingBalanceAsPerRest, true); // handle cumulative fields loanTermInDays += periodDays; totalRepaymentExpected = totalRepaymentExpected .add(totalInstallmentDue.getAmount()); totalCumulativeInterest = totalCumulativeInterest.plus(interestForThisinstallment); totalFeeChargesCharged = totalFeeChargesCharged .add(feeChargesForInstallment.getAmount()); totalPenaltyChargesCharged = totalPenaltyChargesCharged .add(penaltyChargesForInstallment.getAmount()); periodStartDate = detail.getTransactionDate(); periodStartDateApplicableForInterest = periodStartDate; updateLatePaymentMap = true; instalmentNumber++; // creates and insert Loan repayment schedule // for // the period addLoanRepaymentScheduleInstallment(installments, 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 = loanRepaymentScheduleTransactionProcessor .handleRepaymentSchedule(currentTransactions, currency, installments); if (unprocessed.isGreaterThanZero()) { if (loanApplicationTerms.getPreClosureInterestCalculationStrategy() .calculateTillRestFrequencyEnabled()) { LocalDate applicableDate = getNextRestScheduleDate( detail.getTransactionDate().minusDays(1), loanApplicationTerms, holidayDetailDTO); checkForOutstanding = detail.getTransactionDate().isEqual(applicableDate); } // reduces actual outstanding balance outstandingBalance = outstandingBalance.minus(unprocessed); // if outstanding balance becomes less than zero // then adjusts the princiapal Money addToPrinciapal = Money.zero(currency); if (!outstandingBalance.isGreaterThanZero()) { addToPrinciapal = addToPrinciapal.plus(outstandingBalance); outstandingBalance = outstandingBalance.zero(); lastInstallment = installment; } // updates principal portion map with the early // payment amounts and applicable date as per // rest updatePrincipalPaidPortionToMap(loanApplicationTerms, holidayDetailDTO, principalPortionMap, installment, detail, unprocessed.plus(addToPrinciapal), installments); totalRepaymentExpected = totalRepaymentExpected .add(unprocessed.plus(addToPrinciapal).getAmount()); totalCumulativePrincipal = totalCumulativePrincipal .plus(unprocessed.plus(addToPrinciapal)); // method applies early payment strategy reducePrincipal = reducePrincipal.plus(unprocessed); reducePrincipal = applyEarlyPaymentStrategy(loanApplicationTerms, reducePrincipal); } // identify late payments and add compounding // details to // map for interest calculation updateLatePaidAmountsToPrincipalMap(principalPortionMap, detail.getTransaction(), latePaymentMap, compoundingMap, loanApplicationTerms, currency, holidayDetailDTO, lastRestDate); compoundingDateVariations.put(periodStartDateApplicableForInterest, new TreeMap<>(compoundingMap)); if (updateLatePaymentMap) { updateLatePaymentsToMap(loanApplicationTerms, holidayDetailDTO, currency, latePaymentMap, scheduledDueDate, installments, true, lastRestDate, compoundingMap); } } else if (loanRepaymentScheduleTransactionProcessor != null) { LocalDate applicableDate = getNextRestScheduleDate( detail.getTransactionDate().minusDays(1), loanApplicationTerms, holidayDetailDTO); if (applicableDate.isBefore(scheduledDueDate)) { List<LoanTransaction> currentTransactions = createCurrentTransactionList(detail); Money unprocessed = loanRepaymentScheduleTransactionProcessor .handleRepaymentSchedule(currentTransactions, currency, installments); Money arrears = fetchCompoundedArrears(loanApplicationTerms, currency, detail.getTransaction()); if (unprocessed.isGreaterThanZero()) { arrears = getTotalAmount(latePaymentMap, currency); updateMapWithAmount(principalPortionMap, unprocessed, applicableDate); earlyPaidAmount = earlyPaidAmount.plus(unprocessed); // this check is to identify pre-closure and // apply interest calculation as per // configuration if (!outstandingBalance.isGreaterThan(unprocessed) && !loanApplicationTerms.getPreClosureInterestCalculationStrategy() .calculateTillRestFrequencyEnabled()) { LocalDate calculateTill = detail.getTransactionDate(); if (!compoundingDateVariations .containsKey(periodStartDateApplicableForInterest)) { compoundingDateVariations.put(periodStartDateApplicableForInterest, new TreeMap<>(compoundingMap)); } PrincipalInterest principalInterestForThisPeriod = calculatePrincipalInterestComponentsForPeriod( this.paymentPeriodsInOneYearCalculator, interestCalculationGraceOnRepaymentPeriodFraction, totalCumulativePrincipal.minus(reducePrincipal), totalCumulativeInterest, totalInterestChargedForFullLoanTerm, totalOutstandingInterestPaymentDueToGrace, outstandingBalanceAsPerRest, loanApplicationTerms, periodNumber, mc, mergeVariationsToMap(principalPortionMap, latePaymentMap, disburseDetailMap, compoundingMap), compoundingMap, periodStartDateApplicableForInterest, calculateTill, daysInPeriodApplicableForInterest); if (!principalInterestForThisPeriod.interest() .plus(principalInterestForThisPeriod.interestPaymentDueToGrace()) .plus(outstandingBalance).isGreaterThan(unprocessed)) { earlyPaidAmount = earlyPaidAmount.minus(unprocessed); updateMapWithAmount(principalPortionMap, unprocessed.negated(), applicableDate); LoanTransaction loanTransaction = LoanTransaction.repayment(null, unprocessed, null, detail.getTransactionDate(), null, DateUtils.getLocalDateTimeOfTenant(), null); RecalculationDetail recalculationDetail = new RecalculationDetail( detail.getTransactionDate(), 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; outstandingBalance = outstandingBalance.minus(unprocessed); // if outstanding balance becomes less than // zero // then adjusts the princiapal Money addToPrinciapal = Money.zero(currency); if (outstandingBalance.isLessThanZero()) { addToPrinciapal = addToPrinciapal.plus(outstandingBalance); outstandingBalance = outstandingBalance.zero(); updateMapWithAmount(principalPortionMap, addToPrinciapal, applicableDate); earlyPaidAmount = earlyPaidAmount.plus(addToPrinciapal); } } LocalDate tillDate = getNextRestScheduleDate(currentDate.minusDays(1), loanApplicationTerms, holidayDetailDTO); if (arrears.isGreaterThanZero() && applicableDate.isBefore(tillDate)) { updateLatePaidAmountsToPrincipalMap(principalPortionMap, detail.getTransaction(), latePaymentMap, compoundingMap, loanApplicationTerms, currency, holidayDetailDTO, lastRestDate); compoundingDateVariations.put(periodStartDateApplicableForInterest, new TreeMap<>(compoundingMap)); } } } } } applicableTransactions.addAll(unprocessedTransactions); if (checkForOutstanding && outstandingBalance.isZero() && disburseDetailMap.isEmpty()) { continue; } } int periodDays = Days.daysBetween(periodStartDate, scheduledDueDate).getDays(); periodStartDateApplicableForInterest = calculateInterestStartDateForPeriod(loanApplicationTerms, periodStartDate, idealDisbursementDate, periodStartDateApplicableForInterest); // backup for pre-close transaction if (compoundingDateVariations.containsKey(periodStartDateApplicableForInterest)) { compoundingMap.clear(); compoundingMap.putAll(compoundingDateVariations.get(periodStartDateApplicableForInterest)); } else { compoundingDateVariations.put(periodStartDateApplicableForInterest, new TreeMap<>(compoundingMap)); } // 5 determine principal,interest of repayment period PrincipalInterest principalInterestForThisPeriod = calculatePrincipalInterestComponentsForPeriod( this.paymentPeriodsInOneYearCalculator, interestCalculationGraceOnRepaymentPeriodFraction, totalCumulativePrincipal.minus(reducePrincipal), totalCumulativeInterest, totalInterestChargedForFullLoanTerm, totalOutstandingInterestPaymentDueToGrace, outstandingBalanceAsPerRest, loanApplicationTerms, periodNumber, mc, mergeVariationsToMap(principalPortionMap, latePaymentMap, disburseDetailMap, compoundingMap), compoundingMap, periodStartDateApplicableForInterest, scheduledDueDate, daysInPeriodApplicableForInterest); if (loanApplicationTerms.getFixedEmiAmount() != null && loanApplicationTerms.getFixedEmiAmount() .compareTo(principalInterestForThisPeriod.interest().getAmount()) != 1) { String errorMsg = "EMI amount must be greter than : " + principalInterestForThisPeriod.interest().getAmount(); throw new MultiDisbursementEmiAmountException(errorMsg, principalInterestForThisPeriod.interest().getAmount(), loanApplicationTerms.getFixedEmiAmount()); } // update cumulative fields for principal & interest Money interestForThisinstallment = principalInterestForThisPeriod.interest(); Money lastTotalOutstandingInterestPaymentDueToGrace = totalOutstandingInterestPaymentDueToGrace; totalOutstandingInterestPaymentDueToGrace = principalInterestForThisPeriod.interestPaymentDueToGrace(); Money principalForThisPeriod = principalInterestForThisPeriod.principal(); if (principalForThisPeriod.isZero()) { loanApplicationTerms.resetFixedEmiAmount(); } // applies early payments on principal portion if (principalForThisPeriod.isGreaterThan(reducePrincipal)) { principalForThisPeriod = principalForThisPeriod.minus(reducePrincipal); reducePrincipal = reducePrincipal.zero(); } else { reducePrincipal = reducePrincipal.minus(principalForThisPeriod); principalForThisPeriod = principalForThisPeriod.zero(); } // earlyPaidAmount is already subtracted from balancereducePrincipal // reducePrincipal.plus(unprocessed); Money reducedBalance = earlyPaidAmount; earlyPaidAmount = earlyPaidAmount.minus(principalForThisPeriod); if (earlyPaidAmount.isGreaterThanZero()) { reducePrincipal = reducePrincipal.plus(earlyPaidAmount); reducePrincipal = applyEarlyPaymentStrategy(loanApplicationTerms, reducePrincipal); principalForThisPeriod = principalForThisPeriod.plus(earlyPaidAmount); } // 6. update outstandingLoanBlance using current period // 'principalDue' outstandingBalance = outstandingBalance.minus(principalForThisPeriod.minus(reducedBalance)); if (outstandingBalance.isLessThanZero() || !isNextRepaymentAvailable) { principalForThisPeriod = principalForThisPeriod.plus(outstandingBalance); outstandingBalance = outstandingBalance.zero(); } // applies charges for the period PrincipalInterest principalInterest = new PrincipalInterest(principalForThisPeriod, interestForThisinstallment, null); Money feeChargesForInstallment = cumulativeFeeChargesDueWithin(periodStartDate, scheduledDueDate, loanCharges, currency, principalInterest, principalDisbursed, totalCumulativeInterest, numberOfRepayments, true); Money penaltyChargesForInstallment = cumulativePenaltyChargesDueWithin(periodStartDate, scheduledDueDate, loanCharges, currency, principalInterest, principalDisbursed, totalCumulativeInterest, numberOfRepayments, true); totalFeeChargesCharged = totalFeeChargesCharged.add(feeChargesForInstallment.getAmount()); totalPenaltyChargesCharged = totalPenaltyChargesCharged.add(penaltyChargesForInstallment.getAmount()); // 8. sum up real totalInstallmentDue from components final Money totalInstallmentDue = principalForThisPeriod.plus(interestForThisinstallment) .plus(feeChargesForInstallment).plus(penaltyChargesForInstallment); // if previous installment is last then add interest to same // installment if (lastInstallment != null && principalForThisPeriod.isZero()) { lastInstallment.addInterestAmount(interestForThisinstallment); continue; } // 9. create repayment period from parts LoanScheduleModelPeriod installment = LoanScheduleModelRepaymentPeriod.repayment(instalmentNumber, periodStartDate, scheduledDueDate, principalForThisPeriod, outstandingBalance, interestForThisinstallment, feeChargesForInstallment, penaltyChargesForInstallment, totalInstallmentDue, false); // apply loan transactions on installments to identify early/late // payments for interest recalculation if (isInterestRecalculationRequired(loanApplicationTerms, transactions) && loanRepaymentScheduleTransactionProcessor != null) { Money principalProcessed = Money.zero(currency); addLoanRepaymentScheduleInstallment(installments, installment); for (RecalculationDetail detail : applicableTransactions) { if (!detail.isProcessed()) { List<LoanTransaction> currentTransactions = new ArrayList<>(2); currentTransactions.add(detail.getTransaction()); // applies the transaction as per transaction strategy // on scheduled installments to identify the // unprocessed(early payment ) amounts Money unprocessed = loanRepaymentScheduleTransactionProcessor .handleRepaymentSchedule(currentTransactions, currency, installments); if (unprocessed.isGreaterThanZero()) { outstandingBalance = outstandingBalance.minus(unprocessed); // pre closure check and processing if (outstandingBalance.isLessThan(interestForThisinstallment) && !scheduledDueDate.equals(detail.getTransactionDate())) { LocalDate calculateTill = detail.getTransactionDate(); if (loanApplicationTerms.getPreClosureInterestCalculationStrategy() .calculateTillRestFrequencyEnabled()) { calculateTill = getNextRestScheduleDate(calculateTill.minusDays(1), loanApplicationTerms, holidayDetailDTO); } if (compoundingDateVariations.containsKey(periodStartDateApplicableForInterest)) { compoundingMap.clear(); compoundingMap.putAll( compoundingDateVariations.get(periodStartDateApplicableForInterest)); } PrincipalInterest interestTillDate = calculatePrincipalInterestComponentsForPeriod( this.paymentPeriodsInOneYearCalculator, interestCalculationGraceOnRepaymentPeriodFraction, totalCumulativePrincipal, totalCumulativeInterest, totalInterestChargedForFullLoanTerm, lastTotalOutstandingInterestPaymentDueToGrace, outstandingBalanceAsPerRest, loanApplicationTerms, periodNumber, mc, mergeVariationsToMap(principalPortionMap, latePaymentMap, disburseDetailMap, compoundingMap), compoundingMap, periodStartDateApplicableForInterest, calculateTill, daysInPeriodApplicableForInterest); Money diff = interestForThisinstallment.minus(interestTillDate.interest()); if (!outstandingBalance.minus(diff).isGreaterThanZero()) { outstandingBalance = outstandingBalance.minus(diff); interestForThisinstallment = interestForThisinstallment.minus(diff); principalForThisPeriod = principalForThisPeriod.plus(diff); final Money totalDue = principalForThisPeriod// .plus(interestForThisinstallment); // 9. create and replaces repayment period // from parts installment = LoanScheduleModelRepaymentPeriod.repayment(instalmentNumber, periodStartDate, detail.getTransactionDate(), principalForThisPeriod, outstandingBalance, interestForThisinstallment, feeChargesForInstallment, penaltyChargesForInstallment, totalDue, false); totalOutstandingInterestPaymentDueToGrace = interestTillDate .interestPaymentDueToGrace(); } } Money addToPrinciapal = Money.zero(currency); if (outstandingBalance.isLessThanZero()) { addToPrinciapal = addToPrinciapal.plus(outstandingBalance); outstandingBalance = outstandingBalance.zero(); } // updates principal portion map with the early // payment amounts and applicable date as per rest updatePrincipalPaidPortionToMap(loanApplicationTerms, holidayDetailDTO, principalPortionMap, installment, detail, unprocessed.plus(addToPrinciapal), installments); totalRepaymentExpected = totalRepaymentExpected .add(unprocessed.plus(addToPrinciapal).getAmount()); totalCumulativePrincipal = totalCumulativePrincipal .plus(unprocessed.plus(addToPrinciapal)); reducePrincipal = reducePrincipal.plus(unprocessed); reducePrincipal = applyEarlyPaymentStrategy(loanApplicationTerms, reducePrincipal); principalForThisPeriod = principalForThisPeriod.plus(unprocessed.plus(addToPrinciapal)); principalProcessed = principalProcessed.plus(unprocessed.plus(addToPrinciapal)); } } } updateLatePaymentsToMap(loanApplicationTerms, holidayDetailDTO, currency, latePaymentMap, scheduledDueDate, installments, true, lastRestDate, compoundingMap); principalForThisPeriod = principalForThisPeriod.minus(principalProcessed); } periods.add(installment); // Updates principal paid map with efective date for reducing // the amount from outstanding balance(interest calculation) LocalDate amountApplicableDate = installment.periodDueDate(); if (loanApplicationTerms.isInterestRecalculationEnabled()) { amountApplicableDate = getNextRestScheduleDate(installment.periodDueDate().minusDays(1), loanApplicationTerms, holidayDetailDTO); } updateMapWithAmount(principalPortionMap, principalForThisPeriod.minus(reducedBalance), amountApplicableDate); // update outstanding balance for interest calculation outstandingBalanceAsPerRest = updateBalanceForInterestCalculation(principalPortionMap, scheduledDueDate, outstandingBalanceAsPerRest, false); outstandingBalanceAsPerRest = updateBalanceForInterestCalculation(disburseDetailMap, scheduledDueDate, outstandingBalanceAsPerRest, true); // handle cumulative fields loanTermInDays += periodDays; totalCumulativePrincipal = totalCumulativePrincipal.plus(principalForThisPeriod); totalCumulativeInterest = totalCumulativeInterest.plus(interestForThisinstallment); totalRepaymentExpected = totalRepaymentExpected.add(totalInstallmentDue.getAmount()); periodStartDate = scheduledDueDate; periodStartDateApplicableForInterest = periodStartDate; instalmentNumber++; periodNumber++; compoundingDateVariations.clear(); } // this condition is to add the interest from grace period if not // already applied. if (totalOutstandingInterestPaymentDueToGrace.isGreaterThanZero()) { LoanScheduleModelPeriod installment = ((List<LoanScheduleModelPeriod>) periods).get(periods.size() - 1); installment.addInterestAmount(totalOutstandingInterestPaymentDueToGrace); totalRepaymentExpected = totalRepaymentExpected .add(totalOutstandingInterestPaymentDueToGrace.getAmount()); totalCumulativeInterest = totalCumulativeInterest.plus(totalOutstandingInterestPaymentDueToGrace); totalOutstandingInterestPaymentDueToGrace = totalOutstandingInterestPaymentDueToGrace.zero(); } // 7. determine fees and penalties for charges which depends on total // loan interest for (LoanScheduleModelPeriod loanScheduleModelPeriod : periods) { if (loanScheduleModelPeriod.isRepaymentPeriod()) { PrincipalInterest principalInterest = new PrincipalInterest( Money.of(currency, loanScheduleModelPeriod.principalDue()), Money.of(currency, loanScheduleModelPeriod.interestDue()), null); Money feeChargesForInstallment = cumulativeFeeChargesDueWithin( loanScheduleModelPeriod.periodFromDate(), loanScheduleModelPeriod.periodDueDate(), nonCompoundingCharges, currency, principalInterest, principalDisbursed, totalCumulativeInterest, numberOfRepayments, !loanScheduleModelPeriod.isRecalculatedInterestComponent()); Money penaltyChargesForInstallment = cumulativePenaltyChargesDueWithin( loanScheduleModelPeriod.periodFromDate(), loanScheduleModelPeriod.periodDueDate(), nonCompoundingCharges, currency, principalInterest, principalDisbursed, totalCumulativeInterest, numberOfRepayments, !loanScheduleModelPeriod.isRecalculatedInterestComponent()); totalFeeChargesCharged = totalFeeChargesCharged.add(feeChargesForInstallment.getAmount()); totalPenaltyChargesCharged = totalPenaltyChargesCharged .add(penaltyChargesForInstallment.getAmount()); totalRepaymentExpected = totalRepaymentExpected.add(feeChargesForInstallment.getAmount()) .add(penaltyChargesForInstallment.getAmount()); loanScheduleModelPeriod.addLoanCharges(feeChargesForInstallment.getAmount(), penaltyChargesForInstallment.getAmount()); } } // this block is to add extra re-payment schedules with interest portion // if the loan not paid with in loan term if (scheduleTillDate != null) { currentDate = scheduleTillDate; } if (isInterestRecalculationRequired(loanApplicationTerms, transactions) && latePaymentMap.size() > 0 && currentDate.isAfter(periodStartDate)) { Money totalInterest = addInterestOnlyRepaymentScheduleForCurrentdate(mc, loanApplicationTerms, holidayDetailDTO, currency, periods, periodStartDate, actualRepaymentDate, instalmentNumber, latePaymentMap, currentDate, loanRepaymentScheduleTransactionProcessor, principalPortionMap, compoundingMap, transactions, installments, loanCharges); totalCumulativeInterest = totalCumulativeInterest.plus(totalInterest); } loanApplicationTerms.resetFixedEmiAmount(); return LoanScheduleModel.from(periods, applicationCurrency, loanTermInDays, principalDisbursed, totalCumulativePrincipal.getAmount(), totalPrincipalPaid, totalCumulativeInterest.getAmount(), totalFeeChargesCharged, totalPenaltyChargesCharged, totalRepaymentExpected, totalOutstanding); }
From source file:org.mifosplatform.portfolio.loanaccount.loanschedule.domain.AbstractLoanScheduleGenerator.java
License:Mozilla Public License
/** * Method calculates interest on not paid outstanding principal and interest * (if compounding is enabled) till current date and adds new repayment * schedule detail/* w w w . j a va2 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, LocalDate periodStartDate, LocalDate actualRepaymentDate, int instalmentNumber, Map<LocalDate, Money> latePaymentMap, final LocalDate currentDate, LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor, final Map<LocalDate, Money> principalPortionMap, TreeMap<LocalDate, Money> compoundingMap, final Collection<RecalculationDetail> transactions, final List<LoanRepaymentScheduleInstallment> installments, Set<LoanCharge> loanCharges) { boolean isFirstRepayment = false; LocalDate startDate = periodStartDate; Money outstanding = Money.zero(currency); Money totalInterest = Money.zero(currency); Money totalCumulativeInterest = Money.zero(currency); Map<LocalDate, Money> disburseDetailsMap = new HashMap<>(); double interestCalculationGraceOnRepaymentPeriodFraction = Double.valueOf(0); int periodNumberTemp = 1; LocalDate lastRestDate = getNextRestScheduleDate(currentDate.minusDays(1), loanApplicationTerms, holidayDetailDTO); do { actualRepaymentDate = this.scheduledDateGenerator.generateNextRepaymentDate(actualRepaymentDate, loanApplicationTerms, isFirstRepayment); int daysInPeriod = Days.daysBetween(periodStartDate, actualRepaymentDate).getDays(); if (actualRepaymentDate.isAfter(currentDate)) { actualRepaymentDate = currentDate; } outstanding = updateOutstandingFromLatePayment(periodStartDate, latePaymentMap, outstanding); Collection<RecalculationDetail> applicableTransactions = getApplicableTransactionsForPeriod( loanApplicationTerms, actualRepaymentDate, transactions); if (!latePaymentMap.isEmpty()) { populateCompoundingDatesInPeriod(periodStartDate, actualRepaymentDate, currentDate, loanApplicationTerms, holidayDetailDTO, compoundingMap, loanCharges, currency); } for (RecalculationDetail detail : applicableTransactions) { if (detail.isProcessed()) { continue; } List<LoanTransaction> currentTransactions = createCurrentTransactionList(detail); if (!periodStartDate.isEqual(detail.getTransactionDate())) { PrincipalInterest principalInterestForThisPeriod = calculatePrincipalInterestComponentsForPeriod( this.paymentPeriodsInOneYearCalculator, interestCalculationGraceOnRepaymentPeriodFraction, totalInterest.zero(), totalInterest.zero(), totalInterest.zero(), totalInterest.zero(), outstanding, loanApplicationTerms, periodNumberTemp, mc, mergeVariationsToMap(principalPortionMap, latePaymentMap, disburseDetailsMap, compoundingMap), compoundingMap, periodStartDate, detail.getTransactionDate(), daysInPeriod); Money interest = principalInterestForThisPeriod.interest(); totalInterest = totalInterest.plus(interest); LoanScheduleModelRepaymentPeriod installment = LoanScheduleModelRepaymentPeriod.repayment( instalmentNumber++, startDate, detail.getTransactionDate(), totalInterest.zero(), totalInterest.zero(), totalInterest, totalInterest.zero(), totalInterest.zero(), totalInterest, true); periods.add(installment); totalCumulativeInterest = totalCumulativeInterest.plus(totalInterest); totalInterest = totalInterest.zero(); addLoanRepaymentScheduleInstallment(installments, installment); periodStartDate = detail.getTransactionDate(); startDate = detail.getTransactionDate(); } loanRepaymentScheduleTransactionProcessor.handleRepaymentSchedule(currentTransactions, currency, installments); updateLatePaymentsToMap(loanApplicationTerms, holidayDetailDTO, currency, latePaymentMap, currentDate, installments, false, lastRestDate, compoundingMap); outstanding = outstanding.zero(); outstanding = updateOutstandingFromLatePayment(periodStartDate, latePaymentMap, outstanding); outstanding = updateBalanceForInterestCalculation(principalPortionMap, periodStartDate, outstanding, false); if (latePaymentMap.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(principalPortionMap, latePaymentMap, disburseDetailsMap, compoundingMap), compoundingMap, periodStartDate, actualRepaymentDate, daysInPeriod); Money interest = principalInterestForThisPeriod.interest(); totalInterest = totalInterest.plus(interest); if (loanApplicationTerms.getInterestRecalculationCompoundingMethod() .isInterestCompoundingEnabled()) { LocalDate compoundingEffectiveDate = getNextCompoundScheduleDate( actualRepaymentDate.minusDays(1), loanApplicationTerms, holidayDetailDTO); latePaymentMap.put(compoundingEffectiveDate, interest); } } periodStartDate = actualRepaymentDate; } while (actualRepaymentDate.isBefore(currentDate) && outstanding.isGreaterThanZero()); if (totalInterest.isGreaterThanZero()) { LoanScheduleModelRepaymentPeriod installment = LoanScheduleModelRepaymentPeriod.repayment( instalmentNumber++, startDate, actualRepaymentDate, totalInterest.zero(), totalInterest.zero(), totalInterest, totalInterest.zero(), totalInterest.zero(), totalInterest, true); periods.add(installment); totalCumulativeInterest = totalCumulativeInterest.plus(totalInterest); } return totalCumulativeInterest; }
From source file:org.mifosplatform.portfolio.loanaccount.loanschedule.domain.AbstractLoanScheduleGenerator.java
License:Mozilla Public License
/** * Method returns the amount payable to close the loan account as of today. *//* w w w .j a v a 2 s . co m*/ @Override public LoanRepaymentScheduleInstallment calculatePrepaymentAmount( List<LoanRepaymentScheduleInstallment> installments, MonetaryCurrency currency, final LocalDate onDate, LoanApplicationTerms loanApplicationTerms, MathContext mc, Set<LoanCharge> charges, final HolidayDetailDTO holidayDetailDTO, final List<LoanTransaction> loanTransactions, final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor) { LocalDate calculateTill = onDate; if (loanApplicationTerms.getPreClosureInterestCalculationStrategy().calculateTillRestFrequencyEnabled()) { calculateTill = getNextRestScheduleDate(onDate.minusDays(1), loanApplicationTerms, holidayDetailDTO); } Collection<RecalculationDetail> recalculationDetails = new ArrayList<>(); for (LoanTransaction loanTransaction : loanTransactions) { recalculationDetails.add(new RecalculationDetail(loanTransaction.getTransactionDate(), LoanTransaction.copyTransactionProperties(loanTransaction))); } LoanScheduleModel loanScheduleModel = generate(mc, loanApplicationTerms, charges, holidayDetailDTO, recalculationDetails, loanRepaymentScheduleTransactionProcessor, calculateTill); installments = fetchInstallmentsFromScheduleModel(loanScheduleModel); loanRepaymentScheduleTransactionProcessor.handleTransaction( loanApplicationTerms.getExpectedDisbursementDate(), loanTransactions, currency, installments, 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 : installments) { 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); }