DBA Data[Home] [Help]

PACKAGE BODY: APPS.WF_PROCESS_ACTIVITY

Source


1 package body WF_PROCESS_ACTIVITY as
2 /* $Header: wfengb.pls 120.38.12020000.3 2012/11/13 19:42:45 alsosa ship $ */
3 
4 type InstanceArrayTyp is table of pls_integer
5 index by binary_integer;
6 
7 --
8 -- RootInstanceId
9 --   Globals to cache RootInstanceId result for effeciency
10 --
11 c_itemtype varchar2(8);
12 c_itemkey varchar2(240);
13 c_process varchar2(30);
14 c_rootid pls_integer;
15 
16 --
17 -- ClearCache
18 --   Clear runtime cache
19 --
20 procedure ClearCache
21 is
22 begin
23   wf_process_activity.c_itemtype := '';
24   wf_process_activity.c_itemkey := '';
25   wf_process_activity.c_process := '';
26   wf_process_activity.c_rootid := '';
27 exception
28   when others then
29     Wf_Core.Context('Wf_Process_Activity', 'ClearCache');
30     raise;
31 end ClearCache;
32 
33 --
34 -- RootInstanceId (PRIVATE)
35 --   Return the instance id for the process activity under the given item
36 --   type.  If there is no row found, returns null.
37 -- NOTE
38 --   This function only returns the 'ROOT' row for a process in
39 --   WF_PROCESS_ACTIVITIES.  It assumes there will be exactly 1 row
40 --   looking like:
41 --     PROCESS_ITEM_TYPE = itemtype
42 --     PROCESS_NAME = 'ROOT'
43 --     PROCESS_VERSION = version
44 --     INSTANCE_LABEL = process
45 --   for each process in WF_PROCESS_ACTIVITIES.
46 -- IN
47 --   itemtype - Item type of process
48 --   itemkey - Item key
49 --   process - Process name
50 --
51 function RootInstanceId(itemtype in varchar2,
52                         itemkey in varchar2,
53                         process  in varchar2)
54 return number is
55   actdate date;
56   instid pls_integer;
57 begin
58   -- Check cache for a valid value
59   if ((itemtype = wf_process_activity.c_itemtype) and
60       (itemkey = wf_process_activity.c_itemkey) and
61       (process = wf_process_activity.c_process)) then
62     return(wf_process_activity.c_rootid);
63   end if;
64 
65   -- No joy.  Select a new value.
66   actdate := Wf_Item.Active_Date(itemtype, itemkey);
67 
68   select INSTANCE_ID
69   into instid
70   from WF_PROCESS_ACTIVITIES PA, WF_ACTIVITIES A
71   where A.ITEM_TYPE = itemtype
72   and A.NAME = 'ROOT'
73   and actdate >= A.BEGIN_DATE
74   and actdate < NVL(A.END_DATE, actdate+1)
75   and PA.PROCESS_NAME = 'ROOT'
76   and PA.PROCESS_ITEM_TYPE = itemtype
77   and PA.PROCESS_VERSION = A.VERSION
78   and PA.INSTANCE_LABEL = process;
79 
80   -- Save value to cache
81   wf_process_activity.c_itemtype := itemtype;
82   wf_process_activity.c_itemkey := itemkey;
83   wf_process_activity.c_process := process;
84   wf_process_activity.c_rootid := instid;
85 
86   return instid;
87 
88 exception
89   when NO_DATA_FOUND then
90     return '';
91   when OTHERS then
92     Wf_Core.Context('Wf_Process_Activity', 'RootInstanceId', itemtype,
93                     itemkey, process);
94     raise;
95 end RootInstanceId;
96 
97 --
98 -- ActivityName
99 --   Return the activity type and name, given instance id
100 -- IN
101 --   actid - instance id
102 -- OUT
103 --   act_itemtype - activity itemtype
104 --   act_name - activity name
105 --
106 procedure ActivityName(
107   actid in number,
108   act_itemtype out NOCOPY varchar2,
109   act_name out NOCOPY varchar2)
110 is
111   status  PLS_INTEGER;
112 
113 begin
114   WF_CACHE.GetProcessActivity(actid, status);
115 
116   if (status <> WF_CACHE.task_SUCCESS) then
117 
118     select WPA.PROCESS_ITEM_TYPE, WPA.PROCESS_NAME, WPA.PROCESS_VERSION,
119            WPA.ACTIVITY_ITEM_TYPE, WPA.ACTIVITY_NAME, WPA.INSTANCE_ID,
120            WPA.INSTANCE_LABEL, WPA.PERFORM_ROLE, WPA.PERFORM_ROLE_TYPE,
121            WPA.START_END, WPA.DEFAULT_RESULT
122     into   WF_CACHE.ProcessActivities(actid)
123     from   WF_PROCESS_ACTIVITIES WPA
124     where  WPA.INSTANCE_ID = actid;
125 
126   end if;
127 
128   act_itemtype := WF_CACHE.ProcessActivities(actid).ACTIVITY_ITEM_TYPE;
129   act_name     := WF_CACHE.ProcessActivities(actid).ACTIVITY_NAME;
130 
131 exception
132   when no_data_found then
133     Wf_Core.Token('ACTID', to_char(actid));
134     Wf_Core.Token('DATE', '');
135     Wf_Core.Raise('WFENG_ACTID');
136   when others then
137     Wf_Core.Context('Wf_Process_Activity', 'ActivityName', to_char(actid));
138     raise;
139 end ActivityName;
140 
141 --
142 -- StartInstanceId (PRIVATE)
143 --   Returns instance_id for a start activity, given root process
144 --   name, itemtype, and version.
145 -- IN
146 --   itemtype - Itemtype of process
147 --   process - Process name
148 --   version - Process version
149 --   activity - Start activity instance label
150 --
151 function StartInstanceId(itemtype in varchar2,
152                     process  in varchar2,
153                     version in number,
154                     activity in varchar2)
155 return number
156 is
157   instid pls_integer;
158   colon pls_integer;
159   label varchar2(30);
160 begin
161   -- Parse activity arg into <process_name> and <instance_label> components.
162   colon := instr(activity, ':');
163   if (colon <> 0) then
164     -- Activity arg is <process name>:<instance label>
165     label := substr(activity, colon+1);
166   else
167     -- Activity arg is just instance label
168     label := activity;
169   end if;
170 
171   select
172   WPA.INSTANCE_ID
173   into instid
174   from WF_PROCESS_ACTIVITIES WPA
175   where WPA.INSTANCE_LABEL = StartInstanceId.label
176   and WPA.PROCESS_ITEM_TYPE = StartInstanceId.itemtype
177   and WPA.PROCESS_NAME = StartInstanceId.process
178   and WPA.PROCESS_VERSION = StartInstanceId.version
179   and WPA.START_END = wf_engine.eng_start;
180 
181   return instid;
182 exception
183   when no_data_found then
184       Wf_Core.Context('Wf_Process_Activity', 'StartInstanceId', itemtype,
185                       process, to_char(version), activity);
186       Wf_Core.Token('TYPE', itemtype);
187       Wf_Core.Token('PROCESS', process);
188       Wf_Core.Token('NAME', activity);
189       Wf_Core.Raise('WFENG_NOT_START');
190   when too_many_rows then
191     Wf_Core.Context('Wf_Process_Activity', 'StartInstanceId', itemtype,
192                     process, to_char(version), activity);
193     Wf_Core.Token('TYPE', itemtype);
194     Wf_Core.Token('PROCESS', process);
195     Wf_Core.Token('NAME', activity);
196     Wf_Core.Raise('WFENG_ACTIVITY_UNIQUE');
197   when others then
198     Wf_Core.Context('Wf_Process_Activity', 'StartInstanceId', itemtype,
199                     process, to_char(version), activity);
200     raise;
201 end StartInstanceId;
202 
203 --
204 -- ActiveInstanceId (PRIVATE)
205 --   Returns instance_id for an active instance of an activity.
206 -- NOTE
207 --   This is a more efficient version of FindActivity, to be used whenever
208 --   the current status the activity must have is already known.
209 --   It is also able to distinguish between duplicate activities, where
210 --   only one may be active at a given time.
211 -- IN
212 --   itemtype - Itemtype of item
213 --   itemkey - Itemkey of item
214 --   activity - Activity searching for, specified in the form
215 --              [<process_name>:]<instance_label>
216 --   status - Status of activity, or null if status not known
217 -- RETURNS
218 --   Instance id of activity
219 --
220 function ActiveInstanceId(itemtype in varchar2,
221                           itemkey in varchar2,
222                           activity in varchar2,
223                           status in varchar2)
224 return number
225 is
226   colon pls_integer;
227   process varchar2(30);
228   label varchar2(30);
229   instid pls_integer;
230   cur_actid pls_integer;
231   cur_status varchar2(8);
232   cur_result varchar2(30);
233 begin
234   -- Parse activity arg into <process_name> and <instance_label> components.
235   colon := instr(activity, ':');
236   if (colon <> 0) then
237     -- Activity arg is <process name>:<instance label>
238     process := substr(activity, 1, colon-1);
239     label := substr(activity, colon+1);
240   else
241     -- Activity arg is just instance label
242     process := '';
243     label := activity;
244   end if;
245 
246   -- SYNCHMODE:
247   -- In synchmode, the row in the WIAS runtime cache MUST be this row,
248   -- because synch processes can only operate on the current activity.
249   --
250   if (itemkey = wf_engine.eng_synch) then
251     -- Get the current item and status in the cache
252     Wf_Item_Activity_Status.LastResult(itemtype, itemkey,
253         cur_actid, cur_status, cur_result);
254 
255     -- If status doesn't match one asked for, immediate trouble
256     if (nvl(status, '1') <> nvl(cur_status, '2')) then
257       raise no_data_found;
258     end if;
259 
260     -- Check that activity label passed in matched the current actid
261     select WPA.INSTANCE_ID
262     into instid
263     from WF_PROCESS_ACTIVITIES WPA
264     where WPA.INSTANCE_LABEL = activeinstanceid.label
265     and WPA.PROCESS_NAME = nvl(activeinstanceid.process, WPA.PROCESS_NAME)
266     and WPA.INSTANCE_ID = activeinstanceid.cur_actid;
267   else
268     -- NORMAL mode
269     select WPA.INSTANCE_ID
270     into instid
271     from WF_ITEM_ACTIVITY_STATUSES WIAS, WF_PROCESS_ACTIVITIES WPA
272     where WIAS.ITEM_TYPE = activeinstanceid.itemtype
273     and WIAS.ITEM_KEY = activeinstanceid.itemkey
274     and WIAS.ACTIVITY_STATUS = nvl(activeinstanceid.status,
275                                    WIAS.ACTIVITY_STATUS)
276     and WIAS.PROCESS_ACTIVITY = WPA.INSTANCE_ID
277     and WPA.INSTANCE_LABEL = activeinstanceid.label
278     and WPA.PROCESS_NAME = nvl(activeinstanceid.process, WPA.PROCESS_NAME);
279   end if;
280 
281   return instid;
282 exception
283   when no_data_found then
284     return '';
285   when too_many_rows then
286     Wf_Core.Context('Wf_Process_Activity', 'ActiveInstanceId', itemtype,
287                     itemkey, activity, status);
288     Wf_Core.Token('TYPE', itemtype);
289     Wf_Core.Token('KEY', itemkey);
290     Wf_Core.Token('NAME', activity);
291     Wf_Core.Raise('WFENG_ITEM_ACTIVITY_UNIQUE');
292   when others then
293     Wf_Core.Context('Wf_Process_Activity', 'ActiveInstanceId', itemtype,
294                     itemkey, activity, status);
295     raise;
296 end ActiveInstanceId;
297 
298 --
299 --  IsChild (PRIVATE)
300 --   Search for any occurrence of an activity in a process tree, either
301 --   as a direct child, or referenced in an error process attached to a
302 --   child.
303 --   This function does a recursive search of the tree.  It should only be
304 --   used if:
305 --   1. There may not be an entry in WIAS yet for this activity.
306 --   2. You do not know the immediate parent of the activity.
307 -- IN
308 --   rootid - The instance_id of the parent process
309 --   acttype - Activity itemtype searching for
310 --   actname - Activity name searching for
311 --   actdate - Active date
312 -- RETURNS
313 --   True is activity found anywhere in process tree.
314 --
315 function IsChild(
316   rootid in number,
317   acttype in varchar2,
318   actname in varchar2,
319   actdate in date)
320 return boolean
321 is
322   cursor curs(parentid in pls_integer, actdate in date) is
323     select WPA2.INSTANCE_ID, WPA2.ACTIVITY_ITEM_TYPE, WPA2.ACTIVITY_NAME
324     from WF_PROCESS_ACTIVITIES WPA1,
325          WF_ACTIVITIES WA,
326          WF_PROCESS_ACTIVITIES WPA2
327     where WPA1.INSTANCE_ID = parentid
328     and WPA2.PROCESS_ITEM_TYPE = WA.ITEM_TYPE
329     and WPA2.PROCESS_NAME = WA.NAME
330     and WA.ITEM_TYPE = WPA1.ACTIVITY_ITEM_TYPE
331     and WA.NAME = WPA1.ACTIVITY_NAME
332     and actdate >= WA.BEGIN_DATE
333     and actdate < NVL(WA.END_DATE, actdate+1)
334     and WPA2.PROCESS_VERSION = WA.VERSION;
335 
336   childarr InstanceArrayTyp;
337   i pls_integer := 0;
338   errid   pls_integer;
339 
340   found boolean;
341 
342   status PLS_INTEGER;
343   waIND  NUMBER;
344 
345 begin
346   WF_CACHE.GetProcessActivityInfo(rootid, actdate, status, waIND);
347 
348   if (status <> WF_CACHE.task_SUCCESS) then
349     waIND := 0;
350 
351     select WA.ITEM_TYPE, WA.NAME, WA.VERSION, WA.TYPE, WA.RERUN,
352            WA.EXPAND_ROLE, WA.COST, WA.ERROR_ITEM_TYPE, WA.ERROR_PROCESS,
353            WA.FUNCTION, WA.FUNCTION_TYPE,  WA.MESSAGE, WA.BEGIN_DATE,
354            WA.END_DATE, WA.DIRECTION, WPA.PROCESS_ITEM_TYPE,
355            WPA.PROCESS_NAME, WPA.PROCESS_VERSION, WPA.ACTIVITY_ITEM_TYPE,
356            WPA.ACTIVITY_NAME, WPA.INSTANCE_ID, WPA.INSTANCE_LABEL,
357            WPA.PERFORM_ROLE, WPA.PERFORM_ROLE_TYPE, WPA.START_END,
358            WPA.DEFAULT_RESULT
359 
360     into   WF_CACHE.Activities(waIND).ITEM_TYPE,
361            WF_CACHE.Activities(waIND).NAME,
362            WF_CACHE.Activities(waIND).VERSION,
363            WF_CACHE.Activities(waIND).TYPE,
364            WF_CACHE.Activities(waIND).RERUN,
365            WF_CACHE.Activities(waIND).EXPAND_ROLE,
366            WF_CACHE.Activities(waIND).COST,
367            WF_CACHE.Activities(waIND).ERROR_ITEM_TYPE,
368            WF_CACHE.Activities(waIND).ERROR_PROCESS,
369            WF_CACHE.Activities(waIND).FUNCTION,
370            WF_CACHE.Activities(waIND).FUNCTION_TYPE,
371            WF_CACHE.Activities(waIND).MESSAGE,
372            WF_CACHE.Activities(waIND).BEGIN_DATE,
373            WF_CACHE.Activities(waIND).END_DATE,
374            WF_CACHE.Activities(waIND).DIRECTION,
375            WF_CACHE.ProcessActivities(rootid).PROCESS_ITEM_TYPE,
376            WF_CACHE.ProcessActivities(rootid).PROCESS_NAME,
377            WF_CACHE.ProcessActivities(rootid).PROCESS_VERSION,
378            WF_CACHE.ProcessActivities(rootid).ACTIVITY_ITEM_TYPE,
379            WF_CACHE.ProcessActivities(rootid).ACTIVITY_NAME,
380            WF_CACHE.ProcessActivities(rootid).INSTANCE_ID,
381            WF_CACHE.ProcessActivities(rootid).INSTANCE_LABEL,
382            WF_CACHE.ProcessActivities(rootid).PERFORM_ROLE,
383            WF_CACHE.ProcessActivities(rootid).PERFORM_ROLE_TYPE,
384            WF_CACHE.ProcessActivities(rootid).START_END,
385            WF_CACHE.ProcessActivities(rootid).DEFAULT_RESULT
386 
387     from   WF_PROCESS_ACTIVITIES WPA, WF_ACTIVITIES WA
388 
389     where  WPA.INSTANCE_ID = rootid
390     and    WA.ITEM_TYPE = WPA.ACTIVITY_ITEM_TYPE
391     and    WA.NAME = WPA.ACTIVITY_NAME
392     and    actdate >= WA.BEGIN_DATE
393     and    actdate < NVL(WA.END_DATE, actdate+1);
394 
395     waIND := WF_CACHE.HashKey(
396                       WF_CACHE.ProcessActivities(rootid).ACTIVITY_ITEM_TYPE ||
397                       WF_CACHE.ProcessActivities(rootid).ACTIVITY_NAME);
398 
399     WF_CACHE.Activities(waIND) := WF_CACHE.Activities(0);
400 
401    end if;
402 
403 
404   -- Quick check to see if root is already right
405   if (((WF_CACHE.ProcessActivities(rootid).PROCESS_ITEM_TYPE = acttype) and
406        (WF_CACHE.ProcessActivities(rootid).PROCESS_NAME = actname)) or
407       ((WF_CACHE.Activities(waIND).ITEM_TYPE = acttype) and
408        (WF_CACHE.Activities(waIND).NAME = actname))) then
409     return(TRUE);
410   end if;
411 
412   -- If activity at rootid has an error process, check it recursively
413   -- for a reference to the activity.
414   if (WF_CACHE.Activities(waIND).ERROR_PROCESS is not null) then
415     -- Get root id for the error process
416     begin
417       select WPA.INSTANCE_ID
418       into errid
419       from WF_PROCESS_ACTIVITIES WPA, WF_ACTIVITIES WA
420       where WPA.PROCESS_ITEM_TYPE = WF_CACHE.Activities(waIND).ERROR_ITEM_TYPE
421       and WPA.PROCESS_NAME = 'ROOT'
422       and WPA.PROCESS_VERSION = WA.VERSION
423       and WA.ITEM_TYPE = WPA.PROCESS_ITEM_TYPE
424       and WA.NAME = WPA.PROCESS_NAME
425       and actdate >= WA.BEGIN_DATE
426       and actdate < NVL(WA.END_DATE, actdate+1)
427       and WPA.INSTANCE_LABEL = WF_CACHE.Activities(waIND).ERROR_PROCESS;
428     exception
429       when no_data_found then
430         -- Error process is invalid, so ignore it
431         errid := '';
432     end;
433 
434     if (errid is not null) then
435       -- If activity found in error process return immediately.
436       -- If not, continue on to check proper children of rootid.
437       if (IsChild(errid, acttype, actname, actdate)) then
438         return(TRUE);
439       end if;
440     end if;
441   end if;
442 
443   -- Check all children of rootid
444   for child in curs(rootid, actdate) loop
445     -- Desired activity found.  Return immediately.
446     if ((child.activity_item_type = acttype) and
447         (child.activity_name = actname)) then
448       return(TRUE);
449     end if;
450 
451     -- Save all other children in array to be checked
452     childarr(i) := child.instance_id;
453     i := i + 1;
454   end loop;
455   childarr(i) := '';
456 
457   -- Loop through and recursively search any PROCESS-type children.
458   i := 0;
459   while (childarr(i) is not null) loop
460     if (Wf_Activity.Instance_Type(childarr(i), actdate) =
461         wf_engine.eng_process) then
462       found := IsChild(childarr(i), acttype, actname, actdate);
463 
464       -- If a non-null value is returned, then the activity was
465       -- found in this sub-tree.  Return the value and exit immediately.
466       if (found) then
467         return(TRUE);
468       end if;
469     end if;
470     i := i + 1;
471   end loop;
472 
473   -- If you made it here the activity was not found anywhere in the tree.
474   return(FALSE);
475 exception
476   when OTHERS then
477     Wf_Core.Context('Wf_Process_Activity', 'IsChild', to_char(rootid),
478                     acttype, actname, to_char(actdate));
479     raise;
480 end IsChild;
481 
482 --
483 --  FindActivity (PRIVATE)
484 --   Find the instance_id of an activity instance in the tree rooted at
485 --   parentid in the WPA table.
486 --   This function does a recursive search of the tree.  It should only be
487 --   used if:
488 --   1. There may not be an entry in WIAS yet for this activity instance.
489 --      (See ActiveInstanceId above)
490 --   2. You do not know the immediate parent of the activity.
491 -- IN
492 --   parentid - The instance_id of the parent process.
493 --   activity - Activity searching for, specified in the form
494 --              [<process_name>:]<instance_label>
495 --   actdate - Active date
496 -- RETURNS
497 --   Instance id of activity instance in process tree rooted at parentid.
498 --   Returns null if not found.
499 --
500 function FindActivity(parentid in number,
501                       activity in varchar2,
502                       actdate in date)
503 return number
504 is
505   colon pls_integer;
506   process varchar2(30);
507   label varchar2(30);
508 
509   status PLS_INTEGER;
510 
511   cursor curs(parentid in pls_integer, actdate in date) is
512     select WPA2.PROCESS_NAME, WPA2.INSTANCE_ID, WPA2.INSTANCE_LABEL
513     from WF_PROCESS_ACTIVITIES WPA1,
514          WF_ACTIVITIES WA,
515          WF_PROCESS_ACTIVITIES WPA2
516     where WPA1.INSTANCE_ID = parentid
517     and WPA2.PROCESS_ITEM_TYPE = WA.ITEM_TYPE
518     and WPA2.PROCESS_NAME = WA.NAME
519     and WA.ITEM_TYPE = WPA1.ACTIVITY_ITEM_TYPE
520     and WA.NAME = WPA1.ACTIVITY_NAME
521     and actdate >= WA.BEGIN_DATE
522     and actdate < NVL(WA.END_DATE, actdate+1)
523     and WPA2.PROCESS_VERSION = WA.VERSION;
524 
525   childarr InstanceArrayTyp;
526   i pls_integer := 0;
527 
528   childid pls_integer;
529   actid pls_integer := '';
530   wf_dup_activity exception;
531 begin
532   -- Parse activity arg into <process_name> and <instance_label> components.
533   colon := instr(activity, ':');
534   if (colon <> 0) then
535     -- Activity arg is <process name>:<instance label>
536     process := substr(activity, 1, colon-1);
537     label := substr(activity, colon+1);
538   else
539     -- Activity arg is just instance label
540     process := '';
541     label := activity;
542   end if;
543 
544   WF_CACHE.GetProcessActivity(parentid, status);
545 
546   if (status <> WF_CACHE.task_SUCCESS) then
547 
548     select WPA.PROCESS_ITEM_TYPE, WPA.PROCESS_NAME, WPA.PROCESS_VERSION,
549            WPA.ACTIVITY_ITEM_TYPE, WPA.ACTIVITY_NAME, WPA.INSTANCE_ID,
550            WPA.INSTANCE_LABEL, WPA.PERFORM_ROLE, WPA.PERFORM_ROLE_TYPE,
551            WPA.START_END, WPA.DEFAULT_RESULT
552     into   WF_CACHE.ProcessActivities(parentid)
553     from   WF_PROCESS_ACTIVITIES WPA
554     where  WPA.INSTANCE_ID = parentid;
555 
556   end if;
557 
558   if ((WF_CACHE.ProcessActivities(parentid).PROCESS_NAME =
559        nvl(process, WF_CACHE.ProcessActivities(parentid).PROCESS_NAME)) and
560       (WF_CACHE.ProcessActivities(parentid).INSTANCE_LABEL = label)) then
561     return(parentid);
562   end if;
563 
564   for child in curs(parentid, actdate) loop
565     -- Activity with this name found.
566     if ((child.process_name = nvl(process, child.process_name)) and
567         (child.instance_label = label)) then
568       if ((actid is not null) and (actid <> child.instance_id)) then
569         -- Activity already found once in this process - raise duplicate error.
570         raise wf_dup_activity;
571       else
572         -- Save id of activity
573         actid := child.instance_id;
574       end if;
575     end if;
576 
577     -- Save all other children in array to be checked
578     childarr(i) := child.instance_id;
579     i := i + 1;
580   end loop;
581   childarr(i) := '';
582 
583   -- Loop through and recursively search any PROCESS-type children.
584   i := 0;
585   while (childarr(i) is not null) loop
586     if (Wf_Activity.Instance_Type(childarr(i), actdate) =
587         wf_engine.eng_process) then
588       childid := FindActivity(childarr(i), activity, actdate);
589 
590       -- If a non-null value is returned, then the activity was
591       -- found somewhere in this sub-tree.
592       if (childid is not null) then
593         if ((actid is not null) and (actid <> childid)) then
594           -- Activity already found somewhere else.  Raise error.
595           raise wf_dup_activity;
596         else
597           -- Save id of activity
598           actid := childid;
599         end if;
600       end if;
601     end if;
602     i := i + 1;
603   end loop;
604 
605   -- Return saved actid.  If activity not found anywhere in tree this
606   -- will still be null.
607   return(actid);
608 exception
609   when wf_dup_activity then
610     Wf_Core.Context('Wf_Process_Activity', 'FindActivity', to_char(parentid),
611                     activity, to_char(actdate));
612     Wf_Core.Token('NAME', activity);
613     Wf_Core.Raise('WFENG_ACTIVITY_UNIQUE');
614   when OTHERS then
615     Wf_Core.Context('Wf_Process_Activity', 'FindActivity', to_char(parentid),
616                     activity, to_char(actdate));
617     raise;
618 end FindActivity;
619 
620 end WF_PROCESS_ACTIVITY;