ABAP Development for SAP S4HANA (SAP PRESS) (第6页)

第 137 页 / 共 465 页

4.1 Core Data Services Views

calculation based on basic interface layer CDS views but is still intended for reuse. For

now, we’ll simply add all fields of the basic purchase document view to the selection

list of the new view.

Because the overall price calculation is already a calculation with moderate complex-

ity, we should develop this calculation using test-driven development (TDD) to verify

and safeguard our developed logic in an automated and repeatable way. Developing

tests in a test-driven fashion means that before we implement the actual calcula-

tions, we implement tests. TDD will allow us to incrementally implement calcula-

tions and will give us confidence that already-implemented logic of previous steps

isn’t breaking with subsequent steps or releases. It also has the advantage of intro-

ducing testing already during the development process so that test automation isn’t

simply omitted after a feature is working. In the end, we’ll also have a safety net for

future changes that might introduce regression bugs. Unit tests for CDS views are

based on ABAP Unit and the CDS Test Double Framework. With the CDS Test Double

Framework, it’s possible to dynamically create and configure test stubs or doubles for

views or tables that the CDS view under test depends on.

It’s very easy to create unit tests for CDS views because the ABAP development tools

in Eclipse already provide good unit test support. As shown in Figure 4.9, you can sim-

ply right-click on a CDS view to create a new global ABAP Unit test class for this view

by choosing New Test Class.

Figure 4.9 Generating a Unit Test Skeleton Class for a CDS View by Right-Clicking on the

CDS View in the Eclipse Project Explorer

This will bring up the test creation dialog wizard. On the first screen of the wizard, you

must provide a name for the global ABAP test class, the package, and a description

(Figure 4.10). The read-only CDS Under Test field denotes for which CDS view you’re

creating the test. It’s good practice to keep the tests close to the tested code so by

default the tests are put into the same package as the CDS view under test. To go to

the next screen of the wizard, click the Next button.

137

第 138 页 / 共 465 页

4 Developing an SAP Fiori Elements List Report and Object Page

Figure 4.10 Creating a New Test Class for the Purchase Documents Overall Price CDS View

(Z_I_PurchDocOverallPrice)

The next screen allows you to select the CDS views or tables your view under test

depends on and that are required as test doubles for the test cases you want to

implement (Figure 4.11). During test execution, the test doubles replace the actual

views or database tables and make it possible to dynamically provide different test

data for different test cases. In general, there are two test types: Unit Test, which

only considers first level dependencies, and Hierarchy Test, which allows test double

creation for the whole stack of CDS views the view under test depends on. In our

case, we’ll simply click on Next because we want to create a unit test and both basic

interface views our view under test depends on (Z_I_PurchaseDocument and Z_I_Pur-

chaseDocumentItem) are already preselected and required as dependencies for double

creation in our tests.

In addition to generating a global ABAP Unit test class and providing the required

dependencies for test double creation, the test class creation wizard also supports

138

第 139 页 / 共 465 页

4.1 Core Data Services Views

the test doubles with simple form-based test data creation (Figure 4.12). For our first

test case, which we’ll call overall_price_no_items, we won’t create any test data for

purchase document items and just a single table entry for purchase documents.

Figure 4.11 Dependency Selection Screen for the CDS Test

Figure 4.12 Form-Based Test Data Creation for CDS Test Doubles the View

Under Test Depends On

139

第 140 页 / 共 465 页

4 Developing an SAP Fiori Elements List Report and Object Page

When you click on Next, the newly generated global ABAP class will open in the

Eclipse source code editor. However, in the global test class, you’ll only find an empty

class definition and implementation. To look at the generated local ABAP Unit test

class containing the test fixture, data creation, and test methods, we must select the

Test Classes tab in the source code editor (Figure 4.13), which contains our just-

defined test method overall_price_no_items and a prepare_test_data_set method

that will insert our previously specified purchase document test data into the pur-

chase document test double of the test environment.

Figure 4.13 Generated Local ABAP Unit Test Class

Test data defined using the test data creation wizard won’t be inserted in the origi-

nal database tables of the dependent on views. Instead, the generated skeleton test

class will set up a separate test environment in its class_setup method that won’t

affect any productive tables and their data. The stubbed dependent on views and

their data will only be available during the runtime of the test method and are by

default cleared before each test execution in the setup test fixture method environ-

ment->clear_doubles( ).

140

第 141 页 / 共 465 页

4.1 Core Data Services Views

For our first test case, we haven’t created any items test data but only a single pur-

chase document and inserted it into the test environment. If a purchase document

has no items, the overall purchase document price should be 0. We can verify this by

adapting the generated test method, which doesn’t check for the correct value of the

overallprice field yet. In general, the test procedure for CDS views is always as fol-

lows:

1. Create test data for the views the view under test depends on (arrange or given

step).

2. Select data from the CDS view under test (act or when step).

3. Ensure that a calculated value has the expected value (assert or then step).

