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