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.math.BigDecimal; 019 import java.sql.Date; 020 import java.util.ArrayList; 021 import java.util.HashMap; 022 import java.util.List; 023 import java.util.Map; 024 025 import org.kuali.kfs.module.endow.EndowConstants; 026 import org.kuali.kfs.module.endow.EndowParameterKeyConstants; 027 import org.kuali.kfs.module.endow.EndowPropertyConstants; 028 import org.kuali.kfs.module.endow.batch.IncomeDistributionForPooledFundStep; 029 import org.kuali.kfs.module.endow.batch.service.IncomeDistributionForPooledFundService; 030 import org.kuali.kfs.module.endow.businessobject.EndowmentSourceTransactionLine; 031 import org.kuali.kfs.module.endow.businessobject.EndowmentTargetTransactionLine; 032 import org.kuali.kfs.module.endow.businessobject.EndowmentTransactionLineBase; 033 import org.kuali.kfs.module.endow.businessobject.HoldingTaxLot; 034 import org.kuali.kfs.module.endow.businessobject.KemidPayoutInstruction; 035 import org.kuali.kfs.module.endow.businessobject.PooledFundValue; 036 import org.kuali.kfs.module.endow.businessobject.TransactionDocumentExceptionReportLine; 037 import org.kuali.kfs.module.endow.businessobject.TransactionDocumentTotalReportLine; 038 import org.kuali.kfs.module.endow.dataaccess.IncomeDistributionForPooledFundDao; 039 import org.kuali.kfs.module.endow.document.CashIncreaseDocument; 040 import org.kuali.kfs.module.endow.document.CashTransferDocument; 041 import org.kuali.kfs.module.endow.document.EndowmentSecurityDetailsDocumentBase; 042 import org.kuali.kfs.module.endow.document.service.HoldingTaxLotService; 043 import org.kuali.kfs.module.endow.document.service.KEMService; 044 import org.kuali.kfs.module.endow.document.service.PooledFundValueService; 045 import org.kuali.kfs.module.endow.document.validation.event.AddTransactionLineEvent; 046 import org.kuali.kfs.module.endow.util.GloabalVariablesExtractHelper; 047 import org.kuali.kfs.sys.context.SpringContext; 048 import org.kuali.kfs.sys.service.ReportWriterService; 049 import org.kuali.rice.kew.exception.WorkflowException; 050 import org.kuali.rice.kns.rule.event.RouteDocumentEvent; 051 import org.kuali.rice.kns.service.BusinessObjectService; 052 import org.kuali.rice.kns.service.DocumentService; 053 import org.kuali.rice.kns.service.KualiRuleService; 054 import org.kuali.rice.kns.service.ParameterService; 055 import org.kuali.rice.kns.service.TransactionalDocumentDictionaryService; 056 import org.kuali.rice.kns.util.GlobalVariables; 057 import org.kuali.rice.kns.util.KualiDecimal; 058 import org.kuali.rice.kns.util.ObjectUtils; 059 import org.springframework.transaction.annotation.Transactional; 060 061 @Transactional 062 public class IncomeDistributionForPooledFundServiceImpl implements IncomeDistributionForPooledFundService { 063 064 protected static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(IncomeDistributionForPooledFundServiceImpl.class); 065 066 protected BusinessObjectService businessObjectService; 067 protected DocumentService documentService; 068 protected ParameterService parameterService; 069 protected KualiRuleService kualiRuleService; 070 071 protected KEMService kemService; 072 protected HoldingTaxLotService holdingTaxLotService; 073 protected PooledFundValueService pooledFundValueService; 074 075 protected IncomeDistributionForPooledFundDao incomeDistributionForPooledFundDao; 076 077 protected ReportWriterService incomeDistributionForPooledFundExceptionReportWriterService; 078 protected ReportWriterService incomeDistributionForPooledFundTotalReportWriterService; 079 080 private TransactionDocumentTotalReportLine totalReportLine = null; 081 private TransactionDocumentExceptionReportLine exceptionReportLine = null; 082 083 /** 084 * This batch creates pooled fund distribution transactions 085 * 086 * @see org.kuali.kfs.module.endow.batch.service.IncomeDistributionForPooledFundService#createIncomeDistributionForPooledFund() 087 */ 088 public boolean createIncomeDistributionForPooledFund() { 089 090 LOG.info("Beginning the Income Distribution for Pooled Fund Transactions batch ..."); 091 092 // get the list of PooledFundValue with distribute income on date == the current date && income distribution complete == 093 // 'N'&& the most recent value effective date 094 List<PooledFundValue> pooledFundValueList = incomeDistributionForPooledFundDao.getPooledFundValueForIncomeDistribution(kemService.getCurrentDate()); 095 if (pooledFundValueList == null || pooledFundValueList.isEmpty()) { 096 // none exists so end the process 097 return true; 098 } 099 100 // group by security id 101 for (PooledFundValue pooledFundValue : pooledFundValueList) { 102 // get all tax lots with security id equal to pooledSecurityId with holding units > 0 103 List<HoldingTaxLot> holdingTaxLotList = holdingTaxLotService.getTaxLotsPerSecurityIDWithUnitsGreaterThanZero(pooledFundValue.getPooledSecurityID()); 104 105 // group by registration code 106 if (holdingTaxLotList != null) { 107 // create map <registration code, List<HoldingTaxLot>> 108 Map<String, List<HoldingTaxLot>> registrationCodeMap = new HashMap<String, List<HoldingTaxLot>>(); 109 110 for (HoldingTaxLot holdingTaxLot : holdingTaxLotList) { 111 String registrationCode = holdingTaxLot.getRegistrationCode(); 112 if (registrationCodeMap.containsKey(registrationCode)) { 113 registrationCodeMap.get(registrationCode).add(holdingTaxLot); 114 } 115 else { 116 List<HoldingTaxLot> taxLots = new ArrayList<HoldingTaxLot>(); 117 taxLots.add(holdingTaxLot); 118 registrationCodeMap.put(registrationCode, taxLots); 119 } 120 } 121 122 // initialize report lines for ECI; needs to fill details including documentId 123 initializeReports(EndowConstants.DocumentTypeNames.ENDOWMENT_CASH_INCREASE); 124 125 // generate a new ECI document per security id and registration code 126 for (String registrationCode : registrationCodeMap.keySet()) { 127 List<HoldingTaxLot> holdingTaxLotsByRegCode = registrationCodeMap.get(registrationCode); 128 if (holdingTaxLotsByRegCode != null) { 129 createECI(pooledFundValue.getPooledSecurityID(), pooledFundValue.getValueEffectiveDate(), registrationCode, holdingTaxLotsByRegCode); 130 } 131 } 132 } 133 } 134 135 // set incomeDistributionComplete to 'Y' and save 136 pooledFundValueService.setIncomeDistributionCompleted(pooledFundValueList, true); 137 138 // TODO: write the sub total and grand total if necessary 139 140 LOG.info("The Income Distribution for Pooled Fund Transactions Batch Job was finished."); 141 142 return true; 143 } 144 145 /** 146 * Creates an ECI per security id and registration code 147 * 148 * @param securityId 149 * @param registrationCode 150 * @param holdingTaxLotList 151 */ 152 protected boolean createECI(String securityId, Date effectiveDate, String registrationCode, List<HoldingTaxLot> holdingTaxLotList) { 153 154 LOG.info("Creating ECI ..."); 155 156 boolean result = true; 157 158 // set security id for reports 159 totalReportLine.setSecurityId(securityId); 160 exceptionReportLine.setSecurityId(securityId); 161 162 // initialize ECT list, which will be used while adding ECI transaction lines 163 // this must be submitted after the ECI is submitted successfully 164 List<CashTransferDocument> cashTransferDocumentList = new ArrayList<CashTransferDocument>(); 165 166 // initialize CashIncreaseDocument 167 CashIncreaseDocument cashIncreaseDocument = initializeCashDocument(EndowConstants.DocumentTypeNames.ENDOWMENT_CASH_INCREASE, EndowConstants.MAXMUM_NUMBER_OF_EDOC_INITIALIZATION_TRY); 168 if (ObjectUtils.isNull(cashIncreaseDocument)) { 169 return false; 170 } 171 else { 172 totalReportLine.setDocumentId(cashIncreaseDocument.getDocumentNumber()); 173 exceptionReportLine.setDocumentId(cashIncreaseDocument.getDocumentNumber()); 174 } 175 176 // add the doc description and security 177 cashIncreaseDocument.getDocumentHeader().setDocumentDescription(parameterService.getParameterValue(IncomeDistributionForPooledFundStep.class, EndowParameterKeyConstants.INCOME_DESCRIPTION)); 178 cashIncreaseDocument.setTransactionSourceTypeCode(EndowConstants.TransactionSourceTypeCode.AUTOMATED); 179 addSecurityDetailToECI(cashIncreaseDocument, EndowConstants.TRANSACTION_LINE_TYPE_TARGET, securityId, registrationCode); 180 181 // add transaction lines 182 addTransactionLinesToECI(cashIncreaseDocument, cashTransferDocumentList, holdingTaxLotList, effectiveDate); 183 184 // validate ECI first and then submit it 185 GlobalVariables.clear(); 186 if (validateECI(cashIncreaseDocument)) { 187 submitCashDocument(cashIncreaseDocument, EndowConstants.DocumentTypeNames.ENDOWMENT_CASH_INCREASE, EndowParameterKeyConstants.INCOME_NO_ROUTE_IND); 188 189 // and then validate and submit ECT 190 if (cashTransferDocumentList != null) { 191 for (CashTransferDocument cashTransferDocument : cashTransferDocumentList) { 192 GlobalVariables.clear(); // in case 193 if (validateECT(cashTransferDocument)) { 194 submitCashDocument(cashTransferDocument, EndowConstants.DocumentTypeNames.ENDOWMENT_CASH_TRANSFER, EndowParameterKeyConstants.INCOME_TRANSFER_NO_ROUTE_IND); 195 } 196 else { 197 writeValidationErrorReason(); 198 LOG.error("Failed to validate ECT: Document # " + cashTransferDocument.getDocumentNumber()); 199 result = false; 200 } 201 } 202 } 203 204 // write the total report 205 incomeDistributionForPooledFundTotalReportWriterService.writeTableRow(totalReportLine); 206 207 // TODO: prepare for sub total by security id and grand total if necessary 208 209 } 210 else { 211 writeValidationErrorReason(); 212 LOG.error("Failed to validate ECI: Document # " + cashIncreaseDocument.getDocumentNumber()); 213 result = false; 214 } 215 216 return result; 217 } 218 219 /** 220 * Adds transaction lines and create ECT if necessary 221 * 222 * @param cashIncreaseDocument 223 * @param cashTransferDocumentList 224 * @param holdingTaxLotList 225 */ 226 protected void addTransactionLinesToECI(CashIncreaseDocument cashIncreaseDocument, List<CashTransferDocument> cashTransferDocumentList, List<HoldingTaxLot> holdingTaxLotList, Date effectiveDate) { 227 228 // create a kemid map <kemid, map<incomePrincipalIndicator, holdingTaxLots>> in preparation for adding transaction lines 229 Map<String, Map<String, List<HoldingTaxLot>>> kemidMap = new HashMap<String, Map<String, List<HoldingTaxLot>>>(); 230 231 // group by kemid and incomePrincipalIndicator 232 groupHoldingTaxLot(holdingTaxLotList, kemidMap); 233 234 // add transaction lines per kemid and incomePrincipalIndicator 235 for (String kemid : kemidMap.keySet()) { 236 for (String incomePrincipalIndicator : kemidMap.get(kemid).keySet()) { 237 List<HoldingTaxLot> holdingTaxLotGroupedByIPInd = kemidMap.get(kemid).get(incomePrincipalIndicator); 238 KualiDecimal transactionAmount = getTransactionAmount(holdingTaxLotGroupedByIPInd, effectiveDate); 239 if (transactionAmount.isLessThan(KualiDecimal.ZERO)) { 240 transactionAmount = transactionAmount.negated(); 241 } 242 if (holdingTaxLotGroupedByIPInd != null && transactionAmount.isGreaterThan(KualiDecimal.ZERO)) { 243 int maxNumberOfTranLines = kemService.getMaxNumberOfTransactionLinesPerDocument(); 244 for (HoldingTaxLot holdingTaxLot : holdingTaxLotGroupedByIPInd) { 245 246 if (cashIncreaseDocument.getNextTargetLineNumber() > maxNumberOfTranLines) { 247 248 // validate and submit 249 if (validateECI(cashIncreaseDocument)) { 250 submitCashDocument(cashIncreaseDocument, EndowConstants.DocumentTypeNames.ENDOWMENT_CASH_INCREASE, EndowParameterKeyConstants.INCOME_NO_ROUTE_IND); 251 252 // generate a new ECI 253 cashIncreaseDocument = initializeCashDocument(EndowConstants.DocumentTypeNames.ENDOWMENT_CASH_INCREASE, EndowConstants.MAXMUM_NUMBER_OF_EDOC_INITIALIZATION_TRY); 254 if (ObjectUtils.isNull(cashIncreaseDocument)) { 255 return; // we can't do anything 256 } 257 else { 258 // add the doc description and security detail 259 cashIncreaseDocument.getDocumentHeader().setDocumentDescription(parameterService.getParameterValue(IncomeDistributionForPooledFundStep.class, EndowParameterKeyConstants.INCOME_DESCRIPTION)); 260 cashIncreaseDocument.setTransactionSourceTypeCode(EndowConstants.TransactionSourceTypeCode.AUTOMATED); 261 addSecurityDetailToECI(cashIncreaseDocument, EndowConstants.TRANSACTION_LINE_TYPE_TARGET, holdingTaxLot.getSecurityId(), holdingTaxLot.getRegistrationCode()); 262 // reset reports 263 resetTotalReport(cashIncreaseDocument); 264 resetExceptionlReport(cashIncreaseDocument); 265 } 266 } 267 else { 268 writeValidationErrorReason(); 269 LOG.error("Failed to validate ECI: Document # " + cashIncreaseDocument.getDocumentNumber()); 270 } 271 } 272 273 // now create and add a new transaction line 274 EndowmentTargetTransactionLine endowmentTargetTransactionLine = new EndowmentTargetTransactionLine(); 275 endowmentTargetTransactionLine.setKemid(holdingTaxLot.getKemid()); 276 endowmentTargetTransactionLine.setEtranCode(incomeDistributionForPooledFundDao.getIncomeEntraCode(holdingTaxLot.getSecurityId())); 277 endowmentTargetTransactionLine.setTransactionIPIndicatorCode(EndowConstants.IncomePrincipalIndicator.INCOME); 278 // endowmentTargetTransactionLine.setTransactionLineTypeCode(EndowConstants.TRANSACTION_LINE_TYPE_TARGET); 279 endowmentTargetTransactionLine.setTransactionAmount(transactionAmount); 280 281 GlobalVariables.clear(); // clear the previous errors 282 if (validateTransactionLine(cashIncreaseDocument, endowmentTargetTransactionLine, EndowConstants.NEW_TARGET_TRAN_LINE_PROPERTY_NAME)) { 283 cashIncreaseDocument.addTargetTransactionLine(endowmentTargetTransactionLine); 284 285 // prepare the total report 286 prepareTotalReport(transactionAmount, new KualiDecimal(holdingTaxLot.getUnits())); 287 288 // set ECT type to reports 289 setDocumentTypeForReport(EndowConstants.DocumentTypeNames.ENDOWMENT_CASH_TRANSFER); 290 291 // get the list of KemidPayoutInstruction with pay income short date <= current date && (pay income term 292 // date == null or > current date) && kemid != pay_inc_to_kemid 293 List<KemidPayoutInstruction> kemidPayoutInstructionList = incomeDistributionForPooledFundDao.getKemidPayoutInstructionForECT(holdingTaxLot.getKemid(), kemService.getCurrentDate()); 294 if (kemidPayoutInstructionList != null && !kemidPayoutInstructionList.isEmpty()) { 295 // create an ECT per sec_id, regis_cd when each transaction line is added 296 createECT(holdingTaxLot, transactionAmount, cashTransferDocumentList, kemidPayoutInstructionList); 297 } 298 299 // set ECI type to reports 300 setDocumentTypeForReport(EndowConstants.DocumentTypeNames.ENDOWMENT_CASH_INCREASE); 301 } 302 else { 303 // add the info to the exception report 304 writeExceptionReport(kemid, transactionAmount, new KualiDecimal(holdingTaxLot.getUnits())); 305 writeValidationErrorReason(); 306 } 307 } 308 } 309 } 310 } 311 312 } 313 314 /** 315 * Creates ECT 316 * 317 * @param holdingTaxLot 318 * @param transactionAmount 319 * @param cashTransferDocumentList 320 */ 321 protected CashTransferDocument createECT(HoldingTaxLot holdingTaxLot, KualiDecimal transactionAmount, List<CashTransferDocument> cashTransferDocumentList, List<KemidPayoutInstruction> kemidPayoutInstructionList) { 322 323 CashTransferDocument cashTransferDocument = initializeCashDocument(EndowConstants.DocumentTypeNames.ENDOWMENT_CASH_TRANSFER, EndowConstants.MAXMUM_NUMBER_OF_EDOC_INITIALIZATION_TRY); 324 if (ObjectUtils.isNotNull(cashTransferDocument)) { 325 cashTransferDocument.getDocumentHeader().setDocumentDescription(parameterService.getParameterValue(IncomeDistributionForPooledFundStep.class, EndowParameterKeyConstants.INCOME_TRANSFER_DESCRIPTION)); 326 cashTransferDocument.setTransactionSourceTypeCode(EndowConstants.TransactionSourceTypeCode.AUTOMATED); 327 // add security 328 addSecurityDetailToECT(cashTransferDocument, holdingTaxLot.getSecurityId(), holdingTaxLot.getRegistrationCode()); 329 // add transaction lines 330 addTransactionLinesToECT(cashTransferDocumentList, cashTransferDocument, holdingTaxLot, kemidPayoutInstructionList, transactionAmount); 331 // prepare to submit the current ECT later 332 cashTransferDocumentList.add(cashTransferDocument); 333 } 334 return cashTransferDocument; 335 } 336 337 /** 338 * Adds transaction lines to ECT 339 * 340 * @param cashTransferDocumentList 341 * @param cashTransferDocument 342 * @param holdingTaxLot 343 * @param kemidPayoutInstructionList 344 * @param toalTransactionAmount 345 */ 346 protected void addTransactionLinesToECT(List<CashTransferDocument> cashTransferDocumentList, CashTransferDocument cashTransferDocument, HoldingTaxLot holdingTaxLot, List<KemidPayoutInstruction> kemidPayoutInstructionList, KualiDecimal toalTransactionAmount) { 347 348 int maxNumberOfTranLines = kemService.getMaxNumberOfTransactionLinesPerDocument(); 349 350 for (KemidPayoutInstruction kemidPayoutInstruction : kemidPayoutInstructionList) { 351 if (cashTransferDocument.getNextSourceLineNumber() > maxNumberOfTranLines) { 352 // prepare to submit the current ECT later 353 cashTransferDocumentList.add(cashTransferDocument); 354 355 // generate a new ECT 356 cashTransferDocument = initializeCashDocument(EndowConstants.DocumentTypeNames.ENDOWMENT_CASH_TRANSFER, EndowConstants.MAXMUM_NUMBER_OF_EDOC_INITIALIZATION_TRY); 357 if (cashTransferDocument == null) { 358 return; // ?? 359 } 360 else { 361 // add the doc description and security 362 cashTransferDocument.getDocumentHeader().setDocumentDescription(parameterService.getParameterValue(IncomeDistributionForPooledFundStep.class, EndowParameterKeyConstants.INCOME_TRANSFER_DESCRIPTION)); 363 cashTransferDocument.setTransactionSourceTypeCode(EndowConstants.TransactionSourceTypeCode.AUTOMATED); 364 // populate security 365 addSecurityDetailToECT(cashTransferDocument, holdingTaxLot.getSecurityId(), holdingTaxLot.getRegistrationCode()); 366 //addSecurityDetailToECT(cashTransferDocument, EndowConstants.TRANSACTION_LINE_TYPE_TARGET, holdingTaxLot.getSecurityId(), holdingTaxLot.getRegistrationCode()); 367 // reset reports 368 resetTotalReport(cashTransferDocument); 369 resetExceptionlReport(cashTransferDocument); 370 } 371 } 372 373 // add a source transaction line 374 EndowmentSourceTransactionLine sourceTransactionLine = new EndowmentSourceTransactionLine(); 375 sourceTransactionLine.setKemid(holdingTaxLot.getKemid()); 376 sourceTransactionLine.setEtranCode(parameterService.getParameterValue(IncomeDistributionForPooledFundStep.class, EndowParameterKeyConstants.INCOME_TRANSFER_ENDOWMENT_TRANSACTION_CODE)); 377 sourceTransactionLine.setTransactionLineDescription("To <" + kemidPayoutInstruction.getPayIncomeToKemid() + ">"); 378 sourceTransactionLine.setTransactionIPIndicatorCode(EndowConstants.IncomePrincipalIndicator.INCOME); 379 sourceTransactionLine.setTransactionAmount(toalTransactionAmount.multiply(kemidPayoutInstruction.getPercentOfIncomeToPayToKemid())); 380 381 // add a target transaction line 382 EndowmentTargetTransactionLine targetTransactionLine = new EndowmentTargetTransactionLine(); 383 targetTransactionLine.setKemid(holdingTaxLot.getKemid()); 384 targetTransactionLine.setEtranCode(parameterService.getParameterValue(IncomeDistributionForPooledFundStep.class, EndowParameterKeyConstants.INCOME_TRANSFER_ENDOWMENT_TRANSACTION_CODE)); 385 targetTransactionLine.setTransactionLineDescription("From <" + kemidPayoutInstruction.getPayIncomeToKemid() + ">"); 386 targetTransactionLine.setTransactionIPIndicatorCode(EndowConstants.IncomePrincipalIndicator.INCOME); 387 targetTransactionLine.setTransactionAmount(toalTransactionAmount.multiply(kemidPayoutInstruction.getPercentOfIncomeToPayToKemid())); 388 389 GlobalVariables.clear(); 390 if (validateTransactionLine(cashTransferDocument, sourceTransactionLine, EndowConstants.NEW_SOURCE_TRAN_LINE_PROPERTY_NAME)) { 391 if (validateTransactionLine(cashTransferDocument, targetTransactionLine, EndowConstants.NEW_TARGET_TRAN_LINE_PROPERTY_NAME)) { 392 cashTransferDocument.addSourceTransactionLine(sourceTransactionLine); 393 cashTransferDocument.addTargetTransactionLine(targetTransactionLine); 394 395 prepareTotalReport(toalTransactionAmount.multiply(kemidPayoutInstruction.getPercentOfIncomeToPayToKemid()), new KualiDecimal(holdingTaxLot.getUnits())); 396 397 } 398 else { 399 writeExceptionReport(holdingTaxLot.getKemid(), toalTransactionAmount.multiply(kemidPayoutInstruction.getPercentOfIncomeToPayToKemid()), new KualiDecimal(holdingTaxLot.getUnits())); 400 writeValidationErrorReason(); 401 } 402 } 403 else { 404 writeExceptionReport(holdingTaxLot.getKemid(), toalTransactionAmount.multiply(kemidPayoutInstruction.getPercentOfIncomeToPayToKemid()), new KualiDecimal(holdingTaxLot.getUnits())); 405 writeValidationErrorReason(); 406 } 407 } 408 } 409 410 /** 411 * Calculates the total of holding units * distribution amount 412 * 413 * @param holdingTaxLotList 414 * @return KualiDecimal(totalTransactionAmount) 415 */ 416 protected KualiDecimal getTransactionAmount(List<HoldingTaxLot> holdingTaxLotList, Date effectiveDate) { 417 418 // total holding units 419 BigDecimal totalUnits = BigDecimal.ZERO; 420 for (HoldingTaxLot holdingTaxLot : holdingTaxLotList) { 421 totalUnits = totalUnits.add(holdingTaxLot.getUnits()); 422 } 423 424 // distribution amount of pooledFundValue with the security id and effective date 425 BigDecimal totalDistributionAmount = BigDecimal.ZERO; 426 Map<String, Object> fieldValues = new HashMap<String, Object>(); 427 fieldValues.put(EndowPropertyConstants.POOL_SECURITY_ID, holdingTaxLotList.get(0).getSecurityId()); 428 fieldValues.put(EndowPropertyConstants.VALUE_EFFECTIVE_DATE, effectiveDate); 429 PooledFundValue pooledFundValue = (PooledFundValue) businessObjectService.findByPrimaryKey(PooledFundValue.class, fieldValues); 430 totalDistributionAmount = totalDistributionAmount.add(pooledFundValue.getIncomeDistributionPerUnit()); 431 432 return new KualiDecimal(totalUnits.multiply(totalDistributionAmount)); 433 434 } 435 436 /** 437 * Adds security to ECI 438 * 439 * @param cashIncreaseDocument 440 * @param typeCode 441 * @param securityId 442 * @param registrationCode 443 */ 444 protected void addSecurityDetailToECI(CashIncreaseDocument cashIncreaseDocument, String typeCode, String securityId, String registrationCode) { 445 cashIncreaseDocument.getTargetTransactionSecurity().setSecurityLineTypeCode(typeCode); 446 cashIncreaseDocument.getTargetTransactionSecurity().setSecurityID(securityId); 447 cashIncreaseDocument.getTargetTransactionSecurity().setRegistrationCode(registrationCode); 448 } 449 450 /** 451 * Adds security to ECT 452 * 453 * @param cashTransferDocument 454 * @param typeCode 455 * @param securityId 456 * @param registrationCode 457 */ 458 protected void addSecurityDetailToECT(CashTransferDocument cashTransferDocument, String securityId, String registrationCode) { 459 cashTransferDocument.getSourceTransactionSecurity().setSecurityLineTypeCode(EndowConstants.TRANSACTION_LINE_TYPE_SOURCE); 460 cashTransferDocument.getSourceTransactionSecurity().setSecurityID(securityId); 461 cashTransferDocument.getSourceTransactionSecurity().setRegistrationCode(registrationCode); 462 463 cashTransferDocument.getTargetTransactionSecurity().setSecurityLineTypeCode(EndowConstants.TRANSACTION_LINE_TYPE_TARGET); 464 cashTransferDocument.getTargetTransactionSecurity().setSecurityID(securityId); 465 cashTransferDocument.getTargetTransactionSecurity().setRegistrationCode(registrationCode); 466 } 467 468 /** 469 * Initialize a cash document. If fails, try as many times as EndowConstants.MAXMUM_NUMBER_OF_EDOC_INITILIZATION_TRY. 470 * 471 * @param <C> 472 * @param documentType 473 * @param counter 474 * @return 475 */ 476 protected <C extends EndowmentSecurityDetailsDocumentBase> C initializeCashDocument(String documentType, int counter) { 477 478 C cashDocument = null; 479 480 if (counter > 0) { 481 try { 482 cashDocument = (C) documentService.getNewDocument(SpringContext.getBean(TransactionalDocumentDictionaryService.class).getDocumentClassByName(documentType)); 483 } 484 catch (WorkflowException wfe) { 485 incomeDistributionForPooledFundExceptionReportWriterService.writeFormattedMessageLine("Failed to generate a new %s: %s", documentType, wfe.getMessage()); 486 LOG.error((EndowConstants.MAXMUM_NUMBER_OF_EDOC_INITIALIZATION_TRY - counter + 1) + ": The creation of " + documentType + " failed. Tyring it again ..."); 487 cashDocument = (C) initializeCashDocument(documentType, --counter); 488 } 489 catch (Exception e) { 490 incomeDistributionForPooledFundExceptionReportWriterService.writeFormattedMessageLine("Failed to generate a new %s: %s", documentType, e.getMessage()); 491 LOG.error("generateCashDocument Runtime error in initializing document: " + documentType + ": " + e.getMessage()); 492 } 493 } 494 495 return cashDocument; 496 } 497 498 /** 499 * Submits Cash document 500 * 501 * @param <T> 502 * @param cashDocument 503 */ 504 protected <T extends EndowmentSecurityDetailsDocumentBase> void submitCashDocument(T cashDocument, String documentType, String noRouteInd) { 505 try { 506 cashDocument.setNoRouteIndicator(isNoRoute(noRouteInd)); 507 documentService.routeDocument(cashDocument, "Submitted by the batch job", null); 508 } 509 catch (WorkflowException wfe) { 510 LOG.error("Failed to route document #: " + cashDocument.getDocumentNumber()); 511 LOG.error(wfe.getMessage()); 512 try { 513 GlobalVariables.clear(); 514 documentService.saveDocument(cashDocument); 515 } 516 catch (WorkflowException wfe2) { 517 LOG.error("Failed to save document #: " + cashDocument.getDocumentNumber()); 518 LOG.error(wfe2.getMessage()); 519 } 520 finally { 521 writeSubmitError(cashDocument, documentType); 522 } 523 } 524 catch (Exception e) { 525 writeSubmitError(cashDocument, documentType); 526 LOG.error(e.getMessage()); 527 } 528 } 529 530 /** 531 * Groups holdingTaxLotList by kemid and incomePrincipalIndicator where the value of holding units > 0 532 * 533 * @param holdingTaxLotList 534 * @param kemidMap 535 */ 536 protected void groupHoldingTaxLot(List<HoldingTaxLot> holdingTaxLotList, Map<String, Map<String, List<HoldingTaxLot>>> kemidMap) { 537 for (HoldingTaxLot holdingTaxLot : holdingTaxLotList) { 538 if (holdingTaxLot.getUnits().doubleValue() > 0.0) { 539 String kemid = holdingTaxLot.getKemid(); 540 String incomePrincipalIndicator = holdingTaxLot.getIncomePrincipalIndicator(); 541 if (kemidMap.containsKey(kemid)) { 542 if (kemidMap.get(kemid).containsKey(incomePrincipalIndicator)) { 543 // add it to the same kemid and incomePrincipalIndicator list 544 kemidMap.get(kemid).get(incomePrincipalIndicator).add(holdingTaxLot); 545 } 546 else { 547 // create a new incomePrincipalIndicator map and put it to kemidMap 548 List<HoldingTaxLot> taxLots = new ArrayList<HoldingTaxLot>(); 549 taxLots.add(holdingTaxLot); 550 kemidMap.get(kemid).put(incomePrincipalIndicator, taxLots); 551 } 552 } 553 else { 554 Map<String, List<HoldingTaxLot>> ipIndMap = new HashMap<String, List<HoldingTaxLot>>(); 555 List<HoldingTaxLot> taxLots = new ArrayList<HoldingTaxLot>(); 556 taxLots.add(holdingTaxLot); 557 ipIndMap.put(incomePrincipalIndicator, taxLots); 558 kemidMap.put(kemid, ipIndMap); 559 } 560 } 561 } 562 } 563 564 /** 565 * Validates Cash Transaction line 566 * 567 * @param <C> 568 * @param <T> 569 * @param cashDocument 570 * @param endowmentTransactionLine 571 * @param trnsactionPropertyName 572 * @return 573 */ 574 protected <C extends EndowmentSecurityDetailsDocumentBase, T extends EndowmentTransactionLineBase> boolean validateTransactionLine(C cashDocument, T endowmentTransactionLine, String trnsactionPropertyName) { 575 return kualiRuleService.applyRules(new AddTransactionLineEvent(trnsactionPropertyName, cashDocument, endowmentTransactionLine)); 576 } 577 578 /** 579 * validates the ECI business rules 580 * 581 * @param cashIncreaseDocument 582 * @return boolean 583 */ 584 protected boolean validateECI(CashIncreaseDocument cashIncreaseDocument) { 585 return kualiRuleService.applyRules(new RouteDocumentEvent(cashIncreaseDocument)); 586 } 587 588 /** 589 * validates the ECT business rules 590 * 591 * @param cashTransferDocument 592 * @return boolean 593 */ 594 protected boolean validateECT(CashTransferDocument cashTransferDocument) { 595 return kualiRuleService.applyRules(new RouteDocumentEvent(cashTransferDocument)); 596 } 597 598 /** 599 * checks no route indicator 600 * 601 * @return boolean 602 */ 603 public boolean isNoRoute(String paramNoRouteInd) { 604 return parameterService.getIndicatorParameter(IncomeDistributionForPooledFundStep.class, paramNoRouteInd); 605 } 606 607 /** 608 * Resets the total report 609 * 610 * @param <C> 611 * @param cashDocument 612 */ 613 protected <C extends EndowmentSecurityDetailsDocumentBase> void resetTotalReport(C cashDocument) { 614 totalReportLine.setDocumentId(cashDocument.getDocumentNumber()); 615 totalReportLine.setTotalNumberOfTransactionLines(0); 616 totalReportLine.setIncomeAmount(KualiDecimal.ZERO); 617 totalReportLine.setIncomeUnits(KualiDecimal.ZERO); 618 totalReportLine.setPrincipalAmount(KualiDecimal.ZERO); 619 totalReportLine.setPrincipalUnits(KualiDecimal.ZERO); 620 } 621 622 /** 623 * Adds income and units 624 * 625 * @param transactionAmount 626 * @param units 627 */ 628 protected void prepareTotalReport(KualiDecimal transactionAmount, KualiDecimal units) { 629 totalReportLine.addIncomeAmount(transactionAmount); 630 totalReportLine.addIncomeUnits(units); 631 } 632 633 /** 634 * Resets the exception report 635 * 636 * @param <C> 637 * @param cashDocument 638 */ 639 protected <C extends EndowmentSecurityDetailsDocumentBase> void resetExceptionlReport(C cashDocument) { 640 exceptionReportLine.setDocumentId(cashDocument.getDocumentNumber()); 641 exceptionReportLine.setIncomeAmount(cashDocument.getTargetIncomeTotal()); 642 exceptionReportLine.setIncomeUnits(cashDocument.getTargetIncomeTotalUnits()); 643 exceptionReportLine.setPrincipalAmount(cashDocument.getTargetPrincipalTotal()); 644 exceptionReportLine.setPrincipalUnits(cashDocument.getTargetPrincipalTotalUnits()); 645 } 646 647 /** 648 * Write exception errors 649 * 650 * @param kemid 651 * @param transactionAmount 652 * @param units 653 */ 654 protected void writeExceptionReport(String kemid, KualiDecimal transactionAmount, KualiDecimal units) { 655 exceptionReportLine.setKemid(kemid); 656 exceptionReportLine.setIncomeAmount(transactionAmount); 657 exceptionReportLine.setIncomeUnits(units); 658 incomeDistributionForPooledFundExceptionReportWriterService.writeTableRow(exceptionReportLine); 659 List<String> errorMessages = GloabalVariablesExtractHelper.extractGlobalVariableErrors(); 660 for (String errorMessage : errorMessages) { 661 incomeDistributionForPooledFundExceptionReportWriterService.writeFormattedMessageLine("Reason: %s", errorMessage); 662 incomeDistributionForPooledFundExceptionReportWriterService.writeNewLines(1); 663 } 664 } 665 666 /** 667 * Initialize reports 668 * 669 * @param documentType 670 * @param securityId 671 */ 672 protected void initializeReports(String documentType) { 673 674 // initialize totalReportLine 675 this.totalReportLine = new TransactionDocumentTotalReportLine(documentType, "", ""); 676 incomeDistributionForPooledFundTotalReportWriterService.writeSubTitle("<incomeDistributionForPooledFundJob> Totals Processed"); 677 incomeDistributionForPooledFundTotalReportWriterService.writeNewLines(1); 678 incomeDistributionForPooledFundTotalReportWriterService.writeTableHeader(totalReportLine); 679 680 // initialize exceptionReportLine 681 this.exceptionReportLine = new TransactionDocumentExceptionReportLine(documentType, "", ""); 682 incomeDistributionForPooledFundExceptionReportWriterService.writeSubTitle("<incomeDistributionForPooledFundJob> Exception Report"); 683 incomeDistributionForPooledFundExceptionReportWriterService.writeNewLines(1); 684 incomeDistributionForPooledFundExceptionReportWriterService.writeTableHeader(exceptionReportLine); 685 } 686 687 /** 688 * Set the document type to reports 689 * 690 * @param documentType 691 */ 692 protected void setDocumentTypeForReport(String documentType) { 693 totalReportLine.setDocumentType(documentType); 694 exceptionReportLine.setDocumentType(documentType); 695 } 696 697 /** 698 * Writes the validation errors 699 */ 700 protected void writeValidationErrorReason() { 701 List<String> errorMessages = GloabalVariablesExtractHelper.extractGlobalVariableErrors(); 702 for (String errorMessage : errorMessages) { 703 incomeDistributionForPooledFundExceptionReportWriterService.writeFormattedMessageLine("Reason: %s", errorMessage); 704 incomeDistributionForPooledFundExceptionReportWriterService.writeNewLines(1); 705 } 706 } 707 708 /** 709 * Write errors that occur during the submission 710 * 711 * @param <T> 712 * @param cashDocument 713 * @param documentType 714 */ 715 protected <T extends EndowmentSecurityDetailsDocumentBase> void writeSubmitError(T cashDocument, String documentType) { 716 exceptionReportLine.setDocumentId(cashDocument.getDocumentNumber()); 717 exceptionReportLine.setDocumentType(documentType); 718 exceptionReportLine.setSecurityId(cashDocument.getTargetTransactionSecurity().getSecurityID()); 719 exceptionReportLine.setIncomeAmount(cashDocument.getTargetIncomeTotal()); 720 incomeDistributionForPooledFundExceptionReportWriterService.writeTableRow(exceptionReportLine); 721 incomeDistributionForPooledFundExceptionReportWriterService.writeFormattedMessageLine("Falied to route document #: %s", cashDocument.getDocumentNumber()); 722 } 723 724 /** 725 * Sets the businessObjectService attribute value. 726 * 727 * @param businessObjectService The businessObjectService to set. 728 */ 729 public void setBusinessObjectService(BusinessObjectService businessObjectService) { 730 this.businessObjectService = businessObjectService; 731 } 732 733 /** 734 * Sets the documentService attribute value. 735 * 736 * @param documentService The documentService to set. 737 */ 738 public void setDocumentService(DocumentService documentService) { 739 this.documentService = documentService; 740 } 741 742 /** 743 * Sets the parameterService attribute value. 744 * 745 * @param parameterService The parameterService to set. 746 */ 747 public void setParameterService(ParameterService parameterService) { 748 this.parameterService = parameterService; 749 } 750 751 /** 752 * Sets the kualiRuleService attribute value. 753 * 754 * @param kualiRuleService The kualiRuleService to set. 755 */ 756 public void setKualiRuleService(KualiRuleService kualiRuleService) { 757 this.kualiRuleService = kualiRuleService; 758 } 759 760 /** 761 * Sets the kemService attribute value. 762 * 763 * @param kemService The kemService to set. 764 */ 765 public void setKemService(KEMService kemService) { 766 this.kemService = kemService; 767 } 768 769 /** 770 * Sets the holdingTaxLotService attribute value. 771 * 772 * @param holdingTaxLotService The holdingTaxLotService to set. 773 */ 774 public void setHoldingTaxLotService(HoldingTaxLotService holdingTaxLotService) { 775 this.holdingTaxLotService = holdingTaxLotService; 776 } 777 778 /** 779 * Sets the pooledFundValueService attribute value. 780 * 781 * @param pooledFundValueService The pooledFundValueService to set. 782 */ 783 public void setPooledFundValueService(PooledFundValueService pooledFundValueService) { 784 this.pooledFundValueService = pooledFundValueService; 785 } 786 787 /** 788 * Sets the incomeDistributionForPooledFundDao attribute value. 789 * 790 * @param incomeDistributionForPooledFundDao The incomeDistributionForPooledFundDao to set. 791 */ 792 public void setIncomeDistributionForPooledFundDao(IncomeDistributionForPooledFundDao incomeDistributionForPooledFundDao) { 793 this.incomeDistributionForPooledFundDao = incomeDistributionForPooledFundDao; 794 } 795 796 /** 797 * Sets the incomeDistributionForPooledFundExceptionReportWriterService attribute value. 798 * 799 * @param incomeDistributionForPooledFundExceptionReportWriterService The 800 * incomeDistributionForPooledFundExceptionReportWriterService to set. 801 */ 802 public void setIncomeDistributionForPooledFundExceptionReportWriterService(ReportWriterService incomeDistributionForPooledFundExceptionReportWriterService) { 803 this.incomeDistributionForPooledFundExceptionReportWriterService = incomeDistributionForPooledFundExceptionReportWriterService; 804 } 805 806 /** 807 * Sets the incomeDistributionForPooledFundTotalReportWriterService attribute value. 808 * 809 * @param incomeDistributionForPooledFundTotalReportWriterService The incomeDistributionForPooledFundTotalReportWriterService to 810 * set. 811 */ 812 public void setIncomeDistributionForPooledFundTotalReportWriterService(ReportWriterService incomeDistributionForPooledFundTotalReportWriterService) { 813 this.incomeDistributionForPooledFundTotalReportWriterService = incomeDistributionForPooledFundTotalReportWriterService; 814 } 815 816 817 }