com.gst.portfolio.loanaccount.service.LoanUtilService.java Source code

Java tutorial

Introduction

Here is the source code for com.gst.portfolio.loanaccount.service.LoanUtilService.java

Source

/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package com.gst.portfolio.loanaccount.service;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;

import org.apache.commons.lang.StringUtils;
import com.gst.infrastructure.configuration.domain.ConfigurationDomainService;
import com.gst.infrastructure.core.serialization.FromJsonHelper;
import com.gst.organisation.holiday.domain.Holiday;
import com.gst.organisation.holiday.domain.HolidayRepository;
import com.gst.organisation.holiday.domain.HolidayStatusType;
import com.gst.organisation.monetary.domain.ApplicationCurrency;
import com.gst.organisation.monetary.domain.ApplicationCurrencyRepositoryWrapper;
import com.gst.organisation.monetary.domain.MonetaryCurrency;
import com.gst.organisation.workingdays.domain.WorkingDays;
import com.gst.organisation.workingdays.domain.WorkingDaysRepositoryWrapper;
import com.gst.portfolio.calendar.data.CalendarHistoryDataWrapper;
import com.gst.portfolio.calendar.domain.Calendar;
import com.gst.portfolio.calendar.domain.CalendarEntityType;
import com.gst.portfolio.calendar.domain.CalendarHistory;
import com.gst.portfolio.calendar.domain.CalendarInstance;
import com.gst.portfolio.calendar.domain.CalendarInstanceRepository;
import com.gst.portfolio.calendar.service.CalendarReadPlatformService;
import com.gst.portfolio.calendar.service.CalendarUtils;
import com.gst.portfolio.floatingrates.data.FloatingRateDTO;
import com.gst.portfolio.floatingrates.data.FloatingRatePeriodData;
import com.gst.portfolio.floatingrates.exception.FloatingRateNotFoundException;
import com.gst.portfolio.floatingrates.service.FloatingRatesReadPlatformService;
import com.gst.portfolio.group.domain.Group;
import com.gst.portfolio.loanaccount.api.LoanApiConstants;
import com.gst.portfolio.loanaccount.data.HolidayDetailDTO;
import com.gst.portfolio.loanaccount.data.ScheduleGeneratorDTO;
import com.gst.portfolio.loanaccount.domain.Loan;
import com.gst.portfolio.loanaccount.domain.LoanDisbursementDetails;
import com.gst.portfolio.loanaccount.loanschedule.domain.LoanScheduleGeneratorFactory;
import com.gst.portfolio.loanproduct.domain.LoanProductRelatedDetail;
import org.joda.time.LocalDate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.google.gson.JsonArray;
import com.google.gson.JsonObject;

