Tuesday, 11 November 2014

How To Send Notification To Multiple Users

How To Send Notification To Multiple Users

Oracle Workflow roles are stored in the database, in the Oracle Workflow directory service.
The performer can be an item type attribute that dynamically returns a role.
To send a single notification(FYI/Actionable) to multiple users we have to use Role attribute as performer of that notification. 
The attribute value must be the internal name of a role. 

Here we will discuss how can we send a single notification to multiple users.


Basic Requirement
Our old requirement looks like "when a person applies for a leave it should go his/her supervisor for approval.Approver must be able to provide his/her approval/rejection comments. The comments must be entered by the approver while rejecting the leave requisition.If he/she rejects the leave requisition without entering comments, then it should raise an application error.If he/she approves the requisition it should be optional.Approver must also be able to select theOrganization Leave Type from a list of values . If the leave gets rejected, don’t store any information in database.The approver should able to view/edit the person "Special Information Types" before approving the leave request.Before approving the sick leave request the approver must able to see the submitted medical document."

Now we will twist our original requirement- Instead of going to Supervisor for approval, all the leave request must go to the following identified people
  • ROLE1_APPR (User name of the person)
  • ROLE2_APPR (User name of the person)
  • ROLE3_APPR (User name of the person)
Solution Approach
First we will discuss about the changes that we need to do in workflow Definition.

A) Workflow Part
1) Load the latest workflow definition from database.
2) Create an item attribute of Type Role
 Internal Name:- XX_TEST_ROLE
 Display Name:- Test Role for Approval
 Type              :- Role


3) Now we have to add this newly created role to the performer of the notification (Initially it was Supervisor User Name). Now open the process (in our case it will be "Test Leave Approval Process"). Open the notification and go to the "Node" tab. Modify the performer.


4) Validate your design and save it to database.

B) Role Definition create Part
    Role can be defined and used in worklfow in two different ways.
          a) Create a Global Application Role
          b) Create database Adhoc Role

We will first discuss how can we define Role from Oracle Application and use the same in workflow.

a) Create a Global Application Role and its usages in workflow
    i) Go to UK HRMS Manage >> Transaction Maintanance >> Global Roles
   ii) Create your custom Global Role and add the desired users (Users must have a valid active fnd_user entry). Save your definition.

  
  iii) Our Application Role definition creation is complete now we have to set the value for our newly created Role attribute. The value of the Role attribute should be the
      name of the role. Workflow engine will take the names from the role and will send the notification automatically.
     We will set the value for Role attribute in our Trigger workflow procedure (Where we are setting the other attribute values)
      To set the Role attribute we need to call wf_engine.SetItemAttrText procedure. This will internally check for Role attribute(though this utility is for setting Text attribute) and perform the task accordingly.


   iv) Now compile the package and workflow is ready to trigger.


b) Create database Adhoc Role and usages
  Instead of creating a Global Role from application we can also create an database adhoc role and use the same to send notification.
   i)  Create a adhoc Role using wf_directory.CreateAdHocRole ultility. This will create a role in database,.


wf_directory.CreateAdHocRole(role_name                      in out varchar2,
                                              role_display_name        in out varchar2

      language                      in varchar2 default null,

     territory                         in varchar2 default null,

     role_description             in varchar2 default null,

     notification_preference   in varchar2 default ’MAILHTML’,

     role_users in varchar2    default null,

     email_address               in varchar2 default null,

     fax in varchar2               default null,

    status in varchar2           default ’ACTIVE’,

    expiration_date in           date default null,

    parent_orig_system        in varchar2 default null,

    parent_orig_system_id    in number default null,

    owner_tag                      in varchar2 default null
);


Enter the value for "role_name" (Must not be more than 320 character and keep the name in uppercase) and "role_display_name" parameter. Keep the other parameter to its default value.

If you want to make this role inactive after definite time period enter value for it (Ex:- for 365 days it should be sysdate+365. Means after 365 days role will be expired).

Here 
role_name             =>XX_TEST_ADHOC_ROLE
role_display_name=>Test Adhoc Role

 ii) Add the user to the newly created Role using wf_directory.AddUsersToAdHocRole utility.
wf_directory.AddUsersToAdHocRole(role_name in varchar2,
                                                     c in varchar2
                                                     )
Here 
role_name  => Internal name of the role 
role_name  => User name of the list of users that we need to add. The user name of the users must be either comma/space separated.
  
  iii) Now set the Role attribute value. The value of the Role attribute should be the Internal name of the adhoc role
      To set the Role attribute we need to call wf_engine.SetItemAttrText procedure. This will internally check for Role attribute(though this utility is for setting Text attribute) and perform the task accordingly.


   iv) Now compile procedure and workflow is ready to trigger.


Note:- 1) As we see we can define Role in two ways 1) Global Application Role 2) Database Adhoc Role.
              When we have a identified static list of approver/stakeholder to whom we need to send the notification we will use the Global Application 
              Role. It is easy to maintain.
              When we have a dynamic list of approver/stakeholder (if our requirement is something like send the notification to all the people who are in a
              particular grade) to whom we need to send the notification we will use the database adhoc Role.

2) If we check the "Expand Roles"(Double click on notification >> Notification Tab) check box it will send an individual copy of the notification message to each user in the role(The notification id will be different). The notification remains in a user’s notification queue until the user responds  or closes the notification.
Since this is a actionable notification and we are not using voting activity thus "First responder Win" happen.
If any of the role performer acts on the notification it will "Closed" in his/her queue. Whereas other will see it as "Cancelled".


3) Oracle Workflow does not support including the action history in a notification with the Expand Roles check box selected, which causes a separate copy of the notification to be sent to each user in the recipient role.



References:-                1) https://metalink.oracle.com
                                      2) Oracle Workflow Developer's Guide ( Release 12) B31433-04
                                      3) Oracle. Workflow API Reference Release 2.6.3.5 Part No. B12163–02
                         
Disclaimer:- This is a knowledge sharing site. This topic talks about a custom solution. Oracle Corporation may not provide you a support for any data corruption or any other problem in your custom code/problem arises because of the custom code. 
The author is not responsible for any kind of  system/data problem appears because of usages of this code.Reader/implementer must do it on his/her own risk/responsibility.Oracle Corporation may change API definition/usages. User must read the necessary documentation before using Oracle provided APIs.

Thursday, 6 November 2014

How to restrict Duplicate Customer Po Number on Sales Order Form

Steps to Restrict Two Duplicate_PO_Number On Sales Order Form :
Complete the following in TEST instance
1. Please open the sales order form

2. Navigate to: Help -> Diagnostic -> Custom Code -> Personalize

3. Create new sequence as cust_po_number
Select Function

In Condition Tab
Trigger Event: WHEN-VALIDATE-RECORD
Trigger Object: ORDER
Condition:
:Order.CUST_PO_NUMBER is not null and ((select 'Y' from oe_order_headers_all
where cust_po_number = :Order.cust_po_number and header_id <> :Order.header_id and
rownum=1)='Y')
Processing Mode:Not In Enter-query Mode

