Wednesday 10 June 2015

User Hook In Oracle APPS



Oracle has provided user hooks to implement custom logic or validation on standard processes.


For example: 
  1. Creating a element entry when creating an absence
  2. validating the DFF segments in Absence before creating a absence.
  3. Validation on creating EITs, Element entries, absence, termination of employee etc
Custom hook package (User Hook package) is a custom package where we write procedures for doing the customization. The user hook procedure should have same parameters as standard API module procedure which invokes the user hook.

Now lets go through the steps to attach a user hook. I am assuming the user hook for create absence process.

Step 1 : 
Get the Module id from table HR_API_MODULES. In my case the module name is like 'CREATE%ABSENCE%'. Hence I query for the module using the below query. 

SELECT * FROM hr_api_modules WHERE module_name LIKE 'CREATE%ABSENCE%';

I get the api_module_id as 1731.
---------------------------------------------------------------------------
Step 2:
Next I query for hook id in table hr_api_hooks for after process. Note that 'AP' means After Process hook and 'BP' isBefore Process hook.

SELECT * FROM hr_api_hooks WHERE api_module_id = 1731;

 I get  the api_hook_id  as 3840

--------------------------------------------------------------------------
Step 3:
If you know the Module name , hook package name and hook procedure , you can use the script below .
Script for attaching the hook:

Example 1:

set feedback off
set serverout on size 10000

Declare

   CURSOR c1_get_id IS
          SELECT md.module_name mod_nm, 
                 hk.api_hook_id hk_id, 
                 md.api_module_id mod_id,
                 hk.api_hook_type hk_type
          FROM   hr_api_hooks hk, hr_api_modules md
          WHERE  hk.api_module_id = md.api_module_id
          AND    md.module_name IN ('CREATE_PERSON_ABSENCE'
                                   , 'UPDATE_PERSON_ABSENCE'
                                    )
          AND    md.api_module_type = 'BP'
          AND    hk.api_hook_type in ( 'AP','BP')
   ;
--    
   CURSOR c2_get_seq_no IS
          SELECT MAX(hc.SEQUENCE) 
          FROM   hr_api_hook_calls hc
       ;
--
   CURSOR c_chk_call( hk_id IN NUMBER) IS
          SELECT hc.api_hook_call_id, 
                 hc.SEQUENCE, hc.enabled_flag, hc.call_package, 
                 hc.call_procedure, hc.object_version_number
          FROM   hr_api_hook_calls  hc
          WHERE  hc.api_hook_id = hk_id
          AND    hc.call_package='XXARJPER_NEGATIVE_BALANCE_PKG'
          AND    hc.enabled_flag = 'Y'
   ;
--
l_ai_bp_id              NUMBER;     
l_au_bp_id              NUMBER;
l_ai_ap_id              NUMBER;     
l_au_ap_id              NUMBER;
l_hk_type               VARCHAR2(10);       
l_hk_call_seq           NUMBER;
l_mod_id_ai_bp          NUMBER; 
l_mod_id_au_bp          NUMBER;
l_mod_id_ai_ap          NUMBER; 
l_mod_id_au_ap          NUMBER;
l_api_hook_call_id      NUMBER;
l_object_version_number NUMBER;
l_cur_hkcall_id         NUMBER;
l_cur_seqn              NUMBER;
l_cur_flag              VARCHAR2(1);
l_cur_pack              VARCHAR2(30);
l_cur_proc              VARCHAR2(30);
l_cur_num               NUMBER;
--   
BEGIN
--
FOR i IN c1_get_id LOOP 
    IF    i.mod_nm = 'CREATE_PERSON_ABSENCE' and i.hk_type='BP' THEN
          l_ai_bp_id := i.hk_id;
          l_mod_id_ai_bp := i.mod_id; 
    ELSIF i.mod_nm = 'UPDATE_PERSON_ABSENCE' and i.hk_type='BP' THEN
          l_au_bp_id := i.hk_id;
          l_mod_id_au_bp := i.mod_id;
    ELSIF i.mod_nm = 'CREATE_PERSON_ABSENCE' and i.hk_type='AP' THEN
          l_ai_ap_id := i.hk_id;
          l_mod_id_ai_ap := i.mod_id; 
    ELSIF i.mod_nm = 'UPDATE_PERSON_ABSENCE' and i.hk_type='AP' THEN
          l_au_ap_id := i.hk_id;
          l_mod_id_au_ap := i.mod_id;  
    END IF;
END LOOP;
--
OPEN c2_get_seq_no;
FETCH c2_get_seq_no INTO l_hk_call_seq;
CLOSE c2_get_seq_no;
--   
IF l_hk_call_seq > 2000 THEN
   l_hk_call_seq := (l_hk_call_seq + 1);
ELSE
   l_hk_call_seq := 2001;
END IF;
-- For create absence before process
OPEN c_chk_call(l_ai_bp_id);
FETCH c_chk_call INTO 