To check values calculated by a CDS view, we can use the standard CL_ABAP_UNIT_

ASSERT class, which provides many static methods for comparing expected and actual

values. The simple test ensuring that purchase documents without items have an

overall price of 0 is shown in Listing 4.5.

METHOD overall_price_no_items.

prepare_testdata_set_no_items( ).

SELECT * FROM z_i_purchdocoverallprice INTO TABLE @act_results.

cl_abap_unit_assert=>assert_equals( exp = 0 act = act_results[ 1 ]-

overallprice ).

ENDMETHOD.

Listing 4.5 ABAP Unit Test for the Overall Price of a Purchase Document without Items

It follows the test procedure: we first create test data in an analogous manner to the

generated test data creation method, then we select the data from the CDS view

under test using Open SQL, and finally we check whether the selected data has the

expected calculated value. Currently, the test will fail with a syntax error because we

haven’t yet added the field to the view. To fix the test, we can simply add the field to

the view like this:

0 as as OverallPrice,

Note

You can either execute unit tests by right-clicking on the global class and selecting

Run As (cid:2) ABAP Unit Test or by using the shortcut (Ctrl)+(Shift)+(F10).

141

Note You can either execute unit tests by right-clicking on the global class and selecting (cid:2) (Ctrl) (Shift) (F10)
Run As ABAP Unit Test or by using the shortcut + + .

第 142 页 / 共 465 页

4 Developing an SAP Fiori Elements List Report and Object Page

In our next test case, we’ll create a new test data creation method that has two items

with an overall item price of 10 (Listing 4.6).

METHOD prepare_testdata_set.

"Prepare test data for 'z_i_purchasedocument'

lt_z_i_purchasedocument = VALUE #(

(

purchasedocument = '1'

description = 'Doc with no items'

) ).

environment->insert_test_data( i_data = lt_z_i_purchasedocument ).

"Prepare test data for 'z_i_purchasedocumentitem'

lt_z_i_purchasedocumentitem = VALUE #(

( purchasedocument = '1' purchasedocumentitem = '1' overallitemprice =

'10' )

( purchasedocument = '1' purchasedocumentitem = '2' overallitemprice =

'10' ) ).

environment->insert_test_data( i_data = lt_z_i_purchasedocumentitem ).

ENDMETHOD.

Listing 4.6 Test Double Creation for a Purchase Document with Two Purchase Document

Items

Our second test method then will look as shown in Listing 4.7.

METHOD overall_price.

prepare_testdata_set( ).

SELECT * FROM z_i_purchdocoverallprice INTO TABLE @act_results.

cl_abap_unit_assert=>assert_equals( exp = 20 act = act_results[ 1 ]-

overallprice ).

ENDMETHOD.

Listing 4.7 ABAP Unit Test for the Overall Price of a Purchase Document with Two Purchase

Document Items

To fix this test, we must now actually aggregate the overall item prices of a purchase

document’s items. We’ll first add the purchase document item overall price field to

the view using a path expression:

_PurchaseDocumentItem.OverallItemPrice

142

第 143 页 / 共 465 页

4.1 Core Data Services Views

However, this will increase the cardinality of the data returned by the view from 1 pur-

chase document to 2 purchase documents with different overall item prices. To reduce

the cardinality, we must group the overall item prices by their common purchase doc-

ument fields and aggregate the overall item prices using a SUM aggregate function. We

assume that all purchase document items have the same currency. Listing 4.8 shows

an excerpt of the CDS view calculating the overall price of a purchase document.

@AbapCatalog.sqlViewName: 'ZIPURCHDOCPRICE'

@EndUserText.label: 'Purchase Document Item'

@VDM.viewType: #COMPOSITE

define view Z_I_PurchDocOverallPrice

as select from Z_I_PurchaseDocument

{

key PurchaseDocument,

@Semantics.amount.currencyCode: 'Currency'

@DefaultAggregation: #NONE

@ObjectModel.foreignKey.association: '_Currency'

sum( _PurchaseDocumentItem.OverallItemPrice )as OverallPrice,

@Semantics.currencyCode: true

_PurchaseDocumentItem.Currency,

…

// Associations

…

_Currency

}

group by

PurchaseDocument,

_PurchaseDocumentItem.Currency

…

Listing 4.8 Composite Interface View Calculating the Overall Price of a Purchase Document

Note

Field values calculated by a mathematical operator (+, -, /, etc.), aggregate function

(max, min, sum, etc.), or a CASE expression can’t be used in the same view for additional

calculations. In such cases, you always must create an additional view on top to

make use of the calculated value or field.

143

Note Field values calculated by a mathematical operator (+, -, /, etc.), aggregate function (max, min, sum, etc.), or a CASE expression can’t be used in the same view for additional calculations. In such cases, you always must create an additional view on top to
make use of the calculated value or field.

第 144 页 / 共 465 页

