001    /*
002     * Copyright 2011 The Kuali Foundation.
003     * 
004     * Licensed under the Educational Community License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     * 
008     * http://www.opensource.org/licenses/ecl2.php
009     * 
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    package org.kuali.kfs.module.cam.batch.service.impl;
017    
018    import static org.kuali.kfs.sys.KFSConstants.BALANCE_TYPE_ACTUAL;
019    
020    import java.sql.Timestamp;
021    import java.text.DateFormat;
022    import java.text.ParseException;
023    import java.text.SimpleDateFormat;
024    import java.util.ArrayList;
025    import java.util.Calendar;
026    import java.util.Collection;
027    import java.util.HashMap;
028    import java.util.List;
029    import java.util.Map;
030    import java.util.SortedMap;
031    import java.util.TreeMap;
032    
033    import org.apache.commons.lang.StringUtils;
034    import org.kuali.kfs.coa.businessobject.ObjectCode;
035    import org.kuali.kfs.coa.service.ObjectCodeService;
036    import org.kuali.kfs.module.cam.CamsConstants;
037    import org.kuali.kfs.module.cam.CamsKeyConstants;
038    import org.kuali.kfs.module.cam.batch.AssetPaymentInfo;
039    import org.kuali.kfs.module.cam.batch.service.AssetDepreciationService;
040    import org.kuali.kfs.module.cam.batch.service.ReportService;
041    import org.kuali.kfs.module.cam.businessobject.AssetDepreciationTransaction;
042    import org.kuali.kfs.module.cam.businessobject.AssetObjectCode;
043    import org.kuali.kfs.module.cam.document.AssetDepreciationDocument;
044    import org.kuali.kfs.module.cam.document.dataaccess.DepreciableAssetsDao;
045    import org.kuali.kfs.module.cam.document.dataaccess.DepreciationBatchDao;
046    import org.kuali.kfs.sys.KFSConstants;
047    import org.kuali.kfs.sys.KFSKeyConstants;
048    import org.kuali.kfs.sys.businessobject.FinancialSystemDocumentHeader;
049    import org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntry;
050    import org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntrySequenceHelper;
051    import org.kuali.kfs.sys.businessobject.UniversityDate;
052    import org.kuali.kfs.sys.context.SpringContext;
053    import org.kuali.kfs.sys.dataaccess.UniversityDateDao;
054    import org.kuali.kfs.sys.service.GeneralLedgerPendingEntryService;
055    import org.kuali.kfs.sys.service.impl.KfsParameterConstants;
056    import org.kuali.rice.kew.exception.WorkflowException;
057    import org.kuali.rice.kns.service.BusinessObjectService;
058    import org.kuali.rice.kns.service.DataDictionaryService;
059    import org.kuali.rice.kns.service.DateTimeService;
060    import org.kuali.rice.kns.service.KualiConfigurationService;
061    import org.kuali.rice.kns.service.ParameterService;
062    import org.kuali.rice.kns.util.GlobalVariables;
063    import org.kuali.rice.kns.util.KualiDecimal;
064    import org.kuali.rice.kns.util.ObjectUtils;
065    import org.kuali.rice.kns.workflow.service.KualiWorkflowDocument;
066    import org.kuali.rice.kns.workflow.service.WorkflowDocumentService;
067    import org.springframework.transaction.annotation.Transactional;
068    
069    /**
070     * This class is a service that calculates the depreciation amount for each asset that has a eligible asset payment.
071     * <p>
072     * When an error occurs running this process, a pdf file will be created with the error message. However, this doesn't mean that
073     * this process automatically leaves all the records as they were right before running the program. If the process fails, is
074     * suggested to do the following before trying to run the process again: a.)Delete gl pending entry depreciation entries: DELETE
075     * FROM GL_PENDING_ENTRY_T WHERE FDOC_TYP_CD = 'DEPR' b.)Substract from the accumulated depreciation amount the depreciation
076     * calculated for the fiscal month that was ran, and then reset the depreciation amount field for the fiscal month that was ran. ex:
077     * Assuming that the fiscal month = 8 then: UPDATE CM_AST_PAYMENT_T SET AST_ACUM_DEPR1_AMT = AST_ACUM_DEPR1_AMT -
078     * AST_PRD8_DEPR1_AMT, AST_PRD8_DEPR1_AMT=0
079     */
080    @Transactional
081    public class AssetDepreciationServiceImpl implements AssetDepreciationService {
082        private static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(AssetDepreciationServiceImpl.class);
083        private ParameterService parameterService;
084        private ReportService reportService;
085        private DateTimeService dateTimeService;
086        private DepreciableAssetsDao depreciableAssetsDao;
087        private KualiConfigurationService kualiConfigurationService;
088        private GeneralLedgerPendingEntryService generalLedgerPendingEntryService;
089        private BusinessObjectService businessObjectService;
090        private UniversityDateDao universityDateDao;
091        private WorkflowDocumentService workflowDocumentService;
092        private DataDictionaryService dataDictionaryService;
093        private Integer fiscalYear;
094        private Integer fiscalMonth;
095        private String errorMsg = "";
096        private List<String> documentNos = new ArrayList<String>();
097        private DepreciationBatchDao depreciationBatchDao;
098    
099        /**
100         * @see org.kuali.kfs.module.cam.batch.service.AssetDepreciationService#runDepreciation()
101         */
102        public void runDepreciation() {
103            List<String[]> reportLog = new ArrayList<String[]>();
104            boolean hasErrors = false;
105            Calendar depreciationDate = Calendar.getInstance();
106            Calendar currentDate = Calendar.getInstance();
107            String depreciationDateParameter = null;
108            DateFormat dateFormat = new SimpleDateFormat(CamsConstants.DateFormats.YEAR_MONTH_DAY);
109    
110            try {
111                LOG.info("*******" + CamsConstants.Depreciation.DEPRECIATION_BATCH + " HAS BEGUN *******");
112    
113                /*
114                 * Getting the system parameter "DEPRECIATION_DATE" When this parameter is used to determine which fiscal month and year
115                 * is going to be depreciated If blank then the system will take the system date to determine the fiscal period
116                 */
117                if (parameterService.parameterExists(KfsParameterConstants.CAPITAL_ASSETS_BATCH.class, CamsConstants.Parameters.DEPRECIATION_RUN_DATE_PARAMETER)) {
118                    depreciationDateParameter = ((List<String>) parameterService.getParameterValues(KfsParameterConstants.CAPITAL_ASSETS_BATCH.class, CamsConstants.Parameters.DEPRECIATION_RUN_DATE_PARAMETER)).get(0).trim();
119                }
120                else {
121                    throw new IllegalStateException(kualiConfigurationService.getPropertyString(CamsKeyConstants.Depreciation.DEPRECIATION_DATE_PARAMETER_NOT_FOUND));
122                }
123                // This validates the system parameter depreciation_date has a valid format of YYYY-MM-DD.
124                if (depreciationDateParameter != null && !depreciationDateParameter.trim().equals("")) {
125                    try {
126                        depreciationDate.setTime(dateFormat.parse(depreciationDateParameter));
127                    }
128                    catch (ParseException e) {
129                        throw new IllegalArgumentException(kualiConfigurationService.getPropertyString(CamsKeyConstants.Depreciation.INVALID_DEPRECIATION_DATE_FORMAT));
130                    }
131                }
132                LOG.info(CamsConstants.Depreciation.DEPRECIATION_BATCH + "Depreciation run date: " + depreciationDateParameter);
133    
134                UniversityDate universityDate = (UniversityDate) businessObjectService.findBySinglePrimaryKey(UniversityDate.class, new java.sql.Date(depreciationDate.getTimeInMillis()));
135                if (universityDate == null) {
136                    throw new IllegalStateException(kualiConfigurationService.getPropertyString(KFSKeyConstants.ERROR_UNIV_DATE_NOT_FOUND));
137                }
138    
139                this.fiscalYear = universityDate.getUniversityFiscalYear();
140                this.fiscalMonth = new Integer(universityDate.getUniversityFiscalAccountingPeriod());
141                // If the depreciation date is not = to the system date then, the depreciation process cannot run.
142                LOG.info(CamsConstants.Depreciation.DEPRECIATION_BATCH + "Fiscal Year = " + this.fiscalYear + " & Fiscal Period=" + this.fiscalMonth);
143                reportLog.addAll(depreciableAssetsDao.generateStatistics(true, null, fiscalYear, fiscalMonth, depreciationDate));
144                // update if fiscal period is 12
145                depreciationBatchDao.updateAssetsCreatedInLastFiscalPeriod(fiscalMonth, fiscalYear);
146                // Retrieving eligible asset payment details
147                LOG.info(CamsConstants.Depreciation.DEPRECIATION_BATCH + "Getting list of asset payments eligible for depreciation.");
148                Collection<AssetPaymentInfo> depreciableAssetsCollection = depreciationBatchDao.getListOfDepreciableAssetPaymentInfo(this.fiscalYear, this.fiscalMonth, depreciationDate);
149                // if we have assets eligible for depreciation then, calculate depreciation and create glpe's transactions
150                if (depreciableAssetsCollection != null && !depreciableAssetsCollection.isEmpty()) {
151                    SortedMap<String, AssetDepreciationTransaction> depreciationTransactions = this.calculateDepreciation(depreciableAssetsCollection, depreciationDate);
152                    processGeneralLedgerPendingEntry(depreciationTransactions);
153                }
154                else {
155                    throw new IllegalStateException(kualiConfigurationService.getPropertyString(CamsKeyConstants.Depreciation.NO_ELIGIBLE_FOR_DEPRECIATION_ASSETS_FOUND));
156                }
157            }
158            catch (Exception e) {
159                LOG.info("Error occurred");
160                LOG.info(CamsConstants.Depreciation.DEPRECIATION_BATCH + "**************************************************************************");
161                LOG.info(CamsConstants.Depreciation.DEPRECIATION_BATCH + "AN ERROR HAS OCCURRED! - ERROR: " + e.getMessage());
162                LOG.info(CamsConstants.Depreciation.DEPRECIATION_BATCH + "**************************************************************************");
163                hasErrors = true;
164                this.errorMsg = "Depreciation process ran unsucessfuly.\nReason:" + e.getMessage();
165            }
166            finally {
167                if (!hasErrors) {
168                    reportLog.addAll(depreciableAssetsDao.generateStatistics(false, this.documentNos, fiscalYear, fiscalMonth, depreciationDate));
169                }
170                // the report will be generated only when there is an error or when the log has something.
171                if (!reportLog.isEmpty() || !errorMsg.trim().equals(""))
172                    reportService.generateDepreciationReport(reportLog, errorMsg, depreciationDateParameter);
173    
174                LOG.info("*******" + CamsConstants.Depreciation.DEPRECIATION_BATCH + " HAS ENDED *******");
175            }
176        }
177    
178        /**
179         * This method calculates the depreciation of each asset payment, creates the depreciation transactions that will be stored in
180         * the general ledger pending entry table
181         * 
182         * @param depreciableAssetsCollection asset payments eligible for depreciation
183         * @return SortedMap with a list of depreciation transactions
184         */
185        protected SortedMap<String, AssetDepreciationTransaction> calculateDepreciation(Collection<AssetPaymentInfo> depreciableAssetsCollection, Calendar depreciationDate) {
186            LOG.debug("calculateDepreciation() - start");
187    
188            List<String> organizationPlantFundObjectSubType = new ArrayList<String>();
189            List<String> campusPlantFundObjectSubType = new ArrayList<String>();
190            SortedMap<String, AssetDepreciationTransaction> depreciationTransactionSummary = new TreeMap<String, AssetDepreciationTransaction>();
191            double monthsElapsed = 0d;
192            double assetLifeInMonths = 0d;
193            KualiDecimal accumulatedDepreciationAmount = KualiDecimal.ZERO;
194            Calendar assetDepreciationDate = Calendar.getInstance();
195    
196            try {
197                LOG.info(CamsConstants.Depreciation.DEPRECIATION_BATCH + "Getting the parameters for the plant fund object sub types.");
198                // Getting system parameters needed.
199                if (parameterService.parameterExists(KfsParameterConstants.CAPITAL_ASSETS_BATCH.class, CamsConstants.Parameters.DEPRECIATION_ORGANIZATON_PLANT_FUND_SUB_OBJECT_TYPES)) {
200                    organizationPlantFundObjectSubType = parameterService.getParameterValues(KfsParameterConstants.CAPITAL_ASSETS_BATCH.class, CamsConstants.Parameters.DEPRECIATION_ORGANIZATON_PLANT_FUND_SUB_OBJECT_TYPES);
201                }
202                if (parameterService.parameterExists(KfsParameterConstants.CAPITAL_ASSETS_BATCH.class, CamsConstants.Parameters.DEPRECIATION_CAMPUS_PLANT_FUND_OBJECT_SUB_TYPES)) {
203                    campusPlantFundObjectSubType = parameterService.getParameterValues(KfsParameterConstants.CAPITAL_ASSETS_BATCH.class, CamsConstants.Parameters.DEPRECIATION_CAMPUS_PLANT_FUND_OBJECT_SUB_TYPES);
204                }
205                // Initializing the asset payment table.
206                depreciationBatchDao.resetPeriodValuesWhenFirstFiscalPeriod(fiscalMonth);
207                LOG.debug("getBaseAmountOfAssets(Collection<AssetPayment> depreciableAssetsCollection) - Started.");
208                // Invoking method that will calculate the base amount for each asset payment transactions, which could be more than 1
209                // per asset.
210                LOG.info(CamsConstants.Depreciation.DEPRECIATION_BATCH + "Calculating the base amount for each asset.");
211                Map<Long, KualiDecimal> salvageValueAssetDeprAmounts = depreciationBatchDao.getPrimaryDepreciationBaseAmountForSV();
212                // Retrieving the object asset codes.
213                Map<String, AssetObjectCode> assetObjectCodeMap = buildChartObjectToCapitalizationObjectMap();
214                Map<String, ObjectCode> capitalizationObjectCodes = new HashMap<String, ObjectCode>();
215    
216                // Reading asset payments
217                LOG.info(CamsConstants.Depreciation.DEPRECIATION_BATCH + "Reading collection with eligible asset payment details.");
218                int counter = 0;
219                List<AssetPaymentInfo> saveList = new ArrayList<AssetPaymentInfo>();
220                for (AssetPaymentInfo assetPaymentInfo : depreciableAssetsCollection) {
221                    AssetObjectCode assetObjectCode = assetObjectCodeMap.get(assetPaymentInfo.getChartOfAccountsCode() + "-" + assetPaymentInfo.getFinancialObjectCode());
222                    if (assetObjectCode == null) {
223                        LOG.error(CamsConstants.Depreciation.DEPRECIATION_BATCH + "Asset object code not found for " + fiscalYear + "-" + assetPaymentInfo.getChartOfAccountsCode() + "-" + assetPaymentInfo.getFinancialObjectCode());
224                        LOG.error(CamsConstants.Depreciation.DEPRECIATION_BATCH + "Asset payment is not included in depreciation " + assetPaymentInfo.getCapitalAssetNumber() + " - " + assetPaymentInfo.getPaymentSequenceNumber());
225                        continue;
226                    }
227                    ObjectCode accumulatedDepreciationFinancialObject = getDepreciationObjectCode(capitalizationObjectCodes, assetPaymentInfo, assetObjectCode.getAccumulatedDepreciationFinancialObjectCode());
228                    ObjectCode depreciationExpenseFinancialObject = getDepreciationObjectCode(capitalizationObjectCodes, assetPaymentInfo, assetObjectCode.getDepreciationExpenseFinancialObjectCode());
229    
230                    if (ObjectUtils.isNull(accumulatedDepreciationFinancialObject)) {
231                        LOG.error(CamsConstants.Depreciation.DEPRECIATION_BATCH + "Accumulated Depreciation Financial Object Code not found for " + fiscalYear + "-" + assetPaymentInfo.getChartOfAccountsCode() + "-" + assetObjectCode.getAccumulatedDepreciationFinancialObjectCode());
232                        LOG.error(CamsConstants.Depreciation.DEPRECIATION_BATCH + "Asset payment is not included in depreciation " + assetPaymentInfo.getCapitalAssetNumber() + " - " + assetPaymentInfo.getPaymentSequenceNumber());
233                        continue;
234                    }
235    
236                    if (ObjectUtils.isNull(depreciationExpenseFinancialObject)) {
237                        LOG.error(CamsConstants.Depreciation.DEPRECIATION_BATCH + "Depreciation Expense Financial Object Code not found for " + fiscalYear + "-" + assetPaymentInfo.getChartOfAccountsCode() + "-" + assetObjectCode.getDepreciationExpenseFinancialObjectCode());
238                        LOG.error(CamsConstants.Depreciation.DEPRECIATION_BATCH + "Asset payment is not included in depreciation " + assetPaymentInfo.getCapitalAssetNumber() + " - " + assetPaymentInfo.getPaymentSequenceNumber());
239                        continue;
240                    }
241                    Long assetNumber = assetPaymentInfo.getCapitalAssetNumber();
242                    assetDepreciationDate.setTime(assetPaymentInfo.getDepreciationDate());
243                    accumulatedDepreciationAmount = KualiDecimal.ZERO;
244                    KualiDecimal deprAmountSum = salvageValueAssetDeprAmounts.get(assetNumber);
245                    // Calculating the life of the asset in months.
246                    assetLifeInMonths = assetPaymentInfo.getDepreciableLifeLimit() * 12;
247                    // Calculating the months elapsed for the asset using the depreciation date and the asset service date.
248                    monthsElapsed = (depreciationDate.get(Calendar.MONTH) - assetDepreciationDate.get(Calendar.MONTH) + (depreciationDate.get(Calendar.YEAR) - assetDepreciationDate.get(Calendar.YEAR)) * 12) + 1;
249    
250                    // **************************************************************************************************************
251                    // CALCULATING ACCUMULATED DEPRECIATION BASED ON FORMULA FOR SINGLE LINE AND SALVAGE VALUE DEPRECIATION METHODS.
252                    // **************************************************************************************************************
253                    KualiDecimal primaryDepreciationBaseAmount = assetPaymentInfo.getPrimaryDepreciationBaseAmount();
254                    if (primaryDepreciationBaseAmount == null)
255                        assetPaymentInfo.setPrimaryDepreciationBaseAmount(KualiDecimal.ZERO);
256    
257                    if (assetPaymentInfo.getAccumulatedPrimaryDepreciationAmount() == null)
258                        assetPaymentInfo.setAccumulatedPrimaryDepreciationAmount(KualiDecimal.ZERO);
259    
260                    // If the months elapsed >= to the life of the asset (in months) then, the accumulated depreciation should be:
261                    if (monthsElapsed >= assetLifeInMonths) {
262                        if (CamsConstants.Asset.DEPRECIATION_METHOD_STRAIGHT_LINE_CODE.equals(assetPaymentInfo.getPrimaryDepreciationMethodCode())) {
263                            accumulatedDepreciationAmount = primaryDepreciationBaseAmount;
264                        }
265                        else if (CamsConstants.Asset.DEPRECIATION_METHOD_SALVAGE_VALUE_CODE.equals(assetPaymentInfo.getPrimaryDepreciationMethodCode()) && deprAmountSum != null && deprAmountSum.isNonZero()) {
266                            accumulatedDepreciationAmount = primaryDepreciationBaseAmount.subtract((primaryDepreciationBaseAmount.divide(deprAmountSum)).multiply(assetPaymentInfo.getSalvageAmount()));
267                        }
268                    } // If the month elapse < to the life of the asset (in months) then....
269                    else {
270                        if (CamsConstants.Asset.DEPRECIATION_METHOD_STRAIGHT_LINE_CODE.equals(assetPaymentInfo.getPrimaryDepreciationMethodCode())) {
271                            accumulatedDepreciationAmount = new KualiDecimal((monthsElapsed / assetLifeInMonths) * primaryDepreciationBaseAmount.doubleValue());
272                        }
273                        else if (CamsConstants.Asset.DEPRECIATION_METHOD_SALVAGE_VALUE_CODE.equals(assetPaymentInfo.getPrimaryDepreciationMethodCode()) && deprAmountSum != null && deprAmountSum.isNonZero()) {
274                            accumulatedDepreciationAmount = new KualiDecimal((monthsElapsed / assetLifeInMonths) * (primaryDepreciationBaseAmount.subtract((primaryDepreciationBaseAmount.divide(deprAmountSum)).multiply(assetPaymentInfo.getSalvageAmount()))).doubleValue());
275                        }
276                    }
277                    // Calculating in process fiscal month depreciation amount
278                    KualiDecimal transactionAmount = accumulatedDepreciationAmount.subtract(assetPaymentInfo.getAccumulatedPrimaryDepreciationAmount());
279    
280                    String transactionType = KFSConstants.GL_DEBIT_CODE;
281                    if (transactionAmount.isNegative()) {
282                        transactionType = KFSConstants.GL_CREDIT_CODE;
283                    }
284                    String plantAccount = "";
285                    String plantCOA = "";
286    
287                    // getting the right Plant Fund Chart code & Plant Fund Account
288                    if (organizationPlantFundObjectSubType.contains(assetPaymentInfo.getFinancialObjectSubTypeCode())) {
289                        plantAccount = assetPaymentInfo.getOrganizationPlantAccountNumber();
290                        plantCOA = assetPaymentInfo.getOrganizationPlantChartCode();
291                    }
292                    else if (campusPlantFundObjectSubType.contains(assetPaymentInfo.getFinancialObjectSubTypeCode())) {
293                        plantAccount = assetPaymentInfo.getCampusPlantAccountNumber();
294                        plantCOA = assetPaymentInfo.getCampusPlantChartCode();
295                    }
296                    if (StringUtils.isBlank(plantCOA) || StringUtils.isBlank(plantAccount)) {
297                        // skip the payment
298                        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());
299                        continue;
300                    }
301                    LOG.info("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());
302                    assetPaymentInfo.setAccumulatedPrimaryDepreciationAmount(accumulatedDepreciationAmount);
303                    assetPaymentInfo.setTransactionAmount(transactionAmount);
304                    counter++;
305                    saveList.add(assetPaymentInfo);
306                    // Saving depreciation amount in the asset payment table
307                    if (counter % 1000 == 0) {
308                        getDepreciationBatchDao().updateAssetPayments(saveList, fiscalMonth);
309                        saveList.clear();
310                    }
311                    // if the asset has a depreciation amount <> 0 then, create its debit and credit entries.
312                    if (transactionAmount.isNonZero()) {
313                        this.populateDepreciationTransaction(assetPaymentInfo, transactionType, plantCOA, plantAccount, depreciationExpenseFinancialObject, depreciationTransactionSummary);
314                        transactionType = (transactionType.equals(KFSConstants.GL_DEBIT_CODE) ? KFSConstants.GL_CREDIT_CODE : KFSConstants.GL_DEBIT_CODE);
315                        this.populateDepreciationTransaction(assetPaymentInfo, transactionType, plantCOA, plantAccount, accumulatedDepreciationFinancialObject, depreciationTransactionSummary);
316                    }
317                }
318                getDepreciationBatchDao().updateAssetPayments(saveList, fiscalMonth);
319                saveList.clear();
320                return depreciationTransactionSummary;
321            }
322            catch (Exception e) {
323                LOG.error("Error occurred", e);
324                throw new IllegalStateException(kualiConfigurationService.getPropertyString(CamsKeyConstants.Depreciation.ERROR_WHEN_CALCULATING_DEPRECIATION) + " :" + e.getMessage());
325            }
326        }
327    
328    
329        /**
330         * This method stores in a collection of business objects the depreciation transaction that later on will be passed to the
331         * processGeneralLedgerPendingEntry method in order to store the records in gl pending entry table
332         * 
333         * @param assetPayment asset payment
334         * @param transactionType which can be [C]redit or [D]ebit
335         * @param plantCOA plant fund char of account code
336         * @param plantAccount plant fund char of account code
337         * @param financialObject char of account object code linked to the payment
338         * @param depreciationTransactionSummary
339         * @return none
340         */
341        protected void populateDepreciationTransaction(AssetPaymentInfo assetPayment, String transactionType, String plantCOA, String plantAccount, ObjectCode deprObjectCode, SortedMap<String, AssetDepreciationTransaction> depreciationTransactionSummary) {
342            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");
343            LOG.info(CamsConstants.Depreciation.DEPRECIATION_BATCH + "populateDepreciationTransaction(): populating AssetDepreciationTransaction pojo - Asset#:" + assetPayment.getCapitalAssetNumber());
344            AssetDepreciationTransaction depreciationTransaction = new AssetDepreciationTransaction();
345            depreciationTransaction.setCapitalAssetNumber(assetPayment.getCapitalAssetNumber());
346            depreciationTransaction.setChartOfAccountsCode(plantCOA);
347            depreciationTransaction.setAccountNumber(plantAccount);
348            depreciationTransaction.setSubAccountNumber(assetPayment.getSubAccountNumber());
349            depreciationTransaction.setFinancialObjectCode(deprObjectCode.getFinancialObjectCode());
350            depreciationTransaction.setFinancialSubObjectCode(assetPayment.getFinancialSubObjectCode());
351            depreciationTransaction.setFinancialObjectTypeCode(deprObjectCode.getFinancialObjectTypeCode());
352            depreciationTransaction.setTransactionType(transactionType);
353            depreciationTransaction.setProjectCode(assetPayment.getProjectCode());
354            depreciationTransaction.setTransactionAmount(assetPayment.getTransactionAmount());
355            depreciationTransaction.setTransactionLedgerEntryDescription(CamsConstants.Depreciation.TRANSACTION_DESCRIPTION + assetPayment.getCapitalAssetNumber());
356    
357            String sKey = depreciationTransaction.getKey();
358    
359            // Grouping the asset transactions by asset#, accounts, sub account, object, transaction type (C/D), etc. in order to
360            // only have one credit and one credit by group.
361            if (depreciationTransactionSummary.containsKey(sKey)) {
362                depreciationTransaction = depreciationTransactionSummary.get(sKey);
363                depreciationTransaction.setTransactionAmount(depreciationTransaction.getTransactionAmount().add(assetPayment.getTransactionAmount()));
364            }
365            else {
366                depreciationTransactionSummary.put(sKey, depreciationTransaction);
367            }
368            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");
369        }
370    
371        /**
372         * This method stores the depreciation transactions in the general pending entry table and creates a new documentHeader entry.
373         * <p>
374         * 
375         * @param trans SortedMap with the transactions
376         * @return none
377         */
378        protected void processGeneralLedgerPendingEntry(SortedMap<String, AssetDepreciationTransaction> trans) {
379            LOG.debug("populateExplicitGeneralLedgerPendingEntry(AccountingDocument, AccountingLine, GeneralLedgerPendingEntrySequenceHelper, GeneralLedgerPendingEntry) - start");
380    
381            String financialSystemDocumentTypeCodeCode;
382            try {
383    
384                String documentNumber = createNewDepreciationDocument();
385                financialSystemDocumentTypeCodeCode = CamsConstants.DocumentTypeName.ASSET_DEPRECIATION;
386                LOG.info(CamsConstants.Depreciation.DEPRECIATION_BATCH + "Depreciation Document Type Code: " + financialSystemDocumentTypeCodeCode);
387    
388                Timestamp transactionTimestamp = new Timestamp(dateTimeService.getCurrentDate().getTime());
389    
390                GeneralLedgerPendingEntrySequenceHelper sequenceHelper = new GeneralLedgerPendingEntrySequenceHelper();
391                List<GeneralLedgerPendingEntry> saveList = new ArrayList<GeneralLedgerPendingEntry>();
392                int counter = 0;
393    
394                for (AssetDepreciationTransaction t : trans.values()) {
395                    if (t.getTransactionAmount().isNonZero()) {
396                        counter++;
397                        LOG.info(CamsConstants.Depreciation.DEPRECIATION_BATCH + "Creating GLPE entries for asset:" + t.getCapitalAssetNumber());
398                        GeneralLedgerPendingEntry explicitEntry = new GeneralLedgerPendingEntry();
399                        explicitEntry.setFinancialSystemOriginationCode(KFSConstants.ORIGIN_CODE_KUALI);
400                        explicitEntry.setDocumentNumber(documentNumber);
401                        explicitEntry.setTransactionLedgerEntrySequenceNumber(new Integer(sequenceHelper.getSequenceCounter()));
402                        sequenceHelper.increment();
403                        explicitEntry.setChartOfAccountsCode(t.getChartOfAccountsCode());
404                        explicitEntry.setAccountNumber(t.getAccountNumber());
405                        explicitEntry.setSubAccountNumber(t.getSubAccountNumber());
406                        explicitEntry.setFinancialObjectCode(t.getFinancialObjectCode());
407                        explicitEntry.setFinancialSubObjectCode(t.getFinancialSubObjectCode());
408                        explicitEntry.setFinancialBalanceTypeCode(BALANCE_TYPE_ACTUAL);
409                        explicitEntry.setFinancialObjectTypeCode(t.getFinancialObjectTypeCode());
410                        explicitEntry.setUniversityFiscalYear(this.fiscalYear);
411                        explicitEntry.setUniversityFiscalPeriodCode(StringUtils.leftPad(this.fiscalMonth.toString().trim(), 2, "0"));
412                        explicitEntry.setTransactionLedgerEntryDescription(t.getTransactionLedgerEntryDescription());
413                        explicitEntry.setTransactionLedgerEntryAmount(t.getTransactionAmount().abs());
414                        explicitEntry.setTransactionDebitCreditCode(t.getTransactionType());
415                        explicitEntry.setTransactionDate(new java.sql.Date(transactionTimestamp.getTime()));
416                        explicitEntry.setFinancialDocumentTypeCode(financialSystemDocumentTypeCodeCode);
417                        explicitEntry.setFinancialDocumentApprovedCode(KFSConstants.DocumentStatusCodes.APPROVED);
418                        explicitEntry.setVersionNumber(new Long(1));
419                        explicitEntry.setTransactionEntryProcessedTs(new java.sql.Timestamp(transactionTimestamp.getTime()));
420                        // this.generalLedgerPendingEntryService.save(explicitEntry);
421                        saveList.add(explicitEntry);
422                        if (counter % 1000 == 0) {
423                            // save here
424                            getDepreciationBatchDao().savePendingGLEntries(saveList);
425                            saveList.clear();
426                        }
427                        if (sequenceHelper.getSequenceCounter() == 99999) {
428                            // create new document and sequence is reset
429                            documentNumber = createNewDepreciationDocument();
430                            sequenceHelper = new GeneralLedgerPendingEntrySequenceHelper();
431                        }
432                    }
433                }
434                // save last list
435                getDepreciationBatchDao().savePendingGLEntries(saveList);
436                saveList.clear();
437    
438            }
439            catch (Exception e) {
440                LOG.error("Error occurred", e);
441                throw new IllegalStateException(kualiConfigurationService.getPropertyString(CamsKeyConstants.Depreciation.ERROR_WHEN_UPDATING_GL_PENDING_ENTRY_TABLE) + " :" + e.getMessage());
442            }
443            LOG.debug("populateExplicitGeneralLedgerPendingEntry(AccountingDocument, AccountingLine, GeneralLedgerPendingEntrySequenceHelper, GeneralLedgerPendingEntry) - end");
444        }
445    
446    
447        protected String createNewDepreciationDocument() throws WorkflowException {
448            KualiWorkflowDocument workflowDocument = workflowDocumentService.createWorkflowDocument(CamsConstants.DocumentTypeName.ASSET_DEPRECIATION, GlobalVariables.getUserSession().getPerson());
449            // **************************************************************************************************
450            // Create a new document header object
451            // **************************************************************************************************
452            LOG.info(CamsConstants.Depreciation.DEPRECIATION_BATCH + "Creating document header entry.");
453    
454            FinancialSystemDocumentHeader documentHeader = new FinancialSystemDocumentHeader();
455            documentHeader.setWorkflowDocument(workflowDocument);
456            documentHeader.setDocumentNumber(workflowDocument.getRouteHeaderId().toString());
457            documentHeader.setFinancialDocumentStatusCode(KFSConstants.DocumentStatusCodes.APPROVED);
458            documentHeader.setExplanation(CamsConstants.Depreciation.DOCUMENT_DESCRIPTION);
459            documentHeader.setDocumentDescription(CamsConstants.Depreciation.DOCUMENT_DESCRIPTION);
460            documentHeader.setFinancialDocumentTotalAmount(KualiDecimal.ZERO);
461    
462            LOG.info(CamsConstants.Depreciation.DEPRECIATION_BATCH + "Saving document header entry.");
463            this.businessObjectService.save(documentHeader);
464            LOG.info(CamsConstants.Depreciation.DEPRECIATION_BATCH + "Document Header entry was saved successfully.");
465            // **************************************************************************************************
466    
467            String documentNumber = documentHeader.getDocumentNumber();
468            this.documentNos.add(documentNumber);
469            LOG.info(CamsConstants.Depreciation.DEPRECIATION_BATCH + "Document Number Created: " + documentNumber);
470            return documentNumber;
471        }
472    
473    
474        /**
475         * Depreciation object code is returned from cache or from DB
476         * 
477         * @param capitalizationObjectCodes collection cache
478         * @param assetPaymentInfo
479         * @param capitalizationFinancialObjectCode
480         * @return
481         */
482        protected ObjectCode getDepreciationObjectCode(Map<String, ObjectCode> capObjectCodesCache, AssetPaymentInfo assetPaymentInfo, String capitalizationFinancialObjectCode) {
483            ObjectCode deprObjCode = null;
484            String key = assetPaymentInfo.getChartOfAccountsCode() + "-" + capitalizationFinancialObjectCode;
485            if ((deprObjCode = capObjectCodesCache.get(key)) == null) {
486                deprObjCode = SpringContext.getBean(ObjectCodeService.class).getByPrimaryId(fiscalYear, assetPaymentInfo.getChartOfAccountsCode(), capitalizationFinancialObjectCode);
487                if (ObjectUtils.isNotNull(deprObjCode)) {
488                    capObjectCodesCache.put(key, deprObjCode);
489                }
490            }
491            return deprObjCode;
492        }
493    
494        /**
495         * Builds map between object code to corresponding asset object code
496         * 
497         * @return Map
498         */
499        protected Map<String, AssetObjectCode> buildChartObjectToCapitalizationObjectMap() {
500            Map<String, AssetObjectCode> assetObjectCodeMap = new HashMap<String, AssetObjectCode>();
501            Collection<AssetObjectCode> assetObjectCodes = depreciableAssetsDao.getAssetObjectCodes(fiscalYear);
502    
503            for (AssetObjectCode assetObjectCode : assetObjectCodes) {
504                List<ObjectCode> objectCodes = assetObjectCode.getObjectCode();
505                for (ObjectCode objectCode : objectCodes) {
506                    String key = objectCode.getChartOfAccountsCode() + "-" + objectCode.getFinancialObjectCode();
507                    if (!assetObjectCodeMap.containsKey(key)) {
508                        assetObjectCodeMap.put(key, assetObjectCode);
509                    }
510                }
511            }
512            return assetObjectCodeMap;
513        }
514    
515        public void setParameterService(ParameterService parameterService) {
516            this.parameterService = parameterService;
517        }
518    
519        public void setDepreciableAssetsDao(DepreciableAssetsDao depreciableAssetsDao) {
520            this.depreciableAssetsDao = depreciableAssetsDao;
521        }
522    
523        public void setCamsReportService(ReportService reportService) {
524            this.reportService = reportService;
525        }
526    
527        public void setKualiConfigurationService(KualiConfigurationService kcs) {
528            kualiConfigurationService = kcs;
529        }
530    
531        public void setGeneralLedgerPendingEntryService(GeneralLedgerPendingEntryService generalLedgerPendingEntryService) {
532            this.generalLedgerPendingEntryService = generalLedgerPendingEntryService;
533        }
534    
535        public void setDateTimeService(DateTimeService dateTimeService) {
536            this.dateTimeService = dateTimeService;
537        }
538    
539        public void setUniversityDateDao(UniversityDateDao universityDateDao) {
540            this.universityDateDao = universityDateDao;
541        }
542    
543        public void setBusinessObjectService(BusinessObjectService businessObjectService) {
544            this.businessObjectService = businessObjectService;
545        }
546    
547        public void setWorkflowDocumentService(WorkflowDocumentService workflowDocumentService) {
548            this.workflowDocumentService = workflowDocumentService;
549        }
550    
551    
552        public DataDictionaryService getDataDictionaryService() {
553            return dataDictionaryService;
554        }
555    
556        public void setDataDictionaryService(DataDictionaryService dataDictionaryService) {
557            this.dataDictionaryService = dataDictionaryService;
558        }
559    
560        /**
561         * Gets the depreciationBatchDao attribute.
562         * 
563         * @return Returns the depreciationBatchDao.
564         */
565        public DepreciationBatchDao getDepreciationBatchDao() {
566            return depreciationBatchDao;
567        }
568    
569        /**
570         * Sets the depreciationBatchDao attribute value.
571         * 
572         * @param depreciationBatchDao The depreciationBatchDao to set.
573         */
574        public void setDepreciationBatchDao(DepreciationBatchDao depreciationBatchDao) {
575            this.depreciationBatchDao = depreciationBatchDao;
576        }
577    }