Sei sulla pagina 1di 100

Design points BOPF

What
are
the

elements of the programming model?


The business objects are the basic units of the BOPF-based programming model. Business applications or
processes operate on certain business objects. A business object is represented as a hierarchical tree of
nodes. A single node includes a set of semantically related business object data and the corresponding
business logic. On the technical level, each node is implemented with a regular Dictionary table, where
each node instance corresponds to a single table entry (table rows). Nodes, attributes, and alternative keys
set up the data part of a business object. Again from a technical viewpoint, attributes form the columns of
the table. A node serves as an anchor point for connecting the business logic of the business object.

For each node, several types of entities can be defined to form the specific business logic part of a
business object. For migrations, conversions, See also the S 5 stage method Electrolux!

Node Entity is used ... Relevant for BODS?


to implement a service (operation or behavior) of a
Action business object. An action is explicitly triggered by a
service consumer, such as the user interface.
to provide functions that are automatically executed as
soon as certain trigger conditions are fulfilled. A
Determination
determination is triggered internally due to changes made
to the instance of a business object.
to either validate whether a specific action can be executed
on a specific node instance (action validations) or whether
Validation
a set of node instances is consistent (consistency
validations).
to search for business object instances that meet certain
Query search criteria.
to provide an authorization concept for BO nodes (for their
Authorization Check data and for the operations on their data).
to connect business object instances that are located on
Association different nodes.

Tools What kind of development environment is


available for BOPF?
Tool Support
Features Relevant
tcodes
SAP Internal These are internal SAP tools and utilities that provide developers with a
Design Time Tools comprehensive feature set for building BO-centered applications for SAP Business n
(BOBF) Suite.
This tool targets simple enhancement scenarios.

Customers can enhance SAP business objects and also create their own simple
business objects in the customer namespace. The creation of business objects,
nodes, actions, determinations, queries, or validations is extensively supported by
BO Builder (BOB) y
wizard-driven tools and utilities.
BO Builder is available through SAP GUI and is released for customers with SAP
Business Suite EHP5 upwards

.
This tool targets experienced BOPF developers.
It also provides SAP Business Suite customers with a comprehensive feature set for

BO Builder for building BO-centered applications so that they can use it for their own development
y
eXperts (BOBX) projects.

BO Builder for eXperts is available through SAP GUI and is released for customers
with SAP Business Suite EHP6 upward.
With SAP Business Suite EHP7 a new Eclipse-based client application
(Eclipse-Based)
will be provided as part of an ABAP Development Tools installation. This set of
ABAP
tools runs on top of the Eclipse-based ABAP IDE and is aimed at supporting all y
Development Tools
kinds of BO-centric use cases: it allows both the creation of new BOPF-applications
for BOPF
and the enhancement of existing ones.
BO Builder Test
Starting from development environment, you can always test the current range of
Environment y
functions of each business object (or enhancement).
(BOBT)
Debugging on the business object entities level enables you to speed up the
troubleshooting process:
BO-Specific
You can set breakpoints for entities such as actions, determinations, and validations. y
Debugging
Furthermore, you have the option of setting watchpoints for certain activities on the
nodes (for example, to update a particular node attribute).
Integrated BO Test BOPF supports test automation and test-driven development. From the outset, you y
Infrastructure can apply ABAP unit tests to test isolated units of business objects, such as actions,
validations, or determinations. In addition, with the scenario testing function, you
can extend the tests to processes that consist of several steps.

From the beginning, we started of to use a technical


toolset provided by SAP. It consists of ABAP-based
frameworks which are also the foundation for SAP
Transportation Management (TM): -BOPF (for the
“classical backend”),
-FPM (for the user interfaces) and
integration layer between them which is part of the
BOPF reusables:
-FBI, the Floorplan Manager BOPF Integration.
The first thing we‘ll do is to translate the input from a functional specification to entities in our system. Specifications
usually include a domain model, so design starts with creation of technical representations of them: Classes,
structures, database tables. With BOPF, the business objects processing framework, this is a bit different: After having
verified the domain model, the entities can be modeled as so-called Business Objects with almost no transformation
in a SAP GUI-based transaction. • Outside-In-Approach => low representational gap. A domain class becomes a
socalled “Business Object Node” • Representation of the domain models in the system – without designing
ABAPclasses and without the need of writing a ”design” it in a text-editor. You can export the models to MS-Word in
case you need to publish a design document. • The necessary runtime-artifacts can be generated from the modeling
tool or are implemented generically by BOPF.
In addition to the well-known CRUD, BOPF also provides other core-services Most important:
• Read (“retrieve” in BOPF language) also includes „retrieve by association“ which allows to address related entities
based on the model
• A dedicated service allows to read the so-called properties, which is a definition of visibility, changeability or
execute-ability for various entities. This way, the logic which changes the usability of these entities can be
implemented in the business objects and this way de-couples the user interface
• Query allows to identify a set of instances based on modeled criteria All those services are provided without having
written a single line of code.  „Out of the box“ the developer can operate on the business objects once they have
been modeled with full-fledged support for transactional behavior.

NOTE
As a SAP customer, you can use the BO Builder (BOB), the BO Builder for eXperts (BOBX), or the Eclipse-
based client for the development of your own business objects. Here, you will benefit from
that interoperability is ensured between these development tools. So you can, for example, create
new business objects in the BOB tool and then edit them in another tool on.

How to get started?


•Getting Started with BOPF – guides you through all the steps required to create from scratch your
first business object and to implement a basic operation using the BOPF API.
•Read the related blogs posts on SCN:

•Navigating the BOPF: Part 2 - Business Object Overview


•Navigating the BOPF: Part 3 - Working with the BOPF API
•Navigating the BOPF: Part 4 - Advanced BOPF API Features
•Navigating the BOPF: Part 5 - Enhancement Techniques
•Navigating the BOPF: Part 6 - Testing & UI Integration
•TechEd 2013: Agile custom development with BOPF

1. Navigating the BOPF: Part 1 – Getting Started


January 4, 2013 | 3,091 Views |

James Wood

more by this author


ABAP Development
abapbopffloorplan managerWeb Dynproweb dynpro abap

Last year, I began working on a project which was rolling out the new SAP Environment Health and Safety Management (EHSM)

module. This module utilizes some of the more cutting edge technologies in the ABAP space: Floorplan Manager, Web Dynpro ABAP,

Adobe Interactive Forms, and so on. In addition to these standard technologies, EHSM also utilizes a technology framework that I had

not encountered previously: the Business Object Processing Framework (or BOPF).

Whenever I started investigating the BOPF, the first place I went to look was naturally right here at the SDN. However, between SDN

and Google, I found very little information to go on when it comes to working with the BOPF. Indeed, about the only useful

documentation I found was an enhancement guide entitled BOPF Enhancement Workbench. What I was really looking for though was

an in-depth description of the architecture of the BOPF, its API, and most importantly, some examples demonstrating its usage. Short of

that, I was left to muddle my way through much of the SAP standard code until I began to understand how the different pieces fit

together.

After working with the technology for the better part of a year, I thought I would launch a blog series documenting my findings for

others who share a similar plight. This includes other EHSM developers as well as developers working in other new dimension modules

such as SAP Transportation Management (TM), etc. I hope you will find it useful.

What is the BOPF?


As the name suggests, the BOPF provides a framework for working with business objects (BOs). This framework provides tools and

services which span the entire BO lifecycle:

• Design Time

• At design time, BOs are modeled using the BOPF Workbench tool (Transaction /BOBF/CONF_UI). This tool makes it possible to
model a BO’s nodes/attributes, behaviors, associations, and so on. If you’re comfortable with OOP concepts, then this will seem

vaguely familiar to modeling classes in the Class Builder tool. (Note: So far, it seems that this tool is locked down for customer use.

This implies that we cannot create new BOs of our own…yet. As per Thea Hillenbrand’s comments below, the BOPF has been opened
up for general customer use. This happened with SAP Business Suite EHP5 SP11 and SAP Business Suite EHP6 SP05. The related note

is 1760610. Thanks Thea!)

• Behind the scenes, the BO metadata is stored in such a way that it can be introspected and leveraged by runtime APIs.

• Customers can enhance existing BOs using the BOPF Enhancement Workbench tool (Transaction /BOBF/CUST_UI). Here, we have

the option of defining new nodes and attributes, defining additional behaviors, and so on. We’ll see how this works up close in an

upcoming blog entry.

Runtime
• At runtime, BOs are instantiated and controlled via a standard API defined using ABAP Objects classes.

• Transactions are managed by a central transaction manager class.

• Service-level interactions are brokered via a standard service manager class.

• To some extent, much of this will feel similar to ABAP Object Services. However, as you’ll soon see, the BOPF affords us a lot more

power.

The figure below illustrates how these different pieces fit together within an application. As you can see, the BOPF architecture utilizes

a layered approach:

Consumer Layer
• At the consumer layer, we can utilize the BOPF API methods to create new BOs, search for existing BOs, update selected BOs, and so

on.

• Frequently, BOPF BOs will be consumed by UI applications such as WDA applications, etc.

• Of course, that’s not to say that generic consumers cannot get in on the fun as well.

Transaction Layer
• Interactions with BOs within the BOPF are brokered through a centralized transaction layer which handles low-level transaction

handling details such as object locking, etc.

• From the perspective of the consumer layer, interactions with the transaction layer consist of little more than a handful of intuitive API

calls.

BOPF Runtime Layer


• The core of the BOPF functionality lies within the BOPF runtime. This layer contains all of the functionality required to instantiate

BOs, trigger their functionality, and so on.

• As you can see in the figure, the BOPF runtime utilizes the BOPF model definitions created at design time as metadata for instantiating

BO instances, navigating BO associations, etc.

Persistence Layer
• One of the nice things about the BOPF is that it is rather flexible at the persistence layer. Though the end goal is normally to store BO

data within the database, the framework also supports data buffering via shared memory as well as the definition of transient nodes and

attributes that are loaded on demand.


Though the BOPF shares certain similarities to previous business object models (e.g. business objects defined in Transaction SWO1,

GENIL, and BOL), it is quite a bit more evolved than any prior business object model defined by SAP. This will become obvious as we

delve into specific topics in the upcoming blog entries.

Why do we need the BOPF?


Whenever a new development framework comes out, it is only natural for developers to wonder if the framework is truly needed.

Though I will not endeavor to sell anyone on the merits of the BOPF within this blog series, I think it is useful to compare and contrast

the scope of a development project with and without the BOPF. Then, you can decide for yourself if the BOPF provides value.

To put this in perspective, let’s imagine that we’re tasked with developing a new module around some custom-defined business entity. A

minimal bill of materials in terms of development objects for this module are as follows:

• The entity data will be stored within a series of ABAP Dictionary tables. If desired, we can use ABAP Object Services to create an

ORM wrapper around these table accesses.

• In order to synchronize access to the entity data, we’ll need to define one or more lock objects. Plus, we’ll need a mechanism to ensure

that the lock objects are used to control access to the data.

• To secure access to the entity data, we must create one or more authorization objects so that we can define granular authorization

control.

Now, in an an ideal world, we would attempt to encapsulate access to the entity data by creating a series of ABAP Object classes (e.g.

entity classes and the like). These classes would offer basic CRUD (Create, Remove, Update, and Display) operations that provide

users with a one-stop shop for accessing and updating the entity data.

With these basic components in place, we can then build the core application functionality.
Here, we’ll find a number of producers/consumers of the entity data:
• UI applications based on WDA, BSP, or even classic Dynpro technology.

• BI extractors used to export the data for reporting purposes

• SAP Business Workflow processes

• Custom accessor modules used to supply data to home grown reports (e.g. ALV), Adobe Interactive Forms, and so on

• Interface and conversion programs (or, in SOA initiatives, Web service wrappers)
• Others as needed

Overall, these kinds of development objects are pretty standard fare for the average ABAP developer. However, within the context of

your typical SAP project, these tasks are often distributed across a large development team. Here, it can be difficult to enforce best

practices and ensure that each developer accesses the entity data properly. Frequently, this is as a result of developers not understanding

how to access the data via a custom API (provided there is one). In short, there’s no overarching object model which ensures that

business objects are accessed consistently.

It is here that the BOPF shines. Within the BOPF, everything has its place. Business data is modeled consistently in BO nodes and

attributes. Behaviors are defined as actions. Validations are performed automatically via validation modules. Triggers can be defined

using determinations. The relationships between BOs is defined statically via associations. Once a developer becomes comfortable with

the framework, the BOPF takes all of the guessing out of business object development. The BO encapsulates all of the functionality and

provides consistent access to all of the producers/consumers outlined above.

In time, this sort of consistency can give rise to additional frameworks which sit on top of the BOPF. An example of this is

the Floorplan Manager BOPF Integration (FBI) framework which simplifies the way that FPM feeder classes access BOPF nodes.

Much of this will become clearer as we move on. For now, suffice it so say that the BOPF provides us with a tremendous springboard

for developing business objects.

Thea Hillenbrand The related note is 1760610.


Hi James,

Congratulations on this excellent series on BOPF. We feel a bit ashamed not having delivered these informations by our own.

BOPF was an internal tool to ease and govern our application development. Last year we saw an increasing interest from partners and

customers and we decided to open it also for customer usage. Not the /BOPF/CONF_UI transaction – good enough for internal usage –

but the enriched enhancement workbench which we renamed to Business Object Builder (transaction BOB). This transaction can be

used to create new BOs and enhance existing ones.

This happend with SAP Business Suite EHP5 SP11 and SAP Business Suite EHP6 SP05. The related note is 1760610.

Regards Thea

In my previous blog post, I briefly introduced the BOPF framework and its positioning within the ABAP

development landscape. With that information in tow, we’re now ready to begin peeling back the layers of

the BOPF and seeing how all the pieces fit together from a technical perspective. In this blog post, we’ll get

things started by taking a look at the design time aspects of business objects.

2. Business Objects Overview - 2


According to SAP’s BOPF Enhancement Workbench documentation, business objects within the BOPF are “a

representation of a type of uniquely identifiable business entity described by a structural model and an

internal process model.” This is to say that BOPF business objects:

•Have a well-defined component model.

•Have a well-defined process model which governs the business object lifecycle, behaviors, etc.
•Execute within a container-like environment which handles low-level tasks such as caching, transaction

management, and so on.

In this regard, BOs in the BOPF are not unlike objects developed in other component architectures (e.g. EJBs

in Java, Microsoft COM+, etc.).

Anatomy of a Business Object


From a modeling perspective, BOs are made up of several different types of entities:

Nodes
•Nodes are used to model a BO’s data.

•Nodes are arranged hierarchically to model the various dimensions of the BO data. This hierarchy is

organized underneath a single root node (much like XML). From there, the hierarchy can be nested

arbitrarily deep depending upon business requirements.

•There are several different node types supported by the BOPF. However, most of the time you’ll find
yourself working with persistent nodes (e.g. nodes which are backed by the database). It is also possible to

define transient nodes whose contents are loaded on demand at runtime. These types of nodes can come in

handy whenever we want to bridge some alternative persistence model (e.g. data obtained via service

calls).

•Each node consists of one or more attributes which describe the type of data stored within the node:

•Attributes come in two distinct varieties: persistent attributes and transient attributes. Persistent attributes
represent those attributes that will be persisted whenever the BO is saved. Transient attributes are volatile

attributes which are loaded on demand.

•A node’s attributes are defined in terms of structure definitions from the ABAP Dictionary.

•At runtime, a BO node is like a container which may have zero, one, or many rows. If you’re familiar with

the concept of controller contexts with the Web Dynpro programming model, then this concept should feel

familiar to you. If not, don’t worry; we’ll demonstrate how this works whenever we look at the BOPF API.

Actions
•Actions define the services (or behavior) of a BO.

•Actions are assigned to individual nodes within a BO.

•The functionality provided by an action is (usually) defined in terms of an ABAP Objects class that
implements the /BOBF/IF_FRW_ACTION interface.
•To some extent, it is appropriate to think of actions as being similar to the methods of an ABAP Objects

class.
Associations
•Though BOs are designed to be self-contained, autonomous entities, they do not have to exist in isolation.

With associations, we can define a direct and unidirectional relationship from one BO to another.

•For example, in just a moment, we’ll take a look at a sample BO called /BOBF/DEMO_SALES_ORDER which is
used to model sales orders. Here, we’ll see how the product assignments for sales order items is defined in
terms of an association with a product BO called /BOBF/DEMO_PRODUCT. This composition technique makes it
possible to not only leverage the product BOs data model, but also its behaviors, etc.
•Associations allow us to integrate BOs together in complex assemblies à la Legos™.

