Sei sulla pagina 1di 68

WHITE PAPER – TO ENHANCE/

UPTAKE MULTIPLE ORGANIZATION


ACCESS CONTROL FOR R12
UPGRADE

Author: Anuj Kumar Creation Date: 01-Feb-2008

Last Updated: 05-Feb-2008

File Name:
Version: 1.0
Status: For Review

Page 1 of 70

Company Confidential
Contents

0 1................................................................................................................................................Introduction
4
1.1 Access Control.........................................................................................................................4

1.2 Select Operating Unit...............................................................................................................5

0 2............................................................................................................................................Business Needs
7
2.1.1 Organization Security Overview....................................................................................7
0 3...............................................................................................................Functional Feature Descriptions
8
3.1 Access Control - Foundations..................................................................................................8
3.1.1 Defining Security based on Hierarchy or a List of Operating Units.............................8
3.1.4 Process Flow...................................................................................................................9
3.1.5 Assign MO: Security Profile to Application Responsibility........................................10
3.2 Specifications to Oracle Forms..............................................................................................11
3.2.1 Select Operating Unit For Setup and Transaction Forms...........................................11
3.2.3 Operating Unit Default................................................................................................16
3.4 Reporting................................................................................................................................17
3.4.1 Single Org Reports.......................................................................................................17
3.4.2 Cross Org Reports........................................................................................................17
3.4.3 Define Concurrent Programs Window........................................................................18
3.5 Concurrent Programs (others than reports)...........................................................................18
3.5.1 Single Org Concurrent Programs................................................................................18
3.6 Public APIs.................................................................................................................................19

3.7 Workflows..................................................................................................................................19

0 4.................................................................................................................Technical Feature Descriptions


21
4.1 Access Control Architecture..................................................................................................21
4.1.1 Background..................................................................................................................21
4.1.2 Virtual Private Database (VPD)...................................................................................21
4.1.3 Multi-Org Security Policy Predicate............................................................................22
4.1.4 Multi-Org Initialization...............................................................................................24
4.1.5 Datamodel Design........................................................................................................27
4.1.6 Profile Options............................................................................................................28
4.1.8 Multi-Org APIs............................................................................................................29
7.2 Multi-Org Views/Tables Change............................................................................................32

Page 2 of 70

Company Confidential - For Internal Use Only


7.2.1 Enforce NOT NULL constraint on ORG_ID column..................................................32
7.2.2 Modify your Database Views.......................................................................................33
4.2.3 Attach Security Policy to your database objects..........................................................45
4.2.4 Remove Dependency on Multi-Org in AOL tables......................................................46
4.2.5 Register Multi-Org Access Enabled in MO table........................................................46
4.3 Forms Enhancements.............................................................................................................47
4.3.1 Multi-Org Initialization...............................................................................................47
4.3.2 Add Operating Unit Field............................................................................................47
4.3.3 Create LOV for Operating Unit field..........................................................................47
4.3.4 Default Operating Unit on forms startup.....................................................................48
4.3.6 Setting the Dynamic Policy Context............................................................................49
4.3.9 Modify Record Groups for Operating Unit specific fields..........................................57
4.3.10 Add ORG_ID predicate in Client/Server Code.........................................................59
4.3.11 Modify table handlers................................................................................................60
4.3.12 Allow Query on Operating Unit field........................................................................62
4.3.13 Handle Flexfields.......................................................................................................63
4.3.14 Handle Operating Unit value change........................................................................66
4.4 Enhancement to Reports........................................................................................................66
4.4.1 Overview......................................................................................................................66
4.4.1 Single Org Reports.......................................................................................................66
4.4.2 Cross Org Reports........................................................................................................67
4.5 Concurrent Program Enhancements......................................................................................67
4.5.1 Overview......................................................................................................................67
4.5.2 Single Org Concurrent Programs................................................................................67
4.5.3 Multiple Org Concurrent Programs.............................................................................67
0 5........................................................................................................................................................Glossary
68

Page 3 of 70

Company Confidential - For Internal Use Only


1. Introduction
This White Paper on Multi-Org Access Control Uptake/enhance document provides functional and
technical specifications for new Multi-Org architecture and the task to Convert any custom code as an
upgrade.

1.1 Access Control


The Multi-Org Access Control feature, also know as "Security by Operating Unit", will enable users to
access to secured data in one or more Operating Units within one responsibility. The feature uses
Security Profile concept introduced in Release 11i Oracle Human Resources Management System, which
allows system administrator to predefine the scope of access privilege as a profile option. A security
profile may be defined in hierarchical or listing mode, which may consist one or more Operating Units.

A profile option, “MO: Security Profile”, is used to associate predefined security profile to a user
responsibility.

The following two process flows illustrate current and new models for defining Multi-Org.

Page 4 of 70

Company Confidential - For Internal Use Only


E x is tin g S e c u r ity N e w S e c u r it y
M odel M odel

D e fin e O p e r a tin g D e f in e O p e r a t in g
U n it s U n its

STEP 1 Is th e re a n
D e fin e Yes O U h ie r a r c h ic a l
O r g a n iz a tio n s tru c tu re ?
H ie r a r c h y

No

STEP 2
STEP 1 D e fin e S e c u r it y
STEP 2 D e fin e P r o f ile & R u n
D e fin e S e c u r ity O r g a n iz a tio n S e c u r it y L is t
P r o f il e H ie r a r c h y M a in te n a n c e
P ro g ra m

STEP 3 STEP 3
R u n S e c u r it y L is t S e t M O : S e c u r ity
M a in te n a n c e P r o file
P ro g ra m

STEP 4 STEP 4
S e t M O : S e c u r ity S e t U s e r D e fa u lt
P r o f il e O U

STEP 5
R un A ccess
V a lid a tio n R e p o r t

Figure 1 Process Flow for Operating Unit Security

1.2 Select Operating Unit


With the ability to access multiple Multi-Org Operating Units from a single application responsibility,
users are able to enter setup and transaction data and run concurrent programs for multiple Operating
Units without having to switch the responsibility. Except in a few case, all Multi-Org enabled setup and
transaction forms will have "Operating Unit" field. Users will be able to select the Operating Unit from
a list of values assigned to the user via the security profile and responsibility

Page 5 of 70

Company Confidential - For Internal Use Only


O U1

S e tu p D a ta
T r a n s a c tio n D a ta
C o n c u rre n t P ro g ra m s

S c re e n
O U2

R e s p o n s ib ility ------------------------ S e tu p D a ta
------------------------ T r a n s a c tio n D a ta
------------------------ C o n c u rre n t P ro g ra m s

O U3

S e tu p D a ta
T r a n s a c tio n D a ta
C o n c u rre n t P ro g ra m s

Figure 2 - Select Operating Unit Process Flow

Page 6 of 70

Company Confidential - For Internal Use Only


2. Business Needs

2.1 Background
Multi-Org Operating Unit access is intended to work similarly to other kinds of organization security
implemented in the E-Business Suite. For this reason, it is helpful to understand how the various types
of organization security work in the applications.

2.1.1 Organization Security Overview

When you define an organization, you must first indicate the organization type of either internal or
external, and then assign multiple organization classifications to that organization. There exists no
validation between ‘organization classification' and a type of 'internal or external’. Below is a list of all
internal organization types and the products using them:

Organization Classification Used By


AAP Organization Human Resources
Asset Organization Fixed Assets
Business Group Human Resources
Corporate Headquarters Human Resources
CRP Organization Manufacturing
GRE/Legal Entity HR, AP, AR, etc.
HR Organization Human Resources
Inventory Org Manufacturing
MRP Organization Manufacturing
Operating Unit AP, AR, CE, PO, OE, PA, CRM
Project Expenditure/Event Organization Projects
Project Invoice Collection Organization Projects
Project Manufacturing Organization Projects
Project Task Owning Organization Projects
Reporting Establishment Human Resources
WIP Organization Manufacturing

Table 1-Organization Classifications

Page 7 of 70

Company Confidential - For Internal Use Only


3. Functional Feature Descriptions

3.1 Access Control - Foundations


The Multi-Org Access Control feature enables users to access to one or more Operating Units within one
user responsibility. A flexible security profile will be implemented with a new profile option "MO:
Security Profile" to control access for one responsibility to multiple Operating Units. This security
profile will permit access to one, multiple or all Multi-Org Operating Units in the system. The new
security profile will be created through the existing HR Security Profile window

The Human Resources product team currently maintains both the Organization Hierarchy and Security
Profile forms. The Security Profile form needs to be enhanced to support the additional Operating Unit
access features.

3.1.1 Defining Security based on Hierarchy or a List of Operating Units

Users want to base security on an organization hierarchy or a list of organizations. The following
diagram shows a hypothetical enterprise structures:

Figure 3 - Organization Structures

This is a simple enterprise structure. There is no hierarchical structure of organizations, since the ledger
is not considered an organization in the subledgers. In this case, the user will want to gain access to
OU1, OU2, or OU1 & OU2, all of which are lists of organizations, and not based on a hierarchy. Also, a
user might have a simple hierarchical structure with a few numbers of organizations. A user can still
base the security on a list of organizations by selecting all parent and subordinate Operating Units in the
list. For security profiles based on hierarchies, the user must complete set up Step 1 - Define
Organization Hierarchy.

Page 8 of 70

Company Confidential - For Internal Use Only


Figure 4 - Security Profile Form

3.1.4 Process Flow

The following process flow lists the steps to implement Operating Unit security. This process flow
applies to all of the cases listed above.

Page 9 of 70

Company Confidential - For Internal Use Only


D e fin e O p e r a tin g
U n it s

Is th e re a n
Yes O U h ie r a r c h ic a l
s tru c tu re ?

N o

STEP 2
STEP 1 D e fin e S e c u r ity
D e fin e P r o file & R u n
O r g a n iz a tio n S e c u r ity L is t
H ie r a r c h y M a in te n a n c e
P ro g ra m

STEP 3
S e t M O : S e c u r ity
P r o file

STEP 4
S e t U s e r D e fa u lt
O U

STEP 5
R un A ccess
V a lid a t io n R e p o r t

Figure 5 - Operating Unit Security Process Flow

3.1.5 Assign MO: Security Profile to Application Responsibility

The last step is to assign a security profile to user responsibility via System Profile Values window.

Page 10 of 70

Company Confidential - For Internal Use Only


Figure 6 - Assignment of Security Profile

3.2 Specifications to Oracle Forms


3.2.1 Select Operating Unit For Setup and Transaction Forms

Selecting an Operating Unit for setup data and transactions provides the ability to users to enter
transactions and query data for multiple Operating Units with one responsibility and from within one
screen. The Operating Unit will be added as a field on all product forms with which users query or
update Multi-Org-striped data. This includes all transactions and setup data that are Operating Unit
specific. Setup forms not affected are those through which users access global data, such as AP payment
terms, AP calendars and AR receipt classes.

Adding the Operating Unit to all Multi-Org forms will assure that the user will only need one
responsibility for both transaction and setup data for the Operating Units that a user has access. It has
been considered to not provide this feature for setup data, since the volume and/or frequency of adding
or updating setup data can be small. If we did not provide this feature, however, the user would be
forced to create additional responsibilities solely for setup data, which would defeat a main objective of
decreasing the overall number of responsibilities.

The user will enter the setup and transaction forms, select the Operating Unit in the first field, and then
enter the data for the specific Operating Unit. The list of values for the Operating Unit field will be
restricted to the Operating Unit organizations to which the user's application responsibility has access.

There exist several benefits of providing the Operating Unit field on Multi-Org setup and transaction
screens.

 A user can query setup and transaction data for all the Operating Units to which the user has access.
The user does not need to know the Operating Unit by which data might be partitioned.

 A user can make use of the duplicate record feature in forms (from the Edit menu) where applicable,
to copy data from one Operating Unit to another.

 A user will be able to easily tell whether or not setup data is Multi-Org partitioned or not. If data
accessed through a particular setup screen is not org striped, then the Operating Unit field will not
appear in the screen. Currently, the user must refer to the product user's guide or navigate between
Operating Units to see if the setup data are the same for all Operating Units (global) or Operating
Unit specific.

3.2.1.1Recommendations For Operating Unit Field In Forms

General recommendations:

 The Operating Unit field should be placed in the top left corner of the form
Page 11 of 70

Company Confidential - For Internal Use Only


 Global data can be entered before the Operating Unit. Operating Unit specific data should be
entered after the Operating Unit is selected

 If user responsibility allows access to one Operating Unit only, then value for the Operating Unit
field and its dependent attributes should be defaulted

Placement of the Operating Unit field on the window is dependent on the type of window. Child
windows must display the Operating Unit name only in the window title bar in the context of a saved
parent record.

When determining whether to place the Operating Unit on the screens, you should consider various types
of forms. There are two general form models in the applications: single record and multi-record format.
Single record formats allow the maximum number of fields for a single record to be displayed at one
time. Multi-record formats allow the maximum number of records for a single database entity to be
displayed at one time.

The following examples illustrate the different scenarios:

Case 1: Single Record Format--Master/Detail records displayed in a single window


An example is the Payables Distribution Sets form, which allows entry and display of all the attributes of
the distribution set in a single window. The Operating Unit field should be displayed in the master
block. If the detail records can be entered only in the same Operating Unit as the master record, then,
the Operating Unit field need not be displayed in the detail block.

Page 12 of 70

Company Confidential - For Internal Use Only


Figure 7 - Single record format example: Payables Distribution Sets

Case 2: Single Record Format--Master/Detail records displayed in multiple windows


An example of this type is the Receivables Transaction workbench form, which allows a transaction to
be entered in multiple windows - Transaction header and Distributions, Lines, etc. child windows. The
Operating Unit field should be displayed in the master window. If the detail records can be entered only
in the same Operating Unit as the master record, then, the Operating Unit field need not be displayed in
the child windows. Instead, the Operating Unit name should appear in each of the child windows.

Figure 8 - Single record format with tabs example: Receivables Transaction Workbench

Page 13 of 70

Company Confidential - For Internal Use Only


Figure 9 - Child windows example: Receivables Distributions

Case 3: Single Record Format with Tab regions


Single record formats often use tab regions to display all record attributes within a single window. An
example is the Payables Financials Options form. The Operating Unit field should be displayed above
the tab region, so that the field is visible from all tab pages.

Figure 10 - Single record format example with tab regions: Payables Financials Options

Case 4: Multi-Record Format with multiple child windows


An example is the Receivables Transaction Summary form which presents multiple rows of header
records and buttons to navigate to detail windows containing additional attributes of the record. The
Operating Unit field should be displayed in the master window. If the detail records can be entered only
in the same Operating Unit as the master record, then, the Operating Unit field need not be displayed in
the child windows. Instead, the Operating Unit name should appear in each of the child windows.

Page 14 of 70

Company Confidential - For Internal Use Only


Figure 11 - Multi-record "Summary" type example: Receivables Transaction Summary

Case 5: Find Windows


The Find window in any of the form - setup or transaction that displays the Operating Unit field, should
include Operating Unit field in the Find dialogue window. This enables easy data entry and querying
capabilities by Operating Unit.

Figure 12 - Find window

3.2.3 Operating Unit Default

To better facilitate data entry, a user can optionally set up a default Operating Unit value. A new profile
option, "MO: Default Operating Unit," is used to define defaulting Operating Unit, and it can be set at
Responsibility and User levels. The Operating Unit user defines in this profile option must be a valid

Page 15 of 70

Company Confidential - For Internal Use Only


value within his security profile for defaulting. This feature is useful when user needs to transact in
multiple Operating Units, but majority of time he transacts in one Operating Unit.

Also, an Operating Unit value is defaulted when user's security profile contains access to one Operating
Unit only. This eliminates user to explicitly define Operating Unit when he can only access to one
organization.

3.4 Reporting
Reports that are impacted by Multi-Org Access Control feature can be classified into 2 broad categories

1. Single Org Reports

2. Cross Org Reports

3.4.1 Single Org Reports

Single Org reports are the reports that display data for one Operating Unit only. Today (Multi-Org using
CLIENT_INFO) these reports show data for the Operating Unit specified by “MO: Operating Unit”
profile option. With Multi-Org Access Control, a responsibility could have access to one or more
Operating Units. Even with opening up access, the business requirement is that these reports should
continue to report data for one Operating Unit only at a time. This implies that the user needs the ability
to select an Operating Unit and submit the report. For example, if the profile option “MO: Security
Profile” gives access to 3 Operating Units, the user should have the ability to choose one Operating Unit
from the available three and submit the report.

If the user has access to only one Operating Unit, then that value should be defaulted for Operating Unit.
If user has access to multiple Operating Units, then defaulting should happen if the profile “MO: Default
Operating Unit” is set and is valid.

3.4.2 Cross Org Reports

Cross Org Reports introduced in Release 11i, are reports that report data for one or multiple Operating
Units. The Cross Org report executables and valuesets use the unsecured Multi-Org tables. Currently,
there are two parameters – Reporting Level and Reporting Context to determine at what level a user can
submit a report for (single Operating Unit, or all Operating Units under a Legal Entity, or all Operating
Units under a Set of Books). The valuesets for these parameters will be modified for MOAC project to
take “MO: Security Profile” profile option into consideration. Also Ledgers will replace Set of Books for
the Reporting Level as part of Ledger uptake.

Although there is no functional impact to Cross Org reports by Multi-Org Access Control feature, a
minor security enhancement is made. Security enhancement will list valid values for Reporting Level
and Reporting Context based on security access privilege. This is a change from today where profile
“MO: Operating Unit” is considered for security and not “MO: Security Profile” profile.

A user will be able to report at the Ledger level only if all the Operating Units under the current ledger
are encompassed by the security profile and the value of “MO: Top Reporting Level” profile is Ledger.

A user will be able to report at the Legal Entity level only if all the Operating Units under at least 1 legal
entity are encompassed by the security profile and the value of “MO: Top Reporting Level” profile is
Legal Entity or Ledger. The available reporting contexts are the Legal Entities that have the Operating
Units encompassed by the current security profile.

A user will always be able to report at the Operating Unit level. The available reporting contexts are the
Operating Units that are encompassed by the current security profile.

At submission time, when Cross Org reports are selected, the temporary table is initialized with one or
multiple Operating Units based on “MO: Security Profile” profile option. The temporary table controls
the data the users sees for the Reporting Level and Reporting Context parameters. For this special
processing of Multi-Org initialization, Cross-Org reports need to be flagged as MULTIPLE in the Define
Concurrent Programs form for the Operating Unit mode.

Page 16 of 70

Company Confidential - For Internal Use Only


3.4.3 Define Concurrent Programs Window

