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.endow.batch.service.impl; 017 018 import java.text.MessageFormat; 019 import java.util.ArrayList; 020 import java.util.Collection; 021 import java.util.Date; 022 import java.util.HashMap; 023 import java.util.List; 024 import java.util.Map; 025 import java.util.Set; 026 027 import org.apache.commons.lang.StringUtils; 028 import org.kuali.kfs.module.endow.EndowConstants; 029 import org.kuali.kfs.module.endow.EndowParameterKeyConstants; 030 import org.kuali.kfs.module.endow.EndowPropertyConstants; 031 import org.kuali.kfs.module.endow.batch.CreateRecurringCashTransferTransactionsStep; 032 import org.kuali.kfs.module.endow.batch.reporter.ReportDocumentStatistics; 033 import org.kuali.kfs.module.endow.batch.service.CreateRecurringCashTransferTransactionsService; 034 import org.kuali.kfs.module.endow.businessobject.EndowmentRecurringCashTransfer; 035 import org.kuali.kfs.module.endow.businessobject.EndowmentRecurringCashTransferGLTarget; 036 import org.kuali.kfs.module.endow.businessobject.EndowmentRecurringCashTransferKEMIDTarget; 037 import org.kuali.kfs.module.endow.businessobject.EndowmentSourceTransactionLine; 038 import org.kuali.kfs.module.endow.businessobject.EndowmentTargetTransactionLine; 039 import org.kuali.kfs.module.endow.businessobject.KemidCurrentCash; 040 import org.kuali.kfs.module.endow.businessobject.RecurringCashTransferTransactionDocumentExceptionReportLine; 041 import org.kuali.kfs.module.endow.businessobject.RecurringCashTransferTransactionDocumentTotalReportLine; 042 import org.kuali.kfs.module.endow.businessobject.TargetEndowmentAccountingLine; 043 import org.kuali.kfs.module.endow.businessobject.TransactionArchive; 044 import org.kuali.kfs.module.endow.document.CashTransferDocument; 045 import org.kuali.kfs.module.endow.document.EndowmentToGLTransferOfFundsDocument; 046 import org.kuali.kfs.module.endow.document.service.HoldingTaxLotService; 047 import org.kuali.kfs.module.endow.document.service.KEMService; 048 import org.kuali.kfs.module.endow.document.service.KemidCurrentCashService; 049 import org.kuali.kfs.module.endow.document.validation.event.AddEndowmentAccountingLineEvent; 050 import org.kuali.kfs.module.endow.document.validation.event.AddTransactionLineEvent; 051 import org.kuali.kfs.sys.service.ReportWriterService; 052 import org.kuali.rice.kew.exception.WorkflowException; 053 import org.kuali.rice.kns.bo.AdHocRouteRecipient; 054 import org.kuali.rice.kns.bo.DocumentHeader; 055 import org.kuali.rice.kns.rule.event.RouteDocumentEvent; 056 import org.kuali.rice.kns.service.BusinessObjectService; 057 import org.kuali.rice.kns.service.DataDictionaryService; 058 import org.kuali.rice.kns.service.DocumentService; 059 import org.kuali.rice.kns.service.KualiConfigurationService; 060 import org.kuali.rice.kns.service.KualiRuleService; 061 import org.kuali.rice.kns.service.ParameterService; 062 import org.kuali.rice.kns.util.ErrorMessage; 063 import org.kuali.rice.kns.util.GlobalVariables; 064 import org.kuali.rice.kns.util.KualiDecimal; 065 import org.kuali.rice.kns.util.MessageMap; 066 import org.kuali.rice.kns.util.ObjectUtils; 067 import org.springframework.transaction.annotation.Transactional; 068 069 @Transactional 070 public class CreateRecurringCashTransferTransactionsServiceImpl implements CreateRecurringCashTransferTransactionsService { 071 072 protected static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(CreateCashSweepTransactionsServiceImpl.class); 073 074 private Map<String, ReportDocumentStatistics> statistics = new HashMap<String, ReportDocumentStatistics>(); 075 private BusinessObjectService businessObjectService; 076 private KEMService kemService; 077 private DocumentService documentService; 078 private ParameterService parameterService; 079 private KualiRuleService kualiRuleService; 080 private KemidCurrentCashService kemidCurrentCashService; 081 private HoldingTaxLotService holdingTaxLotService; 082 083 private KualiConfigurationService configService; 084 private DataDictionaryService dataDictionaryService; 085 086 private ReportWriterService recurringCashTransferTransactionsExceptionReportWriterService; 087 private ReportWriterService recurringCashTransferTransactionsTotalReportWriterService; 088 089 private RecurringCashTransferTransactionDocumentExceptionReportLine exceptionReportLine = null; 090 private RecurringCashTransferTransactionDocumentTotalReportLine totalReportLine = null; 091 092 private RecurringCashTransferTransactionDocumentTotalReportLine subTotalReportLine = null; 093 private RecurringCashTransferTransactionDocumentTotalReportLine allTotalReportLine = null; 094 095 private boolean isFistTimeForWritingExceptionReport = true; 096 private boolean isFistTimeForWritingTotalReport = true; 097 098 /** 099 * @see org.kuali.kfs.module.endow.batch.service.CreateRecurringCashTransferTransactionsService#createCashSweepTransactions() 100 */ 101 public boolean createRecurringCashTransferTransactions() { 102 103 LOG.info("Starting \"Create Recurring Cash Transfer Transactions\" batch job..."); 104 105 Collection<EndowmentRecurringCashTransfer> recurringCashTransfers = getAllRecurringCashTransferTransactionsForCurrentDate(); 106 107 // get lists of each case of transaction type 108 Collection<EndowmentRecurringCashTransfer> cashTransfers = new ArrayList(); 109 Collection<EndowmentRecurringCashTransfer> glCashTransfers = new ArrayList(); 110 111 for (EndowmentRecurringCashTransfer endowmentRecurringCashTransfer : recurringCashTransfers) { 112 String sourceTransactionType = endowmentRecurringCashTransfer.getTransactionType(); 113 if (sourceTransactionType.equals(EndowConstants.ENDOWMENT_CASH_TRANSFER_TRANSACTION_TYPE)) { 114 cashTransfers.add(endowmentRecurringCashTransfer); 115 } 116 else { 117 glCashTransfers.add(endowmentRecurringCashTransfer); 118 } 119 } 120 121 // initiate total report 122 allTotalReportLine = new RecurringCashTransferTransactionDocumentTotalReportLine(); 123 124 // cashTransfers (ECT) exist, then calculate 125 if (!cashTransfers.isEmpty()) { 126 subTotalReportLine = new RecurringCashTransferTransactionDocumentTotalReportLine(); 127 calculateCashTransfers(cashTransfers); 128 writeSubTotalReportLine(); 129 } 130 131 // cashTransfers (EGLT) exist, then calculate 132 if (!glCashTransfers.isEmpty()) { 133 subTotalReportLine = new RecurringCashTransferTransactionDocumentTotalReportLine(); 134 calculateGlCashTransfers(glCashTransfers); 135 writeSubTotalReportLine(); 136 } 137 138 writeGrandTotalReportLine(); 139 140 writeStatistics(); 141 142 LOG.info("Finished \"Create Recurring Cash Transfer Transactions\" batch job!"); 143 144 return true; 145 } 146 147 // calculate when ECT 148 protected void calculateCashTransfers(Collection<EndowmentRecurringCashTransfer> cashTransfers) { 149 150 for (EndowmentRecurringCashTransfer cashTransfer : cashTransfers) { 151 String sourceKemid = cashTransfer.getSourceKemid(); 152 String transferNumber = cashTransfer.getTransferNumber(); 153 Date lastProcessDate = cashTransfer.getLastProcessDate(); 154 155 KualiDecimal totalSourceTransaction = KualiDecimal.ZERO; 156 KualiDecimal totalTargetTransaction = KualiDecimal.ZERO; 157 KualiDecimal cashEquivalents = calculateTotalCashEquivalents(cashTransfer); 158 159 CashTransferDocument cashTransferDoc = createCashTransferDocument(sourceKemid, transferNumber); 160 List<EndowmentRecurringCashTransferKEMIDTarget> kemidTargets = cashTransfer.getKemidTarget(); 161 162 163 for (EndowmentRecurringCashTransferKEMIDTarget kemidTarget : kemidTargets) { 164 KualiDecimal transactionAmount = calculateCashTransferTransactionAmount(kemidTarget, cashEquivalents, sourceKemid, lastProcessDate); 165 166 // from spec, total of source and target are same, but totalTargetTransaction will be useful for implementing 167 // total part. 168 totalSourceTransaction = totalSourceTransaction.add(transactionAmount); 169 totalTargetTransaction = totalTargetTransaction.add(transactionAmount); 170 171 // add target line 172 boolean addTransactionLine = addKemidTargetTransactionLine(kemidTarget, cashTransferDoc, transactionAmount, sourceKemid, transferNumber); 173 if (!addTransactionLine) { 174 totalTargetTransaction = totalTargetTransaction.subtract(transactionAmount); 175 totalSourceTransaction = totalSourceTransaction.subtract(transactionAmount); 176 } 177 } 178 // check ALLOW_NEGATIVE_BALANCE_IND and if it is ok then route 179 boolean allowNegativeBalanceInd = parameterService.getIndicatorParameter(CreateRecurringCashTransferTransactionsStep.class, EndowParameterKeyConstants.ALLOW_NEGATIVE_BALANCE_IND); 180 // boolean allowNegativeBalanceInd = true; 181 if (!allowNegativeBalanceInd && totalSourceTransaction.isLessEqual(cashEquivalents)) { 182 // report exception 183 // constants?? 184 writeExceptionReportLine(EndowConstants.DocumentTypeNames.ENDOWMENT_CASH_TRANSFER, sourceKemid, transferNumber, "", "calculated source total is not less than the total available cash equivalents"); 185 186 } 187 else { 188 // add source line 189 addSourceTransactionLineForCashTransferDoc(cashTransfer, cashTransferDoc, totalSourceTransaction); 190 if (routeCashTransferDoc(cashTransferDoc, sourceKemid, transferNumber, totalTargetTransaction)) { 191 subTotalReportLine.incrementTotalTransferAmount(totalTargetTransaction); 192 subTotalReportLine.incrementTargetLinesGenerated(cashTransferDoc.getTargetTransactionLines().size()); 193 194 updatePostingStatsForCashTransferDoc(cashTransferDoc); 195 } 196 } 197 } 198 allTotalReportLine.incrementTotalTransferAmount(subTotalReportLine.getTotalTransferAmount()); 199 allTotalReportLine.incrementTargetLinesGenerated(subTotalReportLine.getTargetLinesGenerated()); 200 } 201 202 protected KualiDecimal calculateCashTransferTransactionAmount(EndowmentRecurringCashTransferKEMIDTarget kemidTarget, KualiDecimal cashEquivalents, String sourceKemid, Date lastProcessDate) { 203 204 // check if it is calculation scenario 1 205 if (ObjectUtils.isNotNull(kemidTarget.getTargetPercent()) && ObjectUtils.isNotNull(kemidTarget.getTargetUseEtranCode())) { 206 // retrieves transactionArchives and calculated source cash 207 KualiDecimal totalCashIncomeEtranCode = KualiDecimal.ZERO; 208 209 List<TransactionArchive> transactionArchiveList = retrieveTransactionArchives(sourceKemid, lastProcessDate, kemidTarget.getTargetUseEtranCode()); 210 // if transactionArchives exist, then calculate total income and total percent of same etran code in target 211 if (transactionArchiveList.size() > 0) { 212 // need to change name...like cashIncrease.. 213 totalCashIncomeEtranCode = calculateTotalIncomeTransactionArchives(transactionArchiveList); 214 } 215 return totalCashIncomeEtranCode.multiply(kemidTarget.getTargetPercent()); 216 } 217 // check if it is calculation scenario 2 218 else if (ObjectUtils.isNotNull(kemidTarget.getTargetPercent())) { 219 return cashEquivalents.multiply(kemidTarget.getTargetPercent()); 220 } 221 // check if it is calculation scenario 3 222 else if (ObjectUtils.isNotNull(kemidTarget.getTargetAmount())) { 223 return kemidTarget.getTargetAmount(); 224 } 225 else 226 return KualiDecimal.ZERO; 227 } 228 229 230 // calculate when EGLT 231 protected void calculateGlCashTransfers(Collection<EndowmentRecurringCashTransfer> glCashTransfers) { 232 for (EndowmentRecurringCashTransfer glCashTransfer : glCashTransfers) { 233 234 String sourceTransactionType = glCashTransfer.getTransactionType(); 235 String sourceKemid = glCashTransfer.getSourceKemid(); 236 String transferNumber = glCashTransfer.getTransferNumber(); 237 Date lastProcessDate = glCashTransfer.getLastProcessDate(); 238 239 KualiDecimal totalSourceTransaction = KualiDecimal.ZERO; 240 KualiDecimal totalTargetTransaction = KualiDecimal.ZERO; 241 KualiDecimal cashEquivalents = calculateTotalCashEquivalents(glCashTransfer); 242 243 EndowmentToGLTransferOfFundsDocument gLTransferOfFundsDocument = createEndowmentToGLTransferOfFundsDocument(sourceKemid, transferNumber); 244 List<EndowmentRecurringCashTransferGLTarget> glTargets = glCashTransfer.getGlTarget(); 245 246 for (EndowmentRecurringCashTransferGLTarget glTarget : glTargets) { 247 KualiDecimal transactionAmount = calculateGlCashTransferTransactionAmount(glTarget, cashEquivalents, sourceKemid, lastProcessDate); 248 249 totalSourceTransaction = totalSourceTransaction.add(transactionAmount); 250 totalTargetTransaction = totalTargetTransaction.add(transactionAmount); 251 252 // add target line 253 boolean addTransactionLine = addGlTransactionLine(glTarget, gLTransferOfFundsDocument, transactionAmount, sourceKemid, transferNumber); 254 if (!addTransactionLine) { 255 totalTargetTransaction = totalTargetTransaction.subtract(transactionAmount); 256 totalSourceTransaction = totalSourceTransaction.subtract(transactionAmount); 257 } 258 } 259 260 // check ALLOW_NEGATIVE_BALANCE_IND and if it is ok then route 261 boolean allowNegativeBalanceInd = parameterService.getIndicatorParameter(CreateRecurringCashTransferTransactionsStep.class, EndowParameterKeyConstants.ALLOW_NEGATIVE_BALANCE_IND); 262 // boolean allowNegativeBalanceInd = true; 263 if (!allowNegativeBalanceInd && totalSourceTransaction.isLessEqual(cashEquivalents)) { 264 // report exception 265 writeExceptionReportLine(EndowConstants.ENDOWMENT_GENERAL_LEDGER_CASH_TRANSFER_TRANSACTION_TYPE, sourceKemid, transferNumber, "calculated source total is less than the total available cash equivalents"); 266 } 267 else { 268 // add source... line 269 addSourceTransactionLineForGLTransferOfFundsDocument(glCashTransfer, gLTransferOfFundsDocument, totalSourceTransaction); 270 // route doc 271 if (routeGLTransferOfFundsDocument(gLTransferOfFundsDocument, sourceKemid, transferNumber, totalTargetTransaction)) { 272 273 subTotalReportLine.incrementTotalTransferAmount(totalTargetTransaction); 274 subTotalReportLine.incrementTargetLinesGenerated(gLTransferOfFundsDocument.getTargetAccountingLines().size()); 275 276 updatePostingStatsForGlTransferDoc(gLTransferOfFundsDocument); 277 } 278 } 279 } 280 allTotalReportLine.incrementTotalTransferAmount(subTotalReportLine.getTotalTransferAmount()); 281 allTotalReportLine.incrementTargetLinesGenerated(subTotalReportLine.getTargetLinesGenerated()); 282 } 283 284 protected KualiDecimal calculateGlCashTransferTransactionAmount(EndowmentRecurringCashTransferGLTarget glTarget, KualiDecimal cashEquivalents, String sourceKemid, Date lastProcessDate) { 285 // check if it is calculation scenario 1 286 if (ObjectUtils.isNotNull(glTarget.getTargetPercent()) && ObjectUtils.isNotNull(glTarget.getTargetUseEtranCode())) { 287 // retrieves transactionArchives and calculated source cash 288 KualiDecimal totalCashIncomeEtranCode = KualiDecimal.ZERO; 289 290 List<TransactionArchive> transactionArchiveList = retrieveTransactionArchives(sourceKemid, lastProcessDate, glTarget.getTargetUseEtranCode()); 291 // if transactionArchives exist, then calculate total income and total percent of same etran code in target 292 if (transactionArchiveList.size() > 0) { 293 totalCashIncomeEtranCode = calculateTotalIncomeTransactionArchives(transactionArchiveList); 294 } 295 return totalCashIncomeEtranCode.multiply(glTarget.getTargetPercent()); 296 } 297 // check if it is calculation scenario 2 298 else if (ObjectUtils.isNotNull(glTarget.getTargetPercent())) { 299 return cashEquivalents.multiply(glTarget.getTargetPercent()); 300 } 301 // check if it is calculation scenario 3 302 else if (ObjectUtils.isNotNull(glTarget.getTargetFdocLineAmount())) { 303 return glTarget.getTargetFdocLineAmount(); 304 } 305 else 306 return KualiDecimal.ZERO; 307 } 308 309 protected KualiDecimal calculateTotalCashEquivalents(EndowmentRecurringCashTransfer endowmentRecurringCashTransfer) { 310 // spec 10.2 311 KualiDecimal totalCashEquivalents = KualiDecimal.ZERO; 312 String kemid = endowmentRecurringCashTransfer.getSourceKemid(); 313 KemidCurrentCash currentCash = kemidCurrentCashService.getByPrimaryKey(kemid); 314 if (endowmentRecurringCashTransfer.getSourceIncomeOrPrincipal().equals(EndowConstants.EndowmentTransactionTypeCodes.INCOME_TYPE_CODE)) { 315 totalCashEquivalents = currentCash.getCurrentIncomeCash().add(new KualiDecimal(holdingTaxLotService.getMarketValueForCashEquivalentsForAvailableIncomeCash(kemid))); 316 } 317 else { 318 totalCashEquivalents = currentCash.getCurrentPrincipalCash().add(new KualiDecimal(holdingTaxLotService.getMarketValueForCashEquivalentsForAvailablePrincipalCash(kemid))); 319 } 320 return totalCashEquivalents; 321 } 322 323 /** 324 * This method retrieves all the recurring cash transfer transactions whose frequency code matches the current date. 325 * 326 * @return List of CashSweepModel business objects 327 */ 328 protected Collection<EndowmentRecurringCashTransfer> getAllRecurringCashTransferTransactionsForCurrentDate() { 329 LOG.info("Getting all EndowmentRecurringCashTransfer with Next process date = current date"); 330 331 List<EndowmentRecurringCashTransfer> endowmentRecurringCashTransfer = new ArrayList<EndowmentRecurringCashTransfer>(); 332 Date currentDate = kemService.getCurrentDate(); 333 Map fieldValues = new HashMap(); 334 fieldValues.put(EndowPropertyConstants.ENDOWMENT_RECURRING_CASH_TRANSF_NEXT_PROC_DATE, currentDate); 335 Collection<EndowmentRecurringCashTransfer> recurringCashTransfers = businessObjectService.findMatching(EndowmentRecurringCashTransfer.class, fieldValues); 336 LOG.info("Number of EndowmentRecurringCashTransfer with Next process date = current date" + recurringCashTransfers.size()); 337 return recurringCashTransfers; 338 } 339 340 // protected List getSortFieldList(){ 341 // List returnList = new Arraylist(); 342 // returnList.add(EndowPropertyConstants.ENDOWMENT_RECURRING_CASH_TRANSF_TRANSACTION_TYPE); 343 // returnList.add(EndowPropertyConstants.ENDOWMENT_RECURRING_CASH_TRANSF_TRANSACTION_TYPE); 344 // 345 // return returnList(); 346 // } 347 348 // List<EndowmentRecurringCashTransfer> recurringCashTransfers = (List) 349 // businessObjectService.findMatching(EndowmentRecurringCashTransfer.class, fieldValues); 350 // //Collection<EndowmentRecurringCashTransfer> recurringCashTransfers = 351 // businessObjectService.findMatching(EndowmentRecurringCashTransfer.class, fieldValues); 352 // List sortFieldList = new getSortFieldList(); 353 // Collections.sort(recurringCashTransfers, new BeanPropertyComparator(getDefaultSortColumns(), true)); 354 355 356 // spec 6.2 3.1.a 357 protected List<TransactionArchive> retrieveTransactionArchives(String sourceKemid, Date lastProcessDate, String targetEtranCode) { 358 KualiDecimal totalCashIncomeEtranCode = KualiDecimal.ZERO; 359 360 List<TransactionArchive> transactionArchiveList = null; 361 Map fieldValues = new HashMap(); 362 fieldValues.put(EndowPropertyConstants.KEMID, sourceKemid); 363 fieldValues.put(EndowPropertyConstants.TRANSACTION_LINE_ENDOWMENT_TRANSACTION_CODE, targetEtranCode); 364 transactionArchiveList = (List) businessObjectService.findMatching(TransactionArchive.class, fieldValues); 365 for (TransactionArchive transactionArchive : transactionArchiveList) { 366 if (!transactionArchive.getPostedDate().after(lastProcessDate)) { 367 transactionArchiveList.remove(transactionArchive); 368 } 369 } 370 return transactionArchiveList; 371 } 372 373 // spec 6.2 3.1.a 374 protected KualiDecimal calculateTotalIncomeTransactionArchives(List<TransactionArchive> transactionArchiveList) { 375 KualiDecimal totalCashIncomeEtranCode = KualiDecimal.ZERO; 376 for (TransactionArchive transactionArchive : transactionArchiveList) { 377 if (transactionArchive.getIncomePrincipalIndicatorCode().equals(EndowConstants.EndowmentTransactionTypeCodes.INCOME_TYPE_CODE)) { 378 totalCashIncomeEtranCode = totalCashIncomeEtranCode.add(new KualiDecimal(transactionArchive.getIncomeCashAmount())); 379 } 380 else { 381 totalCashIncomeEtranCode = totalCashIncomeEtranCode.add(new KualiDecimal(transactionArchive.getPrincipalCashAmount())); 382 } 383 } 384 return totalCashIncomeEtranCode; 385 } 386 387 /** 388 * This method... 389 * 390 * @param assetIncreaseDoc 391 * @param assetSaleOffsetCode 392 * @param kemid 393 * @param cashLimit 394 * @param currentCash 395 */ 396 397 protected void addSourceTransactionLineForCashTransferDoc(EndowmentRecurringCashTransfer endowmentRecurringCashTransfer, CashTransferDocument cashTransferDoc, KualiDecimal totalAmount) { 398 boolean rulesPassed = true; 399 EndowmentSourceTransactionLine transactionLine = new EndowmentSourceTransactionLine(); 400 transactionLine.setTransactionLineTypeCode(EndowConstants.TRANSACTION_LINE_TYPE_SOURCE); 401 transactionLine.setKemid(endowmentRecurringCashTransfer.getSourceKemid()); 402 transactionLine.setEtranCode(endowmentRecurringCashTransfer.getSourceEtranCode()); 403 transactionLine.setTransactionLineDescription(endowmentRecurringCashTransfer.getSourceLineDescription()); 404 transactionLine.setTransactionIPIndicatorCode(endowmentRecurringCashTransfer.getSourceIncomeOrPrincipal()); 405 transactionLine.setTransactionAmount(totalAmount); 406 407 rulesPassed = kualiRuleService.applyRules(new AddTransactionLineEvent(EndowConstants.NEW_SOURCE_TRAN_LINE_PROPERTY_NAME, cashTransferDoc, transactionLine)); 408 409 if (rulesPassed) { 410 cashTransferDoc.addSourceTransactionLine(transactionLine); 411 } 412 else { 413 // report to error 414 writeExceptionReportLine(EndowConstants.DocumentTypeNames.ENDOWMENT_CASH_TRANSFER, endowmentRecurringCashTransfer.getSourceKemid(), endowmentRecurringCashTransfer.getTransferNumber(), EndowConstants.EXISTING_SOURCE_TRAN_LINE_PROPERTY_NAME); 415 String documentTypeName = dataDictionaryService.getDocumentTypeNameByClass(cashTransferDoc.getClass()); 416 updateErrorStats(documentTypeName); 417 } 418 } 419 420 protected void addSourceTransactionLineForGLTransferOfFundsDocument(EndowmentRecurringCashTransfer endowmentRecurringCashTransfer, EndowmentToGLTransferOfFundsDocument gLTransferOfFundsDocument, KualiDecimal totalAmount) { 421 boolean rulesPassed = true; 422 EndowmentSourceTransactionLine transactionLine = new EndowmentSourceTransactionLine(); 423 transactionLine.setTransactionLineTypeCode(EndowConstants.TRANSACTION_LINE_TYPE_SOURCE); 424 transactionLine.setKemid(endowmentRecurringCashTransfer.getSourceKemid()); 425 transactionLine.setEtranCode(endowmentRecurringCashTransfer.getSourceEtranCode()); 426 transactionLine.setTransactionLineDescription(endowmentRecurringCashTransfer.getSourceLineDescription()); 427 transactionLine.setTransactionIPIndicatorCode(endowmentRecurringCashTransfer.getSourceIncomeOrPrincipal()); 428 transactionLine.setTransactionAmount(totalAmount); 429 430 rulesPassed = kualiRuleService.applyRules(new AddTransactionLineEvent(EndowConstants.NEW_SOURCE_TRAN_LINE_PROPERTY_NAME, gLTransferOfFundsDocument, transactionLine)); 431 432 if (rulesPassed) { 433 gLTransferOfFundsDocument.addSourceTransactionLine(transactionLine); 434 } 435 else { 436 // report to error 437 writeExceptionReportLine(EndowConstants.ENDOWMENT_GENERAL_LEDGER_CASH_TRANSFER_TRANSACTION_TYPE, endowmentRecurringCashTransfer.getSourceKemid(), endowmentRecurringCashTransfer.getTransferNumber(), EndowConstants.EXISTING_SOURCE_TRAN_LINE_PROPERTY_NAME); 438 String documentTypeName = dataDictionaryService.getDocumentTypeNameByClass(gLTransferOfFundsDocument.getClass()); 439 updateErrorStats(documentTypeName); 440 } 441 } 442 443 protected boolean addKemidTargetTransactionLine(EndowmentRecurringCashTransferKEMIDTarget endowmentRecurringCashTransferKEMIDTarget, CashTransferDocument cashTransferDoc, KualiDecimal totalAmount, String sourceKemid, String transferNumber) { 444 boolean rulesPassed = true; 445 EndowmentTargetTransactionLine transactionLine = new EndowmentTargetTransactionLine(); 446 // set all necessary fields 447 transactionLine.setTransactionLineTypeCode(EndowConstants.TRANSACTION_LINE_TYPE_TARGET); 448 transactionLine.setKemid(endowmentRecurringCashTransferKEMIDTarget.getTargetKemid()); 449 transactionLine.setEtranCode(endowmentRecurringCashTransferKEMIDTarget.getTargetEtranCode()); 450 transactionLine.setTransactionLineDescription(endowmentRecurringCashTransferKEMIDTarget.getTargetLineDescription()); 451 transactionLine.setTransactionIPIndicatorCode(endowmentRecurringCashTransferKEMIDTarget.getTargetIncomeOrPrincipal()); 452 transactionLine.setTransactionAmount(totalAmount); 453 454 // check rules 455 rulesPassed = kualiRuleService.applyRules(new AddTransactionLineEvent(EndowConstants.NEW_TARGET_TRAN_LINE_PROPERTY_NAME, cashTransferDoc, transactionLine)); 456 457 if (rulesPassed) { 458 cashTransferDoc.addTargetTransactionLine(transactionLine); 459 } 460 else { 461 // report to error 462 writeExceptionReportLine(EndowConstants.DocumentTypeNames.ENDOWMENT_CASH_TRANSFER, sourceKemid, endowmentRecurringCashTransferKEMIDTarget.getTransferNumber(), endowmentRecurringCashTransferKEMIDTarget.getTargetSequenceNumber().toString()); 463 String documentTypeName = dataDictionaryService.getDocumentTypeNameByClass(cashTransferDoc.getClass()); 464 updateErrorStats(documentTypeName); 465 } 466 467 return rulesPassed; 468 } 469 470 protected boolean addGlTransactionLine(EndowmentRecurringCashTransferGLTarget endowmentRecurringCashTransferGLTarget, EndowmentToGLTransferOfFundsDocument endowmentToGLTransferOfFundsDocument, KualiDecimal totalAmount, String sourceKemid, String transferNumber) { 471 boolean rulesPassed = true; 472 TargetEndowmentAccountingLine endowmentAccountingLine = new TargetEndowmentAccountingLine(); 473 // set all necessary fields 474 endowmentAccountingLine.setChartOfAccountsCode(endowmentRecurringCashTransferGLTarget.getTargetChartOfAccountsCode()); 475 endowmentAccountingLine.setAccountNumber(endowmentRecurringCashTransferGLTarget.getTargetAccountsNumber()); 476 endowmentAccountingLine.setFinancialObjectCode(endowmentRecurringCashTransferGLTarget.getTargetFinancialObjectCode()); 477 endowmentAccountingLine.setAmount(totalAmount); 478 // number.setScale(digit, BigDecimal.ROUND_HALF_UP) 479 480 // check rules 481 rulesPassed = kualiRuleService.applyRules(new AddEndowmentAccountingLineEvent(EndowConstants.NEW_TARGET_ACC_LINE_PROPERTY_NAME, endowmentToGLTransferOfFundsDocument, endowmentAccountingLine)); 482 483 if (rulesPassed) { 484 endowmentToGLTransferOfFundsDocument.addTargetAccountingLine(endowmentAccountingLine); 485 } 486 else { 487 // report to error 488 writeExceptionReportLine(EndowConstants.ENDOWMENT_GENERAL_LEDGER_CASH_TRANSFER_TRANSACTION_TYPE, sourceKemid, endowmentRecurringCashTransferGLTarget.getTransferNumber(), endowmentRecurringCashTransferGLTarget.getTargetSequenceNumber().toString()); 489 String documentTypeName = dataDictionaryService.getDocumentTypeNameByClass(endowmentToGLTransferOfFundsDocument.getClass()); 490 updateErrorStats(documentTypeName); 491 492 } 493 494 return rulesPassed; 495 } 496 497 protected CashTransferDocument createCashTransferDocument(String sourceKemid, String transferNumber) { 498 499 CashTransferDocument cashTransferDoc = null; 500 try { 501 cashTransferDoc = (CashTransferDocument) documentService.getNewDocument(CashTransferDocument.class); 502 503 // Set values for doc 504 DocumentHeader docHeader = cashTransferDoc.getDocumentHeader(); 505 String description = parameterService.getParameterValue(CreateRecurringCashTransferTransactionsStep.class, EndowParameterKeyConstants.DESCRIPTION); 506 docHeader.setDocumentDescription(description); 507 cashTransferDoc.setDocumentHeader(docHeader); 508 cashTransferDoc.setTransactionSourceTypeCode(EndowConstants.TransactionSourceTypeCode.RECURRING); 509 } 510 catch (WorkflowException ex) { 511 LOG.error(ex.getLocalizedMessage()); 512 writeExceptionReportLine(EndowConstants.DocumentTypeNames.ENDOWMENT_CASH_TRANSFER, sourceKemid, transferNumber, "", ex.getLocalizedMessage() + " from createCashTransferDocument()"); 513 } 514 515 return cashTransferDoc; 516 } 517 518 protected boolean routeCashTransferDoc(CashTransferDocument cashTransferDoc, String sourceKemid, String transferNumber, KualiDecimal totalAmount) { 519 boolean routeSuccessInd = true; 520 boolean rulesPassed = kualiRuleService.applyRules(new RouteDocumentEvent(cashTransferDoc)); 521 522 if (rulesPassed) { 523 // no adhoc recipient need to add when submit doc. doc will route to the doc uploader, i.e. initiator automtically. 524 List<AdHocRouteRecipient> adHocRoutingRecipients = new ArrayList<AdHocRouteRecipient>(); 525 try { 526 cashTransferDoc.setNoRouteIndicator(getNoRouteParameterAsBoolean()); 527 documentService.routeDocument(cashTransferDoc, "Route CashIncreaseDocument", adHocRoutingRecipients); 528 writeTotalReportLine(EndowConstants.DocumentTypeNames.ENDOWMENT_CASH_TRANSFER, cashTransferDoc.getDocumentNumber(), transferNumber, sourceKemid, new Integer(cashTransferDoc.getTargetTransactionLines().size()), totalAmount); 529 530 } 531 catch (WorkflowException ex) { 532 LOG.error(ex.getLocalizedMessage()); 533 writeExceptionReportLine(EndowConstants.DocumentTypeNames.ENDOWMENT_CASH_TRANSFER, sourceKemid, transferNumber, "", ex.getLocalizedMessage() + " from routeCashTransferDoc()"); 534 String documentTypeName = dataDictionaryService.getDocumentTypeNameByClass(cashTransferDoc.getClass()); 535 updateErrorStats(documentTypeName); 536 routeSuccessInd = false; 537 } 538 } 539 else { 540 // report to error 541 writeExceptionReportLine(EndowConstants.DocumentTypeNames.ENDOWMENT_CASH_TRANSFER, sourceKemid, transferNumber, ""); 542 String documentTypeName = dataDictionaryService.getDocumentTypeNameByClass(cashTransferDoc.getClass()); 543 updateErrorStats(documentTypeName); 544 routeSuccessInd = false; 545 } 546 547 return routeSuccessInd; 548 } 549 550 protected boolean routeGLTransferOfFundsDocument(EndowmentToGLTransferOfFundsDocument gLTransferOfFundsDocument, String sourceKemid, String transferNumber, KualiDecimal totalAmount) { 551 boolean routeSuccessInd = true; 552 boolean rulesPassed = kualiRuleService.applyRules(new RouteDocumentEvent(gLTransferOfFundsDocument)); 553 554 if (rulesPassed) { 555 // no adhoc recipient need to add when submit doc. doc will route to the doc uploader, i.e. initiator automtically. 556 List<AdHocRouteRecipient> adHocRoutingRecipients = new ArrayList<AdHocRouteRecipient>(); 557 try { 558 gLTransferOfFundsDocument.setNoRouteIndicator(getNoRouteParameterAsBoolean()); 559 documentService.routeDocument(gLTransferOfFundsDocument, "Route gLTransferOfFundsDocument", adHocRoutingRecipients); 560 561 writeTotalReportLine(EndowConstants.ENDOWMENT_GENERAL_LEDGER_CASH_TRANSFER_TRANSACTION_TYPE, gLTransferOfFundsDocument.getDocumentNumber(), transferNumber, sourceKemid, new Integer(gLTransferOfFundsDocument.getTargetAccountingLines().size()), totalAmount); 562 563 } 564 catch (WorkflowException ex) { 565 LOG.error(ex.getLocalizedMessage()); 566 567 writeExceptionReportLine(EndowConstants.ENDOWMENT_GENERAL_LEDGER_CASH_TRANSFER_TRANSACTION_TYPE, sourceKemid, transferNumber, "", ex.getLocalizedMessage() + " from routeGLTransferOfFundsDocument()"); 568 String documentTypeName = dataDictionaryService.getDocumentTypeNameByClass(gLTransferOfFundsDocument.getClass()); 569 updateErrorStats(documentTypeName); 570 routeSuccessInd = false; 571 } 572 } 573 else { 574 // report to error 575 writeExceptionReportLine(EndowConstants.ENDOWMENT_GENERAL_LEDGER_CASH_TRANSFER_TRANSACTION_TYPE, sourceKemid, transferNumber, ""); 576 String documentTypeName = dataDictionaryService.getDocumentTypeNameByClass(gLTransferOfFundsDocument.getClass()); 577 updateErrorStats(documentTypeName); 578 routeSuccessInd = false; 579 } 580 581 return routeSuccessInd; 582 } 583 584 protected EndowmentToGLTransferOfFundsDocument createEndowmentToGLTransferOfFundsDocument(String sourceKemid, String transferNumber) { 585 EndowmentToGLTransferOfFundsDocument endowmentToGLTransferOfFundsDocument = null; 586 try { 587 endowmentToGLTransferOfFundsDocument = (EndowmentToGLTransferOfFundsDocument) documentService.getNewDocument(EndowmentToGLTransferOfFundsDocument.class); 588 589 // Set values for doc 590 DocumentHeader docHeader = endowmentToGLTransferOfFundsDocument.getDocumentHeader(); 591 String description = parameterService.getParameterValue(CreateRecurringCashTransferTransactionsStep.class, EndowParameterKeyConstants.DESCRIPTION); 592 docHeader.setDocumentDescription(description); 593 endowmentToGLTransferOfFundsDocument.setDocumentHeader(docHeader); 594 endowmentToGLTransferOfFundsDocument.setTransactionSourceTypeCode(EndowConstants.TransactionSourceTypeCode.RECURRING); 595 } 596 catch (WorkflowException ex) { 597 LOG.error(ex.getLocalizedMessage()); 598 599 writeExceptionReportLine(EndowConstants.ENDOWMENT_GENERAL_LEDGER_CASH_TRANSFER_TRANSACTION_TYPE, sourceKemid, transferNumber, "", ex.getLocalizedMessage() + " from createEndowmentToGLTransferOfFundsDocument()"); 600 } 601 602 return endowmentToGLTransferOfFundsDocument; 603 604 } 605 606 protected boolean getNoRouteParameterAsBoolean() { 607 String noRouteIndParm = parameterService.getParameterValue(CreateRecurringCashTransferTransactionsStep.class, EndowParameterKeyConstants.NO_ROUTE_IND); 608 if (noRouteIndParm.equals(EndowConstants.YES)) { 609 return true; 610 } 611 else 612 return false; 613 } 614 615 /** 616 * Extracts errors for error report writing. 617 * 618 * @return a list of error messages 619 */ 620 protected List<String> extractGlobalVariableErrors() { 621 List<String> result = new ArrayList<String>(); 622 623 MessageMap errorMap = GlobalVariables.getMessageMap(); 624 625 Set<String> errorKeys = errorMap.keySet(); 626 List<ErrorMessage> errorMessages = null; 627 Object[] messageParams; 628 String errorKeyString; 629 String errorString; 630 631 for (String errorProperty : errorKeys) { 632 errorMessages = (List<ErrorMessage>) errorMap.get(errorProperty); 633 for (ErrorMessage errorMessage : errorMessages) { 634 errorKeyString = configService.getPropertyString(errorMessage.getErrorKey()); 635 messageParams = errorMessage.getMessageParameters(); 636 637 // MessageFormat.format only seems to replace one 638 // per pass, so I just keep beating on it until all are gone. 639 if (StringUtils.isBlank(errorKeyString)) { 640 errorString = errorMessage.getErrorKey(); 641 } 642 else { 643 errorString = errorKeyString; 644 } 645 System.out.println(errorString); 646 while (errorString.matches("^.*\\{\\d\\}.*$")) { 647 errorString = MessageFormat.format(errorString, messageParams); 648 } 649 result.add(errorString); 650 } 651 } 652 653 // clear the stuff out of globalvars, as we need to reformat it and put it back 654 GlobalVariables.getMessageMap().clear(); 655 return result; 656 } 657 658 protected void writeExceptionReportLine(String documentType, String sourceKemid, String transferNumber, String targetSeqNumber) { 659 writeExceptionReportLine(documentType, sourceKemid, transferNumber, targetSeqNumber, ""); 660 } 661 662 protected void writeExceptionReportLine(String documentType, String sourceKemid, String transferNumber, String targetSeqNumber, String reason) { 663 // write an exception line when a transaction line fails to pass the validation. 664 exceptionReportLine = new RecurringCashTransferTransactionDocumentExceptionReportLine(documentType, sourceKemid, transferNumber, targetSeqNumber); 665 666 if (isFistTimeForWritingExceptionReport) { 667 recurringCashTransferTransactionsExceptionReportWriterService.writeTableHeader(exceptionReportLine); 668 isFistTimeForWritingExceptionReport = false; 669 } 670 recurringCashTransferTransactionsExceptionReportWriterService.writeTableRow(exceptionReportLine); 671 672 if (StringUtils.isBlank(reason)) { 673 List<String> errorMessages = extractGlobalVariableErrors(); 674 for (String errorMessage : errorMessages) { 675 recurringCashTransferTransactionsExceptionReportWriterService.writeFormattedMessageLine("Reason: %s", errorMessage); 676 recurringCashTransferTransactionsExceptionReportWriterService.writeNewLines(1); 677 } 678 } 679 else { 680 recurringCashTransferTransactionsExceptionReportWriterService.writeFormattedMessageLine("Reason: %s", reason); 681 recurringCashTransferTransactionsExceptionReportWriterService.writeNewLines(1); 682 } 683 } 684 685 protected void writeTotalReportLine(String documentType, String documentId, String transferNumber, String sourcekemid, Integer targetLinesGenerated, KualiDecimal totalTransferAmount) { 686 totalReportLine = new RecurringCashTransferTransactionDocumentTotalReportLine(documentType, documentId, transferNumber, sourcekemid, targetLinesGenerated, totalTransferAmount); 687 688 if (isFistTimeForWritingTotalReport) { 689 recurringCashTransferTransactionsTotalReportWriterService.writeTableHeader(totalReportLine); 690 isFistTimeForWritingTotalReport = false; 691 } 692 693 recurringCashTransferTransactionsTotalReportWriterService.writeTableRow(totalReportLine); 694 } 695 696 protected void writeSubTotalReportLine() { 697 writeTotalReportLine("SubTotal", "", "", "", subTotalReportLine.getTargetLinesGenerated(), subTotalReportLine.getTotalTransferAmount()); 698 recurringCashTransferTransactionsTotalReportWriterService.writeNewLines(1); 699 } 700 701 protected void writeGrandTotalReportLine() { 702 writeTotalReportLine("Total", "", "", "", allTotalReportLine.getTargetLinesGenerated(), allTotalReportLine.getTotalTransferAmount()); 703 } 704 705 /** 706 * Sets the businessObjectService attribute value. 707 * 708 * @param businessObjectService The businessObjectService to set. 709 */ 710 public void setBusinessObjectService(BusinessObjectService businessObjectService) { 711 this.businessObjectService = businessObjectService; 712 } 713 714 /** 715 * Sets the kemService attribute value. 716 * 717 * @param kemService The kemService to set. 718 */ 719 public void setKemService(KEMService kemService) { 720 this.kemService = kemService; 721 } 722 723 /** 724 * Sets the documentService attribute value. 725 * 726 * @param documentService The documentService to set. 727 */ 728 public void setDocumentService(DocumentService documentService) { 729 this.documentService = documentService; 730 } 731 732 /** 733 * Sets the parameterService attribute value. 734 * 735 * @param parameterService The parameterService to set. 736 */ 737 public void setParameterService(ParameterService parameterService) { 738 this.parameterService = parameterService; 739 } 740 741 /** 742 * Sets the kualiRuleService attribute value. 743 * 744 * @param kualiRuleService The kualiRuleService to set. 745 */ 746 public void setKualiRuleService(KualiRuleService kualiRuleService) { 747 this.kualiRuleService = kualiRuleService; 748 } 749 750 /** 751 * Sets the kemidCurrentCashOpenRecordsService 752 * 753 * @param kemidCurrentCashOpenRecordsService The kemidCurrentCashOpenRecordsService to set. 754 */ 755 public void setKemidCurrentCashService(KemidCurrentCashService kemidCurrentCashService) { 756 this.kemidCurrentCashService = kemidCurrentCashService; 757 } 758 759 public void setHoldingTaxLotService(HoldingTaxLotService holdingTaxLotService) { 760 this.holdingTaxLotService = holdingTaxLotService; 761 } 762 763 public void setRecurringCashTransferTransactionsExceptionReportWriterService(ReportWriterService recurringCashTransferTransactionsExceptionReportWriterService) { 764 this.recurringCashTransferTransactionsExceptionReportWriterService = recurringCashTransferTransactionsExceptionReportWriterService; 765 } 766 767 public void setRecurringCashTransferTransactionsTotalReportWriterService(ReportWriterService recurringCashTransferTransactionsTotalReportWriterService) { 768 this.recurringCashTransferTransactionsTotalReportWriterService = recurringCashTransferTransactionsTotalReportWriterService; 769 } 770 771 public void setDataDictionaryService(DataDictionaryService dataDictionaryService) { 772 this.dataDictionaryService = dataDictionaryService; 773 } 774 775 /** 776 * Sets the configService. 777 * 778 * @param configService 779 */ 780 public void setConfigService(KualiConfigurationService configService) { 781 this.configService = configService; 782 } 783 784 private void updatePostingStatsForCashTransferDoc(CashTransferDocument cashTransferDoc) { 785 786 String documentTypeName = dataDictionaryService.getDocumentTypeNameByClass(cashTransferDoc.getClass()); 787 ReportDocumentStatistics stats = statistics.get(documentTypeName); 788 789 // If null that means there isn't one in the map, so create it and add 790 // it to the map. 791 if (stats == null) { 792 stats = new ReportDocumentStatistics(documentTypeName); 793 statistics.put(documentTypeName, stats); 794 } 795 stats.addNumberOfSourceTransactionLines(cashTransferDoc.getSourceTransactionLines().size()); 796 stats.addNumberOfTargetTransactionLines(cashTransferDoc.getTargetTransactionLines().size()); 797 798 stats.incrementNumberOfDocuments(); 799 } 800 801 private void updatePostingStatsForGlTransferDoc(EndowmentToGLTransferOfFundsDocument glTransferDoc) { 802 803 String documentTypeName = dataDictionaryService.getDocumentTypeNameByClass(glTransferDoc.getClass()); 804 ReportDocumentStatistics stats = statistics.get(documentTypeName); 805 806 // If null that means there isn't one in the map, so create it and add 807 // it to the map. 808 if (stats == null) { 809 stats = new ReportDocumentStatistics(documentTypeName); 810 statistics.put(documentTypeName, stats); 811 } 812 stats.addNumberOfSourceTransactionLines(glTransferDoc.getSourceTransactionLines().size()); 813 stats.addNumberOfTargetTransactionLines(glTransferDoc.getTargetAccountingLines().size()); 814 815 stats.incrementNumberOfDocuments(); 816 } 817 818 private void updateErrorStats(String documentTypeName) { 819 ReportDocumentStatistics stats = statistics.get(documentTypeName); 820 821 // If null that means there isn't one in the map, so create it and add 822 // it to the map. 823 if (stats == null) { 824 stats = new ReportDocumentStatistics(documentTypeName); 825 statistics.put(documentTypeName, stats); 826 } 827 828 stats.incrementNumberOfErrors(); 829 } 830 831 /** 832 * Write out the statistics. 833 */ 834 private void writeStatistics() { 835 836 for (Map.Entry<String, ReportDocumentStatistics> entry : statistics.entrySet()) { 837 838 ReportDocumentStatistics stats = entry.getValue(); 839 840 recurringCashTransferTransactionsTotalReportWriterService.writeStatisticLine("%s Documents:", stats.getDocumentTypeName()); 841 recurringCashTransferTransactionsTotalReportWriterService.writeStatisticLine(" Number of Documents Generated: %d", stats.getNumberOfDocuments()); 842 recurringCashTransferTransactionsTotalReportWriterService.writeStatisticLine(" Number of Transaction Lines Generated: %d", stats.getTotalNumberOfTransactionLines()); 843 recurringCashTransferTransactionsTotalReportWriterService.writeStatisticLine(" Number of Error Records Written: %d", stats.getNumberOfErrors()); 844 recurringCashTransferTransactionsTotalReportWriterService.writeStatisticLine("", ""); 845 } 846 847 } 848 849 850 }