Você está na página 1de 69

WHITE PAPER TO ENHANCE/

UPTAKE MULTIPLE ORGANIZATION


ACCESS CONTROL FOR R12
UPGRADE
ORACLE APPS
Author:

Anuj Kumar

Last Updated:

Creation Date:

01-Feb-2008

05-Feb-2008

File Name:
Version:

1.0

Status:

For Review

Page 1 of 68
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
3.1.4
3.1.5

3.2

Specifications to Oracle Forms..............................................................................................11


3.2.1
3.2.3

3.4

Select Operating Unit For Setup and Transaction Forms...........................................11


Operating Unit Default................................................................................................16

Reporting................................................................................................................................17
3.4.1
3.4.2
3.4.3

3.5

Defining Security based on Hierarchy or a List of Operating Units.............................8


Process Flow...................................................................................................................9
Assign MO: Security Profile to Application Responsibility........................................10

Single Org Reports.......................................................................................................17


Cross Org Reports........................................................................................................17
Define Concurrent Programs Window.........................................................................18

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
4.1.2
4.1.3
4.1.4
4.1.5
4.1.6
4.1.8

7.2

Background..................................................................................................................21
Virtual Private Database (VPD)...................................................................................21
Multi-Org Security Policy Predicate............................................................................22
Multi-Org Initialization...............................................................................................24
Datamodel Design........................................................................................................27
Profile Options............................................................................................................28
Multi-Org APIs............................................................................................................29

Multi-Org Views/Tables Change............................................................................................32


Page 2 of 68
Company Confidential - For Internal Use Only

7.2.1
7.2.2
4.2.3
4.2.4
4.2.5
4.3

Forms Enhancements.............................................................................................................47
4.3.1
4.3.2
4.3.3
4.3.4
4.3.6
4.3.9
4.3.10
4.3.11
4.3.12
4.3.13
4.3.14

4.4

Multi-Org Initialization...............................................................................................47
Add Operating Unit Field............................................................................................47
Create LOV for Operating Unit field...........................................................................47
Default Operating Unit on forms startup.....................................................................48
Setting the Dynamic Policy Context............................................................................49
Modify Record Groups for Operating Unit specific fields..........................................57
Add ORG_ID predicate in Client/Server Code.........................................................59
Modify table handlers................................................................................................60
Allow Query on Operating Unit field........................................................................62
Handle Flexfields.......................................................................................................63
Handle Operating Unit value change........................................................................66

Enhancement to Reports........................................................................................................66
4.4.1
4.4.1
4.4.2

4.5

Enforce NOT NULL constraint on ORG_ID column..................................................32


Modify your Database Views.......................................................................................33
Attach Security Policy to your database objects..........................................................45
Remove Dependency on Multi-Org in AOL tables......................................................46
Register Multi-Org Access Enabled in MO table........................................................46

Overview......................................................................................................................66
Single Org Reports.......................................................................................................66
Cross Org Reports........................................................................................................67

Concurrent Program Enhancements......................................................................................67


4.5.1
4.5.2
4.5.3

Overview......................................................................................................................67
Single Org Concurrent Programs................................................................................67
Multiple Org Concurrent Programs.............................................................................67

0 5.......................................................................................................................................................Glossary
68

Page 3 of 68
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 68
Company Confidential - For Internal Use Only

E x is tin g S e c u r ity
M odel

N e w S e c u r it y
M odel

D e f in e O p e r a t in g
U n its

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

STEP 1
D e fin e
O r g a n iz a tio n
H ie r a r c h y

Yes

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

No

STEP 2
D e fin e S e c u r ity
P r o f il e

STEP 1
D e fin e
O r g a n iz a tio n
H ie r a r c h y

STEP 2
D e fin e S e c u r it y
P r o f ile & R u n
S e c u r it y L is t
M a in te n a n c e
P ro g ra m

STEP 3
R u n S e c u r it y L is t
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 M O : S e c u r ity
P r o f il e

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 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 68
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
R e s p o n s ib ility

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

O U2
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 68
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
Asset Organization
Business Group
Corporate Headquarters
CRP Organization
GRE/Legal Entity
HR Organization
Inventory Org
MRP Organization
Operating Unit
Project Expenditure/Event Organization
Project Invoice Collection Organization
Project Manufacturing Organization
Project Task Owning Organization
Reporting Establishment
WIP Organization

Human Resources
Fixed Assets
Human Resources
Human Resources
Manufacturing
HR, AP, AR, etc.
Human Resources
Manufacturing
Manufacturing
AP, AR, CE, PO, OE, PA, CRM
Projects
Projects
Projects
Projects
Human Resources
Manufacturing

Table 1-Organization Classifications

Page 7 of 68
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 68
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 68
Company Confidential - For Internal Use Only

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

Yes

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

N o
STEP 1
D e fin e
O r g a n iz a tio n
H ie r a r c h y

STEP 2
D e fin e S e c u r ity
P r o file & R u n
S e c u r ity L is t
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 68
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.

Page 11 of 68
Company Confidential - For Internal Use Only

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

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 68
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.

Page 13 of 68
Company Confidential - For Internal Use Only

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

Page 14 of 68
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 15 of 68
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 16 of 68
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

3.4.1

1.

Single Org Reports

2.

Cross Org Reports

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 17 of 68
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:

3.5.1

1.

Single Org Concurrent Programs

2.

Concurrent Programs that run for the Security Profile

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 18 of 68
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 19 of 68
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 20 of 68
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 users
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 21 of 68
Company Confidential - For Internal Use Only

AP Schema
APPS Schema
AP_INVOICES_ALL (synonym to the
AP_INVOICES_ALL table)
AP_INVOICES (synonym with the
security policy attached that gives
access to ORG_ID=1)

AP_INVOICES_ALL table
ORG_ID
INVOICE_ID
1
1000
1
1001
1
1002
2
1003
2
1004

Figure 15 - Database Schema


Access to multiple Operating Units
AP Schema
APPS Schema
AP_INVOICES_ALL (synonym to the
AP_INVOICES_ALL table)
AP_INVOICES (synonym with the
security policy attached that gives
access to ORG_ID 1 and 2)

