I am writing this blog after a long time. The Idea to write this blog is because the challenges I face with getting all the required details at one place while creating a List Report Application based on Fiori Elements.
When I was developing a List Report Application I have to refer lot many blogs and tutorial, that’s why I thought to sum them all at one place so that it can save time for others
As I am trying to club all topics at one place with more importance to complex things (which are usually not covered together) I will not be explaining the basic things here to make this blog crisp (But dont worry if you are beginner I will share the links of other blogs to you to brush the basic concept).
◉ Knowledge with ABAP
◉ Basic understanding to CDS View (https://sapabapcentral.blogspot.com/2018/01/spotlight-on-abap-for-sap-hana-again.html)
As the content I am sharing here is too big I will divide it into 3 parts.
◉ This will be the 1st part where we will be creating the CDS views.
◉ In the next part I will explain you the BOPF for Determination, Validation and Action
◉ In the final part I will show you how you can create the App on this CDS view using WebIDE
◉ We will be creating a List Report Application based on Fiori Elements/CDS View
◉ The Application can handle the CRUD Operation
◉ We will also add Determination, Action and Validation to it using BOPF
◉ To make sure everyone can try it I used the SPFLI and SFLIGHT Table data
Ok Before starting lets see how the end result will look.
1. In the main screen, we have list with data from header table.
2. We have given a filter bar to filter out the data with a Default Search bar too
3. We have enabled Create, Update and Delete
4. We have even added an Action using BOPF
When I was developing a List Report Application I have to refer lot many blogs and tutorial, that’s why I thought to sum them all at one place so that it can save time for others
As I am trying to club all topics at one place with more importance to complex things (which are usually not covered together) I will not be explaining the basic things here to make this blog crisp (But dont worry if you are beginner I will share the links of other blogs to you to brush the basic concept).
Prerequisite:
◉ Knowledge with ABAP
◉ Basic understanding to CDS View (https://sapabapcentral.blogspot.com/2018/01/spotlight-on-abap-for-sap-hana-again.html)
As the content I am sharing here is too big I will divide it into 3 parts.
◉ This will be the 1st part where we will be creating the CDS views.
◉ In the next part I will explain you the BOPF for Determination, Validation and Action
◉ In the final part I will show you how you can create the App on this CDS view using WebIDE
What you will get from this blog:
◉ We will be creating a List Report Application based on Fiori Elements/CDS View
◉ The Application can handle the CRUD Operation
◉ We will also add Determination, Action and Validation to it using BOPF
◉ To make sure everyone can try it I used the SPFLI and SFLIGHT Table data
Ok Before starting lets see how the end result will look.
1. In the main screen, we have list with data from header table.
2. We have given a filter bar to filter out the data with a Default Search bar too
3. We have enabled Create, Update and Delete
4. We have even added an Action using BOPF
Initial List Screen
In the next Detail Screen:
1. We can see we have divided the Page into 3 Sections:
2. We have an Item table displayed as a list in the second section under Flight Details(We can Navigate to it’s detail page too)
Detail Page of Header Entry
Detail Page of Item Entry
So lets start with the objects:
For this Application we have 5 CDS View and 2 MetaData Extensions
◉ 1 Projection\Transactional View, 1 Cosumption and 1 MetaData extension each for SPFLI and SFLIGHT Table
◉ 1 CDS View for F4 Help
◉ We have one root node for BOPF, which will have 3 classes for- Determination, Action and Validation
We will continue with creating first the basic transaction views of CDS. I have added comments before every code line to help you understand the use of that line:
@AbapCatalog.sqlViewName: 'YIDEMO_SPFLI'
@AbapCatalog.compiler.compareFilter: true
@AbapCatalog.preserveKey: true
@AccessControl.authorizationCheck: #NOT_REQUIRED // To enable Auth Check
@EndUserText.label: 'Flight Schedule' //Description for the CDS View
//Transactional CDS View on SPFLI
//To Create a Business Object from the view
@ObjectModel:{semanticKey: 'carrid',
modelCategory: #BUSINESS_OBJECT,
compositionRoot: true,
transactionalProcessingEnabled: true,
writeActivePersistence: 'SPFLI', //DB Table
//Enable the CRUD Activity
createEnabled: true,
deleteEnabled: true,
updateEnabled: true}
define view ZIDEMO_DDL_SPFLI
as select from spfli
{
//spfli Table fields
key carrid,
key connid,
countryfr,
cityfrom,
airpfrom,
countryto,
cityto,
airpto,
fltime,
deptime,
arrtime,
distance,
distid,
fltype,
period
}
Now similarly create a Transaction CDS view for SFLIGHT Item Table
@AbapCatalog.sqlViewName: 'YIDEMO_SFLIGHT'
@AbapCatalog.compiler.compareFilter: true
@AbapCatalog.preserveKey: true
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Transactional CDS for SFLIGHT'
@ObjectModel.semanticKey: 'carrid'
@ObjectModel.modelCategory: #BUSINESS_OBJECT
@ObjectModel.compositionRoot: true
@ObjectModel.transactionalProcessingEnabled: true
@ObjectModel.writeActivePersistence: 'SFLIGHT' //DB Table
@ObjectModel.createEnabled: true
@ObjectModel.deleteEnabled: true
@ObjectModel.updateEnabled: true
define view ZIDEMO_DDL_SFLIGHT as select from sflight {
//sflight
key carrid,
key connid,
key fldate,
price,
currency,
planetype,
seatsmax,
seatsocc,
paymentsum,
seatsmax_b,
seatsocc_b,
seatsmax_f,
seatsocc_f
}
Create a CDS View for Value Help:
@AbapCatalog.sqlViewName: 'YDEMO_SCURX'
@AbapCatalog.compiler.compareFilter: true
@AbapCatalog.preserveKey: true
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Currency Table for Value Help'
@Search.searchable: true
define view ZDEMO_DDL_SCURX
as select from scurx
{
//scurx
@Search.defaultSearchElement: true
@Search.fuzzinessThreshold: 0.8
key currkey,
@UI.hidden: true //Will not appear in Value help fields
currdec
}
Now let’s go ahead and create Consumption views. This will define what all fields we will be making available to our Presentation layer in FIORI Application:
@AbapCatalog.sqlViewName: 'YDEMO_SPFLI'
@AbapCatalog.compiler.compareFilter: true
@AbapCatalog.preserveKey: true
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Consumption CDS for SPFLI'
@OData.publish: true
//To Make it BOPF Enabled
@ObjectModel:{semanticKey: 'AirlineCode',
//This will Delegate the CRUD to Transactional CDS
transactionalProcessingDelegated: true,
//To define what all actions are enabled
createEnabled: true,
deleteEnabled: true,
updateEnabled: true }
//Adding the List Title
@UI:{headerInfo: { typeName: 'Flight Detail', typeNamePlural: 'Flight Details',
title: {
//Check different better Page header option at https://help.sap.com/viewer/cc0c305d2fab47bd808adcad3ca7ee9d/7.5.18/en-US/7d892d2c94cb483ebe28b133373ba109.html
// This come in Detail Page as Header
label: 'Flight',
value: 'AirlineCode' -- Reference to element in element list
},
description: {
label: 'Flight Connection',
value: 'FlightConnectionNo' -- Reference to element in element list
} },
lineItem:[{criticality:'Criticality'}],
//Not working with List report but added for your reference
chart: {
title: 'Airfare by Airline',
description: 'Line-chart displaying the fare amount by airline',
chartType: #LINE,
dimensions: [ 'AirlineCode' ], -- Reference to one element
measures: [ 'Airfare' ] -- Reference to one or more elements
}
}
@Metadata.allowExtensions: true
define view ZDEMO_DDL_SPFLI
as select from ZIDEMO_DDL_SPFLI as schedule //Transactional CDS
association to scarr as _airline on _airline.carrid = $projection.AirlineCode //Association for Text Element
association [1..*] to ZDEMO_DDL_SFLIGHT as _flight on _flight.AirlineCode = $projection.AirlineCode
and _flight.FlightConnectionNo = $projection.FlightConnectionNo
{
//schedule
//Make Key Value as Read Only and Mandatory
@ObjectModel:{readOnly: true, mandatory: true }
//Language-independent Text Elements
//Other 2 ways can be found at:https://help.sap.com/viewer/cc0c305d2fab47bd808adcad3ca7ee9d/7.5.18/en-US/03cc4e1b8fd447789daaa27ec4927328.html
@ObjectModel.text.element: ['CarrierName']
//Adding Field Labels and Descriptions
@EndUserText: { label: 'Airline Code#',
quickInfo: 'Airline Code part of key' }
key schedule.carrid as AirlineCode,
@ObjectModel:{readOnly: true, mandatory: true }
key schedule.connid as FlightConnectionNo,
@EndUserText: { label: 'Country From',
quickInfo: 'Airline Country From' }
@Semantics.address.country: true
schedule.countryfr as CountryFrom,
@Semantics.address.city: true
schedule.cityfrom as CityFrom,
schedule.airpfrom as AirportFrom,
@EndUserText: { label: 'Country To',
quickInfo: 'Airline Country To' }
@Semantics.address.country: true
schedule.countryto as CountryTo,
@Semantics.address.city: true
schedule.cityto as CityTo,
schedule.airpto as AirportTo,
@Semantics.dateTime: true
schedule.deptime as DepartureTime,
@Semantics.dateTime: true
schedule.arrtime as ArrivalTime,
schedule.distance as Distance,
schedule.distid as DistanceId,
schedule.fltype as FlightType,
schedule.period as Period,
@ObjectModel.readOnly: true
case schedule.carrid
when 'AA' then 1 //red
when 'AB' then 2 //yellow
when 'AZ' then 2 //yellow
when 'AC' then 3 //green
else 0 //No change
end as Criticality,
@ObjectModel.readOnly: true
_airline.carrname as CarrierName,
_flight
}
Similarly create a Consumption view for SFLIGHT Table:
@AbapCatalog.sqlViewName: 'YDEMO_FLIGHT'
@AbapCatalog.compiler.compareFilter: true
@AbapCatalog.preserveKey: true
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'List reporting for Flight Details'
@OData.publish: true
//To Make it BOPF Enabled
@ObjectModel:{semanticKey: 'AirlineCode',
//This will Delegate the CRUD to Transactional CDS
transactionalProcessingDelegated: true,
//To define what all actions are enabled
createEnabled: true,
deleteEnabled: true,
updateEnabled: true }
//Adding the List Title
@UI:{headerInfo: { typeName: 'Flight Detail', typeNamePlural: 'Flight Details',
title: {
//Check different better Page header option at https://help.sap.com/viewer/cc0c305d2fab47bd808adcad3ca7ee9d/7.5.18/en-US/7d892d2c94cb483ebe28b133373ba109.html
// This come in Detail Page as Header
label: 'Flight',
value: 'AirlineCode' -- Reference to element in element list
},
description: {
label: 'Flight Connection',
value: 'FlightConnectionNo' -- Reference to element in element list
} },
lineItem:[{criticality:'Criticality'}],
//Not working with List report
chart: {
title: 'Airfare by Airline',
description: 'Line-chart displaying the fare amount by airline',
chartType: #LINE,
dimensions: [ 'AirlineCode' ], -- Reference to one element
measures: [ 'Airfare' ] -- Reference to one or more elements
}
//Below Annotation not working
//,presentationVariant:{sortOrder: [{ by: 'AirlineCode',direction: #ASC },{ by: 'FlightConnectionNo',direction: #ASC },
//
// { by: ' FlightDate' ,direction: #ASC}]
// }
}
//Adding a Standard Search Filter
@Search.searchable: true
@Metadata.allowExtensions: true
define view ZDEMO_DDL_SFLIGHT
as select from ZIDEMO_DDL_SFLIGHT as flight //Transactional CDS
association to scarr as _airline on _airline.carrid = $projection.AirlineCode //Association for Text Element
association [0..1] to ZDEMO_DDL_SCURX as _currencyValueHelp on _currencyValueHelp.currkey = $projection.LocalCurrency //Association for Value Help
{
//Make Key Value as Mandatory
@ObjectModel:{ mandatory: true }
//Language-independent Text Elements
//Other 2 ways can be found at:https://help.sap.com/viewer/cc0c305d2fab47bd808adcad3ca7ee9d/7.5.18/en-US/03cc4e1b8fd447789daaa27ec4927328.html
@ObjectModel.text.element: ['CarrierName']
//Adding Field Labels and Descriptions
@EndUserText: { label: 'Airline Code#',
quickInfo: 'Airline Code part of key' }
key flight.carrid as AirlineCode,
@ObjectModel:{ mandatory: true }
key flight.connid as FlightConnectionNo,
@ObjectModel:{ mandatory: true }
key flight.fldate as FlightDate,
@Semantics.amount.currencyCode: 'LocalCurrency'
// @DefaultAggregation: #SUM
flight.price as Airfare,
@Semantics.currencyCode: true
//Value Help
//Other way for Value Help can be found at:https://help.sap.com/viewer/cc0c305d2fab47bd808adcad3ca7ee9d/7.5.18/en-US/8a8415c033d441b2b079a53aff129463.html
@Consumption.valueHelp: '_currencyValueHelp'
flight.currency as LocalCurrency,
flight.planetype as AircraftType,
//Calculated Column has to make read only with BOPF
@ObjectModel.readOnly: true
flight.seatsmax + flight.seatsmax_b + flight.seatsmax_f as total_max_seat,
@ObjectModel.readOnly: true
(flight.seatsmax + flight.seatsmax_b + flight.seatsmax_f)
-(flight.seatsocc + flight.seatsocc_b + flight.seatsocc_f) as total_avail_seat,
flight.seatsmax as SeatsMaxEco,
flight.seatsocc as SeatsOccEco,
@Semantics.amount.currencyCode: 'LocalCurrency'
flight.paymentsum as TotalCurrBook,
flight.seatsmax_b as SeatsMaxBus,
flight.seatsocc_b as SeatsOccBus,
flight.seatsmax_f as SeatsMaxFirst,
flight.seatsocc_f as SeatsOccFirst,
@ObjectModel.readOnly: true
_airline.carrname as CarrierName,
@ObjectModel.readOnly: true
case flight.carrid
when 'AA' then 1 //red
when 'AZ' then 2 //yellow
when 'DL' then 3 //green
when 'JL' then 1
when 'LH' then 2
when 'SQ' then 3
else 0 //No change
end as Criticality,
//Expose Association
@Consumption.filter.hidden: true
_currencyValueHelp
}
Now lets create the Metadata extension view. We can even have the UI annotations mentioned in the metadata extension view as a part of Consumption view but as a good practice we keep both of them separate. The metadata view will be responsible for the look and feel of our list report application:
Metadata extension view for SPFLI(Header Table)
@Metadata.layer: #CORE
//Add default search bar
@Search.searchable: true
annotate view ZDEMO_DDL_SPFLI with
{
//ZDEMO_DDL_SPFLI
//Data Come under Separate Tab
@UI.facet: [
{
label: 'Basic Information',
id : 'GeneralInfo',
purpose: #STANDARD,
type : #COLLECTION,
position: 10
},
{ type: #IDENTIFICATION_REFERENCE ,
label : 'General Information',
parentId: 'GeneralInfo',
id: 'idIdentification' ,
position: 10 },
{
label: 'From Data',
id : 'FromData',
purpose: #STANDARD,
parentId : 'GeneralInfo',
type : #FIELDGROUP_REFERENCE,
targetQualifier : 'FromData',
position: 20
},
{
label: 'To Data',
id : 'ToData',
purpose: #STANDARD,
parentId : 'GeneralInfo',
type : #FIELDGROUP_REFERENCE,
targetQualifier : 'ToData',
position: 30
},
{
label: 'Flight Info',
id : 'FlightInfo',
purpose: #STANDARD,
type : #COLLECTION,
position: 20
},
{
label: 'FlightInfoFG',
id : 'FG',
purpose: #STANDARD,
parentId : 'FlightInfo',
type : #FIELDGROUP_REFERENCE,
targetQualifier : 'FG',
position: 10
},
{
label: 'FlightData',
id : 'FlightData',
type : #LINEITEM_REFERENCE,
targetElement: '_flight' ,
position: 10
}
]
//Adding a Filter Bar for Field-Specific Selection
@UI.selectionField:[{position: 10}]
//Positioning and Prioritizing UI Elements
@UI: { lineItem: [{ position: 10,label:'Airline Carrier Code', importance: #HIGH } ,
//Action Button
{ type: #FOR_ACTION, position: 0, dataAction: 'BOPF:Copy', label: 'Copy Data' }]
}
//Positioning on the Details Page
@UI.identification: [{ position: 10, label:'Airline Carrie Code on Detail Page',importance: #HIGH }]
//Apply Default Fuzzy Search
@Search: { defaultSearchElement: true, fuzzinessThreshold: 0.8 ,ranking:#HIGH }
AirlineCode;
//Clubing Annotations
@Search: { defaultSearchElement: true, fuzzinessThreshold: 0.7 ,ranking:#MEDIUM }
@UI:{selectionField:[{position: 20}],
lineItem:[{position: 20, importance: #HIGH}]
}
@UI.identification: [{ position: 20, importance: #HIGH }]
@EndUserText.quickInfo: 'Flight Connection Id'
FlightConnectionNo;
@UI:{selectionField:[{position: 30}],
lineItem:[{position: 30, importance: #HIGH}]
}
@UI.fieldGroup: [ { qualifier: 'FromData', position: 10,importance: #MEDIUM} ]
CountryFrom;
@UI.fieldGroup: [ { qualifier: 'FromData', position: 20,importance: #MEDIUM} ]
CityFrom;
@UI.fieldGroup: [ { qualifier: 'FromData', position: 30,importance: #MEDIUM} ]
AirportFrom;
@UI:{selectionField:[{position: 40}],
lineItem:[{position: 40, importance: #HIGH}]
}
@UI.fieldGroup: [ { qualifier: 'ToData', position: 10,importance: #MEDIUM} ]
CountryTo;
@UI.fieldGroup: [ { qualifier: 'ToData', position: 20,importance: #MEDIUM} ]
CityTo;
@UI.fieldGroup: [ { qualifier: 'ToData', position: 30,importance: #MEDIUM} ]
AirportTo;
// @UI.identification: [{ position: 30, importance: #LOW }]
// FlightTime;
@UI.identification: [{ position: 40, importance: #HIGH }]
DepartureTime;
@UI.identification: [{ position: 50, importance: #HIGH }]
ArrivalTime;
@UI.hidden: true
Distance;
@UI.hidden: true
@UI.fieldGroup: [ { qualifier: 'FG',position: 10,importance: #MEDIUM} ]
DistanceId;
@UI.identification: [{ position: 60, importance: #HIGH }]
FlightType;
@UI.hidden: true
Period;
@Search: { defaultSearchElement: true, fuzzinessThreshold: 0.7 ,ranking:#HIGH }
CarrierName;
}
Similarly we will create Metadata extension view for SFLIGHT. I have created it such that we can use it to have a independent List report Application:
@Metadata.layer: #CORE
//Add default search bar
@Search.searchable: true
annotate view ZDEMO_DDL_SFLIGHT with
{
//Data Come under Separate Tab
@UI.facet: [
{
label: 'Basic Information',
id : 'GeneralInfo',
purpose: #STANDARD,
type : #COLLECTION,
position: 10
},
{ type: #IDENTIFICATION_REFERENCE ,
label : 'General Information',
parentId: 'GeneralInfo',
id: 'idIdentification' ,
position: 10 },
{
label: 'Flight Data',
id : 'FlightData',
purpose: #STANDARD,
parentId : 'GeneralInfo',
type : #FIELDGROUP_REFERENCE,
targetQualifier : 'FlightData',
position: 20
},
{
label: 'Seats Data',
id : 'SeatsData',
purpose: #STANDARD,
parentId : 'GeneralInfo',
type : #FIELDGROUP_REFERENCE,
targetQualifier : 'SeatsData',
position: 30
},
{
label: 'Flight Info',
id : 'FlightInfo',
purpose: #STANDARD,
type : #COLLECTION,
position: 20
}]
//Adding a Filter Bar for Field-Specific Selection
@UI.selectionField:[{position: 10}]
//Positioning and Prioritizing UI Elements
@UI.lineItem: [{ position: 10, label:'Airline Carrier Code', importance: #HIGH}]
//Positioning on the Details Page
@UI.identification: [{ position: 10, label:'Airline Carrie Code on Detail Page',importance: #HIGH }]
//Apply Default Fuzzy Search
@Search: { defaultSearchElement: true, fuzzinessThreshold: 0.8 ,ranking:#HIGH }
AirlineCode;
//Clubing Annotations
@Search: { defaultSearchElement: true, fuzzinessThreshold: 0.7 ,ranking:#MEDIUM }
@UI:{selectionField:[{position: 20}],
lineItem:[{position: 20, importance: #HIGH}]
}
@UI.identification: [{ position: 20, importance: #HIGH }]
@EndUserText.quickInfo: 'Flight Connection Id'
FlightConnectionNo;
@UI:{selectionField:[{position: 30}],
lineItem:[{position: 30, importance: #HIGH}]
}
@UI.identification: [{ position: 30, importance: #HIGH }]
@EndUserText.quickInfo: 'Date of Flight'
FlightDate;
@UI.lineItem:[{position: 50, importance: #HIGH}]
@UI.fieldGroup: [ { qualifier: 'FlightData', position: 20,importance: #MEDIUM} ]
Airfare;
@UI.fieldGroup: [ { qualifier: 'FlightData', position: 30,importance: #MEDIUM } ]
LocalCurrency;
@UI.lineItem:[{position: 40, importance: #HIGH,criticality: 'Criticality'}]
//If both below Annotations are kept then it will repeat in both Field Group
// @UI.identification: [{ position: 40, importance: #HIGH,criticality: 'Criticality' }]
@UI.fieldGroup: [ { qualifier: 'FlightData', position: 10,importance: #HIGH,criticality: 'Criticality' } ]
AircraftType;
@UI.lineItem:[{position: 60, importance: #MEDIUM,label: 'Total Seats'}]
//Below Annotation added as the field label not changed in filter bar
@EndUserText.label: 'Total Seats'
total_max_seat;
@UI.lineItem:[{position: 70, importance: #MEDIUM,label: 'Available Seats'}]
@EndUserText.label: 'Available Seats'
total_avail_seat;
//To group similar type of fields
@UI.fieldGroup: [ { qualifier: 'SeatsData', position: 10 } ]
SeatsMaxEco;
@UI.fieldGroup: [ { qualifier: 'SeatsData', position: 20 } ]
SeatsOccEco;
//If both Annotations are kept then it will repeat in both Field Group
// @UI.identification: [{ position: 90, importance: #LOW }]
@UI.fieldGroup: [ { qualifier: 'SeatsData', position: 30,importance: #LOW } ]
SeatsMaxBus;
@UI.fieldGroup: [ { qualifier: 'SeatsData', position: 40,importance: #LOW } ]
SeatsOccBus;
//Will not appear in filter fields
@UI.hidden: true
@UI.identification: [{ position: 110, importance: #LOW }]
SeatsMaxFirst;
@UI.hidden: true
SeatsOccFirst;
@Search: { defaultSearchElement: true, fuzzinessThreshold: 0.8 ,ranking:#HIGH }
CarrierName;
}
No comments:
Post a Comment