Determinations= bus.logic, trigger


•According to the aforementioned BOPF enhancement guide, a determination “is an element assigned

to a business object node that describes internal changing business logic on the business

object”.

•In some respects, determinations are analogous to database triggers. In other words, they are functions
that are triggered whenever certain triggering conditions are fulfilled. These conditions are described in

terms of a series of patterns:

•“Derive dependent data immediately after modification” AIP

•This pattern allows us to react to changes made to a given BO node. For example, we might use this event

to go clean up some related data.

•“Derive dependent data before saving” BIP

•This pattern allows us to hang some custom logic on a given BO node before it is saved. This could be as

simple as using a number range object to assign an ID value to a node attribute or as complex as triggering

an interface.

•“Fill transient attributes of persistent nodes”

•This pattern is often used in conjunction with UI development. Here, we might want to load labels and

descriptive texts into a series of transient attributes to be displayed on the screen.

•Note: This determination can be bypassed via the API if the lookup process introduces unnecessary

overhead.

•“Derive instances of transient nodes”

•This pattern allows us to load transient nodes into memory on demand. Here, for example, we might

lookup real-time status data from a Web service and load it into the attributes of a transient node from

downstream consumption.

•Determination patterns are described in detail within the aforementioned BOPF enhancement guide.

•The logic within a determination is defined via an ABAP Objects class that implements
the /BOBF/IF_FRW_DETERMINATION interface.
Validations
•According to the BOPF enhancement guide, validations are “an element of a business object node that

describes some internal checking business logic on the business object”.

•Validations come in two distinct forms:

•Action Validations

•Action validations are used to determine whether or not a particular action can be executed against a BO

node.

•Consistency Validations

•As the name suggests, consistency validations are used to ensure that a BO node is consistent. Such

validations are called at pre-defined points within the BOPF BO transaction cycle to ensure that BO nodes

are persisted in a consistent state.

•The validation logic is encapsulated within an ABAP Objects class that implements
the /BOBF/IF_FRW_VALIDATION interface.

Queries
•Queries are BO node entities which allow us to search for BOs using various types of search

criteria.

•Queries make it possible for consumers to access BOs without knowing the BO key up front.

•Queries also integrate quite nicely with search frameworks and the like.

•Queries come in two varieties:

• Node Attribute Queries

•Node attribute queries are modeled queries whose logic is defined within the BOPF runtime. These simple

queries can be used whenever you simply need to search for BO nodes by their attributes (e.g. ID =

‘12345’).

• Custom Queries

•Custom queries allow you define your own query logic by plugging in an ABAP Objects class that
implements the /BOBF/IF_FRW_QUERY interface.
The figure below illustrates how all of these entities fit together within a BO node definition. Here, I’ve
pulled up a BO called /BOBF/DEMO_SALES_ORDER in Transaction /BOBF/CONF_UI. Here, the BO metadata is
organized into several different panels:
•On the top left-hand side of the screen, you can see the BO’s node structure. Here, you can see that the

node structure is organized underneath a top-level ROOT node which models sales order header data.

Underneath this node are several child nodes which model sales order items, customer assignment, and

texts. The ITEM node in turn encompasses its own child nodes to model item-level data.

•On the bottom left-hand side of the screen, we can browse through the node collection of a BO and view

the entity assignments of a given node. As you can see in the figure, each node contains folders which

organize assigned actions, validations, and so on.


•In the middle of the screen, we can view additional details about a selected node by double-clicking on a
node within the Node Structure panel on the left-hand side of the screen. Here, we can look at a node’s data

model, implementation classes, and so on.

tcodes
BOB

BOPF config

BOBT testing

BOBX config
We’ll have an opportunity to get a little more hands on with these entities in upcoming blog entries. For

now, our focus is on grasping how pieces fit together and where to go to find the information we need to get

started with a BO.

Next Steps
At this point, you should have a decent feel for how BOs are modeled at design time. In my next blog, we’ll

shift gears and begin manipulating BOs using the provided BOPF APIs. This will help put all of these entities

into perspective.
Queries
are often misused at runtime, therefore just some small addition:Oliver Jaegle
It’s (almost) true that Queries identify a set of instance of the node at which they are modeled, but: (as per contract) they only return

persisted data!

As during transactional processing it can happen, that instances which shall be found have just been created during the session, queries

should not be used while implementing transactional entities (such as determination, actions, validations).

If a collection of instances has to be identified which can’t be resolved via associations, alternative keys allow a translation of

semantical attributes to technical keys.

Now you are wondering how a key can translate to a collection of keys? The answer is in the non-uniqueness of the key. E. g. a

purchasing group can be thought of identifying a set of purchasing documents. It thus acts as a non-unique alternative key.

Cheers,

Oliver

P.s.:Excellent post, James! It’s quite tricky to publish good non-redundant-content about BOPF after your series �

I have some difficulties regarding the following statements, could you clarifiy these a bit?

a set of instance of the node at which they are modeled

implementing transactional entities (such as determination, actions, validations).

Thanks for mentioning the non-unique alternative key to build up groups. So is it recommended to create groups by using the same

GUID for an alternative group-key? or should the alt-key strictly be semantical in contrast to a purely technical GUID?

Cheers, Kai

queries return a set of node rows for the node they’re defined against.
1.James Wood Post author
Hi Kai,

Regarding the first statement, what he’s saying is that queries return a set of node rows for the node

they’re defined against. Here, you have to distinguish between the design time view of the BO model vs.

the runtime view. At runtime, you can think of nodes as being rather like internal tables/containers which

contain 0..n node rows. Queries fetch the node rows which match up with whatever query selection criteria

specified.

The second point refers to the fact that queries are always performed against persistent data (i.e. node

rows that have been persisted to the system database). With that in mind, he’s saying that you wouldn’t

want to use a query to fetch the current state of a node within a transactional entity such as a

determination or action. Why? Because there may be node rows existing in memory that wouldn’t be

accounted for in the query selection. For these kinds of tasks, we must use the BOPF API to fetch the

complete set of node rows (in memory + persisted) and then filter the result set as needed based on our

business requirements.

Does that help? Thanks.


3. Navigating the BOPF: Part 3 – Working with the BOPF API
January 16, 2013 | 6,105 Views |

ABAP Development
abapbopffloorplan managerweb dynpro abap
w

2.In my previous blog post, we explored the anatomy of business objects within the BOPF. There, we were
able to observe the various entities that make up a BO: nodes/attributes, actions, associations,
determinations, validations, and queries. Now that you have a feel for what these entities are, we’re ready
to begin taking a look at the API that is used to manipulate these entities. To guide us through this
demonstration, we’ll explore the construction of a simple ABAP report program used to perform CRUD
operations on a sample BOPF BO shipped by default by SAP: /BOBF/DEMO_CUSTOMER. You can download the
complete example program source code here.
Note: The code bundle described above has been enhanced as of 9/18/2013. The code was reworked to factor out a BOPF utilities class

of sorts and also demonstrate how to traverse over to dependent objects (DOs).

BOPF API Overview


Before we begin coding with the BOPF API, let’s first take a look at its basic structure. The UML class diagram below highlights some

of the main classes that make up the BOPF API. At the end of the day, there are three main objects that we’ll be working with to

perform most of the operations within the BOPF:

• /BOBF/IF_TRA_TRANSACTION_MGR
• This object reference provides a transaction manager which can be used to manage transactional changes. Such transactions could

contain a single step (e.g. update node X) or be strung out across multiple steps (add a node, call an action, and so on).

• /BOBF/IF_TRA_SERVICE_MANAGER
• The service manager object reference provides us with the methods we need to lookup BO nodes, update BO nodes, trigger validations,

perform actions, and so on.

• /BOBF/IF_FRW_CONFIGURATION
• This object reference provides us with metadata for a particular BO. We’ll explore the utility of having access to this metadata coming

up shortly.
In the upcoming sections, I’ll show you how these various API classes collaborate in typical BOPF use cases. Along the way, we’ll
encounter other useful classes that can be used to perform specific tasks. You can find a complete class listing within
package /BOBF/MAIN.
Note: As you’ll soon see, the BOPF API is extremely generic in nature. While this provides tremendous flexibility, it also adds a certain

amount of tedium to common tasks. Thus, in many applications, you may find that SAP has elected to wrap the API up in another API

that is more convenient to work with. For example, in the SAP EHSM solution, SAP defines an “Easy Node Access” API which

simplfies the way that developers traverse BO nodes, perform updates, and so on.

Case Study: Building a Simple Report Program to Manipulate Customer Objects

To demonstrate the BOPF API, we’ll build a custom report program which performs basic CRUD operations on a sample BO provided
by SAP: /BOBF/DEMO_CUSTOMER. The figure below shows the makeup of this BO in Transaction /BOBF/CONF_UI.

Our sample program provides a basic UI as shown below. Here, users have the option of creating, changing, and displaying a particular

customer using its ID number. Sort of a simplified Transaction XK01-XK03 if you will.
Getting Started

To drive the application functionality, we’ll create a local test driver class called LCL_DEMO. As you can see in the code excerpt below,
this test driver class loads the core BOPF API objects at setup whenever the CONSTRUCTOR method is invoked. Here, the factory
classes illustrated in the UML class diagram shown in the previous section are used to load the various object references.
CLASS lcl_demo DEFINITION CREATE PRIVATE.
PRIVATE SECTION.
DATA mo_txn_mngr TYPE REF TO /bobf/if_tra_transaction_mgr.
DATA mo_svc_mngr TYPE REF TO /bobf/if_tra_service_manager.
DATA mo_bo_conf TYPE REF TO /bobf/if_frw_configuration.
METHODS:
constructor RAISING /bobf/cx_frw.
ENDCLASS.

CLASS lcl_demo IMPLEMENTATION.


METHOD constructor.
"Obtain a reference to the BOPF transaction manager:
me->mo_txn_mngr =
/bobf/cl_tra_trans_mgr_factory=>get_transaction_manager( ).

"Obtain a reference to the BOPF service manager:


me->mo_svc_mngr =
/bobf/cl_tra_serv_mgr_factory=>get_service_manager(
/bobf/if_demo_customer_c=>sc_bo_key ).
"Access the metadata for the /BOBF/DEMO_CUSTOMER BO:

me->mo_bo_conf =

/bobf/cl_frw_factory=>get_configuration(

/bobf/if_demo_customer_c=>sc_bo_key ).

ENDMETHOD. " METHOD constructor

ENDCLASS.

For the most part, this should seem fairly straightforward. However, you might be wondering where I came up with
the IV_BO_KEY parameter in the GET_SERVICE_MANAGER() and GET_CONFIGURATION()factory method calls. This value is
provided to us via the BO’s constants interface (/BOBF/IF_DEMO_CUSTOMER_C in this case) which can be found within the BO
configuration in Transaction /BOBF/CONF_UI (see below). This auto-generated constants interface provides us with a convenient
mechanism for addressing a BO’s key, its defined nodes, associations, queries, and so on. We’ll end up using this interface quite a bit
during the course of our development.
Creating New Customers

Once we have the basic framework in place, we are ready to commence with the development of the various CRUD operations that
our application will support. To get things started, we’ll take a look at the creation of a new customer instance. For the most part, this
involves little more than a call to the MODIFY() method of the /BOBF/IF_TRA_SERVICE_MANAGER object reference. Of course, as
you can see in the code excerpt below, there is a fair amount of setup that we must do before we can call this method.
CLASS lcl_demo DEFINITION CREATE PRIVATE.
PUBLIC SECTION.
CLASS-METHODS:
create_customer IMPORTING iv_customer_id
TYPE /bobf/demo_customer_id.
...
ENDCLASS.
CLASS lcl_demo IMPLEMENTATION.
METHOD create_customer.
"Method-Local Data Declarations:
DATA lo_driver TYPE REF TO lcl_demo.
DATA lt_mod TYPE /bobf/t_frw_modification.
DATA lo_change TYPE REF TO /bobf/if_tra_change.
DATA lo_message TYPE REF TO /bobf/if_frw_message.
DATA lv_rejected TYPE boole_d.
DATA lx_bopf_ex TYPE REF TO /bobf/cx_frw.
DATA lv_err_msg TYPE string.

DATA lr_s_root TYPE REF TO /bobf/s_demo_customer_hdr_k.


DATA lr_s_txt TYPE REF TO /bobf/s_demo_short_text_k.
DATA lr_s_txt_hdr TYPE REF TO /bobf/s_demo_longtext_hdr_k.
DATA lr_s_txt_cont TYPE REF TO /bobf/s_demo_longtext_item_k.

FIELD-SYMBOLS:
<ls_mod> LIKE LINE OF lt_mod.

"Use the BOPF API to create a new customer record:


TRY.
"Instantiate the driver class:
CREATE OBJECT lo_driver.

"Build the ROOT node:


CREATE DATA lr_s_root.
lr_s_root->key = /bobf/cl_frw_factory=>get_new_key( ).
lr_s_root->customer_id = iv_customer_id.
lr_s_root->sales_org = 'AMER'.
lr_s_root->cust_curr = 'USD'.
lr_s_root->address_contry = 'US'.
lr_s_root->address = '1234 Any Street'.
APPEND INITIAL LINE TO lt_mod ASSIGNING <ls_mod>.
<ls_mod>-node = /bobf/if_demo_customer_c=>sc_node-root.
<ls_mod>-change_mode = /bobf/if_frw_c=>sc_modify_create.
<ls_mod>-key = lr_s_root->key.
<ls_mod>-data = lr_s_root.

"Build the ROOT_TEXT node:


CREATE DATA lr_s_txt.
lr_s_txt->key = /bobf/cl_frw_factory=>get_new_key( ).
lr_s_txt->text = 'Sample Customer Record'.
lr_s_txt->language = sy-langu.

APPEND INITIAL LINE TO lt_mod ASSIGNING <ls_mod>.


<ls_mod>-node = /bobf/if_demo_customer_c=>sc_node-root_text.
<ls_mod>-change_mode = /bobf/if_frw_c=>sc_modify_create.
<ls_mod>-source_node = /bobf/if_demo_customer_c=>sc_node-root.
<ls_mod>-association =
/bobf/if_demo_customer_c=>sc_association-root-root_text.
<ls_mod>-source_key = lr_s_root->key.
<ls_mod>-key = lr_s_txt->key.
<ls_mod>-data = lr_s_txt.

"Build the ROOT_LONG_TEXT node:


"If you look at the node type for this node, you'll notice that
"it's a "Delegated Node". In other words, it is defined in terms
"of the /BOBF/DEMO_TEXT_COLLECTION business object. The following
"code accounts for this indirection.
CREATE DATA lr_s_txt_hdr.
lr_s_txt_hdr->key = /bobf/cl_frw_factory=>get_new_key( ).

APPEND INITIAL LINE TO lt_mod ASSIGNING <ls_mod>.


<ls_mod>-node = /bobf/if_demo_customer_c=>sc_node-root_long_text.
<ls_mod>-change_mode = /bobf/if_frw_c=>sc_modify_create.
<ls_mod>-source_node = /bobf/if_demo_customer_c=>sc_node-root.
<ls_mod>-association =
/bobf/if_demo_customer_c=>sc_association-root-root_long_text.
<ls_mod>-source_key = lr_s_root->key.
<ls_mod>-key = lr_s_txt_hdr->key.
<ls_mod>-data = lr_s_txt_hdr.

"Create the CONTENT node:


CREATE DATA lr_s_txt_cont.
lr_s_txt_cont->key = /bobf/cl_frw_factory=>get_new_key( ).
lr_s_txt_cont->language = sy-langu.
lr_s_txt_cont->text_type = 'MEMO'.
lr_s_txt_cont->text_content = 'Demo customer created via BOPF API.'.

APPEND INITIAL LINE TO lt_mod ASSIGNING <ls_mod>.


<ls_mod>-node =
lo_driver->mo_bo_conf->query_node(
iv_proxy_node_name = 'ROOT_LONG_TXT.CONTENT' ).
<ls_mod>-change_mode = /bobf/if_frw_c=>sc_modify_create.
<ls_mod>-source_node = /bobf/if_demo_customer_c=>sc_node-root_long_text.
<ls_mod>-source_key = lr_s_txt_hdr->key.
<ls_mod>-key = lr_s_txt_cont->key.
<ls_mod>-data = lr_s_txt_cont.
<ls_mod>-association =
lo_driver->mo_bo_conf->query_assoc(
iv_node_key = /bobf/if_demo_customer_c=>sc_node-root_long_text
iv_assoc_name = 'CONTENT' ).

