org.kuali.kfs.module.cam.batch.service.impl.AssetDepreciationServiceImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.kuali.kfs.module.cam.batch.service.impl.AssetDepreciationServiceImpl.java

Source

/*
 * The Kuali Financial System, a comprehensive financial management system for higher education.
 * 
 * Copyright 2005-2014 The Kuali Foundation
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 * 
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.kuali.kfs.module.cam.batch.service.impl;

import static org.kuali.kfs.sys.KFSConstants.BALANCE_TYPE_ACTUAL;

import java.math.BigDecimal;
import java.sql.Timestamp;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;

import javax.mail.MessagingException;

import org.apache.commons.lang.StringUtils;
import org.kuali.kfs.coa.businessobject.ObjectCode;
import org.kuali.kfs.coa.service.ObjectCodeService;
import org.kuali.kfs.gl.service.impl.StringHelper;
import org.kuali.kfs.module.cam.CamsConstants;
import org.kuali.kfs.module.cam.CamsKeyConstants;
import org.kuali.kfs.module.cam.CamsPropertyConstants;
import org.kuali.kfs.module.cam.batch.AssetDepreciationStep;
import org.kuali.kfs.module.cam.batch.AssetPaymentInfo;
import org.kuali.kfs.module.cam.batch.service.AssetDepreciationService;
import org.kuali.kfs.module.cam.batch.service.ReportService;
import org.kuali.kfs.module.cam.businessobject.Asset;
import org.kuali.kfs.module.cam.businessobject.AssetDepreciationConvention;
import org.kuali.kfs.module.cam.businessobject.AssetDepreciationTransaction;
import org.kuali.kfs.module.cam.businessobject.AssetObjectCode;
import org.kuali.kfs.module.cam.businessobject.AssetYearEndDepreciation;
import org.kuali.kfs.module.cam.document.dataaccess.DepreciableAssetsDao;
import org.kuali.kfs.module.cam.document.dataaccess.DepreciationBatchDao;
import org.kuali.kfs.module.cam.document.service.AssetDateService;
import org.kuali.kfs.module.cam.document.service.AssetService;
import org.kuali.kfs.sys.KFSConstants;
import org.kuali.kfs.sys.KFSKeyConstants;
import org.kuali.kfs.sys.batch.service.SchedulerService;
import org.kuali.kfs.sys.businessobject.FinancialSystemDocumentHeader;
import org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntry;
import org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntrySequenceHelper;
import org.kuali.kfs.sys.businessobject.UniversityDate;
import org.kuali.kfs.sys.context.SpringContext;
import org.kuali.kfs.sys.service.GeneralLedgerPendingEntryService;
import org.kuali.kfs.sys.service.OptionsService;
import org.kuali.kfs.sys.service.UniversityDateService;
import org.kuali.kfs.sys.service.impl.KfsParameterConstants;
import org.kuali.rice.core.api.config.property.ConfigurationService;
import org.kuali.rice.core.api.datetime.DateTimeService;
import org.kuali.rice.core.api.mail.MailMessage;
import org.kuali.rice.core.api.util.type.KualiDecimal;
import org.kuali.rice.coreservice.api.parameter.Parameter;
import org.kuali.rice.coreservice.framework.parameter.ParameterService;
import org.kuali.rice.kew.api.WorkflowDocument;
import org.kuali.rice.kew.api.exception.WorkflowException;
import org.kuali.rice.kns.service.DataDictionaryService;
import org.kuali.rice.krad.exception.InvalidAddressException;
import org.kuali.rice.krad.service.BusinessObjectService;
import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
import org.kuali.rice.krad.service.MailService;
import org.kuali.rice.krad.util.GlobalVariables;
import org.kuali.rice.krad.util.ObjectUtils;
import org.kuali.rice.krad.workflow.service.WorkflowDocumentService;
import org.springframework.transaction.annotation.Transactional;

/**
 * This class is a service that calculates the depreciation amount for each asset that has a eligible asset payment.
 * <p>
 * When an error occurs running this process, a pdf file will be created with the error message. However, this doesn't mean that
 * this process automatically leaves all the records as they were right before running the program. If the process fails, is
 * suggested to do the following before trying to run the process again: a.)Delete gl pending entry depreciation entries: DELETE
 * FROM GL_PENDING_ENTRY_T WHERE FDOC_TYP_CD = 'DEPR' b.)Substract from the accumulated depreciation amount the depreciation
 * calculated for the fiscal month that was ran, and then reset the depreciation amount field for the fiscal month that was ran. ex:
 * Assuming that the fiscal month = 8 then: UPDATE CM_AST_PAYMENT_T SET AST_ACUM_DEPR1_AMT = AST_ACUM_DEPR1_AMT -
 * AST_PRD8_DEPR1_AMT, AST_PRD8_DEPR1_AMT=0
 */
@Transactional
public class AssetDepreciationServiceImpl implements AssetDepreciationService {
    private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger
            .getLogger(AssetDepreciationServiceImpl.class);
    protected ParameterService parameterService;
    protected AssetService assetService;
    protected ReportService reportService;
    protected DateTimeService dateTimeService;
    protected DepreciableAssetsDao depreciableAssetsDao;
    protected ConfigurationService kualiConfigurationService;
    protected GeneralLedgerPendingEntryService generalLedgerPendingEntryService;
    protected BusinessObjectService businessObjectService;
    protected UniversityDateService universityDateService;
    protected OptionsService optionsService;
    protected DataDictionaryService dataDictionaryService;
    protected DepreciationBatchDao depreciationBatchDao;
    protected String cronExpression;
    protected MailService mailService;
    protected volatile WorkflowDocumentService workflowDocumentService;
    private AssetDateService assetDateService;
    private SchedulerService schedulerService;

    /**
     * @see org.kuali.kfs.module.cam.batch.service.AssetDepreciationService#runDepreciation()
     */
    @Override
    public void runDepreciation() {
        Integer fiscalYear = -1;
        Integer fiscalMonth = -1;
        String errorMsg = "";
        List<String> documentNos = new ArrayList<String>();
        List<String[]> reportLog = new ArrayList<String[]>();
        Collection<AssetObjectCode> assetObjectCodes = new ArrayList<AssetObjectCode>();
        boolean hasErrors = false;
        Calendar depreciationDate = dateTimeService.getCurrentCalendar();
        java.sql.Date depDate = null;
        Calendar currentDate = dateTimeService.getCurrentCalendar();
        String depreciationDateParameter = null;
        DateFormat dateFormat = new SimpleDateFormat(CamsConstants.DateFormats.YEAR_MONTH_DAY);
        boolean executeJob = false;
        String errorMessage = kualiConfigurationService
                .getPropertyValueAsString(CamsKeyConstants.Depreciation.DEPRECIATION_ALREADY_RAN_MSG);

        try {
            executeJob = runAssetDepreciation();
            if (executeJob) {
                LOG.info("*******" + CamsConstants.Depreciation.DEPRECIATION_BATCH + " HAS BEGUN *******");

                /*
                 * Getting the system parameter "DEPRECIATION_DATE" When this parameter is used to determine which fiscal month and year
                 * is going to be depreciated If blank then the system will take the system date to determine the fiscal period
                 */
                if (parameterService.parameterExists(KfsParameterConstants.CAPITAL_ASSETS_BATCH.class,
                        CamsConstants.Parameters.DEPRECIATION_RUN_DATE_PARAMETER)) {
                    depreciationDateParameter = parameterService.getParameterValueAsString(
                            KfsParameterConstants.CAPITAL_ASSETS_BATCH.class,
                            CamsConstants.Parameters.DEPRECIATION_RUN_DATE_PARAMETER);
                } else {
                    throw new IllegalStateException(kualiConfigurationService.getPropertyValueAsString(
                            CamsKeyConstants.Depreciation.DEPRECIATION_DATE_PARAMETER_NOT_FOUND));
                }

                if (StringUtils.isBlank(depreciationDateParameter)) {
                    depreciationDateParameter = dateFormat.format(dateTimeService.getCurrentDate());
                }
                // This validates the system parameter depreciation_date has a valid format of YYYY-MM-DD.
                if (!StringUtils.isBlank(depreciationDateParameter)) {
                    try {
                        depreciationDate.setTime(dateFormat.parse(depreciationDateParameter.trim()));
                    } catch (ParseException e) {
                        throw new IllegalArgumentException(kualiConfigurationService.getPropertyValueAsString(
                                CamsKeyConstants.Depreciation.INVALID_DEPRECIATION_DATE_FORMAT));
                    }
                }
                if (LOG.isInfoEnabled()) {
                    LOG.info(CamsConstants.Depreciation.DEPRECIATION_BATCH + "Depreciation run date: "
                            + depreciationDateParameter);
                }

                UniversityDate universityDate = businessObjectService.findBySinglePrimaryKey(UniversityDate.class,
                        new java.sql.Date(depreciationDate.getTimeInMillis()));
                if (universityDate == null) {
                    throw new IllegalStateException(kualiConfigurationService
                            .getPropertyValueAsString(KFSKeyConstants.ERROR_UNIV_DATE_NOT_FOUND));
                }

                fiscalYear = universityDate.getUniversityFiscalYear();
                fiscalMonth = new Integer(universityDate.getUniversityFiscalAccountingPeriod());
                assetObjectCodes = getAssetObjectCodes(fiscalYear);
                // If the depreciation date is not = to the system date then, the depreciation process cannot run.
                if (LOG.isInfoEnabled()) {
                    LOG.info(CamsConstants.Depreciation.DEPRECIATION_BATCH + "Fiscal Year = " + fiscalYear
                            + " & Fiscal Period=" + fiscalMonth);
                }
                int fiscalStartMonth = Integer
                        .parseInt(optionsService.getCurrentYearOptions().getUniversityFiscalYearStartMo());
                reportLog.addAll(depreciableAssetsDao.generateStatistics(true, null, fiscalYear, fiscalMonth,
                        depreciationDate, dateTimeService.toDateString(depreciationDate.getTime()),
                        assetObjectCodes, fiscalStartMonth, errorMessage));
                // update if fiscal period is 12
                // depreciationBatchDao.updateAssetsCreatedInLastFiscalPeriod(fiscalMonth, fiscalYear);
                updateAssetsDatesForLastFiscalPeriod(fiscalMonth, fiscalYear);
                // Retrieving eligible asset payment details
                LOG.info(CamsConstants.Depreciation.DEPRECIATION_BATCH
                        + "Getting list of asset payments eligible for depreciation.");
                Collection<AssetPaymentInfo> depreciableAssetsCollection = depreciationBatchDao
                        .getListOfDepreciableAssetPaymentInfo(fiscalYear, fiscalMonth, depreciationDate);
                // if we have assets eligible for depreciation then, calculate depreciation and create glpe's transactions
                if (depreciableAssetsCollection != null && !depreciableAssetsCollection.isEmpty()) {
                    SortedMap<String, AssetDepreciationTransaction> depreciationTransactions = this
                            .calculateDepreciation(fiscalYear, fiscalMonth, depreciableAssetsCollection,
                                    depreciationDate, assetObjectCodes);
                    processGeneralLedgerPendingEntry(fiscalYear, fiscalMonth, documentNos,
                            depreciationTransactions);
                } else {
                    throw new IllegalStateException(kualiConfigurationService.getPropertyValueAsString(
                            CamsKeyConstants.Depreciation.NO_ELIGIBLE_FOR_DEPRECIATION_ASSETS_FOUND));
                }
            }
        } catch (Exception e) {
            LOG.error("Error occurred");
            LOG.error(CamsConstants.Depreciation.DEPRECIATION_BATCH
                    + "**************************************************************************");
            LOG.error(CamsConstants.Depreciation.DEPRECIATION_BATCH + "AN ERROR HAS OCCURRED! - ERROR: "
                    + e.getMessage(), e);
            LOG.error(CamsConstants.Depreciation.DEPRECIATION_BATCH
                    + "**************************************************************************");
            hasErrors = true;
            errorMsg = "Depreciation process ran unsucessfuly.\nReason:" + e.getMessage();
        } finally {
            if (!hasErrors && executeJob) {
                int fiscalStartMonth = Integer
                        .parseInt(optionsService.getCurrentYearOptions().getUniversityFiscalYearStartMo());
                reportLog.addAll(depreciableAssetsDao.generateStatistics(false, documentNos, fiscalYear,
                        fiscalMonth, depreciationDate, dateTimeService.toDateString(depreciationDate.getTime()),
                        assetObjectCodes, fiscalStartMonth, errorMessage));
            }
            // the report will be generated only when there is an error or when the log has something.
            if (!reportLog.isEmpty() || !errorMsg.trim().equals("")) {
                reportService.generateDepreciationReport(reportLog, errorMsg, depreciationDateParameter);
            }

            if (LOG.isDebugEnabled()) {
                LOG.debug("*******" + CamsConstants.Depreciation.DEPRECIATION_BATCH + " HAS ENDED *******");
            }
        }
    }

