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