You are on page 1of 20

ORACLE 11i Advanced Pricing

QP: Leveraging Oracle


Configurator and Attribute
Mapping

An Oracle White Paper
March 2003




ORACLE 11i Advanced Pricing : Leveraging Oracle
Configurator and Attribute Mapping
EXECUTIVE OVERVIEW
Oracle Advanced Pricing 11.5.8 adds functionality that greatly simplifies the
process of defining additional pricing attributes. Setup screens have been
consolidated, and it is no longer necessary to define flex fields and defaulting rules.
This paper is offered as an update and supplement to the Oracle Advanced Pricing
Implementation manual Release 11i and the White Paper QP: Dont Customize,
Extend.
In this paper, you will learn how to define a new pricing context, define new pricing
attributes that source data from Oracle Configurator tables, and use those attribute
values in Price List Lines and Formulas. The pricing structure will work exactly the
same in Order Management, Quoting, and iStore demonstrating a single point of
setup for multiple selling channels.
INTRODUCTION
This white paper provides a solution for how to price items based on attributes
chosen at runtime in Oracle Configurator. In this example, the item is a service
where pricing logic is to be applied based on non-BOM options selected for a given
product. While the attributes herein were mapped from the Oracle Configurator
tables, they could have come from any source, and the process would remain
essentially the same.
QP: Leveraging Oracle Configurator and Attribute Mapping PAGE 2

SUPPORTING DATA
Pricing rule data:
Server A Server B Storage 1 Storage 2
Platinum 3324 5916 6060 3264
Gold 2220 3372 4056 1980
- 24/7 onsite Gold+20% Gold+20% Gold+20% Gold+20%
- 2hr onsite response Gold+30% Gold+30% Gold+30% Gold+30%
Silver 1080 1836 1968 1212
- 24/7 online& phone Silver+20% Silver+20% Silver+20% Silver+20%
- 24/7 onsite Silver+30% Silver+30% Silver+30% Silver+30%
- 2hr onsite response Silver+50% Silver+50% Silver+50% Silver+50%
Bronze 800 1200 1300 900
- 24/7 online& phone Brnz+20% Brnz+20% Brnz+20% Brnz+20%

The above rates are for a 1 year service agreement. In addition, there is a
DURATION factor to apply a 1.90 multiplier for 2 years, and a 2.72 multiplier for
3 years.
To summarize, prices are defined for each combination of Product and Metal
Level, then an adjustment is applied based on the Options selected, Duration,
Geography, and Customer.

ORACLES APPOACH
With this scenario, it is possible to set up 16 part numbers, 1 for each combination of product and metal level.
However, in this example we chose to do everything with ONE part number instead. While there are certain
advantages using 16 part number method (easier visibility in Price List Maintenance Form), we wanted to prove the
point that we dont have to have a proliferation of part numbers in order to have dynamic pricing.

In this example, servers and storage units are highly configurable, so Oracle Configurator is part of the solution
footprint. The attributes by which prices are determined are easy to collect within a Configurator user interface. The
only thing left was to tell Advanced Pricing how to find them. The latest Attribute Mapping capabilities make this
easy.
Given the above, our solution breaks down into the following steps:
QP: Leveraging Oracle Configurator and Attribute Mapping PAGE 3
Add the appropriate questions to the Configurator UI (Simple Oracle Configurator Development)
Format the answers to these questions in the CZ CONFIG ATTRIBUTES table for ease of extraction (a
single Oracle Configurator Functional Companion Rule)
Create a single PL/SQL Attribute Mapping function which handles variable inputs
Define the new pricing attributes in advanced pricing
Use the attributes in price list lines, formulae, and modifiers as needed
Configurator Model Structure
We start with a single part number called SUPREME. This service could be applied to one or many servers and or
storage units that are sold as a part of a system configuration. As such, the part number was added to the BOM of
each server and each storage unit. The result is that, for a given system configuration, the SUPREME would appear
multiple times, at various levels in the structure.
The level at which SUPREME appears gives us the PRODUCT attribute. The answers to various questions in the
configuration UI give us METAL LEVEL and OPTIONS, and potentially GEOGRAPHY (not modeled in this
scenario). The CUSTOMER is a normal, readily available attribute of the ordering process.
Configurator Functional Companion to build CZ CONFIG ATTRIBUTES
While it is possible for the pricing engine to source the attributes directly from the normal CZ schema, there are
advantages in efficiency and ease of extraction to putting the attributes into a formatted table within the CZ schema.
The concept of Configurator Output Attributes can extend far beyond pricing attributes to include descriptive or
instructive data for down stream processing, workflow triggers, etc.
A straightforward Functional Companion (FC) rule (a java extension) was written and attached to the model structure.
(Java code is attached in Appendix A) The effect of this FC is to write a row into the standard Oracle table
CZ_CONFIG_ATTRIBUTES for each occurrence of the part number SUPREME in the final, CONFIGURED Bill
of Material. In the columns of each row are the values for the attributes PRODUCT, METAL LEVEL, OPTION,
DURATION.