    @Override
    public Collection<AssetObjectCode> getAssetObjectCodes(Integer fiscalYear) {
        LOG.debug("DepreciableAssetsDAoOjb.getAssetObjectCodes() -  started");
        LOG.info(CamsConstants.Depreciation.DEPRECIATION_BATCH + "Getting asset object codes.");

        Collection<AssetObjectCode> assetObjectCodesCollection;
        HashMap<String, Object> fields = new HashMap<String, Object>();
        fields.put(CamsPropertyConstants.AssetObject.UNIVERSITY_FISCAL_YEAR, fiscalYear);
        fields.put(CamsPropertyConstants.AssetObject.ACTIVE, Boolean.TRUE);
        assetObjectCodesCollection = businessObjectService.findMatching(AssetObjectCode.class, fields);

        LOG.info(CamsConstants.Depreciation.DEPRECIATION_BATCH + "Finished getting asset object codes - which are:"
                + assetObjectCodesCollection.toString());
        LOG.debug("DepreciableAssetsDAoOjb.getAssetObjectCodes() -  ended");
        return assetObjectCodesCollection;
    }

    // CSU 6702 BEGIN
    /**
     * @see org.kuali.kfs.module.cam.batch.service.AssetDepreciationService#runYearEndDepreciation(java.lang.Integer)
     */
    @Override
    public void runYearEndDepreciation(Integer fiscalYearToDepreciate) {
        Integer fiscalYear = -1;
        Integer fiscalMonth = -1;
        List<String> documentNos = new ArrayList<String>();
        String errorMsg = "";

        List<String[]> reportLog = new ArrayList<String[]>();
        boolean hasErrors = false;
        boolean includeRetired = false;
        Calendar depreciationDate = Calendar.getInstance();
        AssetYearEndDepreciation assetYearEndDepreciation = null;
        assetService = SpringContext.getBean(AssetService.class);
        List<String> notAcceptedAssetStatus = new ArrayList<String>();
        boolean statusContainsR = false;
        String notAcceptedAssetStatusString = "";
        String depreciationDateParameter = fiscalYearToDepreciate.toString() + getLastDayOfFiscalyear();
        notAcceptedAssetStatus.addAll(
                parameterService.getParameterValuesAsString(KfsParameterConstants.CAPITAL_ASSETS_BATCH.class,
                        CamsConstants.Parameters.NON_DEPRECIABLE_NON_CAPITAL_ASSETS_STATUS_CODES));
        LOG.info("notAcceptedAssetStatusString = " + notAcceptedAssetStatusString);
        if (notAcceptedAssetStatus.contains("R")) {
            LOG.info("looks like notAcceptedAssetStatusString contains R");
            statusContainsR = true;
            notAcceptedAssetStatus.remove("R");
            for (int i = 0; i < notAcceptedAssetStatus.size(); i++) {
                String s = notAcceptedAssetStatus.get(i);
                notAcceptedAssetStatusString = notAcceptedAssetStatusString + s + ";";
            }
            if (notAcceptedAssetStatusString.endsWith(";")) {
                notAcceptedAssetStatusString = notAcceptedAssetStatusString.substring(0,
                        (notAcceptedAssetStatusString.length() - 1));
            }
            Parameter.Builder param = Parameter.Builder
                    .create(parameterService.getParameter(KfsParameterConstants.CAPITAL_ASSETS_BATCH.class,
                            CamsConstants.Parameters.NON_DEPRECIABLE_NON_CAPITAL_ASSETS_STATUS_CODES));
            param.setValue(notAcceptedAssetStatusString);
            parameterService.updateParameter(param.build());
        }

        try {
            LOG.info("*******YEAR END DEPRECIATION - HAS BEGUN *******");

            //
            // Getting the system parameter "INCLUDE_RETIRED_ASSETS_IND" When this parameter is used to determine whether
            // to include retired assets in the depreciation
            //
            if (LOG.isInfoEnabled()) {
                LOG.info(
                        "parameterService.getParameterValueAsString(KfsParameterConstants.CAPITAL_ASSETS_BATCH.class, CamsConstants.Parameters.INCLUDE_RETIRED_ASSETS_IND) = "
                                + parameterService.getParameterValueAsString(
                                        KfsParameterConstants.CAPITAL_ASSETS_BATCH.class,
                                        CamsConstants.Parameters.INCLUDE_RETIRED_ASSETS_IND));
            }
            includeRetired = parameterService.getParameterValueAsBoolean(
                    KfsParameterConstants.CAPITAL_ASSETS_BATCH.class,
                    CamsConstants.Parameters.INCLUDE_RETIRED_ASSETS_IND);

            UniversityDate universityDate = businessObjectService.findBySinglePrimaryKey(UniversityDate.class,
                    new java.sql.Date(depreciationDate.getTimeInMillis()));
            if (universityDate == null) {
                throw new IllegalStateException(kualiConfigurationService
                        .getPropertyValueAsString(KFSKeyConstants.ERROR_UNIV_DATE_NOT_FOUND));
            }

            fiscalYear = universityDate.getUniversityFiscalYear();
            fiscalMonth = new Integer(universityDate.getUniversityFiscalAccountingPeriod());

            depreciationDate
                    .setTime(java.sql.Date.valueOf(fiscalYearToDepreciate.toString() + getLastDayOfFiscalyear()));
            fiscalYear = fiscalYearToDepreciate;
            fiscalMonth = 12;
            Collection<AssetObjectCode> assetObjectCodes = getAssetObjectCodes(fiscalYear);

            // If the depreciation date is not = to the system date then, the depreciation process cannot run.
            if (LOG.isInfoEnabled()) {
                LOG.info("YEAR END DEPRECIATION - " + "Fiscal Year = " + fiscalYear + " & Fiscal Period="
                        + fiscalMonth);
            }

            // mjmc this caused arrayIndexOutOfBounds: 16
            // TODO            reportLog.addAll(depreciableAssetsDao.generateStatistics(true, null, fiscalYear, fiscalMonth, depreciationDate, true));

            // update if fiscal period is 12
            depreciationBatchDao.updateAssetsCreatedInLastFiscalPeriod(fiscalMonth, fiscalYear);
            // Retrieving eligible asset payment details
            LOG.info(
                    "YEAR END DEPRECIATION - Getting list of YEAR END DEPRECIATION asset payments eligible for depreciation.");
            Collection<AssetPaymentInfo> depreciableAssetsCollection = depreciationBatchDao
                    .getListOfDepreciableAssetPaymentInfoYearEnd(fiscalYear, fiscalMonth, depreciationDate,
                            includeRetired);
            // if we have assets eligible for depreciation then, calculate depreciation and create glpe's transactions
            if (depreciableAssetsCollection != null && !depreciableAssetsCollection.isEmpty()) {
                SortedMap<String, AssetDepreciationTransaction> depreciationTransactions = this
                        .calculateYearEndDepreciation(depreciableAssetsCollection, depreciationDate,
                                fiscalYearToDepreciate, fiscalYear, fiscalMonth, assetObjectCodes);
                processYearEndGeneralLedgerPendingEntry(fiscalYear, documentNos, depreciationTransactions);
            } else {
                throw new IllegalStateException(kualiConfigurationService.getPropertyValueAsString(
                        CamsKeyConstants.Depreciation.NO_ELIGIBLE_FOR_DEPRECIATION_ASSETS_FOUND));
            }
        } catch (Exception e) {
            LOG.error(
                    "YEAR END DEPRECIATION - **************************************************************************");
            LOG.error("YEAR END DEPRECIATION - AN ERROR HAS OCCURRED! - ERROR: " + e.getClass().getName() + " : "
                    + e.getMessage());
            LOG.error(
                    "YEAR END DEPRECIATION - **************************************************************************");
            LOG.error(e);
            hasErrors = true;
            errorMsg = "YEAR END DEPRECIATION -  process ran unsucessfuly.\nReason:" + e.getMessage();
        } finally {
            if (!hasErrors) {

                // mjmc java.lang.ArrayIndexOutOfBoundsException: 16
                // TODO                reportLog.addAll(depreciableAssetsDao.generateStatistics(false, documentNos, fiscalYear, fiscalMonth, depreciationDate, true));

                if (assetYearEndDepreciation != null) {
                    assetYearEndDepreciation.setRunDate(new java.sql.Date(new java.util.Date().getTime()));
                }
            }

            // the report will be generated only when there is an error or when the log has something.
            if (!reportLog.isEmpty() || !errorMsg.trim().equals("")) {
                // mjmc java.lang.ArrayIndexOutOfBoundsException: 16
                reportService.generateDepreciationReport(reportLog, errorMsg, depreciationDateParameter);
            }

            LOG.info("******* YEAR END DEPRECIATION - HAS ENDED *******");
        }

        // reset param so that retired assets are not depreciated during the rest of the year
        if (statusContainsR) {
            notAcceptedAssetStatusString = notAcceptedAssetStatusString + ";R";
            if (LOG.isInfoEnabled()) {
                LOG.info("notAcceptedAssetStatusString after reset= " + notAcceptedAssetStatusString);
            }
            Parameter.Builder param = Parameter.Builder
                    .create(parameterService.getParameter(KfsParameterConstants.CAPITAL_ASSETS_BATCH.class,
                            CamsConstants.Parameters.NON_DEPRECIABLE_NON_CAPITAL_ASSETS_STATUS_CODES));
            param.setValue(notAcceptedAssetStatusString);
            parameterService.updateParameter(param.build());
            if (LOG.isInfoEnabled()) {
                LOG.info(CamsConstants.Parameters.NON_DEPRECIABLE_NON_CAPITAL_ASSETS_STATUS_CODES + " now = "
                        + parameterService.getParameterValueAsString(
                                KfsParameterConstants.CAPITAL_ASSETS_BATCH.class,
                                CamsConstants.Parameters.NON_DEPRECIABLE_NON_CAPITAL_ASSETS_STATUS_CODES));
            }
        }
    }

