View Javadoc

1   /**
2    * Copyright 2004-2013 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.hr.lm.leavecalendar.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.kuali.hr.job.Job;
23  import org.kuali.hr.lm.LMConstants;
24  import org.kuali.hr.lm.leaveblock.LeaveBlock;
25  import org.kuali.hr.lm.leavecalendar.LeaveCalendarDocument;
26  import org.kuali.hr.lm.leavecalendar.dao.LeaveCalendarDao;
27  import org.kuali.hr.lm.workflow.LeaveCalendarDocumentHeader;
28  import org.kuali.hr.lm.workflow.LeaveRequestDocument;
29  import org.kuali.hr.time.assignment.Assignment;
30  import org.kuali.hr.time.calendar.CalendarEntries;
31  import org.kuali.hr.time.service.base.TkServiceLocator;
32  import org.kuali.hr.time.util.TKContext;
33  import org.kuali.hr.time.util.TKUser;
34  import org.kuali.hr.time.util.TKUtils;
35  import org.kuali.hr.time.util.TkConstants;
36  import org.kuali.rice.core.api.config.property.ConfigContext;
37  import org.kuali.rice.kew.api.KewApiServiceLocator;
38  import org.kuali.rice.kew.api.WorkflowDocument;
39  import org.kuali.rice.kew.api.WorkflowDocumentFactory;
40  import org.kuali.rice.kew.api.exception.WorkflowException;
41  import org.kuali.rice.kew.api.note.Note;
42  import org.kuali.rice.kim.api.identity.principal.EntityNamePrincipalName;
43  import org.kuali.rice.kim.api.identity.principal.Principal;
44  import org.kuali.rice.kim.api.services.KimApiServiceLocator;
45  import org.kuali.rice.krad.service.KRADServiceLocator;
46  
47  import java.math.BigDecimal;
48  import java.util.Date;
49  import java.util.List;
50  
51  public class LeaveCalendarServiceImpl implements LeaveCalendarService {
52  	
53  	private static final Logger LOG = Logger.getLogger(LeaveCalendarServiceImpl.class);
54  
55      private LeaveCalendarDao leaveCalendarDao;
56  
57      @Override
58      public LeaveCalendarDocument getLeaveCalendarDocument(String documentId) {
59          LeaveCalendarDocument lcd = null;
60          LeaveCalendarDocumentHeader lcdh = TkServiceLocator.getLeaveCalendarDocumentHeaderService().getDocumentHeader(documentId);
61  
62          if (lcdh != null) {
63              lcd = new LeaveCalendarDocument(lcdh);
64              CalendarEntries pce = TkServiceLocator.getCalendarService().getCalendarDatesByPayEndDate(lcdh.getPrincipalId(), lcdh.getEndDate(), LMConstants.LEAVE_CALENDAR_TYPE);
65              lcd.setCalendarEntry(pce);
66          } else {
67              LOG.error("Could not find LeaveCalendarDocumentHeader for DocumentID: " + documentId);
68              return null;
69          }
70  
71          List<LeaveBlock> leaveBlocks = TkServiceLocator.getLeaveBlockService().getLeaveBlocksForDocumentId(documentId);
72          lcd.setLeaveBlocks(leaveBlocks);
73  
74          // Fetching assignments
75          List<Assignment> assignments = TkServiceLocator.getAssignmentService().getAssignmentsByCalEntryForLeaveCalendar(lcdh.getPrincipalId(), lcd.getCalendarEntry());
76          lcd.setAssignments(assignments);
77          
78          return lcd;
79      }
80  
81      @Override
82      public LeaveCalendarDocument openLeaveCalendarDocument(String principalId, CalendarEntries calEntry) throws WorkflowException {
83          LeaveCalendarDocument doc;
84  
85          Date begin = calEntry.getBeginPeriodDateTime();
86          Date end = calEntry.getEndPeriodDateTime();
87  
88          LeaveCalendarDocumentHeader header = TkServiceLocator.getLeaveCalendarDocumentHeaderService().getDocumentHeader(principalId, begin, end);
89          if (header == null) {
90              EntityNamePrincipalName person = KimApiServiceLocator.getIdentityService().getDefaultNamesForPrincipalId(principalId);
91              String principalName = person != null && person.getDefaultName() != null ? person.getDefaultName().getCompositeName() : StringUtils.EMPTY;
92              String beginDateString = TKUtils.formatDate(new java.sql.Date(begin.getTime()));
93              String endDateString = TKUtils.formatDate(new java.sql.Date(end.getTime()));
94              String leaveCalendarDocumentTitle = LeaveCalendarDocument.LEAVE_CALENDAR_DOCUMENT_TYPE + " - " + principalName + " (" + principalId + ") - " + beginDateString + "-" + endDateString;
95              
96              doc = initiateWorkflowDocument(principalId, begin, end, calEntry, LeaveCalendarDocument.LEAVE_CALENDAR_DOCUMENT_TYPE, leaveCalendarDocumentTitle);
97          } else {
98              doc = getLeaveCalendarDocument(header.getDocumentId());
99          }
100         if (doc != null) {
101             doc.setCalendarEntry(calEntry);
102         }
103         // TODO: need to set the summary
104         return doc;
105     }
106     
107     //Should only create leave calendar document if active jobs were found with flsa elig = no and ben elg = yes
108     public boolean shouldCreateLeaveDocument(String principalId, CalendarEntries calEntry){
109         if (StringUtils.isEmpty(principalId) || calEntry == null) {
110             return false;
111         }
112         
113         boolean isPlanningCalendar = TkServiceLocator.getLeaveCalendarService().isLeavePlanningCalendar(principalId, calEntry.getBeginPeriodDateTime(), calEntry.getEndPeriodDateTime());
114     	if (isPlanningCalendar) {
115     		return false;
116     	}
117         
118         List<Assignment> assignments = TkServiceLocator.getAssignmentService().getAssignmentsByPayEntry(principalId, calEntry);
119     	List<Assignment> results = TkServiceLocator.getAssignmentService().filterAssignments(assignments, TkConstants.FLSA_STATUS_EXEMPT, true);
120     	return CollectionUtils.isNotEmpty(results);
121     }
122     
123     protected LeaveCalendarDocument initiateWorkflowDocument(String principalId, Date payBeginDate, Date payEndDate, CalendarEntries calendarEntries, String documentType, String title) throws WorkflowException {
124         LeaveCalendarDocument leaveCalendarDocument = null;
125         WorkflowDocument workflowDocument = null;
126 
127         workflowDocument =  WorkflowDocumentFactory.createDocument(principalId, documentType, title);
128 
129         String status = workflowDocument.getStatus().getCode();
130         LeaveCalendarDocumentHeader documentHeader = new LeaveCalendarDocumentHeader(workflowDocument.getDocumentId(), principalId, payBeginDate, payEndDate, status);
131 
132         documentHeader.setDocumentId(workflowDocument.getDocumentId());
133         documentHeader.setDocumentStatus(TkConstants.ROUTE_STATUS.INITIATED);
134 
135         KRADServiceLocator.getBusinessObjectService().save(documentHeader);
136         
137         leaveCalendarDocument = new LeaveCalendarDocument(documentHeader);
138         leaveCalendarDocument.setCalendarEntry(calendarEntries);
139         loadLeaveCalendarDocumentData(leaveCalendarDocument, principalId, calendarEntries);
140         TkServiceLocator.getTkSearchableAttributeService().updateSearchableAttribute(leaveCalendarDocument, payEndDate);
141         
142         updateLeaveBlockDocumentIds(principalId, payBeginDate, payEndDate, workflowDocument.getDocumentId());
143         
144         updatePlannedLeaveBlocks(principalId, payBeginDate, payEndDate);
145 
146         return leaveCalendarDocument;
147     }
148     
149     private void updateLeaveBlockDocumentIds(String principalId, Date beginDate, Date endDate, String documentId) {
150         List<LeaveBlock> leaveBlocks = TkServiceLocator.getLeaveBlockService().getLeaveBlocks(principalId, beginDate, endDate);
151         
152         for (LeaveBlock leaveBlock : leaveBlocks) {
153         	leaveBlock.setDocumentId(documentId);
154         }
155         
156         TkServiceLocator.getLeaveBlockService().saveLeaveBlocks(leaveBlocks);
157     }
158     
159     private void updatePlannedLeaveBlocks(String principalId, Date beginDate, Date endDate) {
160         String batchUserPrincipalId = getBatchUserPrincipalId();
161         
162         if (batchUserPrincipalId != null) {
163 	    	List<LeaveBlock> leaveBlocks = TkServiceLocator.getLeaveBlockService().getLeaveBlocks(principalId, beginDate, endDate);
164 	
165 	    	for (LeaveBlock leaveBlock : leaveBlocks) {
166 	    		if (StringUtils.equals(leaveBlock.getRequestStatus(), LMConstants.REQUEST_STATUS.PLANNED) 
167 	    				|| StringUtils.equals(leaveBlock.getRequestStatus(), LMConstants.REQUEST_STATUS.DEFERRED)) {
168 	    			TkServiceLocator.getLeaveBlockService().deleteLeaveBlock(leaveBlock.getLmLeaveBlockId(), batchUserPrincipalId);
169 	    		} else if (StringUtils.equals(leaveBlock.getRequestStatus(), LMConstants.REQUEST_STATUS.REQUESTED)) {
170 	    	        if (StringUtils.equals(getInitiateLeaveRequestAction(), LMConstants.INITIATE_LEAVE_REQUEST_ACTION_OPTIONS.DELETE)) {
171 	    	        	TkServiceLocator.getLeaveBlockService().deleteLeaveBlock(leaveBlock.getLmLeaveBlockId(), batchUserPrincipalId);
172 	    	        } else if (StringUtils.equals(getInitiateLeaveRequestAction(), LMConstants.INITIATE_LEAVE_REQUEST_ACTION_OPTIONS.APPROVE)) {
173 	    	        	List<LeaveRequestDocument> leaveRequestDocuments = TkServiceLocator.getLeaveRequestDocumentService().getLeaveRequestDocumentsByLeaveBlockId(leaveBlock.getLmLeaveBlockId());
174 	    	        	for (LeaveRequestDocument leaveRequestDocument : leaveRequestDocuments) {
175 	    	        		TkServiceLocator.getLeaveRequestDocumentService().suBlanketApproveLeave(leaveRequestDocument.getDocumentNumber(), batchUserPrincipalId);
176 	    	        	}
177 	    	        }
178 	    		}
179 	    	}
180         } else {
181         	String principalName = ConfigContext.getCurrentContextConfig().getProperty(TkConstants.BATCH_USER_PRINCIPAL_NAME);
182         	LOG.error("Could not update leave request blocks due to missing batch user " + principalName);
183         }
184     }
185     
186     private String getBatchUserPrincipalId() {
187     	String principalName = ConfigContext.getCurrentContextConfig().getProperty(TkConstants.BATCH_USER_PRINCIPAL_NAME);
188         Principal principal = KimApiServiceLocator.getIdentityService().getPrincipalByPrincipalName(principalName);
189         return principal == null ? null : principal.getPrincipalId();
190     }
191     
192     private String getInitiateLeaveRequestAction() {
193     	return ConfigContext.getCurrentContextConfig().getProperty(LMConstants.INITIATE_LEAVE_REQUEST_ACTION);
194     }
195 
196     /**
197      * Preload the document data. It preloads:
198      * - LeaveBlocks on the document.
199      * @param ldoc
200      * @param principalId
201      * @param calEntry
202      */
203     protected void loadLeaveCalendarDocumentData(LeaveCalendarDocument ldoc, String principalId, CalendarEntries calEntry) {
204         List<LeaveBlock> leaveBlocks = TkServiceLocator.getLeaveBlockService().getLeaveBlocksForDocumentId(ldoc.getDocumentId());
205         ldoc.setLeaveBlocks(leaveBlocks);
206         List<Assignment> assignments = TkServiceLocator.getAssignmentService().getAssignmentsByCalEntryForLeaveCalendar(principalId, calEntry);
207         ldoc.setAssignments(assignments);
208     }
209 
210     public LeaveCalendarDao getLeaveCalendarDao() {
211         return leaveCalendarDao;
212     }
213 
214     public void setLeaveCalendarDao(LeaveCalendarDao leaveCalendarDao) {
215         this.leaveCalendarDao = leaveCalendarDao;
216     }
217 
218 	@Override
219 	public LeaveCalendarDocument getLeaveCalendarDocument(
220 			String principalId, CalendarEntries calendarEntry) {
221 		LeaveCalendarDocument leaveCalendarDocument = new LeaveCalendarDocument(calendarEntry);
222 		LeaveCalendarDocumentHeader lcdh = new LeaveCalendarDocumentHeader();
223 		lcdh.setBeginDate(calendarEntry.getBeginPeriodDateTime());
224 		lcdh.setEndDate(calendarEntry.getEndPeriodDateTime());
225 		leaveCalendarDocument.setDocumentHeader(lcdh);
226 		// Fetching assignments
227         List<Assignment> assignments = TkServiceLocator.getAssignmentService().getAssignmentsByCalEntryForLeaveCalendar(principalId, calendarEntry);
228         leaveCalendarDocument.setAssignments(assignments);
229 		return leaveCalendarDocument;
230 	}
231 
232     @Override
233     public void routeLeaveCalendar(String principalId, LeaveCalendarDocument leaveCalendarDocument) {
234         leaveCalendarDocumentAction(TkConstants.DOCUMENT_ACTIONS.ROUTE, principalId, leaveCalendarDocument);
235     }
236     
237     @Override
238     public void routeLeaveCalendar(String principalId, LeaveCalendarDocument leaveCalendarDocument, String action) {
239         leaveCalendarDocumentAction(action, principalId, leaveCalendarDocument);
240     }
241 
242     @Override
243     public void approveLeaveCalendar(String principalId, LeaveCalendarDocument leaveCalendarDocument) {
244         leaveCalendarDocumentAction(TkConstants.DOCUMENT_ACTIONS.APPROVE, principalId, leaveCalendarDocument);
245     }
246     
247     @Override
248     public void approveLeaveCalendar(String principalId, LeaveCalendarDocument leaveCalendarDocument, String action) {
249         leaveCalendarDocumentAction(action, principalId, leaveCalendarDocument);
250     }
251 
252     @Override
253     public void disapproveLeaveCalendar(String principalId, LeaveCalendarDocument leaveCalendarDocument) {
254         leaveCalendarDocumentAction(TkConstants.DOCUMENT_ACTIONS.DISAPPROVE, principalId, leaveCalendarDocument);
255     }
256 
257     public boolean isReadyToApprove(LeaveCalendarDocument document) {
258         if (document == null) {
259             return false;
260         }
261         List<LeaveBlock> leaveBlocks = TkServiceLocator.getLeaveBlockService().getLeaveBlocksWithType(document.getPrincipalId(),
262         		document.getCalendarEntry().getBeginPeriodDate(), document.getCalendarEntry().getEndPeriodDate(), LMConstants.LEAVE_BLOCK_TYPE.BALANCE_TRANSFER);
263         leaveBlocks.addAll(TkServiceLocator.getLeaveBlockService().getLeaveBlocksWithType(document.getPrincipalId(),
264         		document.getCalendarEntry().getBeginPeriodDate(), document.getCalendarEntry().getEndPeriodDate(), LMConstants.LEAVE_BLOCK_TYPE.LEAVE_PAYOUT));
265         for(LeaveBlock lb : leaveBlocks) {
266         	if(!StringUtils.equals(lb.getRequestStatus(),LMConstants.REQUEST_STATUS.APPROVED) &&
267         			!StringUtils.equals(lb.getRequestStatus(), LMConstants.REQUEST_STATUS.DISAPPROVED))
268         		return false;
269         }
270         // check if there are any pending calendars are there
271         LeaveCalendarDocumentHeader lcdh = TkServiceLocator.getLeaveCalendarDocumentHeaderService().getMinBeginDatePendingLeaveCalendar(document.getPrincipalId());
272         if (lcdh != null){             //if there were any pending document
273             //check to see if it's before the current document. if it is, then this document is not approvable.
274             if (TkServiceLocator.getLeaveCalendarDocumentHeaderService().getDocumentHeader(document.getDocumentId()).getBeginDate().compareTo(lcdh.getEndDate()) >= 0){
275                 return false;
276             }
277         }
278 
279         return true;
280 /*        List<BalanceTransfer> balanceTransfers = TkServiceLocator.getBalanceTransferService().getBalanceTransfers(document.getPrincipalId(),
281                 document.getCalendarEntry().getBeginPeriodDate(),
282                 document.getCalendarEntry().getEndPeriodDate());
283         if (!CollectionUtils.isEmpty(balanceTransfers))   {
284 	        for(BalanceTransfer balanceTransfer : balanceTransfers) {
285 	        	if(StringUtils.equals(TkConstants.DOCUMENT_STATUS.get(balanceTransfer.getStatus()), TkConstants.ROUTE_STATUS.ENROUTE))
286 	        		return false;
287 	            if (!StringUtils.equals(LMConstants.REQUEST_STATUS.APPROVED, balanceTransfer.getStatus())
288 	                    && !StringUtils.equals(LMConstants.REQUEST_STATUS.DISAPPROVED, balanceTransfer.getStatus())) {
289 	                return false;
290 	            }
291 	        }
292         }
293         List<LeavePayout> leavePayouts = TkServiceLocator.getLeavePayoutService().getLeavePayouts(document.getPrincipalId(),
294         		document.getCalendarEntry().getBeginPeriodDate(),
295         		document.getCalendarEntry().getEndPeriodDate());
296         if (!CollectionUtils.isEmpty(leavePayouts)) {
297         	for(LeavePayout payout : leavePayouts) {
298 	        	if(StringUtils.equals(TkConstants.DOCUMENT_STATUS.get(payout.getStatus()), TkConstants.ROUTE_STATUS.ENROUTE))
299 	        		return false;
300 	            if (!StringUtils.equals(LMConstants.REQUEST_STATUS.APPROVED, payout.getStatus())
301 	                    && !StringUtils.equals(LMConstants.REQUEST_STATUS.DISAPPROVED, payout.getStatus())) {
302 	                return false;
303 	            }
304         	}
305         }
306         return true;*/
307     }
308 
309     protected void leaveCalendarDocumentAction(String action, String principalId, LeaveCalendarDocument leaveCalendarDocument) {
310         WorkflowDocument wd = null;
311         if (leaveCalendarDocument != null) {
312             String rhid = leaveCalendarDocument.getDocumentId();
313             wd = WorkflowDocumentFactory.loadDocument(principalId, rhid);
314 
315             if (StringUtils.equals(action, TkConstants.DOCUMENT_ACTIONS.ROUTE)) {
316                 wd.route("Routing for Approval");
317             } else if (StringUtils.equals(action, TkConstants.BATCH_JOB_ACTIONS.BATCH_JOB_ROUTE)) {
318                 Note.Builder builder = Note.Builder.create(rhid, principalId);
319                 builder.setCreateDate(new DateTime());
320                 builder.setText("Routed via Employee Approval batch job");
321             	KewApiServiceLocator.getNoteService().createNote(builder.build());
322             	
323             	wd.route("Batch job routing leave calendar");
324             } else if (StringUtils.equals(action, TkConstants.DOCUMENT_ACTIONS.APPROVE)) {
325                 if (TKUser.getCurrentTargetRoles().isSystemAdmin() &&
326                         !TKUser.getCurrentTargetRoles().isApproverForTimesheet(leaveCalendarDocument)) {
327                     wd.superUserBlanketApprove("Superuser approving timesheet.");
328                 } else {
329                     wd.approve("Approving timesheet.");
330                 }
331             } else if (StringUtils.equals(action, TkConstants.BATCH_JOB_ACTIONS.BATCH_JOB_APPROVE)) {
332             	 Note.Builder builder = Note.Builder.create(rhid, principalId);
333             	 builder.setCreateDate(new DateTime());
334             	 builder.setText("Approved via Supervisor Approval batch job");
335             	 KewApiServiceLocator.getNoteService().createNote(builder.build());
336             	
337             	wd.superUserBlanketApprove("Batch job approving leave calendar");
338             } else if (StringUtils.equals(action, TkConstants.DOCUMENT_ACTIONS.DISAPPROVE)) {
339                 if (TKUser.getCurrentTargetRoles().isSystemAdmin()
340                         && !TKUser.getCurrentTargetRoles().isApproverForTimesheet(leaveCalendarDocument)) {
341                     wd.superUserDisapprove("Superuser disapproving timesheet.");
342                 } else {
343                     wd.disapprove("Disapproving timesheet.");
344                 }
345             }
346         }
347     }
348 
349     public boolean isLeavePlanningCalendar(String principalId, Date beginDate, Date endDate) {
350         Date today = new Date();
351 
352         List<Job> jobs = TkServiceLocator.getJobService().getJobs(principalId, endDate);
353         for (Job job : jobs) {
354             //  Check for Leave eligibility.
355             if (job.isEligibleForLeave()) {
356                 //  Check for Time (FLSA nonexempt) jobs. If one exists, then the Leave Calendar is always a Leave Planning Calendar
357                 if (job.getFlsaStatus().equalsIgnoreCase(TkConstants.FLSA_STATUS_NON_EXEMPT)) {
358                     return true;
359                 } else {
360                     //  If leave eligible and FLSA exempt, then report leave in the Leave Calendar. Use the date to determine Planning vs Recording Calendars.
361                     if ( beginDate.after(today) ) {
362                         //  future period, this is a Planning Calendar.
363                         return true;
364                     } else {
365                         //  not a future period, this is a Reporting Calendar.
366                         return false;
367                     }
368                 }
369             } else {
370             //  not leave eligible
371                 return false;
372             }
373         }
374         return false;
375     }
376 
377 }
378