4 Developing an SAP Fiori Elements List Report and Object Page

Calculating the Overall Price Criticality by Introducing a Consumption View and

Using Test-Driven Development

Another calculated field that we’ll later need in the UI of our application is an indica-

tor for the overall price criticality. We’ll use the calculated criticality field to highlight

overall prices we consider as critical for the user checking purchase documents. The

logic to be implemented is shown in Table 4.1; for instance, if the overall price of the

document is below 1,000, we don’t consider this critical, which will be indicated by

the number 3. On the other hand, if the price is higher than 10,000, we consider this

very critical and indicate this by the number 1.

Overall Price Criticality

>= 0 and < 1,000 3 – Not critical

>= 1,000 and <= 10,000 2 – Medium criticality

> 10,000 1 – High criticality

Any other value 0 – Undefined

Table 4.1 Overall Price to Criticality Indicator Mappings

To implement this field, we’ll put a final consumption view on top of the previous

composite view, which we’ll name Z_C_PurchaseDocumentLrp where C_ stands for con-

sumption view. We’ll again do the implementation in a test-driven manner and cre-

ate a test case for each corner case before we implement the actual calculation logic

inside the CDS view. Listing 4.9 shows the unit test implementation for an overall

price of 1,000. In this case, we expect an output of 2 for the field indicating a medium

criticality.

METHOD price_1k_medium_critical.

prepare_testdata_price( 1000 ).

SELECT * FROM z_c_purchasedocumentlrp INTO TABLE @act_results.

cl_abap_unit_assert=>assert_equals( exp = 2 act = act_results[ 1 ]-

overallpricecriticality ).

ENDMETHOD.

Listing 4.9 ABAP Unit Test for the Overall Price (1,000) to Criticality (2) Mapping

The other tests will look the same except for the price parameter and the expected

value. The result of the overall price criticality field implementation in the CDS DDL

then looks like Listing 4.10.

144

Overall Price Criticality
>= 0 and < 1,000 3 – Not critical
>= 1,000 and <= 10,000 2 – Medium criticality
> 10,000 1 – High criticality
Any other value 0 – Undefined

第 145 页 / 共 465 页

4.1 Core Data Services Views

case when OverallPrice >= 0 and OverallPrice < 1000 then 3

when OverallPrice >= 1000 and OverallPrice <= 10000 then 2

when OverallPrice > 10000 then 1

else 0 end as OverallPriceCriticality,

Listing 4.10 Calculation Logic for the OverallPriceCriticality Field in the Z_C_PurchaseDocu-

mentLrp CDS Consumption View

Calculating Whether Approval Is Required or Not Using Test-Driven Development

Another simple indicator that we’ll later need in the UI indicates whether the pur-

chase document requires approval by the purchasing manager or not. We define that

a purchase document needs approval when the overall price exceeds 1,000. For this

simple indicator, we just need two tests: one where the overall price is 1,000 and

doesn’t require approval, and another where the overall price is 1,001 and does

requires approval. The implementation result looks like Listing 4.11.

case when OverallPrice > 1000 then 'X' else '' end as IsApprovalRequired,

Listing 4.11 Calculation Logic for the IsApprovalRequired Field in the Z_C_Purchase-

DocumentLrp CDS Consumption View

Figure 4.14 shows all successfully executed unit tests of the CDS view under test for

the overall price criticality field as well as for the approval required field.

Figure 4.14 Successfully Executed Unit Tests for the Overall Price Criticality and

Approval Required Fields

So far, after having added the calculated fields, the VDM of our purchasing app looks

like that shown in Figure 4.15. The database layer contains the transparent tables for

145

第 146 页 / 共 465 页

4 Developing an SAP Fiori Elements List Report and Object Page

the main entities of the application: purchase documents and items. Additionally, we

created separate tables for document priorities, status, and purchasing organizations

to store their keys and texts in a normalized and data redundancy-free way. On top of

the database tables, we introduced a basic view layer with a basic interface view for

each database table. Priorities, statuses, and purchasing organizations are linked to

the purchase document via foreign key associations. Documents and items also have

associations to the other respective entity. On top of the purchase document and

item views, we put a composite interface view to calculate the overall price for docu-

ments by aggregating the overall item prices of a document’s items. Finally, the top-

most layer consists of two consumption views for purchase documents and items. In

the consumption view for purchase documents, we introduced two more calculated

fields for the overall price criticality and whether approval of the document is

required or not.

ZPURCHDOC ZPURCH ZPURCH ZPURCH

ITEM DOCUMENT DOCPRIO DOCSTATUS

Figure 4.15 VDM of the Purchasing App Consisting of Four Different Layers

4.1.5 Adding Data Control Language Files

The access to data exposed by CDS views can be restricted using classic ABAP autho-

rization objects and the DCL of CDS. To do so, we first must create an authorization

field using Transaction SU20 and an authorization class and object using Transaction

SU21. For our app scenario, we’ll create an authorization object for the purchasing

