Posts By: Phani Adivi


Warning: count(): Parameter must be an array or an object that implements Countable in /var/www/wp-content/themes/WpPremium/author.php on line 78

Create Customer using create_cust_account API

The following script helps to create a Customer with basic information (mandatory information) in Oracle Applications TCA Architecture.

DECLARE
    l_cust_account_rec       hz_cust_account_v2pub.cust_account_rec_type;
    l_org_rec                hz_party_v2pub.organization_rec_type;
    l_customer_profile_rec   hz_customer_profile_v2pub.customer_profile_rec_type;
    l_account_name           hz_cust_accounts.account_name%TYPE;

    l_msg_count              NUMBER;
    l_msg_data               VARCHAR2 (4000);
    l_err_msg                VARCHAR2 (4000);
    l_return_status          VARCHAR2 (1);
    l_cust_account_id        NUMBER;
    l_account_number         hz_cust_accounts.account_number%TYPE;
    l_party_id               hz_cust_accounts.party_id%TYPE;
    l_party_number           hz_parties.party_id%TYPE;
    l_cust_acct_profile_id   hz_customer_profiles.cust_account_profile_id%TYPE;
BEGIN
    l_cust_account_rec.account_name := 'Test Customer 98765432';
    l_cust_account_rec.account_number := '98765432';
    l_cust_account_rec.customer_type := 'R';
    l_cust_account_rec.attribute1 := SYSDATE;
    l_cust_account_rec.created_by_module := 'HZ_CPUI';
    l_org_rec.organization_name := 'Test Customer 98765432';

    hz_cust_account_v2pub.create_cust_account (
        p_init_msg_list          => fnd_api.g_true,
        p_cust_account_rec       => l_cust_account_rec,
        p_organization_rec       => l_org_rec,
        p_customer_profile_rec   => l_customer_profile_rec,
        p_create_profile_amt     => fnd_api.g_true,
        x_return_status          => l_return_status,
        x_msg_count              => l_msg_count,
        x_msg_data               => l_msg_data,
        x_cust_account_id        => l_cust_account_id,
        x_account_number         => l_account_number,
        x_party_id               => l_party_id,
        x_party_number           => l_party_number,
        x_profile_id             => l_cust_acct_profile_id);

    DBMS_OUTPUT.put_line ('API Return status is: ' || l_return_status);
    
    IF l_return_status <> 'S'
    THEN 
        DBMS_OUTPUT.put_line ('Error message is: ' || SUBSTR (l_msg_data, 200));
    END IF; 
    
    IF l_return_status = 'S'
    THEN
        BEGIN
            SELECT account_name
              INTO l_account_name
              FROM hz_cust_accounts
             WHERE account_number = '98769876';

            DBMS_OUTPUT.put_line (
                   'Customer Account created with Account number: '
                || l_account_number
                || ' and Cust Account ID: '
                || l_cust_account_id
                || ' and Account Name: '
                || l_account_name);
        EXCEPTION
            WHEN OTHERS
            THEN
                DBMS_OUTPUT.put_line (
                    'Error during deriving Account Name: ' || SQLERRM);
        END;
    END IF;
EXCEPTION
    WHEN OTHERS
    THEN
        DBMS_OUTPUT.put_line ('Error during creating account: ' || SQLERRM);
END;

The DMBS Output is shown as below:

Upon running the script above, a Customer is created. The customer can be found in Hz_Cust_accounts table.

Below is the query to check the customer and some important columns in the Hz_Cust_Accounts table.

SELECT cust_account_id,
       party_id,
       account_number,
       status,
       customer_type,
       account_name,
       created_by_module,
       h.*
  FROM hz_cust_accounts h
 WHERE account_number = '98765432';

 

 


Warning: count(): Parameter must be an array or an object that implements Countable in /var/www/wp-content/themes/WpPremium/author.php on line 78

Customer, Party, Party Sites, Customer Sites, Site Uses

In this post, we shall look at the details of Creating a Customer and the tables affecting it.

Navigation: AR Superuser -> Customers -> Customers

In the OAF page that opened, fill out the following details:

Organization Name, Account Number, Profile Class: Default (Oracle provides this profile class) and Account Site Address: Give a correct address that exists as per HZ_GEOGRAPHIES hierarchy.

Upon creating the account, a record now exists in the Hz_Cust_Accounts table

In the example below – created a Customer with Account number – 10820181

HZ_CUST_ACCOUNTS

SELECT *
  FROM hz_cust_accounts
 WHERE account_number = '10820181';

As can be seen a Party is also created with a party Id – 51284811

HZ_PARTIES

SELECT PARTY_ID, PARTY_NUMBER, PARTY_NAME, PARTY_TYPE, STATUS, JGZZ_FISCAL_CODE, H.* 
  FROM hz_parties H
 WHERE party_id = 51284811;
--- JGZZ_FISCAL_CODE holds the tax payer id.

HZ_PARTY_SITES

 SELECT PARTY_SITE_ID, PARTY_ID, LOCATION_ID, PARTY_SITE_NUMBER, STATUS, CREATED_BY_MODULE, H.*
  FROM HZ_PARTY_SITES H
 WHERE PARTY_ID = 51284811;

HZ_LOCATIONS

Get the location_id from the Hz_Party_Sites

SELECT location_id,
       ORIG_SYSTEM_REFERENCE,
       country,
       address1,
       address2,
       city,
       state,
       county,
       postal_code,
       h.*
  FROM hz_locations h
 WHERE location_id = 38779066;

HZ_CUST_ACCT_SITES_ALL

