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 }