146

noitpmusnoC

etisopmoC

cisaB

selbaT

Z_C_Purchase Z_C_Purchase

DocumentLRP DocumentItemLRP Selection Association

Z_I_PurchDoc

OverallPrice

Z_I_Purchase Z_I_Purchase Z_I_Purchase Z_I_Purchase Z_I_Purchasing

DocumentItem Document DocumentPriority DocumentStatus Organization

ZPURCHORG

etisopmoC Z_I_PurchDoc OverallPrice

第 147 页 / 共 465 页

4.1 Core Data Services Views

organization, which is also a field exposed by the purchase document CDS view. We

only want to show the end users purchase documents of the purchasing organiza-

tions they are assigned to as they aren’t supposed to get access to all purchase docu-

ments in the system by default. The authorization object for the purchasing

organization is shown in Figure 4.16. It contains two authorization fields: one for the

actual purchasing organization (ZPURCHORGA) and one for the allowed activity (ACTVT),

for instance, “01” for create, “02” for change, and “03” for display.

Figure 4.16 Overview of the Connections between Authorization Object, CDS View, and

Access Control

To enforce access restrictions based on authorization objects, we must create an

access control using the DCL of CDS and link it to the CDS view. You can create a CDS

access control in Eclipse by carrying out the following steps:

147

第 148 页 / 共 465 页

4 Developing an SAP Fiori Elements List Report and Object Page

1. Open the ABAP perspective in the ABAP development tools in Eclipse.

2. In the ABAP project, select the package node in which you want to create the table

via the Project Explorer view.

3. Open the context menu by right-clicking on the package, and select New (cid:2) Other

ABAP Repository (cid:2) Core Data Services (cid:2) Access Control.

4. When the creation wizard opens, enter a Name and Description. By convention,

the DCL source name will be identical to the DCL source name. In our case, we’ll

therefore name the DCL file Z_I_PurchaseDocument.

5. You can select one of the provided templates to create a skeleton DCL access con-

trol, for instance, Define Instance Role, which will create a simple DCL skeleton file.

6. When you click the Finish button in the wizard, the source code editor will open so

you can now define access rules and conditions.

Note

To enable the access control from the CDS view side, you also must add the @Access-

Control.authorizationCheck: #CHECK annotation to the CDS view. This will lead to

implicit access control when data is selected from the CDS view using Open SQL.

The full access control definition for our purchase document basic interface view is

shown in Listing 4.12. By convention, the name of the role inside the DCL will be iden-

tical to the CDS view name in camel case. The role definition contains a Transaction

PFCG condition that will be evaluated each time the view is directly accessed using

Open SQL. The left side of the condition consists of the PurchasingOrganization field

of the CDS view where the right side consists of the name of the authorization object

(ZPURCHORGA) as the first parameter followed by its authorization fields (ZPURCHORGA,

ACTVT) to be evaluated.

@EndUserText.label: 'Role for Z_I_PurchaseDocument'

@MappingRole: true

define role Z_I_PurchaseDocument {

grant select

on Z_I_PurchaseDocument

where

( PurchasingOrganization ) =

aspect pfcg_auth(

ZPURCHORGA, // Authorization object

148

Note To enable the access control from the CDS view side, you also must add the @Access- Control.authorizationCheck: #CHECK annotation to the CDS view. This will lead to
implicit access control when data is selected from the CDS view using Open SQL.

第 149 页 / 共 465 页

4.1 Core Data Services Views

ZPURCHORGA , // Matched auth. object field

ACTVT = '03' // Fixed auth. object field

) ;

}

Listing 4.12 Access Control Definition for the Z_I_PurchaseDocument CDS View Using the

Data Control Language

During runtime, when performing an Open SQL select on the view, the user’s

assigned Transaction PFCG roles and authorizations will be fetched and compared

with the data provided by the CDS view. In our example, the user might have been

assigned to purchasing organization Org1, which will lead to the following transpar-

ent enhancement of the CDS SELECT statement.

SELECT * FROM Z_I_PurchaseDocument WHERE PurchaseOrganization = ‘Org1’;

Additionally, our Transaction PFCG condition also contains a hard-coded check

(ACTVT=’03’), which will ensure that the user has display rights. If his assigned ACTVT

value isn’t equal to “03,” the user won’t see any data at all. Figure 4.16 summarizes the

connections between the CDS view, the DCL role, and the authorization object.

To avoid repeating the same access rules and conditions for similar views, access con-

trols can be inherited. It’s also possible to combine inherited and new access condi-

tions. If we simply would like to reuse our access control file created for the purchase

document’s basic interface view in our composite interface view, introduced for cal-

culating the overall price, we could create the following access control shown in List-

ing 4.13 without having to repeat the Transaction PFCG condition.

@EndUserText.label: 'Purchase Document Overall Price'

@MappingRole: true

