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.document.service.impl; 017 018 import java.sql.Date; 019 import java.util.Calendar; 020 import java.util.HashMap; 021 import java.util.Map; 022 023 import org.apache.commons.lang.StringUtils; 024 import org.kuali.kfs.module.endow.EndowConstants; 025 import org.kuali.kfs.module.endow.businessobject.FrequencyCode; 026 import org.kuali.kfs.module.endow.document.service.FrequencyDatesService; 027 import org.kuali.kfs.module.endow.document.service.KEMService; 028 import org.kuali.rice.kns.service.BusinessObjectService; 029 import org.kuali.rice.kns.service.DateTimeService; 030 031 public class FrequencyDatesServiceImpl implements FrequencyDatesService { 032 033 protected BusinessObjectService businessObjectService; 034 protected DateTimeService dateTimeService; 035 protected KEMService kemService; 036 037 /** 038 * @see org.kuali.kfs.module.endow.document.service.FrequencyCodeService#getByPrimaryKey(java.lang.String) 039 */ 040 public FrequencyCode getByPrimaryKey(String code) { 041 FrequencyCode frequencyCode = null; 042 if (StringUtils.isNotBlank(code)) { 043 frequencyCode = (FrequencyCode) businessObjectService.findBySinglePrimaryKey(FrequencyCode.class, code); 044 } 045 046 return frequencyCode; 047 } 048 049 /** 050 * @see org.kuali.kfs.module.endow.document.service.FrequencyCodeService#calculateNextDueDate(java.lang.String, java.sql.Date) 051 */ 052 public Date calculateNextDueDate(String frequencyCode, Date currentDate) { 053 054 Calendar nextDate = null; 055 056 if (currentDate != null && frequencyCode != null && !frequencyCode.trim().isEmpty()) { 057 058 String frequencyType = frequencyCode.substring(0, 1); 059 060 if (frequencyType.equalsIgnoreCase(EndowConstants.FrequencyTypes.DAILY)) { 061 nextDate = calculateNextDate(currentDate); 062 } 063 else if (frequencyType.equalsIgnoreCase(EndowConstants.FrequencyTypes.WEEKLY)) { 064 String dayOfWeek = frequencyCode.substring(1, 4).toUpperCase(); 065 nextDate = calculateNextWeeklyDate(dayOfWeek, currentDate); 066 } 067 else if (frequencyType.equalsIgnoreCase(EndowConstants.FrequencyTypes.SEMI_MONTHLY)) { 068 String dayOfSemiMonthly = frequencyCode.substring(1, 3); 069 nextDate = calculateNextSemiMonthlyDate(dayOfSemiMonthly, currentDate); 070 } 071 else if (frequencyType.equalsIgnoreCase(EndowConstants.FrequencyTypes.MONTHLY)) { 072 String dayOfMonth = frequencyCode.substring(1, 3); 073 nextDate = calculateNextMonthlyDate(dayOfMonth, currentDate); 074 } 075 else if (frequencyType.equalsIgnoreCase(EndowConstants.FrequencyTypes.QUARTERLY) || 076 frequencyType.equalsIgnoreCase(EndowConstants.FrequencyTypes.SEMI_ANNUALLY) || 077 frequencyType.equalsIgnoreCase(EndowConstants.FrequencyTypes.ANNUALLY)) { 078 String month = frequencyCode.substring(1, 2); 079 String dayOfMonth = frequencyCode.substring(2, 4); 080 nextDate = calculateNextQuarterlyOrSemiAnnuallyOrAnnuallyProcessDate(month, dayOfMonth, frequencyType, currentDate); 081 } 082 } 083 084 return nextDate == null ? null : new java.sql.Date(nextDate.getTimeInMillis()); 085 } 086 087 /** 088 * Calculates the next date 089 * @param currentDate 090 * @return 091 */ 092 protected Calendar calculateNextDate(Date currentDate) { 093 Calendar calendar = Calendar.getInstance(); 094 calendar.setTime(currentDate); 095 calendar.add(Calendar.DATE, 1); 096 return calendar; 097 } 098 099 /** 100 * Method to calculate the next processing week date based on the frequency type 101 * adds the appropriate number of days to the current date 102 * @param dayOfWeek 103 * @return next processing date 104 */ 105 protected Calendar calculateNextWeeklyDate(String dayOfWeekFromFrequencyCode, Date currentDate) { 106 Calendar calendar = Calendar.getInstance(); 107 calendar.setTime(currentDate); 108 109 int daysToAdd = 0; 110 int dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK); // today's day of the week 111 int maximumDaysInWeek = calendar.getActualMaximum(Calendar.DAY_OF_WEEK); 112 113 if (dayOfWeekFromFrequencyCode.equalsIgnoreCase(EndowConstants.FrequencyWeekDays.MONDAY)) { 114 if (dayOfWeek < Calendar.MONDAY) 115 daysToAdd = Calendar.MONDAY - dayOfWeek; 116 else 117 daysToAdd = maximumDaysInWeek - dayOfWeek + Calendar.MONDAY; 118 } else if (dayOfWeekFromFrequencyCode.equalsIgnoreCase(EndowConstants.FrequencyWeekDays.TUESDAY)) { 119 if (dayOfWeek < Calendar.TUESDAY) 120 daysToAdd = Calendar.TUESDAY - dayOfWeek; 121 else 122 daysToAdd = maximumDaysInWeek - dayOfWeek + Calendar.TUESDAY; 123 } else if (dayOfWeekFromFrequencyCode.equalsIgnoreCase(EndowConstants.FrequencyWeekDays.WEDNESDAY)) { 124 if (dayOfWeek < Calendar.WEDNESDAY) 125 daysToAdd = Calendar.WEDNESDAY - dayOfWeek; 126 else 127 daysToAdd = maximumDaysInWeek - dayOfWeek + Calendar.WEDNESDAY; 128 } else if (dayOfWeekFromFrequencyCode.equalsIgnoreCase(EndowConstants.FrequencyWeekDays.THURSDAY)) { 129 if (dayOfWeek < Calendar.THURSDAY) 130 daysToAdd = Calendar.THURSDAY - dayOfWeek; 131 else 132 daysToAdd = maximumDaysInWeek - dayOfWeek + Calendar.THURSDAY; 133 } else if (dayOfWeekFromFrequencyCode.equalsIgnoreCase(EndowConstants.FrequencyWeekDays.FRIDAY)) { 134 if (dayOfWeek < Calendar.FRIDAY) 135 daysToAdd = Calendar.FRIDAY - dayOfWeek; 136 else 137 daysToAdd = maximumDaysInWeek - dayOfWeek + Calendar.FRIDAY; 138 } 139 140 calendar.add(Calendar.DAY_OF_MONTH, daysToAdd); 141 142 return calendar; 143 } 144 145 /** 146 * Method to calculate the next processing semi-monthly date based on the frequency type 147 * Sets the day of the month and then returns the processing date 148 * @param dayOfSemiMonthly 149 * @return next processing date 150 */ 151 protected Calendar calculateNextSemiMonthlyDate(String dayOfSemiMonthly, Date currentDate) { 152 Calendar calendar = Calendar.getInstance(); 153 calendar.setTime(currentDate); 154 155 int dayOfMonthToSet = Integer.parseInt(dayOfSemiMonthly); 156 int dayOfMonthNextToSet = 15; 157 158 // start from the target day 159 calendar.set(Calendar.DAY_OF_MONTH, dayOfMonthToSet); 160 161 // it must be greater than the current date 162 if (currentDate.compareTo(new java.sql.Date(calendar.getTimeInMillis())) > -1) { 163 calendar.add(Calendar.DAY_OF_MONTH, dayOfMonthNextToSet); 164 } 165 // if it is not still greater, it should be in the next month 166 if (currentDate.compareTo(new java.sql.Date(calendar.getTimeInMillis())) > -1) { 167 calendar.set(Calendar.DAY_OF_MONTH, dayOfMonthToSet); 168 calendar.add(Calendar.MONTH, 1); 169 } 170 171 return calendar; 172 } 173 174 /** 175 * Method to calculate the next processing monthly date based on the frequency type 176 * Sets the day in the calendar based on day part of the frequency code. 177 * @param dayOfMonth 178 * @return next processing date 179 */ 180 protected Calendar calculateNextMonthlyDate(String dayOfMonth, Date currentDate) { 181 int dayInMonthToSet; 182 183 Calendar calendar = Calendar.getInstance(); 184 calendar.setTime(currentDate); 185 setCalendarWithDays(calendar, dayOfMonth); 186 while (new java.sql.Date(calendar.getTimeInMillis()).before(currentDate)) { 187 calendar.add(Calendar.MONTH, 1); 188 } 189 190 return calendar; 191 } 192 193 /** 194 * Method to calculate the next processing quarterly or semi-annually or annually date based on the frequency type 195 * Sets the day in the calendar based on day part of the frequency code. 196 * @param frequencyType frequency code for quarterly, month, dayOfMonth 197 * @return next processing date 198 */ 199 protected Calendar calculateNextQuarterlyOrSemiAnnuallyOrAnnuallyProcessDate(String month, String dayOfMonth, String frequencyType, Date currentDate) { 200 Calendar calendar = setCalendarWithMonth(month, currentDate); 201 setCalendarWithDays(calendar, dayOfMonth); 202 203 if (frequencyType.equalsIgnoreCase(EndowConstants.FrequencyTypes.QUARTERLY)) { 204 while (new java.sql.Date(calendar.getTimeInMillis()).compareTo(currentDate) < 1) { 205 calendar.add(Calendar.MONTH, 3); 206 setCalendarWithDays(calendar, dayOfMonth); 207 } 208 } else if (frequencyType.equalsIgnoreCase(EndowConstants.FrequencyTypes.SEMI_ANNUALLY)) { 209 while (new java.sql.Date(calendar.getTimeInMillis()).compareTo(currentDate) < 1) { 210 calendar.add(Calendar.MONTH, 6); 211 setCalendarWithDays(calendar, dayOfMonth); 212 } 213 } else if (frequencyType.equalsIgnoreCase(EndowConstants.FrequencyTypes.ANNUALLY)) { 214 while (new java.sql.Date(calendar.getTimeInMillis()).compareTo(currentDate) < 1) { 215 calendar.add(Calendar.MONTH, 12); 216 setCalendarWithDays(calendar, dayOfMonth); 217 } 218 } 219 220 return calendar; 221 } 222 223 /** 224 * This method will check the current month and set the calendar to that month 225 * @param month month to set the calendar 226 * @return calendar calendar is set to the month selected 227 */ 228 protected Calendar setCalendarWithMonth(String month, Date currentDate) { 229 Calendar calendar = Calendar.getInstance(); 230 calendar.setTime(currentDate); 231 int calendarMonth = Calendar.JANUARY; 232 233 if (EndowConstants.FrequencyMonths.JANUARY.equalsIgnoreCase(month)) { 234 calendarMonth = Calendar.JANUARY; 235 } else if (EndowConstants.FrequencyMonths.FEBRUARY.equalsIgnoreCase(month)) { 236 calendarMonth = Calendar.FEBRUARY; 237 } else if (EndowConstants.FrequencyMonths.MARCH.equalsIgnoreCase(month)) { 238 calendarMonth = Calendar.MARCH; 239 } else if (EndowConstants.FrequencyMonths.APRIL.equalsIgnoreCase(month)) { 240 calendarMonth = Calendar.APRIL; 241 } else if (EndowConstants.FrequencyMonths.MAY.equalsIgnoreCase(month)) { 242 calendarMonth = Calendar.MAY; 243 } else if (EndowConstants.FrequencyMonths.JUNE.equalsIgnoreCase(month)) { 244 calendarMonth = Calendar.JUNE; 245 } else if (EndowConstants.FrequencyMonths.JULY.equalsIgnoreCase(month)) { 246 calendarMonth = Calendar.JULY; 247 } else if (EndowConstants.FrequencyMonths.AUGUST.equalsIgnoreCase(month)) { 248 calendarMonth = Calendar.AUGUST; 249 } else if (EndowConstants.FrequencyMonths.SEPTEMBER.equalsIgnoreCase(month)) { 250 calendarMonth = Calendar.SEPTEMBER; 251 } else if (EndowConstants.FrequencyMonths.OCTOBER.equalsIgnoreCase(month)) { 252 calendarMonth = Calendar.OCTOBER; 253 } else if (EndowConstants.FrequencyMonths.NOVEMBER.equalsIgnoreCase(month)) { 254 calendarMonth = Calendar.NOVEMBER; 255 } else if (EndowConstants.FrequencyMonths.DECEMBER.equalsIgnoreCase(month)) { 256 calendarMonth = Calendar.DECEMBER; 257 } 258 259 calendar.set(Calendar.MONTH, calendarMonth); 260 261 return calendar; 262 } 263 264 /** 265 * This method will check the current month and set the calendar to that month 266 * @param month, dayOfMonth month to set the calendar, dayOfMonth day of the month to set to 267 * @return calendar calendar is set to the month selected 268 */ 269 protected void setCalendarWithDays(Calendar calendar, String dayOfMonth) { 270 int dayInMonthToSet; 271 int calendarMonth = calendar.get(Calendar.MONTH); 272 273 if (StringUtils.equalsIgnoreCase(dayOfMonth, EndowConstants.FrequencyMonthly.MONTH_END)) { // month end for the month so need to get max days... 274 dayInMonthToSet = calendar.getActualMaximum(Calendar.DAY_OF_MONTH); 275 } else { 276 dayInMonthToSet = Integer.parseInt(dayOfMonth); 277 if ( dayInMonthToSet > calendar.getActualMaximum(Calendar.DAY_OF_MONTH) ) { 278 dayInMonthToSet = calendar.getActualMaximum(Calendar.DAY_OF_MONTH); 279 } 280 } 281 282 calendar.set(Calendar.DAY_OF_MONTH, dayInMonthToSet); 283 } 284 285 286 /** 287 * This method gets the businessObjectService. 288 * 289 * @return businessObjectService 290 */ 291 protected BusinessObjectService getBusinessObjectService() { 292 return businessObjectService; 293 } 294 295 /** 296 * This method sets the businessObjectService 297 * 298 * @param businessObjectService 299 */ 300 public void setBusinessObjectService(BusinessObjectService businessObjectService) { 301 this.businessObjectService = businessObjectService; 302 } 303 304 public void setDateTimeService(DateTimeService dateTimeService) { 305 this.dateTimeService = dateTimeService; 306 } 307 308 protected DateTimeService getDateTimeService() { 309 return dateTimeService; 310 } 311 312 protected KEMService getKemService() { 313 return kemService; 314 } 315 316 public void setKemService(KEMService kemService) { 317 this.kemService = kemService; 318 } 319 320 }