001 /** 002 * Copyright 2004-2012 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.time.accrual.service; 017 018 import org.apache.commons.lang.StringUtils; 019 import org.apache.log4j.Logger; 020 import org.kuali.hr.time.accrual.AccrualCategory; 021 import org.kuali.hr.time.accrual.TimeOffAccrual; 022 import org.kuali.hr.time.accrual.dao.TimeOffAccrualDao; 023 import org.kuali.hr.time.earncode.EarnCode; 024 import org.kuali.hr.time.service.base.TkServiceLocator; 025 import org.kuali.hr.time.timeblock.TimeBlock; 026 import org.kuali.hr.time.timesheet.TimesheetDocument; 027 import org.kuali.hr.time.util.TKUtils; 028 import org.kuali.hr.time.util.TkConstants; 029 030 import java.math.BigDecimal; 031 import java.sql.Date; 032 import java.util.*; 033 034 public class TimeOffAccrualServiceImpl implements TimeOffAccrualService { 035 036 private static final Logger LOG = Logger.getLogger(TimeOffAccrualServiceImpl.class); 037 public static final String ACCRUAL_CATEGORY_KEY = "accrualCategory"; 038 public static final String ACCRUAL_NAME_KEY = "accrualName"; 039 public static final String YEARLY_CARRYOVER_KEY = "yearlyCarryover"; 040 public static final String HOURS_ACCRUED_KEY = "hoursAccrued"; 041 public static final String HOURS_TAKEN_KEY = "hoursTaken"; 042 public static final String HOURS_ADJUST_KEY = "hoursAdjust"; 043 public static final String TOTAL_HOURS_KEY = "totalHours"; 044 public static final String EFF_DATE_KEY = "effdt"; 045 046 private TimeOffAccrualDao timeOffAccrualDao; 047 048 public void setTimeOffAccrualDao(TimeOffAccrualDao timeOffAccrualDao) { 049 this.timeOffAccrualDao = timeOffAccrualDao; 050 } 051 052 @Override 053 public List<TimeOffAccrual> getTimeOffAccruals(String principalId, Date asOfDate) { 054 java.sql.Date currentDate = TKUtils.getTimelessDate(null); 055 return timeOffAccrualDao.getActiveTimeOffAccruals(principalId, asOfDate); 056 } 057 058 @Override 059 public List<Map<String, Object>> getTimeOffAccrualsCalc(String principalId, Date asOfDate) { 060 061 List<Map<String, Object>> timeOffAccrualsCalc = new ArrayList<Map<String, Object>>(); 062 Map<String,String> accrualCatToDescr = new HashMap<String, String>(); 063 064 for(TimeOffAccrual timeOffAccrual : getTimeOffAccruals(principalId, asOfDate)) { 065 String accrualCatDescr = accrualCatToDescr.get(timeOffAccrual.getAccrualCategory()); 066 //if no accrual cat description found look up accrual category and find one 067 if(StringUtils.isBlank(accrualCatDescr)){ 068 AccrualCategory accrualCat = TkServiceLocator.getAccrualCategoryService().getAccrualCategory(timeOffAccrual.getAccrualCategory(), asOfDate); 069 if(accrualCat != null){ 070 accrualCatDescr = accrualCat.getDescr(); 071 accrualCatToDescr.put(accrualCat.getAccrualCategory(), accrualCatDescr); 072 } 073 } 074 Map<String, Object> output = new LinkedHashMap<String, Object>(); 075 output.put(ACCRUAL_CATEGORY_KEY, accrualCatDescr + "("+timeOffAccrual.getAccrualCategory()+")"); 076 output.put(ACCRUAL_NAME_KEY, timeOffAccrual.getAccrualCategory()); 077 output.put(YEARLY_CARRYOVER_KEY, timeOffAccrual.getYearlyCarryover()); 078 output.put(HOURS_ACCRUED_KEY, timeOffAccrual.getHoursAccrued()); 079 output.put(HOURS_TAKEN_KEY, timeOffAccrual.getHoursTaken()); 080 output.put(HOURS_ADJUST_KEY, timeOffAccrual.getHoursAdjust()); 081 BigDecimal totalHours = timeOffAccrual.getYearlyCarryover().add(timeOffAccrual.getHoursAccrued().subtract(timeOffAccrual.getHoursTaken()).add(timeOffAccrual.getHoursAdjust())); 082 output.put(TOTAL_HOURS_KEY, totalHours); 083 output.put(EFF_DATE_KEY, timeOffAccrual.getEffectiveDate()); 084 085 timeOffAccrualsCalc.add(output); 086 } 087 088 return timeOffAccrualsCalc; 089 } 090 091 @SuppressWarnings("unchecked") 092 public List<String> validateAccrualHoursLimit(TimesheetDocument timesheetDocument) { 093 String pId = ""; 094 if (timesheetDocument != null) { 095 pId = timesheetDocument.getPrincipalId(); 096 } 097 098 return validateAccrualHoursLimit(pId, timesheetDocument.getTimeBlocks(), timesheetDocument.getAsOfDate()); 099 100 } 101 102 @SuppressWarnings("unchecked") 103 public List<String> validateAccrualHoursLimit(String pId, List<TimeBlock> tbList, Date asOfDate) { 104 List<String> warningMessages = new ArrayList<String>(); 105 106 List<Map<String, Object>> calcList = this.getTimeOffAccrualsCalc(pId, asOfDate); 107 108 if (tbList.isEmpty()) { 109 return warningMessages; 110 } 111 List<String> accruals = new ArrayList<String>(); 112 for (Map<String, Object> aMap : calcList) { 113 accruals.add((String) aMap.get(ACCRUAL_CATEGORY_KEY)); 114 } 115 for (Map<String, Object> aMap : calcList) { 116 String accrualCategory = (String) aMap.get(ACCRUAL_NAME_KEY); 117 List<TimeBlock> warningTbs = new ArrayList<TimeBlock>(); 118 BigDecimal totalForAccrCate = this.totalForAccrCate(accrualCategory, tbList, warningTbs); 119 //if there is no timeblocks for this category no warning is necessary 120 if(totalForAccrCate.compareTo(BigDecimal.ZERO)==0){ 121 continue; 122 } 123 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))); 124 125 if (totalForAccrCate.compareTo(balanceHrs) == 1) { 126 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/>"; 127 for(TimeBlock tb : warningTbs) { 128 msg += "Earn code: " + tb.getEarnCode()+ " Hours: " + tb.getHours().toString() + " on Date " + (tb.getBeginTimeDisplay() != null ? tb.getBeginTimeDisplay().toString(TkConstants.DT_BASIC_DATE_FORMAT) : "") + "<br/>"; 129 } 130 warningMessages.add(msg); 131 132 } 133 } 134 return warningMessages; 135 } 136 public List<String> validateAccrualHoursLimitByEarnCode(TimesheetDocument timesheetDocument, String earnCode) { 137 List<String> warningMessages = new ArrayList<String>(); 138 String pId = ""; 139 if (timesheetDocument != null) { 140 pId = timesheetDocument.getPrincipalId(); 141 } 142 List<Map<String, Object>> calcList = this.getTimeOffAccrualsCalc(pId, timesheetDocument.getAsOfDate()); 143 144 List<TimeBlock> tbList = timesheetDocument.getTimeBlocks(); 145 if (tbList.isEmpty()) { 146 return warningMessages; 147 } 148 List<String> accruals = new ArrayList<String>(); 149 for (Map<String, Object> aMap : calcList) { 150 accruals.add((String) aMap.get(ACCRUAL_CATEGORY_KEY)); 151 } 152 153 List<AccrualCategory> accrualCategories = TkServiceLocator.getAccrualCategoryService().getActiveAccrualCategories(timesheetDocument.getAsOfDate()); 154 155 for(AccrualCategory accrualCategory : accrualCategories){ 156 if(!accruals.contains(accrualCategory.getAccrualCategory()) && !StringUtils.equals(TkConstants.HOLIDAY_EARN_CODE, accrualCategory.getAccrualCategory())){ 157 Map<String, Object> accrualData = new LinkedHashMap<String, Object>(); 158 accrualData.put(ACCRUAL_CATEGORY_KEY, accrualCategory.getAccrualCategory()); 159 accrualData.put(YEARLY_CARRYOVER_KEY, new BigDecimal(0.00)); 160 accrualData.put(HOURS_ACCRUED_KEY, new BigDecimal(0.00)); 161 accrualData.put(HOURS_TAKEN_KEY, new BigDecimal(0.00)); 162 accrualData.put(HOURS_ADJUST_KEY, new BigDecimal(0.00)); 163 calcList.add(accrualData); 164 } 165 } 166 for (Map<String, Object> aMap : calcList) { 167 String accrualCategory = (String) aMap.get(ACCRUAL_CATEGORY_KEY); 168 List<TimeBlock> warningTbs = new ArrayList<TimeBlock>(); 169 BigDecimal totalForAccrCate = this.totalForAccrCate(accrualCategory, tbList, warningTbs); 170 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))); 171 172 if (totalForAccrCate.compareTo(balanceHrs) == 1) { 173 if (accrualCategory.equals(earnCode)) { 174 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: "; 175 for(TimeBlock tb : warningTbs) { 176 msg += "Earn code: " + tb.getEarnCode()+ " Hours: " + tb.getHours().toString() + " on Date " + (tb.getBeginTimeDisplay() != null ? tb.getBeginTimeDisplay().toString(TkConstants.DT_BASIC_DATE_FORMAT) : "") + " "; 177 } 178 179 warningMessages.add(msg); 180 } 181 182 } 183 } 184 return warningMessages; 185 } 186 187 public BigDecimal totalForAccrCate(String accrualCategory, List<TimeBlock> tbList, List<TimeBlock> warningTbs) { 188 BigDecimal total = BigDecimal.ZERO; 189 for (TimeBlock tb : tbList) { 190 String earnCode = tb.getEarnCode(); 191 Date asOfDate = new java.sql.Date(tb.getBeginTimestamp().getTime()); 192 EarnCode ec = TkServiceLocator.getEarnCodeService().getEarnCode(earnCode, asOfDate); 193 String accrCate = ""; 194 if (ec != null) { 195 accrCate = ec.getAccrualCategory(); 196 if (accrCate != null) { 197 if (accrCate.equals(accrualCategory)) { 198 total = total.add(tb.getHours()); 199 warningTbs.add(tb); 200 } 201 } 202 } 203 } 204 return total; 205 } 206 207 @Override 208 public TimeOffAccrual getTimeOffAccrual(Long laTimeOffAccrualId) { 209 return timeOffAccrualDao.getTimeOffAccrual(laTimeOffAccrualId); 210 } 211 212 @Override 213 public int getTimeOffAccrualCount(String accrualCategory, Date effectiveDate, String principalId, String lmAccrualId) { 214 return timeOffAccrualDao.getTimeOffAccrualCount(accrualCategory, effectiveDate, principalId, lmAccrualId); 215 } 216 }