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 }