define role Z_I_Purchdocoverallprice {

grant select

on Z_I_PurchDocOverallPrice

inherit Z_I_PurchaseDocument;

}

Listing 4.13 Example of an Inherited Access Control

To assign certain authorizations to users, you must create a Transaction PFCG role

and include authorization objects and their values either manually or via an applica-

tion having assigned authorization default values provided via Transaction SU22.

149

第 150 页 / 共 465 页

4 Developing an SAP Fiori Elements List Report and Object Page

Finally, for the authorizations to become effective, the user must be assigned to the

Transaction PFCG role.

Note

DCL access controls are only considered on direct CDS view access. They are ignored

when queried indirectly via other CDS views. Access controls can be tested using

Transaction SACMSEL, which makes it possible to evaluate CDS access controls for

different users.

4.2 Transactional Processing with the Business Object

Processing Framework

So far, our app consists of a VDM based on a CDS view that provides access to pur-

chase documents and items using Open SQL, the query language of CDS. However, it

isn’t yet possible to create any documents or items. As of now, the application is com-

pletely read-only. If any transactional logic, for instance, creating, deleting, or updat-

ing (CUD) entities, is required, this is where the BOPF comes into play as, since SAP

NetWeaver 7.52, SP 02, it’s possible to generate a BOPF business object and its node

structure from a CDS data model using CDS business object model annotations

(@ObjectModel). Consequently, you’ll learn in this section how you can add transac-

tional capabilities to a read-only CDS-based VDM.

4.2.1 Generating a BOPF Business Object from a CDS Model

Because we want to keep our basic interface views reusable for all application scenar-

ios, we won’t add the semantic business object annotations required for generating

the BOPF business object to existing basic interface views; instead, we’ll create new

composite interface views on top of the basic interface views. We’ll create a transac-

tional view for purchase documents as well as for purchase document items, which

we’ll name Z_I_PurchaseDocumentTP and Z_I_PurchaseDocumentItemTP, where TP stands

for transactional processing. In both views, we add all fields of the underlying view to

the selection field list to do a simple projection of the underlying view.

To generate a BOPF business object based on the two transactional or business object

CDS views, we must add several @ObjectModel annotations to the views. In the pur-

chase document root view (Z_I_PurchaseDocumentTP), we must add the annotations

shown in Listing 4.14 on the header level.

150

Note DCL access controls are only considered on direct CDS view access. They are ignored when queried indirectly via other CDS views. Access controls can be tested using Transaction SACMSEL, which makes it possible to evaluate CDS access controls for
different users.

第 151 页 / 共 465 页

4.2 Transactional Processing with the Business Object Processing Framework

@ObjectModel.modelCategory: #BUSINESS_OBJECT

@ObjectModel.compositionRoot: true

@ObjectModel.transactionalProcessingEnabled: true

@ObjectModel.writeActivePersistence: 'ZPURCHDOCUMENT'

@ObjectModel.createEnabled: true

@ObjectModel.deleteEnabled: true

@ObjectModel.updateEnabled: true

define view Z_I_PurchaseDocumentTP

…

Listing 4.14 BOPF Business Object Root Node Header Annotations

The @ObjectModel.modelCategory: #BUSINESS_OBJECT annotation indicates that this

view represents a BOPF business object. The annotation doesn’t have any runtime

effect but helps in categorizing the view as a BOPF view, for instance, in view browsers.

A business object is represented as a hierarchical tree of nodes, and by adding the

@ObjectModel.compositionRoot: true annotation, a view is characterized as the root

view of the node hierarchy. To enable the transactional runtime, the annotation

@ObjectModel.transactionalProcessingEnabled: true must be added together with

the @ObjectModel.writeActivePersistence: 'ZPURCHDOCUMENT' annotation, which spec-

ifies where created or updated data must be stored or deleted from. In our case, we’re

directly modifying the data without creating any intermediary draft entities. The

@ObjectModel.transactionalProcessingEnabled: true can only be added to the root

view of the BOPF node hierarchy. With the @ObjectModel.createEnabled, @Object-

Model.deleteEnabled, and @ObjectModel.updateEnabled annotations, we specify which

operations will be supported by the transactional runtime. Because we set everything

to true, all operations are supported by our purchase documents root node. Addition-

ally, to build up the business object node hierarchy, we must also annotate the asso-

ciation to the purchase document items view as a child association (Listing 4.15).

/* Associations */

@ObjectModel.association.type: #TO_COMPOSITION_CHILD

_PurchaseDocumentItem,

Listing 4.15 BOPF Business Object Root Node Child Association

Furthermore, we must add several annotations on the header level in the purchase

document item view (Z_I_PurchaseDocumentItemTP) (Listing 4.16).

151

第 152 页 / 共 465 页

4 Developing an SAP Fiori Elements List Report and Object Page

@ObjectModel.writeActivePersistence: 'ZPURCHDOCITEM'

@ObjectModel.createEnabled: true

@ObjectModel.updateEnabled: true

