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