List of usage examples for org.joda.time Days daysBetween
public static Days daysBetween(ReadablePartial start, ReadablePartial end)
Days
representing the number of whole days between the two specified partial datetimes. From source file:org.mifosplatform.portfolio.loanaccount.loanschedule.data.LoanSchedulePeriodData.java
License:Mozilla Public License
private LoanSchedulePeriodData(final Integer periodNumber, final LocalDate fromDate, final LocalDate dueDate, final BigDecimal principalOriginalDue, final BigDecimal principalOutstanding, final BigDecimal interestDueOnPrincipalOutstanding, final BigDecimal feeChargesDueForPeriod, final BigDecimal penaltyChargesDueForPeriod, final BigDecimal totalDueForPeriod) { this.period = periodNumber; this.fromDate = fromDate; this.dueDate = dueDate; this.obligationsMetOnDate = null; this.complete = null; if (fromDate != null) { this.daysInPeriod = Days.daysBetween(this.fromDate, this.dueDate).getDays(); } else {// w w w.j ava2 s . c o m this.daysInPeriod = null; } this.principalDisbursed = null; this.principalOriginalDue = principalOriginalDue; this.principalDue = principalOriginalDue; this.principalPaid = null; this.principalWrittenOff = null; this.principalOutstanding = principalOriginalDue; this.principalLoanBalanceOutstanding = principalOutstanding; this.interestOriginalDue = interestDueOnPrincipalOutstanding; this.interestDue = interestDueOnPrincipalOutstanding; this.interestPaid = null; this.interestWaived = null; this.interestWrittenOff = null; this.interestOutstanding = interestDueOnPrincipalOutstanding; this.feeChargesDue = feeChargesDueForPeriod; this.feeChargesPaid = null; this.feeChargesWaived = null; this.feeChargesWrittenOff = null; this.feeChargesOutstanding = null; this.penaltyChargesDue = penaltyChargesDueForPeriod; this.penaltyChargesPaid = null; this.penaltyChargesWaived = null; this.penaltyChargesWrittenOff = null; this.penaltyChargesOutstanding = null; this.totalOriginalDueForPeriod = totalDueForPeriod; this.totalDueForPeriod = totalDueForPeriod; this.totalPaidForPeriod = BigDecimal.ZERO; this.totalPaidInAdvanceForPeriod = null; this.totalPaidLateForPeriod = null; this.totalWaivedForPeriod = null; this.totalWrittenOffForPeriod = null; this.totalOutstandingForPeriod = totalDueForPeriod; this.totalActualCostOfLoanForPeriod = interestDueOnPrincipalOutstanding.add(feeChargesDueForPeriod); if (dueDate.isBefore(new LocalDate())) { this.totalOverdue = this.totalOutstandingForPeriod; } else { this.totalOverdue = null; } }
From source file:org.mifosplatform.portfolio.loanaccount.loanschedule.data.LoanSchedulePeriodData.java
License:Mozilla Public License
private LoanSchedulePeriodData(final Integer periodNumber, final LocalDate fromDate, final LocalDate dueDate, final LocalDate obligationsMetOnDate, final boolean complete, final BigDecimal principalOriginalDue, final BigDecimal principalPaid, final BigDecimal principalWrittenOff, final BigDecimal principalOutstanding, final BigDecimal principalLoanBalanceOutstanding, final BigDecimal interestDueOnPrincipalOutstanding, final BigDecimal interestPaid, final BigDecimal interestWaived, final BigDecimal interestWrittenOff, final BigDecimal interestOutstanding, final BigDecimal feeChargesDue, final BigDecimal feeChargesPaid, final BigDecimal feeChargesWaived, final BigDecimal feeChargesWrittenOff, final BigDecimal feeChargesOutstanding, final BigDecimal penaltyChargesDue, final BigDecimal penaltyChargesPaid, final BigDecimal penaltyChargesWaived, final BigDecimal penaltyChargesWrittenOff, final BigDecimal penaltyChargesOutstanding, final BigDecimal totalDueForPeriod, final BigDecimal totalPaid, final BigDecimal totalPaidInAdvanceForPeriod, final BigDecimal totalPaidLateForPeriod, final BigDecimal totalWaived, final BigDecimal totalWrittenOff, final BigDecimal totalOutstanding, final BigDecimal totalActualCostOfLoanForPeriod) { this.period = periodNumber; this.fromDate = fromDate; this.dueDate = dueDate; this.obligationsMetOnDate = obligationsMetOnDate; this.complete = complete; if (fromDate != null) { this.daysInPeriod = Days.daysBetween(this.fromDate, this.dueDate).getDays(); } else {// w w w.ja v a 2s. co m this.daysInPeriod = null; } this.principalDisbursed = null; this.principalOriginalDue = principalOriginalDue; this.principalDue = principalOriginalDue; this.principalPaid = principalPaid; this.principalWrittenOff = principalWrittenOff; this.principalOutstanding = principalOutstanding; this.principalLoanBalanceOutstanding = principalLoanBalanceOutstanding; this.interestOriginalDue = interestDueOnPrincipalOutstanding; this.interestDue = interestDueOnPrincipalOutstanding; this.interestPaid = interestPaid; this.interestWaived = interestWaived; this.interestWrittenOff = interestWrittenOff; this.interestOutstanding = interestOutstanding; this.feeChargesDue = feeChargesDue; this.feeChargesPaid = feeChargesPaid; this.feeChargesWaived = feeChargesWaived; this.feeChargesWrittenOff = feeChargesWrittenOff; this.feeChargesOutstanding = feeChargesOutstanding; this.penaltyChargesDue = penaltyChargesDue; this.penaltyChargesPaid = penaltyChargesPaid; this.penaltyChargesWaived = penaltyChargesWaived; this.penaltyChargesWrittenOff = penaltyChargesWrittenOff; this.penaltyChargesOutstanding = penaltyChargesOutstanding; this.totalOriginalDueForPeriod = totalDueForPeriod; this.totalDueForPeriod = totalDueForPeriod; this.totalPaidForPeriod = totalPaid; this.totalPaidInAdvanceForPeriod = totalPaidInAdvanceForPeriod; this.totalPaidLateForPeriod = totalPaidLateForPeriod; this.totalWaivedForPeriod = totalWaived; this.totalWrittenOffForPeriod = totalWrittenOff; this.totalOutstandingForPeriod = totalOutstanding; this.totalActualCostOfLoanForPeriod = totalActualCostOfLoanForPeriod; if (dueDate.isBefore(new LocalDate())) { this.totalOverdue = this.totalOutstandingForPeriod; } else { this.totalOverdue = null; } }
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 www . j a v a 2s . c o m*/ 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/*from w w w .j a v a 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, 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
@Override public LoanRescheduleModel reschedule(final MathContext mathContext, final LoanRescheduleRequest loanRescheduleRequest, final ApplicationCurrency applicationCurrency, final HolidayDetailDTO holidayDetailDTO, final CalendarInstance restCalendarInstance, final CalendarInstance compoundingCalendarInstance) { final Loan loan = loanRescheduleRequest.getLoan(); final LoanSummary loanSummary = loan.getSummary(); final LoanProductMinimumRepaymentScheduleRelatedDetail loanProductRelatedDetail = loan .getLoanRepaymentScheduleDetail(); final MonetaryCurrency currency = loanProductRelatedDetail.getCurrency(); // create an archive of the current loan schedule installments Collection<LoanRepaymentScheduleHistory> loanRepaymentScheduleHistoryList = null; // get the initial list of repayment installments List<LoanRepaymentScheduleInstallment> repaymentScheduleInstallments = loan .getRepaymentScheduleInstallments(); // sort list by installment number in ASC order Collections.sort(repaymentScheduleInstallments, LoanRepaymentScheduleInstallment.installmentNumberComparator); final Collection<LoanRescheduleModelRepaymentPeriod> periods = new ArrayList<>(); Money outstandingLoanBalance = loan.getPrincpal(); for (LoanRepaymentScheduleInstallment repaymentScheduleInstallment : repaymentScheduleInstallments) { Integer oldPeriodNumber = repaymentScheduleInstallment.getInstallmentNumber(); LocalDate fromDate = repaymentScheduleInstallment.getFromDate(); LocalDate dueDate = repaymentScheduleInstallment.getDueDate(); Money principalDue = repaymentScheduleInstallment.getPrincipal(currency); Money interestDue = repaymentScheduleInstallment.getInterestCharged(currency); Money feeChargesDue = repaymentScheduleInstallment.getFeeChargesCharged(currency); Money penaltyChargesDue = repaymentScheduleInstallment.getPenaltyChargesCharged(currency); Money totalDue = principalDue.plus(interestDue).plus(feeChargesDue).plus(penaltyChargesDue); outstandingLoanBalance = outstandingLoanBalance.minus(principalDue); LoanRescheduleModelRepaymentPeriod period = LoanRescheduleModelRepaymentPeriod.instance(oldPeriodNumber, oldPeriodNumber, fromDate, dueDate, principalDue, outstandingLoanBalance, interestDue, feeChargesDue, penaltyChargesDue, totalDue, false); periods.add(period);/*from www . jav a 2 s.c om*/ } Money outstandingBalance = loan.getPrincpal(); Money totalCumulativePrincipal = Money.zero(currency); Money totalCumulativeInterest = Money.zero(currency); Money actualTotalCumulativeInterest = Money.zero(currency); Money totalOutstandingInterestPaymentDueToGrace = Money.zero(currency); Money totalPrincipalBeforeReschedulePeriod = Money.zero(currency); LocalDate installmentDueDate = null; LocalDate adjustedInstallmentDueDate = null; LocalDate installmentFromDate = null; Integer rescheduleFromInstallmentNo = defaultToZeroIfNull( loanRescheduleRequest.getRescheduleFromInstallment()); Integer installmentNumber = rescheduleFromInstallmentNo; Integer graceOnPrincipal = defaultToZeroIfNull(loanRescheduleRequest.getGraceOnPrincipal()); Integer graceOnInterest = defaultToZeroIfNull(loanRescheduleRequest.getGraceOnInterest()); Integer extraTerms = defaultToZeroIfNull(loanRescheduleRequest.getExtraTerms()); final boolean recalculateInterest = loanRescheduleRequest.getRecalculateInterest(); Integer numberOfRepayments = repaymentScheduleInstallments.size(); Integer rescheduleNumberOfRepayments = numberOfRepayments; final Money principal = loan.getPrincpal(); final Money totalPrincipalOutstanding = Money.of(currency, loanSummary.getTotalPrincipalOutstanding()); LocalDate adjustedDueDate = loanRescheduleRequest.getAdjustedDueDate(); BigDecimal newInterestRate = loanRescheduleRequest.getInterestRate(); int loanTermInDays = Integer.valueOf(0); if (rescheduleFromInstallmentNo > 0) { // this will hold the loan repayment installment that is before the // reschedule start installment // (rescheduleFrominstallment) LoanRepaymentScheduleInstallment previousInstallment = null; // get the install number of the previous installment int previousInstallmentNo = rescheduleFromInstallmentNo - 1; // only fetch the installment if the number is greater than 0 if (previousInstallmentNo > 0) { previousInstallment = loan.fetchRepaymentScheduleInstallment(previousInstallmentNo); } LoanRepaymentScheduleInstallment firstInstallment = loan.fetchRepaymentScheduleInstallment(1); // the "installment from date" is equal to the due date of the // previous installment, if it exists if (previousInstallment != null) { installmentFromDate = previousInstallment.getDueDate(); } else { installmentFromDate = firstInstallment.getFromDate(); } installmentDueDate = installmentFromDate; LocalDate periodStartDateApplicableForInterest = installmentFromDate; Integer periodNumber = 1; outstandingLoanBalance = loan.getPrincpal(); for (LoanRescheduleModelRepaymentPeriod period : periods) { if (period.periodDueDate().isBefore(loanRescheduleRequest.getRescheduleFromDate())) { totalPrincipalBeforeReschedulePeriod = totalPrincipalBeforeReschedulePeriod .plus(period.principalDue()); actualTotalCumulativeInterest = actualTotalCumulativeInterest.plus(period.interestDue()); rescheduleNumberOfRepayments--; outstandingLoanBalance = outstandingLoanBalance.minus(period.principalDue()); outstandingBalance = outstandingBalance.minus(period.principalDue()); } } while (graceOnPrincipal > 0 || graceOnInterest > 0) { LoanRescheduleModelRepaymentPeriod period = LoanRescheduleModelRepaymentPeriod.instance(0, 0, new LocalDate(), new LocalDate(), Money.zero(currency), Money.zero(currency), Money.zero(currency), Money.zero(currency), Money.zero(currency), Money.zero(currency), true); periods.add(period); if (graceOnPrincipal > 0) { graceOnPrincipal--; } if (graceOnInterest > 0) { graceOnInterest--; } rescheduleNumberOfRepayments++; numberOfRepayments++; } while (extraTerms > 0) { LoanRescheduleModelRepaymentPeriod period = LoanRescheduleModelRepaymentPeriod.instance(0, 0, new LocalDate(), new LocalDate(), Money.zero(currency), Money.zero(currency), Money.zero(currency), Money.zero(currency), Money.zero(currency), Money.zero(currency), true); periods.add(period); extraTerms--; rescheduleNumberOfRepayments++; numberOfRepayments++; } // get the loan application terms from the Loan object final LoanApplicationTerms loanApplicationTerms = loan.getLoanApplicationTerms(applicationCurrency, restCalendarInstance, compoundingCalendarInstance); // update the number of repayments loanApplicationTerms.updateNumberOfRepayments(numberOfRepayments); LocalDate loanEndDate = this.scheduledDateGenerator.getLastRepaymentDate(loanApplicationTerms, holidayDetailDTO); loanApplicationTerms.updateLoanEndDate(loanEndDate); if (newInterestRate != null) { loanApplicationTerms.updateAnnualNominalInterestRate(newInterestRate); loanApplicationTerms.updateInterestRatePerPeriod(newInterestRate); } graceOnPrincipal = defaultToZeroIfNull(loanRescheduleRequest.getGraceOnPrincipal()); graceOnInterest = defaultToZeroIfNull(loanRescheduleRequest.getGraceOnInterest()); loanApplicationTerms.updateInterestPaymentGrace(graceOnInterest); loanApplicationTerms.updatePrincipalGrace(graceOnPrincipal); loanApplicationTerms.setPrincipal(totalPrincipalOutstanding); loanApplicationTerms.updateNumberOfRepayments(rescheduleNumberOfRepayments); loanApplicationTerms.updateLoanTermFrequency(rescheduleNumberOfRepayments); loanApplicationTerms.updateInterestChargedFromDate(periodStartDateApplicableForInterest); Money totalInterestChargedForFullLoanTerm = loanApplicationTerms .calculateTotalInterestCharged(this.paymentPeriodsInOneYearCalculator, mathContext); if (!recalculateInterest && newInterestRate == null) { totalInterestChargedForFullLoanTerm = Money.of(currency, loanSummary.getTotalInterestCharged()); totalInterestChargedForFullLoanTerm = totalInterestChargedForFullLoanTerm .minus(actualTotalCumulativeInterest); loanApplicationTerms.updateTotalInterestDue(totalInterestChargedForFullLoanTerm); } for (LoanRescheduleModelRepaymentPeriod period : periods) { if (period.periodDueDate().isEqual(loanRescheduleRequest.getRescheduleFromDate()) || period.periodDueDate().isAfter(loanRescheduleRequest.getRescheduleFromDate()) || period.isNew()) { installmentDueDate = this.scheduledDateGenerator.generateNextRepaymentDate(installmentDueDate, loanApplicationTerms, false); if (adjustedDueDate != null && periodNumber == 1) { installmentDueDate = adjustedDueDate; } adjustedInstallmentDueDate = this.scheduledDateGenerator.adjustRepaymentDate(installmentDueDate, loanApplicationTerms, holidayDetailDTO); final int daysInInstallment = Days.daysBetween(installmentFromDate, adjustedInstallmentDueDate) .getDays(); period.updatePeriodNumber(installmentNumber); period.updatePeriodFromDate(installmentFromDate); period.updatePeriodDueDate(adjustedInstallmentDueDate); double interestCalculationGraceOnRepaymentPeriodFraction = this.paymentPeriodsInOneYearCalculator .calculatePortionOfRepaymentPeriodInterestChargingGrace( periodStartDateApplicableForInterest, adjustedInstallmentDueDate, periodStartDateApplicableForInterest, loanApplicationTerms.getLoanTermPeriodFrequencyType(), loanApplicationTerms.getRepaymentEvery()); // ========================= Calculate the interest due // ======================================== // change the principal to => Principal Disbursed - Total // Principal Paid // interest calculation is always based on the total // principal outstanding loanApplicationTerms.setPrincipal(totalPrincipalOutstanding); // determine the interest & principal for the period PrincipalInterest principalInterestForThisPeriod = calculatePrincipalInterestComponentsForPeriod( this.paymentPeriodsInOneYearCalculator, interestCalculationGraceOnRepaymentPeriodFraction, totalCumulativePrincipal, totalCumulativeInterest, totalInterestChargedForFullLoanTerm, totalOutstandingInterestPaymentDueToGrace, outstandingBalance, loanApplicationTerms, periodNumber, mathContext, null, null, installmentFromDate, adjustedInstallmentDueDate, daysInInstallment); // update the interest due for the period period.updateInterestDue(principalInterestForThisPeriod.interest()); // ============================================================================================= // ========================== Calculate the principal due // ====================================== // change the principal to => Principal Disbursed - Total // cumulative Principal Amount before the reschedule // installment loanApplicationTerms.setPrincipal(principal.minus(totalPrincipalBeforeReschedulePeriod)); principalInterestForThisPeriod = calculatePrincipalInterestComponentsForPeriod( this.paymentPeriodsInOneYearCalculator, interestCalculationGraceOnRepaymentPeriodFraction, totalCumulativePrincipal, totalCumulativeInterest, totalInterestChargedForFullLoanTerm, totalOutstandingInterestPaymentDueToGrace, outstandingBalance, loanApplicationTerms, periodNumber, mathContext, null, null, installmentFromDate, adjustedInstallmentDueDate, daysInInstallment); period.updatePrincipalDue(principalInterestForThisPeriod.principal()); // ============================================================================================== outstandingLoanBalance = outstandingLoanBalance.minus(period.principalDue()); period.updateOutstandingLoanBalance(outstandingLoanBalance); Money principalDue = Money.of(currency, period.principalDue()); Money interestDue = Money.of(currency, period.interestDue()); if (principalDue.isZero() && interestDue.isZero()) { period.updateFeeChargesDue(Money.zero(currency)); period.updatePenaltyChargesDue(Money.zero(currency)); } Money feeChargesDue = Money.of(currency, period.feeChargesDue()); Money penaltyChargesDue = Money.of(currency, period.penaltyChargesDue()); Money totalDue = principalDue.plus(interestDue).plus(feeChargesDue).plus(penaltyChargesDue); period.updateTotalDue(totalDue); // update cumulative fields for principal & interest totalCumulativePrincipal = totalCumulativePrincipal.plus(period.principalDue()); totalCumulativeInterest = totalCumulativeInterest.plus(period.interestDue()); actualTotalCumulativeInterest = actualTotalCumulativeInterest.plus(period.interestDue()); totalOutstandingInterestPaymentDueToGrace = principalInterestForThisPeriod .interestPaymentDueToGrace(); installmentFromDate = adjustedInstallmentDueDate; installmentNumber++; periodNumber++; loanTermInDays += daysInInstallment; outstandingBalance = outstandingBalance.minus(period.principalDue()); } } } final Money totalRepaymentExpected = principal // get the loan Principal // amount .plus(actualTotalCumulativeInterest) // add the actual total // cumulative interest .plus(loanSummary.getTotalFeeChargesCharged()) // add the total // fees charged .plus(loanSummary.getTotalPenaltyChargesCharged()); // finally // add the // total // penalty // charged return LoanRescheduleModel.instance(periods, loanRepaymentScheduleHistoryList, applicationCurrency, loanTermInDays, loan.getPrincpal(), loan.getPrincpal().getAmount(), loanSummary.getTotalPrincipalRepaid(), actualTotalCumulativeInterest.getAmount(), loanSummary.getTotalFeeChargesCharged(), loanSummary.getTotalPenaltyChargesCharged(), totalRepaymentExpected.getAmount(), loanSummary.getTotalOutstanding()); }
From source file:org.mifosplatform.portfolio.loanaccount.loanschedule.domain.DecliningBalanceInterestLoanScheduleGenerator.java
License:Mozilla Public License
@Override public PrincipalInterest calculatePrincipalInterestComponentsForPeriod( final PaymentPeriodsInOneYearCalculator calculator, final double interestCalculationGraceOnRepaymentPeriodFraction, final Money totalCumulativePrincipal, @SuppressWarnings("unused") final Money totalCumulativeInterest, @SuppressWarnings("unused") final Money totalInterestDueForLoan, final Money cumulatingInterestPaymentDueToGrace, final Money outstandingBalance, final LoanApplicationTerms loanApplicationTerms, final int periodNumber, final MathContext mc, final TreeMap<LocalDate, Money> principalVariation, final Map<LocalDate, Money> compoundingMap, final LocalDate periodStartDate, final LocalDate periodEndDate, final int daysForInterestInFullPeriod) { LocalDate interestStartDate = periodStartDate; Money interestForThisInstallment = totalCumulativePrincipal.zero(); Money compoundedMoney = totalCumulativePrincipal.zero(); Money compoundedInterest = totalCumulativePrincipal.zero(); Money balanceForInterestCalculation = outstandingBalance; Money cumulatingInterestDueToGrace = cumulatingInterestPaymentDueToGrace; final int daysInPeriodApplicableForInterest = Days.daysBetween(periodStartDate, periodEndDate).getDays(); if (principalVariation != null) { // identifies rest date after current date for reducing all // compounding // values LocalDate compoundingEndDate = principalVariation.ceilingKey(DateUtils.getLocalDateOfTenant()); if (compoundingEndDate == null) { compoundingEndDate = DateUtils.getLocalDateOfTenant(); }//w w w . ja v a2s. c om for (Map.Entry<LocalDate, Money> principal : principalVariation.entrySet()) { if (!principal.getKey().isAfter(periodEndDate)) { int interestForDays = Days.daysBetween(interestStartDate, principal.getKey()).getDays(); if (interestForDays > 0) { final PrincipalInterest result = loanApplicationTerms.calculateTotalInterestForPeriod( calculator, interestCalculationGraceOnRepaymentPeriodFraction, periodNumber, mc, cumulatingInterestDueToGrace, interestForDays, balanceForInterestCalculation); if (loanApplicationTerms.getInterestCalculationPeriodMethod().isDaily()) { interestForThisInstallment = interestForThisInstallment.plus(result.interest()); cumulatingInterestDueToGrace = result.interestPaymentDueToGrace(); } else { interestForThisInstallment = interestForThisInstallment.plus(calculateInterestForDays( daysForInterestInFullPeriod, result.interest().getAmount(), interestForDays)); cumulatingInterestDueToGrace = cumulatingInterestDueToGrace .plus(calculateInterestForDays( daysForInterestInFullPeriod, result.interestPaymentDueToGrace() .minus(cumulatingInterestDueToGrace).getAmount(), interestForDays)); } interestStartDate = principal.getKey(); } Money compoundFee = totalCumulativePrincipal.zero(); if (compoundingMap.containsKey(principal.getKey())) { Money interestToBeCompounded = totalCumulativePrincipal.zero(); // for interest compounding if (loanApplicationTerms.getInterestRecalculationCompoundingMethod() .isInterestCompoundingEnabled()) { interestToBeCompounded = interestForThisInstallment.minus(compoundedInterest); balanceForInterestCalculation = balanceForInterestCalculation .plus(interestToBeCompounded); compoundedInterest = interestForThisInstallment; } // fee compounding will be done after calculation compoundFee = compoundingMap.get(principal.getKey()); compoundedMoney = compoundedMoney.plus(interestToBeCompounded).plus(compoundFee); } balanceForInterestCalculation = balanceForInterestCalculation.plus(principal.getValue()) .plus(compoundFee); } } if (!periodEndDate.isBefore(compoundingEndDate)) { balanceForInterestCalculation = balanceForInterestCalculation.minus(compoundedMoney); compoundingMap.clear(); } else if (compoundedMoney.isGreaterThanZero()) { compoundingMap.put(periodEndDate, compoundedMoney); compoundingMap.put(compoundingEndDate, compoundedMoney.negated()); clearMapDetails(periodEndDate, compoundingMap); } } int interestForDays = Days.daysBetween(interestStartDate, periodEndDate).getDays(); final PrincipalInterest result = loanApplicationTerms.calculateTotalInterestForPeriod(calculator, interestCalculationGraceOnRepaymentPeriodFraction, periodNumber, mc, cumulatingInterestDueToGrace, interestForDays, balanceForInterestCalculation); if (loanApplicationTerms.getInterestCalculationPeriodMethod().isDaily()) { interestForThisInstallment = interestForThisInstallment.plus(result.interest()); cumulatingInterestDueToGrace = result.interestPaymentDueToGrace(); } else { interestForThisInstallment = interestForThisInstallment.plus(calculateInterestForDays( daysForInterestInFullPeriod, result.interest().getAmount(), interestForDays)); cumulatingInterestDueToGrace = cumulatingInterestDueToGrace .plus(calculateInterestForDays(daysForInterestInFullPeriod, result.interestPaymentDueToGrace().minus(cumulatingInterestDueToGrace).getAmount(), interestForDays)); } Money interestForPeriod = interestForThisInstallment; if (interestForPeriod.isGreaterThanZero()) { interestForPeriod = interestForPeriod.minus(cumulatingInterestPaymentDueToGrace); } else { interestForPeriod = cumulatingInterestDueToGrace.minus(cumulatingInterestPaymentDueToGrace); } Money principalForThisInstallment = loanApplicationTerms.calculateTotalPrincipalForPeriod(calculator, daysInPeriodApplicableForInterest, outstandingBalance, periodNumber, mc, interestForPeriod); // update cumulative fields for principal & interest final Money interestBroughtFowardDueToGrace = cumulatingInterestDueToGrace; final Money totalCumulativePrincipalToDate = totalCumulativePrincipal.plus(principalForThisInstallment); // adjust if needed principalForThisInstallment = loanApplicationTerms.adjustPrincipalIfLastRepaymentPeriod( principalForThisInstallment, totalCumulativePrincipalToDate, periodNumber); return new PrincipalInterest(principalForThisInstallment, interestForThisInstallment, interestBroughtFowardDueToGrace); }
From source file:org.mifosplatform.portfolio.loanaccount.loanschedule.domain.EqualInstallmentsAmortizationLoanScheduleGenerator.java
License:Mozilla Public License
@Override public LoanScheduleData generate(final ApplicationCurrency currency, final LoanProductRelatedDetail loanScheduleInfo, final LocalDate disbursementDate, final LocalDate interestCalculatedFrom, final BigDecimal periodInterestRateForRepaymentPeriod, final LocalDate idealDisbursementDateBasedOnFirstRepaymentDate, final List<LocalDate> scheduledDates, final Set<LoanCharge> loanCharges) { final Collection<LoanSchedulePeriodData> periods = new ArrayList<LoanSchedulePeriodData>(); // determine 'total payment' for each repayment based on pmt function // (and hence the total due overall) final MonetaryCurrency monetaryCurrency = loanScheduleInfo.getPrincipal().getCurrency(); final Money totalDuePerInstallment = this.pmtCalculator.calculatePaymentForOnePeriodFrom(loanScheduleInfo, periodInterestRateForRepaymentPeriod, monetaryCurrency); final Money totalRepaymentDueForLoanTerm = this.pmtCalculator.calculateTotalRepaymentFrom(loanScheduleInfo, periodInterestRateForRepaymentPeriod, monetaryCurrency); Money totalInterestDue = totalRepaymentDueForLoanTerm.minus(loanScheduleInfo.getPrincipal()); Money outstandingBalance = loanScheduleInfo.getPrincipal(); Money principalDisbursed = loanScheduleInfo.getPrincipal(); Money totalPrincipal = Money.zero(monetaryCurrency); Money totalInterest = Money.zero(monetaryCurrency); double interestCalculationGraceOnRepaymentPeriodFraction = this.paymentPeriodsInOneYearCalculator .calculateRepaymentPeriodAsAFractionOfDays(loanScheduleInfo.getRepaymentPeriodFrequencyType(), loanScheduleInfo.getRepayEvery(), interestCalculatedFrom, scheduledDates, idealDisbursementDateBasedOnFirstRepaymentDate); BigDecimal chargesDueAtTimeOfDisbursement = BigDecimal.ZERO; for (LoanCharge loanCharge : loanCharges) { if (loanCharge.isDueAtDisbursement()) { chargesDueAtTimeOfDisbursement = chargesDueAtTimeOfDisbursement.add(loanCharge.amount()); }// www.java2 s .c o m } // create entries of disbursement period on loan schedule final LoanSchedulePeriodData disbursementPeriod = LoanSchedulePeriodData.disbursementOnlyPeriod( disbursementDate, principalDisbursed.getAmount(), chargesDueAtTimeOfDisbursement, false); periods.add(disbursementPeriod); int loanTermInDays = Integer.valueOf(0); BigDecimal totalPrincipalDisbursed = principalDisbursed.getAmount(); BigDecimal totalPrincipalExpected = BigDecimal.ZERO; BigDecimal totalPrincipalPaid = BigDecimal.ZERO; BigDecimal totalInterestCharged = BigDecimal.ZERO; BigDecimal totalFeeChargesCharged = chargesDueAtTimeOfDisbursement; BigDecimal totalPenaltyChargesCharged = BigDecimal.ZERO; BigDecimal totalWaived = BigDecimal.ZERO; BigDecimal totalWrittenOff = BigDecimal.ZERO; BigDecimal totalRepaymentExpected = chargesDueAtTimeOfDisbursement; BigDecimal totalRepayment = BigDecimal.ZERO; BigDecimal totalOutstanding = BigDecimal.ZERO; LocalDate startDate = disbursementDate; int periodNumber = 1; for (LocalDate scheduledDueDate : scheduledDates) { // number of days from startDate to this scheduledDate int daysInPeriod = Days.daysBetween(startDate, scheduledDueDate).getDays(); Money interestForInstallment = this.periodicInterestRateCalculator.calculateInterestOn( outstandingBalance, periodInterestRateForRepaymentPeriod, daysInPeriod, loanScheduleInfo); Money principalForInstallment = this.periodicInterestRateCalculator .calculatePrincipalOn(totalDuePerInstallment, interestForInstallment, loanScheduleInfo); if (interestCalculationGraceOnRepaymentPeriodFraction >= Integer.valueOf(1).doubleValue()) { Money graceOnInterestForRepaymentPeriod = interestForInstallment; interestForInstallment = interestForInstallment.minus(graceOnInterestForRepaymentPeriod); totalInterestDue = totalInterestDue.minus(graceOnInterestForRepaymentPeriod); interestCalculationGraceOnRepaymentPeriodFraction = interestCalculationGraceOnRepaymentPeriodFraction - Integer.valueOf(1).doubleValue(); } else if (interestCalculationGraceOnRepaymentPeriodFraction > Double.valueOf("0.25") && interestCalculationGraceOnRepaymentPeriodFraction < Integer.valueOf(1).doubleValue()) { Money graceOnInterestForRepaymentPeriod = interestForInstallment .multipliedBy(interestCalculationGraceOnRepaymentPeriodFraction); interestForInstallment = interestForInstallment.minus(graceOnInterestForRepaymentPeriod); totalInterestDue = totalInterestDue.minus(graceOnInterestForRepaymentPeriod); interestCalculationGraceOnRepaymentPeriodFraction = Double.valueOf("0"); } totalPrincipal = totalPrincipal.plus(principalForInstallment); totalInterest = totalInterest.plus(interestForInstallment); if (periodNumber == loanScheduleInfo.getNumberOfRepayments()) { Money principalDifference = totalPrincipal.minus(loanScheduleInfo.getPrincipal()); if (principalDifference.isLessThanZero()) { principalForInstallment = principalForInstallment.plus(principalDifference.abs()); } else if (principalDifference.isGreaterThanZero()) { principalForInstallment = principalForInstallment.minus(principalDifference.abs()); } final Money interestDifference = totalInterest.minus(totalInterestDue); if (interestDifference.isLessThanZero()) { interestForInstallment = interestForInstallment.plus(interestDifference.abs()); } else if (interestDifference.isGreaterThanZero()) { interestForInstallment = interestForInstallment.minus(interestDifference.abs()); } } outstandingBalance = outstandingBalance.minus(principalForInstallment); final Money feeChargesForInstallment = cumulativeFeeChargesDueWithin(startDate, scheduledDueDate, loanCharges, monetaryCurrency); final Money penaltyChargesForInstallment = cumulativePenaltyChargesDueWithin(startDate, scheduledDueDate, loanCharges, monetaryCurrency); final Money totalInstallmentDue = principalForInstallment.plus(interestForInstallment) .plus(feeChargesForInstallment).plus(penaltyChargesForInstallment); LoanSchedulePeriodData installment = LoanSchedulePeriodData.repaymentOnlyPeriod(periodNumber, startDate, scheduledDueDate, principalForInstallment.getAmount(), outstandingBalance.getAmount(), interestForInstallment.getAmount(), feeChargesForInstallment.getAmount(), penaltyChargesForInstallment.getAmount(), totalInstallmentDue.getAmount()); periods.add(installment); // handle cumulative fields loanTermInDays += daysInPeriod; totalPrincipalExpected = totalPrincipalExpected.add(principalForInstallment.getAmount()); totalInterestCharged = totalInterestCharged.add(interestForInstallment.getAmount()); totalFeeChargesCharged = totalFeeChargesCharged.add(feeChargesForInstallment.getAmount()); totalPenaltyChargesCharged = totalPenaltyChargesCharged.add(penaltyChargesForInstallment.getAmount()); totalRepaymentExpected = totalRepaymentExpected.add(totalInstallmentDue.getAmount()); startDate = scheduledDueDate; periodNumber++; } final CurrencyData currencyData = new CurrencyData(currency.getCode(), currency.getName(), monetaryCurrency.getDigitsAfterDecimal(), currency.getDisplaySymbol(), currency.getNameCode()); return new LoanScheduleData(currencyData, periods, loanTermInDays, totalPrincipalDisbursed, totalPrincipalExpected, totalPrincipalPaid, totalInterestCharged, totalFeeChargesCharged, totalPenaltyChargesCharged, totalWaived, totalWrittenOff, totalRepaymentExpected, totalRepayment, totalOutstanding); }
From source file:org.mifosplatform.portfolio.loanaccount.loanschedule.domain.EqualPrincipalAmortizationLoanScheduleGenerator.java
License:Mozilla Public License
@Override public LoanScheduleData generate(final ApplicationCurrency currency, final LoanProductRelatedDetail loanScheduleInfo, final LocalDate disbursementDate, final LocalDate interestCalculatedFrom, final BigDecimal periodInterestRateForRepaymentPeriod, final LocalDate idealDisbursementDateBasedOnFirstRepaymentDate, final List<LocalDate> scheduledDates, final Set<LoanCharge> loanCharges) { final Collection<LoanSchedulePeriodData> periods = new ArrayList<LoanSchedulePeriodData>(); // 1. we know 'principal' must be equal over repayments so use this to // calculate interest and thus total repayment due. final MonetaryCurrency monetaryCurrency = loanScheduleInfo.getPrincipal().getCurrency(); Money totalDuePerInstallment = Money.zero(monetaryCurrency); Money outstandingBalance = loanScheduleInfo.getPrincipal(); Money principalDisbursed = loanScheduleInfo.getPrincipal(); Money totalPrincipal = Money.zero(monetaryCurrency); Money totalInterest = Money.zero(monetaryCurrency); double interestCalculationGraceOnRepaymentPeriodFraction = this.paymentPeriodsInOneYearCalculator .calculateRepaymentPeriodAsAFractionOfDays(loanScheduleInfo.getRepaymentPeriodFrequencyType(), loanScheduleInfo.getRepayEvery(), interestCalculatedFrom, scheduledDates, idealDisbursementDateBasedOnFirstRepaymentDate); BigDecimal chargesDueAtTimeOfDisbursement = BigDecimal.ZERO; for (LoanCharge loanCharge : loanCharges) { if (loanCharge.isDueAtDisbursement()) { chargesDueAtTimeOfDisbursement = chargesDueAtTimeOfDisbursement.add(loanCharge.amount()); }/* w w w. j av a 2 s. co m*/ } // create entries of disbursement period on loan schedule final LoanSchedulePeriodData disbursementPeriod = LoanSchedulePeriodData.disbursementOnlyPeriod( disbursementDate, principalDisbursed.getAmount(), chargesDueAtTimeOfDisbursement, false); periods.add(disbursementPeriod); int loanTermInDays = Integer.valueOf(0); BigDecimal totalPrincipalDisbursed = principalDisbursed.getAmount(); BigDecimal totalPrincipalExpected = BigDecimal.ZERO; BigDecimal totalPrincipalPaid = BigDecimal.ZERO; BigDecimal totalInterestCharged = BigDecimal.ZERO; BigDecimal totalFeeChargesCharged = chargesDueAtTimeOfDisbursement; BigDecimal totalPenaltyChargesCharged = BigDecimal.ZERO; BigDecimal totalWaived = BigDecimal.ZERO; BigDecimal totalWrittenOff = BigDecimal.ZERO; BigDecimal totalRepaymentExpected = chargesDueAtTimeOfDisbursement; BigDecimal totalRepayment = BigDecimal.ZERO; BigDecimal totalOutstanding = BigDecimal.ZERO; LocalDate startDate = disbursementDate; int periodNumber = 1; for (LocalDate scheduledDueDate : scheduledDates) { // number of days from startDate to this scheduledDate int daysInPeriod = Days.daysBetween(startDate.toDateMidnight().toDateTime(), scheduledDueDate.toDateMidnight().toDateTime()).getDays(); Money interestForInstallment = this.periodicInterestRateCalculator.calculateInterestOn( outstandingBalance, periodInterestRateForRepaymentPeriod, daysInPeriod, loanScheduleInfo); Money principalForInstallment = this.periodicInterestRateCalculator .calculatePrincipalOn(totalDuePerInstallment, interestForInstallment, loanScheduleInfo); if (interestCalculationGraceOnRepaymentPeriodFraction >= Integer.valueOf(1).doubleValue()) { Money graceOnInterestForRepaymentPeriod = interestForInstallment; interestForInstallment = interestForInstallment.minus(graceOnInterestForRepaymentPeriod); interestCalculationGraceOnRepaymentPeriodFraction = interestCalculationGraceOnRepaymentPeriodFraction - Integer.valueOf(1).doubleValue(); } else if (interestCalculationGraceOnRepaymentPeriodFraction > Double.valueOf("0.25") && interestCalculationGraceOnRepaymentPeriodFraction < Integer.valueOf(1).doubleValue()) { Money graceOnInterestForRepaymentPeriod = interestForInstallment .multipliedBy(interestCalculationGraceOnRepaymentPeriodFraction); interestForInstallment = interestForInstallment.minus(graceOnInterestForRepaymentPeriod); interestCalculationGraceOnRepaymentPeriodFraction = Double.valueOf("0"); } totalPrincipal = totalPrincipal.plus(principalForInstallment); totalInterest = totalInterest.plus(interestForInstallment); if (periodNumber == loanScheduleInfo.getNumberOfRepayments()) { Money principalDifference = totalPrincipal.minus(loanScheduleInfo.getPrincipal()); if (principalDifference.isLessThanZero()) { principalForInstallment = principalForInstallment.plus(principalDifference.abs()); } else if (principalDifference.isGreaterThanZero()) { principalForInstallment = principalForInstallment.minus(principalDifference.abs()); } } outstandingBalance = outstandingBalance.minus(principalForInstallment); final Money feeChargesForInstallment = cumulativeFeeChargesDueWithin(startDate, scheduledDueDate, loanCharges, monetaryCurrency); final Money penaltyChargesForInstallment = cumulativePenaltyChargesDueWithin(startDate, scheduledDueDate, loanCharges, monetaryCurrency); final Money totalInstallmentDue = principalForInstallment.plus(interestForInstallment) .plus(feeChargesForInstallment).plus(penaltyChargesForInstallment); LoanSchedulePeriodData installment = LoanSchedulePeriodData.repaymentOnlyPeriod(periodNumber, startDate, scheduledDueDate, principalForInstallment.getAmount(), outstandingBalance.getAmount(), interestForInstallment.getAmount(), feeChargesForInstallment.getAmount(), penaltyChargesForInstallment.getAmount(), totalInstallmentDue.getAmount()); periods.add(installment); // handle cumulative fields loanTermInDays += daysInPeriod; totalPrincipalExpected = totalPrincipalExpected.add(principalForInstallment.getAmount()); totalInterestCharged = totalInterestCharged.add(interestForInstallment.getAmount()); totalFeeChargesCharged = totalFeeChargesCharged.add(feeChargesForInstallment.getAmount()); totalPenaltyChargesCharged = totalPenaltyChargesCharged.add(penaltyChargesForInstallment.getAmount()); totalRepaymentExpected = totalRepaymentExpected.add(totalInstallmentDue.getAmount()); startDate = scheduledDueDate; periodNumber++; } final CurrencyData currencyData = new CurrencyData(currency.getCode(), currency.getName(), monetaryCurrency.getDigitsAfterDecimal(), currency.getDisplaySymbol(), currency.getNameCode()); return new LoanScheduleData(currencyData, periods, loanTermInDays, totalPrincipalDisbursed, totalPrincipalExpected, totalPrincipalPaid, totalInterestCharged, totalFeeChargesCharged, totalPenaltyChargesCharged, totalWaived, totalWrittenOff, totalRepaymentExpected, totalRepayment, totalOutstanding); }
From source file:org.mifosplatform.portfolio.loanaccount.loanschedule.domain.FlatInterestLoanScheduleGenerator.java
License:Mozilla Public License
@Override public PrincipalInterest calculatePrincipalInterestComponentsForPeriod( final PaymentPeriodsInOneYearCalculator calculator, final double interestCalculationGraceOnRepaymentPeriodFraction, final Money totalCumulativePrincipal, final Money totalCumulativeInterest, final Money totalInterestDueForLoan, final Money cumulatingInterestPaymentDueToGrace, final Money outstandingBalance, final LoanApplicationTerms loanApplicationTerms, final int periodNumber, final MathContext mc, @SuppressWarnings("unused") TreeMap<LocalDate, Money> principalVariation, @SuppressWarnings("unused") Map<LocalDate, Money> compoundingMap, LocalDate periodStartDate, LocalDate periodEndDate, @SuppressWarnings("unused") int daysForInterestInFullPeriod) { final int daysInPeriodApplicableForInterest = Days.daysBetween(periodStartDate, periodEndDate).getDays(); Money principalForThisInstallment = loanApplicationTerms.calculateTotalPrincipalForPeriod(calculator, daysInPeriodApplicableForInterest, outstandingBalance, periodNumber, mc, null); final PrincipalInterest result = loanApplicationTerms.calculateTotalInterestForPeriod(calculator, interestCalculationGraceOnRepaymentPeriodFraction, periodNumber, mc, cumulatingInterestPaymentDueToGrace, daysInPeriodApplicableForInterest, outstandingBalance); Money interestForThisInstallment = result.interest(); // update cumulative fields for principal & interest final Money interestBroughtForwardDueToGrace = result.interestPaymentDueToGrace(); final Money totalCumulativePrincipalToDate = totalCumulativePrincipal.plus(principalForThisInstallment); final Money totalCumulativeInterestToDate = totalCumulativeInterest.plus(interestForThisInstallment); // adjust if needed principalForThisInstallment = loanApplicationTerms.adjustPrincipalIfLastRepaymentPeriod( principalForThisInstallment, totalCumulativePrincipalToDate, periodNumber); interestForThisInstallment = loanApplicationTerms.adjustInterestIfLastRepaymentPeriod( interestForThisInstallment, totalCumulativeInterestToDate, totalInterestDueForLoan, periodNumber); return new PrincipalInterest(principalForThisInstallment, interestForThisInstallment, interestBroughtForwardDueToGrace); }
From source file:org.mifosplatform.portfolio.loanaccount.loanschedule.domain.FlatMethodLoanScheduleGenerator.java
License:Mozilla Public License
@Override public LoanScheduleData generate(final ApplicationCurrency currency, final LoanProductRelatedDetail loanScheduleInfo, final Integer loanTermFrequency, final PeriodFrequencyType loanTermFrequencyType, final LocalDate disbursementDate, final LocalDate firstRepaymentDate, final LocalDate interestCalculatedFrom, final Set<LoanCharge> loanCharges) { final Collection<LoanSchedulePeriodData> periods = new ArrayList<LoanSchedulePeriodData>(); final List<LocalDate> scheduledDates = this.scheduledDateGenerator.generate(loanScheduleInfo, disbursementDate, firstRepaymentDate); MathContext mc = new MathContext(8, RoundingMode.HALF_EVEN); BigDecimal loanTermPeriodsInYear = BigDecimal .valueOf(this.paymentPeriodsInOneYearCalculator.calculate(loanTermFrequencyType)); BigDecimal interestRateForLoanTerm = loanScheduleInfo.getAnnualNominalInterestRate() .divide(loanTermPeriodsInYear, mc).divide(BigDecimal.valueOf(Double.valueOf("100.0")), mc) .multiply(BigDecimal.valueOf(loanTermFrequency)); final MonetaryCurrency monetaryCurrency = loanScheduleInfo.getPrincipal().getCurrency(); Money totalInterestForLoanTerm = loanScheduleInfo.getPrincipal() .multiplyRetainScale(interestRateForLoanTerm, RoundingMode.HALF_EVEN); Money interestPerInstallment = totalInterestForLoanTerm .dividedBy(Long.valueOf(loanScheduleInfo.getNumberOfRepayments()), RoundingMode.HALF_EVEN); Money principalPerInstallment = loanScheduleInfo.getPrincipal() .dividedBy(loanScheduleInfo.getNumberOfRepayments(), RoundingMode.HALF_EVEN); Money outstandingBalance = loanScheduleInfo.getPrincipal(); Money principalDisbursed = loanScheduleInfo.getPrincipal(); Money totalPrincipal = Money.zero(outstandingBalance.getCurrency()); Money totalInterest = Money.zero(outstandingBalance.getCurrency()); BigDecimal chargesDueAtTimeOfDisbursement = BigDecimal.ZERO; for (LoanCharge loanCharge : loanCharges) { if (loanCharge.isDueAtDisbursement()) { chargesDueAtTimeOfDisbursement = chargesDueAtTimeOfDisbursement.add(loanCharge.amount()); }//from www . ja va 2 s . c o m } // create entries of disbursement period on loan schedule final LoanSchedulePeriodData disbursementPeriod = LoanSchedulePeriodData.disbursementOnlyPeriod( disbursementDate, principalDisbursed.getAmount(), chargesDueAtTimeOfDisbursement, false); periods.add(disbursementPeriod); int loanTermInDays = Integer.valueOf(0); BigDecimal totalPrincipalDisbursed = principalDisbursed.getAmount(); BigDecimal totalPrincipalExpected = BigDecimal.ZERO; BigDecimal totalPrincipalPaid = BigDecimal.ZERO; BigDecimal totalInterestCharged = BigDecimal.ZERO; BigDecimal totalFeeChargesCharged = chargesDueAtTimeOfDisbursement; BigDecimal totalPenaltyChargesCharged = BigDecimal.ZERO; BigDecimal totalWaived = BigDecimal.ZERO; BigDecimal totalWrittenOff = BigDecimal.ZERO; BigDecimal totalRepaymentExpected = chargesDueAtTimeOfDisbursement; BigDecimal totalRepayment = BigDecimal.ZERO; BigDecimal totalOutstanding = BigDecimal.ZERO; LocalDate startDate = disbursementDate; int periodNumber = 1; for (LocalDate scheduledDueDate : scheduledDates) { totalPrincipal = totalPrincipal.plus(principalPerInstallment); totalInterest = totalInterest.plus(interestPerInstallment); // number of days from startDate to this scheduledDate int daysInPeriod = Days.daysBetween(startDate.toDateMidnight().toDateTime(), scheduledDueDate.toDateMidnight().toDateTime()).getDays(); if (periodNumber == loanScheduleInfo.getNumberOfRepayments()) { final Money difference = totalPrincipal.minus(loanScheduleInfo.getPrincipal()); if (difference.isLessThanZero()) { principalPerInstallment = principalPerInstallment.plus(difference.abs()); } else if (difference.isGreaterThanZero()) { principalPerInstallment = principalPerInstallment.minus(difference.abs()); } final Money interestDifference = totalInterest.minus(totalInterestForLoanTerm); if (interestDifference.isLessThanZero()) { interestPerInstallment = interestPerInstallment.plus(interestDifference.abs()); } else if (interestDifference.isGreaterThanZero()) { interestPerInstallment = interestPerInstallment.minus(interestDifference.abs()); } } outstandingBalance = outstandingBalance.minus(principalPerInstallment); final Money feeChargesForInstallment = cumulativeFeeChargesDueWithin(startDate, scheduledDueDate, loanCharges, monetaryCurrency); final Money penaltyChargesForInstallment = cumulativePenaltyChargesDueWithin(startDate, scheduledDueDate, loanCharges, monetaryCurrency); final Money totalInstallmentDue = principalPerInstallment // .plus(interestPerInstallment) // .plus(feeChargesForInstallment) // .plus(penaltyChargesForInstallment); LoanSchedulePeriodData installment = LoanSchedulePeriodData.repaymentOnlyPeriod(periodNumber, startDate, scheduledDueDate, principalPerInstallment.getAmount(), outstandingBalance.getAmount(), interestPerInstallment.getAmount(), feeChargesForInstallment.getAmount(), penaltyChargesForInstallment.getAmount(), totalInstallmentDue.getAmount()); periods.add(installment); // handle cumulative fields loanTermInDays += daysInPeriod; totalPrincipalExpected = totalPrincipalExpected.add(principalPerInstallment.getAmount()); totalInterestCharged = totalInterestCharged.add(interestPerInstallment.getAmount()); totalFeeChargesCharged = totalFeeChargesCharged.add(feeChargesForInstallment.getAmount()); totalPenaltyChargesCharged = totalPenaltyChargesCharged.add(penaltyChargesForInstallment.getAmount()); totalRepaymentExpected = totalRepaymentExpected.add(totalInstallmentDue.getAmount()); startDate = scheduledDueDate; periodNumber++; } final CurrencyData currencyData = new CurrencyData(currency.getCode(), currency.getName(), monetaryCurrency.getDigitsAfterDecimal(), currency.getDisplaySymbol(), currency.getNameCode()); return new LoanScheduleData(currencyData, periods, loanTermInDays, totalPrincipalDisbursed, totalPrincipalExpected, totalPrincipalPaid, totalInterestCharged, totalFeeChargesCharged, totalPenaltyChargesCharged, totalWaived, totalWrittenOff, totalRepaymentExpected, totalRepayment, totalOutstanding); }