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 private Assignment getIdentifyingKey(TimeBlock block, Date asOfDate, String principalId) {
136 List<Assignment> lstAssign = TkServiceLocator.getAssignmentService().getAssignments(principalId, asOfDate);
137
138 for(Assignment assign : lstAssign){
139 if((assign.getJobNumber().compareTo(block.getJobNumber()) == 0) && (assign.getWorkArea().compareTo(block.getWorkArea()) == 0)){
140 return assign;
141 }
142 }
143 return null;
144 }
145
146
147 public void processDailyOvertimeRules(TimesheetDocument timesheetDocument, TkTimeBlockAggregate timeBlockAggregate){
148 Map<DailyOvertimeRule, List<Assignment>> mapDailyOvtRulesToAssignment = new HashMap<DailyOvertimeRule, List<Assignment>>();
149
150 for(Assignment assignment : timesheetDocument.getAssignments()) {
151 Job job = assignment.getJob();
152 DailyOvertimeRule dailyOvertimeRule = getDailyOvertimeRule(job.getLocation(), job.getHrPayType(), job.getDept(), assignment.getWorkArea(), timesheetDocument.getDocEndDate());
153
154 if(dailyOvertimeRule !=null) {
155 if(mapDailyOvtRulesToAssignment.containsKey(dailyOvertimeRule)){
156 List<Assignment> lstAssign = mapDailyOvtRulesToAssignment.get(dailyOvertimeRule);
157 lstAssign.add(assignment);
158 mapDailyOvtRulesToAssignment.put(dailyOvertimeRule, lstAssign);
159 } else {
160 List<Assignment> lstAssign = new ArrayList<Assignment>();
161 lstAssign.add(assignment);
162 mapDailyOvtRulesToAssignment.put(dailyOvertimeRule, lstAssign);
163 }
164 }
165 }
166
167
168 if(mapDailyOvtRulesToAssignment.isEmpty()){
169 return;
170 }
171
172
173 for(List<TimeBlock> dayTimeBlocks : timeBlockAggregate.getDayTimeBlockList()){
174
175 if (dayTimeBlocks.size() == 0)
176 continue;
177
178
179 Map<DailyOvertimeRule,List<TimeBlock>> dailyOvtRuleToDayTotals = new HashMap<DailyOvertimeRule,List<TimeBlock>>();
180 for(TimeBlock timeBlock : dayTimeBlocks) {
181 Assignment assign = this.getIdentifyingKey(timeBlock, timesheetDocument.getAsOfDate(), timesheetDocument.getPrincipalId());
182 for(Map.Entry<DailyOvertimeRule, List<Assignment>> entry : mapDailyOvtRulesToAssignment.entrySet()){
183 List<Assignment> lstAssign = entry.getValue();
184
185
186
187 if(lstAssign.contains(assign)){
188
189
190 if(dailyOvtRuleToDayTotals.get(entry.getKey()) != null){
191 List<TimeBlock> lstTimeBlock = dailyOvtRuleToDayTotals.get(entry.getKey());
192 lstTimeBlock.add(timeBlock);
193 dailyOvtRuleToDayTotals.put(entry.getKey(), lstTimeBlock);
194 } else {
195 List<TimeBlock> lstTimeBlock = new ArrayList<TimeBlock>();
196 lstTimeBlock.add(timeBlock);
197 dailyOvtRuleToDayTotals.put(entry.getKey(), lstTimeBlock);
198 }
199 }
200 }
201 }
202
203 for(DailyOvertimeRule dr : mapDailyOvtRulesToAssignment.keySet() ){
204 Set<String> fromEarnGroup = TkServiceLocator.getEarnCodeGroupService().getEarnCodeListForEarnCodeGroup(dr.getFromEarnGroup(), TKUtils.getTimelessDate(timesheetDocument.getCalendarEntry().getEndPeriodDateTime()));
205 List<TimeBlock> blocksForRule = dailyOvtRuleToDayTotals.get(dr);
206 if (blocksForRule == null || blocksForRule.size() == 0)
207 continue;
208 sortTimeBlocksNatural(blocksForRule);
209
210
211 BigDecimal hours = BigDecimal.ZERO;
212 List<TimeBlock> applicationList = new LinkedList<TimeBlock>();
213 TimeBlock previous = null;
214 for (TimeBlock block : blocksForRule) {
215 if (exceedsMaxGap(previous, block, dr.getMaxGap())) {
216 apply(hours, applicationList, dr, fromEarnGroup);
217 applicationList.clear();
218 hours = BigDecimal.ZERO;
219 previous = null;
220 } else {
221 previous = block;
222 }
223 applicationList.add(block);
224 for (TimeHourDetail thd : block.getTimeHourDetails())
225 if (fromEarnGroup.contains(thd.getEarnCode()))
226 hours = hours.add(thd.getHours(), TkConstants.MATH_CONTEXT);
227 }
228
229 apply(hours, applicationList, dr, fromEarnGroup);
230 }
231 }
232 }
233
234
235
236
237
238
239
240
241
242
243 private void apply(BigDecimal hours, List<TimeBlock> blocks, DailyOvertimeRule rule, Set<String> earnGroup) {
244 sortTimeBlocksInverse(blocks);
245 if (blocks != null && blocks.size() > 0)
246 if (hours.compareTo(rule.getMinHours()) >= 0) {
247 BigDecimal remaining = hours.subtract(rule.getMinHours(), TkConstants.MATH_CONTEXT);
248 for (TimeBlock block : blocks) {
249 remaining = applyOvertimeToTimeBlock(block, rule.getEarnCode(), earnGroup, remaining);
250 }
251 if (remaining.compareTo(BigDecimal.ZERO) > 0) {
252 LOG.warn("Hours remaining that were unapplied in DailyOvertimeRule.");
253 }
254 }
255 }
256
257
258
259
260
261
262
263
264
265
266
267
268
269 private BigDecimal applyOvertimeToTimeBlock(TimeBlock block, String otEarnCode, Set<String> convertFromEarnCodes, BigDecimal otHours) {
270 BigDecimal applied = BigDecimal.ZERO;
271
272 if (otHours.compareTo(BigDecimal.ZERO) <= 0)
273 return BigDecimal.ZERO;
274
275 List<TimeHourDetail> details = block.getTimeHourDetails();
276 List<TimeHourDetail> addDetails = new LinkedList<TimeHourDetail>();
277 for (TimeHourDetail detail : details) {
278 if (convertFromEarnCodes.contains(detail.getEarnCode())) {
279
280 BigDecimal n = detail.getHours().subtract(otHours, TkConstants.MATH_CONTEXT);
281
282
283 if (n.compareTo(BigDecimal.ZERO) >= 0) {
284
285 applied = otHours;
286 } else {
287 applied = detail.getHours();
288 }
289
290
291 TimeHourDetail timeHourDetail = new TimeHourDetail();
292 timeHourDetail.setHours(applied);
293 timeHourDetail.setEarnCode(otEarnCode);
294 timeHourDetail.setTkTimeBlockId(block.getTkTimeBlockId());
295
296
297 detail.setHours(detail.getHours().subtract(applied, TkConstants.MATH_CONTEXT));
298 addDetails.add(timeHourDetail);
299 }
300 }
301
302 if (addDetails.size() > 0) {
303 details.addAll(addDetails);
304 block.setTimeHourDetails(details);
305 }
306
307 return otHours.subtract(applied);
308 }
309
310
311
312
313 private void sortTimeBlocksInverse(List<TimeBlock> blocks) {
314 Collections.sort(blocks, new Comparator<TimeBlock>() {
315 public int compare(TimeBlock tb1, TimeBlock tb2) {
316 if (tb1 != null && tb2 != null)
317 return -1 * tb1.getBeginTimestamp().compareTo(tb2.getBeginTimestamp());
318 return 0;
319 }
320 });
321 }
322
323
324 private void sortTimeBlocksNatural(List<TimeBlock> blocks) {
325 Collections.sort(blocks, new Comparator<TimeBlock>() {
326 public int compare(TimeBlock tb1, TimeBlock tb2) {
327 if (tb1 != null && tb2 != null)
328 return tb1.getBeginTimestamp().compareTo(tb2.getBeginTimestamp());
329 return 0;
330 }
331 });
332 }
333
334
335
336
337
338
339
340
341
342
343 boolean exceedsMaxGap(TimeBlock previous, TimeBlock current, BigDecimal maxGap) {
344 if (previous == null)
345 return false;
346
347 long difference = current.getBeginTimestamp().getTime() - previous.getEndTimestamp().getTime();
348 BigDecimal gapHours = TKUtils.convertMillisToHours(difference);
349 BigDecimal cmpGapHrs = TKUtils.convertMinutesToHours(maxGap);
350 return (gapHours.compareTo(cmpGapHrs) > 0);
351 }
352
353 @Override
354 public DailyOvertimeRule getDailyOvertimeRule(String tkDailyOvertimeRuleId) {
355 return dailyOvertimeRuleDao.getDailyOvertimeRule(tkDailyOvertimeRuleId);
356 }
357 }