Hz_Cust_Acct_Sites_all table details about the customer account sites across all the operating units. A Customer site has addresses where the customer intends to do business at. A Customer can have more than one site and across more than one operating unit. The site signifies whether the site is a Primary Bill_To or a Primary Ship_To site. Join between the Customer table and Customer site table is the Cust_acct_id.

A Bill_To_Flag indicates if it is a bill to site.

  • Y for a Bill-To site
  • P for the primary Bill-To site
  • N for a site that is not a Bill-To site.

A Ship_to_Flag indicates if it is a Ship to site.

  • Y for a Ship-To site
  • P for the primary Ship-To site
  • N for a site that is not a Ship-To site
SELECT cust_acct_site_id,
       cust_account_id,
       party_site_id,
       status,
       bill_to_flag,
       h.*
  FROM hz_cust_acct_sites_all h
 WHERE cust_account_id = 46149842;

HZ_CUST_SITE_USES_ALL

The HZ_CUST_SITE_USES_ALL table stores business purposes assigned to customer account sites, for example Bill-To, Ship-To, and
Statements. Each customer account site can have one or more purposes. This table is a child of the HZ_CUST_ACCT_SITES_ALL table, with the foreign key CUST_ACCT_SITE_ID. The HZ_CUST_SITE_USES_ALL table also stores operating unit identifier, though the HZ_CUST_ACCT_SITES_ALL table itself stores the operating unit for customer account sites.

SELECT site_use_id,
       cust_acct_site_id,
       site_use_code,
       primary_flag     status,
       location,
       h.*
  FROM HZ_CUST_SITE_USES_ALL h
 WHERE cust_acct_site_id = 49348980;

 


Warning: count(): Parameter must be an array or an object that implements Countable in /var/www/wp-content/themes/WpPremium/author.php on line 78

TCA Architecture

Understanding TCA Architecture in Oracle Applications: 

Oracle Trading Community Architecture (TCA) is a data model that allows us to understand and manage the complex interlinkages between the parties, customers, sites, their uses and relationships. This information is maintained in the TCA Registry, which is the single source of trading community information for Oracle E-Business Suite applications. These applications, as well as TCA itself, provide user interfaces, batch data entry functionality, and other features for you to view, create, and update Registry information.

What is the key difference between a Party and an Account? 

As explained well here in this link, the concept of a Party came into existence when Oracle bought Siebel. The main difference between a Party and a Customer is that a Party can be an entity that may/may not have bought an item/service from the company. But an account is an entity that has made a purchase in some form from the company already.

Consider a scenario where you walk into a TV store to look at the new TV models and their price. There is a fair chance that you may/may not buy a TV that fits your taste and budget and leave. In the scenario where you did not buy, but the store manager takes some basic information from you like your name, address, email id, telephone number and other details like budget and tv model. The manager then enters this information into a database where you will become a Party. This information is used by the company to send communication and marketing emails about new models and prices that may entice a Party to become a Customer. All this activity is maintained in CRM Module where the target is to make the Party a Customer and maintain relationship with the same. (In all the above scenario, you are still a Party).

Now, that you got all those emails and messages, and you got satisfied and you bought a TV from that store. Now, you became a Customer. All the transaction details related to this deal are stored using the account number. Thus, in Oracle, all the details in Accounts Receivables use the Customer account number for report generation purposes.

Can a Party exist without being a Customer?

Yes. A person may just receive information from the company but did not buy any item yet, in this scenario that person/entity is still a Party and not a Customer.

What is the use of a Party without being a Customer? 

The CRM Module main motto is marketing and it is the process of identifying, creating and retaining a Customer. The CRM looks at a Party (that is not a customer yet) as a Potential Customer and tries to reach him/her/entity with marketing emails and information. A Party becomes a Customer and more Customers bring in revenue.

Where is a Party Used? 

When a Party is created, it creates a record in HZ_PARTIES table. This is primarily used by the CRM Module.

 


Warning: count(): Parameter must be an array or an object that implements Countable in /var/www/wp-content/themes/WpPremium/author.php on line 78

Create Table Scripts

Create Table script is one of the initial scripts to be learnt when learning PL/SQL. In this post, we shall look at the variations of creating a table script with few columns and data in it.

1: Basic CREATE Table. 

Syntax:  CREATE TABLE TABLE_NAME  (COLUMN_NAME_1 DATATYPE,  COLUMN_NAME_2 DATATYPE);

CREATE TABLE xx_test_table
(
    emp_id       NUMBER,
    emp_name     VARCHAR2 (200),
    join_date    DATE
);

INSERT INTO xx_test_table
     VALUES (1001, 'Shane Smith', '01-AUG-2001');

INSERT INTO xx_test_table
     VALUES (1002, 'Steve Warne', '02-AUG-2002');

SELECT * FROM xx_test_table;

Result:

2: Create Table with Default Value for columns. 

A default value can be provided for columns in a table in case a value is not provided in the Insert statement. In that scenario, the default value is populated in that column.

CREATE TABLE xx_test_table
(
    emp_id       NUMBER (6, 2),
    emp_name     VARCHAR2 (200) DEFAULT 'John Doe',
    join_date    DATE
);

INSERT INTO xx_test_table
     VALUES (1001, 'Shane Smith', '01-AUG-2001');

INSERT INTO xx_test_table
     VALUES (1002, 'Steve Warne', '02-AUG-2002');

INSERT INTO xx_test_table (emp_id, join_date)
     VALUES (1003, '02-AUG-2002');


SELECT * FROM xx_test_table;

Result:

3. NOT NULL on a column

During creation of a table, it can be mentioned that a particular column cannot have NULL values in it. In the example below, the column Join_date has a NOT NULL condition on it.

CREATE TABLE xx_test_table
(
    emp_id       NUMBER (6, 2),
    emp_name     VARCHAR2 (200) DEFAULT 'John Doe',
    join_date    DATE NOT NULL
);

INSERT INTO xx_test_table
     VALUES (1001, 'Shane Smith', '01-AUG-2001');

INSERT INTO xx_test_table
     VALUES (1002, 'Steve Warne', '02-AUG-2002');

INSERT INTO xx_test_table (emp_id, join_date)
     VALUES (1003, '02-AUG-2002');

INSERT INTO xx_test_table
     VALUES (1004, 'Mike Hannigan', NULL);

Thus, in the last line, when we try to insert a NULL value in to the Join_date column, we get the following error message.

ORA-01400: cannot insert NULL into (“APPS”.”XX_TEST_TABLE”.”JOIN_DATE”)”

4. Defining Constraints on Columns

A constraint is an optional clause while creating a table. A constraint is a rule according to which the data is loaded into that table. A constraint can be defined on column level as well as at table level.

Usually the constraints follow the following naming standards: <tablename>_<column_name>_<constraint_abbreviation>

Column Level Constraints: These constraints refer to a single column on the table and do not specify a column name.

  • Primary Key: Indicates that data in each row of the column can be identified uniquely using this primary key. The identified column must be defined as NOT NULL. In the example below, the emp_id column is being assigned as a Primary Key.
CREATE TABLE xx_test_table
(
    emp_id       NUMBER (6, 2) NOT NULL CONSTRAINT xx_test_emp_id_PK PRIMARY KEY,
    emp_name     VARCHAR2 (200) DEFAULT 'John Doe',
    join_date    DATE NOT NULL
);

INSERT INTO xx_test_table
     VALUES (1001, 'Shane Smith', '01-AUG-2001');

INSERT INTO xx_test_table
     VALUES (1002, 'Steve Warne', '02-AUG-2002');

INSERT INTO xx_test_table (emp_id, join_date)
     VALUES (1003, '02-AUG-2002');

INSERT INTO xx_test_table
     VALUES (1001, 'Mike Hannigan', '01-SEP-2002');

Thus, when a duplicate value is being inserted in the 4th line, a Primary Key Violation is being thrown.

  • Unique: Indicates that the values in the column must be unique. In the example below, the emp_id column has a Unique constraint on it.
CREATE TABLE xx_test_table
(
    emp_id       NUMBER (6, 2) CONSTRAINT xx_test_emp_id_UQ UNIQUE,
    emp_name     VARCHAR2 (200) DEFAULT 'John Doe',
    join_date    DATE NOT NULL
);

INSERT INTO xx_test_table
     VALUES (1001, 'Shane Smith', '01-AUG-2001');

INSERT INTO xx_test_table
     VALUES (1002, 'Steve Warne', '02-AUG-2002');

INSERT INTO xx_test_table (emp_id, join_date)
     VALUES (1003, '02-AUG-2002');

INSERT INTO xx_test_table
     VALUES (1001, 'Mike Hannigan', '01-SEP-2002');

Thus, when we try to insert a value of 1001 into emp_id, when the value already exists, then the command throws an error.

  • CHECK: The CHECK constraint specifies certain rules for the data in the column according to which the user likes the data to be. In the example below, the CHECK constraint makes sure that the Salary is always above 5000.
CREATE TABLE xx_test_table
(
    emp_id       NUMBER (6, 2),
    emp_name     VARCHAR2 (200) DEFAULT 'John Doe',
    join_date    DATE NOT NULL, 
    salary       NUMBER (8,2) CONSTRAINT sal_chk CHECK (salary > 5000)
);

INSERT INTO xx_test_table
     VALUES (1001, 'Shane Smith', '01-AUG-2001', 8500);

INSERT INTO xx_test_table
     VALUES (1002, 'Steve Warne', '02-AUG-2002', 7200);

INSERT INTO xx_test_table
     VALUES (1001, 'Mike Hannigan', '01-SEP-2002', 4800);

Thus, when we try to insert the third line above, with a salary of 4800 (less than 5000), the insert command throws an error.

 


Warning: count(): Parameter must be an array or an object that implements Countable in /var/www/wp-content/themes/WpPremium/author.php on line 78

Cursors – Explicit Cursors

What are Cursors? 

A cursor is a pointer to a private SQL area that stores information about the processing of a SELECT statement or a DML statement (Insert, Update or Delete). In layman terms, a Cursor is used to fetch (SELECT) a particular dataset from a much larger dataset which can then be used for data manipulation using other DML Operations like Insert, Update or Delete.

There are two types of Cursors: Implicit Cursors and Explicit Cursors.

In this post, we will discuss mainly about Explicit Cursors.

Explicit Cursors: Unlike Implicit cursors, application developers have control over the life cycle of the explicit cursors by manually performing the OPEN and FETCH operations and then CLOSE the Cursor.

A Cursor is defined in the declarative part of the PL/SQL Block and the following are three stages are part of the Cursor life cycle.

  • OPEN
    •  Open: Database allocates a private work area in the user’s session memory for cursor processing.
    •  Parse SQL: Validates the SQL query for syntax and privileges.
    •  Bind SQL: Provides an input value to the bind variables that are part of the query.
    •  Executes the SQL Statement (that has the required conditions in the WHERE condition).
  • FETCH
    • Fetches the data from the parsed SQL statement. Fetches the data into a block variable and increments the record pointer.
  • CLOSE
    • Closes the cursor and releases the memory back to System Global Area (SGA).