4. Select Action Tab
Seq: 1
Type: Message
Language: All
Enabled: Yes
Message Type: Warn
Message Text:
=’Duplicate Cust PO number exists is’||nvl(:Order.CUST_PO_NUMBER,’NULL’)

5. Save the changes and try to reproduce the issue.
6. If successful migrate solution to other instances

Thursday, 30 October 2014

OPM

Example 1: Creation of batch using the ‘RECIPE’ creation mode

alter session set nls_language=american;
set serveroutput on size 1000000;
DECLARE
p_api_version NUMBER DEFAULT gme_api_pub.api_version;
   p_validation_level NUMBER DEFAULT gme_api_pub.max_errors;
   p_init_msg_list BOOLEAN DEFAULT FALSE;
   P_batch_type NUMBER DEFAULT 0;
   p_orgn_code VARCHAR2(4) DEFAULT 'PR1';
   x_batch_header gme_batch_header%ROWTYPE;
   x_unallocated_material gme_api_pub.unallocated_materials_tab;
   x_message_count NUMBER;
   x_message_list VARCHAR2(100);
   x_return_status VARCHAR2(1);

   l_batch_header GME_BATCH_HEADER%ROWTYPE;
   l_msg_index_out NUMBER;
BEGIN
l_batch_header.plant_code :='PR1';
l_batch_header.batch_type :=p_batch_type;
l_batch_header.batch_no:= '';
l_batch_header.plan_start_date := '';
l_batch_header.plan_cmplt_date := '';
l_batch_header.due_date :=TO_DATE('10-AUG-2006 12:00:00','DD-MON-YYYY HH24:MI:SS');
l_batch_header.update_inventory_ind :='Y';
l_batch_header.recipe_validity_rule_id :=1239;
l_batch_header.wip_whse_code:='PR1';
fnd_global.apps_initialize(user_id =>1003637,
   resp_id => null,
   resp_appl_id => null);
gme_api_pub.create_batch(
p_api_version => p_api_version
,p_validation_level => p_validation_level
,p_init_msg_list => p_init_msg_list
,p_commit => true
,x_message_count => x_message_count
,x_message_list => x_message_list
,x_return_status => x_return_status
,p_batch_header => l_batch_header
,x_batch_header => x_batch_header
,p_batch_size => 10
,p_batch_size_uom => 'KG'
,p_creation_mode => 'RECIPE'
,p_recipe_id => null
,p_recipe_no => 'JGC_1'
,p_recipe_version => '301'
,p_product_no => null
,p_product_id => null
,p_ignore_qty_below_cap => true
,p_ignore_shortages => true
,x_unallocated_material =>x_unallocated_material );

