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!
- 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.
- 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.
- 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_wrappedSET verify off feedback off
--Readability improvements. First time for everything.DEFINE src=&1DEFINE targ=&2
PROMPT Source user: &&srcPROMPT Target user: &&targPROMPT 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_respEND; --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_wrappedSET verify off feedback offcol user_name for a20col app for a10col end_date for a15col 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 a50col app_name for a10col app_id for 999999col 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/