Consider the following dataset to demonstrate an example of an Explicit cursor. Create a sample table and insert data into the table.

CREATE TABLE xx_test_cursor
(
    emp_name         VARCHAR2 (200),
    emp_id           NUMBER,
    emp_join_date    DATE,
    manager_name     VARCHAR2 (200)
);

INSERT INTO xx_test_cursor
     VALUES ('John',1001,SYSDATE - 30, 'Frank');

INSERT INTO xx_test_cursor
     VALUES ('Graeme', 1002, SYSDATE - 40, 'Frank');

INSERT INTO xx_test_cursor
     VALUES ('Shawn', 1003, SYSDATE - 50, 'Frank');

INSERT INTO xx_test_cursor
     VALUES ('Jonty', 1004, SYSDATE - 60, 'Frank');

INSERT INTO xx_test_cursor
     VALUES ('Mike', 1007, SYSDATE - 60, 'Frank');
     
INSERT INTO xx_test_cursor
     VALUES ('Peter', 1008, SYSDATE - 60, 'Justin');

INSERT INTO xx_test_cursor
     VALUES ('Jason', 1009, SYSDATE - 60, 'Justin');

INSERT INTO xx_test_cursor
     VALUES ('Glenn', 1010, SYSDATE - 60, 'Justin');

INSERT INTO xx_test_cursor
     VALUES ('Adam', 1011, SYSDATE - 60, 'Justin');
     
INSERT INTO xx_test_cursor
     VALUES ('Chris', 1012, SYSDATE - 60, 'Justin');

COMMIT;
SELECT * FROM xx_test_cursor
order by emp_id;

EMP_NAME	EMP_ID	EMP_JOIN_DATE	        MANAGER_NAME
John	         1,001	08/27/2018 19:01:10	Frank
Graeme	         1,002	08/17/2018 19:01:11	Frank
Shawn	         1,003	08/07/2018 19:01:12	Frank
Jonty	         1,004	07/28/2018 19:01:13	Frank
DARELL	         1,005	07/28/2018 19:23:58	Frank
Mike	         1,007	07/31/2018 01:50:23	Justin
Peter	         1,008	07/31/2018 01:50:24	Justin
Jason	         1,009	07/31/2018 01:50:26	Justin
Glenn	         1,010	07/31/2018 01:50:27	Justin
Adam	         1,011	07/31/2018 01:50:28	Justin

The following code will display the data from the table in the required format.

Cursor – FETCH

  • Code from Line Number 2 to 5 is where the code is being defined in the declare section.
  • Code in line number 13 opens the cursor.
  • Code in line number 14 fetches the data from the SQL cursor into a record variable that has a cursor type datatype.
  • Once all the data is fetched, code in line number 15 closes the Cursor.
  • Code from Line 17 to 29, checks if there is any data in the record variable, then loops through the record using an index, and displays the data in the required format, and then closes the loop.
DECLARE
    CURSOR c_data 
    IS
        SELECT emp_name, emp_join_date, manager_name 
        FROM xx_test_cursor;

    TYPE rec_data_type IS TABLE OF c_data%ROWTYPE
         INDEX BY BINARY_INTEGER;
         
    l_data rec_data_type;
    
BEGIN
    OPEN c_data;
    FETCH c_data BULK COLLECT INTO l_data;
    CLOSE c_data;

    IF l_data.COUNT > 0
    THEN
        FOR i IN l_data.FIRST .. l_data.LAST
        LOOP
            DBMS_OUTPUT.put_line (
                   'Employee: '
                || l_data (i).emp_name
                || ' Join date is: '
                || l_data (i).emp_join_date
                || ' and Manager is: '
                || l_data (i).manager_name);
        END LOOP;
    END IF;
EXCEPTION
    WHEN OTHERS
    THEN
        DBMS_OUTPUT.put_line ('Exception when processing data: ' || SQLERRM);
END;
Corresponding Dbms_output is: 

Employee: Mike Join date is: 31-JUL-18 and Manager is: Justin
Employee: Peter Join date is: 31-JUL-18 and Manager is: Justin
Employee: Jason Join date is: 31-JUL-18 and Manager is: Justin
Employee: Glenn Join date is: 31-JUL-18 and Manager is: Justin
Employee: Adam Join date is: 31-JUL-18 and Manager is: Justin
Employee: John Join date is: 27-AUG-18 and Manager is: Frank
Employee: Graeme Join date is: 17-AUG-18 and Manager is: Frank
Employee: Shawn Join date is: 07-AUG-18 and Manager is: Frank
Employee: Jonty Join date is: 28-JUL-18 and Manager is: Frank
Employee: DARELL Join date is: 28-JUL-18 and Manager is: Frank

FOR LOOP: 

DECLARE
    CURSOR c_data 
    IS 
    SELECT emp_name, emp_join_date, manager_name
    FROM xx_test_cursor;
BEGIN
    FOR rec_data IN c_data
    LOOP
        DBMS_OUTPUT.put_line (
               'Employee: '
            || rec_data.emp_name
            || ' Join date is: '
            || rec_data.emp_join_date
            || ' and Manager is: '
            || rec_data.manager_name);
    END LOOP;
EXCEPTION
    WHEN OTHERS
    THEN
        DBMS_OUTPUT.put_line ('Exception when processing data: ' || SQLERRM);
END;
Corresponding dbms_output is: 