Functional Companions are a non-invasive, upgrade resistant method of adding to the functionality of the out of the
box Oracle Configurator. THIS IS AN EXTENSION, NOT A CUSTOMIZATION
Create a single PL/SQL Attribute Mapping function
A new function was written and installed in the instance that will retrieve the values from the
CZ_CONFIG_ATTRIBUTES table. A single function was written, that handles variable inputs, and can thus be re-
used to retrieve various values. (PL/SQL body of the function is attached as Appendix B).
It is likely that it could be achieved in a more generic fashion to maximize re-use, given a broader perspective in an
overall implementation. The key point is that this function can be defined such that it never needs to be maintained
again. All pricing behaviors based on the results of this function are defined in Oracle forms. There has been no
change made to standard code, so future upgrades should have no impact. THIS IS AN EXTENSION, NOT A
CUSTOMIZATION.
QP: Leveraging Oracle Configurator and Attribute Mapping PAGE 4
Define the new pricing attributes in advanced pricing
First we set up a new CONTEXT:
PRICING > SETUP > CONTEXT AND ATTRIBUTES
The context in this case was named SM-2. From within this same screen, you simply add the new attributes, and save
it.












QP: Leveraging Oracle Configurator and Attribute Mapping PAGE 5

Next you LINK the attributes
PRICING > SETUP > ATTRIBUTE LINKING AND MAPPING
















Choose your new context, and click the LINK ATTRIBUTES BUTTON











QP: Leveraging Oracle Configurator and Attribute Mapping PAGE 6

For each of our new attributes, we have chosen ATTRIBUTE MAPPING as the Mapping method. Other options
here include CUSTOM SOURCED which is a call to an external program, or USER ENTERED, which can be used
to generate flex fields that can be populated on an Order Management line, for example.
Now we will drill into the mapping for the METAL LEVEL:




















What we see above is that there is a separate map for ASO, OKC, and ONT. We have selected a User Source Type
of PL/SQL API. Other alternatives include CONSTANT, SYSTEM VARIABLE, PROFILE OPTION, and
PL/SQL API Multi-Record.
The key to the map is the User Value String. This could simply be an attribute that exists in the associated Global
Object, in this case ASO_PRICING_INT.G_LINE_REC, or, it could be a call to a package that you create, using one
of the values in the Global Object as a hook to get somewhere else.
In the above user value string, we have said: Run the function Get_SM_Price_Attr from within package
SM_PRICING, the inputs included the strings ASO METAL-LEVEL and SMQPValue, as well as the config
QP: Leveraging Oracle Configurator and Attribute Mapping PAGE 7
header, revision, and quote line code which are natural elements of the Global Object
ASO_PRICING_INT.G_LINE_REC.
Below is an example of how this string looks when we are calling from ORDER MANAGEMENT, looking for the
DURATION attribute.
















As you can see from the string above, we have substituted ASO for ONT, and DURATION for METAL-LEVEL.
Also, since Order Management uses a different Global Object (OE_ORDER_PUB.G_LINE), and the Config header
and revision and line have different names in that object, the variables are changed accordingly.
THE PL/SQL PACKAGE AND FUNCTION ARE THE SAME!
Now we can use these attribute values wherever we like!
QP: Leveraging Oracle Configurator and Attribute Mapping PAGE 8

Use the attributes in price list lines, formulae, and modifiers as need
Since the prices were defined for each combination of PRODUCT and METAL LEVEL, we set up a price list line for
each:













The pricing engine, based on the combination PRODUCT and METAL LEVEL, selects the price list line. The price
list line stores the base price, and a link to the appropriate formula.
QP: Leveraging Oracle Configurator and Attribute Mapping PAGE 9
Since the OPTION uplifts differ between GOLD, SILVER and BRONZE, we created a formula for each case, and
applied the appropriate formula to the line. Below you can see that the formula SUPREME GOLD uses the values of
the other 2 attributes, DURATION and OPTION to finish the pricing calculation.
























