DBA Data[Home] [Help]

PACKAGE BODY: APPS.HRI_OPL_SUPH_HST

Source


1 PACKAGE BODY hri_opl_suph_hst AS
2 /* $Header: hrioshh.pkb 120.18 2007/01/02 13:58:59 jtitmas noship $ */
3 /******************************************************************************/
4 /*                                                                            */
5 /* OUTLINE / DEFINITIONS                                                      */
6 /*                                                                            */
7 /* A chain is defined for an employee as a list starting with the employee    */
8 /* which contains their supervisor, and successive higher level supervisors   */
9 /* finishing with the highest level (overall) supervisor.                     */
10 /*                                                                            */
11 /* Each chain is valid for the length of time it describes the supervisor     */
12 /* hierarchy between the employee it is defined for and the overall           */
13 /* supervisor in the hierarchy.                                               */
14 /*                                                                            */
15 /* The supervisor hierarchy table implements each link in the chain as a      */
16 /* row with the employee the chain is defined for as the subordinate. The     */
17 /* absolute levels refer to absolute positions within the overall hierarchy   */
18 /* whereas the relative level refers to the difference in the absolute levels */
19 /* for the row.                                                               */
20 /*                                                                            */
21 /* When an employee changes supervisor, their chain must change since their   */
22 /* immediate supervisor is different. However, the chains of all that         */
23 /* employee's subordinates must also change because a chain consists of       */
24 /* each higher level supervisor up to and including the overall supervisor.   */
25 /*                                                                            */
26 /* IMPLEMENTATION LOGIC                                                       */
27 /*                                                                            */
28 /* The supervisor hierarchy history table is populated by carrying out the    */
29 /* following steps using the multi-threading wrapper
30 /*                                                                            */
31 /*  Pre-process (single-thread)                                               */
32 /*  ===========================                                               */
33 /*  1) Update event queue (incremental only)                                  */
34 /*  2) Empty out existing table (all or queued supervisors)                   */
35 /*  3) End date chains for queued supervisors (incremental only)              */
36 /*  4) Remove indexes (full refresh only)                                     */
37 /*  5) Disable WHO trigger                                                    */
38 /*                                                                            */
39 /*  Main collection (multi-thread by person)                                  */
40 /*  ========================================                                  */
41 /*  1) Set first date to sample management chain as later of person hire      */
42 /*     or refresh from date                                                   */
43 /*  2) Loop through sample dates:                                             */
44 /*      i) Insert links in chain when there is a change in supervisor,        */
45 /*         assignment, levels or orphan status.                               */
46 /*     ii) Retain the next sample date as the earliest date any link in       */
47 /*         the sampled chain has the next supervisor change event             */
48 /*  3) Exit the loop when either:                                             */
49 /*      i) No data is found - person has been terminated on the previous date */
50 /*     ii) Sample date hits end of time - no further changes                  */
51 /*  4) Ensure PL/SQL table of rows to insert is fully updated and execute     */
52 /*     the bulk insert                                                        */
53 /*                                                                            */
54 /*  Post-process (single-thread)                                              */
55 /*  ============================                                              */
56 /*  1) Recreate indexes (full refresh only)                                   */
57 /*  2) Enable WHO trigger                                                     */
58 /*                                                                            */
59 /*  Data Structures                                                           */
60 /*  ===============                                                           */
61 /*  A chain cache table stores information about each link in the chain. It   */
62 /*  is indexed by link level. It should be well maintained (i.e. links are    */
63 /*  removed when no longer required). The person being processed will always  */
64 /*  be the last link in the chain.                                            */
65 /*                                                                            */
66 /*  Error handling                                                            */
67 /*  ==============                                                            */
68 /*                                                                            */
69 /*  Orphans (no exception raised)                                             */
70 /*  -----------------------------                                             */
71 /*  If the management chain is sampled and it is found that the top manager   */
72 /*  has a supervisor assigned then the chain is said to be orphaned.          */
73 /*                                                                            */
74 /*  If the supervisor of the top manager has been terminated, it is possible  */
75 /*  they may be re-hired. This should be taken into account when deciding     */
76 /*  which date to next sample the hierarchy.                                  */
77 /*                                                                            */
78 /*  Loops (exception explicitly trapped)                                      */
79 /*  ------------------------------------                                      */
80 /*  When a loop is encountered the person being processed is deemed an orphan */
81 /*  (since no management chain can be found for them).                        */
82 /*                                                                            */
83 /*  They are then treated as an orphan but the chain resampled at regular     */
84 /*  intervals up to system date in case the data is fixed at a later date.    */
85 /*                                                                            */
86 /*  Other errors (not trapped)                                                */
87 /*  --------------------------                                                */
88 /*  Other errors are not handled.                                             */
89 /******************************************************************************/
90 
91 -- Information to be held for each link in a chain
92 TYPE g_link_record_type IS RECORD
93   (chain_id            NUMBER
94   ,person_id           per_all_assignments_f.person_id%TYPE
95   ,assignment_id       per_all_assignments_f.assignment_id%TYPE
96   ,business_group_id   per_all_assignments_f.business_group_id%TYPE
97   ,asg_status_type_id  per_all_assignments_f.assignment_status_type_id%TYPE
98   ,start_date          DATE
99   ,relative_level      PLS_INTEGER
100   ,orphan_flag         VARCHAR2(30));
101 
102 -- Information relating to transfers
103 TYPE g_trn_rec_type IS RECORD
104  (node_exists_before  BOOLEAN
105  ,node_exists_after   BOOLEAN
106  ,node_direct_before  NUMBER
107  ,node_direct_after   NUMBER);
108 
109 -- Table type to hold information about the current chain
110 TYPE g_chain_type IS TABLE OF g_link_record_type INDEX BY BINARY_INTEGER;
111 
112 -- Table tpye to hold transfer information
113 TYPE g_trn_tab_type IS TABLE OF g_trn_rec_type INDEX BY BINARY_INTEGER;
114 
115 -- Simple table types
116 TYPE g_date_tab_type IS TABLE OF DATE INDEX BY BINARY_INTEGER;
117 TYPE g_number_tab_type IS TABLE OF NUMBER INDEX BY BINARY_INTEGER;
118 TYPE g_varchar2_tab_type IS TABLE OF VARCHAR2(30) INDEX BY BINARY_INTEGER;
119 
120 -- PLSQL table of tables representing database table
121 g_suph_sup_psn_id         g_number_tab_type;
122 g_suph_sup_asg_id         g_number_tab_type;
123 g_suph_sup_ast_id         g_number_tab_type;
124 g_suph_sup_level          g_number_tab_type;
125 g_suph_sup_bgr_id         g_number_tab_type;
126 g_suph_sup_sub1_psn_id    g_number_tab_type;
127 g_suph_sup_sub2_psn_id    g_number_tab_type;
128 g_suph_sup_sub3_psn_id    g_number_tab_type;
129 g_suph_sup_sub4_psn_id    g_number_tab_type;
130 g_suph_sub_psn_id         g_number_tab_type;
131 g_suph_sub_asg_id         g_number_tab_type;
132 g_suph_sub_level          g_number_tab_type;
133 g_suph_sub_bgr_id         g_number_tab_type;
134 g_suph_sub_rlt_lvl        g_number_tab_type;
135 g_suph_sub_chain_id       g_number_tab_type;
136 g_suph_start_date         g_date_tab_type;
137 g_suph_end_date           g_date_tab_type;
138 g_suph_orphan_flg         g_varchar2_tab_type;
139 g_suph_row_count          PLS_INTEGER;
140 
141 -- PLSQL table of tables representing database table
142 g_chn_psn_id              g_number_tab_type;
143 g_chn_asg_id              g_number_tab_type;
144 g_chn_start_date          g_date_tab_type;
145 g_chn_end_date            g_date_tab_type;
146 g_chn_chain_id            g_number_tab_type;
147 g_chn_psn_lvl             g_number_tab_type;
148 g_chn_row_count           PLS_INTEGER;
149 
150 -- PLSQL table of tables representing database table
151 g_trn_sup_psn_id          g_number_tab_type;
152 g_trn_sup_sc_fk           g_number_tab_type;
153 g_trn_psn_id              g_number_tab_type;
154 g_trn_ref_id              g_number_tab_type;
155 g_trn_asg_id              g_number_tab_type;
156 g_trn_wty_fk              g_varchar2_tab_type;
157 g_trn_date                g_date_tab_type;
158 g_trn_in_ind              g_number_tab_type;
159 g_trn_out_ind             g_number_tab_type;
160 g_trn_dir_ind             g_number_tab_type;
161 g_trn_dir_rec             g_number_tab_type;
162 g_trn_sec_asg_ind         g_number_tab_type;
163 g_trn_hdc_trn             g_number_tab_type;
164 g_trn_fte_trn             g_number_tab_type;
165 g_trn_row_count           PLS_INTEGER;
166 
167 -- Global time variables
168 g_current_time            DATE;
169 g_end_of_time             DATE := hr_general.end_of_time;
170 
171 -- Whether OBIEE is implemented
172 g_implement_obiee         VARCHAR2(30);
173 
174 -- Whether to print debug messages
175 g_debug                   BOOLEAN := FALSE;
176 g_log_sup_loop            BOOLEAN;
177 
178 -- Global HRI Multithreading Array
179 g_mthd_action_array       HRI_ADM_MTHD_ACTIONS%rowtype;
180 
181 -- Global parameters
182 g_refresh_start_date      DATE;
183 g_full_refresh            VARCHAR2(30);
184 g_load_helper_table       VARCHAR2(30);
185 
186 -- DBI global start date
187 g_dbi_collection_start_date DATE := TRUNC(TO_DATE(fnd_profile.value
188                                      ('BIS_GLOBAL_START_DATE'),'MM/DD/YYYY'));
189 
190 -- Bug 4105868: Global to store msg_sub_group
191 g_msg_sub_group          VARCHAR2(400);
192 
193 -- Write to log
194 PROCEDURE output(p_message  IN VARCHAR2) IS
195 
196 BEGIN
197   HRI_BPL_CONC_LOG.output(p_message);
198 END output;
199 
200 -- Write to log if debugging is set
201 PROCEDURE debug(p_message IN VARCHAR2) IS
202 
203 BEGIN
204   HRI_BPL_CONC_LOG.dbg(p_message);
205 END debug;
206 
207 -- Get the supervisor loop message
208 FUNCTION get_sup_loop_message(p_message         IN VARCHAR2,
209                               p_effective_date  IN DATE,
210                               p_person_id       IN VARCHAR2)
211        RETURN VARCHAR2 IS
212 
213   CURSOR person_name_csr IS
214   SELECT full_name
215   FROM per_people_x
216   WHERE person_id = p_person_id;
217 
218   l_person_name   VARCHAR2(240);
219 
220 BEGIN
221 
222   -- Get person name
223   OPEN person_name_csr;
224   FETCH person_name_csr INTO l_person_name;
225   CLOSE person_name_csr;
226 
227   -- Set message parameters
228   fnd_message.set_name('HRI', p_message);
229   fnd_message.set_token('DATE',to_char(p_effective_date, 'YYYY/MM/DD'));
230   fnd_message.set_token('PERSON_NAME',l_person_name);
231 
232   -- Get the message and return it
233   RETURN fnd_message.get;
234 
235 END get_sup_loop_message;
236 
237 
238 -- ----------------------------------------------------------------------------
239 -- Adds change records to workforce events fact event queue
240 -- ----------------------------------------------------------------------------
241 PROCEDURE populate_wrkfc_evt_eq IS
242 
243 BEGIN
244 
245   IF g_implement_obiee = 'Y' THEN
246 
247     -- Insert assignments with manager who have had chain changes
248     -- to workforce event fact queue
249     INSERT INTO hri_eq_wrkfc_evt
250      (assignment_id
251      ,erlst_evnt_effective_date
252      ,source_code)
253       SELECT /*+ ORDERED */
254        wevt.asg_assgnmnt_fk
255       ,eq.erlst_evnt_effective_date
256       ,'ASG_MGR_' || eq.source_code
257       FROM
258        hri_eq_sprvsr_hrchy_chgs  eq
259       ,hri_mb_wrkfc_evt_ct       wevt
260       WHERE wevt.time_day_evt_end_fk >= eq.erlst_evnt_effective_date
261       AND wevt.per_person_mgr_fk = eq.person_id;
262 
263     INSERT INTO hri_eq_wrkfc_mnth
264      (assignment_id
265      ,erlst_evnt_effective_date
266      ,source_code)
267       SELECT /*+ ORDERED */
268        wevt.asg_assgnmnt_fk
269       ,eq.erlst_evnt_effective_date
270       ,'ASG_MGR_' || eq.source_code
271       FROM
272        hri_eq_sprvsr_hrchy_chgs  eq
273       ,hri_mb_wrkfc_evt_ct       wevt
274       WHERE wevt.time_day_evt_end_fk >= eq.erlst_evnt_effective_date
275       AND wevt.per_person_mgr_fk = eq.person_id;
276 
277     -- commit
278     COMMIT;
279 
280   END IF;
281 
282 END populate_wrkfc_evt_eq;
283 
284 
285 -- ----------------------------------------------------------------------------
286 -- Adds change records to workforce events by manager summary event queue
287 -- ----------------------------------------------------------------------------
288 PROCEDURE populate_wrkfc_evt_mgrh_eq IS
289 
290 BEGIN
291 
292   IF g_implement_obiee = 'Y' THEN
293 
294     INSERT INTO hri_eq_wrkfc_evt_mgrh
295      (sup_person_id
296      ,erlst_evnt_effective_date
297      ,source_code)
298       SELECT
299        person_id
300       ,erlst_evnt_effective_date
301       ,source_code
302       FROM hri_eq_sprvsr_hrchy_chgs;
303 
304     -- commit
305     COMMIT;
306 
307   END IF;
308 
309 END populate_wrkfc_evt_mgrh_eq;
310 
311 
312 -- ----------------------------------------------------------------------------
313 -- POPULATE_ASG_DELTA_EQ (4259598 Incremental Changes)
314 -- This procedure inserts all records from the supervisor event queue into the
315 -- assignment event delta queue, which is used to incrementally refresh
316 -- the assignment delta table
317 --
318 -- Also, if absence is used, the corresponding absences event queue is
319 -- populated
320 -- ----------------------------------------------------------------------------
321 PROCEDURE populate_asg_delta_eq IS
322 
323 BEGIN
324 
325 -- 4259598 Incremental Changes
326 -- Populate the assignment event delta queue using which the assignment delta
327 -- table can be refrshed incrementally It should be noted that any point in
328 -- time there should only be one record for an assingment and type in the event
329 -- queue which contains the earliest event date for the assignment. Therefore,
330 -- if a record exists for the asg then update the record otherwise, insert a
331 -- new record for the assignment
332   IF (fnd_profile.value('HRI_IMPL_DBI') = 'Y') THEN
333 
334     MERGE INTO hri_eq_asg_sup_wrfc delta_eq
335     USING (SELECT assignment_id,
336                   erlst_evnt_effective_date,
337                   'SUPERVISOR' source_type
338            FROM   hri_eq_sprvsr_hrchy_chgs) sup_eq
339     ON    (       delta_eq.source_type = 'SUPERVISOR'
340            AND    sup_eq.assignment_id = delta_eq.source_id)
341     WHEN MATCHED THEN
342       UPDATE SET delta_eq.erlst_evnt_effective_date =
343                  least(delta_eq.erlst_evnt_effective_date,sup_eq.erlst_evnt_effective_date)
344     WHEN NOT MATCHED THEN
345       INSERT (delta_eq.source_type,
346               delta_eq.source_id,
347               delta_eq.erlst_evnt_effective_date
348               )
349       VALUES (sup_eq.source_type,
350               sup_eq.assignment_id,
351               sup_eq.erlst_evnt_effective_date);
352   -- Commit
353     COMMIT;
354 
355   END IF;
356 
357 -- Check if absence is required
358   IF (fnd_profile.value('HRI_COL_ABSNCE_EVENT_EQ') = 'Y') THEN
359 
360     MERGE INTO hri_eq_sup_absnc absnc_eq
361     USING (SELECT person_id,
362                   erlst_evnt_effective_date,
363                   'SUPERVISOR' source_type
364            FROM   hri_eq_sprvsr_hrchy_chgs) sup_eq
365     ON    (       absnc_eq.source_type = 'SUPERVISOR'
366            AND    sup_eq.person_id = absnc_eq.source_id)
367     WHEN MATCHED THEN
368       UPDATE SET absnc_eq.erlst_evnt_effective_date =
369                  LEAST(absnc_eq.erlst_evnt_effective_date,
370                        sup_eq.erlst_evnt_effective_date)
371     WHEN NOT MATCHED THEN
372       INSERT (absnc_eq.source_type,
373               absnc_eq.source_id,
374               absnc_eq.erlst_evnt_effective_date
375               )
376       VALUES (sup_eq.source_type,
377               sup_eq.person_id,
378               sup_eq.erlst_evnt_effective_date);
379 
380   -- Commit
381     COMMIT;
382 
383   END IF;
384 
385 END populate_asg_delta_eq;
386 
387 -- ----------------------------------------------------------------------------
388 -- Recovers rows to insert when an exception occurs
389 -- ----------------------------------------------------------------------------
390 PROCEDURE recover_insert_rows IS
391 
392   -- variables needed for populating the WHO columns
393   l_user_id      NUMBER;
394 
395 BEGIN
396 
397   -- Initialize variables
398   l_user_id      := fnd_global.user_id;
399   g_current_time := sysdate;
400 
401   -- Loop through rows one at a time
402   FOR i IN 1..g_suph_row_count LOOP
403 
404     -- Trap unique constraint errors
405     BEGIN
406 
407       -- Perform single row insert
408       INSERT INTO hri_cs_suph
409         (sup_person_id
410         ,sup_assignment_id
411         ,sup_level
412         ,sup_business_group_id
413         ,sup_assignment_status_type_id
414         ,sup_invalid_flag_code
415         ,sup_sub1_mgr_person_fk
416         ,sup_sub2_mgr_person_fk
417         ,sup_sub3_mgr_person_fk
418         ,sup_sub4_mgr_person_fk
419         ,sub_person_id
420         ,sub_assignment_id
421         ,sub_level
422         ,sub_relative_level
423         ,sub_business_group_id
424         ,sub_invalid_flag_code
425         ,sub_primary_asg_flag_code
426         ,orphan_flag_code
427         ,sub_mngrsc_fk
428         ,effective_start_date
429         ,effective_end_date
430         ,last_update_date
431         ,last_update_login
432         ,last_updated_by
433         ,created_by
434         ,creation_date)
435           VALUES
436             (g_suph_sup_psn_id(i)
437             ,g_suph_sup_asg_id(i)
438             ,g_suph_sup_level(i)
439             ,g_suph_sup_bgr_id(i)
440             ,g_suph_sup_ast_id(i)
441             ,'N'
442             ,g_suph_sup_sub1_psn_id(i)
443             ,g_suph_sup_sub2_psn_id(i)
444             ,g_suph_sup_sub3_psn_id(i)
445             ,g_suph_sup_sub4_psn_id(i)
446             ,g_suph_sub_psn_id(i)
447             ,g_suph_sub_asg_id(i)
448             ,g_suph_sub_level(i)
449             ,g_suph_sub_rlt_lvl(i)
450             ,g_suph_sub_bgr_id(i)
451             ,'N'
452             ,'Y'
453             ,g_suph_orphan_flg(i)
454             ,g_suph_sub_chain_id(i)
455             ,g_suph_start_date(i)
456             ,g_suph_end_date(i)
457             ,g_current_time
458             ,l_user_id
459             ,l_user_id
460             ,l_user_id
461             ,g_current_time);
462 
463     EXCEPTION
464       WHEN OTHERS THEN
465 
466          -- Probable overlap on date tracked assignment rows
467          output('Assignment error: ' || to_char(g_suph_sub_asg_id(i)) ||
468                 ' on ' || to_char(g_suph_start_date(i),'DD-MON-YYYY'));
469 
470     END;
471 
472   END LOOP;
473 
474   FOR i IN 1..g_chn_row_count LOOP
475 
476     BEGIN
477 
478       INSERT INTO hri_cs_mngrsc_ct
479        (mgrs_mngrsc_pk
480        ,mgrs_person_fk
481        ,mgrs_assignment_fk
482        ,mgrs_date_start
483        ,mgrs_date_end
484        ,mgrs_level
485        ,last_update_date
486        ,last_update_login
487        ,last_updated_by
488        ,created_by
489        ,creation_date)
490        VALUES
491         (g_chn_chain_id(i)
492         ,g_chn_psn_id(i)
493         ,g_chn_asg_id(i)
494         ,g_chn_start_date(i)
495         ,g_chn_end_date(i)
496         ,g_chn_psn_lvl(i)
497         ,g_current_time
498         ,l_user_id
499         ,l_user_id
500         ,l_user_id
501         ,g_current_time);
502 
503     EXCEPTION WHEN OTHERS THEN
504       -- If this insert errors the above insert will have also failed
505       null;
506     END;
507 
508   END LOOP;
509 
510   -- Insert manager hierarchy transfers
511   IF (g_trn_row_count > 0) THEN
512 
513     FOR i IN 1..g_trn_row_count LOOP
514 
515       BEGIN
516 
517         INSERT INTO hri_mdp_mgrh_transfers_ct
518          (mgr_sup_person_fk
519          ,per_person_fk
520          ,asg_assgnmnt_fk
521          ,per_person_trn_fk
522          ,time_day_evt_fk
523          ,ptyp_wrktyp_fk
524          ,transfer_in_ind
525          ,transfer_out_ind
526          ,direct_ind
527          ,direct_record_ind
528          ,sec_asg_ind
529          ,last_update_date
530          ,last_update_login
531          ,last_updated_by
532          ,created_by
533          ,creation_date)
534           VALUES
535            (g_trn_sup_psn_id(i)
536            ,g_trn_psn_id(i)
537            ,g_trn_asg_id(i)
538            ,g_trn_ref_id(i)
539            ,g_trn_date(i)
540            ,g_trn_wty_fk(i)
541            ,g_trn_in_ind(i)
542            ,g_trn_out_ind(i)
543            ,g_trn_dir_ind(i)
544            ,g_trn_dir_rec(i)
545            ,g_trn_sec_asg_ind(i)
546            ,g_current_time
547            ,l_user_id
548            ,l_user_id
549            ,l_user_id
550            ,g_current_time);
551 
552       EXCEPTION WHEN OTHERS THEN
553         -- If this insert errors the above insert will have also failed
554         null;
555       END;
556 
557     END LOOP;
558 
559   END IF;
560 
561   -- Commit the chunk of rows
562   COMMIT;
563 
564   -- Reset the row counters
565   g_suph_row_count := 0;
566   g_chn_row_count := 0;
567   g_trn_row_count := 0;
568 
569 END recover_insert_rows;
570 
571 -- ----------------------------------------------------------------------------
572 -- Bulk inserts rows from global temporary table to database table
573 -- ----------------------------------------------------------------------------
574 PROCEDURE bulk_insert_rows IS
575 
576 l_user_id      NUMBER;
577 
578 BEGIN
579 
580   -- Initialize variables
581   l_user_id      := fnd_global.user_id;
582   g_current_time := sysdate;
583 
584   IF (g_suph_row_count > 0) THEN
585 
586   -- insert chunk of rows
587   FORALL i IN 1..g_suph_row_count
588     INSERT INTO hri_cs_suph
589       (sup_person_id
590       ,sup_assignment_id
591       ,sup_level
592       ,sup_business_group_id
593       ,sup_assignment_status_type_id
594       ,sup_invalid_flag_code
595       ,sup_sub1_mgr_person_fk
596       ,sup_sub2_mgr_person_fk
597       ,sup_sub3_mgr_person_fk
598       ,sup_sub4_mgr_person_fk
599       ,sub_person_id
600       ,sub_assignment_id
601       ,sub_level
602       ,sub_relative_level
603       ,sub_business_group_id
604       ,sub_invalid_flag_code
605       ,sub_primary_asg_flag_code
606       ,orphan_flag_code
607       ,sub_mngrsc_fk
608       ,effective_start_date
609       ,effective_end_date
610       ,last_update_date
611       ,last_update_login
612       ,last_updated_by
613       ,created_by
614       ,creation_date)
615         VALUES
616           (g_suph_sup_psn_id(i)
617           ,g_suph_sup_asg_id(i)
618           ,g_suph_sup_level(i)
619           ,g_suph_sup_bgr_id(i)
620           ,g_suph_sup_ast_id(i)
621           ,'N'
622           ,g_suph_sup_sub1_psn_id(i)
623           ,g_suph_sup_sub2_psn_id(i)
624           ,g_suph_sup_sub3_psn_id(i)
625           ,g_suph_sup_sub4_psn_id(i)
626           ,g_suph_sub_psn_id(i)
627           ,g_suph_sub_asg_id(i)
628           ,g_suph_sub_level(i)
629           ,g_suph_sub_rlt_lvl(i)
630           ,g_suph_sub_bgr_id(i)
631           ,'N'
632           ,'Y'
633           ,g_suph_orphan_flg(i)
634           ,g_suph_sub_chain_id(i)
635           ,g_suph_start_date(i)
636           ,g_suph_end_date(i)
637           ,g_current_time
638           ,l_user_id
639           ,l_user_id
640           ,l_user_id
641           ,g_current_time);
642 
643   END IF;
644 
645   IF (g_chn_row_count > 0) THEN
646 
647   FORALL i IN 1..g_chn_row_count
648     INSERT INTO hri_cs_mngrsc_ct
649      (mgrs_mngrsc_pk
650      ,mgrs_person_fk
651      ,mgrs_assignment_fk
652      ,mgrs_date_start
653      ,mgrs_date_end
654      ,mgrs_level
655      ,last_update_date
656      ,last_update_login
657      ,last_updated_by
658      ,created_by
659      ,creation_date)
660      VALUES
661       (g_chn_chain_id(i)
662       ,g_chn_psn_id(i)
663       ,g_chn_asg_id(i)
664       ,g_chn_start_date(i)
665       ,g_chn_end_date(i)
666       ,g_chn_psn_lvl(i)
667       ,g_current_time
668       ,l_user_id
669       ,l_user_id
670       ,l_user_id
671       ,g_current_time);
672 
673   END IF;
674 
675   -- Insert manager hierarchy transfers
676   IF (g_trn_row_count > 0) THEN
677 
678     FORALL i IN 1..g_trn_row_count
679       INSERT INTO hri_mdp_mgrh_transfers_ct
680        (mgr_sup_person_fk
681        ,per_person_fk
682        ,asg_assgnmnt_fk
683        ,per_person_trn_fk
684        ,time_day_evt_fk
685        ,ptyp_wrktyp_fk
686        ,transfer_in_ind
687        ,transfer_out_ind
688        ,direct_ind
689        ,direct_record_ind
690        ,sec_asg_ind
691        ,last_update_date
692        ,last_update_login
693        ,last_updated_by
694        ,created_by
695        ,creation_date)
696         VALUES
697          (g_trn_sup_psn_id(i)
698          ,g_trn_psn_id(i)
699          ,g_trn_asg_id(i)
700          ,g_trn_ref_id(i)
701          ,g_trn_date(i)
702          ,g_trn_wty_fk(i)
703          ,g_trn_in_ind(i)
704          ,g_trn_out_ind(i)
705          ,g_trn_dir_ind(i)
706          ,g_trn_dir_rec(i)
707          ,g_trn_sec_asg_ind(i)
708          ,g_current_time
709          ,l_user_id
710          ,l_user_id
711          ,l_user_id
712          ,g_current_time);
713 
714   END IF;
715 
716   -- commit the chunk of rows
717   COMMIT;
718 
719   -- Reset the row counters
720   g_suph_row_count := 0;
721   g_chn_row_count  := 0;
722   g_trn_row_count := 0;
723 
724 EXCEPTION
725   WHEN OTHERS THEN
726 
727   recover_insert_rows;
728 
729 END bulk_insert_rows;
730 
731 
732 -- ----------------------------------------------------------------------------
733 -- Inserts row into global temporary table
734 -- ----------------------------------------------------------------------------
735 PROCEDURE insert_row(p_supv_person_id           IN NUMBER
736                     ,p_supv_assignment_id       IN NUMBER
737                     ,p_supv_level               IN NUMBER
738                     ,p_supv_business_group_id   IN NUMBER
739                     ,p_supv_asg_status_type_id  IN NUMBER
740                     ,p_supv_sub1_psn_id         IN VARCHAR2
741                     ,p_supv_sub2_psn_id         IN VARCHAR2
742                     ,p_supv_sub3_psn_id         IN VARCHAR2
743                     ,p_supv_sub4_psn_id         IN VARCHAR2
744                     ,p_sub_person_id            IN NUMBER
745                     ,p_sub_assignment_id        IN NUMBER
746                     ,p_sub_level                IN NUMBER
747                     ,p_sub_relative_level       IN NUMBER
748                     ,p_sub_business_group_id    IN NUMBER
749                     ,p_effective_start_date     IN DATE
750                     ,p_effective_end_date       IN DATE
751                     ,p_orphan_flag              IN VARCHAR2
752                     ,p_chain_id                 IN VARCHAR2) IS
753 
754 BEGIN
755 
756   -- increment the index
757   g_suph_row_count := g_suph_row_count + 1;
758 
759   -- set the table structures
760   g_suph_sup_psn_id(g_suph_row_count)      := p_supv_person_id;
761   g_suph_sup_asg_id(g_suph_row_count)      := p_supv_assignment_id;
762   g_suph_sup_level(g_suph_row_count)       := p_supv_level;
763   g_suph_sup_bgr_id(g_suph_row_count)      := p_supv_business_group_id;
764   g_suph_sup_ast_id(g_suph_row_count)      := p_supv_asg_status_type_id;
765   g_suph_sup_sub1_psn_id(g_suph_row_count) := p_supv_sub1_psn_id;
766   g_suph_sup_sub2_psn_id(g_suph_row_count) := p_supv_sub2_psn_id;
767   g_suph_sup_sub3_psn_id(g_suph_row_count) := p_supv_sub3_psn_id;
768   g_suph_sup_sub4_psn_id(g_suph_row_count) := p_supv_sub4_psn_id;
769   g_suph_sub_psn_id(g_suph_row_count)      := p_sub_person_id;
770   g_suph_sub_asg_id(g_suph_row_count)      := p_sub_assignment_id;
771   g_suph_sub_level(g_suph_row_count)       := p_sub_level;
772   g_suph_sub_rlt_lvl(g_suph_row_count)     := p_sub_relative_level;
773   g_suph_sub_bgr_id(g_suph_row_count)      := p_sub_business_group_id;
774   g_suph_start_date(g_suph_row_count)      := p_effective_start_date;
775   g_suph_end_date(g_suph_row_count)        := p_effective_end_date;
776   g_suph_orphan_flg(g_suph_row_count)      := p_orphan_flag;
777   g_suph_sub_chain_id(g_suph_row_count)    := p_chain_id;
778 
779 END insert_row;
780 
781 -- ----------------------------------------------------------------------------
782 -- Inserts row into global temporary table
783 -- ----------------------------------------------------------------------------
784 PROCEDURE insert_chn_row(p_person_id      IN NUMBER
785                         ,p_assignment_id  IN NUMBER
786                         ,p_start_date     IN DATE
787                         ,p_end_date       IN DATE
788                         ,p_chain_id       IN NUMBER
789                         ,p_person_level   IN NUMBER) IS
790 
791   l_user_id      NUMBER;
792 
793 BEGIN
794 
795   -- Initialize variables
796   l_user_id      := fnd_global.user_id;
797   g_current_time := sysdate;
798 
799   -- Add row
800   g_chn_row_count := g_chn_row_count + 1;
801   g_chn_psn_id(g_chn_row_count)     := p_person_id;
802   g_chn_asg_id(g_chn_row_count)     := p_assignment_id;
803   g_chn_start_date(g_chn_row_count) := p_start_date;
804   g_chn_end_date(g_chn_row_count)   := p_end_date;
805   g_chn_chain_id(g_chn_row_count)   := p_chain_id;
806   g_chn_psn_lvl(g_chn_row_count)    := p_person_level;
807 
808 END insert_chn_row;
809 
810 -- ----------------------------------------------------------------------------
811 -- Inserts row into global pl/sql table
812 -- ----------------------------------------------------------------------------
813 PROCEDURE insert_trn_row(p_sup_person_id     IN NUMBER
814                         ,p_trn_person_id     IN NUMBER
815                         ,p_trn_assignment_id IN NUMBER
816                         ,p_ref_person_id     IN NUMBER
817                         ,p_transfer_date     IN DATE
818                         ,p_trn_wrktyp_fk     IN VARCHAR2
819                         ,p_transfer_in_ind   IN NUMBER
820                         ,p_transfer_out_ind  IN NUMBER
821                         ,p_direct_ind        IN NUMBER
822                         ,p_direct_rec        IN NUMBER
823                         ,p_sec_asg_ind       IN NUMBER) IS
824 
825 BEGIN
826 
827   -- Add row
828   g_trn_row_count := g_trn_row_count + 1;
829   g_trn_sup_psn_id(g_trn_row_count)  := p_sup_person_id;
830   g_trn_psn_id(g_trn_row_count)      := p_trn_person_id;
831   g_trn_ref_id(g_trn_row_count)      := p_ref_person_id;
832   g_trn_asg_id(g_trn_row_count)      := p_trn_assignment_id;
833   g_trn_date(g_trn_row_count)        := p_transfer_date;
834   g_trn_wty_fk(g_trn_row_count)      := p_trn_wrktyp_fk;
835   g_trn_in_ind(g_trn_row_count)      := p_transfer_in_ind;
836   g_trn_out_ind(g_trn_row_count)     := p_transfer_out_ind;
837   g_trn_dir_ind(g_trn_row_count)     := p_direct_ind;
838   g_trn_dir_rec(g_trn_row_count)     := p_direct_rec;
839   g_trn_sec_asg_ind(g_trn_row_count) := p_sec_asg_ind;
840 
841 END insert_trn_row;
842 
843 -- ----------------------------------------------------------------------------
844 -- This procedure populates the person_id column in hri_eq_sprvsr_hrchy_chgs
845 -- by using the value of assignment_id
846 -- ----------------------------------------------------------------------------
847 PROCEDURE update_event_queue IS
848 
849 BEGIN
850 
851 -- 3667099 The events queue may contain records for events that have taken place
852 -- to assignment records which do not affect the supervisor hierarchy, for
853 -- example secondary assingments and non employee assingments.
854 -- Delete event queue records that are related to secondary assingments
855 -- ,non employee assignments and assignments that do not have any supervisor
856 -- 4186087 If a person is made a top supervisor or if a new top supervisor is
857 -- added the event should not be deleted, otherwise the person's record may
858 -- not be correct in the hiearchy.
859 -- Removed the condition (AND    supervisor_id is not null) from the inner query
860 
861 -- Delete records that are not primary employee assignment change events
862   DELETE /*+ PARALLEL(eq, default,default)*/ hri_eq_sprvsr_hrchy_chgs eq
863   WHERE assignment_id NOT IN
864          (SELECT assignment_id
865           FROM per_all_assignments_f asg
866           WHERE eq.assignment_id = asg.assignment_id
867           AND primary_flag = 'Y'
868           AND assignment_type IN ('E','C')
869           AND asg.effective_end_date >= eq.erlst_evnt_effective_date);
870 
871   debug(sql%rowcount || ' records deleted from supervior events queue.');
872 
873 -- Commit
874   commit;
875 
876 -- Set person ids on event queue
877   UPDATE hri_eq_sprvsr_hrchy_chgs  eq
878   SET person_id =
879        (SELECT person_id
880         FROM per_all_assignments_f asg
881         WHERE eq.assignment_id = asg.assignment_id
882         AND rownum = 1);
883 
884 EXCEPTION
885   WHEN OTHERS THEN
886 
887   output('An error occured while updating events queue records.');
888   output(sqlerrm);
889   g_msg_sub_group := NVL(g_msg_sub_group, 'UPDATE_EVENT_QUEUE');
890   RAISE;
891 
892 END update_event_queue;
893 
894 
895 -- ----------------------------------------------------------------------------
896 -- Removes records from the supervisor hierarchy after the earliest event date
897 -- End dates latest remaining record
898 -- Processes all records for the chunk
899 -- ----------------------------------------------------------------------------
900 PROCEDURE delete_and_end_date_suph_recs
901   (p_start_person_id   IN NUMBER,
902    p_end_person_id     IN NUMBER) IS
903 
904 BEGIN
905 
906   -- Delete chain updates after the date of refresh
907   DELETE FROM hri_cs_suph sph
908   WHERE sph.rowid IN
909          (SELECT sph2.rowid
910           FROM   hri_eq_sprvsr_hrchy_chgs evt,
911                  hri_cs_suph           sph2
912           WHERE evt.person_id   = sph2.sub_person_id
913           AND evt.person_id BETWEEN p_start_person_id
914                             AND p_end_person_id
915           AND evt.erlst_evnt_effective_date <= sph2.effective_start_date);
916 
917   debug(sql%rowcount || ' supervisor hierarchy records deleted.');
918 
919   -- Delete lookup chain updates after the date of refresh
920   DELETE FROM hri_cs_mngrsc_ct chn
921   WHERE chn.rowid IN
922          (SELECT chn2.rowid
923           FROM   hri_eq_sprvsr_hrchy_chgs evt,
924                  hri_cs_mngrsc_ct      chn2
925           WHERE evt.person_id   = chn2.mgrs_person_fk
926           AND evt.person_id BETWEEN p_start_person_id
927                             AND p_end_person_id
928           AND evt.erlst_evnt_effective_date <= chn2.mgrs_date_start);
929 
930   debug(sql%rowcount || ' supervisor chain lookup records deleted.');
931 
932   -- Set end dates to the day before the earliest effective change date
933   -- for latest chains of supervisor in event queue
934   UPDATE hri_cs_suph sph
935   SET effective_end_date =
936         (SELECT (evt.erlst_evnt_effective_date - 1)
937          FROM   hri_eq_sprvsr_hrchy_chgs evt
938          WHERE  evt.person_id = sph.sub_person_id
939          AND evt.erlst_evnt_effective_date BETWEEN sph.effective_start_date
940          AND     sph.effective_end_date)
941      ,last_update_date = sysdate
942   WHERE (sph.sub_person_id,
943          sph.sup_person_id,
944          sph.effective_start_date) IN
945         (SELECT
946           sph2.sub_person_id,
947           sph2.sup_person_id,
948           sph2.effective_start_date
949          FROM   hri_eq_sprvsr_hrchy_chgs evt,
950                 hri_cs_suph sph2
951          WHERE  evt.person_id = sph2.sub_person_id
952          AND evt.person_id BETWEEN p_start_person_id
953                            AND p_end_person_id
954          AND    evt.erlst_evnt_effective_date BETWEEN sph2.effective_start_date
955                                                   AND sph2.effective_end_date);
956 
957   debug(sql%rowcount || ' supervisor hierarchy records end dated.');
958 
959   -- Set end dates to the day before the earliest effective change date
960   -- for latest lookup chains of supervisor in event queue
961   UPDATE hri_cs_mngrsc_ct      chn
962   SET chn.mgrs_date_end =
963         (SELECT (evt.erlst_evnt_effective_date - 1)
964          FROM   hri_eq_sprvsr_hrchy_chgs evt
965          WHERE  evt.person_id = chn.mgrs_person_fk
966          AND evt.erlst_evnt_effective_date BETWEEN chn.mgrs_date_start
967                                            AND chn.mgrs_date_end)
968      ,last_update_date = sysdate
969   WHERE chn.mgrs_mngrsc_pk IN
970         (SELECT
971           chn2.mgrs_mngrsc_pk
972          FROM   hri_eq_sprvsr_hrchy_chgs evt,
973                 hri_cs_mngrsc_ct         chn2
974          WHERE  evt.person_id = chn2.mgrs_person_fk
975          AND evt.person_id BETWEEN p_start_person_id
976                            AND p_end_person_id
977          AND    evt.erlst_evnt_effective_date BETWEEN chn.mgrs_date_start
978                                               AND chn.mgrs_date_end);
979 
980   debug(sql%rowcount || ' supervisor lookup records end dated.');
981 
982 EXCEPTION
983   WHEN OTHERS THEN
984 
985     output('An error occured while deleting and end dating records');
986     output(SQLERRM);
987     g_msg_sub_group := NVL(g_msg_sub_group, 'END_DATE_PRIOR_RECORDS');
988     RAISE;
989 
990 END delete_and_end_date_suph_recs;
991 
992 -- ----------------------------------------------------------------------------
993 -- Removes later duplicate events for a person in hri_eq_sprvsr_hrchy_chgs
994 -- leaving only the earliest recorded event held in the table
995 -- ----------------------------------------------------------------------------
996 PROCEDURE remove_duplicates IS
997 
998 BEGIN
999 
1000   -- Delete duplicate events from queue
1001   DELETE FROM hri_eq_sprvsr_hrchy_chgs evt
1002   WHERE EXISTS
1003     (SELECT 'x'
1004      FROM   hri_eq_sprvsr_hrchy_chgs evt2
1005      WHERE  evt2.person_id = evt.person_id
1006      AND ((evt.erlst_evnt_effective_date = evt2.erlst_evnt_effective_date
1007            AND evt.rowid < evt2.rowid)
1008       OR
1009           evt.erlst_evnt_effective_date > evt2.erlst_evnt_effective_date));
1010 
1011   debug(sql%rowcount || ' duplicate records deleted.');
1012 
1013   -- Commit
1014   commit;
1015 
1016 EXCEPTION
1017   WHEN OTHERS THEN
1018 
1019     output('An error occured while removing duplicates from the change list.');
1020     output(SQLERRM);
1021     g_msg_sub_group := NVL(g_msg_sub_group, 'REMOVE_DUPLICATES');
1022     RAISE;
1023 
1024 END remove_duplicates;
1025 
1026 -- ----------------------------------------------------------------------------
1027 -- For every change for a supervisor there is a knock on effect on his
1028 -- subordinates i.e. the supervisor hierarchy for the subordinates changes.
1029 -- This procedure finds the subordinates for a supervisor that has an event
1030 -- and inserts them into 'hri_eq_sprvsr_hrchy_chgs'.
1031 -- ----------------------------------------------------------------------------
1032 PROCEDURE find_subordinates IS
1033 
1034 BEGIN
1035 
1036   -- Insert subordinate records into event queue
1037   INSERT /*+ append */ INTO hri_eq_sprvsr_hrchy_chgs
1038    (person_id
1039    ,assignment_id
1040    ,erlst_evnt_effective_date
1041    ,source_code)
1042   SELECT
1043    sph.sub_person_id
1044   ,sph.sub_assignment_id
1045   ,GREATEST(evt.erlst_evnt_effective_date,sph.effective_start_date)
1046   ,'DERIVED'
1047   FROM
1048    hri_eq_sprvsr_hrchy_chgs evt
1049   ,hri_cs_suph sph
1050   WHERE sph.sup_person_id = evt.person_id
1051   AND sph.sub_relative_level > 0
1052   AND sph.effective_end_date >= evt.erlst_evnt_effective_date;
1053 
1054   debug(sql%rowcount || ' subordinate records inserted.');
1055 
1056   -- Commit
1057   commit;
1058 
1059 EXCEPTION
1060   WHEN OTHERS THEN
1061 
1062     output('An error occured while adding subordinates to the change list.');
1063     output(SQLERRM);
1064     g_msg_sub_group := NVL(g_msg_sub_group, 'FIND_SUBORDINATES');
1065     RAISE;
1066 
1067 END find_subordinates;
1068 
1069 -- ----------------------------------------------------------------------------
1070 -- Runs given sql statement dynamically
1071 -- ----------------------------------------------------------------------------
1072 PROCEDURE run_sql_stmt_noerr(p_sql_stmt   VARCHAR2) IS
1073 
1074 BEGIN
1075 
1076   EXECUTE IMMEDIATE p_sql_stmt;
1077 
1078 EXCEPTION WHEN OTHERS THEN
1079 
1080   output('Error running sql:');
1081   output(SUBSTR(p_sql_stmt,1,230));
1082 
1083 END run_sql_stmt_noerr;
1084 
1085 -- ----------------------------------------------------------------------------
1086 -- Sets global parameters from multi-threading process parameters
1087 -- ----------------------------------------------------------------------------
1088 PROCEDURE set_parameters(p_mthd_action_id  IN NUMBER) IS
1089 
1090 BEGIN
1091 
1092 -- If parameters haven't already been set, then set them
1093   IF (g_refresh_start_date IS NULL) THEN
1094     g_mthd_action_array    := hri_opl_multi_thread.get_mthd_action_array
1095                                (p_mthd_action_id);
1096     g_refresh_start_date   := g_mthd_action_array.collect_from_date;
1097     g_full_refresh         := g_mthd_action_array.full_refresh_flag;
1098     g_load_helper_table    := g_mthd_action_array.attribute1;
1099     IF (fnd_profile.value('HRI_IMPL_OBIEE') = 'Y') THEN
1100       g_implement_obiee      := 'Y';
1101     ELSE
1102       g_implement_obiee      := 'N';
1103     END IF;
1104   END IF;
1105 
1106 END set_parameters;
1107 
1108 -- ----------------------------------------------------------------------------
1109 -- Returns next value from chain id sequence
1110 -- ----------------------------------------------------------------------------
1111 FUNCTION get_chain_id RETURN NUMBER IS
1112 
1113   l_chain_id    NUMBER;
1114 
1115 BEGIN
1116 
1117   SELECT hri_cs_mngrsc_ct_s.nextval
1118   INTO l_chain_id
1119   FROM dual;
1120 
1121   RETURN l_chain_id;
1122 
1123 END get_chain_id;
1124 
1125 
1126 -- ----------------------------------------------------------------------------
1127 -- Given a transfer table containing all nodes before and after with details
1128 -- of whether each node existed in the transferees management chain before and
1129 -- after, insert a row for each node having the transfer.
1130 -- ----------------------------------------------------------------------------
1131 PROCEDURE process_transfer
1132      (p_trn_psn_id     IN NUMBER,
1133       p_trn_asg_id     IN NUMBER,
1134       p_trn_aty_id     IN VARCHAR2,
1135       p_trn_date       IN DATE,
1136       p_trn_tab        IN g_trn_tab_type) IS
1137 
1138   -- All secondary assignments reporting to the transferee before
1139   -- and after the transfer
1140   CURSOR sec_directs_csr IS
1141   SELECT
1142    sec_pre.person_id
1143   ,sec_pre.assignment_id
1144   ,CASE WHEN sec_pre.assignment_type = 'E'
1145         THEN 'EMP'
1146         ELSE 'CWK'
1147    END            ptyp_wrktyp_fk
1148   FROM
1149    per_all_assignments_f  sec_pre
1150   ,per_all_assignments_f  sec_post
1151   WHERE sec_pre.supervisor_id = p_trn_psn_id
1152   AND sec_post.assignment_id = sec_pre.assignment_id
1153   AND sec_post.supervisor_id = sec_pre.supervisor_id
1154   AND sec_post.primary_flag = 'N'
1155   AND sec_pre.assignment_type IN ('E','C')
1156   AND p_trn_date - 1 BETWEEN sec_pre.effective_start_date AND sec_pre.effective_end_date
1157   AND p_trn_date BETWEEN sec_post.effective_start_date AND sec_post.effective_end_date;
1158 
1159   l_idx               NUMBER;
1160   l_direct_ind        NUMBER;
1161   l_transfer_in_ind   NUMBER;
1162   l_transfer_out_ind  NUMBER;
1163   l_wrktyp_fk         VARCHAR2(30);
1164 
1165   l_sec_psn_tab       g_number_tab_type;
1166   l_sec_asg_tab       g_number_tab_type;
1167   l_sec_wrktyp_tab    g_varchar2_tab_type;
1168 
1169 BEGIN
1170 
1171   -- Load list of secondary assignments reporting to the transferee before
1172   -- and after the transfer
1173   OPEN sec_directs_csr;
1174   FETCH sec_directs_csr BULK COLLECT INTO
1175     l_sec_psn_tab, l_sec_asg_tab, l_sec_wrktyp_tab;
1176   CLOSE sec_directs_csr;
1177 
1178   -- Set worker type for transferee
1179   IF p_trn_aty_id = 'E' THEN
1180     l_wrktyp_fk := 'EMP';
1181   ELSE
1182     l_wrktyp_fk := 'CWK';
1183   END IF;
1184 
1185   l_idx := p_trn_tab.FIRST;
1186 
1187   WHILE l_idx IS NOT NULL LOOP
1188 
1189     -- If node exists before and after transfer it is a transfer within
1190     -- the hierarchy, so do not do anything
1191     IF (p_trn_tab(l_idx).node_exists_before AND
1192         p_trn_tab(l_idx).node_exists_after) THEN
1193       null;
1194 
1195     ELSE
1196 
1197       -- If node exists before (but not after) then it is a transfer out
1198       IF (p_trn_tab(l_idx).node_exists_before) THEN
1199 
1200         l_transfer_in_ind  := 0;
1201         l_transfer_out_ind := 1;
1202         l_direct_ind       := p_trn_tab(l_idx).node_direct_before;
1203 
1204       -- If node exists after (but not before) then it is a transfer in
1205       ELSE
1206 
1207         l_transfer_in_ind  := 1;
1208         l_transfer_out_ind := 0;
1209         l_direct_ind       := p_trn_tab(l_idx).node_direct_after;
1210 
1211       END IF;
1212 
1213       -- Insert transfer record for transferee
1214       insert_trn_row
1215        (p_sup_person_id     => l_idx
1216        ,p_trn_person_id     => p_trn_psn_id
1217        ,p_trn_assignment_id => p_trn_asg_id
1218        ,p_ref_person_id     => -1
1219        ,p_transfer_date     => p_trn_date
1220        ,p_trn_wrktyp_fk     => l_wrktyp_fk
1221        ,p_transfer_in_ind   => l_transfer_in_ind
1222        ,p_transfer_out_ind  => l_transfer_out_ind
1223        ,p_direct_ind        => l_direct_ind
1224        ,p_direct_rec        => 0
1225        ,p_sec_asg_ind       => 0);
1226 
1227       -- Insert transfer record for any secondary assignments reporting to the
1228       -- transferee before and after transfer
1229       IF l_sec_psn_tab.EXISTS(1) THEN
1230         FOR i IN 1..l_sec_psn_tab.LAST LOOP
1231           insert_trn_row
1232            (p_sup_person_id     => l_idx
1233            ,p_trn_person_id     => l_sec_psn_tab(i)
1234            ,p_trn_assignment_id => l_sec_asg_tab(i)
1235            ,p_ref_person_id     => p_trn_psn_id
1236            ,p_transfer_date     => p_trn_date
1237            ,p_trn_wrktyp_fk     => l_sec_wrktyp_tab(i)
1238            ,p_transfer_in_ind   => l_transfer_in_ind
1239            ,p_transfer_out_ind  => l_transfer_out_ind
1240            ,p_direct_ind        => 0
1241            ,p_direct_rec        => 0
1242            ,p_sec_asg_ind       => 1);
1243         END LOOP;
1244       END IF;
1245 
1246     END IF;
1247 
1248     -- Filter out direct record transfers within
1249     IF (p_trn_tab(l_idx).node_direct_before = 1 AND
1250         p_trn_tab(l_idx).node_direct_after = 1) THEN
1251 
1252       null;
1253 
1254     ELSE
1255 
1256       -- If node is a direct manager before but not after it is a direct record transfer out
1257       IF (p_trn_tab(l_idx).node_direct_before = 1) THEN
1258 
1259         -- Insert transfer record for transferee
1260         insert_trn_row
1261          (p_sup_person_id     => l_idx
1262          ,p_trn_person_id     => p_trn_psn_id
1263          ,p_trn_assignment_id => p_trn_asg_id
1264          ,p_ref_person_id     => -1
1265          ,p_transfer_date     => p_trn_date
1266          ,p_trn_wrktyp_fk     => l_wrktyp_fk
1267          ,p_transfer_in_ind   => 0
1268          ,p_transfer_out_ind  => 1
1269          ,p_direct_ind        => 1
1270          ,p_direct_rec        => 1
1271          ,p_sec_asg_ind       => 0);
1272 
1273       -- If node is a direct manager after but not before it is a direct record transfer in
1274       ELSIF (p_trn_tab(l_idx).node_direct_after = 1) THEN
1275 
1276         -- Insert transfer record for transferee
1277         insert_trn_row
1278          (p_sup_person_id     => l_idx
1279          ,p_trn_person_id     => p_trn_psn_id
1280          ,p_trn_assignment_id => p_trn_asg_id
1281          ,p_ref_person_id     => -1
1282          ,p_transfer_date     => p_trn_date
1283          ,p_trn_wrktyp_fk     => l_wrktyp_fk
1284          ,p_transfer_in_ind   => 1
1285          ,p_transfer_out_ind  => 0
1286          ,p_direct_ind        => 1
1287          ,p_direct_rec        => 1
1288          ,p_sec_asg_ind       => 0);
1289 
1290       END IF;
1291 
1292     END IF;
1293 
1294     -- Increment index
1295     l_idx := p_trn_tab.NEXT(l_idx);
1296 
1297   END LOOP;
1298 
1299 END process_transfer;
1300 
1301 
1302 -- ----------------------------------------------------------------------------
1303 -- Given a chain cache table containing the previous manager chain and a new
1304 -- manager chain
1305 --   - Insert new chain records
1306 --   - Insert chain id lookups
1307 -- ----------------------------------------------------------------------------
1308 PROCEDURE process_chain
1309  (p_new_psn_tab     IN g_number_tab_type,
1310   p_new_asg_tab     IN g_number_tab_type,
1311   p_new_bgr_tab     IN g_number_tab_type,
1312   p_new_sup_tab     IN g_number_tab_type,
1313   p_new_ast_tab     IN g_number_tab_type,
1314   p_new_end_tab     IN g_date_tab_type,
1315   p_new_aty_tab     IN g_varchar2_tab_type,
1316   p_orphan_flag     IN VARCHAR2,
1317   p_chain_table     IN OUT NOCOPY g_chain_type,
1318   p_loop_date       IN  DATE,
1319   p_next_loop_date  IN  DATE) IS
1320 
1321   l_sup_level         PLS_INTEGER;
1322   l_sub_level         PLS_INTEGER;
1323 
1324   l_chain_id          NUMBER;
1325   l_sup_sub1_psn_id   NUMBER;
1326   l_sup_sub2_psn_id   NUMBER;
1327   l_sup_sub3_psn_id   NUMBER;
1328   l_sup_sub4_psn_id   NUMBER;
1329 
1330   l_chain_end_date    DATE;
1331 
1332   l_trn_tab           g_trn_tab_type;
1333   l_is_a_trn          BOOLEAN;
1334 
1335 BEGIN
1336 
1337   -- Get chain id
1338   l_chain_id := get_chain_id;
1339 
1340   -- Set chain end date
1341   IF (p_next_loop_date = g_end_of_time) THEN
1342     l_chain_end_date := g_end_of_time;
1343   ELSE
1344     l_chain_end_date := p_next_loop_date - 1;
1345   END IF;
1346 
1347   -- Set the new level for the person (equal to the number of links in
1348   -- the new chain)
1349   l_sub_level := p_new_psn_tab.LAST;
1350 
1351   -- Load manager chain before transfer
1352   IF p_chain_table.EXISTS(1) THEN
1353     l_is_a_trn := TRUE;
1354     FOR i IN 1..p_chain_table.LAST LOOP
1355       l_trn_tab(p_chain_table(i).person_id).node_exists_before := TRUE;
1356       IF i = p_chain_table.LAST - 1 THEN
1357         l_trn_tab(p_chain_table(i).person_id).node_direct_before := 1;
1358       ELSE
1359         l_trn_tab(p_chain_table(i).person_id).node_direct_before := 0;
1360       END IF;
1361     END LOOP;
1362   ELSE
1363     l_is_a_trn := FALSE;
1364   END IF;
1365 
1366   -- Loop through new management chain (top supervisor last)
1367   FOR i IN 1..l_sub_level LOOP
1368 
1369     -- Set manager chain after transfer
1370     l_trn_tab(p_new_psn_tab(i)).node_exists_after := TRUE;
1371     IF i = 2 THEN
1372       l_trn_tab(p_new_psn_tab(i)).node_direct_after := 1;
1373     ELSE
1374       l_trn_tab(p_new_psn_tab(i)).node_direct_after := 0;
1375     END IF;
1376 
1377     -- Set level for link as default order is reverse level order
1378     l_sup_level := l_sub_level - i + 1;
1379 
1380     -- Set relative levels
1381     IF (i - 1) >= 1 THEN
1382       l_sup_sub1_psn_id := p_new_psn_tab(i - 1);
1383     ELSE
1384       l_sup_sub1_psn_id := p_new_psn_tab(1);
1385     END IF;
1386     IF (i - 2) >= 1 THEN
1387       l_sup_sub2_psn_id := p_new_psn_tab(i - 2);
1388     ELSE
1389       l_sup_sub2_psn_id := p_new_psn_tab(1);
1390     END IF;
1391     IF (i - 3) >= 1 THEN
1392       l_sup_sub3_psn_id := p_new_psn_tab(i - 3);
1393     ELSE
1394       l_sup_sub3_psn_id := p_new_psn_tab(1);
1395     END IF;
1396     IF (i - 4) >= 1 THEN
1397       l_sup_sub4_psn_id := p_new_psn_tab(i - 4);
1398     ELSE
1399       l_sup_sub4_psn_id := p_new_psn_tab(1);
1400     END IF;
1401 
1402     -- Insert row
1403     insert_row
1404      (p_supv_person_id          => p_new_psn_tab(i)
1405      ,p_supv_assignment_id      => p_new_asg_tab(i)
1406      ,p_supv_level              => l_sup_level
1407      ,p_supv_business_group_id  => p_new_bgr_tab(i)
1408      ,p_supv_asg_status_type_id => p_new_ast_tab(i)
1409      ,p_supv_sub1_psn_id        => l_sup_sub1_psn_id
1410      ,p_supv_sub2_psn_id        => l_sup_sub2_psn_id
1411      ,p_supv_sub3_psn_id        => l_sup_sub3_psn_id
1412      ,p_supv_sub4_psn_id        => l_sup_sub4_psn_id
1413      ,p_sub_person_id           => p_new_psn_tab(1)
1414      ,p_sub_assignment_id       => p_new_asg_tab(1)
1415      ,p_sub_level               => l_sub_level
1416      ,p_sub_relative_level      => l_sub_level - l_sup_level
1417      ,p_sub_business_group_id   => p_new_bgr_tab(1)
1418      ,p_effective_start_date    => p_loop_date
1419      ,p_effective_end_date      => l_chain_end_date
1420      ,p_orphan_flag             => p_orphan_flag
1421      ,p_chain_id                => l_chain_id);
1422 
1423     -- Update chain table with new link details
1424     p_chain_table(l_sup_level).person_id          := p_new_psn_tab(i);
1425     p_chain_table(l_sup_level).assignment_id      := p_new_asg_tab(i);
1426     p_chain_table(l_sup_level).business_group_id  := p_new_bgr_tab(i);
1427     p_chain_table(l_sup_level).asg_status_type_id := p_new_ast_tab(i);
1428     p_chain_table(l_sup_level).start_date         := p_loop_date;
1429     p_chain_table(l_sup_level).relative_level     := l_sub_level - l_sup_level;
1430     p_chain_table(l_sup_level).orphan_flag        := p_orphan_flag;
1431 
1432   END LOOP;
1433 
1434   -- Remove any additional records in the chain table that result from
1435   -- a promotion (person decrease in absolute level)
1436   FOR i IN (l_sub_level + 1)..p_chain_table.LAST LOOP
1437     p_chain_table.DELETE(i);
1438   END LOOP;
1439 
1440   -- Insert chain lookup
1441   insert_chn_row
1442    (p_person_id     => p_new_psn_tab(1)
1443    ,p_assignment_id => p_new_asg_tab(1)
1444    ,p_start_date    => p_loop_date
1445    ,p_end_date      => l_chain_end_date
1446    ,p_chain_id      => l_chain_id
1447    ,p_person_level  => l_sub_level);
1448 
1449   -- Process transfer
1450   IF l_is_a_trn THEN
1451     process_transfer
1452      (p_trn_psn_id => p_new_psn_tab(1),
1453       p_trn_asg_id => p_new_asg_tab(1),
1454       p_trn_aty_id => p_new_aty_tab(1),
1455       p_trn_date   => p_loop_date,
1456       p_trn_tab    => l_trn_tab);
1457   END IF;
1458 
1459 END process_chain;
1460 
1461 
1462 -- ----------------------------------------------------------------------------
1463 -- Tree-walk the manager hierarchy for a person on a given date
1464 -- Trap loops and return as orphans
1465 -- ----------------------------------------------------------------------------
1466 PROCEDURE get_manager_chain
1467  (p_person_id         IN NUMBER,
1468   p_effective_date    IN DATE,
1469   p_hier_psn_tab      OUT NOCOPY g_number_tab_type,
1470   p_hier_asg_tab      OUT NOCOPY g_number_tab_type,
1471   p_hier_bgr_tab      OUT NOCOPY g_number_tab_type,
1472   p_hier_sup_tab      OUT NOCOPY g_number_tab_type,
1473   p_hier_ast_tab      OUT NOCOPY g_number_tab_type,
1474   p_hier_end_tab      OUT NOCOPY g_date_tab_type,
1475   p_hier_aty_tab      OUT NOCOPY g_varchar2_tab_type,
1476   p_next_change_date  OUT NOCOPY DATE) IS
1477 
1478   -- Return PL/SQL tables
1479   l_psn_tab     g_number_tab_type;
1480   l_asg_tab     g_number_tab_type;
1481   l_bgr_tab     g_number_tab_type;
1482   l_sup_tab     g_number_tab_type;
1483   l_ast_tab     g_number_tab_type;
1484   l_end_tab     g_date_tab_type;
1485   l_aty_tab     g_varchar2_tab_type;
1486 
1487   -- Loop message
1488   l_loop_msg    VARCHAR2(2000);
1489 
1490   -- Main tree walk returns rows in default order starting with
1491   -- the person and ending with the top manager
1492   CURSOR manager_chain_csr(v_effective_date   DATE) IS
1493   SELECT
1494    hier.person_id
1495   ,hier.assignment_id
1496   ,hier.business_group_id
1497   ,hier.supervisor_person_id
1498   ,hier.assignment_status_type_id
1499   ,hier.effective_end_date
1500   ,hier.assignment_type
1501   FROM
1502    (SELECT
1503      ase.person_id
1504     ,ase.assignment_id
1505     ,ase.business_group_id
1506     ,ase.supervisor_person_id
1507     ,ase.assignment_status_type_id
1508     ,ase.effective_end_date
1509     ,ase.assignment_type
1510     FROM
1511      hri_cs_asgn_suph_events_ct  ase
1512     WHERE ase.primary_flag = 'Y'
1513     AND v_effective_date BETWEEN ase.effective_start_date
1514                          AND ase.effective_end_date
1515    ) hier
1516   START WITH hier.person_id = p_person_id
1517   CONNECT BY hier.person_id = PRIOR hier.supervisor_person_id;
1518 -- NO ORDER BY LEAVE DEFAULT!
1519 
1520   -- If the main tree walk fails then treat the person as an orphan
1521   -- and get their next change date which is the earlier of:
1522   --    - Next change date
1523   --    - 1 month on provided this is less than system date
1524   CURSOR loop_in_chain_csr(v_effective_date   DATE) IS
1525   SELECT
1526    ase.person_id
1527   ,ase.assignment_id
1528   ,ase.business_group_id
1529   ,ase.supervisor_person_id
1530   ,ase.assignment_status_type_id
1531   ,CASE WHEN v_effective_date >= ADD_MONTHS(TRUNC(SYSDATE), -1)
1532         THEN ase.effective_end_date
1533         ELSE LEAST(ase.effective_end_date, ADD_MONTHS(v_effective_date, 1))
1534    END
1535   ,ase.assignment_type
1536   FROM
1537    hri_cs_asgn_suph_events_ct  ase
1538   WHERE ase.person_id = p_person_id
1539   AND ase.primary_flag = 'Y'
1540   AND v_effective_date BETWEEN ase.effective_start_date
1541                        AND ase.effective_end_date;
1542 
1543 BEGIN
1544 
1545   -- PL/SQL block to trap loop exceptions
1546   BEGIN
1547 
1548     -- Get first supervisor chain for person
1549     OPEN manager_chain_csr(p_effective_date);
1550     FETCH manager_chain_csr
1551       BULK COLLECT INTO
1552         l_psn_tab,
1553         l_asg_tab,
1554         l_bgr_tab,
1555         l_sup_tab,
1556         l_ast_tab,
1557         l_end_tab,
1558         l_aty_tab;
1559     CLOSE manager_chain_csr;
1560 
1561     -- Loop not encountered, so output next loop message
1562     g_log_sup_loop := TRUE;
1563 
1564   EXCEPTION WHEN OTHERS THEN
1565 
1566     -- Close cursor
1567     IF manager_chain_csr%ISOPEN THEN
1568       CLOSE manager_chain_csr;
1569     END IF;
1570 
1571     -- Log message if first iteration of encountering loop
1572     IF g_log_sup_loop THEN
1573 
1574       -- Loop diagnostics
1575       l_loop_msg := get_sup_loop_message
1576                      (p_message        => 'HRI_407283_SUP_LOOP_MSG'
1577                      ,p_effective_date => p_effective_date
1578                      ,p_person_id      => p_person_id);
1579 
1580       -- Write message to concurrent program log
1581       output(l_loop_msg);
1582 
1583       -- Write message to log table hri_adm_msg_log
1584       hri_bpl_conc_log.log_process_info
1585        (p_package_name      => 'HRI_OPL_SUPH_HST'
1586        ,p_msg_type          => 'WARNING'
1587        ,p_effective_date    => p_effective_date
1588        ,p_person_id         => p_person_id
1589        ,p_note              => l_loop_msg
1590        ,p_msg_group         => 'SUP_LOOP');
1591 
1592     END IF;
1593 
1594     -- Do not output further loop messages until the loop is fixed
1595     g_log_sup_loop := FALSE;
1596 
1597     -- Loop in chain
1598     -- get default information for the person
1599     OPEN loop_in_chain_csr(p_effective_date);
1600     FETCH loop_in_chain_csr
1601       BULK COLLECT INTO
1602         l_psn_tab,
1603         l_asg_tab,
1604         l_bgr_tab,
1605         l_sup_tab,
1606         l_ast_tab,
1607         l_end_tab,
1608         l_aty_tab;
1609     CLOSE loop_in_chain_csr;
1610 
1611   END;
1612 
1613   -- Get next change date
1614   IF l_end_tab.EXISTS(1) THEN
1615 
1616     -- Initialize to end of time
1617     p_next_change_date := g_end_of_time;
1618 
1619     -- Set to day after earliest link end date
1620     FOR i IN 1..l_end_tab.LAST LOOP
1621       IF (l_end_tab(i) < p_next_change_date) THEN
1622         p_next_change_date := l_end_tab(i) + 1;
1623       END IF;
1624     END LOOP;
1625 
1626   END IF;
1627 
1628   -- Return the tables
1629   p_hier_psn_tab := l_psn_tab;
1630   p_hier_asg_tab := l_asg_tab;
1631   p_hier_bgr_tab := l_bgr_tab;
1632   p_hier_sup_tab := l_sup_tab;
1633   p_hier_ast_tab := l_ast_tab;
1634   p_hier_end_tab := l_end_tab;
1635   p_hier_aty_tab := l_aty_tab;
1636 
1637 EXCEPTION WHEN OTHERS THEN
1638 
1639   IF loop_in_chain_csr%ISOPEN THEN
1640     CLOSE loop_in_chain_csr;
1641   END IF;
1642 
1643   g_msg_sub_group := NVL(g_msg_sub_group, 'GET_MANAGER_CHAIN');
1644 
1645   RAISE;
1646 
1647 END get_manager_chain;
1648 
1649 
1650 -- ----------------------------------------------------------------------------
1651 -- Initializes chain cache in incremental mode
1652 -- ----------------------------------------------------------------------------
1653 PROCEDURE initialize_previous_chain
1654    (p_person_id        IN NUMBER,
1655     p_effective_date   IN DATE,
1656     p_chain_table      IN OUT NOCOPY g_chain_type) IS
1657 
1658   -- Results of tree walk
1659   l_hier_psn_tab     g_number_tab_type;
1660   l_hier_asg_tab     g_number_tab_type;
1661   l_hier_bgr_tab     g_number_tab_type;
1662   l_hier_sup_tab     g_number_tab_type;
1663   l_hier_ast_tab     g_number_tab_type;
1664   l_hier_end_tab     g_date_tab_type;
1665   l_hier_aty_tab     g_varchar2_tab_type;
1666   l_dummy            DATE;
1667   l_sup_level        NUMBER;
1668 
1669 BEGIN
1670 
1671   -- Leave chain table as NULL in full refresh mode
1672   -- as there data before global start date are ignored
1673   IF (g_full_refresh = 'N') THEN
1674 
1675     -- Get previous supervisor chain for person
1676     get_manager_chain
1677      (p_person_id        => p_person_id,
1678       p_effective_date   => p_effective_date,
1679       p_hier_psn_tab     => l_hier_psn_tab,
1680       p_hier_asg_tab     => l_hier_asg_tab,
1681       p_hier_bgr_tab     => l_hier_bgr_tab,
1682       p_hier_sup_tab     => l_hier_sup_tab,
1683       p_hier_ast_tab     => l_hier_ast_tab,
1684       p_hier_end_tab     => l_hier_end_tab,
1685       p_hier_aty_tab     => l_hier_aty_tab,
1686       p_next_change_date => l_dummy);
1687 
1688     -- If previous chain found
1689     IF l_hier_psn_tab.EXISTS(1) THEN
1690 
1691       -- Loop through previous chain
1692       FOR i IN 1..l_hier_psn_tab.LAST LOOP
1693 
1694         -- Set level for link as default order is reverse level order
1695         l_sup_level := l_hier_psn_tab.LAST - i + 1;
1696 
1697         -- Update chain table with new link details
1698         p_chain_table(l_sup_level).person_id          := l_hier_psn_tab(i);
1699         p_chain_table(l_sup_level).assignment_id      := l_hier_asg_tab(i);
1700         p_chain_table(l_sup_level).business_group_id  := l_hier_bgr_tab(i);
1701         p_chain_table(l_sup_level).asg_status_type_id := l_hier_ast_tab(i);
1702         p_chain_table(l_sup_level).start_date         := p_effective_date;
1703         p_chain_table(l_sup_level).relative_level     := l_hier_psn_tab.LAST - l_sup_level;
1704 
1705         -- Set orphan flag
1706         IF (l_hier_sup_tab(l_hier_psn_tab.LAST) = -1) THEN
1707           p_chain_table(l_sup_level).orphan_flag := 'N';
1708         ELSE
1709           p_chain_table(l_sup_level).orphan_flag := 'Y';
1710         END IF;
1711 
1712       END LOOP;
1713 
1714     END IF;
1715   END IF;
1716 
1717 END initialize_previous_chain;
1718 
1719 
1720 -- ----------------------------------------------------------------------------
1721 -- Samples manager chain for given person between the given dates for the
1722 -- period of service
1723 -- ----------------------------------------------------------------------------
1724 PROCEDURE process_period_of_work(p_person_id   IN NUMBER,
1725                                  p_start_date  IN DATE,
1726                                  p_end_date    IN DATE) IS
1727 
1728   -- Main loop variable
1729   l_loop_date        DATE;
1730   l_next_loop_date   DATE;
1731 
1732   -- Whether the chain is an orphan
1733   l_orphan_flag      VARCHAR2(30);
1734 
1735   -- Results of tree walk
1736   l_hier_psn_tab     g_number_tab_type;
1737   l_hier_asg_tab     g_number_tab_type;
1738   l_hier_bgr_tab     g_number_tab_type;
1739   l_hier_sup_tab     g_number_tab_type;
1740   l_hier_ast_tab     g_number_tab_type;
1741   l_hier_end_tab     g_date_tab_type;
1742   l_hier_aty_tab     g_varchar2_tab_type;
1743 
1744   -- Information about current chain within the hierarchy
1745   l_chain_table              g_chain_type;
1746 
1747   -- Whether to exit the loop
1748   l_exit_loop        VARCHAR2(30);
1749 
1750 BEGIN
1751 
1752   -- Initialization
1753   l_loop_date := p_start_date;
1754   l_exit_loop := 'N';
1755   g_log_sup_loop := TRUE;
1756   initialize_previous_chain
1757    (p_person_id      => p_person_id,
1758     p_effective_date => l_loop_date - 1,
1759     p_chain_table    => l_chain_table);
1760 
1761   -- Get first supervisor chain for person
1762   get_manager_chain
1763    (p_person_id        => p_person_id,
1764     p_effective_date   => l_loop_date,
1765     p_hier_psn_tab     => l_hier_psn_tab,
1766     p_hier_asg_tab     => l_hier_asg_tab,
1767     p_hier_bgr_tab     => l_hier_bgr_tab,
1768     p_hier_sup_tab     => l_hier_sup_tab,
1769     p_hier_ast_tab     => l_hier_ast_tab,
1770     p_hier_end_tab     => l_hier_end_tab,
1771     p_hier_aty_tab     => l_hier_aty_tab,
1772     p_next_change_date => l_next_loop_date);
1773 
1774   -- If no data is found there may be assignment records missing
1775   -- at the start of the period of service. Attempt to re-initialize
1776   -- based on the earliest assignment record
1777   IF (NOT l_hier_psn_tab.EXISTS(1)) THEN
1778 
1779   -- Output warning message
1780     output('WARNING: No chain found for person ' || to_char(p_person_id) ||
1781           ' on ' || to_char(l_loop_date, 'DD-MON-YYYY'));
1782 
1783   -- Get the earliest assignment record
1784     SELECT MIN(effective_start_date)
1785     INTO l_loop_date
1786     FROM hri_cs_asgn_suph_events_ct
1787     WHERE person_id = p_person_id
1788     AND primary_flag = 'Y';
1789 
1790   -- If the earliest assignment record exists and is later than
1791   -- the date already attempted then retry chain initialization
1792     IF (l_loop_date > p_start_date) THEN
1793 
1794       -- Get first supervisor chain for person
1795       get_manager_chain
1796        (p_person_id        => p_person_id,
1797         p_effective_date   => l_loop_date,
1798         p_hier_psn_tab     => l_hier_psn_tab,
1799         p_hier_asg_tab     => l_hier_asg_tab,
1800         p_hier_bgr_tab     => l_hier_bgr_tab,
1801         p_hier_sup_tab     => l_hier_sup_tab,
1802         p_hier_ast_tab     => l_hier_ast_tab,
1803         p_hier_end_tab     => l_hier_end_tab,
1804         p_hier_aty_tab     => l_hier_aty_tab,
1805         p_next_change_date => l_next_loop_date);
1806 
1807     END IF;
1808 
1809   END IF;
1810 
1811   -- If still no data is found there is some data issue since
1812   -- this procedure is called with the start date set within
1813   -- an active period of service
1814   IF (NOT l_hier_psn_tab.EXISTS(1)) THEN
1815 
1816   -- Output warning message
1817     output('WARNING: No chain found for person ' || to_char(p_person_id) ||
1818           ' on ' || to_char(l_loop_date, 'DD-MON-YYYY'));
1819 
1820   ELSE
1821 
1822     -- Test for orphan chain
1823     -- Chain is an orphan if the top manager, ordered last, has a
1824     -- not-null supervisor id
1825     IF (l_hier_sup_tab(l_hier_psn_tab.LAST) = -1) THEN
1826       l_orphan_flag := 'N';
1827     ELSE
1828       l_orphan_flag := 'Y';
1829     END IF;
1830 
1831     -- Process chain
1832     process_chain
1833      (p_new_psn_tab    => l_hier_psn_tab,
1834       p_new_asg_tab    => l_hier_asg_tab,
1835       p_new_bgr_tab    => l_hier_bgr_tab,
1836       p_new_sup_tab    => l_hier_sup_tab,
1837       p_new_ast_tab    => l_hier_ast_tab,
1838       p_new_end_tab    => l_hier_end_tab,
1839       p_new_aty_tab    => l_hier_aty_tab,
1840       p_orphan_flag    => l_orphan_flag,
1841       p_chain_table    => l_chain_table,
1842       p_loop_date      => l_loop_date,
1843       p_next_loop_date => l_next_loop_date);
1844 
1845     -- Set new loop date
1846     l_loop_date := l_next_loop_date;
1847 
1848     -- Loop through dates to tree walk supervisor hierarchy
1849     -- for the given person and period of work
1850     -- Exit loop when the next date to sample goes beyond the period of
1851     -- work or reaches end of time
1852     WHILE (l_loop_date <= p_end_date AND
1853            l_loop_date < g_end_of_time AND
1854            l_exit_loop = 'N') LOOP
1855 
1856       -- Reset local PL/SQL tables with latest manager chain
1857       get_manager_chain
1858        (p_person_id        => p_person_id,
1859         p_effective_date   => l_loop_date,
1860         p_hier_psn_tab     => l_hier_psn_tab,
1861         p_hier_asg_tab     => l_hier_asg_tab,
1862         p_hier_bgr_tab     => l_hier_bgr_tab,
1863         p_hier_sup_tab     => l_hier_sup_tab,
1864         p_hier_ast_tab     => l_hier_ast_tab,
1865         p_hier_end_tab     => l_hier_end_tab,
1866         p_hier_aty_tab     => l_hier_aty_tab,
1867         p_next_change_date => l_next_loop_date);
1868 
1869       -- If no data is returned then there is some data problem since
1870       -- the loop date is within an active period of service
1871       -- Print the issue to the log and exit the loop
1872       IF (NOT l_hier_psn_tab.EXISTS(1)) THEN
1873 
1874         output('No chain found for person ' || to_char(p_person_id) ||
1875               ' on ' || to_char(l_loop_date, 'DD-MON-YYYY'));
1876 
1877         -- Set flag to exit loop
1878         l_exit_loop := 'Y';
1879 
1880       ELSE
1881 
1882         -- Test for orphan chain
1883         -- Chain is an orphan if the top manager, ordered last, has a
1884         -- not-null supervisor id
1885         IF (l_hier_sup_tab(l_hier_psn_tab.LAST) = -1) THEN
1886           l_orphan_flag := 'N';
1887         ELSE
1888           l_orphan_flag := 'Y';
1889         END IF;
1890 
1891         -- Process chain
1892         process_chain
1893          (p_new_psn_tab    => l_hier_psn_tab,
1894           p_new_asg_tab    => l_hier_asg_tab,
1895           p_new_bgr_tab    => l_hier_bgr_tab,
1896           p_new_sup_tab    => l_hier_sup_tab,
1897           p_new_ast_tab    => l_hier_ast_tab,
1898           p_new_end_tab    => l_hier_end_tab,
1899           p_new_aty_tab    => l_hier_aty_tab,
1900           p_orphan_flag    => l_orphan_flag,
1901           p_chain_table    => l_chain_table,
1902           p_loop_date      => l_loop_date,
1903           p_next_loop_date => l_next_loop_date);
1904 
1905         -- Set new loop date
1906         l_loop_date := l_next_loop_date;
1907 
1908       END IF;
1909 
1910     END LOOP;
1911 
1912   END IF;
1913 
1914 END process_period_of_work;
1915 
1916 -- ----------------------------------------------------------------------------
1917 -- Calls process_period_of_work with the start and end dates for each active
1918 -- period of service in the collection range
1919 -- ----------------------------------------------------------------------------
1920 PROCEDURE process_person(p_person_id          IN NUMBER,
1921                          p_refresh_from_date  IN DATE) IS
1922 
1923   -- Gets all periods of work for a person
1924   CURSOR period_of_work_csr IS
1925   SELECT
1926    GREATEST(pos.date_start,
1927             p_refresh_from_date)  start_date
1928   ,LEAST(NVL(pos.actual_termination_date, g_end_of_time),
1929          g_end_of_time)  end_date
1930   FROM
1931    per_periods_of_service pos
1932   WHERE pos.person_id = p_person_id
1933   AND (p_refresh_from_date BETWEEN pos.date_start
1934                            AND NVL(pos.actual_termination_date, g_end_of_time)
1935     OR pos.date_start > p_refresh_from_date)
1936   UNION ALL
1937   SELECT
1938    GREATEST(pop.date_start,
1939             p_refresh_from_date)  start_date
1940   ,LEAST(NVL(pop.actual_termination_date, g_end_of_time),
1941          g_end_of_time)  end_date
1942   FROM
1943    per_periods_of_placement  pop
1944   WHERE pop.person_id = p_person_id
1945   AND (p_refresh_from_date BETWEEN pop.date_start
1946                            AND NVL(pop.actual_termination_date, g_end_of_time)
1947     OR pop.date_start > p_refresh_from_date);
1948 
1949 BEGIN
1950 
1951   -- Loop through periods of work
1952   FOR pow_rec IN period_of_work_csr LOOP
1953 
1954     -- Process the period of work
1955     process_period_of_work
1956      (p_person_id  => p_person_id
1957      ,p_start_date => pow_rec.start_date
1958      ,p_end_date   => pow_rec.end_date);
1959 
1960   END LOOP;
1961 
1962 END process_person;
1963 
1964 -- ----------------------------------------------------------------------------
1965 -- Main process entry point
1966 -- ----------------------------------------------------------------------------
1967 PROCEDURE process_range(errbuf             OUT NOCOPY VARCHAR2,
1968                         retcode            OUT NOCOPY NUMBER,
1969                         p_mthd_action_id   IN NUMBER,
1970                         p_mthd_range_id    IN NUMBER,
1971                         p_start_object_id  IN NUMBER,
1972                         p_end_object_id    IN NUMBER) IS
1973 
1974   CURSOR person_csr_full IS
1975   SELECT DISTINCT
1976    ase.person_id
1977   FROM
1978    hri_cs_asgn_suph_events_ct ase
1979   WHERE ase.person_id BETWEEN p_start_object_id and p_end_object_id
1980   AND ase.effective_end_date >= g_refresh_start_date;
1981 
1982   CURSOR person_csr_incr IS
1983   SELECT DISTINCT
1984    eq.person_id
1985   ,eq.erlst_evnt_effective_date  change_date
1986   FROM
1987    hri_eq_sprvsr_hrchy_chgs eq
1988   WHERE eq.person_id BETWEEN p_start_object_id and p_end_object_id;
1989 
1990 BEGIN
1991 
1992   -- Initialization
1993   g_suph_row_count := 0;
1994   g_chn_row_count  := 0;
1995   g_trn_row_count  := 0;
1996 
1997   -- Set parameter globals
1998   set_parameters(p_mthd_action_id);
1999 
2000   -- Full refresh
2001   IF (g_full_refresh = 'Y') THEN
2002 
2003     -- Loop through all employees in range
2004     FOR person_rec IN person_csr_full LOOP
2005 
2006       -- Process each person from refresh start date
2007       process_person(p_person_id => person_rec.person_id,
2008                      p_refresh_from_date => g_refresh_start_date);
2009     END LOOP;
2010 
2011   -- Incremental refresh
2012   ELSE
2013 
2014     -- Loop through all employees in range
2015     FOR person_rec IN person_csr_incr LOOP
2016 
2017       -- Process each person from their earliest change date
2018       process_person(p_person_id => person_rec.person_id,
2019                      p_refresh_from_date => person_rec.change_date);
2020     END LOOP;
2021 
2022     -- Delete and end date supervisor hierarchy rows
2023     delete_and_end_date_suph_recs
2024      (p_start_person_id => p_start_object_id,
2025       p_end_person_id   => p_end_object_id);
2026 
2027     -- Remove transfer records for range
2028     hri_opl_wrkfc_trnsfr_events.delete_transfers_mgrh
2029      (p_start_object_id => p_start_object_id,
2030       p_end_object_id   => p_end_object_id);
2031 
2032   END IF;
2033 
2034   -- Insert stored rows
2035   IF g_suph_row_count > 0 THEN
2036     bulk_insert_rows;
2037   END IF;
2038 
2039   -- Flush log messages
2040   hri_bpl_conc_log.flush_process_info('HRI_CS_SUPH');
2041 
2042 END process_range;
2043 
2044 -- ----------------------------------------------------------------------------
2045 -- Pre process entry point
2046 -- ----------------------------------------------------------------------------
2047 PROCEDURE pre_process(p_mthd_action_id  IN NUMBER,
2048                       p_sqlstr          OUT NOCOPY VARCHAR2) IS
2049 
2050   l_sql_stmt      VARCHAR2(2000);
2051   l_dummy1        VARCHAR2(2000);
2052   l_dummy2        VARCHAR2(2000);
2053   l_schema        VARCHAR2(400);
2054 
2055 BEGIN
2056 
2057   -- Set parameter globals
2058   set_parameters( p_mthd_action_id => p_mthd_action_id );
2059 
2060   -- Get HRI schema name - get_app_info populates l_schema
2061   IF fnd_installation.get_app_info('HRI',l_dummy1, l_dummy2, l_schema) THEN
2062     null;
2063   END IF;
2064 
2065   -- Disable WHO trigger
2066   run_sql_stmt_noerr('ALTER TRIGGER HRI_CS_SUPH_WHO DISABLE');
2067   run_sql_stmt_noerr('ALTER TRIGGER HRI_CS_MNGRSC_CT_WHO DISABLE');
2068   run_sql_stmt_noerr('ALTER TRIGGER HRI_MDP_MGRH_TRANSFERS_CT_WHO DISABLE');
2069 
2070   -- ********************
2071   -- Full Refresh Section
2072   -- ********************
2073   IF (g_full_refresh = 'Y') THEN
2074 
2075     -- Drop all the indexes on the table (except the unique index)
2076     hri_utl_ddl.log_and_drop_indexes
2077      (p_application_short_name => 'HRI',
2078       p_table_name             => 'HRI_CS_SUPH',
2079       p_table_owner            => l_schema,
2080       p_index_excptn_lst       => 'HRI_CS_SUPH_U1');
2081     hri_utl_ddl.log_and_drop_indexes
2082      (p_application_short_name => 'HRI',
2083       p_table_name             => 'HRI_CS_MNGRSC_CT',
2084       p_table_owner            => l_schema,
2085       p_index_excptn_lst       => 'HRI_CS_MNGRSC_CT_U1');
2086     hri_utl_ddl.log_and_drop_indexes
2087      (p_application_short_name => 'HRI',
2088       p_table_name             => 'HRI_MDP_MGRH_TRANSFERS_CT',
2089       p_table_owner            => l_schema,
2090       p_index_excptn_lst       => 'HRI_MDP_MGRH_TRANSFERS_CT_U1');
2091 
2092     -- Empty out supervisor hierarchy history table
2093     l_sql_stmt := 'TRUNCATE TABLE ' || l_schema || '.HRI_CS_SUPH';
2094     EXECUTE IMMEDIATE(l_sql_stmt);
2095     l_sql_stmt := 'TRUNCATE TABLE ' || l_schema || '.HRI_CS_MNGRSC_CT';
2096     EXECUTE IMMEDIATE(l_sql_stmt);
2097     l_sql_stmt := 'TRUNCATE TABLE ' || l_schema || '.HRI_MDP_MGRH_TRANSFERS_CT';
2098     EXECUTE IMMEDIATE(l_sql_stmt);
2099 
2100     -- Insert chain lookup
2101     g_chn_row_count := 0;
2102     insert_chn_row
2103      (p_person_id     => -1
2104      ,p_assignment_id => -1
2105      ,p_start_date    => hr_general.start_of_time
2106      ,p_end_date      => g_end_of_time
2107      ,p_chain_id      => -1
2108      ,p_person_level  => to_number(null));
2109     bulk_insert_rows;
2110 
2111     -- Set the SQL statement for the entire range
2112     p_sqlstr :=
2113       'SELECT DISTINCT person_id  object_id
2114        FROM hri_cs_asgn_suph_events_ct
2115        ORDER BY person_id';
2116 
2117   -- ***************************
2118   -- Incremental refresh section
2119   -- ***************************
2120   ELSE
2121 
2122     -- STEP A - Populate the person_id column in events queue
2123     --          and remove events that are not required
2124     update_event_queue;
2125 
2126     -- STEP B - Find Subordinates
2127     find_subordinates;
2128 
2129     -- STEP C - Remove Duplicates
2130     remove_duplicates;
2131 
2132     -- STEP D - Delete Records After last Change
2133     -- STEP E - End Date Prior Records
2134     --   These steps are done by process_range
2135     --   for each chunk to ensure consistency of
2136     --   table during incremental load
2137 
2138     -- 4259598 Incremental Changes
2139     -- Populate the assignment event delta queue in order to incrementally refresh
2140     -- the assignment delta table
2141     populate_asg_delta_eq;
2142     -- Populate workforce events fact event queue
2143     populate_wrkfc_evt_eq;
2144     -- Populate workforce events by manager event queue
2145     populate_wrkfc_evt_mgrh_eq;
2146 
2147     -- Set the SQL statement for the entire range
2148     p_sqlstr :=
2149       'SELECT person_id  object_id
2150        FROM hri_eq_sprvsr_hrchy_chgs
2151        ORDER BY person_id';
2152 
2153   END IF;
2154 
2155 END pre_process;
2156 
2157 -- ----------------------------------------------------------------------------
2158 -- Post process entry point
2159 -- ----------------------------------------------------------------------------
2160 PROCEDURE post_process(p_mthd_action_id NUMBER) IS
2161 
2162   l_dummy1        VARCHAR2(2000);
2163   l_dummy2        VARCHAR2(2000);
2164   l_schema        VARCHAR2(400);
2165 
2166 BEGIN
2167 
2168   -- Check parameters are set
2169   set_parameters(p_mthd_action_id);
2170 
2171   -- Get HRI schema
2172   IF fnd_installation.get_app_info('HRI',l_dummy1, l_dummy2, l_schema) THEN
2173     null;
2174   END IF;
2175 
2176   -- Enable WHO trigger
2177   run_sql_stmt_noerr('ALTER TRIGGER HRI_CS_SUPH_WHO ENABLE');
2178   run_sql_stmt_noerr('ALTER TRIGGER HRI_CS_MNGRSC_CT_WHO ENABLE');
2179   run_sql_stmt_noerr('ALTER TRIGGER HRI_MDP_MGRH_TRANSFERS_CT_WHO ENABLE');
2180 
2181   -- Recreate indexes if they were dropped (full refresh)
2182   IF (g_full_refresh = 'Y') THEN
2183     hri_utl_ddl.recreate_indexes
2184      (p_application_short_name => 'HRI',
2185       p_table_name             => 'HRI_CS_SUPH',
2186       p_table_owner            => l_schema);
2187     hri_utl_ddl.recreate_indexes
2188      (p_application_short_name => 'HRI',
2189       p_table_name             => 'HRI_CS_MNGRSC_CT',
2190       p_table_owner            => l_schema);
2191     hri_utl_ddl.recreate_indexes
2192      (p_application_short_name => 'HRI',
2193       p_table_name             => 'HRI_MDP_MGRH_TRANSFERS_CT',
2194       p_table_owner            => l_schema);
2195   END IF;
2196 
2197   -- As the supervisor hierarchy has been rebuilt, purge the events queue
2198   hri_opl_event_capture.purge_queue('HRI_EQ_SPRVSR_HRCHY_CHGS');
2199 
2200   IF (p_mthd_action_id > -1) THEN
2201 
2202     -- Log process end
2203     hri_bpl_conc_log.record_process_start('HRI_CS_SUPH');
2204     hri_bpl_conc_log.log_process_end(
2205        p_status         => TRUE
2206       ,p_period_from    => TRUNC(g_refresh_start_date)
2207       ,p_period_to      => TRUNC(SYSDATE)
2208       ,p_attribute1     => g_full_refresh);
2209 
2210   END IF;
2211 
2212 END post_process;
2213 
2214 -- --------------------------------------------
2215 -- API to run single thread incremental refresh
2216 -- --------------------------------------------
2217 PROCEDURE incremental_refresh_single IS
2218 
2219   l_dummy   VARCHAR2(32000);
2220 
2221   CURSOR sup_event_queue_csr IS
2222   SELECT
2223    person_id
2224   ,erlst_evnt_effective_date  start_date
2225   FROM
2226    hri_eq_sprvsr_hrchy_chgs;
2227 
2228 BEGIN
2229 
2230   -- Set globals
2231   g_debug := TRUE;
2232   g_full_refresh := 'N';
2233   g_refresh_start_date := trunc(sysdate);
2234 
2235   -- Pre process
2236   pre_process(-1, l_dummy);
2237 
2238   -- Loop through supervisors in event queue
2239   FOR sup_rec IN sup_event_queue_csr LOOP
2240     process_person(sup_rec.person_id, sup_rec.start_date);
2241   END LOOP;
2242 
2243   -- Post process
2244   post_process(-1);
2245 
2246 END incremental_refresh_single;
2247 
2248 -- --------------------------------------------
2249 -- API to run single thread full refresh
2250 -- --------------------------------------------
2251 PROCEDURE full_refresh_single IS
2252 
2253   CURSOR psn_csr IS
2254   SELECT DISTINCT person_id
2255   FROM hri_cs_asgn_suph_events_ct
2256   WHERE primary_flag = 'Y';
2257 
2258   l_dummy   VARCHAR2(32000);
2259 
2260 BEGIN
2261 
2262   -- Set globals
2263   g_debug := FALSE;
2264   g_full_refresh := 'Y';
2265   g_refresh_start_date := g_dbi_collection_start_date;
2266 
2267   -- Pre process
2268   pre_process(-1, l_dummy);
2269 
2270   -- Set number of rows to 0
2271   g_suph_row_count := 0;
2272   g_chn_row_count  := 0;
2273   g_trn_row_count  := 0;
2274 
2275   -- Loop through all employees
2276   FOR psn_rec IN psn_csr LOOP
2277 
2278     process_person(psn_rec.person_id, g_dbi_collection_start_date);
2279 
2280     -- Insert stored rows
2281     IF g_suph_row_count > 2000 THEN
2282       bulk_insert_rows;
2283     END IF;
2284 
2285   END LOOP;
2286 
2287   -- Insert stored rows
2288   IF g_suph_row_count > 0 THEN
2289     bulk_insert_rows;
2290   END IF;
2291 
2292   -- Post process
2293   post_process(-1);
2294 
2295 END full_refresh_single;
2296 
2297 -- ---------------------------------------------
2298 -- API to run a full refresh for a single person
2299 -- ---------------------------------------------
2300 PROCEDURE run_for_person(p_person_id  IN NUMBER) IS
2301 
2302 BEGIN
2303 
2304   debug('Start');
2305 
2306   DELETE FROM hri_cs_suph
2307   WHERE sub_person_id = p_person_id;
2308 
2309   g_suph_row_count := 0;
2310   g_chn_row_count  := 0;
2311 
2312   process_person(p_person_id, g_dbi_collection_start_date);
2313 
2314   -- Insert stored rows
2315   IF g_suph_row_count > 0 THEN
2316     bulk_insert_rows;
2317   END IF;
2318 
2319   debug('End');
2320 
2321 END run_for_person;
2322 
2323 END hri_opl_suph_hst;