@ObjectModel.deleteEnabled: true

define view Z_I_PurchaseDocumentItemTP

…

Listing 4.16 BOPF Business Object Child Node Header Annotations

Because purchase document items are stored in a different database table than purchase

documents, we must also provide the active persistence of items using the @Object-

Model.writeActivePersistence annotation. As we did for purchase documents, we also

enable all transactional operations for this business object node. The association back

to the purchase document must be annotated as a parent association. Moreover, views

that don’t represent the root node of the hierarchy must also specify their root node

association, which, in our case, is equivalent to the parent association (Listing 4.17).

/* Associations */

@ObjectModel.association.type: [ #TO_COMPOSITION_PARENT, #TO_COMPOSITION_

ROOT ]

_PurchaseDocument,

Listing 4.17 BOPF Business Object Child Node Parent and Root Association

Note

The field names of the business object CDS view and the field names of the underly-

ing database table must match (except for camel case). If the table is a legacy table

containing cryptic field names, you can hide those behind a classic database view

placed between the database table and the CDS view. You can find more information

on how to do this in the “Mapping CDS View-Elements onto Table Fields” guide at

http://bit.ly/2IdnOMo.

On activation of the transactional or business object views, the BOPF runtime will

generate a business object together with several structures and table types based on

the CDS view field structures. The generated BOPF object has the same name as the

root CDS view, in our case, Z_I_PurchaseDocumentTP. As you can see in Figure 4.17, the

node structure of the BOPF object exactly reflects our annotated CDS data model

hierarchy with the purchase document node being the root node and the purchase

document item node being the child node.

152

Note The field names of the business object CDS view and the field names of the underly- ing database table must match (except for camel case). If the table is a legacy table containing cryptic field names, you can hide those behind a classic database view placed between the database table and the CDS view. You can find more information on how to do this in the “Mapping CDS View-Elements onto Table Fields” guide at
http://bit.ly/2IdnOMo.

第 153 页 / 共 465 页

4.2 Transactional Processing with the Business Object Processing Framework

Figure 4.17 Generated BOPF Node Structure

To incorporate the new business object views into our VDM, we’ll make our existing

consumption views to select from the new business object views instead of the previ-

ous Interface views. The final VDM of our application looks like Figure 4.18. With the

new transactional business object views, we’ve introduced a new layer of composite

views below the consumption view layer.

ZPURCHDOC ZPURCH ZPURCHDOC ZPURCHDOC

ITEM DOCUMENT PRIO STATUS

Figure 4.18 Final VDM of the Application

To enable our existing consumption views for transactional processing via the gener-

ated BOPF business object, we also must add some of the @ObjectModel annotations

153

noitpmusnoC

etisopmoC

cisaB

selbaT

Selection Association

Z_C_Purchase Z_C_Purchase

DocumentLRP DocumentItemLRP

Z_I_Purchase Z_I_Purchase

DocumentTP DocumentItemTP

Z_I_PurchDoc

OverallPrice

Z_I_Purchase Z_I_Purchase Z_I_Purchase Z_I_Purchase Z_I_Purchasing

DocumentItem Document DocumentPriority DocumentStatus Organization

ZPURCHORG

cisaB Z_I_Purchase Z_I_Purchase Z_I_Purchase Z_I_Purchase Z_I_Purchasing DocumentItem Document DocumentPriority DocumentStatus Organization
Z_I_Purchasing Organization

第 154 页 / 共 465 页

4 Developing an SAP Fiori Elements List Report and Object Page

on the header level. To the purchase document consumption view (Z_C_PurchaseDoc-

umentLRP), we must add the annotations given in Listing 4.18.

…

@ObjectModel.transactionalProcessingDelegated: true

@ObjectModel.compositionRoot: true

@ObjectModel.createEnabled: true

@ObjectModel.updateEnabled: true

@ObjectModel.deleteEnabled: true

…

define view Z_C_PurchaseDocumentLrp

…

Listing 4.18 Purchase Document Consumption View Header Annotations to Delegate the

Transactional Processing to the Underlying BOPF Business Object Node

We must also mark the view as the root view of our hierarchy node structure by intro-

ducing @ObjectModel.compositionRoot: true. Additionally, we must enable all trans-

actional operations separately because they are already known from the interface

views. The only new annotation is the @ObjectModel.transactionalProcessingDele-

gated: true, which indicates that transactional processing is delegated to the trans-

actional runtime of the underlying view. In our case, this is the Z_I_PurchaseDocu-

mentTP business object and view. Moreover, we must also specify the same association

type annotations as in the underlying views, from document to item and back.

Additionally, we have to add annotations to the purchase document item consump-

tion view (Z_C_PurchaseDocumentItemLRP) to enable transactional processing delega-

tion (Listing 4.19).

…

@ObjectModel.transactionalProcessingDelegated: true

@ObjectModel.createEnabled: true

@ObjectModel.updateEnabled: true