    protected boolean runAssetDepreciation() throws ParseException {
        boolean executeJob = false;
        List<String> errorMessages = new ArrayList<String>();
        Date currentDate = convertToDate(dateTimeService.toDateString(dateTimeService.getCurrentDate()));
        Date beginDate = getBlankOutBeginDate(errorMessages);
        Date endDate = getBlankOutEndDate(errorMessages);

        if (hasBlankOutPeriodStarted(beginDate, endDate, errorMessages)) {
            String blankOutPeriodrunDate = parameterService.getParameterValueAsString(AssetDepreciationStep.class,
                    CamsConstants.Parameters.BLANK_OUT_PERIOD_RUN_DATE);
            if (!StringHelper.isNullOrEmpty(blankOutPeriodrunDate)) {
                Date runDate = convertToDate(blankOutPeriodrunDate);

                if (runDate.compareTo(beginDate) >= 0 && runDate.compareTo(endDate) <= 0) {
                    if (currentDate.equals(runDate)) {
                        executeJob = true;
                    } else {
                        LOG.info("Today is not BLANK_OUT_PERIOD_RUN_DATE. executeJob not set to true");
                    }

                } else {
                    String blankOutBegin = parameterService.getParameterValueAsString(AssetDepreciationStep.class,
                            CamsConstants.Parameters.BLANK_OUT_BEGIN_MMDD);
                    String blankOutEnd = parameterService.getParameterValueAsString(AssetDepreciationStep.class,
                            CamsConstants.Parameters.BLANK_OUT_END_MMDD);
                    String message = "BLANK_OUT_PERIOD_RUN_DATE: " + blankOutPeriodrunDate
                            + " is not in the blank out period range." + "Blank out period range is [ "
                            + blankOutBegin + "-" + blankOutEnd + " ] .";
                    errorMessages.add(message);
                    LOG.info(message);
                }
            } else {
                String message = "Parameter BLANK_OUT_PERIOD_RUN_DATE (component: Asset Depreciation Step) is not set"
                        + " Please set the date correctly to run the job.";
                errorMessages.add(message);
                LOG.info(message);
            }
        } else {
            if (getSchedulerService().cronConditionMet(this.cronExpression)) {
                executeJob = true;
            } else {
                LOG.info("Cron condition not met. executeJob not set to true");
            }
        }

        if (!executeJob && !errorMessages.isEmpty()) {
            sendWarningMail(errorMessages);
        }

        return executeJob;
    }

    protected boolean hasBlankOutPeriodStarted(Date beginDate, Date endDate, List<String> errorMessages)
            throws ParseException {
        Date currentDate = convertToDate(dateTimeService.toDateString(dateTimeService.getCurrentDate()));
        String blankOutBegin = parameterService.getParameterValueAsString(AssetDepreciationStep.class,
                CamsConstants.Parameters.BLANK_OUT_BEGIN_MMDD);
        String blankOutEnd = parameterService.getParameterValueAsString(AssetDepreciationStep.class,
                CamsConstants.Parameters.BLANK_OUT_END_MMDD);
        if (ObjectUtils.isNotNull(beginDate) && ObjectUtils.isNotNull(endDate)) {
            if (currentDate.compareTo(beginDate) >= 0 && currentDate.compareTo(endDate) <= 0) {
                return true;
            }
        } else {
            String message = "Unable to determine blank out period for a given " + blankOutBegin + " - "
                    + blankOutEnd + " range .";

            errorMessages.add(message);
            LOG.info(message);
        }

        return false;
    }

    /**
     *
     * This method calculate blank out period end date.
     * @return blank out period end date in MM/dd/yyyy format.
     * @throws ParseException
     */
    private Date getBlankOutEndDate(List<String> errorMessages) throws ParseException {
        String endDate = parameterService.getParameterValueAsString(AssetDepreciationStep.class,
                CamsConstants.Parameters.BLANK_OUT_END_MMDD);
        if (!StringHelper.isNullOrEmpty(endDate)) {
            int endDay = new Integer(StringUtils.substringAfterLast(endDate, "/")).intValue();
            int endMonth = new Integer(StringUtils.substringBeforeLast(endDate, "/")).intValue() - 1;
            Calendar blankOutEndcalendar = Calendar.getInstance();
            blankOutEndcalendar.set(blankOutEndcalendar.get(Calendar.YEAR), endMonth, endDay);
            return convertToDate(dateTimeService.toString(blankOutEndcalendar.getTime(),
                    CamsConstants.DateFormats.MONTH_DAY_YEAR));

        } else {

            String message = "Parameter BLANK_OUT_END_MMDD (component:Asset Depreciation Step) is not set.";
            errorMessages.add(message);
            LOG.info(message);

        }

        return null;
    }

    /**
     *
     * This method calculate blank out period begin date.
     * @return blank out period begin date in MM/dd/yyyy format.
     * @throws ParseException
     */
    private Date getBlankOutBeginDate(List<String> errorMessages) throws ParseException {
        String beginDate = parameterService.getParameterValueAsString(AssetDepreciationStep.class,
                CamsConstants.Parameters.BLANK_OUT_BEGIN_MMDD);

        if (!StringHelper.isNullOrEmpty(beginDate)) {
            int beginDay = new Integer(StringUtils.substringAfterLast(beginDate, "/")).intValue();
            int beginMonth = new Integer(StringUtils.substringBeforeLast(beginDate, "/")).intValue() - 1;
            Calendar blankOutBegincalendar = Calendar.getInstance();
            blankOutBegincalendar.set(blankOutBegincalendar.get(Calendar.YEAR), beginMonth, beginDay);
            return convertToDate(dateTimeService.toString(blankOutBegincalendar.getTime(),
                    CamsConstants.DateFormats.MONTH_DAY_YEAR));

        } else {
            String message = "Parameter BLANK_OUT_BEGIN_MMDD (component:Asset Depreciation Step) is not set.";
            errorMessages.add(message);
            LOG.info(message);

        }

        return null;
    }

    private Date convertToDate(String date) throws ParseException {
        DateFormat dateFormat = new SimpleDateFormat(CamsConstants.DateFormats.MONTH_DAY_YEAR);
        dateFormat.setLenient(false);
        return dateFormat.parse(date);

    }

