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 }