@ObjectModel.deleteEnabled: true

…

define view Z_C_PurchaseDocumentItemLrp

…

Listing 4.19 Purchase Document Item Consumption View Header Annotations to Delegate

the Transactional Processing to the Underlying BOPF Business Object Node

154

第 155 页 / 共 465 页

4.2 Transactional Processing with the Business Object Processing Framework

Compared to the purchase document consumption view, we must omit the @Object-

Model.compositionRoot annotation but also set transactional processing as delegated

to the underlying business object while enabling transactional operations for CUD

separately.

4.2.2 Testing Business Object Nodes

To test the create, read, update, and delete (CRUD) operations provided by the trans-

actional runtime, we can make use of the BOPF test shell. The BOPF test shell can be

opened in your ABAP development tools in Eclipse by simply opening the context

menu of the previously generated business object and choosing Runs As (cid:2) Test Envi-

ronment (see Figure 4.19).

Figure 4.19 Opening the BOPF Test Shell via the Previously Generated Business Object

The BOPF test shell opens in Eclipse as an embedded SAP GUI window. To create a

new node instance, you can simply select the node in the Node Instances navigation

pane on the right side and click the Add Node Instance button in the toolbar of the

editor pane on the right side, as shown in Figure 4.20. To save a new node instance,

click the Save button in the main toolbar of the window.

155

第 156 页 / 共 465 页

4 Developing an SAP Fiori Elements List Report and Object Page

Figure 4.20 Creating New Node Instances Using the BOPF Test Shell

As you can see, the transactional runtime supports standard CUD operations out of

the box without the developer having to explicitly code those. To refine the behavior

of those operations, you can add BOPF validations or determinations to the corre-

sponding node in the hierarchy.

4.2.3 Creating a Quick Action Using Business Object Processing Framework

As you’ll remember, one of the requirements of the purchasing application was that

the purchasing manager can approve purchase documents if they require approval. To

enable the purchasing manager to quickly approve a purchase document, we’ll add a

custom BOPF action to the purchase document business object node. You can create

an action for an existing business object node by carrying out the following steps:

1. Open the business object (Z_I_PurchaseDocumentTP), for example, by using

(Ctrl)+(Shift)+(A).

2. In the business object editor, click on the Go to the Root Node link.

Figure 4.21 Create a New Action for a Node in the Business Object Node Hierarchy

156

第 157 页 / 共 465 页

4.2 Transactional Processing with the Business Object Processing Framework

3. Select the Actions tab, and choose New (Figure 4.21).

4. A dialog opens where you must specify the details of the action (Figure 4.22):

– Name: You must enter the name of the action; in our case, we enter “APPROVE”.

We’ll later enter an additional “CLOSE” action to close a purchase document.

– Description: Enter a short description that describes the purpose of the action,

for instance, “Approve Purchase Documents”.

– Implementation Class: You can simply accept the suggestion that will lead to

the generation of a global ABAP class or enter an already existing class that

implements the interface /bobf/if_frw_action~execute. We’ll call the class ZCL_

A_SET_STATUS to be able to reuse it for all status actions as we also want to imple-

ment a CLOSE status action for closing a purchase document.

– Instance Multiplicity: Select multiple node instances here as we want the action

to operate on several purchase documents to enable mass processing of

approvals.

– Parameter Structure: We leave this field empty because we’re not going to pro-

vide any parameters to the action in our scenario.

Figure 4.22 Dialog for Creating a BOPF Action

5. Click the Finish button of the dialog.

157

第 158 页 / 共 465 页

4 Developing an SAP Fiori Elements List Report and Object Page

6. To specify further details, you must click on the action name while pressing (Ctrl)

(Figure 4.23).

Figure 4.23 Opening the Action Overview Editor by Clicking on the Action Name

7. In the Action Overview editor of the action, you can set the exporting parameter

and multiplicity of the action (Figure 4.24).

Figure 4.24 Set the Exporting Parameters of the Action

158

第 159 页 / 共 465 页

4.2 Transactional Processing with the Business Object Processing Framework

8. Finally, save ((Ctrl)+(S)) and activate ((Ctrl)+(F3)) the business object, which will

also lead to the creation of the action implementation class if it doesn’t exist yet.

To implement the action, you must navigate to the global ABAP action implementa-

tion class by clicking on the action class name in the Actions editor while pressing the

(Ctrl) key. Listing 4.20 shows the ABAP implementation of the approval action. We

first read the purchase documents to be approved using the io_read parameter

object’s retrieve method. Subsequently, we loop over the retrieved documents and

change their status to approved (2). Finally, to update the changed approval status on

the database, we must write the changes back using the io_modify parameter object’s

update method.

METHOD /bobf/if_frw_action~execute.

DATA(lt_purchase_document) = VALUE ztipurchasedocumenttp( ).

" Retrieve purchase document node instances to be approved

