Technology Blogs by Members
Explore a vibrant mix of technical expertise, industry insights, and tech buzz in member blogs covering SAP products, technology, and events. Get in the mix!
cancel
Showing results for 
Search instead for 
Did you mean: 
mertuytun
Participant

Introduction


In this tutorial, we will build a Fiori Elements application to perform CRUD operations with the BOPF Framework using CDS annotations, without writing a single line of UI5 code.

The Fiori application that we will build today will be focused on visitor management. We will create a database table called "VISITOR" to store visitor information. The table will include fields such as Visit ID, First Name, Last Name, Company Name, Telephone, E-Mail, Reason, Host Information, Visit Date, Check-In Date, Check-Out Date, and Status.

Pre-requisites:



  • S/4 HANA System

  • ECLIPSE with ABAP Development Tools

  • VSCode with SAP Fiori Tools - Extension Pack


Steps:



  • Step 1 - Creating Table

  • Step 2 - Creating CDS views with BO Enabled

    • Step 2.1 - Basic CDS View

    • Step 2.2 - Projection CDS View

    • Step 2.3 - CDS Views for dropdown domain values



  • Step 3 - Testing the BO that have been created by CDS

  • Step 4 - Registering the oData and testing in gateway client

  • Step 5 - Creating Fiori Elements App

    • Step 5.1 - Creating Fiori Elements App

    • Step 5.2 - Previewing Fiori Elements App



  • Step 6 - Adding a custom action to the BO

    • Step 6.1 - Creating New Action to the BO

    • Step 6.2 - Add action to Fiori App as Button by using CDS Annotations

    • Step 6.3 - Previewing and Testing Button





Step 1 - Creating Table


There are a few fields in the table that will be created which contains fixed values so first step we need to do is creating data elements and domains for this fields.



Now, let’s create simple table to store visitor records.


 





































































































































VISITOR TABLE KEY Data Element Domain Fixed Value
MANDT X MANDT
GUID GUID X GUID
Visit ID VISITID VISITID
First Name FNAME FNAME
Last Name LNAME LNAME
Company Name CMPNY CMPNY
Telephone Country Code TELCODE TELCODE
Telephone TELNUM TELNUM
E-Mail EMAIL EMAIL
Reason REASON REASON ZXXX_DO_VIS_REASON Meeting, Interview, Maintance, Other
Host Information VHOST VHOST
Visit Date VDATE VDATE
Check-In Date INTIME INTIME
Check-Out Date OUTTIME OUTTIME
Status STATUS STATUS ZXXX_DO_VIS_STATUS Done, Postponed, Canceled, In Progress

 


Step 2 - Creating CDS views with BO Enabled


We need to create two CDS views which called Basic and Projection Views.

Basic View: interacts with database for fetching data

Projection View: created on top of basic views and contains Annotations

Using the steps shown below, create two CDS Views as follows:







Step 2.1 - Basic CDS View


@AbapCatalog.sqlViewName: 'ZXXXCDSVIS01'
@AbapCatalog.compiler.compareFilter: true
@AbapCatalog.preserveKey: true
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Visitor CDS'
define view ZXXX_CDS_VIS_01
as select from zxxx_visitor2
{
key guid as Guid,
visitid as Visitid,
fname as Fname,
lname as Lname,
cmpny as Cmpny,
telcode as Telcode,
telnum as Telnum,
email as Email,
reason as Reason,
vhost as Vhost,
vdate as Vdate,
intime as Intime,
outtime as Outtime,
status as Status

}

Step 2.2 - Projection CDS View


@AbapCatalog.sqlViewName: 'ZXXXCDSVIS02'
@AbapCatalog.compiler.compareFilter: true
@AbapCatalog.preserveKey: true
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Visitor CDS View'

@ObjectModel:{
modelCategory: #BUSINESS_OBJECT,
compositionRoot: true,
representativeKey: 'Guid',
semanticKey: ['Visitid'],
transactionalProcessingEnabled: true,
writeActivePersistence: 'ZXXX_VISITOR2',
// enable crud
createEnabled: true,
updateEnabled: true,
deleteEnabled: true
}

@UI.headerInfo: {typeName: 'Visitor', typeNamePlural: 'Visitors', title: {value: 'Visitid'}}

@UI: {
presentationVariant: [{
sortOrder: [{
by: 'Visitid',
direction: #ASC
}],
visualizations: [{
type: #AS_LINEITEM
}]
}]
}


@OData.publish: true