AP_INVOICES_ALL table
ORG_ID
INVOICE_ID
1
1000
1
1001
1
1002
2
1003
2
1004

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:
FUNCTIONorg_security(obj_schemaVARCHAR2,
obj_nameVARCHAR2)RETURNVARCHAR2
Page 22 of 68
Company Confidential - For Internal Use Only

IS
BEGIN

Returnsdifferentpredicatesbasedontheaccess_mode
Thecodesforaccess_modeare
MMultipleOUAccess
AAllOUAccess
SSingleOUAccess
NullBackwardCompatibilityCLIENT_INFOcase

IFg_access_modeISNOTNULLTHEN
IFg_access_mode='M'THEN
RETURN'EXISTS(SELECT1
FROMmo_glob_org_access_tmpoa
WHEREoa.organization_id=org_id)';
ELSIFg_access_mode='A'THENforfutureuse
RETURNNULL;
ELSIFg_access_mode='S'THEN
RETURN'org_id=
sys_context(''multi_org2'',''current_org_id'')';
ENDIF;
ELSE
RETURN'org_id=substrb(userenv(''CLIENT_INFO''),1,10)';
ENDIF;
ENDorg_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:
SELECTtrx_numberfromra_customer_trx
Page 23 of 68
Company Confidential - For Internal Use Only

