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 }