    /**
     * This method calculates the depreciation of each asset payment, creates the depreciation transactions that will be stored in
     * the general ledger pending entry table
     *
     * @param depreciableAssetsCollection asset payments eligible for depreciation
     * @return SortedMap with a list of depreciation transactions
     */
    protected SortedMap<String, AssetDepreciationTransaction> calculateDepreciation(Integer fiscalYear,
            Integer fiscalMonth, Collection<AssetPaymentInfo> depreciableAssetsCollection,
            Calendar depreciationDate, Collection<AssetObjectCode> assetObjectCodes) {
        LOG.debug("calculateDepreciation() - start");

        Collection<String> organizationPlantFundObjectSubType = new ArrayList<String>();
        Collection<String> campusPlantFundObjectSubType = new ArrayList<String>();
        SortedMap<String, AssetDepreciationTransaction> depreciationTransactionSummary = new TreeMap<String, AssetDepreciationTransaction>();
        double monthsElapsed = 0d;
        double assetLifeInMonths = 0d;
        KualiDecimal accumulatedDepreciationAmount = KualiDecimal.ZERO;
        Calendar assetDepreciationDate = Calendar.getInstance();

        try {
            LOG.debug(CamsConstants.Depreciation.DEPRECIATION_BATCH
                    + "Getting the parameters for the plant fund object sub types.");
            // Getting system parameters needed.
            if (parameterService.parameterExists(KfsParameterConstants.CAPITAL_ASSETS_BATCH.class,
                    CamsConstants.Parameters.DEPRECIATION_ORGANIZATON_PLANT_FUND_SUB_OBJECT_TYPES)) {
                organizationPlantFundObjectSubType = new ArrayList<String>(parameterService
                        .getParameterValuesAsString(KfsParameterConstants.CAPITAL_ASSETS_BATCH.class,
                                CamsConstants.Parameters.DEPRECIATION_ORGANIZATON_PLANT_FUND_SUB_OBJECT_TYPES));
            }
            if (parameterService.parameterExists(KfsParameterConstants.CAPITAL_ASSETS_BATCH.class,
                    CamsConstants.Parameters.DEPRECIATION_CAMPUS_PLANT_FUND_OBJECT_SUB_TYPES)) {
                campusPlantFundObjectSubType = new ArrayList<String>(parameterService.getParameterValuesAsString(
                        KfsParameterConstants.CAPITAL_ASSETS_BATCH.class,
                        CamsConstants.Parameters.DEPRECIATION_CAMPUS_PLANT_FUND_OBJECT_SUB_TYPES));
            }
            // Initializing the asset payment table.
            depreciationBatchDao.resetPeriodValuesWhenFirstFiscalPeriod(fiscalMonth);
            LOG.debug("getBaseAmountOfAssets(Collection<AssetPayment> depreciableAssetsCollection) - Started.");
            // Invoking method that will calculate the base amount for each asset payment transactions, which could be more than 1
            // per asset.
            LOG.debug(
                    CamsConstants.Depreciation.DEPRECIATION_BATCH + "Calculating the base amount for each asset.");
            Map<Long, KualiDecimal> salvageValueAssetDeprAmounts = depreciationBatchDao
                    .getPrimaryDepreciationBaseAmountForSV();
            // Retrieving the object asset codes.
            Map<String, AssetObjectCode> assetObjectCodeMap = buildChartObjectToCapitalizationObjectMap(
                    assetObjectCodes);
            Map<String, ObjectCode> capitalizationObjectCodes = new HashMap<String, ObjectCode>();

            // Reading asset payments
            LOG.debug(CamsConstants.Depreciation.DEPRECIATION_BATCH
                    + "Reading collection with eligible asset payment details.");
            int counter = 0;
            List<AssetPaymentInfo> saveList = new ArrayList<AssetPaymentInfo>();
            for (AssetPaymentInfo assetPaymentInfo : depreciableAssetsCollection) {
                AssetObjectCode assetObjectCode = assetObjectCodeMap.get(assetPaymentInfo.getChartOfAccountsCode()
                        + "-" + assetPaymentInfo.getFinancialObjectCode());
                if (assetObjectCode == null) {
                    LOG.error(CamsConstants.Depreciation.DEPRECIATION_BATCH + "Asset object code not found for "
                            + fiscalYear + "-" + assetPaymentInfo.getChartOfAccountsCode() + "-"
                            + assetPaymentInfo.getFinancialObjectCode());
                    LOG.error(CamsConstants.Depreciation.DEPRECIATION_BATCH
                            + "Asset payment is not included in depreciation "
                            + assetPaymentInfo.getCapitalAssetNumber() + " - "
                            + assetPaymentInfo.getPaymentSequenceNumber());
                    continue;
                }
                ObjectCode accumulatedDepreciationFinancialObject = getDepreciationObjectCode(fiscalYear,
                        capitalizationObjectCodes, assetPaymentInfo,
                        assetObjectCode.getAccumulatedDepreciationFinancialObjectCode());
                ObjectCode depreciationExpenseFinancialObject = getDepreciationObjectCode(fiscalYear,
                        capitalizationObjectCodes, assetPaymentInfo,
                        assetObjectCode.getDepreciationExpenseFinancialObjectCode());

                if (ObjectUtils.isNull(accumulatedDepreciationFinancialObject)) {
                    LOG.error(CamsConstants.Depreciation.DEPRECIATION_BATCH
                            + "Accumulated Depreciation Financial Object Code not found for " + fiscalYear + "-"
                            + assetPaymentInfo.getChartOfAccountsCode() + "-"
                            + assetObjectCode.getAccumulatedDepreciationFinancialObjectCode());
                    LOG.error(CamsConstants.Depreciation.DEPRECIATION_BATCH
                            + "Asset payment is not included in depreciation "
                            + assetPaymentInfo.getCapitalAssetNumber() + " - "
                            + assetPaymentInfo.getPaymentSequenceNumber());
                    continue;
                }

                if (ObjectUtils.isNull(depreciationExpenseFinancialObject)) {
                    LOG.error(CamsConstants.Depreciation.DEPRECIATION_BATCH
                            + "Depreciation Expense Financial Object Code not found for " + fiscalYear + "-"
                            + assetPaymentInfo.getChartOfAccountsCode() + "-"
                            + assetObjectCode.getDepreciationExpenseFinancialObjectCode());
                    LOG.error(CamsConstants.Depreciation.DEPRECIATION_BATCH
                            + "Asset payment is not included in depreciation "
                            + assetPaymentInfo.getCapitalAssetNumber() + " - "
                            + assetPaymentInfo.getPaymentSequenceNumber());
                    continue;
                }
                Long assetNumber = assetPaymentInfo.getCapitalAssetNumber();
                assetDepreciationDate.setTime(assetPaymentInfo.getDepreciationDate());
                accumulatedDepreciationAmount = KualiDecimal.ZERO;
                KualiDecimal deprAmountSum = salvageValueAssetDeprAmounts.get(assetNumber);
                // Calculating the life of the asset in months.
                assetLifeInMonths = assetPaymentInfo.getDepreciableLifeLimit() * 12;
                // Calculating the months elapsed for the asset using the depreciation date and the asset service date.
                monthsElapsed = (depreciationDate.get(Calendar.MONTH) - assetDepreciationDate.get(Calendar.MONTH)
                        + (depreciationDate.get(Calendar.YEAR) - assetDepreciationDate.get(Calendar.YEAR)) * 12)
                        + 1;

                // **************************************************************************************************************
                // CALCULATING ACCUMULATED DEPRECIATION BASED ON FORMULA FOR SINGLE LINE AND SALVAGE VALUE DEPRECIATION METHODS.
                // **************************************************************************************************************
                KualiDecimal primaryDepreciationBaseAmount = assetPaymentInfo.getPrimaryDepreciationBaseAmount();
                if (primaryDepreciationBaseAmount == null) {
                    assetPaymentInfo.setPrimaryDepreciationBaseAmount(KualiDecimal.ZERO);
                }

                if (assetPaymentInfo.getAccumulatedPrimaryDepreciationAmount() == null) {
                    assetPaymentInfo.setAccumulatedPrimaryDepreciationAmount(KualiDecimal.ZERO);
                }

                // If the months elapsed >= to the life of the asset (in months) then, the accumulated depreciation should be:
                if (monthsElapsed >= assetLifeInMonths) {
                    if (CamsConstants.Asset.DEPRECIATION_METHOD_STRAIGHT_LINE_CODE
                            .equals(assetPaymentInfo.getPrimaryDepreciationMethodCode())) {
                        accumulatedDepreciationAmount = primaryDepreciationBaseAmount;
                    } else if (CamsConstants.Asset.DEPRECIATION_METHOD_SALVAGE_VALUE_CODE
                            .equals(assetPaymentInfo.getPrimaryDepreciationMethodCode()) && deprAmountSum != null
                            && deprAmountSum.isNonZero()) {
                        accumulatedDepreciationAmount = primaryDepreciationBaseAmount
                                .subtract((primaryDepreciationBaseAmount.divide(deprAmountSum))
                                        .multiply(assetPaymentInfo.getSalvageAmount()));
                    }
                } // If the month elapse < to the life of the asset (in months) then....
                else {
                    if (CamsConstants.Asset.DEPRECIATION_METHOD_STRAIGHT_LINE_CODE
                            .equals(assetPaymentInfo.getPrimaryDepreciationMethodCode())) {
                        accumulatedDepreciationAmount = new KualiDecimal(
                                (monthsElapsed / assetLifeInMonths) * primaryDepreciationBaseAmount.doubleValue());
                    } else if (CamsConstants.Asset.DEPRECIATION_METHOD_SALVAGE_VALUE_CODE
                            .equals(assetPaymentInfo.getPrimaryDepreciationMethodCode()) && deprAmountSum != null
                            && deprAmountSum.isNonZero()) {
                        accumulatedDepreciationAmount = new KualiDecimal(
                                (monthsElapsed / assetLifeInMonths) * (primaryDepreciationBaseAmount
                                        .subtract((primaryDepreciationBaseAmount.divide(deprAmountSum))
                                                .multiply(assetPaymentInfo.getSalvageAmount()))).doubleValue());
                    }
                }
                // Calculating in process fiscal month depreciation amount
                KualiDecimal transactionAmount = accumulatedDepreciationAmount
                        .subtract(assetPaymentInfo.getAccumulatedPrimaryDepreciationAmount());

                String transactionType = KFSConstants.GL_DEBIT_CODE;
                if (transactionAmount.isNegative()) {
                    transactionType = KFSConstants.GL_CREDIT_CODE;
                }
                String plantAccount = "";
                String plantCOA = "";

                // getting the right Plant Fund Chart code & Plant Fund Account
                if (organizationPlantFundObjectSubType.contains(assetPaymentInfo.getFinancialObjectSubTypeCode())) {
                    plantAccount = assetPaymentInfo.getOrganizationPlantAccountNumber();
                    plantCOA = assetPaymentInfo.getOrganizationPlantChartCode();
                } else if (campusPlantFundObjectSubType
                        .contains(assetPaymentInfo.getFinancialObjectSubTypeCode())) {
                    plantAccount = assetPaymentInfo.getCampusPlantAccountNumber();
                    plantCOA = assetPaymentInfo.getCampusPlantChartCode();
                }
                if (StringUtils.isBlank(plantCOA) || StringUtils.isBlank(plantAccount)) {
                    // skip the payment
                    LOG.error(CamsConstants.Depreciation.DEPRECIATION_BATCH + "Plant COA is " + plantCOA
                            + " and plant account is " + plantAccount + " for Financial Object SubType Code = "
                            + assetPaymentInfo.getFinancialObjectSubTypeCode()
                            + " so Asset payment is not included in depreciation "
                            + assetPaymentInfo.getCapitalAssetNumber() + " - "
                            + assetPaymentInfo.getPaymentSequenceNumber());
                    continue;
                }
                LOG.debug("Asset#: " + assetNumber + " - Payment sequence#:"
                        + assetPaymentInfo.getPaymentSequenceNumber() + " - Asset Depreciation date:"
                        + assetDepreciationDate + " - Life:" + assetLifeInMonths + " - Depreciation base amt:"
                        + primaryDepreciationBaseAmount + " - Accumulated depreciation:"
                        + assetPaymentInfo.getAccumulatedPrimaryDepreciationAmount() + " - Month Elapsed:"
                        + monthsElapsed + " - Calculated accum depreciation:" + accumulatedDepreciationAmount
                        + " - Depreciation amount:" + transactionAmount.toString() + " - Depreciation Method:"
                        + assetPaymentInfo.getPrimaryDepreciationMethodCode());
                assetPaymentInfo.setAccumulatedPrimaryDepreciationAmount(accumulatedDepreciationAmount);
                assetPaymentInfo.setTransactionAmount(transactionAmount);
                counter++;
                saveList.add(assetPaymentInfo);
                // Saving depreciation amount in the asset payment table
                if (counter % 1000 == 0) {
                    getDepreciationBatchDao().updateAssetPayments(saveList, fiscalMonth);
                    saveList.clear();
                }
                // if the asset has a depreciation amount <> 0 then, create its debit and credit entries.
                if (transactionAmount.isNonZero()) {
                    this.populateDepreciationTransaction(assetPaymentInfo, transactionType, plantCOA, plantAccount,
                            depreciationExpenseFinancialObject, depreciationTransactionSummary);
                    transactionType = (transactionType.equals(KFSConstants.GL_DEBIT_CODE)
                            ? KFSConstants.GL_CREDIT_CODE
                            : KFSConstants.GL_DEBIT_CODE);
                    this.populateDepreciationTransaction(assetPaymentInfo, transactionType, plantCOA, plantAccount,
                            accumulatedDepreciationFinancialObject, depreciationTransactionSummary);
                }
            }
            getDepreciationBatchDao().updateAssetPayments(saveList, fiscalMonth);
            saveList.clear();
            return depreciationTransactionSummary;
        } catch (Exception e) {
            LOG.error("Error occurred", e);
            throw new IllegalStateException(kualiConfigurationService.getPropertyValueAsString(
                    CamsKeyConstants.Depreciation.ERROR_WHEN_CALCULATING_DEPRECIATION) + " :" + e.getMessage());
        }
    }

