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.lm.leaveSummary.service;
017    
018    import org.apache.commons.collections.CollectionUtils;
019    import org.apache.commons.lang.StringUtils;
020    import org.apache.commons.lang.time.DateUtils;
021    import org.joda.time.DateMidnight;
022    import org.joda.time.DateTime;
023    import org.joda.time.Interval;
024    import org.joda.time.LocalDateTime;
025    import org.kuali.hr.lm.LMConstants;
026    import org.kuali.hr.lm.accrual.AccrualCategory;
027    import org.kuali.hr.lm.accrual.AccrualCategoryRule;
028    import org.kuali.hr.lm.employeeoverride.EmployeeOverride;
029    import org.kuali.hr.lm.leaveSummary.LeaveSummary;
030    import org.kuali.hr.lm.leaveSummary.LeaveSummaryRow;
031    import org.kuali.hr.lm.leaveblock.LeaveBlock;
032    import org.kuali.hr.lm.leaveblock.service.LeaveBlockService;
033    import org.kuali.hr.lm.leaveplan.LeavePlan;
034    import org.kuali.hr.lm.workflow.LeaveCalendarDocumentHeader;
035    import org.kuali.hr.time.calendar.CalendarEntries;
036    import org.kuali.hr.time.earncode.EarnCode;
037    import org.kuali.hr.time.principal.PrincipalHRAttributes;
038    import org.kuali.hr.time.service.base.TkServiceLocator;
039    import org.kuali.hr.time.util.TKUtils;
040    import org.kuali.hr.time.util.TkConstants;
041    import org.kuali.rice.krad.util.ObjectUtils;
042    
043    import java.math.BigDecimal;
044    import java.sql.Timestamp;
045    import java.text.DateFormat;
046    import java.text.ParseException;
047    import java.text.SimpleDateFormat;
048    import java.util.*;
049    
050    public class LeaveSummaryServiceImpl implements LeaveSummaryService {
051            private LeaveBlockService leaveBlockService;
052    
053        @Override
054        public LeaveSummary getLeaveSummaryAsOfDate(String principalId, java.sql.Date asOfDate) {
055            return getLeaveSummary(principalId, asOfDate, asOfDate, null, true);
056        }
057    
058        public LeaveSummary getLeaveSummaryAsOfDateWithoutFuture(String principalId, java.sql.Date asOfDate) {
059            return getLeaveSummary(principalId, asOfDate, asOfDate, null, false);
060        }
061    
062        @Override
063        public LeaveSummary getLeaveSummary(String principalId, CalendarEntries calendarEntry) {
064            return getLeaveSummary(principalId, calendarEntry.getBeginPeriodDate(), calendarEntry.getEndPeriodDate(), null, true);
065        }
066    
067        @Override
068        public LeaveSummary getLeaveSummaryAsOfDateForAccrualCategory(String principalId, java.sql.Date asOfDate, String accrualCategory) {
069            return getLeaveSummary(principalId, asOfDate, asOfDate, accrualCategory, true);
070        }
071    
072        @Override
073        // startDate is the leave request start date, endDat is leave request end date, usageEndDate is the date before next accrual interval date for leave requst end date
074        // will get leave balance up to the next earn interval for a certain date, including usage up to that next earn interval
075        public BigDecimal getLeaveBalanceForAccrCatUpToDate(String principalId,
076                java.sql.Date startDate,    
077                java.sql.Date endDate,
078                String accrualCategory,
079                Date usageEndDate) {
080            BigDecimal leaveBalance = BigDecimal.ZERO;
081            if(StringUtils.isEmpty(principalId) || startDate == null || endDate == null || StringUtils.isEmpty(accrualCategory) || usageEndDate == null) {
082                return leaveBalance;
083            }
084    
085            LeaveSummaryRow lsr = new LeaveSummaryRow();
086            AccrualCategory ac = TkServiceLocator.getAccrualCategoryService().getAccrualCategory(accrualCategory, endDate);
087            
088            if(ac != null) {
089                LeavePlan lp = TkServiceLocator.getLeavePlanService().getLeavePlan(ac.getLeavePlan(), ac.getEffectiveDate());
090                if(lp == null) {
091                   return leaveBalance;
092                }
093                PrincipalHRAttributes pha = getPrincipalHrAttributes(principalId, startDate, endDate);
094                //until we have something that creates carry over, we need to grab everything.
095                // Calculating leave bLocks from Calendar Year start instead of Service Date
096                Map<String, LeaveBlock> carryOverBlocks = getLeaveBlockService().getLastCarryOverBlocks(principalId, startDate);
097                //remove unwanted carry over blocks from map
098                LeaveBlock carryOverBlock = carryOverBlocks.get(accrualCategory);
099                carryOverBlocks = new HashMap<String, LeaveBlock>(1);
100                if(ObjectUtils.isNotNull(carryOverBlock))
101                    carryOverBlocks.put(carryOverBlock.getAccrualCategory(), carryOverBlock);
102               
103                List<LeaveBlock> leaveBlocks = getLeaveBlockService().getLeaveBlocksSinceCarryOver(principalId, carryOverBlocks, (new LocalDateTime(endDate)).toDateTime(), true);
104                List<LeaveBlock> acLeaveBlocks = new ArrayList<LeaveBlock>();
105                for(LeaveBlock lb : leaveBlocks) {
106                    if(StringUtils.equals(lb.getAccrualCategory(), accrualCategory)) {
107                            acLeaveBlocks.add(lb);
108                    }
109                }
110                // get all leave blocks from the requested date to the usageEndDate
111                List<LeaveBlock> futureLeaveBlocks = getLeaveBlockService().getLeaveBlocksWithAccrualCategory(principalId, endDate, usageEndDate, accrualCategory);
112                EmployeeOverride maxUsageOverride = TkServiceLocator.getEmployeeOverrideService().getEmployeeOverride(principalId, lp.getLeavePlan(), accrualCategory, "MU", new java.sql.Date(usageEndDate.getTime()));
113    
114                //get max balances
115                AccrualCategoryRule acRule = TkServiceLocator.getAccrualCategoryRuleService().getAccrualCategoryRuleForDate(ac, TKUtils.getCurrentDate(), pha.getServiceDate());
116                //accrual category rule id set on a leave summary row will be useful in generating a relevant balance transfer
117                //document from the leave calendar display. Could put this id in the request for balance transfer document.
118                lsr.setAccrualCategoryRuleId(acRule == null ? null : acRule.getLmAccrualCategoryRuleId());
119                if(acRule != null &&
120                        (acRule.getMaxBalance()!= null
121                                || acRule.getMaxUsage() != null)) {
122                    if (acRule.getMaxUsage() != null) {
123                        lsr.setUsageLimit(new BigDecimal(acRule.getMaxUsage()).setScale(2));
124                    } else {
125                        lsr.setUsageLimit(null);
126                    }
127                } else {
128                    lsr.setUsageLimit(null);
129                }
130    
131                if(maxUsageOverride !=null)
132                    lsr.setUsageLimit(new BigDecimal(maxUsageOverride.getOverrideValue()));
133    
134                //Fetching leaveblocks for accCat with type CarryOver -- This is logic according to the CO blocks creatLed from scheduler job.
135                BigDecimal carryOver = BigDecimal.ZERO.setScale(2);
136                lsr.setCarryOver(carryOver);
137    
138                assignApprovedValuesToRow(lsr, ac.getAccrualCategory(), acLeaveBlocks, lp, startDate, endDate);
139    
140                //merge key sets
141                if (carryOverBlocks.containsKey(lsr.getAccrualCategory())) {
142                    carryOver = carryOverBlocks.get(lsr.getAccrualCategory()).getLeaveAmount();
143                }
144                Set<String> keyset = new HashSet<String>();
145                keyset.addAll(lsr.getPriorYearsUsage().keySet());
146                keyset.addAll(lsr.getPriorYearsTotalAccrued().keySet());
147                for (String key : keyset) {
148                    BigDecimal value = lsr.getPriorYearsTotalAccrued().get(key);
149                    if (value == null) {
150                        value = BigDecimal.ZERO;
151                    }
152                    carryOver = carryOver.add(value);
153                    BigDecimal use = lsr.getPriorYearsUsage().containsKey(key) ? lsr.getPriorYearsUsage().get(key) : BigDecimal.ZERO;
154                    carryOver = carryOver.add(use);
155                    if (acRule != null  && acRule.getMaxCarryOver() != null && acRule.getMaxCarryOver() < carryOver.longValue()) {
156                        carryOver = new BigDecimal(acRule.getMaxCarryOver());
157                    }
158                }
159    
160                lsr.setCarryOver(carryOver);
161                //handle future leave blocks
162                assignPendingValuesToRow(lsr, ac.getAccrualCategory(), futureLeaveBlocks);
163                //compute Leave Balance
164                leaveBalance = lsr.getAccruedBalance().subtract(lsr.getPendingLeaveRequests());
165                if(lsr.getUsageLimit()!=null) { //should not set leave balance to usage limit simply because it's not null.
166                    BigDecimal availableUsage = lsr.getUsageLimit().subtract(lsr.getYtdApprovedUsage().add(lsr.getPendingLeaveRequests()));
167                    if(leaveBalance.compareTo( availableUsage ) > 0)
168                        lsr.setLeaveBalance(availableUsage);
169                    else
170                        lsr.setLeaveBalance(leaveBalance);
171                } else { //no usage limit
172                    lsr.setLeaveBalance(leaveBalance);
173                }
174            }
175            leaveBalance = lsr.getLeaveBalance();
176            return leaveBalance;    
177        }
178        
179        protected LeaveSummary getLeaveSummary(String principalId,
180                                               java.sql.Date startDate,
181                                               java.sql.Date endDate,
182                                               String accrualCategory,
183                                               boolean includeFuture) {
184            LeaveSummary ls = new LeaveSummary();
185            List<LeaveSummaryRow> rows = new ArrayList<LeaveSummaryRow>();
186    
187            if(StringUtils.isEmpty(principalId) || startDate == null || endDate == null) {
188                return ls;
189            }
190    
191            Set<String> leavePlans = getLeavePlans(principalId, startDate, endDate);
192            PrincipalHRAttributes pha = getPrincipalHrAttributes(principalId, startDate, endDate);
193            if (CollectionUtils.isNotEmpty(leavePlans)) {
194                for(String aLpString : leavePlans) {
195                    LeavePlan lp = TkServiceLocator.getLeavePlanService().getLeavePlan(aLpString, startDate);
196                    if(lp == null) {
197                        continue;
198                    }
199                    DateFormat formatter = new SimpleDateFormat("MMMM d");
200                    DateFormat formatter2 = new SimpleDateFormat("MMMM d yyyy");
201                    DateTime entryEndDate = new LocalDateTime(endDate).toDateTime();
202                    if (entryEndDate.getHourOfDay() == 0) {
203                        entryEndDate = entryEndDate.minusDays(1);
204                    }
205                    String aString = formatter.format(startDate) + " - " + formatter2.format(entryEndDate.toDate());
206                    ls.setPendingDatesString(aString);
207    
208                    LeaveCalendarDocumentHeader approvedLcdh = TkServiceLocator.getLeaveCalendarDocumentHeaderService().getMaxEndDateApprovedLeaveCalendar(principalId);
209                    if(approvedLcdh != null) {
210                        Date endApprovedDate = new java.sql.Date(approvedLcdh.getEndDate().getTime());
211                        LocalDateTime aLocalTime = new DateTime(approvedLcdh.getEndDate()).toLocalDateTime();
212                        DateTime endApprovedTime = aLocalTime.toDateTime(TkServiceLocator.getTimezoneService().getUserTimezoneWithFallback());
213                        if(endApprovedTime.getHourOfDay() == 0) {
214                            endApprovedDate = TKUtils.addDates(endApprovedDate, -1);
215                        }
216                        String datesString = formatter.format(approvedLcdh.getBeginDate()) + " - " + formatter2.format(endApprovedDate);
217                        ls.setYtdDatesString(datesString);
218                    }
219    
220                    //until we have something that creates carry over, we need to grab everything.
221                    // Calculating leave bLocks from Calendar Year start instead of Service Date
222                    Map<String, LeaveBlock> carryOverBlocks = getLeaveBlockService().getLastCarryOverBlocks(principalId, startDate);
223                    
224                    boolean filterByAccrualCategory = false;
225                    if (StringUtils.isNotEmpty(accrualCategory)) {
226                        filterByAccrualCategory = true;
227                        //remove unwanted carry over blocks from map
228                        LeaveBlock carryOverBlock = carryOverBlocks.get(accrualCategory);
229                        carryOverBlocks = new HashMap<String, LeaveBlock>(1);
230                        if(ObjectUtils.isNotNull(carryOverBlock))
231                            carryOverBlocks.put(carryOverBlock.getAccrualCategory(), carryOverBlock);
232                    }
233                    List<LeaveBlock> leaveBlocks = getLeaveBlockService().getLeaveBlocksSinceCarryOver(principalId, carryOverBlocks, (new LocalDateTime(endDate)).toDateTime(), filterByAccrualCategory);
234    
235                    List<LeaveBlock> futureLeaveBlocks = new ArrayList<LeaveBlock>();
236                    if (includeFuture) {
237                        if (!filterByAccrualCategory) {
238                            futureLeaveBlocks = getLeaveBlockService().getLeaveBlocks(principalId, endDate, (new LocalDateTime(endDate)).toDateTime().plusMonths(Integer.parseInt(lp.getPlanningMonths())).toDate());
239                        } else {
240                            futureLeaveBlocks = getLeaveBlockService().getLeaveBlocksWithAccrualCategory(principalId, endDate, (new LocalDateTime(endDate)).toDateTime().plusMonths(Integer.parseInt(lp.getPlanningMonths())).toDate(), accrualCategory);
241                        }
242                    }
243                    Map<String, List<LeaveBlock>> leaveBlockMap = mapLeaveBlocksByAccrualCategory(leaveBlocks);
244                    Map<String, List<LeaveBlock>> futureLeaveBlockMap = mapLeaveBlocksByAccrualCategory(futureLeaveBlocks);
245                    List<AccrualCategory> acList = TkServiceLocator.getAccrualCategoryService().getActiveAccrualCategoriesForLeavePlan(lp.getLeavePlan(), endDate);
246                    if(CollectionUtils.isNotEmpty(acList)) {
247                        for(AccrualCategory ac : acList) {
248                            if(ac.getShowOnGrid().equals("Y")) {
249                                    
250                                LeaveSummaryRow lsr = new LeaveSummaryRow();
251                                lsr.setAccrualCategory(ac.getAccrualCategory());
252                                lsr.setAccrualCategoryId(ac.getLmAccrualCategoryId());
253                                //get max balances
254                                AccrualCategoryRule acRule = TkServiceLocator.getAccrualCategoryRuleService().getAccrualCategoryRuleForDate(ac, endDate, pha.getServiceDate());
255                                //accrual category rule id set on a leave summary row will be useful in generating a relevant balance transfer
256                                //document from the leave calendar display. Could put this id in the request for balance transfer document.
257                                EmployeeOverride maxUsageOverride = TkServiceLocator.getEmployeeOverrideService().getEmployeeOverride(principalId, lp.getLeavePlan(), ac.getAccrualCategory(), "MU", endDate);
258    
259                                lsr.setAccrualCategoryRuleId(acRule == null ? null : acRule.getLmAccrualCategoryRuleId());
260                                if(acRule != null &&
261                                        (acRule.getMaxBalance()!= null
262                                                || acRule.getMaxUsage() != null)) {
263                                    if (acRule.getMaxUsage() != null) {
264                                        lsr.setUsageLimit(new BigDecimal(acRule.getMaxUsage()).setScale(2));
265                                    } else {
266                                        lsr.setUsageLimit(null);
267                                    }
268    
269                                } else {
270                                    lsr.setUsageLimit(null);
271                                }
272    
273                                if(maxUsageOverride !=null)
274                                    lsr.setUsageLimit(new BigDecimal(maxUsageOverride.getOverrideValue()));
275    
276                                //Fetching leaveblocks for accCat with type CarryOver -- This is logic according to the CO blocks created from scheduler job.
277                                BigDecimal carryOver = BigDecimal.ZERO.setScale(2);
278                                lsr.setCarryOver(carryOver);
279    
280                                //handle up to current leave blocks
281                                //CalendarEntry.getEndPeriodDate passed to fetch leave block amounts on last day of Calendar period
282                                assignApprovedValuesToRow(lsr, ac.getAccrualCategory(), leaveBlockMap.get(ac.getAccrualCategory()), lp, startDate, endDate);
283    
284                                //how about the leave blocks on the calendar entry being currently handled??
285    /*                            if(carryOverBlocks.containsKey(lsr.getAccrualCategory())) {
286                                    LeaveBlock carryOverBlock = carryOverBlocks.get(lsr.getAccrualCategory());
287                                    DateTime carryOverBlockLPStart = TkServiceLocator.getLeavePlanService().getFirstDayOfLeavePlan(lp.getLeavePlan(), carryOverBlock.getLeaveDate());
288                                    DateTime currentLPStart = TkServiceLocator.getLeavePlanService().getFirstDayOfLeavePlan(lp.getLeavePlan(), TKUtils.getCurrentDate());
289                                    if(carryOverBlockLPStart.equals(currentLPStart))
290                                            carryOver = carryOverBlock.getLeaveAmount();
291                                }*/
292                                //figure out past carry over values!!!
293                                //We now have list of past years accrual and use (with ordered keys!!!)
294    
295                                //merge key sets
296                                if (carryOverBlocks.containsKey(lsr.getAccrualCategory())) {
297                                    carryOver = carryOverBlocks.get(lsr.getAccrualCategory()).getLeaveAmount();
298                                    carryOver = carryOver.setScale(2);
299                                }
300                                Set<String> keyset = new HashSet<String>();
301                                keyset.addAll(lsr.getPriorYearsUsage().keySet());
302                                keyset.addAll(lsr.getPriorYearsTotalAccrued().keySet());
303                                for (String key : keyset) {
304                                    BigDecimal value = lsr.getPriorYearsTotalAccrued().get(key);
305                                    if (value == null) {
306                                        value = BigDecimal.ZERO;
307                                    }
308                                    carryOver = carryOver.add(value);
309                                    BigDecimal use = lsr.getPriorYearsUsage().containsKey(key) ? lsr.getPriorYearsUsage().get(key) : BigDecimal.ZERO;
310                                    carryOver = carryOver.add(use);
311                                    EmployeeOverride carryOverOverride = TkServiceLocator.getEmployeeOverrideService().getEmployeeOverride(principalId, lp.getLeavePlan(), ac.getAccrualCategory(), "MAC", endDate);
312                                    if (acRule != null  && acRule.getMaxCarryOver() != null) {
313                                            BigDecimal carryOverDisplay = BigDecimal.ZERO;
314                                            if(carryOverOverride != null)
315                                                    carryOverDisplay = new BigDecimal(carryOverOverride.getOverrideValue() < carryOver.longValue() ? carryOverOverride.getOverrideValue() : carryOver.longValue());
316                                            else
317                                                    carryOverDisplay =  new BigDecimal(acRule.getMaxCarryOver() < carryOver.longValue() ? acRule.getMaxCarryOver() : carryOver.longValue());
318                                        carryOver = carryOverDisplay;
319                                    }
320                                }
321                                
322                                lsr.setCarryOver(carryOver);
323                                if (acRule != null && acRule.getMaxCarryOver() != null) {
324                                    EmployeeOverride carryOverOverride = TkServiceLocator.getEmployeeOverrideService().getEmployeeOverride(principalId, lp.getLeavePlan(), ac.getAccrualCategory(), "MAC", endDate);
325                                    if(carryOverOverride != null)
326                                            lsr.setMaxCarryOver(new BigDecimal(carryOverOverride.getOverrideValue()));
327                                    else
328                                            lsr.setMaxCarryOver(new BigDecimal(acRule.getMaxCarryOver() < carryOver.longValue() ? acRule.getMaxCarryOver() : carryOver.longValue()));
329                                }
330    
331                                //handle future leave blocks
332                                assignPendingValuesToRow(lsr, ac.getAccrualCategory(), futureLeaveBlockMap.get(ac.getAccrualCategory()));
333    
334                                //compute Leave Balance
335                                BigDecimal leaveBalance = lsr.getAccruedBalance().subtract(lsr.getPendingLeaveRequests());
336                                //if leave balance is set
337                                //Employee overrides have already been taken into consideration and the appropriate values
338                                //for usage have been set by this point.
339    //                            if (acRule != null && StringUtils.equals(acRule.getMaxBalFlag(), "Y")) {
340                                //there exists an accrual category rule with max balance limit imposed.
341                                //max bal flag = 'Y' has no precedence here with max-bal / balance transfers implemented.
342                                //unless institutions are not required to define a max balance limit for action_at_max_bal = LOSE.
343                                //Possibly preferable to procure forfeiture blocks through balance transfer
344                                if(lsr.getUsageLimit()!=null) { //should not set leave balance to usage limit simply because it's not null.
345                                    BigDecimal availableUsage = lsr.getUsageLimit().subtract(lsr.getYtdApprovedUsage().add(lsr.getPendingLeaveRequests()));
346                                    if(leaveBalance.compareTo( availableUsage ) > 0)
347                                        lsr.setLeaveBalance(availableUsage);
348                                    else
349                                        lsr.setLeaveBalance(leaveBalance);
350                                } else { //no usage limit
351                                    lsr.setLeaveBalance(leaveBalance);
352                                }
353    
354                                rows.add(lsr);
355                            }
356                        }
357                        // let's check for 'empty' accrual categories
358                        if (leaveBlockMap.containsKey(null)
359                                || futureLeaveBlockMap.containsKey(null)) {
360                            LeaveSummaryRow otherLeaveSummary = new LeaveSummaryRow();
361                            //otherLeaveSummary.setAccrualCategory("Other");
362    
363                            assignApprovedValuesToRow(otherLeaveSummary, null, leaveBlockMap.get(null), lp, startDate, endDate);
364                            BigDecimal carryOver = BigDecimal.ZERO.setScale(2);
365                            for (Map.Entry<String, BigDecimal> entry : otherLeaveSummary.getPriorYearsTotalAccrued().entrySet()) {
366                                carryOver = carryOver.add(entry.getValue());
367                                BigDecimal use = otherLeaveSummary.getPriorYearsUsage().containsKey(entry.getKey()) ? otherLeaveSummary.getPriorYearsUsage().get(entry.getKey()) : BigDecimal.ZERO;
368                                carryOver = carryOver.add(use);
369                            }
370                            otherLeaveSummary.setCarryOver(carryOver);
371                            assignPendingValuesToRow(otherLeaveSummary, null, futureLeaveBlockMap.get(null));
372                            otherLeaveSummary.setAccrualCategory("Other");
373    
374                            //compute Leave Balance
375                            // blank the avail
376                            otherLeaveSummary.setUsageLimit(null);
377                            otherLeaveSummary.setLeaveBalance(null);
378    
379                            rows.add(otherLeaveSummary);
380                        }
381                    }
382                }
383            }
384            ls.setLeaveSummaryRows(rows);
385            return ls;
386        }
387    
388        private PrincipalHRAttributes getPrincipalHrAttributes(String principalId, Date startDate, Date endDate) {
389            PrincipalHRAttributes pha = TkServiceLocator.getPrincipalHRAttributeService().getPrincipalCalendar(principalId, startDate);
390            if(pha == null) {       // principal hr attributes does not exist at the beginning of this calendar entry
391                pha = TkServiceLocator.getPrincipalHRAttributeService().getPrincipalCalendar(principalId, endDate);
392            }
393            return pha;
394        }
395    
396        private Set<String> getLeavePlans(String principalId, Date startDate, Date endDate) {
397            Set<String> lpStrings = new HashSet<String>();     //
398    
399            PrincipalHRAttributes pha = getPrincipalHrAttributes(principalId, startDate, endDate);
400            // get list of principalHrAttributes that become active during this pay period
401            List<PrincipalHRAttributes> phaList = TkServiceLocator.getPrincipalHRAttributeService()
402                    .getActivePrincipalHrAttributesForRange(principalId, startDate, endDate);
403            if(pha != null) {
404                lpStrings.add(pha.getLeavePlan());
405            }
406            if(CollectionUtils.isNotEmpty(phaList)) {
407                for(PrincipalHRAttributes aPha : phaList) {
408                    lpStrings.add(aPha.getLeavePlan());
409                }
410            }
411    
412            return lpStrings;
413        }
414    
415    
416        private Map<String, List<LeaveBlock>> mapLeaveBlocksByAccrualCategory(List<LeaveBlock> leaveBlocks) {
417            Map<String, List<LeaveBlock>> map = new HashMap<String, List<LeaveBlock>>();
418            for (LeaveBlock lb : leaveBlocks) {
419                if (map.containsKey(lb.getAccrualCategory())) {
420                    map.get(lb.getAccrualCategory()).add(lb);
421                } else {
422                    List<LeaveBlock> splitLeaveBlocks = new ArrayList<LeaveBlock>();
423                    splitLeaveBlocks.add(lb);
424                    map.put(lb.getAccrualCategory(), splitLeaveBlocks);
425                }
426            }
427            return map;
428        }
429    
430        /**
431         * Use with button display for balance transfers available On-Demand.
432         * @param lsr
433         * @param accrualCategoryRule
434         * @param principalId 
435         */
436        private void markTransferable(LeaveSummaryRow lsr, AccrualCategoryRule accrualCategoryRule, String principalId) {
437            //return type must be changed to boolean, or an associated field element must be created for decision
438            //purposes.
439            //an accrual category's balance is transferable if the accrued balance is 
440            //greater than the maximum balance allowed for the accrual category. action_at_max_balance must be TRANSFER
441            boolean transferable = false;
442            if(ObjectUtils.isNotNull(accrualCategoryRule)) {
443                    if(ObjectUtils.isNotNull(accrualCategoryRule.getMaxBalance())) {
444                            BigDecimal maxBalance = accrualCategoryRule.getMaxBalance();
445                            BigDecimal fte = TkServiceLocator.getJobService().getFteSumForAllActiveLeaveEligibleJobs(principalId, TKUtils.getCurrentDate());
446                            BigDecimal adjustedMaxBalance = maxBalance.multiply(fte);
447                            List<EmployeeOverride> overrides = TkServiceLocator.getEmployeeOverrideService().getEmployeeOverrides(principalId, TKUtils.getCurrentDate());
448                            for(EmployeeOverride override : overrides) {
449                                    if(StringUtils.equals(override.getOverrideType(),TkConstants.EMPLOYEE_OVERRIDE_TYPE.get("MB"))
450                                                    && override.isActive()) {
451                                            adjustedMaxBalance = new BigDecimal(override.getOverrideValue());
452                                            break;
453                                    }
454                            }
455                            if(adjustedMaxBalance.compareTo(lsr.getAccruedBalance()) < 0) {
456                                    if(StringUtils.equals(accrualCategoryRule.getActionAtMaxBalance(), LMConstants.ACTION_AT_MAX_BAL.TRANSFER) &&
457                                                    StringUtils.equals(accrualCategoryRule.getMaxBalanceActionFrequency(),LMConstants.MAX_BAL_ACTION_FREQ.ON_DEMAND))
458                                            transferable = true;
459                            }
460                    }
461            }
462            lsr.setTransferable(transferable);
463        }
464        
465        /**
466         * Use with button display for balance transfer payouts available On-Demand.
467         * @param lsr
468         * @param accrualCategoryRule 
469         * @param principalId 
470         */
471        private void markPayoutable(LeaveSummaryRow lsr, AccrualCategoryRule accrualCategoryRule, String principalId) {
472            //return type must be changed to boolean, or an associated field element must be created for decision
473            //purposes.
474            //an accrual category's balance is transferable if max_bal_action_frequency is ON-DEMAND
475            //and action_at_max_balance is PAYOUT
476            boolean payoutable = false;
477            if(ObjectUtils.isNotNull(accrualCategoryRule)) {
478                    if(ObjectUtils.isNotNull(accrualCategoryRule.getMaxBalance())) {
479                            BigDecimal maxBalance = accrualCategoryRule.getMaxBalance();
480                            BigDecimal fte = TkServiceLocator.getJobService().getFteSumForAllActiveLeaveEligibleJobs(principalId, TKUtils.getCurrentDate());
481                            BigDecimal adjustedMaxBalance = maxBalance.multiply(fte);
482                            List<EmployeeOverride> overrides = TkServiceLocator.getEmployeeOverrideService().getEmployeeOverrides(principalId, TKUtils.getCurrentDate());
483                            for(EmployeeOverride override : overrides) {
484                                    if(StringUtils.equals(override.getOverrideType(),TkConstants.EMPLOYEE_OVERRIDE_TYPE.get("MB"))
485                                                    && override.isActive()) {
486                                            adjustedMaxBalance = new BigDecimal(override.getOverrideValue());
487                                            break;
488                                    }
489                            }
490                            if(adjustedMaxBalance.compareTo(lsr.getAccruedBalance()) < 0) {
491                                    if(StringUtils.equals(accrualCategoryRule.getActionAtMaxBalance(), LMConstants.ACTION_AT_MAX_BAL.PAYOUT) &&
492                                                    StringUtils.equals(accrualCategoryRule.getMaxBalanceActionFrequency(),LMConstants.MAX_BAL_ACTION_FREQ.ON_DEMAND))
493                                            payoutable = true;
494                            }
495                    }
496            }
497            lsr.setPayoutable(payoutable);
498        }
499        
500            private void assignApprovedValuesToRow(LeaveSummaryRow lsr, String accrualCategory, List<LeaveBlock> approvedLeaveBlocks, LeavePlan lp, Date ytdEarnedEffectiveDate, Date effectiveDate) {
501    
502            SortedMap<String, BigDecimal> yearlyAccrued = new TreeMap<String, BigDecimal>();
503            SortedMap<String, BigDecimal> yearlyUsage = new TreeMap<String, BigDecimal>();
504            BigDecimal accrualedBalance = BigDecimal.ZERO.setScale(2);
505                    BigDecimal approvedUsage = BigDecimal.ZERO.setScale(2);
506                    BigDecimal fmlaUsage = BigDecimal.ZERO.setScale(2);
507    
508            Date cutOffDateToCheck = ytdEarnedEffectiveDate != null ? ytdEarnedEffectiveDate : effectiveDate;
509            DateTime cutOffDate = TkServiceLocator.getLeavePlanService().getFirstDayOfLeavePlan(lp.getLeavePlan(), cutOffDateToCheck).minus(1);
510    
511            Timestamp priorYearCutOff = new Timestamp(cutOffDate.getMillis());
512    
513            if (CollectionUtils.isNotEmpty(approvedLeaveBlocks)) {
514                // create it here so we don't need to get instance every loop iteration
515                for(LeaveBlock aLeaveBlock : approvedLeaveBlocks) {
516                    // check if leave date is before the next calendar start.
517                    if(!aLeaveBlock.getLeaveBlockType().equals(LMConstants.LEAVE_BLOCK_TYPE.CARRY_OVER)
518                            && aLeaveBlock.getLeaveDate().getTime() < effectiveDate.getTime()) {
519                        if((StringUtils.isBlank(accrualCategory) && StringUtils.isBlank(aLeaveBlock.getAccrualCategory()))
520                                || (StringUtils.isNotBlank(aLeaveBlock.getAccrualCategory())
521                                    && StringUtils.equals(aLeaveBlock.getAccrualCategory(), accrualCategory))) {
522                            if(aLeaveBlock.getLeaveAmount().compareTo(BigDecimal.ZERO) >= 0
523                                    && !aLeaveBlock.getLeaveBlockType().equals(LMConstants.LEAVE_BLOCK_TYPE.LEAVE_CALENDAR)) {
524                                if(!(StringUtils.equals(LMConstants.REQUEST_STATUS.DISAPPROVED, aLeaveBlock.getRequestStatus()) ||
525                                            StringUtils.equals(LMConstants.REQUEST_STATUS.DEFERRED, aLeaveBlock.getRequestStatus()))) {
526                                    if (aLeaveBlock.getLeaveDate().getTime() <= priorYearCutOff.getTime()) {
527                                        String yearKey = getYearKey(aLeaveBlock.getLeaveDate(), lp);
528                                        BigDecimal co = yearlyAccrued.get(yearKey);
529                                        if (co == null) {
530                                            co = BigDecimal.ZERO.setScale(2);
531                                        }
532                                        co = co.add(aLeaveBlock.getLeaveAmount());
533                                        yearlyAccrued.put(yearKey, co);
534                                    } else if(aLeaveBlock.getLeaveDate().getTime() < ytdEarnedEffectiveDate.getTime()) {
535                                        accrualedBalance = accrualedBalance.add(aLeaveBlock.getLeaveAmount());
536                                    }
537                               }
538                            } else {
539                                    BigDecimal currentLeaveAmount = aLeaveBlock.getLeaveAmount().compareTo(BigDecimal.ZERO) > 0 ? aLeaveBlock.getLeaveAmount().negate() : aLeaveBlock.getLeaveAmount();
540                                    //we only want this for the current calendar!!!
541                                    if(!(StringUtils.equals(LMConstants.REQUEST_STATUS.DISAPPROVED, aLeaveBlock.getRequestStatus()) ||
542                                                    StringUtils.equals(LMConstants.REQUEST_STATUS.DEFERRED, aLeaveBlock.getRequestStatus()))) {
543    
544                                            EarnCode ec = TkServiceLocator.getEarnCodeService().getEarnCode(aLeaveBlock.getEarnCode(), aLeaveBlock.getLeaveDate());
545    
546                                            if ((ec != null && StringUtils.equals(ec.getAccrualBalanceAction(), LMConstants.ACCRUAL_BALANCE_ACTION.USAGE))
547                                                            || aLeaveBlock.getLeaveBlockType().equals(LMConstants.LEAVE_BLOCK_TYPE.BALANCE_TRANSFER)
548                                                            || aLeaveBlock.getLeaveBlockType().equals(LMConstants.LEAVE_BLOCK_TYPE.LEAVE_PAYOUT)
549                                                            || aLeaveBlock.getLeaveBlockType().equals(LMConstants.LEAVE_BLOCK_TYPE.LEAVE_ADJUSTMENT_MAINT)
550                                                            || aLeaveBlock.getLeaveBlockType().equals(LMConstants.LEAVE_BLOCK_TYPE.DONATION_MAINT)){
551                                                    if (aLeaveBlock.getLeaveDate().getTime() > priorYearCutOff.getTime()) {
552    
553                                                            approvedUsage = approvedUsage.add(currentLeaveAmount);
554    
555                                                            if(ec != null && ec.getFmla().equals("Y")) {
556                                                                    fmlaUsage = fmlaUsage.add(aLeaveBlock.getLeaveAmount());
557                                                            }
558                                                    } else {
559                                                            //these usages are for previous years, to help figure out correct carry over values
560                                                            String yearKey = getYearKey(aLeaveBlock.getLeaveDate(), lp);
561                                                            BigDecimal use = yearlyUsage.get(yearKey);
562                                                            if (use == null) {
563                                                                    use = BigDecimal.ZERO.setScale(2);
564                                                            }
565                                                            use = use.add(currentLeaveAmount);
566                                                            yearlyUsage.put(yearKey, use);
567                                                    }
568                                            }
569                                    }
570                            }
571    
572                            //}
573                        }
574                    } else {
575                        //we can actually use the carry over block!!
576    
577                    }
578                }
579            }
580    
581            lsr.setPriorYearsTotalAccrued(yearlyAccrued);
582            lsr.setPriorYearsUsage(yearlyUsage);
583                    lsr.setYtdAccruedBalance(accrualedBalance);
584                    lsr.setYtdApprovedUsage(approvedUsage.negate());
585                    lsr.setFmlaUsage(fmlaUsage.negate());
586                    
587                    //lsr.setLeaveBalance(lsr.getYtdAccruedBalance().add(approvedUsage));
588            }
589    
590        private String getYearKey(Date leaveDate, LeavePlan lp){
591            Calendar cal = Calendar.getInstance();
592            Calendar leavePlanCal = Calendar.getInstance();
593            cal.setTime(leaveDate);
594            String yearKey = Integer.toString(cal.get(Calendar.YEAR));
595            leavePlanCal.set(Calendar.MONTH, Integer.parseInt(lp.getCalendarYearStartMonth()) - 1);
596            leavePlanCal.set(Calendar.DAY_OF_MONTH, Integer.parseInt(lp.getCalendarYearStartDayOfMonth()));
597            leavePlanCal.set(Calendar.YEAR, cal.get(Calendar.YEAR));
598            leavePlanCal.set(Calendar.HOUR_OF_DAY, 0);
599            leavePlanCal.set(Calendar.MINUTE, 0);
600            leavePlanCal.set(Calendar.SECOND, 0);
601            leavePlanCal.set(Calendar.MILLISECOND, 0);
602    
603            if (cal.getTime().before(leavePlanCal.getTime())) {
604                yearKey = Integer.toString(cal.get(Calendar.YEAR) - 1);
605            }
606            return yearKey;
607        }
608            
609            private void assignPendingValuesToRow(LeaveSummaryRow lsr, String accrualCategory, List<LeaveBlock> pendingLeaveBlocks ) {
610                    BigDecimal pendingAccrual= BigDecimal.ZERO.setScale(2);
611                    BigDecimal pendingRequests = BigDecimal.ZERO.setScale(2);
612            if (CollectionUtils.isNotEmpty(pendingLeaveBlocks)) {
613                for(LeaveBlock aLeaveBlock : pendingLeaveBlocks) {
614                    if(!aLeaveBlock.getLeaveBlockType().equals(LMConstants.LEAVE_BLOCK_TYPE.CARRY_OVER)) {
615                    if((StringUtils.isBlank(accrualCategory) && StringUtils.isBlank(aLeaveBlock.getAccrualCategory()))
616                            || (StringUtils.isNotBlank(aLeaveBlock.getAccrualCategory())
617                                && StringUtils.equals(aLeaveBlock.getAccrualCategory(), accrualCategory))) {
618                        if(aLeaveBlock.getLeaveAmount().compareTo(BigDecimal.ZERO) >= 0) {
619                            pendingAccrual = pendingAccrual.add(aLeaveBlock.getLeaveAmount());
620                        } else {
621                            pendingRequests = pendingRequests.add(aLeaveBlock.getLeaveAmount());
622                        }
623                    }
624                    }
625                }
626            }
627                    lsr.setPendingLeaveAccrual(pendingAccrual);
628                    lsr.setPendingLeaveRequests(pendingRequests.negate());
629            }
630            
631            @Override
632            public List<Date> getLeaveSummaryDates(CalendarEntries calendarEntry) {
633                    List<Date> leaveSummaryDates = new ArrayList<Date>();
634    
635                    DateTime start = calendarEntry.getBeginLocalDateTime().toDateTime();
636                    DateTime end = calendarEntry.getEndLocalDateTime().toDateTime();
637            Interval interval = new Interval(start, end);
638    
639            for (DateTime day = interval.getStart(); day.isBefore(interval.getEnd()); day = day.plusDays(1)) {
640                    leaveSummaryDates.add(day.toLocalDate().toDateTimeAtStartOfDay().toDate());
641            }
642                     
643                     return leaveSummaryDates;
644            }
645    
646        protected LeaveBlockService getLeaveBlockService() {
647            if (leaveBlockService == null) {
648                leaveBlockService = TkServiceLocator.getLeaveBlockService();
649            }
650            return leaveBlockService;
651        }
652    
653    
654    }
655