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 }