The Define Concurrent Programs form is modified to include a new field “Operating Unit
mode” that would allow users to categorize the concurrent programs for Multi-Org Access
Control feature uptake. The concurrent programs can be categorized into SINGLE, MULTIPLE
or NULL. By default the value of this new field is NULL or blank. A poplist allows users to
change the value. The concurrent program category is used to execute the Multi-Org
initialization and also determine when to expose Operating Unit field in the Submit Requests
window and Schedule Requests window.

3.5 Concurrent Programs (others than reports)


Concurrent Programs that are affected by Multi-Org Access Control are classified into 2 broad
categories:

1. Single Org Concurrent Programs

2. Concurrent Programs that run for the Security Profile

3.5.1 Single Org Concurrent Programs

Single Org concurrent programs are non-report programs that report or process data for one Operating
Unit only. Today (Multi-Org using CLIENT) these programs show data for the Operating Unit specified
by “MO: Operating Unit” profile option. With Multi-Org Access Control, a responsibility could have
access to one or more Operating Units. Even with opening up access, the business requirement is that
these concurrent programs should continue to report or process data for one Operating Unit only at a
time. This implies that the user needs the ability to select an Operating Unit and submit the program.

These programs are treated in the same way as the Single Org Reports and should be flagged as
SINGLE category for Operating Unit mode in the Define Concurrent Programs window.

Special processing is done for these programs in the SRS window and the Schedule Requests pages to
initialize the temporary table and expose Operating Unit special parameter.

3.5.2 Multiple Org Concurrent Programs


These are concurrent programs that process or report data for one or multiple Operating Units
specified by “MO: Security Profile” profile option. Such programs should expose Operating
Unit as an optional parameter. User selects an Operating Unit and submits the program or
leaves it blank. If the parameter is left blank, the concurrent program should process or report
data for the Operating Units specified by “MO: Security Profile”.

Figure below shows SRS screen with the Operating Unit parameter added for the Payables Open
Interface Import program. Users may choose to enter a value for the Operating Unit field and thus
submit the request for only the specified organization, or they may leave the field blank and process
invoices for all the Operating Units within the current security profile.

Page 17 of 70

Company Confidential - For Internal Use Only


Figure 13 - Submission screen for the Payables Open Interface Import concurrent program.

Multiple Org Concurrent programs require that the Multi-Org temporary table be populated
with one or multiple Operating Units depending upon the profile option “MO: Security Profile”.

These programs should be flagged as MULTIPLE for Operating Unit mode in the Define
Concurrent Programs so that the Multi-Org temporary table is initialized when the user selects
the program.

The executables for such concurrent programs should be modified to utilize the Operating Unit
parameter to avoid Cartesian joins and process or report data correctly for the specified
Operating Units.

No special processing is done for such concurrent programs at runtime, since the executables are
modified to handle multiple Operating Units access. The Multi-Org initialization populates the
temporary table with one or multiple Operating Units based on the access enabled status of the product
owning the concurrent program at runtime.

3.6 APIs
Public APIs that are affected by Multi-Org Access Control should accept Operating Unit as input either
as parameter or by defaulting from “MO: Default Operating Unit” profile option similar to the Forms UI
or the Framework Pages. Prior to Multi-Org Access Control, the APIs process data for one Operating
Unit only controlled by “MO: Operating Unit” profile.
 With Multi-Org Access Control, the processing should validate that Operating Unit is passed as
input and it is valid (included within the user responsibility profile “MO: Security Profile”). If
a user is trying to process data for an Operating Unit that (s)he does not have access to, critical
error should be raised and further processing stopped.

3.7 Workflows
Workflows that are affected by Multi-Org Access Control should allow users to initiate the process for
any Operating Unit that (s)he has access to without having to switch responsibility. Also, workflow
administrators should be able to perform administrative tasks like Abort Process or Expedite Process
irrespective of the access to the Operating Unit the workflow is initiated for.

Workflows submitted from UI pages, would have the Operating Unit validated upstream, since the LOV
would allow users to pick an Operating Unit that (s)he has access to. Similarly, workflows submitted
within Public APIs would have the Operating Unit validated before the workflow process is initiated.
The Operating Unit (ORG_ID) should be captured as part of the activity to be processed and should be
used to set the Multi-Org VPD policy context. For example, the ORG_ID of the item keys like Credit
Memo Request Id, Expense Report Header Id etc. The profile options “MO: Default Operating Unit”,
“MO: Security Profile” or “MO: Operating Unit” should not be relied upon for workflows, since the

Page 18 of 70

Company Confidential - For Internal Use Only


Operating Unit of the transaction could be different from the Operating Unit(s) responsibility gives
access to.

6.

Page 19 of 70

Company Confidential - For Internal Use Only


4. Technical Feature Descriptions
 This section provides developers information necessary to implement Multi-Org Access Control
feature. Application developers must carefully study these technical guidelines, and implement
the access control feature according to the guideline.

4.1 Access Control Architecture


4.1.1 Background

Multiple Organization Architecture was first introduced in Release 10.6, for data security by Operating
Unit. In Release 10.7, we added a column, ORG_ID, to each base table that requires ”partitioning” by
Operating Units. All the tables that are partitioned are renamed with suffix, ‘_ALL’, and their
corresponding secured views are created in APPS schema. The diagram given below shows the single
organization view in the applications (APPS) schema.

Figure 14 - Database Schema

Multi-Org views restrict data access by filtering records for a single Operating Unit set by application
responsibility level profile, ““MO: Operating Unit”.” The value for the profile option is cached in
Application Context, and is initialized whenever FND initialization routine is called. All Multi-Org
views as well as any SQL statements that require Multi-Org security contains FND CLIENT_INFO
predicate. FND_CLIENT_INFO function retrieves ORG_ID value stored in the application context. The
value is valid during a session unless it is explicitly changed by procedure calls.

To retrieve all information regardless of the Operating Unit, the _ALL table should be used in the SQL
statement. Cross-Organization reports are good example in which the query statements are performed
against _ALL tables rather than Multi-Org secured views. Most Oracle Financials reports generate
outputs from a single Operating Unit, and the query statements are performed against Multi-Org views.
In order to increase flexibility and performance in Multi-Org environment while providing the same
level data security, Virtual Private Database (VPD) feature introduced in Oracle 8i RDBMS will replace
usage of CLIENT_INFO function in Multi-Org Access Control.

4.1.2 Virtual Private Database (VPD)

The Virtual Private Database feature allows developers to enforce security by attaching a security policy
to tables and views in Oracle8i, and to synonyms in Oracle 9i Release2. It attaches predicates for the
security policies to every SQL statement against the database objects where policies are applied. When a
user directly or indirectly accesses a table with a security policy, the RDBMS dynamically rewrites user’s
SQL statement to include conditions set by security policy transparently to the user. The conditions can
be expressed in, or returned by a function.

Access to single Operating Unit

Page 20 of 70

Company Confidential - For Internal Use Only


AP Schema
APPS Schema
AP_INVOICES_ALL table
ORG_ID INVOICE_ID
AP_INVOICES_ALL (synonym to the 1 1000
AP_INVOICES_ALL table) 1 1001
1 1002
2 1003
AP_INVOICES (synonym with the 2 1004
security policy attached that gives
access to ORG_ID=1)

Figure 15 - Database Schema

Access to multiple Operating Units

AP Schema
APPS Schema
AP_INVOICES_ALL table
ORG_ID INVOICE_ID
AP_INVOICES_ALL (synonym to the 1 1000
AP_INVOICES_ALL table) 1 1001
1 1002
2 1003
AP_INVOICES (synonym with the 2 1004
security policy attached that gives
access to ORG_ID 1 and 2)

Figure 16 - Database Schema

4.1.3 Multi-Org Security Policy Predicate

The single organization views that have the CLIENT_INFO predicate attached to them should be made
obsolete and synonyms should be created to replace them. A security policy function should be attached
to the Multi-Org synonyms during install time. The security is in place, no matter whatever tools is used
to access the secured synonyms.

The security policy function returns different predicate based on the number of Operating Units access.
An application context attribute “ACCESS_MODE” is set based on the Operating Units access. The
policy function is dynamic, as it is reparsed, whenever a SQL statement is executed. The reason to opt
for dynamic security policy function is to minimize the coding impact. The Multi-Org code today works
in the context of one Operating Unit. Majority of the code can be reused if the policy predicate can
change dynamically. For example, you open a form from a responsibility that has access to multiple
Operating Units. After an Operating Unit is selected, the Operating Unit context is established and the
code that is used for validation from that point onwards need not be modified if the synonyms return
data for the Operating Unit selected.

When the access_mode is Multiple (M), the policy predicate issues an EXISTS sub-query against a
global temporary table. The global temporary table is a new feature introduced in Oracle 8i. It allows
table to store and manipulate data specific to a SESSION or TRANSACTION. When the access_mode
is Single, a simple equality predicate is used for performance reasons, since it is cost effective compared
to using the temporary table. An access mode of All (A) is incorporated for future purposes where the
security is bypassed for functionalities that need full table access. If the access_mode is not set, then a
simple predicate that uses the CLIENT_INFO value for ORG_ID is used for the policy predicate. This is
to support the backward compatibility for products, which have not enabled the Multi-Org Access
Control feature, but have made the datamodel changes.

MO_GLOBAL.Org_Security function:
FUNCTION org_security(obj_schema VARCHAR2,
                      obj_name   VARCHAR2) RETURN VARCHAR2

Page 21 of 70

Company Confidential - For Internal Use Only


IS
BEGIN
  ­­
  ­­  Returns different predicates based on the access_mode
  ­­  The codes for access_mode are
  ­­  M ­ Multiple OU Access
  ­­  A ­ All OU Access
  ­­  S ­ Single OU Access
  ­­  Null ­ Backward Compatibility ­ CLIENT_INFO case
  ­­
  IF g_access_mode IS NOT NULL THEN
    IF g_access_mode = 'M' THEN
      RETURN 'EXISTS (SELECT 1
                        FROM mo_glob_org_access_tmp oa
                       WHERE oa.organization_id = org_id)';
    ELSIF g_access_mode = 'A' THEN ­­ for future use
      RETURN NULL;
    ELSIF g_access_mode = 'S' THEN
      RETURN 'org_id = 
              sys_context(''multi_org2'',''current_org_id'')';
    END IF;
  ELSE
    RETURN 'org_id = substrb(userenv(''CLIENT_INFO''),1,10)';
  END IF;
END org_security;

The simple predicate using CLIENT_INFO is used for these cases:


 access is not enabled meaning for backward compatibility. Multi-Org Access
Control is not enabled for all products at one time. There are Multi-Org views that
are shared between products that are at different levels. For example, Payables
opens up access and Purchasing does not. The views that AP shared with PO
(PO_VENDOR_SITES, PO_HEADERS, etc) have to be replaced by PO to secured
synonyms. The secured synonyms should continue to work as today for PO, since
their code is not modified and PO code relies on CLIENT_INFO.

The simple predicate using current_org_id is used for these cases:


 access is enabled, but limited to only Operating Unit. For example, MO: Security
Profile gives access to only one Operating Unit or it is not set, in which case, the
access is based on “MO: Operating Unit”. The access_mode is set to 'S' for this
case.
 access is enabled and security profile gives access to multiple Operating Units, but
within the scope of a transaction, since Operating Unit is determined, a simple
predicate would eliminate additional changes to the server and client side code.
The access mode is set to 'S' for this case.
The reason 2 simple predicates are used one with access_mode = 'S' and the other with Null is to
eliminate the need to set the current_org_id for backward compatibility. If we combine the two
predicates into one, we need to either set the current_org_id along with CLIENT_INFO, or use
CLIENT_INFO as fall back for current_org_id.

The complex predicate is used for these cases:


 access is enabled and the security profile gives access to multiple Operating Units and
the broader access is needed for querying data, derive Operating Unit feature and
consolidated transactions where the scope of the transaction extends to multiple
Operating Units. The access mode is set to 'M' for this case.

For example, any select statement on RA_CUSTOMER_TRX (synonym to which the security policy is
attached) will be dynamically modified to make use of the policy predicate.

A simple query by the user:

SELECT trx_number from ra_customer_trx

Page 22 of 70

Company Confidential - For Internal Use Only


will be modified at runtime if the responsibility has access to multiple Operating Units as:

SELECT trx_number from ra_customer_trx
 WHERE (EXISTS (SELECT 1
                  FROM mo_glob_org_access_tmp oa
                 WHERE oa.organization_id = org_id))

or will be modified at runtime if the responsibility has access to one Operating Unit with access control
enabled for the module as:

SELECT trx_number from ra_customer_trx
 ORG_ID = sys_context('multi_org2','current_org_id')

4.1.4 Multi-Org Initialization

Multi-Org Access Control feature is developed and delivered in phases by financials products first
followed by other products. The Multi-Org global temporary table is populated based on either “MO:
Security Profile” or “MO: Operating Unit” profile option. The profile option “MO: Security Profile”
takes precedence over “MO: Operating Unit”. Until Access Control is turned on for a product, the
profile option “MO: Security Profile” is ignored and only “MO: Operating Unit” is honored.

Products at different levels, access control enabled and not enabled (in transition) can be combined
together under one application menu. Under such case, the Multi-Org initialization should be based on
the application of the calling module and not based on the application tied to the responsibility, since the
profile Option “MO: Security Profile” should be ignored for products who have not enabled access or in
the transition phase.

A new table (MO_PRODUCT_INIT) is introduced for product teams to register their application after
they have opened up access for their product. An entry in this table indicates that the product is Multi-
Org Access Control enabled. The Multi-Org initialization API makes use of the module owner calling
the initialization to initialize the temporary table appropriately with one or multiple Operating Units
depending upon the product status.

Product teams must seed an entry in the Multi-Org table when they are ready to turn on Multi-
Org Access Control for their product.

Table FND_MO_PRODUCT_INIT

When Payables (XXXCHR) opens up access, they must seed a row in the Multi-Org table to indicate that
access is turned on. CRM foundation (JTF) has Multi-Org Access Control turned on already.

Application_Short_Name

JTF

XXXCHR

A loader file must be delivered to the customer to populate this information at the site. Please contact
Shared Services team for the loader file. A loader configuration file afmoinit.lct is available for
extracting the loader file.

Initially the plan was to use register Multi-Org initialization in AOL Callout tables. Since the AOL
Callout routines use the application tied to the responsibility for initialization, the module information
stored in the V$SESSION was planned to be used in the Multi-Org initialization. However there are
certain limitations with this approach:

1. Inability to identify the module owner

Module information is not guaranteed to be set for all modules, especially Self Service
Applications, where there is no strong tie between functions and pages.

Page 23 of 70

Company Confidential - For Internal Use Only


2. AOL Callout is not reentrant

AOL Initialization will be executed only when there is a responsibility change (validateSession
routine is optimized to execute only under a context change). When accessing different pages
from within the same responsibility, the pages belonging to different applications (access enabled
and not enabled), the initialization is done one way which does not work when navigating from a
page that is not enabled access to a page that is enabled or vice versa.

Due to the above reasons, Multi-Org initialization will not be registered in the AOL Callout tables
anymore. Instead, it will be executed only when called explicitly by the products.

Products should call MO_GLOBAL.init() API to execute the Multi-Org initialization.

Multi-Org initialization performs two things:

1. Initializes the security policy predicate

2. Populates a global temporary table used in the UIs and the security policy function.

Functions are available to access data from the temporary table. You should not access the global
temporary table directly in their code. You should use the PL/SQL functions instead.

Pseudo Code for MO Initialization function is given below:

MO_GLOBAL.Init Procedure
PROCEDURE init(p_appl_short_name  VARCHAR2)
IS
   l_security_profile_id  
fnd_profile_option_values.profile_option_value%TYPE := NULL;
   l_org_id               
fnd_profile_option_values.profile_option_value%TYPE := NULL;

   NO_APPL_NAME         EXCEPTION;
BEGIN
   IF is_multi_org_enabled = 'Y' THEN
      IF p_appl_short_name IS NULL THEN
        RAISE NO_APPL_NAME;  ­­ Seed a new mesg ???
      ELSE
        ­­
        ­­ Get the profile values and call set_org_access API
        ­­
        fnd_profile.get('XLA_MO_SECURITY_PROFILE_LEVEL',  
                        l_security_profile_id);        
        fnd_profile.get('ORG_ID', l_org_id);
        set_org_access(l_org_id, 
                       l_security_profile_id, 
                       p_appl_short_name);
      END IF;
   END IF; ­­ Multi­Org is enabled
EXCEPTION
   …
END init;

MO_GLOBAL.Set_Org_Access Procedure

This API can be called to execute Multi-Org initialization outside of Applications. For example, to
execute Multi-Org initialization from tools like SQL*Plus, TOAD etc.
PROCEDURE set_org_access(p_org_id_char     VARCHAR2,
                         p_sp_id_char      VARCHAR2,

Page 24 of 70

Company Confidential - For Internal Use Only


                         p_appl_short_name VARCHAR2)
IS
  PRAGMA  AUTONOMOUS_TRANSACTION;
  l_access_ctrl_enabled    VARCHAR2(1);
  l_security_profile_id    
fnd_profile_option_values.profile_option_value%TYPE := p_sp_id_char;
  l_org_id                 
fnd_profile_option_values.profile_option_value%TYPE := p_org_id_char;

  l_current_org_id         hr_operating_units.name%TYPE;
  l_view_all_orgs          VARCHAR2(1);

  NO_SP_OU_FOUND           EXCEPTION;
  NO_ORG_ACCESS_FOUND      EXCEPTION;

BEGIN

   IF is_multi_org_enabled <> 'Y' THEN
      RETURN;
   END IF;

   IF p_org_id_char IS NULL AND p_sp_id_char IS NULL THEN
     RAISE NO_SP_OU_FOUND;
   END IF;
   ­­
   ­­ Replace this code with 10g shared globals
   ­­
   BEGIN
     SELECT nvl(mpi.status, 'N')
       INTO l_access_ctrl_enabled
       FROM fnd_mo_product_init mpi
      WHERE mpi.application_short_name = p_appl_short_name;
   EXCEPTION
     WHEN NO_DATA_FOUND THEN
       l_access_ctrl_enabled := 'N';
     WHEN OTHERS THEN
       generic_error('MO_GLOBAL.SET_ORG_ACCESS', sqlcode, sqlerrm);
   END;

   ­­
   ­­ Delete temporary table data first for all products access 
   ­­ enabled or not
   ­­
   delete_orgs;
   ­­
   ­­ For all products, when the access control feature is enabled,
   ­­ 1. Use the MO: Security Profile if it is set.
   ­­ 2. Use the MO: Operating Unit if “MO: Security Profile” is not
   ­­    set
   ­­
   IF (l_access_ctrl_enabled = 'Y') THEN
     IF l_security_profile_id IS NOT NULL THEN
       l_org_id := null;
     END IF;
     ­­
     ­­ Populate temp table
     ­­
     populate_orgs(l_org_id,
                   l_security_profile_id,
                   l_current_org_id,
                   l_view_all_orgs);
     ­­
     ­­ Check if you have access to at least one Operating Unit.
     ­­