will be modified at runtime if the responsibility has access to multiple Operating Units as:
SELECTtrx_numberfromra_customer_trx
WHERE(EXISTS(SELECT1
FROMmo_glob_org_access_tmpoa
WHEREoa.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:
SELECTtrx_numberfromra_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 MultiOrg 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 MultiOrg 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


Page 24 of 68
Company Confidential - For Internal Use Only

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.
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
PROCEDUREinit(p_appl_short_nameVARCHAR2)
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_NAMEEXCEPTION;
BEGIN
IFis_multi_org_enabled='Y'THEN
IFp_appl_short_nameISNULLTHEN
RAISENO_APPL_NAME;Seedanewmesg???
ELSE

Gettheprofilevaluesandcallset_org_accessAPI

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);
ENDIF;
ENDIF;MultiOrgisenabled
EXCEPTION

ENDinit;

MO_GLOBAL.Set_Org_Access Procedure

Page 25 of 68
Company Confidential - For Internal Use Only

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.
PROCEDUREset_org_access(p_org_id_charVARCHAR2,
p_sp_id_charVARCHAR2,
p_appl_short_nameVARCHAR2)
IS
PRAGMAAUTONOMOUS_TRANSACTION;
l_access_ctrl_enabledVARCHAR2(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_idhr_operating_units.name%TYPE;
l_view_all_orgsVARCHAR2(1);
NO_SP_OU_FOUNDEXCEPTION;
NO_ORG_ACCESS_FOUNDEXCEPTION;
BEGIN
IFis_multi_org_enabled<>'Y'THEN
RETURN;
ENDIF;
IFp_org_id_charISNULLANDp_sp_id_charISNULLTHEN
RAISENO_SP_OU_FOUND;
ENDIF;

Replacethiscodewith10gsharedglobals

BEGIN
SELECTnvl(mpi.status,'N')
INTOl_access_ctrl_enabled
FROMfnd_mo_product_initmpi
WHEREmpi.application_short_name=p_appl_short_name;
EXCEPTION
WHENNO_DATA_FOUNDTHEN
l_access_ctrl_enabled:='N';
WHENOTHERSTHEN
generic_error('MO_GLOBAL.SET_ORG_ACCESS',sqlcode,sqlerrm);
END;

Deletetemporarytabledatafirstforallproductsaccess
enabledornot

delete_orgs;

Forallproducts,whentheaccesscontrolfeatureisenabled,
1.UsetheMO:SecurityProfileifitisset.
2.UsetheMO:OperatingUnitifMO:SecurityProfileisnot
set

IF(l_access_ctrl_enabled='Y')THEN
IFl_security_profile_idISNOTNULLTHEN
l_org_id:=null;
ENDIF;

Populatetemptable

populate_orgs(l_org_id,
l_security_profile_id,
l_current_org_id,
Page 26 of 68
Company Confidential - For Internal Use Only

l_view_all_orgs);

CheckifyouhaveaccesstoatleastoneOperatingUnit.

IFg_ou_count=0THEN
RAISENO_ORG_ACCESS_FOUND;
ELSIFg_ou_count=1THEN

Setthe'Single'accesscontexts:

set_policy_context('S',l_current_org_id);
ELSE

AddedcodeforAllmodetoavoidusingthepolicypredicate
whenuserhasaccesstoglobalviewallsecurityprofile
Bug(2720892)
Settheaccesscontexts:

IFl_view_all_orgs='Y'THEN
set_policy_context('A','');
ELSE
set_policy_context('M','');
ENDIF;
ENDIF;
ELSE

Resetthecontextforproductsthathavenotenabledaccess
control

set_policy_context('','');
ENDIF;
commit;
EXCEPTION

ENDset_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 responsibilitys (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?

ORGANIZATION_ID

Number(15)

Not
Null

Yes

NAME

Varchar2(240)

null

Column
Description
Operating
Unit
identifier
Name of the
Operating
Unit

Translatable?
No
Yes

Page 27 of 68
Company Confidential - For Internal Use Only

A unique index MO_GLOB_ORG_ACCESS_TMP_U1 exists on ORGANIZATION_ID column.


FND_MO_PRODUCT_INIT
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?

APPLICATION_SHORT_
NAME
CREATION_DATE

Varchar2(50)

Yes

CREATED_BY

Number(15)

LAST_UPDATED_BY

Number(15)

LAST_UPDATE_DATE

Date

Not
Null
Not
Null
Not
Null
Not
Null
Not
Null

LAST_UPDATE_LOGIN

Number(15)

Date

Column
Description
Application
Short Name
Creation Date

Translatable?

Created By

No

Last Updated
By
Last Update
Date
Last Update
Login

No

No
No

No
No

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
Application
User Profile Name
Description
SQL Validation

XLA_MO_SECURITY_PROFILE_LEVEL
Oracle Common Accounting Modules
MO: Security Profile
Multi-Org Access Control

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
Page 28 of 68

Company Confidential - For Internal Use Only

Field Name

Value
Updatable: Yes

System Administrator Access: User

Visible: No

Updatable: No

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 MultiOrg Access Control feature.
MO: Default Operating Unit

Field Name

Value

Name
Application
User Profile Name
Description
SQL Validation

DEFAULT_ORG_ID
Application Object Library
MO: Default Operating Unit
Default Operating Unit the Responsibility Logs Onto
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 users 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.
FUNCTIONcheck_access(p_org_idNUMBER)
RETURNVARCHAR2IS
BEGIN
SELECT'Y'
Page 29 of 68
Company Confidential - For Internal Use Only

INTOl_org_exists
FROMmo_glob_org_access_tmp
WHEREorganization_id=p_org_id;
RETURN'Y';
END;

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.
SELECTname,
organization_id
FROMhr_operating_units
WHEREmo_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.
FUNCTIONget_ou_name(p_org_idNUMBER)
RETURNVARCHAR2IS
BEGIN
SELECTorganization_name
INTOl_ou_name
FROMmo_glob_org_access_tmp
WHEREorganization_id=p_org_id;
RETURNl_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'.
FUNCTIONcheck_valid_org(p_org_idNUMBER)
RETURNVARCHAR2
IS
BEGIN
IF(p_org_idisnull)THEN
Postanerrormessageandreturn:
fnd_message.set_name('FND','MO_ORG_REQUIRED');
FND_MSG_PUB.ADD;
RETURN'N';
ENDIF;
IF(check_access(p_org_id)='Y')THEN
RETURN'Y';
ENDIF;
Postanerrormessageandreturn:
fnd_message.set_name('FND','MO_ORG_INVALID');
FND_MSG_PUB.ADD;
Page 30 of 68
Company Confidential - For Internal Use Only

RETURN'N';
END;
This API can be used in the Public APIs for validating ORG_ID input.
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.
FUNCTIONget_current_org_id
RETURNNUMBERIS
BEGIN
RETURNto_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.
FUNCTIONget_access_mode
RETURNVARCHAR2IS
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 MultiOrg Access Control enabled.

FND_MO_PRODUCT_INIT_PKG.remove_application Procedure
This API deletes an entry in the FND_MO_PRODUCT_INIT_PKG.

Page 31 of 68
Company Confidential - For Internal Use Only

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.
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:

7.2.2

Column Name

DataType

Not Null

ORG_ID

Number(15)

Yes

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.

Page 32 of 68
Company Confidential - For Internal Use Only

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).
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.
CREATEORREPLACEVIEWRA_BATCHESAS
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"
FROMRA_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:
CREATESYNONYMRA_BATCHESFORAR.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.

Page 33 of 68
Company Confidential - For Internal Use Only

CREATEORREPLACEVIEWAR_VAT_TAX_BAS
SELECT"VAT_TAX_ID",
"SET_OF_BOOKS_ID",
"TAX_CODE",
...
"ORG_ID",
...
FROMAR_VAT_TAX_ALL_B
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.
CREATESYNONYMAR_VAT_TAX_BFORAR.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.
CREATEORREPLACEVIEWAP_CARD_SUPPLIERSAS
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.
CREATESYNONYMAP_CARD_SUPPLIERSFOR
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)
Page 34 of 68
Company Confidential - For Internal Use Only

CREATEORREPLACEVIEWAR_PAYMENT_SCHEDULES_VAS
SELECTPS.ROWID,
PS.PAYMENT_SCHEDULE_ID,
PS.TRX_NUMBER,
...
FROMar_lookupsal_status,
ar_collectorsar_coll,
ar_cons_inv_allcons,
ra_cust_trx_types_allctt,
ra_batch_sources_allbs,
ra_customer_trx_allct,
hz_cust_site_uses_allsu,
hz_cust_accountscust_acct,
hz_partiesparty,
ar_payment_schedules_allps
WHEREPS.CUSTOMER_ID=CUST_ACCT.CUST_ACCOUNT_ID
ANDCUST_ACCT.PARTY_ID=PARTY.PARTY_ID
ANDPS.CUSTOMER_SITE_USE_ID=SU.SITE_USE_ID
ANDPS.CUSTOMER_TRX_ID=CT.CUSTOMER_TRX_ID
ANDCT.BATCH_SOURCE_ID=BS.BATCH_SOURCE_ID
ANDCT.CUST_TRX_TYPE_ID=CTT.CUST_TRX_TYPE_ID
ANDPS.STATUS=AL_STATUS.LOOKUP_CODE
ANDAL_STATUS.LOOKUP_TYPE='INVOICE_TRX_STATUS'
ANDPS.COLLECTOR_LAST=AR_COLL.COLLECTOR_ID(+)
ANDPS.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(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,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)
ANDPS.STATUS=NVL(ARP_VIEW_CONSTANTS.GET_STATUS,PS.STATUS)
UNIONALL
SELECTPS.ROWID,
PS.PAYMENT_SCHEDULE_ID,
PS.TRX_NUMBER,
...
FROMar_lookupsal_risk_receipt,
Page 35 of 68
Company Confidential - For Internal Use Only

ar_cons_inv_allcons,
ar_receipt_methodsrm,
ar_batch_sources_allbs,
ar_batches_allarb2,
ar_cash_receipt_history_allcrh,
ar_cash_receipt_history_allcrh_current,
ar_cash_receipts_allcr,
hz_cust_site_uses_allsu,
hz_cust_accountscust_acct,
hz_partiesparty,
ar_payment_schedules_allps,
ar_cash_receipt_history_allcrh_remit,
ar_batches_allarb_remit,
fnd_currenciesfc
WHEREPS.CUSTOMER_ID=CUST_ACCT.CUST_ACCOUNT_ID(+)
ANDCUST_ACCT.PARTY_ID=PARTY.PARTY_ID(+)
ANDPS.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
Page 36 of 68
Company Confidential - For Internal Use Only

),'',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,
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):
CREATEORREPLACEVIEWAR_PAYMENT_SCHEDULES_VAS
SELECTPS.ROWID,
PS.PAYMENT_SCHEDULE_ID,
PS.TRX_NUMBER,
...
PS.ORG_ID
FROMar_lookupsal_status,
ar_collectorsar_coll,
ar_cons_inv_allcons,
ra_cust_trx_types_allctt,
ra_batch_sources_allbs,
ra_customer_trx_allct,
hz_cust_site_uses_allsu,
hz_cust_accountscust_acct,
hz_partiesparty,
ar_payment_schedulesps
WHEREPS.CUSTOMER_ID=CUST_ACCT.CUST_ACCOUNT_ID
ANDCUST_ACCT.PARTY_ID=PARTY.PARTY_ID
ANDPS.CUSTOMER_SITE_USE_ID=SU.SITE_USE_ID
ANDPS.CUSTOMER_TRX_ID=CT.CUSTOMER_TRX_ID
ANDCT.BATCH_SOURCE_ID=BS.BATCH_SOURCE_ID
ANDCT.ORG_ID=BS.ORG_ID
ANDCT.CUST_TRX_TYPE_ID=CTT.CUST_TRX_TYPE_ID
ANDCT.ORG_ID=CTT.ORG_ID
ANDPS.STATUS=AL_STATUS.LOOKUP_CODE
ANDAL_STATUS.LOOKUP_TYPE='INVOICE_TRX_STATUS'
ANDPS.COLLECTOR_LAST=AR_COLL.COLLECTOR_ID(+)
ANDPS.CONS_INV_ID=CONS.CONS_INV_ID(+)
Page 37 of 68
Company Confidential - For Internal Use Only