gme_debug.display_messages(x_message_count);
dbms_output.put_line('x_message_count ='||TO_CHAR(x_message_count));
   dbms_output.put_line('x_message_list =
'||length(x_message_list)||SubStr(x_message_list,length(x_message_list)-20,20));
dbms_output.put_line('x_message_list ='||x_message_list);
   dbms_output.put_line('x_return_status ='||x_return_status);
   dbms_output.put_line('x_batch_header.batch_id= '||TO_CHAR(x_batch_header.batch_id));
   dbms_output.put_line(SubStr('x_batch_header.plant_code ='||x_batch_header.plant_code,1,255));
   dbms_output.put_line(SubStr('x_batch_header.batch_no ='||x_batch_header.batch_no,1,255));

END;
/
To find the Validity Rule Id, run this script
select r.recipe_no
,r.recipe_version
,r.recipe_id
,v.RECIPE_VALIDITY_RULE_ID
,v.orgn_code
,RECIPE_USE
,PREFERENCe
,START_DATe
,END_DATE
,MIN_QTY
,MAX_QTY
,STD_QTY
,ITEM_UM
from gmd_recipes r
,gmd_recipe_validity_rules v
where r.recipe_no='&recipe_no'
and r.recipe_version='&version'
and r.recipe_id=v.recipe_id
order by
v.preference

Remarks on Example 1

  1. Notice the ‘alter session set nls_language=american;’ command. This command is used to ensure that the language in the current SQL*Plus session matches one of the installed languages within the Application. It is always safe to set this to ‘american’.
  2. The ‘set serveroutput on size 1000000’ command is there to ensure that the 'dbms_out.put_line' messages can be seen on the screen. In real life examples, the user will probably use the 'utl_file.put_line' command in order to write these messages to a text log file.
  3. Note the combination of the Recipe Number and Version and the Validity Rule Id. The Validity Rule Id must be attached to the relevant Recipe, and both must be ‘Approved for General Use’. The Validity Rule must be effective on the relevant batch dates (in this case, the batch due date). To find the appropriate value of Validity Rule Id, run the script shown above.
  4. Note the Batch Size and UOM. These must be valid within the limits set by the Validity Rule being used.
  5. The ‘fnd_global.apps_initialize’ is used to set the application context. In the above example, the context is set according to the applications user whose id is 1003637. The relevant id can found for the required user by using this command: ‘select user_id from fnd_user where user_name='&user_name'; and entering the required username. The user selected should have a responsibility attached which allows creation of batches via the application.
  6. A full description of the Create Batch API and its parameters can be found in the User Guide pages 4-16 to 4-19.

Example 2: Creation of batch by Product

set serveroutput on size 1000000
alter session set nls_language=american;

DECLARE
x_message_count NUMBER;
x_message_list VARCHAR2(2000);
x_return_status VARCHAR2(1);
p_batch_header GME_BATCH_HEADER%rowtype;
x_batch_header GME_BATCH_HEADER%rowtype;
x_unallocated_material GME_API_PUB.UNALLOCATED_MATERIALS_TAB;
BEGIN
dbms_output.enable(100000);

fnd_global.apps_initialize( USER_ID =>1007882,
                      resp_id =>NULL,
                      resp_appl_id =>NULL);

p_batch_header.plant_code := 'PR1';
p_batch_header.batch_no := '';
p_batch_header.batch_type := 0;
p_batch_header.plan_start_date := TO_DATE('16-JULY-2006 12:00:00','DD-MON-YYYY HH24:MI:SS');
p_batch_header.wip_whse_code := '';

gme_api_pub.create_batch(
   p_api_version
   ,p_validation_level     =>  100
   ,p_init_msg_list        => TRUE
   ,p_commit               => FALSE
   ,x_message_count        => x_message_count
   ,x_message_list         => x_message_list
   ,x_return_status        => x_return_status
   ,p_batch_header         => p_batch_header
   ,x_batch_header         => x_batch_header
   ,p_batch_size           => 100
   ,p_batch_size_uom       => 'KG'
   ,p_creation_mode        => 'PRODUCT'
   ,p_recipe_id            => null
   ,p_recipe_no            => null
   ,p_recipe_version       => null
   ,p_product_no           => 'JGC_1'
   ,p_product_id           => NULL
   ,p_ignore_qty_below_cap => TRUE
   ,p_ignore_shortages     => TRUE
   ,x_unallocated_material => x_unallocated_material
   );

gme_debug.display_messages(x_message_count);
dbms_output.put_line('x_message_count = '||TO_CHAR(x_message_count));
dbms_output.put_line('x_message_list = '||x_message_list);
dbms_output.put_line('x_return_status = '||x_return_status);
dbms_output.put_line('x_batch_header.batch_id = '||TO_CHAR(x_batch_header.batch_id));
dbms_output.put_line(SubStr('x_batch_header.plant_code = '||x_batch_header.plant_code,1,255));
dbms_output.put_line(SubStr('x_batch_header.batch_no = '||x_batch_header.batch_no,1,255));
dbms_output.put_line('x_batch_header.batch_type = '||TO_CHAR(x_batch_header.batch_type));
dbms_output.put_line('x_batch_header.recipe_validity_rule_id ='||TO_CHAR(x_batch_header.recipe_validity_rule_id));
dbms_output.put_line('x_batch_header.formula_id = '||TO_CHAR(x_batch_header.formula_id));
dbms_output.put_line('x_batch_header.routing_id = '||TO_CHAR(x_batch_header.routing_id));
dbms_output.put_line('x_batch_header.plan_start_date = '||TO_CHAR(x_batch_header.plan_start_date, 'DD-MON-YYYY HH24:MI:SS'));
dbms_output.put_line('x_batch_header.due_date = '||TO_CHAR(x_batch_header.due_date, 'DD-MON-YYYY HH24:MI:SS'));
dbms_output.put_line('x_batch_header.plan_cmplt_date = '||TO_CHAR(x_batch_header.plan_cmplt_date, 'DD-MON-YYYY HH24:MI:SS'));
dbms_output.put_line('x_batch_header.batch_status = '||TO_CHAR(x_batch_header.batch_status));
dbms_output.put_line(SubStr('x_batch_header.wip_whse_code ='||x_batch_header.wip_whse_code,1,255));
dbms_output.put_line(SubStr('x_batch_header.poc_ind = '||x_batch_header.poc_ind,1,255));
dbms_output.put_line(SubStr('x_batch_header.update_inventory_ind = '||x_batch_header.update_inventory_ind,1,255));
dbms_output.put_line('x_batch_header.last_update_date='||TO_CHAR(x_batch_header.last_update_date, 'DD-MON-YYYY HH24:MI:SS'));
dbms_output.put_line('x_batch_header.last_updated_by = '||TO_CHAR(x_batch_header.last_updated_by));
dbms_output.put_line('x_batch_header.creation_date = '||TO_CHAR(x_batch_header.creation_date, 'DD-MON-YYYY HH24:MI:SS'));
dbms_output.put_line('x_batch_header.created_by = '||TO_CHAR(x_batch_header.created_by));
dbms_output.put_line('x_batch_header.last_update_login ='||TO_CHAR(x_batch_header.last_update_login));
dbms_output.put_line('x_batch_header.delete_mark = '||TO_CHAR(x_batch_header.delete_mark));
dbms_output.put_line('x_batch_header.text_code = '||TO_CHAR(x_batch_header.text_code));
dbms_output.put_line('x_batch_header.automatic_step_calculation ='||TO_CHAR(x_batch_header.automatic_step_calculation));IF x_unallocated_material.count > 0 THEN
   FOR i IN x_unallocated_material.first..x_unallocated_material.last LOOP
      IF x_unallocated_material.exists(i) THEN         dbms_output.put_line('x_unallocated_material('||to_char(i)||').batch_id ='||TO_CHAR(x_unallocated_material(i).batch_id));
         dbms_output.put_line(SubStr('x_unallocated_material('||to_char(i)||').batch_no ='||x_unallocated_material(i).batch_no,1,255));
         dbms_output.put_line('x_unallocated_material('||to_char(i)||').material_detail_id='||TO_CHAR(x_unallocated_material(i).material_detail_id));
         dbms_output.put_line('x_unallocated_material('||to_char(i)||').line_type='||TO_CHAR(x_unallocated_material(i).line_type));
         dbms_output.put_line('x_unallocated_material('||to_char(i)||').line_no='||TO_CHAR(x_unallocated_material(i).line_no));
         dbms_output.put_line('x_unallocated_material('||to_char(i)||').item_id='||TO_CHAR(x_unallocated_material(i).item_id));
         dbms_output.put_line(SubStr('x_unallocated_material('||to_char(i)||').item_no ='||x_unallocated_material(i).item_no,1,255));
         dbms_output.put_line('x_unallocated_material('||to_char(i)||').alloc_qty='||TO_CHAR(x_unallocated_material(i).alloc_qty));
         dbms_output.put_line('x_unallocated_material('||to_char(i)||').unalloc_qty ='||TO_CHAR(x_unallocated_material(i).unalloc_qty));
         dbms_output.put_line(SubStr('x_unallocated_material('||to_char(i)||').alloc_uom='||x_unallocated_material(i).alloc_uom,1,255));
      END IF;
   END LOOP;
END IF;

gme_api_pub.save_batch(
   p_batch_header         => p_batch_header,
   X_return_status        => x_return_status,
   p_commit               =>TRUE);

EXCEPTION
   WHEN OTHERS THEN   dbms_output.put_line('Error '||TO_CHAR(SQLCODE)||': '||SQLERRM);
RAISE;
   END;
/

Remarks on Example 2

  1. The remarks made against Example 1 remain valid for Example 2. In addition please note the following.
  2. In this example, the product to be output is specified and the quantity to be output is likewise specified. Once again, however, there must be a valid Recipe Validity Rule that can used to create the batch. See remark 3 on example 1 for more details.
  3. Notice that when the API creating the batch  (gme_api_pub.create_batch) is run, the transaction is not committed. Instead this is done at a later stage using the 'gme_api_pub.save_batch' API.  The user may care to use this technique when, for example, creating and releasing a batch in a single transaction.

Example 3: Creation using Output Quantity

set serveroutput on
alter session set nls_language=american;
DECLARE
x_message_count NUMBER;
x_message_list VARCHAR2(2000);
x_return_status VARCHAR2(1);
p_batch_header GME_BATCH_HEADER%rowtype;
x_batch_header GME_BATCH_HEADER%rowtype;
x_unallocated_material GME_API_PUB.UNALLOCATED_MATERIALS_TAB;
l_msg_index_out NUMBER;
BEGIN
-- Following line is set the output buffer
   dbms_output.enable(100000); /**************************************************************************************
* Enter your user id below
* NOTE: To retrieve user_id Log on to the application.
* Select to Production Supervisor responsibility
* Navigate Others->Session parameters
* You will see your USER ID and Username on the screen.
* fnd_profile.initialize(user_id);
**************************************************************************************/
-- set the applications context
   fnd_global.apps_initialize( USER_ID =>1007882,
      resp_id =>NULL,
      resp_appl_id =>NULL
   );

/**************************************************************************************
* (R) = Required, (O) = Optional
* enter your own plant_code (R), recipe_validity_Rule_Id(O), batch_type (R)
* specify batch_no (R if testing manual doc ordering)
* dates are optional;format of the dates is
* TO_DATE('10-OCT-2001 12:00:00','DD-MON-YYYY HH24:MI:SS');
* In esence you supply the date and appropriate format.
* wip_whse_code is optional.
* update_inventory_ind is optional and should only be used if the plant is a Lab.
* 'Y'=Transactions to be created, 'N'=transactions should not be created.
**************************************************************************************/

p_batch_header.plant_code := 'PR1'; p_batch_header.batch_no := '';
-- batch_type .. 10=fpo, 0=batch
p_batch_header.batch_type := 0;
p_batch_header.recipe_validity_rule_id := null;
p_batch_header.plan_start_date := ''; --TO_DATE('30-JUL-2006 12:00:00','DD-MON-YYYY HH24:MI:SS');
p_batch_header.due_date := ''; ---TO_DATE('');
p_batch_header.plan_cmplt_date := ''; --TO_DATE('');
p_batch_header.wip_whse_code := '';
-- -- you have the option to set the below param to N when testing Lab batch creation
p_batch_header.update_inventory_ind := 'Y';

/**************************************************************************************
* Now call the stored program
* Leave first 4 parameters as they are
* enter p_batch_size and p_batch_size_uom are required
* if p_creation_mode is 'PRODUCT', 'OUTPUT', 'INPUT')
* enter p_creation_mode = RECIPE, PRODUCT,OUTPUT,INPUT
* p_batch_header_validity_rule_id OR p_recipe_id OR (p_recipe_no and p_recipe_version)
* are required if p_creation_mode = 'RECIPE','INPUT','OUTPUT'
* p_batch_header_validity_rule_id OR p_product_id OR p_product_no
* are required if p_creation_mode = 'PRODUCT'
* If p_ignore_shortages id passed as FALSE then in case of shortages
* and profile option GMI:Allow negative inventory is set to Warning or Error
* Batch will not be created and the x_unallocated procedure will be populated
* with the material lines causing shortages.
* p_ignore_qty_below_cap: If set to FALSE, then if the process quantity of any of
* the resources in the batch is less than the minimum capacity for that resource
* batch will not be created.
**************************************************************************************/

gme_api_pub.create_batch(
   ,p_api_version => 1
   ,p_validation_level => 100
   ,p_init_msg_list => TRUE
   ,p_commit => TRUE
   ,x_message_count => x_message_count
   ,x_message_list => x_message_list
   ,x_return_status => x_return_status
   ,p_batch_header => p_batch_header
   ,x_batch_header => x_batch_header
   ,p_batch_size => 1000
   ,p_batch_size_uom => 'KG'
   ,p_creation_mode => 'OUTPUT'
   ,p_recipe_id => NULL
   ,p_recipe_no => 'JGC_1'
   ,p_recipe_version => '301'
   ,p_product_no => 'JGC_1'
   ,p_product_id => NULL
   ,p_ignore_qty_below_cap => TRUE
   ,p_ignore_shortages => TRUE
   ,x_unallocated_material => x_unallocated_material
);

/***************************************************************************************
* Following line can be uncommented, if there are multiple messages return by the
* API, and you want to see all those messages. These will be written to the Log file
* If generated by the API.
***************************************************************************************/
-- gme_debug.display_messages(x_message_count);

/***************************************************************************************
* Output the results
* One of the following will be the outcome of the API call( x_return_status)
* S - Success
* E - Error
* U - Unexpected Error
* N - Items failed auto allocation
* V - Inventory shortage exists
***************************************************************************************/
IF x_return_status <> FND_API.g_ret_sts_success THEN
   IF X_message_count = 1 THEN
   DBMS_OUTPUT.PUT_LINE('Error:'||X_message_list);
   ELSE
   FOR i IN 1..x_message_count LOOP
      FND_MSG_PUB.get (p_msg_index => i
      ,p_data => X_message_list
      ,p_msg_index_out => l_msg_index_out);
      DBMS_OUTPUT.PUT_LINE('Error: '||X_message_list);
   END LOOP;
END IF;

ELSE

dbms_output.put_line('x_message_count = '||TO_CHAR(x_message_count));
dbms_output.put_line('x_message_list = '||length(x_message_list)||SubStr(x_message_list,length(x_message_list)-20,20));
dbms_output.put_line('x_return_status = '||x_return_status);
dbms_output.put_line('x_batch_header.batch_id = '||TO_CHAR(x_batch_header.batch_id));
dbms_output.put_line(SubStr('x_batch_header.plant_code = '||x_batch_header.plant_code,1,255));
dbms_output.put_line(SubStr('x_batch_header.batch_no = '||x_batch_header.batch_no,1,255));
dbms_output.put_line('x_batch_header.batch_type = '||TO_CHAR(x_batch_header.batch_type));
dbms_output.put_line('x_batch_header.recipe_validity_rule_id = '||TO_CHAR(x_batch_header.recipe_validity_rule_id));
dbms_output.put_line('x_batch_header.formula_id = '||TO_CHAR(x_batch_header.formula_id));
dbms_output.put_line('x_batch_header.routing_id = '||TO_CHAR(x_batch_header.routing_id));
dbms_output.put_line('x_batch_header.plan_start_date = '||TO_CHAR(x_batch_header.plan_start_date, 'DD-MON-YYYY HH24:MI:SS'));
dbms_output.put_line('x_batch_header.due_date = '||TO_CHAR(x_batch_header.due_date, 'DD-MON-YYYY HH24:MI:SS'));
dbms_output.put_line('x_batch_header.plan_cmplt_date = '||TO_CHAR(x_batch_header.plan_cmplt_date, 'DD-MON-YYYY HH24:MI:SS'));
dbms_output.put_line('x_batch_header.batch_status = '||TO_CHAR(x_batch_header.batch_status));
dbms_output.put_line(SubStr('x_batch_header.wip_whse_code = '||x_batch_header.wip_whse_code,1,255));
dbms_output.put_line(SubStr('x_batch_header.poc_ind = '||x_batch_header.poc_ind,1,255));
dbms_output.put_line(SubStr('x_batch_header.update_inventory_ind = '||x_batch_header.update_inventory_ind,1,255));
dbms_output.put_line('x_batch_header.last_update_date = '||TO_CHAR(x_batch_header.last_update_date, 'DD-MON-YYYY HH24:MI:SS'));
dbms_output.put_line('x_batch_header.last_updated_by = '||TO_CHAR(x_batch_header.last_updated_by));
dbms_output.put_line('x_batch_header.creation_date = '||TO_CHAR(x_batch_header.creation_date, 'DD-MON-YYYY HH24:MI:SS'));
dbms_output.put_line('x_batch_header.created_by = '||TO_CHAR(x_batch_header.created_by));
dbms_output.put_line('x_batch_header.last_update_login = '||TO_CHAR(x_batch_header.last_update_login));
dbms_output.put_line('x_batch_header.delete_mark = '||TO_CHAR(x_batch_header.delete_mark));
dbms_output.put_line('x_batch_header.text_code = '||TO_CHAR(x_batch_header.text_code));
dbms_output.put_line('x_batch_header.automatic_step_calculation = '||TO_CHAR(x_batch_header.automatic_step_calculation));
IF x_unallocated_material.count > 0 THEN
   FOR i IN x_unallocated_material.first..x_unallocated_material.last LOOP
      IF x_unallocated_material.exists(i) THEN
         dbms_output.put_line('x_unallocated_material('||to_char(i)||').batch_id = '||TO_CHAR(x_unallocated_material(i).batch_id));
         dbms_output.put_line(SubStr('x_unallocated_material('||to_char(i)||').batch_no = '||x_unallocated_material(i).batch_no,1,255));
         dbms_output.put_line('x_unallocated_material('||to_char(i)||').material_detail_id = '||TO_CHAR(x_unallocated_material(i).material_detail_id));
         dbms_output.put_line('x_unallocated_material('||to_char(i)||').line_type = '||TO_CHAR(x_unallocated_material(i).line_type));
         dbms_output.put_line('x_unallocated_material('||to_char(i)||').line_no = '||TO_CHAR(x_unallocated_material(i).line_no));
         dbms_output.put_line('x_unallocated_material('||to_char(i)||').item_id = '||TO_CHAR(x_unallocated_material(i).item_id));
         dbms_output.put_line(SubStr('x_unallocated_material('||to_char(i)||').item_no = '||x_unallocated_material(i).item_no,1,255));
         dbms_output.put_line('x_unallocated_material('||to_char(i)||').alloc_qty = '||TO_CHAR(x_unallocated_material(i).alloc_qty));
         dbms_output.put_line('x_unallocated_material('||to_char(i)||').unalloc_qty = '||TO_CHAR(x_unallocated_material(i).unalloc_qty));
         dbms_output.put_line(SubStr('x_unallocated_material('||to_char(i)||').alloc_uom = '||x_unallocated_material(i).alloc_uom,1,255));
      END IF;
   END LOOP;
END IF;
END IF;

EXCEPTION
WHEN OTHERS THEN
dbms_output.put_line('Error '||TO_CHAR(SQLCODE)||': '||SQLERRM);
RAISE;
END;
/

Remarks on example 3

  1. See Remarks for both Example 1 and Example 2.  As in Example 1, the Recipe and Version numbers are specified and like Example 2, the Product is specified. This example differs from the others in that it allows the user to control both the recipe used and the output quantity. It basically provides the same functionality as using the ‘Total Output’ tab when creating a batch in the application.

Example 4:  Create, Release and Save Batch

alter session set nls_language=american;
set serveroutput on size 100000;

DECLARE
l_batch_header            gme_batch_header%ROWTYPE;
x_batch_header            gme_batch_header%ROWTYPE;
p_batch_header            gme_batch_header%ROWTYPE;
x_message_count            NUMBER;
x_message_list            VARCHAR2(2000);
x_unallocated_material    gme_api_pub.unallocated_materials_tab;
x_return_status            VARCHAR2(1);
l_material_detail        gme_material_details%ROWTYPE;
x_material_detail        gme_material_details%ROWTYPE;
p_values_tab            gme_api_pub.field_values_tab;
l_field                     gme_api_pub.p_field;
l_unallocated_material    gme_api_pub.unallocated_materials_tab;
l_msg_index_out NUMBER;
BEGIN
-- Set the applications context
fnd_global.apps_initialize( USER_ID =>1007882,
                               resp_id =>NULL,                               resp_appl_id =>NULL);
l_batch_header.plant_code            :='PR1';
l_batch_header.batch_type             := 0;
l_batch_header.plan_start_date             := SYSDATE;
l_batch_header.plan_cmplt_date             := SYSDATE;
l_batch_header.due_date             := SYSDATE;
l_batch_header.update_inventory_ind         := 'Y';
l_batch_header.wip_whse_code             := 'PR1';
l_batch_header.recipe_validity_rule_id := 1239;

--
-- CREATE THE BATCH
--
gme_api_pub.create_batch  (p_api_version         => 1
            ,p_validation_level         => 1000
            ,p_init_msg_list         => TRUE
            ,p_commit             => FALSE
            ,x_message_count         => x_message_count
            ,x_message_list         => x_message_list
            ,x_return_status         => x_return_status
            ,p_batch_header         => l_batch_header
            ,x_batch_header         => x_batch_header
            ,p_batch_size         => 10
            ,p_batch_size_uom         => 'KG'               
            ,p_creation_mode         => 'RECIPE'
            ,p_recipe_id             => NULL                                     
            ,p_recipe_no             => 'JGC_1'
            ,p_recipe_version         => '301'    
            ,p_product_no         => NULL     
            ,p_product_id         => NULL       
            ,p_ignore_qty_below_cap     => TRUE
            ,p_ignore_shortages         => TRUE
            ,x_unallocated_material     => x_unallocated_material);

IF x_return_status <> FND_API.g_ret_sts_success THEN
      IF X_message_count = 1 THEN
            DBMS_OUTPUT.PUT_LINE('Error:'||X_message_list);
            ELSE
            FOR i IN 1..x_message_count LOOP
                        FND_MSG_PUB.get (p_msg_index => i
                        ,p_data => X_message_list
                        ,p_msg_index_out => l_msg_index_out);
                        DBMS_OUTPUT.PUT_LINE('Error: '||X_message_list);
            END LOOP;
      END IF;
ELSE

      dbms_output.put_line('x_message_count = '||TO_CHAR(x_message_count));
      dbms_output.put_line('x_message_list = length'||length(x_message_list)||' Contents ' ||substr(x_message_list,1,200));
      dbms_output.put_line('x_return_status = '||x_return_status);
      dbms_output.put_line('x_batch_header.batch_id = '||TO_CHAR(x_batch_header.batch_id));
      dbms_output.put_line(SubStr('x_batch_header.plant_code = '||x_batch_header.plant_code,1,255));
      dbms_output.put_line(SubStr('x_batch_header.batch_no = '||x_batch_header.batch_no,1,255));
      dbms_output.put_line('x_batch_header.batch_type = '||TO_CHAR(x_batch_header.batch_type));
      dbms_output.put_line('x_batch_header.recipe_validity_rule_id = '||TO_CHAR(x_batch_header.recipe_validity_rule_id));
      dbms_output.put_line('x_batch_header.formula_id = '||TO_CHAR(x_batch_header.formula_id));
      dbms_output.put_line('x_batch_header.routing_id = '||TO_CHAR(x_batch_header.routing_id));
      dbms_output.put_line('x_batch_header.plan_start_date = '||TO_CHAR(x_batch_header.plan_start_date, 'DD-MON-YYYY HH24:MI:SS'));
      dbms_output.put_line('x_batch_header.due_date = '||TO_CHAR(x_batch_header.due_date, 'DD-MON-YYYY HH24:MI:SS'));
      dbms_output.put_line('x_batch_header.plan_cmplt_date = '||TO_CHAR(x_batch_header.plan_cmplt_date, 'DD-MON-YYYY HH24:MI:SS');
      dbms_output.put_line('x_batch_header.batch_status = '||TO_CHAR(x_batch_header.batch_status));

      l_batch_header.batch_type               := 0;
      l_batch_header.batch_id                 := x_batch_header.batch_id;
      l_batch_header.plan_start_date          := SYSDATE;
      l_batch_header.plan_cmplt_date          := SYSDATE;
      l_batch_header.due_date                 := SYSDATE;
      l_batch_header.update_inventory_ind     := 'Y';
      l_batch_header.actual_start_date        := SYSDATE;

      ---
      --- RELEASE IT
      ---
       gme_api_pub.release_batch  (p_api_version        => 1
            ,p_validation_level        => 1000
            ,p_init_msg_list        => TRUE
            ,p_commit            => FALSE
            ,x_message_count        => x_message_count
            ,x_message_list        => x_message_list
            ,x_return_status        => x_return_status
            ,p_batch_header        => l_batch_header
            ,x_batch_header        => x_batch_header
            ,p_ignore_shortages        => TRUE
            ,x_unallocated_material     => l_unallocated_material
            ,p_ignore_unalloc         => TRUE);

      IF x_return_status = FND_API.g_ret_sts_success THEN
            dbms_output.put_line('Batch Released');
            p_batch_header.batch_id                 := x_batch_header.batch_id;
      --
      -- SAVE THE CHANGES
      --
            gme_api_pub.save_batch(
            p_batch_header         => p_batch_header,
            X_return_status        => x_return_status,
            p_commit               =>TRUE);
      else 
            dbms_output.put_line('Batch Release failed');
            IF X_message_count = 1 THEN
               DBMS_OUTPUT.PUT_LINE('Error:'||X_message_list);
            ELSE
               FOR i IN 1..x_message_count LOOP                   FND_MSG_PUB.get (p_msg_index => i
                   ,p_data => X_message_list
                   ,p_msg_index_out => l_msg_index_out);
                   DBMS_OUTPUT.PUT_LINE('Error: '||X_message_list);
               END LOOP;
            end if;      end if;
end if;

EXCEPTION
      WHEN OTHERS THEN
         dbms_output.put_line('Error '||TO_CHAR(SQLCODE)||': '||SQLERRM);
RAISE;

END;
/

Remarks on example 4

    This script creates, releases and saves a batch. Note that when the batch is created and released, these changes are not committed (that is to say, 'p_commit' is set to False). This enables the user to treat both batch creation and release as part of the same transaction and commit the changes if both operations succeed.

Example 5: Create a batch and insert an additional material line

alter session set nls_language=american;
set serveroutput on size 100000
DECLARE
l_batch_header gme_batch_header%ROWTYPE;
x_batch_header gme_batch_header%ROWTYPE;
p_batch_header gme_batch_header%ROWTYPE;
x_message_count NUMBER;
x_message_list VARCHAR2(2000);
x_unallocated_material gme_api_pub.unallocated_materials_tab;
x_return_status VARCHAR2(1);
l_material_detail gme_material_details%ROWTYPE;
x_material_detail gme_material_details%ROWTYPE;
p_values_tab gme_api_pub.field_values_tab;
l_field gme_api_pub.p_field;
l_unallocated_material gme_api_pub.unallocated_materials_tab;
l_msg_index_out NUMBER;
BEGIN
-- Set the applications context
fnd_global.apps_initialize( USER_ID =>1007882,
                               resp_id =>NULL,
                               resp_appl_id =>NULL);

l_batch_header.plant_code :='PR1';
l_batch_header.batch_type := 0;
l_batch_header.plan_start_date := SYSDATE;
l_batch_header.plan_cmplt_date := SYSDATE;
l_batch_header.due_date := SYSDATE;
l_batch_header.update_inventory_ind := 'Y';
l_batch_header.wip_whse_code := 'PR1';
l_batch_header.recipe_validity_rule_id := 1239;

--
-- CREATE THE BATCH
--
gme_api_pub.create_batch (p_api_version => 1
     ,p_validation_level => 1000
     ,p_init_msg_list => TRUE
     ,p_commit => FALSE
     ,x_message_count => x_message_count
     ,x_message_list => x_message_list
     ,x_return_status => x_return_status
     ,p_batch_header => l_batch_header
     ,x_batch_header => x_batch_header
     ,p_batch_size => 10
     ,p_batch_size_uom => 'KG'
     ,p_creation_mode => 'RECIPE'
     ,p_recipe_id => NULL
     ,p_recipe_no => 'JGC_1'
     ,p_recipe_version => '301'
     ,p_product_no => NULL
     ,p_product_id => NULL
     ,p_ignore_qty_below_cap => TRUE
     ,p_ignore_shortages => TRUE
     ,x_unallocated_material => x_unallocated_material);

IF x_return_status <> FND_API.g_ret_sts_success THEN
     DBMS_OUTPUT.PUT_LINE('Error:'||X_message_list);
ELSE
     dbms_output.put_line('x_message_count = '||TO_CHAR(x_message_count));
     dbms_output.put_line('x_message_list = lenght '||length(x_message_list)||' Contents ' ||substr(x_message_list,1,200));
     dbms_output.put_line('x_return_status = '||x_return_status);
     dbms_output.put_line('x_batch_header.batch_id = '||TO_CHAR(x_batch_header.batch_id));
     dbms_output.put_line(SubStr('x_batch_header.plant_code = '||x_batch_header.plant_code,1,255));
     dbms_output.put_line(SubStr('x_batch_header.batch_no = '||x_batch_header.batch_no,1,255));
     dbms_output.put_line('x_batch_header.batch_type = '||TO_CHAR(x_batch_header.batch_type));
     dbms_output.put_line('x_batch_header.recipe_validity_rule_id = '||TO_CHAR(x_batch_header.recipe_validity_rule_id));
     dbms_output.put_line('x_batch_header.formula_id = '||TO_CHAR(x_batch_header.formula_id));
     dbms_output.put_line('x_batch_header.routing_id = '||TO_CHAR(x_batch_header.routing_id));
     dbms_output.put_line('x_batch_header.plan_start_date = '||TO_CHAR(x_batch_header.plan_start_date, 'DD-MON-YYYY HH24:MI:SS'));
     dbms_output.put_line('x_batch_header.due_date = '||TO_CHAR(x_batch_header.due_date, 'DD-MON-YYYY HH24:MI:SS'));
     dbms_output.put_line('x_batch_header.plan_cmplt_date = '||TO_CHAR(x_batch_header.plan_cmplt_date, 'DD-MON-YYYY HH24:MI:SS');
     dbms_output.put_line('x_batch_header.batch_status = '||TO_CHAR(x_batch_header.batch_status));

     l_material_detail.batch_id :=x_batch_header.batch_id;
     l_material_detail.line_no :=2;
     l_material_detail.item_id :=712;
     l_material_detail.plan_qty :=100;
     l_material_detail.scale_type :=1;
     l_material_detail.alloc_ind :=0;
     l_material_detail.item_um :='LB';
     l_material_detail.line_type :=-1;
     l_material_detail.cost_alloc :=0;
     l_material_detail.contribute_yield_ind :='Y';
     l_material_detail.scrap_factor :=0;
     l_material_detail.phantom_type :=0;
     p_batch_header := x_batch_header;

--
-- INSERT INGREDIENT LINE
--
gme_api_pub.insert_material_line (p_api_version =>1
     ,p_validation_level =>100
     ,p_init_msg_list =>FALSE
     ,p_commit =>FALSE
     ,x_message_count =>x_message_count
     ,x_message_list =>x_message_list
     ,x_return_status =>x_return_status
     ,p_material_detail =>l_material_detail
     ,p_batchstep_no =>NULL
     ,x_material_detail =>x_material_detail);
     IF x_return_status = FND_API.g_ret_sts_success THEN 
          dbms_output.put_line('Material line inserted for item id '||x_material_detail.item_id);

         --
         -- SAVE THE CHANGES
         --
         gme_api_pub.save_batch(
              p_batch_header => p_batch_header,
              X_return_status => x_return_status,
             p_commit =>TRUE);

     else
              dbms_output.put_line('Material line insertion failed');
              DBMS_OUTPUT.PUT_LINE('Error:'||X_message_list);
     end if;
end if;

EXCEPTION
     WHEN OTHERS THEN
          dbms_output.put_line('Error '||TO_CHAR(SQLCODE)||': '||SQLERRM);
RAISE;

END;
/

Remarks on Example 5

This is similar to Example 4 except that this time, once the batch is created, a new ingredient line is added. The changes are then saved via the save batch API.

Example 6: Insert_line_allocation API

set serverout on size 100000
alter session set nls_language=american;

DECLARE
--
x_message_count NUMBER;
x_message_list VARCHAR2(2000);
x_return_status VARCHAR2(2000);
p_material_details gme_material_details%ROWTYPE;
x_material_details gme_material_details%ROWTYPE;
p_batch_header gme_batch_header%ROWTYPE;
x_def_tran_row gme_inventory_txns_gtmp%ROWTYPE;
x_tran_row gme_inventory_txns_gtmp%ROWTYPE;
x_unallocated_materials gme_api_pub.unallocated_materials_tab;
x_unallocated_items gme_unallocated_items_gtmp%rowtype;
p_tran_row gme_inventory_txns_gtmp%ROWTYPE;
--
x_msg_index number;
x_msg_data varchar2(200);
l_message_count NUMBER;
l_message_list VARCHAR2(200);

BEGIN
-- Following line is set the output buffer
DBMS_OUTPUT.enable(20000);

-- Line below required if formula security is used. It sets the security context.
fnd_global.apps_initialize( USER_ID =>1007882,
                               resp_id =>NULL,
                               resp_appl_id =>NULL
                               );
--
p_material_details.material_detail_id := 22188;
p_batch_header.batch_id := 4354;
--
/**/
p_tran_row.doc_id := 4354;
p_tran_row.material_detail_id := 22188;
p_tran_row.whse_code := 'PR1';
p_tran_row.location := '1';
p_tran_row.lot_id := 7571;
p_tran_row.alloc_qty := 100;
p_tran_row.trans_qty := 100;
p_tran_row.completed_ind := 0;
p_tran_row.trans_date := sysdate;
p_tran_row.reason_code := null;

/**************************************************************************************
* Now call the stored program *
* Leave first 4 parameters as they are *
**************************************************************************************/
gme_api_pub.insert_line_allocation(p_api_version => 1
     p_validation_level => 1000
     ,p_init_msg_list => TRUE
     ,p_commit => TRUE
     ,p_tran_row => p_tran_row
     ,p_lot_no => null
     ,p_sublot_no => null
     ,p_create_lot => TRUE
     ,p_ignore_shortage => FALSE
     ,p_scale_phantom => FALSE
     ,x_material_detail => p_material_details
     ,x_tran_row => x_tran_row
     ,x_def_tran_row => x_def_tran_row
     ,x_message_count => l_message_count
     ,x_message_list => l_message_list
     ,x_return_status => x_return_status);
IF x_return_status = FND_API.g_ret_sts_success THEN
     DBMS_OUTPUT.put_line('doc_id = '||x_tran_row.doc_id);
     DBMS_OUTPUT.put_line('material_detail_id = '||x_tran_row.material_detail_id);
     DBMS_OUTPUT.put_line('trans_id = '||x_tran_row.trans_id);
     DBMS_OUTPUT.put_line('line_type = '||x_tran_row.line_type);
     DBMS_OUTPUT.put_line('whse_code = '||x_tran_row.whse_code);
     DBMS_OUTPUT.put_line('location = '||x_tran_row.location); 
     DBMS_OUTPUT.put_line('alloc_qty = '||x_tran_row.alloc_qty);
     DBMS_OUTPUT.put_line('trans_qty = '||x_tran_row.trans_qty); 
     DBMS_OUTPUT.put_line('trans_qty2 = '||x_tran_row.trans_qty2);
     DBMS_OUTPUT.put_line('completed_ind = '||x_tran_row.completed_ind);
     DBMS_OUTPUT.put_line('trans_date = '||x_tran_row.trans_date);

else
     DBMS_OUTPUT.put_line('x_message_count = '||TO_CHAR(L_message_count));
     DBMS_OUTPUT.put_line(SubStr('x_message_list = '||l_message_list,1,255));
     DBMS_OUTPUT.put_line(SubStr('x_return_status = '||x_return_status,1,255));

end if;

EXCEPTION
     WHEN OTHERS THEN
          DBMS_OUTPUT.put_line('Error '||TO_CHAR(SQLCODE)||': '||SQLERRM);
RAISE;
END;
/

Remarks on Example 6

    This will create a line allocation for either an ingredient or product line. Note this API can be used in conjunction with the gme_api_pub.insert_material_line API to both add and allocate a new material line. Once again the gme_api_pub.save_batch procedure can be used to save both transactions once the user is sure there are no errors.

Example 7: Close (Complete) Batch

alter session set nls_language=american;
set serveroutput on size 1000000
DECLARE
v_batch_header apps.gme_batch_header%ROWTYPE;
x_batch_header apps.gme_batch_header%ROWTYPE;
v_message_count number;
v_message_list VARCHAR2(1024);
v_unallocated_material apps.gme_api_pub.unallocated_materials_tab;
v_return_status VARCHAR2(1);
BEGIN
fnd_global.apps_initialize( USER_ID =>1007882,
                               resp_id =>NULL,
                               resp_appl_id =>NULL);

v_batch_header.batch_id := 4355; 
v_batch_header.ACTUAL_CMPLT_DATE := sysdate;

gme_api_pub.certify_batch (
     p_init_msg_list => TRUE
     ,p_commit => false
     ,x_message_count => v_message_count
     ,x_message_list => v_message_list
     ,x_return_status => v_return_status
     ,p_del_incomplete_manual => TRUE
     ,p_ignore_shortages => TRUE
     ,p_batch_header => v_batch_header
     ,x_batch_header => x_batch_header
     ,x_unallocated_material => v_unallocated_material);

IF v_return_status = 'S' THEN
     dbms_output.put_line( 'Certify succeeded');
     gme_api_pub.save_batch(x_batch_header, v_return_status); 
     dbms_output.put_line( 'save_batch: status='|| v_return_status); 
     commit;
else
     dbms_output.put_line( 'Certify failed status= '|| v_return_status);
     DBMS_OUTPUT.PUT_LINE('Error: '||v_message_list); 
     rollback; 
end if; 

EXCEPTION
    WHEN OTHERS THEN
         dbms_output.put_line('Error '||TO_CHAR(SQLCODE)||': '||SQLERRM);
RAISE;

END;
/

Remarks on example 7

Bear in mind the state the batch must be in before it can completed - that is, the quantity of the output product should have been recorded and the ingredients allocated. If the user has any doubts about this, it is suggested that they create, release, allocate and attempt to complete a batch in the Application. If this can be done in the application then a batch in the same state should be able to be completed using the API. Notice also in this example the commit is done at the end of the script i.e. after the save batch.

Example 8: Release, Record Usage and Complete step

alter session set nls_language=american;
set serveroutput on size 1000000

DECLARE
v_message_count number;
v_message_list VARCHAR2(1024);
v_return_status VARCHAR2(1);
v_batch_header gme_batch_header%ROWTYPE;
x_batch_header gme_batch_header%ROWTYPE;
v_batch_step gme_batch_steps%ROWTYPE;
x_batch_step gme_batch_steps%ROWTYPE;
l_release boolean :=false;
l_usage boolean :=false;
l_certify boolean :=false;
v_unallocated_material gme_api_pub.unallocated_materials_tab;
l_batchstep_id number;
l_batch_id number;
x_poc_trans_id number;
BEGIN
fnd_global.apps_initialize( USER_ID =>1007882,
                               resp_id =>NULL,
                               resp_appl_id =>NULL);
---
--- Get batch details
----
    select * into v_batch_header 
    from gme_batch_header
    where batch_id=4362;
---
--- release step 10
---
    v_batch_step.batchstep_no := 10; 
    v_batch_step.batch_id := v_batch_header.batch_id;
    v_batch_step.actual_start_date := v_batch_header.actual_start_date;

    gme_api_pub.release_step (
        p_init_msg_list => TRUE
        ,p_commit => FALSE
        ,p_batch_step => v_batch_step
        ,x_message_count => v_message_count
        ,x_message_list => v_message_list
        ,x_return_status => v_return_status
        ,x_batch_step => x_batch_step
        ,x_unallocated_material => v_unallocated_material
        ,p_ignore_shortages => TRUE
        ,p_ignore_unalloc => TRUE);

    if v_return_status = 'S' then 
        l_release := true;
        l_batch_id :=x_batch_step.batch_id;
        l_batchstep_id := x_batch_step.batchstep_id;
        dbms_output.put_line( 'Release step succeeded');
    else
        dbms_output.put_line( 'release_step: status='|| v_return_status||', message='||v_message_list);
        rollback;
    end if;
---
--- Record resource usage
---
    if l_release = true then
        for c1 in (select batchstep_resource_id
            , resources
            ,activity 
            from
            gme_batch_step_resources r
            ,gme_batch_step_activities a
            where 
            r.batchstep_id=x_batch_step.batchstep_id 
            and a.batchstep_id=r.batchstep_id
            and a.batch_id=x_batch_step.batch_id
            and r.batchstep_activity_id=a.batchstep_activity_id)
        loop
        gme_api_pub.update_actual_rsrc_usage(
            p_init_msg_list => TRUE
            ,p_commit => FALSE
            ,p_batchstep_rsrc_id => c1.batchstep_resource_id
            ,p_plant_code => v_batch_header.plant_code
            ,p_batch_no => v_batch_header.batch_no
            ,p_batchstep_no => x_batch_step.batchstep_no
            ,p_activity => c1.activity
            ,p_resource => c1.resources
            ,p_trans_date => v_batch_header.actual_start_date 
            ,p_start_date => v_batch_header.actual_start_date 
            ,p_end_date => sysdate
            ,p_usage => 1.0
            ,p_reason_code => ''
            ,x_message_count => v_message_count
            ,x_message_list => v_message_list
            ,x_return_status => v_return_status
            ,x_poc_trans_id => x_poc_trans_id);
            dbms_output.put_line( 'update_actual_rsrc_usage: status='|| v_return_status||', 
            message='||v_message_list);
        end loop; 
    end if;
    if v_return_status = 'S' then 
        l_usage := true;
    else
        rollback;
    end if;
---
--- Complete step
---
    IF l_usage = true then
        gme_api_pub.certify_step(
            p_init_msg_list => TRUE
            ,p_commit => FALSE
            ,x_message_count => v_message_count
            ,x_message_list => v_message_list
            ,x_return_status => v_return_status
            ,p_batch_step => v_batch_step
            ,x_batch_step => x_batch_step
            ,x_unallocated_material => v_unallocated_material
            ,p_del_incomplete_manual => FALSE
            ,p_ignore_shortages => TRUE);

        if v_return_status = 'S' then 
                l_certify := true; 
             --- 
             --- Save the changes
             --- 
            gme_api_pub.save_batch(x_batch_header, v_return_status); 
            dbms_output.put_line( 'save_batch: status='|| v_return_status); 
            if v_return_status = 'S' then 
                commit;
            else 
                rollback;
            end if; 
        else
            dbms_output.put_line( 'Certify step: status='|| v_return_status||', message='||v_message_list); 
            rollback; 
        end if; 
    end if;

EXCEPTION
    WHEN OTHERS THEN
    dbms_output.put_line('Error '||TO_CHAR(SQLCODE)||': '||SQLERRM);
RAISE;

END;
/

Remarks on Example 8

This example releases a batch step, records actual resource usage and then completes the step. Assuming all operations are successful, the changes are then saved using the save batch API and the changes are committed.