@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;
    private final FromJsonHelper fromApiJsonHelper;
    private final CalendarReadPlatformService calendarReadPlatformService;

    @Autowired
    public LoanUtilService(final ApplicationCurrencyRepositoryWrapper applicationCurrencyRepository,
            final CalendarInstanceRepository calendarInstanceRepository,
            final ConfigurationDomainService configurationDomainService, final HolidayRepository holidayRepository,
            final WorkingDaysRepositoryWrapper workingDaysRepository,
            final LoanScheduleGeneratorFactory loanScheduleFactory,
            final FloatingRatesReadPlatformService floatingRatesReadPlatformService,
            final FromJsonHelper fromApiJsonHelper, final CalendarReadPlatformService calendarReadPlatformService) {
        this.applicationCurrencyRepository = applicationCurrencyRepository;
        this.calendarInstanceRepository = calendarInstanceRepository;
        this.configurationDomainService = configurationDomainService;
        this.holidayRepository = holidayRepository;
        this.workingDaysRepository = workingDaysRepository;
        this.loanScheduleFactory = loanScheduleFactory;
        this.floatingRatesReadPlatformService = floatingRatesReadPlatformService;
        this.fromApiJsonHelper = fromApiJsonHelper;
        this.calendarReadPlatformService = calendarReadPlatformService;
    }

    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());
        Calendar calendar = null;
        CalendarHistoryDataWrapper calendarHistoryDataWrapper = null;
        if (calendarInstance != null) {
            calendar = calendarInstance.getCalendar();
            Set<CalendarHistory> calendarHistory = calendar.getCalendarHistory();
            calendarHistoryDataWrapper = new CalendarHistoryDataWrapper(calendarHistory);
        }
        LocalDate calculatedRepaymentsStartingFromDate = this.getCalculatedRepaymentsStartingFromDate(
                loan.getDisbursementDate(), loan, calendarInstance, calendarHistoryDataWrapper);
        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();
        }
        final Boolean isInterestChargedFromDateAsDisbursementDateEnabled = this.configurationDomainService
                .isInterestChargedFromDateSameAsDisbursementDate();
        FloatingRateDTO floatingRateDTO = constructFloatingRateDTO(loan);
        Boolean isSkipRepaymentOnFirstMonth = false;
        Integer numberOfDays = 0;
        boolean isSkipRepaymentOnFirstMonthEnabled = configurationDomainService
                .isSkippingMeetingOnFirstDayOfMonthEnabled();
        if (isSkipRepaymentOnFirstMonthEnabled) {
            isSkipRepaymentOnFirstMonth = isLoanRepaymentsSyncWithMeeting(loan.group(), calendar);
            if (isSkipRepaymentOnFirstMonth) {
                numberOfDays = configurationDomainService.retreivePeroidInNumberOfDaysForSkipMeetingDate()
                        .intValue();
            }
        }
        final Boolean isChangeEmiIfRepaymentDateSameAsDisbursementDateEnabled = this.configurationDomainService
                .isChangeEmiIfRepaymentDateSameAsDisbursementDateEnabled();

        ScheduleGeneratorDTO scheduleGeneratorDTO = new ScheduleGeneratorDTO(loanScheduleFactory,
                applicationCurrency, calculatedRepaymentsStartingFromDate, holidayDetails, restCalendarInstance,
                compoundingCalendarInstance, recalculateFrom, overdurPenaltyWaitPeriod, floatingRateDTO, calendar,
                calendarHistoryDataWrapper, isInterestChargedFromDateAsDisbursementDateEnabled, numberOfDays,
                isSkipRepaymentOnFirstMonth, isChangeEmiIfRepaymentDateSameAsDisbursementDateEnabled);

        return scheduleGeneratorDTO;
    }

    public Boolean isLoanRepaymentsSyncWithMeeting(final Group group, final Calendar calendar) {
        Boolean isSkipRepaymentOnFirstMonth = false;
        Long entityId = null;
        Long entityTypeId = null;

        if (group != null) {
            if (group.getParent() != null) {
                entityId = group.getParent().getId();
                entityTypeId = CalendarEntityType.CENTERS.getValue().longValue();
            } else {
                entityId = group.getId();
                entityTypeId = CalendarEntityType.GROUPS.getValue().longValue();
            }
        }

        if (entityId == null || calendar == null) {
            return isSkipRepaymentOnFirstMonth;
        }
        isSkipRepaymentOnFirstMonth = this.calendarReadPlatformService.isCalendarAssociatedWithEntity(entityId,
                calendar.getId(), entityTypeId);
        return isSkipRepaymentOnFirstMonth;
    }

    public LocalDate getCalculatedRepaymentsStartingFromDate(final Loan loan) {
        final CalendarInstance calendarInstance = this.calendarInstanceRepository
                .findCalendarInstaneByEntityId(loan.getId(), CalendarEntityType.LOANS.getValue());
        final CalendarHistoryDataWrapper calendarHistoryDataWrapper = null;
        return this.getCalculatedRepaymentsStartingFromDate(loan.getDisbursementDate(), loan, calendarInstance,
                calendarHistoryDataWrapper);
    }

    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 CalendarHistoryDataWrapper calendarHistoryDataWrapper) {
        final Calendar calendar = calendarInstance == null ? null : calendarInstance.getCalendar();
        return calculateRepaymentStartingFromDate(actualDisbursementDate, loan, calendar,
                calendarHistoryDataWrapper);
    }

    public LocalDate getCalculatedRepaymentsStartingFromDate(final LocalDate actualDisbursementDate,
            final Loan loan, final Calendar calendar) {
        final CalendarHistoryDataWrapper calendarHistoryDataWrapper = null;
        if (calendar == null) {
            return getCalculatedRepaymentsStartingFromDate(loan);
        }
        return calculateRepaymentStartingFromDate(actualDisbursementDate, loan, calendar,
                calendarHistoryDataWrapper);

    }

    private LocalDate calculateRepaymentStartingFromDate(final LocalDate actualDisbursementDate, final Loan loan,
            final Calendar calendar, final CalendarHistoryDataWrapper calendarHistoryDataWrapper) {
        LocalDate calculatedRepaymentsStartingFromDate = loan.getExpectedFirstRepaymentOnDate();
        if (calendar != null) {// sync repayments

            if (calculatedRepaymentsStartingFromDate == null && !calendar.getCalendarHistory().isEmpty()
                    && calendarHistoryDataWrapper != null) {
                // generate the first repayment date based on calendar history
                calculatedRepaymentsStartingFromDate = generateCalculatedRepaymentStartDate(
                        calendarHistoryDataWrapper, actualDisbursementDate, loan);
                return calculatedRepaymentsStartingFromDate;
            }

            // 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());
                    Boolean isSkipRepaymentOnFirstMonth = false;
                    Integer numberOfDays = 0;
                    boolean isSkipRepaymentOnFirstMonthEnabled = this.configurationDomainService
                            .isSkippingMeetingOnFirstDayOfMonthEnabled();
                    if (isSkipRepaymentOnFirstMonthEnabled) {
                        numberOfDays = configurationDomainService.retreivePeroidInNumberOfDaysForSkipMeetingDate()
                                .intValue();
                        isSkipRepaymentOnFirstMonth = isLoanRepaymentsSyncWithMeeting(loan.group(), calendar);
                    }
                    calculatedRepaymentsStartingFromDate = CalendarUtils.getFirstRepaymentMeetingDate(calendar,
                            actualDisbursementDate, repayEvery, frequency, isSkipRepaymentOnFirstMonth,
                            numberOfDays);
                }
            }
        }
        return calculatedRepaymentsStartingFromDate;
    }

    private LocalDate generateCalculatedRepaymentStartDate(
            final CalendarHistoryDataWrapper calendarHistoryDataWrapper, LocalDate actualDisbursementDate,
            Loan loan) {
        final LoanProductRelatedDetail repaymentScheduleDetails = loan.repaymentScheduleDetail();
        final WorkingDays workingDays = this.workingDaysRepository.findOne();
        LocalDate calculatedRepaymentsStartingFromDate = null;

        List<CalendarHistory> historyList = calendarHistoryDataWrapper.getCalendarHistoryList();

        if (historyList != null && historyList.size() > 0) {
            if (repaymentScheduleDetails != null) {
                final Integer repayEvery = repaymentScheduleDetails.getRepayEvery();
                final String frequency = CalendarUtils.getMeetingFrequencyFromPeriodFrequencyType(
                        repaymentScheduleDetails.getRepaymentPeriodFrequencyType());
                Boolean isSkipRepaymentOnFirstMonth = false;
                Integer numberOfDays = 0;
                boolean isSkipRepaymentOnFirstMonthEnabled = this.configurationDomainService
                        .isSkippingMeetingOnFirstDayOfMonthEnabled();
                if (isSkipRepaymentOnFirstMonthEnabled) {
                    numberOfDays = configurationDomainService.retreivePeroidInNumberOfDaysForSkipMeetingDate()
                            .intValue();
                    isSkipRepaymentOnFirstMonth = isLoanRepaymentsSyncWithMeeting(loan.group(),
                            historyList.get(0).getCalendar());
                }
                calculatedRepaymentsStartingFromDate = CalendarUtils.getNextRepaymentMeetingDate(
                        historyList.get(0).getRecurrence(), historyList.get(0).getStartDateLocalDate(),
                        actualDisbursementDate, repayEvery, frequency, workingDays, isSkipRepaymentOnFirstMonth,
                        numberOfDays);
            }
        }
        return calculatedRepaymentsStartingFromDate;
    }

    public List<LoanDisbursementDetails> fetchDisbursementData(final JsonObject command) {
        final Locale locale = this.fromApiJsonHelper.extractLocaleParameter(command);
        final String dateFormat = this.fromApiJsonHelper.extractDateFormatParameter(command);
        List<LoanDisbursementDetails> disbursementDatas = new ArrayList<>();
        if (command.has(LoanApiConstants.disbursementDataParameterName)) {
            final JsonArray disbursementDataArray = command
                    .getAsJsonArray(LoanApiConstants.disbursementDataParameterName);
            if (disbursementDataArray != null && disbursementDataArray.size() > 0) {
                int i = 0;
                do {
                    final JsonObject jsonObject = disbursementDataArray.get(i).getAsJsonObject();
                    Date expectedDisbursementDate = null;
                    Date actualDisbursementDate = null;
                    BigDecimal principal = null;

                    if (jsonObject.has(LoanApiConstants.disbursementDateParameterName)) {
                        LocalDate date = this.fromApiJsonHelper.extractLocalDateNamed(
                                LoanApiConstants.disbursementDateParameterName, jsonObject, dateFormat, locale);
                        if (date != null) {
                            expectedDisbursementDate = date.toDate();
                        }
                    }
                    if (jsonObject.has(LoanApiConstants.disbursementPrincipalParameterName)
                            && jsonObject.get(LoanApiConstants.disbursementPrincipalParameterName).isJsonPrimitive()
                            && StringUtils.isNotBlank((jsonObject
                                    .get(LoanApiConstants.disbursementPrincipalParameterName).getAsString()))) {
                        principal = jsonObject
                                .getAsJsonPrimitive(LoanApiConstants.disbursementPrincipalParameterName)
                                .getAsBigDecimal();
                    }

                    disbursementDatas.add(new LoanDisbursementDetails(expectedDisbursementDate,
                            actualDisbursementDate, principal));
                    i++;
                } while (i < disbursementDataArray.size());
            }
        }
        return disbursementDatas;
    }

}