1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.kuali.hr.time.overtime.weekly.rule.service;
17
18 import org.apache.commons.lang.StringUtils;
19 import org.joda.time.DateTimeZone;
20 import org.kuali.hr.time.calendar.CalendarEntries;
21 import org.kuali.hr.time.earncode.EarnCode;
22 import org.kuali.hr.time.flsa.FlsaDay;
23 import org.kuali.hr.time.flsa.FlsaWeek;
24 import org.kuali.hr.time.overtime.weekly.rule.WeeklyOvertimeRule;
25 import org.kuali.hr.time.overtime.weekly.rule.dao.WeeklyOvertimeRuleDao;
26 import org.kuali.hr.time.service.base.TkServiceLocator;
27 import org.kuali.hr.time.timeblock.TimeBlock;
28 import org.kuali.hr.time.timeblock.TimeHourDetail;
29 import org.kuali.hr.time.timesheet.TimesheetDocument;
30 import org.kuali.hr.time.util.TKUtils;
31 import org.kuali.hr.time.util.TkConstants;
32 import org.kuali.hr.time.util.TkTimeBlockAggregate;
33 import org.kuali.hr.time.workarea.WorkArea;
34 import org.kuali.hr.time.workflow.TimesheetDocumentHeader;
35
36 import java.math.BigDecimal;
37 import java.sql.Date;
38 import java.util.*;
39
40 public class WeeklyOvertimeRuleServiceImpl implements WeeklyOvertimeRuleService {
41
42 private WeeklyOvertimeRuleDao weeklyOvertimeRuleDao;
43
44 @Override
45 public void processWeeklyOvertimeRule(TimesheetDocument timesheetDocument, TkTimeBlockAggregate aggregate) {
46 DateTimeZone zone = TkServiceLocator.getTimezoneService().getUserTimezoneWithFallback();
47 java.sql.Date asOfDate = TKUtils.getTimelessDate(timesheetDocument.getDocumentHeader().getPayEndDate());
48 String principalId = timesheetDocument.getDocumentHeader().getPrincipalId();
49 List<WeeklyOvertimeRule> weeklyOvertimeRules = this.getWeeklyOvertimeRules(asOfDate);
50
51
52 List<FlsaWeek> flsaWeeks = aggregate.getFlsaWeeks(zone);
53 List<FlsaWeek> previousWeeks = null;
54
55 if (flsaWeeks.size() == 0) {
56 return;
57 }
58
59 FlsaWeek firstWeek = flsaWeeks.get(0);
60
61
62 if (!firstWeek.isFirstWeekFull()) {
63 List<TimeBlock> prevBlocks = TkServiceLocator.getTimesheetService().getPrevDocumentTimeBlocks(principalId, timesheetDocument.getDocumentHeader().getPayBeginDate());
64 if (prevBlocks.size() > 0) {
65 TimesheetDocumentHeader prevTdh = TkServiceLocator.getTimesheetDocumentHeaderService().getPreviousDocumentHeader(principalId, timesheetDocument.getDocumentHeader().getPayBeginDate());
66 if (prevTdh != null) {
67 CalendarEntries prevPayCalendarEntry = TkServiceLocator.getCalendarService().getCalendarDatesByPayEndDate(principalId, prevTdh.getPayEndDate(), null);
68 TkTimeBlockAggregate prevTimeAggregate = new TkTimeBlockAggregate(prevBlocks, prevPayCalendarEntry, prevPayCalendarEntry.getCalendarObj(), true);
69 previousWeeks = prevTimeAggregate.getFlsaWeeks(zone);
70 if (previousWeeks.size() == 0) {
71 previousWeeks = null;
72 }
73 }
74 }
75 }
76
77
78 for (WeeklyOvertimeRule wor : weeklyOvertimeRules) {
79
80 Set<String> maxHoursEarnCodes = TkServiceLocator.getEarnGroupService().getEarnCodeListForEarnGroup(wor.getMaxHoursEarnGroup(), asOfDate);
81 Set<String> convertFromEarnCodes = TkServiceLocator.getEarnGroupService().getEarnCodeListForEarnGroup(wor.getConvertFromEarnGroup(), asOfDate);
82
83
84
85
86 for (int i = 0; i < flsaWeeks.size(); i++) {
87 BigDecimal maxHoursSum = BigDecimal.ZERO;
88 BigDecimal overtimeHours = BigDecimal.ZERO;
89
90
91
92
93 if (i == 0 && previousWeeks != null) {
94 FlsaWeek previousLastWeek = previousWeeks.get(previousWeeks.size() - 1);
95
96
97 for (FlsaDay day : previousLastWeek.getFlsaDays()) {
98
99 for (String ec : day.getEarnCodeToHours().keySet()) {
100 if (maxHoursEarnCodes.contains(ec)) {
101 maxHoursSum = maxHoursSum.add(day.getEarnCodeToHours().get(ec), TkConstants.MATH_CONTEXT);
102 }
103 }
104 }
105 }
106
107
108 FlsaWeek currentWeek = flsaWeeks.get(i);
109 for (FlsaDay day : currentWeek.getFlsaDays()) {
110 for (String ec : day.getEarnCodeToHours().keySet()) {
111 if (maxHoursEarnCodes.contains(ec)) {
112 maxHoursSum = maxHoursSum.add(day.getEarnCodeToHours().get(ec), TkConstants.MATH_CONTEXT);
113 }
114 }
115 }
116
117
118 overtimeHours = maxHoursSum.subtract(wor.getMaxHours(), TkConstants.MATH_CONTEXT);
119 if (overtimeHours.compareTo(BigDecimal.ZERO) <= 0) {
120
121 continue;
122 }
123
124
125 List<FlsaDay> daysOfCurrentWeek = currentWeek.getFlsaDays();
126 if (daysOfCurrentWeek.size() > 0) {
127 for (int j=daysOfCurrentWeek.size()-1; j >= 0; j--) {
128 FlsaDay day = daysOfCurrentWeek.get(j);
129 boolean otApplied = false;
130
131 List<TimeBlock> dayBlocks = day.getAppliedTimeBlocks();
132 Collections.sort(dayBlocks, new Comparator<TimeBlock>() {
133 public int compare(TimeBlock tb1, TimeBlock tb2) {
134 if (tb1 != null && tb2 != null)
135 return -1*tb1.getBeginTimestamp().compareTo(tb2.getBeginTimestamp());
136 return 0;
137 }
138 });
139
140
141 for (TimeBlock block : dayBlocks) {
142 if (overtimeHours.compareTo(BigDecimal.ZERO) > 0) {
143 String overtimeEarnCode = getOvertimeEarnCode(principalId, block, wor, asOfDate);
144 overtimeHours = applyOvertimeToTimeBlock(block, overtimeEarnCode, convertFromEarnCodes, overtimeHours);
145 otApplied = true;
146 }
147 }
148
149
150 if (otApplied)
151 day.remapTimeHourDetails();
152 }
153 }
154 }
155 }
156 }
157
158
159
160
161
162
163
164
165
166
167 protected String getOvertimeEarnCode(String principalId, TimeBlock block, WeeklyOvertimeRule wor, Date asOfDate) {
168
169 if(StringUtils.isNotEmpty(block.getOvertimePref())) {
170 return block.getOvertimePref();
171 }
172 WorkArea workArea = TkServiceLocator.getWorkAreaService().getWorkArea(block.getWorkArea(), asOfDate);
173 if(StringUtils.isNotBlank(workArea.getDefaultOvertimeEarnCode())){
174 return workArea.getDefaultOvertimeEarnCode();
175 }
176 return wor.getConvertToEarnCode();
177 }
178
179
180
181
182
183
184
185
186
187
188
189
190 protected BigDecimal applyOvertimeToTimeBlock(TimeBlock block, String otEarnCode, Set<String> convertFromEarnCodes, BigDecimal otHours) {
191 BigDecimal applied = BigDecimal.ZERO;
192 List<TimeHourDetail> details = block.getTimeHourDetails();
193 List<TimeHourDetail> addDetails = new LinkedList<TimeHourDetail>();
194 for (TimeHourDetail detail : details) {
195
196 String thdEarnCode = detail.getEarnCode();
197 if (convertFromEarnCodes.contains(thdEarnCode)) {
198
199 BigDecimal n = detail.getHours().subtract(otHours, TkConstants.MATH_CONTEXT);
200
201
202 if (n.compareTo(BigDecimal.ZERO) >= 0) {
203
204 applied = otHours;
205 } else {
206 applied = detail.getHours();
207 }
208
209
210 TimeHourDetail timeHourDetail = new TimeHourDetail();
211
212
213 EarnCode earnCodeObj = TkServiceLocator.getEarnCodeService().getEarnCode(otEarnCode, block.getEndDate());
214 BigDecimal hrs = earnCodeObj.getInflateFactor().multiply(applied, TkConstants.MATH_CONTEXT).setScale(TkConstants.BIG_DECIMAL_SCALE,BigDecimal.ROUND_HALF_UP);
215 timeHourDetail.setEarnCode(otEarnCode);
216 timeHourDetail.setHours(hrs);
217 timeHourDetail.setTkTimeBlockId(block.getTkTimeBlockId());
218
219
220 detail.setHours(detail.getHours().subtract(applied, TkConstants.MATH_CONTEXT).setScale(TkConstants.BIG_DECIMAL_SCALE,BigDecimal.ROUND_HALF_UP));
221 addDetails.add(timeHourDetail);
222 }
223 }
224
225
226
227
228 if (addDetails.size() > 0) {
229 details.addAll(addDetails);
230 block.setTimeHourDetails(details);
231 }
232
233 return otHours.subtract(applied);
234 }
235
236 @Override
237 public List<WeeklyOvertimeRule> getWeeklyOvertimeRules(Date asOfDate) {
238 return weeklyOvertimeRuleDao.findWeeklyOvertimeRules(asOfDate);
239 }
240
241 @Override
242 public void saveOrUpdate(WeeklyOvertimeRule weeklyOvertimeRule) {
243 weeklyOvertimeRuleDao.saveOrUpdate(weeklyOvertimeRule);
244 }
245
246 @Override
247 public void saveOrUpdate(List<WeeklyOvertimeRule> weeklyOvertimeRules) {
248 weeklyOvertimeRuleDao.saveOrUpdate(weeklyOvertimeRules);
249 }
250
251 public void setWeeklyOvertimeRuleDao(WeeklyOvertimeRuleDao weeklyOvertimeRuleDao) {
252 this.weeklyOvertimeRuleDao = weeklyOvertimeRuleDao;
253 }
254
255
256 @Override
257 public WeeklyOvertimeRule getWeeklyOvertimeRule(String tkWeeklyOvertimeRuleId) {
258 return weeklyOvertimeRuleDao.getWeeklyOvertimeRule(tkWeeklyOvertimeRuleId);
259 }
260
261 }