/**
* 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;
}
}