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.clocklog.service;
017    
018    import java.math.BigDecimal;
019    import java.sql.Timestamp;
020    import java.util.ArrayList;
021    import java.util.HashSet;
022    import java.util.List;
023    import java.util.Set;
024    
025    import org.apache.commons.collections.CollectionUtils;
026    import org.apache.commons.lang.StringUtils;
027    import org.kuali.hr.lm.leaveblock.LeaveBlock;
028    import org.kuali.hr.time.assignment.Assignment;
029    import org.kuali.hr.time.calendar.CalendarEntries;
030    import org.kuali.hr.time.clocklog.ClockLog;
031    import org.kuali.hr.time.clocklog.dao.ClockLogDao;
032    import org.kuali.hr.time.service.base.TkServiceLocator;
033    import org.kuali.hr.time.timeblock.TimeBlock;
034    import org.kuali.hr.time.timesheet.TimesheetDocument;
035    import org.kuali.hr.time.util.TKContext;
036    import org.kuali.hr.time.util.TkConstants;
037    import org.kuali.hr.time.workflow.TimesheetDocumentHeader;
038    import org.kuali.rice.krad.service.KRADServiceLocator;
039    
040    public class ClockLogServiceImpl implements ClockLogService {
041    
042        private ClockLogDao clockLogDao;
043    
044        public ClockLogServiceImpl() {
045        }
046    
047        public ClockLog saveClockLog(ClockLog clockLog) {
048            return KRADServiceLocator.getBusinessObjectService().save(clockLog);
049        }
050    
051        @Override
052        public ClockLog processClockLog(Timestamp clockTimeStamp, Assignment assignment,CalendarEntries pe, String ip, java.sql.Date asOfDate, TimesheetDocument td, String clockAction, boolean runRules, String principalId) {
053            return processClockLog(clockTimeStamp, assignment, pe, ip, asOfDate, td, clockAction, runRules, principalId, TKContext.getPrincipalId());
054        }
055    
056        @Override
057        public ClockLog processClockLog(Timestamp clockTimeStamp, Assignment assignment,CalendarEntries pe, String ip, java.sql.Date asOfDate, TimesheetDocument td, String clockAction, boolean runRules, String principalId, String userPrincipalId) {
058            // process rules
059            Timestamp roundedClockTimestamp = TkServiceLocator.getGracePeriodService().processGracePeriodRule(clockTimeStamp, new java.sql.Date(pe.getBeginPeriodDateTime().getTime()));
060    
061            ClockLog clockLog = buildClockLog(roundedClockTimestamp, new Timestamp(System.currentTimeMillis()), assignment, td, clockAction, ip, userPrincipalId);
062            
063            if (runRules) {
064                    TkServiceLocator.getClockLocationRuleService().processClockLocationRule(clockLog, asOfDate);
065            }
066            
067            // If the clock action is clock out or lunch out, create a time block besides the clock log
068            if (StringUtils.equals(clockAction, TkConstants.CLOCK_OUT) || StringUtils.equals(clockAction, TkConstants.LUNCH_OUT)) {
069                processTimeBlock(clockLog, assignment, pe, td, clockAction, principalId, userPrincipalId);
070            } else {
071                //Save current clock log to get id for timeblock building
072                KRADServiceLocator.getBusinessObjectService().save(clockLog);
073            }
074    
075            return clockLog;
076        }
077    
078        private void processTimeBlock(ClockLog clockLog, Assignment currentAssignment, CalendarEntries pe, TimesheetDocument td, String clockAction, String principalId, String userPrincipalId) {
079            ClockLog lastLog = null;
080            Timestamp lastClockTimestamp = null;
081            String beginClockLogId = null;
082            String endClockLogId = null;
083    
084            if (StringUtils.equals(clockAction, TkConstants.LUNCH_OUT)) {
085                lastLog = TkServiceLocator.getClockLogService().getLastClockLog(principalId, TkConstants.CLOCK_IN);
086            } else if (StringUtils.equals(clockAction, TkConstants.CLOCK_OUT)) {
087                lastLog = TkServiceLocator.getClockLogService().getLastClockLog(principalId);
088            }
089            if (lastLog != null) {
090                lastClockTimestamp = lastLog.getClockTimestamp();
091                beginClockLogId = lastLog.getTkClockLogId();
092            }
093            //Save current clock log to get id for timeblock building
094            KRADServiceLocator.getBusinessObjectService().save(clockLog);
095            endClockLogId = clockLog.getTkClockLogId();
096    
097            long beginTime = lastClockTimestamp.getTime();
098            Timestamp beginTimestamp = new Timestamp(beginTime);
099            Timestamp endTimestamp = clockLog.getClockTimestamp();
100    
101            // New Time Blocks, pointer reference
102            List<TimeBlock> newTimeBlocks = td.getTimeBlocks();
103            List<TimeBlock> referenceTimeBlocks = new ArrayList<TimeBlock>(td.getTimeBlocks().size());
104            for (TimeBlock tb : td.getTimeBlocks()) {
105                referenceTimeBlocks.add(tb.copy());
106            }
107    
108            // Add TimeBlocks after we store our reference object!
109            List<TimeBlock> aList = TkServiceLocator.getTimeBlockService().buildTimeBlocks(currentAssignment, currentAssignment.getJob().getPayTypeObj().getRegEarnCode(), td, beginTimestamp, endTimestamp, BigDecimal.ZERO, BigDecimal.ZERO, true, false, userPrincipalId);
110            for (TimeBlock tb : aList) {
111                tb.setClockLogBeginId(beginClockLogId);
112                tb.setClockLogEndId(endClockLogId);
113            }
114            newTimeBlocks.addAll(aList);
115            
116            List<Assignment> assignments = td.getAssignments();
117            List<String> assignmentKeys = new ArrayList<String>();
118            for (Assignment assignment : assignments) {
119                    assignmentKeys.add(assignment.getAssignmentKey());
120            }
121            List<LeaveBlock> leaveBlocks = TkServiceLocator.getLeaveBlockService().getLeaveBlocksForTimeCalendar(principalId, td.getAsOfDate(), td.getDocEndDate(), assignmentKeys);
122    
123            //reset time block
124            TkServiceLocator.getTimesheetService().resetTimeBlock(newTimeBlocks, td.getAsOfDate());
125    
126            //apply any rules for this action
127            TkServiceLocator.getTkRuleControllerService().applyRules(TkConstants.ACTIONS.CLOCK_OUT, newTimeBlocks, leaveBlocks, pe, td, principalId);
128    
129            //call persist method that only saves added/deleted/changed timeblocks
130            TkServiceLocator.getTimeBlockService().saveTimeBlocks(referenceTimeBlocks, newTimeBlocks, userPrincipalId);
131        }
132    
133        private ClockLog buildClockLog(Timestamp clockTimestamp, Timestamp originalTimestamp, Assignment assignment, TimesheetDocument timesheetDocument, String clockAction, String ip, String userPrincipalId) {
134            ClockLog clockLog = new ClockLog();
135            clockLog.setDocumentId(timesheetDocument.getDocumentId());
136            clockLog.setPrincipalId(timesheetDocument.getPrincipalId());
137            clockLog.setJob(timesheetDocument.getJob(assignment.getJobNumber()));
138            clockLog.setJobNumber(assignment.getJobNumber());
139            clockLog.setWorkArea(assignment.getWorkArea());
140            clockLog.setTask(assignment.getTask());
141            clockLog.setClockTimestampTimezone(TkServiceLocator.getTimezoneService().getUserTimezoneWithFallback().getID());
142            clockLog.setClockTimestamp(clockTimestamp);
143            clockLog.setClockAction(clockAction);
144            clockLog.setIpAddress(ip);
145            clockLog.setUserPrincipalId(userPrincipalId);
146            // timestamp should be the original time without Grace Period rule applied
147            clockLog.setTimestamp(originalTimestamp);
148    
149            return clockLog;
150        }
151    
152        public void setClockLogDao(ClockLogDao clockLogDao) {
153            this.clockLogDao = clockLogDao;
154        }
155    
156        public ClockLog getLastClockLog(String principalId) {
157            return clockLogDao.getLastClockLog(principalId);
158        }
159    
160        public ClockLog getLastClockLog(String principalId, String clockAction) {
161            return clockLogDao.getLastClockLog(principalId, clockAction);
162        }
163    
164        public ClockLog getLastClockLog(String principalId, String jobNumber, String workArea, String task, CalendarEntries calendarEntry) {
165            //return clockLogDao.getLastClockLog(principalId, jobNumber, workArea, task, calendarEntry);
166            TimesheetDocumentHeader tdh = TkServiceLocator.getTimesheetDocumentHeaderService().getDocumentHeader(principalId, calendarEntry.getBeginPeriodDateTime(), calendarEntry.getEndPeriodDateTime());
167            if (tdh == null) {
168                return null;
169            }
170            return clockLogDao.getLastClockLog(principalId, jobNumber, workArea, task, tdh.getDocumentId());
171        }
172    
173        @Override
174        public ClockLog getClockLog(String tkClockLogId) {
175            return clockLogDao.getClockLog(tkClockLogId);
176        }
177        
178            @Override
179            public void deleteClockLogsForDocumentId(String documentId) {
180                    clockLogDao.deleteClockLogsForDocumentId(documentId);
181            }
182    
183        public List<String> getUnapprovedIPWarning(List<TimeBlock> timeBlocks) {
184                     List<String> warningMessages = new ArrayList<String>();
185                     if (CollectionUtils.isNotEmpty(timeBlocks)) {
186                         Set<String> aSet = new HashSet<String>();
187                         for(TimeBlock tb : timeBlocks) {
188                             if(tb.getClockLogCreated()) {
189                                     if(StringUtils.isNotEmpty(tb.getClockLogBeginId())){
190                                             ClockLog cl = TkServiceLocator.getClockLogService().getClockLog(tb.getClockLogBeginId());
191                                             if(cl.getUnapprovedIP()) {
192                                                     aSet.add(buildUnapprovedIPWarning(cl));
193                                             }
194                                     }
195                                     if(StringUtils.isNotEmpty(tb.getClockLogEndId())){
196                                             ClockLog cl = TkServiceLocator.getClockLogService().getClockLog(tb.getClockLogEndId());
197                                             if(cl.getUnapprovedIP()) {
198                                                     aSet.add(buildUnapprovedIPWarning(cl));
199                                             }
200                                     }              
201                             }
202                         }
203                         warningMessages.addAll(aSet);
204                    }
205                    
206                    return warningMessages;
207        }
208    
209            public String buildUnapprovedIPWarning(ClockLog cl) {
210                    String warning = "Warning: Action '" + TkConstants.CLOCK_ACTION_STRINGS.get(cl.getClockAction()) + "' taken at " 
211                            + cl.getClockTimestamp() + " was from an unapproved IP address - " + cl.getIpAddress();
212                    return warning;
213            }
214    
215    }