Employee: Mike Join date is: 31-JUL-18 and Manager is: Justin
Employee: Peter Join date is: 31-JUL-18 and Manager is: Justin
Employee: Jason Join date is: 31-JUL-18 and Manager is: Justin
Employee: Glenn Join date is: 31-JUL-18 and Manager is: Justin
Employee: Adam Join date is: 31-JUL-18 and Manager is: Justin
Employee: John Join date is: 27-AUG-18 and Manager is: Frank
Employee: Graeme Join date is: 17-AUG-18 and Manager is: Frank
Employee: Shawn Join date is: 07-AUG-18 and Manager is: Frank
Employee: Jonty Join date is: 28-JUL-18 and Manager is: Frank
Employee: DARELL Join date is: 28-JUL-18 and Manager is: Frank

 


Warning: count(): Parameter must be an array or an object that implements Countable in /var/www/wp-content/themes/WpPremium/author.php on line 78

Cursors – Implicit Cursors

What are Cursors? 

A cursor is a pointer to a private SQL area, temporarily allocated in the session’s User Global Area (UGA), that stores information about the processing of a SELECT statement or a DML statement (Insert, Update or Delete). In layman terms, a Cursor is used to fetch (SELECT) a particular dataset from a much larger dataset which can then be used for data manipulation using other DML Operations like Insert, Update or Delete.

There are two types of Cursors: Implicit Cursors and Explicit Cursors.

In this post, we will discuss mainly about Implicit Cursors.

Implicit Cursors: Whenever you execute a simple SELECT statement or a DML Statement like INSERT, UPDATE or DELETE statement, Oracle database creates an implicit cursor to process these statements. In such scenarios, Oracle opens a Cursor, fetches the row and then closes the Cursor after completing the sql operation. In Implicit cursors, the cursor life cycle is maintained by the Oracle Database.

Consider the following example. Create a table called xx_test_cursor and insert 4 records into that table. (below code).

CREATE TABLE xx_test_cursor
(
    emp_name         VARCHAR2 (200),
    emp_id           NUMBER,
    emp_join_date    DATE,
    manager_name     VARCHAR2 (200)
);

INSERT INTO xx_test_cursor
     VALUES ('John',1001,SYSDATE - 30, 'Frank');

INSERT INTO xx_test_cursor
     VALUES ('Graeme', 1002, SYSDATE - 40, 'Frank');

INSERT INTO xx_test_cursor
     VALUES ('Shawn', 1003, SYSDATE - 50, 'Frank');

INSERT INTO xx_test_cursor
     VALUES ('Jonty', 1004, SYSDATE - 60, 'Frank');

COMMIT;
SELECT * FROM xx_test_cursor;

The select statement gives below result:

A sample example of an implicit cursor: SELECT INTO

DECLARE
    l_emp_name    xx_test_imp_cursor.emp_name%TYPE;
    l_join_date   xx_test_imp_cursor.emp_join_date%TYPE;
BEGIN
    SELECT emp_name, emp_join_date
      INTO l_emp_name, l_join_date
      FROM xx_test_imp_cursor
     WHERE manager_name = 'Frank' 
       AND emp_id = 1001;

    DBMS_OUTPUT.put_line (
           'Emp Name is: '
        || l_emp_name
        || ' and Emp Join Date is: '
        || l_join_date);
EXCEPTION
    WHEN OTHERS
    THEN
        DBMS_OUTPUT.put_line ('Exception during fetching data: ' || SQLERRM);
END;

This gives the following result:

But, if the SELECT statement fetches more than 1 row in the dataset, then the program ends in exception. (in the code below I have commented the emp_id = 1001)

DECLARE
    l_emp_name    xx_test_cursor.emp_name%TYPE;
    l_join_date   xx_test_cursor.emp_join_date%TYPE;
BEGIN
    SELECT emp_name, emp_join_date
      INTO l_emp_name, l_join_date
      FROM xx_test_cursor
     WHERE manager_name = 'Frank';

    DBMS_OUTPUT.put_line (
           'Emp Name is: '
        || l_emp_name
        || ' and Emp Join Date is: '
        || l_join_date);
EXCEPTION
    WHEN OTHERS
    THEN
        DBMS_OUTPUT.put_line ('Exception during fetching data: ' || SQLERRM);
END;

And since this Select statement fetches more than 1 row, the code throws the following error:

Note: Actually Implicit Cursors are faster to process than an explicit cursor because there isn’t a separate step that declares, opens, fetches and closes the cursor.

Attributes of an Implicit Cursor:  There are 4 important attributes of an Implicit Cursor:

  • %FOUND – Until a DML statement like Insert, Update or Delete affects one or more rows, the SQL%FOUND will result as NULL The example below tries to update a record in the xx_test_cursor table. Only if the Update statement affects one or more rows, then the next statement in the IF condition is executed:
DECLARE
BEGIN
    UPDATE xx_test_cursor
       SET emp_name = emp_name || '_new'
     WHERE emp_id = 1001;

    IF SQL%FOUND
    THEN
        DBMS_OUTPUT.put_line ('SQL Found is True, thus executing the following statement..');
        INSERT INTO XX_TEST_CURSOR
             VALUES ('DARELL',
                     1005,
                     SYSDATE - 60,
                     'Frank');
        COMMIT;              
    END IF;
    
EXCEPTION
    WHEN OTHERS
    THEN
        DBMS_OUTPUT.put_line ('Exception when processing data: ' || SQLERRM);
