/** * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at http://mozilla.org/MPL/2.0/. */ package org.mifosplatform.portfolio.loanaccount.service; import java.math.BigDecimal; import java.util.List; import org.joda.time.LocalDate; import org.mifosplatform.infrastructure.configuration.domain.ConfigurationDomainService; import org.mifosplatform.organisation.holiday.domain.Holiday; import org.mifosplatform.organisation.holiday.domain.HolidayRepository; import org.mifosplatform.organisation.holiday.domain.HolidayStatusType; import org.mifosplatform.organisation.monetary.domain.ApplicationCurrency; import org.mifosplatform.organisation.monetary.domain.ApplicationCurrencyRepositoryWrapper; import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency; import org.mifosplatform.organisation.workingdays.domain.WorkingDays; import org.mifosplatform.organisation.workingdays.domain.WorkingDaysRepositoryWrapper; import org.mifosplatform.portfolio.calendar.domain.Calendar; import org.mifosplatform.portfolio.calendar.domain.CalendarEntityType; import org.mifosplatform.portfolio.calendar.domain.CalendarInstance; import org.mifosplatform.portfolio.calendar.domain.CalendarInstanceRepository; import org.mifosplatform.portfolio.calendar.service.CalendarUtils; import org.mifosplatform.portfolio.floatingrates.data.FloatingRateDTO; import org.mifosplatform.portfolio.floatingrates.data.FloatingRatePeriodData; import org.mifosplatform.portfolio.floatingrates.exception.FloatingRateNotFoundException; import org.mifosplatform.portfolio.floatingrates.service.FloatingRatesReadPlatformService; import org.mifosplatform.portfolio.loanaccount.data.HolidayDetailDTO; import org.mifosplatform.portfolio.loanaccount.data.ScheduleGeneratorDTO; import org.mifosplatform.portfolio.loanaccount.domain.Loan; import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.LoanScheduleGeneratorFactory; import org.mifosplatform.portfolio.loanproduct.domain.LoanProductRelatedDetail; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component public class LoanUtilService { private final ApplicationCurrencyRepositoryWrapper applicationCurrencyRepository; private final CalendarInstanceRepository calendarInstanceRepository; private final ConfigurationDomainService configurationDomainService; private final HolidayRepository holidayRepository; private final WorkingDaysRepositoryWrapper workingDaysRepository; private final LoanScheduleGeneratorFactory loanScheduleFactory; private final FloatingRatesReadPlatformService floatingRatesReadPlatformService; @Autowired public LoanUtilService(final ApplicationCurrencyRepositoryWrapper applicationCurrencyRepository, final CalendarInstanceRepository calendarInstanceRepository, final ConfigurationDomainService configurationDomainService, final HolidayRepository holidayRepository, final WorkingDaysRepositoryWrapper workingDaysRepository, final LoanScheduleGeneratorFactory loanScheduleFactory, final FloatingRatesReadPlatformService floatingRatesReadPlatformService) { this.applicationCurrencyRepository = applicationCurrencyRepository; this.calendarInstanceRepository = calendarInstanceRepository; this.configurationDomainService = configurationDomainService; this.holidayRepository = holidayRepository; this.workingDaysRepository = workingDaysRepository; this.loanScheduleFactory = loanScheduleFactory; this.floatingRatesReadPlatformService = floatingRatesReadPlatformService; } public ScheduleGeneratorDTO buildScheduleGeneratorDTO(final Loan loan, final LocalDate recalculateFrom) { final HolidayDetailDTO holidayDetailDTO = null; return buildScheduleGeneratorDTO(loan, recalculateFrom, holidayDetailDTO); } public ScheduleGeneratorDTO buildScheduleGeneratorDTO(final Loan loan, final LocalDate recalculateFrom, final HolidayDetailDTO holidayDetailDTO) { HolidayDetailDTO holidayDetails = holidayDetailDTO; if (holidayDetailDTO == null) { holidayDetails = constructHolidayDTO(loan); } final MonetaryCurrency currency = loan.getCurrency(); ApplicationCurrency applicationCurrency = this.applicationCurrencyRepository.findOneWithNotFoundDetection(currency); final CalendarInstance calendarInstance = this.calendarInstanceRepository.findCalendarInstaneByEntityId(loan.getId(), CalendarEntityType.LOANS.getValue()); LocalDate calculatedRepaymentsStartingFromDate = this.getCalculatedRepaymentsStartingFromDate(loan.getDisbursementDate(), loan, calendarInstance); CalendarInstance restCalendarInstance = null; CalendarInstance compoundingCalendarInstance = null; Long overdurPenaltyWaitPeriod = null; if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) { restCalendarInstance = calendarInstanceRepository.findCalendarInstaneByEntityId(loan.loanInterestRecalculationDetailId(), CalendarEntityType.LOAN_RECALCULATION_REST_DETAIL.getValue()); compoundingCalendarInstance = calendarInstanceRepository.findCalendarInstaneByEntityId( loan.loanInterestRecalculationDetailId(), CalendarEntityType.LOAN_RECALCULATION_COMPOUNDING_DETAIL.getValue()); overdurPenaltyWaitPeriod = this.configurationDomainService.retrievePenaltyWaitPeriod(); } FloatingRateDTO floatingRateDTO = constructFloatingRateDTO(loan); ScheduleGeneratorDTO scheduleGeneratorDTO = new ScheduleGeneratorDTO(loanScheduleFactory, applicationCurrency, calculatedRepaymentsStartingFromDate, holidayDetails, restCalendarInstance, compoundingCalendarInstance, recalculateFrom, overdurPenaltyWaitPeriod, floatingRateDTO); return scheduleGeneratorDTO; } public LocalDate getCalculatedRepaymentsStartingFromDate(final Loan loan) { final CalendarInstance calendarInstance = this.calendarInstanceRepository.findCalendarInstaneByEntityId(loan.getId(), CalendarEntityType.LOANS.getValue()); return this.getCalculatedRepaymentsStartingFromDate(loan.getDisbursementDate(), loan, calendarInstance); } private HolidayDetailDTO constructHolidayDTO(final Loan loan) { final boolean isHolidayEnabled = this.configurationDomainService.isRescheduleRepaymentsOnHolidaysEnabled(); final List<Holiday> holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(loan.getOfficeId(), loan .getDisbursementDate().toDate(), HolidayStatusType.ACTIVE.getValue()); final WorkingDays workingDays = this.workingDaysRepository.findOne(); final boolean allowTransactionsOnHoliday = this.configurationDomainService.allowTransactionsOnHolidayEnabled(); final boolean allowTransactionsOnNonWorkingDay = this.configurationDomainService.allowTransactionsOnNonWorkingDayEnabled(); HolidayDetailDTO holidayDetailDTO = new HolidayDetailDTO(isHolidayEnabled, holidays, workingDays, allowTransactionsOnHoliday, allowTransactionsOnNonWorkingDay); return holidayDetailDTO; } private FloatingRateDTO constructFloatingRateDTO(final Loan loan) { FloatingRateDTO floatingRateDTO = null; if (loan.loanProduct().isLinkedToFloatingInterestRate()) { boolean isFloatingInterestRate = loan.getIsFloatingInterestRate(); BigDecimal interestRateDiff = loan.getInterestRateDifferential(); List<FloatingRatePeriodData> baseLendingRatePeriods = null; try { baseLendingRatePeriods = this.floatingRatesReadPlatformService.retrieveBaseLendingRate().getRatePeriods(); } catch (final FloatingRateNotFoundException ex) { // Do not do anything } floatingRateDTO = new FloatingRateDTO(isFloatingInterestRate, loan.getDisbursementDate(), interestRateDiff, baseLendingRatePeriods); } return floatingRateDTO; } private LocalDate getCalculatedRepaymentsStartingFromDate(final LocalDate actualDisbursementDate, final Loan loan, final CalendarInstance calendarInstance) { final Calendar calendar = calendarInstance == null ? null : calendarInstance.getCalendar(); return calculateRepaymentStartingFromDate(actualDisbursementDate, loan, calendar); } public LocalDate getCalculatedRepaymentsStartingFromDate(final LocalDate actualDisbursementDate, final Loan loan, final Calendar calendar) { if (calendar == null) { return getCalculatedRepaymentsStartingFromDate(loan); } return calculateRepaymentStartingFromDate(actualDisbursementDate, loan, calendar); } private LocalDate calculateRepaymentStartingFromDate(final LocalDate actualDisbursementDate, final Loan loan, final Calendar calendar) { LocalDate calculatedRepaymentsStartingFromDate = loan.getExpectedFirstRepaymentOnDate(); if (calendar != null) {// sync repayments // TODO: AA - user provided first repayment date takes precedence // over recalculated meeting date if (calculatedRepaymentsStartingFromDate == null) { // FIXME: AA - Possibility of having next meeting date // immediately after disbursement date, // need to have minimum number of days gap between disbursement // and first repayment date. final LoanProductRelatedDetail repaymentScheduleDetails = loan.repaymentScheduleDetail(); if (repaymentScheduleDetails != null) {// Not expecting to be // null final Integer repayEvery = repaymentScheduleDetails.getRepayEvery(); final String frequency = CalendarUtils.getMeetingFrequencyFromPeriodFrequencyType(repaymentScheduleDetails .getRepaymentPeriodFrequencyType()); calculatedRepaymentsStartingFromDate = CalendarUtils.getFirstRepaymentMeetingDate(calendar, actualDisbursementDate, repayEvery, frequency); } } } return calculatedRepaymentsStartingFromDate; } }