ANDPS.STATUS=NVL(ARP_VIEW_CONSTANTS.GET_STATUS,PS.STATUS)
UNIONALL
SELECTPS.ROWID,
PS.PAYMENT_SCHEDULE_ID,
PS.TRX_NUMBER,
...
PS.ORG_ID
FROMar_lookupsal_risk_receipt,
ar_cons_inv_allcons,
ar_receipt_methodsrm,
ar_batch_sources_allbs,
ar_batches_allarb2,
ar_cash_receipt_history_allcrh,
ar_cash_receipt_history_allcrh_current,
ar_cash_receipts_allcr,
hz_cust_site_uses_allsu,
hz_cust_accountscust_acct,
hz_partiesparty,
ar_payment_schedulesps,
ar_cash_receipt_history_allcrh_remit,
ar_batches_allarb_remit,
fnd_currenciesfc
WHEREPS.CUSTOMER_ID=CUST_ACCT.CUST_ACCOUNT_ID(+)
ANDCUST_ACCT.PARTY_ID=PARTY.PARTY_ID(+)
ANDPS.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.
CREATEORREPLACEVIEWRA_ADDRESSESAS
SELECT ROW_ID ,
Page 38 of 68
Company Confidential - For Internal Use Only

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), '
',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:
CREATEORREPLACEVIEWAR_VAT_TAX_VLAS
SELECTB.ROWIDROW_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
FROMAR_VAT_TAX_ALL_TLT,
AR_VAT_TAX_ALL_BB
WHEREB.VAT_TAX_ID=T.VAT_TAX_ID
ANDNVL(B.ORG_ID,99)=NVL(T.ORG_ID,99)
ANDNVL(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)
ANDT.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:
Page 39 of 68
Company Confidential - For Internal Use Only

Modified View definition with MLS logic


CREATEORREPLACEVIEWAR_VAT_TAX_VLAS
SELECTB.ROWIDROW_ID,
B.ADJ_NON_REC_TAX_CCID,
B.EDISC_NON_REC_TAX_CCID,
B.UNEDISC_NON_REC_TAX_CCID,
B.ORG_ID
...
B.ENABLED_FLAG,
B.TAX_CLASS,
B.DISPLAYEDED_FLAG,
B.TAX_CONSTRAINT_ID
FROMAR_VAT_TAX_ALL_TLT,
AR_VAT_TAX_BB
WHEREB.VAT_TAX_ID=T.VAT_TAX_ID
ANDNVL(B.ORG_ID,99)=NVL(T.ORG_ID,99)(notneededsince
vat_tax_idisuniqueacrossorgs)
ANDT.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:
CREATEORREPLACEVIEWAR_BATCHES_MRC_VAS
SELECTMC.BATCH_ID,
MC.SET_OF_BOOKS_ID,
LAST_UPDATED_BY,
...
TRANSMISSION_ID,
BANK_DEPOSIT_NUMBER,
ORG_ID,
PURGED_CHILDREN_FLAG
FROMAR_BATCHES_ALLB,
AR_MC_BATCHESMC
WHEREMC.BATCH_ID=B.BATCH_ID
ANDMC.SET_OF_BOOKS_ID=NVL(TO_NUMBER(SUBSTRB(USERENV('CLIENT_INFO'),
45,10)),99)
ANDNVL(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)

Page 40 of 68
Company Confidential - For Internal Use Only

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:
CREATEORREPLACEVIEWAR_BATCHES_MRC_VAS
SELECTMC.BATCH_ID,
MC.SET_OF_BOOKS_ID,
LAST_UPDATED_BY,
...
TRANSMISSION_ID,
BANK_DEPOSIT_NUMBER,
ORG_ID,
PURGED_CHILDREN_FLAG
FROMAR_BATCHESB,
AR_MC_BATCHESMC
WHEREMC.BATCH_ID=B.BATCH_ID
ANDMC.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
CREATEORREPLACEVIEWRA_CUSTOMER_TRX_PARTIAL_VAS
SELECTCT.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
Page 41 of 68
Company Confidential - For Internal Use Only

MBE
R')"RAS_PRIMARY_SALESREP_NUM
...
FROMRA_CUST_TRX_LINE_GL_DISTGD,
RA_CUSTOMER_TRXCT,

RA_SITE_USESSU_BILL,
RA_CUSTOMERSRAC_PAYING,
RA_CUSTOMERSRAC_BILL,
RA_SITE_USESSU_BILL,
RA_SITE_USESSU_SHIP,
RA_SITE_USESSU_PAYING,

