DBA Data[Home] [Help]

PACKAGE BODY: APPS.QA_PARENT_CHILD_COPY_PKG

Source


1 PACKAGE BODY QA_PARENT_CHILD_COPY_PKG as
2 /* $Header: qapccpb.pls 120.0.12020000.2 2012/07/03 14:36:21 ntungare ship $ */
3 
4    --this procedure copies the newest parent child relationship between source
5    --parent/child to a new relationship between target parent/child. if there
6    --are more than one relationship between the source plans it only takes the one with
7    --the most recent creation_date. it returns the old_plan_relationship_id,
8    --new plan_relationship_id as well as a return status
9    PROCEDURE COPY_PLAN_REL(p_source_parent_plan_id      IN  NUMBER,
10                            p_source_child_plan_id       IN  NUMBER,
11                            p_target_parent_plan_id      IN  NUMBER,
12                            p_target_child_plan_id       IN  NUMBER,
13                            x_old_plan_relationship_id   OUT NOCOPY NUMBER,
14                            x_new_plan_relationship_id   OUT NOCOPY NUMBER,
15                            x_return_status              OUT NOCOPY VARCHAR2)
16    IS
17       CURSOR C1  (parent_id NUMBER, child_id NUMBER) IS
18         SELECT   qpr.plan_relationship_id, qpr.data_entry_mode, qpr.auto_row_count,
19                  qpr.plan_relationship_type,
20                  qpr.default_parent_spec, qpr.layout_mode
21         FROM     qa_pc_plan_relationship qpr
22         WHERE    qpr.parent_plan_id = parent_id
23                  AND qpr.child_plan_id = child_id
24         ORDER BY qpr.creation_date DESC;
25       c1_rec C1%ROWTYPE;
26       x_row_id  VARCHAR2(1000);
27    BEGIN
28       --fetch the most recently created parent child relationship between source parent and source child
29       OPEN C1(p_source_parent_plan_id, p_source_child_plan_id);
30 
31       --make sure a relationship was found
32       FETCH C1 INTO c1_rec;
33       IF C1%NOTFOUND THEN
34          CLOSE C1;
35          x_old_plan_relationship_id := -1;
36          x_new_plan_relationship_id := -1;
37          RETURN;
38       ELSE
39          CLOSE C1;
40       END IF;
41 
42       --now perform the actual insert, use table handler instead of insert_plan_rel to avoid
43       --calls to qa_ak_mapping_api
44       /*
45       QA_SS_PARENT_CHILD_PKG.insert_plan_rel(
46         p_parent_plan_id         => p_target_parent_plan_id,
47         p_child_plan_id          => p_target_child_plan_id,
48         p_plan_relationship_type => c1_rec.plan_relationship_type,
49         p_data_entry_mode        => c1_rec.data_entry_mode,
50         p_auto_row_count         => c1_rec.auto_row_count,
51         p_default_parent_spec    => c1_rec.default_parent_spec,
52         x_plan_relationship_id   => x_new_plan_relationship_id);
53       */
54 
55       QA_PC_PLAN_REL_PKG.Insert_Row(
56                        X_Rowid                  => x_row_id,
57                        X_Plan_Relationship_Id   => x_new_plan_relationship_id,
58                        X_Parent_Plan_Id         => p_target_parent_plan_id,
59                        X_Child_Plan_id          => p_target_child_plan_id,
60                        X_Plan_Relationship_Type => c1_rec.plan_relationship_type,
61                        X_Data_Entry_Mode        => c1_rec.data_entry_mode,
62                        X_Layout_mode            => c1_rec.layout_mode,
63                        X_Auto_Row_Count         => c1_rec.auto_row_count,
64                        X_Default_Parent_Spec    => c1_rec.default_parent_spec,
65                        X_Last_Update_Date       => SYSDATE,
66                        X_Last_Updated_By        => fnd_global.user_id,
67                        X_Creation_Date          => SYSDATE,
68                        X_Created_By             => fnd_global.user_id,
69                        X_Last_Update_Login      => fnd_global.user_id);
70 
71       --return a value of success
72       x_old_plan_relationship_id := c1_rec.plan_relationship_id;
73       x_return_status := fnd_api.g_true;
74    END;
75 
76    --this procedure copies all element relationships pertaining to p_old_relationship_id
77    --to p_new_plan_relationship_id. returns a status value to indicate success/failure.
78    PROCEDURE COPY_ELEMENT_REL(
79                            p_old_plan_relationship_id   IN  NUMBER,
80                            p_new_plan_relationship_id   IN  NUMBER,
81                            x_return_status              OUT NOCOPY VARCHAR2
82                            )
83    IS
84       CURSOR C1 IS
85          SELECT parent_char_id, child_char_id, element_relationship_type,
86                 decode(link_flag,1,'Y','N') as vlink_flag
87          FROM   qa_pc_element_relationship
88          WHERE  plan_relationship_id = p_old_plan_relationship_id;
89 
90       l_new_element_relationship_id NUMBER;
91    BEGIN
92       --for each element relationship, insert an identical relationship with the
93       --new_plan_relationship_id also
94       FOR c1_rec IN C1 LOOP
95          QA_SS_PARENT_CHILD_PKG.insert_element_rel(
96           p_plan_relationship_id      => p_new_plan_relationship_id,
97           p_parent_char_id            => c1_rec.parent_char_id,
98           p_child_char_id             => c1_rec.child_char_id,
99           p_element_relationship_type => c1_rec.element_relationship_type,
100           p_link_flag                 => c1_rec.vlink_flag,
101           x_element_relationship_id   => l_new_element_relationship_id);
102       END LOOP;
103 
104       --return a value of success if we make it this far
105       x_return_status := fnd_api.g_true;
106    END;
107 
108    --this procedure copies all criteria pertaining to p_old_plan_relationship_id to
109    --p_new_plan_relationship_id.  returns a return_status value to indicate success/failure
110    PROCEDURE COPY_CRITERIA(
111                            p_old_plan_relationship_id   IN  NUMBER,
112                            p_new_plan_relationship_id   IN  NUMBER,
113                            x_return_status              OUT NOCOPY VARCHAR2
114                            )
115    IS
116       CURSOR C1 IS
117          SELECT char_id, operator, low_value, high_value
118          FROM   qa_pc_criteria
119          WHERE  plan_relationship_id = p_old_plan_relationship_id;
120 
121       l_new_criteria_id NUMBER;
122    BEGIN
123       --for each criteria, insert an identical one with the new_plan_relationship_id
124       FOR c1_rec IN C1 LOOP
125         QA_SS_PARENT_CHILD_PKG.insert_criteria_rel(
126           p_plan_relationship_id => p_new_plan_relationship_id,
127           p_char_id              => c1_rec.char_id,
128           p_operator             => c1_rec.operator,
129           p_low_value            => c1_rec.low_value,
130           p_high_value           => c1_rec.high_value,
131           x_criteria_id          => l_new_criteria_id);
132       END LOOP;
133 
134       --return a value of success if we make it this far
135       x_return_status := fnd_api.g_true;
136 
137    END;
138 
139    --this is a helper procedure that calls the UI mapping code for a given plan_id, x_return_status is
140    --fnd_api.g_true on success, fnd_api.g_false on failure
141    --this function assumes that fnd_global.apps_initialize has already been called
142 
143    -- anagarwa Tue Dec 24 11:44:52 PST 2002
144    -- Bug 2725466
145    -- The parameter plan_id is being replaced by p_plan_id to comply
146    -- with coding standards
147 
148    PROCEDURE map_ui_from_plan_id(p_plan_id IN NUMBER,
149                                  x_return_status OUT NOCOPY VARCHAR2)
150    IS
151       l_request_id NUMBER;
152    BEGIN
153       IF p_plan_id IS NOT NULL THEN
154 
155          l_request_id := fnd_request.submit_request(application => 'QA',
156                                                     program     => 'QLTSSCPB',
157                                                     argument1   => 'CREATE',
158                                                     argument2   => 'PLAN',
159                                                     argument3   => to_char(p_plan_id));
160         if( FND_LOG.LEVEL_STATEMENT >= FND_LOG.G_CURRENT_RUNTIME_LEVEL ) then
161            fnd_log.string(FND_LOG.LEVEL_STATEMENT,
162               'qapccpb.pls', 'copy_all, add ak generation request #'||l_request_id);
163            x_return_status := fnd_api.g_true;
164         end if;
165       ELSE
166          x_return_status := fnd_api.g_false;
167       END IF;
168    END;
169 
170    PROCEDURE COPY_ALL(p_source_parent_plan_id   IN  NUMBER,
171                       p_source_child_plan_id    IN  NUMBER,
172                       p_target_parent_plan_id   IN  NUMBER,
173                       p_target_child_plan_id    IN  NUMBER,
174                       p_call_mapping            IN  VARCHAR2,
175                       x_return_status           OUT NOCOPY VARCHAR2)
176    IS
177       l_old_plan_relationship_id NUMBER;
178       l_new_plan_relationship_id NUMBER;
179       l_return_status VARCHAR2(2);
180    BEGIN
181       --create a savepoint before copying any relationship info
182       SAVEPOINT copy_all_pub;
183 
184       --attempt to copy plan relationships first
185       copy_plan_rel(p_source_parent_plan_id,
186                     p_source_child_plan_id,
187                     p_target_parent_plan_id,
188                     p_target_child_plan_id,
189                     l_old_plan_relationship_id,
190                     l_new_plan_relationship_id,
191                     l_return_status);
192       IF l_return_status <> fnd_api.g_true OR l_old_plan_relationship_id = -1 THEN
193          x_return_status := fnd_api.g_false;
194          RETURN;
195       END IF;
196 
197       --attempt to copy element relationships next
198       copy_element_rel(l_old_plan_relationship_id,
199                        l_new_plan_relationship_id,
200                        l_return_status);
201       IF l_return_status <> fnd_api.g_true THEN
202          ROLLBACK TO copy_all_pub;
203          x_return_status := fnd_api.g_false;
204          RETURN;
205       END IF;
206 
207       --attempt to copy criteria lastly
208       copy_criteria(l_old_plan_relationship_id,
209                     l_new_plan_relationship_id,
210                     l_return_status);
211       IF l_return_status <> fnd_api.g_true THEN
212          ROLLBACK TO copy_all_pub;
213          x_return_status := fnd_api.g_false;
214          RETURN;
215       END IF;
216 
217       --see if we need to call the mapping functions
218       IF p_call_mapping = fnd_api.g_true THEN
219          --initialize the app for creating the UI mappings for these plans
220          fnd_global.apps_initialize(user_id      => fnd_global.user_id,
221                                     resp_id      => 20561,
222                                     resp_appl_id => 250);
223 
224 
225          map_ui_from_plan_id(p_target_parent_plan_id, x_return_status);
226          IF x_return_status <> fnd_api.g_true THEN
227             RETURN;
228          END IF;
229 
230          map_ui_from_plan_id(p_target_child_plan_id, x_return_status);
231          IF x_return_status <> fnd_api.g_true THEN
232             RETURN;
233          END IF;
234       END IF;
235 
236       --otherwise everything was successfull so return TRUE
237       x_return_status := fnd_api.g_true;
238    END;
239 
240    --this function gets the organization_code from an org id
241    FUNCTION get_org_code_from_orgid(p_orgid IN NUMBER)
242       RETURN VARCHAR2
243    IS
244       x_orgcode mtl_parameters.organization_code%TYPE;
245       CURSOR C IS
246          SELECT organization_code
247          FROM mtl_parameters
248          WHERE organization_id = p_orgid;
249    BEGIN
250       OPEN C;
251       FETCH C INTO x_orgcode;
252       IF C%NOTFOUND THEN
253          CLOSE C;
254          RETURN NULL;
255       ELSE
256          CLOSE C;
257          RETURN x_orgcode;
258       END IF;
259    END;
260 
261    --this function is copied from qltsswfb.plb to get the user_name from a user_id
262    FUNCTION get_user_name(u_id IN NUMBER) RETURN VARCHAR2 IS
263       u_name fnd_user.user_name%TYPE;
264       cursor c IS
265          SELECT DISTINCT fu.user_name FROM
266        fnd_user fu
267        WHERE
268        fu.user_id = u_id;
269    BEGIN
270       open c;
271       fetch c INTO u_name;
272       IF c%notfound THEN
273          close c;
274          RETURN NULL;
275       ELSE
276          close c;
277          RETURN u_name;
278       END IF;
279    END get_user_name;
280 
281    --get_plan_view and get_import_view are copied from qa_plans_pub to build the view names in the view_name and import_view_name
282    --for a given plan name
283    FUNCTION get_plan_view_name(p_name VARCHAR2) RETURN VARCHAR2 IS
284    BEGIN
285       return 'Q_' || translate(substr(p_name, 1, 26), ' ''', '__') || '_V';
286    END get_plan_view_name;
287 
288 
289    FUNCTION get_import_view_name(p_name VARCHAR2) RETURN VARCHAR2 IS
290    BEGIN
291       return 'Q_' || translate(substr(p_name, 1, 25), ' ''', '__') || '_IV';
292    END get_import_view_name;
293 
294    --this function copied from qltssreb.plb to parse first half of planid, plan_name
295    --token from a flat_string.  returns the planid as a number
296    FUNCTION parse_id(x_result IN VARCHAR2, n IN INTEGER,
297                      p IN INTEGER, q IN INTEGER)
298       RETURN NUMBER
299    IS
300    BEGIN
301       RETURN to_number(substr(x_result, p, q-p));
302    END parse_id;
303 
304 
305    --this function copied from qltssreb.plb to parse the plan_name from a plan_id,
306    --src_plan_name,dest_plan_name token in the flat_string.  returns the plan_name
307    --with all double-separator occurrences removed
308    FUNCTION parse_value(x_result IN VARCHAR2, n IN INTEGER,
309                         p IN OUT NOCOPY INTEGER)
310       RETURN plan_info
311    IS
312       new_info plan_info;
313       value qa_plans.name%TYPE;
314       c VARCHAR2(10);
315       separator CONSTANT VARCHAR2(1) := '@';
316       subseparator CONSTANT VARCHAR2(1) := ',';
317    BEGIN
318       --
319       -- Loop until a single @ is found or x_result is exhausted.
320       --
321       p := p + 1;                   -- add 1 before substr to skip '='
322       WHILE p <= n LOOP
323          c := substr(x_result, p, 1);
324          p := p + 1;
325          IF (c = subseparator) THEN
326             --we've completed the first name, store it in the new_info record
327             new_info.src_name := value;
328             value := '';
329          ELSIF (c = separator) THEN
330             -- take a peak at the next character, if not another @,
331             -- we have reached the end so finalize the record
332             -- Otherwise, skip this @
333             IF substr(x_result, p, 1) <> separator THEN
334                new_info.dest_name := value;
335                RETURN new_info;
336             ELSE
337                p := p + 1;
338             END IF;
339             value := value || c;
340          ELSE
341             value := value || c;
342          END IF;
343       END LOOP;
344 
345       --out of characters, save the dest_name and return the record
346       new_info.dest_name := value;
347       RETURN new_info;
348    END parse_value;
349 
350 
351    --this function is derived from result_to_array() in qltssreb.plb.
352    --it goes through the input flat_string, p_str, and parses planid=src_plan_name!dest_plan_name tokens
353    --and inserts them into the plans_htable.  it returns an integer return code:
354    --0 is success, -1 is failure
355    FUNCTION parse_flat_string(p_str IN VARCHAR2,
356                               p_replace IN BOOLEAN,
357                               x_htable IN OUT NOCOPY plan_htable)
358       RETURN INTEGER
359    IS
360       n INTEGER := length(p_str);
361       p INTEGER;            -- starting string position
362       q INTEGER;            -- ending string position
363       x_plan_id NUMBER;
364       new_info plan_info;
365    BEGIN
366       p := 1;
367       WHILE p < n LOOP
368          q := instr(p_str, '=', p);
369          --
370          -- found the first = sign.  To the left, must be char_id
371          --
372          x_plan_id := parse_id(p_str, n, p, q);
373          --
374          -- To the right, must be the value
375          --
376          new_info := parse_value(p_str, n, q);
377          IF ((x_htable.exists(x_plan_id) = false) OR
378              (x_htable.exists(x_plan_id) AND p_replace)) THEN
379             x_htable(x_plan_id) := new_info;
380          END IF;
381          p := q;
382       END LOOP;
383 
384       RETURN 0;
385    END parse_flat_string;
386 
387    --this procedure finds the search data in the repl_htable and returns the full replacement token
388    PROCEDURE execute_NCM_find_repl_text(p_search_data IN VARCHAR2,
389                                         p_delim IN VARCHAR2,
390                                         p_suffix_num IN NUMBER,
391                                         p_repl_htable IN ncm_repl_htable,
392                                         x_repl_data OUT NOCOPY VARCHAR2)
393    IS
394       repl_info ncm_repl_info;
395       i NUMBER;
396       j NUMBER;
397    BEGIN
398       --try to match the p_search_data to find a repl_str, default repl to p_search_data
399       x_repl_data := p_search_data;
400       i := p_repl_htable.FIRST;
401       WHILE (i IS NOT NULL) LOOP
402          repl_info := p_repl_htable(i);
403 
404          --check for match in all search strings
405          IF repl_info.search_str1 = p_search_data OR
406             repl_info.search_str2 = p_search_data OR
407             repl_info.search_str3 = p_search_data THEN
408             --if it's a match then use the p_suffix to figure out which repl string to use
409             IF p_suffix_num = 1 THEN
410                x_repl_data := repl_info.repl_str1;
411             ELSIF p_suffix_num = 2 THEN
412                x_repl_data := repl_info.repl_str2;
413             ELSIF p_suffix_num = 3 THEN
414                x_repl_data := repl_info.repl_str3;
415             END IF;
416 
417             --exit the loop looking for a replacement
418             EXIT;
419          END IF;
420 
421          i := p_repl_htable.NEXT(i);
422       END LOOP;
423 
424       --add delim info to replacement text
425       IF x_repl_data IS NOT NULL THEN
426          IF p_delim = ' ' THEN
427             x_repl_data := p_delim||x_repl_data;
428          ELSE
429             x_repl_data := p_delim||x_repl_data||p_delim;
430          END IF;
431       END IF;
432    END;
433 
434    --this procedure looks through the text for any of our special tokens and then uses the data before the token to do
435    --a smart replacement based on the contents of the p_repl_htable
436    PROCEDURE execute_NCM_repl_func(p_text IN OUT NOCOPY VARCHAR2,
437                                    p_repl_htable IN ncm_repl_htable,
438                                    p_ncm_suffix_list IN ncm_suffix_list_t)
439    IS
440       --initialize the suffix table
441       suffix VARCHAR2(30);
442       possible_delim VARCHAR2(1);
443       i NUMBER;
444       rev_search_pos NUMBER;
445       token_pos NUMBER;
446       delim_pos NUMBER;
447       match_count NUMBER := 1;
448       text_length NUMBER := length(p_text);
449       match_data_token VARCHAR2(32);
450       match_full_token VARCHAR2(80);
451       repl_data VARCHAR2(32);
452    BEGIN
453       FOR i IN p_ncm_suffix_list.FIRST..p_ncm_suffix_list.LAST LOOP
454          suffix := p_ncm_suffix_list(i);
455          token_pos := instr(p_text, suffix, 1, match_count);
456          WHILE token_pos <> 0 LOOP
457             --dbms_output.put_line('suffix: ('||p_ncm_suffix_list(i)||') found('||token_pos||')');
458             possible_delim := substr(p_text, token_pos-1, 1);
459             rev_search_pos := token_pos - text_length - 3;
460             IF (possible_delim <> SP_NCM_DELIM_CHAR1 AND possible_delim <> SP_NCM_DELIM_CHAR2) THEN
461                possible_delim := ' ';
462             END IF;
463             delim_pos := instr(p_text, possible_delim, rev_search_pos);
464             --dbms_output.put_line('possible delim: ('||possible_delim||') found('||delim_pos||')');
465 
466             --make sure we found the delim, otherwise skip this match
467             IF delim_pos <> 0 THEN
468                match_full_token := substr(p_text, delim_pos, token_pos - delim_pos + length(suffix));
469                --figure out the data token
470                IF possible_delim = ' ' THEN
471                   match_data_token := substr(p_text, delim_pos+1, token_pos - delim_pos - 1);
472                ELSE
473                   match_data_token := substr(p_text, delim_pos+1, token_pos - delim_pos - 2);
474                END IF;
475 
476                --dbms_output.put_line('match_data_token: ('||upper(match_data_token)||')');
477                --try to find the replacement text and do the replacement
478                execute_NCM_find_repl_text(upper(match_data_token), possible_delim, i, p_repl_htable, repl_data);
479                IF repl_data IS NOT NULL THEN
480                   p_text := replace(p_text, match_full_token, repl_data||suffix);
481                END IF;
482             END IF;
483             match_count := match_count + 1;
484             token_pos := instr(p_text, suffix, 1, match_count);
485          END LOOP;
486       END LOOP;
487    END;
488 
489    --this function is one of the helper functions to execute_spec_proc_requests.  It updates all
490    --the actions associated with the NCM project.  it returns either fnd_api.g_true or fnd_api.g_false
491    --in x_return_status and uses x_msg_data for any return code reason.
492    PROCEDURE execute_NCM_spec_proc_request(p_src_org_id         IN  VARCHAR2,
493                                            p_dest_org_code      IN  VARCHAR2,
494                                            p_phtable            IN  plan_htable,
495                                            x_msg_data           OUT NOCOPY VARCHAR2,
496                                            x_return_status      OUT NOCOPY VARCHAR2)
497    IS
498       i NUMBER;
499       suffix VARCHAR2(30);
500       l_src_plan_id NUMBER;
501       l_dest_plan_id NUMBER;
502 
503       repl_info ncm_repl_info;
504       repl_htable ncm_repl_htable;
505 
506       --this cursor selects all actions for a given plan_id
507       CURSOR C1 (p_plan_id NUMBER) IS
508          SELECT qpca.plan_char_action_id, qpca.alr_action_id, qpca.message
509          FROM qa_plans qp, qa_plan_chars qpc, qa_plan_char_action_triggers qpcat, qa_plan_char_actions qpca
510          WHERE qp.plan_id = qpc.plan_id AND qpc.char_id = qpcat.char_id AND qp.plan_id = qpcat.plan_id
511          AND qpcat.plan_char_action_trigger_id = qpca.plan_char_action_trigger_id AND qp.plan_id=p_plan_id;
512       l_message qa_plan_char_actions.message%TYPE;
513 
514       --this cursor get's the body field from alr_actions for a given action_id
515       CURSOR C2 (p_alract_id NUMBER) IS
516          SELECT body
517          FROM alr_actions
518          WHERE action_id = p_alract_id AND application_id = 250;
519       l_body alr_actions.body%TYPE;
520 
521       --initialize the list of suffixes
522       ncm_suffix_list ncm_suffix_list_t := ncm_suffix_list_t(SP_NCM_PLAN_NAME_SUFFIX, SP_NCM_VIEW_NAME_SUFFIX, SP_NCM_IMPORT_NAME_SUFFIX);
523    BEGIN
524       --as a preprocessing step, go through each plan and make the search/replacement names and put them in
525       --the repl_htable
526       l_src_plan_id := p_phtable.FIRST;
527       WHILE (l_src_plan_id IS NOT NULL) LOOP
528          repl_info.search_str1 := upper(p_phtable(l_src_plan_id).src_name);
529          repl_info.repl_str1   := upper(p_phtable(l_src_plan_id).dest_name);
530          repl_info.search_str2 := get_plan_view_name(p_phtable(l_src_plan_id).src_name);
531          repl_info.repl_str2   := get_plan_view_name(p_phtable(l_src_plan_id).dest_name);
532          repl_info.search_str3 := get_import_view_name(p_phtable(l_src_plan_id).src_name);
533          repl_info.repl_str3   := get_import_view_name(p_phtable(l_src_plan_id).dest_name);
534 
535          repl_htable(l_src_plan_id) := repl_info;
536          l_src_plan_id := p_phtable.NEXT(l_src_plan_id);
537       END LOOP;
538 
539       --for each target plan, look up every action associated with it
540       l_src_plan_id := p_phtable.FIRST;
541       WHILE (l_src_plan_id IS NOT NULL) LOOP
542          l_dest_plan_id := p_phtable(l_src_plan_id).dest_id;
543          --dbms_output.put_line('src planid: "'||l_src_plan_id||'", dest planid: "'||l_dest_plan_id||'"');
544 
545          --get every plan_char for this target plan
546          FOR c1_rec IN C1(l_dest_plan_id) LOOP
547             l_message := c1_rec.message;
548 
549             --check if the action has an alr_action_id, if so we need to get the action from the alr_alerts table
550             IF c1_rec.alr_action_id IS NOT NULL THEN
551                FOR c2_rec IN C2(c1_rec.alr_action_id) LOOP
552                   l_body := c2_rec.body;
553 
554                   --now we do the replacement function on l_body
555                   execute_NCM_repl_func(l_body, repl_htable, ncm_suffix_list);
556 
557                   --and update the ALR_ACTIONS table
558                   UPDATE ALR_ACTIONS SET BODY = l_body WHERE APPLICATION_ID=250 AND ACTION_ID = c1_rec.alr_action_id;
559                END LOOP;
560             END IF;
561 
562             --dbms_output.put_line('begin actid '||c1_rec.plan_char_action_id);
563             --make sure the message has something in it
564             IF l_message IS NOT NULL THEN
565                --now check the message field
566                execute_NCM_repl_func(l_message, repl_htable, ncm_suffix_list);
567 
568                --and update the QA_PLAN_CHAR_ACTIONS table
569                UPDATE QA_PLAN_CHAR_ACTIONS SET MESSAGE = l_message WHERE PLAN_CHAR_ACTION_ID = c1_rec.plan_char_action_id;
570             END IF;
571             --dbms_output.put_line('done actid '||c1_rec.plan_char_action_id);
572          END LOOP;
573          l_src_plan_id := p_phtable.NEXT(l_src_plan_id);
574       END LOOP;
575 
576       --return that we succceeded in action processing
577       x_return_status := fnd_api.g_true;
578    EXCEPTION
579       WHEN OTHERS THEN
580          x_msg_data := 'Special Processing Request "'||SP_NCM||'" encountered an error: Code('||SQLCODE||'), Message("'||SQLERRM||'")';
581          x_return_status := fnd_api.g_false;
582          RETURN;
583    END;
584 
585    --this function takes the p_src_org_id, p_dest_org_code, and p_phtable representing
586    --the source and duplicate schema information and applies all special processing requests in
587    --the p_special_proc_field string and returns a success/failure indicator in x_return_status and an
588    --optional failure message in x_msg_data
589    --p_special_proc_field is of the form <token1>@<token2>@...
590    --x_return_status = fnd_api.g_true on success, fnd_api.g_false on failure
591    PROCEDURE execute_spec_proc_requests(p_src_org_id            IN  VARCHAR2,
592                                         p_dest_org_code         IN  VARCHAR2,
593                                         p_phtable               IN  plan_htable,
594                                         p_special_proc_field    IN  VARCHAR2,
595                                         x_msg_data              OUT NOCOPY VARCHAR2,
596                                         x_return_status         OUT NOCOPY VARCHAR2)
597    IS
598       n INTEGER := length(p_special_proc_field);
599       p INTEGER;            -- starting string position
600       q INTEGER;            -- ending string position
601       separator CONSTANT VARCHAR2(1) := '@';
602       token VARCHAR2(200);
603    BEGIN
604       --to allow for multiple, ordered special processing requests on a schema, parse the
605       --p_special_proc_field for the separator
606       p := 1;
607       WHILE p < n LOOP
608          q := instr(p_special_proc_field, separator, p);
609 
610          --if we didn't find the separator, grab the rest of the string
611          IF (q = 0) THEN
612             token := substr(p_special_proc_field, p);
613             p := n;
614          ELSE
615             token := substr(p_special_proc_field, p, (q-p));
616             p := q + 1;
617          END IF;
618 
619          IF (token = SP_NCM) THEN
620             execute_NCM_spec_proc_request(p_src_org_id    => p_src_org_id,
621                                           p_dest_org_code =>p_dest_org_code,
622                                           p_phtable       => p_phtable,
623                                           x_msg_data      => x_msg_data,
624                                           x_return_status => x_return_status);
625          ELSE
626             x_return_status := fnd_api.g_false;
627             x_msg_data := 'Error while executing special processing requests!  Special Processing Request Identifier "'||token||'" invalid.';
628             RETURN;
629          END IF;
630 
631          --see if the special processing function returned ok, if not return
632          IF x_return_status <> fnd_api.g_true THEN
633             RETURN;
634          END IF;
635 
636       END LOOP;
637 
638       --x_msg_data := '';
639       x_return_status := fnd_api.g_true;
640    END;
641 
642    --this function is used by setup_plans to make sure that there isn't a duplicate plan name.
643    --instead of checking the full name, however, it checks the first 25 characters since the view names
644    --generated from the plan name require the first 25 characters be unique
645    --returns 0 on success, -1 on failure
646    FUNCTION check_plan_name(p_plan_name IN VARCHAR2)
647                             RETURN INTEGER
648    IS
649       CURSOR C1 IS
650          -- anagarwa Tue Dec 24 11:44:52 PST 2002
651          -- Bug 2725466
652          -- Changing SYS.DUAL to DUAL as this is the correct coding standard
653          -- The code may fail in databases that do not have SYS schema
654          SELECT 1 FROM DUAL
655          WHERE NOT EXISTS
656             (SELECT 1 FROM qa_plans
657              WHERE translate(substr(upper(name),1,25),' ''','__') =
658              translate(substr(upper(p_plan_name),1,25),' ''','__'));
659 
660       C1_rec C1%ROWTYPE;
661    BEGIN
662       --dbms_output.put_line('entered check_plan_name with name: '||p_plan_name||', mauled: '||translate(substr(upper(p_plan_name), 1, 25), ' ''', '__'));
663       OPEN C1;
664       FETCH C1 INTO C1_rec;
665       IF C1%NOTFOUND THEN
666          --dbms_output.put_line('got in the good case');
667          CLOSE C1;
668          RETURN -1;
669       ELSE
670          --dbms_output.put_line('got in the else case');
671          CLOSE C1;
672          RETURN 0;
673       END IF;
674    END;
675 
676    --To handle capturing an eSignature for each newly created plan, we have a parameter that
677    --forces all new plans to be created as disabled.  This is implemented by moving the
678    --Effective To and Effective From dates to something which makes sense.
679    --optional failure message in x_msg_data
680    --x_return_status = fnd_api.g_true on success, fnd_api.g_false on failure
681    PROCEDURE disable_plans(p_phtable            IN  plan_htable,
682                            x_msg_data           OUT NOCOPY VARCHAR2,
683                            x_return_status      OUT NOCOPY VARCHAR2)
684    IS
685       i INTEGER;
686       j INTEGER;
687       n INTEGER := p_phtable.COUNT;
688       c1 NUMBER;
689       c2 NUMBER;
690       fetch_count NUMBER;
691       ignore NUMBER;
692       indx NUMBER := 1;
693 
694       id_table          dbms_sql.Number_Table;
695       from_table        dbms_sql.Date_Table;
696       to_table          dbms_sql.Date_Table;
697       select_string     VARCHAR2(4000);
698       update_string     VARCHAR2(4000);
699    BEGIN
700       --sanity check
701       IF p_phtable.COUNT < 1 THEN
702          x_msg_data := '';
703          x_return_status := fnd_api.g_true;
704          RETURN;
705       END IF;
706 
707       --init the sql string
708       select_string := 'select plan_id, effective_from, effective_to from qa_plans where plan_id in (';
709       FOR i in 1..n LOOP
710          IF i <> n THEN
711             select_string := select_string || ':' || to_char(i) || ', ';
712          ELSE
713             select_string := select_string || ':' || to_char(i) || ')';
714          END IF;
715       END LOOP;
716 
717       --use dbms_sql to execute the sql
718       c1 := dbms_sql.open_cursor;
719       dbms_sql.parse(c1, select_string, dbms_sql.native);
720 
721       --bind using the dest plan_ids from the phtable
722       i := p_phtable.FIRST;
723       j := 1;
724       WHILE (i IS NOT NULL) LOOP
725          dbms_sql.bind_variable(c1, ':' || to_char(j), p_phtable(i).dest_id);
726          i := p_phtable.NEXT(i);
727          j := j + 1;
728       END LOOP;
729 
730       --set up the bulk collect arrays
731       dbms_sql.define_array(c1, 1, id_table, 20, indx);
732       dbms_sql.define_array(c1, 2, from_table, 20, indx);
733       dbms_sql.define_array(c1, 3, to_table, 20, indx);
734 
735       --execute the sql and collect the results
736       ignore := dbms_sql.execute(c1);
737       loop
738          fetch_count := dbms_sql.fetch_rows(c1);
739 
740          dbms_sql.column_value(c1, 1, id_table);
741          dbms_sql.column_value(c1, 2, from_table);
742          dbms_sql.column_value(c1, 3, to_table);
743 
744          exit when fetch_count <> 20;
745       end loop;
746       dbms_sql.close_cursor(c1);
747 
748       --make sure we got everything
749       IF id_table.COUNT <> n THEN
750          x_msg_data := 'Invalid fetch count('||id_table.COUNT||'), expecting('||n||').';
751          x_return_status := fnd_api.g_false;
752          RETURN;
753       END IF;
754 
755       --now go through each plan_id and modify the dates
756       FOR j in 1..n LOOP
757          IF to_table(j) IS NULL OR to_table(j) >= TRUNC(SYSDATE) THEN
758             to_table(j) := TRUNC(SYSDATE) - 1;
759             --move the from back if it's now later than the to
760             IF from_table(j) > to_table(j) THEN
761                from_table(j) := to_table(j) - 1;
762             END IF;
763          END IF;
764       END LOOP;
765 
766       --now perform the bulk DML update
767       update_string := 'UPDATE QA_PLANS SET effective_from = :1, effective_to = :2 WHERE plan_id = :3';
768       c2 := dbms_sql.open_cursor;
769       dbms_sql.parse(c2, update_string, dbms_sql.native);
770       dbms_sql.bind_array(c2, ':1', from_table);
771       dbms_sql.bind_array(c2, ':2', to_table);
772       dbms_sql.bind_array(c2, ':3', id_table);
773       ignore := dbms_sql.execute(c2);
774       dbms_sql.close_cursor(c2);
775 
776       --return success
777       x_msg_data := '';
778       x_return_status := fnd_api.g_true;
779 
780    exception when others then
781       if dbms_sql.is_open(c1) then
782          dbms_sql.close_cursor(c1);
783       end if;
784       if dbms_sql.is_open(c2) then
785          dbms_sql.close_cursor(c2);
786       end if;
787       x_msg_data := 'unexpected error: Code('||SQLCODE||'), Message("'||SQLERRM||'")';
788       x_return_status := fnd_api.g_false;
789       RETURN;
790    END;
791 
792    FUNCTION SETUP_PLANS(p_src_org_id            IN  VARCHAR2,
793                         p_dest_org_code         IN  VARCHAR2,
794                         p_plans_flatstring      IN  VARCHAR2,
795                         p_root_plan_src_id      IN  VARCHAR2,
796                         x_root_plan_dest_id     OUT NOCOPY NUMBER,
797                         p_special_proc_field    IN  VARCHAR2,
798                         p_disable_plans         IN  VARCHAR2,
799                         x_return_msg            OUT NOCOPY VARCHAR2,
800                         x_index_drop_list       OUT NOCOPY VARCHAR2)
801                         RETURN INTEGER
802    IS
803       phtable plan_htable;
804       this_plan plan_info;
805       src_org_code org_organization_definitions.organization_code%TYPE;
806       src_org_id NUMBER;
807       ret_code INTEGER;
808       row_count INTEGER := 0;
809       src_id NUMBER;
810       dest_id NUMBER;
811       child_id NUMBER;
812       l_request_id NUMBER;
813 
814       user_name fnd_user.user_name%TYPE := get_user_name(fnd_global.user_id);
815       --user_name fnd_user.user_name%TYPE := 'MFG';
816       CURSOR C1(parent_id NUMBER) IS
817          SELECT child_plan_id
818          FROM qa_pc_plan_relationship
819          WHERE parent_plan_id = parent_id;
820 
821    -- Bug 3926150. Performance: searching on softcoded element improved by functional indexes.
822    -- Added the cursor qa_pc, which returns char details of enabled functional indexes.
823    -- Please see bugdb/design document for more details.
824    -- srhariha. Tue Nov 30 11:59:20 PST 2004
825 
826        CURSOR qa_pc(p_plan_id NUMBER) IS
827           SELECT qpc.char_id,qpc.result_column_name,qc.name
828           FROM qa_plan_chars qpc,qa_chars qc, qa_char_indexes qci
829           WHERE qpc.plan_id = p_plan_id
830           AND qpc.char_id = qc.char_id
831           AND qpc.char_id = qci.char_id
832           AND qci.enabled_flag = 1;
833           -- AND qa_char_indexes_pkg.index_exists_and_enabled(qc.char_id) = 1;
834       --local variables to facilitate certain function calls
835       l_return_status                   VARCHAR2(2);
836       l_msg_count                       NUMBER;
837       l_msg_data                        VARCHAR2(2000);
838       l_msg                             VARCHAR2(2000);
839       l_slog                            boolean         := FND_LOG.LEVEL_STATEMENT >= FND_LOG.G_CURRENT_RUNTIME_LEVEL;
840       l_plog                            boolean         := FND_LOG.LEVEL_PROCEDURE >= FND_LOG.G_CURRENT_RUNTIME_LEVEL;
841 
842       -- Bug 3926150. Performance: searching on softcoded element improved by functional indexes.
843       -- Added two local variables.
844       -- Please see bugdb/design document for more details.
845       -- srhariha. Tue Nov 30 11:59:20 PST 2004
846 
847 
848       -- total message size can be 1260.
849       char_id_list                      VARCHAR2(2000);
850       l_temp  NUMBER;
851    BEGIN
852       if FND_LOG.LEVEL_PROCEDURE >= FND_LOG.G_CURRENT_RUNTIME_LEVEL then
853          fnd_log.string(FND_LOG.LEVEL_PROCEDURE, 'qapccpb.pls', 'entered setup_plans');
854       end if;
855       --fnd_log.string(FND_LOG.LEVEL_UNEXPECTED, 'qapccpb.pls', 'entered setup_plans');
856 
857       --reinitialize the message list for this attempt at duplication
858       fnd_msg_pub.initialize;
859 
860       --parse the source_plan_id, dest_plan_name tokens from the flat_string into
861       --a name hash table
862       ret_code := parse_flat_string(p_plans_flatstring, false, phtable);
863       IF (ret_code < 0) THEN
864          x_return_msg := 'setup_plans: failed to parse the provided plans_flatstring.';
865          RETURN -1;
866       END IF;
867 
868       --lookup the org code for this org id
869       src_org_id := to_number(p_src_org_id);
870       src_org_code := get_org_code_from_orgid(src_org_id);
871 
872       --make a savepoint before doing the batch of plan creations
873       SAVEPOINT setup_plans_pub;
874 
875       --first, try to duplicate each involved plan and place the destination's plan id in
876       --the htable
877       src_id := phtable.FIRST;
878       WHILE (src_id IS NOT NULL) LOOP
879          --dbms_output.put_line('row'||row_count||': "'||src_id||'", "'||phtable(src_id).src_name||'", "'||phtable(src_id).dest_name||'"');
880 
881          --get a reference to this plan's info
882          this_plan := phtable(src_id);
883 
884          --even though copy_collection_plan checks the destination name to make sure it's
885          --unique, it doesn't tell us that it didn't copy the plan so we have to explicitly
886          --check here first and make sure that we check only the first 25 chars
887          ret_code := check_plan_name(this_plan.dest_name);
888          IF (ret_code < 0) THEN
889             ROLLBACK TO setup_plans_pub;
890             fnd_message.set_name('QA', 'QA_PC_COPY_DUPLICATE_NAME');
891             fnd_message.set_token('DESTPLANNAME', this_plan.dest_name);
892             fnd_msg_pub.add();
893             x_return_msg := 'dupilicate dest name:'||this_plan.dest_name||', retcode: '||ret_code;
894             RETURN -1;
895          END IF;
896 
897          --call the copy_collection_plan procedure
898          qa_plans_pub.copy_collection_plan(
899            p_api_version          => 1.0,
900            p_user_name            => user_name,
901            p_plan_name            => this_plan.src_name,
902            p_organization_code    => src_org_code,
903            p_to_plan_name         => this_plan.dest_name,
904            p_to_organization_code => p_dest_org_code,
905            x_to_plan_id           => phtable(src_id).dest_id,
906            x_msg_count            => l_msg_count,
907            x_msg_data             => l_msg_data,
908            x_return_status        => l_return_status);
909          IF l_return_status <> fnd_api.g_ret_sts_success THEN
910             ROLLBACK TO setup_plans_pub;
911             x_return_msg := 'setup_plans,copy collection_plan: src_plan('||
912                phtable(src_id).src_name||'), dest_plan('||phtable(src_id).dest_name||
913                ') returned with an error.';
914             RETURN -1;
915          END IF;
916 
917          src_id := phtable.NEXT(src_id);
918          row_count := row_count + 1;
919       END LOOP;
920 
921       --now that all the duplicate plans exist, we go through each plan and check if the child is
922       --created and if so copy the link
923       src_id := phtable.FIRST;
924       row_count := 0;
925       WHILE (src_id IS NOT NULL) LOOP
926          FOR C1_rec IN C1(src_id) LOOP
927             child_id := C1_rec.child_plan_id;
928             --see if we duplicated this child in the last loop
929             IF (phtable.EXISTS(child_id)) THEN
930                --child plan was copied so perform the copy_all between this parent and child with no mapping
931                copy_all(p_source_parent_plan_id => src_id,
932                                    p_source_child_plan_id  => child_id,
933                                    p_target_parent_plan_id => phtable(src_id).dest_id,
934                                    p_target_child_plan_id  => phtable(child_id).dest_id,
935                                    p_call_mapping          => fnd_api.g_false,
936                                    x_return_status         => l_return_status);
937                IF l_return_status <> fnd_api.g_true THEN
938                   x_return_msg := 'setup_plans, copy_all: src_parent_id('||src_id||
939                      '),src_child_id('||child_id||'), dest_parent_id('||
940                      phtable(src_id).dest_id||'), dest_child_id('||phtable(child_id).dest_id||
941                      ') failed.';
942                   ROLLBACK TO setup_plans_pub;
943                   RETURN -1;
944                END IF;
945             END IF;
946          END LOOP;
947          src_id := phtable.NEXT(src_id);
948          row_count := row_count + 1;
949       END LOOP;
950 
951 
952    -- Bug 3926150. Performance: searching on softcoded element improved by functional indexes.
953    -- Compiling the list of char elements whose functional index must be dropped.
954    -- Please see bugdb/design document for more details.
955    -- srhariha. Tue Nov 30 11:59:20 PST 2004
956 
957 
958 
959       src_id := phtable.FIRST;
960       row_count := 0;
961       char_id_list := null;
962 
963       WHILE (src_id IS NOT NULL) LOOP
964          FOR qa_pc_rec in qa_pc(phtable(src_id).dest_id) LOOP -- fetch all indexed softcoded chars
965 
966             if qa_pc_rec.result_column_name =
967                                   qa_char_indexes_pkg.get_default_result_column(qa_pc_rec.char_id) then
968                 null; -- no need to drop because it is already the best fit.
969             else
970                l_temp := qa_char_indexes_pkg.disable_index(qa_pc_rec.char_id); -- disable the index
971 
972                 -- To restrict message length to 1260. (60 is an offset for message content)
973                 if((nvl(length(char_id_list),0) + nvl(length(qa_pc_rec.name),0)) < 1200) then
974                   char_id_list := char_id_list || ', ' || qa_pc_rec.name;
975                 end if;
976             end if;
977         END LOOP;
978 
979          src_id := phtable.NEXT(src_id);
980          row_count := row_count + 1;
981       END LOOP;
982 
983 
984       --at this point, we've fully duplicated the schema. now check if any special processing needs to be done
985       execute_spec_proc_requests(p_src_org_id          => p_src_org_id,
986                                  p_dest_org_code       => p_dest_org_code,
987                                  p_phtable             => phtable,
988                                  p_special_proc_field  => p_special_proc_field,
989                                  x_msg_data            => l_msg_data,
990                                  x_return_status       => l_return_status);
991       IF l_return_status <> fnd_api.g_true THEN
992          ROLLBACK TO setup_plans_pub;
993          x_return_msg := 'setup_plans,execute special processing requests: returned "'||l_msg_data||'"';
994          RETURN -1;
995       END IF;
996 
997       --here we put in the call to either create the dynamic views/UI mapping regions or to
998       --change the effective from/to dates if p_disable_plans is enabled
999       IF p_disable_plans IS NOT NULL and p_disable_plans = 'Y' THEN
1000          disable_plans(p_phtable        => phtable,
1001                        x_msg_data       => l_msg_data,
1002                        x_return_status  => l_return_status);
1003          IF l_return_status <> fnd_api.g_true THEN
1004             ROLLBACK TO setup_plans_pub;
1005             x_return_msg := 'setup_plans,execute disable_plans: returned "'||l_msg_data||'"';
1006             RETURN -1;
1007          END IF;
1008       ELSE
1009          --initialize the app for creating dynamic views
1010          fnd_global.apps_initialize(user_id      => fnd_global.user_id,
1011                                     resp_id      => 20561,
1012                                     resp_appl_id => 250);
1013 
1014          src_id := phtable.FIRST;
1015          WHILE (src_id IS NOT NULL) LOOP
1016             --get a reference to this plan's info
1017             this_plan := phtable(src_id);
1018 
1019             l_request_id := fnd_request.submit_request(application => 'QA',
1020                                                        program     => 'QLTPVWWB',
1021                                                        argument1   => get_plan_view_name(this_plan.dest_name),
1022                                                        argument2   => NULL,
1023                                                        argument3   => to_char(this_plan.dest_id),
1024                                                        argument4   => get_import_view_name(this_plan.dest_name),
1025                                                        argument5   => NULL,
1026                                                        argument6   => NULL);
1027             if FND_LOG.LEVEL_STATEMENT >= FND_LOG.G_CURRENT_RUNTIME_LEVEL then
1028               fnd_log.string(FND_LOG.LEVEL_STATEMENT, 'qapccpb.pls', 'add view generation request #'||l_request_id);
1029             end if;
1030 
1031             l_request_id := fnd_request.submit_request(application => 'QA',
1032                                                        program     => 'QLTSSCPB',
1033                                                        argument1   => 'CREATE',
1034                                                        argument2   => 'PLAN',
1035                                                        argument3   => to_char(this_plan.dest_id));
1036             if FND_LOG.LEVEL_STATEMENT >= FND_LOG.G_CURRENT_RUNTIME_LEVEL then
1037               fnd_log.string(FND_LOG.LEVEL_STATEMENT, 'qapccpb.pls', 'add ui generation request #'||l_request_id);
1038             end if;
1039             src_id := phtable.NEXT(src_id);
1040          END LOOP;
1041       END IF;
1042 
1043       --make one final call to regen the global view
1044       l_request_id := fnd_request.submit_request(application => 'QA',
1045                                                  program     => 'QLTPVWWB',
1046                                                  argument1   => NULL,
1047                                                  argument2   => NULL,
1048                                                  argument3   => NULL,
1049                                                  argument4   => NULL,
1050                                                  argument5   => NULL,
1051                                                  argument6   => 'QA_GLOBAL_RESULTS_V');
1052       if FND_LOG.LEVEL_STATEMENT >= FND_LOG.G_CURRENT_RUNTIME_LEVEL then
1053         fnd_log.string(FND_LOG.LEVEL_STATEMENT, 'qapccpb.pls', 'add global view generation request #'||l_request_id);
1054       end if;
1055 
1056       ret_code := 0;
1057       --x_return_msg := '';
1058       x_return_msg := l_msg_data;
1059       IF (p_root_plan_src_id IS NULL OR p_root_plan_src_id = '' OR p_root_plan_src_id = '-1') THEN
1060          x_root_plan_dest_id := 0;
1061       ELSE
1062          x_root_plan_dest_id := phtable(to_number(p_root_plan_src_id)).dest_id;
1063       END IF;
1064 
1065       -- Bug 3926150. Performance: searching on softcoded element improved by functional indexes.
1066       -- Put list into OUT parameter x_index_drop_list after stripping initial comma.
1067       -- Please see bugdb/design document for more details.
1068       -- srhariha. Tue Nov 30 11:59:20 PST 2004
1069 
1070       if(char_id_list is not null) then
1071          x_index_drop_list := substr(char_id_list,2);
1072       end if;
1073 
1074       RETURN ret_code;
1075 
1076    EXCEPTION
1077       WHEN OTHERS THEN
1078          ROLLBACK TO setup_plans_pub;
1079          x_return_msg := 'setup_plans, unhandled exception:: Code('||SQLCODE||'), Message("'||SQLERRM||'")';
1080          --x_return_msg := 'setup_plans, unhandled exception occurred.';
1081          RETURN -1;
1082    END;
1083 
1084 END QA_PARENT_CHILD_COPY_PKG;