io_read->retrieve(

EXPORTING

iv_node = is_ctx-node_key

it_key = it_key

IMPORTING

et_data = lt_purchase_document

).

LOOP AT lt_purchase_document ASSIGNING FIELD-SYMBOL(<fs_purchase_document>).

<fs_purchase_document>-status = '2'. " Set status to Approved

" Update the node instance with the changed status

io_modify->update(

EXPORTING

iv_node = is_ctx-node_key

iv_key = <fs_purchase_document>-key

iv_root_key = <fs_purchase_document>-root_key

is_data = REF #( <fs_purchase_document> )

it_changed_fields = VALUE #(

( zif_i_purchasedocumenttp_c=>sc_node_attribute-z_i_purchasedocumenttp-

status )

)

).

ENDLOOP.

ENDMETHOD.

Listing 4.20 Simple Implementation of the Approval Action for Purchase Documents

159

第 160 页 / 共 465 页

4 Developing an SAP Fiori Elements List Report and Object Page

4.3 Virtual Elements in Core Data Services

In Section 4.1.4, we learned that the expressive CDS data DDL enables us to push down

calculations to SAP HANA, which reduces calculations on the ABAP server and fosters

code pushdown and the code to data paradigm to exploit the SAP HANA database

and its capabilities. However, in some cases, it might still be necessary to call some

ABAP coding to calculate values that aren’t part of a persistent model, for instance,

legacy ABAP logic that can’t be migrated to CDS views that easily. In such cases, vir-

tual elements come into play that are defined on the CDS consumption view level but

are calculated using ABAP code exits.

4.3.1 Adding a Virtual Element to a Core Data Services View

The annotations required for defining a CDS field as a virtual element are shown in

Listing 4.21. As you can see, on the CDS level, the virtual element field is simply an

empty field. However, SADL will parse the annotations and, when the CDS view is

queried via its corresponding OData entity, delegate the data calculation to an ABAP

exit class (@ObjectModel.virtualElementCalculatedBy).

If it’s also possible to filter and sort the OData entity’s provided data by the virtual

element field, the filter (@ObjectModel.filter.transformedBy) and sort (@Object-

Model.sort.transformedBy) annotations and exits must be implemented.

@ObjectModel.readOnly: true

@ObjectModel.virtualElement

@ObjectModel.virtualElementCalculatedBy: 'ABAP:<ABAP_class>'

@ObjectModel.filter.transformedBy: 'ABAP:<ABAP_class>'

@ObjectModel.sort.transformedBy: 'ABAP:<ABAP_class>'

cast( '' as <data_type> preserving type) as <field_name>

Listing 4.21 Declare a Field as n Virtual Element in the CDS Field Selection List

4.3.2 Implementing an ABAP Code Exit to Populate the Virtual Element

To calculate, sort, and filter virtual elements, the respective ABAP exit classes must

implement the interfaces and methods listed in Table 4.2.

For the virtual element calculation, the GET_CALCULATION_INFO method is called before

the actual database SELECT is performed. The method is supposed to return all fields

that are required for the virtual element field to be calculated. SADL will then make

160

第 161 页 / 共 465 页

4.4 Creating an OData Service

sure that those fields are also selected from the database using its generic query

engine. In the CALCULATE method, which is called after the database SELECT, the calcu-

lation of the virtual element field value must be implemented and can make use of

the before specified and selected fields. The respective mapping methods of the filter

(MAP_ATOM) and sort (MAP_ELEMENT) exits are called before the actual database SELECT

takes place. Sort or filter conditions defined for the virtual element must in this case

be mapped onto other fields that are available in the persistent database table and

can be included in the database SELECT statement the SADL query engine generates.

Interface Method

Calculate IF_SADL_EXIT_CALC_ELEMENT_READ

Filter IF_SADL_EXIT_FILTER_TRANSFORM

Sort IF_SADL_EXIT_SORT_TRANSFORM

Table 4.2 Interfaces to Be Implemented for the Different Virtual Element Exits

4.4 Creating an OData Service

In this step, we’ll expose the previously developed CDS and BOPF model as an OData

service for later consumption by our SAP Fiori elements list report and object page UI.

4.4.1 Auto-Exposing the OData Service

Because our VDM is a coherent model, and all entities relevant for the application are

linked by associations, we can simply expose the CDS model using OData Auto-Expo-

sure. Therefore, we’ll add the @OData.publish: true annotation to the header section

of the purchase document consumption view:

…

@OData.publish: true

…

define view Z_C_PurchaseDocumentLrp

On activating the view, SADL will generate and store several artifacts on the ABAP

backend system that are required for later exposing the OData service on the SAP

Gateway hub or frontend system.

161

Interface Method
Calculate IF_SADL_EXIT_CALC_ELEMENT_READ
Filter IF_SADL_EXIT_FILTER_TRANSFORM
Sort IF_SADL_EXIT_SORT_TRANSFORM
返回ABAP参考资料列表