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 }