"Create the customer record:


CALL METHOD lo_driver->mo_svc_mngr->modify
EXPORTING
it_modification = lt_mod
IMPORTING
eo_change = lo_change
eo_message = lo_message.

"Check for errors:


IF lo_message IS BOUND.
IF lo_message->check( ) EQ abap_true.
lo_driver->display_messages( lo_message ).
RETURN.
ENDIF.
ENDIF.

"Apply the transactional changes:


CALL METHOD lo_driver->mo_txn_mngr->save
IMPORTING
eo_message = lo_message
ev_rejected = lv_rejected.

IF lv_rejected EQ abap_true.
lo_driver->display_messages( lo_message ).
RETURN.
ENDIF.
"If we get to here, then the operation was successful:
WRITE: / 'Customer', iv_customer_id, 'created successfully.'.
CATCH /bobf/cx_frw INTO lx_bopf_ex.
lv_err_msg = lx_bopf_ex->get_text( ).
WRITE: / lv_err_msg.
ENDTRY.
ENDMETHOD. " METHOD create_customer
ENDCLASS.
As you can see in the code excerpt above, the majority of the code is devoted to building a table which is passed in
the IT_MODIFICATION parameter of the MODIFY() method. Here, a separate record is created for each node row that is being
modified (or inserted in this case). This record contains information such as the node object key (NODE), the edit mode
(CHANGE_MODE), the row key (KEY) which is an auto-generated GUID, association/parent key information, and of course, the actual
data (DATA). If you’ve ever worked with ALE IDocs, then this will probably feel vaguely familiar.
Looking more closely at the population of the node row data, you can see that we’re working with data references which are created
dynamically using the CREATE DATA statement. This indirection is necessary since the BOPF API is generic in nature. You can find the
structure definitions for each node by double-clicking on the node in Transaction /BOBF/CONF_UI and looking at the Combined
Structure field (see below).

Once the modification table is filled out, we can call the MODIFY() method to insert the record(s). Assuming all is successful, we can
then commit the transaction by calling the SAVE() method on the /BOBF/IF_TRA_TRANSACTION_MANAGER instance. Should any
errors occur, we can display the error messages using methods of the /BOBF/IF_FRW_MESSAGE object reference which is returned
from both methods. This is evidenced by the simple utility method DISPLAY_MESSAGES() shown below. That’s pretty much all there
is to it.
CLASS lcl_demo DEFINITION CREATE PRIVATE.
PRIVATE SECTION.
METHODS:
display_messages IMPORTING io_message
TYPE REF TO /bobf/if_frw_message.
ENDCLASS.

CLASS lcl_demo IMPLEMENTATION.


METHOD display_messages.
"Method-Local Data Declarations:
DATA lt_messages TYPE /bobf/t_frw_message_k.
DATA lv_msg_text TYPE string.
FIELD-SYMBOLS <ls_message> LIKE LINE OF lt_messages.

"Sanity check:
CHECK io_message IS BOUND.

"Output each of the messages in the collection:


io_message->get_messages( IMPORTING et_message = lt_messages ).
LOOP AT lt_messages ASSIGNING <ls_message>.
lv_msg_text = <ls_message>-message->get_text( ).
WRITE: / lv_msg_text.
ENDLOOP.
ENDMETHOD. " METHOD display_messages
ENDCLASS.
Performing Customer Queries

If you look closely at the customer creation code illustrated in the previous section, you can see that each node row is keyed by an auto-
generated GUID of type /BOBF/CONF_KEY (see below). Since most users don’t happen to have 32-character hex strings memorized,
we typically have to resort to queries if we want to find particular BO instances. For example, in our customer demo program, we want
to provide a way for users to lookup customers using their customer ID value. Of course, we could have just as easily defined an
alternative query selection to pull the customer records.
As we learned in the previous blog post, most BOs come with one or more queries which allow us to search for BOs according to
various node criteria. In the case of the /BOBF/DEMO_CUSTOMER business object, we want to use the SELECT_BY_ATTRIBUTES query
attached to the ROOT node (see below). This allows us to lookup customers by their ID value.
The code excerpt below shows how we defined our query in a method called GET_CUSTOMER_FOR_ID(). As you can see, the query is
executed by calling the aptly named QUERY() method of the /BOBF/IF_TRA_SERVICE_MANAGER instance. The query parameters are
provided in the form of an internal table of type /BOBF/T_FRW_QUERY_SELPARAM. This table type has a similar look and feel to a
range table or SELECT-OPTION. The results of the query are returned in a table of type /BOBF/T_FRW_KEY. This table contains the
keys of the node rows that matched the query parameters. In our sample case, there should be only one match, so we simply return the
first key in the list.
CLASS lcl_demo DEFINITION CREATE PRIVATE.
PRIVATE SECTION.
METHODS:
get_customer_for_id IMPORTING iv_customer_id
TYPE /bobf/demo_customer_id
RETURNING VALUE(rv_customer_key)
TYPE /bobf/conf_key
RAISING /bobf/cx_frw.
ENDCLASS.

CLASS lcl_demo IMPLEMENTATION.


METHOD get_customer_for_id.
"Method-Local Data Declarations:
DATA lo_driver TYPE REF TO lcl_demo.
DATA lt_parameters TYPE /bobf/t_frw_query_selparam.
DATA lt_customer_keys TYPE /bobf/t_frw_key.
DATA lx_bopf_ex TYPE REF TO /bobf/cx_frw.
DATA lv_err_msg TYPE string.
FIELD-SYMBOLS <ls_parameter> LIKE LINE OF lt_parameters.
FIELD-SYMBOLS <ls_customer_key> LIKE LINE OF lt_customer_keys.

"Instantiate the test driver class:


CREATE OBJECT lo_driver.

"Though we could conceivably lookup the customer using an SQL query,


"the preferred method of selection is a BOPF query:
APPEND INITIAL LINE TO lt_parameters ASSIGNING <ls_parameter>.
<ls_parameter>-attribute_name =
/bobf/if_demo_customer_c=>sc_query_attribute-root-select_by_attributes-customer_id.
<ls_parameter>-sign = 'I'.
<ls_parameter>-option = 'EQ'.
<ls_parameter>-low = iv_customer_id.

CALL METHOD lo_driver->mo_svc_mngr->query


EXPORTING
iv_query_key =
/bobf/if_demo_customer_c=>sc_query-root-select_by_attributes
it_selection_parameters = lt_parameters
IMPORTING
et_key = lt_customer_keys.
"Return the matching customer's KEY value:
READ TABLE lt_customer_keys INDEX 1 ASSIGNING <ls_customer_key>.
IF sy-subrc EQ 0.
rv_customer_key = <ls_customer_key>-key.
ENDIF.
ENDMETHOD. " METHOD get_customer_for_id
ENDCLASS.
Displaying Customer Records

With the query logic now in place, we now know which customer record to lookup. The question is, how do we retrieve it? For this

task, we must use the

RETRIEVE() and RETRIEVE_BY_ASSOCIATION() methods provided by the /BOBF/IF_TRA_SERVICE_MANAGER instance. As


simple as this sounds, the devil is in the details. Here, in addition to constructing the calls to the RETRIEVE*() methods, we must also
dynamically define the result tables which will be used to store the results.
As you can see in the code excerpt below, we begin our search by accessing the customer ROOT node using the RETRIEVE() method.
Here, the heavy lifting is performed by the GET_NODE_ROW() and GET_NODE_TABLE() helper methods. Looking at the
implementation of the GET_NODE_TABLE() method, you can see how we’re using the /BOBF/IF_FRW_CONFIGURATION object
reference to lookup the node’s metadata. This metadata provides us with the information we need to construct an internal table to house
the results returned from the RETRIEVE() method. The GET_NODE_ROW() method then dynamically retrieves the record located at the
index defined by the IV_INDEX parameter.
Within the DISPLAY_CUSTOMER() method, we get our hands on the results by performing a cast on the returned structure reference.
From here, we can access the row attributes as per usual.
After the root node has been retrieved, we can traverse to the child nodes of the /BOBF/DEMO_CUSTOMERobject using
the RETRIEVE_BY_ASSOCIATION() method. Here, the process is basically the same. The primary difference is in the way we lookup
the association metadata which is used to build the call to RETRIEVE_BY_ASSOCIATION(). Once again, we perform a cast on the
returned structure reference and display the sub-node attributes from there.
CLASS lcl_demo DEFINITION CREATE PRIVATE.
PUBLIC SECTION.
CLASS-METHODS:
display_customer IMPORTING iv_customer_id
TYPE /bobf/demo_customer_id.

PRIVATE SECTION.
METHODS:
get_node_table IMPORTING iv_key TYPE /bobf/conf_key
iv_node_key TYPE /bobf/obm_node_key
iv_edit_mode TYPE /bobf/conf_edit_mode
DEFAULT /bobf/if_conf_c=>sc_edit_read_only
RETURNING VALUE(rr_data) TYPE REF TO data
RAISING /bobf/cx_frw,

get_node_row IMPORTING iv_key TYPE /bobf/conf_key


iv_node_key TYPE /bobf/obm_node_key
iv_edit_mode TYPE /bobf/conf_edit_mode
DEFAULT /bobf/if_conf_c=>sc_edit_read_only
iv_index TYPE i DEFAULT 1
RETURNING VALUE(rr_data) TYPE REF TO data
RAISING /bobf/cx_frw,

get_node_table_by_assoc IMPORTING iv_key TYPE /bobf/conf_key


iv_node_key TYPE /bobf/obm_node_key
iv_assoc_key TYPE /bobf/obm_assoc_key
iv_edit_mode TYPE /bobf/conf_edit_mode
DEFAULT /bobf/if_conf_c=>sc_edit_read_only
RETURNING VALUE(rr_data) TYPE REF TO data
RAISING /bobf/cx_frw,

get_node_row_by_assoc IMPORTING iv_key TYPE /bobf/conf_key


iv_node_key TYPE /bobf/obm_node_key
iv_assoc_key TYPE /bobf/obm_assoc_key
iv_edit_mode TYPE /bobf/conf_edit_mode
DEFAULT /bobf/if_conf_c=>sc_edit_read_only
iv_index TYPE i DEFAULT 1
RETURNING VALUE(rr_data) TYPE REF TO data
RAISING /bobf/cx_frw.
ENDCLASS.

CLASS lcl_demo IMPLEMENTATION.


METHOD display_customer.
"Method-Local Data Declarations:
DATA lo_driver TYPE REF TO lcl_demo.
DATA lv_customer_key TYPE /bobf/conf_key.
DATA lx_bopf_ex TYPE REF TO /bobf/cx_frw.
DATA lv_err_msg TYPE string.
DATA lr_s_root TYPE REF TO /bobf/s_demo_customer_hdr_k.
DATA lr_s_text TYPE REF TO /bobf/s_demo_short_text_k.

"Try to display the selected customer:


TRY.
"Instantiate the test driver class:
CREATE OBJECT lo_driver.

"Lookup the customer's key attribute using a query:


lv_customer_key = lo_driver->get_customer_for_id( iv_customer_id ).

"Display the header-level details for the customer:


lr_s_root ?=
lo_driver->get_node_row(
iv_key = lv_customer_key
iv_node_key = /bobf/if_demo_customer_c=>sc_node-root
iv_index = 1 ).

WRITE: / 'Display Customer', lr_s_root->customer_id.


ULINE.
WRITE: / 'Sales Organization:', lr_s_root->sales_org.
WRITE: / 'Address:', lr_s_root->address.
SKIP.

"Traverse to the ROOT_TEXT node to display the customer short text:


lr_s_text ?=
lo_driver->get_node_row_by_assoc(
iv_key = lv_customer_key
iv_node_key = /bobf/if_demo_customer_c=>sc_node-root
iv_assoc_key = /bobf/if_demo_customer_c=>sc_association-root-root_text
iv_index = 1 ).
WRITE: / 'Short Text:', lr_s_text->text.
CATCH /bobf/cx_frw INTO lx_bopf_ex.
lv_err_msg = lx_bopf_ex->get_text( ).
WRITE: / lv_err_msg.
ENDTRY.
ENDMETHOD. " METHOD display_customer

METHOD get_node_table.
"Method-Local Data Declarations:
DATA lt_key TYPE /bobf/t_frw_key.
DATA ls_node_conf TYPE /bobf/s_confro_node.
DATA lo_change TYPE REF TO /bobf/if_tra_change.
DATA lo_message TYPE REF TO /bobf/if_frw_message.

FIELD-SYMBOLS <ls_key> LIKE LINE OF lt_key.


FIELD-SYMBOLS <lt_data> TYPE INDEX TABLE.

"Lookup the node's configuration:


CALL METHOD mo_bo_conf->get_node
EXPORTING
iv_node_key = iv_node_key
IMPORTING
es_node = ls_node_conf.

"Use the node configuration metadata to create the result table:


CREATE DATA rr_data TYPE (ls_node_conf-data_table_type).
ASSIGN rr_data->* TO <lt_data>.
"Retrieve the target node:
APPEND INITIAL LINE TO lt_key ASSIGNING <ls_key>.
<ls_key>-key = iv_key.

CALL METHOD mo_svc_mngr->retrieve


EXPORTING
iv_node_key = iv_node_key
it_key = lt_key
IMPORTING
eo_message = lo_message
eo_change = lo_change
et_data = <lt_data>.

"Check the results:


IF lo_message IS BOUND.
IF lo_message->check( ) EQ abap_true.
display_messages( lo_message ).
RAISE EXCEPTION TYPE /bobf/cx_dac.
ENDIF.
ENDIF.
ENDMETHOD. " METHOD get_node_table

METHOD get_node_row.
"Method-Local Data Declarations:
DATA lr_t_data TYPE REF TO data.

FIELD-SYMBOLS <lt_data> TYPE INDEX TABLE.


FIELD-SYMBOLS <ls_row> TYPE ANY.

"Lookup the node data:


lr_t_data =
get_node_table( iv_key = iv_key
iv_node_key = iv_node_key
iv_edit_mode = iv_edit_mode ).

IF lr_t_data IS NOT BOUND.


RAISE EXCEPTION TYPE /bobf/cx_dac.
ENDIF.

"Try to pull the record at the specified index:


ASSIGN lr_t_data->* TO <lt_data>.
READ TABLE <lt_data> INDEX iv_index ASSIGNING <ls_row>.
IF sy-subrc EQ 0.
GET REFERENCE OF <ls_row> INTO rr_data.
ELSE.
RAISE EXCEPTION TYPE /bobf/cx_dac.
ENDIF.
ENDMETHOD. " METHOD get_node_row

METHOD get_node_table_by_assoc.
"Method-Local Data Declarations:
DATA lt_key TYPE /bobf/t_frw_key.
DATA ls_node_conf TYPE /bobf/s_confro_node.
DATA ls_association TYPE /bobf/s_confro_assoc.
DATA lo_change TYPE REF TO /bobf/if_tra_change.
DATA lo_message TYPE REF TO /bobf/if_frw_message.

FIELD-SYMBOLS <ls_key> LIKE LINE OF lt_key.


FIELD-SYMBOLS <lt_data> TYPE INDEX TABLE.

"Lookup the association metadata to find out more


"information about the target sub-node:
CALL METHOD mo_bo_conf->get_assoc
EXPORTING
iv_assoc_key = iv_assoc_key
iv_node_key = iv_node_key
IMPORTING
es_assoc = ls_association.

IF ls_association-target_node IS NOT BOUND.


RAISE EXCEPTION TYPE /bobf/cx_dac.
ENDIF.

"Use the node configuration metadata to create the result table:


ls_node_conf = ls_association-target_node->*.

CREATE DATA rr_data TYPE (ls_node_conf-data_table_type).


ASSIGN rr_data->* TO <lt_data>.

"Retrieve the target node:


APPEND INITIAL LINE TO lt_key ASSIGNING <ls_key>.
<ls_key>-key = iv_key.

CALL METHOD mo_svc_mngr->retrieve_by_association


EXPORTING
iv_node_key = iv_node_key
it_key = lt_key
iv_association = iv_assoc_key
iv_fill_data = abap_true
IMPORTING
eo_message = lo_message
eo_change = lo_change
et_data = <lt_data>.

"Check the results:


IF lo_message IS BOUND.
IF lo_message->check( ) EQ abap_true.
display_messages( lo_message ).
RAISE EXCEPTION TYPE /bobf/cx_dac.
ENDIF.
ENDIF.
ENDMETHOD. " METHOD get_node_table_by_assoc

