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              throw new RuntimeException("Could not find LeaveCalendarDocumentHeader for DocumentID: " + documentId);
68          }
69  
70          List<LeaveBlock> leaveBlocks = TkServiceLocator.getLeaveBlockService().getLeaveBlocksForDocumentId(documentId);
71          lcd.setLeaveBlocks(leaveBlocks);
72  
73          // Fetching assignments
74          List<Assignment> assignments = TkServiceLocator.getAssignmentService().getAssignmentsByCalEntryForLeaveCalendar(lcdh.getPrincipalId(), lcd.getCalendarEntry());
75          lcd.setAssignments(assignments);
76          
77          return lcd;
78      }
79  
80      @Override
81      public LeaveCalendarDocument openLeaveCalendarDocument(String principalId, CalendarEntries calEntry) throws WorkflowException {
82          LeaveCalendarDocument doc;
83  
84          Date begin = calEntry.getBeginPeriodDateTime();
85          Date end = calEntry.getEndPeriodDateTime();
86  
87          LeaveCalendarDocumentHeader header = TkServiceLocator.getLeaveCalendarDocumentHeaderService().getDocumentHeader(principalId, begin, end);
88          if (header == null) {
89              EntityNamePrincipalName person = KimApiServiceLocator.getIdentityService().getDefaultNamesForPrincipalId(principalId);
90              String principalName = person != null && person.getDefaultName() != null ? person.getDefaultName().getCompositeName() : StringUtils.EMPTY;
91              String beginDateString = TKUtils.formatDate(new java.sql.Date(begin.getTime()));
92              String endDateString = TKUtils.formatDate(new java.sql.Date(end.getTime()));
93              String leaveCalendarDocumentTitle = LeaveCalendarDocument.LEAVE_CALENDAR_DOCUMENT_TYPE + " - " + principalName + " (" + principalId + ") - " + beginDateString + "-" + endDateString;
94              
95              doc = initiateWorkflowDocument(principalId, begin, end, calEntry, LeaveCalendarDocument.LEAVE_CALENDAR_DOCUMENT_TYPE, leaveCalendarDocumentTitle);
96          } else {
97              doc = getLeaveCalendarDocument(header.getDocumentId());
98          }
99          doc.setCalendarEntry(calEntry);
100         // TODO: need to set the summary
101         return doc;
102     }
103     
104     //Should only create leave calendar document if active jobs were found with flsa elig = no and ben elg = yes
105     public boolean shouldCreateLeaveDocument(String principalId, CalendarEntries calEntry){
106         if (StringUtils.isEmpty(principalId) || calEntry == null) {
107             return false;
108         }
109         
110         boolean isPlanningCalendar = TkServiceLocator.getLeaveCalendarService().isLeavePlanningCalendar(principalId, calEntry.getBeginPeriodDateTime(), calEntry.getEndPeriodDateTime());
111     	if (isPlanningCalendar) {
112     		return false;
113     	}
114         
115         List<Assignment> assignments = TkServiceLocator.getAssignmentService().getAssignmentsByPayEntry(principalId, calEntry);
116     	List<Assignment> results = TkServiceLocator.getAssignmentService().filterAssignments(assignments, TkConstants.FLSA_STATUS_EXEMPT, true);
117     	return CollectionUtils.isNotEmpty(results);
118     }
119     
120     protected LeaveCalendarDocument initiateWorkflowDocument(String principalId, Date payBeginDate, Date payEndDate, CalendarEntries calendarEntries, String documentType, String title) throws WorkflowException {
121         LeaveCalendarDocument leaveCalendarDocument = null;
122         WorkflowDocument workflowDocument = null;
123 
124         workflowDocument =  WorkflowDocumentFactory.createDocument(principalId, documentType, title);
125 
126         String status = workflowDocument.getStatus().getCode();
127         LeaveCalendarDocumentHeader documentHeader = new LeaveCalendarDocumentHeader(workflowDocument.getDocumentId(), principalId, payBeginDate, payEndDate, status);
128 
129         documentHeader.setDocumentId(workflowDocument.getDocumentId());
130         documentHeader.setDocumentStatus(TkConstants.ROUTE_STATUS.INITIATED);
131 
132         KRADServiceLocator.getBusinessObjectService().save(documentHeader);
133         
134         leaveCalendarDocument = new LeaveCalendarDocument(documentHeader);
135         leaveCalendarDocument.setCalendarEntry(calendarEntries);
136         loadLeaveCalendarDocumentData(leaveCalendarDocument, principalId, calendarEntries);
137         TkServiceLocator.getTkSearchableAttributeService().updateSearchableAttribute(leaveCalendarDocument, payEndDate);
138         
139         updateLeaveBlockDocumentIds(principalId, payBeginDate, payEndDate, workflowDocument.getDocumentId());
140         
141         updatePlannedLeaveBlocks(principalId, payBeginDate, payEndDate);
142 
143         return leaveCalendarDocument;
144     }
145     
146     private void updateLeaveBlockDocumentIds(String principalId, Date beginDate, Date endDate, String documentId) {
147         List<LeaveBlock> leaveBlocks = TkServiceLocator.getLeaveBlockService().getLeaveBlocks(principalId, beginDate, endDate);
148         
149         for (LeaveBlock leaveBlock : leaveBlocks) {
150         	leaveBlock.setDocumentId(documentId);
151         }
152         
153         TkServiceLocator.getLeaveBlockService().saveLeaveBlocks(leaveBlocks);
154     }
155     
156     private void updatePlannedLeaveBlocks(String principalId, Date beginDate, Date endDate) {
157         String batchUserPrincipalId = getBatchUserPrincipalId();
158         
159         if (batchUserPrincipalId != null) {
160 	    	List<LeaveBlock> leaveBlocks = TkServiceLocator.getLeaveBlockService().getLeaveBlocks(principalId, beginDate, endDate);
161 	
162 	    	for (LeaveBlock leaveBlock : leaveBlocks) {
163 	    		if (StringUtils.equals(leaveBlock.getRequestStatus(), LMConstants.REQUEST_STATUS.PLANNED) 
164 	    				|| StringUtils.equals(leaveBlock.getRequestStatus(), LMConstants.REQUEST_STATUS.DEFERRED)) {
165 	    			TkServiceLocator.getLeaveBlockService().deleteLeaveBlock(leaveBlock.getLmLeaveBlockId(), batchUserPrincipalId);
166 	    		} else if (StringUtils.equals(leaveBlock.getRequestStatus(), LMConstants.REQUEST_STATUS.REQUESTED)) {
167 	    	        if (StringUtils.equals(getInitiateLeaveRequestAction(), LMConstants.INITIATE_LEAVE_REQUEST_ACTION_OPTIONS.DELETE)) {
168 	    	        	TkServiceLocator.getLeaveBlockService().deleteLeaveBlock(leaveBlock.getLmLeaveBlockId(), batchUserPrincipalId);
169 	    	        } else if (StringUtils.equals(getInitiateLeaveRequestAction(), LMConstants.INITIATE_LEAVE_REQUEST_ACTION_OPTIONS.APPROVE)) {
170 	    	        	List<LeaveRequestDocument> leaveRequestDocuments = TkServiceLocator.getLeaveRequestDocumentService().getLeaveRequestDocumentsByLeaveBlockId(leaveBlock.getLmLeaveBlockId());
171 	    	        	for (LeaveRequestDocument leaveRequestDocument : leaveRequestDocuments) {
172 	    	        		TkServiceLocator.getLeaveRequestDocumentService().suBlanketApproveLeave(leaveRequestDocument.getDocumentNumber(), batchUserPrincipalId);
173 	    	        	}
174 	    	        }
175 	    		}
176 	    	}
177         } else {
178         	String principalName = ConfigContext.getCurrentContextConfig().getProperty(TkConstants.BATCH_USER_PRINCIPAL_NAME);
179         	LOG.error("Could not update leave request blocks due to missing batch user " + principalName);
180         }
181     }
182     
183     private String getBatchUserPrincipalId() {
184     	String principalName = ConfigContext.getCurrentContextConfig().getProperty(TkConstants.BATCH_USER_PRINCIPAL_NAME);
185         Principal principal = KimApiServiceLocator.getIdentityService().getPrincipalByPrincipalName(principalName);
186         return principal == null ? null : principal.getPrincipalId();
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, CalendarEntries calEntry) {
201         List<LeaveBlock> leaveBlocks = TkServiceLocator.getLeaveBlockService().getLeaveBlocksForDocumentId(ldoc.getDocumentId());
202         ldoc.setLeaveBlocks(leaveBlocks);
203         List<Assignment> assignments = TkServiceLocator.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, CalendarEntries 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 = TkServiceLocator.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(TkConstants.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(TkConstants.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(TkConstants.DOCUMENT_ACTIONS.DISAPPROVE, principalId, leaveCalendarDocument);
252     }
253 
254     public boolean isReadyToApprove(LeaveCalendarDocument document) {
255         if (document == null) {
256             return false;
257         }
258         List<LeaveBlock> leaveBlocks = TkServiceLocator.getLeaveBlockService().getLeaveBlocksWithType(document.getPrincipalId(),
259         		document.getCalendarEntry().getBeginPeriodDate(), document.getCalendarEntry().getEndPeriodDate(), LMConstants.LEAVE_BLOCK_TYPE.BALANCE_TRANSFER);
260         leaveBlocks.addAll(TkServiceLocator.getLeaveBlockService().getLeaveBlocksWithType(document.getPrincipalId(),
261         		document.getCalendarEntry().getBeginPeriodDate(), document.getCalendarEntry().getEndPeriodDate(), LMConstants.LEAVE_BLOCK_TYPE.LEAVE_PAYOUT));
262         for(LeaveBlock lb : leaveBlocks) {
263         	if(!StringUtils.equals(lb.getRequestStatus(),LMConstants.REQUEST_STATUS.APPROVED) &&
264         			!StringUtils.equals(lb.getRequestStatus(), LMConstants.REQUEST_STATUS.DISAPPROVED))
265         		return false;
266         }
267         // check if there are any pending calendars are there
268         LeaveCalendarDocumentHeader lcdh = TkServiceLocator.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 (TkServiceLocator.getLeaveCalendarDocumentHeaderService().getDocumentHeader(document.getDocumentId()).getBeginDate().compareTo(lcdh.getEndDate()) >= 0){
272                 return false;
273             }
274         }
275 
276         return true;
277 /*        List<BalanceTransfer> balanceTransfers = TkServiceLocator.getBalanceTransferService().getBalanceTransfers(document.getPrincipalId(),
278                 document.getCalendarEntry().getBeginPeriodDate(),
279                 document.getCalendarEntry().getEndPeriodDate());
280         if (!CollectionUtils.isEmpty(balanceTransfers))   {
281 	        for(BalanceTransfer balanceTransfer : balanceTransfers) {
282 	        	if(StringUtils.equals(TkConstants.DOCUMENT_STATUS.get(balanceTransfer.getStatus()), TkConstants.ROUTE_STATUS.ENROUTE))
283 	        		return false;
284 	            if (!StringUtils.equals(LMConstants.REQUEST_STATUS.APPROVED, balanceTransfer.getStatus())
285 	                    && !StringUtils.equals(LMConstants.REQUEST_STATUS.DISAPPROVED, balanceTransfer.getStatus())) {
286 	                return false;
287 	            }
288 	        }
289         }
290         List<LeavePayout> leavePayouts = TkServiceLocator.getLeavePayoutService().getLeavePayouts(document.getPrincipalId(),
291         		document.getCalendarEntry().getBeginPeriodDate(),
292         		document.getCalendarEntry().getEndPeriodDate());
293         if (!CollectionUtils.isEmpty(leavePayouts)) {
294         	for(LeavePayout payout : leavePayouts) {
295 	        	if(StringUtils.equals(TkConstants.DOCUMENT_STATUS.get(payout.getStatus()), TkConstants.ROUTE_STATUS.ENROUTE))
296 	        		return false;
297 	            if (!StringUtils.equals(LMConstants.REQUEST_STATUS.APPROVED, payout.getStatus())
298 	                    && !StringUtils.equals(LMConstants.REQUEST_STATUS.DISAPPROVED, payout.getStatus())) {
299 	                return false;
300 	            }
301         	}
302         }
303         return true;*/
304     }
305 
306     protected void leaveCalendarDocumentAction(String action, String principalId, LeaveCalendarDocument leaveCalendarDocument) {
307         WorkflowDocument wd = null;
308         if (leaveCalendarDocument != null) {
309             String rhid = leaveCalendarDocument.getDocumentId();
310             wd = WorkflowDocumentFactory.loadDocument(principalId, rhid);
311 
312             if (StringUtils.equals(action, TkConstants.DOCUMENT_ACTIONS.ROUTE)) {
313                 wd.route("Routing for Approval");
314             } else if (StringUtils.equals(action, TkConstants.BATCH_JOB_ACTIONS.BATCH_JOB_ROUTE)) {
315                 Note.Builder builder = Note.Builder.create(rhid, principalId);
316                 builder.setCreateDate(new DateTime());
317                 builder.setText("Routed via Employee Approval batch job");
318             	KewApiServiceLocator.getNoteService().createNote(builder.build());
319             	
320             	wd.route("Batch job routing leave calendar");
321             } else if (StringUtils.equals(action, TkConstants.DOCUMENT_ACTIONS.APPROVE)) {
322                 if (TKUser.getCurrentTargetRoles().isSystemAdmin() &&
323                         !TKUser.getCurrentTargetRoles().isApproverForTimesheet(leaveCalendarDocument)) {
324                     wd.superUserBlanketApprove("Superuser approving timesheet.");
325                 } else {
326                     wd.approve("Approving timesheet.");
327                 }
328             } else if (StringUtils.equals(action, TkConstants.BATCH_JOB_ACTIONS.BATCH_JOB_APPROVE)) {
329             	 Note.Builder builder = Note.Builder.create(rhid, principalId);
330             	 builder.setCreateDate(new DateTime());
331             	 builder.setText("Approved via Supervisor Approval batch job");
332             	 KewApiServiceLocator.getNoteService().createNote(builder.build());
333             	
334             	wd.superUserBlanketApprove("Batch job approving leave calendar");
335             } else if (StringUtils.equals(action, TkConstants.DOCUMENT_ACTIONS.DISAPPROVE)) {
336                 if (TKUser.getCurrentTargetRoles().isSystemAdmin()
337                         && !TKUser.getCurrentTargetRoles().isApproverForTimesheet(leaveCalendarDocument)) {
338                     wd.superUserDisapprove("Superuser disapproving timesheet.");
339                 } else {
340                     wd.disapprove("Disapproving timesheet.");
341                 }
342             }
343         }
344     }
345 
346     public boolean isLeavePlanningCalendar(String principalId, Date beginDate, Date endDate) {
347         Date today = new Date();
348 
349         List<Job> jobs = TkServiceLocator.getJobService().getJobs(principalId, endDate);
350         for (Job job : jobs) {
351             //  Check for Leave eligibility.
352             if (job.isEligibleForLeave()) {
353                 //  Check for Time (FLSA nonexempt) jobs. If one exists, then the Leave Calendar is always a Leave Planning Calendar
354                 if (job.getFlsaStatus().equalsIgnoreCase(TkConstants.FLSA_STATUS_NON_EXEMPT)) {
355                     return true;
356                 } else {
357                     //  If leave eligible and FLSA exempt, then report leave in the Leave Calendar. Use the date to determine Planning vs Recording Calendars.
358                     if ( beginDate.after(today) ) {
359                         //  future period, this is a Planning Calendar.
360                         return true;
361                     } else {
362                         //  not a future period, this is a Reporting Calendar.
363                         return false;
364                     }
365                 }
366             } else {
367             //  not leave eligible
368                 return false;
369             }
370         }
371         return false;
372     }
373 
374 }
375