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