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). 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; }