    /**
     * This method stores in a collection of business objects the depreciation transaction that later on will be passed to the
     * processGeneralLedgerPendingEntry method in order to store the records in gl pending entry table
     *
     * @param assetPayment asset payment
     * @param transactionType which can be [C]redit or [D]ebit
     * @param plantCOA plant fund char of account code
     * @param plantAccount plant fund char of account code
     * @param financialObject char of account object code linked to the payment
     * @param depreciationTransactionSummary
     * @return none
     */
    protected void populateDepreciationTransaction(AssetPaymentInfo assetPayment, String transactionType,
            String plantCOA, String plantAccount, ObjectCode deprObjectCode,
            SortedMap<String, AssetDepreciationTransaction> depreciationTransactionSummary) {
        LOG.debug(
                "populateDepreciationTransaction(AssetDepreciationTransaction depreciationTransaction, AssetPayment assetPayment, String transactionType, KualiDecimal transactionAmount, String plantCOA, String plantAccount, String accumulatedDepreciationFinancialObjectCode, String depreciationExpenseFinancialObjectCode, ObjectCode financialObject, SortedMap<String, AssetDepreciationTransaction> depreciationTransactionSummary) -  started");
        LOG.debug(CamsConstants.Depreciation.DEPRECIATION_BATCH
                + "populateDepreciationTransaction(): populating AssetDepreciationTransaction pojo - Asset#:"
                + assetPayment.getCapitalAssetNumber());
        AssetDepreciationTransaction depreciationTransaction = new AssetDepreciationTransaction();
        depreciationTransaction.setCapitalAssetNumber(assetPayment.getCapitalAssetNumber());
        depreciationTransaction.setChartOfAccountsCode(plantCOA);
        depreciationTransaction.setAccountNumber(plantAccount);
        depreciationTransaction.setSubAccountNumber(assetPayment.getSubAccountNumber());
        depreciationTransaction.setFinancialObjectCode(deprObjectCode.getFinancialObjectCode());
        depreciationTransaction.setFinancialSubObjectCode(assetPayment.getFinancialSubObjectCode());
        depreciationTransaction.setFinancialObjectTypeCode(deprObjectCode.getFinancialObjectTypeCode());
        depreciationTransaction.setTransactionType(transactionType);
        depreciationTransaction.setProjectCode(assetPayment.getProjectCode());
        depreciationTransaction.setTransactionAmount(assetPayment.getTransactionAmount());
        depreciationTransaction.setTransactionLedgerEntryDescription(
                CamsConstants.Depreciation.TRANSACTION_DESCRIPTION + assetPayment.getCapitalAssetNumber());

        String sKey = depreciationTransaction.getKey();

        // Grouping the asset transactions by asset#, accounts, sub account, object, transaction type (C/D), etc. in order to
        // only have one credit and one credit by group.
        if (depreciationTransactionSummary.containsKey(sKey)) {
            depreciationTransaction = depreciationTransactionSummary.get(sKey);
            depreciationTransaction.setTransactionAmount(
                    depreciationTransaction.getTransactionAmount().add(assetPayment.getTransactionAmount()));
        } else {
            depreciationTransactionSummary.put(sKey, depreciationTransaction);
        }
        LOG.debug(
                "populateDepreciationTransaction(AssetDepreciationTransaction depreciationTransaction, AssetPayment assetPayment, String transactionType, KualiDecimal transactionAmount, String plantCOA, String plantAccount, String accumulatedDepreciationFinancialObjectCode, String depreciationExpenseFinancialObjectCode, ObjectCode financialObject, SortedMap<String, AssetDepreciationTransaction> depreciationTransactionSummary) -  ended");
    }

    /**
     * This method stores the depreciation transactions in the general pending entry table and creates a new documentHeader entry.
     * <p>
     *
     * @param trans SortedMap with the transactions
     * @return none
     */
    protected void processGeneralLedgerPendingEntry(Integer fiscalYear, Integer fiscalMonth,
            List<String> documentNos, SortedMap<String, AssetDepreciationTransaction> trans) {
        LOG.debug(
                "populateExplicitGeneralLedgerPendingEntry(AccountingDocument, AccountingLine, GeneralLedgerPendingEntrySequenceHelper, GeneralLedgerPendingEntry) - start");

        String financialSystemDocumentTypeCodeCode;
        try {

            String documentNumber = createNewDepreciationDocument(documentNos);
            financialSystemDocumentTypeCodeCode = CamsConstants.DocumentTypeName.ASSET_DEPRECIATION;
            LOG.debug(CamsConstants.Depreciation.DEPRECIATION_BATCH + "Depreciation Document Type Code: "
                    + financialSystemDocumentTypeCodeCode);

            Timestamp transactionTimestamp = new Timestamp(dateTimeService.getCurrentDate().getTime());

            GeneralLedgerPendingEntrySequenceHelper sequenceHelper = new GeneralLedgerPendingEntrySequenceHelper();
            List<GeneralLedgerPendingEntry> saveList = new ArrayList<GeneralLedgerPendingEntry>();
            int counter = 0;

            for (AssetDepreciationTransaction t : trans.values()) {
                if (t.getTransactionAmount().isNonZero()) {
                    counter++;
                    LOG.debug(CamsConstants.Depreciation.DEPRECIATION_BATCH + "Creating GLPE entries for asset:"
                            + t.getCapitalAssetNumber());
                    GeneralLedgerPendingEntry explicitEntry = new GeneralLedgerPendingEntry();
                    explicitEntry.setFinancialSystemOriginationCode(KFSConstants.ORIGIN_CODE_KUALI);
                    explicitEntry.setDocumentNumber(documentNumber);
                    explicitEntry.setTransactionLedgerEntrySequenceNumber(
                            new Integer(sequenceHelper.getSequenceCounter()));
                    sequenceHelper.increment();
                    explicitEntry.setChartOfAccountsCode(t.getChartOfAccountsCode());
                    explicitEntry.setAccountNumber(t.getAccountNumber());
                    explicitEntry.setSubAccountNumber(null);
                    explicitEntry.setFinancialObjectCode(t.getFinancialObjectCode());
                    explicitEntry.setFinancialSubObjectCode(null);
                    explicitEntry.setFinancialBalanceTypeCode(BALANCE_TYPE_ACTUAL);
                    explicitEntry.setFinancialObjectTypeCode(t.getFinancialObjectTypeCode());
                    explicitEntry.setUniversityFiscalYear(fiscalYear);
                    explicitEntry.setUniversityFiscalPeriodCode(
                            StringUtils.leftPad(fiscalMonth.toString().trim(), 2, "0"));
                    explicitEntry.setTransactionLedgerEntryDescription(t.getTransactionLedgerEntryDescription());
                    explicitEntry.setTransactionLedgerEntryAmount(t.getTransactionAmount().abs());
                    explicitEntry.setTransactionDebitCreditCode(t.getTransactionType());
                    explicitEntry.setTransactionDate(new java.sql.Date(transactionTimestamp.getTime()));
                    explicitEntry.setFinancialDocumentTypeCode(financialSystemDocumentTypeCodeCode);
                    explicitEntry.setFinancialDocumentApprovedCode(KFSConstants.DocumentStatusCodes.APPROVED);
                    explicitEntry.setVersionNumber(new Long(1));
                    explicitEntry
                            .setTransactionEntryProcessedTs(new java.sql.Timestamp(transactionTimestamp.getTime()));
                    // this.generalLedgerPendingEntryService.save(explicitEntry);
                    saveList.add(explicitEntry);
                    if (counter % 1000 == 0) {
                        // save here
                        getDepreciationBatchDao().savePendingGLEntries(saveList);
                        saveList.clear();
                    }
                    if (sequenceHelper.getSequenceCounter() == 99999) {
                        // create new document and sequence is reset
                        documentNumber = createNewDepreciationDocument(documentNos);
                        sequenceHelper = new GeneralLedgerPendingEntrySequenceHelper();
                    }
                }
            }
            // save last list
            getDepreciationBatchDao().savePendingGLEntries(saveList);
            saveList.clear();

        } catch (Exception e) {
            LOG.error("Error occurred", e);
            throw new IllegalStateException(kualiConfigurationService.getPropertyValueAsString(
                    CamsKeyConstants.Depreciation.ERROR_WHEN_UPDATING_GL_PENDING_ENTRY_TABLE) + " :"
                    + e.getMessage());
        }
        LOG.debug(
                "populateExplicitGeneralLedgerPendingEntry(AccountingDocument, AccountingLine, GeneralLedgerPendingEntrySequenceHelper, GeneralLedgerPendingEntry) - end");
    }

    protected String createNewDepreciationDocument(List<String> documentNos) throws WorkflowException {
        WorkflowDocument workflowDocument = getWorkflowDocumentService().createWorkflowDocument(
                CamsConstants.DocumentTypeName.ASSET_DEPRECIATION, GlobalVariables.getUserSession().getPerson());
        // **************************************************************************************************
        // Create a new document header object
        // **************************************************************************************************
        LOG.debug(CamsConstants.Depreciation.DEPRECIATION_BATCH + "Creating document header entry.");

        FinancialSystemDocumentHeader documentHeader = new FinancialSystemDocumentHeader();
        documentHeader.setWorkflowDocument(workflowDocument);
        documentHeader.setDocumentNumber(workflowDocument.getDocumentId());
        documentHeader.setFinancialDocumentStatusCode(KFSConstants.DocumentStatusCodes.APPROVED);
        documentHeader.setExplanation(CamsConstants.Depreciation.DOCUMENT_DESCRIPTION);
        documentHeader.setDocumentDescription(CamsConstants.Depreciation.DOCUMENT_DESCRIPTION);
        documentHeader.setFinancialDocumentTotalAmount(KualiDecimal.ZERO);

        LOG.debug(CamsConstants.Depreciation.DEPRECIATION_BATCH + "Saving document header entry.");
        this.businessObjectService.save(documentHeader);
        LOG.debug(CamsConstants.Depreciation.DEPRECIATION_BATCH + "Document Header entry was saved successfully.");
        // **************************************************************************************************

        String documentNumber = documentHeader.getDocumentNumber();
        documentNos.add(documentNumber);
        LOG.debug(CamsConstants.Depreciation.DEPRECIATION_BATCH + "Document Number Created: " + documentNumber);
        return documentNumber;
    }

