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.time.accrual.service;
17  
18  import java.math.BigDecimal;
19  import java.sql.Date;
20  import java.util.ArrayList;
21  import java.util.HashMap;
22  import java.util.LinkedHashMap;
23  import java.util.List;
24  import java.util.Map;
25  
26  import org.apache.commons.lang.StringUtils;
27  import org.joda.time.DateTime;
28  import org.kuali.hr.lm.LMConstants;
29  import org.kuali.hr.lm.accrual.AccrualCategory;
30  import org.kuali.hr.lm.accrual.AccrualCategoryRule;
31  import org.kuali.hr.time.accrual.TimeOffAccrual;
32  import org.kuali.hr.time.accrual.dao.TimeOffAccrualDao;
33  import org.kuali.hr.time.earncode.EarnCode;
34  import org.kuali.hr.time.principal.PrincipalHRAttributes;
35  import org.kuali.hr.time.service.base.TkServiceLocator;
36  import org.kuali.hr.time.timeblock.TimeBlock;
37  import org.kuali.hr.time.timesheet.TimesheetDocument;
38  import org.kuali.hr.time.util.TkConstants;
39  
40  public class TimeOffAccrualServiceImpl implements TimeOffAccrualService {
41  
42  	public static final String ACCRUAL_CATEGORY_KEY = "accrualCategory";
43  	public static final String ACCRUAL_RATE_KEY = "accrualRate";
44  	public static final String ACCRUAL_NAME_KEY = "accrualName";
45  	public static final String YEARLY_CARRYOVER_KEY = "yearlyCarryover";
46  	public static final String HOURS_ACCRUED_KEY = "hoursAccrued";
47  	public static final String HOURS_TAKEN_KEY = "hoursTaken";
48  	public static final String HOURS_ADJUST_KEY = "hoursAdjust";
49  	public static final String TOTAL_HOURS_KEY = "totalHours";
50  	public static final String EFF_DATE_KEY = "effdt";
51  
52  	private TimeOffAccrualDao timeOffAccrualDao;
53  
54  	public void setTimeOffAccrualDao(TimeOffAccrualDao timeOffAccrualDao) {
55  		this.timeOffAccrualDao = timeOffAccrualDao;
56  	}
57  
58  	@Override
59  	public List<TimeOffAccrual> getTimeOffAccruals(String principalId, Date asOfDate) {
60  		return timeOffAccrualDao.getActiveTimeOffAccruals(principalId, asOfDate);
61  	}
62  
63  	@Override
64  	public List<Map<String, Object>> getTimeOffAccrualsCalc(String principalId, Date asOfDate) {
65  		List<Map<String, Object>> timeOffAccrualsCalc = new ArrayList<Map<String, Object>>();
66  		Map<String,String> accrualCatToDescr = new HashMap<String, String>();
67  		String accrualRate = "";
68  		
69  		for (TimeOffAccrual timeOffAccrual : getTimeOffAccruals(principalId, asOfDate)) {
70  			String accrualCatDescr = accrualCatToDescr.get(timeOffAccrual.getAccrualCategory());
71  			//if no accrual cat description found look up accrual category and find one
72  			if (StringUtils.isBlank(accrualCatDescr)){
73  				AccrualCategory accrualCat = TkServiceLocator.getAccrualCategoryService().getAccrualCategory(timeOffAccrual.getAccrualCategory(), asOfDate);
74  				if (accrualCat != null) {
75  					accrualCatDescr = accrualCat.getDescr();
76  					accrualCatToDescr.put(accrualCat.getAccrualCategory(), accrualCatDescr);
77  				
78  					DateTime currentDate = new DateTime();
79  					List<AccrualCategoryRule> accrualCategoryRules = accrualCat.getAccrualCategoryRules();
80  					
81  					PrincipalHRAttributes principalHRAttributes = TkServiceLocator.getPrincipalHRAttributeService().getPrincipalCalendar(principalId, asOfDate);
82  					if (principalHRAttributes == null) {
83  						throw new RuntimeException("Cannot find principal hr attributes for " + principalId);
84  					}
85  					Date serviceDate = principalHRAttributes.getServiceDate();
86  					if (serviceDate == null) {
87  						throw new RuntimeException("Cannot find service date on principal hr attribute for " + principalId);
88  					}
89  					DateTime jodaServiceDate = new DateTime(serviceDate);
90  					
91  					Long serviceUnits= new Long(0);
92  					// for each accrualCategoryRule, compare start/end with the length of service, get corresponding rate and unit
93  					for(AccrualCategoryRule accrualCategoryRule : accrualCategoryRules){
94  						String serviceUnitOfTime = accrualCategoryRule.getServiceUnitOfTime();
95  						
96  						if ( serviceUnitOfTime.equals(LMConstants.SERVICE_TIME_YEAR)){
97  							serviceUnits = new Long(currentDate.getYear() - jodaServiceDate.getYear()) + 1;
98  						}
99  						if ( serviceUnitOfTime.equals(LMConstants.SERVICE_TIME_MONTHS)){
100 							serviceUnits = new Long(currentDate.getYear() - jodaServiceDate.getYear()) * 12 + 
101 							new Long(currentDate.getMonthOfYear() - jodaServiceDate.getMonthOfYear()) ;
102 						}
103 						
104 						if ( serviceUnits >= accrualCategoryRule.getStart() && serviceUnits < accrualCategoryRule.getEnd()){
105 							accrualRate = accrualCategoryRule.getAccrualRate().toString() + " " + accrualCat.getUnitOfTime();
106 							break;
107 						}
108 					}
109 				}
110 			}
111 			Map<String, Object> output = new LinkedHashMap<String, Object>();
112 			output.put(ACCRUAL_CATEGORY_KEY, accrualCatDescr + "("+timeOffAccrual.getAccrualCategory()+")");
113 			output.put(ACCRUAL_RATE_KEY, accrualRate);
114 			output.put(ACCRUAL_NAME_KEY, timeOffAccrual.getAccrualCategory());
115 			output.put(YEARLY_CARRYOVER_KEY, timeOffAccrual.getYearlyCarryover());
116 			output.put(HOURS_ACCRUED_KEY, timeOffAccrual.getHoursAccrued());
117 			output.put(HOURS_TAKEN_KEY, timeOffAccrual.getHoursTaken());
118 			output.put(HOURS_ADJUST_KEY, timeOffAccrual.getHoursAdjust());
119 			BigDecimal totalHours = timeOffAccrual.getYearlyCarryover().add(timeOffAccrual.getHoursAccrued().subtract(timeOffAccrual.getHoursTaken()).add(timeOffAccrual.getHoursAdjust()));
120 			output.put(TOTAL_HOURS_KEY, totalHours);
121 			output.put(EFF_DATE_KEY, timeOffAccrual.getEffectiveDate());
122 			
123 			
124 
125 			timeOffAccrualsCalc.add(output);
126 		}
127 
128 		return timeOffAccrualsCalc;
129 	}
130 
131 	public List<String> validateAccrualHoursLimit(TimesheetDocument timesheetDocument) {
132     	 String pId = "";
133          if (timesheetDocument != null) {
134              pId = timesheetDocument.getPrincipalId();
135          }
136          
137      	return validateAccrualHoursLimit(pId, timesheetDocument.getTimeBlocks(),  timesheetDocument.getAsOfDate());
138         
139     }
140 	
141 	public List<String> validateAccrualHoursLimit(String pId, List<TimeBlock> tbList, Date asOfDate) {
142 		 List<String> warningMessages = new ArrayList<String>();
143 
144          List<Map<String, Object>> calcList = this.getTimeOffAccrualsCalc(pId, asOfDate);
145          
146            if (tbList.isEmpty()) {
147              return warningMessages;
148          }
149          List<String> accruals = new ArrayList<String>();
150          for (Map<String, Object> aMap : calcList) {
151     		accruals.add((String) aMap.get(ACCRUAL_CATEGORY_KEY));
152     	 }
153          for (Map<String, Object> aMap : calcList) {
154              String accrualCategory = (String) aMap.get(ACCRUAL_NAME_KEY);
155              List<TimeBlock> warningTbs = new ArrayList<TimeBlock>();
156              BigDecimal totalForAccrCate = this.totalForAccrCate(accrualCategory, tbList, warningTbs);
157              //if there is no timeblocks for this category no warning is necessary 
158              if(totalForAccrCate.compareTo(BigDecimal.ZERO)==0){
159             	 continue;
160              }
161              BigDecimal balanceHrs = (((BigDecimal)aMap.get(YEARLY_CARRYOVER_KEY)).add((BigDecimal)aMap.get(HOURS_ACCRUED_KEY)).subtract((BigDecimal)aMap.get(HOURS_TAKEN_KEY)).add((BigDecimal)aMap.get(HOURS_ADJUST_KEY)));
162              
163              if (totalForAccrCate.compareTo(balanceHrs) == 1) {
164              	String msg = "Warning: Total hours entered (" + totalForAccrCate.toString() + ") for Accrual Category \"" + (String) aMap.get(ACCRUAL_CATEGORY_KEY) + "\" has exceeded balance (" + balanceHrs.toString() + "). Problem Time Blocks are:<br/>";
165              	for(TimeBlock tb : warningTbs) {
166              		msg += "Earn code: " + tb.getEarnCode()+ " Hours: " + tb.getHours().toString() + " on Date " + (tb.getBeginTimeDisplay() != null ? tb.getBeginTimeDisplay().toString(TkConstants.DT_BASIC_DATE_FORMAT) : "") + "<br/>";
167              	}
168                 warningMessages.add(msg);
169 
170              }
171          }
172          return warningMessages;
173     }
174 	public List<String> validateAccrualHoursLimitByEarnCode(TimesheetDocument timesheetDocument, String earnCode) {
175 		 List<String> warningMessages = new ArrayList<String>();
176    	 String pId = "";
177         if (timesheetDocument != null) {
178             pId = timesheetDocument.getPrincipalId();
179         }
180         List<Map<String, Object>> calcList = this.getTimeOffAccrualsCalc(pId, timesheetDocument.getAsOfDate());
181 
182         List<TimeBlock> tbList = timesheetDocument.getTimeBlocks();
183         if (tbList.isEmpty()) {
184             return warningMessages;
185         }
186         List<String> accruals = new ArrayList<String>();
187         for (Map<String, Object> aMap : calcList) {
188    		accruals.add((String) aMap.get(ACCRUAL_CATEGORY_KEY));
189    	 }
190         
191         List<AccrualCategory> accrualCategories = TkServiceLocator.getAccrualCategoryService().getActiveAccrualCategories(timesheetDocument.getAsOfDate());
192     
193         for(AccrualCategory accrualCategory : accrualCategories){
194        	 if(!accruals.contains(accrualCategory.getAccrualCategory()) && !StringUtils.equals(TkConstants.HOLIDAY_EARN_CODE, accrualCategory.getAccrualCategory())){
195        		Map<String, Object> accrualData = new LinkedHashMap<String, Object>();
196     			accrualData.put(ACCRUAL_CATEGORY_KEY, accrualCategory.getAccrualCategory());
197     			accrualData.put(YEARLY_CARRYOVER_KEY, new BigDecimal(0.00));
198     			accrualData.put(HOURS_ACCRUED_KEY, new BigDecimal(0.00));
199     			accrualData.put(HOURS_TAKEN_KEY, new BigDecimal(0.00));
200     			accrualData.put(HOURS_ADJUST_KEY, new BigDecimal(0.00));
201     			calcList.add(accrualData);
202        	 }
203         }
204         for (Map<String, Object> aMap : calcList) {
205             String accrualCategory = (String) aMap.get(ACCRUAL_CATEGORY_KEY);
206             List<TimeBlock> warningTbs = new ArrayList<TimeBlock>();
207             BigDecimal totalForAccrCate = this.totalForAccrCate(accrualCategory, tbList, warningTbs);
208             BigDecimal balanceHrs = (((BigDecimal)aMap.get(YEARLY_CARRYOVER_KEY)).add((BigDecimal)aMap.get(HOURS_ACCRUED_KEY)).subtract((BigDecimal)aMap.get(HOURS_TAKEN_KEY)).add((BigDecimal)aMap.get(HOURS_ADJUST_KEY)));
209             
210             if (totalForAccrCate.compareTo(balanceHrs) == 1) {
211             	if (accrualCategory.equals(earnCode)) {
212 	            	String msg = "Warning: Total hours entered (" + totalForAccrCate.toString() + ") for Accrual Category \"" + (String) aMap.get(ACCRUAL_CATEGORY_KEY) + "\" has exceeded balance (" + balanceHrs.toString() + "). Problem Time Blocks are: ";
213 	            	for(TimeBlock tb : warningTbs) {
214 	            		msg += "Earn code: " + tb.getEarnCode()+ " Hours: " + tb.getHours().toString() + " on Date " + (tb.getBeginTimeDisplay() != null ? tb.getBeginTimeDisplay().toString(TkConstants.DT_BASIC_DATE_FORMAT) : "") + " ";
215 	            	}
216 	            	
217 	               warningMessages.add(msg);
218             	}
219 
220             }
221         }
222         return warningMessages;
223    }
224 
225     public BigDecimal totalForAccrCate(String accrualCategory, List<TimeBlock> tbList, List<TimeBlock> warningTbs) {
226         BigDecimal total = BigDecimal.ZERO;
227         for (TimeBlock tb : tbList) {
228             String earnCode = tb.getEarnCode();
229             Date asOfDate = new java.sql.Date(tb.getBeginTimestamp().getTime());
230             EarnCode ec = TkServiceLocator.getEarnCodeService().getEarnCode(earnCode, asOfDate);
231             String accrCate = "";
232             if (ec != null) {
233                 accrCate = ec.getAccrualCategory();
234                 if (accrCate != null) {
235                     if (accrCate.equals(accrualCategory)) {
236                         total = total.add(tb.getHours());
237                         warningTbs.add(tb);
238                     }
239                 }
240             }
241         }
242         return total;
243     }
244 
245 	@Override
246 	public TimeOffAccrual getTimeOffAccrual(Long laTimeOffAccrualId) {
247 		return timeOffAccrualDao.getTimeOffAccrual(laTimeOffAccrualId);
248 	}
249 	
250 	@Override
251 	public int getTimeOffAccrualCount(String accrualCategory, Date effectiveDate, String principalId, String lmAccrualId) {
252 		return timeOffAccrualDao.getTimeOffAccrualCount(accrualCategory, effectiveDate, principalId, lmAccrualId);
253 	}
254 
255     @Override
256     public  List<TimeOffAccrual> getTimeOffAccruals(String principalId, String accrualCategory){
257         return timeOffAccrualDao.getTimeOffAccruals(principalId, accrualCategory);
258     }
259 }