END;
  • %ISOPEN – For Implicit Cursors, the database automatically closes the Cursor after executing the operation. Thus, the %ISOPEN always yields a FALSE for Implicit Cursors (because the Cursor is already closed). It is more useful for Explicit Cursors.
  • %NOTFOUND – %NOTFOUND is opposite to the %FOUND attribute. This attribute checks if the corresponding DML Operation like Insert, Update or Delete modified any data in the table. If the SQL code did not update / insert / delete any data, then this attribute returns TRUE. Else if the SQL modifies any data, then it returns FALSE.
    • In the example below – the UPDATE statement tries to update the table for a record that does not exist. Thus, no data was modified and the %NOTFOUND is true. And accordingly, the later INSERT statement works.
DECLARE
BEGIN
    UPDATE xx_test_cursor
       SET emp_name = emp_name || '_new'
     WHERE emp_id = 2006;

    IF SQL%NOTFOUND
    THEN
        DBMS_OUTPUT.put_line ('SQL%NOTFOUND is True, thus executing the following statement..');
        INSERT INTO XX_TEST_CURSOR
             VALUES ('SCOTT',
                     1006,
                     SYSDATE - 60,
                     'Frank');
        COMMIT;              
    END IF;
    
EXCEPTION
    WHEN OTHERS
    THEN
        DBMS_OUTPUT.put_line ('Exception when processing data: ' || SQLERRM);
END;
  • %ROWCOUNT – The %ROWCOUNT attribute returns the total number of records that got affected by the DML Operation.
    • For example – in the code below – the test table has 1 record with emp_id = 1006. Thus, the delete statement deletes 1 record and the displays the same in the dbms_output.
DECLARE
BEGIN
    DELETE FROM xx_test_cursor
          WHERE emp_id = 1006;

    DBMS_OUTPUT.put_line ('Total Number of rows deleted: ' || SQL%ROWCOUNT);
    
EXCEPTION
    WHEN OTHERS
    THEN
        DBMS_OUTPUT.put_line ('Exception when processing data: ' || SQLERRM);
END;

And the corresponding dbms_output displays as below:


Warning: count(): Parameter must be an array or an object that implements Countable in /var/www/wp-content/themes/WpPremium/author.php on line 78

How To: Create a Sales Order

In the Order to Cash (OTC) process in Oracle Applications, one of the first steps is to Create a Sales Order in Order Management Module.

The usual process of creating a Sales Order involves inserting data into an inbound staging table and then calling a Custom concurrent program. The detailed steps are as below:

  • Insert data into staging tables.
  • Validate data in the staging tables.
  • Insert the validated data into Oracle Order Management Interface Tables.
  • Run the Seeded Oracle “Order Import” Concurrent program to Create Sales Order.