    /**
     * Depreciation object code is returned from cache or from DB
     *
     * @param capitalizationObjectCodes collection cache
     * @param assetPaymentInfo
     * @param capitalizationFinancialObjectCode
     * @return
     */
    protected ObjectCode getDepreciationObjectCode(Integer fiscalYear, Map<String, ObjectCode> capObjectCodesCache,
            AssetPaymentInfo assetPaymentInfo, String capitalizationFinancialObjectCode) {
        ObjectCode deprObjCode = null;
        String key = assetPaymentInfo.getChartOfAccountsCode() + "-" + capitalizationFinancialObjectCode;
        if ((deprObjCode = capObjectCodesCache.get(key)) == null) {
            deprObjCode = SpringContext.getBean(ObjectCodeService.class).getByPrimaryId(fiscalYear,
                    assetPaymentInfo.getChartOfAccountsCode(), capitalizationFinancialObjectCode);
            if (ObjectUtils.isNotNull(deprObjCode)) {
                capObjectCodesCache.put(key, deprObjCode);
            }
        }
        return deprObjCode;
    }

    /**
     * Builds map between object code to corresponding asset object code
     *
     * @return Map
     */
    protected Map<String, AssetObjectCode> buildChartObjectToCapitalizationObjectMap(
            Collection<AssetObjectCode> assetObjectCodes) {
        Map<String, AssetObjectCode> assetObjectCodeMap = new HashMap<String, AssetObjectCode>();

        for (AssetObjectCode assetObjectCode : assetObjectCodes) {
            List<ObjectCode> objectCodes = assetObjectCode.getObjectCode();
            for (ObjectCode objectCode : objectCodes) {
                String key = objectCode.getChartOfAccountsCode() + "-" + objectCode.getFinancialObjectCode();
                if (!assetObjectCodeMap.containsKey(key)) {
                    assetObjectCodeMap.put(key, assetObjectCode);
                }
            }
        }
        return assetObjectCodeMap;
    }

    private void sendWarningMail(List<String> errorMessages) {

        LOG.debug("sendEmail() starting");
        MailMessage message = new MailMessage();

        message.setFromAddress(mailService.getBatchMailingList());
        String subject = "Asset Depreciation Job status";
        message.setSubject(subject);
        Collection<String> toAddresses = parameterService.getParameterValuesAsString(AssetDepreciationStep.class,
                CamsConstants.Parameters.RUN_DATE_NOTIFICATION_EMAIL_ADDRESSES);
        message.getToAddresses().add(toAddresses);

        StringBuffer sb = new StringBuffer();
        sb.append("Unable to run Depreciation process.Reason:\n");
        for (String msg : errorMessages) {
            sb.append(msg + "\n");
        }

        sb.append("Please set the dates correctly to run the job.");

        message.setMessage(sb.toString());

        try {
            mailService.sendMessage(message);
        } catch (MessagingException e) {
            LOG.error("sendErrorEmail() Invalid email address. Message not sent", e);
        } catch (InvalidAddressException e) {
            LOG.error("sendErrorEmail() Invalid email address. Message not sent", e);
        }
    }

    /**
     * Get the last month and day of the fiscal year.  Returned in the format '-mm-dd'
     * @return
     */
    protected String getLastDayOfFiscalyear() {
        ParameterService parameterService = SpringContext.getBean(ParameterService.class);
        String date = parameterService.getParameterValueAsString(KfsParameterConstants.CAPITAL_ASSETS_ALL.class,
                CamsConstants.Parameters.FISCAL_YEAR_END_MONTH_AND_DAY);
        return "-" + date.substring(0, 2) + "-" + date.substring(2);
    }

    protected void populateYearEndDepreciationTransaction(AssetPaymentInfo assetPayment, String transactionType,
            String plantCOA, String plantAccount, ObjectCode deprObjectCode,
            SortedMap<String, AssetDepreciationTransaction> depreciationTransactionSummary) {
        LOG.info("\npopulateYearEndDepreciationTransaction - Asset#:" + assetPayment.getCapitalAssetNumber()
                + " amount:" + assetPayment.getTransactionAmount() + " type:" + transactionType);
        LOG.info("deprObjectCode.getFinancialObjectCode():" + deprObjectCode.getFinancialObjectCode()
                + " deprObjectCode.getFinancialObjectTypeCode():" + deprObjectCode.getFinancialObjectTypeCode());
        AssetDepreciationTransaction depreciationTransaction = new AssetDepreciationTransaction();
        depreciationTransaction.setCapitalAssetNumber(assetPayment.getCapitalAssetNumber());
        depreciationTransaction.setChartOfAccountsCode(plantCOA);
        depreciationTransaction.setAccountNumber(plantAccount);
        depreciationTransaction.setSubAccountNumber(assetPayment.getSubAccountNumber());
        depreciationTransaction.setFinancialObjectCode(deprObjectCode.getFinancialObjectCode());
        depreciationTransaction.setFinancialSubObjectCode(assetPayment.getFinancialSubObjectCode());
        depreciationTransaction.setFinancialObjectTypeCode(deprObjectCode.getFinancialObjectTypeCode());
        depreciationTransaction.setTransactionType(transactionType);
        depreciationTransaction.setProjectCode(assetPayment.getProjectCode());
        depreciationTransaction.setTransactionAmount(assetPayment.getTransactionAmount());
        depreciationTransaction.setTransactionLedgerEntryDescription(
                "Year End Depreciation Asset " + assetPayment.getCapitalAssetNumber());

        String sKey = depreciationTransaction.getKey();

        // Grouping the asset transactions by asset#, accounts, sub account, object, transaction type (C/D), etc. in order to
        // only have one credit and one credit by group.
        if (depreciationTransactionSummary.containsKey(sKey)) {
            LOG.info("depreciationTransactionSummary.containsKey(sKey) where sKey=" + sKey);
            depreciationTransaction = depreciationTransactionSummary.get(sKey);
            depreciationTransaction.setTransactionAmount(
                    depreciationTransaction.getTransactionAmount().add(assetPayment.getTransactionAmount()));
        } else {
            LOG.info("depreciationTransactionSummary DOESNT containsKey(sKey) where sKey=" + sKey);
            depreciationTransactionSummary.put(sKey, depreciationTransaction);
        }
        LOG.info("\n\n");
        //  LOG.info("populateYearEndDepreciationTransaction(AssetDepreciationTransaction depreciationTransaction, AssetPayment assetPayment, String transactionType, KualiDecimal transactionAmount, String plantCOA, String plantAccount, String accumulatedDepreciationFinancialObjectCode, String depreciationExpenseFinancialObjectCode, ObjectCode financialObject, SortedMap<String, AssetDepreciationTransaction> depreciationTransactionSummary) -  ended");
    }

