1 PACKAGE BODY WIP_WS_LABOR_METRIC_PUB AS
2 /* $Header: wipwslmb.pls 120.9.12020000.7 2013/02/12 15:20:19 sisankar ship $ */
3
4 /* To Get Calendar Code for an organization */
5 function get_calendar_code(p_organization_id number)
6 return varchar2 is
7 l_calendar_code varchar2(100) := null;
8 begin
9 select mp.calendar_code
10 into l_calendar_code
11 from mtl_parameters mp
12 where mp.organization_id = p_organization_id;
13 return l_calendar_code;
14 exception
15 when others then return null;
16 end get_calendar_code;
17
18 /* To validate whether the UOM is time based */
19 function is_time_uom(p_uom_code in varchar2)
20 return number is
21 l_uom_class varchar2(10);
22 l_time_based_uom_flag number;
23
24 cursor time_based_uom_cursor is
25 select distinct muc1.uom_class
26 from mtl_uom_conversions muc1,
27 mtl_uom_conversions muc2
28 where (muc1.uom_class = muc2.uom_class
29 and nvl(muc1.disable_date, sysdate + 1) > sysdate)
30 and nvl(muc2.disable_date, sysdate + 1) > sysdate
31 and muc1.uom_code = fnd_profile.value('BOM:HOUR_UOM_CODE')
32 and muc2.uom_code = p_uom_code;
33 begin
34 open time_based_uom_cursor;
35 fetch time_based_uom_cursor into l_uom_class;
36 if time_based_uom_cursor%notfound then
37 l_time_based_uom_flag := 2;
38 else
39 l_time_based_uom_flag := 1;
40 end if;
41 close time_based_uom_cursor;
42 return l_time_based_uom_flag;
43 end is_time_uom;
44
45 /* To obtain end of the time window for metrics calculation */
46 function get_period_end(p_calendar_code in varchar2,
47 p_date in date)
48 return date is
49 l_end_time Date := null;
50 l_temp_time Date;
51 begin
52
53 l_temp_time := p_date;
54 select
55 max(bsd.shift_date + st.to_time/(60*60*24))
56 into l_end_time
57 from
58 bom_shift_dates bsd,
59 (select bst.shift_num,
60 min(bst.from_time) from_time,
61 max(decode(sign(bst.to_time - bst.from_time), -1, 24*60*60, 0) + bst.to_time) to_time
62 from bom_shift_times bst
63 where bst.calendar_code = p_calendar_code
64 group by bst.shift_num) st
65 where bsd.calendar_code = p_calendar_code
66 and bsd.shift_num = st.shift_num
67 and bsd.exception_set_id = -1
68 and bsd.seq_num is not null
69 and bsd.shift_date = trunc(p_date)
70 and bsd.shift_date + st.to_time/(60*60*24) <= l_temp_time
71 and bsd.shift_date + st.from_time/(60*60*24) < l_temp_time;
72
73 while l_end_time is null loop
74
75 l_temp_time := l_temp_time-1;
76
77 select
78 max(bsd.shift_date + st.to_time/(60*60*24))
79 into l_end_time
80 from
81 bom_shift_dates bsd,
82 (select bst.shift_num,
83 max(decode(sign(bst.to_time - bst.from_time), -1, 24*60*60, 0) + bst.to_time) to_time
84 from bom_shift_times bst
85 where bst.calendar_code = p_calendar_code
86 group by bst.shift_num) st
87 where bsd.calendar_code = p_calendar_code
88 and bsd.shift_num = st.shift_num
89 and bsd.exception_set_id = -1
90 and bsd.seq_num is not null
91 and bsd.shift_date = trunc(l_temp_time);
92
93 end loop;
94
95 return l_end_time;
96
97 end get_period_end;
98
99 /* To obtain start of the time window for metrics calculation */
100 function get_period_start(p_org_id in number,
101 p_date in date)
102 return date
103 is
104 l_start_time Date;
105 begin
106 l_start_time := trunc(p_date)-6;
107 /* Commented this code since calculation is for last 7 calendar days and not last 7 working days.
108 for i in 1..6 loop
109 l_start_time := mrp_calendar.PREV_WORK_DAY(p_org_id,1,l_start_time-1);
110 end loop;*/
111 return l_start_time;
112 end get_period_start;
113
114 /* To obtain date for a given seq_num and shift_num (Used in Charts only) */
115 function get_date_for_seq(p_org_id in number,
116 p_seq_num in number,
117 p_shift_num in number)
118 return date
119 is
120 l_eff_date Date;
121 l_converted_date date;
122 l_calendar_code varchar2(50);
123 l_timezone_enabled boolean := ( fnd_profile.value('ENABLE_TIMEZONE_CONVERSIONS') = 'Y' AND
124 fnd_profile.value('CLIENT_TIMEZONE_ID') IS NOT NULL AND
125 fnd_profile.value('SERVER_TIMEZONE_ID') IS NOT NULL AND
126 fnd_profile.value('CLIENT_TIMEZONE_ID') <>
127 fnd_profile.value('SERVER_TIMEZONE_ID'));
128 l_client_id number := fnd_profile.value('CLIENT_TIMEZONE_ID');
129 l_server_id number := fnd_profile.value('SERVER_TIMEZONE_ID');
130 begin
131 l_calendar_code := get_calendar_code(p_org_id);
132 begin
133 --Fix for bug 11847040, must consider the time component too so that the date will be properly converted
134 select shift_date + at.from_time /(24*60*60)
135 into l_eff_date
136 from bom_shift_dates,
137 (select min(bst.from_time) from_time
138 from bom_shift_times bst
139 where bst.calendar_code = l_calendar_code
140 and bst.shift_num = p_shift_num
141 ) at
142 where calendar_code = l_calendar_code
143 and exception_set_id = -1
144 and seq_num = p_seq_num
145 and shift_num =p_shift_num;
146 exception
147 when no_data_found
148 then l_eff_date := null;
149 end;
150
151 if l_timezone_enabled and l_eff_date is not null then
152 l_converted_date := trunc(hz_timezone_pub.convert_datetime(l_server_id,
153 l_client_id,
154 l_eff_date));
155 else
156 l_converted_date := trunc(l_eff_date);
157 end if;
158
159 return l_converted_date;
160 end get_date_for_seq;
161
162 /* To obtain date for a given seq_num and shift_num */
163 function get_date(p_org_id in number,
164 p_seq_num in number,
165 p_shift_num in number)
166 return date
167 is
168 l_eff_date Date;
169 l_calendar_code varchar2(50);
170 begin
171 l_calendar_code := get_calendar_code(p_org_id);
172 begin
173 select shift_date
174 into l_eff_date
175 from bom_shift_dates
176 where calendar_code = l_calendar_code
177 and exception_set_id = -1
178 and seq_num = p_seq_num
179 and shift_num =p_shift_num;
180 return l_eff_date;
181 exception
182 when no_data_found then return null;
183 end;
184 end get_date;
185
186 function get_index_for_date(p_date in date,
187 p_organization_id in number,
188 p_dept_id in number,
189 p_resource_id in number)
190 return varchar2
191 is
192 l_date_seq number;
193 l_shift_num number;
194 l_shift_start_date date;
195 l_shift_end_date date;
196 l_calendar_code varchar2(100);
197 begin
198
199 l_calendar_code := get_calendar_code(p_organization_id);
200 wip_ws_dl_util.get_first_dept_resource_shift
201 (p_cal_code =>l_calendar_code,
202 p_dept_id =>p_dept_id,
203 p_resource_id =>p_resource_id,
204 p_date =>p_date,
205 x_shift_seq =>l_date_seq,
206 x_shift_num =>l_shift_num,
207 x_shift_start_date =>l_shift_start_date,
208 x_shift_end_date =>l_shift_end_date);
209
210 return to_char(l_date_seq||':'||l_shift_num);
211 end get_index_for_date;
212
213 procedure handle_error(p_error_msg in varchar2,
214 p_stmt_num in number,
215 p_proc_name in varchar2)
216 is
217 begin
218 fnd_file.put_line(fnd_file.log,'Error in '||p_proc_name||'( stmt_num: '||p_stmt_num||') '||p_error_msg);
219 fnd_file.new_line(fnd_file.log, 3);
220 end handle_error;
221
222 /* To calculate metrics for the required period */
223 procedure calculate_metrics(retcode out nocopy number,
224 errbuf out nocopy varchar2,
225 p_organization_id in number)
226 is
227
228 /* Record type for storing metrics data for a particular employee */
229 type rec_emp_perf_temp IS record(
230 actual_date_seq number,
231 shift_num number,
232 shift_start date,
233 shift_end date,
234 shift_duration number,
235 act_att_hrs number,
236 dir_lab_hrs number,
237 sch_avl_hrs number,
238 earned_hrs number);
239
240 /* PL/SQL table for storing metrics data for a particular employee */
241 type t_emp_perf_temp is table of rec_emp_perf_temp index by varchar2(25);
242 v_emp_perf_temp t_emp_perf_temp;
243
244 type t_wip_lab_perf_rate is table of wip_labor_performance_rates%rowtype index by binary_integer;
245 v_wip_lab_perf_rate t_wip_lab_perf_rate;
246
247 type t_date_sequence is table of wip_labor_performance_rates.date_sequence%type index by binary_integer;
248 type t_shift_num is table of wip_labor_performance_rates.shift_num%type index by binary_integer;
249 type t_m1_att1 is table of wip_labor_performance_rates.metric1_attribute1%type index by binary_integer;
250 type t_m1_att2 is table of wip_labor_performance_rates.metric1_attribute2%type index by binary_integer;
251 type t_m2_att1 is table of wip_labor_performance_rates.metric2_attribute1%type index by binary_integer;
252 type t_m2_att2 is table of wip_labor_performance_rates.metric2_attribute2%type index by binary_integer;
253 type t_m3_att1 is table of wip_labor_performance_rates.metric3_attribute1%type index by binary_integer;
254 type t_m3_att2 is table of wip_labor_performance_rates.metric3_attribute2%type index by binary_integer;
255
256 v_date_sequence t_date_sequence;
257 v_shift_num t_shift_num;
258 v_m1_att1 t_m1_att1;
259 v_m1_att2 t_m1_att2;
260 v_m2_att1 t_m2_att1;
261 v_m2_att2 t_m2_att2;
262 v_m3_att1 t_m3_att1;
263 v_m3_att2 t_m3_att2;
264
265 /* cursor to get all employees for a particular organization */
266 cursor employees(org_id number) is
267 select bre.organization_id,
268 bdri.department_id,
269 bdri.resource_id,
270 bdri.instance_id,
271 bre.person_id
272 from per_all_people_f papf,
273 bom_resource_employees bre,
274 bom_dept_res_instances bdri,
275 bom_department_resources bdr,
276 bom_resources br
277 where papf.person_id = bre.person_id
278 and bre.instance_id = bdri.instance_id
279 and sysdate between papf.effective_start_date and nvl(papf.effective_end_date,sysdate+1)
280 and bre.resource_id = bdri.resource_id
281 and bdri.department_id = bdr.department_id
282 and bdri.resource_id = bdr.resource_id
283 and bdr.resource_id = br.resource_id
284 and bdr.available_24_hours_flag = 2
285 and wip_ws_labor_metric_pub.is_time_uom(br.unit_of_measure) = 1
286 and bre.organization_id = org_id
287 order by bdri.department_id,
288 bdri.resource_id;
289
290 /* cursor to get all preferences value for a particular organization */
291 cursor get_preferences(org_id number) is
292 select attribute_name,
293 substr(attribute_name,2,1) as identifier,
294 attribute_value_code
295 from wip_preference_values
296 where preference_id = 35
297 and level_id = 1
298 and sequence_number = (select sequence_number
299 from wip_preference_values
300 where preference_id = 35
301 and level_id = 1
302 and attribute_name = 'Org'
303 and attribute_value_code = to_char(org_id))
304 order by 1 desc;
305
306 /* This Cursor will get the information of all shifts for a department resource in the required period. */
307 -- Modified Date conversion logic for Bug 6972082.
308 cursor emp_shifts(p_calendar_code varchar2,
309 start_period date,
310 end_period date,
311 dept_id number,
312 res_id number) is
313 select trunc(bsd.seq_num) shift_date_seq,
314 bsd.shift_num shift_num,
315 bsd.shift_date + st.from_time/(60*60*24) shift_start_time,
316 bsd.shift_date + st.to_time/(60*60*24) shift_end_time,
317 (bsd.shift_date + st.to_time/(60*60))-(bsd.shift_date + st.from_time/(60*60)) shift_duration
318 from bom_shift_dates bsd,
319 (select bst.shift_num,
320 min(bst.from_time) from_time,
321 max(bst.to_time + decode(sign(bst.to_time - bst.from_time), -1, (24*60*60), 0)) to_time
322 from bom_shift_times bst
323 where bst.calendar_code = p_calendar_code
324 group by bst.shift_num) st,
325 bom_calendar_shifts bcs,
326 bom_resource_shifts brs
327 where bsd.calendar_code = p_calendar_code
328 and bsd.exception_set_id = -1
329 and bsd.shift_date between trunc(start_period) and trunc(end_period)
330 and bsd.shift_num = st.shift_num
331 and bsd.seq_num is not null
332 and bsd.calendar_code = bcs.calendar_code
333 and bsd.shift_num = bcs.shift_num
334 /*Fix bug 14674233, shouldn't use the operator < because it should be inclusive of end_period
335 end period is the last shift before sysdate and was retrieved by get_period_end(). So, if we
336 used operator < instead of <=, the last shift will be considered as part of labor metric calculation*/
337 and fnd_date.canonical_to_date(to_char((bsd.shift_date + st.to_time/(60*60*24)),WIP_CONSTANTS.DATETIME_FMT)) <= end_period
338 and brs.shift_num= bcs.shift_num
339 and brs.department_id = dept_id
340 and brs.resource_id = res_id;
341
342 /* cursor to get records from wip_resource_actual_times for calculating AAH */
343 cursor aah_records(org_id number,
344 emp_id number,
345 start_time date,
346 end_time date) is
347 select start_date as actual_date,
348 decode(action_flag,2,-duration,duration) as duration,
349 uom_code
350 from wip_resource_actual_times
351 where organization_id = org_id
352 and employee_id = emp_id
353 and wip_entity_id is null
354 and time_entry_mode in (6,8)
355 and start_date between start_time and end_time
356 and duration is not null;
357
358 /* cursor to get records from wip_resource_actual_times for calculating SAH */
359 cursor sah_records(org_id number,
360 emp_id number,
361 start_time date,
362 end_time date) is
363 select start_date as actual_date,
364 decode(action_flag,2,-duration,duration) as duration,
365 uom_code
366 from wip_resource_actual_times
367 where organization_id = org_id
368 and employee_id = emp_id
369 and wip_entity_id is null
370 and time_entry_mode =7
371 and start_date between start_time and end_time
372 and duration is not null;
373
374 /* cursor to get all records from wip_resource_actual_times for calculating DLH */
375 -- Lot based jobs also uses wip_resource_actual_times. So added condition on entity_type.
376 -- Fix for Bug 12614160. Consider Borrowed Resources. Removed condition on Department and added instance id.
377 cursor dlh_records(org_id number,
378 dept_id number,
379 res_id number,
380 inst_id number,
381 emp_id number,
382 start_time date,
383 end_time date) is
384 select wrat.start_date as actual_date,
385 decode(wrat.action_flag,2,-wrat.duration,wrat.duration) as duration,
386 wrat.uom_code
387 from wip_resource_actual_times wrat,
388 wip_entities we
389 where wrat.organization_id = org_id
390 and we.organization_id = wrat.organization_id
391 and we.wip_entity_id = wrat.wip_entity_id
392 and we.entity_type = 1
393 and wrat.instance_id = inst_id
394 and wrat.resource_id = res_id
395 and wrat.employee_id = emp_id
396 and wrat.wip_entity_id is not null
397 and wrat.duration is not null
398 and wrat.start_date between start_time and end_time;
399
400 /* Bug 7010115: cursor to get all Job-Ops from Clock-In records for calculating earned hours */
401 -- Fix for Bug 12614160. Consider Borrowed Resources. Removed condition on Department and added instance id.
402 cursor clock_in_records(org_id number,
403 dept_id number,
404 res_id number,
405 inst_id number,
406 emp_id number,
407 start_time date,
408 end_time date) is
409 select distinct wrat.wip_entity_id as wip_entity_id,
410 wrat.operation_seq_num as operation_seq_num,
411 get_index_for_date(wrat.start_date,wrat.organization_id,dept_id,res_id) as emp_index
412 from wip_resource_actual_times wrat,
413 wip_entities we
414 where wrat.organization_id = org_id
415 and we.organization_id = wrat.organization_id
416 and we.wip_entity_id = wrat.wip_entity_id
417 and we.entity_type=1
418 and wrat.instance_id = inst_id
419 and wrat.resource_id = res_id
420 and wrat.employee_id = emp_id
421 and wrat.wip_entity_id is not null
422 and wrat.duration is not null
423 and wrat.start_date between start_time and end_time;
424
425 /* cursor to get all records from wip_move_transactions, wip_operations for obtaining completed quantity */
426 /* Modified for Bug 7010115. */
427 -- Lot based jobs should not be considered for earned hours. So added condition on entity_type.
428 cursor eh_records(org_id number,
429 start_time date,
430 end_time date,
431 wip_id number,
432 op_seq number,
433 res_id number,
434 indx varchar2,
435 dep_id number)
436 is
437 select nvl(sum(nvl(t.move_qty,0)),0) as moved_qty
438 from
439 (select
440 wo.wip_entity_id as job,
441 wo.operation_seq_num as op,
442 fm_operation_seq_num as fm_op,
443 fm_intraoperation_step_type as fm_step,
444 to_operation_seq_num as to_op,
445 to_intraoperation_step_type as to_step,
446 decode(wmt.to_intraoperation_step_type,
447 1, (case when ((wmt.to_operation_seq_num > wo.operation_seq_num)) then wmt.primary_quantity
448 when ((wmt.to_operation_seq_num < wo.operation_seq_num)) then -1 * wmt.primary_quantity
449 when (wmt.to_operation_seq_num <= wmt.fm_operation_seq_num
450 and wmt.to_operation_seq_num = wo.operation_seq_num
451 and wmt.fm_intraoperation_step_type not in (4,5)) then -1 * wmt.primary_quantity
452 else 0 end),
453 2, (case when ((wmt.to_operation_seq_num > wo.operation_seq_num)) then wmt.primary_quantity
454 when ((wmt.to_operation_seq_num < wo.operation_seq_num)) then -1 * wmt.primary_quantity
455 when (wmt.to_operation_seq_num <= wmt.fm_operation_seq_num
456 and wmt.to_operation_seq_num = wo.operation_seq_num
457 and wmt.fm_intraoperation_step_type not in (4,5)) then -1 * wmt.primary_quantity
458 else 0 end),
459 3, (case when (wmt.to_operation_seq_num >= wo.operation_seq_num) then wmt.primary_quantity
460 when (wmt.to_operation_seq_num < wo.operation_seq_num
461 and (wmt.fm_intraoperation_step_type not in (4,5)
462 or wmt.fm_operation_seq_num <> wo.operation_seq_num)) then -1 * wmt.primary_quantity
463 else 0 end),
464 5, (case when wmt.to_operation_seq_num > wo.operation_seq_num then wmt.primary_quantity
465 when wmt.to_operation_seq_num < wo.operation_seq_num then -1 * wmt.primary_quantity
466 when wmt.to_operation_seq_num = wo.operation_seq_num then 0
467 else 0 end),
468 4, (case when wmt.to_operation_seq_num > wo.operation_seq_num then wmt.primary_quantity
469 when wmt.to_operation_seq_num < wo.operation_seq_num then -1 * wmt.primary_quantity
470 when wmt.to_operation_seq_num = wo.operation_seq_num then 0
471 else 0 end),
472 0) as move_qty,
473 get_index_for_date(wmt.transaction_date,wmt.organization_id,dep_id,res_id) as emp_index
474 from
475 wip_move_transactions wmt,
476 wip_operations wo,
477 wip_entities we
478 where
479 wo.organization_id = org_id
480 and wo.organization_id = wmt.organization_id
481 and wo.organization_id = we.organization_id
482 and wo.wip_entity_id = we.wip_entity_id
483 and we.entity_type=1
484 and wo.wip_entity_id = wmt.wip_entity_id
485 and wo.operation_seq_num = op_seq
486 and wo.wip_entity_id = wip_id
487 and wo.repetitive_schedule_id is null
488 and wmt.transaction_date between start_time and end_time
489 and ((wo.operation_seq_num >= wmt.fm_operation_seq_num + decode(sign(wmt.fm_intraoperation_step_type - 2),1,1,0)
490 and wo.operation_seq_num < wmt.to_operation_seq_num + decode(sign(wmt.to_intraoperation_step_type - 2),1,1,0)
491 and (wmt.to_operation_seq_num > wmt.fm_operation_seq_num
492 or (wmt.to_operation_seq_num = wmt.fm_operation_seq_num
493 and wmt.fm_intraoperation_step_type <= 2
494 and wmt.to_intraoperation_step_type > 2)))
495 or (wo.operation_seq_num < wmt.fm_operation_seq_num + decode(sign(wmt.fm_intraoperation_step_type-2),1,1,0)
496 and wo.operation_seq_num >= wmt.to_operation_seq_num + decode(sign(wmt.to_intraoperation_step_type-2),1,1,0)
497 and (wmt.fm_operation_seq_num > wmt.to_operation_seq_num
498 or (wmt.fm_operation_seq_num = wmt.to_operation_seq_num
499 and wmt.to_intraoperation_step_type <= 2
500 and wmt.fm_intraoperation_step_type > 2)))
501 or (wmt.fm_intraoperation_step_type in (4,5)
502 and wo.operation_seq_num = wmt.fm_operation_seq_num))) t
503 where
504 t.emp_index = indx;
505
506 -- Fix for Bug 12614160. Consider cases where resource doesn't exist on the job operation.
507 cursor ear_hours_calc(org_id number,
508 we_ent_id number,
509 op_seq_num number,
510 res_id number,
511 move_qty number)
512 is
513 select usage_rate_or_amount *decode(sign(move_qty),-1,0,move_qty) usage_rate,uom_code,basis_type, 1 resource_pick
514 from wip_operation_resources
515 where organization_id= org_id
516 and wip_entity_id= we_ent_id
517 and operation_seq_num= op_seq_num
518 and resource_id = res_id
519 union all
520 select usage_rate_or_amount *decode(sign(move_qty),-1,0,move_qty) usage_rate,uom_code,basis_type, 2 resource_pick
521 from wip_operation_resources wor
522 where organization_id= org_id
523 and wip_entity_id= we_ent_id
524 and operation_seq_num= op_seq_num
525 and resource_seq_num = (select min(wor.resource_seq_num)
526 from wip_operation_resources wor,bom_resources br
527 where wor.organization_id= org_id
528 and wor.wip_entity_id= we_ent_id
529 and wor.operation_seq_num= op_seq_num
530 and WIP_WS_LABOR_METRIC_PUB.is_time_uom(wor.uom_code) = 1
531 and nvl(wor.basis_type,1) <> 2
532 and wor.organization_id = br.organization_id
533 and wor.resource_id = br.resource_id
534 and br.resource_type = 2)
535 order by 4;
536
537 /* local variables */
538 l_prev_department_id number :=null;
539 l_prev_resource_id number :=null;
540 l_calendar_code varchar2(100) := null;
541 l_bom_uom_code varchar2(3) :=null;
542 l_time_uom_flag number :=null;
543 l_uom_code varchar2(3);
544 l_current_date date := null;
545 window_end_time date :=null;
546 window_start_time date :=null;
547
548 l_act_avl_hrs_required boolean := false;
549 l_dir_lab_hrs_required boolean := false;
550 l_sch_avl_hrs_required boolean := false;
551 l_earned_hrs_required boolean := false;
552
553 /* variables for preferences */
554 l_metric1_required boolean := false;
555 l_metric2_required boolean := false;
556 l_metric3_required boolean := false;
557 l_metric1_attribute1 number :=0;
558 l_metric1_attribute2 number :=0;
559 l_metric2_attribute1 number :=0;
560 l_metric2_attribute2 number :=0;
561 l_metric3_attribute1 number :=0;
562 l_metric3_attribute2 number :=0;
563
564 log_metric1 varchar2(30):= 'Disabled';
565 log_metric2 varchar2(30):= 'Disabled';
566 log_metric3 varchar2(30):= 'Disabled';
567 log_act_avl_hrs varchar2(30):= 'Not Required';
568 log_dir_lab_hrs varchar2(30):= 'Not Required';
569 log_sch_avl_hrs varchar2(30):= 'Not Required';
570 log_earned_hrs varchar2(30):= 'Not Required';
571
572 l_act_att_hrs number :=0;
573 l_dir_lab_hrs number :=0;
574 l_sch_avl_hrs number :=0;
575 l_earned_hrs number :=0;
576
577 g_user_id number;
578 g_user_login_id number;
579 g_program_appl_id number;
580 g_request_id number;
581 g_program_id number;
582 g_logLevel number;
583
584 l_date_seq number := null;
585 l_shift_num number := null;
586 l_shift_start_date date := null;
587 l_shift_end_date date := null;
588
589 l_stmt_num number := null;
590 l_proc_name varchar2(50) := 'WIP_WS_LABOR_METRIC_PUB.CALCULATE_METRICS';
591
592 l_org_code varchar2(3);
593 l_pgm_count number :=0;
594
595 emp_index varchar2(25) := null;
596 idx varchar2(25) := null;
597 new_index number;
598
599 x_error_msg varchar2(2000);
600 l_error_count number:=0;
601 l_conc_status boolean;
602
603 e_null_org_id exception;
604
605 is_aah_record_valid boolean;
606 is_sah_record_valid boolean;
607 is_dlh_record_valid boolean;
608 is_eh_record_valid boolean;
609 l_skip_records Number := 0;
610 l_working_emp_count number; --bug 14845207
611
612 begin
613
614 l_stmt_num :=10;
615 g_user_id := fnd_global.user_id;
616 g_user_login_id := fnd_global.login_id;
617 g_program_appl_id := fnd_global.prog_appl_id;
618 g_request_id := fnd_global.conc_request_id;
619 g_program_id := fnd_global.conc_program_id;
620
621 g_logLevel := FND_LOG.g_current_runtime_level;
622
623 if p_organization_id is null then
624 raise e_null_org_id;
625 end if;
626
627 select organization_code
628 into l_org_code
629 from mtl_parameters
630 where organization_id = p_organization_id;
631
632 wip_ws_util.trace_log('Launching Labor Metrics Calculation for Organization: '||l_org_code);
633
634 l_stmt_num :=20;
635
636 /* If the calculation program is running for this org already, then error out.
637 Bug 6891668. Modified Logic for Checking Concurrency */
638
639 select count(1)
640 into l_pgm_count
641 from fnd_concurrent_requests
642 where program_application_id = g_program_appl_id
643 and concurrent_program_id = g_program_id
644 and upper(phase_code) = 'R'
645 and argument1 = to_char(p_organization_id);
646
647 if (l_pgm_count > 1) then
648 fnd_message.set_name('WIP','WIP_RUNNING_LABOR_PRG');
649 fnd_message.set_token('ORG', to_char(l_org_code));
650 x_error_msg := fnd_message.get;
651 wip_ws_util.trace_log('Error in '||l_proc_name||'( stmt_num: '||l_stmt_num||') '||x_error_msg);
652 wip_ws_util.trace_log('Unable to run calculation program for this organization. Please try after some time.');
653 retcode := 2;
654 l_conc_status := FND_CONCURRENT.SET_COMPLETION_STATUS('ERROR',
655 'Errors encountered in calculation program, please check the log file.');
656 return;
657 end if;
658
659 /* Get window_end_time and window_start_time to determine the
660 Time window for metrics calculation. */
661
662 select sysdate
663 into l_current_date
664 from dual;
665
666 l_stmt_num :=30;
667
668 l_bom_uom_code := fnd_profile.value('BOM:HOUR_UOM_CODE');
669 l_calendar_code := get_calendar_code(p_organization_id);
670 window_end_time := get_period_end(l_calendar_code,l_current_date);
671 window_start_time := get_period_start(p_organization_id,window_end_time);
672
673 if (g_logLevel <= wip_constants.trace_logging) then
674 wip_ws_util.trace_log('Calendar: '||l_calendar_code);
675 wip_ws_util.trace_log('Start Time: '||to_char(window_start_time,WIP_CONSTANTS.DATETIME_FMT));
676 wip_ws_util.trace_log('End Time: '||to_char(window_end_time,WIP_CONSTANTS.DATETIME_FMT));
677 wip_ws_util.trace_log('Base Time UOM: '||l_bom_uom_code);
678 end if;
679
680 l_stmt_num :=40;
681 /* Check for the metrics preferences and its requirement: Start */
682
683 for preferences in get_preferences(p_organization_id) loop
684 if preferences.identifier = '1' then
685 if l_metric1_required then
686 if preferences.attribute_value_code ='1' then /* 1-AAH,2-SAH,3-DLH,4-EH */
687 l_act_avl_hrs_required := true;
688 log_act_avl_hrs := 'Required';
689 if preferences.attribute_name = 'M1Attribute1' then
690 l_metric1_attribute1 :=1;
691 else
692 l_metric1_attribute2 :=1;
693 end if;
694 elsif preferences.attribute_value_code ='2' then
695 l_sch_avl_hrs_required := true;
696 log_sch_avl_hrs :='Required';
697 if preferences.attribute_name = 'M1Attribute1' then
698 l_metric1_attribute1 :=2;
699 else
700 l_metric1_attribute2 :=2;
701 end if;
702 elsif preferences.attribute_value_code ='3' then
703 l_dir_lab_hrs_required := true;
704 log_dir_lab_hrs := 'Required';
705 if preferences.attribute_name = 'M1Attribute1' then
706 l_metric1_attribute1 :=3;
707 else
708 l_metric1_attribute2 :=3;
709 end if;
710 else
711 l_earned_hrs_required := true;
712 log_earned_hrs := 'Required';
713 if preferences.attribute_name = 'M1Attribute1' then
714 l_metric1_attribute1 :=4;
715 else
716 l_metric1_attribute2 :=4;
717 end if;
718 end if;
719 end if;
720 if preferences.attribute_name = 'M1Enabled' and preferences.attribute_value_code ='1' then
721 l_metric1_required := true;
722 log_metric1 := 'Enabled';
723 end if;
724 elsif preferences.identifier = '2' then
725 if l_metric2_required then
726 if preferences.attribute_value_code ='1' then /* 1-AAH,2-SAH,3-DLH,4-EH */
727 l_act_avl_hrs_required := true;
728 log_act_avl_hrs := 'Required';
729 if preferences.attribute_name = 'M2Attribute1' then
730 l_metric2_attribute1 :=1;
731 else
732 l_metric2_attribute2 :=1;
733 end if;
734 elsif preferences.attribute_value_code ='2' then
735 l_sch_avl_hrs_required := true;
736 log_sch_avl_hrs :='Required';
737 if preferences.attribute_name = 'M2Attribute1' then
738 l_metric2_attribute1 :=2;
739 else
740 l_metric2_attribute2 :=2;
741 end if;
742 elsif preferences.attribute_value_code ='3' then
743 l_dir_lab_hrs_required := true;
744 log_dir_lab_hrs := 'Required';
745 if preferences.attribute_name = 'M2Attribute1' then
746 l_metric2_attribute1 :=3;
747 else
748 l_metric2_attribute2 :=3;
749 end if;
750 else
751 l_earned_hrs_required := true;
752 log_earned_hrs := 'Required';
753 if preferences.attribute_name = 'M2Attribute1' then
754 l_metric2_attribute1 :=4;
755 else
756 l_metric2_attribute2 :=4;
757 end if;
758 end if;
759 end if;
760 if preferences.attribute_name = 'M2Enabled' and preferences.attribute_value_code ='1' then
761 l_metric2_required := true;
762 log_metric2 := 'Enabled';
763 end if;
764 elsif preferences.identifier = '3' then
765 if l_metric3_required then
766 if preferences.attribute_value_code ='1' then /* 1-AAH,2-SAH,3-DLH,4-EH */
767 l_act_avl_hrs_required := true;
768 log_act_avl_hrs := 'Required';
769 if preferences.attribute_name = 'M3Attribute1' then
770 l_metric3_attribute1 :=1;
771 else
772 l_metric3_attribute2 :=1;
773 end if;
774 elsif preferences.attribute_value_code ='2' then
775 l_sch_avl_hrs_required := true;
776 log_sch_avl_hrs :='Required';
777 if preferences.attribute_name = 'M3Attribute1' then
778 l_metric3_attribute1 :=2;
779 else
780 l_metric3_attribute2 :=2;
781 end if;
782 elsif preferences.attribute_value_code ='3' then
783 l_dir_lab_hrs_required := true;
784 log_dir_lab_hrs := 'Required';
785 if preferences.attribute_name = 'M3Attribute1' then
786 l_metric3_attribute1 :=3;
787 else
788 l_metric3_attribute2 :=3;
789 end if;
790 else
791 l_earned_hrs_required := true;
792 log_earned_hrs := 'Required';
793 if preferences.attribute_name = 'M3Attribute1' then
794 l_metric3_attribute1 :=4;
795 else
796 l_metric3_attribute2 :=4;
797 end if;
798 end if;
799 end if;
800 if preferences.attribute_name = 'M3Enabled' and preferences.attribute_value_code ='1' then
801 l_metric3_required := true;
802 log_metric3 := 'Enabled';
803 end if;
804 end if;
805 end loop;
806
807 l_stmt_num :=50;
808
809 if (g_logLevel <= wip_constants.trace_logging) then
810
811 wip_ws_util.trace_log('Metric 1: '||log_metric1);
812 wip_ws_util.trace_log('Metric 2: '||log_metric2);
813 wip_ws_util.trace_log('Metric 3: '||log_metric3);
814
815 wip_ws_util.trace_log('Actual Attendance Hours: '||log_act_avl_hrs);
816 wip_ws_util.trace_log('Scheduled Available Hours: '||log_sch_avl_hrs);
817 wip_ws_util.trace_log('Direct Labor Hours: '||log_dir_lab_hrs);
818 wip_ws_util.trace_log('Earned Hours: '||log_earned_hrs);
819
820 end if;
821
822 /* Check for the metrics preferences and its requirement: End */
823
824 if l_metric1_required or l_metric2_required or l_metric3_required then
825 /* Delete existing records in wip_labor_performance_rates table for the org.*/
826
827 delete from wip_labor_performance_rates
828 where organization_id= p_organization_id;
829
830 l_stmt_num :=60;
831
832 if (g_logLevel <= wip_constants.trace_logging) then
833 wip_ws_util.trace_log('Deleted Existing records for this Organization.');
834 end if;
835 /* calculate the metrics for every employee associated to any of
836 the department for this organization. */
837 new_index :=0;
838 for employee in employees(p_organization_id) Loop
839
840 if (g_logLevel <= wip_constants.trace_logging) then
841 wip_ws_util.trace_log('Calculation for Emp with Dep_Id: '||to_char(employee.department_id)
842 ||' Res_Id: '||to_char(employee.resource_id)
843 ||' Pers_Id: '||to_char(employee.person_id));
844 end if;
845
846 if ( l_prev_department_id is null or
847 l_prev_resource_id is null or
848 employee.department_id <> l_prev_department_id or
849 employee.resource_id <> l_prev_resource_id)
850 then
851 l_stmt_num :=70;
852 v_emp_perf_temp.delete;
853 for emp_shift in emp_shifts(l_calendar_code,
854 window_start_time,
855 window_end_time,
856 employee.department_id,
857 employee.resource_id)
858 loop
859 l_stmt_num :=80;
860 emp_index := to_char(emp_shift.shift_date_seq||':'||emp_shift.shift_num);
861 v_emp_perf_temp(emp_index).actual_date_seq :=emp_shift.shift_date_seq;
862 v_emp_perf_temp(emp_index).shift_num :=emp_shift.shift_num;
863 v_emp_perf_temp(emp_index).shift_start :=emp_shift.shift_start_time;
864 v_emp_perf_temp(emp_index).shift_end :=emp_shift.shift_end_time;
865 v_emp_perf_temp(emp_index).shift_duration :=emp_shift.shift_duration;
866 v_emp_perf_temp(emp_index).act_att_hrs :=0;
867 v_emp_perf_temp(emp_index).dir_lab_hrs :=0;
868 v_emp_perf_temp(emp_index).sch_avl_hrs :=0;
869 v_emp_perf_temp(emp_index).earned_hrs :=0;
870 if (g_logLevel <= wip_constants.trace_logging) then
871 wip_ws_util.trace_log('Employee Shift Info: '||emp_index);
872 end if;
873 end loop;
874 if (g_logLevel <= wip_constants.trace_logging) then
875 wip_ws_util.trace_log('Re-Creating Shift Information for Employee');
876 end if;
877 else
878 l_stmt_num :=90;
879 emp_index := v_emp_perf_temp.first;
880 while emp_index <= v_emp_perf_temp.last loop
881 v_emp_perf_temp(emp_index).act_att_hrs :=0;
882 v_emp_perf_temp(emp_index).dir_lab_hrs :=0;
883 v_emp_perf_temp(emp_index).sch_avl_hrs :=0;
884 v_emp_perf_temp(emp_index).earned_hrs :=0;
885 emp_index := v_emp_perf_temp.next(emp_index);
886 end loop;
887
888 if (g_logLevel <= wip_constants.trace_logging) then
889 wip_ws_util.trace_log('Re-Using Shift Information for Employee');
890 end if;
891 end if;
892
893 if l_act_avl_hrs_required then /* Calculate actual available hours here. */
894 l_stmt_num :=100;
895 if (g_logLevel <= wip_constants.trace_logging) then
896 wip_ws_util.trace_log('Inside Actual Available Hours Calculation');
897 end if;
898 for emp_act_avl_hrs in aah_records(p_organization_id,
899 employee.person_id,
900 window_start_time,
901 window_end_time)
902 loop
903 l_stmt_num :=110;
904 if (g_logLevel <= wip_constants.trace_logging) then
905 wip_ws_util.trace_log('Record found for Actual Available Hours Calculation');
906 end if;
907 is_aah_record_valid := true;
908 wip_ws_dl_util.get_first_dept_resource_shift
909 (p_cal_code =>l_calendar_code,
910 p_dept_id =>employee.department_id,
911 p_resource_id =>employee.resource_id,
912 p_date =>emp_act_avl_hrs.actual_date,
913 x_shift_seq =>l_date_seq,
914 x_shift_num =>l_shift_num,
915 x_shift_start_date =>l_shift_start_date,
916 x_shift_end_date =>l_shift_end_date);
917
918 /* UOM validations and conversions */
919 l_time_uom_flag := is_time_uom(emp_act_avl_hrs.uom_code);
920 l_stmt_num :=120;
921 -- Modified for bug 6972129.
922 if l_time_uom_flag = 2 then
923 if (g_logLevel <= wip_constants.trace_logging) then
924 fnd_message.set_name('WIP','WIP_LM_TIME_UOM');
925 fnd_message.set_token('EMP', to_char(employee.person_id));
926 x_error_msg := fnd_message.get;
927 wip_ws_util.trace_log(l_proc_name||'( stmt_num: '||l_stmt_num||') '||x_error_msg);
928 --l_error_count := l_error_count + 1;
929 end if;
930 is_aah_record_valid := false;
931 end if;
932 if is_aah_record_valid then
933 if emp_act_avl_hrs.duration is not null and emp_act_avl_hrs.uom_code <> l_bom_uom_code then
934 l_stmt_num :=130;
935 l_act_att_hrs := inv_convert.inv_um_convert(item_id => -1,
936 precision => 38,
937 from_quantity => emp_act_avl_hrs.duration,
938 from_unit => emp_act_avl_hrs.uom_code,
939 to_unit => l_bom_uom_code,
940 from_name => null,
941 to_name => null);
942 else
943 l_act_att_hrs := emp_act_avl_hrs.duration;
944 end if;
945 l_stmt_num :=140;
946 -- Modified for Bug 7150089. Added this condition to avoid calculation if a shift
947 -- exists across multiple days and record is present for first day in that shift.
948 if v_emp_perf_temp.exists(to_char(l_date_seq||':'||l_shift_num)) then
949 v_emp_perf_temp(to_char(l_date_seq||':'||l_shift_num)).act_att_hrs :=
950 v_emp_perf_temp(to_char(l_date_seq||':'||l_shift_num)).act_att_hrs+l_act_att_hrs;
951
952 if (g_logLevel <= wip_constants.trace_logging) then
953 wip_ws_util.trace_log('Dep: '||to_char(employee.department_id)||
954 ' Res: '||to_char(employee.resource_id)||
955 ' Pers: '||to_char(employee.person_id)||
956 ' Date: '||to_char(emp_act_avl_hrs.actual_date,WIP_CONSTANTS.DATETIME_FMT)||
957 ' Shift Seq: '||to_char(l_date_seq)||
958 ' Shift Num: '||to_char(l_shift_num)||
959 ' AAH Value: '||to_char(l_act_att_hrs));
960 end if;
961 end if;
962 end if;
963 end loop;
964 end if; /* if l_act_avl_hrs_required then */
965
966 if l_sch_avl_hrs_required then /* Calculate scheduled available hours here. */
967 l_stmt_num :=150;
968 if (g_logLevel <= wip_constants.trace_logging) then
969 wip_ws_util.trace_log('Inside Scheduled Available Hours Calculation');
970 end if;
971 /* Add Code for capacity changes and exceptions and factor it on the shift duration. */
972 emp_index := v_emp_perf_temp.first;
973 while emp_index <= v_emp_perf_temp.last loop
974 v_emp_perf_temp(emp_index).sch_avl_hrs := v_emp_perf_temp(emp_index).shift_duration;
975 emp_index := v_emp_perf_temp.next(emp_index);
976 end loop;
977 l_stmt_num :=160;
978 for emp_sch_avl_hrs in sah_records(p_organization_id,
979 employee.person_id,
980 window_start_time,
981 window_end_time)
982 loop
983 l_stmt_num :=170;
984 if (g_logLevel <= wip_constants.trace_logging) then
985 wip_ws_util.trace_log('Record found for Scheduled Available Hours Calculation');
986 end if;
987 is_sah_record_valid :=true;
988 wip_ws_dl_util.get_first_dept_resource_shift
989 (p_cal_code =>l_calendar_code,
990 p_dept_id =>employee.department_id,
991 p_resource_id =>employee.resource_id,
992 p_date =>emp_sch_avl_hrs.actual_date,
993 x_shift_seq =>l_date_seq,
994 x_shift_num =>l_shift_num,
995 x_shift_start_date =>l_shift_start_date,
996 x_shift_end_date =>l_shift_end_date);
997
998 /* UOM validations and conversions */
999 l_time_uom_flag := is_time_uom(emp_sch_avl_hrs.uom_code);
1000 l_stmt_num :=180;
1001 -- Modified for bug 6972129.
1002 if l_time_uom_flag = 2 then
1003 if (g_logLevel <= wip_constants.trace_logging) then
1004 fnd_message.set_name('WIP','WIP_LM_TIME_UOM');
1005 fnd_message.set_token('EMP', to_char(employee.person_id));
1006 x_error_msg := fnd_message.get;
1007 wip_ws_util.trace_log(l_proc_name||'( stmt_num: '||l_stmt_num||') '||x_error_msg);
1008 --l_error_count := l_error_count + 1;
1009 end if;
1010 is_sah_record_valid :=false;
1011 end if;
1012 if is_sah_record_valid then
1013 if emp_sch_avl_hrs.duration is not null and emp_sch_avl_hrs.uom_code <> l_bom_uom_code then
1014 l_stmt_num :=190;
1015 l_sch_avl_hrs := inv_convert.inv_um_convert(item_id => -1,
1016 precision => 38,
1017 from_quantity => emp_sch_avl_hrs.duration,
1018 from_unit => emp_sch_avl_hrs.uom_code,
1019 to_unit => l_bom_uom_code,
1020 from_name => null,
1021 to_name => null);
1022 else
1023 l_sch_avl_hrs := emp_sch_avl_hrs.duration;
1024 end if;
1025 l_stmt_num :=200;
1026 -- Modified for Bug 7150089. Added this condition to avoid calculation if a shift
1027 -- exists across multiple days and record is present for first day in that shift.
1028 if v_emp_perf_temp.exists(to_char(l_date_seq||':'||l_shift_num)) then
1029 v_emp_perf_temp(to_char(l_date_seq||':'||l_shift_num)).sch_avl_hrs :=
1030 v_emp_perf_temp(to_char(l_date_seq||':'||l_shift_num)).sch_avl_hrs+l_sch_avl_hrs;
1031
1032 if (g_logLevel <= wip_constants.trace_logging) then
1033 wip_ws_util.trace_log('Dep: '||to_char(employee.department_id)||
1034 ' Res: '||to_char(employee.resource_id)||
1035 ' Pers: '||to_char(employee.person_id)||
1036 ' Date: '||to_char(emp_sch_avl_hrs.actual_date,WIP_CONSTANTS.DATETIME_FMT)||
1037 ' Shift Seq: '||to_char(l_date_seq)||
1038 ' Shift Num: '||to_char(l_shift_num)||
1039 ' SAH Value: '||to_char(l_sch_avl_hrs));
1040 end if;
1041 end if;
1042 end if;
1043 end loop;
1044 end if; /* if l_sch_avl_hrs_required then */
1045
1046 if l_dir_lab_hrs_required then /* Calculate direct labor hours here. */
1047 l_stmt_num :=210;
1048 if (g_logLevel <= wip_constants.trace_logging) then
1049 wip_ws_util.trace_log('Inside Direct Labor Hours Calculation');
1050 end if;
1051 for emp_dir_lab_hrs in dlh_records(p_organization_id,
1052 employee.department_id,
1053 employee.resource_id,
1054 employee.instance_id,
1055 employee.person_id,
1056 window_start_time,
1057 window_end_time)
1058 loop
1059 l_stmt_num :=220;
1060 if (g_logLevel <= wip_constants.trace_logging) then
1061 wip_ws_util.trace_log('Record found for Direct Labor Hours Calculation');
1062 end if;
1063 is_dlh_record_valid :=true;
1064 wip_ws_dl_util.get_first_dept_resource_shift
1065 (p_cal_code =>l_calendar_code,
1066 p_dept_id =>employee.department_id,
1067 p_resource_id =>employee.resource_id,
1068 p_date =>emp_dir_lab_hrs.actual_date,
1069 x_shift_seq =>l_date_seq,
1070 x_shift_num =>l_shift_num,
1071 x_shift_start_date =>l_shift_start_date,
1072 x_shift_end_date =>l_shift_end_date);
1073
1074 /* UOM validations and conversions */
1075 l_time_uom_flag := is_time_uom(emp_dir_lab_hrs.uom_code);
1076 l_stmt_num :=230;
1077 -- Modified for bug 6972129.
1078 if l_time_uom_flag = 2 then
1079 if (g_logLevel <= wip_constants.trace_logging) then
1080 fnd_message.set_name('WIP','WIP_LM_TIME_UOM');
1081 fnd_message.set_token('EMP', to_char(employee.person_id));
1082 x_error_msg := fnd_message.get;
1083 wip_ws_util.trace_log(l_proc_name||'( stmt_num: '||l_stmt_num||') '||x_error_msg);
1084 --l_error_count := l_error_count + 1;
1085 end if;
1086 is_dlh_record_valid :=false;
1087 end if;
1088 if is_dlh_record_valid then
1089 if emp_dir_lab_hrs.duration is not null and emp_dir_lab_hrs.uom_code <> l_bom_uom_code then
1090 l_stmt_num :=240;
1091 l_dir_lab_hrs := inv_convert.inv_um_convert(item_id => -1,
1092 precision => 38,
1093 from_quantity => emp_dir_lab_hrs.duration,
1094 from_unit => emp_dir_lab_hrs.uom_code,
1095 to_unit => l_bom_uom_code,
1096 from_name => null,
1097 to_name => null);
1098 else
1099 l_dir_lab_hrs := emp_dir_lab_hrs.duration;
1100 end if;
1101 l_stmt_num :=250;
1102 idx := to_char(l_date_seq||':'||l_shift_num);
1103 l_stmt_num :=260;
1104 -- Modified for Bug 7150089. Added this condition to avoid calculation if a shift
1105 -- exists across multiple days and record is present for first day in that shift.
1106 if v_emp_perf_temp.exists(to_char(l_date_seq||':'||l_shift_num)) then
1107 v_emp_perf_temp(to_char(l_date_seq||':'||l_shift_num)).dir_lab_hrs :=
1108 v_emp_perf_temp(to_char(l_date_seq||':'||l_shift_num)).dir_lab_hrs+l_dir_lab_hrs;
1109
1110 if (g_logLevel <= wip_constants.trace_logging) then
1111 wip_ws_util.trace_log('Dep: '||to_char(employee.department_id)||
1112 ' Res: '||to_char(employee.resource_id)||
1113 ' Inst : '||to_char(employee.instance_id)||
1114 ' Pers: '||to_char(employee.person_id)||
1115 ' Date: '||to_char(emp_dir_lab_hrs.actual_date,WIP_CONSTANTS.DATETIME_FMT)||
1116 ' Shift Seq: '||to_char(l_date_seq)||
1117 ' Shift Num: '||to_char(l_shift_num)||
1118 ' DLH Value: '||to_char(l_dir_lab_hrs));
1119 end if;
1120 end if;
1121 end if;
1122 l_stmt_num :=270;
1123 end loop;
1124 end if; /* l_dir_lab_hrs_required */
1125 l_stmt_num :=280;
1126 if l_earned_hrs_required then /* Calculate earned hours here. */
1127 l_stmt_num :=290;
1128 /* Modified for Bug 7010115. */
1129 -- Fix for Bug 12614160. Consider cases where resource doesn't exist on the job operation.
1130 l_skip_records := 0;
1131 if (g_logLevel <= wip_constants.trace_logging) then
1132 wip_ws_util.trace_log(' Get Clock-In records in for Earned Hours - Instance Id : '||employee.instance_id || ' Person Id : ' || employee.person_id || ' Resource Id : ' || employee.resource_id);
1133 end if;
1134 for emp_clock_rec in clock_in_records(p_organization_id,
1135 employee.department_id,
1136 employee.resource_id,
1137 employee.instance_id,
1138 employee.person_id,
1139 window_start_time,
1140 window_end_time)
1141 loop
1142 if (g_logLevel <= wip_constants.trace_logging) then
1143 wip_ws_util.trace_log(' Clock-In Details Job : '|| emp_clock_rec.wip_entity_id || ' Op : '||emp_clock_rec.operation_seq_num || ' Index : ' ||emp_clock_rec.emp_index);
1144 end if;
1145 for emp_earned_hrs in eh_records(p_organization_id,
1146 window_start_time,
1147 window_end_time,
1148 emp_clock_rec.wip_entity_id,
1149 emp_clock_rec.operation_seq_num,
1150 employee.resource_id,
1151 emp_clock_rec.emp_index,
1152 employee.department_id)
1153 loop
1154 if (g_logLevel <= wip_constants.trace_logging) then
1155 wip_ws_util.trace_log(' Earned Hours Move Qty : '|| emp_earned_hrs.moved_qty );
1156 end if;
1157 for earned_hrs_rates in ear_hours_calc(p_organization_id,
1158 emp_clock_rec.wip_entity_id,
1159 emp_clock_rec.operation_seq_num,
1160 employee.resource_id,
1161 emp_earned_hrs.moved_qty)
1162 loop
1163 if (g_logLevel <= wip_constants.trace_logging) then
1164 wip_ws_util.trace_log(' inside ear_hours_calc qty : '|| emp_earned_hrs.moved_qty );
1165 end if;
1166 is_eh_record_valid := true;
1167 --Start of bug 14845207 fix
1168 begin
1169 -- if employee didn't clock in/out in the shift then don't consider it when doing earn hour calculation
1170 select count(distinct wrat.employee_id)
1171 into l_working_emp_count
1172 from wip_resource_actual_times wrat
1173 where wrat.wip_entity_id = emp_clock_rec.wip_entity_id
1174 and wrat.operation_seq_num = emp_clock_rec.operation_seq_num
1175 and wrat.organization_id = p_organization_id
1176 and get_index_for_date(wrat.start_date,wrat.organization_id,employee.department_id,wrat.resource_id) = emp_clock_rec.emp_index
1177 and duration is not null;
1178 -- set the l_working_emp_count = 1 if it was 0, because
1179 -- l_working_emp_count will be used for division
1180 if (l_working_emp_count = 0) then
1181 l_working_emp_count := 1;
1182 end if;
1183 exception
1184 when others then
1185 l_working_emp_count := 1;
1186 end;
1187 if (g_logLevel <= wip_constants.trace_logging) then
1188 wip_ws_util.trace_log('Labor metric calculation, l_working_emp_count :'|| l_working_emp_count);
1189 end if;
1190
1191 if earned_hrs_rates.resource_pick = 1 then
1192 l_skip_records := 1;
1193 end if;
1194 if earned_hrs_rates.resource_pick = 2 and l_skip_records = 1 then
1195 is_eh_record_valid := false;
1196 end if;
1197 l_earned_hrs := earned_hrs_rates.usage_rate/l_working_emp_count;
1198 l_uom_code := earned_hrs_rates.uom_code ;
1199 l_time_uom_flag := is_time_uom(l_uom_code);
1200 l_stmt_num :=310;
1201 -- Modified for bug 6972129.
1202 if l_time_uom_flag = 2 then
1203 if (g_logLevel <= wip_constants.trace_logging) then
1204 fnd_message.set_name('WIP','WIP_LM_TIME_UOM');
1205 fnd_message.set_token('EMP', to_char(employee.person_id));
1206 x_error_msg := fnd_message.get;
1207 wip_ws_util.trace_log(l_proc_name||'( stmt_num: '||l_stmt_num||') '||x_error_msg);
1208 --l_error_count := l_error_count + 1;
1209 end if;
1210 is_eh_record_valid := false;
1211 end if;
1212 if earned_hrs_rates.basis_type = 2 then
1213 if (g_logLevel <= wip_constants.trace_logging) then
1214 fnd_message.set_name('WIP','WIP_LM_LOT_BASED_RES');
1215 fnd_message.set_token('EMP', to_char(employee.person_id));
1216 x_error_msg := fnd_message.get;
1217 wip_ws_util.trace_log(l_proc_name||'( stmt_num: '||l_stmt_num||') '||x_error_msg);
1218 --l_error_count := l_error_count + 1;
1219 end if;
1220 is_eh_record_valid := false;
1221 end if;
1222 if is_eh_record_valid then
1223 if l_earned_hrs is not null and l_uom_code <> l_bom_uom_code then
1224 l_stmt_num :=320;
1225 l_earned_hrs := inv_convert.inv_um_convert(item_id => -1,
1226 precision => 38,
1227 from_quantity => l_earned_hrs,
1228 from_unit => l_uom_code,
1229 to_unit => l_bom_uom_code,
1230 from_name => null,
1231 to_name => null);
1232 end if;
1233 l_stmt_num :=330;
1234 -- Modified for Bug 7150089. Added this condition to avoid calculation if a shift
1235 -- exists across multiple days and record is present for first day in that shift.
1236 if v_emp_perf_temp.exists(emp_clock_rec.emp_index) then
1237 v_emp_perf_temp(emp_clock_rec.emp_index).earned_hrs :=
1238 v_emp_perf_temp(emp_clock_rec.emp_index).earned_hrs+l_earned_hrs;
1239
1240 if (g_logLevel <= wip_constants.trace_logging) then
1241 wip_ws_util.trace_log('Dep: '||to_char(employee.department_id)||
1242 ' Res: '||to_char(employee.resource_id)||
1243 ' Inst: '||to_char(employee.instance_id)||
1244 ' Pers: '||to_char(employee.person_id)||
1245 ' Index: '||to_char(emp_clock_rec.emp_index)||
1246 ' Move Qty: '||to_char(emp_earned_hrs.moved_qty)||
1247 ' EH Value: '||to_char(l_earned_hrs));
1248 end if;
1249 end if;
1250 end if;
1251 l_stmt_num :=340;
1252 end loop;
1253 end loop;
1254 l_skip_records := 0;
1255 end loop;
1256 end if; /* if l_earned_hrs_required then */
1257 l_stmt_num :=350;
1258
1259 if (g_logLevel <= wip_constants.trace_logging) then
1260 wip_ws_util.trace_log('Performing data transfer');
1261 end if;
1262
1263 /* transfer data from collection indexed by varchar2 to a collection
1264 indexed by binary_integer so that we can bulk insert in the table.
1265 Also initialize the new collection always. */
1266 emp_index := v_emp_perf_temp.first;
1267 while emp_index <= v_emp_perf_temp.last loop
1268 if emp_index <> ':' or emp_index is not null then
1269 new_index := new_index+1;
1270 v_wip_lab_perf_rate(new_index).Organization_id := p_organization_id;
1271 v_wip_lab_perf_rate(new_index).Department_id := employee.department_id;
1272 v_wip_lab_perf_rate(new_index).Resource_id := employee.resource_id;
1273 v_wip_lab_perf_rate(new_index).Employee_id := employee.person_id;
1274 v_wip_lab_perf_rate(new_index).date_sequence := v_emp_perf_temp(emp_index).actual_date_seq;
1275 v_wip_lab_perf_rate(new_index).Shift_num := v_emp_perf_temp(emp_index).shift_num;
1276 case l_metric1_attribute1 when 1 then v_wip_lab_perf_rate(new_index).Metric1_attribute1 := v_emp_perf_temp(emp_index).act_att_hrs;
1277 when 2 then v_wip_lab_perf_rate(new_index).Metric1_attribute1 := v_emp_perf_temp(emp_index).sch_avl_hrs;
1278 when 3 then v_wip_lab_perf_rate(new_index).Metric1_attribute1 := v_emp_perf_temp(emp_index).dir_lab_hrs;
1279 when 4 then v_wip_lab_perf_rate(new_index).Metric1_attribute1 := v_emp_perf_temp(emp_index).earned_hrs;
1280 else v_wip_lab_perf_rate(new_index).Metric1_attribute1 :=0;
1281 end case;
1282 case l_metric1_attribute2 when 1 then v_wip_lab_perf_rate(new_index).Metric1_attribute2 := v_emp_perf_temp(emp_index).act_att_hrs;
1283 when 2 then v_wip_lab_perf_rate(new_index).Metric1_attribute2 := v_emp_perf_temp(emp_index).sch_avl_hrs;
1284 when 3 then v_wip_lab_perf_rate(new_index).Metric1_attribute2 := v_emp_perf_temp(emp_index).dir_lab_hrs;
1285 when 4 then v_wip_lab_perf_rate(new_index).Metric1_attribute2 := v_emp_perf_temp(emp_index).earned_hrs;
1286 else v_wip_lab_perf_rate(new_index).Metric1_attribute2 :=0;
1287 end case;
1288 case l_metric2_attribute1 when 1 then v_wip_lab_perf_rate(new_index).Metric2_attribute1 := v_emp_perf_temp(emp_index).act_att_hrs;
1289 when 2 then v_wip_lab_perf_rate(new_index).Metric2_attribute1 := v_emp_perf_temp(emp_index).sch_avl_hrs;
1290 when 3 then v_wip_lab_perf_rate(new_index).Metric2_attribute1 := v_emp_perf_temp(emp_index).dir_lab_hrs;
1291 when 4 then v_wip_lab_perf_rate(new_index).Metric2_attribute1 := v_emp_perf_temp(emp_index).earned_hrs;
1292 else v_wip_lab_perf_rate(new_index).Metric2_attribute1 :=0;
1293 end case;
1294 case l_metric2_attribute2 when 1 then v_wip_lab_perf_rate(new_index).Metric2_attribute2 := v_emp_perf_temp(emp_index).act_att_hrs;
1295 when 2 then v_wip_lab_perf_rate(new_index).Metric2_attribute2 := v_emp_perf_temp(emp_index).sch_avl_hrs;
1296 when 3 then v_wip_lab_perf_rate(new_index).Metric2_attribute2 := v_emp_perf_temp(emp_index).dir_lab_hrs;
1297 when 4 then v_wip_lab_perf_rate(new_index).Metric2_attribute2 := v_emp_perf_temp(emp_index).earned_hrs;
1298 else v_wip_lab_perf_rate(new_index).Metric2_attribute2 :=0;
1299 end case;
1300 case l_metric3_attribute1 when 1 then v_wip_lab_perf_rate(new_index).Metric3_attribute1 := v_emp_perf_temp(emp_index).act_att_hrs;
1301 when 2 then v_wip_lab_perf_rate(new_index).Metric3_attribute1 := v_emp_perf_temp(emp_index).sch_avl_hrs;
1302 when 3 then v_wip_lab_perf_rate(new_index).Metric3_attribute1 := v_emp_perf_temp(emp_index).dir_lab_hrs;
1303 when 4 then v_wip_lab_perf_rate(new_index).Metric3_attribute1 := v_emp_perf_temp(emp_index).earned_hrs;
1304 else v_wip_lab_perf_rate(new_index).Metric3_attribute1 :=0;
1305 end case;
1306 case l_metric3_attribute2 when 1 then v_wip_lab_perf_rate(new_index).Metric3_attribute2 := v_emp_perf_temp(emp_index).act_att_hrs;
1307 when 2 then v_wip_lab_perf_rate(new_index).Metric3_attribute2 := v_emp_perf_temp(emp_index).sch_avl_hrs;
1308 when 3 then v_wip_lab_perf_rate(new_index).Metric3_attribute2 := v_emp_perf_temp(emp_index).dir_lab_hrs;
1309 when 4 then v_wip_lab_perf_rate(new_index).Metric3_attribute2 := v_emp_perf_temp(emp_index).earned_hrs;
1310 else v_wip_lab_perf_rate(new_index).Metric3_attribute2 :=0;
1311 end case;
1312 v_wip_lab_perf_rate(new_index).Created_by := g_user_id;
1313 v_wip_lab_perf_rate(new_index).Creation_date := sysdate;
1314 v_wip_lab_perf_rate(new_index).Last_updated_by := g_user_id;
1315 v_wip_lab_perf_rate(new_index).Last_update_date := sysdate;
1316 v_wip_lab_perf_rate(new_index).Last_update_login := g_user_login_id;
1317 v_wip_lab_perf_rate(new_index).Object_version_number := 1;
1318 v_wip_lab_perf_rate(new_index).Request_id := g_request_id;
1319 v_wip_lab_perf_rate(new_index).Program_id := g_program_id;
1320 v_wip_lab_perf_rate(new_index).Program_application_id := g_program_appl_id;
1321 v_wip_lab_perf_rate(new_index).Program_update_date := sysdate;
1322 end if;
1323 emp_index := v_emp_perf_temp.next(emp_index);
1324 end loop;
1325 l_stmt_num :=360;
1326
1327 if (g_logLevel <= wip_constants.trace_logging) then
1328 wip_ws_util.trace_log(' Calculation Completed for employee Dep: '||to_char(employee.department_id)||
1329 ' Res: '||to_char(employee.resource_id)||
1330 ' Pers: '||to_char(employee.person_id));
1331 end if;
1332 l_stmt_num :=370;
1333
1334 l_prev_department_id := employee.department_id ;
1335 l_prev_resource_id := employee.resource_id ;
1336
1337 end loop; /* for employee in employees(p_organization_id) */
1338
1339 l_stmt_num :=380;
1340
1341 /* Bulk Insert Records into Metrics Table */
1342 forall cntr in v_wip_lab_perf_rate.first..v_wip_lab_perf_rate.last
1343 INSERT into WIP_LABOR_PERFORMANCE_RATES values v_wip_lab_perf_rate(cntr);
1344
1345 l_stmt_num :=390;
1346 if (g_logLevel <= wip_constants.trace_logging) then
1347 wip_ws_util.trace_log('Insertion of all calculated records is successful.');
1348 end if;
1349
1350 /* Since attendance hours is entered for an employee at org level and distributed across all departments
1351 if the employee is associated to multiple departments. So org level attendance hours will be over shooted
1352 when summing the value in the dashboard. So we compute the org level summary and insert into metrics table.*/
1353 select t.date_sequence,
1354 t.shift_num,
1355 sum(t.m1_att1),
1356 sum(t.m1_att2),
1357 sum(t.m2_att1),
1358 sum(t.m2_att2),
1359 sum(t.m3_att1),
1360 sum(t.m3_att2)
1361 bulk collect into
1362 v_date_sequence,
1363 v_shift_num,
1364 v_m1_att1,
1365 v_m1_att2,
1366 v_m2_att1,
1367 v_m2_att2,
1368 v_m3_att1,
1369 v_m3_att2
1370 from (
1371 select date_sequence,
1372 shift_num,
1373 decode(l_metric1_attribute1,1,max(metric1_attribute1),sum(metric1_attribute1)) m1_att1,
1374 decode(l_metric1_attribute2,1,max(metric1_attribute2),sum(metric1_attribute2)) m1_att2,
1375 decode(l_metric2_attribute1,1,max(metric2_attribute1),sum(metric2_attribute1)) m2_att1,
1376 decode(l_metric2_attribute2,1,max(metric2_attribute2),sum(metric2_attribute2)) m2_att2,
1377 decode(l_metric3_attribute1,1,max(metric3_attribute1),sum(metric3_attribute1)) m3_att1,
1378 decode(l_metric3_attribute2,1,max(metric3_attribute2),sum(metric3_attribute2)) m3_att2
1379 from wip_labor_performance_rates
1380 where organization_id = p_organization_id
1381 group by
1382 date_sequence,
1383 shift_num,
1384 employee_id
1385 )t
1386 group by
1387 t.date_sequence,
1388 t.shift_num;
1389
1390 l_stmt_num :=400;
1391 if (g_logLevel <= wip_constants.trace_logging) then
1392 wip_ws_util.trace_log('Computed Org Level Summary Data.');
1393 end if;
1394
1395 /* Insert into Metrics Table the org level summary data. Use -1 for dept,res and employee */
1396 forall org_index in v_shift_num.first..v_shift_num.last
1397 insert into wip_labor_performance_rates
1398 (
1399 ORGANIZATION_ID,
1400 DEPARTMENT_ID,
1401 RESOURCE_ID,
1402 EMPLOYEE_ID,
1403 DATE_SEQUENCE,
1404 SHIFT_NUM,
1405 METRIC1_ATTRIBUTE1,
1406 METRIC1_ATTRIBUTE2,
1407 METRIC2_ATTRIBUTE1,
1408 METRIC2_ATTRIBUTE2,
1409 METRIC3_ATTRIBUTE1,
1410 METRIC3_ATTRIBUTE2,
1411 CREATED_BY,
1412 CREATION_DATE,
1413 LAST_UPDATED_BY,
1414 LAST_UPDATE_DATE,
1415 LAST_UPDATE_LOGIN,
1416 PROGRAM_APPLICATION_ID,
1417 PROGRAM_UPDATE_DATE,
1418 PROGRAM_ID,
1419 REQUEST_ID,
1420 OBJECT_VERSION_NUMBER
1421 )
1422 values
1423 (
1424 p_organization_id,
1425 -1,
1426 -1,
1427 -1,
1428 v_date_sequence(org_index),
1429 v_shift_num(org_index),
1430 v_m1_att1(org_index),
1431 v_m1_att2(org_index),
1432 v_m2_att1(org_index),
1433 v_m2_att2(org_index),
1434 v_m3_att1(org_index),
1435 v_m3_att2(org_index),
1436 g_user_id,
1437 sysdate,
1438 g_user_id,
1439 sysdate,
1440 g_user_login_id,
1441 g_program_appl_id,
1442 sysdate,
1443 g_program_id,
1444 g_request_id,
1445 1);
1446
1447 l_stmt_num :=400;
1448 if (g_logLevel <= wip_constants.trace_logging) then
1449 wip_ws_util.trace_log('Insertion of Org Level Summary Data is successful.');
1450 end if;
1451
1452 end if; /* l_metric1_required or l_metric2_required or l_metric3_required */
1453
1454 l_stmt_num :=410;
1455 wip_ws_util.trace_log('Labor Metrics Calculation completed.');
1456
1457 commit;
1458
1459 if l_error_count=0 then
1460 l_conc_status := true;
1461 retcode:=0;
1462 else
1463 retcode := 1;
1464 errbuf := 'Calculation program encountered invalid records. Invalid records will be ignored during calculation.';
1465 fnd_file.put_line(fnd_file.log,errbuf);
1466 l_conc_status := FND_CONCURRENT.SET_COMPLETION_STATUS('WARNING',errbuf);
1467 end if;
1468
1469 exception
1470 when e_null_org_id then
1471 retcode := 2;
1472 errbuf := 'Organization parameter cannot be null';
1473 fnd_file.put_line(fnd_file.log,errbuf);
1474 l_conc_status := FND_CONCURRENT.SET_COMPLETION_STATUS('ERROR',errbuf);
1475 when others then
1476 fnd_message.set_name('WIP','WIP_LAB_UNEXPECTED_ERROR');
1477 x_error_msg := fnd_message.get;
1478 wip_ws_util.trace_log('Error in '||l_proc_name||'( stmt_num: '||l_stmt_num||') '||x_error_msg);
1479 x_error_msg := sqlerrm(sqlcode);
1480 wip_ws_util.trace_log('Error in '||l_proc_name||'( stmt_num: '||l_stmt_num||') '||x_error_msg);
1481 rollback;
1482 retcode := 2;
1483 errbuf := 'Errors encountered in calculation program, please check the log file.';
1484 l_conc_status := FND_CONCURRENT.SET_COMPLETION_STATUS('ERROR',errbuf);
1485
1486 end calculate_metrics;
1487
1488 /* To obtain Client Date for a given Server Date (Used in Charts only) */
1489 function get_client_date(p_date in Date)
1490 return date
1491 is
1492 l_converted_date date;
1493 l_timezone_enabled boolean := ( fnd_profile.value('ENABLE_TIMEZONE_CONVERSIONS') = 'Y' AND
1494 fnd_profile.value('CLIENT_TIMEZONE_ID') IS NOT NULL AND
1495 fnd_profile.value('SERVER_TIMEZONE_ID') IS NOT NULL AND
1496 fnd_profile.value('CLIENT_TIMEZONE_ID') <>
1497 fnd_profile.value('SERVER_TIMEZONE_ID'));
1498 l_client_id number := fnd_profile.value('CLIENT_TIMEZONE_ID');
1499 l_server_id number := fnd_profile.value('SERVER_TIMEZONE_ID');
1500 begin
1501
1502 if l_timezone_enabled and p_date is not null then
1503 l_converted_date := hz_timezone_pub.convert_datetime(l_server_id,
1504 l_client_id,
1505 p_date);
1506 else
1507 l_converted_date := p_date;
1508 end if;
1509
1510 return l_converted_date;
1511 end get_client_date;
1512
1513 END WIP_WS_LABOR_METRIC_PUB;