define view ZXXX_CDS_VIS_02
as select from ZXXX_CDS_VIS_01
association [0..1] to ZXXX_CDS_VIS_REASON_DOM as _ReasonValueHelp on $projection.Reason = _ReasonValueHelp.Low
association [0..1] to ZXXX_CDS_VIS_STATUS_DOM as _StatusValueHelp on $projection.Status = _StatusValueHelp.Low
{
@UI.facet: [
// Section General Information
{
label: 'General Information',
position: 10,
type: #COLLECTION,
id: 'GeneralInfo'
},
{
label: 'General',
purpose: #STANDARD,
position: 10,
type: #IDENTIFICATION_REFERENCE,
parentId: 'GeneralInfo'
}
]



@UI.hidden: true
key Guid,

@EndUserText.label: 'Visit ID'
@UI: {
lineItem: [{ position: 10}],
identification: [{position: 10}],
fieldGroup: [{qualifier: 'Basic'}]
}
@ObjectModel.mandatory: true
@ObjectModel.readOnly: true
Visitid,

@EndUserText.label: 'First Name'
@Consumption.valueHelpDefinition: [ {
entity: {
name: 'ZXXX_CDS_VIS_02',
element: 'Fname'
}
} ]
@UI: {
lineItem: [{ position: 20}],
identification: [{position: 20}],
fieldGroup: [{qualifier: 'Basic'}]
}
@ObjectModel.mandatory: true
Fname,

@EndUserText.label: 'Last Name'
@Consumption.valueHelpDefinition: [ {
entity: {
name: 'ZXXX_CDS_VIS_02',
element: 'Lname'
}
} ]
@UI: {
lineItem: [{ position: 30}],
identification: [{position: 30}],
fieldGroup: [{qualifier: 'Basic'}]
}
@UI.selectionField: [{ position: 11 }]
@ObjectModel.mandatory: true
Lname,

@EndUserText.label: 'Company'
@Consumption.valueHelpDefinition: [ {
entity: {
name: 'ZXXX_CDS_VIS_02',
element: 'Cmpny'
}
} ]
@UI: {
lineItem: [{ position: 40}],
identification: [{position: 40}],
fieldGroup: [{qualifier: 'Basic'}]
}
@UI.selectionField: [{ position: 12 }]
@ObjectModel.mandatory: true
Cmpny,

@EndUserText.label: 'Tel.No. Country Code'
@UI.hidden: true
@UI: {
lineItem: [{ position: 50}],
identification: [{position: 50}],
fieldGroup: [{qualifier: 'Basic'}]
}
@ObjectModel.mandatory: true
Telcode,

@EndUserText.label: 'Tel.No.'
@UI: {
lineItem: [{ position: 60}],
identification: [{position: 60}],
fieldGroup: [{qualifier: 'Basic'}]
}
@ObjectModel.mandatory: true
Telnum,

@EndUserText.label: 'E-Mail'
@UI: {
lineItem: [{ position: 70}],
identification: [{position: 70}],
fieldGroup: [{qualifier: 'Basic'}]
}
@ObjectModel.mandatory: true
Email,

@EndUserText.label: 'Reason'
@Consumption.valueHelpDefinition: [ {
entity: {
name: 'ZXXX_CDS_VIS_REASON_DOM',
element: 'Low'
}
} ]
@ObjectModel.text.element: ['ReasonText']
@UI: {
lineItem: [{ position: 80}],
identification: [{position: 80}],
fieldGroup: [{qualifier: 'Basic'}]
}
@ObjectModel.mandatory: true
@UI.textArrangement: #TEXT_ONLY
@UI.selectionField: [{ position: 20 }]
Reason,
_ReasonValueHelp.Text as ReasonText,

@EndUserText.label: 'Host Information'
@UI: {
lineItem: [{ position: 90}],
identification: [{position: 90}],
fieldGroup: [{qualifier: 'Basic'}]
}
@ObjectModel.mandatory: true
Vhost,

@EndUserText.label: 'Date'
@UI: {
lineItem: [{ position: 100}],
identification: [{position: 100}],
fieldGroup: [{qualifier: 'Basic'}]
}
@ObjectModel.mandatory: true
Vdate,

@EndUserText.label: 'Check-In Time'
@UI: {
lineItem: [{ position: 110}],
identification: [{position: 110}],
fieldGroup: [{qualifier: 'Basic'}]
}
@ObjectModel.mandatory: true
Intime,

@EndUserText.label: 'Check-Out Time'
@UI: {
lineItem: [{ position: 120}],
identification: [{position: 120}],
fieldGroup: [{qualifier: 'Basic'}]
}
@ObjectModel.mandatory: true
Outtime,

@EndUserText.label: 'Status'
@Consumption.valueHelpDefinition: [ {
entity: {
name: 'ZXXX_CDS_VIS_STATUS_DOM',
element: 'Low'
}
} ]
@ObjectModel.text.element: ['StatusText']
@UI: {
lineItem: [{ position: 81}],
identification: [{position: 81}],
fieldGroup: [{qualifier: 'Basic'}]
}
@ObjectModel.mandatory: true
@UI.textArrangement: #TEXT_ONLY
@UI.selectionField: [{ position: 30 }]
Status,
}

Step 2.3 - CDS views for dropdown domain values


