Fun with EBS APIs, part II: Users and responsbilities (FND_USER_PKG)

In my previous post, I demo'ed some simple scripts to make changes to E-Business Suite profile options, using the API provided by the FND_PROFILE package. This post will look at the FND_USER_PKG package (no relation to the "ABS brake system"). While my examples only reference user creation and responsibility assignment, it's really worth reviewing the contents of the rest of the package; there's lots of useful stuff in there.

First things first

Disclaimer time! Are you surprised? I hope you're not surprised. Am I really copying this section from the previous post? Yup, I am!

  1. The example code presented in this series of blog posts uses APIs written by Oracle to manipulate data in the E-Business Suite database. This does NOT imply that the code in these posts is endorsed or supported by Oracle.
  2. The examples in this series of blog posts are presented as demonstration code only, and are neither guaranteed to work in your environment, nor in all cases. Since my environments are not the same as yours, I cannot offer support in the event that this code does not work for you. You are encouraged to make whatever modifications are necessary to make these examples work for you.
  3. The scripts used in these examples were developed and implemented in test and development environments, where change management rules are often more lax. Be sure to test thoroughly, and to consult your organization's change management procedures, before attempting to use this code in production.

Short form: There's data-changing code in these posts. Oracle won't support you if you use the code; I can't support you either. Before running this code in production without testing and approval, you should probably prepare a resumé and, optionally, schedule an appointment with a psychiatrist.

Example 1: Duplicating a user's responsibilities

This is probably most relevant in test/dev/CRP environments, where it's not uncommon to be asked, "Can you please set up Fred to have the same responsibilities as Jane in the UAT environment?" or "Can you please set up 12 users in the Training environment, all with the same responsibilities?" I've written a script called ebs_dupresp that will duplicate all of a user's directly-assigned responsibilities to a target user, optionally creating the user record:

/*
ebs_dupresp.sql
Contact: John Piwowar
Purpose: Grant one user's responsbilities to another, optionally
creating the target user
Notes: ...
*/

SET serveroutput on size 1000000 format word_wrapped
SET verify off feedback off

--Readability improvements. First time for everything.
DEFINE src=&1
DEFINE targ=&2

PROMPT Source user: &&src
PROMPT Target user: &&targ
PROMPT CTRL-C might be a good idea if these are incorrect/reversed.
ACCEPT create_user_pref prompt 'Create &&targ if non-existent? (y/N) '

DECLARE
   src_user APPLSYS.FND_USER.USER_NAME%TYPE := upper('&&src');
   targ_user APPLSYS.FND_USER.USER_NAME%TYPE := upper('&&targ');
   create_flag BOOLEAN := FALSE;
   targ_userid APPLSYS.FND_USER.USER_ID%TYPE := NULL;

   --Cursor to pull source user's responsbilities
   --Note: does not pull end-dated responsibilities, since they were
   -- presumably end-dated for a reason. Also retains forward-looking
   -- end dates
   CURSOR cGetResps (user_in APPLSYS.FND_USER.USER_NAME%TYPE)
   IS
      SELECT a.application_short_name app
           , r.responsibility_key key
           , t.responsibility_name respname
           , s.security_group_key secgrp
           , g.end_date
        FROM apps.fnd_user_resp_groups_direct g
           , applsys.fnd_responsibility r
           , applsys.fnd_user u
           , applsys.fnd_application a
           , applsys.fnd_security_groups s
           , applsys.fnd_responsibility_tl t
       WHERE g.responsibility_id = r.responsibility_id
         AND g.security_group_id = s.security_group_id
         AND g.responsibility_application_id = r.application_id
         AND g.responsibility_application_id=t.application_id
         AND g.responsibility_id=t.responsibility_id
         AND r.application_id = a.application_id
         AND u.user_name = user_in
         AND g.user_id = u.user_id
         AND (g.end_date > SYSDATE or g.end_date is null);