RA_ADDRESSESRAA_BILL,
RA_ADDRESSESRAA_SHIP,
RA_ADDRESSESRAA_REMIT,
RA_CONTACTSRACO_SHIP,
RA_CONTACTSRACO_BILL,
AP_BANK_ACCOUNTSAPBA,
AP_BANK_BRANCHESAPB,
AR_RECEIPT_METHODSARM,
AR_RECEIPT_CLASSESARC,
RA_BATCH_SOURCESBS,
RA_BATCHESRAB,
RA_CUST_TRX_TYPESCTT,

WHERECT.CUSTOMER_TRX_ID=GD.CUSTOMER_TRX_ID
AND'REC'=GD.ACCOUNT_CLASS
AND'Y'=GD.LATEST_REC_FLAG
ANDCT.RELATED_CUSTOMER_TRX_ID=CT_REL.CUSTOMER_TRX_ID(+)
ANDCT.BILL_TO_CUSTOMER_ID=RAC_BILL.CUSTOMER_ID
ANDCT.SHIP_TO_CUSTOMER_ID=RAC_SHIP.CUSTOMER_ID(+)
ANDCT.SOLD_TO_CUSTOMER_ID=RAC_SOLD.CUSTOMER_ID
ANDCT.PAYING_CUSTOMER_ID=RAC_PAYING.CUSTOMER_ID(+)
ANDCT.BILL_TO_SITE_USE_ID=SU_BILL.SITE_USE_ID

ANDCT.BATCH_SOURCE_ID=BS.BATCH_SOURCE_ID
ANDCT.BATCH_ID=RAB.BATCH_ID(+)
ANDCT.CUST_TRX_TYPE_ID=CTT.CUST_TRX_TYPE_ID
ANDCTT.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:
CREATEORREPLACEVIEWRA_CUSTOMER_TRX_PARTIAL_VAS
SELECTCT.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,

FROMRA_CUST_TRX_LINE_GL_DIST_ALLGD,
Page 42 of 68
Company Confidential - For Internal Use Only

RA_CUSTOMER_TRXCT,

RA_SITE_USES_ALLSU_BILL,
RA_CUSTOMERSRAC_PAYING,
RA_CUSTOMERSRAC_BILL
RA_SITE_USES_ALLSU_BILL,
RA_SITE_USES_ALLSU_SHIP,
RA_SITE_USES_ALLSU_PAYING,

RA_ADDRESSES_ALLRAA_BILL,
RA_ADDRESSES_ALLRAA_SHIP,
RA_ADDRESSES_ALLRAA_REMIT,
RA_CONTACTSRACO_SHIP,
RA_CONTACTSRACO_BILL,
AP_BANK_ACCOUNTS_ALLAPBA,
AP_BANK_BRANCHESAPB,
AR_RECEIPT_METHODSARM,
AR_RECEIPT_CLASSESARC,
RA_BATCH_SOURCES_ALLBS,
RA_BATCHES_ALLRAB,
RA_CUST_TRX_TYPES_ALLCTT,

WHERECT.CUSTOMER_TRX_ID=GD.CUSTOMER_TRX_ID
AND'REC'=GD.ACCOUNT_CLASS
AND'Y'=GD.LATEST_REC_FLAG
ANDCT.RELATED_CUSTOMER_TRX_ID=CT_REL.CUSTOMER_TRX_ID(+)
ANDCT.BILL_TO_CUSTOMER_ID=RAC_BILL.CUSTOMER_ID
ANDCT.SHIP_TO_CUSTOMER_ID=RAC_SHIP.CUSTOMER_ID(+)
ANDCT.SOLD_TO_CUSTOMER_ID=RAC_SOLD.CUSTOMER_ID
ANDCT.PAYING_CUSTOMER_ID=RAC_PAYING.CUSTOMER_ID(+)
ANDCT.BILL_TO_SITE_USE_ID=SU_BILL.SITE_USE_ID

ANDCT.BATCH_SOURCE_ID=BS.BATCH_SOURCE_ID
ANDCT.ORG_ID=BS.ORG_ID
ANDCT.BATCH_ID=RAB.BATCH_ID(+)
ANDCT.CUST_TRX_TYPE_ID=CTT.CUST_TRX_TYPE_ID
ANDCT.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
CREATEORREPLACEVIEWAR_TAX_LINES_VAS
SELECTCTL_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,
...
FROMRA_CUSTOMER_TRX_LINESCTL_INV_LINE,
RA_CUSTOMER_TRX_LINESCTL_INV_TAX,
AR_VAT_TAXINV_VAT,
Page 43 of 68
Company Confidential - For Internal Use Only

RA_CUSTOMER_TRX_LINESCTL_LINE,
RA_CUSTOMER_TRX_LINESCTL_TAX,
AR_VAT_TAXVAT
WHERECTL_TAX.LINK_TO_CUST_TRX_LINE_ID=
CTL_LINE.CUSTOMER_TRX_LINE_ID
ANDCTL_TAX.LINE_TYPE='TAX'
ANDCTL_TAX.VAT_TAX_ID=VAT.VAT_TAX_ID(+)
ANDCTL_TAX.PREVIOUS_CUSTOMER_TRX_LINE_ID=
CTL_INV_TAX.CUSTOMER_TRX_LINE_ID(+)
ANDCTL_INV_TAX.LINK_TO_CUST_TRX_LINE_ID=
CTL_INV_LINE.CUSTOMER_TRX_LINE_ID(+)
ANDCTL_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):
CREATEORREPLACEVIEWAR_TAX_LINES_VAS
SELECTCTL_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
FROMRA_CUSTOMER_TRX_LINES_ALLCTL_INV_LINE,
RA_CUSTOMER_TRX_LINES_ALLCTL_INV_TAX,
AR_VAT_TAX_ALLINV_VAT,
RA_CUSTOMER_TRX_LINES_ALLCTL_LINE,
RA_CUSTOMER_TRX_LINESCTL_TAX,
AR_VAT_TAX_ALLVAT
WHERECTL_TAX.LINK_TO_CUST_TRX_LINE_ID=
CTL_LINE.CUSTOMER_TRX_LINE_ID
ANDCTL_TAX.LINE_TYPE='TAX'
ANDCTL_TAX.VAT_TAX_ID=VAT.VAT_TAX_ID(+)
ANDCTL_TAX.PREVIOUS_CUSTOMER_TRX_LINE_ID=
CTL_INV_TAX.CUSTOMER_TRX_LINE_ID(+)
ANDCTL_INV_TAX.LINK_TO_CUST_TRX_LINE_ID=
CTL_INV_LINE.CUSTOMER_TRX_LINE_ID(+)
ANDCTL_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.

