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