    protected SortedMap<String, AssetDepreciationTransaction> calculateYearEndDepreciation(
            Collection<AssetPaymentInfo> depreciableAssetsCollection, Calendar depreciationDate,
            Integer fiscalYearToDepreciate, Integer fiscalYear, Integer fiscalMonth,
            Collection<AssetObjectCode> assetObjectCodes) {
        LOG.info("calculateDepreciation() - start");

        SortedMap<String, AssetDepreciationTransaction> depreciationTransactionSummary = new TreeMap<String, AssetDepreciationTransaction>();
        double monthsElapsed = 0d;
        double assetLifeInMonths = 0d;
        KualiDecimal accumulatedDepreciationAmount = KualiDecimal.ZERO;
        Calendar assetDepreciationDate = Calendar.getInstance();

        try {
            LOG.info("YEAR END DEPRECIATION - Getting the parameters for the plant fund object sub types.");
            // Getting system parameters needed.
            Collection<String> organizationPlantFundObjectSubType = parameterService.getParameterValuesAsString(
                    KfsParameterConstants.CAPITAL_ASSETS_BATCH.class,
                    CamsConstants.Parameters.DEPRECIATION_ORGANIZATON_PLANT_FUND_SUB_OBJECT_TYPES);
            Collection<String> campusPlantFundObjectSubType = parameterService.getParameterValuesAsString(
                    KfsParameterConstants.CAPITAL_ASSETS_BATCH.class,
                    CamsConstants.Parameters.DEPRECIATION_CAMPUS_PLANT_FUND_OBJECT_SUB_TYPES);
            // Initializing the asset payment table.
            depreciationBatchDao.resetPeriodValuesWhenFirstFiscalPeriod(fiscalMonth);
            LOG.info("getBaseAmountOfAssets(Collection<AssetPayment> depreciableAssetsCollection) - Started.");
            // Invoking method that will calculate the base amount for each asset payment transactions, which could be more than 1
            // per asset.
            LOG.info("YEAR END DEPRECIATION - Calculating the base amount for each asset.");
            Map<Long, KualiDecimal> salvageValueAssetDeprAmounts = depreciationBatchDao
                    .getPrimaryDepreciationBaseAmountForSV();
            // Retrieving the object asset codes.
            Map<String, AssetObjectCode> assetObjectCodeMap = buildChartObjectToCapitalizationObjectMap(
                    assetObjectCodes);
            Map<String, ObjectCode> capitalizationObjectCodes = new HashMap<String, ObjectCode>();

            // Reading asset payments
            LOG.info("YEAR END DEPRECIATION - Reading collection with eligible asset payment details.");
            int counter = 0;
            List<AssetPaymentInfo> saveList = new ArrayList<AssetPaymentInfo>();
            for (AssetPaymentInfo assetPaymentInfo : depreciableAssetsCollection) {

                boolean asset_is_retired = false;
                boolean asset_is_not_in_last_year_of_life = false;
                HashMap<String, Object> pKeys = new HashMap<String, Object>();
                // Asset must be valid and capital active 'A','C','S','U'
                Long assetNumber = assetPaymentInfo.getCapitalAssetNumber();
                pKeys.put(CamsPropertyConstants.Asset.CAPITAL_ASSET_NUMBER, assetNumber);

                Asset asset = businessObjectService.findByPrimaryKey(Asset.class, pKeys);
                if (asset != null) {
                    asset_is_retired = assetService.isAssetRetired(asset);
                    if (LOG.isInfoEnabled()) {
                        LOG.info("asset#" + assetNumber + "   asset_is_retired = " + asset_is_retired);
                    }
                }

                AssetObjectCode assetObjectCode = assetObjectCodeMap.get(assetPaymentInfo.getChartOfAccountsCode()
                        + "-" + assetPaymentInfo.getFinancialObjectCode());
                if (assetObjectCode == null) {
                    LOG.error("YEAR END DEPRECIATION - " + "Asset object code not found for " + fiscalYear + "-"
                            + assetPaymentInfo.getChartOfAccountsCode() + "-"
                            + assetPaymentInfo.getFinancialObjectCode());
                    LOG.error("YEAR END DEPRECIATION - " + "Asset payment is not included in depreciation "
                            + assetPaymentInfo.getCapitalAssetNumber() + " - "
                            + assetPaymentInfo.getPaymentSequenceNumber());
                    continue;
                } else {
                    LOG.info("YEAR END DEPRECIATION - " + "fiscal year " + fiscalYear + " chartOfAccountsCode:"
                            + assetPaymentInfo.getChartOfAccountsCode() + " FinancialObjectCode:"
                            + assetPaymentInfo.getFinancialObjectCode());
                    //                LOG.info("YEAR END DEPRECIATION - " + "CapitalAssetNumber:" + assetPaymentInfo.getCapitalAssetNumber() + " PaymentSequenceNumber:" + assetPaymentInfo.getPaymentSequenceNumber());
                }
                ObjectCode accumulatedDepreciationFinancialObject = getDepreciationObjectCode(fiscalYear,
                        capitalizationObjectCodes, assetPaymentInfo,
                        assetObjectCode.getAccumulatedDepreciationFinancialObjectCode());
                ObjectCode depreciationExpenseFinancialObject = getDepreciationObjectCode(fiscalYear,
                        capitalizationObjectCodes, assetPaymentInfo,
                        assetObjectCode.getDepreciationExpenseFinancialObjectCode());
                String retire_code = parameterService.getParameterValueAsString(
                        org.kuali.kfs.module.cam.businessobject.AssetRetirementGlobal.class,
                        CamsConstants.Parameters.DEFAULT_GAIN_LOSS_DISPOSITION_OBJECT_CODE);
                if (LOG.isInfoEnabled()) {
                    LOG.info("retire_code from system parameter "
                            + CamsConstants.Parameters.DEFAULT_GAIN_LOSS_DISPOSITION_OBJECT_CODE + " = "
                            + retire_code);
                }
                ObjectCode depreciationYearEndExpenseFinancialObject = getDepreciationObjectCode(fiscalYear,
                        capitalizationObjectCodes, assetPaymentInfo, retire_code);

                if (ObjectUtils.isNull(accumulatedDepreciationFinancialObject)) {
                    LOG.error("YEAR END DEPRECIATION - "
                            + "Accumulated Depreciation Financial Object Code not found for " + fiscalYear + "-"
                            + assetPaymentInfo.getChartOfAccountsCode() + "-"
                            + assetObjectCode.getAccumulatedDepreciationFinancialObjectCode());
                    LOG.error("YEAR END DEPRECIATION - " + "Asset payment is not included in depreciation "
                            + assetPaymentInfo.getCapitalAssetNumber() + " - "
                            + assetPaymentInfo.getPaymentSequenceNumber());
                    continue;
                } else {
                    //                LOG.info("YEAR END DEPRECIATION - " + " AccumulatedDepreciationFinancialObjectCode:" + assetObjectCode.getAccumulatedDepreciationFinancialObjectCode());
                    //                LOG.info("YEAR END DEPRECIATION - " + "CapitalAssetNumber:" + assetPaymentInfo.getCapitalAssetNumber() + " PaymentSequenceNumber:" + assetPaymentInfo.getPaymentSequenceNumber());
                    if (LOG.isInfoEnabled()) {
                        LOG.info("YEAR END DEPRECIATION - " + "accumulatedDepreciationFinancialObject:"
                                + accumulatedDepreciationFinancialObject.getFinancialObjectCode());
                    }
                }

                if (ObjectUtils.isNull(depreciationExpenseFinancialObject)) {
                    LOG.error(
                            "YEAR END DEPRECIATION - " + "Depreciation Expense Financial Object Code not found for "
                                    + fiscalYear + "-" + assetPaymentInfo.getChartOfAccountsCode() + "-"
                                    + assetObjectCode.getDepreciationExpenseFinancialObjectCode());
                    LOG.error("YEAR END DEPRECIATION - " + "Asset payment is not included in depreciation "
                            + assetPaymentInfo.getCapitalAssetNumber() + " - "
                            + assetPaymentInfo.getPaymentSequenceNumber());
                    continue;
                } else {
                    if (LOG.isInfoEnabled()) {
                        LOG.info("YEAR END DEPRECIATION - " + "depreciationExpenseFinancialObject:"
                                + depreciationExpenseFinancialObject.getFinancialObjectCode());
                    }
                }
                assetDepreciationDate.setTime(assetPaymentInfo.getDepreciationDate());
                accumulatedDepreciationAmount = KualiDecimal.ZERO;
                KualiDecimal deprAmountSum = salvageValueAssetDeprAmounts.get(assetNumber);
                // Calculating the life of the asset in months.
                assetLifeInMonths = assetPaymentInfo.getDepreciableLifeLimit() * 12;
                // Calculating the months elapsed for the asset using the depreciation date and the asset service date.
                monthsElapsed = (depreciationDate.get(Calendar.MONTH) - assetDepreciationDate.get(Calendar.MONTH)
                        + (depreciationDate.get(Calendar.YEAR) - assetDepreciationDate.get(Calendar.YEAR)) * 12)
                        + 1;

                if ((assetLifeInMonths - monthsElapsed) > 12) {
                    asset_is_not_in_last_year_of_life = true;
                }

                // **************************************************************************************************************
                // CALCULATING ACCUMULATED DEPRECIATION BASED ON FORMULA FOR SINGLE LINE AND SALVAGE VALUE DEPRECIATION METHODS.
                // **************************************************************************************************************
                KualiDecimal primaryDepreciationBaseAmount = assetPaymentInfo.getPrimaryDepreciationBaseAmount();
                if (primaryDepreciationBaseAmount == null) {
                    primaryDepreciationBaseAmount = KualiDecimal.ZERO;
                    assetPaymentInfo.setPrimaryDepreciationBaseAmount(KualiDecimal.ZERO);
                }

                if (assetPaymentInfo.getAccumulatedPrimaryDepreciationAmount() == null) {
                    assetPaymentInfo.setAccumulatedPrimaryDepreciationAmount(KualiDecimal.ZERO);
                }

                // If the months elapsed >= to the life of the asset (in months) then, the accumulated depreciation should be:
                if (monthsElapsed >= assetLifeInMonths) {
                    if (CamsConstants.Asset.DEPRECIATION_METHOD_STRAIGHT_LINE_CODE
                            .equals(assetPaymentInfo.getPrimaryDepreciationMethodCode())) {
                        accumulatedDepreciationAmount = primaryDepreciationBaseAmount;
                    } else if (CamsConstants.Asset.DEPRECIATION_METHOD_SALVAGE_VALUE_CODE
                            .equals(assetPaymentInfo.getPrimaryDepreciationMethodCode()) && deprAmountSum != null
                            && deprAmountSum.isNonZero()) {
                        accumulatedDepreciationAmount = primaryDepreciationBaseAmount
                                .subtract((primaryDepreciationBaseAmount.divide(deprAmountSum))
                                        .multiply(assetPaymentInfo.getSalvageAmount()));
                    }
                } // If the month elapse < to the life of the asset (in months) then....
                else {
                    if (CamsConstants.Asset.DEPRECIATION_METHOD_STRAIGHT_LINE_CODE
                            .equals(assetPaymentInfo.getPrimaryDepreciationMethodCode())) {
                        accumulatedDepreciationAmount = new KualiDecimal(
                                (monthsElapsed / assetLifeInMonths) * primaryDepreciationBaseAmount.doubleValue());
                    } else if (CamsConstants.Asset.DEPRECIATION_METHOD_SALVAGE_VALUE_CODE
                            .equals(assetPaymentInfo.getPrimaryDepreciationMethodCode()) && deprAmountSum != null
                            && deprAmountSum.isNonZero()) {
                        accumulatedDepreciationAmount = new KualiDecimal(
                                (monthsElapsed / assetLifeInMonths) * (primaryDepreciationBaseAmount
                                        .subtract((primaryDepreciationBaseAmount.divide(deprAmountSum))
                                                .multiply(assetPaymentInfo.getSalvageAmount()))).doubleValue());
                    }
                }
                // Calculating in process fiscal month depreciation amount
                KualiDecimal transactionAmount = accumulatedDepreciationAmount
                        .subtract(assetPaymentInfo.getAccumulatedPrimaryDepreciationAmount());

                Map<String, String> primaryKeys = new HashMap<String, String>();
                primaryKeys.put(CamsPropertyConstants.AssetDepreciationConvention.FINANCIAL_OBJECT_SUB_TYPE_CODE,
                        asset.getFinancialObjectSubTypeCode());
                AssetDepreciationConvention depreciationConvention = SpringContext
                        .getBean(BusinessObjectService.class)
                        .findByPrimaryKey(AssetDepreciationConvention.class, primaryKeys);
                String conventionCode = depreciationConvention.getDepreciationConventionCode();
                if (CamsConstants.DepreciationConvention.HALF_YEAR.equalsIgnoreCase(conventionCode)) {
                    if (asset_is_retired && asset_is_not_in_last_year_of_life) { // and not in last year of life mjmc
                        transactionAmount = transactionAmount.divide(new KualiDecimal(2));
                        if (LOG.isInfoEnabled()) {
                            LOG.info("transactionAmount after being halved = " + transactionAmount);
                        }
                    }
                }

                String transactionType = KFSConstants.GL_DEBIT_CODE;
                if (transactionAmount.isNegative()) {
                    transactionType = KFSConstants.GL_CREDIT_CODE;
                }
                String plantAccount = "";
                String plantCOA = "";

                // getting the right Plant Fund Chart code & Plant Fund Account
                if (organizationPlantFundObjectSubType.contains(assetPaymentInfo.getFinancialObjectSubTypeCode())) {
                    plantAccount = assetPaymentInfo.getOrganizationPlantAccountNumber();
                    plantCOA = assetPaymentInfo.getOrganizationPlantChartCode();
                } else if (campusPlantFundObjectSubType
                        .contains(assetPaymentInfo.getFinancialObjectSubTypeCode())) {
                    plantAccount = assetPaymentInfo.getCampusPlantAccountNumber();
                    plantCOA = assetPaymentInfo.getCampusPlantChartCode();
                }
                if (StringUtils.isBlank(plantCOA) || StringUtils.isBlank(plantAccount)) {
                    // skip the payment
                    LOG.error("YEAR END DEPRECIATION - " + "Plant COA is " + plantCOA + " and plant account is "
                            + plantAccount + " for Financial Object SubType Code = "
                            + assetPaymentInfo.getFinancialObjectSubTypeCode()
                            + " so Asset payment is not included in depreciation "
                            + assetPaymentInfo.getCapitalAssetNumber() + " - "
                            + assetPaymentInfo.getPaymentSequenceNumber());
                    continue;
                }
                SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
                if (LOG.isInfoEnabled()) {
                    LOG.info("Asset#: " + assetNumber + " - Payment sequence#:"
                            + assetPaymentInfo.getPaymentSequenceNumber() + " - Asset Depreciation date:"
                            + sdf.format(assetDepreciationDate.getTime()) + " - Life:" + assetLifeInMonths
                            + " - Depreciation base amt:" + primaryDepreciationBaseAmount);
                    LOG.info("Accumulated depreciation:"
                            + assetPaymentInfo.getAccumulatedPrimaryDepreciationAmount() + " - Month Elapsed:"
                            + monthsElapsed + " - Calculated accum depreciation:" + accumulatedDepreciationAmount
                            + " - Depreciation amount:" + transactionAmount.toString() + " - Depreciation Method:"
                            + assetPaymentInfo.getPrimaryDepreciationMethodCode());
                }
                if (asset_is_retired && asset_is_not_in_last_year_of_life) {
                    assetPaymentInfo.setAccumulatedPrimaryDepreciationAmount(
                            accumulatedDepreciationAmount.subtract(transactionAmount));
                } else {
                    assetPaymentInfo.setAccumulatedPrimaryDepreciationAmount(accumulatedDepreciationAmount);
                }
                assetPaymentInfo.setTransactionAmount(transactionAmount);
                counter++;
                saveList.add(assetPaymentInfo);
                // Saving depreciation amount in the asset payment table
                if (counter % 1000 == 0) {
                    getDepreciationBatchDao().updateAssetPayments(saveList, fiscalMonth);
                    saveList.clear();
                }
                // if the asset has a depreciation amount <> 0 then, create its debit and credit entries.
                if (transactionAmount.isNonZero()) {
                    this.populateYearEndDepreciationTransaction(assetPaymentInfo, transactionType, plantCOA,
                            plantAccount, depreciationExpenseFinancialObject, depreciationTransactionSummary);
                    transactionType = (transactionType.equals(KFSConstants.GL_DEBIT_CODE)
                            ? KFSConstants.GL_CREDIT_CODE
                            : KFSConstants.GL_DEBIT_CODE);
                    this.populateYearEndDepreciationTransaction(assetPaymentInfo, transactionType, plantCOA,
                            plantAccount, accumulatedDepreciationFinancialObject, depreciationTransactionSummary);

                    if (asset_is_retired) {
                        this.populateYearEndDepreciationTransaction(assetPaymentInfo, transactionType, plantCOA,
                                plantAccount, depreciationYearEndExpenseFinancialObject,
                                depreciationTransactionSummary);
                        transactionType = (transactionType.equals(KFSConstants.GL_DEBIT_CODE)
                                ? KFSConstants.GL_CREDIT_CODE
                                : KFSConstants.GL_DEBIT_CODE);
                        this.populateYearEndDepreciationTransaction(assetPaymentInfo, transactionType, plantCOA,
                                plantAccount, accumulatedDepreciationFinancialObject,
                                depreciationTransactionSummary);
                    }
                }
            }
            getDepreciationBatchDao().updateAssetPayments(saveList, fiscalMonth);
            saveList.clear();
            return depreciationTransactionSummary;
        } catch (Exception e) {
            LOG.error("Error occurred", e);
            throw new IllegalStateException(kualiConfigurationService.getPropertyValueAsString(
                    CamsKeyConstants.Depreciation.ERROR_WHEN_CALCULATING_DEPRECIATION) + " :" + e.getMessage(), e);
        }
    }