METHOD get_node_row_by_assoc.
"Method-Local Data Declarations:
DATA lr_t_data TYPE REF TO data.

FIELD-SYMBOLS <lt_data> TYPE INDEX TABLE.


FIELD-SYMBOLS <ls_row> TYPE ANY.

"Lookup the node data:


lr_t_data =
get_node_table_by_assoc( iv_key = iv_key
iv_node_key = iv_node_key
iv_assoc_key = iv_assoc_key
iv_edit_mode = iv_edit_mode ).

IF lr_t_data IS NOT BOUND.


RAISE EXCEPTION TYPE /bobf/cx_dac.
ENDIF.

"Try to pull the record at the specified index:


ASSIGN lr_t_data->* TO <lt_data>.
READ TABLE <lt_data> INDEX iv_index ASSIGNING <ls_row>.
IF sy-subrc EQ 0.
GET REFERENCE OF <ls_row> INTO rr_data.
ELSE.
RAISE EXCEPTION TYPE /bobf/cx_dac.
ENDIF.
ENDMETHOD. " METHOD get_node_row_by_assoc
ENDCLASS.

Note: In this simple example, we didn’t bother to drill down to display the contents of the ROOT_LONG_TEXT node. However, if we had
wanted to do so, we would have needed to create a separate service manager instance for
the /BOBF/DEMO_TEXT_COLLECTION business object since the data within that node is defined by that delegated BO as opposed to
the /BOBF/DEMO_CUSTOMER BO. Otherwise, the process is the same.
Modifying Customer Records

The process of modifying a customer record essentially combines logic from the display and create functions. The basic process is as

follows:

1. First, we perform a query to find the target customer record.

2. Next, we use the RETRIEVE*() methods to retrieve the node rows we wish to modify. Using the returned structure references, we can
modify the target attributes using simple assignment statements.
3. Finally, we collect the node row changes into the modification table that is fed into MODIFY()method provided by
the /BOBF/IF_TRA_SERVICE_MANAGER instance.
The code excerpt below shows how the changes are carried out. Here, we’re simply updating the address string on the customer. Of

course, we could have performed wholesale changes if we had wanted to.

CLASS lcl_demo DEFINITION CREATE PRIVATE.


PUBLIC SECTION.
CLASS-METHODS:
change_customer IMPORTING iv_customer_id
TYPE /bobf/demo_customer_id.
ENDCLASS.

CLASS lcl_demo IMPLEMENTATION.


METHOD change_customer.
"Method-Local Data Declarations:
DATA lo_driver TYPE REF TO lcl_demo.
DATA lv_customer_key TYPE /bobf/conf_key.
DATA lt_mod TYPE /bobf/t_frw_modification.
DATA lo_change TYPE REF TO /bobf/if_tra_change.
DATA lo_message TYPE REF TO /bobf/if_frw_message.
DATA lv_rejected TYPE boole_d.
DATA lx_bopf_ex TYPE REF TO /bobf/cx_frw.
DATA lv_err_msg TYPE string.

FIELD-SYMBOLS:
<ls_mod> LIKE LINE OF lt_mod.

DATA lr_s_root TYPE REF TO /bobf/s_demo_customer_hdr_k.

"Try to change the address on the selected customer:


TRY.
"Instantiate the test driver class:
CREATE OBJECT lo_driver.

"Access the customer ROOT node:


lv_customer_key = lo_driver->get_customer_for_id( iv_customer_id ).
lr_s_root ?=
lo_driver->get_node_row( iv_key = lv_customer_key
iv_node_key = /bobf/if_demo_customer_c=>sc_node-root
iv_edit_mode = /bobf/if_conf_c=>sc_edit_exclusive
iv_index = 1 ).

"Change the address string on the customer:


lr_s_root->address = '1234 Boardwalk Ave.'.

APPEND INITIAL LINE TO lt_mod ASSIGNING <ls_mod>.


<ls_mod>-node = /bobf/if_demo_customer_c=>sc_node-root.
<ls_mod>-change_mode = /bobf/if_frw_c=>sc_modify_update.
<ls_mod>-key = lr_s_root->key.
<ls_mod>-data = lr_s_root.

"Update the customer record:


CALL METHOD lo_driver->mo_svc_mngr->modify
EXPORTING
it_modification = lt_mod
IMPORTING
eo_change = lo_change
eo_message = lo_message.

"Check for errors:


IF lo_message IS BOUND.
IF lo_message->check( ) EQ abap_true.
lo_driver->display_messages( lo_message ).
RETURN.
ENDIF.
ENDIF.

"Apply the transactional changes:


CALL METHOD lo_driver->mo_txn_mngr->save
IMPORTING
eo_message = lo_message
ev_rejected = lv_rejected.

IF lv_rejected EQ abap_true.
lo_driver->display_messages( lo_message ).
RETURN.
ENDIF.

"If we get to here, then the operation was successful:


WRITE: / 'Customer', iv_customer_id, 'updated successfully.'.
CATCH /bobf/cx_frw INTO lx_bopf_ex.
lv_err_msg = lx_bopf_ex->get_text( ).
WRITE: / lv_err_msg.
ENDTRY.
ENDMETHOD. " METHOD change_customer
ENDCLASS.
Next Steps

I often find that the best way to learn a technology framework is to see how it plays out via code. At this level, we can clearly visualize

the relationships between the various entities and see how they perform at runtime. Hopefully after reading this post, you should have a

better understanding of how all the BOPF pieces fit together. In my next blog post, we’ll expand upon what we’ve learned and consider

some more advanced features of the BOPF API.

51 Comments

like (0)

1. James Wood Post author


The complete code bundle can be downloaded from here:

http://www.bowdark.com/downloads/BOPFDemoProgram.zip

Thanks.

like (0)
1. SB
James,

I have downloaded the full program. Neither the full program nor the blog post have the logic to read the

long text from node ROOT_LONG_TEXT. Could you please share that logic if you have so.

Thanks

like (0)
1. James Wood Post author
Ah, I missed the long text part. Please find an updated version of the code at the download link above. I

made some wholesale changes to the code so that it’s cleaner and more organized. Alas, the original demo

was rather primitive. Let me know if you have any further questions. Thanks.

like (0)
1. SB
James,

Thank you so much for the complete code.

like (0)

2. Eric Peterson
Hi James,
This blog seems very helpful, so I’d like to start at the beginning of the series. Unfortunately the link in your

first paragraph is broken. Any idea where I can find it?

Thanks,

Eric

like (0)

1. Manish Kumar
It looks like the blogs were originally posted in personal space, and then moved to ABAP Development

space.

Here are the correct links.

Navigating the BOPF: Part 1 – Getting Started

Navigating the BOPF: Part 2 – Business Object Overview

Navigating the BOPF: Part 3 – Working with the BOPF API

Navigating the BOPF: Part 4 – Advanced BOPF API Features

Navigating the BOPF: Part 5 – Enhancement Techniques

Navigating the BOPF: Part 6 – Testing & UI Integration

like (0)

3. Andrew Hampton
Hi James

Great blogs, starting to give me some understanding of BOPF.

I have a question though. I have a requirement for my client for a monster “super query” against the

EHHSS_INCIDENT business object which basically would allow them to query all Incidents by any

combination of any selection criteria from any nodes!

I have already told them I think this is out of the question. However I want to at least be able to give them

something. My main challenge is that it seems that Queries are attached to nodes, but what I really want is

a cross-node query.

For example there is one node called “Persons involved” (table EHHSSD_INC_PINV) and another node called

“Person Role” (table EHHSSD_INC_PROLE). This would allow me to query the persons involved in an

Incident (selecting by John Smith for example) or the roles of people involved in an incident (eg Witness).

But what it does not allow me to do is to query the Incidents where John Smith is a Witness. To do that I

have to use a foreign key relationship in a GUID on EHHSSD_INC_PROLE to point to the DB_KEY on

EHHSSD_INC_PINV.
So my main question is: Is it possible to do cross-node queries? If so how?

I thought about creating a database view as a join of the two tables, but then I don’t know how to hook this

database view onto the BO processing framework and how to attach queries to it. Or is the way of having a

transient structure which is a join of the two tables & somehow hook this into database retrieval & queries.

Would really appreciate some guidance on this.

Thanks

Andy

like (0)

1. James Wood Post author


Hi Andrew,

To answer your question: yes. For your specific use case(s), I think the approach would be to create custom

queries. So, for example, if you want to create a query in which you pull all incidents where John Smith is a

witness, I think you’d simply want to create a custom query against the ROOT node. Here, you’d have a

data structure to capture the selection criteria (e.g. person name and role) and a custom class to implement

the query logic using a SQL JOIN. A decent example of this would be the SELECT_BY_OSHA_CRIT query

defined against the PERSON_INVOLVED node. Hope this helps.

Thanks,

James

like (0)
1. Andrew Hampton
Thanks James, good to know it is possible. I’ll be giving it a try over the next few days.

like (0)

4. Zhenbo Wang
Great article, thanks. I am just beginning to like BOPF.

like (0)
5. Shubhada Gore
Hi James,

Thank you for publishing such an informative blog series to understand the BOPF.

I am facing one error in the program. Can you guide please?


I am trying to create a chemical using BO EHFND_CHEMICAL using program. Referring the sample code

listed in your blog.

I have passed ROOT node and BI_CHEMICAL nodes. But I am getting error ‘Mandatory node ROLE is

missing’.

When I tried to add ROLE node , in the code its giving errors like ‘Mandatory node ROLE is missing’ OR can

not create, source object does not exist’.

I went through the node structure for the BO. In it we have Root – > Revision -> Role.

So my query is how to pass information for ROLE node? We need to add REVISION node also?

I have added REVISION node as below but still getting error ‘Mandetory node ROLE missing’.

*”Build the Revision node:

CREATE DATA lr_s_revision.

lr_s_revision->key = /bobf/cl_frw_factory=>get_new_key( ).

lr_s_revision->root_KEY = lr_s_root->key.

APPEND INITIAL LINE TO lt_mod ASSIGNING <ls_mod>.

<ls_mod>-node = IF_EHFND_CHM_C=>sc_node-revision.

<ls_mod>-change_mode = /bobf/if_frw_c=>sc_modify_create.

<ls_mod>-source_node = IF_EHFND_CHM_C=>sc_node-root.

<ls_mod>-association =

IF_EHFND_CHM_C=>sc_association-root-revision.

<ls_mod>-source_key = lr_s_root->key.

<ls_mod>-key = lr_s_revision->key.

<ls_mod>-data = lr_s_revision.

* “Build the ROLE node:

CREATE DATA lr_s_role.

lr_s_role->key = /bobf/cl_frw_factory=>get_new_key( ).
lr_s_role->PARENT_KEY = lr_s_revision->key.

lr_s_role->ROOT_KEY = lr_s_root->key.

lr_s_role->chemical_role = ‘1’.

APPEND INITIAL LINE TO lt_mod ASSIGNING <ls_mod>.

<ls_mod>-node = if_ehfnd_chm_c=>sc_node-role.

<ls_mod>-change_mode = /bobf/if_frw_c=>sc_modify_create.

<ls_mod>-source_node = if_ehfnd_chm_c=>sc_node-revision.

<ls_mod>-association =

if_ehfnd_chm_c=>sc_association-revision-role.

<ls_mod>-source_key = lr_s_revision->key.

<ls_mod>-root_key = lr_s_root->key.

<ls_mod>-key = lr_s_role->key.

<ls_mod>-data = lr_s_role.

Can you guide me please?

like (0)
1. James Wood Post author
Hi Shubhada,

What version of EHSM are you on? I’m looking at an EHSM 3.0 system with SP 4 installed and I don’t see a

REVISION or ROLE node available in EHFND_CHEMICAL. These types of nodes are not uncommon to master

data objects, so it wouldn’t surprise me that they were added in a later release of the software. However, as

I can’t see the nodes myself, it’s hard to speculate what the error condition might be. At a glance, your code

above looks to be correct…

One thing I might suggest is to look closely at the contents of EO_MESSAGE after you attempt to modify

and/or save the BO. Here, I’d recommend scanning through the MT_MESSAGE table to find the error

message in question and see if the NODE_KEY/VAL_KEY fields are populated. This might give you more of a

clue about where the error condition is emanating from. Hope this helps.

Thanks,

James
like (0)
1. Shubhada Gore
Hi James,

Thank you for the reply.

I am using SAP EHS Management Extension 4.0, release 400.

I will try to look at the MT_MESSAGE as suggested by you.

Thank you.

like (0)

6. Surender reddy
Very good in detail information for techies.

Thank you very much and well done.

Regards,

Surender reddy

like (0)
7. Bob Varghese
Hi James,

The above blog regarding BOPF is really good.

Thanks for sharing your insight in BOPF.

Regards,

Bob.

like (0)
8. Paul Hardy
Mr.James,

Here is a very technical question about the mechanism whereby a number (like a customer number) gets

turned into a GUID type key.

When I debug the SERVICE_MANAGER->QUERY method I see that a fuly dynamic SQL statement is being

built up and then the database table queried using the customer number.
As there is no index on custmer number I would expect a full table scan to occur, and the performance to be

dreadful. Yet this does not seem to be the case – performance is OK and the ST05 trace did not say “full

table scan” but some Oracle gobbledegook I had not seen before.

Is there some black magic at work here to get around the fact you are selecting on a field where there is no

index?

Cheersy Cheers

Paul

like (0)

1. James Wood Post author


Hi Paul,

So am I correct in assuming that you’re testing with the /BOBF/DEMO_CUSTOMER business object

demonstrated in this blog post? If so, I’m seeing that SAP has in fact created an index on the CUSTOMER_ID

field in the table behind the ROOT node (/BOBF/DM_CST_HDR). Are you seeing something different on your

end?

In general, I would say that there’s nothing real special going on with these node attribute queries. When

you get past all of the dynamic code, the SQL queries executed by the BOPF runtime are basically OpenSQL

as per usual.

Anyway, I hope this helps. If I’m off base with my analysis here, let me know a little more details about what

you’re testing with and I’ll dig a little deeper.

Thanks,

James

like (0)
1. Paul Hardy
As a test I had created my own object with a “number” field which is what the human in front of the

computer would use to search for the object. An invoice number let us say.

As the primary key was the GUID and I deliberately did not put an index on the “number” then I expected

the SQL trace to say “full table scan”.


I actually got something like “ROW STOPKEY” which I think means the database looks at every sinlge record

in the table until it finds a match and then stops, which is in effect a full table scan.

I was just wondering if there was anything magic happening here, but it seems not.

This does throw into question the entire wisdom of having a database table with a GUID as the primary key

– if you have an object where people are always going to search by number – invoices are a great example –

then isn’t having two indexes – the primary key and the index on the number – just a doubling up of

resources?

I know in HANA world this is not going to matter, but realistically most people are not going to be there any

time soon.

Cheersy Cheers

Paul

like (0)
1. James Wood Post author
Hi Paul,

OK, I’m with you now. You make a good point here on the wisdom of using GUIDs vs. semantic keys. In

standalone environments, I frequently find this approach to be painful to work with (CRM comes to mind). In

the dynamic world of the BOPF though, I think that the choice to use GUIDs actually makes a lot of sense.

Being able to traverse from node to node by joining on PARENT.DB_KEY = CHILD.PARENT_KEY makes it very

easy to build generic BO query frameworks where the performance is actually quite good. The primary

overhead is when you hit the header table which would normally require an index on the semantic key. I

suppose anytime you build a framework like this, there’s going to be some overhead, but in my mind, what

they have here is pretty manageable. Anyway, my two cents.

Thanks,

James

like (0)

9. Pankaj Lal
Hi James
I am new to BOPF, and trying to set up a condition in TM using BOPF. Standard SAP provide a BO

/SCMTMS/SUPPLIER which is a Master data Object. I can read a Carrier in run time using this. There is

another BO /SCMTMS/TOR, where I can read the data from a freight order for example Customer (in

ConsigneeID field).

So when in SAP TM, I select a carrier to be assigned to a Freight Order, I can read Carrier separately using

/SCMTMS/SUPPLIER and Customer from the FO separately under /SCMTMS/TOR. Once the carrier is assigned

and FO is saved, I can read the carrier under /SCMTMS/TOR as well under TSP_ID but before that TSP_ID is

blank.

My requirement is to read the “carrier to be assigned” under /SCMTMS/TOR before FO is saved, so that I

can check a condition between customer and carrier before saving it. In other words I want to read the

