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.lm.leaveblock.service;
017    
018    import java.math.BigDecimal;
019    import java.sql.Timestamp;
020    import java.util.*;
021    
022    import org.apache.commons.collections.CollectionUtils;
023    import org.apache.commons.lang.StringUtils;
024    import org.apache.commons.lang.time.DateUtils;
025    import org.apache.log4j.Logger;
026    import org.joda.time.DateTime;
027    import org.joda.time.DateTimeConstants;
028    import org.joda.time.DateTimeZone;
029    import org.joda.time.Interval;
030    import org.kuali.hr.lm.LMConstants;
031    import org.kuali.hr.lm.leaveblock.LeaveBlock;
032    import org.kuali.hr.lm.leaveblock.LeaveBlockHistory;
033    import org.kuali.hr.lm.leaveblock.dao.LeaveBlockDao;
034    import org.kuali.hr.lm.workflow.LeaveCalendarDocumentHeader;
035    import org.kuali.hr.time.assignment.Assignment;
036    import org.kuali.hr.time.calendar.CalendarEntries;
037    import org.kuali.hr.time.earncode.EarnCode;
038    import org.kuali.hr.time.service.base.TkServiceLocator;
039    import org.kuali.hr.time.util.TKContext;
040    import org.kuali.hr.time.util.TKUtils;
041    import org.kuali.hr.time.util.TkConstants;
042    import org.kuali.hr.time.workflow.TimesheetDocumentHeader;
043    import org.kuali.rice.krad.service.KRADServiceLocator;
044    
045    public class LeaveBlockServiceImpl implements LeaveBlockService {
046    
047        private static final Logger LOG = Logger.getLogger(LeaveBlockServiceImpl.class);
048    
049        private LeaveBlockDao leaveBlockDao;
050    
051        @Override
052        public LeaveBlock getLeaveBlock(String leaveBlockId) {
053            return leaveBlockDao.getLeaveBlock(leaveBlockId);
054        }
055    
056        public LeaveBlockDao getLeaveBlockDao() {
057            return leaveBlockDao;
058        }
059    
060        public void setLeaveBlockDao(LeaveBlockDao leaveBlockDao) {
061            this.leaveBlockDao = leaveBlockDao;
062        }
063    
064        public List<LeaveBlock> getLeaveBlocksForDocumentId(String documentId) {
065            return leaveBlockDao.getLeaveBlocksForDocumentId(documentId);
066        }
067    
068    
069        @Override
070        public List<LeaveBlock> getLeaveBlocks(String principalId, Date beginDate,
071                                       Date endDate) {
072            return leaveBlockDao.getLeaveBlocks(principalId, beginDate, endDate);
073        }
074    
075        @Override
076        public List<LeaveBlock> getLeaveBlocksWithAccrualCategory(String principalId, Date beginDate,
077                                               Date endDate, String accrualCategory) {
078            return leaveBlockDao.getLeaveBlocksWithAccrualCategory(principalId, beginDate, endDate, accrualCategory);
079        }
080    
081        @Override
082        public List<LeaveBlock> getLeaveBlocksWithType(String principalId, Date beginDate,
083                                               Date endDate, String leaveBlockType) {
084            return leaveBlockDao.getLeaveBlocksWithType(principalId, beginDate, endDate, leaveBlockType);
085        }
086    
087        @Override
088        public List<LeaveBlock> getLeaveBlocksSinceCarryOver(String principalId, Map<String, LeaveBlock> carryOver, DateTime endDate, boolean includeAllAccrualCategories) {
089            return leaveBlockDao.getLeaveBlocksSinceCarryOver(principalId, carryOver, endDate, includeAllAccrualCategories);
090        }
091    
092        @Override
093        public Map<String, LeaveBlock> getLastCarryOverBlocks(String principalId, Date asOfDate) {
094            if (StringUtils.isEmpty(principalId)) {
095                return Collections.emptyMap();
096            }
097            return leaveBlockDao.getLastCarryOverBlocks(principalId, LMConstants.LEAVE_BLOCK_TYPE.CARRY_OVER, asOfDate);
098        }
099    
100        @Override
101        public void saveLeaveBlocks(List<LeaveBlock> leaveBlocks) {
102            KRADServiceLocator.getBusinessObjectService().save(leaveBlocks);
103            
104            List<LeaveBlockHistory> leaveBlockHistories = new ArrayList<LeaveBlockHistory>();
105            for (LeaveBlock leaveBlock : leaveBlocks) {
106                    LeaveBlockHistory lbh = new LeaveBlockHistory(leaveBlock);
107                    lbh.setAction(LMConstants.ACTION.ADD);
108                    leaveBlockHistories.add(lbh);
109            }
110            
111            KRADServiceLocator.getBusinessObjectService().save(leaveBlockHistories);
112        }
113    
114        @Override
115        public void deleteLeaveBlock(String leaveBlockId, String principalId) {
116            LeaveBlock leaveBlock = getLeaveBlock(leaveBlockId);
117            
118    //        leaveBlock.setPrincipalIdModified(TKContext.getTargetPrincipalId());
119    //        leaveBlock.setTimestamp(TKUtils.getCurrentTimestamp());
120            
121            // Make entry into LeaveBlockHistory table
122            LeaveBlockHistory leaveBlockHistory = new LeaveBlockHistory(leaveBlock);
123            leaveBlockHistory.setPrincipalIdDeleted(principalId);
124            leaveBlockHistory.setTimestampDeleted(new Timestamp(System.currentTimeMillis()));
125            leaveBlockHistory.setAction(LMConstants.ACTION.DELETE);
126    
127            // deleting leaveblock
128            KRADServiceLocator.getBusinessObjectService().delete(leaveBlock);
129            
130            // creating history
131            KRADServiceLocator.getBusinessObjectService().save(leaveBlockHistory);
132            
133            
134        }
135    
136        @Override
137        public void saveLeaveBlock(LeaveBlock leaveBlock, String principalId) {
138    
139            // first delete and create new entry in the database
140            KRADServiceLocator.getBusinessObjectService().delete(leaveBlock);
141            
142            // create new 
143            leaveBlock.setLmLeaveBlockId(null);
144            leaveBlock.setTimestamp(new Timestamp(System.currentTimeMillis()));
145            leaveBlock.setPrincipalIdModified(principalId);
146            KRADServiceLocator.getBusinessObjectService().save(leaveBlock);
147    
148            // save history
149            LeaveBlockHistory lbh = new LeaveBlockHistory(leaveBlock);
150            lbh.setAction(LMConstants.ACTION.MODIFIED);
151            TkServiceLocator.getLeaveBlockHistoryService().saveLeaveBlockHistory(lbh);
152            
153        }
154    
155        @Override
156        public void addLeaveBlocks(DateTime beginDate, DateTime endDate, CalendarEntries ce, String selectedEarnCode, 
157                    BigDecimal hours, String description, Assignment selectedAssignment, String spanningWeeks, String leaveBlockType, String principalId) {
158            DateTime calBeginDateTime = beginDate;
159            DateTime calEndDateTime = endDate;
160            if(ce != null) {
161                    calBeginDateTime = ce.getBeginLocalDateTime().toDateTime();
162                    calEndDateTime = ce.getEndLocalDateTime().toDateTime();
163            } else {
164                throw new RuntimeException("Calendar Entry parameter is null.");
165            }
166            Interval calendarInterval = new Interval(calBeginDateTime, calEndDateTime);
167           
168            // To create the correct interval by the given begin and end dates,
169            // we need to plus one day on the end date to include that date
170            List<Interval> leaveBlockIntervals = TKUtils.createDaySpan(beginDate.toDateMidnight().toDateTime(), endDate.plusDays(1).toDateMidnight().toDateTime(), TKUtils.getSystemDateTimeZone());
171            // need to use beginDate and endDate of the calendar to find all leaveBlocks since LeaveCalendarDocument Id is not always available
172            List<LeaveBlock> currentLeaveBlocks = getLeaveBlocks(principalId, calBeginDateTime.toDate(), calEndDateTime.toDate());
173        
174            // use the current calendar's begin and end date to figure out if this pay period has a leaveDocument
175            LeaveCalendarDocumentHeader lcdh = TkServiceLocator.getLeaveCalendarDocumentHeaderService()
176                            .getDocumentHeader(principalId, ce.getBeginLocalDateTime().toDateTime().toDate(), ce.getEndLocalDateTime().toDateTime().toDate());
177            String docId = lcdh == null ? null : lcdh.getDocumentId();
178            
179            // TODO: need to integrate with the scheduled timeoff.
180            for (Interval leaveBlockInt : leaveBlockIntervals) {
181                if (calendarInterval.contains(leaveBlockInt)) {
182                    // KPME-1446 if "Include weekends" check box is checked, don't add Sat and Sun to the leaveblock list
183                    if (StringUtils.isEmpty(spanningWeeks) && 
184                            (leaveBlockInt.getStart().getDayOfWeek() == DateTimeConstants.SATURDAY ||leaveBlockInt.getStart().getDayOfWeek() == DateTimeConstants.SUNDAY)) {
185                            
186                            // do nothing
187                    } else {
188                             // Currently, we store the accrual category value in the leave code table, but store accrual category id in the leaveBlock.
189                        // That's why there is a two step server call to get the id. This might be changed in the future.
190    
191                        java.sql.Date sqlDate = new java.sql.Date(ce.getEndLocalDateTime().toDateTime().toDate().getTime());
192                        if ((leaveBlockType.equals(LMConstants.LEAVE_BLOCK_TYPE.LEAVE_CALENDAR)
193                                    || leaveBlockType.equals((LMConstants.LEAVE_BLOCK_TYPE.TIME_CALENDAR)))
194                                && BigDecimal.ZERO.compareTo(hours) < 0) {
195                            hours = hours.negate();
196                        }
197    
198                        CalendarEntries calendarEntry = TkServiceLocator.getCalendarEntriesService().getCurrentCalendarEntriesByCalendarId(ce.getHrCalendarId(), TKUtils.getCurrentDate());
199                        Date leaveBlockDate = new DateTime(leaveBlockInt.getStartMillis()).toDate();
200                        
201                        String requestStatus = LMConstants.REQUEST_STATUS.USAGE;
202                        if (TkServiceLocator.getLeaveApprovalService().isActiveAssignmentFoundOnJobFlsaStatus(principalId, TkConstants.FLSA_STATUS_NON_EXEMPT, true)) {
203                            TimesheetDocumentHeader tdh = TkServiceLocator.getTimesheetDocumentHeaderService().getDocumentHeaderForDate(principalId, leaveBlockDate);
204                            if (tdh != null) {
205                               if (DateUtils.isSameDay(leaveBlockDate, tdh.getEndDate()) || leaveBlockDate.after(tdh.getEndDate())) {
206                                      requestStatus = LMConstants.REQUEST_STATUS.PLANNED;
207                               }
208                           } else {
209                              requestStatus = LMConstants.REQUEST_STATUS.PLANNED;
210                           }
211                        } else {
212                            if (DateUtils.isSameDay(leaveBlockDate, calendarEntry.getEndPeriodDateTime()) || leaveBlockDate.after(calendarEntry.getEndPeriodDateTime())) {
213                                    requestStatus = LMConstants.REQUEST_STATUS.PLANNED;
214                            }
215                        }
216                        
217                        EarnCode earnCodeObj = TkServiceLocator.getEarnCodeService().getEarnCode(selectedEarnCode, sqlDate);
218                            LeaveBlock leaveBlock = new LeaveBlock.Builder(new DateTime(leaveBlockInt.getStartMillis()), docId, principalId, selectedEarnCode, hours)
219                                    .description(description)
220                                    .principalIdModified(principalId)
221                                    .timestamp(TKUtils.getCurrentTimestamp())
222                                    .scheduleTimeOffId("0")
223                                    .accrualCategory(earnCodeObj.getAccrualCategory())
224                                    .workArea(selectedAssignment.getWorkArea())
225                                    .jobNumber(selectedAssignment.getJobNumber())
226                                    .task(selectedAssignment.getTask())
227                                .requestStatus(requestStatus)
228                                    .leaveBlockType(leaveBlockType)
229                                    .build();
230                        if (!currentLeaveBlocks.contains(leaveBlock)) {
231                            currentLeaveBlocks.add(leaveBlock);
232                        }
233                    }
234                }
235            }
236    
237            saveLeaveBlocks(currentLeaveBlocks);
238        }
239        
240        // KPME-1447
241        @Override
242        public void updateLeaveBlock(LeaveBlock leaveBlock, String principalId) {
243            //verify that if leave block is usage, leave amount is negative
244            if ((LMConstants.LEAVE_BLOCK_TYPE.LEAVE_CALENDAR.equals(leaveBlock.getLeaveBlockType())
245                        || LMConstants.LEAVE_BLOCK_TYPE.LEAVE_CALENDAR.equals(leaveBlock.getLeaveBlockType()))
246                    && BigDecimal.ZERO.compareTo(leaveBlock.getLeaveAmount()) < 0) {
247                leaveBlock.setLeaveAmount(leaveBlock.getLeaveAmount().negate());
248            }
249    
250            // Make entry into LeaveBlockHistory table
251            LeaveBlockHistory leaveBlockHistory = new LeaveBlockHistory(leaveBlock);
252            leaveBlockHistory.setPrincipalIdDeleted(principalId);
253            leaveBlockHistory.setTimestampDeleted(new Timestamp(System.currentTimeMillis()));
254            leaveBlockHistory.setAction(LMConstants.ACTION.MODIFIED);
255    
256            KRADServiceLocator.getBusinessObjectService().save(leaveBlock);
257            
258            // creating history
259            KRADServiceLocator.getBusinessObjectService().save(leaveBlockHistory); 
260        }    
261    
262        public static List<Interval> createDaySpan(DateTime beginDateTime, DateTime endDateTime, DateTimeZone zone) {
263            beginDateTime = beginDateTime.toDateTime(zone);
264            endDateTime = endDateTime.toDateTime(zone);
265            List<Interval> dayIntervals = new ArrayList<Interval>();
266    
267            DateTime currDateTime = beginDateTime;
268            while (currDateTime.isBefore(endDateTime)) {
269                DateTime prevDateTime = currDateTime;
270                currDateTime = currDateTime.plusDays(1);
271                Interval daySpan = new Interval(prevDateTime, currDateTime);
272                dayIntervals.add(daySpan);
273            }
274    
275            return dayIntervals;
276        }
277    
278            @Override
279            public List<LeaveBlock> getLeaveBlocks(String principalId, String leaveBlockType, String requestStatus, Date currentDate) {
280                    return leaveBlockDao.getLeaveBlocks(principalId, leaveBlockType, requestStatus, currentDate);
281            }
282            
283            @Override
284            public List<LeaveBlock> getLeaveBlocksForDate(String principalId, Date leaveDate) {
285                    return leaveBlockDao.getLeaveBlocksForDate(principalId, leaveDate);
286            }
287    
288            @Override
289            public List<LeaveBlock> getNotAccrualGeneratedLeaveBlocksForDate(String principalId, Date leaveDate) {
290                    return leaveBlockDao.getNotAccrualGeneratedLeaveBlocksForDate(principalId, leaveDate);
291            }
292    
293            public List<LeaveBlock> getLeaveBlocksForTimeCalendar(String principalId, Date beginDate, Date endDate, List<String> assignmentKeys) {
294                    List<LeaveBlock> col = leaveBlockDao.getCalendarLeaveBlocks(principalId, beginDate, endDate);
295                    List<LeaveBlock> leaveBlocks = filterLeaveBlocksForTimeCalendar(col, assignmentKeys);
296                    return leaveBlocks;
297            }
298            
299            public List<LeaveBlock> getLeaveBlocksForLeaveCalendar(String principalId, Date beginDate, Date endDate, List<String> assignmentKeys) {
300                    List<LeaveBlock> col = leaveBlockDao.getLeaveBlocks(principalId, beginDate, endDate);
301                    List<LeaveBlock> leaveBlocks = filterLeaveBlocksForLeaveCalendar(col, assignmentKeys);
302                    return leaveBlocks;
303            }
304            
305            public List<LeaveBlock> filterLeaveBlocksForTimeCalendar(List<LeaveBlock> lbs, List<String> assignmentKeys) {
306                    if(CollectionUtils.isEmpty(assignmentKeys)) {
307                            return lbs;
308                    }
309            List<LeaveBlock> results = new ArrayList<LeaveBlock> ();
310            for(LeaveBlock lb : lbs) {
311                    if(lb != null) {
312                            if (StringUtils.equals(lb.getLeaveBlockType(), LMConstants.LEAVE_BLOCK_TYPE.ACCRUAL_SERVICE)
313                                            && StringUtils.isNotEmpty(lb.getScheduleTimeOffId()) 
314                                            && lb.getLeaveAmount().compareTo(BigDecimal.ZERO) < 0) {
315                                            // display usage leave blocks generated by system scheduled time off
316                                            results.add(lb);
317                                    } else if(StringUtils.isNotEmpty(lb.getAssignmentKey()) && assignmentKeys.contains(lb.getAssignmentKey())) {
318                                    if (StringUtils.equals(lb.getLeaveBlockType(), LMConstants.LEAVE_BLOCK_TYPE.LEAVE_CALENDAR)) {
319                                            // only add approved leave blocks that are created from leave calendar
320                                            if (StringUtils.equals(lb.getRequestStatus(), LMConstants.REQUEST_STATUS.APPROVED)) {   
321                                                    results.add(lb);
322                                            }
323                                    } else if(StringUtils.equals(lb.getLeaveBlockType(), LMConstants.LEAVE_BLOCK_TYPE.TIME_CALENDAR)) {
324                                            results.add(lb);
325                                    }
326                            }
327                    }
328            }
329            return results;
330        }
331            
332            public List<LeaveBlock> filterLeaveBlocksForLeaveCalendar(List<LeaveBlock> lbs, List<String> assignmentKeys) {
333                    if(CollectionUtils.isEmpty(assignmentKeys)) {
334                            return lbs;
335                    }
336                    List<LeaveBlock> leaveBlocks = new ArrayList<LeaveBlock>();
337                    for(LeaveBlock lb : lbs) {
338                       if(lb != null) {
339                               if(lb.getLeaveBlockType().equals(LMConstants.LEAVE_BLOCK_TYPE.TIME_CALENDAR)) {
340                                      if(StringUtils.isNotEmpty(lb.getAssignmentKey()) && assignmentKeys.contains(lb.getAssignmentKey())) {
341                                              leaveBlocks.add(lb);
342                                      }
343                               } else {
344                                       leaveBlocks.add(lb);
345                               }
346                       }
347                    }
348            return leaveBlocks;
349        }
350    
351        @Override
352        public void deleteLeaveBlocksForDocumentId(String documentId){
353            leaveBlockDao.deleteLeaveBlocksForDocumentId(documentId);
354        }
355        
356        
357        @Override
358        public List<LeaveBlock> getAccrualGeneratedLeaveBlocks(String principalId, Date beginDate, Date endDate) {
359            return leaveBlockDao.getAccrualGeneratedLeaveBlocks(principalId, beginDate, endDate);
360        }
361        
362        @Override
363        public List<LeaveBlock> getSSTOLeaveBlocks(String principalId, String sstoId, Date accruledDate) {
364            return leaveBlockDao.getSSTOLeaveBlocks(principalId, sstoId, accruledDate);
365        }
366        
367        @Override
368        public List<LeaveBlock> getABELeaveBlocksSinceTime(String principalId, Timestamp lastRanTime) {
369            return leaveBlockDao.getABELeaveBlocksSinceTime(principalId, lastRanTime);
370        }
371    }