Page 25 of 70

Company Confidential - For Internal Use Only


     IF g_ou_count = 0 THEN
       RAISE NO_ORG_ACCESS_FOUND;
     ELSIF g_ou_count = 1 THEN
       ­­
       ­­ Set the 'Single' access contexts:
       ­­
       set_policy_context('S', l_current_org_id);
     ELSE
       ­­
       ­­ Added code for All mode to avoid using the policy predicate
       ­­ when user has access to global view all security profile
       ­­ Bug (2720892)
       ­­ Set the access contexts:
       ­­
       IF l_view_all_orgs = 'Y' THEN
         set_policy_context('A','');
       ELSE
         set_policy_context('M','');
       END IF;
     END IF;
   ELSE
     ­­
     ­­ Reset the context for products that have not enabled access 
     ­­ control
     ­­
     set_policy_context('','');
   END IF;

   commit;

EXCEPTION
   …

END set_org_access;

4.1.5 Datamodel Design

New Tables
MO_GLOB_ORG_ACCESS_TMP

This is a session-specific global temporary table that stores the Operating Units contained in the current
responsibility's (or site level) “MO: Security Profile” profile option. If the profile option, “MO: Security
Profile” is not defined then, the Operating Unit contained in current responsibility’s (or site level) “MO:
Operating Unit” profile option is stored in the table. It is populated with records from the tables/views
PER_ORGANIZATION_LIST and HR_OPERATING_UNITS. It is used in the Multi-Org security
policy initialization.
Column Name Type Null? Unique? Column Translatable?
Description
ORGANIZATION_ID Number(15) Not Yes Operating No
Null Unit
identifier
NAME Varchar2(240) null Name of the Yes
Operating
Unit

A unique index MO_GLOB_ORG_ACCESS_TMP_U1 exists on ORGANIZATION_ID column.

FND_MO_PRODUCT_INIT
Page 26 of 70

Company Confidential - For Internal Use Only


This table is used to store the information if a product is Multi-Org Access Control enabled. An entry in
this table indicates that the product has opened up access. If a product has enabled access, the Multi-
Org initialization code will use the precedence of “MO: Security Profile” over “MO: Operating Unit”.
Column Name Type Null? Unique? Column Translatable?
Description
APPLICATION_SHORT_ Varchar2(50) Not Yes Application No
NAME Null Short Name
CREATION_DATE Date Not Creation Date No
Null
CREATED_BY Number(15) Not Created By No
Null
LAST_UPDATED_BY Number(15) Not Last Updated No
Null By
LAST_UPDATE_DATE Date Not Last Update No
Null Date
LAST_UPDATE_LOGIN Number(15) Last Update No
Login

A unique index FND_MO_PRODUCT_INIT_U1 exists on APPLICATION_SHORT_NAME column.

4.1.6 Profile Options

Prior to opening up access, upon choosing a responsibility, the CLIENT_INFO org predicate was
initialized to the Operating Unit the responsibility has access. The new profile option, “MO: Security
Profile,” will be used in Multi-Org Access Control feature. This profile option can be set at Site and
Responsibility levels.

There is also a new profile option available for defaulting Operating Unit in setup and transaction forms.
This profile option is set at Site, Responsibility and User levels.

MO: Security Profile

Field Name Value


Name XLA_MO_SECURITY_PROFILE_LEVEL
Application Oracle Common Accounting Modules
User Profile Name MO: Security Profile
Description Multi-Org Access Control
SQL Validation SQL="SELECT
S.SECURITY_PROFILE_NAME \"Security Profile\",
S.SECURITY_PROFILE_ID
INTO :VISIBLE_OPTION_VALUE ,
:PROFILE_OPTION_VALUE
FROM PER_SECURITY_PROFILES S
ORDER BY S.SECURITY_PROFILE_NAME"
COLUMN="\"Security Profile\"(*)"
User Access Visible: Yes
Updatable: No
Program Access Visible: Yes
Updatable: No
System Administrator Access: Site Visible: Yes
Updatable: Yes
System Administrator Access: Application Visible: No
Updatable: No
System Administrator Access: Responsibility Visible: Yes
Updatable: Yes
System Administrator Access: User Visible: No
Updatable: No

Page 27 of 70

Company Confidential - For Internal Use Only


To minimize upgrade effort, Multi-Org initialization package will first attempt to read access control
from “MO: Security Profile” option. If no value is assigned for the current responsibility or site, then it
attempts to determine Operating Unit access from “MO: Operating Unit” profile option. “MO:
Operating Unit” profile will be obsolete after all Multi-Org products have been upgraded to use Multi-
Org Access Control feature.

MO: Default Operating Unit

Field Name Value


Name DEFAULT_ORG_ID
Application Application Object Library
User Profile Name MO: Default Operating Unit
Description Default Operating Unit the Responsibility Logs Onto
SQL Validation SQL="SELECT ORGANIZATION_ID, NAME INTO
:PROFILE_OPTION_VALUE, :VISIBLE_OPTION_VALUE FROM
HR_OPERATING_UNITS" COLUMN="NAME(*)"
User Access Visible: Yes
Updatable: Yes
Program Access Visible: Yes
Updatable: No
System Administrator Access: Site Visible: Yes
Updatable: Yes
System Administrator Access: Application Visible: No
Updatable: No
System Administrator Access: Responsibility Visible: Yes
Updatable: Yes
System Administrator Access: User Visible: Yes
Updatable: Yes

User can optionally set this profile option to default an Operating Unit and other Multi-Org dependent
values in user windows. Defaulting will occur only if user’s security profile includes the Operating Unit
specified in this profile option.

4.1.8 Multi-Org APIs

Temporary Table processing


The Multi-Org temporary table MO_GLOB_ORG_ACCESS_TMP is populated when Multi-Org
initialization is invoked. Product teams must not reference the temporary table directly anywhere in
their code. Instead they should use the functions given below to retrieve data from the temporary table:

MO_GLOBAL.Check_Access Function

This function checks if a particular ORG is available in the temporary table populated by the
set_org_access API. If found the function returns flag ‘Y’, otherwise ‘N’.
FUNCTION check_access(p_org_id    NUMBER)
RETURN VARCHAR2 IS

BEGIN
  SELECT 'Y' 
    INTO l_org_exists 
    FROM mo_glob_org_access_tmp
   WHERE organization_id = p_org_id;
  RETURN 'Y';
END;

Page 28 of 70

Company Confidential - For Internal Use Only


This function can be used in the Where Clause of SQL statements to filter the data from the unsecured
objects. For example, the Operating Unit record groups in forms, should display the Operating Units
that a responsibility has access to. The data could be selected from the HR_OPERATING_UNITS views,
but you get to see all Operating Units. Using this function in the where clause, will filter the data to the
Operating Units applicable for the session.
SELECT name,
       organization_id
  FROM hr_operating_units
 WHERE mo_global.check_access(organization_id) = ‘Y’

MO_GLOBAL.get_ou_name function

This function returns the Operating Unit name for a given ORG_ID, if it exists in the temporary table
populated by the set_org_access API.
FUNCTION get_ou_name(p_org_id    NUMBER)
RETURN VARCHAR2 IS

BEGIN
  SELECT organization_name
    INTO l_ou_name
    FROM mo_glob_org_access_tmp
   WHERE organization_id = p_org_id;
  RETURN l_ou_name;
END;

This function can be used to display the Operating Unit name in the form block record groups, caching
SQL etc.

MO_GLOBAL.check_valid_org function
This function checks if an ORG_ID exists in the temporary table. It is equivalent to the check_access
function but also posts an error message if the specified Operating Unit is null or not in the access list.
The calling application can check the returned value of the function and raise an error if it is 'N'.

FUNCTION check_valid_org(p_org_id NUMBER) 
RETURN VARCHAR2
IS

BEGIN
   IF (p_org_id is null) THEN
      ­­ Post an error message and return:
      fnd_message.set_name('FND', 'MO_ORG_REQUIRED');
      FND_MSG_PUB.ADD;
      RETURN 'N';
   END IF;
   IF (check_access(p_org_id) = 'Y') THEN
      RETURN 'Y';
   END IF;
   ­­ Post an error message and return:
   fnd_message.set_name('FND', 'MO_ORG_INVALID');
   FND_MSG_PUB.ADD;
   RETURN 'N';
END;

This API can be used in the Public APIs for validating ORG_ID input.

Page 29 of 70

Company Confidential - For Internal Use Only


Policy Context APIs
APIs are available to set the application context attributes used in the security policy function.

MO_GLOBAL.set_policy_context Procedure

This API sets the application context attributes - current org id and the access mode, which are used in
the Multi-Org security policy function org_security. The current_org_id context can also be used in the
product specific server side validation APIs.

Multi-Org code available today, works within the context of one Operating Unit. To reuse the code, the
application context attribute access_mode can be set to single, so that validation APIs can continue to
work within the context of one Operating Unit without any change. This API can be used to set the
policy context in the different triggers in the forms.

MO_GLOBAL.get_current_org_id Function

This function returns the current_org_id attribute value stored in the application context.
FUNCTION get_current_org_id
RETURN NUMBER IS