l_cur_hkcall_id,l_cur_seqn,l_cur_flag,l_cur_pack,l_cur_proc,l_cur_num;
CLOSE c_chk_call;
--
IF l_cur_hkcall_id IS NOT NULL THEN
   Hr_Api_Hook_Call_Api.delete_api_hook_call
                        (p_api_hook_call_id       => l_cur_hkcall_id,
                         p_object_version_number  => l_cur_num 
                         );
END IF;
--
l_api_hook_call_id := NULL; 
--
   Hr_Api_Hook_Call_Api.create_api_hook_call
     (p_effective_date           =>  TRUNC(SYSDATE),
      p_api_hook_id              =>  l_ai_bp_id,
      p_api_hook_call_type       =>  'PP',
      p_sequence                 =>  l_hk_call_seq,
      p_enabled_flag             =>  'Y' ,
      p_call_package             =>  'XXARJPER_NEGATIVE_BALANCE_PKG',
      p_call_procedure           =>  'SET_PROF_OPT_CREATE_PRC',
      p_api_hook_call_id         =>  l_api_hook_call_id,
      p_object_version_number    =>  l_object_version_number) ;   
--
Hr_Api_User_Hooks_Utility.create_hooks_one_module(l_mod_id_ai_bp); 
Hr_Api_User_Hooks_Utility.write_one_errors_report(l_mod_id_ai_bp);  
--
l_cur_hkcall_id := NULL;
l_cur_seqn      := NULL;
l_cur_flag      := NULL;
l_cur_pack      := NULL;
l_cur_proc      := NULL;
l_cur_num       := NULL;

-- For update absence before process

OPEN c_chk_call(l_au_bp_id);
FETCH c_chk_call INTO 

l_cur_hkcall_id,l_cur_seqn,l_cur_flag,l_cur_pack,l_cur_proc,l_cur_num;
CLOSE c_chk_call; 
--
IF l_cur_hkcall_id IS NOT NULL THEN
   Hr_Api_Hook_Call_Api.delete_api_hook_call
                        (p_api_hook_call_id       => l_cur_hkcall_id,
                         p_object_version_number  => l_cur_num 
                         );
END IF;                         
--
l_api_hook_call_id := NULL; 
--
   Hr_Api_Hook_Call_Api.create_api_hook_call
     (p_effective_date           =>  TRUNC(SYSDATE),
      p_api_hook_id              =>  l_au_bp_id,
      p_api_hook_call_type       =>  'PP',
      p_sequence                 =>  (l_hk_call_seq + 1),
      p_enabled_flag             =>  'Y' ,
      p_call_package             =>  'XXARJPER_NEGATIVE_BALANCE_PKG',
      p_call_procedure           =>  'SET_PROF_OPT_UPDATE_PRC',
      p_api_hook_call_id         =>  l_api_hook_call_id,
      p_object_version_number    =>  l_object_version_number) ;   
-- 
Hr_Api_User_Hooks_Utility.create_hooks_one_module(l_mod_id_au_bp); 
Hr_Api_User_Hooks_Utility.write_one_errors_report(l_mod_id_au_bp);
--
l_cur_hkcall_id := NULL;
l_cur_seqn      := NULL;
l_cur_flag      := NULL;
l_cur_pack      := NULL;
l_cur_proc      := NULL;
l_cur_num       := NULL;
-- For create absence after process
OPEN c_chk_call(l_ai_ap_id);
FETCH c_chk_call INTO 

l_cur_hkcall_id,l_cur_seqn,l_cur_flag,l_cur_pack,l_cur_proc,l_cur_num;
CLOSE c_chk_call;
--
IF l_cur_hkcall_id IS NOT NULL THEN
   Hr_Api_Hook_Call_Api.delete_api_hook_call
                        (p_api_hook_call_id       => l_cur_hkcall_id,
                         p_object_version_number  => l_cur_num 
                         );
END IF;
--
l_api_hook_call_id := NULL; 
--
   Hr_Api_Hook_Call_Api.create_api_hook_call
     (p_effective_date           =>  TRUNC(SYSDATE),
      p_api_hook_id              =>  l_ai_ap_id,
      p_api_hook_call_type       =>  'PP',
      p_sequence                 =>  (l_hk_call_seq + 2),
      p_enabled_flag             =>  'Y' ,
      p_call_package             =>  'XXARJPER_NEGATIVE_BALANCE_PKG',
      p_call_procedure           =>  'RESTRICT_NEG_BAL_CREATE_PRC',
      p_api_hook_call_id         =>  l_api_hook_call_id,
      p_object_version_number    =>  l_object_version_number) ;   
--
Hr_Api_User_Hooks_Utility.create_hooks_one_module(l_mod_id_ai_ap); 
Hr_Api_User_Hooks_Utility.write_one_errors_report(l_mod_id_ai_ap);  
--
l_cur_hkcall_id := NULL;
l_cur_seqn      := NULL;
l_cur_flag      := NULL;
l_cur_pack      := NULL;
l_cur_proc      := NULL;
l_cur_num       := NULL;

-- For update absence after process

OPEN c_chk_call(l_au_ap_id);
FETCH c_chk_call INTO 

