View Javadoc

1   /**
2    * Copyright 2004-2014 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.kpme.tklm.leave.calendar.service;
17  
18  import org.apache.commons.collections.CollectionUtils;
19  import org.apache.commons.lang.StringUtils;
20  import org.apache.log4j.Logger;
21  import org.joda.time.DateTime;
22  import org.joda.time.LocalDate;
23  import org.kuali.kpme.core.assignment.Assignment;
24  import org.kuali.kpme.core.batch.BatchJobUtil;
25  import org.kuali.kpme.core.calendar.entry.CalendarEntry;
26  import org.kuali.kpme.core.document.calendar.CalendarDocument;
27  import org.kuali.kpme.core.job.Job;
28  import org.kuali.kpme.core.service.HrServiceLocator;
29  import org.kuali.kpme.core.util.HrConstants;
30  import org.kuali.kpme.core.util.TKUtils;
31  import org.kuali.kpme.tklm.common.LMConstants;
32  import org.kuali.kpme.tklm.leave.block.LeaveBlock;
33  import org.kuali.kpme.tklm.leave.calendar.LeaveCalendarDocument;
34  import org.kuali.kpme.tklm.leave.calendar.dao.LeaveCalendarDao;
35  import org.kuali.kpme.tklm.leave.service.LmServiceLocator;
36  import org.kuali.kpme.tklm.leave.workflow.LeaveCalendarDocumentHeader;
37  import org.kuali.kpme.tklm.leave.workflow.LeaveRequestDocument;
38  import org.kuali.kpme.tklm.time.service.TkServiceLocator;
39  import org.kuali.rice.core.api.config.property.ConfigContext;
40  import org.kuali.rice.kew.api.KewApiServiceLocator;
41  import org.kuali.rice.kew.api.WorkflowDocument;
42  import org.kuali.rice.kew.api.WorkflowDocumentFactory;
43  import org.kuali.rice.kew.api.exception.WorkflowException;
44  import org.kuali.rice.kew.api.note.Note;
45  import org.kuali.rice.kim.api.identity.principal.EntityNamePrincipalName;
46  import org.kuali.rice.kim.api.services.KimApiServiceLocator;
47  import org.kuali.rice.krad.service.KRADServiceLocator;
48  import org.kuali.rice.krad.util.GlobalVariables;
49  
50  import java.util.List;
51  
52  public class LeaveCalendarServiceImpl implements LeaveCalendarService {
53  	
54  	private static final Logger LOG = Logger.getLogger(LeaveCalendarServiceImpl.class);
55  
56      private LeaveCalendarDao leaveCalendarDao;
57  
58      @Override
59      public LeaveCalendarDocument getLeaveCalendarDocument(String documentId) {
60          LeaveCalendarDocument lcd = null;
61          LeaveCalendarDocumentHeader lcdh = LmServiceLocator.getLeaveCalendarDocumentHeaderService().getDocumentHeader(documentId);
62  
63          if (lcdh != null) {
64              lcd = new LeaveCalendarDocument(lcdh);
65              CalendarEntry pce = HrServiceLocator.getCalendarEntryService().getCalendarDatesByPayEndDate(lcdh.getPrincipalId(), lcdh.getEndDateTime(), HrConstants.LEAVE_CALENDAR_TYPE);
66              lcd.setCalendarEntry(pce);
67          } else {
68          	LOG.error("Could not find LeaveCalendarDocumentHeader for DocumentID: " + documentId);
69   	 	 	return null;
70          }
71  
72          List<LeaveBlock> leaveBlocks = LmServiceLocator.getLeaveBlockService().getLeaveBlocksForDocumentId(documentId);
73          lcd.setLeaveBlocks(leaveBlocks);
74  
75          // Fetching assignments
76          List<Assignment> assignments = HrServiceLocator.getAssignmentService().getAssignmentsByCalEntryForLeaveCalendar(lcdh.getPrincipalId(), lcd.getCalendarEntry());
77          lcd.setAssignments(assignments);
78          
79          return lcd;
80      }
81  
82      @Override
83      public LeaveCalendarDocument openLeaveCalendarDocument(String principalId, CalendarEntry calEntry) throws WorkflowException {
84          LeaveCalendarDocument doc;
85  
86          DateTime begin = calEntry.getBeginPeriodFullDateTime();
87          DateTime end = calEntry.getEndPeriodFullDateTime();
88  
89          LeaveCalendarDocumentHeader header = LmServiceLocator.getLeaveCalendarDocumentHeaderService().getDocumentHeader(principalId, begin, end);
90          if (header == null) {
91              EntityNamePrincipalName person = KimApiServiceLocator.getIdentityService().getDefaultNamesForPrincipalId(principalId);
92              String principalName = person != null && person.getDefaultName() != null ? person.getDefaultName().getCompositeName() : StringUtils.EMPTY;
93              String beginDateString = TKUtils.formatDate(begin.toLocalDate());
94              String endDateString = TKUtils.formatDate(end.toLocalDate());
95              String leaveCalendarDocumentTitle = LeaveCalendarDocument.LEAVE_CALENDAR_DOCUMENT_TYPE + " - " + principalName + " (" + principalId + ") - " + beginDateString + "-" + endDateString;
96              
97              doc = initiateWorkflowDocument(principalId, begin, end, calEntry, LeaveCalendarDocument.LEAVE_CALENDAR_DOCUMENT_TYPE, leaveCalendarDocumentTitle);
98          } else {
99              doc = getLeaveCalendarDocument(header.getDocumentId());
100         }
101         if (doc != null) {
102         	doc.setCalendarEntry(calEntry);
103         }
104         // TODO: need to set the summary
105         return doc;
106     }
107     
108     //Should only create leave calendar document if active jobs were found with flsa elig = no and ben elg = yes
109     public boolean shouldCreateLeaveDocument(String principalId, CalendarEntry calEntry){
110         if (StringUtils.isEmpty(principalId) || calEntry == null) {
111             return false;
112         }
113         
114         boolean isPlanningCalendar = LmServiceLocator.getLeaveCalendarService().isLeavePlanningCalendar(principalId, calEntry.getBeginPeriodFullDateTime().toLocalDate(), calEntry.getEndPeriodFullDateTime().toLocalDate());
115     	if (isPlanningCalendar) {
116     		return false;
117     	}
118         
119         List<Assignment> assignments = HrServiceLocator.getAssignmentService().getAssignmentsByPayEntry(principalId, calEntry);
120     	List<Assignment> results = HrServiceLocator.getAssignmentService().filterAssignments(assignments, HrConstants.FLSA_STATUS_EXEMPT, true);
121     	return CollectionUtils.isNotEmpty(results);
122     }
123     
124     protected LeaveCalendarDocument initiateWorkflowDocument(String principalId, DateTime payBeginDate, DateTime payEndDate, CalendarEntry calendarEntry, String documentType, String title) throws WorkflowException {
125         LeaveCalendarDocument leaveCalendarDocument = null;
126         WorkflowDocument workflowDocument = null;
127 
128         workflowDocument =  WorkflowDocumentFactory.createDocument(principalId, documentType, title);
129 
130         String status = workflowDocument.getStatus().getCode();
131         LeaveCalendarDocumentHeader documentHeader = new LeaveCalendarDocumentHeader(workflowDocument.getDocumentId(), principalId, payBeginDate.toDate(), payEndDate.toDate(), status);
132 
133         documentHeader.setDocumentId(workflowDocument.getDocumentId());
134         documentHeader.setDocumentStatus(HrConstants.ROUTE_STATUS.INITIATED);
135 
136         KRADServiceLocator.getBusinessObjectService().save(documentHeader);
137         
138         leaveCalendarDocument = new LeaveCalendarDocument(documentHeader);
139         leaveCalendarDocument.setCalendarEntry(calendarEntry);
140         loadLeaveCalendarDocumentData(leaveCalendarDocument, principalId, calendarEntry);
141         TkServiceLocator.getTkSearchableAttributeService().updateSearchableAttribute(leaveCalendarDocument, payEndDate.toLocalDate());
142         
143         updateLeaveBlockDocumentIds(principalId, payBeginDate.toLocalDate(), payEndDate.toLocalDate(), workflowDocument.getDocumentId());
144         
145         updatePlannedLeaveBlocks(principalId, payBeginDate.toLocalDate(), payEndDate.toLocalDate());
146 
147         return leaveCalendarDocument;
148     }
149     
150     private void updateLeaveBlockDocumentIds(String principalId, LocalDate beginDate, LocalDate endDate, String documentId) {
151         List<LeaveBlock> leaveBlocks = LmServiceLocator.getLeaveBlockService().getLeaveBlocks(principalId, beginDate, endDate);
152         
153         for (LeaveBlock leaveBlock : leaveBlocks) {
154         	leaveBlock.setDocumentId(documentId);
155         }
156         
157         LmServiceLocator.getLeaveBlockService().saveLeaveBlocks(leaveBlocks);
158     }
159     
160     private void updatePlannedLeaveBlocks(String principalId, LocalDate beginDate, LocalDate endDate) {
161         String batchUserPrincipalId = BatchJobUtil.getBatchUserPrincipalId();
162         
163         if (batchUserPrincipalId != null) {
164 	    	List<LeaveBlock> leaveBlocks = LmServiceLocator.getLeaveBlockService().getLeaveBlocks(principalId, beginDate, endDate);
165 	
166 	    	for (LeaveBlock leaveBlock : leaveBlocks) {
167 	    		if (StringUtils.equals(leaveBlock.getRequestStatus(), HrConstants.REQUEST_STATUS.PLANNED) 
168 	    				|| StringUtils.equals(leaveBlock.getRequestStatus(), HrConstants.REQUEST_STATUS.DEFERRED)) {
169 	    			LmServiceLocator.getLeaveBlockService().deleteLeaveBlock(leaveBlock.getLmLeaveBlockId(), batchUserPrincipalId);
170 	    		} else if (StringUtils.equals(leaveBlock.getRequestStatus(), HrConstants.REQUEST_STATUS.REQUESTED)) {
171 	    	        if (StringUtils.equals(getInitiateLeaveRequestAction(), LMConstants.INITIATE_LEAVE_REQUEST_ACTION_OPTIONS.DELETE)) {
172                         LmServiceLocator.getLeaveRequestDocumentService().suCancelLeave(
173                                 leaveBlock.getLeaveRequestDocumentId(), batchUserPrincipalId);
174 	    	        	LmServiceLocator.getLeaveBlockService().deleteLeaveBlock(leaveBlock.getLmLeaveBlockId(), batchUserPrincipalId);
175 	    	        } else if (StringUtils.equals(getInitiateLeaveRequestAction(), LMConstants.INITIATE_LEAVE_REQUEST_ACTION_OPTIONS.APPROVE)) {
176 	    	        	List<LeaveRequestDocument> leaveRequestDocuments = LmServiceLocator.getLeaveRequestDocumentService().getLeaveRequestDocumentsByLeaveBlockId(leaveBlock.getLmLeaveBlockId());
177 	    	        	for (LeaveRequestDocument leaveRequestDocument : leaveRequestDocuments) {
178 	    	        		LmServiceLocator.getLeaveRequestDocumentService().suBlanketApproveLeave(leaveRequestDocument.getDocumentNumber(), batchUserPrincipalId);
179 	    	        	}
180 	    	        }
181 	    		}
182 	    	}
183         } else {
184         	String principalName = BatchJobUtil.getBatchUserPrincipalName();
185         	LOG.error("Could not update leave request blocks due to missing batch user " + principalName);
186         }
187     }
188     
189     private String getInitiateLeaveRequestAction() {
190     	return ConfigContext.getCurrentContextConfig().getProperty(LMConstants.INITIATE_LEAVE_REQUEST_ACTION);
191     }
192 
193     /**
194      * Preload the document data. It preloads:
195      * - LeaveBlocks on the document.
196      * @param ldoc
197      * @param principalId
198      * @param calEntry
199      */
200     protected void loadLeaveCalendarDocumentData(LeaveCalendarDocument ldoc, String principalId, CalendarEntry calEntry) {
201         List<LeaveBlock> leaveBlocks = LmServiceLocator.getLeaveBlockService().getLeaveBlocksForDocumentId(ldoc.getDocumentId());
202         ldoc.setLeaveBlocks(leaveBlocks);
203         List<Assignment> assignments = HrServiceLocator.getAssignmentService().getAssignmentsByCalEntryForLeaveCalendar(principalId, calEntry);
204         ldoc.setAssignments(assignments);
205     }
206 
207     public LeaveCalendarDao getLeaveCalendarDao() {
208         return leaveCalendarDao;
209     }
210 
211     public void setLeaveCalendarDao(LeaveCalendarDao leaveCalendarDao) {
212         this.leaveCalendarDao = leaveCalendarDao;
213     }
214 
215 	@Override
216 	public LeaveCalendarDocument getLeaveCalendarDocument(
217 			String principalId, CalendarEntry calendarEntry) {
218 		LeaveCalendarDocument leaveCalendarDocument = new LeaveCalendarDocument(calendarEntry);
219 		LeaveCalendarDocumentHeader lcdh = new LeaveCalendarDocumentHeader();
220 		lcdh.setBeginDate(calendarEntry.getBeginPeriodDateTime());
221 		lcdh.setEndDate(calendarEntry.getEndPeriodDateTime());
222 		leaveCalendarDocument.setDocumentHeader(lcdh);
223 		// Fetching assignments
224         List<Assignment> assignments = HrServiceLocator.getAssignmentService().getAssignmentsByCalEntryForLeaveCalendar(principalId, calendarEntry);
225         leaveCalendarDocument.setAssignments(assignments);
226 		return leaveCalendarDocument;
227 	}
228 
229     @Override
230     public void routeLeaveCalendar(String principalId, LeaveCalendarDocument leaveCalendarDocument) {
231         leaveCalendarDocumentAction(HrConstants.DOCUMENT_ACTIONS.ROUTE, principalId, leaveCalendarDocument);
232     }
233     
234     @Override
235     public void routeLeaveCalendar(String principalId, LeaveCalendarDocument leaveCalendarDocument, String action) {
236         leaveCalendarDocumentAction(action, principalId, leaveCalendarDocument);
237     }
238 
239     @Override
240     public void approveLeaveCalendar(String principalId, LeaveCalendarDocument leaveCalendarDocument) {
241         leaveCalendarDocumentAction(HrConstants.DOCUMENT_ACTIONS.APPROVE, principalId, leaveCalendarDocument);
242     }
243     
244     @Override
245     public void approveLeaveCalendar(String principalId, LeaveCalendarDocument leaveCalendarDocument, String action) {
246         leaveCalendarDocumentAction(action, principalId, leaveCalendarDocument);
247     }
248 
249     @Override
250     public void disapproveLeaveCalendar(String principalId, LeaveCalendarDocument leaveCalendarDocument) {
251         leaveCalendarDocumentAction(HrConstants.DOCUMENT_ACTIONS.DISAPPROVE, principalId, leaveCalendarDocument);
252     }
253 
254     public boolean isReadyToApprove(CalendarDocument document) {
255         if (document == null) {
256             return false;
257         }
258         List<LeaveBlock> leaveBlocks = LmServiceLocator.getLeaveBlockService().getLeaveBlocksWithType(document.getPrincipalId(),
259         		document.getCalendarEntry().getBeginPeriodFullDateTime().toLocalDate(), document.getCalendarEntry().getEndPeriodFullDateTime().toLocalDate(), LMConstants.LEAVE_BLOCK_TYPE.BALANCE_TRANSFER);
260         leaveBlocks.addAll(LmServiceLocator.getLeaveBlockService().getLeaveBlocksWithType(document.getPrincipalId(),
261         		document.getCalendarEntry().getBeginPeriodFullDateTime().toLocalDate(), document.getCalendarEntry().getEndPeriodFullDateTime().toLocalDate(), LMConstants.LEAVE_BLOCK_TYPE.LEAVE_PAYOUT));
262         for(LeaveBlock lb : leaveBlocks) {
263         	if(!StringUtils.equals(lb.getRequestStatus(),HrConstants.REQUEST_STATUS.APPROVED) &&
264         			!StringUtils.equals(lb.getRequestStatus(), HrConstants.REQUEST_STATUS.DISAPPROVED))
265         		return false;
266         }
267         // check if there are any pending calendars are there
268         LeaveCalendarDocumentHeader lcdh = LmServiceLocator.getLeaveCalendarDocumentHeaderService().getMinBeginDatePendingLeaveCalendar(document.getPrincipalId());
269         if (lcdh != null){             //if there were any pending document
270             //check to see if it's before the current document. if it is, then this document is not approvable.
271             if (LmServiceLocator.getLeaveCalendarDocumentHeaderService().getDocumentHeader(document.getDocumentId()).getBeginDate().compareTo(lcdh.getEndDate()) >= 0){
272                 return false;
273             }
274         }
275 
276         return true;
277     }
278 
279     protected void leaveCalendarDocumentAction(String action, String principalId, LeaveCalendarDocument leaveCalendarDocument) {
280         WorkflowDocument wd = null;
281         if (leaveCalendarDocument != null) {
282             String rhid = leaveCalendarDocument.getDocumentId();
283             wd = WorkflowDocumentFactory.loadDocument(principalId, rhid);
284 
285             if (StringUtils.equals(action, HrConstants.DOCUMENT_ACTIONS.ROUTE)) {
286                 wd.route("Routing for Approval");
287             } else if (StringUtils.equals(action, HrConstants.BATCH_JOB_ACTIONS.BATCH_JOB_ROUTE)) {
288                 Note.Builder builder = Note.Builder.create(rhid, principalId);
289                 builder.setCreateDate(new DateTime());
290                 builder.setText("Routed via Employee Approval batch job");
291             	KewApiServiceLocator.getNoteService().createNote(builder.build());
292             	
293             	wd.route("Batch job routing leave calendar");
294             } else if (StringUtils.equals(action, HrConstants.DOCUMENT_ACTIONS.APPROVE)) {
295                 if (HrServiceLocator.getHRPermissionService().canSuperUserAdministerCalendarDocument(GlobalVariables.getUserSession().getPrincipalId(), leaveCalendarDocument) 
296                 		&& !HrServiceLocator.getHRPermissionService().canApproveCalendarDocument(GlobalVariables.getUserSession().getPrincipalId(), leaveCalendarDocument)) {
297                     wd.superUserBlanketApprove("Superuser approving timesheet.");
298                 } else {
299                     wd.approve("Approving timesheet.");
300                 }
301             } else if (StringUtils.equals(action, HrConstants.BATCH_JOB_ACTIONS.BATCH_JOB_APPROVE)) {
302             	 Note.Builder builder = Note.Builder.create(rhid, principalId);
303             	 builder.setCreateDate(new DateTime());
304             	 builder.setText("Approved via Supervisor Approval batch job");
305             	 KewApiServiceLocator.getNoteService().createNote(builder.build());
306             	
307             	wd.superUserBlanketApprove("Batch job approving leave calendar");
308             } else if (StringUtils.equals(action, HrConstants.DOCUMENT_ACTIONS.DISAPPROVE)) {
309                 if (HrServiceLocator.getHRPermissionService().canSuperUserAdministerCalendarDocument(GlobalVariables.getUserSession().getPrincipalId(), leaveCalendarDocument) 
310                 		&& !HrServiceLocator.getHRPermissionService().canApproveCalendarDocument(GlobalVariables.getUserSession().getPrincipalId(), leaveCalendarDocument)) {
311                     wd.superUserDisapprove("Superuser disapproving leave calendar.");
312                 } else {
313                     wd.disapprove("Disapproving timesheet.");
314                 }
315             }
316         }
317     }
318 
319     public boolean isLeavePlanningCalendar(String principalId, LocalDate beginDate, LocalDate endDate) {
320         LocalDate today = LocalDate.now();
321 
322         List<Job> jobs = HrServiceLocator.getJobService().getJobs(principalId, endDate);
323         for (Job job : jobs) {
324             //  Check for Leave eligibility.
325             if (job.isEligibleForLeave()) {
326                 //  Check for Time (FLSA nonexempt) jobs. If one exists, then the Leave Calendar is always a Leave Planning Calendar
327                 if (job.getFlsaStatus().equalsIgnoreCase(HrConstants.FLSA_STATUS_NON_EXEMPT)) {
328                     return true;
329                 } else {
330                     //  If leave eligible and FLSA exempt, then report leave in the Leave Calendar. Use the date to determine Planning vs Recording Calendars.
331                     if ( beginDate.isAfter(today) ) {
332                         //  future period, this is a Planning Calendar.
333                         return true;
334                     } else {
335                         //  not a future period, this is a Reporting Calendar.
336                         return false;
337                     }
338                 }
339             } else {
340             //  not leave eligible
341                 return false;
342             }
343         }
344         return false;
345     }
346 
347 }
348