1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.kuali.hr.time.overtime.daily.rule.service;
17
18 import org.apache.log4j.Logger;
19 import org.kuali.hr.job.Job;
20 import org.kuali.hr.time.assignment.Assignment;
21 import org.kuali.hr.time.overtime.daily.rule.DailyOvertimeRule;
22 import org.kuali.hr.time.overtime.daily.rule.dao.DailyOvertimeRuleDao;
23 import org.kuali.hr.time.service.base.TkServiceLocator;
24 import org.kuali.hr.time.timeblock.TimeBlock;
25 import org.kuali.hr.time.timeblock.TimeHourDetail;
26 import org.kuali.hr.time.timesheet.TimesheetDocument;
27 import org.kuali.hr.time.util.TKUtils;
28 import org.kuali.hr.time.util.TkConstants;
29 import org.kuali.hr.time.util.TkTimeBlockAggregate;
30
31 import java.math.BigDecimal;
32 import java.sql.Date;
33 import java.util.*;
34
35 public class DailyOvertimeRuleServiceImpl implements DailyOvertimeRuleService {
36
37 private static final Logger LOG = Logger.getLogger(DailyOvertimeRuleServiceImpl.class);
38
39 private DailyOvertimeRuleDao dailyOvertimeRuleDao = null;
40
41 public void saveOrUpdate(DailyOvertimeRule dailyOvertimeRule) {
42 dailyOvertimeRuleDao.saveOrUpdate(dailyOvertimeRule);
43 }
44
45 public void saveOrUpdate(List<DailyOvertimeRule> dailyOvertimeRules) {
46 dailyOvertimeRuleDao.saveOrUpdate(dailyOvertimeRules);
47 }
48
49 @Override
50
51
52
53
54
55
56
57
58
59 public DailyOvertimeRule getDailyOvertimeRule(String location, String paytype, String dept, Long workArea, Date asOfDate) {
60 DailyOvertimeRule dailyOvertimeRule = null;
61
62
63 if (dailyOvertimeRule == null)
64 dailyOvertimeRule = dailyOvertimeRuleDao.findDailyOvertimeRule(location, paytype, dept, workArea, asOfDate);
65
66
67 if (dailyOvertimeRule == null)
68 dailyOvertimeRule = dailyOvertimeRuleDao.findDailyOvertimeRule(location, paytype, dept, -1L, asOfDate);
69
70
71 if (dailyOvertimeRule == null)
72 dailyOvertimeRule = dailyOvertimeRuleDao.findDailyOvertimeRule(location, paytype, "%", workArea, asOfDate);
73
74
75 if (dailyOvertimeRule == null)
76 dailyOvertimeRule = dailyOvertimeRuleDao.findDailyOvertimeRule(location, paytype, "%", -1L, asOfDate);
77
78
79 if (dailyOvertimeRule == null)
80 dailyOvertimeRule = dailyOvertimeRuleDao.findDailyOvertimeRule(location, "%", dept, workArea, asOfDate);
81
82
83 if (dailyOvertimeRule == null)
84 dailyOvertimeRule = dailyOvertimeRuleDao.findDailyOvertimeRule(location, "%", dept, -1L, asOfDate);
85
86
87 if (dailyOvertimeRule == null)
88 dailyOvertimeRule = dailyOvertimeRuleDao.findDailyOvertimeRule(location, "%", "%", workArea, asOfDate);
89
90
91 if (dailyOvertimeRule == null)
92 dailyOvertimeRule = dailyOvertimeRuleDao.findDailyOvertimeRule(location, "%", "%", -1L, asOfDate);
93
94
95 if (dailyOvertimeRule == null)
96 dailyOvertimeRule = dailyOvertimeRuleDao.findDailyOvertimeRule("%", paytype, dept, workArea, asOfDate);
97
98
99 if (dailyOvertimeRule == null)
100 dailyOvertimeRule = dailyOvertimeRuleDao.findDailyOvertimeRule("%", paytype, dept, -1L, asOfDate);
101
102
103 if (dailyOvertimeRule == null)
104 dailyOvertimeRule = dailyOvertimeRuleDao.findDailyOvertimeRule("%", paytype, "%", workArea, asOfDate);
105
106
107 if (dailyOvertimeRule == null)
108 dailyOvertimeRule = dailyOvertimeRuleDao.findDailyOvertimeRule("%", paytype, "%", -1L, asOfDate);
109
110
111 if (dailyOvertimeRule == null)
112 dailyOvertimeRule = dailyOvertimeRuleDao.findDailyOvertimeRule("%", "%", dept, workArea, asOfDate);
113
114
115 if (dailyOvertimeRule == null)
116 dailyOvertimeRule = dailyOvertimeRuleDao.findDailyOvertimeRule("%", "%", dept, -1L, asOfDate);
117
118
119 if (dailyOvertimeRule == null)
120 dailyOvertimeRule = dailyOvertimeRuleDao.findDailyOvertimeRule("%", "%", "%", workArea, asOfDate);
121
122
123 if (dailyOvertimeRule == null)
124 dailyOvertimeRule = dailyOvertimeRuleDao.findDailyOvertimeRule("%", "%", "%", -1L, asOfDate);
125
126 return dailyOvertimeRule;
127 }
128
129
130
131 public void setDailyOvertimeRuleDao(DailyOvertimeRuleDao dailyOvertimeRuleDao) {
132 this.dailyOvertimeRuleDao = dailyOvertimeRuleDao;
133 }
134
135
136
137
138
139
140 private String getIdentifyingKey(Assignment assignment) {
141 StringBuffer keybuf = new StringBuffer();
142 Job job = assignment.getJob();
143
144 keybuf.append(job.getLocation()).append('_');
145 keybuf.append(job.getHrPayType()).append('_');
146 keybuf.append(job.getDept()).append('_');
147 keybuf.append(assignment.getWorkArea());
148
149 return keybuf.toString();
150 }
151
152 private Assignment getIdentifyingKey(TimeBlock block, Date asOfDate, String principalId) {
153 List<Assignment> lstAssign = TkServiceLocator.getAssignmentService().getAssignments(principalId, asOfDate);
154
155 for(Assignment assign : lstAssign){
156 if((assign.getJobNumber().compareTo(block.getJobNumber()) == 0) && (assign.getWorkArea().compareTo(block.getWorkArea()) == 0)){
157 return assign;
158 }
159 }
160 return null;
161 }
162
163
164 public void processDailyOvertimeRules(TimesheetDocument timesheetDocument, TkTimeBlockAggregate timeBlockAggregate){
165 Map<DailyOvertimeRule, List<Assignment>> mapDailyOvtRulesToAssignment = new HashMap<DailyOvertimeRule, List<Assignment>>();
166
167 for(Assignment assignment : timesheetDocument.getAssignments()) {
168 Job job = assignment.getJob();
169 DailyOvertimeRule dailyOvertimeRule = getDailyOvertimeRule(job.getLocation(), job.getHrPayType(), job.getDept(), assignment.getWorkArea(), timesheetDocument.getAsOfDate());
170
171 if(dailyOvertimeRule !=null) {
172 if(mapDailyOvtRulesToAssignment.containsKey(dailyOvertimeRule)){
173 List<Assignment> lstAssign = mapDailyOvtRulesToAssignment.get(dailyOvertimeRule);
174 lstAssign.add(assignment);
175 mapDailyOvtRulesToAssignment.put(dailyOvertimeRule, lstAssign);
176 } else {
177 List<Assignment> lstAssign = new ArrayList<Assignment>();
178 lstAssign.add(assignment);
179 mapDailyOvtRulesToAssignment.put(dailyOvertimeRule, lstAssign);
180 }
181 }
182 }
183
184
185 if(mapDailyOvtRulesToAssignment.isEmpty()){
186 return;
187 }
188
189
190 for(List<TimeBlock> dayTimeBlocks : timeBlockAggregate.getDayTimeBlockList()){
191
192 if (dayTimeBlocks.size() == 0)
193 continue;
194
195
196 Map<DailyOvertimeRule,List<TimeBlock>> dailyOvtRuleToDayTotals = new HashMap<DailyOvertimeRule,List<TimeBlock>>();
197 for(TimeBlock timeBlock : dayTimeBlocks) {
198 Assignment assign = this.getIdentifyingKey(timeBlock, timesheetDocument.getAsOfDate(), timesheetDocument.getPrincipalId());
199 for(DailyOvertimeRule dr : mapDailyOvtRulesToAssignment.keySet()){
200 List<Assignment> lstAssign = mapDailyOvtRulesToAssignment.get(dr);
201
202
203
204 if(lstAssign.contains(assign)){
205
206
207 if(dailyOvtRuleToDayTotals.get(dr) != null){
208 List<TimeBlock> lstTimeBlock = dailyOvtRuleToDayTotals.get(dr);
209 lstTimeBlock.add(timeBlock);
210 dailyOvtRuleToDayTotals.put(dr, lstTimeBlock);
211 } else {
212 List<TimeBlock> lstTimeBlock = new ArrayList<TimeBlock>();
213 lstTimeBlock.add(timeBlock);
214 dailyOvtRuleToDayTotals.put(dr, lstTimeBlock);
215 }
216 }
217 }
218 }
219
220 for(DailyOvertimeRule dr : mapDailyOvtRulesToAssignment.keySet() ){
221 Set<String> fromEarnGroup = TkServiceLocator.getEarnGroupService().getEarnCodeListForEarnGroup(dr.getFromEarnGroup(), TKUtils.getTimelessDate(timesheetDocument.getPayCalendarEntry().getEndPeriodDateTime()));
222 List<TimeBlock> blocksForRule = dailyOvtRuleToDayTotals.get(dr);
223 if (blocksForRule == null || blocksForRule.size() == 0)
224 continue;
225 sortTimeBlocksNatural(blocksForRule);
226
227
228 BigDecimal hours = BigDecimal.ZERO;
229 List<TimeBlock> applicationList = new LinkedList<TimeBlock>();
230 TimeBlock previous = null;
231 for (TimeBlock block : blocksForRule) {
232 if (exceedsMaxGap(previous, block, dr.getMaxGap())) {
233 apply(hours, applicationList, dr, fromEarnGroup);
234 applicationList.clear();
235 hours = BigDecimal.ZERO;
236 previous = null;
237 } else {
238 previous = block;
239 }
240 applicationList.add(block);
241 for (TimeHourDetail thd : block.getTimeHourDetails())
242 if (fromEarnGroup.contains(thd.getEarnCode()))
243 hours = hours.add(thd.getHours(), TkConstants.MATH_CONTEXT);
244 }
245
246 apply(hours, applicationList, dr, fromEarnGroup);
247 }
248 }
249 }
250
251
252
253
254
255
256
257
258
259
260 private void apply(BigDecimal hours, List<TimeBlock> blocks, DailyOvertimeRule rule, Set<String> earnGroup) {
261 sortTimeBlocksInverse(blocks);
262 if (blocks != null && blocks.size() > 0)
263 if (hours.compareTo(rule.getMinHours()) >= 0) {
264 BigDecimal remaining = hours.subtract(rule.getMinHours(), TkConstants.MATH_CONTEXT);
265 for (TimeBlock block : blocks) {
266 remaining = applyOvertimeToTimeBlock(block, rule.getEarnCode(), earnGroup, remaining);
267 }
268 if (remaining.compareTo(BigDecimal.ZERO) > 0) {
269 LOG.warn("Hours remaining that were unapplied in DailyOvertimeRule.");
270 }
271 }
272 }
273
274
275
276
277
278
279
280
281
282
283
284
285
286 private BigDecimal applyOvertimeToTimeBlock(TimeBlock block, String otEarnCode, Set<String> convertFromEarnCodes, BigDecimal otHours) {
287 BigDecimal applied = BigDecimal.ZERO;
288
289 if (otHours.compareTo(BigDecimal.ZERO) <= 0)
290 return BigDecimal.ZERO;
291
292 List<TimeHourDetail> details = block.getTimeHourDetails();
293 List<TimeHourDetail> addDetails = new LinkedList<TimeHourDetail>();
294 for (TimeHourDetail detail : details) {
295 if (convertFromEarnCodes.contains(detail.getEarnCode())) {
296
297 BigDecimal n = detail.getHours().subtract(otHours, TkConstants.MATH_CONTEXT);
298
299
300 if (n.compareTo(BigDecimal.ZERO) >= 0) {
301
302 applied = otHours;
303 } else {
304 applied = detail.getHours();
305 }
306
307
308 TimeHourDetail timeHourDetail = new TimeHourDetail();
309 timeHourDetail.setHours(applied);
310 timeHourDetail.setEarnCode(otEarnCode);
311 timeHourDetail.setTkTimeBlockId(block.getTkTimeBlockId());
312
313
314 detail.setHours(detail.getHours().subtract(applied, TkConstants.MATH_CONTEXT));
315 addDetails.add(timeHourDetail);
316 }
317 }
318
319 if (addDetails.size() > 0) {
320 details.addAll(addDetails);
321 block.setTimeHourDetails(details);
322 }
323
324 return otHours.subtract(applied);
325 }
326
327
328
329
330 private void sortTimeBlocksInverse(List<TimeBlock> blocks) {
331 Collections.sort(blocks, new Comparator<TimeBlock>() {
332 public int compare(TimeBlock tb1, TimeBlock tb2) {
333 if (tb1 != null && tb2 != null)
334 return -1 * tb1.getBeginTimestamp().compareTo(tb2.getBeginTimestamp());
335 return 0;
336 }
337 });
338 }
339
340
341 private void sortTimeBlocksNatural(List<TimeBlock> blocks) {
342 Collections.sort(blocks, new Comparator<TimeBlock>() {
343 public int compare(TimeBlock tb1, TimeBlock tb2) {
344 if (tb1 != null && tb2 != null)
345 return tb1.getBeginTimestamp().compareTo(tb2.getBeginTimestamp());
346 return 0;
347 }
348 });
349 }
350
351
352
353
354
355
356
357
358
359
360 boolean exceedsMaxGap(TimeBlock previous, TimeBlock current, BigDecimal maxGap) {
361 if (previous == null)
362 return false;
363
364 long difference = current.getBeginTimestamp().getTime() - previous.getEndTimestamp().getTime();
365 BigDecimal gapHours = TKUtils.convertMillisToHours(difference);
366 BigDecimal cmpGapHrs = TKUtils.convertMinutesToHours(maxGap);
367 return (gapHours.compareTo(cmpGapHrs) > 0);
368 }
369
370 @Override
371 public DailyOvertimeRule getDailyOvertimeRule(String tkDailyOvertimeRuleId) {
372 return dailyOvertimeRuleDao.getDailyOvertimeRule(tkDailyOvertimeRuleId);
373 }
374 }