4 -----------------------------------------------------------------
1 PACKAGE BODY AHL_VWP_TIMES_PVT AS
2 /* $Header: AHLVTMSB.pls 120.7.12020000.2 2012/12/07 14:58:40 sareepar ship $ */
3
5 -- Define Global CONSTANTS --
6 -----------------------------------------------------------------
7 G_APP_NAME CONSTANT VARCHAR2(3) := 'AHL';
8 G_PKG_NAME CONSTANT VARCHAR2(30) := 'AHL_VWP_TIMES_PVT';
9
10 G_SECS_IN_DAY CONSTANT NUMBER := 86400; --Seconds in a day
11 G_HOLIDAY_TYPE CONSTANT NUMBER := 2;
12
13 -- Package level Global Variables used in time calculations
14 G_CURRENT_DEPT_ID NUMBER := NULL; -- Cached department id, so to know whether to reload
15
16 G_CAL_START DATE := NULL; -- Calendar start date for shift data
17 G_CAL_END DATE := NULL; -- Calendar end date for shift data
18 G_SHIFT_START NUMBER := NULL; -- Shift start in .decimal value of days
19 G_SHIFT_END NUMBER := NULL; -- Shift end in .decimal value of days
20 G_DAYS_ON NUMBER := NULL; -- Number of Days on
21 G_DAYS_OFF NUMBER := NULL; -- Number of Days off
22 G_VISIT_START_DATE DATE := NULL; -- The visit start date
23 G_VISIT_DEPT_ID NUMBER := NULL; -- The visit department
24 G_VISIT_STATUS VARCHAR2(30):= NULL; --The visit status
25 G_RESET_SYSDATE_FLAG VARCHAR2(1);
26
27 /*Added by sowsubra*/
28 G_ASSOC_TYPE_ROUTE CONSTANT VARCHAR2(30) := 'ROUTE';
29 G_ASSOC_TYPE_OPERATION CONSTANT VARCHAR2(30) := 'OPERATION';
30
31 -- Table type for all the Dates while deriving visit and task times
32 TYPE Dates_Tbl_Type IS TABLE OF DATE
33 INDEX BY BINARY_INTEGER;
34 TYPE Number_Tbl_Type IS TABLE OF NUMBER
35 INDEX BY BINARY_INTEGER;
36
37 G_EXCEPTION_DATES_TBL Dates_Tbl_Type; -- Stores the holidays for dept
38 G_STAGES_TBL Number_Tbl_Type; -- Stores the stage offsets for visit
39 ------------------------------------
40 -- Common constants and variables --
41 ------------------------------------
42 l_log_current_level NUMBER := fnd_log.g_current_runtime_level;
43 l_log_statement NUMBER := fnd_log.level_statement;
44 l_log_procedure NUMBER := fnd_log.level_procedure;
45 l_log_error NUMBER := fnd_log.level_error;
46 l_log_unexpected NUMBER := fnd_log.level_unexpected;
47 -----------------------------------------------------------------------
48 /*procedure adbug(p_msg_txt in varchar2 , p_message in date)
49 is
50 begin
51 if (true) then
52 dbms_output.put_line(p_msg_txt || to_char(p_message,'dd:mm:yyyy hh24:mi:ss'));
53 end if;
54 end adbug;
55
56 procedure adbug(p_message in varchar2)
57 is
58 begin
59 if (true) then
60 dbms_output.put_line(p_message);
61 end if;
62 end adbug;
63 */
64 --------------------------------------------------------------------
65 -- Define local procedures signature --
66 --------------------------------------------------------------------
67 --------------------------------------------------------------------
68 --Initializes the global variables based on the department id
69 --------------------------------------------------------------------
73 --Internal recursion code for setting task times
70 PROCEDURE Init_Shift_Data(p_department_id IN number);
71
72 --------------------------------------------------------------------
74 --------------------------------------------------------------------
75 -- SKPATHAK :: Bug 8343599 :: 14-APR-2009
76 -- Added the optional in param p_task_start_date
77 -- SKPATHAK :: 02-MAY-2011 :: VWPE: ER:12424063 :: Commented the below procedure
78 /*
79 PROCEDURE Adjust_Task_Times_Internal(p_task_id IN NUMBER,
80 p_task_start_date IN DATE default NULL);*/
81
82 --------------------------------------------------------------------
83 -- Define local functions signature --
84 --------------------------------------------------------------------
85
86 ----------------------------------------------------------------------
87 -- Gets the duration of a Route from its timespan column
88 ---------------------------------------------------------------------
89 -- Determines if a specific date is a holiday
90 FUNCTION Is_Dept_Holiday(l_curr_date DATE) RETURN BOOLEAN;
91
92 ----------------------------------------------------------
93 -- Derive the shift start date based on shift timing.
94 ------------------------------------------------------------
95 FUNCTION get_shift_start_date(p_date IN DATE)
96 RETURN DATE
97 IS
98 BEGIN
99 --If it's an overnight shift and time is before shift ends on the same day,
100 --the shift started a day earlier
101 IF (G_SHIFT_END < G_SHIFT_START AND
102 p_date <= trunc(p_date)+G_SHIFT_END) THEN --Fixed bug :: PRAKKUM :: AVF :: 15/11/2011 :: Bug# 13416865 - AVF ENHANCEMENTS
103 RETURN TRUNC(p_date) -1;
104 ELSE
105 RETURN TRUNC(p_date);
106 END IF;
107 END get_shift_start_date;
108
109 -- PRAKKUM :: AVF :: 15/11/2011 :: Bug# 13416865 - AVF ENHANCEMENTS
110 ----------------------------------------------------------
111 -- Derive the shift end date based on shift timing.
112 ------------------------------------------------------------
113 FUNCTION get_shift_end_date(p_date IN DATE)
114 RETURN DATE
115 IS
116 BEGIN
117 --If it's an overnight shift and time is before shift ends on the same day,
118 --the shift started a day earlier
119 IF (G_SHIFT_END < G_SHIFT_START AND
120 p_date >= trunc(p_date)+G_SHIFT_START) THEN --Fixed bug
121 RETURN TRUNC(p_date)+1;
122 ELSE
123 RETURN TRUNC(p_date);
124 END IF;
125 END get_shift_end_date;
126
127 --------------------------------------------------------------------
128 -- Define procedures body --
129 --------------------------------------------------------------------
130 --------------------------------------------------------------------
131 -- Function name : Get_Visit_Start_Time
132 -- Type : Public
133 -- Purpose : Fetches Master Work Order Actual Start Date if the
134 -- Visit is Closed, else RETURNs the Visit Start Date.
135 --
136 -- Parameters :
137 -- p_visit_id Visit ID to fetch the data
138 --
139 -- Version :
140 -- 17 September, 2007 RNAHATA Initial Version - 1.0
141 --
142 -- Added for Bug 6430038
143 --------------------------------------------------------------------
144 function Get_Visit_Start_Time(
145 p_visit_id IN NUMBER
146 )
147 RETURN DATE
148 IS
149 --
150 -- To get the Start Date and Status of the Visit
154 WHERE VISIT_ID = c_visit_id;
151 CURSOR c_visit_csr (c_visit_id IN NUMBER) IS
152 SELECT START_DATE_TIME, STATUS_CODE
153 FROM AHL_VISITS_B
155
156 -- Actual Visit Start Date
160 WHERE VISIT_ID = c_visit_id
157 CURSOR get_actual_start_date_csr(c_visit_id IN NUMBER) IS
158 SELECT ACTUAL_START_DATE
159 FROM AHL_WORKORDERS
161 AND VISIT_TASK_ID IS NULL
162 AND MASTER_WORKORDER_FLAG = 'Y';
163
164 --
165 l_actual_start_date DATE := null;
166 c_visit_rec c_visit_csr%ROWTYPE;
167 L_API_NAME CONSTANT VARCHAR2(30) := 'Get_Visit_Start_Time';
168 L_DEBUG_KEY CONSTANT VARCHAR2(100) := 'ahl.plsql.' || G_PKG_NAME || '.' || L_API_NAME;
169 --
170 BEGIN
171
172 IF (l_log_procedure >= l_log_current_level) THEN
173 fnd_log.string(l_log_procedure,
174 L_DEBUG_KEY ||'.begin',
175 'At the start of PL SQL function. Visit Id = ' || p_visit_id);
176 END IF;
177
178 OPEN c_visit_csr(p_visit_id);
179 FETCH c_visit_csr INTO c_visit_rec;
180 CLOSE c_visit_csr;
181
182 -- Closed
183 IF c_visit_rec.STATUS_CODE ='CLOSED' THEN
184 OPEN get_actual_start_date_csr (p_visit_id);
185 FETCH get_actual_start_date_csr INTO l_actual_start_date;
186 CLOSE get_actual_start_date_csr;
187 IF (l_log_procedure >= l_log_current_level) THEN
188 fnd_log.string(l_log_procedure,
189 L_DEBUG_KEY ||'.end',
190 'At the end of PL SQL function. Visit Start Date = ' || l_actual_start_date);
191 END IF;
192 RETURN l_actual_start_date;
193
194 -- Planning/Released/Partially Released/Cancelled
195 ELSE
196 IF (l_log_procedure >= l_log_current_level) THEN
197 fnd_log.string(l_log_procedure,
198 L_DEBUG_KEY ||'.end',
199 'At the end of PL SQL function. Visit Start Date = ' || c_visit_rec.START_DATE_TIME);
200 END IF;
201 RETURN c_visit_rec.START_DATE_TIME;
202 END IF;
203
204 END Get_Visit_Start_Time;
205
206 -- SKPATHAK :: 02-MAY-2011 :: VWPE: ER:12424063 :: Completely changed the logic of this procedure
207 --------------------------------------------------------------------
208 -- Define procedures body
209 --------------------------------------------------------------------
210 --------------------------------------------------------------------
211 -- Function name : Get_Visit_End_Time
212 -- Type : Public
213 -- Purpose : To RETURN the End Date for the visit.
214 -- The end date of the visit is the scheduled completion date
215 -- of its visit MWO
216 --
217 -- Parameters :
218 -- p_visit_id Visit ID to fetch the data
219 -- the call is made internally from VWP.
220 -- VWPE: ER:12424063 :: p_use_actuals is no more used
221 --------------------------------------------------------------------
222 FUNCTION Get_Visit_End_Time(
223 p_visit_id IN NUMBER,
224 p_use_actuals IN VARCHAR2 := FND_API.G_TRUE
225 )
226 RETURN DATE
227 IS
228 --
229
230 --PRAKKUM :: 18/04/2012 :: Bug 13095151
231 -- To find visit master WO end date
232 CURSOR c_visit_mwo_end_date (c_id IN NUMBER) IS
233 /*SELECT WIP.scheduled_completion_date
234 FROM WIP_DISCRETE_JOBS WIP, AHL_WORKORDERS WO
235 WHERE WO.VISIT_ID = c_id
236 AND WO.visit_task_id IS NULL
237 AND WO.master_workorder_flag = 'Y'
238 AND WO.wip_entity_id = WIP.wip_entity_id;*/
239 SELECT max(WIP.scheduled_completion_date) AS scheduled_completion_date
240 FROM WIP_DISCRETE_JOBS WIP, AHL_WORKORDERS WO
241 WHERE WO.VISIT_ID = c_id
242 AND WO.STATUS_CODE NOT IN ('7', '22') -- Exclude canceled and deleted work orders
243 AND WO.wip_entity_id = WIP.wip_entity_id;
244
245
246 l_end_date DATE := null; --The visit end date
247 L_API_NAME CONSTANT VARCHAR2(30) := 'Get_Visit_End_Time';
248 L_DEBUG_KEY CONSTANT VARCHAR2(100) := 'ahl.plsql.' || G_PKG_NAME || '.' || L_API_NAME;
249 --
250 BEGIN
251
252 IF (l_log_procedure >= l_log_current_level) THEN
253 fnd_log.string(l_log_procedure,
254 L_DEBUG_KEY ||'.begin',
255 'At the start of PL SQL function. Visit Id = ' || p_visit_id );
256 END IF;
257
258 --Fetch the visit master WO end date
259 OPEN c_visit_mwo_end_date(p_visit_id);
260 FETCH c_visit_mwo_end_date INTO l_end_date;
261 CLOSE c_visit_mwo_end_date;
262
263
264 RETURN l_end_date;
265 END Get_Visit_End_Time;
266
267 -- SKPATHAK :: 02-MAY-2011 :: VWPE: ER:12424063 :: The logic of this procedure is completely changed
268 -- Going forward visit tasks' dates will be dependent on stage planned dates
269 -------------------------------------------------------------------
270 -- Procedure name : Calculate_Task_Times
271 -- Type : Private
272 -- Function : Derive the start and end times/hours of tasks
273 -- and the end_date_time of the visit
274 -- Parameters :
275 --
276 -- Standard OUT Parameters :
277 -- x_return_status OUT VARCHAR2 Required
278 -- x_msg_count OUT NUMBER Required
279 -- x_msg_data OUT VARCHAR2 Required
280 --
281 -- Calculate_Task_Times Parameters:
282 -- p_visit_id IN NUMBER Required
283 -- The id of the visit whose associated tasks' start and end times or hours
284 -- need to be derived
285 -- Version :
286 -- Initial Version 1.0
287 --
288 -------------------------------------------------------------------
289 PROCEDURE Calculate_Task_Times
290 (
294 p_validation_level IN NUMBER := Fnd_Api.G_VALID_LEVEL_FULL,
291 p_api_version IN NUMBER,
292 p_init_msg_list IN VARCHAR2 := Fnd_Api.G_FALSE,
293 p_commit IN VARCHAR2 := Fnd_Api.G_FALSE,
295 x_return_status OUT NOCOPY VARCHAR2,
296 x_msg_count OUT NOCOPY NUMBER,
297 x_msg_data OUT NOCOPY VARCHAR2,
298 p_visit_id IN NUMBER)
299 IS
300 --
301 -- PRAKKUM :: BUG Internal (also fixed #13595460):: 19/01/2012 :: Task Time Adjustments Not Required For Tasks those that have workorders
302 -- already since scheduling will be taken care of by PS if the task has a WO :: START
303 --Cursor for fetching task ids for all the tasks in a visit
304 /*CURSOR get_visit_tasks(x_visit_id IN NUMBER) IS
305 SELECT visit_task_id, stage_id
306 FROM AHL_VISIT_TASKS_B
307 WHERE visit_id = x_visit_id
308 AND status_code = 'PLANNING';*/
309 CURSOR get_visit_tasks(x_visit_id IN NUMBER) IS
313 WHERE tsk.visit_id = x_visit_id
310 SELECT tsk.visit_task_id visit_task_id, tsk.stage_id stage_id
311 FROM AHL_VISIT_TASKS_B tsk
312 , ahl_workorders wo
314 AND tsk.status_code = 'PLANNING'
315 AND tsk.visit_task_id = wo.visit_task_id(+)
316 AND ((wo.workorder_id is NULL)
317 -- SKPATHAK :: Bug 13890788 :: Do schedule the future dated planning tasks
318 OR (tsk.start_date_time > SYSDATE));
319 -- PRAKKUM :: BUG Internal (also fixed #13595460):: 19/01/2012 :: Task Time Adjustments Not Required For Tasks those that have workorders
320 -- already since scheduling will be taken care of by PS if the task has a WO :: END
321
322 -- SKPATHAK :: 12-MAY-2011 :: VWPE: ER:12424063 :: Modified the below cursor
323 --Cursor for fetching visit start and end dates
324 CURSOR get_visit_dates(x_visit_id IN NUMBER) IS
325 SELECT past_dated_visit_flag, start_date_time, close_date_time
326 FROM AHL_VISITS_B
327 WHERE visit_id = x_visit_id;
328 visit_dates_rec get_visit_dates%ROWTYPE;
329
330 -- SKPATHAK :: 02-MAY-2011 :: VWPE: ER:12424063
331 --Cursor for deriving the task's stage dates
332 CURSOR get_stage_details(c_visit_id IN number)
333 IS
334 SELECT planned_start_date, planned_end_date, stage_id
335 from ahl_vwp_stages_b
336 where visit_id = c_visit_id;
337
338
339 -- Define local variables
340 l_api_version CONSTANT NUMBER := 1.0;
341 L_API_NAME CONSTANT VARCHAR2(30) := 'Calculate_Task_Times';
342 L_DEBUG_KEY CONSTANT VARCHAR2(100) := 'ahl.plsql.' || G_PKG_NAME || '.' || L_API_NAME;
343 l_task_start_date DATE; --Task start date
344 l_task_end_date DATE; --Task end date
345 --MANESING::Component Maintenance Planning Project, 07-Aug-2011, added local variable l_is_comp_visit
346 --l_is_comp_visit VARCHAR2(1) := AHL_CMP_UTIL_PKG.Is_Comp_Visit (p_visit_id);--PRAKKUM :: 20/01/2012 :: After necessary changes
347
348 -- Associative array of visit task id to project task ref
349 TYPE Ref_Tbl_Type IS TABLE OF DATE INDEX BY BINARY_INTEGER;
350 l_stage_id_st_dt_map_Tbl Ref_Tbl_Type;
351 l_stage_id_end_dt_map_Tbl Ref_Tbl_Type;
352 --
353 BEGIN
354
355 SAVEPOINT calculate_task_Times_pvt;
356
357 IF (l_log_procedure >= l_log_current_level) THEN
358 fnd_log.string(l_log_procedure,
359 L_DEBUG_KEY ||'.begin',
360 'At the start of PL SQL procedure. Visit Id = ' || p_visit_id);
361 END IF;
362
363 -- Standard call to check for call compatibility
364 IF NOT Fnd_Api.Compatible_API_Call(l_api_version, p_api_version, l_api_name,
365 G_PKG_NAME) THEN
366 RAISE Fnd_Api.G_EXC_UNEXPECTED_ERROR;
367 END IF;
368
369 -- Initialize message list if p_init_msg_list is set to TRUE
370 IF Fnd_Api.To_Boolean(p_init_msg_list) THEN
371 Fnd_Msg_Pub.Initialize;
372 END IF;
373
374 -- Initialize API RETURN status to success
375 x_return_status := Fnd_Api.G_RET_STS_SUCCESS;
376
377 OPEN get_visit_dates (p_visit_id);
378 FETCH get_visit_dates INTO visit_dates_rec;
379 CLOSE get_visit_dates;
380
381 -- Get all the stages' dates in associate arrays indexed by stage id
382 FOR l_stage_rec IN get_stage_details( p_visit_id ) LOOP
383 l_stage_id_st_dt_map_Tbl(l_stage_rec.stage_id) := l_stage_rec.planned_start_date;
384 l_stage_id_end_dt_map_Tbl(l_stage_rec.stage_id) := l_stage_rec.planned_end_date;
385 END LOOP;
386
387 IF (l_log_statement >= l_log_current_level) THEN
388 fnd_log.string(l_log_statement, L_DEBUG_KEY , 'p_visit_id = ' || p_visit_id);
389 END IF;
390
394 /* MANESING::Component Maintenance Planning Project, 07-Aug-2011
391 --Loop through all the tasks of the visit
392 FOR l_task_rec IN get_visit_tasks( p_visit_id ) LOOP
393
395 * For Component Visits, Task Start and End date should be defaulted to SYSDATE, and will
396 * be taken care later off by PS.
397 */
398 /*IF (l_is_comp_visit = 'Y' AND
399 NVL(visit_dates_rec.past_dated_visit_flag, 'N') = 'N') THEN
400 l_task_start_date := SYSDATE;
401 l_task_end_date := SYSDATE;
402 ELSE*/
403 IF l_task_rec.stage_id IS NOT NULL THEN
404 --If the task has a stage associated to it, then get the stage dates stored above in associative arrays
405 l_task_start_date := l_stage_id_st_dt_map_Tbl(l_task_rec.stage_id);
406 l_task_end_date := l_stage_id_end_dt_map_Tbl(l_task_rec.stage_id);
407 ELSE
408 l_task_start_date := visit_dates_rec.start_date_time;
409 l_task_end_date := visit_dates_rec.close_date_time;
410 END IF;
411 /*END IF;*/
412
413 IF (l_log_statement >= l_log_current_level) THEN
414 fnd_log.string(l_log_statement,
415 L_DEBUG_KEY , 'l_task_start_date = ' || to_char(l_task_start_date,'DD-MON-YY hh24:mi:ss'));
416 fnd_log.string(l_log_statement,
417 L_DEBUG_KEY , 'l_task_end_date = ' || to_char(l_task_end_date,'DD-MON-YY hh24:mi:ss'));
418 END IF;
419
420 UPDATE AHL_VISIT_TASKS_B
421 SET START_DATE_TIME = l_task_start_date,
422 END_DATE_TIME = l_task_end_date
423 WHERE visit_task_id = l_task_rec.visit_task_id
424 AND PAST_TASK_START_DATE IS NULL;--Line Added by jrotich for fixing bug #13518519
425 END LOOP;
426
427 -- Standard check of p_commit
428 IF Fnd_Api.TO_BOOLEAN(p_commit) THEN
429 COMMIT WORK;
430 END IF;
431
432 -- Standard call to get message count and if count is 1, get message info
433 Fnd_Msg_Pub.Count_And_Get
434 ( p_count => x_msg_count,
435 p_data => x_msg_data,
436 p_encoded => Fnd_Api.g_false
437 );
438
439 IF (l_log_procedure >= l_log_current_level) THEN
440 fnd_log.string(l_log_procedure,
441 L_DEBUG_KEY ||'.end',
442 'At the end of PL SQL procedure. RETURN Status = ' || x_return_status);
443 END IF;
444
445 EXCEPTION
446 WHEN Fnd_Api.G_EXC_ERROR THEN
447 ROLLBACK TO calculate_task_Times_pvt;
448 x_return_status := Fnd_Api.G_RET_STS_ERROR;
449 Fnd_Msg_Pub.count_and_get( p_count => x_msg_count,
450 p_data => x_msg_data,
451 p_encoded => Fnd_Api.g_false);
452
453 WHEN Fnd_Api.G_EXC_UNEXPECTED_ERROR THEN
454 ROLLBACK TO calculate_task_Times_pvt;
455 x_return_status := Fnd_Api.G_RET_STS_UNEXP_ERROR;
456 Fnd_Msg_Pub.count_and_get( p_count => x_msg_count,
457 p_data => x_msg_data,
458 p_encoded => Fnd_Api.g_false);
459
460 WHEN OTHERS THEN
461 ROLLBACK TO calculate_task_Times_pvt;
465 p_procedure_name => 'Calculate_Task_Times',
462 x_return_status := Fnd_Api.G_RET_STS_UNEXP_ERROR;
463 IF Fnd_Msg_Pub.check_msg_level(Fnd_Msg_Pub.G_MSG_LVL_UNEXP_ERROR) THEN
464 Fnd_Msg_Pub.add_exc_msg(p_pkg_name => G_PKG_NAME,
466 p_error_text => SUBSTR(SQLERRM,1,240));
467 END IF;
468 Fnd_Msg_Pub.count_and_get( p_count => x_msg_count,
469 p_data => x_msg_data,
470 p_encoded => Fnd_Api.g_false);
474 -- Going forward visit tasks' dates will be dependent on stage planned dates
471 END Calculate_Task_Times;
472
473 -- SKPATHAK :: 02-MAY-2011 :: VWPE: ER:12424063 :: The logic of this procedure is completely changed
475 --------------------------------------------------------------------
476 -- Procedure name : Adjust_task_times
477 -- Type : Private
478 -- Purpose : Adjusts tasks times and all dependent task times
479 -- Parameters :
480 --
481 -- Standard OUT Parameters :
482 -- x_return_status OUT VARCHAR2 Required,
483 --
484 -- Adjust_task_Times IN Parameters :
485 -- p_task_id IN NUMBER Required
486 -- VWPE: ER:12424063 : parameters p_reset_sysdate_flag and p_task_start_date will no longer be used
487 -- Version :
488 -- Initial Version 1.0
489 --
490 --------------------------------------------------------------------
491 PROCEDURE Adjust_Task_Times
492 (
493 p_api_version IN NUMBER,
494 p_init_msg_list IN VARCHAR2 := Fnd_Api.G_FALSE,
495 p_commit IN VARCHAR2 := Fnd_Api.G_FALSE,
496 p_validation_level IN NUMBER := Fnd_Api.G_VALID_LEVEL_FULL,
497 x_return_status OUT NOCOPY VARCHAR2,
498 x_msg_count OUT NOCOPY NUMBER,
499 x_msg_data OUT NOCOPY VARCHAR2,
500 p_task_id IN NUMBER,
501 p_reset_sysdate_flag IN VARCHAR2 := FND_API.G_FALSE,
502 p_task_start_date IN DATE := NULL)
503 IS
504 --
505 -- SKPATHAK :: 02-MAY-2011 :: VWPE: ER:12424063 :: Modified the below cursor
506 --Cursor for fetching visit start and end dates
507 -- PRAKKUM :: BUG Internal (also fixed #13595460):: 19/01/2012 :: Task Time Adjustments Not Required For Tasks those that have workorders
508 -- already since scheduling will be taken care of by PS if the task has a WO :: START
509 /*CURSOR get_visit_dets(p_task_id IN NUMBER) IS
510 SELECT v.visit_id, v.past_dated_visit_flag, v.status_code, v.start_date_time, v.close_date_time
511 FROM AHL_VISITS_B v, AHL_VISIT_TASKS_B vt
512 WHERE v.visit_id = vt.visit_id
513 AND vt.visit_task_id = p_task_id;*/
514 CURSOR get_visit_dets(p_task_id IN NUMBER) IS
515 SELECT v.visit_id, v.past_dated_visit_flag, v.status_code, v.start_date_time, v.close_date_time
516 FROM AHL_VISITS_B v, AHL_VISIT_TASKS_B vt
517 , ahl_workorders wo
518 WHERE v.visit_id = vt.visit_id
519 AND vt.visit_task_id = p_task_id
520 AND vt.visit_task_id = wo.visit_task_id(+)
521 AND ((wo.workorder_id is NULL)
522 -- SKPATHAK :: Bug 13890788 :: Do schedule the future dated planning tasks
523 OR (vt.start_date_time > SYSDATE));
524 -- PRAKKUM :: BUG Internal (also fixed #13595460):: 19/01/2012 :: Task Time Adjustments Not Required For Tasks those that have workorders
525 -- already since scheduling will be taken care of by PS if the task has a WO :: END
526
527 visit_dets_rec get_visit_dets%ROWTYPE;
528 --
529 -- SKPATHAK :: 02-MAY-2011 :: VWPE: ER:12424063
530 --Cursor for deriving all the stage dates
531 CURSOR get_stage_dates(p_task_ID IN number)
532 IS
533 SELECT st.planned_start_date, st.planned_end_date
534 from ahl_vwp_stages_b st, ahl_visit_tasks_b vt
535 where st.stage_id = vt.stage_id
536 AND vt.visit_task_id = p_task_id;
537
538 -- Define local variables
539 l_api_version CONSTANT NUMBER := 1.0;
540 L_API_NAME CONSTANT VARCHAR2(30) := 'Adjust_Task_Times';
541 L_DEBUG_KEY CONSTANT VARCHAR2(100) := 'ahl.plsql.' || G_PKG_NAME || '.' || L_API_NAME;
542 l_visit_id NUMBER;
543 l_task_start_date DATE;
544 l_task_end_date DATE;
545 --
546 BEGIN
547
548 SAVEPOINT Adjust_Task_Times_pvt;
549
550 IF (l_log_procedure >= l_log_current_level) THEN
551 fnd_log.string(l_log_procedure,
552 L_DEBUG_KEY ||'.begin',
553 'At the start of PL SQL procedure. Task Id = ' || p_task_id);
554 END IF;
555
556 -- Standard call to check for call compatibility
557 IF NOT Fnd_Api.Compatible_API_Call(l_api_version, p_api_version, l_api_name,
558 G_PKG_NAME) THEN
559 RAISE Fnd_Api.G_EXC_UNEXPECTED_ERROR;
560 END IF;
561
562 -- Initialize message list if p_init_msg_list is set to TRUE
563 IF Fnd_Api.To_Boolean(p_init_msg_list) THEN
564 Fnd_Msg_Pub.Initialize;
565 END IF;
566
567 -- Initialize API RETURN status to success
568 x_return_status := Fnd_Api.G_RET_STS_SUCCESS;
569
570 OPEN get_visit_dets (p_task_id);
571 FETCH get_visit_dets INTO visit_dets_rec;
572 CLOSE get_visit_dets;
573
574 -- PRAKKUM :: BUG Internal (also fixed #13595460):: 19/01/2012 :: Task Time Adjustments Not Required For Tasks those that have workorders
575 -- already since scheduling will be taken care of by PS if the task has a WO
576 IF visit_dets_rec.visit_id IS NOT NULL THEN
577
578 /* MANESING::Component Maintenance Planning Project, 07-Aug-2011
579 * For Component Visits, Task Start and End date should be defaulted to SYSDATE, and will
580 * be taken care later off by PS.
581 */
582 /*IF (AHL_CMP_UTIL_PKG.Is_Comp_Visit (visit_dets_rec.visit_id) = 'Y' AND
583 NVL(visit_dets_rec.past_dated_visit_flag, 'N') = 'N') THEN
584 l_task_start_date := SYSDATE;
585 l_task_end_date := SYSDATE;
586 ELSE*/
587 --Start Processing here
588 OPEN get_stage_dates (p_task_id);
589 FETCH get_stage_dates INTO l_task_start_date, l_task_end_date;
590 IF get_stage_dates%NOTFOUND THEN
594 l_task_end_date := visit_dets_rec.close_date_time;
591 -- If for this task, a corresponding stage record does not exist
592 -- Task start and end dates will be the visit's start and end dates
593 l_task_start_date := visit_dets_rec.start_date_time;
595 -- SKPATHAK :: 07-SEP-2011 :: VWPE ER: 12856858
596 -- If the visit is not a past dated visit and is not in Draft status, task's start date should be the sysdate
597 IF ((visit_dets_rec.status_code IN ('PLANNING', 'PARTIALLY RELEASED', 'RELEASED'))
598 AND (NVL(visit_dets_rec.past_dated_visit_flag, 'N') <> 'Y')) THEN
602 END IF;
599 IF (l_task_start_date < sysdate AND p_task_start_date IS NULL) THEN
600 l_task_start_date := sysdate;
601 END IF;
603 END IF;
604 CLOSE get_stage_dates;
605 /*END IF;*/
606
607 IF (p_task_start_date IS NOT NULL) THEN
608 l_task_start_date := p_task_start_date;
609 END IF;
610
611 UPDATE AHL_VISIT_TASKS_B
612 SET START_DATE_TIME = l_task_start_date,
616 END IF; -- End if visit_dets_rec.visit_id
613 END_DATE_TIME = l_task_end_date
614 WHERE visit_task_id = p_task_id;
615
617
618 -- Standard check of p_commit
619 IF Fnd_Api.TO_BOOLEAN(p_commit) THEN
620 COMMIT WORK;
621 END IF;
622
623 -- Standard call to get message count and if count is 1, get message info
624 Fnd_Msg_Pub.Count_And_Get
625 ( p_count => x_msg_count,
626 p_data => x_msg_data,
627 p_encoded => Fnd_Api.g_false
628 );
629
630 IF (l_log_procedure >= l_log_current_level) THEN
631 fnd_log.string(l_log_procedure,
632 L_DEBUG_KEY ||'.end',
633 'At the end of PL SQL procedure. RETURN Status = ' || x_return_status);
634 END IF;
635
636 EXCEPTION
637 WHEN Fnd_Api.G_EXC_ERROR THEN
638 ROLLBACK TO Adjust_Task_Times_pvt;
639 x_return_status := Fnd_Api.G_RET_STS_ERROR;
640 Fnd_Msg_Pub.count_and_get( p_count => x_msg_count,
641 p_data => x_msg_data,
642 p_encoded => Fnd_Api.g_false);
643
644 WHEN Fnd_Api.G_EXC_UNEXPECTED_ERROR THEN
645 ROLLBACK TO Adjust_Task_Times_pvt;
646 x_return_status := Fnd_Api.G_RET_STS_UNEXP_ERROR;
647 Fnd_Msg_Pub.count_and_get( p_count => x_msg_count,
648 p_data => x_msg_data,
649 p_encoded => Fnd_Api.g_false);
650
651 WHEN OTHERS THEN
652 ROLLBACK TO Adjust_Task_Times_pvt;
653 x_return_status := Fnd_Api.G_RET_STS_UNEXP_ERROR;
654 IF Fnd_Msg_Pub.check_msg_level(Fnd_Msg_Pub.G_MSG_LVL_UNEXP_ERROR) THEN
655 Fnd_Msg_Pub.add_exc_msg(p_pkg_name => G_PKG_NAME,
656 p_procedure_name => 'Adjust_Task_Times',
657 p_error_text => SUBSTR(SQLERRM,1,240));
658 END IF;
659 Fnd_Msg_Pub.count_and_get( p_count => x_msg_count,
660 p_data => x_msg_data,
661 p_encoded => Fnd_Api.g_false);
662 END Adjust_Task_Times;
663
664 --SKPATHAK :: 02-MAY-2011 :: VWPE: ER:12424063
665 -- Commented out the below procedure
666 -----------------------------------------------------------------------------
667 --Function: Private Method for recursion of task derivation
668 ------------------------------------------------------------------------------
669
670 -- SKPATHAK :: Bug 8343599 :: 14-APR-2009
671 -- Added the optional in param p_task_start_date
672 /*PROCEDURE Adjust_Task_Times_Internal(p_task_id IN NUMBER,
673 p_task_start_date IN DATE default NULL)
674 IS
675 --Fetch basic visit data
676 CURSOR get_task_data_csr(p_task_id IN NUMBER) IS
677 SELECT vt.start_date_time, vt.end_date_time,
678 nvl(vt.department_id, v.department_id),
679 nvl(vt.duration, NVL(Get_task_duration(vt.quantity, routes.route_id), 0)) duration,
680 vt.stage_id,
681 nvl(vt.start_from_hour, 0)
682 FROM AHL_VISIT_TASKS_B vt, AHL_VISITS_B v,
683 ahl_routes_app_v routes, ahl_mr_routes_app_v mr
684 WHERE vt.visit_id = v.visit_id
685 AND routes.route_id (+) = mr.route_id
686 AND mr.mr_route_id (+)= vt.mr_route_id
687 AND vt.visit_task_id = p_task_id
688 AND vt.task_type_code <> 'SUMMARY'
689 AND (VT.STATUS_CODE IS NULL OR VT.STATUS_CODE <> 'DELETED');
690 --
691 --This cursor uses the technical dependencies and finds the parent task
692 -- that finishes last.
693 CURSOR get_tech_dependency_csr(p_task_id IN NUMBER) IS
694 SELECT max(vt.end_date_time)
695 FROM AHL_VISIT_TASKS_B vt, AHL_TASK_LINKS tl
696 WHERE vt.visit_task_id = tl.parent_task_id
697 AND tl.visit_task_id = p_task_id;
698 --
699 --Cursor which fetches all child technical dependent tasks
700 CURSOR get_child_dependency_csr(p_task_id IN NUMBER) IS
701 SELECT visit_task_id
702 FROM AHL_TASK_LINKS
703 WHERE parent_task_id = p_task_id;
704 --
705 l_old_task_start DATE; --Existing task start time
706 l_old_task_end DATE; --Existing task end time
707 l_task_dept_id NUMBER; --Existing task department
708 l_task_duration NUMBER; --Tasks duration
709 l_task_stage_id NUMBER; --Tasks stage id
710 l_task_offset NUMBER; --Number of hours offset based on stage.
711 l_start_date DATE; --Newly derived task start date
712 l_end_date DATE; --Newly derived task end date
713 l_temp_date DATE; --Temporary date for comparison purposes
714 l_child_task_id NUMBER; --Child task id
715 L_API_VERSION CONSTANT NUMBER := 1.0;
716 L_API_NAME CONSTANT VARCHAR2(30) := 'Adjust_Task_Times_Internal';
717 L_DEBUG_KEY CONSTANT VARCHAR2(100) := 'ahl.plsql.' || G_PKG_NAME || '.' || L_API_NAME;
718 --
719 BEGIN
720 IF (l_log_procedure >= l_log_current_level) THEN
721 fnd_log.string(l_log_procedure,
722 L_DEBUG_KEY ||'.begin',
723 'At the start of PL SQL procedure. Visit Task Id = ' || p_task_id);
724 END IF;
725
726 -- 1. Fetch the task's old info and task related info
727 OPEN get_task_data_csr(p_task_id);
728 FETCH get_task_data_csr INTO l_old_task_start, l_old_task_end, l_task_dept_id,
729 l_task_duration, l_task_stage_id , l_task_offset;
730
731 --If it is a valid task
732 IF (get_task_data_csr%FOUND) THEN
733
734 --2. Check the task stage time value
735 --Get stage offset if stage id is not null
736 -- SKPATHAK :: Bug 8343599 :: 14-APR-2009 :: Begin
737 -- If a task start date has been passed (for Non Routines), use that date as the baseline
738 -- to calculate the actual start time of the task.
739 IF (p_task_start_date IS NOT NULL) THEN
740 l_start_date := p_task_start_date;
741 ELSE
745 l_start_date := Compute_Date (l_start_date, G_VISIT_DEPT_ID, G_STAGES_TBL(l_task_stage_id));
742 l_start_date := G_VISIT_START_DATE;
743 END IF;
744 IF (l_task_stage_id IS NOT NULL) THEN
746 END IF;
747 -- SKPATHAK :: Bug 8343599 :: 14-APR-2009 :: End
748
749
750 --Adjust the start date to the task offset
751 l_start_date := Compute_Date (l_start_date, nvl(l_task_dept_id, G_VISIT_DEPT_ID), l_task_offset);
752
756 IF (get_tech_dependency_csr%FOUND) THEN
753 --3. Find the max end time of the dependent parents. Adjust based on parent
754 OPEN get_tech_dependency_csr(p_task_id);
755 FETCH get_tech_dependency_csr INTO l_temp_date;
757 l_temp_date := Compute_Date (l_temp_date, nvl(l_task_dept_id, G_VISIT_DEPT_ID),0);
758
759 --If shift adjusted dependency date is later than stage offset date
760 IF (l_temp_date > l_start_date) THEN
761 l_start_date := l_temp_date;
762 END IF;
763 END IF;
764 CLOSE get_tech_dependency_csr;
765
766 --Adjust start date for partially released/released tasks to sysdate start date
767 IF ((G_VISIT_STATUS = 'PARTIALLY RELEASED' OR G_VISIT_STATUS = 'RELEASED')
768 AND Fnd_Api.TO_BOOLEAN(G_RESET_SYSDATE_FLAG)) THEN
769 IF (l_start_date < sysdate AND p_task_start_date IS NULL) THEN
770 l_start_date := COMPUTE_DATE(sysdate, nvl(l_task_dept_id, G_VISIT_DEPT_ID),0);
771 END IF;
772 END IF;
773
774 --4. Now derive the end date
775 l_end_date := compute_date(l_start_date, l_task_dept_id, l_task_duration);
776 IF (l_log_statement >= l_log_current_level) THEN
777 fnd_log.string(l_log_statement,
778 L_DEBUG_KEY,
779 'Start Date = ' || l_start_date ||
780 '. End Date = ' || l_end_date);
781 END IF;
782
783 --5. Update the record if it has been changed and call child tasks
784 IF (l_old_task_start IS NULL OR
785 l_old_task_end IS NULL OR
786 l_start_date <> l_old_task_start OR
787 l_end_date <> l_old_task_end) THEN
788
789 --3d Update the tasks table with the new dates and times.
790 UPDATE AHL_VISIT_TASKS_B
791 SET START_DATE_TIME = l_start_date,
792 END_DATE_TIME = l_end_date
793 WHERE visit_task_id = p_task_id;
794
795 --If the end date has changed, recursively call the child tasks
796 -- To see if they need to be adjusted. Potentially expensive.
797 IF (l_old_task_end IS NULL OR
798 l_end_date <> l_old_task_end) THEN
799 OPEN get_child_dependency_csr(p_task_id);
800 LOOP
801 FETCH get_child_dependency_csr INTO l_child_task_id;
802 EXIT WHEN get_child_dependency_csr%NOTFOUND;
803 IF (l_log_statement >= l_log_current_level) THEN
804 fnd_log.string(l_log_statement,
805 L_DEBUG_KEY,
806 'Calling child task: ' || l_child_task_id);
807 END IF;
808 Adjust_Task_Times_Internal(l_child_task_id);
809 END LOOP;
810 CLOSE get_child_dependency_csr;
811 END IF;
812 END IF; --start time has changed;
813
814 END IF; --end found
815 CLOSE get_task_data_csr;
816
817 IF (l_log_procedure >= l_log_current_level) THEN
818 fnd_log.string(l_log_procedure,
819 L_DEBUG_KEY ||'.end',
820 'At the end of PL SQL procedure.');
821 END IF;
822
823 END Adjust_Task_Times_Internal;*/
824 -------------------------------------------------------------------
825 -- Procedure name : Calculate_Task_Times_For_Dept
826 -- Type : Private
827 -- Function : Recalculate all Visits for Dept for Task Times
828 -- Parameters :
829 --
830 -- Standard OUT Parameters :
831 -- x_return_status OUT VARCHAR2 Required
832 -- x_msg_count OUT NUMBER Required
833 -- x_msg_data OUT VARCHAR2 Required
834 --
835 -- Derive_Visit_Task_Times Parameters:
836 -- p_dept_id IN NUMBER Required
837 -- The dept id which need to have all its visits recalculated.
838 -- Need to be called from concurrent program due to performance issues.
839 --
840 -------------------------------------------------------------------
841 PROCEDURE Calculate_Task_Times_For_Dept
842 (
843 p_api_version IN NUMBER,
844 p_init_msg_list IN VARCHAR2 := Fnd_Api.G_FALSE,
845 p_commit IN VARCHAR2 := Fnd_Api.G_FALSE,
846 p_validation_level IN NUMBER := Fnd_Api.G_VALID_LEVEL_FULL,
847 x_return_status OUT NOCOPY VARCHAR2,
851 IS
848 x_msg_count OUT NOCOPY NUMBER,
849 x_msg_data OUT NOCOPY VARCHAR2,
850 p_dept_id IN NUMBER)
852 --
853 CURSOR get_all_visits_csr(p_dept_id IN NUMBER) IS
854 SELECT visit_id
855 FROM AHL_VISITS_B
856 WHERE DEPARTMENT_ID = p_dept_id
857 UNION
858 SELECT visit_id
859 FROM AHL_VISIT_TASKS_B
860 WHERE department_id = p_dept_id;
861 --
862 -- Define local variables
863 l_api_version CONSTANT NUMBER := 1.0;
864 L_API_NAME CONSTANT VARCHAR2(30) := 'Calculate_Task_Times_For_Dept';
865 L_DEBUG_KEY CONSTANT VARCHAR2(100) := 'ahl.plsql.' || G_PKG_NAME || '.' || L_API_NAME;
866 l_visit_id NUMBER;
867 --
868 BEGIN
869 SAVEPOINT Calculate_Times_for_dept_pvt;
870
871 IF (l_log_procedure >= l_log_current_level) THEN
872 fnd_log.string(l_log_procedure,
873 L_DEBUG_KEY ||'.begin',
874 'At the start of PL SQL procedure. Dept Id = ' || p_dept_id);
875 END IF;
876
877 -- Standard call to check for call compatibility
878 IF NOT Fnd_Api.Compatible_API_Call(l_api_version, p_api_version, l_api_name,
879 G_PKG_NAME) THEN
880 RAISE Fnd_Api.G_EXC_UNEXPECTED_ERROR;
881 END IF;
882
883 -- Initialize message list if p_init_msg_list is set to TRUE
884 IF Fnd_Api.To_Boolean(p_init_msg_list) THEN
885 Fnd_Msg_Pub.Initialize;
886 END IF;
887
888 -- Initialize API RETURN status to success
889 x_return_status := Fnd_Api.G_RET_STS_SUCCESS;
890
891 OPEN get_all_visits_csr(p_dept_id);
892 LOOP
893 FETCH get_all_visits_csr INTO l_visit_id;
894 EXIT WHEN get_all_visits_csr%NOTFOUND;
895
896 IF (l_log_statement >= l_log_current_level) THEN
897 fnd_log.string(l_log_statement,
898 L_DEBUG_KEY,
899 'Before calling CALCULATE_TASK_TIMES.');
900 END IF;
901
902 Calculate_Task_Times(p_api_version => 1.0,
903 p_init_msg_list => Fnd_Api.G_FALSE,
904 p_commit => Fnd_Api.G_FALSE,
905 p_validation_level => Fnd_Api.G_VALID_LEVEL_FULL,
906 x_return_status => x_return_status,
907 x_msg_count => x_msg_count,
911 IF (l_log_statement >= l_log_current_level) THEN
908 x_msg_data => x_msg_data,
909 p_visit_id => l_visit_id);
910
912 fnd_log.string(l_log_statement,
913 L_DEBUG_KEY,
914 'After calling CALCULATE_TASK_TIMES. RETURN Status = ' ||
915 x_return_status);
916 END IF;
917
918 IF (x_return_status <> FND_API.G_RET_STS_SUCCESS) THEN
919 IF (l_log_statement >= l_log_current_level) THEN
920 fnd_log.string(l_log_statement,
921 L_DEBUG_KEY,
922 'Errors from CALCULATE_TASK_TIMES. Message count: ' ||
923 x_msg_count || ', message data: ' || x_msg_data);
924 END IF;
925 CLOSE get_all_visits_csr;
926 IF x_return_status = FND_API.G_RET_STS_ERROR THEN
927 RAISE FND_API.G_EXC_ERROR;
928 ELSE
929 RAISE FND_API.G_EXC_UNEXPECTED_ERROR;
930 END IF;
931 END IF;
932 END LOOP;
933 CLOSE get_all_visits_csr;
934
935 -- Standard check of p_commit
936 IF Fnd_Api.TO_BOOLEAN(p_commit) THEN
937 COMMIT WORK;
938 END IF;
939
940 -- Standard call to get message count and if count is 1, get message info
941 Fnd_Msg_Pub.Count_And_Get
942 ( p_count => x_msg_count,
943 p_data => x_msg_data,
944 p_encoded => Fnd_Api.g_false
945 );
946
947 IF (l_log_procedure >= l_log_current_level) THEN
948 fnd_log.string(l_log_procedure,
949 L_DEBUG_KEY ||'.end',
950 'At the end of PL SQL procedure. RETURN Status = ' || x_return_status);
951 END IF;
952
953 EXCEPTION
954 WHEN Fnd_Api.G_EXC_ERROR THEN
955 ROLLBACK TO calculate_Times_for_dept_pvt;
956 x_return_status := Fnd_Api.G_RET_STS_ERROR;
957 Fnd_Msg_Pub.count_and_get( p_count => x_msg_count,
958 p_data => x_msg_data,
959 p_encoded => Fnd_Api.g_false);
960
961 WHEN Fnd_Api.G_EXC_UNEXPECTED_ERROR THEN
962 ROLLBACK TO calculate_Times_for_dept_pvt;
963 x_return_status := Fnd_Api.G_RET_STS_UNEXP_ERROR;
964 Fnd_Msg_Pub.count_and_get( p_count => x_msg_count,
968 WHEN OTHERS THEN
965 p_data => x_msg_data,
966 p_encoded => Fnd_Api.g_false);
967
969 ROLLBACK TO calculate_Times_for_dept_pvt;
970 x_return_status := Fnd_Api.G_RET_STS_UNEXP_ERROR;
971 IF Fnd_Msg_Pub.check_msg_level(Fnd_Msg_Pub.G_MSG_LVL_UNEXP_ERROR) THEN
972 Fnd_Msg_Pub.add_exc_msg( p_pkg_name => G_PKG_NAME,
973 p_procedure_name => 'Calculate_Task_Times_For_Dept',
974 p_error_text => SUBSTR(SQLERRM,1,240));
975 END IF;
976 Fnd_Msg_Pub.count_and_get( p_count => x_msg_count,
977 p_data => x_msg_data,
978 p_encoded => Fnd_Api.g_false);
979 END Calculate_Task_Times_For_Dept;
980
981 ----------------END OF TASK/VISIT SECTION ----------------------------------------
982
983
984 ----------------START OF SHIFT DATE TIME SECTION------------------------------------
985
986 -----------------------------------------------------------
987 -- Function Name: Compute_Date
988 -- Type: Private
989 -- Function: Converts date+duration and department into end date/time
990 -- Has to be super-efficient as this is called repeatedly.
991 -- Performance tuning is top priority for this method.
992 -- - no debug code or debug checks.
993 -- - code inlined as much as possible, use memory instead of queries
994 --
995 -- Compute_Date Parameters:
996 -- p_start_date IN NUMBER Required
997 -- The start date of the compute date value
998 -- p_dept_id IN NUMBER Required
999 -- The department id to calculate the date for
1000 -- p_duration IN NUMBER
1001 -- The duration in hours of the tasks offset
1002 -- RETURNs the department shift adjusted end date of p_start_date+p_duration
1003 --
1004 ---------------------------------------------------------
1005 FUNCTION Compute_Date(
1006 p_start_date date,
1007 p_dept_id number,
1008 p_duration number)
1009 RETURN DATE
1010 IS
1011 --
1012 --Get the number of holiday days that falls under the duration
1013 CURSOR get_holiday_csr(p_department_id IN NUMBER,
1014 p_start_date IN DATE,
1015 p_end_date IN DATE) IS
1016 SELECT COUNT(ex.EXCEPTION_DATE)
1017 FROM bom_calendar_exceptions ex, ahl_department_shifts dept
1018 WHERE ex.CALENDAR_CODE = dept.calendar_code
1019 AND EXCEPTION_TYPE = G_HOLIDAY_TYPE
1020 AND dept.department_id = p_department_id
1021 AND ex.exception_date > p_start_date
1022 AND ex.exception_date <= p_end_date;
1023 --
1024 l_start_date DATE; -- The p_start_date adjusted for different department.
1025 l_shift_start_date DATE; --The date that the shift started.
1026 l_end_date DATE; --The end date that is being calculated.
1027 l_end_hour NUMBER; --Ending hour
1028 l_curr_wday NUMBER; --current wday
1029 l_num_of_weekends NUMBER; --Number of weekends (multiple by off days)
1030 l_mod_days NUMBER; --Modulo workdays for given weekend
1031 l_days_to_add NUMBER; --Number of holiday days to add.
1032 l_shift_duration NUMBER; --The shift duration in UOM of day
1033 l_shift_off_time NUMBER; --The duration of shift off-time in UOM of day :: PRAKKUM :: AVF :: 15/11/2011 :: Bug# 13416865 - AVF ENHANCEMENTS
1034 l_end_date_wo_offset DATE; --The end date calculated without hours part added to it. :: PRAKKUM :: AVF :: 15/11/2011 :: Bug# 13416865 - AVF ENHANCEMENTS
1035 L_API_NAME CONSTANT VARCHAR2(30) := 'Compute_Date';
1036 L_DEBUG_KEY CONSTANT VARCHAR2(100) := 'ahl.plsql.' || G_PKG_NAME || '.' || L_API_NAME;
1037 --
1038 BEGIN
1039
1040 IF (l_log_procedure >= l_log_current_level) THEN
1041 fnd_log.string(l_log_procedure,
1042 L_DEBUG_KEY ||'.begin',
1043 'At the start of PL SQL function. Start Date = ' || p_start_date ||
1044 ', Department Id = ' || p_dept_id || ', Duration = ' || p_duration);
1045 END IF;
1046
1047 --1. Re-init the department shift time data if not the same
1048 -- as the current cached department
1049 IF (G_CURRENT_DEPT_ID IS NULL or
1050 p_dept_id <> G_CURRENT_DEPT_ID) THEN
1051 Init_Shift_Data(p_dept_id);
1052 G_CURRENT_DEPT_ID := p_dept_id;
1053 END IF;
1054
1055 --2. Adjust the start date to department shift
1056 l_start_date := p_start_date;
1057
1058 --Reset the start time to shift start hour if shift starts after current time.
1059 IF (G_SHIFT_END < G_SHIFT_START) THEN
1060 IF (l_start_date < TRUNC(l_start_date) + G_SHIFT_START AND
1061 l_start_date > TRUNC(l_start_date) + G_SHIFT_END) THEN-- Equal changed to special case handle :: PRAKKUM :: AVF :: 15/11/2011 :: Bug# 13416865 - AVF ENHANCEMENTS
1062 l_start_date := trunc(l_start_date)+ G_SHIFT_START;
1063 ELSIF (l_start_date = TRUNC(l_start_date) + G_SHIFT_END) THEN
1064 l_start_date := trunc(l_start_date)+ G_SHIFT_START;
1065 END IF;
1066 ELSE
1067 IF (l_start_date < TRUNC(l_start_date) + G_SHIFT_START) THEN
1068 l_start_date := trunc(l_start_date)+ G_SHIFT_START;
1069 ELSIF (l_start_date >= TRUNC(l_start_date) + G_SHIFT_END) THEN
1070 --Add another day if shift starts tomorrow
1071 l_start_date := trunc(l_start_date)+ 1 + G_SHIFT_START;
1072 END IF;
1073 END IF;
1074
1075 --If it's an overnight shift and time is before shift ends on the same day,
1076 --the shift started a day earlier
1077 l_shift_start_date := get_shift_start_date(l_start_date);
1078
1079 l_curr_wday := MOD((TRUNC(l_shift_start_date) - G_CAL_START), (G_DAYS_ON + G_DAYS_OFF));
1080
1081 -- Add to a day_on (basically, passed the weekend)
1085
1082 IF(l_curr_wday +1 > G_DAYS_ON) THEN
1083 l_start_date := TRUNC(l_shift_start_date + (G_DAYS_ON+G_DAYS_OFF-l_curr_wday))+G_SHIFT_START;
1084 END IF;
1086 --If it's an overnight shift and time is before shift ends on the same day,
1087 --the shift started a day earlier
1088 l_shift_start_date := get_shift_start_date(l_start_date);
1089
1090 -- Not Day Off: Check if holiday and adjust based on holiday
1091 WHILE (Is_Dept_Holiday(l_shift_start_date)) LOOP
1092 l_shift_start_date := l_shift_start_date+1;
1093
1094 l_curr_wday := MOD((l_shift_start_date - G_CAL_START), (G_DAYS_ON + G_DAYS_OFF));
1095 -- Add to a day_on (basically, passed the weekend)
1096 IF(l_curr_wday +1 > G_DAYS_ON) THEN
1097 l_shift_start_date := l_shift_start_date + (G_DAYS_ON+G_DAYS_OFF-l_curr_wday);
1098 END IF;
1099
1100 l_start_date := TRUNC(l_shift_start_date) + G_SHIFT_START;
1101 END LOOP;
1102
1103 IF (p_duration = 0) THEN
1104 -- If duration is 0, RETURN the adjusted start date.
1105 IF (l_log_procedure >= l_log_current_level) THEN
1106 fnd_log.string(l_log_procedure,
1107 L_DEBUG_KEY ||'.end',
1108 'At the end of PL SQL function. Start Date = ' || l_start_date);
1109 END IF;
1110 RETURN l_start_date;
1111
1112 ELSE
1113
1114 --1. derive shift duration
1115 IF(G_SHIFT_END < G_SHIFT_START) THEN
1116 l_shift_duration := (24*G_SHIFT_END + 24 - 24*G_SHIFT_START)/24;
1117 l_shift_off_time := (G_SHIFT_START-G_SHIFT_END);-- PRAKKUM :: AVF :: 15/11/2011 :: Bug# 13416865 - AVF ENHANCEMENTS
1118 ELSE
1119 l_shift_duration := (24*G_SHIFT_END - 24*G_SHIFT_START)/24;
1120 l_shift_off_time := 1-(G_SHIFT_END-G_SHIFT_START);-- PRAKKUM :: AVF :: 15/11/2011 :: Bug# 13416865 - AVF ENHANCEMENTS
1121 END IF;
1122
1123 --2. Calculate the end date based on the start and duration. p_duration is in hours
1124 -- so must divide duration/24 to get fractions of a day
1125 --Both the p_duration/24 and the l_shift_duration are stored as fraction of a day
1126
1127 --If the border condition of p_duration/shift_duration is an exact number of days and
1128 -- l_start_date equals the shift start date. Then set end date to shift end time
1129 -- and date to n-1 days from start date
1130 --SKPATHAK :: Bug 8322149 :: 20-APR-2009
1131 --Changed the condition from (MOD(p_duration/24, l_shift_duration)=0 to (MOD(p_duration, l_shift_duration*24)=0
1132 -- PRAKKUM :: AVF :: 15/11/2011 :: Bug# 13416865 - AVF ENHANCEMENTS
1133 IF (MOD(p_duration, l_shift_duration*24) =0
1134 AND l_start_date-TRUNC(l_start_date)= G_SHIFT_START) THEN
1135 --The end date calculated without hours part added to it.
1136
1137 l_end_date_wo_offset := l_start_date + (TRUNC((p_duration)/(l_shift_duration*24)))-1;
1138 --l_end_date_wo_offset := l_start_date + (TRUNC((p_duration/24)/l_shift_duration));
1139
1140 --The end date calculated with hours part added to it.
1141 l_end_date := l_end_date_wo_offset+l_shift_duration;
1142 ELSE
1143 --The end date calculated without hours part added to it.
1144 l_end_date_wo_offset := l_start_date + TRUNC((p_duration/24)/l_shift_duration);
1145 --The end date calculated with hours part added to it.
1146 l_end_date := l_end_date_wo_offset + MOD(p_duration/24, l_shift_duration);
1147 END IF;
1148
1149 --Get the end hour as fraction of a day
1150 l_end_hour := l_end_date - TRUNC(l_end_date);
1151
1152 /*
1153 --3. If new end hours goes beyond shift end hour, adjust til next shift
1154 IF (l_end_hour > G_SHIFT_END) THEN
1155 --Adjust end hour into the next shift. So if end_hour is 9pm and shift end is 5pm.
1156 -- Then must add 4 hours to shift start time. (all in fraction of a day)
1157 --This value could be > 1 for overnight shifts
1158 l_end_hour := G_SHIFT_START + (l_end_hour-G_SHIFT_END);
1159
1160 --Add a day to the end date if the shift ends after start time. (Regular shift)
1161 IF (G_SHIFT_START < G_SHIFT_END) THEN
1162 l_end_date := TRUNC(l_end_date)+1+l_end_hour;
1163 ELSE
1164 l_end_date := TRUNC(l_end_date)+l_end_hour;
1165 END IF;
1166 END IF;*/-- PRAKKUM :: AVF :: 15/11/2011 :: Bug# 13416865 - AVF ENHANCEMENTS
1167
1168 --If end date that calculated after adding hours part falls beyond the shift end, then
1169 --add off time hours to shift the end time.
1170 -- PRAKKUM :: AVF :: 15/11/2011 :: Bug# 13416865 - AVF ENHANCEMENTS
1171 IF l_end_date > (get_shift_start_date(l_end_date_wo_offset)+G_SHIFT_END) THEN
1172 l_end_date := l_end_date + l_shift_off_time;
1173 END IF;
1174
1175 --Add in the days off
1176 --First calculate number of days off
1177 l_num_of_weekends := TRUNC((get_shift_start_date(l_end_date)-get_shift_start_date(l_start_date))/G_DAYS_ON);
1178
1179 --This gets the number of extra days over week so that we can check if we get an extra weekend.
1180 l_mod_days :=MOD(get_shift_start_date(l_end_date)- get_shift_start_date(l_start_date), G_DAYS_ON);
1181
1182 --This is the weekday of the start day.
1183 l_curr_wday := MOD(get_shift_start_date(l_start_date) - G_CAL_START, G_DAYS_ON + G_DAYS_OFF);
1184
1185 --If the extra days pushes into an extra week, add 1 more weekend.
1186 IF(l_curr_wday+l_mod_days+1>G_DAYS_ON) THEN
1187 l_num_of_weekends := l_num_of_weekends +1;
1188 END IF;
1189 l_end_date := l_end_date + G_DAYS_OFF * l_num_of_weekends;
1190
1191 --Add in the holidays if not already added
1192 l_days_to_add := 0;
1193 OPEN get_holiday_csr(p_dept_id, get_shift_start_date(l_start_date),
1197
1194 get_shift_start_date(l_end_date));
1195 FETCH get_holiday_csr INTO l_days_to_add;
1196 CLOSE get_holiday_csr;
1198 WHILE (l_days_to_add > 0) LOOP
1199 --Increment and decrement the days
1200 l_end_date := l_end_date +1;
1201 l_days_to_add := l_days_to_add -1;
1202
1203 --Skip the weekends and the additional holidays
1204 l_curr_wday := MOD(get_shift_start_date(l_end_date)- G_CAL_START, G_DAYS_ON + G_DAYS_OFF);
1205
1206 IF (l_curr_wday+1 > G_DAYS_ON) THEN
1207 l_days_to_add := l_days_to_add + 1;
1208 ELSIF(Is_Dept_Holiday(get_shift_start_date(l_end_date))) THEN
1209 l_days_to_add := l_days_to_add + 1;
1210 END IF;
1211 END LOOP;
1212
1213 -- PRAKKUM :: AVF :: 15/11/2011 :: Bug# 13416865 - AVF ENHANCEMENTS
1214 --If end time is exact match to shift start time then based on shift timings move it to before day end time.
1215 IF (l_end_date = trunc(l_end_date)+G_SHIFT_START) THEN
1216 IF G_SHIFT_START < G_SHIFT_END THEN--Regular Shift
1217 l_end_date := trunc(l_end_date) -1 + G_SHIFT_END;
1218 ELSE -- Night Shift
1219 l_end_date := trunc(l_end_date) + G_SHIFT_END;
1220 END IF;
1221 END IF;
1222
1223 --RETURN the derived end date.
1224 IF (l_log_procedure >= l_log_current_level) THEN
1225 fnd_log.string(l_log_procedure,
1226 L_DEBUG_KEY ||'.end',
1227 'At the end of PL SQL function. End Date = ' || l_end_date);
1228 END IF;
1229 RETURN l_end_date;
1230
1231 END IF;
1232
1233 END Compute_Date;
1234
1235 ------------------------------------------------------------------------------
1236 -- Private function that initializes global variables for computing date/time
1237 ------------------------------------------------------------------------------
1238 PROCEDURE Init_Shift_Data(p_department_id number)
1239 IS
1240 -- Define local cursors
1241 --Find all shift information into the local variables
1242 CURSOR l_shift_info_csr(p_department_id IN NUMBER) IS
1243 SELECT TRUNC(cal.CALENDAR_START_DATE), TRUNC(cal.CALENDAR_END_DATE),
1244 times.FROM_TIME/G_SECS_IN_DAY, times.TO_TIME/G_SECS_IN_DAY,
1245 pattern.DAYS_ON, pattern.DAYS_OFF
1246 FROM bom_shift_times times, bom_workday_patterns pattern,
1247 bom_calendars cal, ahl_department_shifts dept
1248 WHERE dept.calendar_code = times.calendar_code
1249 AND dept.shift_num = times.shift_num
1250 AND pattern.calendar_code = dept.calendar_code
1251 AND pattern.shift_num = dept.shift_num
1252 AND pattern.seq_num = dept.seq_num -- PRAKKUM :: 01/03/2012 :: Bug 13793787
1253 AND cal.calendar_code = dept.calendar_code
1254 AND dept.department_id = p_department_id;
1255 --
1256 -- Fetch the holidays for the given department
1257 CURSOR l_exceptions_csr(p_department_id IN NUMBER) IS
1258 SELECT ex.EXCEPTION_DATE
1259 FROM bom_calendar_exceptions ex, ahl_department_shifts dept
1260 WHERE ex.CALENDAR_CODE = dept.calendar_code
1261 AND EXCEPTION_TYPE = G_HOLIDAY_TYPE
1262 AND dept.department_id = p_department_id
1263 ORDER BY EXCEPTION_DATE;
1264 --
1265 l_temp_index NUMBER;
1266 l_temp_date DATE;
1267 L_API_NAME CONSTANT VARCHAR2(30) := 'Init_Shift_Data';
1268 L_DEBUG_KEY CONSTANT VARCHAR2(100) := 'ahl.plsql.' || G_PKG_NAME || '.' || L_API_NAME;
1269 --
1270 BEGIN
1271
1272 IF (l_log_procedure >= l_log_current_level) THEN
1273 fnd_log.string(l_log_procedure,
1274 L_DEBUG_KEY ||'.begin',
1275 'At the start of PL SQL procedure. Department Id = ' || p_department_id);
1276 END IF;
1277 -- Get the department informations
1278 OPEN l_shift_info_csr(p_department_id);
1279 FETCH l_shift_info_csr INTO G_CAL_START, G_CAL_END,
1280 G_SHIFT_START, G_SHIFT_END,
1281 G_DAYS_ON, G_DAYS_OFF;
1282 IF (l_shift_info_csr%NOTFOUND) THEN
1283 --Fnd_Message.Set_Name('AHL','AHL_LTP_NO_SHIFT_FOR_DEPT');
1284 --Fnd_Message.Set_Token('DEPT_ID', p_department_id);
1285 --Fnd_Msg_Pub.ADD;
1286 CLOSE l_shift_info_csr;
1287 RETURN; --RAISE Fnd_Api.G_EXC_UNEXPECTED_ERROR;
1291 -- Get the Exception (Holidays) days
1288 END IF;
1289 CLOSE l_shift_info_csr;
1290
1292 OPEN l_exceptions_csr(p_department_id);
1293 l_temp_index := 1;
1294 LOOP
1295 FETCH l_exceptions_csr INTO l_temp_date;
1296 EXIT WHEN l_exceptions_csr%NOTFOUND;
1297 G_EXCEPTION_DATES_TBL(l_temp_index) := TRUNC(l_temp_date);
1298 l_temp_index := l_temp_index + 1;
1299 END LOOP;
1300 CLOSE l_exceptions_csr;
1301
1302 IF (l_log_procedure >= l_log_current_level) THEN
1303 fnd_log.string(l_log_procedure,
1304 L_DEBUG_KEY ||'.end',
1305 'At the end of PL SQL procedure.');
1306 END IF;
1307
1308 END Init_shift_Data;
1309
1310 ----------------------------------------
1311 -- Function to determine if a specific date is a holiday
1312 ----------------------------------------
1313 FUNCTION Is_Dept_Holiday(l_curr_date DATE) RETURN BOOLEAN
1314 IS
1315 l_temp_date DATE := TRUNC(l_curr_date);
1316 BEGIN
1317 IF (G_EXCEPTION_DATES_TBL.COUNT = 0) THEN
1318 RETURN FALSE;
1319 END IF;
1320 --Iterate through the exception dates to make sure it's not a holiday.
1321 FOR i IN G_EXCEPTION_DATES_TBL.FIRST .. G_EXCEPTION_DATES_TBL.LAST LOOP
1322 IF (l_temp_date = G_EXCEPTION_DATES_TBL(i)) THEN
1323 RETURN TRUE;
1324 --Assumes the exception table is sorted by date
1325 ELSIF (l_temp_date < G_EXCEPTION_DATES_TBL(i)) THEN
1326 RETURN FALSE;
1327 END IF;
1328 END LOOP;
1329
1330 RETURN FALSE;
1331
1332 END Is_Dept_Holiday;
1333
1334 --------------------------------------------------------------------
1335 -- Function name : Get_task_duration
1336 -- Type : Public
1337 -- Purpose : To return the total duration of the task
1338 -- based on the resource requirements defined
1339 -- at the route/operation level.
1340 --
1341 -- Parameters :
1342 -- p_vst_task_qty : Visit task quantity
1343 -- p_route_id : Route id
1344 --
1345 -- 27/Nov/2007 Initial Version Sowmya
1346 --------------------------------------------------------------------
1347 Function Get_task_duration(
1348 p_vst_task_qty IN NUMBER,
1349 p_route_id IN NUMBER
1350 )
1351 RETURN NUMBER IS
1352
1356 WHERE OBJECT_ID = c_route_id
1353 CURSOR c_get_route_level_res_reqs (c_route_id IN NUMBER) IS
1354 SELECT COST_BASIS_ID, DURATION, RT_OPER_RESOURCE_ID
1355 FROM ahl_rt_oper_resources
1357 AND NVL(SCHEDULED_TYPE_ID,1) <> 2
1358 AND ASSOCIATION_TYPE_CODE = G_ASSOC_TYPE_ROUTE;
1359
1360 get_route_level_res_reqs_rec c_get_route_level_res_reqs%ROWTYPE;
1361
1362 CURSOR c_get_op_level_res_reqs (c_oprn_id IN NUMBER) IS
1363 SELECT COST_BASIS_ID, DURATION, RT_OPER_RESOURCE_ID
1364 FROM ahl_rt_oper_resources
1365 WHERE OBJECT_ID = c_oprn_id
1366 AND NVL(SCHEDULED_TYPE_ID,1) <> 2
1367 AND ASSOCIATION_TYPE_CODE = G_ASSOC_TYPE_OPERATION;
1368
1369 get_op_level_res_reqs_rec c_get_op_level_res_reqs%ROWTYPE;
1370
1371 CURSOR c_get_oprns (c_route_id IN NUMBER) IS
1372 SELECT RO.operation_id
1373 FROM ahl_operations_vl O, ahl_route_operations RO
1374 WHERE O.operation_id = RO.operation_id
1375 AND RO.route_id = c_route_id
1376 AND O.revision_status_code = 'COMPLETE'
1377 AND O.revision_number IN (SELECT max(revision_number)
1378 FROM ahl_operations_b_kfv
1379 WHERE concatenated_segments = O.concatenated_segments
1380 AND trunc(sysdate) between trunc(start_date_active) and
1381 trunc(NVL(end_date_active,SYSDATE+1))
1382 );
1383
1384 get_oprns_rec c_get_oprns%ROWTYPE;
1385
1386 CURSOR c_get_route_time_span (c_route_id IN NUMBER) IS
1387 SELECT time_span
1388 FROM ahl_routes_b
1389 WHERE ROUTE_ID = c_route_id;
1390
1391 L_API_NAME CONSTANT VARCHAR2(30) := 'Get_task_duration';
1392 L_DEBUG_KEY CONSTANT VARCHAR2(100) := 'ahl.plsql.' || G_PKG_NAME || '.' || l_api_name;
1393 l_vst_task_qty NUMBER := p_vst_task_qty;
1394 max_duration NUMBER := 0;
1395 ro_duration NUMBER := 0;
1396 total_opr_duration NUMBER := 0;
1397 rt_final_duration NUMBER := 0;
1398 rt_time_span NUMBER := 0;
1399
1400 BEGIN
1401 -- Log API entry point
1402 IF (l_log_procedure >= l_log_current_level) THEN
1403 fnd_log.string(l_log_procedure,
1404 L_DEBUG_KEY ||'.begin',
1405 'At the start of function. Route Id = ' || p_route_id || ' and Task Quantity = ' || p_vst_task_qty);
1406 END IF;
1407
1408 IF (p_route_id IS NULL) THEN
1409 IF (l_log_statement >= l_log_current_level)THEN
1410 fnd_log.string(l_log_statement,
1414 RETURN 0;
1411 L_DEBUG_KEY,
1412 'Route Id is null.');
1413 END IF;
1415 END IF;
1416
1417 OPEN c_get_route_time_span (p_route_id);
1418 FETCH c_get_route_time_span INTO rt_time_span;
1419 CLOSE c_get_route_time_span;
1420
1421 IF (l_log_statement >= l_log_current_level)THEN
1422 fnd_log.string(l_log_statement,
1423 L_DEBUG_KEY,
1424 'Route time span = ' || rt_time_span || ', Visit task Quantity = ' || l_vst_task_qty);
1425 END IF;
1426
1427 IF (nvl(l_vst_task_qty,1) = 1) THEN --serialized items
1428 IF (l_log_statement >= l_log_current_level)THEN
1429 fnd_log.string(l_log_statement,
1430 L_DEBUG_KEY,
1431 'serialized item. rt_time_span = ' || rt_time_span);
1432 END IF;
1433 RETURN rt_time_span;
1434 ELSIF (l_vst_task_qty > 1) THEN --non-serialized items
1435 OPEN c_get_route_level_res_reqs (p_route_id);
1436 FETCH c_get_route_level_res_reqs INTO get_route_level_res_reqs_rec;
1437 IF (c_get_route_level_res_reqs%FOUND) THEN --requirements exist at route level
1438 LOOP
1439 EXIT WHEN c_get_route_level_res_reqs%NOTFOUND;
1440 IF (nvl(get_route_level_res_reqs_rec.cost_basis_id,2) = 1) THEN --item based resource
1441 ro_duration := get_route_level_res_reqs_rec.duration * l_vst_task_qty;
1442 ELSE --lot based resource
1443 ro_duration := get_route_level_res_reqs_rec.duration ;
1444 END IF;
1445
1446 IF (ro_duration > max_duration) THEN
1447 max_duration := ro_duration;
1448 END IF;
1449
1450 FETCH c_get_route_level_res_reqs INTO get_route_level_res_reqs_rec;
1451 END LOOP;
1452 CLOSE c_get_route_level_res_reqs;
1453 rt_final_duration := max_duration;
1454 ELSE --requirements exist at operation level
1455 CLOSE c_get_route_level_res_reqs;
1456 OPEN c_get_oprns (p_route_id);
1457 LOOP
1458 FETCH c_get_oprns INTO get_oprns_rec;
1459 EXIT WHEN c_get_oprns%NOTFOUND;
1460
1461 max_duration := 0;
1462
1463 OPEN c_get_op_level_res_reqs (get_oprns_rec.operation_id);
1464 LOOP
1465 FETCH c_get_op_level_res_reqs INTO get_op_level_res_reqs_Rec;
1466 EXIT WHEN c_get_op_level_res_reqs%NOTFOUND;
1467 IF (nvl(get_op_level_res_reqs_Rec.cost_basis_id,2) = 1) THEN --item based resource
1468 ro_duration := get_op_level_res_reqs_Rec.duration * l_vst_task_qty;
1469 ELSE --lot based resource
1470 ro_duration := get_op_level_res_reqs_Rec.duration ;
1471 END IF;
1472
1473 IF (ro_duration > max_duration) THEN
1474 max_duration := ro_duration;
1475 END IF;
1476 END LOOP;
1477 CLOSE c_get_op_level_res_reqs;
1478 /*max durations of all operations are summed up since they are assumed to be performed sequentially.*/
1479 total_opr_duration := total_opr_duration + max_duration;
1480 END LOOP;
1481 CLOSE c_get_oprns;
1482
1483 rt_final_duration := total_opr_duration;
1484 END IF; --requirements exist at route level
1485
1486 IF (l_log_statement >= l_log_current_level)THEN
1487 fnd_log.string(l_log_statement,
1488 L_DEBUG_KEY,
1489 'Route Duration - ' ||rt_final_duration);
1490 END IF;
1491
1492 IF (rt_time_span >= rt_final_duration) THEN
1493 IF (l_log_procedure >= l_log_current_level) THEN
1494 fnd_log.string(l_log_procedure,
1495 L_DEBUG_KEY ||'.end',
1496 'At the end of PL SQL function. non-serialized item. rt_time_span = ' || rt_time_span);
1497 END IF;
1498 RETURN rt_time_span;
1499 ELSE
1500 IF (l_log_procedure >= l_log_current_level) THEN
1501 fnd_log.string(l_log_procedure,
1502 L_DEBUG_KEY ||'.end',
1503 'At the end of PL SQL function. non-serialized item. rt_final_duration = ' || rt_final_duration);
1504 END IF;
1505 RETURN rt_final_duration;
1506 END IF;
1507 END IF; --non-serialized items
1508
1509 END Get_task_duration;
1510
1514 p_end_date date,
1511 -- PRAKKUM :: AVF :: 15/11/2011 :: Bug# 13416865 - AVF ENHANCEMENTS :: START :: New function to calculate start date from given department,
1512 -- end date considering department shifts and holidays
1513 FUNCTION Compute_Start_Date(
1515 p_dept_id number,
1516 p_duration number)
1517 RETURN DATE
1518 IS
1519 --
1520 --Get the number of holiday days that falls under the duration
1521 CURSOR get_holiday_csr(c_department_id IN NUMBER,
1525 FROM bom_calendar_exceptions ex, ahl_department_shifts dept
1522 c_start_date IN DATE,
1523 c_end_date IN DATE) IS
1524 SELECT COUNT(ex.EXCEPTION_DATE)
1526 WHERE ex.CALENDAR_CODE = dept.calendar_code
1527 AND EXCEPTION_TYPE = G_HOLIDAY_TYPE
1528 AND dept.department_id = c_department_id
1529 AND ex.exception_date > c_start_date
1530 AND ex.exception_date <= c_end_date;
1531
1532 --
1533 l_start_date DATE; --The start date that is being calculated.
1534 l_shift_start_date DATE; --The date that the shift started.
1535 l_shift_end_date DATE; --The date that the shift ended.
1536 l_end_date DATE; --The p_start_date adjusted for different department.
1537 l_start_hour NUMBER; --Starting hour
1538 l_curr_wday NUMBER; --current wday
1539 l_num_of_weekends NUMBER; --Number of weekends (multiple by off days)
1540 l_mod_days NUMBER; --Modulo workdays for given weekend
1541 l_days_to_add NUMBER; --Number of holiday days to add.
1542 l_shift_duration NUMBER; --The shift duration in UOM of day
1543 l_shift_off_time NUMBER; --The duration of shift off-time in UOM of day
1544 l_start_date_wo_offset DATE; --The start date calculated without hours part subtracted from it.
1545 L_API_NAME CONSTANT VARCHAR2(30) := 'Compute_Start_Date';
1546 L_DEBUG_KEY CONSTANT VARCHAR2(100) := 'ahl.plsql.' || G_PKG_NAME || '.' || L_API_NAME;
1547 --
1548 BEGIN
1549
1550 IF (l_log_procedure >= l_log_current_level) THEN
1551 fnd_log.string(l_log_procedure,
1552 L_DEBUG_KEY ||'.start',
1553 'At the start of PL SQL function. ');
1554 END IF;
1555
1556 IF (l_log_statement >= l_log_current_level) THEN
1557 fnd_log.string(l_log_statement,
1558 L_DEBUG_KEY , ' Params: p_dept_id : '||p_dept_id||
1559 ', p_duration : '||p_duration||
1560 ', p_end_date : '||to_char(p_end_date,'dd:mm:yyyy hh24:mi:ss'));
1561 END IF;
1562
1563 --1. Re-init the department shift time data if not the same
1564 -- as the current cached department
1565 IF (G_CURRENT_DEPT_ID IS NULL or
1566 p_dept_id <> G_CURRENT_DEPT_ID) THEN
1567 Init_Shift_Data(p_dept_id);
1568 G_CURRENT_DEPT_ID := p_dept_id;
1569 END IF;
1570
1571 --2. Adjust the end date to department shift
1572 l_end_date := p_end_date;
1573
1574 --Reset the end time to shift end hour if shift ends after current time.
1575 IF (G_SHIFT_END < G_SHIFT_START) THEN
1576 IF (l_end_date <= TRUNC(l_end_date) + G_SHIFT_START AND
1577 l_end_date > TRUNC(l_end_date) + G_SHIFT_END) THEN
1578 l_end_date := trunc(l_end_date)+ G_SHIFT_END;
1579 END IF;
1580 ELSE
1581 IF (l_end_date <= TRUNC(l_end_date) + G_SHIFT_START) THEN
1582 l_end_date := trunc(l_end_date) - 1 + G_SHIFT_END;
1583 ELSIF (l_end_date > TRUNC(l_end_date) + G_SHIFT_END) THEN
1584 l_end_date := trunc(l_end_date)+ G_SHIFT_END;
1585 END IF;
1586 END IF;
1587
1588 --If it's an overnight shift and time is before shift ends on the same day,
1589 --the shift started a day earlier
1590 l_shift_start_date := get_shift_start_date(l_end_date);
1591 l_shift_end_date := get_shift_end_date(l_end_date);
1592
1593 l_curr_wday := MOD((TRUNC(l_shift_start_date) - G_CAL_START), (G_DAYS_ON + G_DAYS_OFF));
1594
1595 -- Subtract to a day_on (basically, passed the weekend)
1596 IF(l_curr_wday +1 > G_DAYS_ON) THEN
1597 l_end_date := TRUNC(l_shift_end_date - (l_curr_wday+1-G_DAYS_ON))+G_SHIFT_END;
1598 END IF;
1599
1600 --If it's an overnight shift and time is before shift ends on the same day,
1601 --the shift started a day earlier
1602 l_shift_start_date := get_shift_start_date(l_end_date);
1603 l_shift_end_date := get_shift_end_date(l_end_date);
1604
1605 -- Not Day Off: Check if holiday and adjust based on holiday
1606 WHILE (Is_Dept_Holiday(l_shift_start_date)) LOOP
1607 l_shift_start_date := l_shift_start_date - 1;
1608 l_shift_end_date := l_shift_end_date - 1;
1609
1610 l_curr_wday := MOD((l_shift_start_date - G_CAL_START), (G_DAYS_ON + G_DAYS_OFF));
1611 -- subtract to a day_on (basically, passed the weekend)
1612 IF(l_curr_wday +1 > G_DAYS_ON) THEN
1613 l_shift_start_date := l_shift_end_date - (l_curr_wday+1-G_DAYS_ON);
1614 END IF;
1615
1616 l_end_date := TRUNC(l_shift_start_date) + G_SHIFT_END;
1617 END LOOP;
1618
1619 IF (p_duration = 0) THEN
1620 -- If duration is 0, RETURN the adjusted start date.
1621 IF (l_log_procedure >= l_log_current_level) THEN
1622 fnd_log.string(l_log_procedure,
1623 L_DEBUG_KEY ||'.end',
1624 'At the end of PL SQL function. Start Date = ' || to_char(l_end_date,'dd:mm:yyyy hh24:mi:ss'));
1625 END IF;
1626 RETURN l_end_date;
1627
1628 ELSE
1629
1630 --1. Derive shift duration and shift off time.
1631 IF(G_SHIFT_END < G_SHIFT_START) THEN
1632 l_shift_duration := (24*G_SHIFT_END + 24 - 24*G_SHIFT_START)/24;
1633 l_shift_off_time := (G_SHIFT_START-G_SHIFT_END);
1634 ELSE
1635 l_shift_duration := (24*G_SHIFT_END - 24*G_SHIFT_START)/24;
1636 l_shift_off_time := 1-(G_SHIFT_END-G_SHIFT_START);
1637 END IF;
1638
1639 IF (l_log_statement >= l_log_current_level) THEN
1640 fnd_log.string(l_log_statement,
1641 L_DEBUG_KEY , ' l_shift_off_time : '||l_shift_off_time||' l_shift_duration:'||l_shift_duration);
1642 END IF;
1643
1647 --The start date calculated without hours part subtracted from it.
1644 --Subtract duration days considering shift timings from end date
1645 IF (MOD(p_duration, l_shift_duration*24) =0
1646 AND l_end_date-TRUNC(l_end_date)= G_SHIFT_END) THEN
1648 l_start_date_wo_offset := l_end_date - (TRUNC((p_duration)/(l_shift_duration*24)))+1;
1649 --The start date calculated with hours part subtracted from it.
1650 l_start_date := l_start_date_wo_offset - l_shift_duration;
1651 ELSE
1652 --The start date calculated without hours part subtracted from it.
1653 l_start_date_wo_offset := l_end_date - TRUNC((p_duration/24)/l_shift_duration);
1657
1654 --The start date calculated with hours part subtracted from it.
1655 l_start_date := l_start_date_wo_offset - MOD(p_duration/24, l_shift_duration);
1656 END IF;
1658 IF (l_log_statement >= l_log_current_level) THEN
1659 fnd_log.string(l_log_statement,
1660 L_DEBUG_KEY , ' l_start_date : '||to_char(l_start_date,'dd:mm:yyyy hh24:mi:ss')
1661 ||' l_start_date_wo_offset : '||to_char(l_start_date_wo_offset,'dd:mm:yyyy hh24:mi:ss'));
1662 END IF;
1663
1664 --Get the start hour as fraction of a day
1665 l_start_hour := l_start_date - TRUNC(l_start_date);
1666
1667 --If start date that calculated subtracting hours part falls beyond the shift start, then
1668 --subtract off time hours to shift the start time.
1669 IF l_start_date < (get_shift_start_date(l_start_date_wo_offset)+G_SHIFT_START) THEN
1670 l_start_date := l_start_date - l_shift_off_time;
1671 END IF;
1672
1673 --Add in the days off
1674 --First calculate number of days off in terms of weekends
1675 l_num_of_weekends := TRUNC((get_shift_start_date(l_end_date)-get_shift_start_date(l_start_date))/G_DAYS_ON);
1676
1677 --This gets the number of extra days over week so that we can check if we get an extra weekend.
1678 l_mod_days :=MOD(get_shift_start_date(l_end_date)- get_shift_start_date(l_start_date), G_DAYS_ON);
1679
1680 --Subtract weekends days_off
1681 l_start_date := l_start_date - ( G_DAYS_OFF * l_num_of_weekends );
1682
1683 l_curr_wday := (MOD(get_shift_start_date(l_start_date) - G_CAL_START, G_DAYS_ON + G_DAYS_OFF));
1684
1685 --If the extra days pushes into an extra week, subtract 1 more weekend.
1686 IF(l_curr_wday+l_mod_days+1>G_DAYS_ON) THEN
1687 l_start_date := l_start_date - ( G_DAYS_OFF * 1 );
1688 END IF;
1689
1690 --Subtract in the holidays if not already subtracted
1691 l_days_to_add := 0;
1692 OPEN get_holiday_csr(p_dept_id, get_shift_start_date(l_start_date),get_shift_start_date(l_end_date));
1693 FETCH get_holiday_csr INTO l_days_to_add;
1694 CLOSE get_holiday_csr;
1695
1696 IF (l_log_statement >= l_log_current_level) THEN
1697 fnd_log.string(l_log_statement,
1698 L_DEBUG_KEY , ' l_days_to_add : '||l_days_to_add);
1699 END IF;
1700
1701 WHILE (l_days_to_add > 0) LOOP
1702 --Increment and decrement the days
1703 l_start_date := l_start_date - 1;
1704 l_days_to_add := l_days_to_add - 1;
1705
1706 --Skip the weekends and the additional holidays
1707 l_curr_wday := MOD(get_shift_start_date(l_start_date)- G_CAL_START, G_DAYS_ON + G_DAYS_OFF);
1708
1709 IF (l_curr_wday+1 > G_DAYS_ON) THEN
1710 l_days_to_add := l_days_to_add + 1;
1711 ELSIF(Is_Dept_Holiday(get_shift_start_date(l_start_date))) THEN
1712 l_days_to_add := l_days_to_add + 1;
1713 END IF;
1714 END LOOP;
1715
1716 IF (l_log_statement >= l_log_current_level) THEN
1717 fnd_log.string(l_log_statement,
1718 L_DEBUG_KEY , ' Before exact match correction l_start_date : '||to_char(l_start_date,'dd:mm:yyyy hh24:mi:ss')||
1719 ' trunc(l_start_date)+G_SHIFT_END : '||to_char(trunc(l_start_date)+G_SHIFT_END,'dd:mm:yyyy hh24:mi:ss'));
1720 END IF;
1721
1722 --If start time is exact match to shift end time then based on shift timings move it to next day start date time.
1723 IF (l_start_date = trunc(l_start_date)+G_SHIFT_END) THEN
1727 l_start_date := trunc(l_start_date) + G_SHIFT_START;
1724 IF G_SHIFT_START < G_SHIFT_END THEN--Regular Shift
1725 l_start_date := trunc(l_start_date) + 1 + G_SHIFT_START;
1726 ELSE -- Night Shift
1728 END IF;
1729 END IF;
1730
1731 IF (l_log_procedure >= l_log_current_level) THEN
1732 fnd_log.string(l_log_procedure,
1733 L_DEBUG_KEY ||'.end',
1734 'At the end of PL SQL function. Start Date = ' || to_char(l_start_date,'dd:mm:yyyy hh24:mi:ss'));
1735 END IF;
1736
1737 RETURN l_start_date;
1738
1739 END IF;
1740
1741 END Compute_Start_Date;
1742 -- PRAKKUM :: AVF :: 15/11/2011 :: Bug# 13416865 - AVF ENHANCEMENTS :: END
1743
1744 END AHL_VWP_TIMES_PVT;