BEGIN
   IF upper(substr('&&create_user_pref',1,1)) = 'Y' THEN
      create_flag := TRUE;
   END IF; --create_user_pref=Y
  
   --Create user if needed
   IF create_flag AND NOT (apps.fnd_user_pkg.userExists(targ_user))
   THEN

      targ_userid := apps.fnd_user_pkg.createuserid (
                         x_user_name => targ_user
                       , x_owner => 'CUST'
                       , x_unencrypted_password => 'chang3m3'
                       , x_description => 'Autocreated by ebs_dupresps.sql'
                       );
      --Probably won't ever get to this point, but just to be safe...
      IF targ_userid IS NULL THEN
         dbms_output.put_line('Failed to create ' || targ_user ||
                              ', cannot continue'
                             );
         RETURN;
      ELSE
         dbms_output.put_line('User ' || targ_user || ' created with id ' ||
                              targ_userid);
      END IF; --targ_userid
   ELSIF create_flag AND (apps.fnd_user_pkg.userExists(targ_user))
   THEN
      dbms_output.put_line('User ' || targ_user || ' already exists.');
   ELSIF NOT create_flag AND NOT (apps.fnd_user_pkg.userExists(targ_user))
   THEN
      dbms_output.put_line('User ' || targ_user ||
                           ' does not exist and create flag not specified.');
      RETURN;
   END IF; --create_flag

   --Add responsibilities
  
   FOR new_resp IN cGetResps(src_user)
   LOOP
      dbms_output.put_line('Adding responsibility ' || new_resp.respname);
      apps.fnd_user_pkg.AddResp( username => targ_user
                               , resp_app => new_resp.app
                               , resp_key => new_resp.key
                               , security_group => new_resp.secgrp
                               , description => 'Added by ebs_dupresps.sql'
                               , start_date => SYSDATE
                               , end_date => new_resp.end_date
                               );
   END LOOP; --new_resp
END; --ebs_dupresps
/


Here's a two-part demo. For the first part, I've assumed that I just got the following email from my boss:

John, Please create an EBS account in the test environment for the new DBA, Devnul Larson. Just give him the same responsibilities that you have, for now.

APPS@R12VIS(11.1.0.7)>@ebs_dupresp johnp devnull
Source user: johnp
Target user: devnull
CTRL-C might be a good idea if these are incorrect/reversed.
Create devnull if non-existent? (y/N) y
User DEVNULL created with id 1013423
Adding responsibility System Administrator
Adding responsibility Oracle Diagnostics Tool
APPS@R12VIS(11.1.0.7)>@lresp johnp
Currently active responsibilities for user johnp

APP		     RESPNAME				      END_DATE
-------------------- ---------------------------------------- ---------------
FND		     Application Diagnostics		      NONE
FND		     Application Diagnostics		      NONE
JTF		     Oracle Diagnostics Tool		      NONE
SYSADMIN	     System Administrator		      NONE
APPS@R12VIS(11.1.0.7)>@lresp devnull
Currently active responsibilities for user devnull

APP		     RESPNAME				      END_DATE
-------------------- ---------------------------------------- ---------------
FND		     Application Diagnostics		      NONE
FND		     Application Diagnostics		      NONE
JTF		     Oracle Diagnostics Tool		      NONE
SYSADMIN	     System Administrator		      NONE

Later that day, another email arrives:

On second thought, just assign all of SYSADMIN's responsibilities to the new guy. BTW, why are you assigning all the new tickets to DEVNULL?

APPS@R12VIS(11.1.0.7)>@ebs_dupresp sysadmin devnull
Source user: sysadmin
Target user: devnull
CTRL-C might be a good idea if these are incorrect/reversed.
Create devnull if non-existent? (y/N) y
User DEVNULL already exists.
Adding responsibility Application Developer
Adding responsibility System Administrator
Adding responsibility Application Developer Common Modules
Adding responsibility System Administration
Adding responsibility Preferences SSWA
Adding responsibility Workflow
(I'll spare you the full list)

It's worth noting that this is a fairly primitive approach, particularly when it comes to the user creation piece (no LDAP integration, no association of user record with an HR person/party record, etc). The ebs_dupresp script also doesn't touch things like profile options, so it shouldn't be considered a "user clone" script. You could probably accomplish this work with FNDLOAD or a third-party tool like DataLoader, but I'd rather write SQL. ;-)

Example 2: Adding a list of responsibilities to a user

Another common need is adding responsibilities to multiple users. Maybe you want to add the Application Developer responsibility to a handful of new consultants, or grant the Oracle Diagnostics responsibility to some freshly trained business analysts and power users. With a little bit of setup, it's pretty easy to do bulk responsibility assignments in a way that's easily repeated across multiple instances. Here's an example script, ebs_bulkresp, that does the job:

/*
ebs_bulkresp.sql
Contact: John Piwowar
Purpose: Add 1+ responsibilities to 1+ users
Requires: desired action (validate or apply) as script argument
Table xxrespload, of the format:
Name Type
----------------------------- --------------------
USER_NAME VARCHAR2(100)
APP_SHORTNAME VARCHAR2(50)
RESP_NAME VARCHAR2(100)
END_DATE DATE
Notes: Actually, validation happens no matter what, but it never hurts to be explicit
*/

SET serveroutput on size 1000000 format word_wrapped
SET verify off feedback off
col user_name for a20
col app for a10
col end_date for a15
col resp_name for a35

PROMPT Working with following records in table xxrespload:
SELECT user_name
     , app_shortname app
     , resp_name
     , nvl(to_char(end_date,'DD-MON-YYYY'),'NONE') end_date
  FROM xxrespload
 ORDER BY user_name
     , resp_name
/