Partner value of /SCMTMS/SUPPLIER (Master data Object) in /SCMTMS/TOR in Business Process Object. Is

this feasible? How to achieve this. Looking forward for your response. Thanks for the Help.

Regards

Pankaj

like (0)

1. James Wood Post author


Hi Pankaj,

I haven’t worked with TM before, nor do I have access to a TM system, so I can only speculate on some of

this. Some questions:

1. Am I correct in assuming that the /SCMTMS/SUPPLIER BO is linked to the /SCMTMS/TOR BO via a

cross-business object (XBO) association?

2. Is TSP_ID a (transient) attribute defined somewhere underneath the /SCMTMS/TOR BO node

hierarchy?

If my assumptions above are correct, I expect that you should be able to back track from the XBO

association class to figure out how the two BOs are physically linked. If the carrier’s being identified before

the save event occurs, I’d expect that you’d be able to get your hands on the foreign key to the carrier

somewhere inside the /SCMTMS/TOR BO. From here, you may need to enhance/create a determination to

preemptively fill the TSP_ID field using a SQL query based on the selected carrier key.
Again, I’m sort of flying blind here, so let me know if I’m off base or need further clarification. Hope this

helps.

Thanks,

James

like (0)
3. Pankaj Lal
Hi James,

Thanks for your reply..

For the ease of understanding you can consider Freight order as a Sales Order and Carrier as a TSP

(transport service provider partner which is not yet enter in the SO) and consignee as a ship to party..

Suppose you have saved the sales order with out the partner.. So you can see the Order number in VBAK

table, you can see the Ship to party also in VBPA. But Since the carrier partner TSP is not yet assigned in the

SO, it will be not be there in VBPA, although it exists as a master data in LFA1 table.

Now Consider LFA1 as /SCMTMS/SUPPLIER BO which is just a master data, and VBAK/PA as /SCMTMS/TOR BO

which is the transaction data. When I pass the SO number in VBPA table, I can read ship to party, and when

I pass the carrier number in LFA1, I can read the Carrier number from there.

Similarly I am using a data access determination using /SCMTMS/SUPPLIER (~LFA1) in a condition Cond1

and dad using /SCMTMS/TOR (~VBPA) in a condition Con2, when I pass the Carrier in cond1, I can read the

carrier there and when I pass the freight order number in cond2, I can read ship to (consignee) in cond2, but

since they are read in two different condition, I am not able to do some logical operations on them..

So I want to read both (Carrier to be assigned) and the Consignee under one condition. To do so I am trying

to create a dad for /SCMTMS/SUPPLIER, and dad for /SCMTMS/TOR under same condition. Technically its not

possible to so I am trying to read the /SCMTMS/SUPPLIER in /SCMTMS/TOR using some association and

data crawler.

The TSP_ID field stores the carrier for a freight order and is directly under the ROOT node of TOR BO. But it

can be read only once the Carrier is entered and freight order is saved with it.

/SCMTMS/SUPPLIER is a master data BO, and stores the Carrier under ROOT node in Partner field.
I tried to find an association in Trxn BOPF for /SCMTMS/TOR and I could find an association named

BO_TSP_ROOT, But I am not sure if it links with /SCMTMS/SUPPLIER or not, don’t know how to check it.

I am looking for your help to find out more insight about association, and how to see what how two BO

nodes associate.. and In case there in no association, is there any mechanism to read the Master data from

a Master data node into the Business process Object in run time?

Sorry for such a lengthy post, I appreciate your help and patiently helping me out here.

like (0)
1. James Wood Post author
Hi Pankaj,

This all makes logical sense. One question though: in your description above you mention that the carrier is

not yet assigned to the freight order. Assuming that’s the case, I’m curious to understand when the

condition you’re building is supposed to fire? Am I correct in assuming that you want this to start kicking in

at the point when the carrier’s assigned but before the freight order’s saved?

Anyway, can you send some screenshots in BOPF of the /SCMTMS/TOR BO? Looking specifically for

screenshots with the expanded node hierarchy, association definitions, etc. That would help point you in the

right direction I think.

Thanks,

James

like (0)
1. Pankaj Lal
Hi James

Actually its related to one Incompatibility setting, where when I select a Carrier and Freight Order, System

checks the Carrier and Consignee (assigned in FO), and based on the condition result either allow or gives

error. Standard SAP has given two separate condition for Carrier and FO, hence I have some limitation. I am

trying to club both under one condition. My guess was that BO_TSP_ROOT can be one association, but

somehow its not working as its not the Master data BO..

Thanks

Pankaj

like (0)
1. James Wood Post author
Can you also please send me a screenshot of the BO_TSP_ROOT association (highlighted above) when you

double-click on it? In that definition, you should get a sense for how these two BOs are related. From here,

perhaps we can backtrack and see if we can artificially build a linkage to satisfy your condition.

Thanks,

James

like (0)
1. Pankaj Lal
Hi James
The association is already there in the picture, If you click open it. Its bigger image hence not visible

comment box..

Regards

Pankaj

like (0)
1. James Wood Post author
Yes, but what we need is the details around the association. For instance, is there an association class

defined? On the Association Binding tab, what do the attribute bindings look like? To get where you want to

go, you’ll need to figure out how to hoist the carrier ID up to a point where you can access it in your

condition. So, you may have to create another determination to achieve this which utilizes similar logic to

the association definition.

Thanks,

James

like (0)
1. Pankaj Lal
Hi James

Thanks once again for helping me. I am adding some screenshots around association.

The association class is /BOBF/CL_LIB_C_CROSS_BO

Please let me know If you are looking for anything else.

Regards

Pankaj
like (0)
1. James Wood Post author
Hi Pankaj,

Given the way this association is defined, I’m thinking that you may have to get clever with this. I’m

thinking something along the lines of the following:

2. Implement some enhancement logic to intercept the carrier assignment event and store the

selected carrier ID in a shared memory object (which internally uses a hash table to associate the

carrier ID with the corresponding FO).

3. Create a transient attribute on the root node of the freight order BO to expose the carrier ID.

4. Create a custom determination to populate the transient attribute with the carrier ID value from

shared memory.

I think this should allow you to access the carrier ID from your condition record before the FO is saved. What

do you think?

Thanks,

James

like (0)
4. Pankaj Lal
Hi James
I am not very strong in technical area specially in BOPF as I mostly work in functional side only, but If I

translate what I understood in my language, its a 3 step process

1.- I need to read the Carrier data in a custom Table (at the time of carrier creation event, the carrier will be

stored in /scmtms/supplier as well as in this custom table)

2. – Maintain a custom defined field on BO node of Freight order for carrier.

and

3. – then read from there at the run time while saving the FO using the custom determination..

Please let me know if this is correct understanding.. So could you please help with the steps to do it. I will

try and see if this works. Also I can use this idea in other problems as well.

I also have one basic query.. Once I find the association for a BO node, how can I find out what other BO

nodes its associated to..

Thanks

Pankaj

like (0)
1. James Wood Post author
Hi Pankaj,

My thought with point #1 was to capture the event when the carrier is associated with the freight order (but

before the FO is saved). Based on what you commented earlier (see below), I gathered that this was the

gap you were struggling with: figuring out how to read the carrier ID in a condition before the FO is saved.

The logic described above was intended to provide you with a separate field which makes it easy to link up

the FO and carrier data from within the FO business object. Am I off base here?

…Once the carrier is assigned and FO is saved, I can read the carrier under /SCMTMS/TOR as well under
TSP_ID but before that TSP_ID is blank…

Thanks,

James

like (0)
1. Pankaj Lal
No James, you are absolutely right.. I think I misunderstood pt. 1.. So how to proceed on this?

Thanks and regards


Pankaj

like (0)
1. James Wood Post author
Hi Pankaj,

The next step is to start writing code. Perhaps there’s a developer on your project you can work with to take

this concept to realization. Best of luck with the project.

Thanks,

James

like (0)
1. Pankaj Lal
Sure James.. I will check with the development team here..

Thanks a lot for your valuable suggestions on this topic.. I will keep you updated if this works.

Thanks and Regards

Pankaj

like (0)

10.James Wood Post author


Hi Shakeel,

The issue with your code is in the way you’re creating the PERSON_INVOLVED record. For both records,

you’re mapping SY-UNAME to the PERSON_INVOLVED.ID field. To create separate records, you need to map

different IDs for each distinct person. Here, you have three different types to choose from:

Employee Types (A + {pernr})

Business Partner Types (B + {bupa})

User Types (D + {user ID})

Without knowing a ton about your use case, it would seem that if all you have to go on is contact

information, you probably will have to either use that information to look up one of the three person types

listed above or create an ad hoc business partner on the fly. Regardless of the path you take, the resultant

ID is what you would plug into the ID field. Do that, and I think you’re on the right track.

Thanks,
James

like (0)

1. Md Shakeel Ahmed
Hi James,

Thanks for quick replay.

I have tried by your above inputs and know two Involved person are getting created.

But how to create the REPORTING PERSON under the same INVOLVED_PERSON node. I.e mean who is

creating the incident.

I am trying to create the three INVOLVED PERSONS

1.Reporting Person

2.Injured Person

3. Witness Person

Thanks a lot.

Regards,

Shakeel.

like (0)
1. James Wood Post author
The reporting person would be created just like all the others. In this case though, you’d probably want to

use the CL_EHFND_PARTY_PROXY class’ CONVERT_USER_NAME_TO_PARTY_KEY() method to convert SY-

UNAME into an EHSM party key. Then, plug that key into the ID field and assign the reporting person role in

the PERSON_ROLE node. Thanks.

like (0)
1. Md Shakeel Ahmed
Hi James,

Thanks for reply.

I have followed the above steps and resolved my issue.


I have another question to ask about the delegated node like ‘NEAR_MISS_DESC’ and

‘INJURY_ILLNESS_BP_DESC’ in EHHSS_INCIDENT.

I wrote this code to create the injury description but it is not updating.

CREATE DATA lr_s_inj_info.

lr_s_inj_info->key = /bobf/cl_frw_factory=>get_new_key( ).

lr_s_inj_info->oc_inc_type = ‘EHHSS_OIT_ACC_ON_WAY’.

APPEND INITIAL LINE TO lt_mod ASSIGNING <ls_mod>.

<ls_mod>–node = if_ehhss_inc_c=>sc_node–person_inj_info.

<ls_mod>–change_mode = /bobf/if_frw_c=>sc_modify_create.

<ls_mod>–source_node = if_ehhss_inc_c=>sc_node–person_involved.

<ls_mod>–association = if_ehhss_inc_c=>sc_association–person_involved–person_inj_info.

<ls_mod>–root_key = lr_s_root->key.

<ls_mod>–source_key = lr_s_per_inv->key.

<ls_mod>–key = lr_s_inj_info->key.

<ls_mod>–data = lr_s_inj_info.

CREATE DATA lr_s_injury_illness.

lr_s_injury_illness->key = /bobf/cl_frw_factory=>get_new_key( ).

lr_s_injury_illness->inj_ill = ‘EHHSS_ILLC_INJ’.

* lr_s_INJURY_ILLNESS->TYPE = ‘EHHSS_OIT_ACC_ON_WAY’.

lr_s_injury_illness->type_desc = desc. ” Description

lr_s_injury_illness->type_desc = ‘DESCR’.

APPEND INITIAL LINE TO lt_mod ASSIGNING <ls_mod>.

<ls_mod>–node = if_ehhss_inc_c=>sc_node–injury_illness.

<ls_mod>–change_mode = /bobf/if_frw_c=>sc_modify_create.

<ls_mod>–source_node = if_ehhss_inc_c=>sc_node–person_inj_info.

<ls_mod>–association = if_ehhss_inc_c=>sc_association–person_inj_info–injury_illness.
<ls_mod>–root_key = lr_s_root->key.

<ls_mod>–source_key = lr_s_inj_info->key.

<ls_mod>–key = lr_s_injury_illness->key.

<ls_mod>–data = lr_s_injury_illness.

CREATE DATA lr_s_root_txt.

lr_s_root_txt->key = /bobf/cl_frw_factory=>get_new_key( ).

APPEND INITIAL LINE TO lt_mod ASSIGNING <ls_mod>.

<ls_mod>–node = if_ehhss_inc_c=>sc_node–INJURY_ILLNESS_DESC.

<ls_mod>–change_mode = /bobf/if_frw_c=>sc_modify_create.

<ls_mod>–source_node = if_ehhss_inc_c=>sc_node–INJURY_ILLNESS.

<ls_mod>–association = if_ehhss_inc_c=>sc_association–INJURY_ILLNESS–injury_illness_desc.

<ls_mod>–root_key = lr_s_root->key.

<ls_mod>–source_key = lr_s_INJURY_ILLNESS->key.

<ls_mod>–key = lr_s_root_txt->key.

<ls_mod>–data = lr_s_root_txt.

create data lr_s_INJURY_ILLNESS_DESC.

lr_s_INJURY_ILLNESS_DESC->key = /bobf/cl_frw_factory=>get_new_key( ).

lr_s_INJURY_ILLNESS_DESC->TEXT_EXISTS_IND = ‘X’.

APPEND INITIAL LINE TO lt_mod ASSIGNING <ls_mod>.

<ls_mod>–node = if_ehhss_inc_c=>sc_node–INJURY_ILLNESS_DESC.

<ls_mod>–change_mode = /bobf/if_frw_c=>sc_modify_create.

<ls_mod>–source_node = if_ehhss_inc_c=>sc_node–INJURY_ILLNESS.

<ls_mod>–association = if_ehhss_inc_c=>sc_association–INJURY_ILLNESS–INJURY_ILLNESS_DESC.

<ls_mod>–root_key = lr_s_root->key.

<ls_mod>–source_key = lr_s_INJ_INFO->key.

<ls_mod>–key = lr_s_INJURY_ILLNESS_DESC->key.

<ls_mod>–data = lr_s_INJURY_ILLNESS_DESC.
“Create the TEXT node:

CREATE DATA lr_s_text.

lr_s_text->key = /bobf/cl_frw_factory=>get_new_key( ).

lr_s_text->TEXT_TYPE = ‘DESCR’.

APPEND INITIAL LINE TO lt_mod ASSIGNING <ls_mod>.

<ls_mod>–node = lo_driver->mo_bo_conf-

>query_node( iv_proxy_node_name = ‘INJURY_ILLNESS_DESC.TEXT’ ).

<ls_mod>–change_mode = /bobf/if_frw_c=>sc_modify_create.

<ls_mod>–source_node = if_ehhss_inc_c=>sc_node–injury_illness_desc.

<ls_mod>–source_key = lr_s_INJURY_ILLNESS_DESC->key.

<ls_mod>–root_key = lr_s_root->key.

<ls_mod>–key = lr_s_text->key.

<ls_mod>–data = lr_s_text.

<ls_mod>–association =

lo_driver->mo_bo_conf->query_assoc(

iv_node_key = if_ehhss_inc_c=>sc_node–injury_illness_desc

iv_assoc_name = ‘TEXT’ ).

“Create the TEXT_CONTENT node:

CREATE DATA lr_s_txt_cont.

lr_s_txt_cont->key = /bobf/cl_frw_factory=>get_new_key( ).

lr_s_txt_cont->TEXT = ‘Text for Injury / Illness Description’.

APPEND INITIAL LINE TO lt_mod ASSIGNING <ls_mod>.

<ls_mod>–node = lo_driver->mo_bo_conf-

>query_node( iv_proxy_node_name = ‘INJURY_ILLNESS_DESC.TEXT_CONTENT’ ).

<ls_mod>–change_mode = /bobf/if_frw_c=>sc_modify_create.

<ls_mod>–source_node = if_ehhss_inc_c=>sc_node–injury_illness_desc.

<ls_mod>–source_key = lr_s_INJ_INFO->key. “lr_s_text->key.

<ls_mod>–root_key = lr_s_root->key.

<ls_mod>–key = lr_s_txt_cont->key.
<ls_mod>–data = lr_s_txt_cont.

<ls_mod>–association =

lo_driver->mo_bo_conf->query_assoc(

iv_node_key = if_ehhss_inc_c=>sc_node–injury_illness_desc

iv_assoc_name = ‘TEXT_CONTENT’ ).

Thanks,

Shakeel

like (0)
1. James Wood Post author
For near misses, you can fill in the description field directly in the NEAR_MISS node using the DESC_TEXT

field. Behind the scenes, BOPF determinations will copy the text into the subordinate node automagically.

For injury illness, the field is BP_DESC_TEXT in the INJURY_ILLNESS node.

Hope this helps.

Thanks,