base cds for fetching fixed values of any domain
@AbapCatalog.sqlViewName: 'ZXXXCDSDOMVAL'
@AbapCatalog.compiler.compareFilter: true
@AbapCatalog.preserveKey: true
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Domain Value Fetch'
define view ZXXX_CDS_DOMVAL
as select from dd07l as FixedValue
left outer join dd07t as ValueText on FixedValue.domname = ValueText.domname
and FixedValue.domvalue_l = ValueText.domvalue_l
and FixedValue.as4local = ValueText.as4local

{
@UI.hidden
key FixedValue.domname as DomainName,
@UI.hidden
key FixedValue.as4local as Status,
@Search.defaultSearchElement: true
@Search.fuzzinessThreshold: 0.8
key FixedValue.domvalue_l as Low,
@Semantics.text: true -- identifies the text field
ValueText.ddtext as Text
}

where
FixedValue.as4local = 'A' --Active
and ValueText.ddlanguage = $session.system_language

projection view for fetching fixed values of our domains
@AbapCatalog.sqlViewName: 'ZXXXVISRSNDOM'
@AbapCatalog.compiler.compareFilter: true
@AbapCatalog.preserveKey: true
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Reason Domain Values'
@Search.searchable: true
@ObjectModel.resultSet.sizeCategory: #XS
define view ZXXX_CDS_VIS_REASON_DOM
as select from ZXXX_CDS_DOMVAL as DomValues
{
@UI.hidden
key DomValues.DomainName,
@EndUserText.label: 'Status' -- Custom label text
@UI.textArrangement: #TEXT_ONLY
@ObjectModel.text.element: [ 'Text' ]
key DomValues.Low,
@Semantics.text: true -- identifies the text field
@Search.defaultSearchElement: true
@Search.fuzzinessThreshold: 0.8
@UI.hidden: true
DomValues.Text
}

where
DomValues.DomainName = 'ZXXX_DO_VIS_REASON'

Step 3 - Testing the BO that have been created by CDS





Step 4 - Registering the oData and testing in gateway client


Register the service in the backend on Tcode - /n/IWFND/MAINT_SERVICE



Select the Gateway client to test the Odata Service.




Step 5 - Creating Fiori Elements App


It’s time to create Fiori Elements App by using VSCode.

Step 5.1 - Creating Fiori Elements App



Select Application type and template


Select Data source and Service


Select Main Entity


Provide necessary details



Step 5.2 - Previewing Fiori Elements App


App has been created so We can check CRUD operations of our app.




Step 6 - Adding a custom action to the BO


Step 6.1 - Creating New Action to the BO


Open BOBX Tcode and BO


Create New Action


Enter action name and description then create Implementing Class by double clicking after entering Implementing Class Name


Open execute method of Implementing Class


Purpose of this action is setting status done of selected rows. According to this purpose, firstly we need to read the data we want to change, then update the status with call the UPDATE method of io_modify.



DATA:
lt_head TYPE ztxxx_cds_vis_02,
lv_nextnum TYPE numc10.
io_read->retrieve(
EXPORTING
iv_node = is_ctx-node_key
it_key = it_key
iv_fill_data = abap_true
IMPORTING
et_data = lt_head
).

LOOP AT lt_head REFERENCE INTO DATA(lr_head).

IF lr_head->status NE 'D'.
lr_head->status = 'D'.

io_modify->update(
EXPORTING
iv_node = is_ctx-node_key
iv_key = lr_head->key
iv_root_key = lr_head->root_key
is_data = lr_head
).
ENDIF.
ENDLOOP.

Step 6.2 - Add action to Fiori App as Button by using CDS Annotations


Open Eclipse and Add below code block to our Projection CDS View.
 @UI: {
lineItem: [{ type: #FOR_ACTION, position: 1,
dataAction: 'BOPF:SET_STATUS_DONE',
label: 'Set Status: Done' }],
identification: [{type: #FOR_ACTION,
position: 1,
dataAction: 'BOPF:SET_STATUS_DONE',
label: 'Set Status: Done' }]
}



Step 6.3 - Previewing and Testing Button





Conclusion


Creating a Fiori Elements app for using CDS annotations and the BOPF Framework is a powerful way to build apps without writing extensive UI5 code. If you're interested in exploring more about this let's chat in the comments! Also, diving deeper into modern ABAP development, consider checking out RAP (ABAP RESTful Application Programming) for further insights into streamlined development approaches.








1 Comment
Andre_Fischer
Product and Topic Expert
Product and Topic Expert
If developers want to explore modern ABAP Development they should choose the ABAP RESTful application programming model rather than investing into the BOPF based approach described in this blog post.

In SAP S/4HANA systems as of 2020 FPS00 the above described approach should not be used anymore for new developments. since in these releases the ABAP RESTful application programming (RAP) can be used instead.

RAP supports draft as of version 2020 FPS00 and it even allows to build applications that in addition support OData V4 as of SAP S/4HANA 2020 FPS1.

Version 2020 FPS00 | SAP Help Portal

Version 2020 FPS01 | SAP Help Portal

Kind regards,

Andre

 

 
Labels in this area