Page 44 of 68
Company Confidential - For Internal Use Only

BEGIN
FND_ACCESS_CONTROL_UTIL.Add_Policy
( p_object_schema => '&&1', Appsusername
p_object_name => 'FINANCIALS_SYSTEM_PARAMETERS',
p_policy_name => 'ORG_SEC,
p_function_schema => '&&1', Appsusername
p_policy_function
p_statement_types
p_update_check =>
p_enable => TRUE,
p_static_policy =>

=> 'MO_GLOBAL.ORG_SECURITY',
=> 'SELECT, INSERT, UPDATE, DELETE',
TRUE,
FALSE);

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
PRODUCT_DEPENDENCY

80
80

Yes
Yes

VARCHAR2
VARCHAR2

XXXCHR
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

Page 45 of 68
Company Confidential - For Internal Use Only

Use the API provided by Shared Services to register the access enabled status.
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.
Page 46 of 68
Company Confidential - For Internal Use Only

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.
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:
selecthr.organization_idorg_id
,hr.nameoperating_unit
FROMhr_operating_unitshr
WHEREmo_global.check_access(hr.organization_id)=
Y
Record Group Column Specifications
Column Name
DataType
Length

OPERATING_UNIT
Char
240

ORG_ID
Number
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
Display Width
Return Item
Column Title

OPERATING_UNIT
1.5
<block name>.operating_unit
Operating Unit

ORG_ID
0
<block name>.org_id
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
MO_DEFAULT_ORG_ID
MO_DEFAULT_OU_NAME
MO_OU_COUNT

Datatype
Number
Char
Number

Maximum Length
15
240
15

Pre-Form trigger
DECLARE
l_default_org_idnumber;
l_default_ou_namevarchar2(240);
l_ou_countnumber;
BEGIN
...
mo_utils.get_default_ou(l_default_org_id,l_default_ou_name,
Page 47 of 68
Company Confidential - For Internal Use Only

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;

Canalsouseindirectreferenceasgivenbelow:
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_idisnotnulland:block.org_idis
nullTHEN
:block.org_id:=:parameter.mo_default_org_id);
:block.operating_unit:=:parameter.mo_default_ou_name;
Canusecopybuiltinasgivenbelow:
copy(parameter.mo_default_org_id,block.org_id);
copy(parameter.mo_default_ou_name,block.operating_unit);
ENDIF;
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_idISNOTNULL)THEN
Defaultingorg_idfromprofileoption
:block.org_id:=:parameter.mo_default_org_id;
:block.operating_unit:=:parameter.mo_default_ou_name;
Setpolicycontext
mo_global.set_policy_context('S,:block.org_id);
ELSE
mo_global.set_policy_context('M',null);
ENDIF;
IF:<yourblockname.org_id>isnotnull
IF:<blockname.org_id><>nvl(:<parameter.old_org_id>,99)THEN
Getthecacheforcurrentorg
ENDIF;
ELSE
Refreshthecache

ENDIF;
Page 48 of 68
Company Confidential - For Internal Use Only

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.

When-Validate-Item Trigger of Operating Unit field


IF(:<yourblockname.org_id>ISNOTNULL)THEN
IF:<blockname.org_id><>nvl(:<parameter.old_org_id>,99)THEN
mo_global.set_policy_context('S',:block.org_id);
Getthecacheforthecurrentorg
ENDIF;
ELSE:block.org_idisnull
mo_global.set_policy_context('M',null);
Refreshthecache
ENDIF;
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(:<yourblockname.org_id>ISNOTNULL)THEN
IF:<blockname.org_id><>nvl(:<parameter.old_org_id>,99)THEN
mo_global.set_policy_context('S',:block.org_id);
Getthecacheforthecurrentorg
ENDIF;
ELSE:block.org_idisnull,sosetthecontexttomultiple
mo_global.set_policy_context('M',null);
Refreshthecache
ENDIF;

Pre-Insert Trigger of Operating Unit field block


This trigger is needed only if your form allows multi record commit.
IF(:<yourblockname.org_id>ISNOTNULL)THEN
IF:<blockname.org_id><>nvl(:<parameter.old_org_id>,99)THEN
mo_global.set_policy_context('S',:block.org_id);
Getthecacheforthecurrentorg
ENDIF;
ELSE:block.org_idisnull,sosetthecontexttomultiple
mo_global.set_policy_context('M',null);
Refreshthecache
ENDIF;

Pre-Query Trigger of Operating Unit field block


BEGIN
IF:parameter.mo_ou_count=1THEN
mo_global.set_policy_context(S,:parameter.mo_default_org_id);
ELSE
mo_global.set_policy_context('M',null);
ENDIF;
OtherCode
END;
Page 49 of 68
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);
Getthecacheforthecurrentorg
-- 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(:<yourblockname.org_id>ISNOTNULL)THEN
IF:<blockname.org_id><>nvl(:<parameter.old_org_id>,99)THEN
mo_global.set_policy_context('S',:block.org_id);
Getthecacheforthecurrentorg
ENDIF;
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_grxx_mo_cache_utils.GlobalsRecord;
BEGIN

CheckifthedefaultOUisavailable.
Ifso,copydefaultOUtoformblock
IF:parameter.mo_default_org_idisnotnulland
:block.org_idisnullthen
:block.org_id=:parameter.mo_default_org_id;
:block.operating_unit:=:parameter.mo_default_ou_name;
ENDIF;

Checkiftheblockorgisset.Thencheckiftheoperating
unitavailableasdefaultisthesameastheoneavailablein
parameteroranonbasetableblock.Ifsame,thendonotcopy
Page 50 of 68
Company Confidential - For Internal Use Only

