001 /**
002 * Copyright 2004-2012 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.sql.Date;
019 import java.sql.Timestamp;
020 import java.text.SimpleDateFormat;
021 import java.util.ArrayList;
022 import java.util.Collection;
023 import java.util.Collections;
024 import java.util.HashSet;
025 import java.util.List;
026 import java.util.Map;
027 import java.util.Set;
028
029 import javax.servlet.http.HttpServletRequest;
030 import javax.servlet.http.HttpServletResponse;
031
032 import org.apache.commons.lang.StringUtils;
033 import org.apache.struts.action.ActionForm;
034 import org.apache.struts.action.ActionForward;
035 import org.apache.struts.action.ActionMapping;
036 import org.joda.time.DateTime;
037 import org.joda.time.Interval;
038 import org.kuali.hr.time.assignment.Assignment;
039 import org.kuali.hr.time.calendar.Calendar;
040 import org.kuali.hr.time.calendar.CalendarEntries;
041 import org.kuali.hr.time.calendar.TkCalendar;
042 import org.kuali.hr.time.roles.TkUserRoles;
043 import org.kuali.hr.time.roles.UserRoles;
044 import org.kuali.hr.time.service.base.TkServiceLocator;
045 import org.kuali.hr.time.task.Task;
046 import org.kuali.hr.time.timeblock.TimeBlock;
047 import org.kuali.hr.time.timeblock.TimeBlockHistory;
048 import org.kuali.hr.time.timesheet.TimesheetDocument;
049 import org.kuali.hr.time.timesheet.web.TimesheetAction;
050 import org.kuali.hr.time.timesheet.web.TimesheetActionForm;
051 import org.kuali.hr.time.timesummary.AssignmentRow;
052 import org.kuali.hr.time.timesummary.EarnCodeSection;
053 import org.kuali.hr.time.timesummary.EarnGroupSection;
054 import org.kuali.hr.time.timesummary.TimeSummary;
055 import org.kuali.hr.time.util.TKContext;
056 import org.kuali.hr.time.util.TKUser;
057 import org.kuali.hr.time.util.TKUtils;
058 import org.kuali.hr.time.util.TkConstants;
059 import org.kuali.hr.time.util.TkTimeBlockAggregate;
060 import org.kuali.hr.time.workflow.TimesheetDocumentHeader;
061 import org.kuali.rice.kew.service.KEWServiceLocator;
062 import org.kuali.rice.krad.exception.AuthorizationException;
063 import org.kuali.rice.krad.util.GlobalVariables;
064
065 public class TimeDetailAction extends TimesheetAction {
066
067 @Override
068 protected void checkTKAuthorization(ActionForm form, String methodToCall) throws AuthorizationException {
069 super.checkTKAuthorization(form, methodToCall); // Checks for read access first.
070 TKUser user = TKContext.getUser();
071 UserRoles roles = TkUserRoles.getUserRoles(GlobalVariables.getUserSession().getPrincipalId());
072 TimesheetDocument doc = TKContext.getCurrentTimesheetDocument();
073
074 // Check for write access to Timeblock.
075 if (StringUtils.equals(methodToCall, "addTimeBlock") || StringUtils.equals(methodToCall, "deleteTimeBlock") || StringUtils.equals(methodToCall, "updateTimeBlock")) {
076 if (!roles.isDocumentWritable(doc)) {
077 throw new AuthorizationException(roles.getPrincipalId(), "TimeDetailAction", "");
078 }
079 }
080 }
081
082 @Override
083 public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
084 ActionForward forward = super.execute(mapping, form, request, response);
085 if (forward.getRedirect()) {
086 return forward;
087 }
088 TimeDetailActionForm tdaf = (TimeDetailActionForm) form;
089 tdaf.setAssignmentDescriptions(TkServiceLocator.getAssignmentService().getAssignmentDescriptions(TKContext.getCurrentTimesheetDocument(), false));
090
091 // Handle User preference / timezone information (pushed up from TkCalendar to avoid duplication)
092 // Set calendar
093 CalendarEntries payCalendarEntry = tdaf.getPayCalendarDates();
094 Calendar payCalendar = TkServiceLocator.getCalendarService().getCalendar(payCalendarEntry.getHrCalendarId());
095
096 //List<TimeBlock> timeBlocks = TkServiceLocator.getTimeBlockService().getTimeBlocks(Long.parseLong(tdaf.getTimesheetDocument().getDocumentHeader().getTimesheetDocumentId()));
097 List<TimeBlock> timeBlocks = TKContext.getCurrentTimesheetDocument().getTimeBlocks();
098
099 this.assignStypeClassMapForTimeSummary(tdaf,timeBlocks);
100
101 List<Interval> intervals = TKUtils.getFullWeekDaySpanForCalendarEntry(payCalendarEntry);
102 TkTimeBlockAggregate aggregate = new TkTimeBlockAggregate(timeBlocks, payCalendarEntry, payCalendar, true, intervals);
103 TkCalendar cal = TkCalendar.getCalendar(aggregate);
104 cal.assignAssignmentStyle(tdaf.getAssignStyleClassMap());
105 tdaf.setTkCalendar(cal);
106
107 this.populateCalendarAndPayPeriodLists(request, tdaf);
108
109 tdaf.setTimeBlockString(ActionFormUtils.getTimeBlocksJson(aggregate.getFlattenedTimeBlockList()));
110
111 tdaf.setOvertimeEarnCodes(TkServiceLocator.getEarnCodeService().getOvertimeEarnCodesStrs(TKContext.getCurrentTimesheetDocument().getAsOfDate()));
112
113 if (StringUtils.equals(TKContext.getCurrentTimesheetDocument().getPrincipalId(), GlobalVariables.getUserSession().getPrincipalId())) {
114 tdaf.setWorkingOnItsOwn("true");
115 }
116
117 tdaf.setDocEditable("false");
118 if (TKContext.getUser().isSystemAdmin()) {
119 tdaf.setDocEditable("true");
120 } else {
121 boolean docFinal = TKContext.getCurrentTimesheetDocument().getDocumentHeader().getDocumentStatus().equals(TkConstants.ROUTE_STATUS.FINAL);
122 if (!docFinal) {
123 if(StringUtils.equals(TKContext.getCurrentTimesheetDocument().getPrincipalId(), GlobalVariables.getUserSession().getPrincipalId())
124 || TKContext.getUser().isSystemAdmin()
125 || TKContext.getUser().isLocationAdmin()
126 || TKContext.getUser().isDepartmentAdmin()
127 || TKContext.getUser().isReviewer()
128 || TKContext.getUser().isApprover()) {
129 tdaf.setDocEditable("true");
130 }
131
132 //if the timesheet has been approved by at least one of the approvers, the employee should not be able to edit it
133 if (StringUtils.equals(TKContext.getCurrentTimesheetDocument().getPrincipalId(), GlobalVariables.getUserSession().getPrincipalId())
134 && TKContext.getCurrentTimesheetDocument().getDocumentHeader().getDocumentStatus().equals(TkConstants.ROUTE_STATUS.ENROUTE)) {
135 Collection actions = KEWServiceLocator.getActionTakenService().findByDocIdAndAction(TKContext.getCurrentTimesheetDocument().getDocumentHeader().getDocumentId(), TkConstants.DOCUMENT_ACTIONS.APPROVE);
136 if(!actions.isEmpty()) {
137 tdaf.setDocEditable("false");
138 }
139 }
140 }
141 }
142
143 return forward;
144 }
145
146 // use lists of time blocks and leave blocks to build the style class map and assign css class to associated summary rows
147 private void assignStypeClassMapForTimeSummary(TimeDetailActionForm tdaf, List<TimeBlock> timeBlocks) throws Exception {
148 TimeSummary ts = TkServiceLocator.getTimeSummaryService().getTimeSummary(TKContext.getCurrentTimesheetDocument());
149 tdaf.setAssignStyleClassMap(ActionFormUtils.buildAssignmentStyleClassMap(timeBlocks));
150 Map<String, String> aMap = tdaf.getAssignStyleClassMap();
151 // set css classes for each assignment row
152 for (EarnGroupSection earnGroupSection : ts.getSections()) {
153 for (EarnCodeSection section : earnGroupSection.getEarnCodeSections()) {
154 for (AssignmentRow assignRow : section.getAssignmentsRows()) {
155 if (assignRow.getAssignmentKey() != null && aMap.containsKey(assignRow.getAssignmentKey())) {
156 assignRow.setCssClass(aMap.get(assignRow.getAssignmentKey()));
157 } else {
158 assignRow.setCssClass("");
159 }
160 }
161 }
162
163 }
164 tdaf.setTimeSummary(ts);
165 ActionFormUtils.validateHourLimit(tdaf);
166 ActionFormUtils.addWarningTextFromEarnGroup(tdaf);
167 }
168
169 private void populateCalendarAndPayPeriodLists(HttpServletRequest request, TimeDetailActionForm tdaf) {
170 List<TimesheetDocumentHeader> documentHeaders = (List<TimesheetDocumentHeader>) TkServiceLocator.getTimesheetDocumentHeaderService().getDocumentHeadersForPrincipalId(GlobalVariables.getUserSession().getPrincipalId());
171 SimpleDateFormat sdf = new SimpleDateFormat("yyyy");
172 if(tdaf.getCalendarYears().isEmpty()) {
173 // get calendar year drop down list contents
174 Set<String> yearSet = new HashSet<String>();
175
176 for(TimesheetDocumentHeader tdh : documentHeaders) {
177 yearSet.add(sdf.format(tdh.getPayBeginDate()));
178 }
179 List<String> yearList = new ArrayList<String>(yearSet);
180 Collections.sort(yearList);
181 Collections.reverse(yearList); // newest on top
182 tdaf.setCalendarYears(yearList);
183 }
184 // if selected calendar year is passed in
185 if(request.getParameter("selectedCY")!= null) {
186 tdaf.setSelectedCalendarYear(request.getParameter("selectedCY").toString());
187 }
188 // if there is no selected calendr year, use the year of current pay calendar entry
189 if(StringUtils.isEmpty(tdaf.getSelectedCalendarYear())) {
190 tdaf.setSelectedCalendarYear(sdf.format(tdaf.getPayCalendarDates().getBeginPeriodDate()));
191 }
192 if(tdaf.getPayPeriodsMap().isEmpty()) {
193 List<CalendarEntries> payPeriodList = new ArrayList<CalendarEntries>();
194 for(TimesheetDocumentHeader tdh : documentHeaders) {
195 if(sdf.format(tdh.getPayBeginDate()).equals(tdaf.getSelectedCalendarYear())) {
196 CalendarEntries pe = TkServiceLocator.getCalendarEntriesService().getCalendarEntriesByBeginAndEndDate(tdh.getPayBeginDate(), tdh.getPayEndDate());
197 payPeriodList.add(pe);
198 }
199 }
200 tdaf.setPayPeriodsMap(ActionFormUtils.getPayPeriodsMap(payPeriodList));
201 }
202 if(request.getParameter("selectedPP")!= null) {
203 tdaf.setSelectedPayPeriod(request.getParameter("selectedPP").toString());
204 }
205 if(StringUtils.isEmpty(tdaf.getSelectedPayPeriod())) {
206 tdaf.setSelectedPayPeriod(tdaf.getPayCalendarDates().getHrCalendarEntriesId());
207 }
208 }
209
210
211 /**
212 * This method involves creating an object-copy of every TimeBlock on the
213 * time sheet for overtime re-calculation.
214 *
215 * @throws Exception
216 */
217 public ActionForward deleteTimeBlock(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
218 TimeDetailActionForm tdaf = (TimeDetailActionForm) form;
219 //Grab timeblock to be deleted from form
220 List<TimeBlock> timeBlocks = tdaf.getTimesheetDocument().getTimeBlocks();
221 TimeBlock deletedTimeBlock = null;
222 for (TimeBlock tb : timeBlocks) {
223 if (tb.getTkTimeBlockId().compareTo(tdaf.getTkTimeBlockId()) == 0) {
224 deletedTimeBlock = tb;
225 break;
226 }
227 }
228 //Remove from the list of timeblocks
229 List<TimeBlock> referenceTimeBlocks = new ArrayList<TimeBlock>(tdaf.getTimesheetDocument().getTimeBlocks().size());
230 for (TimeBlock b : tdaf.getTimesheetDocument().getTimeBlocks()) {
231 referenceTimeBlocks.add(b.copy());
232 }
233
234 // simple pointer, for clarity
235 List<TimeBlock> newTimeBlocks = tdaf.getTimesheetDocument().getTimeBlocks();
236 newTimeBlocks.remove(deletedTimeBlock);
237
238 //Delete timeblock
239 TkServiceLocator.getTimeBlockService().deleteTimeBlock(deletedTimeBlock);
240 // Add a row to the history table
241 TimeBlockHistory tbh = new TimeBlockHistory(deletedTimeBlock);
242 tbh.setActionHistory(TkConstants.ACTIONS.DELETE_TIME_BLOCK);
243 TkServiceLocator.getTimeBlockHistoryService().saveTimeBlockHistory(tbh);
244 //reset time block
245 TkServiceLocator.getTimesheetService().resetTimeBlock(newTimeBlocks);
246 TkServiceLocator.getTkRuleControllerService().applyRules(TkConstants.ACTIONS.ADD_TIME_BLOCK, newTimeBlocks, tdaf.getPayCalendarDates(), tdaf.getTimesheetDocument(), TKContext.getPrincipalId());
247 TkServiceLocator.getTimeBlockService().saveTimeBlocks(referenceTimeBlocks, newTimeBlocks);
248
249 return mapping.findForward("basic");
250 }
251
252 /**
253 * This method involves creating an object-copy of every TimeBlock on the
254 * time sheet for overtime re-calculation.
255 *
256 * @throws Exception
257 */
258 public ActionForward addTimeBlock(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
259 TimeDetailActionForm tdaf = (TimeDetailActionForm) form;
260 this.changeTimeBlocks(tdaf);
261
262 ActionFormUtils.validateHourLimit(tdaf);
263 ActionFormUtils.addWarningTextFromEarnGroup(tdaf);
264
265 return mapping.findForward("basic");
266 }
267
268 private void removeOldTimeBlock(TimeDetailActionForm tdaf) {
269 if (tdaf.getTkTimeBlockId() != null) {
270 TimeBlock tb = TkServiceLocator.getTimeBlockService().getTimeBlock(tdaf.getTkTimeBlockId());
271 if (tb != null) {
272 TimeBlockHistory tbh = new TimeBlockHistory(tb);
273 TkServiceLocator.getTimeBlockService().deleteTimeBlock(tb);
274
275 // mark the original timeblock as deleted in the history table
276 tbh.setActionHistory(TkConstants.ACTIONS.DELETE_TIME_BLOCK);
277 TkServiceLocator.getTimeBlockHistoryService().saveTimeBlockHistory(tbh);
278
279 // delete the timeblock from the memory
280 tdaf.getTimesheetDocument().getTimeBlocks().remove(tb);
281 }
282 }
283 }
284
285
286 // add/update time blocks
287 private void changeTimeBlocks(TimeDetailActionForm tdaf) {
288 Timestamp overtimeBeginTimestamp = null;
289 Timestamp overtimeEndTimestamp = null;
290
291 // This is for updating a timeblock or changing
292 // 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.
293 if (tdaf.getTkTimeBlockId() != null) {
294 TimeBlock tb = TkServiceLocator.getTimeBlockService().getTimeBlock(tdaf.getTkTimeBlockId());
295 if (StringUtils.isNotEmpty(tdaf.getOvertimePref())) {
296 overtimeBeginTimestamp = tb.getBeginTimestamp();
297 overtimeEndTimestamp = tb.getEndTimestamp();
298 }
299 this.removeOldTimeBlock(tdaf);
300 }
301
302 Assignment assignment = TkServiceLocator.getAssignmentService().getAssignment(tdaf.getTimesheetDocument(), tdaf.getSelectedAssignment());
303
304
305 // Surgery point - Need to construct a Date/Time with Appropriate Timezone.
306 Timestamp startTime = TKUtils.convertDateStringToTimestamp(tdaf.getStartDate(), tdaf.getStartTime());
307 Timestamp endTime = TKUtils.convertDateStringToTimestamp(tdaf.getEndDate(), tdaf.getEndTime());
308
309 // We need a cloned reference set so we know whether or not to
310 // persist any potential changes without making hundreds of DB calls.
311 List<TimeBlock> referenceTimeBlocks = new ArrayList<TimeBlock>(tdaf.getTimesheetDocument().getTimeBlocks().size());
312 for (TimeBlock tb : tdaf.getTimesheetDocument().getTimeBlocks()) {
313 referenceTimeBlocks.add(tb.copy());
314 }
315
316 // This is just a reference, for code clarity, the above list is actually
317 // separate at the object level.
318 List<TimeBlock> newTimeBlocks = tdaf.getTimesheetDocument().getTimeBlocks();
319 DateTime startTemp = new DateTime(startTime);
320 DateTime endTemp = new DateTime(endTime);
321 // KPME-1446 add spanningweeks to the calls below
322 if (StringUtils.equals(tdaf.getAcrossDays(), "y")
323 && !(endTemp.getDayOfYear() - startTemp.getDayOfYear() <= 1
324 && endTemp.getHourOfDay() == 0)) {
325 newTimeBlocks.addAll(TkServiceLocator.getTimeBlockService().buildTimeBlocksSpanDates(assignment,
326 tdaf.getSelectedEarnCode(), tdaf.getTimesheetDocument(), startTime,
327 endTime, tdaf.getHours(), tdaf.getAmount(), false, Boolean.parseBoolean(tdaf.getLunchDeleted()), tdaf.getSpanningWeeks()));
328 } else {
329 newTimeBlocks.addAll(TkServiceLocator.getTimeBlockService().buildTimeBlocks(assignment,
330 tdaf.getSelectedEarnCode(), tdaf.getTimesheetDocument(), startTime,
331 endTime, tdaf.getHours(), tdaf.getAmount(), false, Boolean.parseBoolean(tdaf.getLunchDeleted())));
332 }
333
334 //reset time block
335 TkServiceLocator.getTimesheetService().resetTimeBlock(newTimeBlocks);
336
337 // apply overtime pref
338 for (TimeBlock tb : newTimeBlocks) {
339 if (tb.getBeginTimestamp().equals(overtimeBeginTimestamp) && tb.getEndTimestamp().equals(overtimeEndTimestamp) && StringUtils.isNotEmpty(tdaf.getOvertimePref())) {
340 tb.setOvertimePref(tdaf.getOvertimePref());
341 }
342
343 }
344
345 TkServiceLocator.getTkRuleControllerService().applyRules(TkConstants.ACTIONS.ADD_TIME_BLOCK, newTimeBlocks, tdaf.getPayCalendarDates(), tdaf.getTimesheetDocument(), TKContext.getPrincipalId());
346 TkServiceLocator.getTimeBlockService().saveTimeBlocks(referenceTimeBlocks, newTimeBlocks);
347 }
348
349 public ActionForward updateTimeBlock(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
350
351 TimeDetailActionForm tdaf = (TimeDetailActionForm) form;
352 Assignment assignment = TkServiceLocator.getAssignmentService().getAssignment(tdaf.getTimesheetDocument(), tdaf.getSelectedAssignment());
353
354 //Grab timeblock to be updated from form
355 List<TimeBlock> timeBlocks = tdaf.getTimesheetDocument().getTimeBlocks();
356 TimeBlock updatedTimeBlock = null;
357 for (TimeBlock tb : timeBlocks) {
358 if (tb.getTkTimeBlockId().compareTo(tdaf.getTkTimeBlockId()) == 0) {
359 updatedTimeBlock = tb;
360 tb.setJobNumber(assignment.getJobNumber());
361 tb.setWorkArea(assignment.getWorkArea());
362 tb.setTask(assignment.getTask());
363 tb.setTkWorkAreaId(assignment.getWorkAreaObj().getTkWorkAreaId());
364 tb.setHrJobId(assignment.getJob().getHrJobId());
365 String tkTaskId = "0";
366 for (Task task : assignment.getWorkAreaObj().getTasks()) {
367 if (task.getTask().compareTo(assignment.getTask()) == 0) {
368 tkTaskId = task.getTkTaskId();
369 break;
370 }
371 }
372 tb.setTkTaskId(tkTaskId);
373 break;
374 }
375 }
376
377 TkServiceLocator.getTimeBlockService().updateTimeBlock(updatedTimeBlock);
378
379 TimeBlockHistory tbh = new TimeBlockHistory(updatedTimeBlock);
380 tbh.setActionHistory(TkConstants.ACTIONS.UPDATE_TIME_BLOCK);
381 TkServiceLocator.getTimeBlockHistoryService().saveTimeBlockHistory(tbh);
382 tdaf.setMethodToCall("addTimeBlock");
383 return mapping.findForward("basic");
384 }
385
386
387 public ActionForward actualTimeInquiry(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
388 TimeDetailActionForm tdaf = (TimeDetailActionForm) form;
389 return mapping.findForward("ati");
390 }
391
392 public ActionForward deleteLunchDeduction(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
393
394 TimeDetailActionForm tdaf = (TimeDetailActionForm) form;
395 String timeHourDetailId = tdaf.getTkTimeHourDetailId();
396 TkServiceLocator.getTimeBlockService().deleteLunchDeduction(timeHourDetailId);
397
398 List<TimeBlock> newTimeBlocks = tdaf.getTimesheetDocument().getTimeBlocks();
399 TkServiceLocator.getTimesheetService().resetTimeBlock(newTimeBlocks);
400
401 return mapping.findForward("basic");
402 }
403
404 public ActionForward gotoCurrentPayPeriod(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
405 String viewPrincipal = TKUser.getCurrentTargetPerson().getPrincipalId();
406 Date currentDate = TKUtils.getTimelessDate(null);
407 CalendarEntries pce = TkServiceLocator.getCalendarService().getCurrentCalendarDates(viewPrincipal, currentDate);
408 TimesheetDocument td = TkServiceLocator.getTimesheetService().openTimesheetDocument(viewPrincipal, pce);
409 setupDocumentOnFormContext((TimesheetActionForm)form, td);
410 return mapping.findForward("basic");
411 }
412
413 //Triggered by changes of pay period drop down list, reload the whole page based on the selected pay period
414 public ActionForward changeCalendarYear(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
415
416 TimeDetailActionForm tdaf = (TimeDetailActionForm) form;
417 if(request.getParameter("selectedCY") != null) {
418 tdaf.setSelectedCalendarYear(request.getParameter("selectedCY").toString());
419 }
420 return mapping.findForward("basic");
421 }
422
423 //Triggered by changes of pay period drop down list, reload the whole page based on the selected pay period
424 public ActionForward changePayPeriod(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
425 TimeDetailActionForm tdaf = (TimeDetailActionForm) form;
426 if(request.getParameter("selectedPP") != null) {
427 tdaf.setSelectedPayPeriod(request.getParameter("selectedPP").toString());
428 CalendarEntries pce = TkServiceLocator.getCalendarEntriesService()
429 .getCalendarEntries(request.getParameter("selectedPP").toString());
430 if(pce != null) {
431 String viewPrincipal = TKUser.getCurrentTargetPerson().getPrincipalId();
432 TimesheetDocument td = TkServiceLocator.getTimesheetService().openTimesheetDocument(viewPrincipal, pce);
433 setupDocumentOnFormContext((TimesheetActionForm)form, td);
434 }
435 }
436 return mapping.findForward("basic");
437 }
438
439 }