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.leavecalendar.service; 017 018 import java.math.BigDecimal; 019 import java.util.Date; 020 import java.util.List; 021 022 import org.apache.commons.collections.CollectionUtils; 023 import org.apache.commons.lang.StringUtils; 024 import org.apache.log4j.Logger; 025 import org.joda.time.DateTime; 026 import org.kuali.hr.job.Job; 027 import org.kuali.hr.lm.LMConstants; 028 import org.kuali.hr.lm.balancetransfer.BalanceTransfer; 029 import org.kuali.hr.lm.leaveblock.LeaveBlock; 030 import org.kuali.hr.lm.leavecalendar.LeaveCalendarDocument; 031 import org.kuali.hr.lm.leavecalendar.dao.LeaveCalendarDao; 032 import org.kuali.hr.lm.leavepayout.LeavePayout; 033 import org.kuali.hr.lm.workflow.LeaveCalendarDocumentHeader; 034 import org.kuali.hr.lm.workflow.LeaveRequestDocument; 035 import org.kuali.hr.time.assignment.Assignment; 036 import org.kuali.hr.time.calendar.CalendarEntries; 037 import org.kuali.hr.time.roles.TkUserRoles; 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.rice.core.api.config.property.ConfigContext; 043 import org.kuali.rice.kew.api.KewApiServiceLocator; 044 import org.kuali.rice.kew.api.WorkflowDocument; 045 import org.kuali.rice.kew.api.WorkflowDocumentFactory; 046 import org.kuali.rice.kew.api.exception.WorkflowException; 047 import org.kuali.rice.kew.api.note.Note; 048 import org.kuali.rice.kim.api.identity.Person; 049 import org.kuali.rice.kim.api.services.KimApiServiceLocator; 050 import org.kuali.rice.krad.service.KRADServiceLocator; 051 052 public class LeaveCalendarServiceImpl implements LeaveCalendarService { 053 054 private static final Logger LOG = Logger.getLogger(LeaveCalendarServiceImpl.class); 055 056 private LeaveCalendarDao leaveCalendarDao; 057 058 @Override 059 public LeaveCalendarDocument getLeaveCalendarDocument(String documentId) { 060 LeaveCalendarDocument lcd = null; 061 LeaveCalendarDocumentHeader lcdh = TkServiceLocator.getLeaveCalendarDocumentHeaderService().getDocumentHeader(documentId); 062 063 if (lcdh != null) { 064 lcd = new LeaveCalendarDocument(lcdh); 065 CalendarEntries pce = TkServiceLocator.getCalendarService().getCalendarDatesByPayEndDate(lcdh.getPrincipalId(), lcdh.getEndDate(), LMConstants.LEAVE_CALENDAR_TYPE); 066 lcd.setCalendarEntry(pce); 067 } else { 068 throw new RuntimeException("Could not find LeaveCalendarDocumentHeader for DocumentID: " + documentId); 069 } 070 071 List<LeaveBlock> leaveBlocks = TkServiceLocator.getLeaveBlockService().getLeaveBlocksForDocumentId(documentId); 072 lcd.setLeaveBlocks(leaveBlocks); 073 074 // Fetching assignments 075 List<Assignment> assignments = TkServiceLocator.getAssignmentService().getAssignmentsByCalEntryForLeaveCalendar(lcdh.getPrincipalId(), lcd.getCalendarEntry()); 076 lcd.setAssignments(assignments); 077 078 return lcd; 079 } 080 081 @Override 082 public LeaveCalendarDocument openLeaveCalendarDocument(String principalId, CalendarEntries calEntry) throws WorkflowException { 083 LeaveCalendarDocument doc; 084 085 Date begin = calEntry.getBeginPeriodDateTime(); 086 Date end = calEntry.getEndPeriodDateTime(); 087 088 LeaveCalendarDocumentHeader header = TkServiceLocator.getLeaveCalendarDocumentHeaderService().getDocumentHeader(principalId, begin, end); 089 if (header == null) { 090 Person person = KimApiServiceLocator.getPersonService().getPerson(principalId); 091 String principalName = person != null ? person.getName() : StringUtils.EMPTY; 092 String beginDateString = TKUtils.formatDate(new java.sql.Date(begin.getTime())); 093 String endDateString = TKUtils.formatDate(new java.sql.Date(end.getTime())); 094 String leaveCalendarDocumentTitle = LeaveCalendarDocument.LEAVE_CALENDAR_DOCUMENT_TYPE + " - " + principalName + " (" + principalId + ") - " + beginDateString + "-" + endDateString; 095 096 doc = initiateWorkflowDocument(principalId, begin, end, calEntry, LeaveCalendarDocument.LEAVE_CALENDAR_DOCUMENT_TYPE, leaveCalendarDocumentTitle); 097 } else { 098 doc = getLeaveCalendarDocument(header.getDocumentId()); 099 } 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