againfromcache.Thisensuresthatyoudonotrefreshthe
parameteroranonbasetableblockifyoucontinuetoenter
transactionsfortheorgwhichissameasthedefaultorg.
IF:<yourblockname.org_id>isnotnull
IF:<blockname.org_id><>nvl(:<parameter.old_org_id>,99)
THEN
GetthecurrentOrgattributesfromclientsidecache
l_gr:=xx_mo_local_cache.get_org_attributes(:<<yourblock
name>>.org_id);

Copyfromcachetoparameterblockornonbasetableblock
Youcanreplaceparameterblockshownherewithanon
basetableblock
: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;
/*<<Beginproductspecificassignments>>*/
Additionalassignments...
:parameter.<column1>:=l_gr.column1;
/*<<Endproductspecificassignments>>*/

Copytheblockorg_idtoparameter.old_org_id
:parameter.old_org_id:=<:blockname.org_id>;
ENDIF;
ELSE
Copynulltoparametercolumns
ENDIF;
PasstheORG_IDtoservercodetousetheservercacheforthe
currentorgfortherecordvalidations
GetBatchSourceHeaderDefaults
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_grxx_mo_cache_utils.GlobalsRecord;
BEGIN
CheckifthenewOperatingUnitselectedbytheuseristhesame
Page 51 of 68
Company Confidential - For Internal Use Only

astheoldOperatingUnitthatisavailableintheparameteror
anonbasetableblock.Ifsamethendonotcopyagainfrom
cache
IF:<blockname.org_id>isnotnullTHEN
IF:<blockname.org_id><>nvl(:<parameter.old_org_id>,99)THEN
GetthecurrentOrgattributesfromclientsidecache
l_gr:=xx_mo_local_cache.get_org_attributes(:<<yourblock
name>>.org_id);
Copyfromcachetoparameterblockoranonbasetableblock
Youcanreplaceparameterblockshownhere
withanynonbasetableblock
: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;
/*<<Beginproductspecificassignments>>*/
Additionalassignments...
:parameter.<column1>:=l_gr.column1;
/*<<Endproductspecificassignments>>*/
Copytheblockorg_idtoparameter.old_org_id
:parameter.old_org_id:=<:blockname.org_id>;
ENDIF;
ELSE
Copynulltoparametercolumns
ENDIF;
PasstheORG_IDtoservercodetousetheservercacheforthe
currentorgfortherecordvalidations
GetBatchSourceHeaderDefaults
arp_trx_defaults.get_header_defaults(param1,param2,
,:block.org_id);

Othercode

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_grxx_mo_cache_utils.GlobalsRecord;
BEGIN
Page 52 of 68
Company Confidential - For Internal Use Only

CheckifthenewOperatingUnitselectedbytheuseristhe
sameastheoldOperatingUnitthatisavailableinthe
parameterornonbasetableblock.Ifsamethendonotcopy
againfromcache
IF:<blockname.org_id>isnotnullTHEN
IF:<blockname.org_id><>nvl(:<parameter.old_org_id>,99)
THEN
GetthecurrentOrgattributesfromclientsidecache
l_gr:=xx_mo_local_cache.get_org_attributes(:<<yourblock
name>>.org_id);
Copyfromcachetoparameterblockornonbasetableblock
Youcanreplaceparameterblockshownhere
withanynonbasetableblock
: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;
/*<<Beginproductspecificassignments>>*/
Additionalassignments...
:parameter.<column1>:=l_gr.column1;
/*<<Endproductspecificassignments>>*/
Copytheblockorg_idtoparameter.old_org_id
:parameter.old_org_id:=<:blockname.org_id>;
ENDIF;
ENDIF;
PasstheORG_IDtoservercodetousetheservercacheforthe
currentorgfortherecordvalidations
GetBatchSourceHeaderDefaults
arp_trx_defaults.get_header_defaults(param1,param2,
,:block.org_id);

Othercode
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 records 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_grxx_mo_cache_utils.GlobalsRecord;
BEGIN
CheckifthenewOperatingUnitselectedbytheuseristhe
Page 53 of 68
Company Confidential - For Internal Use Only

sameastheoldOperatingUnitthatisavailableinthe
parameterornonbasetableblock.Ifsamethendonotcopy
againfromcache
IF:<blockname.org_id>isnotnullTHEN
IF:<blockname.org_id><>nvl(:<parameter.old_org_id>,99)
THEN
GetthecurrentOrgattributesfromclientsidecache
l_gr:=xx_mo_local_cache.get_org_attributes(:<<yourblock
name>>.org_id);
Copyfromcachetoparameterblockornonbasetableblock
Youcanreplaceparameterblockshownhere
withanynonbasetableblock
: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;
/*<<Beginproductspecificassignments>>*/
Additionalassignments...
:parameter.<column1>:=l_gr.column1;
/*<<Endproductspecificassignments>>*/
Copytheblockorg_idtoparameter.old_org_id
:parameter.old_org_id:=<:blockname.org_id>;
ENDIF;
ENDIF;
PasstheORG_IDtoservercodetousetheservercacheforthe
currentorgfortherecordvalidations
GetBatchSourceHeaderDefaults
arp_trx_defaults.get_header_defaults(param1,param2,
,:block.org_id);

Othercode
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_grxx_mo_cache_utils.GlobalsRecord;
BEGIN
CheckifthenewOperatingUnitselectedbytheuseristhe
sameastheoldOperatingUnitthatisavailableinthe
parameterornonbasetableblock.Ifsamethendonotcopy
againfromcache
IF:<blockname.org_id>isnotnullTHEN
IF:<blockname.org_id><>nvl(:<parameter.old_org_id>,99)
THEN
GetthecurrentOrgattributesfromclientsidecache
l_gr:=xx_mo_local_cache.get_org_attributes(:<<yourblock
name>>.org_id);
Page 54 of 68
Company Confidential - For Internal Use Only