These steps are explained as below:

  • Insert data into staging tables: Basing on the source system, data is inserted into Custom staging tables via a middleware application like Webmethods or SOA.
  • Validating Data in the staging tables: Some of the major validations that need to be done before inserting data into Interface tables are:
    • Item Validation: The item that is being used on the Sales order need to be validated. The item has to exist in the Item Master as well should be assigned to the inventory org from where the Item is being shipped from.
      SELECT *
        FROM mtl_system_items_b
       WHERE segment1 = 'TEST_ITEM_1234';
    • Customer Validation: The Customer for which the order is being created for, should exist in the system already.
      SELECT *
        FROM hz_cust_accounts
       WHERE account_number = '12345678'; -- p_customer_number
    • Get BILL_TO Validation: For the Customer that exists, check to see if a valid BILL_TO Site exists.
        SELECT hcsua.org_id, hcsua.site_use_id, hps.party_site_id
                  FROM hz_cust_accounts        hca
                      ,hz_cust_acct_sites_all  hcasa
                      ,hz_cust_site_uses_all   hcsua
                      ,hz_party_sites          hps
                  WHERE     hca.cust_account_id = hcasa.cust_account_id
                        AND hcasa.cust_acct_site_id = hcsua.cust_acct_site_id
                        AND hcasa.status = 'A'
                        AND hcsua.status = 'A'
                        AND hcasa.org_id = hcsua.org_id
                        AND hcsua.site_use_code = 'BILL_TO'
                        AND hps.identifying_address_flag = 'Y'
                        AND hps.party_site_id = hcasa.party_site_id
                        AND hca.cust_account_id = '&Cust_account_id'
                        AND hcsua.org_id = '&Operating_unit_id'
                        AND ROWNUM < 2;
    • GL_Period Validation: Validate whether the Order data has Open GL period or not.
      SELECT gps.period_name,
             gps.set_of_books_id,
             gps.end_date,
             gps.closing_status
        FROM gl_periods          gp,
             gl_period_statuses  gps,
             gl_sets_of_books    gsob,
             hr_operating_units  hou
       WHERE     gp.period_name = gps.period_name
             AND gp.period_set_name = gsob.period_set_name
             AND gsob.set_of_books_id = hou.set_of_books_id
             AND gps.application_id = 222
             AND gps.set_of_books_id = gsob.set_of_books_id
             AND '&Order Date' BETWEEN gp.start_date AND gp.end_date
             AND hou.organization_id = '&Organization_id';
    • UOM Code Validation: Validate Unit of Measure validation.
      SELECT uom_code FROM mtl_units_of_measure_vl;
    • Operating Unit Validation: Validate the Operating Unit for which the order is being created in.
      SELECT organization_id
        FROM hr_operating_units
       WHERE name = ''
    • Get Price List: Derive the Price List.
      SELECT list_header_id, name
        FROM qp_list_headers qlh
       WHERE     qlh.orig_org_id = '&Organization_id'
             AND currency_code = '&Currency_code'
             AND TRUNC (SYSDATE) BETWEEN NVL (TRUNC (qlh.start_date_active),
                                              TRUNC (SYSDATE) - 1)
                                     AND NVL (TRUNC (qlh.end_date_active),
                                              TRUNC (SYSDATE) + 1)
             AND qlh.active_flag = 'Y';
    • Derive Tax Rate Code: Derive the tax rate code based on the tax code passed by the source system.
      SELECT zrab.tax_regime_code,
             zrgb.tax_regime_id,
             zrab.tax,
             zrab.tax_status_code,
             zrab.tax_rate_code,
             zrab.tax_rate_id
        FROM zx_rates_b zrab, zx_regimes_b zrgb
       WHERE     zrab.tax_regime_code = zrgb.tax_regime_code
             AND TRUNC (SYSDATE) BETWEEN NVL (zrab.effective_from,
                                              TRUNC (SYSDATE) - 1)
                                     AND NVL (zrab.effective_to,
                                              TRUNC (SYSDATE) + 1)
             AND zrab.active_flag = 'Y'                     -- Added for Defect#7614
             AND zrab.tax_rate_code = '&tax_rate_code';
  • Inserting Data into Interface Table: Upon Validation, Data is inserted into the following Order Interface Tables:
    • Headers Interface: OE_HEADERS_IFACE_ALL.
      • This is the Headers Interface table that captures all the data that goes into the Order Header data (later into OE_ORDER_HEADERS_ALL table). Some of the important columns to be populated are: Operation_code, order_number, orig_sys_document_ref, org_id, Ordered_date, order_type_id, sold_to_org_id, ship_from_org_id, invoice_to_org_id, booked_flag, change_sequence etc.
    • Lines Interface: OE_LINES_IFACE_ALL
      • This is the Lines Interface table that captures all the data that goes onto the Line Level on the Sales order (later onto the OE_ORDER_LINES_ALL table). Some of the important columns are: Org_id, orig_sys_document_ref, inventory_item_id (item), ordered_quantity, order_quantity_uom, unit_list_price, unit_selling_price, fulfillment_set_id etc.
    • Payments Interface: OE_PAYMENTS_IFACE_ALL
      • The Payments Interface table captures data that is shown later on the payments screen on the order. This table has the data that is later populated into OE_PAYMENTS table. Some important columns are: Payment_number, orig_sys_document_ref, orig_sys_payment_ref, payment_type_code, payment_amount, check_number, credit_card_number, credit_card_expiration_date etc.
    • Actions Interface: OE_ACTIONS_IFACE_ALL
      • The Actions interface table is used to load data that will perform any Action to be taken on the order once the order is created. Some uses include Cancelling a line, Applying Hold etc.. Some important columns are: Org_id, Hold_id, Hold_type_code, Operation_Code.
    • Adjustment Interface: OE_PRICE_ADJS_IFACE_ALL
      • The Adjustment interface table holds the data that later goes to the OE_Price_Adjustments table. This table is used to store price adjustments that have been applied to an order or a line.
  • Submit Order Import program: Once data is inserted into the above interface tables, the Seeded Oracle job – Order Import is submitted as a Concurrent program. The code to call the program is as below:
    • x_request_id :=
                  fnd_request.submit_request (
                     application   => 'ONT',
                     program       => 'OEOIMP',
                     description   => 'EBS Order Import',
                     start_time    => SYSDATE,
                     sub_request   => FALSE,
                     argument1     => Operating Unit,
                     argument2     => Order Source,
                     argument3     => Orig Sys Doc Ref,
                     argument4     => Operation Code,
                     argument5     => Validate Only -- Yes or No,
                     argument6     => Debug Level,   -- 1
                     argument7     => Number of Instances,
                     argument8     => Sold To Org ID,
                     argument9     => Sold To Org,
                     argument10    => Change Sequence,
                     argument11    => Enable Single Line Queue for Instances,
                     argument12    => Trim Trailing Blanks, --- Y
                     argument13    => Process Orders With No Org Specified,
                     argument14    => Default Operating Unit,
                     argument15    =>  Validate DFFs ?     )
  • Order Interface Errors: During importing the Orders from Interface tables to the Order base tables, if an error occurs due to a setup issue or a data issue, then the corresponding error messages are stored in OE_PROCESSING_MSGS_TL table.
    SELECT OPT.*
    FROM OE_PROCESSING_MSGS OPM
           ,OE_PROCESSING_MSGS_TL OPT
    WHERE OPM.TRANSACTION_ID = OPT.TRANSACTION_ID
    AND ORIGINAL_SYS_DOCUMENT_REF = dof_ref; -- ORIGINAL_SYS_DOCUMENT_REF in headers interface
  • Oracle Base Tables: Upon completion of the “Order Import” seeded program, the base tables where order information is stored are:
    • Header Data: OE_ORDER_HEADERS_ALL
    • Lines Data: OE_ORDER_LINES_ALL
    • Payments Data: OE_PAYMENTS
    • Adjustments Data: OE_PRICE_ADJUSTMENTS
    • Holds Data: OE_ORDER_HOLDS_ALL
    • Order Sources: OE_ORDER_SOURCES
    • Sets data – Arrival Set, Ship Set, Fulfillment Set: OE_SETS
    • Transaction Types: OE_TRANSACTION_TYPES_TL.


Warning: count(): Parameter must be an array or an object that implements Countable in /var/www/wp-content/themes/WpPremium/author.php on line 78

Oracle Triggers

What are Triggers in Oracle? 

