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    }