001package org.kuali.ole.deliver.util; 002 003import org.apache.commons.collections.CollectionUtils; 004import org.apache.commons.lang3.StringUtils; 005import org.apache.commons.lang3.time.DateUtils; 006import org.joda.time.Interval; 007import org.kuali.ole.OLEConstants; 008import org.kuali.ole.deliver.bo.OleCirculationDesk; 009import org.kuali.ole.deliver.calendar.bo.*; 010import org.kuali.ole.deliver.drools.FixedDateUtil; 011import org.kuali.ole.deliver.service.ParameterValueResolver; 012import org.kuali.rice.krad.service.BusinessObjectService; 013import org.kuali.rice.krad.service.KRADServiceLocator; 014 015import java.sql.Timestamp; 016import java.util.*; 017 018/** 019 * Created by pvsubrah on 10/3/15. 020 */ 021public class LoanDateTimeUtil extends ExceptionDateLoanDateTimeUtil { 022 private String policyId; 023 private Map<String, OleCalendar> calendarMap; 024 private OleCalendar activeCalendar; 025 private BusinessObjectService businessObjectService; 026 private Boolean nonWorkingHoursCheck = false; 027 028 public Date calculateDateTimeByPeriod(String loanPeriod, OleCirculationDesk oleCirculationDesk) { 029 Date loanDueDate; 030 031 loanDueDate = getLoanDueDate(loanPeriod); 032 033 if (null != loanDueDate && null != oleCirculationDesk) { 034 OleCalendar activeCalendar = getActiveCalendar(loanDueDate, oleCirculationDesk.getCalendarGroupId()); 035 setActiveCalendar(activeCalendar); 036 037 if (null != activeCalendar) { 038 loanDueDate = calculateDueDate(loanDueDate); 039 } 040 } 041 042 return loanDueDate; 043 } 044 045 private Date calculateDueDate(Date loanDueDate) { 046 OleCalendarExceptionPeriod oleCalendarExceptionPeriod = doesDateFallInExceptionPeriod(getActiveCalendar(), loanDueDate); 047 048 if (null == oleCalendarExceptionPeriod) { 049 OleCalendarExceptionDate exceptionDate = isDateAnExceptionDate(getActiveCalendar(), loanDueDate); 050 if (null == exceptionDate) { 051 loanDueDate = handleNonWorkingHoursWorkflow(loanDueDate, getActiveCalendar().getOleCalendarWeekList()); 052 } else { 053 if (StringUtils.isEmpty(exceptionDate.getOpenTime()) && StringUtils.isEmpty(exceptionDate.getCloseTime())) { 054 //Holiday workflow; 055 Date followingDay = DateUtils.addDays(loanDueDate, 1); 056 loanDueDate = calculateDueDate(followingDay); 057 } else { 058 // Partial hours workflow 059 loanDueDate = handleExceptionDayWithPartialHours(loanDueDate, exceptionDate); 060 } 061 } 062 } else { 063 List<OleCalendarExceptionPeriodWeek> oleCalendarExceptionPeriodWeekList = oleCalendarExceptionPeriod.getOleCalendarExceptionPeriodWeekList(); 064 //If the week list is empty i.e its a holiday period; 065 if (CollectionUtils.isEmpty(oleCalendarExceptionPeriodWeekList)) { 066 Timestamp endDate = oleCalendarExceptionPeriod.getEndDate(); 067 Date followingDay = DateUtils.addDays(endDate, 1); 068 loanDueDate = calculateDueDate(followingDay); 069 } else { 070 loanDueDate = handleNonWorkingHoursWorkflow(loanDueDate, oleCalendarExceptionPeriodWeekList); 071 } 072 } 073 return loanDueDate; 074 } 075 076 private Date handleExceptionDayWithPartialHours(Date loanDueDate, OleCalendarExceptionDate exceptionDate) { 077 Calendar instance = Calendar.getInstance(); 078 instance.setTime(exceptionDate.getDate()); 079 exceptionDate.setStartDay(String.valueOf(instance.get(Calendar.DAY_OF_WEEK)-1)); 080 exceptionDate.setEndDay(String.valueOf(instance.get(Calendar.DAY_OF_WEEK)-1)); 081 082 List oleBaseCalendarWeekList = new ArrayList<>(); 083 oleBaseCalendarWeekList.add(exceptionDate); 084 loanDueDate = handleNonWorkingHoursWorkflow(loanDueDate, oleBaseCalendarWeekList); 085 086 return loanDueDate; 087 } 088 089 private Date handleNonWorkingHoursWorkflow(Date loanDueDate, List<? extends OleBaseCalendarWeek> oleBaseCalendarWeekList) { 090 Map<String, Map<String, String>> openAndClosingTimeForTheGivenDayFromWeekList = getOpenAndClosingTimeForTheGivenDayFromWeekList(loanDueDate, oleBaseCalendarWeekList); 091 if (nonWorkingHoursCheck) { 092 Map<String, String> openTime = openAndClosingTimeForTheGivenDayFromWeekList.get("openTime"); 093 Calendar calendar = resolveDateTime(openTime, loanDueDate); 094 loanDueDate = calendar.getTime(); 095 loanDueDate = handleGracePeriod(loanDueDate); 096 } else { 097 boolean loanDueTimeWithinWorkingHours = isLoanDueTimeWithinWorkingHours(loanDueDate, oleBaseCalendarWeekList); 098 099 if (!loanDueTimeWithinWorkingHours) { 100 if (includeNonWorkingHours()) { 101 Date followingDay = DateUtils.addDays(loanDueDate, 1); 102 nonWorkingHoursCheck = true; 103 loanDueDate = calculateDueDate(followingDay); 104 } else { 105 Map<String, String> closeTime = openAndClosingTimeForTheGivenDayFromWeekList.get("closeTime"); 106 Calendar calendar = resolveDateTime(closeTime, loanDueDate); 107 loanDueDate = calendar.getTime(); 108 } 109 } 110 } 111 return loanDueDate; 112 } 113 114 115 private Date getLoanDueDate(String loanPeriod) { 116 Date loanDueDate = null; 117 118 if (loanPeriod.equalsIgnoreCase(OLEConstants.FIXED_DUE_DATE)) { 119 loanDueDate = new FixedDateUtil().getFixedDateByPolicyId(getPolicyId()); 120 } else { 121 StringTokenizer stringTokenizer = new StringTokenizer(loanPeriod, "-"); 122 String amount = stringTokenizer.nextToken(); 123 String period = stringTokenizer.nextToken(); 124 125 if (period.equalsIgnoreCase("m")) { 126 loanDueDate = DateUtils.addMinutes(new Date(), Integer.parseInt(amount)); 127 } else if (period.equalsIgnoreCase("h")) { 128 loanDueDate = DateUtils.addHours(new Date(), Integer.parseInt(amount)); 129 } else if (period.equalsIgnoreCase("d")) { 130 loanDueDate = DateUtils.addDays(new Date(), Integer.parseInt(amount)); 131 } else if (period.equalsIgnoreCase("w")) { 132 loanDueDate = DateUtils.addWeeks(new Date(), Integer.parseInt(amount)); 133 } 134 } 135 return loanDueDate; 136 } 137 138 private Date handleGracePeriod(Date loanDueDate) { 139 Date updatedDate = null; 140 String gracePeriod = getGracePeriodForIncludingNonWorkingHours(); 141 if (StringUtils.isNotBlank(gracePeriod)) { 142 StringTokenizer stringTokenizer = new StringTokenizer(gracePeriod, "-"); 143 String amount = stringTokenizer.nextToken(); 144 String interval = stringTokenizer.nextToken(); 145 if (interval.equalsIgnoreCase("m")) { 146 updatedDate = DateUtils.addMinutes(loanDueDate, Integer.valueOf(amount)); 147 } else if (interval.equalsIgnoreCase("h")) { 148 updatedDate = DateUtils.addHours(loanDueDate, Integer.valueOf(amount)); 149 } 150 } else { 151 updatedDate = loanDueDate; 152 } 153 return updatedDate; 154 } 155 156 public Boolean includeNonWorkingHours() { 157 return ParameterValueResolver.getInstance().getParameterAsBoolean(OLEConstants 158 .APPL_ID, OLEConstants.DLVR_NMSPC, OLEConstants.DLVR_CMPNT, OLEConstants.CALENDER_FLAG); 159 } 160 161 162 private boolean compareTimes(Map<String, String> openTimeForTheGivenDay, Map<String, String> closingTimeForTheGivenDay, Date loanDueDate) { 163 Calendar closeTimeCalendar = resolveDateTime(closingTimeForTheGivenDay, loanDueDate); 164 Calendar openTimeCalendar = resolveDateTime(openTimeForTheGivenDay, loanDueDate); 165 //Compares for the givne day if the loan due time falls within the closing time 166 return (openTimeCalendar.getTime().compareTo(loanDueDate) <= 0 && closeTimeCalendar.getTime().compareTo(loanDueDate) >= 0); 167 } 168 169 private Calendar resolveDateTime(Map<String, String> closingTimeForTheGivenDay, Date loanDueDate) { 170 String time = closingTimeForTheGivenDay.keySet().iterator().next(); 171 String timeSession = closingTimeForTheGivenDay.get(time); 172 StringTokenizer timeTokenizer = new StringTokenizer(time, ":"); 173 int hour = Integer.parseInt(timeTokenizer.nextToken()); 174 175 Calendar instance = Calendar.getInstance(); 176 //The date is being set to the loan due date to ensure the comparisons are for the given day. 177 instance.setTime(loanDueDate); 178 //The hour and minutes are for closing times. 179 instance.set(Calendar.HOUR_OF_DAY, hour); 180 instance.set(Calendar.MINUTE, Integer.parseInt(timeTokenizer.nextToken())); 181 return instance; 182 } 183 184 185 public OleCalendar getActiveCalendar(Date date, String groupId) { 186 if (!getCalendarMap().containsKey(groupId)) { 187 List<OleCalendar> oleCalendarList = getOleCalendars(groupId); 188 for (OleCalendar calendar : oleCalendarList) { 189 if (calendarExists(new Timestamp(date.getTime()), calendar.getBeginDate(), calendar.getEndDate())) { 190 getCalendarMap().put(groupId, calendar); 191 } 192 } 193 } 194 return getCalendarMap().get(groupId); 195 } 196 197 protected List<OleCalendar> getOleCalendars(String groupId) { 198 HashMap criteriaMap = new HashMap(); 199 criteriaMap.put(OLEConstants.CALENDER_ID, groupId); 200 return (List<OleCalendar>) getBusinessObjectService().findMatching(OleCalendar.class, criteriaMap); 201 } 202 203 public boolean calendarExists(Timestamp date, Timestamp fromDate, Timestamp toDate) { 204 Interval interval; 205 if (null != fromDate) { 206 if (null != toDate) { 207 interval = new Interval(fromDate.getTime(), toDate.getTime()); 208 return interval.contains(date.getTime()); 209 } else { 210 return date.compareTo(fromDate) > 0 ? true : false; 211 } 212 } 213 214 return false; 215 } 216 217 public Map<String, OleCalendar> getCalendarMap() { 218 if (null == calendarMap) { 219 calendarMap = new HashMap<>(); 220 } 221 return calendarMap; 222 } 223 224 public void setCalendarMap(Map<String, OleCalendar> calendarMap) { 225 this.calendarMap = calendarMap; 226 } 227 228 public BusinessObjectService getBusinessObjectService() { 229 if (null == businessObjectService) { 230 businessObjectService = KRADServiceLocator.getBusinessObjectService(); 231 } 232 return businessObjectService; 233 } 234 235 public void setBusinessObjectService(BusinessObjectService businessObjectService) { 236 this.businessObjectService = businessObjectService; 237 } 238 239 public String getGracePeriodForIncludingNonWorkingHours() { 240 return ParameterValueResolver.getInstance().getParameter(OLEConstants 241 .APPL_ID, OLEConstants.DLVR_NMSPC, OLEConstants.DLVR_CMPNT, OLEConstants.GRACE_PERIOD_FOR_NON_WORKING_HOURS); 242 } 243 244 245 public String getPolicyId() { 246 return policyId; 247 } 248 249 public void setPolicyId(String policyId) { 250 this.policyId = policyId; 251 } 252 253 public boolean isLoanDueTimeWithinWorkingHours(Date loanDueDate, List<? extends OleBaseCalendarWeek> oleBaseCalendarWeekList) { 254 Map<String, Map<String, String>> openAndClosingTimeForTheGivenDay = getOpenAndClosingTimeForTheGivenDay(loanDueDate, oleBaseCalendarWeekList); 255 return compareTimes(openAndClosingTimeForTheGivenDay.get("openTime"), openAndClosingTimeForTheGivenDay.get("closeTime"), loanDueDate); 256 } 257 258 private Map<String, Map<String, String>> getOpenAndClosingTimeForTheGivenDay(Date loanDueDate, List<? extends OleBaseCalendarWeek> oleBaseCalendarWeekList) { 259 Map<String, Map<String, String>> openingAndClosingTimeMap; 260 261 openingAndClosingTimeMap = getOpenAndClosingTimeForTheGivenDayFromWeekList(loanDueDate, oleBaseCalendarWeekList); 262 263 return openingAndClosingTimeMap; 264 } 265 266 267 public Map<String, Map<String, String>> getOpenAndClosingTimeForTheGivenDayFromWeekList(Date loanDueDate, List<? extends OleBaseCalendarWeek> oleCalendarWeekList) { 268 int day = loanDueDate.getDay(); 269 Map<String, Map<String, String>> openingAndClosingTimeMap = new HashMap<>(); 270 Map<String, String> closingTimeMap = new HashMap<>(); 271 Map<String, String> openingTimeMap = new HashMap<>(); 272 273 for (Iterator<? extends OleBaseCalendarWeek> iterator = oleCalendarWeekList.iterator(); iterator.hasNext(); ) { 274 OleBaseCalendarWeek OleBaseCalendarWeek = iterator.next(); 275 String startDay = OleBaseCalendarWeek.getStartDay(); 276 if (startDay.equals(String.valueOf(day))) { 277 resolveOpenAndCloseTimesForCalendarWeek(closingTimeMap, openingTimeMap, OleBaseCalendarWeek); 278 break; 279 } 280 String endDay = OleBaseCalendarWeek.getEndDay(); 281 //The start day may not always be Sunday (0); Hence the check. 282 if (Integer.valueOf(startDay) < Integer.valueOf(endDay)) { 283 if (day > Integer.valueOf(startDay) && day <= Integer.valueOf(endDay)) { 284 resolveOpenAndCloseTimesForCalendarWeek(closingTimeMap, openingTimeMap, OleBaseCalendarWeek); 285 break; 286 } 287 } else { 288 if (day < Integer.valueOf(startDay) && day >= Integer.valueOf(endDay)) { 289 resolveOpenAndCloseTimesForCalendarWeek(closingTimeMap, openingTimeMap, OleBaseCalendarWeek); 290 break; 291 } 292 } 293 } 294 openingAndClosingTimeMap.put("openTime", openingTimeMap); 295 openingAndClosingTimeMap.put("closeTime", closingTimeMap); 296 return openingAndClosingTimeMap; 297 } 298 299 private void resolveOpenAndCloseTimesForCalendarWeek(Map<String, String> closingTimeMap, Map<String, String> openingTimeMap, OleBaseCalendarWeek oleBaseCalendarWeek) { 300 String closeTime = oleBaseCalendarWeek.getCloseTime(); 301 String closeTimeSession = oleBaseCalendarWeek.getCloseTimeSession(); 302 closingTimeMap.put(closeTime, closeTimeSession); 303 304 String openTime = oleBaseCalendarWeek.getOpenTime(); 305 String openTimeSession = oleBaseCalendarWeek.getOpenTimeSession(); 306 openingTimeMap.put(openTime, openTimeSession); 307 } 308 309 public OleCalendar getActiveCalendar() { 310 return activeCalendar; 311 } 312 313 public void setActiveCalendar(OleCalendar activeCalendar) { 314 this.activeCalendar = activeCalendar; 315 } 316}