ABAP RESTful Programming Model en
ABAP RESTful Programming Model en
2020-10-08
4 Concepts. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
4.1 Data Modeling and Behavior. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
Query. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46
Business Object. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
4.2 Business Service. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 138
Business Object Projection. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140
Service Definition. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 147
Service Binding. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 150
4.3 Service Consumption. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 151
4.4 Runtime Frameworks. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .153
4.5 Entity Manipulation Language (EML). . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 154
5 Develop. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .159
5.1 Developing Read-Only List Reporting Apps. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161
Determining the Data Model for the Read-Only Scenario. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163
Implementing Associations for Existing CDS Views. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 169
Changing UI Field Labels and Descriptions. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 171
Displaying Text for Unreadable Elements. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .174
Providing Value Help for the Selection Fields . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .176
Adding Search Capabilities. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 179
5.2 Developing Managed Transactional Apps . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 182
Reference Business Scenario . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 186
Developing a Ready-to-Run Business Object. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 191
Developing Business Logic . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 213
Developing a Projection Layer for Flexible Service Consumption. . . . . . . . . . . . . . . . . . . . . . . . 274
7 Reference. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 693
9 Glossary. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 943
The ABAP RESTful Application Programming Model (in short RAP) defines the architecture for efficient end-to-
end development of intrinsically SAP HANA-optimized OData services (such as Fiori apps) in SAP Cloud
Platform ABAP Environment [page 960] or Application Server ABAP. It supports the development of all types of
Fiori applications as well as publishing Web APIs. It is based on technologies and frameworks such as Core
Data Services (CDS) for defining semantically rich data models and a service model infrastructure for creating
OData [page 957] services with bindings to an OData protocol and ABAP-based application services for
custom logic and SAPUI5-based user interfaces – as shown in the figure below.
Architecture Overview
Target Audience
ABAP developers who want to provide (OData) services within the scope of ABAP RESTful application
programming model.
This documentation refers to the range of functions that have been shipped as part of delivery of the
application server for
Note
To highlight the specifics for SAP Cloud Platform releases, the icon is used.
The ABAP RESTful Application Programming Model is the evolutionary successor of the ABAP Programming
Model for SAP Fiori. It is generally available to customers and partners within SAP Cloud Platform ABAP
Environment starting with release 1808 and within ABAP Platform starting with release 7.54 SP00 (1909
FPS00).
For more information about the evolution of the ABAP programming model, read this blog on the
community portal.
Contents
2.1 Prerequisites
You have access to and an account for SAP Cloud Platform, ABAP environment.
More on this: Getting Global Account
Authorizations
To create development artifacts described in this guide, you need the developer authorization profile for the
ABAP Environment.
Knowledge
Basic knowledge of
2.2 Constraints
The current version of the ABAP RESTful programming model still has some constraints when ...
● Determinations: You can't use on-modify determinations to trigger other on-modify determinations
(except in another BO, but without cycles).
● Authority: Only instance-based authorizations are available. That means, static authorizations are not
available. Therefore, you cannot apply authorization checks to create operations.
● Numbering: Late numbering is not supported.
● Functions: Functions are not supported.
● Actions: Factory Actions are not supported for the managed implementation type.
To test UI features or the create functionality for nested subentities, you can test the OData service with the
Web IDE and configure the application to enable navigation to any number of child entities.
Alternatively, you can access the preview via the parent entity of the nested subentity that you want to test.
Example
You want to test the create functionality of the subentity BookingSupplement in the Travel business
object, which is the second child entity in the hierarchy. Instead of starting the preview in the service
binding via the root entity Travel or the composition Travel-to_Booking, access the preview via the
child entity Booking or the composition Booking-to_BookSupplement to see the complete feature
scope of the nested subentity BookingSupplement.
● Even though you can delete all generated artifacts except for the generated service definition, it is
recommended not to do so as it corrupts the service consumption model. If you edit or delete a generated
artifact, then the form editor for the service consumption model does not open and an error is displayed.
Also, if you delete any of the generated artifacts, you cannot delete a service consumption model object.
You need to recreate the deleted artifact for the form editor and object deletion to work.
● For a service entity set, the remote OData service may have support only for one CRUD operation, for
example, READ. Currently, code snippets are displayed for all the operations even if the support is provided
only for one operation.
Service Binding does not get updated using abapGit if ALL of the following conditions are applied:
● New Service Definitions are added to Service Binding in the source system.
● Service Definitions are created in target system using abapGit pull.
● Service Binding update is also part of the same abapGit pull.
In such a user flow, the user must follow the below mentioned steps:
Using Draft
Using Determinations/Validations
When using determinations and validations, the following constraints are given:
● You can't execute determine actions inside implementations of determinations and validations.
This Getting Started section provides you with the fundamental basics of development with the ABAP RESTful
Programing Model.
For demonstration and learning purposes we provide the ABAP Flight Reference Scenario which simulates an
application used by a travel agency for booking flights. The first thing to do is therefore to import the ABAP
Flight Reference Scenario in your ADT to get sample data: Downloading the ABAP Flight Reference Scenario
[page 12].
The getting started guide helps you to create a complete application based on the existing data model from the
ABAP Flight Reference Scenario with the most basic features: Developing an OData Service for Simple List
Reporting [page 13].
The ABAP Flight Scenario contains demo content that you can import into your development environment.
The ABAP Flight Reference Scenario helps you to get started with development in the context of the ABAP
RESTful Application Programming Model. It contains demo content that you can play around with and use to
build your own sample applications.
Sample Data
First of all, the reference scenario contains data. You can use database tables that are filled with travel data
including master data items, such as customer, flights, airports, or booking supplements. The structure of the
complete data model allows you to build simple but also more complex services. In this way, it is easy to follow
the steps in the development guides while building your own application based on the same database tables as
in the given examples.
For an overview of the available database tables, see ABAP Flight Reference Scenario [page 912]. They are
available in the package /DMO/FLIGHT_LEGACY. This package also includes a data generator with which you
can fill the database tables.
Sample Services
The development guides for the ABAP RESTful Application Programming model are based on the sample data
from the ABAP Flight Reference Scenario. That means that you can compare the documentation with the
productive code that was used to build the documentation scenario. In addition, the ABAP Flight Reference
Scenario also includes a demo package with the development objects that are created during the course of the
development guides. That means, the whole demo scenario can be downloaded and tested. You obtain full
demo services with code built by following conventions and best practices and you can use and reuse the
delivered objects for your development.
● Developing Read-Only List Reporting Apps [page 161] in the package /DMO/FLIGHT_READONLY
Legacy Coding
The reference scenario also includes legacy coding. This legacy coding is based on function modules and
exemplifies legacy applications that you can include in your new ABAP code. Above all, the legacy coding is
relevant for the development guide, that explains how to build a new service on the basis of an existing
application. It illustrates how you build an application with the unmanaged implementation type. The legacy
coding that is used in this scenario is available in the package /DMO/FLIGHT_LEGACY.
You can download the complete ABAP Flight Reference Scenario for the ABAP RESTful Application
Programming Model from GitHub.
https://github.com/SAP-samples/abap-platform-refscen-flight/tree/Cloud-Platform .
The steps to include the development objects in your ADT are described in the README.md file.
Remember
The namespace /DMO/ is reserved for the demo content. Apart from the downloaded ABAP Flight Scenario,
do not use the namespace /DMO/ and do not create any development objects in the downloaded packages.
You can access the development objects in /DMO/ from your own namespace.
The following guide describes the basic development tasks to create a simple list reporting app based on a
query.
Introduction
The following sections serve as an introductory guide for the development of an OData service based on the
ABAP RESTful Application Programming Model . It forms the basic building block for more elaborate scenarios
with extended read-only features or transactional processing.
An OData service makes it possible to create and consume queryable and interoperable RESTful APIs. A SAP
Fiori Elements application consumes OData services like this, but it also possible for other Web clients to make
use of an OData service that is created with the ABAP RESTful Application Programming Model .
The following guide starts from a data model assuming that database tables already exist. It uses the ABAP
Flight Reference Scenario (in short Flight Scenario), which provides example data comprising travel
information with flight data. For a detailed description of the database tables that are used in this scenario,
refer to ABAP Flight Reference Scenario [page 912]
You are guided step-by-step through the new application model in three consecutive building blocks:
You start by implementing a CDS view as a new data model layer using a data source that is already provided.
You also use basic CDS annotations to manifest semantics for the data model. The next step is to create an
OData service by defining and binding a service based on the corresponding CDS view. As soon as the OData
service is published in the local system repository, it is ready to be consumed using an OData client, such as a
SAP Fiori app. Finally, you learn how to use UI annotations as a UI technology independent semantic
description of the user interface layout.
The result of this Getting Started guide is a consumable OData Service, which can be easily used to set up a
Fiori Elements travel booking app, from which you can derive information about flight connections. Navigation
properties are added to this application to receive more information about bookings, customers, and agencies
in the other scenarios in the Develop [page 159] section. These other development guides also cover extended
read-only and transactional features, whereas the Getting Started guide only deals with the most basic read-
only features for setting up an OData Service. The scenarios in the Develop section assume that you
understood the steps that are described in the following guide.
Via ABAPGit you can import the service including the related development objects into your development
environment for comparison and reuse. You find the service in the package /DMO/FLIGHT_READONLY. The
suffix for development objects in this development guide is _R. Be aware that the development objects
might contain more than explained in the Getting Started guide. This is because the Getting Started
scenario is enhanced in the first development guide Developing Read-Only List Reporting Apps [page 161]
which uses this same demo objects.
For information about downloading the ABAP Flight Reference Scenario, see Downloading the ABAP Flight
Reference Scenario [page 12].
Prerequisites
Developing the scenario that is described in the subsequent chapters requires the following:
● You have access to and an account for SAP Cloud Platform, ABAP environment.
● You have installed ABAP Development Tools (ADT).
SAP recommends to use the latest version of the client installation. The ADT download is available on the
update site https://tools.hana.ondemand.com/.
● To recreate the demo scenario, the ABAP Flight Reference Scenario must be available in your ABAP system.
You can download the complete reference scenario from GitHub: Downloading the ABAP Flight Reference
Scenario [page 12].
Objectives
By the end of this Getting Started section, you will be able to:
This introductory programming guide uses example data from the Flight Reference Scenario. The Getting
Started scenario uses the database table /dmo/connection. It provides information about airline and
connection numbers, flight times, and data related to planes.
To define a data model based on the ABAP CDS view concept, you first need to create a data definition as the
relevant ABAP Repository object using a wizard in ABAP Development Tools.
In the second step, you implement an elementary CDS view from scratch by defining a simple query for flights
based on a single data source from the ABAP Flight Reference Scenario.
In the final task of this section, you have the option of using the test environment to verify the output (a results
set) of the CDS view you have just implemented.
Task 3: Verifying the Results Set in the Data Preview Tool [page 21]
Use the data definition wizard to create the relevant development object for a CDS view.
Context
For our simple read-only scenario, we want to define data that is exposed by an OData service to make it
available for an OData client. For this purpose, you create a development object to define an ABAP CDS entity
(for example, a CDS view). The data definition provides you with the appropriate development object for the
CDS view, which is included in ABAP development tools and directly accesses the standard ABAP functions.
Procedure
3. Open the context menu and choose New Other ABAP Repository Object Core Data Services Data
Definition to launch the creation wizard for a data definition.
4. In addition to the Project and Package, which are already specified depending on the package you selected,
enter the Name (while respecting your namespace) and a Description for the data definition you want to
create.
Note
Results
In the selected package, the ABAP back-end system creates an inactive version of a data definition and stores it
in the ABAP Repository. As a result, the data definition editor is opened. The generated source code already
provides you with the necessary view annotations and adds placeholders for the names of the database view
and for the data source for query definition. The name for the actual CDS view is predefined on the basis of the
name for the data definition, but can be changed in the data definition editor.
Now that you have created a data definition, you can implement the CDS view as a data model for your OData
service.
Use a predefined database table as the data source for a CDS view.
Prerequisites
● You have created the data definition artifact in ABAP Development Tools.
● The database table /dmo/connection is available for you.
Context
In this step, you implement an interface view as a new data model using a predefined data source.
Procedure
1. If you have not yet already done so, open the new data definition in the editor.
2. Specify the names of the following:
a. Database view to be generated in the ABAP Dictionary: /DMO/ICONNECT_R
Note
The data definition editor already provides a suggestion for the name using the name that you
specified for the data definition in the creation wizard. However, these names do not have to be the
same. You can overwrite it in the define statement. Note that the names of the database view and the
CDS view must not be the same.
3. In the SELECT statement, enter the predefined database table /dmo/connection as a data source and
define an optional alias name for the data source.
An alias is useful especially when you use multiple data sources or whenever the name of the data source is
not descriptive or too long.
... select from /dmo/connection as Connection{
{
Connection.carrier_id as AirlineID,
Connection.connection_id as ConnectionID,
Connection.airport_from_id as DepartureAirport,
Connection.airport_to_id as DestinationAirport,
Connection.departure_time as DepartureTime,
Connection.arrival_time as ArrivalTime,
Connection.distance as Distance,
Connection.distance_unit as DistanceUnit
}
Tip
Whenever you insert table fields or view elements in the SELECT list, you can make use of the content
assist function in the data definition editor ( CTRL + SPACE ).
6. Click the activation button or use the shortcut Ctrl + F3 to activate the data definition.
To check the syntax before activation, click or use the shortcut Ctrl + F2 .
Results
The resulting source code for the CDS view is the following:
@AbapCatalog.sqlViewName: '/DMO/ICONNECT_R'
@AbapCatalog.compiler.compareFilter: true
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Read-Only E2E: Data Model Connection'
define view /DMO/I_Connection_R
as select from /dmo/connection as Connection
{
key Connection.carrier_id as AirlineID,
key Connection.connection_id as ConnectionID,
Connection.airport_from_id as DepartureAirport,
Connection.airport_to_id as DestinationAirport,
Connection.departure_time as DepartureTime,
Connection.arrival_time as ArrivalTime,
The source code above is used to define a quite simple CDS view named /DMO/I_Connection_R. This view is
implemented using a query that performs a SELECT statement, where the database table /dmo/connection is
used as the data source. The select list includes a set of fields that are relevant for the scenario. The KEY
elements in the selection list are used to define the key field semantics of the CDS view.
When the data definition source is activated, the following objects are created in ABAP Dictionary:
Next Steps
Use the @Semantics annotation to relate the quantity element to its unit of measure element.
Context
The CDS view /DMO/I_Connection_R that you created contains elements that are heavily dependent on each
other semantically, namely Distance and DistanceUnit. In CDS, you can use semantic annotations to
standardize semantics that have an impact on the consumer side for these elements. In general, elements that
need to be marked as having semantic content to guarantee that they are handled correctly are elements that
contain the following:
● Amounts of money
These elements need a reference to the currency related to this element.
● Amounts of measures
These elements need a reference to the unit of measure related to this element.
If you create annotations that define a link to the unit for the amounts, the amounts and their units are always
handled as being dependent on each other in the OData service. On UIs in particular, amounts are displayed
with the correct decimals with regard to their unit.
In the CDS view /DMO/I_Connection_R, you therefore need to proceed as described in the following to always
display the distance together with the distance unit.
@Semantics.quantity.unitOfMeasure: 'DistanceUnit'
Connection.distance as Distance,
@Semantics.unitOfMeasure: true
Connection.distance_unit as DistanceUnit
Results
If you expose the CDS view to an OData service, the elements are always handled as being semantically related
to each other. This means that they are given the OData annotation sap:unit and sap:semantics in the
OData metadata document. On UIs in particular, the elements are always displayed as being attached to each
other.
Related Information
Use the data preview tool to check the elements in the CDS view.
Prerequisites
The data definition has correct syntax and has been activated.
You have created a data definition and implemented a CDS view with data from the database table /dmo/
connection. Now you have the option of launching the test environment (in the data preview tool), which
enables you to verify that the persisted data from the database is now displayed in the CDS view.
Procedure
In the data definition editor, position the cursor somewhere in the CDS source code. Open the context menu
and choose Open With Data Preview or use the shortcut F8 .
Results
The CDS view does not require any parameters, which means the data preview displays the results set of the
data selection query directly.
Note
You can sort the entries by element by clicking the column header.
Business service artifacts enable the publishing of an OData service using ABAP Development Tools.
In the previous step, you defined a data model based on the persisted data source /dmo/connection in the data
definition /DMO/I_Connection_R. You can now use this data model and expose it for an OData service. The
OData service makes it possible for UI technologies to query data and consume it. The following steps are
necessary to include the CDS view in an OData service.
To define a service, you first need to create a service definition as the relevant ABAP Repository object using a
wizard.
The next step is to define the scope of the OData service by exposing the relevant CDS views (including their
metadata and their behavior).
To define the type and category of the OData service, you need to create a service binding as the relevant ABAP
Repository object. There is also a wizard available for this.
In the next step, you use the form-based editor of the service binding to publish the service locally.
You have the option of checking the resulting OData service by viewing its metadata. The service binding offers
a simple solution for this.
You can also take a look at how the UI of a Fiori Elements of the OData service looks like with the preview tool of
the service binding.
Use the service definition wizard to create the relevant development object that defines the scope of the OData
service
Context
The service definition is a projection of the models and related behavior that you want to expose. In a service
definition, you define the OData service to determine which CDS entities are part of the service. This service is
then exposed either as a UI service or a Web API by a service binding artifact. A service definition can be
integrated in various protocols without any reimplementation.
1. In your ABAP project, select the relevant package node in the Project Explorer.
2. Open the context menu and choose New Other ABAP Repository Object Business Services Service
Definition to launch the creation wizard.
3. In addition to the Project and Package, which are already specified depending on the package you selected,
enter the Name and a Description for the service definition you want to create.
Note
Results
The ABAP back-end system creates an inactive version of a service definition and stores it in the ABAP
Repository.
In the Project Explorer, the new service definition is added to the Business Services folder of the corresponding
package node. As a result, the service definition editor is opened:
Now that you have created a service definition, you can choose one or more CDS entities to be exposed in the
service.
Related Information
Prerequisites
You have created the service definition artifact in ABAP Development Tools.
Context
In the service definition editor, you determine the CDS entities that you want to expose in an OData service.
Procedure
1. If you have not yet already done so, open the new service definition in the editor.
The name of the service is already specified in accordance with the name you gave in the service definition
wizard. It cannot be changed to a different name.
2. Specify the name of each CDS entity that you want to expose for the service. For the getting started read-
only scenario, there is only one CDS view to be exposed: /DMO/I_Connection_R
3. Optionally, you can assign an alias for the CDS view.
An alias is useful, especially when you use multiple CDS views or whenever the name of the CDS view is not
descriptive or too long.
4. Click the activation button or use the shortcut Ctrl + F3 to activate the service definition.
To check the syntax before activation, click or use the shortcut Ctrl + F2 .
The source code above is used to define a service definition named /DMO/FLIGHT_R. It exposes the CDS
view /DMO/I_Connection_R to be included in the service.
Next Steps
Now that the service exists, you can determine the binding type and category for the service using a service
binding.
Use the service binding wizard to create the relevant development object to bind the service to a protocol and,
if necessary, to an OData client.
Prerequisites
You have defined a service and exposed CDS entities that are included in the service.
Context
A service binding implements the protocol that is used for the OData service. It uses a service definition that
projects the data models and their related behaviors to the service.
Procedure
1. In your ABAP project, select the relevant package node in the Project Explorer.
2. Open the context menu and choose New Other ABAP Repository Object Business Services Service
Binding to launch the creation wizard.
3. In addition to the Project and Package, which are already specified depending on the package you selected,
enter the Name and a Description for the service binding you want to create.
Note
The available categories are UI and Web API. A UI-based OData service can be consumed by any SAP
UI5 application. An OData service with Web API binding is exposed as an API.
The ABAP back end creates a service binding and stores it in the ABAP Repository.
In the Project Explorer, the new service binding is added to the Business Services folder of the corresponding
package node. As a result, the service binding form editor is opened and you can verify the information you
have entered.
As soon as you have created the service binding for a service, the service is registered in your local system. It is
not yet active.
Next Steps
Related Information
To make the service ready for consumption, use the activation button in the service binding form editor.
Prerequisites
You have created the service binding and specified the binding type and category.
To make the service available and consumable by an OData client you have to activate the service.
Procedure
1. If you have not already done so, open the new service binding in the form editor.
The binding type and category are already defined and cannot be changed once the service binding is
created. You can verify the type and category in the general information section in the form editor. As soon
as you have specified the binding for the service, it is ready for publishing. The service is then available for
consumption.
2. Choose the Activate button in the form editor.
The Activate button in the tool bar of ADT is not applicable to publish the service. You have to use the
Activate button in the form editor.
Results
The OData service /DMO/UI_FLIGHT_R_V2 is published locally, which means that it is activated in SAP
Gateway. The service is bound to the protocol OData V2 for the category UI. This means it can now be
consumed by a SAPUI5 application.
On the left side of the form editor, the service list with the version and the service definition is filled. The right
side of the form editor shows the service details. It provides a URL to view the metadata of the service and lists
the entity sets that are exposed for the service. The service contains the entities that you have exposed in the
service definition. The service binding editor shows the names that you assigned as alias.
Related Information
Use the URI in the service binding form editor to check the metadata document of the published OData service.
Prerequisites
Context
In the previous steps we defined an OData service and published it. It is now ready for to be consumed by an
HTTP protocol. To verify the data that the OData service exposes, the service offers a metadata document in
which all relevant service elements are listed.
1. If you have not already done so, open the service binding for the relevant service.
2. To open the service document of the OData service, choose the link to the service URL (/sap/opu/
odata/sap/DMO/UI_FLIGHT_R_V2) that is provided in the form editor for the relevant line in the service
details section.
Note
As labels are language dependent, they are only displayed if the language of the browser and the
maintained data elements are in the same language, or if a fallback language matches the browser
configurations.
OData metadata
Note
Depending on your browser and the xml format you choose, the layout of the metadata might differ.
For the described scenario, the following OData annotations are relevant:
○ EntityType: Introduces a CDS entity that is exposed for the service.
sap: label: Provides a semantic description for the entity type. It retrieves the description that was
entered in the wizard for the data definition as no other label is defined.
Name: Specifies the name of the OData entity. It uses the name of the CDS entity and attaches Type. If
an alias is used in the service definition, it uses the alias.
○ Key: Introduces the OData properties that are specified as keys for the OData entities. If the
service is based on CDS entities, it uses the keys of the CDS entities.
Note
The information that is taken from the data elements can be checked in the data definition. Click a CDS
element in the data definition and press F2 . A pop-up opens and you can navigate to all the underlying
elements, displaying the semantic information for the respective OData property.
Next Steps
To check the output of a SAP Fiori UI you can preview the app with the previewing functionality of the service
binding.
Use the preview function in the service binding to check how the UI of a Fiori application looks like.
Prerequisites
Context
The published OData service is ready to be consumed by an HTTP protocol. You can set up a Fiori application
based on this service. The service binding artifact offers a tool which you can use to preview a simple list
reporting Fiori application.
1. If you have not yet already done so, open the service binding for the relevant service.
2. To open the Fiori Elements app preview in the service information section, select the relevant entity set
(Connection) and choose the Preview button.
Results
The Fiori Elements App preview opens in your browser. You see the connection data that you implemented in
the CDS view. The following image displays the list report when selected all available fields.
UI annotations can be used in CDS to configure the look of the user interface of a Fiori App.
To define annotations that concern the UI, we use CDS annotations. CDS offers the option of defining a
universal setup for the presentation and order of items in the CDS layer. This is independent of the UI
technology or application device, which benefits the reuse of one OData service for multiple applications. The
application developer does not have to configure the setting for every application, but can reuse the settings
that were defined in the back end.
Note
You can use metadata extensions to separate the metadata specified by @UI or other UI related
annotations from the actual data definition in the CDS view. See
You are introduced to necessary and useful UI annotations that define the presentation of your data in a UI
service.
In the section List Items [page 35] CDS offers the option to define a universal setup for the presentation and
order of the business data. You will learn how to order and label the columns of your list report.
The second section List Report Header [page 37] deals with the items in the list report header.
The section Object Page [page 38] describes the configuration of an object page and its items.
You can always check the influence of the UI annotations on the UI with the preview option in the service
binding form editor.
The presentation and order of the CDS elements in a SAP Fiori Elements user interface is configured in CDS
with annotations.
Context
You have created an OData service and published it locally. The UI can now be set up with UI annotations in the
CDS layer to define a UI layout independent from the application or the user device. You can always check the
influence of UI annotations by using the preview function in the service binding artifact.
List Items
Context
Using the following annotations, you specify which of the elements appear in the list report when starting the
app. In addition to their order, you can also rename them if you want to display them with a name other than
the name specified in the CDS entity. The columns that are shown in the UI are then predefined and you can
retrieve data by choosing GO without determining the columns to be displayed.
Procedure
1. Open the CDS view for which you want to determine the list report. In our case: /DMO/I_Connection_R.
2. For the headline of the list, use the annotation @UI.headerInfo:typeNamePlural:'name'.
This annotation is an entity annotation because it concerns the whole entity rather than a specific element.
@AbapCatalog.sqlViewName: '/DMO/ICONNECT_R'
@AbapCatalog.compiler.compareFilter: true
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Read-Only E2E: Data Model Connection'
@UI.headerInfo.typeNamePlural: 'Connections'
define view /DMO/I_Connection_R
…
3. Specify a position for each element that you want to show in the list report with the annotation
@UI.lineItem: [ { position:decfloat } ].
The value's number does not represent an absolute measure and works as a relative value to the
positions of the other elements instead. Hence, the elements are arranged in ascending order with
regard to the annotation value.
…
define view /DMO/I_Connection_R
as select from /dmo/connection as Connection
{
@UI.lineItem: [ { position: 10 } ]
key Connection.carrier_id as AirlineID,
@UI.lineItem: [ { position: 20 } ]
key Connection.connection_id as ConnectionID,
@UI.lineItem: [ { position: 30 } ]
Connection.airport_from_id as DepartureAirport,
@UI.lineItem: [ { position: 40 } ]
Connection.airport_to_id as DestinationAirport,
@UI.lineItem: [ { position: 50 } ]
Connection.departure_time as DepartureTime,
@UI.lineItem: [ { position: 60 } ]
Connection.arrival_time as ArrivalTime,
@Semantics.quantity.unitOfMeasure: 'DistanceUnit'
Connection.distance as Distance, //** secondary
information, not to be displayed on list report entry page
@Semantics.unitOfMeasure: true
Connection.distance_unit as DistanceUnit //** secondary
information, not to be displayed on list report entry page
}
4. You can display the elements with a name other than the name specified in CDS by labeling them with the
annotation @UI.lineItem.label: label. In particular, you can label element with names containing
spaces. The label is displayed in the column header of the list report.
…
define view /DMO/I_Connection_R
as select from /dmo/connection as Connection
{
@UI.lineItem: [ { position: 10, label: 'Airline'} ]
key Connection.carrier_id as AirlineID,
@UI.lineItem: [ { position: 20, label:'Connection Number' } ]
key Connection.connection_id as ConnectionID,
@UI.lineItem: [ { position: 30 , label: 'Departure Airport Code'} ]
Connection.airport_from_id as DepartureAirport,
@UI.lineItem: [ { position: 40 , label: 'Destination Airport Code'} ]
Connection.airport_to_id as DestinationAirport,
@UI.lineItem: [ { position: 50 , label: 'Departure Time'} ]
Connection.departure_time as DepartureTime,
@UI.lineItem: [ { position: 60 , label: 'Arrival Time' } ]
Connection.arrival_time as ArrivalTime,
@Semantics.quantity.unitOfMeasure: 'DistanceUnit'
Connection.distance as Distance, //** secondary
information, not to be displayed on list report entry page
@Semantics.unitOfMeasure: true
Connection.distance_unit as DistanceUnit //** secondary
information, not to be displayed on list report entry page
}
The source code specifies which of the elements of the CDS view /DMO/I_Connection_R are displayed in the
list report and in which order. In addition, the list report is given the title Connections. When starting the app,
you do not have to select columns in the settings since they are already displayed. Press the GO button to
retrieve data.
Context
The following annotations specify the items that are shown in the list report header.
You can define a header for the list report or you can implement selection fields on top of the list report to filter
for a specific item. One selection field always refers to one element, but you can have more than one selection
field in a single list report header.
Procedure
To include selection fields for the key elements in the header, use the annotation
@UI.selectionField.position:decfloat on the respective elements.
Note
The value's number does not represent an absolute measure and works as a relative value to the positions
of the other selection fields instead. Hence, the selection fields are arranged in ascending order with regard
to the annotation value.
…
@UI.lineItem: [ { position: 30 , label: 'Departure Airport Code'} ]
Results
The selection field annotation is used on the key elements of the CDS view to create a selection field in the
header on the list report. Using these selection fields, you can filter for specific list items.
Object Page
Context
Whereas the list report gives a general overview of the list items, the object page shows more detailed
information about a single list item. You navigate to the object page by clicking the item in the list report.
Procedure
@AbapCatalog.sqlViewName: '/DMO/ICONNECT_R'
@AbapCatalog.compiler.compareFilter: true
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Read-Only E2E: Data Model Flight'
@UI.headerInfo.typeName: 'Connection'
define view /DMO/I_Connection_R
2. Create a standard facet for the object page with the annotation @UI.facet.purpose: #STANDARD. This
annotation must be in the element section.
A facet is a type of section in the object page. It can contain diagrams or other information in a discrete
part of the user interface.
3. Specify the type of the facet. In our case, the object page displays the detailed information of one list item.
Use the annotation @UI.facet.type: #IDENTIFICATION_REFERENCE.
4. Specify a name for the object page facet header. Use the annotation @UI.facet.label: 'name'.
5. To define the position of the facet, use the annotation @UI.facet.position: decfloat.
An object page of a type identification reference is created. You can now define the elements that are
displayed in the object page.
6. Specify the position and the label for each element that you want to show in the object page. Use the
annotations @UI.identification.position: 'decfloat' and @UI.identification.label:
'name' on each element.
{ …
@UI: { identification:[ { position: 10, label: 'Airline' } ] }
key Connection.carrier_id as AirlineID,
@UI: { identification:[ { position: 20, label: 'Connection Number' } ] }
key Connection.connection_id as ConnectionID,
@UI: { identification:[ { position: 30, label: 'Departure Airport
Code'} ] }
@UI.selectionField: [ { position: 10 } ]
Connection.airport_from_id as DepartureAirport,
The following image displays the object page after clicking the connection item JL 407.
Results
The resulting source code, including all annotations that are relevant for the UI in the data definition, is as
follows:
@AbapCatalog.sqlViewName: '/DMO/ICONNECT_R'
Related Information
The content in Concepts provides background information about the ABAP RESTful Programming Model and
helps you to understand the concepts behind it.
The ABAP RESTful Programming Model has unified the development of OData services [page 958] with ABAP.
It is based on three pillars that facilitate your development.
● Tools: The approach to integrate all implementation tasks in one development environment optimizes the
development flow and offers an end-to-end experience in one tool environment. New development artifacts
support the application developer to develop in a standardized way.
● Language: The ABAP language has been aligned and extended to support the development with the ABAP
RESTful Programming Model, together with CDS. The application developer uses typed APIs for standard
implementation tasks and benefits from auto-completion, element information, and static code checks.
● Frameworks: Powerful frameworks represent another important pillar of the ABAP RESTful Programming
Model. They assume standard implementation tasks with options for the application developer to use
dedicated code exits for application-specific business logic.
Learn how these pillars are incorporated into the architecture of the ABAP RESTful Programming Model in the
following topics.
Design Time
The following diagram structures the development of an OData service from a design time perspective. In other
words, it displays the major development artifacts that you have to deal with during the creation of an OData
service with the ABAP RESTful Programming Model. The diagram takes a bottom-up approach that resembles
the development flow. The main development tasks can be categorized in three layers, data modeling and
behavior, business services provisioning and service consumption.
Hover over the building blocks and get more information and click to find out detailed information about the
components.
Runtime
The following diagram provides a runtime perspective of the ABAP RESTful Programming Model. Runtime
objects are necessary components to run an application. This runtime stack is illustrated in a top-down
approach. An OData client sends a request, which is then passed to the generic runtime frameworks. These
frameworks prepare a consumable request for ABAP code and dispatch it to the relevant business logic
component. The request is executed by the business object [page 945] (BO) when data is modified or by the
query [page 959] if data is only read from the data source.
Hover over the building blocks to get more information and click to navigate to more detailed information about
the components.
The layer of data modeling and behavior deals with data and the corresponding business logic.
Data Model
The data model [page 951] comprises the description of the different entities involved in a business scenario,
for example travel and booking, and their relationships, for example the parent-child relationship between
travel and booking. The ABAP RESTful Programming Model uses CDS [page 948] to define and organize the
data model. CDS provides a framework for defining and consuming semantic data models. These data models
have a physical representation in the database in the form of database views which are automatically created
based on a CDS data model. Every real-world entity is represented by one CDS entity [page 949]. View building
capabilities allow you to define application-specific characteristics in the data model. That means, CDS entities
are the fundamental building blocks for your application. When using the CDS entity for a data selection, the
data access is executed by the SQL-view, which is defined in the CDS entity.
Depending on the use case, data models support transactional access or query access to the database. Thus,
data models are used in business objects [page 945] or queries [page 959] respectively.
The following diagram gives you an overview of the data model that is used in the development guides of this
documentation. Every block refers to one database table view and the respective CDS entity. The blue boxes
represent a Travel business object, with its child entities Booking and Booking Supplement. The white
boxes represent the entities that are not part of the business object, but support with value helps or text
associations. For read-only access to the database, that is simple data retrieval, the data model is used for the
query.
Behavior
The behavior describes what can be done with the data model, for example if the data can be updated.
In transactional scenarios, the business object behavior defines which operations and what characteristics
belong to a business object. For read-only scenarios, the behavior of the data model is defined by the query
capabilities, for example if the data is filterable.
Learn more about the business object and the query in the following topics.
4.1.1 Query
A query is the connecting interface for read-only access to the database in OData services [page 958]. It is
used for list reports or analytical reports to process data.
As the non-transactional counterpart of a business object [page 945], it consists of a data model [page 951],
generic and modeled query capabilities and a runtime. This threefold division is known from the BO concept.
However, a query provides only read access to the database. Its runtime never modifies data, but only executes
structured data retrieval, for example for filtering.
The data model for a query is provided with CDS entities [page 948]. They structure and group database fields
to execute query capabilities on them. The SQL select to retrieve data from the database is generically
integrated in the CDS view.
A query operates on a loose combination of CDS entities. Each entity represents a real-world artifact and
contains the relevant information about it. For example, the information of Flight Connections or Airports is
manifested in CDS entities. The entities are not strictly structured. Their connections, which are modeled with
associations [page 944], only provide a functional relationship. In other words, only if data from other entities is
needed for a certain functionality is the association necessary. In contrast to BO compositions [page 948],
there is no existential relationship for such associations.
Note
Associations with custom entity as source or target support only attribute bindings (A1 = A2 and B1 = B2),
but no:
● OR, NOT
● Other operators than ‘=’
● Using something else than CDS elements as operands (e.g. no literals or variables)
Example
When providing text for ID elements, you need an association to a text providing CDS entity to get the text
from there. The association is only relevant to get information from the text provider. There is no other
structural relationship.
In case of Flight Connections, an association is created to get the information about the long text of the
airport ID in the Airport entity and the full name of the airline in the Carrier entity.
Query Capabilities
Query capabilities provide read access to the database and process data to structure them for a certain output.
In contrast to BO behavior [page 946], the capabilities do not need to be defined in a separate artifact. Some of
the query capabilities which result from OData query options are generically available and applicable. The
query framework provides the SQL statement to retrieve the structured data for these capabilities, for example
in filtering.
Other capabilities are explicitly modeled by the developer in the source code of the CDS entity. These
capabilities depend on associated CDS entities. The application developer has to define this dependency in the
paging search
filtering aggregation
column selections
All of these features do not modify data on the database but process data to structure them for a certain
output.
Query Runtime
The runtime of a query is usually managed by the query framework (SADL [page 960]). The framework takes
into account all query capabilities that are mentioned previously. The application developer does not have to
deal with the construction of the SQL statement to retrieve data from a database table. The data model for the
managed runtime is provided in CDS entity [page 949].
There is also the option to handle the query manually. We speak of an unmanaged query [page 964] in this
case. An unmanaged query can be used, for example, if the data source of a query is not a database table. That
means, the framework cannot provide the SQL statement to access the database. Instead, the application
developer needs to implement every query capability to retrieve the data matching the OData request. For the
unmanaged implementation type, the data model is manifested in a CDS custom entity. In contrast to CDS
views, CDS custom entities [page 949] do not provide an SQL SELECT for the data retrieval from the database.
A query implementation class [page 959] must be implemented to execute the data retrieval.
The following diagram exemplifies the runtime of a managed and an unmanaged query.
For more information about the query runtime, see Query Runtime Implementation [page 49].
Managed Query
The default case for a query is the managed implementation type. In this case, the orchestration framework
manages the data access to the database. Query capabilities, which result from OData query options
($orderby, $top, $skip … ) are considered, as well as possible authorizations, which are derived from
attached access control. The framework creates an SQL statement for the query that is executed based on the
definition in the CDS source code, the query capabilities and the authorizations. For the runtime of the
managed query, the application developer does not have to implement anything. The application development
tasks are limited to defining the data model and the related access controls during the design time.
Example
An OData request with the query option $filter reaches an OData service [page 958]. Once transformed
into an ABAP consumable object, the orchestration framework triggers the query to be executed. Then, the
query framework creates the SQL statement to select the required data from the database. In this case, the
query framework extends the SQL statement with a where clause to only select the data sets that match
the filter condition. In case, access controls are involved, the query framework also evaluates the involved
authorizations.
Unmanaged Query
The unmanaged implementation type for a query is used when the standard SQL push-down by the query
framework is not sufficient or not usable at all.
The unmanaged query is protocol agnostic. That means, like managed queries, it can be reused for multiple
scenarios.
Explanation
The data model for an unmanaged query must be defined in a CDS custom entity [page 949]. A custom entity
defines the structure of the data returned by the query. This is done using CDS syntax in a CDS data definition
(DDLS). A CDS custom entity does not have an SQL view to select data from a database. Instead, the custom
entity specifies an ABAP class that implements the query. The entity annotation
@ObjectModel.query.implementedBy: 'ABAP:...' is used to reference the query implementation class
[page 959] in the data definition of the CDS custom query. This annotation is evaluated when the unmanaged
query is executed whereby the query implementation class is called to perform the query.
Since no SQL artifact is generated for custom entities and the query is implemented in ABAP, custom entities
cannot be used in ABAP SQL or in SQL joins in data definitions [page 951].
@EndUserText.label: 'EndUserText'
@ObjectModel.query.implementedBy: 'ABAP:<Query_Impl_Class>'
[define] [root] custom entity CustomEntityName
[ with parameters
ParamName : dtype [, ...] ]
{
[@element_annot]
A CDS custom entity can have parameters, elements and associations. Like in CDS views, it lists the elements
that are used in the data model. For each element, the data type must be specified as it cannot be retrieved
from an underlying database representation.
A custom entity can be an entity in a business object, for example a root, a parent, or a child entity using root
and composition relationships. Custom entities may also be used as targets in the definition of associations
[page 944] and define associations as a source.
A custom entity cannot be used in ABAP SQL SELECT executions as they to not have a database
representation. In particular, you cannot use elements of an associated custom entity in the element list of the
source CDS entity.
Unmanaged queries are implemented in ABAP classes. The query implementation class [page 959]
implements a predefined ABAP interface (IF_RAP_QUERY_PROVIDER) to ensure that the required basic OData
support is enabled. The interface has a select method which imports an interface instance for the request
data and one for the response data.
Access control needs to be implemented manually in the query implementation class to ensure that only those
records are returned the user is allowed to access. You cannot use an access control object for a custom entity.
In contrast to the managed query, the application developer has to take care for every supported query option
in the query implementation class, including possible authorizations that are also implemented in the query
implementation class.
Example
An example on how to use a CDS custom entity and implement an unmanaged query with the interface
IF_RAP_QUERY_PROVIDER in a query implementation class is given in Implementing an Unmanaged
Query [page 608].
The use case of an unmanaged query in combination with the client proxy is explained in the develop
scenario Developing a UI Service with Access to a Remote Service [page 492].
For more information about the interface IF_RAP_QUERY_PROVIDER, see Unmanaged Query API [page
878].
Note
In RAP, a query is the non-transactional read operation to retrieve data directly from the database.
The following runtime diagram illustrates the main agents' activities when an OData GET (GET
ENTITYSET)request is sent.
Note
This diagram reflects the activities for requesting an entity set (GET ENTITYSET). If you request a single
entity with a key, exceptions that are not depicted in the diagram may arise if, for example, the requested
key does not exist.
Introduction
A business object (BO) is a common term to represent a real-world artifact in enterprise application
development such as the Product, the Travel, or the SalesOrder. In general, a business object contains several
nodes such as Items and ScheduleLines and common transactional operations such as for creating, updating
and deleting business data. An additional application-specific operation in the SalesOrder business object
might be, for example, an Approve action allowing the user to approve the sales order. All changing operations
for all application-related business objects form the transactional behavior in an application scenario.
When going to implement an application scenario based on business objects, we may distinguish between the
external, consumer-related representation of a business object and the internal, provider-related perspective:
● The external perspective hides the intrinsic complexity of business objects. Developers who want to
create a service on top of the existing business objects for role-based UIs do not need to know in detail on
which parts of technical artifacts the business objects are composed of or how runtime implementations
are orchestrated internally. The same also applies to all developers who need to implement a consumer on
top of the business object’s APIs.
● The internal perspective exposes the implementation details and the complexity of business objects. This
perspective is required for application developers who want to provide new or extend existing business
objects for the industries, the globalization and partners.
From a structural point of view, a business object consists of a hierarchical tree of nodes (SalesOrder, Items,
ScheduleLines) where the nodes are linked by special kinds of associations, namely by compositions. A
composition is a specialized association that defines a whole-part relationship. A composite part only exists
together with its parent entity (whole).
Each node of this composition tree is an element that is modeled with a CDS entity and arranged along a
composition path. As depicted in the diagram below, a sequence of compositions connecting entities with each
other, builds up a composition tree of an individual business object.
The root entity is of particular importance in a composition tree: The root entity serves as a representation of
the business object and defines the top node within a hierarchy in a business object's structure. This is
considered in the source code of the CDS data definition with the keyword ROOT.
The root entity serves as the source of a composition which is defined using the keyword COMPOSITION in the
corresponding data definition. The target of this composition defines a direct child entity. On the other hand,
CDS entities that represent child nodes of the business object’s composition tree, must define an association
to their compositional parent or root entity. This relationship is expressed by the keyword ASSOCIATION TO
PARENT. A to-parent association in ABAP CDS is a specialized association which can be defined to model the
child-parent relationship between two CDS entities.
In a nutshell: both, a sequence of compositions and to-parent associations between entities define the
structure of a business object with a root entity on top of the composition tree.
All entities - except the root entity - that represent a node of the business object structure serve as a:
● Parent entity - if it represents a node in a business object's structure that is directly connected to another
node when moving towards the root.
● Child entity - if it represents a node in a business object's structure that is directly connected to another
node (parent node) when moving away from the root.
● Leaf entity - if it represents a node in a business object's structure without any child nodes. A leaf entity is a
CDS entity, which is the target of a composition (a child entity node) but does not contain a composition
definition.
To specify the business object's behavior, the behavior definition as the corresponding development object is
used. A business object behavior definition (behavior definition for short) is an ABAP Repository object that
describes the behavior of a business object in the context of the ABAP RESTful application programming
model. A behavior definition is defined using the Behavior Definition Language (BDL).
A behavior definition always refers to a CDS data model. As shown in the figure below, a behavior definition
relies directly on the CDS root entity. One behavior definition refers exactly to one root entity and one CDS root
entity has at most one behavior definition (a 0..1 cardinality), which also handles all included child entities that
are included in the composition tree. The implementation of a behavior definition can be done in a single ABAP
class (behavior pool) or can be split between an arbitrary set of ABAP classes (behavior pools). The application
developer can assign any number of behavior pools to a behavior definition (1..N cardinality).
A behavior specifies the operations and field properties of an individual business object in the ABAP RESTful
programming model. It includes a behavior characteristic and a set of operations for each entity of the
business object’s composition tree.
Behavior Characteristic
Behavior characteristic is that part of the business object's behavior that specifies general properties of an
entity such as:
Authorizations.
Apart from draft capabilities, these characteristics can be defined for each entity separately.
Operations
Each entity of a business object can offer a set of operations. They can cause business data changes that are
performed within a transactional life cycle of the business object. As depicted in the diagram below, these
modify operations include the standard operations create(), update() and delete() as well as lock
implementations and application-specific operations with a dedicated input and output structure which are
called actions. Another kind of operations are the read operations: they do not change any business data in the
The first part is the interaction phase, in which a consumer calls the business object operations to change
data and read instances with or without the transactional changes. The business object runtime keeps the
changes in its internal transactional buffer which represents the state of the instance data. This transactional
buffer is always required for a business object. After all changes were performed, the data can be persisted.
This is realized with the save sequence.
For each operation the transactional runtime is described in detail in the respective runtime diagrams:
The save sequence has the same structure for each operation. For more information, see Save Sequence [page
122].
4.1.2.1.1 Draft
You can draft-enable business object to automatically persist transactional data in the backend. This approach
supports stateless communication for your applications.
Modern cloud-ready apps require a stateless communication pattern, for example to leverage cloud
capabilities like elasticity and scalability. Thus, there is no fixed backend session resource along a business
The draft concept fills the gap between a stateless communication pattern and a stateful application by
applying REST principles:
● The draft represents the state and stores the transactional changes on the database in shadow tables. It is
an addressable resource, the exact copy of the active data that is currently being edited.
● Between two backend roundtrips, there is no running ABAP session waiting for the next roundtrip. The
execution might even be performed on different backend servers.
Draft-enabled applications allow the end user to store changed data in the backend and continue at a later
point in time or from a different device, even if the application terminates unexpectedly. This kind of scenario
needs to support a stateless communication and requires a replacement for the temporary in-memory version
of the business entity that is created or edited. This temporary version is kept on a separate database and is
known as draft data. Drafts are isolated in their own persistence and do not influence existing business logic
until activated.
In general, the draft implies the following advantages for your application:
Applications usually have a transactional state that buffers all changes of a current transaction until it is
committed by the application user. If the buffer state is consistent, the commit is accepted, if not, the whole
transaction is rejected. This all-or-nothing-approach prevents inconsistent data on the database table.
For smaller applications, sending the whole transactional state to the backend is unproblematic. The state can
be managed completely on the frontend side. We speak of frontend state handling in that case. However, in
case of complex backend-located business logic (especially legacy code), configuration, and data volume,
transferring everything to the frontend layer does not work for performance, maintenance, and security
reasons. Thus, backend state handling (draft) is required.
Basic Principles
● The main business logic is implemented on the active entity, for example with actions or feature control.
This behavior can be applied to draft entities in the same manner.
● There can be only up to one draft instance for each active instance at the same time.
● The primary key value of a draft and the corresponding active instance is the same.
Exclusive Draft
There are several approaches how draft instances that are created by different users can be accessed. The
current version of the ABAP RESTful Application Programming Model only supports the exclusive draft. A draft
exclusively belongs to the user that created the draft. Only this user is able to see and process the draft
instance.
The exclusive draft goes hand in hand with the exclusive lock. As soon as a user starts working on a draft, it sets
an exclusive lock. It is locked for other users. The exclusiveness is maintained for a defined period of time. After
that period, the draft goes into an optimistic lock phase. In this phase, the draft can either be resumed by the
same user or discarded, if other users take over the exclusive draft handling. For more information, see Locking
in Draft Scenarios [page 132]
Draft is an option that you can use for application development with both, the managed and unmanaged
implementation type and also with mixed scenarios, for example, managed with unmanaged save. In all
scenarios, the draft is managed. That means, it is handled by the RAP runtime framework. You, as an
application developer, do not need to care about how draft data is written to the draft database table. This is
done for you. Of course, adding draft capabilities to your business object might imply changes in your business
logic also for the processing of active data that you are responsible of. In addition, RAP also offers
implementation exits for cases in which you need business service-specific draft capabilities that impact the
draft handling.
Given the fact that the runtime of a draft business object differs significantly from a non-draft business object,
it comes with no surprise that there are also quite some differences with regard to the design time of a draft
business object.
The addition with draft in the behavior definition defines a draft business object. As soon as the business
object is draft-enabled with this syntax element, you have various other options to use draft capabilities on
certain actions and operations. For more information, see Draft Business Object [page 63].
As draft instances are stored independently of active instances, a separate database table, the draft table,
must be created. This draft table must be explicitly stated in the behavior definition and can be generated from
there. For more information, see Draft Database Table [page 64].
To control the state of the active BO data, you use the total ETag, which must also be defined in the behavior
definition. For more information, see Total ETag [page 65].
To enable that an association retrieves active data if it is followed from an active instance and draft data if it is
followed from a draft source instance, the associations must be draft-enabled. For more information, see Draft-
Enabled Associations [page 68].
The draft capability for a draft business object is defined in the behavior definition.
You can build draft business objects from scratch, or you can draft-enable existing business objects with both
implementation types managed or unmanaged. The draft-indicating syntax element with draft is added at
the top of the behavior definition as it does not belong to a certain entity of the BO, but concerns the whole BO.
You cannot implement draft capabilities for single BO entities.
In draft business objects, the handling of the draft instances is always managed by the RAP runtime
framework, no matter if your business object is unmanaged or managed. That means, the draft life-cycle is
determined by the RAP draft runtime and specific draft actions are implicitly available for the draft business
object.
Runtime Aspects
If you use %tky to address the application key components of an entity, you do not have to change your
business logic implementation when draft-enabling the business object. The derived type component %tky
automatically includes the draft indicator %IS_DRAFT for draft business object to distinguish draft instances
If you use %key in your business logic implementation, you have to revise the complete implementation when
draft-enabling the business object. This derived type component only comprises the application key
component. In this case, the runtime is not able to distinguish between draft and active instances.
Note
The recommendation is to only use %tky in your business logic implementation for both, active-only and
draft business object.
Draft business objects need two separate database tables for each entity. One for the active persistence and
one for storing draft instances.
The draft data is stored on a separate draft table to match the different access patterns of draft and active
data. While the active database table stores many instances, but rarely has access on these instances, it is vice
verse for the draft table. With two separate tables, the performance can be adapted to the different approaches
of the two tables more easily. During runtime, every request is marked whether it is intended for the draft table,
or for the active with the draft indicator IS_DRAFT.
With using a separate database table for the draft information, it is guaranteed that the active persistence
database table remains untouched and thus consistent for existing database functionality.
Note
It is not allowed to directly access the draft database table via SQL, neither with reading access nor writing
access. The access to the draft database table must always be done via EML, with which the draft
metadata get updated automatically.
The draft database table contains exactly the same fields as the active database table plus some technical
information the RAP runtime needs to handle draft. The technical information is added with the draft admin
include.
Draft Table
...
define table DraftTable {
key client : abap.clnt not null;
key ...
...
"%admin" : include sych_bdl_draft_admin_inc;}
...
define structure sych_bdl_draft_admin_inc {
draftentitycreationdatetime : sych_bdl_draft_created_at;
draftentitylastchangedatetime : sych_bdl_draft_last_changed_at;
draftadministrativedatauuid : sych_bdl_draft_admin_uuid;
draftentityoperationcode : sych_bdl_draft_operation_code;
hasactiveentity : sych_bdl_draft_hasactive;
The draft table can be generated automatically via a quick fix in the behavior definition, which is offered as soon
as the business object is draft-enabled with the syntax with draft. In case the draft database table does
already exist, the quickfix completely overwrites the table.
Whenever you change the active data model in the active database table, you have to regenerate the draft table
to align them. The behavior definition offers a quick fix for regeneration, but it is the responsibility of the
application developer to keep the two database tables synchronized. When regenerating the draft database
table it completely overwrites the existing one.
The total ETag is a designated field in a draft business object to enable optimistic concurrency checks during
the transition from draft to active data.
For draft business objects, it is recommended to use a total ETag. This designated field on the database table is
updated by the RAP runtime framework as soon as an active instance is changed. The total ETag always refers
to the complete BO. As soon as an instance of any BO entity is changed the total ETag is updated. Its value is
compared when resuming a draft instance to ensure that the active data has not been changed after the
exclusive lock has expired. Thus, the total ETag is important for optimistic concurrency control during the
transition from draft to active data. The total ETag is defined in the behavior definition.
The total ETag field is defined on the lock master entity (currently identical to root entity) and optimistically
controls the whole business object regarding concurrency on the active data.
Note
Total ETag and ETag master/dependent are two sides of the same medal. For a smoothly running
application, you need both ETags.
The total ETag is used for edit-drafts, a draft that has a corresponding active instance. As soon as the
exclusive lock expires and an edit-draft is resumed, the total ETag value of the draft instance is compared to
the total ETag value on the active instance. Only if the values coincide can the draft be resumed. The total
ETag is compared for all entities of a BO.
The ETag master/dependent concept ensures that the end user of a UI service only changes instances with
the state that is displayed on the UI. With ETag master, each BO entity can be checked independently.
Draft actions are actions that are implicitly available for draft business objects as soon as the business object is
draft-enabled. They can, but do not have to be explicitly declared in the behavior definition.
Draft actions can only be specified for lock master entities, as they always refer to the whole lockable subtree
of a business object.
Syntax
Draft actions are needed for specific occasions in business services with draft.
The draft action EDIT is automatically exposed to OData with the name <lock_master_entity_name>Edit,
even without explicitly mentioning it in the behavior definition.
In contrast to the draft action Edit, the Activate does not allow feature or authorization control.
Authorization is not as it only transfers the state of the draft buffer to the active buffer. Authorization is
controlled when the active instance is saved to the database.
The draft action ACTIVATE is automatically exposed to OData with the name
<lock_master_entity_name>Activate even without explicitly mentioning it in the behavior definition.
For more information, see Discarding Root Draft Instances [page 75].
In the behavior definition, you must specify which determinations and validations are called during activation.
Only determinations and validations that are defined and implemented for the BO can be used.
The draft action PREPARE is automatically exposed to OData with the name
<lock_master_entity_name>Prepare even without explicitly mentioning it in the behavior definition. No
validations or determinations are called if there is nothing specified for the PREPARE.
For more information, see Preparing Draft Instances for Activation [page 77].
In case of a new draft, the same feature and authorization control is executed as defined for a CREATE. In case
of an edit-draft, the same feature and authorization control is executed like in an Edit.
As the RESUME action is application-specific, it is only exposed to OData if it is explicitly declared in the
behavior definition. It uses the name <lock_master_entity_name>Resume. You can only execute the
RESUME action via EML if the action is explicitly made available in the behavior definition.
If you want to implement your own logic for the resume action, it must be implemented by the application
developer in the related behavior implementation class, just like any other action, see Action Implementation
[page 117].
For more information about the Resume, see Resuming Locks for Draft Instances [page 78].
A draft-enabled association retrieves active data if it is followed from an active instance and draft data if it is
followed from a draft source instance.
The intended behavior for all associations within the composition tree of a draft business object is to be draft-
enabled, so that the associations always lead to the target instance with the same state (draft or active).
Example
On creating a child instance of a draft root instance by a CREATE_BY_ASSOCIATION, you want to create a
draft instance. Creating an active child instance would lead to BO-internal inconsistencies. That is why, all
compositions are automatically draft-enabled.
As soon as you draft-enable a business object by adding with draft to the behavior definition, all BO-internal
associations are automatically draft-enabled. To make this behavior explicit, the behavior prompts you to
specify the compositions within a draft BO with with draft.
RESTful applications require a constant stateless communication with the backend to prevent that transient
data is lost when a user session ends. Data changes are constantly persisted on the draft database tables and
are therefore always retrievable from any device. The fact that two database tables are involved in the runtime
of a draft business object requires an elaborate lifecycle of data. All data undergo several states while being
processed by a draft business object. There are three distinct states that need to be distinguished.
For initial data that is not yet persisted on the active database table, we speak of new-draft instances. New-
draft instances do not have a corresponding active instance. They are created using the modify CREATE
operation having the draft indicator %IS_DRAFT set to true. The draft indicator is automatically available as
derived type when working with the transactional key %tky.
As soon as a draft instance is transferred from the draft database table to the persistent table, the draft
instance is activated. The data that is stored on the persistent database table is called active data. Active
instances come into being by activating new-draft instances or by using the modify CREATE operation and
setting the draft indicator %IS_DRAFT to false.
Edit-drafts always exist in parallel to the corresponding active data. Whenever active data is edited, the whole
active instance is copied to the draft table. All modifications are saved on the draft database table before the
changes are finally applied to the active data on the active database table again. Edit-draft instances are
created by using the EDIT action on active instances.
The following diagram illustrates the lifecycle of draft and active data and their transitions from one state to the
other.
When working with draft instances, you always set an exclusive lock for this instance. In case of an edit-draft,
that means that the corresponding active instance cannot be modified by any other user. In case of a new-
draft, the primary key number is locked exclusively to avoid duplicate key failure on the database. For the time
of the exclusive lock phase, it is not possible to create neither an active, nor a draft instance with the same key.
After the exclusive lock has expired, the draft is in an optimistic lock mode, in case the user starts processing
again, the draft resume action is automatically invoked. It locks the corresponding active instance again,
compares the total ETag and if successful, the draft can be continued in exclusive mode.
You can always send requests for draft instances or active instances. Accessing draft instances via EML or
OData works exactly like accessing active instances. To distinguish if the requests are aimed at draft or active
instances, the RAP runtime framework evaluates the draft indicator %IS_DRAFT. This draft indicator must be
set for every request, which can be processed on active or draft instances. In OData, this technical primary key
component is mapped to isActiveInstance.
On a UI, modifications of data in a draft BO always take place on the draft instance (except for actionss). Only in
case of activation via the activate action are the changes applied to the corresponding active instance. The
transition from draft to active data and vice versa requires specific actions that process tasks that are not
relevant in a non-draft business object . These actions are explained best when considering the lifecycle of a
draft and active data in detail.
As the preceding diagram suggests, there are two options to create draft data:
By using the modify operation for CREATE, you create a new-draft instance, a draft instance that has no
corresponding active instance. For such a create request, the draft indicator must be set to true. Like a
CREATE for active instances, the RAP runtime sets an exclusive lock for the instance and triggers any
determination on modify with trigger CREATE. On creating a new instance the uniqueness of the primary keys
must be ensured. Otherwise, if duplicate keys reach the active database table, the whole request is denied. For
more information about an early uniqueness check, see Uniqueness Check [page 82].
Via EML, however, it is possible to distinguish requests to create draft data and requests to create active data.
To create draft instances, the draft indicator must be set to true:
Edit Action
By executing the draft action EDIT on an active instance, you create an edit-draft instance, a draft instance that
has a corresponding active instance on the persistent database. An edit action creates a new draft document
by automatically copying the corresponding active instance data to the draft table. At the same time, the EDIT
triggers an exclusive lock for the active instance. This lock is maintained until the durable lock phase of the
draft ends, which is either when the draft is activated, or when the durable lock expires after a certain time.
The edit action has a parameter with which you can determine whether editing an active instance for which a
draft already exists is possible or not. Having set the parameter preserve_canges to true, your action
request is rejected if a draft already exists. If preserve_changes is set to false (default setting), the edit
action is executed. That means, the active instance is copied to the draft database table and the existing draft
is overwritten with the values of the active data. In that case, you lose the changes that anyone has done on the
existing draft instance. Hence, it is recommended to always include the parameter in your action requests.
For edit action requests, the draft indicator does not have to be set, as the EDIT is always executed on the
active instance.
Instance authority and feature control are both checked for the action. To implement application-specific
controls, define the EDIT action in the behavior definition and implement the method for features or for
authorization in the behavior pool.
On a Fiori Elements UI, the edit action is triggered when choosing the Edit button, which is only available on the
object page of an active instance. Triggering the edit action from the UI always includes the parameter
preserve_changes set to true.
In this case, OData sends a POST request for action execution of the EDIT, which is then carried out by the RAP
runtime framework.
The edit action can be executed via EML, just like any other business logic action:
You create active data by activating draft data or by directly creating an active instance with a modify CREATE
request.
Creating a new active instance for a draft BO from scratch is not possible from a Fiori Elements UI. Every create
request (OData POST) is sent without the OData draft indicator IsActiveEntity. This is interpreted as false
and the RAP runtime framework creates a new-draft instance.
Via EML, however, it is possible to distinguish modify CREATE requests for active instances and for draft
instances. The syntax for directly creating an active instance via EML is the following:
The runtime of a modify request for active instances can be seen in Create Operation [page 103].
Note
When saving the data from the application buffer on the persistent database table via COMMIT ENTITIES
or BO-internal triggers of the save sequence, validations are executed and can prevent modification
requests if the data is not consistent, see Save Sequence [page 122].
By executing the draft action ACTIVATE on a draft instance, you copy the draft instance to active application
buffer. It invokes the PREPARE and a modify request to change the active BO instance according to the state of
the draft instance. Once the active instance is successfully created or updated, the draft instance is discarded.
The activate action does not save the active instance on the database. The actual save is executed separately,
either by COMMIT ENTITIES via EML or by calling the save sequence in case of OData.
Note
If validations that are called by the PREPARE return failed keys and messages, these messages will also be
returned in REPORTED of the activate action. However, the ACTIVATE itself does not fail in this case, as it
just transfers the draft instance to the active buffer. The failed keys of the validations finally prevent the
save to the active database table during the save sequence, when the validations are called again.
On a Fiori Elements UI, the activate action is triggered when choosing the Save button after editing data. The
Save button also triggers the save sequence after the activation.
For executing the ACTIVATE via EML, the following syntax is used:
Note
The modify request to adapt the active instance according to the state of the draft BO-instance entails
every operation that was done on the draft instance.
In case of an unmanaged implementation scenario, it is just one modify request that is passed to the active
unmanaged provider. This can lead to problems if a child draft instance is deleted; and in the same logical
unit of work, a new child instance is created with the same key. As the order of the modify requests for
delete and create by association is not determined, it can happen that the unmanaged provider executes
the CREATE_BY_ASSOCIATION before the child instance with the same key is deleted. This scenario raises
a short dump during runtime. To prevent this, combine the handler methods for delete and create by
association in one handler method in unmanaged scenarios with draft. In this single handler method,
strictly define the order so that first, the DELETE is executed and then the CREATE_BY_ASSOCIATION.
You execute actions on, update, or read instances of a draft BO by sending a modify or read request. The draft
indicator decides whether the active or the draft instance is processed.
Fields of draft BO instances are changed by the modify request for UPDATE. Depending on the draft indicator,
the request changes the active or the draft instance.
On a Fiori Element UI, the UPDATE with the draft indicator set to true is called whenever input fields on the UI
are filled. After leaving the input field, the modify request for UPDATE is sent and the draft save sequence is
triggered to save the draft on the draft database table.
The UPDATE with the draft indicator set to false is called by the activate action, after the prepare action is
executed on an edit-draft instance. Hence, the modify UPDATE on active entities is hidden under the
functionality of the Save button, see Activate Action [page 73].
Via EML you determine whether you want to update an active or a draft instance by setting the draft indicator
%IS_DRAFT = if_abap_behv=>mk-on/off.
Each modify request for UPDATE, no matter if aimed at draft or active instances, goes through the same
runtime process as in scenarios without draft, including locking, determination triggers etc. For more
information about the runtime orchestrations, see Update Operation [page 105].
Actions on draft business objects are executed by modify requests for action execution. Depending on the draft
indicator, the action is executed on the active or the draft instance.
A Fiori Elements UI sends requests for action execution whenever you choose an action button that is defined
for an application-specific action. Depending on whether this is done on a draft or an active instance, the draft
indicator is set to true or false.
Each modify request for action execution, no matter if aimed at draft or active instances, goes through the
same runtime process as in scenarios without draft, including locking, determination triggers etc. For more
information about the runtime orchestrations of actions, see Action Runtime [page 120].
Static actions are always delegated to the active provider. Static actions contain an additional parameter that
indicates whether the action produces a draft or an active instance.
Business object instances of a draft BO are read by a read request. Depending on the draft indicator, the
request reads the active or the draft instance.
In the reading request via EML, set the draft indicator to determine whether active or draft instances are read.
On a Fiori Elements UI, the transactional read operation is always used when buffer information is relevant for
the next operation. The draft indicator decides whether the draft or the active instance is read from the buffer.
You delete instances of a draft BO by using the modify request for delete on active instances or the discard
action for draft instances.
By executing the draft action, DISCARD you delete the draft instance from the draft database table. In addition,
possible exclusive locks are released. The DISCARD can be executed on new-drafts and on edit-drafts. Apart
from releasing the lock, the action has no impact on the active instance. It is just the draft data that is deleted.
Via EML you discard draft instances with the following syntax:
If a DELETE is executed on a new-draft, for which no active instance exists, the DELETE is internally
transformed into a DISCARD.
As in non-draft business objects, persisted data can be deleted by using the standard operation DELETE.
On a Fiori Elements UI, the DELETE is called when choosing Delete button. If you choose Delete for an instance
that has an active and a draft representation, the UI sends a request for DELETE on the active instance and one
for DELETE on a draft instance. The DELETE on the draft instance is BO-internally considered as a DISCARD.
In general, via EML, you can indicate if you want to delete the active instance or the draft instance of an entity.
In case of a DELETE on a draft instance, the discard action is executed.
Each modify request for DELETE goes through the same runtime process as in scenarios without draft,
including locking, determination triggers etc. For more information about the runtime orchestrations,
seeDelete Operation [page 107].
Deleting an active child instance, with a delete request that has the draft indicator set to true, directly deletes
the active child instance without affecting the other entities of the business object.
Deleting draft child instances, with a delete request that has the draft indicator set to false, deletes the draft
child instance. Once the root entity, and with it all related child entities, is activated, the active child instance is
deleted as well. During activation, it is the state of the whole composition tree that is passed to the active
buffer. The activate action provides the suitable modify request for the changes that are done on the draft BO.
Before draft instances become active, they are checked by the validations and determinations that are
specified for the draft determine action prepare in the behavior definition.
The draft determine action PREPARE is executed automatically at least once before draft data is activated
during the activate action. It ensures that the draft instance is consistent by calling the validations and
determinations on save that are specified for the prepare action in the behavior definition. The prepare action
can only be executed on draft instances.
On a Fiori Elements UI, the action is invoked by choosing Save on a draft instance. The UI then sends the
request for PREPARE before executing the action ACTIVATE.
Note
When choosing Save on the UI, the prepare action is executed at least twice. First, the UI requests the
PREPARE directly, and then it is called during the execution of the activate action.
Via EML the prepare action is executed with the following syntax:
As the PREPARE can only be executed on draft instances, the draft indicator cannot be set for the action.
The prepare action only fails if the instance with the given key is not found. If the validations that are called by
the PREPARE detect inconsistencies, the issues are only written in the REPORTED table, but the FAILED table
remains empty. A possible COMMIT ENTITIES after the prepare action succeeds if the PREPARE does not
return any failed keys, even if the validations within the PREPARE fail.
Note
Only transition messages from validations are handed over to the REPORTED table of the prepare action,
state messages are not.
The draft action resume is called automatically if the work on a draft instance with an expired lock is resumed.
The draft action RESUME is executed automatically whenever there is an update on a draft instance whose
exclusive lock has expired. It re-creates the lock for the corresponding active instance on the active database
table.
On a Fiori Elements UI, the action is invoked when the user chooses a draft instance and continues with editing
data if the exclusive lock is expired. This is the case when the user chooses an own draft and continues with
editing the input fields. An UPDATE on the draft instance is sent, which invokes the resume action if the lock has
expired.
Note
If the user chooses a foreign draft and chooses the Edit button to edit any input fields, it is only possible to
discard the foreign draft completely and start with a new draft based on the current active data. In this
case, the user gets a popup with the warning that any changes done by the other user are lost.
Via EML the resume action is executed with the following syntax:
Note
As the RESUME action is application-specific, it is only exposed to OData if it is explicitly declared in the
behavior definition. It uses the name <lock_master_entity_name>Resume. You can only execute the
RESUME action via EML if the action is explicitly made available in the behavior definition.
When resuming draft instances it must be ensured that the primary keys of the instance are still unique. It
might have happened that other users created an instances with the same primary keys during the optimistic
lock phase. For more information, see Uniqueness Check [page 82].
Persisting business object instances is done during the save sequence. Draft instances are automatically saved
on the draft database by the RAP runtime framework.
Depending on the state of the instance you want to save, the SAVE is done on the draft database table or the
active database table. Active instances are saved on the active database tables; draft instances are saved on
the draft database table.
Via EML, the statement COMMIT ENTITIES triggers the process to save the data on the database. It saves the
state of the buffer, draft instances to the draft database table and active instances to the active database table.
For active instances, the save sequence is triggered. For draft instances, only the actual save to the database is
done. No validations or determinations on save are called.
On a Fiori Elements UI, the save sequence is triggered by choosing the Save button on the UI. After the
PREPARE and the ACTIVATE are executed successfully, the save sequence is triggered automatically.
On a Fiori Element UI, the state of the application buffer is saved to the draft database table whenever the input
fields are edited. This is done for each field separately. That means, after leaving one input field, the data is
transferred to the draft database table. The UI displays the message Draft saved after each roundtrip.
4.1.2.1.2 Numbering
Numbering is about setting values for primary key fields of entity instances during runtime.
There are various options to handle the numbering for primary key fields depending on when (early or late
during the transactional processing) and by whom (consumer, application developer, or framework) the
primary key values are set. You can assign a numbering type for each primary key field separately. The
following options are available:
The numbering type early numbering refers to an early value assignment for the primary key field. In this case,
the final key value is available in the transactional buffer instantly after the MODIFY request for CREATE.
The key value can either be given by the consumer (externally) or by the framework (internally). In both cases,
the newly created BO instance is clearly identifiable directly after the CREATE operation has been executed. It
can be referred to by this value in case other operations are executed on this instance during the interaction
phase (for example an UPDATE). During the save sequence, the newly created BO instance is written to the
database with the primary key value that was assigned earlier on the CREATE operation.
External Numbering
We refer to external numbering if the consumer hands over the primary key values for the CREATE operation,
just like any other values for non-key fields. The runtime framework (managed or unmanaged) takes over the
value and processes it until finally writing it to the database. The control structure for the CREATE operation is
flagged with true for the primary key field.
In this scenario, it must be ensured that the primary key value given by the consumer is uniquely identifiable.
New instances with an already existing primary key value are rejected during the save sequence. The ABAP
RESTful Application Programming Model supports uniqueness checks in early stages of the interaction phase.
For more information, see Uniqueness Check [page 82].
Implementation
For external numbering, you must ensure that the primary key fields are not read-only at CREATE. Otherwise,
the consumer cannot provide any value for the primary key field. The RAP framework offers dynamic primary
key handling for this scenario. To ensure that the primary key fields are filled when creating new instances, but
...
define behavior for Entity [alias AliasedName]
{ ...
field (mandatory:create| readonly:update);
...}
Internal Numbering
In scenarios with internal numbering, the runtime framework assigns the primary key value when creating the
new instance. Internal numbering can be managed by the RAP runtime or unmanaged, that is implemented by
the application developer.
When using managed numbering, a UUID is drawn automatically during the CREATE request by the RAP
managed runtime.
Note
Managed early numbering is only possible for key fields with ABAP type raw(16) (UUID) of BOs with
implementation type managed.
Implementation
Managed numbering is defined in the behavior definition. The key field must be read only.
For more information, see Automatically Drawing Primary Key Values in Managed BOs [page 579].
Unmanaged Numbering
When using unmanaged numbering, the application developer has to implement the numbering logic in
application code that is called during the CREATE operation.
● In unmanaged BOs, the CREATE operation implements the assignment of a primary key value during the
CREATE modification.
Implementation
The assignment of a new number must be included in your application code.
For more information, see Implementing the CREATE Operation for Travel Instances [page 341].
With the current version of the ABAP RESTful Programming Model, it is not possible to use unmanaged
numbering in managed BOs.
We refer to optional external numbering if both, external and internal numbering is possible for the same BO.
If the consumer hands over the primary key value (external numbering), this value is processed by the runtime
framework. If the consumer does not set the primary key value, the framework steps in and draws the number
for the instance on CREATE.
Note
Optional external numbering is only possible for managed business objects with UUID keys.
Use cases for optional external numbering are replication scenarios in which the consumer already knows
some of the UUIDs for specific instances to create.
Implementation
Optional external numbering is defined in the behavior definition. The key field must not be read only, so that
the consumer is able to fill the primary key fields.
Related Information
A uniqueness check ensures the new primary keys are unique before the saving of instances with these
primary keys are rejected by a database table.
The persistent database table rejects any entry with a key that already exists. To avoid that the work of an end
user is in vain if all the changes are rejected by the database, the uniqueness of the primary key values of new
or resumed instances must be checked as early as possible. Hence, on creating new draft data, the application
must ensure that the primary keys are unique.
Internal Numbering
In scenarios with internal numbering, the uniqueness is usually given by the process of determining the
number, for example by a number range object. In scenarios with internal managed numbering, the RAP
runtime framework ensures the uniqueness as it provides unique UUID values.
External Numbering
In scenarios with external numbering, the uniqueness must be checked explicitly. In some cases, it is possible
for the RAP runtime framework to do the check. For other cases, you must implement a precheck to check the
keys before the actual instance is created and provide an implementation for the resume action in scenarios
with draft support.
Managed Scenario with Draft without Unmanaged Lock ● for active instances: RAP runtime framework
● for draft instances: Application developer needs to im
plement a precheck and the draft resume action.
Managed Scenario with/without Draft with Unmanaged Lock Application developer needs to implement the uniqueness
check in the precheck and the draft resume action.
Unmanaged Scenario with/without Draft Application developer needs to implement the uniqueness
check in the precheck and the draft resume action.
After the uniqueness is checked successfully, the RAP runtime framework exclusively reserves the primary key
value for the corresponding draft instance for the time of exclusive lock phase. After the exclusive lock, once
the draft is resumed, the uniqueness check must be executed again.
Related Information
The numbering type late numbering refers to a late value assignment for the primary key fields. The final
number is only assigned just before the instance is saved on the database.
To identify a newly created instance for internal processing before the save sequence, a temporary identifier,
the preliminary ID (%PID) is assigned. It works as a substitute as long as there is no final key value. The
instance is referred to by the PID in case other operations are executed on this instance during the interaction
Late numbering is used for scenarios that need gap-free numbers. As the final value is only set just before the
SAVE, everything is checked before the number is assigned.
In contrast to Early Numbering [page 80], late numbering can only be done internally, as a consumer cannot
interfere anymore during the save sequence.
With the current version of the ABAP RESTful Programming Model, it is not possible to use late numbering in
managed scenarios.
Late numbering must be defined for each node in the behavior definition of an unmanaged BO.
implementation unmanaged;
define behavior for Entity [alias AliasedName]
late numbering
...
{ ...
}
In addition, the Method ADJUST_NUMBERS [page 870] must be implemented to assign a final number for
each instance.
Related Information
This topic is about the concept of feature control for the ABAP RESTful application development.
With feature control, you can provide information to the service on how data has to be displayed for
consumption in a SAP Fiori UI, for example if fields are mandatory or read-only.
You can implement feature control in a static or dynamic way. In a static case, you define which operations are
available for each business object entity or which fields have specific access restrictions like being mandatory
or ready-only. In a dynamic case, the access restrictions for fields or the enabling or disabling of methods
depends on the state of the business object, for example on the value of a specific field.
● Instance Feature Control: You can define feature control on instance level for fields, operations, and
actions. With this, you can control, for example, if a method is enabled or disabled when a business object
has a certain state. You can define instance-based feature control for UPDATE and DELETE, as well as for
actions. Since you need an active instance of a business object in this case, the CREATE operation can't
have feature control on instance level.
[implementation] managed;
define behavior for CDSEntity [alias AliasName]
implementation in class ABAP_ClASS [unique]
...
{
/* (1) Feature control at entity level */
/* Static operation control*/
internal create
internal update
internal delete
/* or (instance-based) dynamic operation control: implementation required! */
update (features: instance);
delete (features: instance);
_association {create (features:instance); }
/* (2) Feature control at field level */
/* Static field control */
field (read only | mandatory) f1[, f2, ..., fn];
/* or dynamic field control: implementation required! */
field (features: instance) f1[, f2, ..., fn];
/* (3) Feature control for actions */
/* Static action control */
internal action ActionName [...]
/* or dynamic action control: implementation required! */
action ( features: instance ) ActionName [... ]
}
You can define specific access restrictions for each field. Depending on your requirements, you can implement
the restrictions either in a static way if the access restriction is always valid for each instance or in a dynamic
way if the restrictions depend on a certain operation or condition.
Within the behavior definition, you can specify individual fields of an entity that have certain access
restrictions.
Syntax
You can use the following field properties to define static field control:
Field Properties
Property Effect
field (readonly) This property defines that values of the specified fields must not be created or updated by
the consumer. You can set this property in the behavior definition.
Note
The BO runtime rejects external EML MODIFY requests that include read-only fields for
update or create operations.
Read-only fields that are included in OData call to update or create instances are ig
nored while possible other fields are processed for update and create operations.
The specified fields must be filled by the consumer when executing modifying requests. For
the relevant fields, the value must be provided in CREATE operations. In update operations,
it must not be given the null value.
Note
The mandatory property must be set in the behavior definition, not in the projection.
This property isn’t evaluated if you've defined it in a projection.
Caution
Note that there’s no implicit validation by the business object framework. As an applica
tion developer, you must ensure that you’ve implemented a corresponding validation
validation [page 965].
With dynamic feature control, you can add access restrictions based on conditions or specify access
restrictions for CREATE and UDPATE of fields.
Syntax
Property Effect
field (features:instance) For defining dynamic field control, the option (features:
instance) must be added to the field in question. In this
case however, the implementation of dynamic feature
control in the referenced class pool ABAP_CLASS is
required. When implementing dynamic field control, you
have the option of specifying the following values for each
field that is notated with ( features: instance ):
Related Information
Here we explain how you can implement static feature control in the behavior model of a business object for
fields.
For the business object from our flight demo application, the static field control is used to restrict properties of
particular fields. Here, the fields that are derived from system data are defined as read-only and can’t be
created or updated by the consumer. All fields that are required for creating a travel are defined as mandatory
in the behavior definition so that the fields must contain a value in create and modifying requests. In this case,
the data is displayed after the CREATE and remains read-only in modify requests.
Read-Only
projection;
Mandatory fields are marked with a * on the user interface and read-only fields can't be changed on the UI. For
a fully implemented example, refer to Modeling Static and Dynamic Feature Control [page 225].
Related Information
In a typical transactional scenario, you have to specify which operations are provided by the respective entity.
The transactional character of a business object is defined in the behavior definition where all supported
transactional operations are specified for each node of the business object’s composition tree. Whenever the
corresponding root or child entity is going to be created, updated, or deleted, these operations must be
declared in the behavior definition. In this way, you specify at the business object entity level whether each
instance is enabled for creation, update, or deletion. For more general information about operations, refer to
Operations [page 102].
internal update;
internal delete;
...
}
Operation Effect
create Specifies that new instances of a business object node that correspond to the underlying (root or
child) entity can be created.
If this operation isn’t declared for an entity in the behavior definition, creation of new instances of
the corresponding business object nodes isn’t allowed. Note that the CREATE for subsequent enti
ties is handled via the create-by-association defined in the parent entity. You can't define an ex
plicit CREATE for subordinated entities.
update Specifies that data of existing instances of a business object node that corresponds to the under
lying (root or child) entity can be updated.
If this operation isn’t declared for the entity in the behavior definition, updating existing instances
isn’t allowed.
delete Specifies that existing instances of a business object node that corresponds to the underlying
(root or child) entity can be deleted.
If this operation isn’t declared for an entity in the behavior definition, deletion of existing instances
isn’t allowed.
internal To manage a transactional behavior, an entity of a business object offers standard operations cre
*operation* ate, update, and delete for external consumption using EML or OData Services. To only provide
these operations without exposing them to consumers, the option internal can be set before
the operation name. An internal operation can only be accessed from the business logic inside
the business object implementation, for example with an action, a validation, or a determination.
_association The create by association operation is a modify operation that creates new instances of an associ
{ create;} ated entity. For more information, refer to Create by Association Operation [page 109].
internal update(features:instance);
internal delete(features:instance);
...
}
Operation Effect
Related Information
Here we explain how you can implement static feature control in the behavior model of a business object for
operations.
In the behavior definition you can define which operations are availble for a business object entity. In the
following example, the travel processor can create, modify and delete travel entries.
projection;
implementation ...;
define behavior for Z_Sample alias Example
...
{
use create;
use update;
use delete;
...
}
For an example, refer to Defining Elementary Behavior for Ready-to-Run Business Object [page 203].
Related Information
You can implement specific operations for an entity of a business object with actions. Just like the CUD-
operations, actions can be enabled or disabled using dynamic feature control. If you define actions in the
behavior definition without feature control, they’re always available for the respective business object entity
they belong to.
Operation Effects
action ActionName [...] If actions are defined in the behavior definition without
dynamic feature control, they’re always available for the
respective entity.
internal action ActionName [...] Specific operations of an entity of a business object can be
defined using actions. Similar to standard operations, you
can define internal actions in the behavior definition by
adding the option internal to the operation name. Internal
actions can only be accessed from the business logic inside
the business object implementation such as from
validations, determinations, or from other noninternal
actions.
Operation Effect
Here we explain how you can implement static feature control in the behavior model of a business object for
actions.
In the behavior definition you can define which actions are availble for a business object node. In the following
example, the action provides a template for creating a new travel based on an already existing travel instance.
projection;
implementation ...;
define behavior for /DMO/I_Travel_M alias travel
...
{
action createTravelByTemplate result [1] $self;
...
}
For more information about this individual action, refer to Implementing Actions [page 219].
Internal actions can only be called from business object internal consumers. In this example, the action
calculates the total price for one travel instance. It adds up the prices of all bookings, including their
supplements, and the booking fee of the travel instance. If different currencies are used, the prices are
converted to the currency of the travel instance.
projection;
implementation ...;
define behavior for /DMO/I_Flight_M alias Flight
...
{
internal action reCalcPrice;
...
}
For more information about the implementation, refer to Implementing Actions for the Travel Entity [page 420].
For dynamic feature control with features:instance, you must implement the For Features method.
Note
Note that the For Features method is required, if your business object is draft-enabled, even though you
might haven’t assigned feature:instanceto any property in the behavior definition. In this case, you
must still have the method definition in the behavior implementation. The implementation of For
Features can be empty in this case, as only the signature is required for the framework draft handling.
The implementation of dynamic feature control is based on the ABAP language in a local handler class
(lhc_handler) as part of the behavior pool. As depicted in the listing below, each such local class inherits
from the base handler class CL_ABAP_BEHAVIOR_HANDLER. The dynamic feature control for an entity is
implemented in this handler class using the method FOR FEATURES. For more information, see <method>
FOR FEATURES [page 866].
The output parameter result is used to return the feature control values.
These include:
Here we explain how you can implement dynamic feature control in the behavior model of a business object for
fields.
Prerequisites
● You’ve implemented the get_features method as described in Implementing Dynamic Feature Control
[page 95].
● For more details about the use case referred in the example, see Developing Transactional Apps with Draft
Capabilities [page 402]
The field bookingFee should only editable, if a travel hasn’t been accepted yet (Status 'A' on the user
interface). To enable a field with dynamic field control, add ( features : instance ) to the field
declaration:
The method implementation begins with reading the OverallStatus to check whether a travel instance has
been approved yet. This read access is implemented by the EML read operation that provides access to the
selected travel instance by using the %tky component that contains all key elements of an entity. The result of
this read operation is stored in lt_travel. For the BookingFee field, the condition-statement checks if the
OverallStatus equals 'A' (Accepted). If that is the case, the field properties are set to read_only.
Otherwise, there are no restrictions defined for the field.
You can use a combination of virtual elements and the hidden annotation to dynamically display fields on the
UI depending on a condition or value.
For information, see Hiding Fields Dynamically on the Object Page [page 657].
Related Information
Here we explain how you can implement dynamic feature control in the behavior model of a business object for
operations.
Prerequisites
● You’ve implemented the get_features method as described in Implementing Dynamic Feature Control
[page 95].
● For more details about the use case referred in the example, see Developing Transactional Apps with Draft
Capabilities [page 402]
Another dynamic feature control is defined for the creating bookings by associations. In this case, the CREATE
button for creating associated booking instance is displayed if the travel instance's overall_status isn’t
rejected. It’s hidden, if the travel is set to rejected.
Related Information
Here we explain how you can implement dynamic feature control in the behavior model of a business object for
actions.
Prerequisites
● You’ve implemented the get_features method as described in Implementing Dynamic Feature Control
[page 95].
● For more details about the use case referred in the example, see Developing Transactional Apps with Draft
Capabilities [page 402]
The two actions Accept Travel and Reject Travel depend on each other: If a travel is already accepted or
rejected, the corresponding button is disabled. To achieve this dynamic behavior, both actions are defined with
features:instance and The expected behavior is implemented in the get_features method.
The following figure shows the effect of dynamic control on the two action buttons Accept Travel and Reject
Travel. The availability of the actions depends on the Status of the travel instance: If the selected travel instance
has a status of A (Accepted), the action button Accept Travel is disabled. If the selected instance has the status
X (rejected), the action button Reject Travel is disabled.
Related Information
4.1.2.2 Operations
You can prevent illegal changes from reaching the application buffer by prechecking modify operations.
The precheck can be defined for every modify operation in the behavior definition with the following syntax:
CREATE(PRECHECK);
UPDATE(PRECHECK);
DELETE(PRECHECK);
ACTION(PRECHECK) ActionName ...;
ASSOCIATION AssocName { CREATE(PRECHECK); }
The implementation for the condition deciding whether the modify operation is executed for a certain instance
must be implemented in the corresponding method in the behavior pool:
The precheck method is called during runtime before the assigned modify operation and removes all input
from the modifying request for which the condition in the precheck is not fulfilled.
Related Information
In RAP, the create operation is a standard modifying operation that creates new instances of a business
object entity.
Note
In case of a managed business object, instances for child entities can only be created by a create-by-
association.
The following runtime diagram illustrates the main agents' activities during the interaction phase of a create
operation when an OData request for create (POST) is sent. The save sequence is illustrated in a separate
diagram, see Save Sequence [page 122].
In RAP, the update operation is a standard modifying operation that changes instances of a business object
entity.
The following runtime diagram illustrates the main agents' activities during the interaction phase of an update
operation when an OData request for update (MERGE) is sent. The save sequence is illustrated in a separate
diagram, see Save Sequence [page 122].
In RAP, the delete operation is a standard modifying operation that deletes instances of a business object
entity.
The following runtime diagram illustrates the main agents' activities during the interaction phase of a delete
operation when an OData request for delete (DELETE) is sent. The save sequence is illustrated in a separate
diagram, see Save Sequence [page 122].
In RAP, the create by association operation is a modify operation that creates new instances of an
associated entity.
The following runtime diagram illustrates the main agents' activities during the interaction phase of a create
by association operation when an OData request for a create by association (POST) is sent. The save
sequence is illustrated in a separate diagram, see Save Sequence [page 122].
4.1.2.2.5 Actions
An action in RAP is a non-standard modifying operation that is part of the business logic.
The standard use case of an action is to change specific fields of a business object entity. When using an
action, it is not the standard update operation that is called, but the action with the predefined update
implementation. On a Fiori UI, this means that the consumer of the Fiori app can change the state of an
individual business object instance without having to switch to edit mode. The application developer provides
action buttons for the action to be executable directly from a list report or an object page.
In the travel demo scenario, we provide examples of actions for changing the status of a travel instance to
booked. Expand the following figure to watch how an action is executed on a Fiori UI.
In general, however, actions can have a much wider scope than just changing single values of an instance. You
can create instances or implement complete work-flows in an action.
Technically, actions are part of the business logic. They are defined in the behavior definition and implemented
in the behavior pool of a business object. Actions are executed by calling the corresponding method FOR
MODIFY that has typed import and export parameters. They are identified as actions by the syntax FOR
ACTION.
Related Information
You define actions in the behavior definition of the entity the action is assigned to.
Actions are specified as non-standard operations in behavior definitions by using the following syntax:
...
define behavior for CDSEntity [alias AliasedEntityName]
implementation in class ABAP_ClASS_NAME [unique]
[authorization master ( instance )]
...
{
[internal [page 113]] [static [page 113]] [factory [page 114]][static [page 113]]
action [( features: instance [page 114], authorization: none [page 114] )]
ActionName [external 'ExternalActionName' [page 114]]
[parameter { InputParameter | $self) } [page 114] ]
result [page 115] [selective [page 115]] [cardinality] [page 115]
{ $self [page 115] |
entity OutputParameterEntity [page
115] |
OutputParameterStructure [page 116]
[external 'ExtResultStructureName' [page 116]] } ;
[draft [page 66]] determine action [page 116] [actionName] { validation
validationName; determination determinationName;}
}
Internal Action
By default, actions are executable by OData requests as well as by EML from another business object or from
the same business object. To only provide an action for the same BO, the option internal can be set before
the action name, for example, when executing internal status updates. An internal action can only be accessed
from the business logic inside the business object implementation such as from a determination or from
another action.
Static Action
By default, actions are related to instances of a business object’s entity. The option static allows you to
define a static action that is not bound to any instance but relates to the complete entity.
With factory actions you can create entity instances by executing an action. Factory actions can be instance-
bound or static. Instance-bound factory actions can be useful if you want to copy specific values of an instance.
Static factory actions can be used to create instances with default values.
Note
Actions: Factory Actions are not supported for the managed implementation type.
You can enable or disable actions depending on other preconditions of the business object. For example, you
might want to offer the action accept_travel only if the status is not rejected already.
Dynamic action control is defined with the syntax features: instance and must be implemented in the
<method> FOR FEATURES [page 866].
Authorization Control
Actions can be checked against unauthorized execution. A precondition for this is the definition of an
authorization master in the behavior definition. To exclude the action from authorization checks, you can use
the syntax authorization: none.
For more information, see Adding Authorization Control to Managed Business Objects [page 601].
By using the syntax external 'ExternalActionName', you can rename the action for external usage. That
means, the new name will be exposed in the OData metadata. This external name can be much longer than the
actual action name, but is not known by ABAP.
If you want to define an action button for an action with an external name, the external name must be used in
the @UI annotation. For more information, see UI Consumption of Actions [page 119].
Input Parameter
Actions can pass abstract CDS entities or other structures as input parameters. They are defined by the
keyword parameter.
Output Parameter
The output parameter for an action is defined with the keyword result. The result parameter for actions is not
obligatory. However, if a result parameter is declared in the action definition, it must be filled in the
implementation. If it is not filled, the action does not return anything, even if the action is declared with result
cardinality greater 0. In such a case, the OData service returns initial values.
Selective Result
By declaring the action result to be selective you can define that the action consumer can decide whether
the result shall be returned completely or only parts of it, for example the keys only. This can help to improve
performance as performance consuming calculated fields can be excluded from the result.
The action method in the behavior pool then receives the additional importing parameter REQUEST. This
parameter specifies which components of the result shall be returned. For more information about the
implementation of actions with selective result parameter, see Action Importing Parameter [page 118].
A Fiori UI requests only the keys of the result when an action with selective result is executed.
Result Cardinality
The result cardinality for actions determines the multiplicity of the output. In this way, it indicates
whether the action produces 0..1, 1, 0..n, or 1..n output instances. The possible values for cardinality are
therefore:
Note
RAP does not support actions with result entity cardinality greater than 1.
Result Parameter
● Result Entity: You can return a business object entity as action result.
○ Use the syntax $self if the result entity instance is the same instance for which the action is
executed..
In a UI service, the UI always stays on the same page where the action is executed. If, like in the demo
above, the action is executed on the list report, the UI stays on the list report page after the action is
executed. Executing the action from the object page returns the action result on the object page.
○ Use the syntax entity OutputParameterEntity to define the action result if the result entity is a
different entity of the same or another BO.
Only actions having output entities that are included in the service definition are exposed in the
service.
For action with return type result entity other than $self, the Fiori UI does not navigate to the result
entity, but stays on the page where the action is executed.
Note
In a projection behavior definition, result entities other than $self must be redefined with the
projection result entity. For more information, see Actions in Projection Behavior Definitions [page
117].
● Result Structure: Apart from returning entities you can also return ABAP structures. This can be an entity
type but also other structures that are defined in the ABAP dictionary. A resulting structure for actions is
defined without the keyword entity.
Using a structure as a return type is useful if you want to use the result in your further implementation.
For result structures it is possible to define an alias to clearly identify the result in the OData metadata. The
keyword external after the result type defines this OData representation of the action result.
Note
If the action result is an abstract entity, you have to define the result without the keyword entity as
abstract entities are generally considered to be structures in ABAP.
Determine Action
Determine actions allow the business object consumer to execute determinations and validations on request
without fulfilling one of the standard trigger . You can assign determinations and validations to a determine
action that is called by a consumer's trigger, just like any other actions. A determine action doesn't have its own
parameters or return values. Only the reported messages of the called determinations and validations are
forwarded and returned,The execution order of the assigned determinations and validations of a determine
action is defined by the framework and is independent of the specified order within the determine action.
Caution
Note that you can't add on modify determinations to a determine action. Furthermore, dynamic feature
control and authorization:none are not enabled for determine actions.
You can also include validations and determinations of child entities, with the follow syntax:
A special determine action is the draft determine action Prepare, which is always implicitly available
for draft BOs. It is always automatically invoked when a draft instance is activated. For more information, see
Draft Actions [page 66]
Like any operation, an action must be included in the projection behavior definition if you want to expose it for
an OData service.. The following syntax is used:
projection; ...
define behavior for CDSEntity [alias AliasedEntityName]
{...
use action ActionName [result entity ProjResultEntity] [as ActionAlias]
[external ExtActName];
}
The keyword use registers action and determine actions for the specified business object projection.
Actions that have an entity result other than $self must be redirected to the projection entity of the result
entity with the syntax result entity ProjResultEntity. Otherwise, it may happen that the action is not
exposed anymore if the result entity is not included in the service.
You can define an internal alias for the action by using the syntax as ActionAlias. This alias is used in EML
calls.
You can define an external name for the action with external ExtActName that is used in OData. This
external name can be much longer than the alias name in ABAP and needs to be used when defining the
corresponding UI annotation.
Example
For a fully defined action, see Defining Actions as Part of the Behavior Definition [page 218].
Related Information
You implement action in the behavior pool with the respective method FOR MODIFY.
As a rule, an action that belongs to a business object’s entity is implemented in the behavior pool that is
defined in the behavior definition by the keyword implementation in class ABAP_ClASS_NAME
[unique].
The concrete implementation of an action is based on the ABAP language in a local handler class as part of the
behavior pool.
Importing Parameter
● Depending on the type of action, the importing parameter it_key_for_action has the following
components:
action with result type entity If the action returns one or more entity instance
stance before the final key is set.
● If the result parameter is defined as selective in the behavior definition, the action declaration in the
behavior pool receives another importing parameter REQUEST it_requested_field. In the request
parameter all fields of the action result that are selected by the action executor are flagged. Because of
this, the action provider knows which fields are expected as a result.
If a result is defined, it has the structure %param to be filled by the action implementation. This component is a
table that reflects the type of the defined result type.
For action with selective result, only the field that are requested in REQUEST must be filled in %param.
UI Consumption of Actions
For an action to be consumable by a Fiori Elements UI, you need to define an action button in the relevant CDS
view.
An action is always assigned to one business object entity in the behavior definition. In the corresponding CDS
view, the action button must be defined in the @UI annotation.
The ActionName must correspond to the action name in the behavior definition. If an external action name is
defined for the action, you have to use this external name.
Example
For a fully implemented action, see Implementing Actions [page 219] and Enabling Actions for UI Consumption
[page 224].
Related Information
The following runtime diagram illustrates the main agents' activities during the interaction phase of an action
when an OData request to execute an action (POST) is sent. The save sequence is illustrated in a separate
diagram, see Save Sequence [page 122].
Related Information
The save sequence is part of the business logic and is called when data must be persisted after all changes are
performed during the interaction phase.
The following runtime diagram illustrates the main agents' activities during the save sequence in OData
requests. The interaction phase of the operations is illustrated in separate diagrams, see
Note
The save sequence is divided into two phases. In the first phase, it is possible to have modify calls that
change the BO even in the save sequence. In the second phase, after the point of no return, any modify call
results in a short dump.
Concurrency control prevents concurrent and interfering database access of different users. It ensures that
data can only be changed if data consistency is assured.
RESTful applications are designed to be usable by multiple users in parallel. In particular, if more than one user
has transactional database access, it must be ensured that every user only executes changes based on the
current state of the data and thus the data stays consistent. In addition, it must be ensured that users do not
change the same data at the same time.
There are two approaches to regulate concurrent writing access to data. Both of them must be used in the
ABAP RESTful Application Programming Model to ensure consistent data changes.
Optimistic concurrency control enables transactional access to data by multiple users while avoiding
inconsistencies and unintentional changes of already modified data.
The approach of optimistically controlling data relies on the concept that every change on a data set is logged
by a specified ETag field. Most often, the ETag field contains a timestamp, a hash value, or any other versioning
that precisely identifies the version of the data set.
When optimistic concurrency control is enabled for RAP business objects, the OData client must send an ETag
value with every modifying operation. On each ETag relevant operation, the value of the ETag field is compared
to the value the client sends with the request. Only if these values match is the change request accepted and
the data can be modified. This mechanism ensures that the client only changes data with exactly the version
the client wants to change. In particular, it is ensured that data an OData client tries to change has not been
changed by another client between data retrieval and sending the change request. On modifying the entity
instance, the ETag value must also be updated to log the change of the instance and to define a new version for
the entity instance.
Concurrency control based on ETags is independent of the ABAP session and instances are not blocked to be
used by other clients.
The following diagram illustrates the ETag checks for two different clients working on the same entity instance.
For more information about the ETag check during the runtime of a modify operation, see Update Operation
[page 105].
Related Information
In RAP business objects, ETags are used for optimistic concurrency control. You define the ETag in the behavior
definition of the business object entity.
Whenever an ETag is defined for a business object entity in the behavior definition, the ETag check is executed
for modifying operations, as described in Optimistic Concurrency Control [page 126]. You can define which
entities support optimistic concurrency control based on their own ETag field and which entities use the ETag
field of other entities, in other words, which are dependent on others.
An ETag is defined using the following syntax elements in the behavior definition:
...
define behavior for CDSEntity [alias AliasedEntityName]
implementation in class ABAP_CLASS_NAME [unique]
...
etag master ETagField [page 127] | etag dependent by _AssocToETagMaster [page
127]
...
{ ...
association _AssocToETagMaster { }
}
ETag Master
An entity is an ETag master if changes of the entity are logged in a field that is part of the business object entity.
This field must be specified as an ETag field in the behavior definition (ETagField). Its value is compared to
the value the change request sends before changes on the business entity are executed.
Root entities are often ETag masters that log the changes of every business object entity that is part of the BO.
ETag Dependent
An entity is defined as ETag dependent if the changes of the entity are logged in a field of another BO entity. In
this case, there must be an association to the ETag master entity. To identify the ETag master, the association
Note
You do not have to include the ETag field in ETag dependent entities. Via the association to the ETag master
entity, it is ensured that the ETag field can always be reached.
The association that defines the ETag master must be explicitly specified in the behavior definition, even
though it is implicitly transaction-enabled due to internal BO relations, for example a child/parent relationship.
The association must also be defined in the data model structure in the CDS views and, if needed, redefined in
the respective projection views.
An ETag master entity must always be higher in the BO composition structure than its dependents. In other
words, a child entity cannot be ETag master of its parent entity.
projection;
define behavior for ProjectionView [alias ProjectionViewAlias]
use etag [page 128]
{
...
use association _AssocToETagMaster
}
To expose the ETag for a service specification in the projection layer, the ETag has to be used in the projection
behavior definition for each entity with the syntax use etag. The ETag type (master or dependent) is derived
from the underlying behavior definition and cannot be changed in the projection behavior definition.
If the entity is an ETag dependent, the association that defines the ETag master must be used in the projection
behavior definition. This association must be correctly redirected in the projection layer.
Related Information
There are two prerequisites that must be fulfilled to make an ETag check work properly:
● The ETag field must be updated reliably with every change on the entity instance.
● The read access to the ETag master field from every entity that uses an ETag must be guaranteed.
If these prerequisites are fulfilled, the actual ETag check is performed by the orchestration framework, see
Update Operation [page 105], for example.
An ETag check is only possible, if the ETag field is updated with a new value whenever the data set of the entity
instance is changed or created. That means, for every modify operation, except for the delete operation, the
ETag value must be uniquely updated.
Managed Scenario
The managed scenario updates administrative fields automatically if they are annotated with the respective
annotations:
@Semantics.user.createdBy: true
@Semantics.systemDateTime.createdAt: true
@Semantics.user.lastChangedBy: true
@Semantics.systemDateTime.localInstanceLastChangedAt: true
If you choose an element as ETag field that is not automatically updated, you have to make sure that the ETag
value is updated on every modify operation via determinations.
Unmanaged Scenario
Unlike in managed scenarios, the application developer in the unmanaged scenario must always ensure that
the defined ETag field is correctly updated on every modify operation in the application code of the relevant
operations, including for updates by actions.
As can be seen in the runtime diagrams of the ETag check-relevant operations (for example Update Operation
[page 105]), the ETag check during runtime can only be performed if the transactional READ operation to the
relevant ETag master entity is enabled.
For ETag master entities that means the READ operation must be defined and implemented, whereas for ETag
dependent entities, the READ by Association operation to the ETag master entity must be defined and
implemented.
Unless you are using groups in your behavior definition, the READ operation is always implicitly defined. You
cannot explicitly specify it. In groups, however, you have to assign the READ operation to one group.
The READ by Association must be defined in the behavior definition by the syntax association
AssocName, see ETag Definition [page 127]. It must be ensured that there is an implementation available for
the READ by Association definition.
Managed Scenario
In the managed scenario, the READ operation, as well as the READ by Association operation for each entity
is provided by the framework. The READ operation is always supported for each entity and the READ by
Association operation is supported as soon as the association is explicitly declared in the behavior
definition, see <method> FOR READ [page 863].
For a complete example, see Implementing the READ Operation for Travel Data [page 352] and Implementing
the READ Operation for Associated Bookings [page 363].
Related Information
Pessimistic concurrency control prevents simultaneous modification access to data on the database by more
than one user.
Pessimistic concurrency control is done by exclusively locking data sets for the time a modification request is
executed. The data set that is being modified by one user cannot be changed by another user at the same time.
In draft scenarios, this modification request lasts as long as the draft instance exists. In other words, it lasts
until the draft instance is saved or the exclusive lock expires.
Technically, this is ensured by using a global lock table. Before data is changed on the database, the
corresponding data set receives a lock entry in the global lock table. Every time a lock is requested, the system
checks the lock table to determine whether the request collides with an existing lock. If this is the case, the
request is rejected. Otherwise, the new lock is written to the lock table. After the change request has been
successfully executed, the lock entry on the lock table is removed. The data set is available to be changed by
any user again.
The lifetime of such an exclusive lock is tied to the session life cycle. The lock expires once the lock is actively
removed after the successful transaction or with the timeout of the ABAP session.
The following diagram illustrates how the lock is set on the global lock table during an UPDATE operation.
The transaction of the client that first sends a change request makes an entry in the global lock table. During
the time of the transaction, the second client cannot set a lock for the same entity instance in the global lock
tables and the change request is rejected. After the successful update of client 1, the lock is removed and the
same entity instance can be locked by any user.
If a lock is defined for a RAP BO entity, it is invoked during the runtime of the following modify operations:
The CREATE operation does not invoke the lock mechanism, as there is no instance whose keys can be written
to the global lock table.
Note
The locking mechanism does not check key values for uniqueness during CREATE. That means, the locking
mechanism does not prevent the simultaneous creation of two instances with the same key values.
In the managed scenario, this uniqueness check is executed by the managed BO framework. In the
unmanaged scenario, the uniqueness check must be ensured by the application code provided by the
application developer.
In managed scenarios, the business object framework assumes all of the locking tasks. You do not have to
implement the locking mechanism in that case. If you do not want the standard locking mechanism by the
managed business object framework, you can create an unmanaged lock in the managed scenario. This
enables you to implement your own locking logic for the business object.
Note
Whereas the managed BO runtime executes a uniqueness check for all dependent entities of the lock
master entity, an unmanaged implementation must ensure that newly created instances are unique.
In unmanaged scenarios, however, the application developer has to implement the method for lock and
implement the locking mechanism including the creation of the lock object. The method for lock is called by
the orchestration framework before the relevant modifying operations are executed. The lock method calls the
enqueue method of a lock object that was previously created to enter the lock for the relevant entity instance
on the lock table. During the save sequence, after data has been successfully saved to the database, the lock is
removed during the cleanup method, see Save Sequence [page 122].
In scenarios with draft support, locking plays an even more crucial role during the draft business object lief
cycle.
As soon as a draft instance is created for an existing active instance, the active instance receives an exclusive
lock and cannot be modified by another. This exclusive lock remains for a determined time, even if the ABAP
session terminates. The duration time of the exclusive lock can be configured. Once the exclusive lock expires
after this duration time, the optimistic lock phase begins.
There are three cases that end the optimistic lock phase:
1. The user that created a draft instance for an active instance, and thus set an exclusive lock on the active
instance, discards the draft explicitly. This can be the case, if the data changes are not relevant anymore.
2. The draft is discarded implicitly, when
1. the draft remains untouched for a certain period of time. The lifetime of a draft is determined, but
configurable. If the draft is not used for a certain period of time, the draft is discarded automatically by
the life-cycle service.
2. the corresponding active instance is changed directly without using the draft (by the draft owner or by
a different user). This is possible during the optimistic lock phase. This change on the active instance
invalidates the draft document. Invalid drafts are discarded automatically after a determined time by
the draft life-cycle service.
3. a second draft is created for the corresponding active document.
3. The draft is resumed by the draft owner. If the user that created the draft continues to work on the draft
instance after the exclusive locking phase has ended, the draft can be resumed and the changes are still
available for the user. The optimistic locking phase ends as a new exclusive lock is set for the
corresponding active document.
In RAP, locking is not only restricted to the entity instance that is being modified. All related entities in the
business object structure are involved if one entity instance is getting locked. The locking structure is defined in
the behavior definition with the keywords lock master and lock dependent by. Every business object that
supports locking must have at least one lock master entity. Whenever a lock is requested for a specific entity
instance, its lock master and all dependent entity instances are locked for editing by a different user. This
mechanism ensures that relevant data is not changed concurrently.
Note
Lock dependent entities must always have a lock master that is superior to them in the business object
structure.
The following diagram illustrates the structure of a business object with lock master and lock dependent
entities.
If one entity instance of the blue BO tree receives a lock request, its lock master, the travel instance, is locked
and with it all dependent entity instances of this travel instance. That means if one of the blue instances is
locked, all blue instances are locked, but not the green instances of a different lock master entity instance.
Related Information
In RAP business objects, enqueue locks are used for pessimistic concurrency control. You define the lock in the
behavior definition of the business object entity.
Whenever the locking mechanism is defined for a business object entity in the behavior definition, the
orchestration framework calls the lock method to lock the respective data set and its lock dependencies. You
define which entities are lock masters and which entities are dependent on other entities. The lock mechanism
is only defined in the behavior definition in the interface layer. Its use for a business service must not be
specified in a projection behavior definition.
Note
In managed scenarios, locking must always be enabled. Therefore, the lock definition is always included in
the template of the behavior definition.
The lock mechanism is defined using the following syntax elements in the behavior definition:
...
define behavior for CDSEntity [alias AliasedEntityName]
implementation in class ABAP_CLASS_NAME [unique]
...
lock master [page 135] [unmanaged [page 135]] | lock dependent by
_AssocToLockMaster [page 135]
Lock Master
Lock master entities are locked on each locking request on one of their lock dependent entities. The method
FOR LOCK in unmanaged scenarios must be implemented for the lock master entities. The lock
implementation must include locking all dependent entities.
Note
Lock dependent entities must always have a lock master that is superior to them in the business object
composition structure.
In the managed scenario, you can define an unmanaged lock if you do not want the managed BO framework to
assume the locking task. In this case the lock mechanism must be implemented in the method FOR LOCK of
the behavior pool, just like the lock implementation in the unmanaged scenario, see Unmanaged Scenario
[page 136].
Lock Dependent
An entity is defined as lock dependent if locking requests shall be delegated to its lock master entity. The lock
master entity of lock dependent entities is identified via the association to the lock master entity. This
association must be explicitly specified in the behavior definition, even though it is implicitly transaction-
enabled due to internal BO relations, for example a child/parent relationship. The association must also be
defined in the data model structure in the CDS views and, if needed, redefined in the respective projection
views.
Related Information
If a lock mechanism is defined for business objects in the behavior definition, it must be ensured that the lock
is set for modifying operations.
Managed Scenario
The lock mechanism is enabled by default for business objects with implementation type managed. The
template for the behavior definition comes with the definition for at least one lock master entity and the
implementation of the lock mechanism is provided by the managed BO framework.
If you define an unmanaged lock for a managed business object, you have to implement the method FOR
LOCK, just like in the unmanaged scenario. It is then invoked at runtime.
Unmanaged Scenario
Just like any other operation in the unmanaged scenario, the lock must be implemented by the application
developer. To enable locking, the method FOR LOCK must be implemented.
For a complete example, see Implementing the LOCK Operation [page 366].
Implements the lock for entities in accordance with the lock properties specified in the behavior definition.
The FOR LOCK method is automatically called by the orchestration framework [page 958] framework before a
changing (MODIFY) operation such as update is called.
In the behavior definition, you can determine which entities support direct locking by defining them as lock
master.
Note
The definition of lock master is currently only supported for root nodes of business objects.
In addition, you can define entities as lock dependent. This status can be assigned to entities that depend
on the locking status of a parent or root entity. The specification of lock dependent contains the association
by which the runtime automatically determines the corresponding lock master whose method FOR LOCK is
executed when change requests for the dependent entities occur.
The keyword IMPORTING can be specified before the import parameter. The name of the import parameter
lock_import_parameter can be freely selected.
The placeholder entity refers to the name of the entity (such as a CDS view) or to the alias defined in the
behavior definition.
Import Parameters
The row type of the import table provides the following data:
● ID fields
All elements that are specified as a key in the related CDS view.
Note
The compiler-generated structures %CID, %CID_REF, and %PID are not relevant in the context of locking
since locking only affects persisted (non-transient) instances.
Changing Parameters
The LOCK method also provides the implicit CHANGING parameters failed and reported.
● The failed parameter is used to log the causes when a lock fails.
● The reported parameter is used to store messages about the fail cause.
You have the option of explicitly declaring these parameters in the LOCK method as follows:
The RAP lock mechanism requires the instantiation of a lock object. A lock object is an ABAP dictionary object,
with which you can enqueue and dequeue locking request. For tooling information about lock objects, see .
The enqueue method of the lock object writes an entry in the global lock tables and locks the required entity
instances.
An example on how to implement the method FOR LOCK is given in Implementing the LOCK Operation [page
366].
Definition
The ABAP development platform can act in the roles of a service provider and a service consumer (such as
SAP Fiori UI client).
In the context of the ABAP RESTful programming model, a business service is a RESTful service which can be
called by a consumer. It is defined by exposing its data model together with the associated behavior. It consists
of a service definition and a service binding.
As illustrated in the figure below, the programming model distinguishes between the data model and behavior
and the service that is defined by exposing these data model together with the behavior. The data model and
the behavior layer contain domain-specific semantic entities like business objects, list views, and analytical
queries, and, in addition, related functionality such as value help, feature control, and reuse objects.
A business object (BO) is a common term used to represent a real-world artifact in enterprise application
development such as the Product, the SalesOrder or the Travel. In general, a business object contains multiple
nodes such as Items and ScheduleLines (data model) and common transactional operations such as creating,
updating and deleting data and additional application-specific operations, such as the Approve operation in a
SalesOrder business object. All modifying operations for all related business objects form the transactional
behavior model of an application.
Value help supports the end user when entering data on user interfaces based on these business objects.
In read-only development scenarios, the Fiori UI technology offers a list report (ALV-like) functionality based on
a query in the back end. A list report UI like this provides a scrollable list of items that are automatically
inserted into the list from the underlying data source.
Both the business objects and the queries require value help for the end user to select data according to filter
values.
In SAP Fiori UI, many role-based and task-oriented apps are based on the same data and related functionality
must be created to support end users in their daily business and in their dedicated roles. This is implemented
by reusable data and behavior models, where the data model and the related behavior is projected in a service-
specific way. The service definition is a projection of the data model and the related behavior to be exposed,
whereas the service binding defines a specific communication protocol, such as OData V2, and the kind of
service to be offered for a consumer. This separation allows the data models and service definitions to be
integrated into various communication protocols without the hassle of re-implementation.
Example
Let us assume that a business object SalesOrder is defined and implemented in the data model and the
behavior layer with the related value help and authorization management. The service definition might expose
Related Information
The business object projection in the ABAP RESTful Programming Model is an ABAP-native approach to
project and to alias a subset of the business object for a specific business service. The projection enables
flexible service consumption as well as role-based service designs.
Introduction
A service projection layer is required for a flexible service consumption of one business object. The basic
business object [page 945] is service agnostic. That means, this BO is built independently from any OData
service [page 958] application. The basic BO comprises the maximum range of features that can be applicable
by a service that exposes this BO. The projection layer is the first layer in the development flow of the ABAP
RESTful Programming Model that is service specific. When projecting the basic BO, you define the real
manifestation of a business object in an OData service. The business object projection entails that part (the
subset) of the BO structure and behavior that is relevant for the respective service, including denormalization
of the underlying data model. Furthermore, the projection layer contains service-specific fine-tuning which
does not belong to the general data model layer, for example UI annotations, value helps, calculations or
defaulting.
By using a projection layer for your business object, you gain flexibility in the service consumption. The general
business object can be extended without affecting the already existing business service. This layering with
projections enables robust application programming. The projection layer exposes the service specific subset
of the general business object and thus, the service remains stable under modification of the underlying
business object. In addition, aliasing in the projection views allows context-specific adaptions of the business
object for a service.
The projection layer also enables one business object to be exposed in an OData service for a Fiori UI and for a
stable Web API. The service-specific differences can then be implemented in the respective projection layers.
For example, UI specifications are defined only in the BO projection that is exposed for the UI service.
Furthermore, with projections, you cannot only define the type of the service, but you can also design role-
Example
The basic BO of a business partner contains a wide range of CDS elements and behavior options.
Depending on the concrete realization of the business partner, that is, depending on which role the
business partner is assigned to, the structure of the data model and the behavior in the BO projection
might vary. In the role of a customer, which is a typical projection of the business partner, the business
partner projection contains the standard data available for business partners and in addition, sales
arrangements. Sales arrangements contain data that is related to specific sales areas and used for the
purposes of sales. All these characteristics must already be available in the basic BO and are then selected
as a subset of the general business partner pool of elements and functionalities.
Imagine the business partner is enriched with characteristics for a new role of a business partner, for
example a supplier. You can add the necessary additional elements, for example delivery information, to the
data model and the behavior implementation in the business partner BO without affecting the already
existing BO projections.
The design time artifacts to create an OData service that includes a projection layer are the following:
To create a projection layer for a business object, you need to create two projection artifacts:
For a detailed description on CDS projection views and their syntax, see CDS Projection View [page 143].
● Projection Behavior Definition
The projection of the behavior is done in a behavior definition of type projection, which is declared in the
header of the behavior definition. According to this type, only syntactical elements for projections can be
used. Only behavior that is defined in the underlying behavior definition can be reused in the projection
behavior definition. You cannot define or implement new behavior. The projection behavior always refers
back to the behavior implementation of the underlying business object.
For more information on projection behavior definitions and their syntax, see Projection Behavior
Definition [page 145].
Restriction
In the current version of the ABAP RESTful Programming Model, it is not possible to migrate a classic CDS
consumption view to a CDS projection view. It is recommended to delete and recreate the CDS
consumption view as CDS projection view.
CDS projection views are defined in data definition development objects. The wizard for data definitions
provides a template for projection views. For a detailed description on how to create projection views, see
Creating Projection Views [page 911].
For the CDS view projection, a subset of the CDS elements is projected in the projection view. These elements
can be aliased, whereas the mapping is automatically done. That means, the elements can be renamed to
match the business service context of the respective projection. It is not possible to add new persistent data
elements in the projection views. Only the elements, that are defined in the underlying data model can be
You can add new read-only associations in the projection view. This can be relevant to display additional
information on the UI, like charts etc. It is not possible, however, to denormalize fields from new associated
entity in the projection view. New associated entities cannot be accessed transactionally. Associations,
including compositions, that are defined in the projected CDS view can be used in the projection CDS view.
However, associations or compositions might change their target, if the target CDS view is also projected. This
is especially relevant for compositions as the complete BO is projected and therefore the composition target
changes. In case of a changed target, the association or composition must be redirected to the new target. The
projection view comes with a new syntax element to express the target change.
For more details about the projection view syntax, see Define View as Projection.
Note
In transactional operations, including the transactional READ, the where clause in projection views is not
respected. Applications must ensure that the instances that are created, updated, or read via the
projection conform to the where clause.
Annotations that are defined in the projected entity on element level are completely propagated to the
projection view. That means, annotation values remain the same in the projection view. Once the same
annotation is used on the same elements in the projection view, the values are overwritten and only the new
values are valid for the respective element.
If you use an annotation with an element reference in the projected entity and the reference element is aliased
in the projection entity, the reference is not drawn to the element in the projection view, due to the name
change. In such a case, you have to redefine the annotation in the projection view and use the alias name of the
element in the annotation value.
Example
The amount and currency elements are annotated in the underlying CDS view with @Semantics
annotations to support the semantic relationship of the elements.
Both @Semantics annotations are propagated to the projection view. However, the element
currency_code is aliased in the projection view and therefore the reference to the correct element is not
established. Hence, the relationship is broken and the metadata of a possible OData service will not
resemble this semantic relationship.
From a design time point of view, the projection layer is the first service-specific layer. If the resulting OData
service is a UI service, all UI specifications or other service-specific annotations must be defined in the CDS
projection views via CDS annotations [page 949]. The following UI specifics are relevant on the projection BO
layer:
Restriction
In the current version of the ABAP RESTful Programming Model, CDS projection views can only be used to
project CDS view entities. Other entities, such as custom entities are not supported.
Related Information
A projection behavior definition provides means to define service-specific behavior for a BO projection.
The behavior definition with type projection is created equally to other types of behavior definitions. When
creating a behavior definition based on a CDS projection view, the syntax template directly uses the projection
type. For more information, see Working with Behavior Definitions [page 896].
In a behavior definition, only behavior characteristics and operations that are defined in the underlying
behavior definition can be defined for the BO projection. The syntax for this is use <Element>.
projection;
define behavior for ProjectionView alias ProjectionViewAlias
/* use the same eTag defined in underlying behavior definititon */
use etag
{
/* define static field control */
field ( readonly ) ProjViewElem1;
field ( mandatory ) ProjViewElem2;
/* expose standard operations defined in underlying behavior definition */
use create;
use update;
use delete;
/* expose actions or functions defined in underlying behavior definition */
use action|function ActionName [result entity ProjResultEntity][as ProjAction]
[external ExtProjname];
/* expose create_by_association for child entities defined in underlying
behavior definition */
use association _Assoc { create; }
}
Explanation
The keyword use exposes the following characteristics or operations for the service-specific projection. In the
projection, only elements can be used that were defined in the underlying behavior definition. These elements
can be
● ETag
● standard operations
● actions
● functions
● create_by_association
Every operation that you want to expose to your service must be listed in the projection behavior definition.
New aliases can be assigned for actions and functions. Projection behavior definitions do not have a behavior
implementation. The complete behavior is realized by mapping it to the underlying behavior.
The definitions that already restrict the character of the underlying BO are automatically applied in the BO
projection and cannot be overwritten. This is the case for:
● locking
● authorization
● feature Control
If no static field control is defined in the underlying behavior definition, you can add this definition in the
projection behavior definition. If it is already defined in the underlying behavior definition, you cannot define
the opposite in the projection layer. If you do, you will get an error during runtime. New dynamic field control
cannot be defined in the projection behavior definition, as there is no option to implement the feature.
Related Information
Definition
A business service definition (short form: service definition) describes which CDS entities of a data model are
to be exposed so that a specific business service, for example, Sales Order handling, can be enabled. It is an
ABAP Repository object that describes the consumer-specific but protocol-agnostic perspective on a data
model. It can directly access the standard ABAP Workbench functionality, such as transports, syntax checks,
element information, and activation. Its transport type is SRVD.
Use
A service definition represents the service model that is generically derived from the underlying CDS-based
data model.
You use a service definition to define which data is to be exposed as a business service using one or more
business service bindings (short form: service bindings). A service definition itself is independent from the
version or type of the protocol that is used for the business service.
Remember
When going to expose a data model as a service, you can make use of a service definition only in
connection with at least one service binding.
Note
You cannot expose an OData service that includes abstract entities. Whereas abstract entities are allowed
to be used in a service definition, the publishing of a service via a service binding causes a dump error.
@EndUserText.label: 'text'
@<Annotation_1>
...
@<Annotation_n>
The source code of the actual service definition is preceded by the optional CDS annotation
@EndUserText.label that is available for all objects which can contain CDS annotations. The annotation
value is a character string with a maximum of 60 characters. The specified text value should consist of a
meaningful short text that describes the service in the original language of the source code. The size of the set
of service-relevant CDS entities depends on the kind of functionality the service should provide to the
application scenario. However, the respective dependencies must be considered:
Depending on the needs of your scenario, further optional annotations @<Annotation_1> ...
@<Annotation_n> can be specified.
The service definition is initiated with the DEFINE SERVICE keyword followed by the name for the service
definition.
Note
This name for the service definition follows the naming rules that are common to ABAP Repository objects:
The source code of a service definition is created within a single bracket { ... } that is used to group all the
related CDS entities (including their associations with the relevant entities) which are to be exposed as part of
an individual service.
The name of each individual CDS entity to be exposed follows the EXPOSE keyword. This is followed by an
optional alias name, which is initiated by the AS keyword. An alias defines an alternative name for a CDS entity
to be exposed. As a result, when accessing the service, the alias names are used instead of the current entity
names. Thus, you have the option of assigning syntactically uniform identifiers in the service definition and
thus decoupling the service semantics from the concrete technical names resulting from the data definition.
Restriction
Whenever you edit a service definition in the context of the ABAP RESTful programming model to create a
UI service or a Web API, you can only use CDS views or custom entities as entities. You cannot use
abstract CDS entities [page 948] in service bindings for in this service exposure use case. The usage of
abstract entities would cause a dump error.
However, abstract entities can be used in a service definition, but only if they are created using OData client
proxy tools (service consumption use case).
Example
The following example shows the corresponding source code for the service definition /DMO/TRAVEL. The
travel management service to be defined in this way includes all dependencies that come from the root
entity /DMO/I_TRAVEL.
Note
In this example, the service definition is based on CDS entities that originate from different namespaces.
Related Information
Definition
The business service binding (short form: service binding) is an ABAP Repository object used to bind a service
definition to a client-server communication protocol such as OData. Like any other repository object, the
service binding uses the proven infrastructure of the ABAP Workbench, including the transport functionality.
Use
As shown in the figure below, a service binding relies directly on a service definition that is derived from the
underlying CDS-based data model. Based on an individual service definition, a plurality of service bindings can
be created. The separation between the service definition and the service binding enables a service to integrate
a variety of service protocols without any kind of re-implementation. The services implemented in this way are
based on a separation of the service protocol from the actual business logic.
Relationship Between the Data Model, the Service Definition and the Service Binding
Parameters
Service Name
Defines a unique system-wide name for the service and is identical to the name of the service binding.
Tip
We recommend using the prefix API_ for Web API services and the prefix UI_ for UI services.
Binding Type
The binding type primarily specifies the specific protocol which is implemented with the service binding. The
OData models of the current version of ABAP Platform support the OData version 2.0 (ODATA V2).
Remember
The Open Data Protocol (OData) enables the creation of HTTP-based services, which allow resources
identified using Uniform Resource Identifiers (URIs) and defined in an abstract data model to be published
and edited by Web clients using HTTP messages. OData is used to expose and access information from a
This parameter also determines the way a service is offered to a consumer. There are two options:
UI UI service
A UI service makes it possible to add a SAP Fiori elements UI or other UI clients to the service.
A service that is exposed as Web API is used for unforeseen extensions of the SAP software. It is
offered by an application for an unknown consumer for direct interaction with the user.
Service Version
The versioning of services is made by a version number which is assigned to a service binding.
The next higher version is created by adding another service definition to the existing service binding. By
means of this further service definition, functional changes or extensions (compared to the previous version)
are exposed. And, vice versa, the version number can be decreased by removing a service definition from the
service binding.
Activation State
This parameter determines whether the service is activated as a local service endpoint (in the service catalog
of the current system) or not.
Service URL
The derived URL (as a part of the service URL) is used to access the OData service starting from the current
ABAP system. It specifies the virtual directory of the service by following the syntax: /sap/opu/odata/
<service_binding_name>.
For more infromation about the service binding, see Using Service Binding Editor [page 907].
Related Information
An OData service [page 958] can be exposed as a UI service, that can be consumed by an SAP Fiori UI, or as a
Web API that can be consumed by any OData client.
UI service
An OData service that is exposed as a UI service is consumable by an SAP Fiori Elements app. Every front-end
configuration, which is manifested in the back-end development object (for example, UI annotations), is
exposed within the metadata of the service. That means, a Fiori UI reads the information in the metadata and
creates the matching UI for the service. These UI settings can be enhanced and overwritten in the SAP Web
IDE.
A UI service can be previewed with the Fiori Elements preview in the service binding [page 947] tool. The
preview mocks a real UI app and has the same look and feel as a Fiori Elements app. It is therefore a powerful
tool to test the UI of your OData service already in the backend. However, it does not substitute the
development in the SAP Web IDE.
For further information about the Fiori Elements preview in the service binding, see Previewing the Resulting UI
Service [page 32].
For more information about SAPUI5 to get more information about creating a deployable SAP Fiori app, see
Developing Apps with SAP Fiori Elements.
An OData service that is exposed as a Web API comes without any UI specific information in the metadata. It is
the public interface for any OData client to access the OData service. For example, you can consume a Web API
from another OData service.
Web APIs require a life cycle management. It must be possible to define the release, the version, and the
possible deprecation of the Web API. This functionality is enabled in the service binding [page 947] tool.
For more information about Web APIs, see Developing a Web API [page 487].
The runtime frameworks SAP Gateway and the Orchestration Framework are the frameworks that manage the
generic runtime for OData services built with the ABAP RESTful Programming Model. As a developer you do
not have to know the concrete inner functioning of these frameworks, as many development tasks are
automatically given. However, the following sections provide a high-level overview.
SAP Gateway
SAP Gateway provides an open, REST-based interface that offers simple access to SAP systems via the Open
Data Protocol (OData).
As the name suggests, the gateway layer is the main entry point to the ABAP world. All services that are
created with the ABAP RESTful Programming Model provide an OData interface to access the service. However,
the underlying data models and frameworks are based on ABAP code. SAP Gateway converts these OData
requests into ABAP objects to be consumed by the ABAP runtime.
Orchestration Framework
The orchestration framework dispatches the requests for the business object (BO) or the query. It receives the
ABAP consumable OData requests from the Gateway layer, forwards it to the relevant part of the business logic
and interprets the matching ABAP calls for it. For transactional requests, the orchestration framework
delegates the requests to the BO and calls the respective method of the BO implementation. For query
requests, the framework executes the query. Depending on the implementation type, the BO [page 945] or the
query [page 959] runtime is implemented by a framework or by the application developer.
If locks [page 956] are implemented, the orchestration framework executes first instance-independent checks
and sets locks. For the eTag [page 953] handling, the framework calls the necessary methods before the actual
request is executed.
The orchestration framework is also known under the name SADL [page 960] (Service Adaptation
Description Language). Apart from the runtime orchestration, the SADL framework is also responsible for
essential parts in the query and BO runtime.
Examples
The OData client sends a DELETE request, which is converted to an object that is understandable for ABAP. The
orchestration framework analyzes this ABAP object and triggers the MODIFY method for DELETE of the
business object to execute the DELETE operation on the database table. Depending on the implementation
type (managed or unmanaged), the code for the MODIFY method is generically available or must be
implemented by the application developer.
Likewise, if an OData request contains a query option, such as $orderby, the Gateway layer converts it to the
query capability SORT. Then, the orchestration framework takes over and delegates the query capability to the
query. Depending on the runtime type (managed or unmanaged), the query is executed by the generic
framework in case of managed type or by the self-implemented runtime in case of unmanaged type. For a
managed query, the generic framework converts the requests to ABAP SQL statements to access the
database.
Entity Manipulation Language (in short: EML) is a part of the ABAP language that is used to control the
business object’s behavior in the context of ABAP RESTful programming model. It provides a type-save read
and modifying access to data in transactional development scenarios.
Business objects that are implemented with the ABAP RESTful architecture based on the behavior definition
and implementation of the interaction phase and save sequence in behavior pools can be consumed not only
by means of OData protocol (Fiori UIs, or Web APIs) but also directly in ABAP by using the EML syntax.
● A standard API, which uses the signature of the business object related entities
● A generic API for dynamic/generic consumption of business objects.
The latter is typically used for generic integration of business objects into other frameworks, such as the Cloud
Data Migration Cockpit or the Process Test Framework.
The standard API is used whenever the "target" business object is statically specified. It provides code
completion and static code checks. This typed API provides statements for read-only access to data (READ
ENTITIES), as well as for modifying data access (MODIFY ENTITIES) and for triggering the save sequence
(COMMIT ENTITIES).
One of the uses cases of EML is the writing of test modules as ABAP Unit tests. As an ABAP application
developer, it gives you the ability to test transactional behavior of business objects for each relevant
operation that is defined in the behavior definition.
EML Syntax
MODIFY ENTITIES
This statement includes all operations that change data of entities. This is handled by the statement MODIFY
ENTITIES, which provides the following operations:
● create
● create by association
● update
● delete
● actions, that is, any modify operation that cannot be covered with create, update, or delete, for example, if
parameters are required.
You can use the short form of the MODIFY statement in special cases when calling modify operations for one
entity only - without any relation to a business object. In this case, however, it is required that you specify the
full name of the CDS entity instead of the alias name. The keywords for modify operations are: CREATE,
UPDATE, DELETE, and EXECUTE for actions. Each operation has a table of instances as input parameters. For
actions, you can also add a RESULT parameter et_result_a in case the action is defined to provide a result.
Remember
Note that at least one of the operations must specify a valid EML statement. The sequence of the
operations is irrelevant.
The MODIFY statement provides a FIELDS ( field1 field2 ... ) WITH option with a field list for direct
creating, creating by association and updating entity's instance data. That means all fields that are to be
updated for an instance or are required for creating an entity's instance are specified in the field list.
You can complete the MODIFY statement with the response parameters FAILED [page 875], MAPPED [page
876], and REPORTED [page 876]. As is common in ABAP, you can use either existing variables (ct_failed)
with matching data types or add an inline declaration (DATA(ct_failed)).
The long form of the MODIFY statement allows you (like a complex OData request) to collect multiple modify
operations on multiple entities of one business object that is identified by RootEntityName. Grouped by
entities, the relevant operations are listed according to the previous short form syntax. If aliases for the entities
are defined in the behavior definition, they should be referred in the long form syntax.
Examples:
The current version of the ABAP RESTful programming model provides the following read operations:
The READ statement provides a FIELDS ( field1 field2 ... ) WITH option with a field list for direct
reading, reading by association of entity's instance data. The fields of an instance to be read are specified in the
field list.
The READ statement always has the addition RESULT, since this specifies the variable that receives the READ
operation's results. This variable contains the target instance(s) with all fields of the entity.
The read-by-association operation provides an additional target variable that follows after the LINK keyword
addition. The et_link_rba variable contains only a list of key pairs: key of the source entity and the key of the
target entity. Target variables must have either a matching type or are declared inline, for example, DATA
(et_link_rba).
You can complete the READ statement with the response parameter FAILED (table containing error keys).
Examples:
The long form READ ENTITIES allows you to group read operations for multiple entities of a business object
that is specified by RootEntityName. The long form allows using aliases defined in the behavior definition for
specifying the entities.
COMMIT ENTITIES
Modify operations [page 957] that are executed within a behavior pool or by an ABAP program, do not cause
any data changes at the database level. This is because they are applied only to the transactional buffer and the
buffer content disappears at the end of the ABAP session. This means the save sequence must be triggered in
this case.
The save sequence is triggered by the COMMIT ENTITIES statement. The runtime infrastructure translates
this statement into the save chain starting with finalize( ) performing the final calculations before data can
be persisted. If the subsequent check_before_save( ) call is positive for all transactional changes, the
point-of-no-return is reached. From now on, a successful save() is guaranteed by all involved BOs. After the
In its simplest form, the statement COMMIT ENTITIES does not have any parameters:
COMMIT ENTITIES.
COMMIT ENTITIES
[RESPONSE OF root_entity_name_1
[FAILED ct_failed]
[REPORTED ct_reported]]
[RESPONSE OF root_entity_name_2
[FAILED ct_failed]
[REPORTED ct_reported]].
The syntax of the COMMIT ENTITIES statement also provides the RESPONSE clause that allows to retrieve the
response information of one or more business objects involved that are grouped by their root entity. For each
root entity (root_entity_name_1, root_entity:name_2), a RESPONSE clause can contain parameters
FAILED and REPORTED.
COMMIT ENTITIES saves all BOs that were changed within the LUW [page 956] .
Remember
The crucial criteria of success for the COMMIT ENTITIES EML statement is not FAILED response is empty,
but SY-SUBRC = 0. Background: Indirect failures (in remote business objects) cannot be foreseen by
using the response parameter.
Related Information
The development guides in this section provide a detailed step-by-step description on how to use and exploit
the ABAP RESTful Programming Model in end-to-end scenarios.
The guides in this section focus on specific development tasks. It depends on your initial situation and on the
aimed outcome of your development, which guide meets your requirements best.
Follow the path in the diagram and ask yourself the questions to find out which development guide helps you
with your development task. You get further information about the steps and the development guides by
hovering over the image.
Use the navigation in the image or the following links to navigate to the development guides:
Based on existing persistent data sources, you create and implement a query for an OData service to get a
running app with useful read-only features.
Introduction
In this chapter you learn how to develop an OData service including multiple read-only features. This OData
service can be consumed by a Fiori Elements application or by any other OData client.
Starting from the elementary list reporting scenario that was introduced in the Getting Started [page 12]
section, you may want to add some further features to the existing elementary OData service. First of all, the
end user wants to be able to navigate to a second information layer for a flight connection to retrieve more
detailed information about the flight, such as flight dates or plane types. Secondly, if the list report contains a
large number of rows, it becomes difficult for end users to find the information they need. To make it easier to
find this information, you can implement search capabilities or label the app elements differently than their
presets in the data model layer in the back end. You might also want to enable value helps for selection field
dialogs. All these features are implemented using specific CDS annotations, which you, as the developer, add to
the source code of the respective CDS view.
We assume that you know the development steps to create an OData service, as described in the Getting
Started [page 12] section. The following guide uses the CDS view /DMO/I_Connection_R and the OData
service /DMO/UI_FLIGHT_R_V2 as the basis for a more elaborate OData service with further read-only
features. You learn how to expand the data model with associated CDS views and how to include useful read-
only functions in the OData service.
You are guided step-by-step through the application model and expand the OData service that you created in
the Getting Started section with the following features and query capabilities.
This scenario implements the query case. We firstly define a CDS data model for which we define modeled
query capabilities. The service that was created in the Getting Started scenario is reused and the new CDS
entities including their query capabilities are exposed for this service.
Note
Via ABAPGit You can import the service including the related development objects into your development
environment for comparison and reuse. You find the service in the package /DMO/FLIGHT_READONLY. The
suffix for development objects in this development guide is _R.
For information about downloading the ABAP Flight Reference Scenario, see Downloading the ABAP Flight
Reference Scenario [page 12].
Prerequisites
Developing the scenario that is described in the subsequent chapters requires the following:
● You have access to and an account for SAP Cloud Platform, ABAP environment.
● You have installed ABAP Development Tools (ADT).
SAP recommends to use the latest version of the client installation. The ADT download is available on the
update site https://tools.hana.ondemand.com/.
● To recreate the demo scenario, the ABAP Flight Reference Scenario must be available in your ABAP system.
You can download the complete reference scenario from GitHub: Downloading the ABAP Flight Reference
Scenario [page 12].
● You have understood the development steps to create an OData service as described in Developing an
OData Service for Simple List Reporting [page 13].
In particular, you are able to use the existing OData service /DMO/UI_FLIGHT_R_V2 to check and try out
the new implementation with the preview tool.
● Apply and enhance your knowledge about how to create and expand an OData service
● Implement associations between CDS views
● Expose new CDS views for an existing OData service
● Use @EndUser.Text annotations
● Implement text associations
● Develop value helps for input fields
● Implement search capabilities
Starting Point
We build the list reporting scenario based on the service that was created in the Getting Started [page 12]
scenario. The CDS view /DMO/I_Connection_R is reused in the following scenario and included in a broader
data model.
In the first step of this development guide, you expand the data model with additional CDS views to create a full
blown reference app for flights. The resulting running app is able to find flights based on connections and the
airports involved. From a technical point of view, this means that the CDS data model is expanded with three
other CDS views, one of which is connected using associations to enable navigation from one CDS view to
As you can see in the figure above, the entry point of the flight scenario is the well-known CDS view /DMO/
I_Connection_R. It manages data for flight connections. From here, you can navigate to more detailed flight
information with flight dates and plane information. The detailed information in the carrier CDS view is used to
display the full name of the airline, whereas the CDS view /DMO/I_Airport_R is used as a value help provider
view for the airport elements in the connection CDS view.
These items only represent a part of the ABAP Flight Reference Scenario.The other items in the reference data
model are applied in other development scenarios.
To provide a complete data model for a read-only OData service, CDS views have to be defined first.
Context
In the introductory guide, you learned how to define CDS views based on an existing persistent data source.
Repeat the process for further CDS views to expand the data model for the flight scenario. We assume that the
CDS view /DMO/I_Connection_R already exists. This view defines the starting point of the app.
For a detailed description of the following steps, look at Defining the Data Model with CDS [page 15].
Procedure
Naming CDS views: Since CDS views are (public) interface views, they are prefixed with I_ in
accordance with the VDM (virtual data model) naming convention. In addition, we add the suffix _R
for the read-only implementation type to the view name.
Data Definition
/DMO/I_FLIGHT_R /dmo/ Provides information about the available flights including flight dates
flight and plane information.
/DMO/I_Flight_R
(DB table) You can navigate to this view once you have chosen a flight connec
/DMO/IFLIGHT_R
tion.
/DMO/I_CARRIER /dmo/ Provides information about the airlines that operate the flights.
carrier
/DMO/I_Carrier This view is later used as a value help provider view for the
(DB table) view /DMO/I_CONNECTION_R.
/DMO/ICARRIER_RE
Note
As this CDS view is reused in more scenarios, it does not carry a
suffix.
The following table helps you to identify the elements that need a semantic annotation to ensure that
semantic data types are consumed in the right way. The elements that must be annotated are currency
codes and their corresponding amount as well as units of measure and their corresponding quantities.
Whereas the existing CDS view /DMO/I_Connection_R contains a unit of measure that needs to be
annotated, the new CDS views contain currency codes and the corresponding price elements that need to
be annotated.
This annotation establishes the link between the amount and the
currency element. Reference the element with the currency code
of the element price CurrencyCode.
4. Insert the relevant UI annotations to ensure that the user interface is rendered properly where necessary.
Whereas the existing CDS view of the introductory guide in the getting started section requires many UI
annotations, since it represents the landing list report page, the new CDS views only require a limited
number of UI annotation. In fact, only the CDS view /DMO/I_Flight_R requires the @UI.lineItem:
[{}] annotation to be applied to the relevant elements. Since the remaining views only function as text
provider or value help provider views, they do not need any UI annotations at all.
A detailed description of this procedure is described in Designing the User Interface for a Fiori Elements
App [page 34].
5. Activate the CDS view.
The resulting source code for each data definition is displayed at the end of this topic: Data Model for Flight
App [page 166].
6. Include the new CDS entities in the existing service /DMO/UI_FLIGHT_R_V2, which you created in the
introductory guide in the getting started section: Developing an OData Service for Simple List Reporting
[page 13]. To do this, expose the CDS entities in the service definition /DMO/FLIGHT_R
The following codeblock displays the source code of the updated service definition /DMO/FLIGHT_R
The following listings display the source code of the CDS views that are involved in our read-only scenario.
You can use metadata extensions to separate the metadata specified by @UI or other UI related
annotations from the actual data definition in the CDS view. See
Expand the following listing to view the source code of the Connection CDS view.
This CDS view /DMO/I_Connection_R was already modeled in the introductory guide in the getting started
section.
@AbapCatalog.sqlViewName: '/DMO/ICONNECT_R'
@AbapCatalog.compiler.compareFilter: true
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Read-Only E2E: Data Model Connection'
@UI.headerInfo: { typeName: 'Connection',
typeNamePlural: 'Connections' }
define view /DMO/I_Connection_R
as select from /dmo/connection as Connection
{
@UI.facet: [
{ id: 'Connection',
purpose: #STANDARD,
type: #IDENTIFICATION_REFERENCE,
label: 'Connection',
position: 10 } ]
@UI: {
lineItem: [ { position: 10, label: 'Airline' } ],
identification:[ { position: 10, label: 'Airline' } ] }
key Connection.carrier_id as AirlineID,
@UI: {
lineItem: [ { position: 20, label: 'Connection Number' } ],
identification:[ { position: 20, label: 'Connection Number' } ] }
key Connection.connection_id as ConnectionID,
@UI: {
lineItem: [ { position: 30, label: 'Departure Airport Code' } ],
selectionField: [ { position: 10 } ],
identification:[ { position: 30, label: 'Departure Airport Code' } ] }
Connection.airport_from_id as DepartureAirport,
@UI: {
lineItem: [ { position: 40, label: 'Destination Airport Code' } ],
selectionField: [ { position: 20 } ],
identification:[ { position: 40, label: 'Destination Airport Code' } ] }
Connection.airport_to_id as DestinationAirport,
@UI:{
lineItem: [ { position: 50, label: 'Departure Time'} ] ,
identification: [ {position: 50, label: 'Departure Time' }] }
Connection.departure_time as DepartureTime,
@UI:{
lineItem: [ { position: 60, label: 'Arrival Time' } ] ,
identification: [ {position: 60, label: 'Arrival Time' }] }
Connection.arrival_time as ArrivalTime,
@UI:{ identification: [ {position: 70, label: 'Distance' } ] } **
establishes the link between quantity and the corresponding unit of measure
@Semantics.quantity.unitOfMeasure: 'DistanceUnit'
Connection.distance as Distance,
@Semantics.unitOfMeasure: true ** defines the semantic content of the
element
Connection.distance_unit as DistanceUnit
}
@AbapCatalog.sqlViewName: '/DMO/IFLIGHT_R'
@AbapCatalog.compiler.compareFilter: true
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Read-Only E2E": Data Model Flight'
define view /DMO/I_Flight_R
as select from /dmo/flight as Flight
{
@UI.lineItem: [ { position: 10, label: 'Airline'} ]
key Flight.carrier_id as AirlineID,
@UI.lineItem: [ { position: 20, label: 'Connection Number' } ]
key Flight.connection_id as ConnectionID,
@UI.lineItem: [ { position: 30, label: 'Flight Date' } ]
key Flight.flight_date as FlightDate,
@UI.lineItem: [ { position: 40, label: 'Price' } ]
@Semantics.amount.currencyCode: 'CurrencyCode' ** establishes the link
between amount and currency code
Flight.price as Price,
@Semantics.currencyCode: true
Flight.currency_code as CurrencyCode, ** defines the semantic content
of the element
@UI.lineItem: [ { position: 50, label: 'Plane Type' } ]
Flight.plane_type_id as PlaneType,
@UI.lineItem: [ { position: 60, label: 'Maximum Seats' } ]
Flight.seats_max as MaximumSeats,
@UI.lineItem: [ { position: 70, label: 'Occupied Seats' } ]
Flight.seats_occupied as OccupiedSeats
}
Expand the following listing to view the source code of the Carrier CDS view.
@AbapCatalog.sqlViewName: '/DMO/ICARRIER_RE'
@AbapCatalog.compiler.compareFilter: true
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Read-Only E2E: Data Model Carrier'
define view /DMO/I_Carrier
as select from /dmo/carrier as Airline
{
key Airline.carrier_id as AirlineID,
Airline.name as Name,
@Semantics.currencyCode: true ** defines the semantic content of
the element
Airline.currency_code as CurrencyCode
}
Expand the following listing to view the source code of the Airport CDS view.
@AbapCatalog.sqlViewName: '/DMO/IAIRPORT_RE'
@AbapCatalog.compiler.compareFilter: true
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Read-Only E2E: Data Model Airport'
define view /DMO/I_Airport
as select from /dmo/airport as Airport
{
key Airport.airport_id as AirportID,
Airport.name as Name,
Airport.city as City,
Airport.country as CountryCode
}
Note
To obtain the full scope of a data model and to enable flexible service consumption, you can project the
data model before creating a business service with CDS projection views. With a projection layer, you can
extend the basic data model without affecting the already existing business service.
Associations structure the relationships between CDS views. To enable navigation in the UI, associations must
be implemented in CDS.
Context
You created the flight CDS view that provides detailed information about the available flights for the
connection. To be able to navigate to this view in the UI, you have to implement an association from /DMO/
I_Connection_R to /DMO/I_Flight_R.
An association defines a structural and unidirectional relationship between two CDS views. Associations are
used to navigate from a source CDS view to a related target CDS view.
Associations are defined in the source CDS view. The connections between the two CDS views are established
by a join condition applied to the respective elements of the source and target view. You need to define a
cardinality for the target CDS view to define how many records of the target CDS view are associated with one
record of the source.
Note
The cardinality defines the minimum and maximum number of associated entries of the target
view.
...
define view /DMO/I_Connection_R as select from /dmo/connection as
Connection
association [1..*]
c. Specify the target CDS view and define an alias. An alias makes it easy to reference the association.
In our example, the associated CDS views are mapped to two elements. Therefore both of them must
be stated in the condition expression.
{
key Connection.carrier_id as AirlineID,
...
/*Associations*/
_Flight ** use the alias to refer to the association
}
4. For the UI: Provide a second facet to make it possible to navigate from the list report page to the detailed
object page of the connection with corresponding flights as line items.
A detailed description of UI annotations can be found in Defining UI Annotations [page 35].
@UI.facet: [
{ id: 'Connection',
purpose: #STANDARD,
type: #IDENTIFICATION_REFERENCE,
label: 'Connection',
position: 10 } ,
{ id: 'Flight',
Results
You have established a connection between the connection CDS view and the flight CDS view. With the right
configuration of UI annotations, the end user can now navigate to the flight information by selecting one
connection in the list report app. The flight information is displayed as line items in the object page as can be
seen in the following figures.
The object page then displays the information about the selected connection and the related flights for this
connection in the second facet. .
Object page displaying all flights of the connection AZ 789 from TYO to FCO
Meaningful descriptions of elements that appear on the user interface are a key concept for working with an
app and improving the user experience. It is essential that all items on the UI are readable and understandable
The labels that are assigned in the CDS layer overwrite the database field descriptions and are exposed to the
OData service. These labels are also propagated to the UI and displayed on the user interface if no UI labeling
annotations exist that overwrite the CDS EndUserText annotations.
Note
A tooltip can provide additional or more thorough information for the element. The tooltip is displayed on the UI
as mouse over text. If no @EndUserText annotations are used, the text for the mouse over function is retrieved
from the long description of the data element stored in ABAP Dictionary.
In general, every text that is used in EndUserText annotations is translated into every relevant language by the
SAP translation process, along with the labels that are given to the data elements.
For more information on how to integrate information for the end user in your data model, refer to Adding Field
Labels and Descriptions [page 628].
In our flight scenario, we have equipped the CDS elements with UI annotations to display them adequately in
the UI, which means that all elements are already represented with a meaningful label.
However, there is one use case where EndUserText labels are necessary to ensure coherence on the UI.
Whereas @UI.listItem and @UI.identification offer the option to directly label list items and object
page items with UI annotations, the @UI.selectionField annotation lacks this option. The selection field
label is therefore retrieved from the database label and might not match the label we have given to the list item
and the identification. In this case, it is useful to apply the @EndUserText annotations to those elements that
represent selection fields to provide consistency.
Example
In the CDS view /DMO/I_Connection_R, we defined two selection fields that are labeled differently than
the corresponding list item. We use the @EndUserText annotation to acquire matching labels.
{...
@UI: {
lineItem: [ { position: 30, label: 'Departure Airport Code' } ],
selectionField: [ { position: 10 } ],
identification:[ { position: 30, label: 'Departure Airport Code' } ] }
@EndUserText.label: 'Departure Airport Code' //*** Use the same
label as in lineItem
Connection.airport_from_id as DepartureAirport,
@UI: {
lineItem: [ { position: 40, label: 'Destination Airport Code'} ],
selectionField: [ { position: 20 } ],
identification:[ { position: 40, label: 'Destination Airport Code' } ] }
If you want additional and longer information about an element, you can use the annotation
@EndUserText.quickInfo: <text> to display a text when hovering over the element.
Note
If you do not define a tooltip in CDS, the mouse over text displays the short description of the data element
in ABAP Dictionary.
Example
@UI: {
lineItem: [ { position: 10, label: 'Airline' } ],
identification:[ { position: 10, label: 'Airline' } ] }
@EndUserText.quickInfo: 'Airline that operates the flight.'
key Connection.carrier_id as AirlineID,
Related Information
Data that is stored in databases is usually kept as short as possible and consequently, many words are
shortened or abbreviated with a character code. While this is convenient for storage reasons, it becomes a
problem on the UI as the elements might then not be understandable anymore. The following figure displays a
UI screen with airline codes that are not commonly known and which makes it difficult (or impossible) to use
the app.
In our use case, the airline is abbreviated with the two letter airline code.
The full name of the airline is given in the CDS view /DMO/I_Carrier_R based on the database /DMO/
carrier. Using a text association, you can establish a link from the connection CDS view to the carrier CDS
view and adopt the names of the airlines as an addition to the airline codes in the UI. This requires the use of
annotations from the domains @semantics and @ObjectModel to mark the names as readable text and to
assign the text addition to the airline code elements.
For more detailed information about text elements, refer to Defining Text Elements [page 544].
Prerequisites
A text provider view already exists. In our case, the CDS view /DMO/I_Carrier contains the relevant text
element Name.
Context
The readable names of the airlines can be displayed in the UI together with the two letter code. The text is taken
from the text provider view /DMO/I_Carrier that contains the airline code as well as the readable name of the
airline. We want to display the text for the airlines on the list report page and on the object page. This is why we
need to implement the text association for both views, /DMO/I_Connection_R and /DMO/I_Flight_R.
Apart from an association with the text provider view, the text element in that view needs to be specified as text
with an @Semantics annotation. In addition, the element with the airline code in the source view must be
annotated to assign the text from the associated view to the element.
1. Open the CDS view /DMO/I_Carrier. Use the annotation @Semantics.text: true on the element
Name to identify the annotated element as a text and activate the view.
...
define view /DMO/I_Carrier
as select from /dmo/carrier as Airline
{
key Airline.carrier_id as AirlineID,
@Semantics.text: true
Airline.name as Name,
@Semantics.currencyCode: true
Airline.currency_code as CurrencyCode
}
Note
In general, you can annotate more than one view field as a text field. However, only the first annotated
field is respected in the text consumer view for OData exposure.
2. Open the CDS view /DMO/I_Connection_R, for which you want to display the text. Implement an
association to the CDS view /DMO/I_Carrier with the join condition on AirlineID. This association
serves as a text association.
The process how to implement associations is described in Implementing Associations for Existing CDS
Views [page 169].
3. Use the annotation @ObjectModel.text.association: '<_AssocToTextProvider>' on the
element AirlineID and reference the association _Carrier as a text association. Then activate the CDS
view.
...
define view /DMO/I_Connection_R
as select from /dmo/connection as Connection
association [1..*] to /DMO/I_Flight_R as _Flight on
$projection.AirlineID = _Flight.AirlineID
and
$projection.ConnectionID = _Flight.ConnectionID
association [1] to /DMO/I_Carrier as _Airline on $projection.AirlineID
= _Airline.AirlineID{
...
@ObjectModel.text.association: '_Airline'
key Connection.carrier_id as AirlineID,
...
/*Association*/
_Airline
}
4. Open the CDS view /DMO/I_Flight_R for which you also want to display the text. Use the annotation
@ObjectModel.text.association: '<_AssocToTextProvider>' on the element AirlineID and
reference the association _Airline as a text association. Then activate the CDS view.
...
define view /DMO/I_Flight_R
as select from /dmo/flight as Flight
association [1] to /DMO/I_Carrier_R as _Airline on
$projection.AirlineID = _Airline.AirlineID
{
...
@ObjectModel.text.association: '_Airline'
Results
You have established a text association from the two CDS views that contain the two-letter airline code. The
text is taken from the text provider view and is displayed in the UI together with the two-letter code, as can be
seen in the following image. Now, the airlines are clearly identifiable by their full names.
Note
You can also establish a text association with the CDS view /DMO/I_Airport to provide a full text for the
airport elements Departure Airport Code and Destination Airport Code.
Related Information
Use value helps to make it easier to find the correct value for selection fields on the UI.
We created some selection fields for the UI to enable the end user to find a suitable flight in the flight app. The
difficulty, however, is to know the correct three letter airport code to determine the correct departure or
destination airport for a flight. You can enable a value help option to assist the end user in finding correct
values. The end user then gets direct access to a value help dialog in which he or she can enter values, such as
city names or country codes, to find the suitable airport code. This is shown in the following figure.
Prerequisites
A value help provider view already exists. In our case, the CDS view /DMO/I_Airport contains the relevant
fields Name, City and CountryCode to help find the airport code.
Context
To provide a value help for the selection fields of the elements Departure Airport Code and Destination
Airport Code in the CDS view /DMO/I_Connection_R, use the annotation
@Consumption.valueHelpDefinition.
Procedure
{...
@Consumption.valueHelpDefinition: [{ entity: { name: '/DMO/
I_Airport',
element: 'AirportID' } }]
airport_from_id as DepartureAirport,
...
@Consumption.valueHelpDefinition: [{ entity: { name: '/DMO/
I_Airport' ,
element:
'AirportID' } }]
airport_to_id as DestinationAirport,
…}
Note
For the value help, you do not need to establish an association between the source and the target view.
Nevertheless, you need to make sure that the target view is part of the service.
If not already done, add the value help provider view to the service definition.
Results
You have implemented a value help for the selection fields that refer to the airport code elements. Clicking
opens the value help dialog with the option to use all the elements of the value help provider view /DMO/
I_Airport to find the correct three letter airport code (see the image above). Additionally, the selection fields
are now equipped with a value completion option. This means that, once you start typing in the selection field,
the possible values are displayed. This is shown in the following figure:
Related Information
Include a search input field to execute a text and fuzzy search on multiple elements.
Prerequisites
The CDS view must be suitable for text and fuzzy search enabling. For more information, take a look at the
corresponding topics in the SAP HANA Search Developer Guide.
Context
In the previous chapter, you implemented a value help for the selection fields, which are based on airport code
fields. This means you can easily search for connections from and to certain airports. However, it is not possible
to search for connections by specific airlines or flights that are operated on a specific day. Maybe the end user
even wants to search for values of different elements, say someone wants to find out which connections are
available to or from Frankfurt on, say, February 18, 2019. This is exactly the use case when search capabilities
are required.
You use annotations to enable search capabilities for a specific CDS view and also to mark the elements that
you want to be included in the search scope. You can also define fuzziness thresholds for the search, so that
entries are also found if the values are entered in the search field with incorrect spelling. This makes the search
easier and more effective.
The following section explains the process used to implement search capabilities when the end user wants to
search for the following:
Whereas the first two searches operate only on the CDS view /DMO/I_Connection_R (because the search
target elements Airline, Departure Airport Code and Destination Airport Code are part of this
view), the searches for days and plane types require the search to be operated on the associated view, since
the elements Flight Date and Plane Type are part of /DMO/I_Flight_R. In addition, we also want to be
able to search for the full airline name. That is why the text provider view /DMO/I_Carrier must also be
search enabled.
Procedure
If you use this annotation in a CDS view, you have to assign at least one default search element.
@AbapCatalog.sqlViewName: '/DMO/ICONNECT_R'
@AbapCatalog.compiler.compareFilter: true
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Read-Only E2E: Data Model Connection'
@UI.headerInfo: { typeName: 'Connection',
typeNamePlural: 'Connections' }
@Search.searchable: true //*** exposes a standard search field on
the UI
define view /DMO/I_Connection_R
as select from /dmo/connection as Connection
...
c. Choose the elements that you want to search for and annotate them with
@Search.defaultSearchElement: true.
d. Define a fuzziness threshold for the searchable elements with @Search.fuzzinessThreshold:
<fuzziness_value>.
Note
You can set a fuzziness threshold of 0 to 1. SAP recommends that you use 0.7 to begin with. This
means that every item with a 70% match and greater is found. You can then customize the
threshold.
The search elements in the connection view are Airline, Departure Airport Code, and
Destination Airport Code. As the first one only consists of a two letter code, you do not need to
define a fuzziness threshold for this element.
{
…
@Search.defaultSearchElement: true
key Connection.carrier_id as AirlineID,
…
@Search.defaultSearchElement: true
@Search.fuzzinessThreshold: 0.7
Connection.airport_from_id as DepartureAirport,
…
@Search.defaultSearchElement: true
@Search.fuzzinessThreshold: 0.7
Connection.airport_to_id as DestinationAirport,
… }
{…
@Search.defaultSearchElement: true
_Flight,
… }
Now the end user can search for connections by specific airlines or to or from a specific airport. The
following figure illustrates the connections operated by SQ (Singapore Airlines Limited).
@AbapCatalog.sqlViewName: '/DMO/IFLIGHT_R'
@AbapCatalog.compiler.compareFilter: true
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Read-Only E2": Data Model Flight'
@Search.searchable: true
define view /DMO/I_Flight_R
as select from /dmo/flight as Flight
{…
@Search.defaultSearchElement: true
@Search.fuzzinessThreshold: 0.7
key Flight.flight_date as FlightDate,
@Search.defaultSearchElement: true
@Search.fuzzinessThreshold: 0.7
Flight.plane_type_id as PlaneType,
… }
The association is now search enabled. This means it is possible to search for connections with a specific
plane type and at a specific airport at the same time, even though the elements are not part of the same
view. The following figure displays the search results for the search for the machine with the code A380
and the airport with the code FRA.
3. Enable the relevant elements for searches in /DMO/I_Carrier to be able to search for the full airline
names and not only for the two letter code. As previously described, open the CDS view /DMO/I_Carrier,
mark it as search enabled, and then choose the elements to search on with a proper fuzziness
threshold.
@AbapCatalog.sqlViewName: '/DMO/ICARRIER_RE'
@AbapCatalog.compiler.compareFilter: true
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Read-Only E2E: Data Model Carrier'
@Search.searchable: true
define view /DMO/I_Carrier_R
as select from /dmo/carrier as Airline
{…
The text association now enables the end user to search for the full names of airlines, even though the full
name element Name is not exposed on the UI. Even if the full name is not complete in the search field, the
search still presents the right results, as can be seen in he following figure.
Related Information
Enabling Text and Fuzzy Searches in SAP Fiori Apps [page 561]
In this chapter, you will be guided through all steps necessary to develop a travel administration app based on
the business object’s managed runtime infrastructure.
Introduction
Let's assume you want to develop completely new transactional apps for SAP Fiori UI or for an arbitrary Web
API consumer in SAP Cloud Platform or ABAP environment. Let's further assume that you don't have any
transactional buffer or business logic implementation or authorization functionality available to you.
In that situation you can benefit from the managed implementation type of the ABAP RESTful programming
model.
Unlike the unmanaged scenario which aims for reusing the persistence layer and integrating an already existing
business logic, the managed scenario addresses use cases where all essential parts of an application must be
developed from scratch. However, these new applications can highly benefit from out-of-the-box support for
transactional processing. Whereas for the unmanaged implementation type, the application developer must
implement essential components of the REST contract manually, for the managed scenario, all required
standard operations (create, update, delete) must only be specified in the behavior definition to obtain a ready-
to-run business object. The technical implementation aspects are taken over by the business object runtime
Architecture Overview
The architecture for a managed scenario can be depicted as in the simplified figure below.
The new implementation of business objects and their functionality is a key component in the architecture. In
addition to the generic implementation of transactional handling, these objects also provide the application-
specific business logic. Even with new developments, you will certainly always use already available reuse
functionality whenever possible. For example, our demo application uses value help views and text views for
text information retrieval, currency conversion procedures, and message constants as reuse components.
Note
If you are running ABAP developments on SAP Cloud Platform, then you have the option to introduce reuse
components after the custom code migration into ABAP Environment.
Prerequisites
Developing the scenario that is described in the subsequent chapters requires the following:
● You have access to and an account for SAP Cloud Platform, ABAP environment.
● You have installed ABAP Development Tools (ADT).
SAP recommends to use the latest version of the client installation. The ADT download is available on the
update site https://tools.hana.ondemand.com/.
Remember
The namespace /DMO/ is reserved for the demo content. Apart from the downloaded ABAP Flight Scenario,
do not use the namespace /DMO/ and do not create any development objects in the downloaded packages.
You can access the development objects in /DMO/ from your own namespace.
However, if you want to recreate all development objects of this demo content, make sure that you use
different names from this documentation.
Constraints
When working with the managed implementation type the following constraints are given:
● Determinations: You can't use on-modify determinations to trigger other on-modify determinations
(except in another BO, but without cycles).
● Authority: Only instance-based authorizations are available. That means, static authorizations are not
available. Therefore, you cannot apply authorization checks to create operations.
● Numbering: Late numbering is not supported.
● Functions: Functions are not supported.
● Actions: Factory Actions are not supported for the managed implementation type.
The figure below provides an overview of the main development objects involved when creating new
transactional apps based on the managed implementation type.
The development of new managed applications mainly requires developers to perform the following
fundamental activities:
Using model-driven development approach, you may focus your attention more on the actual business
requirements themselves by developing actions, validations, determinations and providing feature control for
each entity of the business object structure.
To enable a flexible consumption of the resulting business service, a projection [page 959] is introduced as a
separate layer within the application development. This layer is required for service projections for different
consumption scenarios such as Web APIs and UI-related services according to Fiori UI role-based design.
This essentially includes three steps: providing a data model with CDS views for projections, modelling
behavior definitions for projections as well as defining business services that expose projections for
consumption.
More on this: Developing a Projection Layer for Flexible Service Consumption [page 274]
Using ABAP Development Tools, you have the option of publishing the service to the service repository of your
local system. As soon as the service is published, it is ready for consumption through an OData client, such as
an SAP Fiori app. The service binding editor also offers a preview tool that you can use for testing the resulting
app within your ABAP development environment.
In this demo scenario, we will implement a simple travel provider app that can be used to manage flight
bookings. A single travel should be booked by the travel provider for an existing customer through a travel
agency and include one or multiple flight bookings. In addition, one or more supplements should be able to be
booked for each flight.
Requirements
Based on the same data model, two different views of the travel app are to be realized, each corresponding to
two different user roles:
● The processor acquires all the data relevant to flight bookings: he or she creates individual travel
instances, creates and manages individual flights, and adds supplements to flight bookings. The
accumulated travel costs should be calculated automatically when the underlying bookings are updated.
When editing individual travel data, validation must be made for data consistency and, in the case of an
error, be issued as an appropriate message to the user.
● Travel
● Booking
● BookingSupplement
That is, each travel instance has 0..N bookings and each booking has 0..N booking supplements.
The figure below shows the composition relationship between the travel, the (flight) booking and the
supplement entities, where the travel entity represents the root of the data model.
Behavior
In the following table, we briefly outline the scope of the business logic to be implemented in the demo
scenario. Bear in mind that the managed implementation type comes into play with a few new concepts such
as validations and determinations, which will be introduced later on over the course of this E2E guide.
Processor Role:
Processor
Entity Behavior
Travel Operations:
Validations:
Feature Control:
Booking Operations:
● Update
● Create booking supplements by association
Validations:
Determinations:
Feature Control:
● Update
Determinations:
Feature Control:
Approver Role:
Approver
Entity Behavior
Travel Operations:
Validations:
Feature Control:
Restriction
The current version of the ABAP RESTful programming model does not support late numbering [page 955]
for managed implementation type. Therefore, in our demo scenario, the numbering is not implemented in
the save sequence using the adjust_numbering( ) method when creating instances for travels,
bookings, and booking supplements.
As shown in the figure below, the structure of a business object is defined by the sequence of compositions and
to-parent associations between CDS entities with a root entity on top of the composition tree. Each level of a
business object’s composition tree can offer a set of operations that are specified in the behavior definition
that refers to the entities of the CDS data model. In the case of managed implementation type, the standard
operations (create, update, delete) must only be specified in the behavior definition to obtain a ready-to-run
business object. The business object runtime infrastructure already implements the interaction phase and the
save sequence generically and provides an out-of-the-box support for transactional processing.
The figure below displays the resulting “Travel” business object in Relation Explorer view. If you choose the
Business Object context, the Relation Explorer provides the composition tree of the business object and
displays all operations of the selected entity.
Related Information
From a structural point of view, a business object consists of a tree of entities that are linked by compositions.
Every entity in this composition tree is an element that is modeled with a CDS entity.
For our demo travel booking scenario, we will implement a 3-level hierarchy composition tree.
● Travel
● Booking
That is, each travel instance has 0..N bookings and each booking has 0..N booking supplements.
The figure below shows the composition relationship between the travel, the (flight) booking and the
supplement entities, where the travel entity represents the root of the data model.
To access business data from other entities in our demo content, we are going to reuse master data from the
package /DMO/FLIGHT_REUSE that is part of the downloaded demo content /DMO/FLIGHT. Some of these
entities will be used primarily as text provider views for retrieving text information and as value help provider
views for individual input fields on the UI.
Additional entities for currencies (I_Currency) and countries (I_Country) are generally available in your
system and are included in our data model using associations.
Related Information
Travel data of our demo application is distributed across multiple database tables: a table for the travel header
data, a table for flight bookings, and booking supplements.
To build up our demo scenario from scratch, we will first create a suitable set of database tables using ADT
ABAP Dictionary tools and then use these tables as a data source in the new CDS-based data model.
The standard use case is to work with client-dependent tables. That means, the database table has a key field
in which the client is maintained. Like this, you can control the access to the database entries based on the
client. However, there are use cases in which the instances of the database tables are not client-specific. These
tables do not contain a client field. The RAP managed BO runtime also supports scenarios with client-
independent database tables. For more information, see Using Client-Independent Database Tables in
Managed Transactional Apps [page 197].
To provide data persistence for our sample travel management scenario, we assume that you...
● Have the standard developer authorization profile to create ABAP development objects with ABAP
Development Tools.
● Reuse the data elements from the ABAP Flight Reference Scenario (package: /DMO/FLIGHT_LEGACY)
when editing table fields for tables to be created.
To create and work with database tables in ABAP Development Tools (ADT), do the following:
1. Open the source-based ABAP Dictionary editor in ADT and create the corresponding database tables listed
below. For further information, see: .
This table defines general travel data, such as the key element travel ID, agency ID or customer ID, the date for
travel’s begin and end, as well as the overall status of the travel bookings, and the total price of an individual
travel. In addition, the fields for standard administration data, such as the respective user or the time of
creation, are added to the table.
This table will be used for managing flight booking data, such the flight connection, the carrier, or the price and
flight date, and finally, the status of flight bookings.
This table is used to add additional products to a travel booking. For example, the customer can book together
with a flight, a drink or a meal.
Related Information
Database Tables
The RAP managed BO runtime supports transactional scenarios with client-independent database tables. This
topic provides information about things you need to consider when using client-independent tables.
The procedure for creating client-independent database tables does not differ from the process described in
Procedure: Creating Tables [page 194]. You can simply remove the client field in the template for database
tables.
The following codeblock shows the travel database table without a client field.
The business object must be consistent with regard to the client fields in the database tables. This means that
CDS views that are used for modeling the data model in scenarios with client-independent tables must not
contain a client element. In particular, the generated SQL-view of the CDS view must not contain such an
element.
There must be client-consistency among the entities in the business object composition structure. It is not
possible to have one entity that is client-dependent and one that is client-independent with a compositional
relationship between them.
The client field in database tables influences the runtime of business objects during lock. While locks are set on
entity instances only for one specific client in client-dependent tables, locks in client-independent tables lock
instances for all clients.
Note
In scenarios in which you have client-dependent database tables, but join client-independent fields from
other database tables to your CDS view, the managed BO runtime locks the instances specific to the client.
This means only the fields from the client-dependent database tables are locked. If you also want to lock
the client-independent fields, you have to implement an unmanaged lock [page 964].
Note
In scenarios, in which you use an unmanaged save [page 964] in the managed scenarios, the managed BO
runtime always sets a client-specific lock. If you want to lock client-independently, you have to use an
unmanaged lock [page 964].
Related Information
In this step you create CDS views as the basis for the data model of our demo scenario. For each data definition
a corresponding development object, the related CDS views and the corresponding database views are created
too.
Note
Naming CDS views: Since CDS views are (public) interface views, they are prefixed with I_ in accordance
with the VDM (virtual data model) naming convention. In addition, we add the suffix _M to the view name in
case it is specific for our managed implementation type scenario. For detailed information, see: Naming
Conventions for Development Objects [page 918]
Data Definition
/DMO/I_TRAVEL_M /DMO/ A Travel entity defines general travel data, such as the agency ID
TRAVEL_M or customer ID, overall status of the travel bookings, and the to
/DMO/I_Travel_M (root entity)
tal price of a travel.
(DB table)
/DMO/ITRAVEL_M
/DMO/I_BOOKING_M /DMO/ The booking entity is used for managing flight booking data,
BOOKING_M such as the customer, the flight connection, or the price and
/DMO/I_Booking_M (child entity)
flight date.
(DB table)
/DMO/IBOOKING_M
/DMO/I_BOOKSUPPL_M /DMO/ This entity is used to add additional products to a travel booking.
BOOKSUPPL_
/DMO/I_BookSuppl_M (child en
M
tity)
(DB table)
/DMO/IBOOKSUP_M
To launch the wizard tool for creating a data definition, do the following:
The listing 1 (below) provides you with the implementation of the CDS data model for managing travel
instances, where the database table /dmo/travel_m serves as the data source for the corresponding CDS
view /DMO/I_Travel_M. This CDS view defines the root entity of the data model and represents the root of
compositional hierarchy for the travel business object to be created.
To be able to access master data from other entities, a set of associations is defined in the CDS source code.
These associations refer to CDS entities (/DMO/I_Agency, /DMO/I_Customer) that are part of our demo
application scenario. Some of these views are used primarily as text views for retrieving text information and as
value help provider views for specific UI fields.
Finally, the fields for standard administration data are added to the select list, where the persistent field
last_changed_at plays a special role as an ETag [page 953] field.
To ensure uniform data processing on the consumer side, all administrative as well as quantity and currency
fields are provided with appropriate @Semantics annotations. These annotations are necessary to support
managed ETag handling, that is the automatic update of the relevant ETag field on every operation.
@AbapCatalog.sqlViewName: '/DMO/ITRAVEL_M'
@AbapCatalog.compiler.compareFilter: true
@AbapCatalog.preserveKey: true
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Travel view - CDS data model'
define root view /DMO/I_Travel_M
as select from /dmo/travel_m as Travel -- the travel table is the
data source for this view
composition [0..*] of /DMO/I_Booking_M as _Booking
association [0..1] to /DMO/I_Agency as _Agency on
$projection.agency_id = _Agency.AgencyID
association [0..1] to /DMO/I_Customer as _Customer on
$projection.customer_id = _Customer.CustomerID
association [0..1] to I_Currency as _Currency on
$projection.currency_code = _Currency.Currency
{
key travel_id,
agency_id,
customer_id,
begin_date,
end_date,
@Semantics.amount.currencyCode: 'currency_code'
booking_fee,
@Semantics.amount.currencyCode: 'currency_code'
total_price,
@Semantics.currencyCode: true
currency_code,
overall_status,
description,
@Semantics.user.createdBy: true
created_by,
@Semantics.systemDateTime.createdAt: true
created_at,
@Semantics.user.lastChangedBy: true
last_changed_by,
//local ETag field --> OData ETag
@Semantics.systemDateTime.localInstanceLastChangedAt: true
last_changed_at,
/* Associations */
Listing 2 (below) provides you with a data model implementation of the booking entity. All fields that are
declared in the database table are used in the CDS view. The administrative field last_changed_at is used for
optimistic concurrency control on all nodes. That means, every business object entity stores its own eTag.
In the data definition of the root entity /DMO/I_Travel_M, we specified the booking entity /DMO/
I_Booking_M as a composition child entity. Reversely, this relationship requires an association to their
compositional parent entity – from the child entity. This relationship is expressed by the keyword
ASSOCIATION TO PARENT.
To provide a data model for 3-tier entity hierarchy, we also define a composition relationship from booking to a
booking supplement entity. In our example, we specify /DMO/I_BookSuppl_M as child entity in the
composition _BookSupplement. The cardinality [0 .. *] expresses that any number of booking supplement
instances can be assigned to each booking instance.
To be able to access data from other entities, a set of additional associations (_Customer, _Carrier, and
_Connection) is defined in the CDS source code.
The SELECT list includes all elements of a booking entity that are relevant for consumption in a user interface.
@AbapCatalog.sqlViewName: '/DMO/IBOOKING_M'
@AbapCatalog.compiler.compareFilter: true
@AbapCatalog.preserveKey: true
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Booking view'
define view /DMO/I_Booking_M
as select from /dmo/booking_m as Booking
association to parent /DMO/I_Travel_M as _Travel on
$projection.travel_id = _Travel.travel_id
composition [0..*] of /DMO/I_BookSuppl_M as _BookSupplement
association [1..1] to /DMO/I_Customer as _Customer on
$projection.customer_id = _Customer.CustomerID
association [1..1] to /DMO/I_Carrier as _Carrier on
$projection.carrier_id = _Carrier.AirlineID
association [1..1] to /DMO/I_Connection as _Connection on
$projection.carrier_id = _Connection.AirlineID
and
$projection.connection_id = _Connection.ConnectionID
{
key travel_id,
key booking_id,
booking_date,
customer_id,
carrier_id,
connection_id,
flight_date,
@Semantics.amount.currencyCode: 'currency_code'
The 3-tier composition relationship requires an association for the booking supplement child entity /DMO/
I_BookSuppl_M entity to their compositional parent entity.
Like the booking entity, the booking supplement entity also uses the field last_changed_at to store eTag
values.
To access master data from other entities, additional associations _Product and _SupplementText are
defined in the CDS source code.
@AbapCatalog.sqlViewName: '/DMO/IBOOKSUP_M'
@AbapCatalog.compiler.compareFilter: true
@AbapCatalog.preserveKey: true
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Booking Supplement View - CDS data model'
define view /DMO/I_BookSuppl_M
as select from /dmo/booksuppl_m as BookingSupplement
association to parent /DMO/I_Booking_M as _Booking on
$projection.travel_id = _Booking.travel_id
and
$projection.booking_id = _Booking.booking_id
association [1..1] to /DMO/I_Travel_M as _Travel on
$projection.travel_id = _Travel.travel_id
association [1..1] to /DMO/I_Supplement as _Product on
$projection.supplement_id = _Product.SupplementID
association [1..*] to /DMO/I_SupplementText as _SupplementText on
$projection.supplement_id = _SupplementText.SupplementID
{
key travel_id,
key booking_id,
key booking_supplement_id,
supplement_id,
@Semantics.amount.currencyCode: 'currency_code'
price,
@Semantics.currencyCode: true
currency_code,
//local ETag field --> OData ETag
@Semantics.systemDateTime.localInstanceLastChangedAt: true
last_changed_at,
/* Associations */
_Travel,
In this step, we will limit our focus to modeling an elementary behavior in which only the standard operations
create(), update(), and delete() are defined for each entity. These operations, along with some basic
properties (behavior characteristics), should already be sufficient to obtain a ready-to-run business object.
To launch the wizard tool for creating a behavior definition, do the following:
To define the transactional behavior for a CDS Entity for managed implementation type, the following syntax is
used:
Explanation
A behavior definition consists of a header information and a set of definitions for entity behavior. Each entity of
the composition tree can be referred in the behavior definition at most once.
Remember
Consider that if an entity does not occur in the behavior definition, then it would not be modifiable within
the ABAP RESTful application programming model.
Values Effect
managed This implementation type addresses use cases where all essential parts of an application
must be developed from scratch.
Whereas for the unmanaged implementation type, the application developer must imple
ment essential components of the REST contract manually, for the managed scenario, on
the other hand, all required standard operations (create, update, delete) must only be speci
fied in the behavior definition to obtain a ready-to-run business object. New applications
can highly benefit from out-of-the-box support for transactional processing, since the tech
nical implementation aspects are taken over by the business object runtime infrastructure.
In this case, the business object framework implements generically the interaction phase
and the save sequence. The application developer can then focus on business logic that is
implemented using actions, validations, determinations and user interaction.
The behavior description is divided into a section with behavior characteristic that describes several properties
for each entity, followed by information on any operations, validations, and determinations enclosed in the
brackets {…}.
The AliasName defined in the behavior definition for CDSEntity gives you the option of introducing a more
concise name than the entity name that is hence easier to read. Note that the length of the AliasName is
restricted to 20 characters. The AliasName becomes visible in the implementation part when implementing
the BO’s business logic.
The Behavior Definition Language (BDL) [page 945] allows you to add the following properties to a behavior
definition:
Behavior Characteristic
Characteristic Effect
implementation in When modelling the behavior for an individual entity, you have the option of assigning a spe
class ...unique cific behavior pool that implements the behavior for this entity. Behavior for the entity in
question is implemented in a behavior pool with the specified name ABAP_ClASS_NAME.
persistent This property defines the database table DB_TABLE_NAME for storing CDSEntity’s data
changes that result from transactional behavior (in managed implementation type).
table ...
etag master Field An ETag is used for optimistic concurrency control in the OData protocol to help prevent si
multaneous updates of a resource from overwriting each other.
etag dependent by
_Association The managed scenario updates administrative fields automatically if they are annotated
with the respective annotations:
@Semantics.user.createdBy: true
@Semantics.systemDateTime.createdAt: true
@Semantics.user.lastChangedBy: true
@Semantics.systemDateTime.localInstanceLastChangedAt:
true
lock master In the behavior definition, you can determine which entities support direct locking (lock
master) and which entities depend on the locking status of a parent or root entity (lock
/ lock master
dependent by). For lock dependents it is required to specify which association is used to
unmanaged
determine the lock master. This association must be explicitly defined in the behavior defini-
/lock dependent by tion with association _AssociationToLockMasterEntity.
_Association
If you want to implement your own locking logic in the managed scenario, you can define an
unmanaged lock with lock master unmanaged. In this case, you have to implement
the method FOR LOCK just like in the unmanaged scenario, see Implementing the LOCK
Operation [page 366].
For more information, see Pessimistic Concurrency Control (Locking) [page 130].
authorization To protect data from unauthorized access, you can add authorization checks for modifying
master / operations to each entity of the business object. For standard operations such as update,
authorization delete, as well as for create by associations and actions, the authorization control is then
_Association
Remember
With the current release, the root entity is always defined as authorization
master, whereas all child entities are defined as authorization dependent. If
a child entity is modified (update, delete, create by association) or an action is invoked
on that entity, the authorization check (that is implemented in the behavior class) of the
master is triggered to check if the operation is allowed for being accessed.
Restriction
With the current release, only instance-based authorization control is supported:
authorization master(instance). This means, static authorization is not yet
available. Therefore, you cannot apply authorization checks to create operation (static
operation).
Note
The operations create (by association), update, delete, and actions on child entities are
treated as an update on the corresponding root entity (authorization master). Thus, the
authorization check implementation is triggered to check the authorization for update
at master level - despite of the fact that it was a create (by association), update,
delete, or action request at dependent level.
Field Properties
Property Effect
field (readonly) This property defines that values of the specified fields must not be created or updated by
the consumer. You can set this property in the behavior definition.
Note
The BO runtime rejects external EML MODIFY requests that include read-only fields for
update or create operations.
Read-only fields that are included in OData call to update or create instances are ig
nored while possible other fields are processed for update and create operations.
field (mandatory) Defines that the specified fields must be filled with values – at least at save point of time.
The specified fields are marked with a specific (red star) icon to indicate them as manda
tory.
field (numbering : Defines that the managed BO framework draws the key value for the determined key field
managed) automatically after the CREATE request is executed, if the key value is not provided exter
nally.
If you additionally set the key field to readonly, only internal numbering is possible.
field (features: Defines that specified field is handled by dynamic field control at instance level.
instance)
The dynamic field control must be implemented in a handler class of the behavior pool.
More on this: Modeling Static and Dynamic Feature Control [page 225]
mapping for ... The addition corresponding automatically maps field types of entity fields to types of
corresponding
DB table fields with the same name. The corresponding fields of different names can be
specified through explicitly listed field pairs.
create | update | An important part of the transactional behavior of a business object are the standard opera
delete tions create, update and delete (CUD). Whenever an entity can be created, updated,
or deleted, these operations must be declared in the behavior definition.
Note
Creating instances of managed BOs without managed numbering requires that all pri
mary key values are provided by the consumer. If the consumer does not provide values
for all primary key fields, the instance is created with initial primary key values.
To only provide an operation without exposing it to consumers, the option internal can
be set before the operation, for example, internal update. An internal operation can only be
accessed from the business logic inside the business object implementation such as from a
determination.
association All compositions that form the business object’s structure must also be declared in the be
havior definition as associations. An abbreviation AbbreviationName needs to be de
fined if the composition name in the CDS view is longer than 11 characters. The keyword
{create;} is used to declare that the association is create-enabled, which means that in
stances of the associated entity can be created by the source of the association.
Note
The create_by_association expects that the parent key fields of the child entity
that is to be created are read-only.
validation A validation is an implicitly executed function that checks the consistency of entity instan
ces that belong to a business object.
The BO framework implicitly evaluates all validations if the validation’s trigger condition is
fulfilled at a certain validation time. A trigger condition consists of trigger operation (create,
update, create by association) and list of entity fields (trigger elements) belonging to the
same entity the validation is assigned to. For validations, the current version of the program
ming model supports only the save phase as validation time:
Validations are not allowed to modify data; they can reject the save of inconsistent data and
return messages.
determination A determination is an implicitly executed function that is used to handle side effects of mod
ifications by changing instances and returning messages.
(1) The option on (save | modify) { field f1[, f2, ..., fn]; } de
fines the determination time on save (before save) or on modify (immediately after
modification) and the trigger fields, which (together with the create or update operation)
form the trigger condition for the determination.
(2) The option on (save | modify) { create; } defines the determination time
on save or on modify for the entire entity (all entity fields are trigger fields), which to
gether with the create operation (no update operation!) form the trigger condition for the
determination. .
As depicted in the listing below, the source code of the behavior definition consists of a header information and
three definitions for entity behavior: one for the root travel entity and two for the child entities booking and
booking supplements – corresponding to the composition tree of the business object. Note that for each entity
of the composition tree, the transactional behavior can be defined in the behavior definition at most once. All
required transactional operations of an individual business object’s node are specified in the same behavior
definition (that is introduced by the keyword DEFINE BEHAVIOR FOR ... ).
The header specifies managed implementation type of our business object’s provider since we are going to
implement all essential parts of an application from scratch.
For this implementation type, all required standard operations (create, update, delete) and create by
association must only be specified in the behavior definition to obtain a ready-to-run business object.
Note
The create_by_association expects that the parent key fields of the child entity that is to be created
are read-only as the parent key fields are filled automatically during runtime.
The sub node of TRAVEL business object structure refers to the corresponding data model for bookings that is
represented by the child entity /DMO/I_Booking_M. The transactional handling of booking child entity is
determined by the standard operation update. In addition, the create-enabled association _BookSupplement
is defined for creation of supplements as part of associated booking instances. Similarly, we model the
behavior of the booking supplement entity /DMO/I_BookSuppl_M. Since we reach the end of the composition
tree with this entity, there is no need to define a create-enabled association.
When providing modifying operations, we also must take care for locking support for all relevant entities of the
composition tree. For this purpose, the root entity travel is defined as lock master and the child entities as
lock dependent. In the latter case, the binding information is specified via the association leading from child
entity instance to its lock master instance.
In the managed scenario, all entities receive a separate eTag master field to store eTag values. Like this, every
entity can be accessed and modified independently of others.
managed;
define behavior for /DMO/I_Travel_M alias travel
persistent table /DMO/TRAVEL_M
etag master last_changed_at
lock master
{
mapping for /DMO/TRAVEL_M corresponding;
create;
update;
delete;
association _Booking { create; }
}
define behavior for /DMO/I_Booking_M alias booking
persistent table /DMO/BOOKING_M
etag master last_changed_at
lock dependent by _Travel
{
field ( readonly ) travel_id;
mapping for /DMO/BOOKING_M corresponding;
update;
association _BookSupplement { create; }
association _Travel { }
}
define behavior for /DMO/I_BookSuppl_M alias booksuppl
persistent table /DMO/BOOKSUPPL_M
etag master last_changed_at
lock dependent by _Travel
{
field ( readonly ) travel_id, booking_id;
mapping for /DMO/BOOKSUPPL_M corresponding;
update;
association _Travel { }
At this point, you can now test the newly created travel business object for its basic functionality by creating
some new instances for the 3 entities (travel, booking, booking supplement) that correspond to the business
object’s structure, modifying the existing data sets or deleting existing instances.
Procedures
Tip
Since UI annotations are not yet defined in the CDS views provided, you can benefit from the generic
templates by choosing the Settings ( ) button on the (initial) screen that appears. In the Settings
dialog, click on the Select all box for Columns and confirm with OK. Then choose the Go button.
More on this: Creating an OData Service [page 23] and Designing the User Interface for a Fiori Elements App
[page 34]
To generate a test class for an individual entity of the OData service, perform the following
steps:
1. Create the service definition.
2. Specify which CDS entities are to be exposed as a service.
3. Create a service binding for service consumption.
4. Activate the service endpoint in the local service repository.
5. To launch the test class creation wizard, select the relevant entity in the service binding editor and choose
New ABAP Test Class from context menu.
For the selected entity, the wizard creates a test class with a source code template for ABAP unit tests. After
completing the test code, you can perform CUD operations on relevant entity.
Note
You can either create a separate test class for each entity or copy and paste the generated code, then
change the name of the entity accordingly for writing ABAP unit tests for other entity.
To test the business object functionality by implementing EML consumer class, you can, for
example, proceed as follows:
Tip
To check the results of the MODIFY call implemented in the consumer class, run the main method of the
consumer class by pressing F9 key in the class editor and then search for the created, or updated (and
deleted) travel, booking and booking supplement instances in the data preview tool ( F8 ).
More on this: Entity Manipulation Language (EML) [page 154] and Consuming Business Objects with EML
[page 593]
The managed scenario addresses use cases where all essential parts of an application are to be developed
from scratch. New applications like this can highly benefit from out-of-the-box support for transactional
processing. The corresponding BO runtime manages the entire life cycle of your business objects and covers all
aspects of your business application development.
In a managed scenario, the business object framework implements generically the interaction phase and the
save sequence. You, as an application developer can then focus on business logic that is implemented by
adding actions, validations ad determinations and user interaction.
The figure below displays the resulting "Travel" business object in the Relation Explorer view. If you choose the
Business Object context, the Relation Explorer provides the composition tree of the business object and
displays all operations (including actions), determinations and validations defined by the selected entity.
Contents
Until this step, we got by without any line of ABAP code. This was also not necessary, as in case of managed
implementation type the technical implementation aspects are taken over by the business object runtime
infrastructure itself. In this case, the business object framework implements generically the interaction phase
and the save sequence.
However, to provide our application with specific business logic, we will on the one hand extend the behavior
definition with actions, feature control, validations and determinations and on the other hand implement it in
ABAP code.
In this step, you create the ABAP classes required for extending behavior artifacts of the corresponding
behavior definition that you created earlier.
In doing so, we apply the contribution pattern and split the behavior implementation into different behavior
pools, one for the travel root entity and the others for the booking and booking supplement child entities (as
shown in figure below). In addition, we will make use of a separate auxiliary class for implementing helper
methods (such as for methods reused in different handlers) that can be reused in each behavior
implementation class.
To launch the wizard tool for creating a behavior implementation, do the following:
Compared to the standard ABAP class, the generated behavior pool (in our case /DMO/BP_TRAVEL_M)
provides you with an extension FOR BEHAVIOR OF.
The real substance of a behavior pool is located in Local Types (there is currently no implementation yet). Here
you can implement special local classes, namely handler classes for additional operations (such as actions),
validations and determinations that are triggered at specific points in time within the interaction phase.
Note that these classes can be instantiated or invoked only by the ABAP runtime environment (virtual
machine) [page 944].
Create another behavior pool class /DMO/BP_BOOKING_M, for example, by duplicating the just-created
class /DMO/BP_TRAVEL_M .
Note
The name behind FOR BEHAVIOR OF always refers to the root entity of the business object.
Create another behavior pool class /DMO/BP_BOOKINGSUPPLEMENT_M, for example, by duplicating the
class /DMO/BP_TRAVEL_M
Results
The newly created behavior pools are located in the Behavior Implementations folder of the corresponding
behavior definition node /DMO/I_TRAVEL_M.
To launch the wizard tool for creating a standard ABAP class, do the following:
1. In your ABAP project (or ABAP Cloud Project), select the select the Source Code Library Classes
node of the relevant package.
2. To launch the creation wizard, open the context menu and choose New ABAP Class.
We will use this auxiliary class to offload parts of the handler code and thus reuse them in different behavior
pools.
An action is assigned to an individual entity of a business object and is used to implement a modifying non-
standard operation as part of the business logic.
This demo scenario implements three actions for the travel entity.
The action acceptTravel sets the overall status of the travel entity to accepted (A). The action
rejectTravel sets the overall status of the travel entity to rejected (X). These actions are an instance action
as there is one specific instance of the travel entity for which the status is changed. They return exactly one
instance, namely the instance on which the action is executed. The result is therefore defined as result [1]
$self.
The action createTravelByTemplate creates a new travel instance based on the values of an already
existing travel instance. This action is an instance action as the end user has to select one instance which
serves as the basis for the entity instance to be created. Of course, a different key is assigned. It returns exactly
one instance of the same entity type the action is assigned for. The result is therefore defined as result [1]
$self.
For more information about actions in general, see Actions [page 111].
Corresponding to the listing below, add the following actions to the behavior defintion /DMO/I_TRAVEL_M.
managed;
define behavior for /DMO/I_Travel_M alias travel
implementation in class /DMO/BP_TRAVEL_M unique
...
{
// instance actions
In this topic we demonstrate how you can implement the two actions createTravelByTemplate and
acceptTravel.
Action createTravelByTemplate
This action provides a template for creating a new travel based on an already existing travel instance.
UI Preview
When we run the final app, the UI screen provides the button Create Travel by Template for the action as shown
in the figure below.
In change mode (after the user clicks the Edit button on Fiori UI’s object page), the end user is able to change
the relevant travel fields as shown in the figure below.
As soon as the user chooses the Save button on the object page, the data is persisted in the corresponding
database table and a travel instance with a new travel ID is created.
Definition
In the behavior definition, the action createTravelByTemplate is defined as follows:
managed;
define behavior for /DMO/I_Travel_M alias travel
...
{
...
action createTravelByTemplate result [1] $self;
...
}
The signature of copy_travel method includes the importing parameter keys for referring to the travel
(root) instances, which contains the template data to be copied into the travel root instances to be created. To
identify the root entity, the alias travel is used - according to the alias that is specified in the behavior
definition. The action is then addressed with FOR ACTION travel~createTravelByTemplate.
As given in the listing below, the basic structure of the copy_travel method includes:
- The maximum of travel number from all existing travel instances is determined in the SELECT MAX
statement.
- The EML read operation provides read access to the selected travel instance by using the key. The result of
this read operation is stored in lt_read_result. Based on the resulting data, the parameter lt_create
defines the new data set as a template for the travel instance to be created.
- The actual creation of new travel instance is implemented using EML MODIFY statement. The modifying
operation takes place in LOCAL MODE. As a result, the create operation is excluded from the authorization
checks (that may be implemented later on).
Action acceptTravel
This action provides the end user with the option of accepting individual travels without switching to EDIT
mode.
UI Preview
If you run the app, the resulting UI screen provides you with the label Accept Travel for the new action - as
shown in the figure below.
Action on Fiori UI
Defintion
Remember, in the behavior definition, the action acceptTravel is defined as follows:
managed;
define behavior for /DMO/I_Travel_M alias travel
...
action acceptTravel result [1] $self;
...
}
To update overall_status data of a selected travel instance, operation UPDATE is specified in the EML
MODIFY statement. Note that the modifying operation takes place in LOCAL MODE. As a result, the update
operation is excluded from the authorization checks (that may be implemented later on).
In addition to the fields for travel instance data to be updated, it is necessary to specify the sub-structure
%control, which is part of the import parameter specification of the EML MODIFY statement for updating the
instance data. The UPDATE call allows to trigger delta updates on consumer side where only the key field key-
travel_id and the new value need to be supplied. From provider side, it allows to identify which fields are
overwritten and which need to be kept according to the DB data. The %control structure contains for each
entity field a flag if_abap_behv=>mk-on, which indicates whether the field was provided by the consumer or
not.
Tip
The implementation of the rejectTravel action takes place analogous to the acceptTravel action in
the handler class lhc_travel which belongs to local types component of the behavior pool /DMO/
BP_TRAVEL_M. You can view the corresponding method implementation in the package /DMO/
FLIGHT_MANAGED.
On the UI, actions are triggered by choosing an action button. This action button must be configured in the
backend in the related CDS view. Depending on where you want to use an action button on the UI (list report, or
object page), use the annotation @UI.lineItem or @UI.identification to display an action button.
Note
The UI-annotations for actions must be used as element annotation. However, it does not have an impact
on which element the action is annotated. The action button always appears at the same position on the UI.
You can use the following UI annotation to expose actions to the consumer:
Syntax
Note
The dataAction element references the name of the action as it is defined in the behavior definition.
As an application developer you may want to determine, which entities of your business object should be
create-, delete- and update-enabled, so that they can be modified during consumption using EML [page 953]
or OData services [page 958]. In addition, you may also want to control which (UI) fields of an entity are read-
only or which actions in which usage scenarios are enabled or disabled for execution by the end users.
In ABAP RESTful application programming model, feature control is precisely the way to accomplish such
tasks. It allows you to control the visibility and changeability of fields, operations or entire entities.
The availability of feature control values is modeled in a behavior definition. Unlike static feature control,
instance feature control requires not only a definition but also an implementation in a handler class of the
behavior pool. Therefore, we also talk about dynamic feature control in case of instance feature control.
Both, static and dynamic feature control is defined for different levels (entity, field, or action level) in the
behavior definition by using the following syntax:
[implementation] managed;
define behavior for CDSEntity [alias AliasName]
implementation in class ABAP_ClASS [unique]
...
{
/* (1) Feature control at entity level */
/* Static operation control*/
internal create
internal update
internal delete
/* or (instance-based) dynamic operation control: implementation required! */
update (features: instance);
delete (features: instance);
_association {create (features:instance); }
/* (2) Feature control at field level */
/* Static field control */
field (read only | mandatory) f1[, f2, ..., fn];
/* or dynamic field control: implementation required! */
field (features: instance) f1[, f2, ..., fn];
/* (3) Feature control for actions */
/* Static action control */
internal action ActionName [...]
/* or dynamic action control: implementation required! */
action ( features: instance ) ActionName [... ]
}
To manage a transactional behavior, an entity of a business object offers standard operations create, update,
and delete for external consumption using EML or OData services. To only provide these operations without
exposing them to consumers, the option internal can be set before the operation name.
For defining instance field control, the option (features: instance) must be added to the field in question.
In this case however, the implementation of dynamic feature control in the referenced class pool ABAP_CLASS
is required.
For dynamic control of actions acting on individual entity instances, the option (features: instance) must
be added to the relevant action in the behavior definition.
Corresponding to the listing below, add the static and dynamic feature control to each entity in the behavior
definition.
managed;
define behavior for /DMO/I_Travel_M alias travel
implementation in class /DMO/BP_TRAVEL_M unique
...
{
// administrative fields: read only
field ( readonly ) last_changed_at, last_changed_by, created_at, created_by;
// mandatory fields that are required to create a travel
field ( mandatory ) agency_id, customer_id, begin_date, end_date,
overall_status, booking_fee, currency_code;
// dynamic field control
field (features : instance ) travel_id;
// dynamic action control
action ( features: instance ) acceptTravel result [1] $self;
action ( features: instance ) rejectTravel result [1] $self;
// dynamic operation control
association _Booking { create (features:instance); }
For the travel entity, we define all admin fields as read-only, whereas the fields that are required for creating a
travel instance are defined as mandatory.
The key field travel_id plays a special role and is intended for dynamic field control. In the corresponding
implementation, we will distinguish whether the travel instance already exists (in case of EDIT) or whether it
should still be created.
Examples of dynamic action control are the two methods acceptTravel and rejectTravel. Depending on
the status value (overall_status), these actions can become enabled or disabled.
Creating bookings by association is dynamically controlled. You can only create new bookings if the
overall_status of the corresponding travel instance is not rejected.
For the other entities (booking and booking supplement), there is some feature control on field level. Similar to
creating bookings by association, booking supplements can only be created if the corresponding booking
instance status is not booked.
The implementation of dynamic feature control is based on the ABAP language in a local handler class
(lhc_handler) as part of the behavior pool. As depicted in the listing below, each such local class inherits
from the base handler class CL_ABAP_BEHAVIOR_HANDLER.
The dynamic feature control for an entity is implemented in this handler class using the method FOR
FEATURES. For more information, see <method> FOR FEATURES [page 866].
The output parameter result is used to return the feature control values.
These include
In the following step, we will apply the feature control specifically to our demo scenario.
UI Preview
The figure below shows the realization of the static and dynamic field control using the example of the travel
object page that has been switched to edit mode. All mandatory fields are marked with a red star. Since it is an
already existing travel instance selected, a re-assignment of travel ID number is suppressed in edit mode.
The following figure shows the effect of dynamic control on the two action buttons Accept Travel and Reject
Travel: Since the selected travel instance has a status of A (Accepted), the action button Accept Travel is
disabled.
Another dynamic feature control is defined for the creating bookings by associations and creating booking
supplements by association. In this case the CREATE button for creating associated booking instance is
displayed if the travel instance's overall_status is not rejected. It is hidden, if the travel is set to rejected.
Likewise, the CREATE button for bookings supplements is displayed depending on the booking status of the
corresponding booking instance.
Definition
In the behavior definition, the feature control for travel entity is defined as follows:
Depending on the value of overall_status field, the actions rejectTravel and acceptTravel are
enabled or disabled, and the create by association is possible or not.
The field travel_id is set to readonly in the get_features method. As get_features is not called during
the CREATE operation, the travel_id is modifiable during the CREATE and read only for all other operations,
Expand the following code sample to view the source code of Determination setTravelNumber.
UI Preview
The following figure shows the effect of static and dynamic field control after switching the Fiori UI object page
for bookings in edit mode.
Definition
Listing 3: Dynamic feature control for bookings implemented in behavior class /dmo/
bp_booking_m
UI Preview
The following figure shows the effect of static and dynamic field control after switching the Fiori UI object page
for booking supplements in edit mode.
Definition
A validation is an implicitly executed function intended to check the data consistency of an existing instance of
an entity (consistency validation). It is implicitly invoked by the business object’s framework as soon as a
trigger condition at a predefined point in time is fulfilled. Validations can return messages to the consumer and
reject inconsistent instance data from being saved.
Example
A validation is implemented to check if the customer ID referenced in the Travel order is valid. This
validation is assigned to the "Travel" entity having defined update as the trigger operation on entity "Travel".
As soon as a customer ID is updated by the consumer, the validation will check it and return a warning
message if the ID is unknown.
Remember
Validations never modify any instance data but return the messages and keys of failed (inconsistent) entity
instances.
Note
When working with determinations and validations you have to consider the following runtime specifics:
● The determination result must not change if the determination is executed several times under the
same conditions (idempotence).
● The execution order of validations and determination is not determined. If there is more than one
determination/validation on one operation you cannot know which determination/validation is
executed first.
● It is not allowed to use EML modify statements in validations.
Validations check if an instance is consistent regarding the consistency criteria imposed by the business
requirements. Such validations are called at predefined points within the business object’s transaction cycle to
ensure that business object entities are persisted in a consistent state. Each such validation defines a trigger
condition that is checked by the business object framework during the save phase of a transaction. If the
trigger condition is fulfilled, the consistency validation is executed. Otherwise, if there are inconsistent instance
data, the validation sends messages to the consumer and prevents the transaction from being saved until the
inconsistency is corrected.
Assigned Entity
A validation must always be assigned to the entity for which it may return state messages. If, for example, a
validation is defined for the root entity, the validation implementation must not return messages that refer to
instances of child entities.
Trigger Time
Validations are executed at a predefined point in time within the business object’s transaction cycle. The state
of an instance can only be checked during the save sequence. The validation is triggered implicitly by the
business object framework during the Check_Before_Save point of time, see Save Sequence [page 122].
Note
If you expose your business object for a UI service, you can define side effects to trigger validations or
determinations at other points in time, for example, after updating a field or a field group on the UI in a
draft-enabled application. In non-draft scenarios, side effects can trigger validations and determinations
during the save sequence. Side effects trigger a call to the backend to validate or determine values after
predefined activities on the UI. These side effects must be defined and annotated in the OData document.
Trigger Condition
Validations can be triggered by operations or by field or by both.
Trigger Operations
Validations can be triggered by the operations create, update and delete. Whenever the defined operation
is executed, the validation is called.
Trigger Field
Validations can be triggered by fields belonging to the assigned entity. Whenever the defined field is changed,
the validation is called.
Validations are specified for individual business object’s entities in the behavior definition by using the following
syntax:
[implementation] managed;
define behavior for CDSEntity [alias AliasedName]
implementation in class ABAP_ClASS [unique]
...
{
validation ValidationName on save { create; update; delete; field f1[, f2, ...,
fn]; }
...
}
The definition of a validation of a CDSEntity is initiated with the validation keyword, followed by
ValidationName. For validations only the trigger time on save is possible.
You can define an operation trigger { create, | update, | delete, } that triggers the validation when
the operation is executed.
Note
The update trigger is only supported in combination with the create trigger.
You can also define one or more field triggers { field f1[, f2, ..., fn]; } that trigger the validation
whenever the field is changed.
A validation that belongs to a business object’s entity is implemented in the behavior pool that is specified in
the behavior definition by the keyword implementation in class ABAP_ClASS [unique].
Corresponding to the listing below, add the required validations to each entity in the behavior definition.
managed;
define behavior for /DMO/I_Travel_M alias travel
implementation in class /DMO/BP_TRAVEL_M unique
...
{
...
validation validateCustomer on save { field customer_id; }
validation validateAgency on save { field agency_id; }
validation validateDates on save { field begin_date, end_date; }
validation validateStatus on save { field overall_status; }
}
define behavior for /DMO/I_Booking_M alias booking
implementation in class /DMO/BP_BOOKING_M unique
...
{
...
validation validateStatus on save { field booking_status; }
}
define behavior for /DMO/I_BookSuppl_M alias booksuppl
implementation in class /DMO/BP_BOOKINGSUPPLEMENT_M unique
...
{
...
// No validations
}
For the booking entity, also a validation validateStatus is defined. However, in contrast to the validation with
the same name in the travel entity, booking_status is specified as trigger field.
A validation that is assigned to a business object’s entity is implemented in the behavior pool, which is referred
in the behavior definition by the keyword implementation in class ABAP_ClASS_NAME [unique].
The implementation of a validation is based on the ABAP language (which has been extended compared to the
standard using an additional syntax) in a local handler class as part of the behavior pool.
As depicted in the listing below, each such local class inherits from the base handler class
CL_ABAP_BEHAVIOR_HANDLER. The signature of the corresponding handler method lhc_handler is typed
UI Preview
If the user enters an invalid Customer ID (an ID that is not available in the customer database table /DMO/
Customer) the validation is initiated at the save time. As a result saving the instance data is rejected and a
corresponding message is returned to the user.
Definition
In the behavior definition, the validations on the travel entity are defined as follows:
managed;
define behavior for /DMO/I_Travel_M alias travel
...
{
...
validation validateCustomer on save { field customer_id; }
validation validateAgency on save { field agency_id; }
validation validateDates on save { field begin_date, end_date; }
validation validateStatus on save { field overall_status; }
In the following listing 2, you see details about the implementation of the validate_customer method:
(1) First, the EML read operation READ ENTITY provides read access to the selected travel instance by using
the key.
To access data of the relevant entity fields, the FIELDS ( ) addition is used. As a result of the read operation
the entered (changed) value of the customer_id field for the selected travel instance are written into the table
lt_travel. Only this value is relevant for the validation.
(2) In the following lines of code, we prepare an optimization for the following database select. By using the
sorted table lt_customer, we ensure that only data records with non-initial customer IDs are considered for
database access.
(3) By accessing the contents of the database table /dmo/customer, we can check whether the entered
customer ID exists on the database at all.
(4) If the validation detects inconsistencies (customer ID is not valid or is initial), we must provide the key of all
inconsistent instances as failed key and return error messages to the consumer. For all failed instances, also
corresponding messages are created by calling the framework’s new_message method. For access to suitable
message texts, a message class /DMO/CM_FLIGHT_LEGAC from the /DMO/FLIGHT package is reused.
The validation to check if the agency ID is valid has exactly the same structure as
validate_customer. The code can be downloaded from ABAP Flight Reference Scenario
in /DMO/BP_TRAVEL_M.
In the following listing 3, you see details about the implementation of the validate_dates method:
(1) The EML read operation READ ENTITY provides read access to data from trigger fields begin_date and
end_date. As a result of the read operation the entered (changed) values of the begin_date and end_date
fields for the selected travel instance are written into the table row lt_travel.
(2), (3) The validation detects inconsistencies if the date value of end_date is before the date value of
begin_date or if the date value of begin_date is in the past. Each validation can produce failed keys and
messages. Any failed keys are stored in the table FAILED whereas the REPORTED table includes all instance-
specific messages.
**********************************************************************
*
* Check validity of dates
*
**********************************************************************
METHOD validate_dates.
" (1) Read relevant travel instance data
READ ENTITIES OF /dmo/i_travel_m IN LOCAL MODE
ENTITY travel
FIELDS ( begin_date end_date )
WITH CORRESPONDING #( keys )
RESULT DATA(lt_travel_result).
LOOP AT lt_travel_result INTO DATA(ls_travel_result).
" (2) Check if end_date is not before begin_date
IF ls_travel_result-end_date < ls_travel_result-begin_date.
APPEND VALUE #( %key = ls_travel_result-%key
travel_id = ls_travel_result-travel_id ) TO failed-
travel.
APPEND VALUE #( %key = ls_travel_result-%key
In the following listing 4, you see details about the implementation of the validate_travel_status method:
Checking the validity of overall_status values is performed within a case loop. The valid values O, X, and A
are specified directly in the source code.
UI Preview
In this case, we want to validate the status values of booking instances. If a user enters the wrong value K,
according to the figure below, the instance with its data is not saved and an error message is displayed instead.
Definition
A determination is an implicitly executed action that handles side effects of modified entity instances. It is
invoked by the business object’s framework as soon as a determination’s trigger condition at a predefined
point in time, the determination time, is fulfilled.
Determinations are triggered internally based on changes made to the entity instance of a business object. The
trigger conditions are checked by the business object framework at different points during the transaction
cycle, depending on the determination time and the operations on the relevant entity instances. For each
determination, it is necessary to specify both the determination time and the trigger condition. A trigger
condition consists of a list of fields belonging to the same entity the determination is assigned to or the
operations on which the determinations are called. We call these operations trigger operations.
As a result, a determination can modify entity instances and return transition messages (error, warning,
information, success).
As a typical example, a determination is used to calculate the invoice amount depending from a changed price
or quantity of an item. The determination would be assigned to the “Item” entity having defined the trigger
operation UPDATE, CREATE, and DELETE on entity "Item". As soon as the consumer creates a new item entity
instance or updates the quantity and price of an existing one, the determination will run and update that item
and re-calculate the invoice amount to the new value.
Note
When working with determinations and validations you have to consider the following runtime specifics:
● The determination result must not change if the determination is executed several times under the
same conditions (idempotence).
● The execution order of validations and determination is not determined. If there is more than one
determination/validation on one operation you cannot know which determination/validation is
executed first.
● It is not allowed to use EML modify statements in validations.
● If you create or update an instance and delete it with the same request, it can happen that an EML read
operation in a determination on modify fails as instances with the given key cannot be found.
Determination Time
Determinations can either be executed during the interaction phase, or more specifically during a modify call,
or they can be executed during the save sequence in the FINALIZE method.
A determination time defines at what time in the transaction cycle the trigger condition of that determination
should be evaluated. For example, the re-calculation of the invoice amount should take place every time after a
modification is performed (determination time: on modify).
Note
If you expose your business object for a UI service, you can define side effects to trigger validations or
determinations at other points in time, for example, after updating a field or a field group on the UI in a
draft-enabled application. In non-draft scenarios, side effects can trigger validations and determinations
during the save sequence. Side effects trigger a call to the backend to validate or determine values after
predefined activities on the UI. These side effects must be defined and annotated in the OData document.
Trigger Condition
Determinations can be triggered by operations or by field or by both.
Trigger Operations
Determinations can be triggered by the operations create, update and delete. Whenever the defined
operation is executed, the determination is called.
Determinations are specified for individual business object’s entities in the behavior definition by using the
following syntax:
[implementation] managed;
define behavior for CDSEntity [alias AliasedName]
implementation in class ABAP_ClASS [unique]
...
{
// (1) determination for triggers at field level
determination DeterminationName on (save | modify) { field f1[, f2, ..., fn] ; }
// (2) determination for triggers at entity level
determination DeterminationName on (save | modify) { create; update; delete }
...
}
The definition of a determination that is assigned to a CDSEntity is initiated with the determination
keyword, followed by DeterminationName and the determination time on modify or on save.
Determinations must have a trigger condition. Depending on whether the trigger condition is defined at the
field or the operation level, the following options are available:
You can define an operation trigger { create, | update, | delete, } that triggers the determination
when the operation is executed.
Note
The update trigger for determinations on save is only supported in combination with the create
trigger.
You can also define one or more field triggers { field f1[, f2, ..., fn]; } that trigger the
determination whenever the field is changed.
As depicted in the listing below, add the following determinations to relevant entities in the behavior definition.
managed;
define behavior for /DMO/I_Travel_M alias travel
implementation in class /DMO/BP_TRAVEL_M unique
...
{
...
// No determinations for travel entity
}
define behavior for /DMO/I_Booking_M alias booking
implementation in class /DMO/BP_BOOKING_M unique
...
{
...
// determination for calculation of total flight price
METHODS calculate_total_flight_price FOR DETERMINE ON MODIFY IMPORTING keys
FOR booking~calculateTotalFlightPrice.
}
define behavior for /DMO/I_BookSuppl_M alias booksuppl
implementation in class /DMO/BP_BOOKINGSUPPLEMENT_M unique
...
{
...
// determination for calculation of total suppl. price
determination calculateTotalSupplmPrice on modify { field price,
currency_code; }
}
The determination calculateTotalFlightPrice on the booking entity is intended to handle the calculation
of total price of all flight bookings that belong to the selected travel. The determination will be triggered by on
modify as determination time when creating new booking instances or updating the flight price value or when
changing the currency. In other words: both fields flight_price and currency_code serve as trigger fields
and form, together with create and update operations, the trigger condition for the determination.
For the booking supplement entity, the determination calculateTotalSupplmPrice is defined analogously.
This determination is used to calculate the total price of all supplements assigned to an individual flight
booking instance.
All calculated values are finally used to re-calculate the total travel price at the root entity level.
A determination that is assigned to a business object’s entity is implemented in the behavior pool, which is
referred in the behavior definition by the keyword implementation in class ABAP_ClASS_NAME
[unique].
The implementation of a determination is based on the ABAP language in a local handler class as part of the
behavior pool.
As depicted in the listing below, each such local class inherits from the base handler class
CL_ABAP_BEHAVIOR_HANDLER. The signature of the corresponding handler method lhc_handler is typed
based on the entity that is defined by the keyword FOR DETERMINATION followed by
AliasedEntityName~DeterminationName.
To identify the instance of an entity, it_key is used as input parameter of the handler method.
UI Preview
The figure below refers to the starting point of viewing with a newly created travel instance with the initial
amount (Total Price) and the travel currency 0.00 EUR.
If a user adds a flight booking to the travel, then also the travel’s Total Price is updated.
If the user switches the booking’s object page to edit mode and then changes the Flight Price, then the Total
Price is also updated at root level.
Definition
In the behavior definition, the determination on the booking entity is defined as follows:
managed;
define behavior for /DMO/I_Booking_M alias booking
...
{
...
********************************************************************************
*
* Calculates total booking price
*
********************************************************************************
CLASS lhc_travel DEFINITION INHERITING FROM cl_abap_behavior_handler.
PRIVATE SECTION.
...
METHODS calculate_total_flight_price FOR DETERMINATION
booking~calculateTotalFlightPrice
IMPORTING keys FOR booking.
ENDCLASS.
CLASS lhc_travel IMPLEMENTATION.
METHOD calculate_total_flight_price.
IF keys IS NOT INITIAL.
/dmo/cl_travel_auxiliary_m=>calculate_price(
it_travel_id = VALUE #( FOR GROUPS <booking> OF booking_key IN keys
GROUP BY booking_key-travel_id WITHOUT
MEMBERS
( <booking> ) ) ).
ENDIF.
ENDMETHOD.
...
ENDCLASS.
UI Preview
If the user adds a supplement to a given flight booking, then the travel amount is re-calculated.
In addition to the price, the currency was also defined as another trigger field for determination. In this way, we
want to ensure that currency conversion is also carried out when the total amount is re-calculated. In our case,
the supplement price with the current currency (USD) is converted into the travel currency (EUR).
This section explains how you can integrate Additional Save within the transactional life cycle of managed
business objects.
Use Case
In some application scenarios, an external functionality must be invoked during the save sequence, after the
managed runtime has written the changed data of business object’s instances to the database but before the
final commit work has been executed.
For example, reuse services like change documents and the application log must be triggered during the save
sequence and the changes of the current transaction must be written to change requests.
In real-life business applications, the data of business objects may change frequently. It is often helpful, and
sometime even necessary, to be able to trace or reconstruct changes for objects that are critical, for example
for investigation or auditing purposes. The ABAP Application Server records changes to business data objects
in change documents.
Application events can be centrally recorded in the application log. The entries of an application log contain
information about who gave rise to a given event at what time and with which program.
In order to integrate the additional save into the save sequence as a part of the managed runtime, you must
first add the corresponding syntax to the behavior definition and then implement the saver handler method as
a part of the behavior pool.
Note
If you would like to replace the managed runtime from saving the entity's data and reuse your own save
logic instead, you can integrate the unmanaged save instead. More on this: Integrating Unmanaged Save in
Managed Business Objects [page 264].
The following figure depicts the additional save within the transactional life cycle of a managed business object.
If the subsequent CHECK_BEFORE_SAVE call, including all onSave validations (validations with the trigger time
[page 963] on save), is positive for all transactional changes, the point-of-no-return is reached. From now on,
a successful save is guaranteed by all involved BOs.
If, on the other hand, the result of the checks is negative at the time of CHECK_BEFORE_SAVE, a save is denied
and the save sequence is interrupted. The consumer has now the option of modifying business object data and
then trigger the save sequence again.
After the point-of-no-return, the save call persists all BO instance data from the transactional buffer in the
database.
For each entity of an individual business object, the following options are available to execute the SAVE
processing step:
All change requests of the current LUW [page 956] are committed. The actual save execution is finished by
COMMIT WORK.
The final CLEANUP clears all transactional buffers of all business objects involved in the transaction.
In this topic, you will learn the syntax for defining the additional save for managed business objects.
In general, an additional save can be defined for each entity of a given business object with managed
implementation type. This is done in the behavior definition of the business object by adding the keyword with
additional save - after specifying the persistent table DB_TABLE.
The database table DB_TABLE is used in managed implementation type for storing entity’s business data
changes that result from the transactional life cycle.
[implementation] managed;
define behavior for Entity [alias AliasedName]
implementation in class ABAP_ClASS [unique]
persistent table DB_TABLE
with additional save
...
{
...
}
The following compact notation for defining the additional save is useful as an alternative if you want to define
an additional save for all entities of a business object and the saver implementation is carried out in a single
behavior pool ABAP_CLASS. In this case, the keyword with additional save is already specified in the
header of the business object’s behavior definition.
Example
In the following behavior definition, the additional save is defined for the travel (root) entity and the
booksuppl child entity, whereas for the booking child entity, the (default) managed save is defined.
managed;
define behavior for /DMO/I_Travel_M alias travel
implementation in class /DMO/BP_TRAVEL_M unique
persistent table /DMO/TRAVEL_M
with additional save
...
{
...
}
define behavior for /DMO/I_Booking_M alias booking
implementation in class /DMO/BP_BOOKING_M unique
persistent table /DMO/BOOKING_M
...
{
...
}
define behavior for /DMO/I_BookSuppl_M alias booksuppl
implementation in class /DMO/BP_BOOKINGSUPPLEMENT_M unique
The additional save of the relevant business object’s entity is implemented in the behavior pool (ABAP_ClASS)
that is specified in the behavior definition by the keyword implementation in class ABAP_ClASS
[unique].
The implementation takes place in a local saver class as a part of the behavior pool. As depicted in the listing
below, each such local class inherits from the base saver class CL_ABAP_BEHAVIOR_SAVER. This superclass
provides the predefined method save_modified that needs to be redefined in the local saver class
lhc_saver.
The following listing provides a template with the main steps for implementing additional save within the
save_modified method.
The essential elements of this method are the predefined, implicit parameters:
● CREATE-EntityName
● UPDATE-EntityName
● DELETE-EntityName
These parameters contain not only the table type of the entity to be modified, but also the %control structure
that can be used for identifying which elements are requested by the consumer.
Example
The following example shows you in detail how you can implement additional save based on our travel demo
scenario [page 912].
Since in the current release of SAP CP, ABAP Environment, the reuse services like change documents and the
application log are not yet available, we will demonstrate a simplified example for integrating the for additional
Log Table
changing_operation Standard operation for travel instances: create, update and delete
The following listing shows you the definition of the corresponding table /DMO/LOG_TRAVEL in the table editor.
After successful save, the relevant entries are recorded in the log table
Definition
In the behavior definition, the additional save for the root entity may be defined as follows:
managed;
define behavior for /DMO/I_Travel_M alias travel
implementation in class /DMO/BP_TRAVEL_M unique
persistent table /DMO/TRAVEL_M
with additional save
...
{
...
}
Implementation
The source code of our example implementation is divided into three sections:
Each of these sections is initiated with an IF statement, each of which checks whether
● the travel instances have been created (IF create-travel IS NOT INITIAL),
● their elements have been modified (IF update-travel IS NOT INITIAL), or
The relevant instance data is written to the internal table lt_travel_log and passed to the log table for
persistent storage on the database (INSERT /dmo/log_travel...).
When creating new travel instances and updating instance data, the %control structure is used to get
information on what fields have been provided or updated by the consumer. The %control structure contains
a flag if_abap_behv=>mk-on for each field, which indicates whether the field was provided (or changed) by
the consumer or not.
When creating instances (1), the new values for relevant fields of the travel entity are written into the internal
table lt_travel_log_c. In our demo code, we select the two fields booking_fee and overall_status as
an example. Their values are transferred as separate rows by means of APPEND into lt_travel_log_c and
finally written into the log table /dmo/log_travel with INSERT.
Similarly, in the update case (2), we also select two fields, namely customer_id and description, as
relevant fields for recording. So whenever the value of one of these fields is changed for an existing travel
instance by a consumer, a new table row (with the corresponding change ID) is appended to the internal table
lt_travel_log_c.
The last section (3) deals with the deletion of travel instances. However, in this case we are only interested in
the information of which instances have been deleted. Therefore, there is no information of fields available
when deleting instances.
This section explains how you can integrate unmanaged save within the transactional life cycle of managed
business objects.
Use Case
In certain use cases you might be requested to prevent business object’s managed runtime from saving
business data (changes). By default, the managed runtime saves all changed instances of business object’s
entity in the database table that is specified as persistent table DB_TABLE in the behavior definition
(managed save). However, you define for each entity of the business object or for the entire business object
whether the complete save is done by the managed runtime or by the unmanaged save instead. This
implementation flavor of a managed scenario may be relevant to you if you need to implement the interaction
phase for your application anyway, but the update task function modules are already available.
The following figure outlines the main components of business objects managed runtime that integrates
function modules for persistent save of business data changes. Within the interaction phase, a consumer calls
the business object operations to change business data and read instances with or without the transactional
changes. The business object runtime keeps the changes in its internal transactional buffer which represents
the state of instance data. After all changes on the related entity were performed, the instance data can be
persisted. This is realized during the save sequence. To prevent the managed runtime from saving the data, the
function modules (for the update task) are called to save data changes of the relevant business object’s entity
(unmanaged save). In order to persist the business data changes, the function modules access the
corresponding tables of the HANA database.
Note that the behavior handler can also directly access table data from the database during the interaction
phase: Authorization checks, for example, require direct access to the table data on the database.
The following figure depicts the unmanaged save within the transactional life cycle of a managed business
object.
If the subsequent CHECK_BEFORE_SAVE call, including all onSave validations (validations with the trigger time
[page 963] on save), is positive for all transactional changes, the point-of-no-return is reached. From now on,
a successful save is guaranteed by all involved BOs.
If, on the other hand, the result of the checks is negative at the time of CHECK_BEFORE_SAVE, a save is denied
and the save sequence is interrupted. The consumer has now the option of modifying business object data and
then trigger the save sequence again.
After the point-of-no-return, the save call persists all BO instance data from the transactional buffer in the
database.
For each entity of an individual business object, the following options are available to execute the SAVE
processing step:
All change requests of the current LUW [page 956] are committed. The actual save execution is finished by
COMMIT WORK.
The final CLEANUP clears all transactional buffers of all business objects involved in the transaction.
In order to integrate unmanaged save into the save sequence as a part of the managed runtime, you must first
add the corresponding syntax to the behavior definition and then implement the saver handler method as a
part of the behavior pool.
In this topic, you will learn the syntax for defining unmanaged save for managed business objects.
In general, unmanaged save can be defined for each entity of a given business object with managed
implementation type. This is done in the behavior definition of the business object by adding the keyword with
The actual implementation of the unmanaged save is based on the ABAP language and takes place in a local
saver class as a part of the behavior pool. More on this: Implementing the Save Handler Method [page 269]
[implementation] managed;
define behavior for Entity [alias AliasedName]
implementation in class ABAP_ClASS [unique]
with unmanaged save
...
{
...
}
The following compact notation for defining the unmanaged save is useful as an alternative if you want to
define an unmanaged save for all entities of a business object and the saver implementation is carried out in a
single behavior pool ABAP_CLASS. In this case, the keyword with unmanaged save is already specified in the
header of the business object’s behavior definition.
Example
In the following behavior definition, the unmanaged save is defined for the travel (root) entity, whereas for
the child entity booking the (default) managed save and for the child entity booksuppl an additional save is
defined.
managed;
define behavior for /DMO/I_Travel_M alias travel
implementation in class /DMO/BP_TRAVEL_M unique
with unmanaged save
...
{
...
}
define behavior for /DMO/I_Booking_M alias booking
implementation in class /DMO/BP_BOOKING_M unique
persistent table /DMO/BOOKING_M
...
{
The unmanaged save of the relevant business object’s entity is implemented in the behavior pool
(ABAP_ClASS) that is specified in the behavior definition by the keyword implementation in class
ABAP_ClASS [unique].
The implementation takes place in a local saver class as a part of the behavior pool. As depicted in the listing
below, each such local class inherits from the base saver class CL_ABAP_BEHAVIOR_SAVER. This superclass
provides the predefined method save_modified that needs to be redefined in the local saver class
lhc_saver.
Remember
Convention: The local saver class that implements the save_modified method is either a separate global
class or a part of the root implementation (behavior pool for the root entity).
The following listing provides a template with the main steps for implementing unmanaged save within the
save_modified method.
The essential elements of this method are the predefined, implicit parameters:
● CREATE-EntityName
● UPDATE-EntityName
● DELETE-EntityName
These parameters contain not only the table type of the entity to be modified, but also the %control [page
877] structure that can be used for identifying which elements have been changed during the current
transaction. .
The actual implementation steps of the save_modified method are shown in the template below:
Example
The following example shows you in detail how you can implement unmanaged save specifically based on our
travel demo scenario [page 912]. In particular, the previous implementation must be extended in such a way
Function Modules
In our example, the corresponding function modules for creating, changing, and deleting instances of the
booking supplement entity are already available in the corresponding function group of the demo
package /DMO/FLIGHT_MANAGED.
FUNCTION /dmo/flight_booksuppl_c
IMPORTING
VALUE(values) TYPE /dmo/tt_booksuppl_m.
INSERT /dmo/booksuppl_m FROM TABLE @values.
ENDFUNCTION.
Note
To use this source code, the table type /dmo/tt_booksuppl_m is also required for the values importing
parameter.
The following listing provides you with the function module’s source code for persistent storage of individual
elements of existing booking supplement instances.
FUNCTION /dmo/flight_booksuppl_u
IMPORTING
VALUE(values) TYPE /dmo/tt_booksuppl_m.
UPDATE /dmo/booksuppl_m FROM TABLE @values.
ENDFUNCTION.
The following listing provides you with the source code of the function module for deleting booking supplement
instances.
FUNCTION /dmo/flight_booksuppl_d
IMPORTING
VALUE(values) TYPE /dmo/tt_booksuppl_m.
DELETE /dmo/booksuppl_m FROM TABLE @values.
ENDFUNCTION.
Definition
In the behavior definition, the unmananged save for the child entity booksuppl may be defined as follows:
managed;
define behavior for /DMO/I_Travel_M alias travel
implementation in class /DMO/BP_TRAVEL_M unique
...
{
...
Implementation
As shown in the listing below, the source code of our example implementation is divided into three sections:
Each of these sections is initiated with an IF statement, each of which checks whether the booking
supplement instances have been created, their elements have been modified, or deleted by a consumer. The
relevant instance data is written to the internal table lt_booksuppl_db and passed to the respective
function module for persistent storage on the database.
In case of updating instance data (IF update-booksuppl IS NOT INITIAL), the %control structure is
used to get information on what fields have been updated by the consumer. The %control structure contains
a flag if_abap_behv=>mk-on for each field, which indicates whether the field was provided (changed) by the
consumer or not.
To eliminate the option that the unchanged fields are overwritten with default values, we must ensure that they
are kept according to the database data. This is the reason why we read the current data set from the database
using the statement SELECT * FROM /dmo/booksuppl_m FOR ALL ENTRIES IN
@lt_booksuppl_db...
For a more flexible service consumption, every transactional business object is projected onto a service
specific context. In other words, only those elements of the data model and those behavior characteristics and
operations that are needed in the relevant business service context are exposed for the service. By means of
projections, you can expose one BO in different business contexts by using different BO subsets. The general
business logic is defined in the BO whereas the BO projection adopts a subset of the business logic.
A layering with projections enables robust application programming. You can change or enhance the BO
without changing the exposed service as the scope of the service is defined in the projection layer. Enhancing
the business object with additional structure or behavior does not have any effect on the resulting service.
The business object that you developed with the help of the previous sections is ready to run. It uses the
managed runtime for CRUD (create, read, update, delete) operations. In addition, the relevant business logic
for managing travels with action, determinations and validations was implemented. The BO CDS entities
expose every element that might be relevant for any business service scenario. The behavior is defined and
implemented for any kind of business service.
This demo scenario uses two projections with a different service scope. The resulting apps represent two role-
based approaches for managing travels:
One business object projection uses the BO characteristics and the operations that are relevant for processing
travel data. This resulting UI service serves the role of a data processor. The responsible person can enter the
The other business object projection is the basis for a UI service that contains the functionality that is relevant
for an approver. Imagine a person, maybe a manager of a travel agency, that approves the data that was
entered by the processor. That means, this person sees the travel information and the corresponding booking
data. Based on this data, the approver can either accept or reject the travel. For a minimal scope of fields in the
travel entity the approver is enabled to edit the values, for example the BookingFee or the Description. The
information about bookings is set to read-only for the approver. The approver is not allowed to change the
fields of the booking entity.
See Business Scenario [page 189] for a detailed list of features for the respective roles.
The design time artifacts that you need for these projection scenarios are illustrated in the following figure. The
CDS views, as well as the behavior definition must be projected for both roles. To expose the BO projections for
a UI service, you need to create a service definition and binding for both BO projections. The behavior
implementation is not projected. Every behavior characteristic or operation that is used in the BO projection
must be implemented in the underlying BO implementation. You cannot define new behavior that needs
implementation in the BO projection.
Related Information
The data model for the BO projection is defined in CDS projection views. Projection views are data definition
artifacts with a syntax that differs slightly from CDS views. With projection views, you define the consumption-
specific data model.
The following sections provide a detailed description on how to project the existing BO to define a data model
for one business object that is tailored to expose a UI service for a data processor and one that is tailored for a
data approver.
Related Information
To define a data model for the BO projection that defines the scope for the processor application, the following
tasks need to be done:
● Creating the Projection CDS Views for the Processor [page 277]
● Defining the Data Model for the Processor Projection Views [page 278]
A data processor needs to be able to create, update, and delete entries for the travel entity, the booking entity,
and the booking supplement entity. That means, all three nodes of the composition structure must be
projected.
For the following CDS views, create the corresponding projection views by choosing the projection view
template in the creation wizard for data definitions.
Note
The names are assigned according to the naming conventions for projection views: Naming Conventions
for Development Objects [page 918].
The resulting CDS projection views must have the following syntax:
For more information about the syntax in projection views, see Syntax for CDS Projection Views [page 143]
The following topics provide you with a detailed description on how to define the data model for the CDS
projection views that are used in the BO projection for the processor.
For the service specific projection, the elements as well as all the UI specifics need to be defined.
The data model defines which elements are exposed for the UI service. In addition, in data definitions for
projection views you define all UI specifications.
The following UI is achieved by implementing the corresponding features in the CDS Travel projection view for
the processor.
/DMO/C_TRAVEL_PROCESSOR_M
Explanation
For the data model of the travel projection view in our scenario, you can adopt all elements of the projected
view, except for created_by, created_at and last_changed_by. Those element are not needed for our
service use cases. The element last_changed_at, however, is needed to store the eTag, but the other
administrative elements are not needed in the scenario. The other elements for travel information are used to
process travel data.
Remember
The eTag is needed for optimistic concurrency check. In the travel BO, all nodes use their own master eTag.
All elements of the projection can be given an alias with an automatic mapping done by the service framework.
The travel projection view uses a subset of the associations that are defined in the projected view. _Agency
and _Customer are needed for text provisioning. These associations can simply be adopted in the projection
view. On the other hand, the composition to the child entity booking must be redirected as the target entity
changes in the projection layer. The association _Currency is not necessary in the projection view. It is only
defined in the underlying BO data model to represent a complete data model structure.
Note
Before you can activate the travel projection root view for the processor, you need to create the booking
projection view with the redirection of the composition to parent in the booking projection child view.
Compositions must always be consistent from parent to child and vice-versa.
UI Specifics
The UI header information is given in an entity annotation to label the list report page.
The travel processor projection view is the root node of the BO projection. When opening the travel processing
app, the travel entries are displayed as list items with a navigation to their object page and the corresponding
bookings. From this object page, it is possible to navigate to the booking supplements. In the back end,
navigating is implemented by compositions. For the UI to enable navigation, UI facets need to be defined in the
travel projection view for the identification reference of the travel entity on the object page and for the line item
reference of the booking entity.
To indicate the number range for the TravelID element, the range is added to the label. In the same manner,
the possible values for the element TravelStatus are added.
For more information about UI navigation and positioning of elements, see Designing the User Interface for a
Fiori Elements App [page 34] or UI Annotations [page 746].
The annotations that are used in the projected entity are propagated to the projection view. You do not need to
reannotate elements with the same annotations as in the projected entity. However, if an annotation draws
reference to a specific element and the name of that specific element is changed with an alias in the projection
view, the propagated annotation keeps the reference that was given in the projected entity. A semantic
relationship between two elements can then be lost. In such a case, you have to reuse the same annotation and
use the alias name in the element reference of the annotation.
In our example scenario, this is the case for the semantic relationship between CurrencyCode and
TotalPrice or BookingFee. In the projection view, you do not need to annotate CurrencyCode with
@Semantics.currencyCode: true as this annotation is inherited from the projected entity. The annotation
@Semantics.amount.currencyCode: 'currency_code' is inherited as well, but the name of the field has
changed in the projection view. So you need to reannotate the element with the new alias name:
@Semantics.amount.currencyCode: 'CurrencyCode'.
Tip
Check the Active AnnotationsView to find out which annotations are active for the current CDS view and
what the values of the active annotations are. For more information, see .
To be able to search for a specific data set, the travel projection view must be search enabled and at least one
element must be assigned as default search element. In addition, you can define selection fields to provide a
filter bar for certain elements. For more information about search enabling, see Enabling Text and Fuzzy
Searches in SAP Fiori Apps [page 561]. For more information about selection fields, see List Report Header
[page 37]
To access the corresponding texts or descriptions for ID elements, the relationship between the elements
AgencyID and CustomerID and their text elements in the associated text provider views must be established.
The text elements of the text provider view must be integrated in the projection view. The text provider view
must be associated to the projection view and the text element in the text provider view must be annotated
with @Semantics.text: true. For more information about text povisioning, see Defining Text Elements
[page 544].
Especially for a data processing role, value helps are particularly important to find the necessary values for the
ID elements AgencyID, CustomerID and CurrencyCode. Value helps are defined with the annotation
@Consumption.valueHelpDefinititon. The value help provider view does not have to be associated to get
the value help as the entity and the element are referenced in the annotation. For more information, see
Providing Value Help [page 550].
In the projection view, you also have to define the position of the execution button of actions, that you have
defined in the behavior definition. On the list report page, the position of the button for the action
createTravelByTemplate is defined. For more information about the action, see Developing Actions [page
217].
For the service-specific projection, the elements as well as all the UI specifics must be defined.
The data model defines which elements are exposed for the UI service. In addition, in data definitions you have
to define all UI specifications.
The following UI is achieved by implementing the corresponding features in the CDS Booking projection view
for the processor.
Expand the following listing to view the source code of the booking projection view /DMO/
C_BOOKING_PROCESSOR_M that results in the previously shown UI:
/DMO/C_BOOKING_PROCESSOR_M
@Search.defaultSearchElement: true
key travel_id as TravelID,
/* Associations */
_Travel: redirected to parent /DMO/C_Travel_Processor_M,
_BookSupplement: redirected to composition child /DMO/
C_BookSuppl_Processor_M,
_Customer,
_Carrier
Explanation
For the data model of the booking projection view, you can adopt all elements of the projected view.
All elements of the projection can be given an alias with an automatic mapping done by the service framework.
The booking projection view uses a subset of the associations that are defined in the projected view. The
associations _Customer and _Carrier are needed for text provisioning. These associations can simply be
adopted in the projection view. On the other hand, the compositions to the parent entity _Travel and to the
child entity _BookSupplement must be redirected as the target entities change in the projection layer. The
association _Connection is not necessary in the projection view. It is defined in the underlying BO data model
to complete the BO data model structure.
Note
Before you can activate the booking projection view for the processor, you need to create the booking
supplement projection view with the redirection to the composition to parent from the booking supplement
projection child view. Compositions must always be consistent from parent to child and vice-versa.
UI Specifics
Like in the travel projection view, the UI header information for the booking projection view is given in an entity
annotation.
For the UI to enable navigation from the Booking to the BookingSupplement entity, you need to define UI
facets. The booking entity must be defined as identification reference and the BookingSupplement as line item
reference.
In addition, the elements for list items and identification reference for the second navigation need to be
annotated in the booking projection view with the respective UI annotations to define position, importance,
and possible labels.
For more information about UI navigation and positioning of elements, see Designing the User Interface for a
Fiori Elements App [page 34] or UI Annotations [page 746].
To access the corresponding texts or descriptions for ID elements, the relationship between the element
CarrierID and its text element in the associated text provider view must be established. Therefore, you need
the association to the text provider view. The text element of the text provider view must be integrated in the
projection view. The text provider view must be associated to the projection view and the text element in the
text provider view must be annotated with @Semantics.text: true. For more information about text
provisioning, see Defining Text Elements [page 544].
Especially for a data processing role, value helps are important to find the necessary values for the ID elements
CustomerID, CarrierID, ConnectionID and to find adequate values for FlightDate and CurrencyCode.
Value helps are defined with the annotation @Consumption.valueHelpDefinititon. The value help for
ConnectionID and FlightDate use additional bindings, so that only those values appear that match the
entries in the given local elements. The value help provider view does not have to be associated to get the value
help as the entity and the element are referenced in the annotation. However, it needs to be included in the
service definition. For more information, see Providing Value Help [page 550].
The administrative field last_changed_at is only used for concurrent processing and does not have to be
displayed on the UI. The annotations @UI.hidden = true is used for that purpose.
For the service-specific projection, the elements as well as all the UI specifics must be defined.
The data model defines which elements are exposed for the UI service. In addition, in data definitions you have
to define all UI specifications.
The following UI is achieved by implementing the corresponding features in the CDS Booking Supplement
projection view for the processor.
/DMO/C_BOOKSUPPL_PROCESSOR_M
@Search.defaultSearchElement: true
key booking_id as BookingID,
@UI: { lineItem: [ { position: 10, importance: #HIGH } ],
identification: [ { position: 10 } ] }
key booking_supplement_id as BookingSupplementID,
@UI: { lineItem: [ { position: 20, importance: #HIGH } ],
identification: [ { position: 20 } ] }
@Consumption.valueHelpDefinition: [ {entity: {name: '/DMO/I_SUPPLEMENT',
element: 'SupplementID' } ,
additionalBinding: [ { localElement:
'Price', element: 'Price' },
{ localElement:
'CurrencyCode', element: 'CurrencyCode' }] }]
@ObjectModel.text.element: ['SupplementDescription']
supplement_id as SupplementID,
_SupplementText.Description as SupplementDescription:
localized,
For the data model of the booking supplement projection view, you can adopt all elements of the projected
view.
All elements of the projection can be given an alias with an automatic mapping done by the service framework.
The booking supplement projection view uses a subset of the associations that are defined in the projected
view. The association _SupplementText is needed for text provisioning. This association can simply be
adopted in the projection view. On the other hand, the composition to the parent entity _Booking and to the
Note
Now, that all compositions are redirected, you can activate the three projection views for the processor.
UI Specifics
Like in the travel and the booking projection view, the UI header information for the booking supplement
projection view is given in an entity annotation.
Restriction
The Fiori Elements Preview does not support the navigation to more than one child entity. Hence, when
accessing the preview via entity set Travel and association to_Booking, it is not possible to navigate to
the object page of the BookingSupplement entity. That means, some of the UI annotations are not relevant
if you only use the preview to test your application. For example, the UI annotations referring to
identification cannot be shown in the preview, when testing via the root node Travel.
However, if you want to develop a real application or test your service with the Web IDE, you can configure
the application to enable navigation to any number of child entities. That is why, the UI annotation
concerning identification are included in the following description.
You can imitate the behavior of the Web IDE for the second-level navigation by accessing the Fiori Elements
Preview via entity set Booking and Association to_BookSupplement.
To show the entries of the booking supplement entity on its object page, the UI facet for
#IDENTIFICATION_REFERENCE must be defined.
In addition, the elements for list items (to appear on the object page of the booking entity) and identification
must be annotated in the Booking Supplement projection view with the respective UI annotation to define
position, importance, and possible labels.
For more information about UI navigation and positioning of elements, see Designing the User Interface for a
Fiori Elements App [page 34] or UI Annotations [page 746].
To be able to search for a specific data set, the booking supplement projection view must be search enabled
and at least one element must be assigned as default search element. For more information about search
enabling, see Enabling Text and Fuzzy Searches in SAP Fiori Apps [page 561].
To access the corresponding texts or descriptions for ID elements, the relationship between the element
SupplementID and its text element in the associated text provider view must be established. Therefore, you
need the association to the text provider view. The text element of the text provider view must be integrated in
the projection view. The text provider view must be associated to the projection view and the text element in
the text provider view must be annotated with @Semantics.text: true. For more information about text
provisioning, see Defining Text Elements [page 544].
Especially for a data processing role, value helps are important to find the necessary values for the ID element
SupplementID as well as for CurrencyCode. Value helps are defined with the annotation
@Consumption.valueHelpDefinititon. The value help for SupplementID uses additional binding, so that
The administrative field last_changed_at is only used for concurrent processing and does not have to be
displayed on the UI. The annotations @UI.hidden = true is used for that purpose.
To define a data model for the BO projection that defines the scope for the approver application, the following
tasks need to be done:
● Creating the Projection CDS Views for the Approver [page 288]
● Defining the Data Model for the Approver Projection Views [page 289]
The scope of the UI service for the approver is more limited than for the processor. The approver can only
modify the travel entity with accepting or rejecting the travel entries. The values in the corresponding booking
entries are the basis for this decision-making. Only these two entities are relevant for the approver app. That
means, only these two entities must be projected for the approver BO projection.
For the following CDS views, create the corresponding projection views by choosing the projection view
template in the creation wizard for data definitions.
Note
The names are assigned according to the naming conventions for projection views: Naming Conventions
for Development Objects [page 918].
The resulting CDS projection views must have the following syntax:
For more information about the syntax in projection views, see Syntax for CDS Projection Views [page 143]
The following topics provide you with a detailed description on how to define the data model for the CDS
projection views that are used in the BO projection for the approver.
For the service-specific projection, the elements as well as all the UI specifics for the approver BO projection
must be defined.
The data model defines which elements are exposed for the UI service. In addition, in data definitions you have
to define all UI specifications.
The following UI is achieved by implementing the corresponding features in the CDS Travel projection view for
the approver.
Expand the following listing to view the source code of the travel projection view /DMO/
C_TRAVEL_APPROVER_M that results in the previously shown UI:
/DMO/C_TRAVEL_APPROVER_M
Explanation
Except for the actions, which are different in the processor and the approver projection, the CDS projection
views for the processor and the approver BO are identical. Refer to explanation [page 280] for a thorough
description on the travel projection view.
Minor changes can be detected in the field label of the field TravelID and TravelStatus. This results from
the fact, that the approver does not create new travel entries. It is not necessary for this role to know the
number range of the TravelID or the possible values of the TravelStatus. In addition, the approver BO
projection has gained a selection field for the TravelStatus to make it easier for the approver to filter for
open/accepted/rejected travels.
The data model defines which elements are exposed for the UI service. In addition, in data definitions you have
to define all UI specifications.
Expand the following listing to view the source code of the travel projection view /DMO/
C_BOOKING_APPROVER_M that results in the previously shown UI:
/DMO/C_BOOKING_APPROVER_M
@Search.defaultSearchElement: true
key travel_id as TravelID,
@Search.defaultSearchElement: true
key booking_id as BookingID,
currency_code as CurrencyCode,
Explanation
The CDS projection views for the processor and the approver BO are almost identical. Refer to Booking
Projection View /DMO/C_BOOKING_PROCESSOR_M [page 282] for a thorough description on the booking
projection view.
Value helps are not necessary for the interpretation of the approver role in this scenario. As the booking entity
is a read-only entity in this scenario and selection fields with value helps cannot be defined for a sub entity,
value helps cannot be applied and thus are not necessary to be defined for the booking entity.
The behavior for the BO projection is defined in a behavior definition of type projection. The type is defined
in the behavior definition header. The projection behavior definition provides the behavior for the projection
CDS view. All characteristics and operations that you want to include in the BO projection must be listed
explicitly. The keyword for this is use.
projection;
For a detailed explanation of the syntax, see Projection Behavior Definition [page 145].
Related Information
The behavior for the BO projection that defines the scope for the processor application is defined in a behavior
definition with type projection.
The easiest way to create a projection behavior definition is to use the context menu in the project explorer by
selecting the relevant projection root view /DMO/C_TRAVEL_PROCESSOR_M and choosing New Behavior
Definition. The behavior definition always uses the same name as the corresponding root view.
For a more detailed description, see Creating Behavior Definitions [page 897].
When creating the behavior definition based on the projection view, the template automatically creates the
type projection and lists all available characteristics and operations of the underlying behavior definition.
That means, if nothing is done explicitly the BO projection has exactly the same behavior as the underlying BO.
For the processor projection, only the following elements are used:
projection;
define behavior for /DMO/C_Travel_Processor_M alias TravelProcessor
use etag
{
field ( readonly ) TotalPrice;
use create;
use update;
use delete;
use action createTravelByTemplate;
use association _BOOKING { create; }
}
define behavior for /DMO/C_Booking_Processor_M alias BookingProcessor
use etag
{
use update;
// use delete; // workaround for missing determination on delete
use association _BOOKSUPPLEMENT { create; }
}
define behavior for /DMO/C_BookSuppl_Processor_M alias BookSupplProcessor
use etag
{
use update;
// use delete; // workaround for missing determination on delete
}
Explanation
Only the characteristics and operations that are relevant for the processor are used in the projection behavior
definition. This is only a subset of the behavior that was defined in the underlying BO. See Developing Business
Logic [page 213] to compare the projection BO to the underlying one.
The ETag handling that was defined in the underlying BO is used for all three entities. Especially for the
processor role, which is enabled to modify, it is necessary to have a concurrency check. By using a master ETag
on all entities, concurrent processing is enabled for the travel BO.
The static field control that was defined for the underlying BO cannot be modified.
All standard operations are used for the processor on all the root entity. The child entities can only be created
via a create_by_association. The delete is not enabled for the view Booking and BookingSupplement
as the determination to calculate the total flight price is not triggered on delete.
For the travel entity, the action to create a travel entry by a template is enabled for the processor. This action
copies certain values from an existing travel entry to create a new travel entity.
The behavior for the BO projection that defines the scope for the approver application id defined in a behavior
definition with type projection.
The easiest way to create a projection behavior definition is to use the context menu in the project explorer by
selecting the relevant projection root view /DMO/C_TRAVEL_APPROVER_M and choosing New Behavior
Definition. The behavior definition always uses the same name as the corresponding root view.
For a more detailed description, see Creating Behavior Definitions [page 897].
As the behavior definition is created on the basis of the root projection view, the template with type
projection is generated.
When creating the behavior definition based on the projection, the template automatically creates the type
projection and lists all available characteristics and operations of the underlying behavior definition. That
means, if nothing is done explicitly the BO projection has exactly the same behavior as the underlying BO.
For the approver projection, only the following elements are used:
projection;
define behavior for /DMO/C_Travel_Approver_M alias Approver
use etag
{
field ( readonly ) BeginDate, EndDate, TotalPrice, CustomerID;
use update;
use action acceptTravel;
use action rejectTravel;
}
Explanation
Only the characteristics and operations that are relevant for the approver are used in the projection behavior
definition. This is only a subset of the behavior that was defined in the underlying BO. See Developing Business
Logic [page 213] to compare the projection BO to the underlying one.
The eTag that was defined in the underlying BO is used in the approver projection as well. The concurrency
check is relevant for the approver. It must be ensured that the data has not been changed between checking
the data and executing the action accept or reject travel.
The static field control that was defined for the underlying BO cannot be modified. However, new controls are
added to correspond the approver role. The fields that are mandatory for the processor are set to read-only for
The update operation is enabled for the approver as a modification on the travel entries must be available.
The actions accept and reject are enabled to change the status of the travel entry.
There is no behavior defined for the booking entity. All fields are read-only in this case.
The booking supplement entity is not part of the approver BO projection, so there is no behavior for this entity
either.
With the help of the previous sections, you developed a business object and its projection for two
complementary business roles. The next step is to build a business service for both projections in order to
consume the business object. The business service defines the scope of the service and binds it to a specific
OData protocol. For more information, see Business Service [page 138].
This scenario is designed to build a UI service for both business object projections. Follow the development
steps to build an application for both BO projections. For a detailed step-by-step description, see Creating an
OData Service [page 23].
1. Create a service definition for the processor service and one for the approver service.
2. Expose the relevant CDS views for each service.
Note
Only the projection CDS views of the business object projection must be exposed for the service. The
delegation to the underlying BO is automatically done.
The complete composition hierarchy is exposed for the data processing service. In addition, the text
and value help provider views are necessary components of the service scope to get the respective
feature for the service.
2. Service Definition for Approver Service
The approver service contains only two entities of the travel BO and a more limited number of text and
value help provider views.
3. Create a service binding with binding type ODATA V2 - UI for both service definitions and activate the
local service endpoints.
As soon as the service is activated, it is ready for consumption through an OData client such as an SAP
Fiori app. You can use the preview function in the service binding to check how the UI of the Fiori
application looks like.
This section explains the main development tasks required for enabling transactional processing in a business
objects provider that integrates existing business logic.
Based on an end-to-end example, you create and implement all requisite artifacts for providing OData services
that combine CDS data model and business object semantics with transactional processing from legacy
application logic.
Introduction
The scenario described below focuses on an unmanaged [page 963] implementation type of a business object
provider in the context of the ABAP RESTful Application Programming Model. For the unmanaged
implementation type, the application developer must implement essential components of the REST contract
itself. In this case, all required operations (create, update, delete, or any application-specific actions) must be
specified in the corresponding behavior definition [page 946] before they are manually implemented in ABAP.
Note
In a managed implementation type, on the other hand, a behavior definition would on its own be sufficient
to obtain a ready-to-run business object.
Architecture Overview
The underlying scenario reuses the existing business application logic and the existing persistence, which
manages business data.
If you are running ABAP developments on SAP Cloud Platform, then you can introduce legacy business logic
like this in the course of the custom code migration into ABAP Environment.
Prerequisites
Developing the scenario that is described in the subsequent chapters requires the following:
● You have access to and an account for SAP Cloud Platform, ABAP environment.
● You have installed ABAP Development Tools (ADT).
SAP recommends to use the latest version of the client installation. The ADT download is available on the
update site https://tools.hana.ondemand.com/.
● To recreate the demo scenario, the ABAP Flight Reference Scenario must be available in your ABAP system.
You can download the complete reference scenario from GitHub: Downloading the ABAP Flight Reference
Scenario [page 12].
Remember
The namespace /DMO/ is reserved for the demo content. Apart from the downloaded ABAP Flight Scenario,
do not use the namespace /DMO/ and do not create any development objects in the downloaded packages.
You can access the development objects in /DMO/ from your own namespace.
However, if you want to recreate all development objects of this demo content, make sure that you use
different names from this documentation.
The development of new business services by integrating the transactional behavior of an existing (legacy)
application mainly requires developers to perform the following fundamental activities:
The root entity is of particular importance: this is indicated in the source code of the CDS data definition by the
keyword ROOT. The root entity is a representation of the business object and defines the top node in a business
object's structure.
More on this: Providing CDS Data Model with Business Object Structure [page 305]
The implementation of the transactional behavior is done in specific class pools, which refer to the behavior
definition. The concrete implementation of the business object provider is based on the ABAP language (which
has been expanded from the standard with a special syntax) and the corresponding API for Implementing the
Unmanaged BO Contract. The implementation tasks are roughly divided into an interaction phase and a save
sequence.
The behavior that is relevant for the specific UI service is then projected in projection behavior definition.
More on this: Defining and Implementing Behavior of the Business Object [page 324]
Note
Via ABAPGit You can import the service including the related development objects into your development
environment for comparison and reuse. You find the service in the package /DMO/FLIGHT_UNMANAGED.
The suffix for development objects in this development guide is _U.
For information about downloading the ABAP Flight Reference Scenario, see Downloading the ABAP Flight
Reference Scenario [page 12].
The Managing of Flight Travels scenario used in this guide provides an example of an existing stateful
business application whose business logic is reused in the new implementation for transactional apps. This
The application demo provided (which represents a legacy stateful application) allows a user to create and
manipulate flight bookings. It involves different data sources and entities such as travel, travel agencies,
customers (passengers), flights, and bookings. Some of these are editable (that is, they can be created or
manipulated) and some are not.
The following table gives an overview of the different travel entities involved in the current scenario, including a
categorization into editable and non-editable entities.
Note
All development objects referenced here are available in the package /DMO/FLIGHT_LEGACY. More on
this: ABAP Flight Reference Scenario [page 912]
Travel A Travel entity defines general travel data, such as the agency ID or customer Yes
ID, status of the travel booking, and the price of travel. The travel data is stored in
the database table /DMO/TRAVEL.
Agency An Agency entity defines travel agency data, such as the address and contact No
data. The corresponding data is stored in the database table /DMO/AGENCY. The
flight data model defines a 1:n cardinality between Agency and Travel.
Booking The booking data is stored in the database table /DMO/BOOKING. The flight Yes
data model defines a 1:n cardinality between a Travel and the Booking en
tity.
Flight The specific flight data for each connection is stored in the database table /DMO/ No
FLIGHT. The flight data model defines a 1:n cardinality between a
Connection and the Flight entity.
Connection The flight connections are stored in the database table /DMO/CONNECTION. No
Carrier The IDs and names of airlines are stored in the database table /DMO/CARRIER. No
Each airline has a number of flight connections. Therefore, the data model de
fines a 1:n cardinality between a Carrier and the Connection entity.
Booking This entity is used to add additional products to a travel booking. The booking Yes
Supplement supplement data is stored in the database table /DMO/BOOK_SUPPL. The flight
data model defines an n:1 cardinality between a Booking Supplement en
tity and a Booking entity.
Supplement A Supplement entity defines product data that the customer can book to No
gether with a flight, for example a drink or a meal.
The figure below shows the relationships between the travel, agency, customer, and booking entities, where the
travel entity represents the root of the data model. Additional entities for currencies (I_Currency) and
countries (I_Country) are generally available in your system and are included in our data model using
associations.
Note
For didactic reasons, we have kept the data model as simple as possible. We have hence reduced the
number of entities in our end-to-end guide (compared with the predefined ABAP flight model) to a
minimum set of entities.
Business Logic
The following figure summarizes the essential elements of the business logic:
● The function group /DMO/FLIGHT_TRAVEL_API is used to group all function modules that represent the
application’s legacy code.
● The class /DMO/CL_FLIGHT_LEGACY provides the actual implementation of the business logic in a more
convenient (object-oriented) way.
● The interface /DMO/IF_FLIGHT_LEGACY defines global types and constants for reuse.
● Exception class /DMO/CX_FLIGHT_LEGACY.
From a structural point of view, a business object consists of a tree of entities that are linked by special
associations known as compositions. A composition is a specialized association that defines a whole-part
relationship. A composite part only exists together with its parent entity (whole).
Note
For didactic reasons, we will demonstrate a one-level composition in our sample application, defining a root
entity for the Travel BO and one child entity for bookings. This relationship also means that booking data
can only be created to a given travel instance.
Every entity in this composition tree is an element that is modeled with a CDS entity. The root entity is of
particular importance, since it defines the top node in a business object's structure and serves as a
representation of the business object. This is considered in the source code of the CDS data definition with the
keyword ROOT when defining the corresponding CDS entity.
@AbapCatalog.sqlViewName: 'CDS_DB_VIEW'
@[view_annotation_1]
...
@[view_annotation_n>]
DEFINE ROOT VIEW root_entity
[parameter_list]
AS SELECT FROM data_source [AS alias]
COMPOSITION [min..max] OF child_entity AS _comp_name
[additional_composition_list]
[association_list]
{
element_list
}
Effect:
Using this syntax, you define the root_entity as a root of the compositional hierarchy for the business object
to be created.
With the keyword COMPOSITION, a child_entity is defined as a direct child entity to the business object’s
root. The child_entity is a CDS entity, which is the target of the composition. _comp_name defines the
name of the composition and must be added to the element_list (like associations). The cardinality to the
child entity is expressed in the composition definition with square brackets [min .. max].
For min and max, positive integers (including 0) and asterisks (*) can be specified:
● max cannot be 0.
● An asterisk * for max means any number of rows.
● min cannot be *.
The meaning of the other elements of the syntax is identical to that of DEFINE VIEW.
@AbapCatalog.sqlViewName: 'CDS_DB_VIEW'
[@view_annotation_1]
...
[@view_annotation_n>]
DEFINE VIEW child_entity
[parameter_list]
AS SELECT FROM data_source [AS alias]
ASSOCIATION TO PARENT parent_entity AS _assoc_name ON condition_exp
[additional_association_list]
{
element_list
}
Using this syntax, you define a CDS entity child_entity that serves as a sub node in the compositional
hierarchy of the business object structure. The sub node is a node in a business object's structure that is
directly connected to another node when moving away from the root.
CDS entities that do not represent the root node of the hierarchy must have an association to their
compositional parent entity parent_entity or root_entity. This relationship is expressed by the keyword
ASSOCIATION TO PARENT...
The meaning of the other elements in the association syntax is identical to that of ASSOCIATION in the CDS
SELECT statement.
Next Steps
In this step you create a CDS views as the basis for the data model of our demo scenario. To do this, you create
the appropriate data definitions as transportable ABAP repository objects, as specified in the table below.
Note
Naming CDS views: Since CDS views are (public) interface views, they are prefixed with I_ in accordance
with the VDM (virtual data model) naming convention. In addition, we add the suffix _U to the view name in
case it is specifiic for our unmanaged implementation type scenario. For detailed information, see: Naming
Conventions for Development Objects [page 918]
Remember
The namespace /DMO/ is reserved for the demo content. Therefore, do not use the namespace /DMO/
when creating your own development objects and do not create any development objects in the
downloaded packages.
Data Definition
/DMO/I_TRAVEL_U /DMO/ This CDS view defines the root entity. The root entity is a representa
TRAVEL tion of the travel business object and defines the top node in a busi
/DMO/I_Travel_U
ness object's structure.
(DB table)
/DMO/ITRAVEL_U
It is used for managing general travel data, such as the booking sta
tus of a travel or the total price of a travel.
/DMO/I_AGENCY /DMO/ This CDS view represents the travel agency in the data model of our
AGENCY demo scenario.
/DMO/I_Agency
(DB table)
/DMO/IAGENCY_RE
/DMO/I_CUSTOMER /DMO/ This CDS view defines the data model for managing flight travel cus
CUSTOMER tomers (passengers).
/DMO/I_Customer
(DB table)
/DMO/ICUSTOM_RE
Data Definition
/DMO/I_BOOKING_U /DMO/ This CDS view defines the flight booking entity. The booking entity is
BOOKING a sub node representation of the travel business object structure. .
/DMO/I_Booking_U
(DB table) It is used for managing flight booking data, such as the customer, the
/DMO/IBOOKING_U
flight connection, or the price and flight date.
/DMO/I_FLIGHT /DMO/ This CDS view represents the concrete flights in the travel data
FLIGHT model. In our demo scenario, the CDS view is used for value help defi-
/DMO/I_Flight
nition for specific elements in the booking view.
(DB table)
/DMO/IFLIGHT_RE
/DMO/I_CONNECTION /DMO/ This CDS view defines the data model for managing flight connec
CONNECTION tions.
/DMO/I_Connection
(DB table) In our demo scenario, the connection view is used to retrieve the text
/DMO/ICONNECT_RE
information for the associated elements in the booking view.
/DMO/I_CARRIER /DMO/ This CDS view defines the data model for managing the airline data
CARRIER (ID and the name).
/DMO/I_Carrier
(DB table) In our demo scenario, the carrier view is used to retrieve the text in
/DMO/ICARRIER_RE
formation for the associated elements in the booking view.
To launch the wizard tool for creating a data definition, do the following:
Results
This procedure creates a data definition as a transportable development object in the selected package. For
each data definition, the related CDS view and the corresponding SQL view are created too.
The listing 1 (below) provides you with the implementation of the CDS data model for managing flights, where
the database table /dmo/travel serves as the data source for the corresponding CDS view /DMO/
I_Travel_U (note the camel case notation).
This CDS view defines the root entity of the data model and represents the root of the compositional hierarchy
for the travel business object to be created.
From a structural point of view, a business object consists of a tree of nodes that are linked by special
associations known as compositions. To define a composition relationship from the root to a child entity the
To be able to access business data from semantically related entities, a set of associations is defined in the
CDS source code. These associations refer to CDS views that are part of our demo application scenario. Some
of these views are used primarily as text views for retrieving text information and as value help provider views
for specific UI fields, see Projecting the Data Model in CDS Projection Views [page 315].
Except for the administrative fields createdby, lastchangedby, and createdat, all fields of the data source
table /dmo/travel have been added to the element list in the CDS view. The database table provides several
administrative fields that are used for administrative data which usually includes the user who created or last
changed an instance and the corresponding timestamps. In this example however, the element
LastChangedAt plays a special part, as it is used for ETag checks to determine whether two representations
of an entity are the same. If the representation of the entity ever changes, a new and different ETag value is
assigned. ETags play a significant part in the lock lifetime when working with business objects.
For all elements, we can provide an alias to provide a readable name for the elements.
The price elements and the element CurrencyCode have a semantic relationship. This relationship is
manifested in the business object view via the @semantics annotations. In a Fiori UI, the amount and the
currency value can then be displayed together.
@AbapCatalog.sqlViewName: '/DMO/ITRAVEL_U'
@AbapCatalog.compiler.compareFilter: true
@AbapCatalog.preserveKey: true
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Travel view - CDS data model'
define root view /DMO/I_Travel_U
as select from /dmo/travel as Travel -- the travel table is the data source
for this view
composition [0..*] of /DMO/I_Booking_U as _Booking
association [0..1] to /DMO/I_Agency as _Agency on $projection.AgencyID =
_Agency.AgencyID
association [0..1] to /DMO/I_Customer as _Customer on $projection.CustomerID
= _Customer.CustomerID
association [0..1] to I_Currency as _Currency on
$projection.CurrencyCode = _Currency.Currency
{
key Travel.travel_id as TravelID,
Travel.agency_id as AgencyID,
Travel.customer_id as CustomerID,
Travel.begin_date as BeginDate,
Travel.end_date as EndDate,
@Semantics.amount.currencyCode: 'CurrencyCode'
Travel.booking_fee as BookingFee,
@Semantics.amount.currencyCode: 'CurrencyCode'
Travel.total_price as TotalPrice,
@Semantics.currencyCode: true
Travel.currency_code as CurrencyCode,
Travel.description as Memo,
Travel.status as Status,
Travel.lastchangedat as LastChangedAt,
/* Associations */
_Booking,
Listing 2 (below) provides you with a data model implementation of the booking entity. In the data definition of
the root entity /DMO/I_Travel_U, you specified the booking entity /DMO/I_Booking_U as a child entity. This
composition relationship requires an association to their compositional parent entity for the booking child
entity to be specified in the data model implementation. This relationship is expressed by the keyword
ASSOCIATION TO PARENT. Using this syntax, you define the CDS entity /DMO/I_Booking_U as a direct sub
node in the compositional hierarchy of the travel business object structure.
The SELECT list includes all elements of a booking entity that are relevant for building the BO structure. These
elements are provided with an alias. Like in the Travel CDS view, the semantic relationship between Price
and CurrencyCode is established with the @Semantics annotations.
To be able to access data from other entities, a set of additional associations (_Customer, _Carrier, and
_Connection) is defined in the CDS source code. These views are primarily used as text views for retrieving
text information and as value help provider views for specific booking fields on the UI, Projecting the Data
Model in CDS Projection Views [page 315].
@AbapCatalog.sqlViewName: '/DMO/IBOOKING_U'
@AbapCatalog.compiler.compareFilter: true
@AbapCatalog.preserveKey: true
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Booking view'
define view /DMO/I_Booking_U
as select from /dmo/booking as Booking
association to parent /DMO/I_Travel_U as _Travel on
$projection.TravelID = _Travel.TravelID
association [1..1] to /DMO/I_Customer as _Customer on
$projection.CustomerID = _Customer.CustomerID
association [1..1] to /DMO/I_Carrier as _Carrier on
$projection.AirlineID = _Carrier.AirlineID
association [1..1] to /DMO/I_Connection as _Connection on
$projection.AirlineID = _Connection.AirlineID
and
$projection.ConnectionID = _Connection.ConnectionID
{
key Booking.travel_id as TravelID,
key Booking.booking_id as BookingID,
Booking.booking_date as BookingDate,
Booking.customer_id as CustomerID,
Booking.carrier_id as AirlineID,
Booking.connection_id as ConnectionID,
Booking.flight_date as FlightDate,
@Semantics.amount.currencyCode: 'CurrencyCode'
Booking.flight_price as FlightPrice,
@Semantics.currencyCode: true
Booking.currency_code as CurrencyCode,
/* Associations */
_Travel,
Listing 3 (below) provides you with a data definition for handling travel agency data. The database table /dmo/
agency is the data source for the corresponding CDS view /DMO/I_Agency.
All fields in table /dmo/agency have been added to the element list in the CDS view.
Since the travel agency's data can vary from one country to another, the data model refers to the I_Country
view using the association _Country.
This CDS entity also serves as a text provider view. For this purpose, the annotation @Semantics.text:
true is used to identify the Name element as a text element, which - in this case - points to a textual description
of agency names. In the travel and the booking projection views, the associated text element is added as a field
to the referencing entity. At runtime, this field is read from the database and filtered by the logon language of
the OData consumer automatically.
In addition, the data model enables search capabilities on the Name element: The annotation
@Search.searchable: true marks the CDS view as searchable, whereas
@Search.defaultSearchElement: true specifies that the annotated Name element is to be considered in
a full-text search. For detailed information, see: Enabling Text and Fuzzy Searches in SAP Fiori Apps [page 561]
@AbapCatalog.sqlViewName: '/DMO/IAGENCY_RE'
@AbapCatalog.compiler.compareFilter: true
@AbapCatalog.preserveKey: true
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Agency view - CDS data model'
@Search.searchable: true
define view /DMO/I_Agency
as select from /dmo/agency as Agency -- the agency table serves as the data
source for this view
association [0..1] to I_Country as _Country on $projection.CountryCode =
_Country.Country
{
key Agency.agency_id as AgencyID,
@Semantics.text: true
@Search.defaultSearchElement: true
@Search.fuzzinessThreshold: 0.8
Agency.name as Name,
Agency.street as Street,
Agency.postal_code as PostalCode,
Agency.city as City,
Agency.country_code as CountryCode,
Agency.phone_number as PhoneNumber,
Agency.email_address as EMailAddress,
Agency.web_address as WebAddress,
/* Associations */
_Country
}
Listing 4 (below) is used as a data model implementation for managing passenger data. The database
table /dmo/customer serves as the data source for the corresponding CDS view /DMO/I_Customer.
Except for the administrative fields (createdby, createdat, lastchangedat and lastchangedby), all
fields of the table /dmo/customer have been added to the element list in the CDS view.
Since a passenger’s data can vary from one country to another, the data model refers to the I_Country view
using a corresponding association _Country.
The annotation @Semantics.text: true is added to the LastName element. This element serves as a text
element, which - in this case - points to texts with customer names. This text annotation allows you to use this
customer view as a text provider for the associated elements in the target views /DMO/I_Travel_U
and /DMO/I_Booking_U.
@AbapCatalog.sqlViewName: '/DMO/ICUSTOM_RE'
@AbapCatalog.compiler.compareFilter: true
@AbapCatalog.preserveKey: true
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Customer view - CDS data model'
@Search.searchable: true
define view /DMO/I_Customer
as select from /dmo/customer as Customer -- the customer table serves as the
data source
association [0..1] to I_Country as _Country on $projection.CountryCode =
_Country.Country
{
key Customer.customer_id as CustomerID,
Customer.first_name as FirstName,
@Semantics.text: true
@Search.defaultSearchElement: true
@Search.fuzzinessThreshold: 0.8
Customer.last_name as LastName,
Customer.title as Title,
Customer.street as Street,
Customer.postal_code as PostalCode,
Customer.city as City,
Customer.country_code as CountryCode,
Customer.phone_number as PhoneNumber,
Customer.email_address as EMailAddress,
/* Associations */
_Country
}
Listing 5 (below) provides you with a data definition for flights. The data of specific flights is stored in the
database table /dmo/flight, which serves as the data source for the corresponding CDS view /DMO/
I_Flight.
As demonstrated in listing 2 (above), you can implement the value help with additional binding. To define filter
conditions for the value help based on the same value help provider, the flight view /DMO/I_Flight is used to
filter the value help result list for the annotated elements.
@AbapCatalog.sqlViewName: '/DMO/IFLIGHT_RE'
@AbapCatalog.compiler.compareFilter: true
@AbapCatalog.preserveKey: true
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Flight view'
define view /DMO/I_Flight as select from /dmo/flight as Flight
{
Listing 6 (below) is used as a data definition for flight connections. The flight connections are stored in the
database table /dmo/connection, which serves as the data source for the corresponding CDS view /DMO/
I_Connection.
Except for the administrative fields, all fields in the table /dmo/connection have been added to the element
list in the CDS view.
@AbapCatalog.sqlViewName: '/DMO/ICONNECT_RE'
@AbapCatalog.compiler.compareFilter: true
@AbapCatalog.preserveKey: true
@AccessControl.authorizationCheck: #NOT_REQUIRED
The following data definition for the carrier CDS entity provides you with IDs and names of airlines that are
stored in the database table /DMO/CARRIER.
This CDS entity mainly serves as a text provider view. It provides text data through text associations as defined
in the travel and the booking views (Listing 1 and Listing 2).
For this purpose, a text annotation is required at element level in order to annotate the text elements from the
view’s element list: In this example, the Name element is identified as the text element.
@AbapCatalog.sqlViewName: '/DMO/ICARRIER_RE'
@AbapCatalog.compiler.compareFilter: true
@AbapCatalog.preserveKey: true
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Carrier view'
define view /DMO/I_Carrier as select from /dmo/carrier as Airline
{
key Airline.carrier_id as AirlineID,
@Semantics.text: true
Airline.name as Name,
@Semantics.currencyCode: true
Airline.currency_code as CurrencyCode
}
You use CDS projection views to expose the general data model for a specific business service.
Whereas the general business object defines all available entities and elements, the Business Object Projection
[page 140] defines those entities and elements that are used for a specific service, in our case a service that is
● value helps
● text elements
● search
● UI layout annotations
These UI enrichments are modeled via CDS annotations [page 949] in the CDS projection view [page 950].It is
best practice, to outsource the annotations related to UI layout in metadata extensions [page 949], as these
annotations can easily overload the projection view. A CDS metadata extension is always assigned to a layer
such as industry, partner or customer to easily extend the scope of the metadata extension.
For more information, see Business Object Projection [page 140] and .
For the Travel UI service, create projection views for those CDS entities that are part of the compositional
hierarchy of the business object. These are:
Note
The names are assigned according to the naming conventions for projection views: Naming Conventions
for Development Objects [page 918].
For more information, see Creating Projection Views [page 911] (tool reference).
The resulting CDS projection views must have the following syntax:
For more information about the syntax in projection views, see Syntax for CDS Projection Views [page 276].
The unmanaged scenario provides just one projection use case that reuses all elements that are defined in the
business object views. In the projection layer the data model is enriched with UI functionality that is
implemented via CDS annotations or keywords in the projection view and manifested in the service metadata.
The listing 1 (below) provides you with the implementation of the CDS view projection, where the CDS
view /DMO/I_Travel_U serves as the projection source for the corresponding CDS projection view /DMO/
C_Travel_U. It projects every element from the underlying view.
The Travel projection view also exposes all associations that are defined in the underlying travel view /DMO/
I_Travel_U. However, the composition to the child /DMO/I_Booking_U must be redirected to the newly
created projection counterpart. The syntax for defining redirected compositions is described in CDS Projection
View [page 143].
To define a relationship between the elements AgencyID and CustomerID and their corresponding texts or
descriptions, the text elements must be denormalized in the CDS projection view. Therefore the elements of
the associated text provider views (_Agency.Name and _Customer.LastName) are included in the select
list of the projection view. Their corresponding ID elements are annotated with
@ObjectModel.text.element. At runtime, the referenced element is read from the database and filtered by
the logon language of the OData consumer automatically. For detailed information, see: Defining Text Elements
[page 544]
Value helps are defined in the source code of the CDS projection view /DMO/C_Travel_U by adding the
annotation @Consumption.valueHelpDefinition to the elements AgencyID, CustomerID and
CurrencyCode. In this annotation, you specify the elements for which the value help dialog should appear on
the UI. The value help annotation allows you to reference the value help provider view without implementing an
association. You simply assign a CDS entity as the value help provider and specify an element for the mapping
in the annotation. All fields of the value help provider are displayed on the UI. When the end user chooses one
of the entries of the value help provider, the value of the referenced element is transferred to the corresponding
input field on the UI. For detailed information, see: Simple Value Help [page 552]
Note
For the default implementation of value help, note that you can reuse any CDS entity that contains the
required values of the element that corresponds to the input field on the UI. You do not need to explicitly
define a CDS entity as the value help provider
In the projection view, you model the ability to search for specific values in the view. The entity annotation
@Search.searchable is used in the travel projection to enable the general HANA search. This annotation
also triggers the search bar in the Fiori elements UI. By using this annotation, you have to define elements that
are primarily used as the search target for a free text search. These elements are annotated with
@Search.defaultSearchElement. In addition, you can define a fuzziness threshold that defines the how
exact the search values must be to be able to find element values. For more information on search, see
Enabling Text and Fuzzy Searches in SAP Fiori Apps [page 561].
The listing below does not include annotations that define the UI layout. They are outsourced to metadata
extensions, see Adding UI Metadata to the Data Model [page 320].
The listing 2 (below) provides you with the implementation of the CDS view projection, where the CDS
view /DMO/I_Booking_U serves as the projection source for the corresponding CDS projection view /DMO/
C_Booking_U. It projects every element from the underlying view.
The Booking projection view also exposes all associations that are defined in the underlying booking
view /DMO/I_Travel_U. However, the composition to the parent /DMO/I_Travel_U must be redirected to
the newly created projection counterparts. The syntax for defining redirected compositions is described in CDS
Projection View [page 143].
To access the corresponding texts or descriptions, the relationship between the elements AirlineID and
ConnectionID and the text elements in the associated text provider views /DMO/I_Carrier and /DMO/
I_Connection are used in this example. Their corresponding ID elements are annotated with
@ObjectModel.text.element. At runtime, the referenced element is read from the database and filtered by
the logon language of the OData consumer automatically. For detailed information, see: Defining Text Elements
[page 544]
Value helps are defined in the source code of the booking entity /DMO/C_Booking_U by adding the annotation
@Consumption.valueHelpDefinition to the relevant elements. You simply assign a CDS entity as the
value help provider to the elements CustomerID, AirlineID, and CurrencyCode, and specify an element for
the mapping in the annotation. This simple value help approach is convenient if you only want to display values
from the value help provider view for an input field. In this case, the annotation defines the binding to the value
Listing 2 also demonstrates how you can implement the value help with additional binding, which defines a
filter condition. Different filter conditions for the value help on the same value help provider entity /DMO/
I_Flight are defined for filtering the value help result list for the elements ConnectionID and FlightDate.
For detailed information, see: Value Help with Additional Binding [page 559]
The elements in the booking projection view are also searchable in a UI service. The annotations related to
search are therefore also maintained in the booking view on entity level and on certain elements to mark them
as primary search element. For more information on search, see Enabling Text and Fuzzy Searches in SAP Fiori
Apps [page 561].
The listing below does not include annotations that define the UI layout. They are outsourced to metadata
extensions, see Adding UI Metadata to the Data Model [page 320].
@Search.defaultSearchElement: true
key BookingID,
BookingDate,
@Consumption.valueHelpDefinition: [ { entity: { name: '/DMO/I_Customer',
element: 'CustomerID' } } ]
@Search.defaultSearchElement: true
@ObjectModel.text.element: ['CustomerName']
CustomerID,
_Customer.LastName as CustomerName,
@Consumption.valueHelpDefinition: [ { entity: { name: '/DMO/I_Carrier',
element: 'AirlineID' } } ]
@ObjectModel.text.element: ['AirlineName']
AirlineID,
_Carrier.Name as AirlineName,
@Consumption.valueHelpDefinition: [ { entity: { name: '/DMO/I_Flight',
element: 'ConnectionID' },
additionalBinding: [ { localElement:
'FlightDate', element: 'FlightDate' },
{ localElement:
'AirlineID', element: 'AirlineID' },
{ localElement:
'FlightPrice', element: 'Price' },
{ localElement:
'CurrencyCode', element: 'CurrencyCode' } ] } ]
ConnectionID,
FlightPrice,
The other CDS views that re relevant for text provisioning and value helps to complete the data model structure
do not have to be projected as they are not part of the business object.
Related Information
To enable the business service to be consumable by any UI client, UI relevant metadata are added to the
backend service. These metadata are maintained with @UI annotations that are either added to the whole
entity or, if it relates to a specific UI element, to the corresponding CDS element. Since these annotations can
become excessive, it is recommended to maintain the UI annotations in metadata extensions.
Metadata extensions are used to define CDS annotations for a CDS view outside of the corresponding data
definition. The use of metadata extensions allows the separation of concerns by separating the data model
from domain-specific semantics, such as UI-related information for UI consumption. A CDS metadata
extension is always assigned to a specific layer such as core, industry, partner or customer. These industries
can extend the metadata for the data model.
For the Travel UI service, create metadata extensions for the CDS projection views that are part of the
compositional hierarchy of the business object. These are:
Note
The names of metadata extensions are the same as their related CDS entities, according to the naming
conventions for metadata extensions: Naming Conventions for Development Objects [page 918].
To create a metadata extension object, open the context menu in the project explorer of the related CDS entity
and choose New Metadata Extension. Follow the steps in the wizard to get a metadata extension template that
annotates the related projection view.
Syntax:
@Metadata.layer: layer
annotate view CDSProjectionView
with
{
element_name;
Define a metadata layer for your metadata extension and insert the elements that you want to annotate. The
layering of metadata extension allows a stacking for each development layer, beginning with #CORE. Each layer
can then add its own layer specific metadata to the service.
To enable the usage of metadata extensions for the projection views, add the annotation
@Metadata.allowExtensions:true on entity level in your projection view. For more information, see .
Any CDS element that you want to annotate in metadata extensions must be inserted in the element list.
The listing below (listing 1) provides you with the implementation of the metadata extension /DMO/
C_Travel_U for the projection view with the same name. It annotates the projection view itself and its
elements.
To specify the header texts for Fiori UIs, the annotation @UI.headerInfo is used at entity level. It specifies the
list title as well as the title for the object page. The main building blocks of the UI are specified as UI facets. The
annotation @UI.facet also enables the navigation to the child entity, which is represented as list in the page
body of the object page.
For actions, you can define an action button on the Fiori Elements UI. This scenario contains the action to set
the travel status to booked. To expose the action on the list report page, the information for the action button is
added to the @UI.lineItem annotation on an element.
@Metadata.layer: #CORE
@UI: { headerInfo: { typeName: 'Travel',
typeNamePlural: 'Travels',
title: { type: #STANDARD,
value: 'TravelID' } } }
The listing below (listing 2) provides you with the implementation of the metadata extension /DMO/
C_Booking_U for the projection view with the same name. It annotates the projection view itself and its
elements.
To specify the header texts for Fiori UIs, the annotation @UI.headerInfo is used at entity level. It specifies the
list title as well as the title for the object page. The main building blocks of the UI are specified as UI facets.
The annotations on element level are used to define the position of the element in the list report and on the
object page. In addition, they specify the importance of the element for the list report. The selection fields are
also defined on the elements that require a filter bar in the UI.
@Metadata.layer: #CORE
@UI: {
headerInfo: { typeName: 'Booking',
typeNamePlural: 'Bookings',
title: { type: #STANDARD,
value: 'BookingID' } } }
annotate view /DMO/C_Booking_U with
{
@UI.facet: [ { id: 'Booking',
purpose: #STANDARD,
type: #IDENTIFICATION_REFERENCE,
label: 'Booking',
position: 10 } ]
@UI: { lineItem: [ { position: 20,
importance: #HIGH } ],
identification: [ { position: 20 } ] }
BookingID;
@UI: { lineItem: [ { position: 30,
importance: #HIGH } ],
identification: [ { position: 30 } ] }
BookingDate;
@UI: { lineItem: [ { position: 40,
importance: #HIGH } ],
To specify the business object's behavior, the behavior definition as the corresponding development object is
used. A business object behavior definition (behavior definition for short) is an ABAP Repository object that
describes the behavior of a business object in the context of the ABAP RESTful application programming
model. A behavior definition is defined using the Behavior Definition Language (BDL).
A behavior definition always refers to a CDS data model. As shown in the figure below, a behavior definition
relies directly on the CDS root entity. One behavior definition refers exactly to one root entity and one CDS root
entity has at most one behavior definition (a 0..1 cardinality), which also handles all included child entities that
are included in the composition tree. The implementation of a behavior definition can be done in a single ABAP
class (behavior pool) or can be split between an arbitrary set of ABAP classes (behavior pools). The application
developer can assign any number of behavior pools to a behavior definition (1..N cardinality).
Overview of Steps
Related Information
To launch the wizard tool for creating a behavior definition, do the following:
Remember
By creating a behavior definition, the referenced root entity and its compositions (child entities) gain a
transactional character. The behavior definition is hence the implementation of the BO concept within the
context of the current programming model. All supported transactional operations of a concrete business
object must be specified in the same behavior definition.
The syntax of the Behavior Definition Language (BDL) is oriented to the Data Definition Language (DDL) used to
define CDS entities (camel-case notation). Technically, the respective artifacts differ substantially: behavior
definitions are managed in the ABAP compiler and not in ABAP Dictionary.
You use the following syntax to define the transactional behavior for a CDSEntity.
/* Associations */
association AssociationName [abbreviation AbbreviationName] {[create;]}
/* Mapping CDS view fields to db fields */
mapping for DB_TABLE
{ CDSViewField1 = db_field1;
CDSViewField2 = db_field2;
...
CDSViewFieldn = db_fieldn; }
}
A behavior definition consists of a header information and a set of definitions for entity behavior. Each entity of
the composition tree can be referred in the behavior definition at most once.
Remember
Consider that if an entity does not occur in the behavior definition, then it would not be modifiable within
the ABAP RESTful application programming model.
Within BDL source code, double slashes (//) introduce a comment that continues until the end of the line.
Comments that span lines have the form: /*... */.
Note
The header specifies the implementation type of the business object provider:
Implementation Type
Values Effect
unmanaged For this implementation type, the application developer must implement essential compo
nents of the REST contract itself. In this case, all required operations (create, update, delete,
or any application-specific actions) must be specified in the corresponding behavior defini-
tion before they are implemented manually in ABAP.
Use this implementation type when developing transactional apps that are based on exist
ing legacy business logic.
managed When using this implementation type, the behavior definition is already sufficient to pro
duce a ready-to-run business object.
Use this implementation type if you want to develop transactional apps from scratch.
abstract You cannot use the BO Provider API to implement the behavior definition. An abstract be
havior definition is only a metadata artifact for the representation of external services.
The behavior description is divided into a section with entity properties, followed by information on any
operations enclosed in the brackets {…}.
The AliasName defined in the behavior definition for CDSEntity gives you the option of introducing a more
concise name than the entity name that is hence easier to read. The AliasName becomes visible in the
implementation part BO provider (method syntax of the BO Provider API). The length of the AliasName is
restricted to 20 characters.
The BDL allows you to add the following properties to a behavior definition:
Characteristic Meaning
implementation in In the behavior definition for an individual entity, you have the option of assigning a specific
class ...unique behavior pool that only implements the behavior for this entity. Behavior for the entity in
question can only be implemented in a behavior pool with the specified name. Any other
class that attempts this raises an ABAP compiler error.
persistent In managed implementation type, this property specifies the database table for storing
table ... CDSEntity data changes that result from transactional behavior.
late numbering Newly created entity instances are given a definitive (final) key just before they are persisted
on the database (when saving the object’s data). Until then, the business logic works with a
temporary key (for example: $00000001) which must be replaced upon saving the data.
For providing late numbering, the adjust_numbers() method from the save sequence
is used. If you redefine the respective method in the saver class, the runtime will call this
method.
Note
The current version of the ABAP RESTful Programming Model does not support late
numbering.
etag master / etag An ETag can be used for optimistic concurrency control in the OData protocol to help pre
dependent by vent simultaneous updates of a resource from overwriting each other.
To use etag dependent by, you must define the respective associations explicitly in the be
havior definition for parent and child entity.
lock master / In the behavior definition, you can determine which entities support direct locking (lock
master) and which entities depend on the locking status of a parent or root entity (lock
lock dependent by
dependent by). For lock dependents it is required to specify which association is used to
determine the lock master. This association must be explicitly defined in the behavior defini-
tion with association _AssociationToLockMasterEntity.
Note
The definition of lock master is currently only supported for root nodes of business
objects.
Property Meaning
field (read only) Defines that the specified fields must not be created or updated by the consumer.
The BO runtime rejects modifying requests when creating or updating the specified fields.
The specified fields must be filled by the consumer when executing modifying requests.
Standard Operations:
An important part of the transactional behavior of a business object are the standard operations create,
update and delete (CUD). Whenever an entity can be created, updated, or deleted, these operations must be
declared in the behavior definition.
To only implement an operation without exposing it to consumers, the option internal can be set before the
operation, for example internal update.
Actions
Actions can be specified as non-standard operations in behavior definitions. For more information, seeActions
[page 111].
Compositions
All compositions that form the business object’s structure must also be declared in the behavior definition as
associations. An abbreviation AbbreviationName needs to be defined if the composition name in the CDS
view is longer than 11 characters. The keyword {create;} is used to declare that the association is create-
enabled, which means that instances of the associated entity can be created by the source of the association .
Mapping
The keyword mapping for defines the mapping contract between database table fields and CDS view fields.
This mapping contract solves the discrepancy between the names of database table fields and CDS view fields
to facilitate writing records to the database table at runtime. Especially database tables that originate in the
legacy application might contain quite short or cryptic field names. With the mapping specification in the
behavior definition you can choose the names in the CDS data model independently from the names in the
database tables.
As a quick glance shows you, the behavior definition looks quite easy in our case (see listing below).
It consists of a header information and two definitions for entity behavior: one for the root entity and one for
the child entity – corresponding to the composition tree of the business object. Note that for each entity of the
composition tree, the transactional behavior can be defined in the behavior definition at most once. All
supported transactional operations of a concrete business object’s node must be specified in the same
behavior definition (that is introduced by the keyword DEFINE BEHAVIOR FOR ... ).
As expected, the header specifies the unmanaged implementation type of our business object’s contract
provider since we are going to integrate the legacy business logic in the new app. For this implementation type,
Our TRAVEL business object refers to the underlying CDS data model, which is represented by root
entity /DMO/I_Travel_U. Behavior for the root entity can only be implemented in the specified behavior
pool /DMO/BP_TRAVEL_U.
Static field control is defined in the behavior definition. In our scenario, the value for the key field TravelID is
drawn by the function module for creating travel instances. Thus, the field must not be editable by the end user
on the UI. Likewise, the field TotalPrice is set to read only. The total price is calculated by the price of the
associated bookings and the booking fee in the function module. In this scenario the total price is not editable
by the end user.
Mandatory fields are AgencyID, CustomerID, BeginDate and EndDate. These fields contain mandatory
information for a travel instance.
The transactional handling of the business object's root node is determined by the standard operations
create, update, and delete, and an instance-related action set_status_booked. Using this action, the
end user is able to set the status of selected travel instances to booked. The action in our example affects the
output instances with the same entity type and one input instance is related to exactly one output instance.
Therefore, the output parameter is defined with the predefined type $self and the cardinality [1]. The fact
that in our scenario new instances of the booking sub node can only be created for a specific travel instance is
considered by the addition of the _Booking association. The keyword {create;} declares that this
association is create-enabled what exactly means that instances of the associated bookings can be created by
a travel instance.
Dynamic operation control is defined for the create by association operation. That means, new bookings can
only be created if the corresponding travel instance is not set to booked.
The names of the database table fields and the names of the CDS data model names differ. That is why, we
specify the mapping contract for every field in the CDS data model. To map the control structure accordingly,
the control structure /dmo/s_booking_intx is defined for the database table.
The sub node of TRAVEL business object refers to the corresponding data model for bookings that is
represented by the child entity for /DMO/I_Booking_U. Behavior for the child entity can only be implemented
in the specified behavior pool /DMO/BP_BOOKING_U. The transactional handling of the booking sub node of
TRAVEL business object is determined by the standard operations update and delete. The creation of
booking instances is handled by the create by association, that means bookings can only be created as a
subnode of its travel parent.
implementation unmanaged;
// behavior defintion for the TRAVEL root node
define behavior for /DMO/I_Travel_U alias travel
implementation in class /DMO/BP_TRAVEL_U unique
etag master LastChangedAt
lock master
{
field ( read only ) TravelID, TotalPrice;
field (mandatory) AgencyID, CustomerID, BeginDate, EndDate;
create;
update;
delete;
Related Information
Behavior Pool
The transactional behavior of a business object in the context of the current programming model is
implemented in one or more global ABAP classes. These special classes are dedicated only to implementing
PUBLIC SECTION.
PROTECTED SECTION.
PRIVATE SECTION.
ENDCLASS.
This global class is defined as abstract and final, which means there is no reason to instantiate or inherit this
global behavior pool. In addition, such a ban prevents possible misuse.
A behavior pool can have static methods, namely CLASS-DATA, CONSTANTS, and TYPES. The application may
place common or even public aspects of its implementation in these methods.
You can assign any number of behavior pools to a behavior definition (1: n relationship). This allows the
application developers to distribute their implementations between multiple units, for example one global class
(behavior pool) for each business object’s node, and one or more separate auxiliary classes for implementing
helper methods. The figure below illustrates this distribution pattern for our sample application.
Note
Best Practices: Splitting the implementation into different global classes allows developers to work in
parallel (distributed work mode). If operations on each node have to be forwarded to different APIs
(function module calls), then we recommend using a separate global class (behavior pool) for each node of
the business object’s compositional tree.
Related Information
In this step, you create a behavior pool that is the implementation artifact of the corresponding behavior
definition that you created earlier.
In doing so, we apply the contribution pattern and split the behavior implementation into two different behavior
pools, one for the travel root entity and the other for the booking child entity. In addition, we create a separate
auxiliary class for implementing helper methods (such as for mapping and message handling) that can be
reused in both behavior implementation classes.
Note
If, for example, the called API only implements one operation for CREATE and another API implements one
for UPDATE, it is advisable to implement each operation in a different local handler class.
If, on the other hand, the called API of your application code is able to process multiple changes in one
call, the handler should reflect this to achieve the best performance. In such a case, we combine all
operations supported by this API in one common handler class.
Otherwise the application code is called multiple times with different input, which can result in bad
performance.
If the application code supports a deep create (for example creation for root and child entity in one step),
then this should be reflected in the design of handler classes to achieve best performance.
Remember
Beyond the performance aspects, it is beneficial to implement operations in different FOR MODIFY
methods, since the orchestration is then passed to the BO runtime and the code of the behavior
implementation is more readable.
Note
Convention: The saver class that implements the save sequence for data persistence is either a separate
global class or a part of the root implementation (behavior pool for the root entity).
To launch the wizard tool for creating a behavior implementation, do the following:
Note
The behavior definition must be active to get the behavior implementation template that matches the
modeled behavior
Further information:
The real substance of a behavior pool is located in Local Types. Here you can define two types of special local
classes, namely handler classes for the operations within the interaction phase and saver classes for the
operations within the save sequence. These classes can be instantiated or invoked only by the ABAP runtime
environment (virtual machine) [page 944].
Note
All local class source code within a single global class is stored within a single include, the CCIMP include.
Based on the declarations in the behavior definition /DMO/I_TRAVEL_U, and taking best practices for
modularization and performance into account, adapt the generated skeleton of the local classes for the root
entity accordingly to the listing below:
Caution
In the current version of ADT tools, the skeleton with the code generated by the class pool creation wizard
differs from the source code in the listing below.
**********************************************************************
*
* Handler class for managing travels
Message handling can be reused by different behavior pools and is thereafore outsourced in a separate helper
class.
1. In your ABAP project (or ABAP Cloud Project), select the select the Source Code Library Classes
node of the relevant package.
2. To launch the creation wizard, open the context menu and choose New ABAP Class.
The business object runtime has two parts: The first part is the interaction phase where a consumer calls
business object operations to change data and read instances with or without the transactional changes. The
Implementation Steps
Related Information
In this topic, you will be guided through all implementation steps required for creation of new travel instances.
We will motivate the steps for the implementation, starting from the UI.
Preview
If you run the UI service based on SAP Fiori ElementsFiori Launchpad, the resulting UI provides you with a list of
existing travel items, including all fields exposed for UI consumption. in the
To create a travel item, the end user must click the + (Create) button and fill all required fields in the related
object page to specify the required information for a new travel instance.
As soon as the user clicks the Save button on the object page, the data is persisted in the corresponding
database table and a travel instance with a new travel ID is created.
Implementation Steps
Note
The local handler class lhc_travel inherits from class cl_abap_behavior_handler and is
automatically instantiated by the framework.
Note that import parameter it_travel_create does not have fixed data type at the design time. At
runtime, the data type is assigned by the compiler with the types derived from behavior definition.
Derived Data Type for the Import Parameter it_travel_create in the ABAP Debugger
**********************************************************************
*
* Handler class for managing travels
*
**********************************************************************
CLASS lhc_travel DEFINITION INHERITING FROM cl_abap_behavior_handler.
PRIVATE SECTION.
METHODS:
create_travel FOR MODIFY
IMPORTING it_travel_create FOR CREATE travel,
... .
ENDCLASS.
CLASS lhc_travel IMPLEMENTATION.
METHOD create_travel.
ENDMETHOD.
...
ENDCLASS.
As given in the listing below, the basic structure of the <method> FOR MODIFY implementation includes:
- A loop on all new travel instances to be created for the root node.
- Mapping the CDS view field names to the database table fields names by using the operator MAPPING FROM
ENTITY USING CONTROL. This operator maps only the fields that are flagged in the control structure of the
importing parameter.
- Call of the business logic function module /DMO/FLIGHT_TRAVEL_CREATE for creation of new travel
instances.
- Message handling for processing messages in case of failure. See step 3 Message Handling [page 345].
Each create action call can produce failed keys (<fs_travel_create>-%cid) and messages
(lt_messages). Any failed keys are stored in the table FAILED [page 875] whereas the REPORTED [page 876]
table includes all instance-specific messages.
Besides an ID of the relevant BO instance and the %FAIL [page 878] component, the failed tables also include
the predefined component %CID [page 877]. It stands for the content ID and is used in an OData request to
bind the result of an operation to a name so that it can be referenced in another operation later in the
transactional processing.
Remember
In some use cases, it may happen that a consumer works with data that is not yet persisted and might not
have a primary key yet. The primary key can be created in the <method> FOR MODIFY call or later in the
save sequence (late numbering). In such cases, a temporary primary key, the content ID (%CID) for an
instance, is used as long as no primary key was created by BO runtime. The content ID is consequently also
used then as a foreign key.
In case of success (lt_messages IS INITIAL) , the two values with the content ID %CID and the new key
travelid are written into the mapped-travel table.
The MAPPED [page 876] tables comprise the components %CID and %KEY [page 877]. They include the
information about which key values were created by the application for given content IDs.
3. Message Handling
When handling changing operations for travel instances, fault events may occur. For the processing of
appropriate messages in such a case, the method handle_travel_messages is used. This method is defined
in a separate auxiliary class /dmo/cl_travel_auxiliary so that it can be called in different FOR MODIFY
methods of the class pools. In order to use the message object from behavior processing framework, the
auxiliary class inherits from the framework class cl_abap_behv. The helper method get_message_object
is defined to retrieve the message object obj in various message handler methods.
CLASS-METHODS handle_travel_messages
IMPORTING
iv_cid TYPE string OPTIONAL
iv_travel_id TYPE /dmo/travel_id OPTIONAL
it_messages TYPE /dmo/t_message
CHANGING
failed TYPE tt_travel_failed
reported TYPE tt_travel_reported.
PRIVATE SECTION.
CLASS-DATA obj TYPE REF TO /dmo/cl_travel_auxiliary.
CLASS-METHODS get_message_object
RETURNING VALUE(r_result) TYPE REF TO /dmo/cl_travel_auxiliary.
ENDCLASS.
To refer to the data set where an error (msgty = 'E' ) or an abort (msgty = 'A') occurred, the failed
table is used, whereas the instance-specific messages are stored in the reported table.
However, messages that originate from the legacy code must be mapped to the messages of the class-based
BO framework.
The method new_message is used in this implementation to map the T100 messages (that originate from the
legacy code) to the messages of the class-based BO framework. It returns a message object and is
implemented in the framework class lcl_abap_behv.
When the save method is called, the final commit is executed on the database and the data entered by the
user is persisted to the new travel instance. As depicted in the listing below, the save method only executes a
call to the function module /DMO/FLIGHT_TRAVEL_SAVE from the legacy business logic.
To discard all changes after the last save, the cleanup method is used. This method delegates the call to the
function module /DMO/FLIGHT_TRAVEL_INITIALIZE from legacy code.
Add the definition for the cleanup method to lsc_saver and call the function module in the implementation.
Checking Results
At this point, you have the opportunity to check how does the resulting app work, and especially the new
implementation of the CREATE operation. For this to happen, however, a suitable business service for UI
consumption must first be defined and published.
For more information, see: Defining Business Service for Fiori UI [page 386]
Related Information
This topic guides you through the implementation steps required for data updates to an existing travel
instance. In this case, however, in addition to the <method> FOR MODIFY, the <method> FOR READ must
also be implemented. It provides read access to the application buffer, which is necessary for ETag
comparison.
Preview
In our travel application scenario, the appropriate business data should be modifiable for all required items of
the travel instance when, for example, the user clicks the Edit button on the Fiori UI.
In change mode, the end user is able to change the relevant travel fields as shown in the figure below. As soon
as the user chooses the Save button on the object page, the changed travel data is saved in the corresponding
tables and a new version of the related travel instance is created.
Implementation Steps
- A loop on all new travel instances to be updated for the root node.
- Mapping the CDS view field names to the database table fields names using the operator MAPPING FROM
ENTITY. This operator maps every field that is declared in the mapping specification in the behavior definition.
- Mapping of the %control-structure of the importing parameter to the predefined flag structure
ls_travelx. The control structure identifies which entity fields were changed by the client. It contains the key
and the flag structure to the data fields and is used in BAPIs to flag each individual data field as changed.
- Call of the business logic function module /DMO/FLIGHT_TRAVEL_UPDATE to update travel instances.
Each update call can produce failed keys and messages (lt_messages). Failed keys are addressed by the
content ID (<fs_travel_update>-%cid_ref) and the value <fs_travel_update>-travelid). In case of
An ETag [page 953] determines the changes to the requested data set to help prevent simultaneous updates of
a data set from overwriting each other. This is precisely the reason why the ETag check requires data from the
buffer. The <method> FOR READ is designed to return the data from the application buffer.
For more information on how to implement this method, see Implementing the READ Operation for Travel Data
[page 352].
Related Information
This topic guides you through the implementation steps required to read travel instances from the
transactional buffer.
Context
In contrast to the CREATE, UPDATE, or DELETE operation, the READ does not have its direct trigger on a Fiori UI.
Remember
The GO button on the UI does not call the <method> FOR READ (the transactional READ) in the behavior
pool. Instead, the GO button executes a query via the orchestration framework and reads data directly from
the database.
The READ operation provides read access to the application buffer. It is used to retrieve data for further
processing during the interaction phase [page 955]. This is necessary, for example, for ETag [page 953]
comparison when executing an UPDATE. Before the to check the conditions for actions and decide if they are
enabled or disabled. In the travel scenario, the action <method> for UPDATE is called, the ETag value in the
application buffer must be compared to the value that is displayed on the UI. Only if these values correspond is
the UPDATE to triggered. This check ensures that the data the end user sees on the UI has not been changed by
other consumers.
The READ is also necessary for dynamic action control [page 100]set_to_booked is disabled when the status
is booked. Hence, the data from the transactional buffer must be read to display the action button
correspondingly.
Both use cases are relevant for our travel scenario. Therefore it is necessary to implement a <method> FOR
READ.
The READ is also necessary to complete the business object and make it accessible by EML [page 953]. In this
case, you can consume the business object directly from ABAP.
Implementation Steps
The READ operation cannot be declared in the behavior definition as it is always implicitly assumed that a READ
is implemented.
Note
If you use groups in your behavior definition, you must explicitly specify the READ operation in one of the
groups. For more information, see Using Groups in Large Development Projects [page 587].
With the changing parameter failed, you specify the fail cause if something goes wrong. If, for example, the
used function module returns the message number 16, the travel ID was not found. In this case, you can return
the fail cause not_found. For all other errors, return fail cause unspecific.
**********************************************************************
*
* Read travel data from buffer
*
**********************************************************************
METHOD read_travel.
DATA: ls_travel_out TYPE /dmo/travel,
lt_message TYPE /dmo/t_message.
LOOP AT it_travel INTO DATA(ls_travel_to_read)
GROUP BY ls_travel_to_read-%key.
CALL FUNCTION '/DMO/FLIGHT_TRAVEL_READ'
EXPORTING
iv_travel_id = ls_travel_to_read-travelid
IMPORTING
es_travel = ls_travel_out
et_messages = lt_message.
IF lt_message IS INITIAL.
"fill result parameter with flagged fields
INSERT CORRESPONDING #( ls_travel_out MAPPING TO ENTITY ) INTO TABLE
et_travel.
ELSE.
"fill failed table in case of error
failed-travel = VALUE #(
BASE failed-travel
FOR msg IN lt_message (
%key = ls_travel_to_read-%key
%fail-cause = COND #(
WHEN msg-msgty = 'E' AND ( msg-msgno = '016' OR msg-msgno = '009' )
In contrast to the CREATE, UPDATE and DELETE operation, the READ cannot be easily tested using a Fiori UI, as
there is no direct execution trigger for reading the data from the application buffer. Nevertheless, you can test
your READ implementation by using EML.
Example
To retrieve the complete entity instance for the respective travel ID, you have to flag every element
explicitly. For more information about EML, see Consuming Business Objects with EML [page 593].
This topic guides you through the implementation steps required to delete an existing travel instance.
Preview
In our scenario, the appropriate travel instance should be deleted when, for example, the user clicks the Delete
button on the Fiori UI.
Expected Behavior
Implementation Steps
Each delete operation call can produce failed keys and messages (lt_messages). Failed keys are addressed by
the content ID (<fs_travel_delete>-%cid_ref [page 877]) and the key value <fs_travel_delete>-
travel_id). In case of failure, failed keys are saved in the failed-travel table, whereas the reported-
travel table includes all instance-specific messages.
Related Information
In this topic, you will be guided through all implementation steps required for creation of new bookings.
In our demo application, we assume that new bookings cannot be created separately but can only in
conjunction with a given travel instance.
The fact that new instances of the bookings can only be created for a specific travel instance is considered in
the behavior definition by the _Booking association:
The keyword { create; } declares that this association is create-enabled what exactly means that instances
of the associated bookings are created by a travel instance.
The figure below shows a list with booking items that belong to a travel instance.
List of Bookings
To add a new booking to a selected travel instance, the end user must click the + icon and edit some fields to
specify the required information for a new booking.
As soon as the user clicks the Save button on the Fiori object page, a booking data set with a new booking
number is created.
Implementation Steps
Corresponding to the template [page 337] for the root entity, the local handler class lhc_travel is defined to
implement each changing operation in one individual <method> FOR MODIFY. In this case, the
create_booking_ba FOR MODIFY method should only be used to implement the create operation for
booking instances by means of an association. The signature of the cba_travel FOR MODIFY includes only
one import parameter it_booking_create_ba to refer to the associated booking instances to be created.
To identify the associated bookings, the aliases for the root entity and the child entity are used - according to
the aliases specified in the behavior definition. The association is expressed in the form:
LISTING 1: Signature of the create_booking_ba FOR MODIFY method (excerpt from template)
ENDCLASS.
As reproduced in the listing below, the implementation of the FOR MODIFY method is initiated by a loop
across all selected travel instances for which associated bookings are to be created. Each selected travel (root)
instance is represented by the travel ID as a primary key (lv_travelid).
Even in a case like this, it can happen that a consumer works with data that is not yet persisted and might not
have a primary key yet (for example, if the primary key is going to be created later in the save sequence (late
numbering)). In such cases, a temporary primary key, the content ID (%CID) for the travel instance is used as
long as no primary key was created by BO runtime. The content ID is then written to the mapped-travel table.
In case of failure, the message is handled by means of the handle_travel_messages method, as we know
from the implementation of FOR MODIFY in the previous topics.
In case of success (lt_messages IS INITIAL), the maximum booking number has to be determined. This is
done by the condition COND #( WHEN … ) where the last given booking number lv_last_booking_id is
compared with the maximum booking ID lv_max_booking_id.
The creation of new bookings for a given travel instance takes place in a further loop across the booking
instances to be created, which are addressed by the association <fs_booking_create_ba>-%target. This
association includes the predefined component %target, which is used to address the target of composition.
To provide the incoming structure for bookings with data, the mapping between the element in CDS views and
the original table fields is required. This mapping is implemented by the operator MAPPING FROM ENTITY
USING CONTROL, which maps the CDS view fields to the database table fields based on the mapping
specification in the behavior definition and the control structure.
In case of success, the values with the content ID %CID and the key values travelid, and bookingid are
written to the mapped-booking table.
The function call can produce failed keys (<fs_booking_create>-%cid) and messages (lt_messages).
Any failed keys are stored in the table failed-booking whereas the reported-booking table includes all
messages that are specific for the failed booking instance
Related Information
This topic guides you through the required implementation steps to dynamically enable and disable the create
by association operation for bookings.
Context
Depending on the value of the Status field of the travel instance, the create by association operation is
enabled or disabled on the corresponding travel instance.
The Fiori Elements app preview displays the create button for the associated bookings on the object page only
if the operation is enabled.
Implementation Steps
implementation unmanaged;
define behavior for /DMO/I_Travel_U alias travel
implementation in class /DMO/BP_TRAVEL_U unique
...
{
...
association _Booking { create (features: instance); }
...
}
The travel instance is read and, depending on the value of the Status field, the create by association is
enabled or disabled.
Related Information
This topic guides you through the implementation steps required to read booking instances associated to a
travel instance from the buffer.
Context
Just like the READ for travel data, the READ by association does not have an explicit trigger on the UI.
Nevertheless, the BO must be fully consumable by EML [page 953] and therefore it is necessary to implement
a method that reads the bookings that are associated to a specific travel ID in our travel scenario.
The READ by association operation provides read access to the application buffer by reading child entity
instances of a parent entity. It is used to retrieve data for further processing during the interaction phase [page
955].
Implementation Steps
The READ by association operation does not have to be declared in the behavior definition as it always
implicitly assumed that READ by association is implemented. However, you can specify it explicitly:
Note
If you use groups in your behavior definition, you must explicitly specify the READ by association
operation in one of the groups. If CREATE by association is also enabled for the BO, the READ and the
CREATE by association must be assigned to the same group due to its syntactical relation. For more
information, see Using Groups in Large Development Projects [page 587].
In the definition part of the handler class for travel, define the method read_booking_ba for READ travel
\_Booking with the entity input parameters IMPORTING and FULL, and the output parameters , RESULT and
LINK.
The boolean parameter FULL indicates whether the consumer requests complete entity instances or only the
keys which are returned with the parameter LINK.
The parameter RESULT returns the complete set of associated entity instances based on the control structure
of the importing parameter.
The parameter LINK returns the key of the source entity and of the associated target entities.
To return the actual data from the application buffer, the imported travel ID is passed to the reading function
module /DMO/FLIGHT_TRAVEL_READ. The function module returns the entity instances of the associated
bookings corresponding to the travel ID and possible messages if an error occurs. If no message is returned, for
each associated booking, fill the output parameter et_link_table with the corresponding source key for the
travel instance and the target key for the booking instance. The result parameter et_booking can then be
filled with the matching values for the fields that are flagged in the control structure if the full parameter
iv_full_requested is set.
In the changing parameter failed, you specify the fail cause if something goes wrong. If, for example, the used
function module returns the message number 16, the travel ID was not found. In this case, you can return the
fail cause not_found. For all other error, return fail cause unspecific.
**********************************************************************
*
* Read booking data by association from buffer
*
**********************************************************************
METHOD read_booking_ba.
DATA: ls_travel_out TYPE /dmo/travel,
lt_booking_out TYPE /dmo/t_booking,
ls_booking LIKE LINE OF et_booking,
lt_message TYPE /dmo/t_message.
LOOP AT it_travel ASSIGNING FIELD-SYMBOL(<fs_travel_rba>).
CALL FUNCTION '/DMO/FLIGHT_TRAVEL_READ'
EXPORTING
iv_travel_id = <fs_travel_rba>-travelid
IMPORTING
es_travel = ls_travel_out
et_booking = lt_booking_out
et_messages = lt_message.
IF lt_message IS INITIAL.
LOOP AT lt_booking_out ASSIGNING FIELD-SYMBOL(<fs_booking>).
"fill link table with key fields
INSERT
VALUE #(
source-%key = <fs_travel_rba>-%key
target-%key = VALUE #(
TravelID = <fs_booking>-travel_id
BookingID = <fs_booking>-booking_id
)
)
INTO TABLE et_link_table.
"fill result parameter with flagged fields
IF iv_full_requested = abap_true.
ls_booking = CORRESPONDING #( <fs_booking> MAPPING TO ENTITY ).
ls_booking-lastchangedat = ls_travel_out-lastchangedat.
INSERT ls_booking INTO TABLE et_booking.
ENDIF.
ENDLOOP.
ELSE.
"fill failed table in case of error
failed-travel = VALUE #(
BASE failed-travel
FOR msg IN lt_message (
%key = <fs_travel_rba>-TravelID
%fail-cause = COND #(
WHEN msg-msgty = 'E' AND ( msg-msgno = '016' OR msg-msgno = '009' )
THEN if_abap_behv=>cause-not_found
ELSE if_abap_behv=>cause-unspecific
)
)
).
ENDIF.
ENDLOOP.
ENDMETHOD.
ENDCLASS.
Testing
Just like the READ operation, the READ by association cannot be easily tested using a Fiori UI, as there is
no direct execution trigger for reading the associated booking from the application buffer. Nevertheless, you
can test your READ by association implementation by using EML.
If you request the RESULT parameter, the importing parameter FULL is set and you retrieve the values
based on the control flags. The parameter LINK is independent on the control flags, it always retrieves the
keys of the source and target entity.
This topic guides you through the implementation steps required to lock instances on the database.
Context
The lock operation is executed before modify operations. It locks the entity instance during the time of the
modify operation and disables other clients to modify the specific instance and all its locking related instances.
As soon as the modify operation is finished, the lock is released.
For more information, see Pessimistic Concurrency Control (Locking) [page 130].
In our unmanaged scenario, the travel entity was defined as lock master and the booking entity as lock
dependent. That means, the locking mechanism always locks the lock master instances and all its dependents.
For example, if the lock operation is executed because a booking instance is updated, the parent travel instance
and all its booking instances are also being locked.
To enable locking, the method FOR LOCK must be implemented. The legacy code of the unmanaged scenario
includes a lock object, which must be called in the method FOR LOCK to lock the relevant entity instances.
Preview
In our scenario, it should not be possible to save changes on the database, while another entity instance of the
same lock master entity instance is being changed at the same moment. In this case, the end user on the UI
gets an error message:
Implementation Steps
The legacy code of the unmanaged scenario includes a lock object, which must be called in the method FOR
LOCK to lock the relevant entity instances.
Corresponding to the template [page 337] for the root node behavior implementation, a local handler class is
defined to implement each operation. In this case, the method lock FOR LOCK is used to implement the lock
operation for the travel business object. As you can see in the listing below, the signature of <method> FOR
LOCK includes only one importing parameter it_travel_lock for referring to the lock master's key.
To lock a travel instance and its lock dependents, instantiate the lock object /DMO/ETRAVEL of the legacy
business logic. This is done by calling the factory method get_instance of
cl_abap_lock_object_factory. This instantiation must not fail, except for technical errors. You can raise a
short dump if this happens.
Call the enqueue method of the lock object and pass the imported travel ID to execute locking. If the instance
is already locked, you will get an exception. This exception can be handled by the handle_travel_messages
of the auxiliary class to fill failed and reported table. If the lock fails for technical reasons, raise a short
dump.
Testing
To test if the locking mechanism works properly, open the preview twice and set a breakpoint after the method
enqueue.
In the first preview application, delete or update any entity instance. The ADT debugger stops at the
breakpoint. At this point, the enqueue lock is already set on the chosen entity instance's lock master and its
dependents. Try to do a modify operation with the other preview application on the same entity instance or any
of its lock master's dependents. Do not stop at the breakpoint this time. You will see that the modify operation
is rejected because the entity instance is locked by the client of the first preview application.
This topic describes the implementation of an action related to the travel instances. Using this action, the end
user should be able to change the status of travel processing.
Preview
Again, we use the option of running the resulting app based on Fiori Elements to check the action execution.
When we run the app, the UI screen provides the button Set to Booked for the action as shown in the figure
below.
Implementation Steps
Once an action is defined in the behavior definition, it must be implemented in the behavior pool of the
business object.
The importing parameter is a freely selected name (in our case: it_travel_set_status_booked). The
action name set_status_booked refers to the name of the action defined in the behavior definition and the
entity travel, on which the action is assigned. The result parameter is also a freely selected name (in our
case: et_travel_set_status_booked).
The main implementation steps are the same as in the implementation of the CUD operations.
To fill the action result parameter accordingly to the action definition, a read must be executed.
METHOD set_travel_status.
DATA lt_messages TYPE /dmo/t_message.
DATA ls_travel_out TYPE /dmo/travel.
DATA ls_travel_set_status_booked LIKE LINE OF et_travel_set_status_booked.
CLEAR et_travel_set_status_booked.
LOOP AT it_travel_set_status_booked ASSIGNING FIELD-
SYMBOL(<fs_travel_set_status_booked>).
DATA(lv_travelid) = <fs_travel_set_status_booked>-travelid.
CALL FUNCTION '/DMO/FLIGHT_TRAVEL_SET_BOOKING'
EXPORTING
iv_travel_id = lv_travelid
IMPORTING
et_messages = lt_messages.
IF lt_messages IS INITIAL.
For dynamic control of actions acting on individual entity instances, the option (features: instance) must
be added to the action set_status_booked in the behavior definition. The required implementation must be
provided in the referenced class pool. In the implementation handler of the class pool you can specify the
condition on which the action is enabled or disabled.
Related Information
This topic guides you through the required implementation steps to dynamically enable and disable actions.
Preview
The following figure shows the effect of dynamic control on the action button Set to Booked: Since the selected
travel instance has a status of B (Booked), the action button is disabled.
Implementation Steps
implementation unmanaged;
define behavior for /DMO/I_Travel_U alias travel
implementation in class /DMO/BP_TRAVEL_U unique
...
{
...
action ( features : instance ) set_status_booked result [1] $self;
...
}
2. Adding dynamic feature control for actions to the method get_features in the behavior
pool
The feature control for actions must be added to the method get_features in the behavior pool. You can
implement feature control for different actions or operations in the same method.
Depending on the value of Status field, the action set_status_booked is enabled or disabled.
Related Information
To launch the wizard tool for creating a behavior implementation, do the following:
Further information:
The generated behavior pool (in our case /DMO/CL_BOOKING_U) provides you with an extension FOR
BEHAVIOR OF.
Based on the declarations in the behavior definition /DMO/I_TRAVEL_U, and taking best practices [page 334]
for modularization and performance into account, adapt the generated skeleton of the local classes for the
child entity in accordance with the listing below:
Caution
In the current version of ADT tools, the skeleton with the code generated by the class pool creation wizard
differs from the source code in the listing below.
**********************************************************************
*
* Handler class implements UPDATE for booking instances
*
**********************************************************************
CLASS lhc_booking DEFINITION INHERITING FROM cl_abap_behavior_handler.
PRIVATE SECTION.
TYPES tt_booking_update TYPE TABLE FOR UPDATE /dmo/i_booking_u.
METHODS:
update_booking FOR MODIFY
IMPORTING it_booking_update FOR UPDATE booking,
delete_booking FOR MODIFY
IMPORTING it_booking_delete FOR DELETE booking,
read_booking FOR READ
IMPORTING it_booking_read FOR READ booking
RESULT et_booking,
cba_supplement FOR MODIFY
IMPORTING it_supplement_create_ba FOR CREATE booking\_booksupplement,
This topic guides you through all implementation steps required for data updates and deletion of booking data
sets.
The behavior definition in our demo application scenario requires the standard operations update, and
delete for the booking child entity. Note that the read operation is not explicitly declared in the behavior
definition, but implicitly expected. The create is handled in the travel behavior by a
create_by_association. In addition, since we use different CDS view field names than in the database
table, we need a mapping specification from CDS names to database fields names.
The create operation is already implemented by using the association relation between the travel root
entity and the booking child entity. Further information: Implementing the CREATE Operation for Associated
Bookings [page 356]
In our application scenario, the appropriate booking data sets should be modifiable for all required fields of the
booking instance when, for example, the user clicks the Edit button on the object page of the Fiori UI.
Edit and Delete Buttons are Available for Each Booking Data Set
In change mode, the end user is able to change the relevant travel fields as shown in the figure below. As soon
as the user chooses the Save button on the object page, the changed booking data is persisted on the database
and a new version of the related booking instance is created.
Implementation Steps
The update_booking FOR MODIFY method of the handler lhc_booking implements the update operation
for bookings. The signature of this method includes only one import table parameter it_booking_update for
referring to the booking instances to be updated.
To update data of bookings, the function module /DMO/FLIGHT_TRAVEL_UPDATE is called. In addition to the
incoming parameters is_travel and it_booking, the corresponding flag structure is_travelx as well as
the flag table type it_bookingx are used.
When updating booking data sets, we must first check which individual data was changed by the end user. This
check is done by mapping the control structure of the importing parameter on a local structure that can be
passed to the update function module.
Message handling for processing instance-specific messages in case of failure is implemented by the
handle_booking_messages method. Failed keys are addressed by the booking content ID
(<fs_booking_update>-%cid_ref) and the values for the travel ID <fs_booking_update>-travelid)
and the booking ID <fs_booking_update>-bookingid. In case of failure, failed keys are saved in the
failed-booking table, whereas the reported-booking table contains all instance-specific messages.
**********************************************************************
2. Adding Data Type Declarations for the Booking Entity in the Auxiliary Class
To use the required import, export, or changing parameters in the signature of methods to be defined for
handling booking operations (later on, in the booking behavior pool’s FOR MODIFY methods), we must first add
the appropriate type declarations to the PUBLIC section in the definition part of our auxiliary class.
PUBLIC SECTION.
...
ENDCLASS.
To refer to an individual data set where an error (msgty = 'E' ) or an abort (msgty = 'A') occurred, the
FAILED table is used, whereas the instance-specific messages are stored in the REPORTED table.
However, messages that originate from the legacy code must be mapped to the messages of the class-based
BO framework. To be reused in different behavior pools, the corresponding message handler method is defined
and implemented in the helper class /dmo/cl_travel_auxiliary.
The basic steps within the method implementation are similar to those we got to know in the previous method
update_booking. The function module /DMO/FLIGHT_TRAVEL_UPDATE is also called for the delete
operation. Here, as in the previous case, the same incoming parameters are used, including the flag structure
is_travelx as well as the flag table type it_bookingx). The appropriate action code for deleting bookings
is defined by the statement action_code = /dmo/if_flight_legacy=>action_code-delete.
**********************************************************************
*
* Handler class for managing bookings
*
**********************************************************************
CLASS lhc_booking DEFINITION INHERITING FROM cl_abap_behavior_handler.
PRIVATE SECTION.
...
METHODS delete_booking FOR MODIFY
IMPORTING it_booking_delete FOR DELETE booking.
ENDCLASS.
**********************************************************************
* Implements the DELETE operation for a set of booking instances
**********************************************************************
CLASS lhc_booking IMPLEMENTATION.
...
METHOD delete_booking.
DATA lt_messages TYPE /dmo/t_message.
LOOP AT it_booking_delete INTO DATA(ls_booking_delete).
CALL FUNCTION '/DMO/FLIGHT_TRAVEL_UPDATE'
EXPORTING
is_travel = VALUE /dmo/s_travel_in( travel_id = ls_booking_delete-
travelid )
is_travelx = VALUE /dmo/s_travel_inx( travel_id = ls_booking_delete-
travelid )
it_booking = VALUE /dmo/t_booking_in( ( booking_id =
ls_booking_delete-bookingid ) )
it_bookingx = VALUE /dmo/t_booking_inx( ( booking_id =
ls_booking_delete-bookingid
action_code = /dmo/
if_flight_legacy=>action_code-delete ) )
IMPORTING
et_messages = lt_messages.
IF lt_messages IS NOT INITIAL.
/dmo/cl_travel_auxiliary=>handle_booking_messages(
Note
If you use groups in your behavior definition, you must explicitly specify the READ operation in one of the
groups. For more information, see Using Groups in Large Development Projects [page 587].
In the handler class for booking entities add the declaration of the method read_booking FOR READ with
importing parameter it_booking_read and the result parameter et_booking.
The implementation steps for reading booking entities are similar to those for reading travel entities, see
Implementing the READ Operation for Travel Data [page 352]. The same function module is used. It reads the
booking instances based on the travel ID that is passed to the function module. For performance reasons, we
only call the function module once for all bookings with the same travel ID. That is why, the loop at the
importing parameter is grouped by the travel ID. As the function module retrieves all available bookings for the
travel ID, the output table must be read for the bookings that match the importing parameter
it_booking_read. The read results are then be passed to the result parameter et_booking for those fields
that are requested by the consumer. The key values are always passed to the result.
The error handling is separated for booking IDs that are not found for the respective travel IDs, for travel IDs
that are not found by the function module, and for other fail causes that cannot be specified.
**********************************************************************
*
* Handler class for managing bookings
*
**********************************************************************
CLASS lhc_booking DEFINITION INHERITING FROM cl_abap_behavior_handler.
PRIVATE SECTION.
...
METHODS read_booking FOR READ
IMPORTING it_booking_read FOR READ booking
RESULT et_booking.
ENDCLASS.
**********************************************************************
* Implements the READ operation for a set of booking instances
**********************************************************************
CLASS lhc_booking IMPLEMENTATION.
METHOD read_booking.
DATA: ls_travel_out TYPE /dmo/travel,
lt_booking_out TYPE /dmo/t_booking,
lt_message TYPE /dmo/t_message.
"Only one function call for each requested travelid
LOOP AT it_booking_read ASSIGNING FIELD-SYMBOL(<fs_travel_read>)
GROUP BY <fs_travel_read>-travelid .
CALL FUNCTION '/DMO/FLIGHT_TRAVEL_READ'
EXPORTING
iv_travel_id = <fs_travel_read>-travelid
IMPORTING
es_travel = ls_travel_out
et_booking = lt_booking_out
et_messages = lt_message.
IF lt_message IS INITIAL.
"For each travelID find the requested bookings
LOOP AT GROUP <fs_travel_read> ASSIGNING FIELD-SYMBOL(<fs_booking_read>
GROUP BY <fs_booking_read>-%key.
READ TABLE lt_booking_out INTO DATA(ls_booking) WITH KEY travel_id =
<fs_booking_read>-%key-TravelID
booking_id =
<fs_booking_read>-%key-BookingID .
"if read was successfull
IF sy-subrc = 0.
"fill result parameter with flagged fields
INSERT
VALUE #( travelid = ls_booking-travel_id
bookingid = ls_booking-booking_id
bookingdate = COND #( WHEN <fs_booking_read>-%control-
BookingDate = cl_abap_behv=>flag_changed THEN ls_booking-booking_date )
customerid = COND #( WHEN <fs_booking_read>-%control-
CustomerID = cl_abap_behv=>flag_changed THEN ls_booking-customer_id )
airlineid = COND #( WHEN <fs_booking_read>-%control-
AirlineID = cl_abap_behv=>flag_changed THEN ls_booking-carrier_id )
connectionid = COND #( WHEN <fs_booking_read>-%control-
ConnectionID = cl_abap_behv=>flag_changed THEN ls_booking-connection_id )
flightdate = COND #( WHEN <fs_booking_read>-%control-
FlightDate = cl_abap_behv=>flag_changed THEN ls_booking-flight_date )
flightprice = COND #( WHEN <fs_booking_read>-%control-
FlightPrice = cl_abap_behv=>flag_changed THEN ls_booking-flight_price )
currencycode = COND #( WHEN <fs_booking_read>-%control-
CurrencyCode = cl_abap_behv=>flag_changed THEN ls_booking-currency_code )
* lastchangedat = COND #( WHEN <fs_booking_read>-
%control-LastChangedAt = cl_abap_behv=>flag_changed THEN ls_travel_out-
lastchangedat )
) INTO TABLE et_booking.
ELSE.
"BookingID not found
INSERT
VALUE #( travelid = <fs_booking_read>-TravelID
bookingid = <fs_booking_read>-BookingID
%fail-cause = if_abap_behv=>cause-not_found )
INTO TABLE failed-booking.
ENDIF.
ENDLOOP.
ELSE.
"TravelID not found or other fail cause
LOOP AT GROUP <fs_travel_read> ASSIGNING <fs_booking_read>.
failed-booking = VALUE #( BASE failed-booking
FOR msg IN lt_message ( %key-TravelID =
<fs_booking_read>-TravelID
%key-BookingID =
<fs_booking_read>-BookingID
%fail-cause =
COND #( WHEN (msg-msgty = 'E' AND msg-msgno = '016' OR msg-msgno = '009')
ELSE if_abap_behv=>cause-unspecific ) ) ).
ENDLOOP.
ENDIF.
ENDLOOP.
ENDMETHOD.
6. Defining and Implementing the READ Operation for the Associated Travel Instance
The read by association from booking instances to their parent travel instance is used to read the ETag
master of the booking instances. That is why, it is essential to support complete ETag handling for the travel
BO. In addition, the read by association must be implemented to complete the transactional handling for
the BO, so that it is consumable via EML.
In order to use the association transactionally, in this case, via a transactional read, the association must be
specified in the behavior definition for the booking instance:
In the handler class for booking entities, add the declaration of the method read_travel_ba FOR READ
booking\_travel with importing parameter it_booking, FULL iv_full_requested, RESULT
et_travel and LINK et_link_table.
For more information, see <method> FOR READ By Association [page 865].
In the implementation for reading travel instances by association from booking entities, loop at the incoming
booking and group by the incoming TravelID. This grouping optimizes the performance as the function call is
only executed once for each travel ID. If the function call to read the associated travel instance is successful,
group at the group <fs_travel> to fill the link table for each requested booking. If the all fields of travel are
requested, fill the result parameter et_travel with the values that are read by the function module. To ensure
that only those fields are filled that were requested (indicated by the %control structure of the importing
parameter), the corresponding operator with the addition MAPPING TO ENTITY is used.
If the function modules returns messages, fill the failed table with the corresponding entries. The error
handling is separated for booking IDs that are not found for the respective travel IDs, for travel IDs that are not
found by the function module, and for other fail causes that cannot be specified.
**********************************************************************
*
* Handler class for managing bookings
*
**********************************************************************
CLASS lhc_booking DEFINITION INHERITING FROM cl_abap_behavior_handler.
PRIVATE SECTION.
...
METHODS read_travel_ba FOR READ
IMPORTING it_booking FOR READ booking\_travel FULL
iv_full_requested
METHOD read_travel_ba.
DATA: ls_travel_out TYPE /dmo/travel,
lt_booking_out TYPE /dmo/t_booking,
ls_travel LIKE LINE OF et_travel,
lt_message TYPE /dmo/t_message.
"Only one function call for each requested travelid
LOOP AT it_booking ASSIGNING FIELD-SYMBOL(<fs_travel>)
GROUP BY <fs_travel>-TravelID.
CALL FUNCTION '/DMO/FLIGHT_TRAVEL_READ'
EXPORTING
iv_travel_id = <fs_travel>-%key-TravelID
IMPORTING
es_travel = ls_travel_out
et_messages = lt_message.
IF lt_message IS INITIAL.
LOOP AT GROUP <fs_travel> ASSIGNING FIELD-SYMBOL(<fs_booking>).
"fill link table with key fields
INSERT VALUE #( source-%key = <fs_booking>-%key
target-%key = ls_travel_out-travel_id )
INTO TABLE et_link_table.
IF iv_full_requested = abap_true.
"fill result parameter with flagged fields
ls_travel = CORRESPONDING #( ls_travel_out MAPPING TO ENTITY ).
INSERT ls_travel INTO TABLE et_travel.
ENDIF.
ENDLOOP.
ELSE. "fill failed table in case of error
failed-booking = VALUE #( BASE failed-booking
FOR msg IN lt_message ( %key-TravelID =
<fs_travel>-%key-TravelID
%key-BookingID =
<fs_travel>-%key-BookingID
%fail-cause = COND
#( WHEN (msg-msgty = 'E' AND msg-msgno = '016' OR msg-msgno = '009' )
THEN if_abap_behv=>cause-not_found
ELSE if_abap_behv=>cause-unspecific ) ) ).
ENDIF.
ENDLOOP.
ENDMETHOD.
ENDCLASS.
Checking Results
At this point, you have the opportunity to check how the resulting app works for the CREATE and the UPDATE.
For this to happen, however, a suitable business service for UI consumption must first be defined and
published.
For more information, see: Defining Business Service for Fiori UI [page 386]
The READ cannot be easily tested using a Fiori UI, as there is no direct execution trigger for reading the data
from the application buffer. Nevertheless, you can test your READ implementation by using EML.
To retrieve the complete entity instance for the respective travel ID, you have to set every element explicitly.
For more information about EML, see Consuming Business Objects with EML [page 593].
You project the behavior to define the behavior for the specific business service.
Whereas the general business object defines and implements the behavior of what can be done in general with
the data provided by the data model, the BO projection defines only the behavior that is relevant for the
specific service. In this travel scenario, the BO projection does not define a specific role of the end user, but
projects the entire behavior for the UI service.
In the behavior projection, you only define the behavior that is relevant for the specific service. The
implementation of the individual characteristics is only done in the general BO. The projection behavior
definition delegates to the underlying layer for the behavior that is defined in the projection layer.
A new projection behavior definition is created that defines the behavior for the specific UI service. The
behavior definition uses the same name as the root projection view.
For the UI service, the entire behavior that is defined in the underlying BO is exposed. This includes:
projection;
define behavior for /DMO/C_Travel_U alias travel
use etag
{
use create;
use update;
use delete;
use action set_status_booked;
use association _BOOKING { create; }
}
define behavior for /DMO/C_Booking_U alias booking
{
use update;
use delete;
}
This section explains how you can model an OData service based on the data model and the related behavior
model. A service like this consists of two artifacts, a service definition and a service binding.
The service definition is a projection of the data model and the related behavior to be exposed, whereas the
service binding implements a specific protocol and the kind of service offered to a consumer.
To describe the consumer-specific perspective as a data model, you need to create a business service
definition (service definition for short) as an ABAP Repository object. A service definition represents the
service model that is derived from the underlying CDS-based data model.
To launch the wizard tool for creating a service definition, do the following:
As in the entries in the listing below, add the following entities for to expose as a service:
The entire source code of a service definition for managing travels is included within the single bracket
{ ... }. It groups all the related CDS entities which are to be exposed as part of the UI service - including
their compositions and associations with the relevant entities. Note that the value help provider or text provider
views must also be exposed for the OData service to make use of the value help and text associations.
Using the business service binding (service binding for short), you can bind a service definition to a client-
server communication protocol.
To launch the wizard tool for creating a service binding, do the following:
After successful activation, the editor provides additional information about the entire entity set as well as
about the navigation path of the respective entity.
As soon as the service is activated, it is ready for consumption through an OData client such as an SAP Fiori
app.
In the course of the UI development in the SAP Web IDE, you have the option of testing the resulting app within
the SAP Fiori launchpad environment.
Tip
Alternatively, you can use the preview function in the service binding to check how the UI of a Fiori
application looks like. More on this: Previewing the Resulting UI Service [page 32]
In this section, you get a brief overview on how you can proceed when going to add a further layer to the flight
demo scenario. Specifically, a booking supplement entity is now to be added to the previous 2-tier layer of the
travel business object.
● Travel
● Booking
● BookingSupplement
That is, each travel instance has 0..n bookings and each booking has 0..n booking supplements.
● Supplement
● SupplementText
Note
With the knowledge so far, you can easily reproduce the concrete steps of this extension. Therefore, in this
topic, we will only outline the implementation steps in a nutshell and refer to the full implementation as it is
available in the demo ABAP package /DMO/FLIGHT/UNMANAGED.
BookingSupplement This entity is used to add additional products to a travel booking. The booking Yes
supplement data is stored in the database table /DMO/BOOK_SUPPL. The flight
data model defines an n:1 cardinality between a Booking Supplement en
tity and a Booking entity.
Supplement A Supplement entity defines product data that the customer can book to No
gether with a flight, for example a drink or a meal.
SupplementText This entity mainly serves a text provider for the associated elements in the target NO
entity BookingSupplement. By using a text association, you define the rela
tionship between an element of the target entity and its corresponding texts or
descriptions.
The BookingSupplement entity is part of the compositional hierarchy of the travel business object. This
composition relationship requires an association to their compositional parent entity. This relationship is
expressed by the keyword ASSOCIATION TO PARENT. Using this syntax, you define the CDS entity
BookingSupplement as a sub node in the compositional hierarchy of the travel business object structure. To
access master data from other entities, additional associations _Product and _SupplementText are defined
in the CDS source code. The associated views are primarily used as a text view for retrieving text information
and as value help provider for specific product fields.
@AbapCatalog.sqlViewName: '/DMO/IBOOKSUPP_U'
@AbapCatalog.compiler.compareFilter: true
@AbapCatalog.preserveKey: true
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Booking Supplement view - CDS data model'
define view /DMO/I_BookingSupplement_U
as select from /dmo/book_suppl as BookingSupplement
association to parent /DMO/I_Booking_U as _Booking on
$projection.TravelID = _Booking.TravelID
and
$projection.BookingID = _Booking.BookingID
association [1..1] to /DMO/I_Supplement as _Product on
$projection.SupplementID = _Product.SupplementID
association [1..*] to /DMO/I_SupplementText as _SupplementText on
$projection.SupplementID = _SupplementText.SupplementID
{
key BookingSupplement.travel_id as TravelID,
key BookingSupplement.booking_id as BookingID,
key BookingSupplement.booking_supplement_id as BookingSupplementID,
Since the BookingSupplement entity is part of the compositional hierarchy of the business object, it is
projected in a projection view. In the projection view, the text relationships, the search and value helps are
defined via CDS annotations. In addition, the association to parent must be redirected to /DMO/C_Booking_U.
The BookingSupplement Entity also has a representation in the UI. It must therefore be equipped with UI
annotations, which are outsourced to metadata extension as done with the other BO entities.
@Metadata.layer: #CORE
@UI: { headerInfo: { typeName: 'Booking Supplement',
typeNamePlural: 'Booking Supplements',
title: { type: #STANDARD, label: 'Booking Supplement',
value: 'BookingSupplementID' } } }
Note
To complete the compositional hierarchy of the business object, you need to add the composition to child
in the Booking entity /DMO/I_Booking_U, as well as the redirected composition in the projection
view /DMO/C_Booking_U.
In order to display and change booking supplement data on Fiori UI, you must add the corresponding UI
facet in the booking metadata extension /DMO/C_Booking_U.
@AbapCatalog.sqlViewName: '/DMO/ISUPPL'
@AbapCatalog.compiler.compareFilter: true
@AbapCatalog.preserveKey: true
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Supplement View - CDS Data Model'
@Search.searchable: true
define view /DMO/I_Supplement
as select from /dmo/supplement as Supplement
association [0..*] to /DMO/I_SupplementText as _SupplText on
$projection.SupplementID = _SupplText.SupplementID
association [0..1] to I_Currency as _Currency on
$projection.CurrencyCode = _Currency.Currency
{
@ObjectModel.text.association: '_SupplText'
key Supplement.supplement_id as SupplementID,
@Semantics.amount.currencyCode: 'CurrencyCode'
@AbapCatalog.sqlViewName: '/DMO/ISUPPTXT'
@AbapCatalog.compiler.compareFilter: true
@AbapCatalog.preserveKey: true
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Supplement Text View - CDS Data Model'
@Search.searchable: true
define view /DMO/I_SupplementText
as select from /dmo/suppl_text as SupplementText
{
@ObjectModel.text.element: ['Description']
key SupplementText.supplement_id as SupplementID,
@Semantics.language: true
key SupplementText.language_code as LanguageCode,
@Search.defaultSearchElement: true
@Search.fuzzinessThreshold: 0.8
@Semantics.text: true
SupplementText.description as Description
}
The fact that in our scenario new instances of the booking supplement entity can only be created for a specific
travel and booking instance is considered by the addition of the _BookSupplement association. The keyword
{create;} declares that this association is create-enabled what exactly means that instances of the
associated booking supplements can be created by an individual booking instance.
The sub node of travel business object refers to the corresponding data model for booking supplements that is
represented by the child entity for /DMO/I_BookingSupplement_U. The transactional behavior of the
booking supplement sub node is determined by the standard operations create, update, and delete. In
addition, since we use different CDS view field names than in the database table, we need a mapping
specification from CDS names to database fields names.
implementation unmanaged;
define behavior for /DMO/I_Travel_U alias travel
{...
}
define behavior for /DMO/I_Booking_U alias booking
Implementing the Handler for UPDATE, DELETE, READ and READ BY ASSOCIATION
You will find the same basic structure when implementing the handler methods for update and delete:
To complete the BO, you must implement a READ operation for associated bookings. The READ by
association is implicitly defined in the behavior definition in the behavior for bookings by the definition of
CREATE by association:
The declaration and the implementation of the method to read booking supplements by association is done in
the handler class for bookings /DMO/BP_BOOKING_U in the same manner as in Implementing the READ
Operation for Associated Bookings [page 363]:
As described in Projecting the Behavior [page 385], the behavior of the travel BO must be projected in the
projection behavior definition /DMO/C_Travel_U.
● the standard operation update and delete on the behavior node /DMO/C_BookingSupplement_U and
● the operation create_by_association for booking supplements on the behavior node /DMO/
C_Booking_U.
projection;
define behavior for /DMO/C_Travel_U alias travel
use etag
{
use create;
use update;
use delete;
use action set_status_booked;
use association _BOOKING { create; }
}
define behavior for /DMO/C_Booking_U alias booking
use etag
{
use update;
use delete;
use association _BOOKSUPPLEMENT { create; }
}
define behavior for /DMO/C_BookingSupplement_U alias bookingsupplement
use etag
{
use update;
use delete;
}
Corresponding to the listing below, the following CDS entities are to be exposed with the UI service:
From a technical perspective, the outcome of these chapters is a business service that is developed based on
the implementation type managed with draft of the ABAP RESTful Application Programming Model using UUID
keys. The business object (BO) is exposed as a UI service. In this example scenario, the Fiori Elements App
Preview is used to illustrate the examples. Yet, the same business object can also be consumed as a Web API.
Introduction
The standard use case for creating a managed transactional app with draft capabilities is the following:
You want to create a completely new transactional app for a Fiori Elements UI following the greenfield
approach. That means, there is no legacy business application logic involved. You create everything from
scratch. Following from that, you can benefit from the lightweight BO standard implementation of the managed
implementation type, in which the RAP managed runtime framework assumes an extensive part of the
standard implementation with out-of-the-box support for create, update, and delete. In addition, you may
need to provide the end user with capabilities to store changed data at any time in the backend and proceed at
a later point in time, or to recover such data from a different device, even if the application client has
terminated unexpectedly. This use case is supported by the draft concept of the ABAP RESTful Application
Programming Model. Draft-enabled business objects persist the state of the transactional buffer after every
transaction on a designated draft database table. This allows the end user to stop and continue work processes
at any point in time, even with inconsistent data. For more detailed information about the draft concept, see
Draft [page 60].
The example scenario uses a data model with UUID-key layout. In managed scenarios, UUIDs are beneficial as
they can be drawn automatically by the RAP managed framework upon creating new instances. However, the
draft scenario can also be developed with semantic primary keys.
Prerequisites
Developing the scenario that is described in the subsequent chapters requires the following:
● You have access to and an account for SAP Cloud Platform, ABAP environment.
● You have installed ABAP Development Tools (ADT).
SAP recommends to use the latest version of the client installation. The ADT download is available on the
update site https://tools.hana.ondemand.com/.
● To recreate the demo scenario, the ABAP Flight Reference Scenario must be available in your ABAP system.
You can download the complete reference scenario from GitHub: Downloading the ABAP Flight Reference
Scenario [page 12].
The following figure provides an overview of all development objects that are involved to create and re-create
the example scenario managed with draft. The basic business object is projected for two business services;
one that supports all the draft capabilities that are included in the basic business object, and one that does not
reuse the draft capabilities in the projection layer and behaves like a managed business service without draft.
Development Flow
The example development scenario is designed to support two approaches. On the one hand, it teaches you
how to build a managed business object with draft from scratch. On the other hand, if you just want to add
draft capabilities to your existing BO, you can leave out the first steps to create a managed business object and
start directly with Draft-Enabling the Managed Business Object [page 468].
The development of an application with draft capabilities based on a managed business object requires the
following activities:
The first part of the managed development scenario with draft describes setting up a managed business object
from scratch.
The assumption in this scenario is that you do not have any data or legacy coding that you want to include in
the new business object. Everything is created from scratch. Therefore, the first step is to create database table
to store the data that the business object consumes. We use a simplified approach to have one database table
for each business object entity. In real-world scenario, this might rarely be the case, but this demo scenario
mainly demonstrates how you handle the transactional behavior with draft for the business object and does
not focus on database structures for business objects.
The key structure of the data model in this scenario is based on UUID keys. That means, every BO entity
instance is uniquely identifiable by one single unique key.
Based on these database tables, the data model and structure for the business object is defined via CDS view
entities. For more information about the basic structure of a business object, see Data Modeling and Behavior
[page 45].
In a second step, the second component of a business object, the behavior, is defined and implemented for the
business object. For more information about, the business object behavior, see Business Object [page 54]
The draft scenario uses the same data model as the other development scenarios. The final app processes
travel data, primarily flight data, to create, update, and delete travel instances.
The travel business object consists of three entities that are structured in a hierarchy tree. Every entity in the
BO-composition tree is modeled with a CDS view entity. The following entities are used for the transactional
data model:
● Travel: The root entity comprises general travel data, like a trip's start and end dates, and the relevant
customer and agency data.
● Booking: The booking entity depicts which bookings are relevant for a certain travel. In the current data
model, only flights are available as bookings.
● BookingSupplement: The booking supplement entity presents additional bookable supplement for a
certain booking. These are meals and beverages for the flights.
The BO-entities are related to each other in a composition structure. They maintain a dependency relationship,
which means that a child entity cannot exist without its parent entity. In addition, a parent entity can have one
to many child entities. The following diagram illustrates this relationship.
The root entity is the head of the business object. You address a BO with the name of the root entity. It is also
the root entity that receives special capabilities during runtime. In the managed scenarios, only root entities
can be lock masters. That means, if one of the entities that are part of the composition tree is requested for
lock, it is the lock master instance and its child instances that are locked for the request. In addition, in the draft
scenario, the lock master, in this case the root entity, receives an additional field for optimistic concurrency
control in the transition from draft to active data. This field is called total ETag. For more information about
the locking mechanism and the total ETag, see Concurrency Control [page 125].
The composition tree includes child entities that are also part of the business object. They are connected via
compositions, bidirectional connections by which the relationship to the root entity is defined.
Additional entities for currencies and countries are generally available in your system and are included in out
data model using associations. These are I_Currency and I_Country.
For the managed scenario with draft, create new database tables for the business object data model that
match the transactional data structure of travel, booking and booking supplement.
In scenarios without existing legacy code, the first step is to create persistent database tables that store data.
Create these tables by using the creation wizard for creating database tables in ADT. For a detailed step-by-step
description, see .
The draft scenario in the ABAP Flight Reference Scenario uses the suffix _D. For the persistent database
tables, we use the prefix A_ to indicate the active persistence. For detailed information, see Naming
Conventions for Development Objects [page 918].
When creating database tables for a managed business object with draft that use UUID key layout, the
following considerations are relevant:
● The RAP managed runtime framework requires administrative data fields for internal processing of the BO
entities. Especially, a field that denotes the timestamp when the instance was last changed is important for
the ETag master handling on each entity. If the administrative fields on local instances are annotated in
CDS with the relevant @Semantics annotations, they are automatically filled by the RAP managed runtime
framework. For more information, seeOptimistic Concurrency Control [page 126].
● To check whether an active BO instance was changed by concurrent clients, the total ETag field requires an
update whenever the active BO instance is changed. The total ETag field is only necessary on the lock
master entity, as the control is done for the complete composition tree. If this field is a timestamp and is
annotated with @Semantics.systemDateTime.lastChangedAt in the corresponding CDS view, the
RAP managed runtime framework updates the field accordingly. As this field is only relevant in draft
scenarios, we will add this field to the database table later in the demo scenario when the managed
business object is draft-enabled. For more information, see Total ETag [page 65].
Note
In most draft scenarios, it is best practice to control the concurrency on instance level (local ETag) and
the concurrency for the transition between draft and active (total ETag). As both fields are not
necessarily updated at the same time, you need two separate fields with timestamps that are updated
at the relevant points in time.
● In UUID scenario, every entity needs a field for the UUID key. In addition, child entities must have a field for
their parent's UUID. Otherwise, you cannot define associations for the composition relationship. In
addition, any child entity that has no parent-child relationship with the lock master entity must have a field
with the lock master's UUID. Otherwise you cannot define associations to the lock master entity, which is
important for lock and ETag handling.
● Apart from the UUID key, each database table also has a semantic ID field. These fields are no technical
keys, but are used to semantically distinguish the entity instance in the business object.
This tables defines general travel data, data that describe the basic information of a trip, such as the travel ID,
customer and agency ID, and dates. Since this scenario is based on a UUID key layout, the table also entails a
field with a UUID data type. In addition, the fields for standard administration data, such as the respective user
or the time of creation are added to the table. In managed business objects, these fields can be filled
automatically by the RAP managed runtime framework if the corresponding @Semantics annotations are
added to the fields in the CDS view entity. Administrative fields, in particular the field that determines when
instance data is changed, are important for concurrency control.
Expand the following code sample to view the source code of /dmo/a_travel_d.
This table defines the booking data for a specific travel instance. It contains general booking data, such as the
booking ID, booking date, and general flight data. To identify a booking instance uniquely, the table must
contain a UUID field for the booking. In UUID scenarios, the parent UUID must be used as a foreign-key field in
the database for the child entity. For the ETag master handling, the booking database table receives a field to
store the timestamp when the instance was updated.
Expand the following code sample to view the source code of /dmo/a_booking_d.
Sample Code
This table defines the supplement for a specific booking instance. It contains general booking supplement
data, such as the booking supplement ID and price information. To identify a booking supplement instance
uniquely, the table must contain a UUID field for the booking supplement. In UUID scenarios, the parent UUID
and the lock master UUID (in our case the root UUID) must be used as a foreign-key field in the database. For
the ETag master handling, the booking supplement database table receives a field to store the timestamp when
the instance was updated.
Expand the following code sample to view the source code of /dmo/a_bksuppl_d.
Sample Code
For the managed scenario with draft, create data definitions for CDS view entities to build the structure for the
business object.
Previously, you have created persistent database table for the travel business object. The structure of the
managed business object must now be defined by CDS data modeling.
Note
CDS view entities [page 950] are the successor DDIC-based CDS views. CDS view entities offer many
advantages, such as optimization and simplification of syntax checks, and improved performance during
view activations.
In ADT, use the wizard for creating CDS data definitions. For a detailed step-by-step description, see .
Select the template Define Root View Entity for the travel entity.
Select the template Define View Entity with To-Parent Association for the booking and the booking supplement
entity.
Note
The draft scenario in the ABAP Flight Reference Scenario uses the suffix _D.Since CDS view entities are
interface views, they are prefixed with I_ accordance with the VDM (virtual data model) naming
convention. For detailed information, see Naming Conventions for Development Objects [page 918].
When using CDS view entities for a managed business object with draft that use UUID key layout, the following
considerations are relevant:
● For a business object, you must define a root entity that represents the head of the business object and
provides the name with which the BO is addressed.
● The composition structure of the managed business object must be defined in the CDS view entities by
compositions to the child entity and associations to the parent entity. In addition, an association to the lock
master entity (in our scenario, the root entity) must be available from all entities in the business object.
● The RAP managed runtime framework fills the administrative fields automatically if they are annotated
with the corresponding @Semantics annotation. These fields are only necessary on the root entity of the
business object. In particular, the field that is annotated with
@Semantics.systemDateTime.localInstanceLastChangedAt: true is updated whenever the
instance is changed. In our scenario, this field is used for ETag comparison.
The data source for the travel root view is the database table /DMO/A_TRAVEL_D. All fields from the database
table are used for the data model.
To retrieve master data for value helps from other entities, define the association to the CDS entities /DMO/
I_Agency,/DMO/I_Customer, and /DMO/I_Currency. The former are part of the ABAP Flight Reference
Scenario, which you can download. The CDS view /DMO/I_Currency is generally available in your
development system.
Annotate the price fields with the annotations @Semantics.amount.currencyCode to define which field is
used as currency.
In contrast to DDIC-based CDS views, in CDS view entities, you do not need to specify the currency code
field as a currency code if the field is already specified in the database table.
Use semantics annotations for the administrative fields. The ETag field local_instance_last_changed_at
receives the annotation @Semantics.systemDateTime.localInstanceLastChangedAt: true.
As soon as the entity of the composition structure exists, define the compositional relationship to the child
entity /DMO/I_Booking_D with the keyword composition.
Expand the following code sample to view the source code of /DMO/I_Travel_D.
Sample Code
The data source for the booking view is the database table /DMO/A_BOOKING_D. All fields from the database
table are used for the data model.
To retrieve master data for value helps from other entities, define the association to the CDS entities /DMO/
I_Customer, /DMO/I_Carrier and /DMO/I_Connection. These CDS views are part of the ABAP Flight
Reference Scenario, which you can download.
Annotate the price fields with the annotations @Semantics.amount.currencyCode to define which field is
used as currency.
Note
In contrast to CDS views, in CDS view entities, you do not need to specify the currency code field as a
currency code if the field is already specified in the database table.
Use semantics annotations for the administrative field. The local ETag field
local_instance_last_changed_at receives the annotation
@Semantics.systemDateTime.localInstanceLastChangedAt: true.
As soon as the entities of the composition structure exist, define the compositional relationship to the child
entity /DMO/I_BookingSupplement_D with the keyword composition, and define the compositional
relationship to the parent entity /DMO/I_Travel_D with the keyword association to parent.
Expand the following code sample to view the source code of /DMO/I_Booking_D.
Sample Code
_Customer,
_Carrier,
_Connection
}
The data source for the booking view is the database table /DMO/A_BKSUPPL_D. All fields from the database
table are used for the data model.
To retrieve master data for value helps and texts from other entities, define the association to the CDS
entities /DMO/I_Supplement and /DMO/I_SupplementText. These CDS views are part of the ABAP Flight
Reference Scenario, which you can download.
Annotate the price fields with the annotations @Semantics.amount.currencyCode to define which field is
used as currency.
Note
In contrast to CDS views, in CDS view entities, you do not need to specify the currency code field as a
currency code if the field is already specified in the database table.
Use semantics annotations for the administrative field. The local ETag field
local_instance_last_changed_at receives the annotation
@Semantics.systemDateTime.localInstanceLastChangedAt: true.
As soon as the entities of the composition structure exist, define the compositional relationship to the parent
entity /DMO/I_Booking_D with the keyword association to parent, and define the compositional
relationship to the root entity /DMO/I_Travel_D.
Expand the following code sample to view the source code of /DMO/I_BookingSupplement_D.
Sample Code
This topic describes the behavior that is used for the business object in the managed scenario with draft.
For the managed scenario with draft, create a behavior definition to define the behavior for the business object.
The behavior definition is the development object that defines the implementation type behavior for the
business object. When using the implementation type managed, for some features, you only need to define
behavior characteristics and standard operations in the behavior definition. The RAP managed runtime
framework provides a generic solution for
● create
● update
● delete
● create by association
● lock handling
● ETag handling.
For more information, see Defining Elementary Behavior for Ready-to-Run Business Object [page 203].
Create the behavior definition by using the creation wizard in ADT. By using the context menu on the root view
(/DMO/I_TRAVEL_D), you can directly create a behavior definition for the travel data model. In the wizard,
choose the implementation Managed. For a detailed description, see Creating Behavior Definitions [page 897].
For the demo scenario with a managed business object with draft, the following considerations are relevant.
● The behavior of each entity is implemented in a separate behavior class pool. Hence, the implementation
class must be created for each entity separately. For more information, see Best Practives for
Modularization and Performance [page 334].
● The managed implementation type requires the specification of lock master or lock dependent on
each entity. For more information, see Pessimistic Concurrency Control (Locking) [page 130].
● In managed business objects, it is best practice to define a local ETag master on each entity. With an ETag
master on every entity, you ensure that the ETag check is done for every entity independently. For more
information, see Optimistic Concurrency Control [page 126].
● The RAP managed runtime framework is able to automatically draw primary key values in UUID scenarios.
You use this functionality by defining early managed numbering in the behavior definition. Setting the
primary key field to read only defines strict internal numbering. An external BO consumer is not allowed
to provide the primary key values in this case. For more detailed information, see Automatically Drawing
Primary Key Values in Managed BOs [page 579].
● If different names are used on the database table and in the CDS data model, you need to define the
mapping for these fields. This is done via the mapping operator in the behavior definition.
The template for the managed implementation type provides the behavior definition for every entity in the
composition tree of the root entity. In addition, it specifies the data source of the respective entities as
Define the root entity as lock master. The current version of the ABAP RESTful Application Programming Model
only supports the specification of lock master on the root entity. Define the booking entity and the booking
supplement entity as lock dependent. Lock dependents require an association to the lock master entity. In
the CDS data model, you have specified the association to the travel entity _Travel. To use the association for
the lock dependent, define the association as transaction-enabled in the behavior definition. For more
information, see Lock Dependent [page 135].
As the TravelUUID is used for the lock dependent relationship, it must be set to readonly in the Booking
and BookingSupplement entity behavior.
Define an ETag master field on each entity. Use the field LocalLastChangedAt that is part of the entity's data
model. Since this field is annotated with the annotation
@Semantics.systemDateTime.localInstanceLastChangedAt: true, it is updated automatically by the
RAP managed runtime framework.
Use early managed numbering for the primary key fields on every entity and set the fields to read only, so
that the UUID values are drawn strictly internally.
Map the field names in the CDS data definition to the fields on the database table.
For each entity, define an alias and an implementation class. The behavior definition editor provides a quickfix
to directly create the behavior class with the name that you specify in the behavior definition. As there is no
behavior defined that needs implementation, the local types of the behavior pool are empty.
Expand the following code sample to view the source code of /DMO/I_Travel_D.
Sample Code
managed;
define behavior for /DMO/I_Travel_D alias Travel
implementation in class /dmo/bp_i_travel_d unique
persistent table /dmo/a_travel_d
lock master
//authorization master ( instance )
etag master LocalLastChangedAt
{
create;
update;
delete;
association _Booking { create; }
field ( numbering : managed, readonly ) TravelUUID;
mapping for /dmo/a_travel_d
{ AgencyID = agency_id;
BeginDate = begin_date;
BookingFee = booking_fee;
CurrencyCode = currency_code;
CustomerID = customer_id;
Description = description;
EndDate = end_date;
LocalCreatedAt = local_created_at;
LocalCreatedBy = local_created_by;
LocalLastChangedAt = local_last_changed_at;
For the managed scenario with draft, add actions for nonstandard modify operations.
Actions are used to manifest business-logic-specific work-flows in one operation. You can implement simple
status changes or a complete creation work-flow in one operation. For the UI, you can define action buttons
that execute the action directly when the consumer chooses the button. For more detailed information, see
Actions [page 111].
For the managed scenario with draft, define and implement actions on the travel entity.
Both actions change the status of a travel instance. They are implemented exactly in the same way: Define the
actions in the behavior definition and implement them in the behavior implementation class for Travel.
Definition
Define instance actions with result $self. The field OverallStatus is set to read only, so the consumer
can only change the status via the actions acceptTravel and rejectTravel.
Sample Code
Via a quick fix, you can generate the method declaration in the behavior pool directly from the behavior
definition editor.
Implementation
1. Define constants that are available for the travel status in the private section of the local types.
Sample Code
2. Update the field OverallStatus with a modify request for all instances with the given keys. Provide the
value accpeted for accepting the travel, and rejected for rejecting the travel.
3. The actions return $self. That means, they must return the updated instances in the result parameter. For
that reason, read the updated instances from the buffer and fill an internal table.
4. Pass the internal table to the result parameter.
Expand the following code sample to view the source code of Action acceptTravel.
METHOD acceptTravel.
"Modify travel instance
MODIFY ENTITIES OF /DMO/I_Travel_D IN LOCAL MODE
ENTITY Travel
UPDATE FIELDS ( OverallStatus )
WITH VALUE #( FOR key IN keys ( %tky = key-%tky
OverallStatus = travel_status-
accepted ) )
FAILED failed
REPORTED reported.
"Read changed data for action result
READ ENTITIES OF /DMO/I_Travel_D IN LOCAL MODE
ENTITY Travel
ALL FIELDS WITH
CORRESPONDING #( keys )
RESULT DATA(lt_travel).
result = VALUE #( FOR travel IN lt_travel ( %tky = travel-%tky
%param = travel ) ).
ENDMETHOD.
Expand the following code sample to view the source code of Action rejectTravel.
Sample Code
METHOD rejectTravel.
"Modify travel instance
MODIFY ENTITIES OF /DMO/I_Travel_D IN LOCAL MODE
ENTITY Travel
UPDATE FIELDS ( OverallStatus )
WITH VALUE #( FOR key IN keys ( %tky = key-%tky
OverallStatus = travel_status-
rejected ) )
FAILED failed
REPORTED reported.
"Read changed data for action result
READ ENTITIES OF /DMO/I_Travel_D IN LOCAL MODE
ENTITY Travel
ALL FIELDS WITH
CORRESPONDING #( keys )
RESULT DATA(lt_travel).
result = VALUE #( FOR travel IN lt_travel ( %tky = travel-%tky
%param = travel ) ).
ENDMETHOD.
Action deductDiscount
Define the action in the behavior definition and implement it in the behavior implementation class for Travel.
deductDiscount requires an importing parameter for entering the discount percentage. The value must be
passed together with the action request. The action parameter is modeled with an abstract entity.
Define an element for the discount percentage with the type abap.int1 and activate the data definition.
Define the action in the behavior definition and implement it in the behavior implementation class for Travel.
Definition
Define an instance action with an action parameter /DMO/A_Travel_Discount and the result $self.
Sample Code
Via a quick fix, you can generate the method declaration in the behavior pool directly from the behavior
definition editor.
Implementation
Note
The input parameter of parameter actions is passed to the action method as the component $param in the
importing parameter.
Calculate the new total price for the travel instance by deducting the provided discount percentages. The
following steps guide you through the implementation.
1. Define an internal table and pass the entries of the importing parameter keys. Loop at the internal table
for the keys with parameter values that do not match the preconditions for percentage value:
1. It must not be initial.
2. It must be less than 100.
3. It must be greater or equal than 0.
2. Append the transactional key to the failed table and append the corresponding message to the reported
table if there are keys that do not match the preconditions. Delete the entries in the internal table that do
not match the preconditions.
3. If there are still entries in the internal table that match the preconditions, read all travel instances with the
imported keys into an internal lt_travel for the remaining entries. If the READ fails, add the failed keys
to the failed table of the action.
4. Loop over the internal table and calculate the new reduced booking fee and append it to an internal table
for update.
5. Execute a modify request to update the field BookingFee for the instances in the buffer.
6. To fill the action result, read the involved travel instances and fill the result correspondingly.
Expand the following code sample to view the source code of Action deductDiscount.
METHOD deductDiscount.
DATA lt_update_travel TYPE TABLE FOR UPDATE /DMO/I_Travel_D.
DATA(lt_keys) = keys.
LOOP AT lt_keys ASSIGNING FIELD-SYMBOL(<fs_key>) WHERE %param-
discount_percent IS INITIAL
OR %param-
discount_percent > 100
OR %param-
discount_percent <= 0.
APPEND VALUE #( %tky = <fs_key>-%tky ) TO failed-travel.
APPEND VALUE #( %tky = <fs_key>-%tky
%msg = new_message( id = '/DMO/
CM_FLIGHT_LEGAC'
number = '047'
"discount invalid
severity =
if_abap_behv_message=>severity-error )
%element-TotalPrice = if_abap_behv=>mk-on ) TO reported-
travel.
DELETE lt_keys.
ENDLOOP.
CHECK lt_keys IS NOT INITIAL.
"get total price
READ ENTITIES OF /DMO/I_Travel_D IN LOCAL MODE
ENTITY Travel
FIELDS ( BookingFee )
WITH CORRESPONDING #( lt_keys )
RESULT DATA(lt_travel)
FAILED DATA(read_failed).
failed = CORRESPONDING #( DEEP read_failed ).
LOOP AT lt_travel ASSIGNING FIELD-SYMBOL(<fs_travel>).
DATA lv_percentage TYPE decfloat16.
DATA(lv_discount_percent) = lt_keys[ %tky = <fs_travel>-%tky ]-%param-
discount_percent.
lv_percentage = lv_discount_percent / 100 .
DATA(lv_reduced_fee) = <fs_travel>-BookingFee * ( 1 - lv_percentage ) .
APPEND VALUE #( %tky = <fs_travel>-%tky
BookingFee = lv_reduced_fee ) TO lt_update_travel.
ENDLOOP.
"update total price with reduced price
MODIFY ENTITIES OF /DMO/I_Travel_D IN LOCAL MODE
ENTITY Travel
UPDATE FIELDS ( BookingFee )
WITH lt_update_travel
FAILED DATA(update_failed)
REPORTED DATA(update_reported).
"Read changed data for action result
READ ENTITIES OF /DMO/I_Travel_D IN LOCAL MODE
ENTITY Travel
ALL FIELDS WITH
CORRESPONDING #( lt_travel )
RESULT DATA(lt_travel_with_discount).
result = VALUE #( FOR travel IN lt_travel ( %tky = travel-%tky
%param = travel ) ).
ENDMETHOD.
Action reCalcTotalPrice
The action reCalcTotal price is called by determinations if changes on prices in the travel entity itself or in
the child entities are executed. The total price on the travel entity is then recalculated.
Remember
In our demo scenario, the action is called from determinations on all three BO entities, whenever a price field or
a currency code field is changed.
Sample Code
Via a quick fix, you can generate the method declaration in the behavior pool directly from the behavior
definition editor.
Implementation
To determine the total price of a travel, all prices must be converted to the travel currency and then summed
up. This is done for each BO entity separately. The following steps guide you through the implementation.
1. Define an internal standard table amount_per_currencycode to process amounts and the related
currency code.
2. Read all the instances with the imported keys into an internal table lt_travel. Delete instances with
empty CurrencyCode. For such instances, the total price cannot be calculated.
3. Loop at lt_travel and start filling the amounts per currency with the corresponding values from the
travel instance.
4. Read all the bookings for this travel instance into lt_booking.
5. Loop at the bookings in lt_booking where the currency code is filled and add the price to the travel price
in the table amount_per_currencycode if it has the same currency code. The ABAP statement COLLECT
is useful in this case, as it sums the amounts with the same currency code and appends a new line if the
currency codes differ.
6. Read all the booking supplement instances for all bookings in lt_booking into lt_bookingsupplement.
7. Loop at the booking supplements in lt_bookingsupplement where the currency code is filled and add
the price to the travel price in the table amount_per_currencycode if it has the same currency code.
8. Clear the total price in this travel.
9. Loop at the table amount_per_currencycode and convert the amounts with currency codes other than
the travel currency code into the currency code of the travel instance.
10. Sum up the converted amounts for one travel.
11. Modify the travel entity with the new total price for all requested travel instances.
Expand the following code sample to view the source code of Action reCalCTotalPrice.
Sample Code
METHOD reCalcTotalPrice.
For the managed scenario with draft, add determinations to calculate values implicitly.
With determinations, you can generalize the calculation of values in the business logic of a managed BO. For
more detailed information, see Developing Determinations [page 243].
Note
If you expose your business object for a UI service, you can define side effects to trigger validations or
determinations at other points in time, for example, after updating a field or a field group on the UI in a
draft-enabled application. In non-draft scenarios, side effects can trigger validations and determinations
during the save sequence. Side effects trigger a call to the backend to validate or determine values after
predefined activities on the UI. These side effects must be defined and annotated in the OData document.
For this demo scenario, define and implement determinations for all three BO entities.
Note
In this demo scenario, we use a simplified approach to determine new readable IDs for the BO
instances. To ensure that gap-free and non-duplicate IDs are assigned, use a number range object.
Technically speaking, this determination is a determination on save with the trigger operation
create.
○ setStatusToNew
The travel is set to Open when a new instance is created.
Note
You can only define trigger fields for a determination from the same entity the determination is
assigned to. A determination that is defined for the travel entity cannot have trigger fields from the
booking entity.
Technically speaking, this determination is a determination on modify with the trigger operation
create and with the field triggers BookingFee and CurrencyCode.
For a detailed description on how to implement the determinations for the travel entity, see Determining
Values for the Travel Entity [page 428].
● For the booking entity, the following determinations are described:
○ setBookingNumber
The semantic ID for the booking entity is drawn by the determination when a new instance is created.
Since the semantic BookingID should not be provided by the client, the field must be set to read
only.
Technically speaking, this is a determination on save with the trigger operation create.
○ setBookingDate
The booking date is set when the booking instance is saved to the database. The booking date is only
set internally by the determination and must not be changed after the instance is saved. Therefore, set
the field BookingDate to read only. #
Technically speaking, this is determination on save with the trigger operation create.
○ calculateTotalPrice
See description above. Since the recalculation must also be triggered when the related fields of the
booking entity are changed, the determination must be defined and implemented again.
Technically speaking, this is a determination on modify with the field triggers FlightPrice and
CurrencyCode.
For a detailed description on how to implement the determinations for the booking entity, see Determining
Values for the Booking Entity [page 431].
● For the booking supplement entity, the following determinations are described:
○ setBookSupplNumber
The semantic ID for the booking supplement entity is drawn by the determination when a new instance
is created. Since the semantic BookingSupplementID should not be provided by the client, the field
must be set to read only.
Technically speaking, this is a determination on save with the trigger operation create.
○ calculateTotalPrice
See description above. Since the recalculation must also be triggered when the related fields of the
booking supplement entity are changed, the determination must be defined and implemented again.
Technically speaking, this is a determination on modify with the trigger operation create and the
field triggers BookSupplPrice and CurrencyCode.
For the managed scenario with draft, define and implement determinations on the travel entity for implicit
value calculation.
Determination setTravelNumber
Define the determination in the behavior definition and implement it in the behavior implementation class for
Travel.
Definition
Define a determination on modify with trigger operation create. Since the travel ID must not be changed
externally, define the field TravelID as read only.
Sample Code
Via a quick fix, you can generate the method declaration in the behavior pool directly from the behavior
definition editor.
Implementation
Determine the number for a new travel instance by selecting the highest available number from the database
and add 1. The following steps guide you through the implementation.
1. Read all the travel instances with the imported keys into an internal table and delete the instances that
already have a travel number (TravelID). Continue to work only with instances that do not have a travel
number yet. If the determination is executed several times, the travel numbers must not be calculated
again. This is particularly important in order to stick to the rule of idempotence for determinations.
The determination result must not change if the determination is executed several times under the
same conditions (idempotence). See Rules for Determinations and Validations [page 244].
2. Select the maximum travel number from the database table /dmo/a_travel_d.
3. Update the field TravelID of all involved instances with a modify request and assign a new TravelID by
adding 1 to the max travel from before . Write messages in the request's reported table.
4. Write the messages of the request's reported table to the one of the determination. This passes the
messages to the determination consumer.
Expand the following code sample to view the source code of Determination setTravelNumber.
Sample Code
METHOD setTravelNumber.
"Ensure idempotence
READ ENTITIES OF /DMO/I_Travel_D IN LOCAL MODE
ENTITY Travel
FIELDS ( TravelID )
WITH CORRESPONDING #( keys )
RESULT DATA(lt_travel).
DELETE lt_travel WHERE TravelID IS NOT INITIAL.
CHECK lt_travel IS NOT INITIAL.
"Get max travelID
SELECT SINGLE FROM /dmo/a_travel_d FIELDS MAX( travel_id ) INTO
@DATA(lv_max_travelid).
"update involved instances
MODIFY ENTITIES OF /DMO/I_Travel_D IN LOCAL MODE
ENTITY Travel
UPDATE FIELDS ( TravelID )
WITH VALUE #( FOR ls_travel IN lt_travel INDEX INTO i (
%tky = ls_travel-%tky
TravelID = lv_max_travelid + i ) )
REPORTED DATA(lt_reported).
"fill reported
reported = CORRESPONDING #( DEEP lt_reported ).
ENDMETHOD.
Determination setStatusToNew
Define the determination in the behavior definition and implement it in the behavior implementation class for
Travel.
Definition
Define a determination on modify with trigger operation create. The overall status of the travel is only
changed by the actions rejectTravel and acceptTravel, see Accept and Reject Travel [page 420].
Therefore the field is read only for the external consumer.
Sample Code
Via a quick fix, you can generate the method declaration in the behavior pool directly from the behavior
definition editor.
Implementation
Set the status to Open when new instances are created The following steps guide you through the
implementation.
1. Since the status is not dependent on any other value, directly update the field OverallStatus with
openby executing a modify request for all instances with the imported keys. The constant open was
defined before in the private section of the local types. Write messages in the reported table of the modify
request.
Note
Idempotence is not an issue in this determination. Even if the determination is executed several times
and an instance already has the status New, the outcome of the determination does not change with
the modify request execution.
2. Update the field TravelID of all involved instances with a modify request. Write messages in the request's
reported table.
3. Write the messages of the request's reported table to the one of the determination. This passes the
messages to the determination consumer.
Expand the following code sample to view the source code of Determination setStatusToNew.
Sample Code
METHOD setStatusToNew.
MODIFY ENTITIES OF /DMO/I_Travel_D IN LOCAL MODE
ENTITY travel
UPDATE SET FIELDS
WITH VALUE #( FOR key IN keys ( %tky = key-%tky
OverallStatus = travel_status-open ) )
REPORTED DATA(lt_reported).
reported = CORRESPONDING #( DEEP lt_reported ).
ENDMETHOD.
Determination calculateTotalPrice
Define the determination in the behavior definition and implement it in the behavior implementation class for
Travel.
Note
The actual calculation of the total price is done by the action recalcTotalPrice. The determination just
executes the action. See Action recalcTotalPrice [page 423].
Sample Code
Via a quick fix, you can generate the method declaration in the behavior pool directly from the behavior
definition editor.
Implementation
Execute the action recalcPrice when the trigger fields are changed. The following steps guide you through
the implementation.
1. Execute a modify request for action execution for all instances with the imported keys. Write messages in
the reported table of the modify request.
2. Write the messages of the request's reported table to the one of the determination. This passes the
messages to the determination consumer.
Expand the following code sample to view the source code of Determination calculateTotalPrice.
Sample Code
METHOD calculateTotalPrice.
MODIFY ENTITIES OF /DMO/I_Travel_D IN LOCAL MODE
ENTITY Travel
EXECUTE reCalcTotalPrice
FROM CORRESPONDING #( keys )
REPORTED DATA(lt_reported).
reported = CORRESPONDING #( DEEP lt_reported ).
ENDMETHOD.
For the managed scenario with draft, define and implement determinations on the booking entity for implicit
value calculation.
Determination setBookingNumber
Define the determination in the behavior definition and implement it in the behavior implementation class for
Travel.
Sample Code
Via a quick fix, you can generate the method declaration in the behavior pool directly from the behavior
definition editor.
Implementation
Determine the number for a new booking instance by looping at all bookings for one booking and determine the
greatest ID. Add 1 for every new booking instance. The following steps guide you through the implementation.
1. Read all the corresponding travel instances for the incoming booking keys into an internal table. If multiple
bookings of the travel are requested, the travel is returned only once.
2. Loop at the internal table and read the bookings for each travel instance into an internal table.
3. Define a variable for the maximum booking ID and set the value to '0000' (initial value).
4. Loop at the bookings for one travel and compare the booking ID with the maximum booking ID to
determine the maximum booking ID available for the travel instance.
5. Provide a booking ID for all bookings of this travel that have none. Only the booking instances that have no
booking ID assigned are taken into account. This is particularly important in order to stick to the rule of
idempotence for determinations.
Remember
The determination result must not change if the determination is executed several times under the
same conditions (idempotence). See Rules for Determinations and Validations [page 244].
Write the new booking IDs into a new internal table typed as table for update.
6. Update the booking entity with the entries of the internal update table.
Write the messages of the request's reported table to the one of the determination . This passes the
messages to the determination consumer.
Expand the following code sample to view the source code of Determination setBookingNumber.
Sample Code
METHOD setBookingNumber.
DATA max_bookingid TYPE /dmo/booking_id.
DATA lt_booking_update TYPE TABLE FOR UPDATE /DMO/I_Travel_D\\Booking.
"Read all travels for the requested bookings
" If multiple bookings of the same travel are requested, the travel is
returned only once.
READ ENTITIES OF /DMO/I_Travel_D IN LOCAL MODE
ENTITY Booking BY \_Travel
FIELDS ( TravelUUID )
WITH CORRESPONDING #( keys )
RESULT DATA(lt_travel).
Determination setBookingDate
Define the determination in the behavior definition and implement it in the behavior implementation class for
Booking.
Definition
Define a determination on save with trigger operation create. Once the booking date is set, when a booking
instance is saved to the database, the date should not be changed afterward. Therefore the field BookingDate
is read only for the external consumer.
Sample Code
Via a quick fix, you can generate the method declaration in the behavior pool directly from the behavior
definition editor.
Implementation
Set the system date for the booking date. The following steps guide you through the implementation.
1. Read all the booking instances with the imported keys into an internal table and delete the instances that
already have a booking date (BookingDate). Continue to work only with instances that do not have a
Remember
The determination result must not change if the determination is executed several times under the
same conditions (idempotence). See Rules for Determinations and Validations [page 244].
2. Assign the system date sy-datum to BookingDate for every incoming booking instance .
3. Update the field BookingDate of all involved instances with a modify request. Write messages in the
request's reported table.
4. Write the messages of the request's reported table to the one of the determination. This passes the
messages to the determination consumer.
Expand the following code sample to view the source code of Determination setBookingDate.
Sample Code
METHOD setBookingDate.
ENDMETHOD.
Determination calculateTotalPrice
Define the determination in the behavior definition and implement it in the behavior implementation class for
Booking.
Note
The actual calculation of the total price is done by the action recalcPrice. The determination just
executes the action. See Action recalcTotalPrice [page 423].
Definition
Define a determination on modify with operation trigger create and field triggers FlightPrice and
CurrencyCode.
Via a quick fix, you can generate the method declaration in the behavior pool directly from the behavior
definition editor.
Implementation
Execute the action recalcPrice when the trigger fields are changed. The following steps guide you through
the implementation.
1. To get the TravelUUID, read the travel instance by a read by association into an internal table. The key of
the root instance is needed for the action execution.
2. Execute a modify request for action execution for all affected travel instances. Write messages in the
reported table of the modify request.
3. Write the messages of the request's reported table to the one of the determination. This passes the
messages to the determination consumer.
Expand the following code sample to view the source code of Determination calculateTotalPrice.
Sample Code
METHOD calculateTotalPrice.
" Read all parent UUIDs
READ ENTITIES OF /DMO/I_Travel_D IN LOCAL MODE
ENTITY Booking BY \_Travel
FIELDS ( TravelUUID )
WITH CORRESPONDING #( keys )
RESULT DATA(lt_travel).
" Trigger Re-Calculation on Root Node
MODIFY ENTITIES OF /DMO/I_Travel_D IN LOCAL MODE
ENTITY Travel
EXECUTE reCalcTotalPrice
FROM CORRESPONDING #( lt_travel )
REPORTED DATA(lt_reported).
reported = CORRESPONDING #( DEEP lt_reported ).
ENDMETHOD.
For the managed scenario with draft, define and implement determinations on the booking supplement entity
for implicit value calculation.
Determination setBookSupplNumber
Define the determination in the behavior definition and implement it in the behavior implementation class for
BookingSupplement.
Definition
Define a determination on save with trigger operation create. Since the booking supplement ID must not be
changed externally, define the field BookingSupplementID as read only.
Sample Code
Via a quick fix, you can generate the method declaration in the behavior pool directly from the behavior
definition editor.
Implementation
The implementation for determining new booking supplement numbers is the dame as the calculation for the
booking. For a detailed step-by-step description, see Determination setBookingNumber [page 431].
Expand the following code sample to view the source code of Determination setBookSupplNumber.
Sample Code
METHOD setBookingNumber.
DATA max_bookingid TYPE /dmo/booking_id.
DATA lt_booking_update TYPE TABLE FOR UPDATE /DMO/I_Travel_D\\Booking.
"Read all travels for the requested bookings
" If multiple bookings of the same travel are requested, the travel is
returned only once.
READ ENTITIES OF /DMO/I_Travel_D IN LOCAL MODE
ENTITY Booking BY \_Travel
FIELDS ( TravelUUID )
WITH CORRESPONDING #( keys )
RESULT DATA(lt_travel).
" Process all affected travels. Read respective bookings for one travel
LOOP AT lt_travel INTO DATA(ls_travel).
READ ENTITIES OF /dmo/i_travel_d IN LOCAL MODE
ENTITY Travel BY \_Booking
FIELDS ( BookingID )
WITH VALUE #( ( %tky = ls_travel-%tky ) )
Determination calculateTotalPrice
Define the determination in the behavior definition and implement it in the behavior implementation class for
BookingSupplement.
Note
The actual calculation of the total price is done by the action recalcPrice. The determination just
executes the action. See Action recalcTotalPrice [page 423].
Definition
Define a determination on modify with operation trigger create and field triggers BookSupplPrice and
CurrencyCode.
Sample Code
Via a quick fix, you can generate the method declaration in the behavior pool directly from the behavior
definition editor.
Implementation
Execute the action reCalcTotalPrice when the trigger fields are changed. The steps are exactly the same as
in Determination calculateTotalPrice [page 434].
Expand the following code sample to view the source code of Determination calculateTotalPrice.
METHOD calculateTotalPrice.
" Read all parent UUIDs
READ ENTITIES OF /DMO/I_Travel_D IN LOCAL MODE
ENTITY BookingSupplement BY \_Travel
FIELDS ( TravelUUID )
WITH CORRESPONDING #( keys )
RESULT DATA(lt_travel).
" Trigger Re-Calculation on Root Node
MODIFY ENTITIES OF /DMO/I_Travel_D IN LOCAL MODE
ENTITY Travel
EXECUTE reCalcTotalPrice
FROM CORRESPONDING #( lt_travel )
REPORTED DATA(lt_reported).
reported = CORRESPONDING #( DEEP lt_reported ).
ENDMETHOD.
For the managed scenario with draft, add validations to check the values provided by the client.
Validations are used to check whether provided values by a client are consistent. They give direct feedback
(messages) before the BO instance is saved to the database. For more detailed information, see Developing
Validations [page 234].
Note
If you expose your business object for a UI service, you can define side effects to trigger validations or
determinations at other points in time, for example, after updating a field or a field group on the UI in a
draft-enabled application. In non-draft scenarios, side effects can trigger validations and determinations
during the save sequence. Side effects trigger a call to the backend to validate or determine values after
predefined activities on the UI. These side effects must be defined and annotated in the OData document.
As validations verify the state of an instance, state messages are used to return messages.
Note
For messages that reflect the state of a BO instance, state messages are used. They describe the state of
an instance or explain what can be done to achieve a better state. They are not used to describe failures of
modifications. State messages are stored on the database and can be retrieved together with the BO
instance they relate to. In draft scenarios, state messages are used to describe data inconsistencies. State
messages are stored in a state area. Every instance (no matter if the instance is active or draft) receives
a transactional key, with which the message is clearly identifiable for every instance. As state messages are
saved on the database, the application developer is responsible to clear the messages in the state area if
For messages that relate to a change of a BO instance, transition messages are used. Every modify request
is a change of state. If a modify requests fails, transition messages are used to describe and explain the
failure. Transition messages are not saved on a database. They disappear automatically once the failed
operation is rolled back.
For the travel demo scenario, define and implement validation for all three BO entities:
For the managed scenario with draft, define and implement validations on the travel entity for value
consistency checks.
Validation validateCustomer
Define the validation in the behavior definition and implement it in the behavior implementation class for Travel.
Definition
Define a validation on save with trigger operation create and trigger field CustomerID. Since there must
always be a customer assigned to a certain travel, define the field CustomerID as mandatory.
Sample Code
Via a quick fix, you can generate the method declaration in the behavior pool directly from the behavior
definition editor.
Implementation
Validate the customer ID by checking if the provided value is the ID of an entry in the customer database table.
Raise adequate messages for the consumer if the value is initial or not valid. The following steps guide you
through the implementation.
1. Read all the instances with the imported keys into an internal table lt_travel. This table is the basis to
check whether messages must be raised.
2. Pass the failed entries to the failed table of the validation.
3. Copy the entries to another internal table and delete all instances with an initial customer ID. If the
resulting internal table is not initial, select the entries of /DMO/Customer with the given customer ID. If
there is no corresponding entry in this database table, the provided customer ID is not valid.
4. Loop over the internal table lt_travel. To avoid duplicate state messages for the consumer, append an
empty message to the reported table to clear the state area.
5. If the customer ID is initial, write the transactional key to the failed table and append the corresponding
message to the state area VALIDATE_CUSTOMER. The RAP runtime framework provides the method new
message, with which you can easily retrieve messages from message classes provide the respective
parameters. The reported table includes the component %element. Here you can specify which CDS
The messages for the managed scenario with draft are stored in the message class /DMO/CM_FLIGHT_LEGAC
that is part of the ABAP Flight Reference Scenario.
Expand the following code sample to view the source code of Validation validateCustomer.
Sample Code
METHOD validateCustomer.
READ ENTITIES OF /DMO/I_Travel_D IN LOCAL MODE
ENTITY Travel
FIELDS ( CustomerID )
WITH CORRESPONDING #( keys )
RESULT DATA(lt_travel)
FAILED DATA(lt_failed).
failed = CORRESPONDING #( DEEP lt_failed ).
DATA lt_customer TYPE SORTED TABLE OF /dmo/customer WITH UNIQUE KEY
customer_id.
" Optimization of DB select: extract distinct non-initial customer IDs
lt_customer = CORRESPONDING #( lt_travel DISCARDING DUPLICATES MAPPING
customer_id = CustomerID EXCEPT * ).
DELETE lt_customer WHERE customer_id IS INITIAL.
IF lt_customer IS NOT INITIAL.
" Check if customer ID exists
SELECT FROM /dmo/customer FIELDS customer_id
FOR ALL ENTRIES IN @lt_customer
WHERE customer_id = @lt_customer-customer_id
INTO TABLE @DATA(lt_customer_db).
ENDIF.
" Raise message for non existing customer id
LOOP AT lt_travel INTO DATA(ls_travel).
APPEND VALUE #( %tky = ls_travel-%tky
%state_area = 'VALIDATE_CUSTOMER' ) TO
reported-travel.
IF ls_travel-CustomerID IS INITIAL.
APPEND VALUE #( %tky = ls_travel-%tky ) TO failed-travel.
APPEND VALUE #( %tky = ls_travel-%tky
%state_area = 'VALIDATE_CUSTOMER'
%msg = new_message( id = '/DMO/
CM_FLIGHT_LEGAC'
number = '044' "
Customer is initial
v1 =
ls_travel-TravelID
severity =
if_abap_behv_message=>severity-error )
%element-CustomerID = if_abap_behv=>mk-on ) TO
reported-travel.
ELSEIF ls_travel-CustomerID IS NOT INITIAL AND NOT
line_exists( lt_customer_db[ customer_id = ls_travel-CustomerID ] ).
APPEND VALUE #( %tky = ls_travel-%tky ) TO failed-travel.
APPEND VALUE #( %tky = ls_travel-%tky
%state_area = 'VALIDATE_CUSTOMER'
%msg = new_message( id = '/DMO/
CM_FLIGHT_LEGAC'
number = '002'
" Customer unknown
v1 =
ls_travel-CustomerID
severity =
if_abap_behv_message=>severity-error )
Validation validateAgency
Define the validation in the behavior definition and implement it in the behavior implementation class for Travel.
Definition
Define a validation on save with trigger operation create and trigger field AgencyID. Since there must always
be an agency assigned to a certain travel, define the field AgencyID as mandatory.
Sample Code
Via a quick fix, you can generate the method declaration in the behavior pool directly from the behavior
definition editor.
Implementation
Validate the agency ID by checking if the provided value is the ID of an entry in the agency database table. Raise
adequate messages for the consumer if the value is initial or not valid. The implementation steps are exactly
the same as for the validation ValidateCustomer.
Expand the following code sample to view the source code of Validation validateAgency.
Sample Code
METHOD validateAgency.
READ ENTITIES OF /DMO/I_Travel_D IN LOCAL MODE
ENTITY Travel
FIELDS ( AgencyID )
WITH CORRESPONDING #( keys )
RESULT DATA(lt_travel)
FAILED DATA(lt_failed).
failed = CORRESPONDING #( DEEP lt_failed ).
DATA lt_agency TYPE SORTED TABLE OF /dmo/agency WITH UNIQUE KEY agency_id.
" Optimization of DB select: extract distinct non-initial customer IDs
lt_agency = CORRESPONDING #( lt_travel DISCARDING DUPLICATES MAPPING
agency_id = AgencyID EXCEPT * ).
DELETE lt_agency WHERE agency_id IS INITIAL.
IF lt_agency IS NOT INITIAL.
" Check if customer ID exists
SELECT FROM /dmo/agency FIELDS agency_id
FOR ALL ENTRIES IN @lt_agency
WHERE agency_id = @lt_agency-agency_id
INTO TABLE @DATA(lt_agency_db).
Validation validateDates
Define the validation in the behavior definition and implement it in the behavior implementation class for Travel.
Definition
Define a validation on save with trigger operation create and trigger fields BeginDate and EndDate. Since
the travel dates are an essential part of the travel data, define the fields BeginDate and EndDate as
mandatory.
Sample Code
1. Read all the instances with the imported keys into an internal table lt_travel. This table is the basis to
check whether messages must be raised.
2. Pass the failed entries to the failed table of the validation.
3. Loop over the internal table lt_travel. To avoid duplicate state messages for the consumer, append an
empty message to the reported table to clear the state area.
4. The travel date must fulfill four conditions:
1. Begin date must not be initial.
2. End date must not be initial.
3. Begin date must not be before system date.
4. End date must not be before begin date.
For all of the situations, check with an if-loop and append the transactional key to the failed table. In
addition, append the corresponding message to the state area VALIDATE_DATES. The RAP runtime
framework provides the method new message, with which you can easily retrieve messages from
message classes provide the respective parameters. The reported table includes the component
%element. Here you can specify which CDS element is responsible for the state inconsistency. The Fiori
Elements UI, evaluates this component and highlights the corresponding input field.
Expand the following code sample to view the source code of Validation validateDates.
Sample Code
METHOD validateDates.
READ ENTITIES OF /DMO/I_Travel_D IN LOCAL MODE
ENTITY Travel
FIELDS ( BeginDate EndDate )
WITH CORRESPONDING #( keys )
RESULT DATA(lt_travel)
FAILED DATA(lt_failed).
failed = CORRESPONDING #( DEEP lt_failed ).
LOOP AT lt_travel INTO DATA(ls_travel).
APPEND VALUE #( %tky = ls_travel-%tky
%state_area = 'VALIDATE_DATES' ) TO reported-
travel.
IF ls_travel-BeginDate IS INITIAL.
APPEND VALUE #( %tky = ls_travel-%tky ) TO failed-travel.
APPEND VALUE #( %tky = ls_travel-%tky
%state_area = 'VALIDATE_DATES'
%msg = new_message( id = '/DMO/
CM_FLIGHT_LEGAC'
number = '013' "
Enter Begin Date for travel
v1 =
ls_travel-TravelID
severity =
if_abap_behv_message=>severity-error )
%element-BeginDate = if_abap_behv=>mk-on ) TO
reported-travel.
ENDIF.
IF ls_travel-EndDate IS INITIAL.
APPEND VALUE #( %tky = ls_travel-%tky ) TO failed-travel.
APPEND VALUE #( %tky = ls_travel-%tky
For the managed scenario with draft, define and implement validations on the booking entity for value
consistency checks.
Validation validateCustomer
Define the validation in the behavior definition and implement it in the behavior implementation class for
Booking.
Definition
Define a validation on save with trigger operation create and trigger field CustomerID. Since there must
always be a customer assigned to a certain travel, define the field CustomerID is mandatory.
Sample Code
Via a quick fix, you can generate the method declaration in the behavior pool directly from the behavior
definition editor.
Implementation
The validation for the customer ID field is done similarly to the validation for the customer ID in the travel entity.
See Validation validateCustomer [page 440].
To retrieve the correct messages of validations in child entities and to display them on the UI, you have to
define a path expression to the root entity. To fill the path expression with the correct keys, you have to read the
keys of the parent entity first. This is done by a read by association. You do not need the full result of the read
by association, but just the link table, which contains the key mappings of source and target entity.
Expand the following code sample to view the source code of Validation validateCustomer.
Sample Code
METHOD validateCustomer.
READ ENTITIES OF /DMO/I_Travel_D IN LOCAL MODE
ENTITY Booking
FIELDS ( CustomerID )
WITH CORRESPONDING #( keys )
RESULT DATA(lt_booking)
FAILED DATA(lt_failed).
failed = CORRESPONDING #( DEEP lt_failed ).
READ ENTITIES OF /DMO/I_Travel_D IN LOCAL MODE
ENTITY Booking BY \_Travel
FROM CORRESPONDING #( lt_booking )
LINK DATA(lt_link).
DATA lt_customer TYPE SORTED TABLE OF /dmo/customer WITH UNIQUE KEY
customer_id.
Validation validateConnection
Define the validation in the behavior definition and implement it in the behavior implementation class for
Booking.
Sample Code
Via a quick fix, you can generate the method declaration in the behavior pool directly from the behavior
definition editor.
Implementation
Validate the flight-related fields by checking if the provided values match an entry in the master database
table /dmo/flight. Raise adequate messages for the consumer if the values are initial or not valid. The
following steps guide you through the implementation.
1. Read all the instances with the imported keys into an internal table lt_booking. This table is the basis to
check whether messages must be raised.
2. Read the corresponding travel instances via a read by association. The parent keys are needed for the path
expression %path in the reported table. It represents the path the path to the root entity and is important
to display the messages correctly on the UI.
3. Loop over the internal table lt_booking. To avoid duplicate state messages for the consumer, append an
empty message to the reported table to clear the state area.
4. If the airline ID is initial, write the transactional key to the failed table and append the corresponding
message to the state area VALIDATE_CONNECTION.
The RAP runtime framework provides the method new message, with which you can easily retrieve
messages from message classes and provide the respective parameters.
The reported table contains the component %path, in which you have to maintain the keys of all parent
instances, including the root entity. Use the key mappings in the link table to fill the path expression. Only if
all parent keys are maintained is the message displayed correctly on the UI.
The reported table includes the component %element. Here you can specify which CDS element is
responsible for the state inconsistency. The Fiori Elements UI, evaluates this component and highlights the
corresponding input field.
5. Repeat the step for the values in ConnectionID and FlightDate. Like AirlineID, they must not be
initial.
6. If all related fields are not empty you can check if the values match an entry in the flight master data table.
If the select finds a corresponding row (=sy-subr <> 0), write the transactional key to the failed table
and append the corresponding message to the state area VALIDATE_CONNECTION.
Expand the following code sample to view the source code of Validation validateConnection.
Sample Code
METHOD validateConnection.
For the managed scenario with draft, define and implement validations on the booking supplement entity for
value consistency checks.
Validation validateSupplement
Define the validation in the behavior definition and implement it in the behavior implementation class for
BookingSupplement.
Sample Code
Via a quick fix, you can generate the method declaration in the behavior pool directly from the behavior
definition editor.
Implementation
Validate the supplement ID by checking if the provided value is an entry in the supplement database table.
Raise adequate messages for the consumer if the value is initial or not valid. The implementation steps are
exactly the same as for the validation ValidateCustomer. For a detailed step-by-step description, see
Validation validateCustomer [page 446].
To fill the %path component in the reported table, you have to provide the keys of all parent instances.
Therefore you have to execute a ready by association to every entity in the business object structure. In this
case, to Booking and to BookingSupplement.
Expand the following code sample to view the source code of Validation validateSupplement.
Sample Code
METHOD validateSupplement.
READ ENTITIES OF /DMO/I_Travel_D IN LOCAL MODE
ENTITY BookingSupplement
FIELDS ( SupplementID )
WITH CORRESPONDING #( keys )
RESULT DATA(lt_booksuppl)
FAILED DATA(lt_failed).
failed = CORRESPONDING #( DEEP lt_failed ).
READ ENTITIES OF /dmo/I_Travel_D IN LOCAL MODE
ENTITY BookingSupplement BY \_Booking
FROM CORRESPONDING #( lt_booksuppl )
LINK DATA(lt_link_booking).
READ ENTITIES OF /dmo/I_Travel_D IN LOCAL MODE
ENTITY BookingSupplement BY \_Travel
FROM CORRESPONDING #( lt_booksuppl )
LINK DATA(lt_link_travel).
DATA lt_supplement TYPE SORTED TABLE OF /dmo/supplement WITH UNIQUE KEY
supplement_id.
" Optimization of DB select: extract distinct non-initial customer IDs
lt_supplement = CORRESPONDING #( lt_booksuppl DISCARDING DUPLICATES
MAPPING supplement_id = SupplementID EXCEPT * ).
DELETE lt_supplement WHERE supplement_id IS INITIAL.
IF lt_supplement IS NOT INITIAL.
" Check if customer ID exists
SELECT FROM /dmo/supplement FIELDS supplement_id
FOR ALL ENTRIES IN @lt_supplement
WHERE supplement_id = @lt_supplement-
supplement_id
For the managed scenario with draft, enable and disable functionality with feature control.
Feature control is used to define the availability of behavior components, like fields, actions, and operations.
You can define if these components are always available for the client or only under certain circumstances
(dynamic feature control). For more detailed information, see Feature Control [page 85].
For this demo scenario, define and implement field, operation and action control.
Definition
● Define field control.
○ field (readonly)
Fields that are filled by the RAP managed runtime framework (internally) are set to read only as the
consumer is not allowed to change these. Marking these fields as read only in the behavior definition
makes also has an effect on the UI. The end user cannot provide values for these fields. In the travel
scenario, the fields that are set to read only are the administrative fields and fields that are filled
internally, for example by determinations. These fields are:
○ On the travel entity:
TravelID, OverallStatus, TotalPriceLocalCreatedAt, LocalCreatedBy,
LocalLastChangedAt, LocalLastChangedBy
○ On the booking entity:
TravelUUID, BookingID, BookingDate, LocalLastChangedAt
○ On the booking supplement entity:
TravelUUID, BookingUUID, BookingSupplementID, LocalLastChangedAt
The key fields whose values are set by the RAP managed runtime framework via managed numbering
must also be set to read only. They also receive the attribute numbering: managed. These fields are:
○ On the travel entity: TravelUUID
○ On the booking entity: BookingUUID
○ On the booking supplement entity: BookSupplUUID
○ field (mandatory)
Fields whose values are checked by validations are set to mandatory. On a Fiori Elements UI, this
provokes a consumer hint on the respective input fields. These fields are:
○ On the travel entity:
CustomerID, AgencyID, BeginDate, EndDate
○ On the booking entity:
CustomerID, AirlineID, ConnectionID, FlightDate
○ On the booking supplement entity:
SupplementID
○ field (feature: instance)
Fields whose logic depends on other circumstances are controlled by dynamic feature control. In this
scenario, the BookingFee (on the travel entity) is set to read only if the instance's status is Accepted
(A). The feature control condition must be implemented in the behavior class.
Define the static and dynamic feature control in the behavior definition and implement dynamic feature control
in the behavior implementation class for Travel.
Definition
Define field, action, and operation control. For dynamic feature control, add (feature: instance) to the
feature control definition.
Sample Code
Via a quick fix, you can generate the method declaration for the feature control implementation in the behavior
pool directly from the behavior definition editor.
1. Read all the instances with the imported keys into an internal table lt_travel. The fields that are
relevant to decide whether features are available must be included in the read result. For the travel entity,
only the field OverallStatus is relevant.
2. Fill the result table of the method with the respective feature. Use the transaction key %tky to identify the
travel instance. The importing parameter keys contains the %field, %action, %assoc component to
determine what kind of feature is controlled. Use a condition statement to define the circumstance and the
outcome of each dynamically controlled feature. You can work with the constants, defined in the private
section of the behavior class, to refer to the values of the overall status.
Expand the following code sample to view the source code of GET_FEATURES.
Sample Code
METHOD get_features.
READ ENTITIES OF /DMO/I_Travel_D IN LOCAL MODE
ENTITY Travel
FIELDS ( OverallStatus )
WITH CORRESPONDING #( keys )
RESULT DATA(lt_travel)
FAILED failed.
result = VALUE #( FOR ls_travel IN lt_travel
( %tky = ls_travel-%tky
%field-BookingFee = COND #( WHEN ls_travel-
OverallStatus = travel_status-accepted
THEN
if_abap_behv=>fc-f-read_only
ELSE
if_abap_behv=>fc-f-unrestricted )
%action-acceptTravel = COND #( WHEN ls_travel-
OverallStatus = travel_status-accepted
THEN
if_abap_behv=>fc-o-disabled
ELSE
if_abap_behv=>fc-o-enabled )
%action-rejectTravel = COND #( WHEN ls_travel-
OverallStatus = travel_status-rejected
THEN
if_abap_behv=>fc-o-disabled
ELSE
if_abap_behv=>fc-o-enabled )
%action-deductDiscount = COND #( WHEN ls_travel-
OverallStatus = travel_status-accepted
THEN
if_abap_behv=>fc-o-disabled
ELSE
if_abap_behv=>fc-o-enabled )
%assoc-_Booking = COND #( WHEN ls_travel-
OverallStatus = travel_status-rejected
THEN
if_abap_behv=>fc-o-disabled
ELSE
if_abap_behv=>fc-o-enabled )
) ).
ENDMETHOD.
For the managed scenario with draft, enable and disable functionality with feature control on the booking
entity.
Define static feature control in the behavior definition. For the Booking entity, there is no dynamic feature
control.
Definition
Define field control.
Sample Code
Via a quick fix, you can generate the method declaration for the feature control implementation in the behavior
pool directly from the behavior definition editor.
1. Define constants that are available for the booking status in the private section of the local types.
Sample Code
2. Read all the instances with the imported keys into an internal table lt_booking. The fields that are
relevant to decide whether features are available must be included in the read result. For the travel entity,
only the field BookingStatus is relevant.
3. Fill the result table of the method with the respective feature. Use the transaction key %tky to identify the
travel instance. The importing parameter keys contains the %assoc component to determine what kind of
feature is controlled. Use a condition statement to define the circumstance and the outcome of each
dynamically controlled features. You can work with the constants, defined in the private section of the
behavior class, to refer to the values of the overall status.
Expand the following code sample to view the source code of GET_FEATURES..
METHOD get_features.
READ ENTITIES OF /DMO/I_Travel_D IN LOCAL MODE
ENTITY Booking
FIELDS ( BookingStatus )
WITH CORRESPONDING #( keys )
RESULT DATA(lt_booking)
FAILED failed.
result = VALUE #( FOR ls_booking IN lt_booking
( %tky = ls_booking-%tky
%assoc-_BookingSupplement = COND #( WHEN ls_booking-
BookingStatus = booking_status-booked
THEN
if_abap_behv=>fc-o-disabled
ELSE
if_abap_behv=>fc-o-enabled ) ) ).
ENDMETHOD.
For the managed scenario with draft, enable and disable functionality with feature control on the booking
supplement entity.
Define the static feature control in the behavior definition. For the BookingSupplement, there is no dynamic
feature control.
Definition
Define field control.
Sample Code
In the previous chapters, you have created a managed business object from scratch. To expose it as a business
service the following steps are relevant:
For the managed scenario with draft, create a projection layer for the managed BO.
Whereas the business object that we have created so far is service agnostic, the business object projection is
an approach to define a subset of this business object as service-specific for the UI use case. In this scenario,
we want to publish a UI service, so UI-specifics are defined in the BO projection. For more information about
the BO projection layer, see Business Object Projection [page 140].
For the travel demo scenario, define the data model and the behavior projection:
● For the data model projection, the following steps are relevant:
○ Create CDS projection views.
In the CDS projection views define UI-service-specific features, such as search capabilities, text
associations, and value helps.
○ Create metadata extensions.
Metadata extensions are primarily used to define the annotations that are relevant for the UI design.
They are outsourced in metadata extension to obtain an easy approach for layering.
For a detailed description on how to define data model projection features, see Projecting the BO Data
Model [page 458].
● For the behavior projection, the following steps are relevant:
○ Create a projection behavior definition.
For a detailed description on behavior projection features, see Projecting the BO Behavior [page 466].
For the managed scenario with draft, create and implement CDS projection views and metadata extensions for
the BO entities.
Create data definition for projection views for all three BO entities Travel, Booking, and Booking Supplement. In
ADT, use the context menu on the underlying CDS view entity to open the wizard for creating CDS data
definition. For a detailed step-by-step description, see .
The draft scenario in the ABAP Flight Reference Scenario uses the suffix _D. Since the CDS projection views
only represent the active UI service, we use the marker _A before the suffix to distinguish the active
projection layer from the projection layer that includes draft. In addition, projection views carry the prefix
C_ as they represent the consumption layer. For more information, see Naming Conventions for
Development Objects [page 918].
For each projection view, create a metadata extension. In ADT, right-click on the data definition to open the
creation wizard for metadata extensions. For more information, see .
Note
According to the virtual data model (VDM) guidelines, the metadata extension use the same name as the
related CDS view.
The projection view is a projection on /DMO/I_Travel_D. Include the keyword root in the projection view.
Use all elements of the underlying BO travel entity. You do not need to use the administrative data in the
projection view, except for the element representing the ETag master.
Enable search capabilities for the travel entity, with the @Search annotations on the entity header and on
designated elements. For more information, see Enabling Text and Fuzzy Searches in SAP Fiori Apps [page
561].
To retrieve the long texts of the ID elements CustomerID and AgencyID, denormalize the text elements from
the associated text provider view into the projection view. For more information, see Getting Language-
Dependent Text in Projection Views [page 548].
Define value helps for the elements CustomerID, AgencyID and CurrencyCode. For more information, see
Providing Value Help [page 550].
@Metadata.layer: #COREAs soon as all projection views of the composition structure of the business object
are available, redirect the composition to child to the projection view of the booking entity.
Expand the following code sample to view the source code of Projection View /DMO/C_Travel_A_D.
Sample Code
Define the metadata extension as extension of the CDS projection view /DMO/C_Travel_A_D.
Use all the @UI annotations to design the user interface for the travel app. For more information, see Defining
CDS Annotations for Metadata-Driven UIs [page 629].
To trigger actions via action buttons on the UI, use @UI annotations for data action on both the list report page
and the object page. It is not important which element is annotated with this annotation, the action button
always appears on the upper right corner on the UI.
Expand the following code sample to view the source code of Metadata Extension /DMO/C_Travel_A_D.
Sample Code
@Metadata.layer: #CORE
@UI: { headerInfo: { typeName: 'Travel',
typeNamePlural: 'Travels',
title: { type: #STANDARD, value: 'TravelID' } },
presentationVariant: [{ sortOrder: [{ by: 'TravelID', direction:
#DESC }] }] }
// CurrencyCode;
@UI.hidden: true
LocalLastChangedAt;
Use all elements of the underlying BO travel entity, in particular the element representing the ETag master.
Enable search capabilities for the booking entity, with the @Search annotations. For more information, see
Enabling Text and Fuzzy Searches in SAP Fiori Apps [page 561].
To retrieve the long texts of the ID elements CustomerID and CarrierID, denormalize the text elements from
the associated text provider views into the projection view. For more information, see Getting Language-
Dependent Text in Projection Views [page 548].
Define value helps for the elements CustomerID, AirlineID, ConnectionID, FlightDate, and
CurrencyCode. For more information, see Providing Value Help [page 550].
As soon as all projection views of the composition structure of the business object are available, redirect the
composition to child to the projection view of the booking entity and the association to the root entity to the
respective projection view.
Expand the following code sample to view the source code of Projection View /DMO/C_Booking_A_D.
Sample Code
Define the metadata extension as extension of the CDS projection view /DMO/C_Booking_A_D.
Use all the @UI annotations to design the user interface for the travel app. For more information, see Defining
CDS Annotations for Metadata-Driven UIs [page 629].
Expand the following code sample to view the source code of Metadata Extension /DMO/C_Booking_A_D.
Sample Code
@Metadata.layer: #CORE
@UI: { headerInfo: { typeName: 'Booking',
typeNamePlural: 'Bookings',
title: { type: #STANDARD, value: 'BookingID' } } }
annotate entity /DMO/C_Booking_A_D with
{
@UI.facet: [ { id: 'Booking',
purpose: #STANDARD,
type: #IDENTIFICATION_REFERENCE,
label: 'Booking',
position: 10 },
{ id: 'BookingSupplement',
purpose: #STANDARD,
type: #LINEITEM_REFERENCE,
label: 'Booking Supplement',
position: 20,
targetElement: '_BookingSupplement'} ]
@UI.hidden: true
BookingUUID;
@UI.hidden: true
TravelUUID;
// CurrencyCode;
@UI.hidden: true
LocalLastChangedAt;
Use all elements of the underlying BO travel entity, in particular the element representing the ETag master.
Enable search capabilities for the travel entity, with the @Search annotations. For more information, see
Enabling Text and Fuzzy Searches in SAP Fiori Apps [page 561].
To retrieve the long texts of the ID element SupplementID, denormalize the text element from the associated
text provider view into the projection view. For more information, see Getting Language-Dependent Text in
Projection Views [page 548].
Define value helps for the elements SupplementID and CurrencyCode. For more information, see Providing
Value Help [page 550].
As soon as all projection views of the composition structure of the business object are available, redirect the
association to the parent to the projection view of the booking entity and the association to the root entity to
the respective projection view.
Expand the following code sample to view the source code of Projection View /DMO/
C_BookingSupplement_A_D.
Define the metadata extension as extension of the CDS projection view /DMO/C_BookingSupplement_A_D.
Use all the @UI annotations to design the user interface for the travel app. For more information, see Defining
CDS Annotations for Metadata-Driven UIs [page 629].
Expand the following code sample to view the source code of Metadata Extension /DMO/
C_BookingSupplement_A_D.
Sample Code
@Metadata.layer: #CORE
@UI: { headerInfo: { typeName: 'Booking Supplement',
typeNamePlural: 'Booking Supplements',
title: { type: #STANDARD,
label: 'Booking Supplement',
value: 'BookingSupplementID' } } }
@UI.hidden: true
LocalLastChangedAt;
}
For the managed scenario with draft, create a projection behavior definition for defining the projection
behavior.
Create a projection behavior definition for the travel business object. In ADT, use the context menu of the root
projection view to open the wizard for creating behavior definitions. The implementation type is prefilled with
Projection. For a more detailed description, see Working with Behavior Definitions [page 896].
Use the complete behavior that was defined in the underlying behavior definition for the non-draft BO
projection.
Expand the following code sample to view the source code of Projection Behavior Definition /DMO/
C_Travel_A_D.
Sample Code
projection;
define behavior for /DMO/C_Travel_A_D alias Travel
use etag
For the managed scenario with draft, define a UI service and bind it to a protocol.
To address the managed BO from the ABAP-external world, it must be exposed as a business service to OData.
In the service definition, you define which artifacts are included in the service. The service binding defines the
protocol with which the service is addressed. In addition, it provides the means to activate the service locally.
The Fiori Elements App Preview can be used as soon as the service is activated.
Create a service definition for the non-draft business service. For more information, see Creating Service
Definitions [page 903].
Based on the service definition, create a service binding. For more information, see Creating Service Binding
[page 904]. You can choose between OData V2 and V4 protocol to expose a UI service.
Note
The draft scenario in the ABAP Flight Reference Scenario uses the suffix _D. To distinguish the active-only
service from the service with draft, we use the addition _A before the suffix. For more information, see
Naming Conventions for Development Objects [page 918].
Include the projection views of the business object and all related CDS entities that are relevant for UI
consumption into the service definition.
Expand the following code sample to view the source code of Service Definition /DMO/UI_TRAVEL_A_D.
Sample Code
Expose the service as a UI service with OData protocol V2 or V4. And publish it locally. You can then use the
preview to test the service.
You can now draft-enable the managed business object with a few additions in the behavior definition.
In draft business objects, the implementation of the business logic must be able to handle request for draft and
for active instances. Above all, the implementation must be able to differentiate those requests.
The business object receives the information if a request is aimed at draft or active instances via the draft
indicator %IS_DRAFT. This draft indicator is a component of the primary key in all incoming requests in draft
scenarios. The transactional key %tky includes the draft indicator. So in draft scenarios, it is best practice to
use %tky for referring to the primary key, as the indicator for drafts or non-drafts is already included there. In
non-draft scenarios, the transactional key %tky comprises just the primary key information.
If you consequently use the derived type %tky to refer to the key of instances in your implementation, draft-
enabling is just done in the behavior definition with a few additional syntax elements. The draft indicator
%is_draft is added as a key component to %tky in draft scenarios. This means, the implementation receives
the information if the request is aimed at draft or active instances. So, you do not have to adjust your
implementation in draft scenarios.
For the managed scenario with draft, add draft behavior to the behavior definition.
In the previous sections of this development guide, you have developed a running business service with a
managed business object. Draft capabilities are included to the business service by adding the syntax element
with draft to the behavior definition. As soon as you use this keyword element in the behavior definition, you
are guided through the follow-up steps in the behavior definition by tool support. Syntax warnings and quick
fixes are available to adapt the managed business object to the draft prerequisites.
Note
Business objects with implementation type unmanaged are draft-enabled in the same way.
1. Add the syntax element with draft to the header of the behavior definition.
Note
You receive a syntax error that there is no draft persistence available for the draft BO.
2. Create a draft database table for the Travel entity by using the quick fix in the behavior definition:
○ Define the draft database table in the behavior definition by using the keyword draft table and
specify the name for the draft database table.
In the example scenario, we use /dmo/d_travel_d.
Note
The draft scenario in the ABAP Flight Reference Scenario uses the suffix _D. For the draft database
tables, we use the prefix D_ to indicate the draft persistence. For detailed information, see Naming
Conventions for Development Objects [page 918].
The wizard for creating database tables opens. When you finish the database table generation wizard,
the ADT tooling support automatically generates the table elements corresponding to the active
persistence. In addition, the draft admin structure include %admin is included in the database table.
For more information about the draft database table, see Draft Database Table [page 64].
○ Add the annotation @AbapCatalog.anonymizedWhenDelivered : true to the elements that
store user information. (Only relevant for the travel draft table.)
Expand the following code sample to view the source code of Database Table /dmo/d_travel_d.
Sample Code
3. Repeat the creation of draft database tables for the Booking entity and the BookingSupplement entity.
In the example scenario, we use the names /dmo/d_booking_d and /dmo/d_bksuppl_d.
4. Define a total ETag field for the draft business object.
The total ETag is used to enable optimistic concurrency checks during the transition from draft to active
data.
○ Add a total ETag field to the persistent database table of the Travel entity.
Expand the following code sample to view the source code of Database Table /dmo/a_travel_d.
○ Add the element in the CDS view entity /DMO/I_TRAVEL_D and annotate it with the annotation
@Semantics.systemDateTime.lastChangedAt: true to allow the RAP runtime framework to
update it automatically.
Expand the following code sample to view the source code of Database Table /DMO/I_Travel_D.
Sample Code
Note
The total ETag field does not need to be included in the projection view. The ETag is only used for
internal processing of draft data and does not need to be exposed for the business service.
○ Add the total ETag field to the mapping prescription in the behavior definition for the Travel entity.
Expand the following code sample to view the source code of Database Table /DMO/I_Travel_D.
Sample Code
○ Regenerate the draft database table /dmo/d_travel_d to include the total ETag field. The behavior
definition provides a quick fix for the regeneration.
The definition of the total ETag is only possible after the lock master definition in the behavior
definition.
Sample Code
managed;
with draft;
define behavior for /DMO/I_Travel_D alias Travel
implementation in class /dmo/bp_i_travel_d unique
persistent table /dmo/a_travel_d
draft table /dmo/d_travel_d
lock master
total etag LastChangedAt
...
{
}
Note
It is not generally necessary to include all validations in the Prepare action, but only those that
you want to check during the transition from draft to active.
Sample Code
to themanaged;
with draft;
define behavior for /DMO/I_Travel_D alias Travel
...
draft determine action Prepare {
validation validateAgency;
validation validateCustomer;
validation validateDates;
validation Booking~validateCustomer;
validation Booking~validateConnection;
validation Bookingsupplement~validateSupplement;}
Expand the following code sample to view the source code of Behavior Definition/DMO/I_Travel_D.
managed;
with draft;
define behavior for /DMO/I_Travel_D alias Travel
implementation in class /dmo/bp_i_travel_d unique
persistent table /dmo/a_travel_d
draft table /dmo/d_travel_d
lock master
total etag LastChangedAt
//authorization master ( instance )
etag master LocalLastChangedAt
{
create;
update;
delete;
association _Booking { create ( features : instance ); with draft; }
field ( numbering : managed, readonly ) TravelUUID;
field ( readonly ) TravelID, OverallStatus, TotalPrice, LocalCreatedAt,
LocalCreatedBy, LocalLastChangedAt, LocalLastChangedBy;
field ( mandatory ) CustomerID, AgencyID, BeginDate, EndDate;
field ( features : instance ) BookingFee;
action ( features : instance ) acceptTravel result [1] $self;
action ( features : instance ) rejectTravel result [1] $self;
action deductDiscount parameter /dmo/a_travel_discount result [1] $self;
internal action reCalcTotalPrice;
determination setTravelNumber on modify { create; }
determination setStatusToNew on modify { create; }
determination calculateTotalPrice on modify { create; field BookingFee,
CurrencyCode; }
validation validateCustomer on save { create; field CustomerID; }
validation validateAgency on save { create; field AgencyID; }
validation validateDates on save { create; field BeginDate, EndDate; }
mapping for /dmo/a_travel_d
{ AgencyID = agency_id;
BeginDate = begin_date;
BookingFee = booking_fee;
CurrencyCode = currency_code;
CustomerID = customer_id;
Description = description;
EndDate = end_date;
LocalCreatedAt = local_created_at;
LocalCreatedBy = local_created_by;
LocalLastChangedAt = local_last_changed_at;
LocalLastChangedBy = local_last_changed_by;
OverallStatus = overall_status;
TotalPrice = total_price;
TravelID = travel_id;
TravelUUID = travel_uuid; }
}
define behavior for /DMO/I_Booking_D alias Booking
implementation in class /dmo/bp_i_booking_d unique
persistent table /dmo/a_booking_d
draft table /dmo/d_booking_d
lock dependent by _Travel
//authorization dependent by <association>
etag master LocalLastChangedAt
{
update;
delete;
association _BookingSupplement { create ( features : instance ); with
draft; }
association _Travel { with draft; }
field ( numbering : managed, readonly ) BookingUUID;
field ( readonly ) TravelUUID, BookingID, BookingDate, LocalLastChangedAt;
field ( mandatory ) CustomerID, AirlineID, ConnectionID, FlightDate;
determination setBookingNumber on modify { create; }
Note
Draft-enabled business objects require a feature control method in the behavior pool. If you do not use
dynamic feature control in your business logic, include the method declaration and an empty method
implementation. In Draft BOs, feature control is called for every request to determine whether draft or
active instances are requested.
Result
As soon as you activate the behavior definition, the business object is draft-enabled. In general, that means:
The conversion to a draft business object sometimes requires follow-up activities of existing implementations
in the business logic. In general, scan the existing code thoroughly to detect cases where you need to
differentiate between active and draft processing.
If the transactional key %tky is used for referring to the key components, no adaptation is necessary.
In the previous chapters, you have created a draft business object by draft-enabling a managed business
object. Since the managed business object projection without draft capabilities remains untouched, it
continues to operate only on active instances.
If you want to have a business service with draft capabilities, you can either add the draft features to the
existing business object projection or create a new projection for the draft service. This is done in this demo
scenario. In the end, you have two parallel business services, one for active only and one that includes draft
features. To expose the draft business as a business service the following steps are relevant:
For the managed scenario with draft, create a projection layer for the draft BO.
In the first part of this development guide, you have created a non-draft business service based on a managed
business object. The business object projection includes all elements in the data model and the behavior
features that are relevant for the UI service. This projection remains stable, even when you add draft
capabilities to the basic business object. The projection layer filters out the elements and behavior features
that are draft-specific as they are not included in the projection.
For the travel demo scenario, define the data model and the behavior projection for the draft projection:
● For the data model projection, the following steps are relevant:
○ Create CDS projection views.
In the CDS projection views define UI-service-specific features, such as search capabilities, text
associations, and value helps.
○ Create metadata extensions.
Metadata extensions are primarily used to define the annotations that are relevant for the UI design.
They are outsourced in metadata extension to obtain an easy approach for layering.
For a detailed description on how to define data model projection features, see Projecting the BO Data
Model [page 458].
● For the behavior projection, the following steps are relevant:
○ Create a projection behavior definition.
For a detailed description on behavior projection features, see Projecting the BO Behavior [page 466].
For the managed scenario with draft, create and implement CDS projection views and metadata extensions for
the draft BO entities.
Create data definition for projection views for all three BO entities Travel, Booking, and Booking Supplement. In
ADT, use the context menu on the underlying CDS view entity to open the wizard for creating CDS data
definition. For a detailed step-by-step description, see .
Note
The draft scenario in the ABAP Flight Reference Scenario uses the suffix _D. Since the new CDS projection
views represent the UI service with draft capabilities, we use the marker _D before the suffix to distinguish
the active projection layer from the projection layer that includes draft. In addition, projection views carry
the prefix C_ as they represent the consumption layer. For more information, see Naming Conventions for
Development Objects [page 918].
For each projection view, create a metadata extension. In ADT, right-click on the data definition to open the
creation wizard for metadata extensions.
Note
According to the virtual data model (VDM) guidelines, the metadata extension use the same name as the
related CDS view.
The projection view is a projection on /DMO/I_Travel_D. Include the keyword root in the projection view.
Use all elements of the underlying BO travel entity. You do not need to use the administrative data in the
projection view, except for the element representing the ETag master.
Enable search capabilities for the travel entity, with the @Search annotations on the entity header and on
designated elements. For more information, see Enabling Text and Fuzzy Searches in SAP Fiori Apps [page
561].
To retrieve the long texts of the ID elements CustomerID and AgencyID, denormalize the text elements from
the associated text provider view into the projection view. For more information, see Getting Language-
Dependent Text in Projection Views [page 548].
Define value helps for the elements CustomerID, AgencyID and CurrencyCode. For more information, see
Providing Value Help [page 550].
As soon as all projection views of the composition structure of the business object are available, redirect the
composition to child to the projection view of the booking entity.
Expand the following code sample to view the source code of Projection View /DMO/C_Travel_D_D.
Sample Code
Define the metadata extension as extension of the CDS projection view /DMO/C_Travel_A_D.
Use all the @UI annotations to design the user interface for the travel app. For more information, see Defining
CDS Annotations for Metadata-Driven UIs [page 629].
Expand the following code sample to view the source code of Metadata Extension /DMO/C_Travel_D_D.
@Metadata.layer: #CORE
@UI: { headerInfo: { typeName: 'Travel',
typeNamePlural: 'Travels',
title: { type: #STANDARD, value: 'TravelID' } },
presentationVariant: [{ sortOrder: [{ by: 'TravelID', direction:
#DESC }] }] }
// CurrencyCode;
@UI.hidden: true
LocalLastChangedAt;
Use all elements of the underlying BO travel entity, in particular the element representing the ETag master.
Enable search capabilities for the booking entity, with the @Search annotations. For more information, see
Enabling Text and Fuzzy Searches in SAP Fiori Apps [page 561].
To retrieve the long texts of the ID elements CustomerID and CarrierID, denormalize the text elements from
the associated text provider views into the projection view. For more information, see Getting Language-
Dependent Text in Projection Views [page 548].
Define value helps for the elements CustomerID, AirlineID, ConnectionID, FlightDate, and
CurrencyCode. For more information, see Providing Value Help [page 550].
As soon as all projection views of the composition structure of the business object are available, redirect the
composition to child to the projection view of the booking entity and the association to the root entity to the
respective projection view.
Expand the following code sample to view the source code of Projection View /DMO/C_Booking_D_D.
Define the metadata extension as extension of the CDS projection view /DMO/C_Booking_D_D.
Use all the @UI annotations to design the user interface for the travel app. For more information, see Defining
CDS Annotations for Metadata-Driven UIs [page 629].
Expand the following code sample to view the source code of Metadata Extension View /DMO/C_Booking_D_D.
Sample Code
@Metadata.layer: #CORE
@UI: { headerInfo: { typeName: 'Booking',
typeNamePlural: 'Bookings',
title: { type: #STANDARD, value: 'BookingID' } } }
annotate entity /DMO/C_Booking_D_D with
{
@UI.facet: [ { id: 'Booking',
purpose: #STANDARD,
type: #IDENTIFICATION_REFERENCE,
label: 'Booking',
position: 10 },
{ id: 'BookingSupplement',
purpose: #STANDARD,
type: #LINEITEM_REFERENCE,
label: 'Booking Supplement',
position: 20,
targetElement: '_BookingSupplement'} ]
@UI.hidden: true
BookingUUID;
@UI.hidden: true
TravelUUID;
// CurrencyCode;
Use all elements of the underlying BO travel entity, in particular the element representing the ETag master.
Enable search capabilities for the travel entity, with the @Search annotations. For more information, see
Enabling Text and Fuzzy Searches in SAP Fiori Apps [page 561].
To retrieve the long texts of the ID element SupplementID, denormalize the text element from the associated
text provider view into the projection view. For more information, see Getting Language-Dependent Text in
Projection Views [page 548].
Define value helps for the elements SupplementID and CurrencyCode. For more information, see Providing
Value Help [page 550].
As soon as all projection views of the composition structure of the business object are available, redirect the
association to the parent to the projection view of the booking entity and the association to the root entity to
the respective projection view.
Expand the following code sample to view the source code of Projection View /DMO/
C_BookingSupplement_D_D.
Sample Code
Define the metadata extension as extension of the CDS projection view /DMO/C_BookSuppl_D_D.
Use all the @UI annotations to design the user interface for the travel app. For more information, see Defining
CDS Annotations for Metadata-Driven UIs [page 629].
Expand the following code sample to view the source code of Projection View /DMO/
C_BookingSupplement_D_D.
Sample Code
@Metadata.layer: #CORE
@UI: { headerInfo: { typeName: 'Booking Supplement',
typeNamePlural: 'Booking Supplements',
title: { type: #STANDARD,
label: 'Booking Supplement',
value: 'BookingSupplementID' } } }
@UI.hidden: true
LocalLastChangedAt;
}
For the managed scenario with draft, create a projection behavior definition for defining the projection
behavior.
Create a projection behavior definition for the travel business object. In ADT, use the context menu of the root
projection view to open the wizard for creating behavior definitions. The implementation type is prefilled with
Projection. For a more detailed description, see Working with Behavior Definitions [page 896].
Use the complete behavior that was defined in the underlying behavior definition for the draft BO projection.
(The template does not include everything, some features must be added manually.)
Note
Fiori Elements does not support ETag handling (OData ETag) in draft scenarios with OData V2. For the
projection with draft, the ETag is not used.
Expand the following code sample to view the source code of Projection View /DMO/
C_BookingSupplement_D_D.
Sample Code
projection;
use draft;
define behavior for /DMO/C_TRAVEL_D_D alias Travel
//use etag
{
use create;
use update;
use delete;
use action acceptTravel;
use action rejectTravel;
use action deductDiscount;
use association _Booking { create; with draft; }
}
define behavior for /DMO/C_BOOKING_D_D alias Booking
//use etag
{
use update;
use delete;
use association _BookingSupplement { create; with draft; }
use association _Travel { with draft; }
}
define behavior for /DMO/C_BOOKINGSUPPLEMENT_D_D alias BookingSupplement
//use etag
{
use update;
use delete;
For the managed scenario with draft, define a UI service and bind it to a protocol.
To address the draft BO from the ABAP-external world, it must be exposed as a business service to OData. In
the service definition, you define which artifacts are included in the service. The service binding defines the
protocol with which the service is addressed. In addition, it provides the means to activate the service locally.
The Fiori Elements App Preview can be used as soon as the service is activated.
Create a service definition for the draft business service. For more information, see Creating Service Definitions
[page 903].
Based on the service definition, create a service binding. For more information, see Creating Service Binding
[page 904]. You can choose between OData V2 and V4 protocol to expose a UI service.
Note
The draft scenario in the ABAP Flight Reference Scenario uses the suffix _D. To distinguish the draft service
from the service with draft, we use the addition _D before the suffix. For more information, see Naming
Conventions for Development Objects [page 918].
Include the projection views of the business object and all related CDS entities that are relevant for UI
consumption into the service definition.
Expand the following code sample to view the source code of Projection View /DMO/
C_BookingSupplement_D_D.
Sample Code
Expose the service as a UI service with OData protocol V2 or V4 and publish it locally. You can then use the
preview to test the service.
An OData service can be published as a web API. This enables the consumption of the service by any client,
independent from a particular UI.
Introduction
A Web API [page 965] is an OData service whose metadata does not entail any UI-specific annotations that are
defined for the data model. It is published for the purpose of providing an API to access the service by an
unspecified client. A Web API facilitates the exchange of business information between an application and any
client, including from a different system or server.
In this development guide you will reuse the service that you created in Developing Unmanaged Transactional
Apps [page 299] and publish a Web API for it. Since a Web API is not being used directly in a UI context, the
consumer of a service of this type only requires a reduced set of metadata. The metadata lacks any kind of UI-
relevant information.
The basis for the service remains identical to an OData service exposed for a UI. It is just the binding type in the
service binding that differs, which can be seen in the subsequent figure. It is even possible to expose a service
that was created originally as a UI service for API consumption. The metadata are automatically reduced to the
relevant information for Web API, which means without UI-related annotations or value helps.
Prerequisites
Developing the scenario that is described in the subsequent chapters requires the following:
● You have access to and an account for SAP Cloud Platform, ABAP environment.
● You have installed ABAP Development Tools (ADT).
SAP recommends to use the latest version of the client installation. The ADT download is available on the
update site https://tools.hana.ondemand.com/.
● To recreate the demo scenario, the ABAP Flight Reference Scenario must be available in your ABAP system.
You can download the complete reference scenario from GitHub: Downloading the ABAP Flight Reference
Scenario [page 12].
● You have understood the development steps to create a transactional OData service for a UI as described
in Developing Unmanaged Transactional Apps [page 299].
In particular, you are able to reuse the data model including the behavior of the existing OData
service /DMO/TRAVEL_U to expose it for a Web API.
Via ABAPGit You can import the service /DMO/TRAVEL_U including the related development objects into
your development environment. So you do not have to build the service to test the publishing as Web API.
You find the service in the package /DMO/FLIGHT_UNMANAGED.
For information about downloading the ABAP Flight Reference Scenario, see Downloading the ABAP Flight
Reference Scenario [page 12].
As described above, the following guide reuses the data model and behavior of the service that was created in
the transactional guide. This means that the following artifacts must be available in your system to follow the
steps of this guide.
Artefact Description
CDS Entities
/DMO/I_TRAVEL_U The Travel entity defines general travel data, such as the
agency ID or customer ID, status of the travel booking, and
the price of travel. The entity represents the root node of the
business object
/DMO/I_Booking_U The Booking entity manages data for a booked flight for a
certain travel instance. It is a composition of the Travel
entity and is therefore dependent on its root.
Behavior Artifacts
/DMO/TRAVEL_U The service definition defines all the entities that are ex
posed for the service.
Prerequisites
You have an existing service definition for which you want to create a Web API service. In our example scenario
we reuse the service definition /DMO/TRAVEL_U, which was already exposed as a UI service in the
transactional scenario.
If no service definition is available, choose the entities that you want to expose as an API and create a service
definition for these entities. For a description on how to create a service definition, refer to Creating a Service
Definition [page 23].
You have defined the scope of the service that you want to expose in a service definition. The service must now
be bound to an OData protocol and published as a Web API.
Procedure
1. In your ABAP project, open the context menu for the existing service definition /DMO/TRAVEL_U and
choose New Service Binding to launch the creation wizard.
2. In addition to the Project and Package, enter the Name and the Description for the service binding you want
to create.
Note
3. Select the Binding Type ODATA V2 - Web API to bind the service to a V2 protocol and expose it as a Web
API.
4. Verify that the correct Service Definition is preset in the wizard to be used as a base for your service
binding.
5. Choose Next.
6. Assign a transport request and choose Finish.
The ABAP back end creates a service binding and stores it in the ABAP Repository.
The metadata document can be accessed by following the link Service URL. The UI preview is naturally not
available.
Results
Except for UI features, the service including all implemented features is now exposed as an API. In particular,
this means that the implemented behavior is also exposed in this API.
The difference to a service that is published for a UI client can best be seen in the service metadata. Whereas
the metadata of a UI service carries information about the UI representation of the service, the Web API service
does not contain any such information.
Example
In our example scenario this difference can be seen when comparing the annotation section that refers to
the implemented value help.
The UI service lists every field of a value help provider view as ValueListParameter in the annotation
section. The Web API on the other hand lacks any UI specific information, although the value help
annotation is defined in the respective CDS views.
Based on a remote OData service, you create a new service that enhances the remote service with additional
information.
Introduction
In this scenario, you develop an OData service that consumes a Web API [page 965]. This Web API represents a
remote service from which you retrieve data. This data is then enhanced with additional data. That means, the
new OData service has two data sources. One is the remote service, the other source is a database table in the
consuming development system. You build a new SAP Fiori UI application that consumes the remote service
from the provisioning tenant, and enhances the remote service with persistent data of the consuming tenant.
The data model for the new service is defined in a CDS custom entity [page 949], which enables you to retrieve
data from different sources. A CDS custom entity has an unmanaged query [page 964], which has to be
implemented manually in a related ABAP class, the query implementation class [page 959]. In this ABAP class,
data from different sources, including from another system, can be retrieved.
With the help of the service consumption model [page 961], you can import the OData metadata of the remote
service into the consuming system and create proxy artifacts of the remote service. This gives you a local
representation of the remote data model in your tenant, which helps you to define a data model for the new
service.
This guide also describes how you implement transactional behavior to modify the additional data on the local
database table.
The following image gives an overview of the architecture of the service consumption scenario.
To be able to get data from a remote service, you build an OData client proxy [page 957] in your
implementation to forward and transform the requests for the remote service. The client proxy can only
consume the remote service if a connection to the provisioning tenant is established. The configuration of such
a destination is a precondition for developing this scenario.
Prerequisites
Developing the scenario that is described in the subsequent chapters requires the following:
● You have access to and an account for SAP Cloud Platform, ABAP environment.
● You have installed ABAP Development Tools (ADT).
SAP recommends to use the latest version of the client installation. The ADT download is available on the
update site https://tools.hana.ondemand.com/.
● You know the URL of the remote service that you want to consume or have access to the CSDL XML file of
the service.
In this demo scenario, the service /DMO/API_TRAVEL_U_V2 is used as remote service. You can download
the service as part of the ABAP Flight Reference Scenario, see Downloading the ABAP Flight Reference
Scenario [page 12].
● A cloud destination is configured in the Cloud Cockpit to connect to the provisioning system.
For more information, see Configure a Destination for the Sample Apps.
● A communication arrangement for the destination service instance in the ABAP environment has been
created in your consuming system (SAP_COM_0276).
For more information, see Creating a Communication Arrangement for the Destination Service Instance in
the ABAP Environment
A Web API service is the prototypical example for this consumption scenario. It contains all the required
information, but does not contain any metadata that is superfluous in the consumption scenario (for example,
UI-relevant annotations). However, it is also possible to consume an OData service that was published as a UI
service.
The available entity types of the remote service can be viewed in the metadata document when extended. We
only consume the root node Travel.
● Discount Percentage: The end user can maintain a proportional discount for the total price of travel.
● Discount Absolute: The end user can maintain an absolute value as discount for the total price of the travel
In our scenario, these fields are persisted in the database table /dmo/traveladd, which consists of the two
discount fields, the travel ID as the key, and administrative data to track changes.
In addition to the persistent fields, there is also a calculated field (Total Price with Discount) that displays the
new total price including the possible discount. This field, however, is not persisted in the database, but is
transient.
The following figure shows the end user UI with fields that are retrieved remotely from the provisioning system,
persistent fields, and the transient field.
To merge the locally and remotely retrieved data, the application developer must import the metadata of the
remote service into the consuming system. Abstract entities that mirror the original data model of the remote
service are generated by the service consumption wizard. It is then possible to access the service within the
local ABAP code. To build a new OData service based on the remote service including additional database
fields, a CDS custom entity is used to represent the data model, including both local and remote fields.
The program logic of a custom entity is not managed by ABAP runtime frameworks, but has to be implemented
manually. The implementation includes the query itself and every query option as well as the transactional
behavior for the local fields. The query is implemented in a query implementation class and the transactional
runtime behavior is defined via a behavior definition and implemented in the related behavior pool. To access
the data from the remote service, a destination to the provisioning system must be instantiated in the
implementation classes.
To create the OData service, we expose the custom entity in a service definition and create a service binding to
bind the service against a protocol. In our example case, this is a V2 UI service.
As a result, the existing remote read-only service and database fields with transactional capabilities are merged
within the same SAP Fiori Elements app.
Related Information
In order to consume the remote service in your local system, you need a local representation of the remote
service.
Context
To be able to access the remote OData service, you need to generate proxy artifacts for it. These abstract
entities mirror the data model of the remote service.
The wizard that creates the proxy artifacts requires a CSDL XML [page 950]document of the external service
as a basis for the abstract entities.
Context
A CSDL XML file can be retrieved from any service if the service URL is known. The following procedure
describes the steps for saving a CSDL XML document on your local machine.
Procedure
Note
You get the metadata document by adding /$metadata to the service URL.
You can now use this CSDL XML document in the service consumption wizard.
Prerequisites
The metadata CSDL XML file of the service you want to consume is stored on your local machine.
Context
The proxy artifacts that represent the remote service in your system are required to access the remote service
in your ABAP code. A wizard generates these proxy artifacts.
Procedure
1. In your ABAP package, open the context menu and choose New Other ABAP Business Services
Service Consumption Model to launch the creation wizard.
2. In addition to the Project and Package, enter a Name for the new service consumption model and a
Description.
We use the name /DMO/TRAVEL_C_A in our example scenario. The suffix _C for consumption scenario and
_A for abstract.
Note
This name is also used for the service definition that will be generated.
On the ABAP Artifact Generation List screen, in addition to the selected entity set, the service definition
that is generated is listed.
7. Choose Next and assign a transport request.
8. Choose Finish.
The service consumption model editor is opened, in which you can see and verify the generated artifacts.
The wizard creates an abstract entity [page 948] in your local system for each entity from the external
service that you have chosen. If the source service contains creatable, updatable, or deletable entities, a
behavior definition is created with the same name as the related abstract entity. These entities are found in
the Core Data Services folder in ADT. Additionally, the generated service definition is created in the
Business Services folder. It carries the same name as the service consumption model.
We only use the root node of the external service, which is now represented as /DMO/TRAVEL_C_A.
You can access any of the development objects directly from this editor.
The service consumption model also generated code samples with placeholders for CRUD operations,
which facilitate your entity set consumption.
Results
The following codeblock displays the source code of the abstract entity /DMO/TRAVEL_C_A:
Note
Element types, the semantics annotations, and the OData entity set name, which is referenced in the
annotation, are taken from the service metadata document of the remote service. If an ETAG is maintained
in the original service, an element for the ETAG is added. The text element for the elements that have a text
association in the original service are also added to the abstract entity. We will not use them in the service
consumption scenario.
This abstract entity serves as a template for the data model of the new service.
Context
The remote service is enriched with discount fields (discount_pct and discount_abs). These enable the end
user to maintain discounts for trips. The discounts can be either absolute or relative to the total price of the
trip. These fields are used to calculate the new price (TotalPriceWithDiscount) in a transient field.
To create the database table for the discount fields, follow the steps.
Procedure
1. In the Project Explorer, select the relevant Package node, open the context menu, and choose New
Other ABAP Repository Object Dictionary .
2. Select Database Table to launch the creation wizard.
Custom entities are used for data models whose runtime is implemented manually.
Context
This service consumption scenario retrieves its data partly from a remote service and partly from a local
database table. In custom entities [page 949], you define the elements and type them to be used in the local
Procedure
1. In your ABAP project, select the relevant package node in the Project Explorer. Open the context menu and
choose New Other... Core Data Services Data Definition to launch the creation wizard.
2. Enter the necessary information for the wizard to create the CDS custom entity. Choose Define Custom
Entity with Parameters as the template.
A new data definition for a CDS custom entity is created and added to the Core Data Services folder of the
corresponding package node. In our scenario, the name of the custom entity is /DMO/I_TRAVEL_C_C (first
suffix for consumption scenario, the second for custom entity).
3. Delete the with parameters statement. Parameters are not needed in the current scenario.
Results
Unlike CDS views, custom entities do not have a select statement to retrieve the data from a data source. The
runtime of a custom entity must be implemented in a related ABAP class, which must be referenced using the
annotation @ObjectModel.query.implementedBy.
Next Steps
The elements can now be defined in the editor. At least one element must be key. It is also necessary to
determine a class for the query implementation in the annotation @ObjectModel.query.implementedBy.
Context
As described in Scenario Description [page 494], the data for the new OData service is partly retrieved from a
foreign S/4HANA system and partly taken from a local database. As we have already generated the proxy
artifacts, we can copy the data structure and the data types from the abstract entity /DMO/TRAVEL_C_A. In
addition, the additional discount elements are included, which have the same data types as declared in the
database table /dmo/traveladd.
Procedure
1. If you have not already done so, open the new data definition for the CDS custom entity in the editor.
2. Define the data model for the new travel service. You can copy the elements from the abstract
entity /DMO/TRAVEL_C_A.
You do not have to use the same names as in the abstract entity, but if you do use a different name, you
have to map this manually in the query implementation. For example, the element Memo is renamed to
Description in the custom entity.
3. Delete ETAG_ETAG element and the text elements AgencyID_Text and CustomerID_Text.
Note
You do not need the element ETAG_ETAG from the remote service. We use a calculated eTag in this
scenario, which contains the timestamp from the local database table and the time timestamp from
the remote service to ensure that no data has changed, neither on the local databse table nor in the
remote service.
DiscountPct : abap.dec( 3, 1 );
DiscountAbs : abap.dec( 16, 3 );
5. Add the element for the transient field that calculates the total price with discount.
TotalPriceWithDiscount: abap.dec(17,3)
CalculatedEtag : abap.string( 0 );
7. Define the semantic relations between the amount and currency fields with the relevant annotations..
@Semantics.amount.currencyCode: 'CurrencyCode'
@Semantics.currencyCode: true
The following codeblock displays the custom entity elements with the right types and the semantic
annotations.
Next Steps
Before you can activate the CDS custom entity, you need to create an ABAP class that implements the
interface IF_RAP_QUERY_PROVIDER. This class needs to be referenced in the entity annotation
@ObjectModel.query.implementedBy.
Context
The runtime of a CDS custom entity must be implemented manually. Therefore, it requires an ABAP class that
implements the select method of the interface IF_RAP_QUERY_PROVIDER to handle query requests by the
client. This class is referenced by the custom entity and is dedicated to implementing the OData client data
requests.
1. In the Project Explorer, select the relevant package node, open the context menu, and choose New
ABAP Class to launch the creation wizard.
2. Follow the steps of the creation wizard and enter the necessary information. In the example scenario, we
choose the name /DMO/CL_TRAVEL_C_Q for the query implementation class.
Once you have completed the wizard, the initially generated source code is displayed and ready for editing.
3. Implement the select method of the query provider interface IF_RAP_QUERY_PROVIDER.
The custom entity can only be activated once the select method of the interface
IF_RAP_QUERY_PROVIDER is implemented in the query implementation class.
@ObjectModel.query.implementedBy: 'ABAP:/dmo/cl_travel_c_q'
define custom entity /DMO/I_TRAVEL_C_C
Address a remote service in your ABAP code and implement the query contract and the transactional behavior
for the business object to build a new OData service.
Our service consumption scenario includes the creation of a new OData service in the consuming tenant. You
have defined a data model for this service in a CDS custom entity, and now, the unmanaged query must be
implemented in a related ABAP class as the query is not managed by a framework.
Additionally, this scenario also illustrates how transactional behavior can be included for the new OData
service. To update the discount data, the business object (consisting only of the custom entity in this case)
must be equipped with the implementation of the transactional runtime in a behavior pool. This includes the
implementation of the interaction phase and save sequence.
Remember
The consumption of a Web API requires the configuration of the involved systems (provisioning and
consuming system) to allow the communication between them.
The following sections guide you through the implementation steps to instantiate a remote client proxy to
reach the provisioning system, to implement the query for read access to the remote service and to implement
transactional access to the discount persistence layer.
To enable data retrieval from a remote service, you must establish a connection to the provisioning system and
instantiate a client proxy that passes the OData requests to the remote service.
Prerequisites
To be able to address a remote service, make sure that you meet the Prerequisites for the Consumption of a
Remote Service [page 494].
Context
A remote client proxy is used when an OData service is consumed remotely using an HTTP request. For reuse
reasons, it is useful to instantiate the client proxy in a separate helper class.
In the example scenario, the name of the exception class is CX_TRAVEL_SERV_CONS. The name of the
message class is CM_SERV_CONS.
Implementation Steps
● In the Project Explorer, select the relevant package node, open the context menu, and choose New
ABAP Class to launch the creation wizard.
● Follow the steps of the creation wizard and enter the necessary information. In the example scenario, we
choose the name /DMO/CL_TRAVEL_CP_AUX. Once you have completed the wizard, the initially generated
source code is displayed and ready for editing.
For a detailed description of how to use the wizard, see .
● Declare a static method in the public section of the auxiliary class for the creation of a client proxy:
create_client_proxy with a returning parameter ro_client_proxy and an exception to be raised if
there are errors. .
2. Get the destination to the provisioning system and create an HTTP client
To be able to access the provisioning tenant, you must maintain a cloud destination for the foreign system and
create an HTTP client. Use the code samples of the service consumption model for this.
cl_http_destination_provider=>create_by_cloud_destination(
i_name =
'Travel_Basic'
i_service_instance_name =
'OutboundCommunication' ) ).
" Error handling
CATCH cx_http_dest_provider_error INTO DATA(lx_http_dest_provider_error).
RAISE EXCEPTION TYPE /dmo/cx_travel_serv_cons
EXPORTING
textid = /dmo/cx_travel_serv_cons=>remote_access_failed
previous = lx_http_dest_provider_error.
CATCH cx_web_http_client_error INTO DATA(lx_web_http_client_error).
RAISE EXCEPTION TYPE /dmo/cx_travel_serv_cons
EXPORTING
textid = /dmo/cx_travel_serv_cons=>remote_access_failed
previous = lx_web_http_client_error.
ENDTRY..
To consume the remote service from the provisioning system, we need a client proxy that delegates the
requests to the remote service. Since our scenario works with OData V2, we need a V2 client proxy.
The following topics guide you through the implementation steps required to execute a query request in a
service consumption scenario.
Our scenario has two different data sources. One is a remote service from a possibly different system. To
retrieve data from this service, you must have an appropriate connection to the provisioning tenant (see
Prerequisites for the Consumption of a Remote Service [page 494]) and the select method in the query
implementation class must instantiate a client proxy that executes requests for the remote service.
Additionally, it must include the implementation of every query option you want to support. The other data
source is a database table in the consuming system that stores the additional discount fields. To retrieve the
data from this table, the select method includes an ABAP SQL SELECT.
The following figure illustrates how the query works in the service consumption scenario.
Apart from the simple data retrieval, the scenario also includes the query options filtering, selecting, sorting,
and paging. All of these capabilities must be implemented in the query implementation class.
The client proxy is a powerful means to pass on OData requests to the provisioning tenant. It must be
instantiated in the query implementation class. All the possible query options of the OData request in the
consuming system must then be added to the request for the client proxy. This needs to be done for every
Note
The query options can only be delegated to the client proxy if the query options only affect the elements
from the remote service. If the query options are operated on local database fields, they must be
implemented manually after the retrieval of the data set from the provisioning system. As this might cause
serious performance issues, it is not demonstrated in this example scenario. Consider performance
aspects for your use case, if you decide to implement this.
Note
When choosing the GO button on the UI to retrieve data, it always requests the count and paging.
Therefore, the implementation for data retrieval must include at least counting and paging. Otherwise you
will get a runtime error.
When all necessary query options are added to the request, it can be executed and the result must be given
back to the UI.
The procedure for implementing the query implementation is described in the following in detail:
Related Information
To enable data retrieval from a remote service, you must establish a connection to the provisioning tenant and
instantiate a client proxy that passes the OData requests to the remote service. The data sets from the remote
service are then imported to the consuming tenant and can be merged with additional data.
Preview
Choosing the Go button on an SAP Fiori UI triggers the OData request for data and count retrieval. The UI
automatically includes a paging query option with usually 25 data records per page.
Implementation Steps
● In the query implementation class, call the method get_client_proxy of the auxiliary class /DMO/
CL_TRAVEL_CP_AUX in the select method.
METHOD if_rap_query_provider~select.
"""Instantiate Client Proxy
DATA(lo_client_proxy) = /dmo/cl_travel_cp_aux=>get_client_proxy( ).
...
ENDMETHOD.
● On the client proxy object, call the method create_resource_for_entity_set with the TRAVEL entity
set name as importing parameter.
Note
The OData entity set name can be found in the relevant abstract entity:
Note
Use uppercase for the OData entity set name of the entity for this method, since the internal ABAP
representation of entity sets is in uppercase.
TRY.
"""Create Read Request
DATA(lo_read_request) = lo_client_proxy-
>create_resource_for_entity_set( 'TRAVEL' )->create_request_for_read( ).
CATCH /iwbep/cx_gateway INTO DATA(lx_gateway).
RAISE EXCEPTION TYPE /dmo/cx_travel_serv_cons
EXPORTING
textid = /dmo/cx_travel_serv_cons=>query_failed
previous = lx_gateway.
ENDTRY.
● Within the try-catch Block, check whether the total number of records is requested by the UI by calling the
method is_total_numb_of_rec_requested of the interface IF_RAP_QUERY_REQUEST.
For more information about is_total_numb_of_rec_requested, see Method
is_total_numb_of_rec_requested [page 881].
.
Note
The same exception as previously defined can be used for all methods called on the read request.
"""Request Count
IF io_request->is_total_numb_of_rec_requested( ).
lo_read_request->request_count( ).
ENDIF.
Note
If all data records are requested, the method returns the constant page_size_unlimited, which has
the value -1. If this constant is returned, the query option $skip must not be passed to the read
request.
"""Request Data
IF io_request->is_data_requested( ).
"""Request Paging
DATA(ls_paging) = io_request->get_paging( ).
IF ls_paging->get_offset( ) >= 0.
lo_read_request->set_skip( ls_paging->get_offset( ) ).
ENDIF.
IF ls_paging->get_page_size( ) <>
if_rap_query_paging=>page_size_unlimited.
lo_read_request->set_top( ls_paging->get_page_size( ) ).
ENDIF.
ENDIF.
The read request for the remote OData service is now equipped with the query options for count
$inlinecount=allpages (if total number of records is requested) and for paging $skip and $top (if data is
requested). At least, these two query options should be implemented in the query implementation class if the
consuming service is a UI service.
Remember
By default, the UI sends the query options for counting and paging.
Now, the request can be sent. In other words, it is executed on the remote service.
6. Provide the Count Result for the Response of the Unmanaged Query
The response parameter io_response of IF_RAP_QUERY_PROVIDER~select must be filled with the total
number of records that the count request to the remote service returns.
"""Set Count
IF io_request->is_total_numb_of_rec_requested( ).
io_response->set_total_number_of_records( lo_response->get_count( ) ).
ENDIF.
7. Provide the Result Data for the Response of the Unmanaged Query
Note
The eTag is used to ensure that discount data has not changed. That is why, the retrieved value is
overwritten with the initial timestamp for the field.
"""Set Data
IF io_request->is_data_requested( ).
DATA: lt_travel TYPE STANDARD TABLE OF /dmo/travel_c_a,
lt_travel_ce TYPE STANDARD TABLE OF /dmo/i_travel_c_c,
lt_traveladd TYPE STANDARD TABLE OF /dmo/traveladd.
lo_response->get_business_data( IMPORTING et_business_data =
lt_travel ).
IF lt_travel IS NOT INITIAL.
lt_travel_ce = CORRESPONDING #( lt_travel MAPPING description =
memo ).
SELECT * FROM /dmo/traveladd FOR ALL ENTRIES IN @lt_travel_ce WHERE
travel_id = @lt_travel_ce-travelid INTO TABLE @lt_traveladd.
LOOP AT lt_travel_ce ASSIGNING FIELD-SYMBOL(<fs_travel_ce>).
IF line_exists( lt_traveladd[ travel_id = <fs_travel_ce>-
travelid ] ).
Results
Expand the following listing to view the source code of the query implementation for data retrieval and count:
If you have defined a UI service for the custom entity (see Defining an OData Service [page 540]), you can test
your implementation with the SAP Fiori Elements Preview. When you open the app and choose the Go button,
you receive the data and the count from the remote service together with the matching discount data.
Related Information
Filter options can be delegated to the remote service. Fill the request that is passed to the OData client proxy
with the query option $filter.
Preview
To retrieve the appropriate entries for the filter, you must implement a filter factory to get the filtered results
from the remote service.
Implementation Steps
This scenario only implements filtering by fields of the remote service. The service proxy API provides a filter
factory to delegate the filter to the remote service. Filtering by local fields is not supported in this scenario.
Note
To filter by local fields, you would have to retrieve all available data sets of the remote service and merge the
local fields to these first, to be able to filter by discount values then. In this case, paging could not be
delegated to the remote service either and would have to be implemented after data retrieval manually.
Hence filtering for discount values might cause serious performance issues and is therefore not exemplified
in this scenario.
Note
Filtering on an amount field only works if the end user also provides a filter on the unit field, in our case a
currency code. In our scenario, the fields TotalPrice and BookingFee are amount fields. If a filter is
entered for these fields, the currency code must be in the filter condition as well. Hence, the
implementation must also include the currency every time a filter for the field TotalPrice is supplied.
● The unmanaged query API provides a method to get the filter conditions from the UI OData request into a
local variable. Use io_request->get_filter( )->get_as_ranges( ) to get the filter conditions as
ranges into a local variable lt_filter. Handle the exception if the filter cannot be converted into a ranges
table.
For more information about the unmanaged query filter API, see Interface IF_RAP_QUERY_FILTER [page
886].
● Loop over lt_filter.
Remember
Some filter requests require special handling before sending them to the remote service:
○ Filtering on local fields is not allowed.
○ For filtering on an amount field, the unit must be provided in the filter.
● Raise an exception with an adequate message, for example Filtering on &1 is not allowed if filter
is requested for a local field.
For information about how to work with exception classes and message classes, see
and .
● Find out the currency code for filter requests on amount fields.
● Raise an exception if currency code is not provided, for example Currency code is not supplied
for &1.
● Create the filter property to be passed to the remote service. Map the element Description to Memo for
the remote service to match the data model of the remote service.
● Create a filter factory to provide the filter request for the client proxy.
● Create the filter ranges for the remote service by calling the method create_by_range of the filter
factory and export the filter property, the filter range and, if necessary, the currency code.
● If a filter is defined for more than one element, concatenate the filter condition.
● Set the filter for the request.
"""Request Filtering
TRY.
DATA(lt_filter) = io_request->get_filter( )->get_as_ranges( ).
CATCH cx_rap_query_filter_no_range INTO DATA(lx_no_range).
RAISE EXCEPTION TYPE /dmo/cx_travel_serv_cons
EXPORTING
textid = /dmo/cx_travel_serv_cons=>no_ranges
previous = lx_no_range.
THEN 'MEMO'
ELSE <fs_filter>-name ).
"create filter factory for read request
DATA(lo_filter_factory) = lo_read_request->create_filter_factory( ).
"
DATA(lo_filter_for_current_field) = lo_filter_factory-
>create_by_range( iv_property_path = lv_filter_property
it_range = <fs_filter>-range
iv_currency_code = lv_currencycode ).
"Concatenate filter if more than one filter element
DATA: lo_filter TYPE REF TO /iwbep/if_cp_filter_node.
IF lo_filter IS INITIAL.
lo_filter = lo_filter_for_current_field.
ELSE.
lo_filter = lo_filter->and( lo_filter_for_current_field ).
ENDIF.
ENDLOOP.
"set filter
IF lo_filter IS NOT INITIAL.
lo_read_request->set_filter( lo_filter ).
ENDIF.
Results
If you have defined a UI service for the custom entity (see Defining an OData Service [page 540]), you can test
your implementation with the SAP Fiori Elements Preview. You can define filters and check that the UI only
retrieves data that matches the filter request.
Select options can be delegated to the remote service. Fill the request that is passed to the OData client proxy
with the query option $select.
Preview
To retrieve the appropriate data, you must transfer the selected properties to the read request for the remote
service.
You can optimize the performance if you only select those elements from the remote service that are selected
on the UI. The local fields are selected separately. Therefore, you must exclude the local fields from the
requested elements as they are not delegated to the remote service.
Considering the requested elements from the UI when delegating the request to the remote service is only
necessary when data is requested. Therefore the implementation must only be called if data is requested
before the execution of the read request.
● The unmanaged query API provides a method to get those elements that are requested by the UI. Use
DATA(lt_req_elements) = io_request->get_requested_elements( ). to get the elements into
a local variable.
For more information about the unmanaged query API, see Interface IF_RAP_QUERY_REQUEST [page
880].
● Delete the local elements from the properties that are selected.
Remember
Local fields are not delegated to the remote service. They are selected manually.
● Loop over lt_req_elements and map the element Description to Memo for the remote service to
match the data model of the remote service.
● Declare lt_select_properties and type it as a table of property paths. This table contains the
properties that are given to the client proxy for creating the select query option.
● Fill the table lt_select_properties that is given to the client proxy with the elements to be selected
from the remote service.
● Set the select properties for the OData request of the remote service.
"""Request Elements
DATA(lt_req_elements) = io_request->get_requested_elements( ).
"delete local fields out of the fields to be selected via OData Client
Proxy
DELETE lt_req_elements WHERE table_line = 'DISCOUNTPCT' OR
table_line = 'DISCOUNTABS' OR
table_line = 'TOTALPRICEWITHDISCOUNT' OR
table_line = 'CALCULATEDETAG'.
"map differing names
LOOP AT lt_req_elements ASSIGNING FIELD-SYMBOL(<fs_req_elements>).
DATA(lv_select_property) = COND /iwbep/
if_cp_runtime_types=>ty_property_path( WHEN <fs_req_elements> ='DESCRIPTION'
THEN 'MEMO'
ELSE <fs_req_elements> ).
DATA: lt_select_properties TYPE /iwbep/
if_cp_runtime_types=>ty_t_property_path.
APPEND lv_select_property TO lt_select_properties.
ENDLOOP.
"set select properties
IF lt_select_properties IS NOT INITIAL.
lo_read_request->set_select_properties( lt_select_properties ).
ENDIF.
If you have defined a UI service for the custom entity (see Defining an OData Service [page 540]), you can test
your implementation with the SAP Fiori Elements Preview. Trigger the selection by choosing columns on the
SAP Fiori UI and check whether the request works.
Related Information
Sorting options can be delegated to the remote service. Fill the request that is passed to the OData client proxy
with the relevant query option $orderby.
Preview
To retrieve the data in the appropriate order, you must add sorting information to the request for the OData
client proxy.
Implementation Steps
This scenario only implements sorting by fields of the remote service. The service proxy API provides the
method set_order_by to delegate the sorting order to the remote service.
Note
To sort by local fields, you would have to retrieve all available data sets of the remote service and merge the
local fields to these first, to be able to sort by discount values then. In this case, paging could not be
The sorting information is only relevant if you retrieve data. Therefore the implementation must only be called if
data is requested to equip the read request for the remote service with this information.
● The unmanaged query API provides a method to get the sorting information. Use DATA(lt_sort) =
io_request->get_sort_elements( ). to get the sorting information into a local variable.
For more information about the unmanaged query API, see Interface IF_RAP_QUERY_REQUEST [page
880].
● Loop over lt_sort.
Remember
● Raise an exception with an adequate message, for example Sorting on &1 is not allowed.
For information about how to work with exception classes and message classes, see
and .
● Declare lt_sort_properties. This table is passed to the client proxy with the elements to be sorted.
● Map the element Description to Memo for the remote service to match the data model of the remote
service.
● Fill the table lt_sort_properties that is given to the client proxy with the elements to be selected from
the remote service and map the columns.
● Set the sorting properties for the request by using the set_orderby.
"""Request Sorting
DATA(lt_sort) = io_request->get_sort_elements( ).
LOOP AT lt_sort ASSIGNING FIELD-SYMBOL(<fs_sort>).
IF <fs_sort>-element_name = 'DISCOUNTPCT' OR
<fs_sort>-element_name = 'DISCOUNTABS' OR
<fs_sort>-element_name = 'TOTALPRICEWITHDISCOUNT' OR
<fs_sort>-element_name = 'CALCULATEDETAG'.
RAISE EXCEPTION TYPE /dmo/cx_travel_query
EXPORTING
textid = /dmo/cx_travel_query=>sorting_failed
element = <fs_sort>-element_name.
ENDIF.
"map differing names
DATA: lt_sort_properties TYPE /iwbep/
if_cp_runtime_types=>ty_t_sort_order.
APPEND VALUE #( property_path = COND #( WHEN <fs_sort>-element_name
= 'DESCRIPTION' THEN 'MEMO'
ELSE <fs_sort>-element_name )
descending = <fs_sort>-descending )
TO lt_sort_properties.
ENDLOOP.
"set sorting properties
IF lt_sort_properties IS NOT INITIAL.
lo_read_request->set_orderby( lt_sort_properties ).
ENDIF.
ENDIF.
If you have defined a UI service for the custom entity (see Defining an OData Service [page 540]), you can test
your implementation with the SAP Fiori Elements Preview. Try out the sorting on different elements and check
whether the results are sorted or an exception is thrown.
Related Information
The following steps guide you through the implementation steps to update a business object when a remote
service is merged with local data.
Like in Developing Unmanaged Transactional Apps [page 299], creating, updating, and deleting data requires a
transactional runtime implementation. The service consumption scenario is also a use case for the unmanaged
implementation type as the transactional logic is defined and implemented by the application developer. That
means, the transactional behavior must be defined in a behavior definition [page 945] and has to be
implemented in ABAP classes of a behavior pool [page 945].
Note
This scenario only supports transactional activities on the data that is persisted on a database table in the
consuming tenant. Transactional operations on the remote service are not supported.
In this example scenario, only the discount data can be manipulated by the end user. The fields on the
database table /dmo/traveladd that can be changed are discount_pct and discount_abs. The travel
instance for which the discount is maintained is identified by the travel ID, which is retrieved from the remote
service. Travel ID is a key field and therefore cannot be changed by the end user. For the eTag check, a distinct
field CalculatedeTag is used. Its value is concatenated with the timestamp of the remote service and the
timestamp from the local databse table.
The following tasks are relevant to include transactional behavior for the business object:
The transactional behavior of the travel business object must be defined in a behavior definition.
To get transactional access to the local data, the data model must be turned into a business object. The
custom entity /DMO/I_TRAVEL_C_C must be defined as root entity.
1. Open the custom entity /DMO/I_TRAVEL_C_C and insert the keyword root. A behavior definition can only
be created for a root entity.
By creating a behavior definition [page 946], the referenced root entity gains a transactional character. In the
behavior definition, you define the transactional capabilities of your business object. It is directly related to the
root custom entity and must therefore have the same name /DMO/I_TRAVEL_C_C.
1. In the Project Explorer, select the relevant node for the data definition that contains the CDS root
entity /DMO/I_TRAVEL_C_C, for which you want to create a behavior definition.
2. Open the context menu and select New Behavior Definition to launch the creation wizard.
3. Follow the steps of the creation wizard to create the behavior definition
Note
The implementation type of a custom entity can only be unmanaged. That is why, the wizard only
allows this option.
Result
The created behavior definition object represents the root node of a new business object in ABAP RESTful
application programming model.
In the Project Explorer, the new behavior definition is added to the Core Data Services folder.
Defining the Behavior for the Travel Business Object with Discount Data
For the service consumption scenario, only the implementation type unmanaged [page 327] is supported. That
means, you as an application developer must implement the essential components of the business object
yourself.
Remember
Our scenario is limited to modifying the data that is stored on the database table /dmo/traveladd.
The description of modifying the data that is exposed by the remote service is out of scope of this document.
Nevertheless, each manipulation of the persistent data requires the retrieval of the remote data, as the
discount data can only be stored in conjunction with the travel ID to match the discount to the relevant travel
entry of the remote service. That is why, the creation of new discounts for a travel entry is considered as an
update on a certain travel instance of the business object. In the case of creating a data set of discount data,
you change the discount from the initial value but the key entry (TRAVEL_ID) is already there.
Compare the UI when retrieving the travel data merged with the discount data. The discount fields are
displayed with the initial values if no discount data is maintained for the relevant travel instances.
The modification of discount data is an UPDATE. Hence, we do not need to implement neither the CREATE nor
the DELETE operation to maintain discount data.
A distinct field must be indicated as ETag [page 328] in the behavior definition. The eTag checks that the data
the end user sees that on the UI is consistent with the data on the database and has not been changed in the
meantime. The element CalculatedEtag is used for this purpose and calculated with data from the database
table /dmo/traveladd and the remote service. This ensure that neither the remote data nor the local data
can be changed without the end user knowing the change.
The following code block displays the behavior definition with the definitions that are relevant for the
transactional behavior of the business object.
implementation unmanaged;
define behavior for /DMOI_/TRAVEL_C_C alias Travel_CE
etag calculatedetag
{
update;
}
Note
For better usability in the behavior implementation, you can define an alias for the business object in the
behavior definition.
Related Information
A behavior pool [page 945] for a behavior implementation is needed to implement the behavior that was
defined in a behavior definition. A behavior pool is a special ABAP class that is equipped with the extension FOR
BEHAVIOR OF and references the root entity of the relevant business object.
1. In the Project Explorer, select the relevant behavior definition for which you want to create a behavior
implementation class.
2. Open the context menu and select New Behavior Implementation to launch the creation wizard.
3. Follow the steps of the creation wizard and check if the suggested entries are correct.
Result
The generated global class pool provides you with the extension FOR BEHAVIOR OF with reference to the
behavior definition. This statement determines the connection to the business object. One behavior
implementation can only define the behavior for one business object.
In a service consumption scenario, the transactional behavior for the business object (the updating of discount
data) must be implemented in the local types of a behavior pool. The implementation includes the data
retrieval from the remote service, merging the remote data with the local discount data and finally updating the
local discount data. Hence, the logic of how to access a foreign system also comes into play when updating
local data. To keep performance expenses on a low level, it is indispensable to work with a transactional buffer
and retrieve the remote data only once in one unit of work. Therefore we use a local buffer class to work actively
with a transactional buffer.
The runtime of an update operation of the discount data is displayed in the following diagram. It illustrates how
the methods for READ, MODIFY, and SAVE and the local buffer class interact with each other and how the OData
client proxy is used to request the necessary data from the remote service.
BUFFER
Due to performance reasons, it is useful to work with the transactional buffer in the service consumption
scenario. Retrieving data from the remote service is very costly. Therefore, the data from the remote service
should only be read once in one logical unit of work. We use the buffer as intermediate storage for data retrieval
and processing.
The READ, MODIFY, and SAVE methods call dedicated methods from the buffer to process data. These buffer
methods are:
A detailed step-by-step description for the implementation of the local buffer class is given in Implementing the
Buffer Class [page 530].
For the ETag check (which is necessary for the UPDATE operation), a method for the READ operation must be
implemented.
The latest timestamp of the field lastchangedat from /dmo/traveladd is used to compare with the data in
Last changed on that is displayed on the UI. Only if these timestamps are equal is the UPDATE triggered.
Remember
The field lastchangedat is filled with the time stamp of the last modification of the discount data. In our
example scenario, only the timestamp from the database table /dmo/traveladd is relevant as only these
fields can be updated.
The eTag field is filled with the initial value if no discount is maintained for a certain travel instance.
Note
The example implementation in this guide does not only retrieve the data from the relevant ETag fields, but
reads the whole data set when the READ operation is executed, including the merge with the persistent
data. This is done for performance reasons. To calculate the total price with discount, the value of the field
TotalPrice from the remote instance is needed. Therefore it is more convenient to read the whole data
set once, store it in the buffer and use this data set for the calculation on the update.
A detailed step-by-step description for the implementation of the READ is given in Implementing the READ
[page 535].
For general information about the READ method, see <method> FOR READ [page 863].
The real update takes place during the MODIFY operation. The method update_discount calls the buffer
method put_data, which uses the method get_data to retrieve the data from the buffer (if available). The
discount data is updated and the entry for the transient field TotalPriceWithDiscount is calculated.
A detailed step-by-step description for the implementation of the MODIFY is given in Implementing the MODIFY
[page 536].
For general information about the MODIFY method, see <method> FOR MODIFY [page 858].
SAVE
The save method writes the updated data to the database. It uses the method get_data to retrieve the
updated data from the buffer. It generates a new time stamp to fill the field lastchangedat on the database
table /dmo/traveladd. At last, it persists the updated data on the database table.
For general information about the SAVE method, see Method SAVE [page 871].
Implementation Steps
In this service consumption scenario, the READ and the MODIFY method, including a _map_messages method
are treated in one handler class, as the retrieved data from the READ is reused in the MODIFY method. This
relies to the Best Practices for Modularization and Performance [page 334].
This guide starts with the implementation of the buffer and consequently describes calls of the buffer methods
from the predefined methods READ, MODIFY, and SAVE.
The local buffer class serves as intermediate storage for the data processing in the service consumption
scenario. It is a separate local class in the behavior pool.
1. Create a local buffer class lcl_buffer in the local types of the behavior implementation pool.
2. Set the local class to PRIVATE.
1. Declare the static method get_instance which returns a buffer instance in the public section of the
buffer class
2. Implement get_instance to receive a buffer instance if no instance exists
1. checking whether the travel instance for which discount data is to be updated is already in the buffer and
writing it to the exporting parameter et_travel, if available in the buffer. If not in the buffer, the
travel_id has to be collected to retrieve the relevant data from the persistence.
2. the instantiation of a client proxy, by calling the method get_client_proxy of the auxiliary class /DMO/
CL_TARVEL_CP_AUX, see Creating a Remote Client Proxy [page 506].
3. a reading request for the client proxy with a filter for only requesting the data set for the specific travel ID
4. a select to get and calculate the matching discount data from the database table /dmo/traveladd and to
process the time stamp for the ETag field. A concatenated eTag from the fields lastchangedat of both,
the remote and the local service, is used.
5. calculating the TotalPriceWithDiscount and setting an initial time stamp if no discount data has
changed. .
Remember
The eTag is used to ensure that discount data has not changed. That is why, the retrieved value is not
used for the eTag check but the concatenated value is passed to the UI.
If you want to display a suitable message, you need to create a message class. For example, here, the
message Accesss to remote system cannot be established is convenient. It is retrieved from the
message class /DMO/CM_SERV_CONS.
it_range = lt_filter ).
lo_request->set_filter( lo_filter ).
DATA(lo_response) = lo_request->execute( ).
" get relevant data sets
lo_response->get_business_data( IMPORTING et_business_data =
lt_travel ).
" add local data
IF lt_travel IS NOT INITIAL.
" map OData service to custom entity
lt_travel_ce = CORRESPONDING #( lt_travel MAPPING description =
memo ).
SELECT * FROM /dmo/traveladd FOR ALL ENTRIES IN @lt_travel_ce
WHERE travel_id = @lt_travel_ce-travelid INTO TABLE @lt_traveladd.
LOOP AT lt_travel_id ASSIGNING FIELD-SYMBOL(<fs_travel_id>).
IF line_exists( lt_travel_ce[ travelid = <fs_travel_id> ] ).
ASSIGN lt_travel_ce[ travelid = <fs_travel_id> ] TO
<fs_travel_ce>.
IF line_exists( lt_traveladd[ travel_id = <fs_travel_ce>-
travelid ] ).
<fs_travel_ce>-discountpct = lt_traveladd[ travel_id
= <fs_travel_ce>-travelid ]-discount_pct.
<fs_travel_ce>-discountabs = lt_traveladd[ travel_id
= <fs_travel_ce>-travelid ]-discount_abs.
<fs_travel_ce>-totalpricewithdiscount = <fs_travel_ce>-
totalprice * ( 1 - <fs_travel_ce>-discountpct / 100 ) - <fs_travel_ce>-
discountabs.
<fs_travel_ce>-lastchangedat = lt_traveladd[ travel_id
= <fs_travel_ce>-travelid ]-lastchangedat.
<fs_travel_ce>-calculatedetag = <fs_travel_ce>-
calculatedetag && '-' && lt_traveladd[ travel_id = <fs_travel_ce>-travelid ]-
lastchangedat.
ELSE.
<fs_travel_ce>-totalpricewithdiscount = <fs_travel_ce>-
totalprice.
<fs_travel_ce>-lastchangedat = '20000101120000' . "initial
value Jan 1, 2000, 12:00:00 AM
ENDIF.
ls_result = CORRESPONDING #( <fs_travel_ce> ).
APPEND <fs_travel_ce> TO mt_travel.
APPEND ls_result TO et_travel.
ELSE.
APPEND VALUE #( travelid = <fs_travel_id> ) TO
et_travel_failed.
APPEND VALUE #( travelid = <fs_travel_id>
symsg-msgty = 'E'
symsg-msgid = '/DMO/CM_SERV_CONS'
symsg-msgno = '004'
symsg-msgv1 = <fs_travel_id> )
TO et_message.
ENDIF.
ENDLOOP.
ENDIF.
CATCH /iwbep/cx_gateway.
et_travel_failed = CORRESPONDING #( lt_travel_id MAPPING travelid =
table_line ).
et_message = CORRESPONDING #( lt_travel_id MAPPING travelid =
table_line ).
LOOP AT et_message ASSIGNING FIELD-SYMBOL(<fs_message>).
<fs_message>-symsg-msgty = 'E'.
<fs_message>-symsg-msgid = '/DMO/CM_SERV_CONS'.
<fs_message>-symsg-msgno = '001'.
ENDLOOP.
ENDTRY.
1. calling the method get_data to retrieve the data from the buffer
2. looping at it_travel_upd to identify the changed fields
3. overwriting the changed fields with the new discount data
4. calculating the data for TotalPriceWithDiscount
5. filling the FAILED table in case the total price with discount is negative after discount calculation.
Note
If you want to display a suitable message, you need to add a message to the message class /DMO/
CM_SERV_CONS. In this case, an exception needs to be thrown if the Total Price with Discount is
negative. For example, here, the message Total Price with Discount must be greater than
0 is convenient.
The READ method processes reading requests. Hence, it is necessary to retrieve the data from the field
lastchangedat.
This implementation does not only retrieve the time stamp, which is used for the eTag check, but the whole
data set to store it in the buffer and use it in the actual UPDATE phase without retrieving the data again. The
implementation must ensure that this data is retrieved and handed over to the framework, which handles the
comparison with the time stamp of the UI. It is also the framework that prevents the update if the time stamps
do not match.
See <method> FOR READ [page 863] for information about parameters of the READ.
1. Rename the method for read in the definition part of the handler class with the importing parameter
it_travel_read and the result parameter et_traveladdinfo, which is used for the ETag check. The
implementation.
Remember
METHOD read_travel.
DATA(lo_buffer) = lcl_buffer=>get_instance( ).
lo_buffer->get_data(
EXPORTING
it_travel = it_travel_read
IMPORTING
et_traveladdinfo = et_traveladdinfo
et_travel_failed = failed-travel_ce
).
ENDMETHOD.
The MODIFY method is called by the framework when an UPDATE is defined in the behavior definition. It
modifies the data in the application buffer. In this consumption scenario, the buffer method put_data is called
to do this. To return adequate messages, the MODIFY method also includes a mapping method for messages.
1. Change the name of the MODIFY method to the more transparent name update_discount.
Change the name of the importing parameter into it_travel_update.
The real update is done by the method put_data in the buffer class. This method firstly calls the method
get_data to retrieve the data from the buffer or get it from the remote service. Then it updates the discount
data for the selected travel instance and calculates the amount for TotalPriceDiscount.
For the processing of the appropriate messages if there is a fault event, the method _map_messages is called
within the method update_discount.
Remember
The failed table is an implicit changing parameter of the method for MODIFY.
Remember
The reported table is an implicit changing parameter of the method for MODIFY.
METHOD update_discount.
DATA(lo_buffer) = lcl_buffer=>get_instance( ).
lo_buffer->put_data(
EXPORTING
it_travel_upd = it_travel_update
IMPORTING
et_travel_failed = failed-travel_ce
et_message = DATA(lt_message)
).
_map_messages(
EXPORTING
it_message = lt_message
IMPORTING
et_travel_reported = reported-travel_ce
).
ENDMETHOD.
Message Handling
In case of failure, the issue has to be transferred to the REPORTED table, which includes all instance-specific
messages. For the processing of such messages, the method _map_messages is used. In the consumption
scenario, it is defined in the local handler class, as it is only used in the MODIFY method.
Implement _map_messages
To write messages to the REPORTED table, they have to align with a fixed structure. Messages for this structure
are created via the method new_message, which is inherited from the interface IF_ABAP_BEHV_MESSAGE.
1. For each message in it_message, create a new message for the REPORTED table by calling the method
new_message.
2. Fill the relevant fields in ls_travel_reported with the travel_id of the message and change the flag
for the element which causes the message.
METHOD _map_messages.
DATA: ls_travel_reported LIKE LINE OF et_travel_reported.
FIELD-SYMBOLS: <fs_element> TYPE data.
LOOP AT it_message ASSIGNING FIELD-SYMBOL(<fs_message>).
CLEAR ls_travel_reported.
ls_travel_reported-%msg = new_message( id = <fs_message>-symsg-
msgid
number = <fs_message>-symsg-
msgno
severity =
if_abap_behv_message=>severity-error
v1 = <fs_message>-symsg-
msgv1
v2 = <fs_message>-symsg-
msgv2
v3 = <fs_message>-symsg-
msgv3
v4 = <fs_message>-symsg-
msgv4 ).
IF <fs_message>-travelid IS NOT INITIAL.
ls_travel_reported-%key-travelid = <fs_message>-travelid.
ls_travel_reported-travelid = <fs_message>-travelid.
LOOP AT <fs_message>-fields ASSIGNING FIELD-SYMBOL(<fs_field>).
ASSIGN COMPONENT <fs_field> OF STRUCTURE ls_travel_reported-
%element TO <fs_element>.
CHECK sy-subrc = 0.
<fs_element> = if_abap_behv=>mk-on.
ENDLOOP.
APPEND ls_travel_reported TO et_travel_reported.
ENDIF.
ENDLOOP.
ENDMETHOD.
The template for the behavior implementation provides you with the method declarations for redefinitions of
the save sequence.
Note
In this consumption scenario, we do not exemplify the implementation for finalize and
check_before_save as both of the implementations are optional and not required in our sample
scenario.
In the method SAVE, the transactional buffer is saved to the database. The following listing illustrates the
procedure for the SAVE implementation. The saving action includes
In order to be able to consume the service with an SAP Fiori UI, you need to create an OData service.
1. Follow the development steps as described in Creating an OData Service [page 23].
Create a service definition and a service binding to expose the CDS custom entity including its query
implementation for a service. You only need to include the custom entity in the service definition, its
implementation is included implicitly.
You can then test the resulting UI service by using the Fiori Elements Preview in the service binding.
2. Follow the steps described in Designing the User Interface for a Fiori Elements App [page 34]
Use @UI annotations to define the UI of the SAP Fiori app. UI annotations are maintained in the CDS
custom entity in exactly the same way as in CDS views.
For this consumption scenario, it is convenient to label the element for line items and identification
annotations as the data element information is not retrieved from the remote service.
Note
For selection fields, it is not possible to maintain a label in the @UI annotations. Use the
@EndUser.label annotation instead for elements with selection fields.
Expand the following listing to view the source code of the travel CDS view.
3. Add static feature control for attributes to set them read-only or mandatory.
The only attributes that are modifiable are DiscountPct and DiscountAbs. They need to be filled with
values. Set all other attributes as mandatory. For further information, see Feature Control [page 85].
Expand the following listing to view the source code of the travel CDS view with the relevant feature
control additions.
implementation unmanaged;
define behavior for /DMO/TRAVEL_C_C alias Travel_CE
etag LASTCHANGEDAT
{ update;
field (read only) TRAVELID, AGENCYID, CUSTOMERID,
Related Information
In the common tasks section you find self-contained development tasks that you can apply in any develop
scenario.
Data Model
Defining Text Elements [page 544]
Enabling Text and Fuzzy Searches in SAP Fiori Apps [page 561]
Business Objects
Automatically Drawing Primary Key Values in Managed BOs [page 579]
Query
Implementing an Unmanaged Query [page 608]
UI Semantics
Adding Field Labels and Descriptions [page 628]
This section describes how to determine and provide related text for a CDS view element within the context of
the ABAP RESTful Programming Model.
Example
Contents
You have different options to provide text for CDS view elements:
Note
Language independent text elements can be maintained in the same entity as the identifier element.
Within the context of CDS views, the text elements establish the link between identifier elements (code values)
of the view and its descriptive language-independent texts. For example, you can define a link between a
company code and the (descriptive) company name, or between currency code and the currency name if these
elements are part of the one CDS view. These kinds of descriptive texts are language-independent. That
means, they do not contain text that is to be translated.
Annotation Effect
Note
The usage of this annotation excludes the usage of
@ObjectModel.text.association in the same
CDS entity. .
Note
Runtime Behavior: In scenarios with exposure via OData, only the first text element listed in the
annotation array will be handled as a descriptive text of the annotated field.
Example
In the listing below, the CDS view /DMO/I_SupplementText defines the fields Description that serves as
language-independent descriptions for the view field SupplementID.
Related Information
Context
Using the CDS text association, you can define the relationship between an element (field) and its
corresponding texts or descriptions. The texts are usually language-dependent and are displayed in the end
user's language. If you annotate an element with a text association (as described below), the associated text or
description field is added as a field to the referencing entity. At runtime, this field is read from the database and
filtered by the logon language of the OData consumer automatically. It is not necessary to use session
properties or view parameters in your CDS view.
Note
In scenarios, in which you use projection views, getting text through text associations is not supported.
Procedure
1. Create a data definition with a CDS view that serves as text provider
The following annotations are required at element level in the text provider view to annotate the language
key element and the text elements of the view’s element list:
@Semantics.text: true Identifies view elements as text elements (fields pointing to a textual descrip
tion)
Note
In general, you can annotate more than one view field as a text field. How
ever, only the first annotated field will be considered in the text consumer
view for OData exposure.
2. Create a text association from your consumer CDS view to the text provider view .
@ObjectModel.text.associa Name of an association with a text view that provides descriptive texts for the
tion: annotated element. In other words: the annotation indicates that the descrip
'<_AssocToTextProvider>' tion for the annotated element is available using the text association
<_AssocToTextProvider>.
The view /DMO/I_BookSuppl_T serves as a consumer for the text provider view /DMO/I_SupplText_T.
For this purpose, the association _SupplementText with the text provider view as target is defined. To
indicate the field for which a text should be made available through the association _SupplementText,
the annotation @ObjectModel.text.association is added. Note that only the first text element
(Description) from the text provider, which is annotated with @Semantics.text: true, will be
considered for OData exposure. In Fiori Elements apps, the supplement ID will then be displayed together
with the long text in description of the text provider view.
Related Information
The denormalization of language-dependent text in projection views is done via the keyword localized for
the text elements, which are included in the projection view and referenced with the annotation
@ObjectModel.text.element: '<text_element>'.
Context
You have a text provider view, in which text for unreadable elements is maintained. To get the text from there
you do not need any preparation in the text provider view.
To establish a connection to this text provider view, the projected view must have an association to the text
provider view.
Note
To use the denormalization in projection views, you must not use the indicator for text associations
@ObjectModel.text.association: '<text_association> in the projected view.
You can then include the text element via the association in the projection view. The relationship between
identifier elements and the respective text is defined in the projection view via the annotation
@ObjectModel.text.element: '<text_element>', see CDS Projection View [page 143]. If the keyword
localized is used, the text in the system log-on language is drawn.
The following diagram illustrates the modeling of text denormalization in projection views.
● The text provider view has a key element indicating the language of the text.
● The projected CDS view has an association to the text provider view, but does not use the annotation
ObjectModel.text.association.
Procedure
1. Include the text element from the text provider view into the projection view.
2. Annotate the element in the projection view, for which you want to provide the text with
@ObjectModel.text.element: <text_element> and reference the text element.
3. To get language-dependent texts use the keyword localized on the text element.
Example
The text provider view selects from a database table, in which the description texts in different languages
are stored.
Projected View
Projection View
The projection view denormalizes the supplement description and filters the relevant values based on the
requested language.
Note
It depends on the language configuration of your cloud system, which language is allowed.
If you create a UI service for this example, you can check the results when sending the request in different
languages.
Related Information
The implementation of a value help in CDS enables the end user to choose values from a predefined list for
input fields on a user interface.
You can define value helps in the CDS data layer for any input field on the UI (for example, selection fields,
parameters, or writable fields in transactional scenarios) . A value help is useful when the values for the input
field have to be taken from a predefined set of values. When you call the value help, it displays all the available
values from the value help provider. It appears on the UI when you choose the button in the input field or
Example
To help the end user to enter the right customer ID to create a new booking, the application developer
defines a value help that enables the user to enter the name or any other element from the value help
provider to help find the correct number. The value help provider view in this case is a CDS view that
manages customer information. As shown below, the end user is searching for a particular customer ID.
The value help offers to filter by the customer last name, so that the end user can choose from the available
entries. The value of the customer ID field is then transferred to the respective input field.
Expand the following figure to view the procedure of calling the value help on a Fiori Elements UI.
For the default implementation of the value help, you can use any CDS entity that contains the desired values of
the element for the input field. You do not need to explicitly define a CDS entity as the value help provider.
However, the value help provider must be exposed for the OData service to make use of the value help.
Any annotation that is maintained in the value help provider is evaluated. This means that associated
entities, value helps, and text associations of the value help provider view are also displayed and can be
used in the value help. This means that you can have a value help in the value help.
The following value help options are available within the programming model:
Related Information
A simple value help is convenient if you want to display values from the value help provider for an input field.
Context
You want to provide a value help for an input field on the UI.
The following steps implement a value help for a customer ID field, using a booking CDS view as an example.
Procedure
1. Create a data definition for a CDS view that serves as a value help provider. It must contain a field with the
available values for the input field in the source view.
Example
The value help provider view contains the customer ID and fields to identify the customer ID, such as
the customer's name or address. The end user can then filter by these fields to find the right customer
ID.
To retrieve the full value list when invoking the value help, annotate the vale help view with the annotation
@Consumption.valueHelpDefault.fetchValues: #AUTOMATICALLY_WHEN_DISPLAYED. To
explicitly disable loading the full list, use @Consumption.valueHelpDefault.fetchValues:
#ON_EXPLICIT_REQUEST.
2. In your CDS source view, add the following annotation to the element for which you want to provide the
value help on the UI.
The annotation defines the binding for the value help to the value help providing entity. You must specify
the entity name and the element providing the possible values for the annotated element.
Example
The following code example shows how an annotation is used on the element CustomerID in /DMO/
I_Booking. It references the value help provider view (/DMO/I_CUSTOMER_VH) for the customer ID
and the element providing the possible values (customer_id) in the value help provider view.
Results
If you expose the source view in an OData service, the value help provider view is automatically exposed with it.
You do not have to list value help provider views in the service definition.
On a Fiori UI, choosing F4 in the selection field opens a search mask and the end user can filter by any field in
the value help provider view. Selecting an entry transfers the value of the element that is referenced in the
annotation to the annotated element in the source view.
The metadata of the OData service displays the value help implementation for the following properties:
● The property in the CDS source view for which a value help is implemented (sap:value-
list="standard")
● The value help provider entity type (sap:value-list="true")
Expand to view the extracts of the metadata document of a service exposing a booking CDS view and the
value help provider for the element CustomerID.
Restriction
If you associate the value help provider to your source view, the metadata of the value help provider view do
not have sap:value-list="true". As a result, the request <service_url>/$metadata?sap-value-
list=<service_namespace>.<entity>Type/<element> (where <element> is the element that is
referenced in the annotation @Consumption.ValueHelpDefinition in your CDS view) does not work.
This can lead to problems when invoking value helps.
Any annotation that is maintained in the value help provider is evaluated. This means that the following
capabilities are possible for the value help:
● Associations: If the target of the association is included in the OData service, the elements of entities
associated with the value help provider are also displayed as additional input fields.
● Search Capabilities: Including search capabilities in your value help provider enables the end user to
search for any detail in an input field.
● Value Helps: The value help can itself contain value helps.
● Text: Text that is provided for the value help provider is also displayed.
If you want to show the possible values for the input field as a dropdown list only, instead of a search mask with
all other elements of the value help provider, annotate the value help provider with @ObjectModel :
{ resultSet.sizeCategory: #XS } on the view level.
For dropdown value lists, the OData $metadata document includes the annotation sap:value-list=
"fixed-values" instead of standard on the property in the source view for which the value help is
implemented.
The UI displays the possible values for the input field in a dropdown list. The following image shows a value help
list for the carrier ID field in the booking scenario.
The UI displays the possible values for the input field while typing values from the value help provider:
To enable the typeahead functionality enable the value help provider entity with search capabilities. This is
done by using the annotation @Search.searchable: true at value help level. The value help typeahead only
finds values from the value help provider that are annotated with @Search.defaultSearchElement: true.
Example
If you want to display customer information of customers with first names, last names and cities starting
with V, the corresponding elements in the value help provider entity must be annotated with
@Search.defaultSearchElement: true.
To limit the value help list to certain elements, annotate the elements that you want to display with
@UI.lineItem.importance: #HIGH. Then, the other elements with lower importance are not taken into
consideration for the value help list.
Example
The following code example shows how the annotation is used on the element CustomerID in /DMO/
I_Booking. It references the association to the value help provider view (_Customer).
Note
When using the option of consuming value helps via an association, it is not ensured that the value help
works properly, see Restriction [page 554].
Context
It is possible to provide more than one value help on one element. The end user selects which value help to use
to find the correct value.
The following image displays the value helps for the carrier ID element in the booking CDS view. One value help
provider is defined by a carrier CDS view and one by a connection CDS view that also contains the carrier ID
field.
Procedure
1. To implement two value helps on one element, proceed as described in Simple Value Help [page 552] and
add another entity as a value help provider.
You can define more than two value helps on one element.
2. Assign labels to the different value helps to differentiate them on the UI.
If you have more than one value help, it is important that all except one are equipped with a qualifier. The
default value help is the one without a qualifier. The qualifier marks the value helps that are less important.
If all value helps are annotated with the qualifier argument, then none of them are displayed as there is no
default.
Results
Choosing F4 in the input field opens a search mask with the fields of the default value help. The default value
help is the one without a qualifier. The end user can select which value help to use from a dropdown menu.
A preset filter condition can be established by an additional binding for the value help.
Context
You use an additional binding to define more than one binding condition between the source view and the value
help provider. The value help is then automatically filtered by the additional binding. It proposes only entries
that match the additional binding. This additional binding can either be another element or a parameter. These
need to be present in the source view and in the value help provider view. When an entry is selected in the value
help provider, the values of both binding elements are transferred to the input fields of the source CDS view.
Example
In our booking scenario, we can apply the value help with an additional binding on the field ConnectionID.
The value help provider is a view that manages connections. This value help provider contains not only the
field for the connection IDs, but also a field for carrier IDs, which is also in the consumer view. We can
establish a second binding condition so that the value help provider only displays connections with the
prechosen carrier ID.
Procedure
1. Create a data definition for a CDS view that serves as a value help provider. It must contain a field with the
available values for the input field in the source view. In addition, it must contain the field for which the
additional binding is established.
The value help provider view contains the connection ID and the carrier ID, for which a mapping condition
is defined.
2. In your CDS source view, add the following annotation to the element for which you want to provide the
value help on the UI.
The additional binding defines a second condition for the value help on the same target value help provider
entity for filtering the value help result list and/or returning values from the selected value help record. The
additional binding can be defined for an additional element or parameter. Depending on the value provided
in usage, the additional binding works as a filter, result or filter and result condition:
○ #Filter: The value of the referenced element or parameter in localElement or localParameter is
used as a filter for the value help. The value help only displays filtered results.
Note
For numerical data types of local elements, the value help is always filtered for 0 (initial value) if the
local elements are empty.
The following code example shows the usage of the annotation on the element ConnectionID in /DMO/
I_Booking. It references the value help provider view (/DMO/I_Connection_VH) and the element
providing the possible values (connection_id) in the value help provider view, as well as the matching
condition on the elements CarrierID and carrier_id in the consumer view and the value help provider
view, respectively.
Results
Choosing F4 in the selection field opens a search mask and the end user can display all available entries in the
value help provider or filter by a field, for example, the destination airport. If the carrier ID is already filled in the
The metadata of the OData service displays the value help implementation on the following properties:
● The property in the CDS source view for which a value help is implemented (sap:value-
list="standard")
● The value help provider entity type (sap:value-list="true")
● The value help provider entity is marked as Target in the Annotations property. The value list
enumerates every property in the value help provider that is exposed for the value help (Annotation
Term="Common.ValueList").
● The elements that are defined in the mapping conditions are marked as inbound and outbound parameters
Record Type="Common.ValueListParameterInOut".
Restriction
If you associate the value help provider to your source view, the metadata of the value help provider view do
not have sap:value-list="true". As a result, the request <service_url>/$metadata?sap-value-
list=<service_namespace>.<entity>Type/<element> (where <element> is the element that is
referenced in the annotation @Consumption.ValueHelpDefinition in your CDS view) does not work.
This can lead to problems when invoking value helps.
To avoid this clash, separate your value help information in value help provider views that do not coincide
with other associated views, for example text provider views.
The descriptions in this topic refer to the range of functions for text and fuzzy searches that are provided in the
context of SAP HANA.
The full text searching (or just text search) provides you with the capability to identify natural language terms
that satisfy a query and, optionally, to sort them by relevance (ranking) to the query. The most common type of
search is to find texts that contain the term specified and return them in the order of their similarity to these
terms.
Fuzzy search is a fast and fault-tolerant search feature of SAP HANA. The basic concept behind the fault-
tolerant search is that a database query returns records even if the search term (user input) contains
additional or missing characters, or even spelling errors. Fuzzy search can be used in various applications -- for
example, to trigger a fault-tolerant search in a structured database content, like a search for a product called
'coffe krisp biscuit' and you find 'Toffee Crisp Biscuits'.
Within the context of the ABAP RESTful programming model, you only need to enable the text and fuzzy search
functionality in your data model definitions. For this purpose, you implement it in designated CDS views using
appropriate text and fuzzy search annotations (listed below).
Note
As an application developer however, you must ensure that your CDS views are suitable for text and fuzzy
search enabling. For more information take a look at the corresponding topics in the SAP HANA Search
Developer Guide .
As the name suggests, search annotations enable the search feature on the CDS view elements.
First of all, you need the following CDS annotation at the view level:
@Search.searchable: true/ Defines whether a CDS view is generally relevant for search scenarios. This anno
false tation provides a general switch and a means to quickly detect whether a view is
search-relevant or not. Set to value true in order to enable search support by
means of @Search annotations. Here, at least one view field must be defined as
@defaultSearchElement at element level.
All view elements that are annotated for the default search define the search
scope. (The search will be performed on all elements that have this annotation.).
Caution
Such a search must not operate on all elements – for performance reasons
and because not all elements qualify for this kind of access.
@Search.fuzzinessThreshold This annotation specifies the least level of fuzziness the element has to have in
: <value> order to be considered in a fuzzy search at all.
The <value> defines the threshold for a fuzzy search (how fuzzy scores are cal
culated when comparing two strings or two terms).
Possible values are: 0..1 The default value is 1. The fuzzy search algorithm cal
culates a fuzzy score for each string comparison. The higher the score, the more
similar the strings are. A score of 1.0 means the strings are identical. A score of
0.0 means the strings have nothing in common.
@Search.ranking: <value> This annotation specifies how relevant the values of an element (view field) are
for ranking, should the freestyle search terms match the element’s value.
● HIGH - The element is of high relevance; typically, this is useful for IDs and
their descriptions.
● MEDIUM - The element is of medium relevance; designated usually for im
portant elements.
● LOW - Although the element is relevant for a freestyle search, a hit for this el
ement has no real significance for the ranking of a result item.
Tip
For the fuzzy search threshold, we recommend using the default value 0.7 to start with. Later on, you can
fine-tune the value based on your experiences with the search. You can also fine-tune the search using
feedback collected from your users.
Example
The listing below implements a search model for searching products. The model definition results from the
data source db_pd that already specifies the persistence layer for searching. The data source provides product
data and text reference fields.
The annotation @Search.searchable: true marks the view as searchable. In addition, the elements Name
and Category are annotated with @Search.defaultSearchElement: true. This means that a freestyle
search is enabled on the search UI where it is possible to search for the annotated elements. The annotation
@Search.fuzzinessThreshold: 0.7 (0.8) defines that the text search should be applied to the element
Category with a similarity value of 70% and to the element Name with a similarity value of 80%.
...
@Search.searchable : true
@Search.defaultSearchElement : true
@Search.defaultSearchElement : true
@Search.fuzzinessThreshold : 0.7
@Search.ranking : #LOW
pd_category as Category
}
Results
If you expose a CDS view with search annotations for an OData service, the OData entity set receives the
annotation sap:searchable: true.
The following image display a Fiori UI that consumes an OData service with the example CDS view.
Standard filter allows to search for product category and product name
Related Information
This topic explains how you can provide aggregate data for your SAP Fiori application. The available aggregate
functions operations are sum, minimum, maximum, and average. Alongside this, the framework also provides
options for counting.
We speak of aggregate data when numerical values are combined to form a single value that signifies meaning.
General assumptions can be drawn from this value that is representative for all values that were included in the
calculation of the value.
● sum
● minimum
● maximum
● average
These functions determine a value from which you can assume information relating to all the values that were
included in the calculation.
Apart from these functions, there is also a counting function available to count distinct values:
● distinct count.
This counting option determines a natural number based on the number of entries in the calculation.
All of these functions are supported and evaluated by the SADL framework.
Aggregate data calculated by the SADL framework provides additional and enhanced information for your list
reporting app.
To display aggregate data in your application, annotate the respective elements in CDS with the annotations
described in Annotating Aggregate Functions in CDS [page 566]. These annotations cause the CDS entity to
be respected as an aggregated entity by OData. A thorough description of how OData interprets the
annotations is provided in OData Interpretation of Aggregation Annotations [page 569].
Note
Aggregate functions are not supported in scenarios with projection CDS views. Aggregation annotations in
projection view cause an error on service compilation. Aggregation annotations in projected entities are
ignored.
Based on the entity that calculates aggregate data, the SAP Fiori user interface displays aggregate data
depending on the settings you choose. The aggregated values are displayed in your list reporting app as an
additional field in the relevant column.
The elements for which you want to display aggregate data in your SAP Fiori App must be annotated in the CDS
entity with the relevant annotation for the aggregate function.
The annotation @Aggregation.Default: #<AGGR_FUNCTION> enables the aggregation of the values of the
annotated element.
Only measures can be annotated with an aggregation annotation. Measures are elements that either represent
numerical values, which means they can be summed, averaged, or otherwise mathematically manipulated.
Typically, measures are units that express the size, amount, or degree of something, for example prices. In
addition, elements with date values, can also be compared with each other to determine the maximum or
minimum. That means date values can also be measures for calculating the minimum or the maximum.
The other elements in a CDS entity are called dimensions. Dimensions provide structured labeling information
about otherwise unordered numeric measures. They are relevant for the grouping and the order of the
elements in the Fiori App.
The SADL framework supports the following aggregating functions for measures. As soon as one element is
annotated with one of these annotations, the CDS entity is considered an analytical entity.
Note
Only elements with numerical data types can be annotated with @Aggregation annotations.
@Aggregation.Default: #SUM Calculates the sum of the values of the annotated element.
@Aggregation.Default: #MAX Calculates the maximum of the values of the annotated element.
You can also annotate elements with date types with this annotation.
@Aggregation.Default: #MIN Calculates the minimum of the values of the annotated element.
You can also annotate elements with date types with this annotation.
@Aggregation.Default: #AVG Calculates the average of the values of the annotated element.
Note
Since the distinct count of the referenced element is naturally 1
for single data records, the value of the annotated element is 1
regardless of the actual value of the element. In grouped records
the annotated element counts the distinct values of the refer
enced element. So the actual value of the annotated element is
ignored.
Example
@Aggregation.referenceElement:
['CustomerID']
@Aggregation.default: #COUNT_DISTINCT
cast( 1 as abap.int4 ) as
DistinctCustomers,
Only one aggregate function can be used on one element. You cannot display different aggregated values of the
same element.
In a Fiori Elements UI, records are always grouped by the dimensions that are selected by $select in an OData
request. The elements for $select in OData requests are the column elements that are selected in the Fiori UI.
If you want to suppress the behavior, that the records are grouped automatically if not all key elements are
selected, you can use a @UI annotation that triggers the key elements to be always selected implicitly.
@UI.presentationVariant: Defines the properties that must always be included in the result of
[{requestAtLeast: the queried collection if no grouping is desired.
['<TECHNICAL_KEY>']}]
The UI offers the option to group explicitly. On the UI, go to settings and define the dimensions to group by or
click on a dimension column and choose Group.
For more information on grouping, see OData Interpretation of Aggregation Annotations [page 569].
Example
The following listing displays a CDS entity in which all the necessary annotations for analytical operations are
used. This view describes sales order combined with associated product and customer information.
Note
Aggregate functions only respect values with the same semantics. This means that, if you have prices in
different currencies that are annotated with an aggregation annotation, you receive aggregated data for
each currency.
The following figure displays the maximum tax amount with regard to the respective currency.
Related Information
The following sections provide an overview of the most prominent features of aggregated entities in OData.
Data models with aggregation annotations are considered as aggregated entities by OData. The behavior of
these aggregated OData entities differs from non-aggregated entities.
The OData entity is given multiple annotations based on the aggregate annotation you use in CDS for your data
model. The following figure displays the metadata of an aggregated entity that processes sales order items.
The annotations specific to aggregations are highlighted and labeled. Further descriptions of the annotations in
OData are given below.
In the example of the screenshot above, this OData annotation can be found in the first line of the extract of the
metadata.
Measures
The aggregated entity is characterized by measures and dimensions. Measures are those properties that are
annotated with the annotation relevant for aggregating data in CDS. Measures are given the OData annotation
sap:aggregation-role=”measure”.
In the example of the screenshot above, there are six properties which are marked as measures:
ConvertedGrossAmount, GrossAmount, NetAmount, TaxAmount, AllItems, and DistinctProducts.
Dimensions
Dimensions are all properties that are neither marked as measures nor as attributes. Dimensions are given the
OData annotation sap:aggregation-role=”dimension”.
In the example of the screenshot above, there are six OData properties which are marked as dimensions:
SalesOrderID, ItemPosition, Customer ID, Product, CurrencyCode, and TargetCurrency.
In the example of the screenshot above, the dimension Product has a text property.
An aggregated OData entity is given an additional property for a generated ID (<Property Name="ID"/>).
The generated ID for the aggregate entity uniquely identifies each record so that every entity request for a given
ID always returns the same values.
In the example of the screenshot above, the property for the generated ID can be found as the first property on
the list.
The ID is also generated for every group record when it is requested for the first time. Following from this, you
can use this ID in a further request.
Example
This behavior is exemplified by a request on the entity that supplies the metadata above. It retrieves sales
order items. The following request selects the generated ID ( ID), the dimension Product, and some
aggregated measures related to the selected dimension.
.../sap/opu/odata/SAP/<service_name>/ZDEMO_C_SOI_ANLY?
$select=ID,Product,GrossAmount,NetAmount,TaxAmount,AllProducts
Each group record is given its own generated ID which retrieves the same results when requested again.
Based on this group ID, you can also execute other requests, as in /sap/opu/odata/SAP/
<service_name>/ZDEMO_C_SOI_ANLY('.4~HT%26c1002.6~USD')?$select=AllProducts, which
will only retrieve the count of this group, which is 9.
Results of requesting data from aggregated entities depend on the elements you select in your OData request.
Grouping and aggregation are both driven by the elements you request with the parameter $select in entity
set queries. The result of a query consists of aggregated entities with distinct values for the requested
dimension properties and requested measures are aggregated using the aggregate function with which the
measure elements are annotated in CDS.
If an attribute is requested, the result is grouped by its corresponding dimension and within that group it is
grouped by the attribute itself.
Note
If you use a SAP Fiori app, the $select statement of the OData request directly depends on the columns
you select in the list reporting app. The following descriptions of requesting data with OData can also be
managed by selecting the respective columns in your Fiori App.
Note
Each group record is given its own generated ID, which can be reused in requests to retrieve the same
results.
Only if no dimension and no attribute are requested does the result show the aggregate data of measures for
the whole entity set.
You can still execute other query options, such as $filter or $orderby on grouped requests. The filtering is
executed before the grouping, so that the groups are created from the available records after the filtering. The
ordering is executed after the grouping, which means the records within a group are ordered according to the
query option.
Note
You can filter for properties that you do not select, but you cannot order by properties that are not included
in the $select due to the order of executing query options mentioned above.
Assume you have an aggregated entity of sales order items and you group them by product. From this
group record, you can navigate directly to the associated entity of products if the association exists in CDS.
The same holds true for the query option $expand. Only properties that are selected can be expanded.
Note
The target entity of the navigation or the expand is also given an aggregated ID if the underlying data model
is also aggregated.
Related Information
With virtual elements, you define additional CDS elements that are not persisted on the database, but
calculated during runtime using ABAP classes that implement the virtual element interface.
For some business use cases, it may be necessary to add new elements to the data model of an application
whose values cannot be fetched from the database. Virtual elements are used if these elements are not
provided as part of the original persistence data model but can be calculated using ABAP resources during
runtime.
Virtual elements represent transient fields in business applications. They are defined at the level of CDS
projection views as additional elements within the SELECT list. However, the calculation of their values is
carried out by means of ABAP classes that implement the specific virtual element interface provided for this
purpose. The ABAP implementation class is referenced by annotating the virtual element in the CDS projection
view with @ObjectModel.virtualElementCalculatedBy: 'ABAP:<CLASS_NAME>'. With data retrieval
via OData, the query framework calls the ABAP implementation class of the virtual element to retrieve the
value for it.
The OData service metadata do not differentiate between regular CDS elements with database persistence and
virtual elements. Consequently, the virtual element appears in an application UI equal to any other element.
Note
Example
In an application that processes flight bookings, we want to include a field that calculates how many days are
left until the flight date, or how many days have passed since the flight date. The following booking app UI
provides the booking information with an additional field Days to Flight that calculates the days that have
passed since the flight or the days that are left until the flight. The value is calculated by comparing the flight
date with the system date.
Developer Activities
1. Adding the virtual element and corresponding annotations to the relevant CDS projection view.
Virtual elements are defined in the select list of CDS projection views. Their values are calculated in a
referenced ABAP class. This topic explains how you can model virtual elements in CDS.
Context
A virtual element is declared in the CDS projection view. It enables you to have additional fields that are not
persisted on the database in your application scenario. You can also have different virtual elements in different
BO projections. With those, you are flexible in providing an accurately tailored projection layer for one specific
service without affecting the basic business object.
You can use virtual element for read-only, as well as for transactional scenarios.
The following steps model a virtual element in a CDS projection view using the CDS view /DMO/I_BOOKING_U
as an underlying CDS view. The projection CDS view defines the subset of the CDS view that is relevant to the
respective service and is extended with a virtual field for this service.
Explanation
To define a virtual element, use the keyword virtual and specify a name for the virtual element. Aliases for
virtual elements are not allowed, as you can choose the name of the element freely. Since a virtual element
does not have database persistence and therefore does not have a defined data type, you have to specify the
type of the element. Predefined data elements can be used as well as ABAP types.
For more information about the syntax in CDS projection views, see CDS Projection View [page 143].
As the @ObjectModel annotation references an ABAP resource from CDS, you must use the exact syntax
'ABAP:<class_name>' as the value for the annotation. No space between colon and <class_name> is
allowed.
If you use a basic ABAP type for your virtual element, define end-user information for the element with the
@EndUser.Text annotation.
The label you choose for the end-user text is used in the metadata of an OData service that uses the CDS
views. It is also used as a fallback if no label is maintained in @UI annotations to display the element on a UI.
If the virtual element uses a predefined data element, the OData metadata deduces the information from the
data element.
Example
Next Steps
1. Creating and implementing an ABAP class that implements the virtual element contract.
More on this: Implementing the Calculation of Virtual Elements [page 576].
The values of virtual elements are calculated in a referenced ABAP class. This topic explains how you calculate
their values by means of an example implementation.
Context
By using a virtual element in a CDS projection view, you define an additional field for your OData service. As this
additional field is not persisted in the database layer, its value must be calculated during runtime. The
calculation is implemented in an ABAP class that is referenced in an annotation on the virtual element in the
CDS projection view.
The calculation class must implement the interface IF_SADL_EXIT_CALC_ELEMENT_READ. This interface
provides two methods for the calculation implementation:
Prerequisites
● The virtual element is modeled in the CDS projection view as explained in Modeling Virtual Elements [page
575].
● The virtual element uses the annotation @ObjectModel.virtualElementCalculatedBy which
references an existing ABAP class.
Procedure
1. Add the interface IF_SADL_EXIT_CALC_ELEMENT_READ to the public section of your calculation class
and add the two method implementations.
Example
Example
At first, we check if the requested entity is the entity that contains the virtual element for which the
calculation is intended. To calculate the remaining time or the passed time compared to the flight date,
we need the element FlightDate. This element is added to the requested elements list to ensure that
the value is available to calculate the virtual element.
Note
The methods of the interface IF_SADL_CAL_ELEMENT_READ import and export their parameters as
strings in upper case. The CDS entity and element names must therefore be in upper case as well, so
they can be mapped correctly.
Example
In our example, we want to calculate how many days are left before the flight or how many days have
passed since the flight, so we compare the value of FLIGHTDATE with today's date to calculate the
value for the virtual element.
ABAP is able to calculate with date types as long as the date context is clear. The actual calculation is
therefore quite easy: We just subtract the today's date from the flight date. The returning parameter
can then be filled with the calculated days to flight.
METHOD if_sadl_exit_calc_element_read~calculate.
DATA(lv_today) = cl_abap_context_info=>get_system_date( ).
DATA lt_original_data TYPE STANDARD TABLE OF /dmo/c_booking_proc_ve
WITH DEFAULT KEY.
lt_original_data = CORRESPONDING #( it_original_data ).
LOOP AT lt_original_data ASSIGNING FIELD-SYMBOL(<fs_original_data>).
Related Information
In managed scenarios with UUID keys, the values for primary key fields can be automatically generated by the
managed runtime framework.
Context
Values for primary key fields can be generated automatically for managed business objects. This so-called
Managed Early Numbering [page 81] is defined in the behavior definition of the business object. During the
CREATE operation, the managed runtime framework draws the UUID automatically for the defined key fields.
The newly created BO instance is immediately uniquely identifiable after the CREATE operation and saved with
the given key during the save sequence.
Prerequisites
● The primary key field has the ABAP type raw(16) (UUID).
● The implementation type of the RAP BO is managed.
You define managed early numbering for primary key fields in the behavior definition by using the following
syntax:
The key field must be read-only if you intend the key fields to be filled internally only. If you do not set the key
field to read-only, the key value can also be given by the consumer. However, use cases for this optional
external numbering [page 82] are rare, as it is untypical for the consumer to fill in a UUID value.
Example
The following simplified BO exemplifies the use case to have the framework generate a UUID key for a managed
BO.
@AbapCatalog.sqlViewName: '/dmo/itraveluuid'
@AbapCatalog.compiler.compareFilter: true
@AbapCatalog.preserveKey: true
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Root View with Travel UUID'
define view /dmo/i_travel_uuid as select from /dmo/travel_uuid
{ ///dmo/travel_uuid
key travel_uuid,
travel_id,
agency_id,
customer_id,
begin_date,
end_date,
@Semantics.amount.currencyCode: 'currency_code'
booking_fee,
@Semantics.amount.currencyCode: 'currency_code'
total_price,
@semantics.currencyCode: true
currency_code,
description,
overall_status,
@Semantics.user.createdBy: true
created_by,
@Semantics.systemDateTime.createdAt: true
created_at,
@Semantics.user.lastChangedBy: true
last_changed_by,
@Semantics.systemDateTime.lastChangedAt: true
last_changed_at
}
managed;
define behavior for /dmo/i_travel_uuid alias Travel
implementation in class /dmo/bp_i_travel_uuid unique
persistent table /DMO/TRAVEL_UUID
lock master
etag master last_changed_at
{
field ( read only, numbering : managed ) travel_uuid;
field ( read only ) travel_id;
create;
update;
delete;
//** optional determination to determine the travel ID
determination CreateKeys on modify { create; }
}
Note
To ensure that one travel ID is only assigned once, you can use a number range object.
The following code example does not show how to get this new travel ID. It is just suggested by the
method call of get_number.
5. UI Preview
The following figure shows the creation of a new travel instance when using managed numbering for the
UUID key field and a determination to assign the semantic identifier on CREATE.
For this example to run like it is shown below, you must add UI annotations to the elements in the CDS view.
Note
This example scenario to automatically draw UUID values for primary key fields is highly simplified.
Basic important elements of a travel scenario, such as eTag or BO projection, are left aside to focus on
the principle of managed early numbering.
Related Information
Use Case
You can use this kind of mapping in applications that include unmanaged or managed business objects based
on CDS entities on the one hand, and legacy data types (ABAP Dictionary types) (and functionality) that are
generally older.
This is particularly significant for the unmanaged implementation type, which essentially represents a kind of
wrapper for existing legacy functionality.
Also with the managed implementation type, it can happen that, for example, the code for a determination or
validation already exists, but is based on "old" (legacy) data types.
When accessing the legacy code, the developer would normally have to use "corresponding with mapping" in
many places to map input derived types [page 951] (type table for create, type table for
In addition, the types used to represent the control information also can differ significantly in the legacy code
and the current ABAP programming model:
In some legacy scenarios (especially the ones using BAPIs), in addition to the dictionary type directly
corresponding to the entity, there is another one that contains (ideally) the same fields as that type, but these
all have the type C(1) (like the control structures in BAPIs). This type has the same function as the %CONTROL
[page 877] structure in derived types [page 951], which indicates that fields in the main structure that are
accessed by an operation (update, read, and so on) Such type pairs are often used in BAPIs, for example
BAPIAD2VD/BAPIAD2VDX. The control data element is BAPIUPDATE with the type C(1).
The solution is now to introduce a central, declarative mapping within the behavior definition instead of many
individual corresponding statements. This improves maintainability of the application's source code.
A mapping sets the entity in relation to any other structured ABAP Dictionary type (legacy type). It is
introduced in the behavior definition for an unmanaged [page 963] or managed [page 956] implementation
type with the keyword mapping for.
{
mapping for ...
...
The addition corresponding automatically maps fields of the same name to each another. The
corresponding fields of different names can be specified through explicitly listed field pairs.
The corresponding addition can also be omitted, which is not recommended in general, because then the
automatic mapping of fields with the same name is lost.
Note
Mapping can be partial (legacy type contains fields that do not match any fields in CDS).
Usually, it is assumed that the fields in the main and control type have the same name.
Note
For all control type fields, the type C(1) or X(1) is expected.
The reference to mappings that are defined in a behavior definition is done by some special variants of the
ABAP corresponding operator.
data:
legacy_var type LegacyType,
entity_var type strucure|table for create|update|delete entity.
...
legacy_var = corresponding #( entity_var mapping from entity ).
This mapping works fine if the type of entity_var is an input derived type, or the entity type itself, or the table
type of the entity.
data:
legacy_var type LegacyType,
entity_var type table for read result entity.
...
entity_var = corresponding #( legacy_var mapping to entity ).
The addition to entity assigns legacy_var field-wise to data object entity_var according to the
mapping definition for LegacyType in the behavior definition.
This mapping works fine if the type of entity_var is an output derived type, or the entity type itself, or the
table type of the entity.
Control mapping is about supporting the actual semantics of the %control fields: They indicate which of the
fields should be, for example, changed by an update or read by a read operation.
For the move between an input derived type (type table for create, type table for action
import) and a target structure, this means that only the fields for which the corresponding control field is set
should be moved.
The structure or table source must be a derived type that also includes the %control structure.
For example, if the source has a field amount, then %control-amount decides whether to move the value of
amount to the corresponding field of target – only if the value of the corresponding control field is non-
initial.
By combining with the additional mapping from entity described above, also field mapping specified in the
behavior definition is effective:
Related Information
This section introduces the concept of groups that can be used to divide operations, actions and other
implementation-relevant parts of the business logic into several groups for behavior implementation.
Use Case
Generally, the implementation of business object entity's operations and actions is done in the Local Types
include of the behavior pool (ABAP class that implements business object’s behavior) associated with that
entity – unless the control of the implementation classes using the IMPLEMENTATION IN CLASS syntax (at
entity level) has been completely dispensed.
Since the Local Types include can only be changed by one developer at a time, the efficiency of development
would be significantly reduced in case of larger implementations. Let us think about large business objects with
extensive business logic implementations, with many entities, each of which may contain a variety of elements.
As a solution, the operations and actions of an entity can be divided into several groups, whereby each of these
groups can then be assigned a different behavior pool as implementation class.
Especially in large development projects the responsibilities for the implementation of application logic, are
assigned to different members of a development team. To support this approach technically, we introduce the
concept of groups. This approach enables that a team of developers can implement parts of business logic
independent from each other. In addition, the group concept allows to tailor the functionality of business
objects according to semantic considerations.
Groups can be defined within a behavior definition for a business object of type unmanaged by using the
following syntax:
Groups can be defined within a behavior definition for a business object of type managed by using the following
syntax:
Explanatory Notes
(1) The group name is defined locally within the entity’s behavior definition and must not conflict with actions,
determinations, or validations of the same name.
(2) The implementation in class syntax can only be used on groups, but no longer on individual entity’s
behavior definition itself. A group requires a behavior pool ABAP_CLASS_* and the addition of unique.
Note
With the addition implementation in class ABAP_ClASS in the header of the behavior definition, you
have the option to implement the remaining functionality for all entities in a common behavior pool. For
example, the save sequence for all entities of a business object could be implemented in a single behavior
pool ABAP_CLASS.
It is possible to specify the same behavior pool as the implementation class in different groups. In the syntax
above, the implementation-relevant content of groupName_1 and of groupName_3 must be implemented in
ABAP_CLASS_1.
● Actions
● Instance-based feature control
● Determinations - for managed implementation type only
● Validations - for managed implementation type only.
Note
In the case of unmanaged implementation type, the standard operations (CREATE, UPDATE, DELETE)
as well as READ and CREATE by association must be assigned to a group. In the managed case however, the
standard operations (that are implemented by the framework) and READ and CREATE for associations can
be specified either inside or outside groups.
(4) Information on mapping (which is never relevant to implementation) must always be specified outside of
groups.
(5) Implicit standard operations (defined automatically and did not have to be explicitly specified) must be
explicitly specified within one and the same group in the unmanaged case (where they are implementation-
relevant):
Examples
}
}
// behavior defintion for the BOOKING child entity
define behavior for /DMO/I_Booking_U alias booking
lock dependent ( travel_id = travel_id )
{
}
}
// behavior definition for the BOOKING child entity
define behavior for /DMO/I_Booking_M alias booking
{
read;
update;
delete;
mapping for /dmo/booking
{
...
}
group booking_fc implementation in class /dmo/bp_booking_fc unique
{
field ( read only ) TravelID, BookingID;
field ( mandatory ) CustomerID, AirlineID, ConnectionID, FlightDate;
action(features:instance) confirmBooking result [1] $self;
}
group booking_det_val implementation in class /dmo/bp_booking_det_val unique
{
determination totalBookingPrice on modify { field Flight_Price; }
determination determineCustomerStatus on modify { create; }
// internal action: triggered by determination
internal action SetCustomerStatus;
}
}
(1) The name of the group and which operations are associated with this group do not matter to external users
(and can therefore be changed retrospectively by the developer). This means that external operations and
actions are still accessed by the usual syntax, in which only the name of the entity, the operation, and, if
applicable, the action/determination/validation or association plays a role, but not the name of the group.
However, there are exceptions: the name of the group is relevant for the implementation of instance-based
feature control - the corresponding implementations then control only those features that are associated with
their respective group. (The framework merges the information for the external consumers.) The
corresponding syntax entity-group can only be used within the implementation class associated with that
group. Specifically, the following declarations are concerned:
Note
This declaration can also be done in the public section of the implementation class to make the group-
dependent type public outside. Because it makes the changes to group assignment incompatible with
external users, such publishing is not recommended.
Within the implementation class, the syntax methods ... for features for instance-based feature control
can only be defined by specifying the group name:
(2) Because associations with the usual association syntax can only be assigned as a whole to a group, it is not
possible to implement the association's CREATE operation in an implementation class other than the READ
operation.
Business objects can be consumed not only by means of the OData protocol (for example, in Fiori UIs) but also
directly in ABAP by using Entity Manipulation Language (EML).
This topic offers some code samples to demonstrate how you can access our Travel business object with EML
syntax in a simple consumer class. You will get to know the core structure of EML at this point.
Contents:
EXAMPLE 1: Implementing UPDATE for Travel Data [page 593]
EXAMPLE 3: Implementing DELETE for Travel Instances and Their Child Instances [page 596]
EXAMPLE 4: Creating Instances Along the Composition Hierarchy ("deep create") [page 597]
In this example, two fields agencyid and the memo text should be changed to a given travel instance.
Prerequisites
The entity instances can only be updated in a MODIFY call if the update operation is specified for each relevant
entity in the behavior definition and is implemented in the behavior pool accordingly.
Because the change will only affect one entity, we use the short form of the MODIFY syntax:
The following listing provides you with the source code of an executable consumer class. To enable the classrun
mode, the consumer class implements the if_oo_adt_classrun interface.
Since the result data is not exported as part of the UPDATE statement, a subsequent READ ENTITY call is
required to read the changed data from transactional buffer. The result data of the read operation is specified in
the target variable lt_received_travel_data.
Checking Results
To check the results of the MODIFY call, run the main method of the consumer class /dmo/
cl_eml_travel_update by pressing F9 key and view the received RESULT data
(lt_received_travel_data) on the console output. This data contains all fields of the target travel instance
(in this example, with the travel ID = 11).
All modify operations in EML that cannot be implemented by standard operations (create, update, delete) are
handled by actions.
This example demonstrates the implementation of an action related to a given travel instance. The consumer
class (see Listing 2 below) is implemented to change the status of the travel processing to booked.
Prerequisites
The SET_STATUS_BOOKED action is specified in the behavior definition at the root entity level and is
implemented in the behavior pool accordingly.
The MODIFY statement uses the following general syntax for executing an action:
The action_name refers to the name of an action as it is defined in the behavior definition for the
corresponding entity. The input parameter it_instance_a contains the keys of the entities on which the
action has to be executed.
The syntax for executing an action allows exporting of result data. The result data of an action execution is
specified in the target variable result_action.
The following listing provides you with the source code of an executable consumer class implementing the
execution of set_status_booked action for a selected travel instance. Again, to enable the class-run mode,
the consumer class /dmo/cl_eml_travel_action implements the if_oo_adt_classrun interface.
Checking Results
To check the results of the MODIFY call, run the main method of the class from listing above by pressing F9
key and then view the received RESULT data (lt_set_status_booked) on the console output.
This example demonstrates how you can implement multiple DELETE operations for different entities in one
MODIFY call. In this case, we use the long form of the MODIFY statement that allows you to collect multiple
operations on multiple entities of one business object that is identified by RootEntityName.
Prerequisites
The entity instances can only be deleted in a MODIFY call if the delete operation is specified for each relevant
entity in the behavior definition and is implemented in the behavior pool(s) accordingly.
...
[FAILED DATA(it_failed)]
[REPORTED DATA(it_reported)].
Each DELETE operation has a table of instances as input parameters: it_instance1_d and
it_instance1_d, which provide the MODIFY call with key information.
The following listing provides you with the source code of an executable consumer class that implements the
deletion of a given travel instance lv_travel_to_delete and a booking supplement
lv_booksuppl_to_delete.
Checking Results
To check the results of the MODIFY call, run the main method of the class from listing above by pressing F9
key in the class editor and then search for data of selected instances in the data preview tool ( F8 on the CDS
root view DMO/I_TRAVEL_U.)
This example demonstrates how you can implement a direct CREATE and multiple CREATE BY association
operations for different entities in one MODIFY call. In this case, the long form of the MODIFY statement is used
to collect multiple operations on multiple entities of one business object that is identified by the
RootEntityName.
Prerequisites
The instances of entities (root or child) can only be directly created in a MODIFY call if the create operation is
specified for the relevant entities in the behavior definition (and is implemented in the behavior pool
accordingly - in case of unmanaged implementation type).
The same applies to instances of child entities that are created by association. In this case however, an
association to the child entity must be specified in behavior definition (and implemented in a handler method
of the behavior pool - in case of unmanaged implementation type).
Remember
When multiple entity instances are created by one MODIFY statement, then it is required to provide the
content ID %CID [page 877] information for all instances to be created. .
In addition to the content ID, the required field values for the instance to be created must be populated. This is
done by the input parameter it_instance_c in the CREATE FIELDS ( ) WITH statement.
If the instances of child entities should to be created by an association, besides the parent key, also the new
values for the child entity to be created, must be populated in the create operation. The input parameter
it_instance_cba in the CREATE BY \association1_name... statement is therefore a table type
containing the parent key (reference to content ID in case the parent instance is created in the same MODIFY
call) and the %target sub-table that refers to the child instance to be created.
The following listing provides you with the source code of an executable consumer class that implements the
creation of a new travel instance including a new booking and the associated booking supplement.
Checking Results
To check the results, run the main method of the consumer class /dmo/cl_eml_travel_subnodes from
listing above by pressing F9 key in the class editor and then search for the newly created travel, booking and
booking supplement instances in the data preview tool ( F8 on the travel root CDS view /DMO/I_TRAVEL_U.)
Related Information
Transactional business applications based on business objects require an authorization concept for their data
and for the operations on their data. Display and create, update and delete (CUD) operations, as well as
specific business-related activities, must be protected from unauthorized access and are therefore allowed for
authorized users only.
In a transactional development scenario, you can add authorization checks to various components of an
application. In this case, different mechanisms are used to implement the authorization concept.
The following topics deal with authorization control for read and modify operations in the context of managed
business objects.
To protect data from unauthorized read access, the ABAP CDS already provides its own authorization concept
based on a data control language (DCL [page 951]). The authorization and role concept of ABAP CDS uses
conditions defined in CDS access control objects to check the authorizations of users for read access to the
data in question. In other words, access control allows you to limit the results returned by a CDS entity to those
results you authorize a user to see.
In addition, DCL is also automatically evaluated in case of transactional read access, that is when using EML-
based read and read by association operations, as well as when processing feature control.
For managed business objects, also modifying operations such as standard operations create, update, delete,
create by associations, and actions must be checked against unauthorized access.
The availability of authorization control is modeled in the behavior definition. The authorization control requires
not only a definition but also an implementation in a handler class of the corresponding behavior pool.
The authorization control is checked by the business object runtime as soon as the relevant operation is
executed.
Restriction: With the current release, only instance-based authorization control (authorization
master(instance) ) is supported. That means, static authorization is not available. Therefore, you cannot
apply authorization checks to create operations (static operations).
Restriction
With the current release, only instance-based authorization control is supported: (authorization
master(instance) ). This means, static authorization (that does not depend on an instance) is not yet
available. Therefore, you cannot apply authorization checks to the create operation (that is a static
operation).
To define the instance-based authorization control on a CDS entity for managed implementation type, the
following syntax is used in the behavior definition:
[implementation] managed;
define behavior for RootEntity [alias RootAliasedName]
implementation in class ABAP_ClASS_FOR_ROOT [unique]
authorization master(instance)
...
{
/* (1) Authorization checks cannot be applied on create operation (static
operation) */
create;
/* (1‘) Authorization check: is always enabled for update */
update;
/* (1“) Authorization check can be disabled for delete */
delete (authorization : none);
/* (2) Authorization check: enabled for Action1 */
action Action1 [...]
/* (2‘) Authorization check: disabled for Action2 */
action (authorization : none) Action2 [...]
/* (3) Authorization check: enabled for _Assoc1 */
association _Assoc1 { create; }
/* (3‘) Authorization check: disabled for create by _Assoc2 */
association _Assoc2 { create ( authorization : none); }
...
}
define behavior for ChildEntity [alias ChildAliasedName]
implementation in class ABAP_ClASS_FOR_CHILD [unique]
authorization dependent( key_field_of_child_entity = key_field_of_root_entity )
...
{
/* (4) Operations that are treated as an update operation on the authorization
master */
update;
delete;
/* (5) Authorization check: enabled for Action11 */
action Action11 [...]
/* (5‘) Authorization check: disabled for Action12 */
action (authorization : none) Action12 [...]
/* (6) Treated as an update operation on the authorization master */
association _AssocName11 { create; }
...
}
The root entity is always defined as authorization master. To enable instance-based authorization control,
the parameter (instance) is added to the definition of master. The authorization check can then be
For standard operations such as update, delete, as well as for create by association and actions, the
authorization control is then checked by the business object runtime as soon as the relevant operation is
executed (default behavior). For each relevant operation, you can specify the following values in the
implementing handler of the class pool:
● auth-allowed [page 607] - if the consumer is allowed to execute the operation on the current instance
● auth-unauthorized [page 607] - if the consumer is not allowed to execute the operation on the current
instance.
However, for selected operations you have the option of suppressing the authorization check execution so that
the consumer can access them. To disable the authorization check, the parameter (authorization :
none) must be added to the operation in question in the behavior definition.
With the current release, the root entity is always defined as authorization master, whereas all child
entities are defined as authorization dependent. If a child entity is modified (update, delete, create by
association) on that entity, the authorization check (that is implemented in the corresponding behavior class)
of the master is triggered to check if the operation is allowed for being accessed.
The operations , update, delete, create by association on child entities are treated as an update on the
corresponding root entity (authorization master). Thus, the authorization check implementation is triggered to
check the authorization for update at master level - despite of the fact that it was a update, delete, create
by association request at dependent child entity level. In other words: create by association, update and
delete operations on the authorization dependent child entity are checked on their authorization master
entity as update. For example, the delete operation of ChildEntity instance invokes the authorization
method of the RootEntity and checks the corresponding root instance with update operation.
However, actions on the authorization dependent child entity are checked by the authorization handler of
the authorization dependent entity instead.
Example
In the following behavior definition, the travel (root) entity acts as authorization master, whereas the
child entities booking and booksuppl are defined as authorization dependent.
To disable the authorization check for the createBooking action of the root entity, the parameter
(authorization : none) is added to the action definition. The same applies to the
createBookingSupplement action in the behavior definition of the booking entity.
managed;
define behavior for /DMO/I_Travel_M alias travel
...
authorization master(instance)
{
create;
update;
delete;
association _Booking { create; }
action ( authorization : none ) createBooking result [1] $self;
...
}
define behavior for /DMO/I_Booking_M alias booking
{
update;
...
}
Related Information
The instance-based authorization control of a business object’s entity is implemented in the behavior pool that
is specified in the behavior definition by the keyword implementation in class ABAP_CLASS [unique].
The implementation of authorization control is based on the ABAP language and is done in a local handler class
(lhc_authorization_handler) as part of the behavior pool. As depicted in the listing below, each such local
class inherits from the base handler class cl_abap_behavior_handler.
The signature of the handler method check_authority_for_entity (method name is freely selectable) is
introduced by the keyword FOR AUTHORIZATION, followed by the input parameter it_entity_key, which
Note
The output parameter rt_result (parameter name is freely selectable) is used to return a result containing
information whether the consumer is allowed using the relevant operation for the current instance or not.
The output parameters failed and reported for errors or messages are added implicitly (automatically). They
can, however, be declared explicitly as CHANGING parameters in the method signature using the generic type
DATA:
The authorization method is implemented in a generic manner. For each instance, the output parameter
rt_result contains the result with information whether the consumer is allowed using the relevant operation
for the current instance or not.
The components in the result depend on the consumer’s authorization request. If an operation is disabled for
authorization check by adding the parameter (authorization : none) to the operation in question, then it
is neither included in the authorization request nor in the output parameter.
Note
Example
UI Preview
The following figure shows the effect of the authorization control for an action that is triggered by the
consumer using the Reject Travel button (the consumer is not authorized to trigger the action).
Definition
In the behavior definition, the authorization control for the root entity may be defined as follows:
managed;
define behavior for /DMO/I_Travel_M alias travel
implementation in class /DMO/BP_TRAVEL_M unique
...
authorization master(instance)
{
...
create;
update;
delete ( authorization : none );
action acceptTravel result [1] $self;
action rejectTravel result [1] $self;
action createTravelByTemplate result [1] $self;
action (authorization : none) createBooking result [1] $self;
association _Booking { create; }
Implementation
The following example shows the implementation of authorization control for various operations to be
executed on travel instances:
The authorization control is checked by the business object runtime as soon as the relevant operation is
executed. For each relevant operation, the authorization check must be enabled so that one of the following
values can be assigned in the implementing handler method:
Corresponding to the behavior definition in our example, the authorization check is disabled for delete
operation and createBooking action (authorization : none). The consumer can access these
operations without any authorization check execution.
ENDLOOP.
ENDMETHOD.
...
ENDCLASS.
6.3 Queries
An unmanaged query uses an ABAP interface to implement read-only access to persistent or non-persistent
data. It enables a more flexible data retrieval than using the SQL push down by the query framework to retrieve
data from a database table.
Context
An unmanaged query is implemented for read-only access to a data source whenever the standard SQL push-
down by the query framework is not sufficient or not usable at all; or if there is no persistent data source at all.
● the data source for an OData request is not a database table, but, for example another OData service,
which is reached by an OData client proxy,
● performance optimization with application specific handling,
● using AMDPs with some query push-down parameters in the SQL script implementation,
In these cases, the data model is defined in a custom entity, which references a query implementation class,
where the query is implemented using a query provider interface. The runtime of an unmanaged query is
illustrated in the following diagram.
For more background information about the unmanaged query and the custom entity, see Unmanaged Query
[page 50].
Example Scenario
The following sections offer an example for the implementation of the query provider interface. It is aimed to
give an understanding on how to work with the interface IF_RAP_QUERY_PROVIDER. The example query
implementation retrieves data from a database table, which is not a typical use case.
Note
Do not use the custom query in the straight-forward case of retrieving data from a database table. The
example is only used for demonstration purposes, as no background information about another technology
(for example in AMDP implementations) is necessary to understand the example. The recommended
implementation for such a scenario is to use a CDS view and the underlying query implementation of the
SQL select by the orchestration framework.
The data model is defined in a custom entity. Expand the following codeblock to view the data model of the
example scenario. For simplification reasons, the same element names as in the data source are used. (If you
use differing names, you must map the elements of the custom entity to the corresponding table fields in the
query implementation. Make sure that the types are compatible.)
Expand the following listing to view the source code of the travel CDS view that is used for the demo example.
The data source in the example scenario is the database table /dmo/travel, see ABAP Flight Reference
Scenario [page 912].
Implementation
Every method provided by IF_RAP_QUERY_PROVIDER is used and explained in this implementation. The
complete source code of the query implementation with every method is displayed after the implementation
steps.
Prerequisites
● The custom entity references the query implementation class in the annotation
@ObjectModel.query.implementedBy.
● The query implementation class implements the interface IF_RAP_QUERY_PROVIDER with its select
method.
Note
To avoid high resource consumption when calling an OData service without using $top each OData service,
based on the ABAP RESTful Programming Model uses a default paging. This includes
Note
In scenarios, in which you expose your custom entity for a Fiori UI, you have to include at least the
implementation for counting and paging as the UI always requests the count and sets the query options
Note
Associations with custom entity as source or target support only attribute bindings (A1 = A2 and B1 = B2),
but no:
● OR, NOT
● Other operators than ‘=’
● Using something else than CDS elements as operands (e.g. no literals or variables)
Implementation Steps
● Check that the query is only executed when the requested entity set matches the custom entity.
For implementation details, see Returning Requested Entity in an Unmanaged Query [page 613].
● Separate your implementation for data retrieval and count.
For implementation details, see Requesting and Setting Data or Count in an Unmanaged Query [page 614].
● Implement filter conditions according to
○ a requested filter,
For implementation details, see Implementing Filtering in an Unmanaged Query [page 616]
○ a requested search term,
For implementation details, see Implementing Search in an Unmanaged Query [page 620]
○ requested parameters.
For implementation details, see Using Parameters in an Unmanaged Query [page 618].
● Get the paging information and retrieve data according to the requested page.
For implementation details, see Implementing Paging in an Unmanaged Query [page 621].
● Get the sorting information and order the retrieved data according to the sort elements and direction.
For implementation details, see Implementing Sorting in an Unmanaged Query [page 623].
● Get the requested elements and select only the relevant elements from the data source.
For implementation details, see Considering Requested Elements in an Unmanaged Query [page 625].
● Get the aggregated and the grouped elements and aggregated and group the records accordingly.
For implementation details, see Implementing Aggregations in an Unmanaged Query [page 626].
Expand the following listing to view the source code of query implementation class for the demo example.
ELSE ` ascending` ) ) ).
DATA(lv_sort_string) = COND #( WHEN lt_sort_criteria IS INITIAL
THEN `primary key`
Getting the requested entity ID into your query implementation class can be helpful to ensure that the query is
only executed if a specific entityis queried. You can also differentiate between query implementations of
different custom entities in one query implementation class. The interface IF_RAP_QUERY_REQUEST provides
a method to get the entity ID, which is requested.
For general prerequisites of an unmanaged query implementation, see Prerequisites Unmanaged Query [page
610].
Implementation Steps
The following steps provide an example on how to use the method get_entity_id in your query
implementation class.
1. Call the method get_entity_id of the interface IF_RAP_QUERY_REQUEST, which returns the requested
CDS entity name.
2. Use the returned value to compare to the custom entity the query implementation is aimed at, or to define
query implementation for different custom entities in one query implementation class.
The following codeblock illustrates the implementation within the SELECT method of
IF_RAP_QUERY_PROVIDER in the query implementation class.
CASE io_request->get_entity_id( ).
WHEN `/DMO/I_TRAVEL_UQ` .
****query implementation for travel entity
WHEN `/DMO/I_BOOKING_UQ`.
****query implementation for booking entity
ENDCASE.
The interface IF_RAP_QUERY_REQUEST provides methods to indicate whether data or the count is requested.
These methods can be used to separate the implementations for data retrieval and count or to ensure that the
query implementation is only executed if the respective request is made.
The interface IF_RAP_QUERY_RESPONSE provides methods to return the requested data or the count as a
response for the request.
Note
If data is requested, the method set_data must be called. If the total number of records is requested, the
method set_total_number_of_records must be called. Otherwise there will be an error during
runtime.
In UI scenarios with Fiori Elements the total number of records is always requested by the UI.
For general prerequisites of an unmanaged query implementation, see Prerequisites Unmanaged Query [page
610].
Implementation Steps
The following steps provide an example on how to use the method is_data_requested in your query
implementation class.
1. Call the method is_data_requested of the interface IF_RAP_QUERY_REQUEST, which returns a boolean
value.
2. Create the SQL SELECT to retrieve the requested data into a local variable.
3. Call the method set_data of the interface IF_RAP_QUERY_RESPONSE and use the retrieved data as
importing parameter. The result is then returned to the OData client, for example the SAP Fiori Elements
UI.
The following codeblock illustrates the implementation for data requests within the select method of
IF_RAP_QUERY_PROVIDER in the query implementation class..
IF io_request->is_data_requested( ).
DATA lt_travel_response TYPE STANDARD TABLE OF /dmo/i_travel_uq.
SELECT * FROM /dmo/travel
INTO CORRESPONDING FIELDS OF TABLE @lt_travel_response.
io_response->set_data( lt_travel_response ).
ENDIF.
For API information, see Method is_data_requested [page 881] and Method set_data [page 891].
The following steps provide an example on how to use the method is_total_numb_of_rec_requested in
your query implementation class.
The following codeblock illustrates the implementation for data requests within the select method of
IF_RAP_QUERY_PROVIDER in the query implementation class. .
IF io_request->is_total_numb_of_rec_requested( ).
SELECT COUNT( * ) FROM /dmo/travel
INTO @DATA(lv_travel_count).
io_response->set_total_number_of_records( lv_travel_count ).
ENDIF.
To retrieve filtered data or a filtered number of records in an unmanaged query, you need to implement a filter
in the query implementation class. The interface IF_RAP_QUERY_REQUEST provides a method to get the filter
for the request with two options to retrieve the filter conditions:
Depending on your use case, the one or the other option is more useful. If you retrieve your data from the data
source with an SQL SELECT, you can include the SQL filter string directly in the WHERE clause of the SELECT
statement. If you want to manipulate the filter conditions before executing the filter, a ranges table can be the
better choice.
Note
If the filter is not feasible as a ranges table, an exception is raised. You then have to handle the error
appropriately
Prerequisites
For general prerequisites of an unmanaged query implementation, see Prerequisites Unmanaged Query [page
610].
Implementation Steps
1. Call the method get_filter of the interface IF_RAP_QUERY_REQUEST, which returns an interface
instance of IF_RAP_QUERY_FILTER. Use the method get_as_sql_string to use the filter directly in an
SQL string.
2. Check if data is requested.
3. Use the filter condition in the WHERE clause of the SQL statement to retrieve the filtered data from the data
source.
4. Call the method set_data of the interface IF_RAP_QUERY_RESPONSE to respond to the OData request
with the filtered data. The filtered result is then returned to the OData client, for example the SAP Fiori
Elements UI.
The following codeblock illustrates the filter implementation within the select method of
IF_RAP_QUERY_PROVIDER in the query implementation class.
Note
It is recommended to implement a default sort order to return consistent results from the data source.
The following steps provide an example on how to use the method get_as_ranges in your query
implementation class.
1. In a TRY - CATCH block, call the method get_filter of the interface IF_RAP_QUERY_REQUEST, which
returns an interface instance of IF_RAP_QUERY_FILTER. Use the method get_as_ranges to get the filter
as a ranges table. The format of the returning ranges table is described in Method get_as_ranges [page
886].
2. Use the filter condition of the ranges table in your implementation.
3. Catch the exception CX_RAP_QUERY_FILTER_NO_RANGE, which is raised if the filter cannot be expressed
as a ranges table.
4. Handle the exception appropriately. For example:
1. Throw an error.
2. Use the filter SQL string as a fall back, see Method get_as_sql_string [page 887]
TRY.
DATA(lt_ranges) = io_request->get_filter( )->get_as_ranges( ).
****filter manipulation
CATCH cx_rap_query_filter_no_range.
""error handling
ENDTRY.
To retrieve data dependent on an entity parameter that is set in the custom entity, you need to implement a
handling for the parameters in the query implementation class. The interface IF_RAP_QUERY_REQUEST
provides a method to get the parameters. It returns a string table with the parameter names and values.
It is up to the application developer how to implement the parameter logic. One option is to implement the
parameters as filter criteria. To use the parameter values as a filter in the WHERE clause of an SQL SELECT
statement, you need to create a filter string from the parameter values.
The interface IF_RAP_QUERY_RESPONSE provides a method to set the filtered data or the filtered total number
of records for the query response after retrieving data from the data source.
Prerequisites
Example
● For general prerequisites of an unmanaged query implementation, see Prerequisites Unmanaged Query
[page 610].
Implementation Steps
The following steps describe the procedure of using parameters as additional filter criteria. The parameters
p_start_date and p_end_date are used as a filter on the elements Begin_Date and End_Date.
1. Call the method get_parameters of the interface IF_RAP_QUERY_REQUEST, which returns a string table
of the parameters and their values.
2. Define default parameter values in case the parameters are not given in the request. The implementation
example uses the system date for Begin_Date and the system date the following year for End_Date.
3. Define a variable for the filter string and fill it with the filter condition for the SQL WHERE clause on the
element Begin_Date and End_Date and integrate the default values.
Note
To avoid security risks via SQL string injections, use the method escape_quotes of the public class
CL_ABAP_DYN_PRG.
4. If there are other filter conditions (from filter requests or parameters), concatenate the filter strings with
AND.
5. Check if data is requested.
The following codeblock illustrates an implementation for parameters within the select method of
IF_RAP_QUERY_PROVIDER in the query implementation class.
DATA(lt_parameters) = io_request->get_parameters( ).
DATA(lv_next_year) = CONV
syst_datum( cl_abap_context_info=>get_system_date( ) + 365 ) .
DATA(lv_par_filter) = |( BEGIN_DATE >=
'{ cl_abap_dyn_prg=>escape_quotes( VALUE #( lt_parameters[ parameter_name =
'P_START_DATE' ]-value
Note
It is recommended to implement a default sort order to return consistent results from the data source.
To retrieve data according to the search term in the OData request, you need to implement a search logic in
your query implementation class. The interface IF_RAP_QUERY_REQUEST provides a method to get the search
expression from the request.
It is up to the application developer how the search logic is implemented. One option is to use the search
expression as (additional) filter criteria for one or more elements. To use the search expression as a filter in the
WHERE clause of an SQL SELECT statement, you need to create a filter string from the search expression and
combine it with other possible filter strings.
The interface IF_RAP_QUERY_RESPONSE provides a method to set the filtered data for the query response
after retrieving data from the data source.
Prerequisites
● For general prerequisites of an unmanaged query implementation, see Prerequisites Unmanaged Query
[page 610].
● To send query requests with search conditions from a Fiori Elements UI, you need to annotate the custom
entity with @Search.seachable:true.
Note
Implementation Steps
The following steps provide an example on how to implement the search as a filter for the element
Description. If a filter is also requested, you need to ensure that the SQL filter string has the right syntax for
the SQL SELECT statement.
Note
To avoid security risks via SQL string injections use the method escape_quotes of the public class
CL_ABAP_DYN_PRG.
The following codeblock illustrates the search implementation within the select method of
IF_RAP_QUERY_PROVIDER in the query implementation class.
DATA(lv_search_string) = io_request->get_search_expression( ).
DATA(lv_search_sql) = |DESCRIPTION LIKE '%
{ cl_abap_dyn_prg=>escape_quotes( lv_search_string ) }%'|.
IF lv_sql_filter IS INITIAL.
lv_sql_filter = lv_search_sql.
ELSE.
lv_sql_filter = |( { lv_sql_filter } AND { lv_search_sql } )|.
ENDIF.
IF io_request->is_data_requested( ).
DATA lt_travel_response TYPE STANDARD TABLE OF /dmo/i_travel_uq.
SELECT * FROM /dmo/travel
WHERE (lv_sql_filter)
ORDER BY ('primary key')
INTO CORRESPONDING FIELDS OF TABLE @lt_travel_response
io_response->set_data( lt_travel_response ).
ENDIF.
IF io_request->is_total_numb_of_rec_requested( ).
SELECT COUNT( * ) FROM /dmo/travel
WHERE (lv_sql_filter)
INTO @DATA(lv_travel_count).
io_response->set_total_number_of_records( lv_travel_count ).
ENDIF.
Note
It is recommended to implement a default sort order to return consistent results from the data source.
To retrieve data in packages, you need to implement paging in the query implementation class. The interface
IF_RAP_QUERY_REQUEST provides a method to get the paging information. It returns an interface instance of
IF_RAP_QUERY_PAGING with two methods for the beginning and the number of records to be retrieved.
Note
In accordance to the OData query option $skip, the method get_offset does not return the position of
the first data record to retrieve, but the number of records that are not taken into account before the
retrieval starts. That means, the first line of the data source that is retrieved is the returning value of
get_offset plus 1.
Note
For retrieving all available data records, the method get_page_size returns the constant
page_size_unlimited of the interface IF_RAP_QUERY_PRAGING. This has to be converted when using
the paging information in an SQL string.
The method get_page_size defines the number of records that are retrieved.
The paging information can then be used in the OFFSET and the UP TO n ROWS clause of the SQL SELECT
statement.
The interface IF_RAP_QUERY_RESPONSE provides a method to set the reduced data records for the query
response after retrieving data from the data source.
Prerequisites
For general prerequisites of an unmanaged query implementation, see Prerequisites Unmanaged Query [page
610]
Implementation Steps
1. Call the method get_paging of the interface IF_RAP_QUERY_REQUEST, which returns an interface
instance of IF_RAP_QUERY_PAGING. Use the method get_offset to get the number of records to drop
into a local variable.
2. Call the method get_paging of the interface IF_RAP_QUERY_REQUEST, which returns an interface
instance of IF_RAP_QUERY_PAGING. Use the method get_page_size to get the number of records to
retrieve.
3. Convert the number for infinite numbers of records to be compatible with the definition of the SQL SELECT
statement to retrieve an infinite number.
Note
The SQL SELECT addition UP TO 0 ROWS retrieves all available data sets.
4. Use the additions OFFSET and UP TO n ROWS in the SQL SELECT to retrieve the data records in packages.
The following codeblock illustrates the paging implementation within the select method of
IF_RAP_QUERY_PROVIDER in the query implementation class.
IF io_request->is_data_requested( ).
DATA(lv_offset) = io_request->get_paging( )->get_offset( ).
DATA(lv_page_size) = io_request->get_paging( )->get_page_size( ).
DATA(lv_max_rows) = COND #( WHEN lv_page_size =
if_rap_query_paging=>page_size_unlimited THEN 0
ELSE lv_page_size ).).
DATA lt_travel_response TYPE STANDARD TABLE OF /dmo/i_travel_uq.
SELECT * FROM /dmo/travel
ORDER BY ('primary key')
INTO CORRESPONDING FIELDS OF TABLE @lt_travel_response
OFFSET @lv_offset UP TO @lv_max_rows ROWS.
io_response->set_data( lt_travel_response ).
ENDIF.
Note
It is required to implement a default sort order to return consistent results from the data source. Sorted
results are essential in combination with paging. If you do not provide a default order, the data records for a
certain page might not be consistent.
To retrieve sorted data in an unmanaged query, you need to add sorting criteria to the SQL SELECT statement.
The interface IF_RAP_QUERY_REQUEST provides a method to get the sort element and the sort order for the
request.
The method returns an ordered list of elements with their sort order. If there is more than one sort element, the
sorting priority is in order of appearance. To use the sorting criteria in an SQL SELECT clause, the sorting
criteria has to be transformed into a string that has comma-separated pairs of sort element and sort order.
Whereas abap.bool indicates the sort order for the element descending in the sorted table that you get
from the query request, the sort order must be indicated with the string ascending or descending in the
ORDER BY clause of the SQL statement.
Example
The string for the SQL select statement must look like element1 ascending , element2
descending, … .
The interface IF_RAP_QUERY_RESPONSE provides a method to set the sorted data for the query response
after retrieving the data records from the data source.
For general prerequisites of an unmanaged query implementation, see Prerequisites Unmanaged Query [page
610].
Implementation Steps
The following codeblock illustrates the sort implementation within the select method of
IF_RAP_QUERY_PROVIDER in the query implementation class.
IF io_request->is_data_requested( ).
DATA(sort_elements) = io_request->get_sort_elements( ).
DATA(lt_sort_criteria) = VALUE string_table( FOR sort_element IN
sort_elements
( sort_element-element_name &&
COND #( WHEN sort_element-descending = abap_true
THEN ` descending`
ELSE ` ascending` ) ) ).
DATA(lv_sort_string) = COND #( WHEN lt_sort_criteria IS INITIAL THEN
`primary key`
ELSE
concat_lines_of( table = lt_sort_criteria sep = `, ` ) ).
DATA lt_travel_response TYPE STANDARD TABLE OF /dmo/i_travel_uq.
SELECT * FROM /dmo/travel
ORDER BY (lv_sort_string)
INTO CORRESPONDING FIELDS OF TABLE @lt_travel_response.
io_response->set_data( lt_travel_response ).
ENDIF.
You can optimize the performance for your unmanaged query you can add an implementation to retrieve only
the elements that are requested in the OData request. If you do not specify requested element, the query
retrieves the value for every element in the custom entity.
To retrieve only the elements that are requested in the OData request, you need to implement an element
restriction in the SQL SELECT statement. The interface IF_RAP_QUERY_PROVIDER provides a method to get
the requested elements for the request.
To select only the requested element in the SQL SELECT clause, you must transform the requested elements
into a string with comma separations.
The interface IF_RAP_QUERY_RESPONSE provides a method to set the data for the query response after
retrieving the relevant data from the data source.
Prerequisites
For general prerequisites of an unmanaged query implementation, see Prerequisites Unmanaged Query [page
610].
Implementation Steps
The following steps provide an example on how to select only the requested elements from a data source.
The following codeblock illustrates the implementation to retrieve only the requested elements within the
select method of IF_RAP_QUERY_PROVIDER in the query implementation class.
IF io_request->is_data_requested( ).
DATA(lt_req_elements) = io_request->get_requested_elements( ).
DATA(lv_req_elements) = concat_lines_of( table = lt_req_elements sep = `,
` ).
DATA lt_travel_response TYPE STANDARD TABLE OF /dmo/i_travel_uq.
SELECT (lv_req_elements) FROM /dmo/travel
ORDER BY ('primary key')
INTO CORRESPONDING FIELDS OF TABLE
@lt_travel_response.
Note
It is recommended to implement a default sort order to return consistent results from the data source.
To retrieve aggregate data, you need to implement a logic to retrieve and group data according to the requested
aggregations in the OData request. The interface IF_RAP_QUERY_REQUEST provides a method to get the
aggregation information. It returns an interface instance of IF_RAP_QUERY_AGGREGATION with two methods
to get the elements to be aggregated or grouped.
To select the requested elements according to the requested aggregation, you need to transform the requested
aggregation elements into an aggregation string for the SQL SELECT. The aggregation string must look as
follows:
For more information about aggregate functions in SQL expressions, see ABAP SQL -Aggregate Expressions
agg_exp (ABAP Keyword Documentation).
To avoid double selecting the result element, you need to delete it from the list of elements that are requested
as the aggregation string is used in the SQL SELECT together with the other requested elements.
To group the response data by the requested grouped elements, you need to create an SQL string for the SQL
GROUP BY clause.
The interface IF_RAP_QUERY_RESPONSE provides a method to set the aggregated and grouped data for the
query response after retrieving data from the data source.
Prerequisites
● For general prerequisites of an unmanaged query implementation, see Prerequisites Unmanaged Query
[page 610].
● To use aggregation in a Fiori Elements UI and send requests for aggregate values, you need to annotate the
related elements with @Aggregation.default: <aggr_method>.
This procedure combines the implementation of requested elements and aggregation, as both are used in the
select list of the SQL SELECT. The steps also provide an example on how to use the grouped elements in the
SQL SELECT.
The following codeblock illustrates the implementation for aggregation and grouping select method of
IF_RAP_QUERY_PROVIDER in the query implementation class.
IF io_request->is_data_requested( ).
DATA(lt_req_elements) = io_request->get_requested_elements( ).
DATA(lt_aggr_element) = io_request->get_aggregation( )-
>get_aggregated_elements( ).
IF lt_aggr_element IS NOT INITIAL.
LOOP AT lt_aggr_element ASSIGNING FIELD-SYMBOL(<fs_aggr_element>).
DELETE lt_req_elements WHERE table_line = <fs_aggr_element>-
result_element.
DATA(lv_aggregation) = |{ <fs_aggr_element>-aggregation_method }
( { <fs_aggr_element>-input_element } ) as { <fs_aggr_element>-result_element }|.
APPEND lv_aggregation TO lt_req_elements.
ENDLOOP.
ENDIF.
DATA(lv_req_elements) = concat_lines_of( table = lt_req_elements sep =
`, ` ).
DATA(lt_grouped_element) = io_request->get_aggregation( )-
>get_grouped_elements( ).
DATA(lv_grouping) = concat_lines_of( table = lt_grouped_element sep =
`, ` ).
DATA lt_travel_response TYPE STANDARD TABLE OF /dmo/i_travel_uq.
SELECT (lv_req_elements) FROM /dmo/travel
GROUP BY (lv_grouping)
ORDER BY ('primary key')
INTO CORRESPONDING FIELDS OF TABLE
@lt_travel_response.
io_response->set_data( lt_travel_response ).
ENDIF.
It is recommended to implement a default sort order to return consistent results from the data source.
Note
6.4 UI Semantics
End-user texts, such as field labels or field descriptions, are taken from ABAP Dictionary data elements to
which the corresponding element is bound - unless you redefine the texts using CDS annotations. Unlike
technical element names, the header texts, field labels and descriptions are language-dependent. For
example, the field 'Airline'' would have a language-dependent label 'Airline Code'.
Such texts must be translated. Therefore, the CDS development infrastructure is able to extract them from the
source code and transfer the extracted texts to the actual translation infrastructure of the corresponding
delivery package.
Relevant Annotations
Annotation Effect
@EndUserText.label: This annotation is used to define translatable semantic short texts (with maxi
'<text>' mum 60 characters) for an element of the CDS view (label text).
@EndUserText.quickInfo: The annotation defines a human-readable <text> that provides additional infor
'<text>' mation about an element. The quick info is used for accessibility hints or the
mouse over function.
Remember
The <text> specified in the source code should consist of text in the original language of the CDS source
code and will be translated into the required languages.
Example
The listing below demonstrates the usage of @EndUserText annotations at the view and element (field) level:
Tip
Press F1 in the CDS source code editor for extended help content on @EndUserText annotation!
Note
If @UI labeling annotations are used, they will be evaluated primarily. That means, they will overwrite the
text given with the @EndUserText annotations.
OData Metadata
To verify that the additional information of labels and descriptions is pushed correctly to the OData service, you
can check the OData metadata document. This can also be helpful to find out which label information is used if
you maintain @UI and @EndUserText in your CDS view.
If no UI annotations are used, the OData metadata document of the example above should contain the
annotations that are marked in the following image:
How to solve the most common UI layout tasks with UI annotations in CDS.
Prerequisites
In order to use the example code, download the ABAP Flight Reference Scenario [page 912].
For a UI to work, you do not need to implement everything yourself. The barebones of the UI will be taken care
of automatically by RAP if you use the Fiori Elements App Preview (FEAP) in the service binding. For example
the root page will have a header and a list report (table) facet with sorting and filtering capabilities, even
without you implementing any UI annotations in CDS. Furthermore the UI will include a Share-Button and a
basic view manager.
Note
To view the entries of your database on the UI without implementing any UI annotations, click on the gear
icon on the UI and select at least one column.
FAQ
This FAQ directs you to the correct topics for questions that often arise when working with Metadata-Driven UIs
for the first time.
You can use UI annotations both in Data Definition or Metadata Extension files.
Note
We recommend that you use a Metadata Extension file for all UI annotations because this will improve the
legibility of the source code in the Data Definition and also simplify the development and maintenance of
the CDS view. Metadata Extensions also allow for layering of multiple Metadata Extensions, which is useful
if your BO is used by customers, partners and industries at the same time with different customization
needs.
1. Header section
2. Annotate/define view section
3. Facet Definitions
4. Element List
1. Fields
Find the Metadata Extension files for the Travel Scenario in the Complete UI Annotations Example Code [page
665].
// HEADER SECTION
@Metadata.layer: ...
@UI.headerInfo: ...
// ANNOTATE VIEW SECTION
annotate/define view ...
{
// FACET DEFINITIONS
// Header Facets (Object Page):
@UI.facet: [ { purpose: #HEADER, ... },
// Body Facets (Object Page)
{ ... } ]
// ELEMENT LIST
// FIELDS
User Interface
The screenshots in this guide are taken from the Fiori Elements App Preview (FEAP), which can be started from
the service binding.
The Fiori UI serves as an example for how your UI might look like and how changes in the code influence the UI.
In this guide, we use the two most common page layouts, also called Fiori floorplans, namely:
Find more information about Fiori in the Introduction to SAP Fiori Elements .
The list report is used to display all the entries of a dataset in one list or table. You can specify which columns to
display and which ones to hide or omit.
To navigate from the list report to the object page of an entry, click anywhere on the row of that entry.
Object Page
The object page is mostly used to show more details about one entry of the dataset. Again, you can choose
which details shall be displayed on the object page.
Fields are elements from the CDS view that are displayed on the UI screen. Fields can be table columns of list
reports or single items within facets on the object page, such as “Customer ID: …”. In other words: whenever
you annotate a CDS element to be displayed on the UI, it will appear on the UI as a field.
How to use UI annotations in CDS to overwrite default field labels on your UI.
Quick Info
The labels of exposed CDS elements are derived by default from the data element labels in the ABAP
dictionary, or, if defined, from the CDS annotation @EndUserText.label. You can overwrite both, data
element labels and labels defined by @EndUserText.label, with the subannotation label on the UI. Labels
of UI annotations are only used in UI contexts, whereas labels from the ABAP dictionary or
@EndUserText.label are used in the service document of any service. The subannotation label can be
used with several UI annotations, e.g. @UI.identification or @UI.LineItem (see Quick Info above or UI
Annotations [page 746]).
Place the annotation within the element list section, right before the respective element that you want to label.
Code Snippet
In this example, we use the annotation @UI.identification.label to label the fields that are displayed on
the object page in the facet with type #IDENTIFICATION_REFERENCE as can be seen in the first figure of the
preview above. Similarly we label the fields (i.e. columns) of the list report with the annotation
@UI.lineItem.label as can be seen in the second figure of the preview above.
Find the Metadata Extension files for the Travel Scenario in the Complete UI Annotations Example Code [page
665].
...
annotate view Z_C_TRAVEL_R with
{
...
// Body Facets (Object Page)
{ id: 'Facet3-ID',
purpose: #STANDARD,
type: #IDENTIFICATION_REFERENCE, // Refers to elements
annotated with '@UI.identification' in the element list below
label: 'Facet 3 - Overview and Comments',
position: 30 }
]
// ELEMENT LIST
// No label defined.
TravelID;
@UI: { lineItem: [ { label: 'Agency' } ],
identification: [ { label: 'Agency' } ]
}
AgencyID;
@UI: { lineItem: [ { label: 'Customer' } ],
identification: [ { label: 'Customer' } ]
}
CustomerID;
@UI: { lineItem: [ { label: 'Start of Travel' } ],
identification: [ { label: 'Start of Travel' } ]
}
BeginDate;
@UI: { lineItem: [ { label: 'End of Travel' } ],
identification: [ { label: 'End of Travel' } ]
}
EndDate;
@UI: { hidden: true }
BookingFee;
// No label defined.
TotalPrice;
// Label only defined for identification field
@UI: { identification: [ { label: 'Comment' } ]
Memo;
// Label only defined for line item field
@UI: { lineItem: [ { label: 'Set to Booked' } ]
Status;
}
Quick Info
@UI.identification: [{position:
Annotation Syntax <value>}]
Preview on Fiori UI
UI Identification Fields sorted in order of the position values on the Object Page
UI Line Item Fields sorted in order of the position values on the List Report
Place the annotation in the element list section of your CDS Data Definition or Metadata Extension, directly
preceding the respective element. The value for position is relative, meaning that the order of the elements
on the UI is defined by the relative order of the values for position of each element.
We recommend that you leave gaps between the respective values for position. This will make it easier to add
an element between two other elements retroactively, as you will not need to change the values of position
for all other elements.
UI.facet
UI.fieldGroup
UI.identification
UI.lineItem
UI.selectionField
UI.statusInfo
You can find more details about this in UI Annotations [page 746].
Code Snippet
In this example we use @UI.identification.position to define the order of the identification fields in the
facet with type #IDENTIFICATION_REFERENCE on the object page. Similarly we define the order of the fields
(i.e. columns) of the list report with the annotation @UI.lineItem.position.
You can find the Metadata Extension files for the Travel Scenario in the Complete UI Annotations Example Code
[page 665].
...
annotate view Z_C_TRAVEL_R with
{
...
// Body Facets (Object Page)
...
// Facet 3
{ id: 'Facet3-ID',
purpose: #STANDARD,
type: #IDENTIFICATION_REFERENCE, // Refers to elements
annotated with '@UI.identification' in the element list below
label: 'Facet 3 - Overview and Comments',
position: 30 }
]
// ELEMENT LIST
How to use fieldgroups to structure the content on your UI using UI annotations in CDS.
Quick Info
Related Topics Using Facets to change the Object Page Layout [page 650]
Facets can also collect fieldgroup items from other data definitions. In the example below, 'Fieldgroup1-ID'
assigns items from _Customer to the parent Facet1-ID in the travel CDS view. The items are described in the
Metadata Extension for _Customer. 'Fieldgroup2-ID' assigns items from the travel CDS view to the same
parent Facet1-ID.
Code Snippet
Find the Metadata Extension files for the Travel Scenario in the Complete UI Annotations Example Code [page
665].
1. Metadata Extension for TRAVEL (Data Definition for list report table)
...
annotate view Z_C_CUSTOMER_R with
{
@UI.fieldGroup: [{qualifier: 'Fieldgroup1', position: 05}]
Street;
/* Associations */
//Z_UI_CUSTOMER
@UI.fieldGroup: [{qualifier: 'Fieldgroup1', position: 100}]
_Country;
}
Quick Info
To make sure that the most relevant info is displayed even on small screens, you can define the priority of fields
on the UI. The assigned priority is recognized by Fiori Elements and used to decide which fields will be
dynamically hidden in case the UI is displayed on a smaller screen, e.g. a smartphone.
Code Snippet
In this example we use the annotation @UI.lineItem.importance to make sure that the TravelID,
AgencyID and CustomerID columns on the list report have high priority.
...
annotate view Z_C_TRAVEL_R with
{
...
@UI: { lineItem: [ { importance: #HIGH } ]
TravelID;
@UI: { lineItem: [ { importance: #HIGH } ]
AgencyID;
@UI: { lineItem: [ { importance: #HIGH } ]
CustomerID;
...
}
How to add selection fields for filtering lists or tables on your UI using UI annotations in CDS.
Quick Info
@UI.selectionField: [ { position:
Annotation Syntax <value> } ]
The annotation displays selection fields for the corresponding colums of the list report. The subannotation
@UI.selectionField.position defines the order of the selection fields on the UI. Labels of selection fields
are derived from the data elements in the ABAP Dictionary. If you want to use different labels one the UI, define
labels with the @EndUserText.label annotation. Selection fields enable the user to filter a long list or table
for specific values, such as Customer ID and/or Agency ID (see example).
Place the annotation in the element list section of your CDS Data Definition or Metadata Extension, preceding
each respective element that you want to filter your table for.
You will find more details in the UI Annotations [page 746].
Code Snippet
Find the Metadata Extension files for the Travel Scenario in the Complete UI Annotations Example Code [page
665].
6.4.2.2 Tables
How to use UI annotations in CDS to design the layout of lists and tables on the UI.
The following topics apply to tables both on the list report and on the object page. Note that list reports always
use tables, while object pages may integrate tables.
Find more information about Fiori UI floorplans on Introduction to SAP Fiori Elements .
You will be guided through the following tasks:
Note
The table header of a table can only be defined on a list report. Tables on object pages do not have a table
header.
How to define the table header of a list report of your UI using UI annotations in CDS.
Quick Info
Related Topics Defining the Title Section of an Object Page [page 648]
UI Annotations [page 746]
Place the annotation UI.headerInfo.typeNamePlural in the header section of your CDS Data Definition or
Metadata Extension.
Code Snippet
Find the Metadata Extension files for the Travel Scenario in the Complete UI Annotations Example Code [page
665].
Use UI annotations to influence the design of the object page on your UI.
The object page is mostly used to show details about one entry of a data table. On an object page you can
gather information for one entry from different CDS views in different layouts. Click on a row of the list report
page to navigate to the object page of the corresponding entry.
Find more information about the object page in Defining CDS Annotations for Metadata-Driven UIs [page 629]
or in the Introduction to SAP Fiori Elements .
How to use UI annotations in CDS to define the title of the object page of your UI.
Quick Info
Related Topics Defining the Table Header of a List Report [page 646]
UI Annotations [page 746]
Place the annotations @UI.headerInfo.typeName to define the Object Page title and
@UI.headerInfo.title.value to define the Object Page title value in the header section of your CDS Data
Definition or Metadata Extension.
Note
You can display another value in the title section with the annotation
@UI.headerInfo.description.value.
Code Snippet
Find the Metadata Extension files for the Travel Scenario in the Complete UI Annotations Example Code [page
665].
Quick Info
Facets are useful for organizing the basic layout of a page, on the object page for example. @UI.facet can be
refined with a variety of subannotations. Place the annotation in the facet section of your CDS Data Definition
or Metadata Extension.
The annotation @UI.facet.purpose defnes whether the facet is a header or a standard facet. On the other
hand, the annotation @UI.facet.type defines the format of a facet and the types of fields that will be
displayed in a facet.
purpose: #HEADER Displays the facet and its content in the header section of
the object page.
purpose: #STANDARD Use this for standard facets. Can be omitted in most cases.
type: #COLLECTION The facet serves as parent (or container) for other facets.
Child facets reference the parent facet by 'parentId'.
hidden: true Used to hide facets from a page view. The fields are not dis
played but still available for calculations for example.
Parent facets are annotated with type: #COLLECTION. To nest another facet into a parent facet, you must use
the annotations below.
To retrieve data from a source, you must define the targetElement in the facet. If there is no
targetElement defined, the default target is the entity in which the facet is defined. To nest field into a
fieldgroup, you must define the targetQualifier of the facet. The value of targetQualifier is referenced
by fields with the annotation @UI.fielgroup.qualifier.
targetElement: '…' Needed for retrieving data from associated Data Definitions,
e.g. _Customer
To further customize the layout, you can define the label and position of each facet. By default, the position
of facets will be the order in which they are defined in the source code.
label: '…' Label displayed in the facet header and navigation tabs.
position: <value> Sorts facets in the order of the 'position' values. For
more information, see Positioning Fields [page 636].
Code Snippet
Find the Metadata Extension files for the Travel Scenario in the Complete UI Annotations Example Code [page
665].
...
annotate view Z_UI_TRAVEL with
{
//FACET SECTION
@UI.facet: [
{ id: 'HeaderFacet',
{ id: 'Facet1-ID',
type: #COLLECTION,
label: 'Facet 1 - Customer & Travel Data',
position: 10 },
{ id: 'Facet2-ID',
type: #LINEITEM_REFERENCE, // Facet shows the
referenced items in a list report
label: 'Facet 2 - Bookings',
targetQualifier: 'Facet2',
position: 20,
targetElement: '_Booking'},
// Facet 3
{ id: 'Facet3-ID',
purpose: #STANDARD,
type: #IDENTIFICATION_REFERENCE, // Refers to elements
annotated with '@UI.identification' in the element list below
label: 'Facet 3 - Administration Data',
position: 30 }
]
// ELEMENT LIST
How to use UI annotations in CDS to hide fields from the UI without deleting them.
Quick Info
Description Hides fields from the object page without deleting them.
In general, all fields that are exposed by the OData service are available to the client, regardless of whether the
fields are exposed explicitly using UI annotations. To enable end-user personalization, the client may offer the
possibility to add fields that are hidden for UI consumption, for example in facets on the object page. Use the
annotation @UI.hidden to prevent fields from being displayed on the UI and in the personalization dialog, but
leaving the field available for a WebAPI client.
You can also use this annotation if, for example, a CDS view contains technical keys, for example GUIDs, that
must be exposed to the OData service to work. These keys are usually not supposed to be displayed on the UI.
@UI.hidden: true is also useful if fields are required in calculations, but not supposed to be displayed on the
UI. Hiding fields is especially useful if you want to hide fields dynamically, e.g. if you want to show fields only
when they have positive values.
Note that you can annotate fields of list reports with @UI.hidden: true as well. However, this will only
deselect the annotated fields as columns, not hide them completely. It is effectively the same as if you hadn't
annotated the element with @UI.lineItem in the first place.
Place the annotation in the element list section of your CDS Data Definition or Metadata Extension, directly
preceding the respective element.
Code Snippet
In this example, the Booking Fee shall not be displayed to the user, but might be necessary to calculate the total
price.
Find the Metadata Extension files for the Travel Scenario in the Complete UI Annotations Example Code [page
665].
How to use the hidden annotation to hide fields dynamically on the object page of your application.
Quick Info
@UI.identification: [{hidden:#('virtual
Annotation Syntax Element') }]
Use You can define a condition for the field visibility with a virtual
element. Depending on the condition, the field is visible or
not.
You can use the hidden annotation in combination with a virtual element to show or hide fields dynamically on
the UI depending on a boolean value. If the return value corresponds to true, the respective field is hidden and
if the value corresponds to false the respective field is shown on the user interface. You require the following
steps to enable dynamic field hiding with a virttual element:
1. Define a virtual element in the behavior definition projection. For more general information about how to
implement a virtual element, see Using Virtual Elements in CDS Projection Views [page 573].
2. Create an ABAP class to determine the return value for the virtual element.
3. Annotate the field you want to dynamically hide the respective field on the object page.
In the following you can see sample snippets of all components and below you can find an a sample
implementation for a particular use case.
Code Snippet
In the following definition, the extracted field is checked for its numeric value, if the value of FieldToCheck is
greater than 1000 then the method returns abap_true and the Field is hidden on the object page. If the
contained value is less then 1000, the methord returns abap_false and the Field is displayed on the user
@ObjectModel.virtualElementCalculatedBy: 'ABAP:Z_ShowField'
virtual testField :abap_boolean,
}
ENDMETHOD.
METHOD IF_SADL_EXIT_CALC_ELEMENT_READ~GET_CALCULATION_INFO.
* Extract the field you want the field visibility to depend on
*
IF IV_ENTITY <> '/DMO/Projection_View'.
RAISE EXCEPTION TYPE /dmo/cx_virtual_elements
EXPORTING
TEXTID = /dmo/cx_virtual_elements=>ENTITY_NOT_KNOWN
ENTITY = IV_ENTITY.
ENDIF.
LOOP AT IT_REQUESTED_CALC_ELEMENTS ASSIGNING FIELD-SYMBOL(<FS_CALC_ELEMENT>).
CASE <FS_CALC_ELEMENT>.
WHEN 'testField'.
APPEND 'FieldToCheck' TO ET_REQUESTED_ORIG_ELEMENTS.
...
ENDCASE.
ENDLOOP.
ENDMETHOD.
ENDCLASS.
Quick Info
Use To be placed in the CDS view, above the element that you
want to aggregate.
Note
You must aggregate data, otherwise your chart won't display meaningful data, i.e. will only display data of
one instance. For more information on aggregating data, see Using Aggregate Data in SAP Fiori Apps [page
565].
Use in context
You must define a facet for your chart to define a spot for the chart on the object page and the data source of
the chart. See the sample code below for a better look on how a facet for a chart is defined.
label Defines the name of the facet. The facet will not have a
name on the UI if you don't define a label.
purpose: #STANDARD Defines the purpose of the facet. Can be omitted in most
cases.
type: #CHART_REFERENCE Defines the type of the facet. Charts can only be displayed
in facets of type: #CHART_REFERENCE.
targetElement Specifies where the data for the chart is retrieved from.
Note
Charts can only be displayed in facets with purpose: #STANDARD. Charts are not supported in facets with
purpose: #HEADER.
To aggregate data, you have to use the @Aggregation.default annotation. In our example, we use this
annotation to aggregate all flight prices and to convert them accordingly. We also use the annotation to
aggregate the number of distinct bookings. For more information on aggregating data, see Using Aggregate
Data in SAP Fiori Apps [page 565].
_Customer.CountryCode as CountryCode,
@Aggregation.referenceElement: ['BookingID']
@Aggregation.default: #COUNT_DISTINCT
cast( 1 as abap.int4 ) as
DistinctBookings,
...
@Aggregation annotations are not permitted in metadata extensions. Therefore you have to place these
annotations directly into your CDS view.
See the sample code below for a better look on how the chart itself is defined. Note that you must place this
annotation in the entity previously specified by targetElement or its respective metadata extension.
@UI.chart: [{
qualifier: 'Chart1', //refers to targetQualifier defined in chart facet in
Z_C_TRAVEL_UI
title: 'Flight Prices of this Travel by Airline',
chartType: #COMBINATION_DUAL,
dimensions: [ 'AirlineID'],
measures: [ 'ConvertedFlightPrice', 'DistinctBookings'],
measureAttributes: [ {measure: 'ConvertedFlightPrice', role: #AXIS_1},
{measure: 'DistinctBookings', role: #AXIS_2} ],
dimensionAttributes: [ {dimension: 'AirlineID', role: #SERIES} ],
description: 'Chart shows flight prices of travel by the airlines used per
each booking.'
}
]
...
annotate view Z_UI_BOOKING_U with {
...
chartType Defines the type of the chart. There is a wide variety of chart
types available. For more information on chart types, see
Chart Types [page 663].
Get an overview of all currently supported chart types within CDS and their required dimension/measures.
Axis: x Axis: y
COLUMN_STACKED
Note: AREA, AREA_STACKED and
COLUMN_STACKED_100 AREA_STACKED_100 have the same re
sult as COLUMN, COLUMN_STACKED
AREA
and COLUMN_STACKED_100 on the UI.
AREA_STACKED
AREA_STACKED_100
LINE
Axis: y Axis: x
BAR_STACKED
Note: HORIZONTAL_AREA, HORIZON
BAR_STACKED_100 TAL_AREA_STACKED and HORIZON
TAL_AREA_STACKED_100 have the
HORIZONTAL_AREA
same result as BAR, BAR_STACKED
and BAR_STACKED_100 on the UI.
HORIZONTAL_AREA_STACKED
HORIZONTAL_AREA_100
COLUMN_DUAL
COLUMN_STACKED_DUAL
COLUMN_STACKED_DUAL_100
COMBINATION_STACKED_DUAL
LINE_DUAL
HORIZONTAL_COMBINATION_DUAL
BAR_DUAL
BAR_STACKED_DUAL
BAR_STACKED_DUAL_100
HORIZONTAL_COMBINA
TION_STACKED_DUAL
Axis: y Axis: x
Axis: x Axis: y
Axis: x Axis: y
Axis: y Axis: x
Z_C_TRAVEL_R
@Metadata.layer: #CORE
@UI: { headerInfo: { typeName: 'Object Page - Title',
title: { value: 'TravelID' }, // Defines the value shown in
title section
}
}
{ id: 'HeaderFacet',
purpose: #HEADER,
type: #FIELDGROUP_REFERENCE,
label: 'Object Page - Header Facet',
targetQualifier: 'Fieldgroup:HeaderItems', // Refers to lineItems
with @UI.fieldGroup: [{qualifier: 'Fieldgroup:HeaderItems'}]
position: 10 },
// Body Facets (Object Page)
{ id: 'Facet1-ID',
type: #COLLECTION,
label: 'Facet 1 - Customer & Travel Data',
position: 10 },
{ id: 'Facet2-ID',
type: #LINEITEM_REFERENCE, // Facet shows the
referenced items in a list report
label: 'Facet 2 - Bookings',
targetQualifier: 'Facet2',
position: 20,
targetElement: '_Booking'},
// Facet 3
{ id: 'Facet3-ID',
purpose: #STANDARD,
type: #IDENTIFICATION_REFERENCE, // Refers to elements
annotated with '@UI.identification' in the element list below
label: 'Facet 3 - Overview and Comments',
position: 30 }
]
// ELEMENT LIST
Z_C_CUSTOMER_R
@Metadata.layer: #CORE
annotate view Z_C_CUSTOMER_R
with
{
@UI.fieldGroup: [{qualifier: 'Fieldgroup1', position: 05}]
Street;
/* Associations */
@UI.fieldGroup: [{qualifier: 'Fieldgroup1', position: 100}]
_Country;
}
Z_C_BOOKING_R
@Metadata.layer: #CORE
@UI.chart: [{
qualifier: 'Chart1', //refers to targetQualifier defined in chart facet in
Z_C_TRAVEL_UI
title: 'Flight Prices of this Travel by Airline',
chartType: #COMBINATION_DUAL,
dimensions: [ 'AirlineID'],
measures: [ 'ConvertedFlightPrice', 'DistinctBookings'],
measureAttributes: [ {measure: 'ConvertedFlightPrice', role: #AXIS_1},
{measure: 'DistinctBookings', role: #AXIS_2} ],
dimensionAttributes: [ {dimension: 'AirlineID', role: #SERIES} ],
description: 'Chart shows flight prices of travel by the airlines used per
each booking.'
}
]
annotate view Z_C_BOOKING_R with
{
@UI.lineItem:
[{ qualifier: 'Facet2',
position: 10,
label: 'Travel No.',
hidden: true }]
TravelID;
@UI.lineItem:
[{ qualifier: 'Facet2',
position: 20,
label: 'Booking No.' }]
BookingID;
@UI.lineItem:
[{ qualifier: 'Facet2',
position: 30,
label: 'Date of Booking' }]
BookingDate;
@UI.lineItem:
[{ qualifier: 'Facet2',
position: 40,
label: 'Customer' }]
CustomerID;
@UI.lineItem:
[{ qualifier: 'Facet2',
position: 50,
label: 'Airline' }]
AirlineID;
@UI.lineItem:
[{ qualifier: 'Facet2',
position: 60,
label: 'Connection No.' }]
ConnectionID;
@UI.lineItem:
Get information about what UI annotations to use to manipulate fields for SAP Fiori UIs.
This chapter describes annotations that influence the appearance exposed fields. When a field is marked with
these annotations, it is manipulated no matter in what other annotations the field is used. The reason for this is
that annotations for manipulation are self-contained annotations and not properties of other annotations.
For example, when a field is marked with the @UI.masked [page 813] annotation, the field is masked
regardless if it is used in a @UI.lineItem [page 820]annotation or a @UI.identification [page 827] annotation.
To manipulate the appearance of fields on SAP Fiori UIs, you can use the annotations explained in the following
sections:
Get information about what UI annotations to use to display fields as multi-line text on SAP Fiori UIs.
You can use the following annotation to mark a field to be displayed by a control that supports multi-line input,
for example a text area:
Sample Code
...
define view Product as select from ... {
@UI.identification: [ { position: 10 } ]
key ProductID,
@UI.identification: [ { position: 30 } ]
@UI.multiLineText: true
Description,
...
}
Related Information
Get information about what UI annotations to use to mask fields, for example for password input, on SAP Fiori
UIs.
In some cases, data of fields need to be consumed by the client, but must not be visible on the UI. This field
behavior is required when users need to enter passwords, for example.
You can use the following annotation to mark a field to not to be displayed in clear text by the client because, for
example, it contains sensitive data:
Sample Code
...
define view Destination as select from ... {
@UI.identification: [ { position: 10 } ]
key DestinationID,
...
@UI.identification: [ { position: 20 } ]
AuthType, -- None, Basic, SSO, ...
@UI.identification: [ { position: 30 } ]
BasicAuthUserName,
@UI.identification: [ { position: 40 } ]
@UI.masked
BasicAuthPassword,
...
}
In addition to UI annotations, you can use model-specific annotations that affect the desired client behavior.
You can implement, for example, unit-currency-mappings or ID-text-mappings. These model-specific
annotations can be evaluated by the client and no additional UI annotations are required.
Example
In the following example, the field CurrencyCode is marked with a @Semantics.currencyCode [page 743]
and is referenced by field GrossAmount. This means that the field GrossAmount is always displayed with
the corresponding currency. The field CurrencyCode does not need to be exposed explicitly.
Sample Code
...
define view ZExample_SalesOrder as select from sepm_cds_sales_order as so
{
@UI.identification: [ { position: 10 } ]
key so.sales_order_id as SalesOrder,
@Semantics.currencyCode: true
so.currency_code as CurrencyCode,
@Semantics.amount.currencyCode: 'CurrencyCode'
@UI.identification: [ { position: '20' } ]
so.gross_amount as GrossAmount,
Get information about what property to use to prevent elements from being inherited from an underlying CDS
view.
By default, all UI annotation elements are inherited from the underlying CDS view. You can explicitely disable
this behavior. You can use the following property to prevent a UI annotation element from being inherited:
● exclude
The following sample code depicts the CDS view ZP_SalesOrder that inherits elements from the
underlying CDS view SEPM_CDS_SALES_ORDER, and uses the UI annotation @UI.identification [page 827]:
Sample Code
...
define view ZP_SalesOrder as select from sepm_cds_sales_order as so {
@UI.identification: [ { position: 10 } ]
key so.sales_order_id as SalesOrder,
@UI.identification: [ { position: 20 } ]
so.customer.company_name as CompanyName,
...
}
The following sample code depicts the CDS view ZI_SalesOrder that inherits elements from the
underlying CDS view ZP_SalesOrder. In this view, the element key SalesOrder is inherited from the
underlying CDS view as UI annotation @UI.identification [page 827] by default. The element
so.customer.company_name as CompanyName, however, is not inherited as UI annotation
@UI.identification [page 827] because of the property exclude:
Sample Code
...
define view ZI_SalesOrder as select from ZP_SalesOrder as so {
key SalesOrder,
@UI.identification: [ { exclude } ]
so.customer.company_name as CompanyName,
...
}
Get an overview of how to use data points to display criticality, trends, and references to people and time
periods on SAP Fiori UIs.
In some cases, you want to visualize a single point of data that typically is a number that can be enriched with
business-relevant data but may also be textual, for example a status value.
You can use the following UI annotation to define a single point of data:
Sample Code
...
define view ZExample_SalesOrdersByCustomer as select from ... as so {
key so.buyer_guid as BuyerGuid,
@Semantics.currencyCode: true
so.currency_code as CurrencyCode,
Related Information
6.4.2.5.2.1 Criticality
Get information about how to use data points to display criticality on SAP Fiori UIs.
A more usable variant of the UI annotation @UI.dataPoint [page 786]also contains information about the
criticality, the trend, and the name of a person responsible.
You can use the sub-annotation @dataPoint.criticality [page 790] to express if a value is positive or negative, for
example.
You can use the sub-annotation @dataPoint.trend [page 793] to express if a value has decreased or increased,
for example.
In this case, the properties targetValue, criticality, and trend are already evaluated in the CDS view. In
the CDS view, the target value is already calculated, and if the current value thus is negative or positive, and if
the current value has improved or declined, for example. These values are only referred to from the
@UI.dataPoint [page 786] annotation.
Data can be defined as being either positive, critical, or negative. These data can be statuses, for example.
1 Negative Red
2 Critical Yellow
3 Positive Green
Sample Code
...
define view ZExample_SalesOrdersByCustomer as select from ... as so {
key so.buyer_guid as BuyerGuid,
@Semantics.currencyCode: true
so.currency_code as CurrencyCode,
@UI.dataPoint: {
title: 'Gross Amount',
targetValueElement: 'TargetAmount', -- Reference to element
criticality: 'AmountCriticality', -- Reference to element
trend: 'AmountTrend', -- Reference to element
}
@Semantics.amount.currencyCode: 'CurrencyCode'
so.actual_amount as ActualAmount,
@Semantics.amount.currencyCode: 'CurrencyCode'
so.target_amount as TargetAmount,
so.criticality as AmountCriticality,
so.trend as AmountTrend
}
Related Information
6.4.2.5.2.2 Trends
Get information about how to use data points to display trends on SAP Fiori UIs.
Data can be defined as being either increasing, decreasing, or stable. These data can be measured over a
certain period of time and visualized on the UI.
Example
For an example, see the example code in section Criticality linked below.
Example
The table below lists the values that are valid for the UI annotation @UI.dataPoint.trendCalculation
[page 793], and shows how these values are visualized on the UI:
1 Strong up
2 Up
3 Sideways
4 Down
5 Strong down
For the trend calculation, the flag isRelativeDifference indicates whether the absolute or the relative
difference between the actual value and the reference value is used to calculate the trend.
Sample Code
...
define view ZExample_SalesOrdersByCustomer as select from ... as so {
key so.buyer_guid as BuyerGuid,
@Semantics.currencyCode: true
so.currency_code as CurrencyCode,
Related Information
Get information about how to use data points to calculate and display trend-criticality relations.
Another way to specify properties of criticality and trend is to define rules for criticality and trend within the UI
annotation @UI.dataPoint [page 786].
You can use the following sub-annotations to calculate trends and derive from these calculation the criticality of
data:
Sample Code
...
define view ZExample_SalesOrdersByCustomer as select from ... as so {
key so.buyer_guid as BuyerGuid,
@Semantics.currencyCode: true
so.currency_code as CurrencyCode,
@UI.dataPoint: {
title: 'Gross Amount',
targetValue: 9216,
criticalityCalculation: {
improvementDirection: #TARGET,
toleranceRangeLowValue: 9200,
toleranceRangeHighValue: 9300,
deviationRangeLowValue: 8800,
deviationRangeHighValue: 9700
},
trendCalculation: {
referenceValue: 'ReferenceAmount', -- Reference to element
isRelativeDifference: false, -- Comparison of difference
strongUpDifference: 100,
upDifference: 10,
downDifference: -10,
strongDownDifference: -100
}
}
@Semantics.amount.currencyCode: 'CurrencyCode'
so.target_amount as TargetAmount,
@Semantics.amount.currencyCode: 'CurrencyCode'
so.reference_amount as ReferenceAmount
}
Note
This also applies to the properties of the sub-annotation @UI.dataPoint.trendCalculation [page 793],
except for the property referenceValue. This property always references to another element.
Example
Related Information
Get information about how to use data points to display references to persons responsible and to reference
periods on SAP Fiori UIs.
You can add the following properties to the UI annotation @UI.dataPoint [page 786]:
● referencePeriod
● responsibleName
You can define both properties either in the UI annotation directly, or in another element and reference from
the UI annotation to this element.
Example
In the following example, the data point has a static reference period and a static person responsible. The
value of the gross amount is formatted with the valueFormat property. The value is thus scaled with
factor 1000 and is displayed with one decimal place, this is the value 34500 EUR would be displayed as
34.5 kEUR.
Sample Code
...
define view ZExample_SalesOrdersByCustomer as select from ... as so {
key so.buyer_guid as BuyerGuid,
@Semantics.currencyCode: true
so.currency_code as CurrencyCode,
@UI.dataPoint: {
title: 'Gross Amount',
description: 'Gross Amount per Customer',
longDescription: 'The gross amount per customer ...',
valueFormat: {
scaleFactor: 1000,
numberOfFractionalDigits: 1
},
referencePeriod: { description: '2015 Q3' },
responsibleName: 'John Doe'
}
@Semantics.amount.currencyCode: 'CurrencyCode'
so.actual_amount as ActualAmount
}
Example
In the following example, a dynamic reference period is used that is supplied by the following parameters:
● start
● end
These parameters have to be aliased in the element list before they can be used in the @UI.dataPoint [page
786] annotation. The responsible property must refer to a to-one-association. The target entity of this
association should contain the contact data of the person responsible.
Sample Code
...
@UI.dataPoint: {
title: 'Gross Amount',
referencePeriod: {
start: 'StartDate', -- Reference to element
end: 'EndDate' -- Reference to element
},
responsible: '_PersonResponsible' -- Reference to association
}
@Semantics.amount.currencyCode: 'CurrencyCode'
@DefaultAggregation: #SUM
so.actual_amount as ActualAmount,
_PersonResponsible
}
where so.validity_date >= $parameters.p_StartDate
and so.validity_date <= $parameters.p_EndDate
Related Information
Get information about how to use the type #AS_DATAPOINT to refer to other annotations.
You can use the following type to reference an exposed data point from dataField-like annotations:
● #AS_DATAPOINT
Example
In this example, the UI annotation @UI.lineItem [page 820] has to be defined at the same CDS element as
the UI annotation @UI.dataPoint [page 786] itself.
Sample Code
...
define view ZExample_SalesOrdersByCustomer as select from ... as so {
key so.buyer_guid as BuyerGuid,
...
@Semantics.currencyCode: true
so.currency_code as CurrencyCode,
Related Information
6.4.2.5.3 Actions
Get information about how to use dataField types to provide means of executing actions on SAP Fiori UIs.
Actions are directly related to items that you can see in a table on a master-detail floorplan, for example. Users
can select items and execute certain actions on the selected items.
You can use the following dataField type to expose actions to the client:
● #FOR_ACTION
This property has to be assigned to some arbitrary element. It is thereby irrelevant if the property refers to
the element to which the property is assigned.
Sample Code
...
define view ZExample_SalesOrder as select from sepm_cds_sales_order as so {
@UI.lineItem: [
-- Standard Lineitem
{ position: 10 },
-- Action Lineitem
{ type: #FOR_ACTION, dataAction: 'Copy', label: 'Copy' }
]
key so.sales_order_id as SalesOrder,
...
6.4.2.5.4 Navigation
Get an overview of how to use dataField types to provide means of navigation on SAP Fiori UIs.
It often is not sufficient to stay on one screen. Users might need to navigate between screens or even to web
sites outside an application. You can use the following dataField types ton include navigation concepts:
Get information about how to provide navigation between UI screens and pages on SAP Fiori UIs.
This navigation type contains either a navigation property or a term cast. The term either is of type
Edm.EntityType, a concrete entity type, or a collection of these types.
You can use the following dataField type to expose a link to other pages of a UI:
● #WITH_NAVIGATION_PATH
Example
Sample Code
...
define view ZExample_SalesOrder as select from sepm_cds_sales_order as
so
association [0..1] to sepm_cds_business_partner as _BusinessPartner
on $projection.buyer_guid = _BusinessPartner.business_partner_key
{
key so.sales_order_id as SalesOrder,
so.buyer_guid,
...
@UI.lineItem: [ {
position: 20,
Related Information
Get information about how to provide navigation from SAP Fiori UIs to external web sites, for example.
This type navigation type contains a reference to a URL to navigate to specific web sites, for example.
You can use the following dataField type to display links to external websites:
● #WITH_URL
Example
In the following example, CompanyName is displayed as link referring to the CDS element WebsiteUrl.
Sample Code
...
define view ZExample_SalesOrder as select from sepm_cds_sales_order as
so
{
key so.sales_order_id as SalesOrder,
...
@UI.lineItem: [ {
position: 20,
type: #WITH_URL,
url: 'WebsiteUrl' -- Reference to element
} ]
so.customer.company_name as CompanyName,
Related Information
Get information about how to provide navigation related on actions that are executed on SAP Fiori UIs.
This navigation type contains an action that is related to a semantic object. This combination of action and
semantic object is an intent. The annotation @Consumption.semanticObject [page 698] is required for
navigation based on intent. The client decides how to react when this navigation is triggered.
You can use the following dataField type to expose the intent to navigate without specifying how this navigation
is to be resolved:
● #FOR_INTENT_BASED_NAVIGATION
Example
In the following example, the intent 'Show' (action) 'BusinessPartner' (semantic object) is
expressed. The client can, for example, open a separate application to display the details of the
corresponding business partner.
Sample Code
...
define view ZExample_SalesOrder as select from sepm_cds_sales_order as
so
{
@UI.lineItem: [ {
position: 20,
label: 'Show customer-details',
type: #FOR_INTENT_BASED_NAVIGATION,
semanticObjectAction: 'Show' -- Action
} ]
@Consumption.semanticObject: 'BusinessPartner' -- Semantic Object
so.customer.company_name as CompanyName,
...
}
Related Information
Get information about what UI annotations to use to display contact data on SAP Fiori UIs.
In some cases users of an application need to see contact data, for example, of business partners, customers,
or employees.
You can use the following annotation set to inform a client that an entity contains contact information and map
the CDS elements to the corresponding address field:
● @Semantics
This annotation set contains annotations to inform about telephone numbers, email addresses, names,
addresses, and contacts.
Example
The following example contains sub-annotations belonging to the annotation set @Semantics. For a
complete list, see section Semantics Annotations linked below.
Sample Code
...
define view Employees as select from ...
{
key EmployeeId,
@Semantics.name.givenName
FirstName,
@Semantics.name.additionalName
MiddleName,
@Semantics.name.familyName
LastName,
Related Information
The following list summarizes SAP annotations of the Data Definition Language (DDL) of ABAP CDS that are
relevant in the context of ABAP RESTful programming model and released for ABAP Cloud Platform.
SAP CDS annotations are evaluated by SAP frameworks and can be either ABAP annotations or framework-
specific annotations.
● AbapCatalog Annotations
● AccessControl Annotations
● ClientHandling Annotations
● EndUserText Annotations
● Environment Annotations
● MappingRole Annotations
● Metadata Annotations
● Semantics Annotations
Tip
To access help for an ABAP annotation, position the cursor on the relevant annotation in the DDL editor and
choose F1 .
Framework-Specific Annotations
Framework-specific CDS annotations (as a rule) are exposed for OData and evaluated during runtime.
Related Information
@Scope:[#ELEMENT]
annotation Aggregation
{
default: String(30) enum
{
NONE;
SUM;
MIN;
MAX;
AVG;
COUNT_DISTINCT;
NOP;
FORMULA;
};
referenceElement : array of ElementRef;
};
Usage
Annotation Meaning
Aggregation.Default When the Aggregation annotation has been specified for an element, the corresponding ele
ments are used as so called measures (elements that can be aggregated) in analytical sce
narios. These measures are aggregated automatically with the Aggregation. In SQL SELECT
statements you have to specify the aggregation behavior explicitly.
Scope: [ELEMENT]
Value Description
SUM, MAX, MIN, AVG All these values determine the aggregation of the measure.
Aggregation.Referen elementRef References the element that is used for distinct counts.
ceElement
Note
Can only be used in combination with
@Aggregation.Default: #COUNT_DISTINCT.
Example
The example demonstrates how you can use the aggregate functions on measure elements.
Enable application developers to define how the authorization check for a CDS entity is executed
@Scope:[#VIEW, #TABLE_FUNCTION]
AccessControl.authorizationCheck : String(20) enum { NOT_REQUIRED; NOT_ALLOWED;
CHECK; PRIVILEGED_ONLY; } default #CHECK;
Usage
Annotation Meaning
Engine Behavior: The runtime and design-time engines handle the authorization check
based on the value of the element.
Values:
Value Description
NOTE
Example
When the developer activates the following DDL document, since an authorization check is not required, ABAP
development tools do not produce a warning. It does not matter whether a role exists for the entity or not.
At runtime, if there is a role for the entity, then ABAP performs an authorization check with the role. If there is
no role, there is no check and no protection for the entity.
Sample Code
@AbapCatalog.sqlViewName: 'DEMO_CDS_PRJCTN'
@AccessControl.authorizationCheck: #NOT_REQUIRED
define view demo_cds_spfli
as select from spfli
{ key spfli.carrid,
key spfli.connid,
spfli.cityfrom,
spfli.cityto }
Define a specific behavior that relates to the consumption of CDS content through domain-specific
frameworks.
Usage
Via these annotations, the specific behavior is defined which is related to the consumption of CDS content.
This metadata makes no assumptions about the concrete consumption technology/infrastructure, but it is
applicable across multiple consumption technologies (e.g. Analytics or OData).
Annotation Definition
Consumption.hidden Boolean (true, This annotation prevents fields from being exposed by
false) (default false) OData. Therefore, the field will not be exposed to UIs.
Example
SAP Fiori has introduced the concept of intent-based
navigation, whereby an intent is a combination of
<semanticObject> <action>. A
semanticObject annotation is used in SAP Fiori UIs
to dynamically derive navigation targets for the anno
tated view as a source.
Consumption.valueHe string This annotation defines whether the values of the value help
lpDefault.fetchValu provider view are automatically retrieved without setting a
es: Possible Values:
filter when invoking the value help, or whether they must be
#AUTOMATICALLY_WHEN explicitly requested.
_DISPLAYED
The annotation must be used in the value help provider en
#ON_EXPLICIT_REQUES tity.
T Scope: #ENTITY
Annotations belonging to Consumption.valueHelpDefinition directly establish a relationship to an entity that acts as a value
help provider.
The value help can be consumed without an association to the target value help provider.
element Specifies the element in the target value help provider entity
that is linked to the local element or parameter for the addi
tional binding.
parameter Specifies the parameter in the target value help provider en
tity that is linked to the local element or parameter for the
additional binding.
Consumption.valueHelpDefinition.distinct Specifies whether the value help result list shall only contain
Values distinct values for the annotated field or parameter. If set to
true all mappings will be used for filtering, but only the value
for the field/parameter which the value help was requested
for will be returned by the value help.
Consumption.valueHelpDefinition.entity[] Defines the binding for the value help to the value help pro
viding entity. It requires specification of the entity and the el
ement providing the value help for the annotated element.
Consumption.valueHelpDefinition.presenta The presentation variant indicates how the value help result
tionVariantQualifier should be displayed.
The annotation Consumption.valueHelpDefinition is used to define a value help for the annotated
element. The value help provider can be a different CDS entity without association. To consume the value help,
the value help provider entity must be added to the respective OData service.
You can filter the available value help options by defining an additional binding. In the following example case,
only the business partners are displayed that use the same currency code.
Sample Code
element : 'CurrencyCode'
}]
}]
_BusinessPartner.BusinessPartnerID
Example 2
Sample Code
@Scope:[#ELEMENT]
@API.state: [ #RELEASED_FOR_SAP_CLOUD_PLATFORM ]
text
{
element : array of ElementRef;
association : AssociationRef;
control : String(60) enum { NONE; ASSOCIATED_TEXT_UI_HIDDEN; };
reference
{
association : AssociationRef;
};
};
@Scope:[#VIEW, #CUSTOM_ENTITY]
query
{
implementedBy : String(255);
};
@Scope:[#ELEMENT]
virtualElementCalculatedBy : String(255);
Usage
Annotation Meaning
ObjectModel. Establishes the conjunction of a field with its descriptive language-independent texts.
ObjectModel. Defines the associated view, which provides textual descriptions for the annotated field.
text.associa
tion Scope: [ELEMENT]
● SADL - Enriches the OData entity type of the view with the textual description of the target view
applying an automated language filtering. The name of the auto-generated text property will be
composed out of the annotated field name and the constant suffix _Text. This OData property is
mapped onto the first text field of the associated target CDS view annotated with
@Semantics.text:true.
● Analytic Manager - Uses the associated view as TEXT view for annotated field.
ObjectModel. References the query implementation class for the unmanaged query.
query.implem
Scope: [VIEW, CUSTOM_ENTIY]
entedBy:
This annotation is evaluated when the unmanaged query is executed whereby the query implementa
tion class is called to perform the query.
To reference the query implementation class, ABAP: must be added to the string reference.
Example
@ObjectModel.query.implementedBy: 'ABAP:<query_impl_class>'.
Note
As of SAP Cloud Platform ABAP Environment 1908, this annotation substitutes the deprecated
annotation @QueryImplementedBy: ''.
ObjectModel. References the calculation class for the annotated virtual element.
virtualEleme
Scope: [ELEMENT]
ntCalculated
By: This annotation defines the code exit for virtual elements. The query framework is left during runtime
to retreive the values for the virtual element in the calculation class.
To reference the calculation class, ABAP: must be added tot he string reference.
Examples
Example 1
This example demonstrates how you can define language-dependent texts with a text association.
Sample Code
Example 2
This example demonstrates how you can define language-independent texts within the same view.
Sample Code
Capture OData-related aspects to expose data gained from a CDS entity in an OData service.
Annotation OData
{
@Scope:[#ELEMENT]
etag : Boolean default true;
@Scope:[#ENTITY]
entitySet
{
name : String(30);
};
entityType
{
name : String(128);
};
action: array of {
name : String(128);
localName : String(30);
};
Usage
Runtime:
OaS
r
Dc
r
ao
a
typ
aoe
:
.f
[
a
M
E
ca
L
tn
E
idM
oaE
ntN
oT
r]
y
D
V
e
a
n
l
o
u
t
e
e
s
s
:
t
h● l
e o
e c
x a
t l
e N
r a
n
m
a
e
l
● N
n
a
a
m m
e e
o
f
a
n
a
c
t
i
o
n
f
o
r
a
n
a
r
b
i
t
r
a
r
y
O
D
a
t
a
s
e
r
v
i
c
e
.
T
h
e
a
n
n
o
t
a
t
i
o
n
m
a
p
s
t
h
e
l
o
c
a
l
N
a
m
e
t
o
t
h
e
e
x
t
e
r
n
a
l
O
D
a
t
a
n
a
m
e
.
lSD
ote
crn
aio
lnt
e
Ng(
s
a3
t
m0h
e)e
a
c
t
i
o
n
n
a
m
e
f
o
r
m
a
p
p
i
n
g
t
o
t
h
e
O
D
a
t
a
a
c
t
i
o
n
n
a
m
e
(
n
a
m
e
)
.
nSP
atr
mro
eiv
ni
d
g(
e
1
s
2t
8h
e
a
c
t
i
o
n
n
a
m
e
f
o
r
O
D
a
t
a
.
OSS
Dtc
ar
o
ti
p
an
e
.g(
:
e3
[
n0
t)E
iN
tT
yI
ST
eY
t]
.
D
n
e
a
mn
eo
t
e
s
t
h
e
e
x
t
e
r
n
a
l
n
a
m
e
o
f
t
h
e
e
n
t
i
t
y
s
e
t
f
o
r
a
n
a
r
b
i
t
r
a
r
y
O
D
a
t
a
s
e
r
v
i
c
e
.
N
o
t
e
Y
o
u
c
a
n
a
l
s
o
d
e
fi
n
e
t
h
e
n
a
m
e
o
f
t
h
e
e
n
t
i
t
y
s
e
t
i
n
t
h
e
s
e
r
v
i
c
e
d
e
fi
n
i
t
i
o
n
b
y
u
s
i
n
g
a
n
a
l
i
a
s
:
e
x
p
o
s
e
/
D
M
O
/
C
_
T
r
a
v
e
l
_
U
a
s
T
r
a
v
e
l
.
T
h
e
n
a
m
e
o
f
t
h
e
a
l
i
a
s
i
n
t
h
e
s
e
r
v
i
c
e
d
e
fi
n
i
t
i
o
n
w
i
l
l
b
e
u
s
e
d
e
v
e
n
i
f
y
o
u
u
s
e
t
h
e
a
n
n
o
t
a
t
i
o
n
i
n
t
h
e
C
D
S
v
i
e
w
.
OSS
Dtc
ar
o
ti
p
an
e
.g(
:
e1
[
n2
t8E
i)N
tT
yI
TT
yY
p]
e
D
.
e
n
an
mo
et
e
s
t
h
e
e
x
t
e
r
n
a
l
n
a
m
e
o
f
t
h
e
e
n
t
i
t
y
t
y
p
e
f
o
r
a
n
a
r
b
i
t
r
a
r
y
O
D
a
t
a
s
e
r
v
i
c
e
.
E
x
a
m
p
l
e
T
h
i
s
e
x
a
m
p
l
e
d
e
m
o
n
s
t
r
a
t
e
s
h
o
w
O
D
a
t
a
a
n
n
o
t
a
t
i
o
n
s
c
a
n
b
e
u
s
e
d
t
o
c
h
a
n
g
e
t
h
e
n
a
m
e
o
f
t
h
e
e
n
t
i
t
y
t
y
p
e
i
n
t
h
e
m
e
t
a
d
a
t
a
o
f
t
h
e
O
D
a
t
a
s
e
r
v
i
c
e
:
:
@
O
D
a
t
a
.
e
n
t
i
t
y
S
e
t
.
n
a
m
e
:
'
T
r
a
v
e
l
'
@
O
D
a
t
a
.
e
n
t
i
t
y
T
y
p
e
.
n
a
m
e
:
'
T
r
a
v
e
l
T
y
p
e
'
d
e
f
i
n
e
r
o
o
t
e
n
t
i
t
y
/
D
M
O
/
I
_
T
R
A
V
E
L
{
k
e
y
T
r
a
v
e
l
I
D
a
b
a
p
.
n
u
m
c(
)
;
…
OBS
Doc
aoo
tlp
aee
:
.a
[
en
E
tdN
aeT
gfI
aT
uY
l]
t
D
t
o
r
n
u
o
e
t
u
s
e
t
h
i
s
a
n
n
o
t
a
t
i
o
n
.
D
e
c
l
a
r
e
E
T
a
g
s
i
n
b
e
h
a
v
i
o
r
d
e
fi
n
i
t
i
o
n
s
.
OSS
Dtc
ar
o
ti
p
an
e
.g(
:
p1
[
r2
o8E
p)L
eE
rM
tE
yN
.T
n]
ai
mn
ea
b
s
t
r
a
c
t
e
n
t
i
t
i
e
s
D
e
n
o
t
e
s
t
h
e
e
x
t
e
r
n
a
l
n
a
m
e
o
f
C
D
S
e
l
e
m
e
n
t
s
,
p
a
r
a
m
e
t
e
r
s
o
r
a
s
s
o
c
i
a
t
i
o
n
s
f
o
r
t
h
e
p
r
o
p
e
r
t
i
e
s
o
f
a
n
a
b
s
t
r
a
c
t
e
n
t
i
t
y
.
OSS
Dtc
ar
o
ti
p
an
e
.g(
:
s1
[
c2
h8S
e)E
mR
aV
.I
nC
aE
m]
e
D
e
n
o
t
e
s
t
h
e
e
x
t
e
r
n
a
l
n
a
m
e
o
f
t
h
e
s
e
r
v
i
c
e
i
n
a
n
O
D
a
t
a
r
e
p
r
e
s
e
n
t
a
t
i
o
n
.
T
h
i
s
a
n
n
o
t
a
t
i
o
n
i
s
u
s
e
d
i
n
s
e
r
v
i
c
e
d
e
fi
n
i
t
i
o
n
s
.
This annotation marks a view as searchable. You define the fuzziness threshold as well as the specifics of term
mappings at element level.
@Scope:[#ENTITY]
Annotation Search
{
searchable : Boolean default true;
};
@Scope:[#ELEMENT]
Annotation Search
{
defaultSearchElement : Boolean default true;
ranking : String(6) enum { HIGH = 'high'; MEDIUM = 'medium'; LOW = 'low'; }
default #MEDIUM;
fuzzinessThreshold : Decimal(3,2);
termMappingDictionary : String(128);
termMappingListId : array of String(32);
};
Annotation Meaning
Search.searchable Defines if a CDS entity is generally relevant for search scenarios. This annotation must be
set in case other search-related annotations are being defined for elements of the respec
tive CDS entity. The annotation offers a general switch and a means to quickly detect
whether a view is relevant or not.
Scope: #Entity
Values:
Value Description
Boolean (true, false) Defines whether a view is relevant for search or not.
Default: true
Search.defaultSearc Specifies that the element is to be considered in a freestyle search (for example a
hElement SELECT…) where no columns are specified.
Usually, such a search must not operate on all elements – for performance reasons, and be
cause not all elements (e.g. internal keys) do qualify for this kind of access.
Scope: #Element
Values:
Value Description
Default: true
Search.ranking Specifies how relevant the values of an element are for ranking, if the freestyle search terms
match the element value.
Scope: #Element
Values:
Value Description
Search.fuzzinessThr Specifies the least level of fuzziness (with regard to some comparison criteria passed at run
eshold time) the element has to have to be considered in a fuzzy search at all.
Note
A fuzzy search enables a certain degree of error tolerance and returns records even if
the search term contains additional or missing characters or other types of spelling er
rors.
Note
To perform a fuzzy search you have to set the search mode to fuzzy in the customiz
ing settings of your ABAP system. Find the customizing node under SAP NetWeaver
If in the customizing a value for Fuzzy Similarity is present, the value of the pa
rameter Search.fuzzinessThreshold will become void.
Scope: #Element
Values:
Value Description
Decimal (3,2) The least level of fuzziness the element has to have to be
considered in a fuzzy search at all, e.g. 0.7.
Search.termMappingD Specifies the table that holds the term mappings (synonyms) to be considered in the con
ictionary text of a search on this view.
Scope: #Element
Evaluation Runtime (Engine): No engine usage right now. Reserved for future usage.
Values:
Value Description
Search.termMappingL Specifies one or multiple list IDs within the term mapping dictionary mentioned before.
istID
The list is implemented as a column of the term mapping table, with the list ID as content of
this column. This concept has the aim to enable overarching term mapping dictionaries
while being able to separate domain-specific content at the same time.
Scope: #Element
Evaluation Runtime (Engine): No engine usage right now. Reserved for future usage.
Values:
Value Description
Array of String(32) Defines one or more columns of the term mapping diction
ary.
Example
The following example demonstrates how the search annotations are used in a CDS view.
Sample Code
@Search.searchable: true
define view demo_search
as select from db_flight
{
@Search.defaultSearchElement: true
@Search.fuzzinessThreshold: 0.7
key carrid,
key connid,
@Search.defaultSearchElement: true
@Search.fuzzinessThreshold: 0.7
fldate,
price,
currencycode
}
The search is executed primarily on the elements carrid and fldate with a fuzziness threshold of 0.7.
Used by the core engines for data processing and data consumption
Usage
Semantic annotations complement the concept of semantic data types, while semantic data types always
introduce specific behavior in the provider/core infrastructure (through dedicated operations or conversion
functions).
Semantic annotations allow the standardizing of semantics that only have an impact on the consumption side
(such as currency code representation together with the amount).
Scope: [ELEMENT]
Values:
This can be either an ISO code or an SAP currency code (data type CUKY).
Scope: [ELEMENT]
Values:
Semantics.signReversalI This annotation reverses the sign of the annotated view element
ndicator
Scope: [ELEMENT, PARAMETER]
Annotations belonging to Semantics.systemDate tag elements that specify the date/time that is recorded by the
technical infrastructure/database.
Evaluation Runtime (Engine): Interpreted by the orchestration framework (SADL). Translates CDS annotations into the
corresponding OData annotations.
Semantics.systemDateTim The annotated element contains a timestamp indicating when the database record
e.createdAt was created.
Semantics.systemDateTim The annotated element contains a timestamp indicating when the database record
e.lastChangedAt was last changed.
The annotated element is used for the total ETag field in managed scenarios with draft.
It is automatically updated by the RAP managed runtime framework.
Semantics.systemDateTim The annotated element contains a timestamp indicating when the database record
e.LocalInstanceLastChan was last changed.
gedAt
Values: Boolean default true
The annotated element is used for the ETag master field in managed scenarios. It is au
tomatically updated by the RAP managed runtime framework.
Semantics.text This annotation identifies a human-readable text that is not necessarily language-de
pendent.
Annotations belonging to Semantics.user tag elements that define the ID of the user related to the data record.
Evaluation Runtime (Engine): Interpreted by the orchestration framework (SADL). Translates CDS annotations into the
corresponding OData annotations.
Semantics.user.createdB The value of the annotated field specifies the user who created a data record.
y
Values: Boolean default true
Semantics.user.lastChan The value of the annotated field specifies the user who changed a data record at last.
gedBy
Values: Boolean default true
Examples
Example 1
The following CDS view fetches sales order items. Here, the annotations assign the units and currencies to the
corresponding fields.
Sample Code
@Semantics.currencyCode
currency_code as CurrencyCode,
@Semantics.amount.currencyCode: 'CurrencyCode'
gross_amount as GrossAmount,
@Semantics.unitOfMeasure
unit_of_measure as UnitOfMeasure,
@Semantics.quantity.unitOfMeasure: 'UnitOfMeasure'
quantity as Quantity,
...
}
Example 2
Sample Code
7.1.8 UI Annotations
Represent semantic views on business data through the use of specific patterns that are completely
independent of UI technologies.
@MetadataExtension.usageAllowed : true
{
@Scope:[#ENTITY]
headerInfo
{
@LanguageDependency.maxLength : 40
typeName : String(60);
@LanguageDependency.maxLength : 40
typeNamePlural : String(60);
typeImageUrl : String(1024);
imageUrl : ElementRef;
title
{
type : String(40) enum
{
STANDARD;
WITH_INTENT_BASED_NAVIGATION;
WITH_NAVIGATION_PATH;
WITH_URL;
} default #STANDARD;
@LanguageDependency.maxLength : 40
label : String(60);
iconUrl : String(1024);
criticality : ElementRef;
criticalityRepresentation : String(12) enum
{
WITHOUT_ICON;
WITH_ICON;
} default #WITHOUT_ICON;
value : ElementRef;
targetElement : ElementRef;
url : ElementRef;
};
description
};
trend : ElementRef;
trendCalculation
{
referenceValue : ElementRef;
isRelativeDifference : Boolean default true;
upDifference : DecimalFloat;
upDifferenceElement : ElementRef;
strongUpDifference : DecimalFloat;
strongUpDifferenceElement : ElementRef;
downDifference : DecimalFloat;
downDifferenceElement : ElementRef;
strongDownDifference : DecimalFloat;
strongDownDifferenceElement : ElementRef;
};
responsible : ElementRef;
responsibleName : String(120);
};
@Scope:[#ELEMENT]
selectionField : array of
{
qualifier : String(120);
position : DecimalFloat;
exclude : Boolean default true;
element : ElementRef;
};
@Scope: [#ELEMENT]
valueCriticality: array of
{
qualifier : String(120);
value : String(120);
criticality : Integer enum
{
NEGATIVE;
CRITICAL;
POSITIVE;
};
};
@Scope: [#ELEMENT]
criticalityLabels : array of {
qualifier: String(120);
criticality: Integer enum
{
NEGATIVE;
CRITICAL;
POSITIVE;
};
@LanguageDependency.maxLength: 40
label: String(60);
};
@Scope: [#ELEMENT]
connectedFields : array of
{
qualifier : String(120);
@LanguageDependency.maxLength : 40
groupLabel : String(60);
@LanguageDependency.maxLength : 197
template : String(255);
name : String(120);
exclude : Boolean default true;
hidden : Boolean default true;
importance : String(6) enum { HIGH; MEDIUM; LOW; };
type : String(40) enum
{
};
Usage
The focus of OData UI vocabulary developed by SAP is on usage patterns of data in UIs, not on UI patterns. The
vocabulary is completely independent of the UI technologies or devices that consume the data. The usage
patterns of data used by the OData UI vocabulary represent certain semantic views on business data. Some of
them are very generic, others are based on the concept of an entity, something tangible to end-users.
Examples for entities are semantic object instances or business object instances. Looking at different UI
patterns, these data usage patterns reoccur again and again. To generate OData annotations from CDS views,
CDS annotations are reused from different domains, for example Consumption, Communication, Semantics,
EndUserText. The CDS annotations that are additionally required in a UI domain are listed in the following table.
Scope:ENTITY
Evaluation Runtime
(Engine): SADL:
Translates CDS anno
tations into the corre
sponding OData anno
tations
Values:
Note
When users open
a SAP Fiori appli
cation, they can
see an image re
lated to the entity
type to which all
items displayed
on that page be
long to.
UI.headerInfo title.
● WITH_NAVIGATION
_PATH:Maps to
DataFieldWithNavig
ationPath.
DataFieldWithNavig
ationPath is based
on DataField, and
defines a label-
value pair that re
fers to a property of
the OData service
used. The definition
consists of a link to
navigate to a new
target, based on a
navigation property
provided by the
OData service, or
defined in the anno
tation file.
For more informa
tion, see With Navi
gation Path [page
685].
When you use this
type, you can use
the following ele
ments:
○ label
○ value
When you use this
type, you must use
the following ele
ments:
○ targetElement
● WITH_IN
TENT_BASED_NAV
IGATION;
Default: STANDARD;
Default:
WITHOUT_ICON
UI.headerInfo description
● WITH_NAVIGATION
_PATH:Maps to
DataFieldWithNavig
ationPath.
DataFieldWithNavig
ationPath is based
on DataField, and
defines a label-
value pair that re
fers to a property of
the OData service
used. The definition
consists of a link to
navigate to a new
target, based on a
navigation property
provided by the
OData service, or
defined in the anno
tation file.
For more informa
tion, see With Navi
gation Path [page
685].
When you use this
type, you can use
the following ele
ments:
○ label
○ value
When you use this
type, you must use
the following ele
ments:
○ targetElement
● WITH_IN
TENT_BASED_NAV
IGATION;
Default: STANDARD;
Default:
WITHOUT_ICON
● WITH_NAVIGATION
_PATH:Maps to
DataFieldWithNavig
ationPath.
DataFieldWithNavig
ationPath is based
on DataField, and
defines a label-
value pair that re
fers to a property of
the OData service
used. The definition
consists of a link to
navigate to a new
target, based on a
navigation property
provided by the
OData service, or
defined in the anno
tation file.
For more informa
tion, see With Navi
gation Path [page
685].
When you use this
type, you can use
the following ele
ments:
○ label
○ value
When you use this
type, you must use
the following ele
ments:
○ targetElement
● WITH_IN
TENT_BASED_NAV
IGATION;
Default: STANDARD;
Default:
WITHOUT_ICON
UI.badge title.
● WITH_NAVIGATION
_PATH:Maps to
DataFieldWithNavig
ationPath.
DataFieldWithNavig
ationPath is based
on DataField, and
defines a label-
value pair that re
fers to a property of
the OData service
used. The definition
consists of a link to
navigate to a new
target, based on a
navigation property
provided by the
OData service, or
defined in the anno
tation file.
For more informa
tion, see With Navi
gation Path [page
685].
When you use this
type, you can use
the following ele
ments:
○ label
○ value
When you use this
type, you must use
the following ele
ments:
○ targetElement
● WITH_IN
TENT_BASED_NAV
IGATION;
Default: STANDARD;
Default:
WITHOUT_ICON
● WITH_NAVIGATION
_PATH:Maps to
DataFieldWithNavig
ationPath.
DataFieldWithNavig
ationPath is based
on DataField, and
defines a label-
value pair that re
fers to a property of
the OData service
used. The definition
consists of a link to
navigate to a new
target, based on a
navigation property
provided by the
OData service, or
defined in the anno
tation file.
For more informa
tion, see With Navi
gation Path [page
685].
When you use this
type, you can use
the following ele
ments:
○ label
○ value
When you use this
type, you must use
the following ele
ments:
○ targetElement
● WITH_IN
TENT_BASED_NAV
IGATION;
Default: STANDARD;
Default:
WITHOUT_ICON
● WITH_NAVIGATION
_PATH:Maps to
DataFieldWithNavig
ationPath.
DataFieldWithNavig
ationPath is based
on DataField, and
defines a label-
value pair that re
fers to a property of
the OData service
used. The definition
consists of a link to
navigate to a new
target, based on a
navigation property
provided by the
OData service, or
defined in the anno
tation file.
For more informa
tion, see With Navi
gation Path [page
685].
When you use this
type, you can use
the following ele
ments:
○ label
○ value
When you use this
type, you must use
the following ele
ments:
○ targetElement
● WITH_IN
TENT_BASED_NAV
IGATION;
Default: STANDARD;
Default:
WITHOUT_ICON
● BULLET_CHART: A
data point is visual
ized as a bullet
chart.
● DONUT:A data
point is visualized
as a donut chart.
● PROGRESS: A data
point is visualized
as a progress indi
cator.
● RATING: A data
point is visualized
as partly or com
pletely filled sym
bols such as stars
or hearts.
● Neutral : 0
● Negative: 1
● Critical : 2
● Positive: 3
Default:
WITHOUT_ICON
For an overview of
@Semantics annota
tions, see Semantics
Annotations [page
741].
Note
If you use This an
notation, you can
not use element
UI.dataPoint.respo
nsibleName.
UI.datapoint responsibleName String (120) # not compatible with This annotation can be
UI.dataPoint.responsible
used as an alternative
to the responsible ele
Caution
ment. Only the name
If you use this anno of the responsible per
tation, you can't use son can be specified
the annotation here.
UI.dataPoint.respon
sible.
Scope: [ELEMENT]
UI.kpi id String(120)
Scope: [ELEMENT]
Evaluation Runtime
(Engine): SADL: Trans
lates CDS annotations
into the corresponding
OData annotations
Caution
Must not be used
when a structured
element is anno
tated, in this case
the annotated ele
ment is the value.
Scope: [ ENTITY]
Evaluation Runtime
(Engine): SADL: Trans
lates CDS annotations
into the corresponding
OData annotations
Values: array of
COLUMN_STACKED;
COL
UMN_STACKED_100;
COLUMN_DUAL;
COL
UMN_STACKED_DUAL;
COL
UMN_STACKED_DUAL_
100;
BAR;
BAR_STACKED;
BAR_STACKED_100;
BAR_DUAL;
BAR_STACKED_DUAL;
BAR_STACKED_DUAL_1
00;
AREA;
AREA_STACKED;
AREA_STACKED_100;
HORIZONTAL_AREA;
HORIZON
TAL_AREA_STACKED;
HORIZON
TAL_AREA_STACKED_1
00;
LINE;
LINE_DUAL;
COMBINATION;
COMBINA
TION_STACKED;
COMBINA
TION_STACKED_DUAL;
HORIZONTAL_COMBI
NATION_STACKED;
HORIZONTAL_COMBI
NA
TION_STACKED_DUAL;
PIE;
DONUT;
SCATTER;
BUBBLE;
RADAR;
HEAT_MAP;
TREE_MAP;
WATERFALL;
BULLET;
VERTICAL_BULLET;
HORIZONTAL_WATER
FALL;
HORIZONTAL_COMBI
NATION_DUAL;
DONUT_100;
UI.chart dimensionAttributes.ro String(10) enum These annotatoions de This annotation de
le termine the visualization
fines the manner in
of a chart.
which a dimension is
● CATEGORY: For ex used within a
ample: Line chart: chart.This is config-
Dimensions for ured differently for
which the role is set each chart type.
to CATEGORY,
make up the X-axis
(category axis). If
no dimension is
specified with this
role, the first di
mension is used as
the X-axis.
● SERIES: For exam
ple:
Line chart: Dimen
sions for which the
role is set to SER
IES make up the
line segments of
the chart, with dif
ferent colors as
signed to each di
mension value. If
multiple dimen
sions are assigned
to this role, the val
ues of all such di
mensions together
are considered as
one dimension and
a color is assigned.
● CATEGORY2
UI.chart measureAttributes.role String(10) enum This annotation deter This annotation de
mines the visualization
fines the manner in
of a chart.
which a measure is
● AXIS_1:
used within a chart.
Example
This is configured dif
Bubble chart: The
ferently for each chart
first measure for
type.
which the role is set
to AXIS_1, or if
none exists, the
first measure for
which the role is set
to AXIS_2, or if
none exists, the
first measure for
which the role is set
to AXIS_3, is as
signed to the feed
UID valueAxis. This
makes up the X-
axis.
● AXIS_2:
For an example, see
the description of
AXIS_1.
● AXIS_3:
For an example, see
the description of
AXIS_1.
UI.chart actions
Scope: [ENTITY]
Evaluation Runtime
(Engine): SADL: Trans
lates CDS annotations
into the corresponding
OData annotations
Values: array of
Scope: [ ENTITY]
Evaluation Runtime
(Engine): SADL: Trans
lates CDS annotations
into the corresponding
OData annotations
Values: array of
UI.presentationVariant sortOrder.direction String(4) enum ● ASC: Sort in as This annotation de
cending order. fines the sorting direc
● DESC: Sort in de tion of queried collec
scending order.
tions.
UI.lineItem
UI.chart
UI.dataPoint
Evaluation Runtime
(Engine): SADL: Trans
lates CDS annotations
into the corresponding
OData annotations
Scope: [ELEMENT]
Evaluation Runtime
(Engine): SADL: Trans
lates CDS annotations
into the corresponding
OData annotations
Scope: [ELEMENT]
Evaluation Runtime
(Engine): SADL: Trans
lates CDS annotations
into the corresponding
OData annotations
String
UI.facet purpose String(40) enum #not compatible with This annotation speci
UI.facet.parentID
fies the purpose of a
facet; only allowed if
Caution
parentId is not speci
You can only use fied
this annotation, if
parentId is not
specified.
STANDARD;
HEADER;
QUICK_VIEW;
QUICK_CREATE;
FILTER;
default: STANDARD;
- targetElement ele
ment is available
- targetQualifier ele
ment is available
● STATUSINFO_REFE
RENCE
- targetElement ele
ment is available
- targetQualifier ele
ment is available
● URL_REFERENCE
- url element is
available
Evaluation Runtime
(Engine): SADL: Trans
lates CDS annotations
into the corresponding
OData annotations
AS_CHART;
AS_CON
NECTED_FIELDS;
AS_CONTACT;
AS_DATAPOINT;
AS_FIELDGROUP;
FOR_ACTION;
FOR_IN
TENT_BASED_NAVIGA
TION;
STANDARD;
WITH_IN
TENT_BASED_NAVIGA
TION;
WITH_NAVIGA
TION_PATH;
WITH_URL;
} default #STANDARD;
label
● Neutral : 0
● Negative: 1
● Critical : 2
● Positive: 3
Default:
WITHOUT_ICON
Default: True
Default: ISOLATED
Example
Scope: [ELEMENT]
Evaluation Runtime
(Engine): SADL: Trans
lates CDS annotations
into the corresponding
OData annotations
Values: array of
AS_CHART;
AS_CON
NECTED_FIELDS;
AS_CONTACT;
AS_DATAPOINT;
AS_FIELDGROUP;
FOR_ACTION;
FOR_IN
TENT_BASED_NAVIGA
TION;
STANDARD;
WITH_IN
TENT_BASED_NAVIGA
TION;
WITH_NAVIGA
TION_PATH;
WITH_URL;
} default #STANDARD;
label
● Neutral : 0
● Negative: 1
● Critical : 2
● Positive: 3
Default:
WITHOUT_ICON
Default: True
Default: ISOLATED
Scope: [ELEMENT]
Evaluation Runtime
(Engine): SADL: Trans
lates CDS annotations
into the corresponding
OData annotations
Values: array of
AS_CHART;
AS_CON
NECTED_FIELDS;
AS_CONTACT;
AS_DATAPOINT;
AS_FIELDGROUP;
FOR_ACTION;
FOR_IN
TENT_BASED_NAVIGA
TION;
STANDARD;
WITH_IN
TENT_BASED_NAVIGA
TION;
WITH_NAVIGA
TION_PATH;
WITH_URL;
} default #STANDARD;
label
● Neutral : 0
● Negative: 1
● Critical : 2
● Positive: 3
Default:
WITHOUT_ICON
Default: True
Default: ISOLATED
Scope: [ELEMENT]
Evaluation Runtime
(Engine): SADL: Trans
lates CDS annotations
into the corresponding
OData annotations
Values: array of
AS_CHART;
AS_CON
NECTED_FIELDS;
AS_CONTACT;
AS_DATAPOINT;
AS_FIELDGROUP;
FOR_ACTION;
FOR_IN
TENT_BASED_NAVIGA
TION;
STANDARD;
WITH_IN
TENT_BASED_NAVIGA
TION;
WITH_NAVIGA
TION_PATH;
WITH_URL;
} default #STANDARD;
label
● Neutral : 0
● Negative: 1
● Critical : 2
● Positive: 3
Default:
WITHOUT_ICON
Default: True
Default: ISOLATED
UI.connectedFields
AS_CHART;
AS_CON
NECTED_FIELDS;
AS_CONTACT;
AS_DATAPOINT;
AS_FIELDGROUP;
FOR_ACTION;
FOR_IN
TENT_BASED_NAVIGA
TION;
STANDARD;
WITH_IN
TENT_BASED_NAVIGA
TION;
WITH_NAVIGA
TION_PATH;
WITH_URL;
} default #STANDARD;
label
● Neutral : 0
● Negative: 1
● Critical : 2
● Positive: 3
Default:
WITHOUT_ICON
Default: True
Default: ISOLATED
Example
The ABAP RESTful Programming Model provides components and interfaces to implement the REST contract.
This allows the ABAP developer to implement specific use cases that are not executed automatically.
For the implementation type unmanaged, application developers must implement essential components of the
REST contract themselves. For this, all desired operations (create, update, delete, or any application-specific
actions) must be specified in the corresponding behavior definition artifact by using the Behavior Definition
Language (BDL) [page 945] before they are implemented with ABAP.
This implementation is carried out in a special type of class pool, the behavior pool, which refers to the
behavior definition. The global class is defined with the following syntax:
The concrete implementation of the business logic is based on the ABAP language and the Business Object
Behavior API.
The implementation tasks are roughly divided into an interaction phase and a save sequence. The interaction
phase is represented by the local handler class and the save sequence by the local saver class.
Remember
There are some specific rules for assigning names to local classes in a behavior pool. Both handler classes
and saver classes are recognized by derivation from the respective system base class. The names
LCL_HANDLER and LCL_SAVER are suggested by ADT when you create the class pool, but can be changed.
We recommend applying naming conventions for behavior pools and local handler and saver classes
corresponding to Naming Conventions for Development Objects [page 918].
At the top of the class hierarchy for the BO API is the class CL_ABAP_BEHV. This class is the foundation class
for the handler and the saver class. It defines some fundamental data types to be used in the behavior
processing (such as field names in derived type structures) and also provides message creation methods.
Detailed Information
To implement the behavior specified in the behavior definition, a special global ABAP class, the behavior pool is
used. This global class is implicitly defined as ABSTRACT and FINAL. So, the behavior implementation cannot
be found from outside the BO. A behavior pool can have static methods, CLASS-DATA, CONSTANTS and TYPES.
The application may place common or even public aspects of its implementation here.
The real substance of a behavior pool is located in Local Types. Here you can define two types of special local
classes: handler classes for the operations within the interaction phase and saver classes for the operations
Within the global behavior pool one or multiple local handler classes are defined. Each such local class inherits
from the base class CL_ABAP_BEHAVIOR_HANDLER. The signature of the handler methods FOR MODIFY, FOR
FEATURES, FOR LOCK, and FOR READ are typed based on the entity that is defined by the keyword FOR
[OPERATION] entity. If there is an alias defined in the behavior definition, the alias has to be used.
Method Summary
Method Description
<method> FOR MODIFY Handles all changing operations (create, update, delete, and specific actions as they are
specified in the behavior definition) of an entity
<method> FOR LOCK Implements the locking of entities corresponding to the lock properties in the behavior
definition
Method Details
The FOR MODIFY method implements the standard operations create, update, delete, and application-specific
actions, as they are specified in the behavior definition.
Tip
The FOR MODIFY method can handle multiple entities (root, item, sub item) and multiple operations during
one processing step. In some cases, it might be useful to split the handler implementation into separate
methods. Then, multiple behavior handlers, that is, multiple local behavior classes within one global
behavior pool or even in multiple global behavior pools, can be defined.
The declaration of the <method> FOR MODIFY expresses what changing operations this method is
responsible for. In extreme cases, this is the total number of all changing operations that are possible according
to the behavior definition.
Each individual specification within the declaration of modify_method FOR MODIFY consists of a
combination of an operation with an entity or an entity part. To refer to the entities, the alias given in behavior
definition is used - if there is any.
Each operation type has an import parameter <operation>_import_parameter for the incoming instance
data and. Its name is freely selectable. The method includes an export parameter
action_export_parameter if the operation type expects one. Action, for example, can have export
parameters for their results.
You can also declare a <method> FOR MODIFY for each operation. In many cases, it can be beneficial to
implement the individual MODIFY operations in separate methods. This may be particularly the case if the
implementations for the respective operations are more extensive.
METHODS:
create_entity_method FOR MODIFY
[IMPORTING] create_import_parameter FOR CREATE entity,
update_entity_method FOR MODIFY
[IMPORTING] update_import_parameter FOR UPDATE entity,
delete_entity_method FOR MODIFY
[IMPORTING] delete_import_parameter FOR DELETE entity,
action_method FOR MODIFY
[IMPORTING] action_import_parameter FOR ACTION entity~action_name
RESULT action_export_parameter,
create_by_association FOR MODIFY
[IMPORTING] create_ba_import_parameter FOR CREATE entity\_association.
For the sake of better readability, the keyword IMPORTING can be specified before the first import parameter.
The parameters can also be explicitly declared as REFERENCE(...); However, the declaration as VALUE(...)
is not allowed and therefore the importing parameters cannot be changed in the method.
Note
The data types with which the parameters are implicitly provided by the ABAP compiler are derived types
resulting from the behavior definition. They usually contain at least the instance key according to the CDS
definition, or even the full row type, as well as other components that result from the model (action
parameter) or other features of the BO, such as %pid in case of late numbering. For more information, see
Declaration of Derived Data Types [page 871].
The method FOR MODIFY has three implicit changing parameters failed, mapped, and reported. These
parameters can (but do not need to) be explicitly declared (developers may find this explicit declaration
helpful), like this:
Each of FAILED, MAPPED, REPORTED is a structure type with one component per entity from the behavior
definition (that is, per entity in a business object). The names of the components are the aliases defined in the
behavior definition or else the original entity names.
All parameters and components of these structures are tables to allow mass processing. Together with the
bundling of multiple operations in a method, it is possible to implement large modification requests in a single
FOR MODIFY method call.
The FOR MODIFY method can determine which operations are specifically given, for example, in this way:
Example
There are no explicit return parameters to be filled for all other operations. However, the three returning
structures failed, reported, and mapped must be filled when the corresponding events happen. Their
construction results in a fairly readable pattern, for example, to report failed instances or to store messages for
instances:
All derived types also contain components that do not originate from the line type of the entity and begin with
the character % to avoid naming conflicts with original components. For example, the row type of a failed
table contains a component %fail to store the symptom for a failed instance; Also, an include structure %key
that summarizes the primary key fields of the entity. %key is part of almost all derived types, including
operation parameters. An overview of all derived types is given in
Related Information
Implements the lock for entities in accordance with the lock properties specified in the behavior definition.
The FOR LOCK method is automatically called by the orchestration framework [page 958] framework before a
changing (MODIFY) operation such as update is called.
In the behavior definition, you can determine which entities support direct locking by defining them as lock
master.
Note
The definition of lock master is currently only supported for root nodes of business objects.
In addition, you can define entities as lock dependent. This status can be assigned to entities that depend
on the locking status of a parent or root entity. The specification of lock dependent contains the association
by which the runtime automatically determines the corresponding lock master whose method FOR LOCK is
executed when change requests for the dependent entities occur.
The declaration of the predefined LOCK method in the behavior definition is the following:
The keyword IMPORTING can be specified before the import parameter. The name of the import parameter
lock_import_parameter can be freely selected.
The placeholder entity refers to the name of the entity (such as a CDS view) or to the alias defined in the
behavior definition.
Import Parameters
The row type of the import table provides the following data:
● ID fields
Note
The compiler-generated structures %CID, %CID_REF, and %PID are not relevant in the context of locking
since locking only affects persisted (non-transient) instances.
Changing Parameters
The LOCK method also provides the implicit CHANGING parameters failed and reported.
● The failed parameter is used to log the causes when a lock fails.
● The reported parameter is used to store messages about the fail cause.
You have the option of explicitly declaring these parameters in the LOCK method as follows:
The RAP lock mechanism requires the instantiation of a lock object. A lock object is an ABAP dictionary object,
with which you can enqueue and dequeue locking request. For tooling information about lock objects, see .
The enqueue method of the lock object writes an entry in the global lock tables and locks the required entity
instances.
An example on how to implement the method FOR LOCK is given in Implementing the LOCK Operation [page
366].
Related Information
The FOR READ method is used to return the data from the application buffer. If the buffer is empty, the data is
read from the database (which typically populates the application buffer).
There are two options to read data from the application buffer:
Similar to <method> FOR MODIFY, the handler <method> FOR READ is also implemented to handle mass
requests. It is also designed to bundle multiple operations.
Again, for the sake of better readability, the keyword IMPORTING can be specified before the import parameter.
The name of the import parameter read_import_parameter can be freely selected. It imports the key(s) of
the instance entity to be read and indicates which elements are requested.
The placeholder entity refers to the name of the entity (such as a CDS view) that you want to read from or to
the alias defined in the behavior definition.
The parameter RESULT is a changing parameter. Its name can be freely selected.
Import Parameters
The row type of the import table read_import_parameter provides the following:
● ID fields
All elements that are specified as a key in the related CDS view.
● %CONTROL
The control structure reflects which elements are requested by the consumer.
Exporting Parameters
● read_result_parameter
Returns the successfully read data.
The row type of this table provides all elements that are specified in the element list of the entity that is
read. Only the requested elements, which are indicated in the %control structure, must be filled.
Changing Parameters
In addition to the explicitly declared return parameter, the READ method also provides the implicit CHANGING
parameters failed, mapped and reported.
● The failed parameter is used to log the entries that could not be read. You can specify the fail cause for
the READ, for example not_found.
● The mapped parameter must not be filled in READ implementations.
● The reported parameter must not be filled in READ implementations.
For more information about the implicitly declared parameters, see Implicit Returning Parameters [page 875].
The syntax for the READ by association is similar to the one for the direct READ.
METHODS
method_name FOR READ
[IMPORTING] read_ba_import_parameter FOR READ entity\_association
FULL full_read_import_parameter
RESULT read_result_parameter
LINK read_link_parameter.
As for the other operation implementations, the keyword IMPORTING can optionally be specified explicitly. All
parameter names can be freely selected.
The importing parameter read_ba_import_parameter imports the key(s) of the entity instance whose
associated entity instance shall be read. In addition, it indicates which elements from the associated entity
shall be read.
The placeholder entity refers to the name of the entity (such as a CDS view) or to the alias defined in the
behavior definition, which is the source of the association.
The placeholder association refers to the association along which you want to read data, for example
_booking if you want to read all bookings associated to one travel instance.
The parameter full_read_import_parameter indicates whether the RESULT parameter must be filled or if
only the LINK parameter must be filled. It has a boolean value.
The parameter RESULT is an exporting parameter. It returns the requested elements that are indicated in the
importing parameter if the FULL parameter is set.
The parameter LINK is also an exporting parameter. It returns the key elements of the source and target
entities no matter if the FULL parameter is set.
Import Parameters
● The row type of the import table read_ba_import_parameter provides the following data:
○ ID fields
All elements that are specified as a key in the related CDS view.
○ %CONTROL
The control structure reflects which elements of the associated entity are requested by the consumer.
● The type of the import parameter full_read_import_parameter is a character with boolean value.
Exporting Parameters
● read_ba_import_parameter: Returns the successfully read data from the associated entity if the FULL
parameter is set.
The row type of this table provides all elements that are specified in the element list of the entity that is
read. Only the requested elements, which are indicated in the %control structure, must be filled.
● read_link_parameter: Returns the source and target key of the successfully read entity instances.
For more information about the implicitly declared parameters, see Implicit Returning Parameters [page 875].
Changing Parameters
In addition to the explicitly declared parameters, the method for READ by association also provides the implicit
CHANGING parameters failed, mapped, and reported.
For more information about the implicitly declared parameters, see Implicit Returning Parameters [page 875].
Dynamic feature control can be used for the standard operations update, delete, and
create_by_association, for actions and on field level. Depending on the feature conditions is the operation
executable or not. The <method> FOR FEATURES is called by the orchestration framework for every operation
or field that is dynamically controlled.
The operations or fields that are dynamically controlled are defined in the behavior definition with features:
instance.
The dynamic feature control for an entity is implemented in a handler class using the method
feature_ctrl_method. The signature of this handler method is defined by the keyword FOR FEATURES,
followed by the input parameters keys and the requested_features of the entity.
Again, for the sake of better readability, the keyword IMPORTING can be specified before the import parameter.
Note
The name of the <method> FOR FEATURES can be freely chosen. Often get_features is used as
method name.
Import Parameters
● keys
The table type of keys includes all elements that are specified as a key for the related entity.
● requested_features
Export Parameters
The export parameter result is used to return the feature control values. It includes, besides the key fields, all
the fields of the entity, standard operations and actions for which the features control was defined in the
behavior definition.
Export Parameters
In addition to the explicitly declared export parameter, the FOR FEATURE method also provides the implicit
CHANGING parameters failed and reported.
Related Information
Save Sequence
The save sequence is called for each business object after at least one successful modification was performed
using the BO behavior APIs in the current LUW.
As depicted in the figure below, the save sequence starts with finalize( ) performing the final calculations
before data can be persisted. If the subsequent check_before_save( ) call is positive for all transactional
changes, the point-of-no-return is reached. From now on, a successful save() is guaranteed by all involved
BOs. After the point-of-no-return, the adjust_numbers( ) call can occur to take care of late numbering
[page 955]. The save( ) call persists all BO instance data from the transactional buffer in the database.
All transactional methods are implemented in the local saver class that is a part of a global behavior pool. Each
local saver class of this type inherits from the base class CL_ABAP_BEHAVIOR_SAVER. This superclass
provides the transactional methods that need to be redefined in the local saver class.
Method Description
FINALIZE Finalizes data changes before they can be persisted on the database.
SAVE Saves the data from the transactional buffer to the database.
Method Details
You can use this method to perform final calculations with determinations before data is persisted on the
database with the save( ) call.
Example
Let us assume that a SalesOrder triggers the calculation of the Pricing, which is quite complex and time
consuming to be called for each modification during the consumer-BO interaction (to be precise, pricing is
called by default for each modify( ), but the customer can configure it to be executed only in finalize( )
to optimize the performance).
Changing Parameters
● MAPPED
● FAILED
The parameter FAILED is filled to log the entities for which finalize went wrong.
● REPORTED
To enable a successful save( ), the BO runtime must provide feedback in check_before_save( ) based on
all transactional changes. This is done by validations that are called within the check_before_save method.
If the check_before_save( ) of all involved BOs returns positive feedback, the point-of-no-return is
reached. From now on, a successful save( ) is guaranteed for all involved BOs and the data is persisted.
If, on the other hand, errors are reported in the changing parameter FAILED, the save chain is canceled.
Changing Parameters
● MAPPED
● FAILED
The parameter FAILED is filled to log the entities for which there is no positive feedback.
● REPORTED
You can fill the parameter REPORTED to return messages in case of failure.
The implementation of adjust_numbers( ) is only required if late numbering is modeled in the behavior
definition.
Late numbering is a common concept for drawing gap-free numbers. In some cases, it can be business critical
that identifier numbers are gap-free, for example invoice numbers. The third phase of the save sequence is
implemented in adjust_numbers( ). The output is a link table which maps %PIDs to the related drawn
numbers. These final IDs are provided by means of the MAPPED exporting parameter so that temporary
numbers can be exchanged. The implementation of this method assigns the final keys for the remaining
content IDs.
Changing Parameter
● MAPPED
● REPORTED
Messages can be reported via the implicit returning parameter REPORTED. As consumer errors must not
appear after CHECK_BEFORE_SAVE, REPORTED should only contain success or information messages,
such as Material stock is low.
● The method must not fail and thus does not return any failed keys since the exchange of temporary IDs
takes place after the point-of-no-return. If the application needs to stop the transaction, it can only
produce a short dump.
The actual save( ) implementation gets access to the link table of the content IDs (%CID) and their numbers.
Often these numbers are used as foreign keys, so that they need to be replaced before the data is persisted.
After the data is persisted, it is expected that the transactional buffer is cleared, since the same ABAP session
might be used for more than one LUW [page 956] and any remaining changes in the transactional buffer could
lead to inconsistencies. Of course, the persisted transactional changes can be transferred to a read cache, once
they are successfully saved.
Changing Parameter
● REPORTED
Messages can be reported via the implicit returning parameter REPORTED. As consumer errors must not
appear after CHECK_BEFORE_SAVE, REPORTED should only contain success or information messages,
such as Booking has been saved.
Note
● The method must not fail and thus does not return any failed keys since the exchange of temporary IDs
takes place after the point-of-no-return. If the application needs to stop the transaction, it can only
produce a short dump.
For the type-safe parameterization of the BO provider code, the ABAP compiler derives data types from the
involved CDS views and the behavior definition. These are called derived types because they are implicitly
derived by the compiler from CDS entity types and their behavior definition. Derived types usually contain at
When implementing a BO provider, you can use specific derived types in method signatures in the context of
the behavior implementation. This means you have the option of creating both local and global derived data
types by using a new syntax for declaring import or export parameters.
Each individual type declaration consists of a combination of an operation with an entity or an entity part, such
as an action. To refer to the entities, the alias given in the behavior definition should be used (if one exits).
Note that the syntax of an action import parameter definition differs a little from the type definition of those of
the standard operation-related parameters.
● CREATE
● UPDATE
● DELETE
● LOCK
● READ IMPORT
The name of the import parameter type type_for_import_parameter can be freely selected, for example
it_create_travel or it_read_travel_id.
The placeholder entity_name refers to the name of the entity (such as a CDS view) as it is defined in the
behavior definition.
Similarly, local and global types can be defined for export parameters:
● MAPPED [LATE] - The mapped result parameters provide the consumer with ID mapping information. By
default, the mapping information is already available in the interaction phase (early mapped). The CID is
then mapped to the real key or to the PID. Using the addition LATE, you specify that the mapping
information is only available in the save sequence. This plays a role when providing the late numbering (see
also: Method ADJUST_NUMBERS [page 870]) where the PID is mapped to the real key.
● FAILED [LATE] - The failed parameters include information for identifying the data set where an error
occurred. (Early) FAILED is provided during the interaction phase and contains the CID or the KEY to
Similarly, local and global types can be defined for export parameters:
● MAPPED [LATE] - The mapped result parameters provide the consumer with ID mapping information. By
default, the mapping information is already available in the interaction phase (early mapped). The CID is
then mapped to the real key or to the PID. Using the addition LATE, you specify that the mapping
information is only available in the save sequence. This plays a role when providing the late numbering (see
also: Method ADJUST_NUMBERS [page 870]) where the PID is mapped to the real key.
● FAILED [LATE] - The failed parameters include information for identifying the data set where an error
occurred. (Early) FAILED is provided during the interaction phase and contains the CID or the KEY to
indicate instances for which an operation failed. FAILED with the additional specification LATE is only
provided during the save sequence and contains the PID or the KEY, but not the CID.
● REPORTED [LATE] - The reported parameters are used to return messages in case of failure. (Early)
REPORTED is provided during the interaction phase and contains the CID or the KEY to indicate instances
for which an operation failed. REPORTED with the additional specification LATE is only provided during the
save sequence and contains the PID or the KEY, but not the CID.
● READ RESULT
entity_name refers to the name of the entity (such as a CDS view) or to the alias as it is defined in the
behavior definition.
Examples
TYPES:
et_travel_mapped TYPE TABLE FOR MAPPED travel_root,
et_travel_mapped_late TYPE TABLE FOR MAPPED LATE travel_root,
et_booking_failed TYPE TABLE FOR FAILED booking_item,
et_booking_reported_late TYPE TABLE FOR REPORTED LATE booking_item,
et_booking_read_out TYPE TABLE FOR READ RESULT booking_item,
et_travel_set_booked_out TYPE TABLE FOR ACTION RESULT travel_root~set_booked.
In addition to derived parameter types, you can also define specific derived data types for the IDs and data
fields:
Examples
TYPES:
it_booking_update TYPE TABLE FOR UPDATE booking_item,
ltype_for_update TYPE LINE OF it_booking_update,
ltype_for_key TYPE ltyp_for_update-%key,
ltype_for_data TYPE ltyp_for_update-%data.
For modularization, it may be necessary to declare variables with derived types, even outside the reserved
handler methods. For this purpose, there is an explicit syntax that is supported in the statements TYPES, DATA,
and CREATE DATA. The syntax always has the form ... TYPE TABLE FOR....
In this case, only the entity_name, but not an alias, can be used to refer to the entity. This is because, unlike in
the handler methods, no reference to a particular behavior definition is given.
Examples
When implementing a BO contract, you make use of implicit returning parameters. These parameters do not
have fixed data types and instead are assigned by the compiler with the types derived from behavior definition.
The implicit parameters can be declared explicitly as CHANGING parameters in the method signature of the
handler classes by using the generic type DATA:
The ABAP compiler replaces the type DATA with the respective derived types resulting from the concrete
behavior definition.
Implicit Parameters
Parameter Description
FAILED This exporting parameter is defined as a nested table which contains one table for each entity de
fined in the behavior definition.
The failed tables include information for identifying the data set where an error occurred:
● %CID and
● ID of the relevant BO instance.
REPORTED This exporting parameter is used to return messages. It is defined as a nested table which con
tains one table for each entity defined in the behavior definition.
The data set for which the message is relevant is identified by the following components:
● %CID
● ID of the relevant instance
● %MSG with an instance of the message interface IF_ABAP_BEHV_MESSAGE
● %ELEMENT which refers to all elements of an entity.
Note
Messages that are not related to a specific (entity) instance can be returned using the
%OTHERS component.
MAPPED This mapped parameter is defined as a nested table which contains one table for each entity de
fined in the behavior definition.
The mapped parameters provide the consumer with ID mapping information. They include the in
formation about which key values were created by the application for given content IDs. The BO
runtime passes the created key values in any subsequent calls in the same request and in the re
sponse.
● %CID
● %KEY
All derived data types in the context of the ABAP RESTful programming model also contain components that
do not originate from the row type of the entity and begin with the character % to avoid naming conflicts with
original components. For example, the row type of a failed table contains a component %fail to store the
The following list provides you with a description of the most common %... components:
Component Description
%CID The content ID %CID is a temporary primary key for an instance, as long as no primary key was
created by the BO runtime.
The content ID is always provided by the SADL [page 960] framework. It is only needed in case of
internal numbering and/or late numbering. The content ID provides the reference between the re
lated entity instances. A good example is a DEEP INSERT for multiple parent/child instances with
internal numbering and/or late numbering. In this case, the references between the child and pa
rent instances are established using the content ID %CID.
%tky Contains all key elements of an entity (CDS view) including the derived key components, for exam
ple %IS_DRAFT in draft scenarios.
%tky is part of almost all derived types, including trigger parameters in the for modify( )
method.
%PID Defines the preliminary ID, before the final key is set in the ADJUST_NUMBERS method..
The preliminary ID is only available when LATE NUMBERING is defined in the behavior definition
without the addition IN PLACE.
The fields of the %CONTROL structure provide information, depending on the operation, about
which elements of the entity are supplied in the request (for CREATE and UPDATE operations) or
which elements are requested in the read request (for READ operations).
For each entity element, this control structure contains a flag which indicates whether the corre
sponding field was provided/requested by the consumer or not.
The element names of the entity have the uniform type ABP_BEHV_FLAG.
Note
The possible constants are defined in the basis handler interface if_abap_behv=>mk-
<...>. For example, the elements that have the value if_abap_behv=>mk-on in the
%CONTROL structure are used to handle delta updates within the UPDATE operation.
%FAIL Stores the symptom for a failed data set (BO instance).
Note
The possible values (unspecific, unauthorized, not_found, and so on) are defined
by the ENUM type IF_ABAP_BEHV=> T_FAIL_CAUSE.
Tip
The component %MSG of type REF TO IF_ABAP_BEHV_MESSAGE includes
IF_T100_DYN_MSG. If you do not need your own implementation of this interface, then you
can benefit from the provided standard implementation by using the inherited methods
new_message( ) or new_message_with_text( ).
In contrast to managed queries, in which a framework assumes the implementation tasks to select the
requested data from the data source, the implementation of the unmanaged query must be done by the
application developer. For this, all desired query capabilities (paging, filtering, sorting, counting, … ) must be
implemented in a query implementation class, which is referenced in a CDS custom entity.
Interfaces
For more conceptual information about the unmanaged query, see Query Runtime Implementation [page 49].
For an example on how to implement an unmanaged query, see Implementing an Unmanaged Query [page
608].
For an example on how to implement the unmanaged query contract in a development scenario, see
Implementing the Query for Service Consumption [page 509].
Note
Before SAP Cloud Platform ABAP Environment Release 1908, IF_A4C_RAP_QUERY_PROVIDER and the
related interfaces was the API to implement an unmanaged query. This API is deprecated as of 1908, but
still available in ABAP Environment. However, it is recommended to use only the new interface
IF_RAP_QUERY_PROVIDER.
The interfaces differ in some aspects, for example the handling of filter requests. The new interface offers
some more methods to reflect the query requests in more detail, for example get_aggregation or
get_parameters, which facilitates the implementation.
This interface defines a method that is used for requesting and responding to OData query requests in an
unmanaged query.
Method select
The method select must be implemented in custom entity scenarios. It replaces the SQL-SELECT of a CDS
view to retrieve and return data. The select method must be called by the query implementation class, which
is referenced in the custom entity annotation @ObjectModel.query.implementedBy.
Before SAP Cloud Platform ABAP Environment 1908 the annotation @QueryImplementedBy was in use,
which is deprecated as of 1908.
The select imports an interface instance for the request data and one for the response data:
Signature
Parameter
IO_REQUEST Interface instance for gathering request information that are used as input for the
select implementation. The request interface provides methods for implementing
query options, like filtering or sorting.
IO_RESPONSE Interface instance for the result output of the select implementation.
Exception
CX_RAP_QUERY_PROVIDER Exception that can be raised if there is an error during the query execution.
Example
The interface defines methods to parametrize a query request in an unmanaged query. It is used to handle
OData query options for data retrieval.
Method get_entity_id
This method returns the CDS entity name of the requested entity set of an OData request in an unmanaged
query.
With this method, you can ensure that the query implementation is only executed if the correct entity for this
query implementation set is called.
Example
Method is_data_requested
Note
If this method is used to indicate the request for data, the method set_data [page 891] must be called.
Signature
Parameter
rv_is_requested If data needs to be returned, the value is abap_true. If no data needs to re
turned, the value is abap_false.
Example
See Requesting and Setting Data or Count in an Unmanaged Query [page 614].
Method is_total_numb_of_rec_requested
This method returns a boolean value to indicate if the total number of records is requested. The total number
of records is requested by the query option $inlinecount or a $count request.
Note
If this method indicates the request for the total number of records, the total count needs to be returned by
the method set_total_number_of_records [page 891].
Signature
rv_is_requested If the total number of records needs to be returned the value is abap_true. If
the total number of records is not requested the value is abap_false.
Example
See Requesting and Setting Data or Count in an Unmanaged Query [page 614].
Method get_filter
This method returns a filter object. This filter object is an interface instance of IF_RAP_QUERY_FILTER. If a
filter is requested,its methods return the filter information. Only records that match this filter condition must
be returned or counted.
Signature
Parameter
Example
Method get_paging
This method returns an object with paging information. The paging object is an interface instance of
IF_RAP_QUERY_PAGING. It limits the number of records to be returned as response data with offset and page
size.
Signature
Parameter
Method get_sort_elements
This method returns the sort order for the sort elements.
Signature
Parameter
rt_sort_elements Contains the elements to be sorted with their sort direction. It is an ordered list to
define the ranking order, the first element being the primary sort criteria. The ta
ble indicates the names of the sort element and the sort order with a boolean
value in the column descending. The following table illustrates how the return
ing value looks like.
tt_sort_elements
ELEMENT_NAME DESCENDING
string abap_bool
Example
<service_root_url>/<entity_set>?$orderby=Customer_ID desc
the method get_sort_elements returns the following entries in the returning table:
rt_sort_elements
ELEMENT_NAME DESCENDING
CUSTOMER_ID X
Example
This method returns a list of the entity parameters and their values.
Signature
Parameter
tt_parameters
PARAMETER_NAME VALUE
string string
Example
<service_root_url>/
<entity_set>(p_start_date=datetime'2016-07-08T12:34',p_end_date=datetime'2019-
07-08T12:34')/Set
rt_parameters
PARAMETER_NAME VALUE
P_START_DATE 20160708
P_END_DATE 20190708
Example
Method get_aggregation
Signature
Parameter
Example
Method get_search_expression
Signature
Parameter
Example
Method get_requested_elements
This method returns the requested elements, which need to be given to the response.
Signature
Example
This interface is a filter criteria provider for the unmanaged query. The methods provide different
representations for the filter criteria.
Method get_as_ranges
This method returns the filter as a list of simultaneously applicable range tables. The table is initial if no filter is
supplied.
Signature
Parameter
rt_ranges Contains a list of filter conditions in name-range-table pairs. That means, every
requested filter element is related to a ranges table that indicates the filter condi
tions. The returning value is in a ranges-table-compatible format. The following
table illustrates the list of name and ranges table.
The columns of the ranges tables have the semantics of selection table criteria.
They are defined as follows:
tt_name_range_pairs
NAME RANGE
string tt_range_option
OP
SIGN TION LOW HIGH
Example
tt_name_range_pairs
NAME RANGE
AGENCY_ID tt_range_option
I EQ 070031
Begin_Date tt_range_option
I BT 20190101 20191231
Exception
cx_rap_query_filter_no_ran This exception is thrown if the filter cannot be converted into a ranges table.
ge
In this case the developer can try to use the method get_as_sql_string as
a fall back or throw an error.
Example
Method get_as_sql_string
This method returns the filter as an SQL string. The string is initial if no filter is supplied.
Signature
Parameter
rv_string Contains the filter conditions as an SQL string. The variable can be used directly
in the WHERE clause of an SQL statement to select data.
Example
This interface provides the information for paging requests. The methods provide the offset and the page size
for one OData request.
Method get_offset
This method indicates the number of records to drop from the list of data records in the data source. In an
OData query request, the offset is requested by the query option $skip.
Signature
Parameter
rv_offset Contains the number of records that are dropped from the result list.
Example
If rv_offset is 2, the first record in the result list is the data record on po
sition 3.
Example
Method get_page_size
This method indicates the maximum number of records that are to be returned. In an OData query request, the
page size is requested by the query option $top.
Signature
Note
rv_page_size if_rap_query_pagin=>page_size_unlimited
if no limit is requested.
Example
This interface provides methods to receive information about the requested aggregation and grouping
requests.
Method get_aggregated_elements
This method returns the requested aggregated elements with their aggregation method and the output
elements in a string table. These values can then be extracted and used in the query implementation.
Signature
rt_aggregated_elements Contains the aggregation method, the input element, and the output element.
● COUNT: for returning the number of values of the input element in the out
put element.
The constant co_count_all_identifier as value for
input_element denotes the counting of all rows.
● COUNT_DISTINCT: for returning the number of unique values of the input el
ement in the output element.
● SUM: for returning the sum of the input element in the output element.
● MIN: for returning the minimum of the input element in the output element.
● MAX: for returning the maximum of the input element in the output element.
● AVG: for returning the average of the input element in the output element.
The input element is the element whose values are aggregated and the output el
ement is the element, which contains the aggregated value. The output element
can be the same as the input element.
Example
rt_aggregated_elements
aggregation_method input_element result_element
Signature
Example
Method get_grouped_elements
This method returns the requested elements by which the result is to be grouped.
Signature
Parameter
rt_grouped_elements Returns an ordered list of the elements by which the result is to be grouped. The
elements are listed in the order of grouping priority.
This interface provides methods to return data and the count for the query response. The results of the
methods of interface IF_RAP_QUERY_REQUEST are integrated in the response.
Method set_data
This method provides the response for the method if_rap_query_request~is_data_requested. If this
method is called, the table of result data must be provided (empty if there is no result data).
Signature
Parameter
it_data Contains a table of the data records for the query response.
Use the type of your custom entity for the response to be compatible with the re
quest.
Exception
cx_rap_query_response_set_ Exception is raised when the result table is set more than once.
twic
Example
See Requesting and Setting Data or Count in an Unmanaged Query [page 614].
Method set_total_number_of_records
Parameter
iv_total_number_of_records Contains the total number of records. If no records match the given request crite
ria, the value zero must be passed.
Exception
cx_rap_query_response_set_ Exception is raised when the number of records is set more than once.
twic
Example
See Requesting and Setting Data or Count in an Unmanaged Query [page 614].
Virtual elements are defined in CDS projection views and implemented in related ABAP classes that calculate
their values and transform filtering and sorting requests. For this calculation and transformation, the generic
orchestration framework calls the ABAP classes that implement the methods of the virtual elements API.
For more information about virtual elements, see Using Virtual Elements in CDS Projection Views [page 573].
Interface that must be implemented by calculation classes for virtual elements that are referenced at the level
of the CDS projection view element with the annotation@ObjectModel.virtualElementCalculatedBy.
This interface defines the following methods for implementing the field calculation:
Method get_calculation_info
This method provides a list of all elements that are required for calculating the values of the virtual elements in
the requested entity.
This method is called during runtime before the retrieval of data from the database to ensure that all necessary
elements for calculation are filled with data.
TYPES tt_elements TYPE SORTED TABLE OF string WITH UNIQUE DEFAULT KEY .
METHODS get_calculation_info IMPORTING !it_requested_calc_elements TYPE
tt_elements
!iv_entity TYPE string
EXPORTING !et_requested_orig_elements TYPE
tt_elements
RAISING cx_sadl_exit.
Parameter
importing it_requested_calc_elements List of the virtual elements that are requested by the client
and intended for calculation.
iv_entity Name of the CDS entity that contains the requested virtual
elements in it_requested_calc_elements.
exporting et_requested_orig_elements List of the CDS elements that are needed for the calculation.
Note
Only elements of the requested entity that are needed
for the calculation of the virtual elements. iv_entity
can be appended for data retrieval.
raising cx_sadl_exit Abstract exception class that can be used to raise excep
tions and return messages related to the processing of the
virtual element calculation.
Example
Method calculate
This method is called during runtime after data is retrieved from the database. The elements needed for the
calculation of the virtual elements are already inside the data table passed to this method. The method returns
a table that contains the values of the requested virtual elements.
Signature
TYPES tt_elements TYPE SORTED TABLE OF string WITH UNIQUE DEFAULT KEY .
METHODS calculate IMPORTING !it_original_data TYPE STANDARD TABLE
!it_requested_calc_elements TYPE tt_elements
CHANGING !ct_calculated_data TYPE STANDARD TABLE
importing it_original_data Result table of the retrieved values for the elements re
quested by the client, in addition to the elements that are
needed for calculation, which are requested for data retrieval
by the method get_calculation_info.
it_requested_calc_elements List of virtual elements that are requested by the client and
are intended for calculation.
exporting ct_calculated_data Table of the virtual element for each entity instance corre
sponding to the table it_original_data by index.
raising cx_sadl_exit Abstract exception class that can be used to raise excep
tions and return messages related to the processing of vir
tual element calculation.
With this method you ensure that the virtual element is calculated and that its values are passed to the runtime
engine to process the data in the requested entity.
Example
The following chapter provides information about specific tools and features that are enabled for the ABAP
RESTful programming model in ABAP Development Tools (ADT):
In accordance with the relevant ABAP development scenario, you can find further information about ABAP
development using ADT features in the following development user guides:
●
●
●
Context
You have a business object containing several nodes that are represented as CDS entities and stored in DDL
sources. The corresponding behavior is distributed among the behavior definition and several behavior
implementation classes.
Now, you want to understand the entire structure of this business object at a glance and explore the operations
of each business object node.
Procedure
1. In the Editor, open the root entity or any other object that is related to the business object such as behavior
definition or behavior implementation class.
2. From the context menu, select Show in Relation Explorer . Alternatively, use Alt + Shift + W .
The Relation Explorer displays a composition tree of the business object in the Business Object context. In
the details section, you can view the operations of the selected entity. On each entity in the object's
composition tree and on each operation in the details section, you can trigger the context menu.
If you want to see both contexts at once, instead of switching between them, . For example, if you have the
object opened in the Business Object context, you can create a new instance of the Relation Explorer to
display the CDS-specific related objects for the same business object in the Core Data Services context.
Based on the existing CDS data model, you can create and edit behavior definitions to define the behavior of
business objects in the ABAP RESTful programming model. To implement the behavior, create a behavior
implementation class.
To define the behavior of business objects in the ABAP RESTful programming model, create a behavior
definition as the corresponding ABAP repository object. You create a behavior definition as follows:
1. In the Project Explorer, select the relevant node for the data definition that contains the CDS root entity for
which you want to create a behavior definition.
2. Open the context menu and select New Behavior Definition to launch the creation wizard.
3. The Project and Package are inserted automatically. You can change them if needed.
Note
The Name of the behavior definition is the same as the name of its root entity. It is automatically
inserted and cannot be modified.
Note
If you triggered the New Behavior Definition wizard not from the referenced CDS view, the Root Entity is
not filled in automatically. In this case, select the Root Entity manually by using the Browse button.
6. From the drop-down list of the Implementation Type field, select Managed or Unmanaged implementation
type.
Note
If you create a Behavior Definition from a projection root entity, the implementation type Projection is
inserted automatically.
7. Select Next.
8. Assign a transport request.
9. Select Finish.
Result
The created behavior definition object represents the root node of a new business object in ABAP RESTful
programming model.
In the Project Explorer, the new behavior definition is added to the Core Data Services folder.
In the editor, a new behavior definition is created with a basic structure. You can now start editing or refining
the behavior definition using predefined language elements.
You can define and edit the behavior of the business object in the created behavior definition.
Availability of features
Activation Ctrl + F3
Deleting
Standard
Duplicating
Editing
Search
Where-Used Search Ctrl + Shift + G
Convenience
Formatting Shift + F1
Navigation F3
Outline
Syntax Highlighting
Element Information F2
Others
Comparing Source Code
Version History
Share Link
Related Information
Prerequisites
To implement the behavior of business objects defined in the behavior definition, create a behavior
implementation class.
Procedure
1. In the Project Explorer, select the relevant behavior definition for which you want to create a behavior
implementation class.
2. Open the context menu and select New Behavior Implementation to launch the creation wizard.
The Project and Package are automatically inserted. If needed you can change them.
3. Enter a Name and Description for the behavior implementation class.
The name of the Behavior Definition is automatically inserted. If needed you can change it.
The behavior implementation class is created. In Project Explorer, the new behavior implementation is added to
the corresponding folder.
The Local Types tab is automatically opened. In the tab, local lcl_handler and lcl_saver classes are created.
These local classes are used to implement the interaction phase and the save sequence in an unmanaged
transactional scenario.
You can now start implementing the behavior using predefined language elements.
Related Information
1. From the Project Explorer, select the behavior definition you want to document. Use the context menu to .
The created document is opened in the Knowledge Transfer Document Editor.
2. The behavior definition is displayed with its elements, such as actions, functions, associations, and
standard operations, structured as a tree in the Object Structure section. In the Documentation section,
you can .
In the context of the ABAP RESTful programming model, a business service is a RESTful service which can be
called by a consumer. It is defined by exposing its data model together with the associated behavior. A business
service consists of a service definition and a service binding.
A service definition provides the CDS entities that are part of the data model to be exposed as a business
service.
Prerequisites
You need the standard developer authorization profile to create ABAP development objects.
Context
You want to define the data to be exposed as a business service by one or more service bindings.
1. In your ABAP project, select the relevant package node in the Project Explorer.
2. Open the context menu and choose New Other ABAP Repository Object Business Services Service
Definition to launch the creation wizard.
3. In addition to the Project and Package, enter the Name and the Description for the service definition to be
created.
4. [Optional:] If you want to create a service definition that exposes another CDS entity, enter the relevant
entity name as Exposed Entity.
Note
5. Choose Next.
6. Assign a transport request.
7. Choose Next.
8. Choose a template which you want to base your service definition.
9. Choose Finish.
Results
In the selected package, the ABAP back-end system creates an inactive version of a service definition and
stores it in the ABAP Repository.
In the Project Explorer, the new service definition is added to the Business Services folder of the corresponding
package node. As a result of this creation procedure, the source editor will be opened. Here, you can start enter
the CDS entities to be exposed as a business service.
Using a service binding you can enable a service definition to create a business service with a protocol of your
choice.
Prerequisites
● You need the standard developer authorization profile to create ABAP development objects.
● You have created the relevant Service Definition [page 903].
You can use an existing service definition to create a business service with an OData V2 protocol for example.
Procedure
1. In your ABAP project, select the relevant package node in the Project Explorer.
2. Open the context menu and choose New Other ABAP Repository Object Business Services Service
Binding to launch the creation wizard.
3. In addition to the Project and Package, enter the Name and the Description for the service binding to be
created.
Note
Note
The protocol is OData V2 and the available categories are UI and Web API. An UI based OData V2
service can be consumed by any SAP UI5 application. An Web API based OData V2 service can be used
for providing APIs and not UI based applications.
5. If not yet specified, search for the Service Definition that you want to use as a base for your service binding.
6. Choose Next.
Results
In the selected package, the ABAP back end system creates a service binding and an OData V2 service.
In the Project Explorer, the new service binding is added to the Business Services folder of the corresponding
package node. As a result of this creation procedure, the form editor will be opened. Here, you can activate your
OData V2 service.
Restriction
CDS view with parameters that have simple types are not supported.
Related Information
After you create a service binding, the editor is displayed. The following actions can be done here:
OData V2 (UI) ● The Service Versions section shows the service version and the associated service defini-
tion.
● You can add a new service version for an active service definition by clicking Add.... Similarly,
choose Remove to remove a service version.
● The Service Version Details section shows the local service endpoint and lists the entity sets.
You can click Service URL to view the service document. Select an entity set and click
Preview to open a preview of the Fiori elements app in an external browser. Alternately, you
can do this by right-clicking an entity set and selecting Open Fiori Elements App Preview. For
each entity set, the navigation shown represents the association with another entity set.
● Use the Activate button to see the service details for each service version. After you've acti
vated the service, you can choose Deactivate to revert to the inactive state. The service in
formation doesn’t appear for a service that is deactivated.
● For each service version, an authorization value is generated. Choose Default
Authorization Values to open the Authorization Default Values editor. For maintaining default
authorization values, see SAP Cloud Platform - ABAP Development User Guide.
OData V2 (Web API) ● The Service Versions section shows the service version, API state, and the associated serv
ice definition.
● You can add a new service version for an active service definition by clicking Add.... Similarly,
choose Remove to remove a service version.
Note
You can only remove a service version that was created last, provided it is in the state
Not Released.
● You can change the API state for a service version using the context menu under API State
and selecting API State. For more information, see Released APIs.
● The Service Version Details section shows the service information and lists the entity sets.
For each entity set, the navigation shown represents the association with another entity set.
● Use the Activate button to see the service details for each service version. After you've acti
vated the service, you can choose Deactivate to revert to the inactive state. The service de
tails do not appear for a service that is deactivated.
● For each service version, an authorization value is generated. Choose Default
Authorization Values to open the Authorization Default Values editor. For maintaining default
authorization values, see SAP Cloud Platform - ABAP Development User Guide.
Test Class Generation You can generate automated tests for the OData service you've created using service binding.
The test provides guidance on how to access the OData service using ABAP Units and provides
the test code for performing CRUD operations on an entity set. Perform the following steps to
generate a test class for a selected entity set:
A test class is generated in which local test classes for each CRUD operation in the selected en
tity set can be viewed. You can either create a separate test class for each entity set or copy and
paste the generated code, then change the name of the entity set accordingly for writing ABAP
Units for other entity sets.
Note
The OData service tests and the OData service run in the same session. Thus, these tests
can use various test double mechanisms available in ABAP Unit for isolating database de
pendencies.
1. When service binding is created via abapGit pull, the underlying artifacts are not created.
2. Activate all service definitions that are part of service binding as service versions.
3. When all service definitions are activated, click the Generate service artifacts link to generate the
underlying artifacts.
The service binding has additional service versions in source system and these new versions need to be
updated in the target system using abapGit pull.
1. The abapGit pull for service binding update will not be successful if the new service definitions are added to
the service binding in the source system and service definition are created in the target system using
abapGit pull. Also, the service binding is also part of the same abapGit pull.
Then the service binding will not get updated with the new service versions in the first pull.
2. In this scenario, you need to first activate the newly created service definitions (which are pulled using
abapGit).
3. The abapGit containing the service binding update needs to be pulled again. This will update the service
binding with new service versions.
Context
Procedure
1. In the Project Explorer, select the service binding you want to document. Use the context menu to create a
knowledge transfer document. For more information, see Creating Knowledge Transfer Documents.
The service binding is displayed with its elements, such as service name and service version, in a tree
structure in the Object Structure section.
2. In the Documentation section, you can document at the service and version level. For more information,
see Editing Knowledge Transfer Documents.
A projection view enables you to expose a subset of data from an underlying data model, for example in an
OData service.
Prerequisites
You need the standard developer authorization profile to create development objects.
Context
In a transactional scenario, you want to create and define, for example, a consumption-specific OData service
that only exposes relevant data of an underlying data model using a service definition and service binding.
Note
Procedure
1. In your ABAP cloud project, select a package node in the Project Explorer.
2. To launch the creation wizard, open the context menu and choose New Other... Core Data Services
Data Definition .
3. In addition to the Project and Package, enter a Name and Description for the data definition you want to
create.
Note
4. Choose Next.
5. Assign a transport request.
6. Choose Next.
7. Select the Define Projection View template.
Note
By default, ABAP Development Tools uses the last selected template for creation.
8. Choose Finish.
In the selected package, the ABAP cloud system creates an inactive version of a data definition, and stores it in
the ABAP repository.
In the Project Explorer, the new data definition is added to the Core Data Services folder of the corresponding
package node. As a result of this creation procedure, the source editor is opened, where you can start
completing the template.
You can now define the elements from a datasource from which you want to expose data, for example, in an
OData service.
The ABAP Flight Reference Scenario helps you to get started with development in the context of the ABAP
RESTful Programming Model. It contains demo content that you can play around with and use to build your
own sample applications..
The reference scenario is based on multiple database tables which you can use to build your application. These
tables are also used in the development guides in the Develop [page 159] section.
Hover over each element for a description of the related database table. Click the element to view the database
table fields.
/DMO/AGENCY
The database table /DMO/AGENCY stores general data about the travel agency that operates travels for
customers.
● agency_id (key)
● name
● street
● postal_code
● city
● country_code
● phone_number
● email_address
● web_address
/DMO/TRAVEL
The database table /DMO/TRAVEL stores general travel data. In addition, it includes administrative data about
the creation and changing of instances.
● travel_id (key)
● agency_id
● customer_id
● begin_date
● end_date
● booking_fee
● total_price
● currency_code
● description
● status
● createdby
● createdat
● lastchangedby
● lastchangedat
/DMO/CUSTOMER
The database table /DMO/CUSTOMER stores general data about customers. In addition, it stores
administrative data about the creation and changing of instances.
● customer_id
● first_name
● last_name
● title
● street
● postal_code
/DMO/BOOKING
The database table /DMO/BOOKING stores data about a booked flight for a certain travel instance. Apart from
general flight and booking data, it includes the customer ID for whom the flight is booked as well as the travel
ID to which the booking belongs.
● travel_id (key)
● booking_id (key)
● booking_date
● customer_id
● carrier_id
● connection_id
● flight_date
● flight_price
● currency_code
The key fields are the travel ID for the travel it belongs to and the booking ID, which are unique in combination.
/DMO/FLIGHT
The database table /DMO/FLIGHT stores general data about flights.
● carrier_id (key)
● connection_id (key)
● flight_date (key)
● price
● currency_code
● plane_type_id
● seats_max
● seats_occupied
The key fields are the IDs for carrier and connection as well as the flight date, which makes the flight unique.
/DMO/BOOK_SUPPL
The database table /DMO/BOOK_SUPPL stores data of booking supplements that can be booked for flights,
for example meals or insurances.
● travel_id (key)
● booking_id (key)
● booking_supplement_id (key)
● supplement_id
● price
● currency_code
The key fields are the travel ID, the booking ID and the booking supplement ID, which are unique in
combination.
/DMO/CONNECTION
The database table /DMO/CONNECTION stores general data about flight connections.
● carrier_id (key)
● connection_id (key)
● airport_from_id
● airport_to_id
● departure_time
● arrival_time
● distance
● distance_unit
The key fields are the IDs of carrier and connection, which are unique in combination.
/DMO/CARRIER
● carrier_id (key)
● name
● currency_code
/DMO/AIRPORT
● airport_id (key)
● name
● city
● country
● supplement_id (key)
● price
● currency_code
/DMO/SUPPL_TEXT
The database table /DMO/SUPPL_TEXT stores the readable texts for the supplements in different languages.
● supplement_id (key)
● language_code (key)
● description
The key fields are the IDs of the supplement and the language, which are unique in combination.
You can download the complete ABAP Flight Reference Scenario for the ABAP RESTful Application
Programming Model from GitHub.
https://github.com/SAP-samples/abap-platform-refscen-flight/tree/Cloud-Platform .
The steps to include the development objects in your ADT are described in the README.md file.
Remember
The namespace /DMO/ is reserved for the demo content. Apart from the downloaded ABAP Flight Scenario,
do not use the namespace /DMO/ and do not create any development objects in the downloaded packages.
You can access the development objects in /DMO/ from your own namespace.
Naming conventions facilitate the development. An addition to the name of development objects conveys
standardized meaning and generates consistency in your development.
General Rules
Remember
Note
Consider that the namespace /DMO/ is reserved for demo purposes. Do not use this namespace in
your productive development.
● A prefix is used for cases when there are generically different types of one development object. Then, this
prefix states the semantic difference that cannot be conveyed through the object type.
For example, a service binding can expose an OData service for UI purposes and as a Web API [page 965].
That is why, for service bindings we introduce the prefixes UI_ and API_ to differentiate the semantics of
service bindings.
● A suffix is used for additional differentiation between different types of development objects. It helps to
recognize more subtle or secondary differences in development objects.
For example, a UI service can be bound against the OData protocol [page 957] OData, version 2 and
OData, version 4. This difference can also be manifested by suffixing the name with _O2 or _O4.
Note
In the ABAP Flight Reference Scenario we use another suffixed character (_R, _M, _D, _U, _C). This
character identifies the development object to belong to one specific development guide (read-only,
managed, ddraft, unmanaged, service consumption).
The following list provides an overview of the prefixing and suffixing guidelines on naming specific development
objects.
Database Tables
Use a prefix for database tables in scenarios, in which multiple technical representations of the same semantic
data is necessary, for example in draft scenarios.
Example: /DMO/A_TRAVEL_D
Example: /DMO/ITRAVEL_U
CDS Objects
CDS Entity
Use the prefix
Behavior Definition
A behavior definition has always the same name as the root entity of the business object.
Metadata Extension
A metadata extension has the same name as the CDS entity it relates to. If you use more than one metadata
extension for one CDS entity, you can add a numbered suffix.
Business Services
Service Definition
Since a service definition [page 947] - as a part of a business service - does not have different types or
different specifications, there is (in general) no need for a prefix or suffix to differentiate meaning.
Example: /DMO/TRAVEL_U
However, in use cases where no reuse of the same service definition is planned for UI and API services, the
prefix may follow the rules of the service binding.
Example: /DMO/UI_TRAVEL_U
Example: /DMO/UI_TRAVEL_U_O2
Behavior Pool
Use the prefix
● BP_ for an ABAP class that implements the behavior of a business object.
Example: /DMO/BP_TRAVEL_U
Depending on the modularization of your behavior implementation, you can provide the semantics of the
coding in the name of the classes.
Example: LHC_TRAVEL_CREATE
Example: LHC_BOOKING_CUD
Here are descriptions of some of the changes of interest (delta information) to developers made to ABAP
RESTful Application Programming Model:
The ABAP RESTful Application Programming Model now provides draft support for your business services.
Draft-enabled business objects ensure that transactional data does not get lost, even if work is interrupted or
continued from a different device.
You draft-enable your business object by using the keyword with draft in the behavior definition. Draft
capabilities can be included for both business object implementation types, managed and unmanaged.
You can now download a RAP managed business service with draft from GitHub. The development objects are
now available as part of the ABAP Flight Reference Scenario that you can download directly into your ABAP
system.
For more information, see Downloading the ABAP Flight Reference Scenario [page 12].
You can now define a designated field, the total ETag field, that controls concurrent access on active data in
draft business objects. The RAP runtime framework checks its value on every transition from draft to active
data to prevent concurrent BO consumers from overwriting each other without any information.
The total ETag is defined in the behavior definition and is updated by the RAP runtime framework automatically
if annotated with the annotation @Semantics.systemDateTime.lastChangedAt: true in the
corresponding CDS view.
Determinations and validations are now processed by the determinations-validations-machine (DVM). You can
now define determinations and validations with all three standard operation triggers { create | update |
delete}, and in combination with field triggers. In addition, the method declaration in the behavior pool uses
different syntax:
For more information, see Developing Validations [page 234] or Developing Determinations [page 243].
New determine actions allow the business object consumer to execute determinations and validations without
fulfilling the trigger conditions. You can now assign determinations and validations to a determine action that is
called by a consumer's trigger, just like any other actions.
For more information, see Action Definition [page 113] > Determine Action
You can now prevent illegal changes from reaching the application buffer by prechecking modify operations.
The precheck method is called during runtime before the assigned modify operation and removes all input
from the modifying request for which the condition in the precheck is not fulfilled.
For more information, see Operations [page 102] > Precheck for Modify Operations
Consumer hints in the OData service metadata now include authorization control by a path expression on the
respective operation. That means, if you define authorization control for your business object, the
authorization-relevant entity sets receive a path description instead of a static information about their ability to
be updated or deleted. In addition, the corresponding properties are included in the property list of the entity
type.
For more information, see Be prepared for the Upgrade 2008! Clean Up your RAP Implementation to Pass the
Reinforced RAP Contract Checks .
With SAP Cloud Platform ABAP environment 2008 and the ABAP Platform on premise release 2020, stricter
RAP runtime checks are introduced to help you make your RAP implementation consistent, user-friendly, and
compatible to further RAP features. These runtime checks affect
For more information, see Be prepared for the Upgrade 2008! Clean Up your RAP Implementation to Pass the
Reinforced RAP Contract Checks .
You can now provide documentation using knowledge transfer documents (KTD) for service binding.
You can now use mandatory:create to assure that a field is filled in during a create operation and
readonly:update to assure that the field value isn't changed during modify operations.
For more information, see Feature Control Definition: Fields [page 86]
You can find an overview of possibly incompatible changes for the ABAP RESTful Application Programming in
SAP Note 2943761 .
You can now define an unmanaged lock in a managed scenario. The unmanaged lock mechanism must be
defined in the behavior definition with the syntax lock master unmanaged and implemented in the behavior
pool in the method FOR LOCK, just like in an unmanaged scenario. The method is then invoked during
runtime.
For more information, see Pessimistic Concurrency Control (Locking) [page 130].
You can now use the syntax dependent by _Association for lock, ETag, or authorization dependent
entities. The association to the respective master entity must be explicitly specified in the behavior definition
and implemented when using the unmanaged scenario.
For more information, see Defining Elementary Behavior for Ready-to-Run Business Object [page 203] or
Adding Behavior to the Business Object [page 325].
You can now dynamically control the create by association operation. The syntax for defining dynamic feature
control is the following:
Like other feature controls, the control for the create by association must be implemented in the behavior pool
with the method FOR FEATURES.
For more information, see Dynamic Feature Control: Operations [page 98].
It is now possible to define new read-only associations in the projection view. These associations can be reused
to display additional information in a UI service, for example analytical data which is not part of the
transactional basic business object. With associations in projection views, you can model new service-specific
relationships.
The syntax for the association definition is the same as in CDS views:
OData does not foresee initial values for the following EDM data types:
● Boolean
● GUID
● Time
● DateTime
● DateTimeOffset.
Fiori Elements UIs offer the empty operator in filter bars for these fields. Although they appear as visually
empty in the UIs, the actual value sent to the client is null.
To be able to select records with empty values for these data types, the client filters null values and the filter is
transformed to filter for initial values for the backend. Real null values cannot be retrieved from the database by
filtering for them in OData V2.
The concepts section in the ABAP RESTful Application Programming Model has been enhanced with
interactive diagrams so you can get a better understanding of the runtime processes for OData requests.
The control structure %control for import parameters of CREATE and CREATE by Association operations
is now filled according to the elements that were sent by the OData client or were marked in the EML call. In
particular this means that the application developer knows which elements are relevant for the create.
Example
In the following example the elements agencyid, customerid, begindate, and enddate were filled by
the OData client and thus are marked in the control structure, while the other elements are not.
The managed runtime framework can now automatically generate key values in scenarios with UUID keys if
managed numbering is defined in the behavior definition.
For more information, see Automatically Drawing Primary Key Values in Managed BOs [page 579].
The BO runtime framework now supports results for actions and functions other than $self. The results can
be entities of the same BO the action is defined for, entities of other BOs, or result structures. To differentiate
between result entities and result structures the syntax element entity has been introduced.
In addition, you can now create actions, for which the action consumer can decide whether the result shall be
returned completely or only parts of it, for example the keys only. Such an action must be marked with the
keyword selective in the behavior definition.
The implicit returning parameter REPORTED is now available for the methods adjust_numbers and save. By
filling this parameter you can report information or success messages after the point of no return in the save
sequence.
For more information, see Method ADJUST_NUMBERS [page 870] and Method SAVE [page 871].
Now you can document behavior definitions in the Knowledge Transfer Document editor.
You can now define the OData service namespace in service definitions with the annotation
@OData.schema.name.
SAP Cloud Platform, ABAP Environment 1911 enforces the proper assignment of result values in action
implementations when result parameters are declared in the corresponding action definition. Whereas actions
returned the input entity if the result parameter was not filled in the action implementation before 1911, they
To avoid that a Fiori UI shows initial values, if nothing is returned, you must fill the result parameter for all
actions.
For more information, see Developing Actions [page 217] and Implementing the SET_STATUS_BOOKED
Action [page 369].
It is now possible to navigate to associated entities using more than one navigation for both, V2 and V4
services.
Example
To request the booking supplements for the booking entity with BookingID 5 of the travel with TravelID
1, use the following syntax.
<service_URL>/Travel('1')/to_Booking('5')/to_BookingSupplement
You can now use metadata extensions to define CDS annotations outside of the corresponding data definition
of the CDS projection view. The use of metadata extensions allows the separation of concerns by separating
the data model from domain-specific semantics, such as UI-related information for UI consumption.
For more information, see Adding UI Metadata to the Data Model [page 320].
You can now define a mapping contract for applications that include unmanaged or managed business objects
based on CDS entities on the one hand, and legacy data types that are generally older and implement the
behavior or parts of it.
The general syntax for field and control (information) mapping in a behavior definition is the following:
For more information, see Using Type and Control Mapping [page 583].
You can now integrate Additional Save for each entity of a given business object with managed implementation
type. This is done in the behavior definition of the business object by adding the keyword with additional
save. The actual implementation of Additional Save takes place in a local saver class as part of the behavior
pool.
For more information, see Integrating Additional Save in Managed Business Objects [page 254].
You can now integrate Unmanaged Save within the transactional life cycle of managed business objects. In
order to integrate Unmanaged Save into the save sequence as a part of the managed runtime, you must first
add the corresponding syntax (with unmanaged save) to the behavior definition and then implement the
saver handler method as a part of the behavior pool.
For more information, see Integrating Unmanaged Save in Managed Business Objects [page 264].
Until now, the implementation of business object entity's operations and actions had to be done in the Local
Types include of the behavior pool associated with that entity. Since the Local Types include can only be
changed by one developer at a time, the efficiency of development would be significantly reduced in case of
larger implementations. As a solution, the groups can be now used to divide operations, actions and other
implementation-relevant parts of the business logic into several groups for behavior implementation.
For more information, see Using Groups in Large Development Projects [page 587].
You can now define elements in CDS projection views that are not persisted on the database. These virtual
elements are defined with the new syntax element virtual. Their calculation is done in a separate ABAP
class, which is called during runtime via an ABAP code exit for virtual elements.
For more information, see Using Virtual Elements in CDS Projection Views [page 573].
New URL for the Documentation of the ABAP RESTful Programming Model
in SAP Cloud Platform ABAP Environment
The documentation of the ABAP RESTful Programming Model for SAP Cloud Platform ABAP Environment on
the SAP Help Portal has been moved.
The ABAP RESTful Programming Model now supports the managed implementation type for developing new
transactional apps. This scenario aims at use cases to develop new transactional apps from scratch. All
required standard operations must only be specified in the behavior definition to obtain a ready-to-run
business object. The business logic is implemented using actions, validations and determinations.
For more information, see Developing Managed Transactional Apps [page 182].
You can now use the ABAP-native approach to project and to alias a subset of the business object for a specific
business service. The projection enables flexible service consumption as well as role-based service designs.
With projections, it is possible to project one business object for different role-based UIs. An example is given in
Developing a Projection Layer for Flexible Service Consumption [page 274]. The travel business object is
exposed for a processor Fiori UI and for an approver Fiori UI.
A new API is available to implement an unmanaged query in a query implementation class. In an unmanaged
query, the request is delegated to the query implementation class, which must implement the select method
the interface IF_RAP_QUERY_PROVIDER. The new interface provides methods to implement query
capabilities, such as paging, filtering, sorting, or counting. It replaces the interface
IF_A4C_RAP_QUERY_PROVIDER, which is deprecated as of 1908.
For more information about the new API, see Unmanaged Query API [page 878].
Server-Side Paging
Server-side paging has now been implemented to improve performance for OData calls. If the OData client
does not provide paging query options, the default paging restricts the response to 100 data records. If $top is
set to values greater than 5000, the response is reduced to 5000 data records.
The sources for the unmanaged reference scenario has been updated with the latest features. You can explore
the new features in the sources that you can download from GitHub and in the related description in the guide
on how to develop unmanaged transactional apps based on existing application logic.
To protect data from unauthorized read access, the ABAP CDS provides its own authorization concept based
on a data control language (DCL). The authorization checks for read operations allow you to limit the results
returned by an entity to those results you authorize a user to see.
For business objects that are implemented for managed contract, also modifying operations in such as
standard operations create, update, delete, create by associations, and actions must be checked against
unauthorized access. With the current release, the instance-based authorization control is supported.
For more information, see Adding Authorization Control to Managed Business Objects [page 601].
Apart from static feature control, you can now also use dynamic feature control. In this case, it depends on a
state of the node instance if certain elements or actions are available.
...
define behavior for /DMO/I_TRAVEL_M
field (features : instance ) travel_id;
action ( features: instance ) acceptedTravel result [1] $self;
…
The implementation for the control must then be implemented in the respective methods in the behavior pool.
For example, for this behavior definition, you can implement that the field travel_id is mandatory on create,
but read-only on the update operation. The action can be implemented as disabled if the travel entity is already
set to accepted, but enabled if it is not yet accepted.
A business object consists of hierarchical connected entities. The behavior for each entity is defined in the
behavior definition object and implemented in the behavior classes. In the Relation Explorer, you can see
structure and behavior of a certain business object independent of the technical location. You can navigate to
all the entities and the corresponding behavior (definition and implementation).
Sometimes you might be interested in more CDS-specific aspects and want to see access control lists or test
classes. You can achieve this by switching the context from Business Object to Core Data Services context, as
you can see in the following animation.
1. ETag support can now be selected for any entity set. If ETag support is marked in your edmx file, the Etag
support checkbox is selected by default.
2. An entity set can now be selected for the generation. You can only edit the ABAP artifact name for the
entity set that you've selected for generation.
3. Issues in an entity set are displayed and these entity sets cannot be selected for generation.
Understanding Concepts
As an application developer, you may not only be interested on how you can implement different scenarios and
use cases for your business applications by following the stateless programming paradigm of the ABAP
RESTful programming model. It may also be important for you to understand the main concepts behind it.
The concepts section provides you with background information of ABAP RESTful programming model and
helps you to understand main concepts from both, the design time, and runtime perspective.
You can now generate automated tests for an OData service that you've created using service binding. The test
class provides guidance on how to access the OData service using ABAP Units and provides the test code for
performing CRUD operations on an entity set.
For more information, see Using Service Binding Editor [page 907].
You can now view the code sample for performing the CRUD operations on an entity set belonging to a remote
OData service.
You can now define external names for OData entity sets and entity types that are then used in the OData
service metadata. The annotations @OData.entitySet.name and @OData.entityType.name can now be
used in any CDS entity.
You can now download the relevant development objects that are used in the course of the development guides
via GitHub. This enables you to import a complete OData service into your system, which you can use and
reuse for learning purposes.
Limitation: The import of a service binding artifact is not possible. To complete the OData service, you need to
create service binding in your own package.
For further information, see Downloading the ABAP Flight Reference Scenario [page 12].
Entity Manipulation Language (in short: EML) is a part of the ABAP language that is used to implement the
business object’s behavior in the context of ABAP RESTful programming model. It provides a type-save read
and modifying access to data in transactional development scenarios.
For further information, see Entity Manipulation Language (EML) [page 154].
The development guide Developing Unmanaged Transactional Apps [page 299] was extended with a 3-tier
entity hierarchy. Booking Supplements are now part of the business object.
For further information, see Adding Another Layer to the Transactional Data Model [page 391].
In addition, the travel business object can now be consumed using EML syntax.
For further information, see Consuming Business Objects with EML [page 593].
Service Binding UI
Consuming Services
The service consumption model replaces the OData client proxy to generate service artifacts for an OData
service. It comes with a new wizard and an editor to work on the generated artifacts. The service consumption
model artifact provides an overview of all abstract entities and generated behavior and service definitions that
belong to the imported service.
The guide on how to consume a remote service has been extended with transactional capabilities for additional
data. The use case of maintaining discount data in a local database is exemplified in this scenario.
For further information, see Adding Transactional Behavior to the Business Object [page 524].
Aggregate functions, such as sum, maximum, minimum and average, as well as a counting option are now
available in the ABAP environment to be implemented in CDS and displayed in your SAP Fiori App. Annotations
are used to mark the elements as measures, whose values can be aggregated.
For further information, see Using Aggregate Data in SAP Fiori Apps [page 565].
In a typical transactional scenario, you have to specify which operations should be provided by the whole entity
or you must specify which fields of an entity have specific access restrictions (read-only or mandatory fields).
For further information, see Static Feature Control Examples: Fields [page 88].
The method name in handler classes is now freely selectable. What kind of method it is, is expressed by the
FOR clause.
The old syntax METHODS modify FOR BEHAVIOR … becomes now: METHODS FreeMethodName FOR
MODIFY ....
The old syntax METHODS read FOR BEHAVIOR … becomes now: METHODS FreeMethodName FOR
READ ....
The old syntax METHODS lock FOR BEHAVIOR … becomes now: METHODS FreeMethodName FOR
LOCK ....
Note that the old syntax remains valid but is no longer recommended!
The service binding tools come with a new UI design and some additional functions for versioning of (business)
services.
The current version of the ABAP RESTful programming model supports compositions: A business object
consists of a tree of nodes where the nodes are linked by means of a special kind of associations, the
compositions. A composition is specialized association that defines a whole-part relationship. A composite
part only exists together with its parent entity (whole).
For further information, look at Providing CDS Data Model with Business Object Structure [page 305].
It is possible to publish an OData service as an application-to-cross application (A2X) service. That means the
service is published without information relevant for a UI service (for example Value Helps or annotation to
define a UI). An A2X service facilitates the exchange of business information between an application and any
client, including from a different system or server.
With the help of the service consumption model it is now possible to consume a Web API service and build a
new SAP Fiori application by consuming the remote service. This development guide includes the definition of
a custom entity and implementing an custom query.
For more information, look at Developing a UI Service with Access to a Remote Service [page 492]
ABAP Compiler
ABAP compiler creates a byte code as interim code when generating a program from the ABAP source code.
This interim code is stored in the database as a load program and is loaded to Program Execution Area
(memory for managing the fixed data of an ABAP program while it is being executed) when required.
An ABAP-integrated development environment built on top of the Eclipse platform. Its main objective is to
support developers by offering state-of-the-art ABAP development tools. These tools include strong and
proven ABAP life-cycle management on the open Eclipse platform with powerful UI capabilities.
ABAP Dictionary
Persistent storage for data types that are visible in all repository objects. In addition, the database tables of the
central database, views, and lock objects are managed in the ABAP Dictionary - among other things.
SAP's reference scenario based on an updated flight data model. It is intended to be used for demonstration
and learning purposes in the context of the ABAP RESTful programming model.
An ABAP programming model for browser-based applications that are optimized for SAP HANA.
Processes of the ABAP runtime environment control the execution of an ABAP program by calling the
processing blocks of the program. The ABAP runtime environment is provided by the Application Server ABAP.
ABAP SQL
A subset of SQL realized using ABAP statements. ABAP SQL is used to read (SELECT) and modify (INSERT,
UPDATE, MODIFY, or DELETE) data in database tables defined in ABAP Dictionary. Database tables, views,
and all non-abstract CDS entities can be accessed directly.
Action
A modify operation [page 957] that is a part of the behavior of a business object. Actions can be related to the
instances of a business object [page 947] (default) or are static. Actions can have input parameters and a
result with a cardinality.
A draft action that saves a draft instance on the active database table. It invokes the prepare action and the
update operation in case of an edit-draft, or the create operation in case of a new-draft. Once the active
instance is successfully created the draft instance is discarded.
Additional Save
A processing step within the transactional life cycle of a managed business object [page 957]. It allows an
external functionality to be invoked during the save sequence (after the managed runtime has written the
changed data of the business object’s instances to the database, but before the final commit work is executed).
Additional save is defined in the behavior definition [page 946] of a managed business object and
implemented in the related behavior pool.
Association
A relationship between two entities of a business object's data model [page 951].
An association is a directed connection between two nodes (source and target) of BO structures.
An association path is a sequence of associations [page 944] connecting entities with each other.
Before Image
The before image denotes a data set (data image) without transactional changes in the current LUW.
Declarative language for behavior modeling of business objects in the context of ABAP RESTful programming
model. The language is syntactically oriented to CDS. Technically however, BDL artifacts are not managed by
the ABAP Dictionary [page 943], but by the ABAP compiler [page 943].
The corresponding source code artifact in ABAP repository is the (business object) behavior definition [page
946].
Behavior Characteristic
A part of the business object's behavior that specifies general properties of an entity such as late numbering,
ETag, draft handling, or feature control.
Behavior Pool
A special ABAP class pool that implements the business object’s behavior [page 946] specified in the
behavior definition [page 946].
The real substance of a behavior pool is located in Local Types. Here, the ABAP developer can define two types
of special local classes, namely handler classes for the operations within the interaction phase and saver
classes for the operations within the save sequence. These classes can be instantiated or invoked only by the
kernel.
In the ABAP RESTful programming model, a business object provides the following:
● A data model [page 951] which explicitly defines the structure of the data (the relationships within the
data, the semantics of the data and the data constraints)
The ABAP RESTful programming model uses ABAP CDS to define the data model for business objects. Each
BO contains one distinguished root node which is the leading entity within the BO. Furthermore, nodes within a
BO are connected by compositions [page 948]. All entities which can be reached by the transitive tree of
compositions starting at the root entities belong to the BO structure.
The data model's behavior [page 946] is defined and implemented in a behavior definition [page 946] and
behavior implementation [page 946] respectively.
It includes a behavior characteristic [page 945] an and a set of operations [page 958] for each entity [page
946] of the BO. To specify the business object's behavior, the behavior definition [page 946] as the
corresponding development object is used.
The behavior definition is an ABAP repository object that is used to specify the business object's behavior
[page 946].
The behavior implementation is an ABAP class that implements the business object's behavior [page 946].
A node in a business object's [page 945] structure. In the ABAP RESTful programming model, an entity is used
as the composition [page 948] unit of a business object structure.
A subset of a business object data model and/or business object behavior. A business object that is designed
for general purpose can be restricted for a specific business service in a BO projection. One of the most
prominent examples is the Business Partner, which can be projected as Customer, Supplier, or Vendor.
The period of time during which requests for a business object are processed.
Business Service
A business service is a RESTful service which can be called by a client. It consists of a service definition [page
947] and a service binding [page 947].
A service binding is an ABAP repository object used to bind a service definition [page 947] to a client server
communication protocol such as OData (HTTP).
A service definition is an ABAP repository object defining the CDS entities [page 949] that are exposed for an
OData service, including their behavior [page 946].
In ABAP CDS, entities [page 946] are connected using compositions. A child entity is a CDS entity which is the
target of a composition [page 948].
Composition
A specialized association [page 944] that has a whole-part relationship. A composite part only exists together
with its parent entity [page 958] (whole). Compositions are defined in CDS entities [page 949] using the
keyword COMPOSITION OF.
Composition Path
The composition path is a sequence of compositions [page 948] connecting nodes with each other.
Composition Tree
A composition [page 948] tree represents the hierarchy of nodes in a business object’s [page 945]
Each node of a composition hierarchy has entities that are modeled in the ABAP RESTful programming model
using structure where the nodes are linked by the composition relationship.CDS entities [page 949] where the
root [page 960] is the top node in the business object's structure.
CDS provides an infrastructure for defining and consuming semantically rich data models [page 951] in SAP
HANA.
In particular, ABAP CDS provides a framework for defining and consuming semantic data models on the central
database of the application server AS ABAP. The specified data models are based on the data definition
language (DDL) and the data control language (DCL).
A CDS entity [page 949] defined using the keyword DEFINE ABSTRACT ENTITY in a CDS data definition
[page 951].
An abstract entity defined the type attributes of a CDS entity without creating an instance of a database object.
CDS access control can be applied to CDS entities and is enabled by default for every CDS entity. It can be
disabled for individual entities using an entity annotation.
CDS Annotations
An annotation enriches a definition of a CDS [page 948] object with metadata going beyond the syntactical
features offered by SQL. It can be specified for specific scopes of a CDS object, namely specific places in a
piece of CDS source code.
A CDS entity [page 949] defined using the keyword DEFINE CUSTOM ENTITY in a CDS data definition [page
951]. A CDS custom entity is a non-SQL CDS entity with an unmanaged query [page 964] runtime
implemented in ABAP.
CDS Entities
ABAP CDS entities (also referred to as CDS entities) are structured objects based on the DDL (Data Definition
Language) specification and are managed by ABAP Dictionary.
A CDS object (of the ABAP CDS) defined in a piece of DDL source code using language elements in CDS DDL.
In a metadata extension (MDE), CDS annotations [page 949] are specified for a CDS entity outside of the
Result of CDS view projection. A CDS projection is defined in a data definition in which you can define the
service-specific projected data model, a subset of the data model of the general business object.
An ABAP CDS view entity (also referred to as a CDS view entity) is defined for existing database tables,
database views, or for other non-abstract CDS entities by using the ABAP CDS statement DEFINE VIEW
ENTIIY within a DDL source.
Create Operation
A create operation is an operation [page 958] that implements the creation of persistent instances of entities
(BOs).
Create-by-Association Operation
A modify operation [page 957] that is used to create instances of the associated (child) entity by the source of
the association (parent entity).
The Common Schema Definition Language (CSDL) defines specific representations of the entity data model
(EDM) exposed by an OData service, for example in an XML format.
Consumer Hint
A marker for OData Consumers, that is defined in the backend, for example field control (read-only or
mandatory).
These consumer hints are exposed in the OData metadata and interpreted in a Fiori Elements UI, for example
with an asterisk for mandatory fields.
A subset of SQL statements for executing authorization and consistency checks in relational databases.
The Application Server ABAP maps the functions of the data control language onto constructs such as
authorizations objects and locks.
Data Definition
ABAP development object used to define an ABAP CDS entity [page 949] (for example, a CDS view).
After creating a data definition, the developer is able to use the standard functions of the ABAP Workbench -
such as syntax check, activation, or connecting to the Transport Organizer. The developer creates a data
definition using a wizard in ABAP Development Tools.
Data Model
Set of entities that represents a specific self-contained business object and is used to define a people-centric
view of respective business information.
Delete Operation
A delete operation is an operation [page 958] that implements the deletion of persisted instances of entities
(BOs).
The ABAP compiler allows the creation of derived types for the type-save parametrization of the BO provider
code. Such data types are referred to as derived types because they are implicitly derived by the compiler from
CDS entity types and their behavior definition [page 946].
Determine Action
An action that invokes the determinations and validations that are defined for it in the behavior definition.
A determination is an implicitly executed action that is used to handle side effects of modifications by changing
instances and returning messages.
A draft action [page 952] that deleted the draft instance from the draft database table.
Draft Action
An action that is implicitly available for draft business objects. A draft action can also be declared explicitly in
the behavior definition. For some draft actions, an implementation exit for application specific business logic
exists.
Draft-Eenabled Association
An association that retrieves active data if it is followed from an active instance and draft data if it is followed
from a draft source instance.
Draft Indicator
A component in modify and read requests to determine whether the request is aimed at active or draft
instances.
In OData the draft indicator is IsActiveEntity =true / false. ABAP/ ABAP EML uses %IS_DRAFT =
if_abap_behv=>mk-on/off .
Early Numbering
A numbering concept by which newly created entity instances are given a definitive key value during the
interaction phase [page 955] on the create operation [page 950].
A draft action that copied an active instance to the draft database table.
Element
An integral part of an entity [page 946]. An element can be a field [page 954] or an association [page 944].
EML
Entity Manipulation Language (in short: EML) is a part of the ABAP language that is used to implement the
business object’s behavior [page 946] in the context of ABAP RESTful programming model. It provides a type-
save read and modifying access to data in transactional development scenarios.
An ETag is a field [page 954] that is used to determine changes to the requested resource. Usually, fields like
last changed timestamp, hash values, or version counters are used as ETags.
An ETag can be used for optimistic concurrency control in the OData protocol to help prevent simultaneous
updates of a resource from overwriting each other. An ETag check is used to determine whether two
representations of a business entity [page 946], are the same. Whenever the representation of the entity
changes, a new and different ETag value is assigned.
External Numbering
A numbering concept by which newly created entity instances are given their values by external BO consumers,
e.g. a Fiori [page 961] UI.
Feature Control
A functionality that provides property settings for fields [page 954], entities [page 946], actions [page 944],
or associations [page 944] of a given business object [page 945]
These settings control the behavior of a business object [page 946] when it is in a certain state.
On the user interface, these settings control, for example, the following:
The feature control is either static (valid for all instances of an entity) or dynamic (depends on the state of the
node instances).
Field
An element [page 953] of an entity [page 946] (business object), which represents a data object.
Full text searching (or just text search) provides the capability to identify natural-language terms that satisfy a
query and, optionally, to sort them by relevance (ranking) to the query.
Function
A read operation [page 959] that is a part of a business object's behavior. Functions are defined similarly to
actions [page 944], but they do not cause any side effects.
Fuzzy search is a fast and fault-tolerant search feature of SAP HANA. The concept behind the fault-tolerant
search means that a database query returns records even if the search term (user input) contains additional or
missing characters, or other types of spelling errors.
Instance Action
Interaction Phase
A part of the BO runtime where a consumer calls the business object's operations to modify or read business
data in a transactional context.
A user triggers the interaction phase by clicking the EDIT button on UI. The interaction phase ends when the
user clicks the SAVE button on UI.
Internal Action
An action that can only be executed from the business logic inside the same business object the action is
assigned to, such as from a determination [page 952] or another action.
Internal Numbering
A numbering concept by which newly created entity instances are given their values by BO internal logic, e.g. by
the managed runtime framework.
Late Numbering
Late numbering is a concept by which new entity instances [page 947] are given a definitive key just before
they are saved on the database.
The leaf entity is an entity [page 946] in a business object's structure without any child entities [page 948].
A leaf entity is a CDS entity which is the target of a composition [page 948] (a child entity) but does not
connect further entities (does not contain a composition definition).
Lock
The ability to protect data of entities from concurrent accesses by multiple users.
Lock Master
A lock master defines the property of entities to be locked on theirselves. This is currently only supported for
root entities [page 960] of business objects [page 945].
Lock Dependent
A lock dependent is an entity [page 946] that depends on the locking status of a parent [page 958] or root
[page 960] entity.
When data in database tables is modified by application programs, it must be ensured that the data is
consistent after the changes have been made. This is particularly important when data is edited in the
database. The time span in which a consistent data state is transferred to another consistent state is known as
an LUW (Logical Unit of Work).
Managed
The managed property defines an implementation type of a business object [page 945] or a query [page 959]
provider in the context of the ABAP RESTful programming model. See also: unmanaged [page 963]
For the implementation type managed, the generic runtime framework assumes standard implementation
tasks. The business logic is implemented by the application developer via validation, determinations and
actions.
RAP runtime framework that processes business objects with implementation type managed [page 956].
Managed Save
A processing step within the save sequence of a managed business object [page 957].
Modify Operations
Umbrella term for operations [page 958] causing business data changes in the context of behavior
implementation [page 945]. It includes standard operations (create, update and delete) and action [page 944]
execution. implementation.
New-Draft
A draft instance that is created from scratch without having a corresponding active instance yet.
OData
The Open Data (in short: OData) protocol is a Web protocol for querying and updating data. It applies several
Web technologies, such as HTTP, Atom Publishing Protocol, and JSON to provide access to information from a
variety of applications.
OData is based on industry standards and offers database-like access to business data using a REST-based
(Representational State Transfer) architecture.
A proxy for an OData client that acts as an intermediary to forward and transform requests from one service to
another one.
A service that is implemented in accordance with OData [page 957] protocol. OData services are used to
expose data to consumers.
Operation
Example are changing operations create, update, delete that are performed within a transactional life cycle of a
business object [page 945].
Orchestration Framework
Runtime framework for request dispatching and runtime checks within the ABAP RESTful programming model.
Parent Entity
The parent entity is an entity [page 946] in a business object's [page 945] structure that is directly connected
to another entity when moving towards the root node.
In ABAP CDS, entities are connected using compositions [page 948]. A parent entity is a CDS entity which
contains a composition definition (keyword COMPOSITION OF).
Persistent Field
A draft action [page 952] that invokes the determinations and validations that are defined in the prepare in the
behavior definition. It is invoked by the invoked by the Activate action [page 944].
CDS entity whose elements are projected in a projection view. The projected entity is specified in the
PROJECTION ON clause of the CDS projection view.
Projection
Query
In contrast to the BO [page 945] transactional capabilities, query capabilities are always read-only and do not
modify data on the database
The query implementation class is the class that is referenced by a custom entity [page 949] to implement its
query [page 959].
The framework that orchestrates and processes each operation within RAP business objects. It includes the
SAP Gateway framework, the orchestration framework and the frameworks that process business objects
regarding their implementation type.
Read Operation
Umbrella term for operations [page 958] that do no change any business data in the context of business object
behavior implementation [page 946]. It included operations such as read, read by association, and functions.
A draft action [page 952] that recreates the lock for a draft instance whose exclusive lock is already expired.
Create-by-Association Operation
A read operation [page 959] that is used used to read instance data of the associated (child) entity by the
source of the association (parent entity).
Root Entity
The root entity is the top entity [page 946] in a business object's [page 945] structure. In ABAP CDS, a root
entity is defined using the keyword ROOT in the data definition [page 951].
SADL
Service Adaptation Description Language (in short: SADL) is an ABAP technology that enables the
consumption of entity relationship-like data models [page 951] in ABAP based on a model-driven approach.
In the context of SAP HANA, SADL enables fast read access to database data for scenarios on mobile and
desktop applications using query push-down.
SAP Cloud Platform is an open platform as a service (PaaS) that provides customers and partners with in-
memory capabilities, core platform services, and unique business services for building and extending
personalized, collaborative, mobile-enabled cloud applications.
SAP Cloud Platform ABAP Environment is part of the SAP Cloud Platform [page 960] and offered as Platform
as a Service (PaaS). ABAP Environment provides a special variant of the ABAP platform and supports a subset
of the ABAP language.
SAP Fiori is a new user experience (UX) for SAP software that applies modern design principles. SAP solutions,
such as the SAP Business Suite powered by SAP HANA, use the SAP Fiori UX to provide a personalized,
responsive, and simple user experience.
SAP Gateway
A browser-based development tool set for modeling and developing Fiori UIs.
Save Sequence
Part of the BO runtime when data is persisted after all changes were performed.
A set of artifacts that are generated in ABAP Development Tools on the basis of an entity data model XML
(CSDL) file and are used to provide a generic client for remote OData service [page 958]consumption.
Declarative language for defining service definition [page 947] objects. The language is syntactically oriented
to CDS.
An ABAP API using the service consumption model [page 961] to consume remote OData services [page 958].
A message type that is used for message that relates to the state of an instance.
Static Action
To-Parent Association
A to-parent association in ABAP CDS is a specialized association [page 944] which can be defined to model the
parent-child relationship between two CDS entities [page 949].
Compositions [page 948] and to-parent associations are used to define the structure of a business object
[page 945] which can be used in the ABAP RESTful Programming model.
Total Etag
A designated field in a draft business object to enable optimistic concurrency checks during the transition from
draft to active data.
When resuming a draft instance, the RAP draft runtime framework checks if the value of the total ETag field on
the active persistence matches the value on the draft persistence. If the values do not correspond, the resume
action is rejected.
Transactional Buffer
Temporary storage for data that is used in one logical unit of work (luw) during the interaction phase [page
955] for modifying and read operations (in a transactional context) and which can be persisted during the save
sequence [page 961]. After every LUW, the buffer must be emptied. The state of the buffer is either saved to a
database table or must be rolled back to achieve the state the data had before the LUW.
Transaction-Enabled Association
A message type that is used for messages that arise on the transition of a BO-instance from one state to
another.
Trigger Condition
The condition that needs to be fulfilled to execute a validation [page 965] or a determination [page 952].
A trigger condition consists of a trigger operation [page 963] (create, update, create by association) and a list
of entity fields (trigger elements [page 963]) belonging to the same entity the validation/determination is
assigned to.
Trigger Element
Elements of the assigned entity that trigger the validation [page 965]/determination [page 952] when
affected by the trigger operations [page 963].
Trigger Operation
Operations on which a validation [page 965]/determination [page 952] is executed, for example create or
update.
Trigger Time
The point in time when the validation [page 965]/determination [page 952] is executed during the BO [page
945] lifecycle. The trigger time is declared in the definition of the validation [page 965]/determination [page
952].
Unmanaged
The unmanaged property defines an implementation type of a business object [page 945] or a query [page
959] provider in the context of the ABAP RESTful programming model.
For the implementation type unmanaged, the application developer must implement essential components of
the REST contract itself.
In this case, all required BO operations [page 958] (create, update, delete, or application-specific actions) must
be specified in the corresponding behavior definition [page 946] for a BO before they are implemented in
Unmanaged Query
An implementation type for the runtime of a query [page 959]. In an unmanaged query, the query contract
must be implemented by the application developer and is not managed by the query framework.
Unmanaged Lock
A feature of a managed business object [page 964] with which you can implement the lock mechanism
manually. Like, in the unmanaged [page 964] scenario, the method FOR LOCK is called during runtime.
RAP runtime framework for processing business objects with implementation type unmanaged [page 963].
Unmanaged Save
A processing step within the transactional life cycle of a managed business object [page 964] that prevents the
business object’s managed runtime from saving business data (changes) during the save sequence [page 961].
In this case, the function modules (for update task) are called to save data changes of the relevant business
object.
Unmanaged save is defined in the behavior definition [page 946] of a managed business object and
implemented in the related behavior pool.
Update Operation
An update operation is an operation [page 958] that implements the update of instance data of entities (BOs).
A validation is an implicitly executed function that checks the consistency of entity instances that belong to a
business object.
It is defined in the behavior definition and implemented in the related behavior pool.
Virtual Element
Element that is not persisted on the database but calculated during runtime.
A virtual element is declared with the statement VIRTUAL in CDS projection views [page 950].
Web API
An OData service [page 958] that is published without any UI specific metadata. It is not exposed for a UI
context. Instead it provides an API to access the service by another client, including from a different system or
server.
Hyperlinks
Some links are classified by an icon and/or a mouseover text. These links provide additional information.
About the icons:
● Links with the icon : You are entering a Web site that is not hosted by SAP. By using such links, you agree (unless expressly stated otherwise in your
agreements with SAP) to this:
● The content of the linked-to site is not SAP documentation. You may not infer any product claims against SAP based on this information.
● SAP does not agree or disagree with the content on the linked-to site, nor does SAP warrant the availability and correctness. SAP shall not be liable for any
damages caused by the use of such content unless damages have been caused by SAP's gross negligence or willful misconduct.
● Links with the icon : You are leaving the documentation for that particular SAP product or service and are entering a SAP-hosted Web site. By using such
links, you agree that (unless expressly stated otherwise in your agreements with SAP) you may not infer any product claims against SAP based on this
information.
Example Code
Any software coding and/or code snippets are examples. They are not for productive use. The example code is only intended to better explain and visualize the syntax
and phrasing rules. SAP does not warrant the correctness and completeness of the example code. SAP shall not be liable for errors or damages caused by the use of
example code unless damages have been caused by SAP's gross negligence or willful misconduct.
Gender-Related Language
We try not to use gender-specific word forms and formulations. As appropriate for context and readability, SAP may use masculine word forms to refer to all genders.
SAP and other SAP products and services mentioned herein as well as
their respective logos are trademarks or registered trademarks of SAP
SE (or an SAP affiliate company) in Germany and other countries. All
other product and service names mentioned are the trademarks of their
respective companies.