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 }