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 static org.kuali.kfs.module.endow.EndowConstants.NEW_SOURCE_TRAN_LINE_PROPERTY_NAME; 019 import static org.kuali.kfs.module.endow.EndowConstants.NEW_TARGET_TRAN_LINE_PROPERTY_NAME; 020 021 import java.math.BigDecimal; 022 import java.util.ArrayList; 023 import java.util.Collection; 024 import java.util.HashMap; 025 import java.util.List; 026 import java.util.Map; 027 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.CreateCashSweepTransactionsStep; 032 import org.kuali.kfs.module.endow.batch.reporter.ReportDocumentStatistics; 033 import org.kuali.kfs.module.endow.batch.service.CreateCashSweepTransactionsService; 034 import org.kuali.kfs.module.endow.businessobject.CashSweepModel; 035 import org.kuali.kfs.module.endow.businessobject.EndowmentExceptionReportHeader; 036 import org.kuali.kfs.module.endow.businessobject.EndowmentSourceTransactionLine; 037 import org.kuali.kfs.module.endow.businessobject.EndowmentSourceTransactionSecurity; 038 import org.kuali.kfs.module.endow.businessobject.EndowmentTargetTransactionLine; 039 import org.kuali.kfs.module.endow.businessobject.EndowmentTargetTransactionSecurity; 040 import org.kuali.kfs.module.endow.businessobject.EndowmentTransactionLine; 041 import org.kuali.kfs.module.endow.businessobject.HoldingTaxLot; 042 import org.kuali.kfs.module.endow.businessobject.KEMID; 043 import org.kuali.kfs.module.endow.businessobject.KemidCurrentCash; 044 import org.kuali.kfs.module.endow.businessobject.PooledFundControl; 045 import org.kuali.kfs.module.endow.businessobject.TransactionDocumentExceptionReportLine; 046 import org.kuali.kfs.module.endow.businessobject.TransactionDocumentTotalReportLine; 047 import org.kuali.kfs.module.endow.dataaccess.CashSweepModelDao; 048 import org.kuali.kfs.module.endow.document.AssetDecreaseDocument; 049 import org.kuali.kfs.module.endow.document.AssetIncreaseDocument; 050 import org.kuali.kfs.module.endow.document.EndowmentTaxLotLinesDocumentBase; 051 import org.kuali.kfs.module.endow.document.service.KEMIDService; 052 import org.kuali.kfs.module.endow.document.service.KEMService; 053 import org.kuali.kfs.module.endow.document.service.PooledFundControlService; 054 import org.kuali.kfs.module.endow.document.service.UpdateAssetDecreaseDocumentTaxLotsService; 055 import org.kuali.kfs.module.endow.document.service.UpdateAssetIncreaseDocumentTaxLotsService; 056 import org.kuali.kfs.module.endow.document.validation.event.AddTransactionLineEvent; 057 import org.kuali.kfs.module.endow.util.GloabalVariablesExtractHelper; 058 import org.kuali.kfs.sys.service.ReportWriterService; 059 import org.kuali.rice.kew.exception.WorkflowException; 060 import org.kuali.rice.kns.bo.DocumentHeader; 061 import org.kuali.rice.kns.rule.event.RouteDocumentEvent; 062 import org.kuali.rice.kns.service.BusinessObjectService; 063 import org.kuali.rice.kns.service.DataDictionaryService; 064 import org.kuali.rice.kns.service.DocumentService; 065 import org.kuali.rice.kns.service.KualiConfigurationService; 066 import org.kuali.rice.kns.service.KualiRuleService; 067 import org.kuali.rice.kns.service.ParameterService; 068 import org.kuali.rice.kns.util.KualiDecimal; 069 import org.springframework.transaction.annotation.Transactional; 070 071 @Transactional 072 public class CreateCashSweepTransactionsServiceImpl implements CreateCashSweepTransactionsService { 073 074 protected static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(CreateCashSweepTransactionsServiceImpl.class); 075 private static final String SUBMIT_DOCUMENT_DESCRIPTION = "Created by Create Cash Sweep Batch Process."; 076 077 private Map<String, ReportDocumentStatistics> statistics = new HashMap<String, ReportDocumentStatistics>(); 078 private UpdateAssetIncreaseDocumentTaxLotsService updateEaiTaxLotService; 079 private UpdateAssetDecreaseDocumentTaxLotsService updateEadTaxLotService; 080 private ReportWriterService createCashSweepExceptionReportWriterService; 081 private ReportWriterService createCashSweepProcessedReportWriterService; 082 private PooledFundControlService pooledFundControlService; 083 private BusinessObjectService businessObjectService; 084 private DataDictionaryService dataDictionaryService; 085 private KualiConfigurationService configService; 086 private CashSweepModelDao cashSweepModelDao; 087 private KualiRuleService kualiRuleService; 088 private ParameterService parameterService; 089 private DocumentService documentService; 090 private KEMIDService kemidService; 091 private KEMService kemService; 092 093 /** 094 * @see org.kuali.kfs.module.endow.batch.service.CreateCashSweepTransactionsService#createCashSweepTransactions() 095 */ 096 public boolean createCashSweepTransactions() { 097 098 LOG.info("Starting \"Create Cash Sweep Transactions\" batch job..."); 099 writeHeaders(); 100 101 Collection<CashSweepModel> cashSweepModels = getCashSweepModelMatchingCurrentDate(); 102 for (CashSweepModel cashSweepModel : cashSweepModels) { 103 processIncomeSweepPurchases(cashSweepModel); 104 processIncomeSweepSales(cashSweepModel); 105 processPrincipalSweepPurchases(cashSweepModel); 106 processPrincipalSweepSale(cashSweepModel); 107 } 108 109 writeStatistics(); 110 LOG.info("Finished \"Create Cash Sweep Transactions\" batch job!"); 111 112 return true; 113 } 114 115 /** 116 * Process all the principle cash sweep models for sales. 117 * 118 * @param cashSweepModel 119 */ 120 protected boolean processPrincipalSweepSale(CashSweepModel cashSweepModel) { 121 LOG.info("Entering \"processPrincipalSweepSale\"."); 122 123 boolean success = false; 124 125 // Step 2. Get security and registration code from cash sweep. 126 String sweepRegistraionCode = cashSweepModel.getPrincipleSweepRegistrationCode(); 127 String sweepSecurityId = cashSweepModel.getPrincipleSweepInvestment(); 128 129 // Steps 4 - 12. 130 success = processAssetDecreaseDocuments(cashSweepModel.getCashSweepModelID(), sweepRegistraionCode, sweepSecurityId, cashSweepModel.getSweepPrincipleCashLimit(), false); 131 132 LOG.info("Leaving \"processPrincipalSweepSale\"."); 133 134 return success; 135 } 136 137 /** 138 * Process all the principle cash sweep models for purchases. 139 * 140 * @param cashSweepModel 141 */ 142 protected boolean processPrincipalSweepPurchases(CashSweepModel cashSweepModel) { 143 LOG.info("Entering \"processPrincipalSweepPurchases\"."); 144 145 boolean success = false; 146 147 // Step 2. Get security and registration code from cash sweep. 148 String sweepRegistraionCode = cashSweepModel.getPrincipleSweepRegistrationCode(); 149 String sweepSecurityId = cashSweepModel.getPrincipleSweepInvestment(); 150 151 // Steps 4 - 12. 152 success = processAssetIncreaseDocuments(cashSweepModel.getCashSweepModelID(), sweepRegistraionCode, sweepSecurityId, cashSweepModel.getSweepPrincipleCashLimit(), false); 153 154 LOG.info("Leaving \"processPrincipalSweepPurchases\"."); 155 156 return success; 157 } 158 159 /** 160 * Process all the income cash sweep models for sales. 161 * 162 * @param cashSweepModel 163 */ 164 protected boolean processIncomeSweepSales(CashSweepModel cashSweepModel) { 165 LOG.info("Entering \"processIncomeSweepSales\"."); 166 167 boolean success = false; 168 169 // Step 2. Get security and registration code from cash sweep. 170 String sweepRegistraionCode = cashSweepModel.getIncomeSweepRegistrationCode(); 171 String sweepSecurityId = cashSweepModel.getIncomeSweepInvestment(); 172 173 // Steps 4 - 12. 174 success = processAssetDecreaseDocuments(cashSweepModel.getCashSweepModelID(), sweepRegistraionCode, sweepSecurityId, cashSweepModel.getSweepIncomeCashLimit(), true); 175 176 LOG.info("Leaving \"processIncomeSweepSales\"."); 177 178 return success; 179 } 180 181 /** 182 * Process all the income cash sweep models for purchases. 183 * 184 * @param cashSweepModel 185 */ 186 protected boolean processIncomeSweepPurchases(CashSweepModel cashSweepModel) { 187 LOG.info("Entering \"processIncomeSweepPurchases\"."); 188 189 boolean success = false; 190 191 // Step 2. Get security and registration code from cash sweep. 192 String sweepRegistraionCode = cashSweepModel.getIncomeSweepRegistrationCode(); 193 String sweepSecurityId = cashSweepModel.getIncomeSweepInvestment(); 194 195 // Steps 4 - 12. 196 success = processAssetIncreaseDocuments(cashSweepModel.getCashSweepModelID(), sweepRegistraionCode, sweepSecurityId, cashSweepModel.getSweepIncomeCashLimit(), true); 197 198 LOG.info("Leaving \"processIncomeSweepPurchases\"."); 199 200 return success; 201 } 202 203 /** 204 * Creates asset decrease documents with transaction lines and routes it. 205 * 206 * @param cashSweepModelId 207 * @param sweepRegistraionCode 208 * @param sweepSecurityId 209 * @param cashLimit 210 * @param isIncome 211 */ 212 protected boolean processAssetDecreaseDocuments(Integer cashSweepModelId, String sweepRegistraionCode, String sweepSecurityId, BigDecimal cashLimit, boolean isIncome) { 213 214 boolean success = true; 215 AssetDecreaseDocument assetDecreaseDoc = null; 216 217 // Get the maximum number of allowed transaction lines per eDoc. 218 int maxNumberOfTransactionLines = getMaxNumberOfTransactionLines(); 219 220 // Iterate through all the KEMIDs for processing. 221 List<KEMID> kemids = new ArrayList<KEMID>(kemidService.getByCashSweepId(cashSweepModelId)); 222 for (int i = 0; i < kemids.size(); i++) { 223 224 // Get the current KEMID. 225 KEMID kemid = kemids.get(i); 226 227 // Get the current income/principle cash for this KEMID and compare it to the cash limit. 228 BigDecimal currentCash = getKemidCurrentCash(kemid, isIncome); 229 if (currentCash != null && currentCash.compareTo(BigDecimal.ZERO) < 0) { 230 231 // If this is null that means we need to create a new eDoc for 232 // the first time. 233 if (assetDecreaseDoc == null) { 234 assetDecreaseDoc = createAssetDecrease(sweepSecurityId, sweepRegistraionCode); 235 } 236 237 // Create, validate, and add the transaction line to the eDoc. 238 addTransactionLineForAssetDecrease(assetDecreaseDoc, kemid, cashLimit, currentCash, sweepSecurityId, isIncome); 239 240 // Check to see if we've reached our max number of transaction lines 241 // per eDoc. If so, validate, and submit the current eDoc and start 242 // another eDoc. 243 if (i != 0 && i % maxNumberOfTransactionLines == 0) { 244 // Validate and route the document. Then create a new doc. 245 success = routeAssetDecreaseDocument(assetDecreaseDoc, isIncome); 246 assetDecreaseDoc = createAssetDecrease(sweepSecurityId, sweepRegistraionCode); 247 } 248 } 249 } 250 251 // Verify that we don't need to do any clean-up. There could still be 252 // some let over transaction lines, less than the max amount that need 253 // to still be processed on the current eDoc. This also prevents from 254 // routing an asset decrease document with no transaction lines. 255 if (assetDecreaseDoc != null && !assetDecreaseDoc.getSourceTransactionLines().isEmpty()) { 256 // Validate and route the document. 257 success = routeAssetDecreaseDocument(assetDecreaseDoc, isIncome); 258 } 259 260 return success; 261 } 262 263 /** 264 * Creates asset increase documents with transaction lines and routes it. 265 * 266 * @param cashSweepModelId 267 * @param sweepRegistraionCode 268 * @param sweepSecurityId 269 * @param cashLimit 270 * @param isIncome 271 */ 272 protected boolean processAssetIncreaseDocuments(Integer cashSweepModelId, String sweepRegistraionCode, String sweepSecurityId, BigDecimal cashLimit, boolean isIncome) { 273 274 boolean success = true; 275 AssetIncreaseDocument assetIncreaseDoc = null; 276 277 // Get the maximum number of allowed transaction lines per eDoc. 278 int maxNumberOfTransactionLines = getMaxNumberOfTransactionLines(); 279 280 // Iterate through all the KEMIDs for processing. 281 List<KEMID> kemids = new ArrayList<KEMID>(kemidService.getByCashSweepId(cashSweepModelId)); 282 for (int i = 0; i < kemids.size(); i++) { 283 284 // Get the current KEMID. 285 KEMID kemid = kemids.get(i); 286 287 // Get the current income/principle cash for this KEMID and compare it to the cash limit. 288 BigDecimal currentCash = getKemidCurrentCash(kemid, isIncome); 289 if (currentCash != null && ((currentCash.compareTo(cashLimit) > 0 || currentCash.compareTo(cashLimit) == 0))) { 290 291 // If this is null that means we need to create a new eDoc for 292 // the first time. 293 if (assetIncreaseDoc == null) { 294 assetIncreaseDoc = createAssetIncrease(sweepSecurityId, sweepRegistraionCode); 295 } 296 297 // Create, validate, and add the transaction line to the eDoc. 298 addTransactionLineForAssetIncrease(assetIncreaseDoc, kemid, cashLimit, currentCash, sweepSecurityId, isIncome); 299 300 // Check to see if we've reached our max number of transaction lines 301 // per eDoc. If so, validate, and submit the current eDoc and start 302 // another eDoc. 303 if (i != 0 && i % maxNumberOfTransactionLines == 0) { 304 // Validate and route the document. Then create a new doc. 305 success = routeAssetIncreaseDocument(assetIncreaseDoc, isIncome); 306 assetIncreaseDoc = createAssetIncrease(sweepSecurityId, sweepRegistraionCode); 307 } 308 } 309 } 310 311 // Verify that we don't need to do any clean-up. There could still be 312 // some let over transaction lines, less than the max amount that need 313 // to still be processed on the current eDoc. This also prevents from 314 // routing an asset decrease document with no transaction lines. 315 if (assetIncreaseDoc != null && !assetIncreaseDoc.getTargetTransactionLines().isEmpty()) { 316 // Validate and route the document. 317 success = routeAssetIncreaseDocument(assetIncreaseDoc, isIncome); 318 } 319 320 return success; 321 } 322 323 /** 324 * 325 * Calculates the number of units available. 326 * 327 * @param kemid 328 * @param sweepSecurityId 329 * @return 330 */ 331 private KualiDecimal calculateAvailableKemidSecurities(KEMID kemid, String sweepSecurityId) { 332 333 BigDecimal unitsAvailable = BigDecimal.ZERO; 334 335 Map<String, String> criteria = new HashMap<String, String>(); 336 criteria.put(EndowPropertyConstants.HOLDING_TAX_LOT_KEMID, kemid.getKemid()); 337 338 List<HoldingTaxLot> holdingTaxLots = (List<HoldingTaxLot>)businessObjectService.findMatching(HoldingTaxLot.class, criteria); 339 for (HoldingTaxLot holdingTaxLot : holdingTaxLots) { 340 if(holdingTaxLot.getSecurityId().equalsIgnoreCase(sweepSecurityId)) { 341 unitsAvailable = unitsAvailable.add(holdingTaxLot.getUnits()); 342 } 343 } 344 345 return (new KualiDecimal(unitsAvailable)); 346 } 347 348 /** 349 * This method... 350 * 351 * @param assetDecreaseDoc 352 * @param kemid 353 * @param cashLimit 354 * @param currentCash 355 * @param isIncome 356 */ 357 private void addTransactionLineForAssetDecrease(AssetDecreaseDocument assetDecreaseDoc, KEMID kemid, BigDecimal cashLimit, BigDecimal currentCash, String sweepSecurityId, boolean isIncome) { 358 // Calculate the amount. 359 KualiDecimal amount = calculateCashAvailable(cashLimit, currentCash, false); 360 361 // Calculate available units and determine the transaction amount. 362 // If the units needed are equal to or less than the units held then 363 // use the calculated units. If the units needed are greater than the 364 // units held, then use the units held as the transaction amount. 365 KualiDecimal available = calculateAvailableKemidSecurities(kemid, sweepSecurityId); 366 if (amount.compareTo(available) > 0) { amount = available; } 367 368 // Create the correct transaction line based on if it's a source or target type. 369 EndowmentTransactionLine transactionLine = createTransactionLine(assetDecreaseDoc.getDocumentNumber(), kemid.getKemid(), amount, true, isIncome); 370 371 // Validate the transaction line. 372 boolean rulesPassed = kualiRuleService.applyRules(new AddTransactionLineEvent(NEW_SOURCE_TRAN_LINE_PROPERTY_NAME, assetDecreaseDoc, transactionLine)); 373 374 // If the validation passes, add the transaction line to the document; otherwise, 375 // write the error messages to the error report. 376 if (rulesPassed) { 377 assetDecreaseDoc.addSourceTransactionLine((EndowmentSourceTransactionLine) transactionLine); 378 updateEadTaxLotService.updateTransactionLineTaxLots(assetDecreaseDoc, transactionLine); 379 } 380 else { 381 writeExceptionTableRowAssetDecrease(assetDecreaseDoc, transactionLine, isIncome); 382 List<String> errorMessages = GloabalVariablesExtractHelper.extractGlobalVariableErrors(); 383 for (String errorMessage : errorMessages) { 384 writeExceptionTableReason(errorMessage); 385 } 386 } 387 } 388 389 /** 390 * This method... 391 * 392 * @param assetIncreaseDoc 393 * @param kemid 394 * @param cashLimit 395 * @param currentCash 396 * @param isIncome 397 */ 398 private void addTransactionLineForAssetIncrease(AssetIncreaseDocument assetIncreaseDoc, KEMID kemid, BigDecimal cashLimit, BigDecimal currentCash, String sweepSecurityId, boolean isIncome) { 399 // Calculate the amount. 400 KualiDecimal amount = calculateCashAvailable(cashLimit, currentCash, true); 401 402 // Create the correct transaction line based on if it's a source or target type. 403 EndowmentTransactionLine transactionLine = createTransactionLine(assetIncreaseDoc.getDocumentNumber(), kemid.getKemid(), amount, false, isIncome); 404 405 // Validate the transaction line. 406 boolean rulesPassed = kualiRuleService.applyRules(new AddTransactionLineEvent(NEW_TARGET_TRAN_LINE_PROPERTY_NAME, assetIncreaseDoc, transactionLine)); 407 408 // If the validation passes, add the transaction line to the document; otherwise, 409 // write the error messages to the error report. 410 if (rulesPassed) { 411 assetIncreaseDoc.addTargetTransactionLine((EndowmentTargetTransactionLine) transactionLine); 412 updateEaiTaxLotService.updateTransactionLineTaxLots(assetIncreaseDoc, transactionLine); 413 } 414 else { 415 writeExceptionTableRowAssetIncrease(assetIncreaseDoc, transactionLine, isIncome); 416 List<String> errorMessages = GloabalVariablesExtractHelper.extractGlobalVariableErrors(); 417 for (String errorMessage : errorMessages) { 418 writeExceptionTableReason(errorMessage); 419 } 420 } 421 } 422 423 /** 424 * Validates and routes the asset decrease document. 425 * 426 * @param assetDecreaseDoc 427 * @param balh 428 */ 429 protected boolean routeAssetDecreaseDocument(AssetDecreaseDocument assetDecreaseDoc, boolean isIncome) { 430 431 boolean success = false; 432 433 // Perform validation on the document. 434 boolean rulesPassed = kualiRuleService.applyRules(new RouteDocumentEvent(assetDecreaseDoc)); 435 436 // If the document passed validations, route it accordingly. 437 if (rulesPassed) { 438 boolean noRouteIndicator = getNoRouteIndicator(true); 439 try { 440 assetDecreaseDoc.setNoRouteIndicator(noRouteIndicator); 441 documentService.routeDocument(assetDecreaseDoc, SUBMIT_DOCUMENT_DESCRIPTION, null); 442 writeProcessedTableRowAssetDecrease(assetDecreaseDoc, isIncome); 443 success = true; 444 } 445 catch (WorkflowException we) { 446 writeExceptionTableReason(assetDecreaseDoc.getDocumentNumber() + " - " + we.getLocalizedMessage()); 447 LOG.error(we.getLocalizedMessage()); 448 } 449 } 450 else { 451 // Write the errors to the exception file. 452 List<String> errorMessages = GloabalVariablesExtractHelper.extractGlobalVariableErrors(); 453 writeExceptionTableRowAssetDecrease(assetDecreaseDoc, null, isIncome); 454 for (String errorMessage : errorMessages) { 455 writeExceptionTableReason(errorMessage); 456 } 457 // Try to save the document if it fails validation. 458 try { 459 documentService.saveDocument(assetDecreaseDoc); 460 } 461 catch (WorkflowException we) { 462 writeExceptionTableReason(assetDecreaseDoc.getDocumentNumber() + " - " + we.getLocalizedMessage()); 463 LOG.error(we.getLocalizedMessage()); 464 } 465 } 466 467 return success; 468 } 469 470 /** 471 * Validates and routes the asset increase document. 472 * 473 * @param assetIncreaseDoc 474 */ 475 protected boolean routeAssetIncreaseDocument(AssetIncreaseDocument assetIncreaseDoc, boolean isIncome) { 476 477 boolean success = false; 478 479 // Perform validation on the document. 480 boolean rulesPassed = kualiRuleService.applyRules(new RouteDocumentEvent(assetIncreaseDoc)); 481 482 // If the document passed validations, route it accordingly. 483 if (rulesPassed) { 484 boolean noRouteIndicator = getNoRouteIndicator(false); 485 try { 486 assetIncreaseDoc.setNoRouteIndicator(noRouteIndicator); 487 documentService.routeDocument(assetIncreaseDoc, SUBMIT_DOCUMENT_DESCRIPTION, null); 488 writeProcessedTableRowAssetIncrease(assetIncreaseDoc, isIncome); 489 success = true; 490 } 491 catch (WorkflowException we) { 492 writeExceptionTableReason(assetIncreaseDoc.getDocumentNumber() + " - " + we.getLocalizedMessage()); 493 LOG.error(we.getLocalizedMessage()); 494 } 495 } 496 else { 497 // Write the errors to the exception file. 498 List<String> errorMessages = GloabalVariablesExtractHelper.extractGlobalVariableErrors(); 499 writeExceptionTableRowAssetIncrease(assetIncreaseDoc, null, isIncome); 500 for (String errorMessage : errorMessages) { 501 writeExceptionTableReason(errorMessage); 502 } 503 // Try to save the document if it fails validation. 504 try { 505 documentService.saveDocument(assetIncreaseDoc); 506 } 507 catch (WorkflowException we) { 508 writeExceptionTableReason(assetIncreaseDoc.getDocumentNumber() + " - " + we.getLocalizedMessage()); 509 LOG.error(we.getLocalizedMessage()); 510 } 511 } 512 513 return success; 514 } 515 516 /** 517 * Creates and returns a new principle or income transaction line. 518 * 519 * @param docNumber 520 * @param kemid 521 * @param amount 522 * @param isSource 523 * @return 524 */ 525 private EndowmentTransactionLine createTransactionLine(String docNumber, String kemid, KualiDecimal amount, boolean isSource, boolean isIncome) { 526 527 EndowmentTransactionLine transactionLine = null; 528 if (isSource) { 529 transactionLine = new EndowmentSourceTransactionLine(); 530 } 531 else { 532 transactionLine = new EndowmentTargetTransactionLine(); 533 } 534 535 // Set values on the transaction line. 536 transactionLine.setDocumentNumber(docNumber); 537 transactionLine.setKemid(kemid); 538 if (!isIncome) { 539 transactionLine.setTransactionIPIndicatorCode(EndowConstants.IncomePrincipalIndicator.PRINCIPAL); 540 } 541 else { 542 transactionLine.setTransactionIPIndicatorCode(EndowConstants.IncomePrincipalIndicator.INCOME); 543 } 544 // These should be whole numbers. 545 transactionLine.setTransactionAmount(amount); 546 transactionLine.setTransactionUnits(amount); 547 548 return transactionLine; 549 } 550 551 /** 552 * Creates and returns a new asset increase document. 553 * 554 * @param securityId 555 * @param registrationCode 556 * @return 557 */ 558 private AssetIncreaseDocument createAssetIncrease(String securityId, String registrationCode) { 559 560 AssetIncreaseDocument assetIncrease = null; 561 try { 562 assetIncrease = (AssetIncreaseDocument) documentService.getNewDocument(AssetIncreaseDocument.class); 563 564 // Set the document description. 565 DocumentHeader docHeader = assetIncrease.getDocumentHeader(); 566 docHeader.setDocumentDescription(getPurchaseDescription()); 567 assetIncrease.setDocumentHeader(docHeader); 568 569 // Set the sub type code to cash. 570 assetIncrease.setTransactionSubTypeCode(EndowConstants.TransactionSubTypeCode.CASH); 571 572 // Create and set the target security transaction line. 573 EndowmentTargetTransactionSecurity targetTransactionSecurity = new EndowmentTargetTransactionSecurity(); 574 targetTransactionSecurity.setRegistrationCode(registrationCode); 575 targetTransactionSecurity.setSecurityID(securityId); 576 577 assetIncrease.setTargetTransactionSecurity(targetTransactionSecurity); 578 } 579 catch (WorkflowException ex) { 580 writeExceptionTableReason("Workflow error while trying to create EAI document: " + ex.getLocalizedMessage()); 581 LOG.error(ex.getLocalizedMessage()); 582 } 583 584 return assetIncrease; 585 } 586 587 /** 588 * Creates and returns a new asset decrease document. 589 * 590 * @param securityId 591 * @param registrationCode 592 * @return 593 */ 594 private AssetDecreaseDocument createAssetDecrease(String securityId, String registrationCode) { 595 596 AssetDecreaseDocument assetDecrease = null; 597 try { 598 assetDecrease = (AssetDecreaseDocument) documentService.getNewDocument(AssetDecreaseDocument.class); 599 600 // Set the document description. 601 DocumentHeader docHeader = assetDecrease.getDocumentHeader(); 602 docHeader.setDocumentDescription(getSaleDescription()); 603 assetDecrease.setDocumentHeader(docHeader); 604 605 // Set the sub type code to cash. 606 assetDecrease.setTransactionSubTypeCode(EndowConstants.TransactionSubTypeCode.CASH); 607 608 // Create and set the source security transaction line. 609 EndowmentSourceTransactionSecurity sourceTransactionSecurity = new EndowmentSourceTransactionSecurity(); 610 sourceTransactionSecurity.setRegistrationCode(registrationCode); 611 sourceTransactionSecurity.setSecurityID(securityId); 612 613 assetDecrease.setSourceTransactionSecurity(sourceTransactionSecurity); 614 615 } 616 catch (WorkflowException ex) { 617 writeExceptionTableReason("Workflow error while trying to create EAD document: " + ex.getLocalizedMessage()); 618 LOG.error(ex.getLocalizedMessage()); 619 } 620 621 return assetDecrease; 622 } 623 624 /** 625 * Gets the current principle or income cash for the specified KEMID. 626 * 627 * @param kemid 628 * @return 629 */ 630 private BigDecimal getKemidCurrentCash(KEMID kemid, boolean isIncome) { 631 KemidCurrentCash kemidCurrentCash = businessObjectService.findBySinglePrimaryKey(KemidCurrentCash.class, kemid.getKemid()); 632 633 if (kemidCurrentCash == null) { 634 writeExceptionTableReason("Unable to calculate current cash amount: END_CRNT_CSH_T is \'null\' for KEMID \'" + kemid.getKemid() + "\'"); 635 return null; 636 } 637 638 return !isIncome ? kemidCurrentCash.getCurrentPrincipalCash().bigDecimalValue() : kemidCurrentCash.getCurrentIncomeCash().bigDecimalValue(); 639 } 640 641 /** 642 * Determines the cash available. 643 * 644 * @param cashLimit 645 * @param currentCash 646 * @return 647 */ 648 private KualiDecimal calculateCashAvailable(BigDecimal cashLimit, BigDecimal currentCash, boolean isIncrease) { 649 650 BigDecimal amount = null; 651 652 // Subtract the current amount from the limit amount, and round up. 653 if (isIncrease) { 654 amount = currentCash.subtract(cashLimit); 655 amount = amount.setScale(0, BigDecimal.ROUND_UP); 656 } 657 // Take the absolute value of the current cash, add limit amount, 658 // and round down. 659 else { 660 amount = (currentCash.abs()).add(cashLimit); 661 amount = amount.setScale(0, BigDecimal.ROUND_DOWN); 662 } 663 664 return new KualiDecimal(amount); 665 } 666 667 /** 668 * This method retrieves all the cash sweep models whose frequency code matches the current date. 669 * 670 * @return Collection of CashSweepModel business objects 671 */ 672 private Collection<CashSweepModel> getCashSweepModelMatchingCurrentDate() { 673 return cashSweepModelDao.getCashSweepModelWithNextPayDateEqualToCurrentDate(kemService.getCurrentDate()); 674 } 675 676 /** 677 * Gets the appropriate approval indicator based on if it's for a sale or purchase type. 678 * 679 * @param isSale 680 * @return 681 */ 682 private boolean getNoRouteIndicator(boolean isSale) { 683 boolean approvalIndicator = isSale ? getSaleNoRouteIndicator() : getPurchaseNoRouteIndicator(); 684 return approvalIndicator; 685 } 686 687 /** 688 * This method returns the true or false value of the purchase no route indicator. 689 * 690 * @return 691 */ 692 private boolean getPurchaseNoRouteIndicator() { 693 String noRouteIndicator = parameterService.getParameterValue(CreateCashSweepTransactionsStep.class, EndowParameterKeyConstants.PURCHASE_NO_ROUTE_IND); 694 return (EndowConstants.YES.equalsIgnoreCase(noRouteIndicator) ? true : false); 695 } 696 697 /** 698 * This method returns the true or false value of the sale no route indicator. 699 * 700 * @return 701 */ 702 private boolean getSaleNoRouteIndicator() { 703 String noRouteIndicator = parameterService.getParameterValue(CreateCashSweepTransactionsStep.class, EndowParameterKeyConstants.SALE_NO_ROUTE_IND); 704 return (EndowConstants.YES.equalsIgnoreCase(noRouteIndicator) ? true : false); 705 } 706 707 /** 708 * Gets the purchase description parameter. 709 * 710 * @return 711 */ 712 private String getPurchaseDescription() { 713 return parameterService.getParameterValue(CreateCashSweepTransactionsStep.class, EndowParameterKeyConstants.PURCHASE_DESCRIPTION); 714 } 715 716 /** 717 * Gets the sale description parameter. 718 * 719 * @return 720 */ 721 private String getSaleDescription() { 722 return parameterService.getParameterValue(CreateCashSweepTransactionsStep.class, EndowParameterKeyConstants.SALE_DESCRIPTION); 723 } 724 725 /** 726 * This method... 727 * 728 * @return 729 */ 730 private int getMaxNumberOfTransactionLines() { 731 return kemService.getMaxNumberOfTransactionLinesPerDocument(); 732 } 733 734 /** 735 * This method... 736 * 737 * @param assetIncreaseDoc 738 * @param isIncome 739 */ 740 private void writeProcessedTableRowAssetIncrease(AssetIncreaseDocument assetIncreaseDoc, boolean isIncome) { 741 742 TransactionDocumentTotalReportLine createCashSweepProcessedReportValues = new TransactionDocumentTotalReportLine(EndowConstants.DocumentTypeNames.ENDOWMENT_ASSET_INCREASE, assetIncreaseDoc.getDocumentNumber(), assetIncreaseDoc.getTargetTransactionSecurity().getSecurityID()); 743 744 List<EndowmentTransactionLine> transLines = assetIncreaseDoc.getTargetTransactionLines(); 745 for (EndowmentTransactionLine tranLine : transLines) { 746 if (isIncome) { 747 createCashSweepProcessedReportValues.addIncomeAmount(tranLine.getTransactionAmount()); 748 createCashSweepProcessedReportValues.addIncomeUnits(tranLine.getTransactionUnits()); 749 } 750 else { 751 createCashSweepProcessedReportValues.addPrincipalAmount(tranLine.getTransactionAmount()); 752 createCashSweepProcessedReportValues.addPrincipalUnits(tranLine.getTransactionUnits()); 753 } 754 } 755 updatePostingStats(assetIncreaseDoc); 756 757 createCashSweepProcessedReportWriterService.writeTableRow(createCashSweepProcessedReportValues); 758 createCashSweepProcessedReportWriterService.writeNewLines(1); 759 } 760 761 /** 762 * This method... 763 * 764 * @param assetDecreaseDoc 765 * @param isIncome 766 */ 767 private void writeProcessedTableRowAssetDecrease(AssetDecreaseDocument assetDecreaseDoc, boolean isIncome) { 768 769 TransactionDocumentTotalReportLine createCashSweepProcessedReportValues = new TransactionDocumentTotalReportLine(EndowConstants.DocumentTypeNames.ENDOWMENT_ASSET_DECREASE, assetDecreaseDoc.getDocumentNumber(), assetDecreaseDoc.getTargetTransactionSecurity().getSecurityID()); 770 771 List<EndowmentTransactionLine> transLines = assetDecreaseDoc.getSourceTransactionLines(); 772 for (EndowmentTransactionLine tranLine : transLines) { 773 if (isIncome) { 774 createCashSweepProcessedReportValues.addIncomeAmount(tranLine.getTransactionAmount()); 775 createCashSweepProcessedReportValues.addIncomeUnits(tranLine.getTransactionUnits()); 776 } 777 else { 778 createCashSweepProcessedReportValues.addPrincipalAmount(tranLine.getTransactionAmount()); 779 createCashSweepProcessedReportValues.addPrincipalUnits(tranLine.getTransactionUnits()); 780 } 781 } 782 updatePostingStats(assetDecreaseDoc); 783 784 createCashSweepProcessedReportWriterService.writeTableRow(createCashSweepProcessedReportValues); 785 createCashSweepProcessedReportWriterService.writeNewLines(1); 786 } 787 788 /** 789 * This method... 790 * 791 * @param assetIncreaseDoc 792 * @param tranLine 793 * @param isIncome 794 */ 795 private void writeExceptionTableRowAssetIncrease(AssetIncreaseDocument assetIncreaseDoc, EndowmentTransactionLine tranLine, boolean isIncome) { 796 797 TransactionDocumentExceptionReportLine createCashSweepExceptionReportValues = new TransactionDocumentExceptionReportLine(EndowConstants.DocumentTypeNames.ENDOWMENT_ASSET_INCREASE, assetIncreaseDoc.getDocumentNumber(), assetIncreaseDoc.getTargetTransactionSecurity().getSecurityID()); 798 799 if (tranLine != null) { 800 createCashSweepExceptionReportValues.setKemid(tranLine.getKemid()); 801 if (isIncome) { 802 createCashSweepExceptionReportValues.addIncomeAmount(tranLine.getTransactionAmount()); 803 createCashSweepExceptionReportValues.addIncomeUnits(tranLine.getTransactionUnits()); 804 } 805 else { 806 createCashSweepExceptionReportValues.addPrincipalAmount(tranLine.getTransactionAmount()); 807 createCashSweepExceptionReportValues.addPrincipalUnits(tranLine.getTransactionUnits()); 808 } 809 } 810 updateErrorStats(assetIncreaseDoc); 811 812 createCashSweepExceptionReportWriterService.writeTableRow(createCashSweepExceptionReportValues); 813 createCashSweepExceptionReportWriterService.writeNewLines(1); 814 } 815 816 /** 817 * This method... 818 * 819 * @param assetDecreaseDoc 820 * @param tranLine 821 * @param isIncome 822 */ 823 private void writeExceptionTableRowAssetDecrease(AssetDecreaseDocument assetDecreaseDoc, EndowmentTransactionLine tranLine, boolean isIncome) { 824 825 TransactionDocumentExceptionReportLine createCashSweepExceptionReportValues = new TransactionDocumentExceptionReportLine(EndowConstants.DocumentTypeNames.ENDOWMENT_ASSET_DECREASE, assetDecreaseDoc.getDocumentNumber(), assetDecreaseDoc.getTargetTransactionSecurity().getSecurityID()); 826 827 if (tranLine != null) { 828 createCashSweepExceptionReportValues.setKemid(tranLine.getKemid()); 829 if (isIncome) { 830 createCashSweepExceptionReportValues.addIncomeAmount(tranLine.getTransactionAmount()); 831 createCashSweepExceptionReportValues.addIncomeUnits(tranLine.getTransactionUnits()); 832 } 833 else { 834 createCashSweepExceptionReportValues.addPrincipalAmount(tranLine.getTransactionAmount()); 835 createCashSweepExceptionReportValues.addPrincipalUnits(tranLine.getTransactionUnits()); 836 } 837 } 838 updateErrorStats(assetDecreaseDoc); 839 840 createCashSweepExceptionReportWriterService.writeTableRow(createCashSweepExceptionReportValues); 841 createCashSweepExceptionReportWriterService.writeNewLines(1); 842 } 843 844 /** 845 * Writes the reason row and inserts a blank line. 846 * 847 * @param reasonMessage 848 */ 849 private void writeExceptionTableReason(String reasonMessage) { 850 EndowmentExceptionReportHeader createCashSweepExceptionReportReason = new EndowmentExceptionReportHeader(); 851 createCashSweepExceptionReportReason.setColumnHeading1("Reason: "); 852 createCashSweepExceptionReportReason.setColumnHeading2(reasonMessage); 853 854 createCashSweepExceptionReportWriterService.writeTableRow(createCashSweepExceptionReportReason); 855 createCashSweepExceptionReportWriterService.writeNewLines(1); 856 } 857 858 /** 859 * Initialize the report document headers. 860 */ 861 private void writeHeaders() { 862 createCashSweepExceptionReportWriterService.writeNewLines(1); 863 createCashSweepProcessedReportWriterService.writeNewLines(1); 864 createCashSweepExceptionReportWriterService.writeTableHeader(new TransactionDocumentExceptionReportLine()); 865 createCashSweepProcessedReportWriterService.writeTableHeader(new TransactionDocumentTotalReportLine()); 866 } 867 868 /** 869 * This method... 870 * 871 * @param assetDocument 872 */ 873 private void updatePostingStats(EndowmentTaxLotLinesDocumentBase assetDocument) { 874 875 String documentTypeName = dataDictionaryService.getDocumentTypeNameByClass(assetDocument.getClass()); 876 ReportDocumentStatistics stats = statistics.get(documentTypeName); 877 878 // If null that means there isn't one in the map, so create it and add 879 // it to the map. 880 if (stats == null) { 881 stats = new ReportDocumentStatistics(documentTypeName); 882 statistics.put(documentTypeName, stats); 883 } 884 stats.addNumberOfSourceTransactionLines(assetDocument.getSourceTransactionLines().size()); 885 stats.addNumberOfTargetTransactionLines(assetDocument.getTargetTransactionLines().size()); 886 887 stats.incrementNumberOfDocuments(); 888 } 889 890 /** 891 * This method... 892 * 893 * @param assetDocument 894 */ 895 private void updateErrorStats(EndowmentTaxLotLinesDocumentBase assetDocument) { 896 897 String documentTypeName = dataDictionaryService.getDocumentTypeNameByClass(assetDocument.getClass()); 898 ReportDocumentStatistics stats = statistics.get(documentTypeName); 899 900 // If null that means there isn't one in the map, so create it and add 901 // it to the map. 902 if (stats == null) { 903 stats = new ReportDocumentStatistics(documentTypeName); 904 statistics.put(documentTypeName, stats); 905 } 906 907 stats.incrementNumberOfErrors(); 908 } 909 910 /** 911 * Write out the statistics. 912 */ 913 private void writeStatistics() { 914 915 for (Map.Entry<String, ReportDocumentStatistics> entry : statistics.entrySet()) { 916 917 ReportDocumentStatistics stats = entry.getValue(); 918 919 createCashSweepProcessedReportWriterService.writeStatisticLine("%s Documents:", stats.getDocumentTypeName()); 920 createCashSweepProcessedReportWriterService.writeStatisticLine(" Number of Documents Generated: %d", stats.getNumberOfDocuments()); 921 createCashSweepProcessedReportWriterService.writeStatisticLine(" Number of Transaction Lines Generated: %d", stats.getTotalNumberOfTransactionLines()); 922 createCashSweepProcessedReportWriterService.writeStatisticLine(" Number of Error Records Written: %d", stats.getNumberOfErrors()); 923 createCashSweepProcessedReportWriterService.writeStatisticLine("", ""); 924 925 createCashSweepExceptionReportWriterService.writeStatisticLine("%s Documents:", stats.getDocumentTypeName()); 926 createCashSweepExceptionReportWriterService.writeStatisticLine(" Number of Documents Generated: %d", stats.getNumberOfDocuments()); 927 createCashSweepExceptionReportWriterService.writeStatisticLine(" Number of Transaction Lines Generated: %d", stats.getTotalNumberOfTransactionLines()); 928 createCashSweepExceptionReportWriterService.writeStatisticLine(" Number of Error Records Written: %d", stats.getNumberOfErrors()); 929 createCashSweepExceptionReportWriterService.writeStatisticLine("", ""); 930 } 931 932 } 933 934 /** 935 * Sets the businessObjectService attribute value. 936 * 937 * @param businessObjectService The businessObjectService to set. 938 */ 939 public void setBusinessObjectService(BusinessObjectService businessObjectService) { 940 this.businessObjectService = businessObjectService; 941 } 942 943 /** 944 * Sets the kemService attribute value. 945 * 946 * @param kemService The kemService to set. 947 */ 948 public void setKemService(KEMService kemService) { 949 this.kemService = kemService; 950 } 951 952 /** 953 * Sets the kemidService attribute value. 954 * 955 * @param kemidService The kemidService to set. 956 */ 957 public void setKemidService(KEMIDService kemidService) { 958 this.kemidService = kemidService; 959 } 960 961 /** 962 * Sets the documentService attribute value. 963 * 964 * @param documentService The documentService to set. 965 */ 966 public void setDocumentService(DocumentService documentService) { 967 this.documentService = documentService; 968 } 969 970 /** 971 * Sets the kualiRuleService attribute value. 972 * 973 * @param kualiRuleService The kualiRuleService to set. 974 */ 975 public void setKualiRuleService(KualiRuleService kualiRuleService) { 976 this.kualiRuleService = kualiRuleService; 977 } 978 979 /** 980 * Sets the parameterService attribute value. 981 * 982 * @param parameterService The parameterService to set. 983 */ 984 public void setParameterService(ParameterService parameterService) { 985 this.parameterService = parameterService; 986 } 987 988 /** 989 * Sets the createCashSweepExceptionReportWriterService attribute value. 990 * 991 * @param createCashSweepExceptionReportWriterService The createCashSweepExceptionReportWriterService to set. 992 */ 993 public void setCreateCashSweepExceptionReportWriterService(ReportWriterService createCashSweepExceptionReportWriterService) { 994 this.createCashSweepExceptionReportWriterService = createCashSweepExceptionReportWriterService; 995 } 996 997 /** 998 * Sets the createCashSweepProcessedReportWriterService attribute value. 999 * 1000 * @param createCashSweepProcessedReportWriterService The createCashSweepProcessedReportWriterService to set. 1001 */ 1002 public void setCreateCashSweepProcessedReportWriterService(ReportWriterService createCashSweepProcessedReportWriterService) { 1003 this.createCashSweepProcessedReportWriterService = createCashSweepProcessedReportWriterService; 1004 } 1005 1006 /** 1007 * Sets the configService attribute value. 1008 * 1009 * @param configService The configService to set. 1010 */ 1011 public void setConfigService(KualiConfigurationService configService) { 1012 this.configService = configService; 1013 } 1014 1015 /** 1016 * Sets the cashSweepModelDao attribute value. 1017 * 1018 * @param cashSweepModelDao The cashSweepModelDao to set. 1019 */ 1020 public void setCashSweepModelDao(CashSweepModelDao cashSweepModelDao) { 1021 this.cashSweepModelDao = cashSweepModelDao; 1022 } 1023 1024 /** 1025 * Sets the dataDictionaryService attribute value. 1026 * 1027 * @param dataDictionaryService The dataDictionaryService to set. 1028 */ 1029 public void setDataDictionaryService(DataDictionaryService dataDictionaryService) { 1030 this.dataDictionaryService = dataDictionaryService; 1031 } 1032 1033 /** 1034 * Sets the updateEaiTaxLotService attribute value. 1035 * 1036 * @param updateEaiTaxLotService The updateEaiTaxLotService to set. 1037 */ 1038 public void setUpdateEaiTaxLotService(UpdateAssetIncreaseDocumentTaxLotsService updateEaiTaxLotService) { 1039 this.updateEaiTaxLotService = updateEaiTaxLotService; 1040 } 1041 1042 /** 1043 * Sets the updateEadTaxLotService attribute value. 1044 * 1045 * @param updateEadTaxLotService The updateEadTaxLotService to set. 1046 */ 1047 public void setUpdateEadTaxLotService(UpdateAssetDecreaseDocumentTaxLotsService updateEadTaxLotService) { 1048 this.updateEadTaxLotService = updateEadTaxLotService; 1049 } 1050 1051 }