BEGIN
  RETURN to_number(g_current_org_id
END;

MO_GLOBAL.get_access_mode Function

This function returns the access_mode attribute value stored in the application context.
FUNCTION get_access_mode
RETURN VARCHAR2 IS

BEGIN
   RETURN (g_access_mode); 
END;

Access Control Registration APIs


APIs are available to register or remove an application as access enabled in the Multi-Org table.

FND_MO_PRODUCT_INIT_PKG.register_application Procedure

This API populates an entry in the FND_MO_PRODUCT_INIT_PKG indicating that a product is Multi-
Org Access Control enabled.

FND_MO_PRODUCT_INIT_PKG.remove_application Procedure

This API deletes an entry in the FND_MO_PRODUCT_INIT_PKG.

Org Defaulting APIs


APIs are available to get the default Operating Unit. A new profile option (“MO: Default Operating
Unit”) is available to define the Operating Unit to be used as default for your site, responsibility or user.
The default Operating Unit will be used in both setup as well as transaction forms. When you choose a
responsibility, the Multi-Org initialization code will set the global variables for the default Operating
Unit name and ORG ID.

Page 30 of 70

Company Confidential - For Internal Use Only


The profile option “MO: Default Operating Unit” can be set at site, responsibility and user levels.
However, the profile option “MO: Security Profile” is set at site or responsibility level. The defaulting
is based on the Multi-Org profile options setup. It is possible that, the default Operating Unit value that
is set at user level may not be included in the security profile set at responsibility level. This is taken
into consideration in the Multi-Org API that validates the profile options and returns the default value.
Following defaulting rules apply:
 If the profile option “MO: Security Profile” is not set, then “MO: Operating Unit” value
is used as the default Operating Unit even if “MO: Default Operating Unit” profile is set
to a different value.

 If the profile option “MO: Security Profile” is set and gives access to one Operating
Unit, the default Operating Unit will return this value even if “MO: Default Operating
Unit” is set to a different value.

 If the profile option “MO: Security Profile” is set and gives access to multiple Operating
Units, then the profile value “MO: Default Operating Unit” if set is validated against the
list of Operating Units in “MO: Security Profile”. If the Operating Unit is included in
the security profile then it is returned as the default value. Otherwise there is no
Operating Unit default. Also, if the Profile Option “MO: Default Operating Unit” is not
set, then there is o default Operating Unit.

MO_UTILS.Get_Default_OU Procedure

MO_UTILS.get_default_org_id Function

This API returns the default Operating Unit ORG_ID for a given responsibility. The default ORG_ID
could be NULL, if there is no valid default Operating Unit, which is determined by the defaulting rules.

7.2 Multi-Org Views/Tables Change


Each product team owning Multi-Org views/tables should carefully review and implement proposed
changes:
7.2.1 Enforce NOT NULL constraint on ORG_ID column
Modify your Multi-Org tables (_ALL, _ALL_TL and _ALL_B) to add NOT NULL constraint on
ORG_ID column. Multi-Org is mandatory for R12. Even in Multi-Org instance NULL value for
ORG_ID is allowed for global data (transaction and seed).
The datatype of ORG_ID column should be as shown below:

Column Name DataType Not Null

ORG_ID Number(15) Yes

7.2.2 Modify your Database Views


The Multi-Org Access Control mechanism makes use of a security policy attached to the Multi-Org
synonyms to implement security instead of the CLIENT_INFO predicate. Currently, the security is
implemented in the Multi-Org views by the CLIENT_INFO predicate.

The Multi-Org views can be divided into two categories, single organization views and reference views.

Single Organization views are views based on the _ALL, _ALL_B or_ ALL_TL Multi-Org tables and
have the single org predicate attached to them to return data for the current Operating Unit specified by
the CLIENT_INFO environment variable. The tables’ _ALL_B and _ALL_TL were introduced for
Multi-Lingual Support (MLS).

Page 31 of 70

Company Confidential - For Internal Use Only


Reference Views are the views that are joined to single organization views. They do not have the
single org predicate attached to them. They may or may not have the ORG_ID column included in the
view definition.

The changes that need to be done to single organization views and reference views are explained in
detail here.

Single Organization Views


All single organization views must be replaced by synonyms to _ALL tables. The security policy
function must be attached to the synonyms to enforce Operating Unit security.

Case 1: Single Organization view

Example 1:
The view definition of single organization view RA_BATCHES is shown below in the example.

CREATE OR REPLACE VIEW RA_BATCHES AS 
SELECT "BATCH_ID",       
       “LAST_UPDATE_DATE",        
       "LAST_UPDATED_BY", 
       "CREATION_DATE", 
       ...
       "ORG_ID" , 
       "PURGED_CHILDREN_FLAG" , 
       "ISSUE_DATE" , 
       "MATURITY_DATE" , 
       "SPECIAL_INSTRUCTIONS" , 
       "BATCH_PROCESS_STATUS" , 
       "SELECTION_CRITERIA_ID" 
  FROM RA_BATCHES_ALL 
 WHERE 
NVL(ORG_ID,NVL(TO_NUMBER(DECODE(SUBSTRB(USERENV('CLIENT_INFO'),1,1), 
' ',NULL,SUBSTRB(USERENV('CLIENT_INFO'),1,10))),­99)) = 
NVL(TO_NUMBER(DECODE(SUBSTRB(USERENV('CLIENT_INFO'),1,1),' ',NULL, 
SUBSTRB(USERENV('CLIENT_INFO'),1,10))),­99)

This single organization view RA_BATCHES must be replaced by a synonym as given below:

CREATE SYNONYM RA_BATCHES FOR AR.RA_BATCHES_ALL

The summary of changes that must be done for single organization views joined to single _ALL table
are given below:

 Drop the single organization view


 Create a synonym with the same name as the obsolete single organization view
 Attach policy function to the synonym

Example 2:

The view definition of simple single organization view AR_VAT_TAX_B is shown below in the
example.

CREATE OR REPLACE VIEW AR_VAT_TAX_B AS 
SELECT "VAT_TAX_ID", 
       "SET_OF_BOOKS_ID", 
       "TAX_CODE", 
       ...
       "ORG_ID",
...
  FROM AR_VAT_TAX_ALL_B 
Page 32 of 70

Company Confidential - For Internal Use Only


 WHERE 
NVL(ORG_ID,NVL(TO_NUMBER(DECODE(SUBSTRB(USERENV('CLIENT_IN
FO'),1,1),
' ',NULL,SUBSTRB(USERENV('CLIENT_INFO'),1,10))),­99)) 

NVL(TO_NUMBER(DECODE(SUBSTRB(USERENV('CLIENT_INFO'),1,1),
' ',NULL, SUBSTRB(USERENV('CLIENT_INFO'),1,10))),­99)
  

This single organization view AR_VAT_TAX_B must be replaced by a synonym with the security policy
attached.

CREATE SYNONYM AR_VAT_TAX_B FOR AR.AR_VAT_TAX_ALL_B 

A Multi-Org utility is available to list single organization views by product. Click here to access the
utility.
You can access the utility from the following URL:
http://www-apps.us.oracle.com/ssa/utils/multi-org-views.html

Example 3:
The view example of AP_CARD_SUPPLIERS is as given below. This view uses ROWID alias for
ROW_ID column of the underlying AP_CARD_SUPPLIERS_ALL table.
CREATE OR REPLACE VIEW AP_CARD_SUPPLIERS AS
SELECT ROWID,
CARD_ID,
VENDOR_ID,
ORG_ID,
...
FROM AP_CARD_SUPPLIERS_ALL
WHERE
NVL(ORG_ID,NVL(TO_NUMBER(DECODE(SUBSTRB(USERENV('CLIENT_INFO'),1,1),
' ', NULL,SUBSTRB(USERENV('CLIENT_INFO'),1 ,10))), -99)) =
NVL(TO_NUMBER(DECODE(SUBSTRB(USERENV('CLIENT_INFO'),1,1),' ',NULL,
SUBSTRB(USERENV('CLIENT_INFO'),1,10))),-99)

This single organization view AP_CARD_SUPPLIERS must be replaced by a synonym with the security
policy attached.

CREATE SYNONYM AP_CARD_SUPPLIERS FOR 
AP.AP_CARD_SUPPLIERS_ALL 

When the view is replaced with a synonym, the code that is dependent on ROWID column becomes
INVALID as the synonym AP_CARD_SUPPLIERS does not have this column. Such code using
incorrect column alias should be fixed.

The AR single organization view AR_TA_CR_AGEN_INF_V has the similar issue i.e. uses alias
ROWID for ROW_ID column. The dependent objects referencing the ROWID alias should be fixed.

Example 4:
The view definition of single organization view AR_PAYMENT_SCHEDULES_V is shown below in
the example. This is a special case, where the CLIENT_INFO predicate is coded in the view definition,
for performance reasons (the union clause in this view definition makes it non mergeable, so, using base
tables instead of views in the FROM clause is preferred)

CREATE OR REPLACE VIEW AR_PAYMENT_SCHEDULES_V AS 
SELECT PS.ROWID , 
       PS.PAYMENT_SCHEDULE_ID , 
       PS.TRX_NUMBER , 
       ...
  FROM ar_lookups al_status, 

Page 33 of 70

Company Confidential - For Internal Use Only


       ar_collectors ar_coll, 
       ar_cons_inv_all cons, 
       ra_cust_trx_types_all ctt, 
       ra_batch_sources_all bs, 
       ra_customer_trx_all ct, 
       hz_cust_site_uses_all su, 
       hz_cust_accounts cust_acct, 
       hz_parties party, 
       ar_payment_schedules_all ps 
 WHERE PS.CUSTOMER_ID = CUST_ACCT.CUST_ACCOUNT_ID 
   AND CUST_ACCT.PARTY_ID = PARTY.PARTY_ID 
   AND PS.CUSTOMER_SITE_USE_ID = SU.SITE_USE_ID 
   AND PS.CUSTOMER_TRX_ID = CT.CUSTOMER_TRX_ID 
   AND CT.BATCH_SOURCE_ID = BS.BATCH_SOURCE_ID 
   AND CT.CUST_TRX_TYPE_ID = CTT.CUST_TRX_TYPE_ID 
   AND PS.STATUS = AL_STATUS.LOOKUP_CODE 
   AND AL_STATUS.LOOKUP_TYPE = 'INVOICE_TRX_STATUS'  
   AND PS.COLLECTOR_LAST = AR_COLL.COLLECTOR_ID (+) 
   AND PS.CONS_INV_ID = CONS.CONS_INV_ID (+) 
   AND 
NVL(CONS.ORG_ID,NVL(TO_NUMBER(DECODE(SUBSTRB(USERENV('CLIENT_INFO'),1
,1),' ', NULL, SUBSTRB(USERENV('CLIENT_INFO'),1,10))),­99)) = 
NVL(TO_NUMBER(DECODE(SUBSTRB(USERENV('CLIENT_INFO'),1,1), ' ', NULL, 
SUBSTRB(USERENV('CLIENT_INFO'),1,10))),­99) 
   AND 
NVL(CTT 
.ORG_ID,NVL(TO_NUMBER(DECODE(SUBSTRB(USERENV(‘CLIENT_INFO'),1,1),' ',
NULL, SUBSTRB(USERENV('CLIENT_INFO'),1,10))),99)) = 
NVL(TO_NUMBER(DECODE(SUBSTRB(USERENV ('CLIENT_INFO'),1,1), ' ', NULL,
SUBSTRB(USERENV('CLIENT_INFO'),1,10))),­99) 
   AND 
NVL(BS.ORG_ID,NVL(TO_NUMBER(DECODE(SUBSTRB(USERENV('CLIENT_INFO'),1,1
),' ', NULL, SUBSTRB(USERENV('CLIENT_INFO'),1,10))),­99)) = 
NVL(TO_NUMBER(DECODE(SUBSTRB(  USERENV('CLIENT_INFO'),1,1), ' ', 
NULL, SUBSTRB(USERENV('CLIENT_INFO'),1,10))),­ 99) 
   AND 
NVL(CT.ORG_ID,NVL(TO_NUMBER(DECODE( SUBSTRB(USERENV('CLIENT_INFO'    
),1,1), ' ', NULL, SUBSTRB(USERENV('CLIENT_INFO'),1,10))),­99)) = 
NVL(TO_NUMBER(DECODE(S UBSTRB(USERENV('CLIENT_INFO'),1,1), ' ', NULL,
SUBSTRB(USERENV('CLIENT_INFO'),1, 10))),­99) 
   AND 
NVL(SU.ORG_ID,NVL(TO_NUMBER(DECODE(SUBSTRB(USERENV('CLIENT_INFO'),1,1
),' ', NULL, SUBSTRB(USERENV('CLIENT_INFO'),1,10))),­99)) =        
NVL(TO_NUMBER(DECODE(SUBSTRB(USERENV('CLIENT_INFO'),1,1), ' ',   
NULL, SUBSTRB(USERENV('CLIENT_INFO'),1,10))),­99) 
   AND 
NVL(PS.ORG_ID,NVL(TO_NUMBER(DECODE(SUBSTRB(USERENV('CLIENT_INFO'),1,1
),' ', NULL, SUBSTRB(USERENV('CLIENT_INFO'),1,10))),­99)) = NVL(TO_N 
UMBER(DECODE(SUBSTRB(USERENV('CLIENT_INFO'),1,1), ' ', NULL, 
SUBSTRB(USERENV('CLIENT_INFO'),1,10))),­99) 
   AND PS.STATUS = NVL(ARP_VIEW_CONSTANTS.GET_STATUS,PS.ST ATUS) 
UNION ALL 
SELECT PS.ROWID, 
       PS.PAYMENT_SCHEDULE_ID, 
       PS.TRX_NUMBER, 
       ...
FROM   ar_lookups al_risk_receipt, 
       ar_cons_inv_all cons, 
       ar_receipt_methods rm, 
       ar_batch_sources_all bs, 
       ar_batches_all arb2, 
       ar_cash_receipt_history_all crh, 
       ar_cash_receipt_history_all crh_current, 
       ar_cash_receipts_all cr, 

Page 34 of 70

Company Confidential - For Internal Use Only


       hz_cust_site_uses_all su, 
       hz_cust_accounts cust_acct,
       hz_parties party, 
       ar_payment_schedules_all ps, 
       ar_cash_receipt_history_all crh_remit, 
       ar_batches_all arb_remit, 
       fnd_currencies fc 
WHERE PS.CUSTOMER_ID = CUST_ACCT.CUST_ACCOUNT_ID (+) 
  AND CUST_ACCT.PARTY_ID = PARTY.PARTY_ID (+) 
  AND PS.CUSTOMER_SITE_USE_ID = SU.SITE_USE_ID (+)
  AND PS.CASH_RECEIPT_ID = CR.CASH_RECEIPT_ID
AND CR.CASH_RECEIPT_ID = CRH.CASH_RECEIPT_ID
AND CRH.FIRST_POSTED_RECORD_FLAG = 'Y'
AND CR.RECEIPT_METHOD_ID = RM.RECEIPT_METHOD_ID
AND CRH.BATCH_ID = ARB2.BATCH_ID (+)
AND ARB2.BATCH_SOURCE_ID = BS.BATCH_SOURCE_ID(+)
AND CR.CASH_RECEIPT_ID = CRH_CURRENT.CASH_RECEIPT_ID
AND CRH_CURRENT.CURRENT_RECORD_FLAG = 'Y'
AND PS.CONS_INV_ID = CONS.CONS_INV_ID (+)
AND CR.CASH_RECEIPT_ID = CRH_REMIT.CASH_RECEIPT_ID(+)
AND CRH_REMIT.STATUS(+) = 'REMITTED'
AND CRH_REMIT.BATCH_ID = ARB_REMIT.BATCH_ID(+)
AND FC.CURRENCY_CODE = CR.CURRENCY_CODE
AND AL_RISK_RECEIPT.LOOKUP_TYPE = 'YES/NO'
AND AL_RISK_RECEIPT.LOOKUP_CODE IN
(DECODE(CRH_CURRENT.STATUS,'CLEARED','Y','N'), DECOD
E(NVL(ARP_VIEW_CONSTANTS.GET_INCL_RECEIPTS_AT_RISK,'N'),'Y', 'Y',NULL
) )
AND ARP_VIEW_CONSTANTS.GET_SALES_ORDER IS NULL
  AND 
NVL(CONS.ORG_ID,NVL(TO_NUMBER(DECODE(SUBSTRB(USERENV('CLIENT_INFO'),1
,1),' ', NULL, SUBSTRB(USERENV('CLIENT_INFO'),1,10))),­99)) = 
NVL(TO_NUMBER(DECODE(SUBSTRB(USERENV('CLIENT_INFO'),1,1
), ' ', NULL, SUBSTRB(USERENV('CLIENT_INFO'),1,10))),­99) 
  AND 
NVL(BS.ORG_ID,NVL(TO_NUMBER(DECODE(SUBSTRB(USERENV('CLIENT_INFO'),1,1
),' ', NULL, SUBSTRB(USERENV('CLIENT_INFO'),1,10))),­99)) = 
NVL(TO_NUMBER(DECODE(SUBSTRB(USERENV('CLIENT_INFO'),1,1), ' ', NULL, 
SUBSTRB(USERENV('CLIENT_INFO'),1,10))),­99) 
  AND 
NVL(ARB2.ORG_ID,NVL(TO_NUMBER(DECODE(SUBSTRB(USERENV('CLIENT_INFO'),1
,1),' ', NULL, SUBSTRB(USERENV('CLIENT_INFO'),1,10))),­99)) =
NVL(TO_NUMBER(DECODE(SUBSTRB(USERENV('CLIENT_INFO'),1,1), ' ', NULL, 
SUBSTRB(USERENV('CLIENT_INFO'),1,10))),­99) 
  AND 
NVL(CRH.ORG_ID,NVL(TO_NUMBER(DECODE(SUBSTRB(USERENV('CLIENT_INFO'),1,
1),' ', NULL, SUBSTRB(USERENV('CLIENT_INFO'),1,10))),­99)) = 
NVL(TO_NUMBER(DECODE(SUBSTRB(USERENV('CLIENT_INFO'),1,1), ' ', NULL, 
SUBSTRB(USERENV('CLIENT_INFO'),1,10))),­99) 
  AND 
NVL(CRH_CURRENT.ORG_ID,NVL(TO_NUMBER(DECODE(SUBSTRB(USERENV('CLIENT_I
NFO'),1,1),' ', NULL, SUBSTRB(USERENV('CLIENT_INFO'),1,10))),­99)) = 
NVL(TO_NUMBER(DECODE(SUBSTRB(USERENV('CLIENT_INFO'),1,1), ' ', NULL, 
SUBSTRB(USERENV('CLIENT_INFO'),1,10))),­99) 
  AND 
NVL(CR.ORG_ID,NVL(TO_NUMBER(DECODE(SUBSTRB(USERENV('CLIENT_INFO'),1,1
),' ', NULL,SUBSTRB(USERENV('CLIENT_INFO'),1,10))),­99)) = NVL(
TO_NUMBER(DECODE(SUBSTRB(USERENV('CLIENT_INFO'),1,1), ' ', NULL, 
SUBSTRB(USERENV('CLIENT_INFO'),1,10))),­99) 
  AND 
NVL(SU.ORG_ID,NVL(TO_NUMBER(DECODE(SUBSTRB(USERENV('CLIENT_INFO'),1,1
),' ', NULL, SUBSTRB(USERENV('CLIENT_INFO'),1,10))),­99)) = 
NVL(TO_NUMBER(DECODE(SUBSTRB(USERENV('CLIENT_INFO'),1,1), ' ', NULL, 

Page 35 of 70

Company Confidential - For Internal Use Only


SUBSTRB(USERENV('CLIENT_INFO'),1,10))),­99) 
  AND 
NVL(PS.ORG_ID,NVL(TO_NUMBER(DECODE(SUBSTRB(USERENV('CLIENT_INFO'),1,1
),' ', NULL,SUBSTRB(USERENV('CLIENT_INFO'),1,10))),
­99)) = NVL(TO_NUMBER(DECODE(SUBSTRB(USERENV('CLIENT_INFO'),1,1), ' 
', NULL, SUBSTRB(USERENV('CLIENT_INFO'),1,10))),­99) 
  AND 
NVL(CRH_REMIT.ORG_ID,NVL(TO_NUMBER(DECODE(SUBSTRB(USERENV('CLIENT_INF
O'),1,1),' ', NULL, SUBSTRB(USERENV('CLIENT_INFO'),1,10))),­99)) = 
NVL(TO_NUMBER(DECODE(SUBSTRB(USERENV('CLIENT_INFO'),1,1), ' ', NULL,
SUBSTRB(USERENV('CLIENT_INFO'),1,10))),­99) 
  AND 
NVL(ARB_REMIT.ORG_ID,NVL(TO_NUMBER(DECODE(SUBSTRB(USERENV('CLIENT_INF
O'),1,1),' ', NULL, SUBSTRB(USERENV('CLIENT_INFO'),1,10))),­99)) = 
NVL(TO_NUMBER(DECODE(SUBSTRB(USERENV('CLIENT_INFO'),1,1), ' ', NULL, 
SUBSTRB(USERENV('CLIENT_INFO'),1,10))),­99) 
AND PS.STATUS= NVL(ARP_VIEW_CONSTANTS.GET_STATUS,PS.STATUS)

This single organization view AR_PAYMENT_SCHEDULES_V in addition to CLIENT_INFO


predicate includes additional filter condition, which needs to stay. Hence this single organization view
must be converted to a reference view following the guidelines of reference views given in the next
section.

The CLIENT_INFO predicate must be removed from the Where Clause, ORG_ID column must be
added to the view, ORG_ID filter added for tables with ORG_ID as part of the composite key (as in
setup tables that contain seed data replicated to every org) or ORG_ID is the driving key for the table
(as in product system options tables) and the driving table for the view is replaced by a secured synonym
(AR_PAYMENT_SCHEDULES):

CREATE OR REPLACE VIEW AR_PAYMENT_SCHEDULES_V AS 
SELECT PS.ROWID , 
       PS.PAYMENT_SCHEDULE_ID , 
       PS.TRX_NUMBER , 
       ...
       PS.ORG_ID
  FROM ar_lookups al_status, 
       ar_collectors ar_coll, 
       ar_cons_inv_all cons, 
       ra_cust_trx_types_all ctt, 
       ra_batch_sources_all bs, 
       ra_customer_trx_all ct, 
       hz_cust_site_uses_all su, 
       hz_cust_accounts cust_acct, 
       hz_parties party, 
       ar_payment_schedules ps 
 WHERE PS.CUSTOMER_ID = CUST_ACCT.CUST_ACCOUNT_ID 
   AND CUST_ACCT.PARTY_ID = PARTY.PARTY_ID 
   AND PS.CUSTOMER_SITE_USE_ID = SU.SITE_USE_ID 
   AND PS.CUSTOM ER_TRX_ID = CT.CUSTOMER_TRX_ID 
   AND CT.BATCH_SOURCE_ID = BS.BATCH_SOURCE_ID 
   AND CT.ORG_ID = BS.ORG_ID
   AND CT.CUST_TRX_TYPE_ID = CTT.CUST_TRX_TYPE_ID 
   AND CT.ORG_ID = CTT.ORG_ID
   AND PS.STATUS = AL_STATUS.LOOKUP_CODE 
   AND AL_STATUS.LOOKUP_TYPE = 'INVOICE_TRX_STATUS'  
   AND PS.COLLECTOR_LAST = AR_COLL.COLLECTOR_ID (+) 
   AND PS.CONS_INV_ID = CONS.CONS_INV_ID (+) 
   AND PS.STATUS = NVL(ARP_VIEW_CONSTANTS.GET_STATUS,PS.ST ATUS) 
UNION ALL 
SELECT PS.ROWID, 
       PS.PAYMENT_SCHEDULE_ID, 
       PS.TRX_NUMBER, 
       ...
       PS.ORG_ID

Page 36 of 70

Company Confidential - For Internal Use Only


FROM   ar_lookups al_risk_receipt, 
       ar_cons_inv_all cons, 
       ar_receipt_methods rm, 
       ar_batch_sources_all bs, 
       ar_batches_all arb2, 
       ar_cash_receipt_history_all crh, 
       ar_cash_receipt_history_all crh_current, 
       ar_cash_receipts_all cr, 
       hz_cust_site_uses_all su, 
       hz_cust_accounts cust_acct,
       hz_parties party, 
       ar_payment_schedules ps, 
       ar_cash_receipt_history_all crh_remit, 
       ar_batches_all arb_remit, 
       fnd_currencies fc 
WHERE PS.CUSTOMER_ID = CUST_ACCT.CUST_ACCOUNT_ID (+) 
  AND CUST_ACCT.PARTY_ID = PARTY.PARTY_ID (+) 
  AND PS.CUSTOMER_SITE_USE_ID = SU.SITE_USE_ID (+)
  AND PS.CASH_RECEIPT_ID = CR.CASH_RECEIPT_ID
AND CR.CASH_RECEIPT_ID = CRH.CASH_RECEIPT_ID
AND CRH.FIRST_POSTED_RECORD_FLAG = 'Y'
AND CR.RECEIPT_METHOD_ID = RM.RECEIPT_METHOD_ID
AND CRH.BATCH_ID = ARB2.BATCH_ID (+)
AND ARB2.BATCH_SOURCE_ID = BS.BATCH_SOURCE_ID(+)
AND ARB2.ORG_ID = BS.ORG_ID(+)
AND CR.CASH_RECEIPT_ID = CRH_CURRENT.CASH_RECEIPT_ID
AND CRH_CURRENT.CURRENT_RECORD_FLAG = 'Y'
AND PS.CONS_INV_ID = CONS.CONS_INV_ID (+)
AND CR.CASH_RECEIPT_ID = CRH_REMIT.CASH_RECEIPT_ID(+)
AND CRH_REMIT.STATUS(+) = 'REMITTED'
AND CRH_REMIT.BATCH_ID = ARB_REMIT.BATCH_ID(+)
AND FC.CURRENCY_CODE = CR.CURRENCY_CODE
AND AL_RISK_RECEIPT.LOOKUP_TYPE = 'YES/NO'
AND AL_RISK_RECEIPT.LOOKUP_CODE IN
(DECODE(CRH_CURRENT.STATUS,'CLEARED','Y','N'), DECOD
E(NVL(ARP_VIEW_CONSTANTS.GET_INCL_RECEIPTS_AT_RISK,'N'),'Y', 'Y',NULL
) )
AND ARP_VIEW_CONSTANTS.GET_SALES_ORDER IS NULL
AND PS.STATUS= NVL(ARP_VIEW_CONSTANTS.GET_STATUS,PS.STATUS)

Click here to run the utility script that lists the Multi-Org tables that include ORG_ID as part of the
composite key. You can access the utility from the following URL:
http://www-apps.us.oracle.com/ssa/utils/composite-index.html

Example 5:
The view definition of single organization view RA_ADDRESSES is shown below in the example.
This is a special case. The view is based on RA_ADDRESSES_ALL synonym and includes
CLIENT_INFO filter. The synonym RA_ADDRESSES_ALL in turn is based on
RA_ADDRESSES_MORG view. RA_ADDRESSES_MORG view is based on several HZ tables
(HZ_CUST_ACCT_SITES_ALL, HZ_LOC_ASSIGNMENTS, HZ_LOCATIONS and
HZ_PARTY_SITES). This is done for backward compatibility for customer migration to TCA.

CREATE OR REPLACE VIEW RA_ADDRESSES AS 
SELECT ROW_ID ,
KEY_ACCOUNT_FLAG ,

ORG_ID ,
FROM RA_ADDRESSES_ALL WHERE
NVL(ORG_ID,NVL(TO_NUMBER(DECODE(SUBSTRB(USERENV('CLIENT_INFO'),1 ,1),
' ',NULL,SUBSTRB(USERENV('CLIENT_INFO'), 1,10))),
-99)) = NVL(TO_NUMBER(DECODE( SUBSTRB(USERENV('CLIENT_INFO'),1,1), '

Page 37 of 70

Company Confidential - For Internal Use Only


',NULL,SUBSTRB(USERENV('CLIENT_INFO'),1,10))), -99)

This single organization view RA_ADDRESSES must be rewritten as a reference view following the
guidelines of reference views given in the next section. Basically RA_ADDRESSES must remain as a
view based on HZ_CUST_ACCT_SITES (secured synonym), HZ_LOC_ASSIGNMENTS,
HZ_LOCATIONS and HZ_PARTY_SITES and the CLIENT_INFO predicate must be removed from the
view.

Similarly, the single organization view RA_CUSTOMER_RELATIONSHIPS is based on


RA_CUSTOMER_RELATIONSHIPS_ALL synonym and includes CLIENT_INFO filter. This synonym
in turn is based on RA_CUSTOMER_RELATIONSHIPS_MORG view.
RA_CUSTOMER_RELATIONSHIPS_MORG view in turn is based on
HZ_CUST_ACCT_RELATE_ALL table. This is done for backward compatibility for customer
migration to TCA. The columns in HZ_CUST_ACCT_RELATE_ALL table do not match with the
columns in RA_CUSTOMER_RELATIONSHIPS_ALL table. Hence, the single organization view
RA_CUSTOMER_RELATIONSHIPS must be rewritten as a reference view based on the
HZ_CUST_ACCT_RELATE secured synonym. Another example of this case is the RA_SITE_USES
view that must be rewritten as a reference view.

There are two additional cases, single organization views that have either MLS logic or MRC logic
embedded in the where clause in addition to single org predicate.

Case 2: Single Organization view with Multi-Lingual Support

Example:

Original Single Organization View definition with MLS logic:

CREATE OR REPLACE VIEW AR_VAT_TAX_VL AS 
SELECT B.ROWID ROW_ID, 
B.ADJ_NON_REC_TAX_CCID, 
B.EDISC_NON_REC_TAX_CCID, 
B.UNEDISC_NON_REC_TAX_CCID, 
... 
B.ENABLED_FLAG, 
B.TAX_CLASS, 
B.DISPLAYEDED_FLAG, 
B.TAX_CONSTRAINT_ID 
FROM AR_VAT_TAX_ALL_TL T, 
AR_VAT_TAX_ALL_B B 
WHERE B.VAT_TAX_ID = T.VAT_TAX_ID 
AND NVL(B.ORG_ID, ­99) = NVL(T.ORG_ID, ­99) 
AND NVL(B.ORG_ID,NVL(TO_NUMBER(DECODE(SUBSTRB(USERENV('CLIENT_INFO'), 
1 ,1),' ', NULL, SUBSTRB(USERENV('CLIENT_INFO'),1,10))),­99)) = 
NVL(TO_NUMBER(DECODE(SUBSTRB(USERENV('CLIENT_INFO'),1,1),' ', NULL, 
SUBSTRB(USERENV('CLIENT_INFO'),1,10))),­99) 
AND T.LANGUAGE = userenv('LANG')

The above view definition in addition to CLIENT_INFO predicate includes filter condition for MLS
logic, which needs to stay. Hence, the single organization view should be converted to a reference view
as given below:

Modified View definition with MLS logic

CREATE OR REPLACE VIEW AR_VAT_TAX_VL AS 
SELECT B.ROWID ROW_ID, 
B.ADJ_NON_REC_TAX_CCID, 
B.EDISC_NON_REC_TAX_CCID, 

Page 38 of 70

Company Confidential - For Internal Use Only


B.UNEDISC_NON_REC_TAX_CCID, 
B.ORG_ID 
... 
B.ENABLED_FLAG, 
B.TAX_CLASS, 
B.DISPLAYEDED_FLAG, 
B.TAX_CONSTRAINT_ID 
FROM AR_VAT_TAX_ALL_TL T, 
     AR_VAT_TAX_B B 
WHERE B.VAT_TAX_ID = T.VAT_TAX_ID 
AND NVL(B.ORG_ID, ­99) = NVL(T.ORG_ID, ­99) (not needed since 
vat_tax_id is unique across orgs)
AND T.LANGUAGE = userenv('LANG')

The summary of changes that must be done for single organization views with MLS logic are given
below:

 Add ORG_ID column to view definition if it does not exist


 Remove Client Info predicate from the Where Clause of the view
 Replace the driving Multi-Org base table reference with secured synonym
 Add ORG_ID filters if the underlying Multi-Org tables used in the join condition include
ORG_ID as part of the composite key or ORG_ID is the driving key to avoid Cartesian joins

Note: In the above example, ORG_ID filter in the Where Clause is removed, since it is not part of the
composite index for the tables joined.

Case 3: Single Organization view with Multi-Reporting Currency

Original Single Org View Definition with MRC logic:

CREATE OR REPLACE VIEW AR_BATCHES_MRC_V AS 
SELECT MC.BATCH_ID , 
MC.SET_OF_BOOKS_ID, 
LAST_UPDATED_BY , 
... 
TRANSMISSION_ID , 
BANK_DEPOSIT_NUMBER , 
ORG_ID , 
PURGED_CHILDREN_FLAG 
FROM AR_BATCHES_ALL B, 
     AR_MC_BATCHES MC 
WHERE MC.BATCH_ID=B.BATCH_ID 
AND MC.SET_OF_BOOKS_ID = NVL(TO_NUMBER(SUBSTRB(USERENV('CLIENT_INFO'),

45,10)),­99) 
AND NVL(ORG_ID,NVL(TO_NUMBER(DECODE(SUBSTRB(USERENV('CLIENT_INFO'), 
1 ,1),' ', NULL, SUBSTRB(USERENV('CLIENT_INFO'),1,10))),­99)) = 
NVL(TO_NUMBER(DECODE(SUBSTRB(USERENV('CLIENT_INFO'),1,1),' ', NULL, 
SUBSTRB(USERENV('CLIENT_INFO'),1,10))),­99)

The above view definition in addition to CLIENT_INFO predicate includes filter condition for MRC
logic, which needs to stay. Hence, the single organization view should be converted to a reference view
as given below:

Modified View Definition with MRC logic:

Page 39 of 70

Company Confidential - For Internal Use Only


CREATE OR REPLACE VIEW AR_BATCHES_MRC_V AS 
SELECT MC.BATCH_ID , 
MC.SET_OF_BOOKS_ID, 
LAST_UPDATED_BY , 
... 
TRANSMISSION_ID , 
BANK_DEPOSIT_NUMBER , 
ORG_ID , 
PURGED_CHILDREN_FLAG 
FROM AR_BATCHES B, 
     AR_MC_BATCHES MC 
WHERE MC.BATCH_ID=B.BATCH_ID 
AND MC.SET_OF_BOOKS_ID = NVL(TO_NUMBER(SUBSTRB(USERENV('CLIENT_INFO'),

45,10)),­99)

The summary of changes that must be done for single organization views with MRC are given below:

 Add ORG_ID column if it does not exist


 Remove Client Info predicate from the Where Clause of the view
 Replace the driving Multi-Org base table reference with secured synonym
 Add ORG_ID filters if the underlying Multi-Org tables used in the join condition include
ORG_ID as part of the composite key or ORG_ID is the driving key to avoid Cartesian joins

Reference Views
The reference views join one or more single organization views. These views must be modified to
include just one secured synonym in the join condition. The _ALL tables must be used for the reference
to the rest of the single organization views. The criteria to pick the secured synonym are a) is a driving
table and b) has small volume of data (typically a setup table as opposed to a transaction table).
ORG_ID filter must be added to the WHERE Clause condition to avoid Cartesian products for tables
that include ORG_ID as part of the composite index (as in tables that contain seed data replicated to
every org) or ORG_ID is the driving key for the table (as in product system options tables).

Important: Every reference view should have only one secured synonym. Limiting the number of
secured synonyms to only one improves performance.
Example 1:

Original Referenced View Definition


CREATE OR REPLACE VIEW RA_CUSTOMER_TRX_PARTIAL_V AS 
SELECT CT.ROWID  "ROW_ID",
       CT.CUSTOMER_TRX_ID "CUSTOMER_TRX_ID",
       CT.TRX_NUMBER      "TRX_NUMBER",
       CT.OLD_TRX_NUMBER  "OLD_TRX_NUMBER",
       CT_REL.TRX_NUMBER  "CT_RELATED_TRX_NUMBER”
       ...
ARPT_SQL_FUNC_UTIL.get_salesrep_name_number(CT.PRIMARY_SALESREP_ID,'NA
ME'
) "RAS_PRIMARY_SALESREP_NAME",
       
ARPT_SQL_FUNC_UTIL.get_salesrep_name_number(CT.PRIMARY_SALESREP_ID,'NU
MBE
R') "RAS_PRIMARY_SALESREP_NUM”
         ...
FROM   RA_CUST_TRX_LINE_GL_DIST GD,
       RA_CUSTOMER_TRX CT,
       …

Page 40 of 70

Company Confidential - For Internal Use Only


       RA_SITE_USES SU_BILL,
       RA_CUSTOMERS RAC_PAYING,
       RA_CUSTOMERS RAC_BILL,
       RA_SITE_USES SU_BILL,
       RA_SITE_USES SU_SHIP,
       RA_SITE_USES SU_PAYING,
       …
       RA_ADDRESSES RAA_BILL,
       RA_ADDRESSES RAA_SHIP,
       RA_ADDRESSES RAA_REMIT,
       RA_CONTACTS RACO_SHIP,
       RA_CONTACTS RACO_BILL,
       AP_BANK_ACCOUNTS APBA,
       AP_BANK_BRANCHES APB,
       AR_RECEIPT_METHODS ARM,
       AR_RECEIPT_CLASSES ARC,
       RA_BATCH_SOURCES BS,
       RA_BATCHES RAB,
       RA_CUST_TRX_TYPES CTT,
       …
 WHERE CT.CUSTOMER_TRX_ID = GD.CUSTOMER_TRX_ID
   AND 'REC' = GD.ACCOUNT_CLASS
   AND 'Y' = GD.LATEST_REC_FLAG
   AND CT.RELATED_CUSTOMER_TRX_ID = CT_REL.CUSTOMER_TRX_ID(+)
   AND CT.BILL_TO_CUSTOMER_ID = RAC_BILL.CUSTOMER_ID
   AND CT.SHIP_TO_CUSTOMER_ID = RAC_SHIP.CUSTOMER_ID(+)
   AND CT.SOLD_TO_CUSTOMER_ID = RAC_SOLD.CUSTOMER_ID
   AND CT.PAYING_CUSTOMER_ID = RAC_PAYING.CUSTOMER_ID(+)
   AND CT.BILL_TO_SITE_USE_ID = SU_BILL.SITE_USE_ID
   …
   AND CT.BATCH_SOURCE_ID = BS.BATCH_SOURCE_ID
   AND CT.BATCH_ID = RAB.BATCH_ID(+)
   AND CT.CUST_TRX_TYPE_ID = CTT.CUST_TRX_TYPE_ID
   AND CTT.TYPE <> 'BR' 
   …

The view definition is modified for Multi-Org Access Control replacing reference to single organization
views with _ALL tables for all except one object RA_CUSTOMER_TRX which is the driving table so
kept as secured synonym as given below:

CREATE OR REPLACE VIEW RA_CUSTOMER_TRX_PARTIAL_V AS
SELECT CT.ROWID  "ROW_ID",
       CT.CUSTOMER_TRX_ID "CUSTOMER_TRX_ID",
       CT.TRX_NUMBER      "TRX_NUMBER",
       CT.OLD_TRX_NUMBER  "OLD_TRX_NUMBER",
       CT_REL.TRX_NUMBER  "CT_RELATED_TRX_NUMBER",
ARPT_SQL_FUNC_UTIL.get_salesrep_name_number(CT.PRIMARY_SALESREP_ID,'NA
ME'’, CT.ORG_ID
) "RAS_PRIMARY_SALESREP_NAME",
       
ARPT_SQL_FUNC_UTIL.get_salesrep_name_number(CT.PRIMARY_SALESREP_ID,'NU
MBE
R', CT.ORG_ID) "RAS_PRIMARY_SALESREP_NUM", 
CT.ORG_ID,

FROM   RA_CUST_TRX_LINE_GL_DIST_ALL GD,
       RA_CUSTOMER_TRX CT,
       …
       RA_SITE_USES_ALL SU_BILL,
       RA_CUSTOMERS RAC_PAYING,
       RA_CUSTOMERS RAC_BILL
       RA_SITE_USES_ALL SU_BILL,

Page 41 of 70

Company Confidential - For Internal Use Only


       RA_SITE_USES_ALL SU_SHIP,
       RA_SITE_USES_ALL SU_PAYING,
       …
       RA_ADDRESSES_ALL RAA_BILL,
       RA_ADDRESSES_ALL RAA_SHIP,
       RA_ADDRESSES_ALL RAA_REMIT,
       RA_CONTACTS RACO_SHIP,
       RA_CONTACTS RACO_BILL,
       AP_BANK_ACCOUNTS_ALL APBA,
       AP_BANK_BRANCHES APB,
       AR_RECEIPT_METHODS ARM,
       AR_RECEIPT_CLASSES ARC,
       RA_BATCH_SOURCES_ALL BS,
       RA_BATCHES_ALL RAB,
       RA_CUST_TRX_TYPES_ALL CTT,
       …
 WHERE CT.CUSTOMER_TRX_ID = GD.CUSTOMER_TRX_ID
   AND 'REC' = GD.ACCOUNT_CLASS
   AND 'Y' = GD.LATEST_REC_FLAG
   AND CT.RELATED_CUSTOMER_TRX_ID = CT_REL.CUSTOMER_TRX_ID(+)
   AND CT.BILL_TO_CUSTOMER_ID = RAC_BILL.CUSTOMER_ID
   AND CT.SHIP_TO_CUSTOMER_ID = RAC_SHIP.CUSTOMER_ID(+)
   AND CT.SOLD_TO_CUSTOMER_ID = RAC_SOLD.CUSTOMER_ID
   AND CT.PAYING_CUSTOMER_ID = RAC_PAYING.CUSTOMER_ID(+)
   AND CT.BILL_TO_SITE_USE_ID = SU_BILL.SITE_USE_ID
   …
   AND CT.BATCH_SOURCE_ID = BS.BATCH_SOURCE_ID
   AND CT.ORG_ID          = BS.ORG_ID
   AND CT.BATCH_ID = RAB.BATCH_ID(+)
   AND CT.CUST_TRX_TYPE_ID = CTT.CUST_TRX_TYPE_ID
   AND CT.ORG_ID           = CTT.ORG_ID
   …

The summary of changes that must be done for reference views are given below:

 Add ORG_ID column if it does not exist


 Replace single organization views with _ALL tables for all excepting one, which must be a
secured synonym
 Include ORG_ID filter in the where clause of the view to avoid Cartesian product, if
ORG_ID is the driving key or part of the composite key
 Include ORG_ID parameter in the columns based on functions if necessary

Example 2:

Original Reference View Definition


CREATE OR REPLACE VIEW AR_TAX_LINES_V AS
SELECT CTL_TAX.ROWID , 
       CTL_TAX.CUSTOMER_TRX_ID , 
       CTL_TAX.CUSTOMER_TRX_LINE_ID ,
       CTL_TAX.PREVIOUS_CUSTOMER_TRX_ID , 
       CTL_TAX.PREVIOUS_CUSTOMER_TRX_LINE_ID , 
       CTL_TAX.LINK_TO_CUST_TRX_LINE_ID , 
       ...
  FROM RA_CUSTOMER_TRX_LINES CTL_INV_LINE, 
       RA_CUSTOMER_TRX_LINES CTL_INV_TAX, 
       AR_VAT_TAX INV_VAT, 
       RA_CUSTOMER_TRX_LINES CTL_LINE, 
       RA_CUSTOMER_TRX_LINES CTL_TAX, 
       AR_VAT_TAX VAT 
 WHERE CTL_TAX.LINK_TO_CUST_TRX_LINE_ID = 
CTL_LINE.CUSTOMER_TRX_LINE_ID 
   AND CTL_TAX.LINE_TYPE = 'TAX'  

Page 42 of 70

Company Confidential - For Internal Use Only


   AND CTL_TAX.VAT_TAX_ID = VAT.VAT_TAX_ID(+) 
   AND CTL_TAX.PREVIOUS_CUSTOMER_TRX_LINE_ID = 
       CTL_INV_TAX.CUSTOMER_TRX_LINE_ID (+) 
   AND CTL_INV_TAX.LINK_TO_CUST_TRX_LINE_ID =           
       CTL_INV_LINE.CUSTOMER_TRX_LINE_ID (+) 
   AND CTL_INV_TAX.VAT_TAX_ID = INV_VAT.VAT_TAX_ID (+)  

The view definition is modified for Multi-Org Access Control as given below (ORG_ID column is added
to view definition, all single org views reference replaced by _ALL tables keeping
RA_CUSTOMER_TRX_LINES (CTL_TAX) which is the driving table, as secured synonym):

CREATE OR REPLACE VIEW AR_TAX_LINES_V AS
SELECT CTL_TAX.ROWID , 
       CTL_TAX.CUSTOMER_TRX_ID , 
       CTL_TAX.CUSTOMER_TRX_LINE_ID ,
       CTL_TAX.PREVIOUS_CUSTOMER_TRX_ID , 
       CTL_TAX.PREVIOUS_CUSTOMER_TRX_LINE_ID , 
       CTL_TAX.LINK_TO_CUST_TRX_LINE_ID , 
       ...
       CTL_TAX.ORG_ID
  FROM RA_CUSTOMER_TRX_LINES_ALL CTL_INV_LINE, 
       RA_CUSTOMER_TRX_LINES_ALL CTL_INV_TAX, 
       AR_VAT_TAX_ALL INV_VAT, 
       RA_CUSTOMER_TRX_LINES_ALL CTL_LINE, 
       RA_CUSTOMER_TRX_LINES CTL_TAX, 
       AR_VAT_TAX_ALL VAT 
 WHERE CTL_TAX.LINK_TO_CUST_TRX_LINE_ID = 
CTL_LINE.CUSTOMER_TRX_LINE_ID 
   AND CTL_TAX.LINE_TYPE = 'TAX'  
   AND CTL_TAX.VAT_TAX_ID = VAT.VAT_TAX_ID(+) 
   AND CTL_TAX.PREVIOUS_CUSTOMER_TRX_LINE_ID = 
       CTL_INV_TAX.CUSTOMER_TRX_LINE_ID (+) 
   AND CTL_INV_TAX.LINK_TO_CUST_TRX_LINE_ID =           
       CTL_INV_LINE.CUSTOMER_TRX_LINE_ID (+) 
   AND CTL_INV_TAX.VAT_TAX_ID = INV_VAT.VAT_TAX_ID (+)  

4.2.3 Attach Security Policy to your database objects


Attach the security policy to the Multi-Org synonyms to enforce security. The package
FND_ACCESS_CONTROL_UTIL contains all the utilities needed to administer policies (add policy,
drop policy or check if a policy exists on an object). You should use this package to administer security
policies.

Below is a sample to illustrate how to attach security policy to the synonym


FINANCIALS_SYSTEM_PARAMETERS. Each product team is expected to provide a script to add
policy to all Multi-Org views using FND_ACCESS_CONTROL_UTIL.Add_Policy.

BEGIN
FND_ACCESS_CONTROL_UTIL.Add_Policy
( p_object_schema => '&&1', ­­ Apps user name
p_object_name => 'FINANCIALS_SYSTEM_PARAMETERS',
p_policy_name => 'ORG_SEC’,
p_function_schema => '&&1', ­­ Apps user name

p_policy_function => 'MO_GLOBAL.ORG_SECURITY',


p_statement_types => 'SELECT, INSERT, UPDATE, DELETE',
p_update_check => TRUE,
p_enable => TRUE,
p_static_policy => FALSE);

Page 43 of 70

Company Confidential - For Internal Use Only


END;

The ADD_POLICY API checks if the policy is attached to the object. If it is attached, then drops the
policy and then reattaches. The first two parameters to this procedure are the schema where the object
to which policy is attached resides and the name of the object. The next three parameters are the policy
name, the schema where the policy function is available and the policy function name. The next three
parameters are the statement type (DML) to which policy applies, a flag to check the policy against any
inserted or updated value and a flag to indicate whether the policy is enabled or not. The last parameter
is to indicate static or dynamic policy available in Oracle 9iR2.

4.2.4 Remove Dependency on Multi-Org in AOL tables

The existing registration should be cleaned up. For example, the dependency of Payables on Multi-Org
is seeded in the following table:

FND_PRODUCT_INIT_DEPENDENCY

Column Name Size Type Rqd Value


APPLICATION_SHORT_NAME 80 VARCHAR2 Yes XXXCHR
PRODUCT_DEPENDENCY 80 VARCHAR2 Yes MO

Use the API provided by ATG to remove the dependency information:

To remove dependency:
FND_PRODUCT_INITIALIZATION_PKG.RemoveDependency('XXXCHR','MO');

4.2.5 Register Multi-Org Access Enabled in MO table

Product teams must register their product in the Multi-Org table FND_MO_PRODUCT_INIT to indicate
that Multi-Org Access Control is enabled when they are ready to turn on. This information is needed for
module based initialization, to ignore “MO: Security Profile” or not.

Table FND_MO_PRODUCT_INIT

When Payables (XXXCHR) opens up access, they must seed a row in the Multi-Org table to indicate that
access is turned on. CRM foundation (JTF) has Multi-Org Access Control turned on already.

Application_Short_Name

JTF

XXXCHR

Use the API provided by Shared Services to register the access enabled status.

Page 44 of 70

Company Confidential - For Internal Use Only


To enable access:
FND_MO_PRODUCT_INIT_PKG.register_application('XXXCHR',
‘SEED’,’Y’);
To delete your application entry:
FND_MO_PRODUCT_INIT_PKG.remove_application('XXXCHR');

Use the FNDLOAD utility to extract the seed data in FND_MO_PRODUCT_INIT table. A loader file
must be delivered to the customer to populate this information at the site. Please contact Shared
Services team for the loader file. A loader configuration file afmoinit.lct is available for extracting the
loader file.

4.3 Forms Enhancements


The Multi-Org setup and transaction forms need to expose Operating Unit field. This will allow the
users to select the Operating Unit and then enter the setup or transaction for the Operating Unit.
Wherever, possible, we recommend simple Operating Unit derivations from some attributes of the
transaction.

The following section details the changes that must be done by the product teams in the setup and
transaction forms for Multi-Org Access Control:

4.3.1 Multi-Org Initialization

Every form modified for Multi-Org Access Control should include, the call to Multi-Org initialization
API (MO_GLOBAL.init) in the Pre-Form trigger. The Application Short Name passed to the API is
used to determine the access enabled status of the product in order to populate the temporary table
accordingly. Also the application contexts used in the VPD security policy are initialized. The
Application short name should correspond to the data registered in FND_APPLICATION table.

For example, a Payables form modified to open up access, should include the following code as given
below in the PRE-FORM trigger:

BEGIN
APP_STANDARD.EVENT(‘PRE-FORM’);
MO_GLOBAL.init (‘XXXCHR’);
END;

In the above example, XXXCHR is the application short name for Payables.

If AP has opened up access in 11ix, the above code would populate the temporary table with multiple
Operating Units if the profile option “MO: Security Profile” is set for multiple access. Also the access
mode will be set to “MULTIPLE” or “ALL” depending upon the number of Operating Units the user has
access to.

IMPORTANT: AOL initialization (fnd_global.apps_initialize()) is executed by the app_standard.event()


call in the Pre-Form trigger. Multi-Org initialization should be executed after this call. If this order is
not followed, the profiles “MO: Operating Unit” and “MO: Security Profile” will not be cached for the
right context resulting in incorrect initialization for the session.

4.3.2 Add Operating Unit Field


General recommendation is to place the Operating Unit field as the first displayed field in the canvas in
the Multi-Org forms. It is a non base table item derived based on the ORG_ID value from HR tables
where Operating Unit is defined.
Add Operating Unit and ORG_ID fields in the form block. Operating Unit field is not needed for blocks
that do not expose this field to the users.

Page 45 of 70

Company Confidential - For Internal Use Only


4.3.3 Create LOV for Operating Unit field
Create a query based record group to show Operating Units that are included in the security profile
attached to the responsibility. The Multi-Org global temporary table is populated with the Operating
Unit information based on the “MO: Security Profile” access. For simplicity and to minimize impact of
future change, APIs are provided to get the Operating Unit name from the Temporary table. Product
teams should use these APIs instead of directly accessing the temporary table. A function is also
available to check the access of particular Operating Unit in the Temporary table.

The record group query for Operating Unit field should be coded as given below:
select     hr.organization_id   org_id
         , hr.name              operating_unit
      FROM hr_operating_units hr
     WHERE mo_global.check_access(hr.organization_id) = 
‘Y’

Record Group Column Specifications

Column Name OPERATING_UNIT ORG_ID


DataType Char Number
Length 240 0

Create a LOV based on this record group. LOV window size 3 x 3 inches. The Operating Unit name
must be displayed in the LOV window.

LOV column mapping Properties

Column Name OPERATING_UNIT ORG_ID


Display Width 1.5 0
Return Item <block name>.operating_unit <block name>.org_id
Column Title Operating Unit Org ID

Attach the LOV to Operating Unit field.

4.3.4 Default Operating Unit on forms startup


On forms startup you must call the Multi-Org API MO_UTILS.get_default_ou to copy the global
variables value to form parameters. You must create new form parameters as given below to store the
API output and then copy the default Operating Unit to the form block in the when-create-record
trigger.

New Form Parameters


Parameter Name Datatype Maximum Length
MO_DEFAULT_ORG_ID Number 15
MO_DEFAULT_OU_NAME Char 240
MO_OU_COUNT Number 15

Pre-Form trigger
DECLARE
  l_default_org_id   number;
  l_default_ou_name  varchar2(240);
  l_ou_count         number;

BEGIN
  ...
     mo_utils.get_default_ou(l_default_org_id, l_default_ou_name, 
                             l_ou_count);
     :PARAMETER.mo_default_org_id) := l_default_org_id;
     :PARAMETER.mo_default_ou_name := l_default_ou_name;
     :PARAMETER.mo_ou_count := l_ou_count;

Page 46 of 70

Company Confidential - For Internal Use Only


 
     ­­ Can also use indirect reference as given below:
­­ copy(l_default_org_id,’PARAMETER.mo_default_org_id’);
     ­­ copy(l_default_ou_name,’PARAMETER.mo_default_ou_name’);
     ­­ copy(l_ou_count,’PARAMETER.mo_ou_count’);

  ...
END;

Block Level When-Create-Record trigger


  IF :parameter.mo_default_org_id is not null and :block.org_id is 
null THEN
    :block.org_id := :parameter.mo_default_org_id);
    :block.operating_unit := :parameter.mo_default_ou_name;
    ­­ Can use copy built in as given below:
    ­­ copy(‘parameter.mo_default_org_id’,’block.org_id’);
    ­­ copy(‘parameter.mo_default_ou_name’,’block.operating_unit’);

  END IF;

4.3.6 Setting the Dynamic Policy Context

IMPORTANT: Setting the current org in the different triggers given below SHOULD NOT be used for
new forms that you are building. For new code, you should use _ALL tables and include form block
ORG_ID to restrict data to the Operating Unit that the user selected.

The Multi-Org security policy function uses a dynamic predicate to handle simple predicate when the
access is limited to one Operating Unit vs. complex predicate (exists sub-query) when the access is
multiple. The predicate is based on the application context attribute value for access_mode.

To salvage the existing code, depending upon whether the forms uses Select Operating Unit or Derive
Operating Unit feature, the access_mode can be set to single or multiple in the different triggers given
below:

7.3.6.1 Forms that support Select Operating Unit feature


Call the Multi-Org API to set the context to multiple or single in the following triggers:

When-Create-Record Trigger of Operating Unit field block


IF (:parameter.mo_default_org_id IS NOT NULL ) THEN
  ­­ Defaulting org_id from profile option
  :block.org_id := :parameter.mo_default_org_id;
  :block.operating_unit := :parameter.mo_default_ou_name;
  ­­ Set policy context
  mo_global.set_policy_context('S’,:block.org_id);
ELSE
  mo_global.set_policy_context('M', null);

END IF;

IF :<your block name.org_id> is not null     
  IF :<block name.org_id> <> nvl(:<parameter.old_org_id>,­99) THEN
    ­­ Get the cache for current org
  END IF;
ELSE
  ­­ Refresh the cache

END IF;

Note: The defaulting API will return data even if the “MO: Default Operating Unit” Profile is not set
when the responsibility has access to one operating. So the ELSE condition for setting the policy
context need not check the parameter.ou_count value.
Page 47 of 70

Company Confidential - For Internal Use Only


When-Validate-Item Trigger of Operating Unit field
IF (:<your block name.org_id> IS NOT NULL ) THEN
  IF :<block name.org_id> <> nvl(:<parameter.old_org_id>,­99) THEN
     mo_global.set_policy_context('S', :block.org_id);
     ­­ Get the cache for the current org
  END IF;
ELSE ­­ :block.org_id is null
    mo_global.set_policy_context('M', null);
    ­­ Refresh the cache
END IF;
Note: If you have Find windows in your form that expose Operating Unit field, you must set the policy
context in the When-Validate-Item trigger of the Operating Unit field. For forms that use Row LOVs for
Query Find, should set the policy context to Multiple to see all Operating Units data, provided the
parameter mo_ou_count is more than 1.

When-New-Record-Instance Trigger of Operating Unit field block


IF (:<your block name.org_id> IS NOT NULL ) THEN
  IF :<block name.org_id> <> nvl(:<parameter.old_org_id>,­99) THEN
    mo_global.set_policy_context('S', :block.org_id);
    ­­ Get the cache for the current org
  END IF;
ELSE ­­ :block.org_id is null, so set the context to multiple
  mo_global.set_policy_context('M', null);
  ­­ Refresh the cache
END IF;

Pre-Insert Trigger of Operating Unit field block

This trigger is needed only if your form allows multi record commit.
IF (:<your block name.org_id> IS NOT NULL ) THEN
  IF :<block name.org_id> <> nvl(:<parameter.old_org_id>,­99) THEN
      mo_global.set_policy_context('S', :block.org_id);
      ­­ Get the cache for the current org
  END IF;
ELSE ­­ :block.org_id is null, so set the context to multiple
  mo_global.set_policy_context('M', null);
  ­­ Refresh the cache
END IF;

Pre-Query Trigger of Operating Unit field block


BEGIN
  IF :parameter.mo_ou_count = 1 THEN
    mo_global.set_policy_context(‘S’,:parameter.mo_default_org_id);
  ELSE
    mo_global.set_policy_context('M', null);
  END IF;   
   ­­ Other Code
END;

Page 48 of 70

Company Confidential - For Internal Use Only


Pre-Record Trigger of Operating Unit field block
This trigger is need if your form forces user to commit after every record.
IF (:parameter.current_record is not null and
:parameter.current_record != :system.trigger_record) THEN
IF (:system.form_status in ('CHANGED','INSERT')) THEN
mo_global.set_policy_context('S', :parameter.old_org_id);
­­ Get the cache for the current org
-- raise error message to the user to commit;
-- raise form_trigger_failure;
ELSE
-- No pending commits.
-- Reset the current record variable.
:parameter.current_record := '';
END IF;
ELSE
-- User has not navigated to another record.
-- Do not reset the current record variable.
null;
END IF;

Pre-Update Trigger
This trigger is needed if your form allows multi record commits where the records could be in different
Operating Units.

IF (:<your block name.org_id> IS NOT NULL ) THEN
  IF :<block name.org_id> <> nvl(:<parameter.old_org_id>,­99) THEN
    mo_global.set_policy_context('S', :block.org_id);
    ­­ Get the cache for the current org
  END IF;
END IF;

Step 4: Modify the WHEN-CREATE_RECORD trigger of the form block


You must modify the when-create-record trigger of your Operating Unit block to copy the current
Operating Unit specific information from the cache to the parameter or non base table block. When an
Operating Unit default is available, caching should happen based on the default org.

For product teams that need server side caching to be initialized for validations on the server, you could
get the current_org_id by calling the Multi-Org API mo_global.get_current_org_id provided you set the
dynamic policy context correctly.

When_Create-Record trigger
DECLARE
      l_gr  xx_mo_cache_utils.GlobalsRecord;

BEGIN
    
    ­­ Check if the default OU is available.
    ­­ If so, copy default OU to form block 
    IF :parameter.mo_default_org_id is not null and 
       :block.org_id is null then
        :block.org_id = :parameter.mo_default_org_id;
        :block.operating_unit := :parameter.mo_default_ou_name;
    END IF;
    
    ­­ Check if the block org is set. Then check if the operating 
    ­­ unit available as default is the same as the one available in 
    ­­ parameter or a non base table block. If same, then do not copy

    ­­ again from cache. This ensures that you do not refresh the 
    ­­ parameter or a non base table block if you continue to enter 
    ­­ transactions for the org which is same as the default org.

Page 49 of 70

Company Confidential - For Internal Use Only


    IF :<your block name.org_id> is not null     
      IF :<block name.org_id> <> nvl(:<parameter.old_org_id>,­99) 
THEN
        ­­ Get the current Org attributes from client side cache
        l_gr := xx_mo_local_cache.get_org_attributes(:<<your block 
name>>.org_id);
      
        ­­ Copy from cache to parameter block or non base table block
        ­­ You can replace parameter block shown here with a non 
        ­­ base table block      
        :parameter.chart_of_accounts_id := l_gr.chart_of_accounts_id;
        :parameter.ledger_id      := l_gr.ledger_id;
        :parameter.ledger_name    := l_gr.ledger_name;
        :parameter.currency_code        := l_gr.currency_code;
        /* << Begin product­specific assignments >> */
        ­­ Additional assignments...
        :parameter.<column1>                := l_gr.column1;
        /* << End product­specific assignments >> */
 
        ­­ Copy the block org_id to parameter.old_org_id
        :parameter.old_org_id           := <:block name.org_id>;
      END IF;
    ELSE
      ­­ Copy null to parameter columns
    END IF;

    ­­ Pass the ORG_ID to server code to use the server cache for the

    ­­ current org for the record validations 

    ­­ Get Batch Source Header Defaults 
    arp_trx_defaults.get_header_defaults(param1, param2, 
…,:block.org_id);

-- Other Code --
...
END;

Step 5: Modify the WHEN-VALIDATE-ITEM trigger of the Operating Unit field (as well as Operating
Unit specific fields used in derive operating feature)

After the user selects an Operating Unit, the current Operating Unit record must be copied from the
cache to the parameter or non base table block.

For product teams that need server side caching to be initialized for validations on the server, you could
get the current_org_id by calling the Multi-Org API mo_global.get_current_org_id provided you set the
dynamic policy context correctly.

Note: For forms that support Derive Operating Unit feature, the code to copy the cache to the parameter
or non base table block should be included not only in the When-Validate-Item trigger of the Operating
Unit field, but also in the When-Validate-Item triggers of the Operating Unit specific fields that could be
used to derive the Operating Unit. Please see “Derive Operating Unit feature” section for more details.

When-Validate-Item Trigger
DECLARE
      l_gr  xx_mo_cache_utils.GlobalsRecord;

BEGIN
  ­­ Check if the new Operating Unit selected by the user is the same

  ­­ as the old Operating Unit that is available in the parameter or 
  ­­ a non base table block. If same then do not copy again from    
  ­­ cache 
Page 50 of 70

Company Confidential - For Internal Use Only


  IF :<block name.org_id> is not null THEN
    IF :<block name.org_id> <> nvl(:<parameter.old_org_id>,­99) THEN

      ­­ Get the current Org attributes from client side cache
      l_gr := xx_mo_local_cache.get_org_attributes(:<<your block 
name>>.org_id);

      ­­ Copy from cache to parameter block or a non base table block
      ­­ You can replace parameter block shown here 
      ­­ with any non base table block 
      :parameter.chart_of_accounts_id   := l_gr.chart_of_accounts_id;
      :parameter.ledger_id          := l_gr.ledger_id;
      :parameter.ledger_name    := l_gr.ledger_name;
      :parameter.currency_code           := l_gr.currency_code;
      /* << Begin product­specific assignments >> */
      ­­ Additional assignments...
      :parameter.<column1>                := l_gr.column1;
      /* << End product­specific assignments >> */

       ­­ Copy the block org_id to parameter.old_org_id
       :parameter.old_org_id           := <:block name.org_id>;

    END IF;
  ELSE
    ­­ Copy null to parameter columns
  END IF;

  ­­ Pass the ORG_ID to server code to use the server cache for the  

  ­­ current org for the record validations 
  ­­ Get Batch Source Header Defaults 
  arp_trx_defaults.get_header_defaults(param1, param2, 
…,:block.org_id);
       
  ­­ Other code ­­
  
END;

Step 6: Modify the block level When-New-Record-Instance trigger of the Operating Unit field block
When the user tries to modify any attribute of a transaction after it is saved, the current operating record
must be copied from the cache to the parameter or non base table block, to use it for validations as well
as for controlling the display properties of the items in the record. The parameter or non base table
block will be populated with the current org cache when the user navigates for one record to another
after the records are queried up.

For product teams that need server side caching to be initialized for validations on the server, you could
get the current_org_id by calling the Multi-Org API mo_global.get_current_org_id provided you set the
dynamic policy context correctly.

The when-new-record-instance trigger must be used to detect the updates and accordingly refresh the
cache.

When-New-Record-Instance Trigger
DECLARE
      l_gr  xx_mo_cache_utils.GlobalsRecord;

BEGIN

    ­­ Check if the new Operating Unit selected by the user is the 
    ­­ same as the old Operating Unit that is available in the 
    ­­ parameter or non base table block. If same then do not copy 
    ­­ again from cache     
Page 51 of 70

Company Confidential - For Internal Use Only


    IF :<block name.org_id> is not null THEN
      IF :<block name.org_id> <> nvl(:<parameter.old_org_id>, ­99) 
THEN

        ­­ Get the current Org attributes from client side cache
        l_gr := xx_mo_local_cache.get_org_attributes(:<<your block 
name>>.org_id);

        ­­ Copy from cache to parameter block or non base table block
        ­­ You can replace parameter block shown here 
        ­­ with any non base table block 
        :parameter.chart_of_accounts_id := l_gr.chart_of_accounts_id;
        :parameter.ledger_id      := l_gr.ledger_id;
        :parameter.ledger_name    := l_gr.ledger_name;
        :parameter.currency_code        := l_gr.currency_code;
        /* << Begin product­specific assignments >> */
        ­­ Additional assignments...
        :parameter.<column1>                := l_gr.column1;
        /* << End product­specific assignments >> */

       ­­ Copy the block org_id to parameter.old_org_id
       :parameter.old_org_id           := <:block name.org_id>;

      END IF;
    END IF;

    ­­ Pass the ORG_ID to server code to use the server cache for the

    ­­ current org for the record validations  
    ­­ Get Batch Source Header Defaults 
    arp_trx_defaults.get_header_defaults(param1, param2, 
…,:block.org_id);
       
    ­­ Other code ­­
END;

Step 7: Modify the block level Post-Query trigger of the Operating Unit field block
IMPORTANT: Post-Query trigger fires for every record, when you do a blind query and hence
you should consider rewriting your SQL to use _ALL tables and use ORG_ID join condition (based
on the form block ORG_ID). You are not required to synchronize the cache in the post-query
trigger. The WNRI will synchronize the cache.

In forms, where some of the Operating Unit specific display fields are populated in the post query
trigger, you must synchronize the cache based on the record’s Operating Unit.

For product teams that need server side caching to be initialized for validations on the server, you could
get the current_org_id by calling the Multi-Org API mo_global.get_current_org_id provided you set the
dynamic policy context correctly.

Post-Query Trigger
DECLARE
      l_gr  xx_mo_cache_utils.GlobalsRecord;

BEGIN

    ­­ Check if the new Operating Unit selected by the user is the 
    ­­ same as the old Operating Unit that is available in the 
    ­­ parameter or non base table block. If same then do not copy 
    ­­ again from cache     
    IF :<block name.org_id> is not null THEN

Page 52 of 70

Company Confidential - For Internal Use Only


      IF :<block name.org_id> <> nvl(:<parameter.old_org_id>, ­99) 
THEN

        ­­ Get the current Org attributes from client side cache
        l_gr := xx_mo_local_cache.get_org_attributes(:<<your block 
name>>.org_id);

        ­­ Copy from cache to parameter block or non base table block
        ­­ You can replace parameter block shown here 
        ­­ with any non base table block 
        :parameter.chart_of_accounts_id := l_gr.chart_of_accounts_id;
        :parameter.ledger_id      := l_gr.ledger_id;
        :parameter.ledger_name    := l_gr.ledger_name;
        :parameter.currency_code  := l_gr.currency_code;
        /* << Begin product­specific assignments >> */
        ­­ Additional assignments...
        :parameter.<column1>                := l_gr.column1;
        /* << End product­specific assignments >> */

       ­­ Copy the block org_id to parameter.old_org_id
       :parameter.old_org_id           := <:block name.org_id>;

      END IF;
    END IF;

    ­­ Pass the ORG_ID to server code to use the server cache for the

    ­­ current org for the record validations  
    ­­ Get Batch Source Header Defaults 
    arp_trx_defaults.get_header_defaults(param1, param2, 
…,:block.org_id);
       
    ­­ Other code ­­
END;

Step 8: Modify the block level Pre-Insert trigger of the Operating Unit field block

You need this trigger only if your form allows multi-record commit, where you must synchronize the
cache.

Pre-Insert Trigger
DECLARE
      l_gr  xx_mo_cache_utils.GlobalsRecord;

BEGIN

    ­­ Check if the new Operating Unit selected by the user is the 
    ­­ same as the old Operating Unit that is available in the 
    ­­ parameter or non base table block. If same then do not copy 
    ­­ again from cache     
    IF :<block name.org_id> is not null THEN
      IF :<block name.org_id> <> nvl(:<parameter.old_org_id>, ­99) 
THEN

        ­­ Get the current Org attributes from client side cache
        l_gr := xx_mo_local_cache.get_org_attributes(:<<your block 
name>>.org_id);

        ­­ Copy from cache to parameter block or non base table block
        ­­ You can replace parameter block shown here 
        ­­ with any non base table block 
        :parameter.chart_of_accounts_id := l_gr.chart_of_accounts_id;
Page 53 of 70

Company Confidential - For Internal Use Only


        :parameter.ledger_id      := l_gr.ledger_id;
        :parameter.ledger_name    := l_gr.ledger_name;
        :parameter.currency_code  := l_gr.currency_code;
        /* << Begin product­specific assignments >> */
        ­­ Additional assignments...
        :parameter.<column1>                := l_gr.column1;
        /* << End product­specific assignments >> */

       ­­ Copy the block org_id to parameter.old_org_id
       :parameter.old_org_id           := <:block name.org_id>;

      END IF;
    END IF;

    ­­ Pass the ORG_ID to server code to use the server cache for the

    ­­ current org for the record validations  
    ­­ Get Batch Source Header Defaults 
    arp_trx_defaults.get_header_defaults(param1, param2, 
…,:block.org_id);
       
    ­­ Other code ­­
END;

Step 9: Modify the block level Pre-Update trigger of the Operating Unit field block
You need this trigger only if your form allows multi-record commit, where you must synchronize the
cache.

Pre-Update Trigger
DECLARE
      l_gr  xx_mo_cache_utils.GlobalsRecord;

BEGIN

    ­­ Check if the new Operating Unit selected by the user is the 
    ­­ same as the old Operating Unit that is available in the 
    ­­ parameter or non base table block. If same then do not copy 
    ­­ again from cache     
    IF :<block name.org_id> is not null THEN
      IF :<block name.org_id> <> nvl(:<parameter.old_org_id>, ­99) 
THEN

        ­­ Get the current Org attributes from client side cache
        l_gr := xx_mo_local_cache.get_org_attributes(:<<your block 
name>>.org_id);

        ­­ Copy from cache to parameter block or non base table block
        ­­ You can replace parameter block shown here 
        ­­ with any non base table block 
        :parameter.chart_of_accounts_id := l_gr.chart_of_accounts_id;
        :parameter.ledger_id      := l_gr.ledger_id;
        :parameter.ledger_name    := l_gr.ledger_name;
        :parameter.currency_code        := l_gr.currency_code;
        /* << Begin product­specific assignments >> */
        ­­ Additional assignments...
        :parameter.<column1>            := l_gr.column1;
        /* << End product­specific assignments >> */

       ­­ Copy the block org_id to parameter.old_org_id
       :parameter.old_org_id           := <:block name.org_id>;

      END IF;
Page 54 of 70

Company Confidential - For Internal Use Only


    END IF;

    ­­ Pass the ORG_ID to server code to use the server cache for the

    ­­ current org for the record validations  
    ­­ Get Batch Source Header Defaults 
    arp_trx_defaults.get_header_defaults(param1, param2, 
…,:block.org_id);
       
    ­­ Other code ­­
END;

Note: For forms like AR Receipt Workbench, ON_LOCK trigger may be needed as opposed to Pre-
Update (Feedback from AR).

Step 10: Modify the block level Pre-Record trigger of the Operating Unit field block

You need this trigger only if your form forces users to commit the record before navigating to the next
record.

Pre-Record Trigger
DECLARE
      l_gr  xx_mo_cache_utils.GlobalsRecord;

BEGIN

        ­­ Get the current Org attributes from client side cache 
        ­­ org stored in the parameter.old_org_id
        l_gr := 
xx_mo_local_cache.get_org_attributes(:parameter.old_org_id);

        ­­ Copy from cache to parameter block or non base table block
        ­­ You can replace parameter block shown here 
        ­­ with any non base table block 
        :parameter.chart_of_accounts_id := l_gr.chart_of_accounts_id;
        :parameter.ledger_id      := l_gr.ledger_id;
        :parameter.ledger_name    := l_gr.ledger_name;
        :parameter.currency_code        := l_gr.currency_code;
        /* << Begin product­specific assignments >> */
        ­­ Additional assignments...
        :parameter.<column1>            := l_gr.column1;
        /* << End product­specific assignments >> */

      END IF;
    END IF;

    ­­ Pass the ORG_ID to server code to use the server cache for the

    ­­ current org for the record validations  
    ­­ Get Batch Source Header Defaults 
    arp_trx_defaults.get_header_defaults(param1, param2, 
…,:block.org_id);
       
    ­­ Other code ­­
END;

4.3.9 Modify Record Groups for Operating Unit specific fields


Records for Operating Unit specific fields should be modified as given below based on whether they
support select Operating Unit feature or derive Operating Unit feature:

Page 55 of 70

Company Confidential - For Internal Use Only


 The Multi-Org temporary table should not be used directly in the SQL query. Instead the
PL/SQL functions available to check the Operating Unit access and get the Operating Unit
information should be used.
 Record group SQL joining with two or more Multi-Org views, should be modified to limit
reference to one Multi-Org secured synonym and the rest of the references to _ALL tables
(similar to Reference view standards).
 ORG_ID filter should be added to the WHERE Clause of the record group SQL to avoid
Cartesian joins for tables that include ORG_ID as part of the composite key or ORG_ID is the
driving key.
 Need not include form block ORG_ID in the record group SQL, as setting the policy context as
described in section 7.3.6 will handle single as well as multiple Operating Units data.
 In forms that support “derive Operating Unit” feature, policy context as described in section
7.3.6 must be set not only in When-Validate-Item trigger of Operating Unit field but also the
Operating Unit specific fields that can be used to derive it.

Example 1

Record groups using derive operating feature

The LOV is always enabled. If the Operating Unit field is left blank, the current org is not set and the
access mode is set to multiple. So the record group SQL will return data for multiple Operating Units.
If the Operating Unit is selected, the current org is set and the access mode is single and the same LOV
will return data for the selected Operating Unit.

select bs.name source, bs.batch_source_id batch_source_id,


bs.description description,
bs.auto_trx_numbering_flag auto_trx_numbering_flag,
bs.batch_source_type batch_source_type,
bs.default_inv_trx_type default_inv_trx_type,
ctt.name default_type_name, bs.org_id,
mo_global.get_ou_name(bs.org_id)
from ra_cust_trx_types_all ctt,
ra_batch_sources bs
where bs.default_inv_trx_type = ctt.cust_trx_type_id(+)
and bs.org_id = ctt.org_id(+)
and nvl(:tgw_header.ctt_class,'-99') =
decode(:tgw_header.ctt_class, null, '-99', ctt.type(+) )
and nvl(:tgw_header.trx_date,trunc(sysdate)) between
nvl(bs.start_date,nvl(:tgw_header.trx_date,trunc(sysdate)))
and nvl(bs.end_date, nvl(:tgw_header.trx_date, trunc(sysdate)))
and nvl(:tgw_header.trx_date,trunc(sysdate)) between
nvl(ctt.start_date(+),nvl(:tgw_header.trx_date,trunc(sysdate)))
and nvl(ctt.end_date(+), nvl(:tgw_header.trx_date,
trunc(sysdate)))
and nvl(bs.status, 'A') = 'A'
and ( bs.batch_source_type ='INV' or :tgw_header.ctt_class =
'CM' )
and bs.batch_source_id not in (11, 12)
and ( :tgw_header.trx_number is null
or bs.auto_trx_numbering_flag = nvl(
:tgw_header.bs_auto_trx_numbering_flag,'N') )
order by bs.name, bs.description, bs.batch_source_id

Note: In the above example, ORG_ID filter is added to avoid Cartesian join.

Example 2

Record groups using derive operating feature

select ci.cons_billing_number,
ci.customer_id,

Page 56 of 70

Company Confidential - For Internal Use Only


ci.site_use_id,
cu.customer_name,
cu.customer_number,
su.location,
su.org_id,
mo_global.get_ou_name(su.org_id)
from ar_cons_inv_all ci,
ra_customers cu,
ra_site_uses su
where ci.currency_code = :rgw_folder.currency_code
and ci.site_use_id = su.site_use_id
and ci.customer_id = cu.customer_id
order by cons_billing_number

Note: Here it is not necessary to add ORG_ID filter in the Where Clause to join RA_SITE_USES and
AR_CONS_INV views, since site_use_id is unique and sufficient to determine the ORG.

Example 3

Record groups using select operating feature


The LOV is disabled until an Operating Unit is selected. Once an Operating Unit is selected, the current
org and the access mode are set. So the record group SQL will always return data for one Operating
Unit.

select max(tc.name) name,
       lc.displayed_field type,
       tc.description
  from ap_lookup_codes lc,
       ap_tax_codes tc
 where lc.lookup_type = 'TAX TYPE' 
   and tc.tax_type != 'OFFSET' 
   and tc.tax_type != 'AWT' 
   and lc.lookup_code = tc.tax_type 
   and nvl(tc.enabled_flag,'Y')='Y'
 group by tc.name, lc.displayed_field, tc.description

Note 1: It is left to the product teams to implement select Operating Unit or derive Operating Unit for
the record groups based on the business logic. There is no difference to the record group SQL for select
Operating Unit vs derive Operating Unit, since setting the policy context should take care of that.

Note 2: For forms that support select Operating Unit, since the Operating Unit dependent fields are
greyed out, until an Operating Unit is selected, the records groups of these fields could be based on
_ALL tables instead of secured synonym.

The above select statement could be rewritten to use the ALL tables instead of secured synonyms,
passing the form block ORG_ID as given below:

select max(tc.name) name,
       lc.displayed_field type,
       tc.description
  from ap_lookup_codes lc,
       ap_tax_codes_ALL tc
 where lc.lookup_type = 'TAX TYPE' 
   and tc.tax_type != 'OFFSET' 
   and tc.tax_type != 'AWT' 
   and lc.lookup_code = tc.tax_type 
   and nvl(tc.enabled_flag,'Y')='Y'
   and tc.org_id = :<block_name.org_id>
 group by tc.name, lc.displayed_field, tc.description

Page 57 of 70

Company Confidential - For Internal Use Only


4.3.10 Add ORG_ID predicate in Client/Server Code
In the client side and server side application code, there are SQL statements that issue DML against
Multi-Org views. The APIs that are used for validating data within an Operating Unit, can benefit from
using the current org id set by from triggers before the validation logic is fired, instead of ORG_ID
parameter being passed. However, if the same API is used both in the reference view as well as server
side validation from forms, then the API needs to be modified to include ORG_ID input parameter as
given in the reference views section.

The following rules must be followed:


 The Multi-Org temporary table should not be used directly in the SQL query. Instead the
PL/SQL functions available to check the Operating Unit access and get the Operating Unit
information should be used.
 SQL joining with two or more Multi-Org views should be rewritten to use just one secured
synonym based on the driving table for the query and the rest of the views replaced by _ALL
tables.
 ORG_ID filter should be added to the WHERE Clause of the record group SQL to avoid
Cartesian joins for tables that include ORG_ID as part of the composite key or ORG_ID is the
driving key.

Example 1:

BEGIN
SELECT
NVL(copy_doc_number_flag, 'N')
INTO
l_copy_doc_number_flag
FROM
ra_batch_sources
WHERE
batch_source_id = l_ct_rec.batch_source_id
EXCEPTION
WHEN NO_DATA_FOUND THEN
l_copy_doc_number_flag := 'N';
END;

Example 2:

l_trx_str := 'select ra_trx_number_' ||


REPLACE(p_trx_rec.batch_source_id, '-', 'N') ||
l_org_str||
'_s.nextval trx_number ' ||
'from ra_batch_sources ' ||
'where batch_source_id = ' ||
p_trx_rec.batch_source_id ||
' and auto_trx_numbering_flag = ''Y'''
EXECUTE IMMEDIATE l_trx_str
INTO l_trx_number;

4.3.11 Modify table handlers


Prior to opening up access, the RDBMS default value for ORG_ID column was utilized to handle org_id
column population during inserts, updates and deletes. The RDBMS default value makes use of the
CLIENT_INFO org context. The table handlers used the single organization views.

With opening up access, a responsibility may have access to multiple Operating Units. You must not
rely on the RDBMS default value for ORG_ID column since it will not be set anymore. The value for
ORG_ID column must be specified explicitly in the table handlers.

Note: Product teams should not modify the RDBMS default value for ORG_ID to use the current org.
The current_org is introduced mainly to minimize the code change for the product code that always gets
Page 58 of 70

Company Confidential - For Internal Use Only


executed within the context of one Operating Unit. The Operating Unit is validated upfront in the
forms. It is safe to use this value in the table handlers rather than relying on RDBMS default value.

Please refer to upgrade section to see an example of script to remove RDBMS default value for ORG_ID
column.

For insert statements the ORG_ID column value must be passed to the table handlers. For update
statements, if you use a primary key column in your selection criteria, then ORG_ID value is not
required in the table handler. The examples given below demonstrate this:

Example 1:
An insert statement
insert into <table*>
            (<column1>
             <column2>
             …
             <org_id>)
values ( <value1>,
         <value2>,
         …
         p_org_id)

* the table indicated here is the synonym to which Multi-Org security policy is attached.

Example 2:
An update statement
update <table>
   set <column1> = <value1>
where primary_key = <value>             
         

Example 3:
A delete statement
DELETE FROM ra_customer_trx
WHERE customer_trx_id = p_customer_trx_id;

In the example above, the primary key is used for the update and the delete statements, hence ORG_ID
filter is not added.

Page 59 of 70

Company Confidential - For Internal Use Only


A Multi-Org utility to list the Multi-Org tables that have the RDBMS default value (CLIENT_INFO
default) for ORG_ID column is available.

Note: Table handlers could use ALL tables instead of secured synonyms, provided you have validated the
ORG_ID upstream. It is important that you validate the ORG_ID, since you should not be able to do any
DML for an Operating Unit that you do not have access to.

4.3.12 Allow Query on Operating Unit field


The Operating Unit field is a non database item. In order to query by Operating Unit field, its value
needs to be derived from ORG_ID database column. This must be done in the pre-query and post-query
triggers. Use the Multi-Org API FND_ACCESS_CONTROL_UTIL.Get_Org_Name for this purpose.

Block Level Post-Query Trigger


  :<your block name>.operating_unit := 
fnd_access_control_util.get_org_name(:<your block 
name>.org_id);    
  set_record_property(:system.trigger_record,:system.t
rigger_block, STATUS,QUERY_STATUS);

FND_ACCESS_CONTROL_UTIL.Get_Org_Name
FUNCTION Get_Org_Name( p_org_id NUMBER )
RETURN VARCHAR2
IS
  l_return hr_all_organization_units_tl.name%TYPE;
BEGIN
  SELECT name
    INTO   l_return
    FROM   hr_all_organization_units_tl
   WHERE  organization_id = p_org_id
     AND  language = userenv('LANG');

  IF SQL%NOTFOUND
  THEN
    l_return := NULL;
  END IF;

  RETURN l_return;

END Get_Org_Name;

Forms that wish to enable the Query Enter functionality for the Operating Unit name need to modify the
PRE-QUERY trigger of the Operating Unit block. The trigger must dynamically modify the
DEFAULT_WHERE property of the block to append a LIKE sub-query that examines the
hr_operating_units view for records whose name matches the string entered in the Operating Unit field.

Note: Queries on the hr_operating_units view take into account the user's current language context.

Block Level PRE-QUERY trigger

DECLARE
block_id Block := FIND_BLOCK('<block name>');

sub_where VARCHAR2(512);
def_where VARCHAR2(512);

-- Local function definition:

FUNCTION add_and(p_where IN VARCHAR2) RETURN VARCHAR2


IS

Page 60 of 70

Company Confidential - For Internal Use Only


BEGIN
IF (NVL(NVL(length(p_where), 0), 0) != 0) THEN
RETURN( p_where || ' AND ');
ELSE
RETURN( p_where );
END IF;

RETURN NULL;

END;

BEGIN
sub_where := NULL;

IF (<block name>.operating_unit IS NOT NULL) THEN


sub_where := add_and(sub_where) || '(NAME LIKE '''|| :<block
name>.OPERATING_UNIT ||
''')';
END IF;

IF (sub_where IS NOT NULL) THEN


def_where := add_and(def_where) || '((ORG_ID) IN '||'(SELECT
ORGANIZATION_ID '||'FROM
HR_OPERATING_UNITS WHERE '|| sub_where || '))';
END IF;

-- Specify the default WHERE clause for the block.


-- This will NOT override a value established at design time
-- via the Property Palette for the block's WHERE clause property.

set_block_property(block_id, DEFAULT_WHERE, def_where);

END;
4.3.13 Handle Flexfields

7.3.13.1 Accounting Key Flexfields


The chart of accounts ID associated with the accounting flexfields is based on the GL ledger associated
with the Operating Unit. In order that the accounting flexfields work properly, the chart of accounts ID
must be passed as an input parameter to the API that defines key flexfield. Whenever the Operating
Unit field is changed either by selecting the Operating Unit from the LOV for Operating Unit field or by
deriving the Operating Unit from any Operating Unit specific attributes of the transaction, the COA
value should be refreshed from cache.

There is no way to know the ledger ID and chart of accounts ID at forms opening if the responsibility
has access to multiple Operating Units. It is determined only after an Operating Unit is selected or
derived.

To enable access control for accounting flexfields, the following changes must be done:
1. Add a new item CHART_OF_ACCOUNTS_ID to your form block that is a base block of your
canvas. Use this instead of parameter.chart_of_accounts_id, which reduces the number of calls of
fnd_key_flex.define.
2. Call the fnd_key_flex.define in the following triggers:
 Block Level When-Create-Record
 Item Level When-Validate-Item on Operating Unit field and also on Operating Unit specific
fields used in the “derive Operating Unit” feature.
 Block Level Post-Query
 Block Level Pre-Query (if you need to allow querying on accounting flexfields)

Page 61 of 70

Company Confidential - For Internal Use Only


3. Disable the call to fnd_flex.define in the form level as well as block level Pre-Query trigger.
Otherwise, you will get the error APP-FND-01016: Routine FDFRKS: Unknown structure ID for
flexfield code GL# with application ID 101 during blind query for Multi-Org case.

For example, the following code defines the key flexfield structure for inserts, updates and queries.

Block Level Post-query trigger/When-Create-Record trigger/When-Validate-Item trigger on the


Operating Unit field and other Operating Unit specific fields used to derive Operating Unit

IF (:<your block name.org_id> IS NOT NULL) AND


(:<your block name.operating_unit> IS NOT NULL) THEN

IF :<block name.org_id> <> 
nvl(:<parameter.old_org_id>, ­99) THEN

l_gr := <<your product short


name>>_MO_local_cache.get_org_attributes(:<<your block
name>>.org_id);
:<block name>.chart_of_account_id :=
l_gr.chart_of_account_id;
/* Initialize Other Parameters */
...
FND_KEY_FLEX.DEFINE(
BLOCK=>’<your block name>’,
FIELD =>’<your field name>’,
TITLE =>:<block
name>Field_name, …
NUM => <block
name>.chart_of_account_id, …);
...
END IF;
END IF;

Querying on Accounting Flexfields:


You must disable the key flexfield (fnd_flex.event call) in the form level as well as in the block level
Pre-Query triggers and enable it in the block level Post-Query trigger if you do not need the ability to
query on accounting flexfields in your form. If you do not allow enter-query on Accounting Flexfields,
you must set the item property “QUERY ALLOWED” to No.

However, if you need the ability to query on accounting flexfields, then you should add additional logic
in the block level pre-query trigger to handle enter query. The accounting flexfield must be used in the
query only if the Operating Unit is specified. In other words, the accounting flexfield field should be
made dependent on Operating Unit field. However, during enter-query, we cannot control item
properties to set dependent items. Instead, a message must be displayed to the users asking them to
enter a unique Operating Unit when they execute enter-query.

The following table lists the scenarios when the message should be displayed to the user:
Operating Unit Field Accounting Flexfield Result
Field

1 Any Value Null Execute the standard enter query


2 Null Not Null Display message “Please enter Operating
Unit” for Multi-Org case
3 Non Null (Cannot identify Not Null Display message “The system cannot
Operating Unit uniquely. E.g. identify KFF structure. Please enter the
Vis%) full Operating Unit name”
4 Not Null (Can identify Not Null Execute the standard enter query
Operating Unit uniquely. E.g.
Vision Operations)
5 Not Null (No Operating Unit is Not Null Execute the standard enter query
found matching with the

Page 62 of 70

Company Confidential - For Internal Use Only


condition)

The following code handles enter query on accounting flexfields:

Block Level Pre-Query Trigger


PROCEDURE PRE_QUERY IS
l_org_id NUMBER(15);
l_gr AP_MO_cache_utils.GlobalsRecord;
l_no_ou_found EXCEPTION;
BEGIN
IF :invoices_folder.operating_unit IS NULL THEN
IF :invoices_folder.liability_account IS NOT NULL THEN
fnd_message.set_name('FND','MO_SRCH_OU_REQUIRED');
fnd_message.error;
RAISE FORM_TRIGGER_FAILURE;
END IF;
ELSIF :invoices_folder.operating_unit IS NOT NULL THEN
IF :invoices_folder.liability_account IS NOT NULL THEN
BEGIN
SELECT organization_id
INTO l_org_id
FROM hr_operating_units
WHERE organization_name like
:invoices_folder.operating_unit
AND mo_global.check_access(organization_id) = ‘Y’;
EXCEPTION
WHEN TOO_MANY_ROWS THEN
fnd_message.set_name('FND',’MO_SRCH_MULT_OU_FOUND’);
fnd_message.error;
WHEN NO_DATA_FOUND THEN
fnd_message.set_name('FND',’MO_SRCH_NO_OU_FOUND’);
fnd_message.error;

END;

l_gr := ap_mo_local_cache.get_org_attributes(l_org_id);
:invoices_folder.chart_of_accounts_id :=
l_gr.chart_of_accounts_id;
fnd_key_flex.define(
BLOCK=>'INVOICES_FOLDER',
FIELD=>'LIABILITY_ACCOUNT',
DESCRIPTION=>'LIABILITY_DESCRIPTION',
ID=>'ACCTS_PAY_CODE_COMBINATION_ID',

fnd_flex.event('PRE-QUERY');
END IF;
END IF;
EXCEPTION
WHEN l_no_ou_found THEN
NULL;
WHEN OTHERS THEN
RAISE FORM_TRIGGER_FAILURE;
END;

For master-detail blocks, where accounting flexfield is present in both the master and detail blocks, the
pre-query trigger in the master block must have the call to define key flexfields. You must not call the
define flexfields in the detail block’s pre-query trigger. However, the pre-query trigger in the detail
block should have code to update the block’s chart of accounts ID and call to fnd_flex.event as given
below:

Block Level Pre-Query trigger


Page 63 of 70

Company Confidential - For Internal Use Only


IF (:<your block name.org_id> IS NOT NULL) AND
(:<your block name.operating_unit> IS NOT NULL)
THEN
IF :<block name.org_id> <> 
nvl(:<parameter.old_org_id>, ­99) THEN
l_gr := <<your product short
name>>_MO_local_cache.get_org_attributes(:<<your block
name>>.org_id);

:<block name>.chart_of_account_id :=
l_gr.chart_of_account_id;
fnd_flex.event(‘Pre-Query’);
END IF;
END IF;

4.3.14 Handle Operating Unit value change

The user should be allowed to change the Operating Unit at any point of time before the record is
committed to the database. After the record is committed in the database, the Operating Unit field should
be disabled, preventing users from updating it.

Post-Insert Trigger of Operating Unit field Block

app_item_property.set_property(‘BLOCK_NAME.OPERATING_UNIT’, ENABLED,
PROPERTY_OFF);

The Operating Unit field should not be enabled for the queried records.

Post-Query Trigger of Operating Unit field Block

app_item_property.set_property(‘BLOCK_NAME.OPERATING_UNIT’, ENABLED,
PROPERTY_OFF);

When-New-Record-Instance Trigger of Operating Unit field Block

IF :system.record_status = ‘QUERY’ THEN


app_item_property.set_property(‘BLOCK_NAME.OPERATING_UNIT’, ENABLED,
PROPERTY_OFF);
ELSE
app_item_property.set_property(‘BLOCK_NAME.OPERATING_UNIT’, ENABLED,
PROPERTY_ON);
END IF;

4.4 Enhancement to Reports


4.4.1 Overview

This section details the changes for Single Org and Cross Org reports

Page 64 of 70

Company Confidential - For Internal Use Only


4.4.1 Single Org Reports

The Operating Unit is a requires field and default value is derived from the

4.4.2 Cross Org Reports

Cross Org Reports should be flagged as ‘MULTIPLE’ for the Operating Unit mode in the “Define
Concurrent Programs” form. The special parameter – Operating Unit will not be available for Cross
Org Reports. The security for Cross Org Reports is modified to take into consideration the “MO:
Security Profile” profile.

Cross Org Reports currently have 2 parameters – Reporting Level and Reporting Context. The valuesets
of these parameters are modified to include MO_GLOB_ORG_ACCESS_TMP table. Also, The Cross
Org APIs that are called in the report executables are modified to include “MO: Security Profile”. The
changes are transparent to the product teams, since Multi-Org product owns the valuesets and the Cross
Org APIs.

At runtime the Multi-Org initialization populates the temporary table with one or multiple Operating
Units based on the access enabled status of the product owning the cross org report.

You should not refer to CLIENT_INFO logic anywhere in the reports. Also NVL function for ORG_ID
should be removed, as Multi-Org is mandatory for R12

4.5 Concurrent Program Enhancements


4.5.1 Overview

This section details the changes for single org and multiple org concurrent programs.

4.5.2 Single Org Concurrent Programs

Single Org Concurrent Programs should be flagged as ‘SINGLE’ for the Operating Unit mode in the
“Define Concurrent Programs” form.

The Operating Unit is a requires field and default value is derived from the
MO_URILS.get_default_org_id() API.

4.5.3 Multiple Org Concurrent Programs

Multiple Org Concurrent Programs should be flagged as ‘MULTIPLE’ for the Operating Unit mode in
the “Define Concurrent Programs” form. The ATG Enhancement (ER 2420755) would allow Multi-Org
temporary table MO_GLOB_ORG_ACCESS_TMP to be populated when the user select such concurrent
programs. The special parameter – Operating Unit will not be available for these programs. Instead
product teams should expose Operating Unit parameter as a program parameter. This is an optional
parameter that allows user to submit the concurrent program for a single Operating Unit or for the
Operating Units specified in “MO: Security Profile” profile.

The valueset of the Operating Unit parameter should be as given below:


    SELECT hr.organization_id   org_id
         , hr.name              operating_unit
      FROM hr_operating_units hr
     WHERE mo_global.check_access(hr.organization_id) = ‘Y’

Note: You should not reference the Multi-Org temporary table in the concurrent program seed data or in

Page 65 of 70

Company Confidential - For Internal Use Only


5. Glossary
balancing entity
An organization for which you prepare a balance sheet, represented as a balancing segment value in
your accounting flexfield. This is the equivalent of a fund in government organizations. Examples
include companies, strategic business units, and divisions.

business activity
A process, defined by development, performed by applications users that creates and maintains business
transactions or reference data. Examples of business transactions include, but are not limited to:
requisitions, purchase orders, receipts, inventory transfers, invoices, and payments. Examples of
reference data include customer, supplier and bank account information.

business group
An organization which represents the consolidated enterprise, a major division, or an operation
company. This entity partitions Human Resources information and business group level data is secured
by security groups. A business group (BG) is a highest level in an organization hierarchy.

business unit
An organizational group within an enterprise. (See also: organization).

intercompany invoice
An automatically generated statement that eliminates intercompany profit. This transaction may occur
between organizations in the same or different legal entities.

inventory organization
An organization that tracks inventory transactions and balances, and/or that manufactures or distributes
products.

ledger (Get definition from GL)

legal entity ( Replace LE definition from LE document when it becomes available. 5/4/2001)
An organization that represents a legal company that you control financial statements and taxes
(whether it is income tax, sales tax or any other fiscal liability). All tax related documents should be
linked to the appropriate legal entity to grant audit trail required by fiscal authority. Legal reports should
be available at legal entity level. A legal entity is comprised of one or more Operating Units. A legal
entity is represented in General Ledger as one or more balancing segment values within a ledger.

multiple installations
Refers to installing subledger products (AP, AR, PO, OE) multiple times for data partitioning purpose.
This is no longer necessary under a Multi–Org implementation.

multiple sets of books


A General Ledger concept for having separate entities for which chart of accounts, calendar, or
functional currency differs.

Page 66 of 70

Company Confidential - For Internal Use Only


Operating Unit
Release 11i and prior terminology: any autonomous organization which uses Oracle Receivables, Oracle
Payables, Oracle Order Entry, Oracle Purchasing or Oracle Projects. An Operating Unit is always
associated with a single legal entity. Information is secured by ‘Operating Unit’ in the above products
with some shared information.

Release 12 terminology: any autonomous organization which is assigned business activities


corresponding to any of these products: Receivables, Order Management, Payables, Purchasing and
Projects. Operating Units, in Release 12, may be operating on behalf of one or more legal entities and is
a much broader concept than that in prior releases. Operating Units in prior releases were assigned to
predefined sets of business activities by application module.

Operating Unit relationship


In shared services environment, one Operating Unit can perform business activities on behalf of one or
more other Operating Units. These relationships are called Operating Unit relationships.

Organization
An organization is an autonomous business unit of an enterprise, such as a plant, warehouse, division,
or department. Organizations are categorized by organization classification.

organization classification
An organization classifications are a set of system-defined attributes that categorize an organization. For
example, classifications include, but are not limited to: Operating Unit, project expenditure
organization, inventory organization and human resources organization. For more information, please
refer to Error! Bookmark not defined..

organization hierarchy
An organization hierarchies shows hierarchical relationships among organizations in enterprise.
Organization hierarchies are used to construct security profiles.

Responsibility
Determines the data, forms, menus, reports, and concurrent programs you can access in Oracle
Applications. It is linked directly to a data group. Several users can share the same responsibility, and a
single user can have multiple responsibilities.

In Release 11i and prior releases, a profile option controlled the Operating Unit to which the
responsibility was assigned.

In Release 12, a responsibility is assigned to a security profile to control access to one or more Operating
Units is assigned to a responsibility. This allows a user to access data in multiple Operating Units
without changing his responsibility.

security group
Used to secure data within one business group. If installation only has one business group, there is only
one security group.

security profile
A security profile represents a list of one or more Operating Units to which a user has access for inquiry,
reporting and transaction and data entry. Every application user is assigned an organization security
profile by way of their responsibility. Security profiles are defined based on organization hierarchies.

Page 67 of 70

Company Confidential - For Internal Use Only


service bureau
An implementation that is supporting many separate enterprises.

set of books (aka ledger)

A financial reporting entity that partitions General Ledger information and uses a particular chart of
accounts (Accounting Flexfield structure), functional currency, and accounting calendar. You must
define at least one ledger for each enterprise.

Page 68 of 70

Company Confidential - For Internal Use Only

Potrebbero piacerti anche