Copyfromcachetoparameterblockornonbasetableblock
Youcanreplaceparameterblockshownhere
withanynonbasetableblock
: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;
/*<<Beginproductspecificassignments>>*/
Additionalassignments...
:parameter.<column1>:=l_gr.column1;
/*<<Endproductspecificassignments>>*/
Copytheblockorg_idtoparameter.old_org_id
:parameter.old_org_id:=<:blockname.org_id>;
ENDIF;
ENDIF;
PasstheORG_IDtoservercodetousetheservercacheforthe
currentorgfortherecordvalidations
GetBatchSourceHeaderDefaults
arp_trx_defaults.get_header_defaults(param1,param2,
,:block.org_id);

Othercode
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_grxx_mo_cache_utils.GlobalsRecord;
BEGIN
CheckifthenewOperatingUnitselectedbytheuseristhe
sameastheoldOperatingUnitthatisavailableinthe
parameterornonbasetableblock.Ifsamethendonotcopy
againfromcache
IF:<blockname.org_id>isnotnullTHEN
IF:<blockname.org_id><>nvl(:<parameter.old_org_id>,99)
THEN
GetthecurrentOrgattributesfromclientsidecache
l_gr:=xx_mo_local_cache.get_org_attributes(:<<yourblock
name>>.org_id);
Copyfromcachetoparameterblockornonbasetableblock
Youcanreplaceparameterblockshownhere
withanynonbasetableblock
: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;
/*<<Beginproductspecificassignments>>*/
Additionalassignments...
:parameter.<column1>:=l_gr.column1;
/*<<Endproductspecificassignments>>*/
Page 55 of 68
Company Confidential - For Internal Use Only

Copytheblockorg_idtoparameter.old_org_id
:parameter.old_org_id:=<:blockname.org_id>;
ENDIF;
ENDIF;
PasstheORG_IDtoservercodetousetheservercacheforthe
currentorgfortherecordvalidations
GetBatchSourceHeaderDefaults
arp_trx_defaults.get_header_defaults(param1,param2,
,:block.org_id);

Othercode
END;
Note: For forms like AR Receipt Workbench, ON_LOCK trigger may be needed as opposed to PreUpdate (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_grxx_mo_cache_utils.GlobalsRecord;
BEGIN
GetthecurrentOrgattributesfromclientsidecache
orgstoredintheparameter.old_org_id
l_gr:=
xx_mo_local_cache.get_org_attributes(:parameter.old_org_id);
Copyfromcachetoparameterblockornonbasetableblock
Youcanreplaceparameterblockshownhere
withanynonbasetableblock
: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;
/*<<Beginproductspecificassignments>>*/
Additionalassignments...
:parameter.<column1>:=l_gr.column1;
/*<<Endproductspecificassignments>>*/
ENDIF;
ENDIF;
PasstheORG_IDtoservercodetousetheservercacheforthe
currentorgfortherecordvalidations
GetBatchSourceHeaderDefaults
arp_trx_defaults.get_header_defaults(param1,param2,
,:block.org_id);

Othercode
END;

Page 56 of 68
Company Confidential - For Internal Use Only

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:
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

Page 57 of 68
Company Confidential - For Internal Use Only

Record groups using derive operating feature


select ci.cons_billing_number,
ci.customer_id,
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.
selectmax(tc.name)name,
lc.displayed_fieldtype,
tc.description
fromap_lookup_codeslc,
ap_tax_codestc
wherelc.lookup_type='TAXTYPE'
andtc.tax_type!='OFFSET'
andtc.tax_type!='AWT'
andlc.lookup_code=tc.tax_type
andnvl(tc.enabled_flag,'Y')='Y'
groupbytc.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:
selectmax(tc.name)name,
lc.displayed_fieldtype,
tc.description
fromap_lookup_codeslc,
ap_tax_codes_ALLtc
wherelc.lookup_type='TAXTYPE'
andtc.tax_type!='OFFSET'
andtc.tax_type!='AWT'
andlc.lookup_code=tc.tax_type
andnvl(tc.enabled_flag,'Y')='Y'
andtc.org_id=:<block_name.org_id>
groupbytc.name,lc.displayed_field,tc.description
Page 58 of 68
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.
Page 59 of 68
Company Confidential - For Internal Use Only

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
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
insertinto<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>
whereprimary_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 60 of 68
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
:<yourblockname>.operating_unit:=
fnd_access_control_util.get_org_name(:<yourblock
name>.org_id);
set_record_property(:system.trigger_record,:system.t
rigger_block,STATUS,QUERY_STATUS);
FND_ACCESS_CONTROL_UTIL.Get_Org_Name
FUNCTIONGet_Org_Name(p_org_idNUMBER)
RETURNVARCHAR2
IS
l_returnhr_all_organization_units_tl.name%TYPE;
BEGIN
SELECTname
INTOl_return
FROMhr_all_organization_units_tl
WHEREorganization_id=p_org_id
ANDlanguage=userenv('LANG');
IFSQL%NOTFOUND
THEN
l_return:=NULL;
ENDIF;
RETURNl_return;
ENDGet_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 61 of 68
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 62 of 68
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:<blockname.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
Field

Result

1
2

Any Value
Null

Null
Not Null

Non Null (Cannot identify


Operating Unit uniquely. E.g.
Vis%)
Not Null (Can identify
Operating Unit uniquely. E.g.
Vision Operations)
Not Null (No Operating Unit is
found matching with the

Not Null
Not Null

Execute the standard enter query


Display message Please enter Operating
Unit for Multi-Org case
Display message The system cannot
identify KFF structure. Please enter the
full Operating Unit name
Execute the standard enter query

Not Null

Execute the standard enter query

4
5

Page 63 of 68
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 blocks pre-query trigger. However, the pre-query trigger in the detail
block should have code to update the blocks chart of accounts ID and call to fnd_flex.event as given
below:
Block Level Pre-Query trigger
Page 64 of 68
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:<blockname.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 65 of 68
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:
SELECThr.organization_idorg_id
,hr.nameoperating_unit
FROMhr_operating_unitshr
WHEREmo_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 66 of 68
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 MultiOrg implementation.
multiple sets of books
A General Ledger concept for having separate entities for which chart of accounts, calendar, or
functional currency differs.

Page 67 of 68
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 68 of 68
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 69 of 68
Company Confidential - For Internal Use Only