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    }