James

like (0)
1. Md Shakeel Ahmed
Hi James,

Thank You.

Now texts are getting created.

Thanks & Regards,

Shakeel

like (0)
1. Md Shakeel Ahmed
Hi James,

I am trying to attach the documents like images, videos, .doc and .xls file to sap but it is uploading.
Here is my code.

********Attach file to Node ATT_DOCUMENT

data: lv_filesize TYPE sdok_fsize.

CREATE DATA lr_s_att_document.

describe field content length lv_filesize in byte mode.

lr_s_att_document->key = /bobf/cl_frw_factory=>get_new_key( ).

lr_s_att_document->key_ref = lr_s_root->key.

lr_s_att_document->FILE_SIZE = lv_filesize .

lr_s_att_document->FORM_NAME = ‘INC_INFO_WITNESS’.

lr_s_att_document->MIME_CODE = ‘/SAP/PUBLIC/BOBF’.

lr_s_att_document->FILE_NAME = ‘EHS_Image_file’.

lr_s_att_document->CONTENT = ‘C:\Users\Desktop\mobo_logo.png’.

APPEND INITIAL LINE TO lt_mod ASSIGNING <ls_mod>.

<ls_mod>–node = if_ehhss_inc_c=>sc_node–ATT_DOCUMENT.

<ls_mod>–change_mode = /bobf/if_frw_c=>sc_modify_create.

<ls_mod>–source_node = if_ehhss_inc_c=>sc_node–root.

<ls_mod>–association = if_ehhss_inc_c=>sc_association–root–ATT_DOCUMENT.

<ls_mod>–source_key = lr_s_root->key.

<ls_mod>–key = lr_s_att_document->key.

<ls_mod>–data = lr_s_att_document.

data: lt_att_doc_key TYPE /bobf/t_frw_key,

IS_ATTACHMENT type /BOBF/S_ATF_A_CREATE_FILE.

“Create the attachment record:

CALL METHOD lo_driver->mo_svc_mngr->DO_ACTION(

exporting

iv_act_key = if_ehhss_inc_c=>sc_action–att_document–upload_document

it_key = lt_att_doc_key ).
* is_parameters = is_attachment ).

Please help me.

Thanks & Regards,

Shakeel Ahmed.

like (0)
1. James Wood Post author
Hi Shakeel,

Send me an e-mail and I can send you some sample code to look at.

Thanks,
4. Navigating the BOPF: Part 4 – Advanced BOPF API Features
January 29, 2013 | 1,107 Views |

James Wood


more by this author
ABAP Development
abapbopffloorplan managerWeb Dynpro

3.In my previous blog post, I introduced the BOPF API and demonstrated how the API could be used to
perform routine CRUD operations on a business object. With this basic introduction out of the way, we’re

now ready to tackle more advanced API features such as consistency checks, the execution of actions, and

transaction management. So, without further ado, let’s get started.

Performing Consistency Checks & Validations

In keeping with the object-oriented paradigm, business objects (BOs) are designed to combine business data and business functions
together into one tidy capsule (hence the term encapsulation). One of the primary benefits of combining these entities is to ensure that
updates to business data are reliably filtered through a set of business rules. To put this concept into perspective, imagine a BO which
defines a header-level status field (e.g. 01 = Initial, 02 = In Process, 03 = Closed). Now, from a pure data perspective, there’s nothing
stopping us from updating the status field using the MODIFY()method of the /BOBF/IF_TRA_SERVICE_MANAGER interface (or heck,
even via an SQL UPDATEstatement). However, from a business perspective, there are probably some rules which define when, where,
and how we should change the status field. For example, it might be that the BO cannot be closed until any open line items are closed
out, etc.
Whatever the business rules might be, the point is that we want to ensure that a BO is consistent throughout each checkpoint in its
object lifecycle. As we learned in part 2 of this blog series, the BOPF allows us to define these consistency checks in the form
of validations. For example, in the screenshot below, you can see how SAP has created a validation called CHECK_ROOT for
the ROOT node of the /BOBF/DEMO_SALES_ORDER demo BO. This validation is used to perform a consistency check on the sales order
header-level fields to make sure that they are valid before an update is committed to the database.
One of the nice things about validations like CHECK_ROOT is that they are automatically called by the BOPF framework at specific
points within the transaction lifecycle. However, sometimes we might want to trigger such validations interactively. For example, when
building a UI on top of a BO, we might want to provide a check function which validates user input before they save their changes.
This is demonstrated in the /BOBF/DEMO_SALES_ORDER Web Dynpro ABAP application shown below.
From a code perspective, the heavy lifting for the check operation is driven by the CHECK_CONSISTENCY() method of
the /BOBF/IF_TRA_SERVICE_MANAGER interface as shown in the code excerpt below. Here, we simply provide the service manager
with the target node key and the BO instance key and the framework will take care of calling the various validations on our behalf. We
can then check the results of the validation by looking at the /BOBF/IF_FRW_MESSAGE instance which was introduced in the previous
blog.
DATA lt_key TYPE /bobf/t_frw_key.
FIELD-SYMBOLS <ls_key> LIKE LINE OF lt_key.
DATA lo_message TYPE REF TO /bobf/if_frw_message.
TRY.
APPEND INITIAL LINE TO lt_key ASSIGNING <ls_key>.
<ls_key>-key = iv_key. "<== Sales Order BO Key
CALL METHOD mo_svc_mngr->check_consistency
EXPORTING
iv_node_key = /bobf/if_demo_sales_order_c=>sc_node-root
it_key = lt_key
iv_check_scope = '1'
IMPORTING
eo_message = lo_message.
...
CATCH /bobf/cx_frw INTO lx_frw.
...
ENDTRY.
I’ll show you how to implement validations within a BO in an upcoming blog entry.

Triggering Actions

The behaviors of a business object within the BOPF are defined as actions. From a conceptual point-of-view, actions are analogous to
methods/functions in the object-oriented paradigm. The following code excerpt demonstrates how actions are called using the BOPF
API. Here, we’re calling the DELIVER action defined in the ROOT node of the /BOBF/DEMO_SALES_ORDER demo BO. As you can see,
the code reads like a dynamic function/method call since we have generically pass the name of the action along with its parameters to
the DO_ACTION() method of the /BOBF/IF_TRA_SERVICE_MANAGERinterface. Other than that, it’s pretty much business as usual.
DATA lt_key TYPE /bobf/t_frw_key.
FIELD-SYMBOLS <ls_key> LIKE LINE OF lt_key.
DATA ls_parameters TYPE /bobf/s_demo_sales_order_hdr_d.
DATA lr_s_parameters TYPE REF TO data.
DATA lo_change TYPE REF TO /bobf/if_tra_change.
DATA lo_message TYPE REF TO /bobf/if_frw_message.
DATA lt_failed_key TYPE /bobf/t_frw_key.
DATA lt_failed_act_key TYPE /bobf/t_frw_key.

TRY.
"Set the BO instance key:
APPEND INITIAL LINE TO lt_key ASSIGNING <ls_key>.
<ls_key>-key = iv_key. "<== Sales Order BO Key

"Populate the parameter structure that contains parameters passed


"to the action:
ls_parameters-item_no = '001'.
GET REFERENCE OF ls_parameters INTO lr_s_parameters.

"Call the DELIVER action defined on the ROOT node:


CALL METHOD mo_svc_mngr->do_action
EXPORTING
iv_act_key = /bobf/if_demo_sales_order_c=>sc_action-root-deliver
it_key = lt_key
is_parameters = lr_s_parameters
IMPORTING
eo_change = lo_change
eo_message = lo_message
et_failed_key = lt_failed_key
et_failed_action_key = lt_failed_act_key.

...
CATCH /bobf/cx_frw INTO lx_frw.
...
ENDTRY.
We can verify if a BOPF action call was successful by querying the EO_MESSAGE object reference and/or the ET_FAILED_KEY table
parameters returned by the DO_ACTION() method. Refer back to my previous blog for an example of the former technique. As always,
remember to commit the transaction using the
SAVE() method defined by the /BOBF/IF_TRA_TRANSACTION_MGR interface.
Action Validations

In part two of this blog series, I mentioned that there are technically two different types of validations: the consistency check
validations discussed earlier in this blog and action validations which are used to determine whether or not an action can be executed at
runtime. Since the BOPF framework invokes action validations automatically whenever actions are invoked, it is rare that you will have
a need to invoke them directly. However, the /BOBF/IF_TRA_SERVICE_MANAGER interface does provide the DO_ACTION() method
if you wish to do so (see the code excerpt below).
DATA lt_key TYPE /bobf/t_frw_key.
FIELD-SYMBOLS <ls_key> LIKE LINE OF lt_key.
DATA ls_parameters TYPE /bobf/s_demo_sales_order_hdr_d.
DATA lr_s_parameters TYPE REF TO data.
DATA lo_message TYPE REF TO /bobf/if_frw_message.
DATA lt_failed_key TYPE /bobf/t_frw_key.
DATA lt_failed_act_key TYPE /bobf/t_frw_key.

TRY.
"Set the BO instance key:
APPEND INITIAL LINE TO lt_key ASSIGNING <ls_key>.
<ls_key>-key = iv_key. "<== Sales Order BO Key

"Populate the parameter structure that contains parameters passed


"to the action:
ls_parameters-item_no = '001'.
GET REFERENCE OF ls_parameters INTO lr_s_parameters.

"Check to see if the DELIVER action can be invoked:


CALL METHOD mo_svc_mngr->check_action
EXPORTING
iv_act_key = /bobf/if_demo_sales_order_c=>sc_action-root-deliver
it_key = lt_key
is_parameters = lr_s_parameters
IMPORTING
eo_message = lo_message
et_failed_key = lt_failed_key
et_failed_action_key = lt_failed_act_key.

...
CATCH /bobf/cx_frw INTO lx_frw.
...
ENDTRY.
Transaction Management

Another element of the BOPF API that we have glossed over up to now is the transaction manager
interface /BOBF/IF_TRA_TRANSACTION_MGR. This interface provides us with a simplified access point into a highly sophisticated
transaction management framework. While the details of this framework are beyond the scope of this blog series, suffice it to say that
the BOPF transaction manager does more here than simply provide basic object-relational persistence. It also handles caching,
transactional locking, and more. You can see how some of these features are implemented by looking at the Transactional
Behavior settings of a business object definition in Transaction /BOBF/CONF_UI (see below).
So far, we have seen a bit of the /BOBF/IF_TRA_TRANSACTION_MGR on display whenever we looked at how to insert/update records.
Here, as you may recall, we used SAVE() method of the /BOBF/IF_TRA_TRANSACTION_MGR interface to save these records. In many
respects, the SAVE()method is analogous to the COMMIT WORK statement in ABAP in that it commits the transactional changes to the
database. Here, as is the case with the COMMIT WORK statement, we could be committing multiple updates as one logical unit of work
(LUW) – e.g. an insert followed by a series of updates.
Once a transaction is committed, we can reset the transaction manager by calling the CLEANUP()method. Or, alternatively, we can also
use this method to abandon an in-flight transaction once an error condition has been detected. In the latter case, this is analogous to
using the ROLLBACK WORK statement in ABAP to rollback a transaction.
During the course of a transaction, the BOPF transaction manager tracks the changes that are made to individual business objects
internally so that it can determine what needs to be committed and/or rolled back. If desired, we can get a peek of the queued up
changes by calling the GET_TRANSACTIONAL_CHANGES() method of the /BOBF/IF_TRA_TRANSACTION_MGR interface. This
method will return an object reference of type /BOBF/IF_TRA_CHANGE that can be used to query the change list, modify it in certain
cases, and so on.
Next Steps

At this point, we have hit on most of the high points when it comes to interacting with the BOPF API from a client perspective. In my

next blog, we’ll shift gears and begin looking at ways of enhancing BOs using the BOPF toolset.

16 Comments
You must be Logged on to comment or reply to a post.

1. Oliver Jaegle
Dear James,

in your sample about executing an action, you pass an ID as parameter.

If this is the ID of the sales order item you’re “delivering”, this action design was one I’d discourage.

Let me try to explain that: Usually, the instance(s) you are executing an action on is/are referred to by its

technical key (which is passed as part of IT_KEY).

The parameter shall be used for passing variable options which influence the behavior of the action (e. g.

delivering a split quantity which makes the action create a subsequent delivery item).

You should only pass the reference to the instance as an action parameter if you intend not to operate on

this instance but to use it as parameter of another logic (e. g. if you create a new instance based on the

instance given in the parameter as template).

The advantage of is obvious if you intend to prevent the execution of the action with a validation or when it

comes to mass-processing: You can always refer to the KEYs which are passed to the action: You can

validate them, exclude some of them from the execution as they failed to pass a validation (failed keys),

you can enable or disable the action using properties.

If you use a static action, you will be redundantly converting the parameter into a technical key within the

implementations and you cannot refer to the instance in created message objects.

Hope I could make it a bit clear.

Oliver

like (0)

1. James Wood Post author


Hi Oliver,

This action is not something I defined; rather it’s part of a demo BO provided by SAP. Here, the DELIVER

action defines a parameter structure which receives the item number of the sales order item being

delivered. I agree that this is not the best of conventions, but my purpose with this article is more to

demonstrate how to work with the API and pass parameters than it was to critique the validity of the sample

BOs. Make sense?

Thanks,
James

like (0)
1. Oliver Jaegle
Hi James,

Of course, your demo for using the APIs makes perfect sense. I also understood it that way.We’ll have to talk

to the BOPF guys for preparing better samples �

However, with your blog being the prime source for everyone who has a first glance at the framework, I

wanted to comment on the sample for clarifying that instances and parameters are not interchangeable –

and this difference is also part of the API-usage.

Cheers, Oliver

like (0)

2. Suhas Sondur
Excellent post! Thanks for the information. I have a query regarding the service manager.

My UI right now shows the current BO data (persisted in the DB) in read mode. If some other application

changes this BO data later, how does the service manager know that the data in the DB has changed? How

will my UI display the right(current) data?

Thanks, Suhas

like (0)

1. James Wood Post author


Hi Suhas,

Hmm, that’s a bit of a loaded question. The FBI (view) layer is generally aware of changes to BOPF records,

so it does have the ability to refresh data. If you can provide me with more specifics, I could comment

further.

Thanks,

James

like (0)
1. Suhas Sondur
Hi James,

Let’s take an example. We have a customer BO and have built a FBI UI on it to display the data. We also

have the mode handling in place. If the UI is right now displaying data for ID=ABC in read mode(APPL1) and

another application(APPL2) updates the data for the same ID(e.g. change of status or review date), how will

APPL1know that the data has been updated? Does the APPL1 need to do anything specific to update itself?

Hope my context is clear now.

Thanks,

Suhas.

like (0)
1. James Wood Post author
Hi Suhas,

A couple of questions:
1. Is APPL2 also a UI application?

2. If so, are we assuming that APPL2 has committed the changes?

Thanks,

James

like (0)
3. Suhas Sondur
Hi James,

The APPL2 could be a background process too. But it is safe to assume that the changes are committed.

Thanks,

Suhas

like (0)
2. Oliver Jaegle
Dear Suhas,

Two separate browser windows running a webdynpro-application are isolated sessions in the backend.

Meaning: They don’t know anything about each other and there are no in-built-mechanisms to notify each

other about modified data.

Assuming that the changes in the separate session are committed, they will be visible to other sessions. As

APPL1 has already loaded its state though, APPL1 will have to actively re-read the newly committed data in

order to see it.

This is what a good UI-controller should anyway do: Before switching from read-only to edit-mode, re-read

the current data and – in the very same request – lock it exclusively. And this is exactly what FBI does: On

the EDIT-event, all UIBBs are requested to reload from the database (technically, a retrieve with edit mode

exclusive is being performed).

This way, the APPL2 will appear to have been notified about the changes of the other session.

In contrast to that, the changes occurring within a session are propagated differently: Each core-service via

the service manager returns a change-object (/bobf/if_tra_change) which provides the caller with

information which data has been modified (by the consumer itself or implicitly within a determination).

Based upon this change-object, it’s up to the consumer (e. g. the feeder) to re-read the data (e. g. in order

to pass it into the UI-structures). This is what the generic FBI-components provide.

Hope I could clarify some aspects of change handling,

Oliver

like (0)
1. Suhas Sondur
Dear Oliver,

Thanks for the explanation. I now understand that the FBI controller re-reads the data again via an

implicit call to retrieve. However, for the FBI based UI(APPL1), I need to implement a “refresh” kind of

