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.core.earncode.service;
17  
18  import java.math.BigDecimal;
19  import java.util.ArrayList;
20  import java.util.Comparator;
21  import java.util.HashSet;
22  import java.util.LinkedHashMap;
23  import java.util.LinkedList;
24  import java.util.List;
25  import java.util.Map;
26  import java.util.Set;
27  
28  import org.apache.commons.lang.StringUtils;
29  import org.apache.log4j.Logger;
30  import org.joda.time.DateTime;
31  import org.joda.time.LocalDate;
32  import org.kuali.kpme.core.KPMENamespace;
33  import org.kuali.kpme.core.accrualcategory.AccrualCategory;
34  import org.kuali.kpme.core.assignment.Assignment;
35  import org.kuali.kpme.core.department.Department;
36  import org.kuali.kpme.core.earncode.EarnCode;
37  import org.kuali.kpme.core.earncode.dao.EarnCodeDao;
38  import org.kuali.kpme.core.earncode.security.EarnCodeSecurity;
39  import org.kuali.kpme.core.earncode.security.EarnCodeType;
40  import org.kuali.kpme.core.job.Job;
41  import org.kuali.kpme.core.principal.PrincipalHRAttributes;
42  import org.kuali.kpme.core.role.KPMERole;
43  import org.kuali.kpme.core.service.HrServiceLocator;
44  import org.kuali.kpme.core.util.HrConstants;
45  import org.kuali.kpme.core.util.HrContext;
46  import org.kuali.kpme.core.workarea.WorkArea;
47  import org.kuali.rice.kim.api.role.RoleService;
48  import org.kuali.rice.kim.api.services.KimApiServiceLocator;
49  import org.kuali.rice.krad.util.GlobalVariables;
50  
51  import com.google.common.collect.Ordering;
52  
53  public class EarnCodeServiceImpl implements EarnCodeService {
54  
55  	private EarnCodeDao earnCodeDao;
56  	private static final Logger LOG = Logger.getLogger(EarnCodeServiceImpl.class);
57  
58  	public void setEarnCodeDao(EarnCodeDao earnCodeDao) {
59  		this.earnCodeDao = earnCodeDao;
60  	}
61  
62  	//Move to LeaveCalendarDocumentService
63      public List<EarnCode> getEarnCodesForLeave(Assignment a, LocalDate asOfDate, boolean isLeavePlanningCalendar) {
64          //getEarnCodesForTime and getEarnCodesForLeave have some overlapping logic, but they were separated so that they could follow their own distinct logic, so consolidation of logic is not desirable.
65  
66          if (a == null){
67          	LOG.error("No assignment parameter.");
68          	return null;
69  //        	throw new RuntimeException("No assignment parameter.");
70          }
71          
72          Job job = a.getJob();
73          if (job == null || job.getPayTypeObj() == null) { 
74  //        	throw new RuntimeException("Null job or null job pay type on assignment.");
75          	LOG.error("Null job or null job pay type on assignment.");
76          	return null;
77          }
78  
79          List<EarnCode> earnCodes = new LinkedList<EarnCode>();
80          String earnTypeCode = EarnCodeType.LEAVE.getCode();
81          // skip getting the regular earn code for Leave Calendar
82  
83          List<String> listAccrualCategories = new LinkedList<String>();
84          String accrualCategory;
85  
86          //  first make a list of the accrual categories available to the user's leave plan, for later comparison.
87          PrincipalHRAttributes principalHRAttributes = HrServiceLocator.getPrincipalHRAttributeService().getPrincipalCalendar(job.getPrincipalId(), asOfDate);
88          boolean fmlaEligible = principalHRAttributes.isFmlaEligible();
89          boolean workersCompEligible = principalHRAttributes.isWorkersCompEligible();
90  
91          String leavePlan = principalHRAttributes.getLeavePlan();
92          if (leavePlan != null) {
93              for (AccrualCategory accrualCategories : HrServiceLocator.getAccrualCategoryService().getActiveAccrualCategoriesForLeavePlan(leavePlan, asOfDate)) {
94                  accrualCategory = accrualCategories.getAccrualCategory();
95                  if(accrualCategory != null) {
96                      listAccrualCategories.add(accrualCategory);
97                  }
98              }
99          }
100 
101         //  get all earn codes by user security, then we'll filter on accrual category first as we process them.
102         List<EarnCodeSecurity> decs = HrServiceLocator.getEarnCodeSecurityService().getEarnCodeSecurities(job.getDept(), job.getHrSalGroup(), job.getLocation(), asOfDate);
103         for (EarnCodeSecurity dec : decs) {
104 
105             boolean addEarnCode = addEarnCodeBasedOnEmployeeApproverSettings(dec, a, asOfDate);
106             if (addEarnCode) {
107 
108                 //  allow types Leave AND Both
109                 if (earnTypeCode.equals(dec.getEarnCodeType()) || EarnCodeType.BOTH.getCode().equals(dec.getEarnCodeType())) {
110                     EarnCode ec = getEarnCode(dec.getEarnCode(), asOfDate);
111 
112                     //  make sure we got something back from the earn code dao
113                     if (ec != null) {
114                     	// make sure the earn code's leave plan matches the user's leave plan
115                     	if((StringUtils.isNotBlank(leavePlan) && StringUtils.isNotBlank(ec.getLeavePlan()) && leavePlan.equals(ec.getLeavePlan()))
116     							|| (StringUtils.isBlank(leavePlan) && StringUtils.isBlank(ec.getLeavePlan()))) {
117 	                        //  if the user's fmla flag is Yes, that means we are not restricting codes based on this flag, so any code is shown.
118 	                        //    if the fmla flag on a code is yes they can see it.    (allow)
119 	                        //    if the fmla flag on a code is no they should see it.  (allow)
120 	                        //  if the user's fmla flag is No,
121 	                        //    they can see any codes which are fmla=no.             (allow)
122 	                        //    they can not see codes with fmla=yes.                 (exclude earn code)
123 	                        //  the fmla earn codes=no do not require any exclusion
124 	                        //  the only action required is if the fmla user flag=no: exclude those codes with fmla=yes.
125 	
126 	                        if ( (fmlaEligible || ec.getFmla().equals("N")) ) {
127 	                            if (listAccrualCategories.contains(ec.getAccrualCategory()) || ec.getAccrualCategory() == null) {
128 	                                if (StringUtils.equals(ec.getAccrualBalanceAction(), HrConstants.ACCRUAL_BALANCE_ACTION.USAGE)) {
129 	                                	// go on, we are allowing these three combinations: YY, YN, NN.
130 	                                	
131 	                                	//  Apply the same logic as FMLA to the Worker Compensation flags.
132 	                                	if ( (workersCompEligible || ec.getWorkmansComp().equals("N")) ) {
133 	                                		// go on, we are allowing these three combinations: YY, YN, NN.
134 	
135 		                                    //  now process the scheduled leave flag, but only for the Planning Calendar, not for the Reporting Calendar.
136 		                                    //  determine if the planning calendar is in effect.
137 		                                    if (isLeavePlanningCalendar) {
138 		
139 		                                        //  if the allow_schd_leave flag=yes, add the earn code
140 		                                        if (ec.getAllowScheduledLeave().equals("Y")) {
141 		                                                earnCodes.add(ec);
142 		                                        }
143 		
144 		                                    } else {
145 		                                        //  this is a reporting calendar, so ignore scheduled leave flag, and add this earn code.
146 		                                        earnCodes.add(ec);
147 		                                    }
148 		                                }
149 	                                }
150 	                            }
151 	                        }
152                     	}
153                     }
154                 }
155             }
156         }   //  end of decs loop
157 
158         return earnCodes;
159     }
160 
161     public boolean addEarnCodeBasedOnEmployeeApproverSettings(EarnCodeSecurity security, Assignment a, LocalDate asOfDate) {
162         boolean addEarnCode = false;
163         if (security.isEmployee() &&
164                 (StringUtils.equals(HrContext.getTargetPrincipalId(), GlobalVariables.getUserSession().getPrincipalId())
165                 		|| HrServiceLocator.getKPMEGroupService().isMemberOfSystemAdministratorGroup(GlobalVariables.getUserSession().getPrincipalId(), asOfDate.toDateTimeAtCurrentTime()))) {
166             addEarnCode = true;
167         }
168 
169         RoleService roleService = KimApiServiceLocator.getRoleService();
170         if (!addEarnCode && (security.isEmployee() || security.isApprover() || security.isPayrollProcessor())) {
171             String principalId = GlobalVariables.getUserSession().getPrincipalId();
172             List<String> roleIds = new ArrayList<String>();
173             roleIds.add(roleService.getRoleIdByNamespaceCodeAndName(KPMENamespace.KPME_TK.getNamespaceCode(), KPMERole.TIME_LOCATION_ADMINISTRATOR.getRoleName()));
174             roleIds.add(roleService.getRoleIdByNamespaceCodeAndName(KPMENamespace.KPME_TK.getNamespaceCode(), KPMERole.TIME_SYSTEM_ADMINISTRATOR.getRoleName()));
175             roleIds.add(roleService.getRoleIdByNamespaceCodeAndName(KPMENamespace.KPME_LM.getNamespaceCode(), KPMERole.LEAVE_SYSTEM_ADMINISTRATOR.getRoleName()));
176             roleIds.add(roleService.getRoleIdByNamespaceCodeAndName(KPMENamespace.KPME_LM.getNamespaceCode(), KPMERole.LEAVE_LOCATION_ADMINISTRATOR.getRoleName()));
177             List<Long> workAreas = HrServiceLocator.getKPMERoleService().getWorkAreasForPrincipalInRoles(principalId, roleIds, asOfDate.toDateTimeAtStartOfDay(), true);
178 
179             for (Long wa : workAreas) {
180                 WorkArea workArea = HrServiceLocator.getWorkAreaService().getWorkAreaWithoutRoles(wa, asOfDate);
181                 if (workArea!= null && a.getWorkArea().compareTo(workArea.getWorkArea())==0) {
182                     addEarnCode = true;
183                     break;
184                 }
185             }
186         }
187         // Check approver flag
188         if (!addEarnCode && security.isApprover()) {
189         	String principalId = GlobalVariables.getUserSession().getPrincipalId();
190 
191             List<String> roleIds = new ArrayList<String>();
192             roleIds.add(roleService.getRoleIdByNamespaceCodeAndName(KPMENamespace.KPME_HR.getNamespaceCode(), KPMERole.APPROVER.getRoleName()));
193             roleIds.add(roleService.getRoleIdByNamespaceCodeAndName(KPMENamespace.KPME_HR.getNamespaceCode(), KPMERole.APPROVER_DELEGATE.getRoleName()));
194             roleIds.add(roleService.getRoleIdByNamespaceCodeAndName(KPMENamespace.KPME_HR.getNamespaceCode(), KPMERole.REVIEWER.getRoleName()));
195             List<Long> workAreas = HrServiceLocator.getKPMERoleService().getWorkAreasForPrincipalInRoles(principalId, roleIds, asOfDate.toDateTimeAtStartOfDay(), true);
196 
197             for (Long wa : workAreas) {
198                 WorkArea workArea = HrServiceLocator.getWorkAreaService().getWorkAreaWithoutRoles(wa, asOfDate);
199                 if (workArea!= null && a.getWorkArea().compareTo(workArea.getWorkArea())==0) {
200                     addEarnCode = true;
201                     break;
202                 }
203             }
204         }
205 
206         if (!addEarnCode && security.isPayrollProcessor()) {
207         	String principalId = GlobalVariables.getUserSession().getPrincipalId();
208             List<String> roleIds = new ArrayList<String>();
209             roleIds.add(roleService.getRoleIdByNamespaceCodeAndName(KPMENamespace.KPME_HR.getNamespaceCode(), KPMERole.PAYROLL_PROCESSOR.getRoleName()));
210             roleIds.add(roleService.getRoleIdByNamespaceCodeAndName(KPMENamespace.KPME_HR.getNamespaceCode(), KPMERole.PAYROLL_PROCESSOR_DELEGATE.getRoleName()));
211             List<String> depts = HrServiceLocator.getKPMERoleService().getDepartmentsForPrincipalInRoles(principalId, roleIds, asOfDate.toDateTimeAtStartOfDay(), true);
212 
213             for (String dept : depts) {
214                 Department department = HrServiceLocator.getDepartmentService().getDepartmentWithoutRoles(dept, asOfDate);
215                 if (department!= null && a.getDept().equalsIgnoreCase(department.getDept())) {
216                     addEarnCode = true;
217                     break;
218                 }
219             }
220 
221         }
222         return addEarnCode;
223     }
224 
225     @Override
226     public List<EarnCode> getEarnCodesForPrincipal(String principalId, LocalDate asOfDate, boolean isLeavePlanningCalendar) {
227         List<EarnCode> earnCodes = new LinkedList<EarnCode>();
228         List<Assignment> assignments = HrServiceLocator.getAssignmentService().getAssignments(principalId, asOfDate);
229         for (Assignment assignment : assignments) {
230             List<EarnCode> assignmentEarnCodes = getEarnCodesForLeave(assignment, asOfDate, isLeavePlanningCalendar);
231             //  the following list processing does work as hoped, comparing the objects' data, rather than their references to memory structures.
232             earnCodes.removeAll(assignmentEarnCodes); //ensures no overlap during the addAll
233             earnCodes.addAll(assignmentEarnCodes);
234         }
235 
236         return earnCodes;
237     }
238 
239     public EarnCode getEarnCode(String earnCode, LocalDate asOfDate) {
240 		return earnCodeDao.getEarnCode(earnCode, asOfDate);
241 	}
242 
243     @Override
244     public String getEarnCodeType(String earnCode, LocalDate asOfDate) {
245         EarnCode earnCodeObj = getEarnCode(earnCode, asOfDate);
246         return earnCodeObj != null ? earnCodeObj.getEarnCodeType() : "";       
247     }
248 
249 	@Override
250 	public EarnCode getEarnCodeById(String earnCodeId) {
251 		return earnCodeDao.getEarnCodeById(earnCodeId);
252 	}
253 
254 	public List<EarnCode> getOvertimeEarnCodes(LocalDate asOfDate){
255 		return earnCodeDao.getOvertimeEarnCodes(asOfDate);
256 	}
257 
258 	public List<String> getOvertimeEarnCodesStrs(LocalDate asOfDate){
259 		List<String> ovtEarnCodeStrs = new ArrayList<String>();
260 		List<EarnCode> ovtEarnCodes = getOvertimeEarnCodes(asOfDate);
261 		if(ovtEarnCodes != null){
262 			for(EarnCode ovtEc : ovtEarnCodes){
263 				ovtEarnCodeStrs.add(ovtEc.getEarnCode());
264 			}
265 		}
266 		return ovtEarnCodeStrs;
267 	}
268 
269 	@Override
270 	public int getEarnCodeCount(String earnCode) {
271 		return earnCodeDao.getEarnCodeCount(earnCode);
272 	}
273 
274 	@Override
275 	public int getNewerEarnCodeCount(String earnCode, LocalDate effdt) {
276 		return earnCodeDao.getNewerEarnCodeCount(earnCode, effdt);
277 	}
278 
279 	@Override
280 	public BigDecimal roundHrsWithEarnCode(BigDecimal hours, EarnCode earnCode) {
281 		String roundOption = HrConstants.ROUND_OPTION_MAP.get(earnCode.getRoundingOption());
282 		BigDecimal fractScale = new BigDecimal(earnCode.getFractionalTimeAllowed());
283 		if(roundOption == null) {
284 			LOG.error("Rounding option of Earn Code " + earnCode.getEarnCode() + " is not recognized.");
285         	return null;
286 //			throw new RuntimeException("Rounding option of Earn Code " + earnCode.getEarnCode() + " is not recognized.");
287 		}
288 		BigDecimal roundedHours = hours;
289 		if(roundOption.equals("Traditional")) {
290 			roundedHours = hours.setScale(fractScale.scale(), BigDecimal.ROUND_HALF_EVEN);
291 		} else if(roundOption.equals("Truncate")) {
292 			roundedHours = hours.setScale(fractScale.scale(), BigDecimal.ROUND_DOWN);
293 		}
294 		return roundedHours;
295 	}
296 
297 	@Override
298 	public Map<String, String> getEarnCodesForDisplay(String principalId, boolean isLeavePlanningCalendar) {
299 		return getEarnCodesForDisplayWithEffectiveDate(principalId, LocalDate.now(), isLeavePlanningCalendar);
300 	}
301 
302     public List<EarnCode> getEarnCodes(String earnCode, String ovtEarnCode, String descr, String leavePlan, String accrualCategory, LocalDate fromEffdt, LocalDate toEffdt, String active, String showHist) {
303         return earnCodeDao.getEarnCodes(earnCode, ovtEarnCode, descr, leavePlan, accrualCategory, fromEffdt, toEffdt, active, showHist);
304     }
305 
306     @Override
307     public Map<String, String> getEarnCodesForDisplayWithEffectiveDate(String principalId, LocalDate asOfDate, boolean isLeavePlanningCalendar) {
308         List<EarnCode> earnCodes = this.getEarnCodesForPrincipal(principalId, asOfDate, isLeavePlanningCalendar);
309 
310         boolean futureDate = asOfDate.isAfter(LocalDate.now());
311         List<EarnCode> copyList = new ArrayList<EarnCode>();
312         copyList.addAll(earnCodes);
313         for (EarnCode earnCode : copyList) {
314             if ( futureDate
315                     && !earnCode.getAllowScheduledLeave().equalsIgnoreCase("Y")) {
316                 earnCodes.remove(earnCode);
317             }
318         }
319         Comparator<EarnCode> earnCodeComparator = new Comparator<EarnCode>() {
320             @Override
321             public int compare(EarnCode ec1, EarnCode ec2) {
322                 return ec1.getEarnCode().compareToIgnoreCase(ec2.getEarnCode());
323             }
324         };
325         // Order by leaveCode ascending
326         Ordering<EarnCode> ordering = Ordering.from(earnCodeComparator);
327 
328         Map<String, String> earnCodesForDisplay = new LinkedHashMap<String, String>();
329         for (EarnCode earnCode : ordering.sortedCopy(earnCodes)) {
330             earnCodesForDisplay.put(earnCode.getEarnCodeKeyForDisplay(), earnCode.getEarnCodeValueForDisplay());
331         }
332         return earnCodesForDisplay;
333     }
334 
335 }