l_cur_hkcall_id,l_cur_seqn,l_cur_flag,l_cur_pack,l_cur_proc,l_cur_num;
CLOSE c_chk_call; 
--
IF l_cur_hkcall_id IS NOT NULL THEN
   Hr_Api_Hook_Call_Api.delete_api_hook_call
                        (p_api_hook_call_id       => l_cur_hkcall_id,
                         p_object_version_number  => l_cur_num 
                         );
END IF;                         
--
l_api_hook_call_id := NULL; 
--
   Hr_Api_Hook_Call_Api.create_api_hook_call
     (p_effective_date           =>  TRUNC(SYSDATE),
      p_api_hook_id              =>  l_au_ap_id,
      p_api_hook_call_type       =>  'PP',
      p_sequence                 =>  (l_hk_call_seq + 3),
      p_enabled_flag             =>  'Y' ,
      p_call_package             =>  'XXARJPER_NEGATIVE_BALANCE_PKG',
      p_call_procedure           =>  'RESTRICT_NEG_BAL_UPDATE_PRC',
      p_api_hook_call_id         =>  l_api_hook_call_id,
      p_object_version_number    =>  l_object_version_number) ;   
-- 
Hr_Api_User_Hooks_Utility.create_hooks_one_module(l_mod_id_au_ap); 
Hr_Api_User_Hooks_Utility.write_one_errors_report(l_mod_id_au_ap);
-- 
--
COMMIT;
--    
END;    

--------------------

Example 2:-

-set serveroutput on size 1000000
DECLARE
--
ln_api_hook_call_id            number;
ln_object_version_number       number;
ln_api_hook_id                 number;
--
BEGIN
   --
   select ahk.api_hook_id
     into ln_api_hook_id
     from hr_api_hooks ahk, hr_api_modules ahm
    where ahm.module_name = 'CREATE_PERSON_ABSENCE'
      and ahm.api_module_type = 'BP'
      and ahk.hook_package = 'HR_PERSON_ABSENCE_BK1'
      and ahk.hook_procedure = 'CREATE_PERSON_ABSENCE_A'
      and ahk.api_hook_type = 'AP'               -- After Process
      and ahk.api_module_id = ahm.api_module_id;
   --
   -- insert a row into HR_API_HOOK_CALLS
   --
   hr_api_hook_call_api.create_api_hook_call
      (p_effective_date        => to_date('14-MAR-2014','DD-MON-YYYY')
      ,p_api_hook_id           => ln_api_hook_id
      ,p_api_hook_call_type    => 'PP'
      ,p_sequence              => 3029
      ,p_enabled_flag          => 'Y'
      ,p_call_package          => 'XXMUD_DISCIPLINARY_ACTION_PKG'--Custom Hook PKG
      ,p_call_procedure        => 'CREATE_UNAUTHORIZED_ABS_ENTRY'--Procedure for creating entries
      ,p_api_hook_call_id      => ln_api_hook_call_id
      ,p_object_version_number => ln_object_version_number);
   --
   DBMS_OUTPUT.PUT_LINE('Registered HOOK...'|| ln_api_hook_call_id );
   --
EXCEPTION
   WHEN OTHERS THEN
      DBMS_OUTPUT.PUT_LINE(SUBSTR('Error: '||SQLERRM,1,255));
END;

-------------------------------------------------------

Step 4:
Next step is to run the pre-processor to the hook. Without running the pre-processor the user hook will not work.

DECLARE
l_module_id  NUMBER; --Pass the module id
BEGIN
hr_api_user_hooks_utility.create_hooks_one_module (1282); 
END;

COMMIT;
--------------------------------------------------------
Step 5:
Next step is to verify if hook is registered.

SELECT * FROM hr_api_hook_calls 
WHERE call_package = 'XXMUD_DISCIPLINARY_ACTION_PKG';

If STATUS column is 'V' (Stands for Valid) and ENABLED_FLAG 'Y' then you have successfully registered the user hook.

----------------------------------------------------------------------------
Deleting User Hook:

DECLARE
ln_object_version_number       NUMBER;
ln_api_hook_call_id            NUMBER;
BEGIN

-- Get the api_hook_call_id and object_version_number
SELECT api_hook_call_id, object_version_number
  INTO ln_api_hook_call_id,ln_object_version_number
  FROM hr_api_hook_calls
 WHERE hook_call_package = 'XXMUD_DISCIPLINARY_ACTION_PKG'
   AND hook_procedure = 'CREATE_UNAUTHORIZED_ABS_ENTRY';

--API to delete the user hook
hr_api_hook_call_api.delete_api_hook_call
               (p_api_hook_call_id     =>    ln_api_hook_call_id, -- API_HOOK_CALL_ID
                p_object_version_number => ln_object_version_number );  -- Object_version_number
   DBMS_OUTPUT.PUT_LINE('DELETED HOOK...');
  
EXCEPTION
WHEN OTHERS THEN
      DBMS_OUTPUT.PUT_LINE(SUBSTR('Error: '||SQLERRM,1,255));
END;