DBA Data[Home] [Help]

PACKAGE BODY: APPS.PAY_EVENTS_WRAPPER

Source


1 PACKAGE BODY pay_events_wrapper AS
2 /* $Header: pyevtwrp.pkb 120.1.12020000.3 2012/12/13 10:32:59 nvankadh ship $ */
3 /*
4     +======================================================================+
5     |               Copyright (c) 2002 Oracle Corporation UK               |
6     |                    Thames Valley Park, Reading, UK                   |
7     |                        All rights reserved.                          |
8     +======================================================================+
9     File Name   : pyevtwrp.pkh
10 
11     Description : A wrapper on top of the Payroll Events Model interpreter
12                   for use with the summarisation collection programs which
13                   are used to increase the performance of the Daily Business
14                   Intelligence portlet queries.
15 
16     Change History
17     --------------
18     Name           Date        Version Bug     Text
19     -------------- ----------- ------- ------- ------------------------------------
20     Ed Jones       31-May-2002 115.0           Initial (Stub) version
21     Ed Jones       31-May-2002 115.1           Added dbdrv lines
22                                                Removed dbms_output
23     Ed Jones       31-May-2002 115.2           Corrected invalid dbdrv lines
24     Ed Jones       14-Jun-2002 115.3           Replaced stubs with proper
25                                                code, moved wrapper specific
26                                                detailed output fields from
27                                                interpreter to an additional
28                                                cache table here (dated table
29                                                extras global variable)
30     Ed Jones       02-Jul-2002 115.4           Added refresh period start
31                                                and end dates to the
32                                                get_refresh_periods routine
33                                                and pass these to the interpreter
34                                                rather than the effective assignment
35                                                dates
36     Ed Jones       02-Jul-2002 115.5           Removed owner from join to
37                                                all_tab_columns
38     Ed Jones       23-Jul-2002 115.6           Add debugging mode and allow
39                                                messages to be sent to conc.
40                                                manager output file
41     Ed Jones       23-Jul-2002 115.7           Added more debug information
42                                                Send conc. messages to log
43     Ed Jones       23-Jul-2002 115.8           Add supervisor ID column changes
44                                                separately to normal payroll event
45                                                model updates, this is to detect
46                                                and record old and new supervisors
47                                                in the list of refresh records
48     Ed Jones       27-Mar-2003 115.9   2870801 Changes to support date track
49                                                updates to supervisor as well as
50                                                corrections.
51     Ed Jones       10-Apr-2003 115.10          Changes to pick up correct start date
52                                                for updated supervisor correctly
53     Ed Jones       02-Jun-2003 115.11  2984406 Moved pay_interpreter_pkg.event_group_tables
54                                                call and reset of g_DATED_TABLE_EXTRAS
55 					       cache to a separate procedure (from
56 					       get_event_details) so that it's only
57 					       called once per run.
58 					       Don't pass around event group name
59 					       parameter, use the global ID populated in
60 					       the one-off init_event_group_cache
61 					       procedure.
62 					       Major changes to the way in which affected
63 					       assignments are detected, see specific
64 					       sections for details (search for this bug).
65     Ed Jones       07-Jul-2003 115.13          Moved various cursors to be visible at package
66                                                level for ease of access by diagnostics
67 					       routines.
68 					       Made dt update SQL building function accessible
69 					       for this reason too.
70 					       Change csr_inserts_deletes cursor to decode
71 					       various event types to match update types.
72 					       Corrections cursor looks for C type updates
73 					       as well as U (database updates may be stored
74 					       in the incident register as corrections)
75 				       3033981 Changed incident register accessing cursors
76 				               to get surrogate key and pass that on to
77 					       the event interpreter, if the table in use
78 					       is element entries.
79     Ed Jones       07-Jul-2003 115.14          Remove 'show errors' for gscc
80     Andy Logue     23-DEC-2003 115.15  3329824 Performance fix
81     Andy Logue     05-JAN-2004 115.16          Performance fix
82     N Bristow      26-JAN-2004 115.17          get_assignments_affected changed to drive
83                                                off pay_process_events and to only use
84                                                salary entries.
85     N Bristow      10-MAR-2004 115.18          Performance changes, the PL/SQL
86                                                tables were being over
87                                                referenced. Change these tables
88                                                to use a hash cache.
89     Andy Logue     13-FEB-2006 115.19          Schema clone for all_tab_columns.
90     nvankadh       28-NOV-2012 115.20 15882261 Modified few cursor definitions to refer to
91                                                dba_tab_columns instead of all_tab_columns
92     nvankadh	   13-DEC-2012 115.21 15983018 Modified due to regression because of
93 	                                           the changes made for 15882261
94     ===============================================================================
95 */
96     --
97     -- < PRIVATE TYPES > -----------------------------------------------------
98     TYPE t_dated_table_extras_rec IS RECORD(
99         has_supervisor_id         VARCHAR2(1),
100         has_location_id           VARCHAR2(1),
101         has_assignment_id         VARCHAR2(1),
102         sql_statement             VARCHAR2(32767)
103     );
104     TYPE t_dated_table_extras_tab IS
105         TABLE OF t_dated_table_extras_rec
106         INDEX BY BINARY_INTEGER;
107 --
108     type t_indexing_rec is record(
109          start_ptr number
110     );
111 --
112     type t_indexing_tab is table of t_indexing_rec index by BINARY_INTEGER;
113 --
114     type t_location_chn_rec is record
115     (
116         supervisor_id number,
117         location_id   number,
118         summary_ptr   number,
119         next_ptr      number
120     );
121 --
122     type t_location_chn_tab is table of t_location_chn_rec
123      index by BINARY_INTEGER;
124 --
125     g_supervisor_hash_tab t_indexing_tab;
126     g_location_chn_tab    t_location_chn_tab;
127 
128 --
129     --
130     -- < PRIVATE CONSTANTS > -------------------------------------------------
131     --
132     -- The event model processing mode and other animals
133     c_PROCESS_MODE          CONSTANT VARCHAR2(30)   := 'ASG_CREATION';
134     c_ASSIGNMENTS_TABLE     CONSTANT VARCHAR2(30)   := 'per_all_assignments_f';
135     c_OUTPUT_BUFFER         CONSTANT NUMBER         := 2000000;
136     c_OUTPUT_LINE_LENGTH    CONSTANT NUMBER         := 255;
137     --
138     -- < PRIVATE GLOBALS > ---------------------------------------------------
139     --
140     g_debugging             BOOLEAN := FALSE;
141     g_concurrent            BOOLEAN := FALSE;
142     --
143     -- How long did the last run take
144     g_SECONDS_ELAPSED       NUMBER  := 0;
145     --
146     -- Cached information about an event group
147     g_DATED_TABLE_EXTRAS    t_dated_table_extras_tab;
148     g_EVENT_GROUP_ID        NUMBER := NULL;
149     --
150     -- Globals for record looping
151     g_FIRST_RECORD 	    NUMBER := 0;
152     g_LAST_RECORD 	    NUMBER := 0;
153     g_CURRENT_RECORD 	    NUMBER := 0;
154     --
155     -- < CURSORS > -----------------------------------------------------------
156     --
157 --
158     CURSOR csr_all_changes(p_st IN DATE,p_en IN DATE) IS
159         SELECT
160                     ppe.assignment_id,
161                     ppe.surrogate_key,
162                     peu.dated_table_id,
163                     MIN(ppe.effective_date)   effective_start_date,
164                     MAX(ppe.effective_date)   effective_end_date
165         FROM        pay_process_events      ppe,
166                     pay_event_updates       peu
167         WHERE       ppe.creation_date BETWEEN p_st AND p_en
168         AND         ppe.event_update_id = peu.event_update_id
169         GROUP BY    ppe.assignment_id,ppe.surrogate_key, peu.dated_table_id
170         ORDER BY    ppe.assignment_id, ppe.surrogate_key;
171 --
172     -- Get the inserts into and deletes from the tables we care about
173     CURSOR csr_inserts_deletes(p_evt IN NUMBER,p_st IN DATE,p_en IN DATE) RETURN csr_return IS
174         SELECT
175 	            ppe.assignment_id,
176 	            peu.dated_table_id,
177 	            ppe.surrogate_key,
178                     MIN(ppe.effective_date)   start_date,
179                     MAX(ppe.effective_date)   end_date
180         FROM        pay_process_events      ppe,
181                     pay_event_updates       peu
182         WHERE       ppe.creation_date BETWEEN p_st AND p_en
183         AND         ppe.event_update_id = peu.event_update_id
184         AND         substr(peu.event_type,1,1) in ('D','I','Z')
185         AND EXISTS (
186                     SELECT  'X'
187                     FROM    pay_datetracked_events pde
188                     WHERE   pde.event_group_id = p_evt
189                     AND     pde.dated_table_id = peu.dated_table_id
190                     AND     pde.update_type = SUBSTR(DECODE(peu.event_type,'ZAP','D',peu.event_type),1,1)
191         )
192         GROUP BY    ppe.assignment_id,peu.dated_table_id,ppe.surrogate_key;
193     --
194     -- Get the updates (date-track corrections) to columns we care about, excluding supervisor ID
195     CURSOR csr_dt_corrections(p_evt IN NUMBER,p_st IN DATE,p_en IN DATE) RETURN csr_return IS
196         SELECT
197 	            ppe.assignment_id,
198 	            peu.dated_table_id,
199                     ppe.surrogate_key,
200                     MIN(ppe.effective_date)   start_date,
201                     MAX(ppe.effective_date)   end_date
202         FROM        pay_process_events      ppe,
203                     pay_event_updates       peu
204         WHERE       ppe.creation_date BETWEEN p_st AND p_en
205         AND         ppe.event_update_id = peu.event_update_id
206         AND         substr(peu.event_type,1,1) IN ('U','C')
207         AND EXISTS (
208                     SELECT  'X'
209                     FROM    pay_datetracked_events  pde,
210                             pay_dated_tables        pdt
211       	            WHERE   pde.event_group_id = p_evt
212       	            AND     pde.dated_table_id = peu.dated_table_id
213                     AND     pdt.dated_table_id = pde.dated_table_id
214 	            AND     pde.column_name = peu.column_name
215                     AND     NOT (pdt.table_name = 'PER_ALL_ASSIGNMENTS_F' AND pde.column_name = 'SUPERVISOR_ID')
216 	            AND     pde.update_type = 'C'
217                    )
218         GROUP BY    ppe.assignment_id,peu.dated_table_id,ppe.surrogate_key;
219         --
220         -- Decode the description column of pay_process_events to obtain the
221         -- before and after values, just for the supervisor ID column on
222         -- per_all_assignments_f, and only if that column is one of the ones
223         -- we're tracking via our event group. Group by supervisor ID and
224         -- optionally location ID and return the earliest and latest effective
225         -- dates that were affected by the change
226 	-- 2984406: Changes for performance
227         CURSOR csr_supv_corrections(
228             cp_evt      IN NUMBER,
229             cp_st       IN DATE,
230             cp_en       IN DATE,
231             cp_str      IN VARCHAR2
232         ) RETURN csr_return IS
233             SELECT  TO_NUMBER(DECODE(sic.column_name,'SUPERVISOR_ID',sic.id,NULL))  supervisor_id,
234                     DECODE(cp_str,'Y',paaf.location_id,c_BLANK_LOCATION_ID)         location_id,
235 		    NULL                                                            dummy,
236                     MIN(sic.effective_date)                                         effective_start_date,
237                     MAX(sic.effective_date)                                         effective_end_date
238             FROM    (
239                         -- Get the 'before' information, i.e. the ID before the '->' character sequence
240                         SELECT  /*+ ordered index(ppe pay_process_events_n3) */
241 	                        DECODE(SUBSTR(ppe.description,1,INSTR(ppe.description,' -> ')-1),'<null>',NULL,SUBSTR(ppe.description,1,INSTR(ppe.description,' -> ')-1)) id,
242                                 ppe.effective_date,
243                                 ppe.assignment_id,
244                                 peu.dated_table_id,
245                                 peu.column_name
246                         FROM    pay_process_events  ppe,
247                                 pay_event_updates   peu,
248                                 pay_dated_tables    pdt,
252                         AND     peu.event_update_id = ppe.event_update_id
249 				pay_datetracked_events pde
250                         WHERE   INSTR(ppe.description,' -> ') > 0
251                         AND     SUBSTR(ppe.description,1,6) <> '<null>'
253                         AND     peu.dated_table_id = pdt.dated_table_id
254                         AND     pdt.table_name = 'PER_ALL_ASSIGNMENTS_F'
255                         AND     peu.column_name = 'SUPERVISOR_ID'
256 			AND     pde.update_type = 'C'
257 			AND     pde.column_name = peu.column_name
258 			AND     ppe.creation_date BETWEEN cp_st AND cp_en
259 			AND     cp_evt = pde.event_group_id
260 			AND     pde.dated_table_id = peu.dated_table_id
261                         UNION
262                         -- Add the 'after' information, i.e. the ID after the '->' character sequence, don't UNION ALL 'cos that would give us duplicates
263                         SELECT  /*+ ordered index(ppe pay_process_events_n3) */
264 	                        DECODE(SUBSTR(ppe.description,INSTR(ppe.description,' -> ')+4),'<null>',NULL,SUBSTR(ppe.description,INSTR(ppe.description,' -> ')+4)) id,
265                                 ppe.effective_date,
266                                 ppe.assignment_id,
267                                 peu.dated_table_id,
268                                 peu.column_name
269                         FROM    pay_process_events  ppe,
270                                 pay_event_updates   peu,
271                                 pay_dated_tables    pdt,
272 				pay_datetracked_events pde
273                         WHERE   INSTR(ppe.description,' -> ') > 0
274                         AND     SUBSTR(ppe.description,length(ppe.description)-5) <> '<null>'
275                         AND     peu.event_update_id = ppe.event_update_id
276                         AND     peu.dated_table_id = pdt.dated_table_id
277                         AND     pdt.table_name = 'PER_ALL_ASSIGNMENTS_F'
278                         AND     peu.column_name = 'SUPERVISOR_ID'
279 			AND     pde.update_type = 'C'
280 			AND     pde.column_name = peu.column_name
281 			AND     ppe.creation_date BETWEEN cp_st AND cp_en
282 	                AND     cp_evt = pde.event_group_id
283 			AND     pde.dated_table_id = pdt.dated_table_id
284                     )                       sic,
285                     per_all_assignments_f   paaf
286             -- Join to the assignment at the effective date of the change to get the location
287             WHERE   sic.effective_date BETWEEN paaf.effective_start_date AND paaf.effective_end_date
288             AND     paaf.assignment_id      = sic.assignment_id
289             GROUP BY
290                     TO_NUMBER(DECODE(sic.column_name,'SUPERVISOR_ID',sic.id,NULL)),
291                     DECODE(cp_str,'Y',paaf.location_id,c_BLANK_LOCATION_ID);
292 		            --
293 	-- Get a list of the tables that are in our event group
294         CURSOR csr_table_list(p_evt IN NUMBER) RETURN csr_return IS
295             SELECT DISTINCT pdt.dated_table_id,NULL,pdt.table_name,NULL,NULL
296             FROM   pay_dated_tables pdt,
297                    pay_datetracked_events pde
298             WHERE  pde.dated_table_id = pdt.dated_table_id
299             AND    pde.event_group_id = p_evt
300             AND    pde.update_type = 'U';
301 --
302 procedure get_summary_idx(p_super_id        in            number,
303                           p_location_id     in            number,
304                           p_idx                out nocopy number,
305                           p_summary_refresh in out nocopy t_summary_refresh_tab_type
306                          )
307 is
308 hash_key number;
309 loc_idx  number;
310 sum_idx  number;
311 prev_idx number;
312 l_found  boolean;
313 begin
314 --
315    hash_key := (p_super_id mod 1009 ) + 1;
316 --
317    begin
318 --
319        if (g_supervisor_hash_tab.exists(hash_key)) then
320 --
321           loc_idx := g_supervisor_hash_tab(hash_key).start_ptr;
322 --
323           l_found := FALSE;
324           while (l_found <> TRUE and loc_idx is not null) loop
325              if (   g_location_chn_tab(loc_idx).supervisor_id = p_super_id
326                 and g_location_chn_tab(loc_idx).location_id = p_location_id)
327              then
328                  l_found := TRUE;
329              else
330                  prev_idx := loc_idx;
331                  loc_idx := g_location_chn_tab(loc_idx).next_ptr;
332              end if;
333           end loop;
334 --
335           /* OK if we didn't find one the create one */
336           if (l_found = FALSE) then
337             loc_idx := g_location_chn_tab.count + 1;
338             g_location_chn_tab(loc_idx).supervisor_id := p_super_id;
339             g_location_chn_tab(loc_idx).location_id   := p_location_id;
340             g_location_chn_tab(loc_idx).summary_ptr   := null;
341             g_location_chn_tab(loc_idx).next_ptr      := null;
342 --
343             -- Set the previous pointer.
344             g_location_chn_tab(prev_idx).next_ptr     := loc_idx;
345           end if;
346 --
347        else
348 --
349           loc_idx := g_location_chn_tab.count + 1;
350           g_location_chn_tab(loc_idx).supervisor_id := p_super_id;
351           g_location_chn_tab(loc_idx).location_id   := p_location_id;
352           g_location_chn_tab(loc_idx).summary_ptr   := null;
353           g_location_chn_tab(loc_idx).next_ptr      := null;
354 --
355           g_supervisor_hash_tab(hash_key).start_ptr := loc_idx;
356        end if;
357 --
358    end;
359 --
360    /* OK we should now have a row for the location table.
361       Need to see if we have a row in the summary table
362    */
363 --
364    if (g_location_chn_tab(loc_idx).summary_ptr is null) then
365 --
366      sum_idx := p_summary_refresh.count + 1;
367 --
368      p_summary_refresh(sum_idx).supervisor_id := p_super_id;
369      p_summary_refresh(sum_idx).location_id   := p_location_id;
370 --
371      g_location_chn_tab(loc_idx).summary_ptr := sum_idx;
372 --
373    end if;
374 --
375    p_idx := g_location_chn_tab(loc_idx).summary_ptr;
376 --
377 end get_summary_idx;
378     --
379     -- < PRIVATE FUNCTIONS > -------------------------------------------------
380     --
381     -- Increment a data
382     FUNCTION inc_date(p_date IN DATE) RETURN DATE IS
383     BEGIN
384         IF p_date < hr_general.end_of_time THEN
385             RETURN p_date + 1;
386         ELSE
387             RETURN p_date;
388         END IF;
389     END inc_date;
390     --
391     -- Decrement a date
392     FUNCTION dec_date(p_date IN DATE) RETURN DATE IS
393     BEGIN
394         IF p_date > hr_general.start_of_time THEN
395             RETURN p_date - 1;
396         ELSE
397             RETURN p_date;
398         END IF;
399     END dec_date;
400     --
401     -- Get the business group of an assignment
402     FUNCTION get_business_group_id(p_assignment_id IN NUMBER) RETURN NUMBER IS
403         --
404         CURSOR csr_bg(cp_ass_id IN NUMBER) IS
405             SELECT  business_group_id
406             FROM    per_assignments_f
407             WHERE   assignment_id = cp_ass_id;
408         --
409         l_business_group_id     NUMBER;
410         --
411     BEGIN
412         OPEN csr_bg(p_assignment_id);
413         FETCH csr_bg INTO l_business_group_id;
414         CLOSE csr_bg;
415         --
416         RETURN l_business_group_id;
417     END get_business_group_id;
418     --
419     -- Find the information we need from the dated table cache
420     FUNCTION query_dated_table_cache(p_dated_table_id IN NUMBER) RETURN NUMBER IS
421         --
422         l_tab_idx NUMBER;
423         --
424     BEGIN
425         --
426         -- Try to find the table the event occurred on in the cache
427         l_tab_idx := -1;
428         --
429         IF NVL(pay_interpreter_pkg.t_distinct_tab.FIRST,0) > 0 THEN
430             FOR k IN pay_interpreter_pkg.t_distinct_tab.FIRST .. pay_interpreter_pkg.t_distinct_tab.LAST LOOP
431                 IF pay_interpreter_pkg.t_distinct_tab(k).table_id = p_dated_table_id THEN
432                     msg('Found dated table '||p_dated_table_id||' at index '||k);
433                     l_tab_idx := k;
434                     EXIT;
435                 END IF;
436             END LOOP;
437         ELSE
438             -- Log some debugging info and bail
439             msg('No dated table information was cached.');
440             RAISE dated_table_cache_empty;
441         END IF;
442         --
443         -- Bail if we didn't find the cached info we wanted
444         IF l_tab_idx = -1 THEN
445             msg('Dated table '||p_dated_table_id||' was not cached.');
446             RAISE dated_table_cache_miss;
447         END IF;
448         --
449         RETURN l_tab_idx;
450     END query_dated_table_cache;
451     --
452     -- Return the value of the elapsed time global populated when a full run is
453     -- completed
454     FUNCTION get_elapsed_time RETURN NUMBER IS
455     BEGIN
456         RETURN g_SECONDS_ELAPSED;
457     END get_elapsed_time;
458     --
459     -- Get the time taken to execute the last run
460     FUNCTION get_elapsed_time_text RETURN VARCHAR2 IS
461     BEGIN
462         RETURN 'Elapsed time: '||TO_CHAR(get_elapsed_time,'fm99999990.000')||' seconds';
463     END get_elapsed_time_text;
464     --
465     -- < PRIVATE PROCEDURES > ------------------------------------------------
466     --
467     -- Get the event details for a single assignment ID
468     PROCEDURE get_event_details(
469         p_start_date        IN      DATE,
470         p_end_date          IN      DATE,
471         p_assignment_id     IN      NUMBER,
472 	p_element_entry_id  IN      NUMBER,
473         p_detailed_output   IN OUT  NOCOPY pay_interpreter_pkg.t_detailed_output_table_type,
474         p_proration_dates   IN OUT  NOCOPY pay_interpreter_pkg.t_proration_dates_table_type
475     ) IS
476         -- Business group ID
477         l_business_group_id     NUMBER;
478         --
479         -- Temporary table variables to hold the results from the event model
480         -- procedure calls. These results aren't used
481         l_dynamic_sql           pay_interpreter_pkg.t_dynamic_sql_tab;
482         l_proration_change_type pay_interpreter_pkg.t_proration_type_table_type;
483         l_proration_type        pay_interpreter_pkg.t_proration_type_table_type;
484         --
485     BEGIN
486         msg('Getting event details for assignment: '||p_assignment_id);
487         --
488         -- Get the business group ID
489         l_business_group_id := get_business_group_id(p_assignment_id);
490         --
491         -- Get and parse the events that occurred. Note that we don't call
492         -- unique_sort as we do in entry_affected, since we do actually want
493         -- a list of all the events that occurred and the effective date of
494         -- each one, not just the unique dates, since we'll later merge the
495         -- events up to the supervisor level
496         pay_interpreter_pkg.event_group_tables_affected(
497             p_element_entry_id,
498 	    NULL,
499 	    g_EVENT_GROUP_ID,
500     	    p_assignment_id,
501 	    l_business_group_id,
502 	    p_start_date,
503     	    p_end_date,
504 	    NULL,
505 	    NULL,
506             c_PROCESS_MODE,
507 	    l_dynamic_sql,
508 	    p_proration_dates,
509     	    l_proration_change_type,
510             p_detailed_output
511         );
512         --
513     END get_event_details;
514     --
515     -- Get a flag to indicated whether or not the given table has the requested column
516     PROCEDURE get_column_flag(
517         p_table_info    IN      pay_interpreter_pkg.t_distinct_table_rec,
518         p_column        IN      VARCHAR2,
519         p_flag          IN OUT  NOCOPY VARCHAR2
520     ) IS
521         -- Find the column in the data-dictionary
522 --        CURSOR get_info(cp_name IN VARCHAR2,cp_column IN VARCHAR2,cp_owner IN VARCHAR2) IS
523 --            SELECT  'Y'
524 --            FROM    all_tab_columns
525 --            WHERE   table_name  = cp_name
526 --            AND     column_name = cp_column
527 --            AND     owner = cp_owner;
528 
529 -- Bug 15882261 : Modified cursor defination to refer to dba_tab_columns
530 --		  instead of all_tab_columns
531 
532         CURSOR get_info(cp_name IN VARCHAR2,cp_column IN VARCHAR2,cp_owner IN VARCHAR2) IS
533             SELECT  'Y'
534             FROM    dba_tab_columns tab, user_synonyms syn
535             WHERE   tab.table_name  = syn.table_name
536 			AND	    tab.owner = syn.table_owner
537 			AND	    syn.synonym_name = cp_name
538             AND     tab.column_name = cp_column
539             AND     tab.owner = cp_owner;
540 
541 
542         --
543         l_schema VARCHAR2(30);
544         --
545     BEGIN
546         l_schema := paywsdyg_pkg.get_table_owner(UPPER(p_table_info.table_name));
547         -- If we haven't already populated this flag
548         IF NVL(p_flag,'X') = 'X' THEN
549             --
550             -- Fetch from the cursor
551             OPEN get_info(
552                 UPPER(p_table_info.table_name),
553                 UPPER(p_column),
554                 UPPER(l_schema)
555             );
556             FETCH get_info INTO p_flag;
557             --
558             -- If nothing came back then that column's not in the table we're looking at
559             IF get_info%NOTFOUND THEN
560                 p_flag := 'N';
561             END IF;
562             CLOSE get_info;
563             --
564             -- Write out some debug info
565             msg('table = '||LOWER(p_table_info.table_name)||' '||LOWER(p_column)||' ? = '||p_flag);
566         END IF;
567     END get_column_flag;
568     --
572         p_idx       IN     NUMBER,
569     -- Set the column flags in the dated table cache and return a copy of
570     -- the record we modified
571     PROCEDURE set_dated_table_column_flags(
573         p_rec       IN OUT NOCOPY pay_interpreter_pkg.t_distinct_table_rec,
574         p_xrec      IN OUT NOCOPY t_dated_table_extras_rec
575     ) IS
576     BEGIN
577         -- See if there's a supervisor ID on the dated table
578         get_column_flag(
579             pay_interpreter_pkg.t_distinct_tab(p_idx),
580             'SUPERVISOR_ID',
581             g_DATED_TABLE_EXTRAS(p_idx).has_supervisor_id
582         );
583         -- See if there's a location ID on the dated table
584         get_column_flag(
585             pay_interpreter_pkg.t_distinct_tab(p_idx),
586             'LOCATION_ID',
587             g_DATED_TABLE_EXTRAS(p_idx).has_location_id
588         );
589         --
590         -- See if we've got an assignment ID (only really need one if supervisor or location is missing)
591         get_column_flag(
592             pay_interpreter_pkg.t_distinct_tab(p_idx),
593             'ASSIGNMENT_ID',
594             g_DATED_TABLE_EXTRAS(p_idx).has_assignment_id
595         );
596         --
597         -- Copy the records we updated to the return parameter
598         p_rec := pay_interpreter_pkg.t_distinct_tab(p_idx);
599         p_xrec := g_DATED_TABLE_EXTRAS(p_idx);
600         --
601     END set_dated_table_column_flags;
602     --
603     -- Build the SQL statement we'll need to use to get the supervisor and location IDs
604     -- Note that all statements must have the surrogate key ID and the effective
605     -- date bind variables, event if there're not used, so we can dynamically open the
606     -- cursor in a consistent way
607     -- This statement is cached in the t_distinct_tab record
608     PROCEDURE get_additional_select(
609         p_tab_id        IN      NUMBER,
610         p_want_location IN      BOOLEAN,
611         p_sql           IN OUT  NOCOPY VARCHAR2
612     ) IS
613         --
614         l_tab_info              pay_interpreter_pkg.t_distinct_table_rec;
615         l_tab_extra             t_dated_table_extras_rec;
616         l_used_skt              BOOLEAN := FALSE;
617         l_used_paf              BOOLEAN := FALSE;
618         l_tab_idx               NUMBER;
619         --
620     BEGIN
621         --
622         -- Find the dated table information in the cache which was
623         -- populated when we called event_group_tables in get_assignment_event_details
624         l_tab_idx := query_dated_table_cache(p_tab_id);
625         --
626         -- Check the cached info in the dated table record to see if we've already built
627         -- the SQL statement for this dated table
628         IF g_DATED_TABLE_EXTRAS(l_tab_idx).sql_statement IS NOT NULL THEN
629             msg('Reusing SQL statement from dated table cache');
630             p_sql := g_DATED_TABLE_EXTRAS(l_tab_idx).sql_statement;
631             RETURN;
632         END IF;
633         --
634         -- Set the flags indicating which columns we've got on this table and put
635         -- a copy of the cached information into l_tab_info
636         set_dated_table_column_flags(
637             l_tab_idx,
638             l_tab_info,
639             l_tab_extra
640         );
641         --
642         -- Build the SQL depending on what columns we've got
643         msg('Building SQL statement...');
644         --
645         -- Add the select list
646         p_sql := 'SELECT ';
647         --
648         -- Get the supervisor ID
649         IF l_tab_extra.has_supervisor_id = 'Y' THEN
650             -- We've got a supervisor ID in this table
651             p_sql := p_sql||'skt.supervisor_id, ';
652             msg('Got supervisor_id locally');
653             l_used_skt := TRUE;
654         ELSIF l_tab_extra.has_assignment_id = 'Y' THEN
655             -- Find it from the assignment
656             p_sql := p_sql||'paf.supervisor_id, ';
657             msg('Going to assignment for supervisor_id');
658             l_used_paf := TRUE;
659         ELSE
660             -- Can't get it
661             p_sql := p_sql||c_ALL_SUPERVISORS_ID||' supervisor_id, ';
662             msg('Can''t get supervisor_id');
663         END IF;
664         --
665         -- Get the location ID
666         IF p_want_location THEN
667             IF l_tab_extra.has_location_id = 'Y' THEN
668                 -- We've got a location ID in this table
669                 p_sql := p_sql||'skt.location_id, ';
670                 msg('Got location_id locally');
671                 l_used_skt := TRUE;
672             ELSIF l_tab_extra.has_assignment_id = 'Y' THEN
673                 -- Find it from the assignment
674                 p_sql := p_sql||'paf.location_id, ';
675                 msg('Going to assignment for location_id');
676                 l_used_paf := TRUE;
677             ELSE
678                 -- Can't get it
679                 p_sql := p_sql||c_BLANK_LOCATION_ID||' location_id, ';
680                 msg('Can''t get location_id');
681             END IF;
682         ELSE
683             -- Don't want it
684             p_sql := p_sql||c_BLANK_LOCATION_ID||' location_id, ';
685             msg('Don''t want location_id');
686         END IF;
687         --
688         -- Get the effective dates
689         IF (NOT l_used_paf) AND (NOT l_used_skt) THEN
690             p_sql := p_sql||'TRUNC(SYSDATE) effective_start_date, '||
691                             'TRUNC(SYSDATE) effective_end_date ';
692             msg('Adding default dates');
693             --
694         ELSE
695             p_sql := p_sql||'skt.'||l_tab_info.start_date_name||' effective_start_date, '||
696                             'skt.'||l_tab_info.end_date_name||' effective_end_date ';
697             msg('Using surrogate key table dates');
698             --
699         END IF;
700         --
701         -- Add the from list
702         p_sql := p_sql||'FROM ';
703         --
704         -- Which tables did we have to go to?
705         IF (NOT l_used_paf) AND (NOT l_used_skt) THEN
706             -- Didn't look at any tables
707             p_sql := p_sql||'dual ';
708             msg('No tables used');
709             --
710         ELSE
711             -- Must always join to the table to which the surrogate key relates
712             p_sql := p_sql||l_tab_info.table_name||' skt ';
713             msg('Getting info from '||LOWER(l_tab_info.table_name));
714             --
715             -- Did we also have to go back to the assignment to get anything
716             IF l_used_paf THEN
717                 p_sql := p_sql||', '||c_ASSIGNMENTS_TABLE||' paf ';
718                 msg('Also getting info from '||c_ASSIGNMENTS_TABLE);
719             END IF;
720         END IF;
721         --
722         -- Add the where clause
723         p_sql := p_sql||'WHERE ';
724         --
725         -- Which tables did we have to go to?
726         IF (NOT l_used_paf) AND (NOT l_used_skt) THEN
727             -- Didn't look at any tables
728             p_sql := p_sql||':surrogate_key IS NOT NULL '||
729 	                    'AND :effective_start_date IS NOT NULL '||
730 	                    'AND :effective_end_date IS NOT NULL ';
731             msg('Didn''t need any where clause, adding default');
732             --
733         ELSE
734             -- Always have to join to the table to which the surrogate key relates
735             p_sql := p_sql||'skt.'||l_tab_info.surrogate_key_name||' = :surrogate_key '||
736                             'AND :effective_start_date <= skt.'||l_tab_info.end_date_name||' '||
737 			    'AND :effective_end_date >= skt.'||l_tab_info.start_date_name||' ';
738             msg(
739                 'Adding where clause for '||LOWER(l_tab_info.surrogate_key_name)||', '||
740                 LOWER(l_tab_info.start_date_name)||' and '||
741                 LOWER(l_tab_info.end_date_name)||' columns'
742             );
743             --
744             -- Did we also have to go back to the assignment to get anything
745             IF l_used_paf THEN
746                 p_sql := p_sql||'AND paf.assignment_id = skt.assignment_id '||
747                                 'AND paf.effective_end_date >= skt.'||l_tab_info.start_date_name||' '||
748                                 'AND paf.effective_start_date <= skt.'||l_tab_info.end_date_name||' ';
749                 msg('Also joining to assignments table');
750                 --
751             END IF;
752         END IF;
753         --
754         -- Only include rows where the supervisor ID is set (and the location if needed)
755         IF l_tab_extra.has_supervisor_id = 'Y' THEN
756             p_sql := p_sql||'AND skt.supervisor_id IS NOT NULL ';
757             msg('Surrogate table supervisor must not be null');
758         ELSIF l_tab_extra.has_assignment_id = 'Y' THEN
759             p_sql := p_sql||'AND paf.supervisor_id IS NOT NULL ';
760             msg('Assignment table supervisor must not be null');
761         END IF;
762         --
763         IF p_want_location THEN
764             IF l_tab_extra.has_location_id = 'Y' THEN
765                 p_sql := p_sql||'AND skt.location_id IS NOT NULL ';
766                 msg('Surrogate table location_id must not be null');
767             ELSIF l_tab_extra.has_assignment_id = 'Y' THEN
768                 p_sql := p_sql||'AND paf.location_id IS NOT NULL ';
769                 msg('Assignment table location_id must not be null');
770             END IF;
771         ELSE
772             msg('Don''t care where location_id comes from, or if it''s null');
773         END IF;
774         --
775         -- Lets see the SQL statement
776         msg('Finished building statement');
777         msg('<sqlstatement>');
778         msg(p_sql);
779         msg('</sqlstatement>');
780         --
781         msg('Caching statement in record '||l_tab_idx);
782         g_DATED_TABLE_EXTRAS(l_tab_idx).sql_statement := p_sql;
783         --
784     END get_additional_select;
785     --
786     -- < PUBLIC FUNCTIONS > --------------------------------------------------
787     --
788     -- Return the "all supervisors" constant
789     FUNCTION all_supervisors_id RETURN NUMBER IS
790     BEGIN
791         RETURN c_ALL_SUPERVISORS_ID;
792     END all_supervisors_id;
793     --
794     -- Return the "blank location" constant
795     FUNCTION blank_location_id RETURN NUMBER IS
796     BEGIN
797         RETURN c_BLANK_LOCATION_ID;
798     END blank_location_id;
799     --
800     -- Get the event group ID based on its name and initialise the cache
801     PROCEDURE init_event_group_cache(p_event_group_name IN VARCHAR2) IS
802         --
803         CURSOR get_evt(p_grp IN VARCHAR2) IS
804             SELECT          event_group_id
805             FROM            pay_event_groups
806             WHERE           event_group_name = p_grp;
807         --
808     BEGIN
809 	--
810         -- Find the event group ID, raises no_data_found
811         -- if the event group name is invalid (i.e. not found)
812 	-- 2984406 - Fetch into a global ID to save repeated queries to get the ID
813         OPEN get_evt(p_event_group_name);
814         FETCH get_evt INTO g_EVENT_GROUP_ID;
815         IF get_evt%NOTFOUND THEN
816             -- Trace some debug info and raise the error
817             dbg('Event group "'||p_event_group_name||'" not found.');
818             CLOSE get_evt;
819             RAISE event_group_not_found;
820         END IF;
821         CLOSE get_evt;
822 	--
823 	-- Populate the internal package global caches to hold details of all
824         -- the dated tables that this event group uses
825         pay_interpreter_pkg.event_group_tables(g_EVENT_GROUP_ID);
826         --
827         -- Make sure we've got enough extra information records for all the
828         -- dated tables we're going to use
829         FOR i IN pay_interpreter_pkg.t_distinct_tab.FIRST .. pay_interpreter_pkg.t_distinct_tab.LAST LOOP
830             g_DATED_TABLE_EXTRAS(i).has_supervisor_id := 'X';
831             g_DATED_TABLE_EXTRAS(i).has_location_id   := 'X';
832             g_DATED_TABLE_EXTRAS(i).has_assignment_id := 'X';
833             g_DATED_TABLE_EXTRAS(i).sql_statement     := NULL;
834         END LOOP;
835         --
836     END init_event_group_cache;
837     --
838     -- < PUBLIC PROCEDURES > -------------------------------------------------
839     --
840     -- Get the event group ID based on its name
841     FUNCTION get_event_group_id(p_event_group_name IN VARCHAR2) RETURN NUMBER IS
842     BEGIN
843         IF g_EVENT_GROUP_ID IS NULL THEN
844 	    init_event_group_cache(p_event_group_name);
845 	END IF;
846 	RETURN g_EVENT_GROUP_ID;
847     END get_event_group_id;
848     --
849     -- Log a message, either using fnd_file, or hr_utility.trace
850     PROCEDURE msg(p_text IN VARCHAR2) IS
851         l_pos   NUMBER := 1;
855         -- Chop up the string into 250 char chunks if we're writing to the
852         l_txt   VARCHAR2(255);
853     BEGIN
854         --
856         -- concurrent manager log file
857         IF g_concurrent THEN
858             LOOP
859                 l_txt := SUBSTR(p_text,l_pos,c_OUTPUT_LINE_LENGTH);
860                 fnd_file.put_line(fnd_file.LOG,l_txt);
861                 --
862                 l_pos := l_pos + c_OUTPUT_LINE_LENGTH;
863                 EXIT WHEN l_pos > LENGTH(p_text);
864             END LOOP;
865         ELSE
866             -- Use the normal trace stuff
867             hr_utility.trace(p_text);
868         END IF;
869     END msg;
870     --
871     PROCEDURE dbg(p_text IN VARCHAR2) IS
872     BEGIN
873         IF g_debugging THEN
874             msg(p_text);
875         END IF;
876     END dbg;
877     --
878     -- Switch on or off client debugging.
879     PROCEDURE set_client_debugging(p_on IN BOOLEAN) IS
880     BEGIN
881         -- Stubbed out because we're not allowed to use dbms_output
882         RAISE feature_not_supported;
883     END set_client_debugging;
884     --
885     -- Replacement for the above - allow logging to concurrent manager log
886     PROCEDURE set_concurrent_logging(p_on IN BOOLEAN) IS
887     BEGIN
888         g_concurrent := p_on;
889     END set_concurrent_logging;
890     --
891     -- Switch debugging messages on
892     PROCEDURE set_debugging(p_on IN BOOLEAN) IS
893     BEGIN
894         g_debugging := p_on;
895     END set_debugging;
896     --
897     -- Process the detailed output information from an assignment event
898     PROCEDURE process_event_details(
899         p_detailed_output   IN      pay_interpreter_pkg.t_detailed_output_table_type,
900         p_proration_dates   IN      pay_interpreter_pkg.t_proration_dates_table_type,
901         p_summary_refresh   IN OUT  NOCOPY t_summary_refresh_tab_type,
902         p_location_stripe   IN      BOOLEAN DEFAULT FALSE
903     ) IS
904         --
905         -- Local variables
906         l_idx                   NUMBER;
907         --
908         -- The SQL statement we'll need to use to get the supervisor ID
909         -- and the dynamic cursor stuff
910         TYPE t_csr IS REF CURSOR;
911         --
912         l_csr                   t_csr;
913         l_sql                   VARCHAR2(2000);
914         l_supv                  NUMBER;
915         l_loct                  NUMBER;
916         l_sdt                   DATE;
917         l_edt                   DATE;
918         --
919     BEGIN
920         --
921         -- Make sure we have some detailed output and some dates
922         IF NVL(p_detailed_output.FIRST,0) < 1 AND
923            NVL(p_proration_dates.FIRST,0) < 1
924         THEN
925             msg('No detailed output supplied to process_event_details, ignoring');
926             RETURN;
927         END IF;
928         --
929         -- There should be the same number of records in the detailed output
930         -- and proration dates tables, if not then that's an error since we need
931         -- to have an effective date for each event
932         IF p_detailed_output.FIRST <> p_proration_dates.FIRST OR
933            p_detailed_output.LAST <> p_proration_dates.LAST
934         THEN
935             -- Trace some debug info and raise a custom error
936             msg('Records in detailed output don''t match those in proration dates.');
937             msg('t_detailed_output = '||p_detailed_output.FIRST||' -> '||p_detailed_output.LAST);
938             msg('t_proration_dates = '||p_proration_dates.FIRST||' -> '||p_proration_dates.LAST);
939             RAISE mismatch_when_summarizing;
940         END IF;
941         --
942         -- Process each record in the detailed output
943         FOR i IN p_detailed_output.FIRST .. p_detailed_output.LAST LOOP
944             --
945             -- Debugging information for event found
946             msg(
947                 'Processing event found at '||
948                 dec_date(p_proration_dates(i))||'/'||inc_date(p_proration_dates(i))||' on '||
949                 p_detailed_output(i).dated_table_id||' ID '||
950                 p_detailed_output(i).surrogate_key
951             );
952             --
953             -- Build the query to get the additional IDs based on the
954             -- information about the dated table that the event occurred on,
955             -- must always include the 3 bind variables; surrogate_key,
956             -- effective_start/end_date
957             get_additional_select(
958                 p_detailed_output(i).dated_table_id,
959                 p_location_stripe,
960                 l_sql
961             );
962             --
963             -- Open a cursor for the SQL we just built
964             OPEN l_csr FOR l_sql USING
965 	        p_detailed_output(i).surrogate_key,
966 		dec_date(p_proration_dates(i)),
967 		inc_date(p_proration_dates(i));
968             LOOP
969                 -- Get the IDs and bail when we run out
970                 FETCH l_csr INTO l_supv,l_loct,l_sdt,l_edt;
971                 EXIT WHEN l_csr%NOTFOUND;
972                 --
973                 -- Find the entry in the summary table
974                 --
975                 get_summary_idx(l_supv, l_loct, l_idx, p_summary_refresh);
976                 --
977                 -- The start date is the earliest out of the currently recorded effective date
978                 -- for this combination (NVL'd in case we haven't recorded anything yet) and the
982                         p_summary_refresh(l_idx).effective_start_date,
979                 -- effective date of the event we're recording
980                 p_summary_refresh(l_idx).effective_start_date := LEAST(
981                     NVL(
983                         p_proration_dates(i)
984                     ),
985                     dec_date(l_sdt)
986                 );
987                 --
988                 -- Update the end date similarly, but with the most recent of the two dates
989                 p_summary_refresh(l_idx).effective_end_date := GREATEST(
990                     NVL(
991                         p_summary_refresh(l_idx).effective_end_date,
992                         p_proration_dates(i)
993                     ),
994                     inc_date(l_edt)
995                 );
996                 --
997             END LOOP;
998             --
999             -- Done with the cursor;
1000             CLOSE l_csr;
1001         END LOOP;
1002         --
1003     END process_event_details;
1004     --
1005     -- Build up the SQL query for determining date-effective updates
1006     FUNCTION build_csr_dt_updates(p_dtid IN NUMBER,p_dtname IN VARCHAR2,p_eeid IN NUMBER) RETURN VARCHAR2 IS
1007         --
1008 	-- Get a list of the columns that are in the event group and table
1009         CURSOR get_columns(p_evt IN NUMBER,p_tab IN NUMBER) IS
1010             SELECT column_name
1011             FROM   pay_datetracked_events pde
1012             WHERE  event_group_id = p_evt
1013             AND    dated_table_id = p_tab
1014             AND    update_type = 'U';
1015         --
1016         l_qry VARCHAR2(32767);
1017     BEGIN
1018         l_qry := 'SELECT n.assignment_id, ';
1019 	--
1020 	IF p_dtid = p_eeid THEN
1021 	    l_qry := l_qry||'n.element_entry_id, ';
1022 	END IF;
1023 	--
1024         l_qry := l_qry||
1025 	         '       MIN(LEAST(o.effective_start_date,n.effective_start_date)) effective_start_date, '||
1026                  '       MAX(GREATEST(o.effective_start_date,n.effective_start_date)) effective_end_date '||
1027                  'FROM   '||p_dtname||' n, '||
1028                  '       '||p_dtname||' o '||
1029                  'WHERE n.assignment_id = o.assignment_id '||
1030                  'AND n.effective_start_date = o.effective_end_date + 1 '||
1031                  'AND (';
1032         --
1033         FOR col_rec IN get_columns(g_EVENT_GROUP_ID,p_dtid) LOOP
1034             IF get_columns%rowcount > 1 THEN
1035                 l_qry := l_qry||' OR ';
1036             END IF;
1037             --
1038             l_qry := l_qry||'NVL(TO_CHAR(o.'||col_rec.column_name||'), ''$Sys_Def$'') <> '||
1039                             'NVL(TO_CHAR(n.'||col_rec.column_name||'), ''$Sys_Def$'')';
1040             --
1041         END LOOP;
1042         --
1043         l_qry := l_qry ||') '||
1044 	             'AND n.assignment_id IN ('||
1045 		     'SELECT '||
1046 		     '    ppe.assignment_id '||
1047 		     '    FROM pay_process_events ppe,pay_event_updates peu '||
1048 		     '    WHERE ppe.creation_date BETWEEN :1 AND :2 '||
1049 		     '    AND peu.event_update_id = ppe.event_update_id '||
1050 		     '    AND peu.dated_table_id = '||p_dtid||
1051 		    ') '||
1052                     'GROUP BY n.assignment_id';
1053 	IF p_dtid = p_eeid THEN
1054 	    l_qry := l_qry||',n.element_entry_id';
1055 	END IF;
1056 	--
1057 	RETURN l_qry;
1058     END build_csr_dt_updates;
1059     --
1060     FUNCTION get_element_entry_table_id RETURN NUMBER IS
1061         l_element_entries_dt_id NUMBER;
1062     BEGIN
1063     	--
1064 	-- Get the (special case) element entries table ID
1065 	BEGIN
1066 	    SELECT  dated_table_id
1067 	    INTO    l_element_entries_dt_id
1068 	    FROM    pay_dated_tables
1069 	    WHERE   table_name = 'PAY_ELEMENT_ENTRIES_F';
1070 	EXCEPTION
1071 	    WHEN OTHERS THEN l_element_entries_dt_id := NULL;
1072 	END;
1073 	RETURN l_element_entries_dt_id;
1074     END get_element_entry_table_id;
1075 --
1076     --
1077     -- Is the Entry Id supplied a Salary Element
1078     --
1079     FUNCTION is_salary(p_ee_id in number)
1080     RETURN BOOLEAN IS
1081       l_dummy number;
1082     BEGIN
1083 --
1084        select /*+ ordered */ distinct pee.element_entry_id
1085          into l_dummy
1086          from pay_element_entries_f    pee,
1087               per_all_assignments_f    paf,
1088               per_pay_bases            ppb,
1089               pay_element_entry_values_f peev
1090         where pee.element_entry_id = p_ee_id
1091           and pee.assignment_id = paf.assignment_id
1092           and paf.pay_basis_id = ppb.pay_basis_id
1093           and pee.element_entry_id = peev.element_entry_id
1094           and ppb.input_value_id = peev.input_value_id;
1095 --
1096          return TRUE;
1097 --
1098     EXCEPTION
1099          when no_data_found then
1100             return FALSE;
1101 --
1102     END is_salary;
1103     --
1104     -- Get a list of the assignments that have events recorded for them.
1105     -- Bug 2984406: Restructure to fetch affected assignments in three stages,
1106     -- basically changes the whole structure of this procedure
1107     PROCEDURE get_assignments_affected(
1108         p_start_date        IN      DATE,
1109         p_end_date          IN      DATE,
1110         p_assignments       IN OUT  NOCOPY t_assignment_id_tab_type
1111     ) IS
1112         --
1113         l_csr 				csr_dyn_ref;
1114         l_qry 				VARCHAR2(32767);
1115 	l_assignment_id 		NUMBER;
1116 	l_element_entry_id 		NUMBER;
1117 	l_effective_start_date 		DATE;
1118 	l_effective_end_date 		DATE;
1119 	l_element_entries_dt_id 	NUMBER;
1120 	--
1121         l_loop NUMBER;
1122         curr_ass_id NUMBER;
1123         new_assignment BOOLEAN;
1124         --
1125     BEGIN
1126         -- Get the affected assignments
1127         msg('Getting affected assignments for '||fnd_date.date_to_canonical(p_start_date)||' '||fnd_date.date_to_canonical(p_end_date));
1128         l_loop := 0;
1129         l_element_entries_dt_id := get_element_entry_table_id;
1130 	--
1131 	-- Get those affected by inserts and deletes
1132 	msg('Getting inserts and deletes');
1133         curr_ass_id := -1;
1134         FOR assrec in csr_all_changes(p_start_date,p_end_date) loop
1135 --
1136             if(curr_ass_id <> assrec.assignment_id) then
1137                curr_ass_id := assrec.assignment_id;
1138                new_assignment := TRUE;
1139             end if;
1140 --
1141             /* If the table is element entries then we need to do some thing */
1142             if assrec.dated_table_id = l_element_entries_dt_id then
1143 --
1144                   if( is_salary(assrec.surrogate_key) = TRUE) then
1145 --
1146                     /* It is salary, here comes the tricky part
1147                     */
1148                     if (new_assignment = TRUE) then
1149                       l_loop := l_loop + 1;
1150                       p_assignments(l_loop).element_entry_id
1151                                     := assrec.surrogate_key;
1152                       p_assignments(l_loop).assignment_id
1153                                     := assrec.assignment_id;
1154                       p_assignments(l_loop).effective_start_date
1155                                     := dec_date(assrec.effective_start_date);
1156                       p_assignments(l_loop).effective_end_date
1157                                     := inc_date(assrec.effective_end_date);
1158                       new_assignment := FALSE;
1159                     else
1160                       if (p_assignments(l_loop).element_entry_id is null) then
1161                          p_assignments(l_loop).element_entry_id
1162                            := assrec.surrogate_key;
1163                          p_assignments(l_loop).effective_start_date
1164                            := least(p_assignments(l_loop).effective_start_date,
1165                                     dec_date(assrec.effective_start_date));
1166                          p_assignments(l_loop).effective_end_date
1167                            := greatest(p_assignments(l_loop).effective_end_date,
1168                                        inc_date(assrec.effective_end_date));
1169                       else
1170                          /* Yeah we really need to create a new one */
1171                          l_loop := l_loop + 1;
1172                          p_assignments(l_loop).element_entry_id
1173                                        := assrec.surrogate_key;
1174                          p_assignments(l_loop).assignment_id
1175                                        := assrec.assignment_id;
1176                          p_assignments(l_loop).effective_start_date
1177                                        := dec_date(assrec.effective_start_date);
1178                          p_assignments(l_loop).effective_end_date
1179                                        := inc_date(assrec.effective_end_date);
1180                       end if;
1181                     end if;
1182 --
1183                   else
1184                      /* do nothing it's not salary, hence
1185                         not interested
1186                      */
1187                      null;
1188                   end if;
1189 --
1190             else
1191                 /* It's not an element entry change.
1192                    Check that a row has not already been placed
1193                    in the pl/sql table for this assignment
1194                    If it has just adjust the dates.
1195                 */
1196                 if (new_assignment = TRUE) then
1197                    l_loop := l_loop + 1;
1198                    p_assignments(l_loop).element_entry_id := NULL;
1199                    p_assignments(l_loop).assignment_id
1200                                  := assrec.assignment_id;
1201                    p_assignments(l_loop).effective_start_date
1202                                  := dec_date(assrec.effective_start_date);
1203                    p_assignments(l_loop).effective_end_date
1204                                  := inc_date(assrec.effective_end_date);
1205                    new_assignment := FALSE;
1206                 else
1207                    p_assignments(l_loop).effective_start_date
1208                        := least(p_assignments(l_loop).effective_start_date,
1209                                 dec_date(assrec.effective_start_date));
1210                    p_assignments(l_loop).effective_end_date
1211                        := greatest(p_assignments(l_loop).effective_end_date,
1212                                    inc_date(assrec.effective_end_date));
1213                 end if;
1214             end if;
1215         END LOOP;
1216         --
1217         IF NVL(p_assignments.FIRST,0) < 1 THEN
1218             msg('No assignment events found within specified date range');
1219             RAISE no_assignment_events_found;
1220         END IF;
1221         --
1222     END get_assignments_affected;
1223     --
1224     -- Get the payroll event details based on a list of assignment IDs
1225     PROCEDURE get_refresh_periods(
1226         p_assignments       IN OUT  NOCOPY t_assignment_id_tab_type,
1227         p_summary_refresh   IN OUT  NOCOPY t_summary_refresh_tab_type,
1228         p_start_date        IN      DATE,
1229         p_end_date          IN      DATE,
1230         p_location_stripe   IN      BOOLEAN DEFAULT FALSE
1231     ) IS
1232         --
1233         -- Local table-type variables for use with processing the event details
1234         l_detailed_output       pay_interpreter_pkg.t_detailed_output_table_type;
1235         l_proration_dates       pay_interpreter_pkg.t_proration_dates_table_type;
1236         --
1237     BEGIN
1238         --
1239         -- Check we've got something to process
1240         IF NVL(p_assignments.FIRST,0) < 1 THEN
1241             msg('No data from process in get_assignment_events');
1242             RAISE no_assignments_supplied;
1243         END IF;
1244         --
1245         -- Process all the assignments we got
1246         FOR i IN p_assignments.FIRST .. p_assignments.LAST LOOP
1247 --
1248             l_detailed_output.delete;
1249             l_proration_dates.delete;
1250             --
1251             -- Get the detailed event information for this assignment
1252             get_event_details(
1253                 p_start_date,
1254                 p_end_date,
1255                 p_assignments(i).assignment_id,
1256 		p_assignments(i).element_entry_id,
1257                 l_detailed_output,
1258                 l_proration_dates
1259             );
1260             --
1261             -- Process the event details for this assignment
1262             -- (a check is done within this procedure for the detailed output being empty)
1263             msg(
1264                 'Processing event details ('||
1265                 p_assignments(i).assignment_id||' '||
1266                 p_assignments(i).effective_start_date||' -> '||
1267                 p_assignments(i).effective_end_date||')'
1268             );
1269             process_event_details(
1270                 l_detailed_output,
1271                 l_proration_dates,
1272                 p_summary_refresh,
1273                 p_location_stripe
1274             );
1275         END LOOP;
1276     END get_refresh_periods;
1277     --
1278     -- If we recorded some information for "all records" (i.e. an event ocurred on a
1279     -- table which didn't allow us to get a proper supervisor or location ID)
1280     -- then we need to delete any specific records that fall completely within
1281     -- "refresh all" period, and chop up any records that just overlap that period, otherwise
1282     -- we'll just copy the temporary table to the output parameter
1283     PROCEDURE de_dupe_refresh_periods(
1284         p_summary_refresh_temp  IN OUT NOCOPY t_summary_refresh_tab_type,
1285         p_summary_refresh       IN OUT NOCOPY t_summary_refresh_tab_type,
1286         p_all_supv              IN OUT NOCOPY BOOLEAN,
1287         p_out_num               IN OUT NOCOPY NUMBER,
1288         p_all_start             IN OUT NOCOPY DATE,
1289         p_all_end               IN OUT NOCOPY DATE,
1290         p_want_location         IN     BOOLEAN DEFAULT FALSE
1291     ) IS
1292     BEGIN
1293         p_all_supv := FALSE;
1294         p_out_num  := 0;
1295         --
1296         IF NVL(p_summary_refresh_temp.FIRST,0) > 0 THEN
1297             --
1298             -- Find the "all" record
1299             FOR i IN p_summary_refresh_temp.FIRST .. p_summary_refresh_temp.LAST LOOP
1300                 IF p_summary_refresh_temp(i).supervisor_id = c_ALL_SUPERVISORS_ID OR
1301                    (p_summary_refresh_temp(i).location_id = c_BLANK_LOCATION_ID AND p_want_location)
1302                 THEN
1303                     p_all_start := p_summary_refresh(i).effective_start_date;
1304                     p_all_end := p_summary_refresh(i).effective_end_date;
1305                     p_all_supv := TRUE;
1306                 END IF;
1307             END LOOP;
1308             --
1309             IF p_all_supv THEN
1310                 --
1311                 -- If either the start or end date is null then bail
1312                 IF p_all_start IS NULL OR p_all_end IS NULL THEN
1313                     msg('An "all" record was missing one or other of the required dates');
1314                     RAISE missing_dates_in_all_record;
1315                 END IF;
1316                 --
1317                 -- Record the "all" record
1318                 p_out_num := p_out_num + 1;
1319                 p_summary_refresh(p_out_num).supervisor_id := c_ALL_SUPERVISORS_ID;
1320                 p_summary_refresh(p_out_num).location_id := c_BLANK_LOCATION_ID;
1321                 p_summary_refresh(p_out_num).effective_start_date := p_all_start;
1322                 p_summary_refresh(p_out_num).effective_end_date := p_all_end;
1323                 --
1324                 -- Go through the other records (skipping the "all" one) and chopping the dates
1325                 FOR i IN p_summary_refresh_temp.FIRST .. p_summary_refresh_temp.LAST LOOP
1326                     IF p_summary_refresh_temp(i).supervisor_id <> c_ALL_SUPERVISORS_ID AND
1327                        (p_summary_refresh_temp(i).location_id <> c_BLANK_LOCATION_ID OR (NOT p_want_location))
1328                     THEN
1329                         --
1330                         -- If either the start or end date is null then bail
1331                         IF p_summary_refresh_temp(i).effective_start_date IS NULL OR
1332                            p_summary_refresh_temp(i).effective_end_date IS NULL
1333                         THEN
1334                             msg(
1335                                 'A specific ('||p_summary_refresh_temp(i).supervisor_id||
1336                                 '/'||p_summary_refresh_temp(i).location_id||
1337                                 ') refresh record is missing a start or end date'
1338                             );
1339                             RAISE missing_dates_for_specific;
1340                         END IF;
1341                         --
1342                         -- If the specific refresh record falls completely within the "all"
1343                         -- period the don't process it
1344                         IF p_summary_refresh_temp(i).effective_start_date >= p_all_start AND
1345                            p_summary_refresh_temp(i).effective_end_date <= p_all_end
1346                         THEN
1347                             msg(
1348                                 'Specific '||p_summary_refresh_temp(i).supervisor_id||
1349                                 '/'||p_summary_refresh_temp(i).location_id||
1350                                 ' falls entirely within "all" refresh period, ignoring.'
1351                             );
1352                         ELSE
1353                             -- If this record starts before the "all" period then record a segment
1354                             IF p_summary_refresh_temp(i).effective_start_date < p_all_start THEN
1355                                 msg(
1356                                     'Specific '||p_summary_refresh_temp(i).supervisor_id||
1357                                     '/'||p_summary_refresh_temp(i).location_id||
1358                                     ' starts before the "all" refresh period, processing.'
1359                                 );
1360                                 --
1361                                 p_out_num := p_out_num + 1;
1362                                 p_summary_refresh(p_out_num) := p_summary_refresh_temp(i);
1363                                 p_summary_refresh(p_out_num).effective_end_date := p_all_start - 1;
1364                             END IF;
1365                             --
1366                             -- If this record end after the "all" period then record a segment
1367                             IF p_summary_refresh_temp(i).effective_end_date > p_all_end THEN
1368                                 msg(
1369                                     'Specific '||p_summary_refresh_temp(i).supervisor_id||
1370                                     '/'||p_summary_refresh_temp(i).location_id||
1371                                     ' ends after the "all" refresh period, processing.'
1372                                 );
1373                                 --
1374                                 p_out_num := p_out_num + 1;
1375                                 p_summary_refresh(p_out_num) := p_summary_refresh_temp(i);
1376                                 p_summary_refresh(p_out_num).effective_start_date := p_all_end + 1;
1377                             END IF;
1378                         END IF;
1379                     END IF;
1380                 END LOOP;
1381                 --
1382             ELSE
1383                 -- No "all" period, just copy everything to the output parameter
1384                 FOR i IN p_summary_refresh_temp.FIRST .. p_summary_refresh_temp.LAST LOOP
1385                     --
1386                     -- If either the start or end date is null then bail
1387                     IF p_summary_refresh_temp(i).effective_start_date IS NULL OR
1388                        p_summary_refresh_temp(i).effective_end_date IS NULL
1389                     THEN
1390                         msg('A specific ('||
1391                             p_summary_refresh_temp(i).supervisor_id||'/'||
1392                             p_summary_refresh_temp(i).location_id||
1393                             ') refresh record is missing a start or end date'
1394                         );
1395                         RAISE missing_dates_for_specific;
1396                     END IF;
1397                     --
1398                     p_out_num := p_out_num + 1;
1399                     p_summary_refresh(p_out_num) := p_summary_refresh_temp(i);
1400                 END LOOP;
1401             END IF;
1402         END IF;
1403         --
1404     END de_dupe_refresh_periods;
1405     --
1406     -- Add a record to the refresh table, as long as it's not there already
1407     PROCEDURE add_summary_refresh_record(
1408         p_idx                IN OUT NOCOPY NUMBER,
1409         p_table              IN OUT NOCOPY t_summary_refresh_tab_type,
1410         p_supervisor         IN     NUMBER,
1411         p_start_date         IN     DATE,
1412         p_end_date           IN     DATE,
1413         p_location           IN     NUMBER,
1414         p_update_mode        IN     BOOLEAN
1415     ) IS
1416         l_found     NUMBER := -1;
1417     BEGIN
1418         --
1419         get_summary_idx(p_supervisor, p_location, p_idx, p_table);
1420         --
1421         p_table(p_idx).effective_start_date :=
1422                           LEAST(dec_date(p_start_date),
1423                                 nvl(p_table(p_idx).effective_start_date,
1424                                     p_start_date));
1425         p_table(p_idx).effective_end_date :=
1426                           GREATEST(inc_date(p_end_date),
1427                                    nvl(p_table(p_idx).effective_end_date,
1428                                        p_end_date));
1429         --
1430     END add_summary_refresh_record;
1431     --
1432     -- Add any date track corrections to supervisor ID on per_all_assignments_f
1433     -- if we've got that column in our event group
1434     PROCEDURE add_supervisor_corrections(
1435         p_summary_refresh   IN OUT  NOCOPY t_summary_refresh_tab_type,
1436         p_start_date        IN      DATE,
1437         p_end_date          IN      DATE,
1438         p_location_stripe   IN      BOOLEAN DEFAULT FALSE
1439     ) IS
1440         --
1441         l_start                 NUMBER      := NVL(p_summary_refresh.LAST,0) + 1;
1442         l_idx                   NUMBER      := l_start;
1443         l_end                   NUMBER;
1444         l_stripe                VARCHAR2(1) := 'N';
1445         --
1446     BEGIN
1447         msg('Adding supervisor ID correction changes');
1448         dbg('Start index is at row '||l_idx);
1449         dbg('Parameters are (not including output table):');
1450         dbg('p_start_date => '||fnd_date.date_to_canonical(p_start_date));
1451         dbg('p_end_date => '||fnd_date.date_to_canonical(p_end_date));
1452         --
1453         dbg('g_EVENT_GROUP_ID => '||g_EVENT_GROUP_ID);
1454         --
1455         -- Switch on location striping if desired
1456         IF p_location_stripe THEN
1457             dbg('Switching on location striping');
1458             l_stripe := 'Y';
1459         END IF;
1460         dbg('p_location_stripe => '||l_stripe);
1461         --
1462         -- Get all supervisor ID changes and add them to the list of refresh periods
1463         FOR l_rec IN csr_supv_corrections(
1464             g_EVENT_GROUP_ID,
1465             p_start_date,
1466             p_end_date,
1467             l_stripe
1468         ) LOOP
1469             add_summary_refresh_record(
1470                 l_idx,
1471                 p_summary_refresh,
1472                 l_rec.assignment_or_supervisor_id,
1473                 l_rec.effective_start_date,
1474                 l_rec.effective_end_date,
1475                 l_rec.table_or_location_id,
1476                 p_update_mode => FALSE
1477             );
1478         END LOOP;
1479         --
1483         dbg('End index is now at row '||l_end);
1480         -- Make sure we added some rows to the table, this isn't a fatal exception
1481         -- yet 'cos we could already have something in the table
1482         l_end := NVL(p_summary_refresh.LAST,0) + 1;
1484         IF l_start = l_end THEN
1485             RAISE no_supervisor_corrections;
1486         END IF;
1487         --
1488     END add_supervisor_corrections;
1489     --
1490     -- Get the list of supervisors and the date range across which
1491     -- each of those supervisors should be refreshed.
1492     PROCEDURE get_summaries_affected(
1493         p_event_group     IN     VARCHAR2,
1494         p_start_date      IN     DATE,
1495         p_end_date        IN     DATE,
1496         p_summary_refresh IN OUT NOCOPY t_summary_refresh_tab_type,
1497         p_location_stripe IN     BOOLEAN DEFAULT FALSE,
1498         p_raise_no_data   IN     BOOLEAN DEFAULT FALSE
1499     ) IS
1500         --
1501         -- A list of assignments that events have ocurred for
1502         l_assignments           t_assignment_id_tab_type;
1503         --
1504         -- Temporary table to store the results in, before we post-process it
1505         -- to handle "all supervisor refresh" events
1506         l_summary_refresh_temp  t_summary_refresh_tab_type;
1507         --
1508         -- Parameters used to process the "all supervisor refresh" events
1509         l_all_supv              BOOLEAN;
1510         l_out_num               NUMBER;
1511         l_all_start             DATE;
1512         l_all_end               DATE;
1513         --
1514         -- Profiling (timing) variables
1515         l_start                 NUMBER;
1516 	l_curr                  NUMBER;
1517         --
1518     BEGIN
1519         --
1520         dbg('Running get_summaries_affected, parameters;');
1521         dbg('p_event_group => '||p_event_group);
1522         dbg('p_start_date => '||p_start_date);
1523         dbg('p_end_date => '||p_end_date);
1524         IF p_location_stripe THEN
1525             dbg('p_location_stripe => TRUE');
1526         ELSE
1527             dbg('p_location_stripe => FALSE');
1528         END IF;
1529         IF p_raise_no_data THEN
1530             dbg('p_raise_no_data => TRUE');
1531         ELSE
1532             dbg('p_raise_no_data => FALSE');
1533         END IF;
1534         --
1535         -- Get the current time (100th's of a second)
1536         l_start := dbms_utility.get_time;
1537         g_SECONDS_ELAPSED := 0;
1538         --
1539         -- Clear out the results table, and the event group cache
1540         p_summary_refresh.DELETE;
1541 	g_DATED_TABLE_EXTRAS.DELETE;
1542 	g_EVENT_GROUP_ID := NULL;
1543 	--
1544 	-- Initialise the events group cache
1545 	-- 2984406: Moved to here, instead of on a per-assignment basis
1546 	init_event_group_cache(p_event_group);
1547         --
1548         BEGIN
1549             --
1550             -- Get all the assignment IDs for which events
1551             -- have occurred, but ignore supervisor ID changes
1552             get_assignments_affected(
1553                 p_start_date,
1554                 p_end_date,
1555                 l_assignments
1556             );
1557             --
1558             -- Process all the assignments we found
1559             get_refresh_periods(
1560                 l_assignments,
1561                 l_summary_refresh_temp,
1562                 p_start_date,
1563                 p_end_date,
1564                 p_location_stripe
1565             );
1566         EXCEPTION WHEN no_assignment_events_found THEN
1567             msg('No affected assignments were found in the refresh period');
1568         END;
1569         --
1570         BEGIN
1571             --
1572             -- Add the refresh periods for changes to the supervisor ID column
1573             add_supervisor_corrections(
1574                 l_summary_refresh_temp,
1575                 p_start_date,
1576                 p_end_date,
1577                 p_location_stripe
1578             );
1579         EXCEPTION WHEN no_supervisor_corrections THEN
1580             msg('No datetrack corrections to supervisor ID were found within refresh period');
1581         END;
1582         --
1583         -- Check that we've got something in the summary refresh table
1584         IF NVL(l_summary_refresh_temp.LAST,0) <= 0 THEN
1585             msg('No records in refresh table, nothing to do');
1586             dbg('Finished get_summaries_affected');
1587 	    --
1588 	    -- Record the time taken (to get nothing!)
1589 	    l_curr := dbms_utility.get_time;
1590             g_SECONDS_ELAPSED := (l_curr - l_start) / 100;
1591             msg(get_elapsed_time_text);
1592             --
1593 	    -- Clear the looping globals
1594 	    g_FIRST_RECORD := 0;
1595 	    g_LAST_RECORD := 0;
1596     	    g_CURRENT_RECORD := 0;
1597 	    --
1598             RETURN;
1599         END IF;
1600         --
1601         -- De-duplicate the records, what this means is that we remove any
1602         -- portions of refresh records for specific supervisors that overlap
1603         -- the "all" period. This period will always be contiguous, after the
1604         -- de-dupe process the specific supervisor records may not be.
1605         de_dupe_refresh_periods(
1606             l_summary_refresh_temp,
1607             p_summary_refresh,
1608             l_all_supv,
1609             l_out_num,
1610             l_all_start,
1611             l_all_end
1612         );
1613         --
1614         -- We've finished. Record some diagnostics trace information
1615         msg('Supervisor refresh events recorded: '||l_out_num);
1616         IF NOT l_all_supv THEN
1617             msg('There is no "refresh all" period');
1618         ELSE
1619             msg('Refresh all supervisors for: '||l_all_start||' -> '||l_all_end);
1620         END IF;
1621 	--
1622 	-- Record the time taken for the full run
1623 	l_curr := dbms_utility.get_time;
1624         g_SECONDS_ELAPSED := (l_curr - l_start) / 100;
1625         msg(get_elapsed_time_text);
1626         --
1627 	-- Initialise the globals we use for simplified record looping
1628 	g_FIRST_RECORD := NVL(p_summary_refresh.FIRST,0);
1629 	g_LAST_RECORD := NVL(p_summary_refresh.LAST,0);
1630 	g_CURRENT_RECORD := 0;
1631         --
1632         -- If we asked then raise no_data_found if there's no data in the table
1633         dbg('Finished get_summaries_affected');
1634         IF p_raise_no_data THEN
1635             IF NVL(p_summary_refresh.FIRST,0) <= 0 THEN
1636                 RAISE no_data_found;
1637             END IF;
1638         END IF;
1639     END get_summaries_affected;
1640     --
1641     FUNCTION next_record RETURN BOOLEAN IS
1642     BEGIN
1643         g_CURRENT_RECORD := g_CURRENT_RECORD + 1;
1644 	RETURN (g_CURRENT_RECORD >= g_FIRST_RECORD AND g_CURRENT_RECORD <= g_LAST_RECORD);
1645     END next_record;
1646     --
1647     FUNCTION current_record RETURN NUMBER IS
1648     BEGIN
1649         RETURN LEAST(g_CURRENT_RECORD,g_LAST_RECORD + 1);
1650     END current_record;
1651     --
1652 END pay_events_wrapper;