function without changing the read-only mode. Is it possible?


Triggering the “FPM_REFRESH” does not automatically update the data on the UI. My understanding is that

“FPM_REFRESH” does not reload the buffer.

Thanks,

Suhas

like (0)
1. Oliver Jaegle
As per the refresh-action, this is something I don’t think is provided “out-of-the-box” by FBI, but I believe

you have to on the event by yourself. I couldn’t test this in my system now, but I assume that you should

be fine doing by

• Having a button raise an event for the refresh-request (whether you use the FPM_REFRESH or a

custom name does not matter technically – I don’t know the semantics of the FPM_REFRESH event,

you may ask in the FPM space)

• As feeder of the UIBB which represents the root of the data which shall be refreshed (there might be

dependent nodes in other UIBBs), configure a custom one: Inherit from the FBI-feeder, redefine

process_event.

• When processing the event, retrieve the data again (requesting to read the current image and lock it

• Propagate the change-object

The rest (refreshing the data in the UI-structures and in dependent (wired) UIBBs should be taken care of by

FBI without further ado.

Let us know (either here in the comments or an own blogpost) whether and how you succeeded!

Cheers,

Oliver

like (0)

3. Thirumoorthy Rajaram
Hi James,

Very excellent post!!..

I have a small query. I have created an action, when i am trying to trigger that action using the DO_ACTION

method provided by the trx_mgr, it is not getting triggered. Failed key table is filled. I am not getting what

exactly is happening, tried debugging but no luck. Could you please me what exactly i am missing in

calling the action. The key which i am passing as an import parameter is the SC_BO_KEY of my custom

object created by me.

Regards,

Thiru

like (0)

1. James Wood Post author


Hi Thiru,
I’d need more context to go on to comment on this I think. Send me further details and I’ll try to help as

best I can.

Thanks,

James

like (0)
1. Thirumoorthy Rajaram
Hi James,

Thank you very much for the reply!!

The issue which I have raised in my previous comment is solved now. First of all I want to let you know that

your blog and the docs related to BOPF framework is very nice and understandable, in simple words it is

awesome for someone like me who is a new comer. Thank you for sharing the knowledge.

I have some more questions for you, would say as clarification

My requirement is to have 3 nodes

Root->Parent->Child

The cardinality from ROOT to PARENT is 0..1 and

The cardinality from PARENT to CHILD is 1..N

Is there any option or possibility to restrict the records based on cardinality of the nodes by caller

program(or class) or any configuration set up. we tried by setting the association cardinality of Root(1..1)

and Parent node(1..1) but when tried creating a record in BOBT it is allowing more than one record even

though cardinality is 1…1.

Our main requirement is to have only one record at root and parent node, but child node can have multiple

records corresponding to parent node key. Please find the below image of our BOPF object structure in

pictorial representation.

Would be really nice if you can guide us!!


Regards,

Thiru

like (0)
1. Oliver Jaegle
Hi Thiru,

BOPF does not automatically validate cardinalities as this is not always requested and as every validation

comes with a performance penalty.

However, BOPF offers a generic validation implementation (/BOBF/CL_LIB_V_ASSOC_CA) which you can

configure. Please find the details in the class-documentation:

CL /BOBF/CL_LIB_V_ASSOC_CARD
____________________________________________________
Short Text
Validation: Checks if association cardinality is consistent

Functionality
Generic validation to check the cardinality of associations. The association cardinalities are checked for the

node where the validation is configured.

The associations are retrieved and for all associations

1. which have cardinality one or one-to-many, it is checked, whether at least one node instance is
existing.

2. which have cardinality zero to one or one, it is checked, whether at most one node instance is
existing.

Relationships
The validation is intended to be used as action validation at “check before save” .

Notes
As the validation is quite generic is first of all intended for prototype use or during an early implementation

state. If performance problems occur the validation implementation should be replaced by a specific

implementation.

Cheers,

Oliver

P.s.: I would recommend to ask such good questions in the BOPF community: BOPF Application Framework.

This has multiple advantages: More persons knowing about your question, more people sharing the problem

and answer afterwards, I don’t have a mixed feeling about answering questions directed to James

1. If the BO instance that we want to maintain/display exists already, then we can load it into context using the Load Instances button
menu. As you can see in the screenshot below, this menu affords us with several different alternatives for loading node instances: via a

BOPF node query, by the node instance key, or by an alternative key (e.g. ID). Regardless of the menu path that we take, the system

will attempt to find the target node instance(s) and then load them into the editor window. From here, we can select individual node

instances by double-clicking on them in the Metadata and Instances tree located on the left-hand side of the screen.
To edit node instances, we can select the node instance record in the editor on the right-hand side of the screen and choose the

appropriate option from the Edit button menu (see below). Then, we can edit attributes for a node instance using the provided input

fields. Alternatively, we also have the option of deleting a node instance (or indeed an entire BO instance in the case of a root node

instance) by clicking on the Delete Node Instances button.

Regardless of whether or not we’re creating a new BO instance or editing an existing one, the entire scope of our changes is tracked via

a BOPF transaction like the one we would create if we were doing all this by hand using the BOPF API. At any point along the way, we

can choose to commit the changes using the Save Transaction button, or revert the changes using the Cleanup Transaction button.
Then, we can start the process over by selecting another BO instance or editing the existing one in place. All in all, it’s kind of like table

maintenance on steroids. But wait, there’s more!

Triggering Actions, Validations, & Determinations

In addition to the basic CRUD operations described earlier, the test UI also provides functions to call actions, validations, and even

trigger determinations. For a given node instance, these functions can be accessed in the node instance toolbar via

the Check and Actions button menus (see below). If you read through my blog posts related to the BOPF API, then these should feel

quite intuitive.

UI Integration and the FBI Framework


Since the focus of this blog series has been primarily on introducing the BOPF framework, I have purposefully avoided digressing into

specific applications of the BOPF (e.g. in Transportation Management or EHSM) since these products add additional layers on top of

the BOPF that can sort of cloud the picture a bit if you don’t understand core principles of the BOPF itself. However, before I bring this

blog series to a close, I would be remiss if I didn’t point out one important (and relatively generic) framework built on top of the

BOPF: the Floorplan Manager BOPF Integration (FBI) framework. As the name suggests, this framework links BOs from the BOPF

with Web UIs based on the Floorplan Manager (FPM) framework and Web Dynpro ABAP (WDA).

If you’re developing Web UIs on top of BOs from the BOPF, then the FBI is definitely something to take a look at. Essentially, the FBI

exploits the genericity of the BOPF API and the accessibility of BO model data to enable the rapid development of Generic User

Interface Building Blocks (GUIBBs) based on BO nodes. Here, for example, we could create a form GUIBB that allows users to

populate the data for a BO node using a simple input form. In many applications, this can be achieved without having to write a single

line of code. While a detailed discussion of the FBI is beyond the scope of this blog series, a quick Google search will lead you to some

pretty decent resource materials. If you’re new to FPM, I would also offer a shameless plug for my book Web Dynpro ABAP: The

Comprehensive Guide (SAP PRESS, 2012).

Conclusion
When I first started working with the BOPF almost a year ago, I was surprised at how little documentation there was to get started with.

So, what you’ve seen in this series is the result of a lot of trial-and-error and lessons learned by debugging past application-specific

frameworks into the heart of the BOPF itself. If you’re just getting started with the BOPF, then I hope that you’ll find this series useful

to get you up and running. In the coming months and years, I think many more learning resources will materialize to supplement what

I’ve offered here. Indeed, the number of new dimension applications based on the BOPF appears to be growing by the day…

One complaint I sometimes hear from other developers is that the BOPF API is cumbersome to work with. On this point, I can agree to

a point. However, I would argue that such complexities can be abstracted away pretty easily with a wrapper class or two and some good

old fashioned RTTI code. Other than that, once you get used to the BOPF, I think you’ll find that you like it. And this is coming from a
developer who has had many bad experiences with BO frameworks (both in and outside SAP…curse you EJBs!!!). All in all though, I

have found the BOPF to be very comprehensive and flexible. For me, one of the feel tests I normally conduct to gauge the effectiveness

of a framework is to ask myself how often the framework gets in my way: either because it’s too intractible, limited in functionality or

whatever. I have yet to run into any such occurrences with the BOPF. It does a good job of providing default behaviors/functionality

while at the same time affording you the opportunity to tweak just about everything. For example, if I want to build my own caching

mechanism, I can do so by plugging in my own subclass. If I want to pull data from a HANA appliance in real time, I can do so in a

determination. You get the idea. It’s all there, so just poke around a bit and I think you’ll find what you need.

13 Comments
You must be Logged on to comment or reply to a post.

1. Rajashiva Ramalingam
Hi James,

its very nice series of blog.Now i hope the BOPF stuff is available in SCN with your contibution !

like (0)
2. Bharath Komarapalem
Hi James,

I should thank and appreciate your efforts for preparing such blog series. I just started with SAP TM and

BOPF. I am in the same situation now in what situation you were 1 year back.

But your blog is really helpful. Although it is very difficult to understand and digest in the first read but i

hope over and after some period there will be some improvement in understanding..

Nice blog and nicely documented.

Regards,

Bharath.

like (0)
3. sapkrisboedge mac
Hi James,

It was very nice blog. Now, I hope the BOPF stuff are avaible in community..

Thanks,

kris.

like (0)
4. Rajesh Sharma
Really good stuff james

like (0)
5. sapkrisboedge mac
It is Really Good James..

like (0)
6. Sagar Pilli
Hi James,
Its really very good stuff and I encourage who ever comes on my way to work on BOPF to refer your blogs

with any doubt.

As on date, this blog is the priceless source of Information on BOPF.

Cheers

Sagar

like (0)
7. Zhenbo Wang
Hi James,

great article. It has changed my opinion regarding the BOPF framework (more or less).

Do you know a way to convert the bopf-key to a readable name during debuging? Search it in the constant

interface is disturbing.

thanks and regards,

Zhenbo

like (0)

1. James Wood Post author


Hi Zhenbo,

Unfortunately, no. This is a pain point for sure. Sorry I don’t have a better answer for you.

Thanks,

James

like (0)

8. Paul Hardy
Mr.James,

Have you ever got change documents working in the BOPF with external keys?

I can get entries popping up in CDHDR and CDPOS easily enough, but redefining the method

GET_EXTERNAL_IDS from a subclassof /BOFU/CL_CDO_BO_GENERICCALLBACK never gets called i.e. the

breakpointis never reached, although the rdefinition of IS_CDO_CREATION does.

Having an external ID in the change document log would be lovely.

The code seems to be looking in table TCD0B which I think is the SCD0 and then seeing if the entry is a

table type as opposed to a transparent table. Only then is the GET_EXTERNAL_ID called. If this is true then

you have to define your Z table type with a really short name, as the SCDO transaction still thinks we are in

version 3 with a limit of 16 characters on DB table names.

Cheersy Cheers

Paul

like (0)

1. James Wood Post author


Hi Paul,

I’ve used these callback classes as a hook for triggering downstream interfaces, but I’ve never tried to

implement a scenario like what you’re describing. That being said, have you tried setting a break-point in

the PCASBGDTCRD_GET_EXT_IDS() method of class /BOFU/CL_CHANGE_DOC_HANDLING? It looks like this is


the launching point for the external ID handling. Maybe you can glean some insight from there (or see how

you might enhance the framework to support what you’re looking for)?

Thanks,

James

like (0)
1. Paul Hardy
Hello,

What I did was go through the steps in the official SAP help to set up the change document adapter for my

custom business object i.e. attach the standard BOPf change document object as a delegated object.

This works fine, entries appear in CDHDR/CDPOS, but with the GUID. It is no big thing to get the proper ID

i.e. a number a human can relate to based on the GUID but the SAP help says that you can redefine the

method ion the callback class, and there is an example in the demo customer callback class, not that the

demo customer business object actually works for change documents (does not have a change document

delegated object).

In the /BOFU/CL_CHANGE_DOC_HANDLING nothing gets triggerd unless the flag

MV_KEY_EXTID_MAP_REQUIRED is set to ABAP_TRUE. this gets set in the CONSTRUCTOR if

MV_KEY_TO_EXTID_MAP_STRUCT is not initial.

This value in turn is created during ,ethod INIT_CDO_NODES_AND_TABS. this method looks at the entries in

TCDOB for the object class. I think TCDOB is the table where the entries in transaction SCDO are stored.

The method loops through the entries in TCDOB looking for an entry which NOT a transparent table e.g. a

structure, in fact i think it is looking for /BOFU/CDTEXTID specifically. Then that structure gets analysed and

the program decides if the first include is /BOFU/S_CD_EXTID_KEYS. if this is true, then the

MV_KEY_TO_EXTID_MAP_STRUCT variable is filled.

I will have a go at playing with this, probably adding /BOFU/CDTEXTID to SCdO for my custom object and

then do some more debugging.

There also does not seem to be any standard way to get the CDHDR/CDPOS entries back outside of the

WDA/FPM environment. What if you wanted to use this with UI5 or whatever comes next after that?

Cheersy Cheers

Paul

like (0)
1. Paul Hardy
Yes, that worked fine.

Now i get two entries in CDPOS, both with the GUID as the OBJECTID, but one of them has /BOFU/CDTEXTID

as the “table” being changed and that entry contains the external ID.

I am really happy bunny about getting this working, if the SAP help had just told me waht to do it would not

have been anywhere near as much fun!

5. Navigating the BOPF: Part 5 – Enhancement Techniques


February 22, 2013 | 2,104 Views |

In my previous two blog posts, we explored the BOPF API from a client point-of-view. Here, we learned how to perform basic CRUD

operations, execute actions, and so on. Now that you have a feel for how the API operates, we’re ready to take a peek behind the curtain

and see these services are implemented within the business objects themselves. For now, our emphasis will be on enhancing these

services since SAP does not yet support the creation of new business objects. However, whether we’re enhancing existing business

objects or creating new ones from scratch, the concepts remain the same.

What to Enhance?

Before we dive into the exploration of specific enhancement techniques, let’s first take a look at the kinds of entities we’re allowed to

enhance in a business object. Aside from implicit enhancements applied to implementation classes using the Enhancement Framework,

the types of entities that we can enhance within a business object are as follows:

• Custom Attributes

• For a given node, we might want to define a handful of additional custom attributes.These attributes could be persistent (i.e., they get

appended to the target database table which contains the node data) or transient in nature.

• New Sub-Nodes

• In some cases, we may need to do more than simply define a few new attributes on an existing node. Using the relational data model as

our guide, we may determine that a new sub-node is needed to properly model some new dimension of data (e.g. 1-to-many relations,

etc.). Depending on the requirement, the sub-node(s) might be persistent or transient in nature.

• Determinations

• If we add new custom attributes to a given node, it stands to reason that we might also want to create a custom determination to manage

these attributes.

• Or, we might have a standalone requirement which calls for some sort of “trigger” to be fired whenever a specific event occurs (e.g. fire

an event to spawn a workflow, etc.).

• Consistency Validations

• If we are enhancing the data model of a business object, we might want to define a consistency validation to ensure that the new data

points remain consistent.

• A custom validation might also be used to graft in a new set of business rules or a custom security model.

• Actions

• If we have certain operations which need to be performed on a business object, we would prefer to encapsulate those operations as an

action on the business object as opposed to some standalone function module or class.

• Queries

• In some cases, the set of defined queries for a business object might not be sufficient for our needs. In these situations, we might want

to define custom queries to encapsulate the selection logic so that we can use the generic query services of the BOPF API as opposed to

some custom selection method.


You can find a detailed treatment of supported enhancement options in the BOPF Enhancement Workbench Help documentation which

is provided as a separate download in SAP Note #1457235. This document provides a wealth of information concerning the use of the

BOPF Enhancement Workbench, enhancement strategies, and even the BOPF framework in general. Given the amount of detail

provided there, I won’t attempt to re-invent the wheel in this blog post. Instead, I’ll simply hit on the high points and leave the nitty-

gritty details to the help documentation.

Working with the Enhancement Workbench


When enhancing a business object, you’ll be spending quite a bit of time with the BOPF Enhancement Workbench which can be

accessed using Transaction BOPF_EWB. Here, enhancement projects are organized into enhancement objects. From a conceptual

point-of-view, enhancement objects bear a lot of similarities to sub-classes in the object-oriented programming (OOP)

paradigm. This is to say that enhancement objects inherit all of the entities of their parent BO. With this foundation in place, we can

begin defining custom entities in much the same way we might add new attributes/methods to a subclass in the ABAP Class Builder

tool. However, as is the case with classes in the OOP world, we cannot extend BOs which are marked as final or that do not have the

“Business Object can be enhanced” flag set (see below).


All of the BOs which are eligible for enhancement will show up in the Enhancement Browser perspective of the BOPF Enhancement

Workbench shown below. To create an enhancement, simply right-click on the BO that you wish to enhance and select the Create

Enhancement menu option (see below). From here, the BOPF Enhancement Workbench will guide you through a wizard process which

allows you to select the name of the enhancement object, the constants interface for the enhancement object, and so on.
Once the enhancement is created, you will be able to edit your enhancement object in the workbench perspective of the BOPF

Enhancement Workbench shown below. As you can see, it has a similar look-and-feel to that of the normal BO browser tool

(Transaction /BOBF/CONF_UI). From here, we can begin adding custom entities by right-clicking on the target node and selecting

from the available menu options. We’ll see how this works in the upcoming sections.
One final item I would draw your attention to with enhancement objects is the assigned constants interface (highlighted above). This

constants interface can be used to access the enhancement object entities in the same way that the super BO’s constants interface is used

for BOPF API calls, etc.

Enhancing the BO Data Model

Perhaps the most common type of enhancement to BOs in the BOPF is the addition of new fields. Here, we have the option of adding

new fields to existing nodes or creating sub-nodes to model more complex relationships. In the former case, we sometimes don’t even

need to create an enhancement object; just a simple append structure will suffice (see below).

For more complex data requirements, we typically need to define sub-nodes. This can be achieved by right-clicking on the parent node
and selecting the Create Subnode menu option. This kicks off a wizard process in which you can select the sub-node’s name, its
persistent and/or transient structures, and the rest of the auto-generated dictionary types which go along with a node definition (e.g.
combined structure/table type, database table, etc.). Most of this is pretty standard stuff, but I would draw your attention to the step
which creates the persistent and/or transient structures. Note that these structures must exist in the database before you move on from
the Attributes step in the wizard process. And, in the case of the persistent structure, you must include the /BOBF/S_ADMIN structure as
the first component.
After the custom sub-node is created, you can fill out its attributes by adding components to the persistent/transient structures defined

by the sub-node. If the sub-node is a persistent node, then we can create, modify, and retrieve node instances using the BOPF API as per

usual. However, in the case of transient nodes, we need determinations to pre-fetch the data for us. We’ll see how to define such

determinations next.

Defining Determinations

According to the help documentation, determinations encapsulate internal changing business logic on a business object. Unlike the

logic encapsulated in actions which can be triggered at any time, the business logic contained within determinations is triggered as

specific times within the BO life cycle (e.g. right before a node is saved, etc.). So, in a way, it is appropriate to think of determinations

as being a little bit like user exits/BAdIs/enhancement spots in that they provide a place to hang custom logic at particular points within

the process flow.

Once we determine (no pun intended) that we want to create a determination for a given node, we can do so by simply right-clicking on

that node and selecting the Create Determination menu option. This will spawn a wizard which guides us through the process. Here,

there are two main properties that we must account for:

1. Implementing Class:

• We must create or assign an ABAP Objects class that implements the /BOBF/IF_FRW_DETERMINATION interface.
2. Determination Pattern:
• This property defines the event which triggers the determination. As you can see below, the set of available patterns will vary

depending on the type of node you’re enhancing, its location in the node hierarchy, and so on.
• Once a pattern is selected, you may be presented with additional options for refining when an event is triggered. For example, if we

select the pattern “Derive dependent data immediately after modification”, we will have the opportunity to specify if the dependent data

should be created/modified after any modification, only when the node is created the first time, etc.
Because determinations can be used for a lot of different things, they can be implemented in a lot of different ways. Here, it is very
important that you pay close attention to selecting the right pattern for the right job. The aforementioned help documentation provides a
good set of guidelines to assist here. Other valuable resources include the interface documentation for
the /BOBF/IF_FRW_DETERMINATIONinterface in the Class Builder tool and SAP standard-delivered determinations implementations
available in the system you’re working on.
Defining Consistency Validations

The process of defining a custom consistency validation is quite similar to the one used to define determinations. Walking through the

wizard process, there are three main properties that we must account for:

1. Implementing Class:

• Here, we must create/assign an ABAP Objects class which implements the /BOBF/IF_FRW_VALIDATION interface.
2. Request Nodes:
• This property allows us to specify which node operations should force a validation to occur (e.g. during creates, updates, etc.)

3. Impact:
• With this property, we can specify the behavior of the BOPF framework in cases where the validation fails. For example, should we

simply return an error message, prevent the requested operation from proceeding, or both?

From an implementation perspective, the /BOBF/IF_FRW_VALIDATION interface provides us with everything we need to perform the
validation check: the context of the validation, the current data within the node instance being validated, and so on. For more
information about how to implement the validation class, I would highly recommend that you read through the interface documentation
for the /BOBF/IF_FRW_VALIDATION interface in the Class Builder tool. It can also be helpful to look at various standard-delivered
classes which already implement this interface to see common patterns/idioms used by SAP.
Working with Actions

When it comes to the customization of actions, we have a couple of options:

• We can create a brand new action definition for a given node (standard or custom).

• We can enhance existing actions with pre/post action enhancements.

The first case is pretty straightforward. Basically, we simply follow along with the wizard process up to the point that we reach the

Settings step shown below. Here, we must define three main properties for the action:

• Implementing Class:

• This property is used to specify the ABAP Objects class which encapsulates the action logic. The class must implement
the /BOBF/IF_FRW_ACTION interface.
• Action Cardinality:

• The action cardinality property defines the scope of the action. This is somewhat analogous to the way we have the option of defining

class methods or instance methods within a regular ABAP Objects class. In this case however, we also have the third option of defining

a sort of “mass-processing” action which works on multiple node instances at once.

• Parameter Structure:

• If we wish to pass parameters to the action, we can plug in an ABAP Dictionary structure here to encapsulate the parameters.
Once the action is created, we simply need to plug in the relevant logic in the defined implementation class. You can find

implementation details for this in the interface documentation and/or sample action classes in the system.

In order to create a pre/post action enhancement, the target action definition in the super BO must have its “Action Can Be Enhanced”
flag set (see below). Assuming that the flag is set, then we can proceed through the corresponding wizard process in much the same
way we would if we were creating a custom action from scratch. Indeed, as is the case with regular actions, the implementation
class(es) for pre/post action enhancements must implement the /BOBF/IF_FRW_ACTION interface.

Before you go to implement a pre/post action enhancement, I would definitely recommend that you read through the help

documentation so that you understand what you can and cannot do within an action enhancement. Most of the rules are intuitive, but

you can definitely get into trouble if you abuse these enhancements by using them for things they weren’t designed for.

Defining Custom Queries

Compared to the various enhancement options we’ve seen thus far, custom queries are perhaps the easiest entities to create within an

enhancement object. Indeed, if all we want is a simple node attribute query, we can zip through the wizard and have a working model

up and running in a matter of minutes. If we want something a little more custom/sophisticated, our job is only marginally more

difficult (at least from a configuration perspective) in that we must assign an implementing class and an optional data type which serves

as the parameter structure passed into the query from the client side (see below).
From an implementation perspective, all of the query logic for a custom query gets encapsulated in the implementation class (which
must implement the /BOBF/IF_FRW_QUERY interface). For the most part, you’ll find that the framework doesn’t really get in the way
with regards to how we go about implementing the query. Basically, it passes in the query parameters up front and it’s up to us to figure
out how to find all of the node instances which match the given parameters. Here, we must pay careful attention to the SQL statements
that we use since the query may be used extensively by a number of different clients.
Next Steps

Hopefully by now you have a general feel for how BOs are enhanced and the basic steps required to achieve these enhancements. As is

the case with most programming-related subjects, the best way to really drive these concepts home is to look at live examples and

experiment for yourself. I would also highly recommend that you read through the aforementioned help documentation as it devotes

quite a bit of time to understanding when and where to apply specific enhancement techniques.

In my next and final blog post in this series, I’ll demonstrate another useful tool within the BOPF toolset: the BO test tool. This tool can

be used to experiment with BOs and perform ad hoc unit tests, etc.

15 Comments
You must be Logged on to comment or reply to a post.

1. Werner Herold
Hello James,

is the BOPF based on object service SAP-persistency or does it use an own way to exchange data with the

database?

Werner

like (0)

1. James Wood Post author


Hi Werner,

It actually uses its own mechanisms which are in many respects more sophisticated (e.g. buffering/caching,

etc.).

Thanks,

James

like (0)
1. lakshmi chinta
Hi James,

please let me know your thoughts on this http://scn.sap.com/thread/3518411

regards

luxmi

like (0)

2. Bob Varghese
Hi James,

Is it possible to hide FPM button( in module MOC ) via BOPF?

I tried by enhancing FPM configurations by unchecking enable and setting visible property to not visible.

Still I couldn’t achieve it.


Please let me know if there are differnet ways to achieve the above.

Thanks in advance.

Regards,

Bob.

like (0)
3. aparna trivedi
Hi James,

These BOPF blogs are truly an insight. Thanks for publishing them.

I want to implement an enhancement on Location BO (EHSM) for that i have created a freestyle Webdynpro

component . Now as per the SAP help portal it says i can use the freestyle helper class

/BOFU/CL_FREESTYLE_BOPF_HELPER to use BOPF functionality in my Webdynpro component.

Please advice how to use this class in the freestyle Webdynpro component.

Thanks in Advance.

Regards,

Aparna

like (0)

1. James Wood Post author


Hi Aparna,

If it were me, I’d use this class as follows:

1. Create a subclass of /BOFU/CL_FREESTYLE_BOPF_HELPER and designate that subclass as the

assistance class for your WDA component.

2. Implement the IF_FPM_UIBB_MODEL WDA component interface in your freestyle UIBB (just like you

did with IF_FPM_UI_BUILDING_BLOCK).

3. Implement the GET_MODEL_API() method by returning an instance of your assistance class (e.g.

WD_ASSIST).

4. Enhance the regular callback methods from IF_FPM_UI_BUILDING_BLOCK by calling the HANDLE_*()

methods of the assistance class. Of particular importance here is probably the HANDLE_FLUSH()

method.

5. Use the FPM wire model to connect your freestyle UIBB as the source/target of wire connections as

per usual. Here, the pre-built implementations for methods from the IF_FPM_FEEDER_MODEL

interface in /BOFU/CL_FREESTYLE_BOPF_HELPER should allow you to use your freestyle UIBB just like

you would GUIBBs, etc.

Hope this helps.

Thanks,

James

like (0)
6. aparna trivedi
Hi James,

Thank you for your reply.

I am able to relate and understand all the steps except no. 5.

When i see the enhanced standard component configuration wiring model it doesn’t show this freestyle

component in the Wiring model to connect with the existing LIST and FORM UIBB’s.

Regards,

Aparna

like (0)
1. James Wood Post author
Hi Aparna,

Have you already performed steps 1-4? Without step #2 in particular, the component’s not really wire-

model enabled, so I expect FLUID wouldn’t detect it in the wire editor. I’d recommend stubbing this out and

trying again to see if that helps.

Thanks,

James

like (0)
1. aparna trivedi
Hi James,

Thanks again for your time.

The freestyle WD component has both the interfaces implemented IF_FPM_UIBB_MODEL WDA and

IF_FPM_UI_BUILDING_BLOCK.

But in the component configuration i can see there is an Error saying ‘Error at instantiation of component

ZWDXXXX with config ID ZCC_XXX’. This error disappears the moment i save my Comp Config

enhancement.

Do you think this can be a reason my component is not appearing in FPM wiring model.
Please see an image of how i am configuring my freestyle component.
like (0)
1. James Wood Post author
Hi Aparna,

It’s kind of hard to speculate without looking at your component, but I’d look very closely at the initialization

methods in your WDA component’s component controller to see if that’s the culprit here. Bear in mind that

FLUID will instantiate your component in order to pull in relevant metadata at design time. Normally, I see

this error whenever developers attempt to perform initialization routines which reference runtime-related

data that’s not there at design time (e.g., application parameters, shared data from an application

controller, etc.). I’d set some external breakpoints and see if that’s the issue.

Thanks,

James

like (0)
1. aparna trivedi
Hi James,

I am able to use my freestyle webdynpro component now to enhance the standard location object.

But i am stuck with a strange issue here. In the WDC i have a table to add some fields and update them in

location BO. After saving the fields for first time it gets saved in BO but the SAVE button is disabled due to

which i am not able to add more entries.

Have you faced this kind of issue before or any suggestions how we can get rid of this.

Thanks for your time.

Regards,

Aparna

like (0)
1. James Wood Post author
Hi Aparna,

How are you synchronizing your table entries with the BOPF? Normally the FBI application controller class

will check to see if save is required/enabled based on whether or not there are changes queued up in the

BOPF. I’d look over your implementation there and also check your application controller settings around

transaction management to see if the pattern is set to change the edit mode after a save.

Thanks,

James

like (0)

4. Arunkumar Subramanian
Hi James,

I have a query on the enhancement technique in BOBF.

There is an option to create Business Object Enhancement from transaction BOBX where I can provide my

Super Business Object. In this case I can use the enhanced BO for my custom actions and other custom

functionality.
Now if we try to create a BO in the transaction /BOBF/CONF_UI, then it provides an option to enter a Super

Business Object. In this case too I will be able to create my custom functionality.

But in the second case it doesn’t show as Enhancement object. What is the difference between these two?

When can we use the first option and when the second one.

Thanks

Arun

like (0)
5. Vladimir Staroseltsev
Hello James,

Explain pls how to enhance existing BO.

In this article enhancement means subclassing but whether after subclassing existing software which use

superclass BO will use my enhancements? or subclassed BO can only be used with new business code?

I need to add validations to existing business object and want it will take effect on existing code. Should I

follow actions in this article or my case is different?

Thanks,

Vladimir

like (0)

1. James Wood Post author


Hi Vladimir,

The inheritance/subclassing metaphor was meant mostly to describe the structural relationship between an

enhancement BO and its parent/base BO. However, while this metaphor is sufficient for describing the

design time relationship(s), it does break down at runtime since the BOPF does not allow one to

address/instantiate enhancement BOs separately. In effect, any enhancement elements you define (e.g.

determinations, validations, etc.) become grafted into the overall runtime model for the parent BO (though

at a somewhat lower precedence level in that they generally run after the standard elements). Thus, any

custom validations you define would take effect immediately without any modifications to the code that

uses the parent BO.

So, to answer your question, yes – any validations you add will take effect on existing code. And yes, the

steps outlined in this article would apply for your use case.
6. Navigating the BOPF: Part 6 – Testing & UI Integration
March 4, 2013 | 1,207 Views |

ABAP Development

abapbopffbifloorplan managerfpmWeb Dynproweb dynpro abap

• share 0

• share 0

• tweet

• share 0

Follow

In my previous blog post, we looked at ways of enhancing business objects (BOs) defined within the BOPF. Once these enhancements

are in place, it is a good idea to unit test the changes to make sure that the BO is working correctly. If you subscribe to the test-driven

development (TDD) model, then an obvious choice here would be to use the BOPF API to write some ABAP Unit test cases. However,

sometimes we just want to run a quick ad hoc test. Or, we might just want to display BO data at a glance without constructing a multi-

table join in Transaction SQVI. For these tasks and others, SAP provides us with a very slick tool: the BOPF Test UI. In this final blog

post, I will introduce this transaction and demonstrate its basic capabilities. Then, to round things out, I’ll briefly touch on UI

integration and the FBI framework.

Working with the BOPF Test UI


We can access the BOPF Test UI by opening up Transaction /BOBF/TEST_UI. When you initially open up the tool, you’ll be presented

with a screen like the one shown below. From here, we can begin working with a BO instance by plugging in the BO type in the Select

BO input field (either key it in or use the provided input help) and hitting the ENTER key. This causes the BO metadata to be loaded

into context so that we can use it to guide ourselves through the editing process.
Editing BO Instances
Once the BO metadata is loaded, we have two choices for maintenance:

2. To create a new BO instance, we can double-click on the root node contained in the “Metadata and Instances” tree on the left-hand side
of the editor screen and then select the Create button in the toolbar (see below). This will cause a new record to be created and loaded

into an editable ALV grid. From here, we can begin filling in node attributes, creating sub-node instances, and so on. Here, I would

draw your attention to the Messages panel located in the bottom left-hand corner of the editor. These messages can be used to help you

fill in the right data.


3.

Potrebbero piacerti anche