    protected void processYearEndGeneralLedgerPendingEntry(Integer fiscalYear, List<String> documentNos,
            SortedMap<String, AssetDepreciationTransaction> trans) {
        Integer fiscalMonth = new Integer(13);
        processGeneralLedgerPendingEntry(fiscalYear, fiscalMonth, documentNos, trans);
    }

    /**
     * Depreciation (end of year) Period 13 assets incorrect depreciation start date Update asset created in period 13 with in
     * service date and depreciate date if batch runs in the last fiscal period
     *
     * @param fiscalMonth2
     * @param fiscalYear2
     */
    protected void updateAssetsDatesForLastFiscalPeriod(Integer fiscalMonth, Integer fiscalYear) {
        if (fiscalMonth == 12) {
            LOG.info(CamsConstants.Depreciation.DEPRECIATION_BATCH
                    + "Starting updateAssetsCreatedInLastFiscalPeriod()");
            // Getting last date of fiscal year
            Date lastDateOfFiscalYear = universityDateService.getLastDateOfFiscalYear(fiscalYear);
            if (lastDateOfFiscalYear == null) {
                throw new IllegalStateException(kualiConfigurationService
                        .getPropertyValueAsString(KFSKeyConstants.ERROR_UNIV_DATE_NOT_FOUND));
            }
            final java.sql.Date lastFiscalYearDate = new java.sql.Date(lastDateOfFiscalYear.getTime());

            List<String> movableEquipmentObjectSubTypes = new ArrayList<String>();
            if (parameterService.parameterExists(Asset.class,
                    CamsConstants.Parameters.MOVABLE_EQUIPMENT_OBJECT_SUB_TYPES)) {
                movableEquipmentObjectSubTypes.addAll(parameterService.getParameterValuesAsString(Asset.class,
                        CamsConstants.Parameters.MOVABLE_EQUIPMENT_OBJECT_SUB_TYPES));
            }

            // Only update assets with a object sub type code equals to any MOVABLE_EQUIPMENT_OBJECT_SUB_TYPES.
            if (!movableEquipmentObjectSubTypes.isEmpty()) {
                updateAssetDatesPerConvention(lastFiscalYearDate, movableEquipmentObjectSubTypes,
                        CamsConstants.DepreciationConvention.CREATE_DATE);
                updateAssetDatesPerConvention(lastFiscalYearDate, movableEquipmentObjectSubTypes,
                        CamsConstants.DepreciationConvention.FULL_YEAR);
                updateAssetDatesPerConvention(lastFiscalYearDate, movableEquipmentObjectSubTypes,
                        CamsConstants.DepreciationConvention.HALF_YEAR);
            }
            LOG.info(CamsConstants.Depreciation.DEPRECIATION_BATCH
                    + "Finished updateAssetsCreatedInLastFiscalPeriod()");
        }
    }

    /**
     * Depreciation (end of year) Period 13 assets incorrect depreciation start date
     * <P>
     * Update assets created in period 13 with its in-service date and depreciation date per depreciation convention.
     *
     * @param lastFiscalYearDate
     * @param movableEquipmentObjectSubTypes
     * @param depreciationConventionCd
     */
    protected void updateAssetDatesPerConvention(final java.sql.Date lastFiscalYearDate,
            List<String> movableEquipmentObjectSubTypes, String depreciationConventionCd) {
        List<Map<String, Object>> selectedAssets = getDepreciationBatchDao().getAssetsByDepreciationConvention(
                lastFiscalYearDate, movableEquipmentObjectSubTypes, depreciationConventionCd);

        if (selectedAssets != null && !selectedAssets.isEmpty()) {
            List<String> assetNumbers = new ArrayList<String>();
            for (Map<String, Object> assetMap : selectedAssets) {
                assetNumbers.add(((BigDecimal) assetMap.get("CPTLAST_NBR")).toString());
            }
            // calculate asset deprecation date per depreciation convention
            java.sql.Date depreciationDate = getAssetDateService()
                    .computeDepreciationDateForPeriod13(depreciationConventionCd, lastFiscalYearDate);
            getDepreciationBatchDao().updateAssetInServiceAndDepreciationDate(assetNumbers, lastFiscalYearDate,
                    depreciationDate);
            LOG.info(CamsConstants.Depreciation.DEPRECIATION_BATCH
                    + "Finished updateAssetInServiceAndDepreciationDate() for Depreciation convention "
                    + depreciationConventionCd + " for " + assetNumbers.size() + " assets : "
                    + assetNumbers.toString());
        }
    }

    public void setParameterService(ParameterService parameterService) {
        this.parameterService = parameterService;
    }

    public void setDepreciableAssetsDao(DepreciableAssetsDao depreciableAssetsDao) {
        this.depreciableAssetsDao = depreciableAssetsDao;
    }

    public void setCamsReportService(ReportService reportService) {
        this.reportService = reportService;
    }

    public void setConfigurationService(ConfigurationService kcs) {
        kualiConfigurationService = kcs;
    }

    public void setGeneralLedgerPendingEntryService(
            GeneralLedgerPendingEntryService generalLedgerPendingEntryService) {
        this.generalLedgerPendingEntryService = generalLedgerPendingEntryService;
    }

    public void setDateTimeService(DateTimeService dateTimeService) {
        this.dateTimeService = dateTimeService;
    }

    public void setBusinessObjectService(BusinessObjectService businessObjectService) {
        this.businessObjectService = businessObjectService;
    }

    public DataDictionaryService getDataDictionaryService() {
        return dataDictionaryService;
    }

    public void setDataDictionaryService(DataDictionaryService dataDictionaryService) {
        this.dataDictionaryService = dataDictionaryService;
    }

    /**
     * Gets the depreciationBatchDao attribute.
     *
     * @return Returns the depreciationBatchDao.
     */
    public DepreciationBatchDao getDepreciationBatchDao() {
        return depreciationBatchDao;
    }

    /**
     * Sets the depreciationBatchDao attribute value.
     *
     * @param depreciationBatchDao The depreciationBatchDao to set.
     */
    @Override
    public void setDepreciationBatchDao(DepreciationBatchDao depreciationBatchDao) {
        this.depreciationBatchDao = depreciationBatchDao;
    }

    public void setCronExpression(String cronExpression) {
        this.cronExpression = cronExpression;
    }

    public void setMailService(MailService mailService) {
        this.mailService = mailService;
    }

    public WorkflowDocumentService getWorkflowDocumentService() {
        if (workflowDocumentService == null) {
            workflowDocumentService = KRADServiceLocatorWeb.getWorkflowDocumentService();
        }
        return workflowDocumentService;
    }

    public void setOptionsService(OptionsService optionsService) {
        this.optionsService = optionsService;
    }

    public AssetDateService getAssetDateService() {
        return assetDateService;
    }

    public void setAssetDateService(AssetDateService assetDateService) {
        this.assetDateService = assetDateService;
    }

    public void setUniversityDateService(UniversityDateService universityDateService) {
        this.universityDateService = universityDateService;
    }

    public SchedulerService getSchedulerService() {
        return schedulerService;
    }

    public void setSchedulerService(SchedulerService schedulerService) {
        this.schedulerService = schedulerService;
    }
}