DBA Data[Home] [Help]

PACKAGE BODY: APPS.CS_KB_WF_PKG

Source


1 PACKAGE BODY CS_KB_WF_PKG AS
2 /* $Header: cskbwfb.pls 120.3.12010000.2 2008/09/10 04:52:05 mmaiya ship $ */
3 
4 
5 /****************************************************
6 -------------PACKAGE VARIABLES ----------------------
7 ****************************************************/
8   BAD_INFORMATION_SUPPLIED EXCEPTION;
9   LOCK_SET_ERROR EXCEPTION;
10   PERMISSION_ERROR EXCEPTION;
11 
12 
13 /****************************************************
14 -------------FUNCTIONS-------------------------------
15 ****************************************************/
16 
17 
18 /**************************** Get Action *************************/
19 -- This function is used to get the action code of a flow_details_id
20 --
21 -- VARIABLES
22 -- g_flow_details_id
23 -- OUT: ActionCode or NULL
24 /*******************************************************************/
25 
26 FUNCTION getAction(
27     g_flow_details_id    IN  NUMBER
28     )
29     RETURN VARCHAR2
30     IS
31       returnValue VARCHAR2(10);
32     BEGIN
33 	-- return NULL if g_flow_details_id doesn't match
34         SELECT
35             MAX(action)
36         INTO
37             returnValue
38         FROM
39 	       cs_kb_wf_flow_details
40         WHERE
41           flow_details_id = g_flow_details_id;
42         RETURN returnValue;
43     END;
44 
45 
46 /**************************** Get Action Name *************************/
47 -- This function is used to get the action name given action lookup code
48 --
49 -- VARIABLES
50 -- IN: g_action, action code
51 -- OUT: action meaning
52 /**********************************************************************/
53 
54 FUNCTION getActionName(
55     g_action    IN  VARCHAR2
56     )
57     RETURN VARCHAR2
58     IS
59         returnValue CS_LOOKUPS.MEANING%TYPE;
60     BEGIN
61         SELECT
62             MAX(MEANING)
63         INTO
64             returnValue
65         FROM
66             CS_LOOKUPS
67         WHERE
68             lookup_code = g_action
69         AND
70             lookup_type = 'CS_KB_INTERNAL_CODES';
71 
72         RETURN returnValue;
73     END;
74 
75 /**************************** Get Flow Id *************************/
76 -- This function is used to get the flow id of a flow_details_id
77 --
78 --
79 -- VARIABLES
80 -- g_flow_details_id
81 /*******************************************************************/
82 
83 FUNCTION getFlowId(
84     g_flow_details_id    IN  NUMBER
85     )
86     RETURN NUMBER
87     IS
88       returnValue NUMBER;
89     BEGIN
90         SELECT
91             MAX(flow_id)
92         INTO
93             returnValue
94         FROM
95 	       cs_kb_wf_flow_details
96         WHERE
97           flow_details_id = g_flow_details_id;
98         RETURN returnValue;
99     END;
100 
101 
102 /**************************** Get Status *****************/
103 -- This function is to get a status for display.  It will display
104 -- a translated sets_b.status or flow_details.step, according
105 -- to whether a step is available
106 --
107 --
108 -- VARIABLES
109 -- g_setId -- set Id
110 /*******************************************************************/
111 
112 FUNCTION getStatus(
113     g_setId    IN  NUMBER
114     )
115     RETURN VARCHAR2
116     IS
117         returnValue CS_LOOKUPS.MEANING%TYPE;
118  BEGIN
119         SELECT
120             MAX(DECODE(B.STATUS,'NOT',LU2.MEANING,LU1.MEANING))
121         INTO
122             returnValue
123         FROM
124             cs_kb_sets_b B,
125             cs_kb_wf_flow_details D,
126             CS_LOOKUPS LU1,
127             CS_LOOKUPS LU2
128         WHERE
129             B.flow_details_id = D.flow_details_id (+)
130         AND
131             B.status = LU1.lookup_code
132         AND
133 	    D.STEP = LU2.lookup_code(+)
134         AND
135             LU1.lookup_type = 'CS_KB_INTERNAL_CODES'
136         AND
137             LU2.lookup_type(+) = 'CS_KB_STATUS'
138         AND
139             B.set_id = g_setId;
140 
141         RETURN returnValue;
142  END;
143 
144 
145 /**************************** Get Step Group *****************/
146 -- This function is used to get the group id associated with a
147 -- specific step.
148 --
149 -- VARIABLES
150 -- g_flow_details_id
151 /*******************************************************************/
152 
153 FUNCTION getStepGroup(
154     g_flow_details_id IN NUMBER
155     )
156     RETURN NUMBER
157     IS
158         return_number NUMBER := 0;
159     BEGIN
160          -- MIN() is used to force a value to return and avoid NDF error
161         SELECT
162             MIN(GROUP_ID)
163         INTO
164             return_number
165         FROM
166             CS_KB_WF_FLOW_DETAILS
167         WHERE
168             FLOW_DETAILS_ID = g_flow_details_id;
169 
170         RETURN return_number;
171      EXCEPTION
172         WHEN OTHERS THEN
173             return null;
174      END getStepGroup;
175 
176 
177 /**************************** HAS PERMISSION *************************/
178 -- This function is used to determine if a specified user has permissions
179 -- to a specific Step.
180 --
181 -- This one does NOT have profile checking and is used internally mainly.
182 --
183 -- VARIABLES
184 -- h_Step = Step to check
185 -- h_set_id
186 /*******************************************************************/
187 FUNCTION hasPermission(
188   h_flow_details_id  IN  NUMBER,
189   h_user_id  IN  NUMBER
190   )
191   RETURN NUMBER
192   IS
193     gid NUMBER;
194   BEGIN
195     gid := getStepGroup(h_flow_details_id);
196     IF (isMember(h_user_id,gid) = 1) THEN
197         RETURN 1;
198     END IF;
199     RETURN 0;
200   END hasPermission;
201 
202 
203 /**************************** HAS PERMISSION *************************/
204 -- This function is used to determine if a specified user has permissions
205 -- to a specific Step.
206 --
207 -- This one DOES have profile checking.
208 --
209 -- VARIABLES
210 -- h_Step = Step to check
211 -- h_user_id = the fnd_user user id
212 -- h_set_id
213 /*******************************************************************/
214 --FUNCTION hasPermission(
215 --  h_flow_details_id  IN  NUMBER,
216 --  h_user_id  IN  NUMBER,
217 --  h_set_id IN NUMBER
218 --  )
219 --  RETURN NUMBER
220 --  IS
221 --    gid NUMBER;
222 --  BEGIN
223 --    gid := getStepGroup(h_flow_details_id);
224 --    IF (isMember(h_user_id,gid) = 1) THEN
225 --        IF ((inCategory(h_user_id,h_set_id) = 1) OR (inProduct(h_user_id,h_set_id) = 1)) THEN
226 --            RETURN 1;
227 --        END IF;
228 --    END IF;
229 --    RETURN 0;
230 --  END hasPermission;
231 
232 
233 /**************************** IN CATEGORY *************************/
234 -- This function is used to determine if a specified user has permissions
235 -- to a specific Step.
236 --
237 -- VARIABLES
238 -- h_Step = Step to check
239 -- h_user_id = the fnd_user user id
240 --
241 /*******************************************************************/
242 FUNCTION inCategory(
243   c_user_id  IN  NUMBER,
244   c_set_id  IN  NUMBER
245   )
246   RETURN NUMBER
247   IS
248     CURSOR cats IS
249      SELECT category_id
250      FROM CS_KB_SET_CATEGORIES
251      WHERE set_id = c_set_id;
252 
253     catId NUMBER;
254     returnValue NUMBER := 0;
255   BEGIN
256     OPEN cats;
257     LOOP
258         -- Get values from cursor.
259         FETCH cats INTO catId;
260         EXIT WHEN cats%NOTFOUND;
261         IF (CS_KB_PROFILES_PKG.isCategoryMember(c_user_id,catId) = 1) THEN
262             returnValue := 1;
263             EXIT;
264         END IF;
265     END LOOP;
266     CLOSE cats;
267     RETURN returnValue;
268 
269 EXCEPTION
270     WHEN OTHERS THEN
271         CLOSE cats;
272         RAISE;
273 END inCategory;
274 
275 
276 /**************************** IN PRODUCT *************************/
277 -- This function is used to determine if a user belongs to same
278 -- product as solution.
279 --
280 -- VARIABLES
281 -- c_user_id = Step to check
282 -- c_set_id = the fnd_user user id
283 --
284 /*******************************************************************/
285 FUNCTION inProduct(
286   c_user_id  IN  NUMBER,
287   c_set_id  IN  NUMBER
288   )
289   RETURN NUMBER
290   IS
291     CURSOR prods IS SELECT
292                         product_id, product_org_id
293                     FROM
294                         cs_kb_set_products
295                     WHERE
296                         set_id = c_set_id;
297     prodId NUMBER;
298     prodOrgId NUMBER;
299     returnValue NUMBER := 0;
300   BEGIN
301     OPEN prods;
302     LOOP
303         FETCH prods INTO prodId,prodOrgId;
304         EXIT WHEN prods%NOTFOUND;
305 
306         IF (CS_KB_PROFILES_PKG.isProductMember(c_user_id,prodId,prodOrgId) = 1) THEN
307             returnValue := 1;
308             EXIT;
309         END IF;
310     END LOOP;
311     CLOSE prods;
312     RETURN returnValue;
313 
314 EXCEPTION
315     WHEN OTHERS THEN
316         CLOSE prods;
317         RAISE;
318 END inProduct;
319 
320 
321 /**************************** isMember ********************************/
322 -- This function is used to determine if a specified user belongs
323 -- to a specified notification group.
324 --
325 -- INTERNAL USE MAINLY
326 --
327 -- VARIABLES
328 -- user_id = the fnd_user user id
329 -- group_id =
330 --
331 /*******************************************************************/
332 FUNCTION isMember(
333   m_user_id IN NUMBER,
334   m_group_id IN NUMBER
335   )
336   RETURN NUMBER
337   IS
338     m_temp NUMBER;
339     return_value NUMBER;
340   BEGIN
341         SELECT
342             min(fnd_user.user_id)
343         INTO
344             m_temp
345         FROM
346             fnd_user,
347             jtf_rs_resource_extns,
348             jtf_rs_group_members
349         WHERE
350             jtf_rs_group_members.resource_id=jtf_rs_resource_extns.resource_id
351         AND
352             fnd_user.user_id=m_user_id
353         AND
354             jtf_rs_group_members.group_id=m_group_id
355  	AND jtf_rs_group_members.DELETE_FLAG <>'Y'
356 	AND jtf_rs_resource_extns.START_DATE_ACTIVE <= sysdate
357 	AND NVL(jtf_rs_resource_extns.END_DATE_ACTIVE, sysdate) >= sysdate
358         AND (
359                 (jtf_rs_resource_extns.source_id = fnd_user.employee_id
360              AND
361                 jtf_rs_resource_extns.category ='EMPLOYEE')
362         OR
363                 (jtf_rs_resource_extns.source_id = fnd_user.customer_id
364              AND
365                 jtf_rs_resource_extns.category ='PARTY'));
366 
367 
368         -- set return value to TRUE if something is found.
369         if (m_temp = m_user_id) THEN
370             return_value := 1;
371         ELSE
372             return_value := 0;
373         END IF;
374 
375         RETURN return_value;
376   END isMember;
377 
378 /**************************** CREATE WF PROCESS ********************************/
379 -- This is an internal procedure to create a process.
380 /*******************************************************************************/
381 
382 PROCEDURE Create_Wf_Process(
383   p_set_id          IN NUMBER,
384   p_set_number      IN VARCHAR2,
385   p_command         IN VARCHAR2,
386   p_flow_details_id IN NUMBER,
387   p_group_id        IN NUMBER,
388   p_solution_title  IN VARCHAR2
389   ) IS
390 
391     p_itemtype VARCHAR2(20) := 'CS_KB_W1';
392     wf_process VARCHAR2(20) := 'WFPROCESS';
393     p_itemkey VARCHAR(20) := p_set_id;
394     x_step_code	varchar2(30);
395     x_step_meaning   varchar2(80);
396     l_pub VARCHAR2(2000);
397     l_obs VARCHAR2(2000);
398 
399 
400   BEGIN
401 
402   -- Create workflow process.
403   WF_ENGINE.CreateProcess(p_itemtype, p_itemkey, wf_process);
404 
405   -- Set Attributes.
406   WF_ENGINE.SetItemAttrText(p_itemtype, p_itemkey, 'SETNO', p_set_number);
407   WF_ENGINE.SetItemAttrText(p_itemtype, p_itemkey, 'SETID', p_set_id);
408   WF_ENGINE.SetItemAttrText(p_itemtype, p_itemkey, 'COMMAND', p_command);
409   IF (p_command = 'PUB') THEN
410       l_pub := fnd_message.GET_STRING('CS','CS_KB_WF_PUB');
411      WF_ENGINE.SetItemAttrText(p_itemtype, p_itemkey,
412 	'COMMAND_DISPLAY', l_pub); -- 'Published');
413   ELSIF (p_command = 'OBS') THEN
414      l_obs := fnd_message.GET_STRING('CS','CS_KB_WF_OBS');
415      WF_ENGINE.SetItemAttrText(p_itemtype, p_itemkey,
416 	'COMMAND_DISPLAY', l_obs); --'Obsoleted');
417   END IF;
418   WF_ENGINE.SetItemAttrText(p_itemtype, p_itemkey, 'LANGUAGE', FND_GLOBAL.CURRENT_LANGUAGE);
419 
420   IF (p_flow_details_id is not null) THEN
421     WF_ENGINE.SetItemAttrNumber(p_itemtype, p_itemkey, 'FLOW_DETAILS_ID', p_flow_details_id);
422     select step into x_step_code
423     from cs_kb_wf_flow_details
424     where flow_details_id = p_flow_details_id;
425 
426     SELECT MAX(MEANING)
427     INTO x_step_meaning
428     FROM CS_LOOKUPS
429     WHERE lookup_code = x_step_code
430     AND lookup_type = 'CS_KB_STATUS';
431 
432     WF_ENGINE.SetItemAttrText(p_itemtype, p_itemkey, 'FLOWSTEP', x_step_meaning);
433 
434   END IF;
435 
436   IF (p_group_id is not null) THEN
437     WF_ENGINE.SetItemAttrNumber(p_itemtype, p_itemkey, 'GROUPID', p_group_id);
438   END IF;
439 
440   IF (p_solution_title is not null) THEN
441     WF_ENGINE.SetItemAttrText(p_itemtype, p_itemkey, 'SOLTITLE', p_solution_title);
442   END IF;
443 
444   -- Start workflow process.
445   wf_engine.StartProcess(p_itemtype, p_itemkey);
446 
447   EXCEPTION
448     WHEN OTHERS THEN
449         RAISE;
450   END Create_Wf_Process;
451 
452 /**************************** CREATE REJECT TO AUTHOR PROCESS ********************************/
453 -- This is an internal procedure to create a process to reject to author.
454 /*******************************************************************************/
455 
456 PROCEDURE Create_Reject_Process(
457   p_set_id          IN NUMBER,
458   p_set_number      IN VARCHAR2,
459   p_solution_title  IN VARCHAR2,
460   p_author	    IN VARCHAR2
461   ) IS
462 
463     p_itemtype VARCHAR2(20) := 'CS_KB_W1';
464     wf_process VARCHAR2(20) := 'REJPROCESS';
465     p_itemkey  VARCHAR(200) := to_char(p_set_id) || '-' || p_author; --7117561, 7047779
466 
467   BEGIN
468   -- Create workflow process.
469   WF_ENGINE.CreateProcess(p_itemtype, p_itemkey, wf_process);
470 
471   -- Set Attributes.
472   WF_ENGINE.SetItemAttrText(p_itemtype, p_itemkey, 'SETNO', p_set_number);
473   WF_ENGINE.SetItemAttrText(p_itemtype, p_itemkey, 'SETID', p_set_id);
474   WF_ENGINE.SetItemAttrText(p_itemtype, p_itemkey, 'LANGUAGE', FND_GLOBAL.CURRENT_LANGUAGE);
475   WF_ENGINE.SetItemAttrText(p_itemtype, p_itemkey, 'AUTHOR', p_author);
476 
477   -- Set the From field:
478   WF_ENGINE.SetItemAttrText(p_itemtype, p_itemkey, 'NOTFROM', FND_GLOBAL.User_Name);
479 
480   IF (p_solution_title is not null) THEN
481     WF_ENGINE.SetItemAttrText(p_itemtype, p_itemkey, 'SOLTITLE', p_solution_title);
482   END IF;
483 
484   -- Start workflow process.
485   wf_engine.StartProcess(p_itemtype, p_itemkey);
486 
487   EXCEPTION
488     WHEN OTHERS THEN
489         RAISE;
490   END Create_Reject_Process;
491 
492 /**************************** END WF *******************************************/
493 -- This procedure is basically an empty space to place any actions that are
494 -- desired to happen when a workflow step is complete.
495 -- Called within workflow
496 /*******************************************************************************/
497 PROCEDURE End_Wf(
498   p_itemtype  IN VARCHAR2,
499   p_itemkey   IN VARCHAR2,
500   p_actid     IN NUMBER,
501   p_funcmode  IN VARCHAR2,
502   p_result    OUT NOCOPY VARCHAR2
503   ) IS
504 
505   BEGIN
506     -- Place anything here
507     --COMMIT;
508     null;
509   END;
510 
511   /**************************** Expire Detail ********************/
512 -- This Procedure is used to "soft-delete" a detail line from the
513 -- cs_kb_wf_flow_details table
514 --
515 -- VARIABLES
516 -- p_flow_details_id
517 -- p_result: p_flow_details_id = completed successfully, -1 = error
518 /*******************************************************************/
519 PROCEDURE Expire_Detail(
520   p_flow_details_id IN NUMBER,
521   p_result OUT NOCOPY NUMBER
522   )
523   IS
524     uid NUMBER := fnd_global.user_id;
525     dt DATE := SYSDATE;
526     BEGIN
527 
528         UPDATE CS_KB_WF_FLOW_DETAILS
529         SET end_date = SYSDATE-1,
530             last_updated_by = uid,
531             last_update_date = dt
532         WHERE
533             flow_details_id = p_flow_details_id;
534 
535         --COMMIT;
536         p_result := p_flow_details_id;
537     EXCEPTION
538         WHEN OTHERS THEN
539             p_result := -1;
540     END Expire_Detail;
541 
542 /**************************** Expire Flow ********************/
543 -- This Procedure is used to "soft-delete" a flow from the
544 -- cs_kb_wf_flows_b table
545 --
546 -- VARIABLES
547 -- p_flow_id
548 -- p_result: 1 = completed successfully, -1 = error
549 /*******************************************************************/
550 PROCEDURE Expire_Flow(
551   p_flow_id IN NUMBER,
552   p_result OUT NOCOPY NUMBER
553   )
554   IS
555     uid NUMBER := fnd_global.user_id;
556     dt DATE := SYSDATE;
557 
558     CURSOR CHECK_FLOW_EXITS (flowId NUMBER) IS
559     SELECT count(*)
560     FROM CS_KB_WF_FLOWS_B
561     WHERE flow_id = flowId;
562 
563     CURSOR CHECK_DEFAULT_FLOWS (flowId NUMBER) IS
564     SELECT count(*)
565     FROM fnd_profile_options o,
566          fnd_profile_option_values ov
567     WHERE o.profile_option_name = 'CS_KB_DEFAULT_FLOW'
568     AND o.profile_option_id = ov.profile_option_id
569     AND ov.PROFILE_OPTION_VALUE = to_char(flowId)
570     AND ov.application_id = 170;
571 
572     v_def_count NUMBER := 0;
573     v_count     NUMBER := 0;
574 
575     BEGIN
576       OPEN  CHECK_FLOW_EXITS(p_flow_id);
577       FETCH CHECK_FLOW_EXITS INTO v_count;
578       CLOSE CHECK_FLOW_EXITS;
579 
580       IF v_count = 1 THEN
581 
582         OPEN  CHECK_DEFAULT_FLOWS(p_flow_id);
583         FETCH CHECK_DEFAULT_FLOWS INTO v_def_count;
584         CLOSE CHECK_DEFAULT_FLOWS;
585 
586         IF v_def_count = 0 THEN
587 
588           UPDATE CS_KB_WF_FLOWS_B
589           SET end_date = dt,
590               last_updated_by = uid,
591               last_update_date = dt
592           WHERE
593               flow_id = p_flow_id;
594 
595           p_result := 1;
596         ELSE --Flow is set against a Prof Option, so cant end date
597           p_result := -3;
598         END IF;
599       ELSE -- Invalid Flow id passed to api
600         p_result := -1;
601       END IF;
602 
603     EXCEPTION
604         WHEN OTHERS THEN
605             p_result := -2;
606     END Expire_Flow;
607 
608 
609   /**************************** Enable Flow ********************/
610 -- This Procedure is used to re-activate an End-Dated Flow
611 -- in the cs_kb_wf_flows_b table
612 --
613 -- VARIABLES
614 -- p_flow_id
615 -- p_result: 1 = completed successfully, -1 = error
616 /*******************************************************************/
617   PROCEDURE Enable_Flow( p_flow_id IN NUMBER,
618                          p_result  OUT NOCOPY NUMBER )  IS
619    uid NUMBER := fnd_global.user_id;
620    dt DATE := SYSDATE;
621 
622    CURSOR CHECK_FLOW_EXITS (flowId NUMBER) IS
623    SELECT count(*)
624    FROM CS_KB_WF_FLOWS_B
625    WHERE flow_id = flowId;
626 
627    v_count     NUMBER := 0;
628 
629   BEGIN
630 
631    OPEN  CHECK_FLOW_EXITS(p_flow_id);
632    FETCH CHECK_FLOW_EXITS INTO v_count;
633    CLOSE CHECK_FLOW_EXITS;
634 
635    IF v_count = 1 THEN
636 
637      UPDATE CS_KB_WF_FLOWS_B
638         SET end_date = null,
639             last_updated_by = uid,
640             last_update_date = dt
641       WHERE flow_id = p_flow_id;
642 
643      p_result := 1;
644    ELSE -- Invalid Flow id passed to api
645      p_result := -1;
646    END IF;
647 
648   EXCEPTION
649      WHEN OTHERS THEN
650          p_result := -2;
651   END Enable_Flow;
652 
653   /**************************** Get Actions ********************/
654 -- This Procedure is used to get a list of actions
655 --
656 -- VARIABLES
657 -- p_action_code = group ids
658 -- p_action_name = group names
659 /*******************************************************************/
660 PROCEDURE Get_Actions(
661   p_action_code OUT NOCOPY JTF_VARCHAR2_TABLE_100,
662   p_action_name OUT NOCOPY JTF_VARCHAR2_TABLE_100)
663   IS
664    l_resp_id number;
665    l_application_id number := 170;
666    l_region_code varchar2(30) := 'CS_KB_WF_ACTION';
667    l_user_id number := fnd_global.user_id;
668    region_name varchar2(50);
669    empty_str         varchar2(30) := ' ';
670    items_table jtf_region_pub.ak_region_items_table;
671    code JTF_VARCHAR2_TABLE_100 := JTF_VARCHAR2_TABLE_100();
672    name JTF_VARCHAR2_TABLE_100 := JTF_VARCHAR2_TABLE_100();
673   BEGIN
674 
675     l_resp_id :=  FND_PROFILE.VALUE_SPECIFIC(
676                   'JTF_PROFILE_DEFAULT_RESPONSIBILITY',
677                   l_user_id,
678                   null,
679                   null);
680 
681     jtf_region_pub.get_region(l_region_code,
682                   l_application_id,
683                   l_resp_id,
684                   empty_str,
685                   region_name,
686                   empty_str,
687                   items_table);
688     FOR l in 1..items_table.count LOOP
689     	name.extend;
690     	name(l) := jtf_region_pub.get_region_item_name(
691                               items_table(l).attribute_code,
692                               l_region_code);
693     END LOOP;
694 
695     code.extend;
696     code(1) := 'NOT';
697     code.extend;
698     code(2) := 'PUB';
699     code.extend;
700     code(3) := 'OBS';
701 
702     p_action_code := code;
703     p_action_name := name;
704   EXCEPTION
705 	when others then
706 	-- return empty array
707 		p_action_code := code;
708 		p_action_name := name;
709 
710   END Get_Actions;
711 
712 
713 
714   /**************************** Get All Groups ********************/
715 -- This Procedure is used to get all groups ids and names so that
716 -- the GUI can populate a drop down list.
717 --
718 -- VARIABLES
719 -- p_group_id = group ids
720 -- p_group_name = group names
721 /*******************************************************************/
722 PROCEDURE Get_All_Groups(
723   p_group_id        OUT NOCOPY JTF_NUMBER_TABLE,
724   p_group_name      OUT NOCOPY JTF_VARCHAR2_TABLE_100
725   )
726   IS
727 
728   -- Create the table variables to hold returnable info.
729   t_group_id JTF_NUMBER_TABLE := JTF_NUMBER_TABLE();
730   t_group_name JTF_VARCHAR2_TABLE_100 := JTF_VARCHAR2_TABLE_100();
731 
732   -- Temp variables to use to populate table variables
733   gi    NUMBER;
734   gn    JTF_RS_GROUPS_TL.GROUP_NAME%TYPE;
735 
736   counter NUMBER := 1;
737 
738   CURSOR All_Groups IS
739    SELECT g.GROUP_ID,
740           g.GROUP_NAME
741    FROM  JTF_RS_GROUPS_VL g,
742          JTF_RS_GROUP_USAGES u
743    WHERE g.GROUP_ID = u.GROUP_ID
744    AND u.USAGE = 'ISUPPORT'
745    AND NVL(g.END_DATE_ACTIVE, sysdate) >= sysdate
746    ORDER BY g.GROUP_NAME;
747 
748   BEGIN
749         OPEN All_Groups;
750         LOOP
751 
752             -- Get values from cursor.
753             FETCH All_Groups INTO gi,gn;
754             EXIT WHEN All_Groups%NOTFOUND;
755 
756             -- Extending tables one.
757             t_group_id.extend;
758             t_group_name.extend;
759 
760 
761             -- Setting table variables to value of temp variables
762             t_group_id(counter) := gi;
763             t_group_name(counter) := gn;
764 
765             -- Increment counter.
766             counter := counter + 1;
767         END LOOP;
768         CLOSE All_Groups;
769 
770     p_group_id := t_group_id;
771     p_group_name := t_group_name;
772 
773   EXCEPTION
774     WHEN OTHERS THEN
775         CLOSE All_Groups;
776         RAISE;
777   END;
778 
779 /**************************** Get All Steps *****************/
780 -- This Procedure is used to get a list of all the available
781 -- steps.  This procedure is intended for use with the admin
782 -- pages used to set up flows.
783 --
784 -- WRAPPER FOR GET_STEP_LIST
785 --
786 -- VARIABLES
787 -- p_step = a list of all of the available step codes
788 -- p_step_names = a list of all the names that correspond to the
789 --                  above list of codes.
790 /*******************************************************************/
791 
792 PROCEDURE Get_All_Steps(
793     p_step          OUT NOCOPY JTF_VARCHAR2_TABLE_100,
794     p_step_names    OUT NOCOPY JTF_VARCHAR2_TABLE_100
795     )
796     IS
797     BEGIN
798         Get_Step_List(0,p_step,p_step_names);
799     END Get_All_Steps;
800 
801 /**************************** GET FLOW DETAILS  *******************/
802 -- This procedure provides the details of a specific flow based on
803 -- the input.  This is used in the cskstat.jsp page.
804 -- VARIABLES:
805 --    p_flow_id (input to get results for)
806 --    p_flow_details_id - id that matches the sets_b table
807 --    p_order_num
808 --    p_step - this is the fnd lookup code
809 --    p_group_id - id of the jtf_group this is assigned to
810 --    p_action - NOT(NOTIFY),PUB(PUBLISH),OBS(OBSOLETE)
811 /*******************************************************************/
812 PROCEDURE Get_Flow_Details(
813     p_flow_id IN NUMBER,
814     p_flow_details_id OUT NOCOPY JTF_NUMBER_TABLE,
815     p_order_num OUT NOCOPY JTF_NUMBER_TABLE,
816     p_step OUT NOCOPY JTF_VARCHAR2_TABLE_100,
817     p_group_id OUT NOCOPY JTF_NUMBER_TABLE,
818     p_action OUT NOCOPY JTF_VARCHAR2_TABLE_100
819     )
820     IS
821     --tables to return
822     t_flow_details_id JTF_NUMBER_TABLE := JTF_NUMBER_TABLE();
823     t_order_num JTF_NUMBER_TABLE := JTF_NUMBER_TABLE();
824     t_step JTF_VARCHAR2_TABLE_100 := JTF_VARCHAR2_TABLE_100();
825     t_group_id JTF_NUMBER_TABLE := JTF_NUMBER_TABLE();
826     t_action JTF_VARCHAR2_TABLE_100 := JTF_VARCHAR2_TABLE_100();
827 
828 
829     CURSOR DETAILS_TABLE IS
830         SELECT
831             FD.FLOW_DETAILS_ID,
832             FD.ORDER_NUM,
833             FD.STEP,
834             FD.GROUP_ID,
835             FD.ACTION
836         FROM
837             CS_KB_WF_FLOW_DETAILS FD
838         WHERE
839             FLOW_ID = p_flow_id
840         AND
841             (BEGIN_DATE <= SYSDATE OR BEGIN_DATE IS NULL)
842         AND
843             (END_DATE >= SYSDATE OR END_DATE IS NULL)
844         ORDER BY
845             ORDER_NUM ASC;
846 
847     -- Temp variables.
848     did CS_KB_WF_FLOW_DETAILS.flow_details_id%TYPE; -- temp flow details id.
849     orn NUMBER; -- temp order number
850     sta CS_KB_WF_FLOW_DETAILS.step%TYPE; -- temp step
851     gid NUMBER; -- temp group id
852     act CS_KB_WF_FLOW_DETAILS.action%TYPE; -- temp action
853     counter NUMBER := 1;
854 BEGIN
855     OPEN DETAILS_TABLE;
856     LOOP
857 
858         -- Get values from cursor.
859         FETCH DETAILS_TABLE INTO did,orn,sta,gid,act;
860         EXIT WHEN DETAILS_TABLE%NOTFOUND;
861 
862 
863             -- Extending tables one.
864             t_flow_details_id.extend;
865             t_order_num.extend;
866             t_step.extend;
867             t_group_id.extend;
868             t_action.extend;
869 
870 
871             -- Setting table variables to value of temp variables
872             t_flow_details_id(counter) := did;
873             t_order_num(counter) := orn;
874             t_step(counter) := sta;
875             t_group_id(counter) := gid;
876             t_action(counter) := act;
877 
878 
879             counter := counter + 1;
880     END LOOP;
881     CLOSE DETAILS_TABLE;
882 
883     p_flow_details_id := t_flow_details_id;
884     p_order_num := t_order_num;
885     p_step := t_step;
886     p_group_id := t_group_id;
887     p_action := t_action;
888 
889 EXCEPTION
890     WHEN OTHERS THEN
891         CLOSE DETAILS_TABLE;
892         RAISE;
893 END Get_Flow_Details;
894 
895 /**************************** GET FLOWS  *************************/
896 -- This procedure provides the list of possible flows for the use
897 -- of the UI creating/editting flows
898 --
899 -- VARIABLES
900 -- p_flow_id (id that is needed when updates are made)
901 -- p_flow_name (name to display)
902 /*******************************************************************/
903 PROCEDURE Get_Flows(
904     p_flow_id OUT NOCOPY JTF_NUMBER_TABLE,
905     p_flow_name OUT NOCOPY JTF_VARCHAR2_TABLE_100
906     )
907     IS
908     --tables to return
909     t_flow_id JTF_NUMBER_TABLE := JTF_NUMBER_TABLE();
910     t_flow_name JTF_VARCHAR2_TABLE_100 := JTF_VARCHAR2_TABLE_100();
911 
912     CURSOR FLOWS_TABLE IS
913     select b.flow_id, t.name
914     from cs_kb_wf_flows_b b,
915          cs_kb_wf_flows_tl t
916     where b.flow_id = t.flow_id
917     and t.language = userenv('LANG')
918     and exists (select flow_id from cs_kb_wf_flow_details d
919                 where d.flow_id = b.flow_id
920                 and sysdate between nvl(d.begin_date, sysdate-1)
921                                 and nvl(d.end_date, sysdate+1)
922                 )
923     and sysdate < nvl(end_date, sysdate+1)
924     ORDER BY t.NAME ASC;
925 
926     -- Temp variables.
927     fid CS_KB_WF_FLOWS_TL.flow_id%TYPE; -- temp step.
928     fna CS_KB_WF_FLOWS_TL.name%TYPE; -- temp name
929     counter NUMBER := 1;
930 BEGIN
931     OPEN FLOWS_TABLE;
932     LOOP
933 
934         -- Get values from cursor.
935         FETCH FLOWS_TABLE INTO fid,fna;
936         EXIT WHEN FLOWS_TABLE%NOTFOUND;
937 
938 
939             -- Extending tables one.
940             t_flow_id.extend;
941             t_flow_name.extend;
942 
943             -- Setting table variables to value of temp variables
944             t_flow_id(counter) := fid;
945             t_flow_name(counter) := fna;
946 
947 
948             counter := counter + 1;
949     END LOOP;
950     CLOSE FLOWS_TABLE;
951 
952     p_flow_id := t_flow_id;
953     p_flow_name := t_flow_name;
954 
955 EXCEPTION
956     WHEN OTHERS THEN
957         CLOSE FLOWS_TABLE;
958         RAISE;
959 END Get_Flows;
960 
961   /**************************** GET PERMISSIONS *****************/
962 -- Given a user_id and set_id, determine the level of permissions
963 -- that a user has to the solution.  This is mainly used with the
964 -- manual accept ability.
965 --
966 -- Results are as follows:
967 --                        0 = no permissions
968 --                        1 = can accept solution task (currently in wf)
969 --                        2 = user owns it.
970 -- VARIABLES
971 -- p_set_id.
972 -- p_user_id.
973 -- p_results.
974 /*******************************************************************/
975 
976 PROCEDURE Get_Permissions(
977     p_set_id  IN  NUMBER,
978     p_user_id   IN  NUMBER,
979     p_results   OUT NOCOPY NUMBER
980     )
981     IS
982         lockedBy NUMBER;
983         status VARCHAR2(10);
984         fdid NUMBER; -- flow details id
985         setNo VARCHAR2(30);
986     BEGIN
987          -- Getting information about the record
988         CS_KB_SOLUTION_PVT.Get_Set_Details(p_set_id,setNo,status,fdid,lockedBy);
989 
990         -- If this user owns the item
991         IF (lockedBy = p_user_id) THEN
992             p_results := 2;
993         -- If workflow owns this item
994         ELSIF (lockedBy = -1) THEN
995 	    -- IF (hasPermission(fdid, p_user_id, p_set_id) = 1) THEN
996 	    -- lyao:remove p_set_id param so that it skipps the profile check
997             IF (hasPermission(fdid, p_user_id) = 1) THEN
998                 p_results := 1;
999             ELSE
1000                 p_results := 0;
1001             END IF;
1002         ELSE
1003             p_results := 0;
1004         END IF;
1005 END Get_Permissions;
1006 
1007 /*********************************************************************
1008    for a published soln, it may have gone thru multiple workflows.
1009    Only people who belong to any of the resource groups of those
1010    workflows can see a CHECKOUT button.  When they click on this button,
1011    lock will be chcked. (p_results=1)
1012    Otherwise, no button. (p_results=0)
1013    If already locked_by this user, still show CHECK OUT.
1014 ************************************************************************/
1015 PROCEDURE Get_Permissions_for_PUB_soln(
1016   p_set_number      IN  VARCHAR2,
1017   p_user_id         IN  NUMBER,
1018   p_results         OUT NOCOPY NUMBER ) IS
1019 
1020   -- cursor for all distinct  groups for all flows this solns has gone thru
1021   CURSOR groups IS
1022 	SELECT DISTINCT details2.GROUP_ID
1023 	FROM cs_kb_wf_flow_details details1,
1024 	     cs_kb_wf_flow_details details2,
1025 	     cs_kb_wf_flows_b flows,
1026 	     cs_kb_sets_b sets
1027 	WHERE
1028 	     sets.SET_NUMBER = p_set_number
1029 	AND  sets.FLOW_DETAILS_ID = details1.FLOW_DETAILS_ID
1030       	AND  details1.FLOW_ID = flows.FLOW_ID
1031 	AND  flows.FLOW_ID = details2.FLOW_ID;
1032 
1033   x_group_id number;
1034 
1035 BEGIN
1036 
1037   p_results := 0;
1038 
1039   OPEN groups;
1040   LOOP
1041      FETCH groups INTO x_group_id;
1042      EXIT WHEN groups%NOTFOUND;
1043      IF (isMember(p_user_id, x_group_id) = 1) THEN
1044         p_results := 1;
1045 	EXIT;
1046      END IF;
1047   END LOOP;
1048   CLOSE groups;
1049 
1050 EXCEPTION
1051   WHEN OTHERS THEN
1052  	p_results :=0;
1053 
1054 END Get_Permissions_for_PUB_soln;
1055 
1056 
1057   /**************************** Get Step List *****************/
1058 -- This Procedure is used to get a list of steps to be displayed
1059 -- as drop-down list.
1060 --
1061 -- This procedure is for internal use... see wrappers
1062 --
1063 -- VARIABLES
1064 -- p_restriction = 0: no restriction, 1: no actions besides NOT
1065 -- p_step = a list of all of the available step codes
1066 -- p_step_names = a list of all the names that correspond to the
1067 --                  above list of codes.
1068 /*******************************************************************/
1069 
1070 PROCEDURE Get_Step_List(
1071     p_restriction   IN  NUMBER,
1072     p_step          OUT NOCOPY JTF_VARCHAR2_TABLE_100,
1073     p_step_names    OUT NOCOPY JTF_VARCHAR2_TABLE_100
1074     )
1075     IS
1076 
1077     -- Create the table variables to hold returnable info.
1078     t_step JTF_VARCHAR2_TABLE_100 := JTF_VARCHAR2_TABLE_100();
1079     t_step_names JTF_VARCHAR2_TABLE_100 := JTF_VARCHAR2_TABLE_100();
1080 
1081     steps INTEGER;
1082     cursorReturn INTEGER;
1083 
1084     SQL1 VARCHAR2(1000) := 'SELECT DISTINCT CS_LOOKUPS.LOOKUP_CODE, CS_LOOKUPS.MEANING FROM CS_LOOKUPS ';
1085     SQL2 VARCHAR2(1000) := ' WHERE CS_LOOKUPS.LOOKUP_TYPE = :T AND  TRUNC(SYSDATE) BETWEEN Trunc(NVL(start_date_active,sysdate)) AND Trunc(NVL(end_date_active,sysdate)) AND enabled_flag = ''Y'''; --5198112
1086     SQL3 VARCHAR2(100) := ' ORDER BY CS_LOOKUPS.MEANING ';
1087 
1088     -- Temp variables.
1089     st CS_LOOKUPS.LOOKUP_CODE%TYPE; -- temp step.
1090     na CS_LOOKUPS.MEANING%TYPE; -- temp names
1091 
1092     -- Counter variable.
1093     counter NUMBER := 1;
1094 
1095     BEGIN
1096 
1097         IF(p_restriction = 1) THEN
1098            SQL1 := SQL1 || ' ,CS_KB_WF_FLOW_DETAILS ';
1099            SQL2 := SQL2 || ' AND CS_KB_WF_FLOW_DETAILS.STEP = CS_LOOKUPS.LOOKUP_CODE AND CS_KB_WF_FLOW_DETAILS.ACTION = :N ';
1100          END IF;
1101             -- open and parse statement
1102         steps := DBMS_SQL.OPEN_CURSOR;
1103         DBMS_SQL.PARSE(steps, SQL1||SQL2||SQL3, DBMS_SQL.V7);
1104         DBMS_SQL.BIND_VARIABLE(steps,':T', 'CS_KB_STATUS');
1105 
1106         IF(p_restriction = 1) THEN
1107             DBMS_SQL.BIND_VARIABLE(steps,':N', 'NOT');
1108         END IF;
1109 
1110          -- Define output variables
1111         DBMS_SQL.DEFINE_COLUMN(steps, 1, st,100);
1112         DBMS_SQL.DEFINE_COLUMN(steps, 2, na,100);
1113 
1114 
1115       --Execute cursor
1116         cursorReturn := DBMS_SQL.EXECUTE(steps);
1117 
1118 
1119     /******* LOOPING ********************/
1120 
1121     LOOP
1122         IF DBMS_SQL.FETCH_ROWS(steps) = 0 THEN
1123             EXIT;
1124         END IF;
1125         -- Loading variables with column values
1126         DBMS_SQL.COLUMN_VALUE(steps, 1, st);
1127         DBMS_SQL.COLUMN_VALUE(steps, 2, na);
1128 
1129         -- Extending tables one.
1130         t_step.extend;
1131         t_step_names.extend;
1132 
1133         -- Setting table variables to value of temp variables
1134         t_step(counter) := st;
1135         t_step_names(counter) := na;
1136 
1137            -- Increment counter.
1138         counter := counter + 1;
1139      END LOOP;
1140      DBMS_SQL.CLOSE_CURSOR(steps);
1141 
1142      IF (p_restriction = 1) THEN
1143         t_step.extend;
1144         t_step_names.extend;
1145         t_step(counter) := 'REJ';
1146         t_step_names(counter) := getActionName('REJ');
1147 	counter := counter + 1;
1148 
1149         t_step.extend;
1150         t_step_names.extend;
1151         t_step(counter) := 'SAV';
1152         t_step_names(counter) := getActionName('SAV');
1153      END IF;
1154 
1155      -- set OUT variables.
1156      p_step := t_step;
1157      p_step_names := t_step_names;
1158  EXCEPTION
1159     WHEN OTHERS THEN
1160         IF (steps is not null) THEN
1161             DBMS_SQL.CLOSE_CURSOR(steps);
1162         END IF;
1163         RAISE;
1164     END Get_Step_List;
1165 
1166 PROCEDURE Get_Next_Step(
1167   p_flow_details_id IN NUMBER,
1168   p_next_details_id OUT NOCOPY NUMBER
1169 ) IS
1170 x_flow_id  NUMBER;
1171 x_order_num NUMBER;
1172 x_next_order_num NUMBER;
1173 
1174 BEGIN
1175     x_flow_id := getFlowId(p_flow_details_id);
1176 
1177     select order_num
1178     into x_order_num
1179     from CS_KB_WF_FLOW_DETAILS
1180     where FLOW_DETAILS_ID = p_flow_details_id;
1181 
1182 
1183     select  MIN(order_num)
1184     into x_next_order_num
1185     from CS_KB_WF_FLOW_DETAILS
1186     where flow_id = x_flow_id
1187     and  (BEGIN_DATE <= SYSDATE OR BEGIN_DATE IS NULL)
1188     and  (END_DATE >= SYSDATE OR END_DATE IS NULL)
1189     and order_num > x_order_num;
1190 
1191     select flow_details_id
1192     into p_next_details_id
1193     from CS_KB_WF_FLOW_DETAILS
1194     where flow_id = x_flow_id
1195     and  (BEGIN_DATE <= SYSDATE OR BEGIN_DATE IS NULL)
1196     and  (END_DATE >= SYSDATE OR END_DATE IS NULL)
1197     and  order_num = x_next_order_num;
1198 
1199 
1200 EXCEPTION
1201     WHEN OTHERS THEN
1202     p_next_details_id := -1;
1203 
1204 END Get_Next_Step;
1205 
1206 FUNCTION Is_Step_Disabled ( P_FLOW_ID NUMBER,
1207                             P_FLOW_DETAILS_ID NUMBER,
1208                             P_FLOW_DETAILS_ORDER NUMBER,
1209                             P_CURRENT_FLOW_DETAILS_ID NUMBER) RETURN VARCHAR2
1210 IS
1211 
1212  CURSOR Get_First_Step IS
1213   SELECT Flow_Details_Id
1214   FROM CS_KB_WF_FLOW_DETAILS
1215   WHERE Flow_id = P_FLOW_ID
1216   AND sysdate between nvl(begin_date, sysdate-1)
1217                   and nvl(end_date, sysdate+1)
1218   AND Order_Num = ( SELECT min(order_num)
1219                     FROM CS_KB_WF_FLOW_DETAILS
1220                     WHERE Flow_id = P_FLOW_ID
1221                     AND sysdate between nvl(begin_date, sysdate-1)
1222                                     and nvl(end_date, sysdate+1) );
1223 
1224  CURSOR Get_Current_Detail_Order IS
1225   SELECT order_num
1226   FROM CS_KB_WF_FLOW_DETAILS
1227   WHERE FLOW_DETAILS_ID = P_CURRENT_FLOW_DETAILS_ID
1228   AND  (BEGIN_DATE <= SYSDATE OR BEGIN_DATE IS NULL)
1229   AND  (END_DATE >= SYSDATE OR END_DATE IS NULL);
1230 
1231 
1232  l_current_step  NUMBER;
1233  l_current_order NUMBER;
1234  l_next_step     NUMBER;
1235  l_first_step    NUMBER;
1236 
1237  Disabled   VARCHAR2(1) := 'Y';
1238  Enabled    VARCHAR2(1) := 'N';
1239  l_return VARCHAR2(1);
1240 
1241 BEGIN
1242  l_return := Disabled;
1243 
1244  IF P_CURRENT_FLOW_DETAILS_ID is not null THEN
1245  --dbms_output.put_line('Current Step Id is not null');
1246    -- Check if current step is still active, if Yes return Order Number
1247    OPEN  Get_Current_Detail_Order;
1248    FETCH Get_Current_Detail_Order INTO l_current_order;
1249    CLOSE Get_Current_Detail_Order;
1250 
1251  --dbms_output.put_line('Current Step Order='||l_current_order);
1252    IF l_current_order IS NOT NULL THEN
1253      -- Current Step is still active
1254 
1255      IF l_current_order >= P_FLOW_DETAILS_ORDER THEN
1256 
1257        -- Grant this step as submitable
1258        RETURN Enabled;
1259 
1260      ELSE -- Step is beyond current current flow position
1261 
1262        -- If next step then it is submitable
1263        CS_KB_WF_PKG.Get_Next_Step( p_flow_details_id => P_CURRENT_FLOW_DETAILS_ID,
1264                                    p_next_details_id => l_next_step);
1265 
1266        IF l_next_step = P_FLOW_DETAILS_ID THEN
1267          -- Grant this step as submitable
1268          RETURN Enabled;
1269        ELSE
1270          RETURN Disabled;
1271        END IF;
1272 
1273      END IF;
1274 
1275    ELSE -- The Current Flow Step is Inactive
1276 
1277      -- If this is the first Step then enable
1278      OPEN  Get_First_Step;
1279      FETCH Get_First_Step INTO l_first_step;
1280      CLOSE Get_First_Step;
1281 
1282        IF P_FLOW_DETAILS_ID = l_first_step THEN
1283          -- Grant this step as submitable
1284          RETURN Enabled;
1285        ELSE
1286          RETURN Disabled;
1287        END IF;
1288 
1289    END IF;
1290 
1291  ELSE --current step is null
1292    --dbms_output.put_line('Current Step Id is null');
1293    RETURN Disabled;
1294  END IF;
1295 
1296  RETURN l_return;
1297 
1298 END Is_Step_Disabled;
1299 
1300 
1301 PROCEDURE Get_Prev_Step(
1302   p_flow_details_id IN NUMBER,
1303   p_next_details_id OUT NOCOPY NUMBER
1304 ) IS
1305 x_flow_id  NUMBER;
1306 x_order_num NUMBER;
1307 x_prev_order_num NUMBER;
1308 
1309 BEGIN
1310     x_flow_id := getFlowId(p_flow_details_id);
1311 
1312     select order_num
1313     into x_order_num
1314     from CS_KB_WF_FLOW_DETAILS
1315     where FLOW_DETAILS_ID = p_flow_details_id;
1316 
1317 
1318     select  MAX(order_num)
1319     into x_prev_order_num
1320     from CS_KB_WF_FLOW_DETAILS
1321     where flow_id = x_flow_id
1322     and  (BEGIN_DATE <= SYSDATE OR BEGIN_DATE IS NULL)
1323     and  (END_DATE >= SYSDATE OR END_DATE IS NULL)
1324     and order_num < x_order_num;
1325 
1326     select flow_details_id
1327     into p_next_details_id
1328     from CS_KB_WF_FLOW_DETAILS
1329     where flow_id = x_flow_id
1330     and  order_num = x_prev_order_num;
1331 
1332 
1333 EXCEPTION
1334     WHEN OTHERS THEN
1335     p_next_details_id := -1;
1336 
1337 END Get_Prev_Step;
1338 
1339 /**************************** INSERT DETAIL  **********************/
1340 -- This procedure provides the ability to add a new flow
1341 --
1342 -- VARIABLES
1343 --  p_flow_id
1344 --  p_order_num
1345 --  p_step
1346 --  p_group_id
1347 --  p_action
1348 --  p_flow_details_id: flow_details_id or -1 if failed.
1349 /*******************************************************************/
1350 
1351 PROCEDURE Insert_Detail(
1352   p_flow_id IN NUMBER,
1353   p_order_num IN NUMBER,
1354   p_step IN VARCHAR2,
1355   p_group_id IN NUMBER,
1356   p_action IN VARCHAR2,
1357   p_flow_details_id OUT NOCOPY NUMBER
1358   )
1359   IS
1360     --temp vars
1361     uid NUMBER := fnd_global.user_id;
1362     dt DATE := SYSDATE;
1363   BEGIN
1364 
1365     -- Get next available details id number
1366     SELECT
1367         cs_kb_wf_flow_details_s.NextVal
1368     INTO
1369        p_flow_details_id
1370     FROM
1371        DUAL;
1372 
1373     -- Insert data
1374     INSERT INTO CS_KB_WF_FLOW_DETAILS(flow_details_id,
1375                                          flow_id,
1376                                          step,
1377                                          order_num,
1378                                          action,
1379                                          group_id,
1380                                          created_by,
1381                                          creation_date,
1382                                          last_updated_by,
1383                                          last_update_date)
1384                                   VALUES(p_flow_details_id,
1385                                          p_flow_id,
1386                                          p_step,
1387                                          p_order_num,
1388                                          p_action,
1389                                          p_group_id,
1390                                          uid,
1391                                          dt,
1392                                          uid,
1393                                          dt);
1394 
1395     --COMMIT;
1396 
1397   EXCEPTION
1398     WHEN OTHERS THEN
1399         p_flow_details_id := -1;
1400   END Insert_Detail;
1401 
1402 /**************************** INSERT FLOW  *************************/
1403 -- This procedure provides the ability to add a new flow
1404 --
1405 -- VARIABLES
1406 -- p_flow_name (name to display)
1407 -- p_flow_id (the new id, -1 if already taken or other error)
1408 -- 	bug 1966494: -3 for duplicated flow name
1409 /*******************************************************************/
1410 PROCEDURE Insert_Flow(
1411     p_flow_name IN VARCHAR2,
1412     p_flow_id OUT NOCOPY NUMBER
1413     ) IS
1414         --temp vars
1415         uid NUMBER := fnd_global.user_id;
1416         dt DATE := SYSDATE;
1417 	x_count number;
1418 BEGIN
1419 
1420   SELECT count(1)
1421   INTO x_count
1422   FROM CS_KB_WF_FLOWS_B b, CS_KB_WF_FLOWS_TL tl
1423   WHERE b.FLOW_ID = tl.FLOW_ID
1424   AND tl.NAME = p_flow_name
1425   AND tl.LANGUAGE = FND_GLOBAL.CURRENT_LANGUAGE;
1426 
1427   --check for duplicated flow name
1428   IF (x_count > 0) THEN
1429 	p_flow_id := -3;
1430   ELSE
1431         SELECT
1432              cs_kb_wf_flows_s.NextVal
1433         INTO
1434             p_flow_id
1435         FROM
1436             DUAL;
1437 
1438         INSERT INTO CS_KB_WF_FLOWS_B(flow_id,created_by,creation_date,
1439 		last_updated_by,last_update_date)
1440         VALUES(p_flow_id,uid,dt,uid,dt);
1441 
1442         INSERT INTO CS_KB_WF_FLOWS_TL (flow_id,
1443                                           name,
1444                                           creation_date,
1445                                           created_by,
1446                                           last_update_date,
1447                                           last_update_login,
1448                                           last_updated_by,
1449                                           language,
1450                                           source_lang)
1451                                    SELECT p_flow_id,
1452                                           p_flow_name,
1453                                           dt,
1454                                           uid,
1455                                           dt,
1456                                           uid,
1457                                           uid,
1458                                           l.language_code,
1459                                           USERENV('LANG')
1460                                    FROM fnd_languages l
1461                                    WHERE l.installed_flag IN ('I', 'B')
1462                                    AND NOT EXISTS
1463                                        (SELECT NULL
1464                                         FROM CS_KB_WF_FLOWS_TL t
1465                                         WHERE t.flow_id = p_flow_id
1466                                         AND t.language = l.language_code);
1467 
1468 
1469   END IF;
1470 
1471 EXCEPTION
1472   WHEN OTHERS THEN
1473         p_flow_id := -1;
1474 END Insert_Flow;
1475 
1476 /**************************** START WF ***************************/
1477 -- This procedure is used start the workflow process.  It is used
1478 -- regarless if the solution is in the wf process yet or ready to be
1479 -- published.
1480 --
1481 -- THIS ONE IS CALLED EXTERNALLY!
1482 --
1483 -- NOTES
1484 -- If users want to skip any workflow and want to publish the solutions
1485 -- directly, we go go directly to publish step, two conditions must be present:
1486 -- 1) p_set_id must have a valid value (max set_id for set_number)
1487 -- 2) p_new_step must be null (nowhere to send it to)
1488 -- If both values are null, it will throw an error.  If there is a value
1489 -- for p_set_id when p_new_step is not null, it will be ignored.
1490 --
1491 -- VARIABLES
1492 -- p_set_number
1493 -- p_set_id: NULL unless need to go directly to publish
1494 -- p_new_step: flow details id of new step to move to.  NULL if need
1495 --             to go directly to publish
1496 -- p_result: 1 if all fine, 0 if no permissions, -1 if general error,
1497 --           -2 bad input information, -3 general error in Post_pub_obs
1498 -- INTERNAL VARS
1499 -- p_command = cs_kb_wf_flow_details.action
1500 --
1501 /*******************************************************************/
1502 PROCEDURE Start_wf(
1503   p_set_number  IN VARCHAR2,
1504   p_set_id      IN NUMBER ,
1505   p_new_step    IN NUMBER ,
1506   p_results     OUT NOCOPY NUMBER,
1507   p_errormsg    OUT NOCOPY VARCHAR2
1508   ) IS
1509 
1510   solution_title CS_KB_SETS_TL.NAME%TYPE;
1511   p_group_id NUMBER;
1512   p_command VARCHAR2(30);
1513   p_locked_by NUMBER;
1514   current_id NUMBER;
1515   user_id NUMBER := fnd_global.user_id;
1516   set_id NUMBER := p_set_id;
1517   x_original_author_id FND_USER.USER_ID%TYPE;
1518   x_author		FND_USER.USER_NAME%TYPE;
1519   x_new_step number;
1520 
1521   BEGIN
1522 
1523   IF FND_LOG.LEVEL_PROCEDURE >= FND_LOG.G_CURRENT_RUNTIME_LEVEL THEN
1524     FND_LOG.STRING(FND_LOG.LEVEL_PROCEDURE, 'cs.plsql.CS_KB_WF_PKG.Start_Wf.begin',
1525                    ' Current User='||user_id||' Step='||p_new_step);
1526   END IF;
1527 
1528     IF (p_new_step is null) THEN
1529       IF (p_set_id is null) THEN
1530         RAISE BAD_INFORMATION_SUPPLIED;
1531       ELSE
1532 
1533          IF FND_LOG.LEVEL_STATEMENT >= FND_LOG.G_CURRENT_RUNTIME_LEVEL THEN
1534             FND_LOG.STRING(FND_LOG.LEVEL_STATEMENT, 'cs.plsql.CS_KB_WF_PKG.Start_Wf',
1535                           'Direct Publish - '||p_set_id);
1536          END IF;
1537 
1538 	    -- no step, intepreted as workflow disabled. Publish directly.
1539 	    -- do NOT kick off workflow
1540         p_command := 'PUB';
1541 	    p_locked_by := null;
1542         set_id := CS_KB_SOLUTION_PVT.clone_solution(p_set_number,
1543                                       p_command, p_new_step, p_locked_by);
1544 
1545       END IF;
1546     ELSE
1547 
1548       current_id := CS_KB_SOLUTION_PVT.locked_by(p_set_id);
1549 
1550       IF FND_LOG.LEVEL_STATEMENT >= FND_LOG.G_CURRENT_RUNTIME_LEVEL THEN
1551          FND_LOG.STRING(FND_LOG.LEVEL_STATEMENT, 'cs.plsql.CS_KB_WF_PKG.Start_Wf',
1552                        'Standard Flow Submission. Current Lock - '||current_id);
1553       END IF;
1554 
1555       -- If lock is not held by anyone, by workflow, by the current user,
1556       IF ((current_id is null) or (current_id = -1) or (current_id = user_id)) THEN
1557 
1558         -- check whether it is Rejected to Author
1559 	    IF (p_new_step = -999) THEN
1560 		  p_command := 'REJ';
1561 		  x_new_step := null;
1562 	    ELSE
1563           p_command := getAction(p_new_step);
1564 		  p_group_id := getstepGroup(p_new_step);
1565 		  x_new_step := p_new_step;
1566 	    END IF;
1567 
1568 	    -- Determine locked_by for each command
1569 	    IF (p_command = 'NOT') THEN
1570 	      p_locked_by := -1;
1571 	    ELSIF (p_command = 'REJ') THEN
1572 		  -- set locked by last updater
1573 		  -- 7117561
1574 		  SELECT created_by
1575                   INTO   x_original_author_id
1576                   FROM   cs_kb_sets_b
1577                   WHERE  set_id = ( SELECT MAX(set_id)       -- Bug fix: 7159784 - made it max(set_id) to get the last updater
1578                                     FROM   cs_kb_sets_b
1579                                     WHERE  set_number = p_set_number
1580                                     AND    status = 'SAV' --Bugfix7228667 - Added the Status to change the Locked by
1581                                    ) ;
1582 		  -- 7117561
1583           p_locked_by := x_original_author_id;
1584 	    ELSE
1585 		  -- PUB/OBS
1586 		  p_locked_by := null;
1587 	    END IF;
1588 
1589         IF FND_LOG.LEVEL_STATEMENT >= FND_LOG.G_CURRENT_RUNTIME_LEVEL THEN
1590            FND_LOG.STRING(FND_LOG.LEVEL_STATEMENT, 'cs.plsql.CS_KB_WF_PKG.Start_Wf',
1591                          'Before Clone - '||p_set_number||' - '
1592                                           ||p_command||' - '
1593                                           ||x_new_step||' - '
1594                                           ||p_locked_by);
1595         END IF;
1596 
1597         -- clone solution
1598         set_id := CS_KB_SOLUTION_PVT.clone_solution(p_set_number,
1599 					p_command, x_new_step, p_locked_by);
1600 
1601       ELSE
1602 	    -- someone forced a lock on it
1603         RAISE PERMISSION_ERROR;
1604       END IF;
1605 
1606 	  -- Send notifications
1607 	  solution_title := CS_KB_SOLUTION_PVT.get_solution_title(p_set_id);
1608 
1609 	  IF (p_command <> 'REJ') THEN
1610         Create_Wf_Process(set_id,p_set_number, p_command, p_new_step,
1611                           p_group_id, solution_title );
1612 	  ELSE
1613 	    /*select user_name
1614 	    into x_author
1615 	    from fnd_user
1616 	    where user_id = x_original_author_id;*/
1617 	    FOR get_user IN --Bugfix7117561 -  get the users to whom we need to send notifications
1618             (
1619                SELECT DISTINCT user_id, user_name
1620                FROM
1621                   (
1622                      SELECT user_id, user_name
1623                      FROM   cs_kb_sets_b a, fnd_user b
1624                      WHERE  set_id IN (
1625                                        SELECT MIN(set_id)
1626                                        FROM   cs_kb_sets_b
1627                                        WHERE  set_number = p_set_number
1628                                       )
1629                      AND    a.created_by = b.user_id
1630                      UNION
1631                      SELECT user_id, user_name
1632                      FROM   cs_kb_sets_b a, fnd_user b
1633                      WHERE  set_id IN (
1634                                        SELECT MAX(set_id)   --Bug fix:7159784 - made it max(set_id) to get the last updater
1635                                        FROM   cs_kb_sets_b
1636                                        WHERE  set_number = p_set_number AND
1637                                             status     = 'SAV'    --Bug fix:7228667
1638                                        )
1639                      AND    a.created_by = b.user_id
1640                   )
1641             )
1642 	    LOOP
1643 
1644                IF (CS_KB_SECURITY_PVT.IS_COMPLETE_SOLUTION_VISIBLE(x_original_author_id,set_id)= 'TRUE') THEN
1645 
1646                    Create_Reject_Process(set_id,p_set_number, solution_title, get_user.user_name);
1647                END IF;
1648 	    END LOOP;
1649 	  END IF;
1650 
1651     END IF;
1652 
1653     p_results := 1;
1654     p_errormsg := null;
1655 
1656   IF FND_LOG.LEVEL_PROCEDURE >= FND_LOG.G_CURRENT_RUNTIME_LEVEL THEN
1657     FND_LOG.STRING(FND_LOG.LEVEL_PROCEDURE, 'cs.plsql.CS_KB_WF_PKG.Start_Wf.end',
1658                    'Status=Success');
1659   END IF;
1660 
1661 EXCEPTION
1662   WHEN PERMISSION_ERROR THEN
1663 
1664     IF FND_LOG.LEVEL_EXCEPTION >= FND_LOG.G_CURRENT_RUNTIME_LEVEL THEN
1665        FND_LOG.STRING(FND_LOG.LEVEL_EXCEPTION, 'cs.plsql.CS_KB_WF_PKG.Start_Wf.EXCEPTION.Permission_Error',
1666                      ' Locking User='||Current_id||
1667                      ' Current User='||User_Id);
1668     END IF;
1669 
1670     p_results := 0;
1671     p_errormsg := 'No permission error';
1672   WHEN BAD_INFORMATION_SUPPLIED THEN
1673 
1674     IF FND_LOG.LEVEL_EXCEPTION >= FND_LOG.G_CURRENT_RUNTIME_LEVEL THEN
1675        FND_LOG.STRING(FND_LOG.LEVEL_EXCEPTION, 'cs.plsql.CS_KB_WF_PKG.Start_Wf.EXCEPTION.Bad_Info',
1676                      ' New Step='||p_new_step||
1677                      ' Set Id='||p_set_id);
1678     END IF;
1679 
1680     p_results := -2;
1681     p_errormsg := 'Bad information supplied';
1682   WHEN OTHERS THEN
1683 
1684     IF FND_LOG.LEVEL_UNEXPECTED >= FND_LOG.G_CURRENT_RUNTIME_LEVEL THEN
1685        FND_LOG.STRING(FND_LOG.LEVEL_UNEXPECTED, 'cs.plsql.CS_KB_WF_PKG.Start_Wf.UNEXPECTED',
1686                      ' Error= '||sqlerrm);
1687     END IF;
1688 
1689     p_results := -1;
1690     p_errormsg := SQLERRM;
1691 END Start_wf;
1692 
1693 
1694 
1695 /**************************** START WF PROCESSING ******************/
1696 -- This procedure is used start the workflow process.  It is used
1697 -- regarless if the solution is in the wf process yet or ready to be
1698 -- published. Always called by workflow node
1699 /*******************************************************************/
1700 PROCEDURE Start_wf_processing(
1701   p_itemtype  IN VARCHAR2,
1702   p_itemkey   IN VARCHAR2,
1703   p_actid     IN NUMBER,
1704   p_funcmode  IN VARCHAR2,
1705   p_result    OUT NOCOPY VARCHAR2
1706   ) IS
1707 
1708  command VARCHAR2(30) := WF_ENGINE.GetItemAttrText(p_itemtype,p_itemkey,'COMMAND');
1709  set_id NUMBER := WF_ENGINE.GetItemAttrNumber(p_itemtype,p_itemkey,'SETID');
1710  l_group_id NUMBER := WF_ENGINE.GetItemAttrNumber(p_itemtype,p_itemkey,'GROUPID');
1711 
1712  adhoc_role VARCHAR2(320);
1713  adhoc_role_name VARCHAR2(360);
1714 
1715  CURSOR GET_GROUP_NAME (v_group_id NUMBER) IS
1716   SELECT GROUP_NAME
1717   FROM   JTF_RS_GROUPS_VL
1718   WHERE  GROUP_ID = v_group_id;
1719 
1720 
1721  CURSOR Get_Group_Members (v_group_id IN NUMBER) IS
1722   SELECT DISTINCT
1723          fnd_user.user_name,
1724          fnd_user.user_id
1725   FROM fnd_user,
1726        jtf_rs_resource_extns,
1727        jtf_rs_group_members
1728   WHERE jtf_rs_group_members.resource_id=jtf_rs_resource_extns.resource_id
1729   AND jtf_rs_group_members.group_id = v_group_id
1730   AND jtf_rs_group_members.DELETE_FLAG <> 'Y'
1731   AND jtf_rs_resource_extns.START_DATE_ACTIVE <= sysdate
1732   AND NVL(jtf_rs_resource_extns.END_DATE_ACTIVE, sysdate) >= sysdate
1733   AND( ( jtf_rs_resource_extns.source_id = fnd_user.employee_id
1734          AND jtf_rs_resource_extns.category = 'EMPLOYEE' )
1735       OR (jtf_rs_resource_extns.source_id = fnd_user.customer_id
1736           AND jtf_rs_resource_extns.category = 'PARTY' )
1737       );
1738 
1739 BEGIN
1740 
1741  -- Loading process type name
1742  WF_ENGINE.SetItemAttrText(p_itemtype, p_itemkey, 'PROCTYPENAME', getActionName(command));
1743 
1744  OPEN  GET_GROUP_NAME (l_group_id);
1745  FETCH GET_GROUP_NAME INTO adhoc_role_name;
1746  CLOSE GET_GROUP_NAME;
1747 
1748  IF adhoc_role_name IS NULL THEN
1749    adhoc_role := NULL;
1750  ELSE
1751    adhoc_role := 'SOLNID'||to_char(set_id)||'-'||TO_CHAR(SYSDATE,'DDMMYYYYHH24MISS');
1752    --Bug 3588397 - USE OF SYSTIMESTAMP IS NOT COMPATABLE WITH 8.1.7
1753    --adhoc_role := 'SOLNID'||to_char(set_id)||'-'||TO_CHAR(SYSTIMESTAMP,'DDMMYYYYHH24MISSFF1');
1754  END IF;
1755 
1756  WF_DIRECTORY.CREATEADHOCROLE(adhoc_role,adhoc_role_name,null,null,null,'MAILHTML',null,null,null,'ACTIVE',sysdate+365);
1757 
1758  WF_ENGINE.SetItemAttrText(p_itemtype,p_itemkey,'RECIPIENTGROUP',adhoc_role);
1759 
1760  FOR Users IN Get_Group_Members (l_group_id) LOOP
1761 
1762    IF ((inCategory(Users.user_id,set_id) = 1) OR
1763        (inProduct(Users.user_id,set_id) = 1)) THEN
1764      -- Check if user can view the 'Complete' Solution
1765      IF (CS_KB_SECURITY_PVT.IS_COMPLETE_SOLUTION_VISIBLE(Users.user_id,set_id)= 'TRUE') THEN
1766        WF_DIRECTORY.ADDUSERSTOADHOCROLE(adhoc_role, Users.user_name);
1767      END IF;
1768 
1769    END IF;
1770 
1771  END LOOP;
1772 
1773  -- Set the From field:
1774  WF_ENGINE.SetItemAttrText(p_itemtype, p_itemkey, 'NOTFROM', FND_GLOBAL.User_Name);
1775 
1776  -- Send workflow down correct path according to command determined.
1777  IF (command = 'PUB') OR (command = 'OBS') THEN
1778    p_result := 'COMPLETE:PUB_OBS';
1779  ELSE
1780    p_result := 'COMPLETE:NOTIFY';
1781  END IF;
1782 
1783 EXCEPTION
1784 WHEN BAD_INFORMATION_SUPPLIED THEN
1785   wf_engine.SetItemAttrNumber(p_itemtype,p_itemkey,'TRACKING','NO VALID COMMAND FOUND');
1786   RAISE;
1787 WHEN OTHERS THEN
1788   wf_engine.SetItemAttrNumber(p_itemtype,p_itemkey,'TRACKING','Unspecified error in Start_wf_processing procedure');
1789   RAISE;
1790 END Start_wf_processing;
1791 
1792 
1793 /**************************** UPDATE DETAIL  *************************/
1794 -- This procedure provides the ability to add a new flow
1795 --
1796 -- VARIABLES
1797 -- p_flow_details_id - provided when a list of details is requested
1798 -- p_order_num
1799 -- p_step
1800 -- p_group_id
1801 -- p_action
1802 -- p_result: p_flow_details_id = completed successfully, 0 = completed but nothing was
1803 --           updated,  -1 = error
1804 /*******************************************************************/
1805 PROCEDURE Update_Detail(
1806     p_flow_details_id IN NUMBER,
1807     p_order_num       IN NUMBER,
1808     p_step            IN VARCHAR2,
1809     p_group_id        IN NUMBER,
1810     p_action          IN VARCHAR2,
1811     p_result          OUT NOCOPY NUMBER
1812 )
1813 IS
1814     uid NUMBER := fnd_global.user_id;
1815     dt DATE := SYSDATE;
1816     temp NUMBER; --temp variable to see if id exists
1817 
1818     BEGIN
1819         -- MIN() is used to force a value to return and avoid NDF error
1820         SELECT
1821             MIN(flow_details_id)
1822         INTO
1823             temp
1824         FROM
1825             cs_kb_wf_flow_details
1826         WHERE
1827             flow_details_id = p_flow_details_id;
1828 
1829         IF(temp is not null) THEN
1830             UPDATE CS_KB_WF_FLOW_DETAILS
1831             SET order_num = p_order_num,
1832                 step = p_step,
1833                 group_id = p_group_id,
1834                 action = p_action,
1835                 last_updated_by = uid,
1836                 last_update_date = dt
1837             WHERE
1838                 flow_details_id = p_flow_details_id;
1839 
1840             p_result := p_flow_details_id;
1841         ELSE
1842             p_result := 0;
1843         END IF;
1844     EXCEPTION
1845         WHEN OTHERS THEN
1846             p_result := -1;
1847     END Update_Detail;
1848 
1849 
1850 
1851 
1852 
1853 
1854 /**************************** UPDATE DETAIL ADMIN ******************/
1855 -- This procedure is a wrapper for both update and insert procedures
1856 -- to be used by the admin pages.
1857 --
1858 -- VARIABLES
1859 -- p_flow_details_id - provided when a list of details is requested
1860 -- p_order_num
1861 -- p_step
1862 -- p_group_id
1863 -- p_action
1864 -- p_result: flow_details_id OR -1 = error
1865 /*******************************************************************/
1866 
1867 PROCEDURE Update_Detail_Admin(
1868     p_flow_details_id IN NUMBER,
1869     p_flow_id         IN NUMBER,
1870     p_order_num       IN NUMBER,
1871     p_step            IN VARCHAR2,
1872     p_group_id        IN NUMBER,
1873     p_action          IN VARCHAR2,
1874     p_flag            IN VARCHAR2,
1875     p_result          OUT NOCOPY NUMBER
1876  ) IS
1877     result NUMBER;
1878  BEGIN
1879 
1880     -- DELETE
1881     IF (p_flag = 'DELETE') THEN
1882         Expire_Detail(p_flow_details_id, result);
1883     ELSE
1884         -- IF NOT A DELETE THEN LOOK FOR OTHER ACTION
1885         -- INSERT
1886         IF (p_flow_details_id = -2 ) THEN
1887             Insert_Detail(p_flow_id, p_order_num, p_step, p_group_id, p_action, result);
1888         -- UPDATE
1889         ELSE
1890             Update_Detail(p_flow_details_id, p_order_num, p_step, p_group_id, p_action, result);
1891         END IF;
1892     END IF;
1893 
1894     p_result := result;
1895  END Update_Detail_Admin;
1896 
1897 
1898 
1899 
1900 
1901 
1902 /**************************** UPDATE FLOW  *************************/
1903 -- This procedure provides the ability to add a new flow
1904 --
1905 -- VARIABLES
1906 -- p_flow_id
1907 -- p_flow_name (name to display)
1908 -- p_result: 1 = completed successfully, 0 = completed but nothing was
1909 --           updated,  -1 = error, -3=duplicated file name
1910 /*******************************************************************/
1911 PROCEDURE Update_Flow(
1912     p_flow_id IN NUMBER,
1913     p_flow_name IN VARCHAR2,
1914     p_result    OUT NOCOPY NUMBER
1915     ) IS
1916         uid NUMBER := fnd_global.user_id;
1917         dt DATE := SYSDATE;
1918         temp NUMBER;
1919 	x_count number;
1920 BEGIN
1921 
1922   SELECT count(1)
1923   INTO x_count
1924   FROM CS_KB_WF_FLOWS_B b, CS_KB_WF_FLOWS_TL tl
1925   WHERE b.FLOW_ID = tl.FLOW_ID
1926   AND b.FLOW_ID <> p_flow_id
1927   AND tl.NAME = p_flow_name
1928   AND tl.LANGUAGE = FND_GLOBAL.CURRENT_LANGUAGE;
1929 
1930   --check for duplicated flow name
1931   IF (x_count > 0) THEN
1932         p_result := -3;
1933   ELSE
1934          -- MIN() is used to force a value to return and avoid NDF error
1935         SELECT
1936             MIN(flow_id)
1937         INTO
1938             temp
1939         FROM
1940             cs_kb_wf_flows_tl
1941         WHERE
1942             flow_id = p_flow_id;
1943 
1944         IF(temp is not null) THEN
1945             UPDATE CS_KB_WF_FLOWS_TL
1946                 SET name = p_flow_name,
1947                     last_updated_by = uid,
1948                     last_update_date = dt,
1949 
1950                     source_lang = USERENV('LANG')
1951 
1952                 WHERE
1953                     flow_id = p_flow_id
1954                 AND USERENV('LANG') IN (language, source_lang);
1955 
1956                     --language = FND_GLOBAL.CURRENT_LANGUAGE;
1957 
1958             UPDATE CS_KB_WF_FLOWS_B
1959                 SET last_updated_by = uid,
1960                     last_update_date = dt
1961                 WHERE
1962                     flow_id = p_flow_id;
1963 
1964             p_result := 1;
1965         ELSE
1966             p_result := 0;
1967         END IF;
1968   END IF;
1969 EXCEPTION
1970   WHEN OTHERS THEN
1971         RAISE;
1972             p_result := -1;
1973 END Update_Flow;
1974 
1975 /**************************** UPDATE FLOW ADMIN ********************/
1976 -- This procedure is a wrapper of both insert and update functionality
1977 -- designed for the admin pages
1978 --
1979 -- VARIABLES
1980 -- p_flow_id
1981 -- p_flow_name (name to display)
1982 -- p_result: flow_id OR -1 = error, OR -3 = duplicated flow name
1983 /*******************************************************************/
1984 
1985    PROCEDURE Update_Flow_Admin(
1986     p_flow_id IN NUMBER,
1987     p_flow_name IN VARCHAR2,
1988     p_result    OUT NOCOPY NUMBER
1989     )
1990     IS
1991         result NUMBER;
1992     BEGIN
1993         -- INSERT, result is new flow_id or -1/-3
1994         IF (p_flow_id is null) THEN
1995             Insert_Flow(p_flow_name, result);
1996 
1997         -- UPDATE, result is 0/1/-1/-3
1998         ELSE
1999             Update_Flow(p_flow_id, p_flow_name, result);
2000         END IF;
2001 
2002         IF ((result = 0) OR (result = 1)) THEN
2003             p_result := p_flow_id;
2004         ELSE
2005             p_result := result;
2006         END IF;
2007     END Update_Flow_admin;
2008 
2009 
2010 PROCEDURE Add_Language IS
2011 
2012 BEGIN
2013 
2014   DELETE FROM CS_KB_WF_FLOWS_TL t
2015   WHERE NOT EXISTS
2016     (SELECT NULL
2017     FROM CS_KB_WF_FLOWS_B b
2018     WHERE b.flow_id = t.flow_id
2019     );
2020 
2021   UPDATE CS_KB_WF_FLOWS_TL T SET (
2022       name,
2023       description
2024     ) = (SELECT
2025       b.name,
2026       b.description
2027     FROM CS_KB_WF_FLOWS_TL b
2028     WHERE b.flow_id = t.flow_id
2029     AND b.language = t.source_lang)
2030   WHERE (
2031       t.flow_id,
2032       t.language
2033   ) IN (SELECT
2034       subt.flow_id,
2035       subt.language
2036     FROM CS_KB_WF_FLOWS_TL subb, CS_KB_WF_FLOWS_TL subt
2037     WHERE subb.flow_id = subt.flow_id
2038     AND subb.language = subt.source_lang
2039     AND (subb.name <> subt.name
2040       OR (subb.name IS NULL AND subt.name IS NOT NULL)
2041       OR (subb.name IS not NULL AND subt.name IS NULL)
2042       OR subb.description <> subt.description
2043       OR (subb.description IS NULL AND subt.description IS NOT NULL)
2044       OR (subb.description IS NOT NULL AND subt.description IS NULL)
2045   ));
2046 
2047   INSERT INTO CS_KB_WF_FLOWS_TL (
2048     flow_id,
2049     name,
2050     description,
2051     creation_date,
2052     created_by,
2053     last_update_date,
2054     last_update_login,
2055     last_updated_by,
2056     language,
2057     source_lang
2058   ) SELECT
2059     b.flow_id,
2060     b.name,
2061     b.description,
2062     b.creation_date,
2063     b.created_by,
2064     b.last_update_date,
2065     b.last_update_login,
2066     b.last_updated_by,
2067     l.language_code,
2068     b.source_lang
2069   FROM CS_KB_WF_FLOWS_TL b, fnd_languages l
2070   WHERE l.installed_flag IN ('I', 'B')
2071   AND b.language = USERENV('LANG')
2072   AND NOT EXISTS
2073     (SELECT NULL
2074     FROM CS_KB_WF_FLOWS_TL t
2075     WHERE t.flow_id = b.flow_id
2076     AND t.language = l.language_code);
2077 
2078 END Add_Language;
2079 
2080 -- Package Body CS_KB_WF
2081 END;