DECLARE
   action VARCHAR2(8) := upper('&1');
   TYPE RespPair IS RECORD ( app APPLSYS.FND_APPLICATION.APPLICATION_SHORT_NAME%TYPE
                           , resp APPLSYS.FND_RESPONSIBILITY_TL.RESPONSIBILITY_NAME %TYPE
                           );
   TYPE UserList IS TABLE OF APPLSYS.FND_USER.USER_NAME%TYPE;
   TYPE RespList IS TABLE OF RespPair;
   BadUsers UserList;
   BadResps RespList;
   --N.B. Annoying security_group_id kludge. NULL=STANDARD, woohoo!
   CURSOR cGetBulkResps IS
      SELECT x.user_name
           , x.app_shortname
           , x.resp_name
           , r.responsibility_key resp_key
           , s.security_group_key secgrp
           , x.end_date
        FROM xxrespload x
        JOIN applsys.fnd_application a on (a.application_short_name=x.app_shortname)
        JOIN applsys.fnd_responsibility_tl t on ( a.application_id=t.application_id
                                                 and x.resp_name=t.responsibility_name)
        JOIN applsys.fnd_responsibility r on ( t.application_id=r.application_id
                                               and t.responsibility_id=r.responsibility_id)
        JOIN applsys.fnd_security_groups s on (nvl(r.security_group_id,0)=s.security_group_id)
       WHERE x.user_name is not null;
BEGIN
   
   dbms_output.put_line('Starting validation...');

   --Validate supplied users

   SELECT DISTINCT user_name
     BULK COLLECT INTO BadUsers
     FROM xxrespload x
    WHERE NOT EXISTS (SELECT user_name
                        FROM fnd_user u
                       WHERE u.user_name = x.user_name
                         AND (u.end_date is null OR u.end_date > SYSDATE)
                     );

   --Validate app-resp pairs

   SELECT DISTINCT app_shortname
                 , resp_name
     BULK COLLECT INTO BadResps
     FROM xxrespload x
    WHERE NOT EXISTS ( SELECT a.application_short_name
                            , r.responsibility_name
                         FROM applsys.fnd_responsibility_tl r
                         JOIN applsys.fnd_application a using (application_id)
                        WHERE a.application_short_name = x.app_shortname
                          AND r.responsibility_name = x.resp_name
                     );

   IF BadUsers.COUNT > 0 THEN
      dbms_output.put_line('Found ' || BadUsers.COUNT || ' invalid user names.');
   
      FOR i IN BadUsers.FIRST .. BadUsers.LAST
      LOOP
         dbms_output.put_line('User ' || nvl(BadUsers(i),'<NULL>') ||
                              ' does not exist or is end-dated.'
                             );
      END LOOP; --BadUsers;
   ELSE
      dbms_output.put_line('All users valid.');
   END IF; --BadUsers.COUNT

   IF BadResps.COUNT > 0 THEN
      dbms_output.put_line('Found ' || BadResps.COUNT || ' invalid app-responsibility pairs.');

      FOR i IN BadResps.FIRST .. BadResps.LAST
      LOOP
         dbms_output.put_line('Responsibility ' || nvl(BadResps(i).resp,'<NULL>') ||
                              ' invalid for app ' ||
                              nvl(BadResps(i).app,'<NULL>') ||
                              ', or vice versa.'
                             );
      END LOOP; --BadResps
   ELSE
      dbms_output.put_line('All app-responsibility pairs valid');
   END IF; --BadResps.COUNT

   --Okay, now let's actually do stuff
   IF action = 'APPLY' THEN
      dbms_output.put_line('Adding responsibilities...');
      FOR new_resp IN cGetBulkResps
      LOOP
         dbms_output.put_line('User: ' || new_resp.user_name ||
                              ' App: ' || new_resp.app_shortname ||
                              ' Resp: ' || new_resp.resp_name
                             );

         apps.fnd_user_pkg.AddResp( username => new_resp.user_name
                                  , resp_app => new_resp.app_shortname
                                  , resp_key => new_resp.resp_key
                                  , security_group => new_resp.secgrp
                                  , description => 'Added by ebs_bulkresps.sql'
                                  , start_date => SYSDATE
                                  , end_date => new_resp.end_date
                                  );
      END LOOP; --cGetBulkResps
   ELSE
      dbms_output.put_line('Invoke script with APPLY to add responsibilities');
   END IF; --action='APPLY';
END; --ebs_bulkresp
/

As a demo, here's another theoretical work request:

Please assign Application Developer and Oracle Diagnostics responsibilities to users SBERG, YWATSON, and WPARKER. SBERG will also need Functional Administrator. WPARKER will be rolling off the project at the end of the year, so please end-date his responsibilities accordingly. These changes are approved for both development environments and all three CRP instances.