The result is, when you order a SERVER A, and select GOLD for your metal level, 24 hour onsite service option, and
a duration of 2 years, the following occurs:
1. The price list line with $2220 is selected because of PRODUCT and METAL LEVEL
2. The formula calculates to 2220 x 1.2 x 1.9 = $5061.60
QP: Leveraging Oracle Configurator and Attribute Mapping PAGE 10
CONCLUSION
The enhancements to the Attribute Mapping methodology in 11.5.8 have streamlined the pricing business process.
Using a structured methodology like the one described here opens up tremendous flexibility in pricing techniques, and
can have a massive favorable impact on maintenance costs and ROI.
QP: Leveraging Oracle Configurator and Attribute Mapping PAGE 11
APPENDIX A: Java Functional Companion WriteConfigAttributes


import oracle.apps.fnd.common.Context;
import oracle.apps.cz.cio.*;
import com.sun.java.util.collections.*;
import java.sql.*;
import oracle.apps.cz.utilities.*;
import java.io.*;
import java.text.*;
import java.util.StringTokenizer;

public class WriteConfigAttributes extends AutoFunctionalCompanion {

Configuration config = null;
String mserviceItemName = "SUPREME"; //"SM SERVICE SUPREME";
ArrayList mselServiceNodes = new ArrayList();
String mQpPropName = "QPValue";
String mffContext = "SMQPValue";
String mMetalLevelFeat = "Service Medal Level";
String mMetalLevel = "";
String mMetalLevelAttr = "ATTRIBUTE1";
String mDurationFeat = "Duration";
String mDuration = "";
String mDurationAttr = "ATTRIBUTE2";
String mOptionsFeat = "Service Options";
String mOptions = "";
String mOptionsAttr = "ATTRIBUTE3";
String mServerFeat = "Server Platforms";
String mStorageFeat = "StorEdge Device";
String mProdAttr = "ATTRIBUTE4";

public WriteConfigAttributes() {
}

public void afterSave() {
config = getRuntimeNode().getConfiguration();
// First, clear all attribute data for this configuration from
// the CZ_CONFIG_ATTRIBUTES table.
try {
clearConfigAttributes(config);
} catch (SQLException sqle) {
throw new RuntimeException("Error in clearing the previous attribute
data");
}
// Starting from the root, get all the service bom items selected.
RuntimeNode root = (RuntimeNode)config.getRootComponent();
if (root instanceof BomNode) {
traverseBomTree(root);
} else {
Iterator iter = root.getChildren().iterator();
while (iter.hasNext()) {
RuntimeNode child = (RuntimeNode)iter.next();
if (child instanceof BomNode || child instanceof ComponentSet) {
traverseBomTree(child);
}
}
QP: Leveraging Oracle Configurator and Attribute Mapping PAGE 12
}
int noNodes = mselServiceNodes.size();

// get the sevice level attributes
mMetalLevel = getQpValue((IRuntimeNode)root, mMetalLevelFeat);
mDuration = getQpValue((IRuntimeNode)root, mDurationFeat);
mOptions = getQpValue((IRuntimeNode)root, mOptionsFeat);

// write the config data
writeAttributes();

}

private String getQpValue (IRuntimeNode currRoot, String featName) {
String ret = "";
IRuntimeNode mlIrtn = getFeaturebyName(currRoot, featName);
if (mlIrtn != null) {
try {
IOption optSel = ((IOptionFeature)mlIrtn).getSelectedOption();
if (optSel == null) {
ret = "none";
} else {
Property qpProp =
((RuntimeNode)optSel).getPropertyByName(mQpPropName);
if (qpProp != null && qpProp.hasStringValue()) {
ret = qpProp.getStringValue();
}
}
} catch (SelectionNotMutexedException snme) {
ret = "none";
}
}
return ret;
}

/**
* Clears all attribute data for this configuration from
* the CZ_CONFIG_ATTRIBUTES table.
*/
public void clearConfigAttributes(Configuration config) throws SQLException {
long configHdrId = config.getConfigHeaderIdLong();
long configHdrRev = config.getConfigHeaderRevisionLong();
Connection conn = config.getContext().getJDBCConnection();
PreparedStatement pStmt = null;
int ret;
try {
String sql = "DELETE FROM CZ_CONFIG_ATTRIBUTES WHERE CONFIG_HDR_ID=? AND
CONFIG_REV_NBR=?";
pStmt = conn.prepareStatement(sql);
pStmt.setLong(1, configHdrId);
pStmt.setLong(2, configHdrRev);
ret = pStmt.executeUpdate();
} finally {
if (pStmt != null) pStmt.close();
}
}

private void traverseBomTree(RuntimeNode node) {
QP: Leveraging Oracle Configurator and Attribute Mapping PAGE 13
//List childAttribs = null;
if (!(node instanceof ComponentSet)) {
BomNode bomNode = (BomNode)node;
String nodeName = bomNode.getName();
if (nodeName.compareToIgnoreCase(mserviceItemName) == 0 &&
bomNode.isSelected()) {
mselServiceNodes.add(bomNode);
}
}
// Now recursively visit all the children subtrees.
Iterator iter = node.getChildren().iterator();
while (iter.hasNext()) {
RuntimeNode child = (RuntimeNode)iter.next();
if (child instanceof BomNode || child instanceof ComponentSet) {
traverseBomTree(child);
}
}
}

//____________________________________________________________________________
private IRuntimeNode getFeaturebyName (IRuntimeNode curRoot, String featName){
boolean isComp = false;
IRuntimeNode retFeat = null;
// get to a component to start
if (curRoot.getType() == (IRuntimeNode.COMPONENT) ||
curRoot.getType() == (IRuntimeNode.COMPONENT_SET) ||
curRoot.getType() == IRuntimeNode.BOM_MODEL ||
curRoot.getType() == IRuntimeNode.BOM_OPTION_CLASS) {
isComp = true;
}
while (isComp != true) {
curRoot = curRoot.getParent();
int type = curRoot.getType();
if (curRoot.getType() == (IRuntimeNode.COMPONENT) ||
curRoot.getType() == (IRuntimeNode.COMPONENT_SET)||
curRoot.getType() == IRuntimeNode.BOM_MODEL ||
curRoot.getType() == IRuntimeNode.BOM_OPTION_CLASS) {
isComp = true;
}
}
retFeat = getFeatureInComponent(curRoot, featName);
// if feature not found go to parent and test
while (retFeat == null && !curRoot.isRoot()) {
curRoot = curRoot.getParent();
retFeat = getFeatureInComponent(curRoot, featName);
}

return (retFeat);
}

//____________________________________________________________________________
private IRuntimeNode getFeatureInComponent (IRuntimeNode curRoot, String
featName){
boolean ffound = false;
IRuntimeNode retFeat = null;

List compList = curRoot.getChildren();
Iterator cit = compList.iterator();
QP: Leveraging Oracle Configurator and Attribute Mapping PAGE 14
while (cit.hasNext() && !ffound) {
IRuntimeNode irtn = (IRuntimeNode)cit.next();
if (irtn.getType() == (IRuntimeNode.OPTION_FEATURE) ||
irtn.getType() == (IRuntimeNode.BOOLEAN_FEATURE) ||
irtn.getType() == (IRuntimeNode.COUNT_FEATURE) ||
irtn.getType() == (IRuntimeNode.COUNT_FEATURE) ||
irtn.getType() == (IRuntimeNode.DECIMAL_FEATURE) ||
irtn.getType() == (IRuntimeNode.INTEGER_FEATURE) ||
irtn.getType() == (IRuntimeNode.TEXT_FEATURE)) {
String fname = irtn.getName();
if (featName.compareTo(fname) == 0) {
ffound = true;
retFeat = irtn;
}
}
if (irtn.getType() == (IRuntimeNode.COMPONENT) ||
irtn.getType() == (IRuntimeNode.COMPONENT_SET)) {
retFeat = getFeatureInComponent(irtn, featName);
if (retFeat != null) {
ffound = true;
}
}
}

return (retFeat);

}

private void writeAttributes() {
String context = mffContext;
IRuntimeNode prodIrtn = null;
HashMap attrValuePair = new HashMap();
attrValuePair.put(mMetalLevelAttr, mMetalLevel);
attrValuePair.put(mDurationAttr, mDuration);
attrValuePair.put(mOptionsAttr, mOptions);
Iterator iter = mselServiceNodes.iterator();
while (iter.hasNext()) {
BomNode node = (BomNode)iter.next();
// get the product name
IRuntimeNode svrbnIrtn = getFeaturebyName((IRuntimeNode)node,mServerFeat);
IRuntimeNode strbnIrtn =
getFeaturebyName((IRuntimeNode)node,mStorageFeat);
if (svrbnIrtn != null) {
String fname = svrbnIrtn.getName();
prodIrtn = svrbnIrtn;
} else if (strbnIrtn != null) {
String fname = strbnIrtn.getName();
prodIrtn = strbnIrtn;
}
if (prodIrtn != null) {
try {
IOption optSel = ((IOptionFeature)prodIrtn).getSelectedOption();
String prodName = ((IRuntimeNode)optSel).getName();
attrValuePair.put(mProdAttr, prodName);
} catch (SelectionNotMutexedException snme) {
String junk = "here";;
}
}
QP: Leveraging Oracle Configurator and Attribute Mapping PAGE 15

insertAttributes(node, context, attrValuePair, config.getContext());
}
}

/**
* Inserts the attributes belonging to a given node and context into
* the CZ_CONFIG_ATTRIBUTES table.
*/
public int insertAttributes (BomNode node, String attrContext, Map attributes,
Context ctx) {
Connection conn = ctx.getJDBCConnection(ctx);
PreparedStatement pStmt = null;
int ret;
try {
try {
String insertString = " INSERT INTO CZ_CONFIG_ATTRIBUTES (CONFIG_HDR_ID,
CONFIG_REV_NBR, " +
"CONFIG_ITEM_ID, ATTRIBUTE_CATEGORY";
String valueString = " VALUES(?,?,?,?";
// Build an insert string based on the attribute name for the attribute
context.
Collection keys = attributes.keySet();
Iterator keyIter = keys.iterator();
while (keyIter.hasNext()) {
//insertString = insertString + "," + getAttributeFieldName
(attrContext,(String)keyIter.next(), ctx);
insertString = insertString + "," + (String)keyIter.next();
valueString = valueString + ",?";
}
insertString = insertString + ")";
valueString = valueString + ")";
String sql = insertString + valueString;
pStmt = conn.prepareStatement(sql);
//Bind/set values to the parameters
// Add config_hdr_id.
pStmt.setLong(1,config.getConfigHeaderIdLong());
// Add config_rev_nbr.
pStmt.setLong(2,config.getConfigHeaderRevisionLong());
// Add config_item_id.
pStmt.setLong(3,node.getConfigItemID());
// Add flexfield context for attribute.
pStmt.setString(4,attrContext);
// Iterate over attributes.
int n = 5;
List attrValues = new ArrayList();
attrValues.addAll(attributes.values());
Iterator iter = attrValues.iterator();
while (iter.hasNext()) {
pStmt.setString(n++, iter.next().toString());
}
ret = pStmt.executeUpdate();
} finally {
if(pStmt != null) pStmt.close();
}
} catch (SQLException e) {
throw new RuntimeException("Error Inserting into CZ_CONFIG_ATTRIBUTES
table" + e);
QP: Leveraging Oracle Configurator and Attribute Mapping PAGE 16
}
return ret;
}

/**
* Return the column name in CZ_CONFIG_ATTRIBUTES to write this attribute value
to,
* given an attribute context name, attribute name, and database context.
* This only applies to flexfield attributes
*/
public String getAttributeFieldName (String attrContext, String attribute,
Context ctx) {
Connection conn = ctx.getJDBCConnection(ctx);
ResultSet rs = null;
PreparedStatement pStmt = null;
String sql, columnName = null;
try {
try {
sql = "SELECT dfu.application_column_name " +
"FROM FND_DESCR_FLEX_COLUMN_USAGES DFU, " +
"FND_DESCRIPTIVE_FLEXS FDL , FND_APPLICATION FAN " +
"WHERE fan.application_short_name = 'CZ' " +
"AND dfu.application_id = fan.application_id " +
"AND fdl.application_id = fan.application_id " +
"AND fdl.APPLICATION_TABLE_NAME = 'CZ_CONFIG_ATTRIBUTES' " +
"AND dfu.descriptive_flexfield_name =
fdl.descriptive_flexfield_name " +
"AND dfu.descriptive_flex_context_code = ? " +
"AND end_user_column_name = ? ";
pStmt = conn.prepareStatement(sql);
pStmt.setString(1,attrContext );
pStmt.setString(2,attribute );
rs = pStmt.executeQuery();
if (rs.next()) {
columnName = rs.getString(1);
} else {
throw new RuntimeException("No column name found for context = " +
attrContext +
" and attribute = " + attribute);
}
} finally {
// close all
if (rs != null) rs.close();
if (pStmt != null) pStmt.close();
}
} catch (SQLException e) {
throw new RuntimeException("Error querying attribute names");
}
return columnName;
}

}

QP: Leveraging Oracle Configurator and Attribute Mapping PAGE 17

APPENDIX B: PL/SQL function Get_SM_Price_Attr in package sm_pricing

CREATE OR REPLACE PACKAGE sm_pricing AS

FUNCTION Get_SM_Price_Attr(p_calling_app IN VARCHAR2,
p_type IN VARCHAR2,
p_context IN VARCHAR2,
p_config_hdr_id IN NUMBER,
p_config_rev_nbr IN NUMBER,
p_id IN NUMBER)
RETURN VARCHAR2;

END sm_pricing;


CREATE OR REPLACE PACKAGE BODY sm_pricing AS

FUNCTION Get_SM_Price_Attr(p_calling_app IN VARCHAR2,
p_type IN VARCHAR2,
p_context IN VARCHAR2,
p_config_hdr_id IN NUMBER,
p_config_rev_nbr IN NUMBER,
p_id IN NUMBER)
RETURN VARCHAR2 IS

v_config_item_id NUMBER := null;
v_return VARCHAR2(10);

BEGIN

IF p_calling_app = 'ASO' THEN

SELECT config_item_id
INTO v_config_item_id
FROM aso_quote_line_details
WHERE quote_line_id = p_id
AND config_header_id = p_config_hdr_id
AND config_revision_num = p_config_rev_nbr;

ELSIF p_calling_app = 'ONT' THEN

v_config_item_id := p_id;

ELSE

v_return := null;

END IF;

IF v_config_item_id is not null THEN

IF p_type = 'PROD' THEN

SELECT ATTRIBUTE4
INTO v_return
FROM cz_config_attributes
QP: Leveraging Oracle Configurator and Attribute Mapping PAGE 18
WHERE config_hdr_id = p_config_hdr_id
AND config_rev_nbr = p_config_rev_nbr
AND config_item_id = v_config_item_id
AND attribute_category = p_context;

ELSIF p_type ='METAL-LEVEL' THEN

SELECT ATTRIBUTE1
INTO v_return
FROM cz_config_attributes
WHERE config_hdr_id = p_config_hdr_id
AND config_rev_nbr = p_config_rev_nbr
AND config_item_id = v_config_item_id
AND attribute_category = p_context;

ELSIF p_type ='DURATION' THEN

SELECT ATTRIBUTE2
INTO v_return
FROM cz_config_attributes
WHERE config_hdr_id = p_config_hdr_id
AND config_rev_nbr = p_config_rev_nbr
AND config_item_id = v_config_item_id
AND attribute_category = p_context;

ELSIF p_type ='OPTION' THEN

SELECT ATTRIBUTE3
INTO v_return
FROM cz_config_attributes
WHERE config_hdr_id = p_config_hdr_id
AND config_rev_nbr = p_config_rev_nbr
AND config_item_id = v_config_item_id
AND attribute_category = p_context;

ELSE

v_return := null;

END IF;

ELSE
v_return := null;

END IF;

RETURN v_return;

END Get_SM_Price_Attr;

END sm_pricing;
/
.]


QP: Leveraging Oracle Configurator and Attribute Mapping PAGE 19
ORACLE 11i Advanced Pricing
QP: Leveraging Oracle Configurator and Attribute Mapping
March 2003
Author: Rick Welling
Contributing Authors: Michael Dellorso

Oracle Corporation
World Headquarters
500 Oracle Parkway
Redwood Shores, CA 94065
U.S.A.

Worldwide Inquiries:
Phone: +1.650.506.7000
Fax: +1.650.506.7200
www.oracle.com

Oracle Corporation provides the software
that powers the internet.

Oracle is a registered trademark of Oracle Corporation. Various
product and service names referenced herein may be trademarks
of Oracle Corporation. All other product and service names
mentioned may be trademarks of their respective owners.

Copyright 2003 Oracle Corporation
All rights reserved.

You might also like