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.sql.Date; 019 import java.util.List; 020 021 import org.kuali.kfs.module.endow.batch.service.RollFrequencyDatesService; 022 import org.kuali.kfs.module.endow.businessobject.AutomatedCashInvestmentModel; 023 import org.kuali.kfs.module.endow.businessobject.CashSweepModel; 024 import org.kuali.kfs.module.endow.businessobject.EndowmentRecurringCashTransfer; 025 import org.kuali.kfs.module.endow.businessobject.FeeMethod; 026 import org.kuali.kfs.module.endow.businessobject.Security; 027 import org.kuali.kfs.module.endow.businessobject.Tickler; 028 import org.kuali.kfs.module.endow.dataaccess.AutomatedCashInvestmentModelDao; 029 import org.kuali.kfs.module.endow.dataaccess.CashSweepModelDao; 030 import org.kuali.kfs.module.endow.dataaccess.FeeMethodDao; 031 import org.kuali.kfs.module.endow.dataaccess.RecurringCashTransferDao; 032 import org.kuali.kfs.module.endow.dataaccess.SecurityDao; 033 import org.kuali.kfs.module.endow.dataaccess.TicklerDao; 034 import org.kuali.kfs.module.endow.document.service.FrequencyDatesService; 035 import org.kuali.kfs.module.endow.document.service.KEMService; 036 import org.kuali.kfs.sys.service.ReportWriterService; 037 import org.kuali.rice.kns.bo.PersistableBusinessObject; 038 import org.kuali.rice.kns.service.BusinessObjectService; 039 import org.kuali.rice.kns.util.ObjectUtils; 040 import org.springframework.transaction.annotation.Transactional; 041 042 /** 043 * This class implements the RollFrequencyDatesService batch job. 044 */ 045 @Transactional 046 public class RollFrequencyDatesServiceImpl implements RollFrequencyDatesService { 047 protected static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(RollFrequencyDatesServiceImpl.class); 048 049 protected BusinessObjectService businessObjectService; 050 protected KEMService kemService; 051 protected FrequencyDatesService frequencyDatesService; 052 053 protected SecurityDao securityDao; 054 protected FeeMethodDao feeMethodDao; 055 protected TicklerDao ticklerDao; 056 protected RecurringCashTransferDao recurringCashTransferDao; 057 protected AutomatedCashInvestmentModelDao automatedCashInvestmentModelDao; 058 protected CashSweepModelDao cashSweepModelDao; 059 060 protected ReportWriterService rollFrequencyDatesTotalReportWriterService; 061 protected ReportWriterService rollFrequencyDatesExceptionReportWriterService; 062 063 /** 064 * Updates some date fields based on the frequency for the activity 065 * @return true if the fields are updated successfully; false otherwise 066 */ 067 public boolean updateFrequencyDate() { 068 069 LOG.info("Begin the batch Roll Frequncy Dates ..."); 070 071 // update Security Income Next Pay Dates 072 updateSecurityIncomeNextPayDates(); 073 074 // update Tickler Next Due Dates 075 updateTicklerNextDueDates(); 076 077 // update Fee Method Next Process Dates 078 updateFeeMethodProcessDates(); 079 080 // update Recurring Cash Transfer Next Process Dates 081 updateRecurringCashTransferProcessDates(); 082 083 // update Cash Sweep Model Next Due Dates 084 updateCashSweepModelNextDueDates(); 085 086 // update Cash Investment Model Next Due Dates 087 updateAutomatedCashInvestmentModelNextDueDates(); 088 089 LOG.info("The batch Roll Frequncy Dates was finished."); 090 091 return true; 092 } 093 094 /** 095 * This method updates the income next pay dates in Security 096 */ 097 protected boolean updateSecurityIncomeNextPayDates() { 098 099 boolean success = true; 100 101 int counter = 0; 102 // get all the active security records whose next income pay date is equal to the current date 103 List<Security> securityRecords = securityDao.getSecuritiesWithNextPayDateEqualToCurrentDate(); 104 if (securityRecords != null) { 105 for (Security security : securityRecords) { 106 107 Date incomeNextPayDate = security.getIncomeNextPayDate(); 108 109 // if maturity date is equals to income next pay date, do nothing 110 Date maturityDate = security.getMaturityDate(); 111 if (ObjectUtils.isNotNull(maturityDate) && ObjectUtils.isNotNull(incomeNextPayDate)) { 112 if (maturityDate.compareTo(incomeNextPayDate) == 0) { 113 continue; 114 } 115 } 116 117 // replace income next date 118 // first, with the next date calculated based on the frequency code 119 // if it is invalid, with the dividend pay date 120 String frequencyCode = security.getIncomePayFrequency(); 121 Date nextDate = frequencyDatesService.calculateNextDueDate(frequencyCode, kemService.getCurrentDate()); 122 if (nextDate == null) { 123 nextDate = security.getDividendPayDate(); 124 if (ObjectUtils.isNull(nextDate) || (ObjectUtils.isNotNull(incomeNextPayDate) && nextDate.compareTo(incomeNextPayDate) == 0)) { 125 // we don't need to update income next pay date 126 continue; 127 } 128 } 129 // update income next pay date 130 security.setIncomeNextPayDate(nextDate); 131 if (updateBusinessObject(security)) { 132 counter++; 133 generateTotalReport("END_SEC_T", counter); 134 } else { 135 LOG.error("Failed to update Security " + security.getId()); 136 generateExceptionReport("END_SEC_T", security.getId()); 137 success = false; 138 } 139 } 140 } 141 142 LOG.info("Total Security Income Next Pay Dates updated in END_SEC_T: " + counter); 143 144 return success; 145 } 146 147 /** 148 * This method updates the next due dates in Tickler 149 */ 150 protected boolean updateTicklerNextDueDates() { 151 152 boolean success = true; 153 154 int counter = 0; 155 List<Tickler> ticklerRecords = ticklerDao.getTicklerWithNextPayDateEqualToCurrentDate(); 156 if (ticklerRecords != null) { 157 for (Tickler tickler : ticklerRecords) { 158 String frequencyCode = tickler.getFrequencyCode(); 159 Date nextDate = frequencyDatesService.calculateNextDueDate(frequencyCode, kemService.getCurrentDate()); 160 if (nextDate != null) { 161 tickler.setNextDueDate(nextDate); 162 if (updateBusinessObject(tickler)) { 163 counter++; 164 generateTotalReport("END_TKLR_T", counter); 165 } else { 166 LOG.error("Failed to update Tickler " + tickler.getNumber()); 167 generateExceptionReport("END_TKLR_T", tickler.getNumber()); 168 success = false; 169 } 170 } 171 } 172 } 173 174 LOG.info("Total Tickler Next Due Dates updated in END_TKLR_T: " + counter); 175 176 return success; 177 } 178 179 /** 180 * This method updates the next process dates in FeeMethod 181 */ 182 protected boolean updateFeeMethodProcessDates() { 183 184 boolean success = true; 185 186 int counter = 0; 187 List<FeeMethod> feeMethodRecords = feeMethodDao.getFeeMethodWithNextPayDateEqualToCurrentDate(); 188 if (feeMethodRecords != null) { 189 for (FeeMethod feeMethod : feeMethodRecords) { 190 String frequencyCode = feeMethod.getFeeFrequencyCode(); 191 Date nextDate = frequencyDatesService.calculateNextDueDate(frequencyCode, kemService.getCurrentDate()); 192 if (nextDate != null) { 193 feeMethod.setFeeLastProcessDate(feeMethod.getFeeNextProcessDate()); 194 feeMethod.setFeeNextProcessDate(nextDate); 195 if (updateBusinessObject(feeMethod)) { 196 counter++; 197 generateTotalReport("END_FEE_MTHD_T", counter); 198 } else { 199 LOG.error("Failed to update FeeMethod " + feeMethod.getCode()); 200 generateExceptionReport("END_FEE_MTHD_T", feeMethod.getCode()); 201 success = false; 202 } 203 } 204 } 205 } 206 207 LOG.info("Total Fee Next Process Dates and Fee Last Process Dates updated in END_FEE_MTHD_T: " + counter); 208 209 return success; 210 } 211 212 /** 213 * This method updates the next process dates in EndowmentRecurringCashTransfer 214 */ 215 protected boolean updateRecurringCashTransferProcessDates() { 216 217 boolean success = true; 218 219 int counter = 0; 220 List<EndowmentRecurringCashTransfer> recurringCashTransferRecords = recurringCashTransferDao.getRecurringCashTransferWithNextPayDateEqualToCurrentDate(); 221 if (recurringCashTransferRecords != null) { 222 for (EndowmentRecurringCashTransfer recurringCashTransfer : recurringCashTransferRecords) { 223 String frequencyCode = recurringCashTransfer.getFrequencyCode(); 224 Date nextDate = frequencyDatesService.calculateNextDueDate(frequencyCode, kemService.getCurrentDate()); 225 if (nextDate != null) { 226 recurringCashTransfer.setLastProcessDate(recurringCashTransfer.getNextProcessDate()); 227 recurringCashTransfer.setNextProcessDate(nextDate); 228 if (updateBusinessObject(recurringCashTransfer)) { 229 counter++; 230 generateTotalReport("END_REC_CSH_XFR_T", counter); 231 } else { 232 LOG.error("Failed to update EndowmentRecurringCashTransfer " + recurringCashTransfer.getTransferNumber()); 233 generateExceptionReport("END_REC_CSH_XFR_T", recurringCashTransfer.getTransferNumber()); 234 success = false; 235 } 236 } 237 } 238 } 239 240 LOG.info("Total Next Process Dates and Last Process Dates updated in END_REC_CSH_XFR_T: " + counter); 241 242 return success; 243 } 244 245 protected boolean updateCashSweepModelNextDueDates() { 246 247 boolean success = true; 248 249 int counter = 0; 250 List<CashSweepModel> csmRecords = cashSweepModelDao.getCashSweepModelWithNextPayDateEqualToCurrentDate(kemService.getCurrentDate()); 251 if (csmRecords != null) { 252 for (CashSweepModel csm : csmRecords) { 253 String frequencyCode = csm.getCashSweepFrequencyCode(); 254 Date nextDate = frequencyDatesService.calculateNextDueDate(frequencyCode, kemService.getCurrentDate()); 255 if (nextDate != null) { 256 csm.setCashSweepNextDueDate(nextDate); 257 if (updateBusinessObject(csm)) { 258 counter++; 259 generateTotalReport("END_CSH_SWEEP_MDL_T", counter); 260 } else { 261 LOG.error("Failed to update FeeMethod " + csm.getCashSweepModelID()); 262 generateExceptionReport("END_CSH_SWEEP_MDL_T", csm.getCashSweepModelID().toString()); 263 success = false; 264 } 265 } 266 } 267 } 268 269 LOG.info("Total Cash Sweep Model Next Due Dates updated in END_CSH_SWEEP_MDL_T: " + counter); 270 271 return success; 272 } 273 274 protected boolean updateAutomatedCashInvestmentModelNextDueDates() { 275 276 boolean success = true; 277 278 int counter = 0; 279 List<AutomatedCashInvestmentModel> aciRecords = automatedCashInvestmentModelDao.getAutomatedCashInvestmentModelWithNextPayDateEqualToCurrentDate(kemService.getCurrentDate()); 280 if (aciRecords != null) { 281 for (AutomatedCashInvestmentModel aci : aciRecords) { 282 String frequencyCode = aci.getAciFrequencyCode(); 283 Date nextDate = frequencyDatesService.calculateNextDueDate(frequencyCode, kemService.getCurrentDate()); 284 if (nextDate != null) { 285 aci.setAciNextDueDate(nextDate); 286 if (updateBusinessObject(aci)) { 287 counter++; 288 generateTotalReport("END_AUTO_CSH_INVEST_MDL_T", counter); 289 } else { 290 LOG.error("Failed to update FeeMethod " + aci.getAciModelID()); 291 generateExceptionReport("END_AUTO_CSH_INVEST_MDL_T", aci.getAciModelID().toString()); 292 success = false; 293 } 294 } 295 } 296 } 297 298 LOG.info("Total ACI Next Due Dates updated in END_AUTO_CSH_INVEST_MDL_T: " + counter); 299 300 return success; 301 } 302 303 /** 304 * Generates the statistic report for updated tables 305 * @param tableName 306 * @param counter 307 */ 308 protected void generateTotalReport(String tableName, int counter) { 309 310 try { 311 rollFrequencyDatesTotalReportWriterService.writeFormattedMessageLine(tableName + ": %s", counter); 312 } catch (Exception e) { 313 LOG.error("Failed to generate the statistic report: " + e.getMessage()); 314 rollFrequencyDatesExceptionReportWriterService.writeFormattedMessageLine("Failed to generate the total report: " + e.getMessage()); 315 } 316 } 317 318 /** 319 * Generates the exception report 320 * @param tableName 321 * @param counter 322 */ 323 protected void generateExceptionReport(String tableName, String errorMessage) { 324 325 try { 326 rollFrequencyDatesExceptionReportWriterService.writeFormattedMessageLine(tableName + ": %s", errorMessage); 327 } catch (Exception e) { 328 LOG.error("Failed to generate the exception report.",e); 329 } 330 } 331 332 protected void initializeReports() { 333 rollFrequencyDatesTotalReportWriterService.writeSubTitle("<rollFrequencyDatesJob> Number of Records Updated"); 334 rollFrequencyDatesTotalReportWriterService.writeNewLines(1); 335 336 rollFrequencyDatesExceptionReportWriterService.writeSubTitle("<rollFrequencyDatesJob> Records Failed for update"); 337 rollFrequencyDatesExceptionReportWriterService.writeNewLines(1); 338 } 339 340 /** 341 * Updates business object 342 * @param businessObject 343 * @return boolean 344 */ 345 protected boolean updateBusinessObject(PersistableBusinessObject businessObject) { 346 347 boolean result = true; 348 try { 349 businessObjectService.save(businessObject); 350 } catch (Exception e) { // such as IllegalArgumentException 351 LOG.error("Unable to save " + businessObject, e); 352 result = false; 353 } 354 355 return result; 356 } 357 358 /** 359 * Sets the businessObjectService attribute value. 360 * @param businessObjectService The businessObjectService to set. 361 */ 362 public void setBusinessObjectService(BusinessObjectService businessObjectService) 363 { 364 this.businessObjectService = businessObjectService; 365 } 366 367 /** 368 * Sets the kemService attribute value. 369 * @param kemService The kemService to set. 370 */ 371 public void setKemService(KEMService kemService) { 372 this.kemService = kemService; 373 } 374 375 /** 376 * Sets the securityDao attribute value. 377 * @param securityDao The securityDao to set. 378 */ 379 public void setSecurityDao(SecurityDao securityDao) { 380 this.securityDao = securityDao; 381 } 382 383 /** 384 * Sets the feeMethodDao attribute value. 385 * @param feeMethodDao The feeMethodDao to set. 386 */ 387 public void setFeeMethodDao(FeeMethodDao feeMethodDao) { 388 this.feeMethodDao = feeMethodDao; 389 } 390 391 /** 392 * Sets the ticklerDao attribute value. 393 * @param ticklerDao The ticklerDao to set. 394 */ 395 public void setTicklerDao(TicklerDao ticklerDao) { 396 this.ticklerDao = ticklerDao; 397 } 398 399 /** 400 * Sets the recurringCashTransferDao attribute value. 401 * @param recurringCashTransferDao The recurringCashTransferDao to set. 402 */ 403 public void setRecurringCashTransferDao(RecurringCashTransferDao recurringCashTransferDao) { 404 this.recurringCashTransferDao = recurringCashTransferDao; 405 } 406 407 /** 408 * Sets the rollFrequencyDatesTotalReportWriterService attribute value. 409 * @param rollFrequencyDatesTotalReportWriterService The rollFrequencyDatesTotalReportWriterService to set. 410 */ 411 public void setRollFrequencyDatesTotalReportWriterService(ReportWriterService rollFrequencyDatesTotalReportWriterService) { 412 this.rollFrequencyDatesTotalReportWriterService = rollFrequencyDatesTotalReportWriterService; 413 } 414 415 /** 416 * Sets the rollFrequencyDatesExceptionReportWriterService attribute value. 417 * @param rollFrequencyDatesExceptionReportWriterService The rollFrequencyDatesExceptionReportWriterService to set. 418 */ 419 public void setRollFrequencyDatesExceptionReportWriterService(ReportWriterService rollFrequencyDatesExceptionReportWriterService) { 420 this.rollFrequencyDatesExceptionReportWriterService = rollFrequencyDatesExceptionReportWriterService; 421 } 422 423 /** 424 * Sets the automatedCashInvestmentModelDao attribute value. 425 * @param automatedCashInvestmentModelDao The automatedCashInvestmentModelDao to set. 426 */ 427 public void setAutomatedCashInvestmentModelDao(AutomatedCashInvestmentModelDao automatedCashInvestmentModelDao) { 428 this.automatedCashInvestmentModelDao = automatedCashInvestmentModelDao; 429 } 430 431 /** 432 * Sets the cashSweepModelDao attribute value. 433 * @param cashSweepModelDao The cashSweepModelDao to set. 434 */ 435 public void setCashSweepModelDao(CashSweepModelDao cashSweepModelDao) { 436 this.cashSweepModelDao = cashSweepModelDao; 437 } 438 439 /** 440 * Gets the frequencyDatesService attribute. 441 * @return Returns the frequencyDatesService. 442 */ 443 protected FrequencyDatesService getFrequencyDatesService() { 444 return frequencyDatesService; 445 } 446 447 /** 448 * Sets the frequencyDatesService attribute value. 449 * @param frequencyDatesService The frequencyDatesService to set. 450 */ 451 public void setFrequencyDatesService(FrequencyDatesService frequencyDatesService) { 452 this.frequencyDatesService = frequencyDatesService; 453 } 454 }