View Javadoc

1   /**
2    * Copyright 2004-2013 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.hr.lm.leaveSummary.service;
17  
18  import org.apache.commons.collections.CollectionUtils;
19  import org.apache.commons.lang.StringUtils;
20  import org.apache.commons.lang.time.DateUtils;
21  import org.joda.time.DateMidnight;
22  import org.joda.time.DateTime;
23  import org.joda.time.Interval;
24  import org.joda.time.LocalDateTime;
25  import org.kuali.hr.lm.LMConstants;
26  import org.kuali.hr.lm.accrual.AccrualCategory;
27  import org.kuali.hr.lm.accrual.AccrualCategoryRule;
28  import org.kuali.hr.lm.employeeoverride.EmployeeOverride;
29  import org.kuali.hr.lm.leaveSummary.LeaveSummary;
30  import org.kuali.hr.lm.leaveSummary.LeaveSummaryRow;
31  import org.kuali.hr.lm.leaveblock.LeaveBlock;
32  import org.kuali.hr.lm.leaveblock.service.LeaveBlockService;
33  import org.kuali.hr.lm.leaveplan.LeavePlan;
34  import org.kuali.hr.lm.workflow.LeaveCalendarDocumentHeader;
35  import org.kuali.hr.time.calendar.CalendarEntries;
36  import org.kuali.hr.time.earncode.EarnCode;
37  import org.kuali.hr.time.principal.PrincipalHRAttributes;
38  import org.kuali.hr.time.service.base.TkServiceLocator;
39  import org.kuali.hr.time.util.TKUtils;
40  import org.kuali.hr.time.util.TkConstants;
41  import org.kuali.rice.krad.util.ObjectUtils;
42  
43  import java.math.BigDecimal;
44  import java.sql.Timestamp;
45  import java.text.DateFormat;
46  import java.text.ParseException;
47  import java.text.SimpleDateFormat;
48  import java.util.*;
49  
50  public class LeaveSummaryServiceImpl implements LeaveSummaryService {
51  	private LeaveBlockService leaveBlockService;
52  
53      @Override
54      public LeaveSummary getLeaveSummaryAsOfDate(String principalId, java.sql.Date asOfDate) {
55          return getLeaveSummary(principalId, asOfDate, asOfDate, null, true);
56      }
57  
58      public LeaveSummary getLeaveSummaryAsOfDateWithoutFuture(String principalId, java.sql.Date asOfDate) {
59          return getLeaveSummary(principalId, asOfDate, asOfDate, null, false);
60      }
61  
62      @Override
63      public LeaveSummary getLeaveSummary(String principalId, CalendarEntries calendarEntry) {
64          return getLeaveSummary(principalId, calendarEntry.getBeginPeriodDate(), calendarEntry.getEndPeriodDate(), null, true);
65      }
66  
67      @Override
68      public LeaveSummary getLeaveSummaryAsOfDateForAccrualCategory(String principalId, java.sql.Date asOfDate, String accrualCategory) {
69          return getLeaveSummary(principalId, asOfDate, asOfDate, accrualCategory, true);
70      }
71  
72      @Override
73      // 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
74      // will get leave balance up to the next earn interval for a certain date, including usage up to that next earn interval
75      public BigDecimal getLeaveBalanceForAccrCatUpToDate(String principalId,
76              java.sql.Date startDate,	
77              java.sql.Date endDate,
78              String accrualCategory,
79              Date usageEndDate) {
80      	BigDecimal leaveBalance = BigDecimal.ZERO;
81          if(StringUtils.isEmpty(principalId) || startDate == null || endDate == null || StringUtils.isEmpty(accrualCategory) || usageEndDate == null) {
82              return leaveBalance;
83          }
84  
85          LeaveSummaryRow lsr = new LeaveSummaryRow();
86          AccrualCategory ac = TkServiceLocator.getAccrualCategoryService().getAccrualCategory(accrualCategory, endDate);
87          
88          if(ac != null) {
89              LeavePlan lp = TkServiceLocator.getLeavePlanService().getLeavePlan(ac.getLeavePlan(), ac.getEffectiveDate());
90              if(lp == null) {
91                 return leaveBalance;
92              }
93              PrincipalHRAttributes pha = getPrincipalHrAttributes(principalId, startDate, endDate);
94              //until we have something that creates carry over, we need to grab everything.
95              // Calculating leave bLocks from Calendar Year start instead of Service Date
96              Map<String, LeaveBlock> carryOverBlocks = getLeaveBlockService().getLastCarryOverBlocks(principalId, startDate);
97              //remove unwanted carry over blocks from map
98              LeaveBlock carryOverBlock = carryOverBlocks.get(accrualCategory);
99              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