001 /** 002 * Copyright 2004-2013 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.hr.time.detail.web; 017 018 import java.math.BigDecimal; 019 import java.sql.Date; 020 import java.sql.Timestamp; 021 import java.text.SimpleDateFormat; 022 import java.util.ArrayList; 023 import java.util.Collection; 024 import java.util.Collections; 025 import java.util.HashMap; 026 import java.util.HashSet; 027 import java.util.List; 028 import java.util.Map; 029 import java.util.Set; 030 import java.util.Map.Entry; 031 032 import javax.servlet.http.HttpServletRequest; 033 import javax.servlet.http.HttpServletResponse; 034 035 import org.apache.commons.lang.StringUtils; 036 import org.apache.commons.lang.time.DateUtils; 037 import org.apache.struts.action.ActionForm; 038 import org.apache.struts.action.ActionForward; 039 import org.apache.struts.action.ActionMapping; 040 import org.joda.time.DateTime; 041 import org.joda.time.Interval; 042 import org.kuali.hr.lm.LMConstants; 043 import org.kuali.hr.lm.accrual.AccrualCategory; 044 import org.kuali.hr.lm.accrual.AccrualCategoryRule; 045 import org.kuali.hr.lm.balancetransfer.BalanceTransfer; 046 import org.kuali.hr.lm.balancetransfer.validation.BalanceTransferValidationUtils; 047 import org.kuali.hr.lm.leaveSummary.LeaveSummary; 048 import org.kuali.hr.lm.leaveSummary.LeaveSummaryRow; 049 import org.kuali.hr.lm.leaveblock.LeaveBlock; 050 import org.kuali.hr.lm.leavecalendar.validation.LeaveCalendarValidationUtil; 051 import org.kuali.hr.lm.util.LeaveBlockAggregate; 052 import org.kuali.hr.time.assignment.Assignment; 053 import org.kuali.hr.time.calendar.Calendar; 054 import org.kuali.hr.time.calendar.CalendarEntries; 055 import org.kuali.hr.time.calendar.TkCalendar; 056 import org.kuali.hr.time.earncode.EarnCode; 057 import org.kuali.hr.time.principal.PrincipalHRAttributes; 058 import org.kuali.hr.time.roles.TkUserRoles; 059 import org.kuali.hr.time.roles.UserRoles; 060 import org.kuali.hr.time.service.base.TkServiceLocator; 061 import org.kuali.hr.time.timeblock.TimeBlock; 062 import org.kuali.hr.time.timeblock.TimeBlockHistory; 063 import org.kuali.hr.time.timesheet.TimesheetDocument; 064 import org.kuali.hr.time.timesheet.web.TimesheetAction; 065 import org.kuali.hr.time.timesheet.web.TimesheetActionForm; 066 import org.kuali.hr.time.timesummary.AssignmentRow; 067 import org.kuali.hr.time.timesummary.EarnCodeSection; 068 import org.kuali.hr.time.timesummary.EarnGroupSection; 069 import org.kuali.hr.time.timesummary.TimeSummary; 070 import org.kuali.hr.time.util.TKContext; 071 import org.kuali.hr.time.util.TKUser; 072 import org.kuali.hr.time.util.TKUtils; 073 import org.kuali.hr.time.util.TkConstants; 074 import org.kuali.hr.time.util.TkTimeBlockAggregate; 075 import org.kuali.hr.time.workflow.TimesheetDocumentHeader; 076 import org.kuali.rice.kew.service.KEWServiceLocator; 077 import org.kuali.rice.krad.exception.AuthorizationException; 078 import org.kuali.rice.krad.util.GlobalVariables; 079 import org.kuali.rice.krad.util.ObjectUtils; 080 081 public class TimeDetailAction extends TimesheetAction { 082 083 @Override 084 protected void checkTKAuthorization(ActionForm form, String methodToCall) throws AuthorizationException { 085 super.checkTKAuthorization(form, methodToCall); // Checks for read access first. 086 UserRoles roles = TkUserRoles.getUserRoles(GlobalVariables.getUserSession().getPrincipalId()); 087 TimesheetDocument doc = TKContext.getCurrentTimesheetDocument(); 088 089 // Check for write access to Timeblock. 090 if (StringUtils.equals(methodToCall, "addTimeBlock") || StringUtils.equals(methodToCall, "deleteTimeBlock") || StringUtils.equals(methodToCall, "updateTimeBlock")) { 091 if (!roles.isDocumentWritable(doc)) { 092 throw new AuthorizationException(roles.getPrincipalId(), "TimeDetailAction", ""); 093 } 094 } 095 } 096 097 @Override 098 public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { 099 ActionForward forward = super.execute(mapping, form, request, response); 100 if (forward.getRedirect()) { 101 return forward; 102 } 103 if (TKContext.getCurrentTimesheetDocument() == null) { 104 return forward; 105 } 106 TimeDetailActionForm tdaf = (TimeDetailActionForm) form; 107 tdaf.setAssignmentDescriptions(TkServiceLocator.getAssignmentService().getAssignmentDescriptions(TKContext.getCurrentTimesheetDocument(), false)); 108 109 // Handle User preference / timezone information (pushed up from TkCalendar to avoid duplication) 110 // Set calendar 111 CalendarEntries payCalendarEntry = tdaf.getPayCalendarDates(); 112 Calendar payCalendar = TkServiceLocator.getCalendarService().getCalendar(payCalendarEntry != null ? payCalendarEntry.getHrCalendarId() : null); 113 114 //List<TimeBlock> timeBlocks = TkServiceLocator.getTimeBlockService().getTimeBlocks(Long.parseLong(tdaf.getTimesheetDocument().getDocumentHeader().getTimesheetDocumentId())); 115 List<TimeBlock> timeBlocks = TKContext.getCurrentTimesheetDocument().getTimeBlocks(); 116 // get leave blocks 117 List<Assignment> timeAssignments = TKContext.getCurrentTimesheetDocument().getAssignments(); 118 List<String> tAssignmentKeys = new ArrayList<String>(); 119 for(Assignment assign : timeAssignments) { 120 tAssignmentKeys.add(assign.getAssignmentKey()); 121 } 122 List<LeaveBlock> leaveBlocks = TkServiceLocator.getLeaveBlockService().getLeaveBlocksForTimeCalendar(TKContext.getCurrentTimesheetDocument().getPrincipalId(), 123 payCalendarEntry.getBeginPeriodDate(), payCalendarEntry.getEndPeriodDate(), tAssignmentKeys); 124 List<LeaveBlock> balanceTransferLeaveBlocks = TkServiceLocator.getLeaveBlockService().getLeaveBlocksWithType(TKContext.getCurrentTimesheetDocument().getPrincipalId(), 125 payCalendarEntry.getBeginPeriodDate(), payCalendarEntry.getEndPeriodDate(), LMConstants.LEAVE_BLOCK_TYPE.BALANCE_TRANSFER); 126 127 // List<String> warnings = tdaf.getWarnings(); 128 /* List<String> infoMessages = tdaf.getInfoMessages(); 129 List<String> warningMessages = tdaf.getWarningMessages(); 130 List<String> actionMessages = tdaf.getActionMessages();*/ 131 132 Map<String, Set<String>> allMessages = LeaveCalendarValidationUtil.getWarningMessagesForLeaveBlocks(balanceTransferLeaveBlocks); 133 Map<String, Set<String>> transactionalMessages = LeaveCalendarValidationUtil.validatePendingTransactions(TKContext.getTargetPrincipalId(), 134 payCalendarEntry.getBeginPeriodDate(), payCalendarEntry.getEndPeriodDate()); 135 136 List<String> warnings = new ArrayList<String>(); 137 //placing the following "validation" further down in this method will overwrite messages added prior to this call. 138 //allMessages.putAll(LeaveCalendarValidationUtil.validatePendingTransactions(viewPrincipal, payCalendarEntry.getBeginPeriodDate(), payCalendarEntry.getEndPeriodDate())); 139 140 // add warning messages based on max carry over balances for each accrual category for non-exempt leave users 141 String viewPrincipal = TKContext.getTargetPrincipalId(); 142 List<BalanceTransfer> losses = new ArrayList<BalanceTransfer>(); 143 if (TkServiceLocator.getLeaveApprovalService().isActiveAssignmentFoundOnJobFlsaStatus(viewPrincipal, TkConstants.FLSA_STATUS_NON_EXEMPT, true)) { 144 PrincipalHRAttributes principalCalendar = TkServiceLocator.getPrincipalHRAttributeService().getPrincipalCalendar(viewPrincipal, payCalendarEntry.getEndPeriodDate()); 145 146 Interval calendarInterval = new Interval(payCalendarEntry.getBeginPeriodDate().getTime(), payCalendarEntry.getEndPeriodDate().getTime()); 147 Map<String,Set<LeaveBlock>> maxBalInfractions = new HashMap<String,Set<LeaveBlock>>(); 148 149 if(ObjectUtils.isNotNull(principalCalendar)) { 150 maxBalInfractions = TkServiceLocator.getAccrualCategoryMaxBalanceService().getMaxBalanceViolations(payCalendarEntry, viewPrincipal); 151 152 for(Entry<String,Set<LeaveBlock>> entry : maxBalInfractions.entrySet()) { 153 for(LeaveBlock lb : entry.getValue()) { 154 if(calendarInterval.contains(lb.getLeaveDate().getTime())) { 155 AccrualCategory accrualCat = TkServiceLocator.getAccrualCategoryService().getAccrualCategory(lb.getAccrualCategory(), lb.getLeaveDate()); 156 AccrualCategoryRule aRule = TkServiceLocator.getAccrualCategoryRuleService().getAccrualCategoryRule(lb.getAccrualCategoryRuleId()); 157 if(StringUtils.equals(aRule.getActionAtMaxBalance(),LMConstants.ACTION_AT_MAX_BAL.LOSE)) { 158 DateTime aDate = null; 159 if(StringUtils.equals(aRule.getMaxBalanceActionFrequency(), LMConstants.MAX_BAL_ACTION_FREQ.YEAR_END)) { 160 aDate = TkServiceLocator.getLeavePlanService().getRolloverDayOfLeavePlan(principalCalendar.getLeavePlan(), lb.getLeaveDate()); 161 } 162 else { 163 Calendar cal = TkServiceLocator.getCalendarService().getCalendarByPrincipalIdAndDate(viewPrincipal, lb.getLeaveDate(), true); 164 CalendarEntries leaveEntry = TkServiceLocator.getCalendarEntriesService().getCurrentCalendarEntriesByCalendarId(cal.getHrCalendarId(), lb.getLeaveDate()); 165 aDate = new DateTime(leaveEntry.getEndPeriodDate()); 166 } 167 aDate = aDate.minusDays(1); 168 if(calendarInterval.contains(aDate.getMillis()) && aDate.toDate().compareTo(payCalendarEntry.getEndPeriodDate()) <= 0) { 169 //may want to calculate summary for all rows, displayable or not, and determine displayability via tags. 170 AccrualCategory accrualCategory = TkServiceLocator.getAccrualCategoryService().getAccrualCategory(aRule.getLmAccrualCategoryId()); 171 BigDecimal accruedBalance = TkServiceLocator.getAccrualCategoryService().getAccruedBalanceForPrincipal(viewPrincipal, accrualCategory, lb.getLeaveDate()); 172 173 BalanceTransfer loseTransfer = TkServiceLocator.getBalanceTransferService().initializeTransfer(viewPrincipal, lb.getAccrualCategoryRuleId(), accruedBalance, lb.getLeaveDate()); 174 boolean valid = BalanceTransferValidationUtils.validateTransfer(loseTransfer); 175 if(valid) 176 losses.add(loseTransfer); 177 } 178 } 179 180 181 // accrual categories within the leave plan that are hidden from the leave summary WILL appear. 182 String message = "You have exceeded the maximum balance limit for '" + accrualCat.getAccrualCategory() + "' as of " + lb.getLeaveDate() + ". "+ 183 "Depending upon the accrual category rules, leave over this limit may be forfeited."; 184 // leave blocks are sorted in getMaxBalanceViolations() method, so we just take the one with the earliest leave date for an accrual category. 185 if(!StringUtils.contains(allMessages.get("warningMessages").toString(),"You have exceeded the maximum balance limit for '"+accrualCat.getAccrualCategory())) { 186 allMessages.get("warningMessages").add(message); 187 } 188 } 189 } 190 } 191 } 192 tdaf.setForfeitures(losses); 193 194 if (principalCalendar != null) { 195 Calendar calendar = TkServiceLocator.getCalendarService().getCalendarByPrincipalIdAndDate(viewPrincipal, tdaf.getEndPeriodDateTime(), true); 196 197 if (calendar != null) { 198 List<CalendarEntries> leaveCalendarEntries = TkServiceLocator.getCalendarEntriesService().getCalendarEntriesEndingBetweenBeginAndEndDate(calendar.getHrCalendarId(), tdaf.getBeginPeriodDateTime(), tdaf.getEndPeriodDateTime()); 199 200 List<AccrualCategory> accrualCategories = TkServiceLocator.getAccrualCategoryService().getActiveLeaveAccrualCategoriesForLeavePlan(principalCalendar.getLeavePlan(), new java.sql.Date(tdaf.getEndPeriodDateTime().getTime())); 201 for (AccrualCategory accrualCategory : accrualCategories) { 202 if (TkServiceLocator.getAccrualCategoryMaxCarryOverService().exceedsAccrualCategoryMaxCarryOver(accrualCategory.getAccrualCategory(), viewPrincipal, leaveCalendarEntries, tdaf.getEndPeriodDateTime())) { 203 String message = "Your pending leave balance is greater than the annual max carry over for accrual category '" + accrualCategory.getAccrualCategory() + "' and upon approval, the excess balance will be lost."; 204 if (!warnings.contains(message)) { 205 warnings.add(message); 206 } 207 } 208 } 209 } 210 } 211 } 212 /* warnings.addAll(allMessages.get("infoMessages")); 213 warnings.addAll(allMessages.get("actionMessages")); 214 warnings.addAll(allMessages.get("warningMessages"));*/ 215 allMessages.get("warningMessages").addAll(warnings); 216 217 List<String> infoMessages = tdaf.getInfoMessages(); 218 infoMessages.addAll(allMessages.get("infoMessages")); 219 infoMessages.addAll(transactionalMessages.get("infoMessages")); 220 221 List<String> warningMessages = tdaf.getWarningMessages(); 222 warningMessages.addAll(allMessages.get("warningMessages")); 223 warningMessages.addAll(transactionalMessages.get("warningMessages")); 224 225 List<String> actionMessages = tdaf.getActionMessages(); 226 actionMessages.addAll(allMessages.get("actionMessages")); 227 actionMessages.addAll(transactionalMessages.get("actionMessages")); 228 229 tdaf.setInfoMessages(infoMessages); 230 tdaf.setWarningMessages(warningMessages); 231 tdaf.setActionMessages(actionMessages); 232 233 this.assignStypeClassMapForTimeSummary(tdaf,timeBlocks, leaveBlocks); 234 235 List<Interval> intervals = TKUtils.getFullWeekDaySpanForCalendarEntry(payCalendarEntry); 236 LeaveBlockAggregate lbAggregate = new LeaveBlockAggregate(leaveBlocks, payCalendarEntry, intervals); 237 TkTimeBlockAggregate tbAggregate = new TkTimeBlockAggregate(timeBlocks, payCalendarEntry, payCalendar, true,intervals); 238 // use both time aggregate and leave aggregate to populate the calendar 239 TkCalendar cal = TkCalendar.getCalendar(tbAggregate, lbAggregate); 240 cal.assignAssignmentStyle(tdaf.getAssignStyleClassMap()); 241 tdaf.setTkCalendar(cal); 242 243 this.populateCalendarAndPayPeriodLists(request, tdaf); 244 245 tdaf.setTimeBlockString(ActionFormUtils.getTimeBlocksJson(tbAggregate.getFlattenedTimeBlockList())); 246 tdaf.setLeaveBlockString(ActionFormUtils.getLeaveBlocksJson(lbAggregate.getFlattenedLeaveBlockList())); 247 248 tdaf.setOvertimeEarnCodes(TkServiceLocator.getEarnCodeService().getOvertimeEarnCodesStrs(TKContext.getCurrentTimesheetDocument().getAsOfDate())); 249 250 if (StringUtils.equals(TKContext.getCurrentTimesheetDocument().getPrincipalId(), GlobalVariables.getUserSession().getPrincipalId())) { 251 tdaf.setWorkingOnItsOwn("true"); 252 } 253 254 tdaf.setDocEditable("false"); 255 if (TKUser.isSystemAdmin()) { 256 tdaf.setDocEditable("true"); 257 } else { 258 boolean docFinal = TKContext.getCurrentTimesheetDocument().getDocumentHeader().getDocumentStatus().equals(TkConstants.ROUTE_STATUS.FINAL); 259 if (!docFinal) { 260 if(StringUtils.equals(TKContext.getCurrentTimesheetDocument().getPrincipalId(), GlobalVariables.getUserSession().getPrincipalId()) 261 || TKUser.isSystemAdmin() 262 || TKUser.isLocationAdmin() 263 || TKUser.isDepartmentAdmin() 264 || TKUser.isReviewer() 265 || TKUser.isApprover()) { 266 tdaf.setDocEditable("true"); 267 } 268 269 //if the timesheet has been approved by at least one of the approvers, the employee should not be able to edit it 270 if (StringUtils.equals(TKContext.getCurrentTimesheetDocument().getPrincipalId(), GlobalVariables.getUserSession().getPrincipalId()) 271 && TKContext.getCurrentTimesheetDocument().getDocumentHeader().getDocumentStatus().equals(TkConstants.ROUTE_STATUS.ENROUTE)) { 272 Collection actions = KEWServiceLocator.getActionTakenService().findByDocIdAndAction(TKContext.getCurrentTimesheetDocument().getDocumentHeader().getDocumentId(), TkConstants.DOCUMENT_ACTIONS.APPROVE); 273 if(!actions.isEmpty()) { 274 tdaf.setDocEditable("false"); 275 } 276 } 277 } 278 } 279 280 return forward; 281 } 282 283 // use lists of time blocks and leave blocks to build the style class map and assign css class to associated summary rows 284 private void assignStypeClassMapForTimeSummary(TimeDetailActionForm tdaf, List<TimeBlock> timeBlocks, List<LeaveBlock> leaveBlocks) throws Exception { 285 TimeSummary ts = TkServiceLocator.getTimeSummaryService().getTimeSummary(TKContext.getCurrentTimesheetDocument()); 286 tdaf.setAssignStyleClassMap(ActionFormUtils.buildAssignmentStyleClassMap(timeBlocks, leaveBlocks)); 287 Map<String, String> aMap = tdaf.getAssignStyleClassMap(); 288 // set css classes for each assignment row 289 for (EarnGroupSection earnGroupSection : ts.getSections()) { 290 for (EarnCodeSection section : earnGroupSection.getEarnCodeSections()) { 291 for (AssignmentRow assignRow : section.getAssignmentsRows()) { 292 if (assignRow.getAssignmentKey() != null && aMap.containsKey(assignRow.getAssignmentKey())) { 293 assignRow.setCssClass(aMap.get(assignRow.getAssignmentKey())); 294 } else { 295 assignRow.setCssClass(""); 296 } 297 } 298 } 299 300 } 301 tdaf.setTimeSummary(ts); 302 // ActionFormUtils.validateHourLimit(tdaf); 303 ActionFormUtils.addWarningTextFromEarnGroup(tdaf); 304 ActionFormUtils.addUnapprovedIPWarningFromClockLog(tdaf); 305 } 306 307 private void populateCalendarAndPayPeriodLists(HttpServletRequest request, TimeDetailActionForm tdaf) { 308 String viewPrincipal = TKContext.getTargetPrincipalId(); 309 List<TimesheetDocumentHeader> documentHeaders = (List<TimesheetDocumentHeader>) TkServiceLocator.getTimesheetDocumentHeaderService().getDocumentHeadersForPrincipalId(TKContext.getTargetPrincipalId()); 310 SimpleDateFormat sdf = new SimpleDateFormat("yyyy"); 311 if(tdaf.getCalendarYears().isEmpty()) { 312 // get calendar year drop down list contents 313 Set<String> yearSet = new HashSet<String>(); 314 315 for(TimesheetDocumentHeader tdh : documentHeaders) { 316 yearSet.add(sdf.format(tdh.getBeginDate())); 317 } 318 List<String> yearList = new ArrayList<String>(yearSet); 319 Collections.sort(yearList); 320 Collections.reverse(yearList); // newest on top 321 tdaf.setCalendarYears(yearList); 322 } 323 // if selected calendar year is passed in 324 if(request.getParameter("selectedCY")!= null) { 325 tdaf.setSelectedCalendarYear(request.getParameter("selectedCY").toString()); 326 } 327 // if there is no selected calendr year, use the year of current pay calendar entry 328 if(StringUtils.isEmpty(tdaf.getSelectedCalendarYear())) { 329 tdaf.setSelectedCalendarYear(sdf.format(tdaf.getPayCalendarDates().getBeginPeriodDate())); 330 } 331 if(tdaf.getPayPeriodsMap().isEmpty()) { 332 List<CalendarEntries> payPeriodList = new ArrayList<CalendarEntries>(); 333 for(TimesheetDocumentHeader tdh : documentHeaders) { 334 if(sdf.format(tdh.getBeginDate()).equals(tdaf.getSelectedCalendarYear())) { 335 CalendarEntries pe = TkServiceLocator.getCalendarService().getCalendarDatesByPayEndDate(tdh.getPrincipalId(), tdh.getEndDate(), TkConstants.PAY_CALENDAR_TYPE); 336 //CalendarEntries pe = TkServiceLocator.getCalendarEntriesService().getCalendarEntriesByBeginAndEndDate(tdh.getBeginDate(), tdh.getEndDate()); 337 payPeriodList.add(pe); 338 } 339 } 340 tdaf.setPayPeriodsMap(ActionFormUtils.getPayPeriodsMap(payPeriodList, viewPrincipal)); 341 } 342 if(request.getParameter("selectedPP")!= null) { 343 tdaf.setSelectedPayPeriod(request.getParameter("selectedPP").toString()); 344 } 345 if(StringUtils.isEmpty(tdaf.getSelectedPayPeriod())) { 346 tdaf.setSelectedPayPeriod(tdaf.getPayCalendarDates().getHrCalendarEntriesId()); 347 } 348 } 349 350 351 /** 352 * This method involves creating an object-copy of every TimeBlock on the 353 * time sheet for overtime re-calculation. 354 * 355 * @throws Exception 356 */ 357 public ActionForward deleteTimeBlock(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { 358 TimeDetailActionForm tdaf = (TimeDetailActionForm) form; 359 //Grab timeblock to be deleted from form 360 List<TimeBlock> timeBlocks = tdaf.getTimesheetDocument().getTimeBlocks(); 361 TimeBlock deletedTimeBlock = null; 362 for (TimeBlock tb : timeBlocks) { 363 if (tb.getTkTimeBlockId().compareTo(tdaf.getTkTimeBlockId()) == 0) { 364 deletedTimeBlock = tb; 365 break; 366 } 367 } 368 if (deletedTimeBlock == null) { 369 return mapping.findForward("basic"); 370 } 371 //Remove from the list of timeblocks 372 List<TimeBlock> referenceTimeBlocks = new ArrayList<TimeBlock>(tdaf.getTimesheetDocument().getTimeBlocks().size()); 373 for (TimeBlock b : tdaf.getTimesheetDocument().getTimeBlocks()) { 374 referenceTimeBlocks.add(b.copy()); 375 } 376 377 // simple pointer, for clarity 378 List<TimeBlock> newTimeBlocks = tdaf.getTimesheetDocument().getTimeBlocks(); 379 newTimeBlocks.remove(deletedTimeBlock); 380 381 //Delete timeblock 382 TkServiceLocator.getTimeBlockService().deleteTimeBlock(deletedTimeBlock); 383 // Add a row to the history table 384 TimeBlockHistory tbh = new TimeBlockHistory(deletedTimeBlock); 385 tbh.setActionHistory(TkConstants.ACTIONS.DELETE_TIME_BLOCK); 386 TkServiceLocator.getTimeBlockHistoryService().saveTimeBlockHistory(tbh); 387 //reset time block 388 TkServiceLocator.getTimesheetService().resetTimeBlock(newTimeBlocks); 389 TkServiceLocator.getTkRuleControllerService().applyRules(TkConstants.ACTIONS.ADD_TIME_BLOCK, newTimeBlocks, tdaf.getPayCalendarDates(), tdaf.getTimesheetDocument(), TKContext.getPrincipalId()); 390 TkServiceLocator.getTimeBlockService().saveTimeBlocks(referenceTimeBlocks, newTimeBlocks, TKContext.getPrincipalId()); 391 392 return mapping.findForward("basic"); 393 } 394 395 /** 396 * This method involves creating an object-copy of every TimeBlock on the 397 * time sheet for overtime re-calculation. 398 * Based on the form's timeBlockId or leaveBlockId, existing Time/Leave blocks will be deleted and new ones created 399 * 400 * @throws Exception 401 */ 402 public ActionForward addTimeBlock(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { 403 TimeDetailActionForm tdaf = (TimeDetailActionForm) form; 404 405 if(StringUtils.isNotEmpty(tdaf.getTkTimeBlockId())) { 406 // the user is changing an existing time block, so need to delete this time block 407 this.removeOldTimeBlock(tdaf); 408 } else if(StringUtils.isNotEmpty(tdaf.getLmLeaveBlockId())) { 409 // the user is changing an existing leave block, so need to delete this leave block 410 this.removeOldLeaveBlock(tdaf.getLmLeaveBlockId()); 411 } 412 if(StringUtils.isNotEmpty(tdaf.getSelectedEarnCode())) { 413 EarnCode ec = TkServiceLocator.getEarnCodeService().getEarnCode(tdaf.getSelectedEarnCode(), tdaf.getTimesheetDocument().getAsOfDate()); 414 if(ec != null && (ec.getLeavePlan() != null || ec.getEligibleForAccrual().equals("N"))) { // leave blocks changes 415 this.changeLeaveBlocks(tdaf); 416 } else { // time blocks changes 417 this.changeTimeBlocks(tdaf); 418 } 419 } 420 421 // ActionFormUtils.validateHourLimit(tdaf); 422 ActionFormUtils.addWarningTextFromEarnGroup(tdaf); 423 424 return mapping.findForward("basic"); 425 } 426 427 private void removeOldTimeBlock(TimeDetailActionForm tdaf) { 428 if (tdaf.getTkTimeBlockId() != null) { 429 TimeBlock tb = TkServiceLocator.getTimeBlockService().getTimeBlock(tdaf.getTkTimeBlockId()); 430 if (tb != null) { 431 TimeBlockHistory tbh = new TimeBlockHistory(tb); 432 TkServiceLocator.getTimeBlockService().deleteTimeBlock(tb); 433 434 // mark the original timeblock as deleted in the history table 435 tbh.setActionHistory(TkConstants.ACTIONS.DELETE_TIME_BLOCK); 436 TkServiceLocator.getTimeBlockHistoryService().saveTimeBlockHistory(tbh); 437 438 // delete the timeblock from the memory 439 tdaf.getTimesheetDocument().getTimeBlocks().remove(tb); 440 } 441 } 442 } 443 444 private void removeOldLeaveBlock(String lbId) { 445 if (lbId != null) { 446 LeaveBlock lb = TkServiceLocator.getLeaveBlockService().getLeaveBlock(lbId); 447 if (lb != null) { 448 TkServiceLocator.getLeaveBlockService().deleteLeaveBlock(lbId, TKContext.getPrincipalId()); 449 } 450 } 451 } 452 453 // add/update leave blocks 454 private void changeLeaveBlocks(TimeDetailActionForm tdaf) { 455 DateTime beginDate = null; 456 DateTime endDate = null; 457 458 if(tdaf.getStartTime() != null && tdaf.getEndTime() != null) { 459 beginDate = new DateTime(TKUtils.convertDateStringToTimestamp(tdaf.getStartDate(), tdaf.getStartTime())); 460 endDate = new DateTime(TKUtils.convertDateStringToTimestamp(tdaf.getEndDate(), tdaf.getEndTime())); 461 } else { 462 beginDate = new DateTime(TKUtils.convertDateStringToTimestamp(tdaf.getStartDate())); 463 endDate = new DateTime(TKUtils.convertDateStringToTimestamp(tdaf.getEndDate())); 464 } 465 466 String selectedEarnCode = tdaf.getSelectedEarnCode(); 467 BigDecimal leaveAmount = tdaf.getLeaveAmount(); 468 469 String desc = ""; // there's no description field in time calendar pop window 470 String spanningWeeks = tdaf.getSpanningWeeks(); 471 Assignment assignment = TkServiceLocator.getAssignmentService().getAssignment(tdaf.getTimesheetDocument(), tdaf.getSelectedAssignment()); 472 TkServiceLocator.getLeaveBlockService().addLeaveBlocks(beginDate, endDate, tdaf.getPayCalendarDates(), selectedEarnCode, leaveAmount, desc, assignment, 473 spanningWeeks, LMConstants.LEAVE_BLOCK_TYPE.TIME_CALENDAR, TKContext.getTargetPrincipalId()); 474 } 475 476 // add/update time blocks 477 private void changeTimeBlocks(TimeDetailActionForm tdaf) { 478 Timestamp overtimeBeginTimestamp = null; 479 Timestamp overtimeEndTimestamp = null; 480 boolean isClockLogCreated = false; 481 482 // This is for updating a timeblock or changing 483 // If tkTimeBlockId is not null and the new timeblock is valid, delete the existing timeblock and a new one will be created after submitting the form. 484 if (tdaf.getTkTimeBlockId() != null) { 485 TimeBlock tb = TkServiceLocator.getTimeBlockService().getTimeBlock(tdaf.getTkTimeBlockId()); 486 if (tb != null) { 487 isClockLogCreated = tb.getClockLogCreated(); 488 if (StringUtils.isNotEmpty(tdaf.getOvertimePref())) { 489 //TODO: This doesn't do anything!!! these variables are never used. Should they be? 490 overtimeBeginTimestamp = tb.getBeginTimestamp(); 491 overtimeEndTimestamp = tb.getEndTimestamp(); 492 } 493 } 494 // old time block is deleted from addTimeBlock method 495 // this.removeOldTimeBlock(tdaf); 496 } 497 498 Assignment assignment = TkServiceLocator.getAssignmentService().getAssignment(tdaf.getTimesheetDocument(), tdaf.getSelectedAssignment()); 499 500 501 // Surgery point - Need to construct a Date/Time with Appropriate Timezone. 502 Timestamp startTime = TKUtils.convertDateStringToTimestamp(tdaf.getStartDate(), tdaf.getStartTime()); 503 Timestamp endTime = TKUtils.convertDateStringToTimestamp(tdaf.getEndDate(), tdaf.getEndTime()); 504 505 // We need a cloned reference set so we know whether or not to 506 // persist any potential changes without making hundreds of DB calls. 507 List<TimeBlock> referenceTimeBlocks = new ArrayList<TimeBlock>(tdaf.getTimesheetDocument().getTimeBlocks().size()); 508 for (TimeBlock tb : tdaf.getTimesheetDocument().getTimeBlocks()) { 509 referenceTimeBlocks.add(tb.copy()); 510 } 511 512 // This is just a reference, for code clarity, the above list is actually 513 // separate at the object level. 514 List<TimeBlock> newTimeBlocks = tdaf.getTimesheetDocument().getTimeBlocks(); 515 DateTime startTemp = new DateTime(startTime); 516 DateTime endTemp = new DateTime(endTime); 517 // KPME-1446 add spanningweeks to the calls below 518 if (StringUtils.equals(tdaf.getAcrossDays(), "y") 519 && !(endTemp.getDayOfYear() - startTemp.getDayOfYear() <= 1 520 && endTemp.getHourOfDay() == 0)) { 521 List<TimeBlock> timeBlocksToAdd = TkServiceLocator.getTimeBlockService().buildTimeBlocksSpanDates(assignment, 522 tdaf.getSelectedEarnCode(), tdaf.getTimesheetDocument(), startTime, 523 endTime, tdaf.getHours(), tdaf.getAmount(), isClockLogCreated, Boolean.parseBoolean(tdaf.getLunchDeleted()), tdaf.getSpanningWeeks(), TKContext.getPrincipalId()); 524 for (TimeBlock tb : timeBlocksToAdd) { 525 if (!newTimeBlocks.contains(tb)) { 526 newTimeBlocks.add(tb); 527 } 528 } 529 } else { 530 List<TimeBlock> timeBlocksToAdd = TkServiceLocator.getTimeBlockService().buildTimeBlocks(assignment, 531 tdaf.getSelectedEarnCode(), tdaf.getTimesheetDocument(), startTime, 532 endTime, tdaf.getHours(), tdaf.getAmount(), isClockLogCreated, Boolean.parseBoolean(tdaf.getLunchDeleted()), TKContext.getPrincipalId()); 533 for (TimeBlock tb : timeBlocksToAdd) { 534 if (!newTimeBlocks.contains(tb)) { 535 newTimeBlocks.add(tb); 536 } 537 } 538 } 539 540 //reset time block 541 TkServiceLocator.getTimesheetService().resetTimeBlock(newTimeBlocks); 542 543 // apply overtime pref 544 // I changed start and end times comparison below. it used to be overtimeBeginTimestamp and overtimeEndTimestamp but 545 // for some reason, they're always null because, we have removed the time block before getting here. KPME-2162 546 for (TimeBlock tb : newTimeBlocks) { 547 if (tb.getBeginTimestamp().equals(startTime) && tb.getEndTimestamp().equals(endTime) && StringUtils.isNotEmpty(tdaf.getOvertimePref())) { 548 tb.setOvertimePref(tdaf.getOvertimePref()); 549 } 550 551 } 552 553 TkServiceLocator.getTkRuleControllerService().applyRules(TkConstants.ACTIONS.ADD_TIME_BLOCK, newTimeBlocks, tdaf.getPayCalendarDates(), tdaf.getTimesheetDocument(), TKContext.getPrincipalId()); 554 TkServiceLocator.getTimeBlockService().saveTimeBlocks(referenceTimeBlocks, newTimeBlocks, TKContext.getPrincipalId()); 555 } 556 557 public ActionForward updateTimeBlock(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { 558 559 TimeDetailActionForm tdaf = (TimeDetailActionForm) form; 560 Assignment assignment = TkServiceLocator.getAssignmentService().getAssignment(tdaf.getTimesheetDocument(), tdaf.getSelectedAssignment()); 561 562 //Grab timeblock to be updated from form 563 List<TimeBlock> timeBlocks = tdaf.getTimesheetDocument().getTimeBlocks(); 564 TimeBlock updatedTimeBlock = null; 565 for (TimeBlock tb : timeBlocks) { 566 if (tb.getTkTimeBlockId().compareTo(tdaf.getTkTimeBlockId()) == 0) { 567 updatedTimeBlock = tb; 568 tb.setJobNumber(assignment.getJobNumber()); 569 tb.setWorkArea(assignment.getWorkArea()); 570 tb.setTask(assignment.getTask()); 571 break; 572 } 573 } 574 575 TkServiceLocator.getTimeBlockService().updateTimeBlock(updatedTimeBlock); 576 577 TimeBlockHistory tbh = new TimeBlockHistory(updatedTimeBlock); 578 tbh.setActionHistory(TkConstants.ACTIONS.UPDATE_TIME_BLOCK); 579 TkServiceLocator.getTimeBlockHistoryService().saveTimeBlockHistory(tbh); 580 tdaf.setMethodToCall("addTimeBlock"); 581 return mapping.findForward("basic"); 582 } 583 584 585 public ActionForward actualTimeInquiry(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { 586 return mapping.findForward("ati"); 587 } 588 589 public ActionForward deleteLunchDeduction(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { 590 591 TimeDetailActionForm tdaf = (TimeDetailActionForm) form; 592 String timeHourDetailId = tdaf.getTkTimeHourDetailId(); 593 TkServiceLocator.getTimeBlockService().deleteLunchDeduction(timeHourDetailId); 594 595 List<TimeBlock> newTimeBlocks = tdaf.getTimesheetDocument().getTimeBlocks(); 596 597 TkServiceLocator.getTimesheetService().resetTimeBlock(newTimeBlocks); 598 599 // KPME-1340 600 TkServiceLocator.getTkRuleControllerService().applyRules(TkConstants.ACTIONS.ADD_TIME_BLOCK, newTimeBlocks, tdaf.getPayCalendarDates(), tdaf.getTimesheetDocument(), TKContext.getPrincipalId()); 601 TkServiceLocator.getTimeBlockService().saveTimeBlocks(newTimeBlocks); 602 TKContext.getCurrentTimesheetDocument().setTimeBlocks(newTimeBlocks); 603 604 return mapping.findForward("basic"); 605 } 606 607 public ActionForward gotoCurrentPayPeriod(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { 608 String viewPrincipal = TKUser.getCurrentTargetPersonId(); 609 Date currentDate = TKUtils.getTimelessDate(null); 610 CalendarEntries pce = TkServiceLocator.getCalendarService().getCurrentCalendarDates(viewPrincipal, currentDate); 611 TimesheetDocument td = TkServiceLocator.getTimesheetService().openTimesheetDocument(viewPrincipal, pce); 612 setupDocumentOnFormContext((TimesheetActionForm)form, td); 613 return mapping.findForward("basic"); 614 } 615 616 //Triggered by changes of pay period drop down list, reload the whole page based on the selected pay period 617 public ActionForward changeCalendarYear(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { 618 619 TimeDetailActionForm tdaf = (TimeDetailActionForm) form; 620 if(request.getParameter("selectedCY") != null) { 621 tdaf.setSelectedCalendarYear(request.getParameter("selectedCY").toString()); 622 } 623 return mapping.findForward("basic"); 624 } 625 626 //Triggered by changes of pay period drop down list, reload the whole page based on the selected pay period 627 public ActionForward changePayPeriod(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { 628 TimeDetailActionForm tdaf = (TimeDetailActionForm) form; 629 if(request.getParameter("selectedPP") != null) { 630 tdaf.setSelectedPayPeriod(request.getParameter("selectedPP").toString()); 631 CalendarEntries pce = TkServiceLocator.getCalendarEntriesService() 632 .getCalendarEntries(request.getParameter("selectedPP").toString()); 633 if(pce != null) { 634 String viewPrincipal = TKUser.getCurrentTargetPersonId(); 635 TimesheetDocument td = TkServiceLocator.getTimesheetService().openTimesheetDocument(viewPrincipal, pce); 636 setupDocumentOnFormContext((TimesheetActionForm)form, td); 637 } 638 } 639 return mapping.findForward("basic"); 640 } 641 642 public ActionForward deleteLeaveBlock(ActionMapping mapping,ActionForm form, HttpServletRequest request, 643 HttpServletResponse response) throws Exception { 644 TimeDetailActionForm tdaf = (TimeDetailActionForm) form; 645 String leaveBlockId = tdaf.getLmLeaveBlockId(); 646 647 LeaveBlock blockToDelete = TkServiceLocator.getLeaveBlockService().getLeaveBlock(leaveBlockId); 648 if (blockToDelete != null && TkServiceLocator.getPermissionsService().canDeleteLeaveBlock(blockToDelete)) { 649 TkServiceLocator.getLeaveBlockService().deleteLeaveBlock(leaveBlockId, TKContext.getPrincipalId()); 650 } 651 652 // if the leave block is NOT eligible for accrual, rerun accrual service for the leave calendar the leave block is on 653 EarnCode ec = TkServiceLocator.getEarnCodeService().getEarnCode(blockToDelete.getEarnCode(), blockToDelete.getLeaveDate()); 654 if(ec != null && ec.getEligibleForAccrual().equals("N")) { 655 CalendarEntries ce = TkServiceLocator.getCalendarService() 656 .getCurrentCalendarDatesForLeaveCalendar(blockToDelete.getPrincipalId(), blockToDelete.getLeaveDate()); 657 if(ce != null) { 658 TkServiceLocator.getLeaveAccrualService().runAccrual(blockToDelete.getPrincipalId(), ce.getBeginPeriodDate(), ce.getEndPeriodDate(), false); 659 } 660 } 661 662 return mapping.findForward("basic"); 663 } 664 665 }