1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.kuali.hr.time.timesummary.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.apache.log4j.Logger;
22 import org.joda.time.DateTime;
23 import org.joda.time.DateTimeFieldType;
24 import org.joda.time.Interval;
25 import org.joda.time.LocalDateTime;
26 import org.kuali.hr.job.Job;
27 import org.kuali.hr.lm.LMConstants;
28 import org.kuali.hr.lm.accrual.AccrualCategory;
29 import org.kuali.hr.lm.accrual.AccrualCategoryRule;
30 import org.kuali.hr.lm.accrual.service.AccrualServiceImpl;
31 import org.kuali.hr.lm.leaveSummary.LeaveSummary;
32 import org.kuali.hr.lm.leaveSummary.LeaveSummaryRow;
33 import org.kuali.hr.lm.leaveblock.LeaveBlock;
34 import org.kuali.hr.lm.util.LeaveBlockAggregate;
35 import org.kuali.hr.time.assignment.Assignment;
36 import org.kuali.hr.time.assignment.AssignmentDescriptionKey;
37 import org.kuali.hr.time.calendar.Calendar;
38 import org.kuali.hr.time.calendar.CalendarEntries;
39 import org.kuali.hr.time.earncode.EarnCode;
40 import org.kuali.hr.time.earncodegroup.EarnCodeGroup;
41 import org.kuali.hr.time.flsa.FlsaDay;
42 import org.kuali.hr.time.flsa.FlsaWeek;
43 import org.kuali.hr.time.principal.PrincipalHRAttributes;
44 import org.kuali.hr.time.service.base.TkServiceLocator;
45 import org.kuali.hr.time.timeblock.TimeBlock;
46 import org.kuali.hr.time.timeblock.TimeHourDetail;
47 import org.kuali.hr.time.timesheet.TimesheetDocument;
48 import org.kuali.hr.time.timesummary.AssignmentRow;
49 import org.kuali.hr.time.timesummary.EarnCodeSection;
50 import org.kuali.hr.time.timesummary.EarnGroupSection;
51 import org.kuali.hr.time.timesummary.TimeSummary;
52 import org.kuali.hr.time.util.TKUtils;
53 import org.kuali.hr.time.util.TkConstants;
54 import org.kuali.hr.time.util.TkTimeBlockAggregate;
55 import org.kuali.hr.time.workarea.WorkArea;
56
57 import java.math.BigDecimal;
58 import java.sql.Timestamp;
59 import java.util.*;
60
61 public class TimeSummaryServiceImpl implements TimeSummaryService {
62 private static final String OTHER_EARN_GROUP = "Other";
63 private static final Logger LOG = Logger.getLogger(TimeSummaryServiceImpl.class);
64
65 @Override
66 public TimeSummary getTimeSummary(TimesheetDocument timesheetDocument) {
67 TimeSummary timeSummary = new TimeSummary();
68
69 if(timesheetDocument.getTimeBlocks() == null) {
70 return timeSummary;
71 }
72
73 List<Boolean> dayArrangements = new ArrayList<Boolean>();
74
75 timeSummary.setSummaryHeader(getHeaderForSummary(timesheetDocument.getCalendarEntry(), dayArrangements));
76 TkTimeBlockAggregate tkTimeBlockAggregate = new TkTimeBlockAggregate(timesheetDocument.getTimeBlocks(), timesheetDocument.getCalendarEntry(), TkServiceLocator.getCalendarService().getCalendar(timesheetDocument.getCalendarEntry().getHrCalendarId()), true);
77
78 List<Assignment> timeAssignments = timesheetDocument.getAssignments();
79 List<String> tAssignmentKeys = new ArrayList<String>();
80 for(Assignment assign : timeAssignments) {
81 tAssignmentKeys.add(assign.getAssignmentKey());
82 }
83 List<LeaveBlock> leaveBlocks = TkServiceLocator.getLeaveBlockService().getLeaveBlocksForTimeCalendar(timesheetDocument.getPrincipalId(),
84 timesheetDocument.getCalendarEntry().getBeginPeriodDate(), timesheetDocument.getCalendarEntry().getEndPeriodDate(), tAssignmentKeys);
85 LeaveBlockAggregate leaveBlockAggregate = new LeaveBlockAggregate(leaveBlocks, timesheetDocument.getCalendarEntry());
86 tkTimeBlockAggregate = combineTimeAndLeaveAggregates(tkTimeBlockAggregate, leaveBlockAggregate);
87
88 timeSummary.setWorkedHours(getWorkedHours(tkTimeBlockAggregate));
89
90 List<EarnGroupSection> earnGroupSections = getEarnGroupSections(tkTimeBlockAggregate, timeSummary.getSummaryHeader().size()+1,
91 dayArrangements, timesheetDocument.getAsOfDate(), timesheetDocument.getDocEndDate());
92 timeSummary.setSections(earnGroupSections);
93
94 try {
95 List<LeaveSummaryRow> maxedLeaveRows = getMaxedLeaveRows(timesheetDocument.getCalendarEntry(),timesheetDocument.getPrincipalId());
96 timeSummary.setMaxedLeaveRows(maxedLeaveRows);
97 } catch (Exception e) {
98
99 LOG.error("error retreiving maxed leave rows", e);
100 }
101
102 return timeSummary;
103 }
104
105 private List<LeaveSummaryRow> getMaxedLeaveRows(
106 CalendarEntries calendarEntry, String principalId) throws Exception {
107 List<LeaveSummaryRow> maxedLeaveRows = new ArrayList<LeaveSummaryRow>();
108
109 if (TkServiceLocator.getLeaveApprovalService().isActiveAssignmentFoundOnJobFlsaStatus(principalId, TkConstants.FLSA_STATUS_NON_EXEMPT, true)) {
110
111 Map<String,Set<LeaveBlock>> eligibilities = TkServiceLocator.getAccrualCategoryMaxBalanceService().getMaxBalanceViolations(calendarEntry,principalId);
112 Set<LeaveBlock> onDemandTransfers = eligibilities.get(LMConstants.MAX_BAL_ACTION_FREQ.ON_DEMAND);
113
114 Interval calendarEntryInterval = new Interval(calendarEntry.getBeginPeriodDate().getTime(),calendarEntry.getEndPeriodDate().getTime());
115
116
117
118
119 if(!onDemandTransfers.isEmpty()) {
120 for(LeaveBlock lb : onDemandTransfers) {
121 Date leaveDate = lb.getLeaveDate();
122 LeaveSummary summary = TkServiceLocator.getLeaveSummaryService().getLeaveSummaryAsOfDate(principalId, new java.sql.Date(DateUtils.addDays(leaveDate, 1).getTime()));
123 LeaveSummaryRow row = summary.getLeaveSummaryRowForAccrualCtgy(lb.getAccrualCategory());
124 if(row != null) {
125
126
127 if(calendarEntryInterval.contains(leaveDate.getTime())) {
128
129
130
131 row.setInfractingLeaveBlockId(lb.getAccrualCategoryRuleId());
132 AccrualCategoryRule aRule = TkServiceLocator.getAccrualCategoryRuleService().getAccrualCategoryRule(lb.getAccrualCategoryRuleId());
133
134 if(StringUtils.equals(aRule.getActionAtMaxBalance(),LMConstants.ACTION_AT_MAX_BAL.TRANSFER))
135 row.setTransferable(true);
136 else if(StringUtils.equals(aRule.getActionAtMaxBalance(),LMConstants.ACTION_AT_MAX_BAL.PAYOUT))
137 row.setPayoutable(true);
138
139 boolean exists = false;
140 for(LeaveSummaryRow maxedRow : maxedLeaveRows) {
141 if(StringUtils.equals(maxedRow.getAccrualCategoryId(),row.getAccrualCategoryId()))
142 exists = true;
143 }
144 if(!exists)
145 maxedLeaveRows.add(row);
146 }
147 }
148 }
149 }
150 }
151 return maxedLeaveRows;
152 }
153
154
155
156
157
158
159
160
161
162 public List<EarnGroupSection> getEarnGroupSections(TkTimeBlockAggregate tkTimeBlockAggregate, int numEntries, List<Boolean> dayArrangements, Date asOfDate , Date docEndDate){
163 List<EarnGroupSection> earnGroupSections = new ArrayList<EarnGroupSection>();
164 List<FlsaWeek> flsaWeeks = tkTimeBlockAggregate.getFlsaWeeks(TkServiceLocator.getTimezoneService().getUserTimezoneWithFallback());
165 Map<String, EarnCodeSection> earnCodeToEarnCodeSection = new HashMap<String, EarnCodeSection>();
166 Map<String, EarnGroupSection> earnGroupToEarnGroupSection = new HashMap<String, EarnGroupSection>();
167
168 int dayCount = 0;
169
170
171
172 List<FlsaWeek> trimmedFlsaWeeks = new ArrayList<FlsaWeek>();
173 for(FlsaWeek flsaWeek : flsaWeeks){
174 if(flsaWeek.getFlsaDays().size() > 0){
175 trimmedFlsaWeeks.add(flsaWeek);
176 }
177 }
178
179
180
181 for(FlsaWeek flsaWeek : trimmedFlsaWeeks){
182 int weekSize = 0;
183 List<FlsaDay> flsaDays = flsaWeek.getFlsaDays();
184 for(FlsaDay flsaDay : flsaDays){
185 Map<String, List<TimeBlock>> earnCodeToTimeBlocks = flsaDay.getEarnCodeToTimeBlocks();
186
187 for(List<TimeBlock> timeBlocks : earnCodeToTimeBlocks.values()){
188 for(TimeBlock timeBlock : timeBlocks){
189 for(TimeHourDetail thd : timeBlock.getTimeHourDetails()){
190 if(StringUtils.equals(TkConstants.LUNCH_EARN_CODE, thd.getEarnCode())){
191 continue;
192 }
193 EarnCodeSection earnCodeSection = earnCodeToEarnCodeSection.get(thd.getEarnCode());
194 if(earnCodeSection == null){
195 earnCodeSection = new EarnCodeSection();
196 earnCodeSection.setEarnCode(thd.getEarnCode());
197 EarnCode earnCodeObj = TkServiceLocator.getEarnCodeService().getEarnCode(thd.getEarnCode(), TKUtils.getTimelessDate(asOfDate));
198 earnCodeSection.setDescription(earnCodeObj.getDescription());
199 earnCodeSection.setIsAmountEarnCode((earnCodeObj.getRecordMethod()!= null && earnCodeObj.getRecordMethod().equalsIgnoreCase(TkConstants.EARN_CODE_AMOUNT)) ? true : false);
200 for(int i = 0;i<(numEntries-1);i++){
201 earnCodeSection.getTotals().add(BigDecimal.ZERO);
202 }
203
204 earnCodeToEarnCodeSection.put(thd.getEarnCode(), earnCodeSection);
205 }
206 String assignKey = timeBlock.getAssignmentKey();
207 AssignmentRow assignRow = earnCodeSection.getAssignKeyToAssignmentRowMap().get(assignKey);
208 if(assignRow == null){
209 assignRow = new AssignmentRow();
210 assignRow.setAssignmentKey(assignKey);
211 AssignmentDescriptionKey assignmentKey = TkServiceLocator.getAssignmentService().getAssignmentDescriptionKey(assignKey);
212 Assignment assignment = TkServiceLocator.getAssignmentService().getAssignment(timeBlock.getPrincipalId(), assignmentKey, TKUtils.getTimelessDate(asOfDate));
213
214 if(assignment == null) {
215 assignment = TkServiceLocator.getAssignmentService().getAssignment(timeBlock.getPrincipalId(), assignmentKey, TKUtils.getTimelessDate(docEndDate));
216 }
217
218 if(assignment != null){
219 if(assignment.getJob() == null){
220 Job aJob = TkServiceLocator.getJobService().getJob(assignment.getPrincipalId(),assignment.getJobNumber(),TKUtils.getTimelessDate(assignment.getEffectiveDate()));
221 assignment.setJob(aJob);
222 }
223 if(assignment.getWorkAreaObj() == null){
224 WorkArea aWorkArea = TkServiceLocator.getWorkAreaService().getWorkArea(assignment.getWorkArea(), TKUtils.getTimelessDate(assignment.getEffectiveDate()));
225 assignment.setWorkAreaObj(aWorkArea);
226 }
227 assignRow.setDescr(assignment.getAssignmentDescription());
228 }
229 for(int i = 0;i<(numEntries-1);i++){
230 assignRow.getTotal().add(BigDecimal.ZERO);
231 assignRow.getAmount().add(BigDecimal.ZERO);
232 }
233 assignRow.setEarnCodeSection(earnCodeSection);
234 earnCodeSection.addAssignmentRow(assignRow);
235 }
236 assignRow.addToTotal(dayCount, thd.getHours());
237 assignRow.addToAmount(dayCount, thd.getAmount());
238 }
239 }
240 }
241 dayCount++;
242 weekSize++;
243 }
244
245 for(EarnCodeSection earnCodeSection : earnCodeToEarnCodeSection.values()){
246 earnCodeSection.addWeeklyTotal(dayCount, weekSize);
247 }
248 weekSize = 0;
249
250 dayCount++;
251 }
252
253 dayCount = 0;
254
255 for(EarnCodeSection earnCodeSection : earnCodeToEarnCodeSection.values()){
256 String earnCode = earnCodeSection.getEarnCode();
257 EarnCodeGroup earnGroupObj = TkServiceLocator.getEarnCodeGroupService().getEarnCodeGroupSummaryForEarnCode(earnCode, TKUtils.getTimelessDate(asOfDate));
258 String earnGroup = null;
259 if(earnGroupObj == null){
260 earnGroup = OTHER_EARN_GROUP;
261 } else{
262 earnGroup = earnGroupObj.getDescr();
263 }
264
265 EarnGroupSection earnGroupSection = earnGroupToEarnGroupSection.get(earnGroup);
266 if(earnGroupSection == null){
267 earnGroupSection = new EarnGroupSection();
268 earnGroupSection.setEarnGroup(earnGroup);
269 for(int i =0;i<(numEntries-1);i++){
270 earnGroupSection.getTotals().add(BigDecimal.ZERO);
271 }
272 earnGroupToEarnGroupSection.put(earnGroup, earnGroupSection);
273 }
274 earnGroupSection.addEarnCodeSection(earnCodeSection, dayArrangements);
275
276 }
277 for(EarnGroupSection earnGroupSection : earnGroupToEarnGroupSection.values()){
278 earnGroupSections.add(earnGroupSection);
279 }
280 return earnGroupSections;
281 }
282
283
284
285
286
287
288 protected List<String> getSummaryHeader(CalendarEntries payCalEntry){
289 List<String> summaryHeader = new ArrayList<String>();
290 int dayCount = 0;
291 Date beginDateTime = payCalEntry.getBeginPeriodDateTime();
292 Date endDateTime = payCalEntry.getEndPeriodDateTime();
293 boolean virtualDays = false;
294 LocalDateTime endDate = payCalEntry.getEndLocalDateTime();
295
296 if (endDate.get(DateTimeFieldType.hourOfDay()) != 0 || endDate.get(DateTimeFieldType.minuteOfHour()) != 0 ||
297 endDate.get(DateTimeFieldType.secondOfMinute()) != 0){
298 virtualDays = true;
299 }
300
301 Date currDateTime = beginDateTime;
302 java.util.Calendar cal = GregorianCalendar.getInstance();
303
304 while(currDateTime.before(endDateTime)){
305 LocalDateTime currDate = new LocalDateTime(currDateTime);
306 summaryHeader.add(makeHeaderDiplayString(currDate, virtualDays));
307
308 dayCount++;
309 if((dayCount % 7) == 0){
310 summaryHeader.add("Week "+ ((dayCount / 7)));
311 }
312 cal.setTime(currDateTime);
313 cal.add(java.util.Calendar.HOUR, 24);
314 currDateTime = cal.getTime();
315 }
316
317 summaryHeader.add("Period Total");
318 return summaryHeader;
319 }
320
321
322 private TkTimeBlockAggregate combineTimeAndLeaveAggregates(TkTimeBlockAggregate tbAggregate, LeaveBlockAggregate lbAggregate) {
323 if (tbAggregate != null
324 && lbAggregate != null
325 && tbAggregate.getDayTimeBlockList().size() == lbAggregate.getDayLeaveBlockList().size()) {
326 for (int i = 0; i < tbAggregate.getDayTimeBlockList().size(); i++) {
327 List<LeaveBlock> leaveBlocks = lbAggregate.getDayLeaveBlockList().get(i);
328 if (CollectionUtils.isNotEmpty(leaveBlocks)) {
329 for (LeaveBlock lb : leaveBlocks) {
330
331
332 TimeBlock timeBlock = new TimeBlock();
333 timeBlock.setHours(lb.getLeaveAmount().negate());
334 timeBlock.setBeginTimestamp(new Timestamp(lb.getLeaveDate().getTime()));
335 timeBlock.setEndTimestamp(new Timestamp(new DateTime(lb.getLeaveDate()).plusMinutes(timeBlock.getHours().intValue()).getMillis()));
336 timeBlock.setAssignmentKey(lb.getAssignmentKey());
337 timeBlock.setEarnCode(lb.getEarnCode());
338 timeBlock.setPrincipalId(lb.getPrincipalId());
339 timeBlock.setWorkArea(lb.getWorkArea());
340 TimeHourDetail timeHourDetail = new TimeHourDetail();
341 timeHourDetail.setEarnCode(timeBlock.getEarnCode());
342 timeHourDetail.setHours(timeBlock.getHours());
343 timeHourDetail.setAmount(BigDecimal.ZERO);
344 timeBlock.addTimeHourDetail(timeHourDetail);
345 tbAggregate.getDayTimeBlockList().get(i).add(timeBlock);
346 }
347 }
348
349 }
350 }
351 return tbAggregate;
352 }
353
354
355
356
357
358
359
360
361
362
363
364
365 private List<BigDecimal> getWorkedHours(TkTimeBlockAggregate aggregate) {
366 List<BigDecimal> hours = new ArrayList<BigDecimal>();
367 BigDecimal periodTotal = TkConstants.BIG_DECIMAL_SCALED_ZERO;
368 for (FlsaWeek week : aggregate.getFlsaWeeks(TkServiceLocator.getTimezoneService().getUserTimezoneWithFallback())) {
369 BigDecimal weeklyTotal = TkConstants.BIG_DECIMAL_SCALED_ZERO;
370 for (FlsaDay day : week.getFlsaDays()) {
371 BigDecimal totalForDay = TkConstants.BIG_DECIMAL_SCALED_ZERO;
372 for (TimeBlock block : day.getAppliedTimeBlocks()) {
373 totalForDay = totalForDay.add(block.getHours(), TkConstants.MATH_CONTEXT);
374 weeklyTotal = weeklyTotal.add(block.getHours(), TkConstants.MATH_CONTEXT);
375 periodTotal = periodTotal.add(block.getHours(), TkConstants.MATH_CONTEXT);
376 }
377 hours.add(totalForDay);
378 }
379 hours.add(weeklyTotal);
380 }
381 hours.add(periodTotal);
382
383 return hours;
384 }
385
386
387
388
389
390
391
392
393
394
395
396 @Override
397 public List<String> getHeaderForSummary(CalendarEntries cal, List<Boolean> dayArrangements) {
398 List<String> header = new ArrayList<String>();
399 if (cal == null) {
400 return Collections.emptyList();
401 }
402
403 int flsaBeginDay = this.getPayCalendarForEntry(cal).getFlsaBeginDayConstant();
404 boolean virtualDays = false;
405 LocalDateTime startDate = cal.getBeginLocalDateTime();
406 LocalDateTime endDate = cal.getEndLocalDateTime();
407
408
409
410
411 if (endDate.get(DateTimeFieldType.hourOfDay()) != 0 || endDate.get(DateTimeFieldType.minuteOfHour()) != 0 ||
412 endDate.get(DateTimeFieldType.secondOfMinute()) != 0)
413 {
414 endDate = endDate.plusDays(1);
415 virtualDays = true;
416 }
417
418 boolean afterFirstDay = false;
419 int week = 1;
420 for (LocalDateTime currentDate = startDate; currentDate.compareTo(endDate) < 0; currentDate = currentDate.plusDays(1)) {
421
422 if (currentDate.getDayOfWeek() == flsaBeginDay && afterFirstDay) {
423 header.add("Week " + week);
424 dayArrangements.add(false);
425 week++;
426 }
427
428 header.add(makeHeaderDiplayString(currentDate, virtualDays));
429 dayArrangements.add(true);
430
431
432 afterFirstDay = true;
433 }
434
435
436
437
438 if (!header.get(header.size()-1).startsWith("Week")) {
439 dayArrangements.add(false);
440 header.add("Week " + week);
441 }
442
443
444 header.add("Period Total");
445 dayArrangements.add(false);
446 return header;
447 }
448
449
450
451
452
453
454
455 private String makeHeaderDiplayString(LocalDateTime currentDate, boolean virtualDays) {
456 StringBuilder display = new StringBuilder();
457
458 display.append(currentDate.toString("E"));
459 if (virtualDays) {
460 LocalDateTime nextDate = currentDate.plusDays(1);
461 display.append(" - ");
462 display.append(nextDate.toString("E"));
463 }
464
465 display.append("<br />");
466
467 display.append(currentDate.toString(TkConstants.DT_ABBREV_DATE_FORMAT));
468 if (virtualDays) {
469 LocalDateTime nextDate = currentDate.plusDays(1);
470 display.append(" - ");
471 display.append(nextDate.toString(TkConstants.DT_ABBREV_DATE_FORMAT));
472 }
473
474 return display.toString();
475 }
476
477
478
479
480
481 private Calendar getPayCalendarForEntry(CalendarEntries calEntry) {
482 Calendar cal = null;
483
484 if (calEntry != null) {
485 cal = TkServiceLocator.getCalendarService().getCalendar(calEntry.getHrCalendarId());
486 }
487
488 return cal;
489 }
490
491 }