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