Oracle Triggers are similar to PL/SQL Stored procedures that can be stored in the database and are explicitly fired whenever a table or a view is changed by a user action or a database system actions occur.

A trigger can be fired when one of the following happens on a table:

DML (Data Manipulation Language) statements like  ‘Insert“, “Update” or “Delete” are run against the table.

DDL (Data Definition Language) statements like “Create“, “Alter” or “Drop” are issued either by a particular schema / user in the database.

Note: Using Triggers is effective to modify objects in the database, but they have to be used carefully. Excessive use of triggers may cause a SQL statement inside that trigger to fire other triggers – calling an effect called Cascading Triggers.

Parts of a Trigger: 

  • Triggering statement: A triggering statement is the SQL Statement or database event that causes a trigger to fire.
  • Triggering Restriction: A trigger restriction results in a Boolean expression i.e. either a true or a false that specifies whether the trigger should fire or not. A trigger fires if the Triggering restriction results in “True“. A “False” or an “Unknown” result doesn’t fire a trigger.
  • Trigger Action: A trigger action is the pl/sql statement (or anonymous block of code) that runs when the Triggering restriction results as True.

Example:

The following code is an example of a trigger that insert an Audit record into Table2 whenever an Insert event occurs into Table1.

Step 1: Create Table1 and Table2 for this test case.

CREATE TABLE table1
(
    col1    NUMBER
);

CREATE TABLE table2
(
    col1    DATE,
    col2    VARCHAR2 (2000)
);

Step 2: Create Trigger script.

CREATE TRIGGER tab1_trig
    AFTER INSERT
    ON table1
BEGIN
    INSERT INTO table2
         VALUES (SYSDATE, 'Insert into table1');
EXCEPTION WHEN OTHERS 
THEN 
   DBMS_OUTPUT.PUT_LINE ('Error During Trigger creation: )'||SQLERRM);
END;

Step 3: Generate the Triggering event.

INSERT INTO table1
     VALUES (1);

Step 4: Check the data in the Table 2.

SELECT * FROM table2;

Step 5: Result

Explanation: 

In the example above:

  • The Triggering statement is: AFTER INSERT ON table1.

The trigger fires immediately after an insert operation occurs on the Table1.

  • The Triggering Action is to Insert values in the Table2 when an Insert happens on Table1.

Different Types of Triggers

  • Row Triggers:
  • Statement Triggers:
  • BEFORE and AFTER Triggers:
  • INSTEAD OF Triggers:

 


Warning: count(): Parameter must be an array or an object that implements Countable in /var/www/wp-content/themes/WpPremium/author.php on line 78

How to delete a Value from a Valueset

Sometimes we create values inside valuesets that we may not need during code migration. As a developer, we might want to delete that value from that valueset during development, that way we are not migrating unnecessary information to higher instances.

Let’s create a test value set.

And create a couple of test values to the valueset.

SQL code to derive the values of the valueset from backend:

select * 
from fnd_flex_values
where flex_value_set_id = (select flex_value_set_id from fnd_flex_value_sets
where flex_value_set_name = 'XX_TEST_VALUESET');

Now, below is the code to delete value from the valueset:

declare 

l_flex_value_id  fnd_flex_values.flex_value_id%TYPE; 
l_flex_value     fnd_flex_values.flex_value%TYPE;

begin 

SELECT ffv.flex_value_id, ffv.flex_value
INTO   l_flex_value_id, l_flex_value
        FROM fnd_flex_value_sets ffvs,
             fnd_flex_values ffv,
             fnd_flex_values_tl ffvt
       WHERE     flex_value_set_name = 'XX_TEST_VALUESET' -- Value Set Name
             AND ffv.flex_value_set_id = ffvs.flex_value_set_id
             AND ffvt.flex_value_id = ffv.flex_value_id
             AND ffvs.flex_value_set_id = ffv.flex_value_set_id
             AND ffvt.language = 'US'
             AND ffv.enabled_flag = 'Y'
             AND ffv.summary_flag = 'N'
             and flex_value = 'Test Value 2';
             
      dbms_output.put_line ('l_flex_value_id is: '||l_flex_value_id||' and l_flex_value is :'||l_flex_value);

      fnd_flex_values_pkg.delete_row (l_flex_value_id);
      COMMIT;
      
      dbms_output.put_line (l_flex_value||' value deleted succesfully...');
                   
exception when others
then 

dbms_output.put_line ('Error during execution: '||SQLERRM); 

end;

Valueset after the script is run:

Note: Make sure this script is used only in a development instance and not in any controlled instances. APPS access is required to run the above script. Please use it carefully.


Warning: count(): Parameter must be an array or an object that implements Countable in /var/www/wp-content/themes/WpPremium/author.php on line 78

Oracle apps order error interface table

During a Sales Order Creation scenario, when data is inserted into Interface table – sometimes Orders can fail to get created due to issues in the Interface table. The corresponding error information is stored in the oe_processing_msgs_tl table.

The query to find out error messages in the Order Interface table is:

SELECT * 
FROM oe_processing_msgs opm, 
     oe_processing_msgs_tl opmt
WHERE opm.transaction_id = opmt.transaction_id
AND opm.original_sys_document_ref = '123456';  
--- where 123456 is the Order Number in the interface table.

--- Interface table information. 

SELECT * 
FROM oe_headers_iface_all 
WHERE order_number = '123456';

SELECT * 
FROM oe_lines_iface_all 
WHERE orig_sys_document_ref = (SELECT orig_sys_document_ref 
                               FROM oe_headers_iface_all 
                               WHERE order_number = '123456');