After loading the necessary information into a "staging table" (actually a simply-formatted external table), away we go:

APPS@R12VIS(11.1.0.7)>@ebs_bulkresp validate
Working with following records in table xxrespload:

USER_NAME	     APP	RESP_NAME			    END_DATE
-------------------- ---------- ----------------------------------- ---------------
SBERG		     FND	Application Developer		    NONE
SBERG		     FND	Functional Administrator	    NONE
SBERG		     JTF	Oracle Diagnostics Tool 	    NONE
WPARKER 	     FND	Application Developer		    31-DEC-2011
WPARKER 	     JTF	Oracle Diagnostics Tool 	    31-DEC-2011
YWATSON 	     FND	Application Developer		    NONE
YWATSON 	     JTF	Oracle Diagnostics Tool 	    NONE
Starting validation...
All users valid.
All app-responsibility pairs valid
Invoke script with APPLY to add responsibilities
APPS@R12VIS(11.1.0.7)>@ebs_bulkresp apply
Working with following records in table xxrespload:

USER_NAME	     APP	RESP_NAME			    END_DATE
-------------------- ---------- ----------------------------------- ---------------
SBERG		     FND	Application Developer		    NONE
SBERG		     FND	Functional Administrator	    NONE
SBERG		     JTF	Oracle Diagnostics Tool 	    NONE
WPARKER 	     FND	Application Developer		    31-DEC-2011
WPARKER 	     JTF	Oracle Diagnostics Tool 	    31-DEC-2011
YWATSON 	     FND	Application Developer		    NONE
YWATSON 	     JTF	Oracle Diagnostics Tool 	    NONE
Starting validation...
All users valid.
All app-responsibility pairs valid
Adding responsibilities...
User: YWATSON App: FND Resp: Application Developer
User: WPARKER App: FND Resp: Application Developer
User: SBERG App: FND Resp: Application Developer
User: SBERG App: FND Resp: Functional Administrator
User: YWATSON App: JTF Resp: Oracle Diagnostics Tool
User: WPARKER App: JTF Resp: Oracle Diagnostics Tool
User: SBERG App: JTF Resp: Oracle Diagnostics Tool
APPS@R12VIS(11.1.0.7)>@lresp ywatson
Currently active responsibilities for user ywatson

APP		     RESPNAME				      END_DATE
-------------------- ---------------------------------------- ---------------
FND		     Application Developer		      NONE
FND		     Application Diagnostics		      NONE
PSB		     Budget User - Accounting Dept, Progress  NONE
SQLGL		     General Ledger Super User, Progress S&L  NONE
ICX		     Internet Procurement, Progress S&L       NONE
JTF		     Oracle Diagnostics Tool		      NONE
SQLAP		     Payables Manager, Progress S&L	      NONE
ICX		     Preferences SSWA			      NONE
FND		     Workflow User, Progress S&L	      NONE
APPS@R12VIS(11.1.0.7)>@lresp sberg
Currently active responsibilities for user sberg

APP		     RESPNAME				      END_DATE
-------------------- ---------------------------------------- ---------------
FND		     Application Developer		      NONE
FND		     Application Diagnostics		      NONE
FND		     Functional Administrator		      NONE
IBE		     IBE_CUSTOMER			      NONE
JTF		     Oracle Diagnostics Tool		      NONE
ICX		     Preferences SSWA			      NONE
APPS@R12VIS(11.1.0.7)>@lresp wparker
Currently active responsibilities for user wparker

APP		     RESPNAME				      END_DATE
-------------------- ---------------------------------------- ---------------
FND		     Application Developer		      31-DEC-2011
FND		     Application Diagnostics		      31-DEC-2011
JTF		     Oracle Diagnostics Tool		      31-DEC-2011
ICX		     Preferences SSWA			      NONE
WMS		     Whse Mgmt Mobile User, Vision Operations NONE
		      (USA)

It's rare that you'll get a request that also includes the name of the application to which a responsibility belongs. If you don't keep a mental inventory of valid pairs of application and responsibility names (or maybe that's just me?), here's a quick way to look up that sort of info:

/*
iresp.sql
Contact: John Piwowar
Purpose: List useful identifier for EBS responsibilities
Notes: Provide fragment of responsbility name
*/

col responsibility_name for a50
col app_name for a10
col app_id for 999999
col resp_id for 999999

set pagesize 9999 verify off

select application_id app_id
     , a.application_short_name app_name
     , r.responsibility_id resp_id
     , r.responsibility_name
  from applsys.fnd_responsibility_tl r
  join applsys.fnd_application a using (application_id)
 where upper(r.responsibility_name) like upper('%&1%')
 order by responsibility_name
/

view raw iresp.sql This Gist brought to you by GitHub.

Post a Comment

Your email is never published nor shared. Required fields are marked *

*
*